I2C驱动学习笔记

 

一、I2C基本知识

i2c总线是philips公司推出的一种串行总线。是具备多主机系统所需的,包括总线裁决和高低带器件同步功能的高性能串行总线。

i2c总线有两根双向信号线,一根是数据线SDA,另一根是时钟线SCL,两线都是开漏输出,要接上拉电阻(典型10K)。

3种数据传输模式:标准模式(100Kb/s),快速模式(400Kb/s),高速模式(3.4Mb/s)

每一个器件都有自己的地址。

工作模式:主发送,主接收,从发送,从接收。

数据格式:SDA线上的数据在SCL的高电平期间必须保持稳定,所心只SDA线上的数据只能在SCL线的低电平时改变。开始信号:SCL为高电平时,SDA线由高电平变为低电平。停止信号:SCL为高电平时,SDA线由低电平变为高电平。I2C总线中数据和地址都以字节为单位传送的,发送器每发送一个字节后,在时钟的第9个时钟脉冲期间释放总线,由从接收器发送一个ACK(SDA拉低)来确认数据传送成功。主机发送STOP来停止通信。主接收器接收到最后一个字节后发送NACK(SDA线为高电平),以确认接收完成。接着发送stop以停止通信。I2C分7位和10位两种寻址方式。通信时,发送的第一个字节是器件地址,无论是哪种寻址,第一个字节的最后一位为0,表示传送的是地址。10位寻址的前5位为11110。传送完地址后,从机会发送ack信号,然后主机再发送字节地址,从机发送ack信号。如果此时要写数据,则直接传送数据,如果为读数据,则要重新发送一次启动信号,和器件地址,但第一个字节变为1,之后再接到ack信号后,开始传送数据。也就是说读数据时,传送了两次设备地址。

二、I2C体系结构

Linux的i2c体系结构有3部分组成

1)I2C核心:提供了总线和设备驱动的注册和注销办法,I2C通信方法等。

2)I2C总线驱动:I2C硬件体系结构中适配器端的实现。主要包含两个数据结构i2c_adapter,algorithm和产生通信信号的函数。经过这一层的驱动代码,可以控制i2c适配器以主控的方式产生开始位、停止位,读写周期等。

3)I2C设备驱动:I2C硬件体系结构中设备端的实现。设备要挂接在受CPU控制的i2c适配器上,通过i2c适配器与CPU交换数据。

i2c设备驱动主要包含了数据结构i2c_driver和i2c_client。在linux2.6内核中,所有的i2c设备都在sysfs文件系统中显示。

 

4个重要的数据结构

struct i2c_adapter {

       struct module *owner;

       unsigned int id;

       unsigned int class;

       const struct i2c_algorithm *algo; /* the algorithm to access the bus */

       void *algo_data;

 

       /* --- administration stuff. */

       int (*client_register)(struct i2c_client *);

       int (*client_unregister)(struct i2c_client *);

 

       /* data fields that are valid for all devices     */

       u8 level;                /* nesting level for lockdep */

       struct mutex bus_lock;

       struct mutex clist_lock;

 

       int timeout;

       int retries;

       struct device dev;          /* the adapter device */

 

       int nr;

       struct list_head clients;   /* DEPRECATED */

       char name[48];

       struct completion dev_released;

};

i2c_adapter对应于物理上的一个适配器

algo,总线通信方法结构体指针

algo_data,私有数据结构指针

client_register,client注册时调用

client_unregister,client注销时调用

retries,重试次数

dev,适配器设备

name,适配器名称

clien,client链表头

 

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

       /* To determine what the adapter supports */

       u32 (*functionality) (struct i2c_adapter *);

};

SMbus_xfer对应SMbus,它是I2C总线的一个子集。

一个i2c适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的周期访问。

master_xfer()用于产生i2c访问周期需要的信号,以i2c_msg为单位。

struct i2c_msg {

       __u16 addr;    /* slave address                    */

       __u16 flags;

#define I2C_M_TEN            0x0010    /* this is a ten bit chip address */

#define I2C_M_RD              0x0001    /* read data, from slave to master */

#define I2C_M_NOSTART           0x4000    /* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_REV_DIR_ADDR 0x2000    /* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_IGNORE_NAK    0x1000    /* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_NO_RD_ACK             0x0800    /* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_RECV_LEN         0x0400    /* length will be first received byte */

