android|Android GpioService从app到驱动

【android|Android GpioService从app到驱动】因为最近项目Android要用到APP控制相关GPIO控制,因为网上大部分都是app直接通过JNI控制GPIO,这样做存在一个问题,GPIO被多个app打开会报错。同时也违背了Android设计初衷。这里demo也是从其他项目拷贝过来的。自己修改的。源码使用的是Android 5.1 的rk3288.使用控制led灯的方式来实现gpioservice。
通过JNI方式一般流程是
#app->jni->gpio驱动
我自己新编写流程是
#app->ledmanager->ledservice.java->ledservice.cpp->jni->led驱动
首先是led的相关gpio驱动
dts文件

}; xgpio_beep { compatible = "9tripod,beep"; gpio = <&gpio6 GPIO_B3 GPIO_ACTIVE_HIGH>; status = "okay"; };

led驱动代码
struct xgpio_device_t { int gpio; struct device * dev; }; static ssize_t xgpio_state_show(struct device * dev, struct device_attribute * attr, char * buf) { struct xgpio_device_t * xdev = dev_get_drvdata(dev); if(!strcmp(attr->attr.name, "state")) { if(gpio_direction_input(xdev->gpio) == 0) return strlcpy(buf, "0\n", 3); else return strlcpy(buf, "1\n", 3); } return strlcpy(buf, "0\n", 3); }static ssize_t xgpio_state_store(struct device * dev, struct device_attribute * attr, const char * buf, size_t count) { struct xgpio_device_t * xdev = dev_get_drvdata(dev); unsigned long on = simple_strtoul(buf, NULL, 10); if(!strcmp(attr->attr.name, "state")) { if(on) gpio_direction_output(xdev->gpio, 1); else gpio_direction_output(xdev->gpio, 0); } return count; }static DEVICE_ATTR(state, 0666, xgpio_state_show, xgpio_state_store); static struct attribute * xgpio_attrs[] = { &dev_attr_state.attr, NULL }; static const struct attribute_group xgpio_group = { .attrs = xgpio_attrs, }; static int xgpio_probe(struct platform_device * pdev) { struct device_node * node = pdev->dev.of_node; struct xgpio_device_t * xdev; enum of_gpio_flags flags; int gpio; if(!node) return -ENODEV; gpio = of_get_named_gpio_flags(node, "gpio", 0, &flags); if(!gpio_is_valid(gpio)) { printk("xgpio: invalid gpio %d\n", gpio); return -EINVAL; } if(devm_gpio_request(&pdev->dev, gpio, "xgpio-pin") != 0) { printk("xgpio: can not request gpio %d\n", gpio); return -EINVAL; } xdev = devm_kzalloc(&pdev->dev, sizeof(struct xgpio_device_t), GFP_KERNEL); if(!xdev) return -ENOMEM; xdev->gpio = gpio; printk("xgpio: pptv gpiosis======== %d\n", gpio); xdev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, xdev); return sysfs_create_group(&pdev->dev.kobj, &xgpio_group); }static int xgpio_remove(struct platform_device *pdev) { struct xgpio_device_t * xdev = dev_get_drvdata(&pdev->dev); devm_gpio_free(&pdev->dev, xdev->gpio); sysfs_remove_group(&pdev->dev.kobj, &xgpio_group); return 0; }#ifdef CONFIG_PM static int xgpio_suspend(struct device *dev) { return 0; }static int xgpio_resume(struct device *dev) { return 0; } #else #define xgpio_suspend NULL #define xgpio_resume NULL #endifstatic const struct dev_pm_ops xgpio_pm_ops = { .suspend = xgpio_suspend, .resume = xgpio_resume, }; static struct of_device_id xgpio_of_match[] = { { .compatible = "9tripod,beep" }, {}, }; MODULE_DEVICE_TABLE(of, xgpio_of_match); static struct platform_driver xgpio_driver = { .driver= { .name = "xgpio", .owner = THIS_MODULE, .pm = &xgpio_pm_ops, .of_match_table = of_match_ptr(xgpio_of_match), }, .probe= xgpio_probe, .remove= xgpio_remove, }; module_platform_driver(xgpio_driver); MODULE_DESCRIPTION("9tripod xgpio driver"); MODULE_AUTHOR("Jianjun Jiang, 8192542@qq.com"); MODULE_LICENSE("GPL");

