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