一种使用ioctl输入这三个地址,进行客户端交互。第二种是直接把这三个数据写死在里面,只读这三个数据

好的,下面我将为您提供两种实现方案,用于通过 GPIO 模拟 I2C 协议读取数据。第一种方案使用 `ioctl` 接口让用户空间应用程序传入 `bus_id`、`i2c_addr` 和 `register_address`,以实现灵活的读操作;第二种方案则将这三个参数硬编码在驱动中,仅支持读取特定的寄存器地址。

为了便于理解,代码中包含了详细的注释,解释每一部分的功能和实现细节。

---

## 方案一:使用 `ioctl` 接口输入 `bus_id`、`i2c_addr` 和 `register_address` 进行客户端交互

### 1. **头文件和宏定义**

首先,定义所需的头文件和宏,包括 `ioctl` 命令的定义。

```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/mutex.h>

// 模块元数据
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("RTL8367 Switch GPIO and I2C Driver with IOCTL Interface");
MODULE_VERSION("1.0");

// 设备名称和数量
#define SWITCH_CNT 1
#define SWITCH_NAME "RTL8367_RW"

// GPIO 状态定义
#define SWITCH_OFF 0
#define SWITCH_ON 1

// IOCTL 命令定义
#define RTL8367_IOC_MAGIC 'r' // 魔术数,用于区分不同设备的 IOCTL 命令
#define RTL8367_IOC_I2C_READ _IOWR(RTL8367_IOC_MAGIC, 1, struct i2c_read_data)

// IOCTL 数据结构,用户空间传入和驱动返回
struct i2c_read_data {
    int bus_id;            // I2C 总线编号
    int i2c_addr;          // I2C 设备地址
    int register_address;  // 寄存器地址
    int data;              // 读取的数据
};
```

### 2. **I2C GPIO 模拟函数**

接下来,实现基于 GPIO 模拟 I2C 协议的基本操作,包括启动条件、停止条件、发送字节、接收字节等。