       __u16 len;             /* msg length                       */

       __u8 *buf;             /* pointer to msg data                  */

};

 

struct i2c_driver {

       int id;

       unsigned int class;

 

       int (*attach_adapter)(struct i2c_adapter *);

       int (*detach_adapter)(struct i2c_adapter *);

 

       int (*detach_client)(struct i2c_client *);

 

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

 

       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 *, int kind, struct i2c_board_info *);

       const struct i2c_client_address_data *address_data;

       struct list_head clients;

};

i2c_driver对应一套驱动方法,id_table是该驱动所支持的i2c设备的ID表。

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 list;        /* DEPRECATED */

       struct list_head detected;

       struct completion released;

};

addr,低7位芯片地址

name,设备名称

adapter,依附的i2c_adapter

driver,依附的i2c_driver

irq,设备使用的中断号

i2c_client,对应一个真实的物理设备。每一个i2c设备都需要一个i2c_client来描述。i2c_client信息通常在BSP板文件中通过i2c_board_info填充。

struct i2c_board_info {

       char        type[I2C_NAME_SIZE];

       unsigned short       flags;

       unsigned short       addr;

       void        *platform_data;

       int           irq;

};

在i2c总线驱动i2c_bus_type的mach()函数会调用i2c_match_id()函数匹配板文件定义的ID和i2c_driver所支持的ID表

i2c_client依附于i2c_adapter。由于一个适配器可以连接多个I2C设备,所以i2c_adpater也可以被多个i2c_client依附,i2c_adapter中包括依附于它的i2c_client的链表。

三、编写I2C驱动

有两方面工作:

1.     编写总线驱动

n         申请,初始化硬件资源

n         i2c_adapter数据结构添加删除

n         提供适配器的algorithm的master_xfer指针

2.       编写设备驱动

n         填充i2c_driver结构,并实现其结构成员。

n         注册或注销i2c_driver

     总线驱动,即适配器驱动作用,是将设备挂载到总线,实现设备与总线的交互。而设备自身的操作还是依赖于设备驱动。在总线驱动与设备驱动关联中还有一个i2c核心,提供了linux内核与i2c总线驱动,设备驱动之间联系的实现方法。

关于i2c核心中的几个重要操作函数

增加/删除i2c_adapter

int i2c_add_adapter(struct i2c_adapter *adap);

int i2c_del_adapter(struct i2c_adapter *adap);

增加/删除i2c_driver

int i2c_register_driver(struct module *owner,struct i2c_driver *driver);

int i2c_del_driver(struct i2c_driver *driver);

inline int i2c_add_driver(struct i2c_driver *driver);

增加/删除i2c_client

int i2c_attach_client(struct i2c_client *client);

int i2c_detach_client(struct i2c_client *client);

I2C传输、发送和接收

int i2c_transfer(struct i2c_adapter *adap,struct i2c_msg *msgs,int num);

int i2c_master_send(struct i2c_client *client,const char *buf,int count);

int i2c_master_recv(struct i2c_client *client,char *buf,int count);

 

实例

总线驱动(drivers/i2c/busses/i2c-pnx.c)

i2c适配器的加载与卸载

static int __init i2c_adap_pnx_init(void)

{

     return platform_driver_register(&i2c_pnx_driver);

}

 

static void __exit i2c_adap_pnx_exit(void)

{

     platform_driver_unregister(&i2c_pnx_driver);

}

 

i2c_pnx_driver的定义

static struct platform_driver i2c_pnx_driver = {

     .driver = {

       .name = "pnx-i2c",

       .owner = THIS_MODULE,

     },

     .probe = i2c_pnx_probe,

     .remove = __devexit_p(i2c_pnx_remove),

     .suspend = i2c_pnx_controller_suspend,

     .resume = i2c_pnx_controller_resume,

};

i2c总线驱动被平台驱动封装了一下,那么加载i2c总线时,应在probe方法里。

 

static int __devinit i2c_pnx_probe(struct platform_device *pdev)

