Android字符设备驱动开发基于高通msm8916

作者:庄泽彬

前言:写一篇文档总结自己在msm8916平台上移植自己编写的简单的字符设备驱动开发的整个流程。这个小项目的主要功能是开发一个简单的APP,APP通过JNI去调用位于kernel的字符设备驱动。

一、APP的设计,开发平台Android Studio

主要的文件是下面的三个文件:

Android字符设备驱动开发基于高通msm8916

MainActivity.java文件的内容如下:

package com.example.administrator.myled;

import android.nfc.Tag;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;


import com.zbahuang.led.lowlevel.LedNative;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private final static String TAG = "zbzhuang";
    Button btn_led_on;
    Button btn_led_off;
    LedNative myled;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initUI();

        myled = new LedNative();
        myled.openDev();
        Log.d(TAG,"app:open Dev");
    }

    private void initUI() {
        btn_led_on = (Button) findViewById(R.id.btn_led_on);
        btn_led_on.setOnClickListener(this);

        btn_led_off = (Button) findViewById(R.id.btn_led_off);
        btn_led_off.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_led_on:
                Toast.makeText(MainActivity.this,"拉灯-->",Toast.LENGTH_SHORT).show();
                Log.d(TAG,"app:LED on");
                myled.devOn();
                break;
            case R.id.btn_led_off:
                Toast.makeText(MainActivity.this,"灭灯-->",Toast.LENGTH_SHORT).show();
                Log.d(TAG,"app:LED off");
                myled.devOff();
                break;
            default:
                break;
        }

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        myled.closeDev();
        Log.d(TAG,"app:close Dev");
    }
}

LedNative.java文件的内容如下:

在这个文件中所声明的方法是在jni中实现的啊。使用了特殊的关键字表示该方法是在JNI当中实现的啊。

package com.zbahuang.led.lowlevel;

/**
 * Created by Administrator on 2017/3/29 0029.
 */

public class LedNative {

    static {
        System.loadLibrary("led_jni");
    }

    public native int openDev();
    public native int devOn();
    public native int devOff();
    public native int closeDev();
}
package com.zbahuang.led.lowlevel;

/**
 * Created by Administrator on 2017/3/29 0029.
 */

public class LedNative {

    static {
        System.loadLibrary("led_jni");
    }

    public native int openDev();
    public native int devOn();
    public native int devOff();
    public native int closeDev();
}

activity_main.xml文件的内容如下:

<?xml version="1.0" encoding="utf-8"?>



    

        

JNI的文件:

led_jni.cpp

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


#define LOG_TAG "zbzhuang"
#include 

#include "jni.h"




static jint fd;


