1. 程式人生 > >Android GpioService從app到驅動

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
#endif

static 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, 
[email protected]
"); 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

57      input_method: [com.android.internal.view.IInputMethodManager]
58      bluetooth_manager: [android.bluetooth.IBluetoothManager]
59      input: [android.hardware.input.IInputManager]
60      window: [android.view.IWindowManager]
61      alarm: [android.app.IAlarmManager]
62      consumer_ir: [android.hardware.IConsumerIrService]
63      led: [android.os.ILedService]
64      vibrator: [android.os.IVibratorService]
65      content: [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;
import  android.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", "LedManager  start--- !");
                        return mLedService.ledCtrl(which, status);
                //      Log.e("pptv", "LedManager  ledCtrl--- !");
                } catch (RemoteException e) {
                        Log.e("pptv", "LedManager  error--- !");
                        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 \
  • 1

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 <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;

    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/<VIBRATOR_HARDWARE_MODULE_ID>.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 := eng

include $(BUILD_SHARED_LIBRARY)

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 "pptv"


/* 1. ʵΪHMI_module_t/

/* 2. ʵen, d_device_t/

/* 3. ʵd_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>
#include <errno.h>
#include <string.h>

//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/state  u: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服務

led    u: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

--------------------- 本文來自 terry--tt 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/lb5761311/article/details/79739569?utm_source=copy