From f4011670023f28cf9081904f8986c0c1be5c9f1e Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 30 Mar 2012 17:15:01 +0100 Subject: [PATCH] staging: comedi: add bus-type-specific attach hooks for PCI and USB 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 Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/comedidev.h | 6 +- drivers/staging/comedi/drivers.c | 92 +++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/drivers/staging/comedi/comedidev.h b/drivers/staging/comedi/comedidev.h index e4626b6231d6..300fd8400a4b 100644 --- a/drivers/staging/comedi/comedidev.h +++ b/drivers/staging/comedi/comedidev.h @@ -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); diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c index cfb6fe94f782..69e6fa345d3f 100644 --- a/drivers/staging/comedi/drivers.c +++ b/drivers/staging/comedi/drivers.c @@ -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); -- 2.39.5