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