添加设备记录

本文详细记录了在Linux kernel中为RK3399平台添加GPIO驱动的过程,包括创建drivers文件夹、编写dts文件、驱动程序的编写与匹配、menuconfig配置、内核编译调试,以及在Android系统上编译应用程序和开机启动脚本的方法。同时,文章提出了在设备树兼容性设置和Android调用C/C++库方面存在的疑问。
摘要由CSDN通过智能技术生成

添加设备记录

gpio

1.用到gedit编辑器

2.在 kernel/drivers 路径下创建自己的drivers文件夹 rk3399-ZK-drivers/gpio

3.创建自己的dts文件夹并添加dts文件

添加设备树文件

路径 kernel/arch/arm64/boot/dts/rockchip

添加

rk3399-ZK.dtsi 文件

添加设备树节点
   gpio_demo: gpio_demo {
       status = "okay";
       compatible = "ZK,ZK-gpio";
       ZK-gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;          /* GPIO0_B4 */
       ZK-irq-gpio = <&gpio4 29 IRQ_TYPE_EDGE_RISING>;  /* GPIO4_D5 */
   };
在Makefile中添加要编译成.dtb的设备树文件

路径 kernel/arch/arm64/boot/dts/rockchip

添加

 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-ZK.dtb

4.添加驱动文件

在kernel/drivers路径下添加自己的驱动文件夹rk3399-ZK-drivers

修改本路径下Makefle,Konfig 文件

Kconfig添加
#ZK drivers.
source "drivers/rk3399-ZK-drivers/Kconfig"

Makefile添加
#rk3399 ZK drivers
obj-y                           += rk3399-ZK-drivers/

在kernel/drivers/rk3399-ZK-drivers路径下添加gpio文件

添加Makefle,Konfig 文件

Kconfig添加
menu "rk3399-ZK-drivers Drivers"
source "drivers/rk3399-ZK-drivers/gpio/Kconfig"
endmenu

Makefile添加
obj-y   += gpio/

在kernel/drivers/rk3399-ZK-drivers/gpio路径下创建rk3399-ZK-gpio-demo.c驱动文件

添加Makefle,Konfig 文件

Kconfig添加
config CONFIG_GPIO_DEMO
	tristate "leanr for config gpio driver"#y/m 
	help
		test gpio driver
		
Makefile添加
obj-$(CONFIG_GPIO_DEMO)         += rk3399-ZK-gpio-demo.o

5.编写驱动程序

我这里复制了官方的demo修改成自己的驱动程序

了解常用gpio控制函数
#include <linux/gpio.h>
#include <linux/of_gpio.h>

enum of_gpio_flags {
     OF_GPIO_ACTIVE_LOW = 0x1,
};
/*获取io口号*/
int of_get_named_gpio_flags(struct device_node *np, const char *propname,int index, enum of_gpio_flags *flags);

/*测试gpio端口是否合法*/
int gpio_is_valid(int gpio);

/*申请gpio口的使用,若申请成功,则说明gpio口未被使用*/
int gpio_request(unsigned gpio, const char *label);

/*释放gpio口*/
void gpio_free(unsigned gpio);

/*设置gpio为输入功能*/
int gpio_direction_input(int gpio);

/*设置gpio为输出功能*/
int gpio_direction_output(int gpio, int v);
将match_table 的compatible改成与设备树的compatible一致

还留有一个疑问,根文件下的compatible 怎么改?

static struct of_device_id ZK_match_table[] = {
        { .compatible = "ZK,ZK-gpio",},
        {},
};
实现rpobe函数

6.修改menuconfig配置

7.编译调试

编译编译到内核
切换到root下编译
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar

make -j8 ARCH=arm64 rk3399-firefly.img

烧录kerne.img 跟resource.img

//执行脚本打包.img文件// sdk根目录执行:./mkimage.sh

踩坑:

1.查看 .config 看宏有没有被打开
.c文件没有被编译
原因:
Kconfig
config CONFIG_GPIO_DEMO 这样是错的
config GPIO_DEMO 这样才能编译
 
 Makefile
obj-$(CONFIG_GPIO_DEMO)         += rk3399-ZK-gpio-demo.o

2.报错
Error during update of the configuration
切换到root下编译

logcat *:E 开机信息查看错误信息
编译模块(sdk暂时不知道怎么改)

