Linux驅動開發筆記(四):裝置驅動介紹、熟悉雜項裝置驅動和ubuntu開發雜項裝置Demo

21497936發表於2023-11-21

前言

  驅動的開發需要先熟悉基本概念型別,本篇講解linux雜項裝置基礎,還是基於虛擬機器ubuntu去製作驅動,只需要虛擬機器就可以嘗試編寫註冊雜項裝置的基本流程。

linux三大裝置驅動

  • 字元裝置:IO的傳輸過程是以字元為單位的,沒有緩衝,比如I2C(SDA、SCL),SPI(MISO、MOSI、SCLK、CS)。
  • 塊裝置:IO的傳輸過程是以塊為單位的,跟儲存相關的都屬於塊裝置,比如tf卡,sd卡。
  • 網路裝置:IO的傳輸以socket套接字來訪問的。

雜項裝置

  • 雜項裝置是屬於 字元裝置,可以自動生成裝置節點,裝置節點位於/dev/目錄下,是裝置名稱,如/dev/ttyS9等。
  • 主裝置號相同,統一為10,次裝置號不同,主裝置相同可以節省核心資源。
    透過下列指令,可以檢視系統雜項裝置
cat /proc/misc

  在虛擬機器上測試,檢視雜項:
   在這裡插入圖片描述

  • 裝置號分為主裝置號和次裝置號,主裝置號是wei的,次裝置號不一定wei
    透過下列指令,可以檢視系統主裝置號:
cat /proc/devices

   在這裡插入圖片描述

雜項裝置描述結構體

  ubuntu來說,自帶的/usr/src下的就是核心的標頭檔案。

cd /usr/src/linux-headers-4.18.0-15vi include/linux/miscdevice.h

  定位到之前ubuntu自帶的核心標頭檔案下:
   在這裡插入圖片描述
   在這裡插入圖片描述

  檢視到雜項裝置的結構體:

struct miscdevice  {
        int minor;  // 次裝置號
        const char *name;  // 裝置節點名稱(如/dev/ttyS8,則ttyS是名稱)
        const struct file_operations *fops; // 檔案操作集(非常重要)
        struct list_head list; 
        struct device *parent;
        struct device *this_device;
        const struct attribute_group **groups; 
        const char *nodename; 
        umode_t mode;};

  (注意:沒打註釋的,一般不管)

雜項裝置檔案操作集

cd /usr/src/linux-headers-4.18.0-15vi include/linux/fs.h

  搜尋到(vi則直接使用“/”):
   在這裡插入圖片描述

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 (*setfl)(struct file *, unsigned long);
        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;

  例如read函式,那麼就是開啟驅動使用系統read,開啟這個裝置驅動的控制程式碼,那麼久會呼叫read函式,其他的以此類推,還比較好理解。
  以我們一個registerHelloWorld為例子,來簡單說明。

驅動編寫空模板準備

  首先複製之前的hello world的驅動,改個名字為:registerMiscDev:

cd ~/work/drivecp -arf hellowolrd registerMiscDev

   在這裡插入圖片描述

cd registerMiscDev/rm *.ko *.o *.order *.symvers

  這裡刪除起來麻煩,修改makefile,新增clean:
   在這裡插入圖片描述

  然後測試一下:
   在這裡插入圖片描述

  繼續修改原始碼檔名稱:

mv helloworld.c registerMiscDev.c

  修改完如下:
   在這裡插入圖片描述

  然後修改makefile裡面的(obj-m模組名稱改下),模板準備好了
   在這裡插入圖片描述

  下面基於registerMiscDev.c檔案進行註冊雜項裝置,在修改.c檔案:
   在這裡插入圖片描述

#include <linux/init.h>#include <linux/module.h>static int registerMiscDev_init(void){ 
    // 在核心裡面無法使用基礎c庫printf,需要使用核心庫printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	
    return 0;}static void registerMiscDev_exit(void){
    printk("bye-bye!!!\n");}MODULE_LICENSE("GPL");module_init(registerMiscDev_init);module_exit(registerMiscDev_exit);

雜項裝置註冊流程Demo

步驟一:填充miscdevice結構體

  在編寫驅動的時候,程式碼中填充資訊結構體。
  新增標頭檔案miscdevice.h

