簡単なskeletonデバイスドライバ
Linuxデバイスドライバの構造はUNIX一般のそれと同じような構造を
持っていますが、細かい点で異なっている。図1はLinxuのデバイス
ドライバがユーザプログラムとどのように繋がっているかを説明しています。
図1:Linuxカーネルの中のデバイスドライバ
ユーザプログラムではデバイスをアクセスするためまずはopen関数を
用います。open関数の引数としてスペシャルファイルを指定します。
そのスペシャルファイルはユニークなメジャ番号を持っています。
この番号は大変重要です。カーネルはこの番号でドライバを対応づけます。
read関数がユーザプログラムから呼ばれるとカーネルはテーブルにメジャ番号順に
デバイスの関数が並んでいるので、メジャ番号に対応するデバイスの
read関数に相当するプログラムコードを呼ぶことになります。
通常は、/usr/src/linuxというディレクトリのもとでカーネルやドライバは
コンパイルされますが、
下記の様にまずは自分のディレクトリにskeleton.cを持ってきて、コンパイルし、
インストール、デバイスファイルの作成を行うのがいいでしょう。
デバイスファイルは一度作成するだけでいいです。
% gcc -c -D__KERNEL__ -DMODULE -Wall -I/usr/src/linux/include \
-DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h skeleton.c
% su
Password:
# insmod skeleton.o
# mknod -m 666 /dev/skeleton c 32 0
# exit
再度、コンパイルが必要な場合は、下記のようにまたドライバを リムーブしてから、インストールしなおす必要が
あります。
% su
Password:
# rmmod skeleton
# insmod skeleton.o
# exit
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#define CASE1 1
#define CASE2 2
static unsigned int counter = 0;
static char string [132];
static int data;
static int skeleton_open (struct inode *inode, struct file *file)
{
MOD_INC_USE_COUNT;
return 0;
}
static int skeleton_release (struct inode *inode, struct file *file)
{
MOD_DEC_USE_COUNT;
return 0;
}
static ssize_t skeleton_read (struct file *file, char *buf, size_t count, loff_t *ppos)
{
int len, err;
if( counter <= 0 )
return 0;
err = copy_to_user(buf,string,counter);
if (err != 0)
return -EFAULT;
len = counter;
counter = 0;
return len;
}
static ssize_t skeleton_write (struct file *file, const char *buf, size_t count, loff_t *ppos)
{
int err;
err = copy_from_user(string,buf,count);
if (err != 0)
return -EFAULT;
counter += count;
return count;
}
static int skeleton_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int retval = 0;
switch ( cmd ) {
case CASE1:/* for writing data to arg */
if (copy_from_user(&data, (int *) arg,
sizeof(int)))
return -EFAULT;
break;
case CASE2:/* for reading data from arg */
if (copy_to_user((int *) arg, &data,
sizeof(int)))
return -EFAULT;
break;
default:
retval = -EINVAL;
}
return retval;
}
static struct file_operations skeleton_fops =
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
NULL, /* skeleton_llseek */
skeleton_read, /* skeleton_read */
skeleton_write, /* skeleton_write */
NULL, /* skeleton_readdir */
NULL, /* skeleton_poll */
skeleton_ioctl, /* skeleton_ioctl */
NULL, /* skeleton_mmap */
skeleton_open, /* skeleton_open */
NULL, /* skeleton_flush */
skeleton_release, /* skeleton_release */
NULL, /* skeleton_fsync */
NULL, /* skeleton_fasync */
NULL, /* skeleton_check_media_change */
NULL, /* skeleton_revalidate */
NULL /* skeleton_lock */
#else /* for LINUX_VERSION_CODE 2.4.0 and later */
THIS_MODULE, /* struct module *owner;*/
NULL, /* skeleton_llseek */
skeleton_read, /* skeleton_read */
skeleton_write, /* skeleton_write */
NULL, /* skeleton_readdir */
NULL, /* skeleton_poll */
skeleton_ioctl, /* skeleton_ioctl */
NULL, /* skeleton_mmap */
skeleton_open, /* skeleton_open */
NULL, /* skeleton_flush */
skeleton_release, /* skeleton_release */
NULL, /* skeleton_fsync */
NULL, /* skeleton_fasync */
NULL, /* skeleton_lock */
NULL, /* skeleton_readv */
NULL /* skeleton_writev */
#endif
};
int init_module (void)
{
int i;
i = register_chrdev (32, "skeleton", & skeleton_fops);
if (i != 0) return - EIO;
return 0;
}
void cleanup_module (void)
{
unregister_chrdev (32, "skeleton");
}
% cat > /dev/skeleton
this is a pen.
^d
% cat < /dev/skeleton
this is a pen.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#define CASE1 1
#define CASE2 2
main() {
int fd, len, wlen;
char string[132];
int data, rdata;
strcpy(string, "this is a pen.");
fd = open("/dev/skeleton", O_RDWR);
if( fd == -1) {
printf("open error...\n");
exit(0);
}
wlen = strlen(string);
len = write(fd, string, wlen);
if( len == -1 ) {
printf("write error...\n");
exit(1);
}
len = read(fd, string, 132);
if( len == -1 ) {
printf("read error...\n");
exit(1);
}
data = 0x55555555;
ioctl(fd, CASE1, &data);
ioctl(fd, CASE2, &rdata);
if(rdata != data)
printf("Operation of ioctl was wrong...\n");
printf("written data is %x and read data is %x\n", data, rdata);
close(fd);
}