Tiny4412 LED 硬件服务
1.Android系统中启动框架
2.首先实现驱动程序
#include <linux/kernel.h> #include <linux/module.h> #include <linux/miscdevice.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/delay.h> #include <linux/gpio.h> #include <mach/gpio.h> #include <plat/gpio-cfg.h> static int led_gpios[] = { EXYNOS4212_GPM4(0), EXYNOS4212_GPM4(1), EXYNOS4212_GPM4(2), EXYNOS4212_GPM4(3), }; static int led_open(struct inode *inode, struct file *file) { /* 配置GPIO为输出引脚 */ int i; for (i = 0; i < 4; i++) s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT); return 0; } /* app : ioctl(fd, cmd, arg) */ static long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { /* 根据传入的参数设置GPIO */ /* cmd : 0-off, 1-on */ /* arg : 0-3, which led */ if ((cmd != 0) && (cmd != 1)) return -EINVAL; if (arg > 4) return -EINVAL; gpio_set_value(led_gpios[arg], !cmd); return 0; } static struct file_operations leds_ops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = led_open, .unlocked_ioctl = led_ioctl, }; static int major; static struct class *cls; int leds_init(void) { major = register_chrdev(0, "leds", &leds_ops); /* 为了让系统udev,mdev给我们创建设备节点 */ /* 创建类, 在类下创建设备 : /sys */ cls = class_create(THIS_MODULE, "leds"); device_create(cls, NULL, MKDEV(major, 0), NULL, "leds"); /* /dev/leds */ return 0; } void leds_exit(void) { device_destroy(cls, MKDEV(major, 0)); class_destroy(cls); unregister_chrdev(major, "leds"); } module_init(leds_init); module_exit(leds_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("www.100ask.net");
放入内核 drivers/char
修改 drivers/char/Makefile,添加:
obj-y += leds_4412.o
重新编译内核
或者将其编译成模块
#Makefile obj-m := leds_4412.o KDIR := /home/cent/work/androidL_Tiny4412/linux-3.0.86 all: make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm .PHONY: clean clean: rm -f *.ko *.o *.mod.c *.bak *.order *.symvers *~
执行 make后 用adb push 命令将其上传到开发板的/data/local 目录,adb shell 使用 insmod 命令将驱动模块加载到内核。
3.实现HAL层
//led_hal.h #ifndef ANDROID_LED_INTERFACE_H #define ANDROID_LED_INTERFACE_H #include <stdint.h> #include <sys/cdefs.h> #include <sys/types.h> #include <hardware/hardware.h> __BEGIN_DECLS struct led_device_t { struct hw_device_t common; int (*led_open)(struct led_device_t* dev); int (*led_ctrl)(struct led_device_t* dev, int which, int status); }; __END_DECLS #endif // ANDROID_LED_INTERFACE_H
//led_hal.c #define LOG_TAG "LedHal" /* 1. 实现一个名为HMI的hw_module_t结构体 */ /* 2. 实现一个open函数, 它返回led_device_t结构体 */ /* 3. 实现led_device_t结构体 */ /* 参考 hardware\libhardware\modules\vibrator\vibrator.c */ #include <hardware/vibrator.h> #include <hardware/hardware.h> #include <cutils/log.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <hardware/led_hal.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <utils/Log.h> static int fd; /** Close this device */ static int led_close(struct hw_device_t* device) { close(fd); return 0; } static int led_open(struct led_device_t* dev) { fd = open("/dev/leds", O_RDWR); ALOGI("led_open : %d", fd); if (fd >= 0) return 0; else return -1; } static int led_ctrl(struct led_device_t* dev, int which, int status) { int ret = ioctl(fd, status, which); ALOGI("led_ctrl : %d, %d, %d", which, status, ret); return ret; } static struct led_device_t led_dev = { .common = { .tag = HARDWARE_DEVICE_TAG, .close = led_close, }, .led_open = led_open, .led_ctrl = led_ctrl, }; static int led_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device) { *device = &led_dev; return 0; } static struct hw_module_methods_t led_module_methods = { .open = led_device_open, }; struct hw_module_t HAL_MODULE_INFO_SYM = { .tag = HARDWARE_MODULE_TAG, .id = "led", .methods = &led_module_methods, };
#Android.mk LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := led.default LOCAL_MODULE_RELATIVE_PATH := hw LOCAL_C_INCLUDES := hardware/libhardware LOCAL_SRC_FILES := led_hal.c LOCAL_SHARED_LIBRARIES := liblog LOCAL_MODULE_TAGS := eng include $(BUILD_SHARED_LIBRARY)
//com_android_server_LedService.cpp #define LOG_TAG "LedService" #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" #include <utils/misc.h> #include <utils/Log.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <hardware/led_hal.h> namespace android { static led_device_t* led_device; jint ledOpen(JNIEnv *env, jobject cls) { jint err; hw_module_t* module; hw_device_t* device; ALOGI("native ledOpen ..."); /* 1. hw_get_module */ err = hw_get_module("led", (hw_module_t const**)&module); if (err == 0) { /* 2. get device : module->methods->open */ err = module->methods->open(module, NULL, &device); if (err == 0) { /* 3. call led_open */ led_device = (led_device_t *)device; return led_device->led_open(led_device); } else { return -1; } } return -1; } void ledClose(JNIEnv *env, jobject cls) { //ALOGI("native ledClose ..."); //close(fd); } jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status) { ALOGI("native ledCtrl %d, %d", which, status); return led_device->led_ctrl(led_device, which, status); } static const JNINativeMethod methods[] = { {"native_ledOpen", "()I", (void *)ledOpen}, {"native_ledClose", "()V", (void *)ledClose}, {"native_ledCtrl", "(II)I", (void *)ledCtrl}, }; int register_android_server_LedService(JNIEnv *env) { return jniRegisterNativeMethods(env, "com/android/server/LedService", methods, NELEM(methods)); } }
放到Android源码frameworks/base/services/core/jni/com_android_server_LedService.cpp位置
修改frameworks/base/services/core/jni/onload.cpp
添加:
int register_android_server_Watchdog(JNIEnv* env);
+int register_android_server_LedService(JNIEnv *env);
register_android_server_VibratorService(env);
+register_android_server_LedService(env);
package android.os; /** {@hide} */ interface ILedService { int ledCtrl(int which, int status); }
把 ILedService.aidl 放入 frameworks/base/core/java/android/os
package com.android.server; import android.os.ILedService; public class LedService extends ILedService.Stub { private static final String TAG = "LedService"; /* call native c function to access hardware */ public int ledCtrl(int which, int status) throws android.os.RemoteException { return native_ledCtrl(which, status); } public LedService() { native_ledOpen(); } public static native int native_ledOpen(); public static native void native_ledClose(); public static native int native_ledCtrl(int which, int status); }
frameworks/base/services/java/com/android/server/SystemServer.java中注册LedService
Slog.i(TAG, "Vibrator Service");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
+Slog.i(TAG, "Led Service");
+ServiceManager.addService("led", new LedService());
package cc.icen.leddemo; import android.os.RemoteException; import android.app.Activity; import android.os.Bundle; import android.widget.Button; import android.view.View; import android.widget.CheckBox; import android.widget.Toast; import android.os.ILedService; import android.os.ServiceManager; import android.util.Log; public class MainActivity extends Activity { private boolean ledon = false; private Button button = null; private CheckBox checkBoxLed1 = null; private CheckBox checkBoxLed2 = null; private CheckBox checkBoxLed3 = null; private CheckBox checkBoxLed4 = null; private ILedService iLedService = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button) findViewById(R.id.BUTTON); iLedService = ILedService.Stub.asInterface(ServiceManager.getService("led")); if(iLedService == null) Log.d("tian","iLedService is null!!!!"); checkBoxLed1 = (CheckBox)findViewById(R.id.LED1); checkBoxLed2 = (CheckBox)findViewById(R.id.LED2); checkBoxLed3 = (CheckBox)findViewById(R.id.LED3); checkBoxLed4 = (CheckBox)findViewById(R.id.LED4); button.setOnClickListener(new MyButtonListener()); } class MyButtonListener implements View.OnClickListener { @Override public void onClick(View v) { ledon = !ledon; if (ledon) { button.setText("ALL OFF"); checkBoxLed1.setChecked(true); checkBoxLed2.setChecked(true); checkBoxLed3.setChecked(true); checkBoxLed4.setChecked(true); try { for (int i = 0; i < 4; i++) iLedService.ledCtrl(i, 1); } catch (RemoteException e) { e.printStackTrace(); } } else { button.setText("ALL ON"); checkBoxLed1.setChecked(false); checkBoxLed2.setChecked(false); checkBoxLed3.setChecked(false); checkBoxLed4.setChecked(false); try { for (int i = 0; i < 4; i++) iLedService.ledCtrl(i, 0); } catch (RemoteException e) { e.printStackTrace(); } } } } public void onCheckboxClicked(View view) { // Is the view now checked? boolean checked = ((CheckBox) view).isChecked(); try { // Check which checkbox was clicked switch(view.getId()) { case R.id.LED1: if (checked) { // Put some meat on the sandwich Log.d("tian","1"); //Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show(); iLedService.ledCtrl(0, 1); Log.d("tian","2"); } else { // Remove the meat //Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show(); iLedService.ledCtrl(0, 0); } break; case R.id.LED2: if (checked) { // Put some meat on the sandwich //Toast.makeText(getApplicationContext(), "LED2 on", Toast.LENGTH_SHORT).show(); iLedService.ledCtrl(1, 1); } else { // Remove the meat //Toast.makeText(getApplicationContext(), "LED2 off", Toast.LENGTH_SHORT).show(); iLedService.ledCtrl(1, 0); } break; case R.id.LED3: if (checked) { // Put some meat on the sandwich //Toast.makeText(getApplicationContext(), "LED3 on", Toast.LENGTH_SHORT).show(); iLedService.ledCtrl(2, 1); } else { // Remove the meat //Toast.makeText(getApplicationContext(), "LED3 off", Toast.LENGTH_SHORT).show(); iLedService.ledCtrl(2, 0); } break; case R.id.LED4: if (checked) { // Put some meat on the sandwich //Toast.makeText(getApplicationContext(), "LED4 on", Toast.LENGTH_SHORT).show(); iLedService.ledCtrl(3, 1); } else { // Remove the meat //Toast.makeText(getApplicationContext(), "LED4 off", Toast.LENGTH_SHORT).show(); iLedService.ledCtrl(3, 0); } break; // TODO: Veggie sandwich } } catch (RemoteException e) { e.printStackTrace(); } } }
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity" android:orientation="vertical" > <Button android:id="@+id/BUTTON" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/app_name" /> <CheckBox android:id="@+id/LED1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/app_name" android:onClick="onCheckboxClicked" /> <CheckBox android:id="@+id/LED2" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/app_name" android:onClick="onCheckboxClicked" /> <CheckBox android:id="@+id/LED3" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/app_name" android:onClick="onCheckboxClicked" /> <CheckBox android:id="@+id/LED4" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/app_name" android:onClick="onCheckboxClicked" /> </LinearLayout>
#Android.mk LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # Build all java files in the java subdirectory LOCAL_SRC_FILES := $(call all-subdir-java-files) # Name of the APK to build LOCAL_PACKAGE_NAME := LEDDemo # Tell it to build an APK include $(BUILD_PACKAGE)
android应用如何访问C库
安卓应用是由java编写的,而在android底层,如安卓底层的硬件驱动程序,都是由C/C++编写的,那应用层java是必须通过某种手段才能使用底层提供的硬件资源。毫无疑问,在java看要调用底层C实现的代码,中间必须要有一层接口,那就是JNI。本例要实现的就是JAVA如何访问底层C库。
假设JAVA应用需要实现的功能是控制底层硬件LED设备,包括开,关及控制操作。需要实现下述流程:
1 实现一个JAVA硬件控制类,声明本地的Native方法(操作硬件的接口),并使用static模块加载C的动态库。实现代码如下
public class HardControl{ //声明本地的Native方法 public static native int ledCtrl(int which, int status); public static native int ledOpen(); public static native void ledClose(); //我们要在这个java程序里面加载C库,定义一个static模块 static { //用System方法来加载C库 try { //添加捕获异常代码的快捷键为:ctrl+alt+T(前提先用鼠标选中需要捕获的代码) System.loadLibrary("hardcontrol"); //参数为C库的库名 } catch (Exception e) { e.printStackTrace(); } } }
注意,加载C库的方法为System.loadLibrary.
#include <jni.h> #include <stdio.h> #include <stdlib.h> #include <android/log.h> /* liblog */ jint ledOpen(JNIEnv *env, jobject cls) { __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native_ledOpen"); //调用这句就可以打印了。 return 0; } void ledClose(JNIEnv *env, jobject cls) { __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native_ledClose"); //调用这句就可以打印了。 } void ledCtrl(JNIEnv *env, jobject cls, jint which, jint status) { __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native_ledCtrl:%d %d", which, status); //调用这句就可以打印了。 return 0; } static const JNINativeMethod methods[] = { {"ledOpen", "()I", (void *)ledOpen}, //() inside empty=> no params, 'I' means: return type is int {"ledClose", "()V", (void *)ledClose},//() inside empty=> no params, 'V' means: return type is void {"ledCtrl", "(II)I", (void *)ledCtrl}, //()inside two I => means two params, types is int, ouside () is 'I' ==> return type is int }; /* System.LoadLibrary */ JNI_OnLoad(JavaVM *jvm, void *reserved) { JNIEnv *env; jclass cls; if((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4) != JNI_OK) { return JNI_ERR;; //jni version not supported } //find the class that realize the local native method: *.java, here is HardControl //PS:here need to indification the package & class name. // "." --> "/" cls = (*env)->FindClass(env, "com/example/zhm/hardlibrary/HardControl"); if( cls == NULL ) { return JNI_ERR; } //find the class then register their methods . Java's method <==correspond with ==> C's method if((*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])) < 0 ) // 3: means there are 3 funcs in methods. { } }