staging: comedi: ni_usb6501: add counter subdevice

Add counter support for NI USB-6501.

The following functions are introduced:
- ni6501_counter_command()
- ni6501_cnt_insn_config()
- ni6501_cnt_insn_read()
- ni6501_cnt_insn_write()

Signed-off-by: Luca Ellero <luca.ellero@brickedbrain.com>
Reviewed-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Luca Ellero 2014-09-19 13:50:41 +02:00 committed by Greg Kroah-Hartman
parent 5116a48c1b
commit 7baf929491
1 changed files with 166 additions and 1 deletions

View File

@ -254,6 +254,96 @@ end:
return ret;
}
static int ni6501_counter_command(struct comedi_device *dev, int command,
u32 *val)
{
struct usb_device *usb = comedi_to_usb_dev(dev);
struct ni6501_private *devpriv = dev->private;
int request_size, response_size;
u8 *tx = devpriv->usb_tx_buf;
int ret;
if ((command == READ_COUNTER || command == WRITE_COUNTER) && !val)
return -EINVAL;
down(&devpriv->sem);
switch (command) {
case START_COUNTER:
request_size = sizeof(START_COUNTER_REQUEST);
response_size = sizeof(GENERIC_RESPONSE);
memcpy(tx, START_COUNTER_REQUEST, request_size);
break;
case STOP_COUNTER:
request_size = sizeof(STOP_COUNTER_REQUEST);
response_size = sizeof(GENERIC_RESPONSE);
memcpy(tx, STOP_COUNTER_REQUEST, request_size);
break;
case READ_COUNTER:
request_size = sizeof(READ_COUNTER_REQUEST);
response_size = sizeof(READ_COUNTER_RESPONSE);
memcpy(tx, READ_COUNTER_REQUEST, request_size);
break;
case WRITE_COUNTER:
request_size = sizeof(WRITE_COUNTER_REQUEST);
response_size = sizeof(GENERIC_RESPONSE);
memcpy(tx, WRITE_COUNTER_REQUEST, request_size);
/* Setup tx packet: bytes 12,13,14,15 hold the */
/* u32 counter value (Big Endian) */
*((__be32 *)&tx[12]) = cpu_to_be32(*val);
break;
default:
ret = -EINVAL;
goto end;
}
ret = usb_bulk_msg(usb,
usb_sndbulkpipe(usb,
devpriv->ep_tx->bEndpointAddress),
devpriv->usb_tx_buf,
request_size,
NULL,
NI6501_TIMEOUT);
if (ret)
goto end;
ret = usb_bulk_msg(usb,
usb_rcvbulkpipe(usb,
devpriv->ep_rx->bEndpointAddress),
devpriv->usb_rx_buf,
response_size,
NULL,
NI6501_TIMEOUT);
if (ret)
goto end;
/* Check if results are valid */
if (command == READ_COUNTER) {
int i;
/* Read counter value: bytes 12,13,14,15 of rx packet */
/* hold the u32 counter value (Big Endian) */
*val = be32_to_cpu(*((__be32 *)&devpriv->usb_rx_buf[12]));
/* mask counter value for comparing */
for (i = 12; i < sizeof(READ_COUNTER_RESPONSE); ++i)
devpriv->usb_rx_buf[i] = 0x00;
if (memcmp(devpriv->usb_rx_buf, READ_COUNTER_RESPONSE,
sizeof(READ_COUNTER_RESPONSE))) {
ret = -EINVAL;
}
} else if (memcmp(devpriv->usb_rx_buf, GENERIC_RESPONSE,
sizeof(GENERIC_RESPONSE))) {
ret = -EINVAL;
}
end:
up(&devpriv->sem);
return ret;
}
static int ni6501_dio_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
@ -311,6 +401,71 @@ static int ni6501_dio_insn_bits(struct comedi_device *dev,
return insn->n;
}
static int ni6501_cnt_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
int ret;
u32 val = 0;
switch (data[0]) {
case INSN_CONFIG_ARM:
ret = ni6501_counter_command(dev, START_COUNTER, NULL);
break;
case INSN_CONFIG_DISARM:
ret = ni6501_counter_command(dev, STOP_COUNTER, NULL);
break;
case INSN_CONFIG_RESET:
ret = ni6501_counter_command(dev, STOP_COUNTER, NULL);
if (ret)
break;
ret = ni6501_counter_command(dev, WRITE_COUNTER, &val);
break;
default:
return -EINVAL;
}
return ret ? ret : insn->n;
}
static int ni6501_cnt_insn_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
int ret;
u32 val;
unsigned int i;
for (i = 0; i < insn->n; i++) {
ret = ni6501_counter_command(dev, READ_COUNTER, &val);
if (ret)
return ret;
data[i] = val;
}
return insn->n;
}
static int ni6501_cnt_insn_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
int ret;
if (insn->n) {
u32 val = data[insn->n - 1];
ret = ni6501_counter_command(dev, WRITE_COUNTER, &val);
if (ret)
return ret;
}
return insn->n;
}
static int ni6501_alloc_usb_buffers(struct comedi_device *dev)
{
struct ni6501_private *devpriv = dev->private;
@ -389,7 +544,7 @@ static int ni6501_auto_attach(struct comedi_device *dev,
sema_init(&devpriv->sem, 1);
usb_set_intfdata(intf, devpriv);
ret = comedi_alloc_subdevices(dev, 1);
ret = comedi_alloc_subdevices(dev, 2);
if (ret)
return ret;
@ -403,6 +558,16 @@ static int ni6501_auto_attach(struct comedi_device *dev,
s->insn_bits = ni6501_dio_insn_bits;
s->insn_config = ni6501_dio_insn_config;
/* Counter subdevice */
s = &dev->subdevices[1];
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_WRITEABLE | SDF_LSAMPL;
s->n_chan = 1;
s->maxdata = 0xffffffff;
s->insn_read = ni6501_cnt_insn_read;
s->insn_write = ni6501_cnt_insn_write;
s->insn_config = ni6501_cnt_insn_config;
return 0;
}