Android7 Led闪光灯的控制 从底层到上层

Android Version:v7.1.1
Linux Version:V4.1.15
Hardware:IMX6Q

1.底层led驱动程序,为上层提供/dev/led

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>

#define DRIVER_NAME "flash_led"
#define DEVICE_NAME "flash_led"
#define MISC_NAME   "led"

#define MISC_IOC_MAGIC 'M'
#define LED_ON _IO(MISC_IOC_MAGIC, 0x01)
#define LED_OFF _IO(MISC_IOC_MAGIC, 0x02)

struct flash_led_data {
	unsigned int gpio;
};
static int gpio = 0;

static long flash_led_misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
	
	if(_IOC_TYPE(cmd)!= MISC_IOC_MAGIC)
	  return -EFAULT;

	switch(cmd){
		case LED_ON:
			gpio_set_value(gpio, 1);
			break;
		case LED_OFF:
			gpio_set_value(gpio, 0);
			break;
		default:
			break;
	}

	return 0;
}

static const struct file_operations flash_led_misc_fops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = flash_led_misc_ioctl,
};

static struct miscdevice flash_led_misc_device = {
	.minor = MISC_DYNAMIC_MINOR,
	.name  = MISC_NAME,
	.fops  = &flash_led_misc_fops,
};


static int flash_led_probe(struct platform_device *pdev){
	struct device_node *np = pdev->dev.of_node;
	struct flash_led_data *data;
	int ret = 0;

	if(np == NULL){
		return 0;//void the probe exec twice
	}
	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
	if(data == NULL)
		return -ENOMEM;

	data->gpio = of_get_named_gpio(np,"gpios", 0);
	gpio = data->gpio;

	ret = devm_gpio_request_one(&pdev->dev, data->gpio,GPIOF_OUT_INIT_LOW,NULL);
	if(ret < 0)
	  printk("failed to request flash led gpio\n");
	misc_register(&flash_led_misc_device);


	return 0;
}

static int flash_led_remove(struct platform_device *pdev){
	struct device_node *np = pdev->dev.of_node;
	if(np == NULL)
	  return 0;

	misc_deregister(&flash_led_misc_device);
	return 0;
}

static int flash_led_suspend(struct platform_device *pdev, pm_message_t state){

	return 0;
}

static int flash_led_resume(struct platform_device *pdev)
{
	return 0;
}

static void flash_led_device_release(struct device *pdev){

	return ;
}

static struct of_device_id flash_dt_ids[] = {
	{ .compatible = "flash-leds"},
	{}
};

MODULE_DEVICE_TABLE(of, flash_dt_ids);

static struct platform_driver flash_led_driver = {
	.probe  = flash_led_probe,
	.remove = flash_led_remove,
	.driver = {
		.name  = DRIVER_NAME,
		.owner = THIS_MODULE,
		.of_match_table = of_match_ptr(flash_dt_ids),
	},
	.suspend = flash_led_suspend,
	.resume  = flash_led_resume,
};


static struct platform_device flash_led_device = {
	.name = DEVICE_NAME,
	.dev = {
		.release = flash_led_device_release,
	}
};

static int __init flash_led_init(void){
	platform_device_register(&flash_led_device);
	platform_driver_register(&flash_led_driver);
	return 0;
}

static void __exit flash_led_exit(void){
	platform_driver_unregister(&flash_led_driver);
	platform_device_unregister(&flash_led_device);
}

module_init(flash_led_init);
module_exit(flash_led_exit);

MODULE_LICENSE("GPL");

--------------------------------------------------------------------
dts里配置gpio 为led 闪光灯
leds {

      compatible = "flash-leds";
      gpios = <&gpio1 19 0>;
};

MX6QDL_PAD_SD1_DAT2__GPIO1_IO19 0x80000000   /*flash led*/

2.HAL层,增加模块接口,以访问驱动程序

主要是操作驱动程序,open,ioctl,close
生成led.default.so 在system/lib/hw/目录下
在modules目录新建led目录
hardware/libhardware/modules/led/
led.c

#define LOG_TAG "LedStub"

#include <hardware/hardware.h>
#include <hardware/led.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>

