|
|
@@ -45,14 +45,6 @@
|
|
|
#include "comedidev.h"
|
|
|
#include "comedi_internal.h"
|
|
|
|
|
|
-static int postconfig(struct comedi_device *dev);
|
|
|
-static int insn_rw_emulate_bits(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- struct comedi_insn *insn, unsigned int *data);
|
|
|
-static void *comedi_recognize(struct comedi_driver *driv, const char *name);
|
|
|
-static void comedi_report_boards(struct comedi_driver *driv);
|
|
|
-static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s);
|
|
|
-
|
|
|
struct comedi_driver *comedi_drivers;
|
|
|
|
|
|
int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices)
|
|
|
@@ -131,121 +123,53 @@ void comedi_device_detach(struct comedi_device *dev)
|
|
|
__comedi_device_detach(dev);
|
|
|
}
|
|
|
|
|
|
-/* do a little post-config cleanup */
|
|
|
-/* called with module refcount incremented, decrements it */
|
|
|
-static int comedi_device_postconfig(struct comedi_device *dev)
|
|
|
-{
|
|
|
- int ret = postconfig(dev);
|
|
|
- module_put(dev->driver->module);
|
|
|
- if (ret < 0) {
|
|
|
- __comedi_device_detach(dev);
|
|
|
- return ret;
|
|
|
- }
|
|
|
- if (!dev->board_name) {
|
|
|
- dev_warn(dev->class_dev, "BUG: dev->board_name=NULL\n");
|
|
|
- dev->board_name = "BUG";
|
|
|
- }
|
|
|
- smp_wmb();
|
|
|
- dev->attached = 1;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
|
|
|
+static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s)
|
|
|
{
|
|
|
- struct comedi_driver *driv;
|
|
|
- int ret;
|
|
|
-
|
|
|
- if (dev->attached)
|
|
|
- return -EBUSY;
|
|
|
-
|
|
|
- for (driv = comedi_drivers; driv; driv = driv->next) {
|
|
|
- if (!try_module_get(driv->module))
|
|
|
- continue;
|
|
|
- if (driv->num_names) {
|
|
|
- dev->board_ptr = comedi_recognize(driv, it->board_name);
|
|
|
- if (dev->board_ptr)
|
|
|
- break;
|
|
|
- } else if (strcmp(driv->driver_name, it->board_name) == 0)
|
|
|
- break;
|
|
|
- module_put(driv->module);
|
|
|
- }
|
|
|
- if (driv == NULL) {
|
|
|
- /* recognize has failed if we get here */
|
|
|
- /* report valid board names before returning error */
|
|
|
- for (driv = comedi_drivers; driv; driv = driv->next) {
|
|
|
- if (!try_module_get(driv->module))
|
|
|
- continue;
|
|
|
- comedi_report_boards(driv);
|
|
|
- module_put(driv->module);
|
|
|
- }
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
- if (driv->attach == NULL) {
|
|
|
- /* driver does not support manual configuration */
|
|
|
- dev_warn(dev->class_dev,
|
|
|
- "driver '%s' does not support attach using comedi_config\n",
|
|
|
- driv->driver_name);
|
|
|
- module_put(driv->module);
|
|
|
- return -ENOSYS;
|
|
|
- }
|
|
|
- /* initialize dev->driver here so
|
|
|
- * comedi_error() can be called from attach */
|
|
|
- dev->driver = driv;
|
|
|
- ret = driv->attach(dev, it);
|
|
|
- if (ret < 0) {
|
|
|
- module_put(dev->driver->module);
|
|
|
- __comedi_device_detach(dev);
|
|
|
- return ret;
|
|
|
- }
|
|
|
- return comedi_device_postconfig(dev);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
-int comedi_driver_register(struct comedi_driver *driver)
|
|
|
+int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s,
|
|
|
+ struct comedi_insn *insn, unsigned int *data)
|
|
|
{
|
|
|
- driver->next = comedi_drivers;
|
|
|
- comedi_drivers = driver;
|
|
|
-
|
|
|
- return 0;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(comedi_driver_register);
|
|
|
|
|
|
-int comedi_driver_unregister(struct comedi_driver *driver)
|
|
|
+static int insn_rw_emulate_bits(struct comedi_device *dev,
|
|
|
+ struct comedi_subdevice *s,
|
|
|
+ struct comedi_insn *insn, unsigned int *data)
|
|
|
{
|
|
|
- struct comedi_driver *prev;
|
|
|
- int i;
|
|
|
-
|
|
|
- /* check for devices using this driver */
|
|
|
- for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) {
|
|
|
- struct comedi_device *dev = comedi_dev_from_minor(i);
|
|
|
+ struct comedi_insn new_insn;
|
|
|
+ int ret;
|
|
|
+ static const unsigned channels_per_bitfield = 32;
|
|
|
|
|
|
- if (!dev)
|
|
|
- continue;
|
|
|
+ unsigned chan = CR_CHAN(insn->chanspec);
|
|
|
+ const unsigned base_bitfield_channel =
|
|
|
+ (chan < channels_per_bitfield) ? 0 : chan;
|
|
|
+ unsigned int new_data[2];
|
|
|
+ memset(new_data, 0, sizeof(new_data));
|
|
|
+ memset(&new_insn, 0, sizeof(new_insn));
|
|
|
+ new_insn.insn = INSN_BITS;
|
|
|
+ new_insn.chanspec = base_bitfield_channel;
|
|
|
+ new_insn.n = 2;
|
|
|
+ new_insn.subdev = insn->subdev;
|
|
|
|
|
|
- mutex_lock(&dev->mutex);
|
|
|
- if (dev->attached && dev->driver == driver) {
|
|
|
- if (dev->use_count)
|
|
|
- dev_warn(dev->class_dev,
|
|
|
- "BUG! detaching device with use_count=%d\n",
|
|
|
- dev->use_count);
|
|
|
- comedi_device_detach(dev);
|
|
|
- }
|
|
|
- mutex_unlock(&dev->mutex);
|
|
|
+ if (insn->insn == INSN_WRITE) {
|
|
|
+ if (!(s->subdev_flags & SDF_WRITABLE))
|
|
|
+ return -EINVAL;
|
|
|
+ new_data[0] = 1 << (chan - base_bitfield_channel); /* mask */
|
|
|
+ new_data[1] = data[0] ? (1 << (chan - base_bitfield_channel))
|
|
|
+ : 0; /* bits */
|
|
|
}
|
|
|
|
|
|
- if (comedi_drivers == driver) {
|
|
|
- comedi_drivers = driver->next;
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ ret = s->insn_bits(dev, s, &new_insn, new_data);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
|
|
|
- for (prev = comedi_drivers; prev->next; prev = prev->next) {
|
|
|
- if (prev->next == driver) {
|
|
|
- prev->next = driver->next;
|
|
|
- return 0;
|
|
|
- }
|
|
|
- }
|
|
|
- return -EINVAL;
|
|
|
+ if (insn->insn == INSN_READ)
|
|
|
+ data[0] = (new_data[1] >> (chan - base_bitfield_channel)) & 1;
|
|
|
+
|
|
|
+ return 1;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(comedi_driver_unregister);
|
|
|
|
|
|
static int postconfig(struct comedi_device *dev)
|
|
|
{
|
|
|
@@ -326,6 +250,25 @@ static int postconfig(struct comedi_device *dev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* do a little post-config cleanup */
|
|
|
+/* called with module refcount incremented, decrements it */
|
|
|
+static int comedi_device_postconfig(struct comedi_device *dev)
|
|
|
+{
|
|
|
+ int ret = postconfig(dev);
|
|
|
+ module_put(dev->driver->module);
|
|
|
+ if (ret < 0) {
|
|
|
+ __comedi_device_detach(dev);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ if (!dev->board_name) {
|
|
|
+ dev_warn(dev->class_dev, "BUG: dev->board_name=NULL\n");
|
|
|
+ dev->board_name = "BUG";
|
|
|
+ }
|
|
|
+ smp_wmb();
|
|
|
+ dev->attached = 1;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Generic recognize function for drivers that register their supported
|
|
|
* board names.
|
|
|
@@ -384,53 +327,102 @@ static void comedi_report_boards(struct comedi_driver *driv)
|
|
|
pr_info(" %s\n", driv->driver_name);
|
|
|
}
|
|
|
|
|
|
-static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s)
|
|
|
+int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
|
|
|
{
|
|
|
- return -EINVAL;
|
|
|
+ struct comedi_driver *driv;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (dev->attached)
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ for (driv = comedi_drivers; driv; driv = driv->next) {
|
|
|
+ if (!try_module_get(driv->module))
|
|
|
+ continue;
|
|
|
+ if (driv->num_names) {
|
|
|
+ dev->board_ptr = comedi_recognize(driv, it->board_name);
|
|
|
+ if (dev->board_ptr)
|
|
|
+ break;
|
|
|
+ } else if (strcmp(driv->driver_name, it->board_name) == 0)
|
|
|
+ break;
|
|
|
+ module_put(driv->module);
|
|
|
+ }
|
|
|
+ if (driv == NULL) {
|
|
|
+ /* recognize has failed if we get here */
|
|
|
+ /* report valid board names before returning error */
|
|
|
+ for (driv = comedi_drivers; driv; driv = driv->next) {
|
|
|
+ if (!try_module_get(driv->module))
|
|
|
+ continue;
|
|
|
+ comedi_report_boards(driv);
|
|
|
+ module_put(driv->module);
|
|
|
+ }
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+ if (driv->attach == NULL) {
|
|
|
+ /* driver does not support manual configuration */
|
|
|
+ dev_warn(dev->class_dev,
|
|
|
+ "driver '%s' does not support attach using comedi_config\n",
|
|
|
+ driv->driver_name);
|
|
|
+ module_put(driv->module);
|
|
|
+ return -ENOSYS;
|
|
|
+ }
|
|
|
+ /* initialize dev->driver here so
|
|
|
+ * comedi_error() can be called from attach */
|
|
|
+ dev->driver = driv;
|
|
|
+ ret = driv->attach(dev, it);
|
|
|
+ if (ret < 0) {
|
|
|
+ module_put(dev->driver->module);
|
|
|
+ __comedi_device_detach(dev);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ return comedi_device_postconfig(dev);
|
|
|
}
|
|
|
|
|
|
-int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s,
|
|
|
- struct comedi_insn *insn, unsigned int *data)
|
|
|
+int comedi_driver_register(struct comedi_driver *driver)
|
|
|
{
|
|
|
- return -EINVAL;
|
|
|
+ driver->next = comedi_drivers;
|
|
|
+ comedi_drivers = driver;
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
+EXPORT_SYMBOL(comedi_driver_register);
|
|
|
|
|
|
-static int insn_rw_emulate_bits(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- struct comedi_insn *insn, unsigned int *data)
|
|
|
+int comedi_driver_unregister(struct comedi_driver *driver)
|
|
|
{
|
|
|
- struct comedi_insn new_insn;
|
|
|
- int ret;
|
|
|
- static const unsigned channels_per_bitfield = 32;
|
|
|
+ struct comedi_driver *prev;
|
|
|
+ int i;
|
|
|
|
|
|
- unsigned chan = CR_CHAN(insn->chanspec);
|
|
|
- const unsigned base_bitfield_channel =
|
|
|
- (chan < channels_per_bitfield) ? 0 : chan;
|
|
|
- unsigned int new_data[2];
|
|
|
- memset(new_data, 0, sizeof(new_data));
|
|
|
- memset(&new_insn, 0, sizeof(new_insn));
|
|
|
- new_insn.insn = INSN_BITS;
|
|
|
- new_insn.chanspec = base_bitfield_channel;
|
|
|
- new_insn.n = 2;
|
|
|
- new_insn.subdev = insn->subdev;
|
|
|
+ /* check for devices using this driver */
|
|
|
+ for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) {
|
|
|
+ struct comedi_device *dev = comedi_dev_from_minor(i);
|
|
|
|
|
|
- if (insn->insn == INSN_WRITE) {
|
|
|
- if (!(s->subdev_flags & SDF_WRITABLE))
|
|
|
- return -EINVAL;
|
|
|
- new_data[0] = 1 << (chan - base_bitfield_channel); /* mask */
|
|
|
- new_data[1] = data[0] ? (1 << (chan - base_bitfield_channel))
|
|
|
- : 0; /* bits */
|
|
|
- }
|
|
|
+ if (!dev)
|
|
|
+ continue;
|
|
|
|
|
|
- ret = s->insn_bits(dev, s, &new_insn, new_data);
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
+ mutex_lock(&dev->mutex);
|
|
|
+ if (dev->attached && dev->driver == driver) {
|
|
|
+ if (dev->use_count)
|
|
|
+ dev_warn(dev->class_dev,
|
|
|
+ "BUG! detaching device with use_count=%d\n",
|
|
|
+ dev->use_count);
|
|
|
+ comedi_device_detach(dev);
|
|
|
+ }
|
|
|
+ mutex_unlock(&dev->mutex);
|
|
|
+ }
|
|
|
|
|
|
- if (insn->insn == INSN_READ)
|
|
|
- data[0] = (new_data[1] >> (chan - base_bitfield_channel)) & 1;
|
|
|
+ if (comedi_drivers == driver) {
|
|
|
+ comedi_drivers = driver->next;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
|
|
|
- return 1;
|
|
|
+ for (prev = comedi_drivers; prev->next; prev = prev->next) {
|
|
|
+ if (prev->next == driver) {
|
|
|
+ prev->next = driver->next;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
+EXPORT_SYMBOL(comedi_driver_unregister);
|
|
|
|
|
|
int comedi_auto_config(struct device *hardware_device,
|
|
|
struct comedi_driver *driver, unsigned long context)
|