RTC设备驱动
问题:pcf8563 RTC设备驱动不能被正常的加载!问题分析过程。
问题在下午得到解决,虽然解决的办法比较笨,采用的是不断的使用printk来跟踪rtc-8563驱动的加载的过程,以及iic模块的工作过程。
曾经想过将内核的DEBUG打开,打开的方法在/linux2.6.21/include/linux/device.h文件中搜索DEBUG,这样的话,设备的所有的操作的debug信息都会输出,你会受不了,因为输出的无用的信息会掩盖你需要的真正的信息。
所以就改为在i2c-core.c和rtc-8563文件中加入printk调试信息来跟踪系统的信息输出。我们来分析一下rtc驱动的加载过程。
Linux驱动的i2c文件夹下有algos,busses,chips三个文件夹,另外还有i2c-core.c和i2c-dev.c两个文件。其中 i2c-core.c文件实现了I2C core框架,是Linux内核用来维护和管理的I2C的核心部分,其中维护了两个静态的List,分别记录系统中的I2C driver结构和I2C adapter结构。I2C core提供接口函数,允许一个I2C adatper,I2C driver和I2C client初始化时在I2C core中进行注册,以及退出时进行注销。同时还提供了I2C总线读写访问的一般接口,主要应用在I2C设备驱动中。
在rtc-8563文件中:
static int __init pcf8563_init(void)
{
    return i2c_add_driver(&pcf8563_driver);
}
static void __exit pcf8563_exit(void)
{
    i2c_del_driver(&pcf8563_driver);
}
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
module_init(pcf8563_init);
module_exit(pcf8563_exit);
从这里我们可以知道模块加载的初始化函数和卸载函数都是使用的i2c的框架函数i2c_add_driver和i2c_del_driver。
i2c_add_driver被定义在 include/linux/i2c.h文件中。其实质是i2c-core.c文件中的i2c_register_driver函数
该函数的原型如下:
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    struct list_head   *item;
    struct i2c_adapter *adapter;
    int res;
    /* add the driver to the list of i2c drivers in the driver core */
    driver->driver.owner = owner;
    driver->driver.bus = &i2c_bus_type;
    res = driver_register(&driver->driver);
    if (res)
        return res;
    mutex_lock(&core_lists);
    //将该driver的list成员加入到全局的drivers链表尾部,linux中大量存在这种链表的结构体
    list_add_tail(&driver->list,&drivers);
    pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
    /* now look for instances of driver on our adapters */
    if (driver->attach_adapter) {
//该函数搜索整个adapters链表,item指向每一个链表中的成员,这里实际是一个for循环。
//      #define list_for_each(pos, head) \
//        for (pos = (head)->next; prefetch(pos->next), pos //!=(head); pos = pos->next)
        list_for_each(item,&adapters) {
  //获得一个适配器结构体
            adapter = list_entry(item, struct i2c_adapter, list);
            driver->attach_adapter(adapter);
        }
    }
    mutex_unlock(&core_lists);
    ;
}
EXPORT_SYMBOL(i2c_register_driver);
在这个函数中,首先向内核中注册你的驱动,然后锁信号量。。最关键的一步是:
driver->attach_adapter(adapter);
而attach_adapter就是在rtc-pcf8563.c文件中定义的重要的驱动结构体,定义如下。
static struct i2c_driver pcf8563_driver = {
    .driver        = {
        .name    = "pcf8563",
    },
    .id        = I2C_DRIVERID_PCF8563,
    .attach_adapter = &pcf8563_attach,
    .detach_client    = &pcf8563_detach,
};
所以也就是说i2c框架函数会回调你写的适配器加载函数,我们的适配器加载函数是pcf8563_attach函数。该函数定义如下:
static int pcf8563_attach(struct i2c_adapter *adapter)
{
    return i2c_probe(adapter, &addr_data, pcf8563_probe);
}
调用i2c框架函数i2c_probe来进行适配器的加载。
在下面这个函数中传递参数为
适配器变量:adapter。
i2c_client_address_data结构体原型
static struct i2c_client_address_data addr_data = {            \
    .normal_i2c    = normal_i2c,                    \
    .probe        = probe,                    \
    .ignore        = ignore,                    \
    .forces        = forces,                    \
}
注意,在我们的rtc-.c文件中只定义了normal_i2c数组。
static unsigned short normal_i2c[] = { 0x51,I2C_CLIENT_END };
而出错的地方就在这里,原来的定义没有0x51这个成员,所以根本不去加载pcf8563这个器件。
int i2c_probe(struct i2c_adapter *adapter,
          struct i2c_client_address_data *address_data,
          int (*found_proc) (struct i2c_adapter *, int, int))
{
    int i, err;
    int adap_id = i2c_adapter_id(adapter);
    /* Force entries are done first, and are not affected by ignore
       entries */
//为空,不执行
    if (address_data->forces) {
        unsigned short **forces = address_data->forces;
        int kind;
        ; forces[kind]; kind++) {
            ; forces[kind][i] != I2C_CLIENT_END;
                 i += ) {
                if (forces[kind][i] == adap_id
                 || forces[kind][i] == ANY_I2C_BUS) {
                    dev_dbg(&adapter->dev, "found force "
                        "parameter for adapter %d, "
                        "addr 0x%02x, kind %d\n",
                        adap_id, forces[kind][i + ],
                        kind);
                    err = i2c_probe_address(adapter,
                        forces[kind][i + ],
                        kind, found_proc);
                    if (err)
                        return err;
                }
            }
        }
    }
    /* Stop here if we can't use SMBUS_QUICK */
    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
        ] == I2C_CLIENT_END
         && address_data->normal_i2c[] == I2C_CLIENT_END)
            ;
        dev_warn(&adapter->dev, "SMBus Quick command not supported, "
             "can't probe for chips\n");
        ;
    }
    /* Probe entries are done second, and are not affected by ignore
       entries either */
    ; address_data->probe[i] != I2C_CLIENT_END; i += ) {
        if (address_data->probe[i] == adap_id
         || address_data->probe[i] == ANY_I2C_BUS) {
            dev_dbg(&adapter->dev, "found probe parameter for "
                "adapter %d, addr 0x%02x\n", adap_id,
                address_data->probe[i + ]);
            err = i2c_probe_address(adapter,
                        address_data->probe[i + ],
                        -, found_proc);
            if (err)
                return err;
        }
    }
    /* Normal entries are done last, unless shadowed by an ignore entry */
