linux下I2c框架分析之终端设备(三)
平台:君正x1000
内核:Linux3.5
经过前面两篇文章已经知道了i2c框架的大概结构和适配器注册,现在分析i2c终端设备的驱动,作为普通的驱动工程师,一般都是拿着已有的驱动修改适配产品的sensor,这就是修改的终端驱动了,对咱们也是很重要。
此时要关注一个关键的已经实例化的结构
I2c总线
i2c终端设备和驱动都挂载在它下面,match不断扫描device和driver两个链表进行匹配,在 /sys/bus/能看到这个i2c
struct bus_type i2c_bus_type = { /* \drivers\i2c\i2c-core.c */
.name = "i2c",
.match = i2c_device_match, /* 匹配设备名和驱动名 */
.probe = i2c_device_probe, /* 匹配成功执行驱动probe函数 */
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
i2c终端的实例化使用i2c_board_info结构描述
struct i2c_board_info { /* \include\linux\i2c.h */
char type[I2C_NAME_SIZE]; // 设备名
unsigned short flags;
unsigned short addr; //地址
void *platform_data;
struct dev_archdata *archdata;
struct device_node *of_node;
struct acpi_dev_node acpi_node;
int irq; //中断号
};
板级文件中使用I2C_BOARD_INFO初始化type和addr
#define I2C_BOARD_INFO(dev_type, dev_addr) \
.type = dev_type, .addr = (dev_addr)
注册i2c终端设备
注册i2c终端设备有两种方法
方法一:
静态注册,使用此函数申请I2C设备信息结构体,将I2C总线号和设备信息赋值给设备信息结构体,并且将设备信息结构体的链表插入到__i2c_board_list中
int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
{
int status;
down_write(&__i2c_board_lock);
/* dynamic bus numbers will be assigned after the last static one */
if (busnum >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = busnum + 1;
/ * 将设备信息插入到链表 */
for (status = 0; len; len--, info++) {
struct i2c_devinfo *devinfo;
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
...
}
devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list);
}
up_write(&__i2c_board_lock);
return status;
}
还记得linux下I2c框架分析之适配器(二)中最后i2c_register_adapter接口实现吗
static int i2c_register_adapter(struct i2c_adapter *adap)
{ /* \drivers\i2c\i2c-core.c */
exit_recovery:
/* create pre-declared device nodes */
if (adap->nr < __i2c_first_dynamic_bus_num)
// 扫描__i2c_board_list链表上挂接的所有的i2c次设备信息并与适配器进行匹配,匹配成功创建i2c次设备
i2c_scan_static_board_info(adap);
.......
return 0;
}
当终端设备插入到链表后就等待i2c_register_adapter函数注册适配器时调用i2c_scan_static_board_info(adap)函数通过遍历__i2c_board_list链表找到注册的设备,调用i2c_new_device函数把链表中的每个成员构造成一个i2c_client。
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{ /* \drivers\i2c\i2c-core.c */
struct i2c_client *client;
client->adapter = adap; // 设定设备的设配器
client->addr = info->addr; // 地址
client->irq = info->irq; // 中断号
client->dev.bus = &i2c_bus_type; // 绑定总线
device_register(&client->dev); // 向总线注册设备
return client;
}
int device_register(struct device *dev)
{ /* \drivers\base\core.c */
device_initialize(dev);
return device_add(dev);
}
int device_add(struct device *dev)
{ /* \drivers\base\core.c */
bus_add_device(dev);
bus_probe_device(dev);
}
int bus_add_device(struct device *dev)
{ /* \drivers\base\bus.c */
/* 将设备添加到总线的设备链表中(bus->p->klist_devices) */
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
}
void bus_probe_device(struct device *dev)
{
device_attach(dev);
}
int device_attach(struct device *dev)
{ /* \drivers\base\dd.c 将设备附加到驱动*/
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
}
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
void *data, int (*fn)(struct device_driver *, void *))
{ /* \drivers\base\bus.c */
/* 遍历总线的驱动链表上的所有驱动,调用fn函数指向__device_attach */
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data);
}
static int __device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;
if (!driver_match_device(drv, dev))
return 0;
return driver_probe_device(drv, dev);
}
终端设备的注册咱们暂且先分析这,剩下的函数调用放到驱动中分析更合适明了;
方法二:
从用户空间创建设备(详细阅读/Documentation/i2c/instantiating-devices文档)
执行命令cd /sys/class/i2c-adapter/,可以看到内容i2c-0 i2c-1 i2c-2,说明有多款适配器
< 做下面实验需要把内核中静态编译进的drv驱动给去掉,然后加载自己的drv驱动>
创建设备
echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-0/new_device,导致i2c_new_device被调用,最后drv里的probe 函数就不会被调用。如果把地址改为0x51,那么也会在bus的dev链表中增加一个dev结构,所以这种方法也是不会判断地址是否正确。
删除设备
echo 0x50 > /sys/class/i2c-adapter/i2c-0/delete_device,导致i2c_unregister_device。