1. I2C对外API

  2. I2C client的注册

    1. i2c_register_board_info具体实现

    2. i2c_new_device

  3. I2C driver

    1. 关于I2C设备驱动的小总结

  4. I2C adapter的注册

    1. 使用场景

在(一)中简述了Linux I2C子系统的三个主要成员i2c_adapter、i2c_driver、i2c_client。三者的关系也在上一节进行了描述。应该已经算是对Linux I2C子系统有了初步的了解。下面再对他们之间的关系进行代码层的深入分析,我认为对他们的关系了解的越好,越有助于I2C设备的驱动开发及调试。

带着问题去分析可能会更有帮助吧,通过对(一)的了解后,可能会产生以下的几点疑问:

  • i2c_adapter驱动如何添加?

  • i2c_client与i2c_board_info究竟是什么关系?

I2C对外API

在解答问题前,不妨先缕顺一下Linux内核的I2C子系统对驱动模块的API有哪些。(来自https://www.kernel.org/doc/htmldocs/device-drivers/i2c.html)

// 对外数据结构
struct i2c_driver — 代表一个I2C设备驱动
struct i2c_client — 代表一个I2C从设备
struct i2c_board_info — 从设备创建的模版
I2C_BOARD_INFO — 创建I2C设备的宏,包含名字和地址
struct i2c_algorithm — 代表I2C传输方法
struct i2c_bus_recovery_info — I2C总线恢复信息?内核新加入的结构,不是很清楚。
//对外函数操作
module_i2c_driver — 注册I2C设备驱动的宏定义
i2c_register_board_info — 静态声明(注册)I2C设备,可多个
i2c_verify_client — 如果设备是i2c_client的dev成员则返回其父指针,否则返回NULL。用来校验设备是否为I2C设备
i2c_lock_adapter — I2C总线持锁操作,会找到最根源的那个i2c_adapter。说明你的模块必须符合GPL协议才可以使用这个接口。后边以GPL代表。
i2c_unlock_adapter — 上一个的反操作,GPL
i2c_new_device — 由i2c_board_info信息声明一个i2c设备(client),GPL
i2c_unregister_device — 上一个的反操作,GPL。
i2c_new_dummy — 声明一个名为dummy(指定地址)的I2C设备,GPL
i2c_verify_adapter — 验证是否是i2c_adapter
i2c_add_adapter — 声明I2C适配器,系统动态分配总线号。
i2c_add_numbered_adapter — 同样是声明I2C适配器,但是指定了总线号,GPL
i2c_del_adapter — 卸载I2C适配器
i2c_del_driver — 卸载I2C设备驱动
i2c_use_client — i2c_client引用数+1
i2c_release_client — i2c_client引用数-1
__i2c_transfer — 没有自动持锁(adapter lock)的I2C传输接口
i2c_transfer — 自动持锁的I2C传输接口
i2c_master_send — 单条消息发送
i2c_master_recv — 单条消息接收
i2c_smbus_read_byte — SMBus “receive byte” protocol
i2c_smbus_write_byte — SMBus “send byte” protocol
i2c_smbus_read_byte_data — SMBus “read byte” protocol
i2c_smbus_write_byte_data — SMBus “write byte” protocol
i2c_smbus_read_word_data — SMBus “read word” protocol
i2c_smbus_write_word_data — SMBus “write word” protocol
i2c_smbus_read_block_data — SMBus “block read” protocol
i2c_smbus_write_block_data — SMBus “block write” protocol
i2c_smbus_xfer — execute SMBus protocol operations

(一)中对几个基本的结构体和宏定义也有了大概的解释,相信结合I2C的理论基础不难理解。对以上一些I2C的API进行分类:

No. Adapter Driver Device(client) Transfer
1 i2c_add_adapter module_i2c_driver i2c_register_board_info __i2c_transfer
2 i2c_add_numbered_adapter i2c_del_driver i2c_new_device i2c_transfer
3 i2c_del_adapter i2c_new_dummy i2c_master_send
4 i2c_lock_adapter i2c_verify_client i2c_master_recv
5 i2c_unlock_adapter i2c_unregister_device i2c_smbus_read_byte
6 i2c_verify_adapter i2c_use_client i2c_smbus_write_byte
7 i2c_release_client i2c_smbus_read_byte_data
8 i2c_smbus_write_byte_data
9 i2c_smbus_read_word_data
10 i2c_smbus_write_word_data
11 i2c_smbus_read_block_data
12 i2c_smbus_write_block_data
13 i2c_smbus_xfer

经过一个表格的整理,不难发现在Linux I2C子系统中,最重要的要数i2c_client,而最多样化的就是数据的传输。

为了更好的理解和衔接,我想也许倒着分析会更有帮助,而这里先暂且不讨论I2C传输过程中的细节。下边的顺序是由client到driver,再到adapter。

I2C client的注册

i2c_client即I2C设备的注册接口有三个:

i2c_register_board_info
i2c_new_device
i2c_new_dummy

而i2c_new_dummy在内部其实也就是将client的name指定为dummy后依旧执行的是i2c_new_device,所以就只分析前两个就可以了。首先看这两个函数的原型:

i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)

