작업환경 : 우분투

AOSP버전 : 7.0 이상에서 확인하였다. 그 이하 버전들은 폴더구조가 다를 수 있다.

보드 : hikey960




안드로이드의 기본적인 장치디바이스 연결 구조



< 센서 드라이버 구조 예 >




< 장치 드라이버 콜 스텍 >




<디렉토리 구조>








HAL 추가


먼저 모든 작업을 시작하기 전에 AOSP를 전체빌드를 한다.


그후 모듈(여기서 모듈은 device driver의 모듈과는 다르다)을 작성한다.



< AOSP_ROOT/hardware/libhardware/include/hardware/whkong_gpio.h[만들 모듈 이름]  생성 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#ifndef ANDROID_WHKONG_GPIO_INTERFACE_H
#define ANDROID_WHKONG_GPIO_INTERFACE_H
 
#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>
 
#include <hardware/hardware.h>
 
__BEGIN_DECLS
 
 
#define WHKONG_GPIO_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(01)
#define WHKONG_GPIO_MODULE_ID "whkong_gpio"
 
 
struct whkong_gpio_device_t {
    struct hw_device_t common;
 
    // 장치드라이버와 JNI를 연결하는 통로가 된다.
    int (*read)(char* buffer);
    int (*write)(char* buffer);
    int (*test)(int value);
};
 
/**
 * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
 * and the fields of this data structure must begin with hw_module_t
 * followed by module specific information.
 */
struct whkong_gpio_module_t {
    struct hw_module_t common;
};
 
 
__END_DECLS
 
    
 
 
#endif
 
cs


모든 하드웨어 모듈은 HAL_MODULE_INFO_SYM 으로 이름지어져야한다고 하고, 그 모듈은 hw_module_t로 시작해야 한다고 설명되어 있다.


hardware/libhardware/include/hardware/hardware.h 를 보면 자세한 설명이 나와있다.





< AOSP/hardware/libhardware/modules/whkong_gpio[모듈폴더]/whkonggpio.cpp[모듈이름] 생성 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

#include <hardware/whkong_gpio.h>
#include <malloc.h>
#include <cutils/log.h>
#include <cstring>
 
#define UNUSED(x) (void)(x)
 
static int open_whkonggpio(const struct hw_module_t* module, char const* name, struct hw_device_t** device);
static int close_whkong_gpio(struct whkong_gpio_device_t *dev);
int whkonggpio_read(char *buffer);
int whkonggpio_write(char *buffer);
int whkonggpio_text(int vaule);
 
//GPIO NUM 여기서는 예제로 gpio를 사용한다고 가정했다.
int gpio[] = { 296 };  //23pin
int fd = 0;
 
 
static int close_whkong_gpio(struct whkong_gpio_device_t *dev)
{
    if(dev)
    {
        //장치드라이버 해제
        //close(fd);
 
        // 드라이버 모듈 해제
        free(dev);
    }
 
 
 
    return 0;
}
 
int whkonggpio_read(char *buffer)
{
    UNUSED(buffer);
    
    int ret = 0;
 
    // 장치드라이버 읽기
    //ret = read(fd, buffer, sizeof(buffer));
 
    return ret;
}
 
int whkonggpio_write(char *buffer)
{
    UNUSED(buffer);
    // 장치드라이버 쓰기
    //ret = write(fd, buffer, sizeof(buffer));
 
    return 0;
}
 
int whkonggpio_test(int value)
{
    UNUSED(value);
    
    //printf("THIS IS TEST VALUE = %d !!!! \n", value);
    return 0;
 
}
 
 
// 모듈을 오픈할 함수
static int open_whkonggpio(const struct hw_module_t* module, char const* name, struct hw_device_t** device)
{
    UNUSED(name);
 
    // 사용할 하드웨어 객체 생성
    struct whkong_gpio_device_t *dev = (whkong_gpio_device_t*)malloc(sizeof(struct whkong_gpio_device_t));
    // 초기화
    memset(dev, 0sizeof(*dev));
 
    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = 0;
    dev->common.module = (struct hw_module_t*)module;
    dev->common.close = (int (*)(struct hw_device_t*))close_whkong_gpio;
 
    // 헤더에서 선언한 함수포인터에 함수 연결
    dev->read = whkonggpio_read;
    dev->write = whkonggpio_write;
    dev->test = whkonggpio_test;
 
    
    // 장치 드라이버를 열어준다.
    // 권한을 줘야한다.
    //fd = open("/dev/whkong_gpio", O_RDWR);
 
    
    // 생성된 하드웨어 객체주소를 넘긴다.
    // device 를 이용하여 디바이스 드라이버를 제어한다. read, write 등 연결된 함수들 이용하여
    *device = (struct hw_device_t*)dev;
    printf("whkong gpio HAL initailize!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
 
    return 0;
}
 
static struct hw_module_methods_t whkong_gpio_module_methods = {
    .open = open_whkonggpio,
};
 
struct whkong_gpio_module_t HAL_MODULE_INFO_SYM = {
    // 이 tag의 아이디를 이용하여 이 객체를 가져오고
    // methods의 등록된 모듈의 open을 이용하여 디아비스를 연결한다.
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = WHKONG_GPIO_API_VERSION_0_1,
        .hal_api_version = HARDWARE_HAL_API_VERSION,
        .id = WHKONG_GPIO_MODULE_ID,
        .name = "whkong gpio controll test HAL",
        .author = "The Android Open Source Project",
        .methods = &whkong_gpio_module_methods,        //모듈을 오픈할 함수주소를 가진 구조체
    }
// .init = whkong_gpio_init,
};
cs





