分享

i2c设备驱动实例分析

 WUCANADA 2015-05-04
刚学习到i2c驱动这一块,在linux内核源码中(我用的是linux-2.6.38.6)有pca9541.c的驱动源码,所以我就拿该实例来学习i2c设备驱动开发。我在网上找了该设备的相关资料(主要是工作原理和datasheet),下面我把我的分析思路记录下来,作为我以后学习的参考资料。里面有许多我暂时不理解的地方,欢迎朋友们帮忙解决。谢谢!
/***************
**(1)头文件**
***************/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>

#include <linux/i2c/pca954x.h>

/****************
**(2)宏定义**
****************/

#define PCA9541_CONTROL        0x01
#define PCA9541_ISTAT        0x02

#define PCA9541_CTL_MYBUS    (1 << 0)
#define PCA9541_CTL_NMYBUS    (1 << 1)
#define PCA9541_CTL_BUSON    (1 << 2)
#define PCA9541_CTL_NBUSON    (1 << 3)
#define PCA9541_CTL_BUSINIT    (1 << 4)
#define PCA9541_CTL_TESTON    (1 << 6)
#define PCA9541_CTL_NTESTON    (1 << 7)

#define PCA9541_ISTAT_INTIN    (1 << 0)
#define PCA9541_ISTAT_BUSINIT    (1 << 1)
#define PCA9541_ISTAT_BUSOK    (1 << 2)
#define PCA9541_ISTAT_BUSLOST    (1 << 3)
#define PCA9541_ISTAT_MYTEST    (1 << 6)
#define PCA9541_ISTAT_NMYTEST    (1 << 7)

#define BUSON        (PCA9541_CTL_BUSON | PCA9541_CTL_NBUSON)
#define MYBUS        (PCA9541_CTL_MYBUS | PCA9541_CTL_NMYBUS)
#define mybus(x)    (!((x) & MYBUS) || ((x) & MYBUS) == MYBUS)
#define busoff(x)    (!((x) & BUSON) || ((x) & BUSON) == BUSON)

/* arbitration timeouts, in jiffies */
#define ARB_TIMEOUT    (HZ / 8)    /* 125 ms until forcing bus ownership */
#define ARB2_TIMEOUT    (HZ / 4)    /* 250 ms until acquisition failure */

/* arbitration retry delays, in us */
#define SELECT_DELAY_SHORT    50
#define SELECT_DELAY_LONG    1000
/******************
**(3)加载函数**
******************/

static int __init pca9541_init(void)
{
    return i2c_add_driver(&pca9541_driver);
}
首先从加载函数入手,慢慢展开分析。

 /*
 **注册:调用i2c_add_driver(struct i2c_driver *)函数
 **该函数位于linux/i2c.h中
 **其原型为:
 **static inline int i2c_add_driver(struct i2c_driver *driver)
   {     
         return i2c_register_driver(THIS_MODULE, driver);
   }

 **它又调用i2c_register_driver(sruct module *, struct i2c_driver *)
 **函数来完成注册,该函数位于driver/i2c/i2c-core.c中
 */
/*i2c_register_driver函数*/
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    int res;

    /* Can't register until after driver model init */
    if (unlikely(WARN_ON(!i2c_bus_type.p)))    //警告信息
        return -EAGAIN;                                      //      #define    EAGAIN        11    /* Try again */

    /* add the driver to the list of i2c drivers in the driver core */
    driver->driver.owner = owner;              //所属模块
    driver->driver.bus = &i2c_bus_type;    //总线类型

    /* When registration returns, the driver core
     * will have called probe() for all matching-but-unbound devices.
     */
    res = driver_register(&driver->driver);
    if (res)
        return res;

    /* Drivers should switch to dev_pm_ops instead. */
    if (driver->suspend)
        pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
            driver->driver.name);
    if (driver->resume)
        pr_warn("i2c-core: driver [%s] using legacy resume method\n",
            driver->driver.name);

    pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

    INIT_LIST_HEAD(&driver->clients);
    /* Walk the adapters that are already present */
    mutex_lock(&core_lock);                        //获取mutex
    bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
    mutex_unlock(&core_lock);                    //释放mutex

    return 0;
}

