先复习一下设备的分类,前面博客有提过的:
字符设备指那些必须以串行顺序依次进行访问的设备。
块设备可以用任意顺序进行访问,为什么?经过了系统的快速缓冲。
网络设备面向数据包的接收和发送设计,没有对应的文件系统节点。
不管怎么样,先看一下字符设备这个结构体吧:
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");
对于字符设备是不是觉得还是麻烦,对!确实麻烦,注册这,注册那的,好在我们还有一个选择,杂项设备驱动,这个它的主设备号规定为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");
字符驱动是基础!要理解一个驱动的架构!驱动的工作就是完成struct file_operation 的回调函数!
上面这两个例子,是很久之前写的,现在回头看看,也有更多的体会!努力加油!
今天下雨,微冷!热点的时候,是否就更好的迁徙了呢?