S3C2440驱动简析——I2C驱动

      这次来研究内核自带I2C驱动的代码,在深入代码之前,首先简单了解一下I2C核心数据结构的相互关系。由此来展开,也许能够对驱动代码有更好地理解。 软件数据结构的设计、数据结构之间的关系就至少应该描述硬件物理连接的这种组织关系。Linux的i2c 框架中各个部分的关系如下图所示:

 

struct i2c_driver {
char name[32];          //最大32字节的字符串
int id;                       //id 可选0xf000 到0xffff 中的任一数值
unsigned int flags;     //一般直接设置为I2C_DF_NOTIFY
int (*attach_adapter)(struct i2c_adapter *);
int (*detach_client)(struct i2c_client *);
int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);
void (*inc_use)(struct i2c_client *client);
void (*dec_use)(struct i2c_client *client);
};

attach_adapter 回调函数在安装i2c 设备驱动程序模块时、或者在安装i2c 适配器驱动
程序模块时被调用,用于检测、认领设备并为设备分配i2c_client 数据结构。

 

detach_client 方法在卸载适配器或设备驱动程序模块时被调用,用于从总线上注销设备、并释放i2c_client 及相应的私有数据结构。

 

inc_use 和dec_use 所指向的函数用于改变i2c 设备驱动程序模块的引用计数。注意不要直接调用i2c_driver数据结构中的这两个方法,而要通过如下函数调用路径:
i2c_use_client > i2c_inc_use_client > inc_use
i2c_release_client > i2c_dec_use_client > dec_use

 

 

2. 一个 i2c 设备由i2c_client 数据结构进行描述:

 

struct i2c_client {
char name[32];
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 */
struct i2c_adapter *adapter; /* the adapter we sit on */
struct i2c_driver *driver; /* and our access routines */
void *data; /* for the clients */
int usage_count; /* How many accesses currently to the client */
};

       在安装适配器或者设备的驱动程序时通过设备驱动程序 i2c_driver 中的attach_adapter 函数检测设备地址。如果检测成功则调用设备驱动程序提供的回调函数创建描述设备的i2c_client 数据结构,并将其中的driver指针指向设备驱动程序的i2c_driver 数据结构。这样将来就可以使用i2c_driver 中的注销设备和控制引用计数的方法了。



 3. 一个 i2c 适配器由i2c_adapter 数据结构描述:

struct i2c_adapter {
char name[32];
unsigned int id; /* == is algo->id | hwdep.struct->id, for registered values see below */
struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
void (*inc_use)(struct i2c_adapter *);
void (*dec_use)(struct i2c_adapter *);
int (*client_register)(struct i2c_client *);
int (*client_unregister)(struct i2c_client *);
void *data; /* private data for the adapter */
struct semaphore lock;
unsigned int flags; /* flags specifying div. data */
struct i2c_client *clients[I2C_CLIENT_MAX];
int client_count;
int timeout;
int retries;
#ifdef CONFIG_PROC_FS
/* No need to set this when you initialize the adapter */
int inode;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)
struct proc_dir_entry *proc_entry;
#endif
#endif /* def CONFIG_PROC_FS */
};

       在 i2c_adapter 数据结构中设计了clients 指针数组,指向该总线上每个设备的i2c_client 数据结构。由于一条i2c 总线上最多只有I2C_CLENT_MAX 个设备,所以可以使用静态数组(题外话,如果相关数据结构的个数是未知的,链表显然是更好的选择)。lock 信号量用于实现对i2c 总线的互斥访问:在访问i2c 总线上的任一设备期间当前进程必须首先获得该信号量,并且在阻塞等待i2c 操作完成期间不释放。



4.具体 i2c 适配器的通信方法由i2c_algorithm 数据结构进行描述:

struct i2c_algorithm {
char name[32]; /* textual description */
unsigned int id;
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);
int (*slave_send)(struct i2c_adapter *,char*,int);
int (*slave_recv)(struct i2c_adapter *,char*,int);
int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long);
u32 (*functionality) (struct i2c_adapter *);
};

master_xfer/smbus_xfer 指针指向i2c 适配器驱动程序模块实现的i2c 通信协议或者smbus 通信协议。由下文分析可见在用户进程通过i2c-dev 提供的/dev/i2c/%d 设备节点访问i2c 设备时,最终是通过调用master_xfer 或者smbus_xfer 指向的方法完成的。

slave_send/recv 函数用于实现当i2c 适配器扮演slave 角色时的传输方法。 

由于内容统一的关系,在一篇文章里又记录数据结构,又贴出驱动代码,这样个人感觉比较乱。所以我还是选择把I2C驱动这一块内容分成三部分,本文讨论数据结构为第一部分,在接下来的I2C驱动文章《S3C2440驱动简析——I2C驱动(2)》里,将会深入i2c-dev.c驱动代码。第三部分是深入i2c-core.c,以了解实际底层操作的过程。

