基于iTop4412的FM收音机系统设计(二)
说明:第一版架构为:APP+JNI(NDK)+Driver(linux),优点是开发简单,周期短,也作为自己的毕业设计
现在更新第二版,FM服务完全植入Android系统中,成为系统服务,架构为:APP+Frameworks+JNI+HAL+Driver
整个系统设计,大致分为三篇文章介绍完毕,包括:
一、驱动设计篇
二、系统API接口篇
三、APP功能实现篇
---------------------------------------------------(二)系统接口篇-----------------------------------------------------------------
说明:关于系统接口,在FM系统中完全用不到这么复杂的流程,我这里是想把整个系统调用流程捋顺,所以使用了Frameworks(AIDL)->JNI->HAL
1.Frameworks
1.1.首先我们需设计好暴露给APP端的API接口,这里我们采用aidl的机制实现
进入到frameworks/base/core/java/android/os目录下,新建IFMService.aidl文件
1 package android.os; 2 3 interface IFMService { 4 int getADC(); 5 int getFreq(); 6 void setFreq(int freq); 7 void searchNextorPreFreq(int enable); 8 void setNextorPreFreq(int enable); 9 void enableMute(int enable); 10 int getIsMute(); 11 void startAutoSearch(); 12 }
1.2.回到frameworks/base目录,编写编译规则,生成Stub接口文件
打开Android.mk文件,在LOCAL_SRC_FILES属性中添加
1 LOCAL_SRC_FILES += \ 2 ... 3 core/java/android/os/IFMService.aidl \ 4 ...
1.3.在frameworks/base目录下,使用mm命令编译IFMService.aidl生成IHelloService.Stub,在使用mm目录之前需要lunch系统的编译环境
Stub实质上就是binder通信,client<-->proxy<-->stub<-->service
1.4.进入到frameworks/base/services/java/com/android/server,新建FMService.java文件,继承IHelloService.Stub并实现接口函数
1 package com.android.server; 2 3 import android.content.Context; 4 import android.os.IFMService; 5 import android.util.Slog; 6 7 public class FMService extends IFMService.Stub { 8 9 private static final String TAG = "FMService"; 10 11 FMService() { 12 Slog.d(TAG,"FM init..."); 13 init_native(); 14 } 15 16 public int getADC(){ 17 18 return getADC_native(); 19 } 20 21 public int getFreq(){ 22 return getFreq_native(); 23 } 24 25 public void setFreq(int freq){ 26 27 setFreq_native(freq); 28 return ; 29 } 30 31 public void searchNextorPreFreq(int enable){ 32 33 searchNextorPreFreq_native(enable); 34 return ; 35 } 36 37 public void setNextorPreFreq(int enable){ 38 39 setNextorPreFreq_native(enable); 40 return ; 41 } 42 43 public void enableMute(int enable){ 44 45 enableMute_native(enable); 46 return ; 47 } 48 49 public int getIsMute(){ 50 return getIsMute_native(); 51 } 52 53 public void startAutoSearch(){ 54 55 startAutoSearch_native(); 56 return ; 57 } 58 59 private static native boolean init_native(); 60 private static native int getADC_native(); 61 private static native int getFreq_native(); 62 private static native void setFreq_native(int freq); 63 private static native void searchNextorPreFreq_native(int enable); 64 private static native void setNextorPreFreq_native(int enable); 65 private static native void enableMute_native(int enable); 66 private static native int getIsMute_native(); 67 private static native void startAutoSearch_native(); 68 69 };
在这个文件中,我们把函数调用传递到了JNI
1.5.最后在Frameworks中我们需要启动我们的FMservice
进入frameworks/base/services/java/com/android/server目录,在SystemServer.java文件中添加
1 class ServerThread extends Thread { 2 @Override 3 public void run() { 4 if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { 5 try { 6 Slog.i(TAG, "DiskStats Service"); 7 ServiceManager.addService("diskstats", new DiskStatsService(context)); 8 } catch (Throwable e) { 9 reportWtf("starting DiskStats Service", e); 10 } 11 try { 12 13 Slog.i(TAG, "FM Service"); 14 15 ServiceManager.addService("fm5767", new FMService()); 16 17 } catch (Throwable e) { 18 19 Slog.e(TAG, "Failure starting FM Service", e); 20 21 } 22 } 23 } 24 }
1.6.编译service(若修改了service的实现方法可模块编译service模块)
进入frameworks/base/services/java,使用命令mm进行编译,在out目录下的system/frameworks下生成services.jar,使用adb直接cp到板子上重启即可生效
2.JNI
2.1.实现JNi函数,函数传递到HAL层
进入frameworks/base/services/jni目录,新建com_android_server_FMService.cpp文件
1 #define LOG_TAG "FMService_jni" 2 #include "jni.h" 3 #include "JNIHelp.h" 4 #include "android_runtime/AndroidRuntime.h" 5 #include <utils/misc.h> 6 #include <utils/Log.h> 7 #include <hardware/hardware.h> 8 #include <hardware/hw_tea5767.h> 9 #include <stdio.h> 10 11 namespace android 12 { 13 /*在硬件抽象层中定义的硬件访问结构体,参考<hardware/tea5767.h>*/ 14 struct tea5767_device_t* tea5767_device = NULL; 15 16 //访问硬件的接口 17 static jint tea5767_getADC(JNIEnv* env, jobject clazz){ 18 int adc = 0; 19 if(!tea5767_device){ 20 LOGE("FM jni is not open.."); 21 return adc; 22 } 23 adc = tea5767_device->getADC(tea5767_device); 24 LOGI("get fm adc = %d",adc); 25 26 return adc; 27 } 28 29 static jint tea5767_getFreq(JNIEnv* env, jobject clazz){ 30 int freq = 0; 31 if(!tea5767_device){ 32 LOGE("FM jni is not open.."); 33 return freq; 34 } 35 freq = tea5767_device->getFreq(tea5767_device); 36 LOGI("get fm freq = %d",freq); 37 38 return freq; 39 } 40 41 static void tea5767_setFreq(JNIEnv* env, jobject clazz, jint freq){ 42 43 if(!tea5767_device){ 44 LOGE("FM jni is not open.."); 45 return ; 46 } 47 tea5767_device->setFreq(tea5767_device,freq); 48 LOGI("set fm freq = %d",freq); 49 50 return ; 51 } 52 53 static void tea5767_searchNextorPreFreq(JNIEnv* env, jobject clazz, jint enable){ 54 55 if(!tea5767_device){ 56 LOGE("FM jni is not open.."); 57 return ; 58 } 59 tea5767_device->searchNextorPreFreq(tea5767_device,enable); 60 LOGI("searchNextorPreFreq state = %d",enable); 61 62 return ; 63 } 64 65 static void tea5767_setNextorPreFreq(JNIEnv* env, jobject clazz, jint enable){ 66 67 if(!tea5767_device){ 68 LOGE("FM jni is not open.."); 69 return ; 70 } 71 tea5767_device->setNextorPreFreq(tea5767_device,enable); 72 LOGI("setNextorPreFreq state = %d",enable); 73 74 return ; 75 } 76 77 78 static void tea5767_enableMute(JNIEnv* env, jobject clazz, jint enable){ 79 80 if(!tea5767_device){ 81 LOGE("FM jni is not open.."); 82 return ; 83 } 84 tea5767_device->enableMute(tea5767_device,enable); 85 LOGI("enableMute state = %d",enable); 86 87 return ; 88 } 89 90 static jint tea5767_getIsMute(JNIEnv* env, jobject clazz){ 91 int enable = 0; 92 if(!tea5767_device){ 93 LOGE("FM jni is not open.."); 94 return enable; 95 } 96 enable = tea5767_device->getIsMute(tea5767_device); 97 LOGI("getIsMute state = %d",enable); 98 99 return enable; 100 } 101 102 static void tea5767_autoSearch(JNIEnv* env, jobject clazz){ 103 104 if(!tea5767_device){ 105 LOGE("FM jni is not open.."); 106 return ; 107 } 108 tea5767_device->autoSearch(tea5767_device); 109 LOGI("fm start autoSearch"); 110 111 return ; 112 } 113 114 /*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/ 115 static inline int tea5767_device_open(const hw_module_t* module, struct tea5767_device_t** device) { 116 117 return module->methods->open(module, tea5767_HARDWARE_MODULE_ID, (struct hw_device_t**)device); 118 } 119 120 /*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/ 121 static jboolean tea5767_init(JNIEnv* env, jclass clazz) { 122 tea5767_module_t* module; 123 124 LOGI("tea5767 JNI: initializing......"); 125 if(hw_get_module(tea5767_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) { 126 LOGI("tea5767 JNI: tea5767 Stub found."); 127 if(tea5767_device_open(&(module->common), &tea5767_device) == 0) { 128 LOGI("tea5767 JNI: tea5767 device is open."); 129 return 0; 130 } 131 LOGE("tea5767 JNI: failed to open tea5767 device."); 132 return -1; 133 } 134 LOGE("tea5767 JNI: failed to get tea5767 stub module."); 135 return -1; 136 } 137 /*JNI方法表*/ 138 static const JNINativeMethod method_table[] = { 139 {"init_native", "()Z", (void*)tea5767_init}, 140 {"getADC_native", "()I", (void*)tea5767_getADC}, 141 {"getFreq_native", "()I", (void*)tea5767_getFreq}, 142 {"setFreq_native", "(I)V", (void*)tea5767_setFreq}, 143 {"searchNextorPreFreq_native", "(I)V", (void*)tea5767_searchNextorPreFreq}, 144 {"setNextorPreFreq_native", "(I)V", (void*)tea5767_setNextorPreFreq}, 145 {"enableMute_native", "(I)V", (void*)tea5767_enableMute}, 146 {"getIsMute_native", "()I", (void*)tea5767_getIsMute}, 147 {"startAutoSearch_native", "()V", (void*)tea5767_autoSearch}, 148 }; 149 /*注册JNI方法*/ 150 int register_android_server_FMService(JNIEnv *env) { 151 return jniRegisterNativeMethods(env, "com/android/server/FMService", method_table, NELEM(method_table)); 152 } 153 };
注意:1.在tea5767_init函数中,通过HAL层提供的hw_get_module方法来加载模块ID,即tea5767_HARDWARE_MODULE_ID的硬件抽象层模块,而这个ID是在<hardware/hw_tea5767.h>中定义的。HAL层会根据该ID的值在Android系统的/system/lib/hw目录中找到相应的模块,然后加载起来,并且返回hw_module_t接口给调用者使用。
2.在jniRegisterNativeMethods函数中,第二个参数的值必须对应FMService所在的包的路径,即com.android.server.FMService。
2.2.自动加载FM模块的jni方法
进入frameworks/base/services/jni目录中,编辑onload.cpp文件,添加属性
1 namespace android { 2 ... 3 int register_android_server_FMService(JNIEnv *env); 4 }; 5 6 extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) 7 { 8 ... 9 register_android_server_FMService(env); 10 11 return JNI_VERSION_1_4; 12 }
2.3 添加编译选项,编译Fm的JNI模块
进入frameworks/base/services/jni目录中,编辑Android.mk文件
1 LOCAL_SRC_FILES:= \ 2 ... 3 com_android_server_FMService.cpp \ 4 onload.cpp
2.4.编译
进入frameworks/base/services/jni目录,执行命令mm,编译,最后会在system/lib下生成libandroid_servers.so文件
3.HAL层
3.1.定义HAL层的接口
进入hardware/libhardware/include/hardware目录下,新建hw_tea5767.h文件
1 #ifndef ANDROID_TEA5767_INTERFACE_H 2 #define ANDROID_TEA5767_INTERFACE_H 3 #include <hardware/hardware.h> 4 5 __BEGIN_DECLS 6 7 /*定义模块ID*/ 8 #define tea5767_HARDWARE_MODULE_ID "tea5767" 9 10 /*硬件模块结构体*/ 11 struct tea5767_module_t { 12 struct hw_module_t common; 13 }; 14 15 /*硬件接口结构体*/ 16 struct tea5767_device_t { 17 struct hw_device_t common; 18 int fd; 19 int (*getADC)(struct tea5767_device_t* dev); 20 int (*getFreq)(struct tea5767_device_t* dev); 21 void (*setFreq)(struct tea5767_device_t* dev, int freq); 22 void (*searchNextorPreFreq)(struct tea5767_device_t* dev, int enable); 23 void (*setNextorPreFreq)(struct tea5767_device_t* dev, int enable); 24 void (*enableMute)(struct tea5767_device_t* dev, int enable); 25 int (*getIsMute)(struct tea5767_device_t* dev); 26 void (*autoSearch)(struct tea5767_device_t* dev); 27 }; 28 29 __END_DECLS 30 31 #endif
3.2.实现HAL层的方法
进入hardware/libhardware/modules目录下,新建hwfm/hwtea5767.c文件
1 #define LOG_TAG "FM5767_Stub" 2 3 #include <hardware/hardware.h> 4 #include <hardware/hw_tea5767.h> 5 #include <fcntl.h> 6 #include <errno.h> 7 #include <cutils/log.h> 8 #include <cutils/atomic.h> 9 10 #define DEVICE_NAME "/dev/tea5767" 11 #define MODULE_NAME "tea5767" 12 #define MODULE_AUTHOR "pngcui" 13 14 #define Search 3 15 #define AutoSearch 4 16 #define SETFREQ 5 17 #define OPENMUTE 8 18 #define CLOSEMUTE 9 19 #define SHUTDOWN 10 20 #define ADC 100 21 #define FREQ 101 22 23 /*设备打开和关闭接口*/ 24 static int tea5767_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device); 25 static int tea5767_device_close(struct hw_device_t* device); 26 27 /*设备访问接口*/ 28 static int fm_getADC(struct tea5767_device_t* dev); 29 static int fm_getFreq(struct tea5767_device_t* dev); 30 static void fm_setFreq(struct tea5767_device_t* dev, int freq); 31 static void fm_searchNextorPreFreq(struct tea5767_device_t* dev, int enable); 32 static void fm_setNextorPreFreq(struct tea5767_device_t* dev, int enable); 33 static void fm_enableMute(struct tea5767_device_t* dev, int enable); 34 static int fm_getIsMute(struct tea5767_device_t* dev); 35 static void fm_autoSearch(struct tea5767_device_t* dev); 36 37 /*模块方法表*/ 38 static struct hw_module_methods_t tea5767_module_methods = { 39 open: tea5767_device_open 40 }; 41 42 /* 43 * 44 *实例变量名必须为HAL_MODULE_INFO_SYM,tag也必须为HARDWARE_MODULE_TAG,这是Android硬件抽象层规范规定的。 45 */ 46 47 /*模块实例变量*/ 48 struct tea5767_module_t HAL_MODULE_INFO_SYM = { 49 common: { 50 tag: HARDWARE_MODULE_TAG, 51 version_major: 1, 52 version_minor: 0, 53 id: tea5767_HARDWARE_MODULE_ID, 54 name: MODULE_NAME, 55 author: MODULE_AUTHOR, 56 methods: &tea5767_module_methods, 57 } 58 }; 59 60 static int tea5767_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) { 61 struct tea5767_device_t* dev; 62 dev = (struct tea5767_device_t*)malloc(sizeof(struct tea5767_device_t)); 63 64 if(!dev){ 65 LOGE("tea5767 Stub: failed to malloc space"); 66 return -EFAULT; 67 } 68 69 memset(dev, 0, sizeof(struct tea5767_device_t)); 70 dev->common.tag = HARDWARE_DEVICE_TAG; 71 dev->common.version = 0; 72 dev->common.module = (hw_module_t*)module; 73 dev->common.close = tea5767_device_close; 74 75 dev->getADC = fm_getADC; 76 dev->getFreq = fm_getFreq; 77 dev->setFreq = fm_setFreq; 78 dev->searchNextorPreFreq = fm_searchNextorPreFreq; 79 dev->setNextorPreFreq = fm_setNextorPreFreq; 80 dev->enableMute = fm_enableMute; 81 dev->autoSearch = fm_autoSearch; 82 dev->getIsMute = fm_getIsMute; 83 84 if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) { 85 LOGE("tea5767 Stub: failed to open /dev/tea5767 -- %s.", strerror(errno)); 86 free(dev); 87 return -EFAULT; 88 } 89 90 *device = &(dev->common); 91 LOGI("tea5767 Stub: open /dev/tea5767 successfully."); 92 93 return 0; 94 } 95 96 static int tea5767_device_close(struct hw_device_t* device) { 97 struct tea5767_device_t* tea5767_device = (struct tea5767_device_t*)device; 98 99 if(tea5767_device) { 100 close(tea5767_device->fd); 101 free(tea5767_device); 102 } 103 104 return 0; 105 } 106 107 static int fm_getADC(struct tea5767_device_t* dev){ 108 LOGI("fm get ADC...."); 109 110 int ret = ioctl(dev->fd,ADC, 0); 111 LOGI("ret = %d",ret); 112 113 return ret; 114 } 115 116 static int fm_getFreq(struct tea5767_device_t* dev){ 117 LOGI("fm get fm_getFreq...."); 118 119 int ret = ioctl(dev->fd,100, 0); 120 LOGI("ret = %d",ret); 121 122 return ret; 123 } 124 125 static void fm_setFreq(struct tea5767_device_t* dev, int freq){ 126 LOGI("fm get fm_setFreq...."); 127 128 int ret = ioctl(dev->fd,FREQ, 0); 129 LOGI("ret = %d",ret); 130 131 return ret; 132 } 133 static void fm_searchNextorPreFreq(struct tea5767_device_t* dev, int enable){ 134 LOGI("fm get fm_searchNextorPreFreq...."); 135 136 int ret = ioctl(dev->fd,Search, enable); 137 138 LOGI("ret = %d",ret); 139 140 return ret; 141 } 142 143 static void fm_setNextorPreFreq(struct tea5767_device_t* dev, int enable){ 144 LOGI("fm get fm_setNextorPreFreq...."); 145 146 int ret = ioctl(dev->fd,Search, enable); 147 LOGI("ret = %d",ret); 148 149 return ret; 150 } 151 152 static void fm_enableMute(struct tea5767_device_t* dev, int enable){ 153 LOGI("fm get fm_enableMute...."); 154 155 int ret; 156 if(enable){ 157 ret = ioctl(dev->fd,OPENMUTE, 0); 158 }else{ 159 ret = ioctl(dev->fd,CLOSEMUTE, 0); 160 } 161 LOGI("ret = %d",ret); 162 163 return ret; 164 } 165 166 static int fm_getIsMute(struct tea5767_device_t* dev){ 167 LOGI("fm get fm_getIsMute...."); 168 169 int ret = ioctl(dev->fd,CLOSEMUTE, 0); 170 LOGI("ret = %d",ret); 171 172 return ret; 173 } 174 175 static void fm_autoSearch(struct tea5767_device_t* dev){ 176 LOGI("fm get fm_autoSearch...."); 177 178 int ret = ioctl(dev->fd,AutoSearch, 87500); 179 LOGI("ret = %d",ret); 180 181 return ret; 182 }
通过这个函数,实际上是操作了FM的文件设备,即/dev/tea5767,使用ioctl调用驱动程序中的函数,最终驱动起芯片工作。
3.3.添加编译选项
3.3.1.进入hardware/libhardware/modules/hwfm下,新建Android.mk文件
1 LOCAL_PATH := $(call my-dir) 2 include $(CLEAR_VARS) 3 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw 4 LOCAL_SHARED_LIBRARIES := liblog 5 LOCAL_SRC_FILES := hwtea5767.c 6 LOCAL_MODULE := tea5767.smdk4x12 7 LOCAL_MODULE_TAGS := optional 8 include $(BUILD_SHARED_LIBRARY)
这里的LOCAL_MODULE的定义规则,我们可以根据hardware/libhardware/hardware.c文件中得出
1 #define HAL_LIBRARY_PATH1 "/system/lib/hw" 2 #define HAL_LIBRARY_PATH2 "/vendor/lib/hw" 3 4 static const char *variant_keys[] = { 5 "ro.hardware", /* This goes first so that it can pick up a different 6 file on the emulator. */ 7 "ro.product.board", 8 "ro.board.platform", 9 "ro.arch" 10 }; 11 12 int hw_get_module_by_class(const char *class_id, const char *inst, 13 const struct hw_module_t **module) 14 { 15 int status; 16 int i; 17 const struct hw_module_t *hmi = NULL; 18 char prop[PATH_MAX]; 19 char path[PATH_MAX]; 20 char name[PATH_MAX]; 21 22 if (inst) 23 snprintf(name, PATH_MAX, "%s.%s", class_id, inst); 24 else 25 strlcpy(name, class_id, PATH_MAX); 26 27 /* 28 * Here we rely on the fact that calling dlopen multiple times on 29 * the same .so will simply increment a refcount (and not load 30 * a new copy of the library). 31 * We also assume that dlopen() is thread-safe. 32 */ 33 34 /* Loop through the configuration variants looking for a module */ 35 for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) { 36 if (i < HAL_VARIANT_KEYS_COUNT) { 37 if (property_get(variant_keys[i], prop, NULL) == 0) { 38 continue; 39 } 40 snprintf(path, sizeof(path), "%s/%s.%s.so", 41 HAL_LIBRARY_PATH2, name, prop); 42 if (access(path, R_OK) == 0) break; 43 44 snprintf(path, sizeof(path), "%s/%s.%s.so", 45 HAL_LIBRARY_PATH1, name, prop); 46 if (access(path, R_OK) == 0) break; 47 } else { 48 snprintf(path, sizeof(path), "%s/%s.default.so", 49 HAL_LIBRARY_PATH1, name); 50 if (access(path, R_OK) == 0) break; 51 } 52 } 53 54 status = -ENOENT; 55 if (i < HAL_VARIANT_KEYS_COUNT+1) { 56 /* load the module, if this fails, we're doomed, and we should not try 57 * to load a different variant. */ 58 status = load(class_id, path, module); 59 } 60 61 return status; 62 } 63 64 int hw_get_module(const char *id, const struct hw_module_t **module) 65 { 66 return hw_get_module_by_class(id, NULL, module); 67 }
所以LOCAL_MODULE的命名应该为name.prop,而prop的值为getprop ro.product.board的属性,itop4412平台上是smdk4x12,故LOCAL_MODULE为tea5767.smdk4x12
3.3.2.把hwfm文件夹添加到编译系统中
进入到hardware/libhardware/modules目录下,编辑Android.mk文件
1 hardware_modules := gralloc hwcomposer audio nfc hwfm 2 3 ifeq ($(BOARD_HAVE_MTK_MT6620),true) 4 hardware_modules += gps 5 endif 6 7 include $(call all-named-subdir-makefiles,$(hardware_modules))
3.4.编译
进入hardware/libhardware/modules/hwfm下,执行命令mm,最后在/system/lib/hw下生成tea5767.smdk4x12.so文件
4.更改/dev/tea5767设备文件权限
由于设备文件是在内核驱动里面通过device_create创建的,而device_create创建的设备文件默认只有root用户可读写,而我们的APP一般不具有root权限,这时候就导致打开设备文件失败而报错:failed to open /dev/tea5767 -- Permission denied.
进入device/samsung/smdk4x12/conf目录,编辑init.smdk4x12.rc文件,添加
chmod 0777 /dev/tea5767
最后可以完整的编译一次Android测试
至此,从Frameworks暴露给APP的接口到驱动的调用完成,后续会进行优化,欢迎大家指出错误与不足指出,非常感谢~~
完整工程代码下载:
https://github.com/pngcui/FM-radio
作者:pngcui
博客园:http://www.cnblogs.com/pngcui/
github:https://github.com/pngcui
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明。