busnum 通过总线号指定这个(些)设备属于哪个总线
info i2c设备的数组集合 i2c_board_info格式
len 数组个数ARRAY_SIZE(info)

i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)

adap 此设备所依附的I2C适配器指针
info 此设备描述,i2c_board_info格式,bus_num成员是被忽略的

i2c_register_board_info具体实现
int __init
i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len)
{int status;down_write(&__i2c_board_lock);  //i2c设备信息读写锁,锁写操作,其他只读/* dynamic bus numbers will be assigned after the last static one */if (busnum >= __i2c_first_dynamic_bus_num)  //与动态分配的总线号相关,动态分配的总线号应该是从已经现有最大总线号基础上+1的,这样能够保证动态分配出的总线号与板级总线号不会产生冲突__i2c_first_dynamic_bus_num = busnum + 1;for (status = 0; len; len--, info++) {  //处理info数组中每个成员struct i2c_devinfo    *devinfo;devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);if (!devinfo) {pr_debug("i2c-core: can't register boardinfo!\n");status = -ENOMEM;break;}devinfo->busnum = busnum;  //组装总线号devinfo->board_info = *info;  //组装设备信息list_add_tail(&devinfo->list, &__i2c_board_list);  //加入到__i2c_board_list链表中(尾部)}up_write(&__i2c_board_lock);  //释放读锁,其他可读可写return status;
}

看完后相信都会产生个疑问?怎么将相关信息放到链表中就算完事了吗?不着急,来看下内核中已经给出的解释:

 * Systems using the Linux I2C driver stack can declare tables of board info* while they initialize.  This should be done in board-specific init code* near arch_initcall() time, or equivalent, before any I2C adapter driver is* registered.  For example, mainboard init code could define several devices,* as could the init code for each daughtercard in a board stack.* * The I2C devices will be created later, after the adapter for the relevant* bus has been registered.  After that moment, standard driver model tools* are used to bind "new style" I2C drivers to the devices.  The bus number* for any device declared using this routine is not available for dynamic* allocation.

核心内容就是说关于集成的I2C设备注册过程应该在板级代码初始化期间,也就是arch_initcall前后的时间,或者就在这个时候(board-xxx-yyy.c中),切记切记!!!一定要在I2C适配器驱动注册前完成!!!为什么说是静态注册,是因为真实的I2C设备是在适配器成功注册后才被生成的。如果在I2C适配器注册完后还想要添加I2C设备的话,就要通过新方式!(即i2c_new_device)

