当前位置: > 华清远见教育科技集团 > 嵌入式学习 > 讲师博文 > 以s3c2410的适配器为例看适配器及通讯方法如何注册
以s3c2410的适配器为例看适配器及通讯方法如何注册
时间:2016-12-13作者:华清远见

图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对应适配器的一套通信方法,或者可以说是适配器的驱动,没有通信方法的适配器好比没有灵魂的僵尸一样,做不了任何的事情。下面列出两个重要的数据机构;

流程图

发表评论
评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)