Android8.1系统Led的控制从底层到上层的实现

我们都知道,安卓系统是一个分层的系统,那么它的底层到上层或者上层到底层的标准流程是怎么走的呢?这里通过apk操作一个GPIO控制led的亮灭从而实现从上层到底层的完整调用流程。写得不足之处欢迎有识之士不吝赐教,在此先行谢过,技术无边,欢迎各位大牛来此多多交流。(这里只给出驱动部分的代码,其他部分的代码请到我的网盘地址那里去下载,那里有代码包括相关说明)。
网盘链接:链接:https://pan.baidu.com/s/1gQNui11XkawMX78tvG592w 提取码:4ou4

Android Version:v8.0.1
Linux Version:V4.4
Hardware:mtk6739

1、在kernel层,编写和添加驱动和编译文件Malefie,创建设备节点文件/dev/led

在kernel-4.4/drivers目录新建目录led_test,新建led_test.c

#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	#define BUFSIZE  1024 

	/*ioctl*/
	#define LED_ON 1
	#define LED_OFF 0

	/*主设备和从设备号变量*/
	static int test_major = 0;
	static int test_minor = 0;

	/*设备类别和设备变量*/
	static struct cdev cdev;
	static struct class *test_class = NULL;
	static struct device *test_class_dev = NULL;

	int number_of_devices = 1;
	static dev_t dev = 0;
	static char *buf;
	static unsigned int len;
	static unsigned int gpio_led;

	/*从dts中解析出gpio口*/
		static int get_gpio_number(void)
	{
    struct device_node *node = NULL;
    int gpio = 0;
	
	printk("---czd--- Enter %s\n",__func__);
    node = of_find_compatible_node(NULL, NULL, "led_node");
    if (node == NULL) {
       printk("%s:node:led_node not find!\n", __func__);
       return -EFAULT;
    }
    gpio = of_get_named_gpio(node, "gpio_num", 0);
    return gpio;
	}

	/*打开设备方法*/
	static int test_open(struct inode *inode, struct file *filp) {

	int ret;

	printk("---czd--- Enter %s\n",__func__);
	gpio_led = get_gpio_number();
	if (gpio_is_valid(gpio_led)){
	ret = gpio_request(gpio_led,"gpio_led");
	if(ret){
		printk("---czd--- failed to get %d gpio.\n", gpio_led);
		return -1;
		}
	}
	else gpio_led = -1;
    gpio_direction_output(gpio_led,1);
	printk("---czd--- gpio is valid and pull up now\n");

	return 0;
	}

	/*设备文件释放时调用*/
	static int test_release(struct inode *inode, struct file *filp) {

	return 0;
	}

	/************************
	 * file_operations->read
	 * 可以在adb工具进入机器的dev目录,执行adb shell && cd dev && cat led,
	 * 即可读出节点le'd的内容
	 * 函数原型:static ssize_t read(struct file *file, char *buf, 
	 * 										size_t count, loff_t *ppos)
	 ************************/

	static ssize_t test_read(struct file *file, char __user *buffer,
										size_t count, loff_t *f_pos) 
	{
	if(*f_pos > 0)
		return 0;
 
	printk("---start read---\n");
	printk("the string is >>>>> %s\n", buf);
 
	if(copy_to_user(buffer, buf, len))
		return -EFAULT;
	*f_pos = *f_pos + len;
	return len;
	}


	/************************
	 * file_operations->write
	 * 可以在adb工具进入机器的dev目录,执行adb shell && cd dev && echo 12345 > led,
	 * 即可把12345写入节点led
	 * 函数原型:static ssize_t write(struct file *file, const char *buf, 
	 * size_t count, loff_t *ppos)
	 ************************/

		static ssize_t test_write(struct file *file, const char __user *buffer,
												size_t count, loff_t *f_pos) 
	{
	
	if(count <= 0)
		return -EFAULT;
	printk("---start write---\n");
	
	len = count > BUFSIZE ? BUFSIZE : count;
 
	// kfree memory by kmalloc before
	if(buf != NULL)
		kfree(buf);
	buf = (char*)kmalloc(len+1, GFP_KERNEL);
	if(buf == NULL)
	{
		printk("device_create kmalloc fail!\n");
		return -EFAULT;
	}
 
	//memset(buf, 0, sizeof(buf));
	memset(buf, 0, len+1);
 
	if(copy_from_user(buf, buffer, len))
		return -EFAULT;
	printk("device_create writing :%s",buf);
	return len;
	}


	static long test_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
	{

    printk("---czd--- Enter %s\n",__func__);
	printk("-----czd--- kernel cmd is : %d\n",cmd);

    switch(cmd)
    {
    case LED_ON:
        printk("---czd--- you set gpio up\n");
		gpio_set_value(gpio_led, 1);
        break;
    
	case LED_OFF:
        printk("---czd--- you set gpio down\n");
		gpio_set_value(gpio_led, 0);
        break;
    default:
        return -EINVAL;
    }
    return 0;
	}



	/*设备文件操作方法表*/
	static struct file_operations test_fops = {
	.owner = THIS_MODULE,
	.open = test_open,
	.release = test_release,
	.read = test_read,
	.write = test_write, 
	.unlocked_ioctl = test_ioctl,
	};



	static void test_setup_cdev(void)
	{
					int error;
		dev_t devno = MKDEV(test_major, test_minor);

	cdev_init (&cdev, &test_fops);
	cdev.owner = THIS_MODULE;
	cdev.ops = &test_fops;        
 
	/*注册字符设备*/
	error = cdev_add (&cdev, devno, 1);
	if(error)
	printk("Error %d adding test_setup_cdev", error);  

	}



	static int __init test_init(void)
    {		
	int err = -1;
 
	printk("---czd--- Initializing test device.\n");        
 
	/*动态分配主设备和从设备号*/
	err = alloc_chrdev_region(&dev, 0, number_of_devices, "led");
	if(err < 0) {
		printk("Failed to alloc char dev region.\n");
		return err;
	}
 
	test_major = MAJOR(dev);
	test_minor = MINOR(dev);
	printk("---czd--- test_major ==> %d ; test_minor ==> %d \n",test_major,test_minor);


	/*初始化设备*/
	test_setup_cdev();       
 
	/*在/sys/class/目录下创建设备类test_class*/
	test_class = class_create(THIS_MODULE, "led");
	if(IS_ERR(test_class)) 
        {
            printk("Err: failed in creating class.\n");
            return -1; 
        }       


	/*在/dev/目录和/sys/class/led目录下分别创建设备文件led*/
	test_class_dev = device_create(test_class, NULL, MKDEV(test_major, 0), NULL, "led");
	if(IS_ERR(test_class_dev)) 
        {
		printk("Err: failed in creating device.\n");
		return -1; 
        }

	printk("---czd--- Succedded to initialize test device.\n");  
	return 0;
 
    }


	/*模块卸载方法*/
	static void __exit test_exit(void)
    {
	dev_t devno = MKDEV (test_major, test_minor);

	cdev_del(&cdev);
	device_destroy(test_class, devno); //delete device node under /dev
	class_destroy(test_class); //delete class created by us
	unregister_chrdev_region (devno, number_of_devices);
	gpio_free(gpio_led);
	printk ("Char driver test_exit.\n");

    }



	module_init(test_init);
	module_exit(test_exit);

	MODULE_AUTHOR("czd");
	MODULE_DESCRIPTION("Device_create Driver");
	MODULE_LICENSE("GPL");


dts添加设备节点信息如下:

	led_node{
		compatible = "led_node";
		gpio_num = <&pio 9 0>;
		status = "okay";
	};

2、在HAL层,增加模块接口,以访问驱动程序
主要是操作驱动程序,open,ioctl,close
生成led.default.so 在system/lib/hw/目录下

在hardware/libhardware/include/hardware目录新建led.h文件
hardware/libhardware/include/hardware/led.h

在modules目录新建led目录,添加以下文件:
hardware/libhardware/modules/led/led.c
hardware/libhardware/modules/led/Andorid.mk
执行编译命令:mmm hardware/libhardware/modules/led/,编译得到led.default.so文件

在 led_init 函数中,通过Android硬件抽象层提供的hw_get_module方法来加载模块 LED_HARDWARE_MODULE_ID 的硬件抽象层模块,
其中,LED_HARDWARE_MODULE_ID 是在中定义的。Android硬件抽象层会根据 LED_HARDWARE_MODULE_ID 的值
在Android系统的/system/lib/hw目录中找到相应的模块,然后加载起来,并且返回hw_module_t接口给调用者使用。

3、JNI层,编写JNI方法,在应用程序框架层提供Java接口访问硬件
frameworks/base/services/core/jni/com_android_server_led_LedService.cpp
生成文件打包进system/lib/libandroid_servers.so
Android系统初始化时,会自动加载JNI方法.

在frameworks/base/services/core/jni/添加com_android_server_led_LedService.cpp文件
在com_android_server_led_LedService.cpp文件中,实现JNI方法。注意文件的命令方法,com_android_server_led前缀表示的是包名,
表示硬件服务LedService是放在frameworks/base/services/core/java/目录下的com/android/server/led目录的,即存在一个命令
为com.android.server.led.LedService的类。
简单地说,LedService是一个提供Java接口的硬件访问服务类。

然后修改同目录下的onload.cpp文件,首先在namespace android增加register_android_server_LedService函数声明,这样,
在Android系统初始化时,就会自动加载该JNI方法调用表。

