linux 驱动——高级字符驱动程序操作

2023-11-13

内容

ioctl 的

  1. ioctl 的系统概念
  2. 与用户空间同步的方法
  3. 进程休眠
  4. 非阻塞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");
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux 驱动——高级字符驱动程序操作 的相关文章

  • TransUNet: Transformers Make Strong Encoders for Medical Image Segmentation

    TransUNet Transformers Make Strong Encoders for Medical Image Segmentation 发表时间 2021年2月 发表期刊 Arxiv Abstract 医学图像分割是发展医疗保
  • Spring- 上传文件 MultipartFile.transferTo() 报错 FileNotFoundException

    上传文件时 使用MultipartFile transferTo 将文件保存到本地路径 报错 java io IOException java io FileNotFoundException C Users XXXXX AppData L
  • vue单文件组件1(webpack打包)

    一 vue单文件组件开发流程 webpack打包 1 源文件目录结构 2 package json 3 webpack config js HTML Webpack Plugin依据html模板生成一个自动引用你打包后的文件 js或css
  • 【数据结构(C语言描述)】环形队列

    目录 一 基础知识 二 数组实现环队 2 1 初始化 2 2 判断环队是否为空 2 3 判断环队是否为满 2 4 入队 2 5 出队 2 6 取队头元素 2 7 取队尾元素 2 8 销毁环队 三 链表实现环队 3 1 初始化 3 2 判断环
  • STM32学习心得三十二:CAN通信基础知识、原理、配置及实验

    记录一下 方便以后翻阅 主要内容 1 CAN通信基础知识 2 STM32 CAN控制器简介 3 相关实验代码解读 参考资料 STM32中文参考手册 V10 第22章 控制器局域网 bxCAN 实验功能 CAN实验需要两个开发板 系统启动后
  • ctfshow-萌新-web1( 利用intval函数的特性获取敏感数据)

    ctf show 萌新模块的web1关 这一关考察的是intval 函数转换字符串时的特性以及SQL的拼接绕过 这一关直接就给了源码 并提示我们 id 1000 时 就是flag 先分析一下源码 首先是 intval 函数将参数id转换为数
  • PostgreSQL学习研究笔记(一)

    何为PostgreSQL PostgreSQL 是以加州大学伯克利分校计算机系开发的 postgres 版本 4 2 为基础的对象关系型数据库管理系统 PostgreSQL是最初的伯克利代码的开源继承者 任何人都可以以任何目的免费使用 修改
  • T-Kernel Error Code

    参考 tk errno h define E OK 0 Completed successfully define E SYS ERCD 5 0 System error define E NOCOP ERCD 6 0 Coprocesso
  • linux—通配符详解及总结

    本博客主要详解关于linux中常使用的通配符的知识点 主要分为三个步骤 通配符 和 的详细解释 举例截图说明 个人的总结理解 一 通配符 和 的详细解释 ps 因为通配符 中可添加很多变量 所以这里我会简单举出几个例子来说明 其他情况请类比
  • R 和 Rstudio 在线更新

    R 在线更新 最近安装 R 包的时候老是会遇到有些包不适应老版本 无奈还是更新了新的版本 卸载重装就太麻烦了 而且以前的包还需要重新加载 在线更新是最好的方法 可以直接在 Rstudio 中直接更新 直接运行以下命令就行 install p
  • Python还能这样学?独一档的学习路线与方法!两个月就能彻底掌握

    为什么要选择学习Python 我大学本专业当时学的是过时很久的工程物流管理 经常跟学长学姐们聊到他们的就业情况 然后自己也对未来的就业之路产生了很大的怀疑 后面经过一些了解 以及学长学姐的介绍 知道了Python 后面深入的了解了后 我果断
  • 基于Protege的知识建模实战

    一 Protege简介 用途和特点 1 Protege简介 Protege是斯坦福大学医学院生物信息研究中心基于Java开发的本体编辑和本体开发工具 也是基于知识的编辑器 属于开放源代码软件 这个软件主要用于语义网中本体的构建 是语义网中本
  • 华为OD机试真题B卷 Java 实现【停车场车辆统计】,附详细解题思路

    一 题目描述 特定大小的停车场 数组cars 表示 其中1表示有车 0表示没车 车辆大小不一 小车占一个车位 长度1 货车占两个车位 长度2 卡车占三个车位 长度3 统计停车场最少可以停多少辆车 返回具体的数目 二 输入描述 整型字符串数组
  • DHCP DNS 综合案例分析

    1 首先我们来配置一下192 168 1 2这台机器 1 1 操作系统 windows server 2003 R2 1 2 IP 192 168 1 2 24 GW 1922 168 1 1 DNS 192 168 1 2 注意 这台机器
  • 阿里云盘内测_【邀请码】阿里云盘内测码分享

    备受瞩目的阿里云网盘已在苹果App Store上架 并更名 阿里云盘 最新版本更名为v1 0 1 8 月下旬 阿里巴巴推出了一款名为 阿里云网盘 的独立 App 该应用开发者为阿里旗下的阿里云团队 定位是为 C 端用户提供可靠安全的存储备份
  • CUBEIDE 使用指南

    cubeIDE includes 下一直有一个错误的路径 如图 不知道什么原因引入了一个错误的路径怎么也消不掉 最后发现在工程文件里 用记事本等打开 cproject文件 删掉对应路径就好了 图是已经删掉的了 2 include 路径问题
  • QT信号与槽机制

    QT开发 QT信号与槽机制 一 QT消息模型 QT封装了具体操作系统的消息机制 遵循经典的GUI消息驱动事件模型 QT定义了与操作系统消息相关的自己的概念 即信号与槽 信号signal是由操作系统产生的消息 槽slot是程序中的消息处理函数
  • Openmv,stm32串口(定时器配置)

    新手学习记录中 得先有from pyb import UART的包以及import json 初始化uart UART 3 115200 表示是在串口3处 其波特率为115200 需要看openmv的原理图 看uart3的TX和RX是哪两个
  • sklearn矩阵分解类库学习

    sklearn decomposition模块提供矩阵分解算法 其他PCA NMF 或ICA 其中大部分算法都被视为降维技术 主成分分析 sklearn decomposition PCA n components None copy Tr

随机推荐