PIOと割り込み処理を含むPCI-CAMACデバイスドライバ


はじめに

このデバイスドライバは東陽テクニカ製のPCIインターフェース(cc/pci)と CAMACコントローラ(CC7700)を使ったCAMACデバイスドライバです。

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

includeファイル及び共有変数

#include <linux/module.h>
#include <linux/version.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include "cc.h"

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

static struct cc_device ccdev;

CAMAC関連のインクルードファイルはcc.hで下記のように 定義されます。
cc.h:

#define PCI_VENDOR_ID_CC77 1
#define PCI_DEVICE_ID_CC77 0xcc77
#define CC7X00_MAJOR 36
#define CARD_NAME "cc"
#define CC_CRATE_NO 1
#define CC_TIMEOUT 500
#define CCIOC_RESET _IO('ps', 1)
#define CCIOC_CAMAC _IOWR('ps', 2, 8)
#define CCIOC_WAIT_LAM _IO('cc', 3)

struct camac {
  int csr;            /* Control/Status Register */
  int cnafr;          /* Crate address and NAF Register */
  int datar;          /* Data Register */
  int exer;           /* EXEcute Register */
};

/* NAF code generation */
#define NAFGEN(n,a,f) ((((n<<4) + a) << 5) + f)

#define CC_ADDR 0x310   /* Base Address of PCI/CC-7700 */
#define CC_SIZE 0x10    /* Size of the registers */
#define CC_IRQ  0x9     /* IRQ of PCI/CC-7700 */
#define CSR   0         /* Control/Status Register */
#define CCR   4         /* Crate address & CAMAC Command Registers */
#define LAMR  4         /* LAM  Register */
#define DATR  8         /* Data Low Register */
#define EXER  12        /* EXEcute Register */

/* CSR field */
#define CC_CSRM_NOQ     1       /* NO Q */
#define CC_CSRM_NOX     2       /* NO X */
#define CC_CSRM_NOQX    0XFFFC  /* NO Q and X */
#define CC_CSRM_CLEAR   1       /* CAMAC Clear */
#define CC_CSRM_INIT    2       /* CAMAC Initialize */
#define CC_CSRM_INHBIT  4       /* CAMAC Inhibit */
#define CC_CSRM_EN      8       /* Enable Interrupt */
#define CC_CSRM_DONE    0X10    /* Completion Status */
#define CC_CSRM_ONLINE  0X20    /* Data Ready */
#define CC_CSRM_LAM     0X40    /* LAM is present. */
#define CC_CSRM_RST     0X40    /* Reset the controller */
#define CC_CSRM_LAMI    0X80    /* LAM internal */

/* CAMAC Command field */
#define CC_NAFRM_F       0       /* Functioin */
#define CC_NAFRK_F8      8       /* F = 8 */
#define CC_NAFRK_F16     0x10    /* F = 16 */
#define CC_NAFRK_F24     0x18    /* F = 24 */
#define CC_NAFRM_A       0x20    /* Subaddress */
#define CC_NAFRM_N       0X200   /* Station number */

/* EXE field */
#define CC_EXERM_GO     1       /* Go CAMAC Cycle */

CAMACアクセス関数
void CAMAC(int naf, int *data, int *qx) {
  int status;

  if( CC_NAFRK_F16 & naf ) {
    if( CC_NAFRK_F8 & naf ) {
      outl( naf, ccdev.camac.cnafr );
      outl( CC_EXERM_GO, ccdev.camac.exer);
      while(!(( *qx = inl(ccdev.camac.csr)) & CC_CSRM_DONE ));
    } else {
      outl(naf,ccdev.camac.cnafr);
      outl(*data, ccdev.camac.datar);
      outl(CC_EXERM_GO, ccdev.camac.exer);
      while(!(( *qx = inl(ccdev.camac.csr)) & CC_CSRM_DONE ));
    }
  } else {
    if( CC_NAFRK_F8 & naf ) {
      outl(naf, ccdev.camac.cnafr);
      outl(CC_EXERM_GO,ccdev.camac.exer);
      while(!(( *qx = inl(ccdev.camac.csr)) & CC_CSRM_DONE ));
    } else {
      outl(naf, ccdev.camac.cnafr);
      outl(CC_EXERM_GO, ccdev.camac.exer);
      while(!(( *qx = inl(ccdev.camac.csr)) & CC_CSRM_DONE ));
      *data = inl(ccdev.camac.datar);
    }
  }
  status = ~status;
  *qx = status & (CC_CSRM_NOQ| CC_CSRM_NOX);
}

void dump_reg() {

  printk("CSR   = %x\n", inl(ccdev.camac.csr));
  printk("LAMR = %x\n", inl(ccdev.camac.cnafr));
  printk("DATAR   = %x\n", inl(ccdev.camac.datar));
}


void ccreset(){
    outl( CC_CSRM_RST, ccdev.camac.csr);
    outl( CC_CSRM_CLEAR, ccdev.camac.csr);
}

open/close

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

static void cc_release(struct inode * inode, struct file * file)
{
  MOD_DEC_USE_COUNT;
}

ioctl

