LINUX下的IIC驱动(二)

时间:2021-03-05 23:34:03

这篇文章主要以友善的实验板为例介绍一下,他们所说的IIC下的EEPROM驱动吧。因为我个人觉得不是很好,可能会给初学者带来一下迷惑,纯属个人观点!

#include <stdio.h>
#include <fcntl.h>
#include <getopt.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "24cXX.h"

#define usage_if(a) do { do_usage_if( a , __LINE__); } while(0);
void do_usage_if(int b, int line)
{
	const static char *eeprog_usage = 
		"I2C-24C08(256 bytes) Read/Write Program, ONLY FOR TEST!\n"
		"FriendlyARM Computer Tech. 2009\n";
	if(!b)
		return;
	fprintf(stderr, "%s\n[line %d]\n", eeprog_usage, line);
	exit(1);
}


#define die_if(a, msg) do { do_die_if( a , msg, __LINE__); } while(0);
void do_die_if(int b, char* msg, int line)
{
	if(!b)
		return;
	fprintf(stderr, "Error at line %d: %s\n", line, msg);
	fprintf(stderr, "	sysmsg: %s\n", strerror(errno));
	exit(1);
}


static int read_from_eeprom(struct eeprom *e, int addr, int size)
{
	int ch, i;
	for(i = 0; i < size; ++i, ++addr)
	{
		die_if((ch = eeprom_read_byte(e, addr)) < 0, "read error");
		if( (i % 16) == 0 ) 
			printf("\n %.4x|  ", addr);
		else if( (i % 8) == 0 ) 
			printf("  ");
		printf("%.2x ", ch);
		fflush(stdout);
	}
	fprintf(stderr, "\n\n");
	return 0;
}

static int write_to_eeprom(struct eeprom *e, int addr)
{
	int i;
	for(i=0, addr=0; i<256; i++, addr++)
	{
		if( (i % 16) == 0 ) 
			printf("\n %.4x|  ", addr);
		else if( (i % 8) == 0 ) 
			printf("  ");
		printf("%.2x ", i);
		fflush(stdout);
		die_if(eeprom_write_byte(e, addr, i), "write error");
	}
	fprintf(stderr, "\n\n");
	return 0;
}

int main(int argc, char** argv)
{
	struct eeprom e;
	int op;

	op = 0;

	usage_if(argc != 2 || argv[1][0] != '-' || argv[1][2] != '\0');
	op = argv[1][1];

	fprintf(stderr, "Open /dev/i2c/0 with 8bit mode\n");
	die_if(eeprom_open("/dev/i2c/0", 0x50, EEPROM_TYPE_8BIT_ADDR, &e) < 0, 
			"unable to open eeprom device file "
			"(check that the file exists and that it's readable)");
	switch(op)
	{
	case 'r':
		fprintf(stderr, "  Reading 256 bytes from 0x0\n");
		read_from_eeprom(&e, 0, 256);
		break;
	case 'w':
		fprintf(stderr, "  Writing 0x00-0xff into 24C08 \n");
		write_to_eeprom(&e, 0);
		break;
	default:
		usage_if(1);
		exit(1);
	}
	eeprom_close(&e);

	return 0;
}

上面的程序,是友善提供的eeprom的测试程序,而且只是里面的包括主函数的程序,完整的程序我给个链接吧,因为可能有人手上没有友善开发板的程序。链接:http://download.csdn.net/detail/xie0812/5862347,你可以这里下载,哈哈,因为要完全理解还得下载下来看看的。要是你完全看不懂,请先阅读《Unix环境高级编程》这本书了,这里就假设都能看懂了!

看应用程序嘛,当然要从main函数开始了,首先是打开设备了,这里的eeprom_open函数就是了,设备的名字就是/dev/i2c/0了,然后紧接着就是0x50,这是eeprom的地址,真真的地址是0xa0,当然跟你对eeprom的外部电路的接法有关了。那为什么这里是0x50呢,啊,原来在驱动里边会把地址向左移动一位,所以真真的地址还是0xa0了。这里就有例外的一个问题了,不是说设备是唯一的,怎么还要地址了?是啊,这个问题我也困惑了好久,我追寻这个测试程序对应的驱动程序,发现根本不是友善提供的文档上说的地方,它所说的那个驱动是适配器的驱动程序,也就是说是IIC总线的驱动程序,可能是说错了吧。既然不是,再找,发现原来真真的驱动程序是i2c-dev.c,这个文件在linux源文件的driver目录下的iic目录下。下面就简单地介绍一下i2c-dev.c。

i2c-dev.c实现的一个i2c_client(就是IIC的设备驱动)是虚拟的、临时的,随着设备文件的打开而产生,并随设备文件的关闭而撤销,并没有被添加到i2c_adapter的client链表中。i2c-dev.c针对每个I2C适配器生产一个主设备号为89的设备文件,linux中一切都是文件的思想嘛。实现了i2c-driver的成员函数以及文件操作接口。

static int i2cdev_open(struct inode *inode, struct file *file)
{
	unsigned int minor = iminor(inode);
	struct i2c_client *client;
	struct i2c_adapter *adap;
	struct i2c_dev *i2c_dev;
	int ret = 0;

	lock_kernel();
	i2c_dev = i2c_dev_get_by_minor(minor);
	if (!i2c_dev) {
		ret = -ENODEV;
		goto out;
	}

	adap = i2c_get_adapter(i2c_dev->adap->nr);
	if (!adap) {
		ret = -ENODEV;
		goto out;
	}

	/* This creates an anonymous i2c_client, which may later be
	 * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
	 *
	 * This client is ** NEVER REGISTERED ** with the driver model
	 * or I2C core code!!  It just holds private copies of addressing
	 * information and maybe a PEC flag.
	 */
	client = kzalloc(sizeof(*client), GFP_KERNEL);
	if (!client) {
		i2c_put_adapter(adap);
		ret = -ENOMEM;
		goto out;
	}
	snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
	client->driver = &i2cdev_driver;

	client->adapter = adap;
	file->private_data = client;

out:
	unlock_kernel();
	return ret;
}
这是i2c-dev.c中的open函数,也就是测试程序中对应的open系统调用了。分析这个函数可以知道,当调用这个函数时,它首先创建一个i2c-client结构,注册了一下 i2c-client结构中的i2c_driver以及适配器,通过注释可以看到真真的设备的地址是在icotl调用中完成的。要是你仔细查看了测试程序中的eeprom_open函数,会发现其实它是封装了open和icotl系统调用的。这也说明了它的确是虚拟的设备文件。当然这里提到的两个结构体,你可能不了解,没关系,我们下面的内容会讲到。谈到这里,大家应该明白了一件事,友善的实验板上根本没实现eeprom的驱动,只是利用一个虚拟的设备文件做了硬件的测试。那我们要真真写一个iic设备的驱动程序应该怎么写了,是啊,我们要学习iic驱动,这样肯定是不能算是明白了,所以我们需要进一步的探索,既然上了路,就应该勇敢的走下去!