Android应用通过JNI调用ioctl操作设备驱动

目录

前提背景:

NDK环境搭建和Demo代码实现

环境搭建

下载NDK包和配置环境变量

代码实现

创建Android.mk, Application.mk, touch.cpp这3个文件

 应用层app demo实现

 使用android studio创建默认工程

 引用libtouch.so库文件

 编译运行安装touch应用

设备驱动程序ioctl实现关键代码


前提背景:

1. 底层设备驱动已经实现ioctl相关操作接口

2. 本篇主要讲如何实现JNI层和应用层的ioctl相关内容

3. 这里是基于sda810开发板调试,其他arm开发板搭载安卓系统均可以调试

NDK环境搭建和Demo代码实现

环境搭建

下载NDK包和配置环境变量

 1.到官网下载android-ndk-r13b-windows-x86_64.zip(我用的是这个版本,最新版本

 2.解压缩后将目录添加到PATH中,我这里是d:\android-ndk-r13b\

 3.测试如下图,表示配置成功

代码实现

创建Android.mk, Application.mk, touch.cpp这3个文件

Note:这3个文件放在同一目录,我这里放在jni目录

1. Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS :=-llog
LOCAL_MODULE    := touch
LOCAL_SRC_FILES := touch.cpp
include $(BUILD_SHARED_LIBRARY)

2. Application.mk

APP_ABI := all #编译所有平台

3. touch.cpp (文件名和Android.mk 的 MODULE匹配, JNI里面的语法知识不做介绍)

#include<android/log.h>
#include <jni.h>
#include <fcntl.h>

#define TAG "TOUCH-JNI" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)

#define IOCTL_GET_DRIVER_VERSION   0x80084300
#define IOCTL_GET_DEVICE_TYPE      0x80084301

extern "C"
{
    jstring Java_com_morgen_touch_MainActivity_getStringFromJni(JNIEnv* jni, jobject obj) {
        return jni->NewStringUTF("Hello jni!");
    }

    jlong Java_com_morgen_touch_MainActivity_ioctl(JNIEnv* jni, jobject obj, jint code, jlong arg) {
        char path[30] = "/proc/icn85xx_tool";
        int fd = 0;

        LOGE("code:0x%x arg:0x%x", code, arg);

        fd = open(path, O_RDWR);
        if (fd < 0) {
            LOGE("Open file failed");
            return -1;
        }

        switch (code) {
        case IOCTL_GET_DRIVER_VERSION:
        case IOCTL_GET_DEVICE_TYPE:
            ioctl(fd, code, &arg);//注意这里是核心代码
            LOGE("arg:0x%x", arg);
            break;
        default:
            LOGE("CTLCODE was not right");
        }

        close(fd);
        LOGE("ioctl successfully");
        return arg;
    }
}//extern "C"

4.到这里基本的jni层相关代码都配置OK了,接下来编译

        a.打开命令窗口进入jni目录,输入ndk-build,

        b.如下图,可以看到相关平台的so文件都已经编译成功

        c.编译好了之后so文件在jni同级目录的libs目录下  (libtouch.so以生成)

        d.到这里就为应用层app调用jni so库文件做好了准备

 应用层app demo实现

 使用android studio创建默认工程

 1.创建好的工程目录如下(怎么创建android demo工程这里就不赘述了,只需要创建一个空壳应用就好):

 2.将ndk编译好的libs目录拷贝到touch工程中的main目录下,如上图

 引用libtouch.so库文件

 1.在app工程目录下的build.gradle文件中添加 ndk和sourceSets.main这两个代码段

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"
    defaultConfig {
        applicationId "com.morgen.touch"
        minSdkVersion 15
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        ndk {                               //1.添加ndk模块名
            moduleName "touch"
        }
    }
    sourceSets.main{                        //2.添加jni lib目录
        jni.srcDirs=[]
        jniLibs.srcDir "src/main/libs"
    }
}

 3.然后在MainActivity中载入touch模块

 4.然后在onCreate中调用

 5.MainActivity完整代码如下(到这里,引用和调用都以完成):

