2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-29 15:43:59 +08:00

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 <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Ian Abbott 2012-03-30 17:15:01 +01:00 committed by Greg Kroah-Hartman
parent 3902a37028
commit f401167002
2 changed files with 95 additions and 3 deletions

View 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);

View 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);