好的,作为一名Linux驱动开发工程师,我将帮助你在现有的GPIO驱动基础上,基于18101主板和RTL8367手册,实现GPIO模拟I2C,并获取RTL8367的寄存器状态。我们将分步骤完成以下任务:
1. **GPIO操作实现**
- 源码层次接口操作
- 命令行层次串口操作
2. **I2C协议实现**
- 分析RTL8367与标准I2C的差异
- 封装RTL8367特有的I2C读写函数
### 目录
1. [环境准备](#1-环境准备)
2. [GPIO模拟I2C的基本实现](#2-GPIO模拟I2C的基本实现)
- [GPIO初始化与配置](#21-GPIO初始化与配置)
- [I2C基本信号控制函数](#22-I2C基本信号控制函数)
- [I2C通信协议函数](#23-I2C通信协议函数)
3. [RTL8367特有的I2C协议处理](#3-RTL8367特有的I2C协议处理)
- [分析RTL8367与标准I2C的差异](#31-分析RTL8367与标准I2C的差异)
- [封装RTL8367的I2C读写函数](#32-封装RTL8367的I2C读写函数)
4. [实现命令行接口](#4-实现命令行接口)
- [使用`proc`文件系统](#41-使用proc文件系统)
- [使用`sysfs`文件系统](#42-使用sysfs文件系统)
5. [完整驱动代码示例](#5-完整驱动代码示例)
6. [测试与验证](#6-测试与验证)
---
### 1. 环境准备
确保你已经具备以下环境和资源:
- **开发环境**:交叉编译工具链(针对18101主板的架构)、内核源码与头文件。
- **硬件资源**:18101主板、RTL8367交换芯片、相关连接电缆。
- **文档资源**:RTL8367手册、SDK、标准I2C协议规范。
### 2. GPIO模拟I2C的基本实现
我们将通过GPIO引脚模拟I2C通信(即所谓的"bit-banging")。这涉及手动控制SCL(时钟线)和SDA(数据线)的高低电平,以实现I2C的起始、停止、读写等操作。
#### 2.1 GPIO初始化与配置
首先,确保你的驱动中已经正确初始化和配置了用于I2C通信的GPIO引脚。假设`i2c1scl_gpio`和`i2c1sda_gpio`分别用于SCL和SDA。
```c
// 在RTL8367_init函数中,确保申请和配置了SCL和SDA GPIO
// 已在你提供的代码中完成
```
#### 2.2 I2C基本信号控制函数
我们需要实现基本的I2C信号控制函数,如设置SCL和SDA高低电平、生成起始和停止条件、读取和写入单个比特等。
```c
#include <linux/delay.h>
// 定义I2C时序延迟,单位为微秒
#define I2C_DELAY_US 5
struct switch_dev {
// ... 现有成员
int i2c1scl_gpio;
int i2c1sda_gpio;
// 添加锁用于保护I2C操作
struct mutex i2c_lock;
};
// 设置SCL为高电平
static void i2c_set_scl_high(struct switch_dev *dev) {
gpio_set_value(dev->i2c1scl_gpio, 1);
udelay(I2C_DELAY_US);
}
// 设置SCL为低电平
static void i2c_set_scl_low(struct switch_dev *dev) {
gpio_set_value(dev->i2c1scl_gpio, 0);
udelay(I2C_DELAY_US);
}
// 设置SDA为高电平
static void i2c_set_sda_high(struct switch_dev *dev) {
gpio_set_value(dev->i2c1sda_gpio, 1);
udelay(I2C_DELAY_US);
}
// 设置SDA为低电平
static void i2c_set_sda_low(struct switch_dev *dev) {
gpio_set_value(dev->i2c1sda_gpio, 0);
udelay(I2C_DELAY_US);
}
// 读取SDA电平(返回0或1)
static int i2c_read_sda(struct switch_dev *dev) {
// 设置SDA为输入模式
gpio_direction_input(dev->i2c1sda_gpio);
udelay(I2C_DELAY_US);
return gpio_get_value(dev->i2c1sda_gpio);
}
// 生成I2C起始条件
static void i2c_start(struct switch_dev *dev) {
i2c_set_sda_high(dev);
i2c_set_scl_high(dev);
i2c_set_sda_low(dev);
udelay(I2C_DELAY_US);
i2c_set_scl_low(dev);
}
// 生成I2C停止条件
static void i2c_stop(struct switch_dev *dev) {
i2c_set_sda_low(dev);
i2c_set_scl_high(dev);
i2c_set_sda_high(dev);
udelay(I2C_DELAY_US);
}
// 发送一个字节,返回ACK/NACK(0/1)
static int i2c_send_byte(struct switch_dev *dev, unsigned char byte) {
int i;
for (i = 0; i < 8; i++) {
if (byte & 0x80)
i2c_set_sda_high(dev);
else
i2c_set_sda_low(dev);
byte <<= 1;
i2c_set_scl_high(dev);
i2c_set_scl_low(dev);
}
// 读取ACK
i2c_set_sda_high(dev); // 释放SDA线
gpio_direction_input(dev->i2c1sda_gpio);
udelay(I2C_DELAY_US);
i2c_set_scl_high(dev);
int ack = i2c_read_sda(dev);
i2c_set_scl_low(dev);
return ack;
}
// 接收一个字节,发送ACK/NACK
static unsigned char i2c_receive_byte(struct switch_dev *dev, int ack) {
int i;
unsigned char byte = 0;
gpio_direction_input(dev->i2c1sda_gpio);
for (i = 0; i < 8; i++) {
byte <<= 1;
i2c_set_scl_high(dev);
if (i2c_read_sda(dev))
byte |= 0x01;
i2c_set_scl_low(dev);
}
// 发送ACK/NACK
if (ack)
i2c_set_sda_low(dev);
else
i2c_set_sda_high(dev);
gpio_direction_output(dev->i2c1sda_gpio, ack ? 0 : 1);
udelay(I2C_DELAY_US);
i2c_set_scl_high(dev);
udelay(I2C_DELAY_US);
i2c_set_scl_low(dev);
gpio_direction_input(dev->i2c1sda_gpio);
return byte;
}
```
#### 2.3 I2C通信协议函数
基于上述基本信号控制函数,构建I2C的读写操作。
```c
// 发送I2C地址和读/写位
static int i2c_write_addr(struct switch_dev *dev, unsigned char addr, int read) {
unsigned char byte = (addr << 1) | (read ? 1 : 0);
return i2c_send_byte(dev, byte);
}
// 写一个寄存器
static int i2c_write_reg(struct switch_dev *dev, unsigned char reg, unsigned char value) {
int ret;
mutex_lock(&dev->i2c_lock);
i2c_start(dev);
ret = i2c_send_byte(dev, (dev->i2c_addr << 1) | 0); // 写操作
if (ret) { // NACK
goto stop;
}
ret = i2c_send_byte(dev, reg);
if (ret) {
goto stop;
}
ret = i2c_send_byte(dev, value);
if (ret) {
goto stop;
}
i2c_stop(dev);
mutex_unlock(&dev->i2c_lock);
return 0;
stop:
i2c_stop(dev);
mutex_unlock(&dev->i2c_lock);
return -EIO;
}
// 读一个寄存器
static int i2c_read_reg(struct switch_dev *dev, unsigned char reg, unsigned char *value) {
int ret;
mutex_lock(&dev->i2c_lock);
i2c_start(dev);
ret = i2c_send_byte(dev, (dev->i2c_addr << 1) | 0); // 写操作
if (ret) { // NACK
goto stop;
}
ret = i2c_send_byte(dev, reg);
if (ret) {
goto stop;
}
// 重复起始
i2c_start(dev);
ret = i2c_send_byte(dev, (dev->i2c_addr << 1) | 1); // 读操作
if (ret) {
goto stop;
}
*value = i2c_receive_byte(dev, 0); // NACK
i2c_stop(dev);
mutex_unlock(&dev->i2c_lock);
return 0;
stop:
i2c_stop(dev);
mutex_unlock(&dev->i2c_lock);
return -EIO;
}
```
### 3. RTL8367特有的I2C协议处理
#### 3.1 分析RTL8367与标准I2C的差异
根据RTL8367的手册和SDK,确定其I2C实现与标准I2C协议的差异。常见的差异可能包括:
- **时钟频率**:RTL8367可能支持不同的I2C时钟频率。
- **起始/停止条件**:可能有特殊的起始或停止条件。
- **ACK/NACK处理**:可能有不同的ACK/NACK信号处理方式。
- **寄存器地址位数**:可能使用更长或更短的寄存器地址。
- **数据位数**:可能使用不同的数据位宽(如16位数据)。
假设RTL8367的I2C与标准I2C大致相同,但有一些特定要求,如延迟要求或特殊的操作顺序。具体细节需要根据手册确认。
#### 3.2 封装RTL8367的I2C读写函数
基于上一步的I2C读写函数,封装RTL8367特有的读写操作。
```c
// 假设RTL8367的I2C地址为0x16(根据手册确认)
#define RTL8367_I2C_ADDR 0x16
struct switch_dev {
// ... 现有成员
int i2c_addr; // I2C设备地址
// ... 其他成员
};
// 在初始化函数中,设置RTL8367的I2C地址
switch_device.i2c_addr = RTL8367_I2C_ADDR;
// 读取RTL8367寄存器
static int rtl8367_read_reg(struct switch_dev *dev, unsigned short reg, unsigned short *value) {
unsigned char reg_high = (reg >> 8) & 0xFF;
unsigned char reg_low = reg & 0xFF;
unsigned char val_high, val_low;
int ret;
// RTL8367可能使用16位寄存器地址和16位数据
ret = i2c_write_reg(dev, reg_high, reg_low);
if (ret < 0) {
printk(KERN_ERR "Failed to write register address\n");
return ret;
}
ret = i2c_read_reg(dev, reg_high, &val_high);
if (ret < 0) {
printk(KERN_ERR "Failed to read register high byte\n");
return ret;
}
ret = i2c_read_reg(dev, reg_low, &val_low);
if (ret < 0) {
printk(KERN_ERR "Failed to read register low byte\n");
return ret;
}
*value = ((unsigned short)val_high << 8) | val_low;
return 0;
}
// 写入RTL8367寄存器
static int rtl8367_write_reg(struct switch_dev *dev, unsigned short reg, unsigned short value) {
unsigned char reg_high = (reg >> 8) & 0xFF;
unsigned char reg_low = reg & 0xFF;
unsigned char val_high = (value >> 8) & 0xFF;
unsigned char val_low = value & 0xFF;
int ret;
ret = i2c_write_reg(dev, reg_high, reg_low);
if (ret < 0) {
printk(KERN_ERR "Failed to write register address\n");
return ret;
}
ret = i2c_write_reg(dev, val_high, val_low);
if (ret < 0) {
printk(KERN_ERR "Failed to write register value\n");
return ret;
}
return 0;
}
```
**注意**:上述代码假设RTL8367使用16位寄存器地址和16位数据。具体实现应根据RTL8367手册进行调整。
### 4. 实现命令行接口
为了通过命令行(串口)操作GPIO和I2C,可以通过`proc`或`sysfs`文件系统暴露接口。这里以`sysfs`为例,因为它更现代且易于使用。
#### 4.1 使用`sysfs`文件系统
创建`/sys`下的设备文件,用户可以通过读写文件进行GPIO和I2C操作。
```c
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/string.h>
// 定义kobject
static struct kobject *rtl8367_kobj;
// GPIO控制接口:例如,设置SCL和SDA
static ssize_t scl_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
struct switch_dev *dev = &switch_device;
unsigned int val;
if (kstrtouint(buf, 10, &val))
return -EINVAL;
gpio_set_value(dev->i2c1scl_gpio, val ? 1 : 0);
return count;
}
static ssize_t scl_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
struct switch_dev *dev = &switch_device;
int val = gpio_get_value(dev->i2c1scl_gpio);
return sprintf(buf, "%d\n", val);
}
static ssize_t sda_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
struct switch_dev *dev = &switch_device;
unsigned int val;
if (kstrtouint(buf, 10, &val))
return -EINVAL;
gpio_set_value(dev->i2c1sda_gpio, val ? 1 : 0);
return count;
}
static ssize_t sda_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
struct switch_dev *dev = &switch_device;
int val = gpio_get_value(dev->i2c1sda_gpio);
return sprintf(buf, "%d\n", val);
}
// I2C读写接口
static ssize_t i2c_read_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
struct switch_dev *dev = &switch_device;
unsigned short reg;
unsigned short val;
if (kstrtoushort(buf, 16, ®))
return -EINVAL;
if (rtl8367_read_reg(dev, reg, &val) < 0)
return -EIO;
printk(KERN_INFO "Read reg 0x%04X: 0x%04X\n", reg, val);
return count;
}
static ssize_t i2c_read_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
// 通常不实现show,使用store触发读操作
return 0;
}
static ssize_t i2c_write_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
struct switch_dev *dev = &switch_device;
unsigned short reg, val;
if (sscanf(buf, "%hx %hx", ®, &val) != 2)
return -EINVAL;
if (rtl8367_write_reg(dev, reg, val) < 0)
return -EIO;
printk(KERN_INFO "Wrote reg 0x%04X: 0x%04X\n", reg, val);
return count;
}
static ssize_t i2c_write_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
// 通常不实现show,使用store触发写操作
return 0;
}
// 定义属性
static struct kobj_attribute scl_attr = __ATTR(scl, 0664, scl_show, scl_store);
static struct kobj_attribute sda_attr = __ATTR(sda, 0664, sda_show, sda_store);
static struct kobj_attribute i2c_read_attr = __ATTR(i2c_read, 0220, i2c_read_show, i2c_read_store);
static struct kobj_attribute i2c_write_attr = __ATTR(i2c_write, 0220, i2c_write_show, i2c_write_store);
// 初始化sysfs接口
static int rtl8367_sysfs_init(void) {
int error = 0;
rtl8367_kobj = kobject_create_and_add("rtl8367", kernel_kobj);
if (!rtl8367_kobj)
return -ENOMEM;
error = sysfs_create_file(rtl8367_kobj, &scl_attr.attr);
if (error) {
printk(KERN_ERR "Failed to create sysfs scl file\n");
return error;
}
error = sysfs_create_file(rtl8367_kobj, &sda_attr.attr);
if (error) {
printk(KERN_ERR "Failed to create sysfs sda file\n");
return error;
}
error = sysfs_create_file(rtl8367_kobj, &i2c_read_attr.attr);
if (error) {
printk(KERN_ERR "Failed to create sysfs i2c_read file\n");
return error;
}
error = sysfs_create_file(rtl8367_kobj, &i2c_write_attr.attr);
if (error) {
printk(KERN_ERR "Failed to create sysfs i2c_write file\n");
return error;
}
return 0;
}
// 清理sysfs接口
static void rtl8367_sysfs_exit(void) {
kobject_put(rtl8367_kobj);
}
```
在初始化函数中调用`rtl8367_sysfs_init()`,在卸载函数中调用`rtl8367_sysfs_exit()`。
#### 4.2 使用`proc`文件系统
另一种方法是使用`proc`文件系统,不过`sysfs`更适合设备相关的接口。
### 5. 完整驱动代码示例
以下是结合上述各部分的完整驱动代码示例。请根据实际情况调整寄存器地址、I2C地址等参数。
```c
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/printk.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/mutex.h>
#define SWITCH_CNT 1
#define SWITCH_NAME "RTL8367_RW"
#define SWITCH_OFF 0
#define SWITCH_ON 1
#define RTL8367_I2C_ADDR 0x16 // 根据手册确认
#define LOG_TAG "RTL8367_I2C"
struct switch_dev {
dev_t devid; // 设备号
struct device *device; // 设备
struct class *class; // 类
struct cdev cdev; // 结构体变量
struct device_node *nd; // 设备节点
int i2c1scl_gpio; // GPIO for I2C SCL
int i2c1sda_gpio; // GPIO for I2C SDA
int i2c_addr; // I2C设备地址
struct mutex i2c_lock; // I2C操作锁
};
static struct switch_dev switch_device;
// I2C基本信号控制函数
#define I2C_DELAY_US 5
static void i2c_set_scl_high(struct switch_dev *dev) {
gpio_set_value(dev->i2c1scl_gpio, 1);
udelay(I2C_DELAY_US);
}
static void i2c_set_scl_low(struct switch_dev *dev) {
gpio_set_value(dev->i2c1scl_gpio, 0);
udelay(I2C_DELAY_US);
}
static void i2c_set_sda_high(struct switch_dev *dev) {
gpio_set_value(dev->i2c1sda_gpio, 1);
udelay(I2C_DELAY_US);
}
static void i2c_set_sda_low(struct switch_dev *dev) {
gpio_set_value(dev->i2c1sda_gpio, 0);
udelay(I2C_DELAY_US);
}
static int i2c_read_sda(struct switch_dev *dev) {
gpio_direction_input(dev->i2c1sda_gpio);
udelay(I2C_DELAY_US);
return gpio_get_value(dev->i2c1sda_gpio);
}
static void i2c_start(struct switch_dev *dev) {
i2c_set_sda_high(dev);
i2c_set_scl_high(dev);
i2c_set_sda_low(dev);
udelay(I2C_DELAY_US);
i2c_set_scl_low(dev);
}
static void i2c_stop(struct switch_dev *dev) {
i2c_set_sda_low(dev);
i2c_set_scl_high(dev);
i2c_set_sda_high(dev);
udelay(I2C_DELAY_US);
}
static int i2c_send_byte(struct switch_dev *dev, unsigned char byte) {
int i;
for (i = 0; i < 8; i++) {
if (byte & 0x80)
i2c_set_sda_high(dev);
else
i2c_set_sda_low(dev);
byte <<= 1;
i2c_set_scl_high(dev);
i2c_set_scl_low(dev);
}
// 读取ACK
i2c_set_sda_high(dev); // 释放SDA线
gpio_direction_input(dev->i2c1sda_gpio);
udelay(I2C_DELAY_US);
i2c_set_scl_high(dev);
int ack = i2c_read_sda(dev);
i2c_set_scl_low(dev);
return ack;
}
static unsigned char i2c_receive_byte(struct switch_dev *dev, int ack) {
int i;
unsigned char byte = 0;
gpio_direction_input(dev->i2c1sda_gpio);
for (i = 0; i < 8; i++) {
byte <<= 1;
i2c_set_scl_high(dev);
if (i2c_read_sda(dev))
byte |= 0x01;
i2c_set_scl_low(dev);
}
// 发送ACK/NACK
if (ack)
i2c_set_sda_low(dev);
else
i2c_set_sda_high(dev);
gpio_direction_output(dev->i2c1sda_gpio, ack ? 0 : 1);
udelay(I2C_DELAY_US);
i2c_set_scl_high(dev);
udelay(I2C_DELAY_US);
i2c_set_scl_low(dev);
gpio_direction_input(dev->i2c1sda_gpio);
return byte;
}
static int i2c_write_addr(struct switch_dev *dev, unsigned char addr, int read) {
unsigned char byte = (addr << 1) | (read ? 1 : 0);
return i2c_send_byte(dev, byte);
}
static int i2c_write_reg(struct switch_dev *dev, unsigned char reg, unsigned char value) {
int ret;
mutex_lock(&dev->i2c_lock);
i2c_start(dev);
ret = i2c_send_byte(dev, (dev->i2c_addr << 1) | 0); // 写操作
if (ret) { // NACK
goto stop;
}
ret = i2c_send_byte(dev, reg);
if (ret) {
goto stop;
}
ret = i2c_send_byte(dev, value);
if (ret) {
goto stop;
}
i2c_stop(dev);
mutex_unlock(&dev->i2c_lock);
return 0;
stop:
i2c_stop(dev);
mutex_unlock(&dev->i2c_lock);
return -EIO;
}
static int i2c_read_reg(struct switch_dev *dev, unsigned char reg, unsigned char *value) {
int ret;
mutex_lock(&dev->i2c_lock);
i2c_start(dev);
ret = i2c_send_byte(dev, (dev->i2c_addr << 1) | 0); // 写操作
if (ret) { // NACK
goto stop;
}
ret = i2c_send_byte(dev, reg);
if (ret) {
goto stop;
}
// 重复起始
i2c_start(dev);
ret = i2c_send_byte(dev, (dev->i2c_addr << 1) | 1); // 读操作
if (ret) {
goto stop;
}
*value = i2c_receive_byte(dev, 0); // NACK
i2c_stop(dev);
mutex_unlock(&dev->i2c_lock);
return 0;
stop:
i2c_stop(dev);
mutex_unlock(&dev->i2c_lock);
return -EIO;
}
// RTL8367寄存器读写
static int rtl8367_read_reg(struct switch_dev *dev, unsigned short reg, unsigned short *value) {
unsigned char reg_high = (reg >> 8) & 0xFF;
unsigned char reg_low = reg & 0xFF;
unsigned char val_high, val_low;
int ret;
// 根据手册,可能需要先写入寄存器地址
ret = i2c_write_reg(dev, reg_high, reg_low);
if (ret < 0) {
printk(KERN_ERR "Failed to write register address\n");
return ret;
}
// 读取高字节
ret = i2c_read_reg(dev, reg_high, &val_high);
if (ret < 0) {
printk(KERN_ERR "Failed to read register high byte\n");
return ret;
}
// 读取低字节
ret = i2c_read_reg(dev, reg_low, &val_low);
if (ret < 0) {
printk(KERN_ERR "Failed to read register low byte\n");
return ret;
}
*value = ((unsigned short)val_high << 8) | val_low;
return 0;
}
static int rtl8367_write_reg(struct switch_dev *dev, unsigned short reg, unsigned short value) {
unsigned char reg_high = (reg >> 8) & 0xFF;
unsigned char reg_low = reg & 0xFF;
unsigned char val_high = (value >> 8) & 0xFF;
unsigned char val_low = value & 0xFF;
int ret;
ret = i2c_write_reg(dev, reg_high, reg_low);
if (ret < 0) {
printk(KERN_ERR "Failed to write register address\n");
return ret;
}
ret = i2c_write_reg(dev, val_high, val_low);
if (ret < 0) {
printk(KERN_ERR "Failed to write register value\n");
return ret;
}
return 0;
}
// 文件操作函数
static int switch_open(struct inode *inode, struct file *filp) {
filp->private_data = &switch_device; // 设置私有数据
return 0;
}
static ssize_t switch_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {
// 具体实现取决于需要通过写入命令执行什么操作
// 这里简单处理:假设用户传入寄存器地址和数据,格式为 "reg data"
char kbuf[16];
unsigned short reg, val;
struct switch_dev *dev = filp->private_data;
int ret;
if (cnt > sizeof(kbuf) - 1)
return -EINVAL;
if (copy_from_user(kbuf, buf, cnt))
return -EFAULT;
kbuf[cnt] = '\0';
if (sscanf(kbuf, "%hx %hx", ®, &val) != 2)
return -EINVAL;
ret = rtl8367_write_reg(dev, reg, val);
if (ret < 0)
return ret;
return cnt;
}
static ssize_t switch_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {
// 具体实现取决于需要返回什么信息
// 这里简单处理:假设用户传入寄存器地址并读取数据
char kbuf[16];
unsigned short reg;
unsigned short val;
struct switch_dev *dev = filp->private_data;
int ret;
if (copy_from_user(kbuf, buf, min(cnt, sizeof(kbuf)-1)))
return -EFAULT;
kbuf[min(cnt, sizeof(kbuf)-1)] = '\0';
if (sscanf(kbuf, "%hx", ®) != 1)
return -EINVAL;
ret = rtl8367_read_reg(dev, reg, &val);
if (ret < 0)
return ret;
return sprintf(buf, "0x%04X\n", val);
}
static int switch_release(struct inode *inode, struct file *filp) {
return 0;
}
static const struct file_operations switch_fops = {
.owner = THIS_MODULE,
.open = switch_open,
.write = switch_write,
.read = switch_read,
.release = switch_release,
};
// Sysfs接口定义
static struct kobject *rtl8367_kobj;
static ssize_t scl_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
struct switch_dev *dev = &switch_device;
unsigned int val;
if (kstrtouint(buf, 10, &val))
return -EINVAL;
gpio_set_value(dev->i2c1scl_gpio, val ? 1 : 0);
return count;
}
static ssize_t scl_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
struct switch_dev *dev = &switch_device;
int val = gpio_get_value(dev->i2c1scl_gpio);
return sprintf(buf, "%d\n", val);
}
static ssize_t sda_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
struct switch_dev *dev = &switch_device;
unsigned int val;
if (kstrtouint(buf, 10, &val))
return -EINVAL;
gpio_set_value(dev->i2c1sda_gpio, val ? 1 : 0);
return count;
}
static ssize_t sda_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
struct switch_dev *dev = &switch_device;
int val = gpio_get_value(dev->i2c1sda_gpio);
return sprintf(buf, "%d\n", val);
}
static ssize_t i2c_read_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
struct switch_dev *dev = &switch_device;
unsigned short reg;
unsigned short val;
if (kstrtoushort(buf, 16, ®))
return -EINVAL;
if (rtl8367_read_reg(dev, reg, &val) < 0)
return -EIO;
printk(KERN_INFO "Read reg 0x%04X: 0x%04X\n", reg, val);
return count;
}
static ssize_t i2c_read_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
return 0;
}
static ssize_t i2c_write_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
struct switch_dev *dev = &switch_device;
unsigned short reg, val;
if (sscanf(buf, "%hx %hx", ®, &val) != 2)
return -EINVAL;
if (rtl8367_write_reg(dev, reg, val) < 0)
return -EIO;
printk(KERN_INFO "Wrote reg 0x%04X: 0x%04X\n", reg, val);
return count;
}
static ssize_t i2c_write_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
return 0;
}
static struct kobj_attribute scl_attr = __ATTR(scl, 0664, scl_show, scl_store);
static struct kobj_attribute sda_attr = __ATTR(sda, 0664, sda_show, sda_store);
static struct kobj_attribute i2c_read_attr = __ATTR(i2c_read, 0220, i2c_read_show, i2c_read_store);
static struct kobj_attribute i2c_write_attr = __ATTR(i2c_write, 0220, i2c_write_show, i2c_write_store);
static int rtl8367_sysfs_init_func(void) {
int error = 0;
rtl8367_kobj = kobject_create_and_add("rtl8367", kernel_kobj);
if (!rtl8367_kobj)
return -ENOMEM;
error = sysfs_create_file(rtl8367_kobj, &scl_attr.attr);
if (error) {
printk(KERN_ERR "Failed to create sysfs scl file\n");
return error;
}
error = sysfs_create_file(rtl8367_kobj, &sda_attr.attr);
if (error) {
printk(KERN_ERR "Failed to create sysfs sda file\n");
return error;
}
error = sysfs_create_file(rtl8367_kobj, &i2c_read_attr.attr);
if (error) {
printk(KERN_ERR "Failed to create sysfs i2c_read file\n");
return error;
}
error = sysfs_create_file(rtl8367_kobj, &i2c_write_attr.attr);
if (error) {
printk(KERN_ERR "Failed to create sysfs i2c_write file\n");
return error;
}
return 0;
}
static void rtl8367_sysfs_exit_func(void) {
kobject_put(rtl8367_kobj);
}
// 驱动初始化
static int __init RTL8367_init(void) {
int ret = 0;
// 初始化锁
mutex_init(&switch_device.i2c_lock);
// 创建设备号
ret = alloc_chrdev_region(&switch_device.devid, 0, SWITCH_CNT, SWITCH_NAME);
if (ret < 0) {
printk(KERN_ERR "Failed to allocate chrdev region\n");
return ret;
}
// 初始化cdev
cdev_init(&switch_device.cdev, &switch_fops);
switch_device.cdev.owner = THIS_MODULE;
// 添加cdev
ret = cdev_add(&switch_device.cdev, switch_device.devid, SWITCH_CNT);
if (ret < 0) {
printk(KERN_ERR "Failed to add cdev\n");
goto unregister_chrdev;
}
// 创建类
switch_device.class = class_create(THIS_MODULE, SWITCH_NAME);
if (IS_ERR(switch_device.class)) {
ret = PTR_ERR(switch_device.class);
printk(KERN_ERR "Failed to create class\n");
goto del_cdev;
}
// 创建设备
switch_device.device = device_create(switch_device.class, NULL, switch_device.devid, NULL, SWITCH_NAME);
if (IS_ERR(switch_device.device)) {
ret = PTR_ERR(switch_device.device);
printk(KERN_ERR "Failed to create device\n");
goto destroy_class;
}
// 获取设备节点
switch_device.nd = of_find_node_by_path("/switch");
if (!switch_device.nd) {
printk(KERN_ERR "switch node not found!\n");
ret = -EINVAL;
goto destroy_device;
}
printk(KERN_INFO "switch node found.\n");
// 获取并申请i2c1scl_gpio
switch_device.i2c1scl_gpio = of_get_named_gpio(switch_device.nd, "i2c1scl-gpio", 0);
if (switch_device.i2c1scl_gpio < 0) {
printk(KERN_ERR "can't get i2c1scl-gpio\n");
ret = -EINVAL;
goto destroy_device;
}
printk(KERN_INFO "got i2c1scl-gpio.\n");
ret = gpio_request(switch_device.i2c1scl_gpio, "i2c1scl-gpio");
if (ret) {
printk(KERN_ERR "Failed to request GPIO %d, error %d\n", switch_device.i2c1scl_gpio, ret);
goto destroy_device;
}
ret = gpio_direction_output(switch_device.i2c1scl_gpio, 1);
if (ret < 0) {
printk(KERN_ERR "can't set gpio %d!\n", switch_device.i2c1scl_gpio);
goto free_gpio_scl;
}
printk(KERN_INFO "set gpio %d.\n", switch_device.i2c1scl_gpio);
// 获取并申请i2c1sda_gpio
switch_device.i2c1sda_gpio = of_get_named_gpio(switch_device.nd, "i2c1sda-gpio", 0);
if (switch_device.i2c1sda_gpio < 0) {
printk(KERN_ERR "can't get i2c1sda-gpio\n");
ret = -EINVAL;
goto free_gpio_scl;
}
printk(KERN_INFO "got i2c1sda-gpio.\n");
ret = gpio_request(switch_device.i2c1sda_gpio, "i2c1sda-gpio");
if (ret) {
printk(KERN_ERR "Failed to request GPIO %d, error %d\n", switch_device.i2c1sda_gpio, ret);
goto free_gpio_scl;
}
ret = gpio_direction_output(switch_device.i2c1sda_gpio, 1);
if (ret < 0) {
printk(KERN_ERR "can't set gpio %d!\n", switch_device.i2c1sda_gpio);
goto free_gpio_sda;
}
printk(KERN_INFO "set gpio %d.\n", switch_device.i2c1sda_gpio);
// 设置I2C设备地址
switch_device.i2c_addr = RTL8367_I2C_ADDR;
// 初始化sysfs接口
ret = rtl8367_sysfs_init_func();
if (ret) {
printk(KERN_ERR "Failed to initialize sysfs\n");
goto free_gpio_sda;
}
printk(KERN_INFO "RTL8367 driver initialized successfully.\n");
return 0;
free_gpio_sda:
gpio_free(switch_device.i2c1sda_gpio);
free_gpio_scl:
gpio_free(switch_device.i2c1scl_gpio);
destroy_device:
device_destroy(switch_device.class, switch_device.devid);
destroy_class:
class_destroy(switch_device.class);
del_cdev:
cdev_del(&switch_device.cdev);
unregister_chrdev:
unregister_chrdev_region(switch_device.devid, SWITCH_CNT);
return ret;
}
// 驱动卸载
static void __exit RTL8367_exit(void) {
// 清理sysfs接口
rtl8367_sysfs_exit_func();
// 删除cdev和注销设备号
cdev_del(&switch_device.cdev);
unregister_chrdev_region(switch_device.devid, SWITCH_CNT);
// 删除设备和类
device_destroy(switch_device.class, switch_device.devid);
class_destroy(switch_device.class);
// 释放GPIO
gpio_free(switch_device.i2c1scl_gpio);
gpio_free(switch_device.i2c1sda_gpio);
printk(KERN_INFO "RTL8367 driver exited.\n");
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("RTL8367 Switch GPIO and I2C Driver");
MODULE_VERSION("1.0");
module_init(RTL8367_init);
module_exit(RTL8367_exit);
```
### 6. 测试与验证
1. **编译驱动**:使用适当的Makefile交叉编译驱动模块。
```makefile
obj-m += rtl8367_gpio_i2c.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
```
2. **加载驱动**:
```bash
insmod rtl8367_gpio_i2c.ko
```
3. **验证设备节点**:
检查`/dev/RTL8367_RW`是否存在。
```bash
ls -l /dev/RTL8367_RW
```
4. **使用sysfs接口进行I2C读写**:
- **读取寄存器**:
```bash
echo "0x0010" > /sys/rtl8367/i2c_read
```
查看内核日志:
```bash
dmesg | tail
```
- **写入寄存器**:
```bash
echo "0x0010 0x1234" > /sys/rtl8367/i2c_write
```
查看内核日志:
```bash
dmesg | tail
```
5. **通过字符设备读写**:
- **写入**:
```bash
echo "0010 1234" > /dev/RTL8367_RW
```
- **读取**:
```bash
echo "0010" > /dev/RTL8367_RW
cat /dev/RTL8367_RW
```
输出应为:
```
0x1234
```
6. **调试**:
- 使用`dmesg`查看内核日志,确认I2C通信是否正常。
- 使用逻辑分析仪或示波器观察SCL和SDA信号,确保时序符合I2C协议要求。
### 7. 其他建议
- **同步机制**:在多线程环境下,`mutex`保证了I2C操作的互斥性,避免竞态条件。
- **错误处理**:进一步完善错误处理机制,如重试机制。
- **性能优化**:根据需求优化I2C延迟,提高通信速率。
- **扩展功能**:根据需要实现更多的寄存器读写功能,或支持批量操作。
通过上述步骤,你应能在现有的GPIO驱动基础上,实现基于GPIO的I2C模拟,并成功获取RTL8367的寄存器状态。请根据实际硬件和手册进行必要的调整和优化。