{

     unsigned long tmp;

     int ret = 0;

/*以下是i2c_pnx_algo_data的定义,这些数据可以自定义

 struct i2c_pnx_algo_data {

     u32                  base;

     u32                  ioaddr;

     int                    irq;

     struct i2c_pnx_mif   mif;

     int                    last;

}; */

     struct i2c_pnx_algo_data *alg_data;

     int freq_mhz;

 

/*以下是i2c_pnx_data的定义

 struct i2c_pnx_data {

     int (*suspend) (struct platform_device *pdev, pm_message_t state);

     int (*resume) (struct platform_device *pdev);

     u32 (*calculate_input_freq) (struct platform_device *pdev);

     int (*set_clock_run) (struct platform_device *pdev);

     int (*set_clock_stop) (struct platform_device *pdev);

     struct i2c_adapter *adapter;

}; */

     struct i2c_pnx_data *i2c_pnx = pdev->dev.platform_data;

     struct i2c_client *client;

 

//   printk("guojun i2c: %d, %s \n", pdev->id, i2c_pnx->adapter->name);

 

 

     if (!i2c_pnx || !i2c_pnx->adapter) {

       dev_err(&pdev->dev, "%s: no platform data supplied\n",

              __func__);

       ret = -EINVAL;

       goto out;

     }

 

     platform_set_drvdata(pdev, i2c_pnx);

/*device结构体有两个指针成员driver_data和platform_data。此时是将platform_data的指针和driver_data的指针指向同一个位置。因为platform_data在BSP板上已经初始化过,所以此处的作用即初始化driver_data */

 

//总线设备驱动中申请硬件资源。

     if (i2c_pnx->calculate_input_freq)

       freq_mhz = i2c_pnx->calculate_input_freq(pdev);

/*根据pdev->id成员,获得设备的时钟*/

     else {

       freq_mhz = PNX_DEFAULT_FREQ;

       dev_info(&pdev->dev, "Setting bus frequency to default value: "

              "%d MHz\n", freq_mhz);

     }

 

     i2c_pnx->adapter->algo = &pnx_algorithm;

//根据pnx_algorithm可以将数据控制成i2c总线要求的数据格式。

 

     alg_data = i2c_pnx->adapter->algo_data;

//这是一个私有数据结构,一般用来传递一些指针

     init_timer(&alg_data->mif.timer);

     alg_data->mif.timer.function = i2c_pnx_timeout;

     alg_data->mif.timer.data = (unsigned long)i2c_pnx->adapter;

//初始化一个定时器,以及和定时器相关联的函数

 

//申请完时钟,下一步申请I/O

     /* Register I/O resource */

     if (!request_region(alg_data->base, I2C_PNX_REGION_SIZE, pdev->name)) {

       dev_err(&pdev->dev,

              "I/O region 0x%08x for I2C already in use.\n",

              alg_data->base);

       ret = -ENODEV;

       goto out_drvdata;

     }

//在platform_device结构中,有一个resource结构,它也可以用来记录所需要的I/O资源。

 

     if (!(alg_data->ioaddr =

              (u32)ioremap(alg_data->base, I2C_PNX_REGION_SIZE))) {

       dev_err(&pdev->dev, "Couldn't ioremap I2C I/O region\n");

       ret = -ENOMEM;

       goto out_release;

     }

 

     i2c_pnx->set_clock_run(pdev);

 

     /*

      * Clock Divisor High This value is the number of system clocks

      * the serial clock (SCL) will be high.

      * For example, if the system clock period is 50 ns and the maximum

      * desired serial period is 10000 ns (100 kHz), then CLKHI would be

      * set to 0.5*(f_sys/f_i2c)-2=0.5*(20e6/100e3)-2=98. The actual value

      * programmed into CLKHI will vary from this slightly due to

      * variations in the output pad's rise and fall times as well as

      * the deglitching filter length.

      */

 

     tmp = ((freq_mhz * 1000) / I2C_PNX_SPEED_KHZ) / 2 - 2;

     iowrite32(tmp, I2C_REG_CKH(alg_data));

     iowrite32(tmp, I2C_REG_CKL(alg_data));

 

     iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data));

     if (wait_reset(I2C_PNX_TIMEOUT, alg_data)) {

       ret = -ENODEV;

       goto out_unmap;

     }

