linux-2.6.38 总线驱动分析
一、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);
这里就不分析具体是干什么用的了,只是提供一个分析代码的框架。
linux-2.6.38 总线驱动分析的更多相关文章
- Linux内核中SPI总线驱动分析
本文主要有两个大的模块:一个是SPI总线驱动的分析 (研究了具体实现的过程): 另一个是SPI总线驱动的编写(不用研究具体的实现过程). 1 SPI概述 SPI是英语Serial Peripheral ...
- linux内核SPI总线驱动分析(一)(转)
linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析 (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...
- Linux 串口、usb转串口驱动分析(2-2) 【转】
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...
- Linux 串口、usb转串口驱动分析(2-1) 【转】
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...
- 19.Linux-USB总线驱动分析
如下图所示,以windows为例,我们插上一个没有USB设备驱动的USB,就会提示你安装驱动程序 为什么一插上就有会提示信息? 是因为windows自带了USB总线驱动程序, USB总线驱动程序负责: ...
- linux驱动基础系列--Linux下Spi接口Wifi驱动分析
前言 本文纯粹的纸上谈兵,我并未在实际开发过程中遇到需要编写或调试这类驱动的时候,本文仅仅是根据源码分析后的记录!基于内核版本:2.6.35.6 .主要是想对spi接口的wifi驱动框架有一个整体的把 ...
- Linux下的USB总线驱动(一)
版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 一.USB理论 1. USB概念概述 USB1.0版本速度1.5Mbps(低速USB) USB1 ...
- linux驱动基础系列--Linux 串口、usb转串口驱动分析
前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...
- linux内核SPI总线驱动分析(二)(转)
简而言之,SPI驱动的编写分为: 1.spi_device就构建并注册 在板文件中添加spi_board_info,并在板文件的init函数中调用spi_register_board_info(s3 ...
随机推荐
- Qt 开源串口工具serialplot
一.总结 serialplot,可视化很强大的串口收发工具.源代码:https://bitbucket.org/hyOzd/serialplot 1.serialplot用到了qwt插件,所以先安装好 ...
- Jmeter 逻辑控制器 之 Runtime Controller
一.认识 Runtime Controller 控制其下样例执行的时间长度. 设置界面: Runtime (seconds):运行时间,单位秒.即控制其下样例执行多长时间.与线程组中的调度器的持续 ...
- Arduino基础入门—3.连接 IIC 1602 LCD显示文字
1. IIC转接板介绍 Arduino Uno R3开发板的外部IO口是非常有限的.在驱动LCD1602时,尽管我们的数据线使用了4线,相对于8线方式减少一半,但是在需要外接多种传感器的应用中,4线驱 ...
- 【图像处理与医学图像处理】NV12与YV12的区别
用videoCapture和IAMStreamConfig拿到的支持的格式列表.发现支持2中图像格式,YV12和NV12.具体是怎么样的内存分布不知道.查了些文档.自己修改了几个图.看出了点端倪YV1 ...
- axios二次封装的几种方法
一.用Class方法 import axios from "axios"; declare var Promise: any; export class Request { sta ...
- HIVE配置mysql metastore
HIVE配置mysql metastore hive中除了保存真正的数据以外还要额外保存用来描述库.表.数据的数据,称为hive的元数据.这些元数据又存放在何处呢? 如果不修改配置hive ...
- 洛谷 题解 P1284 【三角形牧场】
状态: dp[i][j]表示用i和j的木板能否搭成,不用去管第三块,因为知道了两块的长度与周长,那就可以表示出第三块:c-i-j 转移 有点类似于背包 if((j-l[i]>=0&&am ...
- POJ 2195 Going Home 【最小费用最大流】
题目链接:http://poj.org/problem?id=2195 Time Limit: 1000MS Memory Limit: 65536K Total Submissions:2715 ...
- 【转载】在一台电脑上运行两个或两个以上的tomcat
作者注: 本片为转载文章,一台电脑运行两个及以上tomcat的原因是:第一个eclipse版本是4.5,最高支持tomcat8.0版本,并且这个版本的eclipse通过svn提交和更新项目极其缓慢,无 ...
- fiddler笔记:Find Session窗口
通过Edit菜单选项或CTRL+F打开Find Session窗口.其主要是用来搜索捕捉到的请求和响应. find 指定要搜索的文本 Options Search 支持的搜索选项:Requests a ...