```c
// 定义 I2C 时序延时,单位为微秒(us)
// 根据实际硬件时序要求调整
#define I2C_DELAY_US 5

// 模拟 I2C 协议的各种状态和操作
struct i2c_gpio {
    int scl_gpio;           // 时钟线 GPIO 编号
    int sda_gpio;           // 数据线 GPIO 编号
    struct mutex lock;      // 互斥锁,防止并发访问
};

// 初始化 GPIO 模拟 I2C
static void i2c_gpio_init(struct i2c_gpio *i2c_gpio)
{
    // 设置 SDA 和 SCL 为高电平,空闲状态
    gpio_set_value(i2c_gpio->sda_gpio, 1);
    gpio_set_value(i2c_gpio->scl_gpio, 1);
}

// 设置 SCL 线的电平
static void i2c_gpio_set_scl(struct i2c_gpio *i2c_gpio, int value)
{
    gpio_set_value(i2c_gpio->scl_gpio, value);
    udelay(I2C_DELAY_US);
}

// 设置 SDA 线的电平
static void i2c_gpio_set_sda(struct i2c_gpio *i2c_gpio, int value)
{
    gpio_set_value(i2c_gpio->sda_gpio, value);
    udelay(I2C_DELAY_US);
}

// 读取 SDA 线的电平
static int i2c_gpio_get_sda(struct i2c_gpio *i2c_gpio)
{
    // 设置 SDA 为输入模式,以便读取电平
    gpio_direction_input(i2c_gpio->sda_gpio);
    udelay(I2C_DELAY_US);
    return gpio_get_value(i2c_gpio->sda_gpio);
}

// 生成 I2C 时序延时
static void i2c_delay(void)
{
    udelay(I2C_DELAY_US);
}

// 生成 I2C 启动条件
static void i2c_start(struct i2c_gpio *i2c_gpio)
{
    // SDA 从高变低 while SCL 为高
    i2c_gpio_set_sda(i2c_gpio, 1);
    i2c_gpio_set_scl(i2c_gpio, 1);
    i2c_delay();
    i2c_gpio_set_sda(i2c_gpio, 0);
    i2c_delay();
    i2c_gpio_set_scl(i2c_gpio, 0);
    i2c_delay();
}

// 生成 I2C 停止条件
static void i2c_stop(struct i2c_gpio *i2c_gpio)
{
    // SDA 从低变高 while SCL 为高
    i2c_gpio_set_scl(i2c_gpio, 0);
    i2c_gpio_set_sda(i2c_gpio, 0);
    i2c_delay();
    i2c_gpio_set_scl(i2c_gpio, 1);
    i2c_delay();
    i2c_gpio_set_sda(i2c_gpio, 1);
    i2c_delay();
}

// 向 I2C 总线发送一个字节,并读取应答位
static int i2c_write_byte(struct i2c_gpio *i2c_gpio, unsigned char byte)
{
    int i;
    int ack;

    // 逐位发送字节,从最高位开始
    for (i = 7; i >= 0; i--) {
        // 设置 SDA 线的电平
        i2c_gpio_set_sda(i2c_gpio, (byte >> i) & 0x01);
        // 生成一个时钟脉冲
        i2c_gpio_set_scl(i2c_gpio, 1);
        i2c_gpio_set_scl(i2c_gpio, 0);
    }

    // 发送完字节后,释放 SDA 线并读取应答位
    i2c_gpio_set_sda(i2c_gpio, 1); // 释放 SDA
    i2c_gpio_set_scl(i2c_gpio, 1); // 生成时钟脉冲
    ack = i2c_gpio_get_sda(i2c_gpio);
    i2c_gpio_set_scl(i2c_gpio, 0);

    if (ack == 0) {
        // 收到应答
        return 0;
    } else {
        // 未收到应答
        return -1;
    }
}

// 从 I2C 总线读取一个字节,并发送应答位
static unsigned char i2c_read_byte(struct i2c_gpio *i2c_gpio, int ack)
{
    int i;
    unsigned char byte = 0;

    // 设置 SDA 为输入模式,以便读取数据
    gpio_direction_input(i2c_gpio->sda_gpio);

    // 逐位读取字节,从最高位开始
    for (i = 7; i >= 0; i--) {
        i2c_gpio_set_scl(i2c_gpio, 1); // 拉高 SCL
        if (gpio_get_value(i2c_gpio->sda_gpio)) {
            byte |= (1 << i);
        }
        i2c_gpio_set_scl(i2c_gpio, 0); // 拉低 SCL
    }

    // 发送应答位
    if (ack) {
        i2c_gpio_set_sda(i2c_gpio, 0); // 发送 ACK
    } else {
        i2c_gpio_set_sda(i2c_gpio, 1); // 发送 NACK
    }
    i2c_gpio_set_scl(i2c_gpio, 1); // 生成时钟脉冲
    i2c_gpio_set_scl(i2c_gpio, 0); // 拉低 SCL
    i2c_delay();

    // 恢复 SDA 为输入模式
    gpio_direction_input(i2c_gpio->sda_gpio);

    return byte;
}

// 基于 GPIO 模拟 I2C 传输函数
static int i2c_transfer_func(struct i2c_gpio *i2c_gpio, int addr, unsigned char *write_buf, int write_len, unsigned char *read_buf, int read_len)
{
    int ret;
    int i;

    // 生成启动条件
    i2c_start(i2c_gpio);

    // 发送设备地址,写操作(最低位为 0)
    ret = i2c_write_byte(i2c_gpio, (addr << 1) | 0);
    if (ret < 0) {
        printk(KERN_ERR "%s: No ACK after sending device address for write\n", LOG_TAG);
        i2c_stop(i2c_gpio);
        return -1;
    }

    // 发送写入的数据
    for (i = 0; i < write_len; i++) {
        ret = i2c_write_byte(i2c_gpio, write_buf[i]);
        if (ret < 0) {
            printk(KERN_ERR "%s: No ACK after sending data byte %d\n", LOG_TAG, i);
            i2c_stop(i2c_gpio);
            return -1;
        }
    }

    // 生成启动条件(重复启动)
    i2c_start(i2c_gpio);

    // 发送设备地址,读操作(最低位为 1)
    ret = i2c_write_byte(i2c_gpio, (addr << 1) | 1);
    if (ret < 0) {
        printk(KERN_ERR "%s: No ACK after sending device address for read\n", LOG_TAG);
        i2c_stop(i2c_gpio);
        return -1;
    }

    // 读取数据
    for (i = 0; i < read_len; i++) {
        // 发送 ACK,除了最后一个字节发送 NACK
        read_buf[i] = i2c_read_byte(i2c_gpio, (i < (read_len - 1)) ? 1 : 0);
    }

    // 生成停止条件
    i2c_stop(i2c_gpio);

    return 0;
}
```

### 3. **设备结构体和全局变量**

定义设备结构体和全局变量,包含字符设备、设备号、类、GPIO 信息等。

```c
// 定义设备结构体
struct switch_dev {
    dev_t devid;               // 设备号
    struct device *device;     // 设备结构体
    struct class *class;       // 设备类
    struct cdev cdev;          // 字符设备结构体
    struct device_node *nd;    // 设备节点
    struct i2c_gpio i2c_gpio;  // GPIO 模拟 I2C 结构体
};

static struct switch_dev switch_device;
```

### 4. **文件操作函数**

实现设备的 `open`、`release` 和 `unlocked_ioctl` 函数。