然后修改同目录下的Android.mk文件,在LOCAL_SRC_FILES变量中增加一行:
com_android_server_led_LedService.cpp \

4、framework层,应用程序框架层,增加硬件服务接口
jni在注册方法时用到com/android/server/led/LedService
所以在frameworks/base/services/core/java/com/android/server/目录下新建led文件夹,添加LedService.java文件

在Android系统中,硬件服务一般是运行在一个独立的进程中为各种应用程序提供服务。
因此,调用这些硬件服务的应用程序与这些硬件服务之间的通信需要通过代理来进行创建aidl文件来描述通信接口
在frameworks/base/core/java/android/os/目录新建ILedService.aidl文件

然后修改frameworks/base/services/core/jni/Android.mk文件,在LOCAL_SRC_FILES += \ 增加
+ $(LOCAL_REL_DIR)/com_android_server_led_LedService.cpp \

在frameworks/base/Android.mk里增加对aidl的编译项
LOCAL_SRC_FILES +=
+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文件

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

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

service list | grep led

7 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() {
+ @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 -j8

8、修改节点权限

Android在Selinux下要获取对内核节点访问的权限,需要修改.te文件(根据各个平台不同而异,这里是mtk平台的权限)

MTK平台的修改节点权限(android8.1):

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

— a/device/mediatek/mt6739/init.mt6739.rc

+++ b/device/mediatek/mt6739/init.mt6739.rc

@@ -807,7 +807,13 @@ on boot

#added for sunwave finger tee hidl

chown system system /dev/sunwave_fp

chmod 0660 /dev/sunwave_fp
+
+#added for led by czd
+ chown system system /dev/led
+ chmod 0660 /dev/led
+

(2)为/dev/led节点定义一个名字led_device
— a/device/mediatek/mt6739/sepolicy/bsp/file_contexts
+++ b/device/mediatek/mt6739/sepolicy/bsp/file_contexts
@@ -9,4 +9,5 @@
# Data files

/dev/goodix_fp u:object_r:fingerprint_device:s0
+/dev/led u:object_r:led_device:s0

(3)将led_device声明为dev_type
— a/device/mediatek/mt6739/sepolicy/basic/device.te
+++ b/device/mediatek/mt6739/sepolicy/basic/device.te
@@ -4,3 +4,4 @@

type gps_emi_device, dev_type;
type mntl_block_device, dev_type;
+type led_device,dev_type;

(4)修改读写权限
— a/device/mediatek/mt6739/sepolicy/basic/system_server.te
+++ b/device/mediatek/mt6739/sepolicy/basic/system_server.te
@@ -5,4 +5,6 @@
# Date : WK15.45
# Operation : Migration
# Purpose : for debug
-allow system_server gps_data_file:dir search;
\ No newline at end of file
+allow system_server gps_data_file:dir search;
+allow system_server led_device:chr_file { open read write ioctl getattr };

(5)增加服务的权限
sepolicy 部分添加
参考来自以下测试验证平台MSM8909 android8.1(验证可行)

一. sepolicy 部分添加

  1. \system\sepolicy\public\service.te 定义服务名称和属性

type xxx_service,app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;

2.\system\sepolicy\private\service_contexts 添加服务名称

xxx u:object_r:xxx_service:s0

3.\system\sepolicy\private\compat\26.0\26.0.cil 文件最后添加

(typeattributeset xxx_service_26_0 (xxx_service))

4.\system\sepolicy\prebuilts\api\26.0\public\service.te 定义服务名称和属性

type xxx_service,app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;

5.\system\sepolicy\prebuilts\api\26.0\private\service_contexts 添加服务名称

xxx u:object_r:xxx_service:s0

6.\system\sepolicy\prebuilts\api\26.0\nonplat_sepolicy.cil 添加相应配置

6.1 typeattributeset system_server_service 在最后添加自定义的服务 xxx_service_26_0

6.2 typeattributeset app_api_service 在最后添加自定义的服务 xxx_service_26_0

6.3 typeattributeset ephemeral_app_api_service 在最后添加自定义的服务 xxx_service_26_0

6.4 typeattributeset service_manager_type 在最后添加自定义的服务 xxx_service_26_0

6.5 添加一对 配置(参考已有的例子)

(typeattribute xxx_service_26_0)
(roletype object_r xxx_service_26_0)

9、编译更新系统镜像到机器emmc(flash)
make -j8 2>&1 | tee make.log

10、android app 测试
10.1 eclipse做法:
将out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar里的
android/os/LedManager.class
导入eclipse 的sdk/platforms/android-xx/android.jar包中
的android/os目录

10.2 Android stadio做法:
将out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar导入libs目录

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

https://blog.csdn.net/shui1025701856/article/details/78480776

你可能感兴趣的:(Android系统开发)