1. 程式人生 > >Linux 驅動程序入門 四

Linux 驅動程序入門 四

roc parent init linx 插入圖片 卸載 har add off

版權聲明 本文為博主隨手筆記,歡迎評論和轉載 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() 內容進行硬件操作。’
}

Linux 驅動程序入門 四