11基于18101主板根据RTL8367手册,完成GPIO模拟I2C,实现RTL8367寄存器状态获取

好的,作为一名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, &reg))
        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", &reg, &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", &reg, &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", &reg) != 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, &reg))
        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", &reg, &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的寄存器状态。请根据实际硬件和手册进行必要的调整和优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值