小弟永远要挡在老大前边嘛!老大还没出来前,小弟们赶紧前排列阵好,老大到了可不等你,你列阵也没用了。而对于迟到的小弟,自己想办法追上去吧,在原地自己列阵是白费工夫了。

对于__i2c_board_list链表中的信息是如何变成实际的i2c设备信息的过程放在之后adapter注册过程的分析中。记得,重点是i2c_register_board_info方式一定要赶在I2C适配器的注册前,这样就没有问题。

i2c_new_device
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{struct i2c_client    *client;int            status;client = kzalloc(sizeof *client, GFP_KERNEL);  //为即将注册的client申请内存if (!client)return NULL;client->adapter = adap;  //绑定指定的adapter适配器client->dev.platform_data = info->platform_data;  //保存设备数据if (info->archdata)  //代码上看是DMA相关操作数据client->dev.archdata = *info->archdata;client->flags = info->flags;  //类型,(一)中说过,或是10位地址,或是使用SMBus检错client->addr = info->addr;  //设备从地址client->irq = info->irq;  //设备终端strlcpy(client->name, info->type, sizeof(client->name));  //从设备名//瞧!(一)中说过i2c_board_info中的信息是与i2c_client有对应关系的,灵验了吧!/* Check for address validity */status = i2c_check_client_addr_validity(client);  //检测地址是否有效,10位地址是否大于0x3ff,7位地址是否大于0x7f或为0if (status) {  //非零(实际上为-22,无效参数Invalid argumentdev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);goto out_err_silent;}/* Check for address business */status = i2c_check_addr_busy(adap, client->addr);  //检测指定适配器上该地址状态if (status)goto out_err;client->dev.parent = &client->adapter->dev;  //建立从设备与适配器的父子关系client->dev.bus = &i2c_bus_type;client->dev.type = &i2c_client_type;client->dev.of_node = info->of_node;ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);/* For 10-bit clients, add an arbitrary offset to avoid collisions */dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),client->addr | ((client->flags & I2C_CLIENT_TEN)? 0xa000 : 0));  //如果是10位地址设备,那名字格式与7bit的会有不同status = device_register(&client->dev);  //注册了!注册了!!!if (status)goto out_err;dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",client->name, dev_name(&client->dev));return client;out_err:dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x ""(%d)\n", client->name, client->addr, status);
out_err_silent:kfree(client);return NULL;
}

i2d_new_device没什么好多说的,由于有i2c_register_board_info的铺垫,相信也很好理解了。而i2c_new_device不但印证了i2c_client与i2c_board_info的对应关系,还顺便体现了10bit地址设备与7bit地址设备的略微不同。通过两者的对比,可以再总结出几点区别,从而更好的理解I2C设备的注册方法:

  • i2c_register_board_info的形参需要的是总线号

  • i2c_new_device的形参需要的直接是适配器的指针

我想这也正好能完美的说明两者的根本区别,对于板级设备更在乎适配器的总线号,因为这是固定的,没有异议的。而对于可插拔设备,因为其适配器可能非板级集成的,所以不能在乎其总线号,反而只要寻求其适配器指针进行绑定即可。后边adapter注册的分析能更好的证明这一点。

  • i2c_register_board_info可以同时注册多个I2C设备

  • i2c_new_device只能一次注册一个I2C设备

这也是其根本区别决定的,板级代码中常包含有许多I2C设备,所以i2c_register_board_info需要有同时注册多个I2C设备的能力也可以说是刚需。而i2c_new_device既然是用来给可插拔设备用的,想必设备数量并不多,而常可能只是一个两个而已,所以一次一个就够了,需求量并不大。

I2C driver

I2C设备驱动。Linux内核给出的接口只有两个,一个是注册,另一个就是卸载。在(一)也分析过module_i2c_driver这个宏定义,因为有它的存在,I2C设备驱动的开发可以不用在意你的I2C驱动需要如何注册以及如何卸载的,全部的精力都放在i2c_driver的完善上就可以了。

