割り込みデバイスドライバ


はじめに

mouseintデバイスドライバコード

このデバイスドライバは仲吉さんによって作成されました。

includeファイルなど

#include <linux/version.h>
#include <linux/modversions.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/interrupt.h>
#include <linux/tqueue.h>
#include <asm/uaccess.h>
#include <asm/keyboard.h>

#define MOUSEINT_IRQ 12

static int bh   = 0;
static int soft = 0;
static int which_tq = 0;
static int soft_level = 0;

以下のMODULE_PARMはドライバのインストール時にドライバ内の 変数の値を変更するための仕掛けです。 変数はこの場合、bh, soft, which_tq, soft_levelです。
MODULE_PARM(bh, "i");
MODULE_PARM(soft, "i");
MODULE_PARM(which_tq, "i");
MODULE_PARM(soft_level, "i");

static struct tq_struct mouseint_task;
DECLARE_WAIT_QUEUE_HEAD(mouseint_wq);

void mouseint_softirq(unsigned long);
DECLARE_TASKLET(mouseint_tasklet, mouseint_softirq, (unsigned long)0);

static int mouse_counter;
static int irqid;

Bottom halfハンドラ

static void mouseint_bottom_half(void)
{
  printk("(mouseint_bottom_half)BH\n");
  printk("(mouseint_bottom_half)jif:%ld\n", jiffies);
  
  wake_up_interruptible(&mouseint_wq);
}

static void mouseint_bh_handler(void)
{
  if( mouse_counter )
    return;
  mouse_counter++;
  printk("(mouseint_bh_handler)BH handler\n");
  printk("(mouseint_bh_handler)jif:%ld\n", jiffies);

  switch(which_tq){
  case 0:
    queue_task(&mouseint_task, &tq_immediate);
    break;
  case 1:
    queue_task(&mouseint_task, &tq_timer);
    break;    
  default:
    queue_task(&mouseint_task, &tq_immediate);
    break;
  }
  mark_bh(IMMEDIATE_BH);
}

Non software interruptハンドラ

static void mouseint_handler(void)
{
  if( mouse_counter )
    return;
  mouse_counter++;
  printk("(mouseint_handler)Non software interrupt\n");
  printk("(mouseint_handler)jif:%ld\n", jiffies);
  
  wake_up_interruptible(&mouseint_wq);
}

Software interruptハンドラ

void mouseint_softirq(unsigned long data)
{
  printk("(mouseint_softirq)software irq\n");
  printk("(mouseint_softirq)jif:%ld\n", jiffies);

  wake_up_interruptible(&mouseint_wq);
}

static void mouseint_soft_handler(void)
{
  if( mouse_counter )
    return;
  mouse_counter++;
  printk("(mouseint_soft_handler)Software handler\n");
  printk("(mouseint_soft_handler)jif:%ld\n", jiffies);

  switch(soft_level){
  case 0:
    tasklet_hi_schedule (&mouseint_tasklet);
    break;
  case 1:
    tasklet_schedule (&mouseint_tasklet);
    break;    
  default:
    tasklet_schedule (&mouseint_tasklet);
    break;
  }
}

open/close

static
int mouseint_open(struct inode *inode, struct file *filp)
{
  MOD_INC_USE_COUNT;
  return 0;
}

static
void mouseint_close(struct inode *inode, struct file *filp)
{
  MOD_DEC_USE_COUNT;
  return;
}

read

int mouseint_read(struct inode *inode, struct file filp,
                       char *buf, int count)
{
  printk("(mouseint_read)Waiting for mouse IRQ\n");
  printk("(mouseint_read)jif:%ld\n", jiffies);
  mouse_counter = 0;

  //if(soft == 1)
  //tasklet_schedule(&mouseint_tasklet);

  interruptible_sleep_on(&mouseint_wq);

  printk("(mouseint_read)wake up\n");
  printk("(mouseint_read)jif:%ld\n", jiffies);

  return 0;
}

file_operation構造体の定義

static struct file_operations mouseint_fops =
{
  owner:THIS_MODULE,                    
  read: mouseint_read,          
  open: mouseint_open,          
  release: mouseint_close,              
};

init_module

extern int mouseint_init(void);
module_init(mouseint_init);

static int mouseint_init(void)
{
  int err;

  if(register_chrdev (60, "mouseint", &mouseint_fops) != 0)
    return - EIO;

  printk("*** mouseint module init!\n");

  if(bh == 0 && soft == 0) {
    
    err = request_irq(MOUSEINT_IRQ, mouseint_handler, SA_SHIRQ, "mouseint", &irq
id);
    if (err) printk("Request_irq failed(%d)\n", err);
    printk("(mouseint_init)Non software interrupt handler was selected\n");
  }

  if(bh == 1 && soft == 0) {   
    err = request_irq(MOUSEINT_IRQ, mouseint_bh_handler, SA_SHIRQ, "mouseint", &
irqid);
    if (err) printk("Request_irq failed(%d)\n", err);
    mouseint_task.routine = mouseint_bottom_half;
    mouseint_task.data    = NULL;
    printk("(mouseint_init)BH handler was selected\n");
    printk("(mouseint_init)Task Queue type: %d\n", which_tq);
  }

  if(bh == 0 && soft == 1) {   
    err = request_irq(MOUSEINT_IRQ, mouseint_soft_handler, SA_SHIRQ, "mouseint",
 &irqid);
    if (err) printk("Request_irq failed(%d)\n", err);
    printk("(mouseint_init)Software Int. handler was selected\n");
    printk("(mouseint_init)Software Int. Level: %d\n", soft_level);
  }

  return 0;
}

