#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/major.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/mm.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include "pci17x4.h"
#include "Reg9056.h"

/* ==== pci17x4 private device structure, module private data, and some accessor macros ==== */
struct plx9056_dmadesc {
  uint32_t pciaddr;						/* PCI destination address (physical address, must be in the 1st 4GB) */
  uint32_t ioaddr;						/* ADC I/O port to transfer data from */
  uint32_t count;						/* number of bytes to transfer */
  uint32_t chain;						/* pointer to next DMA descriptor */
};

enum {
  PLX_REGS = 1,							/* PCI BAR index for the PLX 9054 run-time registers */
  ADC_REGS = 2,							/* PCI BAR index for the PCI 17x4 A/D control registers */
  DMA_DESCS_PER_PAGE = PAGE_SIZE / sizeof(struct plx9056_dmadesc)
};

struct pci17x4_private_data {
  char name[32];						/* device name */
  uint64_t waitcount;						/* User space is waiting for this DMA transfer to complete */
  struct pci_dev *pdev;						/* kernel's PCI device desriptor */
  struct device *dev;						/* kernel's Linux device descriptor */
  struct pci17x4_private_data *next;				/* next item in driver's linked list of all attached devices */
  struct pci17x4_dmastatus dma;					/* part of our structure that is visible to the user via ioctl()-s */
  struct plx9056_dmadesc **dmadescs;				/* PLX DMA descriptors (with physical addresses) of our circular buffers (two level indexing) */
  int16_t **dmabuffers;						/* DMA buffers */
  wait_queue_head_t waitqueue;					/* usual stuff: go to sleep here */
  spinlock_t lock;						/* used only while running to protect the DMA status record */
  uint16_t raddr[4];						/* PCI BAR start addresses (copied from pdev) */
  uint16_t rsize[4];						/* PCI BAR lengths (copied from pdev) */
  uint8_t minor;						/* this device's minor number */
  uint8_t busy;							/* indicates device is opened */
  uint8_t doblockwait;						/* Indicates user space waiting on block read */
};

#define pci17x4_printk						if(PCI17X4_DEBUG) printk
#define pci17x4_timeout(adc)					(max(5, (int)msecs_to_jiffies(adc->dma.setup.timeout)))
#define pci17x4_dmadesc(adc,idx)				(adc->dmadescs[(idx) / DMA_DESCS_PER_PAGE][(idx) % DMA_DESCS_PER_PAGE])
#define pci17x4_validport(adc,bar,idx,size)			({ size_t _ix = (idx), _sz = (size) - 1; (((_ix + _sz) < (adc)->rsize[(bar)]) && !(_ix & _sz)); })
#define pci17x4_inportb(adc,bar,idx)				((uint32_t)(inb((adc)->raddr[(bar)] + (uint16_t)(idx))))
#define pci17x4_inportw(adc,bar,idx)				((uint32_t)(inw((adc)->raddr[(bar)] + (uint16_t)(idx))))
#define pci17x4_inportl(adc,bar,idx)				((uint32_t)(inl((adc)->raddr[(bar)] + (uint16_t)(idx))))
#define pci17x4_outportb(adc,bar,idx,data)			(outb((uint8_t)(data), ((adc)->raddr[(bar)] + (uint16_t)(idx))))
#define pci17x4_outportw(adc,bar,idx,data)			(outw((uint16_t)(data), ((adc)->raddr[(bar)] + (uint16_t)(idx))))
#define pci17x4_outportl(adc,bar,idx,data)			(outl((uint32_t)(data), ((adc)->raddr[(bar)] + (uint16_t)(idx))))
#define pci17x4_reg16to32(rval)					((rval) | ((rval) << 16))
#define pci17x4_reg8to16(rval)					((rval) | ((rval) << 8))
#define pci17x4_reg8to32(rval)					(pci17x4_reg16to32(pci17x4_reg8to16(rval)))

static void pci17x4_setuptrigger(struct pci17x4_private_data *adc);
static void pci17x4_checktrigger(struct pci17x4_private_data *adc);

