1. 程式人生 > >linux設備驅動之misc驅動框架源碼分析(一)

linux設備驅動之misc驅動框架源碼分析(一)

linux驅動開發misc設備驅動

1、misc設備驅動框架源碼部分是由內核開發者實現提供的,主要是創建misc類和為驅動開發者提供misc_register函數,來進行創建misc設備。

這部分的源碼在/drvier/char/misc.c裏,代碼如下:

/*
 * linux/drivers/char/misc.c
 *
 * Generic misc open routine by Johan Myreen
 *
 * Based on code from Linus
 *
 * Teemu Rantanen‘s Microsoft Busmouse support and Derrick Cole‘s
 *   changes incorporated into 0.97pl4
 *   by Peter Cervasio ([email protected]
/* */) (08SEP92) * See busmouse.c for particulars. * * Made things a lot mode modular - easy to compile in just one or two * of the misc drivers, as they are now completely independent. Linus. * * Support for loadable modules. 8-Sep-95 Philip Blundell <[email protected]> * * Fixed a failing symbol register to free the device registration * Alan Cox <[email protected]
/* */> 21-Jan-96 * * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96 * * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96 * * Handling of mouse minor numbers for kerneld: * Idea by Jacques Gelinas <[email protected]>, * adapted by Bjorn Ekwall <[email protected]
/* */> * corrected by Alan Cox <[email protected]> * * Changes for kmod (from kerneld): * Cyrus Durgin <[email protected]> * * Added devfs support. Richard Gooch <[email protected]> 10-Jan-1998 */ #include <linux/module.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/miscdevice.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/mutex.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/stat.h> #include <linux/init.h> #include <linux/device.h> #include <linux/tty.h> #include <linux/kmod.h> #include <linux/gfp.h> /* * Head entry for the doubly linked miscdevice list */ static LIST_HEAD(misc_list); static DEFINE_MUTEX(misc_mtx); /* * Assigned numbers, used for dynamic minors */ #define DYNAMIC_MINORS 64 /* like dynamic majors */ static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS); #ifdef CONFIG_PROC_FS static void *misc_seq_start(struct seq_file *seq, loff_t *pos) { mutex_lock(&misc_mtx); return seq_list_start(&misc_list, *pos); } static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos) { return seq_list_next(v, &misc_list, pos); } static void misc_seq_stop(struct seq_file *seq, void *v) { mutex_unlock(&misc_mtx); } static int misc_seq_show(struct seq_file *seq, void *v) { const struct miscdevice *p = list_entry(v, struct miscdevice, list); seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : ""); return 0; } static const struct seq_operations misc_seq_ops = { .start = misc_seq_start, .next = misc_seq_next, .stop = misc_seq_stop, .show = misc_seq_show, }; static int misc_seq_open(struct inode *inode, struct file *file) { return seq_open(file, &misc_seq_ops); } static const struct file_operations misc_proc_fops = { .owner = THIS_MODULE, .open = misc_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; #endif 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; } static struct class *misc_class; static const struct file_operations misc_fops = { .owner = THIS_MODULE, .open = misc_open, }; /** * misc_register - register a miscellaneous device * @misc: device structure * * Register a miscellaneous device with the kernel. If the minor * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned * and placed in the minor field of the structure. For other cases * the minor number requested is used. * * The structure passed is linked into the kernel and may not be * destroyed until it has been unregistered. * * A zero is returned on success and a negative errno code for * failure. */ 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_deregister - unregister a miscellaneous device * @misc: device to unregister * * Unregister a miscellaneous device that was previously * successfully registered with misc_register(). Success * is indicated by a zero return, a negative errno code * indicates an error. */ 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; } EXPORT_SYMBOL(misc_register); EXPORT_SYMBOL(misc_deregister); static char *misc_devnode(struct device *dev, mode_t *mode) { struct miscdevice *c = dev_get_drvdata(dev); if (mode && c->mode) *mode = c->mode; if (c->nodename) return kstrdup(c->nodename, GFP_KERNEL); return NULL; } static int __init misc_init(void) { int err; #ifdef CONFIG_PROC_FS proc_create("misc", 0, NULL, &misc_proc_fops); #endif misc_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源碼框架本身也是一個模塊,內核啟動時自動加載。這麽做的目的是為了可以加載和卸載,當實際情況中板子上一個misc設備都沒有用到,那麽我們就可以對內核進行裁剪,將misc驅動框架的代碼也卸載掉。因為這個驅動框架的存在的作用就是讓其驅動開發著來調用其提供的接口函數來創建misc設備等。如果沒有用到misc設備,也就是沒有一個misc驅動代碼需要依附於misc驅動框架源碼,那麽這個misc驅動框架源碼就可以卸載掉,使內核盡可能的被裁剪。

