1/* 2 * pcmad.c 3 * Hardware driver for Winsystems PCM-A/D12 and PCM-A/D16 4 * 5 * COMEDI - Linux Control and Measurement Device Interface 6 * Copyright (C) 2000,2001 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 19/* 20 * Driver: pcmad 21 * Description: Winsystems PCM-A/D12, PCM-A/D16 22 * Devices: (Winsystems) PCM-A/D12 [pcmad12] 23 * (Winsystems) PCM-A/D16 [pcmad16] 24 * Author: ds 25 * Status: untested 26 * 27 * This driver was written on a bet that I couldn't write a driver 28 * in less than 2 hours. I won the bet, but never got paid. =( 29 * 30 * Configuration options: 31 * [0] - I/O port base 32 * [1] - IRQ (unused) 33 * [2] - Analog input reference (must match jumpers) 34 * 0 = single-ended (16 channels) 35 * 1 = differential (8 channels) 36 * [3] - Analog input encoding (must match jumpers) 37 * 0 = straight binary (0-5V input range) 38 * 1 = two's complement (+-10V input range) 39 */ 40 41#include <linux/module.h> 42#include "../comedidev.h" 43 44#define PCMAD_STATUS 0 45#define PCMAD_LSB 1 46#define PCMAD_MSB 2 47#define PCMAD_CONVERT 1 48 49struct pcmad_board_struct { 50 const char *name; 51 unsigned int ai_maxdata; 52}; 53 54static const struct pcmad_board_struct pcmad_boards[] = { 55 { 56 .name = "pcmad12", 57 .ai_maxdata = 0x0fff, 58 }, { 59 .name = "pcmad16", 60 .ai_maxdata = 0xffff, 61 }, 62}; 63 64static int pcmad_ai_eoc(struct comedi_device *dev, 65 struct comedi_subdevice *s, 66 struct comedi_insn *insn, 67 unsigned long context) 68{ 69 unsigned int status; 70 71 status = inb(dev->iobase + PCMAD_STATUS); 72 if ((status & 0x3) == 0x3) 73 return 0; 74 return -EBUSY; 75} 76 77static int pcmad_ai_insn_read(struct comedi_device *dev, 78 struct comedi_subdevice *s, 79 struct comedi_insn *insn, 80 unsigned int *data) 81{ 82 unsigned int chan = CR_CHAN(insn->chanspec); 83 unsigned int range = CR_RANGE(insn->chanspec); 84 unsigned int val; 85 int ret; 86 int i; 87 88 for (i = 0; i < insn->n; i++) { 89 outb(chan, dev->iobase + PCMAD_CONVERT); 90 91 ret = comedi_timeout(dev, s, insn, pcmad_ai_eoc, 0); 92 if (ret) 93 return ret; 94 95 val = inb(dev->iobase + PCMAD_LSB) | 96 (inb(dev->iobase + PCMAD_MSB) << 8); 97 98 /* data is shifted on the pcmad12, fix it */ 99 if (s->maxdata == 0x0fff) 100 val >>= 4; 101 102 if (comedi_range_is_bipolar(s, range)) { 103 /* munge the two's complement value */ 104 val ^= ((s->maxdata + 1) >> 1); 105 } 106 107 data[i] = val; 108 } 109 110 return insn->n; 111} 112 113static int pcmad_attach(struct comedi_device *dev, struct comedi_devconfig *it) 114{ 115 const struct pcmad_board_struct *board = dev->board_ptr; 116 struct comedi_subdevice *s; 117 int ret; 118 119 ret = comedi_request_region(dev, it->options[0], 0x04); 120 if (ret) 121 return ret; 122 123 ret = comedi_alloc_subdevices(dev, 1); 124 if (ret) 125 return ret; 126 127 s = &dev->subdevices[0]; 128 s->type = COMEDI_SUBD_AI; 129 if (it->options[1]) { 130 /* 8 differential channels */ 131 s->subdev_flags = SDF_READABLE | AREF_DIFF; 132 s->n_chan = 8; 133 } else { 134 /* 16 single-ended channels */ 135 s->subdev_flags = SDF_READABLE | AREF_GROUND; 136 s->n_chan = 16; 137 } 138 s->len_chanlist = 1; 139 s->maxdata = board->ai_maxdata; 140 s->range_table = it->options[2] ? &range_bipolar10 : &range_unipolar5; 141 s->insn_read = pcmad_ai_insn_read; 142 143 return 0; 144} 145 146static struct comedi_driver pcmad_driver = { 147 .driver_name = "pcmad", 148 .module = THIS_MODULE, 149 .attach = pcmad_attach, 150 .detach = comedi_legacy_detach, 151 .board_name = &pcmad_boards[0].name, 152 .num_names = ARRAY_SIZE(pcmad_boards), 153 .offset = sizeof(pcmad_boards[0]), 154}; 155module_comedi_driver(pcmad_driver); 156 157MODULE_AUTHOR("Comedi http://www.comedi.org"); 158MODULE_DESCRIPTION("Comedi low-level driver"); 159MODULE_LICENSE("GPL"); 160