紧接上一篇博文的I2C主要数据结构的介绍,现在就让我们真正地进入I2C驱动的代码里面,领略一下这个稍微复杂点点的驱动。由于代码已经有一定长度,再也不能像之前那样整段copy,然后直接分析了。为了鄙人以后能够更好地翻阅自己的笔记,也为了比我更菜的小菜鸟考虑(应该没有的,呵呵),这次还是采取按照代码逻辑顺序,讲到哪,代码就贴到哪~ go go go~~~

 

     看到i2c-dev.c 600多行的驱动代码,对于一般初学者来说还是够呛的,不过没关系,咱们谨记看驱动程序,第一要务找到入口和出口!__init 和 __exit 代码如下:



static int __init i2c_dev_init(void)
{
	int res;

	printk(KERN_INFO "i2c /dev entries driver/n");

	res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
	if (res)
		goto out;

	i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
	if (IS_ERR(i2c_dev_class)) {
		res = PTR_ERR(i2c_dev_class);
		goto out_unreg_chrdev;
	}

	res = i2c_add_driver(&i2cdev_driver);
	if (res)
		goto out_unreg_class;

	return 0;

out_unreg_class:
	class_destroy(i2c_dev_class);
out_unreg_chrdev:
	unregister_chrdev(I2C_MAJOR, "i2c");
out:
	printk(KERN_ERR "%s: Driver Initialisation failed/n", __FILE__);
	return res;
}

static void __exit i2c_dev_exit(void)
{
	i2c_del_driver(&i2cdev_driver);
	class_destroy(i2c_dev_class);
	unregister_chrdev(I2C_MAJOR,"i2c");
}

i2c_dev_init :

这个初始函数主要做了三件事。

1.res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);

  显然,这里注册一个字符设备,并且链接到用户空间的操作函数所在的结构体i2cdev_fops。

 

2.i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");

  class_create函数对于我们来说是一个新鲜事物,需要研究一下。Linux内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

 

3.res = i2c_add_driver(&i2cdev_driver);

  注册设备,描述设备的结构体i2cdev_driver在代码其他地方定义如下:

static struct i2c_driver i2cdev_driver = {
	.driver = {
		.name	= "dev_driver",
	},
	.attach_adapter	= i2cdev_attach_adapter,
	.detach_adapter	= i2cdev_detach_adapter,
};

这个结构体主要包括了两个函数i2cdev_attach_adapter 和i2cdev_detach_adapter。其中i2cdev_attach_adapter负责链接到I2C适配器上,而后者反之。

先讨论i2cdev_attach_adapter,代码如下:

static int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
    struct i2c_dev *i2c_dev;
    int res;

    i2c_dev = get_free_i2c_dev(adap);
    if (IS_ERR(i2c_dev))
        return PTR_ERR(i2c_dev);

    /* register this i2c device with the driver core */
    i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
                     MKDEV(I2C_MAJOR, adap->nr), NULL,
                     "i2c-%d", adap->nr);
    if (IS_ERR(i2c_dev->dev)) {
        res = PTR_ERR(i2c_dev->dev);
        goto error;
    }
    res = device_create_file(i2c_dev->dev, &dev_attr_name);
    if (res)
        goto error_destroy;

    pr_debug("i2c-dev: adapter [%s] registered as minor %d/n",
         adap->name, adap->nr);
    return 0;
error_destroy:
    device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
error:
    return_i2c_dev(i2c_dev);
    return res;
}

函数首先传入一个i2c_adapter结构体指针,该指针正是代表一个i2c适配器,函数主要做了3件事情。

1.i2c_dev = get_free_i2c_dev(adap);

  传入的adap适配器指针,正是返回的i2c_dev结构中,adap成员指向传入的适配器指针。

2.i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
         MKDEV(I2C_MAJOR, adap->nr), NULL,"i2c-%d", adap->nr);

  第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是设备名称,第五个参数是从设备号。创建一个模块并链接到sysfs。

3.res = device_create_file(i2c_dev->dev, &dev_attr_name);

  创建设备的属性文件。

好了,再来看一下i2cdev_detach_adapter函数。

static int i2cdev_detach_adapter(struct i2c_adapter *adap)
{
	struct i2c_dev *i2c_dev;

	i2c_dev = i2c_dev_get_by_minor(adap->nr);
	if (!i2c_dev) /* attach_adapter must have failed */
		return 0;

	device_remove_file(i2c_dev->dev, &dev_attr_name);
	return_i2c_dev(i2c_dev);
	device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));

	pr_debug("i2c-dev: adapter [%s] unregistered/n", adap->name);
	return 0;
}

该函数一共做了4件事情来“摆脱”适配器。

1.i2c_dev = i2c_dev_get_by_minor(adap->nr);

  通过次设备号找到设备,并由i2c_dev结构体指针带回,供接下来的函数使用。

 

2.device_remove_file(i2c_dev->dev, &dev_attr_name);

  对应device_create_file,移除之。

 

3.return_i2c_dev(i2c_dev);

  将传入的设备从设备列表中删除,并释放i2c_dev。

 