/*
**参数说明:&pca9541_driver是一个i2c_driver的结构体地址
**struct i2c_driver结构体的作用:它对应的设备的驱动方法,不对应具体的设备。这点暂时先说到这里,待分析到
**i2c设备驱动两大结构体之一的另一大结构体struct i2c_client时再具体分析。
**所以pca9541设备驱动中需要定义设备结构体struct i2c_driver pca9541_driver
*/
/*pca9541_driver结构体及其初始化*/
static struct i2c_driver pca9541_driver =
{
        driver =
        {
                .name = "pca9541",
                .owner = THIS_MODULE,
        },
        .probe = pca9541_probe,
        .remove = pca9541_remove
        .id_table = pca9541_id,
};
/*有了这个结构体后,就可以完成pca9541设备驱动的注册和注销。*/
下面就说一说i2c设备驱动的两大结构体:
                     struct i2c_driver
                     struct i2c_client
/*(1)struct i2c_driver结构体以及它所涉及到的结构体*/

struct i2c_driver {
    unsigned int class;

    int (*attach_adapter)(struct i2c_adapter *);                                      //依附i2c_adapter的函数指针
    int (*detach_adapter)(struct i2c_adapter *);                                     //脱离i2c_adapter的指针
   
    /* 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);                      //类似ioctl


    struct device_driver driver;                                                  //内嵌的device_driver结构体
    const struct i2c_device_id *id_table;                                   //
id_table 主要用来匹配设备与驱动程序的。
                                                                                               // 对于I2C驱动程序来说,只是比较它们的名字是否相同

    /* Device detection callback for automatic device creation */
    int (*detect)(struct i2c_client *, struct i2c_board_info *);         //i2c_client脱离函数指针

    const unsigned short *address_list;
    struct list_head clients;                                                            //链表头
};

/*attach_adapter,detach_adapter很重要。后面会说到*/
在这里说一下device_driver这个设备结构体。位于linux/device.h中
系统中的每个驱动程序由一个device_driver对象描述,对应的数据结构定义为:
struct device_driver {
    char *name;                                                                //设备驱动程序的名称
    struct bus_type *bus;                                                  //该驱动所管理的设备挂接的总线类型
    struct kobject kobj;                                                      //内嵌kobject对象
    struct list_head devices;                                             // 该驱动所管理的设备链表头
    int (*probe)(struct device *dev);                                  //指向设备探测函数,用于探测设备是否可以被该驱动程序管理
    int (*remove)(struct device *dev);                               //用于删除设备的函数
/* some fields omitted*/
};

/*(2)struct i2c_client结构体*/
struct i2c_client
{
        unsigned int flags;                                  //标志
        unsigned short addr;                              //低7位为芯片地址
        struct i2c_adapter *adapter;                  //依附的i2c_adapter
        struct i2c_driver *driver                          //依附的i2c_driver
        int usage_count;                                    //访问计数
        struct device dev;                                  //内嵌的设备结构体,这个很重要包括该设备属性信息
        struct list_head list;                               //链表头
        char name[I2C_NAME_SIZE];              //设备名称
        struct completion released;                   //用于同步
};

/*
**i2c_client对应具体的i2c实体设备,每一个i2c实体设备都需要一个i2c_client结构体来描述。
**上文中说到i2c_driver这个结构体,当它的attach_adapter()函数被运行时,attach_adapter()函数会探测物理设备,当探测到
**它所支持的设备时,它就会创建一个struct i2c_cilent结构体来描述这个设备。一个i2c_client代表着位于adapter总线上,地址为
**addr,使用driver来驱动的一个设备。它将总线驱动与设备驱动,以及设备地址绑定在了一起。一个i2c_client就代表着一个
**I2C设备
*/


/*下面我们分析pca9541_driver这个结构体*/
1).driver:它是一个内嵌的device_driver结构体。主要作用是对驱动的描述。
              这里pca541_driver初始化,说明了pca9541驱动
                                                                                           名称:
