Ueventによるドライバからユーザランド層への通知

はじめに

 前節では、アプリケーションやライブラリ等のユーザーランド層からシステムコールを呼び出し、ドライバを制御する方法を説明しました。本章では、反対にドライバからユーザーランド層へ通知して、制御を変える方法として、照度センサデバイスドライバでも使用されているueventを説明します。

ueventについて

 ueventは、Linuxカーネルとユーザーランド層のプログラム間のコミュニケーションを実現する仕組みです。新しいデバイスの検出、デバイスの接続/切断、カーネルモジュールのロード/アンロード等、様々な情報を入れる事ができます。ueventは、Linuxカーネルとユーザーランド層との通信プロトコルであるnetlinkにより伝達されます。

照度センサデバイスドライバでは、ueventを以下で使用しています。

kernel/mediatek/4.4/drivers/misc/mediatek/sensors-1.0/alsps/alsps.c

kobject_uevent(&alsps_context_obj->als_mdev.this_device->kobj, KOBJ_ADD);

このkobject_uevent関数は以下に定義されています。

kernel/mediatek/4.4/lib/kobject_uevent.c

/**
 * kobject_uevent - notify userspace by sending an uevent
 *
 * @action: action that is happening
 * @kobj: struct kobject that the action is happening to
 *
 * Returns 0 if kobject_uevent() is completed with success or the
 * corresponding error when it fails.
 */
int kobject_uevent(struct kobject *kobj, enum kobject_action action)

第1引数には、ドライバ登録時に割り当てられているstruct kobjectのポインタを指定します。
第2引数には、以下の列挙体を指定します。

kernel/mediatek/4.4/include/linux/kobject.h

/*
 * The actions here must match the index to the string array
 * in lib/kobject_uevent.c
 *
 * Do not add new actions here without checking with the driver-core
 * maintainers. Action strings are not meant to express subsystem
 * or device specific properties. In most cases you want to send a
 * kobject_uevent_env(kobj, KOBJ_CHANGE, env) with additional event
 * specific variables added to the event environment.
 */
enum kobject_action {
        KOBJ_ADD,
        KOBJ_REMOVE,
        KOBJ_CHANGE,
        KOBJ_MOVE,
        KOBJ_ONLINE,
        KOBJ_OFFLINE,
        KOBJ_MAX
};

kobject_ueventは、第1引数のkobjectで、第2引数のイベントが発生したことを通知する関数となります。

 kobject_ueventでは、列挙体に定義されている情報しか通知できませんが、kobject_uevent_envを用いる事により、第3引数として任意の情報を付加できます。

/**
 * kobject_uevent_env - send an uevent with environmental data
 *
 * @action: action that is happening
 * @kobj: struct kobject that the action is happening to
 * @envp_ext: pointer to environmental data
 *
 * Returns 0 if kobject_uevent_env() is completed with success or the
 * corresponding error when it fails.
 */
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
		       char *envp_ext[])

第3引数としては、文字列の配列としてchar *の配列を指定します。配列の各要素には”定義名=値”を入れ、最後の要素はNULLとします。以下に使用している参考例を示します。

kernel/mediatek/4.4/drivers/usb/gadget/configfs.c

char *connected[2]    = { "USB_STATE=CONNECTED", NULL };

kobject_uevent_env(&android_device->kobj,
			KOBJ_CHANGE, connected);

kobject_ueventや、kobject_uevent_envによって、通知したイベントは、以下の方法でユーザーランド層のプログラムから汎用的に取得できます。

/*
 * Copyright (C) 2008 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.
 */
#include <hardware/uevent.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <linux/netlink.h>
static int fd = -1;
/* Returns 0 on failure, 1 on success */
int uevent_init()
{
    struct sockaddr_nl addr;
    int sz = 64*1024;
    int s;
    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid();
    addr.nl_groups = 0xffffffff;
    s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if(s < 0)
        return 0;
    setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        close(s);
        return 0;
    }
    fd = s;
    return (fd > 0);
}
int uevent_next_event(char* buffer, int buffer_length)
{
    while (1) {
        struct pollfd fds;
        int nr;
    
        fds.fd = fd;
        fds.events = POLLIN;
        fds.revents = 0;
        nr = poll(&fds, 1, -1);
     
        if(nr > 0 && fds.revents == POLLIN) {
            int count = recv(fd, buffer, buffer_length, 0);
            if (count > 0) {
                return count;
            } 
        }
    }
    
    // won't get here
    return 0;
}

最後に

 uevent_next_eventはAOSP(Android Open Source Project)のライブラリ関数の実装になります。システムコール等では無いので、必要に応じて同様の実装をする必要があります。uevent_next_eventの実装では、特定のデバイスからでは無く、全ての情報が取得できるので、受け取った文字列から、期待するデバイスドライバに紐づく情報か判別する必要があります。受け取った文字列の先頭に change@/devices/virtual/android_usb/android0 等が入っているので、strstr/strcasestr関数等を用いて判別することになります。kobject_uevent_envで入れた文字列は改行コード以降に入っているので、文字列操作を使用して取得する必要があります。次節に、ueventの確認結果の例を記載しているので、参考にして下さい。

コメント

タイトルとURLをコピーしました