I2C设备驱动框架和一个实例注册
来源:互联网 发布:千牛淘宝卖家版 编辑:程序博客网 时间:2024/05/16 12:03
注:这是我在自己学习I2C驱动框架时候记录的笔记,不喜勿喷
一、内核中实现I2C驱动框架和一个实例注册相关的文件
1./dricer/i2c/i2c-core.c
2./arch/arm/mach-s3c2440/mach-smdk2440.c
3.driver/i2c/buses/i2c-s3c2410.c
4./driver/i2c/i2c-dev.c
以后分析的代码就来源于这几个文件中
二、几个相关的结构体
1.相关的结构体
(1)struct i2c_adapter 主芯片的I2C适配器
(2)struct i2c_algorthm I2C算法
(3)struct i2c_client I2C(从设备)客户端(信息)
(4)struct i2c_driver I2C(从设备)驱动
struct i2c_adapter { struct module *owner; //id号 unsigned int id; unsigned int class; /* classes to allow probing for */ //I2C通信相关的算法。注意是指针,也就是说一个adapter可以使用多种I2C通信算法 const struct i2c_algorithm *algo; /* the algorithm to access the bus */ //算法相关数据 void *algo_data; /* data fields that are valid for all devices */ //I2C总线相关的互斥锁 struct rt_mutex bus_lock; int timeout; /* in jiffies */ //尝试重新连接的发送数据的次数 int retries; struct device dev; /* the adapter device */ int nr; //名字 char name[48]; struct completion dev_released; struct list_head userspace_clients;};
struct i2c_algorithm { //发送数据的函数指针 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 */ //决定支持什么类型的adapter的函数指针 u32 (*functionality) (struct i2c_adapter *);};
struct i2c_client { unsigned short flags; /* div., see below */ //低7位的I2C设备地址 unsigned short addr; //名字 char name[I2C_NAME_SIZE]; //相对的adapter struct i2c_adapter *adapter; //相对的驱动 struct i2c_driver *driver; /* and our access routines */ //实体I2C设备 struct device dev; /* the device structure */ //中断号 int irq; /* irq issued by device */ struct list_head detected;};
//i2C相关驱动所需的函数指针的封装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 *); /* Alert callback, for example for the SMBus alert protocol. * The format and meaning of the data value depends on the protocol. * For the SMBus alert protocol, there is a single bit of data passed * as the alert response's low bit ("event flag"). */ void (*alert)(struct i2c_client *, unsigned int data); /* 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; //用来machdevice和driver的idtable const struct i2c_device_id *id_table; /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *, struct i2c_board_info *); const unsigned short *address_list; struct list_head clients;};
三、I2C框架的注册
struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match, .probe = i2c_device_probe, .remove = i2c_device_remove, .shutdown = i2c_device_shutdown, .pm = &i2c_device_pm_ops,};
static int __init i2c_init(void){ int retval; //在BUS总线下注册I2C总线 retval = bus_register(&i2c_bus_type); if (retval) return retval;#ifdef CONFIG_I2C_COMPAT //在class下面创建"i2c-adapter"目录 i2c_adapter_compat_class = class_compat_register("i2c-adapter"); if (!i2c_adapter_compat_class) { retval = -ENOMEM; goto bus_err; }#endif //添加一个dummy_driver,具体没看代码 retval = i2c_add_driver(&dummy_driver); if (retval) goto class_err; return 0;class_err:#ifdef CONFIG_I2C_COMPAT class_compat_unregister(i2c_adapter_compat_class);bus_err:#endif bus_unregister(&i2c_bus_type); return retval;}
框架注册总结:实际在BUS总线下注册了I2C总线,重要的还是2c_bus_type结构体,具体我们看两个重要的函数指针.match和.prob。
//mach函数用来匹配client和driver,具体的匹配方式是通过driver中的idtable和client中的name来匹配的。具体函数看i2c_match_id(driver->id_table, client)static int i2c_device_match(struct device *dev, struct device_driver *drv){ struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; if (!client) return 0; driver = to_i2c_driver(drv); /* match on an id table if there is one */ if (driver->id_table) return i2c_match_id(driver->id_table, client) != NULL; return 0;}
static int i2c_device_probe(struct device *dev){ struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; int status; if (!client) return 0; driver = to_i2c_driver(dev->driver); //检验driver中是否提供prob和id_table if (!driver->probe || !driver->id_table) return -ENODEV; //client的driver和驱动关联 client->driver = driver; if (!device_can_wakeup(&client->dev)) device_init_wakeup(&client->dev, client->flags & I2C_CLIENT_WAKE); dev_dbg(dev, "probe\n"); //调用驱动中写的driver,这个函数是我们自己实现的prob status = driver->probe(client, i2c_match_id(driver->id_table, client)); if (status) { client->driver = NULL; i2c_set_clientdata(client, NULL); } return status;}
注册到这里就完成了,到时候我们自己写的驱动和设备mach之后会自动调用prob函数执行。
i2c-core.c中提供了一些驱动工程师可以调用的接口来帮助写设备驱动,这些接口在用到的时候会列出源代码
四、实例分析
1.我们先看i2c的device的注册
(1)具体的代码在mach-xxx.c里面
这里只列出相关的函数调用(并且只举例了一个i2c0)
smdkc110_machine_init platform_add_devices(smdkc110_devices, ARRAY_SIZE(smdkc110_devices)); platform_device_register(&s3c_device_i2c0); s3c_i2c0_set_platdata(NULL); i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
/*******************相关注册device的结构体**********************/static struct s3c2410_platform_i2c default_i2c_data0 __initdata = { .flags = 0, .slave_addr = 0x10, .frequency = 400*1000, .sda_delay = S3C2410_IICLC_SDA_DELAY15 | S3C2410_IICLC_FILTER_ON,};static struct resource s3c_i2c_resource[] = { [0] = { .start = S3C_PA_IIC, .end = S3C_PA_IIC + SZ_4K - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_IIC, .end = IRQ_IIC, .flags = IORESOURCE_IRQ, },};//这里的name必须和driver里面的id_table的name一致struct platform_device s3c_device_i2c0 = { .name = "s3c2410-i2c", .id = 0, .num_resources = ARRAY_SIZE(s3c_i2c_resource), .resource = s3c_i2c_resource,};/*******************相关注册device的结构体**********************///传参为NULLvoid __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd){ struct s3c2410_platform_i2c *npd; if (!pd) pd = &default_i2c_data0; npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL); if (!npd) printk(KERN_ERR "%s: no memory for platform data\n", __func__); else if (!npd->cfg_gpio) npd->cfg_gpio = s3c_i2c0_cfg_gpio; s3c_device_i2c0.dev.platform_data = npd;}
static struct i2c_board_info i2c_devs0[] __initdata = {#ifdef CONFIG_SND_SOC_WM8580 { I2C_BOARD_INFO("wm8580", 0x1b), },#endif};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); if (!devinfo) { pr_debug("i2c-core: can't register boardinfo!\n"); status = -ENOMEM; break; } devinfo->busnum = busnum; devinfo->board_info = *info; //最重要的就是这句,把devinfo注册到__i2c_board_list中,到时候在注册adapter的时候才能找到I2C设备 list_add_tail(&devinfo->list, &__i2c_board_list); } up_write(&__i2c_board_lock); return status;}
到这I2C设备注册也就结束了,相关结构体之间的关联,我懒得画图,我也是借用大神的图来参考的。
这两张图片对应上面的函数,可以自己分析。
五、驱动实例的分析
1.参考i2c-s3c2410.c来分析,它采用的平台驱动模型来写的I2C驱动
static struct platform_device_id s3c24xx_driver_ids[] = { { .name = "s3c2410-i2c", .driver_data = TYPE_S3C2410, }, { .name = "s3c2440-i2c", .driver_data = TYPE_S3C2440, }, { },};static struct platform_driver s3c24xx_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove = s3c24xx_i2c_remove, .id_table = s3c24xx_driver_ids, .driver = { .owner = THIS_MODULE, .name = "s3c-i2c", .pm = S3C24XX_DEV_PM_OPS, },};static int __init i2c_adap_s3c_init(void){ return platform_driver_register(&s3c24xx_i2c_driver);}
我们知道在platform平台驱动,中只要driver和device相遇匹配之后就会执行prob函数。所以接下来我们看prob函数
static int s3c24xx_i2c_probe(struct platform_device *pdev){ struct s3c24xx_i2c *i2c; struct s3c2410_platform_i2c *pdata; struct resource *res; int ret; pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "no platform data\n"); return -EINVAL; } i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL); if (!i2c) { dev_err(&pdev->dev, "no memory for state\n"); return -ENOMEM; } //设置I2C的名字 strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); i2c->adap.owner = THIS_MODULE; //填充I2C中adap的通信算法,这个算法我不太懂。 i2c->adap.algo = &s3c24xx_i2c_algorithm; //填充重连次数 i2c->adap.retries = 2; i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; i2c->tx_setup = 50; spin_lock_init(&i2c->lock); init_waitqueue_head(&i2c->wait); /* find the clock and enable it */ i2c->dev = &pdev->dev; i2c->clk = clk_get(&pdev->dev, "i2c"); if (IS_ERR(i2c->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); ret = -ENOENT; goto err_noclk; } dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); clk_enable(i2c->clk); /* map the registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "cannot find IO resource\n"); ret = -ENOENT; goto err_clk; } i2c->ioarea = request_mem_region(res->start, resource_size(res), pdev->name); if (i2c->ioarea == NULL) { dev_err(&pdev->dev, "cannot request IO\n"); ret = -ENXIO; goto err_clk; } i2c->regs = ioremap(res->start, resource_size(res)); if (i2c->regs == NULL) { dev_err(&pdev->dev, "cannot map IO\n"); ret = -ENXIO; goto err_ioarea; } dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res); /* setup info block for the i2c core */ //相互关联 i2c->adap.algo_data = i2c; i2c->adap.dev.parent = &pdev->dev; /* initialise the i2c controller */ //具体的寄存器配置就在这个函数里面 ret = s3c24xx_i2c_init(i2c); if (ret != 0) goto err_iomap; /* find the IRQ for this unit (note, this relies on the init call to * ensure no current IRQs pending */ i2c->irq = ret = platform_get_irq(pdev, 0); if (ret <= 0) { dev_err(&pdev->dev, "cannot find IRQ\n"); goto err_iomap; } ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c); if (ret != 0) { dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); goto err_iomap; } ret = s3c24xx_i2c_register_cpufreq(i2c); if (ret < 0) { dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); goto err_irq; } /* Note, previous versions of the driver used i2c_add_adapter() * to add the bus at any number. We now pass the bus number via * the platform data, so if unset it will now default to always * being bus 0. */ i2c->adap.nr = pdata->bus_num; //这个函数才是最重要的,也是i2c-core.c提供的接口,来注册i2c设备。 ret = i2c_add_numbered_adapter(&i2c->adap); if (ret < 0) { dev_err(&pdev->dev, "failed to add bus to i2c core\n"); goto err_cpufreq; } platform_set_drvdata(pdev, i2c); clk_disable(i2c->clk); dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev)); return 0; err_cpufreq: s3c24xx_i2c_deregister_cpufreq(i2c); err_irq: free_irq(i2c->irq, i2c); err_iomap: iounmap(i2c->regs); err_ioarea: release_resource(i2c->ioarea); kfree(i2c->ioarea); err_clk: clk_disable(i2c->clk); clk_put(i2c->clk); err_noclk: kfree(i2c); return ret;}
调用层次(注意:从这里开始就是i2c-core.c提供的接口了)
i2c_add_numbered_adapter i2c_register_adapter
static int i2c_register_adapter(struct i2c_adapter *adap){ int res = 0, dummy; /* Can't register until after driver model init */ if (unlikely(WARN_ON(!i2c_bus_type.p))) { res = -EAGAIN; goto out_list; } rt_mutex_init(&adap->bus_lock); INIT_LIST_HEAD(&adap->userspace_clients); /* Set default timeout to 1 second if not already set */ if (adap->timeout == 0) adap->timeout = HZ; //设置dev的名字 dev_set_name(&adap->dev, "i2c-%d", adap->nr); //设置dev.bus adap->dev.bus = &i2c_bus_type; //设置dev.type adap->dev.type = &i2c_adapter_type; //注册adap->dev res = device_register(&adap->dev); if (res) goto out_list; dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);#ifdef CONFIG_I2C_COMPAT res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev, adap->dev.parent); if (res) dev_warn(&adap->dev, "Failed to create compatibility class link\n");#endif /* create pre-declared device nodes */ //重要点。根据分析会调用i2c_scan_static_board_info函数 if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); /* Notify drivers */ mutex_lock(&core_lock); //找到相应的driver对应adapter dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); mutex_unlock(&core_lock); return 0;out_list: mutex_lock(&core_lock); idr_remove(&i2c_adapter_idr, adap->nr); mutex_unlock(&core_lock); return res;}
static void i2c_scan_static_board_info(struct i2c_adapter *adapter){ struct i2c_devinfo *devinfo; down_read(&__i2c_board_lock); //注意__i2c_board_list链表在I2C设备注册时候就已经填充了,接着调用i2c_new_device函数 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);}
//这个函数作用:创建并且填充client,还绑定了相对的adapterstruct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info){ struct i2c_client *client; int status; client = kzalloc(sizeof *client, GFP_KERNEL); if (!client) return NULL; client->adapter = adap; client->dev.platform_data = info->platform_data; if (info->archdata) client->dev.archdata = *info->archdata; client->flags = info->flags; client->addr = info->addr; client->irq = info->irq; strlcpy(client->name, info->type, sizeof(client->name)); /* Check for address validity */ status = i2c_check_client_addr_validity(client); if (status) { dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n", client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr); goto out_err_silent; } /* Check for address business */ status = i2c_check_addr_busy(adap, client->addr); if (status) goto out_err; client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; client->dev.type = &i2c_client_type;#ifdef CONFIG_OF client->dev.of_node = info->of_node;#endif dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), client->addr); //在这里device_register在I2C总线下添加了client status = device_register(&client->dev); if (status) goto out_err; dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", client->name, dev_name(&client->dev)); return client;out_err: dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x " "(%d)\n", client->name, client->addr, status);out_err_silent: kfree(client); return NULL;}
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); //这里的参数是简化过的,自己分析下就明白了 __process_new_adapter(drv, data); i2c_do_add_adapter(to_i2c_driver(d), data);
static int i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap){ //这是老版本使用的,所以在这里直接返回了。 i2c_detect(adap, driver); /* Let legacy drivers scan this bus for matching devices */ //driver->attach_adapter函数指针在i2c-dev.c里面被赋值 if (driver->attach_adapter) { /* We ignore the return code; if it fails, too bad */ driver->attach_adapter(adap); } return 0;}
到这里基本的初始化已经完成了。
- I2C设备驱动框架和一个实例注册
- i2c设备驱动框架模型实例
- I2C设备驱动注册
- I2C设备驱动注册
- 简单i2c设备驱动实例
- i2c驱动注册流程实例分析
- I2C设备驱动(三)--linux i2c驱动框架
- i2c设备驱动实例 ds1307为例
- Linux 下I2C设备驱动新框架
- linux驱动:i2c驱动(三)流程图之注册设备
- 电容屏驱动,输入设备注册,I2C设备注册,中断注册,虽然没有完全调试成功
- Linux设备驱动之I2C架构分析 adapter注册
- 怎么写I2c和SMBus设备驱动
- Linux I2C 设备注册
- linux I2c设备注册
- I2C驱动注册过程
- i2c驱动--驱动框架
- 一个platform总线型的i2c设备的注册
- 每天一个 Linux 命令(21):find命令之xargs
- Kubernetes 1.5安装 heapster
- spring bean的形式(3)
- 原生Ajax:XMLHttpRequest对象
- HDU-1003
- I2C设备驱动框架和一个实例注册
- Linux 环境下/etc/profile和/etc/profile.d 的区别和用法
- NE555
- Spring中property-placeholder的使用与解析
- (iOS-基本知识)堆和栈的基本知识详解
- postgresql字符转义
- PHP面试题(持续更新)
- HDUoj 2289 Cup (二分 高精度
- 一些基本概念的笔记(技术向)