1、申請設備號
// 1、注冊獲取設備號// 2、初始化設備// 3、操作設備 file_operations – open release read write ioctl…// 4、兩個宏定義 module_init module_exit // 5、注冊設備號 register_chrdev_region// 6、cdev_init 初始化字符設備// 7、cdev_add 添加字符設備到系統
1)向系統申請主設備號
int register_chrdev(unsigned int major, const char * name, const struct file_operations * fops)
//參數://1、major:主設備號// 設備號(32bit–dev_t)==主設備號(高12bit) + 次設備號(低20bit)// 主設備號:表示一類設備—(如:camera)// 次設備號: 表示一類設備中某一個—(如:前置camera/后置camera)// 0 -->動態分配 ; 250 --> 給定整數,靜態指定//2、name: 描述設備信息,可自定義// 在目錄/proc/devices列舉出了所有的已經注冊的設備//3、fops: 文件操作對象// 提供open, read,write//返回值:成功-0,失敗-負數
2)釋放設備號
void unregister_chrdev(unsigned int major, const char * name)
3)例:主設備號的申請
chr_drv.c
加載驅動前:
加載驅動后:
2、創建設備節點
1)手動創建
··
缺點/dev/目錄中文件都是在內存中,斷電后/dev/文件就會消失
mknod /dev/設備名 類型 主設備號 次設備號
(主設備號要和驅動中申請的主設備號保持一致)
比如:
mknod /dev/chr0 c 250 0
eg:
[root@farsight drv_module]# ls /dev/chr0 -l
crw-r--r-- 1 0 0 250, 0 Jan 1 00:33 /dev/chr0
2)自動創建
通過udev/mdev機制
struct class *class_create(owner, name)//創建一個類
//參數://1、owner:THIS_MODULE//2、name :字符串名字,自定義//返回:// 返回一個class指針
創建一個設備文件:
//創建一個設備文件struct device *device_create(struct class * class, struct device * parent, dev_t devt, void * drvdata, const char * fmt,...)
//參數://1、class結構體,class_create調用之后的返回值//2、表示父親,一般直接填NULL//3、設備號類型 dev_t//4、私有數據,一般直接填NULL//5/6、表示可變參數,字符串,表示設備節點名字
設備號類型:dev_t devt
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) //獲取主設備號
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) //獲取次設備號
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) //生成設備號
銷毀設備文件:
void device_destroy(devcls, MKDEV(dev_major, 0));//參數://1、class結構體,class_create調用之后到返回值//2、設備號類型 dev_t
void class_destroy(devcls);//參數:class結構體,class_create調用之后到返回值
3)示例:
chr_drv.c
3、實現文件IO接口--fops
1)驅動中實現文件io操作接口:struct file_operations
1 struct file_operations { 2 struct module *owner; 3 loff_t (*llseek) (struct file *, loff_t, int); 4 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 5 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 6 ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); 7 ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); 8 int (*iterate) (struct file *, struct dir_context *); 9 unsigned int (*poll) (struct file *, struct poll_table_struct *);10 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);11 long (*compat_ioctl) (struct file *, unsigned int, unsigned long);12 int (*mmap) (struct file *, struct vm_area_struct *);13 int (*open) (struct inode *, struct file *);14 ....16 long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);17 int (*show_fdinfo)(struct seq_file *m, struct file *f);18 }; //函數指針的集合,其實就是接口,我們寫驅動到時候需要去實現19 20 const struct file_operations my_fops = {21 .open = chr_drv_open,22 .read = chr_drv_read,23 .write = chr_drv_write,24 .release = chr_drv_close,25 };
示例:
chr_drv1.c
實現了底層的fops成員函數,再實現應用程序的調用
2)應用程序調用文件IO控制驅動 :open、read...
chr_drv1.c
chr_test.c
Makefile
測試結果;
4、應用程序控制驅動
應用程序要控制驅動,就涉及用戶空間與內核空間的數據交互,如何實現?通過以下函數:
1)copy_to_user
1 //將數據從內核空間拷貝到用戶空間,一般是在驅動中chr_drv_read()用2 int copy_to_user(void __user * to, const void * from, unsigned long n)3 //參數:4 //1:應用驅動中的一個buffer5 //2:內核空間到一個buffer6 //3:個數7 //返回值:大于0,表示出錯,剩下多少個沒有拷貝成功等于0,表示正確
2)copy_from_user
1 //將數據從用戶空間拷貝到內核空間,一般是在驅動中chr_drv_write()用2 int copy_from_user(void * to, const void __user * from, unsigned long n)3 //參數:4 //1:內核驅動中的一個buffer5 //2:應用空間到一個buffer6 //3:個數
示例:
chr_drv1.c
chr_test.c
測試:
5、驅動程序控制外設
之前我們了解了應用程序如何與內核空間進行數據交互,那么內核驅動與外設間的控制是怎么樣的?
寫過裸機程序的都知道,可以通過修改外設對應的控制寄存器來控制外設,即向寄存器的地址寫入數據,這個地址就是物理地址,且物理地址是已知的,有硬件設計決定。
在內核中,同樣也是操作地址控制外設,但是內核中的地址,是經過MMU映射后的虛擬地址,而且CPU通常并沒有為這些已知的外設I/O內存資源的物理地址預定義虛擬地址范圍,驅動程序并不能直接通過物理地址訪問I/O內存資源,而必須將它們映射到核心虛地址空間內,然后才能根據映射所得到的核心虛地址范圍,通過訪內指令訪問這些I/O內存資源。Linux在io.h頭文件中聲明了函數ioremap(),用來將I/O內存資源的物理地址映射到核心虛地址空間中
ioremap的函數如下:
1 //映射虛擬地址2 void *ioremap(cookie, size)3 //參數:4 //1、cookie:物理地址5 //2、size:長度(連續映射一定長度的地址空間)6 //返回值:虛擬地址
解除映射:
1 //去映射--解除映射2 void iounmap(void __iomem *addr)3 //參數:映射后的虛擬地址
實例:通過驅動控制LED燈
LED —— GPX2_7 —— GPX2CON —— 0x11000C40
GPX2DAT—— 0x11000C44
將0x11000c40映射為虛擬地址
chr_drv1.c
chr_test.c
測試:
執行app后,可以看到LED等以一秒的間隔亮滅
? ? ? ? ymf
評論