pca9541
                                                                                            所属模块:THIS_MODULE
               这里介绍一下THIS_MODULE
               在linux/module.h中有定义:
               extern struct module __this_module;
               #define THIS_MODULE (&__this_module)
               可见THIS_MODULE是个指针,它指向__this_module.
               而__this_module它是一个struct module结构类型的结构体。struct module 在该头文件中也有定义。
               这里要说明的是__this_module它是一个变量,表示当前模块。只有当驱动加载到内核后,这个符号才会产生,才会被
               赋值。

2).probe:探测函数指针 int (*probe)(struct i2c_client *, const struct i2c_device_id *)
              .probe = pca9541_probe
              表示该驱动要使用pca9541_probe函数来实现探测。主要就是去找能使用该驱动的i2c设备,并为其创建i2c_client结体。
3).remove:注销设备的i2c_client结构体。与.probe功能相反。int (*remove)(struct i2c_client *)
                 .remove = pca9541_remove
                 表示该驱动将使用pca9541_remove函数来实现注销。主要就是为注销掉使用该驱动的设备的i2c_client结构体。
4).id_table:
 id_table 主要用来匹配设备与驱动程序的。
                   对于I2C驱动程序来说,只是比较它们的名字是否相同。const struct i2c_device_id *id_table                                       
                  .id_table = pca9541_id

    
              表示
该驱动要使用pca9541_id来比较设备与驱动程序是否匹配。

/*下面我们要分析pca9541_id,pca9541_remove,pca9541_probe*/
1)pca9541_id
static const struct i2c_device_id pca9541_id[] = 
{
    {"pca9541", 0},
    {}
};

MODULE_DEVICE_TABLE(i2c, pca9541_id);
这里要分析一下i2c_device_id这个结构体。它位于linux/mod_devicetable.h中。
如果驱动可以支持好几个设备,那么这里面就要包含这些设备的ID
struct i2c_device_id {
    char name[I2C_NAME_SIZE];            //#define I2C_NAME_SIZE    20  表示设备名称
    kernel_ulong_t driver_data    /* Data private to the driver */         
            __attribute__((aligned(sizeof(kernel_ulong_t))));
};


2)pca9541_probe()
/*定义结构体pca9541,下面函数会用到*/
struct pca9541
 {
    struct i2c_adapter *mux_adap;
    unsigned long select_timeout;
    unsigned long arb_timeout;
};


static int pca9541_probe(struct i2c_client *client, const struct i2c_device_id *id) //首先传进来一个设备,一个设备ID
{
    struct i2c_adapter *adap = client->adapter;             //定义一个指向i2c_adapter的指针adap,并把赋初值client->adapter
    struct pca954x_platform_data *pdata = client->dev.platform_data;//定义平台数据信息,并把client的平台信息传给*pdata
    struct pca9541 *data;      //这里用到了struct pca9541这个结构体,所以我们需要定义它。
    int force;
    int ret = -ENODEV;          //ENODEV是系统错误定义,在linux/asm-generic/errno-bash.h中定义。表示no such device 值为19

    //下面函数用到了i2c_check_functionality(struct i2c_adapter *adap, u32 func)函数,所以在这里我们插入说明一下这个函数
    //在linux/i2c.h中定义了i2c_check_functionality(struct i2c_adapter *adap, u32 func)函数
    /* Return 1 if adapter supports everything we need, 0 if not. */
    static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
   {
        return (func & i2c_get_functionality(adap)) == func; //
这里用到了u32 i2c_get_functionality(struct i2c_adapter *adap)函数
                         //即判断适配器是否完全符合要求,匹配返回1,不匹配返回0
   }
   /* Return the functionality mask */
   static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
   {
       return adap->algo->functionality(adap);                  //即返回适配器支持的功能
   }


  关于I2C_FUNC_SMBUS_BYTE_DATA定义:在linux/i2c.h中
 
 #define I2C_FUNC_SMBUS_BYTE_DATA    (I2C_FUNC_SMBUS_READ_BYTE_DATA | \
                                                                         I2C_FUNC_SMBUS_WRITE_BYTE_DATA)

  #define I2C_FUNC_SMBUS_READ_BYTE_DATA      0x00080000
  #define I2C_FUNC_SMBUS_WRITE_BYTE_DATA    0x00100000

                    
    if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
        goto err;              //如果不匹配
