/************************************************************************************html
*本文爲我的學習記錄,若有錯誤,歡迎指正。node
* http://www.javashuo.com/article/p-hfnxtfsk-ck.html
數據結構
* https://blog.csdn.net/armwind/article/details/52166139
框架
************************************************************************************/ide
misc類設備,即雜項設備,全部的misc類設備都是字符設備,其主設備號固定爲10。由於如今的硬件設備多種多樣,有好些設備很差對他們進行一個單獨的分類,因此就將這些設備所有歸屬於misc類設備,譬如adc、buzzer等這些設備通常都歸屬於misc類設備中。misc類設備在應用層的操做接口:/dev/xxxx,設備類對應在 /sys/class/misc。函數
Linux內核中提供了一套misc類設備驅動框架,因此咱們寫一個misc設備的驅動直接利用的是內核中提供的驅動框架來實現的;misc類設備驅動一般嵌套在platform 總線驅動中,配合總線驅動達到更復雜,多功能的效果。misc類設備驅動框架和以前的LED設備驅動框架都是實現爲一個模塊的形式,在內核配置的時候能夠進行動態的編譯或者是不編譯進內核當中。使用misc類設備驅動框架以前,需確保Linux內核支持misc類設備驅動框架。進入Linux內核的配置界面menuconfig進行設置,具體配置以下:學習
Device Drivers --->this
[*] Misc devices ---> spa
misc類設備驅動框架的核心文件:
/kernel/ drivers/char/misc.c
/kernel/include/linux/miscdevice.h
misc類設備驅動框架使用subsys_initcall宏修飾misc_init()函數,所以misc_init()函數在內核啓動階段被調用。
misc_init函數是misc類設備驅動框架模塊註冊時的一個初始化函數,只有執行了初始化,咱們纔可以利用misc類設備驅動框架來進行編寫misc類設備驅動程序和管理misc類設備。
static const struct file_operations misc_fops = { .owner = THIS_MODULE, .open = misc_open, }; static int __init misc_init(void) { int err; #ifdef CONFIG_PROC_FS //CONFIG_PROC_FS用來控制咱們的系統中是否須要proc虛擬文件系統 proc_create("misc", 0, NULL, &misc_proc_fops); //在proc文件系統下建立一個名爲 misc 的文件 #endif misc_class = class_create(THIS_MODULE, "misc"); //在sys文件系統下建立misc設備類 err = PTR_ERR(misc_class); if (IS_ERR(misc_class)) goto fail_remove; err = -EIO; if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))//註冊misc字符設備,主設備號爲MISC_MAJOR = 10 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);
register_chrdev(MISC_MAJOR,"misc",&misc_fops),從這裏能夠看出來 misc_fops 就是傳入的一個file_operations結構體;misc_fops結構體中只實現了open函數,而沒有實現其餘的函數,由於具體的驅動實現的open、read、write函數在具體的misc類設備中的file_operations結構體中,並不在這裏實現。系統經過這裏的open函數去找到具體的要打開的硬件設備,而後找到該設備下的的file_operations結構體,調用結構體中實現的open函數,而且將要打開的設備的file_operations結構體替換當前要操做的這個結構體,以後咱們就能夠經過這個結構體來調用設備的其餘操做函數,例如read、write....等函數。
在註冊misc類設備以前,須要先定義並初始化一個struct miscdevice結構體變量,該結構體包含了該misc類設備的全部信息。
struct miscdevice { int minor; //次設備號 const char *name; //設備名稱 const struct file_operations *fops; //設備操做集 struct list_head list; //做爲一個鏈表節點掛接到misc設備維護的一個鏈表頭上去 struct device *parent; //父設備 struct device *this_device; //本設備的device基類 const char *nodename; mode_t mode; };
struct miscdevice結構體變量的一個實例。
static struct miscdevice buzzer_device = { .minor = BUZZER_MINOR, //次設備號 .name = BUZZER_NAME, //設備名字宏定義 .fops = &buzzer_fops, //文件操做指針集合 };
misc_register()函數是misc驅動框架提供給驅動工程師編寫misc類設備時的註冊函數。
int misc_register(struct miscdevice * misc) { struct miscdevice *c; //定義一個 miscdevice結構體指針 dev_t dev; //設備號 int err = 0; INIT_LIST_HEAD(&misc->list); //初始化鏈表 mutex_lock(&misc_mtx); //上鎖 list_for_each_entry(c, &misc_list, list) { //遍歷misc_list 鏈表,查找是否存在次設備號與當前註冊的設備的次設備號相同的 if (c->minor == misc->minor) { mutex_unlock(&misc_mtx); return -EBUSY; //若是存在直接退出 } } /*在咱們的misc類設備的驅動框架中使用了一種位來表示次設備號是否被佔用的狀況。使用8個字節的一個變量來表示,這個數據的每一位表示一個次設備號,第一位表明次設備號0,
第二位表明次設備號1,以此類推。
若是這個位被置1表示已經被分配出去了,置0表示沒有被分配出去。因此這段代碼就是在找一個最小的沒有被使用被置1的位,由此可知misc類設備最多隻有64個。*/ if (misc->minor == MISC_DYNAMIC_MINOR) // misc->minor == 255 表示 自動分配次設備號 { 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; //咱們這裏的意思就是咱們是從小到大去尋找,那麼分配就是從大到小,例如: i=0 ,minor=63 i =1,minor=62 set_bit(i, misc_minors); //而後將該位置1 } dev = MKDEV(MISC_MAJOR, misc->minor); //使用主次設備號合成設備號 misc->this_device = device_create(misc_class, misc->parent, dev, misc, "%s", misc->name); //建立設備/sys/devices/virtual/misc/xxx 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); //將 misc->list 做爲節點掛接到 misc_list 鏈表上去 out: mutex_unlock(&misc_mtx); return err; }
misc_deregister就是相對應的misc類設備的註銷函數。
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; }