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

APP的设计,开发平台Android Studio

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

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 < ){
ALOGD("JNI:open error:%s\n",strerror(errno));
return -;
} return ;
} static jint led_on(JNIEnv *env,jobject thiz)
{
ALOGD("JNI:-----------%s--------------",__FUNCTION__);
jint ret ;
jint on = ; ret = write(fd,&on,);
if(ret < ){
ALOGD("JNI:write off error:%s\n",strerror(errno));
return -;
} return ;
} static jint led_off(JNIEnv *env,jobject thiz)
{
ALOGD("JNI:-----------%s--------------",__FUNCTION__);
jint ret;
jint off = ; ret = write(fd,&off,);
if(ret < ){
ALOGD("JNI:write off error:%s\n",strerror(errno));
return -;
} return ;
} static jint close_led(JNIEnv *env,jobject thiz)
{
ALOGD("JNI:-----------%s--------------",__FUNCTION__);
close(fd); return ;
} 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 -;
} jclass clz = env->FindClass("com/zbahuang/led/lowlevel/LedNative"); if(clz == NULL){
ALOGE("%s[%s:%d]JNI:env->FindClass error",__func__,__FILE__,__LINE__);
return -;
} ret = env->RegisterNatives(clz,
led_jni_methods,
sizeof(led_jni_methods)/sizeof(led_jni_methods[])); if(ret < ){
ALOGE("%s[%s:%d]JNI:env->RegisterNatives error\n",__func__,__FILE__,__LINE__);
return -;
} 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

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

