Linux内核i2c子系统驱动介绍 (linux内核i2c子系统驱动)

I2C(Inter-Integrated Circuit)总线是一种串行数据传输协议,被用于连接芯片之间的通信。在Linux内核中,i2c子系统驱动负责管理I2C总线,允许多个设备通过同一条总线进行通信。本文将介绍Linux内核i2c子系统驱动的相关知识。

一、i2c子系统驱动的结构

在Linux内核中,i2c子系统驱动通常包含以下几个结构:

1. i2c_adapter结构体

i2c_adapter结构体描述了一条I2C总线的特性,包括总线地址、时钟频率、数据传送方式等。该结构体的定义在i2c.h头文件中,如下所示:

struct i2c_adapter {

struct module *owner;

const char *name;

u32 class;

u32 nr;

struct bus_type *bus;

u32 algo_data;

struct i2c_algorithm *algo;

void *algo_priv;

struct device dev;

unsigned long algo_data_dma;

unsigned long owndata;

unsigned int timeout;

unsigned int retries;

bool (*unrecoverable)(struct i2c_adapter *, struct i2c_msg *);

int (*setup)(struct i2c_adapter *);

void (*shutdown)(struct i2c_adapter *);

int (*bus_recovery)(struct i2c_adapter *);

void *adapdata;

struct i2c_lock_operations *lock_ops;

const struct of_device_id *of_match_table;

};

2. i2c_algorithm结构体

i2c_algorithm结构体定义了I2C总线中的通信算法,包括数据传输格式、地址范围等。该结构体的定义在i2c.h头文件中,如下所示:

struct i2c_algorithm {

int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,

int num);

int (*bus_xfer)(struct i2c_adapter *adapter, u16 addr,

unsigned short flags, char read_write,

u8 command, int size, union i2c_bus_data *data);

int (*master_recv)(struct i2c_adapter *adap, struct i2c_msg *msgs,

int num);

int (*master_send)(struct i2c_adapter *adap, struct i2c_msg *msgs,

int num);

u32 (*functionality)(struct i2c_adapter *);

struct module *owner;

const char *name;

};

3. i2c_driver结构体

i2c_driver结构体描述了一个i2c设备的特性,包括设备类型、ID等信息。该结构体的定义在i2c.h头文件中,如下所示:

struct i2c_driver {

unsigned int class;

const struct i2c_device_id *id_table;

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 state);

int (*resume)(struct i2c_client *);

const struct i2c_device_id *of_match_table;

const struct of_device_id *of_match_table;

struct device_driver driver;

};

4. i2c_client结构体

i2c_client结构体描述了一个具体的i2c设备,包括设备地址、设备类型、驱动等信息。该结构体的定义在i2c.h头文件中,如下所示:

struct i2c_client {

int irq;

unsigned short flags;

unsigned short addr;

char name[I2C_NAME_SIZE];

struct device dev;

struct i2c_adapter *adapter;

struct i2c_driver *driver;

void *algo_data;

struct i2c_board_info const *board_info;

};

二、i2c子系统驱动的通信过程

i2c子系统驱动的通信过程需要经过以下步骤:

1. 注册i2c_adapter

初次使用I2C总线时,需要先注册i2c_adapter结构体,告诉内核I2C总线的特性。在Linux内核中,注册i2c_adapter结构体可以使用i2c_add_adapter函数来完成。

2. 注册i2c_driver

为了能够与具体的i2c设备通信,需要先注册i2c_driver结构体。在Linux内核中,注册i2c_driver结构体可以使用i2c_add_driver函数来完成。

3. 连接i2c_client设备

一旦I2C总线被注册成功,可以使用i2c_new_device函数连接一个i2c_client设备。在连接设备时,需要指定设备地址、设备类型等信息。

4. 与设备通信

当i2c_client设备连接成功后,就可以使用该设备进行I2C通信了。在Linux内核中,I2C数据的传输通常使用i2c_transfer函数进行。该函数需要传入一个指向i2c_msg结构体的指针,该结构体用于描述数据的传输信息。

5. 注销i2c_driver和i2c_adapter

当使用完i2c_driver和i2c_adapter之后,需要使用i2c_del_driver和i2c_del_adapter函数来注销。

