从内核LED驱动到APK应用实例(旧HAL架构 )

时间:2012-08-07作者:klpeng分类:swoole浏览:1929评论:0

一、内核驱动

 myled/
├── Kconfig
├── Makefile
└── led.c

/*led.c*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/highmem.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>

#define GPMBASE 0x7f008000
#define GPMCON (GPMBASE + 0x820)
#define GPMDAT (GPMBASE + 0x824)
#define LED_ON      1111
#define LED_OFF     2222

static void *gpio_virt;
static struct resource *conf_res, *data_res;

static int led_open(struct inode *inode, struct file *filp)
{
    int ret = 0;
    return ret;
}

static long led_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
    int ret = 0;

    switch (cmd) {
    case LED_ON:
        iowrite32(ioread32(GPMCON) & ~0xffff, GPMCON);
        iowrite32(ioread32(GPMCON) | 0x1111, GPMCON);
        iowrite32(ioread32(GPMDAT) | ~0xF, GPMDAT);
        break;
    case LED_OFF:
        iowrite32(ioread32(GPMCON) & ~0xffff, GPMCON);
        iowrite32(ioread32(GPMCON) | 0x1111, GPMCON);
        iowrite32(ioread32(GPMDAT) | 0xf, GPMDAT);
        break;
    }
    return ret;
}

static int led_release(struct inode *inode, struct file *filp)
{
    int ret = 0;
    return ret;
}

struct file_operations ledops = {
    .open       = led_open,
    .unlocked_ioctl      = led_ioctl,
    .release    = led_release,
};

struct miscdevice led_misc = {
    .name = "led_misc",
    .minor= 100,
    .fops = &ledops,
};

static int led_probe(struct platform_device *dev)
{
    int ret = -1;
    
    conf_res = request_mem_region(GPMCON, 4, "myled_conf");
    if (conf_res == NULL) 
    {
        goto ERR0;
    }

    data_res = request_mem_region(GPMDAT, 4, "myled_dat");
    if (data_res == NULL)
    {
        goto ERR1;
    }
   
    gpio_virt = ioremap(GPMBASE, SZ_4K);
    if (gpio_virt == NULL)
    {
        goto ERR2;
    }

    ret = misc_register(&led_misc);
    if (ret)
    {
        goto ERR3;
    }
    return ret;

ERR3:
    iounmap(gpio_virt);
ERR2:
    release_resource(data_res);
ERR1:
    release_resource(conf_res);
ERR0:
    return ret;
}

static int led_remove(struct platform_device *dev)
{
    int ret = 0;
    misc_deregister(&led_misc);
    iounmap(gpio_virt);
    return ret;
}

static void led_pdev_release(struct device *dev)
{

}

struct platform_device led_pdev = {
    .name = "s3c6410_led",
    .dev  = {
        .release = led_pdev_release,
    },
};

struct platform_driver led_pdrv = {
    .probe  = led_probe,
    .remove = led_remove,
    .driver = {
        .name = "s3c6410_led",
    }
};

static int __init led_init(void)
{
    int ret;

    ret = platform_device_register(&led_pdev);
    if (ret) {
        goto ERR0;        
    }
    ret = platform_driver_register(&led_pdrv);
    
    if (ret) {
        goto ERR1;
    }
    return ret;
ERR1:
    platform_device_unregister(&led_pdev);
ERR0:
    return ret;
}

static void __exit led_exit(void)
{
    platform_device_unregister(&led_pdev);
    platform_device_unregister(&led_pdev);
}


module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Micky Liu");

/*Makefile*/
obj-$(CONFIG_MY_LED_TEST) = myled.o

/*Kconfig*/
config MY_LED_TEST
    tristate "MY_LED_TEST"
    ---help---
    It's my led test.

myled是放在android-kernel-*.*.*/drivers/目录下面的。

在drivers目录下Makefile中加入一行:

   obj-$(CONFIG_MY_LED_TEST)   += myled/
在drivers目录下的Kconfig中加入一行:    

   source "drivers/myled/Kconfig"

接着在内核根目录下make menuconfig,找到刚才的MY_LED_TEST选项,设置成*号,然后保存,make。OK,现在LED驱动已经编译进内核了。


2、编写JNI代码

jni
├── Android.mk
└── led.c

/*led.c*/
#include <jni.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <errno.h>

#define LOG_TAG "LED_JNI"
#undef LOG
#include <utils/Log.h>

#define LED_ON  1111
#define LED_OFF 2222

static jboolean turn_led(int cmd)
{
    int fd, ret = -1;
    fd = open("/dev/led_misc", O_RDWR);
    if (fd < 0)
    {
        goto ERR0;
    }

    ret = ioctl(fd, cmd);
    close(fd);

    if (ret)
    {
        LOGE("ioctl /dev/led_misc failed!");
        goto ERR0;
    }
    return JNI_TRUE;
ERR0:
    return JNI_FALSE;
}

/*
 * Class:       com_lhw_framework_led_Led
 * Method:      turnOn
 * Signature:   ()Z
 */
JNIEXPORT jboolean JNICALL com_lhw_led_Led_turnOn(JNIEnv *env, jobject thiz)
{
    LOGD("LED ON!");
    return turn_led(LED_ON);
}

/*
 * Class:       com_lhw_framework_led_Led
 * Method:      turnOff
 * Signature:   ()Z
 */
JNIEXPORT jboolean JNICALL com_lhw_led_Led_turnOff(JNIEnv *env, jobject thiz)
{
    LOGE("LED OFF!");
    return turn_led(LED_OFF);
}

static JNINativeMethod gMethods[] = {
    {"turnOn", "()Z", (void *)com_lhw_led_Led_turnOn},
    {"turnOff", "()Z", (void *)com_lhw_led_Led_turnOff},
};

static int register_com_lhw_led_Led(JNIEnv *env) {
    jclass clazz = (*env)->FindClass(env, "com/lhw/framework/led/Led");
    return (*env)->RegisterNatives(env, clazz, gMethods, 
                sizeof(gMethods) / sizeof(gMethods[0]));
}

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
    JNIEnv *env = NULL;
    jint result = -1;
    if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("ERROR: GetEnv failed!\n");
        goto bail;
    }

    if (register_com_lhw_led_Led(env) < 0) {
        LOGE("ERROR: LED_JNI native registration failed!\n");
        goto bail;
    }

    result = JNI_VERSION_1_4;
bail:
        return result;
}


/*Android.mk*/

LOCAL_PATH 			:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES 	:= led.c

LOCAL_MODULE		:= libled_jni
LOCAL_MODULE_TAGS	:= libled_jni

LOCAL_PRELINK_MODULE	:= false
LOCAL_C_INCLUDES		:= $(JNI_H_INCLUDE)
LOCAL_SHARED_LIBRARIES	:= \
						libc \
						libutils


include $(BUILD_SHARED_LIBRARY)

接着使用在Android源码目录下使用mmm命令编译.

mmm /home/administrator/workspace/android_test/led/ljni

cp system/lib/libled_jni.so /nfsroot1/system/lib/


3、framework jar包

framework/
├── Android.mk
└── com
    └── lhw
        └── framework
            └── led
                └── Led.java


/*Led.java*/
package com.lhw.framework.led;

import android.util.Log;

/*
 * Led 操作Jar库
 * @author Micky Liu
 * @email sglazelhw@126.com
 */
public class Led {
    
    static {
        System.loadLibrary("led_jni");
    }

    public native boolean turnOn();
    public native boolean turnOff();
   
    /*开灯*/
    public boolean turnLedOn() {
        return turnOn();
    }

    /*关灯*/
    public boolean turnLedOff() {
        return turnOff();
    }
}

/*Android.mk*/

LOCAL_PATH		:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_SRC_FILES			:= $(call all-subdir-java-files)
LOCAL_MODULE			:= led
LOCAL_MODULE_TAGS		:= led

LOCAL_JAVA_LIBRARIES	:=
LOCAL_STATIC_JAVA_LIBRARIES :=

include $(BUILD_JAVA_LIBRARY)

接着使用在Android源码目录下使用mmm命令编译.

mmm /home/administrator/workspace/android_test/led/framework

cp system/framework/led.jar /nfsroot1/system/framework/


4、APK应用

使用如下命令创建Android应用:

android create project -t 1 -k com.lhw.led_test -a MainActivity -p ./apk

led_apk
├── AndroidManifest.xml
├── Android.mk
├── bin
├── default.properties
├── libs
├── res
│   ├── drawable-hdpi
│   │   └── icon.png
│   ├── drawable-ldpi
│   │   └── icon.png
│   ├── drawable-mdpi
│   │   └── icon.png
│   ├── layout
│   │   └── main.xml
│   └── values
│       └── strings.xml
└── src
    └── com
        └── lhw
            └── led_test
                └── MainActivity.java

/*MainActivity.java*/

package com.lhw.led_test;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.view.View;
import android.view.View.OnClickListener;

import com.lhw.framework.led.Led;

public class MainActivity extends Activity
{
    private Button btnOn;
    private Button btnOff;
    private TextView tvState;
    private View.OnClickListener listener;

    private Led led;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        btnOn = (Button) findViewById(R.id.btn_on);
        btnOff = (Button) findViewById(R.id.btn_off);
        tvState = (TextView) findViewById(R.id.tv_state);

        listener = new MyListener();
        btnOn.setOnClickListener(listener);
        btnOff.setOnClickListener(listener);
        
        led = new Led();
    }

    class MyListener implements View.OnClickListener {
        public void onClick(View v) {
            switch(v.getId())
            {
            case R.id.btn_on:
                if(led.turnLedOn()) {
                    tvState.setText("LED ON"); 
                }
                break;
            case R.id.btn_off:
                if(led.turnLedOff()) {
                    tvState.setText("LED OFF");
                }
                break;
            default:
                break;
            }
        }
    }
}


<!-- main.xml -->


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView
        android:id="@+id/tv_state"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/btn_on"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/turn_on" />
    <Button
        android:id="@+id/btn_off"
        android:layout_marginLeft="10dip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/turn_off" />
</LinearLayout>

<!-- AndroidManifest -->

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.lhw.led_test"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:label="@string/app_name" android:icon="@drawable/icon">
        <uses-library android:name="led" /> 
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

/*Android.mk*/


LOCAL_PATH				:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_SRC_FILES			:= $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME		:= led_test

LOCAL_JAVA_LIBRARIES	:= led 
LOCAL_STATIC_JAVA_LIBRARIES	:=

include $(BUILD_PACKAGE)


接着使用在Android源码目录下使用mmm命令编译.

mmm /home/administrator/workspace/android_test/led/led_apk

cp system/framework/led_apk.apk  /nfsroot1/system/app/


===================================================

到这,APK-->> framework  -->> JNI -->> Kernel Driver,整个过程都已完成。可以从Android应用里面控制LED等的亮和灭了。

在这个过程中,遇到很多问题,这里小记一下:

     一、在超级中端里不能执行system/bin下的一些命令,解决方法:

     修init.rc文件

      service console /system/bin/sh
          console
          disabled
          user  sh
          group log
     修改后设置:
     service console /system/bin/sh
        console
        disabled
        user root
        group log


     二、在JNI里打开/dev/下的设备文件报没权限错误,解决办法:

         方法一、进入高级终端,chmod 777 /dev/led_misc
         方法二、在init.rc中加入这么一行:chmod 777 /dev/led_misc


打赏
文章版权声明:除非注明,否则均为彭超的博客原创文章,转载或复制请以超链接形式并注明出处。
相关推荐

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

猜你喜欢