• 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驱动也是一个字符设备驱动,可能相对特殊一点而已。在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
    ", 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
    ");
    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:
    ");
     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:
    ");
    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
  • 相关阅读:
    Oracle学习笔记:oracle的表空间管理和sqlserver的文件组对比
    Oracle学习笔记:一个特殊的ORA12541错误原因
    Oracle学习笔记:通过种子数据库设置dbid为指定值
    Oracle学习笔记:使用rman duplicate {to|for} 创建数据库
    Oracle学习笔记:利用rman数据库备份,手工创建clone数据库
    使用Cufon技术实现Web自定义字体
    分享七个非常有用的Android开发工具和工具包
    60佳灵感来自大自然的网页设计作品欣赏
    20个独一无二的图片滑动效果创意欣赏
    40个幻灯片效果在网页设计中的应用案例
  • 原文地址:https://www.cnblogs.com/biaohc/p/6676499.html
Copyright © 2020-2023  润新知