56 linux内核里声明I2C设备的方法

来源:互联网 发布:统计师的python日记 编辑:程序博客网 时间:2024/05/18 03:53

I2C控制器驱动好后,由i2c_adapter对象来描述, 我们只需通过函数调用控制器来实现数据传输就可以了。
但i2c控制器上接的i2c设备,需要我们来声明才可以.

在linux内核里,每个i2c_client对象表示一个i2c硬件设备

"include/linux/i2c.h"struct i2c_client {    unsigned short flags;  //没用. 如设备地址为10位,则设I2C_CLIENT_TEN    unsigned short addr;   //i2c设备地址    char name[I2C_NAME_SIZE]; //设备的名字    struct i2c_adapter *adapter;   //此指针指向此设备在具体i2c控制器的i2c_adapter对象    struct i2c_driver *driver;  //指向匹配上的i2c设备驱动    struct device dev;      //基于device结构体扩展面来, 驱动模型. 也可以用dev.platform_data来给设备驱动提供硬件资源    int irq;            //中断号    struct list_head detected;};

但比较坑的事情是通常情况下不会直接用i2c_client结构体声明对象来描述设备,因描述设备时它的adapter成员根本没法指定具体的i2c_adapter对象的地址.所以内核里的做法是,先用struct i2c_board_info来描述设备, 等控制器驱动好后,也就是i2c_adapter对象都搞好后,再根据i2c_board_info对象的内容生成i2c_client对象,并指定i2c_client对象的adapter指向对应的i2c_adapter对象地址.

struct i2c_board_info {    char        type[I2C_NAME_SIZE]; //就是i2c_client的name    unsigned short  flags;  // i2c_client的flags    unsigned short  addr;   // i2c_client的addr    void        *platform_data; // i2c_client的dev.platform_data    struct dev_archdata *archdata;    struct device_node *of_node; //用于设备树    int     irq;  // i2c_client的irq};

///////////////////////// i2c设备的描述方法////////////////
可参考内核源码目录下Documentation/i2c/instantiating-devices文档

1) 通过指定控制器号来声明i2c设备(最常用的方法)

    先声明一个i2c_board_info对象表示一个i2c硬件设备.    struct i2c_board_info info = {        I2C_BOARD_INFO("24c02", 0x50),  //名为24c02, 设备地址为0x50        .platform_data  = NULL,    };    如此i2c设备是接着第9个控制器(即i2c_adapter的nr成员值为9):    i2c_register_board_info(9, &info, 1); //注册i2c_board_info对象    ///////////////根据i2c_board_info对象生成i2c_client对象的过程////    int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)    {            ...        for (status = 0; len; len--, info++) {        struct i2c_devinfo  *devinfo; //注册i2c_board_info对象后,再用i2c_devinfo对象来记录i2c_board_info对象的地址        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);            ...        devinfo->busnum = busnum; //指定i2c_board_info对象是用于哪个控制器的        devinfo->board_info = *info;        list_add_tail(&devinfo->list, &__i2c_board_list); //再把i2c_devinfo对象加入链表__i2c_board_list里。 也就是可通过此链表获取出所有的i2c_board_info对象的内容            ...       }    }     //然后查看在什么地址遍历链表__i2c_board_list, 可发现在"drivers/i2c/i2c-core.c"     783 static void i2c_scan_static_board_info(struct i2c_adapter *adapter)     784 {     785     struct i2c_devinfo  *devinfo;            ...        //遍历链表,并根据指定的控制器号调用i2c_new_device创建i2c_client对象     788     list_for_each_entry(devinfo, &__i2c_board_list, list) {     789         if (devinfo->busnum == adapter->nr     790                 && !i2c_new_device(adapter,     791                         &devinfo->board_info))            ...      797 } 506 struct i2c_client * 507 i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) 508 { 509     struct i2c_client   *client; 510     int         status; 511  512     client = kzalloc(sizeof *client, GFP_KERNEL);    ... 516     client->adapter = adap;//指定对应的i2c_adapter对象的地址 517  518     client->dev.platform_data = info->platform_data; 519  520     if (info->archdata) 521         client->dev.archdata = *info->archdata; 522  523     client->flags = info->flags; 524     client->addr = info->addr; 525     client->irq = info->irq; 526  527     strlcpy(client->name, info->type, sizeof(client->name));    ... 542     client->dev.parent = &client->adapter->dev; 543     client->dev.bus = &i2c_bus_type;  // i2c_client对象是挂载到i2c总线下的, 所以每个i2c设备描述好后可在"/sys/bus/i2c/devices"目录下查看到 544     client->dev.type = &i2c_client_type; 545     client->dev.of_node = info->of_node;        ... 551     status = device_register(&client->dev); //注册设备,驱动模型. 552     if (status)    //i2c_scan_static_board_info函数是用于根据i2c_board_info生成i2c_client对象,但这个函数是在i2c控制器注册时被调用的. 所以必须在i2c控制器注册前描述好i2c_board_info并i2c_register_board_info注册好i2c_board_info对象才可以    822 static int i2c_register_adapter(struct i2c_adapter *adap)    823 {           ...    870     if (adap->nr < __i2c_first_dynamic_bus_num)    871         i2c_scan_static_board_info(adap);
///////////////////////////////////
    "arch/arm/mach-sunxi/sun8i.c"    524 MACHINE_START(SUNXI, "sun8i")    525     .atag_offset    = 0x100,    526     .init_machine   = sunxi_dev_init, //这个是板上设备初始化函数, 可以sunxi_dev_init函数里实现i2c_board_info对象的准备    527     .init_early     = sunxi_init_early, //是更前面的初始化函数    //在第9个控制器里增加三个设备    451 struct i2c_board_info infos[] = {    452     {I2C_BOARD_INFO("haha", 0x33)},    453     {I2C_BOARD_INFO("nono", 0x44)},    454     {I2C_BOARD_INFO("what", 0x55)},    455 };    456         457 static void __init sunxi_dev_init(void)    458 {       459     i2c_register_board_info(9, infos, ARRAY_SIZE(infos));        //重编内核镜像好,控制器驱动好后,在"/sys/bus/i2c/device"目录下应用9-0033, 9-0044等子目录的出现    

///////////////////////////////////////////////////////////
2) 直接使用i2c_new_device函数来创建出i2c_client对象

 struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);    //调用此函数需要控制器对应的i2c_adapter对象的地址, 还需要一个i2c_board_info对象        struct i2c_adapter *i2c_adap;    i2c_adap = i2c_get_adapter(2); //可调用此函数获取第2个控制器的i2c_adapter对象的地址        此方法可以写成模块,动态加载    test.c    #include <linux/init.h>    #include <linux/module.h>    #include <linux/i2c.h>    struct i2c_board_info info = {        .type = "myi2c_dev",        .addr = 0x67,    };    struct i2c_client *cli;    static int __init test_init(void)    {        struct i2c_adapter *adap;        adap = i2c_get_adapter(9);        cli = i2c_new_device(adap, &info);        return 0;    }    static void __exit test_exit(void)    {        i2c_unregister_device(cli); //反注册i2c设备    }    module_init(test_init);    module_exit(test_exit);    MODULE_LICENSE("GPL");

