Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 151418
b: refs/heads/master
c: e04748e
h: refs/heads/master
v: v3
  • Loading branch information
Sarah Sharp authored and Greg Kroah-Hartman committed Jun 16, 2009
1 parent c3fbee9 commit 09f6310
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 50 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: b10de142119a676552df3f0d2e3a9d647036c26a
refs/heads/master: e04748e3a87271fcf30d383e3780c5d3ee1c1618
3 changes: 2 additions & 1 deletion trunk/drivers/usb/core/hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1239,7 +1239,8 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,

/* Map the URB's buffers for DMA access.
* Lower level HCD code should use *_dma exclusively,
* unless it uses pio or talks to another transport.
* unless it uses pio or talks to another transport,
* or uses the provided scatter gather list for bulk.
*/
if (is_root_hub(urb->dev))
return 0;
Expand Down
139 changes: 91 additions & 48 deletions trunk/drivers/usb/core/message.c
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
int i;
int urb_flags;
int dma;
int use_sg;

if (!io || !dev || !sg
|| usb_pipecontrol(pipe)
Expand Down Expand Up @@ -392,7 +393,19 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
if (io->entries <= 0)
return io->entries;

io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags);
/* If we're running on an xHCI host controller, queue the whole scatter
* gather list with one call to urb_enqueue(). This is only for bulk,
* as that endpoint type does not care how the data gets broken up
* across frames.
*/
if (usb_pipebulk(pipe) &&
bus_to_hcd(dev->bus)->driver->flags & HCD_USB3) {
io->urbs = kmalloc(sizeof *io->urbs, mem_flags);
use_sg = true;
} else {
io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags);
use_sg = false;
}
if (!io->urbs)
goto nomem;

Expand All @@ -402,62 +415,92 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
if (usb_pipein(pipe))
urb_flags |= URB_SHORT_NOT_OK;

for_each_sg(sg, sg, io->entries, i) {
unsigned len;

io->urbs[i] = usb_alloc_urb(0, mem_flags);
if (!io->urbs[i]) {
io->entries = i;
if (use_sg) {
io->urbs[0] = usb_alloc_urb(0, mem_flags);
if (!io->urbs[0]) {
io->entries = 0;
goto nomem;
}

io->urbs[i]->dev = NULL;
io->urbs[i]->pipe = pipe;
io->urbs[i]->interval = period;
io->urbs[i]->transfer_flags = urb_flags;

io->urbs[i]->complete = sg_complete;
io->urbs[i]->context = io;

/*
* Some systems need to revert to PIO when DMA is temporarily
* unavailable. For their sakes, both transfer_buffer and
* transfer_dma are set when possible. However this can only
* work on systems without:
*
* - HIGHMEM, since DMA buffers located in high memory are
* not directly addressable by the CPU for PIO;
*
* - IOMMU, since dma_map_sg() is allowed to use an IOMMU to
* make virtually discontiguous buffers be "dma-contiguous"
* so that PIO and DMA need diferent numbers of URBs.
*
* So when HIGHMEM or IOMMU are in use, transfer_buffer is NULL
* to prevent stale pointers and to help spot bugs.
*/
if (dma) {
io->urbs[i]->transfer_dma = sg_dma_address(sg);
len = sg_dma_len(sg);
io->urbs[0]->dev = NULL;
io->urbs[0]->pipe = pipe;
io->urbs[0]->interval = period;
io->urbs[0]->transfer_flags = urb_flags;

io->urbs[0]->complete = sg_complete;
io->urbs[0]->context = io;
/* A length of zero means transfer the whole sg list */
io->urbs[0]->transfer_buffer_length = length;
if (length == 0) {
for_each_sg(sg, sg, io->entries, i) {
io->urbs[0]->transfer_buffer_length +=
sg_dma_len(sg);
}
}
io->urbs[0]->sg = io;
io->urbs[0]->num_sgs = io->entries;
io->entries = 1;
} else {
for_each_sg(sg, sg, io->entries, i) {
unsigned len;

io->urbs[i] = usb_alloc_urb(0, mem_flags);
if (!io->urbs[i]) {
io->entries = i;
goto nomem;
}

io->urbs[i]->dev = NULL;
io->urbs[i]->pipe = pipe;
io->urbs[i]->interval = period;
io->urbs[i]->transfer_flags = urb_flags;

io->urbs[i]->complete = sg_complete;
io->urbs[i]->context = io;

/*
* Some systems need to revert to PIO when DMA is
* temporarily unavailable. For their sakes, both
* transfer_buffer and transfer_dma are set when
* possible. However this can only work on systems
* without:
*
* - HIGHMEM, since DMA buffers located in high memory
* are not directly addressable by the CPU for PIO;
*
* - IOMMU, since dma_map_sg() is allowed to use an
* IOMMU to make virtually discontiguous buffers be
* "dma-contiguous" so that PIO and DMA need diferent
* numbers of URBs.
*
* So when HIGHMEM or IOMMU are in use, transfer_buffer
* is NULL to prevent stale pointers and to help spot
* bugs.
*/
if (dma) {
io->urbs[i]->transfer_dma = sg_dma_address(sg);
len = sg_dma_len(sg);
#if defined(CONFIG_HIGHMEM) || defined(CONFIG_GART_IOMMU)
io->urbs[i]->transfer_buffer = NULL;
io->urbs[i]->transfer_buffer = NULL;
#else
io->urbs[i]->transfer_buffer = sg_virt(sg);
io->urbs[i]->transfer_buffer = sg_virt(sg);
#endif
} else {
/* hc may use _only_ transfer_buffer */
io->urbs[i]->transfer_buffer = sg_virt(sg);
len = sg->length;
}
} else {
/* hc may use _only_ transfer_buffer */
io->urbs[i]->transfer_buffer = sg_virt(sg);
len = sg->length;
}

if (length) {
len = min_t(unsigned, len, length);
length -= len;
if (length == 0)
io->entries = i + 1;
if (length) {
len = min_t(unsigned, len, length);
length -= len;
if (length == 0)
io->entries = i + 1;
}
io->urbs[i]->transfer_buffer_length = len;
}
io->urbs[i]->transfer_buffer_length = len;
io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT;
}
io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT;

/* transaction state */
io->count = io->entries;
Expand Down
2 changes: 2 additions & 0 deletions trunk/include/linux/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,8 @@ struct urb {
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
struct usb_sg_request *sg; /* (in) scatter gather buffer list */
int num_sgs; /* (in) number of entries in the sg list */
u32 transfer_buffer_length; /* (in) data buffer length */
u32 actual_length; /* (return) actual transfer length */
unsigned char *setup_packet; /* (in) setup packet (control only) */
Expand Down

0 comments on commit 09f6310

Please sign in to comment.