summaryrefslogtreecommitdiff
path: root/drivers/firewire/core-iso.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firewire/core-iso.c')
-rw-r--r--drivers/firewire/core-iso.c102
1 files changed, 55 insertions, 47 deletions
diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c
index a67493862c85..3190b2ca1298 100644
--- a/drivers/firewire/core-iso.c
+++ b/drivers/firewire/core-iso.c
@@ -30,48 +30,57 @@
int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count)
{
- int i;
+ struct page **page_array __free(kfree) = kcalloc(page_count, sizeof(page_array[0]), GFP_KERNEL);
- buffer->page_count = 0;
- buffer->page_count_mapped = 0;
- buffer->pages = kmalloc_array(page_count, sizeof(buffer->pages[0]),
- GFP_KERNEL);
- if (buffer->pages == NULL)
+ if (!page_array)
return -ENOMEM;
- for (i = 0; i < page_count; i++) {
- buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
- if (buffer->pages[i] == NULL)
- break;
- }
- buffer->page_count = i;
- if (i < page_count) {
- fw_iso_buffer_destroy(buffer, NULL);
+ // Retrieve noncontiguous pages. The descriptors for 1394 OHCI isochronous DMA contexts
+ // have a set of address and length per each, while the reason to use pages is the
+ // convenience to map them into virtual address space of user process.
+ unsigned long nr_populated = alloc_pages_bulk(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO,
+ page_count, page_array);
+ if (nr_populated != page_count) {
+ // Assuming the above call fills page_array sequentially from the beginning.
+ release_pages(page_array, nr_populated);
return -ENOMEM;
}
+ buffer->page_count = page_count;
+ buffer->pages = no_free_ptr(page_array);
+
return 0;
}
int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card,
enum dma_data_direction direction)
{
- dma_addr_t address;
+ dma_addr_t *dma_addrs __free(kfree) = kcalloc(buffer->page_count, sizeof(dma_addrs[0]),
+ GFP_KERNEL);
int i;
- buffer->direction = direction;
+ if (!dma_addrs)
+ return -ENOMEM;
+ // Retrieve DMA mapping addresses for the pages. They are not contiguous. Maintain the cache
+ // coherency for the pages by hand.
for (i = 0; i < buffer->page_count; i++) {
- address = dma_map_page(card->device, buffer->pages[i],
- 0, PAGE_SIZE, direction);
- if (dma_mapping_error(card->device, address))
+ // The dma_map_phys() with a physical address per page is available here, instead.
+ dma_addr_t dma_addr = dma_map_page(card->device, buffer->pages[i], 0, PAGE_SIZE,
+ direction);
+ if (dma_mapping_error(card->device, dma_addr))
break;
- set_page_private(buffer->pages[i], address);
+ dma_addrs[i] = dma_addr;
}
- buffer->page_count_mapped = i;
- if (i < buffer->page_count)
+ if (i < buffer->page_count) {
+ while (i-- > 0)
+ dma_unmap_page(card->device, dma_addrs[i], PAGE_SIZE, buffer->direction);
return -ENOMEM;
+ }
+
+ buffer->direction = direction;
+ buffer->dma_addrs = no_free_ptr(dma_addrs);
return 0;
}
@@ -96,34 +105,31 @@ EXPORT_SYMBOL(fw_iso_buffer_init);
void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer,
struct fw_card *card)
{
- int i;
- dma_addr_t address;
+ if (buffer->dma_addrs) {
+ for (int i = 0; i < buffer->page_count; ++i) {
+ dma_addr_t dma_addr = buffer->dma_addrs[i];
+ dma_unmap_page(card->device, dma_addr, PAGE_SIZE, buffer->direction);
+ }
+ kfree(buffer->dma_addrs);
+ buffer->dma_addrs = NULL;
+ }
- for (i = 0; i < buffer->page_count_mapped; i++) {
- address = page_private(buffer->pages[i]);
- dma_unmap_page(card->device, address,
- PAGE_SIZE, buffer->direction);
+ if (buffer->pages) {
+ release_pages(buffer->pages, buffer->page_count);
+ kfree(buffer->pages);
+ buffer->pages = NULL;
}
- for (i = 0; i < buffer->page_count; i++)
- __free_page(buffer->pages[i]);
- kfree(buffer->pages);
- buffer->pages = NULL;
buffer->page_count = 0;
- buffer->page_count_mapped = 0;
}
EXPORT_SYMBOL(fw_iso_buffer_destroy);
/* Convert DMA address to offset into virtually contiguous buffer. */
size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed)
{
- size_t i;
- dma_addr_t address;
- ssize_t offset;
-
- for (i = 0; i < buffer->page_count; i++) {
- address = page_private(buffer->pages[i]);
- offset = (ssize_t)completed - (ssize_t)address;
+ for (int i = 0; i < buffer->page_count; i++) {
+ dma_addr_t dma_addr = buffer->dma_addrs[i];
+ ssize_t offset = (ssize_t)completed - (ssize_t)dma_addr;
if (offset > 0 && offset <= PAGE_SIZE)
return (i << PAGE_SHIFT) + offset;
}
@@ -131,14 +137,14 @@ size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed)
return 0;
}
-struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
- int type, int channel, int speed, size_t header_size,
- fw_iso_callback_t callback, void *callback_data)
+struct fw_iso_context *__fw_iso_context_create(struct fw_card *card, int type, int channel,
+ int speed, size_t header_size, size_t header_storage_size,
+ union fw_iso_callback callback, void *callback_data)
{
struct fw_iso_context *ctx;
- ctx = card->driver->allocate_iso_context(card,
- type, channel, header_size);
+ ctx = card->driver->allocate_iso_context(card, type, channel, header_size,
+ header_storage_size);
if (IS_ERR(ctx))
return ctx;
@@ -146,8 +152,10 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
ctx->type = type;
ctx->channel = channel;
ctx->speed = speed;
+ ctx->flags = 0;
ctx->header_size = header_size;
- ctx->callback.sc = callback;
+ ctx->header_storage_size = header_storage_size;
+ ctx->callback = callback;
ctx->callback_data = callback_data;
trace_isoc_outbound_allocate(ctx, channel, speed);
@@ -156,7 +164,7 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
return ctx;
}
-EXPORT_SYMBOL(fw_iso_context_create);
+EXPORT_SYMBOL(__fw_iso_context_create);
void fw_iso_context_destroy(struct fw_iso_context *ctx)
{