通过最开始的表单能明显察觉到,I2C子系统中I2C driver的开放接口最少,说白了就是需要驱动编写者完成完了i2c_driver放入module_i2c_driver宏中即可,而正因为如此,也恰恰说明,i2c_driver的灵活性是最高的。通常驱动会首先在意在用户空间的打开、关闭、读写等接口,但是对于i2c_driver来说,这些工作是I2C子系统已经做好的,关于常用的读写最终也是通过adapter实现的i2c_algorithm达到目的。好吧,再次说明了I2C子系统的完善程度,对于I2C设备及驱动开发来说是极其方便的。那么I2C驱动要实现什么呢?

再次回顾一下i2c_driver结构体,不过现在要剔除一些不常用的成员:

struct i2c_driver {int (*probe)(struct i2c_client *, const struct i2c_device_id *); //现行通用的与对应设备进行绑定的接口函数int (*remove)(struct i2c_client *);  //现行通用与对应设备进行解绑的接口函数void (*shutdown)(struct i2c_client *);  //关闭设备int (*suspend)(struct i2c_client *, pm_message_t mesg); //挂起设备,与电源管理有关,为省电int (*resume)(struct i2c_client *); //从挂起状态恢复struct device_driver driver;  //I2C设备的驱动模型const struct i2c_device_id *id_table;  //匹配设备列表...
};

如果有可能的话,我还想再精简一下:

struct i2c_driver {int (*probe)(struct i2c_client *, const struct i2c_device_id *); //现行通用的与对应设备进行绑定的接口函数int (*remove)(struct i2c_client *);  //现行通用与对应设备进行解绑的接口函数struct device_driver driver;  //I2C设备的驱动模型const struct i2c_device_id *id_table;  //匹配设备列表...
};

好了,精简到这种程度,为什么把电源管理相关也干掉了呢?实际上没有,通常实际的I2C驱动喜欢在drivers中完成这个动作(以mpu3050为例):

static UNIVERSAL_DEV_PM_OPS(mpu3050_pm, mpu3050_suspend, mpu3050_resume, NULL);static const struct i2c_device_id mpu3050_ids[] = {{ "mpu3050", 0 },{ }
};
MODULE_DEVICE_TABLE(i2c, mpu3050_ids);static const struct of_device_id mpu3050_of_match[] = {{ .compatible = "invn,mpu3050", },{ },
};
MODULE_DEVICE_TABLE(of, mpu3050_of_match);static struct i2c_driver mpu3050_i2c_driver = {.driver    = {.name    = "mpu3050",.owner    = THIS_MODULE,.pm    = &mpu3050_pm,.of_match_table = mpu3050_of_match,},.probe        = mpu3050_probe,.remove        = mpu3050_remove,.id_table    = mpu3050_ids,
};module_i2c_driver(mpu3050_i2c_driver);

可以看到,实际驱动中喜欢将电源管理集成在i2c_driver的driver成员中。

UNIVERSAL_DEV_PM_OPS这个名字很犀利,貌似是“宇宙终极驱动电源管理大法”的样子:

#define UNIVERSAL_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn) \
const struct dev_pm_ops name = { \SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \
}#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \.suspend = suspend_fn, \.resume = resume_fn, \.freeze = suspend_fn, \.thaw = resume_fn, \.poweroff = suspend_fn, \.restore = resume_fn,#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \.runtime_suspend = suspend_fn, \.runtime_resume = resume_fn, \.runtime_idle = idle_fn,

结合MPU3050的驱动将其完整展开可以得到:

static const struct dev_pm_ops mpu3050_pm = {.suspend = mpu3050_suspend,.resume = mpu3050_resume,.freeze = mpu3050_suspend,.thaw = mpu3050_resume,.poweroff = mpu3050_suspend,.restore = mpu3050_resume,.runtime_suspend = mpu3050_suspend,.runtime_resume = mpu3050_resume,.runtime_idle = NULL,
}

