字符设备驱动以及杂项设备驱动

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

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

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

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

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

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!\n",__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!\n",__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!\n",__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\n",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!\n",__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!\n",__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!\n");
199         return result;
200     }
201     else {
202         printk("the device number is %d !\n",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.\n");
 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.\n");
 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!\n",__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\n",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.\n");
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\n");
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 的回调函数!

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

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

posted on 2015-03-18 22:13  tiger_chen  阅读(348)  评论(0编辑  收藏  举报

导航