https://blog.csdn.net/qq_28992301/article/details/52287792
http://www.t-firefly.com/doc/product/info/id/56.html#420E4BFAEE694B920parameter20E69687E4BBB6

1.make menuconfig 中选择驱动为m  ,编译成模块

2.编译模块:make modules ,在.c路径下生产.ko文件

3.挂载nfs
开发板端:
busybox mount -o remount, rw /
创建nfs文件夹:在开发板/data路径下创建一个nfs文件夹  /data/nfs
挂载nfs:busybox mount -t nfs -o nolock 192.168.163.128:/home/book/nfs_rootfs /data/nfs
andriod 下要加busybox,linux下不用

4.拷贝ko文件并执行
拷贝.ko文件
ubuntu端:拷贝.ko 文件到nfs路径下
开发板:
执行读写权限 busybox mount -o remount, rw /
从nfs中拷贝.img到根目录下

5.装载模块
加载模块
busybox insmod usbtest.ko
insmod: can't insert 'rk3399-zk-sim-gpio.ko': Operation not permitted ??
查看模块
busybox lsmod | grep "usbtest
卸载模块
busybox rmmod usbtest.ko
错误: mmod: can't change directory to '/lib/modules': No such file or directory
解决方法:执行读写权限 busybox mount -o remount, rw /
mkdir /lib/modules

打印dbug信息:dmesg

9.查看设备是否装载成功

  1. 查看设备跟设备号 cat /proc/devices
  2. 查看设备文件是否存在并查看主设备号跟次设备号 ls -l /dev/

10.编译应用程序

知识储备链接:

https://www.cnblogs.com/rootshaw/p/12938167.html

https://blog.csdn.net/SlowIsFastLemon/article/details/102776526

https://blog.csdn.net/u011913612/article/details/51878356?utm_medium=distribute.pc_relevant_right.none-task-blog-BlogCommendFromBaidu-3.channel_param_right&depth_1-utm_source=distribute.pc_relevant_right.none-task-blog-BlogCommendFromBaidu-3.channel_param_right

安卓编译应用程序的基本方式:https://blog.csdn.net/kuishao1314aa/article/details/80689735

Andriod.mk 的常用写法:http://blog.chinaunix.net/uid-12348673-id-3079508.html

第一步:编译条件

在/home/book/proj/firefly-rk3399-Industry/external 路径下创建自己的app模块文件夹zk_sim_app

在文件夹里面创建rk3399-zk-sim-gpio-app.c 文件

修改Android.mk 文件

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

#LOCAL_SRC_FILES :=$(call all-subdir-c-files)
LOCAL_SRC_FILES :=rk3399-zk-sim-gpio-app.c #source files

LOCAL_MODULE := zk-sim-app    #module name
LOCAL_MODULE_TAGS := optional #user \ eng \ tests \ optional
#LOCAL_SHARED_LIBRARIES :=libc liblog libselinux libext2_blkid
#LOCAL_CFLAGS += -Wno-error

include $(BUILD_EXECUTABLE)
#include $(BUILD_EXECUTABLE)        以一个可执行程序的方式进行编译
#include $(BUILD_STATIC_LIBRARY)  编译静态库
#include $(BUILD_SHARED_LIBRARY) 编译动态库

第二步:编译

编译方法:

1.source build/envsetup.sh
2.lunch 你产品分支数字
3.make  [模块名(Android.mk中模块名字)]

第三步:拷贝app程序到开发板执行

我这里写一个脚本install_app.sh在sdk根目录下 ,执行脚本将编译好的app拷贝到nfs共享目录nfsrootfs文件夹下

#!/bin/bash

a=$1
b="img"

