1.5JNI的简单使用-加载内核驱动

目录

LED驱动加载

方式一

方式二:

设备树方式:

C库修改

实验现象


上一节我们介绍 了andriod软件层怎么调用C程序,以及C库的编译与加载,实现了应用层和底层连系的关键部分,接下来,把内核驱动部分编写完成,该章节就结束了。

LED驱动加载

相信大家看到这里已经有了一定linux驱动的基础,简单驱动不做介绍,可另行查阅其他资料,创建C文件leds_drv.c,代码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/gpio.h>

static const int led1_gpio = (32*4 + 8*3 + 3);//LED1 BLUE 0有效
//static const int led2_gpio = (32*0 + 8*1 + 0);

static int rongpin3288_leds_open(struct inode *inode, struct file *file)
{
	return 0;
}

static long rongpin3288_leds_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	//printk("cmd = %u    arg = %lu\n",cmd,arg);
	switch(cmd)
	{
		case 0:	switch(arg)
				{
					case 0: gpio_set_value(led1_gpio,0); break;
					case 1:	gpio_set_value(led1_gpio,1); break;
				}break;
		
		case 1: switch(arg)
				{
					//case 0: gpio_set_value(led2_gpio,0); break;
					//case 1:	gpio_set_value(led2_gpio,1); break;			
				}break;
	}
	return 0;
}

static int rongpin3288_leds_release(struct inode *inode, struct file *file)
{
	return 0;
}

static const struct file_operations gec3288_leds_fops = {
	.owner = THIS_MODULE,
	.open = rongpin3288_leds_open,
	.unlocked_ioctl = rongpin3288_leds_ioctl,
	.release = rongpin3288_leds_release,
}; 

static struct miscdevice rongpin3288_leds_misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.fops = &rongpin3288_leds_fops,
	.name   = "leds_drv",	
}; 

static int __init rongpin3288_leds_init(void)
{
    int ret;
	ret =  misc_register(&rongpin3288_leds_misc);   //注册字符设备
	if(ret < 0){
		printk("misc register error\n");
		goto err0;		
	}
	ret = gpio_request(led1_gpio,"led1_gpio");    //申请led1_gpio引脚为GPIO模式
	if(ret < 0){
		printk("gpio_request led1_gpio error\n");
		goto err1;
	}
/*	
	ret = gpio_request(led2_gpio,"led2_gpio");    //申请led2_gpio引脚为GPIO模式
	if(ret < 0){
		printk("gpio_request  led2_gpio error\n");
		goto err2;
	}
*/

	ret = gpio_direction_output(led1_gpio,0);      //初始划LED1为熄灭状态
	if(ret < 0){
		printk("gpio direction output  led1_gpio error\n");
		//goto err3;
		goto err2;
	}
/*
	ret = gpio_direction_output(led2_gpio,1);      //初始划LED2为熄灭状态
	if(ret < 0){
		printk("gpio direction output  led2_gpio error\n");
		goto err3;
	}
*/
    return 0;

/*	
err3:
	gpio_free(led2_gpio);
*/
err2:
	gpio_free(led1_gpio);
err1:
	misc_deregister(&rongpin3288_leds_misc);
err0:
    return ret;

}


static void __exit rongpin3288_leds_exit(void)
{
    gpio_free(led1_gpio);
	misc_deregister(&rongpin3288_leds_misc);
}

module_init(rongpin3288_leds_init);
module_exit(rongpin3288_leds_exit);

MODULE_LICENSE("GPL");


 

以下是不行的,后面再研究

编写完成之后,把该文件放在SDK/kernel/drivers/char/目录下,并且修改该目录下Makefile文件,添加:
obj-y+= led_drv.o

修改设备树:

arch/arm/boot/dts/rpdzkj_config.dtsi

添加:

回到源码根目录编译kernel

./make kernel

烧写kernel

参考:

然后执行:
make ARCH=arm64 rk3399-sapphire-excavator-edp.img -j4
编译内核,编译完成之后,返回到SDK目录,执行:
source build/envsetup.sh
lunch rk3399_all-userdebug
make bootimage -j3
等待编译完成之后,可以看到打印信息:
Target boot image: out/target/product/rk3399/boot.img
把该boot.img烧写到开发板
 

测试ok的方式

方式一

注释掉设备arch/arm/boot/dts/rpdzkj_config.dtsi树和驱动kernel/drivers/rongpin/led-gpio.c

Makefile

将上面的驱动编写成模块

Makefile

KERN_DIR = /home/rpdzkj/rk3288_5.1/3288_5.1/kernel

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m    += leds_drv.o

编译成模块:led_drv.ko

push到开发板上,切换root用户,修改权限,再将生成节点的权限修改即可

方式二:

添加配置kernel/arch/arm/configs/rp-rk3288_defconfig

修改节点权限system/core/rootdir/ueventd.rc

 

设备树方式:

arch/arm/boot/dts/rpdzkj_config.dtsi

