linux 驱动——高级字符驱动程序操作
2017-10-20 10:13
453 查看
内容
ioctl 的ioctl 的系统概念
与用户空间同步的方法
进程休眠
非阻塞IO及与用户间的通信
原型函数
int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
一、ioctl 命令
ioctl 的命令由四部分组成通过ioctl()函数的cmd,下发需要底层响应的命令。cmd命令的大小32位,分成4个部分: bit31~bit30:“区别读写” 区,作用是区分是读取命令还是写入命令; bit29~bit16:“数据大小” 区,表示 ioctl() 中的 arg 变量传送的内存大小。 bit15~bit8 :“魔数”(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。 bit7~bit0 :“区别序号”区,是区分命令的命令顺序序号。
定义方式
#define PPPIOCGIDLE <---> (2 << 30) | ('t' << 8) | (63 << 0) | (size << 16)
参考Linux内核文件:include/asm-generic/ioctl.h
#ifndef _ASM_GENERIC_IOCTL_H #define _ASM_GENERIC_IOCTL_H /* ioctl command encoding: 32 bits total, command in lower 16 bits, * size of the parameter structure in the lower 14 bits of the * upper 16 bits. * Encoding the size of the parameter structure in the ioctl request * is useful for catching programs compiled with old versions * and to avoid overwriting user space outside the user buffer area. * The highest 2 bits are reserved for indicating the ``access mode''. * NOTE: This limits the max parameter size to 16kB -1 ! */ /* * The following is for compatibility across the various Linux * platforms. The generic ioctl numbering scheme doesn't really enforce * a type field. De facto, however, the top 8 bits of the lower 16 * bits are indeed used as a type field, so we might just as well make * this explicit here. Please be sure to use the decoding macros * below from now on. */ #define _IOC_NRBITS 8 #define _IOC_TYPEBITS 8 #define _IOC_SIZEBITS 14 #define _IOC_DIRBITS 2 #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) #define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) #define _IOC_NRSHIFT 0 #define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) #define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) #define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) /* * Direction bits. */ #define _IOC_NONE 0U #define _IOC_WRITE 1U #define _IOC_READ 2U #define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT)) /* provoke compile error for invalid uses of size argument */ extern unsigned int __invalid_size_argument_for_IOC; #define _IOC_TYPECHECK(t) \ ((sizeof(t) == sizeof(t[1]) && \ sizeof(t) < (1 << _IOC_SIZEBITS)) ? \ sizeof(t) : __invalid_size_argument_for_IOC) /* used to create numbers */ #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size))) #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) #define _IOR_BAD(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) #define _IOW_BAD(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size)) #define _IOWR_BAD(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)) /* used to decode ioctl numbers.. */ #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) /* ...and for the drivers/sound files... */ #define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) #define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) #define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) #define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) #define IOCSIZE_SHIFT (_IOC_SIZESHIFT) #endif /* _ASM_GENERIC_IOCTL_H */
ldd3中的实例
/* Use 'k' as magic number */ #define SCULL_IOC_MAGIC 'k' /* Please use a different 8-bit number in your code */ #define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0) /* * S means "Set" through a ptr, * T means "Tell" directly with the argument value * G means "Get": reply by setting through a pointer * Q means "Query": response is on the return value * X means "eXchange": switch G and S atomically * H means "sHift": switch T and Q atomically */ #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int) #define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int) #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3) #define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4) #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int) #define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int) #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7) #define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8) #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int) #define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, int) #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11) #define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12) #define SCULL_IOC_MAXNR 14
ioctl 参数
arg 可以是整数,也可以是指针,整数可以直接使用,指针需要转换。
可使用的转换函数:
copy_to_user(), copy_from_user(), //针对的是大的数据 put_user(datum, ptr); //把datum 写到用户空间的指针,速度较快,大小由指针ptr 的类型决定 get_user(local, ptr); //从用户中接收一个数据
权能与受限操作
如:
CAP_SYS_ADMIN
实例
switch(cmd) { case SCULL_IOCRESET: scull_quantum = SCULL_QUANTUM; scull_qset = SCULL_QSET; break; case SCULL_IOCSQUANTUM: /* Set: arg points to the value */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; retval = __get_user(scull_quantum, (int __user *)arg); break; case SCULL_IOCTQUANTUM: /* Tell: arg is the value */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; scull_quantum = arg; break; case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */ retval = __put_user(scull_quantum, (int __user *)arg); break; case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */ return scull_quantum; case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; tmp = scull_quantum; retval = __get_user(scull_quantum, (int __user *)arg); if (retval == 0) retval = __put_user(tmp, (int __user *)arg); break; case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; tmp = scull_quantum; scull_quantum = arg; return tmp; default: /* redundant, as cmd was checked against MAXNR */ return -ENOTTY; } return retval; int quantum; ioctl(fd,SCULL_IOCSQUANTUM, &quantum); /* Set by pointer */ ioctl(fd,SCULL_IOCTQUANTUM, quantum); /* Set by value */ ioctl(fd,SCULL_IOCGQUANTUM, &quantum); /* Get by pointer */ quantum = ioctl(fd,SCULL_IOCQQUANTUM); /* Get by return value */ ioctl(fd,SCULL_IOCXQUANTUM, &quantum); /* Exchange by pointer */ quantum = ioctl(fd,SCULL_IOCHQUANTUM, quantum); /* Exchange by value */
二、阻塞I/O
1、概念休眠:当进程被休眠时,会被标记为一种特殊状态,并从调度器运行队列中移走,直到某些情况下修改了这种状态,进程才会在GPU上调度。休眠时,此进程搁置一边,等待将来某个事情的发生
休眠需要遵守的规则:
1) 不要再原子上下文中休眠,即不能在拥有自旋锁、seqlock、RCU锁时休眠,中断中也不能休眠;
2) 休眠中等待唤醒的资源,可能被其他进程获取,所以,除非我们知道其他人会唤醒此进程,否则进程不能休眠
利用等待队列维护休眠中的进程。
2、等待队列
wait_event(queue, condition) wait_event_interruptible(queue, condition) wait_event_timeout(queue, condition, timeout) wait_event_interruptible_timeout(queue, condition, timeout)
conditon 为假时,进入休眠状态
唤醒时的函数
void wake_up(wait_queue_head_t *queue); void wake_up_interruptible(wait_queue_head_t *queue);
wake_up 唤醒所有等待在给定queue 上的进程,wake_up_interruptible()唤醒对应wait_event_interruptible()可中断休眠的进程。
3、高级休眠
在复杂的锁定以及性能需求会强制驱动程序使用低层的函数实现休眠。(schedule())
使用步骤:
//DECLARE_WAITQUEUE, add_wait_queue, 在wait_queue_head_t 指向的链表上, 新定义的wait_queue 元素被插入, 插 //入的元素绑定一个task_struct,(一般为current); DECLARE_WAITQUEUE(wait, current); add_wait_queue(wait_queue_head_t *xxx_wait, &wait); _set_current_state(TASK_INTERRUPTEIBLE); //改变当前进程状态 sechedule() //调取其他进程执行,进程切换 if(signal_pending(current)){ //如果是因为信号,唤醒 ret = -ERESTARTSYS; goto out; }
完整实例
static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { int ret; struct globalfifo_dev *dev = filp->private_data; **DECLARE_WAITQUEUE(wait, current);** mutex_lock(&dev->mutex); **add_wait_queue(&dev->r_wait, &wait);** while (dev->current_len == 0) { if (filp->f_flags & O_NONBLOCK) { ret = -EAGAIN; goto out; } **__set_current_state(TASK_INTERRUPTIBLE);** mutex_unlock(&dev->mutex); **schedule();** **if (signal_pending(current)) {** ret = -ERESTARTSYS; goto out2; } mutex_lock(&dev->mutex); } if (count > dev->current_len) count = dev->current_len; if (copy_to_user(buf, dev->mem, count)) { ret = -EFAULT; goto out; } else { memcpy(dev->mem, dev->mem + count, dev->current_len - count); dev->current_len -= count; printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len); wake_up_interruptible(&dev->w_wait); ret = count; } out: mutex_unlock(&dev->mutex); out2: remove_wait_queue(&dev->w_wait, &wait); set_current_state(TASK_RUNNING); return ret; } static ssize_t globalfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { struct globalfifo_dev *dev = filp->private_data; int ret; **DECLARE_WAITQUEUE(wait, current);** mutex_lock(&dev->mutex); **add_wait_queue(&dev->w_wait, &wait);** while (dev->current_len == GLOBALFIFO_SIZE) { if (filp->f_flags & O_NONBLOCK) { ret = -EAGAIN; goto out; } **__set_current_state(TASK_INTERRUPTIBLE);** mutex_unlock(&dev->mutex); **schedule();** **if (signal_pending(current)) {** ret = -ERESTARTSYS; goto out2; } mutex_lock(&dev->mutex); } if (count > GLOBALFIFO_SIZE - dev->current_len) count = GLOBALFIFO_SIZE - dev->current_len; if (copy_from_user(dev->mem + dev->current_len, buf, count)) { ret = -EFAULT; goto out; } else { dev->current_len += count; printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev->current_len); wake_up_interruptible(&dev->r_wait); ret = count; } out: mutex_unlock(&dev->mutex); out2: remove_wait_queue(&dev->w_wait, &wait); set_current_state(TASK_RUNNING); return ret; }
4、轮询操作
概念: 使用select(), poll(), epoll()系统调用查询是否可以做设备进行无阻塞的访问。
poll 在内核中的使用
static unsigned int xxx_poll(struct file *filp, poll_table * wait)// 为内核中函数定义 static unsigned int globalfifo_poll(struct file *filp, poll_table * wait) { unsigned int mask = 0; struct globalfifo_dev *dev = filp->private_data; mutex_lock(&dev->mutex); poll_wait(filp, &dev->r_wait, wait); // 加入读等待队列, 唤醒用户中,因为select()而睡眠的进程 poll_wait(filp, &dev->w_wait, wait); //加入写等待队列 if (dev->current_len != 0) { //可读,返回可读标志 mask |= POLLIN | POLLRDNORM; } if (dev->current_len != GLOBALFIFO_SIZE) { //可写,返回可写标志 mask |= POLLOUT | POLLWRNORM; } mutex_unlock(&dev->mutex); return mask; }
应用实例
#include <sys/select.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #define FIFO_CLEAR 0x1 #define BUFFER_LEN 20 void main(void) { int fd, num; char rd_ch[BUFFER_LEN]; fd_set rfds, wfds; /* 读/写文件描述符集 */ /* 以非阻塞方式打开/dev/globalfifo设备文件 */ fd = open("/dev/globalfifo", O_RDONLY | O_NONBLOCK); if (fd != -1) { /* FIFO清0 */ if (ioctl(fd, FIFO_CLEAR, 0) < 0) printf("ioctl command failed\n"); while (1) { FD_ZERO(&rfds); FD_ZERO(&wfds); FD_SET(fd, &rfds); FD_SET(fd, &wfds); select(fd + 1, &rfds, &wfds, NULL, NULL); /* 数据可获得 */ if (FD_ISSET(fd, &rfds)) printf("Poll monitor:can be read\n"); /* 数据可写入 */ if (FD_ISSET(fd, &wfds)) printf("Poll monitor:can be written\n"); } } else { printf("Device open failure\n"); } }
#include <sys/types.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <sys/epoll.h> #include <strings.h> #define FIFO_CLEAR 0x1 #define BUFFER_LEN 20 void main(void) { int fd; fd = open("/dev/globalfifo", O_RDONLY | O_NONBLOCK); if (fd != -1) { struct epoll_event ev_globalfifo; int err; int epfd; if (ioctl(fd, FIFO_CLEAR, 0) < 0) printf("ioctl command failed\n"); epfd = epoll_create(1); if (epfd < 0) { perror("epoll_create()"); return; } bzero(&ev_globalfifo, sizeof(struct epoll_event)); ev_globalfifo.events = EPOLLIN | EPOLLPRI; err = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev_globalfifo); if (err < 0) { perror("epoll_ctl()"); return; } err = epoll_wait(epfd, &ev_globalfifo, 1, 15000); if (err < 0) { perror("epoll_wait()"); } else if (err == 0) { printf("No data input in FIFO within 15 seconds.\n"); } else { printf("FIFO is not empty\n"); } err = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev_globalfifo); if (err < 0) perror("epoll_ctl()"); } else { printf("Device open failure\n"); } }
轮询可以参考: http://blog.csdn.net/klarclm/article/details/8551493
5、异步通知(发送信号通知)
异步通知的好处: 非阻塞I/O的应用程序无须轮询设备是否可访问,
阻塞的访问也可以被类似“中断”的异步通知所取代
概念:设备准备就绪,就发送信号通知应用程序,应用程序就不需要查询设备状态,类似硬件中断。 信号是软件层次上对中断机制的模拟。信号是异步的,一个进程不需要通过任何操作等待信号的到达。
阻塞: 等待设备可访问,
非阻塞: poll 轮询,查询设备是否可以访问,
异步:通知用户可访问,然后再调用I/O 进行处理。
用户态中的使用模板
signal(SIGIO, &input_handler); /* sigaction() 更好*/ fcntl(STDIN_FILENO, F_SETOWN, getpid()); /*F_SETOWN IO命令设置设备文件的拥有者为本进程*/ oflags = fcntl(STDIN_FILENO, F_GETFL); /*获取标准输入的标志位*/ fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC) /*在程序中设置FASYNC标志以保证驱动程序中的fasync()函数得以执行*/
内核中的程序
int fasync_helper(int fd, struct file *filp,int mode, struct fasync_struct **fa); //当打开一个的文件的FASYNC 标志被修改时,调用fasync_helper以便从相关的进程列表中增加或删除文件。 void kill_fasync(struct fasync_struct **fa, int sig, int band); //当数据到达时,可以调用kill_fasync通知所有相关的进程,发送相关的信号,和内容
用户实例
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <sys/stat.h> static void signalio_handler(int signum) { printf("receive a signal from globalfifo,signalnum:%d\n", signum); } void main(void) { int fd, oflags; fd = open("/dev/globalfifo", O_RDWR, S_IRUSR | S_IWUSR); if (fd != -1) { signal(SIGIO, signalio_handler); fcntl(fd, F_SETOWN, getpid()); oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, oflags | FASYNC); while (1) { sleep(100); } } else { printf("device open failure\n"); } }
驱动示例,与异步相关的代码用加黑标注
#include <linux/module.h> #include <linux/types.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/poll.h> #define GLOBALFIFO_SIZE 0x1000 #define FIFO_CLEAR 0x1 #define GLOBALFIFO_MAJOR 231 static int globalfifo_major = GLOBALFIFO_MAJOR; module_param(globalfifo_major, int, S_IRUGO); struct globalfifo_dev { struct cdev cdev; unsigned int current_len; unsigned char mem[GLOBALFIFO_SIZE]; struct mutex mutex; wait_queue_head_t r_wait; wait_queue_head_t w_wait; **struct fasync_struct *async_queue;** }; struct globalfifo_dev *globalfifo_devp; **static int globalfifo_fasync(int fd, struct file *filp, int mode) { struct globalfifo_dev *dev = filp->private_data; return fasync_helper(fd, filp, mode, &dev->async_queue); }** static int globalfifo_open(struct inode *inode, struct file *filp) { filp->private_data = globalfifo_devp; return 0; } static int globalfifo_release(struct inode *inode, struct file *filp) { **globalfifo_fasync(-1, filp, 0);** //从异步通知列表中,删除filp return 0; } static long globalfifo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct globalfifo_dev *dev = filp->private_data; switch (cmd) { case FIFO_CLEAR: mutex_lock(&dev->mutex); dev->current_len = 0; memset(dev->mem, 0, GLOBALFIFO_SIZE); mutex_unlock(&dev->mutex); printk(KERN_INFO "globalfifo is set to zero\n"); break; default: return -EINVAL; } return 0; } static unsigned int globalfifo_poll(struct file *filp, poll_table * wait) { unsigned int mask = 0; struct globalfifo_dev *dev = filp->private_data; mutex_lock(&dev->mutex); poll_wait(filp, &dev->r_wait, wait); poll_wait(filp, &dev->w_wait, wait); if (dev->current_len != 0) { mask |= POLLIN | POLLRDNORM; } if (dev->current_len != GLOBALFIFO_SIZE) { mask |= POLLOUT | POLLWRNORM; } mutex_unlock(&dev->mutex); return mask; } static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { int ret; struct globalfifo_dev *dev = filp->private_data; DECLARE_WAITQUEUE(wait, current); mutex_lock(&dev->mutex); add_wait_queue(&dev->r_wait, &wait); while (dev->current_len == 0) { if (filp->f_flags & O_NONBLOCK) { ret = -EAGAIN; goto out; } __set_current_state(TASK_INTERRUPTIBLE); mutex_unlock(&dev->mutex); schedule(); if (signal_pending(current)) { ret = -ERESTARTSYS; goto out2; } mutex_lock(&dev->mutex); } if (count > dev->current_len) count = dev->current_len; if (copy_to_user(buf, dev->mem, count)) { ret = -EFAULT; goto out; } else { memcpy(dev->mem, dev->mem + count, dev->current_len - count); dev->current_len -= count; printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len); wake_up_interruptible(&dev->w_wait); ret = count; } out: mutex_unlock(&dev->mutex); out2: remove_wait_queue(&dev->w_wait, &wait); set_current_state(TASK_RUNNING); return ret; } static ssize_t globalfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { struct globalfifo_dev *dev = filp->private_data; int ret; DECLARE_WAITQUEUE(wait, current); mutex_lock(&dev->mutex); add_wait_queue(&dev->w_wait, &wait); while (dev->current_len == GLOBALFIFO_SIZE) { if (filp->f_flags & O_NONBLOCK) { ret = -EAGAIN; goto out; } __set_current_state(TASK_INTERRUPTIBLE); mutex_unlock(&dev->mutex); schedule(); if (signal_pending(current)) { ret = -ERESTARTSYS; goto out2; } mutex_lock(&dev->mutex); } if (count > GLOBALFIFO_SIZE - dev->current_len) count = GLOBALFIFO_SIZE - dev->current_len; if (copy_from_user(dev->mem + dev->current_len, buf, count)) { ret = -EFAULT; goto out; } else { dev->current_len += count; printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev->current_len); wake_up_interruptible(&dev->r_wait); **if (dev->async_queue) { kill_fasync(&dev->async_queue, SIGIO, POLL_IN); printk(KERN_DEBUG "%s kill SIGIO\n", __func__); }** ret = count; } out: mutex_unlock(&dev->mutex); out2: remove_wait_queue(&dev->w_wait, &wait); set_current_state(TASK_RUNNING); return ret; } static const struct file_operations globalfifo_fops = { .owner = THIS_MODULE, .read = globalfifo_read, .write = globalfifo_write, .unlocked_ioctl = globalfifo_ioctl, .poll = globalfifo_poll, **.fasync = globalfifo_fasync,** .open = globalfifo_open, .release = globalfifo_release, }; static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index) { int err, devno = MKDEV(globalfifo_major, index); cdev_init(&dev->cdev, &globalfifo_fops); dev->cdev.owner = THIS_MODULE; err = cdev_add(&dev->cdev, devno, 1); if (err) printk(KERN_NOTICE "Error %d adding globalfifo%d", err, index); } static int __init globalfifo_init(void) { int ret; dev_t devno = MKDEV(globalfifo_major, 0); if (globalfifo_major) ret = register_chrdev_region(devno, 1, "globalfifo"); else { ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo"); globalfifo_major = MAJOR(devno); } if (ret < 0) return ret; globalfifo_devp = kzalloc(sizeof(struct globalfifo_dev), GFP_KERNEL); if (!globalfifo_devp) { ret = -ENOMEM; goto fail_malloc; } globalfifo_setup_cdev(globalfifo_devp, 0); mutex_init(&globalfifo_devp->mutex); init_waitqueue_head(&globalfifo_devp->r_wait); init_waitqueue_head(&globalfifo_devp->w_wait); return 0; fail_malloc: unregister_chrdev_region(devno, 1); return ret; } module_init(globalfifo_init); static void __exit globalfifo_exit(void) { cdev_del(&globalfifo_devp->cdev); kfree(globalfifo_devp); unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); } module_exit(globalfifo_exit); MODULE_AUTHOR("Barry Song <baohua@kernel.org>"); MODULE_LICENSE("GPL v2");
相关文章推荐
- 【Linux 驱动】第六章 高级字符驱动程序操作----poll,select,epoll
- 【Linux 驱动】第六章 高级字符驱动程序操作 ----阻塞型I/O
- 【Linux 驱动】第六章 高级字符驱动程序操作 ----ioctl
- 【Linux 驱动】第六章 高级字符驱动程序操作----异步通知
- Linux设备驱动程序第三版学习(5)- 高级字符驱动程序操作 - ioctl .
- linux 高级字符设备驱动 ioctl操作介绍 例程分析实现
- Linux设备驱动程序第三版学习(6)- 高级字符驱动程序操作(续1)- 进程休眠 .
- linux 高级字符设备驱动 ioctl操作介绍 例程分析实现【转】
- Linux设备驱动程序学习(4)-高级字符驱动程序操作[(1)ioctl and llseek]
- Linux设备驱动程序学习(5)-高级字符驱动程序操作〔(2)阻塞型I/O和休眠〕
- linux设备驱动开发-高级字符设备操作poll
- Linux设备驱动程序第三版学习(7)- 高级字符驱动程序操作(续2)- poll/select
- Linux设备驱动程序——高级字符驱动程序操作(阻塞型I/O)
- Linux设备驱动程式学习(5)-高级字符驱动程式操作[(2)阻塞型I/O和休眠]
- linux 高级字符设备驱动 ioctl操作介绍 例程分析实现
- Linux设备驱动程序学习(4) -高级字符驱动程序操作[(1)ioctl and llseek]
- Linux设备驱动程式学习(6)-高级字符驱动程式操作[(3)设备文档的访问控制]
- Linux设备驱动程序学习 高级字符驱动程序操作[阻塞型I/O和非阻塞I/O]【转】
- Linux设备驱动程序——高级字符驱动程序操作(poll机制)
- 高级字符驱动程序操作(Linux设备驱动程序)