4.device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));

  连同设备对应的类(实则是一个高层视图,忽略底层操作的东东)一并删除。

到此为止,驱动的初始化部分算是完成了,回过头来看一下退出函数__exit吧。

static void __exit i2c_dev_exit(void)
{
	i2c_del_driver(&i2cdev_driver);
	class_destroy(i2c_dev_class);
	unregister_chrdev(I2C_MAJOR,"i2c");
}

只有三个语句,其执行顺序跟初始化函数__init的注册顺序刚好倒过来,具体里面的函数是怎样执行的这里就不再赘述了。

至于i2cdev_fops里的提供给用户空间函数留待下一篇博文继续探讨。在此仅贴出其结构体代码如下:

static const struct file_operations i2cdev_fops = {
    .owner        = THIS_MODULE,
    .llseek        = no_llseek,
    .read        = i2cdev_read,
    .write        = i2cdev_write,
    .unlocked_ioctl    = i2cdev_ioctl,
    .open        = i2cdev_open,
    .release    = i2cdev_release,
};

书接上回,在讨论完i2c设备、i2c适配器等初始化和删除相应驱动的程序后,我们在这个小节把注意力放在file_operations里面的几个函数操作上,先贴上file_operations结构体代码,让我们先看看其包含了哪几个函数。

static const struct file_operations i2cdev_fops = {
	.owner		= THIS_MODULE,
	.llseek		= no_llseek,
	.read		= i2cdev_read,
	.write		= i2cdev_write,
	.unlocked_ioctl	= i2cdev_ioctl,
	.open		= i2cdev_open,
	.release	= i2cdev_release,
};

这个结构体想必大家对其结构都相当清晰啦,由此可见,i2c驱动为用户空间提供的操作函数包括:

1.no_llseek

2.i2cdev_read

3.i2cdev_write

4.i2cdev_ioctl

5.i2cdev_open

6.i2cdev_release

 

下面就对它们逐一进行分析:

1.no_llseek

loff_t no_llseek(struct file *file, loff_t offset, int origin)
{
    return -ESPIPE;
}

从结构体i2cdev_fops 的成员名字llseek推测,其作用应该是改变当前I2C器件读写的位置,而现在正如大家所见,驱动程序并未实现这功能。

 

2.i2cdev_read

static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
		loff_t *offset)
{
	char *tmp;
	int ret;

	struct i2c_client *client = file->private_data;

	if (count > 8192)
		count = 8192;

	tmp = kmalloc(count, GFP_KERNEL);
	if (tmp == NULL)
		return -ENOMEM;

	pr_debug("i2c-dev: i2c-%d reading %zu bytes./n",
		iminor(file->f_path.dentry->d_inode), count);

	ret = i2c_master_recv(client, tmp, count);
	if (ret >= 0)
		ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
	kfree(tmp);
	return ret;
}

read函数主要做的事情有:

(1)struct i2c_client *client = file->private_data;

       通过参数传入的file(在上一篇博文有阐述)而找到像对应的一个i2c设备,接下来函数就是对该设备进行操作。

(2)ret = i2c_master_recv(client, tmp, count);

       其中i2c_master_recv函数在i2c-core.c 中实现,为了使本文结构更加清晰,故把跟硬件打交道的代码文件i2c-core.c 放在后面的博文分析。在这里,我们只需简单地理解为i2c_master_recv函数的作用是通过调用上文提及的i2c设备,并从其中提取count 字节的内容存到tmp 缓冲区里。

(3)ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;

       若无异常情况发生,就会调用咱们非常熟悉的copy_to_user 函数,这个函数在这里就不赘述了,建议还没掌握的朋友查找相关资料,或者我前面的博文都有提及过,这个是基础吖!


3.i2cdev_write


static ssize_t i2cdev_write(struct file *file, const char __user *buf,
		size_t count, loff_t *offset)
{
	int ret;
	char *tmp;
	struct i2c_client *client = file->private_data;

	if (count > 8192)
		count = 8192;

	tmp = memdup_user(buf, count);
	if (IS_ERR(tmp))
		return PTR_ERR(tmp);

	pr_debug("i2c-dev: i2c-%d writing %zu bytes./n",
		iminor(file->f_path.dentry->d_inode), count);

	ret = i2c_master_send(client, tmp, count);
	kfree(tmp);
	return ret;
}

write函数又做了什么事情呢?

(1)struct i2c_client *client = file->private_data;

       见上文read函数中的解释。

(2)tmp = memdup_user(buf, count);

       memdup_user 函数在Util.c 中实现,源代码如下

void *memdup_user(const void __user *src, size_t len)
{
	void *p;

	/*
	 * Always use GFP_KERNEL, since copy_from_user() can sleep and
	 * cause pagefault, which makes it pointless to use GFP_NOFS
	 * or GFP_ATOMIC.
	 */
	p = kmalloc_track_caller(len, GFP_KERNEL);
	if (!p)
		return ERR_PTR(-ENOMEM);

	if (copy_from_user(p, src, len)) {
		kfree(p);
		return ERR_PTR(-EFAULT);
	}

	return p;
}

  函数的作用一目了然,就是把指针src 所指向长度为len 的内容中copy_from_user 到函数返回的指针。