< AOSP/hardware/libhardware/modules/whkong_gpio[모듈폴더]/Android.mk  생성 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LOCAL_PATH := $(call my-dir)
 
# HAL module implemenation stored in
# hw/<POWERS_HARDWARE_MODULE_ID>.<ro.hardware>.so
include $(CLEAR_VARS)
 
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_RELATIVE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_PROPRIETARY_MODULE := true
LOCAL_C_INCLUDES += hardware/libhardware/include
#LOCAL_CFLAGS := -Wconversion -Wall -Werror -Wno-sign-conversion
#LOCAL_CLANG  := true
LOCAL_SHARED_LIBRARIES := liblog libcutils libhardware
LOCAL_SRC_FILES := whkonggpio.cpp
LOCAL_MODULE := whkonggpio.default
#LOCAL_MODULE := memtrack.$(TARGET_BOARD_PLATFORM)
include $(BUILD_SHARED_LIBRARY)
cs


< 중간 빌드 하기 >

$:~/AOSP$ mmm hardware/libhardware/modules/whkong_gpio





프레임워크 JNI 추가




< AOSP/frameworks/base/services/core/jni/com_android_server_whkonggpio_Service.cpp  생성 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#define LOG_TAG "WhkongGpio"
 
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
 
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/whkong_gpio.h>
 
#include <stdio.h>
 
 
namespace android
{
 
    // HAL에서 만든 구조체
    whkong_gpio_device_t *whkonggpio_dev = NULL;
 
 
    static jlong init_native(JNIEnv *env, jobject clazz)
    {
        int err;
        whkong_gpio_module_t *module;
 
 
 
        // 모듈 ID를 이용하여 whkong_gpio_device_t 객체를 생성한다.
        // 객체는 hardware.c 의 hw_get_module() -> hw_get_module_by_class() -> load() 를 이용하여 생성한다.
        err = hw_get_module(WHKONG_GPIO_MODULE_ID, (hw_module_t const**)&module);
        if(err == 0)
        {
                        
            // 생성된 모듈 객체를 이용하여 open_whkonggpio 를 호출후 whkonggpio_dev에 디바이스 주소를 가져온다.
            if(module->common.methods->open((hw_module_t*)module, "", ((hw_device_t**)&whkonggpio_dev)) != 0)
                return 0;
 
        }
 
        long tmp = reinterpret_cast<long>(whkonggpio_dev);
 
        return tmp;
 
    }
 
    static void finalize_native(JNIEnv *env, jobject clazz, jlong ptr)
    {
 
        whkong_gpio_device_t *dev = reinterpret_cast<whkong_gpio_device_t*>(ptr);
        if(dev == NULL)
            return;
 
        free(dev);
        
    }
 
    static int read_native(JNIEnv *env, jobject clazz, int ptr, jbyteArray buffer)
    {
        jbyte* real_byte_array;
 
        real_byte_array = env->GetByteArrayElements(buffer, NULL);
 
        if(whkonggpio_dev == NULL)
            return 0;
 
        whkonggpio_dev->read((char*)real_byte_array);
 
        env->ReleaseByteArrayElements(buffer, real_byte_array, 0);
        
 
        return sizeof(real_byte_array);
    }
 
    static int write_native(JNIEnv *env, jobject clazz, int ptr, jbyteArray buffer)
    {
 
        jbyte* real_byte_array;
 
        real_byte_array = env->GetByteArrayElements(buffer, NULL);
 
        if(whkonggpio_dev == NULL)
            return 0;
 
        whkonggpio_dev->write((char*)real_byte_array);
 
        env->ReleaseByteArrayElements(buffer, real_byte_array, 0);
 
        return sizeof(real_byte_array);
    }
 
 
    static int test_native(JNIEnv *env, jobject clazz, int ptr, int value)
    {
 
        return 0;
 
    }
    
 
    // B=byte
    // C=char
    // D=double
    // F=float
    // I=int
    // J=long
    // S=short
    // V=void
    // Z=boolean
    // Lfully-qualified-class=fully qualified class
    // [type=array of type>
    // (argument types)return type=method type. If no arguments, use empty argument types: (). If return type is void (or constructor) use (argument types)V.
 
    
    // 함수들을 테이블로 관리한다 (메소드가 많을경우 좋다).
    static JNINativeMethod method_table[] =
    {
        {"init_native""()J", (void*)init_native},
        {"finalize_native""(J)V", (void*)finalize_native},
        {"read_native""(I[B)I", (void*)read_native},
        {"write_native""(I[B)I", (void*)write_native},
        {"test_native""(II)I", (void*)test_native},
    };
 
