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