'단체/ㄴKETI 연수생'에 해당되는 글 2건

  1. 2012.10.25 JNI를 사용하여 GPIO LED 컨트롤
  2. 2012.10.20 LED GPIO 드라이버 추가하기

JNI를 사용하여 GPIO LED 컨트롤

Posted in 단체/ㄴKETI 연수생 by

저번에 작성한 파일은 Syscall을 이용하여 디바이스를 컨트롤했지만 이번에는 sysfs의 attribute를 사용하여 컨트롤 해보자


디바이스 드라이버 컨트롤 4가지 방식 : proc, sysfs, syscall, 디바이스모드 파일 


### FLOW CHART ###

1. 디바이스 드라이버 제작(myled.c)

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/errno.h>

#include <linux/kernel.h>

#include <linux/major.h>

#include <linux/mutex.h>

#include <linux/proc_fs.h>

#include <linux/seq_file.h>

#include <linux/stat.h>

#include <linux/init.h>

#include <linux/device.h>

#include <linux/tty.h>

#include <linux/kmod.h>

#include <linux/gfp.h>

#include <linux/spinlock.h>

#include <linux/delay.h>

#include <linux/platform_device.h>

#include <linux/gpio.h>

#include <linux/sysfs.h>

#include <linux/myled.h>

#include <plat/gpio-cfg.h>

#include <asm/uaccess.h>

static ssize_t set_myled(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);

static DEVICE_ATTR(led, S_IWUGO, NULL, set_myled);

static struct myled_platform_data *pdata;

static struct attribute *myled_sysfs_entries[] = {

&dev_attr_led,

NULL

};

static struct attribute_group myled_sysfs_attr_group = {

.name = NULL,

.attrs = myled_sysfs_entries,

};

static ssize_t set_myled(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

{

unsigned int val;

if(!(sscanf(buf, "%d\n", &val))) return -EINVAL;

gpio_set_value(pdata->gpio_tx, ((val != 0) ? 1 : 0));

return count;

}

static int myled_probe(struct platform_device *pdev)

{

int err;

pdata = pdev->dev.platform_data;

err = gpio_request(pdata->gpio_tx, pdata->gpio_tx_bank);

if (err < 0) {

pr_err("myled : failed to request GPIO %d,"

" error %d\n", pdata->gpio_tx, err);

goto fail;

}

gpio_direction_output(pdata->gpio_tx, 0);

return sysfs_create_group(&pdev->dev.kobj, &myled_sysfs_attr_group);

fail:

gpio_free(pdata->gpio_tx);

platform_set_drvdata(pdev, NULL);

return err;

}

static int myled_remove(struct platform_device *pdev)

{

return 0;

}

static struct platform_driver myled_device_driver = {

.probe = myled_probe,

.remove = myled_remove,

.driver = {

.name = "myled",

.owner = THIS_MODULE,

}

};

static int __init myled_init(void)

{

platform_driver_register(&myled_device_driver);

printk(KERN_INFO "============= myled init OK =============\n");

return 0;

}

static void __exit myled_exit(void)

{

platform_driver_unregister(&myled_device_driver);

}

module_init(myled_init);

module_exit(myled_exit);

MODULE_LICENSE("GPL");

MODULE_DESCRIPTION("GPIO LED TEST");


2. 안드로이드 APK 제작(myled.apk)

    1) Eclipse에서 MyLed 프로젝트를 작성

    2) java 파일에서 public native 반환자료형 함수명(...);으로 C언어로 사용할 함수명을 선언

    3) XML파일을 작성

    4) MyLed 프로젝트 아래에 Eclipse에서 JNI 폴더 생성

    5) 프로젝트\bin\class로 이동 후 Javah -classpath C:\CookAndroid\android-sdk\platforms\

        android-8\android.jar; com.example.myled.MainActivity 실행하여 헤더파일 작성

        [platforms\android-OS버전별 API레벨\android.jar; 패키지명.MainClass명]

        → 이 때 JAVA파일에 선언된 native 함수명을 참조하여 헤더파일을 만드므로 미리 선언해놔야한다.

     6) 프로젝트\bin\class에 생성된 헤더파일을 JNI폴더 아래로 복사

     7) JNI 폴더 아래 myled.c 작성

         JNIEXPORT 반환자료형 JNICALL (위에서 생성된 헤더파일 안에 선언된 함수명 - 동일해야 링크됨)

     8) JNI 폴더 아래 Android.mk 파일 작성

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := myled

LOCAL_SRC_FILES := myled.c

include $(BUILD_SHARED_LIBRARY)

