linux/drivers/staging/comedi/drivers/ni_usb6501.c
<<
>>
Prefs
   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
 100#include "../comedi_usb.h"
 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
 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
 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
 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
 156enum commands {
 157        READ_PORT,
 158        WRITE_PORT,
 159        SET_PORT_DIR,
 160        START_COUNTER,
 161        STOP_COUNTER,
 162        READ_COUNTER,
 163        WRITE_COUNTER
 164};
 165
 166struct ni6501_private {
 167        struct usb_endpoint_descriptor *ep_rx;
 168        struct usb_endpoint_descriptor *ep_tx;
 169        struct semaphore sem;
 170        u8 *usb_rx_buf;
 171        u8 *usb_tx_buf;
 172};
 173
 174static int ni6501_port_command(struct comedi_device *dev, int command,
 175                               unsigned int val, u8 *bitmap)
 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
 183        if (command != SET_PORT_DIR && !bitmap)
 184                return -EINVAL;
 185
 186        down(&devpriv->sem);
 187
 188        switch (command) {
 189        case READ_PORT:
 190                request_size = sizeof(READ_PORT_REQUEST);
 191                response_size = sizeof(READ_PORT_RESPONSE);
 192                memcpy(tx, READ_PORT_REQUEST, request_size);
 193                tx[14] = val & 0xff;
 194                break;
 195        case WRITE_PORT:
 196                request_size = sizeof(WRITE_PORT_REQUEST);
 197                response_size = sizeof(GENERIC_RESPONSE);
 198                memcpy(tx, WRITE_PORT_REQUEST, request_size);
 199                tx[14] = val & 0xff;
 200                tx[17] = *bitmap;
 201                break;
 202        case SET_PORT_DIR:
 203                request_size = sizeof(SET_PORT_DIR_REQUEST);
 204                response_size = sizeof(GENERIC_RESPONSE);
 205                memcpy(tx, SET_PORT_DIR_REQUEST, request_size);
 206                tx[14] = val & 0xff;
 207                tx[15] = (val >> 8) & 0xff;
 208                tx[16] = (val >> 16) & 0xff;
 209                break;
 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) {
 238                *bitmap = devpriv->usb_rx_buf[14];
 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:
 251        up(&devpriv->sem);
 252
 253        return ret;
 254}
 255
 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
 268        down(&devpriv->sem);
 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:
 341        up(&devpriv->sem);
 342
 343        return ret;
 344}
 345
 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;
 352
 353        ret = comedi_dio_insn_config(dev, s, insn, data, 0);
 354        if (ret)
 355                return ret;
 356
 357        ret = ni6501_port_command(dev, SET_PORT_DIR, s->io_bits, NULL);
 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;
 379                        ret = ni6501_port_command(dev, WRITE_PORT,
 380                                                  port, &bitmap);
 381                        if (ret)
 382                                return ret;
 383                }
 384        }
 385
 386        data[1] = 0;
 387
 388        for (port = 0; port < 3; port++) {
 389                ret = ni6501_port_command(dev, READ_PORT, port, &bitmap);
 390                if (ret)
 391                        return ret;
 392                data[1] |= bitmap << port * 8;
 393        }
 394
 395        return insn->n;
 396}
 397
 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
 463static int ni6501_alloc_usb_buffers(struct comedi_device *dev)
 464{
 465        struct ni6501_private *devpriv = dev->private;
 466        size_t size;
 467
 468        size = le16_to_cpu(devpriv->ep_rx->wMaxPacketSize);
 469        devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL);
 470        if (!devpriv->usb_rx_buf)
 471                return -ENOMEM;
 472
 473        size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
 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
 538        sema_init(&devpriv->sem, 1);
 539        usb_set_intfdata(intf, devpriv);
 540
 541        ret = comedi_alloc_subdevices(dev, 2);
 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;
 552        s->insn_bits    = ni6501_dio_insn_bits;
 553        s->insn_config  = ni6501_dio_insn_config;
 554
 555        /* Counter subdevice */
 556        s = &dev->subdevices[1];
 557        s->type         = COMEDI_SUBD_COUNTER;
 558        s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
 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
 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
 576        down(&devpriv->sem);
 577
 578        usb_set_intfdata(intf, NULL);
 579
 580        kfree(devpriv->usb_rx_buf);
 581        kfree(devpriv->usb_tx_buf);
 582
 583        up(&devpriv->sem);
 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");
 616