はじめに
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);
}