    int register_android_server_WhkongGpioService(JNIEnv *env)
    {
 
        // method_table을 등록 한다.
        // 이름과 파일명 확인필요.
        return jniRegisterNativeMethods(env, "com/android/server/whkonggpio_Service",
                                        method_table, NELEM(method_table));
 
 
    }
 
};
 
cs





< AOSP/frameworks/base/services/core/jni/onload.cpp 에 등록하기 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace android {
    .
    .
    .
    .
int register_android_server_WhkongGpioService(JNIEnv *env);
};
 
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
    .
    .
    .
    .
    .
    register_android_server_WhkongGpioService(env);
    .
    .
 
}
 
cs


위와 같이 JNI의 register 부분을 등록 한다.



< AOSP/frameworks/base/services/core/jni/Android.bp 에 등록하기 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    .
    .
    .
    .
    srcs: [
        .
        .
        .
        .
        "com_android_server_whkonggpio_Service.cpp",
        "onload.cpp",
    ],
    .
    .
    .
 
}
 
cs



< 중간 빌드 하기 >

$:~/AOSP$ mmm framesorks/base/services/core/jni






프레임 워크 인터페이스 추가


HAL을 통해 시스템 서비스가 새로운 유형의 하드웨어와 대화하기 위해서는 새로운 시스템 서비스의 API를 상위 레이어에 노출해야한다.  이를 위해서는 <aosp>/frameworks/base/core/java/android/os/IwhkongService.aidl 과 같은 것이 필요하다.




< AOSP/frameworks/base/core/java/android/os/IwhkonggpioService.aidl  새성 >

1
2
3
4
5
6
7
8
9
package android.os;
 
// 인터페이스를 통해 장치드라이버에대한 액세스를 제공합니다.
// 상위 레이어에 
interface IwhkonggpioService{
 
int read();
int write(int parInput);
}
cs



< AOSP/frameworks/base/Android.bp  수정 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
...
 