static int cc_ioctl(struct inode *inode, struct file *file,
                    unsigned int cmd, unsigned long arg)
{
  int retval = 0;
  int cnaf, data, qx;
  int cmddata[3];

  switch ( cmd ) {
  case CCIOC_RESET :
    ccreset();
    dump_reg();
    break;
  case CCIOC_CAMAC:
    if (copy_from_user(cmddata, (int *)arg, sizeof(int)*2))
      return -EFAULT;
    cnaf = cmddata[0];
    data = cmddata[1];
    CAMAC(cnaf, &data, &qx );
    cmddata[1] = data;
    cmddata[2] = qx;
    if (copy_to_user((int *)arg, cmddata, sizeof(int)*3))
      return -EFAULT;
    break;
  case CCIOC_WAIT_LAM :
    data = inl(ccdev.camac.csr);
    data |= CC_CSRM_EN;
    cli();
    outl( data, ccdev.camac.csr ); // Enable Interrupt of CC7700
    interruptible_sleep_on_timeout(&ccdev.waitq, CC_TIMEOUT);
    outl( 0,  ccdev.camac.csr );  // Disable Interrupt of CC7700
    data = inl(ccdev.camac.cnafr);
    if (copy_to_user((int *)arg, &data, sizeof(int)))
      return -EFAULT;
    break;
  default:
    retval = -EINVAL;
  };
  return 0;
}

割り込みルーチン
static void cc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{

  outl( 0,  ccdev.camac.csr );  // Disable Interrupt of CC7700
  while (irq != ccdev.irq) {
    printk("unexpected interrupt...\n");
    return;
  }
  wake_up_interruptible(&ccdev.waitq);
}

file_operation構造体の定義

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

init_module

int init_module(void)
{
  struct pci_dev *pcidev=NULL;
  int retval;

  if(!pci_present())
    return -ENODEV;

  pcidev = pci_find_device(PCI_VENDOR_ID_CC77,  PCI_DEVICE_ID_CC77, pcidev);
  if (pcidev == NULL) {
    return -ENODEV;
  }
  ccdev.irq = 9;
  pcidev->irq = ccdev.irq;
  pci_write_config_byte(pcidev, PCI_INTERRUPT_LINE, ccdev.irq);
  retval = pci_enable_device(pcidev);
  if(retval)
    return retval;
  ccdev.io_base = (char *)pci_resource_start(pcidev, 0);
  printk("CC7x00:IO_BASE = %x\n", ccdev.io_base);
  ccdev.camac.csr = ccdev.io_base + CSR;
  ccdev.camac.cnafr = ccdev.io_base + CCR;
  ccdev.camac.datar = ccdev.io_base + DATR;
  ccdev.camac.exer = ccdev.io_base + EXER;
  printk("irq number of CC = %x\n", ccdev.irq);

  retval = register_chrdev(CC7X00_MAJOR,CARD_NAME,&cc_fops);
  if (retval) {
    printk("unable to get major %d for CAMAC\n", CC7X00_MAJOR);
    return retval;
  }
  retval = request_irq(ccdev.irq, cc_interrupt, SA_INTERRUPT, CARD_NAME, &ccdev.
irq);
  if (retval){
    printk("pci_cc77: interrupt registration fault on level %d\n", ccdev.irq);
    return retval;
  }
  init_waitqueue_head(&ccdev.waitq);
  printk("CAMAC(CC7X00) has been installed.\n");
  return 0;
}

cleanup_module

void cleanup_module(void)
{
  free_irq(ccdev.irq, &ccdev.irq);
  unregister_chrdev(CC7X00_MAJOR,CARD_NAME);
  printk("CAMAC(CC7X00) has been removed.\n");
}

Makefile

INCDIR = -I/usr/src/linux/include

VERSIONINC = -include /usr/src/linux/include/linux/modversions.h

CFLAGS = -O2 -Wall -c -D__KERNEL__ -DMODULE -Wall $(INCDIR) $(VERSIONINC)

DRIVER  = cc
TEST    = test_cc

all:    $(DRIVER).o $(TEST) $(TEST)_int

$(DRIVER).o:   $(DRIVER).c $(DRIVER).h
        gcc $(CFLAGS) $(DRIVER).c

device:
        mknod -m 666 /dev/$(DRIVER) c 36 0


$(TEST): $(TEST).c $(DRIVER).h
        gcc -o $(TEST) $(TEST).c

$(TEST)_int: $(TEST)_int.c $(DRIVER).h
        gcc -o $(TEST)_int $(TEST)_int.c

clean:
        rm -f *.o *~ core $(TEST) $(TEST)_int

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

#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 MEM 3-1

main() {
  int fd;
  int cmddata[3];
  int crate = 0;
  unsigned int i = 0;
  int ccwrite, ccread, ccclear;

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

  ccclear = NAFGEN(MEM, 0, 9);
  ccwrite = NAFGEN(MEM, 0, 16);
  ccread  = NAFGEN(MEM, 0, 0 );
  cmddata[0] = ccclear + crate;
  ioctl(fd, CCIOC_CAMAC, cmddata);

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

    cmddata[0] = ccwrite + crate;
    cmddata[1] = i;
    ioctl(fd, CCIOC_CAMAC, cmddata);

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

    cmddata[0] = ccread + crate;
    ioctl(fd, CCIOC_CAMAC, cmddata);
    //printf("MEM : read data = %x qx = %x\n", cmddata[1], cmddata[2]);

    i++;
  }
  close(fd);
}

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

#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 = 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 );
  while(1) {
    cmddata[0] = ccenable + crate;
    ioctl(fd, CCIOC_CAMAC, cmddata);

    ioctl(fd, CCIOC_WAIT_LAM, &lam);
    i++;
    printf("Read LAM : %x, total interrupt counter = %d\n", lam, i); 

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

  }
  close(fd);
}