单片机
返回首页

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

2025-02-18 来源:cnblogs

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驱动模型

第一,节省主设备号:
使用普通字符设备,不管该驱动的主设备号是静态还是动态分配,都会消耗一个主设备号,这太浪费了。而且如果你的这个驱动最终会提交到内核主线版本上的话,需要申请一个专门的主设备号,这也麻烦。
如果使用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 devicesn', 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' initializedn');

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:rn');

 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:rn');

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 }


所以说不通的硬件会根据他不同的特点来写驱动,这需要更多的经验;


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 家用电器遥控器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • DS1669数字电位器

  • HA1377 桥式放大器 BCL 电容 17W(汽车音频)

    相关电子头条文章