linux I2C驅動框架(一)

目錄

I2C核心。

I2C 總線驅動。

I2C 設備驅動。

linux I2C代碼

i2c-core.c。

i2c-dev.c。

chips 文件夾。

busses 文件夾。

algos 文件夾。

I2C結構體

i2c_adapter 結構體

i2c_algorithm 結構體

i2c_driver 結構體

i2c_client 結構體

I2C結構體解析

i2c_adapter 與i2c_algorithm。

i2c_driver 與i2c_client。

i2c_adpater 與i2c_client。

驅動工程師工作


Linux 的I2C 體系結構分爲3個組成部分,下面對這三個部分進行詳細解釋。

I2C核心。

I2C 核心提供了I2C總線驅動和設備驅動的註冊、註銷方法,I2C 通信方法(即“algorithm”,筆者認爲 直譯爲“運算方法”並不合適,爲免引起誤解,下文將直接使用“algorithm”)上層的、與具體適配器無關的代碼以及探測設備、檢測設備地址的上層代碼等。如上圖中的紅色部分

I2C 總線驅動。

I2C 總線驅動是對 I2C 硬件體系結構中適配器端的實現,適配器可由 CPU 控制,甚至可以直接集成在 CPU 內部。

I2C 總線驅動主要包含了 I2C 適配器數據結構i2c_adapterI2C 適配器的algorithm數據結構i2c_algorithm和控制I2C 適配器產生通信信號的函數。

經由 I2C 總線驅動的代碼,我們可以控制 I2C適配器以主控方式產生開始位、停止位、讀寫週期,以及以從設備方式被 讀寫、產生 ACK 等。

如上圖中的紫色部分

I2C 設備驅動。

I2C 設備驅動是對 I2C 硬件體系結構中設備端的實現,設備一般掛接在受 CPU 控制的 I2C 適配器上,通過 I2C 適配器與 CPU 交換數據。如上圖中的綠色部分

I2C 設備驅動主要包含了數據結構 i2c_driver和i2c_client,我們需要根據具體設備實現其中的成員函數。

Linux 2.6 內核中,所有的 I2C 設備都在 sysfs 文件系統 中顯示,存於/sys/bus/i2c/目錄,以適配器地址和芯片地址的形式列出,例如:

$ tree /sys/bus/i2c/

/sys/bus/i2c/

|-- devices

| |-- 0-0048 -> ../../../devices/legacy/i2c-0/0-0048

| |-- 0-0049 -> ../../../devices/legacy/i2c-0/0-0049

| |-- 0-004a -> ../../../devices/legacy/i2c-0/0-004a

| |-- 0-004b -> ../../../devices/legacy/i2c-0/0-004b

| |-- 0-004c -> ../../../devices/legacy/i2c-0/0-004c

| |-- 0-004d -> ../../../devices/legacy/i2c-0/0-004d

| |-- 0-004e -> ../../../devices/legacy/i2c-0/0-004e

| '-- 0-004f -> ../../../devices/legacy/i2c-0/0-004f

'-- drivers

|-- i2c_adapter

'-- lm75

|-- 0-0048 -> ../../../../devices/legacy/i2c-0/0-0048

|-- 0-0049 -> ../../../../devices/legacy/i2c-0/0-0049

|-- 0-004a -> ../../../../devices/legacy/i2c-0/0-004a

|-- 0-004b -> ../../../../devices/legacy/i2c-0/0-004b

|-- 0-004c -> ../../../../devices/legacy/i2c-0/0-004c

|-- 0-004d -> ../../../../devices/legacy/i2c-0/0-004d

|-- 0-004e -> ../../../../devices/legacy/i2c-0/0-004e

'-- 0-004f -> ../../../../devices/legacy/i2c-0/0-004f

linux I2C代碼

Linux 內核源代碼中的 drivers 目錄下包含一個 i2c 目錄,而在 i2c 目錄下又包含如下文件和文件夾。

  • i2c-core.c

這個文件實現了 I2C 核心的功能以及/proc/bus/i2c*接口。

  • i2c-dev.c。

實現了 I2C 適配器設備文件的功能,每一個I2C 適配器都被分配一個設備。通過適配器訪問設備時的主設備號都爲89,次設備號爲0~255。應用程序通過“i2c-%d”(i2c-0, i2c-1,…, i2c-10,…)文件名並使用文件操作接口open()、write()、read()、ioctl()和close()等來訪問這個設備。

