标签 aosp 下的文章

永久关闭SELinux
https://zhuanlan.zhihu.com/p/336648934

android+关闭selinux权限问题,记录Android开发中SELINUX权限问题-Go语言中文社区
https://blog.csdn.net/weixin_39600704/article/details/117757795

Android程序报错: 权限问题java.io.FileNotFoundException: ……:open failed: EACCES (Permission denied)
https://blog.csdn.net/tm_6666/article/details/106792314?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-2.highlightwordscore&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-2.highlightwordscore

操作sys/device下文件节点的三种方法
https://blog.csdn.net/tanliyin/article/details/72723229?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-9.highlightwordscore&spm=1001.2101.3001.4242.6

读取设备节点/sys/class/XX :Android java.io.FileNotFoundException: open failed: EACCES (Permission denied)
https://blog.csdn.net/wds1181977/article/details/54967110

充电指示灯控制中SELinux权限问题
https://blog.csdn.net/mickeymousemei123/article/details/99828287

LED节点访问增加selinux权限记录
https://blog.csdn.net/wangwei6227/article/details/86648876

Selinux SeAndroid理解
https://blog.csdn.net/bsxiaomage/article/details/51126826

SELinux权限问题解决方法
https://blog.csdn.net/w2064004678/article/details/105515244/

记录Android开发中SELINUX权限问题
https://blog.csdn.net/maowendi/article/details/105564908

android:sharedUserId="android.uid.system" 的使用
https://blog.csdn.net/m0_38094058/article/details/82628731

关于android:sharedUserId="android.uid.system" 的使用
https://blog.csdn.net/MYBOYER/article/details/104902159

selinux常见neverallow项解决方法与常用命令
https://blog.csdn.net/k663514387/article/details/107983037

Android P系统编译报错SELinux违反Neverallow
https://blog.csdn.net/Sunxiaolin2016/article/details/91435362

[Android Framework]Android 11 SELinux avc权限解决方法
http://www.zyiz.net/tech/detail-259152.html

Android SELiunx 权限添加
https://blog.51cto.com/u_15127678/2800306

深刻理解SELinux SEAndroid
https://www.shangmayuan.com/a/d7ccd609100e479fad0952d6.html

Neverallow failures occurred Error while expanding policy in AOSP10
https://discuss.96boards.org/t/neverallow-failures-occurred-error-while-expanding-policy-in-aosp10/9712

Need some Guide with a Android SELinux Build Error
https://stackoverflow.com/questions/58956629/need-some-guide-with-a-android-selinux-build-error

SELINUX安全访问机制,配置
https://www.codenong.com/cs110875925/

【Android笔记】Android应用层读取驱动设备节点
https://www.jianshu.com/p/e4a8549a903c

selinux 问题集锦
https://blog.csdn.net/chenpuo/article/details/116456987

linux sysfs文件系统(三)属性文件读写原理分析
https://blog.csdn.net/oqqYuJi12345678/article/details/101849978

Android 上层应用读写设备节点
https://blog.csdn.net/huhuayouxu/article/details/53783799?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-0.no_search_link&spm=1001.2101.3001.4242.1

Android app如何正确读写系统sys设备节点
https://blog.csdn.net/u014341735/article/details/51244258

Selinux机制介绍与添加流程
https://blog.csdn.net/hjlgs/article/details/98958141
android 开发串口中遇到的权限问题 (个人笔记)
https://blog.csdn.net/only_you_zj/article/details/80098192 Android串口设备热插拔,ttyACM、ttyUSB权限修改
https://blog.csdn.net/u013595260/article/details/119496169
android _5.1 _串口权限
https://blog.csdn.net/izhetu/article/details/52311450
Android 串口相关权限问题
https://blog.csdn.net/qq_39734379/article/details/80525389
android读写串口的权限问题
https://blog.csdn.net/dddxxxx/article/details/53635966

