i2c 驱动一:简介
2016-10-11 19:30
330 查看
有关linux的i2c相关文章有一下几篇,他们互相关联,应该一同看:
- i2c 驱动一:简介
- i2c 驱动二:devfs文件系统
- i2c 驱动三:自己实现设备和驱动分离
- i2c 驱动四:sysfs文件系统
- i2c 驱动五:gpio模拟i2c
i2c-dev.c,代表一个 i2c 的adapter ,一个 i2c或者 smbus 的主设备,不是一个i2c的从设备 i2c_client,这个文件提供了 read()、write()、read()、ioctl()和 close()等函数。i2c 的主设备号是89,次设备号是 0~255 ,自动注册的设备节点是“i2c-%d” (i2c-0, i2c-1,…, i2c-10,…)
busses/s3c2410.c,是 2440/2410 的驱动,其中包括 adapter 中的master_xfer 的实现和关联
chip文件夹,包含一些特定的 i2c 设备驱动,但是现在内核版本是 3.4.112 ,将这个文件的内容合并到了 misc 文件夹。
busses文件夹,包含了一些i2c总线的驱动,如针对S3C2410、S3C2440 和 S3C6410 等处理器的I2C 控制器驱动为 i2c-s3c2410.c
algos文件夹,实现了i2c总线适配器的 algorithm
mutex文件夹,实现了 i2c switch 的功能,扩展了i2c的路数,使得有限的i2c资源扩展出足够的接口,解决了i2c容量的问题
busses/s3c2410.c 是驱动文件
其他文件 是提供支持的文件
2.1
代表一个挂载到 i2c 总线上的一个实体的从设备,包含该设备所需的数据:
【该 i2c 设备所依附的 i2c 控制器】:struct i2c_adapter *adapter
【该 i2c 设备的驱动】:struct i2c_driver *driver
【该 i2c 设备的地址】:addr
【该 i2c 设备的名字】:name
2.2
i2c设备的驱动程序
2.3
算法,具体实现硬件上的传输控制
2.4
每一个 adapter 对应一个物理上的 i2c 控制器
2.5
说明:对 client 的其他成员的赋值在 ioctl 中由用户完成
将读到的数据放到了 msg 里边
对 (1) 中 adapter 中的 master_xfer 指针的赋值在 busses/s3c2410.c 中
subsys_initcall(i2c_adap_s3c_init);
--> i2c_adap_s3c_init{platform_driver_register(&s3c24xx_i2c_driver);}
--> s3c24xx_i2c_driver = {.probe = s3c24xx_i2c_probe,}
--> s3c24xx_i2c_probe{i2c->adap.algo = &s3c24xx_i2c_algorithm;}
--> s3c24xx_i2c_algorithm{.master_xfer= s3c24xx_i2c_xfer,}
--> s3c24xx_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
--> s3c24xx_i2c_doxfer(i2c, msgs, num);
--> s3c24xx_i2c_enable_irq(i2c); s3c24xx_i2c_message_start(i2c, msgs);
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);/* 阻塞在这里,直到 msg_num = 0,或者超过 5s */
这张图可以看到,当接收,或者发送完一个字节在ack后边会产生一个中断
在probe中有申请中断
s3c24xx_i2c_probe(struct platform_device *pdev){ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0,
dev_name(&pdev->dev), i2c);}
--> s3c24xx_i2c_irq(int irqno, void *dev_id){i2c_s3c_irq_nextbyte(i2c, status);}
--> i2c_s3c_irq_nextbyte{switch (i2c->state)
{
case STATE_STOP:
case STATE_START:
case STATE_WRITE:
byte = i2c->msg->buf[i2c->msg_ptr++];
writeb(byte, i2c->regs + S3C2410_IICDS);
case STATE_READ:
byte = readb(i2c->regs + S3C2410_IICDS);
i2c->msg->buf[i2c->msg_ptr++] = byte;
}}
至此,数据发送的整个过程就全说完
(1)的结构体 i2c_rdwr_ioctl_data
需要包含头文件 <i2c-dev.h>
从设备的地址需要 右移一位
msg.flags = 0 是写,msg.flags =I2C_M_RD 是读
打开的i2c的设备是 open("/dev/i2c-0",O_RDWR); (需要先查看/dev 是不是有,如果没有,需要内核去支持 i2c 驱动)
- i2c 驱动一:简介
- i2c 驱动二:devfs文件系统
- i2c 驱动三:自己实现设备和驱动分离
- i2c 驱动四:sysfs文件系统
- i2c 驱动五:gpio模拟i2c
1. 内核源码中有关 i2c 的目录和文件:
1.1 有关 i2c 的驱动在 driver 的 i2c 目录下,先来介绍一下关键的几个文件:
i2c-core.c,实现了 I2C 核心的功能以及/proc/bus/i2c*接口i2c-dev.c,代表一个 i2c 的adapter ,一个 i2c或者 smbus 的主设备,不是一个i2c的从设备 i2c_client,这个文件提供了 read()、write()、read()、ioctl()和 close()等函数。i2c 的主设备号是89,次设备号是 0~255 ,自动注册的设备节点是“i2c-%d” (i2c-0, i2c-1,…, i2c-10,…)
busses/s3c2410.c,是 2440/2410 的驱动,其中包括 adapter 中的master_xfer 的实现和关联
chip文件夹,包含一些特定的 i2c 设备驱动,但是现在内核版本是 3.4.112 ,将这个文件的内容合并到了 misc 文件夹。
busses文件夹,包含了一些i2c总线的驱动,如针对S3C2410、S3C2440 和 S3C6410 等处理器的I2C 控制器驱动为 i2c-s3c2410.c
algos文件夹,实现了i2c总线适配器的 algorithm
mutex文件夹,实现了 i2c switch 的功能,扩展了i2c的路数,使得有限的i2c资源扩展出足够的接口,解决了i2c容量的问题
1.2 简单的理解:
i2c-dev.c 是设备文件busses/s3c2410.c 是驱动文件
其他文件 是提供支持的文件
2. 在 include/linux/i2c.h 中定义了 i2c 用到关键的结构体:
2.1
i2c_client 结构体:
struct i2c_client { unsigned int flags; /* 标志 */ unsigned short addr; /* 低 7 位为芯片地址 */ char name[I2C_NAME_SIZE]; /* 设备名称 */ struct i2c_adapter *adapter; /*依附的 i2c_adapter*/ struct i2c_driver *driver; /*依附的 i2c_driver */ struct device dev; /* 设备结构体*/ int irq; /* 设备使用的中断号*/ struct list_head list; /* 链表头 */ struct completion released; /* 用于同步 */ };
代表一个挂载到 i2c 总线上的一个实体的从设备,包含该设备所需的数据:
【该 i2c 设备所依附的 i2c 控制器】:struct i2c_adapter *adapter
【该 i2c 设备的驱动】:struct i2c_driver *driver
【该 i2c 设备的地址】:addr
【该 i2c 设备的名字】:name
2.2
i2c_driver 结构体:
struct i2c_driver { int id; unsigned int class; int (*attach_adapter)(struct i2c_adapter *); /*依附 i2c_adapter 函数指针 */ int (*detach_adapter)(struct i2c_adapter *); /*脱离 i2c_adapter 函数指针*/ int (*detach_client)(struct i2c_client *); /*i2c client 脱离函数指针*/ int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); void (*shutdown)(struct i2c_client *); int (*suspend)(struct i2c_client *, pm_message_t mesg); int (*resume)(struct i2c_client *); int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); struct device_driver driver; const struct i2c_device_id *id_table; /* 该驱动所支持的设备 ID 表 */ int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *); const struct i2c_client_address_data *address_data; struct list_head clients; };
i2c设备的驱动程序
2.3
i2c_algorithm 结构体:
struct i2c_algorithm { int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs, int num); /*i2c 传输函数指针*/ int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data * data); /*smbus 传输函数指针*/ u32 (*functionality) (struct i2c_adapter *); /*返回适配器支持的功能*/ };
算法,具体实现硬件上的传输控制
2.4
i2c_adapter 结构体:
struct i2c_adapter { struct module *owner; /*所属模块*/ unsigned int id; /*algorithm 的类型,定义于 i2c-id.h,以 I2C_ALGO_开始*/ unsigned int class; struct i2c_algorithm *algo; /*总线通信方法结构体指针*/ void *algo_data; /* algorithm 数据 */ int (*client_register)(struct i2c_client *); /*client 注册时调用*/ int (*client_unregister)(struct i2c_client *); /*client 注销时调用*/ u8 level; struct semaphore bus_lock; /*控制并发访问的自旋锁*/ struct semaphore clist_lock; int timeout; int retries; /* 重试次数 */ struct device dev; /* 适配器设备 */ struct class_device class_dev; /* 类设备 */ int nr; /* /dev/i2c-devnr */ /* (1)这里要注意 */ struct list_head clien; /* client 链表头*/ struct list_head list; char name[48]; /*适配器名称*/ struct completion dev_released; /*用于同步*/ };
每一个 adapter 对应一个物理上的 i2c 控制器
2.5
i2c_msg 结构体:
struct i2c_msg { __u16 addr; /* 设备地址*/ __u16 flags; /* 标志,下边是标志的定义 */ #define I2C_M_TEN 0x0010 /* 这是一个10位的芯片地址 */ #define I2C_M_RD 0x0001 /* 从从设备读数据,如果要写的话,flags = 0 */ #define I2C_M_NOSTART 0x4000 /* */ #define I2C_M_REV_DIR_ADDR 0x2000 /* */ #define I2C_M_IGNORE_NAK 0x1000 /* */ #define I2C_M_NO_RD_ACK 0x0800 /* */ #define I2C_M_RECV_LEN 0x0400 /* 长度将会是接收的第一个字节 */ __u16 len; /* 消息长度*/ __u8 *buf; /* 消息数据*/ };
2.6 以上的几个结构体之间的关系:
3. 对 i2c-dev.c 文件中的函数的理解:
struct file_operations i2cdev_fops = { ... .read = i2cdev_read, .write = i2cdev_write, .unlocked_ioctl= i2cdev_ioctl, .open = i2cdev_open, .release = i2cdev_release, };
3.1i2cdev_open 函数 (.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); /* (3)通过adap->nr找到相应的adapter的首地址,idr机制采用的是radix树,可以很方便的将整数和指针关联起来,要了解更多idr机制,可以查看 http://blog.sina.com.cn/s/blog_476d8cf30100nhfc.html */ 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->name = i2c-dev ... */ client->adapter = adap; /* (1)匹配上 adapter,并且关联 client中的 adapter 指针 */ file->private_data = client; /* (2)放到私有数据中去 */ return 0; }
说明:对 client 的其他成员的赋值在 ioctl 中由用户完成
3.2 单个 msg 的读 (.read)
/* buf 是用于存放接收到的数据的指针,count 是数据中字节数 */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); /* (1)将读到的数据放到了 tmp指针指向的空间 里边 */ if (ret >= 0) ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret; kfree(tmp); return ret; }
将读到的数据放到了 msg 里边
int i2c_master_recv(const 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); /* (1)调用 adapter 中的 master_xfer 函数 */ /* * If everything went ok (i.e. 1 msg received), return #bytes received, * else error code. */ return (ret == 1) ? count : ret; }
对 (1) 中 adapter 中的 master_xfer 指针的赋值在 busses/s3c2410.c 中
subsys_initcall(i2c_adap_s3c_init);
--> i2c_adap_s3c_init{platform_driver_register(&s3c24xx_i2c_driver);}
--> s3c24xx_i2c_driver = {.probe = s3c24xx_i2c_probe,}
--> s3c24xx_i2c_probe{i2c->adap.algo = &s3c24xx_i2c_algorithm;}
--> s3c24xx_i2c_algorithm{.master_xfer= s3c24xx_i2c_xfer,}
--> s3c24xx_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
--> s3c24xx_i2c_doxfer(i2c, msgs, num);
--> s3c24xx_i2c_enable_irq(i2c); s3c24xx_i2c_message_start(i2c, msgs);
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);/* 阻塞在这里,直到 msg_num = 0,或者超过 5s */
这张图可以看到,当接收,或者发送完一个字节在ack后边会产生一个中断
在probe中有申请中断
s3c24xx_i2c_probe(struct platform_device *pdev){ret = request_irq(i2c->irq, s3c24xx_i2c_irq, 0,
dev_name(&pdev->dev), i2c);}
--> s3c24xx_i2c_irq(int irqno, void *dev_id){i2c_s3c_irq_nextbyte(i2c, status);}
--> i2c_s3c_irq_nextbyte{switch (i2c->state)
{
case STATE_STOP:
case STATE_START:
case STATE_WRITE:
byte = i2c->msg->buf[i2c->msg_ptr++];
writeb(byte, i2c->regs + S3C2410_IICDS);
case STATE_READ:
byte = readb(i2c->regs + S3C2410_IICDS);
i2c->msg->buf[i2c->msg_ptr++] = byte;
}}
至此,数据发送的整个过程就全说完
3.3 单个 msg 的写 (.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); /* 发送一个 count 字节的 msg */ kfree(tmp); return ret; }
3.4 给用户层提供的控制 ioctl (.unlocked_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); /* 实现 多个 msg 的读写 */ case I2C_SMBUS: return i2cdev_ioctl_smbus(client, arg); case I2C_RETRIES: client->adapter->retries = arg; break; case I2C_TIMEOUT: /* 单位是 10ms. */ client->adapter->timeout = msecs_to_jiffies(arg * 10); break; default: return -ENOTTY; } return 0; }
3.5 多个msg的读写
static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client, unsigned long arg) { struct i2c_rdwr_ioctl_data rdwr_arg; /* (1)应用程序要想发送多个msg需要用到的结构体 */ 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))) /* (2)从用户空间传给内核空间 */ return -EFAULT; /* 限制一次发送的 msg 的最大的个数是 42 */ if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) return -EINVAL; rdwr_pa = memdup_user(rdwr_arg.msgs, rdwr_arg.nmsgs * sizeof(struct i2c_msg)); /* (3)取出 rdwr_arg 中的 msg */ if (IS_ERR(rdwr_pa)) return PTR_ERR(rdwr_pa); 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 = memdup_user(data_ptrs[i], rdwr_pa[i].len); if (IS_ERR(rdwr_pa[i].buf)) { res = PTR_ERR(rdwr_pa[i].buf); 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); /* (4)发送 nmsgs 个 msg */ 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)的结构体 i2c_rdwr_ioctl_data
需要包含头文件 <i2c-dev.h>
struct i2c_rdwr_ioctl_data { struct i2c_msg __user *msgs; /* i2c_msgs 指针 */ __u32 nmsgs; /* i2c_msgs 的个数 */ };
4. 用户写一个i2c驱动需要做的:
包含头文件: <linux/i2c.h><linux/i2c-dev.h>从设备的地址需要 右移一位
msg.flags = 0 是写,msg.flags =I2C_M_RD 是读
打开的i2c的设备是 open("/dev/i2c-0",O_RDWR); (需要先查看/dev 是不是有,如果没有,需要内核去支持 i2c 驱动)
相关文章推荐
- I2C子系统驱动架构 - 简介
- WinCE流设备驱动简介及GPIO驱动的实现
- linux i2c驱动讲解
- linux下I2C驱动的开发
- Linux I2C设备驱动编写
- linux下I2C驱动架构全面分析
- STM32F4XX高效驱动篇2 I2C
- 手把手教你写Linux I2C设备驱动
- linux下I2C驱动架构全面分析
- 嵌入式开发之zynq---Zynq PS侧I2C驱动架构
- 如何在Linux中让I2C驱动支持Sub Address的两种方法
- linux驱动基础系列--Linux I2c驱动分析
- OMAP3630 Linux I2C总线驱动分析
- 【引用】Linux-2.6.32.2内核在mini2440上的移植(十二)---移植I2C EEPROM驱动
- OMAP3630 Linux I2C总线驱动分析(1)
- Linux ALSA声卡驱动之一:ALSA架构简介
- Linux 驱动i2c -- Gsenser(一)
- Linux的I2C驱动架构
- linux下的I2C驱动记录(RK)
- Linux I2C驱动完全分析