对电源管理有兴趣的可以去查阅pm.h,其中对电源管理有详尽的说明了,这里不做分析。可以看到,在电源管理中,有很多成员实际上是一样的,在现实驱动中这样的情况也经常出现,所以会有“终极电源管理大法”宏的出现了。

of_match_table是OpenFirmware相关,在3.0(具体版本本人不清楚)kernel后对arm平台引入了Device Tree,可通过dts配置文件代替大量板级代码,有兴趣可自行查阅。

上边说过,i2c_driver的多样化最多,从mpu3050的驱动注册中也可以发现,其注重实现的为probe与电源管理,其中probe最为重要(好像是废话,哪个驱动这个都是最重要的-。-)。因为主要是从驱动的角度看待I2C子系统,所以这里不详尽分析mpu3050的代码,只以其为例说明I2C驱动大体框架。在mpu3050的probe主要对此传感器进行上电、工作模式初始化、注册INPUT子系统接口、关联中断处理程序(在中断处理线程中上报三轴参数)等工作。

关于I2C设备驱动的小总结

I2C设备驱动通常只是需要挂载在I2C总线(即依附于I2C子系统),I2C子系统对于设备驱动来说只是一个载体、基石。许多设备的主要核心是建立在其他子系统上,如重力传感器、三轴传感器、触摸屏等通常主要工作集中在INPUT子系统中,而相机模块、FM模块、GPS模块大多主要依附于V4L2子系统。这也能通过I2C设计理念证明,I2C的产生正是为了节省外围电路复杂度,让CPU使用有限的IO口挂载更多的外部模块。假设CPU的扩展IO口足够多,我想I2C也没什么必要存在了,毕竟直接操作IO口驱动设备比I2C来的更简单。

I2C adapter的注册

如上表所示,对于I2C adapter的注册有两种途径:i2c_add_adapter 或i2c_add_numbered_adapter,两者的区别是后者在注册时已经指定了此I2C适配器的总线号,而前者的总线号将由系统自动分配。

其各自的声明格式为:

int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)

在i2c_add_numberd_adapter使用前必须制定adap->nr,如果给-1,说明还是叫系统去自动生成总线号的。

使用场景

之所以区分开两种I2C adapter的注册方式,是因为他们的使用场景有所不同。

  • i2c_add_adapter的使用经常是用来注册那些可插拔设备,如USB PCI设备等。主板上的其他模块与其没有直接联系,说白了就是现有模块不在乎新加入的I2C适配器的总线号是多少,因为他们不需要。反而这个可插拔设备上的一些模块会需要其注册成功的适配器指针。回看一开始就分析的i2c_client,会发现不同场景的设备与其匹配的适配器有着这样的对应关系:

      1. i2c_register_board_info需要指定已有的busnum,而i2c_add_numbered_adapter注册前已经指定总线号;2. i2c_new_device需要指定adapter指针,而i2c_add_adapter注册成功后恰好这个指针就有了。

    想象这样一个场景:新设备插入后,对应的驱动程序通过i2c_add_adapter注册自己的I2C适配器,然后根据与小弟们的协定将其是适配器指针存放在某处,相当于对小弟们(依附在其上的I2C设备)说:“看见没?你们注册你们自己的设备的时候就通过这个就能找到我,就能跟我混了!”然后驱动程序继续,当执行到对自己的I2C设备注册时候,小弟们去约定地点找老大留下的记号,发现有效信息后,一拥而上:“看!老大在那!!!”

  • i2c_add_numbered_adapter用来注册CPU自带的I2C适配器,或是集成在主板上的I2C适配器。主板上的其他I2C从设备(client)在注册时候需要这个总线号。

通过简短的代码分析看一看他们的区别究竟如何,以及为什么静态注册的i2c_client必须要在adapter注册前(此处会精简部分代码,只留重要部分):

