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> 24 * Updated: 8 Sep 2014 25 * Status: works 26 * 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 40 * every field (all values are hex): 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 * 57 * PORT PACKETS 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 62 * 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 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 * 94 */ 95 96#include <linux/kernel.h> 97#include <linux/module.h> 98#include <linux/slab.h> 99#include <linux/usb.h> 100 101#include "../comedidev.h" 102 103#define NI6501_TIMEOUT 1000 104 105/* Port request packets */ 106static const u8 READ_PORT_REQUEST[] = {0x00, 0x01, 0x00, 0x10, 107 0x00, 0x0C, 0x01, 0x0E, 108 0x02, 0x10, 0x00, 0x00, 109 0x00, 0x03, 0x00, 0x00}; 110 111static 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}; 116 117static 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}; 123 124/* Counter request packets */ 125static const u8 START_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x0C, 126 0x00, 0x08, 0x01, 0x09, 127 0x02, 0x20, 0x00, 0x00}; 128 129static const u8 STOP_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x0C, 130 0x00, 0x08, 0x01, 0x0C, 131 0x02, 0x20, 0x00, 0x00}; 132 133static const u8 READ_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x0C, 134 0x00, 0x08, 0x01, 0x0E, 135 0x02, 0x20, 0x00, 0x00}; 136 137static const u8 WRITE_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x10, 138 0x00, 0x0C, 0x01, 0x0F, 139 0x02, 0x20, 0x00, 0x00, 140 0x00, 0x00, 0x00, 0x00}; 141 142/* Response packets */ 143static const u8 GENERIC_RESPONSE[] = {0x00, 0x01, 0x00, 0x0C, 144 0x00, 0x08, 0x01, 0x00, 145 0x00, 0x00, 0x00, 0x02}; 146 147static const u8 READ_PORT_RESPONSE[] = {0x00, 0x01, 0x00, 0x10, 148 0x00, 0x0C, 0x01, 0x00, 149 0x00, 0x00, 0x00, 0x02, 150 0x00, 0x03, 0x00, 0x00}; 151 152static const u8 READ_COUNTER_RESPONSE[] = {0x00, 0x01, 0x00, 0x10, 153 0x00, 0x0C, 0x01, 0x00, 154 0x00, 0x00, 0x00, 0x02, 155 0x00, 0x00, 0x00, 0x00}; 156 157enum commands { 158 READ_PORT, 159 WRITE_PORT, 160 SET_PORT_DIR, 161 START_COUNTER, 162 STOP_COUNTER, 163 READ_COUNTER, 164 WRITE_COUNTER 165}; 166 167struct ni6501_private { 168 struct usb_endpoint_descriptor *ep_rx; 169 struct usb_endpoint_descriptor *ep_tx; 170 struct semaphore sem; 171 u8 *usb_rx_buf; 172 u8 *usb_tx_buf; 173}; 174 175static int ni6501_port_command(struct comedi_device *dev, int command, 176 const u8 *port, u8 *bitmap) 177{ 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; 182 int ret; 183 184 if (command != SET_PORT_DIR && !bitmap) 185 return -EINVAL; 186 187 down(&devpriv->sem); 188 189 switch (command) { 190 case READ_PORT: 191 request_size = sizeof(READ_PORT_REQUEST); 192 response_size = sizeof(READ_PORT_RESPONSE); 193 memcpy(tx, READ_PORT_REQUEST, request_size); 194 tx[14] = port[0]; 195 break; 196 case WRITE_PORT: 197 request_size = sizeof(WRITE_PORT_REQUEST); 198 response_size = sizeof(GENERIC_RESPONSE); 199 memcpy(tx, WRITE_PORT_REQUEST, request_size); 200 tx[14] = port[0]; 201 tx[17] = bitmap[0]; 202 break; 203 case SET_PORT_DIR: 204 request_size = sizeof(SET_PORT_DIR_REQUEST); 205 response_size = sizeof(GENERIC_RESPONSE); 206 memcpy(tx, SET_PORT_DIR_REQUEST, request_size); 207 tx[14] = port[0]; 208 tx[15] = port[1]; 209 tx[16] = port[2]; 210 break; 211 default: 212 ret = -EINVAL; 213 goto end; 214 } 215 216 ret = usb_bulk_msg(usb, 217 usb_sndbulkpipe(usb, 218 devpriv->ep_tx->bEndpointAddress), 219 devpriv->usb_tx_buf, 220 request_size, 221 NULL, 222 NI6501_TIMEOUT); 223 if (ret) 224 goto end; 225 226 ret = usb_bulk_msg(usb, 227 usb_rcvbulkpipe(usb, 228 devpriv->ep_rx->bEndpointAddress), 229 devpriv->usb_rx_buf, 230 response_size, 231 NULL, 232 NI6501_TIMEOUT); 233 if (ret) 234 goto end; 235 236 /* Check if results are valid */ 237 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; 242 243 if (memcmp(devpriv->usb_rx_buf, READ_PORT_RESPONSE, 244 sizeof(READ_PORT_RESPONSE))) { 245 ret = -EINVAL; 246 } 247 } else if (memcmp(devpriv->usb_rx_buf, GENERIC_RESPONSE, 248 sizeof(GENERIC_RESPONSE))) { 249 ret = -EINVAL; 250 } 251end: 252 up(&devpriv->sem); 253 254 return ret; 255} 256 257static int ni6501_counter_command(struct comedi_device *dev, int command, 258 u32 *val) 259{ 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; 264 int ret; 265 266 if ((command == READ_COUNTER || command == WRITE_COUNTER) && !val) 267 return -EINVAL; 268 269 down(&devpriv->sem); 270 271 switch (command) { 272 case START_COUNTER: 273 request_size = sizeof(START_COUNTER_REQUEST); 274 response_size = sizeof(GENERIC_RESPONSE); 275 memcpy(tx, START_COUNTER_REQUEST, request_size); 276 break; 277 case STOP_COUNTER: 278 request_size = sizeof(STOP_COUNTER_REQUEST); 279 response_size = sizeof(GENERIC_RESPONSE); 280 memcpy(tx, STOP_COUNTER_REQUEST, request_size); 281 break; 282 case READ_COUNTER: 283 request_size = sizeof(READ_COUNTER_REQUEST); 284 response_size = sizeof(READ_COUNTER_RESPONSE); 285 memcpy(tx, READ_COUNTER_REQUEST, request_size); 286 break; 287 case WRITE_COUNTER: 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); 294 break; 295 default: 296 ret = -EINVAL; 297 goto end; 298 } 299 300 ret = usb_bulk_msg(usb, 301 usb_sndbulkpipe(usb, 302 devpriv->ep_tx->bEndpointAddress), 303 devpriv->usb_tx_buf, 304 request_size, 305 NULL, 306 NI6501_TIMEOUT); 307 if (ret) 308 goto end; 309 310 ret = usb_bulk_msg(usb, 311 usb_rcvbulkpipe(usb, 312 devpriv->ep_rx->bEndpointAddress), 313 devpriv->usb_rx_buf, 314 response_size, 315 NULL, 316 NI6501_TIMEOUT); 317 if (ret) 318 goto end; 319 320 /* Check if results are valid */ 321 322 if (command == READ_COUNTER) { 323 int i; 324 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])); 328 329 /* mask counter value for comparing */ 330 for (i = 12; i < sizeof(READ_COUNTER_RESPONSE); ++i) 331 devpriv->usb_rx_buf[i] = 0x00; 332 333 if (memcmp(devpriv->usb_rx_buf, READ_COUNTER_RESPONSE, 334 sizeof(READ_COUNTER_RESPONSE))) { 335 ret = -EINVAL; 336 } 337 } else if (memcmp(devpriv->usb_rx_buf, GENERIC_RESPONSE, 338 sizeof(GENERIC_RESPONSE))) { 339 ret = -EINVAL; 340 } 341end: 342 up(&devpriv->sem); 343 344 return ret; 345} 346 347static int ni6501_dio_insn_config(struct comedi_device *dev, 348 struct comedi_subdevice *s, 349 struct comedi_insn *insn, 350 unsigned int *data) 351{ 352 int ret; 353 u8 port[3]; 354 355 ret = comedi_dio_insn_config(dev, s, insn, data, 0); 356 if (ret) 357 return ret; 358 359 port[0] = (s->io_bits) & 0xff; 360 port[1] = (s->io_bits >> 8) & 0xff; 361 port[2] = (s->io_bits >> 16) & 0xff; 362 363 ret = ni6501_port_command(dev, SET_PORT_DIR, port, NULL); 364 if (ret) 365 return ret; 366 367 return insn->n; 368} 369 370static int ni6501_dio_insn_bits(struct comedi_device *dev, 371 struct comedi_subdevice *s, 372 struct comedi_insn *insn, 373 unsigned int *data) 374{ 375 unsigned int mask; 376 int ret; 377 u8 port; 378 u8 bitmap; 379 380 mask = comedi_dio_update_state(s, data); 381 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, 386 &port, &bitmap); 387 if (ret) 388 return ret; 389 } 390 } 391 392 data[1] = 0; 393 394 for (port = 0; port < 3; port++) { 395 ret = ni6501_port_command(dev, READ_PORT, &port, &bitmap); 396 if (ret) 397 return ret; 398 data[1] |= bitmap << port * 8; 399 } 400 401 return insn->n; 402} 403 404static int ni6501_cnt_insn_config(struct comedi_device *dev, 405 struct comedi_subdevice *s, 406 struct comedi_insn *insn, 407 unsigned int *data) 408{ 409 int ret; 410 u32 val = 0; 411 412 switch (data[0]) { 413 case INSN_CONFIG_ARM: 414 ret = ni6501_counter_command(dev, START_COUNTER, NULL); 415 break; 416 case INSN_CONFIG_DISARM: 417 ret = ni6501_counter_command(dev, STOP_COUNTER, NULL); 418 break; 419 case INSN_CONFIG_RESET: 420 ret = ni6501_counter_command(dev, STOP_COUNTER, NULL); 421 if (ret) 422 break; 423 ret = ni6501_counter_command(dev, WRITE_COUNTER, &val); 424 break; 425 default: 426 return -EINVAL; 427 } 428 429 return ret ? ret : insn->n; 430} 431 432static int ni6501_cnt_insn_read(struct comedi_device *dev, 433 struct comedi_subdevice *s, 434 struct comedi_insn *insn, 435 unsigned int *data) 436{ 437 int ret; 438 u32 val; 439 unsigned int i; 440 441 for (i = 0; i < insn->n; i++) { 442 ret = ni6501_counter_command(dev, READ_COUNTER, &val); 443 if (ret) 444 return ret; 445 data[i] = val; 446 } 447 448 return insn->n; 449} 450 451static int ni6501_cnt_insn_write(struct comedi_device *dev, 452 struct comedi_subdevice *s, 453 struct comedi_insn *insn, 454 unsigned int *data) 455{ 456 int ret; 457 458 if (insn->n) { 459 u32 val = data[insn->n - 1]; 460 461 ret = ni6501_counter_command(dev, WRITE_COUNTER, &val); 462 if (ret) 463 return ret; 464 } 465 466 return insn->n; 467} 468 469static int ni6501_alloc_usb_buffers(struct comedi_device *dev) 470{ 471 struct ni6501_private *devpriv = dev->private; 472 size_t size; 473 474 size = le16_to_cpu(devpriv->ep_rx->wMaxPacketSize); 475 devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL); 476 if (!devpriv->usb_rx_buf) 477 return -ENOMEM; 478 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); 483 return -ENOMEM; 484 } 485 486 return 0; 487} 488 489static int ni6501_find_endpoints(struct comedi_device *dev) 490{ 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; 495 int i; 496 497 if (iface_desc->desc.bNumEndpoints != 2) { 498 dev_err(dev->class_dev, "Wrong number of endpoints\n"); 499 return -ENODEV; 500 } 501 502 for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { 503 ep_desc = &iface_desc->endpoint[i].desc; 504 505 if (usb_endpoint_is_bulk_in(ep_desc)) { 506 if (!devpriv->ep_rx) 507 devpriv->ep_rx = ep_desc; 508 continue; 509 } 510 511 if (usb_endpoint_is_bulk_out(ep_desc)) { 512 if (!devpriv->ep_tx) 513 devpriv->ep_tx = ep_desc; 514 continue; 515 } 516 } 517 518 if (!devpriv->ep_rx || !devpriv->ep_tx) 519 return -ENODEV; 520 521 return 0; 522} 523 524static int ni6501_auto_attach(struct comedi_device *dev, 525 unsigned long context) 526{ 527 struct usb_interface *intf = comedi_to_usb_interface(dev); 528 struct ni6501_private *devpriv; 529 struct comedi_subdevice *s; 530 int ret; 531 532 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 533 if (!devpriv) 534 return -ENOMEM; 535 536 ret = ni6501_find_endpoints(dev); 537 if (ret) 538 return ret; 539 540 ret = ni6501_alloc_usb_buffers(dev); 541 if (ret) 542 return ret; 543 544 sema_init(&devpriv->sem, 1); 545 usb_set_intfdata(intf, devpriv); 546 547 ret = comedi_alloc_subdevices(dev, 2); 548 if (ret) 549 return ret; 550 551 /* Digital Input/Output subdevice */ 552 s = &dev->subdevices[0]; 553 s->type = COMEDI_SUBD_DIO; 554 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 555 s->n_chan = 24; 556 s->maxdata = 1; 557 s->range_table = &range_digital; 558 s->insn_bits = ni6501_dio_insn_bits; 559 s->insn_config = ni6501_dio_insn_config; 560 561 /* Counter subdevice */ 562 s = &dev->subdevices[1]; 563 s->type = COMEDI_SUBD_COUNTER; 564 s->subdev_flags = SDF_READABLE | SDF_WRITEABLE | SDF_LSAMPL; 565 s->n_chan = 1; 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; 570 571 return 0; 572} 573 574static void ni6501_detach(struct comedi_device *dev) 575{ 576 struct usb_interface *intf = comedi_to_usb_interface(dev); 577 struct ni6501_private *devpriv = dev->private; 578 579 if (!devpriv) 580 return; 581 582 down(&devpriv->sem); 583 584 usb_set_intfdata(intf, NULL); 585 586 kfree(devpriv->usb_rx_buf); 587 kfree(devpriv->usb_tx_buf); 588 589 up(&devpriv->sem); 590} 591 592static struct comedi_driver ni6501_driver = { 593 .module = THIS_MODULE, 594 .driver_name = "ni6501", 595 .auto_attach = ni6501_auto_attach, 596 .detach = ni6501_detach, 597}; 598 599static int ni6501_usb_probe(struct usb_interface *intf, 600 const struct usb_device_id *id) 601{ 602 return comedi_usb_auto_config(intf, &ni6501_driver, id->driver_info); 603} 604 605static const struct usb_device_id ni6501_usb_table[] = { 606 { USB_DEVICE(0x3923, 0x718a) }, 607 { } 608}; 609MODULE_DEVICE_TABLE(usb, ni6501_usb_table); 610 611static struct usb_driver ni6501_usb_driver = { 612 .name = "ni6501", 613 .id_table = ni6501_usb_table, 614 .probe = ni6501_usb_probe, 615 .disconnect = comedi_usb_auto_unconfig, 616}; 617module_comedi_usb_driver(ni6501_driver, ni6501_usb_driver); 618 619MODULE_AUTHOR("Luca Ellero"); 620MODULE_DESCRIPTION("Comedi driver for National Instruments USB-6501"); 621MODULE_LICENSE("GPL"); 622