字元設備是按照位元組流進行讀寫操作的設備,讀寫數據是分先後順序的。常見的點燈、按鍵、 IIC、 SPI和LCD 等都是字元設備 。 字元設備驅動開發步驟: 總體思路: 定義並初始化一個字元設備 -1、定義一個字元設備—>struct cdev 2、定義並初始化字元設備的文件操作集—>struct fi ...
字元設備是按照位元組流進行讀寫操作的設備,讀寫數據是分先後順序的。常見的點燈、按鍵、 IIC、 SPI和LCD 等都是字元設備 。
字元設備驅動開發步驟:
總體思路:
------定義並初始化一個字元設備--------
-1、定義一個字元設備—>struct cdev
2、定義並初始化字元設備的文件操作集—>struct file_operations
3、給字元設備申請一個設備號—>設備號=主設備號<<20 + 次設備號
4、初始化字元設備
5、將字元設備加入內核
-------自動生成設備文件---------
6、創建class
7、創建device,其中device是屬於class的
-------得到物理地址對應的虛擬地址-------
8、申請物理記憶體區,申請SFR的地址區。SFR — Special Function Register: GPIOEOUT
9、記憶體的動態映射,得到物理地址對應的虛擬地址 10、訪問虛擬地址
1、驅動模塊的載入和卸載
module_init(xxx_init); //註冊模塊載入函數 module_exit(xxx_exit); //註冊模塊卸載函數
字元設備開發第一步需要向Linux內核註冊模塊載入函數和卸載函數。使用“ insmod”或“modprobe”命令載入驅動的時候 xxx_init這個函數就會被調用。使用“ rmmod”命令卸載具體驅動的時候 xxx_exit函數就會被調用。一般在載入函數里進行一些驅動的初始化工作,在卸載裡面就需要對驅動程式的卸載做一些回收工作。
註:insmod命令不能解決模塊的依賴關係 。modprobe 命令主要智能在提供了模塊的依賴性分析、錯誤檢查、錯誤報告等功能。
2、字元設備註冊與註銷
當驅動模塊載入成功以後需要註冊字元設備,卸載驅動模塊的時候也需要註銷掉字元設備。
static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) static inline void unregister_chrdev(unsigned int major, const char *name)
register_chrdev函數用於註冊字元設備,此函數共有三個參數: major:主設備號,linux每個設備都有一個設備號,設備號分為主設備號和次設備號。 name:設備名字,指向一串字元串。 fops:結構體file_operations類型指針。 unregister_chrdev函數用那個與註銷字元設備,此函數共有兩個參數: major:要註銷的設備對應的主設備號。 name:要註銷的設備對應的設備名。
2.1 創建設備號
如果沒有定義了主設備號:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
如果給定了設備的主設備號和次設備號 :
int register_chrdev_region(dev_t from, unsigned count, const char *name)
註銷設備號:
void unregister_chrdev_region(dev_t from, unsigned count)
2.2 初始化並添加cdev
在 Linux 中使用 cdev 結構體表示一個字元設備,定義好 cdev 變數以後就要使用 cdev_init 函數對其進行初始化並添加到內核。
void cdev_init(struct cdev *cdev, const struct file_operations *fops) int cdev_add(struct cdev *p, dev_t dev, unsigned count)
刪除字元設備:
void cdev_del(struct cdev *p)
2.3 自動創建類創建設備
創建class和device的目的是在安裝的驅動的時候,可以自動生成設備文件,在卸載驅動的時候,可以自動的刪除設備文件。
類創建和刪除。
struct class *class_create (struct module *owner, const char *name) void class_destroy(struct class *cls);
設備創建和刪除。
struct device *device_create(struct class *class,struct device *parent,...) void device_destroy(struct class *class, dev_t devt)
2.4 申請物理記憶體區
linux驅動使用的虛擬地址,不能直接使用物理地址。在Linux中一般申請物理地址區作為一個資源,將物理記憶體區做記憶體的動態映射,得到虛擬地址。
註:資源—有限的,一旦一個物理記憶體區已經申請了,後面就不能再次申請。
struct resource * request_mem_region(resource_size_t start,resource_size_t n,const char *name)
釋放物理記憶體區:
void release_mem_region(resource_size_t start, resource_size_t n)
2.5 物理地址記憶體映射
將一段物理地址記憶體區映射成一段虛擬地址記憶體區。
#include <linux/io.h> void __iomem *ioremap(phys_addr_t offset, unsigned long size)
解除IO記憶體動態映射
void iounmap(void __iomem *addr)
2.6使用虛擬地址
訪問虛擬地址的方法:與訪問物理地址的方法一樣,使用內核提供的函數對虛擬地址進行操作。
3、用戶空間和內核空間交互數據
3.1 從用戶空間獲取數據
#include <linux/uaccess.h> unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
在write函數對獲取的數據進行寫操作。
3.1 將數據拷貝給用戶
unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
放在驅動程式的read()。