最新的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
4.1 Linux下交叉编译
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
4.2 Android下面交叉编译
(1). 修改源码
在Android下编译,有两个地方,需要修改size_t改成ssize_t, 第二个就是去掉program_invocation_name
这个是glibc提供的用法。android下面不改编译不过。
(2). Android NDK下面进行交叉编译
下载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.
(3). 放在Android源码中进行交叉编译
这个就需要书写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查看,就可以看到:
5.3 gpiofind使用
这个工具是通过上面所说的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