linux/drivers/staging/comedi/drivers/8255.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/8255.c
   3    Driver for 8255
   4
   5    COMEDI - Linux Control and Measurement Device Interface
   6    Copyright (C) 1998 David A. Schleef <ds@schleef.org>
   7
   8    This program is free software; you can redistribute it and/or modify
   9    it under the terms of the GNU General Public License as published by
  10    the Free Software Foundation; either version 2 of the License, or
  11    (at your option) any later version.
  12
  13    This program is distributed in the hope that it will be useful,
  14    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16    GNU General Public License for more details.
  17
  18    You should have received a copy of the GNU General Public License
  19    along with this program; if not, write to the Free Software
  20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21
  22*/
  23/*
  24Driver: 8255
  25Description: generic 8255 support
  26Devices: [standard] 8255 (8255)
  27Author: ds
  28Status: works
  29Updated: Fri,  7 Jun 2002 12:56:45 -0700
  30
  31The classic in digital I/O.  The 8255 appears in Comedi as a single
  32digital I/O subdevice with 24 channels.  The channel 0 corresponds
  33to the 8255's port A, bit 0; channel 23 corresponds to port C, bit
  347.  Direction configuration is done in blocks, with channels 0-7,
  358-15, 16-19, and 20-23 making up the 4 blocks.  The only 8255 mode
  36supported is mode 0.
  37
  38You should enable compilation this driver if you plan to use a board
  39that has an 8255 chip.  For multifunction boards, the main driver will
  40configure the 8255 subdevice automatically.
  41
  42This driver also works independently with ISA and PCI cards that
  43directly map the 8255 registers to I/O ports, including cards with
  44multiple 8255 chips.  To configure the driver for such a card, the
  45option list should be a list of the I/O port bases for each of the
  468255 chips.  For example,
  47
  48  comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c
  49
  50Note that most PCI 8255 boards do NOT work with this driver, and
  51need a separate driver as a wrapper.  For those that do work, the
  52I/O port base address can be found in the output of 'lspci -v'.
  53
  54*/
  55
  56/*
  57   This file contains an exported subdevice for driving an 8255.
  58
  59   To use this subdevice as part of another driver, you need to
  60   set up the subdevice in the attach function of the driver by
  61   calling:
  62
  63     subdev_8255_init(device, subdevice, callback_function, arg)
  64
  65   device and subdevice are pointers to the device and subdevice
  66   structures.  callback_function will be called to provide the
  67   low-level input/output to the device, i.e., actual register
  68   access.  callback_function will be called with the value of arg
  69   as the last parameter.  If the 8255 device is mapped as 4
  70   consecutive I/O ports, you can use NULL for callback_function
  71   and the I/O port base for arg, and an internal function will
  72   handle the register access.
  73
  74   In addition, if the main driver handles interrupts, you can
  75   enable commands on the subdevice by calling subdev_8255_init_irq()
  76   instead.  Then, when you get an interrupt that is likely to be
  77   from the 8255, you should call subdev_8255_interrupt(), which
  78   will copy the latched value to a Comedi buffer.
  79 */
  80
  81#include "../comedidev.h"
  82
  83#include <linux/ioport.h>
  84
  85#define _8255_SIZE 4
  86
  87#define _8255_DATA 0
  88#define _8255_CR 3
  89
  90#define CR_C_LO_IO      0x01
  91#define CR_B_IO         0x02
  92#define CR_B_MODE       0x04
  93#define CR_C_HI_IO      0x08
  94#define CR_A_IO         0x10
  95#define CR_A_MODE(a)    ((a)<<5)
  96#define CR_CW           0x80
  97
  98struct subdev_8255_struct {
  99        unsigned long cb_arg;
 100        int (*cb_func) (int, int, int, unsigned long);
 101        int have_irq;
 102};
 103
 104#define CALLBACK_ARG    (((struct subdev_8255_struct *)s->private)->cb_arg)
 105#define CALLBACK_FUNC   (((struct subdev_8255_struct *)s->private)->cb_func)
 106#define subdevpriv      ((struct subdev_8255_struct *)s->private)
 107
 108static int dev_8255_attach(struct comedi_device *dev,
 109                           struct comedi_devconfig *it);
 110static int dev_8255_detach(struct comedi_device *dev);
 111static struct comedi_driver driver_8255 = {
 112        .driver_name = "8255",
 113        .module = THIS_MODULE,
 114        .attach = dev_8255_attach,
 115        .detach = dev_8255_detach,
 116};
 117
 118COMEDI_INITCLEANUP(driver_8255);
 119
 120static void do_config(struct comedi_device *dev, struct comedi_subdevice *s);
 121
 122void subdev_8255_interrupt(struct comedi_device *dev,
 123                           struct comedi_subdevice *s)
 124{
 125        short d;
 126
 127        d = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
 128        d |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
 129
 130        comedi_buf_put(s->async, d);
 131        s->async->events |= COMEDI_CB_EOS;
 132
 133        comedi_event(dev, s);
 134}
 135
 136static int subdev_8255_cb(int dir, int port, int data, unsigned long arg)
 137{
 138        unsigned long iobase = arg;
 139
 140        if (dir) {
 141                outb(data, iobase + port);
 142                return 0;
 143        } else {
 144                return inb(iobase + port);
 145        }
 146}
 147
 148static int subdev_8255_insn(struct comedi_device *dev,
 149                            struct comedi_subdevice *s,
 150                            struct comedi_insn *insn, unsigned int *data)
 151{
 152        if (data[0]) {
 153                s->state &= ~data[0];
 154                s->state |= (data[0] & data[1]);
 155
 156                if (data[0] & 0xff)
 157                        CALLBACK_FUNC(1, _8255_DATA, s->state & 0xff,
 158                                      CALLBACK_ARG);
 159                if (data[0] & 0xff00)
 160                        CALLBACK_FUNC(1, _8255_DATA + 1, (s->state >> 8) & 0xff,
 161                                      CALLBACK_ARG);
 162                if (data[0] & 0xff0000)
 163                        CALLBACK_FUNC(1, _8255_DATA + 2,
 164                                      (s->state >> 16) & 0xff, CALLBACK_ARG);
 165        }
 166
 167        data[1] = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
 168        data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
 169        data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 2, 0, CALLBACK_ARG) << 16);
 170
 171        return 2;
 172}
 173
 174static int subdev_8255_insn_config(struct comedi_device *dev,
 175                                   struct comedi_subdevice *s,
 176                                   struct comedi_insn *insn, unsigned int *data)
 177{
 178        unsigned int mask;
 179        unsigned int bits;
 180
 181        mask = 1 << CR_CHAN(insn->chanspec);
 182        if (mask & 0x0000ff) {
 183                bits = 0x0000ff;
 184        } else if (mask & 0x00ff00) {
 185                bits = 0x00ff00;
 186        } else if (mask & 0x0f0000) {
 187                bits = 0x0f0000;
 188        } else {
 189                bits = 0xf00000;
 190        }
 191
 192        switch (data[0]) {
 193        case INSN_CONFIG_DIO_INPUT:
 194                s->io_bits &= ~bits;
 195                break;
 196        case INSN_CONFIG_DIO_OUTPUT:
 197                s->io_bits |= bits;
 198                break;
 199        case INSN_CONFIG_DIO_QUERY:
 200                data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
 201                return insn->n;
 202                break;
 203        default:
 204                return -EINVAL;
 205        }
 206
 207        do_config(dev, s);
 208
 209        return 1;
 210}
 211
 212static void do_config(struct comedi_device *dev, struct comedi_subdevice *s)
 213{
 214        int config;
 215
 216        config = CR_CW;
 217        /* 1 in io_bits indicates output, 1 in config indicates input */
 218        if (!(s->io_bits & 0x0000ff))
 219                config |= CR_A_IO;
 220        if (!(s->io_bits & 0x00ff00))
 221                config |= CR_B_IO;
 222        if (!(s->io_bits & 0x0f0000))
 223                config |= CR_C_LO_IO;
 224        if (!(s->io_bits & 0xf00000))
 225                config |= CR_C_HI_IO;
 226        CALLBACK_FUNC(1, _8255_CR, config, CALLBACK_ARG);
 227}
 228
 229static int subdev_8255_cmdtest(struct comedi_device *dev,
 230                               struct comedi_subdevice *s,
 231                               struct comedi_cmd *cmd)
 232{
 233        int err = 0;
 234        unsigned int tmp;
 235
 236        /* step 1 */
 237
 238        tmp = cmd->start_src;
 239        cmd->start_src &= TRIG_NOW;
 240        if (!cmd->start_src || tmp != cmd->start_src)
 241                err++;
 242
 243        tmp = cmd->scan_begin_src;
 244        cmd->scan_begin_src &= TRIG_EXT;
 245        if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
 246                err++;
 247
 248        tmp = cmd->convert_src;
 249        cmd->convert_src &= TRIG_FOLLOW;
 250        if (!cmd->convert_src || tmp != cmd->convert_src)
 251                err++;
 252
 253        tmp = cmd->scan_end_src;
 254        cmd->scan_end_src &= TRIG_COUNT;
 255        if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
 256                err++;
 257
 258        tmp = cmd->stop_src;
 259        cmd->stop_src &= TRIG_NONE;
 260        if (!cmd->stop_src || tmp != cmd->stop_src)
 261                err++;
 262
 263        if (err)
 264                return 1;
 265
 266        /* step 2 */
 267
 268        if (err)
 269                return 2;
 270
 271        /* step 3 */
 272
 273        if (cmd->start_arg != 0) {
 274                cmd->start_arg = 0;
 275                err++;
 276        }
 277        if (cmd->scan_begin_arg != 0) {
 278                cmd->scan_begin_arg = 0;
 279                err++;
 280        }
 281        if (cmd->convert_arg != 0) {
 282                cmd->convert_arg = 0;
 283                err++;
 284        }
 285        if (cmd->scan_end_arg != 1) {
 286                cmd->scan_end_arg = 1;
 287                err++;
 288        }
 289        if (cmd->stop_arg != 0) {
 290                cmd->stop_arg = 0;
 291                err++;
 292        }
 293
 294        if (err)
 295                return 3;
 296
 297        /* step 4 */
 298
 299        if (err)
 300                return 4;
 301
 302        return 0;
 303}
 304
 305static int subdev_8255_cmd(struct comedi_device *dev,
 306                           struct comedi_subdevice *s)
 307{
 308        /* FIXME */
 309
 310        return 0;
 311}
 312
 313static int subdev_8255_cancel(struct comedi_device *dev,
 314                              struct comedi_subdevice *s)
 315{
 316        /* FIXME */
 317
 318        return 0;
 319}
 320
 321int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
 322                     int (*cb) (int, int, int, unsigned long),
 323                     unsigned long arg)
 324{
 325        s->type = COMEDI_SUBD_DIO;
 326        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 327        s->n_chan = 24;
 328        s->range_table = &range_digital;
 329        s->maxdata = 1;
 330
 331        s->private = kmalloc(sizeof(struct subdev_8255_struct), GFP_KERNEL);
 332        if (!s->private)
 333                return -ENOMEM;
 334
 335        CALLBACK_ARG = arg;
 336        if (cb == NULL) {
 337                CALLBACK_FUNC = subdev_8255_cb;
 338        } else {
 339                CALLBACK_FUNC = cb;
 340        }
 341        s->insn_bits = subdev_8255_insn;
 342        s->insn_config = subdev_8255_insn_config;
 343
 344        s->state = 0;
 345        s->io_bits = 0;
 346        do_config(dev, s);
 347
 348        return 0;
 349}
 350
 351int subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice *s,
 352                         int (*cb) (int, int, int, unsigned long),
 353                         unsigned long arg)
 354{
 355        int ret;
 356
 357        ret = subdev_8255_init(dev, s, cb, arg);
 358        if (ret < 0)
 359                return ret;
 360
 361        s->do_cmdtest = subdev_8255_cmdtest;
 362        s->do_cmd = subdev_8255_cmd;
 363        s->cancel = subdev_8255_cancel;
 364
 365        subdevpriv->have_irq = 1;
 366
 367        return 0;
 368}
 369
 370void subdev_8255_cleanup(struct comedi_device *dev, struct comedi_subdevice *s)
 371{
 372        if (s->private) {
 373                /* this test does nothing, so comment it out
 374                 * if (subdevpriv->have_irq) {
 375                 * }
 376                 */
 377
 378                kfree(s->private);
 379        }
 380}
 381
 382/*
 383
 384   Start of the 8255 standalone device
 385
 386 */
 387
 388static int dev_8255_attach(struct comedi_device *dev,
 389                           struct comedi_devconfig *it)
 390{
 391        int ret;
 392        unsigned long iobase;
 393        int i;
 394
 395        printk("comedi%d: 8255:", dev->minor);
 396
 397        dev->board_name = "8255";
 398
 399        for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
 400                iobase = it->options[i];
 401                if (!iobase)
 402                        break;
 403        }
 404        if (i == 0) {
 405                printk(" no devices specified\n");
 406                return -EINVAL;
 407        }
 408
 409        ret = alloc_subdevices(dev, i);
 410        if (ret < 0)
 411                return ret;
 412
 413        for (i = 0; i < dev->n_subdevices; i++) {
 414                iobase = it->options[i];
 415
 416                printk(" 0x%04lx", iobase);
 417                if (!request_region(iobase, _8255_SIZE, "8255")) {
 418                        printk(" (I/O port conflict)");
 419
 420                        dev->subdevices[i].type = COMEDI_SUBD_UNUSED;
 421                } else {
 422                        subdev_8255_init(dev, dev->subdevices + i, NULL,
 423                                         iobase);
 424                }
 425        }
 426
 427        printk("\n");
 428
 429        return 0;
 430}
 431
 432static int dev_8255_detach(struct comedi_device *dev)
 433{
 434        int i;
 435        unsigned long iobase;
 436        struct comedi_subdevice *s;
 437
 438        printk("comedi%d: 8255: remove\n", dev->minor);
 439
 440        for (i = 0; i < dev->n_subdevices; i++) {
 441                s = dev->subdevices + i;
 442                if (s->type != COMEDI_SUBD_UNUSED) {
 443                        iobase = CALLBACK_ARG;
 444                        release_region(iobase, _8255_SIZE);
 445                }
 446                subdev_8255_cleanup(dev, s);
 447        }
 448
 449        return 0;
 450}
 451
 452EXPORT_SYMBOL(subdev_8255_init);
 453EXPORT_SYMBOL(subdev_8255_init_irq);
 454EXPORT_SYMBOL(subdev_8255_cleanup);
 455EXPORT_SYMBOL(subdev_8255_interrupt);
 456