// I/O ports wrapper code
// This file might need tweaking if you're trying to port my code to other
// x86 Unix platforms.  Code is already available for Linux, FreeBSD, and    
// QNX; see the Makefile.
//
// Original Linux code by: Patrick Reynolds <patrickr@virginia.edu>
// QNX code by: Anders Arpteg <aa11ac@hik.se>
// FreeBSD code by: Patrick Reynolds <patrickr@virginia.edu> and Charles
//                  Henrich <henrich@msu.edu>


#include <stdio.h>
#include <errno.h>
#include "config.h"

#ifdef LOCKING
#include <fcntl.h>
#include <pwd.h>
#endif

#if defined(QNX)
#include <conio.h>
#elif defined(LINUX)
#include <asm/io.h>
#elif defined(FREEBSD)
#include <machine/cpufunc.h>
#elif defined(BSDI)
#include <machine/inline.h>
#elif defined(LYNX)
#include "lynx-io.h"
#else
#error Please define a platform in the Makefile
#endif

#include "port.h"

port_t::port_t(int iport) {
  port = -1;

#ifdef LOCKING
  if (lock(iport) == -1) {
#ifdef DEBUG
    fprintf(stderr, "port 0x%x already locked\n", iport);
#endif
    return;
  }
#endif

#ifdef LINUX
  if (ioperm(iport, 3, 1) != 0) {  // grab port permissions -- must be root
    perror("ioperm()");
    return;
  }
#elif defined(FREEBSD)
  if ((devio = fopen("/dev/io", "r+")) == NULL) {
    perror("fopen /dev/io");
    return;
  }
#elif defined(LYNX)
  if (io_access() < 0) {
    perror("io_access");
    return;
  }
#endif

  port = iport;
  port1 = port + 1;
  port2 = port + 2;
  control_reg = read_control();
}

port_t::~port_t(void) {
#ifdef LOCKING
  unlock(port);
#endif
#ifdef LINUX
  if (port > 0)
    if (ioperm(port, 3, 0) != 0)  // drop port permissions -- still must
                                  // be root
      perror("ioperm()");
#elif defined(FREEBSD)
  if (devio != NULL)
    fclose(devio);
#endif
}

#ifdef LOCKING
int port_t::lock(int portnum) {
  char lockfile[80];
  sprintf(lockfile, "/tmp/LOCK.qcam.0x%x", portnum);
  if ((lock_fd = open(lockfile, O_WRONLY | O_CREAT, 0600)) == -1) {
    perror("cqcam: open");
    exit(1);
  }
  static struct flock lock_info;
  lock_info.l_type = F_WRLCK;
  if (fcntl(lock_fd, F_SETLK, &lock_info) != 0) {
    if (errno != EAGAIN)
      perror("fcntl");
    return -1;
  }
  return 0;
}
    
void port_t::unlock(int portnum) {
  if (portnum == -1)
    return;
  close(lock_fd);    // this clears the lock
  char lockfile[80];
  sprintf(lockfile, "/tmp/LOCK.qcam.0x%x", portnum);
  unlink(lockfile); 
}
#endif