Tled_drv.c

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h> 
#include <linux/delay.h>
#include <linux/gpio.h>
#ifdef CONFIG_OF
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#endif

#include <linux/fb.h>
#include <linux/device.h>
#include <linux/miscdevice.h>


#define INVALID_GPIO -1
int led1_gpio = (32*4 + 8*3 + 3);//LED1 BLUE 0有效
//int led2_gpio = INVALID_GPIO;
int led_gpio_active = 0;

static int led_open(struct inode *inode, struct file *file)
{    
    printk("carroll led_open ok \n");

    return 0;
}

static int led_release(struct inode *inode, struct file *file)
{    
    printk("carroll led_close \n");

    return 0;
}

/* app : ioctl(fd, cmd, arg) */
static long led_ioctl(struct file *filp, unsigned int cmd,
        unsigned long arg)
{
    /* 根据传入的参数设置GPIO */
    /* cmd : 0-1, which led  */
    /* arg : 0-off, 1-on*/
    
    if(cmd == 0)
    {
        //if(led1_gpio != INVALID_GPIO)
            gpio_set_value(led1_gpio, arg);
			printk("cmd == 0 carroll led_ioctl: %d \n", arg);
    }
    else if(cmd == 1)
    {
        //if(led2_gpio != INVALID_GPIO)
            //gpio_set_value(led2_gpio, !cmd);
		printk("cmd == 1 carroll led_ioctl: %d \n", arg);
    }

	
    
    printk("carroll led_ioctl: cmd:%d arg:%d\n", cmd, arg);

    return 0;
}

static struct file_operations led_fops = {
        .owner   = THIS_MODULE,
        .open    = led_open,
        .release    = led_release,
        .unlocked_ioctl   = led_ioctl,
};

static struct miscdevice led_dev =
{
    .minor = MISC_DYNAMIC_MINOR,
    //.name = "test_led",
    .name = "leds_drv",
    .fops = &led_fops,
};

static int rongpin_led_probe(struct platform_device *pdev)
{
    int ret = -1;
    enum of_gpio_flags flags;
    struct device_node *hello_node = pdev->dev.of_node;

    printk("%s-%d: carroll \n",__FUNCTION__,__LINE__);

    /* register test_led dev file */
    ret = misc_register(&led_dev);
    if (ret < 0){
            printk("carroll led register err!\n");
            return ret;
    }

    /* led1 init */
    led1_gpio = of_get_named_gpio_flags(hello_node,"led1", 0,&flags);
    if (!gpio_is_valid(led1_gpio)){
        printk("carroll: invalid gpio : %d\n",led1_gpio);
        return -1;
    } 
    ret = gpio_request(led1_gpio, "rongpin_led");
    if (ret != 0) {
        gpio_free(led1_gpio);
        printk("carroll: led1_gpio free\n");
        return -EIO;
    }
    gpio_direction_output(led1_gpio, !led_gpio_active);

#if 0
    /* led2 init */
    led2_gpio = of_get_named_gpio_flags(hello_node,"led2", 0,&flags);
    if (!gpio_is_valid(led2_gpio)){
        printk("carroll: invalid gpio : %d\n",led2_gpio);
        return -1;
    } 
    ret = gpio_request(led2_gpio, "test_led");
    if (ret != 0) {
        gpio_free(led2_gpio);
        printk("carroll: led2_gpio free\n");
        return -EIO;
    }
    gpio_direction_output(led2_gpio, !led_gpio_active);
#endif

    gpio_set_value(led1_gpio, !led_gpio_active);
    //gpio_set_value(led2_gpio, led_gpio_active);
    //mdelay(500);
    //gpio_set_value(led1_gpio, !led_gpio_active);
    //gpio_set_value(led2_gpio, !led_gpio_active);

    return 0;  //return Ok
}

static int rongpin_led_remove(struct platform_device *pdev)
{ 
    return 0;
}

#ifdef CONFIG_OF
static const struct of_device_id of_rongpin_led_match[] = {
    { .compatible = "rongpin,rongpin_led" },
    { /* Sentinel */ }
};
#endif

static struct platform_driver rongpin_led_driver = {
    .probe        = rongpin_led_probe,
    .remove        = rongpin_led_remove,
    .driver        = {
        .name    = "rongpin_led",
        .owner    = THIS_MODULE,
#ifdef CONFIG_OF
        .of_match_table    = of_rongpin_led_match,
#endif
    },

};

static int major;
static struct class *cls;

static int __init led_init(void)
{
    printk(KERN_INFO "carroll register led dev %s\n", __FUNCTION__);

    return platform_driver_register(&rongpin_led_driver);
}

static void __exit led_exit(void)
{
    platform_driver_unregister(&rongpin_led_driver);
    printk(KERN_INFO "carroll unregister led dev %s\n", __FUNCTION__);
}

subsys_initcall(led_init);
module_exit(led_exit);

