Linux I2C裝置驅動編寫(一)

來源:互聯網
上載者:User

標籤:style   color   使用   資料   width   os   

在Linux驅動中I2C系統中主要包含以下幾個成員:

I2C adapter 即I2C適配器I2C driver 某個I2C裝置的裝置驅動,可以以driver理解。I2C client  某個I2C裝置的裝置宣告,可以以device理解。
I2C adapter

是CPU整合或外接的I2C適配器,用來控制各種I2C從裝置,其驅動需要完成對適配器的完整描述,最主要的工作是需要完成i2c_algorithm結構體。這個結構體包含了此I2C控制器的資料轉送具體實現,以及對外上報此裝置所支援的功能類型。i2c_algorithm結構體如下:

struct i2c_algorithm {    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,               int num);    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 *);};

如果一個I2C適配器不支援I2C通道,那麼就將master_xfer成員設為NULL。如果適配器支援SMBUS協議,那麼需要去實現smbus_xfer,如果smbus_xfer指標被設為NULL,那麼當使用SMBUS協議的時候將會通過I2C通道進行模擬。master_xfer指向的函數的傳回值應該是已經成功處理的訊息數,或者返回負數表示出錯了。functionality指標很簡單,告訴詢問著這個I2C主控器都支援什麼功能。

在核心的drivers/i2c/i2c-stub.c中實現了一個i2c adapter的例子,其中實現的是更為複雜的SMBUS。

SMBus 與 I2C的區別

通常情況下,I2C和SMBus是相容的,但是還是有些微妙的區別的。

時脈速度對比:

  I2C SMBus
最小 10kHz
最大 100kHZ(標準)400kHz(快速模式)2MHz(高速模式) 100kHz
逾時 35ms

在電氣特性上他們也有所不同,SMBus要求的電壓範圍更低。

I2C driver

具體的I2C裝置驅動,如相機、感應器、觸控螢幕、背光控制器常見硬體裝置大多都有或都是通過I2C協議與主機進行資料轉送、控制。結構體如下:

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;  //I2C裝置的驅動模型    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;};#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)  //一般編寫驅動過程中對象常是driver類型,可以通過to_i2c_driver找到其父類型i2c_driver

如同普通裝置的驅動能夠驅動多個裝置一樣,一個I2C driver也可以對應多個I2C client。

以重力感應器AXLL34X為例,其實現的I2C驅動為:

static const struct i2c_device_id adxl34x_id[] = {      { "adxl34x", 0 },  //匹配i2c client名為adxl34x的裝置     { }  }; MODULE_DEVICE_TABLE(i2c, adxl34x_id); static struct i2c_driver adxl34x_driver = {      .driver = {          .name = "adxl34x",         .owner = THIS_MODULE,         .pm = &adxl34x_i2c_pm,  //指定裝置驅動的電源管理介面,包含suspend、resume     },       .probe    = adxl34x_i2c_probe,  //組裝裝置匹配時候的匹配動作     .remove   = adxl34x_i2c_remove,  //組裝裝置移除介面     .id_table = adxl34x_id,  //制定匹配裝置列表 }; module_i2c_driver(adxl34x_driver);

這裡要說明一下module_i2c_driver宏定義(i2c.h):

#define module_i2c_driver(__i2c_driver)     module_driver(__i2c_driver, i2c_add_driver,                      i2c_del_driver)#define i2c_add_driver(driver)         i2c_register_driver(THIS_MODULE, driver)

module_driver():

#define module_driver(__driver, __register, __unregister, ...) static int __init __driver##_init(void) {         return __register(&(__driver) , ##__VA_ARGS__); } module_init(__driver##_init); static void __exit __driver##_exit(void) {         __unregister(&(__driver) , ##__VA_ARGS__); } module_exit(__driver##_exit);

理解上述宏定義後,將module_i2c_driver(adxl34x_driver)展開就可以得到:

static int __int adxl34x_driver_init(void){    return i2c_register_driver(&adxl34x_driver);}module_init(adxl34x_driver_init);static void __exit adxl34x_driver_exit(void){    return i2c_del_driver(&adxl34x_driver);}module_exit(adxl34x_driver_exit);

這一句宏就解決了模組module安裝卸載的複雜代碼。這樣驅動開發人員在實現I2C驅動時只要將i2c_driver結構體填充進來就可以了,無需關心裝置的註冊與反註冊過程。

I2C client

即I2C裝置。I2C裝置的註冊一般在板級代碼中,在解析執行個體前還是先熟悉幾個定義:

struct i2c_client {    unsigned short flags;        //I2C_CLIENT_TEN表示裝置使用10bit從地址,I2C_CLIENT_PEC表示裝置使用SMBus檢錯    unsigned short addr;        //裝置從地址,7bit。這裡說一下為什麼是7位,因為最後以為0表示寫,1表示讀,通過對這個7bit地址移位處理即可。addr<<1 & 0x0即寫,addr<<1 | 0x01即讀。    char name[I2C_NAME_SIZE];  //從裝置名稱    struct i2c_adapter *adapter;    //此從裝置依附於哪個adapter上    struct i2c_driver *driver;    // 此裝置對應的I2C驅動指標    struct device dev;        // 裝置模型    int irq;            // 裝置使用的中斷號    struct list_head detected;  //用於鏈表操作};#define to_i2c_client(d) container_of(d, struct i2c_client, dev)  //通常使用device裝置模型進行操作,可以通過to_i2c_client找到對應client指標struct i2c_board_info {    char        type[I2C_NAME_SIZE];  //裝置名稱,最長20個字元,最終安裝到client的name上    unsigned short    flags;  //最終安裝到client.flags    unsigned short    addr;  //裝置從地址slave address,最終安裝到client.addr上    void        *platform_data;  //裝置資料,最終儲存到i2c_client.dev.platform_data上    struct dev_archdata    *archdata;    struct device_node *of_node;  //OpenFirmware裝置節點指標    struct acpi_dev_node acpi_node;    int        irq;  //裝置採用的中斷號,最終儲存到i2c_client.irq上};//可以看到,i2c_board_info基本是與i2c_client對應的。#define I2C_BOARD_INFO(dev_type, dev_addr)     .type = dev_type, .addr = (dev_addr)//通過這個宏定義可以方便的定義I2C裝置的名稱和從地址(別忘了是7bit的)

下面還是以adxl34x為例:

static struct i2c_board_info i2c0_devices[] = {     {           I2C_BOARD_INFO("ak4648", 0x12),    },      {           I2C_BOARD_INFO("r2025sd", 0x32),    },      {           I2C_BOARD_INFO("ak8975", 0x0c),        .irq = intcs_evt2irq(0x3380), /* IRQ28 */    },      {           I2C_BOARD_INFO("adxl34x", 0x1d),        .irq = intcs_evt2irq(0x3340), /* IRQ26 */    },  };...i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices));

這樣ADXL34X的i2c裝置就被註冊到了系統中,當名字與i2c_driver中的id_table中的成員匹配時就能夠出發probe匹配函數了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.