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