當咱們對字符設備進行編程的時候,須要作一些常有的準備工做,獲取設備號,對設備文件操做函數的註冊,文件信息的初始化,文件的內核表現形式,向內核的註冊等等.node
對字符設備的訪問是經過文件系統內的設備名稱進行的,一般在/dev目錄下.
使用ls -l 每行的第一個字符用來識別該文件類型,c就是字符設備驅動文件.b就是塊設備驅動文件.
內核經過主次設備號來進行管理設備.
主設備號表示對應的驅動程序(雖然linux容許多個驅動程序共享主設備號,可是絕大部分的設備仍是一個主設備號對應一個驅動程序),次設備號表示具體的設備編號.
使用ls -l的命令,顯示在用戶組後面的兩列數據就是主次設備號.linux
dev_t:<linux/types.h>一個32位的數,12位表示主設備號,20位表示次設備號.
爲了不形成衝突,咱們不能本身直接定義主次設備號,應該使用函數來得到主次設備號.編程
<linux/kdev_t.h>
MAJOR(dev_t dev);//得到主設備號
MINOR(dev_t dev);//得到次設備號
MKDEV(int major,int minor);//將主次設備號轉化爲dev_t類型後端
<linux/fs.h>
int register_chrdev_region(dev_t from,unsigned int count,char * name);//得到設備號
參數:from:已經知道了主設備號的設備號
count:請求連續設備編號的個數,若是很大會和下一個主設備號重疊,可是隻要是分配經過的,都是能夠正常使用的.
name:該類型設備的名稱.
返回值:成功返回0,失敗返回失敗碼.若是是負數,則該請求的編號區域不可用.app
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);//獲取主設備號
參數:dev:用於輸出,在成功調用後保存以分配的第一個編號
firstminor:要使用使用的第一個次設備號,一般爲0
count:請求連續設備編號的個數,若是很大會和下一個主設備號重疊,可是隻要是分配經過的,都是能夠正常使用的.
name:該類型設備的名稱.
返回值:成功返回0,失敗返回失敗碼.異步
void unregister_chrdev_region(dev_t first,unsigned int count);//釋放設備編號
參數:first:釋放的設備編號的一個編號
count:須要釋放的設備號的個數.
一般在清除函數中調用.async
已使用主設備號能夠在/proc/devices文件中查看
/sys/dev目錄下有詳細的設備號的使用狀況函數
字符設備使用文件操做方式進行操做
<linux/fs.h>
file_operations:結構體,內核開放給驅動的一個接口,經過這個結構體能夠將對設備文件的讀寫等操做和驅動的讀寫操做鏈接起來.指針
struct file_operations {
struct module *owner;//擁有該模塊的指針
loff_t (*llseek) (struct file *, loff_t, int);//改變讀寫位置
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//讀數據
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//寫數據
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//異步讀
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//異步寫
int (*readdir) (struct file *, void *, filldir_t);//讀取目錄
/*poll 方法是 3 個系統調用的後端: poll, epoll, 和 select, 都用做查詢對一個或多個文件描述符的讀或寫是否會阻塞. poll 方法應當返回一個位掩碼指示是否非阻塞的讀或寫是可能的, 而且, 可能地, 提供給內核信息用來使調用進程睡眠直到 I/O 變爲可能. 若是一個驅動的 poll 方法爲 NULL, 設備假定爲不阻塞地可讀可寫.*/
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);//請求將設備內存映射到進程的地址空間
int (*open) (struct inode *, struct file *);//打開操做
int (*flush) (struct file *, fl_owner_t id);//刷新
int (*release) (struct inode *, struct file *);//釋放
int (*fsync) (struct file *, int datasync);//fsync 系統調用的後端, 用戶調用來刷新任何掛着的數據
int (*aio_fsync) (struct kiocb *, int datasync);//異步版本
int (*fasync) (int, struct file *, int);//這個操做用來通知設備它的 FASYNC 標誌的改變,異步
int (*lock) (struct file *, int, struct file_lock *);//鎖定
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);//檢查標誌
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);
};接口
實現接口的對接:
struct file_operations op = {
.owner = THIS_MODULE,
.read = test_read,
.write = test_write,
};
file結構體:打開的文件的一些信息
inode結構體:表示文件
dev_t i_rdev:對錶示設備文件的inode結構,該字段包含了真正的設備編號
struct cdev *i_cdev:表示字符設備的內核的內部構建,當indoe指向一個字符設備文件時,該字段包含了指向struct cdev結構的指針
unsigned int iminor(struct inode *inode);//從indoe中得到次設備號
unsigned int imajor(struct inode *inode);//從indoe中得到主設備號
<linux/cedv.h>
cedv結構體:表示字符設備
struct cdev *my_cdev = cdev_alloc();//申請空間
cdev->owner = THIS_MODULE;//全部者
//註冊接口函數
struct file_operations *fops;
cdev_init(my_cdev,fops);
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
//告訴內核該結構的信息,添加完了之後,這個設備就可使用了,因此要全部東西初始化完成之後才能進行操做
參數:保存有信息的cdev結構體指針
dev設備號
count:設備數量,一般爲1
返回值:0成功,錯誤返回錯誤碼
void cdev_del(struct cdev *p);//從系統中移除該字符設備