9) Cygwin 창에서 /cygdrive/c/CookAndroid/Project/HelloWorld/JNI로 이동 후

    $/cygdrive/c/CookAndroid/android-ndk-r8b/ndk-build

10) MyLed/libs/armeabi/libmyled.so 파일 생성 확인, MyLed/bin/Myled.apk 생성된 것을 sdcard에 복사후 설치


3. MyLed\JNI\myled.c 소스 파일

#include <stdlib.h>

#include "android_log.h"

#include "com_example_myled_MainActivity.h"


JNIEXPORT void JNICALL Java_com_example_myled_MainActivity_LED_1ON(JNIEnv *env, jobject jObj){

system("echo 0 > /sys/devices/platform/myled.0/led");

}

JNIEXPORT void JNICALL Java_com_example_myled_MainActivity_LED_1OFF(JNIEnv *env, jobject jObj){

system("echo 1 > /sys/devices/platform/myled.0/led");

}


4. MyLed\JNI\Android.mk 소스 파일 (log를 사용하기 위해 2,3줄 추가)

LOCAL_PATH := $(call my-dir)

LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog

LOCAL_CFLAGS := -DCONFIG_EMBEDDED\ -DUSE_IND_THREAD\


include $(CLEAR_VARS)

LOCAL_MODULE := myled

LOCAL_SRC_FILES := myled.c


include $(BUILD_SHARED_LIBRARY)


LED GPIO 드라이버 추가하기

Posted in 단체/ㄴKETI 연수생 by

### Flow Chart ###

ㅁㅁㅁ        하드웨어        ㅁㅁㅁ

ㅁㅁㅁ        커널              ㅁㅁㅁ

   - kernel/arch/arm/mach-s5pv210/mach-mango210.c : platform_data 및 platfom_device 생성

   - kernel/drivers/char/IR/ir-nec-encoder.c : driver를 통해 .name이 같은 device를 찾아 data 구조체를 갖고와서 컨트롤 

ㅁㅁㅁ        System call    ㅁㅁㅁ

   - System call을 이용하여 ir-nec-encoder에 정의되어 있는 함수를 호출

ㅁㅁㅁ        Application     ㅁㅁㅁ

   - App에서 System call을 호출하여 커널 디바이스 드라이버를 컨트롤


### 01. Platform data 및 device 생성하기 ###

1. 아래에서 생성할 Platform data 구조체를  : include/linux/myled.h

    → 2번에서 생성할 data 구조체를 정의해놓는다. (참고 : include/linux/smr210_ir_nec_encoder.h)

2. Data 및 Device 생성 : /kernel/arch/arm/mach-s5pv210/mach-mango210.c

    → mango210.c에서 CONFIG_SMR210_IR_NEC_ENCODER 심벌로 정의되어 있는 부분 참고(2부분)

    → 1부분 : Data 구조체와 Device 구조체 생성 // 2부분 *mango210_device[] __initdata에 생성한 Device 추가

    → 새로 정의하는 심벌이름은 CONFIG_MY_GPIO_LED로 하겠다

    → LED 주소값 설정 : (data구조체 변수).gpio_tx = S5PV210_GPH2(0)


### 02. LED 드라이버 추가 ###

1. 위에서 정의한 data와 device를 컨트롤할 드라이버 구현

    → drivers/leds/폴더명 (참조 : drivers/char/IR/Makefile, Kconfig, ir-nec-encoder.c 3파일 Modify)

    → ir-nec-encoder.c 파일이름을 변경 : mv ir-nec-encoder.c myled.c

    → Makefile 수정 : 심볼이름을 위에서 정의한 심볼(CONFIG_MY_GPIO_LED)로 바꾸고 오브젝트 파일을 myled.o로 바꾼다

    → KConfig 수정 : 기존 내용을 다 지우고 아래 내용을 추가해준다 (menuconfig가 CONFIG_라는 이름을 자동으로 붙여줌)

config MY_GPIO_LED

bool "GPH2[0] LED ADD SYMBOL"


2. 드라이버 기능을 myled.c 파일에 선언과 정의

     → 01번에서 정의한 Platform data 구조체를 사용해야 하니 #include <linux/myled.h>를 해준다

     → static struct platform_driver myled_device_driver을 정의해주고 구조체변수들을 입력

     → driver의 구조체변수 .probe, .remove 함수포인터 전달 & .driver = {.name="myled"}은 mach-mango210.c에서 

         "myled"와 같은 드라이버를 찾아서 그 디바이스 주소값을 넘겨준다.(이를 통해 접근하면서 디바이스를 컨트롤)

     → static int __init myled_init() 정의와 안에 platform_driver_register(&myled_device_driver)를 통해 드라이버 등록

     → static void __exit myled_exit() 정의와 안에 platform_driver_unregister(&myled_device_driver)를 통해 드라이버 해제

     → module_init(myled_init) 선언 - insmod로 불려지는 함수, module_exit(myled_exit) 선언 - rmmod로 불려지는 함수 


