diff options
Diffstat (limited to 'drivers/firewire/ohci.c')
-rw-r--r-- | drivers/firewire/ohci.c | 88 |
1 files changed, 65 insertions, 23 deletions
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 9dcb17d51aee..84eb607d6c03 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c | |||
@@ -577,17 +577,11 @@ static int ohci_update_phy_reg(struct fw_card *card, int addr, | |||
577 | return ret; | 577 | return ret; |
578 | } | 578 | } |
579 | 579 | ||
580 | static int ar_context_add_page(struct ar_context *ctx) | 580 | static void ar_context_link_page(struct ar_context *ctx, |
581 | struct ar_buffer *ab, dma_addr_t ab_bus) | ||
581 | { | 582 | { |
582 | struct device *dev = ctx->ohci->card.device; | ||
583 | struct ar_buffer *ab; | ||
584 | dma_addr_t uninitialized_var(ab_bus); | ||
585 | size_t offset; | 583 | size_t offset; |
586 | 584 | ||
587 | ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC); | ||
588 | if (ab == NULL) | ||
589 | return -ENOMEM; | ||
590 | |||
591 | ab->next = NULL; | 585 | ab->next = NULL; |
592 | memset(&ab->descriptor, 0, sizeof(ab->descriptor)); | 586 | memset(&ab->descriptor, 0, sizeof(ab->descriptor)); |
593 | ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE | | 587 | ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE | |
@@ -606,6 +600,19 @@ static int ar_context_add_page(struct ar_context *ctx) | |||
606 | 600 | ||
607 | reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); | 601 | reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); |
608 | flush_writes(ctx->ohci); | 602 | flush_writes(ctx->ohci); |
603 | } | ||
604 | |||
605 | static int ar_context_add_page(struct ar_context *ctx) | ||
606 | { | ||
607 | struct device *dev = ctx->ohci->card.device; | ||
608 | struct ar_buffer *ab; | ||
609 | dma_addr_t uninitialized_var(ab_bus); | ||
610 | |||
611 | ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC); | ||
612 | if (ab == NULL) | ||
613 | return -ENOMEM; | ||
614 | |||
615 | ar_context_link_page(ctx, ab, ab_bus); | ||
609 | 616 | ||
610 | return 0; | 617 | return 0; |
611 | } | 618 | } |
@@ -730,16 +737,17 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer) | |||
730 | static void ar_context_tasklet(unsigned long data) | 737 | static void ar_context_tasklet(unsigned long data) |
731 | { | 738 | { |
732 | struct ar_context *ctx = (struct ar_context *)data; | 739 | struct ar_context *ctx = (struct ar_context *)data; |
733 | struct fw_ohci *ohci = ctx->ohci; | ||
734 | struct ar_buffer *ab; | 740 | struct ar_buffer *ab; |
735 | struct descriptor *d; | 741 | struct descriptor *d; |
736 | void *buffer, *end; | 742 | void *buffer, *end; |
743 | __le16 res_count; | ||
737 | 744 | ||
738 | ab = ctx->current_buffer; | 745 | ab = ctx->current_buffer; |
739 | d = &ab->descriptor; | 746 | d = &ab->descriptor; |
740 | 747 | ||
741 | if (d->res_count == 0) { | 748 | res_count = ACCESS_ONCE(d->res_count); |
742 | size_t size, rest, offset; | 749 | if (res_count == 0) { |
750 | size_t size, size2, rest, pktsize, size3, offset; | ||
743 | dma_addr_t start_bus; | 751 | dma_addr_t start_bus; |
744 | void *start; | 752 | void *start; |
745 | 753 | ||
@@ -750,29 +758,63 @@ static void ar_context_tasklet(unsigned long data) | |||
750 | */ | 758 | */ |
751 | 759 | ||
752 | offset = offsetof(struct ar_buffer, data); | 760 | offset = offsetof(struct ar_buffer, data); |
753 | start = buffer = ab; | 761 | start = ab; |
754 | start_bus = le32_to_cpu(ab->descriptor.data_address) - offset; | 762 | start_bus = le32_to_cpu(ab->descriptor.data_address) - offset; |
763 | buffer = ab->data; | ||
755 | 764 | ||
756 | ab = ab->next; | 765 | ab = ab->next; |
757 | d = &ab->descriptor; | 766 | d = &ab->descriptor; |
758 | size = buffer + PAGE_SIZE - ctx->pointer; | 767 | size = start + PAGE_SIZE - ctx->pointer; |
768 | /* valid buffer data in the next page */ | ||
759 | rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count); | 769 | rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count); |
770 | /* what actually fits in this page */ | ||
771 | size2 = min(rest, (size_t)PAGE_SIZE - offset - size); | ||
760 | memmove(buffer, ctx->pointer, size); | 772 | memmove(buffer, ctx->pointer, size); |
761 | memcpy(buffer + size, ab->data, rest); | 773 | memcpy(buffer + size, ab->data, size2); |
762 | ctx->current_buffer = ab; | 774 | |
763 | ctx->pointer = (void *) ab->data + rest; | 775 | while (size > 0) { |
764 | end = buffer + size + rest; | 776 | void *next = handle_ar_packet(ctx, buffer); |
777 | pktsize = next - buffer; | ||
778 | if (pktsize >= size) { | ||
779 | /* | ||
780 | * We have handled all the data that was | ||
781 | * originally in this page, so we can now | ||
782 | * continue in the next page. | ||
783 | */ | ||
784 | buffer = next; | ||
785 | break; | ||
786 | } | ||
787 | /* move the next packet to the start of the buffer */ | ||
788 | memmove(buffer, next, size + size2 - pktsize); | ||
789 | size -= pktsize; | ||
790 | /* fill up this page again */ | ||
791 | size3 = min(rest - size2, | ||
792 | (size_t)PAGE_SIZE - offset - size - size2); | ||
793 | memcpy(buffer + size + size2, | ||
794 | (void *) ab->data + size2, size3); | ||
795 | size2 += size3; | ||
796 | } | ||
765 | 797 | ||
766 | while (buffer < end) | 798 | if (rest > 0) { |
767 | buffer = handle_ar_packet(ctx, buffer); | 799 | /* handle the packets that are fully in the next page */ |
800 | buffer = (void *) ab->data + | ||
801 | (buffer - (start + offset + size)); | ||
802 | end = (void *) ab->data + rest; | ||
803 | |||
804 | while (buffer < end) | ||
805 | buffer = handle_ar_packet(ctx, buffer); | ||
768 | 806 | ||
769 | dma_free_coherent(ohci->card.device, PAGE_SIZE, | 807 | ctx->current_buffer = ab; |
770 | start, start_bus); | 808 | ctx->pointer = end; |
771 | ar_context_add_page(ctx); | 809 | |
810 | ar_context_link_page(ctx, start, start_bus); | ||
811 | } else { | ||
812 | ctx->pointer = start + PAGE_SIZE; | ||
813 | } | ||
772 | } else { | 814 | } else { |
773 | buffer = ctx->pointer; | 815 | buffer = ctx->pointer; |
774 | ctx->pointer = end = | 816 | ctx->pointer = end = |
775 | (void *) ab + PAGE_SIZE - le16_to_cpu(d->res_count); | 817 | (void *) ab + PAGE_SIZE - le16_to_cpu(res_count); |
776 | 818 | ||
777 | while (buffer < end) | 819 | while (buffer < end) |
778 | buffer = handle_ar_packet(ctx, buffer); | 820 | buffer = handle_ar_packet(ctx, buffer); |