三、i2c子系统驱动的应用场景

i2c子系统驱动在Linux内核中的应用广泛,包括但不限于以下场景:

1. 屏幕显示

在Linux内核中,i2c子系统驱动可用于控制显示器的点阵屏幕,比如树莓派的OLED屏幕等。

2. 触摸屏

i2c子系统驱动还可用于连接触摸屏设备,实现触摸控制。

3. 传感器

i2c子系统驱动也可以用于连接各种传感器设备,如温度传感器、气压传感器等。这些设备通常需要I2C总线进行数据传输。

i2c子系统驱动在Linux内核中的应用范围广泛,是连接各种设备的重要手段。通过对i2c子系统驱动的深入了解,可以更好地开发和使用相关设备。

相关问题拓展阅读:

求教怎么学习linux内核驱动

1. 分享Linux内核学习和驱动开发的经验。

内核逗戚学习

Linux 内核功能越来越完善,如果没有充裕的时间,深入内核并不是很现实。所以建议先读一本内核的书,

之一遍是读,会读的很迷糊;之后反省一下,然后再浏览一下;可以想象一个 OS 是如何运行的,这样可以不

陷入 Linux 内核的细节;最后可以深入自己感兴趣或者需要的那一子系统

推荐 《Linux Kernel Development》

即便是子系统,也是很庞大的。一个省力的方式是网上搜一些相关的文章,便于快速了解这个子系统的运作;

然后结合代码,形成自己的认知,最后做一下总结。如果仅仅是快速了解某一子系统的运作,可以参考一些早期

代码的注解书籍,再深入的时候看看最新的代码实现

对内核的认知是一个反复的过程,一开始并不完善,可能需要反复纠正。不要陷入这种纠错中;而是以后继续

使用和学习过程中,发现了没有弄清楚的地方再深入,毕竟 Linux 内核是不断变化的

还有一个很好的方式是,从系统调用入手,现在这方面的数据不少,而且对系统调用的语义都有讲解,这样可以

间接了解 Linux 系统的一些概念。对系统调用熟悉了,可以根据系统调用的执行过程,来大体了解内核的一个

运作过程;但是跟踪系统调用的时候要注意抓主线,现在内核系统很复杂,一些 code path 上可能会涉及多个

子系统,可以从名字上猜测它们是干什么的,不需要深入,否则会发现精力完全被分散掉了

学习 Linux 内核,一个很重要的是抽象的能力,所谓的抽象这里仅仅是指分清接口和接口的实现。因为 Linux

内核子系统很多,有很多子系统相互渗透,这样 code path 看上去很复杂。阅读代码的时候,为了排除干扰,

需要分清哪些是自己需要看的,哪些是其它子系统的接口,对于其它子系统的接口,先当作它们功能完善不会

出问题好了,这样可以关注重点;打个比方,一个应用程序的代码可能量很大,比如一个 apache 项目,它

包含很多组件,有时候阅读代码的时候会看到不同组件的 API,深入看相关组件实现并不现实,这时候分清主次

对于代碰孝码的阅读就很有帮助了,总不能看到了 malloc 就要先把它的实现弄清楚吧,系统调用多者呢

推荐书籍

OReilly.Linux.System.Programming.2nd.Edition

The.Linux.Programming.Interface

驱动开发

一直围绕服务器做,接触的比较多的是网卡驱动。最开始想着从上到下,好好学习协议栈,后来发现内容太多,

进展太慢。后来参考一些驱动开发方面的书籍,把驱动独立开,使用内核提供的接口,就类似写应用的时候很多

情况下只需要了解系统接口和库函数的原型描述而不需要继续深入一样。这样把自己从内核复杂的实现细节中解

放出来,可以重点看网卡的特性部分;之后可以再深入了解设备的运作过程,比如网卡的收发包在协议栈中的

位置和运作

个人感觉如果工作中能接触驱动开发更好,否则很多情况下,有的设备并不常见,比如 Infiniband 卡

现在民用设备越来越广泛,可以选择自己感兴山吵陵趣而手头又容易有的设备进行研究,比如无线网卡、wifi 等

