以前の記事で 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) より使い勝手がよいかもしれません。
Linux kernel の機能の一部である Power supply class を利用した、仮想バッテリドライバを公開します。
Power supply class は、sysfs 、及び uevent によってユーザースペースから電源状態に関するイベントの監視を可能にする仕組みですが、本ドライバはその仕組みを利用して、コマンドラインからAC電源、バッテリに関するイベントを生成し、電源状態の変化を仮想的に再現するためのものです。
パイロンでは、バッテリを持たないカスタムボードへ Android を移植する際に使用しています。
本ドライバを使用するには、Linux kernel 2.6.25 以降を、
を有効にしてコンパイルした環境が必要です。
また、Android で Power supply class から uevent を受けとるには、
も有効にする必要があります。
# makeします。(または、各変数をコマンドラインから直接、定義します)
# 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経由でモジュールパラメータにアクセスする方法を紹介します。
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 を使います。