首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

Linux的GPIO子系统(gpiolib)的原理详解【每个GPIO控制器对应于一个gpio_chip结构体】【如何查看每个GPIO控制器的详细情况】【如何利用其操作具体的GPIO口】

  • 25-03-05 03:28
  • 4338
  • 13516
blog.csdn.net

引言

GPIO是高频使用的硬件资源,所以Linux专门搞了一套GPIO子系统来对其进行管理,目的就是方便大家使用片上或板上的GPIO资源。

虽然之前已经利用GPIO子系统来实现过LED的驱动和按键的中断驱动了,但是对GPIO子系统的原理和结构还是比较模糊的,所以在这篇博文中仔细学习一下GPIO子系统。
之前用GPIO子系统来实现LED的驱动和按键的中断驱动的博文链接如下:
http://iyenn.com/rec/1709341.html
http://iyenn.com/rec/1709342.html

重要概念:对各个GPIO口的操作,是通过GPIO控制器实现的

对各个 GPIO 口(GPIO 引脚)的操作,是通过 GPIO 控制器(GPIO Controller)实现的。
在 Linux 内核中,GPIO 控制器由 GPIO 控制器驱动 管理,并通过 GPIO 子系统(gpiolib) 统一提供 API,使用户空间和其他内核驱动可以方便地访问 GPIO 口。

GPIO 控制器(GPIO Controller) 是一个管理多个 GPIO 引脚的硬件模块,通常集成在 SoC(如 IMX6ULL)中。它的主要作用是:

  • 通过 寄存器 控制 GPIO 口的 输入/输出 模式
  • 允许 使能/禁用 GPIO 口
  • 提供 中断控制(如果支持)
  • 管理 复用功能(Muxing)(部分引脚可能被复用为其他功能,如 I2C、UART、PWM)

在 Linux 中,GPIO 控制器通常会注册为一个 gpio_chip,并由 GPIO 子系统(gpiolib) 进行统一管理。对于 IMX6ULL,GPIO 控制器由 gpio-mxc 驱动管理。

GPIO子系统(gpiolib)的原理

问:gpiolib是不是就是我们所说的GPIO子系统?

是的,gpiolib 就是我们通常所说的 GPIO 子系统(GPIO Subsystem)!


1. 什么是 gpiolib?

gpiolib(GPIO Library)是 Linux 内核中的 GPIO 子系统,它提供了一套 统一的 GPIO 处理框架,用于管理不同 SoC 平台的 GPIO 控制器。

它的主要功能包括:

  • 管理 GPIO 控制器(gpiochip):支持多个 GPIO 控制器(比如 gpiochip0、gpiochip1)。
  • 提供 /sys/class/gpio/ 接口(Legacy sysfs,较老的方法)。
  • 支持 gpiod_*() API(更现代的 libgpiod 接口)。
  • 支持 GPIO 消息通知(如 gpio_request_irq() 处理中断)。
  • 抽象不同硬件的 GPIO 访问方式(比如基于 I2C、SPI、SoC 内部 GPIO 控制器)。

2. gpiolib 与 GPIO 子系统的关系

在 Linux 内核架构中:

  • GPIO 子系统 = gpiolib + 各个 GPIO 驱动
  • GPIO 控制器(gpiochip)是具体的硬件设备,而 gpiolib 负责提供管理框架。

可以简单理解为:

Linux 内核
└── GPIO 子系统(gpiolib)
    ├── GPIO 控制器驱动(如 `fsl-imx6ul-gpio`)
    ├── GPIO 用户接口(sysfs、libgpiod、device tree)
    ├── GPIO 虚拟总线(/sys/bus/gpio/)
  • 1
  • 2
  • 3
  • 4
  • 5

3.gpio_chip 结构体的介绍(核心数据结构)

gpio_chip 结构体是 Linux 内核 GPIO 子系统(gpiolib) 用来表示 GPIO 控制器(GPIO Controller) 的数据结构,每个 GPIO 控制器都必须有一个对应的 gpio_chip 结构体。可以说它是GPIO 子系统(gpiolib)的核心数据结构。

