1. 程式人生 > 其它 >imx6ull字元裝置驅動(1)

imx6ull字元裝置驅動(1)

技術標籤:linux嵌入式

字元裝置驅動(1)

基於imx6ull以及其使用手冊

linux驅動

外設主要分為

  • 字元裝置
    感覺就是簡單的i/o?
    • 以位元組驅動為單位,順序訪問,字元裝置面向流裝置,
    • 蜂鳴器,led,滑鼠,鍵盤
  • 塊裝置
    • 訪問塊裝置時候,是以扇區過著塊為基礎,屬於無序訪問
  • 網路裝置
    • 網路裝置就是網路介面卡等用來上網的裝置
    • 網絡卡分為有線和無線兩種

字元裝置驅動

  • 字元裝置驅動在linux系統中有其規定的框架

常規字元裝置驅動

為裝置定義一個相關結構體

初始化函式 xxx_init

  • 向系統申請裝置號
  • 申請裝置記憶體
  • 呼叫cdev_init()初始化
  • 呼叫cdev_add()向系統註冊裝置

解除安裝函式 xxx_exit的定義

  • 釋放裝置號
  • 呼叫cdev_add()

定義 file_operations

  • 實現write()
  • 實現read()
  • 根據需要實現其他函式
//機構體模組
struct A{
    struct cdev cdev;
    
};

struct A *dev;//建立一個裝置結構體指標
dev_t devno;//裝置號結構體

//讀裝置
ssize_t xxx_read(struct file *p,char __user *buf,size_t count,loff_t* f_pos)
{
    copy_to_user(buf, , , );
}

//寫裝置
ssize_t xxx_write
(struct file *filp, const char __user *buf, size_t count, loff_t* f_pos) { copy_from_user(..., buf, ...); } //檔案操作函式file_operations struct file_operations xxx_fops = { .owner = THIS_MODULE, .read = xxx_read, .write = xxx_write, }; //裝置驅動模組載入函式 static int __init xxx_init(void) { ... devno =
MKDEV(xxx_major, 0); //(1)申請裝置號 if(xxx_major) { register_chrdev_region(devno, 1, "xxx_dev"); } else { alloc_chrdev_region(&devno, 0, 1, "xxx_dev"); } //(2)為裝置結構體申請記憶體(推薦使用devm_kzalloc) dev = kzalloc(sizeof(struct xxx_dev_t), GFP_KERNEL); //(3)初始化cdev cdev_init(&dev.cdev, &xxx_fops); dev.cdev.owner = THIS_MODULE; //(4)向系統註冊裝置 cdev_add(dev.cdev, dev_no, 1); } module_init(xxx_init); //裝置驅動模組解除安裝函式 static void __exit xxx_exit(void) { //釋放裝置號 unregister_chrdev_region(dev_no, 1); //登出裝置 cdev_del(&dev.cdev); ... } module_exit(xxx_exit); MODULE_LICENSE("GPL v2");

程式碼解釋

  • 首先定義了機構體,直接定義了結構體cdev

    struct cdev {
    struct kobject kobj; //內嵌的kobject 物件
    struct module *owner; //所屬模組
    const struct file_operations *ops; //檔案操作結構體
    struct list_head list;
    dev_t dev; //裝置號
    unsigned int count;
    };
    
  • 然後定義了裝置號機構體,並在第37行申請了裝置號、

  • 實現了檔案操作函式總的部分功能。

    file_operations中的成員函式似乎字元裝置驅動程式的主要內容

  • 裝置驅動模組載入函式

  • 為此函式註冊為裝置驅動的入口函式

  • 初始化cdev,向系統註冊裝置

簡化版字元裝置驅動框架

可以使用這個兩個函式對上面的函式進行簡化

static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
//註冊字元裝置,返回申請的主裝置號
static inline void unregister_chrdev(unsigned int major, const char *name)
//登出字元裝置   
//通過這兩個函式就不用一步一步的去註冊裝置了

兩個檔案就實現註冊裝置了,具體檔案於fs/char_dev.c

裝置號

· 各種裝置都以檔案的形式放在/dev 目錄下,稱為裝置檔案,應用程式可以開啟,關閉,讀寫這些裝置檔案,完成對應的裝置的操作。

組成

  • 一個字元裝置或者塊裝置都有一個主次裝置號。

    • 主裝置號都用來表示一個特定的驅動程式。

    • 次裝置號永安裡表示該驅動程式的各個裝置

  • dev_t的資料型別表示裝置號

    typedef __u32 __kernel_dev_t; typedef __kernel_dev_t dev_t;

  • dev_t是一個32位變數

    linux裝置號範圍是0 - 4095

  • 分配

    • 靜態分配裝置號

      • 驅動程式開發者通過靜態指定一個裝置號
      • 核心裝置號再核心原始碼documentation/ devices.txt裡面
      • cat /proc/devices檢視所有裝置號
    • 動態分配裝置號

      用於解決裝置號存在衝突問題,因此建議使用動態分配裝置號

      • 註冊字元裝置之前申請一個裝置號
    • 不用了解除安裝即可

      驅動模組的安裝與解除安裝

  • 寫好驅動之後,執行驅動

    • 驅動編譯到linux核心中,linux核心執行時候會自動執行驅動程式

    • 驅動程式編譯字尾為.ko的模組檔案

      • 安裝和解除安裝.ko的命令

        insmod xxx.ko安裝驅動

        rmmod xxx.ko解除安裝驅動

        實際上是呼叫module_init函式,這個函式會註冊一個載入函式模組,使用上述命令的時候就呼叫了函式

      • 安裝和解除安裝驅動

        modprobe xxx.ko //安裝模組

        modprobe -r xxx.ko//解除安裝模組

        這個命令在載入模組時候,會同時載入該模組所依賴的其他模組

實驗課題

(2)