Android应用程序访问硬件驱动(JNI方式)

Android应用程序访问硬件驱动(JNI方式)

Java应用程序通过JNI方式访问C库,C库向上给Java程序提供接口,向下调用了硬件驱动,这里以LED的控制为例。

一、LED驱动

leds_4412.c是tiny4412开发板一个简单的LED驱动

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

static int led_gpios[] = {
    EXYNOS4212_GPM4(0),
    EXYNOS4212_GPM4(1),
    EXYNOS4212_GPM4(2),
    EXYNOS4212_GPM4(3),
};

static int led_open(struct inode *inode, struct file *file)
{
    /* 配置GPIO为输出引脚 */
    int i;
    for (i = 0; i < 4; i++)
        s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);

    return 0;
}

/* app : ioctl(fd, cmd, arg) */
static long led_ioctl(struct file *filp, unsigned int cmd,
        unsigned long arg)
{
    /* 根据传入的参数设置GPIO */
    /* cmd : 0-off, 1-on */
    /* arg : 0-3, which led */

    if ((cmd != 0) && (cmd != 1))
        return -EINVAL;

    if (arg > 4)
        return -EINVAL;

    gpio_set_value(led_gpios[arg], !cmd);

    return 0;
}

static struct file_operations leds_ops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   led_open,     
    .unlocked_ioctl = led_ioctl,

};

static int major;
static struct class *cls;

int leds_init(void)
{
    major = register_chrdev(0, "leds", &leds_ops);

    /* 为了让系统udev,mdev给我们创建设备节点 */
    /* 创建类, 在类下创建设备 : /sys */
    cls = class_create(THIS_MODULE, "leds");
    device_create(cls, NULL, MKDEV(major, 0), NULL, "leds"); /* /dev/leds */

    return 0;
}

void leds_exit(void)
{
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    unregister_chrdev(major, "leds");
}

module_init(leds_init);
module_exit(leds_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xy");

将驱动程序放入内核编译:
a. 放入Linux内核 drivers/char中
b. 修改 drivers/char/Makefile, 添加:

obj-y += leds_4412.o

为避免干扰,将自带的 leds 驱动屏蔽:

config TINY4412_LEDS
    tristate "LED Support for FriendlyARM Tiny4412 GPIO LEDs"
    depends on MACH_TINY4412
    default n

c. 重新编译内核: make zImage
d. 重新烧录zImage,系统起来后会看到/dev/leds设备。

二、JNI文件(C库)

hardcontrl.c用于实现后面的HardContrl.java里面的native方法对应的C函数,C函数分别对应了驱动里面各个接口。

JNI_OnLoad在java程序执行System.loadLibrary的时候被调用,作用:
1. 通过GetEnv获取运行环境
2. 通过FindClass(env, “com/thisway/hardlibrary/HardControl”)找到HardControl这个Java类。
3. 通过RegisterNatives注册下面三个native方法

/* Java方法名,JNI字段描述符,C语言函数名 */
static const JNINativeMethod methods[] = {
    {"ledOpen", "()I", (void *)ledOpen},
    {"ledClose", "()V", (void *)ledClose},
    {"ledCtrl", "(II)I", (void *)ledCtrl},
};

编译命令:

arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so 
//编译共享动态库so文件
-I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/  
//指定jni.h文件路径;
-nostdlib /work/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so
// 编译时不会自动使用标准的libc.so.6
-I /work/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include 
//指定log.h文件路径
/work/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/liblog.so
//__android_log_print依赖的库

hardcontrol.c完整代码:

#include   /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#include 
#include 
#include 
#include 
#include 
#include 

#include   /* liblog */

//__android_log_print(ANDROID_LOG_DEBUG, "JNIDemo", "native add ...");


#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", O_RDWR);
    __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledOpen : %d", fd);
    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, status, which);
    __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledCtrl : %d, %d, %d", which, status, ret);
    return ret;
}

/* Java方法名,JNI字段描述符,C语言函数名 */
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/thisway/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;
}

三、HardControl文件(java类)

用于声明native方法,让其他应用程序的代码调用。

package com.thisway.hardlibrary;

