Linux下DM644x设备驱动I2C之设备驱动架构详解

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。

欢迎和大家交流。qq:1037701636 email:200803090209@zjut.comgzzaigcn2012@gmail.com

 

2.I2C之设备驱动开发

在I2C的驱动开发中,另一块主要内容是设备的驱动开发。在这里以tlv320ai23.c来分析主要的驱动架构。在前面的博文中,主要介绍了I2C驱动开发下的总线开发架构,这里主要介绍相关的设备驱动开发。和之前的Bus驱动不同,设备驱动的开发主要由

结构体i2c_client和i2c_driver够成,以下是两个结构体的具体内容
struct i2c_client {
	int id;
	unsigned int flags;		/* div., see below		*/
	unsigned int addr;		/* chip address - NOTE: 7bit 	*/
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits of this char	*/
	/* addr: unsigned int to make lm_sensors i2c-isa adapter work
	  more cleanly. It does not take any more memory space, due to
	  alignment considerations */
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
	struct i2c_driver *driver;	/* and our access routines	*/
	int usage_count;		/* How many accesses currently  */
					/* to the client		*/
	struct device dev;		/* the device structure		*/
	struct list_head list;
	char name[I2C_NAME_SIZE];
	struct completion released;
};
 
struct i2c_driver {
	struct module *owner;
	char name[32];
	int id;
	unsigned int class;
	unsigned int flags;		/* div., see below		*/

	/* Notifies the driver that a new bus has appeared. This routine
	 * can be used by the driver to test if the bus meets its conditions
	 * & seek for the presence of the chip(s) it supports. If found, it 
	 * registers the client(s) that are on the bus to the i2c admin. via
	 * i2c_attach_client.
	 */
	int (*attach_adapter)(struct i2c_adapter *);
	int (*detach_adapter)(struct i2c_adapter *);

	/* tells the driver that a client is about to be deleted & gives it 
	 * the chance to remove its private data. Also, if the client struct
	 * has been dynamically allocated by the driver in the function above,
	 * it must be freed here.
	 */
	int (*detach_client)(struct i2c_client *);
	
	/* a ioctl like command that can be used to perform specific functions
	 * with the device.
	 */
	int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);

	struct device_driver driver;
	struct list_head list;
};
在aic23_i2c_init这个模块初始化函数中
aic23_i2c_init(void)
{
	int err;
	struct i2c_driver *driver = &aic23_i2c_dev.driver;

	driver->owner = THIS_MODULE;
	strlcpy(driver->name, "Audio Codec I2C driver", sizeof(driver->name));
	driver->id = I2C_DRIVERID_EXP0;
	driver->flags = I2C_DF_NOTIFY;
	driver->attach_adapter = aic23_i2c_probe_adapter;   //依附于适配器的探针
	driver->detach_client = aic23_i2c_detach_client;

	err = i2c_add_driver(driver);  //驱动的注册
	if (err) {
		printk(KERN_ERR "Failed to register Audio Codec I2C client.\n");
		return err;
	}
	return 0;
}

我们可以看到,围绕结构体static struct aic23_i2c_param aic23_i2c_dev(存储着设备的i2c_driver和i2c_client)这个全局变量

完成对i2c_driver实例对象driver的初始化,核心在于设置连接适配器的指针函数aic23_i2c_probe_adapter。

调用i2c_add_driver完成driver驱动的注册,内容如下所示:

int i2c_add_driver(struct i2c_driver *driver)
{
	struct list_head   *item;
	struct i2c_adapter *adapter;
	int res = 0;

	down(&core_lists);

	/* add the driver to the list of i2c drivers in the driver core */
	driver->driver.name = driver->name;
	driver->driver.bus = &i2c_bus_type;
	driver->driver.probe = i2c_device_probe;
	driver->driver.remove = i2c_device_remove;

	res = driver_register(&driver->driver);
	if (res)
		goto out_unlock;
	
	list_add_tail(&driver->list,&drivers);
	pr_debug("i2c-core: driver %s registered.\n", driver->name);

	/* now look for instances of driver on our adapters */
	if (driver->flags & I2C_DF_NOTIFY) {
		list_for_each(item,&adapters) {
			adapter = list_entry(item, struct i2c_adapter, list);
			driver->attach_adapter(adapter);   //遍历已经注册的I2C适配器,做连接
		}
	}

在这里遍历前,其实我们已经完成了I2C总线的驱动(注意:为何总线驱动模块比设备驱动模块先加载,因为在这里我们可以看到subsys_initcall(i2c_davinci_init);不是像我们常见的module_init,因为该宏的定义执行优先级比module_init高很多,一个是4一个是6,简单的说在编译时生成的init.text段中前者排在较前面,=,由编译器通过优先级决定,在系统启动初始化调用时,再前面的模块想当然会先被执行加载)

因此这里我们可以获得内核总已经存在的i2c_adapter这个适配器实例,随后调用适配器连接函数driver->attach_adapter = aic23_i2c_probe_adapter; 内容如下:

 

static int
aic23_i2c_attach_client(struct i2c_adapter *adap, int addr)     //芯片地址0x1A
{
	struct aic23_i2c_param *aic23_i2c_if = &aic23_i2c_dev;
	struct i2c_client *client = &aic23_i2c_if->client;
	int err;

	if (client->adapter)     //I2C客服端不存在适配器,则先初始化客服端信息
		return -EBUSY;  /* our client is already attached */

	client->addr = addr;  //0x1A
	client->flags = I2C_CLIENT_ALLOW_USE;   //允许客户端
	client->driver = &aic23_i2c_if->driver;       //aic23_i2c_dev中的driver初始化
	client->adapter = adap;                           //填充设备的相关信息

	err = i2c_attach_client(client);      //将客户端设备添加到适配器列表中并完成设备的注册
	if (err) {
		client->adapter = NULL;
		return err;
	}

	return 0;
}

该函数的核心内容就是讲i2c_client实例的处理(实际为一个I2C设备),完成对该实例的初始化,包括设备自检的地址,设备对应的驱动,已经设备需要连接的适配器,随后调用i2c_attach_client完成设备的注册,内容如下:

int i2c_attach_client(struct i2c_client *client)
{
	struct i2c_adapter *adapter = client->adapter;

	down(&adapter->clist_lock);
	if (__i2c_check_addr(client->adapter, client->addr)) {  //client->addr=0x1a,查看是否已经存在该客服端地址
		up(&adapter->clist_lock);
		return -EBUSY;
	}
	list_add_tail(&client->list,&adapter->clients);  //把新的客户端添加到适配器clinets链表中
	up(&adapter->clist_lock);
	
	if (adapter->client_register)  {
		if (adapter->client_register(client))  {
			dev_warn(&adapter->dev, "warning: client_register "
				"seems to have failed for client %02x\n",
				client->addr);
		}
	}

	dev_dbg(&adapter->dev, "client [%s] registered to adapter\n",
		client->name);

	if (client->flags & I2C_CLIENT_ALLOW_USE)
		client->usage_count = 0;

	client->dev.parent = &client->adapter->dev;
	client->dev.driver = &client->driver->driver;
	client->dev.bus = &i2c_bus_type;
	client->dev.release = &i2c_client_release;
	
	snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
		"%d-%04x", i2c_adapter_id(adapter), client->addr);
	pr_debug("registering %s\n", client->dev.bus_id);
	device_register(&client->dev);         //客户端设备完成注册
	device_create_file(&client->dev, &dev_attr_client_name);  //客户端设备文件节点
	
	return 0;
}

首先使用__i2c_check_addr

static int __i2c_check_addr(struct i2c_adapter *adapter, unsigned int addr)
{
	struct list_head   *item;
	struct i2c_client  *client;

	list_for_each(item,&adapter->clients) {                   //遍历clients链表头
		client = list_entry(item, struct i2c_client, list);
		if (client->addr == addr)       //该适配器下的已经存在该地址的设备,返回一个负值
			return -EBUSY;
	}
	return 0;
}

遍历适配器的client链表,查找已经连接到适配器上的i2_client,即判断该地址的设备是否已经加入到适配器的链表中去,如果没有则通过

list_add_tail家i2c_clinet的链表数据加入到适配器i2c_adapter的链表头中,完成设备向适配器的加载。

最后通过device_register完成i2c_client设备的注册和文件设备节点的创建。

在完成以上的操作之后,就剩下对设备的控制包括读和写

aic23_i2c_read_reg(u8 reg, u8 *val)
{
	int err;
	struct i2c_client *client = &aic23_i2c_dev.client;

	struct i2c_msg msg[1];
	unsigned char data[1];

	if (!client->adapter)
		return -ENODEV;

/*Rishi*/
	data[0]=reg;
	msg->addr = client->addr;
	msg->flags = 0;
	msg->len = 1;
	msg->buf = data;
/*Rishi*/
	//*data = reg;
	err = i2c_transfer(client->adapter, msg, 1);
	if (err >= 0) {
		msg->flags = I2C_M_RD;
		err = i2c_transfer(client->adapter, msg, 1);
	}
	if (err >= 0) {
		*val = *data;
		return 0;
	}

	return err;
}

static int aic23_i2c_write_reg(u8 reg, u8  val)
{
	int err;
	struct i2c_client *client = &aic23_i2c_dev.client;

	struct i2c_msg msg[1];
	unsigned char data[2];

	if (!client->adapter)
		return -ENODEV;

	msg->addr = client->addr;
	msg->flags = 0;
	msg->len = 2;
	msg->buf = data;
	data[0] = (reg<<1)|(val>>8);
	data[1] = val;
	err = i2c_transfer(client->adapter, msg, 1);

	//printk(KERN_INFO "i2c write: error = %d\n", err);

	if (err >= 0)
		return 0;
	else
		printk(KERN_INFO "i2c write: error = %d\n", err);

	return err;
}

完成相关需要传输的信息的初始化内容,调用client对应的适配器完成数据的传输

i2c_transfer-->ret = adap->algo->master_xfer(adap,msgs,num);此后内容进入到总线驱动这一块,包括数据的读和写。详细内容请看前面的总线驱动。
i2c_transferi2c_transferi2c_transfer


 

 


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值