/*这个函数就是原来的两个函数的整合 , 即原来我们每次申请内存的时候都会这么做 , 先是用 kmalloc() 申请空间 , 然后用 memset() 来初始化 , 而现在省事了, 一步到位 , 直接调用 kzalloc(), 效果等同于原来那两个函数 , 所有申请的元素都被初始化为 0. 其实对写驱动的来说 , 知道现在应该用 kzalloc() 代替原来的kmalloc()  memset() 就可以了 , 这是内核中内存管理部分做出的改变 , 确切的说是改进 , 负责内存管理那部分的兄弟们的目标无非就是让内核跑起来更快一些 , 而从 kmalloc/memset  kzalloc 的改变确实也是为了实现这方面的优化 */
     //宏GFP_KERNEL在linux/gfp.h中有定义
    data = kzalloc(sizeof(struct pca9541), GFP_KERNEL);//在内核空间分配一块内存
    if (!data) {
        ret = -ENOMEM;                //#define    ENOMEM        12    /* Out of memory */
        goto err;
    }
   //下面函数要用到void i2c_set_clientdata(struct i2c_client *dev, void *data)函数,这里插入说明(原型:linux/i2c.h)
   static inline void i2c_set_clientdata(struct i2c_client *dev, void *data)
   {
       dev_set_drvdata(&dev->dev, data);            //这里有疑问因为我没有找到dev_set_drvdata()函数原型
   }
     i2c_set_clientdata(client, data);    //传送私有数据

    /*
     * I2C accesses are unprotected here.
     * We have to lock the adapter before releasing the bus.
     */
    i2c_lock_adapter(adap);              //该函数在i2c-core.c中实现。获得锁
    pca9541_release_bus(client);     //该函数要在本程序中实现,完成操作后释放总线
    i2c_unlock_adapter(adap);         //释放锁

    /* Create mux adapter */

    force = 0;
    if (pdata)
        force = pdata->modes[0].adap_id;
    data->mux_adap = i2c_add_mux_adapter(adap, client, force, 0,   // i2c_add_mux_adapter()函数在drivers/i2c/i2c-mux.c
                         pca9541_select_chan,        //该函数在本驱动中需实现                                      //用于初始化 data->mux_adap
                         pca9541_release_chan);    //该函数在本驱动中需实现      

    if (data->mux_adap == NULL) {
        dev_err(&client->dev, "failed to register master selector\n");
        goto exit_free;
    }

    dev_info(&client->dev, "registered master selector for I2C %s\n",
         client->name);

    return 0;

exit_free:
    kfree(data);
err:
    return ret;
}

上面函数中涉及到pca9541_release_bus(struct i2c_client *client)函数下面我们就分析它
/* Release bus. Also reset NTESTON and BUSINIT if it was set. */
static void pca9541_release_bus(struct i2c_client *client)
{
    int reg;

    reg = pca9541_reg_read(client, PCA9541_CONTROL);               
 //读函数,在本驱动中需实现
    if (reg >= 0 && !busoff(reg) && mybus(reg))                                  
        pca9541_reg_write(client, PCA9541_CONTROL,                      
//写函数,在本驱动中需实现
                  (reg & PCA9541_CTL_NBUSON) >> 1);
}

/*pca9541_select_chan()函数*/
static int pca9541_select_chan(struct i2c_adapter *adap, void *client, u32 chan)
{
    struct pca9541 *data = i2c_get_clientdata(client);
    int ret;
    unsigned long timeout = jiffies + ARB2_TIMEOUT;
        /* give up after this time */

    data->arb_timeout = jiffies + ARB_TIMEOUT;
        /* force bus ownership after this time */

    do {
        ret = pca9541_arbitrate(client);
        if (ret)
            return ret < 0 ? ret : 0;

        if (data->select_timeout == SELECT_DELAY_SHORT)
            udelay(data->select_timeout);
        else
            msleep(data->select_timeout / 1000);
    } while (time_is_after_eq_jiffies(timeout));

    return -ETIMEDOUT;
}
/*pca9541_release_chan()函数*/
static int pca9541_release_chan(struct i2c_adapter *adap,
                void *client, u32 chan)
{
    pca9541_release_bus(client);
    return 0;
}
/*pca9541_contrl[]数组*/
/* Control commands per PCA9541 datasheet */
static const u8 pca9541_control[16] = {
    4, 0, 1, 5, 4, 4, 5, 5, 0, 0, 1, 1, 0, 4, 5, 1
};
/*pca9541_arbitrate()函数*/
/*
 * Channel arbitration
 *
 * Return values:
 *  <0: error
 *  0 : bus not acquired
 *  1 : bus acquired
 */