正常启动后在led控制的路径
/sys/devices/xgpio_beep.21/state
通过echo 1 > /sys/devices/xgpio_beep.21/state
打开led灯
echo 0 > /sys/devices/xgpio_beep.21/state
关闭led灯
APP 应用相关代码
public class MainActivity extends Activity { private static final String TAG = "LedServiceTest"; private LedManager mLedManager = null; private Button mLed01OnBtn = null; private Button mLed01OffBtn = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLedManager = (LedManager) getSystemService(Context.LED_SERVICE); if(mLedManager == null){ Log.e("pptv", "mLedManager is null--- !"); } mLed01OnBtn = (Button) findViewById(R.id.btnLed01On); mLed01OffBtn = (Button) findViewById(R.id.btnLed01Off); mLed01OnBtn.setOnClickListener(new Button.OnClickListener(){@Override public void onClick(View arg0) { // TODO Auto-generated method stub Log.e("pptv", "Call ledOn() start--- !"); mLedManager.ledCtrl(1,1); Log.e("pptv", "Call ledOn() start !"); }});

首先获取LED_SERVICE,添加 LED_SERVICE在
frameworks/base/core/java/android/content/Context.java
public static final String LED_SERVICE = "led";

如果把LED_SERVICE添加到systemserver成功的话,系统启动完成可以通过 adb shell service list 可以看到 led service
57input_method: [com.android.internal.view.IInputMethodManager] 58bluetooth_manager: [android.bluetooth.IBluetoothManager] 59input: [android.hardware.input.IInputManager] 60window: [android.view.IWindowManager] 61alarm: [android.app.IAlarmManager] 62consumer_ir: [android.hardware.IConsumerIrService] 63led: [android.os.ILedService] 64vibrator: [android.os.IVibratorService] 65content: [android.content.IContentService]

下面介绍把LedManager添加到系统服务的步骤,
第一步,完成LedManager.java 路径在
frameworks/base/core/java/android/os/LedManager.java
package android.os; import android.os.RemoteException; import android.util.Log; importandroid.os.ILedService; import android.content.Context; import android.content.pm.PackageManager; /** * Wrapper class for LedService; */public class LedManager { private static final String TAG = "LedManager"; private final ILedService mLedService; private Context mContext; public LedManager(Context context, ILedService service) { mContext = context; mLedService = service; }public int ledCtrl(int which, int status) { try{ Log.e("pptv", "LedManagerstart--- !"); return mLedService.ledCtrl(which, status); //Log.e("pptv", "LedManagerledCtrl--- !"); } catch (RemoteException e) { Log.e("pptv", "LedManagererror--- !"); e.printStackTrace(); return -1; } }} ~

frameworks/base/Android.mk
core/java/android/os/IVibratorService.aidl \ core/java/android/os/ILedService.aidl \ core/java/android/os/LedManager.java\

frameworks/base/core/java/android/app/ContextImpl.java
import android.os.LedManager; 、、、、、、、、、、、、 registerService(LED_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(LED_SERVICE); return new LedManager(ctx, ILedService.Stub.asInterface(b)); }});

这样就可以获取LedManager的服务了。LedManager只是对LedService做了一层封装。真正的工作都是由service 完成
frameworks/base/services/core/java/com/android/server/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); }

ILedServiceaidl 路径在
frameworks/base/core/java/android/os/ILedService.aidl
package android.os; /** {@hide} */ interface ILedService { int ledCtrl(int which, int status); }

frameworks/base/Android.mk
core/java/android/os/ILedService.aidl \

LedManager 之所以能通过 ServiceManager.getService(LED_SERVICE) 获取到LedService ,需要把LedService 添加到SystemServer系统服务中
frameworks/base/services/java/com/android/server/SystemServer.java
Slog.i(TAG, "Led Service"); ServiceManager.addService("led", new LedService());

LedService 的相关函数具体是由jni实现的
frameworks/base/services/core/jni/com_android_server_LedService.cpp
#define LOG_TAG "pptv"#include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h"#include #include #include #include #include #include #include #include #include namespace android {static led_device_t* led_device; jint ledOpen(JNIEnv *env, jobject cls) { jint err; hw_module_t* module; hw_device_t* device; ALOGE("pptv,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 { ALOGE("pptv,open failed ..."); return -1; } }else{ ALOGE("pptv,get_module failed ..."); } return -1; }void ledClose(JNIEnv *env, jobject cls) { //ALOGI("native ledClose ..."); //close(fd); }jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status) { ALOGE("pptv 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)); }}

frameworks/base/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_UsbHostManager.cpp \ $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \ $(LOCAL_REL_DIR)/com_android_server_LedService.cpp \

hardware/libhardware/modules/led
有三个文件是led的hal代码
Android.mk
# Copyright (C) 2012 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # #http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := led.default# HAL module implementation stored in # hw/.default.so LOCAL_MODULE_RELATIVE_PATH := hw LOCAL_C_INCLUDES := hardware/libhardware LOCAL_SRC_FILES := led_hal.c LOCAL_SHARED_LIBRARIES := liblog LOCAL_MODULE_TAGS := enginclude $(BUILD_SHARED_LIBRARY)

led_hal.h
#ifndef ANDROID_LED_INTERFACE_H #define ANDROID_LED_INTERFACE_H#include #include #include #include __BEGIN_DECLSstruct 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 "pptv"/* 1. ??HMI_module_t//* 2. ?en, d_device_t//* 3. ?d_device_t//*hardware\libhardware\modules\vibrator\vibrator.c */#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //FILE *fd; int errNum = 0; 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) { ALOGE(" UID\t= %d\n", getuid()); ALOGE(" EUID\t= %d\n", geteuid()); ALOGE(" GID\t= %d\n", getgid()); ALOGE(" EGID\t= %d\n", getegid()); //fd =fopen("/sys/devices/xgpio_beep.29/state", "w"); fd = open("/sys/devices/xgpio_beep.29/state", O_RDWR); //if(fd==NULL){ if(fd== -1){ errNum = errno; ALOGE("open fail errno = %d reason = %s \n", errNum, strerror(errno)); ALOGI("led_hal led_open failed!!!----"); return 0; }else{ ALOGI("led_hal led_open sucess!!!"); return -1; } }static int led_ctrl(struct led_device_t* dev, int which, int status) { //int ret = fprintf(fd, "%s","1"); //int ret = ioctl(fd, status, which); int ret = write(fd,"1",1); ALOGE("led_hal 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 5.0 开始由于seLinux权限控制,直接控制sys或者dev设备驱动文件会报错E/SELinux ( 154): avc: denied { add } for service=led scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager
所以设置app访问led方法如下:
1、设置设备节点的上下文
device/rockchip/common/sepolicy/file_contexts:
/sys/devices/xgpio_beep.29/stateu:object_r:led_device:s0

2、声明设备类型
device/rockchip/common/sepolicy/file.te
type led_device, fs_type,sysfs_type;

3、申请system_server服务访问led设备节点权限
device/rockchip/common/sepolicy/system_server.te
添加 allow system_server led_device:file rw_file_perms;

这里设置权限仅仅能让system_server能访问
/sys/devices/xgpio_beep.21/state
如果想让app不经过LedManager直接访问Ledservice,可以在文件
device/rockchip/common/sepolicy/system_app.te
添加
allow system_app led_device:file rw_file_perms;

4、在seLinux注册我们的led服务
ledu:object_r:system_server_service:s0

如果不注册,运行的时候会报
ServiceManager(232): add_service('led_device,45) uid=1000 - PERMISSION DENIED

做完这些你运行还会发现
open fail errno = 13 reason = Permission denied

这个我查了很久才解决,开始因为是selinux 权限赋值错误导致,后面经过仔细查找 是因为忘记修改/sys/devices/xgpio_beep.21/state权限导致
默认情况下 /sys/devices/xgpio_beep.21/state 是root 权限 并且是0755,
而我们ledservice 是system权限。没有正常写权限。
5、修改init.rc 添加
chmod 0770 /sys/devices/xgpio_beep.29 chown system system /sys/devices/xgpio_beep.29

一定要是0770权限 如果是0660权限还是报Permission denied
相关代码
两篇很有用的博客
https://blog.csdn.net/u014767700/article/details/52996552
https://www.jianshu.com/p/9da8cdb4e684
https://blog.csdn.net/kongbaidepao/article/details/63254666
https://blog.csdn.net/LEAD_SOLO/article/details/53418117
https://blog.csdn.net/fengyuwuzu0519/article/details/73864567

    推荐阅读