zoukankan      html  css  js  c++  java
  • linux字符设备驱动程序怎么写

    摘要:linux设备驱动程序第三版第三章笔记

    1.scull:simple character utility for loading localities.

    2.scull0-scull3: 4个global and persistent设备,映射到的物理内存区是不会消失的。

      scullpipe0-scullpipe3:4个FIFO设备,一个进程读一个进程写。

    3.主次编号:

      3.1

      major number标志了设备相联系的driver。

      minor number用来决定引用了哪个设备。

      3.2 设备编号内部表示:

      dev_t类型<linux/types.h>中含有设备编号

      获得dev_t的主次编号,使用<linux/kdev_t.h>中的:

        MAJOR(dev_t dev); 
        MINOR(dev_t dev);
     将主次编号转换为dev_t使用:
      MKDEV(int major, int minor); 

      3.3分配和释放设备编号:
      头文件<linux/fs.h>
    int register_chrdev_region(dev_t first, unsigned int count, char *name);            
    
    int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
    //动态分配,一旦分配了,可以在/proc/devices中读取
    void unregister_chrdev_region(dev_t first, unsigned int count); 


    3.4主编号的动态分配:
    一旦主编号动态分配了,就可以到/proc/device中来读取主编号,可以使用一个script来代替调用insmod,在insmod以后创建special files。
    下面就是脚本scull_load:
    #!/bin/sh
    module="scull"
    device="scull"
    mode="664"
    
    # invoke insmod with all arguments we got
    # and use a pathname, as newer modutils don't look in . by default
    /sbin/insmod ./$module.ko $* || exit 1
    
    # remove stale nodes
    rm -f /dev/${device}[0-3]
    
    major=$(awk "\\$2==\"$module\" {print \\$1}" /proc/devices) 
    mknod /dev/${device}0 c $major 0
    mknod /dev/${device}1 c $major 1
    mknod /dev/${device}2 c $major 2
    mknod /dev/${device}3 c $major 3
    
    # give appropriate group/permissions, and change the group.
    # Not all distributions have staff, some have "wheel" instead.
    group="staff"
    grep -q '^staff:' /etc/group || group="wheel"
    
    chgrp $group /dev/${device}[0-3]
    chmod $mode /dev/${device}[0-3]
    

     另外还有一个脚本scull_unload来清理/dev目录并去除模块。

    分配主编号的示例代码:

    if (scull_major) {
     dev = MKDEV(scull_major, scull_minor);
     result = register_chrdev_region(dev, scull_nr_devs, "scull");
    } else {
     result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");
     scull_major = MAJOR(dev);
    }
    if (result < 0) {
     printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
     return result;
    }
    

      

    4.file_operation:

    file_operation结构定义在<linux/fs.h>是一个函数指针集合。每个打开文件用file指针指向,包含一个称为f_op的成员指向file_operation结构。这些函数负责实现系统调用。
    当parameter包含__user这样的字符串的时候,这是一种documentation,表示指向的是一个user-space地址。例如:char __user *。

    文件操作函数有许多,scull设备驱动仅仅实现相对重要的函数:
    struct file_operations scull_fops = {
        .owner =    THIS_MODULE,
        .llseek =   scull_llseek,
        .read =     scull_read,
        .write =    scull_write,
        .ioctl =    scull_ioctl,
        .open =     scull_open,
        .release =  scull_release,
    };
    

     

    5.struct file:(代表打开文件描述符) 

    struct file 定义在<linux/fs.h>,它与用户空间FILE没有任何关系,它由内核在open时创建,并传递给在文件上操作的任何函数,直至关闭,最后释放这个数据结构。
    内核中struct file的指针通常是file 或者filp。
    其中重要成员:
    mode_t f_mode;
    struct file_operations *f_op;
    void *private_data;
    loff_t f_pos;

    struct dentry *f_dentry;

    6.inode(表示文件)

    inode结构由内核在内部用来表示文件,与file不同,可以有许多struct file指针指向一个inode,但是inode是唯一的。

    inode有大量文件信息,但是只有两个成员对于编写驱动有用:

    1)dev_t i_rdev; 包含实际设备编号

    2)struct cdev * i_cdev; 代表字符设备。

    unsigned int iminor(struct inode *inode);
    unsigned int imajor(struct inode *inode);

    7.字符设备注册
    内核中使用struct cdev来代表字符设备,<linux/cdev.h>中包含了这个结构和它相关连的函数。下面的
    struct cdev *my_cdev = cdev_alloc();
    my_cdev->ops = &my_fops;
    //----实际上SCULL用的是特定的结构,如下-------------------
    void cdev_init(struct cdev *cdev, struct file_operations *fops);
    //添加字符设备
    int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
    //去除字符设备
    void cdev_del(struct cdev *dev);
    

      scull设备使用一个stuct scull_dev类型的结构来表示每个设备:

    1 struct scull_dev { 
    2  struct scull_qset *data;          /* Pointer to first quantum set */ 
    3  int quantum;                  /* the current quantum size */ 
    4  int qset;                      /* the current array size */ 
    5  unsigned long size;             /* amount of data stored here */ 
    6  unsigned int access_key;         /* used by sculluid and scullpriv */ 
    7  struct semaphore sem;          /* mutual exclusion semaphore  */ 
    8  struct cdev cdev;               /* Char device structure */
    9 };

    上述结构初始化过程如下:

    static void scull_setup_cdev(struct scull_dev *dev, int index)
    {
     int err, devno = MKDEV(scull_major, scull_minor + index);
    
     cdev_init(&dev->cdev, &scull_fops);
     dev->cdev.owner = THIS_MODULE;
     dev->cdev.ops = &scull_fops;
     err = cdev_add (&dev->cdev, devno, 1);
     /* Fail gracefully if need be */
     if (err)
     printk(KERN_NOTICE "Error %d adding scull%d", err, index);
    } 

     8.open和release:

    open 方法的原型是:

    int (*open)(struct inode *inode, struct file *filp);

    
    

    1)确定打开的是哪个设备:

      a.通过INODE参数:inode参数成员i_cdev包含cdev,转化成scull_dev类型(如下dev)。

    
    

    struct scull_dev *dev;                              /* device information */

    dev = container_of(inode->i_cdev, struct scull_dev, cdev);

    filp->private_data = dev;                            /* for other methods */

    
    

      b.查看inode结构的次编号,如果是用register_chrdev注册的设备,就必须用这种方法。

    最终,scull_open的代码如下:

    
    
    int scull_open(struct inode *inode, struct file *filp)
    {
            struct scull_dev *dev; /* device information */
            dev = container_of(inode->i_cdev, struct scull_dev, cdev);
            filp->private_data = dev; /* for other methods */
    
            /* now trim to 0 the length of the device if open was write-only */
            if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
            {
                    scull_trim(dev); /* ignore errors */
            }
            return 0; /* success */
    }
    
    
    

      2) release 方法:

        目的:释放open分配在filp->private_data中的任何东西,在最后的close关闭设备。

    
    
    int scull_release(struct inode *inode, struct file *filp)
    {
     return 0;
    }
    
    
    

      9.read和write:

    ssize_t read(struct file *filp, char __user *buff,size_t count, loff_t *offp);
    ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);
    

      

    
    
    ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
    {
            struct scull_dev *dev = filp->private_data;
            struct scull_qset *dptr; /* the first listitem */
            int quantum = dev->quantum, qset = dev->qset;
            int itemsize = quantum * qset; /* how many bytes in the listitem */
            int item, s_pos, q_pos, rest;
            ssize_t retval = 0;
    
            if (down_interruptible(&dev->sem))
                    return -ERESTARTSYS;
            if (*f_pos >= dev->size)
                    goto out;
            if (*f_pos + count > dev->size)
                    count = dev->size - *f_pos;
    
            /* find listitem, qset index, and offset in the quantum */
            item = (long)*f_pos / itemsize;
            rest = (long)*f_pos % itemsize;
            s_pos = rest / quantum;
            q_pos = rest % quantum;
    
            /* follow the list up to the right position (defined elsewhere) */
            dptr = scull_follow(dev, item);
            if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])
                    goto out; /* don't fill holes */
    
            /* read only up to the end of this quantum */
            if (count > quantum - q_pos)
                    count = quantum - q_pos;
    
            if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count))
            {
                    retval = -EFAULT;
                    goto out;
    
            }
            *f_pos += count;
            retval = count;
    
    out:
            up(&dev->sem);
            return retval;
    }
    
    
    

      

    ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
    {
            struct scull_dev *dev = filp->private_data;
            struct scull_qset *dptr;
            int quantum = dev->quantum, qset = dev->qset;
            int itemsize = quantum * qset;
            int item, s_pos, q_pos, rest;
            ssize_t retval = -ENOMEM; /* value used in "goto out" statements */
            if (down_interruptible(&dev->sem))
                    return -ERESTARTSYS;
    
            /* find listitem, qset index and offset in the quantum */
            item = (long)*f_pos / itemsize;
            rest = (long)*f_pos % itemsize;
            s_pos = rest / quantum;
            q_pos = rest % quantum;
            /* follow the list up to the right position */
            dptr = scull_follow(dev, item);
            if (dptr == NULL)
                    goto out;
            if (!dptr->data)
            {
                    dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
                    if (!dptr->data)
                            goto out;
                    memset(dptr->data, 0, qset * sizeof(char *));
            }
            if (!dptr->data[s_pos])
            {
                    dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
                    if (!dptr->data[s_pos])
    
                            goto out;
            }
            /* write only up to the end of this quantum */
            if (count > quantum - q_pos)
    
                    count = quantum - q_pos;
            if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count))
            {
                    retval = -EFAULT;
                    goto out;
    
            }
            *f_pos += count;
            retval = count;
    
            /* update the size */
            if (dev->size < *f_pos)
                    dev->size = *f_pos;
    
    out:
            up(&dev->sem);
            return retval;
    
    }
    

      



    ===============================分割线================================================================
    一整天就搞了这麽个东西,做笔记真是个累人的事情,要提高效率,感觉自己特别急躁,可能是没有分派好任务的缘故。
    作业很多,压力很大,但我却并没有尽全力,反而玩游戏什么的浪费了不少时间。sign!绝对不能这样了。

  • 相关阅读:
    蒟蒻の搜索学习总结
    蒟蒻の红黑树总结
    数据结构与算法
    linux常用命令
    Linux防火墙Firewall和Iptables的使用
    Spring Boot 自定义Intercepter
    Spring Boot 自定义Filter
    SpringBoot 全局异常配置
    SpringBoot设置支持跨域请求
    springboot整合shiro安全框架
  • 原文地址:https://www.cnblogs.com/bubbler/p/2500416.html
Copyright © 2011-2022 走看看