图1
以s3c2410(稍微修改也可适用于 samsung a8 s5pc100)的适配器为例看适配器及通讯方法如何注册,在linux内i2c适配器是以platform的形式想内核注册的。也就是拿platform_device来描述适配器占用的资源,platform_driver来描述如何使用资源;
再看适配器的platform_device:
打开arch/arm/plat-samsung/dev-i2c0.c
static struct resource s3c_i2c_resource[] = {
[0] = {
.start = S3C_PA_IIC, /*i2c控制器寄存器的物理起始地址*/
.end = S3C_PA_IIC + SZ_4K - 1, /*结束地址*/
.flags = IORESOURCE_MEM, /*资源类型 io内存*/
},
[1] = {
.start = IRQ_IIC, /*占有的中断资源起始号*/
.end = IRQ_IIC, /*占有的中断资源结束号**/
.flags = IORESOURCE_IRQ, /*资源类型,irq 中断*/
},
};
struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c", /*用于匹配驱动的名字*/
#ifdef CONFIG_S3C_DEV_I2C1
.id = 0, /*设备编号*/
#else
.id = -1, /*如果只有一个设备编号为-1*/
#endif
.num_resources = ARRAY_SIZE(s3c_i2c_resource), /*资源数量*/
.resource = s3c_i2c_resource, /*指向占有的资源*/
};
图2
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
const struct platform_device_id *id_entry;
/* arch specific additions */
struct pdev_archdata archdata;
};
图3
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name;
struct device_type *type;
struct mutex mutex;
struct bus_type *bus;
struct device_driver *driver;
void *platform_data;
struct dev_pm_info power;
#ifdef CONFIG_NUMA
int numa_node;
#endif
u64 *dma_mask;
u64 coherent_dma_mask;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools;
struct dma_coherent_mem *dma_mem;
struct dev_archdata archdata;
#ifdef CONFIG_OF
struct device_node *of_node;
#endif
dev_t devt;
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
const struct attribute_group **groups;
void (*release)(struct device *dev);
&nbsnbsp;};
图4
void__init smdkc100_machine_init(void)
图5
platform_add_devices(smdkc100_devices,ARRAY_SIZE(smdkc100_devices));
依次注册所有platform _device
图6
platform_device_register(dev[i]);
图7
platform_device_add(struce platform_device pdev)
pdev->dev.bus = &platform_bus_type;
图8
kobject_add(&dev->kobj,dev->kobj.parent, NULL);设置kobj的parent,及创建sysfs的入口;
device_create_file();在 /sys/device/目录按指定权限创建文件;
device_add_class_symlinks(); 创建设备的符号连接
图9
bus_probe_device(dev);设备探测驱动
图10
kobject_uevent(dev->kobj, KOBJ_ADD)
向用户空间发送KOBJ_ADD的事件消息
图11
list_for_each_entry(list_info, &dev->class->p->class_interface, node)
If(class_info->add_dev) Class_info->add_dev(dev, class_info);
注册设备;
图12
注册结束
图13
device_attach(dev)
图14
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
为dev遍历所有drv尝试匹配;具体匹配有回调函数__device_attach完成;
图15
__device_attach(drv dev);
图16
driver_match_device(struct device_driver *drv, struct device *dev) { return drv->bus->match ? drv->bus->match(dev, drv) :1;}
图17
driver_probe_device(struct device_driver *drv, struct device *dev)
图18
really_probe(struct device * dev, struct device_driver * drv);
图19
drv->probe(dev);
调用platform->device_driver->probe结构里面的probe函数;并且传入dev结构。
图20
static int platform_drv_probe(struct device *_dev)
{
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev);
return drv->probe(dev);
}
图21
Struct bus_type platform_bus_type ={
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
图22
I2c 适配器 platform_driver注册流程:
打开driver/i2c/busses/i2c-s3c2410.c
适配器的platform_driver:
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe, /*设备和驱动匹配后执行的探测函数*/
.remove = s3c24xx_i2c_remove,
.id_table = s3c24xx_driver_ids, /*driver可以支持的设备*/
.driver = { /*设备模型使用*/
.owner = THIS_MODULE, /*所属模块*/
.name = "s3c-i2c", /*驱动的名字,如果id_table成员没有赋值则匹配此*/
.pm = S3C24XX_DEV_PM_OPS,
},
};
static struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-i2c", /* 可以匹配的名字*/
.driver_data = TYPE_S3C2410,
}, {
.name = "s3c2440-i2c", /* 可以匹配的名字 */
.driver_data = TYPE_S3C2440,
}, { },
};
图23
static int __init
i2c_adap_s3c_init(void);
图24
platform_driver_register(&s3cc24xx_i2c_driver)
drv->driver.bus = &platform_bus_type;
drv->driver.probe = platform_drv_probe;
图25
driver_register(&drv->driver);
图26
Other = driver_find(drv->name, drv->bus);
查看driver是否被注册过了。如果注册了,返回-EBUSY;
bus_add_driver( Struct device_driver drv);
图27
driver_attach(Struct device_driver drv);
图28
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
为drv,遍历所有dev尝试匹配;具体匹配有回调函数__driver_attach完成;
图29
__driver_attach(struct device *dev, void * data);
图30
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 (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
图31
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs;
#if defined(CONFIG_OF)
const struct of_device_id *of_match_table;
#endif
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
图32
struct i2c_adapter {
struct module *owner; /* 所属模块*/
unsigned int id; /*适配器的编号,i2c0或 i2c1*/
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* 指向适配器的通信方法 */
void *algo_data; /*适用于所有设备的数据域*/
struct rt_mutex bus_lock;
int timeout; /* 通讯的超时时间以 jiffies为单位 */
int retries;
struct device dev; /* 设备模型使用 */
int nr;
char name[48]; /*适配器名字*/
struct completion dev_released;
struct list_head userspace_clients;
};
图33
struct i2c_algorithm {
/*如果适配器不能算法不能做i2c底层访问,master_xfer设置为null,
如果可以使用smbus访问则设置smbus_xfer,如果他设置为null,则smbus
协议使用模拟的公共的i2c消息*/
/* master_xfer返回处理成功的消息的数量,出错返回负数*/
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);
/* 返回适配器支持哪里功能*/
u32 (*functionality) (struct i2c_adapter *);
};
图34
i2c适配器及适配器驱动(又叫做总线驱动)注册流程:
Linux拿i2c_adapter结果来描述一个物理上的i2c适配器(也就是soc内的i2c控制器),i2c_algorithm对应适配器的一套通信方法,或者可以说是适配器的驱动,没有通信方法的适配器好比没有灵魂的僵尸一样,做不了任何的事情。下面列出两个重要的数据机构;
流程图