//将I2C时钟设置为I2C_PNX_SPEED_KHZ

 

     init_completion(&alg_data->mif.complete);

//初始化完成量

 

     ret = request_irq(alg_data->irq, i2c_pnx_interrupt,

              0, pdev->name, i2c_pnx->adapter);

     if (ret)

       goto out_clock;

//申请中断

 

     /* Register this adapter with the I2C subsystem */

     i2c_pnx->adapter->dev.parent = &pdev->dev;

//此处将i2c适配器连接到平台设备下

     ret = i2c_add_adapter(i2c_pnx->adapter);

//申请完并初始化硬件资源后添加adapter结构体,从而完成I2C总线设备驱动的前两步

 

     if (ret < 0) {

       dev_err(&pdev->dev, "I2C: Failed to add bus\n");

       goto out_irq;

     }

 

     dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n",

            i2c_pnx->adapter->name, alg_data->base, alg_data->irq);

 

#if 1

     if (pdev->id == 1) { //I2C2

       struct i2c_board_info info = {

              I2C_BOARD_INFO("pcf8563", 0xa3 >> 1),

       };

       client = i2c_new_device(i2c_pnx->adapter, &info);

 

       if (client == NULL) {

              printk("check pcf8563 faile \n");

       }

     }

 

#endif

//   printk("i2c go on \n");

    

     return 0;

 

out_irq:

     free_irq(alg_data->irq, alg_data);

out_clock:

     i2c_pnx->set_clock_stop(pdev);

out_unmap:

     iounmap((void *)alg_data->ioaddr);

out_release:

     release_region(alg_data->base, I2C_PNX_REGION_SIZE);

out_drvdata:

     platform_set_drvdata(pdev, NULL);

out:

     return ret;

}

 

还有第三步,完成i2c_agorithm算法。其实就是完成其中两个结构成员:master_xfer和functionality两个函数

 

static struct i2c_algorithm pnx_algorithm = {

     .master_xfer = i2c_pnx_xfer,

     .functionality = i2c_pnx_func,

};

 

static u32 i2c_pnx_func(struct i2c_adapter *adapter)

{

     return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;

}

 

至于I2C_FUNC_I2C, I2C_FUNC_SMBUS_EMUL具体代表什么样的通信协议,再议。

static int i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

{

     struct i2c_msg *pmsg;

     int rc = 0, completed = 0, i;

     struct i2c_pnx_algo_data *alg_data = adap->algo_data;

     u32 stat = ioread32(I2C_REG_STS(alg_data));

//I2C_REG_STS是一个宏,alg_data是一个结构体,最终的效果是查看位于地址

//0x400A0004地址处的i2c状态寄存器

     dev_dbg(&adap->dev, "%s(): entering: %d messages, stat = %04x.\n",

       __func__, num, ioread32(I2C_REG_STS(alg_data)));

 

     bus_reset_if_active(adap);

 

     /* Process transactions in a loop. */

//为什么要重启一次总线,有待研究

     for (i = 0; rc >= 0 && i < num; i++) {

//num为传递过来参数,即msg的数量

       u8 addr;

 

       pmsg = &msgs[i];

       addr = pmsg->addr;

 

       if (pmsg->flags & I2C_M_TEN) {

              dev_err(&adap->dev,

                     "%s: 10 bits addr not supported!\n",

                     adap->name);

              rc = -EINVAL;

              break;

       }

// I2C_M_TEN这个标志应该是用来表明地址类型为10位的(未验证)

 

       alg_data->mif.buf = pmsg->buf;

       alg_data->mif.len = pmsg->len;

       alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ?

              I2C_SMBUS_READ : I2C_SMBUS_WRITE;

       alg_data->mif.ret = 0;

       alg_data->last = (i == num - 1);

//以上数据用来初始化alg_data的mif,并指示未传输的msg.

 

       dev_dbg(&adap->dev, "%s(): mode %d, %d bytes\n", __func__,

              alg_data->mif.mode,

              alg_data->mif.len);

 

       i2c_pnx_arm_timer(adap);

//运行定时器,定时器处理函数为i2c_pnx_timeout,现将其代码贴出

/*----------------------------------------------------------------------------------------------

static void i2c_pnx_timeout(unsigned long data)

{

     struct i2c_adapter *adap = (struct i2c_adapter *)data;

     struct i2c_pnx_algo_data *alg_data = adap->algo_data;

     u32 ctl;

 

     dev_err(&adap->dev, "Master timed out. stat = %04x, cntrl = %04x. "

            "Resetting master...\n",

            ioread32(I2C_REG_STS(alg_data)),

            ioread32(I2C_REG_CTL(alg_data)));

//读i2c总线的状态和控制寄存器信息,并将其写入stat和cntl

     /* Reset master and disable interrupts */

     ctl = ioread32(I2C_REG_CTL(alg_data));

     ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | mcntrl_drmie);

     iowrite32(ctl, I2C_REG_CTL(alg_data));

//设置相关寄存器

     ctl |= mcntrl_reset;

     iowrite32(ctl, I2C_REG_CTL(alg_data));

//重启以清空RTX_FIFO,7位寻址。

     wait_reset(I2C_PNX_TIMEOUT, alg_data);

     alg_data->mif.ret = -EIO;

     complete(&alg_data->mif.complete);

//唤醒完成量

}

