linux/drivers/staging/comedi/drivers/vmk80xx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * vmk80xx.c
   4 * Velleman USB Board Low-Level Driver
   5 *
   6 * Copyright (C) 2009 Manuel Gebele <forensixs@gmx.de>, Germany
   7 *
   8 * COMEDI - Linux Control and Measurement Device Interface
   9 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
  10 */
  11
  12/*
  13 * Driver: vmk80xx
  14 * Description: Velleman USB Board Low-Level Driver
  15 * Devices: [Velleman] K8055 (K8055/VM110), K8061 (K8061/VM140),
  16 *   VM110 (K8055/VM110), VM140 (K8061/VM140)
  17 * Author: Manuel Gebele <forensixs@gmx.de>
  18 * Updated: Sun, 10 May 2009 11:14:59 +0200
  19 * Status: works
  20 *
  21 * Supports:
  22 *  - analog input
  23 *  - analog output
  24 *  - digital input
  25 *  - digital output
  26 *  - counter
  27 *  - pwm
  28 */
  29
  30#include <linux/kernel.h>
  31#include <linux/module.h>
  32#include <linux/mutex.h>
  33#include <linux/errno.h>
  34#include <linux/input.h>
  35#include <linux/slab.h>
  36#include <linux/poll.h>
  37#include <linux/uaccess.h>
  38
  39#include "../comedi_usb.h"
  40
  41enum {
  42        DEVICE_VMK8055,
  43        DEVICE_VMK8061
  44};
  45
  46#define VMK8055_DI_REG          0x00
  47#define VMK8055_DO_REG          0x01
  48#define VMK8055_AO1_REG         0x02
  49#define VMK8055_AO2_REG         0x03
  50#define VMK8055_AI1_REG         0x02
  51#define VMK8055_AI2_REG         0x03
  52#define VMK8055_CNT1_REG        0x04
  53#define VMK8055_CNT2_REG        0x06
  54
  55#define VMK8061_CH_REG          0x01
  56#define VMK8061_DI_REG          0x01
  57#define VMK8061_DO_REG          0x01
  58#define VMK8061_PWM_REG1        0x01
  59#define VMK8061_PWM_REG2        0x02
  60#define VMK8061_CNT_REG         0x02
  61#define VMK8061_AO_REG          0x02
  62#define VMK8061_AI_REG1         0x02
  63#define VMK8061_AI_REG2         0x03
  64
  65#define VMK8055_CMD_RST         0x00
  66#define VMK8055_CMD_DEB1_TIME   0x01
  67#define VMK8055_CMD_DEB2_TIME   0x02
  68#define VMK8055_CMD_RST_CNT1    0x03
  69#define VMK8055_CMD_RST_CNT2    0x04
  70#define VMK8055_CMD_WRT_AD      0x05
  71
  72#define VMK8061_CMD_RD_AI       0x00
  73#define VMK8061_CMR_RD_ALL_AI   0x01    /* !non-active! */
  74#define VMK8061_CMD_SET_AO      0x02
  75#define VMK8061_CMD_SET_ALL_AO  0x03    /* !non-active! */
  76#define VMK8061_CMD_OUT_PWM     0x04
  77#define VMK8061_CMD_RD_DI       0x05
  78#define VMK8061_CMD_DO          0x06    /* !non-active! */
  79#define VMK8061_CMD_CLR_DO      0x07
  80#define VMK8061_CMD_SET_DO      0x08
  81#define VMK8061_CMD_RD_CNT      0x09    /* TODO: completely pointless? */
  82#define VMK8061_CMD_RST_CNT     0x0a    /* TODO: completely pointless? */
  83#define VMK8061_CMD_RD_VERSION  0x0b    /* internal usage */
  84#define VMK8061_CMD_RD_JMP_STAT 0x0c    /* TODO: not implemented yet */
  85#define VMK8061_CMD_RD_PWR_STAT 0x0d    /* internal usage */
  86#define VMK8061_CMD_RD_DO       0x0e
  87#define VMK8061_CMD_RD_AO       0x0f
  88#define VMK8061_CMD_RD_PWM      0x10
  89
  90#define IC3_VERSION             BIT(0)
  91#define IC6_VERSION             BIT(1)
  92
  93enum vmk80xx_model {
  94        VMK8055_MODEL,
  95        VMK8061_MODEL
  96};
  97
  98static const struct comedi_lrange vmk8061_range = {
  99        2, {
 100                UNI_RANGE(5),
 101                UNI_RANGE(10)
 102        }
 103};
 104
 105struct vmk80xx_board {
 106        const char *name;
 107        enum vmk80xx_model model;
 108        const struct comedi_lrange *range;
 109        int ai_nchans;
 110        unsigned int ai_maxdata;
 111        int ao_nchans;
 112        int di_nchans;
 113        unsigned int cnt_maxdata;
 114        int pwm_nchans;
 115        unsigned int pwm_maxdata;
 116};
 117
 118static const struct vmk80xx_board vmk80xx_boardinfo[] = {
 119        [DEVICE_VMK8055] = {
 120                .name           = "K8055 (VM110)",
 121                .model          = VMK8055_MODEL,
 122                .range          = &range_unipolar5,
 123                .ai_nchans      = 2,
 124                .ai_maxdata     = 0x00ff,
 125                .ao_nchans      = 2,
 126                .di_nchans      = 6,
 127                .cnt_maxdata    = 0xffff,
 128        },
 129        [DEVICE_VMK8061] = {
 130                .name           = "K8061 (VM140)",
 131                .model          = VMK8061_MODEL,
 132                .range          = &vmk8061_range,
 133                .ai_nchans      = 8,
 134                .ai_maxdata     = 0x03ff,
 135                .ao_nchans      = 8,
 136                .di_nchans      = 8,
 137                .cnt_maxdata    = 0,    /* unknown, device is not writeable */
 138                .pwm_nchans     = 1,
 139                .pwm_maxdata    = 0x03ff,
 140        },
 141};
 142
 143struct vmk80xx_private {
 144        struct usb_endpoint_descriptor *ep_rx;
 145        struct usb_endpoint_descriptor *ep_tx;
 146        struct semaphore limit_sem;
 147        unsigned char *usb_rx_buf;
 148        unsigned char *usb_tx_buf;
 149        enum vmk80xx_model model;
 150};
 151
 152static void vmk80xx_do_bulk_msg(struct comedi_device *dev)
 153{
 154        struct vmk80xx_private *devpriv = dev->private;
 155        struct usb_device *usb = comedi_to_usb_dev(dev);
 156        __u8 tx_addr;
 157        __u8 rx_addr;
 158        unsigned int tx_pipe;
 159        unsigned int rx_pipe;
 160        size_t size;
 161
 162        tx_addr = devpriv->ep_tx->bEndpointAddress;
 163        rx_addr = devpriv->ep_rx->bEndpointAddress;
 164        tx_pipe = usb_sndbulkpipe(usb, tx_addr);
 165        rx_pipe = usb_rcvbulkpipe(usb, rx_addr);
 166
 167        /*
 168         * The max packet size attributes of the K8061
 169         * input/output endpoints are identical
 170         */
 171        size = usb_endpoint_maxp(devpriv->ep_tx);
 172
 173        usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf,
 174                     size, NULL, devpriv->ep_tx->bInterval);
 175        usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, size, NULL, HZ * 10);
 176}
 177
 178static int vmk80xx_read_packet(struct comedi_device *dev)
 179{
 180        struct vmk80xx_private *devpriv = dev->private;
 181        struct usb_device *usb = comedi_to_usb_dev(dev);
 182        struct usb_endpoint_descriptor *ep;
 183        unsigned int pipe;
 184
 185        if (devpriv->model == VMK8061_MODEL) {
 186                vmk80xx_do_bulk_msg(dev);
 187                return 0;
 188        }
 189
 190        ep = devpriv->ep_rx;
 191        pipe = usb_rcvintpipe(usb, ep->bEndpointAddress);
 192        return usb_interrupt_msg(usb, pipe, devpriv->usb_rx_buf,
 193                                 usb_endpoint_maxp(ep), NULL,
 194                                 HZ * 10);
 195}
 196
 197static int vmk80xx_write_packet(struct comedi_device *dev, int cmd)
 198{
 199        struct vmk80xx_private *devpriv = dev->private;
 200        struct usb_device *usb = comedi_to_usb_dev(dev);
 201        struct usb_endpoint_descriptor *ep;
 202        unsigned int pipe;
 203
 204        devpriv->usb_tx_buf[0] = cmd;
 205
 206        if (devpriv->model == VMK8061_MODEL) {
 207                vmk80xx_do_bulk_msg(dev);
 208                return 0;
 209        }
 210
 211        ep = devpriv->ep_tx;
 212        pipe = usb_sndintpipe(usb, ep->bEndpointAddress);
 213        return usb_interrupt_msg(usb, pipe, devpriv->usb_tx_buf,
 214                                 usb_endpoint_maxp(ep), NULL,
 215                                 HZ * 10);
 216}
 217
 218static int vmk80xx_reset_device(struct comedi_device *dev)
 219{
 220        struct vmk80xx_private *devpriv = dev->private;
 221        size_t size;
 222        int retval;
 223
 224        size = usb_endpoint_maxp(devpriv->ep_tx);
 225        memset(devpriv->usb_tx_buf, 0, size);
 226        retval = vmk80xx_write_packet(dev, VMK8055_CMD_RST);
 227        if (retval)
 228                return retval;
 229        /* set outputs to known state as we cannot read them */
 230        return vmk80xx_write_packet(dev, VMK8055_CMD_WRT_AD);
 231}
 232
 233static int vmk80xx_ai_insn_read(struct comedi_device *dev,
 234                                struct comedi_subdevice *s,
 235                                struct comedi_insn *insn,
 236                                unsigned int *data)
 237{
 238        struct vmk80xx_private *devpriv = dev->private;
 239        int chan;
 240        int reg[2];
 241        int n;
 242
 243        down(&devpriv->limit_sem);
 244        chan = CR_CHAN(insn->chanspec);
 245
 246        switch (devpriv->model) {
 247        case VMK8055_MODEL:
 248                if (!chan)
 249                        reg[0] = VMK8055_AI1_REG;
 250                else
 251                        reg[0] = VMK8055_AI2_REG;
 252                break;
 253        case VMK8061_MODEL:
 254        default:
 255                reg[0] = VMK8061_AI_REG1;
 256                reg[1] = VMK8061_AI_REG2;
 257                devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AI;
 258                devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
 259                break;
 260        }
 261
 262        for (n = 0; n < insn->n; n++) {
 263                if (vmk80xx_read_packet(dev))
 264                        break;
 265
 266                if (devpriv->model == VMK8055_MODEL) {
 267                        data[n] = devpriv->usb_rx_buf[reg[0]];
 268                        continue;
 269                }
 270
 271                /* VMK8061_MODEL */
 272                data[n] = devpriv->usb_rx_buf[reg[0]] + 256 *
 273                    devpriv->usb_rx_buf[reg[1]];
 274        }
 275
 276        up(&devpriv->limit_sem);
 277
 278        return n;
 279}
 280
 281static int vmk80xx_ao_insn_write(struct comedi_device *dev,
 282                                 struct comedi_subdevice *s,
 283                                 struct comedi_insn *insn,
 284                                 unsigned int *data)
 285{
 286        struct vmk80xx_private *devpriv = dev->private;
 287        int chan;
 288        int cmd;
 289        int reg;
 290        int n;
 291
 292        down(&devpriv->limit_sem);
 293        chan = CR_CHAN(insn->chanspec);
 294
 295        switch (devpriv->model) {
 296        case VMK8055_MODEL:
 297                cmd = VMK8055_CMD_WRT_AD;
 298                if (!chan)
 299                        reg = VMK8055_AO1_REG;
 300                else
 301                        reg = VMK8055_AO2_REG;
 302                break;
 303        default:                /* NOTE: avoid compiler warnings */
 304                cmd = VMK8061_CMD_SET_AO;
 305                reg = VMK8061_AO_REG;
 306                devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
 307                break;
 308        }
 309
 310        for (n = 0; n < insn->n; n++) {
 311                devpriv->usb_tx_buf[reg] = data[n];
 312
 313                if (vmk80xx_write_packet(dev, cmd))
 314                        break;
 315        }
 316
 317        up(&devpriv->limit_sem);
 318
 319        return n;
 320}
 321
 322static int vmk80xx_ao_insn_read(struct comedi_device *dev,
 323                                struct comedi_subdevice *s,
 324                                struct comedi_insn *insn,
 325                                unsigned int *data)
 326{
 327        struct vmk80xx_private *devpriv = dev->private;
 328        int chan;
 329        int reg;
 330        int n;
 331
 332        down(&devpriv->limit_sem);
 333        chan = CR_CHAN(insn->chanspec);
 334
 335        reg = VMK8061_AO_REG - 1;
 336
 337        devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AO;
 338
 339        for (n = 0; n < insn->n; n++) {
 340                if (vmk80xx_read_packet(dev))
 341                        break;
 342
 343                data[n] = devpriv->usb_rx_buf[reg + chan];
 344        }
 345
 346        up(&devpriv->limit_sem);
 347
 348        return n;
 349}
 350
 351static int vmk80xx_di_insn_bits(struct comedi_device *dev,
 352                                struct comedi_subdevice *s,
 353                                struct comedi_insn *insn,
 354                                unsigned int *data)
 355{
 356        struct vmk80xx_private *devpriv = dev->private;
 357        unsigned char *rx_buf;
 358        int reg;
 359        int retval;
 360
 361        down(&devpriv->limit_sem);
 362
 363        rx_buf = devpriv->usb_rx_buf;
 364
 365        if (devpriv->model == VMK8061_MODEL) {
 366                reg = VMK8061_DI_REG;
 367                devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_DI;
 368        } else {
 369                reg = VMK8055_DI_REG;
 370        }
 371
 372        retval = vmk80xx_read_packet(dev);
 373
 374        if (!retval) {
 375                if (devpriv->model == VMK8055_MODEL)
 376                        data[1] = (((rx_buf[reg] >> 4) & 0x03) |
 377                                  ((rx_buf[reg] << 2) & 0x04) |
 378                                  ((rx_buf[reg] >> 3) & 0x18));
 379                else
 380                        data[1] = rx_buf[reg];
 381
 382                retval = 2;
 383        }
 384
 385        up(&devpriv->limit_sem);
 386
 387        return retval;
 388}
 389
 390static int vmk80xx_do_insn_bits(struct comedi_device *dev,
 391                                struct comedi_subdevice *s,
 392                                struct comedi_insn *insn,
 393                                unsigned int *data)
 394{
 395        struct vmk80xx_private *devpriv = dev->private;
 396        unsigned char *rx_buf = devpriv->usb_rx_buf;
 397        unsigned char *tx_buf = devpriv->usb_tx_buf;
 398        int reg, cmd;
 399        int ret = 0;
 400
 401        if (devpriv->model == VMK8061_MODEL) {
 402                reg = VMK8061_DO_REG;
 403                cmd = VMK8061_CMD_DO;
 404        } else { /* VMK8055_MODEL */
 405                reg = VMK8055_DO_REG;
 406                cmd = VMK8055_CMD_WRT_AD;
 407        }
 408
 409        down(&devpriv->limit_sem);
 410
 411        if (comedi_dio_update_state(s, data)) {
 412                tx_buf[reg] = s->state;
 413                ret = vmk80xx_write_packet(dev, cmd);
 414                if (ret)
 415                        goto out;
 416        }
 417
 418        if (devpriv->model == VMK8061_MODEL) {
 419                tx_buf[0] = VMK8061_CMD_RD_DO;
 420                ret = vmk80xx_read_packet(dev);
 421                if (ret)
 422                        goto out;
 423                data[1] = rx_buf[reg];
 424        } else {
 425                data[1] = s->state;
 426        }
 427
 428out:
 429        up(&devpriv->limit_sem);
 430
 431        return ret ? ret : insn->n;
 432}
 433
 434static int vmk80xx_cnt_insn_read(struct comedi_device *dev,
 435                                 struct comedi_subdevice *s,
 436                                 struct comedi_insn *insn,
 437                                 unsigned int *data)
 438{
 439        struct vmk80xx_private *devpriv = dev->private;
 440        int chan;
 441        int reg[2];
 442        int n;
 443
 444        down(&devpriv->limit_sem);
 445        chan = CR_CHAN(insn->chanspec);
 446
 447        switch (devpriv->model) {
 448        case VMK8055_MODEL:
 449                if (!chan)
 450                        reg[0] = VMK8055_CNT1_REG;
 451                else
 452                        reg[0] = VMK8055_CNT2_REG;
 453                break;
 454        case VMK8061_MODEL:
 455        default:
 456                reg[0] = VMK8061_CNT_REG;
 457                reg[1] = VMK8061_CNT_REG;
 458                devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_CNT;
 459                break;
 460        }
 461
 462        for (n = 0; n < insn->n; n++) {
 463                if (vmk80xx_read_packet(dev))
 464                        break;
 465
 466                if (devpriv->model == VMK8055_MODEL)
 467                        data[n] = devpriv->usb_rx_buf[reg[0]];
 468                else /* VMK8061_MODEL */
 469                        data[n] = devpriv->usb_rx_buf[reg[0] * (chan + 1) + 1]
 470                            + 256 * devpriv->usb_rx_buf[reg[1] * 2 + 2];
 471        }
 472
 473        up(&devpriv->limit_sem);
 474
 475        return n;
 476}
 477
 478static int vmk80xx_cnt_insn_config(struct comedi_device *dev,
 479                                   struct comedi_subdevice *s,
 480                                   struct comedi_insn *insn,
 481                                   unsigned int *data)
 482{
 483        struct vmk80xx_private *devpriv = dev->private;
 484        unsigned int chan = CR_CHAN(insn->chanspec);
 485        int cmd;
 486        int reg;
 487        int ret;
 488
 489        down(&devpriv->limit_sem);
 490        switch (data[0]) {
 491        case INSN_CONFIG_RESET:
 492                if (devpriv->model == VMK8055_MODEL) {
 493                        if (!chan) {
 494                                cmd = VMK8055_CMD_RST_CNT1;
 495                                reg = VMK8055_CNT1_REG;
 496                        } else {
 497                                cmd = VMK8055_CMD_RST_CNT2;
 498                                reg = VMK8055_CNT2_REG;
 499                        }
 500                        devpriv->usb_tx_buf[reg] = 0x00;
 501                } else {
 502                        cmd = VMK8061_CMD_RST_CNT;
 503                }
 504                ret = vmk80xx_write_packet(dev, cmd);
 505                break;
 506        default:
 507                ret = -EINVAL;
 508                break;
 509        }
 510        up(&devpriv->limit_sem);
 511
 512        return ret ? ret : insn->n;
 513}
 514
 515static int vmk80xx_cnt_insn_write(struct comedi_device *dev,
 516                                  struct comedi_subdevice *s,
 517                                  struct comedi_insn *insn,
 518                                  unsigned int *data)
 519{
 520        struct vmk80xx_private *devpriv = dev->private;
 521        unsigned long debtime;
 522        unsigned long val;
 523        int chan;
 524        int cmd;
 525        int n;
 526
 527        down(&devpriv->limit_sem);
 528        chan = CR_CHAN(insn->chanspec);
 529
 530        if (!chan)
 531                cmd = VMK8055_CMD_DEB1_TIME;
 532        else
 533                cmd = VMK8055_CMD_DEB2_TIME;
 534
 535        for (n = 0; n < insn->n; n++) {
 536                debtime = data[n];
 537                if (debtime == 0)
 538                        debtime = 1;
 539
 540                /* TODO: Prevent overflows */
 541                if (debtime > 7450)
 542                        debtime = 7450;
 543
 544                val = int_sqrt(debtime * 1000 / 115);
 545                if (((val + 1) * val) < debtime * 1000 / 115)
 546                        val += 1;
 547
 548                devpriv->usb_tx_buf[6 + chan] = val;
 549
 550                if (vmk80xx_write_packet(dev, cmd))
 551                        break;
 552        }
 553
 554        up(&devpriv->limit_sem);
 555
 556        return n;
 557}
 558
 559static int vmk80xx_pwm_insn_read(struct comedi_device *dev,
 560                                 struct comedi_subdevice *s,
 561                                 struct comedi_insn *insn,
 562                                 unsigned int *data)
 563{
 564        struct vmk80xx_private *devpriv = dev->private;
 565        unsigned char *tx_buf;
 566        unsigned char *rx_buf;
 567        int reg[2];
 568        int n;
 569
 570        down(&devpriv->limit_sem);
 571
 572        tx_buf = devpriv->usb_tx_buf;
 573        rx_buf = devpriv->usb_rx_buf;
 574
 575        reg[0] = VMK8061_PWM_REG1;
 576        reg[1] = VMK8061_PWM_REG2;
 577
 578        tx_buf[0] = VMK8061_CMD_RD_PWM;
 579
 580        for (n = 0; n < insn->n; n++) {
 581                if (vmk80xx_read_packet(dev))
 582                        break;
 583
 584                data[n] = rx_buf[reg[0]] + 4 * rx_buf[reg[1]];
 585        }
 586
 587        up(&devpriv->limit_sem);
 588
 589        return n;
 590}
 591
 592static int vmk80xx_pwm_insn_write(struct comedi_device *dev,
 593                                  struct comedi_subdevice *s,
 594                                  struct comedi_insn *insn,
 595                                  unsigned int *data)
 596{
 597        struct vmk80xx_private *devpriv = dev->private;
 598        unsigned char *tx_buf;
 599        int reg[2];
 600        int cmd;
 601        int n;
 602
 603        down(&devpriv->limit_sem);
 604
 605        tx_buf = devpriv->usb_tx_buf;
 606
 607        reg[0] = VMK8061_PWM_REG1;
 608        reg[1] = VMK8061_PWM_REG2;
 609
 610        cmd = VMK8061_CMD_OUT_PWM;
 611
 612        /*
 613         * The followin piece of code was translated from the inline
 614         * assembler code in the DLL source code.
 615         *
 616         * asm
 617         *   mov eax, k  ; k is the value (data[n])
 618         *   and al, 03h ; al are the lower 8 bits of eax
 619         *   mov lo, al  ; lo is the low part (tx_buf[reg[0]])
 620         *   mov eax, k
 621         *   shr eax, 2  ; right shift eax register by 2
 622         *   mov hi, al  ; hi is the high part (tx_buf[reg[1]])
 623         * end;
 624         */
 625        for (n = 0; n < insn->n; n++) {
 626                tx_buf[reg[0]] = (unsigned char)(data[n] & 0x03);
 627                tx_buf[reg[1]] = (unsigned char)(data[n] >> 2) & 0xff;
 628
 629                if (vmk80xx_write_packet(dev, cmd))
 630                        break;
 631        }
 632
 633        up(&devpriv->limit_sem);
 634
 635        return n;
 636}
 637
 638static int vmk80xx_find_usb_endpoints(struct comedi_device *dev)
 639{
 640        struct vmk80xx_private *devpriv = dev->private;
 641        struct usb_interface *intf = comedi_to_usb_interface(dev);
 642        struct usb_host_interface *iface_desc = intf->cur_altsetting;
 643        struct usb_endpoint_descriptor *ep_desc;
 644        int i;
 645
 646        if (iface_desc->desc.bNumEndpoints != 2)
 647                return -ENODEV;
 648
 649        for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
 650                ep_desc = &iface_desc->endpoint[i].desc;
 651
 652                if (usb_endpoint_is_int_in(ep_desc) ||
 653                    usb_endpoint_is_bulk_in(ep_desc)) {
 654                        if (!devpriv->ep_rx)
 655                                devpriv->ep_rx = ep_desc;
 656                        continue;
 657                }
 658
 659                if (usb_endpoint_is_int_out(ep_desc) ||
 660                    usb_endpoint_is_bulk_out(ep_desc)) {
 661                        if (!devpriv->ep_tx)
 662                                devpriv->ep_tx = ep_desc;
 663                        continue;
 664                }
 665        }
 666
 667        if (!devpriv->ep_rx || !devpriv->ep_tx)
 668                return -ENODEV;
 669
 670        return 0;
 671}
 672
 673static int vmk80xx_alloc_usb_buffers(struct comedi_device *dev)
 674{
 675        struct vmk80xx_private *devpriv = dev->private;
 676        size_t size;
 677
 678        size = usb_endpoint_maxp(devpriv->ep_rx);
 679        devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL);
 680        if (!devpriv->usb_rx_buf)
 681                return -ENOMEM;
 682
 683        size = usb_endpoint_maxp(devpriv->ep_tx);
 684        devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
 685        if (!devpriv->usb_tx_buf)
 686                return -ENOMEM;
 687
 688        return 0;
 689}
 690
 691static int vmk80xx_init_subdevices(struct comedi_device *dev)
 692{
 693        const struct vmk80xx_board *board = dev->board_ptr;
 694        struct vmk80xx_private *devpriv = dev->private;
 695        struct comedi_subdevice *s;
 696        int n_subd;
 697        int ret;
 698
 699        down(&devpriv->limit_sem);
 700
 701        if (devpriv->model == VMK8055_MODEL)
 702                n_subd = 5;
 703        else
 704                n_subd = 6;
 705        ret = comedi_alloc_subdevices(dev, n_subd);
 706        if (ret) {
 707                up(&devpriv->limit_sem);
 708                return ret;
 709        }
 710
 711        /* Analog input subdevice */
 712        s = &dev->subdevices[0];
 713        s->type         = COMEDI_SUBD_AI;
 714        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 715        s->n_chan       = board->ai_nchans;
 716        s->maxdata      = board->ai_maxdata;
 717        s->range_table  = board->range;
 718        s->insn_read    = vmk80xx_ai_insn_read;
 719
 720        /* Analog output subdevice */
 721        s = &dev->subdevices[1];
 722        s->type         = COMEDI_SUBD_AO;
 723        s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
 724        s->n_chan       = board->ao_nchans;
 725        s->maxdata      = 0x00ff;
 726        s->range_table  = board->range;
 727        s->insn_write   = vmk80xx_ao_insn_write;
 728        if (devpriv->model == VMK8061_MODEL) {
 729                s->subdev_flags |= SDF_READABLE;
 730                s->insn_read    = vmk80xx_ao_insn_read;
 731        }
 732
 733        /* Digital input subdevice */
 734        s = &dev->subdevices[2];
 735        s->type         = COMEDI_SUBD_DI;
 736        s->subdev_flags = SDF_READABLE;
 737        s->n_chan       = board->di_nchans;
 738        s->maxdata      = 1;
 739        s->range_table  = &range_digital;
 740        s->insn_bits    = vmk80xx_di_insn_bits;
 741
 742        /* Digital output subdevice */
 743        s = &dev->subdevices[3];
 744        s->type         = COMEDI_SUBD_DO;
 745        s->subdev_flags = SDF_WRITABLE;
 746        s->n_chan       = 8;
 747        s->maxdata      = 1;
 748        s->range_table  = &range_digital;
 749        s->insn_bits    = vmk80xx_do_insn_bits;
 750
 751        /* Counter subdevice */
 752        s = &dev->subdevices[4];
 753        s->type         = COMEDI_SUBD_COUNTER;
 754        s->subdev_flags = SDF_READABLE;
 755        s->n_chan       = 2;
 756        s->maxdata      = board->cnt_maxdata;
 757        s->insn_read    = vmk80xx_cnt_insn_read;
 758        s->insn_config  = vmk80xx_cnt_insn_config;
 759        if (devpriv->model == VMK8055_MODEL) {
 760                s->subdev_flags |= SDF_WRITABLE;
 761                s->insn_write   = vmk80xx_cnt_insn_write;
 762        }
 763
 764        /* PWM subdevice */
 765        if (devpriv->model == VMK8061_MODEL) {
 766                s = &dev->subdevices[5];
 767                s->type         = COMEDI_SUBD_PWM;
 768                s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 769                s->n_chan       = board->pwm_nchans;
 770                s->maxdata      = board->pwm_maxdata;
 771                s->insn_read    = vmk80xx_pwm_insn_read;
 772                s->insn_write   = vmk80xx_pwm_insn_write;
 773        }
 774
 775        up(&devpriv->limit_sem);
 776
 777        return 0;
 778}
 779
 780static int vmk80xx_auto_attach(struct comedi_device *dev,
 781                               unsigned long context)
 782{
 783        struct usb_interface *intf = comedi_to_usb_interface(dev);
 784        const struct vmk80xx_board *board = NULL;
 785        struct vmk80xx_private *devpriv;
 786        int ret;
 787
 788        if (context < ARRAY_SIZE(vmk80xx_boardinfo))
 789                board = &vmk80xx_boardinfo[context];
 790        if (!board)
 791                return -ENODEV;
 792        dev->board_ptr = board;
 793        dev->board_name = board->name;
 794
 795        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 796        if (!devpriv)
 797                return -ENOMEM;
 798
 799        devpriv->model = board->model;
 800
 801        sema_init(&devpriv->limit_sem, 8);
 802
 803        ret = vmk80xx_find_usb_endpoints(dev);
 804        if (ret)
 805                return ret;
 806
 807        ret = vmk80xx_alloc_usb_buffers(dev);
 808        if (ret)
 809                return ret;
 810
 811        usb_set_intfdata(intf, devpriv);
 812
 813        if (devpriv->model == VMK8055_MODEL)
 814                vmk80xx_reset_device(dev);
 815
 816        return vmk80xx_init_subdevices(dev);
 817}
 818
 819static void vmk80xx_detach(struct comedi_device *dev)
 820{
 821        struct usb_interface *intf = comedi_to_usb_interface(dev);
 822        struct vmk80xx_private *devpriv = dev->private;
 823
 824        if (!devpriv)
 825                return;
 826
 827        down(&devpriv->limit_sem);
 828
 829        usb_set_intfdata(intf, NULL);
 830
 831        kfree(devpriv->usb_rx_buf);
 832        kfree(devpriv->usb_tx_buf);
 833
 834        up(&devpriv->limit_sem);
 835}
 836
 837static struct comedi_driver vmk80xx_driver = {
 838        .module         = THIS_MODULE,
 839        .driver_name    = "vmk80xx",
 840        .auto_attach    = vmk80xx_auto_attach,
 841        .detach         = vmk80xx_detach,
 842};
 843
 844static int vmk80xx_usb_probe(struct usb_interface *intf,
 845                             const struct usb_device_id *id)
 846{
 847        return comedi_usb_auto_config(intf, &vmk80xx_driver, id->driver_info);
 848}
 849
 850static const struct usb_device_id vmk80xx_usb_id_table[] = {
 851        { USB_DEVICE(0x10cf, 0x5500), .driver_info = DEVICE_VMK8055 },
 852        { USB_DEVICE(0x10cf, 0x5501), .driver_info = DEVICE_VMK8055 },
 853        { USB_DEVICE(0x10cf, 0x5502), .driver_info = DEVICE_VMK8055 },
 854        { USB_DEVICE(0x10cf, 0x5503), .driver_info = DEVICE_VMK8055 },
 855        { USB_DEVICE(0x10cf, 0x8061), .driver_info = DEVICE_VMK8061 },
 856        { USB_DEVICE(0x10cf, 0x8062), .driver_info = DEVICE_VMK8061 },
 857        { USB_DEVICE(0x10cf, 0x8063), .driver_info = DEVICE_VMK8061 },
 858        { USB_DEVICE(0x10cf, 0x8064), .driver_info = DEVICE_VMK8061 },
 859        { USB_DEVICE(0x10cf, 0x8065), .driver_info = DEVICE_VMK8061 },
 860        { USB_DEVICE(0x10cf, 0x8066), .driver_info = DEVICE_VMK8061 },
 861        { USB_DEVICE(0x10cf, 0x8067), .driver_info = DEVICE_VMK8061 },
 862        { USB_DEVICE(0x10cf, 0x8068), .driver_info = DEVICE_VMK8061 },
 863        { }
 864};
 865MODULE_DEVICE_TABLE(usb, vmk80xx_usb_id_table);
 866
 867static struct usb_driver vmk80xx_usb_driver = {
 868        .name           = "vmk80xx",
 869        .id_table       = vmk80xx_usb_id_table,
 870        .probe          = vmk80xx_usb_probe,
 871        .disconnect     = comedi_usb_auto_unconfig,
 872};
 873module_comedi_usb_driver(vmk80xx_driver, vmk80xx_usb_driver);
 874
 875MODULE_AUTHOR("Manuel Gebele <forensixs@gmx.de>");
 876MODULE_DESCRIPTION("Velleman USB Board Low-Level Driver");
 877MODULE_SUPPORTED_DEVICE("K8055/K8061 aka VM110/VM140");
 878MODULE_LICENSE("GPL");
 879