47.驱动--i2c

这篇感觉写得最清晰

【驱动】linux下I2C驱动架构全面分析 - Leo.cheng - 博客园

Linux I2C驱动框架(超详细)_JT同学的博客-CSDN博客_i2c驱动

搞了很久,总算有点明白I2C的驱动框架,主要是2.6到3.4的变化把我搞蒙了,写写做点记录给自己看,免得以后忘记。

I2C是属于字符设备类型,最直接简单就是把I2C驱动当成字符设备驱动来写,就是platform_match之后直接在prob函数里注册字符设备驱动,然后像写裸板I2C一样,通过file_operations函数实现功能。好处是不需要花很多时间去了解linux中复杂的I2C子系统的操作方法,缺点是不容易移植,用户需要对I2C的裸板写法很清楚,无法使用内核资源。

在内核2.6里面,I2C驱动分成三部分:

I2C核心:I2C核心提供了I2C总线驱动和设备驱动的注册,注销方法

I2C总线驱动:I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。(知道如何收发,基本就是i2c控制器,adapter所在的地方)

I2C设备驱动:I2C设备驱动(也称为客户驱动)是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。(在这个版本里,该层既包含对用户空间的读写接口,也包含设备资源如设备地址。在3.42版本会进行简化,当只有一种适配器的收把这层拆开成driver和device两部分,就是这里把我搞晕)

1.总线驱动把adapter挂接上链表

2.设备驱动把i2c_driver结构体挂接进链表。通过i2c_probe()函数,把设备地址都用adapter试一下,看设备是否真是存在

/* 1. 分配一个i2c_driver结构体 */
/* 2. 设置i2c_driver结构体 */
static struct i2c_driver at24cxx_driver = {
	.driver = {
		.name	= "at24cxx",
	},
	.attach_adapter = at24cxx_attach,
	.detach_client  = at24cxx_detach,
};

static unsigned short ignore[]      = { I2C_CLIENT_END };
static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /* 地址值是7位 */
                                        /* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用 */

static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
static unsigned short * forces[] = {force_addr, NULL};
										
static struct i2c_client_address_data addr_data = {
	.normal_i2c	= normal_addr,  /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */
	.probe		= ignore,
	.ignore		= ignore,
	//.forces     = forces, /* 强制认为存在这个设备 */
};

到了内核3.42里

I2C核心:I2C核心提供了I2C总线驱动和设备驱动的注册,注销方法(照旧)

I2C总线驱动:I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。(知道如何收发,基本就是i2c控制器,adapter所在的地方,照旧)

I2C的driver驱动:I2C设备驱动(也称为客户驱动)是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。(如果只有一种适配器的收,这里不会再有设备的具体地址信息)

I2C的device驱动:含有设备的具体地址,创建device(如果不知道有哪些适配器,则device跟driver合并,原理跟2.6版本一样)

1.只有一种适配器的时候:

此时,需要匹配的是driver驱动和device驱动。匹配上之后,调用i2c_driver结构体中的prob函数,在prob里注册字符设备,为用户空间的操作提供读写接口

device驱动里:

driver里 

 2.有多种适配器的时候:

则需要先用设备地址把所有adapter都试一下,看设备是否存在。这种跟上面只有一个适配器的情况是不同的。俺就是被这个搞乱,以为是一样的。一般建议使用上面的写法。

static int at24cxx_detect(struct i2c_client *client,
		       struct i2c_board_info *info)
{
	/* 能运行到这里, 表示该addr的设备是存在的
	 * 但是有些设备单凭地址无法分辨(A芯片的地址是0x50, B芯片的地址也是0x50)
	 * 还需要进一步读写I2C设备来分辨是哪款芯片
	 * detect就是用来进一步分辨这个芯片是哪一款,并且设置info->type
	 */
	
	printk("at24cxx_detect : addr = 0x%x\n", client->addr);

	/* 进一步判断是哪一款 */
	
	strlcpy(info->type, "at24c08", I2C_NAME_SIZE);
	return 0;
}

 匹配完成之后就是probe函数的调用,这里就很简单没什么了。

static int major;
static struct class *class;
static struct i2c_client *at24cxx_client;

/* 传入: buf[0] : addr
 * 输出: buf[0] : data
 */
static ssize_t at24cxx_read(struct file * file, char __user *buf, size_t count, loff_t *off)
{
	unsigned char addr, data;
	
	copy_from_user(&addr, buf, 1);
	data = i2c_smbus_read_byte_data(at24cxx_client, addr);
	copy_to_user(buf, &data, 1);
	return 1;
}

/* buf[0] : addr
 * buf[1] : data
 */
static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
{
	unsigned char ker_buf[2];
	unsigned char addr, data;

	copy_from_user(ker_buf, buf, 2);
	addr = ker_buf[0];
	data = ker_buf[1];

	printk("addr = 0x%02x, data = 0x%02x\n", addr, data);

	if (!i2c_smbus_write_byte_data(at24cxx_client, addr, data))
		return 2;
	else
		return -EIO;	
}

static struct file_operations at24cxx_fops = {
	.owner = THIS_MODULE,
	.read  = at24cxx_read,
	.write = at24cxx_write,
};

static int __devinit at24cxx_probe(struct i2c_client *client,
				  const struct i2c_device_id *id)
{
	at24cxx_client = client;
		
	//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "at24cxx", &at24cxx_fops);
	class = class_create(THIS_MODULE, "at24cxx");
	device_create(class, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值