#define MISC_IOC_MAGIC 'M'
#define LED_ON  _IO(MISC_IOC_MAGIC, 0x01)
#define LED_OFF _IO(MISC_IOC_MAGIC, 0x02)
#define DEVICE_NAME "/dev/led"

int led_device_close(struct hw_device_t* device){
	struct led_control_device_t *led_dev = (struct led_control_device_t*)device;
	
	if(led_dev){
	  close(led_dev->fd);
	  free(led_dev);
	}

	return 0;
}

int led_on(struct led_control_device_t *dev, int led){
	ALOGI("LED set %d on", led);
	ioctl(dev->fd, LED_ON, NULL);
	return 0;
}

int led_off(struct led_control_device_t *dev, int led){
	ALOGI("LED set %d off", led);
	ioctl(dev->fd, LED_OFF, NULL);
	return 0;
}

static int led_device_open(const hw_module_t* module, const char* name,
			hw_device_t** device){
	struct led_control_device_t *dev;

	dev = calloc(1, sizeof(struct led_control_device_t));
	if(!dev){
		ALOGE("led device open error");	
		return -ENOMEM;
	}

	dev->common.tag     = HARDWARE_DEVICE_TAG;
	dev->common.version = 0;
	dev->common.module  = (struct hw_module_t *)module;
	dev->common.close   = led_device_close;

	dev->set_on  = led_on;
	dev->set_off = led_off;


	dev->fd = open(DEVICE_NAME, O_RDWR);
	if(dev->fd <0){
		ALOGE("open /dev/led error");
	}else
	  ALOGI("open /dev/led success");
	
	*device = &dev->common;
	return 0;
}

static struct hw_module_methods_t led_module_methods = {
	.open = led_device_open,
};

struct led_module_t HAL_MODULE_INFO_SYM = {
	.common = {
		.tag = HARDWARE_MODULE_TAG,
		.version_major = 1,
		.version_minor = 0,
		.id = LED_HARDWARE_MODULE_ID,
		.name = "LED FLASH HW HAL",
		.author = "The Android Open Source Project",
		.methods = &led_module_methods,
	},
};
-------------------------------------------------
led.h
hardware/libhardware/include/hardware/led.h
#ifndef LED_H
#define LED_H

#include <hardware/hardware.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h>

#define LED_HARDWARE_MODULE_ID "led"

struct led_module_t {
	struct hw_module_t common;
};

struct led_control_device_t {
	struct hw_device_t common;
	int fd;

	int (*set_on)(struct led_control_device_t *dev, int led);
	int (*set_off)(struct led_control_device_t *dev, int led);

};
#endif
------------------------------------------------------------
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.c
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_TAGS := optional

include $(BUILD_SHARED_LIBRARY)

3.JNI层,编写JNI方法,在应用程序框架层提供Java接口访问硬件

frameworks/base/services/core/jni/com_android_server_led_LedService.cpp
生成文件打包进system/lib/libandroid_servers.so
Android系统初始化时,会自动加载JNI方法
01-06 06:45:45.611   520   520 I SystemServer: Led Service
01-06 06:45:45.611   520   520 I LedService: Go to get Led Stub
01-06 06:45:45.611   520   520 I LedStub : led jni init......
01-06 06:45:45.615   520   520 I LedStub : Led JNI: led stub found.

#define LOG_TAG "LedStub"

#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"

#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/led.h>

#include <stdio.h>

namespace android {
	
	struct led_control_device_t *sLedDevice = NULL;

	static jboolean led_on(JNIEnv *env, jobject clazz, jint led){
		ALOGI("LedService JNI:led_on");
		if(sLedDevice==NULL){
			ALOGE("LedService JNI: led_on() is invoked");
			return -1;
		}else
		  return sLedDevice->set_on(sLedDevice,led);
	}

	static jboolean led_off(JNIEnv *env, jobject clazz, jint led){
		ALOGI("LedService JNI:led_off");
		if(sLedDevice==NULL){
			ALOGE("LedService JNI: led_off() is invoked");
			return -1;
		}else
		  return sLedDevice->set_off(sLedDevice,led);
	}
	static inline int led_control_open(const struct hw_module_t *module, struct led_control_device_t** device){
		return module->methods->open(module, LED_HARDWARE_MODULE_ID, 
					(struct hw_device_t**)device);
	}