package com.morgen.touch;

import android.os.Bundle;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.annotation.NonNull;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.File;

public class MainActivity extends AppCompatActivity {
    private TextView mTextMessage;
    private Button mButton;

    static {
        System.loadLibrary("touch");
    }
    public native long ioctl(int a, long b);

    private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
            = new BottomNavigationView.OnNavigationItemSelectedListener() {

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            switch (item.getItemId()) {
                case R.id.navigation_home:
                    mTextMessage.setText(R.string.title_home);
                    return true;
                case R.id.navigation_dashboard:
                    mTextMessage.setText(R.string.title_dashboard);
                    return true;
                case R.id.navigation_notifications:
                    mTextMessage.setText(R.string.title_notifications);
                    return true;
            }
            return false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        BottomNavigationView navView = findViewById(R.id.nav_view);
        mTextMessage = findViewById(R.id.message);
        navView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
        mButton = findViewById(R.id.button);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                long arg = 0;
                Log.e("TOUCH-APP", "0x" + Long.toHexString(ioctl(0x80084300, arg)));
                Log.e("TOUCH-APP", "0x" + Long.toHexString(ioctl(0x80084301, arg)));
                Log.e("TOUCH-APP", "0x" + Long.toHexString(ioctl(0x80084302, arg)));
                Log.e("TOUCH-APP", "0x" + Long.toHexString(ioctl(0x80084303, arg)));
                Log.e("TOUCH-APP", "0x" + Long.toHexString(ioctl(0x80084304, arg)));
                Log.e("TOUCH-APP", "0x" + Long.toHexString(ioctl(0xc0084310, arg)));
                Log.e("TOUCH-APP", "0x" + Long.toHexString(ioctl(0xc0084320, arg)));
                Log.e("TOUCH-APP", "0x" + Long.toHexString(ioctl(0xc0084321, arg)));
            }
        });
    }
}

 编译运行安装touch应用

  1.安装后的应用

  2.点击按钮运行,同时抓取logcat log(如下图),这里可以看到app层和jni层的log都有打印出来,但是有没有被底层识别呢,接下来我们再看底层设备驱动的简单实现

130|root@msm8994:/ # logcat | grep TOUCH
E/TOUCH-JNI(14217): code:0x80084300 arg:0x0
E/TOUCH-JNI(14217): arg:0x10310
E/TOUCH-JNI(14217): ioctl successfully
E/TOUCH-APP(14217): 0x10310
E/TOUCH-JNI(14217): code:0x80084301 arg:0x0
E/TOUCH-JNI(14217): arg:0x991110
E/TOUCH-JNI(14217): ioctl successfully
E/TOUCH-APP(14217): 0x991110
E/TOUCH-JNI(14217): code:0x80084302 arg:0x0
E/TOUCH-JNI(14217): arg:0x212
E/TOUCH-JNI(14217): ioctl successfully
E/TOUCH-APP(14217): 0x212
E/TOUCH-JNI(14217): code:0x80084303 arg:0x0
E/TOUCH-JNI(14217): arg:0x61702cf
E/TOUCH-JNI(14217): ioctl successfully
E/TOUCH-APP(14217): 0x61702cf
E/TOUCH-JNI(14217): code:0x80084304 arg:0x0
E/TOUCH-JNI(14217): arg:0x120020
E/TOUCH-JNI(14217): ioctl successfully
E/TOUCH-APP(14217): 0x120020
E/TOUCH-JNI(14217): code:0xc0084310 arg:0x0
E/TOUCH-JNI(14217): arg:0x0
E/TOUCH-JNI(14217): ioctl successfully
E/TOUCH-APP(14217): 0x0
E/TOUCH-JNI(14217): code:0xc0084320 arg:0x0
E/TOUCH-JNI(14217): arg:0x0
E/TOUCH-JNI(14217): ioctl successfully
E/TOUCH-APP(14217): 0x0
E/TOUCH-JNI(14217): code:0xc0084321 arg:0x0
E/TOUCH-JNI(14217): arg:0x0
E/TOUCH-JNI(14217): ioctl successfully
E/TOUCH-APP(14217): 0x0

       