(3)ret = i2c_master_send(client, tmp, count);

       把缓存器tmp 里的内容发送到设备client 。

4.i2cdev_ioctl

static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct i2c_client *client = file->private_data;
	unsigned long funcs;

	dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx/n",
		cmd, arg);

	switch (cmd) {
	case I2C_SLAVE:
	case I2C_SLAVE_FORCE:
		/* NOTE:  devices set up to work with "new style" drivers
		 * can't use I2C_SLAVE, even when the device node is not
		 * bound to a driver.  Only I2C_SLAVE_FORCE will work.
		 *
		 * Setting the PEC flag here won't affect kernel drivers,
		 * which will be using the i2c_client node registered with
		 * the driver model core.  Likewise, when that client has
		 * the PEC flag already set, the i2c-dev driver won't see
		 * (or use) this setting.
		 */
		if ((arg > 0x3ff) ||
		    (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
			return -EINVAL;
		if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
			return -EBUSY;
		/* REVISIT: address could become busy later */
		client->addr = arg;
		return 0;
	case I2C_TENBIT:
		if (arg)
			client->flags |= I2C_M_TEN;
		else
			client->flags &= ~I2C_M_TEN;
		return 0;
	case I2C_PEC:
		if (arg)
			client->flags |= I2C_CLIENT_PEC;
		else
			client->flags &= ~I2C_CLIENT_PEC;
		return 0;
	case I2C_FUNCS:
		funcs = i2c_get_functionality(client->adapter);
		return put_user(funcs, (unsigned long __user *)arg);

	case I2C_RDWR:
		return i2cdev_ioctl_rdrw(client, arg);

	case I2C_SMBUS:
		return i2cdev_ioctl_smbus(client, arg);

	case I2C_RETRIES:
		client->adapter->retries = arg;
		break;
	case I2C_TIMEOUT:
		/* For historical reasons, user-space sets the timeout
		 * value in units of 10 ms.
		 */
		client->adapter->timeout = msecs_to_jiffies(arg * 10);
		break;
	default:
		/* NOTE:  returning a fault code here could cause trouble
		 * in buggy userspace code.  Some old kernel bugs returned
		 * zero in this case, and userspace code might accidentally
		 * have depended on that bug.
		 */
		return -ENOTTY;
	}
	return 0;
}

由于i2c适配器的设备节点代表的是整条i2c总线,所以在对其进行具体的文件系统操作之前还必须指明
待访问设备的总线地址。指明地址的操作通过ioctl 系统调用完成的,它最终调用设备方法i2cdev_ioctl。ioctl函数还是采用我们非常熟悉的switch-case逻辑结构,其各种命令对应的操作分析如下

case I2C_SLAVE:
case I2C_SLAVE_FORCE: 设置从设备地址

 

case I2C_TENBIT: 设置7bit 地址 or 10bit 地址

 

case I2C_PEC: 如果I2C_PEC != 0 , to use PEC with SMBus

 

case I2C_FUNCS: 返回控制器算法所支持的传输种类

 

case I2C_RDWR: 执行i2cdev_ioctl_rdrw函数,代码如下

static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
		unsigned long arg)
{
	struct i2c_rdwr_ioctl_data rdwr_arg;
	struct i2c_msg *rdwr_pa;
	u8 __user **data_ptrs;
	int i, res;

	if (copy_from_user(&rdwr_arg,
			   (struct i2c_rdwr_ioctl_data __user *)arg,
			   sizeof(rdwr_arg)))
		return -EFAULT;

	/* Put an arbitrary limit on the number of messages that can
	 * be sent at once */
	if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
		return -EINVAL;

	rdwr_pa = (struct i2c_msg *)
		kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
		GFP_KERNEL);
	if (!rdwr_pa)
		return -ENOMEM;

	if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
			   rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
		kfree(rdwr_pa);
		return -EFAULT;
	}

	data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);
	if (data_ptrs == NULL) {
		kfree(rdwr_pa);
		return -ENOMEM;
	}

	res = 0;
	for (i = 0; i < rdwr_arg.nmsgs; i++) {
		/* Limit the size of the message to a sane amount;
		 * and don't let length change either. */
		if ((rdwr_pa[i].len > 8192) ||
		    (rdwr_pa[i].flags & I2C_M_RECV_LEN)) {
			res = -EINVAL;
			break;
		}
		data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;
		rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
		if (rdwr_pa[i].buf == NULL) {
			res = -ENOMEM;
			break;
		}
		if (copy_from_user(rdwr_pa[i].buf, data_ptrs[i],
				   rdwr_pa[i].len)) {
				++i; /* Needs to be kfreed too */
				res = -EFAULT;
			break;
		}
	}
	if (res < 0) {
		int j;
		for (j = 0; j < i; ++j)
			kfree(rdwr_pa[j].buf);
		kfree(data_ptrs);
		kfree(rdwr_pa);
		return res;
	}

	res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
	while (i-- > 0) {
		if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {
			if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,
					 rdwr_pa[i].len))
				res = -EFAULT;
		}
		kfree(rdwr_pa[i].buf);
	}
	kfree(data_ptrs);
	kfree(rdwr_pa);
	return res;
}

