From: Frank Mori Hess Date: Sat, 25 Aug 2001 23:49:23 +0000 (+0000) Subject: skeleton of a new driver, doesn't do anything yet. X-Git-Tag: r0_7_61~146 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=87807e5a5a6aa6f1b3ffc53a525b99866819af54;p=comedi.git skeleton of a new driver, doesn't do anything yet. --- diff --git a/comedi/drivers/cb_pcidas64.c b/comedi/drivers/cb_pcidas64.c new file mode 100644 index 00000000..95648f0a --- /dev/null +++ b/comedi/drivers/cb_pcidas64.c @@ -0,0 +1,499 @@ +/* + cb_pcidas64.c + This is a driver for the ComputerBoards/MeasurementComputing PCI-DAS + 64xxx cards. + + Options: + [0] - PCI bus number + [1] - PCI slot number + + Copyright (C) 2001 Frank Mori Hess + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +************************************************************************ + +STATUS: + quite experimental and unfinished + +TODO: + just about everything +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "8253.h" +#include "8255.h" + +#define PCIDAS64_DEBUG // enable debugging code +//#undef PCIDAS64_DEBUG // disable debugging code + +// PCI vendor number of ComputerBoards/MeasurementComputing +#define PCI_VENDOR_CB 0x1307 +#define TIMER_BASE 25 // 40MHz master clock + +/* PCI-DAS64xxx base addresses */ + +// indices of base address regions +#define PLX9080_BADRINDEX 0 +#define MAIN_BADRINDEX 2 +#define DIO_COUNTER_BADRINDEX 3 + +#define PLX9080_IOSIZE 0xec +#define MAIN_IOSIZE 0x302 +#define DIO_COUNTER_IOSIZE 0x29 + +// plx pci9080 configuration registers + +// bit in hexadecimal representation of range index that indicates unipolar input range +#define IS_UNIPOLAR 0x4 +// analog input ranges for most boards +static comedi_lrange ai_ranges = +{ + 8, + { + BIP_RANGE(10), + BIP_RANGE(5), + BIP_RANGE(2.5), + BIP_RANGE(1.25), + UNI_RANGE(10), + UNI_RANGE(5), + UNI_RANGE(2.5), + UNI_RANGE(1.25) + } +}; + +// analog output ranges +static comedi_lrange ao_ranges = +{ + 4, + { + BIP_RANGE(5), + BIP_RANGE(10), + UNI_RANGE(5), + UNI_RANGE(10), + } +}; + +typedef struct pcidas64_board_struct +{ + char *name; + int device_id; // pci device id + int ai_se_chans; // number of ai inputs in single-ended mode + int ai_bits; // analog input resolution + int ai_speed; // fastest conversion period in ns + int ao_nchan; // number of analog out channels + int ao_scan_speed; // analog output speed (for a scan, not conversion) +} pcidas64_board; + +static pcidas64_board pcidas64_boards[] = +{ + { + name: "pci-das6402/16", + device_id: 0x1d, + ai_se_chans: 64, + ai_bits: 16, + ai_speed: 5000, + ao_nchan: 2, + ao_scan_speed: 10000, + }, +// XXX supports more boards... +}; +// Number of boards in cb_pcidas_boards +#define N_BOARDS (sizeof(pcidas64_boards) / sizeof(pcidas64_board)) + +/* + * Useful for shorthand access to the particular board structure + */ +#define thisboard ((pcidas64_board *)dev->board_ptr) + +/* this structure is for data unique to this hardware driver. If + several hardware drivers keep similar information in this structure, + feel free to suggest moving the variable to the comedi_device struct. */ +typedef struct +{ + // base addresses (physical) + unsigned long plx9080_phys_iobase; + unsigned long main_phys_iobase; + unsigned long dio_counter_phys_iobase; + // base addresses (ioremapped) + unsigned long plx9080_iobase; + unsigned long main_iobase; + unsigned long dio_counter_iobase; + // divisor of master clock for analog input pacing + unsigned int ai_divisor; + volatile unsigned int ai_count; // number of analog input samples remaining + // divisors of master clock for analog output pacing + unsigned int ao_divisor; + volatile unsigned int ao_count; // number of analog output samples remaining + unsigned int ao_value[2]; // remember what the analog outputs are set to, to allow readback +} pcidas64_private; + +/* + * most drivers define the following macro to make it easy to + * access the private structure. + */ +#define devpriv ((pcidas64_private *)dev->private) + +/* + * The comedi_driver structure tells the Comedi core module + * which functions to call to configure/deconfigure (attach/detach) + * the board, and also about the kernel module that contains + * the device code. + */ +static int attach(comedi_device *dev,comedi_devconfig *it); +static int detach(comedi_device *dev); +comedi_driver driver_cb_pcidas={ + driver_name: "cb_pcidas64", + module: THIS_MODULE, + attach: attach, + detach: detach, +}; + +static int ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); +static int ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); +static int ao_readback_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); +static int ai_cmd(comedi_device *dev,comedi_subdevice *s); +static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s, + comedi_cmd *cmd); +static int ao_cmd(comedi_device *dev,comedi_subdevice *s); +static int ao_inttrig(comedi_device *dev, comedi_subdevice *subdev, unsigned int trig_num); +static int ao_cmdtest(comedi_device *dev,comedi_subdevice *s, + comedi_cmd *cmd); +static void handle_interrupt(int irq, void *d, struct pt_regs *regs); +static int ai_cancel(comedi_device *dev, comedi_subdevice *s); +static int ao_cancel(comedi_device *dev, comedi_subdevice *s); + +/* + * A convenient macro that defines init_module() and cleanup_module(), + * as necessary. + */ +COMEDI_INITCLEANUP(driver_cb_pcidas); + +/* + * Attach is called by the Comedi core to configure the driver + * for a particular board. + */ +static int attach(comedi_device *dev, comedi_devconfig *it) +{ + comedi_subdevice *s; + struct pci_dev* pcidev; + int index; + // base addresses + unsigned long plx9080_iobase; + unsigned long main_iobase; + unsigned long dio_counter_iobase; + + printk("comedi%d: cb_pcidas64\n",dev->minor); + +/* + * Allocate the private structure area. + */ + if(alloc_private(dev,sizeof(pcidas64_private)) < 0) + return -ENOMEM; + +/* + * Probe the device to determine what device in the series it is. + */ + + pci_for_each_dev(pcidev) + { + // is it not a computer boards card? + if(pcidev->vendor != PCI_VENDOR_CB) + continue; + // loop through cards supported by this driver + for(index = 0; index < N_BOARDS; index++) + { + if(pcidas64_boards[index].device_id != pcidev->device) + continue; + // was a particular bus/slot requested? + if(it->options[0] || it->options[1]) + { + // are we on the wrong bus/slot? + if(pcidev->bus->number != it->options[0] || + PCI_SLOT(pcidev->devfn) != it->options[1]) + { + continue; + } + } + dev->board_ptr = pcidas64_boards + index; + goto found; + } + } + + printk("No supported ComputerBoards/MeasurementComputing card found on " + "requested position\n"); + return -EIO; + +found: + + printk("Found %s on bus %i, slot %i\n", pcidas64_boards[index].name, + pcidev->bus->number, PCI_SLOT(pcidev->devfn)); + + //Initialize dev->board_name + dev->board_name = thisboard->name; + + /* Initialize devpriv->control_status and devpriv->adc_fifo to point to + * their base address. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + plx9080_iobase = + pcidev->base_address[PLX9080_BADRINDEX] & + PCI_BASE_ADDRESS_MEM_MASK; + main_iobase = + pcidev->base_address[MAIN_BADRINDEX] & + PCI_BASE_ADDRESS_MEM_MASK; + dio_counter_iobase = + pcidev->base_address[DIO_COUNTER_BADRINDEX] & + PCI_BASE_ADDRESS_MEM_MASK; +#else + if(pci_enable_device(pcidev)) + return -EIO; + pci_set_master(pcidev); + plx9080_iobase = + pcidev->resource[PLX9080_BADRINDEX].start & + PCI_BASE_ADDRESS_MEM_MASK; + main_iobase = + pcidev->resource[MAIN_BADRINDEX].start & + PCI_BASE_ADDRESS_MEM_MASK; + dio_counter_iobase = + pcidev->resource[DIO_COUNTER_BADRINDEX].start & + PCI_BASE_ADDRESS_MEM_MASK; + +#if KERNEL_VERSION_CODE >= KERNEL_VERSION(2,3,17) + + if(check_mem_region(plx9080_iobase, PLX9080_IOSIZE)) + { + /* Couldn't allocate io space */ + printk(KERN_WARNING "couldn't allocate IO space\n"); + return -EIO; + } + if(check_mem_region(main_iobase, MAIN_IOSIZE)) + { + /* Couldn't allocate io space */ + printk(KERN_WARNING "couldn't allocate IO space\n"); + return -EIO; + } + if(check_mem_region(dio_counter_iobase, DIO_COUNTER_IOSIZE)) + { + /* Couldn't allocate io space */ + printk(KERN_WARNING "couldn't allocate IO space\n"); + return -EIO; + } + + request_mem_region(plx_iobase, PLX9080_IOSIZE, "cb_pcidas64"); + devpriv->plx9080_phys_iobase = dio_counter_iobase; + request_mem_region(main_iobase, MAIN_IOSIZE, "cb_pcidas64"); + devpriv->main_phys_iobase = dio_counter_iobase; + request_mem_region(dio_counter_iobase, DIO_COUNTER_IOSIZE, "cb_pcidas64"); + devpriv->dio_counter_phys_iobase = dio_counter_iobase; + +#endif +#endif + +#ifdef PCIDAS64_DEBUG + +printk("plx9080 phys io addr 0x%lx\n", devpriv->plx9080_iobase); +printk("main phys io addr 0x%lx\n", devpriv->main_iobase); +printk("diocounter phys io addr 0x%lx\n", devpriv->dio_counter_iobase); +printk("irq %i\n", dev->irq); + +#endif + + // remap, won't work with 2.0 kernels but who cares + devpriv->plx9080_iobase = (unsigned long)ioremap(plx9080_iobase, PLX9080_IOSIZE); + devpriv->main_iobase = (unsigned long)ioremap(main_iobase, MAIN_IOSIZE); + devpriv->dio_counter_iobase = (unsigned long)ioremap(dio_counter_iobase, DIO_COUNTER_IOSIZE); + + // get irq + if(comedi_request_irq(pcidev->irq, handle_interrupt, SA_SHIRQ, "cb_pcidas64", dev )) + { + printk(" unable to allocate irq %d\n", pcidev->irq); + return -EINVAL; + } + dev->irq = pcidev->irq; + +#ifdef PCIDAS64_DEBUG + +printk("plx9080 virt io addr 0x%lx\n", devpriv->plx9080_iobase); +printk("main virt io addr 0x%lx\n", devpriv->main_iobase); +printk("diocounter virt io addr 0x%lx\n", devpriv->dio_counter_iobase); +printk("irq %i\n", dev->irq); + +#endif + +/* + * Allocate the subdevice structures. + */ + dev->n_subdevices = 5; + if(alloc_subdevices(dev)<0) + return -ENOMEM; + + s = dev->subdevices + 0; + /* analog input subdevice */ + dev->read_subdev = s; + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF; + /* WARNING: Number of inputs in differential mode is ignored */ + s->n_chan = thisboard->ai_se_chans; + s->len_chanlist = 8092; + s->maxdata = (1 << thisboard->ai_bits) - 1; + s->range_table = &ai_ranges; +// s->insn_read = ai_rinsn; +// s->do_cmd = ai_cmd; +// s->do_cmdtest = ai_cmdtest; +// s->cancel = ai_cancel; + + /* analog output subdevice */ + s = dev->subdevices + 1; + dev->write_subdev = s; + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_READABLE | SDF_WRITEABLE | SDF_GROUND; + s->n_chan = thisboard->ao_nchan; + // analog out resolution is the same as analog input resolution, so use ai_bits + s->maxdata = (1 << thisboard->ai_bits) - 1; + s->range_table = &ao_ranges; +// s->insn_read = ao_readback_insn; +// s->insn_write = ao_winsn; +// s->do_cmdtest = ao_cmdtest; +// s->do_cmd = ao_cmd; + s->len_chanlist = thisboard->ao_nchan; +// s->cancel = ao_cancel; + + // digital input + s = dev->subdevices + 2; + s->type = COMEDI_SUBD_UNUSED; + + // digital output + s = dev->subdevices + 3; + s->type = COMEDI_SUBD_UNUSED; + + /* 8255 */ + s = dev->subdevices + 4; + s->type = COMEDI_SUBD_UNUSED; +// subdev_8255_init(dev, s, ); + + return 0; +} + + +/* + * _detach is called to deconfigure a device. It should deallocate + * resources. + * This function is also called when _attach() fails, so it should be + * careful not to release resources that were not necessarily + * allocated by _attach(). dev->private and dev->subdevices are + * deallocated automatically by the core. + */ +static int detach(comedi_device *dev) +{ + printk("comedi%d: cb_pcidas: remove\n",dev->minor); + + if(devpriv) + { + if(devpriv->plx9080_iobase) + iounmap((void*)devpriv->plx9080_iobase); + if(devpriv->main_iobase) + iounmap((void*)devpriv->main_iobase); + if(devpriv->dio_counter_iobase) + iounmap((void*)devpriv->dio_counter_iobase); +#if KERNEL_VERSION_CODE >= KERNEL_VERSION(2,3,17) + if(devpriv->plx9080_phys_iobase) + release_mem_region(devpriv->plx9080_iobase, PLX9080_IOSIZE); + if(devpriv->main_iobase) + release_mem_region(devpriv->main_phys_iobase, MAIN_IOSIZE); + if(devpriv->dio_counter_iobase) + release_mem_region(devpriv->dio_counter_phys_iobase, DIO_COUNTER_IOSIZE); +#endif + } + if(dev->irq) + comedi_free_irq(dev->irq, dev); +// if(dev->subdevices) +// subdev_8255_cleanup(dev,dev->subdevices + 4); + + return 0; +} + +static int ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) +{ + return -1; +} + +static int ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) +{ + return -1; +} + +static int ao_readback_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) +{ + return -1; +} + +static int ai_cmd(comedi_device *dev,comedi_subdevice *s) +{ + return -1; +} + +static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd) +{ + return -1; +} + +static int ao_cmd(comedi_device *dev,comedi_subdevice *s) +{ + return -1; +} + +static int ao_inttrig(comedi_device *dev, comedi_subdevice *subdev, unsigned int trig_num) +{ + return -1; +} + +static int ao_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd) +{ + return -1; +} + +static void handle_interrupt(int irq, void *d, struct pt_regs *regs) +{ +} + +static int ai_cancel(comedi_device *dev, comedi_subdevice *s) +{ + return -1; +} + +static int ao_cancel(comedi_device *dev, comedi_subdevice *s) +{ + return -1; +} + + +