Pylone Blog - タグ:module

sysfs 経由でモジュールパラメータにアクセス (2)

以前の記事で sysfs からモジュールパラメータにアクセスする簡単な方法を紹介しましたが、今回はパラメータの型を独自に定義する例として Base36 のパラメータを持つ簡単なモジュールを紹介します。

コード

base36param.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>

static int stored_value = 0;

static int param_set_base36(const char *val, struct kernel_param *kp)
{
    if (('0' <= val[0]) && (val[0] <= '9')) {
            stored_value = val[0] -'0';
            return 0;
    }
    if (('a' <= val[0]) && (val[0] <= 'z')) {
            stored_value = val[0] -'a' +10;
            return 0;
    }
    if (('A' <= val[0]) && (val[0] <= 'Z')) {
            stored_value = val[0] -'A' +10;
            return 0;
    }
    return -EINVAL;
}

static int param_get_base36(char *buffer, struct kernel_param *kp)
{
    if (stored_value < 0 || 36 < stored_value)
        return -EINVAL;

    if (stored_value < 10)
        buffer[0] = '0' + stored_value;
    else
        buffer[0] = 'A' + stored_value - 10;

    buffer[1] = '\0';

    return 2;
}

/* dummy checker */
#define param_check_base36(name, p) __param_check(name, p, void);

static int __init base36param_init(void)
{
    printk(KERN_INFO "%s called\n", __func__);
    return 0; /* succeeded */
}

static void __exit base36param_cleanup(void)
{
    printk(KERN_INFO "%s called\n", __func__);
    return;
}

module_init(base36param_init);
module_exit(base36param_cleanup);

module_param(stored_value, base36, 0644);
MODULE_PARM_DESC(stored_value, "can write [0-9A-Za-z], read as [0-9A-Z].");

MODULE_LICENSE("GPL");
MODULE_AUTHOR("MINAMI Hirokazu");
MODULE_DESCRIPTION("sample module which has a custom typed parameter");

Makefile:

KERNEL_SRC = /lib/modules/$(shell uname -r)/build
#CROSS_COMPILE =
#ARCH =
BUILD_DIR := $(shell pwd)
VERBOSE = 0

obj-m := base36param.o

all:
	make -C $(KERNEL_SRC) SUBDIRS=$(BUILD_DIR) KBUILD_VERBOSE=$(VERBOSE) modules

clean:
	rm -rf *.o *.ko *.mod.c .*.cmd .tmp_versions *~ Module.symvers

ビルド

環境に応じて Makefile の KERNEL_SRC, CROSS_COMPILE, ARCH を修正し、make を実行します。

使い方

値 (Base36) をパラメータに書き込む:

# echo 'h' > /sys/module/base36param/parameters/stored_value

パラメータを読み込む:

# cat /sys/module/base36param/parameters/stored_value
H

解説

モジュールパラメータに独自の型を持たせるためには、 module_param を使います。

module_param(stored_value, base36, 0644);

module_param は以下のようなマクロです。

/* Helper functions: type is byte, short, ushort, int, uint, long,
   ulong, charp, bool or invbool, or XXX if you define param_get_XXX,
   param_set_XXX and param_check_XXX. */