i2c-dev.c 並沒有針對特定的設備而設計,只是提供了通用的read()、write()和ioctl()等接口,應用層可以借用這些接口訪問掛接在適配器上的I2C設備的存儲空間或寄存器,並控制I2C 設備的工作方式。直接只用此函數盡心I2C通信,通過過程不需要I2C設備驅動的支持

  • chips 文件夾。

這個目錄中包含了一些特定的I2C設備驅動,如Dallas公司的DS1337實時鐘芯片、EPSON公司的RTC8564實時鐘芯片和I2C接口的EEPROM驅動等。

  • busses 文件夾。

這個文件中包含了一些I2C主機控制器驅動(總線驅動),如S3C2410 的I2C 控制器驅動爲i2c-s3c2410.c。

  • algos 文件夾。

實現了一些 I2C總線適配器的algorithm通信方法。

I2C結構體

內核中的 i2c.h 這個頭文件i2c_driver、i2c_client、i2c_adapter 和i2c_algorithm 這4 個數據結構進行了定義。理解這4 個結構體的作用十分關鍵。

  • i2c_adapter 結構體

struct i2c_adapter {
    struct module *owner;/*所屬模塊*/    
    unsigned int class;
    struct i2c_algorithm *algo;/*總線通信方法結構體指針 */
    void *algo_data; /* algorithm 數據 */

    struct semaphore bus_lock; /*控制併發訪問的自旋鎖*/
    int timeout;
    int retries; /*重試次數*/
    struct device dev; /* 適配器設備 */

    int nr;    
    char name[48]; /*適配器名稱*/
    struct completion dev_released; /*用於同步*/

    struct mutex userspace_clients_lock;
    struct list_head userspace_clients; /* client 鏈表頭*/
    Struct i2c_bus_recovery_info *bus_recovery_info;
};
  • i2c_algorithm 結構體

struct i2c_algorithm {
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
    /*i2c 傳輸函數指針,對應於普通的 i2c 傳輸協議*/

    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
    /*smbus 傳輸函數指針,對應於i2c協議子集 smbus ,有些設備只支持這個協議*/
            unsigned short flags, char read_write,
            u8 command, int size, union i2c_smbus_data * data);

    u32 (*functionality) (struct i2c_adapter *);
    /*返回適配器支持的功能,用來描述,adapter 所具有的功能,比如是否支持 smbus*/

};

上述代碼的master_xfer對應爲I2C傳輸函數指針,I2C的主機驅動部分的大部分工作也聚焦在這裏,smbus_xfer對應SMBus 傳輸函數指針,SMBus大部分基於I2C總線規範,SMBus不需增加額外引腳。與I2C總線相比,SMBus增加了一些新的功能性,在訪問時序也有一定的差異。

  • i2c_driver 結構體

struct i2c_driver {
    unsigned int class;
    /* Notifies the driver that a new bus has appeared or is about to be
     * removed. You should avoid using this, it will be removed in a
     * near future.
     */
    int (*attach_adapter)(struct i2c_adapter *) __deprecated;
    int (*detach_adapter)(struct i2c_adapter *) __deprecated;

    /* Standard driver model interfaces */
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    int (*remove)(struct i2c_client *);

    /* driver model interfaces that don't relate to enumeration  */
    void (*shutdown)(struct i2c_client *);
    int (*suspend)(struct i2c_client *, pm_message_t mesg);
    int (*resume)(struct i2c_client *);

    /* Alert callback, for example for the SMBus alert protocol.
     * The format and meaning of the data value depends on the protocol.
     * For the SMBus alert protocol, there is a single bit of data passed
     * as the alert response's low bit ("event flag").
     */
    void (*alert)(struct i2c_client *, unsigned int data);

    /* a ioctl like command that can be used to perform specific functions
     * with the device.
     */
    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

    struct device_driver driver;
    const struct i2c_device_id *id_table;

    /* Device detection callback for automatic device creation */
    int (*detect)(struct i2c_client *, struct i2c_board_info *);
    const unsigned short *address_list;
    struct list_head clients;
};
  • i2c_client 結構體