	static jboolean led_init(JNIEnv *env, jclass clazz){
		led_module_t* module;
		ALOGI("led jni init......");
		if(hw_get_module(LED_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0){
			ALOGI("Led JNI: led stub found.");
			if(led_control_open(&module->common, &sLedDevice) == 0){
				ALOGI("JNI: led device is open");
				return 0;
			}
			ALOGE("LED JNI: failed to open led device");
			return -1;
		}
		ALOGE("LED JNI: failed to get led stub module");
		return -1;
	}

	static const JNINativeMethod method_table[] = {
		{"init_native","()Z", (void*)led_init},
		{"led_on", "(I)Z", (void*)led_on},
		{"led_off", "(I)Z", (void*)led_off},
	};

	int register_android_server_LedService(JNIEnv *env){
	
		return jniRegisterNativeMethods(env, "com/android/server/led/LedService", method_table, NELEM(method_table));
	}
};

--- a/frameworks/base/services/core/jni/onload.cpp
+++ b/frameworks/base/services/core/jni/onload.cpp
@@ -46,6 +46,7 @@ int register_android_server_tv_TvInputHal(JNIEnv* env);
 int register_android_server_PersistentDataBlockService(JNIEnv* env);
 int register_android_server_Watchdog(JNIEnv* env);
 int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
+int register_android_server_LedService(JNIEnv* env);
 };
 
 using namespace android;
@@ -87,7 +88,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
     register_android_server_PersistentDataBlockService(env);
     register_android_server_Watchdog(env);
     register_android_server_HardwarePropertiesManagerService(env);
-
+       register_android_server_LedService(env);

4.framework层,应用程序框架层,增加硬件服务接口

jni在注册方法时用到com/android/server/led/LedService
所以在frameworks/base/services/core/java/com/android/server/目录下新建led文件夹
# ls frameworks/base/services/core/java/com/android/server/led/
LedService.java
----------------------------------------------------------------------------
在Android系统中,硬件服务一般是运行在一个独立的进程中为各种应用程序提供服务。
因此,调用这些硬件服务的应用程序与这些硬件服务之间的通信需要通过代理来进行
创建aidl文件来描述通信接口

frameworks/base/core/java/android/os/ILedService.aidl
package android.os;

/**
 * {@hide}
 */
interface ILedService {
	boolean setOn(); 
	boolean setOff();
}
--------------------------------------------------------
frameworks/base/services/core/java/com/android/server/led/LedService.java
package com.android.server.led;

import android.util.Config;
import android.util.Log;
import android.content.Context;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.ILedService;

public final class LedService extends ILedService.Stub{

	private Context mContext;

	public LedService(Context context){
		super();
		mContext = context;
		Log.i("LedService","Go to get Led Stub");
		init_native();
	}
	
	public boolean setOn(){
		Log.i("LedService","Led On");
		return led_on(0);
	}

	public boolean setOff(){
		Log.i("LedService","Led Off");
		return led_off(1);
	}

	private static native boolean init_native(); //和jni里面的对应
	private static native boolean led_on(int led);
	private static native boolean led_off(int led);
}

--- a/frameworks/base/services/core/jni/Android.mk
+++ b/frameworks/base/services/core/jni/Android.mk
@@ -36,6 +36,7 @@ LOCAL_SRC_FILES += \
     $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
     $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_PersistentDataBlockService.cpp \
+       $(LOCAL_REL_DIR)/com_android_server_led_LedService.cpp \


---------------------------------------------------------------------------
在frameworks/base里增加对aidl的编译项
--- a/frameworks/base/Android.mk
+++ b/frameworks/base/Android.mk
@@ -218,6 +218,7 @@ LOCAL_SRC_FILES += \
        core/java/android/nfc/INfcFCardEmulation.aidl \
        core/java/android/nfc/INfcUnlockHandler.aidl \
        core/java/android/nfc/ITagRemovedCallback.aidl \
+       core/java/android/os/ILedService.aidl \

编译后会在out目录下产生ILedService.java文件
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java

5.通过Manager调用Service

Manager管理service,在注册服务时候就实例化一个Manager,App获取服务getService也就获取
了这个对象

