Linux i2c驅動架構分析 (一)
Linux i2c驅動架構分析 (二)
Linux i2c驅動架構分析 (三)
通用i2c裝置驅動分析
Linux的i2c體系結構
Linux的i2c體系結構分為3個組成部分。
(1) i2c核心
i2c核心提供了i2c總線驅動和裝置驅動的注冊、登出方法,i2c通信方法上層的、與具體擴充卡無關的代碼以及探測裝置、檢測裝置位址的上層代碼等。
(2) i2c總線驅動
i2c總線驅動是對i2c硬體體系結構中擴充卡端的實作。
i2c總線驅動主要包含了i2c擴充卡資料結構i2c_adapter、i2c擴充卡的i2c_algorithm資料結構和控制i2c擴充卡産生通信信号的函數。
經由i2c總線驅動的代碼,我們可以控制i2c擴充卡以主要方式産生開始位、停止位、讀寫周期,以及以從裝置方式被讀寫、産生ack等。
(3) i2c裝置驅動
i2c裝置驅動是對i2c硬體體系結構中裝置端的實作,裝置挂接在i2c擴充卡上,通過i2c擴充卡與cpu交換資料。i2c裝置驅動主要包含了資料結構i2c_driver和i2c_client,我們需要根據具體裝置實作其中的成員函數。
i2c驅動架構如下圖所示。
在Linux核心源代碼中的drivers目錄下包含一個i2c目錄,而在i2c目錄下又包含如下檔案和檔案夾:
i2c-core.c,這個檔案實作了i2c核心的功能。
i2c-dev.c,實作了i2c擴充卡裝置檔案的功能,每一個i2c擴充卡都被配置設定一個裝置。
busses檔案夾包含了一些I2C總線的驅動,如針對S3C2410、S3C2440和S3C6410等處理器的i2c控制器驅動為i2c-s3c2410.c。
algos檔案夾,實作了一些i2c總線擴充卡的algorithm。
主要的資料結構
i2c_adapter結構體,描述一個i2c擴充卡(控制器),定義如下:
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
//總線通信方法
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* data fields that are valid for all devices */
const struct i2c_lock_operations *lock_ops;
struct rt_mutex bus_lock;
struct rt_mutex mux_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
//總線号
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
};
i2c_algorithm結構體,描述一個擴充卡的總線通信方法:
struct i2c_algorithm {
//傳輸資料函數
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
//SMBus傳輸函數
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/* 這個函數傳回擴充卡支援的功能 */
u32 (*functionality) (struct i2c_adapter *);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
SMBus大部分基于i2c總線規範,SMBus不需增加額外引腳。與i2c總線相比,SMBus增加了一些新的功能特性,在通路時序也有一定的差異。
i2c_driver結構體,描述一個i2c裝置驅動,定義如下:
struct i2c_driver {
unsigned int class;
//老式的探測函數,已不推薦使用
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
/* 驅動與适配時,會調用probe函數 */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
......
struct device_driver driver;
//該驅動所支援的裝置ID
const struct i2c_device_id *id_table;
/* 探測函數 */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
//探測裝置位址清單
const unsigned short *address_list;
//該驅動支援的裝置會通過這個連結清單頭鍊在一起
struct list_head clients;
}
i2c_client結構體,描述一個i2c裝置,定義如下:
struct i2c_client {
unsigned short flags; /* div., see below */
//裝置位址
unsigned short addr;
char name[I2C_NAME_SIZE];
//裝置所挂在的控制器
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
//通過這個連結清單節點,挂接在驅動的clients連結清單
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};
下面梳理一下i2c_driver、i2c_client、i2c_adapter和i2c_algorithm這4個資料結構的作用及其盤根錯節的關系。
-
i2c_adapter與i2c_algorithm
i2c_adapter對應于實體上的一個i2c擴充卡,而i2c_algorithm對應一套通信方法。一個i2c擴充卡需要i2c_algorithm中提供的通信函數來控制擴充卡上産生特定的通路周期。缺少 i2c_algorithm的i2c_adapter什麼也做不了,是以i2c_adapter中包含其使用的 i2c_algorithm 的指針。i2c_algorithm中的關鍵函數master_xfer()用于産生i2c通路周期需要的信号,以 i2c_msg為機關。i2c_msg結構體也非常關鍵,代碼清單如下給出了它的定義:
struct i2c_msg {
__u16 addr; /* 裝置位址 */
__u16 flags;
#define I2C_M_RD 0x0001 /* read data, from slave to master */
/* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
__u16 len; /* 資訊長度 */
__u8 *buf; /* 資訊資料 */
};
-
i2c_driver與i2c_client
i2c_driver對應一套驅動方法,其主要成員函數是probe()、 remove()、 suspend()、 resume()等,另外id_table是該驅動所支援的i2c裝置的ID表。i2c_client對應于真實的實體裝置,每個i2c裝置都需要一個i2c_client來描述。i2c_driver與i2c_client的關系是一對多,一個 i2c_driver上可以支援多個同等類型的i2c_client。
-
i2c_adpater與i2c_client
i2c_adpater與i2c_client的關系與i2c硬體體系中擴充卡和裝置的關系一緻,即i2c_client依附于i2c_adpater。由于一個擴充卡上可以連接配接多個I2C裝置,是以一個i2c_adpater也可以被多個i2c_client 依附,i2c_adpater中包括依附于它的i2c_client的連結清單。