```c
// Open 函数
static int switch_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &switch_device; // 设置私有数据
    return 0;
}

// Release 函数
static int switch_release(struct inode *inode, struct file *filp)
{
    return 0;
}

// IOCTL 函数
static long switch_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct switch_dev *dev = filp->private_data;
    struct i2c_read_data user_data;
    unsigned char write_buf[3];
    unsigned char read_buf[1];
    int ret;

    // 检查 IOCTL 命令是否为我们定义的
    if (_IOC_TYPE(cmd) != RTL8367_IOC_MAGIC) {
        return -EINVAL;
    }

    switch (cmd) {
        case RTL8367_IOC_I2C_READ:
            // 从用户空间复制数据到内核空间
            if (copy_from_user(&user_data, (void __user *)arg, sizeof(struct i2c_read_data))) {
                return -EFAULT;
            }

            // 加锁,防止并发访问
            mutex_lock(&dev->i2c_gpio.lock);

            // 准备写入的数据(寄存器地址),假设为 3 字节地址
            write_buf[0] = (user_data.register_address >> 16) & 0xFF;
            write_buf[1] = (user_data.register_address >> 8) & 0xFF;
            write_buf[2] = user_data.register_address & 0xFF;

            // 执行 I2C 传输
            ret = i2c_transfer_func(&dev->i2c_gpio, user_data.i2c_addr, write_buf, 3, read_buf, 1);
            if (ret < 0) {
                printk(KERN_ERR "%s: I2C transfer failed\n", LOG_TAG);
                mutex_unlock(&dev->i2c_gpio.lock);
                return -EIO;
            }

            // 将读取的数据返回给用户空间
            user_data.data = read_buf[0];
            if (copy_to_user((void __user *)arg, &user_data, sizeof(struct i2c_read_data))) {
                mutex_unlock(&dev->i2c_gpio.lock);
                return -EFAULT;
            }

            // 解锁
            mutex_unlock(&dev->i2c_gpio.lock);
            break;

        default:
            return -EINVAL;
    }

    return 0;
}

// 定义文件操作结构体
static const struct file_operations switch_fops = {
    .owner = THIS_MODULE,
    .open = switch_open,
    .release = switch_release,
    .unlocked_ioctl = switch_ioctl, // 支持 ioctl
};
```

### 5. **驱动初始化和卸载函数**

实现驱动的初始化函数 `RTL8367_init` 和卸载函数 `RTL8367_exit`,包括设备号分配、字符设备注册、GPIO 请求等。

```c
// 驱动初始化
static int __init RTL8367_init(void)
{
    int ret = 0;

    // 分配设备号
    ret = alloc_chrdev_region(&switch_device.devid, 0, SWITCH_CNT, SWITCH_NAME);
    if (ret < 0) {
        printk(KERN_ERR "%s: Failed to allocate chrdev region\n", LOG_TAG);
        return ret;
    }

    // 初始化字符设备
    cdev_init(&switch_device.cdev, &switch_fops);
    switch_device.cdev.owner = THIS_MODULE;

    // 添加字符设备
    ret = cdev_add(&switch_device.cdev, switch_device.devid, SWITCH_CNT);
    if (ret < 0) {
        printk(KERN_ERR "%s: Failed to add cdev\n", LOG_TAG);
        goto unregister_chrdev;
    }

    // 创建设备类
    switch_device.class = class_create(THIS_MODULE, SWITCH_NAME);
    if (IS_ERR(switch_device.class)) {
        printk(KERN_ERR "%s: Failed to create class\n", LOG_TAG);
        ret = PTR_ERR(switch_device.class);
        goto del_cdev;
    }

    // 创建设备节点
    switch_device.device = device_create(switch_device.class, NULL, switch_device.devid, NULL, SWITCH_NAME);
    if (IS_ERR(switch_device.device)) {
        printk(KERN_ERR "%s: Failed to create device\n", LOG_TAG);
        ret = PTR_ERR(switch_device.device);
        goto destroy_class;
    }

    // 查找设备节点
    switch_device.nd = of_find_node_by_path("/switch");
    if (!switch_device.nd) {
        printk(KERN_ERR "%s: switch node not found!\n", LOG_TAG);
        ret = -EINVAL;
        goto destroy_device;
    }
    printk(KERN_INFO "%s: switch node found.\n", LOG_TAG);

    // 获取并请求 GPIO 引脚
    switch_device.i2c_gpio.scl_gpio = of_get_named_gpio(switch_device.nd, "i2c1scl-gpio", 0);
    if (switch_device.i2c_gpio.scl_gpio < 0) {
        printk(KERN_ERR "%s: can't get i2c1scl-gpio\n", LOG_TAG);
        ret = -EINVAL;
        goto destroy_device;
    }
    printk(KERN_INFO "%s: got i2c1scl-gpio.\n", LOG_TAG);

    ret = gpio_request(switch_device.i2c_gpio.scl_gpio, "i2c1scl-gpio");
    if (ret) {
        printk(KERN_ERR "%s: Failed to request GPIO %d, error %d\n", LOG_TAG, switch_device.i2c_gpio.scl_gpio, ret);
        goto destroy_device;
    }

    ret = gpio_direction_output(switch_device.i2c_gpio.scl_gpio, 1);
    if (ret < 0) {
        printk(KERN_ERR "%s: can't set gpio %d!\n", LOG_TAG, switch_device.i2c_gpio.scl_gpio);
        goto free_gpio_scl;
    }
    printk(KERN_INFO "%s: set gpio %d.\n", LOG_TAG, switch_device.i2c_gpio.scl_gpio);

    switch_device.i2c_gpio.sda_gpio = of_get_named_gpio(switch_device.nd, "i2c1sda-gpio", 0);
    if (switch_device.i2c_gpio.sda_gpio < 0) {
        printk(KERN_ERR "%s: can't get i2c1sda-gpio\n", LOG_TAG);
        ret = -EINVAL;
        goto free_gpio_scl;
    }
    printk(KERN_INFO "%s: got i2c1sda-gpio.\n", LOG_TAG);

    ret = gpio_request(switch_device.i2c_gpio.sda_gpio, "i2c1sda-gpio");
    if (ret) {
        printk(KERN_ERR "%s: Failed to request GPIO %d, error %d\n", LOG_TAG, switch_device.i2c_gpio.sda_gpio, ret);
        goto free_gpio_scl;
    }

    ret = gpio_direction_output(switch_device.i2c_gpio.sda_gpio, 1);
    if (ret < 0) {
        printk(KERN_ERR "%s: can't set gpio %d!\n", LOG_TAG, switch_device.i2c_gpio.sda_gpio);
        goto free_gpio_sda;
    }
    printk(KERN_INFO "%s: set gpio %d.\n", LOG_TAG, switch_device.i2c_gpio.sda_gpio);

    // 初始化 I2C GPIO 模拟
    i2c_gpio_init(&switch_device.i2c_gpio);

    // 初始化互斥锁
    mutex_init(&switch_device.i2c_gpio.lock);

    printk(KERN_INFO "%s: Driver initialized successfully.\n", LOG_TAG);
    return 0;

// 错误处理和资源释放
free_gpio_sda:
    gpio_free(switch_device.i2c_gpio.sda_gpio);
free_gpio_scl:
    gpio_free(switch_device.i2c_gpio.scl_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)
{
    // 删除设备和类
    device_destroy(switch_device.class, switch_device.devid);
    class_destroy(switch_device.class);

    // 删除字符设备和注销设备号
    cdev_del(&switch_device.cdev);
    unregister_chrdev_region(switch_device.devid, SWITCH_CNT);

    // 释放 GPIO
    gpio_free(switch_device.i2c_gpio.scl_gpio);
    gpio_free(switch_device.i2c_gpio.sda_gpio);

    printk(KERN_INFO "%s: Driver exited.\n", LOG_TAG);
}

module_init(RTL8367_init);
module_exit(RTL8367_exit);
```

