2019.05.08 《Linux驅動開發入門與實戰》


第六章:字符設備

 

申請設備號---注冊設備

 

1、字符設備的框架:

2、結構體,struct cdev:

3、字符設備的組成:

4、例子:

5、申請和釋放設備號:

 

 

設備號和設備節點是什么關系。?

設備驅動中,很多功能是通過設備號完成的。

 

 

步驟:

構建字符設備前,應該申請設備號:所用到的函數是下面兩個:

該函數在<fs/char_dev.c>中定義

  int register_chrdev_region(dev_t from, unsigned count, const char *name);

  int alloc_chrdev_region(dev_t *dev, unsigned baseminor,unsigned count, const char *name) ;

釋放設備號,統一使用下面的函數,通常在模塊的卸載函數中會調用這個。

  void unregister_chrdev_region(dev_t from, unsigned count);

申請字符設備的設備號后,需要將字符設備注冊到系統中。這樣才能使用字符設備。

為了實現這個過程,需要先解釋cdev結構體。cdev結構體描述字符設備,該結構體是

所有字符設備的抽象,其包含了大量字符設備所共有的特性。

  cdev中kobj成員,用於內核管理字符設備,驅動開發基本不用。

  cdev中ops指向的file_operations結構的指針,該結構定義了操作字符設備的函數。

  cdev中dev是用來存儲字符設備所申請的設備號。

  cdev中count表示目前有多少個字符設備在使用改啟動程序。

  cdev中list是一個雙向鏈表,用於將其他結構體鏈接成一個雙向鏈表。這種結構在linux中常用,需要掌握。

每一個字符設備在/dev目錄下都有一個設備文件,打開設備文件就相當於打開相應的字
符設備。例如應用程序打開設備文件A,那么系統會產生一個inode結點。這樣可以通過
inode結點的i_cdev字段找到cdev字符結構體。通過cdev的ops指針,就能找到設備A的
操作函數。對操作函數的講解,將放在后面的內容中。

 

file_operations是一個對設備進行操作的抽象結構體。Linux內核的設計非常巧妙。內
核允許為設備建立一個設備文件,對設備文件的所有操作,就相當於對設備的操作。這
樣的好處是,用戶程序可以使用訪問普通文件的方法訪問設備文件,進而訪問設備。這
樣的方法,極大地減輕了程序員的編程負擔,程序員不必去熟悉新的驅動接口,就能夠
訪問設備。

 

對普通文件的訪問常常使用open()、read()、write()、close()、ioctl()等方法。同
樣對設備文件的訪問,也可以使用這些方法。這些調用最終會引起對
file_operations結構體中對應函數的調用。對於程序員來說,只要為不同的設備編寫
不同的操作函數就可以了。

為了使file_operations結構體具有通用性,file_operations會不斷地擴充,現在已經非常

龐大,只需要對file_operations結構體中幾個重要的成員進行分析:

  owner成員根本不是一個函數;它是一個指向擁有這個結構模塊的指針。這個成員用來
  維持模塊的引用計數,當模塊還在使用時,不能用rmmod卸載模塊。幾乎所有時刻,它
  被簡單初始化為 THIS_MODULE,一個在<linux/module.h>中定義的宏。

  llseek()函數用來改變文件中的當前讀/寫位置,並將新位置返回。loff_t參數是一個
  "long long"類型,"long long"類型即使在32位機上也是64位寬。這是為了與64位機兼
  容而定的,因為64位機的文件大小完全可以突破4G。

  read()函數用來從設備中獲取數據,成功時函數返回讀取的字節數,失敗時返回一個負
  的錯誤碼。
  write()函數用來寫數據到設備中。成功時該函數返回寫入的字節數,失敗時返回一個
  負的錯誤碼。
  ioctl()函數提供了一種執行設備特定命令的方法。例如使設備復位,這既不是讀操作
  也不是寫操作,不適合用read()和write()方法來實現。如果在應用程序中給ioctl傳入
  沒有定義的命令,那么將返回-ENOTTY的錯誤,表示該設備不支持這個命令。
  open()函數用來打開一個設備,在該函數中可以對設備進行初始化。如果這個函數被復
  制NULL,那么設備打開永遠成功,並不會對設備產生影響。
  release()函數用來釋放open()函數中申請的資源,將在文件引用計數為0時,被系統調
  用。其對應應用程序的close()方法,但並不是每一次調用close()方法,都會觸發
  release()函數,在對設備文件的所有打開都釋放后,才會被調用。

一般來說,驅動開發人員會將特定設備的特定數據放到cdev結構體后,組成一個新的結
構體。如圖6.3所示,"自定義字符設備"中就包含特定設備的數據。該"自定義設備"中
有一個cdev結構體。cdev結構體中有一個指向file_operations的指針。這里
,file_operations中的函數就可以用來操作硬件,或者"自定義字符設備"中的其他數
據,從而起到控制設備的作用。

 

inode結構體 【這個可以理解為設備節點?】
內核使用inode結構在系統內部表示文件。inode一般作為file_operations結構中函數的參
數傳遞過來。例如,open()函數將傳遞一個inode指針進來,表示目前打開的文件結點
。需要注意的是,inode的成員已經被系統賦予了合適的值,驅動程序只需要使用該結
點中的信息,而不用更改。Oepn()函數為:int (*open) (struct inode *, struct file *);

inode結構中包含大量的有關文件的信息。這里,只對編寫驅動程序有用的字段進行介
紹,對於該結構更多的信息,可以參看內核源碼。

  dev_t i_rdev,表示設備文件對應的設備號。

  struct list_head i_devices,如圖6.2所示,該成員使設備文件連接到對應的cdev結
  構,從而對應到自己的驅動程序。
  struct cdev *i_cdev,如圖6.2所示,該成員也指向cdev設備。
  除了從dev_t得到主設備號和次設備號外,這里還可以使用imajor()和iminor()函數從
  i_rdev中得到主設備號和次設備號。

 

在Linux系統中,字符設備驅動程序由以下幾個部分組成。

  字符設備加載和卸載函數

    在字符設備的加載函數中,應該實現字符設備號的申請和cdev的注冊。相反,在字符設
    備的卸載函數中應該實現字符設備號的釋放和cdev的注銷。
    cdev是內核開發者對字符設備的一個抽象。除了cdev中的信息外,特定的字符設備還需
    要特定的信息,常常將特定的信息放在cdev之后,形成一個設備結構體,如代碼中的
    xxx_dev。

  file_operations結構體和其成員函數

  驅動程序與應用程序的數據交換

字符設備是3大類設備(字符設備、塊設備、網絡設備)中較簡單的一類設備,其驅動
程序中完成的主要工作是初始化、添加和刪除cdev結構體,申請和釋放設備號,以及填
充file_operation結構體中操作函數,並實現file_operations結構體中的read()、
write()、ioctl()等重要函數。如圖6.4所示為cdev結構體、file_operations和用戶空
間調用驅動的關系。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM