はじめに

このデバイスドライバは下記のUSB関連機器を操作するためのものです。 また、使用したPC/Linuxは下記の通りです。 このドライバはKEKの 仲吉さん佐藤さん のドライバを基に スキャナーのドライバ(/usr/src/linux/drivers/usb/scanner.c)を 参考にして作成されました。

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

includeファイル及び共有変数及びデータ構造

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/smp_lock.h>
//#define DEBUG
#include <linux/usb.h>

MODULE_AUTHOR("Y.Yasu, Yoshiji.YASU@kek.jp, http://www-online.kek.jp/~yasu/");
MODULE_DESCRIPTION("USB VME and CAMAC device driver");

#define IS_EP_BULK(ep)  ((ep).bmAttributes == USB_ENDPOINT_XFER_BULK ? 1 : 0)
#define IS_EP_BULK_IN(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOI
NT_DIR_MASK) == USB_DIR_IN)
#define IS_EP_BULK_OUT(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPO
INT_DIR_MASK) == USB_DIR_OUT)

#define IOCTL_LOAD_CDB _IOW('u', 0, int)
#define IOCTL_LOAD_SCSI_ID _IOW('u', 1, int)
#define VMECAM_MAX_MNR 16          /* We're allocated 16 minors */
#define VMECAM_BASE_MNR 128         /* USB VMECAM start at minor 128 */
#define MAX_CDB 20

#define USB_VMECAM_MINOR(X) MINOR((X)->i_rdev) - VMECAM_BASE_MNR

struct vmecam_usb_data {
  struct usb_device *vmecam_dev;
  unsigned int ifnum;     /* Interface number of the USB device */
  kdev_t vmecam_minor;    /* VMECAM minor - used in disconnect() */
  int scsi_id;             /* SCSI ID */
  char isopen;            /* Not zero if the device is open */
  char present;           /* Not zero if device is present */
  char *buf;              /* transfer buffers */
  unsigned int bulk_in_ep, bulk_out_ep, intr_ep; /* Endpoint assignments */
  wait_queue_head_t wait_q; /* read timeouts */
  struct semaphore gen_lock; /* lock to prevent concurrent reads or writes */
};

static struct vmecam_usb_data *p_vmecam_table[VMECAM_MAX_MNR] = { NULL, };
static struct usb_driver vmecam_driver;

static struct usb_device_id vmecam_device_ids [] = {
  { USB_DEVICE(0x789, 0x4) }, /* LOGITEC */
  { }                         /* Terminating entry */
};

ドライバの内部関数

int send_scsi_id_by_dev( struct usb_device *dev, int id)
{
  unsigned int       pipe;
  __u8               requesttype = 0x40;
  __u8               request     = 0x05;
  __u16              value       = 0x0107;
  __u16              index       = 0x0000;
  __u16              size        = 0;
  int                timeout     = 3*HZ; /* 5sec */
  int                r;

  pipe   = usb_rcvctrlpipe(dev, 0);
  dbg("send_scsi_id id=%d", id);
  value = ( id << 8 ) | 7;
  r = usb_control_msg(dev, pipe, request, requesttype, value,
                      index, NULL, size, timeout);
  if( r != 0 ) {
    err("send_scsi_id ERR=%d", r);
    return -1;
  }
  return 0;
}

int send_cdb(struct file *file, unsigned char *cdb, unsigned short size)
{
  struct usb_device *dev;
  unsigned int       pipe;
  __u8               requesttype = 0x21;
  __u8               request     = 0x00;
  __u16              value       = 0x0000;
  __u16              index       = 0x0000;
  int                timeout     = 10*HZ; /* 5sec */
  int                result;

  dev   = ((struct vmecam_usb_data *)(file->private_data))->vmecam_dev;
  pipe   = usb_sndctrlpipe(dev, 0);
  result = usb_control_msg(dev, pipe, request, requesttype,
                           value, index, cdb, size, timeout);
  dbg("send_cdb: cdb[0]:%x cdb[1]:%x cdb[2]:%x cdb[3]:%x cdb[4]:%x cdb[5]:%x", cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5]);
  if( result >= 0 ){
    return 0;
  }
  if( result == -EPIPE )
    usb_clear_halt(dev, pipe);
  return result;
}

open/close

static int
open_vmecam(struct inode * inode, struct file * file)
{
  struct vmecam_usb_data *vmecam;
  struct usb_device *dev;
  kdev_t vmecam_minor;
  int err=0;

  lock_kernel();

  vmecam_minor = USB_VMECAM_MINOR(inode);
  dbg("open_vmecam: vmecam_minor:%d", vmecam_minor);
  if (!p_vmecam_table[vmecam_minor]) {
    err("open_vmecam(%d): Unable to access minor data", vmecam_minor);
    err = -ENODEV;
    goto out_error;
  }
  vmecam = p_vmecam_table[vmecam_minor];
  dev = vmecam->vmecam_dev;
  if (!dev) {
    err("open_vmecam(%d): Vmecam device not present", vmecam_minor);
    err = -ENODEV;
    goto out_error;
  }
  if (!vmecam->present) {
    err("open_vmecam(%d): Vmecam is not present", vmecam_minor);
    err = -ENODEV;
    goto out_error;
  }
  if (vmecam->isopen) {
    err("open_vmecam(%d): Vmecam device is already open", vmecam_minor);
    err = -EBUSY;
    goto out_error;
  }
  init_waitqueue_head(&vmecam->wait_q);
  vmecam->isopen = 1;
  file->private_data = vmecam; /* Used by the read and write metheds */
  MOD_INC_USE_COUNT;

 out_error:
  unlock_kernel();
  return err;
}

static int
close_vmecam(struct inode * inode, struct file * file)
{
  struct vmecam_usb_data *vmecam;
  kdev_t vmecam_minor;
  vmecam_minor = USB_VMECAM_MINOR (inode);

  dbg("close_vmecam: vmecam_minor:%d", vmecam_minor);
  if (!p_vmecam_table[vmecam_minor]) {
    err("close_vmecam(%d): invalid vmecam_minor", vmecam_minor);
    return -ENODEV;
  }
  vmecam = p_vmecam_table[vmecam_minor];
  vmecam->isopen = 0;
  file->private_data = NULL;
  MOD_DEC_USE_COUNT;
  return 0;
}

ioctl

static int
ioctl_vmecam(struct inode *inode, struct file *file,
              unsigned int cmd, unsigned long arg)
{
  struct vmecam_usb_data *vmecam;
  struct usb_device *dev;
  int result;
  kdev_t vmecam_minor;
  int cdb_buf_len;
  unsigned char cdb_buf[MAX_CDB];
  int scsi_id;

  vmecam_minor = USB_VMECAM_MINOR(inode);
  if (!p_vmecam_table[vmecam_minor]) {
    err("ioctl_vmecam(%d): invalid vmecam_minor", vmecam_minor);
    return -ENODEV;
  }
  vmecam = p_vmecam_table[vmecam_minor];
  dev = vmecam->vmecam_dev;
  switch (cmd)
    {
    case IOCTL_LOAD_CDB :
      if(copy_from_user(&cdb_buf_len, (void *)arg, sizeof(int)))
        return -EFAULT;
      if(copy_from_user(cdb_buf, (void *)(arg+4), cdb_buf_len))
        return -EFAULT;
      result = send_cdb(file, cdb_buf, cdb_buf_len);
      break;
    case IOCTL_LOAD_SCSI_ID :
      if(copy_from_user(&scsi_id, (void *)arg, sizeof(int)))
        return -EFAULT;
      if( (result = send_scsi_id_by_dev(dev, scsi_id) ) != 0 )
        break;
      vmecam->scsi_id = scsi_id;
      break;
    default :
      result = -ENOIOCTLCMD;
    }
  return result;
}

read/write

ssize_t
read_write( int direction, struct file * file, char * buffer, size_t count)
{
  struct vmecam_usb_data *vmecam;
  struct usb_device *dev;
  ssize_t actual;
  kdev_t vmecam_minor;
  unsigned int pipe;
  int result = 0;
  ssize_t ret = 0;

  vmecam = file->private_data;
  vmecam_minor = vmecam->vmecam_minor;
  dev = vmecam->vmecam_dev;

  down(&(vmecam->gen_lock));

  if (signal_pending(current)) {
    ret = -EINTR;
    goto read_write_end;
  }
  if (!(vmecam->buf = (char *)kmalloc(count, GFP_KERNEL))) {
    err("vmecam: read_write: Out of memory.");
    ret = -EFAULT;
    goto read_write_end;
  }
  if( direction ) { // write
    if (copy_from_user(vmecam->buf, buffer, count)) {
      ret = -EFAULT;
      goto read_write_end1;
    }
    pipe = usb_sndbulkpipe(dev, vmecam->bulk_out_ep);
    result = usb_bulk_msg(dev,pipe,
                          vmecam->buf, count, &actual, 10*HZ);
    dbg("write stats(%d): result:%d count:%d actual:%d", vmecam_minor, result, count, actual);
  } else { // read
    pipe = usb_rcvbulkpipe(dev, vmecam->bulk_in_ep);
    result = usb_bulk_msg(dev, pipe,
                          vmecam->buf, count, &actual, 10*HZ);
    dbg("read stats(%d): result:%d count:%d actual:%d", vmecam_minor, result, count, actual);
  }
  if (result == USB_ST_TIMEOUT) { /* NAK -- shouldn't happen */
    warn("vmecam: read_write: NAK recieved.");
    ret = -ETIME;
    goto read_write_end1;
  } else if (result < 0) { /* We should not get any I/O errors */
    warn("vmecam: read_write(%d): funky result: %d. Please notify the maintainer
.", vmecam_minor, result);
    ret = -EIO;
    goto read_write_end1;
  }
  if( !direction ) { // read
    if( copy_to_user(buffer, vmecam->buf, actual) ) {
      ret =  -EFAULT;
      goto read_write_end1;
    }
  }
 read_write_end1:
  kfree(vmecam->buf);
 read_write_end:
  up(&(vmecam->gen_lock));
  if( ret )
    return ret;
  else
    return actual;
}

static ssize_t
write_vmecam(struct file * file, const char * buffer,
              size_t count, loff_t *ppos)
{
  int direction = 1;
  return read_write(direction, file, (char *)buffer, count);
}

static ssize_t
read_vmecam(struct file * file, char * buffer,
             size_t count, loff_t *ppos)
{
  int direction = 0;
  return read_write(direction, file, buffer, count);
}

probe/disconnect

static void *
probe_vmecam(struct usb_device *dev, unsigned int ifnum,
              const struct usb_device_id *id)
{
  struct vmecam_usb_data *vmecam;
  struct usb_interface_descriptor *interface;
  struct usb_endpoint_descriptor *endpoint;
  int ep_cnt;
  kdev_t vmecam_minor;
  char valid_device = 0;
  unsigned int have_bulk_in, have_bulk_out;
  int ix;

  /*
   * 1. Check Vendor/Product
   * 2. Determine/Assign Bulk Endpoints
   */
  for (ix=0; ixdescriptor.idVendor == vmecam_device_ids [ix].idVendor) &&
        (dev->descriptor.idProduct == vmecam_device_ids [ix].idProduct)) {
      valid_device = 1;
      break;
    }
  }
  if (!valid_device)
    return NULL;    /* We didn't find anything pleasing */

  if (dev->descriptor.bNumConfigurations != 1) {
    info("probe_vmecam: Only one device configuration is supported.");
    return NULL;
  }
  if (dev->config[0].bNumInterfaces != 1) {
    info("probe_vmecam: Only one device interface is supported.");
    return NULL;
  }
  interface = dev->config[0].interface[ifnum].altsetting;
  endpoint = interface[ifnum].endpoint;
  dbg("probe_vmecam: Number of Endpoints:%d", (int) interface->bNumEndpoints);
  if ((interface->bNumEndpoints != 2)) {
    info("probe_vmecam: Only two endpoints supported.");
    return NULL;
  }
  ep_cnt = have_bulk_in = have_bulk_out = 0;
  while (ep_cnt < interface->bNumEndpoints) {
    if (!have_bulk_in && IS_EP_BULK_IN(endpoint[ep_cnt])) {
      have_bulk_in = endpoint[ep_cnt].bEndpointAddress;
      dbg("probe_vmecam: bulk_in_ep:%d", have_bulk_in);
      ep_cnt++;
      continue;
    }

    if (!have_bulk_out && IS_EP_BULK_OUT(endpoint[ep_cnt])) {
      have_bulk_out = endpoint[ep_cnt].bEndpointAddress;
      dbg("probe_vmecam: bulk_out_ep:%d", have_bulk_out);
      ep_cnt++;
      continue;
    }
    info("probe_vmecam: Undetected endpoint. Notify the maintainer.");
    return NULL;    /* Shouldn't ever get here unless we have something weird */
  }

  for (vmecam_minor = 0; vmecam_minor < VMECAM_MAX_MNR; vmecam_minor++) {
    if (!p_vmecam_table[vmecam_minor])
      break;
  }
  /* Check to make sure that the last slot isn't already taken */
  if (p_vmecam_table[vmecam_minor]) {
    err("probe_vmecam: No more minor devices remaining.");
    return NULL;
  }

  if (!(vmecam = kmalloc (sizeof (struct vmecam_usb_data), GFP_KERNEL))) {
    err("probe_vmecam: Out of memory.");
    return NULL;
  }
  memset (vmecam, 0, sizeof(struct vmecam_usb_data));
  dbg ("probe_vmecam(%d): Address of vmecam:%p", vmecam_minor, vmecam);

  vmecam->bulk_in_ep = have_bulk_in;
  vmecam->bulk_out_ep = have_bulk_out;
  vmecam->present = 1;
  vmecam->vmecam_dev = dev;
  vmecam->vmecam_minor = vmecam_minor;
  vmecam->isopen = 0;

  init_MUTEX(&(vmecam->gen_lock));

  return p_vmecam_table[vmecam_minor] = vmecam;
}

static void
disconnect_vmecam(struct usb_device *dev, void *ptr)
{
  struct vmecam_usb_data *vmecam = (struct vmecam_usb_data *) ptr;

  if( vmecam->isopen )
    MOD_DEC_USE_COUNT;

  usb_driver_release_interface(&vmecam_driver,
         &vmecam->vmecam_dev->actconfig->interface[vmecam->ifnum]);

  dbg("disconnect_vmecam: De-allocating minor:%d", vmecam->vmecam_minor);
  p_vmecam_table[vmecam->vmecam_minor] = NULL;
  kfree (vmecam);
}

file_operation構造体及びUSBドライバ構造体の定義

static struct
file_operations usb_vmecam_fops = {
  read:           read_vmecam,
  write:          write_vmecam,
  ioctl:          ioctl_vmecam,
  open:           open_vmecam,
  release:        close_vmecam,
};

static struct
usb_driver vmecam_driver = {
  name:           "usbvmecam",
  probe:          probe_vmecam,
  disconnect:     disconnect_vmecam,
  fops:           &usb_vmecam_fops,
  minor:          VMECAM_BASE_MNR,
  id_table:       NULL, /* This would be vmecam_device_ids, but we
                                 need to check every USB device, in case
                                 we match a user defined vendor/product ID. */
};

init_module/cleanup_module

void __exit
usb_vmecam_exit(void)
{
  usb_deregister(&vmecam_driver);
}

int __init
usb_vmecam_init (void)
{
  if (usb_register(&vmecam_driver) < 0)
    return -1;

  info("USB VMECAM support registered.");
  return 0;
}

module_init(usb_vmecam_init);
module_exit(usb_vmecam_exit);

usbvmecamデバイスドライバ用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  = usb-vmecam
TEST    = simple-usb

all:    $(DRIVER).o $(TEST)-vme $(TEST)-camac

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

device:
	mknod -m 666 /dev/usbvmecam0 c 180 128
	mknod -m 666 /dev/usbvmecam1 c 180 129

$(TEST)-vme: $(TEST)-vme.c
	gcc -o $(TEST)-vme $(TEST)-vme.c

$(TEST)-camac: $(TEST)-camac.c
	gcc -o $(TEST)-camac $(TEST)-camac.c

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

usbvmecamデバイスドライバを使ったCAMACプログラム

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>

#define IOCTL_LOAD_CDB _IOW('u', 0, int)
#define IOCTL_LOAD_SCSI_ID _IOW('u', 1, int)

#define NAFGEN(n,a,f) ((((n<<4) + a) << 5) + f)

void setcdb_scsi_inquiry( unsigned char *cdb, int len)
{
  cdb[0] = 0x12;  // inquiry comand
  cdb[1] = cdb[2] = cdb[5] = 0;
  cdb[3] = (len>>8)&0xff;
  cdb[4] = len&0xff;
}

void setcdb_scsi_request_sense( unsigned char *cdb, int len)
{
  cdb[0] = 0x03;  // SCSI REQUEST SENSE COMMAND
  cdb[1] = cdb[2] = cdb[3] = cdb[5] = 0;
  cdb[4] = 0x12;
}

void setcdb_camac_single( unsigned char *cdb, int mode, int naf)
{
  cdb[0] = 0x09; // single action
  cdb[1] = 0; // must be zero
  cdb[2] = mode; // 16bit:2 24bit:0
  cdb[3] = ((naf & 0xff00) >>8);
  cdb[4] = naf & 0x00ff;
  cdb[5]=0;
}

void setcdb_camac_block( unsigned char *cdb, int mode, int naf, int len)
{
  cdb[0] = 0x22; // block action
  cdb[1] = 0; // must be zero
  cdb[2] = mode; // 16bit:2 24bit:0,
  cdb[3] = ((naf & 0xff00) >>8);
  cdb[4] = naf & 0x00ff;
  cdb[5] = ((len & 0xff0000) >> 16);
  cdb[6] = ((len & 0x00ff00) >> 8);
  cdb[7] = ( len & 0x0000ff);
  cdb[8] = 0;
  cdb[9] = 0;
}

static int cam_fd;
static int cdbbuf[6];

int CAMOPN( int minor, int scsi_id )
{
  char filename[16];
  strcpy(filename, "/dev/usbvmecam");
  filename[14] = minor;
  filename[15] = 0;

  if((cam_fd = open(filename, O_RDWR)) == -1 )
    return -1;
  if( ioctl(cam_fd, IOCTL_LOAD_SCSI_ID, &scsi_id) == -1 )
    return -1;
  return 0;
}

int CAMCLS ( void )
{
  if( close(cam_fd) == -1 )
    return -1;
  return 0;
}

int SCSIinquiry(int len, unsigned char *buffer)
{
  int status;

  cdbbuf[0] = 6; // size of CDB for SCSI inquiry
  setcdb_scsi_inquiry((unsigned char *)&cdbbuf[1], len);
  if( ioctl(cam_fd, IOCTL_LOAD_CDB, cdbbuf) == -1 )
    return -1;
  if( (status = read(cam_fd, buffer, len)) < 0 )
    return status;
  return 0;
}

int SCSIsense(int len, unsigned char *buffer)
{
  int status;

  cdbbuf[0] = 6; // size of CDB for SCSI request sense
  setcdb_scsi_request_sense((unsigned char *)&cdbbuf[1], len);
  if( ioctl(cam_fd, IOCTL_LOAD_CDB, cdbbuf) == -1 )
    return -1;
  if( (status = read(cam_fd, buffer, len)) < 0 )
    return status;
  return 0;
}

int readQX()
{
  unsigned char buf[0x12];
  int key, code, qual;

  if( SCSIsense(0x12, buf) )
    return -1;
  code = buf[12] & 0xff;
  key  = buf[2]  & 0x0f;
  qual = buf[13] & 0xff;
  //  printf("code = %x, key = %x, qual = %x\n", buf[12], buf[2], buf[13]);
  //  printf("code = %x, key = %x, qual = %x\n", code, key, qual);

  if( key  == 0x09 && code == 0x80 && qual == 0x06 )
    return 1;
  else if( key  == 0x09 && code == 0x80 && qual == 0x05 )
    return 0;
  else if( key  == 0x00 && code == 0x00 && qual == 0x00 )
    return 3;
  else
    return 0;
}

int CAMAC( int naf, int *data, int *q, int *x)
{
  int status, qx, function;

  cdbbuf[0] = 6; // size of CDB for CAMAC single
  setcdb_camac_single( (unsigned char *)&cdbbuf[1], 0, naf);
  if( ioctl(cam_fd, IOCTL_LOAD_CDB, cdbbuf) == -1 )
    return -1;
  function = naf & 0x1f;
  if( function < 8 ) { // read
    if( (status = read(cam_fd, data, 4)) < 0 )
      return status;
  } else if( function >15 && function < 24 ) { // write
    if( (status = write(cam_fd, data, 4)) < 0 )
      return status;
  }
  if( (qx = readQX()) < 0)
    return qx;
  *q = (qx >> 1);
  *x = (qx & 1);

  return 0;
}

int CDMA( int mode, int naf, int *buffer, int len, int *retlen)
{
  int status;

  cdbbuf[0] = 10; // size of CDB for CAMAC block action
  setcdb_camac_block( (unsigned char *)&cdbbuf[1], mode, naf, len );
  if( ioctl(cam_fd, IOCTL_LOAD_CDB, cdbbuf) == -1 )
    return -1;
  if( (naf & 0x1f) < 8 ) // read
    if( (status = read(cam_fd, buffer, len*4 )) < 0 )
      return status;
    else if( !( naf & 8 ) )
      if( (status = write(cam_fd, buffer, len*4)) < 0 )
        return status;
  return 0;
}

