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