基于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 

 

posted @ 2017-08-04 16:02  某某璀  阅读(788)  评论(0编辑  收藏  举报
levels of contents