Android Sensor HAL Porting Guide?

0. 개발 환경 및 목표

환경

odroid c2, android 5.1.1, 심박센서

목표

i2c 통신을 하는 심박 센서를 HAL에 추가하여 android sensor service framework에서 호출해 사용할 수 있도록 포팅

코드 포팅을 완료하고 빌드하면 android에서 사용하는 so 라이브러리 파일(shared object)이 생성된다
보통 삼성, LG같은 유명 벤더의 안드로이드 휴대폰 소스코드에는 각각 센서의 소스코드까지 포함되어 있지는 않고 HAL영역은 이런 so파일로 제공된다

물론 거기에 해당 벤더에 맞게 android 소스 전체를 환경설정하는 많은 스크립트 및 설정 파일들이 있겠지만 일단 그런건 무시
그리고 작업을 완료하고나서 가능하면 cts도 돌려볼 생각이다


0. 심박 센서 통신 구조

data
\
processing
\
raw data interrupt
\ /
ioctl() poll()
\ /
/dev/i2c /sys/class/gpio/gpioxxx
\ /
SENSOR

포팅하려는 센서의 linux application과 통신하는 구조이다
별로 좋은 구조는 아니다
driver부터 새로 짜서 다 뜯어고치고 싶은데 우선 포팅이 목표이므로 그냥 진행한다
이제 우리는 linux application이 아닌 Android HAL과 통신하게 된다


1. HAL Interface 문서

https://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-identify
https://source.android.com/devices/sensors/hal-interface.html
https://source.android.com/compatibility/cts/
모든 것이 깔끔하게 정리되어 나와있다


2. 기존 sensors.cpp 구조 파악

적당히 어떻게 추가할지 흐름을 파악한다
참고 : https://source.android.com/devices/sensors/hal-interface.html

  • get_sensors_list - 새 하드웨어 정의 추가
  • struct sensors_poll_context_t - enum 추가, 새 센서 객체 생성 및 file descriptor, pipe 연결
  • handleToDriver - id에 따른 return 하는 enum값 수정
  • activate - 획득한 handle이 accelerometer인지 심박센서인지 구분하는 코드 추가 및 센서의 enable, wake pipe 구현 -> open 및 init
  • setDelay - handle 구분 코드 추가 및 센서의 setDelay 구현
  • pollEvents - handle 구분 코드 추가 및 센서의 readEvents 구현, pipe 연결 -> ioctl로 i2c read를 넣으면 되겠다


3. 새 센서 객체 파일 추가

hardware/hardkernel/libsensor/HeartRateMonitorSensor.h
hardware/hardkernel/libsensor/HeartRateMonitorSensor.cpp
hardware/hardkernel/libsensor/Android.mk - cpp 파일 추가
만약 객체를 더 생성하거나 그럴 필요가 있으면 더 추가하면 된다


4. get_sensors_list 수정 - sensors.cpp

static struct sensor_t sSensorList[] = {
    {
        .name     = "Odroid-USBIO Accelermeter Sensor", 
        ...
    },
    {
        .name    = "heartrate monitor sensor",
        .vendor = "chocokeki",
        .version = 1,
        .handle = SENSORS_HEARTRATEMONITOR_HANDLE,
        .type = SENSOR_TYPE_HEART_RATE,
        .maxRange = ,
        .resolution = ,
        .power = ,
        .minDelay =  ,
        .fifoReservedEventCount = ,
        .fifoMaxEventCount = ,
        .stringType = SENSOR_STRING_TYPE_HEART_RATE,
        .requiredPermission = SERSOR_PERMISSION_BODY_SENSORS,    //for manifest
        .maxDelay = 0,
        .flags = SENSOR_FLAG_ON_CHANGE_MODE,
    },
}

각 항목에 관해서는 이 링크를 참조할 것. 너무 방대해서 간단한 정리가 불가하다
센서 별 특징이 있으므로 스펙에 맞게 정확히 기록
아래 링크의 sensor_t 를 확인하기 바란다
https://source.android.com/devices/sensors/hal-interface.html


5. open 수정 - sensors.cpp