整个函数主要做了以下几件事

(1)copy_from_user(&rdwr_arg,
                              (struct i2c_rdwr_ioctl_data __user *)arg,
                              sizeof(rdwr_arg))

       获得用户传进来的命令,并存放在rdwr_arg。

(2)copy_from_user(rdwr_pa, rdwr_arg.msgs,
                              rdwr_arg.nmsgs * sizeof(struct i2c_msg))

       把指针rdwr_pa指向结构体rdwr_arg的成员msgs。

(3)整个for循环实质就是把要进行传输的每个msg的信息copy到字符串指针data_ptrs当中。

(4)res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);

       i2c_transfer ()函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_transfer()函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithmmaster_xfer()函数真正驱动硬件流程。至于i2c_algorithm和master_xfer等在接下来的博文介绍。

(5)while循环里把data_ptrs所存的信息统统copy_to_user。

case I2C_SMBUS: 执行i2cdev_ioctl_smbus 函数,主要调用i2c_smbus_xfer,函数在i2c-core.c 里实现。在这里我们先简单理解此函数作用为选择smbus 通信协议。

case I2C_RETRIES: 设置重发次数

case I2C_TIMEOUT: 设置超时时间,设定为 arg * 10 (ms)

呼呼~ioctl函数总算带过了,具体没有深入的地方,均是涉及到i2c-core.c 和数据结构i2c_algorithm的地方,这两个东东留待接下来的博文再来收拾。

