[PATCH 2/2] iio: Add nt133 I/O board support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The NovaTech 133 I/O board is an expansion card for the NovaTech
OrionLXm with 16 digital input channels and 4 digital output channels.

Signed-off-by: George McCollister <george.mccollister@xxxxxxxxx>
---
 drivers/iio/Kconfig           |   1 +
 drivers/iio/Makefile          |   1 +
 drivers/iio/waveform/Kconfig  |  14 ++
 drivers/iio/waveform/Makefile |   5 +
 drivers/iio/waveform/nt133.c  | 572 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 593 insertions(+)
 create mode 100644 drivers/iio/waveform/Kconfig
 create mode 100644 drivers/iio/waveform/Makefile
 create mode 100644 drivers/iio/waveform/nt133.c

diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 345395e..c0af707 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -77,5 +77,6 @@ endif #IIO_TRIGGER
 source "drivers/iio/pressure/Kconfig"
 source "drivers/iio/proximity/Kconfig"
 source "drivers/iio/temperature/Kconfig"
+source "drivers/iio/waveform/Kconfig"
 
 endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 698afc2..599e34e 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -27,3 +27,4 @@ obj-y += pressure/
 obj-y += proximity/
 obj-y += temperature/
 obj-y += trigger/
+obj-y += waveform/
diff --git a/drivers/iio/waveform/Kconfig b/drivers/iio/waveform/Kconfig
new file mode 100644
index 0000000..c97558d
--- /dev/null
+++ b/drivers/iio/waveform/Kconfig
@@ -0,0 +1,14 @@
+#
+# Waveform drivers
+#
+menu "Waveform output"
+
+config NT133
+	tristate "NovaTech 133 I/O board"
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	depends on USB
+	help
+	  Say yes here to build support for NovaTech 133 I/O board.
+
+endmenu
diff --git a/drivers/iio/waveform/Makefile b/drivers/iio/waveform/Makefile
new file mode 100644
index 0000000..d59887c
--- /dev/null
+++ b/drivers/iio/waveform/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O waveform drivers
+#
+
+obj-$(CONFIG_NT133) += nt133.o
diff --git a/drivers/iio/waveform/nt133.c b/drivers/iio/waveform/nt133.c
new file mode 100644
index 0000000..8fdf172
--- /dev/null
+++ b/drivers/iio/waveform/nt133.c
@@ -0,0 +1,572 @@
+/*
+ * NovaTech 133 I/O board driver
+ *
+ * Copyright 2014 NovaTech LLC.
+ *
+ * George McCollister <george.mccollister@xxxxxxxxx>
+ *
+ * Portions derivied from USB Skeleton driver - 2.2
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define NT133_DRV_NAME		"nt133"
+#define NT133_VENDOR_ID		0x2aeb
+#define NT133_PRODUCT_ID	133
+
+#define NT133_USB_REQ_INPUT_STATUS	0x10
+#define NT133_USB_REQ_OUTPUT_STATUS	0x11
+#define NT133_USB_REQ_OUTPUT_CTRL	0x12
+#define NT133_USB_REQ_OUTPUT_ENABLE	0x13
+
+/* table of devices that work with this driver */
+static const struct usb_device_id nt133_table[] = {
+	{ USB_DEVICE(NT133_VENDOR_ID, NT133_PRODUCT_ID) },
+	{ }					/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, nt133_table);
+
+struct nt133_state {
+	/* the usb device for this device */
+	struct usb_device	*udev;
+	/* the interface for this device */
+	struct usb_interface	*interface;
+	/* in case we need to retract our submissions */
+	struct usb_anchor	submitted;
+	/* the urb to read data with */
+	struct urb		*int_in_urb;
+	/* the buffer to receive data */
+	__be16			int_in_data;
+	/* the address of the int in endpoint */
+	__u8			int_in_endpointAddr;
+	/* synchronize I/O with disconnect */
+	struct mutex		io_mutex;
+	/* Interrupt end point poll interval */
+	u8			bInterval;
+	struct iio_trigger	*trig;
+	u16			*buffer;
+};
+
+#define NT133_IN_CHAN(idx) {					\
+	.type = IIO_VOLTAGE,					\
+	.indexed = 1,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+	.channel = idx,						\
+	.scan_index = idx,					\
+	.scan_type = {						\
+		.sign		= 'u',				\
+		.realbits	= 1,				\
+		.storagebits	= 16,				\
+	},							\
+}
+
+#define NT133_OUT_CHAN(chan, chan2) {				\
+	.type = IIO_WAVEFORM,					\
+	.indexed = 1,						\
+	.scan_index = -1,					\
+	.output = 1,						\
+	.modified = 1,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+	.channel = chan,					\
+	.channel2 = chan2,					\
+	.scan_type = {						\
+		.sign		= 'u',				\
+		.realbits	= 29,				\
+		.storagebits	= 32,				\
+	},							\
+}
+
+static const struct iio_chan_spec nt133_channels[] = {
+	NT133_IN_CHAN(0),
+	NT133_IN_CHAN(1),
+	NT133_IN_CHAN(2),
+	NT133_IN_CHAN(3),
+	NT133_IN_CHAN(4),
+	NT133_IN_CHAN(5),
+	NT133_IN_CHAN(6),
+	NT133_IN_CHAN(7),
+	NT133_IN_CHAN(8),
+	NT133_IN_CHAN(9),
+	NT133_IN_CHAN(10),
+	NT133_IN_CHAN(11),
+	NT133_IN_CHAN(12),
+	NT133_IN_CHAN(13),
+	NT133_IN_CHAN(14),
+	NT133_IN_CHAN(15),
+	IIO_CHAN_SOFT_TIMESTAMP(16),
+	NT133_OUT_CHAN(0, IIO_MOD_HIGHTIME),
+	NT133_OUT_CHAN(0, IIO_MOD_LOWTIME),
+	NT133_OUT_CHAN(1, IIO_MOD_HIGHTIME),
+	NT133_OUT_CHAN(1, IIO_MOD_LOWTIME),
+	NT133_OUT_CHAN(2, IIO_MOD_HIGHTIME),
+	NT133_OUT_CHAN(2, IIO_MOD_LOWTIME),
+	NT133_OUT_CHAN(3, IIO_MOD_HIGHTIME),
+	NT133_OUT_CHAN(3, IIO_MOD_LOWTIME),
+};
+
+
+static void nt133_read_int_callback(struct urb *urb);
+
+static int nt133_do_read_io(struct nt133_state *st)
+{
+	int rv;
+
+	mutex_lock(&st->io_mutex);
+	if (!st->interface) {
+		mutex_unlock(&st->io_mutex);
+		rv = -ENODEV;
+		goto error;
+	}
+
+	usb_fill_int_urb(st->int_in_urb,
+			 st->udev,
+			 usb_rcvintpipe(st->udev, st->int_in_endpointAddr),
+			 &st->int_in_data,
+			 sizeof(st->int_in_data),
+			 nt133_read_int_callback,
+			 st,
+			 st->bInterval);
+	usb_anchor_urb(st->int_in_urb, &st->submitted);
+
+	rv = usb_submit_urb(st->int_in_urb, GFP_KERNEL);
+	mutex_unlock(&st->io_mutex);
+	if (rv < 0) {
+		usb_unanchor_urb(st->int_in_urb);
+		dev_err(&st->interface->dev,
+			"%s - failed submitting read urb, error %d\n",
+			__func__, rv);
+	}
+error:
+	return rv;
+}
+
+static void nt133_read_int_callback(struct urb *urb)
+{
+	struct nt133_state *st;
+
+	st = urb->context;
+
+	dev_dbg(&st->interface->dev,
+		"%s - urb status = %d, actual length = %d\n",
+		__func__, urb->status, urb->actual_length);
+
+	if (urb->status) {
+		if (!(urb->status == -ENOENT ||
+		    urb->status == -ECONNRESET ||
+		    urb->status == -ESHUTDOWN))
+			dev_err(&st->interface->dev,
+				"%s - nonzero write int status received: %d\n",
+				__func__, urb->status);
+		nt133_do_read_io(st);
+	} else if (urb->actual_length == sizeof(st->int_in_data)) {
+		iio_trigger_poll(st->trig);
+	} else {
+		dev_err(&st->interface->dev,
+			"%s - unexpected size of %d\n",
+			__func__, urb->actual_length);
+		nt133_do_read_io(st);
+	}
+}
+
+static int nt133_read_raw(struct iio_dev *indio_dev,
+			  struct iio_chan_spec const *chan,
+			  int *val,
+			  int *val2,
+			  long m)
+{
+	struct nt133_state *st = iio_priv(indio_dev);
+	int rv = -EINVAL;
+	u16 inbits;
+
+	switch (m) {
+	case IIO_CHAN_INFO_RAW:
+		rv = usb_control_msg(st->udev,
+				     usb_rcvctrlpipe(st->udev, 0),
+				     NT133_USB_REQ_INPUT_STATUS,
+				     USB_DIR_IN | USB_TYPE_VENDOR,
+				     0,
+				     0x0,
+				     &inbits,
+				     sizeof(inbits),
+				     USB_CTRL_SET_TIMEOUT);
+		if (rv < 0) {
+			dev_err(&st->interface->dev,
+				"%s - failed to get input status, error %d\n",
+				__func__, rv);
+		} else {
+			*val = (int) (be16_to_cpu(inbits) >> chan->channel)
+				      & 0x1;
+			rv = IIO_VAL_INT;
+		}
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		if (chan->type == IIO_VOLTAGE) {
+			/* A raw value of 0 indicates 0VDC */
+			/* A raw value of 1 indicates 90VDC */
+			*val = 90000000UL;
+			rv = IIO_VAL_INT;
+		} else if (chan->type == IIO_WAVEFORM) {
+			/* Scale raw time (milliseconds) to seconds */
+			*val = 0;
+			*val2 = 1000;
+			rv = IIO_VAL_INT_PLUS_MICRO;
+		}
+		break;
+	}
+
+	return rv;
+}
+
+static int nt133_write_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan,
+			   int val,
+			   int val2,
+			   long m)
+{
+	struct nt133_state *st = iio_priv(indio_dev);
+	const int max_val = (1 << chan->scan_type.realbits);
+	int rv;
+	u16 direction;
+	__be32 duration;
+
+	if (chan->channel2 == IIO_MOD_HIGHTIME)
+		direction = 0x1;
+	else if (chan->channel2 == IIO_MOD_LOWTIME)
+		direction = 0x0;
+	else
+		return -EINVAL;
+
+	if (val >= max_val || val < 0)
+		return -EINVAL;
+
+	/* Check if output is already in progress */
+	rv = usb_control_msg(st->udev,
+			     usb_rcvctrlpipe(st->udev, 0),
+			     NT133_USB_REQ_OUTPUT_STATUS,
+			     USB_DIR_IN | USB_TYPE_VENDOR,
+			     0x0,
+			     (u16)chan->channel,
+			     &duration,
+			     sizeof(duration),
+			     USB_CTRL_SET_TIMEOUT);
+	if (rv < 0) {
+		dev_err(&st->interface->dev,
+			"%s - failed to get output status, error %d\n",
+			__func__, rv);
+		return rv;
+	}
+
+	if (be32_to_cpu(duration))
+		return -EBUSY;
+
+	duration = cpu_to_be32((u32)val);
+
+	rv = usb_control_msg(st->udev,
+			     usb_sndctrlpipe(st->udev, 0),
+			     NT133_USB_REQ_OUTPUT_CTRL,
+			     USB_DIR_OUT | USB_TYPE_VENDOR,
+			     direction,
+			     (u16)chan->channel,
+			     &duration,
+			     sizeof(duration),
+			     USB_CTRL_SET_TIMEOUT);
+	if (rv < 0)
+		dev_err(&st->interface->dev,
+			"%s - failed to control output, error %d\n",
+			__func__, rv);
+	return rv;
+}
+
+static int nt133_enable_outputs(struct nt133_state *st)
+{
+	int rv;
+
+	rv = usb_control_msg(st->udev,
+			     usb_sndctrlpipe(st->udev, 0),
+			     NT133_USB_REQ_OUTPUT_ENABLE,
+			     USB_DIR_OUT | USB_TYPE_VENDOR,
+			     0x1, /*0=disabled, 1=enabled*/
+			     0x0,
+			     NULL,
+			     0,
+			     USB_CTRL_SET_TIMEOUT);
+	if (rv < 0)
+		dev_err(&st->interface->dev,
+			"%s - failed to enable outputs, error %d\n",
+			__func__, rv);
+	return rv;
+}
+
+static irqreturn_t nt133_trigger_handler(int irq, void *private)
+{
+	struct iio_poll_func *pf = private;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct nt133_state *st = iio_priv(indio_dev);
+	int i;
+	int j = 0;
+
+	dev_dbg(&st->interface->dev,
+		"%s - active_scan_mask=%d, masklength=%d\n",
+		__func__, (int)indio_dev->active_scan_mask[0],
+		indio_dev->masklength);
+
+	for_each_set_bit(i, indio_dev->active_scan_mask,
+		indio_dev->masklength) {
+		st->buffer[j++] = (u16)
+				  (be16_to_cpu(st->int_in_data) >> i) & 0x1;
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, st->buffer,
+					   pf->timestamp);
+	iio_trigger_notify_done(indio_dev->trig);
+
+	nt133_do_read_io(st);
+
+	return IRQ_HANDLED;
+}
+
+static int nt133_update_scan_mode(struct iio_dev *indio_dev,
+				  const unsigned long *scan_mask)
+{
+	struct nt133_state *st = iio_priv(indio_dev);
+
+	dev_dbg(&st->interface->dev,
+		"%s - scan_bytes=%d\n",
+		__func__, indio_dev->scan_bytes);
+
+	kfree(st->buffer);
+	st->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+	if (st->buffer == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static const struct iio_trigger_ops nt133_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static const struct iio_info nt133_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = nt133_read_raw,
+	.write_raw = nt133_write_raw,
+	.update_scan_mode = nt133_update_scan_mode,
+};
+
+static int nt133_probe(struct usb_interface *interface,
+		       const struct usb_device_id *id)
+{
+	struct iio_dev *indio_dev;
+	struct iio_trigger *trig;
+	struct nt133_state *st;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	int i;
+	int retval = -ENOMEM;
+
+	/* allocate memory for our device state and initialize it */
+	indio_dev = devm_iio_device_alloc(&interface->dev, sizeof(*st));
+	if (!indio_dev) {
+		dev_err(&interface->dev, "Out of memory\n");
+		goto error_ret;
+	}
+
+	st = iio_priv(indio_dev);
+
+	mutex_init(&st->io_mutex);
+	init_usb_anchor(&st->submitted);
+
+	st->udev = usb_get_dev(interface_to_usbdev(interface));
+	st->interface = interface;
+
+	/* set up the endpoint information */
+	/* use only the first int-in endpoint */
+	iface_desc = interface->cur_altsetting;
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (!st->int_in_endpointAddr &&
+		    usb_endpoint_is_int_in(endpoint)) {
+			/* we found a int in endpoint */
+			st->int_in_endpointAddr = endpoint->bEndpointAddress;
+			st->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+			if (!st->int_in_urb) {
+				dev_err(&interface->dev,
+					"Couldn't allocate int_in_urb.\n");
+				goto error_put_dev;
+			}
+			st->bInterval = endpoint->bInterval;
+			break;
+		}
+	}
+
+	if (!(st->int_in_endpointAddr)) {
+		dev_err(&interface->dev,
+			"Couldn't find int-in endpoint.\n");
+		goto error_put_dev;
+	}
+
+	indio_dev->dev.parent = &interface->dev;
+	indio_dev->name = NT133_DRV_NAME;
+	indio_dev->channels = nt133_channels;
+	indio_dev->num_channels = ARRAY_SIZE(nt133_channels);
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &nt133_info;
+
+	/* save our data pointer in this interface device */
+	usb_set_intfdata(interface, indio_dev);
+
+	trig = devm_iio_trigger_alloc(&interface->dev, "%s-dev%d",
+				      indio_dev->name, indio_dev->id);
+	if (!trig)
+		goto error_free_urb;
+
+	st->trig = trig;
+	trig->dev.parent = indio_dev->dev.parent;
+	iio_trigger_set_drvdata(trig, indio_dev);
+	trig->ops = &nt133_trigger_ops;
+
+	retval = iio_trigger_register(trig);
+	if (retval) {
+		dev_err(&interface->dev, "Failed to register trigger.\n");
+		goto error_free_urb;
+	}
+
+	retval = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+		&nt133_trigger_handler, NULL);
+	if (retval) {
+		dev_err(&interface->dev, "Failed to setup triggered buffer.\n");
+		goto error_unregister_trigger;
+	}
+
+	retval = iio_device_register(indio_dev);
+	if (retval) {
+		dev_err(&interface->dev, "Failed to regsiter device.\n");
+		goto error_triggered_buffer_cleanup;
+	}
+
+	retval = nt133_enable_outputs(st);
+	if (retval)
+		goto error_device_unregister;
+
+	retval = nt133_do_read_io(st);
+	if (retval)
+		goto error_device_unregister;
+
+	return 0;
+
+error_device_unregister:
+	iio_device_unregister(indio_dev);
+error_triggered_buffer_cleanup:
+	iio_triggered_buffer_cleanup(indio_dev);
+error_unregister_trigger:
+	iio_trigger_unregister(st->trig);
+error_free_urb:
+	usb_free_urb(st->int_in_urb);
+error_put_dev:
+	usb_put_dev(st->udev);
+error_ret:
+	usb_set_intfdata(interface, NULL);
+	return retval;
+}
+
+static void nt133_disconnect(struct usb_interface *interface)
+{
+	struct iio_dev *indio_dev = usb_get_intfdata(interface);
+	struct nt133_state *st = iio_priv(indio_dev);
+
+	usb_set_intfdata(interface, NULL);
+
+	/* prevent more I/O from starting */
+	mutex_lock(&st->io_mutex);
+	st->interface = NULL;
+	mutex_unlock(&st->io_mutex);
+
+	usb_kill_anchored_urbs(&st->submitted);
+
+	iio_device_unregister(indio_dev);
+
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	iio_trigger_unregister(st->trig);
+
+	usb_free_urb(st->int_in_urb);
+	usb_put_dev(st->udev);
+
+	dev_info(&interface->dev, "nt133 now disconnected");
+}
+
+static void nt133_draw_down(struct nt133_state *st)
+{
+	int time;
+
+	time = usb_wait_anchor_empty_timeout(&st->submitted, 1000);
+	if (!time)
+		usb_kill_anchored_urbs(&st->submitted);
+	usb_kill_urb(st->int_in_urb);
+}
+
+static int nt133_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct nt133_state *st = usb_get_intfdata(intf);
+
+	if (!st)
+		return 0;
+	nt133_draw_down(st);
+	return 0;
+}
+
+static int nt133_resume(struct usb_interface *intf)
+{
+	return 0;
+}
+
+static int nt133_pre_reset(struct usb_interface *intf)
+{
+	struct nt133_state *st = usb_get_intfdata(intf);
+
+	mutex_lock(&st->io_mutex);
+	nt133_draw_down(st);
+
+	return 0;
+}
+
+static int nt133_post_reset(struct usb_interface *intf)
+{
+	struct nt133_state *st = usb_get_intfdata(intf);
+
+	/* we are sure no URBs are active - no locking needed */
+	mutex_unlock(&st->io_mutex);
+
+	return 0;
+}
+
+static struct usb_driver nt133_driver = {
+	.name =		NT133_DRV_NAME,
+	.probe =	nt133_probe,
+	.disconnect =	nt133_disconnect,
+	.suspend =	nt133_suspend,
+	.resume =	nt133_resume,
+	.pre_reset =	nt133_pre_reset,
+	.post_reset =	nt133_post_reset,
+	.id_table =	nt133_table,
+};
+
+module_usb_driver(nt133_driver);
+
+MODULE_LICENSE("GPL");
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux