저번에 작성한 파일은 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)