#define MEM 4

main( int argc, char **argv )
{
  int data, q, x, retlen;
  int databuf[8000];
  unsigned char buffer[37];
  int i, sid, status;
  char minor;
  int ccclear = NAFGEN(MEM, 0, 9);
  int ccwrite = NAFGEN(MEM, 0, 16);
  int ccread  = NAFGEN(MEM, 0, 0 );

  if(argc > 1)
    minor = *argv[1];
  else
    minor = '0';
  if(argc > 2)
    sid = atoi(argv[2]);
  else
    sid = 0; // SCSI_ID = 0
  CAMOPN( minor, sid );

  SCSIinquiry(36, buffer);
  buffer[36] = 0;
  printf("SCSIinquiry message = %s\n", &buffer[8]);

  // CGENZ
  if( (status = CAMAC(NAFGEN(30,0,1),&data,&q,&x) ) != 0 )
    return status;
  data |= 1;
  if( (status = CAMAC(NAFGEN(30,0,17),&data,&q,&x) ) != 0 )
    return status;
  printf("CGENZ done : X = %d Q = %d\n", x, q);

  // CGENC
  if( (status = CAMAC(NAFGEN(30,0,1),&data,&q,&x) ) != 0 )
    return status;
  data |= 2;
  if( (status = CAMAC(NAFGEN(30,0,17),&data,&q,&x) ) != 0 )
    return status;
  printf("CGENC done\n");

  // CREMI
  if( (status = CAMAC(NAFGEN(30,0,1),&data,&q,&x) ) != 0 )
    return status;
  data &= ~4;
  if( (status = CAMAC(NAFGEN(30,0,17),&data,&q,&x) ) != 0 )
    status;
  printf("CREMI done : X = %d Q = %d\n", x, q);

  CAMAC(ccclear ,&data,&q,&x);
  for(i = 0; i < 8000; i++) {
    CAMAC(ccwrite ,&i,&q,&x);
  }
  CAMAC(ccclear ,&data,&q,&x);
  for(i = 0; i < 8000; i++)
    CAMAC(ccread ,&data,&q,&x);

  CAMCLS();
}

usbvmecamデバイスドライバを使ったVMEプログラム

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>

#define IOCTL_LOAD_CDB _IOW('u', 0, int)
#define IOCTL_LOAD_SCSI_ID _IOW('u', 1, int)

void setcdb_scsi_inquiry( unsigned char *cdb, int len)
{
  cdb[0] = 0x12;  // inquiry comand
  cdb[1] = cdb[2] = cdb[5] = 0;
  cdb[3] = (len>>8)&0xff;
  cdb[4] = len&0xff;
}

void setcdb_vme_write( unsigned char *cdb, int vme_addr, int len)
{
  cdb[0] = 0x2a;  // write command
  cdb[1] = cdb[6] = cdb[9] = 0;
  cdb[2] = (vme_addr>>24)&0xff;
  cdb[3] = (vme_addr>>16)&0xff;
  cdb[4] = (vme_addr>>8)&0xff;
  cdb[5] = vme_addr&0xff;
  cdb[7] = (len>>8)&0xff;
  cdb[8] = len&0xff;
}

void setcdb_vme_read( unsigned char *cdb, int vme_addr, int len)
{
  cdb[0] = 0x28;  // read command
  cdb[1] = cdb[6] = cdb[9] = 0;
  cdb[2] = (vme_addr>>24)&0xff;
  cdb[3] = (vme_addr>>16)&0xff;
  cdb[4] = (vme_addr>>8)&0xff;
  cdb[5] = vme_addr&0xff;
  cdb[7] = (len>>8)&0xff;
  cdb[8] = len&0xff;
}

static int vme_fd;
static int cdbbuf[6];

int VMEOPN ( char minor, int scsi_id )
{
  char filename[16];
  strcpy(filename, "/dev/usbvmecam");
  filename[14] = minor;
  filename[15] = 0;

  if((vme_fd = open(filename, O_RDWR)) == -1 )
    return -1;
  if( ioctl(vme_fd, IOCTL_LOAD_SCSI_ID, &scsi_id) == -1 )
    return -1;
  return 0;
}