驱动开发,一定要先专注一个设备,从头到尾熟悉一遍,然后总结驱动开发是怎样的、驱动是如何关联到系统中

的、Linux 采用什么样的分层模式来提供对多种多样设备的支持,如类似 VFS 一样的抽象分层

推荐

linux device driver, 3rd edition

Linux设备驱动开发详解

2. 您觉得Linux驱动开发的难点是什么,有什么好的方法来克服?

Linux 内核对各种设备的驱动开发提供了完善的框架支持,对应某个驱动,把对外的接口弄清楚就可以了。打个

比方,一个设备可能在不同的 OS 上需要支持,比如 FreeBSD/Windows 等,每个 OS 都有自己定义的接口,

设备的驱动定义好与这些 OS 接口的连接,剩下的就是设备本身的特性管理以及驱动接口中对设备管理函数的调

用了,比如寄存器访问、配置管理、缓冲区管理、数据收发等,比较重要的中断和同步的控制,要避免数据处理的

时候的死锁。

比如网卡驱动,基本的要求是提供内核需要的接口,这样网卡驱动才能挂接到系统中,剩下的就是接口需要调用

网卡驱动的内部函数,来对网卡进行控制、数据收发和管理等

Linux 支持的设备种类繁多,不可能所有都掌握,某一子系统也只能是熟悉,因为同类设备还有许多自由的特性。

写驱动的步骤可以概括为:

) 阅读设备规范,对设备的运行机理有所了解

为了减少干扰,不考虑要支持的 OS,独立与 OS 考虑基本的功能如何实现

) 参考同类设备在 Linux 内核中的驱动架构

) 提供基本的 Linux 设备驱动接口和实现设备的基本功能,比如网卡收发小数据量

) 在性能上逐步提示,比如网卡传输的数据量加大、中断及时处理、避免死锁等

) 对边界条件进行完善,网卡上就是对一些特殊大小的数据包传输完善等

) 对设备进行更高级控制的支持,比如网卡支持 ethtool 等工具

) 反复调试、改进和优化

3. Linux内核有上百个驱动子系统,你研究过内核各种驱动子系统的共性,层次结构设计吗?分享学习一个Linux内核子系统的经验,例如USB、I2C、HID等driver。

每一个子系统都巨大无比,而且涉及各种硬件规范,很难去搞明白所有。只能是遇到问题的时候,

能对某一部分深入下去。之前了解过 SCSI 的架构,最上层的抽象,中间层的桥梁,更底层的设备

驱动控制

如果仅仅是做 driver 的工作,可以把精力放在设备特性上,Linux 内核部分只需要了解驱动

接口和同步、内存管理等基本功能

4. 您深入研究过Linux设备驱动模型,关注过Linux内核驱动的设计思想吗?

Linux 设备驱动模型是从分类的角度来看待设备,分类是多维的,所以 /sys 下也是多个目录

另外,设备驱动模型给出了系统中设备布局信息,比如根据总线地址可以定位对应的设备目录等

Linux 内核驱动可以都是遵循一个逐层抽象的架构:

最上层的抽象层便于系统软件的访问,

中间层的实现硬件协议细节,同时提供上下两层连接的接口,

对于最下层的 driver 来说就是要定义底层驱动要实现的接口和实际的设备控制

由于 Linux 内核各类驱动的框架支持,driver 可以更加关注设备本身的特性

5. Android系统是基于Linux内核的,传统的Linux驱动开发和Android驱动开发有什么区别?

传统的 Linux 内核驱动开发,只需要定义好 Linux 需要的驱动接口就好了,之后

专注与设备本身的特性

而 Android 驱动开发,需要将设备操作接口继续封装,提供上层使用的 Java API;

driver 部分可以使用 C 代码,一些更复杂的操作可以用 Java 实现,然后 driver

提供对复杂操作的接口支持

一个很重要的方面是功能的分离定义,比如哪些是 driver 实现的,哪些是上层实现的,

它们之间的接口是怎样的,定义好这些,就可以按照传统 driver 的实现方式来做了;

从某种程度上,可以看作是传统 driver 跟相关 tools 转换成了 Android driver

和上层 Java 接口

