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");
}
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;
}
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;
}
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_algorithm的master_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个小节的学习,自己对代码的分析,特别是驱动代码的分析有了一定的提高,起码在心理上不会感到畏惧。希望各位初学的朋友能够跟我一起坚持下去,踏踏实实地走好每一步!