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)
把新文件放到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 == 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);

修改 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 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.
    {
        
    }

}

 

如果某个硬件资源只能被某一个应用使用,可以使用下面的方法访问硬件: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硬件抽象层模块开发
 
 
Android硬件访问服务开发

 

 

 

posted @ 2016-08-10 22:47  CrazyDiode  阅读(730)  评论(0编辑  收藏  举报