struct gpio_chip {
    const char *label;          // 控制器名称
    struct device *parent;      // 父设备
    int base;                   // GPIO 编号基地址(-1 代表动态分配)
    u16 ngpio;                  // 控制的 GPIO 数量
    struct module *owner;       // 所属驱动模块
    int (*direction_input)(struct gpio_chip *chip, unsigned offset);
    int (*direction_output)(struct gpio_chip *chip, unsigned offset, int value);
    int (*get)(struct gpio_chip *chip, unsigned offset);
    void (*set)(struct gpio_chip *chip, unsigned offset, int value);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

作用

  • 代表 一个 GPIO 控制器,它可以管理多个 GPIO 引脚。
  • 定义了 GPIO 操作函数(如 set()、get()、direction_output())。
  • 由 gpiolib 统一管理,并向用户空间提供 /sys/class/gpio/ 接口。

GPIO子系统的正常工作需要BSP的支持

GPIO子系统使得我们在屏蔽硬件底层操作的情况下,方便的实现对GPIO口的控制。由于这套系统存在,我们的系统启动后就能直接操作GPIO口。
底层的操作包含在BSP(Board Support Package,板级支持包) 中,所以gpio_chip 结构体通常由 BSP(Board Support Package,板级支持包) 提供。
所以GPIO子系统的实现是需要BSP支持的。

4. gpiolib 的工作方式

当 GPIO 控制器驱动(比如 fsl-imx6ul-gpio)加载时,它会:

  1. 注册 gpio_chip 结构体:

    • 将 gpio_chip 结构体通过 gpiochip_add_data() 或 devm_gpiochip_add_data() 向 gpiolib 注册 ,从而实现GPIO 控制器的注册。
    • gpio_chip 结构体注册完成后,gpiochip 设备(即GPIO控制器)会出现在 /sys/class/gpio/gpiochipX,同时也会出现在 /sys/bus/gpio/devices/。★★★
  2. 管理 GPIO 资源:

    • 允许用户空间访问 GPIO(通过用户接口sysfs 或 libgpiod实现)。
    • 提供 gpiod_get() / gpiod_direction_output() / gpiod_set_value() 等 API 供内核调用。
  3. 支持 GPIO 消息通知:

    • 处理 GPIO 输入/输出方向配置。
    • 允许 GPIO 触发中断(IRQ)。

5. 结论

✅ gpiolib 就是 GPIO 子系统,它提供了统一的 GPIO 处理框架。
✅ GPIO 控制器驱动(如 fsl-imx6ul-gpio)通过 gpiochip_add_data() 接口向 gpiolib 注册,成为 gpiochipX 设备。
✅ 用户可以通过 sysfs(较老方式)或 libgpiod(新方式)访问 GPIO,gpiolib 负责调度。

所以,GPIO 子系统 = gpiolib,它是 Linux 内核管理 GPIO 资源的核心机制! ?

片外的GPIO资源如何管理?

GPIO 子系统(gpiolib)通常用于管理片上(SoC 内部)的 GPIO 资源,而不是直接管理片外(外部扩展)的 GPIO 资源。


1. 片上 GPIO(On-Chip GPIO)

片上的 GPIO 指的是 SoC 自带的 GPIO 控制器所管理的 GPIO 引脚,例如:

