2 * comedi/drivers/ni_usb6501.c
3 * Comedi driver for National Instruments USB-6501
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 2014 Luca Ellero <luca.ellero@brickedbrain.com>
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.
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.
21 * Description: National Instruments USB-6501 module
22 * Devices: [National Instruments] USB-6501 (ni_usb6501)
23 * Author: Luca Ellero <luca.ellero@brickedbrain.com>
28 * Configuration Options:
33 * NI-6501 - USB PROTOCOL DESCRIPTION
35 * Every command is composed by two USB packets:
39 * Every packet is at least 12 bytes long, here is the meaning of
40 * every field (all values are hex):
45 * byte 3 is the total packet length
48 * byte 5 is is the total packet length - 4
50 * byte 7 is the command
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)
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
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
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
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
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
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>
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
91 * Please visit http://www.brickedbrain.com if you need
92 * additional information or have any questions.
96 #include <linux/kernel.h>
97 #include <linux/module.h>
98 #include <linux/slab.h>
99 #include <linux/usb.h>
101 #include "../comedidev.h"
103 #define NI6501_TIMEOUT 1000
105 /* Port request packets */
106 static const u8 READ_PORT_REQUEST
[] = {0x00, 0x01, 0x00, 0x10,
107 0x00, 0x0C, 0x01, 0x0E,
108 0x02, 0x10, 0x00, 0x00,
109 0x00, 0x03, 0x00, 0x00};
111 static const u8 WRITE_PORT_REQUEST
[] = {0x00, 0x01, 0x00, 0x14,
112 0x00, 0x10, 0x01, 0x0F,
113 0x02, 0x10, 0x00, 0x00,
114 0x00, 0x03, 0x00, 0x00,
115 0x03, 0x00, 0x00, 0x00};
117 static const u8 SET_PORT_DIR_REQUEST
[] = {0x00, 0x01, 0x00, 0x18,
118 0x00, 0x14, 0x01, 0x12,
119 0x02, 0x10, 0x00, 0x00,
120 0x00, 0x05, 0x00, 0x00,
121 0x00, 0x00, 0x05, 0x00,
122 0x00, 0x00, 0x00, 0x00};
124 /* Counter request packets */
125 static const u8 START_COUNTER_REQUEST
[] = {0x00, 0x01, 0x00, 0x0C,
126 0x00, 0x08, 0x01, 0x09,
127 0x02, 0x20, 0x00, 0x00};
129 static const u8 STOP_COUNTER_REQUEST
[] = {0x00, 0x01, 0x00, 0x0C,
130 0x00, 0x08, 0x01, 0x0C,
131 0x02, 0x20, 0x00, 0x00};
133 static const u8 READ_COUNTER_REQUEST
[] = {0x00, 0x01, 0x00, 0x0C,
134 0x00, 0x08, 0x01, 0x0E,
135 0x02, 0x20, 0x00, 0x00};
137 static const u8 WRITE_COUNTER_REQUEST
[] = {0x00, 0x01, 0x00, 0x10,
138 0x00, 0x0C, 0x01, 0x0F,
139 0x02, 0x20, 0x00, 0x00,
140 0x00, 0x00, 0x00, 0x00};
142 /* Response packets */
143 static const u8 GENERIC_RESPONSE
[] = {0x00, 0x01, 0x00, 0x0C,
144 0x00, 0x08, 0x01, 0x00,
145 0x00, 0x00, 0x00, 0x02};
147 static const u8 READ_PORT_RESPONSE
[] = {0x00, 0x01, 0x00, 0x10,
148 0x00, 0x0C, 0x01, 0x00,
149 0x00, 0x00, 0x00, 0x02,
150 0x00, 0x03, 0x00, 0x00};
152 static const u8 READ_COUNTER_RESPONSE
[] = {0x00, 0x01, 0x00, 0x10,
153 0x00, 0x0C, 0x01, 0x00,
154 0x00, 0x00, 0x00, 0x02,
155 0x00, 0x00, 0x00, 0x00};
167 struct ni6501_private
{
168 struct usb_endpoint_descriptor
*ep_rx
;
169 struct usb_endpoint_descriptor
*ep_tx
;
170 struct semaphore sem
;
175 static int ni6501_port_command(struct comedi_device
*dev
, int command
,
176 const u8
*port
, u8
*bitmap
)
178 struct usb_device
*usb
= comedi_to_usb_dev(dev
);
179 struct ni6501_private
*devpriv
= dev
->private;
180 int request_size
, response_size
;
181 u8
*tx
= devpriv
->usb_tx_buf
;
184 if (command
!= SET_PORT_DIR
&& !bitmap
)
191 request_size
= sizeof(READ_PORT_REQUEST
);
192 response_size
= sizeof(READ_PORT_RESPONSE
);
193 memcpy(tx
, READ_PORT_REQUEST
, request_size
);
197 request_size
= sizeof(WRITE_PORT_REQUEST
);
198 response_size
= sizeof(GENERIC_RESPONSE
);
199 memcpy(tx
, WRITE_PORT_REQUEST
, request_size
);
204 request_size
= sizeof(SET_PORT_DIR_REQUEST
);
205 response_size
= sizeof(GENERIC_RESPONSE
);
206 memcpy(tx
, SET_PORT_DIR_REQUEST
, request_size
);
216 ret
= usb_bulk_msg(usb
,
218 devpriv
->ep_tx
->bEndpointAddress
),
226 ret
= usb_bulk_msg(usb
,
228 devpriv
->ep_rx
->bEndpointAddress
),
236 /* Check if results are valid */
238 if (command
== READ_PORT
) {
239 bitmap
[0] = devpriv
->usb_rx_buf
[14];
240 /* mask bitmap for comparing */
241 devpriv
->usb_rx_buf
[14] = 0x00;
243 if (memcmp(devpriv
->usb_rx_buf
, READ_PORT_RESPONSE
,
244 sizeof(READ_PORT_RESPONSE
))) {
247 } else if (memcmp(devpriv
->usb_rx_buf
, GENERIC_RESPONSE
,
248 sizeof(GENERIC_RESPONSE
))) {
257 static int ni6501_counter_command(struct comedi_device
*dev
, int command
,
260 struct usb_device
*usb
= comedi_to_usb_dev(dev
);
261 struct ni6501_private
*devpriv
= dev
->private;
262 int request_size
, response_size
;
263 u8
*tx
= devpriv
->usb_tx_buf
;
266 if ((command
== READ_COUNTER
|| command
== WRITE_COUNTER
) && !val
)
273 request_size
= sizeof(START_COUNTER_REQUEST
);
274 response_size
= sizeof(GENERIC_RESPONSE
);
275 memcpy(tx
, START_COUNTER_REQUEST
, request_size
);
278 request_size
= sizeof(STOP_COUNTER_REQUEST
);
279 response_size
= sizeof(GENERIC_RESPONSE
);
280 memcpy(tx
, STOP_COUNTER_REQUEST
, request_size
);
283 request_size
= sizeof(READ_COUNTER_REQUEST
);
284 response_size
= sizeof(READ_COUNTER_RESPONSE
);
285 memcpy(tx
, READ_COUNTER_REQUEST
, request_size
);
288 request_size
= sizeof(WRITE_COUNTER_REQUEST
);
289 response_size
= sizeof(GENERIC_RESPONSE
);
290 memcpy(tx
, WRITE_COUNTER_REQUEST
, request_size
);
291 /* Setup tx packet: bytes 12,13,14,15 hold the */
292 /* u32 counter value (Big Endian) */
293 *((__be32
*)&tx
[12]) = cpu_to_be32(*val
);
300 ret
= usb_bulk_msg(usb
,
302 devpriv
->ep_tx
->bEndpointAddress
),
310 ret
= usb_bulk_msg(usb
,
312 devpriv
->ep_rx
->bEndpointAddress
),
320 /* Check if results are valid */
322 if (command
== READ_COUNTER
) {
325 /* Read counter value: bytes 12,13,14,15 of rx packet */
326 /* hold the u32 counter value (Big Endian) */
327 *val
= be32_to_cpu(*((__be32
*)&devpriv
->usb_rx_buf
[12]));
329 /* mask counter value for comparing */
330 for (i
= 12; i
< sizeof(READ_COUNTER_RESPONSE
); ++i
)
331 devpriv
->usb_rx_buf
[i
] = 0x00;
333 if (memcmp(devpriv
->usb_rx_buf
, READ_COUNTER_RESPONSE
,
334 sizeof(READ_COUNTER_RESPONSE
))) {
337 } else if (memcmp(devpriv
->usb_rx_buf
, GENERIC_RESPONSE
,
338 sizeof(GENERIC_RESPONSE
))) {
347 static int ni6501_dio_insn_config(struct comedi_device
*dev
,
348 struct comedi_subdevice
*s
,
349 struct comedi_insn
*insn
,
355 ret
= comedi_dio_insn_config(dev
, s
, insn
, data
, 0);
359 port
[0] = (s
->io_bits
) & 0xff;
360 port
[1] = (s
->io_bits
>> 8) & 0xff;
361 port
[2] = (s
->io_bits
>> 16) & 0xff;
363 ret
= ni6501_port_command(dev
, SET_PORT_DIR
, port
, NULL
);
370 static int ni6501_dio_insn_bits(struct comedi_device
*dev
,
371 struct comedi_subdevice
*s
,
372 struct comedi_insn
*insn
,
380 mask
= comedi_dio_update_state(s
, data
);
382 for (port
= 0; port
< 3; port
++) {
383 if (mask
& (0xFF << port
* 8)) {
384 bitmap
= (s
->state
>> port
* 8) & 0xFF;
385 ret
= ni6501_port_command(dev
, WRITE_PORT
,
394 for (port
= 0; port
< 3; port
++) {
395 ret
= ni6501_port_command(dev
, READ_PORT
, &port
, &bitmap
);
398 data
[1] |= bitmap
<< port
* 8;
404 static int ni6501_cnt_insn_config(struct comedi_device
*dev
,
405 struct comedi_subdevice
*s
,
406 struct comedi_insn
*insn
,
413 case INSN_CONFIG_ARM
:
414 ret
= ni6501_counter_command(dev
, START_COUNTER
, NULL
);
416 case INSN_CONFIG_DISARM
:
417 ret
= ni6501_counter_command(dev
, STOP_COUNTER
, NULL
);
419 case INSN_CONFIG_RESET
:
420 ret
= ni6501_counter_command(dev
, STOP_COUNTER
, NULL
);
423 ret
= ni6501_counter_command(dev
, WRITE_COUNTER
, &val
);
429 return ret
? ret
: insn
->n
;
432 static int ni6501_cnt_insn_read(struct comedi_device
*dev
,
433 struct comedi_subdevice
*s
,
434 struct comedi_insn
*insn
,
441 for (i
= 0; i
< insn
->n
; i
++) {
442 ret
= ni6501_counter_command(dev
, READ_COUNTER
, &val
);
451 static int ni6501_cnt_insn_write(struct comedi_device
*dev
,
452 struct comedi_subdevice
*s
,
453 struct comedi_insn
*insn
,
459 u32 val
= data
[insn
->n
- 1];
461 ret
= ni6501_counter_command(dev
, WRITE_COUNTER
, &val
);
469 static int ni6501_alloc_usb_buffers(struct comedi_device
*dev
)
471 struct ni6501_private
*devpriv
= dev
->private;
474 size
= le16_to_cpu(devpriv
->ep_rx
->wMaxPacketSize
);
475 devpriv
->usb_rx_buf
= kzalloc(size
, GFP_KERNEL
);
476 if (!devpriv
->usb_rx_buf
)
479 size
= le16_to_cpu(devpriv
->ep_tx
->wMaxPacketSize
);
480 devpriv
->usb_tx_buf
= kzalloc(size
, GFP_KERNEL
);
481 if (!devpriv
->usb_tx_buf
) {
482 kfree(devpriv
->usb_rx_buf
);
489 static int ni6501_find_endpoints(struct comedi_device
*dev
)
491 struct usb_interface
*intf
= comedi_to_usb_interface(dev
);
492 struct ni6501_private
*devpriv
= dev
->private;
493 struct usb_host_interface
*iface_desc
= intf
->cur_altsetting
;
494 struct usb_endpoint_descriptor
*ep_desc
;
497 if (iface_desc
->desc
.bNumEndpoints
!= 2) {
498 dev_err(dev
->class_dev
, "Wrong number of endpoints\n");
502 for (i
= 0; i
< iface_desc
->desc
.bNumEndpoints
; i
++) {
503 ep_desc
= &iface_desc
->endpoint
[i
].desc
;
505 if (usb_endpoint_is_bulk_in(ep_desc
)) {
507 devpriv
->ep_rx
= ep_desc
;
511 if (usb_endpoint_is_bulk_out(ep_desc
)) {
513 devpriv
->ep_tx
= ep_desc
;
518 if (!devpriv
->ep_rx
|| !devpriv
->ep_tx
)
524 static int ni6501_auto_attach(struct comedi_device
*dev
,
525 unsigned long context
)
527 struct usb_interface
*intf
= comedi_to_usb_interface(dev
);
528 struct ni6501_private
*devpriv
;
529 struct comedi_subdevice
*s
;
532 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
536 ret
= ni6501_find_endpoints(dev
);
540 ret
= ni6501_alloc_usb_buffers(dev
);
544 sema_init(&devpriv
->sem
, 1);
545 usb_set_intfdata(intf
, devpriv
);
547 ret
= comedi_alloc_subdevices(dev
, 2);
551 /* Digital Input/Output subdevice */
552 s
= &dev
->subdevices
[0];
553 s
->type
= COMEDI_SUBD_DIO
;
554 s
->subdev_flags
= SDF_READABLE
| SDF_WRITABLE
;
557 s
->range_table
= &range_digital
;
558 s
->insn_bits
= ni6501_dio_insn_bits
;
559 s
->insn_config
= ni6501_dio_insn_config
;
561 /* Counter subdevice */
562 s
= &dev
->subdevices
[1];
563 s
->type
= COMEDI_SUBD_COUNTER
;
564 s
->subdev_flags
= SDF_READABLE
| SDF_WRITABLE
| SDF_LSAMPL
;
566 s
->maxdata
= 0xffffffff;
567 s
->insn_read
= ni6501_cnt_insn_read
;
568 s
->insn_write
= ni6501_cnt_insn_write
;
569 s
->insn_config
= ni6501_cnt_insn_config
;
574 static void ni6501_detach(struct comedi_device
*dev
)
576 struct usb_interface
*intf
= comedi_to_usb_interface(dev
);
577 struct ni6501_private
*devpriv
= dev
->private;
584 usb_set_intfdata(intf
, NULL
);
586 kfree(devpriv
->usb_rx_buf
);
587 kfree(devpriv
->usb_tx_buf
);
592 static struct comedi_driver ni6501_driver
= {
593 .module
= THIS_MODULE
,
594 .driver_name
= "ni6501",
595 .auto_attach
= ni6501_auto_attach
,
596 .detach
= ni6501_detach
,
599 static int ni6501_usb_probe(struct usb_interface
*intf
,
600 const struct usb_device_id
*id
)
602 return comedi_usb_auto_config(intf
, &ni6501_driver
, id
->driver_info
);
605 static const struct usb_device_id ni6501_usb_table
[] = {
606 { USB_DEVICE(0x3923, 0x718a) },
609 MODULE_DEVICE_TABLE(usb
, ni6501_usb_table
);
611 static struct usb_driver ni6501_usb_driver
= {
613 .id_table
= ni6501_usb_table
,
614 .probe
= ni6501_usb_probe
,
615 .disconnect
= comedi_usb_auto_unconfig
,
617 module_comedi_usb_driver(ni6501_driver
, ni6501_usb_driver
);
619 MODULE_AUTHOR("Luca Ellero");
620 MODULE_DESCRIPTION("Comedi driver for National Instruments USB-6501");
621 MODULE_LICENSE("GPL");