Linux 驅動程式入門 四
版權宣告 本文為博主隨手筆記,歡迎評論和轉載 https://www.cnblogs.com/dl04301201/p/10114103.html
博主CNSD原文 :https://mp.csdn.net/mdeditor/84987796
驅動是具有入口和出口的一組方法的集合,這一組方法才是驅動的核心內容。
對於字元裝置驅動程式,最核心的就是 file_operation 結構,這個結構實際上是提供給虛擬檔案系統 [ VFS ] 的檔案介面,它的每一個成員函式一般都對應一個系統呼叫。使用者程序利用系統呼叫對裝置檔案進行諸如讀和寫操作時,系統呼叫通過裝置檔案的主裝置號找到相應的裝置驅動程式。
可根據這篇 https://www.cnblogs.com/dl04301201/p/10098864.html 字元裝置驅動來理解本文的講解,並根據本文的講解來改用 cdev 結構進行寫驅動。
看圖來理清一下思路 :
理解 : 系統呼叫通過裝置檔案的主裝置號找到相應的裝置驅動程式
從下圖中,/dev 目錄下的裝置檔案可知,agpgart 的主裝置號為 10 次裝置號為 175
而且裝置檔案中,主裝置號時可以相同的,次裝置號的唯一的。
主裝置號跟次裝置號就可以用來識別一個裝置檔案。像通過身份證號碼就可以識別一個人一樣。
首字元c b 是識別符號, c 表示字元裝置, b 表示塊裝置
file_operations結構
摘自 Linxu 4.17.14,,在 /include/linux/fs.h中定義。用來儲存驅動核心模組提供的對裝置進行各種操作的函式的指標。該結構體的每個域都對應著驅動核心模組用來處理某個被請求的 事務的函式的地址。
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 (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); int (*iterate_shared) (struct file *, struct dir_context *); __poll_t (*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 *); unsigned long mmap_supported_flags; int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, loff_t, loff_t, int datasync); int (*fasync) (int, struct file *, int); 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 **, void **); long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len); void (*show_fdinfo)(struct seq_file *m, struct file *f); #ifndef CONFIG_MMU unsigned (*mmap_capabilities)(struct file *); #endif ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,loff_t, size_t, unsigned int); int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,u64); ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,u64); } __randomize_layout;
file_opration 簡單的運用 :
應用層系統呼叫與驅動層 struct file_operation fops 的呼叫核心。
struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
註冊字元裝置的機制
{
使用 register_chrdev 註冊字元裝置
註冊字元裝置函式 :int register_chrdev(unsigned int major, char name, struct file_operations fops);
引數 major 如果等於 0,則表示採用系統動態分配的主裝置號。如果不等於 0,則需要通過查詢未用的裝置號來為函式傳引數
使用 register_chrdev 登出字元裝置
登出字元裝置函式 : int unregister_chrdev(int major, const char *name);
}
使用 cdev_add 註冊字元裝置
{
在 Linux 核心中的字元裝置用 cdev 結構來描述,其結構如下,摘自 Linxu 4.17.14
struct cdev {
struct kobject kobj;
struct module *owner; //所屬模組,一般為 THIS_MODULE
const struct file_operations *ops; //檔案操作結構,即上文中的 file_operations 結構
struct list_head list;
dev_t dev; //裝置號
unsigned int count;
} __randomize_layout;
對 cdev 結構的操作,核心提供了一組函式
void cdev_init(struct cdev *, const struct file_operations *); //初始化一個 cdev 結構
struct cdev *cdev_alloc(void); //為 cdev 結構分配記憶體
void cdev_put(struct cdev *p); //減少使用計數
int cdev_add(struct cdev *, dev_t, unsigned); //註冊裝置,通常發生在驅動模組的載入函式中
void cdev_set_parent(struct cdev *p, struct kobject *kobj);
int cdev_device_add(struct cdev *cdev, struct device *dev);
void cdev_device_del(struct cdev *cdev, struct device *dev);
void cdev_del(struct cdev *); //登出裝置,通常發生在驅動模組的解除安裝函式中
void cd_forget(struct inode *);
使用 cdev 結構註冊裝置的一般過程如下。
以動態分配裝置號為例:
申請裝置號
分配裝置結構
初始化裝置結構
新增字元裝置
無論是用 register_chrdev 還是用 cdev 註冊字元裝置都是可以的。 register_chrdev機制出現在 linux 2.6之前的核心版本中,而 cdev 出現在 linux 2.6及之後的核心版本中,不過 linux 2.6及之後的核心版本依然相容 register_chrdev 機制。
}
字元裝置的讀寫
{
在 Linux 有個最核心的思想,一切皆為檔案。字元裝置依然不例外。字元裝置只是一個裝置檔案,應用程式可以像操作普通檔案一樣對硬體裝置進行操作。
最後根據 xxx_read(),xxx_write() 內容進行硬體操作。’
}