///////////////////////////////////////////////////////////
3) 使用i2c_new_probed_device函数来创建存在的i2c设备对象
此方法会让控制器发设备地址,检查应答信号确认设备是否存在,如存在,则创建i2c_client对象

   struct i2c_client *i2c_new_probed_device(struct i2c_adapter *adap,              struct i2c_board_info *info,   unsigned short const *addr_list,              int (*probe)(struct i2c_adapter *, unsigned short addr))    //adap为要检查设备的控制器的i2c_adapter对象地址,  info为创建设备对象时准备的信息,    // addr_list是一个要检查的设备地址数组,以I2C_CLIENT_END结尾. 控制器会按数组里的顺序发出设备地址,直到有应答信号时,创建i2c_client对象,并返回对象的地址.    // probe函数指针为NULL.当为NULL时会使用默认的检查方法
    test.c    #include <linux/init.h>    #include <linux/module.h>    #include <linux/i2c.h>    struct i2c_board_info info = {        .type = "myi2c_dev",        .addr = 0x67,    };    struct i2c_client *cli = NULL;    unsigned short addrs[] = {0x11, 0x22, 0x50, 0x66, 0x77, I2C_CLIENT_END};    static int __init test_init(void)    {        struct i2c_adapter *adap;        adap = i2c_get_adapter(9);        cli = i2c_new_probed_device(adap, &info, addrs, NULL);        if (NULL == cli)            return -ENODEV;        printk("cli->addr = %x\n", cli->addr);        return 0;    }    static void __exit test_exit(void)    {        i2c_unregister_device(cli); //反注册i2c设备    }    module_init(test_init);    module_exit(test_exit);    MODULE_LICENSE("GPL");
   //////////////////////////////////////利用此方法还可以检查出控制器上接的设备,输出存在的设备的地址test.c  
    #include <linux/init.h>    #include <linux/module.h>    #include <linux/i2c.h>    unsigned short addrs[129];    struct i2c_client *cli = NULL;    static int __init test_init(void)    {        struct i2c_adapter *adapter;        struct i2c_board_info myinfo;        int i;        unsigned short *addr;        //准备好从0~128的设备地址        for (i = 0; i < ARRAY_SIZE(addrs); i++)                addrs[i] = i;        addrs[i] = I2C_CLIENT_END;        adapter = i2c_get_adapter(9);         memset(&myinfo, 0, sizeof(myinfo));        strcpy(myinfo.type , "mydev");   //准备i2c_board_info信息        addr = addrs;        while (1)        {            cli = i2c_new_probed_device(adapter, &myinfo, addr, NULL);            if (cli)            {                printk("addr: %x found\n", cli->addr);                addr += cli->addr;                continue;            }            break;        }        return 0;    }    static void __exit test_exit(void)    {        if (cli)            i2c_unregister_device(cli);    }    module_init(test_init);    module_exit(test_exit);    MODULE_LICENSE("GPL");

///////////////////////////////////////////////////////
4) 使用”/sys/bus/i2c/devices/i2c-0/new_device”的属性文件接口

     echo "aaa 0x50" > /sys/bus/i2c/devices/i2c-0/new_device   //这种方法只适用于简单的设备。这类型设备不会有中断号,也不需要提供额外的硬件资源.
原创粉丝点击