open이 호출되면 sensors_poll_context_t 객체가 생성된다
sensors_poll_context 객체가 생성될 때 새로운 센서 객체를 같이 생성하도록 코드를 추가하고 poll 환경 설정 정도를 해준다
SensorBase를 통해 상속을 받은 메서드들이 호출되므로 SensorBase의 구현 코드를 참조한다(SensorBase.cpp)
그리고 InputEventCircularReader 객체도 상속을 통해 생성되니 참고하기 바란다(InputEventReader.cpp)


6. 새로운 센서 객체 구현 - HeartRateMonitorSensor.h

A. 자료형

일단 SensorBase로 부터 상속을 받고 sensors.cpp에서 사용하는 method들을 동일하게 선언한다
OdroidSensor.h 참조

#ifndef ANDROID_HRM_SENSOR_H_
#define ANDROID_HRM_SENSOR_H_

#include "SensorBase.h"
#include "InputEventReader.h"
#include "sensor.h"

class HeartRateMonitorSensor : public SensorBase {
public:
    HeartRateMonitorSensor();
    virtual ~HeartRateMonitorSensor();
    virtual int readEvents(sensors_event_t* data, int count);
    ...
};
#endif

펌웨어나 driver만 개발하는 시스템 프로그래밍에 익숙하던 분들이 갑자기 c++ 상속 개념 나오고 virtual 날아다니고 하면 멘붕이 올 수 있으나 금방 적응할 수 있다
class 상속 관련 추천 링크 : http://blog.eairship.kr/175


B. 역할 분담 계획

HeartRateMonitorSensor()
변수 초기화, 장치 open, 연관된 객체들 초기화

~HeartRateMonitorSensor()
enable을 0으로 초기화 - 센서 비활성화

hasPendingEvents()
처리 못하고 pending된 이벤트 있니?

setDelay(int32_t handle, int64_t ns)
보통 sysfs를 통해 sampling frequency를 변경하는데 1.0이하의 HAL에서만 유효 -> deprecated, batch로 변경되었다
실제 구현은 하지 않기로 한다

enable(int32_t handle, int enable)
sysfs 통해 센서 활/비활성화, interrupt 활/비활성화

readEvents(sensors_event_t* data, int count)
생성자를 통해 open한 장치로부터 InputEventCircularReader 를 통해 event data를 읽어 온다
보편적인 linux의 input_event 를 사용하지 않고 /dev/i2c와 통신을 하는 방식으로 구현하기로 했으므로 InputEventCircularReader는 사용하지 않는다

C. Method 구현

HeartRateMonitorSensor()

SensorBase를 통해 상속 받은 객체들을 초기화 한다
readEvents에서 InputEventCircularReader를 사용하기 때문에 객체 초기화를 해준다 i2c dev를 읽어올 수 있도록 장치 open
pending event를 관리할 sensors_event_t 역시 초기화를 해준다
interrupt 설정 및 활성화
마지막으로 enable을 호출하여 센서를 활성화 한다

InputEventCircularReader의 버퍼 크기는 적당히 잡아준다
말 그대로 링이 구현되어 있는 것인데 struct input_event크기로 데이터를 읽어오니 주의 바란다
그런데 input_event라하면 /sys/class/input 여기인데 무조건 이런 형태로 데이터를 넣고 받아야 하는지 의문이다
레퍼런스 어디서 못 보려나

우리는 용감하게 i2c dev를 읽도록 결정하였으므로 과감하게 무시하자

~HeartRateMonitorSensor()

센서가 활성화 중인지 체크하고 enable을 호출하여 센서를 비활성화 한다
얘가 왜 virtual로 선언되어야 하는지 몰랐는데 상속 받은 아이들의 destructor는 virtual가 붙어야 한다고 하더라

hasPendingEvents()

readEvents 에서 data를 읽었는지 여부를 확인

setDelay(int32_t handle, int64_t ns)

sysfs를 통해 센서에 명령을 전달, sampling frequency를 변경
하지만 구현하지 않는다. 신 버전에서 batch로 변경되었으니까

enable(int32_t handle, int enable)

