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驅動模型
第一,節省主設備號:
使用普通字符設備,無論該驅動的主設備號是靜態仍是動態分配,都會消耗一個主設備號,這太浪費了。並且若是你的這個驅動最終會提交到內核主線版本上的話,須要申請一個專門的主設備號,這也麻煩。
若是使用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