检查 io 是否被占用。
adb root
, adb shell
cd /sys/class/gpio
进入 gpio 目录
echo 46 > export
获取 gpio_46
- 检查上条命令是否提示
/system/bin/sh: can't create export: Read-only file system
,如果提示这个,那么说明这个 gpio 被占用了,否则这个 gpio 就是空的。
echo 46 > unexport
释放 gpio_46
驱动代码 (quectel)
不管输入还是输出 gpio,直接仿照这个源文件里面进行添加即可。
比如说这个源码里面提供了四个 gpio,key_home
, red_led_en
, green_led_en
, yellow_led_en
。
SC200R_Android10.0_R04_r023/kernel/msm-4.9/drivers/misc/gpio_control.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/workqueue.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#if defined(CONFIG_FB)
#include <linux/notifier.h>
#include <linux/fb.h>
#endif
#include <linux/timer.h>
#include <linux/string.h>
struct quectel_platform_data {
int key_home;
int red_led_en;
int green_led_en;
int yellow_led_en;
};
struct quectel_platform_data *quec_pdata;
static ssize_t quec_key_home_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, 2, "%d\n", __gpio_get_value(quec_pdata->key_home));
}
static ssize_t quec_red_led_en_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, 2, "%d\n", __gpio_get_value(quec_pdata->red_led_en));
}
static ssize_t quec_green_led_en_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, 2, "%d\n", __gpio_get_value(quec_pdata->green_led_en));
}
static ssize_t quec_yellow_led_en_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, 2, "%d\n", __gpio_get_value(quec_pdata->yellow_led_en));
}
static ssize_t quec_red_led_en_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
unsigned int val;
if (size > 2)
return -EINVAL;
if (sscanf(buf, "%u", &val) != 1)
return -EINVAL;
if(val)
__gpio_set_value(quec_pdata->red_led_en,1);
else
__gpio_set_value(quec_pdata->red_led_en,0);
return size;
}
static ssize_t quec_green_led_en_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
unsigned int val;
if (size > 2)
return -EINVAL;
if (sscanf(buf, "%u", &val) != 1)
return -EINVAL;
if(val)
__gpio_set_value(quec_pdata->green_led_en,1);
else
__gpio_set_value(quec_pdata->green_led_en,0);
return size;
}
static ssize_t quec_yellow_led_en_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
unsigned int val;
if (size > 2)
return -EINVAL;
if (sscanf(buf, "%u", &val) != 1)
return -EINVAL;
if(val)
__gpio_set_value(quec_pdata->yellow_led_en,1);
else
__gpio_set_value(quec_pdata->yellow_led_en,0);
return size;
}
static DEVICE_ATTR(key_home, (S_IRUGO | S_IWUSR | S_IWGRP),
quec_key_home_show,
NULL);
static DEVICE_ATTR(red_led_en, (S_IRUGO | S_IWUSR | S_IWGRP),
quec_red_led_en_show,
quec_red_led_en_store);
static DEVICE_ATTR(green_led_en, (S_IRUGO | S_IWUSR | S_IWGRP),
quec_green_led_en_show,
quec_green_led_en_store);
static DEVICE_ATTR(yellow_led_en, (S_IRUGO | S_IWUSR | S_IWGRP),
quec_yellow_led_en_show,
quec_yellow_led_en_store);
static struct attribute *quec_attrs[] = {
&dev_attr_key_home.attr,
&dev_attr_red_led_en.attr,
&dev_attr_green_led_en.attr,
&dev_attr_yellow_led_en.attr,
NULL
};
static const struct attribute_group quec_attr_grp = {
.attrs = quec_attrs,
};
static int quectel_misc_parse_dt(struct device *dev,
struct quectel_platform_data *pdata)
{
struct device_node *np = dev->of_node;
pdata->key_home = of_get_named_gpio(np, "quec,key_home", 0);
if (pdata->key_home < 0){
pr_err("%s: get quec,key_home failed\n", __func__);
return pdata->key_home;
}
pdata->red_led_en = of_get_named_gpio(np, "quec,red_led_en", 0);
if (pdata->red_led_en < 0){
pr_err("%s: get quec,red_led_en failed\n", __func__);
return pdata->red_led_en;
}
pdata->green_led_en = of_get_named_gpio(np, "quec,green_led_en", 0);
if (pdata->green_led_en < 0){
pr_err("%s: get quec,green_led_en failed\n", __func__);
return pdata->green_led_en;
}
pdata->yellow_led_en = of_get_named_gpio(np, "quec,yellow_led_en", 0);
if (pdata->yellow_led_en < 0){
pr_err("%s: get quec,yellow_led_en failed\n", __func__);
return pdata->yellow_led_en;
}
return 0;
}
static int quectel_request_io_port(struct quectel_platform_data *pdata)
{
int ret = 0;
if (gpio_is_valid(pdata->key_home)) {
ret = gpio_request(pdata->key_home, "quectel_key_home");
if (ret) {
pr_err("%s: Unable to request key_home [%d]\n", __func__,pdata->key_home);
goto err_pwr_off;
}
ret = gpio_direction_output(pdata->key_home, 0);
if (ret) {
pr_err("%s: Unable to set direction for key_home [%d]\n", __func__,pdata->key_home);
goto err_free_key_home;
}
} else {
pr_err("%s: Invalid key_home [%d]!\n", __func__,pdata->key_home);
ret = -EINVAL;
goto err_free_key_home;
}
if (gpio_is_valid(pdata->red_led_en)) {
ret = gpio_request(pdata->red_led_en, "quectel_red_led_en");
if (ret) {
pr_err("%s: Unable to request red_led_en [%d]\n", __func__,pdata->red_led_en);
goto err_pwr_off;
}
ret = gpio_direction_output(pdata->red_led_en, 0);
if (ret) {
pr_err("%s: Unable to set direction for red_led_en [%d]\n", __func__,pdata->red_led_en);
goto err_free_red_led_en;
}
} else {
pr_err("%s: Invalid red_led_en [%d]!\n", __func__,pdata->red_led_en);
ret = -EINVAL;
goto err_free_red_led_en;
}
if (gpio_is_valid(pdata->green_led_en)) {
ret = gpio_request(pdata->green_led_en, "quectel_green_led_en");
if (ret) {
pr_err("%s: Unable to request green_led_en [%d]\n", __func__,pdata->green_led_en);
goto err_pwr_off;
}
ret = gpio_direction_output(pdata->green_led_en, 0);
if (ret) {
pr_err("%s: Unable to set direction for green_led_en [%d]\n", __func__,pdata->green_led_en);
goto err_free_green_led_en;
}
} else {
pr_err("%s: Invalid green_led_en [%d]!\n", __func__,pdata->green_led_en);
ret = -EINVAL;
goto err_free_green_led_en;
}
if (gpio_is_valid(pdata->yellow_led_en)) {
ret = gpio_request(pdata->yellow_led_en, "quectel_yellow_led_en");
if (ret) {
pr_err("%s: Unable to request yellow_led_en [%d]\n", __func__,pdata->yellow_led_en);
goto err_pwr_off;
}
ret = gpio_direction_output(pdata->yellow_led_en, 0);
if (ret) {
pr_err("%s: Unable to set direction for yellow_led_en [%d]\n", __func__,pdata->yellow_led_en);
goto err_free_yellow_led_en;
}
} else {
pr_err("%s: Invalid yellow_led_en [%d]!\n", __func__,pdata->yellow_led_en);
ret = -EINVAL;
goto err_free_yellow_led_en;
}
return 0;
err_free_key_home:
if (gpio_is_valid(pdata->key_home))
gpio_free(pdata->key_home);
err_free_red_led_en:
if (gpio_is_valid(pdata->red_led_en))
gpio_free(pdata->red_led_en);
err_free_green_led_en:
if (gpio_is_valid(pdata->green_led_en))
gpio_free(pdata->green_led_en);
err_free_yellow_led_en:
if (gpio_is_valid(pdata->yellow_led_en))
gpio_free(pdata->yellow_led_en);
err_pwr_off:
return ret;
}
static void quec_gpio_free(void)
{
gpio_free(quec_pdata->key_home);
gpio_free(quec_pdata->red_led_en);
gpio_free(quec_pdata->green_led_en);
gpio_free(quec_pdata->yellow_led_en);
}
static int quec_gpio_probe(struct platform_device *pdev)
{
int ret;
struct quectel_platform_data *pdata ;
pr_err("-----------quectel_probe start--------------\n");
if (pdev->dev.of_node) {
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct quectel_platform_data), GFP_KERNEL);
if (!pdata) {
dev_err(&pdev->dev,
"quec allocate memory for pdata\n");
return -ENOMEM;
}
ret = quectel_misc_parse_dt(&pdev->dev, pdata);
if (ret) {
dev_err(&pdev->dev, "parse dts failed \n");
return PTR_ERR(pdata);
}
} else {
pdata = pdev->dev.platform_data;
}
quec_pdata = pdata;
if(!pdata) {
dev_err(&pdev->dev, "quec Invalid pdata\n");
return -EINVAL;
}
ret = quectel_request_io_port(pdata);
if(ret){
dev_err(&pdev->dev, "request io failed\n");
return ret;
}
ret = sysfs_create_group(&pdev->dev.kobj, &quec_attr_grp);
if (ret < 0) {
dev_err(&pdev->dev, "sys file creation failed.\n");
return -ENOMEM;
}
pr_err("-----------quectel_probe success--------------\n");
return 0;
}
static int quec_gpio_remove(struct platform_device *pdev)
{
struct quectel_platform_data *pdata = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
sysfs_remove_group(&pdev->dev.kobj, &quec_attr_grp);
quec_gpio_free();
kfree(pdata);
return 0;
}
static const struct of_device_id quec_gpio_match[] = {
{ .compatible = "quec,gpio_driver" },
{},
};
static struct platform_driver quec_gpio_driver = {
.driver = {
.name = "quec_gpio",
.owner = THIS_MODULE,
.of_match_table = quec_gpio_match,
},
.probe = quec_gpio_probe,
.remove = quec_gpio_remove,
};
module_platform_driver(quec_gpio_driver);
MODULE_DESCRIPTION("misc driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0");
makefile
SC200R_Android10.0_R04_r023/kernel/msm-4.9/drivers/misc/Makefile
里面增加 obj-y += gpio_control.o
dtsi
SC200R_Android10.0_R04_r023/kernel/msm-4.9/arch/arm64/boot/dts/qcom/qm215-qrd.dtsi
&soc {
gpio_ctrl {
compatible = "quec,gpio_driver";
quec,key_home = <&tlmm 95 0x00>;
quec,red_led_en = <&tlmm 93 0x00>;
quec,green_led_en = <&tlmm 46 0x00>;
quec,yellow_led_en = <&tlmm 23 0x00>;
};
};
解除被占用 gpio
SC200R_Android10.0_R04_r023/kernel/msm-4.9/arch/arm64/boot/dts/qcom/msm8917-pinctrl.dtsi
中把被占用的 gpio 去除掉即可。
编译,烧录
如果修改的时驱动源码,那么编译 boot.img
,如果修改的是 dts,那么编译 dtbo.img
。
然后用 fastboot,把 boot.img
, dtbo.img
都烧录进去。
读取 gpio
查找 dts 中对应的节点 key_home
, find . -name "*home*"
, 结果
./devices/platform/soc/soc:input-gpio/key_home
./firmware/devicetree/base/soc/input-gpio/quec,key_home
./module/home_gpio
cd ./devices/platform/soc/soc:input-gpio/
cat key_home
就可以读取到输入的高低电平
输出 gpio
find . -name "*led_en*"
./devices/platform/soc/soc:gpio_ctrl/green_led_en
./devices/platform/soc/soc:gpio_ctrl/red_led_en
./devices/platform/soc/soc:gpio_ctrl/yellow_led_en
./firmware/devicetree/base/__symbols__/front_flash_led_enable
./firmware/devicetree/base/__symbols__/rear_flash_led_enable
./firmware/devicetree/base/soc/gpio_ctrl/quec,green_led_en
./firmware/devicetree/base/soc/gpio_ctrl/quec,yellow_led_en
./firmware/devicetree/base/soc/gpio_ctrl/quec,red_led_en
./firmware/devicetree/base/soc/pinctrl@1000000/tlmm_pmi_flash_led/front_flash_led_enable
./firmware/devicetree/base/soc/pinctrl@1000000/tlmm_pmi_flash_led/rear_flash_led_enable
cd devices/platform/soc/soc\:gpio_ctrl/
echo 1 > green_led_en
echo 0 > green_led_en
就可以设置输出 gpio 的高低电平。
另外一种方法实现针对 led 的 gpio 输出。 (fibcom)
内核本身就有 led 的驱动代码,在 kernel/msm-4.9/drivers/leds/leds-gpio.c
中,所以如果是 led 的用途 gpio,可以直接修改 dtsi 即可。
kernel/msm-4.9/arch/arm64/boot/dts/qcom/sq808-evk/msm8917-pinctrl.dtsi
gpio_led_active: gpio_led_active {
mux {
pins = "gpio98", "gpio97", "gpio12";
drive-strength = <2>;
output-low;
};
};
kernel/msm-4.9/arch/arm64/boot/dts/qcom/sq808-evk/qm215-qrd.dtsi
gpio-leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&gpio_led_active>;
led-red {
label = "red";
default-state = "keep";
gpios = <&tlmm 12 0x00>;
};
led-blue{
label = "blue";
default-state = "off";
gpios = <&tlmm 97 0x00>;
};
led-green{
label = "green";
default-state = "off";
gpios = <&tlmm 98 0x00>;
};
};
kernel/msm-4.9/arch/arm64/configs/sq80x_defconfig
CONFIG_LEDS_GPIO=y
串口
除了 adb 还可以使用串口,但是注意,只有屏幕点亮的情况下,串口命令才能正常工作。
一般先要用 su
提示为 root
用户,然后再使用各种命令。
参考
gpio按键驱动
https://blog.csdn.net/syn_dyf/article/details/104366146
高通GPIO驱动(DTS方式)
https://www.cnblogs.com/linhaostudy/p/8372777.html
高通GPIO配置简介
https://blog.csdn.net/ldinvicible/article/details/52421640
高通DTS 配置 GPIO 中断
https://ciellee.blog.csdn.net/article/details/101226749
高通平台中gpio简单操作和调试
https://blog.csdn.net/s_jason/article/details/73864103?spm=1001.2101.3001.6650.9&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-9.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-9.no_search_link
高通平台GPIO pinctrl调试心得
https://blog.csdn.net/yxw0609131056/article/details/80662462
高通平台直接通过adb控制GPIO电平
https://blog.csdn.net/lhh_qrsly/article/details/110939319?spm=1001.2101.3001.6650.17&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-17.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-17.no_search_link
android控制gpio实现对小灯读写(一)
https://blog.csdn.net/zhonglunshun/article/details/70312945
android驱动学习---led实验
https://www.iteye.com/blog/koliy-1424304
Android字符设备驱动及应用层从jni控制GPIO实战
https://blog.csdn.net/qf0727/article/details/52856490
android jni控制gpio (rk3288)
https://www.cnblogs.com/CZM-/p/9622663.html