Commit | Line | Data |
---|---|---|
4bf21fa4 BP |
1 | /* |
2 | comedi/drivers/usbdux.c | |
3 | Copyright (C) 2003-2007 Bernd Porr, Bernd.Porr@f2s.com | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
4bf21fa4 BP |
14 | */ |
15 | /* | |
16 | Driver: usbdux | |
17 | Description: University of Stirling USB DAQ & INCITE Technology Limited | |
18 | Devices: [ITL] USB-DUX (usbdux.o) | |
19 | Author: Bernd Porr <BerndPorr@f2s.com> | |
6742c0af BP |
20 | Updated: 8 Dec 2008 |
21 | Status: Stable | |
4bf21fa4 BP |
22 | Configuration options: |
23 | You have to upload firmware with the -i option. The | |
24 | firmware is usually installed under /usr/share/usb or | |
25 | /usr/local/share/usb or /lib/firmware. | |
26 | ||
27 | Connection scheme for the counter at the digital port: | |
28 | 0=/CLK0, 1=UP/DOWN0, 2=RESET0, 4=/CLK1, 5=UP/DOWN1, 6=RESET1. | |
29 | The sampling rate of the counter is approximately 500Hz. | |
30 | ||
31 | Please note that under USB2.0 the length of the channel list determines | |
32 | the max sampling rate. If you sample only one channel you get 8kHz | |
33 | sampling rate. If you sample two channels you get 4kHz and so on. | |
34 | */ | |
35 | /* | |
36 | * I must give credit here to Chris Baugher who | |
37 | * wrote the driver for AT-MIO-16d. I used some parts of this | |
38 | * driver. I also must give credits to David Brownell | |
39 | * who supported me with the USB development. | |
40 | * | |
41 | * Bernd Porr | |
42 | * | |
43 | * | |
44 | * Revision history: | |
45 | * 0.94: D/A output should work now with any channel list combinations | |
46 | * 0.95: .owner commented out for kernel vers below 2.4.19 | |
47 | * sanity checks in ai/ao_cmd | |
4274ea02 GKH |
48 | * 0.96: trying to get it working with 2.6, moved all memory alloc to comedi's |
49 | * attach final USB IDs | |
50 | * moved memory allocation completely to the corresponding comedi | |
51 | * functions firmware upload is by fxload and no longer by comedi (due to | |
52 | * enumeration) | |
4bf21fa4 | 53 | * 0.97: USB IDs received, adjusted table |
20ce161d | 54 | * 0.98: SMP, locking, memory alloc: moved all usb memory alloc |
4bf21fa4 BP |
55 | * to the usb subsystem and moved all comedi related memory |
56 | * alloc to comedi. | |
57 | * | kernel | registration | usbdux-usb | usbdux-comedi | comedi | | |
58 | * 0.99: USB 2.0: changed protocol to isochronous transfer | |
59 | * IRQ transfer is too buggy and too risky in 2.0 | |
4274ea02 GKH |
60 | * for the high speed ISO transfer is now a working version |
61 | * available | |
4bf21fa4 BP |
62 | * 0.99b: Increased the iso transfer buffer for high sp.to 10 buffers. Some VIA |
63 | * chipsets miss out IRQs. Deeper buffering is needed. | |
4274ea02 GKH |
64 | * 1.00: full USB 2.0 support for the A/D converter. Now: max 8kHz sampling |
65 | * rate. | |
4bf21fa4 BP |
66 | * Firmware vers 1.00 is needed for this. |
67 | * Two 16 bit up/down/reset counter with a sampling rate of 1kHz | |
68 | * And loads of cleaning up, in particular streamlining the | |
69 | * bulk transfers. | |
70 | * 1.1: moved EP4 transfers to EP1 to make space for a PWM output on EP4 | |
02582e9b | 71 | * 1.2: added PWM support via EP4 |
4bf21fa4 BP |
72 | * 2.0: PWM seems to be stable and is not interfering with the other functions |
73 | * 2.1: changed PWM API | |
6742c0af | 74 | * 2.2: added firmware kernel request to fix an udev problem |
ea25371a | 75 | * 2.3: corrected a bug in bulk timeouts which were far too short |
d4c3a565 BP |
76 | * 2.4: fixed a bug which causes the driver to hang when it ran out of data. |
77 | * Thanks to Jan-Matthias Braun and Ian to spot the bug and fix it. | |
4bf21fa4 BP |
78 | * |
79 | */ | |
80 | ||
4bf21fa4 BP |
81 | #include <linux/kernel.h> |
82 | #include <linux/module.h> | |
4bf21fa4 BP |
83 | #include <linux/slab.h> |
84 | #include <linux/input.h> | |
85 | #include <linux/usb.h> | |
4bf21fa4 BP |
86 | #include <linux/fcntl.h> |
87 | #include <linux/compiler.h> | |
88 | ||
89 | #include "../comedidev.h" | |
4bf21fa4 | 90 | |
27020ffe HS |
91 | #include "comedi_fc.h" |
92 | ||
1731a826 HS |
93 | /* constants for firmware upload and download */ |
94 | #define USBDUX_FIRMWARE "usbdux_firmware.bin" | |
95 | #define USBDUX_FIRMWARE_MAX_LEN 0x2000 | |
96 | #define USBDUX_FIRMWARE_CMD 0xa0 | |
97 | #define VENDOR_DIR_IN 0xc0 | |
98 | #define VENDOR_DIR_OUT 0x40 | |
99 | #define USBDUX_CPU_CS 0xe600 | |
4bf21fa4 | 100 | |
757fbc2a HS |
101 | /* usbdux bulk transfer commands */ |
102 | #define USBDUX_CMD_MULT_AI 0 | |
103 | #define USBDUX_CMD_AO 1 | |
104 | #define USBDUX_CMD_DIO_CFG 2 | |
105 | #define USBDUX_CMD_DIO_BITS 3 | |
106 | #define USBDUX_CMD_SINGLE_AI 4 | |
107 | #define USBDUX_CMD_TIMER_RD 5 | |
108 | #define USBDUX_CMD_TIMER_WR 6 | |
109 | #define USBDUX_CMD_PWM_ON 7 | |
110 | #define USBDUX_CMD_PWM_OFF 8 | |
111 | ||
a998a3db HS |
112 | #define USBDUX_NUM_AO_CHAN 4 |
113 | ||
1731a826 HS |
114 | /* timeout for the USB-transfer in ms */ |
115 | #define BULK_TIMEOUT 1000 | |
4bf21fa4 | 116 | |
e54fb9c1 | 117 | /* 300Hz max frequ under PWM */ |
4bf21fa4 BP |
118 | #define MIN_PWM_PERIOD ((long)(1E9/300)) |
119 | ||
e54fb9c1 | 120 | /* Default PWM frequency */ |
4bf21fa4 BP |
121 | #define PWM_DEFAULT_PERIOD ((long)(1E9/100)) |
122 | ||
e54fb9c1 | 123 | /* Size of one A/D value */ |
470180c6 | 124 | #define SIZEADIN ((sizeof(uint16_t))) |
4bf21fa4 | 125 | |
4274ea02 GKH |
126 | /* |
127 | * Size of the input-buffer IN BYTES | |
128 | * Always multiple of 8 for 8 microframes which is needed in the highspeed mode | |
129 | */ | |
4bf21fa4 BP |
130 | #define SIZEINBUF ((8*SIZEADIN)) |
131 | ||
e54fb9c1 | 132 | /* 16 bytes. */ |
4bf21fa4 BP |
133 | #define SIZEINSNBUF 16 |
134 | ||
e54fb9c1 | 135 | /* size of one value for the D/A converter: channel and value */ |
470180c6 | 136 | #define SIZEDAOUT ((sizeof(uint8_t)+sizeof(uint16_t))) |
4bf21fa4 | 137 | |
e54fb9c1 GKH |
138 | /* |
139 | * Size of the output-buffer in bytes | |
140 | * Actually only the first 4 triplets are used but for the | |
141 | * high speed mode we need to pad it to 8 (microframes). | |
142 | */ | |
4bf21fa4 BP |
143 | #define SIZEOUTBUF ((8*SIZEDAOUT)) |
144 | ||
e54fb9c1 GKH |
145 | /* |
146 | * Size of the buffer for the dux commands: just now max size is determined | |
147 | * by the analogue out + command byte + panic bytes... | |
148 | */ | |
4bf21fa4 BP |
149 | #define SIZEOFDUXBUFFER ((8*SIZEDAOUT+2)) |
150 | ||
e54fb9c1 | 151 | /* Number of in-URBs which receive the data: min=2 */ |
4bf21fa4 BP |
152 | #define NUMOFINBUFFERSFULL 5 |
153 | ||
e54fb9c1 | 154 | /* Number of out-URBs which send the data: min=2 */ |
4bf21fa4 BP |
155 | #define NUMOFOUTBUFFERSFULL 5 |
156 | ||
e54fb9c1 GKH |
157 | /* Number of in-URBs which receive the data: min=5 */ |
158 | /* must have more buffers due to buggy USB ctr */ | |
159 | #define NUMOFINBUFFERSHIGH 10 | |
4bf21fa4 | 160 | |
e54fb9c1 GKH |
161 | /* Number of out-URBs which send the data: min=5 */ |
162 | /* must have more buffers due to buggy USB ctr */ | |
163 | #define NUMOFOUTBUFFERSHIGH 10 | |
4bf21fa4 | 164 | |
e54fb9c1 | 165 | /* number of retries to get the right dux command */ |
4bf21fa4 BP |
166 | #define RETRIES 10 |
167 | ||
38691ec1 HS |
168 | static const struct comedi_lrange range_usbdux_ai_range = { |
169 | 4, { | |
170 | BIP_RANGE(4.096), | |
171 | BIP_RANGE(4.096 / 2), | |
172 | UNI_RANGE(4.096), | |
173 | UNI_RANGE(4.096 / 2) | |
174 | } | |
4bf21fa4 BP |
175 | }; |
176 | ||
38691ec1 HS |
177 | static const struct comedi_lrange range_usbdux_ao_range = { |
178 | 2, { | |
179 | BIP_RANGE(4.096), | |
180 | UNI_RANGE(4.096) | |
181 | } | |
4bf21fa4 BP |
182 | }; |
183 | ||
40f1a5ab | 184 | struct usbdux_private { |
e54fb9c1 | 185 | /* actual number of in-buffers */ |
c79bc875 | 186 | int n_ai_urbs; |
e54fb9c1 | 187 | /* actual number of out-buffers */ |
c79bc875 | 188 | int n_ao_urbs; |
e54fb9c1 | 189 | /* ISO-transfer handling: buffers */ |
c79bc875 HS |
190 | struct urb **ai_urbs; |
191 | struct urb **ao_urbs; | |
e54fb9c1 | 192 | /* pwm-transfer handling */ |
c79bc875 | 193 | struct urb *pwm_urb; |
e54fb9c1 | 194 | /* PWM period */ |
b74e5f56 | 195 | unsigned int pwm_period; |
e54fb9c1 | 196 | /* PWM internal delay for the GPIF in the FX2 */ |
470180c6 | 197 | uint8_t pwm_delay; |
e54fb9c1 | 198 | /* size of the PWM buffer which holds the bit pattern */ |
c79bc875 | 199 | int pwm_buf_sz; |
e54fb9c1 | 200 | /* input buffer for the ISO-transfer */ |
470180c6 | 201 | uint16_t *in_buf; |
e54fb9c1 | 202 | /* input buffer for single insn */ |
470180c6 | 203 | uint16_t *insn_buf; |
a998a3db | 204 | |
470180c6 | 205 | uint8_t ao_chanlist[USBDUX_NUM_AO_CHAN]; |
a998a3db | 206 | unsigned int ao_readback[USBDUX_NUM_AO_CHAN]; |
b8c162c9 HS |
207 | |
208 | unsigned int high_speed:1; | |
209 | unsigned int ai_cmd_running:1; | |
210 | unsigned int ai_continous:1; | |
211 | unsigned int ao_cmd_running:1; | |
212 | unsigned int ao_continous:1; | |
213 | unsigned int pwm_cmd_running:1; | |
214 | ||
9d220c6b | 215 | /* number of samples to acquire */ |
4bf21fa4 BP |
216 | int ai_sample_count; |
217 | int ao_sample_count; | |
e54fb9c1 | 218 | /* time between samples in units of the timer */ |
4bf21fa4 BP |
219 | unsigned int ai_timer; |
220 | unsigned int ao_timer; | |
e54fb9c1 | 221 | /* counter between aquisitions */ |
4bf21fa4 BP |
222 | unsigned int ai_counter; |
223 | unsigned int ao_counter; | |
e54fb9c1 | 224 | /* interval in frames/uframes */ |
4bf21fa4 | 225 | unsigned int ai_interval; |
e54fb9c1 | 226 | /* commands */ |
470180c6 | 227 | uint8_t *dux_commands; |
4bf21fa4 | 228 | struct semaphore sem; |
cc92fca7 | 229 | }; |
4bf21fa4 | 230 | |
0c4349c9 | 231 | static void usbdux_unlink_urbs(struct urb **urbs, int num_urbs) |
4bf21fa4 | 232 | { |
3c50bbb7 | 233 | int i; |
4bf21fa4 | 234 | |
0c4349c9 HS |
235 | for (i = 0; i < num_urbs; i++) |
236 | usb_kill_urb(urbs[i]); | |
4bf21fa4 BP |
237 | } |
238 | ||
3c50bbb7 | 239 | static void usbdux_ai_stop(struct comedi_device *dev, int do_unlink) |
4bf21fa4 | 240 | { |
b3476e67 | 241 | struct usbdux_private *devpriv = dev->private; |
4bf21fa4 | 242 | |
0c4349c9 HS |
243 | if (do_unlink && devpriv->ai_urbs) |
244 | usbdux_unlink_urbs(devpriv->ai_urbs, devpriv->n_ai_urbs); | |
4bf21fa4 | 245 | |
b3476e67 | 246 | devpriv->ai_cmd_running = 0; |
4bf21fa4 BP |
247 | } |
248 | ||
0a85b6f0 MT |
249 | static int usbdux_ai_cancel(struct comedi_device *dev, |
250 | struct comedi_subdevice *s) | |
4bf21fa4 | 251 | { |
aa6081e5 | 252 | struct usbdux_private *devpriv = dev->private; |
c0e0c26e | 253 | |
e54fb9c1 | 254 | /* prevent other CPUs from submitting new commands just now */ |
aa6081e5 | 255 | down(&devpriv->sem); |
e54fb9c1 | 256 | /* unlink only if the urb really has been submitted */ |
3c50bbb7 | 257 | usbdux_ai_stop(dev, devpriv->ai_cmd_running); |
aa6081e5 HS |
258 | up(&devpriv->sem); |
259 | ||
3c50bbb7 | 260 | return 0; |
4bf21fa4 BP |
261 | } |
262 | ||
e54fb9c1 | 263 | /* analogue IN - interrupt service routine */ |
b74e5f56 | 264 | static void usbduxsub_ai_isoc_irq(struct urb *urb) |
4bf21fa4 | 265 | { |
0a9502f2 HS |
266 | struct comedi_device *dev = urb->context; |
267 | struct comedi_subdevice *s = dev->read_subdev; | |
268 | struct usbdux_private *devpriv = dev->private; | |
4bf21fa4 | 269 | int i, err, n; |
4bf21fa4 | 270 | |
e54fb9c1 | 271 | /* first we test if something unusual has just happened */ |
4bf21fa4 BP |
272 | switch (urb->status) { |
273 | case 0: | |
e54fb9c1 | 274 | /* copy the result in the transfer buffer */ |
c79bc875 | 275 | memcpy(devpriv->in_buf, urb->transfer_buffer, SIZEINBUF); |
4bf21fa4 BP |
276 | break; |
277 | case -EILSEQ: | |
e54fb9c1 GKH |
278 | /* error in the ISOchronous data */ |
279 | /* we don't copy the data into the transfer buffer */ | |
280 | /* and recycle the last data byte */ | |
0a9502f2 | 281 | dev_dbg(dev->class_dev, "CRC error in ISO IN stream\n"); |
4bf21fa4 BP |
282 | break; |
283 | ||
4bf21fa4 BP |
284 | case -ECONNRESET: |
285 | case -ENOENT: | |
286 | case -ESHUTDOWN: | |
287 | case -ECONNABORTED: | |
e54fb9c1 | 288 | /* happens after an unlink command */ |
0a9502f2 | 289 | if (devpriv->ai_cmd_running) { |
4bf21fa4 BP |
290 | s->async->events |= COMEDI_CB_EOA; |
291 | s->async->events |= COMEDI_CB_ERROR; | |
0a9502f2 | 292 | comedi_event(dev, s); |
e54fb9c1 | 293 | /* stop the transfer w/o unlink */ |
b3476e67 | 294 | usbdux_ai_stop(dev, 0); |
4bf21fa4 BP |
295 | } |
296 | return; | |
297 | ||
4bf21fa4 | 298 | default: |
e54fb9c1 GKH |
299 | /* a real error on the bus */ |
300 | /* pass error to comedi if we are really running a command */ | |
0a9502f2 HS |
301 | if (devpriv->ai_cmd_running) { |
302 | dev_err(dev->class_dev, | |
303 | "Non-zero urb status received in ai intr context: %d\n", | |
304 | urb->status); | |
4bf21fa4 BP |
305 | s->async->events |= COMEDI_CB_EOA; |
306 | s->async->events |= COMEDI_CB_ERROR; | |
0a9502f2 | 307 | comedi_event(dev, s); |
e54fb9c1 | 308 | /* don't do an unlink here */ |
b3476e67 | 309 | usbdux_ai_stop(dev, 0); |
4bf21fa4 BP |
310 | } |
311 | return; | |
312 | } | |
313 | ||
4274ea02 GKH |
314 | /* |
315 | * at this point we are reasonably sure that nothing dodgy has happened | |
316 | * are we running a command? | |
317 | */ | |
0a9502f2 | 318 | if (unlikely(!devpriv->ai_cmd_running)) { |
e54fb9c1 GKH |
319 | /* |
320 | * not running a command, do not continue execution if no | |
321 | * asynchronous command is running in particular not resubmit | |
322 | */ | |
4bf21fa4 BP |
323 | return; |
324 | } | |
325 | ||
0b20d613 | 326 | urb->dev = comedi_to_usb_dev(dev); |
4bf21fa4 | 327 | |
8fa07567 | 328 | /* resubmit the urb */ |
4aa3a823 | 329 | err = usb_submit_urb(urb, GFP_ATOMIC); |
4bf21fa4 | 330 | if (unlikely(err < 0)) { |
0a9502f2 HS |
331 | dev_err(dev->class_dev, |
332 | "urb resubmit failed in int-context! err=%d\n", err); | |
8fa07567 | 333 | if (err == -EL2NSYNC) |
0a9502f2 HS |
334 | dev_err(dev->class_dev, |
335 | "buggy USB host controller or bug in IRQ handler!\n"); | |
4bf21fa4 BP |
336 | s->async->events |= COMEDI_CB_EOA; |
337 | s->async->events |= COMEDI_CB_ERROR; | |
0a9502f2 | 338 | comedi_event(dev, s); |
8fa07567 | 339 | /* don't do an unlink here */ |
b3476e67 | 340 | usbdux_ai_stop(dev, 0); |
4bf21fa4 BP |
341 | return; |
342 | } | |
343 | ||
0a9502f2 HS |
344 | devpriv->ai_counter--; |
345 | if (likely(devpriv->ai_counter > 0)) | |
4bf21fa4 | 346 | return; |
8fa07567 | 347 | |
e54fb9c1 | 348 | /* timer zero, transfer measurements to comedi */ |
0a9502f2 | 349 | devpriv->ai_counter = devpriv->ai_timer; |
4bf21fa4 | 350 | |
e54fb9c1 | 351 | /* test, if we transmit only a fixed number of samples */ |
0a9502f2 | 352 | if (!devpriv->ai_continous) { |
25985edc | 353 | /* not continuous, fixed number of samples */ |
0a9502f2 | 354 | devpriv->ai_sample_count--; |
e54fb9c1 | 355 | /* all samples received? */ |
0a9502f2 | 356 | if (devpriv->ai_sample_count < 0) { |
e54fb9c1 | 357 | /* prevent a resubmit next time */ |
b3476e67 | 358 | usbdux_ai_stop(dev, 0); |
e54fb9c1 | 359 | /* say comedi that the acquistion is over */ |
4bf21fa4 | 360 | s->async->events |= COMEDI_CB_EOA; |
0a9502f2 | 361 | comedi_event(dev, s); |
4bf21fa4 BP |
362 | return; |
363 | } | |
364 | } | |
e54fb9c1 | 365 | /* get the data from the USB bus and hand it over to comedi */ |
4bf21fa4 BP |
366 | n = s->async->cmd.chanlist_len; |
367 | for (i = 0; i < n; i++) { | |
7acf26ed | 368 | unsigned int range = CR_RANGE(s->async->cmd.chanlist[i]); |
470180c6 | 369 | uint16_t val = le16_to_cpu(devpriv->in_buf[i]); |
7acf26ed HS |
370 | |
371 | /* bipolar data is two's-complement */ | |
372 | if (comedi_range_is_bipolar(s, range)) | |
373 | val ^= ((s->maxdata + 1) >> 1); | |
374 | ||
e54fb9c1 | 375 | /* transfer data */ |
7acf26ed | 376 | err = comedi_buf_put(s->async, val); |
efe8d60a BP |
377 | if (unlikely(err == 0)) { |
378 | /* buffer overflow */ | |
b3476e67 | 379 | usbdux_ai_stop(dev, 0); |
efe8d60a | 380 | return; |
4bf21fa4 BP |
381 | } |
382 | } | |
e54fb9c1 | 383 | /* tell comedi that data is there */ |
d4c3a565 | 384 | s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; |
0a9502f2 | 385 | comedi_event(dev, s); |
4bf21fa4 BP |
386 | } |
387 | ||
3c50bbb7 | 388 | static void usbdux_ao_stop(struct comedi_device *dev, int do_unlink) |
4bf21fa4 | 389 | { |
7c8ed94e | 390 | struct usbdux_private *devpriv = dev->private; |
4bf21fa4 | 391 | |
0c4349c9 HS |
392 | if (do_unlink && devpriv->ao_urbs) |
393 | usbdux_unlink_urbs(devpriv->ao_urbs, devpriv->n_ao_urbs); | |
4bf21fa4 | 394 | |
7c8ed94e | 395 | devpriv->ao_cmd_running = 0; |
4bf21fa4 BP |
396 | } |
397 | ||
0a85b6f0 MT |
398 | static int usbdux_ao_cancel(struct comedi_device *dev, |
399 | struct comedi_subdevice *s) | |
4bf21fa4 | 400 | { |
eee7d9e9 | 401 | struct usbdux_private *devpriv = dev->private; |
c0e0c26e | 402 | |
e54fb9c1 | 403 | /* prevent other CPUs from submitting a command just now */ |
eee7d9e9 | 404 | down(&devpriv->sem); |
e54fb9c1 | 405 | /* unlink only if it is really running */ |
3c50bbb7 | 406 | usbdux_ao_stop(dev, devpriv->ao_cmd_running); |
eee7d9e9 HS |
407 | up(&devpriv->sem); |
408 | ||
3c50bbb7 | 409 | return 0; |
4bf21fa4 BP |
410 | } |
411 | ||
b74e5f56 | 412 | static void usbduxsub_ao_isoc_irq(struct urb *urb) |
4bf21fa4 | 413 | { |
e057288f HS |
414 | struct comedi_device *dev = urb->context; |
415 | struct comedi_subdevice *s = dev->write_subdev; | |
416 | struct usbdux_private *devpriv = dev->private; | |
470180c6 | 417 | uint8_t *datap; |
c769f85e HS |
418 | int len; |
419 | int ret; | |
420 | int i; | |
4bf21fa4 BP |
421 | |
422 | switch (urb->status) { | |
423 | case 0: | |
424 | /* success */ | |
425 | break; | |
426 | ||
4bf21fa4 BP |
427 | case -ECONNRESET: |
428 | case -ENOENT: | |
429 | case -ESHUTDOWN: | |
430 | case -ECONNABORTED: | |
e54fb9c1 GKH |
431 | /* after an unlink command, unplug, ... etc */ |
432 | /* no unlink needed here. Already shutting down. */ | |
e057288f | 433 | if (devpriv->ao_cmd_running) { |
4bf21fa4 | 434 | s->async->events |= COMEDI_CB_EOA; |
e057288f | 435 | comedi_event(dev, s); |
7c8ed94e | 436 | usbdux_ao_stop(dev, 0); |
4bf21fa4 BP |
437 | } |
438 | return; | |
439 | ||
4bf21fa4 | 440 | default: |
e54fb9c1 | 441 | /* a real error */ |
e057288f HS |
442 | if (devpriv->ao_cmd_running) { |
443 | dev_err(dev->class_dev, | |
444 | "Non-zero urb status received in ao intr context: %d\n", | |
445 | urb->status); | |
4bf21fa4 BP |
446 | s->async->events |= COMEDI_CB_ERROR; |
447 | s->async->events |= COMEDI_CB_EOA; | |
e057288f | 448 | comedi_event(dev, s); |
e54fb9c1 | 449 | /* we do an unlink if we are in the high speed mode */ |
7c8ed94e | 450 | usbdux_ao_stop(dev, 0); |
4bf21fa4 BP |
451 | } |
452 | return; | |
453 | } | |
454 | ||
e54fb9c1 | 455 | /* are we actually running? */ |
e057288f | 456 | if (!devpriv->ao_cmd_running) |
4bf21fa4 | 457 | return; |
8fa07567 | 458 | |
e54fb9c1 | 459 | /* normal operation: executing a command in this subdevice */ |
e057288f HS |
460 | devpriv->ao_counter--; |
461 | if ((int)devpriv->ao_counter <= 0) { | |
e54fb9c1 | 462 | /* timer zero */ |
e057288f | 463 | devpriv->ao_counter = devpriv->ao_timer; |
4bf21fa4 | 464 | |
25985edc | 465 | /* handle non continous acquisition */ |
e057288f | 466 | if (!devpriv->ao_continous) { |
e54fb9c1 | 467 | /* fixed number of samples */ |
e057288f HS |
468 | devpriv->ao_sample_count--; |
469 | if (devpriv->ao_sample_count < 0) { | |
e54fb9c1 | 470 | /* all samples transmitted */ |
7c8ed94e | 471 | usbdux_ao_stop(dev, 0); |
4bf21fa4 | 472 | s->async->events |= COMEDI_CB_EOA; |
e057288f | 473 | comedi_event(dev, s); |
e54fb9c1 | 474 | /* no resubmit of the urb */ |
4bf21fa4 BP |
475 | return; |
476 | } | |
477 | } | |
c769f85e | 478 | |
e54fb9c1 | 479 | /* transmit data to the USB bus */ |
c769f85e HS |
480 | datap = urb->transfer_buffer; |
481 | len = s->async->cmd.chanlist_len; | |
482 | *datap++ = len; | |
4bf21fa4 | 483 | for (i = 0; i < s->async->cmd.chanlist_len; i++) { |
c5bbfe50 | 484 | unsigned int chan = devpriv->ao_chanlist[i]; |
470180c6 | 485 | unsigned short val; |
8fa07567 | 486 | |
a998a3db HS |
487 | ret = comedi_buf_get(s->async, &val); |
488 | if (ret < 0) { | |
489 | dev_err(dev->class_dev, "buffer underflow\n"); | |
490 | s->async->events |= (COMEDI_CB_EOA | | |
491 | COMEDI_CB_OVERFLOW); | |
492 | } | |
e54fb9c1 | 493 | /* pointer to the DA */ |
c769f85e HS |
494 | *datap++ = val & 0xff; |
495 | *datap++ = (val >> 8) & 0xff; | |
496 | *datap++ = chan; | |
a998a3db HS |
497 | devpriv->ao_readback[chan] = val; |
498 | ||
4bf21fa4 | 499 | s->async->events |= COMEDI_CB_BLOCK; |
e057288f | 500 | comedi_event(dev, s); |
4bf21fa4 BP |
501 | } |
502 | } | |
503 | urb->transfer_buffer_length = SIZEOUTBUF; | |
0b20d613 | 504 | urb->dev = comedi_to_usb_dev(dev); |
4bf21fa4 | 505 | urb->status = 0; |
e057288f HS |
506 | if (devpriv->ao_cmd_running) { |
507 | if (devpriv->high_speed) | |
508 | urb->interval = 8; /* uframes */ | |
509 | else | |
510 | urb->interval = 1; /* frames */ | |
4bf21fa4 BP |
511 | urb->number_of_packets = 1; |
512 | urb->iso_frame_desc[0].offset = 0; | |
513 | urb->iso_frame_desc[0].length = SIZEOUTBUF; | |
514 | urb->iso_frame_desc[0].status = 0; | |
4aa3a823 | 515 | ret = usb_submit_urb(urb, GFP_ATOMIC); |
4274ea02 | 516 | if (ret < 0) { |
e057288f HS |
517 | dev_err(dev->class_dev, |
518 | "ao urb resubm failed in int-cont. ret=%d", | |
519 | ret); | |
8fa07567 | 520 | if (ret == EL2NSYNC) |
e057288f HS |
521 | dev_err(dev->class_dev, |
522 | "buggy USB host controller or bug in IRQ handling!\n"); | |
8fa07567 | 523 | |
4bf21fa4 BP |
524 | s->async->events |= COMEDI_CB_EOA; |
525 | s->async->events |= COMEDI_CB_ERROR; | |
e057288f | 526 | comedi_event(dev, s); |
e54fb9c1 | 527 | /* don't do an unlink here */ |
7c8ed94e | 528 | usbdux_ao_stop(dev, 0); |
4bf21fa4 BP |
529 | } |
530 | } | |
531 | } | |
532 | ||
d0b31b8b HS |
533 | static int usbdux_submit_urbs(struct comedi_device *dev, |
534 | struct urb **urbs, int num_urbs, | |
535 | int input_urb) | |
4bf21fa4 | 536 | { |
0b20d613 | 537 | struct usb_device *usb = comedi_to_usb_dev(dev); |
865d9eed HS |
538 | struct usbdux_private *devpriv = dev->private; |
539 | struct urb *urb; | |
540 | int ret; | |
541 | int i; | |
8fa07567 | 542 | |
4bf21fa4 | 543 | /* Submit all URBs and start the transfer on the bus */ |
d0b31b8b HS |
544 | for (i = 0; i < num_urbs; i++) { |
545 | urb = urbs[i]; | |
4274ea02 | 546 | |
e54fb9c1 | 547 | /* in case of a resubmission after an unlink... */ |
d0b31b8b HS |
548 | if (input_urb) |
549 | urb->interval = devpriv->ai_interval; | |
6754698b | 550 | urb->context = dev; |
0b20d613 | 551 | urb->dev = usb; |
6754698b HS |
552 | urb->status = 0; |
553 | urb->transfer_flags = URB_ISO_ASAP; | |
554 | ||
555 | ret = usb_submit_urb(urb, GFP_ATOMIC); | |
556 | if (ret) | |
557 | return ret; | |
4bf21fa4 BP |
558 | } |
559 | return 0; | |
560 | } | |
561 | ||
0a85b6f0 MT |
562 | static int usbdux_ai_cmdtest(struct comedi_device *dev, |
563 | struct comedi_subdevice *s, struct comedi_cmd *cmd) | |
4bf21fa4 | 564 | { |
40f1a5ab | 565 | struct usbdux_private *this_usbduxsub = dev->private; |
27020ffe | 566 | int err = 0, i; |
b74e5f56 | 567 | unsigned int tmp_timer; |
4274ea02 | 568 | |
27020ffe | 569 | /* Step 1 : check if triggers are trivially valid */ |
4bf21fa4 | 570 | |
27020ffe HS |
571 | err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT); |
572 | err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER); | |
573 | err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW); | |
574 | err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); | |
575 | err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
4bf21fa4 BP |
576 | |
577 | if (err) | |
578 | return 1; | |
579 | ||
27020ffe HS |
580 | /* Step 2a : make sure trigger sources are unique */ |
581 | ||
582 | err |= cfc_check_trigger_is_unique(cmd->start_src); | |
583 | err |= cfc_check_trigger_is_unique(cmd->stop_src); | |
584 | ||
585 | /* Step 2b : and mutually compatible */ | |
4bf21fa4 BP |
586 | |
587 | if (err) | |
588 | return 2; | |
589 | ||
f4d36c7a | 590 | /* Step 3: check if arguments are trivially valid */ |
4bf21fa4 | 591 | |
f4d36c7a HS |
592 | err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); |
593 | ||
594 | if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */ | |
595 | err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0); | |
4bf21fa4 BP |
596 | |
597 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
598 | if (this_usbduxsub->high_speed) { | |
e54fb9c1 GKH |
599 | /* |
600 | * In high speed mode microframes are possible. | |
601 | * However, during one microframe we can roughly | |
602 | * sample one channel. Thus, the more channels | |
603 | * are in the channel list the more time we need. | |
604 | */ | |
4bf21fa4 | 605 | i = 1; |
e54fb9c1 | 606 | /* find a power of 2 for the number of channels */ |
4274ea02 | 607 | while (i < (cmd->chanlist_len)) |
4bf21fa4 | 608 | i = i * 2; |
4274ea02 | 609 | |
f4d36c7a HS |
610 | err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, |
611 | 1000000 / 8 * i); | |
e54fb9c1 GKH |
612 | /* now calc the real sampling rate with all the |
613 | * rounding errors */ | |
b74e5f56 | 614 | tmp_timer = |
0a85b6f0 MT |
615 | ((unsigned int)(cmd->scan_begin_arg / 125000)) * |
616 | 125000; | |
e54fb9c1 GKH |
617 | } else { |
618 | /* full speed */ | |
619 | /* 1kHz scans every USB frame */ | |
f4d36c7a HS |
620 | err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, |
621 | 1000000); | |
4274ea02 GKH |
622 | /* |
623 | * calc the real sampling rate with the rounding errors | |
624 | */ | |
b74e5f56 | 625 | tmp_timer = ((unsigned int)(cmd->scan_begin_arg / |
0a85b6f0 | 626 | 1000000)) * 1000000; |
4bf21fa4 | 627 | } |
f4d36c7a | 628 | err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, |
b74e5f56 | 629 | tmp_timer); |
4bf21fa4 BP |
630 | } |
631 | ||
f4d36c7a HS |
632 | err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); |
633 | ||
4bf21fa4 BP |
634 | if (cmd->stop_src == TRIG_COUNT) { |
635 | /* any count is allowed */ | |
636 | } else { | |
637 | /* TRIG_NONE */ | |
f4d36c7a | 638 | err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); |
4bf21fa4 BP |
639 | } |
640 | ||
641 | if (err) | |
642 | return 3; | |
643 | ||
644 | return 0; | |
645 | } | |
646 | ||
e54fb9c1 GKH |
647 | /* |
648 | * creates the ADC command for the MAX1271 | |
649 | * range is the range value from comedi | |
650 | */ | |
470180c6 | 651 | static uint8_t create_adc_command(unsigned int chan, unsigned int range) |
4bf21fa4 | 652 | { |
470180c6 IA |
653 | uint8_t p = (range <= 1); |
654 | uint8_t r = ((range % 2) == 0); | |
655 | ||
4bf21fa4 BP |
656 | return (chan << 4) | ((p == 1) << 2) | ((r == 1) << 3); |
657 | } | |
658 | ||
470180c6 | 659 | static int send_dux_commands(struct comedi_device *dev, unsigned int cmd_type) |
4bf21fa4 | 660 | { |
0b20d613 | 661 | struct usb_device *usb = comedi_to_usb_dev(dev); |
49cc49dd | 662 | struct usbdux_private *devpriv = dev->private; |
91aa6b21 | 663 | int nsent; |
4bf21fa4 | 664 | |
49cc49dd | 665 | devpriv->dux_commands[0] = cmd_type; |
91aa6b21 | 666 | |
a3ef101f | 667 | return usb_bulk_msg(usb, usb_sndbulkpipe(usb, 1), |
49cc49dd | 668 | devpriv->dux_commands, SIZEOFDUXBUFFER, |
91aa6b21 | 669 | &nsent, BULK_TIMEOUT); |
4bf21fa4 BP |
670 | } |
671 | ||
470180c6 | 672 | static int receive_dux_commands(struct comedi_device *dev, unsigned int command) |
4bf21fa4 | 673 | { |
0b20d613 | 674 | struct usb_device *usb = comedi_to_usb_dev(dev); |
fb908568 | 675 | struct usbdux_private *devpriv = dev->private; |
fb908568 | 676 | int ret; |
4bf21fa4 BP |
677 | int nrec; |
678 | int i; | |
679 | ||
680 | for (i = 0; i < RETRIES; i++) { | |
a3ef101f | 681 | ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, 8), |
c79bc875 | 682 | devpriv->insn_buf, SIZEINSNBUF, |
ea25371a | 683 | &nrec, BULK_TIMEOUT); |
fb908568 HS |
684 | if (ret < 0) |
685 | return ret; | |
c79bc875 | 686 | if (le16_to_cpu(devpriv->insn_buf[0]) == command) |
fb908568 | 687 | return ret; |
4bf21fa4 | 688 | } |
fb908568 | 689 | /* command not received */ |
4bf21fa4 BP |
690 | return -EFAULT; |
691 | } | |
692 | ||
0a85b6f0 | 693 | static int usbdux_ai_inttrig(struct comedi_device *dev, |
e5cb2f94 HS |
694 | struct comedi_subdevice *s, |
695 | unsigned int trignum) | |
4bf21fa4 | 696 | { |
e5cb2f94 HS |
697 | struct usbdux_private *devpriv = dev->private; |
698 | int ret = -EINVAL; | |
4274ea02 | 699 | |
e5cb2f94 | 700 | down(&devpriv->sem); |
4bf21fa4 | 701 | |
e5cb2f94 HS |
702 | if (trignum != 0) |
703 | goto ai_trig_exit; | |
704 | ||
705 | if (!devpriv->ai_cmd_running) { | |
706 | devpriv->ai_cmd_running = 1; | |
d0b31b8b HS |
707 | ret = usbdux_submit_urbs(dev, devpriv->ai_urbs, |
708 | devpriv->n_ai_urbs, 1); | |
4bf21fa4 | 709 | if (ret < 0) { |
e5cb2f94 HS |
710 | devpriv->ai_cmd_running = 0; |
711 | goto ai_trig_exit; | |
4bf21fa4 BP |
712 | } |
713 | s->async->inttrig = NULL; | |
e5cb2f94 HS |
714 | } else { |
715 | ret = -EBUSY; | |
4bf21fa4 | 716 | } |
e5cb2f94 HS |
717 | |
718 | ai_trig_exit: | |
719 | up(&devpriv->sem); | |
720 | return ret; | |
4bf21fa4 BP |
721 | } |
722 | ||
34c43922 | 723 | static int usbdux_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
4bf21fa4 | 724 | { |
9a79dfce | 725 | struct usbdux_private *devpriv = dev->private; |
ea6d0d4c | 726 | struct comedi_cmd *cmd = &s->async->cmd; |
9a79dfce HS |
727 | int len = cmd->chanlist_len; |
728 | int ret = -EBUSY; | |
729 | int i; | |
8fa07567 GKH |
730 | |
731 | /* block other CPUs from starting an ai_cmd */ | |
9a79dfce HS |
732 | down(&devpriv->sem); |
733 | ||
734 | if (devpriv->ai_cmd_running) | |
735 | goto ai_cmd_exit; | |
736 | ||
25985edc | 737 | /* set current channel of the running acquisition to zero */ |
4bf21fa4 BP |
738 | s->async->cur_chan = 0; |
739 | ||
9a79dfce HS |
740 | devpriv->dux_commands[1] = len; |
741 | for (i = 0; i < len; ++i) { | |
742 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); | |
743 | unsigned int range = CR_RANGE(cmd->chanlist[i]); | |
744 | ||
9a79dfce | 745 | devpriv->dux_commands[i + 2] = create_adc_command(chan, range); |
4bf21fa4 BP |
746 | } |
747 | ||
757fbc2a | 748 | ret = send_dux_commands(dev, USBDUX_CMD_MULT_AI); |
9a79dfce HS |
749 | if (ret < 0) |
750 | goto ai_cmd_exit; | |
751 | ||
752 | if (devpriv->high_speed) { | |
8fa07567 GKH |
753 | /* |
754 | * every channel gets a time window of 125us. Thus, if we | |
755 | * sample all 8 channels we need 1ms. If we sample only one | |
756 | * channel we need only 125us | |
757 | */ | |
9a79dfce | 758 | devpriv->ai_interval = 1; |
8fa07567 | 759 | /* find a power of 2 for the interval */ |
9a79dfce HS |
760 | while (devpriv->ai_interval < len) |
761 | devpriv->ai_interval *= 2; | |
762 | ||
763 | devpriv->ai_timer = cmd->scan_begin_arg / | |
764 | (125000 * devpriv->ai_interval); | |
4bf21fa4 | 765 | } else { |
8fa07567 | 766 | /* interval always 1ms */ |
9a79dfce HS |
767 | devpriv->ai_interval = 1; |
768 | devpriv->ai_timer = cmd->scan_begin_arg / 1000000; | |
4bf21fa4 | 769 | } |
9a79dfce HS |
770 | if (devpriv->ai_timer < 1) { |
771 | ret = -EINVAL; | |
772 | goto ai_cmd_exit; | |
4bf21fa4 | 773 | } |
9a79dfce HS |
774 | |
775 | devpriv->ai_counter = devpriv->ai_timer; | |
4bf21fa4 BP |
776 | |
777 | if (cmd->stop_src == TRIG_COUNT) { | |
e54fb9c1 | 778 | /* data arrives as one packet */ |
9a79dfce HS |
779 | devpriv->ai_sample_count = cmd->stop_arg; |
780 | devpriv->ai_continous = 0; | |
4bf21fa4 | 781 | } else { |
25985edc | 782 | /* continous acquisition */ |
9a79dfce HS |
783 | devpriv->ai_continous = 1; |
784 | devpriv->ai_sample_count = 0; | |
4bf21fa4 BP |
785 | } |
786 | ||
787 | if (cmd->start_src == TRIG_NOW) { | |
e54fb9c1 | 788 | /* enable this acquisition operation */ |
9a79dfce | 789 | devpriv->ai_cmd_running = 1; |
d0b31b8b HS |
790 | ret = usbdux_submit_urbs(dev, devpriv->ai_urbs, |
791 | devpriv->n_ai_urbs, 1); | |
4bf21fa4 | 792 | if (ret < 0) { |
9a79dfce | 793 | devpriv->ai_cmd_running = 0; |
e54fb9c1 | 794 | /* fixme: unlink here?? */ |
9a79dfce | 795 | goto ai_cmd_exit; |
4bf21fa4 BP |
796 | } |
797 | s->async->inttrig = NULL; | |
798 | } else { | |
799 | /* TRIG_INT */ | |
e54fb9c1 GKH |
800 | /* don't enable the acquision operation */ |
801 | /* wait for an internal signal */ | |
4bf21fa4 BP |
802 | s->async->inttrig = usbdux_ai_inttrig; |
803 | } | |
9a79dfce HS |
804 | |
805 | ai_cmd_exit: | |
806 | up(&devpriv->sem); | |
807 | ||
808 | return ret; | |
4bf21fa4 BP |
809 | } |
810 | ||
811 | /* Mode 0 is used to get a single conversion on demand */ | |
0a85b6f0 MT |
812 | static int usbdux_ai_insn_read(struct comedi_device *dev, |
813 | struct comedi_subdevice *s, | |
a79b4cdb HS |
814 | struct comedi_insn *insn, |
815 | unsigned int *data) | |
4bf21fa4 | 816 | { |
a79b4cdb HS |
817 | struct usbdux_private *devpriv = dev->private; |
818 | unsigned int chan = CR_CHAN(insn->chanspec); | |
819 | unsigned int range = CR_RANGE(insn->chanspec); | |
820 | unsigned int val; | |
821 | int ret = -EBUSY; | |
4bf21fa4 | 822 | int i; |
4bf21fa4 | 823 | |
a79b4cdb | 824 | down(&devpriv->sem); |
c0e0c26e | 825 | |
a79b4cdb HS |
826 | if (devpriv->ai_cmd_running) |
827 | goto ai_read_exit; | |
4bf21fa4 | 828 | |
e54fb9c1 | 829 | /* set command for the first channel */ |
a79b4cdb | 830 | devpriv->dux_commands[1] = create_adc_command(chan, range); |
4bf21fa4 | 831 | |
e54fb9c1 | 832 | /* adc commands */ |
757fbc2a | 833 | ret = send_dux_commands(dev, USBDUX_CMD_SINGLE_AI); |
a79b4cdb HS |
834 | if (ret < 0) |
835 | goto ai_read_exit; | |
4bf21fa4 BP |
836 | |
837 | for (i = 0; i < insn->n; i++) { | |
757fbc2a | 838 | ret = receive_dux_commands(dev, USBDUX_CMD_SINGLE_AI); |
a79b4cdb HS |
839 | if (ret < 0) |
840 | goto ai_read_exit; | |
841 | ||
c79bc875 | 842 | val = le16_to_cpu(devpriv->insn_buf[1]); |
91891f7c HS |
843 | |
844 | /* bipolar data is two's-complement */ | |
845 | if (comedi_range_is_bipolar(s, range)) | |
846 | val ^= ((s->maxdata + 1) >> 1); | |
8fa07567 | 847 | |
a79b4cdb | 848 | data[i] = val; |
4bf21fa4 | 849 | } |
a79b4cdb HS |
850 | |
851 | ai_read_exit: | |
852 | up(&devpriv->sem); | |
853 | ||
854 | return ret ? ret : insn->n; | |
4bf21fa4 BP |
855 | } |
856 | ||
0a85b6f0 MT |
857 | static int usbdux_ao_insn_read(struct comedi_device *dev, |
858 | struct comedi_subdevice *s, | |
818782c8 HS |
859 | struct comedi_insn *insn, |
860 | unsigned int *data) | |
4bf21fa4 | 861 | { |
818782c8 HS |
862 | struct usbdux_private *devpriv = dev->private; |
863 | unsigned int chan = CR_CHAN(insn->chanspec); | |
4bf21fa4 | 864 | int i; |
4bf21fa4 | 865 | |
818782c8 | 866 | down(&devpriv->sem); |
8fa07567 | 867 | for (i = 0; i < insn->n; i++) |
a998a3db | 868 | data[i] = devpriv->ao_readback[chan]; |
818782c8 | 869 | up(&devpriv->sem); |
8fa07567 | 870 | |
818782c8 | 871 | return insn->n; |
4bf21fa4 BP |
872 | } |
873 | ||
0a85b6f0 MT |
874 | static int usbdux_ao_insn_write(struct comedi_device *dev, |
875 | struct comedi_subdevice *s, | |
37c1d1ec HS |
876 | struct comedi_insn *insn, |
877 | unsigned int *data) | |
4bf21fa4 | 878 | { |
37c1d1ec HS |
879 | struct usbdux_private *devpriv = dev->private; |
880 | unsigned int chan = CR_CHAN(insn->chanspec); | |
a998a3db | 881 | unsigned int val = devpriv->ao_readback[chan]; |
470180c6 | 882 | uint16_t *p = (uint16_t *)&devpriv->dux_commands[2]; |
37c1d1ec HS |
883 | int ret = -EBUSY; |
884 | int i; | |
4bf21fa4 | 885 | |
37c1d1ec | 886 | down(&devpriv->sem); |
8fa07567 | 887 | |
37c1d1ec HS |
888 | if (devpriv->ao_cmd_running) |
889 | goto ao_write_exit; | |
890 | ||
891 | /* number of channels: 1 */ | |
892 | devpriv->dux_commands[1] = 1; | |
893 | /* channel number */ | |
894 | devpriv->dux_commands[4] = chan << 6; | |
4bf21fa4 BP |
895 | |
896 | for (i = 0; i < insn->n; i++) { | |
37c1d1ec HS |
897 | val = data[i]; |
898 | ||
e54fb9c1 | 899 | /* one 16 bit value */ |
37c1d1ec HS |
900 | *p = cpu_to_le16(val); |
901 | ||
757fbc2a | 902 | ret = send_dux_commands(dev, USBDUX_CMD_AO); |
37c1d1ec HS |
903 | if (ret < 0) |
904 | goto ao_write_exit; | |
4bf21fa4 | 905 | } |
a998a3db | 906 | devpriv->ao_readback[chan] = val; |
4bf21fa4 | 907 | |
37c1d1ec HS |
908 | ao_write_exit: |
909 | up(&devpriv->sem); | |
910 | ||
911 | return ret ? ret : insn->n; | |
4bf21fa4 BP |
912 | } |
913 | ||
0a85b6f0 | 914 | static int usbdux_ao_inttrig(struct comedi_device *dev, |
f994282d HS |
915 | struct comedi_subdevice *s, |
916 | unsigned int trignum) | |
4bf21fa4 | 917 | { |
f994282d HS |
918 | struct usbdux_private *devpriv = dev->private; |
919 | int ret = -EINVAL; | |
4bf21fa4 | 920 | |
f994282d | 921 | down(&devpriv->sem); |
8fa07567 | 922 | |
f994282d HS |
923 | if (trignum != 0) |
924 | goto ao_trig_exit; | |
925 | ||
926 | if (!devpriv->ao_cmd_running) { | |
927 | devpriv->ao_cmd_running = 1; | |
d0b31b8b HS |
928 | ret = usbdux_submit_urbs(dev, devpriv->ao_urbs, |
929 | devpriv->n_ao_urbs, 0); | |
4bf21fa4 | 930 | if (ret < 0) { |
f994282d HS |
931 | devpriv->ao_cmd_running = 0; |
932 | goto ao_trig_exit; | |
4bf21fa4 BP |
933 | } |
934 | s->async->inttrig = NULL; | |
f994282d HS |
935 | } else { |
936 | ret = -EBUSY; | |
4bf21fa4 | 937 | } |
f994282d HS |
938 | |
939 | ao_trig_exit: | |
940 | up(&devpriv->sem); | |
941 | return ret; | |
4bf21fa4 BP |
942 | } |
943 | ||
0a85b6f0 MT |
944 | static int usbdux_ao_cmdtest(struct comedi_device *dev, |
945 | struct comedi_subdevice *s, struct comedi_cmd *cmd) | |
4bf21fa4 | 946 | { |
40f1a5ab | 947 | struct usbdux_private *this_usbduxsub = dev->private; |
27020ffe HS |
948 | int err = 0; |
949 | unsigned int flags; | |
4bf21fa4 | 950 | |
8fa07567 | 951 | if (!this_usbduxsub) |
4bf21fa4 | 952 | return -EFAULT; |
8fa07567 | 953 | |
27020ffe HS |
954 | /* Step 1 : check if triggers are trivially valid */ |
955 | ||
956 | err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT); | |
4bf21fa4 | 957 | |
4bf21fa4 | 958 | if (0) { /* (this_usbduxsub->high_speed) */ |
e54fb9c1 | 959 | /* the sampling rate is set by the coversion rate */ |
27020ffe | 960 | flags = TRIG_FOLLOW; |
4bf21fa4 | 961 | } else { |
e54fb9c1 | 962 | /* start a new scan (output at once) with a timer */ |
27020ffe | 963 | flags = TRIG_TIMER; |
4bf21fa4 | 964 | } |
27020ffe | 965 | err |= cfc_check_trigger_src(&cmd->scan_begin_src, flags); |
4bf21fa4 | 966 | |
4bf21fa4 | 967 | if (0) { /* (this_usbduxsub->high_speed) */ |
4274ea02 | 968 | /* |
27020ffe HS |
969 | * in usb-2.0 only one conversion it transmitted |
970 | * but with 8kHz/n | |
4274ea02 | 971 | */ |
27020ffe | 972 | flags = TRIG_TIMER; |
4bf21fa4 | 973 | } else { |
27020ffe HS |
974 | /* |
975 | * all conversion events happen simultaneously with | |
976 | * a rate of 1kHz/n | |
977 | */ | |
978 | flags = TRIG_NOW; | |
4bf21fa4 | 979 | } |
27020ffe | 980 | err |= cfc_check_trigger_src(&cmd->convert_src, flags); |
4bf21fa4 | 981 | |
27020ffe HS |
982 | err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); |
983 | err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
4bf21fa4 BP |
984 | |
985 | if (err) | |
986 | return 1; | |
987 | ||
27020ffe HS |
988 | /* Step 2a : make sure trigger sources are unique */ |
989 | ||
990 | err |= cfc_check_trigger_is_unique(cmd->start_src); | |
991 | err |= cfc_check_trigger_is_unique(cmd->stop_src); | |
992 | ||
993 | /* Step 2b : and mutually compatible */ | |
4bf21fa4 BP |
994 | |
995 | if (err) | |
996 | return 2; | |
997 | ||
f4d36c7a | 998 | /* Step 3: check if arguments are trivially valid */ |
4bf21fa4 | 999 | |
f4d36c7a | 1000 | err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); |
4bf21fa4 | 1001 | |
f4d36c7a HS |
1002 | if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */ |
1003 | err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0); | |
1004 | ||
1005 | if (cmd->scan_begin_src == TRIG_TIMER) | |
1006 | err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, | |
1007 | 1000000); | |
4bf21fa4 | 1008 | |
e54fb9c1 | 1009 | /* not used now, is for later use */ |
f4d36c7a HS |
1010 | if (cmd->convert_src == TRIG_TIMER) |
1011 | err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 125000); | |
4bf21fa4 | 1012 | |
f4d36c7a | 1013 | err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); |
4bf21fa4 BP |
1014 | |
1015 | if (cmd->stop_src == TRIG_COUNT) { | |
1016 | /* any count is allowed */ | |
1017 | } else { | |
1018 | /* TRIG_NONE */ | |
f4d36c7a | 1019 | err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); |
4bf21fa4 BP |
1020 | } |
1021 | ||
4bf21fa4 BP |
1022 | if (err) |
1023 | return 3; | |
1024 | ||
1025 | return 0; | |
1026 | } | |
1027 | ||
34c43922 | 1028 | static int usbdux_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
4bf21fa4 | 1029 | { |
2a226948 | 1030 | struct usbdux_private *devpriv = dev->private; |
ea6d0d4c | 1031 | struct comedi_cmd *cmd = &s->async->cmd; |
2a226948 HS |
1032 | int ret = -EBUSY; |
1033 | int i; | |
4bf21fa4 | 1034 | |
2a226948 | 1035 | down(&devpriv->sem); |
8fa07567 | 1036 | |
2a226948 HS |
1037 | if (devpriv->ao_cmd_running) |
1038 | goto ao_cmd_exit; | |
4bf21fa4 | 1039 | |
25985edc | 1040 | /* set current channel of the running acquisition to zero */ |
4bf21fa4 | 1041 | s->async->cur_chan = 0; |
2a226948 | 1042 | |
4bf21fa4 | 1043 | for (i = 0; i < cmd->chanlist_len; ++i) { |
2a226948 HS |
1044 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); |
1045 | ||
c5bbfe50 | 1046 | devpriv->ao_chanlist[i] = chan << 6; |
4bf21fa4 BP |
1047 | } |
1048 | ||
e54fb9c1 GKH |
1049 | /* we count in steps of 1ms (125us) */ |
1050 | /* 125us mode not used yet */ | |
2a226948 | 1051 | if (0) { /* (devpriv->high_speed) */ |
e54fb9c1 GKH |
1052 | /* 125us */ |
1053 | /* timing of the conversion itself: every 125 us */ | |
2a226948 | 1054 | devpriv->ao_timer = cmd->convert_arg / 125000; |
4bf21fa4 | 1055 | } else { |
e54fb9c1 GKH |
1056 | /* 1ms */ |
1057 | /* timing of the scan: we get all channels at once */ | |
2a226948 HS |
1058 | devpriv->ao_timer = cmd->scan_begin_arg / 1000000; |
1059 | if (devpriv->ao_timer < 1) { | |
1060 | ret = -EINVAL; | |
1061 | goto ao_cmd_exit; | |
4bf21fa4 BP |
1062 | } |
1063 | } | |
2a226948 HS |
1064 | |
1065 | devpriv->ao_counter = devpriv->ao_timer; | |
4bf21fa4 BP |
1066 | |
1067 | if (cmd->stop_src == TRIG_COUNT) { | |
25985edc | 1068 | /* not continuous */ |
e54fb9c1 GKH |
1069 | /* counter */ |
1070 | /* high speed also scans everything at once */ | |
2a226948 HS |
1071 | if (0) { /* (devpriv->high_speed) */ |
1072 | devpriv->ao_sample_count = cmd->stop_arg * | |
1073 | cmd->scan_end_arg; | |
4bf21fa4 | 1074 | } else { |
e54fb9c1 GKH |
1075 | /* there's no scan as the scan has been */ |
1076 | /* perf inside the FX2 */ | |
1077 | /* data arrives as one packet */ | |
2a226948 | 1078 | devpriv->ao_sample_count = cmd->stop_arg; |
4bf21fa4 | 1079 | } |
2a226948 | 1080 | devpriv->ao_continous = 0; |
4bf21fa4 | 1081 | } else { |
25985edc | 1082 | /* continous acquisition */ |
2a226948 HS |
1083 | devpriv->ao_continous = 1; |
1084 | devpriv->ao_sample_count = 0; | |
4bf21fa4 BP |
1085 | } |
1086 | ||
1087 | if (cmd->start_src == TRIG_NOW) { | |
e54fb9c1 | 1088 | /* enable this acquisition operation */ |
2a226948 | 1089 | devpriv->ao_cmd_running = 1; |
d0b31b8b HS |
1090 | ret = usbdux_submit_urbs(dev, devpriv->ao_urbs, |
1091 | devpriv->n_ao_urbs, 0); | |
4bf21fa4 | 1092 | if (ret < 0) { |
2a226948 | 1093 | devpriv->ao_cmd_running = 0; |
e54fb9c1 | 1094 | /* fixme: unlink here?? */ |
2a226948 | 1095 | goto ao_cmd_exit; |
4bf21fa4 BP |
1096 | } |
1097 | s->async->inttrig = NULL; | |
1098 | } else { | |
1099 | /* TRIG_INT */ | |
e54fb9c1 GKH |
1100 | /* submit the urbs later */ |
1101 | /* wait for an internal signal */ | |
4bf21fa4 BP |
1102 | s->async->inttrig = usbdux_ao_inttrig; |
1103 | } | |
1104 | ||
2a226948 HS |
1105 | ao_cmd_exit: |
1106 | up(&devpriv->sem); | |
1107 | ||
1108 | return ret; | |
4bf21fa4 BP |
1109 | } |
1110 | ||
0a85b6f0 MT |
1111 | static int usbdux_dio_insn_config(struct comedi_device *dev, |
1112 | struct comedi_subdevice *s, | |
fc110df6 HS |
1113 | struct comedi_insn *insn, |
1114 | unsigned int *data) | |
4bf21fa4 | 1115 | { |
ddf62f2c | 1116 | int ret; |
4bf21fa4 | 1117 | |
ddf62f2c HS |
1118 | ret = comedi_dio_insn_config(dev, s, insn, data, 0); |
1119 | if (ret) | |
1120 | return ret; | |
fc110df6 HS |
1121 | |
1122 | /* | |
1123 | * We don't tell the firmware here as it would take 8 frames | |
1124 | * to submit the information. We do it in the insn_bits. | |
1125 | */ | |
4bf21fa4 BP |
1126 | return insn->n; |
1127 | } | |
1128 | ||
0a85b6f0 MT |
1129 | static int usbdux_dio_insn_bits(struct comedi_device *dev, |
1130 | struct comedi_subdevice *s, | |
81a9bdaa HS |
1131 | struct comedi_insn *insn, |
1132 | unsigned int *data) | |
4bf21fa4 BP |
1133 | { |
1134 | ||
81a9bdaa | 1135 | struct usbdux_private *devpriv = dev->private; |
81a9bdaa | 1136 | int ret; |
4bf21fa4 | 1137 | |
81a9bdaa | 1138 | down(&devpriv->sem); |
8fa07567 | 1139 | |
77e9487b | 1140 | comedi_dio_update_state(s, data); |
4bf21fa4 | 1141 | |
77e9487b | 1142 | /* Always update the hardware. See the (*insn_config). */ |
81a9bdaa HS |
1143 | devpriv->dux_commands[1] = s->io_bits; |
1144 | devpriv->dux_commands[2] = s->state; | |
4bf21fa4 | 1145 | |
81a9bdaa HS |
1146 | /* |
1147 | * This command also tells the firmware to return | |
1148 | * the digital input lines. | |
1149 | */ | |
757fbc2a | 1150 | ret = send_dux_commands(dev, USBDUX_CMD_DIO_BITS); |
81a9bdaa HS |
1151 | if (ret < 0) |
1152 | goto dio_exit; | |
757fbc2a | 1153 | ret = receive_dux_commands(dev, USBDUX_CMD_DIO_BITS); |
81a9bdaa HS |
1154 | if (ret < 0) |
1155 | goto dio_exit; | |
4bf21fa4 | 1156 | |
c79bc875 | 1157 | data[1] = le16_to_cpu(devpriv->insn_buf[1]); |
81a9bdaa HS |
1158 | |
1159 | dio_exit: | |
1160 | up(&devpriv->sem); | |
1161 | ||
1162 | return ret ? ret : insn->n; | |
4bf21fa4 BP |
1163 | } |
1164 | ||
0a85b6f0 MT |
1165 | static int usbdux_counter_read(struct comedi_device *dev, |
1166 | struct comedi_subdevice *s, | |
48967d4f HS |
1167 | struct comedi_insn *insn, |
1168 | unsigned int *data) | |
4bf21fa4 | 1169 | { |
48967d4f HS |
1170 | struct usbdux_private *devpriv = dev->private; |
1171 | unsigned int chan = CR_CHAN(insn->chanspec); | |
1172 | int ret = 0; | |
1173 | int i; | |
4bf21fa4 | 1174 | |
48967d4f | 1175 | down(&devpriv->sem); |
4bf21fa4 | 1176 | |
48967d4f | 1177 | for (i = 0; i < insn->n; i++) { |
757fbc2a | 1178 | ret = send_dux_commands(dev, USBDUX_CMD_TIMER_RD); |
48967d4f HS |
1179 | if (ret < 0) |
1180 | goto counter_read_exit; | |
757fbc2a | 1181 | ret = receive_dux_commands(dev, USBDUX_CMD_TIMER_RD); |
48967d4f HS |
1182 | if (ret < 0) |
1183 | goto counter_read_exit; | |
4bf21fa4 | 1184 | |
c79bc875 | 1185 | data[i] = le16_to_cpu(devpriv->insn_buf[chan + 1]); |
4bf21fa4 BP |
1186 | } |
1187 | ||
48967d4f HS |
1188 | counter_read_exit: |
1189 | up(&devpriv->sem); | |
1190 | ||
1191 | return ret ? ret : insn->n; | |
4bf21fa4 BP |
1192 | } |
1193 | ||
0a85b6f0 MT |
1194 | static int usbdux_counter_write(struct comedi_device *dev, |
1195 | struct comedi_subdevice *s, | |
f2929618 HS |
1196 | struct comedi_insn *insn, |
1197 | unsigned int *data) | |
4bf21fa4 | 1198 | { |
f2929618 HS |
1199 | struct usbdux_private *devpriv = dev->private; |
1200 | unsigned int chan = CR_CHAN(insn->chanspec); | |
470180c6 | 1201 | uint16_t *p = (uint16_t *)&devpriv->dux_commands[2]; |
f2929618 HS |
1202 | int ret = 0; |
1203 | int i; | |
4bf21fa4 | 1204 | |
f2929618 | 1205 | down(&devpriv->sem); |
4bf21fa4 | 1206 | |
f2929618 HS |
1207 | devpriv->dux_commands[1] = chan; |
1208 | ||
1209 | for (i = 0; i < insn->n; i++) { | |
1210 | *p = cpu_to_le16(data[i]); | |
4bf21fa4 | 1211 | |
757fbc2a | 1212 | ret = send_dux_commands(dev, USBDUX_CMD_TIMER_WR); |
f2929618 HS |
1213 | if (ret < 0) |
1214 | break; | |
4bf21fa4 BP |
1215 | } |
1216 | ||
f2929618 | 1217 | up(&devpriv->sem); |
4bf21fa4 | 1218 | |
f2929618 | 1219 | return ret ? ret : insn->n; |
4bf21fa4 BP |
1220 | } |
1221 | ||
0a85b6f0 MT |
1222 | static int usbdux_counter_config(struct comedi_device *dev, |
1223 | struct comedi_subdevice *s, | |
90035c08 | 1224 | struct comedi_insn *insn, unsigned int *data) |
4bf21fa4 | 1225 | { |
e54fb9c1 | 1226 | /* nothing to do so far */ |
4bf21fa4 BP |
1227 | return 2; |
1228 | } | |
1229 | ||
3c50bbb7 | 1230 | static void usbduxsub_unlink_pwm_urbs(struct comedi_device *dev) |
4bf21fa4 | 1231 | { |
3c50bbb7 | 1232 | struct usbdux_private *devpriv = dev->private; |
4bf21fa4 | 1233 | |
c79bc875 | 1234 | usb_kill_urb(devpriv->pwm_urb); |
4bf21fa4 BP |
1235 | } |
1236 | ||
3c50bbb7 | 1237 | static void usbdux_pwm_stop(struct comedi_device *dev, int do_unlink) |
4bf21fa4 | 1238 | { |
38f06835 | 1239 | struct usbdux_private *devpriv = dev->private; |
4bf21fa4 | 1240 | |
8fa07567 | 1241 | if (do_unlink) |
3c50bbb7 | 1242 | usbduxsub_unlink_pwm_urbs(dev); |
8fa07567 | 1243 | |
38f06835 | 1244 | devpriv->pwm_cmd_running = 0; |
4bf21fa4 BP |
1245 | } |
1246 | ||
0a85b6f0 MT |
1247 | static int usbdux_pwm_cancel(struct comedi_device *dev, |
1248 | struct comedi_subdevice *s) | |
4bf21fa4 | 1249 | { |
96ca3704 HS |
1250 | struct usbdux_private *devpriv = dev->private; |
1251 | int ret; | |
1252 | ||
1253 | down(&devpriv->sem); | |
8fa07567 | 1254 | /* unlink only if it is really running */ |
3c50bbb7 | 1255 | usbdux_pwm_stop(dev, devpriv->pwm_cmd_running); |
757fbc2a | 1256 | ret = send_dux_commands(dev, USBDUX_CMD_PWM_OFF); |
96ca3704 HS |
1257 | up(&devpriv->sem); |
1258 | ||
1259 | return ret; | |
4bf21fa4 BP |
1260 | } |
1261 | ||
4bf21fa4 BP |
1262 | static void usbduxsub_pwm_irq(struct urb *urb) |
1263 | { | |
c9f3363a HS |
1264 | struct comedi_device *dev = urb->context; |
1265 | struct usbdux_private *devpriv = dev->private; | |
4bf21fa4 | 1266 | int ret; |
4bf21fa4 BP |
1267 | |
1268 | switch (urb->status) { | |
1269 | case 0: | |
1270 | /* success */ | |
1271 | break; | |
1272 | ||
4bf21fa4 BP |
1273 | case -ECONNRESET: |
1274 | case -ENOENT: | |
1275 | case -ESHUTDOWN: | |
1276 | case -ECONNABORTED: | |
8fa07567 GKH |
1277 | /* |
1278 | * after an unlink command, unplug, ... etc | |
1279 | * no unlink needed here. Already shutting down. | |
1280 | */ | |
c9f3363a | 1281 | if (devpriv->pwm_cmd_running) |
38f06835 | 1282 | usbdux_pwm_stop(dev, 0); |
8fa07567 | 1283 | |
4bf21fa4 BP |
1284 | return; |
1285 | ||
4bf21fa4 | 1286 | default: |
8fa07567 | 1287 | /* a real error */ |
c9f3363a HS |
1288 | if (devpriv->pwm_cmd_running) { |
1289 | dev_err(dev->class_dev, | |
1290 | "Non-zero urb status received in pwm intr context: %d\n", | |
1291 | urb->status); | |
38f06835 | 1292 | usbdux_pwm_stop(dev, 0); |
4bf21fa4 BP |
1293 | } |
1294 | return; | |
1295 | } | |
1296 | ||
8fa07567 | 1297 | /* are we actually running? */ |
c9f3363a | 1298 | if (!devpriv->pwm_cmd_running) |
4bf21fa4 | 1299 | return; |
4bf21fa4 | 1300 | |
c79bc875 | 1301 | urb->transfer_buffer_length = devpriv->pwm_buf_sz; |
0b20d613 | 1302 | urb->dev = comedi_to_usb_dev(dev); |
4bf21fa4 | 1303 | urb->status = 0; |
c9f3363a | 1304 | if (devpriv->pwm_cmd_running) { |
4aa3a823 | 1305 | ret = usb_submit_urb(urb, GFP_ATOMIC); |
4274ea02 | 1306 | if (ret < 0) { |
c9f3363a HS |
1307 | dev_err(dev->class_dev, |
1308 | "pwm urb resubm failed in int-cont. ret=%d", | |
1309 | ret); | |
8fa07567 | 1310 | if (ret == EL2NSYNC) |
c9f3363a HS |
1311 | dev_err(dev->class_dev, |
1312 | "buggy USB host controller or bug in IRQ handling!\n"); | |
8fa07567 GKH |
1313 | |
1314 | /* don't do an unlink here */ | |
38f06835 | 1315 | usbdux_pwm_stop(dev, 0); |
4bf21fa4 BP |
1316 | } |
1317 | } | |
1318 | } | |
1319 | ||
5a80fa04 | 1320 | static int usbduxsub_submit_pwm_urbs(struct comedi_device *dev) |
4bf21fa4 | 1321 | { |
0b20d613 | 1322 | struct usb_device *usb = comedi_to_usb_dev(dev); |
5a80fa04 | 1323 | struct usbdux_private *devpriv = dev->private; |
c79bc875 | 1324 | struct urb *urb = devpriv->pwm_urb; |
8fa07567 | 1325 | |
c0e0c26e | 1326 | /* in case of a resubmission after an unlink... */ |
a3ef101f | 1327 | usb_fill_bulk_urb(urb, usb, usb_sndbulkpipe(usb, 4), |
5a80fa04 | 1328 | urb->transfer_buffer, |
c79bc875 | 1329 | devpriv->pwm_buf_sz, |
5a80fa04 HS |
1330 | usbduxsub_pwm_irq, |
1331 | dev); | |
1332 | ||
1333 | return usb_submit_urb(urb, GFP_ATOMIC); | |
4bf21fa4 BP |
1334 | } |
1335 | ||
0a85b6f0 | 1336 | static int usbdux_pwm_period(struct comedi_device *dev, |
9fe4df4d HS |
1337 | struct comedi_subdevice *s, |
1338 | unsigned int period) | |
4bf21fa4 | 1339 | { |
9fe4df4d | 1340 | struct usbdux_private *devpriv = dev->private; |
8fa07567 GKH |
1341 | int fx2delay = 255; |
1342 | ||
1343 | if (period < MIN_PWM_PERIOD) { | |
4bf21fa4 BP |
1344 | return -EAGAIN; |
1345 | } else { | |
9fe4df4d | 1346 | fx2delay = (period / (6 * 512 * 1000 / 33)) - 6; |
91aa6b21 | 1347 | if (fx2delay > 255) |
4bf21fa4 | 1348 | return -EAGAIN; |
4bf21fa4 | 1349 | } |
9fe4df4d HS |
1350 | devpriv->pwm_delay = fx2delay; |
1351 | devpriv->pwm_period = period; | |
91aa6b21 | 1352 | |
4bf21fa4 BP |
1353 | return 0; |
1354 | } | |
1355 | ||
0a85b6f0 MT |
1356 | static int usbdux_pwm_start(struct comedi_device *dev, |
1357 | struct comedi_subdevice *s) | |
4bf21fa4 | 1358 | { |
81e80134 HS |
1359 | struct usbdux_private *devpriv = dev->private; |
1360 | int ret = 0; | |
4bf21fa4 | 1361 | |
81e80134 | 1362 | down(&devpriv->sem); |
4bf21fa4 | 1363 | |
81e80134 HS |
1364 | if (devpriv->pwm_cmd_running) |
1365 | goto pwm_start_exit; | |
1366 | ||
c79bc875 | 1367 | devpriv->dux_commands[1] = devpriv->pwm_delay; |
757fbc2a | 1368 | ret = send_dux_commands(dev, USBDUX_CMD_PWM_ON); |
4274ea02 | 1369 | if (ret < 0) |
81e80134 | 1370 | goto pwm_start_exit; |
4274ea02 | 1371 | |
37523e84 | 1372 | /* initialise the buffer */ |
c79bc875 | 1373 | memset(devpriv->pwm_urb->transfer_buffer, 0, devpriv->pwm_buf_sz); |
4bf21fa4 | 1374 | |
81e80134 | 1375 | devpriv->pwm_cmd_running = 1; |
5a80fa04 | 1376 | ret = usbduxsub_submit_pwm_urbs(dev); |
81e80134 HS |
1377 | if (ret < 0) |
1378 | devpriv->pwm_cmd_running = 0; | |
1379 | ||
1380 | pwm_start_exit: | |
1381 | up(&devpriv->sem); | |
1382 | ||
1383 | return ret; | |
4bf21fa4 BP |
1384 | } |
1385 | ||
89878614 HS |
1386 | static void usbdux_pwm_pattern(struct comedi_device *dev, |
1387 | struct comedi_subdevice *s, | |
1388 | unsigned int chan, | |
1389 | unsigned int value, | |
1390 | unsigned int sign) | |
4bf21fa4 | 1391 | { |
3b2dc08b HS |
1392 | struct usbdux_private *devpriv = dev->private; |
1393 | char pwm_mask = (1 << chan); /* DIO bit for the PWM data */ | |
1394 | char sgn_mask = (16 << chan); /* DIO bit for the sign */ | |
1395 | char *buf = (char *)(devpriv->pwm_urb->transfer_buffer); | |
1396 | int szbuf = devpriv->pwm_buf_sz; | |
1397 | int i; | |
4274ea02 | 1398 | |
4bf21fa4 | 1399 | for (i = 0; i < szbuf; i++) { |
3b2dc08b HS |
1400 | char c = *buf; |
1401 | ||
1402 | c &= ~pwm_mask; | |
4bf21fa4 | 1403 | if (i < value) |
3b2dc08b HS |
1404 | c |= pwm_mask; |
1405 | if (!sign) | |
1406 | c &= ~sgn_mask; | |
1407 | else | |
1408 | c |= sgn_mask; | |
1409 | *buf++ = c; | |
4bf21fa4 | 1410 | } |
4bf21fa4 BP |
1411 | } |
1412 | ||
0a85b6f0 MT |
1413 | static int usbdux_pwm_write(struct comedi_device *dev, |
1414 | struct comedi_subdevice *s, | |
5c63f094 HS |
1415 | struct comedi_insn *insn, |
1416 | unsigned int *data) | |
4bf21fa4 | 1417 | { |
5c63f094 | 1418 | unsigned int chan = CR_CHAN(insn->chanspec); |
4bf21fa4 | 1419 | |
5c63f094 HS |
1420 | /* |
1421 | * It doesn't make sense to support more than one value here | |
1422 | * because it would just overwrite the PWM buffer. | |
1423 | */ | |
1424 | if (insn->n != 1) | |
4bf21fa4 | 1425 | return -EINVAL; |
4bf21fa4 | 1426 | |
4274ea02 | 1427 | /* |
5c63f094 HS |
1428 | * The sign is set via a special INSN only, this gives us 8 bits |
1429 | * for normal operation, sign is 0 by default. | |
4274ea02 | 1430 | */ |
89878614 HS |
1431 | usbdux_pwm_pattern(dev, s, chan, data[0], 0); |
1432 | ||
1433 | return insn->n; | |
4bf21fa4 BP |
1434 | } |
1435 | ||
0a85b6f0 MT |
1436 | static int usbdux_pwm_config(struct comedi_device *dev, |
1437 | struct comedi_subdevice *s, | |
893a2778 HS |
1438 | struct comedi_insn *insn, |
1439 | unsigned int *data) | |
4bf21fa4 | 1440 | { |
893a2778 HS |
1441 | struct usbdux_private *devpriv = dev->private; |
1442 | unsigned int chan = CR_CHAN(insn->chanspec); | |
1443 | ||
4bf21fa4 BP |
1444 | switch (data[0]) { |
1445 | case INSN_CONFIG_ARM: | |
8fa07567 GKH |
1446 | /* |
1447 | * if not zero the PWM is limited to a certain time which is | |
1448 | * not supported here | |
1449 | */ | |
1450 | if (data[1] != 0) | |
4bf21fa4 | 1451 | return -EINVAL; |
4bf21fa4 BP |
1452 | return usbdux_pwm_start(dev, s); |
1453 | case INSN_CONFIG_DISARM: | |
4bf21fa4 BP |
1454 | return usbdux_pwm_cancel(dev, s); |
1455 | case INSN_CONFIG_GET_PWM_STATUS: | |
893a2778 | 1456 | data[1] = devpriv->pwm_cmd_running; |
4bf21fa4 BP |
1457 | return 0; |
1458 | case INSN_CONFIG_PWM_SET_PERIOD: | |
8fa07567 | 1459 | return usbdux_pwm_period(dev, s, data[1]); |
4bf21fa4 | 1460 | case INSN_CONFIG_PWM_GET_PERIOD: |
893a2778 | 1461 | data[1] = devpriv->pwm_period; |
4bf21fa4 BP |
1462 | return 0; |
1463 | case INSN_CONFIG_PWM_SET_H_BRIDGE: | |
893a2778 HS |
1464 | /* |
1465 | * data[1] = value | |
1466 | * data[2] = sign (for a relay) | |
1467 | */ | |
89878614 HS |
1468 | usbdux_pwm_pattern(dev, s, chan, data[1], (data[2] != 0)); |
1469 | return 0; | |
4bf21fa4 | 1470 | case INSN_CONFIG_PWM_GET_H_BRIDGE: |
8fa07567 | 1471 | /* values are not kept in this driver, nothing to return here */ |
4bf21fa4 BP |
1472 | return -EINVAL; |
1473 | } | |
1474 | return -EINVAL; | |
1475 | } | |
1476 | ||
1731a826 HS |
1477 | static int usbdux_firmware_upload(struct comedi_device *dev, |
1478 | const u8 *data, size_t size, | |
1479 | unsigned long context) | |
1480 | { | |
1481 | struct usb_device *usb = comedi_to_usb_dev(dev); | |
1482 | uint8_t *buf; | |
1483 | uint8_t *tmp; | |
1484 | int ret; | |
1485 | ||
1486 | if (!data) | |
1487 | return 0; | |
1488 | ||
1489 | if (size > USBDUX_FIRMWARE_MAX_LEN) { | |
1490 | dev_err(dev->class_dev, | |
1491 | "usbdux firmware binary it too large for FX2.\n"); | |
1492 | return -ENOMEM; | |
1493 | } | |
1494 | ||
1495 | /* we generate a local buffer for the firmware */ | |
1496 | buf = kmemdup(data, size, GFP_KERNEL); | |
1497 | if (!buf) | |
1498 | return -ENOMEM; | |
1499 | ||
1500 | /* we need a malloc'ed buffer for usb_control_msg() */ | |
1501 | tmp = kmalloc(1, GFP_KERNEL); | |
1502 | if (!tmp) { | |
1503 | kfree(buf); | |
1504 | return -ENOMEM; | |
1505 | } | |
1506 | ||
1507 | /* stop the current firmware on the device */ | |
1508 | *tmp = 1; /* 7f92 to one */ | |
1509 | ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0), | |
1510 | USBDUX_FIRMWARE_CMD, | |
1511 | VENDOR_DIR_OUT, | |
1512 | USBDUX_CPU_CS, 0x0000, | |
1513 | tmp, 1, | |
1514 | BULK_TIMEOUT); | |
1515 | if (ret < 0) { | |
1516 | dev_err(dev->class_dev, "can not stop firmware\n"); | |
1517 | goto done; | |
1518 | } | |
1519 | ||
1520 | /* upload the new firmware to the device */ | |
1521 | ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0), | |
1522 | USBDUX_FIRMWARE_CMD, | |
1523 | VENDOR_DIR_OUT, | |
1524 | 0, 0x0000, | |
1525 | buf, size, | |
1526 | BULK_TIMEOUT); | |
1527 | if (ret < 0) { | |
1528 | dev_err(dev->class_dev, "firmware upload failed\n"); | |
1529 | goto done; | |
1530 | } | |
1531 | ||
1532 | /* start the new firmware on the device */ | |
1533 | *tmp = 0; /* 7f92 to zero */ | |
1534 | ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0), | |
1535 | USBDUX_FIRMWARE_CMD, | |
1536 | VENDOR_DIR_OUT, | |
1537 | USBDUX_CPU_CS, 0x0000, | |
1538 | tmp, 1, | |
1539 | BULK_TIMEOUT); | |
1540 | if (ret < 0) | |
1541 | dev_err(dev->class_dev, "can not start firmware\n"); | |
1542 | ||
1543 | done: | |
1544 | kfree(tmp); | |
1545 | kfree(buf); | |
1546 | return ret; | |
1547 | } | |
4bf21fa4 | 1548 | |
11642c65 | 1549 | static int usbdux_alloc_usb_buffers(struct comedi_device *dev) |
080bcc18 | 1550 | { |
0b20d613 | 1551 | struct usb_device *usb = comedi_to_usb_dev(dev); |
11642c65 | 1552 | struct usbdux_private *devpriv = dev->private; |
080bcc18 HS |
1553 | struct urb *urb; |
1554 | int i; | |
1555 | ||
080bcc18 | 1556 | devpriv->dux_commands = kzalloc(SIZEOFDUXBUFFER, GFP_KERNEL); |
c79bc875 | 1557 | devpriv->in_buf = kzalloc(SIZEINBUF, GFP_KERNEL); |
c79bc875 | 1558 | devpriv->insn_buf = kzalloc(SIZEINSNBUF, GFP_KERNEL); |
ded82ef9 | 1559 | devpriv->ai_urbs = kcalloc(devpriv->n_ai_urbs, sizeof(void *), |
c79bc875 | 1560 | GFP_KERNEL); |
ded82ef9 | 1561 | devpriv->ao_urbs = kcalloc(devpriv->n_ao_urbs, sizeof(void *), |
ef1e3c4a HS |
1562 | GFP_KERNEL); |
1563 | if (!devpriv->dux_commands || !devpriv->in_buf || !devpriv->insn_buf || | |
1564 | !devpriv->ai_urbs || !devpriv->ao_urbs) | |
080bcc18 HS |
1565 | return -ENOMEM; |
1566 | ||
c79bc875 | 1567 | for (i = 0; i < devpriv->n_ai_urbs; i++) { |
080bcc18 HS |
1568 | /* one frame: 1ms */ |
1569 | urb = usb_alloc_urb(1, GFP_KERNEL); | |
1570 | if (!urb) | |
1571 | return -ENOMEM; | |
c79bc875 | 1572 | devpriv->ai_urbs[i] = urb; |
080bcc18 | 1573 | |
0b20d613 | 1574 | urb->dev = usb; |
ef1e3c4a | 1575 | urb->context = dev; |
a3ef101f | 1576 | urb->pipe = usb_rcvisocpipe(usb, 6); |
080bcc18 HS |
1577 | urb->transfer_flags = URB_ISO_ASAP; |
1578 | urb->transfer_buffer = kzalloc(SIZEINBUF, GFP_KERNEL); | |
1579 | if (!urb->transfer_buffer) | |
1580 | return -ENOMEM; | |
1581 | ||
1582 | urb->complete = usbduxsub_ai_isoc_irq; | |
1583 | urb->number_of_packets = 1; | |
1584 | urb->transfer_buffer_length = SIZEINBUF; | |
1585 | urb->iso_frame_desc[0].offset = 0; | |
1586 | urb->iso_frame_desc[0].length = SIZEINBUF; | |
1587 | } | |
1588 | ||
c79bc875 | 1589 | for (i = 0; i < devpriv->n_ao_urbs; i++) { |
080bcc18 HS |
1590 | /* one frame: 1ms */ |
1591 | urb = usb_alloc_urb(1, GFP_KERNEL); | |
1592 | if (!urb) | |
1593 | return -ENOMEM; | |
c79bc875 | 1594 | devpriv->ao_urbs[i] = urb; |
080bcc18 | 1595 | |
0b20d613 | 1596 | urb->dev = usb; |
ef1e3c4a | 1597 | urb->context = dev; |
a3ef101f | 1598 | urb->pipe = usb_sndisocpipe(usb, 2); |
080bcc18 HS |
1599 | urb->transfer_flags = URB_ISO_ASAP; |
1600 | urb->transfer_buffer = kzalloc(SIZEOUTBUF, GFP_KERNEL); | |
1601 | if (!urb->transfer_buffer) | |
1602 | return -ENOMEM; | |
1603 | ||
1604 | urb->complete = usbduxsub_ao_isoc_irq; | |
1605 | urb->number_of_packets = 1; | |
1606 | urb->transfer_buffer_length = SIZEOUTBUF; | |
1607 | urb->iso_frame_desc[0].offset = 0; | |
1608 | urb->iso_frame_desc[0].length = SIZEOUTBUF; | |
1609 | if (devpriv->high_speed) | |
1610 | urb->interval = 8; /* uframes */ | |
1611 | else | |
1612 | urb->interval = 1; /* frames */ | |
1613 | } | |
1614 | ||
1615 | /* pwm */ | |
c79bc875 | 1616 | if (devpriv->pwm_buf_sz) { |
080bcc18 HS |
1617 | urb = usb_alloc_urb(0, GFP_KERNEL); |
1618 | if (!urb) | |
1619 | return -ENOMEM; | |
c79bc875 | 1620 | devpriv->pwm_urb = urb; |
080bcc18 HS |
1621 | |
1622 | /* max bulk ep size in high speed */ | |
c79bc875 | 1623 | urb->transfer_buffer = kzalloc(devpriv->pwm_buf_sz, |
080bcc18 HS |
1624 | GFP_KERNEL); |
1625 | if (!urb->transfer_buffer) | |
1626 | return -ENOMEM; | |
1627 | } | |
1628 | ||
1629 | return 0; | |
1630 | } | |
1631 | ||
71500b27 | 1632 | static void usbdux_free_usb_buffers(struct comedi_device *dev) |
8ff14ee8 | 1633 | { |
71500b27 | 1634 | struct usbdux_private *devpriv = dev->private; |
8ff14ee8 HS |
1635 | struct urb *urb; |
1636 | int i; | |
1637 | ||
c79bc875 | 1638 | urb = devpriv->pwm_urb; |
8ff14ee8 HS |
1639 | if (urb) { |
1640 | kfree(urb->transfer_buffer); | |
8ff14ee8 HS |
1641 | usb_free_urb(urb); |
1642 | } | |
c79bc875 HS |
1643 | if (devpriv->ao_urbs) { |
1644 | for (i = 0; i < devpriv->n_ao_urbs; i++) { | |
1645 | urb = devpriv->ao_urbs[i]; | |
8ff14ee8 HS |
1646 | if (urb) { |
1647 | kfree(urb->transfer_buffer); | |
8ff14ee8 HS |
1648 | usb_free_urb(urb); |
1649 | } | |
1650 | } | |
c79bc875 | 1651 | kfree(devpriv->ao_urbs); |
8ff14ee8 | 1652 | } |
c79bc875 HS |
1653 | if (devpriv->ai_urbs) { |
1654 | for (i = 0; i < devpriv->n_ai_urbs; i++) { | |
1655 | urb = devpriv->ai_urbs[i]; | |
8ff14ee8 HS |
1656 | if (urb) { |
1657 | kfree(urb->transfer_buffer); | |
8ff14ee8 HS |
1658 | usb_free_urb(urb); |
1659 | } | |
1660 | } | |
c79bc875 | 1661 | kfree(devpriv->ai_urbs); |
8ff14ee8 | 1662 | } |
c79bc875 HS |
1663 | kfree(devpriv->insn_buf); |
1664 | kfree(devpriv->in_buf); | |
8ff14ee8 | 1665 | kfree(devpriv->dux_commands); |
8ff14ee8 HS |
1666 | } |
1667 | ||
807e65b0 IA |
1668 | static int usbdux_auto_attach(struct comedi_device *dev, |
1669 | unsigned long context_unused) | |
3403cc0f | 1670 | { |
ef1ee8cf HS |
1671 | struct usb_interface *intf = comedi_to_usb_interface(dev); |
1672 | struct usb_device *usb = comedi_to_usb_dev(dev); | |
1673 | struct usbdux_private *devpriv; | |
cc84f4f9 | 1674 | struct comedi_subdevice *s; |
080bcc18 | 1675 | int ret; |
4bf21fa4 | 1676 | |
ef1ee8cf HS |
1677 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
1678 | if (!devpriv) | |
1679 | return -ENOMEM; | |
4bf21fa4 | 1680 | |
da903590 | 1681 | sema_init(&devpriv->sem, 1); |
4bf21fa4 | 1682 | |
ef1ee8cf | 1683 | usb_set_intfdata(intf, devpriv); |
4bf21fa4 | 1684 | |
0b20d613 | 1685 | devpriv->high_speed = (usb->speed == USB_SPEED_HIGH); |
080bcc18 | 1686 | if (devpriv->high_speed) { |
c79bc875 HS |
1687 | devpriv->n_ai_urbs = NUMOFINBUFFERSHIGH; |
1688 | devpriv->n_ao_urbs = NUMOFOUTBUFFERSHIGH; | |
1689 | devpriv->pwm_buf_sz = 512; | |
080bcc18 | 1690 | } else { |
c79bc875 HS |
1691 | devpriv->n_ai_urbs = NUMOFINBUFFERSFULL; |
1692 | devpriv->n_ao_urbs = NUMOFOUTBUFFERSFULL; | |
080bcc18 | 1693 | } |
8fa07567 | 1694 | |
11642c65 | 1695 | ret = usbdux_alloc_usb_buffers(dev); |
29d0c074 | 1696 | if (ret) |
080bcc18 | 1697 | return ret; |
4bf21fa4 | 1698 | |
080bcc18 | 1699 | /* setting to alternate setting 3: enabling iso ep and bulk ep. */ |
903f4fde HS |
1700 | ret = usb_set_interface(usb, intf->altsetting->desc.bInterfaceNumber, |
1701 | 3); | |
080bcc18 | 1702 | if (ret < 0) { |
ef1ee8cf | 1703 | dev_err(dev->class_dev, |
080bcc18 | 1704 | "could not set alternate setting 3 in high speed\n"); |
080bcc18 | 1705 | return ret; |
4bf21fa4 BP |
1706 | } |
1707 | ||
1731a826 | 1708 | ret = comedi_load_firmware(dev, &usb->dev, USBDUX_FIRMWARE, |
ef1ee8cf HS |
1709 | usbdux_firmware_upload, 0); |
1710 | if (ret < 0) | |
1711 | return ret; | |
4bf21fa4 | 1712 | |
cc84f4f9 HS |
1713 | ret = comedi_alloc_subdevices(dev, (devpriv->high_speed) ? 5 : 4); |
1714 | if (ret) | |
1715 | return ret; | |
1716 | ||
1717 | /* Analog Input subdevice */ | |
e5acdc35 | 1718 | s = &dev->subdevices[0]; |
cc84f4f9 HS |
1719 | dev->read_subdev = s; |
1720 | s->type = COMEDI_SUBD_AI; | |
1721 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ; | |
1722 | s->n_chan = 8; | |
1723 | s->maxdata = 0x0fff; | |
1724 | s->len_chanlist = 8; | |
1725 | s->range_table = &range_usbdux_ai_range; | |
1726 | s->insn_read = usbdux_ai_insn_read; | |
1727 | s->do_cmdtest = usbdux_ai_cmdtest; | |
1728 | s->do_cmd = usbdux_ai_cmd; | |
1729 | s->cancel = usbdux_ai_cancel; | |
1730 | ||
1731 | /* Analog Output subdevice */ | |
e5acdc35 | 1732 | s = &dev->subdevices[1]; |
cc84f4f9 HS |
1733 | dev->write_subdev = s; |
1734 | s->type = COMEDI_SUBD_AO; | |
1735 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE; | |
a998a3db | 1736 | s->n_chan = USBDUX_NUM_AO_CHAN; |
cc84f4f9 | 1737 | s->maxdata = 0x0fff; |
a998a3db | 1738 | s->len_chanlist = s->n_chan; |
cc84f4f9 HS |
1739 | s->range_table = &range_usbdux_ao_range; |
1740 | s->do_cmdtest = usbdux_ao_cmdtest; | |
1741 | s->do_cmd = usbdux_ao_cmd; | |
1742 | s->cancel = usbdux_ao_cancel; | |
1743 | s->insn_read = usbdux_ao_insn_read; | |
1744 | s->insn_write = usbdux_ao_insn_write; | |
1745 | ||
1746 | /* Digital I/O subdevice */ | |
e5acdc35 | 1747 | s = &dev->subdevices[2]; |
cc84f4f9 HS |
1748 | s->type = COMEDI_SUBD_DIO; |
1749 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
1750 | s->n_chan = 8; | |
1751 | s->maxdata = 1; | |
1752 | s->range_table = &range_digital; | |
1753 | s->insn_bits = usbdux_dio_insn_bits; | |
1754 | s->insn_config = usbdux_dio_insn_config; | |
1755 | ||
1756 | /* Counter subdevice */ | |
e5acdc35 | 1757 | s = &dev->subdevices[3]; |
cc84f4f9 HS |
1758 | s->type = COMEDI_SUBD_COUNTER; |
1759 | s->subdev_flags = SDF_WRITABLE | SDF_READABLE; | |
1760 | s->n_chan = 4; | |
1761 | s->maxdata = 0xffff; | |
1762 | s->insn_read = usbdux_counter_read; | |
1763 | s->insn_write = usbdux_counter_write; | |
1764 | s->insn_config = usbdux_counter_config; | |
1765 | ||
1766 | if (devpriv->high_speed) { | |
1767 | /* PWM subdevice */ | |
e5acdc35 | 1768 | s = &dev->subdevices[4]; |
cc84f4f9 HS |
1769 | s->type = COMEDI_SUBD_PWM; |
1770 | s->subdev_flags = SDF_WRITABLE | SDF_PWM_HBRIDGE; | |
1771 | s->n_chan = 8; | |
c79bc875 | 1772 | s->maxdata = devpriv->pwm_buf_sz; |
cc84f4f9 | 1773 | s->insn_write = usbdux_pwm_write; |
cc84f4f9 HS |
1774 | s->insn_config = usbdux_pwm_config; |
1775 | ||
1776 | usbdux_pwm_period(dev, s, PWM_DEFAULT_PERIOD); | |
1777 | } | |
1778 | ||
1779 | return 0; | |
ef1ee8cf HS |
1780 | } |
1781 | ||
1782 | static void usbdux_detach(struct comedi_device *dev) | |
1783 | { | |
5d293d90 | 1784 | struct usb_interface *intf = comedi_to_usb_interface(dev); |
ef1ee8cf | 1785 | struct usbdux_private *devpriv = dev->private; |
6742c0af | 1786 | |
4d9861b5 | 1787 | usb_set_intfdata(intf, NULL); |
8ff14ee8 | 1788 | |
4d9861b5 HS |
1789 | if (!devpriv) |
1790 | return; | |
1791 | ||
1792 | down(&devpriv->sem); | |
8ff14ee8 | 1793 | |
4d9861b5 HS |
1794 | /* force unlink all urbs */ |
1795 | usbdux_pwm_stop(dev, 1); | |
1796 | usbdux_ao_stop(dev, 1); | |
1797 | usbdux_ai_stop(dev, 1); | |
8ff14ee8 | 1798 | |
71500b27 | 1799 | usbdux_free_usb_buffers(dev); |
8ff14ee8 | 1800 | |
4d9861b5 | 1801 | up(&devpriv->sem); |
ef1ee8cf HS |
1802 | } |
1803 | ||
1804 | static struct comedi_driver usbdux_driver = { | |
1805 | .driver_name = "usbdux", | |
1806 | .module = THIS_MODULE, | |
1807 | .auto_attach = usbdux_auto_attach, | |
1808 | .detach = usbdux_detach, | |
1809 | }; | |
1810 | ||
1811 | static int usbdux_usb_probe(struct usb_interface *intf, | |
1812 | const struct usb_device_id *id) | |
1813 | { | |
1814 | return comedi_usb_auto_config(intf, &usbdux_driver, 0); | |
4bf21fa4 BP |
1815 | } |
1816 | ||
3403cc0f HS |
1817 | static const struct usb_device_id usbdux_usb_table[] = { |
1818 | { USB_DEVICE(0x13d8, 0x0001) }, | |
1819 | { USB_DEVICE(0x13d8, 0x0002) }, | |
1820 | { } | |
4bf21fa4 | 1821 | }; |
3403cc0f | 1822 | MODULE_DEVICE_TABLE(usb, usbdux_usb_table); |
4bf21fa4 | 1823 | |
42318c32 | 1824 | static struct usb_driver usbdux_usb_driver = { |
3403cc0f HS |
1825 | .name = "usbdux", |
1826 | .probe = usbdux_usb_probe, | |
a7dd65f5 | 1827 | .disconnect = comedi_usb_auto_unconfig, |
3403cc0f | 1828 | .id_table = usbdux_usb_table, |
4bf21fa4 | 1829 | }; |
42318c32 | 1830 | module_comedi_usb_driver(usbdux_driver, usbdux_usb_driver); |
4bf21fa4 | 1831 | |
3403cc0f HS |
1832 | MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com"); |
1833 | MODULE_DESCRIPTION("Stirling/ITL USB-DUX -- Bernd.Porr@f2s.com"); | |
4bf21fa4 | 1834 | MODULE_LICENSE("GPL"); |
1731a826 | 1835 | MODULE_FIRMWARE(USBDUX_FIRMWARE); |