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 <olso...@umich.edu>
---
 drivers/staging/comedi/drivers/ni_660x.c | 260 +++++++++++++++++++++++++++++++
 1 file changed, 260 insertions(+)

diff --git a/drivers/staging/comedi/drivers/ni_660x.c 
b/drivers/staging/comedi/drivers/ni_660x.c
index 41c4960..bd1c9c1 100644
--- a/drivers/staging/comedi/drivers/ni_660x.c
+++ b/drivers/staging/comedi/drivers/ni_660x.c
@@ -572,6 +572,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) {
@@ -607,6 +611,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) {
@@ -626,6 +634,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;
@@ -636,6 +648,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)
@@ -658,6 +674,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];
 }
 
@@ -666,6 +686,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);
@@ -714,6 +738,235 @@ 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,
+                                        struct comedi_route_pair **pptr) {
+       struct ni_660x_private *devpriv = dev->private;
+
+       return ni_get_valid_routes(&devpriv->routing_tables, pptr);
+}
+
+/*
+ * 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)
 {
@@ -788,6 +1041,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;
-- 
1.9.1

_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to