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

作者:庄泽彬

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

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

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

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

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"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.administrator.myled.MainActivity"> <RelativeLayout android:layout_width="394dp" android:layout_height="520dp" tools:layout_editor_absoluteX="-5dp" tools:layout_editor_absoluteY="-10dp"> <Button android:id="@ id/btn_led_on" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:layout_marginStart="56dp" android:layout_marginTop="109dp" android:text="拉灯" /> <Button android:id="@ id/btn_led_off" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@ id/btn_led_on" android:layout_alignBottom="@ id/btn_led_on" android:layout_marginStart="81dp" android:layout_toEndOf="@ id/btn_led_on" android:text="灭灯" /> </RelativeLayout> </android.support.constraint.ConstraintLayout>

JNI的文件:

led_jni.cpp

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #define LOG_TAG "zbzhuang" #include <utils/Log.h> #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\n",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\n",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\n",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:--------------^_&--------------------\n",__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\n",__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)(2)

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

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

/*1. 头文件*/ #include<linux/init.h> #include<linux/module.h> #include<linux/fs.h> #include<linux/device.h> #include<linux/slab.h> #include<linux/cdev.h> #include<asm/uaccess.h> #include<asm/io.h> 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!\n"); return ret; } printk(KERN_INFO "zbzhuang### val=%d\n", 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!\n"); return ret; } if(s5pv_led_dev->val) { /*点亮LED*/ //*gpc0dat |= ((0x1<<3)|(0x1<<4)); printk(KERN_ERR "zbzhuang### led on\n"); } else { /*熄灭LED*/ // *gpc0dat &= ~((0x1<<3)|(0x1<<4)); printk(KERN_ERR "zbzhuang### led off\n"); } 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-------------\n",__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-----------------\n",__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!\n"); 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\n"); 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\n"); 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(Android字符设备驱动开发基于高通msm8916)(4)

kconfig:

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

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

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

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

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

android studio查看log的结果:

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

adb查看log的结果:

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

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

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

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

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

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

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页