簡単な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

ドライバのプログラムコード(skeleton)

#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");
}

ドライバを使った簡単な例題(1)

% cat > /dev/skeleton
this is a pen.
^d

% cat < /dev/skeleton
this is a pen.

ドライバを使った簡単な例題(2)

#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);
}