### 6. **驱动导出**

将初始化和卸载函数与内核模块关联。

```c
module_init(RTL8367_init);
module_exit(RTL8367_exit);
```

---

## 方案二:将 `bus_id`、`i2c_addr` 和 `register_address` 硬编码在驱动中

在这种方案中,驱动内部直接使用固定的 `bus_id`、`i2c_addr` 和 `register_address`,无需用户通过 `ioctl` 传入。这适用于只需与特定设备通信的场景。

### 1. **头文件和宏定义**

```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/mutex.h>

// 模块元数据
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("RTL8367 Switch GPIO and I2C Driver with Fixed Parameters");
MODULE_VERSION("1.0");

// 设备名称和数量
#define SWITCH_CNT 1
#define SWITCH_NAME "RTL8367_RW"

// GPIO 状态定义
#define SWITCH_OFF 0
#define SWITCH_ON 1

// 硬编码的 I2C 参数
#define FIXED_BUS_ID 1
#define FIXED_I2C_ADDR 0x5C
#define FIXED_REGISTER_ADDRESS 0x1B03 // 注意:0x1B03 超过 8 位,需要根据实际寄存器地址长度调整
```

### 2. **I2C GPIO 模拟函数**

与方案一相同,基于 GPIO 模拟 I2C 协议的基本操作。

