selectシステムコールやシグナルをサポートするデバイスドライバ


はじめに

selectシステムコールやシグナルを用いてCAMACを操作する方法を提供するに あたり、cc.hの変更が必要になりました。
#define CCIOC_RESET _IO('c', 1)
#define CCIOC_CAMAC _IOWR('c', 2, 9)
#define CCIOC_WAIT_LAM _IO('c', 3)
#define CCIOC_GET_LAM _IOWR('c', 4, int)
#define CCIOC_SET_SIGNAL _IOWR('c', 5, int)
#define CCIOC_CLEAR_SIGNAL _IO('c', 6)

selectシステムコールをサポートするデバイスドライバコード

include文の追加

#include <linux/poll.h>

cc_pollメソッドの追加

cc_poll:
static unsigned int cc_poll(struct file *file, poll_table *wait)
{
  //  printk("cc_poll enters.\n");
  poll_wait(file, &ccdev.waitq, wait);
  //  printk("cc_poll wakes up.\n");
  if( inl(ccdev.camac.cnafr) )
    return POLLIN;
  else
    return 0;
}

cc_ioctlメソッドへの追加

  case CCIOC_GET_LAM :
    data = inl(ccdev.camac.cnafr);
    if (copy_to_user((int *)arg, &data, sizeof(int)))
      return -EFAULT;
    break;

file_operations構造体の変更

static struct file_operations cc_fops = {
ioctl:    cc_ioctl,
poll:     cc_poll,
open:     cc_open,
release:  cc_release,
};

selectシステムコールを用いたユーザプログラム

下記のプログラムはキーボードからの入力とCAMACからの割り込みをselect システムコールを用いて待つ方法を提供しています。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <linux/wait.h>
#include "cc.h"
#define INT 5-1

main() {
  int fd;
  int cmddata[3];
  int crate = 0;
  unsigned int i = 0;
  int ccreadclear, ccenable, ccclear;
  int lam = 0;
  fd_set mask;
  int maxfd, nfound, nread;
  char buff[256];

  fd = open("/dev/cc", O_RDWR);
  if( fd == -1) {
    printf("open error...\n");
    exit(0);
  }

  FD_ZERO(&mask);
  maxfd = fd + 1;

  ioctl(fd, CCIOC_RESET);

  ccclear = NAFGEN(INT, 0, 9);
  cmddata[0] = ccclear + crate;
  ioctl(fd, CCIOC_CAMAC, cmddata);

  ccenable = NAFGEN(INT, 0, 26);
  ccreadclear  = NAFGEN(INT, 0, 2 );
  while(1) {
    FD_SET(0, &mask); // stdin
    FD_SET(fd, &mask);

    nfound = select(maxfd, &mask, (fd_set *)0, (fd_set *)0,
           (struct timeval *)0);
    if(nfound < 0) {
      printf("select error\n");
      exit(0);
    }
    if(FD_ISSET(0, &mask)){ //stdin
      printf("STDIN hit\n");
      nread = read(0, buff, 256);
      if( nread < 0 ) {
        printf("STDIN: read error\n");
        exit(0);
      } else if( nread == 0 ) {
        printf("STDIN: no data\n");
        continue;
      }
    }
    if(FD_ISSET(fd, &mask)) {
      printf("CAMAC Interrupt occurred.\n");
      cmddata[0] = ccenable + crate;
      ioctl(fd, CCIOC_CAMAC, &cmddata);
      ioctl(fd, CCIOC_GET_LAM, &lam);
      i++;
      printf("Read LAM : %x, total interrupt counter = %d\n", lam, i); 
      cmddata[0] = ccclear + crate;
      ioctl(fd, CCIOC_CAMAC, &cmddata);
    }
  }
  close(fd);
}
上記のプログラムを走らせます。CAMACからの割り込みは毎秒1回とします。 立て続けにキーボードを叩くと下記のようにキーボードからの入力を 受け入れつつ割り込みが入っている様子がわかりいます。
% ./test_cc_select

STDIN hit

STDIN hit
CAMAC Interrupt occurred.
Read LAM : 10, total interrupt counter = 1

STDIN hit

STDIN hit
CAMAC Interrupt occurred.
Read LAM : 10, total interrupt counter = 2

STDIN hit

STDIN hit

STDIN hit
CAMAC Interrupt occurred.
Read LAM : 10, total interrupt counter = 3

シグナルを使ったデバイスドライバ

CAMACデバイス構造体の変更