int i2c_add_adapter(struct i2c_adapter *adapter)
{int    id, res = 0;res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);  //动态获取总线号adapter->nr = id;return i2c_register_adapter(adapter);  //注册adapter
}int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{int    id;int    status;if (adap->nr == -1) /* -1 means dynamically assign bus id */return i2c_add_adapter(adap);status = i2c_register_adapter(adap);return status;
}

可见,最终他们都是通过i2c_register_adapter注册适配器:

static int i2c_register_adapter(struct i2c_adapter *adap)
{int res = 0;/* Can't register until after driver model init */  //时序检查if (unlikely(WARN_ON(!i2c_bus_type.p))) {res = -EAGAIN;goto out_list;}/* Sanity checks */if (unlikely(adap->name[0] == '\0')) {  //防御型代码,检查适配器名称pr_err("i2c-core: Attempt to register an adapter with ""no name!\n");return -EINVAL;}if (unlikely(!adap->algo)) {  //适配器是否已经完成了通信方法的实现pr_err("i2c-core: Attempt to register adapter '%s' with ""no algo!\n", adap->name);return -EINVAL;}rt_mutex_init(&adap->bus_lock);mutex_init(&adap->userspace_clients_lock);INIT_LIST_HEAD(&adap->userspace_clients);/* Set default timeout to 1 second if not already set */if (adap->timeout == 0)adap->timeout = HZ;dev_set_name(&adap->dev, "i2c-%d", adap->nr);adap->dev.bus = &i2c_bus_type;adap->dev.type = &i2c_adapter_type;res = device_register(&adap->dev);  //注册设备节点if (res)goto out_list;/* create pre-declared device nodes */ //创建预-声明的I2C设备节点if (adap->nr < __i2c_first_dynamic_bus_num)  i2c_scan_static_board_info(adap);//如果adapter的总线号小于动态分配的总线号的最小那个,说明是板级adapter。//因为通过i2c_add_adapter加入的适配器所分配的总线号一定是比__i2c_first_dynamic_bus_num大的。...
}

对于i2c_add_numbered_adapter来说会触发i2c_scan_static_board_info:

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{struct i2c_devinfo    *devinfo;down_read(&__i2c_board_lock);  //持有读写锁的读,有用户读的时候不允许写入list_for_each_entry(devinfo, &__i2c_board_list, list) {  //又见__i2c_board_list,这不是通过i2c_register_board_info组建起来的那个链表吗!if (devinfo->busnum == adapter->nr&& !i2c_new_device(adapter,&devinfo->board_info)) //找到总线号与刚注册的这个adapter相同的并通过i2c_new_device进行注册dev_err(&adapter->dev,"Can't create device at 0x%02x\n",devinfo->board_info.addr);}up_read(&__i2c_board_lock);  //释放读写锁
}

而i2c_board_info成员与i2c_client的对应动作也是在i2c_new_device中进行的,这一点在上边已经分析过了。看到这里,对adapter与client的微妙关系应该了解程度就比较深了,为什么说i2c_register_board_info与i2c_add_numbered_adapter对应而不是i2c_add_adapter也可以说得通。

那么,最终回答开篇提出的那两个问题:

  • i2c_adapter驱动如何添加?

板级适配器(CPU自带、主板集成)要通过i2c_add_numbered_adapter注册,注册前要指定总线号,从0开始。假如板级I2C适配器注册了3个,那么第一个动态总线号一定是3,也就是说可插拔设备所带有的I2C适配器需要通过i2c_add_adapter进行注册,其总线号由系统指定。

  • i2c_client与i2c_board_info究竟是什么关系?

i2c_client与i2c_board_info的对应关系在i2c_new_device中有完整体现。

i2c_client->dev.platform_data = i2c_board_info->platform_data;
i2c_client->dev.archdata = i2c_board_info->archdata;
i2c_client->flags = i2c_board_info->flags;
i2c_client->addr = i2c_board_info->addr;
i2c_client->irq = i2c_board_info->irq;