static jint open_led(JNIEnv *env,jobject thiz)
{
    ALOGD("JNI:-----------%s--------------",__FUNCTION__);

    fd = open("/dev/led1",O_RDWR);
    if(fd < 0){
        ALOGD("JNI:open error:%s
",strerror(errno));
        return -1;
    }

    return 0;
}


static jint led_on(JNIEnv *env,jobject thiz)
{
    ALOGD("JNI:-----------%s--------------",__FUNCTION__);
    jint ret ;
    jint on = 1;

    ret = write(fd,&on,4);
    if(ret < 0){
        ALOGD("JNI:write off error:%s
",strerror(errno));
        return -1;
    }


    return 0;
}


static jint led_off(JNIEnv *env,jobject thiz)
{
    ALOGD("JNI:-----------%s--------------",__FUNCTION__);
    jint ret;
    jint off = 0;

    ret = write(fd,&off,4);
    if(ret < 0){
        ALOGD("JNI:write off error:%s
",strerror(errno));
        return -1;
    }

    return 0;
}


static jint close_led(JNIEnv *env,jobject thiz)
{
    ALOGD("JNI:-----------%s--------------",__FUNCTION__);
    close(fd);

    return 0;
}





const JNINativeMethod  led_jni_methods[] = {
    {"openDev","()I",(void *)open_led},
    {"devOn","()I",(void *)led_on},
    {"devOff","()I",(void *)led_off},
    {"closeDev","()I",(void *)close_led},


};







jint JNI_OnLoad(JavaVM * vm,void * reserved)
{
    JNIEnv *env = NULL;
    jint ret ;



    ALOGD("%s[%s:%d]JNI:--------------^_&--------------------
",__func__,__FILE__,__LINE__);
    ret = vm->GetEnv((void * *)&env,JNI_VERSION_1_4);
    if(ret != JNI_OK){
        ALOGE("JNI:vm->GetEnv error");
        return -1;
    }

    jclass clz = env->FindClass("com/zbahuang/led/lowlevel/LedNative");

    if(clz == NULL){
        ALOGE("%s[%s:%d]JNI:env->FindClass error",__func__,__FILE__,__LINE__);
        return -1;
    }

    ret = env->RegisterNatives(clz,
                led_jni_methods,
                sizeof(led_jni_methods)/sizeof(led_jni_methods[0]));

    if(ret < 0){
        ALOGE("%s[%s:%d]JNI:env->RegisterNatives error
",__func__,__FILE__,__LINE__);
        return -1;
    }



    return JNI_VERSION_1_4;



}

Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE:= libled_jni

LOCAL_SRC_FILES:= \
  led_jni.cpp

LOCAL_SHARED_LIBRARIES := \
    libutils liblog

LOCAL_C_INCLUDES += \
    $(JNI_H_INCLUDE)

include $(BUILD_SHARED_LIBRARY)

执行: mmm mytest/led_jni/ 之后会生成动态库放在 out/target/product/msm8916_64/obj/lib/libled_jni.so

将这个库推送到平板电脑就可以通过这个库去调用驱动。

Android字符设备驱动开发基于高通msm8916

Android字符设备驱动开发基于高通msm8916

内核的驱动文件:kernel/drivers/input/misc/led.c

/*1. 头文件*/
#include
#include
#include
#include
#include
#include
#include
#include

static unsigned int led_major=0;
volatile unsigned long *gpc0con = NULL;
volatile unsigned long *gpc0dat = NULL;

struct led_device{
    struct class *led_class ;    //表示一类设备, 存储某些信息
    struct device *led_device ;    //表示一个设备
    struct cdev  *led_cdev;
    unsigned int val;

};

struct led_device *s5pv_led_dev;


/*可用于查询LED的状态*/
static ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *opps)
{
    int ret;

    ret = copy_to_user(buf, &s5pv_led_dev->val, count);
    if(ret>0)
    {
        printk(KERN_ERR "zbzhuang### copy to user failed!
");
        return ret;
    }

    printk(KERN_INFO "zbzhuang### val=%d
", s5pv_led_dev->val);

    return ret?0:count;
}
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *opps)
{
    int ret;

    /*拷贝成功,返回0; 拷贝失败, 返回没有被拷贝的字节数*/
    ret = copy_from_user(&s5pv_led_dev->val, buf, count);
    if(ret>0)
    {
        printk(KERN_ERR "zbzhuang### copy from user failed!
");
        return ret;
    }

    if(s5pv_led_dev->val)
    {
        /*点亮LED*/
        //*gpc0dat |= ((0x1<<3)|(0x1<<4));
        printk(KERN_ERR "zbzhuang### led on
");
    }
    else
    {
        /*熄灭LED*/
//        *gpc0dat &= ~((0x1<<3)|(0x1<<4));
        printk(KERN_ERR "zbzhuang### led off
");
    }

    return ret?0:count;
}



static int led_open(struct inode *inode, struct file *file)
{
#if 0
    /*1. 将物理地址映射为虚拟地址*/
    gpc0con = ioremap(0xE0200060, 8);
    gpc0dat = gpc0con +1;

    /*2. 初始化GPC0_3,4引脚功能为输出*/
    *gpc0con &= ~((0xf<<12)|(0xf<<16));
    *gpc0con |=  ((0x1<<12)|(0x1<<16));
#endif
    printk(KERN_ERR "zbzhuang### -----------%s-------------
",__FUNCTION__);

    return 0;
}

static int led_close(struct inode *inode, struct file *file)
{
#if 0

    *gpc0con &= ~((0xf<<12)|(0xf<<16));
    iounmap(gpc0con);
#endif
    printk(KERN_ERR "zbzhuang### ------------%s-----------------
",__FUNCTION__);

    return 0;

}

/*硬件操作方法*/
struct file_operations led_fops={
    .owner = THIS_MODULE,    //当前模块所用
    .open  = led_open,
    .write = led_write,
    .read  = led_read,
    .release = led_close,

};

static void setup_led_cdev(void)
{
    /*1. 为cdev结构体分配空间*/
    s5pv_led_dev->led_cdev = cdev_alloc();

    /*2. 初始化cdev结构体*/
    cdev_init(s5pv_led_dev->led_cdev, &led_fops);

    /*3. 注册cdev,加载到内核哈希表中*/
    cdev_add(s5pv_led_dev->led_cdev, MKDEV(led_major, 0), 1);

}

/*2. 实现模块加载函数*/
static int __init led_init(void)
{
    dev_t devno;
    int ret;
    /*1. 新的申请主设备号的方法*/
    if(led_major)
    {
        /*静态申请*/
        devno = MKDEV(led_major, 0);
        register_chrdev_region(devno, 1, "led");
    }
    else
    {
        /*动态申请*/
        alloc_chrdev_region(&devno, 0, 1, "led");
        led_major = MAJOR(devno);
    }

    /*2. 为本地结构体分配空间*/

        /*
        **    param1: 大小
        **    param2:    标号: GFP_KERNEL--->表示如果分配不成功,则休眠
        */
    s5pv_led_dev = kmalloc(sizeof(struct led_device), GFP_KERNEL);
    if (!s5pv_led_dev)
    {
        printk(KERN_ERR "zbzhuang NO memory for malloc!
");
        ret = -ENOMEM;
        goto out_err_1;
    }

    /*3. 构建struct cdev结构体*/
    setup_led_cdev();


    /*4. 创建设备文件*/
    /*
    **    param1:    struct class
    **    param2:    父类, 一般为NULL
    **    param3:    dev_t ---> 表示一个设备号, 是一个无符号32位整形
    **                        其中高12位为主设备号, 低20为次设备号
    **            如何操作设备号: MKDEV(major, minor)根据主设备号和次设备号组成一个设备号
    */

    s5pv_led_dev->led_class = class_create(THIS_MODULE, "led_class");
    if (IS_ERR(s5pv_led_dev->led_class)) {
        printk(KERN_ERR "zbzhuang class_create() failed for led_class
");
        ret = -EINVAL;
        goto out_err_2;
    }


    s5pv_led_dev->led_device = device_create(s5pv_led_dev->led_class, NULL, MKDEV(led_major, 0), NULL, "led1");    // 创建设备文件/dev/led1
    if (IS_ERR(s5pv_led_dev->led_device)) {
        printk(KERN_ERR "zbzhuang device_create failed for led_device
");
        ret = -ENODEV;
        goto out_err_3;
    }

    return 0;

out_err_3:
    class_destroy(s5pv_led_dev->led_class);

out_err_2:
    cdev_del(s5pv_led_dev->led_cdev);
    kfree(s5pv_led_dev);

out_err_1:
    unregister_chrdev_region(MKDEV(led_major, 0), 1);
    return ret;

}

/*3. 实现模块卸载函数*/
static void __exit led_exit(void)
{
    unregister_chrdev_region(MKDEV(led_major, 0), 1);
    device_destroy(s5pv_led_dev->led_class, MKDEV(led_major, 0));
    class_destroy(s5pv_led_dev->led_class);
    cdev_del(s5pv_led_dev->led_cdev);
    kfree(s5pv_led_dev);
}

/*4. 模块许可声明*/
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

修改kconfig和makefile,在内核当中添加:

makefile

Android字符设备驱动开发基于高通msm8916

kconfig:

Android字符设备驱动开发基于高通msm8916

修改这个文件将驱动编译进内核:kernel/arch/arm64/configs/msm_defconfig 与msm-perf_deconfig

Android字符设备驱动开发基于高通msm8916

之后使用adb工具将重新编译的boot.imge镜像重新烧录就可以了啊。

看看log的输出验证一下结果:

android studio查看log的结果:

Android字符设备驱动开发基于高通msm8916

adb查看log的结果:

Android字符设备驱动开发基于高通msm8916

Android字符设备驱动开发基于高通msm8916

从app到jni到kernel,整个调用的过程成功。

如果出现打开设备失败的话在system/core/rootdir/uevent.rc中添加设备节点的权限777即可。

Android字符设备驱动开发基于高通msm8916

觉得不错,就给我点小支持吧

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章