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