java_library {
    name: "framework",
 
    srcs: [
        // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
          .
        .
        .
        "core/java/android/os/IwhkonggpioService.aidl",
        .
        .
        .
      
    ],
    .
    .
    .
    .
   
cs



< 중간 빌드 하기 >

$:~/AOSP$ mmm framesorks/base/ >





프레임 워크와 자바 연결




< AOSP/frameworks/base/services/core/java/com/android/server/WhkongGpioSerivce.java  생성 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.android.server;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.IwhkonggpioService;
import android.util.Slog;
 
public class WhkongGpioService extends IwhkonggpioService.Stub
{
 
    private static final String TAG = "WhkongGpioService";
    private Context mContext;
    private int mNativePointer;
 
 
 
 
    // 이클래스가 불리우면 init_native() 를 통해 
    WhkongGpioService(Context context){
        mContext = context;
        init_native();
    }
 
 
    // 함수가 추가적으로 필요한 경우 IwhkonggpioService 에서 추가한다.
    // 인터페이스를 구현해준다.
    @Override
    public int read()    {
    
    // read_native()를 연결해서 사용한다
    
        return 0;
    }
 
    @Override
    public int write(int parInput){
 
    // write_native()를 연결해서 사용한다.
        return 0;
    }
 
    
 
    private static native long init_native();
    private static native void finalize_native(long ptr);
    private static native int read_native(int ptr, byte[] buffer);
    private static native int write_native(int ptr, byte[] buffer);
    private static native int test_native(int ptr, int value);
        
}
 
cs



< 중간 빌드 하기 >

$:~/AOSP$ mmm frameworks/base/services/core >



여기까지 진행 하였다면 다음과 같이 AOSP내에서 어플개발시 서비스를 부를 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.os.ServiceManager;   // AOSP 내에서만 부를수 있다.
import android.os.IwhkonggpioService;  // Interface "hidden" in SDK
 
public class HelloOpersysInternalActivity extends Activity {
    private static final String DTAG = "whkong_gpio internal";
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
 
        // AOSP 내에서는 아래와 같이 서비스를 가져와서 사용 가능함.
        IwhkonggpioService om = IwhkonggpioService.Stub.asInterface(ServiceManager.getService("whkong_gpio"));
        try {
            Log.d(DTAG, "Going to write to the \"whkong_gpio\" service");
            om.write(10);
            Log.d(DTAG, "Service returned: " + om.read());
        }
        catch (Exception e) {
            Log.d(DTAG, "FAILED to call service");
            e.printStackTrace();
        }
    }
cs



하지만 새로운 시스템 서비스는 일반적인 개발자 API 에서 부르는것이 좋다.



그러기 위해서 다음과 같이 등록 과정을 진행한다.




API 등록





< AOSP/frameworks/base/core/java/android/os/WhkonggpioManager.java 생성 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package android.os;
 
import android.os.IwhkonggpioService;
import android.content.Context;
 
public class WhkonggpioManager
{
 
    Context mContext;
    IwhkonggpioService mService;
    
    public WhkonggpioManager(Context context, IwhkonggpioService service) {
        mService = service;
        mContext = context;
    }
 
 
    // 생성자에서 저장된 IwhkonggpioService 객체를 이용하여 read, write
    public int read() {
        try {
            return mService.read();
        } catch (RemoteException e) {
            return 0;
        }
    }
 
    public int write(int parInput) {
        try {
            return mService.write(parInput);
        } catch (RemoteException e) {
            return 0;
        }
    }
 
 
}
 
cs






< AOSP/frameworks/base/core/java/android/content/Context.java  수정>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    .
    .
    .
    .
    .
 
    /**
     * Use with {@link #getSystemService} to retrieve a
     * {@link android.app.NotificationManager} for informing the user of
     * background events.
     *
     * @see #getSystemService
     * @see android.app.NotificationManager
     */
    public static final String NOTIFICATION_SERVICE = "notification";
 
 
    //////////////////////////////////// ***** <ADDED-BY-WHKONG> *****
    /**
     * Use with {@link #getSystemService} to retrieve a
     * {@link android.os.WhkonggpioManager} for using Opersys Service.
     *
     * @see #getSystemService
     */
    public static final String WHKONGGPIO_SERVICE = "whkong_gpio";
    ////////////////////////////////// ***** </ADDED-BY-WHKONG> *****
 
    /**
     * Use with {@link #getSystemService} to retrieve a
     * {@link android.view.accessibility.AccessibilityManager} for giving the user
     * feedback for UI events through the registered event listeners.
     *
     * @see #getSystemService
     * @see android.view.accessibility.AccessibilityManager
     */
    public static final String ACCESSIBILITY_SERVICE = "accessibility";
 
    .
    .
    .
    .
 
cs


적당한 자리에 껴 넣는다.




< AOSP/frameworks/base/core/java/android/app/SystemServiceRegistry.java  수정>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import android.os.UserManager;
import android.os.Vibrator;
// ===============< ADDED-BY-WHKONG> =========
import android.os.IwhkonggpioService;
import android.os.WhkonggpioManager;
// ===============< ADDED-BY-WHKONG> =========
import android.os.health.SystemHealthManager;
import android.os.storage.StorageManager;
 
 
        ...
        ...
 
        
        // ======================== ADDED BY WHKONG ==========
        registerService(Context.WHKONGGPIO_SERVICE, WhkonggpioManager.class,
                        new CachedServiceFetcher<WhkonggpioManager>() {
            @Override
            public WhkonggpioManager createService(ContextImpl ctx) {
                IBinder b = ServiceManager.getService(Context.WHKONGGPIO_SERVICE);
                return new WhkonggpioManager(ctx, IwhkonggpioService.Stub.asInterface(b));
            }});
        // ======================== ADDED BY WHKONG ==========
 
        
 
        registerService(Context.WALLPAPER_SERVICE, WallpaperManager.class,
                        new CachedServiceFetcher<WallpaperManager>() {
            @Override
            public WallpaperManager createService(ContextImpl ctx) {
                return new WallpaperManager(ctx.getOuterContext(),
                                            ctx.mMainThread.getHandler());
            }});
 
 
        ...
        ...
        .
        .
cs


위와 같이 import와 서비스를 등록한다.




< AOSP/frameworks/base/services/java/com/android/server/SystemServer.java  수정>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
        .
        .
        .
 
    
            traceBeginAndSlog("StartTelephonyRegistry");
            telephonyRegistry = new TelephonyRegistry(context);
            ServiceManager.addService("telephony.registry", telephonyRegistry);
            traceEnd();
 
            // =======================  ADDED BY WHKONG ===================
            traceBeginAndSlog("StartWhkongGpioService");
            ServiceManager.addService(Context.WHKONGGPIO_SERVICE, new WhkongGpioService(context));
            traceEnd();
            // =======================  ADDED BY WHKONG ===================
 
            traceBeginAndSlog("StartEntropyMixer");
            mEntropyMixer = new EntropyMixer(context);
            traceEnd();
 
 
        .
        .
        .
cs


위와 같이 등록.




업데이트 중................


+ Recent posts