转载于:https://blog.51cto.com/wanpeng/1427746

Linux I2C设备驱动编写(二)相关推荐

  1. 迅为IMX6ULL开发板Linux I2C设备驱动编写流程-信息描述

    1 .不使用设备树文件 当开始编写 I2C 设备驱动时,首先要添加设备信息.先来看一下在不使用设备树,使用平台文件时, 如何在平台文件中添加 I2C 设备信息. 在平台文件中通过 i2c_board_ ...

  2. Linux I2C总线(二)I2C设备驱动编写方法

    Linux I2C总线 Linux I2C总线(一)I2C驱动框架 Linux I2C总线(二)I2C设备驱动编写方法 Linux I2C总线(二)I2C设备驱动编写方法 文章目录 Linux I2C ...

  3. 手把手教你写Linux I2C设备驱动

    手把手教你写Linux I2C设备驱动 标签:Linux 设备 驱动 详解 i2c 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http:/ ...

  4. linux探测i2c设备连接状态,手把手教你写Linux I2C设备驱动

    Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...

  5. 手把手教你写Linux I2C设备驱动 tvp5158

    Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...

  6. linux i2c detect函数,手把手教你写Linux I2C设备驱动

    Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...

  7. Linux添加一个i2c设备,手把手教你写Linux I2C设备驱动

    Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...

  8. linux下i2c设备驱动程序,Linux I2C 设备驱动

    I2C 设备驱动要使用 i2c_driver 和 i2c_client 数据结构并填充其中的成员函数.i2c_client 一般被包含在设备的私有信息结构体yyy_data 中,而 i2c_drive ...

  9. linux 块设备驱动(二)——块设备数据结构

    linux 块设备驱动(二)--块设备数据结构 本文来源于: 1. http://www.cnblogs.com/dyllove98/archive/2013/07/01/3165567.html 块 ...

最新文章

  1. mysql索引设计策略_MySQL索引设计一些策略
  2. MacOS系统下的图形化工具
  3. dubbo源码解析-集群容错架构设计
  4. 2015年第六届蓝桥杯C/C++ B组国赛 —— 第一题:积分之迷
  5. linux cal 命令详解
  6. Bash Shell学习笔记三
  7. 使用Windows 自带防火墙拦截勒索病毒
  8. Python自动运维系列:每天凌晨定时执行特定任务
  9. catia保存成stp文件时部件丢失_在线教学文件同步神器——坚果云
  10. Linux 中的grep命令单引号,不加任何参数以及双引号的作用
  11. git 小乌龟 配置_git 小乌龟安装教程
  12. 这些年看过的书...
  13. jmeter接口压力测试详解
  14. [c#]删除PDF权限密码
  15. 嵌入式字符设备驱动——ULN2003步进电机驱动程序实现
  16. 测试用例---场景法和错误推测法
  17. 获取SSL证书private key私钥文件的步骤
  18. 学大伟业:学长是如何对待数学竞赛的
  19. 文献阅读:利用斯托克斯参量验证光波的偏振态
  20. 【表格合并与底纹】vue-elementul表格简单实现合并单元格,与列和行给底纹颜色

热门文章

  1. jQuery学习之DOM操作
  2. PostgreSQL一些简单问题以及解决办法
  3. visual studio 2012 下配置OPENcv3.1 和CMAKE问题总结
  4. 螃蟹先生2 android,螃蟹先生2
  5. oracle set ansi_nulls off,sqlserver存储过程转换成oracle存储过程
  6. 运行php web文件路径,php的web路径获取
  7. ex.php,Exphp代码走读
  8. python 只用opencv吗,python – OpenCV:使用solvePnP来确定单应性
  9. java音乐网站源码_Vue + SpringBoot + MyBatis 音乐网站
  10. java 485通讯_CAKJ-963U3-KT带485通讯上下限报警智能型仪表