Tiny4412 LED 硬件服务

时间:2022-11-12 21:47:27

1.Android系统中启动框架

Tiny4412 LED 硬件服务

Tiny4412 LED 硬件服务

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(),
EXYNOS4212_GPM4(),
EXYNOS4212_GPM4(),
EXYNOS4212_GPM4(),
}; static int led_open(struct inode *inode, struct file *file)
{
/* 配置GPIO为输出引脚 */
int i;
for (i = ; i < ; i++)
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT); return ;
} /* 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 != ) && (cmd != ))
return -EINVAL; if (arg > )
return -EINVAL; gpio_set_value(led_gpios[arg], !cmd); return ;
} 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(, "leds", &leds_ops); /* 为了让系统udev,mdev给我们创建设备节点 */
/* 创建类, 在类下创建设备 : /sys */
cls = class_create(THIS_MODULE, "leds");
device_create(cls, NULL, MKDEV(major, ), NULL, "leds"); /* /dev/leds */ return ;
} void leds_exit(void)
{
device_destroy(cls, MKDEV(major, ));
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.
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 ;
} static int led_open(struct led_device_t* dev)
{
fd = open("/dev/leds", O_RDWR);
ALOGI("led_open : %d", fd);
if (fd >= )
return ;
else
return -;
} 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 ;
} 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)
把新文件放到Android源码中, 所在目录:
hardware/libhardware/include/hardware/led_hal.h
hardware/libhardware/modules/led/led_hal.c
hardware/libhardware/modules/led/Android.mk
 
编译:
$ mmm hardware/libhardware/modules/led
 
4.实现JAVA硬件服务JNI层
//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 == ) {
/* 2. get device : module->methods->open */
err = module->methods->open(module, NULL, &device);
if (err == ) {
/* 3. call led_open */
led_device = (led_device_t *)device;
return led_device->led_open(led_device);
} else {
return -;
}
} return -;
} 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);

修改 frameworks/base/services/core/jni/Android.mk :
  $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_LedService.cpp \
 
5.实现AIDL代码
package android.os;

/** {@hide} */
interface ILedService
{
int ledCtrl(int which, int status);
}

把 ILedService.aidl 放入 frameworks/base/core/java/android/os

 修改 frameworks/base/Android.mk  添加一行
         core/java/android/os/IVibratorService.aidl \
+        core/java/android/os/ILedService.aidl \
 
 
 mmm frameworks/base
它会生成: ./out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java
 
6.实现LedService JAVA层代码
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());

上传到Android源码目录中
frameworks/base/services/java/com/android/server/SystemServer.java
 
不需要修改 frameworks/base/services/core/Android.mk
它的内容里已经把该目录下所有JAVA文件自动包含进去了:
LOCAL_SRC_FILES += \
    $(call all-java-files-under,java)
 
编译:
$ mmm frameworks/base/services
 
7.Android应用程序中使用
 
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.

 
 2 JAVA本地native方法与C库接口的关联
 
 
#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 ;
} 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 ;
} 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[])) < ) // 3: means there are 3 funcs in methods.
{ } }
如果某个硬件资源只能被某一个应用使用,可以使用下面的方法访问硬件:JAVA APP--->JNI_OnLoad()加载C库---->将JAVA三个地方法与C库函数进行关联并注册---->调用JAVA本地Native方法就可以访问C库的C接口------>进而访问硬件驱动中的open, read, write,从进访问硬件。但是,以上场景仅限于只有一个APP使用这个硬件资源,如果有多个应用想要使用某个硬件时,如果还按上面方法,必须会造成硬件资源的冲突,所以此时需要有一种框架来解决这个问题。解决方案就是访问硬件资源的程序只能并且只有一个,我们称之为System Server, 其它要访问这个硬件资源的APP必须要给Server发请求,由Server间接的操作硬件,从而实现资源的访问。这个就称之为硬件访问服务。
关于硬件访问服务需要注意以下几点:
1 System Server是由JAVA编写的,所以它要想访问硬件,必须要加载JNI的C库(Loadlibrary).
2 C库的JNI_Onload函数里要注册本地方法,分别调用各个硬件的函数来注册本地方法。比如LED,振动器,有串口。。。等等。。
3 System Server:
(1)对每个硬件都要添加服务,add service
 前提需要实现的是:对每个硬件构造service,使用本地Native方法
(2)对于(1)添加的服务就是向service_manager.c注册,比如serialservice, vibratorservice, ledservice等。如果JAVA应用程序需要使用某些Service的时候,就需要通过这个Service_manager查询及获取相应的Service。
4 最终APP怎么使用?
(1)APP使用之前需要获得这个服务getService
(2)最后就是使用这个服务了。执行Service的方法
以后修改硬件驱动的时候,把驱动文件放在hal里面,如hal_led.c,有几个好处:
(1)容易修改
(2)很多公司不愿意开放其硬件操作,他们只提供so文件,出于保密的目的。试想一下,如果把硬件操作源代码放到JNI文件里,如果要修改,需要编译整个工程,此外,硬件源代码暴露出来了,保密性不好。
分析一下:
以上操作涉及到三个进程, 
1 SystemServer进程:它提供的功能如下:
---a: 它向service_manager.c注册服务
---b: 加载硬件Service JNI 的C库
---C: 接收其它app的硬件操作请求,访问硬件资源
2 Service_Manager进程:负责硬件资源各种Service的注册添加,以及接怍JAVA应用app的各种service查询请求及资源的获取。
3 JAVA应用APP进程,它其实是一个客户端,它首先向Service_Manager查询获得某一个Service, 最后,把这个Service发送给SystemServer进程以请求相应的服务,
  而以上三个进程之间的内部通信,主要依靠Android内核的Binder Driver系统进行内部进程间通信。这个Binder并不是linux内核自带的,是google公司对linux内核进行修改添加的一个驱动程序,可实现更加高效的进程间通信。
 
思考:如何实现一个硬件访问服务。
1 编写JNI和HAL,以led为例,先编写com_android_server_ledservice.cpp,用于注册JNI本地方法。再编写hal_led.c,里面就是实现open,read,write等硬件访问接口。
2 修改onload.cpp,调它调用com_android_server_ledservice.cpp内实现的函数,
3 修改system server.java, 
   new ledservice()
   add ledservice()
4 编写LEDService.java,用于调用本地方法,实现硬件操作
5 编写ILEDService.java接口给app使用。
 
Android硬件抽象层模块开发
 
Tiny4412 LED 硬件服务
 
Android硬件访问服务开发
Tiny4412 LED 硬件服务
 

Tiny4412 LED 硬件服务