if [ "$a" = "$b" ]
then
cd /home/book/proj/firefly-rk3399-Industry/kernel
make -j8 ARCH=arm64 rk3399-firefly.img
printf "make img success\n"
cp -r ./*.img /home/book/proj/image/
printf "copy img to samba success\n"

else
cd /home/book/proj/firefly-rk3399-Industry
source build/envsetup.sh

lunch 8
make -j8 $1
cp -r /home/book/proj/firefly-rk3399-Industry/out/target/product/rk3399_all/system/bin /home/book/nfs_rootfs/
printf "copy app to nfs success\n"
fi

第四步:开发板挂载nfs并执行app程序

su

文件系统读写权限:busybox mount -o remount, rw /

创建挂载路径:mkdir  /data/nfs

修改驱动权限:busybox chmod 777 dev/ioctrl

ifconfig 192.168.163.123

挂载nfs:busybox mount -t nfs -o nolock 192.168.163.128:/home/book/nfs_rootfs /data/nfs

安装应用程序: ./data/nfs/bin/zk_sim_app_share


andriod 下要加busybox,linux下不用

添加安卓开机脚本

方法1(未成功)

参考链接:https://blog.csdn.net/u012975242/article/details/83274693

​ https://blog.csdn.net/qq_39048075/article/details/103176332

​ https://zhuanlan.zhihu.com/p/48968590

1.在device/rockchip/rk3399/路径下创建zkstartboot.sh文件

2.system/core/rootdir/init.rc中添加

service zkstartboot /system/bin/zkstartboot.sh
	class main
    user root
    group root
    oneshot
	seclabel u:r:zkstartboot:s0

on property:persist.sys.boot_completed=1
    start zkstartboot
    

注意!!服务的名字不要有下划线‘_’,没完全理解,请不要另类

3.将自己写的脚本文件拷贝到system/bin/ 目录(编译的时候还生成)

实际最终是被拷贝到/home/book/proj/firefly-rk3399-Industry/out/target/product/rk3399_firefly/system/bin

在device/rockchip/rk3399/device.mk中添加

PRODUCT_COPY_FILES += \
 $(LOCAL_PATH)/zkstartboot.sh:system/bin/zkstartboot.sh \

4.seclabel u:r:zk_start:s0 一起的还需要在 system/sepolicy/file_contexts中添加

/system/bin/zkstartboot            u:object_r:zkstartboot_exec:s0

5.在system/sepolicy/目录下新建一个文件——zkstartboot.te

type zkstartboot, domain;
type zkstartboot_exec, exec_type, file_type;
init_daemon_domain(zkstartboot)

6.编译安卓系统,重新烧录

make installclean

source build/envsetup.sh

lunch 你产品分支数字

//整体编译
./FFTools/make.sh -j8 -d rk3399-firefly -l rk3399_firefly-userdebug

//make -j8

./mkimage.sh
方法2(成功)

https://blog.csdn.net/u010164190/article/details/84532951

1.开机修改为:Permissive模式启动(sdk已经是这个模式)

2.在device/rockchip/rk3399/路径下创建zkstartboot.sh文件

#!/bin/bash
 
#ip rule add from all lookup main pref 9999
#setprop persist.sys.boot_completed 1
#on property:persist.sys.boot_completed=1
echo "*******************************"
echo "test zk_startboot success\n" 
echo "*******************************"

su

busybox mount -o remount, rw /

mkdir  /data/nfs
#chmod 777 /data/nfs

#mkdir   /data/install_app
#busybox cp /data/nfs/bin /data/install_app

chmod 777 dev/ioctrl

ifconfig eth0 192.168.163.123

busybox mount -t nfs -o nolock 192.168.163.128:/home/book/nfs_rootfs /data/nfs

echo "*******************************"
echo "busybox mount -t nfs -o nolock 192.168.163.128:/home/book/nfs_rootfs /data/nfs  \n" 
echo "*******************************"

3.system/core/rootdir/init.rc中添加

service zkstartboot  /system/bin/busybox  sh /system/bin/zkstartboot.sh
#service zkstartboot  /system/bin/zkstartboot.sh
	class main
    user root
#    group root
    oneshot
#	seclabel u:r:zkstartboot:s0

on property:persist.sys.boot_completed=1
    start zkstartboot

注意!!服务的名字不要有下划线‘_’,没完全理解,请不要另类

4.将自己写的脚本文件拷贝到system/bin/ 目录(编译的时候还生成)

实际最终是被拷贝到/home/book/proj/firefly-rk3399-Industry/out/target/product/rk3399_firefly/system/bin

在device/rockchip/rk3399/device.mk中添加

PRODUCT_COPY_FILES += \
 $(LOCAL_PATH)/zkstartboot.sh:system/bin/zkstartboot.sh \
注意:make installclean 必须执行不然编译报错

5.编译安卓系统,重新烧录

安卓如何调用c/c++

1.andriod studio创建一个native c++ 工程,拷贝怎么cpp文件到自己的app工程

2.添加并修改自己的c/c++代码

3.创建native调用函数并把c/c++的的数据放入这个native函数

JNIEXPORT jint JNICALL
Java_com_example_myapplication_MainActivity_testJNI(JNIEnv *env, jobject thiz) {
   // test(0  ,"/dev/contrl");
    return 0;
}
函数命名规则:
com_example_myapplication  package名     package com.example.myapplication;
MainActivity               类的名字
testJNI                    native函数名   public native int testJNI();

注意,这个函数名一定要跟自己的package一致

4.修改CMakeLists.tx

add_library( # Sets the name of the library.
        lib-test //生成的库的名字
        SHARED   //共享库
        # Provides a relative path to your source file(s).
        test.c   //生成库的源文件
        )
        
include_directories(src/main/cpp/)//包含文件路径


target_link_libraries( # Specifies the target library.
        lib-test //cmake链接的库的名字

                       ${log-lib} )

5.修改src目录下的build.gradle

添加

    
        defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        /*******添加*******/
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
        /***************************/

    }

 android {
 下添加
 /***********************************/
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"//cmakelists 的路径
            version "3.10.2"
        }
    }
    /*********************************/