int VMECLS ( void )
{
  if( close(vme_fd) == -1 )
    return -1;
  return 0;
}

int SCSIinquiry(int len, unsigned char *buffer)
{
  int status;

  cdbbuf[0] = 6; // size of CDB for SCSI inquiry
  setcdb_scsi_inquiry((unsigned char *)&cdbbuf[1], len);
  if( ioctl(vme_fd, IOCTL_LOAD_CDB, cdbbuf) == -1 )
    return -1;
  if( (status = read(vme_fd, buffer, len)) < 0 )
    return status;
  return 0;
}

int VMEwrite(int len, int vme_addr, unsigned char *buffer)
{
  int status;

  cdbbuf[0] = 10; // size of CDB for VME write
  setcdb_vme_write((unsigned char *)&cdbbuf[1], vme_addr, len);
  if( ioctl(vme_fd, IOCTL_LOAD_CDB, cdbbuf) == -1 )
    return -1;
  if( (status = write(vme_fd, buffer, len)) < 0 )
    return status;
  return 0;
}

int VMEread(int len, int vme_addr, unsigned char *buffer)
{
  int status;

  cdbbuf[0] = 10; // size of CDB for SCSI inquiry
  setcdb_vme_read((unsigned char *)&cdbbuf[1], vme_addr, len);
  if( ioctl(vme_fd, IOCTL_LOAD_CDB, cdbbuf) == -1 )
    return -1;
  if( (status = read(vme_fd, buffer, len)) < 0 )
    return status;
  return 0;
}

#define INTREG_ADDR 0xFFFF8F00
#define LAT0  (INTREG_ADDR + 0)
#define LAT1  (INTREG_ADDR + 2)
#define FF    (INTREG_ADDR + 4)
#define IN    (INTREG_ADDR + 6)
#define PULSE (INTREG_ADDR + 8)
#define OUT   (INTREG_ADDR + 10)
#define CSR0  (INTREG_ADDR + 12)
#define CSR1  (INTREG_ADDR + 14)

short swap16( short data )
{
  char tmp;
  union swap {
    char c[2];
    short s;
  } swap_data;
  swap_data.s = data;
  tmp = swap_data.c[1];
  swap_data.c[1] = swap_data.c[0];
  swap_data.c[0] = tmp;
  return swap_data.s;
}

int swap32( int data )
{
  char tmp;
  union swap {
    char c[4];
    int s;
  } swap_data;
  swap_data.s = data;
  tmp = swap_data.c[0];
  swap_data.c[0] = swap_data.c[3];
  swap_data.c[3] = tmp;
  tmp = swap_data.c[1];
  swap_data.c[1] = swap_data.c[2];
  swap_data.c[2] = tmp;
  return swap_data.s;
}

void print_data16( short data )
{
  short result = swap16(data);
  printf("data = %x\n", result&0xff);
}

main( int argc, char **argv) {

  int vme_addr;
  int sid;
  short data;
  unsigned char buffer[37];
  int i;
  char minor;

  if(argc > 1)
    minor = *argv[1];
  else
    minor = '0';
  if(argc > 2)
    sid = atoi(argv[2]);
  else
    sid = 0; // SCSI_ID = 0
  VMEOPN( minor, sid );

  SCSIinquiry(36, buffer);
  buffer[36] = 0;
  printf("SCSIinquiry message = %s\n", &buffer[8]);

  data = swap16(0xf);
  VMEwrite(2, OUT, (unsigned char *)&data);

  data = swap16(0xf);
  VMEwrite(2, CSR0, (unsigned char *)&data);

  data = swap16(0xf);
  VMEwrite(2, CSR1, (unsigned char *)&data);

  data = 0;
  VMEread(2, LAT0, (unsigned char *)&data);
  print_data16(data);

  VMEread(2, LAT1, (unsigned char *)&data);
  print_data16(data);

  VMEread(2, FF, (unsigned char *)&data);
  print_data16(data);

  VMEread(2, IN, (unsigned char *)&data);
  print_data16(data);

  VMEread(2, PULSE, (unsigned char *)&data);
  print_data16(data);

  VMEread(2, OUT, (unsigned char *)&data);
  print_data16(data);

  VMEread(2, CSR0, (unsigned char *)&data);
  print_data16(data);

  VMEread(2, CSR1, (unsigned char *)&data);
  print_data16(data);

  VMECLS();

}