一、内核驱动
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