diff options
Diffstat (limited to 'drivers/firewire/core-cdev.c')
| -rw-r--r-- | drivers/firewire/core-cdev.c | 71 |
1 files changed, 26 insertions, 45 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 49dc1612c691..9e964fdd175c 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -63,10 +63,10 @@ struct client { u64 bus_reset_closure; struct fw_iso_context *iso_context; + struct mutex iso_context_mutex; u64 iso_closure; struct fw_iso_buffer buffer; unsigned long vm_start; - bool buffer_is_mapped; struct list_head phy_receiver_link; u64 phy_receiver_closure; @@ -306,6 +306,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file) INIT_LIST_HEAD(&client->phy_receiver_link); INIT_LIST_HEAD(&client->link); kref_init(&client->kref); + mutex_init(&client->iso_context_mutex); file->private_data = client; @@ -1025,25 +1026,10 @@ static enum dma_data_direction iso_dma_direction(struct fw_iso_context *context) return DMA_FROM_DEVICE; } -static struct fw_iso_context *fw_iso_mc_context_create(struct fw_card *card, - fw_iso_mc_callback_t callback, - void *callback_data) -{ - struct fw_iso_context *ctx; - - ctx = fw_iso_context_create(card, FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL, - 0, 0, 0, NULL, callback_data); - if (!IS_ERR(ctx)) - ctx->callback.mc = callback; - - return ctx; -} - static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) { struct fw_cdev_create_iso_context *a = &arg->create_iso_context; struct fw_iso_context *context; - union fw_iso_callback cb; int ret; BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT || @@ -1055,20 +1041,15 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) case FW_ISO_CONTEXT_TRANSMIT: if (a->speed > SCODE_3200 || a->channel > 63) return -EINVAL; - - cb.sc = iso_callback; break; case FW_ISO_CONTEXT_RECEIVE: if (a->header_size < 4 || (a->header_size & 3) || a->channel > 63) return -EINVAL; - - cb.sc = iso_callback; break; case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: - cb.mc = iso_mc_callback; break; default: @@ -1076,38 +1057,36 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) } if (a->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) - context = fw_iso_mc_context_create(client->device->card, cb.mc, - client); + context = fw_iso_mc_context_create(client->device->card, iso_mc_callback, client); else - context = fw_iso_context_create(client->device->card, a->type, - a->channel, a->speed, - a->header_size, cb.sc, client); + context = fw_iso_context_create(client->device->card, a->type, a->channel, a->speed, + a->header_size, iso_callback, client); if (IS_ERR(context)) return PTR_ERR(context); if (client->version < FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW) - context->drop_overflow_headers = true; + context->flags |= FW_ISO_CONTEXT_FLAG_DROP_OVERFLOW_HEADERS; // We only support one context at this time. - guard(spinlock_irq)(&client->lock); - - if (client->iso_context != NULL) { - fw_iso_context_destroy(context); - - return -EBUSY; - } - if (!client->buffer_is_mapped) { - ret = fw_iso_buffer_map_dma(&client->buffer, - client->device->card, - iso_dma_direction(context)); - if (ret < 0) { + scoped_guard(mutex, &client->iso_context_mutex) { + if (client->iso_context != NULL) { fw_iso_context_destroy(context); - return ret; + return -EBUSY; + } + // The DMA mapping operation is available if the buffer is already allocated by + // mmap(2) system call. If not, it is delegated to the system call. + if (client->buffer.pages && !client->buffer.dma_addrs) { + ret = fw_iso_buffer_map_dma(&client->buffer, client->device->card, + iso_dma_direction(context)); + if (ret < 0) { + fw_iso_context_destroy(context); + + return ret; + } } - client->buffer_is_mapped = true; + client->iso_closure = a->closure; + client->iso_context = context; } - client->iso_closure = a->closure; - client->iso_context = context; a->handle = 0; @@ -1826,13 +1805,14 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) if (ret < 0) return ret; - scoped_guard(spinlock_irq, &client->lock) { + scoped_guard(mutex, &client->iso_context_mutex) { + // The direction of DMA can be determined if the isochronous context is already + // allocated. If not, the DMA mapping operation is postponed after the allocation. if (client->iso_context) { ret = fw_iso_buffer_map_dma(&client->buffer, client->device->card, iso_dma_direction(client->iso_context)); if (ret < 0) goto fail; - client->buffer_is_mapped = true; } } @@ -1879,6 +1859,7 @@ static int fw_device_op_release(struct inode *inode, struct file *file) if (client->iso_context) fw_iso_context_destroy(client->iso_context); + mutex_destroy(&client->iso_context_mutex); if (client->buffer.pages) fw_iso_buffer_destroy(&client->buffer, client->device->card); |
