任务需求:
我们的机器采用的是麦克风阵列录音,其中麦克风阵列录音板有两个ADC和一个FPGA,这个麦克风阵列录音板在静电比较大的时候会死掉,导致不能录音,机器陷入不能使用的状态,当时情况紧急,在不改硬件的情况下使用软件复位ADC的方发来解决问题.
在这里我采用的方法是使用NDK通过上层APP给底层驱动发指令让ADC复位的方法(后续介绍使用自动复位方法).
什么是NDK开发,怎么进行NDK开发在这里我就不再一一细说,在这里我讲述一下NDK开发需要进行的几个重要步骤:
在底层驱动创建设备节点,并实现服务函数file_operation.
撰写JNI并将JNI集成到APK里.
编写APK调用JNI操作设备节点,并给设备节点发指令.
1.创建设备节点,并实现服务函数
作为一个linux驱动程序员创建设备节点三步曲应该熟练
register_chrdev
class_create
device_create
其中设备节点名字是device_create函数参数里定义的名字,在本例中是GP_I2C_DEV_NAME,这是一个宏
#define GP_I2C_DEV_NAME "i2c_operator_device"
接下来实现file_operation
static struct file_operations gp_fops =
{
.owner = THIS_MODULE,
.open = gp_open,
.release = gp_close,
.read = gp_read,
.write = gp_write,
};
接下来实现接口
重要的是 gp_write这个函数
这个服务函数里的一个函数copy_from_user,接收上层APP发送下来的指令,通过这个指令的具体内容,就可以做相应的操作.
2.撰写JNI,并且集成到APK项目
具体的NDK开发在这里不细讲,这里只讲JNI的编写,JNI如何和APK的方法联系起来并且又如何能和底层驱动练习起来的呢?有以下几点:
JNI指定APK源码的包名类名
JNI指定设备节点的名字
JNI映射表里将JNI方法和APK方法一一对应
1.JNI指定APK源码的包名类名
jclass clz = env->FindClass("com/hq/lowlevel/ResetNative");
if(clz == NULL)
{
ALOGE(" env->FindClass error
");
return -1;
}
其中包名类名写在了FindClass方法里.
2.JNI指定设备节点的名字
fd = open("/dev/i2c_operator_device", O_RDWR);
if(fd < 0)
{
ALOGE("open error : %s
", strerror(errno));
return -1;
}
这里指定可设备节点所在路径以及设备节点名字,使用open方法去打开设备节点,才能对这个设备节点进行操作.才可以向设备节点也就是驱动里写数据.
到这里,就将底层驱动和JN联系起来了.
3.JNI映射表里将JNI方法和APK方法一一对应
static JNINativeMethod led_methods[] ={
{"openDev", "()I", (void*)open_jni_Reset},
{"ResetOn", "()I", (void*)Reset_jni_on},
{"closeDev", "()I", (void*)close_jni_Reset},
};
在这个映射表里,写明了APK的JAVA方法对应JNI的方法,即APK调用那个JAVA方法,就会调用对应JNI的方法.
到这里就将APK和JNI联系起来了,这样就实现了从APK层到底层驱动的贯穿,具体的JNI如附件.
3.编写APK
这个APK仅用于测试,在实际开发项目中会把这些方法合入具体的项目分支,这个测试APK仅仅用到了几个简单的方法
openDev
ResetOn
closeDev
经过以上的步骤,就实现上层APP到底层驱动的贯穿连接,上层APK可以通过给驱动发指令来控制ADC复位,但是,你会发现,将以上步骤全部不差的做完,打开APK你会发现报错,原因就是JNI去open设备节点的时候,没有系统权限,需要指定这个设备节点为系统权限
在system/core/rootdir/ueventd.rc中添加
/dev/i2c_operator_device 0666 root root
这样就不会报错,就可以通过上层APK手动复位ADC了.
也可以不使用NDK的方法,另一种方法是将JNI变成so动态库,APK直接加载动态库的方法也能实现需求.这时候就需要编写Android.mk了.在附件我会附上自己编写的Android.mk以及JNI供参考。
原作者:Monster_Ps
|