MODULE_AUTHOR("carroll <1247627668@qq.com>");
MODULE_DESCRIPTION("carroll led driver");
MODULE_LICENSE("GPL");


这里我一直想单独编译kernel,一直编译不到,只能全编

C库修改

在上一小节中,向需虚拟机注册的C函数,只做了简单的打印信息,现在把hardcontrol.c代码补全如下:

#include <jni.h>  /* /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/ */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include <android/log.h>  /* liblog */
 
#if 0
typedef struct {
    char *name;          /* Java閲岃皟鐢ㄧ殑鍑芥暟鍚?*/
    char *signature;    /* JNI瀛楁鎻忚堪绗? 鐢ㄦ潵琛ㄧずJava閲岃皟鐢ㄧ殑鍑芥暟鐨勫弬鏁板拰杩斿洖鍊肩被鍨?*/
    void *fnPtr;          /* C璇█瀹炵幇鐨勬湰鍦板嚱鏁?*/
} JNINativeMethod;
#endif

static jint fd;


jint ledOpen(JNIEnv *env, jobject cls)
{
	fd = open("/dev/leds_drv", O_RDWR);
	__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledOpen");
	if (fd >= 0)
		return 0;
	else
		return -1;
}

void ledClose(JNIEnv *env, jobject cls)
{
	__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledClose");
	close(fd);
}

jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
	
	int ret = ioctl(fd, which, status);
	__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledCtrl : %d, %d, %d", which, status, ret);
	return ret;
}


static const JNINativeMethod methods[] = {
	{"ledOpen", "()I", (void *)ledOpen},
	{"ledClose", "()V", (void *)ledClose},
	{"ledCtrl", "(II)I", (void *)ledCtrl},
};




/* System.loadLibrary */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;

	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}
	cls = (*env)->FindClass(env, "com/example/hardlibrary/HardControl");
	if (cls == NULL) {
		return JNI_ERR;
	}

	/* 2. map java hello <-->c c_hello */
	if ((*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])) < 0)
		return JNI_ERR;

	return JNI_VERSION_1_4;
}

MainActivity.java

package com.example.a3288_led_demo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Toast;

import com.example.hardlibrary.HardControl;

public class MainActivity extends AppCompatActivity {

    //创建一个button对象
    private Button button = null;
    //定义两个变量
    private CheckBox checkBoxLed1 = null;
    private CheckBox checkBoxLed2 = null;

    private boolean ledon = false;

    class MyButtonListener implements View.OnClickListener {
        @Override
        public void onClick(View view) {

            //创建一个HardControl类
            HardControl hardControl = new HardControl();
            ledon = !ledon;
            if (ledon) {
                button.setText("ALL OFF");
                checkBoxLed1.setChecked(true);
                checkBoxLed2.setChecked(true);
                for (int i = 0; i < 2; i++)
                    HardControl.ledCtrl(i, 0);
            }
            else {
                button.setText("ALL ON");
                checkBoxLed1.setChecked(false);
                checkBoxLed2.setChecked(false);
                for (int i = 0; i < 2; i++)
                    HardControl.ledCtrl(i, 1);
            }
        }

    }

    //点击Checkbox时,会执行函数onCheckboxClicked
    public void onCheckboxClicked(View view) {
        boolean checked = ((CheckBox) view).isChecked();
        switch(view.getId()) {
            case R.id.LED1:
                if (checked) {
                    //提示框的作用,当我们点击Checkbox,浮现出当前LED的状态
                    Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(0, 0);
                }
                else {
                    Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(0, 1);
                }
                break;
            case R.id.LED2:
                if (checked) {
                    Toast.makeText(getApplicationContext(), "LED2 on", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(1, 0);
                }
                else {
                    Toast.makeText(getApplicationContext(), "LED2 off", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(1, 1);
                }
                break;
        }
    }

    //这个方法会在在APP启动的时候就会被调用
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        HardControl.ledOpen();
        button = (Button)findViewById(R.id.BUTTON);
        //用findViewById得到实例化对象
        checkBoxLed1 = (CheckBox)findViewById(R.id.LED1);
        checkBoxLed2 = (CheckBox)findViewById(R.id.LED2);

        //绑定其点击执行函数
        button.setOnClickListener(new MyButtonListener());
    }
}

 

 

实验现象


编译hardcontrol.c文件生成动态C库,加载到我们的APP工程中(加载过程和位置可以参考前面的博文),运行APP之前,我们先查看开发板/dev/leds_drv是否存在,然后通过chmod命令赋予其权限777,然后执行APP程序,通过点击button和checkbox可以控制LED的状态

章节结语
该章节到此结束,从andriod源码的编译,到APP界面程序的设计,以及JNI的简单使用已经讲解完成。现在我们是通过JNI直接访问硬件的,这样是十分不安全的,比如,如果多个APP同时访问LED将导致实际现象和预计现象的不一样。在andriod系统中,存在专门管理硬件访问的服务框架,下一章节将对其进行解刨。
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值