frameworks/base/core/java/android/os/LedManager.java
package android.os;
import android.content.Context;
import android.os.Binder;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.Handler;
import android.os.Message;
import android.os.ServiceManager;
import android.util.Log;
import android.os.ILedService;

public class LedManager{

	private static final String TAG="LedManager";
	private ILedService mLedService;
	private Context mContext;

	public LedManager(Context context, ILedService service){
			mContext = context;
			mLedService = service;
	}

	public boolean LedOn(){
		try{
			return mLedService.setOn();
		}catch(RemoteException e){
			Log.e(TAG,"RemoteException in LedManager Ledon",e);
			return false;
		}
	}

	public boolean LedOff(){
		try{
			return mLedService.setOff();
		}catch(RemoteException e){
			Log.e(TAG,"RemoteException in LedManager Ledoff",e);
			return false;
		}
	}
}

6.添加注册服务,addService开机服务就可以启动

通过service list 命令查看服务是否启动

# service list | grep led                                          
35    led: [android.os.ILedService]

--- a/frameworks/base/services/java/com/android/server/SystemServer.java
+++ b/frameworks/base/services/java/com/android/server/SystemServer.java
@@ -103,6 +103,7 @@ import com.android.server.usage.UsageStatsService;
 import com.android.server.vr.VrManagerService;
 import com.android.server.webkit.WebViewUpdateService;
 import com.android.server.wm.WindowManagerService;
+import com.android.server.led.LedService;
 
 import dalvik.system.VMRuntime;
 
@@ -516,6 +517,7 @@ public final class SystemServer {
      */
     private void startOtherServices() {
         final Context context = mSystemContext;
+               LedService led = null;
         VibratorService vibrator = null;
         IMountService mountService = null;
         NetworkManagementService networkManagement = null;
@@ -987,6 +989,15 @@ public final class SystemServer {
                     }
                     Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
                 }
+                               
+                               try{
+                                       Slog.i(TAG,"Led Service");
+                                       led = new LedService(context);
+                                       ServiceManager.addService(Context.LED_SERVICE, led);
+                                       Slog.i(TAG,"Led Service-----");
+                               }catch(Throwable e){
+                                       Slog.e(TAG,"Failure starting LedService", e);
+                               }

--- a/frameworks/base/core/java/android/content/Context.java
+++ b/frameworks/base/core/java/android/content/Context.java
@@ -3431,6 +3431,15 @@ public abstract class Context {
     public static final String SERIAL_SERVICE = "serial";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a {@link
+     * android.os.LedManager} for access to led ports.
+     *
+     * @see #getSystemService
+     *
+     * @hide
+     */
+    public static final String LED_SERVICE = "led";

注册服务
--- a/frameworks/base/core/java/android/app/SystemServiceRegistry.java
+++ b/frameworks/base/core/java/android/app/SystemServiceRegistry.java
@@ -127,6 +127,8 @@ import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.CaptioningManager;
 import android.view.inputmethod.InputMethodManager;
 import android.view.textservice.TextServicesManager;
+import android.os.LedManager;
+import android.os.ILedService;
 
 import java.util.HashMap;
 
@@ -480,6 +482,14 @@ final class SystemServiceRegistry {
                 return new SerialManager(ctx, ISerialManager.Stub.asInterface(b));
             }});
 
+        registerService(Context.LED_SERVICE, LedManager.class,
+                new CachedServiceFetcher<LedManager>() {
+            @Override
+            public LedManager createService(ContextImpl ctx) {
+                IBinder b = ServiceManager.getService(Context.LED_SERVICE);
+                return new LedManager(ctx, ILedService.Stub.asInterface(b));
+            }});
+

7.更新API

make update-api -j16

8.修改节点权限

Android在Selinux下要获取对内核节点访问的权限,需要修改.te文件

1)为/dev/led节点定义一个名字led_device

--- a/device/fsl/imx6/sepolicy/file_contexts
+++ b/device/fsl/imx6/sepolicy/file_contexts
@@ -8,6 +8,7 @@
 /dev/ttymxc[0-9]*               u:object_r:tty_device:s0
 /dev/ttyUSB[0-9]*               u:object_r:tty_device:s0
 /dev/mma8x5x                    u:object_r:sensors_device:s0