//执行这部!通过i2c_probe_address函数来回调你编写的pcf8563_probe加载函数。
    ; address_data->normal_i2c[i] != I2C_CLIENT_END; i += ) {
        int j, ignore;
        ignore = ;
        ; address_data->ignore[j] != I2C_CLIENT_END;
             j += ) {
            if ((address_data->ignore[j] == adap_id ||
                 address_data->ignore[j] == ANY_I2C_BUS)
             && address_data->ignore[j + ]
                == address_data->normal_i2c[i]) {
                dev_printk(KERN_ERR ,&adapter->dev, "found ignore "
                    "parameter for adapter %d, "
                    "addr 0x%02x\n", adap_id,
                    address_data->ignore[j + ]);
                ignore = ;
                break;
            }
        }
        if (ignore)
            continue;
        dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
            "addr 0x%02x\n", adap_id,
            address_data->normal_i2c[i]);
        err = i2c_probe_address(adapter, address_data->normal_i2c[i],
                    -, found_proc);
        if (err)
            return err;
    }
    ;
}
i2c_probe_address的原型存在于i2c-core.c文件中:
static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,
                 int (*found_proc) (struct i2c_adapter *, int, int))
{
    int err;
    /* Make sure the address is valid */
    if (addr < 0x03 || addr > 0x77) {
        dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
             addr);
        return -EINVAL;
    }
    /* Skip if already in use */
    if (i2c_check_addr(adapter, addr))
        ;
    /* Make sure there is something at this address, unless forced */
    ) {
        , , ,
                   I2C_SMBUS_QUICK, NULL) < )
            ;
        /* prevent 24RF08 corruption */
        if ((addr & ~0x0f) == 0x50)
            i2c_smbus_xfer(adapter, addr, , , ,
                       I2C_SMBUS_QUICK, NULL);
    }
    /* Finally call the custom detection function */
//这里回调你写的适配器加载函数pcf8563_probe,完成一个iic适配器的加载
    err = found_proc(adapter, addr, kind);
    /* -ENODEV can be returned if there is a chip at the given address
       but it isn't supported by this chip driver. We catch it here as
       this isn't an error. */
    if (err == -ENODEV)
        err = ;
    if (err)
        dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n",
             addr, err);
    return err;
}
RTC设备驱动的更多相关文章
- 【Linux-驱动】RTC设备驱动架构
		在Linux操作系统中,RTC设备驱动的架构如下图所示: RTC设备驱动涉及的文件:class.c.rtc-dev.c : 建立/dev/rtc0设备,同时注册相应的操作函数.interface.c ... 