------------------------------------------------------------------------------------------------------------*/

       /* initialize the completion var */

       init_completion(&alg_data->mif.complete);

//初始化完成量

       /* Enable master interrupt */

       iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_afie |

                     mcntrl_naie | mcntrl_drmie,

                I2C_REG_CTL(alg_data));

//设置相关寄存器

       /* Put start-code and slave-address on the bus. */

       rc = i2c_pnx_start(addr, adap);

       if (rc < 0)

              break;

//在i2c_pnx_start函数中有这么一句

//iowrite32((slave_addr << 1) | start_bit | //alg_data->mif.mode,I2C_REG_TX(alg_data));

//作用就是产生发生start条件并发送地址

 

 

       /* Wait for completion */

       wait_for_completion(&alg_data->mif.complete);

//定时的作用应该是等待从机反应。

       if (!(rc = alg_data->mif.ret))

              completed++;

       dev_dbg(&adap->dev, "%s(): Complete, return code = %d.\n",

              __func__, rc);

 

       /* Clear TDI and AFI bits in case they are set. */

       if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_tdi) {

              dev_dbg(&adap->dev,

                     "%s: TDI still set... clearing now.\n",

                     adap->name);

              iowrite32(stat, I2C_REG_STS(alg_data));

       }

       if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_afi) {

              dev_dbg(&adap->dev,

                     "%s: AFI still set... clearing now.\n",

                     adap->name);

              iowrite32(stat, I2C_REG_STS(alg_data));

       }

     }

//最后如果两个中断置1,则清0

     bus_reset_if_active(adap);

 

     /* Cleanup to be sure... */

     alg_data->mif.buf = NULL;

     alg_data->mif.len = 0;

 

     dev_dbg(&adap->dev, "%s(): exiting, stat = %x\n",

       __func__, ioread32(I2C_REG_STS(alg_data)));

 

     if (completed != num)

       return ((rc < 0) ? rc : -EREMOTEIO);

 

     return num;

}

 

数据的发送和传输是放在中断函数中完成的。

static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)

