linux/drivers/staging/comedi/drivers/ssv_dnp.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/ssv_dnp.c
   3    generic comedi driver for SSV Embedded Systems' DIL/Net-PCs
   4    Copyright (C) 2001 Robert Schwebel <robert@schwebel.de>
   5
   6    COMEDI - Linux Control and Measurement Device Interface
   7    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   8
   9    This program is free software; you can redistribute it and/or modify
  10    it under the terms of the GNU General Public License as published by
  11    the Free Software Foundation; either version 2 of the License, or
  12    (at your option) any later version.
  13
  14    This program is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18
  19    You should have received a copy of the GNU General Public License
  20    along with this program; if not, write to the Free Software
  21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22
  23*/
  24/*
  25Driver: ssv_dnp
  26Description: SSV Embedded Systems DIL/Net-PC
  27Author: Robert Schwebel <robert@schwebel.de>
  28Devices: [SSV Embedded Systems] DIL/Net-PC 1486 (dnp-1486)
  29Status: unknown
  30*/
  31
  32/* include files ----------------------------------------------------------- */
  33
  34#include "../comedidev.h"
  35
  36/* Some global definitions: the registers of the DNP ----------------------- */
  37/*                                                                           */
  38/* For port A and B the mode register has bits corresponding to the output   */
  39/* pins, where Bit-N = 0 -> input, Bit-N = 1 -> output. Note that bits       */
  40/* 4 to 7 correspond to pin 0..3 for port C data register. Ensure that bits  */
  41/* 0..3 remain unchanged! For details about Port C Mode Register see         */
  42/* the remarks in dnp_insn_config() below.                                   */
  43
  44#define CSCIR 0x22              /* Chip Setup and Control Index Register       */
  45#define CSCDR 0x23              /* Chip Setup and Control Data Register        */
  46#define PAMR  0xa5              /* Port A Mode Register                        */
  47#define PADR  0xa9              /* Port A Data Register                        */
  48#define PBMR  0xa4              /* Port B Mode Register                        */
  49#define PBDR  0xa8              /* Port B Data Register                        */
  50#define PCMR  0xa3              /* Port C Mode Register                        */
  51#define PCDR  0xa7              /* Port C Data Register                        */
  52
  53/* This data structure holds information about the supported boards -------- */
  54
  55struct dnp_board {
  56        const char *name;
  57        int ai_chans;
  58        int ai_bits;
  59        int have_dio;
  60};
  61
  62static const struct dnp_board dnp_boards[] = {  /* we only support one DNP 'board'   */
  63        {                       /* variant at the moment             */
  64         .name = "dnp-1486",
  65         .ai_chans = 16,
  66         .ai_bits = 12,
  67         .have_dio = 1,
  68         },
  69};
  70
  71/* Useful for shorthand access to the particular board structure ----------- */
  72#define thisboard ((const struct dnp_board *)dev->board_ptr)
  73
  74/* This structure is for data unique to the DNP driver --------------------- */
  75struct dnp_private_data {
  76
  77};
  78
  79/* Shorthand macro for faster access to the private data ------------------- */
  80#define devpriv ((dnp_private *)dev->private)
  81
  82/* ------------------------------------------------------------------------- */
  83/* The struct comedi_driver structure tells the Comedi core module which functions  */
  84/* to call to configure/deconfigure (attach/detach) the board, and also      */
  85/* about the kernel module that contains the device code.                    */
  86/*                                                                           */
  87/* In the following section we define the API of this driver.                */
  88/* ------------------------------------------------------------------------- */
  89
  90static int dnp_attach(struct comedi_device *dev, struct comedi_devconfig *it);
  91static int dnp_detach(struct comedi_device *dev);
  92
  93static struct comedi_driver driver_dnp = {
  94        .driver_name = "ssv_dnp",
  95        .module = THIS_MODULE,
  96        .attach = dnp_attach,
  97        .detach = dnp_detach,
  98        .board_name = &dnp_boards[0].name,
  99        /* only necessary for non-PnP devs   */
 100        .offset = sizeof(struct dnp_board),     /* like ISA-PnP, PCI or PCMCIA.      */
 101        .num_names = ARRAY_SIZE(dnp_boards),
 102};
 103
 104COMEDI_INITCLEANUP(driver_dnp);
 105
 106static int dnp_dio_insn_bits(struct comedi_device *dev,
 107                             struct comedi_subdevice *s,
 108                             struct comedi_insn *insn, unsigned int *data);
 109
 110static int dnp_dio_insn_config(struct comedi_device *dev,
 111                               struct comedi_subdevice *s,
 112                               struct comedi_insn *insn, unsigned int *data);
 113
 114/* ------------------------------------------------------------------------- */
 115/* Attach is called by comedi core to configure the driver for a particular  */
 116/* board. If you specified a board_name array in the driver structure,       */
 117/* dev->board_ptr contains that address.                                     */
 118/* ------------------------------------------------------------------------- */
 119
 120static int dnp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 121{
 122
 123        struct comedi_subdevice *s;
 124
 125        printk("comedi%d: dnp: ", dev->minor);
 126
 127        /* Autoprobing: this should find out which board we have. Currently only   */
 128        /* the 1486 board is supported and autoprobing is not implemented :-)      */
 129        /* dev->board_ptr = dnp_probe(dev); */
 130
 131        /* Initialize the name of the board. We can use the "thisboard" macro now. */
 132        dev->board_name = thisboard->name;
 133
 134        /* Allocate the private structure area. alloc_private() is a convenient    */
 135        /* macro defined in comedidev.h.                                           */
 136        if (alloc_private(dev, sizeof(struct dnp_private_data)) < 0)
 137                return -ENOMEM;
 138
 139        /* Allocate the subdevice structures. alloc_subdevice() is a convenient    */
 140        /* macro defined in comedidev.h.                                           */
 141
 142        if (alloc_subdevices(dev, 1) < 0)
 143                return -ENOMEM;
 144
 145        s = dev->subdevices + 0;
 146        /* digital i/o subdevice                                                   */
 147        s->type = COMEDI_SUBD_DIO;
 148        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 149        s->n_chan = 20;
 150        s->maxdata = 1;
 151        s->range_table = &range_digital;
 152        s->insn_bits = dnp_dio_insn_bits;
 153        s->insn_config = dnp_dio_insn_config;
 154
 155        printk("attached\n");
 156
 157        /* We use the I/O ports 0x22,0x23 and 0xa3-0xa9, which are always
 158         * allocated for the primary 8259, so we don't need to allocate them
 159         * ourselves. */
 160
 161        /* configure all ports as input (default)                                  */
 162        outb(PAMR, CSCIR);
 163        outb(0x00, CSCDR);
 164        outb(PBMR, CSCIR);
 165        outb(0x00, CSCDR);
 166        outb(PCMR, CSCIR);
 167        outb((inb(CSCDR) & 0xAA), CSCDR);
 168
 169        return 1;
 170
 171}
 172
 173/* ------------------------------------------------------------------------- */
 174/* detach is called to deconfigure a device. It should deallocate the        */
 175/* resources. This function is also called when _attach() fails, so it       */
 176/* should be careful not to release resources that were not necessarily      */
 177/* allocated by _attach(). dev->private and dev->subdevices are              */
 178/* deallocated automatically by the core.                                    */
 179/* ------------------------------------------------------------------------- */
 180
 181static int dnp_detach(struct comedi_device *dev)
 182{
 183
 184        /* configure all ports as input (default)                                  */
 185        outb(PAMR, CSCIR);
 186        outb(0x00, CSCDR);
 187        outb(PBMR, CSCIR);
 188        outb(0x00, CSCDR);
 189        outb(PCMR, CSCIR);
 190        outb((inb(CSCDR) & 0xAA), CSCDR);
 191
 192        /* announce that we are finished                                           */
 193        printk("comedi%d: dnp: remove\n", dev->minor);
 194
 195        return 0;
 196
 197}
 198
 199/* ------------------------------------------------------------------------- */
 200/* The insn_bits interface allows packed reading/writing of DIO channels.    */
 201/* The comedi core can convert between insn_bits and insn_read/write, so you */
 202/* are able to use these instructions as well.                               */
 203/* ------------------------------------------------------------------------- */
 204
 205static int dnp_dio_insn_bits(struct comedi_device *dev,
 206                             struct comedi_subdevice *s,
 207                             struct comedi_insn *insn, unsigned int *data)
 208{
 209
 210        if (insn->n != 2)
 211                return -EINVAL; /* insn uses data[0] and data[1]     */
 212
 213        /* The insn data is a mask in data[0] and the new data in data[1], each    */
 214        /* channel cooresponding to a bit.                                         */
 215
 216        /* Ports A and B are straight forward: each bit corresponds to an output   */
 217        /* pin with the same order. Port C is different: bits 0...3 correspond to  */
 218        /* bits 4...7 of the output register (PCDR).                               */
 219
 220        if (data[0]) {
 221
 222                outb(PADR, CSCIR);
 223                outb((inb(CSCDR)
 224                      & ~(u8) (data[0] & 0x0000FF))
 225                     | (u8) (data[1] & 0x0000FF), CSCDR);
 226
 227                outb(PBDR, CSCIR);
 228                outb((inb(CSCDR)
 229                      & ~(u8) ((data[0] & 0x00FF00) >> 8))
 230                     | (u8) ((data[1] & 0x00FF00) >> 8), CSCDR);
 231
 232                outb(PCDR, CSCIR);
 233                outb((inb(CSCDR)
 234                      & ~(u8) ((data[0] & 0x0F0000) >> 12))
 235                     | (u8) ((data[1] & 0x0F0000) >> 12), CSCDR);
 236        }
 237
 238        /* on return, data[1] contains the value of the digital input lines.       */
 239        outb(PADR, CSCIR);
 240        data[0] = inb(CSCDR);
 241        outb(PBDR, CSCIR);
 242        data[0] += inb(CSCDR) << 8;
 243        outb(PCDR, CSCIR);
 244        data[0] += ((inb(CSCDR) & 0xF0) << 12);
 245
 246        return 2;
 247
 248}
 249
 250/* ------------------------------------------------------------------------- */
 251/* Configure the direction of the bidirectional digital i/o pins. chanspec   */
 252/* contains the channel to be changed and data[0] contains either            */
 253/* COMEDI_INPUT or COMEDI_OUTPUT.                                            */
 254/* ------------------------------------------------------------------------- */
 255
 256static int dnp_dio_insn_config(struct comedi_device *dev,
 257                               struct comedi_subdevice *s,
 258                               struct comedi_insn *insn, unsigned int *data)
 259{
 260
 261        u8 register_buffer;
 262
 263        int chan = CR_CHAN(insn->chanspec);     /* reduces chanspec to lower 16 bits */
 264
 265        switch (data[0]) {
 266        case INSN_CONFIG_DIO_OUTPUT:
 267        case INSN_CONFIG_DIO_INPUT:
 268                break;
 269        case INSN_CONFIG_DIO_QUERY:
 270                data[1] =
 271                    (inb(CSCDR) & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
 272                return insn->n;
 273                break;
 274        default:
 275                return -EINVAL;
 276                break;
 277        }
 278        /* Test: which port does the channel belong to?                            */
 279
 280        /* We have to pay attention with port C: this is the meaning of PCMR:      */
 281        /* Bit in PCMR:              7 6 5 4 3 2 1 0                               */
 282        /* Corresponding port C pin: d 3 d 2 d 1 d 0   d= don't touch              */
 283
 284        if ((chan >= 0) && (chan <= 7)) {
 285                /* this is port A */
 286                outb(PAMR, CSCIR);
 287        } else if ((chan >= 8) && (chan <= 15)) {
 288                /* this is port B */
 289                chan -= 8;
 290                outb(PBMR, CSCIR);
 291        } else if ((chan >= 16) && (chan <= 19)) {
 292                /* this is port C; multiplication with 2 brings bits into correct        */
 293                /* position for PCMR!                                                    */
 294                chan -= 16;
 295                chan *= 2;
 296                outb(PCMR, CSCIR);
 297        } else {
 298                return -EINVAL;
 299        }
 300
 301        /* read 'old' direction of the port and set bits (out=1, in=0)             */
 302        register_buffer = inb(CSCDR);
 303        if (data[0] == COMEDI_OUTPUT) {
 304                register_buffer |= (1 << chan);
 305        } else {
 306                register_buffer &= ~(1 << chan);
 307        }
 308        outb(register_buffer, CSCDR);
 309
 310        return 1;
 311
 312}
 313