linux驱动开发(十)——misc杂散设备

1:什么是misc驱动模型?html

2:为何要有misc驱动模型?node

3:misc驱动模型的代码实现linux

4:misc驱动模型实战ide

参考:函数

http://blog.csdn.net/yicao821/article/details/6785738this

http://www.thinksaas.cn/topics/0/507/507168.htmlspa

http://www.cnblogs.com/fellow1988/p/6235080.html.net

https://www.zhihu.com/question/21508904code

http://www.cnblogs.com/snake-hand/p/3212483.htmlorm

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驱动也是一个字符设备驱动,可能相对特殊一点而已。在drivers/char/misc.c的misc驱动初始化函数misc_init()中实际上使用了MISC_MAJOR(主设备号为10)并调用register_chrdev()去注册了一个字符设备驱动。同时也建立了一个misc_class,使得最后可自动在/dev下自动生成一个主设备号为10的字符设备。总的来说,若是使用misc驱动能够知足要求的话,那么这能够为开发人员剩下很多麻烦。

因此说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
相关文章
相关标签/搜索