内核的驱动文件: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=;
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>)
{
printk(KERN_ERR "zbzhuang### copy to user failed!\n");
return ret;
} printk(KERN_INFO "zbzhuang### val=%d\n", s5pv_led_dev->val); return ret?: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>)
{
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?:count;
} static int led_open(struct inode *inode, struct file *file)
{
#if 0
/*1. 将物理地址映射为虚拟地址*/
gpc0con = ioremap(0xE0200060, );
gpc0dat = gpc0con +; /*2. 初始化GPC0_3,4引脚功能为输出*/
*gpc0con &= ~((0xf<<)|(0xf<<));
*gpc0con |= ((0x1<<)|(0x1<<));
#endif
printk(KERN_ERR "zbzhuang### -----------%s-------------\n",__FUNCTION__); return ;
} static int led_close(struct inode *inode, struct file *file)
{
#if 0 *gpc0con &= ~((0xf<<)|(0xf<<));
iounmap(gpc0con);
#endif
printk(KERN_ERR "zbzhuang### ------------%s-----------------\n",__FUNCTION__); return ; } /*硬件操作方法*/
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, ), ); } /*2. 实现模块加载函数*/
static int __init led_init(void)
{
dev_t devno;
int ret;
/*1. 新的申请主设备号的方法*/
if(led_major)
{
/*静态申请*/
devno = MKDEV(led_major, );
register_chrdev_region(devno, , "led");
}
else
{
/*动态申请*/
alloc_chrdev_region(&devno, , , "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, ), 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 ; 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, ), );
return ret; } /*3. 实现模块卸载函数*/
static void __exit led_exit(void)
{
unregister_chrdev_region(MKDEV(led_major, ), );
device_destroy(s5pv_led_dev->led_class, MKDEV(led_major, ));
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

kconfig:

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

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

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

android studio查看log的结果:

adb查看log的结果:

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

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

Android字符设备驱动开发基于高通msm8916【原创 】的更多相关文章

  1. 【驱动】linux设备驱动·字符设备驱动开发

    Preface 前面对linux设备驱动的相应知识点进行了总结,现在进入实践阶段! <linux设备驱动入门篇>:http://infohacker.blog.51cto.com/6751 ...

  2. arm-linux字符设备驱动开发之---简单字符设备驱动

    一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 1.字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面 ...

  3. Android上HDMI介绍(基于高通平台)

    本文重点针对HDMI在android上的应用,而比较相关的就是overlay机制.overlay在这里只是简单的介绍,后续会有文章再专门详述. 我没记错的话,高通从7X30开始,平台就可以支持HDMI ...

  4. Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动【转】

    本文转载自:https://blog.csdn.net/zqixiao_09/article/details/50858776 版权声明:本文为博主原创文章,未经博主允许不得转载.    https: ...

  5. (57)Linux驱动开发之三Linux字符设备驱动

    1.一般情况下,对每一种设备驱动都会定义一个软件模块,这个工程模块包含.h和.c文件,前者定义该设备驱动的数据结构并声明外部函数,后者进行设备驱动的具体实现. 2.典型的无操作系统下的逻辑开发程序是: ...

  6. Linux字符设备驱动结构(一)--cdev结构体、设备号相关知识机械【转】

    本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50839042 一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符 ...

  7. linux driver ------ 字符设备驱动 之 “ 创建设备节点流程 ”

    在字符设备驱动开发的入门教程中,最常见的就是用device_create()函数来创建设备节点了,但是在之后阅读内核源码的过程中却很少见device_create()的踪影了,取而代之的是device ...

  8. Linux 设备驱动开发 —— platform设备驱动应用实例解析

    前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 —— platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platfor ...

  9. 《Linux设备驱动开发具体解释(第3版)》(即《Linux设备驱动开发具体解释:基于最新的Linux 4.0内核》)网购链接

    <Linux设备驱动开发具体解释:基于最新的Linux 4.0内核> china-pub   spm=a1z10.3-b.w4011-10017777404.30.kvceXB&i ...

随机推荐

  1. 2016.10.19 intelliJ的基本操作

    参考大部分来自:IntelliJ IDEA 13试用手记(附详细截图) 用eclipse实在用的有点心累了.所以准备转战intelliJ.   一.下载安装 官网地址:http://www.jetbr ...

  2. Ubuntu下的计划任务 -- cron的基本知识

    下面不完全: 参考:http://blog.csdn.net/cuker919/article/details/6336457 cron是一个Linux下的后台进程,用来定期的执行一些任务.因为我用的 ...

  3. 13.2 处理静态资源【从零开始学Spring Boot】

    转载:http://blog.csdn.net/linxingliang/article/details/51637052目录(?)[-] 默认资源映射 自定义资源映射 自定义目录 使用外部目录 通过 ...

  4. /etc/shadow 密码加密方法

    [root@mysql-master ~]# cat /etc/shadowroot:$6$spzQDWctb8Lmju0o$KoUz5Qwv1tWyVYfd5cuBw.TQVIaCvCX8ixGG9 ...

  5. Locust压力测试Odoo

    Table of Contents 编写测试任务集 TaskSet 运行 Locust 分布式 运行 master 运行 slave.     Locust 是个伸缩性很好的压力测试框架,OdooLo ...

  6. ReactiveCocoa入门教程——第一部分【转载】

    作为一个iOS开发者,你写的每一行代码几乎都是在响应某个事件,例如按钮的点击,收到网络消息,属性的变化(通过KVO)或者用户位置的变化(通过CoreLocation).但是这些事件都用不同的方式来处理 ...

  7. 有两个好友A和B,住在一片长有蘑菇的由n*m个方格组成的草地,A在(1,1),B在(n,m)。现在A想要拜访B,由于她只想去B的家,所以每次她只会走(i,j+1)或(i+1,j)这样的路线,在草地上有k个蘑菇种在格子里(多个蘑菇可能在同一方格),问:A如果每一步随机选择的话(若她在边界上,则只有一种选择),那么她不碰到蘑菇走到B的家的概率是多少?

    第二种方法:首先分析题意,可用概率的方法来计算,做了好几道百度的题目,觉得大多数是再考概率论,所以首先要弄懂题意,最后做题前把公式写出来,这样编码时才能游刃有余. 本题中下面的第一种用迭代枚举的方法来 ...

  8. _DataStructure_C_Impl:Floyd算法求有向网N的各顶点v和w之间的最短路径

    #include<stdio.h> #include<stdlib.h> #include<string.h> typedef char VertexType[4] ...

  9. CGI FASTCGI php-fpm

    CGI(Common Gateway Interface) CGI全称是“公共网关接口”(Common Gateway Interface),HTTP服务器与你的或其它机器上的程序进行“交谈”的一种工 ...

  10. EF获取DbContext中已注册的所有实体类型

    /// <summary> /// 获取DbContext中已注册的实体类型 /// </summary> /// <typeparam name="T&quo ...