掌握i2c架构必须明白的i2c_adapter和i2c_driver和和i2c_client之间的关系。
mach-mini2440.c--实现i2c外层的平台设备
i2c-s3c2410.c--实现i2c_adapter
i2c-dev.c--实现i2c_driver和i2c_client
详细如下:
mach-mini2440.c,
struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c",
#ifdef CONFIG_S3C_DEV_I2C1
.id = 0,
#else
.id = -1,
#endif
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
i2c-s3c2410.c,平台驱动的probe函数中执行 i2c_add_numbered_adapter(&i2c->adap)用于注册i2c_adapter,同时该文件实现了i2c_adapter的i2c_algorithm结构体
i2c-dev.c,init函数中执行i2c_add_driver(&i2cdev_driver)用于注册i2c_driver,i2c_driver有一个成员函数attach_adapter=i2cdev_attach_adapter;每当系统中有i2c_adapter注册时,都会调用此函数,并把当时的i2c_adapter传递过来,并在sys下创建对应文件。
文件中实现了一个字符设备,在字符设备的open函数中构造了一个i2c_client,并且设置
client->driver = &i2cdev_driver;
client->adapter = adap;
这样之后在读写i2c的时候直接如次调用即可 i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
mx53_smd.c--实现i2c外层的平台设备,注册
i2c-imx.c
ov5640.c
mx53_smd.c,静态注册mxc_i2c0_board_info,mxc_i2c1_board_info,mxc_i2c2_board_info
static struct i2c_board_info mxc_i2c0_board_info[] __initdata = {
{
.type = "ov5640",
.addr = 0x3c,
.platform_data = (void *)&camera_data,
},
};
static struct i2c_board_info mxc_i2c1_board_info[] __initdata = {
{
.type = "pcf8563",
.addr = 0x51, //0xa3 read and 0xa2 write
},
{
.type="Goodix-TS",
.addr = 0x5d,
},
};
static struct i2c_board_info mxc_i2c2_board_info[] __initdata = {
{
.type = "wm8758",
.addr = 0x1a,
},
{
.type = "isl29023",
.addr = 0x44,
.platform_data = &ls_data,
},
};
i2c_register_board_info( 0, mxc_i2c0_board_info,
ARRAY_SIZE(mxc_i2c0_board_info));
i2c_register_board_info( 1, mxc_i2c1_board_info,
ARRAY_SIZE(mxc_i2c1_board_info));
i2c_register_board_info( 2, mxc_i2c2_board_info,
ARRAY_SIZE(mxc_i2c2_board_info));
i2c_register_board_info将这些info注册到__i2c_board_list
而系统启动时,i2c核心会为每一个i2c_adapter (以 adapter->nr区分) 执行i2c_scan_static_board_info():扫描__i2c_board_list上的board_info,为总线号等于当前adpater->nr的client创建client
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
down_read(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if ( devinfo->busnum == adapter->nr
&& ! i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}
在i2c_new_device()中,创建client并执行client->adapter = adap;这样i2c_client就和i2c_adapter根据根据总线号绑定上了。
而i2c_adapter实现在i2c-imx.c,通过i2c_adapter的平台驱动的probe执行ret = i2c_add_numbered_adapter(&mxc_i2c->adap);来注册adapter。i2c_adapter的平台设备
位于mach-mx5/devices.c
struct platform_device mxci2c_devices[] = {
{
.name = "imx-i2c",
.id = 0,
.num_resources = ARRAY_SIZE(mxci2c1_resources),
.resource = mxci2c1_resources,
},
{
.name = "imx-i2c",
.id = 1,
.num_resources = ARRAY_SIZE(mxci2c2_resources),
.resource = mxci2c2_resources,
},
{
.name = "imx-i2c",
.id = 2,
.num_resources = ARRAY_SIZE(mxci2c3_resources),
.resource = mxci2c3_resources,
},
};
mxc_register_device(&mxci2c_devices[0], &mxci2c_data);
mxc_register_device(&mxci2c_devices[1], &mxci2c_data);
mxc_register_device(&mxci2c_devices[2], &mxci2c_data);
i2c_driver和i2c_client的匹配规则:
i2c_bus_type的 i2c_device_match 进行i2c_driver->id_table->name 与i2c_client->name 的匹配
http://blog.csdn.net/gchww/article/details/7367718
i2c驱动目录
i2c-s3c2410.c里面有一个重要的全局结构体,
①i2c-s3c2410.c实现i2c总线驱动(即cpu的i2c主控制器和外接设备的i2c接口的通信),
1.需要填充i2c_adapter结构体。i2c_adapter结构体即i2c适配器即i2c主控制器,对于不同的硬件需要有不同的配置。然后向通过i2c核心添加这个适配器。
2.需要填充i2c-algorithm结构体。i2c-algorithm结构体即指定i2c通信算法,
②i2c-dev.c 实现i2c设备驱动。即实现i2c_driver结构体并向i2c核心注册,还要实现设备自身的驱动如普通字符设备(或许也可以用misc设备)的驱动,以便与用户空间交互。
如下
i2c总线驱动(对应i2c_adapter)和i2c设备驱动(对应i2c_driver)是通过i2c核心关联起来的。当有一个i2c_adapter通过i2c核心添加,i2c核心会自动匹配对应的i2c_driver,调用i2c_driver的probe函数....不过在i2c-dev.c 没有实现这个函数指针,而是实现了attach_adapter函数指针。
****************************************************************************************************************************************
在i2c-dev.h中定义了如下ioctl命令:
当在用户侧使用ioctl(fd, I2C_RDWR, (unsigned long )&e2prom_data);读取数据时,会调用驱动的ioctl设备方法
i2c-dev.c
case I2C_RDWR:
return i2cdev_ioctl_rdrw(client, arg);
先看client是什么东东,line 3有
struct i2c_client *client = (struct i2c_client *)file->private_data;
接着看一下打开设备时的动作,如下
client->driver = &i2cdev_driver;//client绑定i2c_driver
client->adapter = adap;//client绑定i2c_adapter
很明确的说明了client就是把driver(i2c-dev.c)和adapter(i2c-s3c2410.c)关联
回到i2cdev_ioctl_rdrw(client, arg);源码是
是一个要在设备驱动(i2c-dev.c)里面调用以便访问总线驱动(i2c-s3c2410.c)的接口函数。
使用i2c subsystem的这种分离设计是为了cpu平台更换时可以保持与用户的接口不变:
比如有1个i2c_driver(设备驱动,处理用户接口)和5个i2c_adapter(代表5个cpu平台的i2c主控制器驱动,处理与特定设备的寄存器配置和信息传输的具体实现)。在换平台的时候,只需要实现i2c_adapter部分,而i2c_driver不必改变,因为它最终调用i2c核心的i2c_transfer()与i2c_adapter交互。
而如果不分离。即将用户接口的实现(比如字符设备)和i2c数据传输的实现够放在一个文件里面------传统的做法,则公司在换cpu平台的时候,就需要修改整个驱动文件。
这种分离设计也可以实现cpu平台不变外设更换时,可以保持同一套用户接口。此时仍然是只需要修改i2c_adapter或者如果外设特性能完全相同则不必修改。
但对于专注于一个cpu一个外设的厂家而言这种分离设计就有点浪费了,,,
并且这种设计也遵守内核实现机制(套路)的思想。比如i2c子系统为已经定义了结构体i2c_msg,在用户空间只需填充i2c_msg然后发送。
综上可知:
即内核实现怎么发,用户决定发什么。而内核实现怎么发的同时,还考虑到用户的换平台的情况,所以采用分离设计将用户接口与具体的怎么发分离。
虽然i2c子系统是按照这个思想设计的,但和同样是这个设计思想的spi子系统对比,却没有spi的明朗。
--2011年12月4日
几个重要的结构体
i2c.h
s3c2440的i2c主控制器仅5个寄存器,在linux内核中却搞得这么繁杂,,,,,
IICCON 0x54000000 R/W IIC-Bus control register
IICSTAT 0x54000004 R/W IIC-Bus control/status register
IICADD 0x54000008 R/W IIC-Bus address register
IICDS 0x5400000C R/W IIC-Bus transmit/receive data shift register
IICLC 0x54000010 R/W IIC-Bus multi-master line control register
并且micro2440的eeprom的i2c驱动最外层是platform总线然后是i2c总线然后才是面向用户访问的字符设备。。。
目前的一点个人理解,有待更新。。。
存在的问题,在insmod时i2c核心具体怎么匹配外设驱动(设备)和总线(控制器驱动),在ioctl时i2c核心具体怎么处理(匹配)外设驱动和总线,i2c具体数据的传输。有时间再去细读。
对总线设备驱动模型认识不清,对sysfs认识不清,对mdev如何利用sysfs创建设备节点的过程不清。
refer to
http://blog.csdn.net/cjok376240497/article/details/6982883
http://blog.csdn.net/hongtao_liu/article/details/4964244
http://blog.csdn.net/hongtao_liu/article/details/5260739
旧方式:
参考例子linux2.6.32 下面的mach-mini2440.c--实现i2c外层的平台设备
i2c-s3c2410.c--实现i2c_adapter
i2c-dev.c--实现i2c_driver和i2c_client
详细如下:
mach-mini2440.c,
struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c",
#ifdef CONFIG_S3C_DEV_I2C1
.id = 0,
#else
.id = -1,
#endif
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
i2c-s3c2410.c,平台驱动的probe函数中执行 i2c_add_numbered_adapter(&i2c->adap)用于注册i2c_adapter,同时该文件实现了i2c_adapter的i2c_algorithm结构体
i2c-dev.c,init函数中执行i2c_add_driver(&i2cdev_driver)用于注册i2c_driver,i2c_driver有一个成员函数attach_adapter=i2cdev_attach_adapter;每当系统中有i2c_adapter注册时,都会调用此函数,并把当时的i2c_adapter传递过来,并在sys下创建对应文件。
文件中实现了一个字符设备,在字符设备的open函数中构造了一个i2c_client,并且设置
client->driver = &i2cdev_driver;
client->adapter = adap;
这样之后在读写i2c的时候直接如次调用即可 i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
新方式:
参考例子linux2.6.35 下面的mx53_smd.c--实现i2c外层的平台设备,注册
i2c-imx.c
ov5640.c
mx53_smd.c,静态注册mxc_i2c0_board_info,mxc_i2c1_board_info,mxc_i2c2_board_info
static struct i2c_board_info mxc_i2c0_board_info[] __initdata = {
{
.type = "ov5640",
.addr = 0x3c,
.platform_data = (void *)&camera_data,
},
};
static struct i2c_board_info mxc_i2c1_board_info[] __initdata = {
{
.type = "pcf8563",
.addr = 0x51, //0xa3 read and 0xa2 write
},
{
.type="Goodix-TS",
.addr = 0x5d,
},
};
static struct i2c_board_info mxc_i2c2_board_info[] __initdata = {
{
.type = "wm8758",
.addr = 0x1a,
},
{
.type = "isl29023",
.addr = 0x44,
.platform_data = &ls_data,
},
};
i2c_register_board_info( 0, mxc_i2c0_board_info,
ARRAY_SIZE(mxc_i2c0_board_info));
i2c_register_board_info( 1, mxc_i2c1_board_info,
ARRAY_SIZE(mxc_i2c1_board_info));
i2c_register_board_info( 2, mxc_i2c2_board_info,
ARRAY_SIZE(mxc_i2c2_board_info));
i2c_register_board_info将这些info注册到__i2c_board_list
而系统启动时,i2c核心会为每一个i2c_adapter (以 adapter->nr区分) 执行i2c_scan_static_board_info():扫描__i2c_board_list上的board_info,为总线号等于当前adpater->nr的client创建client
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
down_read(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if ( devinfo->busnum == adapter->nr
&& ! i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}
在i2c_new_device()中,创建client并执行client->adapter = adap;这样i2c_client就和i2c_adapter根据根据总线号绑定上了。
而i2c_adapter实现在i2c-imx.c,通过i2c_adapter的平台驱动的probe执行ret = i2c_add_numbered_adapter(&mxc_i2c->adap);来注册adapter。i2c_adapter的平台设备
位于mach-mx5/devices.c
struct platform_device mxci2c_devices[] = {
{
.name = "imx-i2c",
.id = 0,
.num_resources = ARRAY_SIZE(mxci2c1_resources),
.resource = mxci2c1_resources,
},
{
.name = "imx-i2c",
.id = 1,
.num_resources = ARRAY_SIZE(mxci2c2_resources),
.resource = mxci2c2_resources,
},
{
.name = "imx-i2c",
.id = 2,
.num_resources = ARRAY_SIZE(mxci2c3_resources),
.resource = mxci2c3_resources,
},
};
mxc_register_device(&mxci2c_devices[0], &mxci2c_data);
mxc_register_device(&mxci2c_devices[1], &mxci2c_data);
mxc_register_device(&mxci2c_devices[2], &mxci2c_data);
i2c_driver和i2c_client的匹配规则:
i2c_bus_type的 i2c_device_match 进行i2c_driver->id_table->name 与i2c_client->name 的匹配
http://blog.csdn.net/gchww/article/details/7367718
i2c驱动目录
[root@localhost i2c]# pwd
/opt/FriendlyArm/mini2440/linux-2.6.32.2/drivers/i2c
[root@localhost i2c]# tree //.有省略
|-- Kconfig
|-- Makefile
|-- algos
| |-- Kconfig
| |-- Makefile
| |-- built-in.o
| |-- i2c-algo-bit.c
| |-- i2c-algo-pca.c
| |-- i2c-algo-pcf.c
| |-- i2c-algo-pcf.h
| `-- modules.order
|-- built-in.o
|-- busses
| |-- Kconfig
| |-- Makefile
| |-- ...
| -- i2c-s3c2410.c
|-- chips
| |-- Kconfig
| |-- Makefile
| |-- ds1682.c
| `-- tsl2550.c
|-- i2c-boardinfo.c
|-- i2c-core.c
|-- i2c-core.h
|-- i2c-core.o
|-- i2c-dev.c
i2c-s3c2410.c里面有一个重要的全局结构体,
struct s3c24xx_i2c {
spinlock_t lock;
wait_queue_head_t wait;
unsigned int suspended:1;
struct i2c_msg *msg;
unsigned int msg_num;
unsigned int msg_idx;
unsigned int msg_ptr;
unsigned int tx_setup;
unsigned int irq;
enum s3c24xx_i2c_state state;
unsigned long clkrate;
void __iomem *regs;
struct clk *clk;
struct device *dev;
struct resource *ioarea;
struct i2c_adapter adap;
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
};
①i2c-s3c2410.c实现i2c总线驱动(即cpu的i2c主控制器和外接设备的i2c接口的通信),
1.需要填充i2c_adapter结构体。i2c_adapter结构体即i2c适配器即i2c主控制器,对于不同的硬件需要有不同的配置。然后向通过i2c核心添加这个适配器。
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;//指定adapter的通信算法结构体
i2c->adap.retries = 2;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = 50;
...
ret = i2c_add_numbered_adapter(&i2c->adap);
2.需要填充i2c-algorithm结构体。i2c-algorithm结构体即指定i2c通信算法,
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
在s3c24xx_i2c_xfer里(其调用到的函数里)用到i2c_msg结构体访问i2c.
②i2c-dev.c 实现i2c设备驱动。即实现i2c_driver结构体并向i2c核心注册,还要实现设备自身的驱动如普通字符设备(或许也可以用misc设备)的驱动,以便与用户空间交互。
如下
static int __init i2c_dev_init(void)
{
int res;
printk(KERN_INFO "i2c /dev entries driver\n");
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);//
.........
return res;
}
在i2cdev_attach_adapter里面向sysfs文件系统写入信息。以便i2c_adapter”热插拔“的时候动态创建节点.
static int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
int res;
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
/* register this i2c device with the driver core */
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);
.........
}
i2c总线驱动(对应i2c_adapter)和i2c设备驱动(对应i2c_driver)是通过i2c核心关联起来的。当有一个i2c_adapter通过i2c核心添加,i2c核心会自动匹配对应的i2c_driver,调用i2c_driver的probe函数....不过在i2c-dev.c 没有实现这个函数指针,而是实现了attach_adapter函数指针。
****************************************************************************************************************************************
在i2c-dev.h中定义了如下ioctl命令:
/* /dev/i2c-X ioctl commands. The ioctl's parameter is always an
* unsigned long, except for:
* - I2C_FUNCS, takes pointer to an unsigned long
* - I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data
* - I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data
*/
#define I2C_RETRIES 0x0701 /* number of times a device address should
be polled when not acknowledging */
#define I2C_TIMEOUT 0x0702 /* set timeout in units of 10 ms */
/* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses
* are NOT supported! (due to code brokenness)
*/
#define I2C_SLAVE 0x0703 /* Use this slave address */
#define I2C_SLAVE_FORCE 0x0706 /* Use this slave address, even if it
is already in use by a driver! */
#define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */
#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */
#define I2C_RDWR 0x0707 /* Combined R/W transfer (one STOP only) */
#define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */
#define I2C_SMBUS 0x0720 /* SMBus transfer */
当在用户侧使用ioctl(fd, I2C_RDWR, (unsigned long )&e2prom_data);读取数据时,会调用驱动的ioctl设备方法
i2c-dev.c
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = (struct i2c_client *)file->private_data;
unsigned long funcs;
dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
cmd, arg);
switch ( cmd ) {
case I2C_SLAVE:
case I2C_SLAVE_FORCE:
/* NOTE: devices set up to work with "new style" drivers
* can't use I2C_SLAVE, even when the device node is not
* bound to a driver. Only I2C_SLAVE_FORCE will work.
*
* Setting the PEC flag here won't affect kernel drivers,
* which will be using the i2c_client node registered with
* the driver model core. Likewise, when that client has
* the PEC flag already set, the i2c-dev driver won't see
* (or use) this setting.
*/
if ((arg > 0x3ff) ||
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL;
if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
return -EBUSY;
/* REVISIT: address could become busy later */
client->addr = arg;
return 0;
case I2C_TENBIT:
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return 0;
case I2C_PEC:
if (arg)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return 0;
case I2C_FUNCS:
funcs = i2c_get_functionality(client->adapter);
return put_user(funcs, (unsigned long __user *)arg);
case I2C_RDWR:
return i2cdev_ioctl_rdrw(client, arg);
case I2C_SMBUS:
return i2cdev_ioctl_smbus(client, arg);
case I2C_RETRIES:
client->adapter->retries = arg;
break;
case I2C_TIMEOUT:
/* For historical reasons, user-space sets the timeout
* value in units of 10 ms.
*/
client->adapter->timeout = msecs_to_jiffies(arg * 10);
break;
default:
/* NOTE: returning a fault code here could cause trouble
* in buggy userspace code. Some old kernel bugs returned
* zero in this case, and userspace code might accidentally
* have depended on that bug.
*/
return -ENOTTY;
}
return 0;
}
而
case I2C_RDWR:
return i2cdev_ioctl_rdrw(client, arg);
先看client是什么东东,line 3有
struct i2c_client *client = (struct i2c_client *)file->private_data;
接着看一下打开设备时的动作,如下
static int i2cdev_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int ret = 0;
lock_kernel();
i2c_dev = i2c_dev_get_by_minor(minor);
if (!i2c_dev) {
ret = -ENODEV;
goto out;
}
adap = i2c_get_adapter(i2c_dev->adap->nr);
if (!adap) {
ret = -ENODEV;
goto out;
}
/* This creates an anonymous i2c_client, which may later be
* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
*
* This client is ** NEVER REGISTERED ** with the driver model
* or I2C core code!! It just holds private copies of addressing
* information and maybe a PEC flag.
*/
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
ret = -ENOMEM;
goto out;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
client->driver = &i2cdev_driver;//client绑定i2c_driver
client->adapter = adap;//client绑定i2c_adapter
file->private_data = client;//一般都是这样搞的,文件私有数据指向client
out:
unlock_kernel();
return ret;
}
其中这2行
client->driver = &i2cdev_driver;//client绑定i2c_driver
client->adapter = adap;//client绑定i2c_adapter
回到i2cdev_ioctl_rdrw(client, arg);源码是
static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
unsigned long arg)
{
struct i2c_rdwr_ioctl_data rdwr_arg;
struct i2c_msg *rdwr_pa;
u8 __user **data_ptrs;
int i, res;
if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg)))
return -EFAULT;
/* Put an arbitrary limit on the number of messages that can
* be sent at once */
if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
return -EINVAL;
rdwr_pa = (struct i2c_msg *)
kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
GFP_KERNEL);
if (!rdwr_pa)
return -ENOMEM;
if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
kfree(rdwr_pa);
return -EFAULT;
}
data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);
if (data_ptrs == NULL) {
kfree(rdwr_pa);
return -ENOMEM;
}
res = 0;
for (i = 0; i < rdwr_arg.nmsgs; i++) {
/* Limit the size of the message to a sane amount;
* and don't let length change either. */
if ((rdwr_pa[i].len > 8192) ||
(rdwr_pa[i].flags & I2C_M_RECV_LEN)) {
res = -EINVAL;
break;
}
data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;
rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
if (rdwr_pa[i].buf == NULL) {
res = -ENOMEM;
break;
}
if (copy_from_user(rdwr_pa[i].buf, data_ptrs[i],
rdwr_pa[i].len)) {
++i; /* Needs to be kfreed too */
res = -EFAULT;
break;
}
}
if (res < 0) {
int j;
for (j = 0; j < i; ++j)
kfree(rdwr_pa[j].buf);
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
}
res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);//
while (i-- > 0) {
if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {
if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,
rdwr_pa[i].len))
res = -EFAULT;
}
kfree(rdwr_pa[i].buf);
}
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
}
line 68调用了i2c core提供的一个函数i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);看下这个函数的注释
是一个要在设备驱动(i2c-dev.c)里面调用以便访问总线驱动(i2c-s3c2410.c)的接口函数。
/* ----------------------------------------------------
* the functional interface to the i2c busses.
* ----------------------------------------------------
*/
/**
* i2c_transfer - execute a single or combined I2C message
* @adap: Handle to I2C bus
* @msgs: One or more messages to execute before STOP is issued to
* terminate the operation; each message begins with a START.
* @num: Number of messages to be executed.
*
* Returns negative errno, else the number of messages executed.
*
* Note that there is no requirement that each message be sent to
* the same slave address, although that is the most common model.
*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
调用此函数时,会将设备驱动的i2c_msg传输给总线驱动,i2c核心自动调用总线驱动里的master_xfer函数指针指向的实际执行数据传输的函数将i2c_msg在i2c主控制器与i2c外设之间传输。
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
使用i2c subsystem的这种分离设计是为了cpu平台更换时可以保持与用户的接口不变:
比如有1个i2c_driver(设备驱动,处理用户接口)和5个i2c_adapter(代表5个cpu平台的i2c主控制器驱动,处理与特定设备的寄存器配置和信息传输的具体实现)。在换平台的时候,只需要实现i2c_adapter部分,而i2c_driver不必改变,因为它最终调用i2c核心的i2c_transfer()与i2c_adapter交互。
而如果不分离。即将用户接口的实现(比如字符设备)和i2c数据传输的实现够放在一个文件里面------传统的做法,则公司在换cpu平台的时候,就需要修改整个驱动文件。
这种分离设计也可以实现cpu平台不变外设更换时,可以保持同一套用户接口。此时仍然是只需要修改i2c_adapter或者如果外设特性能完全相同则不必修改。
但对于专注于一个cpu一个外设的厂家而言这种分离设计就有点浪费了,,,
并且这种设计也遵守内核实现机制(套路)的思想。比如i2c子系统为已经定义了结构体i2c_msg,在用户空间只需填充i2c_msg然后发送。
综上可知:
即内核实现怎么发,用户决定发什么。而内核实现怎么发的同时,还考虑到用户的换平台的情况,所以采用分离设计将用户接口与具体的怎么发分离。
虽然i2c子系统是按照这个思想设计的,但和同样是这个设计思想的spi子系统对比,却没有spi的明朗。
--2011年12月4日
几个重要的结构体
i2c.h
/**
* struct i2c_driver - represent an I2C device driver
* @class: What kind of i2c device we instantiate (for detect)
* @attach_adapter: Callback for bus addition (for legacy drivers)
* @detach_adapter: Callback for bus removal (for legacy drivers)
* @probe: Callback for device binding
* @remove: Callback for device unbinding
* @shutdown: Callback for device shutdown
* @suspend: Callback for device suspend
* @resume: Callback for device resume
* @command: Callback for bus-wide signaling (optional)
* @driver: Device driver model driver
* @id_table: List of I2C devices supported by this driver
* @detect: Callback for device detection
* @address_data: The I2C addresses to probe, ignore or force (for detect)
* @clients: List of detected clients we created (for i2c-core use only)
*
* The driver.owner field should be set to the module owner of this driver.
* The driver.name field should be set to the name of this driver.
*
* For automatic device detection, both @detect and @address_data must
* be defined. @class should also be set, otherwise only devices forced
* with module parameters will be created. The detect function must
* fill at least the name field of the i2c_board_info structure it is
* handed upon successful detection, and possibly also the flags field.
*
* If @detect is missing, the driver will still work fine for enumerated
* devices. Detected devices simply won't be supported. This is expected
* for the many I2C/SMBus devices which can't be detected reliably, and
* the ones which can always be enumerated in practice.
*
* The i2c_client structure which is handed to the @detect callback is
* not a real i2c_client. It is initialized just enough so that you can
* call i2c_smbus_read_byte_data and friends on it. Don't do anything
* else with it. In particular, calling dev_dbg and friends on it is
* not allowed.
*/
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 if you can, it will probably
* be removed in a near future.
*/
int (*attach_adapter)(struct i2c_adapter *);
int (*detach_adapter)(struct 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 *);
/* 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 *, int kind, struct i2c_board_info *);
const struct i2c_client_address_data *address_data;
struct list_head clients;
};
#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)
/**
* 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;
};
#define to_i2c_client(d) container_of(d, struct i2c_client, dev)
/**
* struct i2c_msg - an I2C transaction segment beginning with START
* @addr: Slave address, either seven or ten bits. When this is a ten
* bit address, I2C_M_TEN must be set in @flags and the adapter
* must support I2C_FUNC_10BIT_ADDR.
* @flags: I2C_M_RD is handled by all adapters. No other flags may be
* provided unless the adapter exported the relevant I2C_FUNC_*
* flags through i2c_check_functionality().
* @len: Number of data bytes in @buf being read from or written to the
* I2C slave address. For read transactions where I2C_M_RECV_LEN
* is set, the caller guarantees that this buffer can hold up to
* 32 bytes in addition to the initial length byte sent by the
* slave (plus, if used, the SMBus PEC); and this value will be
* incremented by the number of block data bytes received.
* @buf: The buffer into which data is read, or from which it's written.
*
* An i2c_msg is the low level representation of one segment of an I2C
* transaction. It is visible to drivers in the @i2c_transfer() procedure,
* to userspace from i2c-dev, and to I2C adapter drivers through the
* @i2c_adapter.@master_xfer() method.
*
* Except when I2C "protocol mangling" is used, all I2C adapters implement
* the standard rules for I2C transactions. Each transaction begins with a
* START. That is followed by the slave address, and a bit encoding read
* versus write. Then follow all the data bytes, possibly including a byte
* with SMBus PEC. The transfer terminates with a NAK, or when all those
* bytes have been transferred and ACKed. If this is the last message in a
* group, it is followed by a STOP. Otherwise it is followed by the next
* @i2c_msg transaction segment, beginning with a (repeated) START.
*
* Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then
* passing certain @flags may have changed those standard protocol behaviors.
* Those flags are only for use with broken/nonconforming slaves, and with
* adapters which are known to support the specific mangling options they
* need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR).
*/
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 */
};
/*
* 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 id;
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 */
u8 level; /* nesting level for lockdep */
struct mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
};
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
/*
* 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, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, 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 *);
};
/**
* 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
* @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;
int irq;
};
s3c2440的i2c主控制器仅5个寄存器,在linux内核中却搞得这么繁杂,,,,,
IICCON 0x54000000 R/W IIC-Bus control register
IICSTAT 0x54000004 R/W IIC-Bus control/status register
IICADD 0x54000008 R/W IIC-Bus address register
IICDS 0x5400000C R/W IIC-Bus transmit/receive data shift register
IICLC 0x54000010 R/W IIC-Bus multi-master line control register
并且micro2440的eeprom的i2c驱动最外层是platform总线然后是i2c总线然后才是面向用户访问的字符设备。。。
目前的一点个人理解,有待更新。。。
存在的问题,在insmod时i2c核心具体怎么匹配外设驱动(设备)和总线(控制器驱动),在ioctl时i2c核心具体怎么处理(匹配)外设驱动和总线,i2c具体数据的传输。有时间再去细读。
对总线设备驱动模型认识不清,对sysfs认识不清,对mdev如何利用sysfs创建设备节点的过程不清。
refer to
http://blog.csdn.net/cjok376240497/article/details/6982883
http://blog.csdn.net/hongtao_liu/article/details/4964244
http://blog.csdn.net/hongtao_liu/article/details/5260739