diff options
-rw-r--r-- | drivers/firewire/core-card.c | 6 | ||||
-rw-r--r-- | drivers/firewire/core-cdev.c | 12 | ||||
-rw-r--r-- | drivers/firewire/core-iso.c | 6 | ||||
-rw-r--r-- | drivers/firewire/core.h | 2 | ||||
-rw-r--r-- | drivers/firewire/ohci.c | 78 | ||||
-rw-r--r-- | include/linux/firewire-cdev.h | 35 | ||||
-rw-r--r-- | include/linux/firewire.h | 1 |
7 files changed, 127 insertions, 13 deletions
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index b19db0f6a254..cc595eba7ba9 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c | |||
@@ -650,6 +650,11 @@ static void dummy_flush_queue_iso(struct fw_iso_context *ctx) | |||
650 | { | 650 | { |
651 | } | 651 | } |
652 | 652 | ||
653 | static int dummy_flush_iso_completions(struct fw_iso_context *ctx) | ||
654 | { | ||
655 | return -ENODEV; | ||
656 | } | ||
657 | |||
653 | static const struct fw_card_driver dummy_driver_template = { | 658 | static const struct fw_card_driver dummy_driver_template = { |
654 | .read_phy_reg = dummy_read_phy_reg, | 659 | .read_phy_reg = dummy_read_phy_reg, |
655 | .update_phy_reg = dummy_update_phy_reg, | 660 | .update_phy_reg = dummy_update_phy_reg, |
@@ -662,6 +667,7 @@ static const struct fw_card_driver dummy_driver_template = { | |||
662 | .set_iso_channels = dummy_set_iso_channels, | 667 | .set_iso_channels = dummy_set_iso_channels, |
663 | .queue_iso = dummy_queue_iso, | 668 | .queue_iso = dummy_queue_iso, |
664 | .flush_queue_iso = dummy_flush_queue_iso, | 669 | .flush_queue_iso = dummy_flush_queue_iso, |
670 | .flush_iso_completions = dummy_flush_iso_completions, | ||
665 | }; | 671 | }; |
666 | 672 | ||
667 | void fw_card_release(struct kref *kref) | 673 | void fw_card_release(struct kref *kref) |
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 4cb27dc542ce..22c6df5f136d 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c | |||
@@ -438,6 +438,7 @@ union ioctl_arg { | |||
438 | struct fw_cdev_send_phy_packet send_phy_packet; | 438 | struct fw_cdev_send_phy_packet send_phy_packet; |
439 | struct fw_cdev_receive_phy_packets receive_phy_packets; | 439 | struct fw_cdev_receive_phy_packets receive_phy_packets; |
440 | struct fw_cdev_set_iso_channels set_iso_channels; | 440 | struct fw_cdev_set_iso_channels set_iso_channels; |
441 | struct fw_cdev_flush_iso flush_iso; | ||
441 | }; | 442 | }; |
442 | 443 | ||
443 | static int ioctl_get_info(struct client *client, union ioctl_arg *arg) | 444 | static int ioctl_get_info(struct client *client, union ioctl_arg *arg) |
@@ -1168,6 +1169,16 @@ static int ioctl_stop_iso(struct client *client, union ioctl_arg *arg) | |||
1168 | return fw_iso_context_stop(client->iso_context); | 1169 | return fw_iso_context_stop(client->iso_context); |
1169 | } | 1170 | } |
1170 | 1171 | ||
1172 | static int ioctl_flush_iso(struct client *client, union ioctl_arg *arg) | ||
1173 | { | ||
1174 | struct fw_cdev_flush_iso *a = &arg->flush_iso; | ||
1175 | |||
1176 | if (client->iso_context == NULL || a->handle != 0) | ||
1177 | return -EINVAL; | ||
1178 | |||
1179 | return fw_iso_context_flush_completions(client->iso_context); | ||
1180 | } | ||
1181 | |||
1171 | static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) | 1182 | static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) |
1172 | { | 1183 | { |
1173 | struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2; | 1184 | struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2; |
@@ -1589,6 +1600,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { | |||
1589 | [0x15] = ioctl_send_phy_packet, | 1600 | [0x15] = ioctl_send_phy_packet, |
1590 | [0x16] = ioctl_receive_phy_packets, | 1601 | [0x16] = ioctl_receive_phy_packets, |
1591 | [0x17] = ioctl_set_iso_channels, | 1602 | [0x17] = ioctl_set_iso_channels, |
1603 | [0x18] = ioctl_flush_iso, | ||
1592 | }; | 1604 | }; |
1593 | 1605 | ||
1594 | static int dispatch_ioctl(struct client *client, | 1606 | static int dispatch_ioctl(struct client *client, |
diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 57c3973093ad..2f432a20ce7e 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c | |||
@@ -191,6 +191,12 @@ void fw_iso_context_queue_flush(struct fw_iso_context *ctx) | |||
191 | } | 191 | } |
192 | EXPORT_SYMBOL(fw_iso_context_queue_flush); | 192 | EXPORT_SYMBOL(fw_iso_context_queue_flush); |
193 | 193 | ||
194 | int fw_iso_context_flush_completions(struct fw_iso_context *ctx) | ||
195 | { | ||
196 | return ctx->card->driver->flush_iso_completions(ctx); | ||
197 | } | ||
198 | EXPORT_SYMBOL(fw_iso_context_flush_completions); | ||
199 | |||
194 | int fw_iso_context_stop(struct fw_iso_context *ctx) | 200 | int fw_iso_context_stop(struct fw_iso_context *ctx) |
195 | { | 201 | { |
196 | return ctx->card->driver->stop_iso(ctx); | 202 | return ctx->card->driver->stop_iso(ctx); |
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 62f57a4331e3..9047f5547d98 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h | |||
@@ -106,6 +106,8 @@ struct fw_card_driver { | |||
106 | 106 | ||
107 | void (*flush_queue_iso)(struct fw_iso_context *ctx); | 107 | void (*flush_queue_iso)(struct fw_iso_context *ctx); |
108 | 108 | ||
109 | int (*flush_iso_completions)(struct fw_iso_context *ctx); | ||
110 | |||
109 | int (*stop_iso)(struct fw_iso_context *ctx); | 111 | int (*stop_iso)(struct fw_iso_context *ctx); |
110 | }; | 112 | }; |
111 | 113 | ||
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 632562667a01..59e7894ae3b8 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c | |||
@@ -172,6 +172,9 @@ struct iso_context { | |||
172 | struct context context; | 172 | struct context context; |
173 | void *header; | 173 | void *header; |
174 | size_t header_length; | 174 | size_t header_length; |
175 | unsigned long flushing_completions; | ||
176 | u32 mc_buffer_bus; | ||
177 | u16 mc_completed; | ||
175 | u16 last_timestamp; | 178 | u16 last_timestamp; |
176 | u8 sync; | 179 | u8 sync; |
177 | u8 tags; | 180 | u8 tags; |
@@ -2749,28 +2752,51 @@ static int handle_ir_buffer_fill(struct context *context, | |||
2749 | { | 2752 | { |
2750 | struct iso_context *ctx = | 2753 | struct iso_context *ctx = |
2751 | container_of(context, struct iso_context, context); | 2754 | container_of(context, struct iso_context, context); |
2755 | unsigned int req_count, res_count, completed; | ||
2752 | u32 buffer_dma; | 2756 | u32 buffer_dma; |
2753 | 2757 | ||
2754 | if (last->res_count != 0) | 2758 | req_count = le16_to_cpu(last->req_count); |
2759 | res_count = le16_to_cpu(ACCESS_ONCE(last->res_count)); | ||
2760 | completed = req_count - res_count; | ||
2761 | buffer_dma = le32_to_cpu(last->data_address); | ||
2762 | |||
2763 | if (completed > 0) { | ||
2764 | ctx->mc_buffer_bus = buffer_dma; | ||
2765 | ctx->mc_completed = completed; | ||
2766 | } | ||
2767 | |||
2768 | if (res_count != 0) | ||
2755 | /* Descriptor(s) not done yet, stop iteration */ | 2769 | /* Descriptor(s) not done yet, stop iteration */ |
2756 | return 0; | 2770 | return 0; |
2757 | 2771 | ||
2758 | buffer_dma = le32_to_cpu(last->data_address); | ||
2759 | dma_sync_single_range_for_cpu(context->ohci->card.device, | 2772 | dma_sync_single_range_for_cpu(context->ohci->card.device, |
2760 | buffer_dma & PAGE_MASK, | 2773 | buffer_dma & PAGE_MASK, |
2761 | buffer_dma & ~PAGE_MASK, | 2774 | buffer_dma & ~PAGE_MASK, |
2762 | le16_to_cpu(last->req_count), | 2775 | completed, DMA_FROM_DEVICE); |
2763 | DMA_FROM_DEVICE); | ||
2764 | 2776 | ||
2765 | if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) | 2777 | if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) { |
2766 | ctx->base.callback.mc(&ctx->base, | 2778 | ctx->base.callback.mc(&ctx->base, |
2767 | le32_to_cpu(last->data_address) + | 2779 | buffer_dma + completed, |
2768 | le16_to_cpu(last->req_count), | ||
2769 | ctx->base.callback_data); | 2780 | ctx->base.callback_data); |
2781 | ctx->mc_completed = 0; | ||
2782 | } | ||
2770 | 2783 | ||
2771 | return 1; | 2784 | return 1; |
2772 | } | 2785 | } |
2773 | 2786 | ||
2787 | static void flush_ir_buffer_fill(struct iso_context *ctx) | ||
2788 | { | ||
2789 | dma_sync_single_range_for_cpu(ctx->context.ohci->card.device, | ||
2790 | ctx->mc_buffer_bus & PAGE_MASK, | ||
2791 | ctx->mc_buffer_bus & ~PAGE_MASK, | ||
2792 | ctx->mc_completed, DMA_FROM_DEVICE); | ||
2793 | |||
2794 | ctx->base.callback.mc(&ctx->base, | ||
2795 | ctx->mc_buffer_bus + ctx->mc_completed, | ||
2796 | ctx->base.callback_data); | ||
2797 | ctx->mc_completed = 0; | ||
2798 | } | ||
2799 | |||
2774 | static inline void sync_it_packet_for_cpu(struct context *context, | 2800 | static inline void sync_it_packet_for_cpu(struct context *context, |
2775 | struct descriptor *pd) | 2801 | struct descriptor *pd) |
2776 | { | 2802 | { |
@@ -2925,8 +2951,10 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, | |||
2925 | if (ret < 0) | 2951 | if (ret < 0) |
2926 | goto out_with_header; | 2952 | goto out_with_header; |
2927 | 2953 | ||
2928 | if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) | 2954 | if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) { |
2929 | set_multichannel_mask(ohci, 0); | 2955 | set_multichannel_mask(ohci, 0); |
2956 | ctx->mc_completed = 0; | ||
2957 | } | ||
2930 | 2958 | ||
2931 | return &ctx->base; | 2959 | return &ctx->base; |
2932 | 2960 | ||
@@ -3388,6 +3416,39 @@ static void ohci_flush_queue_iso(struct fw_iso_context *base) | |||
3388 | reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); | 3416 | reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); |
3389 | } | 3417 | } |
3390 | 3418 | ||
3419 | static int ohci_flush_iso_completions(struct fw_iso_context *base) | ||
3420 | { | ||
3421 | struct iso_context *ctx = container_of(base, struct iso_context, base); | ||
3422 | int ret = 0; | ||
3423 | |||
3424 | tasklet_disable(&ctx->context.tasklet); | ||
3425 | |||
3426 | if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) { | ||
3427 | context_tasklet((unsigned long)&ctx->context); | ||
3428 | |||
3429 | switch (base->type) { | ||
3430 | case FW_ISO_CONTEXT_TRANSMIT: | ||
3431 | case FW_ISO_CONTEXT_RECEIVE: | ||
3432 | if (ctx->header_length != 0) | ||
3433 | flush_iso_completions(ctx); | ||
3434 | break; | ||
3435 | case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: | ||
3436 | if (ctx->mc_completed != 0) | ||
3437 | flush_ir_buffer_fill(ctx); | ||
3438 | break; | ||
3439 | default: | ||
3440 | ret = -ENOSYS; | ||
3441 | } | ||
3442 | |||
3443 | clear_bit_unlock(0, &ctx->flushing_completions); | ||
3444 | smp_mb__after_clear_bit(); | ||
3445 | } | ||
3446 | |||
3447 | tasklet_enable(&ctx->context.tasklet); | ||
3448 | |||
3449 | return ret; | ||
3450 | } | ||
3451 | |||
3391 | static const struct fw_card_driver ohci_driver = { | 3452 | static const struct fw_card_driver ohci_driver = { |
3392 | .enable = ohci_enable, | 3453 | .enable = ohci_enable, |
3393 | .read_phy_reg = ohci_read_phy_reg, | 3454 | .read_phy_reg = ohci_read_phy_reg, |
@@ -3405,6 +3466,7 @@ static const struct fw_card_driver ohci_driver = { | |||
3405 | .set_iso_channels = ohci_set_iso_channels, | 3466 | .set_iso_channels = ohci_set_iso_channels, |
3406 | .queue_iso = ohci_queue_iso, | 3467 | .queue_iso = ohci_queue_iso, |
3407 | .flush_queue_iso = ohci_flush_queue_iso, | 3468 | .flush_queue_iso = ohci_flush_queue_iso, |
3469 | .flush_iso_completions = ohci_flush_iso_completions, | ||
3408 | .start_iso = ohci_start_iso, | 3470 | .start_iso = ohci_start_iso, |
3409 | .stop_iso = ohci_stop_iso, | 3471 | .stop_iso = ohci_stop_iso, |
3410 | }; | 3472 | }; |
diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index b9bd349c6930..d50036953497 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h | |||
@@ -212,10 +212,11 @@ struct fw_cdev_event_request2 { | |||
212 | * @header: Stripped headers, if any | 212 | * @header: Stripped headers, if any |
213 | * | 213 | * |
214 | * This event is sent when the controller has completed an &fw_cdev_iso_packet | 214 | * This event is sent when the controller has completed an &fw_cdev_iso_packet |
215 | * with the %FW_CDEV_ISO_INTERRUPT bit set, or when there have been so many | 215 | * with the %FW_CDEV_ISO_INTERRUPT bit set, when explicitly requested with |
216 | * completed packets without the interrupt bit set that the kernel's internal | 216 | * %FW_CDEV_IOC_FLUSH_ISO, or when there have been so many completed packets |
217 | * buffer for @header is about to overflow. (In the latter case, kernels with | 217 | * without the interrupt bit set that the kernel's internal buffer for @header |
218 | * ABI version < 5 drop header data up to the next interrupt packet.) | 218 | * is about to overflow. (In the last case, kernels with ABI version < 5 drop |
219 | * header data up to the next interrupt packet.) | ||
219 | * | 220 | * |
220 | * Isochronous transmit events (context type %FW_CDEV_ISO_CONTEXT_TRANSMIT): | 221 | * Isochronous transmit events (context type %FW_CDEV_ISO_CONTEXT_TRANSMIT): |
221 | * | 222 | * |
@@ -271,7 +272,8 @@ struct fw_cdev_event_iso_interrupt { | |||
271 | * This event is sent in multichannel contexts (context type | 272 | * This event is sent in multichannel contexts (context type |
272 | * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL) for &fw_cdev_iso_packet buffer | 273 | * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL) for &fw_cdev_iso_packet buffer |
273 | * chunks that have been completely filled and that have the | 274 | * chunks that have been completely filled and that have the |
274 | * %FW_CDEV_ISO_INTERRUPT bit set. | 275 | * %FW_CDEV_ISO_INTERRUPT bit set, or when explicitly requested with |
276 | * %FW_CDEV_IOC_FLUSH_ISO. | ||
275 | * | 277 | * |
276 | * The buffer is continuously filled with the following data, per packet: | 278 | * The buffer is continuously filled with the following data, per packet: |
277 | * - the 1394 iso packet header as described at &fw_cdev_event_iso_interrupt, | 279 | * - the 1394 iso packet header as described at &fw_cdev_event_iso_interrupt, |
@@ -421,6 +423,9 @@ union fw_cdev_event { | |||
421 | #define FW_CDEV_IOC_RECEIVE_PHY_PACKETS _IOW('#', 0x16, struct fw_cdev_receive_phy_packets) | 423 | #define FW_CDEV_IOC_RECEIVE_PHY_PACKETS _IOW('#', 0x16, struct fw_cdev_receive_phy_packets) |
422 | #define FW_CDEV_IOC_SET_ISO_CHANNELS _IOW('#', 0x17, struct fw_cdev_set_iso_channels) | 424 | #define FW_CDEV_IOC_SET_ISO_CHANNELS _IOW('#', 0x17, struct fw_cdev_set_iso_channels) |
423 | 425 | ||
426 | /* available since kernel version 3.4 */ | ||
427 | #define FW_CDEV_IOC_FLUSH_ISO _IOW('#', 0x18, struct fw_cdev_flush_iso) | ||
428 | |||
424 | /* | 429 | /* |
425 | * ABI version history | 430 | * ABI version history |
426 | * 1 (2.6.22) - initial version | 431 | * 1 (2.6.22) - initial version |
@@ -445,6 +450,7 @@ union fw_cdev_event { | |||
445 | * %FW_CDEV_IOC_SET_ISO_CHANNELS | 450 | * %FW_CDEV_IOC_SET_ISO_CHANNELS |
446 | * 5 (3.4) - send %FW_CDEV_EVENT_ISO_INTERRUPT events when needed to | 451 | * 5 (3.4) - send %FW_CDEV_EVENT_ISO_INTERRUPT events when needed to |
447 | * avoid dropping data | 452 | * avoid dropping data |
453 | * - added %FW_CDEV_IOC_FLUSH_ISO | ||
448 | */ | 454 | */ |
449 | 455 | ||
450 | /** | 456 | /** |
@@ -855,6 +861,25 @@ struct fw_cdev_stop_iso { | |||
855 | }; | 861 | }; |
856 | 862 | ||
857 | /** | 863 | /** |
864 | * struct fw_cdev_flush_iso - flush completed iso packets | ||
865 | * @handle: handle of isochronous context to flush | ||
866 | * | ||
867 | * For %FW_CDEV_ISO_CONTEXT_TRANSMIT or %FW_CDEV_ISO_CONTEXT_RECEIVE contexts, | ||
868 | * report any completed packets. | ||
869 | * | ||
870 | * For %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL contexts, report the current | ||
871 | * offset in the receive buffer, if it has changed; this is typically in the | ||
872 | * middle of some buffer chunk. | ||
873 | * | ||
874 | * Any %FW_CDEV_EVENT_ISO_INTERRUPT or %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL | ||
875 | * events generated by this ioctl are sent synchronously, i.e., are available | ||
876 | * for reading from the file descriptor when this ioctl returns. | ||
877 | */ | ||
878 | struct fw_cdev_flush_iso { | ||
879 | __u32 handle; | ||
880 | }; | ||
881 | |||
882 | /** | ||
858 | * struct fw_cdev_get_cycle_timer - read cycle timer register | 883 | * struct fw_cdev_get_cycle_timer - read cycle timer register |
859 | * @local_time: system time, in microseconds since the Epoch | 884 | * @local_time: system time, in microseconds since the Epoch |
860 | * @cycle_timer: Cycle Time register contents | 885 | * @cycle_timer: Cycle Time register contents |
diff --git a/include/linux/firewire.h b/include/linux/firewire.h index ab5b7a18decf..cdc9b719e9c7 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h | |||
@@ -426,6 +426,7 @@ int fw_iso_context_queue(struct fw_iso_context *ctx, | |||
426 | struct fw_iso_buffer *buffer, | 426 | struct fw_iso_buffer *buffer, |
427 | unsigned long payload); | 427 | unsigned long payload); |
428 | void fw_iso_context_queue_flush(struct fw_iso_context *ctx); | 428 | void fw_iso_context_queue_flush(struct fw_iso_context *ctx); |
429 | int fw_iso_context_flush_completions(struct fw_iso_context *ctx); | ||
429 | int fw_iso_context_start(struct fw_iso_context *ctx, | 430 | int fw_iso_context_start(struct fw_iso_context *ctx, |
430 | int cycle, int sync, int tags); | 431 | int cycle, int sync, int tags); |
431 | int fw_iso_context_stop(struct fw_iso_context *ctx); | 432 | int fw_iso_context_stop(struct fw_iso_context *ctx); |