|
@@ -577,17 +577,11 @@ static int ohci_update_phy_reg(struct fw_card *card, int addr,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static int ar_context_add_page(struct ar_context *ctx)
|
|
|
+static void ar_context_link_page(struct ar_context *ctx,
|
|
|
+ struct ar_buffer *ab, dma_addr_t ab_bus)
|
|
|
{
|
|
|
- struct device *dev = ctx->ohci->card.device;
|
|
|
- struct ar_buffer *ab;
|
|
|
- dma_addr_t uninitialized_var(ab_bus);
|
|
|
size_t offset;
|
|
|
|
|
|
- ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC);
|
|
|
- if (ab == NULL)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
ab->next = NULL;
|
|
|
memset(&ab->descriptor, 0, sizeof(ab->descriptor));
|
|
|
ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE |
|
|
@@ -606,6 +600,19 @@ static int ar_context_add_page(struct ar_context *ctx)
|
|
|
|
|
|
reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
|
|
|
flush_writes(ctx->ohci);
|
|
|
+}
|
|
|
+
|
|
|
+static int ar_context_add_page(struct ar_context *ctx)
|
|
|
+{
|
|
|
+ struct device *dev = ctx->ohci->card.device;
|
|
|
+ struct ar_buffer *ab;
|
|
|
+ dma_addr_t uninitialized_var(ab_bus);
|
|
|
+
|
|
|
+ ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC);
|
|
|
+ if (ab == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ ar_context_link_page(ctx, ab, ab_bus);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -730,16 +737,17 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
|
|
|
static void ar_context_tasklet(unsigned long data)
|
|
|
{
|
|
|
struct ar_context *ctx = (struct ar_context *)data;
|
|
|
- struct fw_ohci *ohci = ctx->ohci;
|
|
|
struct ar_buffer *ab;
|
|
|
struct descriptor *d;
|
|
|
void *buffer, *end;
|
|
|
+ __le16 res_count;
|
|
|
|
|
|
ab = ctx->current_buffer;
|
|
|
d = &ab->descriptor;
|
|
|
|
|
|
- if (d->res_count == 0) {
|
|
|
- size_t size, rest, offset;
|
|
|
+ res_count = ACCESS_ONCE(d->res_count);
|
|
|
+ if (res_count == 0) {
|
|
|
+ size_t size, size2, rest, pktsize, size3, offset;
|
|
|
dma_addr_t start_bus;
|
|
|
void *start;
|
|
|
|
|
@@ -750,29 +758,63 @@ static void ar_context_tasklet(unsigned long data)
|
|
|
*/
|
|
|
|
|
|
offset = offsetof(struct ar_buffer, data);
|
|
|
- start = buffer = ab;
|
|
|
+ start = ab;
|
|
|
start_bus = le32_to_cpu(ab->descriptor.data_address) - offset;
|
|
|
+ buffer = ab->data;
|
|
|
|
|
|
ab = ab->next;
|
|
|
d = &ab->descriptor;
|
|
|
- size = buffer + PAGE_SIZE - ctx->pointer;
|
|
|
+ size = start + PAGE_SIZE - ctx->pointer;
|
|
|
+ /* valid buffer data in the next page */
|
|
|
rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count);
|
|
|
+ /* what actually fits in this page */
|
|
|
+ size2 = min(rest, (size_t)PAGE_SIZE - offset - size);
|
|
|
memmove(buffer, ctx->pointer, size);
|
|
|
- memcpy(buffer + size, ab->data, rest);
|
|
|
- ctx->current_buffer = ab;
|
|
|
- ctx->pointer = (void *) ab->data + rest;
|
|
|
- end = buffer + size + rest;
|
|
|
+ memcpy(buffer + size, ab->data, size2);
|
|
|
+
|
|
|
+ while (size > 0) {
|
|
|
+ void *next = handle_ar_packet(ctx, buffer);
|
|
|
+ pktsize = next - buffer;
|
|
|
+ if (pktsize >= size) {
|
|
|
+ /*
|
|
|
+ * We have handled all the data that was
|
|
|
+ * originally in this page, so we can now
|
|
|
+ * continue in the next page.
|
|
|
+ */
|
|
|
+ buffer = next;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ /* move the next packet to the start of the buffer */
|
|
|
+ memmove(buffer, next, size + size2 - pktsize);
|
|
|
+ size -= pktsize;
|
|
|
+ /* fill up this page again */
|
|
|
+ size3 = min(rest - size2,
|
|
|
+ (size_t)PAGE_SIZE - offset - size - size2);
|
|
|
+ memcpy(buffer + size + size2,
|
|
|
+ (void *) ab->data + size2, size3);
|
|
|
+ size2 += size3;
|
|
|
+ }
|
|
|
|
|
|
- while (buffer < end)
|
|
|
- buffer = handle_ar_packet(ctx, buffer);
|
|
|
+ if (rest > 0) {
|
|
|
+ /* handle the packets that are fully in the next page */
|
|
|
+ buffer = (void *) ab->data +
|
|
|
+ (buffer - (start + offset + size));
|
|
|
+ end = (void *) ab->data + rest;
|
|
|
+
|
|
|
+ while (buffer < end)
|
|
|
+ buffer = handle_ar_packet(ctx, buffer);
|
|
|
|
|
|
- dma_free_coherent(ohci->card.device, PAGE_SIZE,
|
|
|
- start, start_bus);
|
|
|
- ar_context_add_page(ctx);
|
|
|
+ ctx->current_buffer = ab;
|
|
|
+ ctx->pointer = end;
|
|
|
+
|
|
|
+ ar_context_link_page(ctx, start, start_bus);
|
|
|
+ } else {
|
|
|
+ ctx->pointer = start + PAGE_SIZE;
|
|
|
+ }
|
|
|
} else {
|
|
|
buffer = ctx->pointer;
|
|
|
ctx->pointer = end =
|
|
|
- (void *) ab + PAGE_SIZE - le16_to_cpu(d->res_count);
|
|
|
+ (void *) ab + PAGE_SIZE - le16_to_cpu(res_count);
|
|
|
|
|
|
while (buffer < end)
|
|
|
buffer = handle_ar_packet(ctx, buffer);
|