#define module_param_named(name, value, type, perm)			   \
	param_check_##type(name, &(value));				   \
	module_param_call(name, param_set_##type, param_get_##type, &value, perm); \
	__MODULE_PARM_TYPE(name, #type)

#define module_param(name, type, perm)				\
	module_param_named(name, name, type, perm)

param_set_##type と param_get_##type が肝です。 sysfs 経由でパラメータが読み書きされると、ここで登録される param_set_##type と param_get_##type が呼ばれます。 (module_param_call の説明は省きます)

base36param.c の場合、プリプロセッサによってマクロ展開された param_set_base36 と param_get_base36 が登録されることになります。

マクロのコメントにある通り、 独自の型ではなく int などの場合は あらかじめ用意されているヘルパー関数群が使われるので 自前で用意する必要はありません。

先日公開した 仮想バッテリドライバ はこれの応用です。 ファイルとして読み書きできるので ユーザ空間のプログラムとヘッダを共有する必要がない点は ioctl(2) より使い勝手がよいかもしれません。

関連記事

Virtual Battery Driver

Linux kernel の機能の一部である Power supply class を利用した、仮想バッテリドライバを公開します。

概要

Power supply class は、sysfs 、及び uevent によってユーザースペースから電源状態に関するイベントの監視を可能にする仕組みですが、 本ドライバはその仕組みを利用して、コマンドラインからAC電源、バッテリに関するイベントを生成し、 電源状態の変化を仮想的に再現するためのものです。

パイロンでは、バッテリを持たないカスタムボードへ Android を移植する際に使用しています。

前提

本ドライバを使用するには、Linux kernel 2.6.25 以降を、

  • CONFIG_POWER_SUPPLY

を有効にしてコンパイルした環境が必要です。

また、Android で Power supply class から uevent を受けとるには、

  • CONFIG_SYSFS_DEPRECATED_V2

も有効にする必要があります。

導入手順

  1. アーカイブを展開して、Makefile 中の各変数である、
    • 組み込むカーネルのソースへのパス(本Makefileでは'KERNEL_SRC'としています)
    • ARCH
    • CROSS_COMPILE
    の内容を利用する環境に合わせて修正し、
    # make
    
    します。(または、各変数をコマンドラインから直接、定義します)
  2. 生成したモジュールを、対象のカーネルに組み込みます。
    # insmod virtual_battery.ko
    

    予め他のPower supply classドライバがロードされている状態で本ドライバを登録すると、Kernel Panic が起きてしまう点にご注意ください。

以上の手順を実行すると、sysfs のプラットフォームデバイスディレクトリ(/sys/devices/platform/) に、 「virtual_battery.0」というディレクトリが生成されいるはずです。

$ ls /sys/devices/platform/virtual_battery.0/
bus                   power_supply:ac       uevent
modalias              power_supply:battery
power                 subsystem

また、sysfs のモジュールパラメータのディレクトリにも以下のようなディレクトリパスと仮想ファイル群が生成されているはずです。

$ ls /sys/module/virtual_battery/parameters/
ac_status           battery_health      battery_status
battery_capacity    battery_present     battery_technology

使用方法

電源状態を変更するには、sysfs に生成されたモジュールパラメータを使用します。

各仮想ファイルの使用方法は以下の通りです。

パラメータ初期状態説明
ac_status on
off
on AC 接続の状態を変更します。
battery_status charging
discharging
not-charging
full
charging バッテリの状態を変更します。
battery_health good
overheat
dead
overvoltage
failure
good バッテリの劣化状態を変更します。
battery_present true
false
true バッテリの装着状態を変更します。
battery_technology NiMH
LION
LIPO
LiFe
NiCd
LiMn
LION バッテリの種類を変更します。
battery_capacity 0から100 50 バッテリの残容量を変更します。
使用例

まずは現在の各値を確認してみます。sysfs の class ディレクトリ以下に、power_supply ディレクトリがあるので、 そこの uevent を利用すると一覧を取得することができます。

# cat  /sys/class/power_supply/uevent
PHYSDEVPATH=/devices/platform/virtual_battery.0
PHYSDEVBUS=platform
POWER_SUPPLY_NAME=battery
POWER_SUPPLY_TYPE=Battery
POWER_SUPPLY_STATUS=Charging
POWER_SUPPLY_HEALTH=Good
POWER_SUPPLY_PRESENT=1
POWER_SUPPLY_TECHNOLOGY=Li-ion
POWER_SUPPLY_CAPACITY=50

バッテリ容量とバッテリ状態を変更してみます。

# echo 30 > /sys/module/virtual_battery/parameters/battery_capacity
# echo discharging > /sys/module/virtual_battery/parameters/battery_status

変更されているか確認してみます。

# cat /sys/module/virtual_battery/parameters/battery_capacity
30
# cat /sys/module/virtual_battery/parameters/battery_status
discharging

変更したタイミングで uevent が発生するので、バッテリの状況を動的に把握することが可能です。

組み込み用途向けのカスタムボードでは、バッテリを搭載していない事も多いと思いますが、 そのような環境においても、本ドライバを利用することによって、ユーザスペースにおける電源管理の評価を行えるのではないでしょうか。

sysfs 経由でモジュールパラメータにアクセス

sysfs経由でモジュールパラメータにアクセスする方法を紹介します。

Linux-2.6でモジュールパラメータを定義するマクロがMODULE_PARMからmodule_paramに変更されました。

Linux-2.4のMODULE_PARM:

/* パラメータparamをint型で宣言 */
static int param = 0;
MODULE_PARM(param, "i");
MODULE_PARM_DESC(param, "Description");

Linux-2.6のmodule_param:

/* パラメータparamをint型で宣言 */
static int param = 0;
module_param(param, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(param, "Description");

insmodの引数として指定できるパラメータであることは同じですが、module_paramではsysfsからもパラメータにアクセスできます。

# echo 1 > /sys/module/モジュール名/parameters/param

似たようなことはprocfsでもできますが面倒です。 module_paramであればsysfsの詳細を知らなくても定義するだけでモジュール内の変数をパラメータとしてユーザ空間にexportできます。 デバッグ時など、ioctlを使うまでもない場合には便利じゃないでしょうか?

モジュールではなくカーネルにstaticリンクされててもアクセスできます。

sysfsによってファイルとして見えるパラメータのパーミッションはmodule_paramの引数で指定します。

パラメータとして使う変数が文字列の場合、module_param_string を使います。

関連記事