T42のボリュームボタンを何とかしたい

おれんじ・ぺこ

ThinkPad T42のキーボードには、Access IBMボタンとかボリュームを上げ下げするボタンがついています。Windowsでボリュームを上げ下げするボタンを押すと、TVでリモコンを押したみたいに現在のボリュームの値が画面上に表示されます。これをNetBSDでも使いたい。
ぐぐってみた結果、どうやらAccess IBMボタンとかボリュームを上げ下げするボタンを押すと不揮発性のメモリ領域(RTCとかを保存しているところ)の値が変更するようです。で、LinuxとかOpenBSDは/dev/nvramというデバイスファイルを用意して、その領域をアクセスできるようにしています。ユーザランドからは、TPB - ThinkPad Buttonsというツールを使って一定時間ごとにその領域に変更がないかポーリングしています。しかしいくら機種依存の機能とはいえ、不揮発性のメモリ領域をそのままユーザランドに見せるのはカーネルのインターフェースとしていかがなものでしょう…
さて、肝心のNetBSDの場合についてですが、NetBSDには/dev/nvramなんてものはありません。仕方がないので、OpenBSDから移植してみました。以下にpatchを添付します。

diff -ruN sys.orig/arch/i386/conf/files.i386 sys/arch/i386/conf/files.i386
--- sys.orig/arch/i386/conf/files.i386	2005-10-15 22:47:19.000000000 +0900
+++ sys/arch/i386/conf/files.i386	2005-10-15 22:47:55.000000000 +0900
@@ -466,4 +466,8 @@
 attach vesatext at vesabios
 file	arch/i386/bios/vesa_text.c	vesatext
 
+# NVRAM for 'Access IBM' button etc...
+defpseudo      nvram
+file arch/i386/i386/nvram.c            nvram
+
 include "arch/i386/conf/majors.i386"
diff -ruN sys.orig/arch/i386/conf/majors.i386 sys/arch/i386/conf/majors.i386
--- sys.orig/arch/i386/conf/majors.i386	2005-10-15 22:47:19.000000000 +0900
+++ sys/arch/i386/conf/majors.i386	2005-10-15 22:47:56.000000000 +0900
@@ -107,6 +107,7 @@
 device-major	rd		char 105 block 22	rd
 device-major	ct		char 106 block 23	ct
 device-major	mt		char 107 block 24	mt
+device-major	nvram		char 108		nvram
 
 # Majors up to 143 are reserved for machine-dependant drivers.
 # New machine-independant driver majors are assigned in 
diff -ruN sys.orig/arch/i386/i386/nvram.c sys/arch/i386/i386/nvram.c
--- sys.orig/arch/i386/i386/nvram.c	1970-01-01 09:00:00.000000000 +0900
+++ sys/arch/i386/i386/nvram.c	2005-10-15 22:47:58.000000000 +0900
@@ -0,0 +1,163 @@
+/*	$OpenBSD: nvram.c,v 1.3 2004/11/11 08:28:28 jcs Exp $ */
+
+/*
+ * Copyright (c) 2004 Joshua Stein <jcs@openbsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#if defined(__NetBSD__)
+#include <sys/event.h>
+#endif
+
+#include <dev/ic/mc146818reg.h>
+#include <i386/isa/nvram.h>
+
+/* checksum is calculated over bytes 2 to 31 and stored in byte 32 */
+#define NVRAM_CSUM_START	(MC_NVRAM_START + 2)
+#define NVRAM_CSUM_END		(MC_NVRAM_START + 31)
+#define NVRAM_CSUM_LOC		(MC_NVRAM_START + 32)
+
+#define NVRAM_SIZE		(128 - MC_NVRAM_START)
+
+/* #define NVRAM_DEBUG 1 */
+
+void nvramattach(int);
+
+int nvramopen(dev_t dev, int flag, int mode, struct proc *p);
+int nvramclose(dev_t dev, int flag, int mode, struct proc *p);
+int nvramread(dev_t dev, struct uio *uio, int flags);
+
+int nvram_csum_valid(void);
+int nvram_get_byte(int byteno);
+
+#if defined(__NetBSD__)
+const struct cdevsw nvram_cdevsw = {
+        nvramopen, nvramclose, nvramread, nullwrite, nullioctl,
+        nostop, notty, nopoll, nommap, nokqfilter,
+};
+#endif
+
+static int nvram_initialized;
+
+void
+nvramattach(int num)
+{
+	if (num > 1)
+		return;
+
+	if (nvram_initialized || nvram_csum_valid()) {
+#ifdef NVRAM_DEBUG
+		printf("nvram: initialized\n");
+#endif
+		nvram_initialized = 1;
+	} else
+		printf("nvram: invalid checksum\n");
+}
+
+int
+nvramopen(dev_t dev, int flag, int mode, struct proc *p)
+{
+	/* TODO: re-calc checksum on every open? */
+
+	if ((minor(dev) != 0) || (!nvram_initialized))
+		return (ENXIO);
+
+	if ((flag & FWRITE))
+		return (EPERM);
+
+	return (0);
+}
+
+int
+nvramclose(dev_t dev, int flag, int mode, struct proc *p)
+{
+	return (0);
+}
+
+int
+nvramread(dev_t dev, struct uio *uio, int flags)
+{
+	u_char buf[NVRAM_SIZE];
+	u_int pos = uio->uio_offset;
+	u_char *tmp;
+	int count = min(sizeof(buf), uio->uio_resid);
+	int ret;
+
+	if (!nvram_initialized)
+		return (ENXIO);
+
+	if (uio->uio_resid == 0)
+		return (0);
+
+#ifdef NVRAM_DEBUG
+	printf("attempting to read %d bytes at offset %d\n", count, pos);
+#endif
+
+	for (tmp = buf; count-- > 0 && pos < NVRAM_SIZE; ++pos, ++tmp)
+		*tmp = nvram_get_byte(pos);
+
+#ifdef NVRAM_DEBUG
+	printf("nvramread read %d bytes (%s)\n", (tmp - buf), tmp);
+#endif
+
+	ret = uiomove((caddr_t)buf, (tmp - buf), uio);
+
+	uio->uio_offset += uio->uio_resid;
+
+	return (ret);
+}
+
+int
+nvram_get_byte(int byteno)
+{
+	if (!nvram_initialized)
+		return (ENXIO);
+
+	return (mc146818_read(NULL, byteno + MC_NVRAM_START) & 0xff);
+}
+
+int
+nvram_csum_valid()
+{
+	u_short csum = 0;
+	u_short csumexpect;
+	int nreg;
+
+	for (nreg = NVRAM_CSUM_START; nreg <= NVRAM_CSUM_END; nreg++)
+		csum += mc146818_read(NULL, nreg);
+
+	csumexpect = mc146818_read(NULL, NVRAM_CSUM_LOC) << 8 |
+	    mc146818_read(NULL, NVRAM_CSUM_LOC + 1);
+
+#ifdef NVRAM_DEBUG
+	printf("nvram: checksum is %x, expecting %x\n", (csum & 0xffff),
+		csumexpect);
+#endif
+
+	return ((csum & 0xffff) == csumexpect);
+}

使い方は、カーネルの設定ファイルに

pseudo-device	nvram

を追加して、普通にカーネルコンパイルしてください。続いて、

$ sudo mknod /dev/nvram c 108 0

で/dev/nvramを作成してください。
あとはtpbをコンパイルしてインストールすれば、キーボードについてるボリュームボタンなどが使えるようになります。