深入理解SELinux SEAndroid(第一部分)
https://blog.csdn.net/innost/article/details/19299937
SELinux/SEAndroid 实例简述(一) 基础概念
https://blog.csdn.net/shell812/article/details/54930027
SELinux/SEAndroid 实例简述(二) TE语言规则
https://blog.csdn.net/shell812/article/details/54930246
SELinux/SEAndroid 实例简述(三)实例看SELinux/SEAndroid
https://blog.csdn.net/shell812/article/details/58596377
【selinux】selinux加prop,hal层读取
https://blog.csdn.net/qq_38091632/article/details/113831585

转自: https://blog.csdn.net/vviccc/article/details/108806893

1 原理

在init启动时就直接设置selinux_is_enforcing强制返回为false。这样 开机启动后 执行

$getenforce

操作时 获取到的是 Permissive,即 SELinux 关闭状态。

2 修改方案(Android O)

修改文件为:system/core/init/init.cpp,修改内容如下:

    @@ -571,7 +571,7 @@ static void selinux_init_all_handles(void)
     }

     enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
    -
    +#if 0
     static selinux_enforcing_status selinux_status_from_cmdline() {
         selinux_enforcing_status status = SELINUX_ENFORCING;

    @@ -583,13 +583,17 @@ static selinux_enforcing_status selinux_status_from_cmdline() {

         return status;
     }
    +#endif

     static bool selinux_is_enforcing(void)
     {
    +   return false;
    +#if 0
         if (ALLOW_PERMISSIVE_SELINUX) {
             return selinux_status_from_cmdline() == SELINUX_ENFORCING;
         }
         return true;
    +#endif
     }

     static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {

3 修改方案(Android Q)

修改文件为:system/core/init/selinux.cpp,修改内容如下:

     selabel_handle* sehandle = nullptr;

     enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
    -
    +#if 0
     EnforcingStatus StatusFromCmdline() {
         EnforcingStatus status = SELINUX_ENFORCING;

    @@ -95,12 +95,15 @@ EnforcingStatus StatusFromCmdline() {

         return status;
     }
    -
    +#endif
     bool IsEnforcing() {
    +#if 0
         if (ALLOW_PERMISSIVE_SELINUX) {
             return StatusFromCmdline() == SELINUX_ENFORCING;
         }
         return true;
    +#endif
    +    return false;
     }

     // Forks, executes the provided program in the child, and waits for the completion in the parent.

转自: https://note.youdao.com/ynoteshare/index.html?id=a5e6662dc0b4a2c0d929fdcf6bdea975&type=note&_time=1638928207538

验证是否是权限问题。

1.机器首先需要有root权限,执行setenforce 0,执行完getenforce显示Permissive就成功了; 2.修改代码 system/core/init/init.c

bool is_enforcing = selinux_is_enforcing();
+is_enforcing=false;
INFO("SELinux: security_setenforce(%d)\n", is_enforcing);  

先模块编译system/core/init/,然后再编译bootimage

如果以上处理完成后,问题得到解决,则说明是Selinux权限限制导致的该问题;

项目处理:

<38>[   22.872478] type=1400 audit(1325376041.960:3): avc: denied { read write } for pid=585 comm="system_server" name="au_interrupt" dev="tmpfs" ino=11449 scontext=u:r:system_server:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissive=1
<38>[   22.896195] type=1400 audit(1325376041.960:4): avc: denied { open } for pid=585 comm="system_server" path="/dev/au_interrupt" dev="tmpfs" ino=11449 scontext=u:r:system_server:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissive=1
<6>[   23.009953] lowmemorykiller: lowmem_shrink: convert oom_adj to oom_score_adj:

1.使用adb shell进入/dev/au_interrupt,执行ls -Z查看此设备的属性设置,此时显示是u:object_r:device:s0 ,对此device进行重新定义;

device\intel\baytrail\sepolicy\file_contexts-->/dev/au_interrupt                 u:object_r:au_interrupt_device:s0
device\intel\baytrail\sepolicy\device.te-->type au_interrupt_device, dev_type;

编译后全部升级,再次进入/dev/下查看ls -Z属性,此时应显示为 u:object_r:au_interrupt_device:s0; (restorecon /dev/au_interrupt 重新加载属性,然后ls -Z;也可以将此命令写入rc文件)

2.线程名称是system_server,所以进入system_server.te对其进行权限赋值操作;

allow system_server au_interrupt_device:chr_file {read write open};

如一行LOG:

<5>[ 17.285600].(0)[503:idmap]type=1400 audit(1356999072.320:202): avc: denied {create } for pid=503 comm="idmap" name="overlays.list" scontext=u:r:zygote:s0
tcontext=u:object_r:resource_cache_data_file:s0 tclass=file

即表明idmap 这个process, 使用zygote 的source context, 访问/data/resource_cache 目录,并创建文件时,被SELinux 拒绝访问。

为了规避这种权限放大情况, 我们需要细化访问目标(Object) 的SELinux Label, 做到按需申请.

通常会由三步构成 3.1 定义相关的SELinux type. 比如上述案例, 在 device/mediatek/common/sepolicy/device.te 添加

type tfa9897_device, dev_type;

3.2 绑定文件与SELinux type. 比如上述案例, 在 device/mediatek/common/sepolicy/file_contexts 添加

/dev/tfa9897(/.*)? u:object_r:tfa9897_device:s0

3.3 添加对应process/domain 的访问权限. 比如上述案例, 在 device/mediatek/common/sepolicy/mediaserver.te 添加

allow mediaserver tfa9897_device:chr_file { open read write };

- 阅读剩余部分 -

转自: https://blog.csdn.net/jydzm/article/details/105864194?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link

通过对/sys/class/leds/brightness节点写数据来驱动LED灯打开或关闭。

adb shell
echo 125 > /sys/class/leds/brightness (打开LED灯,1-255,值越大,LED越亮)
echo 0 > /sys/class/leds/brightness (关闭LED灯)

以上是通过adb 操作的,接下来是在APP应用中驱动LED灯打开和关闭方法:

方法1:

public void cmdLedOpen() {
    String cmd = "/system/bin/busybox " + "echo 240 >/sys/class/leds/brightness " + "\n";
    try {
        Process exeEcho = Runtime.getRuntime().exec("sh");
        exeEcho.getOutputStream().write(cmd.getBytes());
        exeEcho.getOutputStream().flush();
        Log.d(TAG, " " + cmd);
    } catch (Exception e) {
        Log.d(TAG, "cmdRelayOpen faild");
    }
}

方法2:

public void openLed(){
    try {
        BufferedWriter bw = new BufferedWriter(new FileWriter("/sys/class/leds/brightness"));
        bw.write("250");
        bw.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

转自: https://blog.csdn.net/u010753159/article/details/51356331?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-7-51356331.nonecase&utm_term=android%20%E9%AB%98%E9%80%9A%E7%9A%84%E5%BC%80%E6%9C%BA%E5%8A%A8%E7%94%BB

在上篇文章《Android5.1开机画面显示工作流程分析》中,详细分析了Android开机动画显示的工作流程。其中提到了每个开机动画压缩文件中必须包含一个描述文件desc.txt,该文件用来描述开机动画具体是怎么样显示的。这篇文章就对desc.txt进行一个详细的解读。

1 desc.txt文件格式分析

desc.txt文件由若干行组成,每一行代表一种描述。下面以一个具体的例子为例,具体说明

    480 640 20
    p 1 0 folder1
    p 2 20 folder2
    c 0 0 folder3
    c 1 0 folder4

第1行用来描述开机动画在屏幕显示的大小及速度。具体为:开机动画的宽度为480个像素,高度为640个像素,显示频率为每秒20帧,即每帧显示1/20秒。

下面的每一行代表一个片段,显示的时候会按照顺序从上到下依次显示。第1个字符为片段类型,有'c'和'p'两种,两者的区别后面会结合代码说明。

第2个数字为该片段重复显示的次数,如果为‘0’,表示会无限重复显示;第3个数字为两次显示之间的间隔,单位为第一行中定义的每帧显示的时间;第4个字符串为该片段所在的文件夹,一个片段可以由多个png图片组成,都存放在folder文件夹中。

“p 1 0 folder1”代表该片段显示1次,与下一个片段间隔0s,该片段的显示图片路径为bootanimation.zip/folder1。

“p 2 20 folder2”代表该片段显示2次,且两次之间显示的间隔为20(1/20)=1s,与下一个片段间隔20(1/20)=1s,该片段的显示图片路径为bootanimation.zip/folder2。

“c 0 0 folder3”代表该片段无限循环显示,且两次显示的间隔为0s,与下一个片段间隔0s,该片段的显示图路径为bootanimation.zip/folder3。

“c 1 10 folder4”代表该片段显示1次,显示后暂停10*(1/20)=0.5s,该片段的显示图路径为bootanimation.zip/folder4。

2 "p"片段和“c”片段的区别

在早期Android版本中只有“p”片段,且movie()中的显示代码如下:

    for (int i=0 ; i<pcount && !exitPending() ; i++) {  
            const Animation::Part& part(animation.parts[i]);  
            const size_t fcount = part.frames.size();  
            glBindTexture(GL_TEXTURE_2D, 0);  

            for (int r=0 ; !part.count || r<part.count ; r++) {  
                for (int j=0 ; j<fcount && !exitPending(); j++) {  
                    const Animation::Frame& frame(part.frames[j]);  

                .......

        .....
    }

里面的主要参数和函数说吗如下:

pcount---显示片段的数量,比如上面的例子,pcount=4

p.count---该片段的重复显示次数。

fcount---该片段中png图片的数量 exitPending()---如果SurfaceFlinger服务通知bootanimation停止显示动画,则该函数返回值为true,否则为false。

第一个for循环用于顺序显示所有片段,第二个for循环用于重复显示该片段,第三个for循环用于顺序显示该片段中所有的png图片。

分析代码,可知:若exitPending()返回值为true,即SurfaceFlinger服务要求bootanimation停止显示动画,则不管当前显示到哪个片段或png图片,都会导致退出for循环,从而停止开机动画的显示。

在Android5.1中,加入了“c”片段。对与以"c"标识的片段,即使exitPending()返回值为true,也会继续显示。

我们分析一下源码,首先看一下movie()中解析desc.txt的代码:

        // Parse the description file  
        for (;;) {  
            ...... 
            if (sscanf(l, "%d %d %d %d", &width, &height, &fps, &flg) >= 3) {  
                animation.width = width;  
                animation.height = height;  
                animation.fps = fps;  
            }  
            else if (sscanf(l, " %c %d %d %s #%6s", &pathType, &count, &pause, path, color) >= 4) {  
                Animation::Part part;  
                part.playUntilComplete = pathType == 'c';  
                part.count = count;  
                part.pause = pause;  
                part.path = path;  
                part.audioFile = NULL;  
                if (!parseColor(color, part.backgroundColor)) {  
                    ALOGE("> invalid color '#%s'", color);  
                    part.backgroundColor[0] = 0.0f;  
                    part.backgroundColor[1] = 0.0f;  
                    part.backgroundColor[2] = 0.0f;  
                }  
                animation.parts.add(part);  
            }  

            s = ++endl;  
        }

可以看到,如果pathType==‘c’,part.playUntilComplete等于true,否则为false。接着,看一下显示代码:

    for (size_t i=0 ; i<pcount ; i++) {  
            const Animation::Part& part(animation.parts[i]);  
            const size_t fcount = part.frames.size();  
            glBindTexture(GL_TEXTURE_2D, 0);  

            for (int r=0 ; !part.count || r<part.count ; r++) {  
                // Exit any non playuntil complete parts immediately  
                if(exitPending() && !part.playUntilComplete)  
                    break;  

                ......
                for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {  

            ......

                    checkExit();  
                }  

                usleep(part.pause * ns2us(frameDuration));  

                // For infinite parts, we've now played them at least once, so perhaps exit  
                if(exitPending() && !part.count)  
                    break;  
            }  

            ......  
        } 

可以看到,如果exitPending()返回值为true且part.playUntilComplete=false,则会break。即:当SurfaceFlinger服务要求bootanimation停止显示动画时,以‘p’标识的片段会停止,而以'c'标识的片段会继续显示。这就是两者之间的主要区别。

这里有个问题:重复循环显示的'c'标识片段,会不受任何约束的一直显示下去,这显然是不合适的。 于是在第二个for循环体最后,有如下代码:

    // For infinite parts, we've now played them at least once, so perhaps exit  
                if(exitPending() && !part.count)  
                    break; 

意思是,如果检测到SurfaceFlinger服务要求bootanimation停止显示,且该片段的显示次数为'0',即重复循环显示,则会break停止显示。

我猜想"c"标识的意思是continue,即:即使SurfaceFlinger要求bootanimation停止动画,bootanimation也不会立刻停止动画,它会等c标识片段都显示完毕后,再停止。

这样,我们可以利用'c'和'p'片段的区别,设计出更灵活的开机动画。

转自: https://blog.csdn.net/u010218230/article/details/80007819?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-1.fixedcolumn&spm=1001.2101.3001.4242.2

应用层中,一般都能够读取设备节点。对于写节点这个操作,需要更高的root权限。

示例: 设备节点:

private static final String LED_RED_TRIGGER = "sys/class/leds/red/on_off";

写节点:

    private void setLedOnOff(String path, String value) {
        try {
            BufferedWriter bufWriter = null;
            bufWriter = new BufferedWriter(new FileWriter(path));
            bufWriter.write(value + "");
            bufWriter.close();
            Log.d(TAG, "write setLedOnOff: " + value);
        } catch (IOException e) {
            Log.e(TAG,"erro= "+ Log.getStackTraceString(e));
        }
    }

读节点:

    private boolean isLedOn(String path){
        try {
            FileReader fr = new FileReader(path);
            BufferedReader br = new BufferedReader(fr);
            String readString = null;
        String valueString = null;
        while ((readString = br.readLine())!=null){
                if(readString == null)break;
                valueString = readString;
            }
            br.close();
            return valueString != null && valueString.equals("1");//此处1表示灯亮,0表示灯灭
        } catch (IOException e) {
           Log.e(TAG,"erro= "+ Log.getStackTraceString(e));
        }
        return false;
    }

打开更多信息

adb shell echo 8 > proc/sys/kernel/printk

抓取 log 到文件

adb logcat -v time -b all > logcatlog.txt

查看按键是否上报

adb shell
getevent -l

获取按键更多 log

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java 中,把 PhoneWindowManager 这个类开始的 DEBUG_ 相应的选项全部开启,然后抓 log,会有更多的信息。 在 logcatlog.txt 中搜索 keycode 能够看到相应的按键信息。

配置以太网

ifconfig eth0 192.168.42.10 up
ndc network create 100
ndc network interface add 100 eth0
ip route add  default  dev eth0 table eth0
ndc network default set 100 

串口调试

su 进入 root 模式,然后就可以使用各种命令了。

获取 配置信息

cat proc/cmdline 可以获取到详细的信息。类似:

PS C:\Users\Administrator\Downloads> adb shell cat proc/cmdline
sched_enable_hmp=1 console=ttyMSM0,115200,n8 androidboot.console=ttyMSM0 androidboot.hardware=qcom user_debug=30 msm_rtb.filter=0x237 ehci-hcd.park=3 androidboot.bootdevice=7824900.sdhci lpm_levels.sleep_disabled=1 earlycon=msm_hsl_uart,0x78B0000 vmalloc=300M androidboot.usbconfigfs=true loop.max_part=7 buildvariant=userdebug androidboot.emmc=true androidboot.verifiedbootstate=orange androidboot.keymaster=1 dm="1 vroot none ro 1,0 6192024 verity 1 PARTUUID=7e631c69-e9c2-c3a5-4546-cce1a015dd9e PARTUUID=7e631c69-e9c2-c3a5-4546-cce1a015dd9e 4096 4096 774003 774003 sha1 2784ab9d836408d49146c1652a46d2475ced3b5c ff0a52cb0d14e1706741d069fd806262a53cc6eb 10 ignore_zero_blocks ignore_zero_blocks use_fec_from_device PARTUUID=7e631c69-e9c2-c3a5-4546-cce1a015dd9e fec_roots 2 fec_blocks 780099 fec_start 780099" root=/dev/dm-0 androidboot.vbmeta.device=PARTUUID=7b85f276-e2ea-7cec-b973-4fe4bafd7fb0 androidboot.vbmeta.avb_version=1.0 androidboot.vbmeta.device_state=unlocked androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=4160 androidboot.vbmeta.digest=1b4ac7cea500c90d3e98094f85b9bbef5c2f0cec330efd8ff9be7ddc6919cf9f androidboot.veritymode=eio androidboot.serialno=e7ba90a1 androidboot.baseband=msm mdss_mdp.panel=1:dsi:0:qcom,mdss_dsi_ili9881c_720p_video:1:none:cfg:single_dsi skip_initramfs rootwait ro init=/init androidboot.dtbo_idx=23 androidboot.dtb_idx=5

参考

android系列:第三篇android调试常用工具:模拟按键输入,修改分辨率,获得按键消息
https://blog.csdn.net/radianceblau/article/details/75221368

制作 bootanimation.zip

  1. 准备好 part0 等文件夹 和 desc.txt. desc.txt 中最后一行一定是空行。
  2. 选中所有 part 文件夹 和 desc.txt,后右键压缩,选择存储和 zip 格式。

注意,一定是选中所有文件之后压缩,而不是在 bootanimation 这个文件夹上压缩,否则压缩文件中会多出一层文件夹。系统启动的时候,动画阶段会黑屏几秒,然后直接跳到桌面。 还有 desc.txt 中最后一定要是空行,否则也会出问题。

验证 动画包是否正确。

  1. adb root, adb disable-verity, adb reboot
  2. adb root, adb remount
  3. adb shell ls /system/media 确保 system 文件夹下面有 media,如果没有,那么 adb shell mkdir /system/media
  4. adb push bootanimation.zip /system/media/ 确认 push 正常。
  5. adb reboot 检查是否替换完成。

增加文件

vendor/qcom/proprietary/qrdplus/Extension/apps/BootAnimation 文件夹下面增加相应的压缩包和编译文件.

.
└── apps
    └── BootAnimation
        ├── Android.mk
        ├── bootanimation.zip    
        └── shutdownanimation.zip

bootanimation.zip 开机动画,shutdownanimation.zip 关机动画。

Android.mk

 LOCAL_PATH := $(call my-dir)
$(warning '------------------------------------abcxyz---------------------------------------')
$(shell mkdir -p $(TARGET_OUT)/media)
$(warning $(TARGET_OUT))
$(warning '------------------------------------abcxyz---------------------------------------')
$(shell cp -r $(LOCAL_PATH)/bootanimation.zip  $(TARGET_OUT)/media/bootanimation.zip)
$(shell cp -r $(LOCAL_PATH)/shutdownanimation.zip  $(TARGET_OUT)/media/shutdownanimation.zip)
$(shell cp -r $(LOCAL_PATH)/*.wav  $(TARGET_OUT)/media)

检查是否生效

查看 out/target/product/msm8937_32/system/media 下面是否有相应的动画压缩包。

参考:

https://blog.csdn.net/myvest/article/details/50856199
https://blog.csdn.net/qq_37858386/article/details/120365883
https://blog.csdn.net/luoshengyang/article/details/7691321
安卓手机开机动画bootanimation.zip文件制作以及注意事项
https://blog.csdn.net/zhaoqi2617/article/details/70170387

增加编译信息

LOCAL_PATH := $(call my-dir)
$(warning '------------------------------------abcxyz---------------------------------------')
...

编译

make  2>&1 | tee xyz.log

这样相关的信息,就会打印到 xyz.log 这个文件里面了,在这个文件里面搜索 abcxyz 就比较方便能看到是否编译了。

检查 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