+/dev/led                        u:object_r:led_device:s0

2)将led_device声明为dev_type

--- a/device/fsl/imx6/sepolicy/device.te
+++ b/device/fsl/imx6/sepolicy/device.te
@@ -1,2 +1,3 @@
 type caam_device, dev_type;
 type pxp_device, dev_type;
+type led_device,dev_type;

3)修改读写权限

--- a/device/fsl/imx6/sepolicy/system_server.te
+++ b/device/fsl/imx6/sepolicy/system_server.te
@@ -4,3 +4,4 @@ allow system_server system_data_file:file {relabelto rw_file_perms};
 allow system_server system_data_file:dir {relabelto rw_dir_perms};
 allow system_server kernel:system { syslog_read };
 allow system_server debugfs_tracing:file { write };
+allow system_server led_device:chr_file { open read write ioctl };

4)修改linux下节点自身的权限

--- a/device/fsl/imx6/etc/ueventd.freescale.rc
+++ b/device/fsl/imx6/etc/ueventd.freescale.rc
@@ -1,3 +1,4 @@
+/dev/led                  0660   system     system

5)增加服务的权限

--- a/system/sepolicy/service_contexts
+++ b/system/sepolicy/service_contexts
@@ -116,6 +116,7 @@ scheduling_policy                         u:object_r:scheduling_policy_service:s
 search                                    u:object_r:search_service:s0
 sensorservice                             u:object_r:sensorservice_service:s0
 serial                                    u:object_r:serial_service:s0
+led                                       u:object_r:led_service:s0

--- a/system/sepolicy/service.te
+++ b/system/sepolicy/service.te
@@ -96,6 +96,7 @@ type rttmanager_service, app_api_service, system_server_service, service_manager
 type samplingprofiler_service, system_server_service, service_manager_type;
 type scheduling_policy_service, system_server_service, service_manager_type;
 type search_service, app_api_service, system_server_service, service_manager_type;
+type led_service, app_api_service, system_server_service, service_manager_type;

9.编译更新系统镜像到emmc

10.android app 测试

将out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar里的
android/os/LedManager.class
导入eclipse 的sdk/platforms/android-xx/android.jar包中
的android/os目录

package com.led.ledclient;

import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.os.LedManager;
public class MainActivity extends Activity implements OnClickListener {
	private Button btOn;
	private Button btOff;
	LedManager ledm;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		btOn = (Button)findViewById(R.id.btOn);
		btOff = (Button)findViewById(R.id.btOff);
		btOn.setOnClickListener(this);
		btOff.setOnClickListener(this);
		
		ledm = (LedManager)getSystemService("led");
		
		
	}
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.btOn:
			ledm.LedOn();
			break;
		case R.id.btOff:
			ledm.LedOff();
			break;
		default:
			break;
		}
		
	}

	

}
//开Button
01-06 07:40:30.664   520   531 I LedService: Led On
01-06 07:40:30.664   520   531 I LedStub : LedService JNI:led_on
01-06 07:40:30.664   520   531 I LedStub : LED set 0 on
//关Button
01-06 07:40:39.069   520   906 I LedService: Led Off
01-06 07:40:39.069   520   906 I LedStub : LedService JNI:led_off
01-06 07:40:39.069   520   906 I LedStub : LED set 1 off


11.总结

APP调用了LedManager里的LedOn LedOff
LedManager服务通过aidl调用到LedService.java里的setOn setOff
LedService通过jni提供的接口调用led_on led_off
private static native boolean led_on(int led);
private static native boolean led_off(int led);
jni注册的方法表
{"led_on", "(I)Z", (void*)led_on},
{"led_off", "(I)Z", (void*)led_off},
jni层调用 led_on----->return sLedDevice->set_on(sLedDevice,led);
      led_off---->return sLedDevice->set_off(sLedDevice,led);

调用到硬件抽象层led.default.so里的
    dev->set_on  = led_on;
    dev->set_off = led_off;
led_on/led_off通过ioctl调用到驱动调置gpio的高低电平


参考:
http://www.cnblogs.com/hackfun/p/7418902.html
http://blog.csdn.net/baiduluckyboy/article/details/6973015
http://blog.csdn.net/luoshengyang/article/details/6567257



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值