struct i2c_client {
    unsigned short flags; /* div., see below */
    unsigned short addr; /* chip address - NOTE: 7bit */
    /* addresses are stored in the */
    /* _LOWER_ 7 bits */
    char name[I2C_NAME_SIZE];
    struct i2c_adapter *adapter; /* the adapter we sit on */
    struct i2c_driver *driver; /* and our access routines */
    struct device dev; /* the device structure */
    int irq; /* irq issued by device */
    struct list_head detected;
};

I2C結構體解析

下面分析i2c_driver、i2c_client、i2c_adapter 和i2c_algorithm 這4 個數據結構的作用及其盤根錯節的關係。

  • i2c_adapter 與i2c_algorithm。

i2c_adapter 對應於物理上的一個適配器,而i2c_algorithm 對應一套通信方法。一個I2C 適配器需要i2c_algorithm 中提供的通信函數來控制適配器上產生特定的訪問週期。缺少i2c_algorithm 的i2c_adapter 什麼也做不了,因此i2c_adapter 中包含其使用的i2c_algorithm 的指針。

i2c_algorithm 中的關鍵函數master_xfer()用於產生I2C 訪問週期需要的信號,以i2c_msg(即I2C 消息)爲單位。i2c_msg 結構體也非常關鍵,代碼如下所示

struct i2c_msg {
    __u16 addr; /* slave address */
    __u16 flags;
    __u16 len; /* msg length */
    __u8 *buf; /* pointer to msg data */
};
  • i2c_driver 與i2c_client。

i2c_driver 對應一套驅動方法,是純粹的用於輔助作用的數據結構,它不對應於任何的物理實體。其主要成員函數是probe、remove、suspend、resume等,另外。Struct i2c_device_id形式的id_table是該驅動所支持的I2C設備的ID表。i2c_client對應於真正的物理設備,每個i2c設備都需要一個i2c_client來描述,i2c_driver於i2c_client的關係是一對多,一個i2c_driver可以支持多個類型的i2c_client。

i2c_client的信息通常在bsp的版文件中通過i2c_board_info填充,如下面的代碼就定義了一個I2C設備的id爲“ad7142_joystick”、地址爲0X2C,中斷號爲39的i2c_client。

static struct i2c_board_info __initdata bfin_i2c_board_info = {

{
    I2C_BOARD_INFO("ad7142_joystick", 0x2C),
    .irq = 39,
}
  • i2c_adpater 與i2c_client。

i2c_adpater 與i2c_client 的關係與I2C 硬件體系中適配器和設備的關係一致,即i2c_client 依附於i2c_adpater。由於一個適配器上可以連接多個I2C 設備,所以一個i2c_adpater 也可以被多個i2c_client 依附,i2c_adpater 中包括依附於它的i2c_client 的鏈表。

驅動工程師工作

雖然 I2C 硬件體系結構比較簡單,但是I2C 體系結構在Linux 中的實現卻相當複雜。當工程師拿到實際的電路板,面對複雜的Linux I2C 子系統,應該如何下手寫驅動呢?究竟有哪些是需要親自做的,哪些是內核已經提供的呢?理清這個問題非常有意義,可以使我們面對具體問題時迅速地抓住重點。

一方面,適配器驅動可能是 Linux 內核本身還不包含的;另一方面,掛接在適配器上的具體設備驅動可能也是Linux 內核還不包含的。即便上述設備驅動都存在於Linux 內核中,其基於的平臺也可能與我們的電路板不一樣。因此,工程師要實現的主要工作如下。

  • 提供 I2C 適配器的硬件驅動,探測、初始化I2C 適配器(如申請I2C 的I/O 地址和中斷號)、驅動CPU 控制的I2C 適配器從硬件上產生各種信號以及處理I2C 中斷等。

  • 提供 I2C 適配器的algorithm,用具體適配器的xxx_xfer()函數填充i2c_algorithm 的master_xfer 指針,並把i2c_algorithm 指針賦值給i2c_adapter 的algo 指針。

  • 實現 I2C 設備驅動與i2c_driver 接口,用具體設備yyy 的yyy_attach_adapter()函數指針、yyy_detach_client()函數指針和yyy_command()函數指針的賦值給i2c_driver 的attach_ adapter、detach_adapter 和detach_client 指針。

  • 實現 I2C 設備驅動的文件操作接口,即實現具體設備yyy 的yyy_read()、yyy_write()和yyy_ioctl()函數等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章