```c
// 定义 I2C 时序延时,单位为微秒(us)
// 根据实际硬件时序要求调整
#define I2C_DELAY_US 5

// 模拟 I2C 协议的各种状态和操作
struct i2c_gpio {
    int scl_gpio;           // 时钟线 GPIO 编号
    int sda_gpio;           // 数据线 GPIO 编号
    struct mutex lock;      // 互斥锁,防止并发访问
};

// 初始化 GPIO 模拟 I2C
static void i2c_gpio_init(struct i2c_gpio *i2c_gpio)
{
    // 设置 SDA 和 SCL 为高电平,空闲状态
    gpio_set_value(i2c_gpio->sda_gpio, 1);
    gpio_set_value(i2c_gpio->scl_gpio, 1);
}

// 设置 SCL 线的电平
static void i2c_gpio_set_scl(struct i2c_gpio *i2c_gpio, int value)
{
    gpio_set_value(i2c_gpio->scl_gpio, value);
    udelay(I2C_DELAY_US);
}

// 设置 SDA 线的电平
static void i2c_gpio_set_sda(struct i2c_gpio *i2c_gpio, int value)
{
    gpio_set_value(i2c_gpio->sda_gpio, value);
    udelay(I2C_DELAY_US);
}

// 读取 SDA 线的电平
static int i2c_gpio_get_sda(struct i2c_gpio *i2c_gpio)
{
    // 设置 SDA 为输入模式,以便读取电平
    gpio_direction_input(i2c_gpio->sda_gpio);
    udelay(I2C_DELAY_US);
    return gpio_get_value(i2c_gpio->sda_gpio);
}

// 生成 I2C 时序延时
static void i2c_delay(void)
{
    udelay(I2C_DELAY_US);
}

// 生成 I2C 启动条件
static void i2c_start(struct i2c_gpio *i2c_gpio)
{
    // SDA 从高变低 while SCL 为高
    i2c_gpio_set_sda(i2c_gpio, 1);
    i2c_gpio_set_scl(i2c_gpio, 1);
    i2c_delay();
    i2c_gpio_set_sda(i2c_gpio, 0);
    i2c_delay();
    i2c_gpio_set_scl(i2c_gpio, 0);
    i2c_delay();
}

// 生成 I2C 停止条件
static void i2c_stop(struct i2c_gpio *i2c_gpio)
{
    // SDA 从低变高 while SCL 为高
    i2c_gpio_set_scl(i2c_gpio, 0);
    i2c_gpio_set_sda(i2c_gpio, 0);
    i2c_delay();
    i2c_gpio_set_scl(i2c_gpio, 1);
    i2c_delay();
    i2c_gpio_set_sda(i2c_gpio, 1);
    i2c_delay();
}

// 向 I2C 总线发送一个字节,并读取应答位
static int i2c_write_byte(struct i2c_gpio *i2c_gpio, unsigned char byte)
{
    int i;
    int ack;

    // 逐位发送字节,从最高位开始
    for (i = 7; i >= 0; i--) {
        // 设置 SDA 线的电平
        i2c_gpio_set_sda(i2c_gpio, (byte >> i) & 0x01);
        // 生成一个时钟脉冲
        i2c_gpio_set_scl(i2c_gpio, 1);
        i2c_gpio_set_scl(i2c_gpio, 0);
    }

    // 发送完字节后,释放 SDA 线并读取应答位
    i2c_gpio_set_sda(i2c_gpio, 1); // 释放 SDA
    i2c_gpio_set_scl(i2c_gpio, 1); // 生成时钟脉冲
    ack = i2c_gpio_get_sda(i2c_gpio);
    i2c_gpio_set_scl(i2c_gpio, 0);

    if (ack == 0) {
        // 收到应答
        return 0;
    } else {
        // 未收到应答
        return -1;
    }
}

// 从 I2C 总线读取一个字节,并发送应答位
static unsigned char i2c_read_byte(struct i2c_gpio *i2c_gpio, int ack)
{
    int i;
    unsigned char byte = 0;

    // 设置 SDA 为输入模式,以便读取数据
    gpio_direction_input(i2c_gpio->sda_gpio);

    // 逐位读取字节,从最高位开始
    for (i = 7; i >= 0; i--) {
        i2c_gpio_set_scl(i2c_gpio, 1); // 拉高 SCL
        if (gpio_get_value(i2c_gpio->sda_gpio)) {
            byte |= (1 << i);
        }
        i2c_gpio_set_scl(i2c_gpio, 0); // 拉低 SCL
    }

    // 发送应答位
    if (ack) {
        i2c_gpio_set_sda(i2c_gpio, 0); // 发送 ACK
    } else {
        i2c_gpio_set_sda(i2c_gpio, 1); // 发送 NACK
    }
    i2c_gpio_set_scl(i2c_gpio, 1); // 生成时钟脉冲
    i2c_gpio_set_scl(i2c_gpio, 0); // 拉低 SCL
    i2c_delay();

    // 恢复 SDA 为输入模式
    gpio_direction_input(i2c_gpio->sda_gpio);

    return byte;
}

// 基于 GPIO 模拟 I2C 传输函数
static int i2c_transfer_fixed(struct i2c_gpio *i2c_gpio)
{
    int ret;
    unsigned char write_buf[3];
    unsigned char read_buf[1];

    // 准备写入的数据(寄存器地址),假设为 3 字节地址
    write_buf[0] = (FIXED_REGISTER_ADDRESS >> 16) & 0xFF;
    write_buf[1] = (FIXED_REGISTER_ADDRESS >> 8) & 0xFF;
    write_buf[2] = FIXED_REGISTER_ADDRESS & 0xFF;

    // 生成启动条件
    i2c_start(i2c_gpio);

    // 发送设备地址,写操作(最低位为 0)
    ret = i2c_write_byte(i2c_gpio, (FIXED_I2C_ADDR << 1) | 0);
    if (ret < 0) {
        printk(KERN_ERR "%s: No ACK after sending device address for write\n", LOG_TAG);
        i2c_stop(i2c_gpio);
        return -1;
    }

    // 发送写入的数据
    ret = i2c_write_byte(i2c_gpio, write_buf[0]);
    if (ret < 0) {
        printk(KERN_ERR "%s: No ACK after sending data byte 0\n", LOG_TAG);
        i2c_stop(i2c_gpio);
        return -1;
    }

    ret = i2c_write_byte(i2c_gpio, write_buf[1]);
    if (ret < 0) {
        printk(KERN_ERR "%s: No ACK after sending data byte 1\n", LOG_TAG);
        i2c_stop(i2c_gpio);
        return -1;
    }

    ret = i2c_write_byte(i2c_gpio, write_buf[2]);
    if (ret < 0) {
        printk(KERN_ERR "%s: No ACK after sending data byte 2\n", LOG_TAG);
        i2c_stop(i2c_gpio);
        return -1;
    }

    // 生成启动条件(重复启动)
    i2c_start(i2c_gpio);

    // 发送设备地址,读操作(最低位为 1)
    ret = i2c_write_byte(i2c_gpio, (FIXED_I2C_ADDR << 1) | 1);
    if (ret < 0) {
        printk(KERN_ERR "%s: No ACK after sending device address for read\n", LOG_TAG);
        i2c_stop(i2c_gpio);
        return -1;
    }

    // 读取数据,发送 NACK
    read_buf[0] = i2c_read_byte(i2c_gpio, 0);

    // 生成停止条件
    i2c_stop(i2c_gpio);

    // 打印读取的数据
    printk(KERN_INFO "%s: Read data 0x%02X from register 0x%X\n", LOG_TAG, read_buf[0], FIXED_REGISTER_ADDRESS);

    return 0;
}
```