public class HardControl {
    public static native int ledCtrl(int which, int status);
    public static native int ledOpen();
    public static native void ledClose();

    static {
        try {
            System.loadLibrary("hardcontrol");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

当应用程序使用到该HardControl 类的时候,会先通过System.loadLibrary 加载hardcontrol.so库文件;

hardcontrol.c编译生成的hardcontrol.so必须放在应用程序的app/libs/armeabi目录下;

Android studio应用程序的build.gradle还要添加:

sourceSets {  
        main {  
            jniLibs.srcDirs = ['libs']  
        }  
    }  

四、应用程序调用HardControl类方法

  1. 通过 import com.thisway.hardlibrary.*; 导入HardControl类;
  2. 在MainActivity的onCreate方法中open LED设备:HardControl.ledOpen();
  3. 在逻辑判断中操作LED:HardControl.ledCtrl(which, status);
package com.thisway.app_0001_leddemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.view.View;
import android.widget.CheckBox;
import android.widget.Toast;
import com.thisway.hardlibrary.*;

public class MainActivity extends AppCompatActivity {

    private boolean ledon = false;
    private Button button = null;
    private CheckBox checkBoxLed1 = null;
    private CheckBox checkBoxLed2 = null;
    private CheckBox checkBoxLed3 = null;
    private CheckBox checkBoxLed4 = null;

    class MyButtonListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            ledon = !ledon;
            if (ledon) {
                button.setText("ALL OFF");
                checkBoxLed1.setChecked(true);
                checkBoxLed2.setChecked(true);
                checkBoxLed3.setChecked(true);
                checkBoxLed4.setChecked(true);

                for (int i = 0; i < 4; i++)
                    HardControl.ledCtrl(i, 1);
            }
            else {
                button.setText("ALL ON");
                checkBoxLed1.setChecked(false);
                checkBoxLed2.setChecked(false);
                checkBoxLed3.setChecked(false);
                checkBoxLed4.setChecked(false);

                for (int i = 0; i < 4; i++)
                    HardControl.ledCtrl(i, 0);
            }
        }
    }

    public void onCheckboxClicked(View view) {
        // Is the view now checked?
        boolean checked = ((CheckBox) view).isChecked();

        // Check which checkbox was clicked
        switch(view.getId()) {
            case R.id.LED1:
                if (checked) {
                    // Put some meat on the sandwich
                    Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(0, 1);
                }
                else {
                    // Remove the meat
                    Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(0, 0);
                }
                break;
            case R.id.LED2:
                if (checked) {
                    // Put some meat on the sandwich
                    Toast.makeText(getApplicationContext(), "LED2 on", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(1, 1);
                }
                else {
                    // Remove the meat
                    Toast.makeText(getApplicationContext(), "LED2 off", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(1, 0);
                }
                break;

            case R.id.LED3:
                if (checked) {
                    // Put some meat on the sandwich
                    Toast.makeText(getApplicationContext(), "LED3 on", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(2, 1);
                }
                else {
                    // Remove the meat
                    Toast.makeText(getApplicationContext(), "LED3 off", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(2, 0);
                }
                break;

            case R.id.LED4:
                if (checked) {
                    // Put some meat on the sandwich
                    Toast.makeText(getApplicationContext(), "LED4 on", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(3, 1);
                }
                else {
                    // Remove the meat
                    Toast.makeText(getApplicationContext(), "LED4 off", Toast.LENGTH_SHORT).show();
                    HardControl.ledCtrl(3, 0);
                }
                break;
            // TODO: Veggie sandwich
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = (Button) findViewById(R.id.BUTTON);

        HardControl.ledOpen();

        checkBoxLed1 = (CheckBox) findViewById(R.id.LED1);
        checkBoxLed2 = (CheckBox) findViewById(R.id.LED2);
        checkBoxLed3 = (CheckBox) findViewById(R.id.LED3);
        checkBoxLed4 = (CheckBox) findViewById(R.id.LED4);

        button.setOnClickListener(new MyButtonListener());
/*
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // Perform action on click
                ledon = !ledon;
                if (ledon)
                    button.setText("ALL OFF");
                else
                    button.setText("ALL ON");
            }
        });
*/
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

你可能感兴趣的:(Android系统,android系统)