### 03. Makefile, Kconfig 의존성 해결(Menuconfig 추가하기) ###

1. Makefile 의존성 해결

    → myled의 Makefile이 실행되기 위해 상위 Makefile을 수정해줘야 한다

    → leds/Makefile에 obj-$(CONFIG_MY_GPIO_LED)    += myled/ 를 추가시켜준다

2. Kconfig 의존성 해결

    → make menuconfig 에서 CONFIG_MY_GPIO_LED을 체크하려는 창을 만들려면 myled의 Kconfig를 수정해야한다.

    → if NEW_LEDS 아래에 source "drivers/leds/myled/Kconfig"를 추가해준다

    → 그러면 make menuconfig에서 drivers-LED를 체크해주면 NEW_LEDS가 정의되어 있으니 Source 파일이 활성화되면서 

  우리가 추가해준 myled의 Kconfig가 보인다

    → MY_GPIO_LED 심볼을 활성화하여 CONFIG_MY_GPIO_LED 심볼이 활성화되도록 한다

    → kernel/.config 와 kernel/include/linux/autoconf.h 에서 CONFIG_MY_GPIO_LED 심볼이 추가됐는지 확인


### 04. System Call 추가하기 ###

1. System Call 번호 할당 : $ /kernel/arch/arm/include/asm/unistd.h (각 시스템 호출의 고유 번호가 정의되어 있음)

    → 새로 추가할 System call의 고유번호 정의 추가 : #define __NR_my_gpio_led                                [__NR_심볼명]

2. System Call 테이블에 처리 함수 등록 : $ /kernel/arch/arm/kernel/calls.S

    → System Call 처리함수 등록 : CALL(sys_my_gpio_led)                                                      [CALL(sys_심볼명)]

    → entry-common.S파일에서 ENTRY(sys_call_table)이 calls.S를 include하고 있기 때문에 calls.S를 수정하는 것

3. 처리 함수 구현 : /kernel/kernel/my_gpio_led.c                                                                                 [파일명.c]

    → System Call 이 발생했을 때 수행될 함수를 구현

    → asmlinkage 반환자료형 sys_my_gpio_led(...) { 내용 } : 2번에서 CALL로 등록한 이름과 동일해야 함 [sys_심볼명]

4. Makefile 수정 : /kernel/kernel/Makefile 에 3번에서 구현한 '파일명.c'의 오브젝트 파일을 생성할 수 있도록 조건 추가

    → obj-y    = ***.o ***.o my_gpio_led.o                                                                                           [파일명.o]

5. Kernel 컴파일 수행 및 커널 이미지 복사 : ./fastbuild ( make && cp zImage ../../sdboot/zImage )

6. Kernel 이미지 퓨징 : sudo ./sdwriter_maple210 sdb


### 05. Android App 추가하기 : System call Test ###

1. Application 작성 : Android_work/external/디렉토리

    → external에 새로운 작업 디렉토리를 만든다

    → 작업디렉토리에 소스파일(my_gpio_led.c)를 만들고 System call를 호출한다(syscall(__NR_my_gpio_led))

2. 안드로이드 어플 Makefile 작성 : Android_work/external/디렉토리/Android.mk

    → 파일명.c은 위에서 작성한 소스파일명과 동일, 모듈이름은 안드로이드 프롬프트에서 실행할 명령어

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_C_INCLUDES:= \

$(LOCAL_PATH)/include \

$(LOCAL_PATH)/android

LOCAL_SRC_FILES:= \

my_gpio_led.c                                                                                                          [파일명.c]

LOCAL_MODULE:= my_gpio_led                                                                                     [실행 명령어]

include $(BUILD_EXECUTABLE)

3. 컴파일 및 설치 : Android_work/

    → 컴파일 한 후 실행파일을 rootfs/system/bin에 복사

$ . build/envsetup.sh

$ chooseproduct sec_smdkv210

$ mmm external/디렉토리/

$ cp out/target/product/smdkv210/system/bin/my_gpio_led rootfs/system/bin

4. rootfs 를 묶어서 sdboot로 이동 : Android_work/rootfs/

    → rootfs 폴더에서 $ tar cvfz ../../sdboot/maple210_android210.tgz .

    → Kernel 이미지와 안드로이드 이미지 퓨징 $ sudo ./sdwrite_maple210 sdb

5. 테스트

    → sh-4.0 # 모듈명