Commit | Line | Data |
---|---|---|
f0922ec5 DS |
1 | /* |
2 | comedi/drivers/das16cs.c | |
3 | Driver for Computer Boards PC-CARD DAS16/16. | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | Copyright (C) 2000, 2001, 2002 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 | ||
cd1a76fb HS |
18 | PCMCIA support code for this driver is adapted from the dummy_cs.c |
19 | driver of the Linux PCMCIA Card Services package. | |
1b19f53e HS |
20 | |
21 | The initial developer of the original code is David A. Hinds | |
22 | <dahinds@users.sourceforge.net>. Portions created by David A. Hinds | |
23 | are Copyright (C) 1999 David A. Hinds. All Rights Reserved. | |
24 | ||
f0922ec5 DS |
25 | */ |
26 | /* | |
27 | Driver: cb_das16_cs | |
28 | Description: Computer Boards PC-CARD DAS16/16 | |
29 | Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO | |
30 | Author: ds | |
31 | Updated: Mon, 04 Nov 2002 20:04:21 -0800 | |
32 | Status: experimental | |
33 | ||
34 | ||
35 | */ | |
36 | ||
25436dc9 | 37 | #include <linux/interrupt.h> |
5a0e3ad6 | 38 | #include <linux/slab.h> |
f0922ec5 | 39 | #include <linux/delay.h> |
f0922ec5 | 40 | |
2bdaef1a HS |
41 | #include "../comedidev.h" |
42 | ||
f0922ec5 DS |
43 | #include <pcmcia/cistpl.h> |
44 | #include <pcmcia/ds.h> | |
45 | ||
27020ffe | 46 | #include "comedi_fc.h" |
f0922ec5 DS |
47 | #include "8253.h" |
48 | ||
49 | #define DAS16CS_SIZE 18 | |
50 | ||
51 | #define DAS16CS_ADC_DATA 0 | |
52 | #define DAS16CS_DIO_MUX 2 | |
53 | #define DAS16CS_MISC1 4 | |
54 | #define DAS16CS_MISC2 6 | |
55 | #define DAS16CS_CTR0 8 | |
56 | #define DAS16CS_CTR1 10 | |
57 | #define DAS16CS_CTR2 12 | |
58 | #define DAS16CS_CTR_CONTROL 14 | |
59 | #define DAS16CS_DIO 16 | |
60 | ||
3281a63d | 61 | struct das16cs_board { |
f0922ec5 DS |
62 | const char *name; |
63 | int device_id; | |
64 | int n_ao_chans; | |
3281a63d | 65 | }; |
13ab179c | 66 | |
3281a63d | 67 | static const struct das16cs_board das16cs_boards[] = { |
f0922ec5 | 68 | { |
13ab179c HS |
69 | .name = "PC-CARD DAS16/16-AO", |
70 | .device_id = 0x0039, | |
71 | .n_ao_chans = 2, | |
72 | }, { | |
73 | .name = "PCM-DAS16s/16", | |
74 | .device_id = 0x4009, | |
75 | .n_ao_chans = 0, | |
76 | }, { | |
77 | .name = "PC-CARD DAS16/16", | |
78 | .device_id = 0x0000, /* unknown */ | |
79 | .n_ao_chans = 0, | |
80 | }, | |
f0922ec5 DS |
81 | }; |
82 | ||
bfae362a | 83 | struct das16cs_private { |
790c5541 | 84 | unsigned int ao_readback[2]; |
f0922ec5 DS |
85 | unsigned short status1; |
86 | unsigned short status2; | |
bfae362a | 87 | }; |
f0922ec5 | 88 | |
443bbedb HS |
89 | static const struct comedi_lrange das16cs_ai_range = { |
90 | 4, { | |
91 | BIP_RANGE(10), | |
92 | BIP_RANGE(5), | |
93 | BIP_RANGE(2.5), | |
94 | BIP_RANGE(1.25), | |
95 | } | |
f0922ec5 DS |
96 | }; |
97 | ||
70265d24 | 98 | static irqreturn_t das16cs_interrupt(int irq, void *d) |
f0922ec5 | 99 | { |
2696fb57 | 100 | /* struct comedi_device *dev = d; */ |
f0922ec5 DS |
101 | return IRQ_HANDLED; |
102 | } | |
103 | ||
0a85b6f0 MT |
104 | static int das16cs_ai_rinsn(struct comedi_device *dev, |
105 | struct comedi_subdevice *s, | |
106 | struct comedi_insn *insn, unsigned int *data) | |
f0922ec5 | 107 | { |
16a8cdfc | 108 | struct das16cs_private *devpriv = dev->private; |
e05b98b6 HS |
109 | int chan = CR_CHAN(insn->chanspec); |
110 | int range = CR_RANGE(insn->chanspec); | |
111 | int aref = CR_AREF(insn->chanspec); | |
f0922ec5 DS |
112 | int i; |
113 | int to; | |
f0922ec5 | 114 | |
ae1a4000 | 115 | outw(chan, dev->iobase + DAS16CS_DIO_MUX); |
f0922ec5 DS |
116 | |
117 | devpriv->status1 &= ~0xf320; | |
118 | devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020; | |
ae1a4000 | 119 | outw(devpriv->status1, dev->iobase + DAS16CS_MISC1); |
f0922ec5 DS |
120 | |
121 | devpriv->status2 &= ~0xff00; | |
e05b98b6 HS |
122 | switch (range) { |
123 | case 0: | |
124 | devpriv->status2 |= 0x800; | |
125 | break; | |
126 | case 1: | |
127 | devpriv->status2 |= 0x000; | |
128 | break; | |
129 | case 2: | |
130 | devpriv->status2 |= 0x100; | |
131 | break; | |
132 | case 3: | |
133 | devpriv->status2 |= 0x200; | |
134 | break; | |
e05b98b6 | 135 | } |
ae1a4000 | 136 | outw(devpriv->status2, dev->iobase + DAS16CS_MISC2); |
f0922ec5 DS |
137 | |
138 | for (i = 0; i < insn->n; i++) { | |
ae1a4000 | 139 | outw(0, dev->iobase + DAS16CS_ADC_DATA); |
f0922ec5 DS |
140 | |
141 | #define TIMEOUT 1000 | |
142 | for (to = 0; to < TIMEOUT; to++) { | |
ae1a4000 | 143 | if (inw(dev->iobase + DAS16CS_MISC1) & 0x0080) |
f0922ec5 DS |
144 | break; |
145 | } | |
146 | if (to == TIMEOUT) { | |
f41ad667 | 147 | dev_dbg(dev->class_dev, "cb_das16_cs: ai timeout\n"); |
f0922ec5 DS |
148 | return -ETIME; |
149 | } | |
ae1a4000 | 150 | data[i] = inw(dev->iobase + DAS16CS_ADC_DATA); |
f0922ec5 DS |
151 | } |
152 | ||
153 | return i; | |
154 | } | |
155 | ||
da91b269 | 156 | static int das16cs_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
f0922ec5 DS |
157 | { |
158 | return -EINVAL; | |
159 | } | |
160 | ||
0a85b6f0 MT |
161 | static int das16cs_ai_cmdtest(struct comedi_device *dev, |
162 | struct comedi_subdevice *s, | |
163 | struct comedi_cmd *cmd) | |
f0922ec5 DS |
164 | { |
165 | int err = 0; | |
166 | int tmp; | |
167 | ||
27020ffe | 168 | /* Step 1 : check if triggers are trivially valid */ |
f0922ec5 | 169 | |
27020ffe HS |
170 | err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW); |
171 | err |= cfc_check_trigger_src(&cmd->scan_begin_src, | |
172 | TRIG_TIMER | TRIG_EXT); | |
173 | err |= cfc_check_trigger_src(&cmd->convert_src, | |
174 | TRIG_TIMER | TRIG_EXT); | |
175 | err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); | |
176 | err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
f0922ec5 DS |
177 | |
178 | if (err) | |
179 | return 1; | |
180 | ||
27020ffe | 181 | /* Step 2a : make sure trigger sources are unique */ |
f0922ec5 | 182 | |
27020ffe HS |
183 | err |= cfc_check_trigger_is_unique(cmd->scan_begin_src); |
184 | err |= cfc_check_trigger_is_unique(cmd->convert_src); | |
185 | err |= cfc_check_trigger_is_unique(cmd->stop_src); | |
186 | ||
187 | /* Step 2b : and mutually compatible */ | |
f0922ec5 DS |
188 | |
189 | if (err) | |
190 | return 2; | |
191 | ||
7d4adbe4 HS |
192 | /* Step 3: check if arguments are trivially valid */ |
193 | ||
194 | err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); | |
f0922ec5 | 195 | |
f0922ec5 DS |
196 | #define MAX_SPEED 10000 /* in nanoseconds */ |
197 | #define MIN_SPEED 1000000000 /* in nanoseconds */ | |
198 | ||
199 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
7d4adbe4 HS |
200 | err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, |
201 | MAX_SPEED); | |
202 | err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg, | |
203 | MIN_SPEED); | |
f0922ec5 DS |
204 | } else { |
205 | /* external trigger */ | |
206 | /* should be level/edge, hi/lo specification here */ | |
207 | /* should specify multiple external triggers */ | |
7d4adbe4 | 208 | err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg, 9); |
f0922ec5 DS |
209 | } |
210 | if (cmd->convert_src == TRIG_TIMER) { | |
7d4adbe4 HS |
211 | err |= cfc_check_trigger_arg_min(&cmd->convert_arg, |
212 | MAX_SPEED); | |
213 | err |= cfc_check_trigger_arg_max(&cmd->convert_arg, | |
214 | MIN_SPEED); | |
f0922ec5 DS |
215 | } else { |
216 | /* external trigger */ | |
217 | /* see above */ | |
7d4adbe4 | 218 | err |= cfc_check_trigger_arg_max(&cmd->convert_arg, 9); |
f0922ec5 DS |
219 | } |
220 | ||
7d4adbe4 HS |
221 | err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); |
222 | ||
223 | if (cmd->stop_src == TRIG_COUNT) | |
224 | err |= cfc_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff); | |
225 | else /* TRIG_NONE */ | |
226 | err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); | |
f0922ec5 DS |
227 | |
228 | if (err) | |
229 | return 3; | |
230 | ||
231 | /* step 4: fix up any arguments */ | |
232 | ||
233 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
48b1aff5 | 234 | unsigned int div1 = 0, div2 = 0; |
f0922ec5 DS |
235 | |
236 | tmp = cmd->scan_begin_arg; | |
237 | i8253_cascade_ns_to_timer(100, &div1, &div2, | |
0a85b6f0 MT |
238 | &cmd->scan_begin_arg, |
239 | cmd->flags & TRIG_ROUND_MASK); | |
f0922ec5 DS |
240 | if (tmp != cmd->scan_begin_arg) |
241 | err++; | |
242 | } | |
243 | if (cmd->convert_src == TRIG_TIMER) { | |
48b1aff5 | 244 | unsigned int div1 = 0, div2 = 0; |
f0922ec5 DS |
245 | |
246 | tmp = cmd->convert_arg; | |
247 | i8253_cascade_ns_to_timer(100, &div1, &div2, | |
0a85b6f0 MT |
248 | &cmd->scan_begin_arg, |
249 | cmd->flags & TRIG_ROUND_MASK); | |
f0922ec5 DS |
250 | if (tmp != cmd->convert_arg) |
251 | err++; | |
252 | if (cmd->scan_begin_src == TRIG_TIMER && | |
0a85b6f0 MT |
253 | cmd->scan_begin_arg < |
254 | cmd->convert_arg * cmd->scan_end_arg) { | |
f0922ec5 | 255 | cmd->scan_begin_arg = |
0a85b6f0 | 256 | cmd->convert_arg * cmd->scan_end_arg; |
f0922ec5 DS |
257 | err++; |
258 | } | |
259 | } | |
260 | ||
261 | if (err) | |
262 | return 4; | |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
0a85b6f0 MT |
267 | static int das16cs_ao_winsn(struct comedi_device *dev, |
268 | struct comedi_subdevice *s, | |
269 | struct comedi_insn *insn, unsigned int *data) | |
f0922ec5 | 270 | { |
16a8cdfc | 271 | struct das16cs_private *devpriv = dev->private; |
f0922ec5 DS |
272 | int i; |
273 | int chan = CR_CHAN(insn->chanspec); | |
274 | unsigned short status1; | |
275 | unsigned short d; | |
276 | int bit; | |
277 | ||
278 | for (i = 0; i < insn->n; i++) { | |
279 | devpriv->ao_readback[chan] = data[i]; | |
280 | d = data[i]; | |
281 | ||
ae1a4000 | 282 | outw(devpriv->status1, dev->iobase + DAS16CS_MISC1); |
5f74ea14 | 283 | udelay(1); |
f0922ec5 DS |
284 | |
285 | status1 = devpriv->status1 & ~0xf; | |
286 | if (chan) | |
287 | status1 |= 0x0001; | |
288 | else | |
289 | status1 |= 0x0008; | |
290 | ||
ae1a4000 | 291 | outw(status1, dev->iobase + DAS16CS_MISC1); |
5f74ea14 | 292 | udelay(1); |
f0922ec5 DS |
293 | |
294 | for (bit = 15; bit >= 0; bit--) { | |
295 | int b = (d >> bit) & 0x1; | |
296 | b <<= 1; | |
ae1a4000 | 297 | outw(status1 | b | 0x0000, dev->iobase + DAS16CS_MISC1); |
5f74ea14 | 298 | udelay(1); |
ae1a4000 | 299 | outw(status1 | b | 0x0004, dev->iobase + DAS16CS_MISC1); |
5f74ea14 | 300 | udelay(1); |
f0922ec5 | 301 | } |
0a77678c HS |
302 | /* |
303 | * Make both DAC0CS and DAC1CS high to load | |
304 | * the new data and update analog the output | |
305 | */ | |
ae1a4000 | 306 | outw(status1 | 0x9, dev->iobase + DAS16CS_MISC1); |
f0922ec5 DS |
307 | } |
308 | ||
309 | return i; | |
310 | } | |
311 | ||
0a85b6f0 MT |
312 | static int das16cs_ao_rinsn(struct comedi_device *dev, |
313 | struct comedi_subdevice *s, | |
314 | struct comedi_insn *insn, unsigned int *data) | |
f0922ec5 | 315 | { |
16a8cdfc | 316 | struct das16cs_private *devpriv = dev->private; |
f0922ec5 DS |
317 | int i; |
318 | int chan = CR_CHAN(insn->chanspec); | |
319 | ||
320 | for (i = 0; i < insn->n; i++) | |
321 | data[i] = devpriv->ao_readback[chan]; | |
322 | ||
323 | return i; | |
324 | } | |
325 | ||
0a85b6f0 MT |
326 | static int das16cs_dio_insn_bits(struct comedi_device *dev, |
327 | struct comedi_subdevice *s, | |
328 | struct comedi_insn *insn, unsigned int *data) | |
f0922ec5 | 329 | { |
f0922ec5 DS |
330 | if (data[0]) { |
331 | s->state &= ~data[0]; | |
332 | s->state |= data[0] & data[1]; | |
333 | ||
ae1a4000 | 334 | outw(s->state, dev->iobase + DAS16CS_DIO); |
f0922ec5 DS |
335 | } |
336 | ||
ae1a4000 | 337 | data[1] = inw(dev->iobase + DAS16CS_DIO); |
f0922ec5 | 338 | |
a2714e3e | 339 | return insn->n; |
f0922ec5 DS |
340 | } |
341 | ||
0a85b6f0 MT |
342 | static int das16cs_dio_insn_config(struct comedi_device *dev, |
343 | struct comedi_subdevice *s, | |
344 | struct comedi_insn *insn, unsigned int *data) | |
f0922ec5 | 345 | { |
16a8cdfc | 346 | struct das16cs_private *devpriv = dev->private; |
f0922ec5 DS |
347 | int chan = CR_CHAN(insn->chanspec); |
348 | int bits; | |
349 | ||
350 | if (chan < 4) | |
351 | bits = 0x0f; | |
352 | else | |
353 | bits = 0xf0; | |
354 | ||
355 | switch (data[0]) { | |
356 | case INSN_CONFIG_DIO_OUTPUT: | |
357 | s->io_bits |= bits; | |
358 | break; | |
359 | case INSN_CONFIG_DIO_INPUT: | |
360 | s->io_bits &= bits; | |
361 | break; | |
362 | case INSN_CONFIG_DIO_QUERY: | |
363 | data[1] = | |
0a85b6f0 | 364 | (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; |
f0922ec5 DS |
365 | return insn->n; |
366 | break; | |
367 | default: | |
368 | return -EINVAL; | |
369 | break; | |
370 | } | |
371 | ||
372 | devpriv->status2 &= ~0x00c0; | |
373 | devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0; | |
374 | devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0; | |
375 | ||
ae1a4000 | 376 | outw(devpriv->status2, dev->iobase + DAS16CS_MISC2); |
f0922ec5 DS |
377 | |
378 | return insn->n; | |
379 | } | |
380 | ||
2bdaef1a HS |
381 | static const void *das16cs_find_boardinfo(struct comedi_device *dev, |
382 | struct pcmcia_device *link) | |
ae7df434 | 383 | { |
2bdaef1a | 384 | const struct das16cs_board *board; |
ae7df434 HS |
385 | int i; |
386 | ||
1efc5d53 | 387 | for (i = 0; i < ARRAY_SIZE(das16cs_boards); i++) { |
2bdaef1a HS |
388 | board = &das16cs_boards[i]; |
389 | if (board->device_id == link->card_id) | |
390 | return board; | |
ae7df434 HS |
391 | } |
392 | ||
ae7df434 HS |
393 | return NULL; |
394 | } | |
395 | ||
2bdaef1a HS |
396 | static int das16cs_auto_attach(struct comedi_device *dev, |
397 | unsigned long context) | |
ae7df434 | 398 | { |
2bdaef1a HS |
399 | struct pcmcia_device *link = comedi_to_pcmcia_dev(dev); |
400 | const struct das16cs_board *board; | |
9a1a6cf8 | 401 | struct das16cs_private *devpriv; |
ae7df434 HS |
402 | struct comedi_subdevice *s; |
403 | int ret; | |
ae7df434 | 404 | |
2bdaef1a HS |
405 | board = das16cs_find_boardinfo(dev, link); |
406 | if (!board) | |
407 | return -ENODEV; | |
408 | dev->board_ptr = board; | |
409 | dev->board_name = board->name; | |
ae7df434 | 410 | |
9e29ce10 | 411 | link->config_flags |= CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; |
a3ac9519 | 412 | ret = comedi_pcmcia_enable(dev, NULL); |
2bdaef1a HS |
413 | if (ret) |
414 | return ret; | |
9e29ce10 | 415 | dev->iobase = link->resource[0]->start; |
2bdaef1a | 416 | |
bcd22e8e HS |
417 | link->priv = dev; |
418 | ret = pcmcia_request_irq(link, das16cs_interrupt); | |
419 | if (ret) | |
ae7df434 | 420 | return ret; |
ae7df434 HS |
421 | dev->irq = link->irq; |
422 | ||
c34fa261 HS |
423 | devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); |
424 | if (!devpriv) | |
425 | return -ENOMEM; | |
426 | dev->private = devpriv; | |
ae7df434 | 427 | |
5c416ef3 | 428 | ret = comedi_alloc_subdevices(dev, 3); |
ae7df434 HS |
429 | if (ret) |
430 | return ret; | |
431 | ||
3d69288a | 432 | s = &dev->subdevices[0]; |
ae7df434 HS |
433 | dev->read_subdev = s; |
434 | /* analog input subdevice */ | |
cf2592d0 HS |
435 | s->type = COMEDI_SUBD_AI; |
436 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ; | |
437 | s->n_chan = 16; | |
438 | s->maxdata = 0xffff; | |
439 | s->range_table = &das16cs_ai_range; | |
440 | s->len_chanlist = 16; | |
441 | s->insn_read = das16cs_ai_rinsn; | |
442 | s->do_cmd = das16cs_ai_cmd; | |
443 | s->do_cmdtest = das16cs_ai_cmdtest; | |
ae7df434 | 444 | |
3d69288a | 445 | s = &dev->subdevices[1]; |
ae7df434 | 446 | /* analog output subdevice */ |
2bdaef1a | 447 | if (board->n_ao_chans) { |
cf2592d0 HS |
448 | s->type = COMEDI_SUBD_AO; |
449 | s->subdev_flags = SDF_WRITABLE; | |
2bdaef1a | 450 | s->n_chan = board->n_ao_chans; |
cf2592d0 HS |
451 | s->maxdata = 0xffff; |
452 | s->range_table = &range_bipolar10; | |
453 | s->insn_write = &das16cs_ao_winsn; | |
454 | s->insn_read = &das16cs_ao_rinsn; | |
da7fde2b | 455 | } else { |
cf2592d0 | 456 | s->type = COMEDI_SUBD_UNUSED; |
ae7df434 HS |
457 | } |
458 | ||
3d69288a | 459 | s = &dev->subdevices[2]; |
ae7df434 | 460 | /* digital i/o subdevice */ |
cf2592d0 HS |
461 | s->type = COMEDI_SUBD_DIO; |
462 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
463 | s->n_chan = 8; | |
464 | s->maxdata = 1; | |
465 | s->range_table = &range_digital; | |
466 | s->insn_bits = das16cs_dio_insn_bits; | |
467 | s->insn_config = das16cs_dio_insn_config; | |
ae7df434 | 468 | |
1fce3034 HS |
469 | dev_info(dev->class_dev, "%s: %s, I/O base=0x%04lx, irq=%u\n", |
470 | dev->driver->driver_name, dev->board_name, | |
471 | dev->iobase, dev->irq); | |
472 | ||
a659643a | 473 | return 0; |
ae7df434 HS |
474 | } |
475 | ||
2f018267 HS |
476 | static struct comedi_driver driver_das16cs = { |
477 | .driver_name = "cb_das16_cs", | |
478 | .module = THIS_MODULE, | |
2bdaef1a | 479 | .auto_attach = das16cs_auto_attach, |
7fadd565 | 480 | .detach = comedi_pcmcia_disable, |
2f018267 HS |
481 | }; |
482 | ||
c2107ff4 | 483 | static int das16cs_pcmcia_attach(struct pcmcia_device *link) |
55a19b39 | 484 | { |
2bdaef1a | 485 | return comedi_pcmcia_auto_config(link, &driver_das16cs); |
1b19f53e | 486 | } |
f0922ec5 | 487 | |
2202a5a7 | 488 | static const struct pcmcia_device_id das16cs_id_table[] = { |
f0922ec5 DS |
489 | PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039), |
490 | PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009), | |
491 | PCMCIA_DEVICE_NULL | |
492 | }; | |
f0922ec5 DS |
493 | MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table); |
494 | ||
e8bed9c6 HS |
495 | static struct pcmcia_driver das16cs_driver = { |
496 | .name = "cb_das16_cs", | |
497 | .owner = THIS_MODULE, | |
e8bed9c6 | 498 | .id_table = das16cs_id_table, |
2bdaef1a HS |
499 | .probe = das16cs_pcmcia_attach, |
500 | .remove = comedi_pcmcia_auto_unconfig, | |
f0922ec5 | 501 | }; |
66bf1ed6 | 502 | module_comedi_pcmcia_driver(driver_das16cs, das16cs_driver); |
16920671 HS |
503 | |
504 | MODULE_AUTHOR("David A. Schleef <ds@schleef.org>"); | |
505 | MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16"); | |
506 | MODULE_LICENSE("GPL"); |