static int pca9541_arbitrate(struct i2c_client *client)
{
    struct pca9541 *data = i2c_get_clientdata(client);
    int reg;

    reg = pca9541_reg_read(client, PCA9541_CONTROL);
    if (reg < 0)
        return reg;

    if (busoff(reg)) {
        int istat;
        /*
         * Bus is off. Request ownership or turn it on unless
         * other master requested ownership.
         */
        istat = pca9541_reg_read(client, PCA9541_ISTAT);
        if (!(istat & PCA9541_ISTAT_NMYTEST)
            || time_is_before_eq_jiffies(data->arb_timeout)) {
            /*
             * Other master did not request ownership,
             * or arbitration timeout expired. Take the bus.
             */
            pca9541_reg_write(client,
                      PCA9541_CONTROL,
                      pca9541_control[reg & 0x0f]
                      | PCA9541_CTL_NTESTON);
            data->select_timeout = SELECT_DELAY_SHORT;
        } else {
            /*
             * Other master requested ownership.
             * Set extra long timeout to give it time to acquire it.
             */
            data->select_timeout = SELECT_DELAY_LONG * 2;
        }
    } else if (mybus(reg)) {
        /*
         * Bus is on, and we own it. We are done with acquisition.
         * Reset NTESTON and BUSINIT, then return success.
         */
        if (reg & (PCA9541_CTL_NTESTON | PCA9541_CTL_BUSINIT))
            pca9541_reg_write(client,
                      PCA9541_CONTROL,
                      reg & ~(PCA9541_CTL_NTESTON
                          | PCA9541_CTL_BUSINIT));
        return 1;
    } else {
        /*
         * Other master owns the bus.
         * If arbitration timeout has expired, force ownership.
         * Otherwise request it.
         */
        data->select_timeout = SELECT_DELAY_LONG;
        if (time_is_before_eq_jiffies(data->arb_timeout)) {
            /* Time is up, take the bus and reset it. */
            pca9541_reg_write(client,
                      PCA9541_CONTROL,
                      pca9541_control[reg & 0x0f]
                      | PCA9541_CTL_BUSINIT
                      | PCA9541_CTL_NTESTON);
        } else {
            /* Request bus ownership if needed */
            if (!(reg & PCA9541_CTL_NTESTON))
                pca9541_reg_write(client,
                          PCA9541_CONTROL,
                          reg | PCA9541_CTL_NTESTON);
        }
    }
    return 0;
}

/*pca9541_reg_read函数*/
/*
 * Read from chip register. Don't use i2c_transfer()/i2c_smbus_xfer()
 * as they will try to lock adapter a second time.
 */

static int pca9541_reg_read(struct i2c_client *client, u8 command)
{
    struct i2c_adapter *adap = client->adapter;
    int ret;
    u8 val;

    if (adap->algo->master_xfer) {
        struct i2c_msg msg[2] = {
            {
                .addr = client->addr,
                .flags = 0,
                .len = 1,
                .buf = &command
            },
            {
                .addr = client->addr,
                .flags = I2C_M_RD,
                .len = 1,
                .buf = &val
            }
        };
        ret = adap->algo->master_xfer(adap, msg, 2);
        if (ret == 2)
            ret = val;
        else if (ret >= 0)
            ret = -EIO;
    } else {
        union i2c_smbus_data data;

        ret = adap->algo->smbus_xfer(adap, client->addr,
                         client->flags,
                         I2C_SMBUS_READ,
                         command,
                         I2C_SMBUS_BYTE_DATA, &data);
        if (!ret)
            ret = data.byte;
    }
    return ret;
}

/pca9541_reg_writer()函数/
/*
 * Write to chip register. Don't use i2c_transfer()/i2c_smbus_xfer()
 * as they will try to lock the adapter a second time.
 */

