Commit | Line | Data |
---|---|---|
58e732ee DS |
1 | /* |
2 | comedi/drivers/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 | 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 | /* | |
24 | Driver: pcmad | |
25 | Description: Winsystems PCM-A/D12, PCM-A/D16 | |
26 | Author: ds | |
27 | Devices: [Winsystems] PCM-A/D12 (pcmad12), PCM-A/D16 (pcmad16) | |
28 | Status: untested | |
29 | ||
30 | This driver was written on a bet that I couldn't write a driver | |
31 | in less than 2 hours. I won the bet, but never got paid. =( | |
32 | ||
33 | Configuration options: | |
34 | [0] - I/O port base | |
35 | [1] - unused | |
36 | [2] - Analog input reference | |
dedf3ad1 BA |
37 | 0 = single ended |
38 | 1 = differential | |
58e732ee | 39 | [3] - Analog input encoding (must match jumpers) |
dedf3ad1 BA |
40 | 0 = straight binary |
41 | 1 = two's complement | |
58e732ee DS |
42 | */ |
43 | ||
25436dc9 | 44 | #include <linux/interrupt.h> |
58e732ee DS |
45 | #include "../comedidev.h" |
46 | ||
47 | #include <linux/ioport.h> | |
48 | ||
49 | #define PCMAD_SIZE 4 | |
50 | ||
51 | #define PCMAD_STATUS 0 | |
52 | #define PCMAD_LSB 1 | |
53 | #define PCMAD_MSB 2 | |
54 | #define PCMAD_CONVERT 1 | |
55 | ||
56 | struct pcmad_board_struct { | |
57 | const char *name; | |
58 | int n_ai_bits; | |
59 | }; | |
60 | static const struct pcmad_board_struct pcmad_boards[] = { | |
61 | { | |
0a85b6f0 MT |
62 | .name = "pcmad12", |
63 | .n_ai_bits = 12, | |
64 | }, | |
58e732ee | 65 | { |
0a85b6f0 MT |
66 | .name = "pcmad16", |
67 | .n_ai_bits = 16, | |
68 | }, | |
58e732ee DS |
69 | }; |
70 | ||
71 | #define this_board ((const struct pcmad_board_struct *)(dev->board_ptr)) | |
b6ac1613 | 72 | #define n_pcmad_boards ARRAY_SIZE(pcmad_boards) |
58e732ee DS |
73 | |
74 | struct pcmad_priv_struct { | |
75 | int differential; | |
76 | int twos_comp; | |
77 | }; | |
78 | #define devpriv ((struct pcmad_priv_struct *)dev->private) | |
79 | ||
da91b269 BP |
80 | static int pcmad_attach(struct comedi_device *dev, struct comedi_devconfig *it); |
81 | static int pcmad_detach(struct comedi_device *dev); | |
139dfbdf | 82 | static struct comedi_driver driver_pcmad = { |
68c3dbff BP |
83 | .driver_name = "pcmad", |
84 | .module = THIS_MODULE, | |
85 | .attach = pcmad_attach, | |
86 | .detach = pcmad_detach, | |
87 | .board_name = &pcmad_boards[0].name, | |
88 | .num_names = n_pcmad_boards, | |
89 | .offset = sizeof(pcmad_boards[0]), | |
58e732ee DS |
90 | }; |
91 | ||
92 | COMEDI_INITCLEANUP(driver_pcmad); | |
93 | ||
94 | #define TIMEOUT 100 | |
95 | ||
0a85b6f0 MT |
96 | static int pcmad_ai_insn_read(struct comedi_device *dev, |
97 | struct comedi_subdevice *s, | |
98 | struct comedi_insn *insn, unsigned int *data) | |
58e732ee DS |
99 | { |
100 | int i; | |
101 | int chan; | |
102 | int n; | |
103 | ||
104 | chan = CR_CHAN(insn->chanspec); | |
105 | ||
106 | for (n = 0; n < insn->n; n++) { | |
107 | outb(chan, dev->iobase + PCMAD_CONVERT); | |
108 | ||
109 | for (i = 0; i < TIMEOUT; i++) { | |
110 | if ((inb(dev->iobase + PCMAD_STATUS) & 0x3) == 0x3) | |
111 | break; | |
112 | } | |
113 | data[n] = inb(dev->iobase + PCMAD_LSB); | |
114 | data[n] |= (inb(dev->iobase + PCMAD_MSB) << 8); | |
115 | ||
71a74bd9 | 116 | if (devpriv->twos_comp) |
58e732ee | 117 | data[n] ^= (1 << (this_board->n_ai_bits - 1)); |
58e732ee DS |
118 | } |
119 | ||
120 | return n; | |
121 | } | |
122 | ||
123 | /* | |
124 | * options: | |
125 | * 0 i/o base | |
126 | * 1 unused | |
127 | * 2 0=single ended 1=differential | |
128 | * 3 0=straight binary 1=two's comp | |
129 | */ | |
da91b269 | 130 | static int pcmad_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
58e732ee DS |
131 | { |
132 | int ret; | |
34c43922 | 133 | struct comedi_subdevice *s; |
58e732ee DS |
134 | unsigned long iobase; |
135 | ||
136 | iobase = it->options[0]; | |
dedf3ad1 | 137 | printk(KERN_INFO "comedi%d: pcmad: 0x%04lx ", dev->minor, iobase); |
58e732ee | 138 | if (!request_region(iobase, PCMAD_SIZE, "pcmad")) { |
fcc55b30 | 139 | printk(KERN_CONT "I/O port conflict\n"); |
58e732ee DS |
140 | return -EIO; |
141 | } | |
fcc55b30 | 142 | printk(KERN_CONT "\n"); |
58e732ee DS |
143 | dev->iobase = iobase; |
144 | ||
c3744138 BP |
145 | ret = alloc_subdevices(dev, 1); |
146 | if (ret < 0) | |
58e732ee | 147 | return ret; |
c3744138 BP |
148 | |
149 | ret = alloc_private(dev, sizeof(struct pcmad_priv_struct)); | |
150 | if (ret < 0) | |
58e732ee DS |
151 | return ret; |
152 | ||
153 | dev->board_name = this_board->name; | |
154 | ||
155 | s = dev->subdevices + 0; | |
156 | s->type = COMEDI_SUBD_AI; | |
157 | s->subdev_flags = SDF_READABLE | AREF_GROUND; | |
158 | s->n_chan = 16; /* XXX */ | |
159 | s->len_chanlist = 1; | |
160 | s->insn_read = pcmad_ai_insn_read; | |
161 | s->maxdata = (1 << this_board->n_ai_bits) - 1; | |
162 | s->range_table = &range_unknown; | |
163 | ||
164 | return 0; | |
165 | } | |
166 | ||
da91b269 | 167 | static int pcmad_detach(struct comedi_device *dev) |
58e732ee | 168 | { |
dedf3ad1 | 169 | printk(KERN_INFO "comedi%d: pcmad: remove\n", dev->minor); |
58e732ee | 170 | |
71a74bd9 | 171 | if (dev->irq) |
58e732ee | 172 | free_irq(dev->irq, dev); |
dedf3ad1 | 173 | |
58e732ee DS |
174 | if (dev->iobase) |
175 | release_region(dev->iobase, PCMAD_SIZE); | |
176 | ||
177 | return 0; | |
178 | } |