最新的kernel中的gpio的使用方法
最近在从4.x的内核移到5.4内核上的时候发现,原来的GPIO进行request所使用的GPIOF_EXPORT突然不能用了。上网上找发现,/sys/classs/gpio已经被最新的内核废弃了。
https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
查阅相关的资料发现,这个改动是从2015年开始逐渐发从linux kernel 4.6到4.8 引入的。到了2020年,正式废弃。提交的代码记录如下:
kernel/git/torvalds/linux.git - Linux kernel source tree
但是虽然/sys/classs/gpio已经不能用了,但是还是可以通过打开CONFIG_GPIO_SYSFS,再加回来,

由于这个模块相对独立,当前使用还不会造成任何问题。但是不能确定主线内核到时候,会不会把这个也移除。
但是打开这个CONFIG_GPIO_SYSFS,也不能像以前一样看到GPIO的设备节点。
2. 当前gpio在kernel的架构
当前的GPIO在内核中,会使能CONFIG_GPIOLIB, 会以一个chardev的方式出现. 这样对于所有注册的GPIO可以通过通用的通用的gpio访问的库libgpiod来对GPIO进行操作,操作的方法是通过IOCTL的接口来进行。而这个设备会在/dev下面,如:

- /dev/gpiochip0
- /dev/gpiochip1
- /dev/gpiochip2
3. 调试调用方法
想要调用,可以通过libgpiod来进行。下载地址如下:
libgpiod/libgpiod.git - C library and tools for interacting with the linux GPIO character device
这个工具提供了调用库文件libgpiod,还提供了调试的工具,不过想要用到android上面,还需要进行交叉编译。
下载:
https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/snapshot/libgpiod-1.6.3.tar.gz
4. 交叉编译方法
编译前,ubuntu16.04下,需要安装libtool, autoconf
sudo apt install -y libtool autoconf
arm64的编译器可以使用linaro的,比如:gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu, 和在如下目录:
/opt/arm-gcc/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc
先配置环境变量:
export PATH=$PATH:/opt/arm-gcc/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/
然后通过如下命令配置后,生成编译Makefile:
./autogen.sh --enable-tools=yes --host=armv8 CC=aarch64-linux-gnu-gcc --enable-static --prefix=/work/kernel/out
然后:
- make
- make install
在Android下编译,有两个地方,需要修改size_t改成ssize_t, 第二个就是去掉program_invocation_name
这个是glibc提供的用法。android下面不改编译不过。
下载android 的最新的NDK
NDK 下载 | Android NDK | Android Developers
在这里下载linux版本最新的NKD就可以。
https://dl.google.com/android/repository/android-ndk-r23c-linux.zip
解压后,打开终端,输入下面命令,加入到终端环境变量:
export PATH=$PATH:/work/Android/android-ndk-r23c/toolchains/llvm/prebuilt/linux-x86_64/bin
然后使用编译器:aarch64-linux-android24-clang 进行编译,进行配置如下:
./autogen.sh --enable-tools=yes --host=armv8 CC=aarch64-linux-android24-clang --with-sysroot=/work/Android/android-ndk-r23b/toolchains/llvm/prebuilt/linux-x86_64/sysroot --prefix=/work/kernel/out
然后输入:
make all install
编译完成后,会生成:
- gpiodetect
- gpiofind
- gpioget
- gpioinfo
- gpiomon
- gpioset
这个有一点类似于i2ctools.
这个就需要书写Android.mk, 把tools, lib,及include下面的文件都入在一起。然后,lib的部分,可以编译成
一个静太库,或动态库。如果是动态库需要 push到android目录下面:
如果需要编成动态静态库,libgpiod的Android.mk如下:
- LOCAL_PATH:= $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES:= \
- core.c \
- ctxless.c \
- misc.c \
- helpers.c \
- iter.c
-
- LOCAL_MODULE_TAGS := optional
- LOCAL_MODULE := libgpiod
- LOCAL_VENDOR_MODULE := true
- include $(BUILD_SHARED_LIBRARY)
编译gpiod的工具的Android.mk如下:
- LOCAL_PATH:= $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES:= \
- gpiodetect.c \
- tools-common.c
-
- LOCAL_MODULE_TAGS := optional
- LOCAL_MODULE := gpiodetect
- LOCAL_SHARED_LIBRARIES += libgpiod
- LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_EXECUTABLES)
- include $(BUILD_EXECUTABLE)
如果需要编成静态库,libgpiod的Android.mk如下:
- LOCAL_PATH:= $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES:= \
- core.c \
- ctxless.c \
- misc.c \
- helpers.c \
- iter.c
-
- LOCAL_MODULE_TAGS := optional
- LOCAL_MODULE := libgpiod
- LOCAL_VENDOR_MODULE := true
- include $(BUILD_STATIC_LIBRARY)
编译gpiod的工具的Android.mk如下:
- LOCAL_PATH:= $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES:= \
- gpiodetect.c \
- tools-common.c
-
- LOCAL_MODULE_TAGS := optional
- LOCAL_MODULE := gpiodetect
- LOCAL_STATIC_LIBRARIES += libgpiod
- LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_EXECUTABLES)
- include $(BUILD_EXECUTABLE)
然后进入目前,使用mm编译就可以了。gpio tools有六个,除了gpiodetect,还有gpioget, gpiofind, gpioinfo, gpiomon, gpioset。也需要按gpiodetect一样增加mk的内容。编译成完会生成在/vendor/bin下面。
5. gpiotool 在android下面的使用
编译完成后,push进android系统的/vendor/bin下面。
5.1 gpiodetect使用
- msmnile_au:/ # gpiodetect
- gpiochip0 [3100000.pinctrl] (176 lines)
- gpiochip1 [c440000.qcom,spmi:qcom,pm8150@0] (10 lines)
- gpiochip2 [c440000.qcom,spmi:qcom,pm8150@4] (10 lines)
直接输入,就可以,可以列出当前的有几个gpio控制器,分别有几个GPIO.
5.2 gpioinfo使用
输入gpioinfo,可以看到所有的gpio的使用情况:
- msmnile_au:/ # gpioinfo
- gpiochip0 - 176 lines:
- line 0: unnamed unused input active-high
- line 1: unnamed unused input active-high
- line 2: unnamed unused input active-high
- line 3: unnamed unused input active-high
- line 4: unnamed unused input active-high
- line 5: unnamed unused input active-high
- line 6: unnamed unused input active-high
- line 7: unnamed unused input active-high
- line 8: unnamed unused input active-high
- line 9: unnamed unused input active-high
- line 10: unnamed unused input active-high
- line 11: unnamed "ssVreset-gpio" output active-high [used]
- line 12: unnamed unused input active-high
- line 13: unnamed "interrupt" input active-high [used]
- line 14: unnamed unused input active-high
- line 15: unnamed unused input active-high
- line 16: unnamed unused input active-high
- line 17: unnamed "CCI_I2C_DATA0" input active-high [used]
- line 18: unnamed "CCI_I2C_CLK0" input active-high [used]
- line 19: unnamed "CCI_I2C_DATA1" input active-high [used]
- line 20: unnamed "CCI_I2C_CLK1" input active-high [used]
- line 21: unnamed "CAM_RESET0" output active-high [used]
- line 22: unnamed unused output active-high
- line 23: unnamed unused output active-high
第一列是gpio的编号,第二行,是一个叫line-name的东西,可以在dts里配置的时候通过gpio-line-names来进行指定,如:
- &gpio1 {
- gpio-line-names = "red_led", "blue_led", "green_led";
- };
这样通过gpioinfo查看的时候,会出现:
- $ gpioinfo
- gpiochip0 - 32 lines:
- line 0: "red_led" unused input active-high
- line 1: "blue_led" unused input active-high
- line 2: "green_led" unused input active-high
- line 3: unnamed "interrupt" input active-high [used]
- line 4: unnamed unused output active-high
- line 5: unnamed "status" output active-high [used]
- line 6: unnamed unused input active-high
- line 7: unnamed "reset" input active-high [used]
- line 8: unnamed unused input active-high
- ...
不带命令是查看所有的,还可以指定控制器:
gpioinfo gpiochip0
或通过设备名:
gpioinfo 3100000.pinctrl
在高通平台上:
- &tlmm {
- gpio-line-names =
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "line12",
- "",
- "line14",
- "",
- "";
- };
再通过gpioinfo查看,就可以看到:
这个工具是通过上面所说的gpio-line-names, 如果没有指定gpio-line-names的话,是无法查找的。如上面提到的例子:
- gpiofind blue_led
- gpiochip0 1
- gpioset gpiochip0 1=1
如高通平台:
5.4 gpioget使用
用法如下:
- msmnile_au:/ # gpioget gpiochip0 78
- 0
5.5. gpioset使用
- gpioset --mode=wait gpiochip0 12=0
-
- gpioset gpiochip0 1=1
5.6 . gpiomon使用
gpiomon --silent --num-events=1 gpiochip0 24 25 31