summaryrefslogtreecommitdiff
path: root/drivers/usb/core/usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/usb.c')
-rw-r--r--drivers/usb/core/usb.c510
1 files changed, 270 insertions, 240 deletions
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 4ebfbd737905..e740f7852bcd 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -19,17 +19,16 @@
* just a collection of helper routines that implement the
* generic USB things that the real drivers can use..
*
- * Think of this as a "USB library" rather than anything else.
- * It should be considered a slave, with no callbacks. Callbacks
- * are evil.
+ * Think of this as a "USB library" rather than anything else,
+ * with no callbacks. Callbacks are evil.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/of.h>
#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/slab.h>
-#include <linux/interrupt.h> /* for in_interrupt() */
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/spinlock.h>
@@ -46,8 +45,8 @@
#include <linux/mm.h>
#include <linux/dma-mapping.h>
-#include "usb.h"
-
+#include "hub.h"
+#include "trace.h"
const char *usbcore_name = "usbcore";
@@ -65,8 +64,8 @@ int usb_disabled(void)
EXPORT_SYMBOL_GPL(usb_disabled);
#ifdef CONFIG_PM
-static int usb_autosuspend_delay = 2; /* Default delay value,
- * in seconds */
+/* Default delay value, in seconds */
+static int usb_autosuspend_delay = CONFIG_USB_AUTOSUSPEND_DELAY;
module_param_named(autosuspend, usb_autosuspend_delay, int, 0644);
MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
@@ -210,6 +209,82 @@ int usb_find_common_endpoints_reverse(struct usb_host_interface *alt,
EXPORT_SYMBOL_GPL(usb_find_common_endpoints_reverse);
/**
+ * usb_find_endpoint() - Given an endpoint address, search for the endpoint's
+ * usb_host_endpoint structure in an interface's current altsetting.
+ * @intf: the interface whose current altsetting should be searched
+ * @ep_addr: the endpoint address (number and direction) to find
+ *
+ * Search the altsetting's list of endpoints for one with the specified address.
+ *
+ * Return: Pointer to the usb_host_endpoint if found, %NULL otherwise.
+ */
+static const struct usb_host_endpoint *usb_find_endpoint(
+ const struct usb_interface *intf, unsigned int ep_addr)
+{
+ int n;
+ const struct usb_host_endpoint *ep;
+
+ n = intf->cur_altsetting->desc.bNumEndpoints;
+ ep = intf->cur_altsetting->endpoint;
+ for (; n > 0; (--n, ++ep)) {
+ if (ep->desc.bEndpointAddress == ep_addr)
+ return ep;
+ }
+ return NULL;
+}
+
+/**
+ * usb_check_bulk_endpoints - Check whether an interface's current altsetting
+ * contains a set of bulk endpoints with the given addresses.
+ * @intf: the interface whose current altsetting should be searched
+ * @ep_addrs: 0-terminated array of the endpoint addresses (number and
+ * direction) to look for
+ *
+ * Search for endpoints with the specified addresses and check their types.
+ *
+ * Return: %true if all the endpoints are found and are bulk, %false otherwise.
+ */
+bool usb_check_bulk_endpoints(
+ const struct usb_interface *intf, const u8 *ep_addrs)
+{
+ const struct usb_host_endpoint *ep;
+
+ for (; *ep_addrs; ++ep_addrs) {
+ ep = usb_find_endpoint(intf, *ep_addrs);
+ if (!ep || !usb_endpoint_xfer_bulk(&ep->desc))
+ return false;
+ }
+ return true;
+}
+EXPORT_SYMBOL_GPL(usb_check_bulk_endpoints);
+
+/**
+ * usb_check_int_endpoints - Check whether an interface's current altsetting
+ * contains a set of interrupt endpoints with the given addresses.
+ * @intf: the interface whose current altsetting should be searched
+ * @ep_addrs: 0-terminated array of the endpoint addresses (number and
+ * direction) to look for
+ *
+ * Search for endpoints with the specified addresses and check their types.
+ *
+ * Return: %true if all the endpoints are found and are interrupt,
+ * %false otherwise.
+ */
+bool usb_check_int_endpoints(
+ const struct usb_interface *intf, const u8 *ep_addrs)
+{
+ const struct usb_host_endpoint *ep;
+
+ for (; *ep_addrs; ++ep_addrs) {
+ ep = usb_find_endpoint(intf, *ep_addrs);
+ if (!ep || !usb_endpoint_xfer_int(&ep->desc))
+ return false;
+ }
+ return true;
+}
+EXPORT_SYMBOL_GPL(usb_check_int_endpoints);
+
+/**
* usb_find_alt_setting() - Given a configuration, find the alternate setting
* for the given interface.
* @config: the configuration to search (not necessarily the current config).
@@ -326,9 +401,9 @@ struct find_interface_arg {
struct device_driver *drv;
};
-static int __find_interface(struct device *dev, void *data)
+static int __find_interface(struct device *dev, const void *data)
{
- struct find_interface_arg *arg = data;
+ const struct find_interface_arg *arg = data;
struct usb_interface *intf;
if (!is_usb_interface(dev))
@@ -357,7 +432,7 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
struct device *dev;
argb.minor = minor;
- argb.drv = &drv->drvwrap.driver;
+ argb.drv = &drv->driver;
dev = bus_find_device(&usb_bus_type, NULL, &argb, __find_interface);
@@ -426,9 +501,9 @@ static void usb_release_dev(struct device *dev)
kfree(udev);
}
-static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int usb_dev_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct usb_device *usb_dev;
+ const struct usb_device *usb_dev;
usb_dev = to_usb_device(dev);
@@ -508,17 +583,17 @@ static const struct dev_pm_ops usb_device_pm_ops = {
#endif /* CONFIG_PM */
-static char *usb_devnode(struct device *dev,
+static char *usb_devnode(const struct device *dev,
umode_t *mode, kuid_t *uid, kgid_t *gid)
{
- struct usb_device *usb_dev;
+ const struct usb_device *usb_dev;
usb_dev = to_usb_device(dev);
return kasprintf(GFP_KERNEL, "bus/usb/%03d/%03d",
usb_dev->bus->busnum, usb_dev->devnum);
}
-struct device_type usb_device_type = {
+const struct device_type usb_device_type = {
.name = "usb_device",
.release = usb_release_dev,
.uevent = usb_dev_uevent,
@@ -528,21 +603,35 @@ struct device_type usb_device_type = {
#endif
};
-
-/* Returns 1 if @usb_bus is WUSB, 0 otherwise */
-static unsigned usb_bus_is_wusb(struct usb_bus *bus)
+static bool usb_dev_authorized(struct usb_device *dev, struct usb_hcd *hcd)
{
- struct usb_hcd *hcd = bus_to_hcd(bus);
- return hcd->wireless;
-}
+ struct usb_hub *hub;
+
+ if (!dev->parent)
+ return true; /* Root hub always ok [and always wired] */
+
+ switch (hcd->dev_policy) {
+ case USB_DEVICE_AUTHORIZE_NONE:
+ default:
+ return false;
+
+ case USB_DEVICE_AUTHORIZE_ALL:
+ return true;
+ case USB_DEVICE_AUTHORIZE_INTERNAL:
+ hub = usb_hub_to_struct_hub(dev->parent);
+ return hub->ports[dev->portnum - 1]->connect_type ==
+ USB_PORT_CONNECT_TYPE_HARD_WIRED;
+ }
+}
/**
* usb_alloc_dev - usb device constructor (usbcore-internal)
* @parent: hub to which device is connected; null to allocate a root hub
* @bus: bus used to access the device
* @port1: one-based index of port; ignored for root hubs
- * Context: !in_interrupt()
+ *
+ * Context: task context, might sleep.
*
* Only hub drivers (including virtual root hub drivers for host
* controllers) should ever call this.
@@ -557,7 +646,6 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
{
struct usb_device *dev;
struct usb_hcd *usb_hcd = bus_to_hcd(bus);
- unsigned root_hub = 0;
unsigned raw_port = port1;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@@ -580,21 +668,10 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
dev->dev.groups = usb_device_groups;
- /*
- * Fake a dma_mask/offset for the USB device:
- * We cannot really use the dma-mapping API (dma_alloc_* and
- * dma_map_*) for USB devices but instead need to use
- * usb_alloc_coherent and pass data in 'urb's, but some subsystems
- * manually look into the mask/offset pair to determine whether
- * they need bounce buffers.
- * Note: calling dma_set_mask() on a USB device would set the
- * mask for the entire HCD, so don't do that.
- */
- dev->dev.dma_mask = bus->sysdev->dma_mask;
- dev->dev.dma_pfn_offset = bus->sysdev->dma_pfn_offset;
set_dev_node(&dev->dev, dev_to_node(bus->sysdev));
dev->state = USB_STATE_ATTACHED;
dev->lpm_disable_count = 1;
+ dev->offload_usage = 0;
atomic_set(&dev->urbnum, 0);
INIT_LIST_HEAD(&dev->ep0.urb_list);
@@ -619,17 +696,17 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->dev.parent = bus->controller;
device_set_of_node_from_dev(&dev->dev, bus->sysdev);
dev_set_name(&dev->dev, "usb%d", bus->busnum);
- root_hub = 1;
} else {
+ int n;
+
/* match any labeling on the hubs; it's one-based */
if (parent->devpath[0] == '0') {
- snprintf(dev->devpath, sizeof dev->devpath,
- "%d", port1);
+ n = snprintf(dev->devpath, sizeof(dev->devpath), "%d", port1);
/* Root ports are not counted in route string */
dev->route = 0;
} else {
- snprintf(dev->devpath, sizeof dev->devpath,
- "%s.%d", parent->devpath, port1);
+ n = snprintf(dev->devpath, sizeof(dev->devpath), "%s.%d",
+ parent->devpath, port1);
/* Route string assumes hubs have less than 16 ports */
if (port1 < 15)
dev->route = parent->route +
@@ -638,6 +715,11 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->route = parent->route +
(15 << ((parent->level - 1)*4));
}
+ if (n >= sizeof(dev->devpath)) {
+ usb_put_hcd(bus_to_hcd(bus));
+ usb_put_dev(dev);
+ return NULL;
+ }
dev->dev.parent = &parent->dev;
dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
@@ -663,12 +745,9 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->connect_time = jiffies;
dev->active_duration = -jiffies;
#endif
- if (root_hub) /* Root hub always ok [and always wired] */
- dev->authorized = 1;
- else {
- dev->authorized = !!HCD_DEV_AUTHORIZED(usb_hcd);
- dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0;
- }
+
+ dev->authorized = usb_dev_authorized(dev, usb_hcd);
+ trace_usb_alloc_dev(dev);
return dev;
}
EXPORT_SYMBOL_GPL(usb_alloc_dev);
@@ -682,6 +761,10 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev);
* Drivers for USB interfaces should normally record such references in
* their probe() methods, when they bind to an interface, and release
* them by calling usb_put_dev(), in their disconnect() methods.
+ * However, if a driver does not access the usb_device structure after
+ * its disconnect() method returns then refcounting is not necessary,
+ * because the USB core guarantees that a usb_device will not be
+ * deallocated until after all of its interface drivers have been unbound.
*
* Return: A pointer to the device with the incremented reference counter.
*/
@@ -716,6 +799,10 @@ EXPORT_SYMBOL_GPL(usb_put_dev);
* Drivers for USB interfaces should normally record such references in
* their probe() methods, when they bind to an interface, and release
* them by calling usb_put_intf(), in their disconnect() methods.
+ * However, if a driver does not access the usb_interface structure after
+ * its disconnect() method returns then refcounting is not necessary,
+ * because the USB core guarantees that a usb_interface will not be
+ * deallocated until after its driver has been unbound.
*
* Return: A pointer to the interface with the incremented reference counter.
*/
@@ -742,6 +829,38 @@ void usb_put_intf(struct usb_interface *intf)
}
EXPORT_SYMBOL_GPL(usb_put_intf);
+/**
+ * usb_intf_get_dma_device - acquire a reference on the usb interface's DMA endpoint
+ * @intf: the usb interface
+ *
+ * While a USB device cannot perform DMA operations by itself, many USB
+ * controllers can. A call to usb_intf_get_dma_device() returns the DMA endpoint
+ * for the given USB interface, if any. The returned device structure must be
+ * released with put_device().
+ *
+ * See also usb_get_dma_device().
+ *
+ * Returns: A reference to the usb interface's DMA endpoint; or NULL if none
+ * exists.
+ */
+struct device *usb_intf_get_dma_device(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct device *dmadev;
+
+ if (!udev->bus)
+ return NULL;
+
+ dmadev = get_device(udev->bus->sysdev);
+ if (!dmadev || !dmadev->dma_mask) {
+ put_device(dmadev);
+ return NULL;
+ }
+
+ return dmadev;
+}
+EXPORT_SYMBOL_GPL(usb_intf_get_dma_device);
+
/* USB device locking
*
* USB devices and interfaces are locked using the semaphore in their
@@ -755,7 +874,7 @@ EXPORT_SYMBOL_GPL(usb_put_intf);
* is simple:
*
* When locking both a device and its parent, always lock the
- * the parent first.
+ * parent first.
*/
/**
@@ -915,226 +1034,134 @@ void usb_free_coherent(struct usb_device *dev, size_t size, void *addr,
EXPORT_SYMBOL_GPL(usb_free_coherent);
/**
- * usb_buffer_map - create DMA mapping(s) for an urb
- * @urb: urb whose transfer_buffer/setup_packet will be mapped
- *
- * URB_NO_TRANSFER_DMA_MAP is added to urb->transfer_flags if the operation
- * succeeds. If the device is connected to this system through a non-DMA
- * controller, this operation always succeeds.
- *
- * This call would normally be used for an urb which is reused, perhaps
- * as the target of a large periodic transfer, with usb_buffer_dmasync()
- * calls to synchronize memory and dma state.
+ * usb_alloc_noncoherent - allocate dma-noncoherent buffer for URB_NO_xxx_DMA_MAP
+ * @dev: device the buffer will be used with
+ * @size: requested buffer size
+ * @mem_flags: affect whether allocation may block
+ * @dma: used to return DMA address of buffer
+ * @dir: DMA transfer direction
+ * @table: used to return sg_table of allocated memory
*
- * Reverse the effect of this call with usb_buffer_unmap().
+ * To explicit manage the memory ownership for the kernel vs the device by
+ * USB core, the user needs save sg_table to urb->sgt. Then USB core will
+ * do DMA sync for CPU and device properly.
*
- * Return: Either %NULL (indicating no buffer could be mapped), or @urb.
+ * When the buffer is no longer used, free it with usb_free_noncoherent().
*
+ * Return: Either null (indicating no buffer could be allocated), or the
+ * cpu-space pointer to a buffer that may be used to perform DMA to the
+ * specified device. Such cpu-space buffers are returned along with the DMA
+ * address (through the pointer provided).
*/
-#if 0
-struct urb *usb_buffer_map(struct urb *urb)
+void *usb_alloc_noncoherent(struct usb_device *dev, size_t size,
+ gfp_t mem_flags, dma_addr_t *dma,
+ enum dma_data_direction dir,
+ struct sg_table **table)
{
- struct usb_bus *bus;
- struct device *controller;
+ struct device *dmadev;
+ struct sg_table *sgt;
+ void *buffer;
- if (!urb
- || !urb->dev
- || !(bus = urb->dev->bus)
- || !(controller = bus->sysdev))
+ if (!dev || !dev->bus)
return NULL;
- if (controller->dma_mask) {
- urb->transfer_dma = dma_map_single(controller,
- urb->transfer_buffer, urb->transfer_buffer_length,
- usb_pipein(urb->pipe)
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
- /* FIXME generic api broken like pci, can't report errors */
- /* if (urb->transfer_dma == DMA_ADDR_INVALID) return 0; */
- } else
- urb->transfer_dma = ~0;
- urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- return urb;
-}
-EXPORT_SYMBOL_GPL(usb_buffer_map);
-#endif /* 0 */
+ dmadev = bus_to_hcd(dev->bus)->self.sysdev;
-/* XXX DISABLED, no users currently. If you wish to re-enable this
- * XXX please determine whether the sync is to transfer ownership of
- * XXX the buffer from device to cpu or vice verse, and thusly use the
- * XXX appropriate _for_{cpu,device}() method. -DaveM
- */
-#if 0
-
-/**
- * usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s)
- * @urb: urb whose transfer_buffer/setup_packet will be synchronized
- */
-void usb_buffer_dmasync(struct urb *urb)
-{
- struct usb_bus *bus;
- struct device *controller;
-
- if (!urb
- || !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
- || !urb->dev
- || !(bus = urb->dev->bus)
- || !(controller = bus->sysdev))
- return;
+ sgt = dma_alloc_noncontiguous(dmadev, size, dir, mem_flags, 0);
+ if (!sgt)
+ return NULL;
- if (controller->dma_mask) {
- dma_sync_single_for_cpu(controller,
- urb->transfer_dma, urb->transfer_buffer_length,
- usb_pipein(urb->pipe)
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
- if (usb_pipecontrol(urb->pipe))
- dma_sync_single_for_cpu(controller,
- urb->setup_dma,
- sizeof(struct usb_ctrlrequest),
- DMA_TO_DEVICE);
+ buffer = dma_vmap_noncontiguous(dmadev, size, sgt);
+ if (!buffer) {
+ dma_free_noncontiguous(dmadev, size, sgt, dir);
+ return NULL;
}
-}
-EXPORT_SYMBOL_GPL(usb_buffer_dmasync);
-#endif
-/**
- * usb_buffer_unmap - free DMA mapping(s) for an urb
- * @urb: urb whose transfer_buffer will be unmapped
- *
- * Reverses the effect of usb_buffer_map().
- */
-#if 0
-void usb_buffer_unmap(struct urb *urb)
-{
- struct usb_bus *bus;
- struct device *controller;
-
- if (!urb
- || !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
- || !urb->dev
- || !(bus = urb->dev->bus)
- || !(controller = bus->sysdev))
- return;
+ *table = sgt;
+ *dma = sg_dma_address(sgt->sgl);
- if (controller->dma_mask) {
- dma_unmap_single(controller,
- urb->transfer_dma, urb->transfer_buffer_length,
- usb_pipein(urb->pipe)
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
- }
- urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP;
+ return buffer;
}
-EXPORT_SYMBOL_GPL(usb_buffer_unmap);
-#endif /* 0 */
+EXPORT_SYMBOL_GPL(usb_alloc_noncoherent);
-#if 0
/**
- * usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint
- * @dev: device to which the scatterlist will be mapped
- * @is_in: mapping transfer direction
- * @sg: the scatterlist to map
- * @nents: the number of entries in the scatterlist
- *
- * Return: Either < 0 (indicating no buffers could be mapped), or the
- * number of DMA mapping array entries in the scatterlist.
- *
- * Note:
- * The caller is responsible for placing the resulting DMA addresses from
- * the scatterlist into URB transfer buffer pointers, and for setting the
- * URB_NO_TRANSFER_DMA_MAP transfer flag in each of those URBs.
- *
- * Top I/O rates come from queuing URBs, instead of waiting for each one
- * to complete before starting the next I/O. This is particularly easy
- * to do with scatterlists. Just allocate and submit one URB for each DMA
- * mapping entry returned, stopping on the first error or when all succeed.
- * Better yet, use the usb_sg_*() calls, which do that (and more) for you.
- *
- * This call would normally be used when translating scatterlist requests,
- * rather than usb_buffer_map(), since on some hardware (with IOMMUs) it
- * may be able to coalesce mappings for improved I/O efficiency.
+ * usb_free_noncoherent - free memory allocated with usb_alloc_noncoherent()
+ * @dev: device the buffer was used with
+ * @size: requested buffer size
+ * @addr: CPU address of buffer
+ * @dir: DMA transfer direction
+ * @table: describe the allocated and DMA mapped memory,
*
- * Reverse the effect of this call with usb_buffer_unmap_sg().
+ * This reclaims an I/O buffer, letting it be reused. The memory must have
+ * been allocated using usb_alloc_noncoherent(), and the parameters must match
+ * those provided in that allocation request.
*/
-int usb_buffer_map_sg(const struct usb_device *dev, int is_in,
- struct scatterlist *sg, int nents)
+void usb_free_noncoherent(struct usb_device *dev, size_t size,
+ void *addr, enum dma_data_direction dir,
+ struct sg_table *table)
{
- struct usb_bus *bus;
- struct device *controller;
-
- if (!dev
- || !(bus = dev->bus)
- || !(controller = bus->sysdev)
- || !controller->dma_mask)
- return -EINVAL;
-
- /* FIXME generic api broken like pci, can't report errors */
- return dma_map_sg(controller, sg, nents,
- is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE) ? : -ENOMEM;
-}
-EXPORT_SYMBOL_GPL(usb_buffer_map_sg);
-#endif
+ struct device *dmadev;
-/* XXX DISABLED, no users currently. If you wish to re-enable this
- * XXX please determine whether the sync is to transfer ownership of
- * XXX the buffer from device to cpu or vice verse, and thusly use the
- * XXX appropriate _for_{cpu,device}() method. -DaveM
- */
-#if 0
+ if (!dev || !dev->bus)
+ return;
+ if (!addr)
+ return;
+
+ dmadev = bus_to_hcd(dev->bus)->self.sysdev;
+ dma_vunmap_noncontiguous(dmadev, addr);
+ dma_free_noncontiguous(dmadev, size, table, dir);
+}
+EXPORT_SYMBOL_GPL(usb_free_noncoherent);
/**
- * usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s)
- * @dev: device to which the scatterlist will be mapped
- * @is_in: mapping transfer direction
- * @sg: the scatterlist to synchronize
- * @n_hw_ents: the positive return value from usb_buffer_map_sg
+ * usb_endpoint_max_periodic_payload - Get maximum payload bytes per service
+ * interval
+ * @udev: The USB device
+ * @ep: The endpoint
*
- * Use this when you are re-using a scatterlist's data buffers for
- * another USB request.
+ * Returns: the maximum number of bytes isochronous or interrupt endpoint @ep
+ * can transfer during a service interval, or 0 for other endpoints.
*/
-void usb_buffer_dmasync_sg(const struct usb_device *dev, int is_in,
- struct scatterlist *sg, int n_hw_ents)
+u32 usb_endpoint_max_periodic_payload(struct usb_device *udev,
+ const struct usb_host_endpoint *ep)
{
- struct usb_bus *bus;
- struct device *controller;
-
- if (!dev
- || !(bus = dev->bus)
- || !(controller = bus->sysdev)
- || !controller->dma_mask)
- return;
+ if (!usb_endpoint_xfer_isoc(&ep->desc) &&
+ !usb_endpoint_xfer_int(&ep->desc))
+ return 0;
- dma_sync_sg_for_cpu(controller, sg, n_hw_ents,
- is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ switch (udev->speed) {
+ case USB_SPEED_SUPER_PLUS:
+ if (USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes))
+ return le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval);
+ fallthrough;
+ case USB_SPEED_SUPER:
+ return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
+ default:
+ if (usb_endpoint_is_hs_isoc_double(udev, ep))
+ return le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval);
+ return usb_endpoint_maxp(&ep->desc) * usb_endpoint_maxp_mult(&ep->desc);
+ }
}
-EXPORT_SYMBOL_GPL(usb_buffer_dmasync_sg);
-#endif
+EXPORT_SYMBOL_GPL(usb_endpoint_max_periodic_payload);
-#if 0
/**
- * usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist
- * @dev: device to which the scatterlist will be mapped
- * @is_in: mapping transfer direction
- * @sg: the scatterlist to unmap
- * @n_hw_ents: the positive return value from usb_buffer_map_sg
+ * usb_endpoint_is_hs_isoc_double - Tell whether an endpoint uses USB 2
+ * Isochronous Double IN Bandwidth
+ * @udev: The USB device
+ * @ep: The endpoint
*
- * Reverses the effect of usb_buffer_map_sg().
+ * Returns: true if an endpoint @ep conforms to USB 2 Isochronous Double IN
+ * Bandwidth ECN, false otherwise.
*/
-void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in,
- struct scatterlist *sg, int n_hw_ents)
+bool usb_endpoint_is_hs_isoc_double(struct usb_device *udev,
+ const struct usb_host_endpoint *ep)
{
- struct usb_bus *bus;
- struct device *controller;
-
- if (!dev
- || !(bus = dev->bus)
- || !(controller = bus->sysdev)
- || !controller->dma_mask)
- return;
-
- dma_unmap_sg(controller, sg, n_hw_ents,
- is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ return ep->eusb2_isoc_ep_comp.bDescriptorType &&
+ le16_to_cpu(udev->descriptor.bcdUSB) == 0x220 &&
+ usb_endpoint_is_isoc_in(&ep->desc) &&
+ !le16_to_cpu(ep->desc.wMaxPacketSize);
}
-EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg);
-#endif
+EXPORT_SYMBOL_GPL(usb_endpoint_is_hs_isoc_double);
/*
* Notifications of device and interface registration
@@ -1166,19 +1193,15 @@ static struct notifier_block usb_bus_nb = {
.notifier_call = usb_bus_notify,
};
-struct dentry *usb_debug_root;
-EXPORT_SYMBOL_GPL(usb_debug_root);
-
static void usb_debugfs_init(void)
{
- usb_debug_root = debugfs_create_dir("usb", NULL);
debugfs_create_file("devices", 0444, usb_debug_root, NULL,
&usbfs_devices_fops);
}
static void usb_debugfs_cleanup(void)
{
- debugfs_remove_recursive(usb_debug_root);
+ debugfs_lookup_and_remove("devices", usb_debug_root);
}
/*
@@ -1205,6 +1228,9 @@ static int __init usb_init(void)
retval = usb_major_init();
if (retval)
goto major_init_failed;
+ retval = class_register(&usbmisc_class);
+ if (retval)
+ goto class_register_failed;
retval = usb_register(&usbfs_driver);
if (retval)
goto driver_register_failed;
@@ -1224,6 +1250,8 @@ hub_init_failed:
usb_devio_init_failed:
usb_deregister(&usbfs_driver);
driver_register_failed:
+ class_unregister(&usbmisc_class);
+class_register_failed:
usb_major_cleanup();
major_init_failed:
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
@@ -1251,6 +1279,7 @@ static void __exit usb_exit(void)
usb_deregister(&usbfs_driver);
usb_devio_cleanup();
usb_hub_cleanup();
+ class_unregister(&usbmisc_class);
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
bus_unregister(&usb_bus_type);
usb_acpi_unregister();
@@ -1260,4 +1289,5 @@ static void __exit usb_exit(void)
subsys_initcall(usb_init);
module_exit(usb_exit);
+MODULE_DESCRIPTION("USB core host-side support");
MODULE_LICENSE("GPL");