diff options
Diffstat (limited to 'drivers/firewire')
-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 |
5 files changed, 96 insertions, 8 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) { } +static int dummy_flush_iso_completions(struct fw_iso_context *ctx) +{ + return -ENODEV; +} + static const struct fw_card_driver dummy_driver_template = { .read_phy_reg = dummy_read_phy_reg, .update_phy_reg = dummy_update_phy_reg, @@ -662,6 +667,7 @@ static const struct fw_card_driver dummy_driver_template = { .set_iso_channels = dummy_set_iso_channels, .queue_iso = dummy_queue_iso, .flush_queue_iso = dummy_flush_queue_iso, + .flush_iso_completions = dummy_flush_iso_completions, }; 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 { struct fw_cdev_send_phy_packet send_phy_packet; struct fw_cdev_receive_phy_packets receive_phy_packets; struct fw_cdev_set_iso_channels set_iso_channels; + struct fw_cdev_flush_iso flush_iso; }; 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) return fw_iso_context_stop(client->iso_context); } +static int ioctl_flush_iso(struct client *client, union ioctl_arg *arg) +{ + struct fw_cdev_flush_iso *a = &arg->flush_iso; + + if (client->iso_context == NULL || a->handle != 0) + return -EINVAL; + + return fw_iso_context_flush_completions(client->iso_context); +} + static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) { 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 *) = { [0x15] = ioctl_send_phy_packet, [0x16] = ioctl_receive_phy_packets, [0x17] = ioctl_set_iso_channels, + [0x18] = ioctl_flush_iso, }; 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) } EXPORT_SYMBOL(fw_iso_context_queue_flush); +int fw_iso_context_flush_completions(struct fw_iso_context *ctx) +{ + return ctx->card->driver->flush_iso_completions(ctx); +} +EXPORT_SYMBOL(fw_iso_context_flush_completions); + int fw_iso_context_stop(struct fw_iso_context *ctx) { 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 { void (*flush_queue_iso)(struct fw_iso_context *ctx); + int (*flush_iso_completions)(struct fw_iso_context *ctx); + int (*stop_iso)(struct fw_iso_context *ctx); }; 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 { struct context context; void *header; size_t header_length; + unsigned long flushing_completions; + u32 mc_buffer_bus; + u16 mc_completed; u16 last_timestamp; u8 sync; u8 tags; @@ -2749,28 +2752,51 @@ static int handle_ir_buffer_fill(struct context *context, { struct iso_context *ctx = container_of(context, struct iso_context, context); + unsigned int req_count, res_count, completed; u32 buffer_dma; - if (last->res_count != 0) + req_count = le16_to_cpu(last->req_count); + res_count = le16_to_cpu(ACCESS_ONCE(last->res_count)); + completed = req_count - res_count; + buffer_dma = le32_to_cpu(last->data_address); + + if (completed > 0) { + ctx->mc_buffer_bus = buffer_dma; + ctx->mc_completed = completed; + } + + if (res_count != 0) /* Descriptor(s) not done yet, stop iteration */ return 0; - buffer_dma = le32_to_cpu(last->data_address); dma_sync_single_range_for_cpu(context->ohci->card.device, buffer_dma & PAGE_MASK, buffer_dma & ~PAGE_MASK, - le16_to_cpu(last->req_count), - DMA_FROM_DEVICE); + completed, DMA_FROM_DEVICE); - if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) + if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) { ctx->base.callback.mc(&ctx->base, - le32_to_cpu(last->data_address) + - le16_to_cpu(last->req_count), + buffer_dma + completed, ctx->base.callback_data); + ctx->mc_completed = 0; + } return 1; } +static void flush_ir_buffer_fill(struct iso_context *ctx) +{ + dma_sync_single_range_for_cpu(ctx->context.ohci->card.device, + ctx->mc_buffer_bus & PAGE_MASK, + ctx->mc_buffer_bus & ~PAGE_MASK, + ctx->mc_completed, DMA_FROM_DEVICE); + + ctx->base.callback.mc(&ctx->base, + ctx->mc_buffer_bus + ctx->mc_completed, + ctx->base.callback_data); + ctx->mc_completed = 0; +} + static inline void sync_it_packet_for_cpu(struct context *context, struct descriptor *pd) { @@ -2925,8 +2951,10 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, if (ret < 0) goto out_with_header; - if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) + if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) { set_multichannel_mask(ohci, 0); + ctx->mc_completed = 0; + } return &ctx->base; @@ -3388,6 +3416,39 @@ static void ohci_flush_queue_iso(struct fw_iso_context *base) reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); } +static int ohci_flush_iso_completions(struct fw_iso_context *base) +{ + struct iso_context *ctx = container_of(base, struct iso_context, base); + int ret = 0; + + tasklet_disable(&ctx->context.tasklet); + + if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) { + context_tasklet((unsigned long)&ctx->context); + + switch (base->type) { + case FW_ISO_CONTEXT_TRANSMIT: + case FW_ISO_CONTEXT_RECEIVE: + if (ctx->header_length != 0) + flush_iso_completions(ctx); + break; + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + if (ctx->mc_completed != 0) + flush_ir_buffer_fill(ctx); + break; + default: + ret = -ENOSYS; + } + + clear_bit_unlock(0, &ctx->flushing_completions); + smp_mb__after_clear_bit(); + } + + tasklet_enable(&ctx->context.tasklet); + + return ret; +} + static const struct fw_card_driver ohci_driver = { .enable = ohci_enable, .read_phy_reg = ohci_read_phy_reg, @@ -3405,6 +3466,7 @@ static const struct fw_card_driver ohci_driver = { .set_iso_channels = ohci_set_iso_channels, .queue_iso = ohci_queue_iso, .flush_queue_iso = ohci_flush_queue_iso, + .flush_iso_completions = ohci_flush_iso_completions, .start_iso = ohci_start_iso, .stop_iso = ohci_stop_iso, }; |