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