一、IIC总线驱动代码

IIC总线控制器通常是在内存上的,连接在platform总线上,所以需要通过platform_driver和platform_device的匹配。我想大概根据总线设备驱动模型的分层思想,将一个驱动程序分为device和driver两层,将IIC总线驱动程序也分成platform_device和platform_driver这两个部分来介绍。在platform_device中提供底层的硬件资源,在platform_driver中取出这些资源进行使用。可能有些不恰当,但是明确分成两部分后更容易理解

1.1 platform_device层进行的操作

相关的数据结构(资源、名称id、参数相关的结构体)的定义在dev-i2c0.c这个文件中

    

static struct resource s3c_i2c_resource[] = {
[] = {
.start = S3C_PA_IIC,
.end = S3C_PA_IIC + SZ_4K - ,
.flags = IORESOURCE_MEM,
},
[] = {
.start = IRQ_IIC,
.end = IRQ_IIC,
.flags = IORESOURCE_IRQ,
},
}; struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c",
#ifdef CONFIG_S3C_DEV_I2C1
.id = ,
#else
.id = -,
#endif
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
}; static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
.flags = ,
.slave_addr = 0x10,
.frequency = *,
.sda_delay = ,
};

这里对platform_device的注册不做过多的分析,接下来重点分析在platform_driver层的操作。

1.2 platform_driver层分析

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);
}
static void __exit i2c_adap_s3c_exit(void)
{
platform_driver_unregister(&s3c24xx_i2c_driver);
}

1.2.1 platform_driver_register函数分析:

platform_driver_register(&s3c24xx_i2c_driver)

drv->driver.bus = &platform_bus_type

driver_register(&drv->driver)

driver_find(drv->name, drv->bus) //查找驱动是否已经装载如果 //没有装载则执行下边的bus_add_driver

bus_add_driver(drv) //根据总线类型添加对应的驱动

driver_add_groups(drv, drv->groups)//驱动添加到对应的组中

1.2.2 driver_find函数分析:

kset_find_obj(bus->p->drivers_kset, name)//根据传入总线类型以//及驱动名称,在该总线类型对应的链表中寻找看该驱动是否已经存在

1.2.3 bus_add_driver函数分析:

bus_add_driver(struct device_driver *drv)

bus_get(drv->bus) // 获取总线类型

klist_init(&priv->klist_devices, NULL, NULL)

kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,"%s", drv->name)// 这些有关的kset kobject ktype等有关的数据结构等以后有空了在进行分析

driver_attach(drv); // 因为drv->bus->p->drivers_autoprobe 一般默认的是1

bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);//重点分析这个函数:根据dirver所属的总线,遍历该总线的device,找到名称匹配的,在回调函数 __driver_attach中先执行

platform_match(struct device *dev, struct device_driver *drv)

of_driver_match_device(dev, drv) //这个函数应该是一个 空函数返回0

platform_match_id(pdrv->id_table, pdev) != NULL; //这里应该是这个匹配函数起了作用,从pdrv->id_table中逐个匹配设备名称"s3c2410-i2c-i2c"

匹配成功后执行,否则不执行

driver_probe_device(drv, dev)

really_probe(dev, drv);

dev->bus->probe(dev)或者drv->probe(dev);// 因为平台总线结构体中没有探测函数,故执行驱动的探测函数即s3c24xx_i2c_probe

klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

module_add_driver(drv->owner, drv);

driver_create_file(drv, &driver_attr_uevent);

driver_add_attrs(bus, drv);

add_bind_files(drv);

kobject_uevent(&priv->kobj, KOBJ_ADD);

上面是platform_driver_register函数一层一层的函数调用关系将platform_driver_register函数的作用简明扼要的说:将s3c24xx_i2c_driver这个驱动注册到platform_bus中去,在sys/bus/platform这个文件夹中穿件一些文件和文件夹(这里我们暂时不分析),并且遍历该platform_bus总线上的device设备,找到与driver_device名称匹配的设备,如果存在这种device,那么将执行probde探测函数。

1.3  s3c24xx_i2c_probe 函数分析

1.3.1 s3c24xx_i2c_probe 传入参数分析

  在这里有一个疑惑:传入到s3c2410_i2c_probe中的参数是一个device类型的结构体,而实际函数的参数是一个platfrom_device,我想着两个类型之间肯定有一定的关联。难道是在注册platform_device时,做了类型转换????????????????还是函数调用分析的有错误????????????

为了解决这个疑惑我们看一下这是两个成对的结构体:platform_driver 与device_driver      platform_device 与 device

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;
};
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; /* disables bind/unbind via sysfs */ #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;
}; 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;
}; struct device {
struct device *parent; struct device_private *p; struct kobject kobj;
const char *init_name; /* initial name of the device */
struct device_type *type; struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/ struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
struct dev_pm_info power; #ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */ struct device_dma_parameters *dma_parms; struct list_head dma_pools; /* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
/* arch specific additions */
struct dev_archdata archdata;
#ifdef CONFIG_OF
struct device_node *of_node;
#endif dev_t devt; /* dev_t, creates the sysfs "dev" */ spinlock_t devres_lock;
struct list_head devres_head; struct klist_node knode_class;
struct class *class;
const struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev);
};

注意到:

int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)//
drv->driver.probe = platform_drv_probe; // 这里有对driver_device结构体的探测函数赋值
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown; return driver_register(&drv->driver);
} static int platform_drv_probe(struct device *_dev) // 这里才是really_probe真正执行的探测函数
{
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev); return drv->probe(dev);
}

