Linux I2C体系结构分析
来源:互联网 发布:资料员要会什么软件 编辑:程序博客网 时间:2024/05/18 00:32
开发环境:Linux-2.6.32.2内核 Fedora 10虚拟机 gcc4.3.2版本的交叉编译器
一、I2C体系结构
Linux的I2C体系结构分为3个组成部分:I2C核心、I2C总线驱动、I2C设备,如下图所示。I2C核心提供总线驱动和设备驱动的注册、注销方法。它以通用的,与平台无关的接口实现了I2C中设备与适配器的沟通。I2C总线驱动对硬件体系结构中适配器的实现,主要包括适配器i2c_adapter、适配器通信算法i2c_algorithm,如果CPU集成了I2C控制器并且linux内核支持这个CPU,那么总线驱动就不用管,比如S3C2440就属于这类情况,在后文中我们将分析它的总线驱动;I2C设备驱动是具体的一个设备(如AT24C02),挂接在CPU控制的I2C适配器的设备驱动,有了这部分驱动才能使用控制器操作该设备,设备驱动主要包括i2c_driver 和i2c_client数据结构。填充i2c_driver 结构体并实现其本身对应设备类型的驱动。
二、分析I2C核心
linux-2.6.32.2/drivers/i2c/i2c-core.c //i2c子系统的公用代码,驱动开发者只需要用而不需要修改
1. 初始化i2c子系统
static int __init i2c_init(void)//在系统启动模块加载阶段中调用来初始化i2c子系统
1.1 bus_register(&i2c_bus_type);//注册一条IIC总线,注册适配器、IIC设备、IIC设备驱动都会连接到这条总线上
1.2 class_compat_register("i2c-adapter");//注册适配器类,用于实现文件系统的部分功能 (驱动人员不用关心)
1.3 retval = i2c_add_driver(&dummy_driver);//将一个空驱动注册到总线上 (驱动人员不用关心)
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
};
2. 卸载i2c子系统
static void __exit i2c_exit(void)
i2c_del_driver(&dummy_driver);//注销空驱动
bus_unregister(&i2c_bus_type);//注销一条IIC总线
3.函数接口
3.1适配器i2c_adapter的添加、删除
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)//两个接口都是向IIC子系统添加适配器结构体,该结构体在前面要进行分配和初始化
使用:在总线驱动probe()被调用。如:s3c24xx_i2c_probe,先设置adap.name、adap.owner、adap.algo、adap.retries adap.class、adap.algo_data、adap.dev.parent、adap.nr等成员
int i2c_del_adapter(struct i2c_adapter *adap)//删除上面两个接口添加的适配器结构体
3.2增加/删除i2c_driver
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
int i2c_del_driver(struct i2c_driver *driver);
inline int i2c_add_driver(struct i2c_driver *driver);
使用:在设备驱动i2c_dev_init被调用。如i2c-dev.c中的i2c_dev_init()函数。首先要定义一个i2c_driver结构体
static struct i2c_driver i2cdev_driver = { .driver = { .name = "dev_driver", }, .attach_adapter = i2cdev_attach_adapter, .detach_adapter = i2cdev_detach_adapter,};
在i2c_register_driver设置driver->driver.owner、driver->driver.bus
3.3 i2c_client依附/脱离
int i2c_attach_client(struct i2c_client *client);
int i2c_detach_client(struct i2c_client *client);
//当一个具体的client被侦测到并被关联的时候,设备和sysfs文件将被注册。相反地,在client被取消关联的时候,sysfs文件和设备也被注销。
3.4i2c传输、发送和接收
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);
int i2c_master_send(struct i2c_client *client,const char *buf ,int count);
int i2c_master_recv(struct i2c_client *client, char *buf ,int count);
i2c_transfer()函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息。 i2c_transfer()函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter 对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程。(比如:s3c24xx_i2c_xfer)
2.2 IDR机制/include/linux/idr.h
二、分析重要的结构体
linux-2.6.32.2/include/linux/i2c.h
1.struct i2c_msg; 消息结构体,是适配器到IIC设备传输数据的基本单位
struct i2c_msg {
__u16 addr; /* slave address IIC设备地址 */
__u16 flags; /*消息类型标志*/
__u16 len; /* msg length 消息字节长度 */
__u8 *buf; /* pointer to msg data 指向消息数据的缓冲区*/
#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 */ //第一次接收的字节长度
};
//==============Start IIC总线层=========================================
2.struct i2c_algorithm; 描述了适配器与设备之间的通信方法
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
//指向实现IIC总线通信协议的函数
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
//指向实现SMBus总线通信协议的函数,SMBus总线通信协议是基于IIC协议的原理(也是2条总线,时钟和数据)
u32 (*functionality) (struct i2c_adapter *);//确定适配器支持哪些传输类型
};
例子:
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
3.struct i2c_adapter;
IIC总线适配器,即是IIC总线控制器。主要功能是完成IIC总线控制器相关的数据通信。为描述了各种IIC适配器提供了通用的"模版",定义指向具体IIC适配器的总线通信方法i2c_algorithm的指针algo、实现IIC总线操作原子性操作的lock信号量。特定的适配器可在此基础上进行扩充
struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class; /* classes to allow probing for 允许探测的驱动类型 */
const struct i2c_algorithm *algo; /* the algorithm to access the bus 总线通信方法指针*/
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; //通过nr整型数从红黑树得到与之对应的i2c_adapter适配器结构
char name[48]; /*适配器名称*/
struct completion dev_released; /*用于同步*/
};
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
//==================End IIC总线==================================
//==================Start IIC设备层================================
4.struct i2c_client; //IIC设备,一个结构体描述一个真实的物理IIC设备
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit 芯片低7位地址*/
char name[I2C_NAME_SIZE]; /*设备名字*/
struct i2c_adapter *adapter; /* the adapter we sit on 依附i2c_adapter*/
struct i2c_driver *driver; /* and our access routines 依附i2c_driver */
struct device dev; /* the device structure 设备结构体 */
int irq; /* irq issued by device */
struct list_head detected; /*链表头 */
};
#define to_i2c_client(d) container_of(d, struct i2c_client, dev)
5.struct i2c_driver; //IIC设备驱动
每个IIC设备对应一个驱动,即是每个i2c_client对应一个i2c_driver结构,通过包含指针来连接
struct i2c_driver {
unsigned int class; /*驱动类型*/
/*************************************传统函数**********************************/
int (*attach_adapter)(struct i2c_adapter *); /*依附i2c_adapter函数指针*/
int (*detach_adapter)(struct i2c_adapter *); /*脱离i2c_adapter函数指针*/
/**********************************************************************************/
//新旧两种驱动程序函数,只能选择其中一种。新的支持IIC设备的动态插入和拔出,旧的不支持!!!!!!!
/************************************新型函数**************************************/
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 *); /* probe,remove,suspend,resume驱动方法重要成员函数 */
/***********************************************************************************/
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); /*类似ioctl*/
struct device_driver driver; /*指向设备驱动的结构体*/
const struct i2c_device_id *id_table; /* 驱动支持多个设备,这里面就要包含这些设备的ID */
const struct i2c_client_address_data *address_data; / *设备映射到内存的地址范围*/
struct list_head clients; /*将该驱动支持的所有IIC设备连成链表*/
int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *); /*i2c client脱离函数指针*/
};
#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)
//=================End IIC设备层==================================
四个重要结构体的关系:
(1)总线层: i2c_adapter 与i2c_algorithm
i2c_adapter 与i2c_algorithm,i2c_adapter 对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm 的i2c_adapter 什么也做不了,因此i2c_adapter 中包含其使用的i2c_algorithm的指针。i2c_algorithm 中的关键函数master_xfer()用于产生I2C 访问周期需要的信号,以i2c_msg(即I2C消息)为单位。
(2)设备层:i2c_driver 与i2c_client
i2c_driver 与i2c_client,i2c_driver 对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体,主要的成员函数有probe() remove() suspend() resume()等。更外id_table成员是该驱动所支持的IIC设备的ID表。i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。i2c_client一般被包含在I2C字符设备的私有信息结构体中。可以知道i2c_driver 与i2c_client是一对多的关系,即是一个i2c_driver上可以支持多个同类型的 i2c_client。
i2c_driver 与i2c_client发生关联的时刻在i2c_driver的attach_adapter()函数被运行时。attach_adapter()会探测物理设备,当确定一个client存在时,把该client使用的i2c_client数据结构的adapter指针指向对应的i2c_adapter, driver指针指向该i2c_driver,并会调用i2c_adapter的client_register()函数。相反的过程发生在 i2c_driver 的detach_client()函数被调用的时候。
(3)i2c_adpater 与i2c_client
i2c_adpater 与i2c_client 的关系与I2C 硬件体系中适配器和设备的关系一致,即i2c_client 依附i2c_adpater。
//==================End IIC设备层================================
6.union i2c_smbus_data;
#define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */
union i2c_smbus_data {
__u8 byte;
__u16 word;
__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
/* and one more for user-space compatibility */
};
7.struct i2c_board_info; //linux-2.6.32.2/include/linux/i2c.h
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short flags;
unsigned short addr;
void *platform_data;
struct dev_archdata *archdata;
int irq;
};
#define I2C_BOARD_INFO(dev_type, dev_addr) \
.type = dev_type, .addr = (dev_addr)
8.struct i2c_devinfo; //i2c-core.h
struct i2c_devinfo { struct list_head list; int busnum; struct i2c_board_info board_info;};
LIST_HEAD(__i2c_board_list);// 定义并初始化 在/linux-2.6.32.2/drivers/i2c/i2c-boardinfo.cEXPORT_SYMBOL_GPL(__i2c_board_list);
链表__i2c_board_list保存着一些平台信息,和I2C设备地址
9.struct i2c_client_address_data;//linux-2.6.32.2/include/linux/i2c.h
struct i2c_client_address_data { 不懂
const unsigned short *normal_i2c; //该数组指定对每个适配器上的指定地址都进行探测
const unsigned short *probe; //该数组是匹对出现的只对指定适配器的指定地址进行探测,前一个
数是适配器后面是指该适配器的上的一个地址
const unsigned short *ignore;//在进行normal_i2c探测是看看此中是否忽略,若忽略则放弃探测,
其也是成对出现的,前者指适配器后者指地址
const unsigned short * const *forces;//强制使用的地址,确定某个地址代 表 的设备已经连接在总线上了。这个指针指向一个指针数组,每一个成员所指的内容也是每个适配器号后紧跟一个地址
};
四、实例(IIC总线驱动层)
linux-2.6.32.2/drivers/i2c/busses/i2c-s3c2410.c
1、具体的适配器s3c24xx_i2c
struct s3c24xx_i2c {
spinlock_t lock;
wait_queue_head_t wait;
unsigned int suspended:1; //表示设备是否挂起
struct i2c_msg *msg; //从适配器到设备一次传输的单位,用结构体将数据封装起来便于操作
unsigned int msg_num; //消息个数
unsigned int msg_idx; //表示第几个消息,完成后一个消息后自加1
unsigned int msg_ptr; //指向当前要处理的下一个字节,在i2c_msg.buf中的偏移位置
unsigned int tx_setup; //写一个寄存器的延时时间
unsigned int irq;
enum s3c24xx_i2c_state state; //IIC目前的状态
unsigned long clkrate; //时钟速率
void __iomem *regs; //寄存器地址,映射
struct clk *clk; //对应的时钟
struct device *dev; //适配器对应的设备结构体
struct resource *ioarea; //适配器的资源
struct i2c_adapter adap; //适配器主体 模版!!!!
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
};
2、IIC总线通信方法
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)//返回总线支持的协议
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,struct i2c_msg *msgs, int num)
//实现IIC通信协议,将i2c_msg消息传给IIC设备
3、寄存器操作
static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)//判断总线忙闲状态
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,struct i2c_msg *msg)//启动适配器消息传输函数
static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)//适配器传输停止函数
static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret)//传输完成函数
static inline void s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c)
static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c)//禁止/使能应答
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)//中断处理函数
static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c)
static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c)//使能/禁止中断
static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)//传输下一个字节
static inline int is_lastmsg(struct s3c24xx_i2c *i2c)//判断当前处理的消息是否为最后一个消息
static inline int is_msglast(struct s3c24xx_i2c *i2c)
static inline int is_msgend(struct s3c24xx_i2c *i2c)//判断当前消息是否已经传输完所有字节
static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,unsigned int *div1, unsigned int *divs)
static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
//计算传输分频系数、设置控制器的数据发送频率 在s3c24xx_i2c_init被调用
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)//IIC控制器的初始化,在s3c24xx_i2c_probe调用
4、IIC设备层驱动程序
4.1 实现函数操作
static int s3c24xx_i2c_probe(struct platform_device *pdev)//平台设备注册函数platform_driver_register()会调用探测函数
申请一个适配器结构i2c,并赋初值
获得i2c时钟资源
将适配器的寄存器资源映射到虚拟内存中去
申请中断处理函数
初始化IIC控制器
添加适配器i2c到内核中
static int s3c24xx_i2c_remove(struct platform_device *pdev)//与s3c24xx_i2c_probe相反
4.2 填充平台设备结构体
static struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-i2c",
.driver_data = TYPE_S3C2410,
}, {
.name = "s3c2440-i2c",
.driver_data = TYPE_S3C2440,
}, { },
};
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.id_table = s3c24xx_driver_ids,
.driver = {
.owner = THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
},
};
4.2注册、注销平台驱动
static int __init i2c_adap_s3c_init(void)
{
return platform_driver_register(&s3c24xx_i2c_driver);
}
static void __exit i2c_adap_s3c_exit(void)
{
platform_driver_unregister(&s3c24xx_i2c_driver);
}
五、IIC设备驱动 Linux i2c-dev.c文件分析
i2c-dev.c文件完全可以被看作一个I2C设备驱动,其结构与上述的描述是基本一致的,不过,它实现的一个i2c_client是虚拟的、临时的,随着设备文件的打开而产生,并随设备文件的关闭而撤销,并没有被添加到i2c_adapter的clients链表中。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()函数来构造1条 I2C消息并引发适配器algorithm通信函数的调用,完成消息的传输。但是,很遗憾,大多数稍微复杂一点I2C设备的读写流程并不对应于1条消息,往往需要2条甚至更多的消息来进行一次读写周期,这种情况下,在应用层仍然调用read()、write()文件API来读写I2C设备,将不能正确地读写。
鉴于上述原因,i2c-dev.c中i2cdev_read()和i2cdev_write()函数不具备太强的通用性,没有太大的实用价值,只能适用于非 RepStart模式的情况。对于2条以上消息组成的读写,在用户空间需要组织i2c_msg消息数组并调用I2C_RDWR IOCTL命令。
系统中i2c-dev.c文件定义的主设备号为89的设备可以方便地给应用程序提供读写I2C设备的寄存器的能力,使得工程师大多时候不需要为具体的I2C设备定义文件接口函数。
六、i2c设备的4种构建方法
6.1在板文件(如:mach-mini2440.c)定义一个i2c_board_info, 里面有:名字, 设备地址 然后i2c_register_board_info(busnum, ...) (把它们放入__i2c_board_list链表) list_add_tail(&devinfo->list, &__i2c_board_list);
链表何时使用: i2c_register_adapter > i2c_scan_static_board_info > i2c_new_device
使用限制:必须在 i2c_register_adapter 之前 i2c_register_board_info 所以:不适合我们动态加载insmod
例如:
需要通过i2c_register_board_info()函数注册i2c_board_info,向内核提供i2c设备的相关信息。
在arch/arm/mach-s3c2440/mach-mini2440.c
static struct i2c_board_info i2c_devices[] __initdata = { { I2C_BOARD_INFO("at24cxx", 0x50), },
{ I2C_BOARD_INFO("at24c02", 0x52), },
{ I2C_BOARD_INFO("at24c08", 0x58), },
}; static void __init mini2440_machine_init(void){ i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));
........
}
}
这样启动内核后在sys/bus/i2c/devices/目录下就有0-0050、0-0052、0-0058,其目录下的name就保存着要匹配的名字。如:cat sys/bus/i2c/devices/0-0052/name 可以看到at24c02,这个名字要与设备驱动中i2c_device_id 结构体填充name要一样。若匹配成功就会调用设备驱动中i2c_driver结构体里的proble()函数。
例子:下面7.2 newstyle方式
6.2 直接i2c_new_device, i2c_new_probed_device两种方法。注意区别
i2c_new_device :认为设备肯定存在
i2c_new_probed_device :对于"已经识别出来的设备"(probed_device),才会创建("new")
会根据传递进来的地址列表参数addr_list进行判断有没有地址列表中的设备,如果有并且设备可用就调用i2c_new_device来创建设备i2c_client。
i2c_new_probed_device(i2c_adap,&at24cxx_info,addr_list);
i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)//判断总线
i2c_check_addr(adap, addr_list[i])//检查地址是否可用
i2c_smbus_xfer(adap, addr_list[i], 0,I2C_SMBUS_READ, 0,I2C_SMBUS_BYTE, &data)
i2c_smbus_xfer(adap, addr_list[i], 0,I2C_SMBUS_WRITE, 0,I2C_SMBUS_QUICK, NULL) //判断是否有相应从而得知该地址的设备是否存在 info->addr = addr_list[i];//如果有响应则设置地址 i2c_new_device(adap, info);//创建设备结构i2c_client
例子代码:
/*注册一个在总线上IIC设备*采用i2c_new_probe_device*/#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/mutex.h>#include <linux/sysfs.h>#include <linux/mod_devicetable.h>#include <linux/log2.h>#include <linux/bitops.h>#include <linux/jiffies.h>#include <linux/i2c.h>#include <linux/fs.h>#include <linux/list.h>#include <linux/smp_lock.h>#include <asm/uaccess.h>static struct i2c_client *at24cxx_client;static unsigned short const addr_list[]={0x60,0x50,I2C_CLIENT_END};//地址列表,开发板的地址是0x50。如果列表没有设备将会不能注册进内核//mini2440开发板0x50,0x51,0x52,0x53static int __init at24cxx_dev_init(void){struct i2c_adapter *i2c_adap;struct i2c_board_info at24cxx_info;memset(&at24cxx_info, 0, sizeof(struct i2c_board_info));strlcpy(at24cxx_info.type, "at24cxx_dev_drv", I2C_NAME_SIZE);//匹配名字i2c_adap = i2c_get_adapter(0);//获得第几个适配器,对于mini2440只有一个就是0at24cxx_client = i2c_new_probed_device(i2c_adap,&at24cxx_info,addr_list);i2c_put_adapter(i2c_adap);if(at24cxx_client) return 0;else{ printk("creat at24cxx_client failed\n"); return -ENODEV; }}static void __exit at24cxx_dev_exit(void){ i2c_unregister_device(at24cxx_client);}module_init(at24cxx_dev_init);module_exit(at24cxx_dev_exit);MODULE_DESCRIPTION("Driver for most I2C EEPROMs");MODULE_AUTHOR("lys");MODULE_LICENSE("GPL");
/*采用i2c_new_device*/#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/mutex.h>#include <linux/sysfs.h>#include <linux/mod_devicetable.h>#include <linux/log2.h>#include <linux/bitops.h>#include <linux/jiffies.h>#include <linux/i2c.h>#include <linux/fs.h>#include <linux/list.h>#include <linux/smp_lock.h>#include <asm/uaccess.h>static struct i2c_client *at24cxx_client;static struct i2c_board_info at24cxx_info[] = { {I2C_BOARD_INFO("at24cxx_dev_drv", 0x50),}, };static int __init at24cxx_dev_init(void){ struct i2c_adapter *i2c_adap; i2c_adap = i2c_get_adapter(0);//获得第几个适配器,对于mini2440只有一个就是0 if(i2c_adap == NULL) { printk("get i2c_adap failed\n"); return -ENODEV; } at24cxx_client = i2c_new_device(i2c_adap,&at24cxx_info); i2c_put_adapter(i2c_adap); if(at24cxx_client) return 0; else{ printk("creat at24cxx_client failed\n"); return -ENODEV; }}static void __exit at24cxx_dev_exit(void){ i2c_unregister_device(at24cxx_client);}module_init(at24cxx_dev_init);module_exit(at24cxx_dev_exit);MODULE_DESCRIPTION("Driver for most I2C EEPROMs");MODULE_AUTHOR("lys");MODULE_LICENSE("GPL");
测试条件:insmod i2c-s3c2410.ko
加载自己写驱动
6.3从用户空间创建设备
创建:执行后就导致i2c_new_device被调用
echo at24cxx_dev_drv(设备名称,与驱动文件中要匹配) 0x50 > /sys/class/i2c-adapter/adapter_devi2c-0(适配器设备名)/new_device
echo at24cxx_dev_drv 0x50 > /sys/devices/platform/s3c2440-i2c/adapter_devi2c-0/new_device
删除:导致i2c_unregister_device
echo 0x50 (设备地址)> /sys/class/i2c-adapter/adapter_devi2c-0/delete_device
echo 0x50 > /sys/devices/platform/s3c2440-i2c/adapter_devi2c-0/delete_device
创建后在sys/bus/i2c/devices/目录下就会有该设备,表示IIC总线下支持设备地址为0x50的设备
分析:
static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device);
static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device);
当在应用层操作new_device这个文件时候就会调用i2c_sysfs_new_device()函数
测试条件:insmod i2c-s3c2410.ko
加载自己写驱动
6.4前面的3种方法都要事先确定适配器(I2C总线,I2C控制器)
如果我事先并不知道这个I2C设备在哪个适配器上,怎么办?去class表示的所有的适配器上查找有上一些I2C设备的地址是一样,怎么继续分配它是哪一款?用detect函数
static struct i2c_driver at24cxx_driver = {
.class = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备 */
.driver = {
.name = "100ask",
.owner = THIS_MODULE,
},
.probe = at24cxx_probe,
.remove = __devexit_p(at24cxx_remove),
.id_table = at24cxx_id_table,
.detect = at24cxx_detect, /* 用这个函数来检测设备确实存在 */
.address_list = addr_list, /* 这些设备的地址 */
.class = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备 */
.driver = {
.name = "100ask",
.owner = THIS_MODULE,
},
.probe = at24cxx_probe,
.remove = __devexit_p(at24cxx_remove),
.id_table = at24cxx_id_table,
.detect = at24cxx_detect, /* 用这个函数来检测设备确实存在 */
.address_list = addr_list, /* 这些设备的地址 */
去"class表示的这一类"I2C适配器,用"detect函数"来确定能否找到"address_list里的设备",如果能找到就调用i2c_new_device来注册i2c_client, 这会和i2c_driver的id_table比较,如果匹配,调用probe
i2c_add_driver
i2c_register_driver
1.driver->driver.bus = &i2c_bus_type; //at24cxx_driver放入i2c_bus_type的drv链表
driver_register(&driver->driver);//并且从dev链表里取出能匹配的i2c_client并调用probe
i2c_register_driver
1.driver->driver.bus = &i2c_bus_type; //at24cxx_driver放入i2c_bus_type的drv链表
driver_register(&driver->driver);//并且从dev链表里取出能匹配的i2c_client并调用probe
2.对于每一个适配器,调用__attach_adapter()函数,确定address_list里的设备是否存在。 如果存在,再调用detect进一步确定、设置,然后i2c_new_device
bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter);
__attach_adapter()
adapter = to_i2c_adapter(dev);
i2c_detect(adapter, driver);i2c_detect_address(temp_client, kind, driver);driver->detect(temp_client, kind, &info);//回调驱动层的detect()函数
七、如何下手写驱动
一方面,适配器驱动可能是 Linux内核本身还不包含的。另一方面,挂接在适配器上的具体设备驱动可能也是 Linux不存在的。即便上述设备驱动都存在于 Linux内核中,其基于的平台也可能与我们的电路板不一样。因此,工程师要实现的主要工作将包括:
1、提供 I2C适配器的硬件驱动,探测、初始化 I2C适配器(如申请I2C的 I/O地址和中断号)、驱动CPU控制的 I2C适配器从硬件上产生各种信号以及处理 I2C中断等。
2、提供 I2C适配器的algorithm ,用具体适配器的 xxx_xfer()函数填充i2c_algorithm 的master_xfer指针,并把 i2c_algorithm指针赋值给i2c_adapter 的algo指针
3、实现 I2C设备驱动与i2c_driver 接口,用具体设备 yyy的yyy_attach_adapter() 函数指针、 yyy_detach_client()函数指针和 yyy_command()函数指针的赋值给i2c_driver的 attach_adapter、 detach_adapter和 detach_client指针。
4、实现 I2C设备驱动的文件操作接口,即实现具体设备 yyy的yyy_read() 、yyy_write()和 yyy_ioctl()函数等。
上述工作中 1、2 属于I2C总线驱动, 3、4 属于I2C设备驱动,做完这些工作,系统会增加两个内核模块。
1、提供 I2C适配器的硬件驱动,探测、初始化 I2C适配器(如申请I2C的 I/O地址和中断号)、驱动CPU控制的 I2C适配器从硬件上产生各种信号以及处理 I2C中断等。
2、提供 I2C适配器的algorithm ,用具体适配器的 xxx_xfer()函数填充i2c_algorithm 的master_xfer指针,并把 i2c_algorithm指针赋值给i2c_adapter 的algo指针
3、实现 I2C设备驱动与i2c_driver 接口,用具体设备 yyy的yyy_attach_adapter() 函数指针、 yyy_detach_client()函数指针和 yyy_command()函数指针的赋值给i2c_driver的 attach_adapter、 detach_adapter和 detach_client指针。
4、实现 I2C设备驱动的文件操作接口,即实现具体设备 yyy的yyy_read() 、yyy_write()和 yyy_ioctl()函数等。
上述工作中 1、2 属于I2C总线驱动, 3、4 属于I2C设备驱动,做完这些工作,系统会增加两个内核模块。
7.1I2C总线驱动(一般不需要我们自己写)
(1)主要是完成i2c_adapter 适配器结构的注册
i2c_add_numbered_adapter()
i2c_add_adapter()
i2c_register_adapter()
/*1、设置适配器结构的成员,所属总线类型,然后注册*/
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);
/*2、搜索与平台相关的地址信息*/
i2c_scan_static_board_info(adap)
/*3、*/
bus_for_each_drv(&i2c_bus_type, NULL, adap,i2c_do_add_adapter);
i2c_do_add_adapter()
i2c_detect(adap, driver);
i2c_detect_address()
i2c_new_device(adapter, &info)
device_register(&client->dev)
(2)I2C总线驱动写法
1、定义一个i2c_adapter和算法结构体
static const struct i2c_algorithm i2c_bus_s3c2440_algo = {
.master_xfer = i2c_bus_s3c2440_xfer,
.functionality = i2c_bus_s3c2440_func,
};
struct i2c_adapter i2c_bus_s3c2440_adapter{
.owner =THIS_MODULE,
.name = "i2c_s3c2440_adap",
.algo =i2c_bus_s3c2440_algo,
};
.master_xfer = i2c_bus_s3c2440_xfer,
.functionality = i2c_bus_s3c2440_func,
};
struct i2c_adapter i2c_bus_s3c2440_adapter{
.owner =THIS_MODULE,
.name = "i2c_s3c2440_adap",
.algo =i2c_bus_s3c2440_algo,
};
2、两个算法函数的实现
static int i2c_bus_s3c2440_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
//硬件方面的操作
static u32 i2c_bus_s3c2440_func(struct i2c_adapter *adap)
static u32 i2c_bus_s3c2440_func(struct i2c_adapter *adap)
3、模块加载卸载
static int __init i2c_bus_s3c2440(void)
{
/*硬件初始化、寄存器虚拟地址映射*/
s3c2440_i2c_regsp = ioremap(0x54000000,sizeof(struct s3c2440_i2c_regs));
/*2注册一个i2c_adapter*/
i2c_add_adapter(&i2c_bus_s3c2440_adapter);
return 0;
}
{
/*硬件初始化、寄存器虚拟地址映射*/
s3c2440_i2c_regsp = ioremap(0x54000000,sizeof(struct s3c2440_i2c_regs));
/*2注册一个i2c_adapter*/
i2c_add_adapter(&i2c_bus_s3c2440_adapter);
return 0;
}
static void __exit i2c_bus_s3c2440(void)
{
i2c_del_adapter(&i2c_bus_s3c2440_adapter);
iounmap(s3c2440_i2c_regsp);
}
{
i2c_del_adapter(&i2c_bus_s3c2440_adapter);
iounmap(s3c2440_i2c_regsp);
}
7.2 I2C设备驱动(i2cdev_driver)
(1)i2c设备驱动注册补充分析(参考i2c_dev.c,提供了以i2c为通信协议的设备通用接口,注册了主设备号89的字符驱动)
i2c_dev_init(void)
register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);//字符驱动
class_create(THIS_MODULE, "i2c-dev"); //创建类exit in /sys/class/
i2c_add_driver(&i2cdev_driver) //注册i2c设备驱动, 实现在linux-2.6.32.2/include/linux/i2c.h
i2c_register_driver(THIS_MODULE, driver);//实现在linux-2.6.32.2/drivers/i2c/i2c-core.c
driver->driver.bus = &i2c_bus_type;//设置i2c_driver (即为i2cdev_driver)的总线类型,即属于哪条总线
driver_register(&driver->driver); //注册驱动,调用结束后就会调用proble()函数
bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter);
//调用__attach_adapter在总线上查找合适driver的适配器
driver_register(struct device_driver *drv)
struct device_driver *other = driver_find(drv->name, drv->bus); //判断i2c_driver 是否被注册金内核
bus_add_driver(drv);//将i2c_driver 挂接到i2c总线上
driver_attach(drv);
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); //对i2c总线上的每一个i2c设备i2c_client都会调用
__driver_attach,这里的dev即i2c_client,drv即i2c_driver
driver_bind()
driver_match_device(struct device_driver *drv, struct device *dev)
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
实际上就是调用总线i2c_bus_type结构中match函数,即是:
i2c_device_match(struct device *dev, struct device_driver *drv)
i2c_match_id(driver->id_table, client) //若i2c_client的名字和i2c_device_id的中名字相
同,则匹配成功,才会调用后面的probe()
driver_probe_device(struct device_driver *drv, struct device *dev)
really_probe(struct device *dev, struct device_driver *drv) dev->bus->probe(dev);//调用总线i2c_bus_type结构中proble()函数
实际上就是 i2c_device_probe()
driver->probe(client, i2c_match_id(driver->id_table, client));
//调用到i2c_driver的probe()函数(即是自己写的设备驱动proble()函数) __attach_adapter(struct device *dev, void *data)
i2c_detect(adapter, driver); //探测适配器支持的设备的地址
i2c_detect_address(struct i2c_client *temp_client, int kind, struct i2c_driver *driver)
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
device_register(&client->dev); //注册一个真是IIC设备i2c_client
driver->attach_adapter(adapter);//如果Legacy 型驱动,则调用i2c_driver结构中attach_adapter函数为适配器创建设备节点
(2)i2c设备驱动的写法
(参考i2c-dev.c,自定义文件操作函数接口,没有借用内核i2c-dev.c字符驱动文件操作接口)
newstyle方式 :(i2c_driver成员函数 + 字符设备驱动)
1.注册i2c设备相关信息(目的是让总线支持该设备,方法有四种。具体见上面:六、i2c设备的4种构建方法)
需要通过i2c_register_board_info()函数注册i2c_board_info,向内核提供i2c设备的相关信息。
在arch/arm/mach-s3c2440/mach-mini2440.c
static struct i2c_board_info i2c_devices[] __initdata = {
{ I2C_BOARD_INFO("at24cxx", 0x50), },
{ I2C_BOARD_INFO("at24cxx", 0x50), },
{ I2C_BOARD_INFO("at24c02", 0x52), },
{ I2C_BOARD_INFO("at24c08", 0x58), },
}; static void __init mini2440_machine_init(void)
{
i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));
{
i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));
........
}
}
这样启动内核后在sys/bus/i2c/devices/目录下就有0-0050、0-0052、0-0058,其目录下的name就保存着要匹配的名字。如:cat sys/bus/i2c/devices/0-0052/name 可以看到at24c02,这个名字要与设备驱动中i2c_device_id 结构体填充name要一样。若匹配成功就会调用设备驱动中i2c_driver结构体里的proble()函数。
2.定义填充i2c_driver结构体
static const struct i2c_device_id at24cxx_id[] = {
{ "at24cxx", 0 }, //匹配时,I2C_BOARD_INFO
{ "at24cxx", 0 }, //匹配时,I2C_BOARD_INFO
{ }
};
MODULE_DEVICE_TABLE(i2c, at24cxx_id);
};
MODULE_DEVICE_TABLE(i2c, at24cxx_id);
static struct i2c_driver at24cxx_driver = {
.driver = {
.name = "at24c08b",
.owner = THIS_MODULE,
},
.probe = at24cxx_probe,
.name = "at24c08b",
.owner = THIS_MODULE,
},
.probe = at24cxx_probe,
.remove = __devexit_p(at24cxx_remove),
.id_table = at24cxx_id,
};
3.实现i2c_driver结构体中函数
static int at24cxx_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
//(1)分配一个i2c_client 结构体at24cxx_client(全局结构)
//(2)设置i2c_client
//(3)注册一个IIC字符设备
//(4)创建设备类和设备节点,sys/class下产生类,同时在/dev下自动创建设备节点(应用层open要用到)
}
在加载该模块时i2c_add_driver(&at24cxx_driver)会将驱动注册到IIC总线上,并且进行设备的探测。探测主要是调用i2c_match_id()函数比较i2c_client结构和i2c_device_id结构中的name是否相同,相同时匹配就成功。说明总线上有支持该模块驱动的设备,这时就会调用at24cxx_probe()函数注册一个字符驱动。(与下面对应i2c_core.c中函数)
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,const struct i2c_client *client)
{
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
{
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status;
if (!client)
return 0;
driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n");
status = driver->probe(client, i2c_match_id(driver->id_table, client));
if (status)
client->driver = NULL;
return status;
}
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status;
if (!client)
return 0;
driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n");
status = driver->probe(client, i2c_match_id(driver->id_table, client));
if (status)
client->driver = NULL;
return status;
}
4.I2C设备驱动模块加载与卸载
static int __init at24cxx_init(void)
{
return i2c_add_driver(&at24cxx_driver);
}
static void __exit at24cxx_exit(void)
{
i2c_del_driver(&at24cxx_driver);
}
{
return i2c_add_driver(&at24cxx_driver);
}
static void __exit at24cxx_exit(void)
{
i2c_del_driver(&at24cxx_driver);
}
5.填充字符驱动函数操作结构体file_operations
static struct file_operations at24cxx_fops ={
.owner = THIS_MODULE,
.read = at24cxx_read,
.write = at24cxx_write,
.open = at24cxx_open,
.release = at24cxx_release,
};
.owner = THIS_MODULE,
.read = at24cxx_read,
.write = at24cxx_write,
.open = at24cxx_open,
.release = at24cxx_release,
};
6.实现字符驱动的操作函数
例子:下面是驱动程序和测试程序
#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/mutex.h>#include <linux/sysfs.h>#include <linux/mod_devicetable.h>#include <linux/log2.h>#include <linux/bitops.h>#include <linux/jiffies.h>#include <linux/i2c.h>#include <linux/fs.h>#include <linux/list.h>#include <linux/smp_lock.h>#include <asm/uaccess.h>#define DEVICE_NAME "at24c_name" //cat /proc/devicesstatic int at24cxx_major = 110;struct at24cxx_dev{struct i2c_client *client;};struct at24cxx_dev *at24cxx_dev_p;static struct i2c_driver at24cxx_driver;static struct class *at24cxx_class;static struct device *at24cxx_class_devs;static int at24cxx_open(struct inode *inode, struct file *file){ file->private_data = at24cxx_dev_p;return 0;}static int at24cxx_release(struct inode *inode, struct file *file){ file->private_data = NULL;return 0;}static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset){ unsigned char addr,data; copy_from_user(&addr,buf,1); data = i2c_smbus_read_byte_data(at24cxx_dev_p->client,addr); copy_to_user(buf,&data,1); return 1;}static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset){ unsigned char ker_buf[2]; unsigned char addr,data; copy_from_user(ker_buf,buf,2); addr = ker_buf[0]; data = ker_buf[1]; if(!i2c_smbus_write_byte_data(at24cxx_dev_p->client,addr,data)) return 2; else return -EIO;}static struct file_operations at24cxx_fops ={ .owner = THIS_MODULE,.read = at24cxx_read,.write = at24cxx_write,.open = at24cxx_open,.release = at24cxx_release,};static int at24cxx_probe(struct i2c_client *client, const struct i2c_device_id *id){ int ret; at24cxx_dev_p = kzalloc(sizeof(struct at24cxx_dev), GFP_KERNEL); if(!at24cxx_dev_p){ ret = -ENOMEM; return ret; } memset(at24cxx_dev_p, 0, sizeof(struct at24cxx_dev)); at24cxx_dev_p->client = client; ret = register_chrdev(at24cxx_major,DEVICE_NAME,&at24cxx_fops);// if(ret) goto out; at24cxx_class = class_create(THIS_MODULE,"at24cxx_class");//sys/class //设备类 at24cxx_class_devs = device_create(at24cxx_class,NULL,MKDEV(at24cxx_major,0),NULL,"at24cxx%d",0);// dev/设备节点 if (IS_ERR(at24cxx_class)) {ret = PTR_ERR(at24cxx_class);goto out_unreg_chrdev; } printk(DEVICE_NAME"\tinitialized\n"); return 0;out_unreg_chrdev:unregister_chrdev(at24cxx_major, DEVICE_NAME);out:printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);return ret;}static int __devexit at24cxx_remove(struct i2c_client *client){ device_destroy(at24cxx_class,MKDEV(at24cxx_major,0)); class_destroy(at24cxx_class); kfree(at24cxx_dev_p); unregister_chrdev(at24cxx_major, DEVICE_NAME); return 0;}static const struct i2c_device_id at24cxx_ids[] = {{ "at24cxx", 0 },//匹配{ }};MODULE_DEVICE_TABLE(i2c, at24cxx_ids);static struct i2c_driver at24cxx_driver = {.driver = {.name = "at24cxx_driver",.owner = THIS_MODULE,},.probe = at24cxx_probe,.remove = __devexit_p(at24cxx_remove),.id_table = at24cxx_ids,};static int __init at24cxx_init(void){ return i2c_add_driver(&at24cxx_driver);}static void __exit at24cxx_exit(void){ i2c_del_driver(&at24cxx_driver);}module_init(at24cxx_init);module_exit(at24cxx_exit);MODULE_DESCRIPTION("Driver for most I2C EEPROMs");MODULE_AUTHOR("lys");MODULE_LICENSE("GPL");
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>/* i2c_addr_test r addr * i2c_addr_test w addr val */void print_usage(char *file){printf("%s r addr\n", file);printf("%s w addr val\n", file);}int main(int argc, char **argv){int fd;unsigned char buf[2];if ((argc != 3) && (argc != 4)){print_usage(argv[0]);return -1;}fd = open("/dev/at24cxx0", O_RDWR);if (fd < 0){printf("can't open /dev/at24cxx0\n");return -1;}if (strcmp(argv[1], "r") == 0){ buf[0] = strtoul(argv[2], NULL, 0); if(read(fd, buf, 1) == 1) { printf("read data: %d, 0x%2x\n", buf[0], buf[0]); }}else if ((strcmp(argv[1], "w") == 0) && (argc == 4)){buf[0] = strtoul(argv[2], NULL, 0);//addrbuf[1] = strtoul(argv[3], NULL, 0);//dataif(write(fd, buf, 2) != 2) { printf("write err\n"); }}else{print_usage(argv[0]);return -1;}return 0;}
测试条件:1、insmod i2c-s3c2410.ko
2、为内核添加IIC设备(方法见上面分析的四种)
八、用户应用程序直接访问IIC设备
用户可以不用为IIC设备写驱动,而是在应用程序直接访问IIC设备。这种方法其实是借用内核i2c-dev.c字符驱动文件操作接口,应用程序需要一个头文件i2c-dev.h 。可以百度下载i2c-tools-3.1.1.tar.bz2开源工具包。
i2c_smbus_read_word_data和i2c_smbus_write_byte_data函数来读写数据。参考程序如下:
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include "i2c-dev.h"/* i2c_test </dev/i2c-0> <dev_addr> r addr * i2c_test </dev/i2c-0> <dev_addr> w addr val */void print_usage(char *file){printf("%s </dev/i2c-0> <dev_addr> r addr\n", file);printf("%s </dev/i2c-0> <dev_addr> w addr val\n", file);}int main(int argc, char **argv){int fd;unsigned char addr,data;if ((argc != 5) && (argc != 6)){print_usage(argv[0]);return -1;}fd = open(argv[1], O_RDWR);if (fd < 0){printf("can't open %s\n",argv[1]);return -1;}addr = strtoul(argv[2],NULL,0);if(ioctl(fd,I2C_SLAVE,addr) < 0){ printf("set addr error\n"); return -1;}if (strcmp(argv[3], "r") == 0){ addr = strtoul(argv[4], NULL, 0); data = i2c_smbus_read_word_data(fd,addr); printf("read data: %d, 0x%2x\n", data, data); }else if ((strcmp(argv[3], "w") == 0) && (argc == 6)){addr = strtoul(argv[4], NULL, 0);//addrdata = strtoul(argv[5], NULL, 0);//datai2c_smbus_write_byte_data(fd,addr,data);}else{print_usage(argv[0]);return -1;}return 0;}
测试条件:1、insmod i2c-s3c2410.ko
2、insmod i2c-dev.ko
以上是我学IIC驱动做的一些笔记总结,仅供大家学习参考,如果有错误的恳请指正。
参考博客:
Linux I2C核心、总线与设备驱动
http://tanatseng.blog.163.com/blog/static/1749916292011440122182/
i2c 驱动之设备模型建立
http://chxxxyg.blog.163.com/blog/static/1502811932010635818167/
i2c驱动之难点释疑
http://chxxxyg.blog.163.com/blog/static/1502811932010636351825/
写一个 IIC适配器驱动需要做些什么
http://chxxxyg.blog.163.com/blog/static/150281193201063103618798/
I2c-s3c2440.c 分析
http://blog.chinaunix.net/uid-25120309-id-3357609.html
http://www.doc88.com/p-910624500869.html
0 0
- Linux I2C体系结构分析
- Linux的I2C体系结构
- linux的i2c体系结构
- Linux 的 I2C 体系结构
- 实例解析linux内核I2C体系结构
- 实例解析linux内核I2C体系结构
- 实例解析linux内核I2C体系结构
- Linux中I2C总线驱动体系结构
- 实例解析Linux内核I2C体系结构(1)
- 实例解析linux内核I2C体系结构
- 实例解析linux内核I2C体系结构
- 实例解析linux内核I2C体系结构
- 实例解析linux内核I2C体系结构
- 实例解析linux内核I2C体系结构
- 实例解析linux内核I2C体系结构
- 实例解析linux内核I2C体系结构
- 实例解析linux内核I2C体系结构
- linux内核I2C体系结构(1)
- 刷新本地DNS缓存的方法
- 存储过程示例
- 《黑马程序员》内存地址值
- 使用PhoneGap+JQM开发App时遇到的问题,持续更新中。。。
- c#编程基础知识——VS2010常用技巧,键盘常用键
- Linux I2C体系结构分析
- 周凯:如何利用数据挖掘让RTB广告效果倍增?
- cvMinMaxLoc函数实例
- Nan c++ study
- 黄圣依张柏芝小S不足1.6米 实际身高大曝光
- 悲观锁和乐观锁
- eclipse no more handles )
- poi操作excel,复制sheet,复制行,复制单元格 .
- UVA10905- Children's Game
原创粉丝点击
热门IT博客
热门问题
老师的惩罚
人脸识别
我在镇武司摸鱼那些年
重生之率土为王
我在大康的咸鱼生活
盘龙之生命进化
天生仙种
凡人之先天五行
春回大明朝
姑娘不必设防,我是瞎子
函数定义域怎么求
反正弦函数定义域
三角函数定义域
求函数定义域
反正切函数定义域
对数函数的定义域
指数函数的定义域
函数的定义域怎么求
求函数的定义域
函数定义域的七种情况
复合函数的定义域
求下列函数的定义域
抽象函数的定义域
求函数定义域的例题20道
求函数定义域的方法
偶函数定义域
反函数的定义域
函数定义域的口诀
三角函数的定义域
幂函数的定义域
分段函数的定义域
反三角函数的定义域
反三角函数的定义域是什么
android函数库
复变函数论
实变函数论的典型问题与方法
分布函数习题
三角函数题
高一函数经典题1000道及答案
高中复合函数题
三角函数高考题
高一函数数学题
反比例函数复习题
一次函数20道题及答案
初中数学函数大题
高考函数经典题1000道及答案
高中数学函数大题
反函数题
高一函数题
反比例函数中考题汇编
初中函数题