linux的I2C驱动——读写操作
2017-08-06 16:12
477 查看
一、体系结构
接下来开始整体的介绍I2C,主要参考《Linux设备驱动开发详解》。
1、I2C核心
I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法。
2、I2C总线驱动
I2C总线驱动是对I2C硬件体系结构中适配器的实现。
I2C总线驱动主要包含I2C适配器数据结构i2c_adapter、I2C适配器的algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数。
经由I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。
3、I2C设备驱动
I2C设备驱动主要包含数据结构i2c_driver和i2c_client。
二、I2C总线
代码路径:drivers/I2C/busses/i2c_tegra.c每个代码架构都有他对应的总线代码,下面介绍i2c_adapter、i2c_algorithm、i2c_msg三个结构体
定义总线适配器
struct i2c_adapter { struct module *owner; unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; /* data fields that are valid for all devices */ struct rt_mutex bus_lock; int timeout; /* in jiffies */ int retries; struct device dev; /* the adapter device */ int nr; char name[48]; struct completion dev_released; struct mutex userspace_clients_lock; struct list_head userspace_clients; };
数据传输结构体,决定I2C的通信方式
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); /* To determine what the adapter supports */ u32 (*functionality) (struct i2c_adapter *); };
数据格式
struct i2c_msg { __u16 addr; /* 从地址 */ __u16 flags; #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RD 0x0001 /* read data, from slave to master */ #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ __u16 len; /* 数据长度 */ __u8 *buf; /* 数据指针 */ };
I2C总线的初始化
static int __init tegra_i2c_init_driver(void) { return platform_driver_register(&tegra_i2c_driver); } subsys_initcall(tegra_i2c_init_driver);
int platform_driver_register(struct platform_driver *drv) { drv->driver.bus = &platform_bus_type; if (drv->probe) drv->driver.probe = platform_drv_probe; if (drv->remove) drv->driver.remove = platform_drv_remove; if (drv->shutdown) drv->driver.shutdown = platform_drv_shutdown; return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(platform_driver_register);
总线会像普通设备那个去注册驱动,并调用probe函数。
tegra_i2c_probe函数用于初始化i2c_adapter和i2c_algorithm结构体
static int tegra_i2c_probe(struct platform_device *pdev) { struct tegra_i2c_dev *i2c_dev; struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data; struct resource *res; struct resource *iomem; struct clk *clk; struct clk *i2c_clk; void *base; int irq; int ret = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); …… res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); …… i2c_dev = kzalloc(sizeof(struct tegra_i2c_dev), GFP_KERNEL); if (!i2c_dev) { ret = -ENOMEM; goto err_i2c_clk_put; } i2c_dev->base = base; i2c_dev->clk = clk; i2c_dev->i2c_clk = i2c_clk; i2c_dev->iomem = iomem; i2c_dev->adapter.algo = &tegra_i2c_algo; i2c_dev->irq = irq; i2c_dev->cont_id = pdev->id; i2c_dev->dev = &pdev->dev; i2c_dev->bus_clk_rate = pdata ? pdata->bus_clk_rate : 100000; if (pdev->id == 3) i2c_dev->is_dvc = 1; init_completion(&i2c_dev->msg_complete); platform_set_drvdata(pdev, i2c_dev); ret = tegra_i2c_init(i2c_dev); …… i2c_set_adapdata(&i2c_dev->adapter, i2c_dev); i2c_dev->adapter.owner = THIS_MODULE; i2c_dev->adapter.class = I2C_CLASS_HWMON; strlcpy(i2c_dev->adapter.name, "Tegra I2C adapter", sizeof(i2c_dev->adapter.name)); i2c_dev->adapter.algo = &tegra_i2c_algo; i2c_dev->adapter.dev.parent = &pdev->dev; i2c_dev->adapter.nr = pdev->id; ret = i2c_add_numbered_adapter(&i2c_dev->adapter); if (ret) { dev_err(&pdev->dev, "Failed to add I2C adapter\n"); goto err_free_irq; } return 0; …… }
通过i2c_set_adapdata函数设置i2c_adapter;
通过i2c_dev->adapter.algo = &tegra_i2c_algo去指定i2c_algorithm结构体
/*drivers/I2C/busses/i2c-tegra.c*/ static const struct i2c_algorithm tegra_i2c_algo = { .master_xfer = tegra_i2c_xfer, .functionality = tegra_i2c_func, };
master_xfer定义了数据传输函数。
三、上层调用
上层通过read、write接口函数去调用驱动中对应的函数,在驱动中会定义file_operations
struct file_operations at24cxx_fops = { .owner = THIS_MODULE, .re 9cd7 ad = at24cxx_read, .write = at24cxx_write, };
在at24cxx_read函数中会调用i2c_transfer函数,进行数据的读取。
/*drivers/I2C/i2c-core.c*/ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { unsigned long orig_jiffies; int ret, try; if (adap->algo->master_xfer) { if (in_atomic() || irqs_disabled()) { ret = i2c_trylock_adapter(adap); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; } else { i2c_lock_adapter(adap); } /* 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; } i2c_unlock_adapter(adap); return ret; } else { dev_dbg(&adap->dev, "I2C level transfers not supported\n"); return -EOPNOTSUPP; } } EXPORT_SYMBOL(i2c_transfer);
在i2c_transfer函数中调用adap->algo->master_xfer所指定的函数进行数据的传输。即调用总线去传输数据。
I2C设备的整体运行过程如下:
上层通过read、write函数去调用驱动中对应的接口,然后由接口函数再去调用总线的相关函数,然后实现与设备的通信。
相关文章推荐
- S3C2440 Linux下的I2C驱动以及I2C体系下对EEPROM进行读写操作。
- Linux I2C 对16位寄存器地址 进行读写操作
- Linux i2c驱动(eeprom 读写)
- Linux i2c驱动(eeprom 读写)
- linux设备驱动之 i2c设备驱动 at24c08驱动程序分析【全部地址的操作】
- linux 内核驱动加载过程中 向文件系统中的文件进行读写操作
- Linux i2c驱动(eeprom 读写)
- (3)LinuxI2C驱动--解析EEPROM的读写
- Linux 字符设备驱动开发--内存读写操作
- Linux ARM IIC I2C EEPROM 读写操作
- LINUX驱动之IIC子系统之三I2C的数…
- 【Linux 驱动】第六章 高级字符驱动程序操作 ----阻塞型I/O
- [Linux流操作]使用gets和puts读写文件
- linux驱动——内核通知链(探究i2c-dev.c 中的bus_register_notifier函数所得)
- Linux驱动子系统之I2C(6)
- linux程序设计——对FIFO进行读写操作(第十三章)
- linux驱动开发:用户空间操作LCD显示简单的图片
- linux I2C驱动学习笔记
- LinuxI2C驱动--从两个访问eeprom的例子开始
- Linux驱动子系统之I2C(5)