怎麽知道misc驅動框架源碼也是一個模塊呢,通過上面的misc驅動框架源碼的最後一行代碼

subsys_initcall(misc_init);

這個和module_init一樣,所以知道misc的驅動框架源碼也是模塊化的,可以加載和卸載。

subsys_initcall的啟動順序比module_Init的啟動順序要早,因為肯定要先將misc雜散類設備的驅動框架代碼進行模塊加載初始化成功後,才能寫misc設備的驅動代碼,module_init加載的是驅動代碼,驅動框架如果都沒有初始化加載好,怎麽調用驅動框架寫misc設備的驅動代碼,只有驅動框架初始化加載好了,才能在驅動代碼中利用驅動框架去註冊你的設備。

subsys_initcall(misc_init);

這個subsys_initcall的參數misc_init,就是這個驅動框架代碼被加載的時候執行的函數。misc_init的代碼如下並進行分析:

static int __init misc_init(void)
{
	int err;

#ifdef CONFIG_PROC_FS
	proc_create("misc", 0, NULL, &misc_proc_fops);    //如果當前內核需要使用proc虛擬文件系統,那麽就在proc目錄下創建misc文件
	                                                    //通過這個文件,我們可以在用戶空間來知道我們當前系統中註冊了那些雜散類設備
#endif                                                    //proc虛擬文件系統實現的沒有sysfs文件系統實現的好,在內核為2.4版本的時候較為流行,
                                                        //因為裏面太亂
                                                        
	misc_class = class_create(THIS_MODULE, "misc");    //創建一個類,名字叫做misc,在/sys/class/目錄中,此時創建完這個misc類後目錄的內
	                                                //容是空的。但是當我們調用device_create創建一個misc設備的時候,這misc目錄裏面就會多
	                                                //一個設備。device_create是在misc_register函數中被調用的,misc_register是misc的驅動
	                                                //框架源碼,驅動開發者調用misc_register來創建一個misc設備,之後就會在misc目錄裏面看到
	                                                //創建出來的設備,這裏class_create創建出來的類目錄的內容和device_create函數調用後
	                                                //後創建出來的設備是綁定的,創建出來的設備就會在class_create創建出來的類目錄中體現。
	err = PTR_ERR(misc_class);
	if (IS_ERR(misc_class))
		goto fail_remove;

	err = -EIO;
	
	//字符設備驅動的創建,主設備號是10,名字是misc,將misc類設備驅動註冊為了字符設備驅動。
	if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))    //這個register_chrdev是一個老接口,利用這接口註冊設備的時候是只有主設備號沒有
	                                                    //次設備號的,直接調用這個接口註冊的時候,就相當於將所有的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;
}

上面的代碼,misc驅動框架代碼被加載後的效果,就是創建misc類,註冊字符設備驅動。這是驅動框架源碼實現的部分,驅動開發者在將來一旦調用了misc_register函數註冊一個misc設備結構體後就會間接調用device_create創建一個misc設備,udev或者mdev的機制就會自動在/dev目錄下創建出一個設備文件節點。我們就可以使用這個設備文件節點來操作這個misc設備了。

