staging: comedi: add bus-type-specific attach hooks for PCI and USB
authorIan Abbott <abbotti@mev.co.uk>
Fri, 30 Mar 2012 16:15:01 +0000 (17:15 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 10 Apr 2012 18:22:01 +0000 (11:22 -0700)
The Comedi auto-configuration mechanism used to bind hardware devices to
comedi devices automatically is pretty kludgy.  It fakes a "manual"
configuration of the comedi device as though the COMEDI_DEVCONFIG ioctl
(or the 'comedi_config' utility) were used.  In particular, the
low-level comedi driver's '->attach()' routine is called with a pointer
to the struct comedi_device being attached and a pointer to a 'struct
devconfig' containing a device name string and a few integer options to
help the attach routine locate the device being attached.  In the case
of PCI devices, these integer options are the PCI bus and slot numbers.
In the case of USB devices, there are no integer options and it relies
more on pot luck to attach the correct device.

This patch adds a couple of bus-type-specific attach routine hooks to
the struct comedi_driver, which a low-level driver can optionally fill
in if it supports auto-configuration.

A low-level driver that supports auto-configuration of {PCI,USB} devices
calls the existing comedi_{pci,usb}_auto_config() when it wishes to
auto-configure a freshly probed device (maybe after loading firmware).
This will call the new '->attach_{pci,usb}()' hook if the driver has
defined it, otherwise it will fall back to calling the '->attach()' hook
as before.  The '->attach_{pci,usb}()' hook gets a pointer to the struct
comedi_device and a pointer to the struct {pci_dev,usb_interface} and
can figure out the {PCI,USB} device details for itself.

Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/comedidev.h
drivers/staging/comedi/drivers.c

index e4626b6231d6c66ffe6e22bdd5056a51b54b3d8b..300fd8400a4b2813c357d380543b75bb6c779a9f 100644 (file)
@@ -180,6 +180,9 @@ struct comedi_async {
                        unsigned int x);
 };
 
+struct pci_dev;
+struct usb_interface;
+
 struct comedi_driver {
        struct comedi_driver *next;
 
@@ -187,6 +190,8 @@ struct comedi_driver {
        struct module *module;
        int (*attach) (struct comedi_device *, struct comedi_devconfig *);
        int (*detach) (struct comedi_device *);
+       int (*attach_pci) (struct comedi_device *, struct pci_dev *);
+       int (*attach_usb) (struct comedi_device *, struct usb_interface *);
 
        /* number of elements in board_name and board_id arrays */
        unsigned int num_names;
@@ -460,7 +465,6 @@ void comedi_free_subdevice_minor(struct comedi_subdevice *s);
 int comedi_pci_auto_config(struct pci_dev *pcidev,
                           struct comedi_driver *driver);
 void comedi_pci_auto_unconfig(struct pci_dev *pcidev);
-struct usb_interface;          /* forward declaration */
 int comedi_usb_auto_config(struct usb_interface *intf,
                           struct comedi_driver *driver);
 void comedi_usb_auto_unconfig(struct usb_interface *intf);
index cfb6fe94f78267a1324f88cb602fccbef0c2b301..69e6fa345d3fd8712d6a5bf619efc62a2235b9bc 100644 (file)
@@ -811,6 +811,51 @@ void comedi_reset_async_buf(struct comedi_async *async)
        async->events = 0;
 }
 
+static int
+comedi_auto_config_helper(struct device *hardware_device,
+                         struct comedi_driver *driver,
+                         int (*attach_wrapper) (struct comedi_device *,
+                                                void *), void *context)
+{
+       int minor;
+       struct comedi_device_file_info *dev_file_info;
+       struct comedi_device *comedi_dev;
+       int ret;
+
+       if (!comedi_autoconfig)
+               return 0;
+
+       minor = comedi_alloc_board_minor(hardware_device);
+       if (minor < 0)
+               return minor;
+
+       dev_file_info = comedi_get_device_file_info(minor);
+       comedi_dev = dev_file_info->device;
+
+       mutex_lock(&comedi_dev->mutex);
+       if (comedi_dev->attached)
+               ret = -EBUSY;
+       else if (!try_module_get(driver->module)) {
+               printk(KERN_INFO "comedi: failed to increment module count\n");
+               ret = -EIO;
+       } else {
+               /* set comedi_dev->driver here for attach wrapper */
+               comedi_dev->driver = driver;
+               ret = (*attach_wrapper)(comedi_dev, context);
+               if (ret < 0) {
+                       module_put(driver->module);
+                       __comedi_device_detach(comedi_dev);
+               } else {
+                       ret = comedi_device_postconfig(comedi_dev);
+               }
+       }
+       mutex_unlock(&comedi_dev->mutex);
+
+       if (ret < 0)
+               comedi_free_board_minor(minor);
+       return ret;
+}
+
 static int comedi_auto_config(struct device *hardware_device,
                              const char *board_name, const int *options,
                              unsigned num_options)
@@ -857,7 +902,8 @@ static void comedi_auto_unconfig(struct device *hardware_device)
        comedi_free_board_minor(minor);
 }
 