5.i2cdev_open

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;

	i2c_dev = i2c_dev_get_by_minor(minor);
	if (!i2c_dev)
		return -ENODEV;

	adap = i2c_get_adapter(i2c_dev->adap->nr);
	if (!adap)
		return -ENODEV;

	/* 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);
		return -ENOMEM;
	}
	snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
	client->driver = &i2cdev_driver;

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

	return 0;
}

最后两个函数来点轻松的,一眼扫下来,就是一些注册、初始化等工作,唯一要注意地方是这个open函数比之前所介绍的其他驱动多了一点绑定的工作,如:

client->driver = &i2cdev_driver;

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

至于其用意,不记得的朋友建议翻看本i2c驱动系列的第一篇博文。

 

6.i2cdev_release

static int i2cdev_release(struct inode *inode, struct file *file)
{
	struct i2c_client *client = file->private_data;

	i2c_put_adapter(client->adapter);
	kfree(client);
	file->private_data = NULL;

	return 0;
}

到此为止,i2c-dev.c 的驱动代码分析暂告一段落,由于本人水平所限,不能很深刻、到位的写出这份驱动代码的分析,还请各位看客多多提点。内容错漏,请严厉指出!接下来本系列的第4篇博文将讲述i2c-core.c 和数据结构i2c_algorithm,敬请关注!

说时迟,那时快,马上进入I2C驱动的最后一个小节了,在这个小节里,我们主要探讨i2c_algorithm 数据结构和i2c-core.c 的一些主要函数及其作用。有鉴于i2c-core.c 代码达2000行,所以本文仅对导出的函数(EXPORT_SYMBOL)进行简单注释,其它函数想必也是为前者服务的啦。好,马上进入正题:

 

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 *);
};

一个 i2c 适配器上的i2c 总线通信方法由其驱动程序提供的i2c_algorithm 数据结构描述,由algo 指针指向。i2c_algorithm 数据结构即为i2c_adapter 数据结构与具体i2c 适配器的总线通信方法的中间层,正是这个中间层使得上层的i2c 框架代码与与具体i2c 适配器的总线通信方法无关,从而实现了i2c 框架的
可移植性和重用性。当安装具体i2c 适配器的驱动程序时由相应驱动程序实现具体的i2c_algorithm 数据结构,其中的函数指针指向操作具体i2c 适配器的代码。

 

master_xfer/smbus_xfer 指针指向i2c 适配器驱动程序模块实现的i2c 通信协议或者smbus 通信协议。在用户进程通过i2c-dev 提供的/dev/i2c/%d 设备节点访问i2c 设备时,最终是通过调用
master_xfer 或者smbus_xfer 指向的方法完成的。

 

 

i2c-core.c

i2c.h 和i2c-core.c 为i2c 框架的主体,提供了核心数据结构的定义、i2c 适配器驱动和设备驱
动的注册、注销管理,i2c 通信方法上层的、与具体适配器无关的代码、检测设备地址的上层代码等;
i2c-dev.c 用于创建i2c 适配器的/dev/i2c/%d 设备节点,提供i2c 设备访问方法等。

 

下面介绍其主要的函数

函数i2c_init

static int __init i2c_init(void)
{
	int retval;

	retval = bus_register(&i2c_bus_type);
	if (retval)
		return retval;
#ifdef CONFIG_I2C_COMPAT
	i2c_adapter_compat_class = class_compat_register("i2c-adapter");
	if (!i2c_adapter_compat_class) {
		retval = -ENOMEM;
		goto bus_err;
	}
#endif
	retval = i2c_add_driver(&dummy_driver);
	if (retval)
		goto class_err;
	return 0;

class_err:
#ifdef CONFIG_I2C_COMPAT
	class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
	bus_unregister(&i2c_bus_type);
	return retval;
}

主要做了以下几件事情:

1.i2c_add_driver(&dummy_driver);
   注册i2c总线

2.class_compat_register("i2c-adapter");

   注册一个可兼容的类

3.i2c_add_driver(&dummy_driver);
   加载驱动

 

函数i2c_exit

static void __exit i2c_exit(void)
{
	i2c_del_driver(&dummy_driver);
#ifdef CONFIG_I2C_COMPAT
	class_compat_unregister(i2c_adapter_compat_class);
#endif
	bus_unregister(&i2c_bus_type);
}

这个就不解释了呀~~

 

结构体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,
	.suspend	= i2c_device_suspend,
	.resume		= i2c_device_resume,
};
本结构体被导出,其里面包含了6个函数,其作用看名字就能猜到个大概。下面贴出以上6个函数的代码。
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;

    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;
}

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;
}

static int i2c_device_remove(struct device *dev)
{
    struct i2c_client    *client = i2c_verify_client(dev);
    struct i2c_driver    *driver;
    int            status;

    if (!client || !dev->driver)
        return 0;

    driver = to_i2c_driver(dev->driver);
    if (driver->remove) {
        dev_dbg(dev, "remove/n");
        status = driver->remove(client);
    } else {
        dev->driver = NULL;
        status = 0;
    }
    if (status == 0)
        client->driver = NULL;
    return status;
}

static void i2c_device_shutdown(struct device *dev)
{
    struct i2c_client *client = i2c_verify_client(dev);
    struct i2c_driver *driver;

    if (!client || !dev->driver)
        return;
    driver = to_i2c_driver(dev->driver);
    if (driver->shutdown)
        driver->shutdown(client);
}

static int i2c_device_suspend(struct device *dev, pm_message_t mesg)
{
    struct i2c_client *client = i2c_verify_client(dev);
    struct i2c_driver *driver;

    if (!client || !dev->driver)
        return 0;
    driver = to_i2c_driver(dev->driver);
    if (!driver->suspend)
        return 0;
    return driver->suspend(client, mesg);
}

static int i2c_device_resume(struct device *dev)
{
    struct i2c_client *client = i2c_verify_client(dev);
    struct i2c_driver *driver;

    if (!client || !dev->driver)
        return 0;
    driver = to_i2c_driver(dev->driver);
    if (!driver->resume)
        return 0;
    return driver->resume(client);
}

函数i2c_add_adapter

int i2c_add_adapter(struct i2c_adapter *adapter)
{
	int	id, res = 0;

retry:
	if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
		return -ENOMEM;

	mutex_lock(&core_lock);
	/* "above" here means "above or equal to", sigh */
	res = idr_get_new_above(&i2c_adapter_idr, adapter,
				__i2c_first_dynamic_bus_num, &id);
	mutex_unlock(&core_lock);

	if (res < 0) {
		if (res == -EAGAIN)
			goto retry;
		return res;
	}

	adapter->nr = id;
	return i2c_register_adapter(adapter);
}

函数主要作用是添加一个适配器

1.idr_get_new_above(&i2c_adapter_idr, adapter,
                               __i2c_first_dynamic_bus_num, &id);

   确认是否还能分配得到适配器id,该操作不能被打断。

2.i2c_register_adapter(adapter);
   注册一个适配器

 

函数i2c_del_adapter

