* message.c - synchronous message handling
*/
-#include <linux/config.h>
#include <linux/pci.h> /* for scatterlist macros */
#include <linux/usb.h>
#include <linux/module.h>
#include "hcd.h" /* for usbcore internals */
#include "usb.h"
-static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs)
+static void usb_api_blocking_completion(struct urb *urb)
{
complete((struct completion *)urb->context);
}
-static void timeout_kill(unsigned long data)
-{
- struct urb *urb = (struct urb *) data;
-
- usb_unlink_urb(urb);
-}
-
-// Starts urb and waits for completion or timeout
-// note that this call is NOT interruptible, while
-// many device driver i/o requests should be interruptible
-static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
+/*
+ * Starts urb and waits for completion or timeout. Note that this call
+ * is NOT interruptible. Many device driver i/o requests should be
+ * interruptible and therefore these drivers should implement their
+ * own interruptible routines.
+ */
+static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
{
- struct completion done;
- struct timer_list timer;
- int status;
+ struct completion done;
+ unsigned long expire;
+ int status;
init_completion(&done);
urb->context = &done;
urb->actual_length = 0;
status = usb_submit_urb(urb, GFP_NOIO);
-
- if (status == 0) {
- if (timeout > 0) {
- init_timer(&timer);
- timer.expires = jiffies + msecs_to_jiffies(timeout);
- timer.data = (unsigned long)urb;
- timer.function = timeout_kill;
- /* grr. timeout _should_ include submit delays. */
- add_timer(&timer);
- }
- wait_for_completion(&done);
+ if (unlikely(status))
+ goto out;
+
+ expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
+ if (!wait_for_completion_timeout(&done, expire)) {
+
+ dev_dbg(&urb->dev->dev,
+ "%s timed out on ep%d%s len=%d/%d\n",
+ current->comm,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out",
+ urb->actual_length,
+ urb->transfer_buffer_length);
+
+ usb_kill_urb(urb);
+ status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status;
+ } else
status = urb->status;
- /* note: HCDs return ETIMEDOUT for other reasons too */
- if (status == -ECONNRESET) {
- dev_dbg(&urb->dev->dev,
- "%s timed out on ep%d%s len=%d/%d\n",
- current->comm,
- usb_pipeendpoint(urb->pipe),
- usb_pipein(urb->pipe) ? "in" : "out",
- urb->actual_length,
- urb->transfer_buffer_length
- );
- if (urb->actual_length > 0)
- status = 0;
- else
- status = -ETIMEDOUT;
- }
- if (timeout > 0)
- del_timer_sync(&timer);
- }
-
+out:
if (actual_length)
*actual_length = urb->actual_length;
+
usb_free_urb(urb);
return status;
}
}
+/**
+ * usb_interrupt_msg - Builds an interrupt urb, sends it off and waits for completion
+ * @usb_dev: pointer to the usb device to send the message to
+ * @pipe: endpoint "pipe" to send the message to
+ * @data: pointer to the data to send
+ * @len: length in bytes of the data to send
+ * @actual_length: pointer to a location to put the actual length transferred in bytes
+ * @timeout: time in msecs to wait for the message to complete before
+ * timing out (if 0 the wait is forever)
+ * Context: !in_interrupt ()
+ *
+ * This function sends a simple interrupt message to a specified endpoint and
+ * waits for the message to complete, or timeout.
+ *
+ * If successful, it returns 0, otherwise a negative error number. The number
+ * of actual bytes transferred will be stored in the actual_length paramater.
+ *
+ * Don't use this function from within an interrupt context, like a bottom half
+ * handler. If you need an asynchronous message, or need to send a message
+ * from within interrupt context, use usb_submit_urb() If a thread in your
+ * driver uses this call, make sure your disconnect() method can wait for it to
+ * complete. Since you don't have a handle on the URB used, you can't cancel
+ * the request.
+ */
+int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
+ void *data, int len, int *actual_length, int timeout)
+{
+ return usb_bulk_msg(usb_dev, pipe, data, len, actual_length, timeout);
+}
+EXPORT_SYMBOL_GPL(usb_interrupt_msg);
+
/**
* usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion
* @usb_dev: pointer to the usb device to send the message to
io->dev = NULL;
}
-static void sg_complete (struct urb *urb, struct pt_regs *regs)
+static void sg_complete (struct urb *urb)
{
- struct usb_sg_request *io = (struct usb_sg_request *) urb->context;
+ struct usb_sg_request *io = urb->context;
spin_lock (&io->lock);
int retval;
io->urbs [i]->dev = io->dev;
- retval = usb_submit_urb (io->urbs [i], SLAB_ATOMIC);
+ retval = usb_submit_urb (io->urbs [i], GFP_ATOMIC);
/* after we submit, let completions or cancelations fire;
* we handshake using io->status.
* Returns the number of bytes received on success, or else the status code
* returned by the underlying usb_control_msg() call.
*/
-int usb_get_string(struct usb_device *dev, unsigned short langid,
- unsigned char index, void *buf, int size)
+static int usb_get_string(struct usb_device *dev, unsigned short langid,
+ unsigned char index, void *buf, int size)
{
int i;
int result;
err = -EINVAL;
goto errout;
} else {
- dev->have_langid = -1;
+ dev->have_langid = 1;
dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
/* always use the first langid listed */
dev_dbg (&dev->dev, "default language 0x%04x\n",
* Context: !in_interrupt ()
*
* Updates the copy of the device descriptor stored in the device structure,
- * which dedicates space for this purpose. Note that several fields are
- * converted to the host CPU's byte order: the USB version (bcdUSB), and
- * vendors product and version fields (idVendor, idProduct, and bcdDevice).
- * That lets device drivers compare against non-byteswapped constants.
+ * which dedicates space for this purpose.
*
* Not exported, only for use by the core. If drivers really want to read
* the device descriptor directly, they can call usb_get_descriptor() with
ep = dev->ep_in[epnum];
dev->ep_in[epnum] = NULL;
}
- if (ep && dev->bus && dev->bus->op && dev->bus->op->disable)
- dev->bus->op->disable(dev, ep);
+ if (ep && dev->bus)
+ usb_hcd_endpoint_disable(dev, ep);
}
/**
if (cp && configuration == 0)
dev_warn(&dev->dev, "config 0 descriptor??\n");
- if (dev->state == USB_STATE_SUSPENDED)
- return -EHOSTUNREACH;
-
/* Allocate memory for new interfaces before doing anything else,
* so that if we run out then nothing will have changed. */
n = nintf = 0;
return ret;
}
}
- }
- /* if it's already configured, clear out old state first.
- * getting rid of old interfaces means unbinding their drivers.
- */
- if (dev->state != USB_STATE_ADDRESS)
- usb_disable_device (dev, 1); // Skip ep0
-
- if (cp) {
i = dev->bus_mA - cp->desc.bMaxPower * 2;
if (i < 0)
dev_warn(&dev->dev, "new config #%d exceeds power "
configuration, -i);
}
+ /* Wake up the device so we can send it the Set-Config request */
+ ret = usb_autoresume_device(dev);
+ if (ret)
+ goto free_interfaces;
+
+ /* if it's already configured, clear out old state first.
+ * getting rid of old interfaces means unbinding their drivers.
+ */
+ if (dev->state != USB_STATE_ADDRESS)
+ usb_disable_device (dev, 1); // Skip ep0
+
if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
- NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0)
- goto free_interfaces;
+ NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0) {
+
+ /* All the old state is gone, so what else can we do?
+ * The device is probably useless now anyway.
+ */
+ cp = NULL;
+ }
dev->actconfig = cp;
- if (!cp)
+ if (!cp) {
usb_set_device_state(dev, USB_STATE_ADDRESS);
- else {
- usb_set_device_state(dev, USB_STATE_CONFIGURED);
+ usb_autosuspend_device(dev);
+ goto free_interfaces;
+ }
+ usb_set_device_state(dev, USB_STATE_CONFIGURED);
- /* Initialize the new interface structures and the
- * hc/hcd/usbcore interface/endpoint state.
- */
- for (i = 0; i < nintf; ++i) {
- struct usb_interface_cache *intfc;
- struct usb_interface *intf;
- struct usb_host_interface *alt;
-
- cp->interface[i] = intf = new_interfaces[i];
- intfc = cp->intf_cache[i];
- intf->altsetting = intfc->altsetting;
- intf->num_altsetting = intfc->num_altsetting;
- kref_get(&intfc->ref);
-
- alt = usb_altnum_to_altsetting(intf, 0);
-
- /* No altsetting 0? We'll assume the first altsetting.
- * We could use a GetInterface call, but if a device is
- * so non-compliant that it doesn't have altsetting 0
- * then I wouldn't trust its reply anyway.
- */
- if (!alt)
- alt = &intf->altsetting[0];
-
- intf->cur_altsetting = alt;
- usb_enable_interface(dev, intf);
- intf->dev.parent = &dev->dev;
- intf->dev.driver = NULL;
- intf->dev.bus = &usb_bus_type;
- intf->dev.dma_mask = dev->dev.dma_mask;
- intf->dev.release = release_interface;
- device_initialize (&intf->dev);
- mark_quiesced(intf);
- sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
- dev->bus->busnum, dev->devpath,
- configuration,
- alt->desc.bInterfaceNumber);
- }
- kfree(new_interfaces);
+ /* Initialize the new interface structures and the
+ * hc/hcd/usbcore interface/endpoint state.
+ */
+ for (i = 0; i < nintf; ++i) {
+ struct usb_interface_cache *intfc;
+ struct usb_interface *intf;
+ struct usb_host_interface *alt;
- if (cp->string == NULL)
- cp->string = usb_cache_string(dev,
- cp->desc.iConfiguration);
+ cp->interface[i] = intf = new_interfaces[i];
+ intfc = cp->intf_cache[i];
+ intf->altsetting = intfc->altsetting;
+ intf->num_altsetting = intfc->num_altsetting;
+ kref_get(&intfc->ref);
- /* Now that all the interfaces are set up, register them
- * to trigger binding of drivers to interfaces. probe()
- * routines may install different altsettings and may
- * claim() any interfaces not yet bound. Many class drivers
- * need that: CDC, audio, video, etc.
+ alt = usb_altnum_to_altsetting(intf, 0);
+
+ /* No altsetting 0? We'll assume the first altsetting.
+ * We could use a GetInterface call, but if a device is
+ * so non-compliant that it doesn't have altsetting 0
+ * then I wouldn't trust its reply anyway.
*/
- for (i = 0; i < nintf; ++i) {
- struct usb_interface *intf = cp->interface[i];
-
- dev_dbg (&dev->dev,
- "adding %s (config #%d, interface %d)\n",
- intf->dev.bus_id, configuration,
- intf->cur_altsetting->desc.bInterfaceNumber);
- ret = device_add (&intf->dev);
- if (ret != 0) {
- dev_err(&dev->dev,
- "device_add(%s) --> %d\n",
- intf->dev.bus_id,
- ret);
- continue;
- }
- usb_create_sysfs_intf_files (intf);
+ if (!alt)
+ alt = &intf->altsetting[0];
+
+ intf->cur_altsetting = alt;
+ usb_enable_interface(dev, intf);
+ intf->dev.parent = &dev->dev;
+ intf->dev.driver = NULL;
+ intf->dev.bus = &usb_bus_type;
+ intf->dev.dma_mask = dev->dev.dma_mask;
+ intf->dev.release = release_interface;
+ device_initialize (&intf->dev);
+ mark_quiesced(intf);
+ sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
+ dev->bus->busnum, dev->devpath,
+ configuration, alt->desc.bInterfaceNumber);
+ }
+ kfree(new_interfaces);
+
+ if (cp->string == NULL)
+ cp->string = usb_cache_string(dev, cp->desc.iConfiguration);
+
+ /* Now that all the interfaces are set up, register them
+ * to trigger binding of drivers to interfaces. probe()
+ * routines may install different altsettings and may
+ * claim() any interfaces not yet bound. Many class drivers
+ * need that: CDC, audio, video, etc.
+ */
+ for (i = 0; i < nintf; ++i) {
+ struct usb_interface *intf = cp->interface[i];
+
+ dev_dbg (&dev->dev,
+ "adding %s (config #%d, interface %d)\n",
+ intf->dev.bus_id, configuration,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+ ret = device_add (&intf->dev);
+ if (ret != 0) {
+ dev_err(&dev->dev, "device_add(%s) --> %d\n",
+ intf->dev.bus_id, ret);
+ continue;
}
+ usb_create_sysfs_intf_files (intf);
}
+ usb_autosuspend_device(dev);
+ return 0;
+}
+
+struct set_config_request {
+ struct usb_device *udev;
+ int config;
+ struct work_struct work;
+};
+
+/* Worker routine for usb_driver_set_configuration() */
+static void driver_set_config_work(struct work_struct *work)
+{
+ struct set_config_request *req =
+ container_of(work, struct set_config_request, work);
+
+ usb_lock_device(req->udev);
+ usb_set_configuration(req->udev, req->config);
+ usb_unlock_device(req->udev);
+ usb_put_dev(req->udev);
+ kfree(req);
+}
+
+/**
+ * usb_driver_set_configuration - Provide a way for drivers to change device configurations
+ * @udev: the device whose configuration is being updated
+ * @config: the configuration being chosen.
+ * Context: In process context, must be able to sleep
+ *
+ * Device interface drivers are not allowed to change device configurations.
+ * This is because changing configurations will destroy the interface the
+ * driver is bound to and create new ones; it would be like a floppy-disk
+ * driver telling the computer to replace the floppy-disk drive with a
+ * tape drive!
+ *
+ * Still, in certain specialized circumstances the need may arise. This
+ * routine gets around the normal restrictions by using a work thread to
+ * submit the change-config request.
+ *
+ * Returns 0 if the request was succesfully queued, error code otherwise.
+ * The caller has no way to know whether the queued request will eventually
+ * succeed.
+ */
+int usb_driver_set_configuration(struct usb_device *udev, int config)
+{
+ struct set_config_request *req;
+
+ req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+ req->udev = udev;
+ req->config = config;
+ INIT_WORK(&req->work, driver_set_config_work);
+
+ usb_get_dev(udev);
+ if (!schedule_work(&req->work)) {
+ usb_put_dev(udev);
+ kfree(req);
+ return -EINVAL;
+ }
return 0;
}
+EXPORT_SYMBOL_GPL(usb_driver_set_configuration);
// synchronous request completion model
EXPORT_SYMBOL(usb_control_msg);
// synchronous control message convenience routines
EXPORT_SYMBOL(usb_get_descriptor);
EXPORT_SYMBOL(usb_get_status);
-EXPORT_SYMBOL(usb_get_string);
EXPORT_SYMBOL(usb_string);
// synchronous calls that also maintain usbcore state