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)
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,
};
下記のプログラムはキーボードからの入力と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