Commit | Line | Data |
---|---|---|
011f01bf IA |
1 | /******************************************************************************* |
2 | comedi/drivers/pci1723.c | |
3 | ||
4 | COMEDI - Linux Control and Measurement Device Interface | |
5 | Copyright (C) 2000 David A. Schleef <ds@schleef.org> | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software | |
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | ||
21 | *******************************************************************************/ | |
22 | /* | |
23 | Driver: adv_pci1723 | |
24 | Description: Advantech PCI-1723 | |
25 | Author: yonggang <rsmgnu@gmail.com>, Ian Abbott <abbotti@mev.co.uk> | |
26 | Devices: [Advantech] PCI-1723 (adv_pci1723) | |
27 | Updated: Mon, 14 Apr 2008 15:12:56 +0100 | |
28 | Status: works | |
29 | ||
30 | Configuration Options: | |
31 | [0] - PCI bus of device (optional) | |
32 | [1] - PCI slot of device (optional) | |
33 | ||
34 | If bus/slot is not specified, the first supported | |
35 | PCI device found will be used. | |
36 | ||
37 | Subdevice 0 is 8-channel AO, 16-bit, range +/- 10 V. | |
38 | ||
39 | Subdevice 1 is 16-channel DIO. The channels are configurable as input or | |
40 | output in 2 groups (0 to 7, 8 to 15). Configuring any channel implicitly | |
41 | configures all channels in the same group. | |
42 | ||
43 | TODO: | |
44 | ||
45 | 1. Add the two milliamp ranges to the AO subdevice (0 to 20 mA, 4 to 20 mA). | |
46 | 2. Read the initial ranges and values of the AO subdevice at start-up instead | |
47 | of reinitializing them. | |
48 | 3. Implement calibration. | |
49 | */ | |
50 | ||
51 | #include "../comedidev.h" | |
52 | ||
53 | #include "comedi_pci.h" | |
54 | ||
59af888d | 55 | #define PCI_VENDOR_ID_ADVANTECH 0x13fe /* Advantech PCI vendor ID */ |
011f01bf | 56 | |
d6d9bd32 | 57 | /* hardware types of the cards */ |
011f01bf IA |
58 | #define TYPE_PCI1723 0 |
59 | ||
60 | #define IORANGE_1723 0x2A | |
61 | ||
62 | /* all the registers for the pci1723 board */ | |
af6ddd57 MD |
63 | #define PCI1723_DA(N) ((N)<<1) /* W: D/A register N (0 to 7) */ |
64 | ||
65 | #define PCI1723_SYN_SET 0x12 /* synchronized set register */ | |
66 | #define PCI1723_ALL_CHNNELE_SYN_STROBE 0x12 | |
67 | /* synchronized status register */ | |
68 | ||
69 | #define PCI1723_RANGE_CALIBRATION_MODE 0x14 | |
70 | /* range and calibration mode */ | |
71 | #define PCI1723_RANGE_CALIBRATION_STATUS 0x14 | |
72 | /* range and calibration status */ | |
73 | ||
74 | #define PCI1723_CONTROL_CMD_CALIBRATION_FUN 0x16 | |
75 | /* | |
76 | * SADC control command for | |
77 | * calibration function | |
78 | */ | |
79 | #define PCI1723_STATUS_CMD_CALIBRATION_FUN 0x16 | |
80 | /* | |
81 | * SADC control status for | |
82 | * calibration function | |
83 | */ | |
84 | ||
85 | #define PCI1723_CALIBRATION_PARA_STROBE 0x18 | |
86 | /* Calibration parameter strobe */ | |
011f01bf IA |
87 | |
88 | #define PCI1723_DIGITAL_IO_PORT_SET 0x1A /* Digital I/O port setting */ | |
89 | #define PCI1723_DIGITAL_IO_PORT_MODE 0x1A /* Digital I/O port mode */ | |
90 | ||
af6ddd57 MD |
91 | #define PCI1723_WRITE_DIGITAL_OUTPUT_CMD 0x1C |
92 | /* Write digital output command */ | |
011f01bf IA |
93 | #define PCI1723_READ_DIGITAL_INPUT_DATA 0x1C /* Read digital input data */ |
94 | ||
af6ddd57 MD |
95 | #define PCI1723_WRITE_CAL_CMD 0x1E /* Write calibration command */ |
96 | #define PCI1723_READ_CAL_STATUS 0x1E /* Read calibration status */ | |
011f01bf | 97 | |
af6ddd57 | 98 | #define PCI1723_SYN_STROBE 0x20 /* Synchronized strobe */ |
011f01bf | 99 | |
af6ddd57 MD |
100 | #define PCI1723_RESET_ALL_CHN_STROBE 0x22 |
101 | /* Reset all D/A channels strobe */ | |
011f01bf | 102 | |
af6ddd57 MD |
103 | #define PCI1723_RESET_CAL_CONTROL_STROBE 0x24 |
104 | /* | |
105 | * Reset the calibration | |
106 | * controller strobe | |
107 | */ | |
011f01bf | 108 | |
af6ddd57 MD |
109 | #define PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE 0x26 |
110 | /* | |
111 | * Change D/A channels output | |
112 | * type strobe | |
113 | */ | |
011f01bf IA |
114 | |
115 | #define PCI1723_SELECT_CALIBRATION 0x28 /* Select the calibration Ref_V */ | |
116 | ||
d6d9bd32 | 117 | /* static unsigned short pci_list_builded=0; =1 list of card is know */ |
011f01bf | 118 | |
9ced1de6 | 119 | static const struct comedi_lrange range_pci1723 = { 1, { |
0a85b6f0 MT |
120 | BIP_RANGE(10) |
121 | } | |
011f01bf IA |
122 | }; |
123 | ||
124 | /* | |
125 | * Board descriptions for pci1723 boards. | |
126 | */ | |
006449f0 | 127 | struct pci1723_board { |
011f01bf | 128 | const char *name; |
af6ddd57 | 129 | int vendor_id; /* PCI vendor a device ID of card */ |
011f01bf IA |
130 | int device_id; |
131 | int iorange; | |
132 | char cardtype; | |
af6ddd57 MD |
133 | int n_aochan; /* num of D/A chans */ |
134 | int n_diochan; /* num of DIO chans */ | |
135 | int ao_maxdata; /* resolution of D/A */ | |
136 | const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */ | |
006449f0 | 137 | }; |
011f01bf | 138 | |
006449f0 | 139 | static const struct pci1723_board boardtypes[] = { |
011f01bf | 140 | { |
0a85b6f0 | 141 | .name = "pci1723", |
59af888d | 142 | .vendor_id = PCI_VENDOR_ID_ADVANTECH, |
0a85b6f0 MT |
143 | .device_id = 0x1723, |
144 | .iorange = IORANGE_1723, | |
145 | .cardtype = TYPE_PCI1723, | |
146 | .n_aochan = 8, | |
147 | .n_diochan = 16, | |
148 | .ao_maxdata = 0xffff, | |
149 | .rangelist_ao = &range_pci1723, | |
150 | }, | |
011f01bf IA |
151 | }; |
152 | ||
af6ddd57 MD |
153 | /* |
154 | * This is used by modprobe to translate PCI IDs to drivers. | |
155 | * Should only be used for PCI and ISA-PnP devices | |
156 | */ | |
011f01bf | 157 | static DEFINE_PCI_DEVICE_TABLE(pci1723_pci_table) = { |
0a85b6f0 MT |
158 | { |
159 | PCI_VENDOR_ID_ADVANTECH, 0x1723, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { | |
160 | 0} | |
011f01bf IA |
161 | }; |
162 | ||
163 | MODULE_DEVICE_TABLE(pci, pci1723_pci_table); | |
164 | ||
165 | /* | |
139dfbdf | 166 | * The struct comedi_driver structure tells the Comedi core module |
011f01bf IA |
167 | * which functions to call to configure/deconfigure (attach/detach) |
168 | * the board, and also about the kernel module that contains | |
169 | * the device code. | |
170 | */ | |
0a85b6f0 MT |
171 | static int pci1723_attach(struct comedi_device *dev, |
172 | struct comedi_devconfig *it); | |
da91b269 | 173 | static int pci1723_detach(struct comedi_device *dev); |
011f01bf | 174 | |
006449f0 | 175 | #define n_boardtypes (sizeof(boardtypes)/sizeof(struct pci1723_board)) |
011f01bf | 176 | |
139dfbdf | 177 | static struct comedi_driver driver_pci1723 = { |
68c3dbff BP |
178 | .driver_name = "adv_pci1723", |
179 | .module = THIS_MODULE, | |
180 | .attach = pci1723_attach, | |
181 | .detach = pci1723_detach, | |
011f01bf IA |
182 | }; |
183 | ||
af6ddd57 | 184 | /* This structure is for data unique to this hardware driver. */ |
006449f0 | 185 | struct pci1723_private { |
d6d9bd32 | 186 | int valid; /* card is usable; */ |
011f01bf IA |
187 | |
188 | struct pci_dev *pcidev; | |
af6ddd57 | 189 | unsigned char da_range[8]; /* D/A output range for each channel */ |
011f01bf | 190 | |
af6ddd57 | 191 | short ao_data[8]; /* data output buffer */ |
006449f0 | 192 | }; |
011f01bf | 193 | |
af6ddd57 | 194 | /* The following macro to make it easy to access the private structure. */ |
006449f0 | 195 | #define devpriv ((struct pci1723_private *)dev->private) |
011f01bf IA |
196 | |
197 | #define this_board boardtypes | |
198 | ||
199 | /* | |
af6ddd57 | 200 | * The pci1723 card reset; |
011f01bf | 201 | */ |
da91b269 | 202 | static int pci1723_reset(struct comedi_device *dev) |
011f01bf IA |
203 | { |
204 | int i; | |
205 | DPRINTK("adv_pci1723 EDBG: BGN: pci1723_reset(...)\n"); | |
206 | ||
af6ddd57 MD |
207 | outw(0x01, dev->iobase + PCI1723_SYN_SET); |
208 | /* set synchronous output mode */ | |
011f01bf IA |
209 | |
210 | for (i = 0; i < 8; i++) { | |
af6ddd57 | 211 | /* set all outputs to 0V */ |
011f01bf IA |
212 | devpriv->ao_data[i] = 0x8000; |
213 | outw(devpriv->ao_data[i], dev->iobase + PCI1723_DA(i)); | |
af6ddd57 | 214 | /* set all ranges to +/- 10V */ |
011f01bf IA |
215 | devpriv->da_range[i] = 0; |
216 | outw(((devpriv->da_range[i] << 4) | i), | |
0a85b6f0 | 217 | PCI1723_RANGE_CALIBRATION_MODE); |
011f01bf IA |
218 | } |
219 | ||
af6ddd57 MD |
220 | outw(0, dev->iobase + PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE); |
221 | /* update ranges */ | |
222 | outw(0, dev->iobase + PCI1723_SYN_STROBE); /* update outputs */ | |
011f01bf | 223 | |
af6ddd57 | 224 | /* set asynchronous output mode */ |
011f01bf IA |
225 | outw(0, dev->iobase + PCI1723_SYN_SET); |
226 | ||
227 | DPRINTK("adv_pci1723 EDBG: END: pci1723_reset(...)\n"); | |
228 | return 0; | |
229 | } | |
230 | ||
0a85b6f0 MT |
231 | static int pci1723_insn_read_ao(struct comedi_device *dev, |
232 | struct comedi_subdevice *s, | |
233 | struct comedi_insn *insn, unsigned int *data) | |
011f01bf IA |
234 | { |
235 | int n, chan; | |
236 | ||
237 | chan = CR_CHAN(insn->chanspec); | |
238 | DPRINTK(" adv_PCI1723 DEBUG: pci1723_insn_read_ao() ----- \n"); | |
239 | for (n = 0; n < insn->n; n++) | |
240 | data[n] = devpriv->ao_data[chan]; | |
241 | ||
242 | return n; | |
243 | } | |
244 | ||
245 | /* | |
246 | analog data output; | |
247 | */ | |
0a85b6f0 MT |
248 | static int pci1723_ao_write_winsn(struct comedi_device *dev, |
249 | struct comedi_subdevice *s, | |
250 | struct comedi_insn *insn, unsigned int *data) | |
011f01bf IA |
251 | { |
252 | int n, chan; | |
253 | chan = CR_CHAN(insn->chanspec); | |
254 | ||
255 | DPRINTK("PCI1723: the pci1723_ao_write_winsn() ------\n"); | |
256 | ||
257 | for (n = 0; n < insn->n; n++) { | |
258 | ||
259 | devpriv->ao_data[chan] = data[n]; | |
260 | outw(data[n], dev->iobase + PCI1723_DA(chan)); | |
261 | } | |
262 | ||
263 | return n; | |
264 | } | |
265 | ||
266 | /* | |
267 | digital i/o config/query | |
268 | */ | |
0a85b6f0 MT |
269 | static int pci1723_dio_insn_config(struct comedi_device *dev, |
270 | struct comedi_subdevice *s, | |
271 | struct comedi_insn *insn, unsigned int *data) | |
011f01bf IA |
272 | { |
273 | unsigned int mask; | |
274 | unsigned int bits; | |
275 | unsigned short dio_mode; | |
276 | ||
277 | mask = 1 << CR_CHAN(insn->chanspec); | |
af6ddd57 | 278 | if (mask & 0x00FF) |
011f01bf | 279 | bits = 0x00FF; |
af6ddd57 | 280 | else |
011f01bf | 281 | bits = 0xFF00; |
af6ddd57 | 282 | |
011f01bf IA |
283 | switch (data[0]) { |
284 | case INSN_CONFIG_DIO_INPUT: | |
285 | s->io_bits &= ~bits; | |
286 | break; | |
287 | case INSN_CONFIG_DIO_OUTPUT: | |
288 | s->io_bits |= bits; | |
289 | break; | |
290 | case INSN_CONFIG_DIO_QUERY: | |
291 | data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; | |
292 | return insn->n; | |
293 | default: | |
294 | return -EINVAL; | |
295 | } | |
296 | ||
af6ddd57 MD |
297 | /* update hardware DIO mode */ |
298 | dio_mode = 0x0000; /* low byte output, high byte output */ | |
011f01bf | 299 | if ((s->io_bits & 0x00FF) == 0) |
af6ddd57 | 300 | dio_mode |= 0x0001; /* low byte input */ |
011f01bf | 301 | if ((s->io_bits & 0xFF00) == 0) |
af6ddd57 | 302 | dio_mode |= 0x0002; /* high byte input */ |
011f01bf IA |
303 | outw(dio_mode, dev->iobase + PCI1723_DIGITAL_IO_PORT_SET); |
304 | return 1; | |
305 | } | |
306 | ||
307 | /* | |
308 | digital i/o bits read/write | |
309 | */ | |
0a85b6f0 MT |
310 | static int pci1723_dio_insn_bits(struct comedi_device *dev, |
311 | struct comedi_subdevice *s, | |
312 | struct comedi_insn *insn, unsigned int *data) | |
011f01bf IA |
313 | { |
314 | if (data[0]) { | |
315 | s->state &= ~data[0]; | |
316 | s->state |= (data[0] & data[1]); | |
317 | outw(s->state, dev->iobase + PCI1723_WRITE_DIGITAL_OUTPUT_CMD); | |
318 | } | |
319 | data[1] = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA); | |
320 | return 2; | |
321 | } | |
322 | ||
323 | /* | |
324 | * Attach is called by the Comedi core to configure the driver | |
325 | * for a pci1723 board. | |
326 | */ | |
0a85b6f0 MT |
327 | static int pci1723_attach(struct comedi_device *dev, |
328 | struct comedi_devconfig *it) | |
011f01bf | 329 | { |
34c43922 | 330 | struct comedi_subdevice *s; |
011f01bf IA |
331 | int ret, subdev, n_subdevices; |
332 | struct pci_dev *pcidev; | |
333 | unsigned int iobase; | |
334 | unsigned char pci_bus, pci_slot, pci_func; | |
335 | int opt_bus, opt_slot; | |
336 | const char *errstr; | |
337 | ||
af6ddd57 MD |
338 | printk(KERN_ERR "comedi%d: adv_pci1723: board=%s", |
339 | dev->minor, this_board->name); | |
011f01bf IA |
340 | |
341 | opt_bus = it->options[0]; | |
342 | opt_slot = it->options[1]; | |
343 | ||
c3744138 BP |
344 | ret = alloc_private(dev, sizeof(struct pci1723_private)); |
345 | if (ret < 0) { | |
5f74ea14 | 346 | printk(" - Allocation failed!\n"); |
011f01bf IA |
347 | return -ENOMEM; |
348 | } | |
349 | ||
350 | /* Look for matching PCI device */ | |
351 | errstr = "not found!"; | |
352 | pcidev = NULL; | |
353 | while (NULL != (pcidev = | |
354 | pci_get_device(PCI_VENDOR_ID_ADVANTECH, | |
0a85b6f0 | 355 | this_board->device_id, pcidev))) { |
011f01bf IA |
356 | /* Found matching vendor/device. */ |
357 | if (opt_bus || opt_slot) { | |
358 | /* Check bus/slot. */ | |
359 | if (opt_bus != pcidev->bus->number | |
0a85b6f0 | 360 | || opt_slot != PCI_SLOT(pcidev->devfn)) |
011f01bf IA |
361 | continue; /* no match */ |
362 | } | |
363 | /* | |
364 | * Look for device that isn't in use. | |
365 | * Enable PCI device and request regions. | |
366 | */ | |
367 | if (comedi_pci_enable(pcidev, "adv_pci1723")) { | |
0a85b6f0 MT |
368 | errstr = |
369 | "failed to enable PCI device and request regions!"; | |
011f01bf IA |
370 | continue; |
371 | } | |
372 | break; | |
373 | } | |
374 | ||
375 | if (!pcidev) { | |
376 | if (opt_bus || opt_slot) { | |
af6ddd57 MD |
377 | printk(KERN_ERR " - Card at b:s %d:%d %s\n", |
378 | opt_bus, opt_slot, errstr); | |
011f01bf | 379 | } else { |
af6ddd57 | 380 | printk(KERN_ERR " - Card %s\n", errstr); |
011f01bf IA |
381 | } |
382 | return -EIO; | |
383 | } | |
384 | ||
385 | pci_bus = pcidev->bus->number; | |
386 | pci_slot = PCI_SLOT(pcidev->devfn); | |
387 | pci_func = PCI_FUNC(pcidev->devfn); | |
388 | iobase = pci_resource_start(pcidev, 2); | |
389 | ||
af6ddd57 MD |
390 | printk(KERN_ERR ", b:s:f=%d:%d:%d, io=0x%4x", |
391 | pci_bus, pci_slot, pci_func, iobase); | |
011f01bf IA |
392 | |
393 | dev->iobase = iobase; | |
394 | ||
395 | dev->board_name = this_board->name; | |
396 | devpriv->pcidev = pcidev; | |
397 | ||
398 | n_subdevices = 0; | |
399 | ||
400 | if (this_board->n_aochan) | |
401 | n_subdevices++; | |
402 | if (this_board->n_diochan) | |
403 | n_subdevices++; | |
404 | ||
c3744138 BP |
405 | ret = alloc_subdevices(dev, n_subdevices); |
406 | if (ret < 0) { | |
5f74ea14 | 407 | printk(" - Allocation failed!\n"); |
011f01bf IA |
408 | return ret; |
409 | } | |
410 | ||
411 | pci1723_reset(dev); | |
412 | subdev = 0; | |
413 | if (this_board->n_aochan) { | |
414 | s = dev->subdevices + subdev; | |
415 | dev->write_subdev = s; | |
416 | s->type = COMEDI_SUBD_AO; | |
417 | s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; | |
418 | s->n_chan = this_board->n_aochan; | |
419 | s->maxdata = this_board->ao_maxdata; | |
420 | s->len_chanlist = this_board->n_aochan; | |
421 | s->range_table = this_board->rangelist_ao; | |
422 | ||
423 | s->insn_write = pci1723_ao_write_winsn; | |
424 | s->insn_read = pci1723_insn_read_ao; | |
425 | ||
af6ddd57 MD |
426 | /* read DIO config */ |
427 | switch (inw(dev->iobase + PCI1723_DIGITAL_IO_PORT_MODE) | |
428 | & 0x03) { | |
429 | case 0x00: /* low byte output, high byte output */ | |
011f01bf IA |
430 | s->io_bits = 0xFFFF; |
431 | break; | |
af6ddd57 | 432 | case 0x01: /* low byte input, high byte output */ |
011f01bf IA |
433 | s->io_bits = 0xFF00; |
434 | break; | |
af6ddd57 | 435 | case 0x02: /* low byte output, high byte input */ |
011f01bf IA |
436 | s->io_bits = 0x00FF; |
437 | break; | |
af6ddd57 | 438 | case 0x03: /* low byte input, high byte input */ |
011f01bf IA |
439 | s->io_bits = 0x0000; |
440 | break; | |
441 | } | |
af6ddd57 | 442 | /* read DIO port state */ |
011f01bf IA |
443 | s->state = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA); |
444 | ||
445 | subdev++; | |
446 | } | |
447 | ||
448 | if (this_board->n_diochan) { | |
449 | s = dev->subdevices + subdev; | |
450 | s->type = COMEDI_SUBD_DIO; | |
451 | s->subdev_flags = | |
0a85b6f0 | 452 | SDF_READABLE | SDF_WRITABLE | SDF_GROUND | SDF_COMMON; |
011f01bf IA |
453 | s->n_chan = this_board->n_diochan; |
454 | s->maxdata = 1; | |
455 | s->len_chanlist = this_board->n_diochan; | |
456 | s->range_table = &range_digital; | |
457 | s->insn_config = pci1723_dio_insn_config; | |
458 | s->insn_bits = pci1723_dio_insn_bits; | |
459 | subdev++; | |
460 | } | |
461 | ||
462 | devpriv->valid = 1; | |
463 | ||
464 | pci1723_reset(dev); | |
465 | ||
466 | return 0; | |
467 | } | |
468 | ||
469 | /* | |
470 | * _detach is called to deconfigure a device. It should deallocate | |
471 | * resources. | |
472 | * This function is also called when _attach() fails, so it should be | |
473 | * careful not to release resources that were not necessarily | |
474 | * allocated by _attach(). dev->private and dev->subdevices are | |
475 | * deallocated automatically by the core. | |
476 | */ | |
da91b269 | 477 | static int pci1723_detach(struct comedi_device *dev) |
011f01bf | 478 | { |
af6ddd57 | 479 | printk(KERN_ERR "comedi%d: pci1723: remove\n", dev->minor); |
011f01bf IA |
480 | |
481 | if (dev->private) { | |
482 | if (devpriv->valid) | |
483 | pci1723_reset(dev); | |
484 | ||
485 | if (devpriv->pcidev) { | |
af6ddd57 | 486 | if (dev->iobase) |
011f01bf | 487 | comedi_pci_disable(devpriv->pcidev); |
011f01bf IA |
488 | pci_dev_put(devpriv->pcidev); |
489 | } | |
490 | } | |
491 | ||
492 | return 0; | |
493 | } | |
494 | ||
495 | /* | |
496 | * A convenient macro that defines init_module() and cleanup_module(), | |
497 | * as necessary. | |
498 | */ | |
727b286b AT |
499 | static int __devinit driver_pci1723_pci_probe(struct pci_dev *dev, |
500 | const struct pci_device_id *ent) | |
501 | { | |
502 | return comedi_pci_auto_config(dev, driver_pci1723.driver_name); | |
503 | } | |
504 | ||
505 | static void __devexit driver_pci1723_pci_remove(struct pci_dev *dev) | |
506 | { | |
507 | comedi_pci_auto_unconfig(dev); | |
508 | } | |
509 | ||
510 | static struct pci_driver driver_pci1723_pci_driver = { | |
511 | .id_table = pci1723_pci_table, | |
512 | .probe = &driver_pci1723_pci_probe, | |
513 | .remove = __devexit_p(&driver_pci1723_pci_remove) | |
514 | }; | |
515 | ||
516 | static int __init driver_pci1723_init_module(void) | |
517 | { | |
518 | int retval; | |
519 | ||
520 | retval = comedi_driver_register(&driver_pci1723); | |
521 | if (retval < 0) | |
522 | return retval; | |
523 | ||
524 | driver_pci1723_pci_driver.name = (char *)driver_pci1723.driver_name; | |
525 | return pci_register_driver(&driver_pci1723_pci_driver); | |
526 | } | |
527 | ||
528 | static void __exit driver_pci1723_cleanup_module(void) | |
529 | { | |
530 | pci_unregister_driver(&driver_pci1723_pci_driver); | |
531 | comedi_driver_unregister(&driver_pci1723); | |
532 | } | |
533 | ||
534 | module_init(driver_pci1723_init_module); | |
535 | module_exit(driver_pci1723_cleanup_module); | |
90f703d3 AT |
536 | |
537 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
538 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
539 | MODULE_LICENSE("GPL"); |