Linux のカーネルを いじるとき の Tips など。
usb_card_reader/Linuxメモ USBカードリーダ
Linux で、CF などの USB カードリーダ/ライタを使う方法について
装置は、ADTEC の AD-MCR/W
これを Linux につなぐと
cat /proc/scsi/scsi Attached devices: Host: scsi0 Channel: 00 Id: 00 Lun: 00 Vendor: Y-E DATA Model: CF Card Reader Rev: 1.01 Type: Direct-Access ANSI SCSI revision: 02
というふうに CF が scsi として見える。
さて、MMC などはどうやって使うノダロウ?
LUN として見えるだろうと思い
echo "scsi add-single-device 0 0 0 1" > /proc/scsi/scsi echo "scsi add-single-device 0 0 0 2" > /proc/scsi/scsi echo "scsi add-single-device 0 0 0 3" > /proc/scsi/scsi
とやってみた。
そうすると ..
cat /proc/scsi/scsi Attached devices: Host: scsi0 Channel: 00 Id: 00 Lun: 00 Vendor: Y-E DATA Model: CF Card Reader Rev: 1.01 Type: Direct-Access ANSI SCSI revision: 02 Host: scsi0 Channel: 00 Id: 00 Lun: 01 Vendor: Y-E DATA Model: SM Card Reader Rev: 1.01 Type: Direct-Access ANSI SCSI revision: 02 Host: scsi0 Channel: 00 Id: 00 Lun: 02 Vendor: Y-E DATA Model: MS Card Reader Rev: 1.01 Type: Direct-Access ANSI SCSI revision: 02 Host: scsi0 Channel: 00 Id: 00 Lun: 03 Vendor: Y-E DATA Model: SD Card Reader Rev: 1.01 Type: Direct-Access ANSI SCSI revision: 02
このように、4 つの LUN があって使えることがわかった。 ソフト的には 4 つの LUN があって同時に使用できるが、MMC/SD と メモリースティックは、1つのスロットなので、同時にはつかえない。
cat /proc/bus/usb/devices T: Bus=01 Lev=03 Prnt=04 Port=02 Cnt=01 Dev#= 10 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=057b ProdID=0020 Rev= 1.01 S: Manufacturer=Y-E DATA S: Product=Silicon Media R/W S: SerialNumber=000000006258 C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage E: Ad=81(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
hub.c: USB new device connect on bus1/2/3/3, assigned device number 10 usb.c: USB device 10 (vend/prod 0x57b/0x20) is not claimed by any active driver. Initializing USB Mass Storage driver... usb.c: registered new driver usb-storage scsi0 : SCSI emulation for USB Mass Storage devices Vendor: Y-E DATA Model: CF Card Reader Rev: 1.01 Type: Direct-Access ANSI SCSI revision: 02 Attached scsi removable disk sda at scsi0, channel 0, id 0, lun 0 SCSI device sda: 62720 512-byte hdwr sectors (32 MB) sda: Write Protect is off sda: sda1 WARNING: USB Mass Storage data integrity not assured USB Mass Storage device found at 10 USB Mass Storage support registered.
USB の Mass Storage device ならなんでも使えるのではないようだ。
おなじ ADTEC の AD-CFR/W2 でためしてみる。
こいつは、DataFAB 製の chip を使っているようだ。
hub.c: USB new device connect on bus1/2/3/3, assigned device number 11 usb.c: USB device 11 (vend/prod 0xc3c/0x11) is not claimed by any active driver. Initializing USB Mass Storage driver... usb.c: registered new driver usb-storage scsi0 : SCSI emulation for USB Mass Storage devices WARNING: USB Mass Storage data integrity not assured USB Mass Storage device found at 11 USB Mass Storage support registered.
T: Bus=01 Lev=03 Prnt=04 Port=02 Cnt=01 Dev#= 11 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=0c3c ProdID=0011 Rev= 1.13 C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr= 70mA I: If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage E: Ad=81(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
となる。
S: Manufacturer=XXX S: Product=XXXX S: SerialNumber=XXX
が出ていない。
そういえば、 linux の config で DATAFAB を使えるようにするというメニューがある。 CONFIG_USB_STORAGE_DATAFAB 。
これをキーにして、grep かけてみると .. どうも DATAFAB の USB mass は、drivers/usb/storage の unusual_devs.h に登録してないと使えなさそうだ。
#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ vendorName, productName,useProtocol, useTransport, \ initFunction, flags) \
という定義らしい。id_vender, id_product は 0xc3c, 0x11 なので 適当に作ってみる。
UNUSUAL_DEV( 0x0c3c, 0x0011, 0x0000, 0xffff, "ADTEC", "AD-CFR/W2", US_SC_SCSI, US_PR_DATAFAB, NULL, US_FL_MODE_XLATE | US_FL_START_STOP ),
scsi0 : SCSI emulation for USB Mass Storage devices Vendor: ADTEC Model: AD-CFR/W2 Rev: 0113 Type: Direct-Access ANSI SCSI revision: 02
そして、ランプもついた。
ところが ...
SCSI device sda: 62720 512-byte hdwr sectors (32 MB) sda: Write Protect is off sda: sda1
と続くはずの Disk の probe のメッセージが出ない。 ... 上の定義はまちがっているということだ。
中をあけて、chip の上に datafab の刻印があったので、 他の Datafab ものと 同じ ようにしてみたんだが ... ダメなのか。
US_PR_DATAFAB というのは、DATAFAB 特有のやりかたをするということだ。 ひょっとしたら それは昔のデバイスに対するもので、 こいつは、一般的な手続きで、やれば良いのかも知れない。
■ カーネルスタック拡張
Linux は、カーネルはスレッドとして動作する。したがって スタックのサイズは 固定であり スタックがオーバーフローすることは許されない。 ( このことは Linux に限らずカーネル一般の制限である。)
カーネルで、スタックがオーバーフローすれば、システム全体が止まる 危険がある。
さて linux(i386) のカーネルスタックはつぎのような構造になっている。
+------------------+ ---- | task_struct | 約 1.5KB ↑ +------------------+ | | | | 8KB | ↑ | | stack | ↓ +------------------+ ----
ようするに、6.5K バイトほどしかない。この上で、システムコールのサービス を行い、さらに割り込みが来たら、スタックをつみあげて割り込み処理をおこなう。
スタックは、ほんとうに いかなるときもオーバフローしないのであろうか?
しらべてみると、カーネルのスタックのサイズを 16k にする パッチが、2002/2 月頃 出ていた。
... このようなパッチがあるということは、 実際にオーバフローがおきているのかもしれない。
kernel/exit.c の部分をみると、現状のカーネルでどこまでスタックを 使っているのかが分かるようになっているようだ。
まだ、つくりさしのパッチ(2.4.17 用)
上のパッチとあんまりかわらない。... しかも変なことをしているので、 動かないかも。
簡単に違いをかくと、task_struct にマークをいれてあって、 schedule() で、それが 壊れたことを検出すると BUG() で落ちる。
BUG() の先で やっぱり スタックつかって コードが動くわけなので、 その分の 緩衝 領域を設けてある。... その量を 8K にしてみたので、 task_struct のサイズが、9.5KB にもなってしまっている。
そのサイズを 1K バイトぐらいにするのが適切なのだが... わざと極端な 設定にしている。
■ proc を使ったツール
カーネルの データを見る.. とか カーネルの変数をちょっと変更してみる だとか ... そういうことができるツールを モジュールで組み込みたい
.. となると userland のインターフェイスは、proc が便利。
以下では 2 つの例 と コンパイルのやりかたのメモ
#include <linux/config.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/timex.h> #include <linux/smp_lock.h> #include <linux/init.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> struct test_struct { int ibuf_len; char ibuf[128]; int obuf_len; char obuf[512]; } test_data; static int test_read_proc(char *, char **,off_t, int, int *, void *); static int test_write_proc(struct file *, const char *, unsigned long, void *); static int test_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { struct test_struct *p = (struct test_struct *)data; int len; if (!p) return -EINVAL; len = p->obuf_len - (int)off; if (len < 0) return -EINVAL; if (len > count) { len = count; } memcpy(page, p->obuf+(int)off, len); return len; } static int test_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { struct test_struct *p = (struct test_struct *)data; long len; if (!p) return -EINVAL; len = count; if (len > 128) len = 128; if (copy_from_user(p->ibuf,buffer,len)) return -EFAULT; memcpy(p->obuf,p->ibuf,(int)len); p->obuf_len = len; return count; } static struct proc_dir_entry *test_entry; int __init test_init(void) { test_data.ibuf_len = 0; test_data.obuf_len = 10; test_entry = create_proc_entry("testa", 0600, NULL); if (!test_entry) return -ENOMEM; test_entry->nlink = 1; test_entry->data = (void *)&test_data; test_entry->read_proc = test_read_proc; test_entry->write_proc = test_write_proc; return 0; } void test_exit(void) { remove_proc_entry("testa", NULL); test_entry = NULL; } /* no EXPORT_SYMBOL(xxx); */ module_init(test_init); module_exit(test_exit);
#include <linux/config.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/timex.h> #include <linux/smp_lock.h> #include <linux/init.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> struct test_struct { int state; struct semaphore lock; int ibuf_len; char ibuf[128]; int obuf_len; char obuf[128]; } test_data; static int test_read_proc(char *, char **,off_t, int, int *, void *); static int test_write_proc(struct file *, const char *, unsigned long, void *); static void callback_done(struct test_struct *); static int test_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { struct test_struct *p = (struct test_struct *)data; int len; if (!p) return -EINVAL; while (p->state == 1) { /* wait for call_back */ down(&p->lock); up(&p->lock); } if (p->state != 2) return -EINVAL; len = p->obuf_len - (int)off; if (len < 0) return -EINVAL; if (len > count) { len = count; } memcpy(page, p->obuf+(int)off, len); return len; } static void callback_done(struct test_struct *p) { int len = p->ibuf_len; memcpy(p->obuf,p->ibuf,(int)len); p->obuf_len = len; p->state = 2; up(&p->lock); MOD_DEC_USE_COUNT; } static int test_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { struct test_struct *p = (struct test_struct *)data; long len; if (!p) return -EINVAL; if ((p->state != 0) && (p->state != 2)) return -EINVAL; len = count; if (len > 128) len = 128; if (copy_from_user(p->ibuf,buffer,len)) return -EFAULT; p->ibuf_len = len; MOD_INC_USE_COUNT; down(&p->lock); p->state = 1; callback_done(p); /* test */ return count; } static struct proc_dir_entry *test_dir_entry; static struct proc_dir_entry *test_entry; int __init test_init(void) { test_dir_entry = proc_mkdir("suz", NULL); if (!test_dir_entry) return -ENOMEM; test_data.ibuf_len = 0; test_data.obuf_len = 10; /* for test */ test_data.state = 2; /* for test */ init_MUTEX(&(test_data.lock)); test_entry = create_proc_entry("test", 0600, test_dir_entry); if (!test_entry) return -ENOMEM; test_entry->nlink = 1; test_entry->data = (void *)&test_data; test_entry->read_proc = test_read_proc; test_entry->write_proc = test_write_proc; return 0; } void test_exit(void) { remove_proc_entry("test", test_dir_entry); remove_proc_entry("suz", NULL); test_dir_entry = NULL; test_entry = NULL; } /* no EXPORT_SYMBOL(xxx); */ module_init(test_init); module_exit(test_exit);
cc -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer \ -fno-strict-aliasing -pipe -fno-strength-reduce -m486 -malign-loops=2 \ -malign-jumps=2 -malign-functions=2 -DCPU=586 -DMODULE -DMODVERSIONS \ -include /usr/include/linux/modversions.h -c $1
gcc -D__KERNEL__ -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \ -fomit-frame-pointer -fno-strict-aliasing -fno-common \ -pipe -march=i686 -DMODULE -DMODVERSIONS \ -include /usr/include/linux/modversions.h \ -DEXPORT_SYMTAB -c $1