一、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子目录中的相应的设备文件。