[PATCH v2 13/13] staging: comedi: ni_660x: add device-global routing

Spencer E. Olson olsonse at umich.edu
Thu Sep 27 19:27:18 UTC 2018


Provides the device-global routing interface for ni_660x devices.  Using
the device-global names in comedi_cmd structures for commands was already
supported through the ni_tio module.

Signed-off-by: Spencer E. Olson <olsonse at umich.edu>
---
 drivers/staging/comedi/drivers/ni_660x.c | 265 +++++++++++++++++++++++
 1 file changed, 265 insertions(+)

diff --git a/drivers/staging/comedi/drivers/ni_660x.c b/drivers/staging/comedi/drivers/ni_660x.c
index 59055f366138..e70a461e723f 100644
--- a/drivers/staging/comedi/drivers/ni_660x.c
+++ b/drivers/staging/comedi/drivers/ni_660x.c
@@ -568,6 +568,10 @@ static void ni_660x_select_pfi_output(struct comedi_device *dev,
 	unsigned int idle_chip = 0;
 	unsigned int bits;
 
+	if (chan >= NI_PFI(0))
+		/* allow new and old names of pfi channels to work. */
+		chan -= NI_PFI(0);
+
 	if (board->n_chips > 1) {
 		if (out_sel == NI_660X_PFI_OUTPUT_COUNTER &&
 		    chan >= 8 && chan <= 23) {
@@ -603,6 +607,10 @@ static void ni_660x_set_pfi_direction(struct comedi_device *dev,
 	struct ni_660x_private *devpriv = dev->private;
 	u64 bit;
 
+	if (chan >= NI_PFI(0))
+		/* allow new and old names of pfi channels to work. */
+		chan -= NI_PFI(0);
+
 	bit = 1ULL << chan;
 
 	if (direction == COMEDI_OUTPUT) {
@@ -622,6 +630,10 @@ static unsigned int ni_660x_get_pfi_direction(struct comedi_device *dev,
 	struct ni_660x_private *devpriv = dev->private;
 	u64 bit;
 
+	if (chan >= NI_PFI(0))
+		/* allow new and old names of pfi channels to work. */
+		chan -= NI_PFI(0);
+
 	bit = 1ULL << chan;
 
 	return (devpriv->io_dir & bit) ? COMEDI_OUTPUT : COMEDI_INPUT;
@@ -632,6 +644,10 @@ static int ni_660x_set_pfi_routing(struct comedi_device *dev,
 {
 	struct ni_660x_private *devpriv = dev->private;
 
+	if (chan >= NI_PFI(0))
+		/* allow new and old names of pfi channels to work. */
+		chan -= NI_PFI(0);
+
 	switch (source) {
 	case NI_660X_PFI_OUTPUT_COUNTER:
 		if (chan < 8)
@@ -654,6 +670,10 @@ static int ni_660x_get_pfi_routing(struct comedi_device *dev, unsigned int chan)
 {
 	struct ni_660x_private *devpriv = dev->private;
 
+	if (chan >= NI_PFI(0))
+		/* allow new and old names of pfi channels to work. */
+		chan -= NI_PFI(0);
+
 	return devpriv->io_cfg[chan];
 }
 
@@ -662,6 +682,10 @@ static void ni_660x_set_pfi_filter(struct comedi_device *dev,
 {
 	unsigned int val;
 
+	if (chan >= NI_PFI(0))
+		/* allow new and old names of pfi channels to work. */
+		chan -= NI_PFI(0);
+
 	val = ni_660x_read(dev, 0, NI660X_IO_CFG(chan));
 	val &= ~NI660X_IO_CFG_IN_SEL_MASK(chan);
 	val |= NI660X_IO_CFG_IN_SEL(chan, value);
@@ -710,6 +734,240 @@ static int ni_660x_dio_insn_config(struct comedi_device *dev,
 	return insn->n;
 }
 
+static unsigned int _ni_get_valid_routes(struct comedi_device *dev,
+					 unsigned int n_pairs,
+					 unsigned int *pair_data)
+{
+	struct ni_660x_private *devpriv = dev->private;
+
+	return ni_get_valid_routes(&devpriv->routing_tables, n_pairs,
+				   pair_data);
+}
+
+/*
+ * Retrieves the current source of the output selector for the given
+ * destination.  If the terminal for the destination is not already configured
+ * as an output, this function returns -EINVAL as error.
+ *
+ * Return: The register value of the destination output selector;
+ *	   -EINVAL if terminal is not configured for output.
+ */
+static inline int get_output_select_source(int dest, struct comedi_device *dev)
+{
+	struct ni_660x_private *devpriv = dev->private;
+	int reg = -1;
+
+	if (channel_is_pfi(dest)) {
+		if (ni_660x_get_pfi_direction(dev, dest) == COMEDI_OUTPUT)
+			reg = ni_660x_get_pfi_routing(dev, dest);
+	} else if (channel_is_rtsi(dest)) {
+		dev_dbg(dev->class_dev,
+			"%s: unhandled rtsi destination (%d) queried\n",
+			__func__, dest);
+		/*
+		 * The following can be enabled when RTSI routing info is
+		 * determined (not currently documented):
+		 * if (ni_get_rtsi_direction(dev, dest) == COMEDI_OUTPUT) {
+		 *	reg = ni_get_rtsi_routing(dev, dest);
+
+		 *	if (reg == NI_RTSI_OUTPUT_RGOUT0) {
+		 *		dest = NI_RGOUT0; ** prepare for lookup below **
+		 *		reg = get_rgout0_reg(dev);
+		 *	} else if (reg >= NI_RTSI_OUTPUT_RTSI_BRD(0) &&
+		 *		   reg <= NI_RTSI_OUTPUT_RTSI_BRD(3)) {
+		 *		const int i = reg - NI_RTSI_OUTPUT_RTSI_BRD(0);
+
+		 *		dest = NI_RTSI_BRD(i); ** prepare for lookup **
+		 *		reg = get_ith_rtsi_brd_reg(i, dev);
+		 *	}
+		 * }
+		 */
+	} else if (channel_is_ctr(dest)) {
+		reg = ni_tio_get_routing(devpriv->counter_dev, dest);
+	} else {
+		dev_dbg(dev->class_dev,
+			"%s: unhandled destination (%d) queried\n",
+			__func__, dest);
+	}
+
+	if (reg >= 0)
+		return ni_find_route_source(CR_CHAN(reg), dest,
+					    &devpriv->routing_tables);
+	return -EINVAL;
+}
+
+/*
+ * Test a route:
+ *
+ * Return: -1 if not connectible;
+ *	    0 if connectible and not connected;
+ *	    1 if connectible and connected.
+ */
+static inline int test_route(unsigned int src, unsigned int dest,
+			     struct comedi_device *dev)
+{
+	struct ni_660x_private *devpriv = dev->private;
+	s8 reg = ni_route_to_register(CR_CHAN(src), dest,
+				      &devpriv->routing_tables);
+
+	if (reg < 0)
+		return -1;
+	if (get_output_select_source(dest, dev) != CR_CHAN(src))
+		return 0;
+	return 1;
+}
+
+/* Connect the actual route.  */
+static inline int connect_route(unsigned int src, unsigned int dest,
+				struct comedi_device *dev)
+{
+	struct ni_660x_private *devpriv = dev->private;
+	s8 reg = ni_route_to_register(CR_CHAN(src), dest,
+				      &devpriv->routing_tables);
+	s8 current_src;
+
+	if (reg < 0)
+		/* route is not valid */
+		return -EINVAL;
+
+	current_src = get_output_select_source(dest, dev);
+	if (current_src == CR_CHAN(src))
+		return -EALREADY;
+	if (current_src >= 0)
+		/* destination mux is already busy. complain, don't overwrite */
+		return -EBUSY;
+
+	/* The route is valid and available. Now connect... */
+	if (channel_is_pfi(CR_CHAN(dest))) {
+		/*
+		 * set routing and then direction so that the output does not
+		 * first get generated with the wrong pin
+		 */
+		ni_660x_set_pfi_routing(dev, dest, reg);
+		ni_660x_set_pfi_direction(dev, dest, COMEDI_OUTPUT);
+	} else if (channel_is_rtsi(CR_CHAN(dest))) {
+		dev_dbg(dev->class_dev, "%s: unhandled rtsi destination (%d)\n",
+			__func__, dest);
+		return -EINVAL;
+		/*
+		 * The following can be enabled when RTSI routing info is
+		 * determined (not currently documented):
+		 * if (reg == NI_RTSI_OUTPUT_RGOUT0) {
+		 *	int ret = incr_rgout0_src_use(src, dev);
+
+		 *	if (ret < 0)
+		 *		return ret;
+		 * } else if (ni_rtsi_route_requires_mux(reg)) {
+		 *	** Attempt to allocate and  route (src->brd) **
+		 *	int brd = incr_rtsi_brd_src_use(src, dev);
+
+		 *	if (brd < 0)
+		 *		return brd;
+
+		 *	** Now lookup the register value for (brd->dest) **
+		 *	reg = ni_lookup_route_register(brd, CR_CHAN(dest),
+		 *				       &devpriv->routing_tables);
+		 * }
+
+		 * ni_set_rtsi_direction(dev, dest, COMEDI_OUTPUT);
+		 * ni_set_rtsi_routing(dev, dest, reg);
+		 */
+	} else if (channel_is_ctr(CR_CHAN(dest))) {
+		/*
+		 * we are adding back the channel modifier info to set
+		 * invert/edge info passed by the user
+		 */
+		ni_tio_set_routing(devpriv->counter_dev, dest,
+				   reg | (src & ~CR_CHAN(-1)));
+	} else {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static inline int disconnect_route(unsigned int src, unsigned int dest,
+				   struct comedi_device *dev)
+{
+	struct ni_660x_private *devpriv = dev->private;
+	s8 reg = ni_route_to_register(CR_CHAN(src), CR_CHAN(dest),
+				      &devpriv->routing_tables);
+
+	if (reg < 0)
+		/* route is not valid */
+		return -EINVAL;
+	if (get_output_select_source(dest, dev) != CR_CHAN(src))
+		/* cannot disconnect something not connected */
+		return -EINVAL;
+
+	/* The route is valid and is connected.  Now disconnect... */
+	if (channel_is_pfi(CR_CHAN(dest))) {
+		unsigned int source = ((CR_CHAN(dest) - NI_PFI(0)) < 8)
+					? NI_660X_PFI_OUTPUT_DIO
+					: NI_660X_PFI_OUTPUT_COUNTER;
+
+		/* set the pfi to high impedance, and disconnect */
+		ni_660x_set_pfi_direction(dev, dest, COMEDI_INPUT);
+		ni_660x_set_pfi_routing(dev, dest, source);
+	} else if (channel_is_rtsi(CR_CHAN(dest))) {
+		dev_dbg(dev->class_dev, "%s: unhandled rtsi destination (%d)\n",
+			__func__, dest);
+		return -EINVAL;
+		/*
+		 * The following can be enabled when RTSI routing info is
+		 * determined (not currently documented):
+		 * if (reg == NI_RTSI_OUTPUT_RGOUT0) {
+		 *	int ret = decr_rgout0_src_use(src, dev);
+
+		 *	if (ret < 0)
+		 *		return ret;
+		 * } else if (ni_rtsi_route_requires_mux(reg)) {
+		 *	** find which RTSI_BRD line is source for rtsi pin **
+		 *	int brd = ni_find_route_source(
+		 *		ni_get_rtsi_routing(dev, dest), CR_CHAN(dest),
+		 *		&devpriv->routing_tables);
+
+		 *	if (brd < 0)
+		 *		return brd;
+
+		 *	** decrement/disconnect RTSI_BRD line from source **
+		 *	decr_rtsi_brd_src_use(src, brd, dev);
+		 * }
+
+		 * ** set rtsi output selector to default state **
+		 * reg = default_rtsi_routing[CR_CHAN(dest) - TRIGGER_LINE(0)];
+		 * ni_set_rtsi_direction(dev, dest, COMEDI_INPUT);
+		 * ni_set_rtsi_routing(dev, dest, reg);
+		 */
+	} else if (channel_is_ctr(CR_CHAN(dest))) {
+		ni_tio_unset_routing(devpriv->counter_dev, dest);
+	} else {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int ni_global_insn_config(struct comedi_device *dev,
+				 struct comedi_insn *insn,
+				 unsigned int *data)
+{
+	switch (data[0]) {
+	case INSN_DEVICE_CONFIG_TEST_ROUTE:
+		data[0] = test_route(data[1], data[2], dev);
+		return 2;
+	case INSN_DEVICE_CONFIG_CONNECT_ROUTE:
+		return connect_route(data[1], data[2], dev);
+	case INSN_DEVICE_CONFIG_DISCONNECT_ROUTE:
+		return disconnect_route(data[1], data[2], dev);
+	/*
+	 * This case is already handled one level up.
+	 * case INSN_DEVICE_CONFIG_GET_ROUTES:
+	 */
+	default:
+		return -EINVAL;
+	}
+	return 1;
+}
+
 static void ni_660x_init_tio_chips(struct comedi_device *dev,
 				   unsigned int n_chips)
 {
@@ -784,6 +1042,13 @@ static int ni_660x_auto_attach(struct comedi_device *dev,
 			 __func__, board->name);
 		dev_warn(dev->class_dev, "%s: High level NI signal names will not be available for this %s board.\n",
 			 __func__, board->name);
+	} else {
+		/*
+		 * only(?) assign insn_device_config if we have global names for
+		 * this device.
+		 */
+		dev->insn_device_config = ni_global_insn_config;
+		dev->get_valid_routes = _ni_get_valid_routes;
 	}
 
 	n_counters = board->n_chips * NI660X_COUNTERS_PER_CHIP;
-- 
2.17.1



More information about the devel mailing list