Commit d7569ad7 authored by Spencer E. Olson's avatar Spencer E. Olson Committed by Greg Kroah-Hartman
Browse files

staging: comedi: add new device-global config interface



Adds interface for configuring options that are global to all sub-devices.
For now, only options to configure device-globally identified signal routes
have been defined.

Signed-off-by: default avatarSpencer E. Olson <olsonse@umich.edu>
Reviewed-by: default avatarIan Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 5912827d
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -107,6 +107,7 @@
#define INSN_WRITE		(1 | INSN_MASK_WRITE)
#define INSN_BITS		(2 | INSN_MASK_READ | INSN_MASK_WRITE)
#define INSN_CONFIG		(3 | INSN_MASK_READ | INSN_MASK_WRITE)
#define INSN_DEVICE_CONFIG	(INSN_CONFIG | INSN_MASK_SPECIAL)
#define INSN_GTOD		(4 | INSN_MASK_READ | INSN_MASK_SPECIAL)
#define INSN_WAIT		(5 | INSN_MASK_WRITE | INSN_MASK_SPECIAL)
#define INSN_INTTRIG		(6 | INSN_MASK_WRITE | INSN_MASK_SPECIAL)
@@ -350,6 +351,23 @@ enum configuration_ids {
	INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS = 5005,
};

/**
 * enum device_configuration_ids - COMEDI configuration instruction codes global
 * to an entire device.
 * @INSN_DEVICE_CONFIG_TEST_ROUTE:	Validate the possibility of a
 *					globally-named route
 * @INSN_DEVICE_CONFIG_CONNECT_ROUTE:	Connect a globally-named route
 * @INSN_DEVICE_CONFIG_DISCONNECT_ROUTE:Disconnect a globally-named route
 * @INSN_DEVICE_CONFIG_GET_ROUTES:	Get a list of all globally-named routes
 *					that are valid for a particular device.
 */
enum device_config_route_ids {
	INSN_DEVICE_CONFIG_TEST_ROUTE = 0,
	INSN_DEVICE_CONFIG_CONNECT_ROUTE = 1,
	INSN_DEVICE_CONFIG_DISCONNECT_ROUTE = 2,
	INSN_DEVICE_CONFIG_GET_ROUTES = 3,
};

/**
 * enum comedi_digital_trig_op - operations for configuring a digital trigger
 * @COMEDI_DIGITAL_TRIG_DISABLE:	Return digital trigger to its default,
+69 −0
Original line number Diff line number Diff line
@@ -1234,6 +1234,57 @@ static int check_insn_config_length(struct comedi_insn *insn,
	return -EINVAL;
}

static int check_insn_device_config_length(struct comedi_insn *insn,
					   unsigned int *data)
{
	if (insn->n < 1)
		return -EINVAL;

	switch (data[0]) {
	case INSN_DEVICE_CONFIG_TEST_ROUTE:
	case INSN_DEVICE_CONFIG_CONNECT_ROUTE:
	case INSN_DEVICE_CONFIG_DISCONNECT_ROUTE:
		if (insn->n == 3)
			return 0;
		break;
	case INSN_DEVICE_CONFIG_GET_ROUTES:
		/*
		 * Big enough for config_id and the length of the userland
		 * memory buffer.  Additional length should be in factors of 2
		 * to communicate any returned route pairs (source,destination).
		 */
		if (insn->n >= 2)
			return 0;
		break;
	}
	return -EINVAL;
}

/**
 * get_valid_routes() - Calls low-level driver get_valid_routes function to
 *			either return a count of valid routes to user, or copy
 *			of list of all valid device routes to buffer in
 *			userspace.
 * @dev: comedi device pointer
 * @data: data from user insn call.  The length of the data must be >= 2.
 *	  data[0] must contain the INSN_DEVICE_CONFIG config_id.
 *	  data[1](input) contains the number of _pairs_ for which memory is
 *		  allotted from the user.  If the user specifies '0', then only
 *		  the number of pairs available is returned.
 *	  data[1](output) returns either the number of pairs available (if none
 *		  where requested) or the number of _pairs_ that are copied back
 *		  to the user.
 *	  data[2::2] returns each (source, destination) pair.
 *
 * Return: -EINVAL if low-level driver does not allocate and return routes as
 *	   expected.  Returns 0 otherwise.
 */
static int get_valid_routes(struct comedi_device *dev, unsigned int *data)
{
	data[1] = dev->get_valid_routes(dev, data[1], data + 2);
	return 0;
}

static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
		      unsigned int *data, void *file)
{
@@ -1297,6 +1348,24 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
			if (ret >= 0)
				ret = 1;
			break;
		case INSN_DEVICE_CONFIG:
			ret = check_insn_device_config_length(insn, data);
			if (ret)
				break;

			if (data[0] == INSN_DEVICE_CONFIG_GET_ROUTES) {
				/*
				 * data[1] should be the number of _pairs_ that
				 * the memory can hold.
				 */
				data[1] = (insn->n - 2) / 2;
				ret = get_valid_routes(dev, data);
				break;
			}

			/* other global device config instructions. */
			ret = dev->insn_device_config(dev, insn, data);
			break;
		default:
			dev_dbg(dev->class_dev, "invalid insn\n");
			ret = -EINVAL;
+14 −0
Original line number Diff line number Diff line
@@ -516,6 +516,15 @@ struct comedi_driver {
 *	called when @use_count changes from 0 to 1.
 * @close: Optional pointer to a function set by the low-level driver to be
 *	called when @use_count changed from 1 to 0.
 * @insn_device_config: Optional pointer to a handler for all sub-instructions
 *	except %INSN_DEVICE_CONFIG_GET_ROUTES of the %INSN_DEVICE_CONFIG
 *	instruction.  If this is not initialized by the low-level driver, a
 *	default handler will be set during post-configuration.
 * @get_valid_routes: Optional pointer to a handler for the
 *	%INSN_DEVICE_CONFIG_GET_ROUTES sub-instruction of the
 *	%INSN_DEVICE_CONFIG instruction set.  If this is not initialized by the
 *	low-level driver, a default handler that copies zero routes back to the
 *	user will be used.
 *
 * This is the main control data structure for a COMEDI device (as far as the
 * COMEDI core is concerned).  There are two groups of COMEDI devices -
@@ -565,6 +574,11 @@ struct comedi_device {

	int (*open)(struct comedi_device *dev);
	void (*close)(struct comedi_device *dev);
	int (*insn_device_config)(struct comedi_device *dev,
				  struct comedi_insn *insn, unsigned int *data);
	unsigned int (*get_valid_routes)(struct comedi_device *dev,
					 unsigned int n_pairs,
					 unsigned int *pair_data);
};

/*
+19 −0
Original line number Diff line number Diff line
@@ -211,6 +211,19 @@ static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s)
	return -EINVAL;
}

static int insn_device_inval(struct comedi_device *dev,
			     struct comedi_insn *insn, unsigned int *data)
{
	return -EINVAL;
}

static unsigned int get_zero_valid_routes(struct comedi_device *dev,
					  unsigned int n_pairs,
					  unsigned int *pair_data)
{
	return 0;
}

int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s,
	       struct comedi_insn *insn, unsigned int *data)
{
@@ -652,6 +665,12 @@ static int __comedi_device_postconfig(struct comedi_device *dev)
	int ret;
	int i;

	if (!dev->insn_device_config)
		dev->insn_device_config = insn_device_inval;

	if (!dev->get_valid_routes)
		dev->get_valid_routes = get_zero_valid_routes;

	for (i = 0; i < dev->n_subdevices; i++) {
		s = &dev->subdevices[i];