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

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];
    static int __init test_init(void)
    {
        struct i2c_adapter *adapter;
        struct i2c_board_info myinfo;
        struct i2c_client *cli = NULL;
        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;
                i2c_unregister_device(cli);
                continue;
            }
            break;
        }
        return 0;
    }

    static void __exit test_exit(void)
    {
    }

    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

   //这种方法只适用于简单的设备。这类型设备不会有中断号,也不需要提供额外的硬件资源.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值