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
相關文章
相關標籤/搜索