I2C设备驱动的编写

  前面我们说了如何I2C用户模式驱动,这种驱动基于I2C子系统,但是他对于应用程序开发人员的要求较高,需要应用程序开发人员了解硬件的一些东西,比如时序,地址等等,而多数时候应用程序开发人员是按照操作文件的方法操作设备,所以我们更希望用一些更简单的接口去访问。也就是我们今天的内容——基于I2C子系统的字符驱动。 
I2C子系统的代码分为三部分如图: 
 
Host:主机控制器驱动 
Device:设备驱动代码 
Core: 核心代码,提供设备与控制器的接口 
一、主机控制器驱动 
Linux下主机控制器驱动,大多数是BSP提供的,这里不多说,简单说下它主要干的活。I2C主机控制器在内核里又叫适配器,用结构i2c_adapter描述。 
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
……
}; 
struct i2c_algorithm 提供设备访问控制器的接口,定义如下: 
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 *);
}; 
其中master_xfer就是我们给设备端提供的接口,这部分内容按照芯片手册中寄存器的操作实现数据的收发。 
最终我们将i2c_adapter注册到系统中,使用如下函数:
int i2c_add_numbered_adapter(struct i2c_adapter *); 
二、核心代码 
这部分就不说了,刚才我们介绍的函数全部是核心代码提供,它主要是提供标准的统一的接口。 
三、设备代码 
 
基于I2C的字符驱动的编写首先我们需要了解几个特定的结构。 
1、i2c_bus_type 
i2c总线结构定义了一些总线相关的方法,这里我们关系的是i2c_driver和i2c_client的配备规则,为什么匹配呢,i2c_client携带硬件信息,而i2c_driver只负责操作设备而不管操作的是那个设备它需要的硬件信息有i2c_client提供,所以需要i2c_client和i2c_driver协同操作,而一个系统中i2c_driver和i2c_client都可能有多个,如何得到自己的另一半就是我所说的匹配规则,i2c_bus_type的匹配规则定义如下: 
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
}; 
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
if (!client)
return 0;
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;

我们发现i2c总线的匹配规则是id或name两种,id优先级高。 
2、板级结构: 
struct i2c_board_info {
char type[I2C_NAME_SIZE]; //芯片类型,其实也就是名字,用来匹配
unsigned short flags; //标志位,一些特定的标志

unsigned short addr; //地址,从设备地址,不包括读写位
void *platform_data; //用来传递一些私有数据
struct dev_archdata *archdata; //同上
struct device_node *of_node;
int irq;
}; 
板子上没有一个I2C的设备,我们就要注册一个这样的结构体,到内核里边,这部分代码一般添加在平台代码里边,注册函数如下: 
i2c_register_board_info(int busnum, struct i2c_board_info const *info,unsigned n);
busnum 现在很多CPU有多条I2C总线,这个参数表示第几条总线
info 是一个结构体数据,表示我们要注册的I2C设备
n 表示我们注册了几个I2C设备 
通过上面函数就能把设备注册到系统中。结构如图: 
 