- Linux RTC设备驱动
		1. 在Linux2.6.29内核中,RTC是以平台设备的方式注册进内核的. ① RTC驱动定义于文件:drivers/rtc/rtc-s3c.c static struct platform_dri ... 
- linux设备驱动的分层设计思想--input子系统及RTC
		转自:linux设备驱动的分层设计思想 宋宝华 http://blog.csdn.net/21cnbao/article/details/5615493 1.1 设备驱动核心层和例化 在面向对象的程序 ... 
- Linux设备驱动中的软件架构思想
		目录 更新记录 一.Linux驱动的软件架构 1.1 出发点 1.2 分离思想 1.3 分层思想 二.platform设备驱动 2.1 platform设备 2.2 platform驱动 2.3 pl ... 
- S3C2440上RTC时钟驱动开发实例讲解(转载)
		嵌入式Linux之我行,主要讲述和总结了本人在学习嵌入式linux中的每个步骤.一为总结经验,二希望能给想入门嵌入式Linux的朋友提供方便.如有错误之处,谢请指正. 共享资源,欢迎转载:http:/ ... 
- i2c设备驱动移植笔记(二)
		说明:上一篇博客写了我在移植android驱动之TEF6606的苦逼遭遇,即驱动层向应用层提供接口支持,查找了两天的资料,不得不放弃,转而进行IIC下移植RTC设备的实验. 第一步:查找设备的数据手册 ... 
- linux设备驱动归纳总结(十):1.udev&misc【转】
		本文转载自:http://blog.chinaunix.net/uid-25014876-id-111839.html linux设备驱动归纳总结(十):1.udev&misc xxxxxxx ... 
- linux设备驱动归纳总结(七):1.时间管理与内核延时【转】
		本文转载自:http://blog.chinaunix.net/uid-25014876-id-100005.html linux设备驱动归纳总结(七):1.时间管理与内核延时 xxxxxxxxxxx ... 
- linux设备驱动归纳总结(三):1.字符型设备之设备申请【转】
		本文转载自:http://blog.chinaunix.net/uid-25014876-id-59416.html linux设备驱动归纳总结(三):1.字符型设备之设备申请 操作系统:Ubunru ... 
随机推荐
- 【BZOJ】1016: [JSOI2008]最小生成树计数
			题解 考虑kruskal 我们都是从边权最小的边开始取,然后连在一起 那我们选出边权最小的一堆边,然后这个图就分成了很多联通块,把每个联通块内部用矩阵树定理算一下生成树个数,再把联通块缩成一个大点,重 ... 
- 轻松实现Ecshop商城多语言切换
			很多人都想让自己的ECSHOP商城实现多语言支持(能够方便的在首页切换多语言).其实实现起来也挺简单的. 效果图如下: 下面就说一下修改方法. 1).首先打开 includds/init.php 文 ... 
- C#中的特性 (Attribute) 入门 (二)
			C#中的特性 (Attribute) 入门 (二) 接下来我们要自己定义我们自己的特性,通过我们自己定义的特性来描述我们的代码. 自定义特性 所有的自定义特性都应该继承或者间接的继承自Attribut ... 
- android 消息机制,handler机制,messageQueue,looper
			韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha handler 就是 处理器 . 用来处理消息, 发送消息. handler 就 ... 
- android 数据存储方式
			韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha 1,文件 2,内容提供者 3,偏好设置 4,数据库 5,网络存储. 网络存储,就是上传到 ... 
- BlocksKit(2)-DynamicDelegate
			BlocksKit(2)-DynamicDelegate 动态代理可以说是这个Block里面最精彩的一部分了,可以通过自己给一个类的的协议方法指定对应的block来实现让这个协议的回调都直接在bloc ... 
- 命令神器:lsof 常用
			lsof -i 显示所有网络连接lsof -i 6 获取IPv6信息lsof -itcp 显示tcp连接lsof -i:80 显示指定端口信息lsof -i@172.12.5.6 显示指定ip连接ls ... 
- UVALive 6886 Golf Bot FFT
			Golf Bot 题目连接: http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=129724 Description Do ... 
- Hihocoder #1081 最短路径一 dijkstra
			#1081 : 最短路径·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 万圣节的早上,小Hi和小Ho在经历了一个小时的争论后,终于决定了如何度过这样有意义的一天—— ... 
- flask 中 session的源码解析
			1.首先请求上下文和应用上下文中已经知道session是一个LocalProxy()对象 2.然后需要了解整个请求流程, 3.客户端的请求进来时,会调用app.wsgi_app(),于此此时,会生成一 ... 