misc_register函數是提供給驅動開發者使用的,功能是將misc設備註冊到misc驅動框架中,註冊misc設備成功後,會在/sys/class/misc/目錄中看到註冊的設備文件。

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;
}

這個函數的參數是struct miscdevice類型的結構體指針,這個結構體類型內容為:

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函數和提供的參數可以知道,驅動開發者要將一個設備註冊到misc驅動框架中就需要使用這個函數,需要填充struct miscdevice類型的變量,然後進行註冊,表示一個misc設備,註冊設備完畢後,會在/sys/class/misc/目錄中看到註冊的設備文件。udev或mdev會在/dev目錄下創建出設備文件節點來讓應用層進行操作。

其實驅動框架說白了,就是內核(驅動框架)給驅動開發者提供了一個結構體以及註冊和註銷相關的函數接口,驅動開發者只需要用內核(驅動框架)提供的結構體來定義變量然後進行合理填充,然後利用內核(驅動框架)提供的註冊或註銷相關的函數接口來進行註冊或註銷這個結構體變量,則表示將設備註冊到了驅動框架中,註冊到了一類中,這樣udev或mdev就可以在/dev/下創建出設備文件節點,應用層就可以用這個設備文件節點來進行操作設備。

驅動框架具有強烈的面向對象的思維邏輯,利用驅動框架寫驅動代碼,邏輯思維上就是利用驅動框架提供的類,new出來一個設備對象然後進行合理填充,填充後利用類中提供的方法來將new出來的對象進行註冊到驅動框架中。


2、misc_list鏈表的作用

在misc_register函數中,用到了一個全局變量misc_list,misc_list是個鏈表。


本文出自 “whylinux” 博客,謝絕轉載!

linux設備驅動之misc驅動框架源碼分析(一)

相關推薦

linux驅動misc驅動框架分析

