展讯智能机平台代码i2c设备驱动解读(上)

来源:互联网 发布:sas有几个软件 编辑:程序博客网 时间:2024/05/19 14:35
 
接触linux有两年多的时间了,以前觉得懂linux的人很牛B,学会了linux还可以装B,现在回想起来,有这种想法真的是很傻很天真,图样图森破。真正装B的人是那些一边呼唤公平正义一边利用各种关系上位,一边呼喊保护环境蓝天白云一边开着大排量的SUV,一边高呼民主自由占中一边断了别人的行路的自由上学的自由工作挣钱的自由购物的自由。对于这样的装B,我只能羡慕妒忌恨,臣妾做不到,臣妾真的做不到啊。

我琢磨着这辈子都必须和linux相依为伴,不离不弃了,不然,我还能干什么?这些年来,对我来说,生存总是那么困难,生活的压力总是那么大,不好好码代码,还能有什么出路。

闲话少说,妈咪说,上钟的时间到了。今天,要码的猪脚,就是linux i2c设备驱动。理论知识上的诸多描述,摘抄自网络上的多位大牛,而所有的代码,都来源于展讯发布的7715平台代码版本。

 

前面已经说过,linux设备驱动模型分为3个重要部分,总线bus,设备device,驱动driver。现在,具体问题具体分析。在Linux驱动中I2C系统中主要包含以下几个成员:

 

I2C adapter 即I2C适配器,依附于I2C总线,可以理解为I2C总线控制器。

I2C driver 某个I2C设备的设备驱动。

I2C device, 某个I2C设备的设备本身,在代码中以i2c_client指代。

 

下面,咱们开始具体介绍:

 

I2C adapter

目前一般的手机平台都集成了I2C适配器,用来控制各种I2C从设备,我们可以理解为I2C总线控制器。

i2c_adapter结构体如下:

/*

 * i2c_adapter is the structure used to identify a physical i2c bus along

 * with the access algorithms necessary to access it.

 */

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       */

         struct rt_mutex bus_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;

};

实现适配器的最主要的工作是需要完成i2c_algorithm结构体。这个结构体包含了此I2C控制器的数据传输具体实现,以及对外上报此设备所支持的功能类型。i2c_algorithm结构体如下:

/*

 * The following structs are for those who like to implement new bus drivers:

 * i2c_algorithm is the interface to a class of hardware solutions which can

 * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584

 * to name two of the most common.

 */

struct i2c_algorithm {

         /* If an adapter algorithm can't do I2C-level access, set master_xfer to NULL. If an adapter algorithm can do SMBus access, setsmbus_xfer. If set to NULL, the SMBus protocol is simulated using common I2C messages */

         /* master_xfer should return the number of messages successfullyprocessed, or a negative value on error */

         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);

         /* To determine what the adapter supports */

         u32 (*functionality) (struct i2c_adapter *);

};

代码不多,而且大部分都还是注释。注释也很简单:如果一个I2C适配器不支持I2C通道,那么就将master_xfer成员设为NULL。如果适配器支持SMBUS协议,那么需要去实现smbus_xfer,如果smbus_xfer指针被设为NULL,那么当使用SMBUS协议的时候将会通过I2C通道进行仿真。master_xfer指向的函数的返回值应该是已经成功处理的消息数,或者返回负数表示出错了。functionality指针很简单,告诉询问着这个I2C主控器都支持什么功能。

在展讯平台,这个i2c_algorithm被定义如下:

/* i2c bus registration info */

static const struct i2c_algorithm sprd_i2c_algorithm = {

         .master_xfer             = sprd_i2c_xfer,

         .functionality             = sprd_i2c_func,

};

具体的I2C通讯实现,都依靠这个sprd_i2c_xfer来执行。支持的功能,则在prd_i2c_func里声明。

 

 

I2C driver

 

具体的I2C设备驱动,如相机、传感器、触摸屏、背光控制器常见硬件设备大多都有或都是通过I2C协议与主机进行数据传输、控制。结构体如下:

struct i2c_driver {

         unsigned int class;

 

         /* Notifies the driver that a new bus has appeared. You should avoid

          * using this, it will be removed in a near future.

          */