  • i.MX6ULL 的 gpio1 ~ gpio5(如 209c000.gpio)。
  • 这些 GPIO 控制器通常由 SoC 内部的寄存器控制,并通过 gpiolib 统一管理。
  • 在设备树(DTS)中,片上 GPIO 资源通常是这样定义的:
    gpio1: gpio@209c000 {
        compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
        reg = <0x0209C000 0x4000>;
        gpio-controller;
        #gpio-cells = <2>;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

✅ 片上 GPIO 受 gpiolib 管理,出现在 /sys/class/gpio/ 和 /sys/bus/gpio/devices/。


2. 片外 GPIO(Off-Chip GPIO)

片外 GPIO 指的是 通过 I2C、SPI、PCI 或其他接口扩展的 GPIO 设备,例如:

  • I2C GPIO 扩展芯片(如 PCF8574、PCA9535)。
  • SPI GPIO 扩展芯片(如 MCP23S17)。
  • FPGA 或 CPLD 作为 GPIO 扩展。

这些 GPIO 并不是 SoC 自带的,而是通过 外部接口(I2C/SPI/PCI)连接,所以:

  1. 片外 GPIO 需要额外的驱动来管理。
  2. GPIO 子系统不会直接管理这些 GPIO 控制器,但可以通过驱动将它们注册为 gpio_chip,让 gpiolib 统一管理。

比如,I2C GPIO 扩展芯片 PCA9535 的设备树定义:

&i2c1 {
    gpio_expander@20 {
        compatible = "nxp,pca9535";
        reg = <0x20>;
        gpio-controller;
        #gpio-cells = <2>;
    };
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这样,PCA9535 的 16 个 GPIO 口就会在 GPIO 子系统 里注册为 gpiochipX,用户可以像操作片上 GPIO 那样来使用它们。

✅ 片外 GPIO 不是 SoC 自带的,但可以通过 gpio_chip 机制让 gpiolib 统一管理。


3. gpiolib 是否能管理片外 GPIO?

gpiolib 本身不直接管理片外 GPIO,但 如果片外 GPIO 控制器的驱动支持 gpiolib,那么它也可以被 GPIO 子系统管理。
? 这就是为什么 I2C/SPI GPIO 扩展芯片可以出现在 /sys/class/gpio/ 里!

✅ 如果片外 GPIO 的驱动实现了 gpio_chip 机制,它就能被 GPIO 子系统管理。
❌ 如果驱动没有实现 gpio_chip,那它就不会出现在 GPIO 子系统中,而是由其他子系统(I2C/SPI/PCI)管理。


4. 结论

✔ GPIO 子系统(gpiolib)主要管理 SoC 片上的 GPIO 资源。
✔ 片外 GPIO(I2C/SPI 扩展的 GPIO)默认不受 gpiolib 直接管理,但可以通过 gpio_chip 机制注册到 GPIO 子系统。
✔ 如果片外 GPIO 驱动支持 gpio_chip,它就能像片上 GPIO 一样被操作。

如何查看GPIO子系统里有哪些GPIO控制器,每个GPIO控制器的详细信息怎么查看

在上面对“gpiolib的工作方式”的叙述中,我们知道当 GPIO 控制器的驱动加载时,它会利用函数 gpiochip_add_data() 或 devm_gpiochip_add_data() 向 gpiolib 向 gpiolib 注册gpio_chip 结构体,注册完成后,,gpiochip 设备(即GPIO控制器)会出现在 /sys/class/gpio/gpiochipX,同时也会出现在 /sys/bus/gpio/devices/。

所以我们查看目录/sys/bus/gpio/devices/或目录/sys/class/gpio/就可以知道系统中有哪些GPIO控制器。

在我的开发板上运行下面的命令:

ls /sys/bus/gpio/devices/
  • 1

结果如下:
在这里插入图片描述
从上面的运行结果中可以看出,一共有6个GPIO控制器的gpiochip结构体,IMX6ULL本来只有5组GPIO口,即5个GPIO控制器,那么为什么会有6个GPIO控制器呢?请往后面看,后面有答案。

再运行下面的命令:

ls /sys/class/gpio/
  • 1

在这里插入图片描述
从上面的运行结果中可以看出,系统中有6个与GPIO相关的设备类,每个名字后面的数字代表其对应的GPIO口的超始编号。IMX6ULL本来只有5组GPIO口,那么为什么会有6个GPIO的设备类?请往后面看,后面有答案。

下面来回答上面两条命令遗留的问题。

可以用下面命令来查看每个gpio_chip 结构体的详细情况:

cat /sys/kernel/debug/gpio
  • 1

运行结果如下:
在这里插入图片描述

gpiochip0: GPIOs 0-31, parent: platform/209c000.gpio, 209c000.gpio:
 gpio-5   (                    |goodix_ts_int       ) in  hi IRQ
 gpio-19  (                    |cd                  ) in  hi IRQ
 gpio-20  (                    |spi_imx             ) out hi    

gpiochip1: GPIOs 32-63, parent: platform/20a0000.gpio, 20a0000.gpio:

gpiochip2: GPIOs 64-95, parent: platform/20a4000.gpio, 20a4000.gpio:
 gpio-68  (                    |lcdif_rst           ) out hi    

gpiochip3: GPIOs 96-127, parent: platform/20a8000.gpio, 20a8000.gpio:
 gpio-120 (                    |spi_imx             ) in  lo    
 gpio-122 (                    |spi_imx             ) in  lo    

gpiochip4: GPIOs 128-159, parent: platform/20ac000.gpio, 20ac000.gpio:
 gpio-130 (                    |goodix_ts_rst       ) out hi    
 gpio-134 (                    |phy-reset           ) out hi    
 gpio-135 (                    |spi32766.0          ) out hi    
 gpio-136 (                    |?                   ) out lo    
 gpio-137 (                    |phy-reset           ) out hi    
 gpio-138 (                    |spi4                ) out hi    
 gpio-139 (                    |spi4                ) out lo    

gpiochip5: GPIOs 504-511, parent: spi/spi32766.0, 74hc595, can sleep:
 gpio-505 (                    |?                   ) out hi  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

因为一个gpiochip结构体代表一个GPIO控制器,所以从上面的运行结果就可以看到每个GPIO控制器的详情情况了。

我们可以看到,之所以系统中出现了6个GPIO控制器,是因为板上通过SPI总线连接74hc595形成了扩展GPIO(SPI总线是一种串行总线,而74hc595能将串行数据转换为并行输出),即SPI总线和74hc595共同构成了一个含GPIO控制器功能和GPIO功能的结构。这个扩展的GPIO被识别成了gpiochip5结构体,所以多了一个GPIO控制器。
从这里也可以看出,片外的GPIO资源的确如前面所述,也是可以通过GPIO子系统(gpiolib)来管理的。

另外,从运行结果我们可以看出,实际上片内的GPIO的驱动是通过platform来实现的,相关的证据在下图中用红线勾画出来了:
在这里插入图片描述
从运行结果我们还可以得出下面的信息:

gpiochip0对应于IMX6ULL的GPIO1,并把GPIO1的各GPIO口分别编号为0~31【一共32个GPIO口】,其寄存器基地址为209c000;
gpiochip1对应于IMX6ULL的GPIO2,并把GPIO2的各GPIO口分别编号为32~63【一共32个GPIO口】,其寄存器基地址为20a0000;
gpiochip2对应于IMX6ULL的GPIO3,并把GPIO3的各GPIO口分别编号为64~95【一共32个GPIO口】,其寄存器基地址为20a4000;
gpiochip3对应于IMX6ULL的GPIO4,并把GPIO4的各GPIO口分别编号为96~127【一共32个GPIO口】,其寄存器基地址为20a8000;
gpiochip4对应于IMX6ULL的GPIO5,并把GPIO5的各GPIO口分别编号为128~159【一共32个GPIO口】,其寄存器基地址为20ac000;

有了上面的信息,我们就可以计算出某个具体的GPIO口的编号。当然在博文 http://iyenn.com/rec/1709342.html 中我们可以用函数of_get_gpio_flags()获取到某个GPIO口的编号。

我们可以看芯片手册,发现这些基地址是对的,如下图所示:
在这里插入图片描述

终端中如何利用GPIO子系统(gpiolib)操作GPIO口

现在我要在终端中对GPIO5_IO03进行操作,通过它控制LED2:
在这里插入图片描述

根据上面的得到的信息:

gpiochip0对应于IMX6ULL的GPIO1,并把GPIO1的各GPIO口分别编号为0~31【一共32个GPIO口】,其寄存器基地址为209c000;
gpiochip1对应于IMX6ULL的GPIO2,并把GPIO2的各GPIO口分别编号为32~63【一共32个GPIO口】,其寄存器基地址为20a0000;
gpiochip2对应于IMX6ULL的GPIO3,并把GPIO3的各GPIO口分别编号为64~95【一共32个GPIO口】,其寄存器基地址为20a4000;
gpiochip3对应于IMX6ULL的GPIO4,并把GPIO4的各GPIO口分别编号为96~127【一共32个GPIO口】,其寄存器基地址为20a8000;
gpiochip4对应于IMX6ULL的GPIO5,并把GPIO5的各GPIO口分别编号为128~159【一共32个GPIO口】,其寄存器基地址为20ac000;

可以计算出GPIO5_IO03的编号为128+3=131 【注意:具体的GPIO口是从0开始编号的,但是GPIO组的组号是从1开始编号的。】

有了 GPIO的编号就可以用下面的方法对其进行操作了:

方式 1:利用sysfs伪文件系统(旧方法)

使用下面的命令:

echo 131 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio131/direction
echo 1 > /sys/class/gpio/gpio131/value
  • 1
  • 2
  • 3

实际上是通过 sysfs伪文件系统 /sys/class/gpio/ 间接操作 GPIO 控制器,让它设置 GPIO131 的方向和输出值。
这三条命令的详细解释见 http://iyenn.com/rec/1709312.html 【搜索“一个实际的例子分析”】
上面的三条命令运行完后,在下面的原理图基础上,灯灭了。
在这里插入图片描述
再运行下面的命令,灯又亮了:

echo 0 > /sys/class/gpio/gpio131/value
  • 1

但这种方式在 Linux 5.10 及以上版本中已被废弃,官方推荐使用 libgpiod。

方式 2:libgpiod(gpiod API)(新方法,推荐)

# 设置 GPIO131 为高电平
gpioset gpiochip0 131=1
  • 1
  • 2

这种方法直接调用 GPIO 控制器驱动提供的 API,比 sysfs 更高效。虽然资料显示,libgpiod(gpiod API)在Linux_4.8被引入,在4.11中被稳定,而我当前用的版本为4.9,有可能有,有可能没有,我实测了下,没有…
在这里插入图片描述

程序中如何如何利用GPIO子系统(gpiolib)操作GPIO口

详见 http://iyenn.com/rec/1709286.html

问题探讨:为什么没有相关的设备文件生成?

在看下面的解释前可以先看下博文 http://iyenn.com/rec/1709312.html 的第1个目录中我对“sysfs伪文件”和“设备文件”的区分【搜索“sysfs伪文件系统实际上和设备文件很类似”】。由于GPIO子系统(gpiolib)并不使用驱动程序的那套东西,所以没有设备文件的生成,但也可以通过sysfs伪文件去控制GPIO子系统中的那些GPIO口。

GPIO 子系统并不会为每个 GPIO 控制器创建设备文件。

在 Linux 内核中,GPIO 控制器被抽象为 gpio_chip 结构,并由 gpiolib 统一管理,但它 不会作为字符设备(/dev/ 设备文件)直接暴露给用户空间。


1. GPIO 控制器不会创建 /dev/gpioX 这样的设备文件

与 I2C、SPI、UART 这些子系统不同,GPIO 控制器:

  • 不会 通过 /dev/ 创建设备文件(如 /dev/gpioX)。
  • 其控制方式 主要通过 sysfs、ioctl 或 libgpiod 进行。
  • 在 /sys/class/gpio/ 仅提供了一个接口,而不是传统的设备节点。

为什么 GPIO 控制器不创建 /dev/ 设备文件?

  1. GPIO 本质上是单个引脚,而不是一个完整的字符/块设备

    • I2C、SPI、UART 是面向数据流的接口,需要 /dev/ 设备文件进行读写操作。
    • 而 GPIO 只是 高低电平的开关,没有连续的数据流,因此通常不使用 /dev/ 设备文件。
  2. GPIO 控制器被 gpiolib 统一管理

    • Linux 采用 gpio_chip 结构来管理 GPIO 控制器,每个 gpio_chip 负责多个 GPIO 引脚。
    • 用户访问 GPIO 时,通常通过 /sys/class/gpio/ 或 libgpiod 访问,而不是通过设备文件。

2. 那么 GPIO 控制器在系统中表现为什么?

虽然 没有 /dev/gpioX 这样的设备文件,但 GPIO 控制器仍然在 /sys 下可见,主要体现在以下几个地方:

(1) /sys/class/gpio/(用户空间控制 GPIO)

ls /sys/class/gpio/
  • 1

可能会看到:

export  gpiochip0  gpiochip32  gpiochip64  gpiochip96  unexport
  • 1
  • gpiochipX 代表不同的 GPIO 控制器,每个控制器管理多个 GPIO 口。
  • export / unexport 用于申请或释放 GPIO 口。

查看某个 GPIO 控制器的信息:

cat /sys/class/gpio/gpiochip0/label
  • 1

可能输出:

gpio-mxc
  • 1

表示这个 GPIO 控制器由 gpio-mxc 驱动管理。

(2) /sys/kernel/debug/gpio(调试 GPIO 口状态)

如果内核开启了 CONFIG_GPIO_SYSFS 选项,可以通过以下命令查看所有 GPIO 口的状态:

cat /sys/kernel/debug/gpio
  • 1

示例输出:

gpiochip0: GPIOs 0-31, parent: platform/30200000.gpio, gpio-mxc:
 gpio-10  (sysfs               ) out lo  
 gpio-20  (sysfs               ) in  hi  
  • 1
  • 2
  • 3

这表示:

  • gpiochip0 管理 GPIO0~GPIO31
  • GPIO10 通过 sysfs 导出,方向是 输出,当前电平是 低
  • GPIO20 通过 sysfs 导出,方向是 输入,当前电平是 高

(3) /sys/bus/gpio/(GPIO 设备管理)

ls /sys/bus/gpio/
  • 1

可能看到:

devices  drivers  drivers_autoprobe  drivers_probe  uevent
  • 1

但 /sys/bus/gpio/drivers/ 目录通常为空,因为 大部分 GPIO 控制器不会在 gpio 总线下注册驱动,而是作为 platform device 存在。


3. 其他子系统的对比

子系统设备文件 (/dev/)主要管理方式
GPIO❌ 无sysfs(旧)、libgpiod(推荐)
I2C✅ /dev/i2c-Xi2c-dev 通过 ioctl 访问
SPI✅ /dev/spidevX.Xspidev 进行数据传输
UART✅ /dev/ttySx通过 read/write/ioctl 访问

可以看出,GPIO 不像 I2C、SPI、UART 那样需要 /dev/ 设备文件,而是通过 sysfs 或 libgpiod 进行管理。


4. 结论

✅ GPIO 子系统不会创建 /dev/gpioX 这样的设备文件,因为 GPIO 只是开关信号,不是数据流设备。
✅ GPIO 控制器在 /sys/class/gpio/ 中可见,但它们作为 gpio_chip 由 gpiolib 统一管理,而不是独立的设备文件。
✅ 推荐使用 libgpiod 而不是 sysfs 进行 GPIO 操作,因为 sysfs 方式在Linux 5.10中已被废弃(不过我使用的4.9版本里还没有引入,是4.11才正式引入的。)。

昊虹嵌入式技术交流群
QQ群名片
注:本文转载自blog.csdn.net的昊虹AI笔记的文章"https://blog.csdn.net/wenhao_ir/article/details/145444452"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top