/************************************************************************************html
*本文爲我的學習記錄,若有錯誤,歡迎指正。node
* http://www.cnblogs.com/xiaojiang1025/p/6367061.html數據結構
* http://www.cnblogs.com/xiaojiang1025/p/6367910.html框架
* http://www.cnblogs.com/xiaojiang1025/p/6369065.html函數
* http://www.javashuo.com/article/p-dnlpbddz-bh.html學習
* http://www.javashuo.com/article/p-rzrvxiho-by.htmlspa
************************************************************************************/.net
字符設備是指在I/O傳輸過程當中以字節爲單位進行傳輸的設備。其特色是,不能隨機讀取字符設備的某一數據,只能按照字節的前後順序讀取數據。常見的字符設備有串口、鍵盤、鼠標等。指針
字符設備相關數據結構詳見Linux設備管理(二):內核中字符設備的管理。
(1)動態申請cdev內存
/* *所在文件:/kernel/fs/char_dev.c *參數:無 *返回值: * 成功: cdev 對象首地址 * 失敗:NULL */ struct cdev *cdev_alloc(void);
(2)初始化cdev
初始化cdev,創建cdev與file_operations之間的關聯。
/* *所在文件:/kernel/fs/char_dev.c *參數: * struct cdev *p: 被初始化的cdev對象 * const struct file_operations *p:字符設備操做方法集 *返回值:無 */ void cdev_init(struct cdev *p, const struct file_operations *p);
(3)申請設備號
1)指定設備號
/* *所在文件:/kernel/fs/char_dev.c *參數: * dev_t from: 起始設備號 * unsigned count: 需註冊的次設備個數 * const char *name: 設備名稱 *返回值: * 0: 註冊成功 * 負數: 註冊失敗 */ int register_chrdev_region(dev_t from, unsigned count, const char *name);
2)系統自動分配設備號
/* *所在文件:/kernel/fs/char_dev.c *參數: * dev_t *dev: 輸出型參數,系統自動分配的設備號 * unsigned baseminor: 次設備的起始編號 * unsigned count: 次設備的個數 * const char *name: 設備名稱 *返回值: * 0: 註冊成功 * 負數: 註冊失敗 */ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
(4)註冊cdev設備對象
將cdev添加至系統字符設備鏈表中。
/* *所在文件:/kernel/fs/char_dev.c *參數: * struct cdev *p:被初始化的cdev對象 * dev_t dev: 起始設備號 * unsigned count:該設備連續的次設備號的個數 *返回值: * 成功:返回0 * 失敗:返回負數 */ int cdev_add(struct cdev *p, dev_t dev, unsigned count);
建立設備文件的細節詳見Linux設備文件的建立。
(1)在/sys中導出設備類信息
/* *所在文件:/kernel/include/linux/device.h *參數: * owner:類所屬模塊,通常爲THIS_MODULE * name: 類名 *返回值: * struct class *:指向所建立的類 */ #define class_create(owner, name) \ ({ \ static struct lock_class_key __key; \ __class_create(owner, name, &__key); \ })
(2)建立設備文件
/* *所在文件:/kernel/driver/base/core.c *參數: * struct class *class: 被初始化的cdev對象 * struct device *parent:父設備 * dev_t dev: 設備號 * void *drvdata: 設備的回調數據 * const char *fmt: 設備名 *返回值: * struct device *: 指向所建立的設備 */ struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
根據須要實現file_operations中的函數,經常使用的函數以下:
(1)read、write
Linux下各個進程都有本身獨立的進程空間,即便是將內核的數據映射到用戶進程,該數據的PID也會自動轉變爲該用戶進程的PID,因爲這種機制的存在,咱們不能直接將數據從內核空間和用戶空間進行拷貝,而須要專門的拷貝數據函數/宏:
long copy_from_user(void *to, const void __user * from, unsigned long n) long copy_to_user(void __user *to, const void *from, unsigned long n)
有了這兩個函數,內核中的read,write就能夠實現內核空間和用戶空間的數據拷貝。
(2)ioctl
雖然在文件操做結構體"structfile_operations"中有不少對應的設備操做函數,可是有些命令是實在找不到對應的操做函數。如CD-ROM的驅動,想要一個彈出光驅的操做,這種操做並非全部的字符設備都須要的,因此文件操做結構體也不會有對應的函數操做。
出於這樣的緣由,ioctl就有它的用處了——一些沒辦法歸類的函數就統一放在ioctl這個函數操做中,經過指定的命令來實現對應的操做。因此,ioctl函數裏面都實現了多個的對硬件的操做,經過應用層傳入的命令來調用相應的操做。
ioctl函數使用的詳細方法,詳見Linux驅動程序中ioctl函數的用法。
/* *參數: * struct inode *inode和struct file *filp: ioctl的操做有多是要修改文件的屬性,或者訪問硬件。要修改文件屬性的話,就要用到這兩個結構體了,因此這裏傳來了它們的指針。 * unsigned int cmd: 命令 * unsigned long arg:參數 *返回值: 無 */ int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
(1)註銷cdev
將cdev對象從系統字符設備鏈表中剔除,並清理cdev所佔的內存資源。
/* *所在文件:/kernel/fs/char_dev.c *參數: * struct cdev *p:被註銷的cdev對象 *返回值: 無 */ void cdev_del(struct cdev *p);
(2)釋放設備號
/* *所在文件:/kernel/fs/char_dev.c *參數: * dev_t from:被註銷設備的起始設備號 * unsigned count:該設備連續的次設備號個數 *返回值: 無 */ void unregister_chrdev_region(dev_t from, unsigned count);
(3)刪除設備類
/* *所在文件:/kernel/driver/base/class.c *參數: * struct class *cls:設備類 *返回值: 無 */ void class_destroy(struct class *cls);
(4)刪除設備文件
/* *所在文件:/kernel/driver/base/core.c *參數: * struct class *class:設備類 * dev_t devt: 設備號 *返回值: 無 */ void device_destroy(struct class *class, dev_t devt);