当前位置:首页 > 技术 > 正文内容

RTL8367 I2C GPIO 驱动用户空间交互实现

访客 技术 2026年6月11日 1

本文介绍如何在用户空间通过 ioctl 接口与 RTL8367 I2C GPIO 驱动进行通信,实现寄存器读写及端口速率查询功能。

核心数据结构定义

首先定义与内核驱动交互的数据结构,包括 I2C 传输参数和端口速率信息:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>

#define RTL8367_MAGIC        's'
#define CMD_I2C_RD           _IOWR(RTL8367_MAGIC, 0, struct i2c_msg)
#define CMD_I2C_WR           _IOWR(RTL8367_MAGIC, 1, struct i2c_msg)
#define CMD_PORT_RATE_GET    _IOWR(RTL8367_MAGIC, 2, struct port_info)

struct i2c_msg {
    int  bus;           /* I2C 总线索引 */
    int  dev_addr;      /* 从机地址(7位) */
    int  reg;           /* 寄存器偏移 */
    int  val;           /* 数据内容 */
};

struct port_info {
    int  port;          /* 物理端口号 */
    int  rate;          /* 协商速率 (Mbps) */
};

底层操作封装

ioctl 调用封装为可读性更高的函数,便于上层业务调用:

static inline void die(const char *ctx)
{
    perror(ctx);
    _exit(EXIT_FAILURE);
}

int reg_read(int fd, int bus, int sla, int reg_off)
{
    struct i2c_msg msg = {
        .bus  = bus,
        .dev_addr = sla,
        .reg  = reg_off,
        .val  = 0
    };

    if (ioctl(fd, CMD_I2C_RD, &msg) < 0)
        die("寄存器读取失败");

    printf("[读] 寄存器 0x%04X = 0x%04X\n", reg_off, msg.val);
    return msg.val;
}

void reg_write(int fd, int bus, int sla, int reg_off, int data)
{
    struct i2c_msg msg = {
        .bus  = bus,
        .dev_addr = sla,
        .reg  = reg_off,
        .val  = data
    };

    if (ioctl(fd, CMD_I2C_WR, &msg) < 0)
        die("寄存器写入失败");

    printf("[写] 寄存器 0x%04X <- 0x%04X\n", reg_off, data);
}

int link_speed_query(int fd, int port_no)
{
    struct port_info info = { .port = port_no, .rate = 0 };

    if (ioctl(fd, CMD_PORT_RATE_GET, &info) < 0)
        die("速率查询失败");

    printf("端口 %d 当前速率: %d Mbps\n", port_no, info.rate);
    return info.rate;
}

命令行交互实现

通过解析命令行参数,支持三种操作模式:读寄存器、写寄存器、查速率:

static void usage(const char *prog)
{
    fprintf(stderr,
        "用法: %s <设备节点> <命令> [参数...]\n\n"
        "命令:\n"
        "  rd  <总线> <从机地址> <寄存器>     读取寄存器\n"
        "  wr  <总线> <从机地址> <寄存器> <值> 写入寄存器\n"
        "  rate <端口号>                       查询端口速率\n",
        prog);
}

int main(int argc, char **argv)
{
    if (argc < 4) {
        usage(argv[0]);
        return EXIT_FAILURE;
    }

    int fd = open(argv[1], O_RDWR);
    if (fd < 0)
        die("打开设备失败");

    const char *op = argv[2];

    if (strcmp(op, "rd") == 0) {
        if (argc != 6) { usage(argv[0]); return EXIT_FAILURE; }
        int bus = atoi(argv[3]);
        int sla = (int)strtol(argv[4], NULL, 0);
        int reg = (int)strtol(argv[5], NULL, 0);
        reg_read(fd, bus, sla, reg);

    } else if (strcmp(op, "wr") == 0) {
        if (argc != 7) { usage(argv[0]); return EXIT_FAILURE; }
        int bus = atoi(argv[3]);
        int sla = (int)strtol(argv[4], NULL, 0);
        int reg = (int)strtol(argv[5], NULL, 0);
        int val = (int)strtol(argv[6], NULL, 0);
        reg_write(fd, bus, sla, reg, val);

    } else if (strcmp(op, "rate") == 0) {
        if (argc != 4) { usage(argv[0]); return EXIT_FAILURE; }
        int port = atoi(argv[3]);
        link_speed_query(fd, port);

    } else {
        fprintf(stderr, "未知命令: %s\n", op);
        usage(argv[0]);
        close(fd);
        return EXIT_FAILURE;
    }

    close(fd);
    return EXIT_SUCCESS;
}

编译与运行

使用 GCC 编译并赋予执行权限:

gcc -Wall -O2 -o rtl8367_ctl rtl8367_ctl.c

# 读取寄存器示例
./rtl8367_ctl /dev/rtl8367 rd 0 0x5c 0x1b00

# 写入寄存器示例
./rtl8367_ctl /dev/rtl8367 wr 0 0x5c 0x1b00 0x00ff

# 查询端口速率示例
./rtl8367_ctl /dev/rtl8367 rate 1

设计要点说明

  • Magic Number 选择:使用字符 's' 作为 ioctl 幻数,避免与系统其他驱动冲突
  • 地址解析:采用 strtol 自动识别十进制或十六进制输入(前缀 0x
  • 错误处理:统一封装错误输出,确保异常情况下的资源释放
  • 可扩展性:数据结构预留字段,便于后续支持多字节读写或批量操作
标签: RTL8367I2CGPIO

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。