Merge remote-tracking branch 'staging/staging-next'
[deliverable/linux.git] / drivers / staging / comedi / drivers / ni_usb6501.c
CommitLineData
a03bb00e
LE
1/*
2 * comedi/drivers/ni_usb6501.c
3 * Comedi driver for National Instruments USB-6501
4 *
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 2014 Luca Ellero <luca.ellero@brickedbrain.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19/*
20 * Driver: ni_usb6501
21 * Description: National Instruments USB-6501 module
22 * Devices: [National Instruments] USB-6501 (ni_usb6501)
23 * Author: Luca Ellero <luca.ellero@brickedbrain.com>
ddf9b6a4 24 * Updated: 8 Sep 2014
a03bb00e
LE
25 * Status: works
26 *
a03bb00e
LE
27 *
28 * Configuration Options:
29 * none
30 */
31
32/*
33 * NI-6501 - USB PROTOCOL DESCRIPTION
34 *
35 * Every command is composed by two USB packets:
36 * - request (out)
37 * - response (in)
38 *
39 * Every packet is at least 12 bytes long, here is the meaning of
ddf9b6a4 40 * every field (all values are hex):
a03bb00e
LE
41 *
42 * byte 0 is always 00
43 * byte 1 is always 01
44 * byte 2 is always 00
45 * byte 3 is the total packet length
46 *
47 * byte 4 is always 00
48 * byte 5 is is the total packet length - 4
49 * byte 6 is always 01
50 * byte 7 is the command
51 *
52 * byte 8 is 02 (request) or 00 (response)
53 * byte 9 is 00 (response) or 10 (port request) or 20 (counter request)
54 * byte 10 is always 00
55 * byte 11 is 00 (request) or 02 (response)
56 *
ddf9b6a4 57 * PORT PACKETS
a03bb00e
LE
58 *
59 * CMD: 0xE READ_PORT
60 * REQ: 00 01 00 10 00 0C 01 0E 02 10 00 00 00 03 <PORT> 00
61 * RES: 00 01 00 10 00 0C 01 00 00 00 00 02 00 03 <BMAP> 00
ddf9b6a4 62 *
a03bb00e
LE
63 * CMD: 0xF WRITE_PORT
64 * REQ: 00 01 00 14 00 10 01 0F 02 10 00 00 00 03 <PORT> 00 03 <BMAP> 00 00
65 * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
66 *
67 * CMD: 0x12 SET_PORT_DIR (0 = input, 1 = output)
68 * REQ: 00 01 00 18 00 14 01 12 02 10 00 00
69 * 00 05 <PORT 0> <PORT 1> <PORT 2> 00 05 00 00 00 00 00
70 * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
ddf9b6a4
LE
71 *
72 * COUNTER PACKETS
73 *
74 * CMD 0x9: START_COUNTER
75 * REQ: 00 01 00 0C 00 08 01 09 02 20 00 00
76 * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
77 *
78 * CMD 0xC: STOP_COUNTER
79 * REQ: 00 01 00 0C 00 08 01 0C 02 20 00 00
80 * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
81 *
82 * CMD 0xE: READ_COUNTER
83 * REQ: 00 01 00 0C 00 08 01 0E 02 20 00 00
84 * RES: 00 01 00 10 00 0C 01 00 00 00 00 02 <u32 counter value, Big Endian>
85 *
86 * CMD 0xF: WRITE_COUNTER
87 * REQ: 00 01 00 10 00 0C 01 0F 02 20 00 00 <u32 counter value, Big Endian>
88 * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
89 *
90 *
91 * Please visit http://www.brickedbrain.com if you need
92 * additional information or have any questions.
93 *
a03bb00e
LE
94 */
95
96#include <linux/kernel.h>
97#include <linux/module.h>
98#include <linux/slab.h>
a03bb00e 99
3fdffa5d 100#include "../comedi_usb.h"
a03bb00e
LE
101
102#define NI6501_TIMEOUT 1000
103
104/* Port request packets */
105static const u8 READ_PORT_REQUEST[] = {0x00, 0x01, 0x00, 0x10,
106 0x00, 0x0C, 0x01, 0x0E,
107 0x02, 0x10, 0x00, 0x00,
108 0x00, 0x03, 0x00, 0x00};
109
110static const u8 WRITE_PORT_REQUEST[] = {0x00, 0x01, 0x00, 0x14,
111 0x00, 0x10, 0x01, 0x0F,
112 0x02, 0x10, 0x00, 0x00,
113 0x00, 0x03, 0x00, 0x00,
114 0x03, 0x00, 0x00, 0x00};
115
116static const u8 SET_PORT_DIR_REQUEST[] = {0x00, 0x01, 0x00, 0x18,
117 0x00, 0x14, 0x01, 0x12,
118 0x02, 0x10, 0x00, 0x00,
119 0x00, 0x05, 0x00, 0x00,
120 0x00, 0x00, 0x05, 0x00,
121 0x00, 0x00, 0x00, 0x00};
122
aa66cd16
LE
123/* Counter request packets */
124static const u8 START_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x0C,
125 0x00, 0x08, 0x01, 0x09,
126 0x02, 0x20, 0x00, 0x00};
127
128static const u8 STOP_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x0C,
129 0x00, 0x08, 0x01, 0x0C,
130 0x02, 0x20, 0x00, 0x00};
131
132static const u8 READ_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x0C,
133 0x00, 0x08, 0x01, 0x0E,
134 0x02, 0x20, 0x00, 0x00};
135
136static const u8 WRITE_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x10,
137 0x00, 0x0C, 0x01, 0x0F,
138 0x02, 0x20, 0x00, 0x00,
139 0x00, 0x00, 0x00, 0x00};
140
a03bb00e
LE
141/* Response packets */
142static const u8 GENERIC_RESPONSE[] = {0x00, 0x01, 0x00, 0x0C,
143 0x00, 0x08, 0x01, 0x00,
144 0x00, 0x00, 0x00, 0x02};
145
146static const u8 READ_PORT_RESPONSE[] = {0x00, 0x01, 0x00, 0x10,
147 0x00, 0x0C, 0x01, 0x00,
148 0x00, 0x00, 0x00, 0x02,
149 0x00, 0x03, 0x00, 0x00};
150
aa66cd16
LE
151static const u8 READ_COUNTER_RESPONSE[] = {0x00, 0x01, 0x00, 0x10,
152 0x00, 0x0C, 0x01, 0x00,
153 0x00, 0x00, 0x00, 0x02,
154 0x00, 0x00, 0x00, 0x00};
155
a03bb00e
LE
156enum commands {
157 READ_PORT,
158 WRITE_PORT,
aa66cd16
LE
159 SET_PORT_DIR,
160 START_COUNTER,
161 STOP_COUNTER,
162 READ_COUNTER,
163 WRITE_COUNTER
a03bb00e
LE
164};
165
166struct ni6501_private {
167 struct usb_endpoint_descriptor *ep_rx;
168 struct usb_endpoint_descriptor *ep_tx;
5a731c70 169 struct mutex mut;
a03bb00e
LE
170 u8 *usb_rx_buf;
171 u8 *usb_tx_buf;
172};
173
5116a48c 174static int ni6501_port_command(struct comedi_device *dev, int command,
365741e6 175 unsigned int val, u8 *bitmap)
a03bb00e
LE
176{
177 struct usb_device *usb = comedi_to_usb_dev(dev);
178 struct ni6501_private *devpriv = dev->private;
179 int request_size, response_size;
180 u8 *tx = devpriv->usb_tx_buf;
181 int ret;
182
a03bb00e
LE
183 if (command != SET_PORT_DIR && !bitmap)
184 return -EINVAL;
185
5a731c70 186 mutex_lock(&devpriv->mut);
a03bb00e
LE
187
188 switch (command) {
189 case READ_PORT:
a03bb00e 190 request_size = sizeof(READ_PORT_REQUEST);
88367bd5 191 response_size = sizeof(READ_PORT_RESPONSE);
a03bb00e 192 memcpy(tx, READ_PORT_REQUEST, request_size);
365741e6 193 tx[14] = val & 0xff;
a03bb00e 194 break;
a03bb00e 195 case WRITE_PORT:
a03bb00e
LE
196 request_size = sizeof(WRITE_PORT_REQUEST);
197 response_size = sizeof(GENERIC_RESPONSE);
a03bb00e 198 memcpy(tx, WRITE_PORT_REQUEST, request_size);
365741e6
HS
199 tx[14] = val & 0xff;
200 tx[17] = *bitmap;
a03bb00e 201 break;
a03bb00e 202 case SET_PORT_DIR:
a03bb00e
LE
203 request_size = sizeof(SET_PORT_DIR_REQUEST);
204 response_size = sizeof(GENERIC_RESPONSE);
a03bb00e 205 memcpy(tx, SET_PORT_DIR_REQUEST, request_size);
365741e6
HS
206 tx[14] = val & 0xff;
207 tx[15] = (val >> 8) & 0xff;
208 tx[16] = (val >> 16) & 0xff;
a03bb00e 209 break;
a03bb00e
LE
210 default:
211 ret = -EINVAL;
212 goto end;
213 }
214
215 ret = usb_bulk_msg(usb,
216 usb_sndbulkpipe(usb,
217 devpriv->ep_tx->bEndpointAddress),
218 devpriv->usb_tx_buf,
219 request_size,
220 NULL,
221 NI6501_TIMEOUT);
222 if (ret)
223 goto end;
224
225 ret = usb_bulk_msg(usb,
226 usb_rcvbulkpipe(usb,
227 devpriv->ep_rx->bEndpointAddress),
228 devpriv->usb_rx_buf,
229 response_size,
230 NULL,
231 NI6501_TIMEOUT);
232 if (ret)
233 goto end;
234
235 /* Check if results are valid */
236
237 if (command == READ_PORT) {
365741e6 238 *bitmap = devpriv->usb_rx_buf[14];
a03bb00e
LE
239 /* mask bitmap for comparing */
240 devpriv->usb_rx_buf[14] = 0x00;
241
242 if (memcmp(devpriv->usb_rx_buf, READ_PORT_RESPONSE,
243 sizeof(READ_PORT_RESPONSE))) {
244 ret = -EINVAL;
245 }
246 } else if (memcmp(devpriv->usb_rx_buf, GENERIC_RESPONSE,
247 sizeof(GENERIC_RESPONSE))) {
248 ret = -EINVAL;
249 }
250end:
5a731c70 251 mutex_unlock(&devpriv->mut);
a03bb00e
LE
252
253 return ret;
254}
255
7baf9294
LE
256static int ni6501_counter_command(struct comedi_device *dev, int command,
257 u32 *val)
258{
259 struct usb_device *usb = comedi_to_usb_dev(dev);
260 struct ni6501_private *devpriv = dev->private;
261 int request_size, response_size;
262 u8 *tx = devpriv->usb_tx_buf;
263 int ret;
264
265 if ((command == READ_COUNTER || command == WRITE_COUNTER) && !val)
266 return -EINVAL;
267
5a731c70 268 mutex_lock(&devpriv->mut);
7baf9294
LE
269
270 switch (command) {
271 case START_COUNTER:
272 request_size = sizeof(START_COUNTER_REQUEST);
273 response_size = sizeof(GENERIC_RESPONSE);
274 memcpy(tx, START_COUNTER_REQUEST, request_size);
275 break;
276 case STOP_COUNTER:
277 request_size = sizeof(STOP_COUNTER_REQUEST);
278 response_size = sizeof(GENERIC_RESPONSE);
279 memcpy(tx, STOP_COUNTER_REQUEST, request_size);
280 break;
281 case READ_COUNTER:
282 request_size = sizeof(READ_COUNTER_REQUEST);
283 response_size = sizeof(READ_COUNTER_RESPONSE);
284 memcpy(tx, READ_COUNTER_REQUEST, request_size);
285 break;
286 case WRITE_COUNTER:
287 request_size = sizeof(WRITE_COUNTER_REQUEST);
288 response_size = sizeof(GENERIC_RESPONSE);
289 memcpy(tx, WRITE_COUNTER_REQUEST, request_size);
290 /* Setup tx packet: bytes 12,13,14,15 hold the */
291 /* u32 counter value (Big Endian) */
292 *((__be32 *)&tx[12]) = cpu_to_be32(*val);
293 break;
294 default:
295 ret = -EINVAL;
296 goto end;
297 }
298
299 ret = usb_bulk_msg(usb,
300 usb_sndbulkpipe(usb,
301 devpriv->ep_tx->bEndpointAddress),
302 devpriv->usb_tx_buf,
303 request_size,
304 NULL,
305 NI6501_TIMEOUT);
306 if (ret)
307 goto end;
308
309 ret = usb_bulk_msg(usb,
310 usb_rcvbulkpipe(usb,
311 devpriv->ep_rx->bEndpointAddress),
312 devpriv->usb_rx_buf,
313 response_size,
314 NULL,
315 NI6501_TIMEOUT);
316 if (ret)
317 goto end;
318
319 /* Check if results are valid */
320
321 if (command == READ_COUNTER) {
322 int i;
323
324 /* Read counter value: bytes 12,13,14,15 of rx packet */
325 /* hold the u32 counter value (Big Endian) */
326 *val = be32_to_cpu(*((__be32 *)&devpriv->usb_rx_buf[12]));
327
328 /* mask counter value for comparing */
329 for (i = 12; i < sizeof(READ_COUNTER_RESPONSE); ++i)
330 devpriv->usb_rx_buf[i] = 0x00;
331
332 if (memcmp(devpriv->usb_rx_buf, READ_COUNTER_RESPONSE,
333 sizeof(READ_COUNTER_RESPONSE))) {
334 ret = -EINVAL;
335 }
336 } else if (memcmp(devpriv->usb_rx_buf, GENERIC_RESPONSE,
337 sizeof(GENERIC_RESPONSE))) {
338 ret = -EINVAL;
339 }
340end:
5a731c70 341 mutex_unlock(&devpriv->mut);
7baf9294
LE
342
343 return ret;
344}
345
a03bb00e
LE
346static int ni6501_dio_insn_config(struct comedi_device *dev,
347 struct comedi_subdevice *s,
348 struct comedi_insn *insn,
349 unsigned int *data)
350{
351 int ret;
a03bb00e
LE
352
353 ret = comedi_dio_insn_config(dev, s, insn, data, 0);
354 if (ret)
355 return ret;
356
365741e6 357 ret = ni6501_port_command(dev, SET_PORT_DIR, s->io_bits, NULL);
a03bb00e
LE
358 if (ret)
359 return ret;
360
361 return insn->n;
362}
363
364static int ni6501_dio_insn_bits(struct comedi_device *dev,
365 struct comedi_subdevice *s,
366 struct comedi_insn *insn,
367 unsigned int *data)
368{
369 unsigned int mask;
370 int ret;
371 u8 port;
372 u8 bitmap;
373
374 mask = comedi_dio_update_state(s, data);
375
376 for (port = 0; port < 3; port++) {
377 if (mask & (0xFF << port * 8)) {
378 bitmap = (s->state >> port * 8) & 0xFF;
5116a48c 379 ret = ni6501_port_command(dev, WRITE_PORT,
365741e6 380 port, &bitmap);
a03bb00e
LE
381 if (ret)
382 return ret;
383 }
384 }
385
386 data[1] = 0;
387
388 for (port = 0; port < 3; port++) {
365741e6 389 ret = ni6501_port_command(dev, READ_PORT, port, &bitmap);
a03bb00e
LE
390 if (ret)
391 return ret;
392 data[1] |= bitmap << port * 8;
393 }
394
395 return insn->n;
396}
397
7baf9294
LE
398static int ni6501_cnt_insn_config(struct comedi_device *dev,
399 struct comedi_subdevice *s,
400 struct comedi_insn *insn,
401 unsigned int *data)
402{
403 int ret;
404 u32 val = 0;
405
406 switch (data[0]) {
407 case INSN_CONFIG_ARM:
408 ret = ni6501_counter_command(dev, START_COUNTER, NULL);
409 break;
410 case INSN_CONFIG_DISARM:
411 ret = ni6501_counter_command(dev, STOP_COUNTER, NULL);
412 break;
413 case INSN_CONFIG_RESET:
414 ret = ni6501_counter_command(dev, STOP_COUNTER, NULL);
415 if (ret)
416 break;
417 ret = ni6501_counter_command(dev, WRITE_COUNTER, &val);
418 break;
419 default:
420 return -EINVAL;
421 }
422
423 return ret ? ret : insn->n;
424}
425
426static int ni6501_cnt_insn_read(struct comedi_device *dev,
427 struct comedi_subdevice *s,
428 struct comedi_insn *insn,
429 unsigned int *data)
430{
431 int ret;
432 u32 val;
433 unsigned int i;
434
435 for (i = 0; i < insn->n; i++) {
436 ret = ni6501_counter_command(dev, READ_COUNTER, &val);
437 if (ret)
438 return ret;
439 data[i] = val;
440 }
441
442 return insn->n;
443}
444
445static int ni6501_cnt_insn_write(struct comedi_device *dev,
446 struct comedi_subdevice *s,
447 struct comedi_insn *insn,
448 unsigned int *data)
449{
450 int ret;
451
452 if (insn->n) {
453 u32 val = data[insn->n - 1];
454
455 ret = ni6501_counter_command(dev, WRITE_COUNTER, &val);
456 if (ret)
457 return ret;
458 }
459
460 return insn->n;
461}
462
a03bb00e
LE
463static int ni6501_alloc_usb_buffers(struct comedi_device *dev)
464{
465 struct ni6501_private *devpriv = dev->private;
466 size_t size;
467
62190d49 468 size = usb_endpoint_maxp(devpriv->ep_rx);
a03bb00e
LE
469 devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL);
470 if (!devpriv->usb_rx_buf)
471 return -ENOMEM;
472
62190d49 473 size = usb_endpoint_maxp(devpriv->ep_tx);
a03bb00e
LE
474 devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
475 if (!devpriv->usb_tx_buf) {
476 kfree(devpriv->usb_rx_buf);
477 return -ENOMEM;
478 }
479
480 return 0;
481}
482
483static int ni6501_find_endpoints(struct comedi_device *dev)
484{
485 struct usb_interface *intf = comedi_to_usb_interface(dev);
486 struct ni6501_private *devpriv = dev->private;
487 struct usb_host_interface *iface_desc = intf->cur_altsetting;
488 struct usb_endpoint_descriptor *ep_desc;
489 int i;
490
491 if (iface_desc->desc.bNumEndpoints != 2) {
492 dev_err(dev->class_dev, "Wrong number of endpoints\n");
493 return -ENODEV;
494 }
495
496 for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
497 ep_desc = &iface_desc->endpoint[i].desc;
498
499 if (usb_endpoint_is_bulk_in(ep_desc)) {
500 if (!devpriv->ep_rx)
501 devpriv->ep_rx = ep_desc;
502 continue;
503 }
504
505 if (usb_endpoint_is_bulk_out(ep_desc)) {
506 if (!devpriv->ep_tx)
507 devpriv->ep_tx = ep_desc;
508 continue;
509 }
510 }
511
512 if (!devpriv->ep_rx || !devpriv->ep_tx)
513 return -ENODEV;
514
515 return 0;
516}
517
518static int ni6501_auto_attach(struct comedi_device *dev,
519 unsigned long context)
520{
521 struct usb_interface *intf = comedi_to_usb_interface(dev);
522 struct ni6501_private *devpriv;
523 struct comedi_subdevice *s;
524 int ret;
525
526 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
527 if (!devpriv)
528 return -ENOMEM;
529
530 ret = ni6501_find_endpoints(dev);
531 if (ret)
532 return ret;
533
534 ret = ni6501_alloc_usb_buffers(dev);
535 if (ret)
536 return ret;
537
5a731c70 538 mutex_init(&devpriv->mut);
a03bb00e
LE
539 usb_set_intfdata(intf, devpriv);
540
7baf9294 541 ret = comedi_alloc_subdevices(dev, 2);
a03bb00e
LE
542 if (ret)
543 return ret;
544
545 /* Digital Input/Output subdevice */
546 s = &dev->subdevices[0];
547 s->type = COMEDI_SUBD_DIO;
548 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
549 s->n_chan = 24;
550 s->maxdata = 1;
551 s->range_table = &range_digital;
e9826c0c
LE
552 s->insn_bits = ni6501_dio_insn_bits;
553 s->insn_config = ni6501_dio_insn_config;
a03bb00e 554
7baf9294
LE
555 /* Counter subdevice */
556 s = &dev->subdevices[1];
557 s->type = COMEDI_SUBD_COUNTER;
ef49d832 558 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
7baf9294
LE
559 s->n_chan = 1;
560 s->maxdata = 0xffffffff;
561 s->insn_read = ni6501_cnt_insn_read;
562 s->insn_write = ni6501_cnt_insn_write;
563 s->insn_config = ni6501_cnt_insn_config;
564
a03bb00e
LE
565 return 0;
566}
567
568static void ni6501_detach(struct comedi_device *dev)
569{
570 struct usb_interface *intf = comedi_to_usb_interface(dev);
571 struct ni6501_private *devpriv = dev->private;
572
573 if (!devpriv)
574 return;
575
5a731c70 576 mutex_lock(&devpriv->mut);
a03bb00e
LE
577
578 usb_set_intfdata(intf, NULL);
579
580 kfree(devpriv->usb_rx_buf);
581 kfree(devpriv->usb_tx_buf);
582
5a731c70 583 mutex_unlock(&devpriv->mut);
a03bb00e
LE
584}
585
586static struct comedi_driver ni6501_driver = {
587 .module = THIS_MODULE,
588 .driver_name = "ni6501",
589 .auto_attach = ni6501_auto_attach,
590 .detach = ni6501_detach,
591};
592
593static int ni6501_usb_probe(struct usb_interface *intf,
594 const struct usb_device_id *id)
595{
596 return comedi_usb_auto_config(intf, &ni6501_driver, id->driver_info);
597}
598
599static const struct usb_device_id ni6501_usb_table[] = {
600 { USB_DEVICE(0x3923, 0x718a) },
601 { }
602};
603MODULE_DEVICE_TABLE(usb, ni6501_usb_table);
604
605static struct usb_driver ni6501_usb_driver = {
606 .name = "ni6501",
607 .id_table = ni6501_usb_table,
608 .probe = ni6501_usb_probe,
609 .disconnect = comedi_usb_auto_unconfig,
610};
611module_comedi_usb_driver(ni6501_driver, ni6501_usb_driver);
612
613MODULE_AUTHOR("Luca Ellero");
614MODULE_DESCRIPTION("Comedi driver for National Instruments USB-6501");
615MODULE_LICENSE("GPL");
This page took 0.295887 seconds and 5 git commands to generate.