3、i2c_client 
这个结构我们不需要操作,是操作系统即核心代码自动完成,这个过程其实是在注册i2c_adapter的时候完成的。即在函数i2c_add_numbered_adapter中完成,最终i2c_client携带者i2c_board_info和i2c_adapter的信息。 
4、i2c_driver 
这部分代码主要负责注册i2c_driver和匹配相应的i2c_client。I2c_driver定义如下: 
struct i2c_driver {
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
struct device_driver driver;
const struct i2c_device_id *id_table;
……
}; 
注册函数如下:
int i2c_add_driver(struct i2c_driver *driver); 
这个函数负责注册i2c_driver并匹配i2c_client,当匹配到了对于的i2c_client,probe函数被执行,并且i2c_client被以参数的形式传递过来。我们可以通过i2c_client提供的硬件信息和操作接口操作我们想要的设备。 
5、数据传输 
数据传输结构: 
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, fro***ave 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 */
}; 
消息的封装与上节用户模式驱动相似,封装好消息使用如下函数提交给核心代码,最终通过控制器驱动发送给具体的设备。 
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
adap 适配器,由client->adapter获得。
msgs 消息
num 消息个数 
通过上面内容我们就可以构建我们的基于linux下i2c子系统的设备驱动了,例程如下: 
平台代码添加: 
static struct i2c_board_info i2c_devs0[] __initdata = {
{I2C_BOARD_INFO("lm75", 0x48),},
};
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0)); 
驱动代码: 
#include < linux/module.h>
#include < linux/kernel.h>
#include < linux/init.h>
#include < linux/fs.h>
#include < linux/cdev.h>
#include < linux/i2c.h>
#include < linux/slab.h>
#include < asm/uaccess.h>
MODULE_LICENSE ("GPL");
#define LM75_REG_CONF 0x01
static const u8 LM75_REG_TEMP[3] = {

0x00, /* input */
0x03, /* max */
0x02, /* hyst */
};
struct lm75_data 
{
u16 temp[3]; /* Register values,
0 = input
1 = max
2 = hyst */
};
static int lm75_major = 250;
static int lm75_minor = 0;
static int number_of_devices = 1;
static dev_t devno = 0;
static struct cdev cdev;
static struct i2c_client *new_client;
struct lm75_data *data;
static int lm75_read_value(struct i2c_client *client)
{
struct i2c_msg msgs[2];
int status;
char buf1[2];
char buf2[2];
msgs[0].len = 1;
msgs[0].addr = client->addr; // lm75 设备地址
msgs[0].flags = 0;//write
msgs[0].buf = buf1;
msgs[0].buf[0] = LM75_REG_TEMP[0];
msgs[1].len = 2;//读出的数据
msgs[1].addr = client->addr;// lm75 设备地址 
msgs[1].flags = I2C_M_RD;//read
msgs[1].buf = buf2;//存放返回值的地址。
status = i2c_transfer(client->adapter, msgs, 2);
if(status < 0)
return status;
printk("1 = %2x %2x\n", buf2[0], buf2[1]);
return (buf2[0] << 8) | buf2[1];
}
static ssize_t lm75_read(struct file *file, char __user *buff, size_t count, loff_t *offset) 
{
int status;
status = lm75_read_value(new_client);
if(status < 0)
{
return status;
}
printk("status = %x\n", status);
if(copy_to_user(buff, (char *)&status, sizeof(status)))
return -EFAULT;
return 0;
}
static int lm75_open(struct inode *inode, struct file *file)
{
return 0;
}
static int lm75_release(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations lm75_fops = {
.owner = THIS_MODULE,
.read = lm75_read,
.open = lm75_open,
.release = lm75_release,
};
static int lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
new_client = client;
devno = MKDEV(lm75_major, lm75_minor);
ret = register_chrdev_region(devno, number_of_devices, "lm75");
if(ret)
{
printk("failed to register device number\n");
goto err_register_chrdev_region;
}
cdev_init(&cdev, &lm75_fops);
cdev.owner = THIS_MODULE;
ret = cdev_add(&cdev, devno, number_of_devices);
if(ret)
{
printk("failed to add device\n");
goto err_cdev_add;
}
return 0;
err_cdev_add:
unregister_chrdev_region(devno, number_of_devices);
err_register_chrdev_region:
kfree(data);
return ret;
}
static int lm75_remove(struct i2c_client *client)
{
cdev_del(&cdev);
unregister_chrdev_region(devno, number_of_devices);
return 0;
}
enum lm75_type { /* keep sorted in alphabetical order */
lm75,
lm75a,
};
static const struct i2c_device_id lm75_ids[] = {
{ "lm75", lm75, },
{ "lm75a", lm75a, },
{ /* LIST END */ }
};
static struct i2c_driver lm75_driver = {
.driver = {
.name = "lm75",
},
.probe = lm75_probe,
.remove = lm75_remove,
.id_table = lm75_ids,
};
static int __init s5pc100_lm75_init(void)
{
return i2c_add_driver(&lm75_driver);
}
static void __exit s5pc100_lm75_exit(void)
{
i2c_del_driver(&lm75_driver);
}
module_init(s5pc100_lm75_init);
module_exit(s5pc100_lm75_exit);

I2C设备驱动的编写,布布扣,bubuko.com

时间: 2024-11-02 23:31:42

I2C设备驱动的编写的相关文章

Linux I2C设备驱动编写(二)

在(一)中简述了Linux I2C子系统的三个主要成员i2c_adapter.i2c_driver.i2c_client.三者的关系也在上一节进行了描述.应该已经算是对Linux I2C子系统有了初步的了解.下面再对他们之间的关系进行代码层的深入分析,我认为对他们的关系了解的越好,越有助于I2C设备的驱动开发及调试. 带着问题去分析可能会更有帮助吧,通过对(一)的了解后,可能会产生以下的几点疑问: i2c_adapter驱动如何添加? i2c_client与i2c_board_info究竟是什么

Linux I2C设备驱动编写(一)

