misc类设备
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函數:
static int __init misc_init(void) {int err;#ifdef CONFIG_PROC_FSproc_create("misc", 0, NULL, &misc_proc_fops); #endifmisc_class = class_create(THIS_MODULE, "misc");err = PTR_ERR(misc_class);if (IS_ERR(misc_class))goto fail_remove;err = -EIO;if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))goto fail_printk;misc_class->devnode = misc_devnode;return 0;fail_printk:printk("unable to get major %d for misc devices\n", MISC_MAJOR);class_destroy(misc_class); fail_remove:remove_proc_entry("misc", NULL);return err; } subsys_initcall(misc_init);?
misc_init
class_create 創建了一個名為misc的類
register_chrdev(MISC_MAJOR,"misc",&misc_fops) ?使用register_chrdev注冊了一個字符設備驅動,主設備號為MISC_MAJOR(10);
static const struct file_operations misc_fops = {.owner = THIS_MODULE,.open = misc_open, };misc類型驅動提供了一個統一.open函數misc_open函數;
misc_open 這個函數的實質是通過inode找到misc類的次設備號minor,然后在通過次設備號和misc鏈表的次設備號進行匹配,匹配好以后取出
static int misc_open(struct inode * inode, struct file * file) {int minor = iminor(inode);struct miscdevice *c;int err = -ENODEV;const struct file_operations *old_fops, *new_fops = NULL;mutex_lock(&misc_mtx);list_for_each_entry(c, &misc_list, list) {if (c->minor == minor) {new_fops = fops_get(c->fops); break;}}if (!new_fops) {mutex_unlock(&misc_mtx);request_module("char-major-%d-%d", MISC_MAJOR, minor);mutex_lock(&misc_mtx);list_for_each_entry(c, &misc_list, list) {if (c->minor == minor) {new_fops = fops_get(c->fops);break;}}if (!new_fops)goto fail;}err = 0;old_fops = file->f_op;file->f_op = new_fops;if (file->f_op->open) {file->private_data = c;err=file->f_op->open(inode,file);if (err) {fops_put(file->f_op);file->f_op = fops_get(old_fops);}}fops_put(old_fops); fail:mutex_unlock(&misc_mtx);return err; }?
?
在include/linux/miscdevice.h中定義了miscdevice ?結構體,所有的misc模型驅動設備;都在內核圍護的一個misc_list鏈表中;
內核維護一個misc_list鏈表,misc設備在misc_register注冊的時候鏈接到這個鏈表,在misc_deregister中解除鏈接。
struct miscdevice {int minor;const char *name;const struct file_operations *fops;struct list_head list;struct device *parent;struct device *this_device;const char *nodename;mode_t mode; };?
misc_register函數
int misc_register(struct miscdevice * misc) {struct miscdevice *c;dev_t dev;int err = 0;INIT_LIST_HEAD(&misc->list);mutex_lock(&misc_mtx);list_for_each_entry(c, &misc_list, list) {if (c->minor == misc->minor) {mutex_unlock(&misc_mtx);return -EBUSY;}}if (misc->minor == MISC_DYNAMIC_MINOR) {int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);if (i >= DYNAMIC_MINORS) {mutex_unlock(&misc_mtx);return -EBUSY;}misc->minor = DYNAMIC_MINORS - i - 1;set_bit(i, misc_minors);}dev = MKDEV(MISC_MAJOR, misc->minor);misc->this_device = device_create(misc_class, misc->parent, dev,misc, "%s", misc->name);if (IS_ERR(misc->this_device)) {int i = DYNAMIC_MINORS - misc->minor - 1;if (i < DYNAMIC_MINORS && i >= 0)clear_bit(i, misc_minors);err = PTR_ERR(misc->this_device);goto out;}/** Add it to the front, so that later devices can "override"* earlier defaults*/list_add(&misc->list, &misc_list);out:mutex_unlock(&misc_mtx);return err; }?
misc_register
misc->this_device = device_create(misc_class, misc->parent, dev,?misc, "%s", misc->name);
?調用這個函數來初創建設備;
?
misc_deregister函數來取消注冊;
int misc_deregister(struct miscdevice *misc) {int i = DYNAMIC_MINORS - misc->minor - 1;if (list_empty(&misc->list))return -EINVAL;mutex_lock(&misc_mtx);list_del(&misc->list);device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));if (i < DYNAMIC_MINORS && i >= 0)clear_bit(i, misc_minors);mutex_unlock(&misc_mtx);return 0; }?
4:代碼實戰:
拿一段x210_buzzer的代碼進行分析
module_init(dev_init);
module_exit(dev_exit);
看一下dev_init函數(首先初始化好dev_fops結構體、misc結構體)
static struct file_operations dev_fops = {.owner = THIS_MODULE,.open = x210_pwm_open,.release = x210_pwm_close, .ioctl = x210_pwm_ioctl, };static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &dev_fops, };?
?
static int __init dev_init(void) {int ret;init_MUTEX(&lock);ret = misc_register(&misc);/* GPD0_2 (PWMTOUT2) */ret = gpio_request(S5PV210_GPD0(2), "GPD0");if(ret)printk("buzzer-x210: request gpio GPD0(2) fail");s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1));gpio_set_value(S5PV210_GPD0(2), 0);printk ("x210 "DEVICE_NAME" initialized\n");return ret; }?
這個函數中做了三件事:
init_MUTEX ? 初始化信號量
misc_register 注冊驅動
gpio_request 申請gpio
這樣misc設備驅動已經寫好了,在補充一下具體fops中的硬件的操作方法即可;
--------------------------------------------------------------------------------------------------
三個函數分別為:x210_pwm_close、x210_pwm_open、x210_pwm_ioctl
x210_pwm_open,嘗試lock如果成功則返回0,表示可以使用,如果不成功則返回EBUSY
static int x210_pwm_open(struct inode *inode, struct file *file) {if (!down_trylock(&lock))return 0;elsereturn -EBUSY;}?
x210_pwm_close,解鎖返回0
static int x210_pwm_close(struct inode *inode, struct file *file) {up(&lock);return 0; }最關鍵的是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函數;
static int x210_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) {switch (cmd) {case PWM_IOCTL_SET_FREQ:printk("PWM_IOCTL_SET_FREQ:\r\n");if (arg == 0)return -EINVAL;PWM_Set_Freq(arg);break;case PWM_IOCTL_STOP:default:printk("PWM_IOCTL_STOP:\r\n");PWM_Stop();break;}return 0; } void PWM_Stop( void ) {//將GPD0_2設置為inputs3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(0)); }pwm_set_freq函數是真正的操作硬件的函數
static void PWM_Set_Freq( unsigned long freq ) {unsigned long tcon;unsigned long tcnt;unsigned long tcfg1;struct clk *clk_p;unsigned long pclk;//unsigned tmp;//設置GPD0_2為PWM輸出s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(2));tcon = __raw_readl(S3C2410_TCON);tcfg1 = __raw_readl(S3C2410_TCFG1);//mux = 1/16tcfg1 &= ~(0xf<<8);tcfg1 |= (0x4<<8);__raw_writel(tcfg1, S3C2410_TCFG1);clk_p = clk_get(NULL, "pclk");pclk = clk_get_rate(clk_p);tcnt = (pclk/16/16)/freq;__raw_writel(tcnt, S3C2410_TCNTB(2));__raw_writel(tcnt/2, S3C2410_TCMPB(2));//占空比為50%tcon &= ~(0xf<<12);tcon |= (0xb<<12); //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0__raw_writel(tcon, S3C2410_TCON);tcon &= ~(2<<12); //clear manual update bit__raw_writel(tcon, S3C2410_TCON); }?
總結
- 上一篇: Cortex-M3栈内存操作
- 下一篇: linux系统如何用root用户登陆,L