aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBernd Porr <BerndPorr@f2s.com>2008-11-14 17:49:34 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-01-06 16:52:19 -0500
commitf47c697d130087831ac47bbbd2758c01de2e7081 (patch)
tree821d7dce23e5782c7c4fd6b3910aa5945b7570da
parent4bf21fa4dabaa8803906c6548496fcf12b15b10f (diff)
Staging: comedi: add usb usbduxfast driver
ITL USB-DUXfast driver From: Bernd Porr <BerndPorr@f2s.com> Cc: David Schleef <ds@schleef.org> Cc: Frank Mori Hess <fmhess@users.sourceforge.net> Cc: Ian Abbott <abbotti@mev.co.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/staging/comedi/drivers/Makefile1
-rw-r--r--drivers/staging/comedi/drivers/usbduxfast.c1779
2 files changed, 1780 insertions, 0 deletions
diff --git a/drivers/staging/comedi/drivers/Makefile b/drivers/staging/comedi/drivers/Makefile
index 6b38d8360950..fcf6bcfd1d59 100644
--- a/drivers/staging/comedi/drivers/Makefile
+++ b/drivers/staging/comedi/drivers/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_COMEDI_PCI_DRIVERS) += mite.o
9 9
10# Comedi USB drivers 10# Comedi USB drivers
11obj-$(CONFIG_COMEDI_USB_DRIVERS) += usbdux.o 11obj-$(CONFIG_COMEDI_USB_DRIVERS) += usbdux.o
12obj-$(CONFIG_COMEDI_USB_DRIVERS) += usbduxfast.o
diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c
new file mode 100644
index 000000000000..403a614000b1
--- /dev/null
+++ b/drivers/staging/comedi/drivers/usbduxfast.c
@@ -0,0 +1,1779 @@
1#define DRIVER_VERSION "v0.99a"
2#define DRIVER_AUTHOR "Bernd Porr, BerndPorr@f2s.com"
3#define DRIVER_DESC "USB-DUXfast, BerndPorr@f2s.com"
4/*
5 comedi/drivers/usbduxfast.c
6 Copyright (C) 2004 Bernd Porr, Bernd.Porr@f2s.com
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: usbduxfast
25Description: ITL USB-DUXfast
26Devices: [ITL] USB-DUX (usbduxfast.o)
27Author: Bernd Porr <BerndPorr@f2s.com>
28Updated: 04 Dec 2006
29Status: testing
30*/
31
32/*
33 * I must give credit here to Chris Baugher who
34 * wrote the driver for AT-MIO-16d. I used some parts of this
35 * driver. I also must give credits to David Brownell
36 * who supported me with the USB development.
37 *
38 * Bernd Porr
39 *
40 *
41 * Revision history:
42 * 0.9: Dropping the first data packet which seems to be from the last transfer.
43 * Buffer overflows in the FX2 are handed over to comedi.
44 * 0.92: Dropping now 4 packets. The quad buffer has to be emptied.
45 * Added insn command basically for testing. Sample rate is 1MHz/16ch=62.5kHz
46 * 0.99: Ian Abbott pointed out a bug which has been corrected. Thanks!
47 * 0.99a: added external trigger.
48 */
49
50#include <linux/kernel.h>
51#include <linux/module.h>
52#include <linux/init.h>
53#include <linux/slab.h>
54#include <linux/input.h>
55#include <linux/usb.h>
56#include <linux/smp_lock.h>
57#include <linux/fcntl.h>
58#include <linux/compiler.h>
59#include "comedi_fc.h"
60#include "../comedidev.h"
61#include "../usb.h"
62
63// (un)comment this if you want to have debug info.
64//#define CONFIG_COMEDI_DEBUG
65#undef CONFIG_COMEDI_DEBUG
66
67#define BOARDNAME "usbduxfast"
68
69// timeout for the USB-transfer
70#define EZTIMEOUT 30
71
72// constants for "firmware" upload and download
73#define USBDUXFASTSUB_FIRMWARE 0xA0
74#define VENDOR_DIR_IN 0xC0
75#define VENDOR_DIR_OUT 0x40
76
77// internal adresses of the 8051 processor
78#define USBDUXFASTSUB_CPUCS 0xE600
79
80// max lenghth of the transfer-buffer for software upload
81#define TB_LEN 0x2000
82
83// Input endpoint number
84#define BULKINEP 6
85
86// Endpoint for the A/D channellist: bulk OUT
87#define CHANNELLISTEP 4
88
89// Number of channels
90#define NUMCHANNELS 32
91
92// size of the waveform descriptor
93#define WAVESIZE 0x20
94
95// Size of one A/D value
96#define SIZEADIN ((sizeof(int16_t)))
97
98// Size of the input-buffer IN BYTES
99#define SIZEINBUF 512
100
101// 16 bytes.
102#define SIZEINSNBUF 512
103
104// Size of the buffer for the dux commands
105#define SIZEOFDUXBUFFER 256 // bytes
106
107// Number of in-URBs which receive the data: min=5
108#define NUMOFINBUFFERSHIGH 10
109
110// Total number of usbduxfast devices
111#define NUMUSBDUXFAST 16
112
113// Number of subdevices
114#define N_SUBDEVICES 1
115
116// Analogue in subdevice
117#define SUBDEV_AD 0
118
119// min delay steps for more than one channel
120// basically when the mux gives up. ;-)
121#define MIN_SAMPLING_PERIOD 9 // steps at 30MHz in the FX2
122
123// Max number of 1/30MHz delay steps:
124#define MAX_SAMPLING_PERIOD 500
125
126// Number of received packets to ignore before we start handing data over to comedi.
127// It's quad buffering and we have to ignore 4 packets.
128#define PACKETS_TO_IGNORE 4
129
130/////////////////////////////////////////////
131// comedi constants
132static const comedi_lrange range_usbduxfast_ai_range = { 2, {
133 BIP_RANGE(0.75),
134 BIP_RANGE(0.5),
135 }
136};
137
138/*
139 * private structure of one subdevice
140 */
141
142// This is the structure which holds all the data of this driver
143// one sub device just now: A/D
144typedef struct {
145 // attached?
146 int attached;
147 // is it associated with a subdevice?
148 int probed;
149 // pointer to the usb-device
150 struct usb_device *usbdev;
151 // BULK-transfer handling: urb
152 struct urb *urbIn;
153 int8_t *transfer_buffer;
154 // input buffer for single insn
155 int16_t *insnBuffer;
156 // interface number
157 int ifnum;
158#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
159 // interface structure in 2.6
160 struct usb_interface *interface;
161#endif
162 // comedi device for the interrupt context
163 comedi_device *comedidev;
164 // asynchronous command is running
165 short int ai_cmd_running;
166 // continous aquisition
167 short int ai_continous;
168 // number of samples to aquire
169 long int ai_sample_count;
170 // commands
171 uint8_t *dux_commands;
172 // counter which ignores the first buffers
173 int ignore;
174 struct semaphore sem;
175} usbduxfastsub_t;
176
177// The pointer to the private usb-data of the driver
178// is also the private data for the comedi-device.
179// This has to be global as the usb subsystem needs
180// global variables. The other reason is that this
181// structure must be there _before_ any comedi
182// command is issued. The usb subsystem must be
183// initialised before comedi can access it.
184static usbduxfastsub_t usbduxfastsub[NUMUSBDUXFAST];
185
186static DECLARE_MUTEX(start_stop_sem);
187
188// bulk transfers to usbduxfast
189
190#define SENDADCOMMANDS 0
191#define SENDINITEP6 1
192
193static int send_dux_commands(usbduxfastsub_t * this_usbduxfastsub, int cmd_type)
194{
195 int result, nsent;
196 this_usbduxfastsub->dux_commands[0] = cmd_type;
197#ifdef CONFIG_COMEDI_DEBUG
198 int i;
199 printk("comedi%d: usbduxfast: dux_commands: ",
200 this_usbduxfastsub->comedidev->minor);
201 for (i = 0; i < SIZEOFDUXBUFFER; i++) {
202 printk(" %02x", this_usbduxfastsub->dux_commands[i]);
203 }
204 printk("\n");
205#endif
206 result = USB_BULK_MSG(this_usbduxfastsub->usbdev,
207 usb_sndbulkpipe(this_usbduxfastsub->usbdev,
208 CHANNELLISTEP),
209 this_usbduxfastsub->dux_commands,
210 SIZEOFDUXBUFFER, &nsent, 10000);
211 if (result < 0) {
212 printk("comedi%d: could not transmit dux_commands to the usb-device, err=%d\n", this_usbduxfastsub->comedidev->minor, result);
213 }
214 return result;
215}
216
217// Stops the data acquision
218// It should be safe to call this function from any context
219static int usbduxfastsub_unlink_InURBs(usbduxfastsub_t * usbduxfastsub_tmp)
220{
221 int j = 0;
222 int err = 0;
223
224 if (usbduxfastsub_tmp && usbduxfastsub_tmp->urbIn) {
225 usbduxfastsub_tmp->ai_cmd_running = 0;
226#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
227 j = usb_unlink_urb(usbduxfastsub_tmp->urbIn);
228 if (j < 0) {
229 err = j;
230 }
231#else
232 // waits until a running transfer is over
233 usb_kill_urb(usbduxfastsub_tmp->urbIn);
234 j = 0;
235#endif
236 }
237#ifdef CONFIG_COMEDI_DEBUG
238 printk("comedi: usbduxfast: unlinked InURB: res=%d\n", j);
239#endif
240 return err;
241}
242
243/* This will stop a running acquisition operation */
244// Is called from within this driver from both the
245// interrupt context and from comedi
246static int usbduxfast_ai_stop(usbduxfastsub_t * this_usbduxfastsub,
247 int do_unlink)
248{
249 int ret = 0;
250
251 if (!this_usbduxfastsub) {
252 printk("comedi?: usbduxfast_ai_stop: this_usbduxfastsub=NULL!\n");
253 return -EFAULT;
254 }
255#ifdef CONFIG_COMEDI_DEBUG
256 printk("comedi: usbduxfast_ai_stop\n");
257#endif
258
259 this_usbduxfastsub->ai_cmd_running = 0;
260
261 if (do_unlink) {
262 // stop aquistion
263 ret = usbduxfastsub_unlink_InURBs(this_usbduxfastsub);
264 }
265
266 return ret;
267}
268
269// This will cancel a running acquisition operation.
270// This is called by comedi but never from inside the
271// driver.
272static int usbduxfast_ai_cancel(comedi_device * dev, comedi_subdevice * s)
273{
274 usbduxfastsub_t *this_usbduxfastsub;
275 int res = 0;
276
277 // force unlink of all urbs
278#ifdef CONFIG_COMEDI_DEBUG
279 printk("comedi: usbduxfast_ai_cancel\n");
280#endif
281 this_usbduxfastsub = dev->private;
282 if (!this_usbduxfastsub) {
283 printk("comedi: usbduxfast_ai_cancel: this_usbduxfastsub=NULL\n");
284 return -EFAULT;
285 }
286 down(&this_usbduxfastsub->sem);
287 if (!(this_usbduxfastsub->probed)) {
288 up(&this_usbduxfastsub->sem);
289 return -ENODEV;
290 }
291 // unlink
292 res = usbduxfast_ai_stop(this_usbduxfastsub, 1);
293 up(&this_usbduxfastsub->sem);
294
295 return res;
296}
297
298// analogue IN
299// interrupt service routine
300#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
301static void usbduxfastsub_ai_Irq(struct urb *urb)
302#else
303static void usbduxfastsub_ai_Irq(struct urb *urb PT_REGS_ARG)
304#endif
305{
306 int n, err;
307 usbduxfastsub_t *this_usbduxfastsub;
308 comedi_device *this_comedidev;
309 comedi_subdevice *s;
310 uint16_t *p;
311
312 // sanity checks
313 // is the urb there?
314 if (!urb) {
315 printk("comedi_: usbduxfast_: ao int-handler called with urb=NULL!\n");
316 return;
317 }
318 // the context variable points to the subdevice
319 this_comedidev = urb->context;
320 if (!this_comedidev) {
321 printk("comedi_: usbduxfast_: urb context is a NULL pointer!\n");
322 return;
323 }
324 // the private structure of the subdevice is usbduxfastsub_t
325 this_usbduxfastsub = this_comedidev->private;
326 if (!this_usbduxfastsub) {
327 printk("comedi_: usbduxfast_: private of comedi subdev is a NULL pointer!\n");
328 return;
329 }
330 // are we running a command?
331 if (unlikely(!(this_usbduxfastsub->ai_cmd_running))) {
332 // not running a command
333 // do not continue execution if no asynchronous command is running
334 // in particular not resubmit
335 return;
336 }
337
338 if (unlikely(!(this_usbduxfastsub->attached))) {
339 // no comedi device there
340 return;
341 }
342 // subdevice which is the AD converter
343 s = this_comedidev->subdevices + SUBDEV_AD;
344
345 // first we test if something unusual has just happened
346 switch (urb->status) {
347 case 0:
348 break;
349
350 // happens after an unlink command or when the device is plugged out
351 case -ECONNRESET:
352 case -ENOENT:
353 case -ESHUTDOWN:
354 case -ECONNABORTED:
355 // tell this comedi
356 s->async->events |= COMEDI_CB_EOA;
357 s->async->events |= COMEDI_CB_ERROR;
358 comedi_event(this_usbduxfastsub->comedidev, s);
359 // stop the transfer w/o unlink
360 usbduxfast_ai_stop(this_usbduxfastsub, 0);
361 return;
362
363 default:
364 printk("comedi%d: usbduxfast: non-zero urb status received in ai intr context: %d\n", this_usbduxfastsub->comedidev->minor, urb->status);
365 s->async->events |= COMEDI_CB_EOA;
366 s->async->events |= COMEDI_CB_ERROR;
367 comedi_event(this_usbduxfastsub->comedidev, s);
368 usbduxfast_ai_stop(this_usbduxfastsub, 0);
369 return;
370 }
371
372 p = urb->transfer_buffer;
373 if (!this_usbduxfastsub->ignore) {
374 if (!(this_usbduxfastsub->ai_continous)) {
375 // not continous, fixed number of samples
376 n = urb->actual_length / sizeof(uint16_t);
377 if (unlikely(this_usbduxfastsub->ai_sample_count < n)) {
378 // we have send only a fraction of the bytes received
379 cfc_write_array_to_buffer(s,
380 urb->transfer_buffer,
381 this_usbduxfastsub->ai_sample_count *
382 sizeof(uint16_t));
383 usbduxfast_ai_stop(this_usbduxfastsub, 0);
384 // say comedi that the acquistion is over
385 s->async->events |= COMEDI_CB_EOA;
386 comedi_event(this_usbduxfastsub->comedidev, s);
387 return;
388 }
389 this_usbduxfastsub->ai_sample_count -= n;
390 }
391 // write the full buffer to comedi
392 cfc_write_array_to_buffer(s,
393 urb->transfer_buffer, urb->actual_length);
394
395 // tell comedi that data is there
396 comedi_event(this_usbduxfastsub->comedidev, s);
397
398 } else {
399 // ignore this packet
400 this_usbduxfastsub->ignore--;
401 }
402
403 // command is still running
404 // resubmit urb for BULK transfer
405 urb->dev = this_usbduxfastsub->usbdev;
406 urb->status = 0;
407 if ((err = USB_SUBMIT_URB(urb)) < 0) {
408 printk("comedi%d: usbduxfast: urb resubm failed: %d",
409 this_usbduxfastsub->comedidev->minor, err);
410 s->async->events |= COMEDI_CB_EOA;
411 s->async->events |= COMEDI_CB_ERROR;
412 comedi_event(this_usbduxfastsub->comedidev, s);
413 usbduxfast_ai_stop(this_usbduxfastsub, 0);
414 }
415}
416
417static int usbduxfastsub_start(usbduxfastsub_t * usbduxfastsub)
418{
419 int errcode = 0;
420 unsigned char local_transfer_buffer[16];
421
422 if (usbduxfastsub->probed) {
423 // 7f92 to zero
424 local_transfer_buffer[0] = 0;
425 errcode = USB_CONTROL_MSG(usbduxfastsub->usbdev,
426 // create a pipe for a control transfer
427 usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
428 // bRequest, "Firmware"
429 USBDUXFASTSUB_FIRMWARE,
430 // bmRequestType
431 VENDOR_DIR_OUT,
432 // Value
433 USBDUXFASTSUB_CPUCS,
434 // Index
435 0x0000,
436 // address of the transfer buffer
437 local_transfer_buffer,
438 // Length
439 1,
440 // Timeout
441 EZTIMEOUT);
442 if (errcode < 0) {
443 printk("comedi_: usbduxfast_: control msg failed (start)\n");
444 return errcode;
445 }
446 }
447 return 0;
448}
449
450static int usbduxfastsub_stop(usbduxfastsub_t * usbduxfastsub)
451{
452 int errcode = 0;
453
454 unsigned char local_transfer_buffer[16];
455 if (usbduxfastsub->probed) {
456 // 7f92 to one
457 local_transfer_buffer[0] = 1;
458 errcode = USB_CONTROL_MSG
459 (usbduxfastsub->usbdev,
460 usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
461 // bRequest, "Firmware"
462 USBDUXFASTSUB_FIRMWARE,
463 // bmRequestType
464 VENDOR_DIR_OUT,
465 // Value
466 USBDUXFASTSUB_CPUCS,
467 // Index
468 0x0000, local_transfer_buffer,
469 // Length
470 1,
471 // Timeout
472 EZTIMEOUT);
473 if (errcode < 0) {
474 printk("comedi_: usbduxfast: control msg failed (stop)\n");
475 return errcode;
476 }
477 }
478 return 0;
479}
480
481static int usbduxfastsub_upload(usbduxfastsub_t * usbduxfastsub,
482 unsigned char *local_transfer_buffer,
483 unsigned int startAddr, unsigned int len)
484{
485 int errcode;
486
487 if (usbduxfastsub->probed) {
488#ifdef CONFIG_COMEDI_DEBUG
489 printk("comedi%d: usbduxfast: uploading %d bytes",
490 usbduxfastsub->comedidev->minor, len);
491 printk(" to addr %d, first byte=%d.\n",
492 startAddr, local_transfer_buffer[0]);
493#endif
494 errcode = USB_CONTROL_MSG
495 (usbduxfastsub->usbdev,
496 usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
497 // brequest, firmware
498 USBDUXFASTSUB_FIRMWARE,
499 // bmRequestType
500 VENDOR_DIR_OUT,
501 // value
502 startAddr,
503 // index
504 0x0000,
505 // our local safe buffer
506 local_transfer_buffer,
507 // length
508 len,
509 // timeout
510 EZTIMEOUT);
511#ifdef CONFIG_COMEDI_DEBUG
512 printk("comedi_: usbduxfast: result=%d\n", errcode);
513#endif
514 if (errcode < 0) {
515 printk("comedi_: usbduxfast: uppload failed\n");
516 return errcode;
517 }
518 } else {
519 // no device on the bus for this index
520 return -EFAULT;
521 }
522 return 0;
523}
524
525int firmwareUpload(usbduxfastsub_t * usbduxfastsub,
526 unsigned char *firmwareBinary, int sizeFirmware)
527{
528 int ret;
529
530 if (!firmwareBinary) {
531 return 0;
532 }
533 ret = usbduxfastsub_stop(usbduxfastsub);
534 if (ret < 0) {
535 printk("comedi_: usbduxfast: can not stop firmware\n");
536 return ret;
537 }
538 ret = usbduxfastsub_upload(usbduxfastsub,
539 firmwareBinary, 0, sizeFirmware);
540 if (ret < 0) {
541 printk("comedi_: usbduxfast: firmware upload failed\n");
542 return ret;
543 }
544 ret = usbduxfastsub_start(usbduxfastsub);
545 if (ret < 0) {
546 printk("comedi_: usbduxfast: can not start firmware\n");
547 return ret;
548 }
549 return 0;
550}
551
552int usbduxfastsub_submit_InURBs(usbduxfastsub_t * usbduxfastsub)
553{
554 int errFlag;
555
556 if (!usbduxfastsub) {
557 return -EFAULT;
558 }
559 usb_fill_bulk_urb(usbduxfastsub->urbIn,
560 usbduxfastsub->usbdev,
561 usb_rcvbulkpipe(usbduxfastsub->usbdev, BULKINEP),
562 usbduxfastsub->transfer_buffer,
563 SIZEINBUF, usbduxfastsub_ai_Irq, usbduxfastsub->comedidev);
564
565#ifdef CONFIG_COMEDI_DEBUG
566 printk("comedi%d: usbduxfast: submitting in-urb: %x,%x\n",
567 usbduxfastsub->comedidev->minor,
568 (int)(usbduxfastsub->urbIn->context),
569 (int)(usbduxfastsub->urbIn->dev));
570#endif
571 errFlag = USB_SUBMIT_URB(usbduxfastsub->urbIn);
572 if (errFlag) {
573 printk("comedi_: usbduxfast: ai: ");
574 printk("USB_SUBMIT_URB");
575 printk(" error %d\n", errFlag);
576 return errFlag;
577 }
578 return 0;
579}
580
581static int usbduxfast_ai_cmdtest(comedi_device * dev,
582 comedi_subdevice * s, comedi_cmd * cmd)
583{
584 int err = 0, stop_mask = 0;
585 long int steps, tmp = 0;
586 int minSamplPer;
587 usbduxfastsub_t *this_usbduxfastsub = dev->private;
588 if (!(this_usbduxfastsub->probed)) {
589 return -ENODEV;
590 }
591#ifdef CONFIG_COMEDI_DEBUG
592 printk("comedi%d: usbduxfast_ai_cmdtest\n", dev->minor);
593 printk("comedi%d: usbduxfast: convert_arg=%u scan_begin_arg=%u\n",
594 dev->minor, cmd->convert_arg, cmd->scan_begin_arg);
595#endif
596 /* step 1: make sure trigger sources are trivially valid */
597
598 tmp = cmd->start_src;
599 cmd->start_src &= TRIG_NOW | TRIG_EXT | TRIG_INT;
600 if (!cmd->start_src || tmp != cmd->start_src)
601 err++;
602
603 tmp = cmd->scan_begin_src;
604 cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT;
605 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
606 err++;
607
608 tmp = cmd->convert_src;
609 cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
610 if (!cmd->convert_src || tmp != cmd->convert_src)
611 err++;
612
613 tmp = cmd->scan_end_src;
614 cmd->scan_end_src &= TRIG_COUNT;
615 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
616 err++;
617
618 tmp = cmd->stop_src;
619 stop_mask = TRIG_COUNT | TRIG_NONE;
620 cmd->stop_src &= stop_mask;
621 if (!cmd->stop_src || tmp != cmd->stop_src)
622 err++;
623
624 if (err)
625 return 1;
626
627 /* step 2: make sure trigger sources are unique and mutually compatible */
628
629 if (cmd->start_src != TRIG_NOW &&
630 cmd->start_src != TRIG_EXT && cmd->start_src != TRIG_INT)
631 err++;
632 if (cmd->scan_begin_src != TRIG_TIMER &&
633 cmd->scan_begin_src != TRIG_FOLLOW &&
634 cmd->scan_begin_src != TRIG_EXT)
635 err++;
636 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
637 err++;
638 if (cmd->stop_src != TRIG_COUNT &&
639 cmd->stop_src != TRIG_EXT && cmd->stop_src != TRIG_NONE)
640 err++;
641
642 // can't have external stop and start triggers at once
643 if (cmd->start_src == TRIG_EXT && cmd->stop_src == TRIG_EXT)
644 err++;
645
646 if (err)
647 return 2;
648
649 /* step 3: make sure arguments are trivially compatible */
650
651 if (cmd->start_src == TRIG_NOW && cmd->start_arg != 0) {
652 cmd->start_arg = 0;
653 err++;
654 }
655
656 if (!cmd->chanlist_len) {
657 err++;
658 }
659 if (cmd->scan_end_arg != cmd->chanlist_len) {
660 cmd->scan_end_arg = cmd->chanlist_len;
661 err++;
662 }
663
664 if (cmd->chanlist_len == 1) {
665 minSamplPer = 1;
666 } else {
667 minSamplPer = MIN_SAMPLING_PERIOD;
668 }
669
670 if (cmd->convert_src == TRIG_TIMER) {
671 steps = cmd->convert_arg * 30;
672 if (steps < (minSamplPer * 1000)) {
673 steps = minSamplPer * 1000;
674 }
675 if (steps > (MAX_SAMPLING_PERIOD * 1000)) {
676 steps = MAX_SAMPLING_PERIOD * 1000;
677 }
678 // calc arg again
679 tmp = steps / 30;
680 if (cmd->convert_arg != tmp) {
681 cmd->convert_arg = tmp;
682 err++;
683 }
684 }
685
686 if (cmd->scan_begin_src == TRIG_TIMER) {
687 err++;
688 }
689 // stop source
690 switch (cmd->stop_src) {
691 case TRIG_COUNT:
692 if (!cmd->stop_arg) {
693 cmd->stop_arg = 1;
694 err++;
695 }
696 break;
697 case TRIG_NONE:
698 if (cmd->stop_arg != 0) {
699 cmd->stop_arg = 0;
700 err++;
701 }
702 break;
703 // TRIG_EXT doesn't care since it doesn't trigger off a numbered channel
704 default:
705 break;
706 }
707
708 if (err)
709 return 3;
710
711 /* step 4: fix up any arguments */
712
713 return 0;
714
715}
716
717static int usbduxfast_ai_inttrig(comedi_device * dev,
718 comedi_subdevice * s, unsigned int trignum)
719{
720 int ret;
721 usbduxfastsub_t *this_usbduxfastsub = dev->private;
722 if (!this_usbduxfastsub) {
723 return -EFAULT;
724 }
725 down(&this_usbduxfastsub->sem);
726 if (!(this_usbduxfastsub->probed)) {
727 up(&this_usbduxfastsub->sem);
728 return -ENODEV;
729 }
730#ifdef CONFIG_COMEDI_DEBUG
731 printk("comedi%d: usbduxfast_ai_inttrig\n", dev->minor);
732#endif
733
734 if (trignum != 0) {
735 printk("comedi%d: usbduxfast_ai_inttrig: invalid trignum\n",
736 dev->minor);
737 up(&this_usbduxfastsub->sem);
738 return -EINVAL;
739 }
740 if (!(this_usbduxfastsub->ai_cmd_running)) {
741 this_usbduxfastsub->ai_cmd_running = 1;
742 ret = usbduxfastsub_submit_InURBs(this_usbduxfastsub);
743 if (ret < 0) {
744 printk("comedi%d: usbduxfast_ai_inttrig: urbSubmit: err=%d\n", dev->minor, ret);
745 this_usbduxfastsub->ai_cmd_running = 0;
746 up(&this_usbduxfastsub->sem);
747 return ret;
748 }
749 s->async->inttrig = NULL;
750 } else {
751 printk("comedi%d: ai_inttrig but acqu is already running\n",
752 dev->minor);
753 }
754 up(&this_usbduxfastsub->sem);
755 return 1;
756}
757
758// offsets for the GPIF bytes
759// the first byte is the command byte
760#define LENBASE 1+0x00
761#define OPBASE 1+0x08
762#define OUTBASE 1+0x10
763#define LOGBASE 1+0x18
764
765static int usbduxfast_ai_cmd(comedi_device * dev, comedi_subdevice * s)
766{
767 comedi_cmd *cmd = &s->async->cmd;
768 unsigned int chan, gain, rngmask = 0xff;
769 int i, j, ret;
770 usbduxfastsub_t *this_usbduxfastsub = dev->private;
771 int result;
772 long steps, steps_tmp;
773
774#ifdef CONFIG_COMEDI_DEBUG
775 printk("comedi%d: usbduxfast_ai_cmd\n", dev->minor);
776#endif
777 if (!this_usbduxfastsub) {
778 return -EFAULT;
779 }
780 down(&this_usbduxfastsub->sem);
781 if (!(this_usbduxfastsub->probed)) {
782 up(&this_usbduxfastsub->sem);
783 return -ENODEV;
784 }
785 if (this_usbduxfastsub->ai_cmd_running) {
786 printk("comedi%d: ai_cmd not possible. Another ai_cmd is running.\n", dev->minor);
787 up(&this_usbduxfastsub->sem);
788 return -EBUSY;
789 }
790 // set current channel of the running aquisition to zero
791 s->async->cur_chan = 0;
792
793 // ignore the first buffers from the device if there is an error condition
794 this_usbduxfastsub->ignore = PACKETS_TO_IGNORE;
795
796 if (cmd->chanlist_len > 0) {
797 gain = CR_RANGE(cmd->chanlist[0]);
798 for (i = 0; i < cmd->chanlist_len; ++i) {
799 chan = CR_CHAN(cmd->chanlist[i]);
800 if (chan != i) {
801 printk("comedi%d: cmd is accepting only consecutive channels.\n", dev->minor);
802 up(&this_usbduxfastsub->sem);
803 return -EINVAL;
804 }
805 if ((gain != CR_RANGE(cmd->chanlist[i]))
806 && (cmd->chanlist_len > 3)) {
807 printk("comedi%d: the gain must be the same for all channels.\n", dev->minor);
808 up(&this_usbduxfastsub->sem);
809 return -EINVAL;
810 }
811 if (i >= NUMCHANNELS) {
812 printk("comedi%d: channel list too long\n",
813 dev->minor);
814 break;
815 }
816 }
817 }
818 steps = 0;
819 if (cmd->scan_begin_src == TRIG_TIMER) {
820 printk("comedi%d: usbduxfast: scan_begin_src==TRIG_TIMER not valid.\n", dev->minor);
821 up(&this_usbduxfastsub->sem);
822 return -EINVAL;
823 }
824 if (cmd->convert_src == TRIG_TIMER) {
825 steps = (cmd->convert_arg * 30) / 1000;
826 }
827 if ((steps < MIN_SAMPLING_PERIOD) && (cmd->chanlist_len != 1)) {
828 printk("comedi%d: usbduxfast: ai_cmd: steps=%ld, scan_begin_arg=%d. Not properly tested by cmdtest?\n", dev->minor, steps, cmd->scan_begin_arg);
829 up(&this_usbduxfastsub->sem);
830 return -EINVAL;
831 }
832 if (steps > MAX_SAMPLING_PERIOD) {
833 printk("comedi%d: usbduxfast: ai_cmd: sampling rate too low.\n",
834 dev->minor);
835 up(&this_usbduxfastsub->sem);
836 return -EINVAL;
837 }
838 if ((cmd->start_src == TRIG_EXT) && (cmd->chanlist_len != 1)
839 && (cmd->chanlist_len != 16)) {
840 printk("comedi%d: usbduxfast: ai_cmd: TRIG_EXT only with 1 or 16 channels possible.\n", dev->minor);
841 up(&this_usbduxfastsub->sem);
842 return -EINVAL;
843 }
844#ifdef CONFIG_COMEDI_DEBUG
845 printk("comedi%d: usbduxfast: steps=%ld, convert_arg=%u, ai_timer=%u\n",
846 dev->minor,
847 steps, cmd->convert_arg, this_usbduxfastsub->ai_timer);
848#endif
849
850 switch (cmd->chanlist_len) {
851 // one channel
852 case 1:
853 if (CR_RANGE(cmd->chanlist[0]) > 0)
854 rngmask = 0xff - 0x04;
855 else
856 rngmask = 0xff;
857
858 // for external trigger: looping in this state until the RDY0 pin
859 // becomes zero
860 if (cmd->start_src == TRIG_EXT) { // we loop here until ready has been set
861 this_usbduxfastsub->dux_commands[LENBASE + 0] = 0x01; // branch back to state 0
862 this_usbduxfastsub->dux_commands[OPBASE + 0] = 0x01; // deceision state w/o data
863 this_usbduxfastsub->dux_commands[OUTBASE + 0] =
864 0xFF & rngmask;
865 this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0x00; // RDY0 = 0
866 } else { // we just proceed to state 1
867 this_usbduxfastsub->dux_commands[LENBASE + 0] = 1;
868 this_usbduxfastsub->dux_commands[OPBASE + 0] = 0;
869 this_usbduxfastsub->dux_commands[OUTBASE + 0] =
870 0xFF & rngmask;
871 this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
872 }
873
874 if (steps < MIN_SAMPLING_PERIOD) {
875 // for fast single channel aqu without mux
876 if (steps <= 1) {
877 // we just stay here at state 1 and rexecute the same state
878 // this gives us 30MHz sampling rate
879 this_usbduxfastsub->dux_commands[LENBASE + 1] = 0x89; // branch back to state 1
880 this_usbduxfastsub->dux_commands[OPBASE + 1] = 0x03; // deceision state with data
881 this_usbduxfastsub->dux_commands[OUTBASE + 1] =
882 0xFF & rngmask;
883 this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0xFF; // doesn't matter
884 } else {
885 // we loop through two states: data and delay: max rate is 15Mhz
886 this_usbduxfastsub->dux_commands[LENBASE + 1] =
887 steps - 1;
888 this_usbduxfastsub->dux_commands[OPBASE + 1] = 0x02; // data
889 this_usbduxfastsub->dux_commands[OUTBASE + 1] =
890 0xFF & rngmask;
891 this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0; // doesn't matter
892
893 this_usbduxfastsub->dux_commands[LENBASE + 2] = 0x09; // branch back to state 1
894 this_usbduxfastsub->dux_commands[OPBASE + 2] = 0x01; // deceision state w/o data
895 this_usbduxfastsub->dux_commands[OUTBASE + 2] =
896 0xFF & rngmask;
897 this_usbduxfastsub->dux_commands[LOGBASE + 2] = 0xFF; // doesn't matter
898 }
899 } else {
900 // we loop through 3 states: 2x delay and 1x data. This gives a min
901 // sampling rate of 60kHz.
902
903 // we have 1 state with duration 1
904 steps = steps - 1;
905
906 // do the first part of the delay
907 this_usbduxfastsub->dux_commands[LENBASE + 1] =
908 steps / 2;
909 this_usbduxfastsub->dux_commands[OPBASE + 1] = 0;
910 this_usbduxfastsub->dux_commands[OUTBASE + 1] =
911 0xFF & rngmask;
912 this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0;
913
914 // and the second part
915 this_usbduxfastsub->dux_commands[LENBASE + 2] =
916 steps - steps / 2;
917 this_usbduxfastsub->dux_commands[OPBASE + 2] = 0;
918 this_usbduxfastsub->dux_commands[OUTBASE + 2] =
919 0xFF & rngmask;
920 this_usbduxfastsub->dux_commands[LOGBASE + 2] = 0;
921
922 // get the data and branch back
923 this_usbduxfastsub->dux_commands[LENBASE + 3] = 0x09; // branch back to state 1
924 this_usbduxfastsub->dux_commands[OPBASE + 3] = 0x03; // deceision state w data
925 this_usbduxfastsub->dux_commands[OUTBASE + 3] =
926 0xFF & rngmask;
927 this_usbduxfastsub->dux_commands[LOGBASE + 3] = 0xFF; // doesn't matter
928 }
929 break;
930
931 case 2:
932 // two channels
933 // commit data to the FIFO
934 if (CR_RANGE(cmd->chanlist[0]) > 0)
935 rngmask = 0xff - 0x04;
936 else
937 rngmask = 0xff;
938 this_usbduxfastsub->dux_commands[LENBASE + 0] = 1;
939 this_usbduxfastsub->dux_commands[OPBASE + 0] = 0x02; // data
940 this_usbduxfastsub->dux_commands[OUTBASE + 0] = 0xFF & rngmask;
941 this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
942
943 // we have 1 state with duration 1: state 0
944 steps_tmp = steps - 1;
945
946 if (CR_RANGE(cmd->chanlist[1]) > 0)
947 rngmask = 0xff - 0x04;
948 else
949 rngmask = 0xff;
950 // do the first part of the delay
951 this_usbduxfastsub->dux_commands[LENBASE + 1] = steps_tmp / 2;
952 this_usbduxfastsub->dux_commands[OPBASE + 1] = 0;
953 this_usbduxfastsub->dux_commands[OUTBASE + 1] = 0xFE & rngmask; //count
954 this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0;
955
956 // and the second part
957 this_usbduxfastsub->dux_commands[LENBASE + 2] =
958 steps_tmp - steps_tmp / 2;
959 this_usbduxfastsub->dux_commands[OPBASE + 2] = 0;
960 this_usbduxfastsub->dux_commands[OUTBASE + 2] = 0xFF & rngmask;
961 this_usbduxfastsub->dux_commands[LOGBASE + 2] = 0;
962
963 this_usbduxfastsub->dux_commands[LENBASE + 3] = 1;
964 this_usbduxfastsub->dux_commands[OPBASE + 3] = 0x02; // data
965 this_usbduxfastsub->dux_commands[OUTBASE + 3] = 0xFF & rngmask;
966 this_usbduxfastsub->dux_commands[LOGBASE + 3] = 0;
967
968 // we have 2 states with duration 1: step 6 and the IDLE state
969 steps_tmp = steps - 2;
970
971 if (CR_RANGE(cmd->chanlist[0]) > 0)
972 rngmask = 0xff - 0x04;
973 else
974 rngmask = 0xff;
975 // do the first part of the delay
976 this_usbduxfastsub->dux_commands[LENBASE + 4] = steps_tmp / 2;
977 this_usbduxfastsub->dux_commands[OPBASE + 4] = 0;
978 this_usbduxfastsub->dux_commands[OUTBASE + 4] = (0xFF - 0x02) & rngmask; //reset
979 this_usbduxfastsub->dux_commands[LOGBASE + 4] = 0;
980
981 // and the second part
982 this_usbduxfastsub->dux_commands[LENBASE + 5] =
983 steps_tmp - steps_tmp / 2;
984 this_usbduxfastsub->dux_commands[OPBASE + 5] = 0;
985 this_usbduxfastsub->dux_commands[OUTBASE + 5] = 0xFF & rngmask;
986 this_usbduxfastsub->dux_commands[LOGBASE + 5] = 0;
987
988 this_usbduxfastsub->dux_commands[LENBASE + 6] = 1;
989 this_usbduxfastsub->dux_commands[OPBASE + 6] = 0;
990 this_usbduxfastsub->dux_commands[OUTBASE + 6] = 0xFF & rngmask;
991 this_usbduxfastsub->dux_commands[LOGBASE + 6] = 0;
992 break;
993
994 case 3:
995 // three channels
996 for (j = 0; j < 1; j++) {
997 if (CR_RANGE(cmd->chanlist[j]) > 0)
998 rngmask = 0xff - 0x04;
999 else
1000 rngmask = 0xff;
1001 // commit data to the FIFO and do the first part of the delay
1002 this_usbduxfastsub->dux_commands[LENBASE + j * 2] =
1003 steps / 2;
1004 this_usbduxfastsub->dux_commands[OPBASE + j * 2] = 0x02; // data
1005 this_usbduxfastsub->dux_commands[OUTBASE + j * 2] = 0xFF & rngmask; // no change
1006 this_usbduxfastsub->dux_commands[LOGBASE + j * 2] = 0;
1007
1008 if (CR_RANGE(cmd->chanlist[j + 1]) > 0)
1009 rngmask = 0xff - 0x04;
1010 else
1011 rngmask = 0xff;
1012 // do the second part of the delay
1013 this_usbduxfastsub->dux_commands[LENBASE + j * 2 + 1] =
1014 steps - steps / 2;
1015 this_usbduxfastsub->dux_commands[OPBASE + j * 2 + 1] = 0; // no data
1016 this_usbduxfastsub->dux_commands[OUTBASE + j * 2 + 1] = 0xFE & rngmask; //count
1017 this_usbduxfastsub->dux_commands[LOGBASE + j * 2 + 1] =
1018 0;
1019 }
1020
1021 // 2 steps with duration 1: the idele step and step 6:
1022 steps_tmp = steps - 2;
1023 // commit data to the FIFO and do the first part of the delay
1024 this_usbduxfastsub->dux_commands[LENBASE + 4] = steps_tmp / 2;
1025 this_usbduxfastsub->dux_commands[OPBASE + 4] = 0x02; // data
1026 this_usbduxfastsub->dux_commands[OUTBASE + 4] = 0xFF & rngmask; // no change
1027 this_usbduxfastsub->dux_commands[LOGBASE + 4] = 0;
1028
1029 if (CR_RANGE(cmd->chanlist[0]) > 0)
1030 rngmask = 0xff - 0x04;
1031 else
1032 rngmask = 0xff;
1033 // do the second part of the delay
1034 this_usbduxfastsub->dux_commands[LENBASE + 5] =
1035 steps_tmp - steps_tmp / 2;
1036 this_usbduxfastsub->dux_commands[OPBASE + 5] = 0; // no data
1037 this_usbduxfastsub->dux_commands[OUTBASE + 5] = (0xFF - 0x02) & rngmask; // reset
1038 this_usbduxfastsub->dux_commands[LOGBASE + 5] = 0;
1039
1040 this_usbduxfastsub->dux_commands[LENBASE + 6] = 1;
1041 this_usbduxfastsub->dux_commands[OPBASE + 6] = 0;
1042 this_usbduxfastsub->dux_commands[OUTBASE + 6] = 0xFF & rngmask;
1043 this_usbduxfastsub->dux_commands[LOGBASE + 6] = 0;
1044
1045 case 16:
1046 if (CR_RANGE(cmd->chanlist[0]) > 0)
1047 rngmask = 0xff - 0x04;
1048 else
1049 rngmask = 0xff;
1050 if (cmd->start_src == TRIG_EXT) { // we loop here until ready has been set
1051 this_usbduxfastsub->dux_commands[LENBASE + 0] = 0x01; // branch back to state 0
1052 this_usbduxfastsub->dux_commands[OPBASE + 0] = 0x01; // deceision state w/o data
1053 this_usbduxfastsub->dux_commands[OUTBASE + 0] = (0xFF - 0x02) & rngmask; // reset
1054 this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0x00; // RDY0 = 0
1055 } else { // we just proceed to state 1
1056 this_usbduxfastsub->dux_commands[LENBASE + 0] = 255; // 30us reset pulse
1057 this_usbduxfastsub->dux_commands[OPBASE + 0] = 0;
1058 this_usbduxfastsub->dux_commands[OUTBASE + 0] = (0xFF - 0x02) & rngmask; // reset
1059 this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
1060 }
1061
1062 // commit data to the FIFO
1063 this_usbduxfastsub->dux_commands[LENBASE + 1] = 1;
1064 this_usbduxfastsub->dux_commands[OPBASE + 1] = 0x02; // data
1065 this_usbduxfastsub->dux_commands[OUTBASE + 1] = 0xFF & rngmask;
1066 this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0;
1067
1068 // we have 2 states with duration 1
1069 steps = steps - 2;
1070
1071 // do the first part of the delay
1072 this_usbduxfastsub->dux_commands[LENBASE + 2] = steps / 2;
1073 this_usbduxfastsub->dux_commands[OPBASE + 2] = 0;
1074 this_usbduxfastsub->dux_commands[OUTBASE + 2] = 0xFE & rngmask;
1075 this_usbduxfastsub->dux_commands[LOGBASE + 2] = 0;
1076
1077 // and the second part
1078 this_usbduxfastsub->dux_commands[LENBASE + 3] =
1079 steps - steps / 2;
1080 this_usbduxfastsub->dux_commands[OPBASE + 3] = 0;
1081 this_usbduxfastsub->dux_commands[OUTBASE + 3] = 0xFF & rngmask;
1082 this_usbduxfastsub->dux_commands[LOGBASE + 3] = 0;
1083
1084 this_usbduxfastsub->dux_commands[LENBASE + 4] = 0x09; // branch back to state 1
1085 this_usbduxfastsub->dux_commands[OPBASE + 4] = 0x01; // deceision state w/o data
1086 this_usbduxfastsub->dux_commands[OUTBASE + 4] = 0xFF & rngmask;
1087 this_usbduxfastsub->dux_commands[LOGBASE + 4] = 0xFF; // doesn't matter
1088
1089 break;
1090
1091 default:
1092 printk("comedi %d: unsupported combination of channels\n",
1093 dev->minor);
1094 up(&this_usbduxfastsub->sem);
1095 return -EFAULT;
1096 }
1097
1098#ifdef CONFIG_COMEDI_DEBUG
1099 printk("comedi %d: sending commands to the usb device\n", dev->minor);
1100#endif
1101 // 0 means that the AD commands are sent
1102 result = send_dux_commands(this_usbduxfastsub, SENDADCOMMANDS);
1103 if (result < 0) {
1104 printk("comedi%d: adc command could not be submitted. Aborting...\n", dev->minor);
1105 up(&this_usbduxfastsub->sem);
1106 return result;
1107 }
1108 if (cmd->stop_src == TRIG_COUNT) {
1109 this_usbduxfastsub->ai_sample_count =
1110 (cmd->stop_arg) * (cmd->scan_end_arg);
1111 if (usbduxfastsub->ai_sample_count < 1) {
1112 printk("comedi%d: (cmd->stop_arg)*(cmd->scan_end_arg)<1, aborting.\n", dev->minor);
1113 up(&this_usbduxfastsub->sem);
1114 return -EFAULT;
1115 }
1116 this_usbduxfastsub->ai_continous = 0;
1117 } else {
1118 // continous aquisition
1119 this_usbduxfastsub->ai_continous = 1;
1120 this_usbduxfastsub->ai_sample_count = 0;
1121 }
1122
1123 if ((cmd->start_src == TRIG_NOW) || (cmd->start_src == TRIG_EXT)) {
1124 // enable this acquisition operation
1125 this_usbduxfastsub->ai_cmd_running = 1;
1126 ret = usbduxfastsub_submit_InURBs(this_usbduxfastsub);
1127 if (ret < 0) {
1128 this_usbduxfastsub->ai_cmd_running = 0;
1129 // fixme: unlink here??
1130 up(&this_usbduxfastsub->sem);
1131 return ret;
1132 }
1133 s->async->inttrig = NULL;
1134 } else {
1135 /* TRIG_INT */
1136 // don't enable the acquision operation
1137 // wait for an internal signal
1138 s->async->inttrig = usbduxfast_ai_inttrig;
1139 }
1140 up(&this_usbduxfastsub->sem);
1141
1142 return 0;
1143}
1144
1145/* Mode 0 is used to get a single conversion on demand */
1146static int usbduxfast_ai_insn_read(comedi_device * dev,
1147 comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
1148{
1149 int i, j, n, actual_length;
1150 int chan, range, rngmask;
1151 int err;
1152 usbduxfastsub_t *usbduxfastsub = dev->private;
1153
1154 if (!usbduxfastsub) {
1155 printk("comedi%d: ai_insn_read: no usb dev.\n", dev->minor);
1156 return -ENODEV;
1157 }
1158#ifdef CONFIG_COMEDI_DEBUG
1159 printk("comedi%d: ai_insn_read, insn->n=%d, insn->subdev=%d\n",
1160 dev->minor, insn->n, insn->subdev);
1161#endif
1162 down(&usbduxfastsub->sem);
1163 if (!(usbduxfastsub->probed)) {
1164 up(&usbduxfastsub->sem);
1165 return -ENODEV;
1166 }
1167 if (usbduxfastsub->ai_cmd_running) {
1168 printk("comedi%d: ai_insn_read not possible. Async Command is running.\n", dev->minor);
1169 up(&usbduxfastsub->sem);
1170 return -EBUSY;
1171 }
1172 // sample one channel
1173 chan = CR_CHAN(insn->chanspec);
1174 range = CR_RANGE(insn->chanspec);
1175 // set command for the first channel
1176
1177 if (range > 0)
1178 rngmask = 0xff - 0x04;
1179 else
1180 rngmask = 0xff;
1181 // commit data to the FIFO
1182 usbduxfastsub->dux_commands[LENBASE + 0] = 1;
1183 usbduxfastsub->dux_commands[OPBASE + 0] = 0x02; // data
1184 usbduxfastsub->dux_commands[OUTBASE + 0] = 0xFF & rngmask;
1185 usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
1186
1187 // do the first part of the delay
1188 usbduxfastsub->dux_commands[LENBASE + 1] = 12;
1189 usbduxfastsub->dux_commands[OPBASE + 1] = 0;
1190 usbduxfastsub->dux_commands[OUTBASE + 1] = 0xFE & rngmask;
1191 usbduxfastsub->dux_commands[LOGBASE + 1] = 0;
1192
1193 usbduxfastsub->dux_commands[LENBASE + 2] = 1;
1194 usbduxfastsub->dux_commands[OPBASE + 2] = 0;
1195 usbduxfastsub->dux_commands[OUTBASE + 2] = 0xFE & rngmask;
1196 usbduxfastsub->dux_commands[LOGBASE + 2] = 0;
1197
1198 usbduxfastsub->dux_commands[LENBASE + 3] = 1;
1199 usbduxfastsub->dux_commands[OPBASE + 3] = 0;
1200 usbduxfastsub->dux_commands[OUTBASE + 3] = 0xFE & rngmask;
1201 usbduxfastsub->dux_commands[LOGBASE + 3] = 0;
1202
1203 usbduxfastsub->dux_commands[LENBASE + 4] = 1;
1204 usbduxfastsub->dux_commands[OPBASE + 4] = 0;
1205 usbduxfastsub->dux_commands[OUTBASE + 4] = 0xFE & rngmask;
1206 usbduxfastsub->dux_commands[LOGBASE + 4] = 0;
1207
1208 // second part
1209 usbduxfastsub->dux_commands[LENBASE + 5] = 12;
1210 usbduxfastsub->dux_commands[OPBASE + 5] = 0;
1211 usbduxfastsub->dux_commands[OUTBASE + 5] = 0xFF & rngmask;
1212 usbduxfastsub->dux_commands[LOGBASE + 5] = 0;
1213
1214 usbduxfastsub->dux_commands[LENBASE + 6] = 1;
1215 usbduxfastsub->dux_commands[OPBASE + 6] = 0;
1216 usbduxfastsub->dux_commands[OUTBASE + 6] = 0xFF & rngmask;
1217 usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
1218
1219#ifdef CONFIG_COMEDI_DEBUG
1220 printk("comedi %d: sending commands to the usb device\n", dev->minor);
1221#endif
1222 // 0 means that the AD commands are sent
1223 err = send_dux_commands(usbduxfastsub, SENDADCOMMANDS);
1224 if (err < 0) {
1225 printk("comedi%d: adc command could not be submitted. Aborting...\n", dev->minor);
1226 up(&usbduxfastsub->sem);
1227 return err;
1228 }
1229#ifdef CONFIG_COMEDI_DEBUG
1230 printk("comedi%d: usbduxfast: submitting in-urb: %x,%x\n",
1231 usbduxfastsub->comedidev->minor,
1232 (int)(usbduxfastsub->urbIn->context),
1233 (int)(usbduxfastsub->urbIn->dev));
1234#endif
1235 for (i = 0; i < PACKETS_TO_IGNORE; i++) {
1236 err = USB_BULK_MSG(usbduxfastsub->usbdev,
1237 usb_rcvbulkpipe(usbduxfastsub->usbdev, BULKINEP),
1238 usbduxfastsub->transfer_buffer,
1239 SIZEINBUF, &actual_length, 10000);
1240 if (err < 0) {
1241 printk("comedi%d: insn timeout. No data.\n",
1242 dev->minor);
1243 up(&usbduxfastsub->sem);
1244 return err;
1245 }
1246 }
1247 // data points
1248 for (i = 0; i < insn->n;) {
1249 err = USB_BULK_MSG(usbduxfastsub->usbdev,
1250 usb_rcvbulkpipe(usbduxfastsub->usbdev, BULKINEP),
1251 usbduxfastsub->transfer_buffer,
1252 SIZEINBUF, &actual_length, 10000);
1253 if (err < 0) {
1254 printk("comedi%d: insn data error: %d\n",
1255 dev->minor, err);
1256 up(&usbduxfastsub->sem);
1257 return err;
1258 }
1259 n = actual_length / sizeof(uint16_t);
1260 if ((n % 16) != 0) {
1261 printk("comedi%d: insn data packet corrupted.\n",
1262 dev->minor);
1263 up(&usbduxfastsub->sem);
1264 return -EINVAL;
1265 }
1266 for (j = chan; (j < n) && (i < insn->n); j = j + 16) {
1267 data[i] =
1268 ((uint16_t *) (usbduxfastsub->
1269 transfer_buffer))[j];
1270 i++;
1271 }
1272 }
1273 up(&usbduxfastsub->sem);
1274 return i;
1275}
1276
1277static unsigned hex2unsigned(char *h)
1278{
1279 unsigned hi, lo;
1280 if (h[0] > '9') {
1281 hi = h[0] - 'A' + 0x0a;
1282 } else {
1283 hi = h[0] - '0';
1284 }
1285 if (h[1] > '9') {
1286 lo = h[1] - 'A' + 0x0a;
1287 } else {
1288 lo = h[1] - '0';
1289 }
1290 return hi * 0x10 + lo;
1291}
1292
1293// for FX2
1294#define FIRMWARE_MAX_LEN 0x2000
1295
1296// taken from David Brownell's fxload and adjusted for this driver
1297static int read_firmware(usbduxfastsub_t * usbduxfastsub, void *firmwarePtr,
1298 long size)
1299{
1300 int i = 0;
1301 unsigned char *fp = (char *)firmwarePtr;
1302 unsigned char *firmwareBinary = NULL;
1303 int res = 0;
1304 int maxAddr = 0;
1305
1306 firmwareBinary = kmalloc(FIRMWARE_MAX_LEN, GFP_KERNEL);
1307 if (!firmwareBinary) {
1308 printk("comedi_: usbduxfast: mem alloc for firmware failed\n");
1309 return -ENOMEM;
1310 }
1311
1312 for (;;) {
1313 char buf[256], *cp;
1314 char type;
1315 int len;
1316 int idx, off;
1317 int j = 0;
1318
1319 // get one line
1320 while ((i < size) && (fp[i] != 13) && (fp[i] != 10)) {
1321 buf[j] = fp[i];
1322 i++;
1323 j++;
1324 if (j >= sizeof(buf)) {
1325 printk("comedi_: usbduxfast: bogus firmware file!\n");
1326 return -1;
1327 }
1328 }
1329 // get rid of LF/CR/...
1330 while ((i < size) && ((fp[i] == 13) || (fp[i] == 10)
1331 || (fp[i] == 0))) {
1332 i++;
1333 }
1334
1335 buf[j] = 0;
1336 //printk("comedi_: buf=%s\n",buf);
1337
1338 /* EXTENSION: "# comment-till-end-of-line", for copyrights etc */
1339 if (buf[0] == '#')
1340 continue;
1341
1342 if (buf[0] != ':') {
1343 printk("comedi_: usbduxfast: upload: not an ihex record: %s", buf);
1344 return -EFAULT;
1345 }
1346
1347 /* Read the length field (up to 16 bytes) */
1348 len = hex2unsigned(buf + 1);
1349
1350 /* Read the target offset */
1351 off = (hex2unsigned(buf + 3) * 0x0100) + hex2unsigned(buf + 5);
1352
1353 if ((off + len) > maxAddr) {
1354 maxAddr = off + len;
1355 }
1356
1357 if (maxAddr >= FIRMWARE_MAX_LEN) {
1358 printk("comedi_: usbduxfast: firmware upload goes beyond FX2 RAM boundaries.");
1359 return -EFAULT;
1360 }
1361 //printk("comedi_: usbduxfast: off=%x, len=%x:",off,len);
1362
1363 /* Read the record type */
1364 type = hex2unsigned(buf + 7);
1365
1366 /* If this is an EOF record, then make it so. */
1367 if (type == 1) {
1368 break;
1369 }
1370
1371 if (type != 0) {
1372 printk("comedi_: usbduxfast: unsupported record type: %u\n", type);
1373 return -EFAULT;
1374 }
1375
1376 for (idx = 0, cp = buf + 9; idx < len; idx += 1, cp += 2) {
1377 firmwareBinary[idx + off] = hex2unsigned(cp);
1378 //printk("%02x ",firmwareBinary[idx+off]);
1379 }
1380 //printk("\n");
1381
1382 if (i >= size) {
1383 printk("comedi_: usbduxfast: unexpected end of hex file\n");
1384 break;
1385 }
1386
1387 }
1388 res = firmwareUpload(usbduxfastsub, firmwareBinary, maxAddr + 1);
1389 kfree(firmwareBinary);
1390 return res;
1391}
1392
1393static void tidy_up(usbduxfastsub_t * usbduxfastsub_tmp)
1394{
1395#ifdef CONFIG_COMEDI_DEBUG
1396 printk("comedi_: usbduxfast: tiding up\n");
1397#endif
1398 if (!usbduxfastsub_tmp) {
1399 return;
1400 }
1401#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
1402 // shows the usb subsystem that the driver is down
1403 if (usbduxfastsub_tmp->interface) {
1404 usb_set_intfdata(usbduxfastsub_tmp->interface, NULL);
1405 }
1406#endif
1407
1408 usbduxfastsub_tmp->probed = 0;
1409
1410 if (usbduxfastsub_tmp->urbIn) {
1411#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8)
1412 // waits until a running transfer is over
1413 // thus, under 2.4 hotplugging while a command
1414 // is running is not safe
1415 usb_kill_urb(usbduxfastsub_tmp->urbIn);
1416#endif
1417 if (usbduxfastsub_tmp->transfer_buffer) {
1418 kfree(usbduxfastsub_tmp->transfer_buffer);
1419 usbduxfastsub_tmp->transfer_buffer = NULL;
1420 }
1421 usb_free_urb(usbduxfastsub_tmp->urbIn);
1422 usbduxfastsub_tmp->urbIn = NULL;
1423 }
1424 if (usbduxfastsub_tmp->insnBuffer) {
1425 kfree(usbduxfastsub_tmp->insnBuffer);
1426 usbduxfastsub_tmp->insnBuffer = NULL;
1427 }
1428 if (usbduxfastsub_tmp->dux_commands) {
1429 kfree(usbduxfastsub_tmp->dux_commands);
1430 usbduxfastsub_tmp->dux_commands = NULL;
1431 }
1432 usbduxfastsub_tmp->ai_cmd_running = 0;
1433}
1434
1435// allocate memory for the urbs and initialise them
1436#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
1437static void *usbduxfastsub_probe(struct usb_device *udev,
1438 unsigned int interfnum, const struct usb_device_id *id)
1439{
1440#else
1441static int usbduxfastsub_probe(struct usb_interface *uinterf,
1442 const struct usb_device_id *id)
1443{
1444 struct usb_device *udev = interface_to_usbdev(uinterf);
1445#endif
1446 int i;
1447 int index;
1448
1449 if (udev->speed != USB_SPEED_HIGH) {
1450 printk("comedi_: usbduxfast_: This driver needs USB 2.0 to operate. Aborting...\n");
1451 return PROBE_ERR_RETURN(-ENODEV);
1452 }
1453#ifdef CONFIG_COMEDI_DEBUG
1454 printk("comedi_: usbduxfast_: finding a free structure for the usb-device\n");
1455#endif
1456 down(&start_stop_sem);
1457 // look for a free place in the usbduxfast array
1458 index = -1;
1459 for (i = 0; i < NUMUSBDUXFAST; i++) {
1460 if (!(usbduxfastsub[i].probed)) {
1461 index = i;
1462 break;
1463 }
1464 }
1465
1466 // no more space
1467 if (index == -1) {
1468 printk("Too many usbduxfast-devices connected.\n");
1469 up(&start_stop_sem);
1470 return PROBE_ERR_RETURN(-EMFILE);
1471 }
1472#ifdef CONFIG_COMEDI_DEBUG
1473 printk("comedi_: usbduxfast: usbduxfastsub[%d] is ready to connect to comedi.\n", index);
1474#endif
1475
1476 init_MUTEX(&(usbduxfastsub[index].sem));
1477 // save a pointer to the usb device
1478 usbduxfastsub[index].usbdev = udev;
1479
1480#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
1481 // save the interface number
1482 usbduxfastsub[index].ifnum = interfnum;
1483#else
1484 // 2.6: save the interface itself
1485 usbduxfastsub[index].interface = uinterf;
1486 // get the interface number from the interface
1487 usbduxfastsub[index].ifnum = uinterf->altsetting->desc.bInterfaceNumber;
1488 // hand the private data over to the usb subsystem
1489 // will be needed for disconnect
1490 usb_set_intfdata(uinterf, &(usbduxfastsub[index]));
1491#endif
1492
1493#ifdef CONFIG_COMEDI_DEBUG
1494 printk("comedi_: usbduxfast: ifnum=%d\n", usbduxfastsub[index].ifnum);
1495#endif
1496 // create space for the commands going to the usb device
1497 usbduxfastsub[index].dux_commands = kmalloc(SIZEOFDUXBUFFER,
1498 GFP_KERNEL);
1499 if (!usbduxfastsub[index].dux_commands) {
1500 printk("comedi_: usbduxfast: error alloc space for dac commands\n");
1501 tidy_up(&(usbduxfastsub[index]));
1502 up(&start_stop_sem);
1503 return PROBE_ERR_RETURN(-ENOMEM);
1504 }
1505 // create space of the instruction buffer
1506 usbduxfastsub[index].insnBuffer = kmalloc(SIZEINSNBUF, GFP_KERNEL);
1507 if (!(usbduxfastsub[index].insnBuffer)) {
1508 printk("comedi_: usbduxfast: could not alloc space for insnBuffer\n");
1509 tidy_up(&(usbduxfastsub[index]));
1510 up(&start_stop_sem);
1511 return PROBE_ERR_RETURN(-ENOMEM);
1512 }
1513 // setting to alternate setting 1: enabling bulk ep
1514 i = usb_set_interface(usbduxfastsub[index].usbdev,
1515 usbduxfastsub[index].ifnum, 1);
1516 if (i < 0) {
1517 printk("comedi_: usbduxfast%d: could not switch to alternate setting 1.\n", index);
1518 tidy_up(&(usbduxfastsub[index]));
1519 up(&start_stop_sem);
1520 return PROBE_ERR_RETURN(-ENODEV);
1521 }
1522 usbduxfastsub[index].urbIn = USB_ALLOC_URB(0);
1523 if (usbduxfastsub[index].urbIn == NULL) {
1524 printk("comedi_: usbduxfast%d: Could not alloc. urb\n", index);
1525 tidy_up(&(usbduxfastsub[index]));
1526 up(&start_stop_sem);
1527 return PROBE_ERR_RETURN(-ENOMEM);
1528 }
1529 usbduxfastsub[index].transfer_buffer = kmalloc(SIZEINBUF, GFP_KERNEL);
1530 if (!(usbduxfastsub[index].transfer_buffer)) {
1531 printk("comedi_: usbduxfast%d: could not alloc. transb.\n",
1532 index);
1533 tidy_up(&(usbduxfastsub[index]));
1534 up(&start_stop_sem);
1535 return PROBE_ERR_RETURN(-ENOMEM);
1536 }
1537 // we've reached the bottom of the function
1538 usbduxfastsub[index].probed = 1;
1539 up(&start_stop_sem);
1540 printk("comedi_: usbduxfast%d has been successfully initialized.\n",
1541 index);
1542#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
1543 return (void *)(&usbduxfastsub[index]);
1544#else
1545 // success
1546 return 0;
1547#endif
1548}
1549
1550#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
1551static void usbduxfastsub_disconnect(struct usb_device *udev, void *ptr)
1552{
1553 usbduxfastsub_t *usbduxfastsub_tmp = (usbduxfastsub_t *) ptr;
1554#else
1555static void usbduxfastsub_disconnect(struct usb_interface *intf)
1556{
1557 usbduxfastsub_t *usbduxfastsub_tmp = usb_get_intfdata(intf);
1558 struct usb_device *udev = interface_to_usbdev(intf);
1559#endif
1560 if (!usbduxfastsub_tmp) {
1561 printk("comedi_: usbduxfast: disconnect called with null pointer.\n");
1562 return;
1563 }
1564 if (usbduxfastsub_tmp->usbdev != udev) {
1565 printk("comedi_: usbduxfast: BUG! called with wrong ptr!!!\n");
1566 return;
1567 }
1568 down(&start_stop_sem);
1569 down(&usbduxfastsub_tmp->sem);
1570 tidy_up(usbduxfastsub_tmp);
1571 up(&usbduxfastsub_tmp->sem);
1572 up(&start_stop_sem);
1573#ifdef CONFIG_COMEDI_DEBUG
1574 printk("comedi_: usbduxfast: disconnected from the usb\n");
1575#endif
1576}
1577
1578// is called when comedi-config is called
1579static int usbduxfast_attach(comedi_device * dev, comedi_devconfig * it)
1580{
1581 int ret;
1582 int index;
1583 int i;
1584 comedi_subdevice *s = NULL;
1585 dev->private = NULL;
1586
1587 down(&start_stop_sem);
1588 // find a valid device which has been detected by the probe function of the usb
1589 index = -1;
1590 for (i = 0; i < NUMUSBDUXFAST; i++) {
1591 if ((usbduxfastsub[i].probed) && (!usbduxfastsub[i].attached)) {
1592 index = i;
1593 break;
1594 }
1595 }
1596
1597 if (index < 0) {
1598 printk("comedi%d: usbduxfast: error: attach failed, no usbduxfast devs connected to the usb bus.\n", dev->minor);
1599 up(&start_stop_sem);
1600 return -ENODEV;
1601 }
1602
1603 down(&(usbduxfastsub[index].sem));
1604 // pointer back to the corresponding comedi device
1605 usbduxfastsub[index].comedidev = dev;
1606
1607 // trying to upload the firmware into the chip
1608 if (comedi_aux_data(it->options, 0) &&
1609 it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
1610 read_firmware(usbduxfastsub,
1611 comedi_aux_data(it->options, 0),
1612 it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]);
1613 }
1614
1615 dev->board_name = BOARDNAME;
1616
1617 /* set number of subdevices */
1618 dev->n_subdevices = N_SUBDEVICES;
1619
1620 // allocate space for the subdevices
1621 if ((ret = alloc_subdevices(dev, N_SUBDEVICES)) < 0) {
1622 printk("comedi%d: usbduxfast: error alloc space for subdev\n",
1623 dev->minor);
1624 up(&start_stop_sem);
1625 return ret;
1626 }
1627
1628 printk("comedi%d: usbduxfast: usb-device %d is attached to comedi.\n",
1629 dev->minor, index);
1630 // private structure is also simply the usb-structure
1631 dev->private = usbduxfastsub + index;
1632 // the first subdevice is the A/D converter
1633 s = dev->subdevices + SUBDEV_AD;
1634 // the URBs get the comedi subdevice
1635 // which is responsible for reading
1636 // this is the subdevice which reads data
1637 dev->read_subdev = s;
1638 // the subdevice receives as private structure the
1639 // usb-structure
1640 s->private = NULL;
1641 // analog input
1642 s->type = COMEDI_SUBD_AI;
1643 // readable and ref is to ground
1644 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
1645 // 16 channels
1646 s->n_chan = 16;
1647 // length of the channellist
1648 s->len_chanlist = 16;
1649 // callback functions
1650 s->insn_read = usbduxfast_ai_insn_read;
1651 s->do_cmdtest = usbduxfast_ai_cmdtest;
1652 s->do_cmd = usbduxfast_ai_cmd;
1653 s->cancel = usbduxfast_ai_cancel;
1654 // max value from the A/D converter (12bit+1 bit for overflow)
1655 s->maxdata = 0x1000;
1656 // range table to convert to physical units
1657 s->range_table = &range_usbduxfast_ai_range;
1658
1659 // finally decide that it's attached
1660 usbduxfastsub[index].attached = 1;
1661
1662 up(&(usbduxfastsub[index].sem));
1663
1664 up(&start_stop_sem);
1665
1666 printk("comedi%d: successfully attached to usbduxfast.\n", dev->minor);
1667
1668 return 0;
1669}
1670
1671static int usbduxfast_detach(comedi_device * dev)
1672{
1673 usbduxfastsub_t *usbduxfastsub_tmp;
1674
1675#ifdef CONFIG_COMEDI_DEBUG
1676 printk("comedi%d: usbduxfast: detach usb device\n", dev->minor);
1677#endif
1678
1679 if (!dev) {
1680 printk("comedi?: usbduxfast: detach without dev variable...\n");
1681 return -EFAULT;
1682 }
1683
1684 usbduxfastsub_tmp = dev->private;
1685 if (!usbduxfastsub_tmp) {
1686 printk("comedi?: usbduxfast: detach without ptr to usbduxfastsub[]\n");
1687 return -EFAULT;
1688 }
1689
1690 down(&usbduxfastsub_tmp->sem);
1691 down(&start_stop_sem);
1692 // Don't allow detach to free the private structure
1693 // It's one entry of of usbduxfastsub[]
1694 dev->private = NULL;
1695 usbduxfastsub_tmp->attached = 0;
1696 usbduxfastsub_tmp->comedidev = NULL;
1697#ifdef CONFIG_COMEDI_DEBUG
1698 printk("comedi%d: usbduxfast: detach: successfully removed\n",
1699 dev->minor);
1700#endif
1701 up(&start_stop_sem);
1702 up(&usbduxfastsub_tmp->sem);
1703 return 0;
1704}
1705
1706/* main driver struct */
1707static comedi_driver driver_usbduxfast = {
1708 driver_name:"usbduxfast",
1709 module:THIS_MODULE,
1710 attach:usbduxfast_attach,
1711 detach:usbduxfast_detach,
1712};
1713
1714static void init_usb_devices(void)
1715{
1716 int index;
1717#ifdef CONFIG_COMEDI_DEBUG
1718 printk("comedi_: usbduxfast: setting all possible devs to invalid\n");
1719#endif
1720 // all devices entries are invalid to begin with
1721 // they will become valid by the probe function
1722 // and then finally by the attach-function
1723 for (index = 0; index < NUMUSBDUXFAST; index++) {
1724 memset(&(usbduxfastsub[index]), 0x00,
1725 sizeof(usbduxfastsub[index]));
1726 init_MUTEX(&(usbduxfastsub[index].sem));
1727 }
1728}
1729
1730// Table with the USB-devices: just now only testing IDs
1731static struct usb_device_id usbduxfastsub_table[] = {
1732 // { USB_DEVICE(0x4b4, 0x8613), //testing
1733 // },
1734 {USB_DEVICE(0x13d8, 0x0010) //real ID
1735 },
1736 {USB_DEVICE(0x13d8, 0x0011) //real ID
1737 },
1738 {} /* Terminating entry */
1739};
1740
1741MODULE_DEVICE_TABLE(usb, usbduxfastsub_table);
1742
1743// The usbduxfastsub-driver
1744static struct usb_driver usbduxfastsub_driver = {
1745#ifdef COMEDI_HAVE_USB_DRIVER_OWNER
1746 owner:THIS_MODULE,
1747#endif
1748 name:BOARDNAME,
1749 probe:usbduxfastsub_probe,
1750 disconnect:usbduxfastsub_disconnect,
1751 id_table:usbduxfastsub_table,
1752};
1753
1754// Can't use the nice macro as I have also to initialise the USB
1755// subsystem:
1756// registering the usb-system _and_ the comedi-driver
1757static int init_usbduxfast(void)
1758{
1759 printk(KERN_INFO KBUILD_MODNAME ": "
1760 DRIVER_VERSION ":" DRIVER_DESC "\n");
1761 init_usb_devices();
1762 usb_register(&usbduxfastsub_driver);
1763 comedi_driver_register(&driver_usbduxfast);
1764 return 0;
1765}
1766
1767// deregistering the comedi driver and the usb-subsystem
1768static void exit_usbduxfast(void)
1769{
1770 comedi_driver_unregister(&driver_usbduxfast);
1771 usb_deregister(&usbduxfastsub_driver);
1772}
1773
1774module_init(init_usbduxfast);
1775module_exit(exit_usbduxfast);
1776
1777MODULE_AUTHOR(DRIVER_AUTHOR);
1778MODULE_DESCRIPTION(DRIVER_DESC);
1779MODULE_LICENSE("GPL");