struct cc_device {
  char *io_base;
  char *mem_base;
  unsigned int irq;
  char *name;
  wait_queue_head_t waitq;
  struct camac camac;
  int status;
  int crate;
  int intcounter;
  int signal;
  struct  task_struct *task;
};

割り込みルーチンの変更

static void cc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{

  printk("cc_interrupt : interrupt occurred...\n");
  outl( 0,  ccdev.camac.csr );  // Disable Interrupt of CC7700
  while (irq != ccdev.irq) {
    printk("unexpected interrupt...\n");
    return;
  }
  if( ccdev.signal ) {
    send_sig(ccdev.signal, ccdev.task, 1);
    //send_sig_info(ccdev.signal, (void*)(long)(1 != 0), ccdev.task);
  } else 
    wake_up_interruptible(&ccdev.waitq);
  //  ccdev.intcounter++;
}

cc_ioctlメソッドへの追加

  case CCIOC_SET_SIGNAL :
    if (copy_from_user(&data, (int *)arg, sizeof(int)))
      return -EFAULT;
    ccdev.signal = data;
    ccdev.task = current;
    data = inl(ccdev.camac.csr);
    data |= CC_CSRM_EN;
    outl( data, ccdev.camac.csr ); // Enable Interrupt of CC7700
    break;
  case CCIOC_CLEAR_SIGNAL :
    ccdev.signal = 0;
    break;

init_moduleメソッドへの追加

  ccdev.signal = 0; // No signal handler

シグナルを使ったユーザプログラム

POSIXのシグナルを用いてみました。シグナルはSIGUSR1を使いました。 シグナルはsigsuspendルーチンが呼ばれるまでは、たとえその前に 割り込みが入りシグナルを出してもブロックされるようになっているため 安全なプログラムになっています。
#define _POSIX_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <linux/wait.h>
#include <signal.h>
#include "cc.h"
#define INT 5-1

void interrupt_completion( int signo ) {
  printf("this is CAMAC interrupt.\n");
  return;
}

main() {
  int fd;
  int cmddata[3];
  int crate = 0;
  unsigned int i = 0;
  int ccreadclear, ccenable, ccclear;
  int lam = 0;
  struct sigaction sa;
  sigset_t wait;
  int sig=SIGUSR1;

  sa.sa_handler = interrupt_completion;
  sa.sa_flags = 0;
  sigemptyset(&sa.sa_mask);
  if( sigaction(sig, &sa, NULL) ) {
    printf("sigaction error\n");
    exit(0);
  }

  fd = open("/dev/cc", O_RDWR);
  if( fd == -1) {
    printf("open error...\n");
    exit(0);
  }
  ioctl(fd, CCIOC_RESET);

  ccclear = NAFGEN(INT, 0, 9);
  cmddata[0] = ccclear + crate;
  ioctl(fd, CCIOC_CAMAC, cmddata);

  ccenable = NAFGEN(INT, 0, 26);
  ccreadclear  = NAFGEN(INT, 0, 2 );

  sigemptyset(&wait);
  sigaddset(&wait, sig);
  sigprocmask(SIG_BLOCK, &wait, NULL);

  for(i = 0; i < 20; i++) {
    cmddata[0] = ccenable + crate;
    ioctl(fd, CCIOC_CAMAC, cmddata);

    ioctl(fd, CCIOC_SET_SIGNAL, &sig);
    (void)sigsuspend(&sa.sa_mask);
    ioctl(fd, CCIOC_GET_LAM, &lam);
    printf("Read LAM : %x, total interrupt counter = %d\n", lam, i); 

    cmddata[0] = ccclear + crate;
    ioctl(fd, CCIOC_CAMAC, cmddata);

  }
  ioctl(fd, CCIOC_CLEAR_SIGNAL, &sig);
  close(fd);
}

上記のプログラムを走らせます。CAMACは毎秒1回割り込みをかけます。
% ./test_cc_signal 
this is CAMAC interrupt.
Read LAM : 10, total interrupt counter = 0
this is CAMAC interrupt.
Read LAM : 10, total interrupt counter = 1
this is CAMAC interrupt.
Read LAM : 10, total interrupt counter = 2
this is CAMAC interrupt.
Read LAM : 10, total interrupt counter = 3
this is CAMAC interrupt.
Read LAM : 10, total interrupt counter = 4
this is CAMAC interrupt.
Read LAM : 10, total interrupt counter = 5
this is CAMAC interrupt.
Read LAM : 10, total interrupt counter = 6
this is CAMAC interrupt.
Read LAM : 10, total interrupt counter = 7
this is CAMAC interrupt.
Read LAM : 10, total interrupt counter = 8