/* * * kf701_chardev.c * * 2006-12-06 13:22:22 * * Contact me: kf701.ye AT gmail.com * * 2.6内核字符驱动示例程序,供学习。 * * /dev/kf701_chardev0 * /proc/devices * * 分配1024个字节的buffer,对/dev/kf701_chardev0的读写将 * 作用到buffer上。目前的写操作会覆盖先前的buffer。 * * 内核定时器每次向buffer里追加一个字符和一个回车。 * * 测试方法: * $> echo "only for test" > /dev/kf701_chardev0 * $> cat /dev/kf701_chardev0 * * 2006-12-18 09:19:44 修改 * 加入对 NONBLOCK 和 poll 的支持。 * */
#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <linux/cdev.h> #include <linux/timer.h> #include <linux/jiffies.h> #include <linux/ioctl.h> #include <linux/poll.h> #include <linux/wait.h>
int chardev_open(struct inode *, struct file *); int chardev_release(struct inode *, struct file *); ssize_t chardev_read(struct file *, char __user *, size_t, loff_t *); ssize_t chardev_write(struct file *, const char __user *, size_t, loff_t *); int chardev_ioctl(struct inode *,struct file *,unsigned int,unsigned long); unsigned int chardev_poll(struct file* file, poll_table *wait); void timer_function(unsigned long);
#define DEVICE "kf701_chardev" #define SUCCESS 0 #define BUF_SIZE 1024 #define TIMER_INTERVAL 20 // 每20秒运行一次定时器函数
int major = 233; int minor = 0;
// define ioctl command
#define KF701_IOC_MAGIC 'K' #define KF701_IO_RESET _IO(KF701_IOC_MAGIC,0) #define KF701_IO_R _IOR(KF701_IOC_MAGIC,1,int) #define KF701_IO_W _IOW(KF701_IOC_MAGIC,2,int) #define KF701_IO_RW _IOWR(KF701_IOC_MAGIC,3,int) #define KF701_IOC_MAXNR 4
struct file_operations fops = { .owner = THIS_MODULE, .read = chardev_read, .write = chardev_write, .open = chardev_open, .release = chardev_release, .ioctl = chardev_ioctl, .poll = chardev_poll, };
struct my_dev{ wait_queue_head_t rq; // 读取等待队列
uint8_t *buf; uint32_t size; uint32_t index; struct semaphore sem; struct cdev cdev; struct timer_list timer; char timer_char; uint32_t timer_interval; };
struct my_dev *kf701_dev;
static int __init chardev_init(void) { // 1. 分配主设备号
dev_t devno = MKDEV( major, minor ); int ret = register_chrdev_region( devno, 1, DEVICE ); if( ret < 0 ) { printk(KERN_DEBUG "register major number failed with %d\n", ret); return ret; } printk(KERN_DEBUG "%s:register major number OK\n",DEVICE);
// 2. 注册设备
kf701_dev = kmalloc(sizeof(struct my_dev), GFP_KERNEL); memset( kf701_dev, 0, sizeof(struct my_dev) ); cdev_init( &kf701_dev->cdev, &fops ); kf701_dev->cdev.ops = &fops; kf701_dev->cdev.owner = THIS_MODULE; ret = cdev_add( &kf701_dev->cdev, devno, 1 ); if( ret < 0 ) { printk(KERN_DEBUG "register device failed with %d\n", ret); return ret; } printk(KERN_DEBUG "%s:register device OK\n",DEVICE);
// 3. 分配本驱动要使用的内存
kf701_dev->index = 0; kf701_dev->size = BUF_SIZE; kf701_dev->buf = kmalloc( kf701_dev->size, GFP_KERNEL ); if( NULL == kf701_dev->buf ) { printk(KERN_DEBUG "kmalloc failed\n"); return -ENOMEM; } printk(KERN_DEBUG "%s:kmalloc buffer OK\n",DEVICE);
// 4. 初始化信号量
init_MUTEX( &(kf701_dev->sem) ); printk(KERN_DEBUG "%s:init semaphore OK\n",DEVICE);
// 5. 初始化等待队列头
init_waitqueue_head(&kf701_dev->rq);
// 6. 初始化并启动定时器
kf701_dev->timer_char = 'A'; init_timer( &kf701_dev->timer ); kf701_dev->timer_interval = HZ * TIMER_INTERVAL; kf701_dev->timer.function = timer_function; kf701_dev->timer.expires = jiffies + kf701_dev->timer_interval; kf701_dev->timer.data = (unsigned long)kf701_dev; add_timer( &kf701_dev->timer ); printk(KERN_DEBUG "%s:init timer OK\n",DEVICE);
return SUCCESS; }
static void __exit chardev_exit(void) { dev_t devno = MKDEV( major, minor ); // 以相反的顺序清除
del_timer_sync( &kf701_dev->timer ); printk(KERN_DEBUG "%s:del timer OK\n",DEVICE); kfree( kf701_dev->buf ); cdev_del( &kf701_dev->cdev ); kfree( kf701_dev ); printk(KERN_DEBUG "%s:kfree OK\n",DEVICE); unregister_chrdev_region( devno, 1 ); printk(KERN_DEBUG "%s:unregister device OK\n",DEVICE); }
int chardev_open(struct inode *inode, struct file *file) { struct my_dev *dev = container_of(inode->i_cdev, struct my_dev, cdev); file->private_data = dev;
printk(KERN_DEBUG "%s:open OK\n",DEVICE); return SUCCESS; }
int chardev_release(struct inode *inode, struct file *file) { printk(KERN_DEBUG "%s:release OK\n",DEVICE); return SUCCESS; }
ssize_t chardev_read(struct file *file, char __user *buf, size_t count,loff_t *offset) { struct my_dev *dev = file->private_data; ssize_t retval = 0;
if( down_interruptible(&dev->sem) ) return -ERESTARTSYS;
// 增加了对NONBLOCK的支持
while( 0 == dev->index ) { up(&dev->sem); if( file->f_flags & O_NONBLOCK ) return -EAGAIN; // 在这里准备睡眠,等待条件为真
if( wait_event_interruptible(dev->rq, (0 != dev->index)) ) return -ERESTARTSYS; // 返回非0表示被信号中断
if( down_interruptible(&dev->sem) ) return -ERESTARTSYS; // 先取得锁再检查条件
}
if( *offset >= dev->index ) goto out; if( (*offset+count) > dev->index ) count = dev->index - *offset; if( copy_to_user(buf, dev->buf + *offset, count) ) { retval = -EFAULT; goto out; } *offset += count; retval = count;
out: up(&dev->sem); return retval; }
// omit offset argument for write
ssize_t chardev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) { struct my_dev *dev = file->private_data; ssize_t retval = -ENOMEM;
if( down_interruptible(&dev->sem) ) return -ERESTARTSYS;
if( count > dev->size ) count = dev->size - 1; if( copy_from_user(dev->buf, buf, count) ) { retval = -EFAULT; goto out; } dev->index = count; retval = count;
out: up(&dev->sem); // 唤醒阻塞在 read 和 select 上的进程
if( retval > 0 ) wake_up_interruptible(&dev->rq); return retval; }
int chardev_ioctl(struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg) { struct my_dev *dev = file->private_data; int retval = 0, tmp = 0;
if( _IOC_TYPE(cmd) != KF701_IOC_MAGIC ) return -ENOTTY; if( _IOC_NR(cmd) > KF701_IOC_MAXNR ) return -ENOTTY;
switch(cmd) { case KF701_IO_RESET: printk(KERN_DEBUG "%s:IO_RESET CMD\n",DEVICE); break; case KF701_IO_R: printk(KERN_DEBUG "%s:IO READ CMD\n",DEVICE); retval = put_user(dev->index, (int __user *)arg); break; case KF701_IO_W: printk(KERN_DEBUG "%s:IO WRITE CMD\n",DEVICE); retval = get_user(tmp, (int __user *)arg); printk(KERN_DEBUG "CMD ARG = %d\n", tmp); break; case KF701_IO_RW: printk(KERN_DEBUG "%s:IO W-R CMD\n",DEVICE); break; default: return -ENOTTY; } return retval; }
unsigned int chardev_poll(struct file * file, poll_table * wait) { struct my_dev *dev = file->private_data; unsigned int mask = 0; down(&dev->sem); poll_wait(file,&dev->rq,wait); if( 0 != dev->index ) mask |= POLLIN | POLLRDNORM; up(&dev->sem); return mask; }
// 此函数在进程上下文之外运行,类似中断上下文,因此:
// 1. 不能访问用户空间
// 2. current指针没有意义
// 3. 不能调用可能引起体眠和调度的代码,信号量也不行。
// 4. 最好使用 spinlock
void timer_function(unsigned long arg) { struct my_dev *dev = (struct my_dev*)arg; if( dev->index >= (dev->size - 1) ) return; if( dev->timer_char > 'Z' ) dev->timer_char = 'A'; if( '\n' == dev->buf[dev->index-1] ) dev->index--; dev->buf[dev->index++] = (dev->timer_char)++; dev->buf[dev->index++] = '\n';
// 唤醒阻塞在 read 和 select 上的进程
wake_up_interruptible(&dev->rq);
dev->timer.expires += dev->timer_interval; add_timer( &dev->timer ); }
module_init(chardev_init); module_exit(chardev_exit);
MODULE_AUTHOR("kf701.ye at gmail.com"); MODULE_DESCRIPTION("Study for kf701"); MODULE_SUPPORTED_DEVICE(DEVICE); MODULE_LICENSE("GPL");
|