cleanup_module

extern void mouseint_cleanup(void);
module_exit(mouseint_cleanup);

void mouseint_cleanup(void)
{
  free_irq(MOUSEINT_IRQ, &irqid);
  unregister_chrdev (60, "mouseint");
  
  printk("*** mouseint module removed\n");
}

Makefile

DEV    = mouseint
CC     = gcc 

KERNEL_DIR = /usr/local/src/linux

DEFS   = -DMODULE -D__KERNEL__ 

CFLAGS =  -I $(KERNEL_DIR)/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe  -march=i686

DEVS = mouseint
TEST = read_mouseint

all : $(DEVS).o $(TEST)

$(DEVS).o:$(DEVS).c
        $(CC) $(CFLAGS) $(DEFS) -c $(DEVS).c

device:
        mknod -m 666 /dev/$(DEVS) c 60 0

$(TEST): $(TEST).c
        $(CC) -o $(TEST) $(TEST).c

clean:
        rm -f *.o *~
ドライバのインストールの方法ですが、 bhとsoftというドライバパラメータがあります。
# insmod mouseint.o
とした場合はbh = soft = 0を選択することとなり、 no software interruptハンドラが選ばれます。
#insmod mouseint.o bh=1
とした場合はbh = 1, soft = 0を選択することとなり、 bottom halfハンドラが選ばれます。
#insmod mouseint.o soft=1
としたした場合は、bh = 0, soft = 1を選択することとなり、 software interruptハンドラが選ばれます。 また、 bottom halfハンドラが選ばれたら、
#insmod mouseint.o bh=1 which_tq=0
#insmod mouseint.o bh=1 which_tq=1
どちらかを選べます。前者はすぐにハンドラを起動するように セットアップしますが、後者の場合はタイマにまかせます。 さらに、 software interruptハンドラが選ばれたら、
#insmod mouseint.o soft=1 soft_level=0
#insmod mouseint.o soft=1 soft_level=1
どちらかを選べます。 前者はxxx、後者はxxx。

mouseintデバイスドライバを使ったユーザプログラム

下記のプログラムはドライバがインストールされた後、実行可能な プログラムです。実行した後、マウスを動かしてください。プログラムは 終了します。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
     
main()
{
  int fd;
  char *buf;
  size_t count;
  fd = open("/dev/mouseint", O_RDWR);
  printf("fd = %d\n",fd);
  if(fd == -1)
    exit(-1);

  read(fd, buf, count);

  close(fd);

}

そこで、dmesgコマンドを使ってシステムメッセージを見ると、 ドライバから出されたメッセージが書かれていて、動作を確認できます。 これはNon software interruptハンドラが選ばれた場合のメッセージです。
*** mouseint module init!
(mouseint_init)Non software interrupt handler was selected
(mouseint_handler)Non software interrupt
(mouseint_handler)jif:9767332
(mouseint_read)Waiting for mouse IRQ
(mouseint_read)jif:9767708
(mouseint_handler)Non software interrupt
(mouseint_handler)jif:9767819
(mouseint_read)wake up
(mouseint_read)jif:9767819
これはBottom half interruptハンドラが選ばれた場合のメッセージです。
*** mouseint module init!
(mouseint_init)BH handler was selected
(mouseint_init)Task Queue type: 0
(mouseint_bh_handler)BH handler
(mouseint_bh_handler)jif:9823351
(mouseint_read)Waiting for mouse IRQ
(mouseint_read)jif:9823629
(mouseint_bh_handler)BH handler
(mouseint_bh_handler)jif:9823788
(mouseint_read)wake up
(mouseint_read)jif:9825508
これはSoftware interruptハンドラが選ばれた場合のメッセージです。
*** mouseint module init!
(mouseint_init)Software Int. handler was selected
(mouseint_init)Software Int. Level: 0
(mouseint_soft_handler)Software handler
(mouseint_soft_handler)jif:9843775
(mouseint_softirq)software irq
(mouseint_softirq)jif:9843775
(mouseint_read)Waiting for mouse IRQ
(mouseint_read)jif:9844877
(mouseint_soft_handler)Software handler
(mouseint_soft_handler)jif:9844950
(mouseint_softirq)software irq
(mouseint_softirq)jif:9844950
(mouseint_read)wake up
(mouseint_read)jif:9844950