2026/8/1 1:54:11

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

Android8.1系统Led的控制从底层到上层的实现 我们都知道安卓系统是一个分层的系统那么它的底层到上层或者上层到底层的标准流程是怎么走的呢这里通过apk操作一个GPIO控制led的亮灭从而实现从上层到底层的完整调用流程。写得不足之处欢迎有识之士不吝赐教在此先行谢过技术无边欢迎各位大牛来此多多交流。这里只给出驱动部分的代码其他部分的代码请到我的网盘地址那里去下载那里有代码包括相关说明。网盘链接链接https://pan.baidu.com/s/1gQNui11XkawMX78tvG592w 提取码4ou4Android Version:v8.0.1Linux Version:V4.4Hardware:mtk67391、在kernel层,编写和添加驱动和编译文件Malefie创建设备节点文件/dev/led在kernel-4.4/drivers目录新建目录led_test新建led_test.c#includelinux/init.h#includelinux/slab.h#includelinux/module.h#includelinux/kernel.h#includelinux/fs.h#includelinux/cdev.h#includelinux/device.h#includelinux/ioctl.h#includelinux/uaccess.h#includelinux/string.h#includelinux/wait.h#includelinux/types.h#includelinux/proc_fs.h#includelinux/of.h#includelinux/of_gpio.h#includelinux/gpio.h#defineBUFSIZE1024/*ioctl*/#defineLED_ON1#defineLED_OFF0/*主设备和从设备号变量*/staticinttest_major0;staticinttest_minor0;/*设备类别和设备变量*/staticstructcdevcdev;staticstructclass*test_classNULL;staticstructdevice*test_class_devNULL;intnumber_of_devices1;staticdev_tdev0;staticchar*buf;staticunsignedintlen;staticunsignedintgpio_led;/*从dts中解析出gpio口*/staticintget_gpio_number(void){structdevice_node*nodeNULL;intgpio0;printk(---czd--- Enter %s\n,__func__);nodeof_find_compatible_node(NULL,NULL,led_node);if(nodeNULL){printk(%s:node:led_node not find!\n,__func__);return-EFAULT;}gpioof_get_named_gpio(node,gpio_num,0);returngpio;}/*打开设备方法*/staticinttest_open(structinode*inode,structfile*filp){intret;printk(---czd--- Enter %s\n,__func__);gpio_ledget_gpio_number();if(gpio_is_valid(gpio_led)){retgpio_request(gpio_led,gpio_led);if(ret){printk(---czd--- failed to get %d gpio.\n,gpio_led);return-1;}}elsegpio_led-1;gpio_direction_output(gpio_led,1);printk(---czd--- gpio is valid and pull up now\n);return0;}/*设备文件释放时调用*/staticinttest_release(structinode*inode,structfile*filp){return0;}/************************ * file_operations-read * 可以在adb工具进入机器的dev目录执行adb shell cd dev cat led * 即可读出节点led的内容 * 函数原型static ssize_t read(struct file *file, char *buf, * size_t count, loff_t *ppos) ************************/staticssize_ttest_read(structfile*file,char__user*buffer,size_tcount,loff_t*f_pos){if(*f_pos0)return0;printk(---start read---\n);printk(the string is %s\n,buf);if(copy_to_user(buffer,buf,len))return-EFAULT;*f_pos*f_poslen;returnlen;}/************************ * 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) ************************/staticssize_ttest_write(structfile*file,constchar__user*buffer,size_tcount,loff_t*f_pos){if(count0)return-EFAULT;printk(---start write---\n);lencountBUFSIZE?BUFSIZE:count;// kfree memory by kmalloc beforeif(buf!NULL)kfree(buf);buf(char*)kmalloc(len1,GFP_KERNEL);if(bufNULL){printk(device_create kmalloc fail!\n);return-EFAULT;}//memset(buf, 0, sizeof(buf));memset(buf,0,len1);if(copy_from_user(buf,buffer,len))return-EFAULT;printk(device_create writing :%s,buf);returnlen;}staticlongtest_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg){printk(---czd--- Enter %s\n,__func__);printk(-----czd--- kernel cmd is : %d\n,cmd);switch(cmd){caseLED_ON:printk(---czd--- you set gpio up\n);gpio_set_value(gpio_led,1);break;caseLED_OFF:printk(---czd--- you set gpio down\n);gpio_set_value(gpio_led,0);break;default:return-EINVAL;}return0;}/*设备文件操作方法表*/staticstructfile_operationstest_fops{.ownerTHIS_MODULE,.opentest_open,.releasetest_release,.readtest_read,.writetest_write,.unlocked_ioctltest_ioctl,};staticvoidtest_setup_cdev(void){interror;dev_tdevnoMKDEV(test_major,test_minor);cdev_init(cdev,test_fops);cdev.ownerTHIS_MODULE;cdev.opstest_fops;/*注册字符设备*/errorcdev_add(cdev,devno,1);if(error)printk(Error %d adding test_setup_cdev,error);}staticint__inittest_init(void){interr-1;printk(---czd--- Initializing test device.\n);/*动态分配主设备和从设备号*/erralloc_chrdev_region(dev,0,number_of_devices,led);if(err0){printk(Failed to alloc char dev region.\n);returnerr;}test_majorMAJOR(dev);test_minorMINOR(dev);printk(---czd--- test_major %d ; test_minor %d \n,test_major,test_minor);/*初始化设备*/test_setup_cdev();/*在/sys/class/目录下创建设备类test_class*/test_classclass_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_devdevice_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);return0;}/*模块卸载方法*/staticvoid__exittest_exit(void){dev_tdevnoMKDEV(test_major,test_minor);cdev_del(cdev);device_destroy(test_class,devno);//delete device node under /devclass_destroy(test_class);//delete class created by usunregister_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{compatibleled_node;gpio_numpio90;statusokay;};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.chardware/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 是在hardware/led.h中定义的。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.soAndroid系统初始化时,会自动加载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.java5、通过Manager调用ServiceManager管理service,在注册服务的时候就实例化一个Manager,App获取服务getService也就获取了这个对象添加frameworks/base/core/java/android/os/LedManager.java文件6、添加注册服务,addService开机服务就可以启动通过service list 命令查看服务是否启动service list | grep led7 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、更新APImake update-api -j88、修改节点权限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 hidlchown system system /dev/sunwave_fpchmod 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 fileallow 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 部分添加\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:s03.\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:s06.\system\sepolicy\prebuilts\api\26.0\nonplat_sepolicy.cil 添加相应配置6.1 typeattributeset system_server_service 在最后添加自定义的服务 xxx_service_26_06.2 typeattributeset app_api_service 在最后添加自定义的服务 xxx_service_26_06.3 typeattributeset ephemeral_app_api_service 在最后添加自定义的服务 xxx_service_26_06.4 typeattributeset service_manager_type 在最后添加自定义的服务 xxx_service_26_06.5 添加一对 配置参考已有的例子(typeattribute xxx_service_26_0)(roletype object_r xxx_service_26_0)9、编译更新系统镜像到机器emmcflashmake -j8 21 | tee make.log10、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 LedOffLedManager服务通过aidl调用到LedService.java里的setOn setOffLedService通过jni提供的接口调用led_on led_offprivate 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/6973015http://blog.csdn.net/luoshengyang/article/details/6567257https://blog.csdn.net/shui1025701856/article/details/78480776