检查 io 是否被占用。

  1. adb root, adb shell
  2. cd /sys/class/gpio 进入 gpio 目录
  3. echo 46 > export 获取 gpio_46
  4. 检查上条命令是否提示 /system/bin/sh: can't create export: Read-only file system,如果提示这个,那么说明这个 gpio 被占用了,否则这个 gpio 就是空的。
  5. echo 46 > unexport 释放 gpio_46

驱动代码 (quectel)

不管输入还是输出 gpio,直接仿照这个源文件里面进行添加即可。 比如说这个源码里面提供了四个 gpio,key_home, red_led_en, green_led_en, yellow_led_enSC200R_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_homefind . -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

标签: aosp

添加新评论