### 3. **设备结构体和全局变量**

定义设备结构体和全局变量,包含字符设备、设备号、类、GPIO 信息等。

```c
// 定义设备结构体
struct switch_dev {
    dev_t devid;               // 设备号
    struct device *device;     // 设备结构体
    struct class *class;       // 设备类
    struct cdev cdev;          // 字符设备结构体
    struct device_node *nd;    // 设备节点
    struct i2c_gpio i2c_gpio;  // GPIO 模拟 I2C 结构体
};

static struct switch_dev switch_device;
```

### 4. **文件操作函数**

实现设备的 `open`、`release` 和 `read` 函数,其中 `read` 函数执行硬编码的 I2C 读操作。

```c
// Open 函数
static int switch_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &switch_device; // 设置私有数据
    return 0;
}

// Release 函数
static int switch_release(struct inode *inode, struct file *filp)
{
    return 0;
}

// Read 函数
static ssize_t switch_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    struct switch_dev *dev = filp->private_data;
    int ret;

    // 加锁,防止并发访问
    mutex_lock(&dev->i2c_gpio.lock);

    // 执行 I2C 传输
    ret = i2c_transfer_fixed(&dev->i2c_gpio);
    if (ret < 0) {
        printk(KERN_ERR "%s: Fixed I2C transfer failed\n", LOG_TAG);
        mutex_unlock(&dev->i2c_gpio.lock);
        return -EIO;
    }

    // 解锁
    mutex_unlock(&dev->i2c_gpio.lock);

    // 返回读取的字节数
    return 0;
}

// 定义文件操作结构体
static const struct file_operations switch_fops = {
    .owner = THIS_MODULE,
    .open = switch_open,
    .release = switch_release,
    .read = switch_read, // 支持 read
};
```

### 5. **驱动初始化和卸载函数**

实现驱动的初始化函数 `RTL8367_init` 和卸载函数 `RTL8367_exit`,包括设备号分配、字符设备注册、GPIO 请求等。