然后继续看在really_probe(struct device *dev, struct device_driver *drv)中执行的是drv->probe(dev), 也就是说执行的是platform_drv_probe(struct device *_dev)就是在这个函数中实现了从device_driver变到platform_driver, device变到platform_device。

1.3.2 probe函数分析

这个函数是设备驱动函数中比较重要的一个函数,在分析这个函数之前先分析一下i2c_adapter这个结构体。在i2c-s3c2410.c中用i2c_adapter数据结构来描述一个i2c适配器(芯片内部的i2c控制器),按照我的理解:S3C6410有两个IIC控制器,则有两个i2c_adapter,而S3C2410有一个IIC控制器,则有一个i2c_adapter。

struct i2c_adapter {
struct module *owner;
unsigned int id __deprecated;
unsigned int class; //适配器所属的类
const struct i2c_algorithm *algo;//该总线上的通信方法这个结构体非常重要:i2c_algorithm 实际上就是具体的IIC控制器的底层操作:发出P信号S信号,中断,数据传输
void *algo_data; // 通信方法的附加数据 struct rt_mutex bus_lock;// 对所有设备的锁结构体 int timeout; //超时时间
int retries; //重复次数
struct device dev; //适配器设备
i2c_adapter中封装这个结构体,说明这个适配器是作为一个设备被注册进入内核中的 int nr; //总线的编号
char name[]; //总线的名称
struct completion dev_released; struct mutex userspace_clients_lock;
struct list_head userspace_clients;
}; struct i2c_algorithm { int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);//master_xfer:对应于普通的 i2c 传输协议
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
//Smbus-xfer:i2c协议子集 smbus ,有些设备只支持这个协议
u32 (*functionality) (struct i2c_adapter *);
//functionality 用来描述,adapter 所具有的功能,比如是否支持 smbus
};

在s3c2410_i2c_probe中要干的事有:

(1)设置i2c适配器

(2)使能i2c时钟

(3)内存映射

(4)初始化i2c控制器

(5)设置中断

(6)将i2c适配器注册到i2c总线上

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
struct s3c2410_platform_i2c *pdata;
struct resource *res;
int ret;
// 在platform_device中介绍了s3c_i2c0_set_platdata(...),通过这个函数我们
//将platform_data设置好,在这里将platform_data取出来使用
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;
}
//1. 对IIC适配器做一些设置,尤其要注意algo关于通信方法的设置
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
i2c->adap.retries = ;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = ;
//
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait); /* find the clock and enable it */
//2. 使能IIC时钟
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 */
// 3. io内存映射
res = platform_get_resource(pdev, IORESOURCE_MEM, );
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 */
//4. IIC 控制器初始化
ret = s3c24xx_i2c_init(i2c);
if (ret != )
goto err_iomap; /* find the IRQ for this unit (note, this relies on the init call to
* ensure no current IRQs pending
*/
// 5. 注册中断
i2c->irq = ret = platform_get_irq(pdev, );
if (ret <= ) {
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 != ) {
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
goto err_iomap;
} ret = s3c24xx_i2c_register_cpufreq(i2c);
if (ret < ) {
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;
//6. 比较重要的一个函数,将IIC适配器注册到i2c总线上去
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < ) {
dev_err(&pdev->dev, "failed to add bus to i2c core\n");
goto err_cpufreq;
} platform_set_drvdata(pdev, i2c); dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
clk_disable(i2c->clk);
return ; 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_bus_type的driver 链表,取出每一个driver 调用 i2c_do_add_adapter

i2c_do_add_adapter

i2c_detect(adap, driver);

i2c_detect_address(temp_client, driver);

i2c_new_device(adapter, &info);

这里就不分析具体是干什么用的了,只是提供一个分析代码的框架。

最新文章

  1. 深入浅出设计模式——享元模式(Flyweight Pattern)
  2. 在Android应用中使用Clean架构
  3. [转]javascript console 函数详解 js开发调试的利器
  4. win8系统开发者预览版安装中文软件报错怎么办
  5. SQL server 时间日期函数、类型转换
  6. android生成二维码
  7. 复制、移动和删除:cp, rm, mv
  8. vs2012配置opencv及简单测试
  9. JAVA GUI学习 - JInternalFrame浮动窗口:可拖拽窗口(依赖于父窗口)
  10. Java的LockSupport.park()实现分析
  11. java学习笔记-set
  12. 几种JAVA加密算法
  13. 自己动手编写IOC框架(一)
  14. python 玩耍天地
  15. [蓝牙前沿应用] 照明即平台 —— 通过蓝牙增强服务提高照明投资回报率(蓝牙MESH、定位AoA、AoD)
  16. SuperMap空间数据处理与制图操作短视频汇总
  17. Python MySQL - 进行数据查询
  18. 实习第一天:static 声明的 变量和 方法
  19. javascript数据结构与算法--二叉树遍历(先序)
  20. linux/mac下命令行rm回收站--rmtrash

热门文章

  1. 解决升Win10系统后VMware虚拟机不能联网的问题
  2. java内存回收需要了解的知识
  3. OpenCV.用户选择
  4. NOIp2012:借教室
  5. (十八)JDBC获取存储过程和主键
  6. dfs/bfs专项训练
  7. 最长回文 HDU - 3068(马拉车算法)
  8. 网络流+最小生成树的最少割边数--How Many to Be Happy?
  9. phpstorm右侧边栏怎么打开?
  10. LeetCode 2——两数相加(JAVA)