在Linux驱动中I2C系统中主要包含以下几个成员: I2C adapter 即I2C适配器 I2C driver 某个I2C设备的设备驱动,可以以driver理解. I2C client 某个I2C设备的设备声明,可以以device理解. I2C adapter 是CPU集成或外接的I2C适配器,用来控制各种I2C从设备,其驱动需要完成对适配器的完整描述,最主要的工作是需要完成i2c_algorithm结构体.这个结构体包含了此I2C控制器的数据传输具体实现,以及对外上报此设备所支持的功能类型

【转】Linux I2C设备驱动编写(一)

原文网址:http://www.cnblogs.com/biglucky/p/4059576.html 在Linux驱动中I2C系统中主要包含以下几个成员: I2C adapter 即I2C适配器 I2C driver 某个I2C设备的设备驱动,可以以driver理解. I2C client 某个I2C设备的设备声明,可以以device理解. I2C adapter 是CPU集成或外接的I2C适配器,用来控制各种I2C从设备,其驱动需要完成对适配器的完整描述,最主要的工作是需要完成i2c_alg

【转】Linux I2C设备驱动编写(二)

原文网址:http://www.cnblogs.com/biglucky/p/4059582.html 在(一)中简述了Linux I2C子系统的三个主要成员i2c_adapter.i2c_driver.i2c_client.三者的关系也在上一节进行了描述.应该已经算是对Linux I2C子系统有了初步的了解.下面再对他们之间的关系进行代码层的深入分析,我认为对他们的关系了解的越好,越有助于I2C设备的驱动开发及调试. 带着问题去分析可能会更有帮助吧,通过对(一)的了解后,可能会产生以下的几点疑

linux设备驱动辩编写基础

Linux设备驱动辩编写基础 一.linux中的驱动是以模块的形式存在的,这样就大大提高了驱动的灵活性,linux内核模块的程序结构如下: l  模块加载函数(必须):module_init() l  模块卸载函数(必须):module_exit() l  模块许可证声明(必须):MODULE_LECENSE("GPL") l  模块参数(可选):module_param(a,int,0400) l  模块到处符号(可选):EXPORT_SYMBOL_GPL(func) l  模块作者

【转】Linux I2C设备驱动编写(三)-实例分析AM3359

原文网址:http://www.cnblogs.com/biglucky/p/4059586.html TI-AM3359 I2C适配器实例分析 I2C Spec简述 特性: 兼容飞利浦I2C 2.1版本规格 支持标准模式(100K bits/s)和快速模式(400K bits/s) 多路接收.发送模式 支持7bit.10bit设备地址模式 32字节FIFO缓冲区 可编程时钟发生器 双DMA通道,一条中断线 三个I2C模块实例I2C0\I2C1\I2C2 时钟信号能够达到最高48MHz,来自PR

Linux I2C设备驱动编写(三)-实例分析AM3359

TI-AM3359 I2C适配器实例分析 I2C Spec简述 特性: 兼容飞利浦I2C 2.1版本规格 支持标准模式(100K bits/s)和快速模式(400K bits/s) 多路接收.发送模式 支持7bit.10bit设备地址模式 32字节FIFO缓冲区 可编程时钟发生器 双DMA通道,一条中断线 三个I2C模块实例I2C0\I2C1\I2C2 时钟信号能够达到最高48MHz,来自PRCM 不支持 SCCB协议 高速模式(3.4MBPS) 管脚 管脚 类型 描述 I2Cx_SCL I/O

《Linux4.0设备驱动开发详解》笔记--第十五章:Linux I2C核心、总线与设备驱动

15.1 Linux I2C体系结构 I2C核心 I2C核心提供了I2C总线驱动和设备驱动的注册.注销的方法,I2C通信(Algorithm)方法上层的与具体适配器无关代码以及探测设备.检测设备地址的上层代码等 I2C总线驱动 是对I2C体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部 总线驱动包含I2C适配器数据结构i2c_adapter.I2C适配器的Algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数 I2C设备驱动 它是对I

乾坤合一~Linux设备驱动之I2C核心、总线以及设备驱动

我思念的城市已是黄昏 为何我总对你一往情深 曾经给我快乐 也给我创伤 曾经给我希望 也给我绝望 我在遥远的城市 陌生的人群 感觉着你遥远的忧伤 我的幻想 你的忧伤,像我的的绝望,那样漫长,,,,,这是今天的旋律,直入心底~~~~~~~~~~~~~~~~ 在Linux 系统中,I2C 驱动由3 部分组成,即I2C 核心.I2C 总线驱动和I2C 设备驱动,I2C 总线仅仅使用SCL.SDA 这两根信号线就实现了设备之间的数据交互,极大地简化了对硬件资源和PCB 板布线空间的占用 1 Linux的I