1.首先要了解为什么要学习内核?下图已表明,如果要从事驱动开发或系统研究,就要学习内核。

2.内核的知识就像下面的绳结一样,一环余或携扣一环,我们要解开它们,就必须要先找到线头也就是内核中的函数竖伏接口。初学阶段,我们一般不深入的研究内核代码,会使用内核的接口函数就不错了。

3.下面提供了如何学习这些内核函数的方法,就像解绳子一样

4.学习内核的四步法则,思维导图的设计尤为团清重要,这也是能否学习好内核的关键

5.语言基础也需要扎实,所以需要把C语言巩固巩固

在linux上怎样增加一个i2c设备?

假设手上有一块从淘宝上买来的开发板,我要在开发板的I2C总线上增加一个从设备(如at24c08),那么我要怎样写这晌答个“I2C设备驱动”,让

应用程序可以访问at24c08呢?

先来看一个最简单的i2c设备驱动:

static struct i2c_board_info at24cxx_info = { //所支持的i2c设备的列表

I2C_BOARD_INFO(“at24c08”, 0x50), //一项代表一个支持的设备,它的名字叫做“at24c08”,器件地址是0x50

};

static struct i2c_client *at24cxx_client;

static int at24cxx_dev_init(void)

{

struct i2c_adapter *i2c_adap; //分配一个适配器的指针

i2c_adap = i2c_get_adapter(0); //调用core层的函数,获得一个i2c总线。这里我们已经知道新增的器件挂接在编号为0的i2c总线上

at24cxx_client = i2c_new_device(i2c_adap, &at24cxx_info); // 把i2c适配器和新增的I2C器件关联起来,这个用了i2c总线0,地址是0x50。这就组成了一个客户端

at24cxx_client i2c_put_adapter(i2c_adap);

return 0;

}

static void at24cxx_dev_exit(void)

{

i2c_unregister_device(at24cxx_client);

}

module_init(at24cxx_dev_init);

module_exit(at24cxx_dev_exit);

从上面的程序可以看到,写一个i2c设备驱动程序,与写普通的字符驱动基本一样。特别之处是它调用了i2c的core层的函数,以获得对i2c总线的控制。因为用的是开发板,板上的与soc芯片(一般来说就是arm的芯片)i2c总线驱动一般都做好了,直接调用core层的函数就可以控制soc的i2c模块了。也就是说,写i2c设备驱动不需要关注arm内部的i2c模块的寄存器,我们需要关注的是设备(at24c08)的寄存器以及猛谨高它的datasheet对时序的要求。

其实,添加i2c设备的方法很灵活。根据Linux的官方文档《linux-3.4.2\Documentation\i2c\instantiating-devices》,添加i2c设备的方法总结有4种:

1. i2c_register_board_info:根据总线编号、设备名字(“at24c08”)、设备地址(0x50)注册一个字符驱动。这种方法最简单、最粗暴,最贴近平时在开片机上开发i2c器件的。

2. i2c_new_device:根据i2c总线的编号,声明一个i2c设备:这种方法就是上面例子用的方法。这种方法也简单,但是需要事先知道器件挂接在哪条总线上。对于设备,还实现知道了设备地址0x50,总线适配器也支持名字为“at24c08”的设备

3. i2c_new_probed_device:

4.从用户空间实例化一个器件:这个方法相当智能快速,如下输入指令,即可增加一个i2c设备,同时增加了对应的设备文件。

# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device

根据英文文档的标题,添加i2c设备有称之为枝尺“i2c设备的实例化”。

从上述可以知道,在实例化一个i2c设备之前,除了有对应的驱动支持总线外(这里是总线0),还需要有一个驱动使用了总线0发送时序,支持名字为”at24c08″的器件。这个驱动用总线驱动的函数,配置了at24c08的寄存器。

linux内核i2c子系统驱动的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux内核i2c子系统驱动,Linux内核i2c子系统驱动介绍,求教怎么学习linux内核驱动,在linux上怎样增加一个i2c设备?的信息别忘了在本站进行查找喔。


数据运维技术 » Linux内核i2c子系统驱动介绍 (linux内核i2c子系统驱动)