linux驅動開發misc設備驅動 1、misc設備驅動框架源碼部分是由內核開發者實現提供的,主要是創建misc類和為驅動開發者提供misc_register函數,來進行創建misc設備。 這部分的源碼在/drvier/char/misc.c裏,代碼如下:/* * linux/drivers/c

linux驅動misc驅動框架分析

linux驅動開發misc設備驅動1、misc_open函數分析 該函數在driver/char/misc.c中,misc.c是驅動框架實現的,這裏面的misc_Open函數是misc驅動框架為應用層提供的一個打開misc設備的一個接口。 1、首先我們要知道在misc.c中的misc_init函數

linux驅動開發蜂鳴器驅動分析

linux 蜂鳴器 驅動 蜂鳴器的驅動源碼在/driver/char/buzzer/x210-buzzer.c文件中,源碼如下#include <linux/module.h> #include <linux/kernel.h> #include <linux

死磕 java集合TreeMap分析

過程 tree structure 有序 準備 紅色 復雜度 繼承 lower 歡迎關註我的公眾號“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢遊源碼的海洋。 簡介 TreeMap使用紅黑樹存儲元素,可以保證元素按key值的大小進行遍歷。 繼承體系 TreeMap實

死磕 java集合ConcurrentHashMap分析

為什麽 取整 http lean else nsf 遷移 fault 運行 開篇問題 (1)ConcurrentHashMap與HashMap的數據結構是否一樣? (2)HashMap在多線程環境下何時會出現並發安全問題? (3)ConcurrentHashMap是怎麽解決

J.U.C並發框架閱讀Semaphore

throws incr optional con osi prevent zed block best 基於版本jdk1.7.0_80 java.util.concurrent.Semaphore 代碼如下 /* * ORACLE PROPRIETARY/CONF

J.U.C並發框架閱讀CyclicBarrier

sse 核心 exception row new t shared ati processes str 基於版本jdk1.7.0_80 java.util.concurrent.CyclicBarrier 代碼如下 /* * ORACLE PROPRIETARY/

J.U.C並發框架閱讀ConcurrentLinkedQueue

bstr ray there vol line explained 1.7 root run 基於版本jdk1.7.0_80 java.util.concurrent.ConcurrentLinkedQueue 代碼如下 /* * ORACLE PROPRIETA

Django基於Pycharm開發四[關於靜態文件的使用,配置以及分析]原創

OS director 一個 pin mes map text 開發人員 容易 對於django靜態文件的使用,如果開發過netcore程序的開發人員,可能會比較容易理解django關於靜態文件訪問的設計原理,個人覺得,這是一個middlerware的設計,但是在djang

Spark學習十五SparkCore的解讀啟動腳本

-o 啟動服務 binary dirname ppi std 參數 exp 情況 一、啟動腳本分析 獨立部署模式下,主要由master和slaves組成,master可以利用zk實現高可用性,其driver,work,app等信息可以持久化到zk上;slaves由一臺至多

zigbee ZStack-2.5.1a原始碼分析

先看main, 在檔案Zmain.c裡面 main osal_init_system(); osalInitTasks(); ... ... SampleApp_Init( taskID ); // 使用者定義的任務

SSM框架報錯分析——There is no getter for property named 'XXX' in 'class java.lang.String'

一、發現問題 <select id="queryStudentByNum" resultType="student" parameterType="string"> select num,name,phone from student <where> <if t

JDK分析8 Reference 實現和應用

over verilog 工廠方法 很大的 obj lock 源碼分析 .info 策略 在閱讀本文之前最好對 Reference 框架有一個整體的把握,可以參考我上一篇博客 Reference 框架概覽 ;本文主要講了 Reference 的子類實現和應用(SoftRef

JDK分析9 WeakHashMap 相關

string 簡潔 ring get val tca hash pool adp 平時我們使用最多的數據結構肯定是 HashMap,但是在使用的時候我們必須知道每個鍵值對的生命周期,並且手動清除它;但是如果我們不是很清楚它的生命周期,這時候就比較麻煩;通常有這樣幾種處理方式

死磕 java集合TreeMap分析

進行 ins else table java集合 entry 修改 qrc 方式 插入元素 插入元素,如果元素在樹中存在,則替換value;如果元素不存在,則插入到對應的位置,再平衡樹。 public V put(K key, V value) { Entry<

死磕 java集合TreeMap分析- 內含紅黑樹分析全過程

根節點 eem right .com ast size 一個 table else 歡迎關註我的公眾號“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢遊源碼的海洋。 刪除元素 刪除元素本身比較簡單,就是采用二叉樹的刪除規則。 (1)如果刪除的位置有兩個葉子節點,則從其

死磕 java集合TreeMap分析-內含彩蛋

留言 cti 簡單 刪除元素 回顧 over foreach hub rst 歡迎關註我的公眾號“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢遊源碼的海洋。 二叉樹的遍歷 我們知道二叉查找樹的遍歷有前序遍歷、中序遍歷、後序遍歷。 (1)前序遍歷,先遍歷我,再遍歷我的

死磕 java集合ConcurrentHashMap分析

ret tree 而是 答案 img equal java集合 自旋鎖 數組 本章接著上兩章,鏈接直達: 死磕 java集合之ConcurrentHashMap源碼分析(一) 死磕 java集合之ConcurrentHashMap源碼分析(二) 刪除元素 刪除元素跟添加元

Java並發AQS分析

next dac mage bool 需要 狀態 false 兩個 繼續 我在Java並發之AQS源碼分析(一)這篇文章中,從源碼的角度深度剖析了 AQS 獨占鎖模式下的獲取鎖與釋放鎖的邏輯,如果你把這部分搞明白了,再看共享鎖的實現原理,思路就會清晰很多。下面我們繼續從源碼

linux驅動platform平臺總線工作原理

linux設備和驅動設備為數據,驅動為加工著1、以led-s3c24xx.c為例來分析platform設備和驅動的註冊過程其中關於led的驅動數據結構為:static struct platform_driver s3c24xx_led_driver = { .probe = s3c24xx_led_pr