Pylone Blog

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) より使い勝手がよいかもしれません。

関連記事