static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val)
{
    struct i2c_adapter *adap = client->adapter;
    int ret;

    if (adap->algo->master_xfer) {
        struct i2c_msg msg;
        char buf[2];

        msg.addr = client->addr;
        msg.flags = 0;
        msg.len = 2;
        buf[0] = command;
        buf[1] = val;
        msg.buf = buf;
        ret = adap->algo->master_xfer(adap, &msg, 1);
    } else {
        union i2c_smbus_data data;

        data.byte = val;
        ret = adap->algo->smbus_xfer(adap, client->addr,
                         client->flags,
                         I2C_SMBUS_WRITE,
                         command,
                         I2C_SMBUS_BYTE_DATA, &data);
    }

    return ret;
}

//接下来我们分析pca9541_remove().它的作用是注销驱动设备
3)pca9541_remove()
static int pca9541_remove(struct i2c_client *client)
{
    struct pca9541 *data = i2c_get_clientdata(client);

    i2c_del_mux_adapter(data->mux_adap);

    kfree(data);
    return 0;
}

分析:上文中我们分析了i2c_client这个结构体。我们知道它对应一个实际的i2c设备信息。接下来我们就要分析它的另一个重要成
员-
struct i2c_adapter *adapter;                  //依附的i2c_adapter
/i2c_adapter结构体/
struct i2c_adapter
{
        struct module *owner;                                            //所属模块,一般为THIS_MODULE见注释5
        unsigned int id;                                                      //algorithm的类型,定义于i2c-id.h,以I2C_ALGO_开始
        unsigned int class;
        struct i2c_algorithm *algo;                                     //总线通信方法结构体指针
        void *algo_data;                                                     //algorithm数据
        int (*client_register)(struct i2c_client *);                 //client 注册时调用
        int (*client_unregister)(struct i2c_client *);             //client 注销时嗲用
        struct semaphore bus_lock;                                  //控制并发访问的自旋锁
        struct semaphore clist_lock;                                  //
        int timeout;
        int retries;                                                             //重试次数
        struct device dev;                                                 //适配器设备
        struct class_device class_dev;                             //类设备
        int nr;
        struct list_head clients;                                         //client链表头
        struct list_head list;
        char name[I2C_NAME_SIZE];                              //适配器名称
        struct completion dev_released;                           //用于同步
        struct completion class_dev_released;
};
说明:
         一个i2c_adapter结构体对应实际物理上的一个适配器。它是i2c总线驱动两大结构体之一。它的作用是:为实际的i2c设备提
         供通信总线。即i2c_client依附与i2c_adapter。由于一个适配器可以连接多个i2c设备,所以一个i2c_adapter可以被多个
         i2c_client所依附。故在i2c_adapter结构中包含依附它的i2c_client链表
:struct list_head clients。           
struct i2c_algorithm
{
        int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, \                    int num);  //i2c传输函数指针
        int (*master_xfer)(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);
        //smbus传输函数指针

        int (*slave_send)(struct i2c_adapter *, char *, int);                           
//当i2c适配器为slave时,发送函数
        
        int (*slave_recv)(struct i2c_adapter *, chat *, int);                            
//当i2c适配器为slave时,接收函数
       
        int (*algo_cintrol)(struct i2c-adapter*,unsigned int,unsigned long);
 //类似ioctl
        
        u32 (*functionality)(struct i2c_adapter *);                                        
//返回适配器支持的功能
};
说明:i2c_algorithm对应一套通信方法。它的作用是提供通信函数来控制i2c适配器上产生的特定的访问周期。
回过头来再来分析:pca9541_probe()函数。该函数的功能是探测能使用pca9541这个设备驱动的i2c设备并为其注册i2c_client结
构体。我们看它的参数1:struct i2c_client *client,
对应的是一个i2c设备;参数2: const struct i2c_device_id *id对应的是i2c设备名和设备ID。


/******************
**(4)卸载函数**
******************/

static void __exit pca9541_exit(void)
{
    i2c_del_driver(&pca9541_driver);
}
/******************
**(5)声明部分**
******************/

module_init(pca9541_init);
module_exit(pca9541_exit);

MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
MODULE_DESCRIPTION("PCA9541 I2C master selector driver");
MODULE_LICENSE("GPL v2");

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多