linux/drivers/staging/comedi/drivers/amplc_pc263.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/amplc_pc263.c
   3    Driver for Amplicon PC263 and PCI263 relay boards.
   4
   5    Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
   6
   7    COMEDI - Linux Control and Measurement Device Interface
   8    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   9
  10    This program is free software; you can redistribute it and/or modify
  11    it under the terms of the GNU General Public License as published by
  12    the Free Software Foundation; either version 2 of the License, or
  13    (at your option) any later version.
  14
  15    This program is distributed in the hope that it will be useful,
  16    but WITHOUT ANY WARRANTY; without even the implied warranty of
  17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18    GNU General Public License for more details.
  19
  20    You should have received a copy of the GNU General Public License
  21    along with this program; if not, write to the Free Software
  22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  23
  24*/
  25/*
  26Driver: amplc_pc263
  27Description: Amplicon PC263, PCI263
  28Author: Ian Abbott <abbotti@mev.co.uk>
  29Devices: [Amplicon] PC263 (pc263), PCI263 (pci263 or amplc_pc263)
  30Updated: Wed, 22 Oct 2008 14:10:53 +0100
  31Status: works
  32
  33Configuration options - PC263:
  34  [0] - I/O port base address
  35
  36Configuration options - PCI263:
  37  [0] - PCI bus of device (optional)
  38  [1] - PCI slot of device (optional)
  39  If bus/slot is not specified, the first available PCI device will be
  40  used.
  41
  42Each board appears as one subdevice, with 16 digital outputs, each
  43connected to a reed-relay. Relay contacts are closed when output is 1.
  44The state of the outputs can be read.
  45*/
  46
  47#include "../comedidev.h"
  48
  49#include "comedi_pci.h"
  50
  51#define PC263_DRIVER_NAME       "amplc_pc263"
  52
  53/* PCI263 PCI configuration register information */
  54#define PCI_VENDOR_ID_AMPLICON 0x14dc
  55#define PCI_DEVICE_ID_AMPLICON_PCI263 0x000c
  56#define PCI_DEVICE_ID_INVALID 0xffff
  57
  58/* PC263 / PCI263 registers */
  59#define PC263_IO_SIZE   2
  60
  61/*
  62 * Board descriptions for Amplicon PC263 / PCI263.
  63 */
  64
  65enum pc263_bustype { isa_bustype, pci_bustype };
  66enum pc263_model { pc263_model, pci263_model, anypci_model };
  67
  68struct pc263_board {
  69        const char *name;
  70        const char *fancy_name;
  71        unsigned short devid;
  72        enum pc263_bustype bustype;
  73        enum pc263_model model;
  74};
  75static const struct pc263_board pc263_boards[] = {
  76        {
  77         .name = "pc263",
  78         .fancy_name = "PC263",
  79         .bustype = isa_bustype,
  80         .model = pc263_model,
  81         },
  82#ifdef CONFIG_COMEDI_PCI
  83        {
  84         .name = "pci263",
  85         .fancy_name = "PCI263",
  86         .devid = PCI_DEVICE_ID_AMPLICON_PCI263,
  87         .bustype = pci_bustype,
  88         .model = pci263_model,
  89         },
  90#endif
  91#ifdef CONFIG_COMEDI_PCI
  92        {
  93         .name = PC263_DRIVER_NAME,
  94         .fancy_name = PC263_DRIVER_NAME,
  95         .devid = PCI_DEVICE_ID_INVALID,
  96         .bustype = pci_bustype,
  97         .model = anypci_model, /* wildcard */
  98         },
  99#endif
 100};
 101
 102#ifdef CONFIG_COMEDI_PCI
 103static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table) = {
 104        {
 105        PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263,
 106                    PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
 107        0}
 108};
 109
 110MODULE_DEVICE_TABLE(pci, pc263_pci_table);
 111#endif /* CONFIG_COMEDI_PCI */
 112
 113/*
 114 * Useful for shorthand access to the particular board structure
 115 */
 116#define thisboard ((const struct pc263_board *)dev->board_ptr)
 117
 118/* this structure is for data unique to this hardware driver.  If
 119   several hardware drivers keep similar information in this structure,
 120   feel free to suggest moving the variable to the struct comedi_device struct.  */
 121#ifdef CONFIG_COMEDI_PCI
 122struct pc263_private {
 123        /* PCI device. */
 124        struct pci_dev *pci_dev;
 125};
 126
 127#define devpriv ((struct pc263_private *)dev->private)
 128#endif /* CONFIG_COMEDI_PCI */
 129
 130/*
 131 * The struct comedi_driver structure tells the Comedi core module
 132 * which functions to call to configure/deconfigure (attach/detach)
 133 * the board, and also about the kernel module that contains
 134 * the device code.
 135 */
 136static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it);
 137static int pc263_detach(struct comedi_device *dev);
 138static struct comedi_driver driver_amplc_pc263 = {
 139        .driver_name = PC263_DRIVER_NAME,
 140        .module = THIS_MODULE,
 141        .attach = pc263_attach,
 142        .detach = pc263_detach,
 143        .board_name = &pc263_boards[0].name,
 144        .offset = sizeof(struct pc263_board),
 145        .num_names = ARRAY_SIZE(pc263_boards),
 146};
 147
 148static int pc263_request_region(unsigned minor, unsigned long from,
 149                                unsigned long extent);
 150static int pc263_dio_insn_bits(struct comedi_device *dev,
 151                               struct comedi_subdevice *s,
 152                               struct comedi_insn *insn, unsigned int *data);
 153static int pc263_dio_insn_config(struct comedi_device *dev,
 154                                 struct comedi_subdevice *s,
 155                                 struct comedi_insn *insn, unsigned int *data);
 156
 157/*
 158 * This function looks for a PCI device matching the requested board name,
 159 * bus and slot.
 160 */
 161#ifdef CONFIG_COMEDI_PCI
 162static int
 163pc263_find_pci(struct comedi_device *dev, int bus, int slot,
 164               struct pci_dev **pci_dev_p)
 165{
 166        struct pci_dev *pci_dev = NULL;
 167
 168        *pci_dev_p = NULL;
 169
 170        /* Look for matching PCI device. */
 171        for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
 172             pci_dev != NULL;
 173             pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
 174                                      PCI_ANY_ID, pci_dev)) {
 175                /* If bus/slot specified, check them. */
 176                if (bus || slot) {
 177                        if (bus != pci_dev->bus->number
 178                            || slot != PCI_SLOT(pci_dev->devfn))
 179                                continue;
 180                }
 181                if (thisboard->model == anypci_model) {
 182                        /* Match any supported model. */
 183                        int i;
 184
 185                        for (i = 0; i < ARRAY_SIZE(pc263_boards); i++) {
 186                                if (pc263_boards[i].bustype != pci_bustype)
 187                                        continue;
 188                                if (pci_dev->device == pc263_boards[i].devid) {
 189                                        /* Change board_ptr to matched board. */
 190                                        dev->board_ptr = &pc263_boards[i];
 191                                        break;
 192                                }
 193                        }
 194                        if (i == ARRAY_SIZE(pc263_boards))
 195                                continue;
 196                } else {
 197                        /* Match specific model name. */
 198                        if (pci_dev->device != thisboard->devid)
 199                                continue;
 200                }
 201
 202                /* Found a match. */
 203                *pci_dev_p = pci_dev;
 204                return 0;
 205        }
 206        /* No match found. */
 207        if (bus || slot) {
 208                printk(KERN_ERR
 209                       "comedi%d: error! no %s found at pci %02x:%02x!\n",
 210                       dev->minor, thisboard->name, bus, slot);
 211        } else {
 212                printk(KERN_ERR "comedi%d: error! no %s found!\n",
 213                       dev->minor, thisboard->name);
 214        }
 215        return -EIO;
 216}
 217#endif
 218
 219/*
 220 * Attach is called by the Comedi core to configure the driver
 221 * for a particular board.  If you specified a board_name array
 222 * in the driver structure, dev->board_ptr contains that
 223 * address.
 224 */
 225static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 226{
 227        struct comedi_subdevice *s;
 228        unsigned long iobase = 0;
 229#ifdef CONFIG_COMEDI_PCI
 230        struct pci_dev *pci_dev = NULL;
 231        int bus = 0, slot = 0;
 232#endif
 233        int ret;
 234
 235        printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
 236               PC263_DRIVER_NAME);
 237/*
 238 * Allocate the private structure area.  alloc_private() is a
 239 * convenient macro defined in comedidev.h.
 240 */
 241#ifdef CONFIG_COMEDI_PCI
 242        ret = alloc_private(dev, sizeof(struct pc263_private));
 243        if (ret < 0) {
 244                printk(KERN_ERR "comedi%d: error! out of memory!\n",
 245                       dev->minor);
 246                return ret;
 247        }
 248#endif
 249        /* Process options. */
 250        switch (thisboard->bustype) {
 251        case isa_bustype:
 252                iobase = it->options[0];
 253                break;
 254#ifdef CONFIG_COMEDI_PCI
 255        case pci_bustype:
 256                bus = it->options[0];
 257                slot = it->options[1];
 258
 259                ret = pc263_find_pci(dev, bus, slot, &pci_dev);
 260                if (ret < 0)
 261                        return ret;
 262                devpriv->pci_dev = pci_dev;
 263                break;
 264#endif /* CONFIG_COMEDI_PCI */
 265        default:
 266                printk(KERN_ERR
 267                       "comedi%d: %s: BUG! cannot determine board type!\n",
 268                       dev->minor, PC263_DRIVER_NAME);
 269                return -EINVAL;
 270                break;
 271        }
 272
 273/*
 274 * Initialize dev->board_name.
 275 */
 276        dev->board_name = thisboard->name;
 277
 278        /* Enable device and reserve I/O spaces. */
 279#ifdef CONFIG_COMEDI_PCI
 280        if (pci_dev) {
 281                ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME);
 282                if (ret < 0) {
 283                        printk(KERN_ERR
 284                               "comedi%d: error! cannot enable PCI device and request regions!\n",
 285                               dev->minor);
 286                        return ret;
 287                }
 288                iobase = pci_resource_start(pci_dev, 2);
 289        } else
 290#endif
 291        {
 292                ret = pc263_request_region(dev->minor, iobase, PC263_IO_SIZE);
 293                if (ret < 0) {
 294                        return ret;
 295                }
 296        }
 297        dev->iobase = iobase;
 298
 299/*
 300 * Allocate the subdevice structures.  alloc_subdevice() is a
 301 * convenient macro defined in comedidev.h.
 302 */
 303        ret = alloc_subdevices(dev, 1);
 304        if (ret < 0) {
 305                printk(KERN_ERR "comedi%d: error! out of memory!\n",
 306                       dev->minor);
 307                return ret;
 308        }
 309
 310        s = dev->subdevices + 0;
 311        /* digital i/o subdevice */
 312        s->type = COMEDI_SUBD_DIO;
 313        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 314        s->n_chan = 16;
 315        s->maxdata = 1;
 316        s->range_table = &range_digital;
 317        s->insn_bits = pc263_dio_insn_bits;
 318        s->insn_config = pc263_dio_insn_config;
 319        /* all outputs */
 320        s->io_bits = 0xffff;
 321        /* read initial relay state */
 322        s->state = inb(dev->iobase);
 323        s->state = s->state | (inb(dev->iobase) << 8);
 324
 325        printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
 326        if (thisboard->bustype == isa_bustype) {
 327                printk("(base %#lx) ", iobase);
 328        } else {
 329#ifdef CONFIG_COMEDI_PCI
 330                printk("(pci %s) ", pci_name(pci_dev));
 331#endif
 332        }
 333
 334        printk("attached\n");
 335
 336        return 1;
 337}
 338
 339/*
 340 * _detach is called to deconfigure a device.  It should deallocate
 341 * resources.
 342 * This function is also called when _attach() fails, so it should be
 343 * careful not to release resources that were not necessarily
 344 * allocated by _attach().  dev->private and dev->subdevices are
 345 * deallocated automatically by the core.
 346 */
 347static int pc263_detach(struct comedi_device *dev)
 348{
 349        printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
 350               PC263_DRIVER_NAME);
 351
 352#ifdef CONFIG_COMEDI_PCI
 353        if (devpriv)
 354#endif
 355        {
 356#ifdef CONFIG_COMEDI_PCI
 357                if (devpriv->pci_dev) {
 358                        if (dev->iobase) {
 359                                comedi_pci_disable(devpriv->pci_dev);
 360                        }
 361                        pci_dev_put(devpriv->pci_dev);
 362                } else
 363#endif
 364                {
 365                        if (dev->iobase) {
 366                                release_region(dev->iobase, PC263_IO_SIZE);
 367                        }
 368                }
 369        }
 370        if (dev->board_name) {
 371                printk(KERN_INFO "comedi%d: %s removed\n",
 372                       dev->minor, dev->board_name);
 373        }
 374        return 0;
 375}
 376
 377/*
 378 * This function checks and requests an I/O region, reporting an error
 379 * if there is a conflict.
 380 */
 381static int pc263_request_region(unsigned minor, unsigned long from,
 382                                unsigned long extent)
 383{
 384        if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) {
 385                printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
 386                       minor, from, extent);
 387                return -EIO;
 388        }
 389        return 0;
 390}
 391
 392/* DIO devices are slightly special.  Although it is possible to
 393 * implement the insn_read/insn_write interface, it is much more
 394 * useful to applications if you implement the insn_bits interface.
 395 * This allows packed reading/writing of the DIO channels.  The
 396 * comedi core can convert between insn_bits and insn_read/write */
 397static int pc263_dio_insn_bits(struct comedi_device *dev,
 398                               struct comedi_subdevice *s,
 399                               struct comedi_insn *insn, unsigned int *data)
 400{
 401        if (insn->n != 2)
 402                return -EINVAL;
 403
 404        /* The insn data is a mask in data[0] and the new data
 405         * in data[1], each channel cooresponding to a bit. */
 406        if (data[0]) {
 407                s->state &= ~data[0];
 408                s->state |= data[0] & data[1];
 409                /* Write out the new digital output lines */
 410                outb(s->state & 0xFF, dev->iobase);
 411                outb(s->state >> 8, dev->iobase + 1);
 412        }
 413
 414        /* on return, data[1] contains the value of the digital
 415         * input and output lines. */
 416        /* or we could just return the software copy of the output values if
 417         * it was a purely digital output subdevice */
 418        data[1] = s->state;
 419
 420        return 2;
 421}
 422
 423static int pc263_dio_insn_config(struct comedi_device *dev,
 424                                 struct comedi_subdevice *s,
 425                                 struct comedi_insn *insn, unsigned int *data)
 426{
 427        if (insn->n != 1)
 428                return -EINVAL;
 429        return 1;
 430}
 431
 432/*
 433 * A convenient macro that defines init_module() and cleanup_module(),
 434 * as necessary.
 435 */
 436#ifdef CONFIG_COMEDI_PCI
 437COMEDI_PCI_INITCLEANUP(driver_amplc_pc263, pc263_pci_table);
 438#else
 439COMEDI_INITCLEANUP(driver_amplc_pc263);
 440#endif
 441