关于i2c子系统从以下几个问题开始
设备是如何通过vfs的接口调用到驱动的file_operation的
一句话简单来讲就是,统一设备模型是基于kobject和ktype的。而统一设备模型最重要的就是设备和驱动,struct device和struct device_driver driver;
struct device {
...
struct kobject kobj;
...
}
其中设备的结构体中就包含了kobject
函数调用流程为:
vfs---->sysfs---->kobject---->attibute---->kobj_type---->sysfs_ops---->xxx_attribute,
参考蜗窝科技写的文章,写的非常好
http://www.wowotech.net/device_model/device_and_driver.html
struct i2c_driver {
int id;
unsigned int class;
/* Notifies the driver that a new bus has appeared. This routine
* can be used by the driver to test if the bus meets its conditions
* & seek for the presence of the chip(s) it supports. If found, it
* registers the client(s) that are on the bus to the i2c admin. via
* i2c_attach_client. (LEGACY I2C DRIVERS ONLY)
*/
int (*attach_adapter)(struct i2c_adapter *);
int (*detach_adapter)(struct i2c_adapter *);
/* tells the driver that a client is about to be deleted & gives it
* the chance to remove its private data. Also, if the client struct
* has been dynamically allocated by the driver in the function above,
* it must be freed here. (LEGACY I2C DRIVERS ONLY)
*/
int (*detach_client)(struct i2c_client *);
/* Standard driver model interfaces, for "new style" i2c drivers.
* With the driver model, device enumeration is NEVER done by drivers;
* it's done by infrastructure. (NEW STYLE DRIVERS ONLY)
*/
int (*probe)(struct i2c_client *);
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;
struct list_head list;
};
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 */
int usage_count; /* How many accesses currently */
/* to the client */
struct device dev; /* the device structure */
int irq; /* irq issued by device (or -1) */
char driver_name[KOBJ_NAME_LEN];
struct list_head list;
struct completion released;
};
可以看到i2c子系统最重要的,struct i2c_driver和struct i2c_client就是对struct device和struct device_driver driver进行了封装,增加了和i2c有关的信息。
i2c子系统和字符设备有什么关系
分析一下代码可以看出,i2c子系统的核心还是调用register_chrdev,字符设备的接口,也就是说,各种子系统都是建立在字符设备之上的,如果足够强的话,可以绕过子系统,自己直接调用字符设备的接口实现i2c的框架,不过没啥必要。
static struct i2c_driver i2cdev_driver = {
.driver = {
.name = "dev_driver",
},
.id = I2C_DRIVERID_I2CDEV,
.attach_adapter = i2cdev_attach_adapter,
.detach_adapter = i2cdev_detach_adapter,
.detach_client = i2cdev_detach_client,
};
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);
if (res)
goto out;
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class))
goto out_unreg_chrdev;
res = i2c_add_driver(&i2cdev_driver);
if (res)
goto out_unreg_class;
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev(I2C_MAJOR, "i2c");
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
i2c 子系统和platform有什么区别
platform和i2c子系统最大的实现区别就在于,没有struct i2c_adapter *adapter; 因为它是一种虚拟总线,没有特定总线的时序算法,所以就简单很多。所以驱动直接调用probe函数,而不用进行探测。
struct platform_device {
const char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
i2c 子系统的算法是怎样实现的
以at24c的驱动作为讲解:
- 很明显,这最重要就是,at24cxx_attach这个函数,这个就是i2c子系统,最先调用的函数
static struct file_operations at24cxx_fops = {
.owner = THIS_MODULE,
.read = at24cxx_read,
.write = at24cxx_write,
};
static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind)
{
printk("at24cxx_detect\n");
/* 构构一个i2c_client结构体: 以后收改数据时会用到它 */
at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); at24cxx_client->addr = address;
at24cxx_client->adapter = adapter;
at24cxx_client->driver = &at24cxx_driver;
strcpy(at24cxx_client->name, "at24cxx");
i2c_attach_client(at24cxx_client);
major = register_chrdev(0, "at24cxx", &at24cxx_fops);
cls = class_create(THIS_MODULE, "at24cxx");
class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */
return 0;
}
static int at24cxx_attach(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, at24cxx_detect);
}
static struct i2c_driver at24cxx_driver = {
.driver = {
.name = "at24cxx",
},
.attach_adapter = at24cxx_attach,
.detach_client = at24cxx_detach,
};
module_init(at24cxx_init);
- 整理代码发现最重要的就是调用i2c_probe_address
int i2c_probe(struct i2c_adapter *adapter,
struct i2c_client_address_data *address_data,
int (*found_proc) (struct i2c_adapter *, int, int))
{
...
err = i2c_probe_address(adapter, address_data->normal_i2c[i],
-1, found_proc);
...
}
根据名字可以判断,是需要地址的,以我的经验估计就是,读取i2c设备的地址,查看是否和写入的地址吻合,判断是否有设备。那么地址肯定要传递进去,回头查看传入参数
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr, /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */ .probe = ignore,
.ignore = ignore,
//.forces = forces, /* 强制认为存在这个设备 */
};
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr, /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */ .probe = ignore,
.ignore = ignore,
//.forces = forces, /* 强制认为存在这个设备 */
};
刚好发现i2c_probe(adapter, &addr_data, at24cxx_detect); 通过这里将地址传递进去啦。
接下来就看i2c_probe_address是如何实现的。
- 代码比较清晰,主要是判断改地址是否已经被别的设备使用,没有使用的话才会通过i2c_smbus_xfer去验证地址。如果验证通过,则会调用err = found_proc(adapter, addr, kind); 那么这个found_proc是哪里来的呢
static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,
int (*found_proc) (struct i2c_adapter *, int, int))
{
int err;
/* Make sure the address is valid */
if (addr < 0x03 || addr > 0x77) {
dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
addr);
return -EINVAL;
}
/* Skip if already in use */
if (i2c_check_addr(adapter, addr))
return 0;
/* Make sure there is something at this address, unless forced */
if (kind < 0) {
if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL) < 0)
return 0;
/* prevent 24RF08 corruption */
if ((addr & ~0x0f) == 0x50)
i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL);
}
/* Finally call the custom detection function */
err = found_proc(adapter, addr, kind);
/* -ENODEV can be returned if there is a chip at the given address
but it isn't supported by this chip driver. We catch it here as
this isn't an error. */
if (err == -ENODEV) err = 0;
if (err)
dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n",
addr, err);
return err;
}
继续回头看i2c_probe(adapter, &addr_data, at24cxx_detect); 发现就是at24cxx_detect,因此只有在检测到设备时,才会调用at24cxx_detect。那么我们继续看i2c_smbus_xfer的实现。
- 到这里发现,就是调用smbus_xfer,或者是i2c_smbus_xfer_emulated
s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data * data)
{ s32 res;
flags &= I2C_M_TEN | I2C_CLIENT_PEC;
if (adapter->algo->smbus_xfer) {
mutex_lock(&adapter->bus_lock);
res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,
command,size,data);
mutex_unlock(&adapter->bus_lock);
} else
res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
command,size,data);
return res;
}
那么在这里我们首先必须要搜索,smbus_xfer是在哪里复制的。查找和struct i2c_adapter 相关赋值的内容发现都在busses目录下,以i2c-at91.c为例。
static struct i2c_algorithm at91_algorithm = {
.master_xfer = at91_xfer,
.functionality = at91_func,
};
static int __devinit at91_i2c_probe(struct platform_device *pdev)
{
struct i2c_adapter *adapter;
struct resource *res;
int rc;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENXIO;
if (!request_mem_region(res->start, res->end - res->start + 1, "at91_i2c"))
return -EBUSY;
twi_base = ioremap(res->start, res->end - res->start + 1);
if (!twi_base) {
rc = -ENOMEM;
goto fail0;
}
twi_clk = clk_get(NULL, "twi_clk");
if (IS_ERR(twi_clk)) {
dev_err(&pdev->dev, "no clock defined\n");
rc = -ENODEV;
goto fail1;
}
adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
if (adapter == NULL) {
dev_err(&pdev->dev, "can't allocate inteface!\n");
rc = -ENOMEM;
goto fail2;
}
sprintf(adapter->name, "AT91");
adapter->algo = &at91_algorithm;
adapter->class = I2C_CLASS_HWMON;
adapter->dev.parent = &pdev->dev;
/* adapter->id == 0 ... only one TWI controller for now */
platform_set_drvdata(pdev, adapter);
clk_enable(twi_clk); /* enable peripheral clock */
at91_twi_hwinit(); /* initialize TWI controller */
rc = i2c_add_numbered_adapter(adapter);
if (rc) {
dev_err(&pdev->dev, "Adapter %s registration failed\n",
adapter->name);
goto fail3;
}
dev_info(&pdev->dev, "AT91 i2c bus driver.\n");
return 0;
fail3:
platform_set_drvdata(pdev, NULL);
kfree(adapter);
clk_disable(twi_clk);
fail2:
clk_put(twi_clk);
fail1:
iounmap(twi_base);
fail0:
release_mem_region(res->start, res->end - res->start + 1);
return rc;
}
static struct platform_driver at91_i2c_driver = {
.probe = at91_i2c_probe,
.remove = __devexit_p(at91_i2c_remove),
.suspend = at91_i2c_suspend,
.resume = at91_i2c_resume,
.driver = {
.name = "at91_i2c",
.owner = THIS_MODULE,
},
};
static int __init at91_i2c_init(void)
{
return platform_driver_register(&at91_i2c_driver);
}
module_init(at91_i2c_init);
只看其中的probe函数发现,就是通过adapter->algo = &at91_algorithm;和rc = i2c_add_numbered_adapter(adapter);将i2c外设的时序算法注册到i2c子系统中。但是这个demo很明显没有smbus_xfer函数所以应该是调用,i2c_smbus_xfer_emulated。那我们接下来解析i2c_smbus_xfer_emulated这个函数
- 经过简化,最终调用的是i2c_transfer
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data * data)
{
···
if (i2c_transfer(adapter, msg, num) < 0)
return -1;
···
}
- 这里就非常清晰啦,就是调用算法里的master_xfer函数去读地址的
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
int ret;
if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
mutex_lock_nested(&adap->bus_lock, adap->level);
ret = adap->algo->master_xfer(adap,msgs,num);
mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -ENOSYS;
}
}
而master_xfer就是在busses里面的众多算法里面定义的,回头看就是
static struct i2c_algorithm at91_algorithm = {
.master_xfer = at91_xfer,
.functionality = at91_func,
};
``