-int comedi_pci_auto_config(struct pci_dev *pcidev, struct comedi_driver *driver)
+static int comedi_old_pci_auto_config(struct pci_dev *pcidev,
+                                     struct comedi_driver *driver)
 {
        int options[2];
 
@@ -869,6 +915,27 @@ int comedi_pci_auto_config(struct pci_dev *pcidev, struct comedi_driver *driver)
        return comedi_auto_config(&pcidev->dev, driver->driver_name,
                                  options, ARRAY_SIZE(options));
 }
+
+static int comedi_pci_attach_wrapper(struct comedi_device *dev, void *pcidev)
+{
+       return dev->driver->attach_pci(dev, pcidev);
+}
+
+static int comedi_new_pci_auto_config(struct pci_dev *pcidev,
+                                     struct comedi_driver *driver)
+{
+       return comedi_auto_config_helper(&pcidev->dev, driver,
+                                        comedi_pci_attach_wrapper, pcidev);
+}
+
+int comedi_pci_auto_config(struct pci_dev *pcidev, struct comedi_driver *driver)
+{
+
+       if (driver->attach_pci)
+               return comedi_new_pci_auto_config(pcidev, driver);
+       else
+               return comedi_old_pci_auto_config(pcidev, driver);
+}
 EXPORT_SYMBOL_GPL(comedi_pci_auto_config);
 
 void comedi_pci_auto_unconfig(struct pci_dev *pcidev)
@@ -877,11 +944,32 @@ void comedi_pci_auto_unconfig(struct pci_dev *pcidev)
 }
 EXPORT_SYMBOL_GPL(comedi_pci_auto_unconfig);
 
+static int comedi_old_usb_auto_config(struct usb_interface *intf,
+                                     struct comedi_driver *driver)
+{
+       return comedi_auto_config(&intf->dev, driver->driver_name, NULL, 0);
+}
+
+static int comedi_usb_attach_wrapper(struct comedi_device *dev, void *intf)
+{
+       return dev->driver->attach_usb(dev, intf);
+}
+
+static int comedi_new_usb_auto_config(struct usb_interface *intf,
+                                     struct comedi_driver *driver)
+{
+       return comedi_auto_config_helper(&intf->dev, driver,
+                                        comedi_usb_attach_wrapper, intf);
+}
+
 int comedi_usb_auto_config(struct usb_interface *intf,
                           struct comedi_driver *driver)
 {
        BUG_ON(intf == NULL);
-       return comedi_auto_config(&intf->dev, driver->driver_name, NULL, 0);
+       if (driver->attach_usb)
+               return comedi_new_usb_auto_config(intf, driver);
+       else
+               return comedi_old_usb_auto_config(intf, driver);
 }
 EXPORT_SYMBOL_GPL(comedi_usb_auto_config);