《Linux设备驱动开发详解》——I2C核心、总线与设备驱动
来源:互联网 发布:服务器机柜和网络机柜 编辑:程序博客网 时间:2024/05/24 05:39
15.1 LInux 的 I2C体系结构
(1)I2C核心
I2C核心提供:①I2C总线驱动和设备驱动的注册、注销方法
②I2C通信方法(“algorithm”)
③上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码
(2)I2C总线驱动
I2C总线驱动在适配器端实现,适配器由CPU控制,甚至可以集成在CPU内部。
I2C总线驱动主要包含了:①I2C适配器数据结构 i2c_adapter
②I2C适配器的 algorithm 数据结构 i2c_algorithm
③控制适配器产生通信信号的函数
I2C总线驱动控制I2C适配器产生以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。
(3)I2C设备驱动
I2C设备驱动在设备端实现,设备一般挂接在I2C适配器上,通过I2C适配器与CPU交换数据。
主要数据结构:i2c_driver i2c_client
Linux中 I2C驱动的主要文件:
(1)i2c-core.c——实现I2C核心功能以及 /proc/bus/i2c* 接口
(2)i2c-dev.c——I2C设配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适配器访问设备时的主设备号都是89,次设备号为0-255.应用程序通过“i2c-%d”(i2c-0,i2c-1...)文件名使用文件操作接口 open() write() read()等来访问设备。
i2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read(),write(),ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上的I2C设备或寄存器,并控制I2C设备的工作方式。
(3)chips文件夹——包含特定的I2C设备驱动
(4)busses文件夹——包含一些I2C总线驱动
(5)algos文件夹——实现一些I2C总线适配器的algorithm。
/* i2c_adapter 结构体,对应一个i2c适配器,一个i2c适配器必须对应一套通信方法 */struct i2c_adapter { struct module *owner; unsigned int id; /* algorithm 的类型,定义于 i2c-id.h ,以 I2C_ALGO_开始 */ unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* 总线通信方法结构体指针 */ void *algo_data; /* algorithm 数据*/ u8 level; /* nesting level for lockdep */ struct mutex bus_lock; int timeout; /* in jiffies */ int retries; struct device dev; /* the adapter device适配器设备 */ int nr; char name[48]; struct completion dev_released;};
/*i2c_algorithm结构体,产生i2c访问周期需要的信号函数,以msg为单位*/struct i2c_algorithm { int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data); u32 (*functionality) (struct i2c_adapter *);};smbus_xfer对应SMBus传输函数指针,SMBus大部分基于I2C总线规范,SMBus不许增加额外的引脚。与I2C总线相比,SMBus增加了一些新的功能特性,在访问时序也有一定的差异。
/* i2c_driver 结构体 */struct i2c_driver { unsigned int class; int (*attach_adapter)(struct i2c_adapter *); /* 依附i2c_adapter函数指针 */ int (*detach_adapter)(struct i2c_adapter *); /* 脱离i2c_adapter函数指针 */ int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); /* driver model interfaces that don't relate to enumeration */ void (*shutdown)(struct i2c_client *); int (*suspend)(struct i2c_client *, pm_message_t mesg); int (*resume)(struct i2c_client *); int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); struct device_driver driver; const struct i2c_device_id *id_table; /* 驱动所支持的ID表 */ /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *); const struct i2c_client_address_data *address_data; struct list_head clients;};
/*i2c_client结构体,对应真是的物理设备*/struct i2c_client { unsigned short flags; /* 标志 */ unsigned short addr; /* 低7位芯片地址 */ char name[I2C_NAME_SIZE]; /* 设备名称 */ struct i2c_adapter *adapter; /* 依附的i2c_adapter */ struct i2c_driver *driver; /* 依附的i2c_driver */ struct device dev; /* 设备结构体 */ int irq; /* 设备使用的中段号 */ struct list_head detected; /* 链表头 */};
i2c_driver与i2c_client的关系式有一对多,一个i2c_driver可以支持多个同等类型的i2c_client.
i2c_client信息通常在BSP的板文件中通过i2c_board_info填充。
/* i2c_msg ,i2c访问周期的信号 */struct i2c_msg { __u16 addr; /* 设备地址 */ __u16 flags;#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */#define I2C_M_RD 0x0001 /* read data, from slave to master */#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ __u16 len; /* msg length */ __u8 *buf; /* pointer to msg data */};
i2c驱动实现步骤:
15.2 Linux核心
对应 i2c-core.c 文件。
15.3 Linux I2C总线驱动
分析 /drivers/i2c/busses/i2c_s3c2410.c文件15.4 Linux I2C设备驱动
分析 /drivers/i2c/chips目录下文件
15.4.2 Linux I2C设备驱动的数据传输
/* i2c设备驱动数据传输范例 */struct i2c_msg msg[2];/* 第一条消息是写消息 */msg[0].addr = client->addr;msg[0].flags = 0;msg[0].len = 1;msg[0].buf = &ffs;/* 第二条消息是读消息 */msg[1].addr = client->addr;msg[1].flags = I2C_M_RD;msg[1].len = sizeof(buf);msg[1].buf = &buf[0];i2c_transfer(client->adapter, msg, 2);
15.4.3 Linux的i2c-dev.c文件分析
i2c-dev.c实现了一个虚拟的、临时的i2c_client,随着设备文件的打开而产生,并随着设备文件的关闭而销毁,并没有添加到i2c_adapter的clien链表中。i2c-dev.c针对每个i2c适配器生成一个主设备号为89的设备文件,实现了i2c_driver的成员函数以及文件操作的接口,所以i2c-dev.c的主体是“i2c_driver成员函数+字符设备驱动”。
i2c-dev.c中提供 i2cdev_read()、i2cdev_write()函数来对应用户空间要使用的 read() 和 write() 文件操作接口,这两个函数分别调用i2c核心的 i2c_master_recv()和i2c_master_send()函数来构造一条i2c消息并引发适配器 algorithm 通信函数的调用,完成数据的传输。
但是对于大多数的i2c设备读写流程并不对应于一条消息,往往需要两条甚至更多的消息来进行一次读写周期,这种情况,在应用层调用 i2cread() 、i2cwrite() 文件API进行i2c设备读写,将不能正确的读写。
鉴于上述原因,i2c-dev.c中的i2cdev_read()和i2cdev_write()函数不具备太强的通用性,没有太大的实用价值,只能适用于非RepStart模式情况。对于两条以上消息组成的读写,在用户控件需要组织i2c_msg消息数组并调用I2C_RDWR_IOCTL命令。
<span style="color:#000000;">/* i2c-dev.c 中的 i2cdev_ioctl 函数 */static int i2cdev_ioctl(struct inode* inode, struct file* file, unsigned int cmd, unsigned long arg){ struct i2c_client* client = (struct i2c_client *)file->private_data; ... switch( cmd ){ case I2C_SLAVE: case I2C_SLAVE_FORCE: .../* 设置从设备地址 */ case I2C_TENBIT: ... case I2C_PEC: ... case I2C_FUNCS: ... case I2C_RDWR: return i2cdev_ioctl_rdrw(client, arg); case I2C_SMBUS: ... case I2C_RETRIES: ... case I2C_TIMEOUT: ... default: return i2c_control(client, cmd, arg); } return 0;}</span>常用的IOCTL包括I2C_SLAVE(设置从设备地址)、I2C_RETRIES(没有收到设备ACK情况下的重试次数,默认为1)、I2C_TIMEOU(超时)以及I2C_RDWR。
/* 直接通过read()/write()读写i2c设备 */#include <stdio.h>#include <linux/types.h>#include <fcntl.h>#include <unistd.h>#include <sys/types.h>#include <sys/ioctl.h>#include <linux/i2c.h>#include <linux/i2c-dev.h>int main(int argc, char** argv){ unsigned int fd; unsigned short mem_addr; unsigned short size; unsigned short idx;#define BUFF_SIZE 32 char buf[BUFF_SIZE]; char cswap; union{ unsinged short addr; char bytes[2]; }tmp; if( argc < 3 ){ printf("Use:\n%s /dev/i2c-x mem_addr size\n", argv[0]); return 0; } sscanf(argv[2], "%d", &mem_addr); sscanf(argv[3], "%d", &size); if( size > BUFF_SIZE ) size = BUFF_SIZE; fd = open(argv[1], O_RDWR); if( !fd ){ printf("Error on opening the device file\n"); return 0; } ioctl(fd, I2C_SLAVE, 0x50); /* 设置EEPROM地址 */ ioctl(fd, I2C_TIMEOUT, 1); /* 设置超时 */ ioctl(fd, I2C_RETRIES, 1); /* 设置重试次数 */ for( idx = 0; idx < size; ++idx, ++mem_addr ){ tmp.addr = mem_addr; cswap = tmp.bytes[0]; tmp.bytes[0] = tmp.bytes[1]; tmp.bytes[1] = cswap; write(fd, &tmp.addr, 2); read()fd, &buf[idx], 1); } buf[size] = 0; close(fd); printf("Read %d char : %s\n", size, buf); return 0;}
/* 通过O_RDWR IOCATL读写i2c设备 */#include <stdio.h>#include <linux/types.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <sys/types.h>#include <sys/ioctl.h>#include <errno.h>#include <assert.h>#include <string.h>#include <linux/i2c.h>#include <linux/i2c-dev.h>int main(int argc, char** argv){ struct i2c_rdwr_ioctl_data work_queue; unsigned int idx; unsigned int fd; unsigned int slave_address, reg_address; unsigned char val; int i; int ret; if( argc < 4 ){ printf("Usage:\n%s /dev/i2c-x start_addr reg_addr\n", argv[0]); return 0; } fd = open(argv[1], O_RDWR); if( !fd ){ printf("Error on opening the device file\n"); return 0; } sscanf(argv[2], "%x", &slave_address); sscanf(argv[3], "%x", ®_address); work_queue.nmsgs = 2; /* 消息数量 */ work_queue.msgs = (struct i2c_msg*)malloc(work_queue.nmsgs * sizeof(struct i2c_msg)); if( !work_queue.msgs ){ printf("Memory alloc error\n"); close(fd); return 0; } ioctl(fd, I2C_TIMEOUT, 2); /* 设置超时 */ ioctl(fd, I2C_RETRIES, 1); /* 设置重试次数 */ for( i = reg_address; i < reg_address + 16; i++ ){ val = i; (work_queue.msgs[0]).len = 1; (work_queue.msgs[0]).addr = slave_address; (work_queue.msgs[0]).buf = &val; (work_queue.msgs[1]).len = 1; (work_queue.msgs[1]).flags = I2C_M_RD; (work_queue.msgs[1]).addr = slave_address; (work_queue.msgs[1]).buf = &val; ret = ioctl(fd, I2C_RDWR, (unsigned long)&work_queue); if( ret < 0 ) printf("Error during I2C_RDWR ioctl with error code:%d\n", ret); else printf("reg:%02x val:%02x\n", i, val); } close(fd); return;}
- 《Linux设备驱动开发详解》——I2C核心、总线与设备驱动
- 《Linux4.0设备驱动开发详解》笔记--第十五章:Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C核心、总线与设备驱动
- Linux I2C 核心、总线、与设备驱动
- N queens
- asp.net中使用ajax的两种方式
- Java初学札记step by step(一)
- R逻辑回归与CTR预估
- GhostDoc使用与原始注释
- 《Linux设备驱动开发详解》——I2C核心、总线与设备驱动
- [LeetCode]Merge Sorted Array
- 我的CSDN博客世界:欢迎切磋和交流
- HDU 4417 Super Mario
- 【IPV6基础知识】IPV6地址结构
- 1033. To Fill or Not to Fill
- Ubuntu 配置 Tomcat与日常错误解决
- android学习笔记(2.1)--7.20--(5中布局方式+android单位)
- Ubuntu字典app开发(二)——探索与实践