设备驱动程序ioctl实现关键代码

1.代码如下:

static long tool_ioctl(struct file *file, unsigned int cmd,
        unsigned long arg)
{
    struct ts_data *data;
    struct device *dev;

    pr_info("ioctl, cmd=0x%08x, arg=0x%08lx", cmd, arg);

    data = file->private_data;
    if (data == NULL) {
        pr_err("IOCTL with private data = NULL");
        return -EFAULT;
    }

    switch (cmd) {
    case IOCTL_GET_DRIVER_VERSION:
        return put_user(CTS_DRIVER_VERSION_CODE,
                (unsigned int __user *)arg);
    case IOCTL_GET_DEVICE_TYPE:
        return put_user(dev->hwdata->hwid,
                (unsigned int __user *)arg);
    }

    return -ENOTSUPP;
}
static int tool_open(struct inode *inode, struct file *file)
{
    file->private_data = PDE_DATA(inode);
    return 0;
}
static struct file_operations tool_fops = {
    .owner = THIS_MODULE,
    .llseek = no_llseek,
    .open  = tool_open,
    .unlocked_ioctl = tool_ioctl,
};

 2.这时候抓取kernel log如下(cat dev/kmsg):

  这里可以看到应用层传下来的cmd,底层设备驱动已经接收到了,这就说明从app-jni-kernel的ioctl功能已经实现

<I>Tool ioctl, cmd=0x80084300, arg=0x7fd4dc4508                                      
<I>Tool ioctl, cmd=0x80084301, arg=0x7fd4dc4508
<I>Tool ioctl, cmd=0x80084302, arg=0x7fd4dc4508
<I>Tool ioctl, cmd=0x80084303, arg=0x7fd4dc4508
<I>Tool ioctl, cmd=0x80084304, arg=0x7fd4dc4508
                                       

======== 完 =========

到这里,从应用层到JNI层再到kernel层,功能实现OK,这就是上层应用来控制底层设备的大概逻辑。

以上,如有错误欢迎大家指出,共同进步,谢谢!

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Android JNI是一种可用于在Java代码中调用C/C++代码的技术,驱动ko则是指内核模块文件,通常用于实现与硬件设备的交互。在Android应用中,我们可以使用JNI调用驱动ko,实现与底层硬件的交互。 要在Android应用调用驱动ko,首先需要在应用中使用JNI技术编写C/C++代码,实现与驱动ko的交互逻辑。在C/C++代码中,我们可以通过使用Linux系统提供的API来访问并且控制驱动ko。通过JNI,我们可以将C/C++代码嵌入到Java代码中,并在Android应用调用这些代码。 具体来说,调用驱动ko的过程如下: 1. 在JNI层编写与驱动ko交互的C/C++代码。这些代码可以使用Linux系统提供的ioctl函数等API来与驱动ko进行通信,并发送相应的指令和数据。 2. 在Java层编写对应的JNI接口和方法。这些方法将与JNI层的C/C++代码进行绑定,以便在Java代码中调用这些方法来间接调用驱动ko。 3. 在Java代码中加载JNI库,并调用JNI接口方法。加载JNI库可以使用System.loadLibrary方法,然后在Java代码中就可以调用JNI接口方法,进而间接调用驱动ko。 通过这种方式,我们可以在Android应用中通过JNI技术调用驱动ko,实现与底层硬件的交互。注意,使用JNI调用驱动ko需要确保设备具备相应的权限,否则可能会出现权限不足的错误。同时,也需要确保驱动ko文件存在于系统中,并且可以被应用访问到。 总之,使用Android JNI可以实现在应用调用驱动ko文件的功能,通过编写C/C++代码并与Java代码进行绑定,实现与底层硬件交互的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值