linux驱动开发(十)——misc杂散设备
1:什么是misc驱动模型?
2:为什么要有misc驱动模型?
3:misc驱动模型的代码实现
4:misc驱动模型实战
参考:
http://blog.csdn.net/yicao821/article/details/6785738
http://www.thinksaas.cn/topics/0/507/507168.html
http://www.cnblogs.com/fellow1988/p/6235080.html
https://www.zhihu.com/question/21508904
http://www.cnblogs.com/snake-hand/p/3212483.html
http://blog.csdn.net/chenlong12580/article/details/7339127
--------------------------------------------------------------------------------------------------------------------------------------------------------------
1:什么是misc驱动模型
Linux包含了许多的设备驱动类型,而不管分类有多细,总会有些漏网的,这就是我们经常说到的“其他的”等等。
在Linux里面,把无法归类的五花八门的设备定义为混杂设备(用miscdevice结构体来描述)。Linux/内核所提供的miscdevice有很强的包容性。如NVRAM,看门狗,DS1286等实时时钟,字符LCD,AMD 768随机数发生器。
miscdevice共享一个主设备号MISC_MAJOR(10),但此设备号不同,所有的miscdevice设备形成一个链表,对设备访问时内核根据次设备号查找对应的 miscdevice设备,然后调用其中的file_operations结构体中注册的文件操作接口进程操作。
2:为什么要有misc驱动模型
链接:https://www.zhihu.com/question/21508904/answer/19623523
第一,节省主设备号:
使用普通字符设备,不管该驱动的主设备号是静态还是动态分配,都会消耗一个主设备号,这太浪费了。而且如果你的这个驱动最终会提交到内核主线版本上的话,需要申请一个专门的主设备号,这也麻烦。
如果使用misc驱动的话就好多了。因为内核中已经为misc驱动分配了一个主设备号。当系统中拥有多个misc设备驱动时,那么它们的主设备号相同,而用子设备号来区分它们。
第二,使用简单:
有时候驱动开发人员需要开发一个功能较简单的字符设备驱动,导出接口让用户空间程序方便地控制硬件,只需要使用misc子系统提供的接口即可快速地创建一个misc设备驱动。
当使用普通的字符设备驱动时,如果开发人员需要导出操作接口给用户空间的话,需要自己去注册字符驱动,并创建字符设备class以自动在/dev下生成设备节点,相对麻烦一点。而misc驱动则无需考虑这些,基本上只需要把一些基本信息通过struct miscdevice交给misc_register()去处理即可。
所以说misc驱动模型让我们很简单的在底层实现了字符设备驱动,并且在在应用层给予了一定的接口,节省了主设备号;其实就相当于一个杂货铺,乱七八糟的字符设备驱动模型都可以往里面
堆。
3:驱动模型代码实现:
misc驱动的实现代码在driver/char/misc.c目录下,
misc_init函数:
1 static int __init misc_init(void)
2 {
3 int err;
4
5 #ifdef CONFIG_PROC_FS
6 proc_create("misc", 0, NULL, &misc_proc_fops);
7 #endif
8 misc_class = class_create(THIS_MODULE, "misc");
9 err = PTR_ERR(misc_class);
10 if (IS_ERR(misc_class))
11 goto fail_remove;
12
13 err = -EIO;
14 if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
15 goto fail_printk;
16 misc_class->devnode = misc_devnode;
17 return 0;
18
19 fail_printk:
20 printk("unable to get major %d for misc devices\n", MISC_MAJOR);
21 class_destroy(misc_class);
22 fail_remove:
23 remove_proc_entry("misc", NULL);
24 return err;
25 }
26 subsys_initcall(misc_init);
misc_init
class_create 创建了一个名为misc的类
register_chrdev(MISC_MAJOR,"misc",&misc_fops) 使用register_chrdev注册了一个字符设备驱动,主设备号为MISC_MAJOR(10);
1 static const struct file_operations misc_fops = {
2 .owner = THIS_MODULE,
3 .open = misc_open,
4 };
misc类型驱动提供了一个统一.open函数misc_open函数;
misc_open 这个函数的实质是通过inode找到misc类的次设备号minor,然后在通过次设备号和misc链表的次设备号进行匹配,匹配好以后取出
1 static int misc_open(struct inode * inode, struct file * file)
2 {
3 int minor = iminor(inode);
4 struct miscdevice *c;
5 int err = -ENODEV;
6 const struct file_operations *old_fops, *new_fops = NULL;
7
8 mutex_lock(&misc_mtx);
9
10 list_for_each_entry(c, &misc_list, list) {
11 if (c->minor == minor) {
12 new_fops = fops_get(c->fops);
13 break;
14 }
15 }
16
17 if (!new_fops) {
18 mutex_unlock(&misc_mtx);
19 request_module("char-major-%d-%d", MISC_MAJOR, minor);
20 mutex_lock(&misc_mtx);
21
22 list_for_each_entry(c, &misc_list, list) {
23 if (c->minor == minor) {
24 new_fops = fops_get(c->fops);
25 break;
26 }
27 }
28 if (!new_fops)
29 goto fail;
30 }
31
32 err = 0;
33 old_fops = file->f_op;
34 file->f_op = new_fops;
35 if (file->f_op->open) {
36 file->private_data = c;
37 err=file->f_op->open(inode,file);
38 if (err) {
39 fops_put(file->f_op);
40 file->f_op = fops_get(old_fops);
41 }
42 }
43 fops_put(old_fops);
44 fail:
45 mutex_unlock(&misc_mtx);
46 return err;
47 }
1 int misc_register(struct miscdevice * misc)
2 {
3 struct miscdevice *c;
4 dev_t dev;
5 int err = 0;
6
7 INIT_LIST_HEAD(&misc->list);
8
9 mutex_lock(&misc_mtx);
10 list_for_each_entry(c, &misc_list, list) {
11 if (c->minor == misc->minor) {
12 mutex_unlock(&misc_mtx);
13 return -EBUSY;
14 }
15 }
16
17 if (misc->minor == MISC_DYNAMIC_MINOR) {
18 int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
19 if (i >= DYNAMIC_MINORS) {
20 mutex_unlock(&misc_mtx);
21 return -EBUSY;
22 }
23 misc->minor = DYNAMIC_MINORS - i - 1;
24 set_bit(i, misc_minors);
25 }
26
27 dev = MKDEV(MISC_MAJOR, misc->minor);
28
29 misc->this_device = device_create(misc_class, misc->parent, dev,
30 misc, "%s", misc->name);
31 if (IS_ERR(misc->this_device)) {
32 int i = DYNAMIC_MINORS - misc->minor - 1;
33 if (i < DYNAMIC_MINORS && i >= 0)
34 clear_bit(i, misc_minors);
35 err = PTR_ERR(misc->this_device);
36 goto out;
37 }
38
39 /*
40 * Add it to the front, so that later devices can "override"
41 * earlier defaults
42 */
43 list_add(&misc->list, &misc_list);
44 out:
45 mutex_unlock(&misc_mtx);
46 return err;
47 }
在include/linux/miscdevice.h中定义了miscdevice 结构体,所有的misc模型驱动设备;都在内核围护的一个misc_list链表中;
内核维护一个misc_list链表,misc设备在misc_register注册的时候链接到这个链表,在misc_deregister中解除链接。
1 struct miscdevice {
2 int minor; //次设备号,若为 MISC_DYNAMIC_MINOR 自动分配
3 const char *name; //设备名
4 const struct file_operations *fops; //设备文件操作结构体
5 struct list_head list; //misc_list链表头
6 struct device *parent;
7 struct device *this_device;
8 const char *nodename;
9 mode_t mode;
10 };
misc_register函数
1 int misc_register(struct miscdevice * misc)
2 {
3 struct miscdevice *c;
4 dev_t dev;
5 int err = 0;
6
7 INIT_LIST_HEAD(&misc->list);
8
9 mutex_lock(&misc_mtx);
10 list_for_each_entry(c, &misc_list, list) {
11 if (c->minor == misc->minor) {
12 mutex_unlock(&misc_mtx);
13 return -EBUSY;
14 }
15 }
16
17 if (misc->minor == MISC_DYNAMIC_MINOR) {
18 int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
19 if (i >= DYNAMIC_MINORS) {
20 mutex_unlock(&misc_mtx);
21 return -EBUSY;
22 }
23 misc->minor = DYNAMIC_MINORS - i - 1;
24 set_bit(i, misc_minors);
25 }
26
27 dev = MKDEV(MISC_MAJOR, misc->minor);
28
29 misc->this_device = device_create(misc_class, misc->parent, dev,
30 misc, "%s", misc->name);
31 if (IS_ERR(misc->this_device)) {
32 int i = DYNAMIC_MINORS - misc->minor - 1;
33 if (i < DYNAMIC_MINORS && i >= 0)
34 clear_bit(i, misc_minors);
35 err = PTR_ERR(misc->this_device);
36 goto out;
37 }
38
39 /*
40 * Add it to the front, so that later devices can "override"
41 * earlier defaults
42 */
43 list_add(&misc->list, &misc_list);
44 out:
45 mutex_unlock(&misc_mtx);
46 return err;
47 }
misc_register
misc->this_device = device_create(misc_class, misc->parent, dev, misc, "%s", misc->name);
调用这个函数来初创建设备;
misc_deregister函数来取消注册;
1 int misc_deregister(struct miscdevice *misc)
2 {
3 int i = DYNAMIC_MINORS - misc->minor - 1;
4
5 if (list_empty(&misc->list))
6 return -EINVAL;
7
8 mutex_lock(&misc_mtx);
9 list_del(&misc->list);
10 device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
11 if (i < DYNAMIC_MINORS && i >= 0)
12 clear_bit(i, misc_minors);
13 mutex_unlock(&misc_mtx);
14 return 0;
15 }
4:代码实战:
拿一段x210_buzzer的代码进行分析
1 module_init(dev_init); 2 module_exit(dev_exit);
看一下dev_init函数(首先初始化好dev_fops结构体、misc结构体)
1 static struct file_operations dev_fops = {
2 .owner = THIS_MODULE,
3 .open = x210_pwm_open,
4 .release = x210_pwm_close,
5 .ioctl = x210_pwm_ioctl,
6 };
7
8 static struct miscdevice misc = {
9 .minor = MISC_DYNAMIC_MINOR,
10 .name = DEVICE_NAME,
11 .fops = &dev_fops,
12 };
1 static int __init dev_init(void)
2 {
3 int ret;
4
5 init_MUTEX(&lock);
6 ret = misc_register(&misc);
7
8 /* GPD0_2 (PWMTOUT2) */
9 ret = gpio_request(S5PV210_GPD0(2), "GPD0");
10 if(ret)
11 printk("buzzer-x210: request gpio GPD0(2) fail");
12
13 s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP);
14 s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1));
15 gpio_set_value(S5PV210_GPD0(2), 0);
16
17 printk ("x210 "DEVICE_NAME" initialized\n");
18 return ret;
19 }
这个函数中做了三件事:
init_MUTEX 初始化信号量
misc_register 注册驱动
gpio_request 申请gpio
这样misc设备驱动已经写好了,在补充一下具体fops中的硬件的操作方法即可;
--------------------------------------------------------------------------------------------------
三个函数分别为:x210_pwm_close、x210_pwm_open、x210_pwm_ioctl
x210_pwm_open:尝试lock如果成功则返回0,表示可以使用,如果不成功则返回EBUSY
1 static int x210_pwm_open(struct inode *inode, struct file *file)
2 {
3 if (!down_trylock(&lock))
4 return 0;
5 else
6 return -EBUSY;
7
8 }
x210_pwm_close,解锁返回0
1 static int x210_pwm_close(struct inode *inode, struct file *file)
2 {
3 up(&lock);
4 return 0;
5 }
最关键的是x210_pwm_ioctl函数
这个函数是真正的提供给应用层操作buzzer的函数;
函数原型:
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
使用内核的ioctl函数可以对很多驱动程序的参数进行设置,如串口波特率、buzzer的频率等等;
这个函数主要的两个参数是:unsigned int, unsigned long
unsigned int传的是cmd,unsigned long 传的是参数;
当命令为PWM_IOCTL_SET_FREQ时,调用PWM_Set_Freq函数设置频率
当命令为PWM_IOCTL_STOP时,调用PWM_Stop函数;
1 static int x210_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
2 {
3 switch (cmd)
4 {
5 case PWM_IOCTL_SET_FREQ:
6 printk("PWM_IOCTL_SET_FREQ:\r\n");
7 if (arg == 0)
8 return -EINVAL;
9 PWM_Set_Freq(arg);
10 break;
11
12 case PWM_IOCTL_STOP:
13 default:
14 printk("PWM_IOCTL_STOP:\r\n");
15 PWM_Stop();
16 break;
17 }
18
19 return 0;
20 }
1 void PWM_Stop( void )
2 {
3 //将GPD0_2设置为input
4 s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(0));
5 }
pwm_set_freq函数是真正的操作硬件的函数
1 static void PWM_Set_Freq( unsigned long freq )
2 {
3 unsigned long tcon;
4 unsigned long tcnt;
5 unsigned long tcfg1;
6
7 struct clk *clk_p;
8 unsigned long pclk;
9
10 //unsigned tmp;
11
12 //设置GPD0_2为PWM输出
13 s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(2));
14
15 tcon = __raw_readl(S3C2410_TCON);
16 tcfg1 = __raw_readl(S3C2410_TCFG1);
17
18 //mux = 1/16
19 tcfg1 &= ~(0xf<<8);
20 tcfg1 |= (0x4<<8);
21 __raw_writel(tcfg1, S3C2410_TCFG1);
22
23 clk_p = clk_get(NULL, "pclk");
24 pclk = clk_get_rate(clk_p);
25
26 tcnt = (pclk/16/16)/freq;
27
28 __raw_writel(tcnt, S3C2410_TCNTB(2));
29 __raw_writel(tcnt/2, S3C2410_TCMPB(2));//占空比为50%
30
31 tcon &= ~(0xf<<12);
32 tcon |= (0xb<<12); //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0
33 __raw_writel(tcon, S3C2410_TCON);
34
35 tcon &= ~(2<<12); //clear manual update bit
36 __raw_writel(tcon, S3C2410_TCON);
37 }
所以说不通的硬件会根据他不同的特点来写驱动,这需要更多的经验;
--------------------------------------------------------------------------------------------------------------------------------
x210_pwm_open