zoukankan      html  css  js  c++  java
  • 字符设备驱动以及杂项设备驱动

      先复习一下设备的分类,前面博客有提过的:

        字符设备指那些必须以串行顺序依次进行访问的设备。

        块设备可以用任意顺序进行访问,为什么?经过了系统的快速缓冲。

        网络设备面向数据包的接收和发送设计,没有对应的文件系统节点。

      不管怎么样,先看一下字符设备这个结构体吧:

    1 struct cdev {
    2       struct kobject kobj;   //内嵌的kobject对象
    3       struct module *owner;  //所属模块
    4       const struct file_operations *ops;  //文件操作结构体
    5       struct list_head list;  //队列
    6       dev_t dev;  //设备号
    7       unsigned int count;
    8  };

      好吧,我来分析一下,不知道对不对,请指导:

        kobj--作为设备存在于文件系统中的一个纽带,比如它的上层目录,同级设备,下级目录等。

        owner--所属模块,通常都是指向THIS_MODULE。

        ops--这是一个很重要的结构,驱动的工作,基本都是为了实现这个结构体里面的函数。

        list--队列,一般用于添加同种设备,查询,遍历作用???

        dev--设备号,包括主设备号和次设备号。

        count--记录设备被打开的次数???

      设备号的三个函数:

        MAJOR(dev_t dev)      通过设备号dev获取主设备号

        MINOR(dev_t dev)      通过设备号dev获取次设备号

        MKDEV(int major, int minor)  通过主设备号和次设备号生成设备号

      好吧,这些没什么用,讲下字符设备的注册流程吧:

        1,定义一个结构体,里面包含cdev;其实可以直接定义一个cdev结构体就可以了,但是这样做的目的是实现了很好的封装性。

        struct my_cdev {

          struct cdev cdev;

          int flag;

          、、、、

        }

        定义一个struct class *my_class; 这个主要是为了自动创建设备节点;如果你想手动的mknod 那么这个便可以不用。

        2,定义一个file_operations 结构体,并完成其中的一些函数的实现,比如:

    1 /* File operations on mytime,*/
    2 struct file_operations mytime_fops = {
    3     .owner     =     THIS_MODULE,
    4     .read     =     mytime_read,
    5     .write     =     mytime_write,
    6     .unlocked_ioctl     =     mytime_ioctl,
    7     .open     =     mytime_open,
    8     .release =     mytime_release,
    9 };

        3,init函数要完成的工作:   

          分配设备号;
            无主设备号的时候使用:动态(alloc_chrdev_region)

            确定主设备号的时候使用:静态(register_chrdev_region)

          cdev设备初始化
             静态内存定义初始化:
            cdev_init(&my_cdev.cdev, &fops);
            my_cdev.owner = THIS_MODULE;
            动态内存定义初始化:
            struct cdev *my_cdev = cdev_alloc();
            my_cdev->ops = &fops;
            my_cdev->owner = THIS_MODULE;

           注册cdev设备   cdev_add(struct cdev *p, dev_t dev,unsigned count)

           自动创建设备节点:在驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用 class_device_create创建对应的设备。
            struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);
            class_device_create(myclass, NULL, MKDEV(major_num, 0), NULL, “my_device”);

           当然刚才说了,如果你不需要自动创建设备节点,这个可以免去。

        驱动卸载的流程,根据注册的先后顺序,反过来。

        删除字符设备:cdev_del( struct cdev *p);

        释放设备号:void unregister_chrdev_region(dev_t first, unsigned int count);    
        删除设备文件:device_destroy (struct class * class, dev_t devt);
        删除/sys/class下的类:class_destroy(struct class *cls);

        流程大概就是这样,看例子吧,自己写的一个时钟,感觉写得还可以,照顾的方面蛮多的。可以拿来作为参考:

      1 #include <linux/module.h>
      2 #include <linux/moduleparam.h>
      3 #include <linux/init.h>
      4 
      5 #include <linux/kernel.h>        /* printk() */
      6 #include <linux/slab.h>                /* kmalloc() */
      7 #include <linux/fs.h>                /* everything... */
      8 #include <linux/errno.h>        /* error codes */
      9 #include <linux/types.h>        /* size_t */
     10 #include <linux/proc_fs.h>
     11 #include <linux/fcntl.h>        /* O_ACCMODE */
     12 #include <linux/seq_file.h>
     13 #include <linux/cdev.h>
     14 
     15 #include <asm/system.h>                /* cli(), *_flags */
     16 #include <asm/uaccess.h>        /* copy_*_user */
     17 
     18 
     19 #include <linux/sched.h>
     20 #include <linux/mm.h>
     21 #include <asm/io.h>
     22 #include <linux/kd.h>
     23 #include <linux/kbd_kern.h>
     24 #include <linux/ioctl.h>
     25 #include <linux/poll.h>
     26 #include <linux/interrupt.h>
     27 #include <linux/timer.h>
     28 #include <linux/input.h>
     29 #include <linux/platform_device.h>
     30 #include <linux/delay.h>
     31 #include <linux/completion.h>
     32 
     33 #define MYTIME_DEBUG 1;
     34 
     35 struct mytime_dev{  /* need to supplement,*/
     36     struct cdev cdev;
     37     struct timer_list timer;
     38     int timer_count;
     39     struct completion com;
     40 };
     41 
     42 static struct class *mytime_class;
     43 static struct mytime_dev  *mytime_device;
     44 
     45 static dev_t dev_no;
     46 
     47 /* open method, */
     48 static int mytime_open(struct inode *inode, struct file *file)
     49 {
     50     struct mytime_dev *dev;
     51 #ifdef    MYTIME_DEBUG
     52     printk(KERN_INFO "%s:I am here!
    ",__func__);
     53 #endif
     54     dev = container_of(inode->i_cdev, struct mytime_dev, cdev);
     55     file->private_data = dev; /* for other methods */
     56 
     57     return 0; /* success */
     58 }
     59 
     60 /* release method,  */
     61 static int mytime_release(struct inode *inode, struct file *file)
     62 {
     63 #ifdef    MYTIME_DEBUG
     64     printk(KERN_INFO "%s:I am here!
    ",__func__);
     65 #endif
     66     return 0;
     67 }
     68 
     69 /* read method,  */
     70 static ssize_t mytime_read(struct file *file, char __user *buf, 
     71             size_t count, loff_t *pos)
     72 {
     73     struct mytime_dev *dev = file->private_data;
     74     int seconds, minutes, hours;
     75     char time_buf[10] = {'0'};
     76 #ifdef    MYTIME_DEBUG
     77     printk(KERN_INFO "%s:I am here!
    ",__func__);
     78 #endif
     79     /* wait for completion */
     80     wait_for_completion_interruptible(&dev->com);
     81 
     82     seconds = (dev->timer_count)%60;
     83     minutes = (dev->timer_count)/60%60;
     84     hours     = (dev->timer_count)/60/60%24;
     85 
     86     /* this is a foolish way */
     87 /*    time_buf[7] = seconds%10 + '0';
     88     time_buf[6] = seconds/10 + '0';
     89     time_buf[4] = minutes%10 + '0';
     90     time_buf[3] = minutes/10 + '0';
     91     time_buf[1] = hours%10 + '0';
     92     time_buf[0] = hours/10 + '0';*/
     93 
     94     /* this is a smart way */
     95     snprintf(time_buf, 10, "%02d:%02d:%02d
    ",hours, minutes, seconds);    
     96     if(copy_to_user(buf, time_buf, 10))
     97         return -EFAULT;
     98 
     99     return 10;     /* be care of the return value is the count */
    100 }
    101 
    102 /* write method, */
    103 static ssize_t mytime_write(struct file *file, const char __user *buf, 
    104             size_t count, loff_t *pos)
    105 {
    106     struct mytime_dev *dev = file->private_data;
    107     ssize_t retval = -ENOMEM;
    108     char settime[9] = {'0'};
    109     copy_from_user(settime, buf, count);
    110 
    111     /* can we use this function? */
    112     //time_int = atoi(settime);
    113 
    114     retval = count;
    115     if(settime[0] == 'r') 
    116     {
    117         /* awaken the completion */
    118         complete(&dev->com);    
    119     }
    120     else 
    121     {
    122     dev->timer_count = (settime[7]-'0')+((settime[6]-'0')*10)+((settime[4]-'0')*60)+((settime[3]-'0')*10*60)+
    123                 ((settime[1]-'0')*3600)+((settime[0]-'0')*10*3600);
    124     //dev->timer_count = time_int%100 + ((time_int/100)%100)*60 + (time_int/10000)*3600;
    125     }
    126     //printk("%s",settime);
    127     return retval;
    128 }
    129 
    130  /*ioctr method, */
    131 static long mytime_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    132 {
    133     struct mytime_dev *dev = file->private_data;
    134 
    135     /* wait for adding code */    
    136     switch (cmd) {
    137         case 0:
    138             dev->timer_count = 0;
    139         default:
    140             return -EINVAL;
    141     }    
    142     return 0;
    143 }
    144 
    145 /* mytime_timer function */
    146 static void mytime_timer(unsigned long _data)
    147 {
    148     struct mytime_dev *device = (struct mytime_dev *)_data; 
    149     device->timer_count++;    
    150     mod_timer(&device->timer, jiffies+HZ);
    151 }
    152 
    153 /* File operations on mytime,*/
    154 struct file_operations mytime_fops = {
    155     .owner     =     THIS_MODULE,
    156     .read     =     mytime_read,
    157     .write     =     mytime_write,
    158     .unlocked_ioctl     =     mytime_ioctl,
    159     .open     =     mytime_open,
    160     .release =     mytime_release,
    161 };
    162 
    163 /* Driver Exit */
    164 static void  mytime_exit(void)
    165 {
    166 #ifdef     MYTIME_DEBUG
    167     printk("%s:Good Bye!
    ",__func__);
    168 #endif
    169     /* delete timer */
    170     del_timer(&mytime_device->timer);
    171 
    172     /* remove the cdev */
    173     cdev_del(&mytime_device->cdev);
    174     
    175     /* free memory block */
    176     kfree(mytime_device);
    177     
    178     /* release the char device number */
    179     unregister_chrdev_region(dev_no,1);
    180 
    181     /* it will destroy the device node under /dev, */
    182     device_destroy(mytime_class, dev_no);
    183 
    184     /* Destroy class */
    185     class_destroy(mytime_class);    
    186 }
    187 
    188 /* Driver Initialization,*/
    189 static int  mytime_init(void)
    190 {
    191     int result;
    192 #ifdef     MYTIME_DEBUG
    193     printk("%s:I am here!
    ",__func__);
    194 #endif
    195     /* asking for a dynamic major*/
    196     result = alloc_chrdev_region(&dev_no, 0, 1, "mytime_dev");
    197     if(result < 0) {
    198         printk(KERN_WARNING "mytime_dev:can't get device number!
    ");
    199         return result;
    200     }
    201     else {
    202         printk("the device number is %d !
    ",dev_no);
    203     }
    204     
    205     /* allocate the devices*/
    206     mytime_device = kzalloc(sizeof(struct mytime_dev),GFP_KERNEL);
    207     if(!mytime_device) {
    208         result = -ENOMEM;
    209         goto fail;
    210     }
    211 //    memset(mytime_device, 0, sizeof(struct mytime_dev));
    212 
    213     /* set up the char_dev structure for this device.*/
    214 
    215     /* when declare cdev as pointer, use cdev_alloc()*/
    216 //    (mytime_device->cdev) = cdev_alloc();
    217     /* if declare cdev as structure, use cdev_init */
    218     cdev_init(&mytime_device->cdev, &mytime_fops);
    219     mytime_device->cdev.owner = THIS_MODULE;
    220 //    mytime_device->cdev.ops = &mytime_fops;    //for cdev_alloc()
    221     
    222     if(cdev_add(&mytime_device->cdev, dev_no,1)) {
    223         printk("%s:add cdev failde!",__func__);
    224         return 1;
    225     }
    226     /* initialize competion */
    227     init_completion(&mytime_device->com);
    228 
    229     /* setup a timer */
    230     setup_timer(&mytime_device->timer, mytime_timer, (unsigned long)mytime_device);
    231     mytime_device->timer.expires = jiffies + HZ;
    232 
    233     /* add this timer to timer queue */
    234     add_timer(&mytime_device->timer);
    235 
    236     /* create sysfs entries */
    237     mytime_class = class_create(THIS_MODULE, "mytime_class");
    238 
    239     /* Send uevents to udev, so it'll create /dev nodes */
    240     device_create(mytime_class, NULL, dev_no, NULL, "mytime_dev");
    241     
    242     return 0;     /*succeed*/
    243     
    244     fail:
    245         mytime_exit();
    246         return result;
    247 }
    248 
    249 module_init(mytime_init);
    250 module_exit(mytime_exit);
    251 
    252 MODULE_AUTHOR("Chen Yucai, ksime");
    253 MODULE_DESCRIPTION("Time Char Driver");
    254 MODULE_LICENSE("GPL");
    View Code

       对于字符设备是不是觉得还是麻烦,对!确实麻烦,注册这,注册那的,好在我们还有一个选择,杂项设备驱动,这个它的主设备号规定为10,它帮我们省掉了很多注册或者初始化的操作,只需要调用它的API就可以了。下面我们来看看:

      1,定义一个struct miscdevice misc;

      2,init注册:ret = misc_register(&misc);

      3,exit卸载:misc_deregister(&misc);

       真的就这么简单么?真的!所以,一般的字符设备驱动,我们都使用杂项驱动!!!看例子吧,根据上面那个改的:

      1 #include <linux/module.h>
      2 #include <linux/sched.h>
      3 #include <linux/mm.h>
      4 #include <linux/init.h>
      5 #include <asm/io.h>
      6 #include <asm/uaccess.h>
      7 //#include <asm/arch/hardware.h>
      8 #include <linux/kd.h>
      9 #include <linux/fs.h>
     10 #include <linux/kbd_kern.h>
     11 #include <linux/ioctl.h>
     12 #include <linux/poll.h>
     13 #include <linux/interrupt.h>
     14 #include <linux/timer.h>
     15 #include <linux/input.h>
     16 #include <linux/miscdevice.h>
     17 #include <linux/platform_device.h>
     18 #include <linux/delay.h>
     19 #include <linux/kernel.h>
     20 #include <linux/completion.h>
     21 
     22 #define MISC_TIME_DEBUG 1;
     23 
     24 struct misc_time {
     25     struct miscdevice misc;
     26     struct completion com;
     27     struct timer_list timer;
     28     int timer_count;
     29 };
     30 
     31 static struct misc_time time_device;
     32 
     33 static int misc_time_open(struct inode *inode, struct file *file)
     34 {
     35     struct misc_time *time;
     36 #ifdef MISC_TIME_DEBUG
     37     printk(KERN_INFO "MISC_TIME open.
    ");
     38 #endif
     39     time = &time_device;
     40     if(!time)
     41         return -ENODEV;
     42     file->private_data = time;
     43     
     44     return 0;
     45 }
     46 
     47 static int misc_time_release(struct inode *inode, struct file *file)
     48 {
     49 #ifdef MISC_TIME_DEBUG
     50     printk(KERN_INFO "MISC_TIME release.
    ");
     51 #endif
     52     return 0;
     53 }
     54 
     55 static ssize_t misc_time_read(struct file *file, char __user *buf, size_t count,loff_t *pos)
     56 {
     57     struct misc_time *time = file->private_data;
     58         int seconds, minutes, hours;
     59     char time_buf[10] = {'0'};
     60 #ifdef    MISC_TIME_DEBUG
     61     printk(KERN_INFO "%s:I am here!
    ",__func__);
     62 #endif
     63     /* wait for completion */
     64     wait_for_completion_interruptible(&time->com);
     65 
     66     seconds = (time->timer_count)%60;
     67     minutes = (time->timer_count)/60%60;
     68     hours     = (time->timer_count)/60/60%24;
     69 
     70     /* this is a foolish way */
     71 /*    time_buf[7] = seconds%10 + '0';
     72     time_buf[6] = seconds/10 + '0';
     73     time_buf[4] = minutes%10 + '0';
     74     time_buf[3] = minutes/10 + '0';
     75     time_buf[1] = hours%10 + '0';
     76     time_buf[0] = hours/10 + '0';*/
     77 
     78     /* this is a smart way */
     79     snprintf(time_buf, 10, "%02d:%02d:%02d
    ",hours, minutes, seconds);    
     80     if(copy_to_user(buf, time_buf, 10))
     81         return -EFAULT;
     82 
     83     return 10;     /* be care of the return value is the count */
     84 }
     85 
     86 static ssize_t misc_time_write(struct file *file, const char __user *buf, 
     87             size_t count, loff_t *pos)
     88 {
     89     struct misc_time *time = file->private_data;
     90     ssize_t retval = -ENOMEM;
     91     char settime[9] = {'0'};
     92     copy_from_user(settime, buf, count);
     93 
     94     /* can we use this function? */
     95     //time_int = atoi(settime);
     96 
     97     retval = count;
     98     if(settime[0] == 'r') 
     99     {
    100         /* awaken the completion */
    101         complete(&time->com);    
    102     }
    103     else 
    104     {
    105     time->timer_count = (settime[7]-'0')+((settime[6]-'0')*10)+((settime[4]-'0')*60)+((settime[3]-'0')*10*60)+
    106                 ((settime[1]-'0')*3600)+((settime[0]-'0')*10*3600);
    107     //time->timer_count = time_int%100 + ((time_int/100)%100)*60 + (time_int/10000)*3600;
    108     }
    109     //printk("%s",settime);
    110     return retval;
    111 }
    112 
    113 static long misc_time_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    114 {
    115     struct misc_time *time = file->private_data;
    116 
    117     /* wait for adding code */    
    118     switch (cmd) {
    119         case 0:
    120             time->timer_count = 0;
    121         default:
    122             return -EINVAL;
    123     }    
    124     return 0;
    125 }
    126 
    127 static void misc_timer(unsigned long _data)
    128 {
    129     struct misc_time *time = (struct misc_time *)_data; 
    130     time->timer_count++;    
    131     mod_timer(&time->timer, jiffies+HZ);
    132 }
    133 
    134 
    135 struct file_operations misc_time_fops = {
    136     .owner     =     THIS_MODULE,
    137     .read     =     misc_time_read,
    138     .write     =     misc_time_write,
    139     .unlocked_ioctl     =     misc_time_ioctl,
    140     .open     =     misc_time_open,
    141     .release =     misc_time_release,
    142 };
    143 
    144 static struct misc_time time_device = {
    145     .misc = {
    146         .minor = MISC_DYNAMIC_MINOR,
    147         .name = "misc_time",
    148         .fops = &misc_time_fops,
    149     },
    150 };
    151 
    152 static int __init misc_time_init(void)
    153 {
    154     int ret;
    155 
    156     //allocate for time data
    157     //register the misc driver
    158     ret = misc_register(&time_device.misc);
    159     if(unlikely(ret)) {
    160         printk(KERN_ERR "misc time driver failed to registe "
    161             "the misc drivers.
    ");
    162         return ret;
    163     }
    164     init_completion(&(time_device.com));
    165     setup_timer(&time_device.timer, misc_timer, (unsigned long)&time_device);
    166     time_device.timer.expires = jiffies + HZ;
    167 
    168     /* add this timer to timer queue */
    169     add_timer(&time_device.timer);
    170     
    171     return 0;
    172 }
    173 
    174 static void __exit misc_time_exit(void)
    175 {
    176     printk(KERN_INFO "MISC_TIME exit
    ");
    177     del_timer(&time_device.timer);
    178     misc_deregister(&time_device.misc);
    179 }
    180 
    181 
    182 module_init(misc_time_init);
    183 module_exit(misc_time_exit);
    184 
    185 MODULE_AUTHOR("Chen Yucai, ksime");
    186 MODULE_DESCRIPTION("Time Misc Driver");
    187 MODULE_LICENSE("GPL");
    View Code

       字符驱动是基础!要理解一个驱动的架构!驱动的工作就是完成struct file_operation 的回调函数!

      上面这两个例子,是很久之前写的,现在回头看看,也有更多的体会!努力加油!

      今天下雨,微冷!热点的时候,是否就更好的迁徙了呢?

  • 相关阅读:
    GitHub入门之一:使用github下载项目
    Android Fragment 真正的完全解析(下)
    Android Fragment 真正的完全解析(上)
    c# 发送邮件
    SmartThreadPool
    虚拟机
    相关系数
    为枚举类型添加说明 zt
    MD5
    hashcode
  • 原文地址:https://www.cnblogs.com/cyc2009/p/4348809.html
Copyright © 2011-2022 走看看