aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firewire')
-rw-r--r--drivers/firewire/core-cdev.c51
-rw-r--r--drivers/firewire/core-iso.c80
-rw-r--r--drivers/firewire/core.h7
3 files changed, 99 insertions, 39 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 2e6b24547e2a..2783f69dada6 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -22,6 +22,7 @@
22#include <linux/compat.h> 22#include <linux/compat.h>
23#include <linux/delay.h> 23#include <linux/delay.h>
24#include <linux/device.h> 24#include <linux/device.h>
25#include <linux/dma-mapping.h>
25#include <linux/errno.h> 26#include <linux/errno.h>
26#include <linux/firewire.h> 27#include <linux/firewire.h>
27#include <linux/firewire-cdev.h> 28#include <linux/firewire-cdev.h>
@@ -70,6 +71,7 @@ struct client {
70 u64 iso_closure; 71 u64 iso_closure;
71 struct fw_iso_buffer buffer; 72 struct fw_iso_buffer buffer;
72 unsigned long vm_start; 73 unsigned long vm_start;
74 bool buffer_is_mapped;
73 75
74 struct list_head phy_receiver_link; 76 struct list_head phy_receiver_link;
75 u64 phy_receiver_closure; 77 u64 phy_receiver_closure;
@@ -959,11 +961,20 @@ static void iso_mc_callback(struct fw_iso_context *context,
959 sizeof(e->interrupt), NULL, 0); 961 sizeof(e->interrupt), NULL, 0);
960} 962}
961 963
964static enum dma_data_direction iso_dma_direction(struct fw_iso_context *context)
965{
966 if (context->type == FW_ISO_CONTEXT_TRANSMIT)
967 return DMA_TO_DEVICE;
968 else
969 return DMA_FROM_DEVICE;
970}
971
962static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) 972static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
963{ 973{
964 struct fw_cdev_create_iso_context *a = &arg->create_iso_context; 974 struct fw_cdev_create_iso_context *a = &arg->create_iso_context;
965 struct fw_iso_context *context; 975 struct fw_iso_context *context;
966 fw_iso_callback_t cb; 976 fw_iso_callback_t cb;
977 int ret;
967 978
968 BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT || 979 BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT ||
969 FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE || 980 FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE ||
@@ -1004,8 +1015,21 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
1004 if (client->iso_context != NULL) { 1015 if (client->iso_context != NULL) {
1005 spin_unlock_irq(&client->lock); 1016 spin_unlock_irq(&client->lock);
1006 fw_iso_context_destroy(context); 1017 fw_iso_context_destroy(context);
1018
1007 return -EBUSY; 1019 return -EBUSY;
1008 } 1020 }
1021 if (!client->buffer_is_mapped) {
1022 ret = fw_iso_buffer_map_dma(&client->buffer,
1023 client->device->card,
1024 iso_dma_direction(context));
1025 if (ret < 0) {
1026 spin_unlock_irq(&client->lock);
1027 fw_iso_context_destroy(context);
1028
1029 return ret;
1030 }
1031 client->buffer_is_mapped = true;
1032 }
1009 client->iso_closure = a->closure; 1033 client->iso_closure = a->closure;
1010 client->iso_context = context; 1034 client->iso_context = context;
1011 spin_unlock_irq(&client->lock); 1035 spin_unlock_irq(&client->lock);
@@ -1651,7 +1675,6 @@ static long fw_device_op_compat_ioctl(struct file *file,
1651static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) 1675static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
1652{ 1676{
1653 struct client *client = file->private_data; 1677 struct client *client = file->private_data;
1654 enum dma_data_direction direction;
1655 unsigned long size; 1678 unsigned long size;
1656 int page_count, ret; 1679 int page_count, ret;
1657 1680
@@ -1674,20 +1697,28 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
1674 if (size & ~PAGE_MASK) 1697 if (size & ~PAGE_MASK)
1675 return -EINVAL; 1698 return -EINVAL;
1676 1699
1677 if (vma->vm_flags & VM_WRITE) 1700 ret = fw_iso_buffer_alloc(&client->buffer, page_count);
1678 direction = DMA_TO_DEVICE;
1679 else
1680 direction = DMA_FROM_DEVICE;
1681
1682 ret = fw_iso_buffer_init(&client->buffer, client->device->card,
1683 page_count, direction);
1684 if (ret < 0) 1701 if (ret < 0)
1685 return ret; 1702 return ret;
1686 1703
1687 ret = fw_iso_buffer_map(&client->buffer, vma); 1704 spin_lock_irq(&client->lock);
1705 if (client->iso_context) {
1706 ret = fw_iso_buffer_map_dma(&client->buffer,
1707 client->device->card,
1708 iso_dma_direction(client->iso_context));
1709 client->buffer_is_mapped = (ret == 0);
1710 }
1711 spin_unlock_irq(&client->lock);
1688 if (ret < 0) 1712 if (ret < 0)
1689 fw_iso_buffer_destroy(&client->buffer, client->device->card); 1713 goto fail;
1690 1714
1715 ret = fw_iso_buffer_map_vma(&client->buffer, vma);
1716 if (ret < 0)
1717 goto fail;
1718
1719 return 0;
1720 fail:
1721 fw_iso_buffer_destroy(&client->buffer, client->device->card);
1691 return ret; 1722 return ret;
1692} 1723}
1693 1724
diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c
index d1565828ae2c..8382e27e9a27 100644
--- a/drivers/firewire/core-iso.c
+++ b/drivers/firewire/core-iso.c
@@ -39,52 +39,73 @@
39 * Isochronous DMA context management 39 * Isochronous DMA context management
40 */ 40 */
41 41
42int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, 42int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count)
43 int page_count, enum dma_data_direction direction)
44{ 43{
45 int i, j; 44 int i;
46 dma_addr_t address;
47
48 buffer->page_count = page_count;
49 buffer->direction = direction;
50 45
46 buffer->page_count = 0;
47 buffer->page_count_mapped = 0;
51 buffer->pages = kmalloc(page_count * sizeof(buffer->pages[0]), 48 buffer->pages = kmalloc(page_count * sizeof(buffer->pages[0]),
52 GFP_KERNEL); 49 GFP_KERNEL);
53 if (buffer->pages == NULL) 50 if (buffer->pages == NULL)
54 goto out; 51 return -ENOMEM;
55 52
56 for (i = 0; i < buffer->page_count; i++) { 53 for (i = 0; i < page_count; i++) {
57 buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); 54 buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
58 if (buffer->pages[i] == NULL) 55 if (buffer->pages[i] == NULL)
59 goto out_pages; 56 break;
57 }
58 buffer->page_count = i;
59 if (i < page_count) {
60 fw_iso_buffer_destroy(buffer, NULL);
61 return -ENOMEM;
62 }
60 63
64 return 0;
65}
66
67int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card,
68 enum dma_data_direction direction)
69{
70 dma_addr_t address;
71 int i;
72
73 buffer->direction = direction;
74
75 for (i = 0; i < buffer->page_count; i++) {
61 address = dma_map_page(card->device, buffer->pages[i], 76 address = dma_map_page(card->device, buffer->pages[i],
62 0, PAGE_SIZE, direction); 77 0, PAGE_SIZE, direction);
63 if (dma_mapping_error(card->device, address)) { 78 if (dma_mapping_error(card->device, address))
64 __free_page(buffer->pages[i]); 79 break;
65 goto out_pages; 80
66 }
67 set_page_private(buffer->pages[i], address); 81 set_page_private(buffer->pages[i], address);
68 } 82 }
83 buffer->page_count_mapped = i;
84 if (i < buffer->page_count)
85 return -ENOMEM;
69 86
70 return 0; 87 return 0;
88}
71 89
72 out_pages: 90int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
73 for (j = 0; j < i; j++) { 91 int page_count, enum dma_data_direction direction)
74 address = page_private(buffer->pages[j]); 92{
75 dma_unmap_page(card->device, address, 93 int ret;
76 PAGE_SIZE, direction); 94
77 __free_page(buffer->pages[j]); 95 ret = fw_iso_buffer_alloc(buffer, page_count);
78 } 96 if (ret < 0)
79 kfree(buffer->pages); 97 return ret;
80 out: 98
81 buffer->pages = NULL; 99 ret = fw_iso_buffer_map_dma(buffer, card, direction);
100 if (ret < 0)
101 fw_iso_buffer_destroy(buffer, card);
82 102
83 return -ENOMEM; 103 return ret;
84} 104}
85EXPORT_SYMBOL(fw_iso_buffer_init); 105EXPORT_SYMBOL(fw_iso_buffer_init);
86 106
87int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma) 107int fw_iso_buffer_map_vma(struct fw_iso_buffer *buffer,
108 struct vm_area_struct *vma)
88{ 109{
89 unsigned long uaddr; 110 unsigned long uaddr;
90 int i, err; 111 int i, err;
@@ -107,15 +128,18 @@ void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer,
107 int i; 128 int i;
108 dma_addr_t address; 129 dma_addr_t address;
109 130
110 for (i = 0; i < buffer->page_count; i++) { 131 for (i = 0; i < buffer->page_count_mapped; i++) {
111 address = page_private(buffer->pages[i]); 132 address = page_private(buffer->pages[i]);
112 dma_unmap_page(card->device, address, 133 dma_unmap_page(card->device, address,
113 PAGE_SIZE, buffer->direction); 134 PAGE_SIZE, buffer->direction);
114 __free_page(buffer->pages[i]);
115 } 135 }
136 for (i = 0; i < buffer->page_count; i++)
137 __free_page(buffer->pages[i]);
116 138
117 kfree(buffer->pages); 139 kfree(buffer->pages);
118 buffer->pages = NULL; 140 buffer->pages = NULL;
141 buffer->page_count = 0;
142 buffer->page_count_mapped = 0;
119} 143}
120EXPORT_SYMBOL(fw_iso_buffer_destroy); 144EXPORT_SYMBOL(fw_iso_buffer_destroy);
121 145
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index 9047f5547d98..94257aecd054 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -3,6 +3,7 @@
3 3
4#include <linux/compiler.h> 4#include <linux/compiler.h>
5#include <linux/device.h> 5#include <linux/device.h>
6#include <linux/dma-mapping.h>
6#include <linux/fs.h> 7#include <linux/fs.h>
7#include <linux/list.h> 8#include <linux/list.h>
8#include <linux/idr.h> 9#include <linux/idr.h>
@@ -169,7 +170,11 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event);
169 170
170/* -iso */ 171/* -iso */
171 172
172int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma); 173int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count);
174int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card,
175 enum dma_data_direction direction);
176int fw_iso_buffer_map_vma(struct fw_iso_buffer *buffer,
177 struct vm_area_struct *vma);
173 178
174 179
175/* -topology */ 180/* -topology */