6.c/c++的实现

JNIEXPORT jint
JNICALL
Java_com_example_myapplication_ZKnative_InstallZKDEV(JNIEnv *env, jobject thiz) {

    if (fd <= 0)fd = open("/dev/ioctrl", O_RDWR | O_NDELAY | O_NOCTTY);
//    if (fd <= 0)fd = open("/dev/contrl", O_RDWR );
    __android_log_print(ANDROID_LOG_INFO, "kernel_c", "open dev %d successful !", fd);
    return 0;
}

JNIEXPORT jint
JNICALL
Java_com_example_myapplication_ZKnative_led(JNIEnv *env, jobject thiz, jint lednumber,
                                            jstring led_status) {
    
    char *ledops;
    //const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
    ledops = (char*)(*env)->GetStringUTFChars(env,led_status, NULL);//将jstring类型装换成char类型

    int led;
    if (lednumber == 1) {
        led = ZK_SIM_LED_R;
    }

    if (fd <= 0)fd = open("/dev/ioctrl", O_RDWR | O_NDELAY | O_NOCTTY);
    else {
        __android_log_print(ANDROID_LOG_INFO, "kernel_c", "in led ops!!!");
        if (strcmp("open", ledops) == 0 || strcmp("OPEN", ledops) == 0) {
            ioctl(fd, led, OPEN); //call the output fu
            // nction to off LEDs
            __android_log_print(ANDROID_LOG_INFO, "kernel_c", "OPEN %d led!");
            printf("OPEN %d led!\n", led);
        } else if (strcmp("close", ledops) == 0 || strcmp("CLOSE", ledops) == 0) {
            ioctl(fd, led, CLOSE); //call the
            __android_log_print(ANDROID_LOG_INFO, "kernel_c", "CLOSE %d led!");
            printf("CLOSE %d led!\n", led);
        } else {
            __android_log_print(ANDROID_LOG_INFO, "kernel_c", "unknown led command !");
            __android_log_print(ANDROID_LOG_INFO, "kernel_c", " ledops %s" ,ledops);
            printf("unknown led command !");
        }
    }
    return 0;
}

7.app调用

这里创建一个类
package com.example.myapplication;

public class ZKnative {
    static {
        System.loadLibrary("lib-test");
    }

    //    public native String stringFromJNI();
    public native int InstallZKDEV();
    public native int led(int lednumber,String led_status);

}


程序调用类
    public void aaa(View v) {
        new ZKnative().led(1,"open");
        //Log.d("jni",""+new ZKnative().testJNI());
        //Toast.makeText(MainActivity.this,"开灯",Toast.LENGTH_SHORT).show();

    }

    public void bbb(View v) {
        new ZKnative().led(1,"close");
        //Toast.makeText(this,"关灯",Toast.LENGTH_LONG).show();
    }

遗留问题:

3.如何快速地位日志信息

其他记录:

烧录app路径:/home/book/nfs_rootfs

烧录驱动路径:/home/book/proj/firefly-rk3399-Industry/kernel

/home/book/proj/firefly-rk3399-Industry/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-gcc-4.9 test.c -o haha

#export ARCH=arm64
#export CROSS_COMPILE=aarch64-linux-gnu-
#export PATH=$PATH:/home/book/proj/firefly-rk3399-Industry/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin

git

$ git push -u origin master

/home/book/proj/firefly-rk3399-Industry/out/target/product/rk3399_firefly/system/lib64#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值