字元裝置驅動——申請、建立、應用.

安信實驗室發表於2021-10-19

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  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等以一秒的間隔亮滅

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946382/viewspace-2838192/,如需轉載,請註明出處,否則將追究法律責任。

相關文章