         int (*attach_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设备驱动使用如下:

static const struct i2c_device_id gslX680_ts_id[] = {

         { GSLX680_NAME, 0 },{ }

};

 

 

MODULE_DEVICE_TABLE(i2c, gslX680_ts_id);

 

static struct i2c_driver gslX680_ts_driver = {

         .probe                = gslX680_ts_probe,

         .remove            = gslX680_ts_remove,

         .id_table  = gslX680_ts_id,

         .driver      = {

                   .name       = GSLX680_NAME,

                   .owner     = THIS_MODULE,

         },

};

module_i2c_driver(gslX680_ts_driver);

 

gslX680_ts_id内定义了该I2C DRIVER驱动支持设备名为GSLX680_NAME。需要注意的是,如同一个普通的USB驱动可以支持多个USB存储设备一样,一个I2C driver也可以对应支持多个I2C设备。可以支持的I2C设备在该driver的i2c_device_id内一一列明。我们之前已经说过,Linux内核依靠i2c_device_id所列内容将设备与驱动进行配对。这就好比人类的相亲,条件先列好来:有房有车,上无老下无小,条件符合就继续进行,等不急的直接拿证;条件不符合的,你不是我的菜,该干嘛干嘛去,接下来继续寻找条件符合的去。在Linux的世界里,一就是一,二就是二,一切都写的明明白白,不像咱们人类世界,暗箱操作,潜规则横行,兄弟我尤其羡慕那些导演的潜规则……

 

MODULE_DEVICE_TABLE(i2c, gslX680_ts_id),将该driver支持的设备列表加入i2c总线设备列表中。

 

gslX680_ts_probe,驱动匹配函数,driver通过该函数与设备进行进一步的匹配确认,通过后该设备才真正可用。

 

.owner     = THIS_MODULE,驱动拥有者为THIS_MODULE,当前模块。别问我为什么这么写,大家都这么用,我也不知道。就像大家都说民主自由后就可以买得起房,看得起病,养得起孩纸,读得起书,食物无毒,水无毒,空气无毒。Oh,My god,民主真是万能的包治百病的灵丹妙药。民主的美利坚万岁,民主的印度万岁,请和我干了这杯圣洁的带着自由灵光的恒河水,不吐不归。

 

module_i2c_driver(gslX680_ts_driver),我们看一下module_i2c_driver这个宏是如何定义:

/**

 * module_i2c_driver() - Helper macro for registering a I2C driver

 * @__i2c_driver: i2c_driver struct

 *

 * Helper macro for I2C drivers which do not do anything special in module

 * init/exit. This eliminates a lot of boilerplate. Each module may only

 * use this macro once, and calling it replaces module_init() and module_exit()

 */

#define module_i2c_driver(__i2c_driver) \

         module_driver(__i2c_driver, i2c_add_driver, \

                            i2c_del_driver)

 

简单解释下:module_i2c_driver宏用于注册一个I2C驱动,用户使用该宏来代替module_init() and module_exit()这两个函数。一言以蔽之,用了module_i2c_driver,就不再需要使用module_init() and module_exit()。

这一句宏就解决了模块module安装卸载的复杂代码。这样驱动开发者在实现I2C驱动时只要将i2c_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);

 

 

I2C device

 

在代码里用I2C client来指代,字面上的意思是I2C客户端,也即I2C device设备。在《linux内核学习—总线,设备,驱动》里我们有提到,I2C设备的注册一般在板级代码中,在展讯7715平台这个板级代码的名字叫board-sp7715ga.c,让我们来看看代码:

 

static struct i2c_board_info i2c1_boardinfo[] = {

#ifdef CONFIG_TOUCHSCREEN_FOCALTECH

         {

                   I2C_BOARD_INFO(FOCALTECH_TS_NAME, FOCALTECH_TS_ADDR),

                   .platform_data = &ft5x0x_ts_info,

         },

#endif

#ifdef       CONFIG_TOUCHSCREEN_GSLX680

    {

                   I2C_BOARD_INFO(GSLX680_TS_DEVICE, GSLX680_TS_ADDR),

                   .platform_data = &gslx680_ts_info,

         },

#endif       

}



i2c_register_board_info(1, i2c1_boardinfo, ARRAY_SIZE(i2c1_boardinfo));

 

看到了吗,GSLX680这个I2C TP设备的设备名和地址在此被注册,当名字与i2c_driver中的id_table中的成员匹配时就能够执行probe匹配函数并最终和其驱动勾搭上了。那么i2c_board_info这个结构体如何定义?

/**

 * struct i2c_board_info - template for device creation

 * @type: chip type, to initialize i2c_client.name

 * @flags: to initialize i2c_client.flags

 * @addr: stored in i2c_client.addr

 * @platform_data: stored in i2c_client.dev.platform_data

 * @archdata: copied into i2c_client.dev.archdata

 * @of_node: pointer to OpenFirmware device node

 * @acpi_node: ACPI device node

 * @irq: stored in i2c_client.irq

 *

 * I2C doesn't actually support hardware probing, although controllers and

 * devices may be able to use I2C_SMBUS_QUICK to tell whether or not there's

 * a device at a given address.  Drivers commonly need more information than

 * that, such as chip type, configuration, associated IRQ, and so on.

 *

 * i2c_board_info is used to build tables of information listing I2C devices

 * that are present.  This information is used to grow the driver model tree.

 * For mainboards this is done statically using i2c_register_board_info();

 * bus numbers identify adapters that aren't yet available.  For add-on boards,

 * i2c_new_device() does this dynamically with the adapter already known.

 */

struct i2c_board_info {

         char          type[I2C_NAME_SIZE];

         unsigned short         flags;

         unsigned short         addr;

         void           *platform_data;

         struct dev_archdata        *archdata;

         struct device_node *of_node;

         struct acpi_dev_node acpi_node;

         int              irq;

};

 

#define I2C_BOARD_INFO(dev_type, dev_addr) \

         .type = dev_type, .addr = (dev_addr)

 

结构体不长,注释却很多。幸好有这些注释,让我们在学习linux代码时没那么痛苦。将注释加上后如下:

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_client,漫漫人生路,我们需要再理解下i2c_client。

/**

 * struct i2c_client - represent an I2C slave device

 * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;

 *     I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking

 * @addr: Address used on the I2C bus connected to the parent adapter.

 * @name: Indicates the type of the device, usually a chip name that's

 *     generic enough to hide second-sourcing and compatible revisions.

 * @adapter: manages the bus segment hosting this I2C device

 * @driver: device's driver, hence pointer to access routines

 * @dev: Driver model device node for the slave.

 * @irq: indicates the IRQ generated by this device (if any)

 * @detected: member of an i2c_driver.clients list or i2c-core's

 *     userspace_devices list

 *

 * An i2c_client identifies a single device (i.e. chip) connected to an

 * i2c bus. The behaviour exposed to Linux is defined by the driver

 * managing the device.

 */

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_board_info的信息被用来填充i2c_client了。


0 0