#include <linux/miscdevice.h>#include <linux/fs.h>

   在這裡插入圖片描述

  然後填充雜項裝置結構體:
   在這裡插入圖片描述

  (注意:開始為“.”,結束為“,”,最後一行習慣加“,”了,這樣可以全部統一複製啥的,省的加沒加的)

struct miscdevice misc_dev {
    .minor = MISC_DYNAMIC_MINRO, // 這個宏是動態分配次裝置號,避免衝突
    .name = "register_hongPangZi_misc,  // 裝置節點名稱
    .fops = misc_fops, // 這個變數記住,自己起的,步驟二使用}

   在這裡插入圖片描述

步驟二:填充file_operations結構體

  在編寫驅動的時候,程式碼中填充檔案操作結構體。
   在這裡插入圖片描述

struct file_operations misc_fops {
  .owner = THIS_MODULE}

   在這裡插入圖片描述

步驟三:註冊雜項裝置並生成裝置節點

  註冊到核心:

static int registerMiscDev_init(void){ 
    // 在核心裡面無法使用基礎c庫printf,需要使用核心庫printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	
    int ret = 0;
    ret = misc_register(misc_dev);
    if(ret < 0)
    {
        printk("Failed to misc_register(misc_dev)\n");	
        return -1;
    } 
    return 0;}

   在這裡插入圖片描述

  有註冊就有登出:

static int registerMiscDev_init(void){ 
    // 在核心裡面無法使用基礎c庫printf,需要使用核心庫printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	
    int ret = 0;
    ret = misc_register(&misc_dev);
    if(ret < 0)
    {
        printk("Failed to misc_register(misc_dev)\n");	
        return -1;
    } 
    return 0;}

   在這裡插入圖片描述

  完整的檔案原始碼:

#include <linux/init.h>#include <linux/module.h>#include <linux/miscdevice.h>#include <linux/fs.h>struct file_operations misc_fops = {
  .owner = THIS_MODULE,};struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR, // 這個宏是動態分配次裝置號,避免衝突
    .name = "register_hongPangZi_misc", // 裝置節點名稱
    .fops = &misc_fops,  // 這個變數記住,自己起的,步驟二使用};static int registerMiscDev_init(void){ 
    // 在核心裡面無法使用基礎c庫printf,需要使用核心庫printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	
    int ret = 0;
    ret = misc_register(&misc_dev);
    if(ret < 0)
    {
        printk("Failed to misc_register(&misc_dev)\n");	
        return -1;
    } 
    return 0;}static void registerMiscDev_exit(void){
    misc_deregister(&misc_dev);
    printk("bye-bye!!!\n");}MODULE_LICENSE("GPL");module_init(registerMiscDev_init);module_exit(registerMiscDev_exit);

步驟四:編譯make

make

  直接在驅動工程目錄編譯:
   在這裡插入圖片描述

  下面這個警告,實際上定義要在任何使用函式之前:
   在這裡插入圖片描述

  修改下:
   在這裡插入圖片描述

   在這裡插入圖片描述

  編譯成功
   在這裡插入圖片描述

步驟五:載入解除安裝驅動測試

  將驅動複製到開發板或者目標系統,然後使用載入指令:

sudo insmod registerMiscDev.ko

  會列印入口載入的printk輸出。
   在這裡插入圖片描述

  出現問題可能原因一是核心編譯使用的編譯器和模組使用的編譯器版本不一致。ubuntu中printk終端打入核心日誌訊息了,可以使用dmesg進行檢視:

dmesg

   在這裡插入圖片描述

  然後檢視是否加入了雜項裝置節點:
   在這裡插入圖片描述

  然後登出:

sudo rmmod registerMiscDev.ko

   在這裡插入圖片描述

  跟隨著,結點消失了:
   在這裡插入圖片描述

入坑

入坑一:編譯報錯,結構體之後未加分號

問題

  編譯錯誤,結構體後面加分號

解決

  加分號,腦袋有點蒙
   在這裡插入圖片描述

入坑二:編譯錯誤,檔案操作指標問題

問題

   在這裡插入圖片描述

解決

  這是寫錯了,是指標,需要加取地址&。


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

相關文章