device driver (1)

今日からこつこつ進めます。一旦仕切り直してリトライします。色々参考にしつつ。

雑多な情報から

昨日まで色々調べつつ中途半端に終わっているナニを控えておく。

errno

まず errno から。どういった仕掛けなのかは不明ですが、ワシ環境では include/asm は include/asm-i386 への symlink になっている模様。

  • include/asm/errno.h は include/asm-generic/errno.h を include してるのみ
  • include/asm-generic/errno.h は errno-base.h を include しつつ 35 から 131 までの errrno が定義
  • include/asm-generic/errno-base.h では 1 から 34 までの errno が定義

でも errno 戻すナニで asm/errno.h を直接 include ではないはず。で探してみたら include/linux/errno.h というソレがある。これを include しておけば良いのでしょうか。

open

チェック項目を以下に。

  • 対応する H/W があれば利用可能 && オンラインな事を調べる
  • マイナー番号が有効かどうか
  • 排他制御
  • エラーの時には負の値を戻す
  • 成功時には 0 を戻す
release

以下の要求を満たせ、とある。

  • 処理中 I/O は後始末
  • 必要なら H/W 資源解放
  • 排他制御してるならリセット
カーネル空間とユーザ空間のやりとり
  • copy_{from, to}_user とか strncpy_{from, to}_user とかを使う模様
    • linux/uaccess.h を include する
    • その中で asm/uaccess.h が include されている
    • 関数の実体はアーキテクチャ毎に定義

asm/uaccess.h でのプロトタイプ定義は以下

unsigned long __must_check copy_to_user(void __user *to,
				const void *from, unsigned long n);
unsigned long __must_check copy_from_user(void *to,
				const void __user *from, unsigned long n);
long __must_check strncpy_from_user(char *dst, const char __user *src,
				long count);
long __must_check __strncpy_from_user(char *dst,
				const char __user *src, long count);
verify_area

この関数で_指定されたアクセスが許可されている_かどうかを判断するとの事。ただし、現在見ている 2.6.20 では rw_verify_area に変更されている模様。プロトタイプ定義は以下。

extern int rw_verify_area(int, struct file *, loff_t *, size_t);

実体は fs/read_write.c な模様。しかし用途がなんか微妙な気が。ってやっぱりそうか。ここによると verify_area は access_ok というナニになっている模様。linux/asm/uaccess.h にて define されている。

/**
 * access_ok: - Checks if a user space pointer is valid
 * @type: Type of access: %VERIFY_READ or %VERIFY_WRITE.  Note that
 *        %VERIFY_WRITE is a superset of %VERIFY_READ - if it is safe
 *        to write to a block, it is always safe to read from it.
 * @addr: User space pointer to start of block to check
 * @size: Size of block to check
 *
 * Context: User context only.  This function may sleep.
 *
 * Checks if a pointer to a block of memory in user space is valid.
 *
 * Returns true (nonzero) if the memory block may be valid, false (zero)
 * if it is definitely invalid.
 *
 * Note that, depending on architecture, this function probably just
 * checks that the pointer is in the user space range - after calling
 * this function, memory access functions may still return -EFAULT.
 */
#define access_ok(type,addr,size) (likely(__range_ok(addr,size) == 0))

コメントによればビンゴな模様。使い方的には例えば以下??

access_ok(VERIFY_WRITE, dst, len)

VERIFY_READ とか VERIFY_WRITE とかも include/asm/uaccess.h にて define されている。

open あたりから動作を見てみる

とりあえずイチから、という事でディレクトリを掘って、既存な Makefile をコピッておいて修正。

obj-m	:= yamanetoshi.o

KDIR	:= /lib/modules/$(shell uname -r)/build
PWD	:= $(shell pwd)

default:
#	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
	$(MAKE) -C $(KDIR) M=$(PWD) modules

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

で、最初はシンプルに、で以下。

#include <linux/module.h>   // required by all modules
#include <linux/kernel.h>   // required by printk()
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <asm/uaccess.h>    // required by copy_{to_user, from_user}

MODULE_LICENSE("GPL");

#define EOF (-1)
#define TRUE 1
#define FALSE 0

#define DRIVERNAME "yamanetoshi"
static int fifo_major = 130;

static int yamanetoshi_open(struct inode *inode, struct file *file)
{
        printk("yamanetoshi_open\n");
        printk(" MINOR(inode->i_rdev): %d\n", MINOR(inode->i_rdev));

        return 0;
}

static int yamanetoshi_release(struct inode *inode, struct file *file)
{
        printk("yamanetoshi_close\n");
        return 0;
}

struct file_operations fifo_fops = {
        .owner = THIS_MODULE,
        .read = NULL,
        .write = NULL,
        .ioctl = NULL,
        .open = yamanetoshi_open,
        .release = yamanetoshi_release,
};

// Start/Init function

static int yamanetoshi_init(void) {
        int ret;

        printk("initialize the fifo device\n");
        ret = register_chrdev(fifo_major, DRIVERNAME, &fifo_fops);

        if(ret < 0)
                return ret;

        if(fifo_major == 0)
                fifo_major = ret;

    return 0;
}

// End/Cleanup function
static void yamanetoshi_exit(void) {
        printk("cleanup the fifo device\n");
        unregister_chrdev(fifo_major, DRIVERNAME);
}

module_init(yamanetoshi_init);
module_exit(yamanetoshi_exit);

これだと open/close だけなんで vmware でなくても良いな。とりあえず insmod/rmmod の正常動作は確認。よく考えたら device 作ってない。

# mknod ./fifo100130 c 130 100
# mknod ./fifo101130 c 130 101
# ls
total 24
-rw-r--r-- 1 rms  rms       232 Aug 21 23:01 Makefile
-rw-r--r-- 1 rms  rms         0 Aug 21 23:01 Module.symvers
crw-r--r-- 1 root root 130, 100 Aug 21 23:14 fifo100130
crw-r--r-- 1 root root 130, 101 Aug 21 23:15 fifo101130
-rw-r--r-- 1 rms  rms      1474 Aug 21 23:00 yamanetoshi.c
-rw-r--r-- 1 rms  rms      3248 Aug 21 23:01 yamanetoshi.ko
-rw-r--r-- 1 rms  rms       681 Aug 21 23:01 yamanetoshi.mod.c
-rw-r--r-- 1 rms  rms      2072 Aug 21 23:01 yamanetoshi.mod.o
-rw-r--r-- 1 rms  rms      1888 Aug 21 23:01 yamanetoshi.o
#

で、

# cat >fifo100130

してみたら

Aug 21 23:15:43 debian kernel: yamanetoshi_open
Aug 21 23:15:43 debian kernel:  MINOR(inode->i_rdev): 100
Aug 21 23:15:46 debian kernel: yamanetoshi_close

との事。一般ユーザだとどうか

$ cat >fifo100130 
bash: fifo100130: 許可がありません
$

ぬ。一般ユーザからだと書き込みできない?
ええと、ls -l してみたら確かに権限無いな。とほほ。権限 666 にしたら大丈夫でした。とりあえずハードル一つ越えました。とりあえず今日はこれで終了。