```c
// 驱动初始化
static int __init RTL8367_init(void)
{
    int ret = 0;

    // 分配设备号
    ret = alloc_chrdev_region(&switch_device.devid, 0, SWITCH_CNT, SWITCH_NAME);
    if (ret < 0) {
        printk(KERN_ERR "%s: Failed to allocate chrdev region\n", LOG_TAG);
        return ret;
    }

    // 初始化字符设备
    cdev_init(&switch_device.cdev, &switch_fops);
    switch_device.cdev.owner = THIS_MODULE;

    // 添加字符设备
    ret = cdev_add(&switch_device.cdev, switch_device.devid, SWITCH_CNT);
    if (ret < 0) {
        printk(KERN_ERR "%s: Failed to add cdev\n", LOG_TAG);
        goto unregister_chrdev;
    }

    // 创建设备类
    switch_device.class = class_create(THIS_MODULE, SWITCH_NAME);
    if (IS_ERR(switch_device.class)) {
        printk(KERN_ERR "%s: Failed to create class\n", LOG_TAG);
        ret = PTR_ERR(switch_device.class);
        goto del_cdev;
    }

    // 创建设备节点
    switch_device.device = device_create(switch_device.class, NULL, switch_device.devid, NULL, SWITCH_NAME);
    if (IS_ERR(switch_device.device)) {
        printk(KERN_ERR "%s: Failed to create device\n", LOG_TAG);
        ret = PTR_ERR(switch_device.device);
        goto destroy_class;
    }

    // 查找设备节点
    switch_device.nd = of_find_node_by_path("/switch");
    if (!switch_device.nd) {
        printk(KERN_ERR "%s: switch node not found!\n", LOG_TAG);
        ret = -EINVAL;
        goto destroy_device;
    }
    printk(KERN_INFO "%s: switch node found.\n", LOG_TAG);

    // 获取并请求 GPIO 引脚
    switch_device.i2c_gpio.scl_gpio = of_get_named_gpio(switch_device.nd, "i2c1scl-gpio", 0);
    if (switch_device.i2c_gpio.scl_gpio < 0) {
        printk(KERN_ERR "%s: can't get i2c1scl-gpio\n", LOG_TAG);
        ret = -EINVAL;
        goto destroy_device;
    }
    printk(KERN_INFO "%s: got i2c1scl-gpio.\n", LOG_TAG);

    ret = gpio_request(switch_device.i2c_gpio.scl_gpio, "i2c1scl-gpio");
    if (ret) {
        printk(KERN_ERR "%s: Failed to request GPIO %d, error %d\n", LOG_TAG, switch_device.i2c_gpio.scl_gpio, ret);
        goto destroy_device;
    }

    ret = gpio_direction_output(switch_device.i2c_gpio.scl_gpio, 1);
    if (ret < 0) {
        printk(KERN_ERR "%s: can't set gpio %d!\n", LOG_TAG, switch_device.i2c_gpio.scl_gpio);
        goto free_gpio_scl;
    }
    printk(KERN_INFO "%s: set gpio %d.\n", LOG_TAG, switch_device.i2c_gpio.scl_gpio);

    switch_device.i2c_gpio.sda_gpio = of_get_named_gpio(switch_device.nd, "i2c1sda-gpio", 0);
    if (switch_device.i2c_gpio.sda_gpio < 0) {
        printk(KERN_ERR "%s: can't get i2c1sda-gpio\n", LOG_TAG);
        ret = -EINVAL;
        goto free_gpio_scl;
    }
    printk(KERN_INFO "%s: got i2c1sda-gpio.\n", LOG_TAG);

    ret = gpio_request(switch_device.i2c_gpio.sda_gpio, "i2c1sda-gpio");
    if (ret) {
        printk(KERN_ERR "%s: Failed to request GPIO %d, error %d\n", LOG_TAG, switch_device.i2c_gpio.sda_gpio, ret);
        goto free_gpio_scl;
    }

    ret = gpio_direction_output(switch_device.i2c_gpio.sda_gpio, 1);
    if (ret < 0) {
        printk(KERN_ERR "%s: can't set gpio %d!\n", LOG_TAG, switch_device.i2c_gpio.sda_gpio);
        goto free_gpio_sda;
    }
    printk(KERN_INFO "%s: set gpio %d.\n", LOG_TAG, switch_device.i2c_gpio.sda_gpio);

    // 初始化 I2C GPIO 模拟
    i2c_gpio_init(&switch_device.i2c_gpio);

    // 初始化互斥锁
    mutex_init(&switch_device.i2c_gpio.lock);

    printk(KERN_INFO "%s: Driver initialized successfully.\n", LOG_TAG);
    return 0;

// 错误处理和资源释放
free_gpio_sda:
    gpio_free(switch_device.i2c_gpio.sda_gpio);
free_gpio_scl:
    gpio_free(switch_device.i2c_gpio.scl_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)
{
    // 删除设备和类
    device_destroy(switch_device.class, switch_device.devid);
    class_destroy(switch_device.class);

    // 删除字符设备和注销设备号
    cdev_del(&switch_device.cdev);
    unregister_chrdev_region(switch_device.devid, SWITCH_CNT);

    // 释放 GPIO
    gpio_free(switch_device.i2c_gpio.scl_gpio);
    gpio_free(switch_device.i2c_gpio.sda_gpio);

    printk(KERN_INFO "%s: Driver exited.\n", LOG_TAG);
}

module_init(RTL8367_init);
module_exit(RTL8367_exit);
```

---

## 详细解释

### 1. **方案一:使用 `ioctl` 接口**

#### **IOCTL 命令定义**

- **`RTL8367_IOC_MAGIC`**:用于区分不同设备的魔术数,确保 `ioctl` 命令的唯一性。
- **`RTL8367_IOC_I2C_READ`**:定义了一个 `ioctl` 命令,用于执行 I2C 读操作,用户空间应用程序通过该命令传入所需的参数。

#### **数据结构**

- **`struct i2c_read_data`**:用于在用户空间和内核空间之间传递 `bus_id`、`i2c_addr`、`register_address` 和读取的数据 `data`。

