summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/hcd.c4
-rw-r--r--drivers/usb/core/urb.c18
-rw-r--r--include/linux/usb.h30
3 files changed, 48 insertions, 4 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index cc5b1d3c368..bcbaedc897d 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -928,7 +928,7 @@ static void urb_unlink(struct usb_hcd *hcd, struct urb *urb)
dma_unmap_single (hcd->self.controller,
urb->transfer_dma,
urb->transfer_buffer_length,
- usb_pipein (urb->pipe)
+ usb_urb_dir_in(urb)
? DMA_FROM_DEVICE
: DMA_TO_DEVICE);
}
@@ -1014,7 +1014,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
hcd->self.controller,
urb->transfer_buffer,
urb->transfer_buffer_length,
- usb_pipein (urb->pipe)
+ usb_urb_dir_in(urb)
? DMA_FROM_DEVICE
: DMA_TO_DEVICE);
}
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index ff53acb4fab..1a64a6a850f 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -309,7 +309,21 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
* and don't need to duplicate tests
*/
xfertype = usb_endpoint_type(&ep->desc);
- is_out = usb_pipeout(urb->pipe);
+ if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
+ struct usb_ctrlrequest *setup =
+ (struct usb_ctrlrequest *) urb->setup_packet;
+
+ if (!setup)
+ return -ENOEXEC;
+ is_out = !(setup->bRequestType & USB_DIR_IN) ||
+ !setup->wLength;
+ } else {
+ is_out = usb_endpoint_dir_out(&ep->desc);
+ }
+
+ /* Cache the direction for later use */
+ urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK) |
+ (is_out ? URB_DIR_OUT : URB_DIR_IN);
if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
dev->state < USB_STATE_CONFIGURED)
@@ -363,7 +377,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
/* enforce simple/standard policy */
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP |
- URB_NO_INTERRUPT);
+ URB_NO_INTERRUPT | URB_DIR_MASK);
switch (xfertype) {
case USB_ENDPOINT_XFER_BULK:
if (is_out)
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 818a1b4f737..9d08f5a5ba7 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1021,6 +1021,8 @@ extern int usb_disabled(void);
/*
* urb->transfer_flags:
+ *
+ * Note: URB_DIR_IN/OUT is automatically set in usb_submit_urb().
*/
#define URB_SHORT_NOT_OK 0x0001 /* report short reads as errors */
#define URB_ISO_ASAP 0x0002 /* iso-only, urb->start_frame
@@ -1033,6 +1035,10 @@ extern int usb_disabled(void);
* needed */
#define URB_FREE_BUFFER 0x0100 /* Free transfer buffer with the URB */
+#define URB_DIR_IN 0x0200 /* Transfer from device to host */
+#define URB_DIR_OUT 0
+#define URB_DIR_MASK URB_DIR_IN
+
struct usb_iso_packet_descriptor {
unsigned int offset;
unsigned int length; /* expected length */
@@ -1380,6 +1386,30 @@ extern void usb_unanchor_urb(struct urb *urb);
extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
unsigned int timeout);
+/**
+ * usb_urb_dir_in - check if an URB describes an IN transfer
+ * @urb: URB to be checked
+ *
+ * Returns 1 if @urb describes an IN transfer (device-to-host),
+ * otherwise 0.
+ */
+static inline int usb_urb_dir_in(struct urb *urb)
+{
+ return (urb->transfer_flags & URB_DIR_MASK) != URB_DIR_OUT;
+}
+
+/**
+ * usb_urb_dir_out - check if an URB describes an OUT transfer
+ * @urb: URB to be checked
+ *
+ * Returns 1 if @urb describes an OUT transfer (host-to-device),
+ * otherwise 0.
+ */
+static inline int usb_urb_dir_out(struct urb *urb)
+{
+ return (urb->transfer_flags & URB_DIR_MASK) == URB_DIR_OUT;
+}
+
void *usb_buffer_alloc (struct usb_device *dev, size_t size,
gfp_t mem_flags, dma_addr_t *dma);
void usb_buffer_free (struct usb_device *dev, size_t size,