在嵌入式开发中,EEPROM 作为一种重要的非易失性存储设备,广泛用于保存配置数据和校准参数。本文以 /sys/bus/i2c/drivers/at24/eeprom 为例,从底层驱动的视角详细解析其实现原理及核心技术点,帮助开发者理解如何将硬件功能抽象为用户友好的文件接口。

1. 什么是 Sysfs?
Sysfs 是 Linux 内核中的一个虚拟文件系统,位于 /sys 目录下,用于提供用户空间与内核对象之间的接口。它的设计目标是通过文件系统抽象硬件设备,使开发者能够以文件操作的形式与设备交互。
Sysfs 的特点:
- 直观性:硬件设备被映射为文件或目录,操作简单。
- 可编程性:支持用户态对设备的动态配置。
- 内核模块化:通过接口绑定,简化驱动开发。
对于 I2C EEPROM 设备,其内容被映射到 Sysfs 文件中,例如 /sys/bus/i2c/drivers/at24/eeprom,用户可以直接读取或写入该文件来操作硬件。
2. EEPROM 驱动的整体框架
EEPROM 驱动的核心目标是将设备存储区暴露为文件接口,涉及的主要技术点包括:
- I2C 驱动框架:基于 Linux 的 I2C 子系统,负责与 EEPROM 设备通信。
- 设备树支持:通过设备树描述硬件设备参数。
- Sysfs 文件创建:将 EEPROM 数据区域映射为用户空间文件。
- 读写操作实现:处理用户态的读写请求并映射到 I2C 传输。

2.1 驱动代码的基本结构
EEPROM 驱动 at24 是一个典型的 Linux 驱动,核心结构如下:
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
},
.probe = at24_probe,
.remove = at24_remove,
.id_table = at24_ids,
};
主要函数:
at24_probe:初始化设备并注册相关资源。at24_remove:设备移除时释放资源。at24_ids:支持的设备列表。
3. 设备树的配置
在现代嵌入式系统中,设备树用于描述硬件设备。对于 EEPROM,通常需要在设备树中指定以下内容:
3.1 示例设备树配置
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>;
pagesize = <16>;
size = <2048>;
};
字段解释:
compatible:匹配驱动,表明设备为 Atmel 的 24c02 EEPROM。reg:I2C 地址(0x50)。pagesize:EEPROM 的写入页大小。size:EEPROM 的总容量。
3.2 驱动中的解析
驱动通过 of_device_id 匹配设备树节点,并在 at24_probe 函数中解析设备参数:
static const struct of_device_id at24_of_match[] = {
{ .compatible = "atmel,24c02", .data = (void *)AT24_DEVICE_TYPE_24C02 },
{}
};
MODULE_DEVICE_TABLE(of, at24_of_match);
probe 函数中会读取 pagesize 和 size 等信息,并初始化设备结构体。
4. Sysfs 节点的创建
在 at24_probe 函数中,驱动通过以下代码将 EEPROM 数据映射到 Sysfs 文件:
sysfs_create_bin_file(&client->dev.kobj, &at24_bin_attr);
4.1 bin_attribute 结构
bin_attribute 是 Linux 中用于二进制文件接口的结构体:
static const struct bin_attribute at24_bin_attr = {
.attr = {
.name = "eeprom",
.mode = 0644,
},
.size = EEPROM_SIZE,
.read = at24_bin_read,
.write = at24_bin_write,
};
name:Sysfs 文件名(eeprom)。mode:文件权限。read和write:指向具体的读写函数。size:映射文件的大小,与 EEPROM 的容量一致。
5. 数据读写的实现
5.1 I2C 通信基础
I2C 是一种简单的串行总线协议,EEPROM 通常通过以下方式交互:
- 读操作:主设备发送地址后,EEPROM 返回对应数据。
- 写操作:主设备发送地址和数据,EEPROM 写入并返回 ACK。
5.2 驱动中的读写实现
读取操作
驱动中读取 EEPROM 数据的实现通常如下:
static ssize_t at24_bin_read(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t pos, size_t count)
{
struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj));
struct at24_data *at24 = i2c_get_clientdata(client);
return i2c_smbus_read_i2c_block_data(client, pos, count, buf);
}
上述代码中:
pos:读取起始地址。count:读取字节数。i2c_smbus_read_i2c_block_data:通过 I2C 读取指定位置的数据。
写入操作
写入操作实现如下:
static ssize_t at24_bin_write(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t pos, size_t count)
{
struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj));
struct at24_data *at24 = i2c_get_clientdata(client);
return i2c_smbus_write_i2c_block_data(client, pos, count, buf);
}
类似地,写入时会根据用户请求将数据通过 I2C 总线写入到指定地址。
6. 用户态操作示例
用户可以通过标准文件操作直接与 EEPROM 交互。
6.1 读取数据
使用 cat 或 hexdump 查看 EEPROM 内容:
cat /sys/bus/i2c/drivers/at24/eeprom
hexdump -C /sys/bus/i2c/drivers/at24/eeprom
6.2 写入数据
使用 echo 或 dd 写入数据:
echo "test_data" > /sys/bus/i2c/drivers/at24/eeprom
7. 关键点总结与注意事项
7.1 驱动实现要点
- 设备树解析:确保设备参数正确。
- Sysfs 文件接口:正确实现
bin_attribute。 - I2C 通信:处理可能的超时和错误情况。
7.2 使用注意事项
- 容量限制:Sysfs 文件大小不能超过 EEPROM 容量。
- 写入延迟:EEPROM 写入较慢,应避免频繁操作。
- 掉电数据保护:写入后需确保数据已写入非易失存储。
8. 结语
通过本文的详细讲解,相信您对 /sys/bus/i2c/drivers/at24/eeprom 的底层驱动实现有了深入理解。这种将硬件抽象为文件的设计不仅提高了用户态操作的便利性,也展示了 Linux 驱动框架的强大能力。在实际开发中,您可以借鉴该框架实现其他类似设备的支持。
834

被折叠的 条评论
为什么被折叠?