int i2c_del_adapter(struct i2c_adapter *adap)
{
	int res = 0;
	struct i2c_adapter *found;
	struct i2c_client *client, *next;

	/* First make sure that this adapter was ever added */
	mutex_lock(&core_lock);
	found = idr_find(&i2c_adapter_idr, adap->nr);
	mutex_unlock(&core_lock);
	if (found != adap) {
		pr_debug("i2c-core: attempting to delete unregistered "
			 "adapter [%s]/n", adap->name);
		return -EINVAL;
	}

	/* Tell drivers about this removal */
	mutex_lock(&core_lock);
	res = bus_for_each_drv(&i2c_bus_type, NULL, adap,
			       i2c_do_del_adapter);
	mutex_unlock(&core_lock);
	if (res)
		return res;

	/* Remove devices instantiated from sysfs */
	list_for_each_entry_safe(client, next, &userspace_devices, detected) {
		if (client->adapter == adap) {
			dev_dbg(&adap->dev, "Removing %s at 0x%x/n",
				client->name, client->addr);
			list_del(&client->detected);
			i2c_unregister_device(client);
		}
	}

	/* Detach any active clients. This can't fail, thus we do not
	   checking the returned value. */
	res = device_for_each_child(&adap->dev, NULL, __unregister_client);

#ifdef CONFIG_I2C_COMPAT
	class_compat_remove_link(i2c_adapter_compat_class, &adap->dev,
				 adap->dev.parent);
#endif

	/* clean up the sysfs representation */
	init_completion(&adap->dev_released);
	device_unregister(&adap->dev);

	/* wait for sysfs to drop all references */
	wait_for_completion(&adap->dev_released);

	/* free bus id */
	mutex_lock(&core_lock);
	idr_remove(&i2c_adapter_idr, adap->nr);
	mutex_unlock(&core_lock);

	dev_dbg(&adap->dev, "adapter [%s] unregistered/n", adap->name);

	/* Clear the device structure in case this adapter is ever going to be
	   added again */
	memset(&adap->dev, 0, sizeof(adap->dev));

	return 0;
}

对应于i2c_add_adapter,此函数删除一个适配器。

 

函数 i2c_register_driver

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	int res;

	/* Can't register until after driver model init */
	if (unlikely(WARN_ON(!i2c_bus_type.p)))
		return -EAGAIN;

	/* add the driver to the list of i2c drivers in the driver core */
	driver->driver.owner = owner;
	driver->driver.bus = &i2c_bus_type;

	/* When registration returns, the driver core
	 * will have called probe() for all matching-but-unbound devices.
	 */
	res = driver_register(&driver->driver);
	if (res)
		return res;

	pr_debug("i2c-core: driver [%s] registered/n", driver->driver.name);

	INIT_LIST_HEAD(&driver->clients);
	/* Walk the adapters that are already present */
	mutex_lock(&core_lock);
	bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter);
	mutex_unlock(&core_lock);

	return 0;
}

注册一个设备

1.driver_register(&driver->driver);
   开始先注册一个设备

2.bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter);
   调用相对应的__attach_adapter 函数,并传递参数driver 给它。完成设备绑定适配器的动作。至于__attach_adapter 的代码粘贴如下

static int __attach_adapter(struct device *dev, void *data)
{
	struct i2c_adapter *adapter;
	struct i2c_driver *driver = data;

	if (dev->type != &i2c_adapter_type)
		return 0;
	adapter = to_i2c_adapter(dev);

	i2c_detect(adapter, driver);

	/* Legacy drivers scan i2c busses directly */
	if (driver->attach_adapter)
		driver->attach_adapter(adapter);

	return 0;
}

函数i2c_del_driver

void i2c_del_driver(struct i2c_driver *driver)
{
	mutex_lock(&core_lock);
	bus_for_each_dev(&i2c_bus_type, NULL, driver, __detach_adapter);
	mutex_unlock(&core_lock);

	driver_unregister(&driver->driver);
	pr_debug("i2c-core: driver [%s] unregistered/n", driver->driver.name);
}

相对应i2c_register_driver,本函数执行卸载设备的动作。

1.bus_for_each_dev(&i2c_bus_type, NULL, driver, __detach_adapter);
   调用__detach_adapter 函数,并传递函数driver 给它,完成分离设备和适配器的功能。

2.driver_unregister(&driver->driver);
   卸载设备。

   __detach_adapter 代码如下

static int __detach_adapter(struct device *dev, void *data)
{
	struct i2c_adapter *adapter;
	struct i2c_driver *driver = data;
	struct i2c_client *client, *_n;

	if (dev->type != &i2c_adapter_type)
		return 0;
	adapter = to_i2c_adapter(dev);

	/* Remove the devices we created ourselves as the result of hardware
	 * probing (using a driver's detect method) */
	list_for_each_entry_safe(client, _n, &driver->clients, detected) {
		dev_dbg(&adapter->dev, "Removing %s at 0x%x/n",
			client->name, client->addr);
		list_del(&client->detected);
		i2c_unregister_device(client);
	}

	if (driver->detach_adapter) {
		if (driver->detach_adapter(adapter))
			dev_err(&adapter->dev,
				"detach_adapter failed for driver [%s]/n",
				driver->driver.name);
	}

	return 0;
}

最后来看3个重要的i2c总线操作函数

函数 i2c_transfer