{

     u32 stat, ctl;

     struct i2c_adapter *adap = dev_id;

     struct i2c_pnx_algo_data *alg_data = adap->algo_data;

 

     dev_dbg(&adap->dev, "%s(): mstat = %x mctrl = %x, mode = %d\n",

       __func__,

       ioread32(I2C_REG_STS(alg_data)),

       ioread32(I2C_REG_CTL(alg_data)),

       alg_data->mif.mode);

     stat = ioread32(I2C_REG_STS(alg_data));

 

     /* let's see what kind of event this is */

//仲裁失败中断

     if (stat & mstatus_afi) {

       /* We lost arbitration in the midst of a transfer */

       alg_data->mif.ret = -EIO;

 

       /* Disable master interrupts. */

       ctl = ioread32(I2C_REG_CTL(alg_data));

       ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie |

               mcntrl_drmie);

       iowrite32(ctl, I2C_REG_CTL(alg_data));

 

       /* Stop timer, to prevent timeout. */

       del_timer_sync(&alg_data->mif.timer);

       complete(&alg_data->mif.complete);

     } else if (stat & mstatus_nai) {

//无应答中断时

       /* Slave did not acknowledge, generate a STOP */

       dev_dbg(&adap->dev, "%s(): "

              "Slave did not acknowledge, generating a STOP.\n",

              __func__);

       i2c_pnx_stop(adap);

 

       /* Disable master interrupts. */

       ctl = ioread32(I2C_REG_CTL(alg_data));

       ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie |

               mcntrl_drmie);

       iowrite32(ctl, I2C_REG_CTL(alg_data));

 

       /* Our return value. */

       alg_data->mif.ret = -EIO;

 

       /* Stop timer, to prevent timeout. */

       del_timer_sync(&alg_data->mif.timer);

       complete(&alg_data->mif.complete);

     } else {

       /*

        * Two options:

        * - Master Tx needs data.

        * - There is data in the Rx-fifo

        * The latter is only the case if we have requested for data,

        * via a dummy write. (See 'i2c_pnx_master_rcv'.)

        * We therefore check, as a sanity check, whether that interrupt

        * has been enabled.

        */

//主机数据请求中断或从机数据请求中断时

       if ((stat & mstatus_drmi) || !(stat & mstatus_rfe)) {

              if (alg_data->mif.mode == I2C_SMBUS_WRITE) {

                     i2c_pnx_master_xmit(adap);

              } else if (alg_data->mif.mode == I2C_SMBUS_READ) {

                     i2c_pnx_master_rcv(adap);

              }

       }

     }

 

     /* Clear TDI and AFI bits */

     stat = ioread32(I2C_REG_STS(alg_data));

     iowrite32(stat | mstatus_tdi | mstatus_afi, I2C_REG_STS(alg_data));

 

     dev_dbg(&adap->dev, "%s(): exiting, stat = %x ctrl = %x.\n",

        __func__, ioread32(I2C_REG_STS(alg_data)),

        ioread32(I2C_REG_CTL(alg_data)));

 

     return IRQ_HANDLED;

}

 

关于remove方法以用中断的具体处理不再分析。分析完毕。

下面分析i2c驱动的设备驱动(drivers/i2c/chips/pcf8574.c)

该设备是以bin_attribute二进制sysfs结点形式出现。大部分I2C设备都会采用这种形式。

模块加载与卸载

static int __init pcf8574_init(void)

{

     return i2c_add_driver(&pcf8574_driver);

}

 

static void __exit pcf8574_exit(void)

{

     i2c_del_driver(&pcf8574_driver);

}

加载与卸载模块调用了i2c提供的i2c_driver两个操作函数。

关于pcf8574_driver结构体

static struct i2c_driver pcf8574_driver = {

     .driver = {

       .name      = "pcf8574",

     },

     .probe              = pcf8574_probe,

     .remove            = pcf8574_remove,

     .id_table    = pcf8574_id,

 

     .detect              = pcf8574_detect,

     .address_data    = &addr_data,

};

.id_table成员会在i2c_bus_type的mach()函数中检查,以确定和i2c_board_info提供的驱动id是否匹配。

static int pcf8574_probe(struct i2c_client *client,

               const struct i2c_device_id *id)

{

     struct pcf8574_data *data;

     int err;

 

     data = kzalloc(sizeof(struct pcf8574_data), GFP_KERNEL);

     if (!data) {

       err = -ENOMEM;

       goto exit;

     }

 

     i2c_set_clientdata(client, data);

 

     /* Initialize the PCF8574 chip */

     pcf8574_init_client(client);

 

     /* Register sysfs hooks */

     err = sysfs_create_group(&client->dev.kobj, &pcf8574_attr_group);

     if (err)

       goto exit_free;

     return 0;

 

      exit_free:

     kfree(data);

      exit:

     return err;

}

关于其它各函数的实现不再分析。以sysfs节点存在的设备驱动不会在/dev子目录中的相应的设备文件。

你可能感兴趣的:(驱动笔记)