#### **文件操作函数**

- **`switch_open` 和 `switch_release`**:标准的设备文件操作函数,用于打开和关闭设备。
  
- **`switch_ioctl`**:
  - **参数检查**:首先检查 `cmd` 是否为定义的 `RTL8367_IOC_MAGIC` 类型。
  - **数据复制**:通过 `copy_from_user` 将用户空间传入的数据复制到内核空间的 `user_data` 结构体中。
  - **互斥锁**:在执行 I2C 传输前加锁,防止并发访问。
  - **准备写入数据**:将寄存器地址拆分成多个字节,根据实际硬件需求调整字节数。
  - **执行 I2C 传输**:调用 `i2c_transfer_func` 进行 I2C 数据传输。
  - **返回数据**:通过 `copy_to_user` 将读取到的数据返回给用户空间。
  - **错误处理**:在任何步骤失败时,进行适当的错误处理和资源释放。

#### **用户空间交互**

用户空间应用程序可以通过 `ioctl` 系统调用,传入 `bus_id`、`i2c_addr` 和 `register_address`,驱动将读取到的数据返回。例如:

```c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

// 定义与驱动中相同的结构体
struct i2c_read_data {
    int bus_id;
    int i2c_addr;
    int register_address;
    int data;
};

#define RTL8367_IOC_MAGIC 'r'
#define RTL8367_IOC_I2C_READ _IOWR(RTL8367_IOC_MAGIC, 1, struct i2c_read_data)

int main() {
    int fd;
    struct i2c_read_data read_data;

    // 打开设备文件
    fd = open("/dev/RTL8367_RW", O_RDWR);
    if (fd < 0) {
        perror("Failed to open device");
        return -1;
    }

    // 设置要读取的参数
    read_data.bus_id = 1;
    read_data.i2c_addr = 0x5C;
    read_data.register_address = 0x1B03;

    // 执行 ioctl 读取操作
    if (ioctl(fd, RTL8367_IOC_I2C_READ, &read_data) == -1) {
        perror("ioctl failed");
        close(fd);
        return -1;
    }

    // 输出读取的数据
    printf("Read data: 0x%X\n", read_data.data);

    // 关闭设备文件
    close(fd);
    return 0;
}
```

### 2. **方案二:硬编码 I2C 参数**

#### **硬编码参数定义**

将 `bus_id`、`i2c_addr` 和 `register_address` 定义为宏,驱动内部直接使用这些值进行 I2C 传输。

```c
#define FIXED_BUS_ID 1
#define FIXED_I2C_ADDR 0x5C
#define FIXED_REGISTER_ADDRESS 0x1B03
```

#### **文件操作函数**

在 `read` 函数中,驱动执行固定参数的 I2C 读操作。每次用户空间应用程序执行 `read` 调用时,驱动将读取固定寄存器的值。

```c
// Read 函数
static ssize_t switch_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    struct switch_dev *dev = filp->private_data;
    int ret;

    // 加锁,防止并发访问
    mutex_lock(&dev->i2c_gpio.lock);

    // 执行 I2C 传输
    ret = i2c_transfer_fixed(&dev->i2c_gpio);
    if (ret < 0) {
        printk(KERN_ERR "%s: Fixed I2C transfer failed\n", LOG_TAG);
        mutex_unlock(&dev->i2c_gpio.lock);
        return -EIO;
    }

    // 解锁
    mutex_unlock(&dev->i2c_gpio.lock);

    // 返回 0 表示已完成,避免重复读取
    return 0;
}
```

#### **驱动初始化和卸载**

驱动的初始化和卸载函数与方案一类似,只是不需要处理 `ioctl` 相关的逻辑。

---

## 总结

通过上述两种方案,您可以根据具体需求选择合适的实现方式:

1. **方案一**:适用于需要动态指定 I2C 参数的场景,用户空间应用程序可以灵活地读取不同寄存器的数据。

2. **方案二**:适用于仅需读取特定寄存器的场景,驱动内部已知需要读取的寄存器地址,无需用户传参。

无论选择哪种方案,务必确保 GPIO 的配置和时序符合 I2C 协议要求,并在实际硬件上进行充分测试。此外,注意处理好错误情况,确保驱动在各种情况下都能稳定运行。

---

## 进一步的建议

- **错误处理**:增强驱动的错误处理能力,例如在 I2C 传输失败时,尝试重新初始化 I2C 总线或发送复位信号。

- **多线程支持**:如果驱动可能会被多个进程同时访问,确保所有共享资源都受到适当的保护,如互斥锁。

- **性能优化**:基于 GPIO 的 I2C 模拟通常较为耗时,确保延时设置适当,避免不必要的延时影响系统性能。

- **扩展功能**:根据需要,您可以进一步扩展驱动的功能,例如支持写操作、批量读取等。

希望这些实现方案和详细注释能帮助您更好地理解和开发基于 GPIO 模拟 I2C 的驱动。如有进一步问题,欢迎继续讨论!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值