unsigned long orig_jiffies;
	int ret, try;

	if (adap->algo->master_xfer) {
#ifdef DEBUG
		for (ret = 0; ret < num; ret++) {
			dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
				"len=%d%s/n", ret, (msgs[ret].flags & I2C_M_RD)
				? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
				(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
		}
#endif

		if (in_atomic() || irqs_disabled()) {
			ret = mutex_trylock(&adap->bus_lock);
			if (!ret)
				/* I2C activity is ongoing. */
				return -EAGAIN;
		} else {
			mutex_lock_nested(&adap->bus_lock, adap->level);
		}

		/* Retry automatically on arbitration loss */
		orig_jiffies = jiffies;
		for (ret = 0, try = 0; try <= adap->retries; try++) {
			ret = adap->algo->master_xfer(adap, msgs, num);
			if (ret != -EAGAIN)
				break;
			if (time_after(jiffies, orig_jiffies + adap->timeout))
				break;
		}
		mutex_unlock(&adap->bus_lock);

		return ret;
	} else {
		dev_dbg(&adap->dev, "I2C level transfers not supported/n");
		return -EOPNOTSUPP;
	}
}

该函数几乎与 i2c_master_recv 和i2c_master_send (接下来马上叙述)函数一模一样,都是在持有i2c_adapter 的lock 信号量的情况下利用i2c 适配器的algo 所指的i2c_algorithm 数据结构的master_xfer 方法执行实际的i2c 操作。

整个函数最重要的,当数for 循环里面的内容,而for 循环里面的内容,最最重要的,肯定就是

ret = adap->algo->master_xfer(adap, msgs, num);  经过前文提及的内容,master_xfer用于产生I2C访问周期需要的信号,以I2C消息为单位。

 

函数 i2c_master_send

int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
	int ret;
	struct i2c_adapter *adap=client->adapter;
	struct i2c_msg msg;

	msg.addr = client->addr;
	msg.flags = client->flags & I2C_M_TEN;
	msg.len = count;
	msg.buf = (char *)buf;

	ret = i2c_transfer(adap, &msg, 1);

	/* If everything went ok (i.e. 1 msg transmitted), return #bytes
	   transmitted, else error code. */
	return (ret == 1) ? count : ret;
}

把需要发送的数据以及其信息保存到msg 结构体,通过i2c_transfer 函数发送。

 

函数 i2c_master_recv

int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
{
	struct i2c_adapter *adap=client->adapter;
	struct i2c_msg msg;
	int ret;

	msg.addr = client->addr;
	msg.flags = client->flags & I2C_M_TEN;
	msg.flags |= I2C_M_RD;
	msg.len = count;
	msg.buf = buf;

	ret = i2c_transfer(adap, &msg, 1);

	/* If everything went ok (i.e. 1 msg transmitted), return #bytes
	   transmitted, else error code. */
	return (ret == 1) ? count : ret;
}

跟发送函数i2c_master_send 对应的一个接收函数,具体不作解释啦。

 

由于篇幅问题,故不能肆意展开全部代码,但我还是在这里罗列一下关于smbus协议需要注意的函数:

s32 i2c_smbus_read_byte(struct i2c_client *client);

s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value);

s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command);

s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value);

s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command);

s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value);

s32 i2c_smbus_read_block_data(struct i2c_client *client, u8 command,
                                              u8 *values);

s32 i2c_smbus_write_block_data(struct i2c_client *client, u8 command,
                                              u8 length, const u8 *values);

static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
                                                   unsigned short flags,
                                                   char read_write, u8 command, int size,
                                                   union i2c_smbus_data * data);

以上一系列的函数,都是在i2c_smbus_xfer 函数的基础上建立起来的,只要理解了基石,其上的函数就不足为惧了。最后的最后,看一下这个藏在代码最后的基石吧!

 

函数 i2c_smbus_xfer

s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
		   char read_write, u8 command, int protocol,
		   union i2c_smbus_data *data)
{
	unsigned long orig_jiffies;
	int try;
	s32 res;

	flags &= I2C_M_TEN | I2C_CLIENT_PEC;

	if (adapter->algo->smbus_xfer) {
		mutex_lock(&adapter->bus_lock);

		/* Retry automatically on arbitration loss */
		orig_jiffies = jiffies;
		for (res = 0, try = 0; try <= adapter->retries; try++) {
			res = adapter->algo->smbus_xfer(adapter, addr, flags,
							read_write, command,
							protocol, data);
			if (res != -EAGAIN)
				break;
			if (time_after(jiffies,
				       orig_jiffies + adapter->timeout))
				break;
		}
		mutex_unlock(&adapter->bus_lock);
	} else
		res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
					      command, protocol, data);

	return res;
}

该函数通过适配器驱动提供的总线访问方法(i2c_algorithm 的smbus_xfer 方法)尝试访问处于addr 地址上的设备。其实函数里面最核心的一句是:

res = adapter->algo->smbus_xfer(adapter, addr, flags,
                                                read_write, command,
                                                protocol, data);

哈哈,这岂不妙哉,到最后我们还是回到结构体i2c_algorithm 的smbus_xfef 成员函数(见本文起始部分)。真是众里寻他千百度,蓦然回首,此函数却在灯火阑珊处。

经过这一个i2c系列4个小节的学习,自己对代码的分析,特别是驱动代码的分析有了一定的提高,起码在心理上不会感到畏惧。希望各位初学的朋友能够跟我一起坚持下去,踏踏实实地走好每一步!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值