센서 활성화/비활성화 - 보통 sysfs 를 통해 명령을 내린다

readEvents(sensors_events_t* data, int count)

data에 sensors_event_t 형태의 자료를 붙여서 보내면 된다
count는 몇개 읽을까요?고 return값으로는 몇개 이벤트를 읽었는지 보내면 ok

InputEventCircularReader를 사용하는 루틴은 정형화 되어 있으므로
readEvents에서 fill하거나(read), readEvent를 호출하는 구조를 그대로 사용하면 된다

현재 구현하려는 센서가 심박센서라 input_event 모델이 딱히 필요 없기 때문에(사실 귀찮아서)
대충 간략하게 sensors_event_t를 만들어서 보내는 식으로 개발하려 한다


D. Device Driver 구현

이번에는 /dev/i2c와 통신을 하기 때문에 따로 driver를 구현할 필요는 없으나 일반적인 경우에는 InputEventCircularReader가 struct input_event 형태로 데이터를 읽어가므로 read 요청이 오면 해당 형태로 데이터를 보내주도록 한다
그런데 여기에도 함정카드가 있는데 일반적인 기법에 대해서 말이다
퀄컴 보드의 경우 sensors_classdev 라는 linux kernel device driver 영역에 자료형과 api를 만들어서 제공하며 그 규칙을 따라서 제작해야 한다
참고 : Qualcomm Snapdragon Sensors Porting Guide
https://developer.qualcomm.com/qfile/28820/lm80-p0436-9_sensors_porting_guide.pdf

nexus 5x 기준 android kernel source
drivers/sensors/sensors_class.c
drivers/input/misc/akm8963.c - compass sensor
drivers/misc/apds993x.c - ambient light, proximity sensor 등등 좋은 예시가 많다

이런 규칙을 따라 작성하는게 통일성도 유지되고 좋을 것 같다

불행히 일반적인 심박센서가 어떤 자료형으로 어떻게 통신되는지 레퍼런스를 알 길이 없으므로 일단 input_event 형태로 구현하고
driver가 데이터를 넘겨주면 hal에서는 struct heart_rate_event_t형태로 만들어 줘야할 것 같다

hardware/libhardware/include/hardware/sensors.h

typedef struct sensors_event_t {
...
    heart_rate_event_t heart_rate
}sensors_event_t;

typedef struct {
    float bpm;
    int8_t status;
}heart_rate_event_t;

7. Build

모듈 빌드

source build/envsetup.sh
lunch odroidc2-eng-32
mmm hardware/hardkernel/libsensor

안드로이드 풀 빌드는 너무 오래 걸리기 때문에 일부 모듈만 빌드하는 mmm을 사용하도록 한다
참고 : http://www.kaisyu.com/notes/google-android/android-partial-module-build

결과

root@ubuntu: # mmm hardware/hardkernel/libsensor
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=5.1.1
TARGET_PRODUCT=odroidc2
...
target Strip: sensors.odroidc2 (out/target/product/odroidc2/obj/lib/sensors.odroidc2.so)
Install: out/target/product/odroidc2/system/lib/hw/sensors.odroidc2.so
make: Leaving directory

#### make completed successfully (1 seconds) ####

8. Install

백업

adb pull /system/lib/hw/sensors.odroidc2.so .

작업한 결과물이 이상할 수 있으므로 미리 백업한다

적용**

cp odroid/android/out/target/product/odroidc2/system/lib/hw/sensors.odroidc2.so /mnt/2/lib/hw/sensors.odroidc2.so

2번째 파티션에 파일을 교체한다


9. Reboot & Run

재부팅 하고 어플리케이션을 돌려본다
디버그 메세지들이 잘 뜨는지 확인


까먹고 안 썼는데 application 구현 참고

https://developer.android.com/reference/android/hardware/SensorManager.html

https://android.googlesource.com/platform/cts/+/ee43e0b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/HeartRateMonitorTestActivity.java


Posted by 쵸코케키

블로그 이미지
chocokeki
쵸코케키

공지사항

Yesterday
Today
Total

달력

 « |  » 2024.3
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

최근에 올라온 글

최근에 달린 댓글

글 보관함