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: Spencer E. Olson <olsonse@umich.edu>
Reviewed-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Spencer E. Olson 2018-10-03 14:56:02 -06:00 committed by Greg Kroah-Hartman
parent 5912827dfe
commit d7569ad766
4 changed files with 120 additions and 0 deletions

View File

@ -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,

View File

@ -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;

View File

@ -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);
};
/*

View File

@ -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];