diff options
author | Magnus Damm <magnus.damm@gmail.com> | 2008-01-23 01:58:35 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-02-01 17:35:05 -0500 |
commit | b3476675320eda83cf061a686cdc80b76f2bfdc4 (patch) | |
tree | 11d31a0ade5c961fd5738a67b99ccb411325607d | |
parent | 0915f490d81c1a5098b399ec6c0be45bd421ee1d (diff) |
usb: dma bounce buffer support
usb: dma bounce buffer support V4
This patch adds dma bounce buffer support to the usb core. These buffers
can be enabled with the HCD_LOCAL_MEM flag, and they make sure that all data
passed to the host controller is allocated using dma_alloc_coherent().
Signed-off-by: Magnus Damm <damm@igel.co.jp>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/core/buffer.c | 9 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 176 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 1 |
3 files changed, 163 insertions, 23 deletions
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 28d4972f7ad5..acd9f00a0b76 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c | |||
@@ -53,7 +53,8 @@ int hcd_buffer_create(struct usb_hcd *hcd) | |||
53 | char name[16]; | 53 | char name[16]; |
54 | int i, size; | 54 | int i, size; |
55 | 55 | ||
56 | if (!hcd->self.controller->dma_mask) | 56 | if (!hcd->self.controller->dma_mask && |
57 | !(hcd->driver->flags & HCD_LOCAL_MEM)) | ||
57 | return 0; | 58 | return 0; |
58 | 59 | ||
59 | for (i = 0; i < HCD_BUFFER_POOLS; i++) { | 60 | for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
@@ -107,7 +108,8 @@ void *hcd_buffer_alloc( | |||
107 | int i; | 108 | int i; |
108 | 109 | ||
109 | /* some USB hosts just use PIO */ | 110 | /* some USB hosts just use PIO */ |
110 | if (!bus->controller->dma_mask) { | 111 | if (!bus->controller->dma_mask && |
112 | !(hcd->driver->flags & HCD_LOCAL_MEM)) { | ||
111 | *dma = ~(dma_addr_t) 0; | 113 | *dma = ~(dma_addr_t) 0; |
112 | return kmalloc(size, mem_flags); | 114 | return kmalloc(size, mem_flags); |
113 | } | 115 | } |
@@ -132,7 +134,8 @@ void hcd_buffer_free( | |||
132 | if (!addr) | 134 | if (!addr) |
133 | return; | 135 | return; |
134 | 136 | ||
135 | if (!bus->controller->dma_mask) { | 137 | if (!bus->controller->dma_mask && |
138 | !(hcd->driver->flags & HCD_LOCAL_MEM)) { | ||
136 | kfree(addr); | 139 | kfree(addr); |
137 | return; | 140 | return; |
138 | } | 141 | } |
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 372372116bb0..febd6b40da3b 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/mutex.h> | 35 | #include <linux/mutex.h> |
36 | #include <asm/irq.h> | 36 | #include <asm/irq.h> |
37 | #include <asm/byteorder.h> | 37 | #include <asm/byteorder.h> |
38 | #include <asm/unaligned.h> | ||
38 | #include <linux/platform_device.h> | 39 | #include <linux/platform_device.h> |
39 | #include <linux/workqueue.h> | 40 | #include <linux/workqueue.h> |
40 | 41 | ||
@@ -1112,48 +1113,177 @@ void usb_hcd_unlink_urb_from_ep(struct usb_hcd *hcd, struct urb *urb) | |||
1112 | } | 1113 | } |
1113 | EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep); | 1114 | EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep); |
1114 | 1115 | ||
1115 | static void map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) | 1116 | /* |
1117 | * Some usb host controllers can only perform dma using a small SRAM area. | ||
1118 | * The usb core itself is however optimized for host controllers that can dma | ||
1119 | * using regular system memory - like pci devices doing bus mastering. | ||
1120 | * | ||
1121 | * To support host controllers with limited dma capabilites we provide dma | ||
1122 | * bounce buffers. This feature can be enabled using the HCD_LOCAL_MEM flag. | ||
1123 | * For this to work properly the host controller code must first use the | ||
1124 | * function dma_declare_coherent_memory() to point out which memory area | ||
1125 | * that should be used for dma allocations. | ||
1126 | * | ||
1127 | * The HCD_LOCAL_MEM flag then tells the usb code to allocate all data for | ||
1128 | * dma using dma_alloc_coherent() which in turn allocates from the memory | ||
1129 | * area pointed out with dma_declare_coherent_memory(). | ||
1130 | * | ||
1131 | * So, to summarize... | ||
1132 | * | ||
1133 | * - We need "local" memory, canonical example being | ||
1134 | * a small SRAM on a discrete controller being the | ||
1135 | * only memory that the controller can read ... | ||
1136 | * (a) "normal" kernel memory is no good, and | ||
1137 | * (b) there's not enough to share | ||
1138 | * | ||
1139 | * - The only *portable* hook for such stuff in the | ||
1140 | * DMA framework is dma_declare_coherent_memory() | ||
1141 | * | ||
1142 | * - So we use that, even though the primary requirement | ||
1143 | * is that the memory be "local" (hence addressible | ||
1144 | * by that device), not "coherent". | ||
1145 | * | ||
1146 | */ | ||
1147 | |||
1148 | static int hcd_alloc_coherent(struct usb_bus *bus, | ||
1149 | gfp_t mem_flags, dma_addr_t *dma_handle, | ||
1150 | void **vaddr_handle, size_t size, | ||
1151 | enum dma_data_direction dir) | ||
1152 | { | ||
1153 | unsigned char *vaddr; | ||
1154 | |||
1155 | vaddr = hcd_buffer_alloc(bus, size + sizeof(vaddr), | ||
1156 | mem_flags, dma_handle); | ||
1157 | if (!vaddr) | ||
1158 | return -ENOMEM; | ||
1159 | |||
1160 | /* | ||
1161 | * Store the virtual address of the buffer at the end | ||
1162 | * of the allocated dma buffer. The size of the buffer | ||
1163 | * may be uneven so use unaligned functions instead | ||
1164 | * of just rounding up. It makes sense to optimize for | ||
1165 | * memory footprint over access speed since the amount | ||
1166 | * of memory available for dma may be limited. | ||
1167 | */ | ||
1168 | put_unaligned((unsigned long)*vaddr_handle, | ||
1169 | (unsigned long *)(vaddr + size)); | ||
1170 | |||
1171 | if (dir == DMA_TO_DEVICE) | ||
1172 | memcpy(vaddr, *vaddr_handle, size); | ||
1173 | |||
1174 | *vaddr_handle = vaddr; | ||
1175 | return 0; | ||
1176 | } | ||
1177 | |||
1178 | static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, | ||
1179 | void **vaddr_handle, size_t size, | ||
1180 | enum dma_data_direction dir) | ||
1181 | { | ||
1182 | unsigned char *vaddr = *vaddr_handle; | ||
1183 | |||
1184 | vaddr = (void *)get_unaligned((unsigned long *)(vaddr + size)); | ||
1185 | |||
1186 | if (dir == DMA_FROM_DEVICE) | ||
1187 | memcpy(vaddr, *vaddr_handle, size); | ||
1188 | |||
1189 | hcd_buffer_free(bus, size + sizeof(vaddr), *vaddr_handle, *dma_handle); | ||
1190 | |||
1191 | *vaddr_handle = vaddr; | ||
1192 | *dma_handle = 0; | ||
1193 | } | ||
1194 | |||
1195 | static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, | ||
1196 | gfp_t mem_flags) | ||
1116 | { | 1197 | { |
1198 | enum dma_data_direction dir; | ||
1199 | int ret = 0; | ||
1200 | |||
1117 | /* Map the URB's buffers for DMA access. | 1201 | /* Map the URB's buffers for DMA access. |
1118 | * Lower level HCD code should use *_dma exclusively, | 1202 | * Lower level HCD code should use *_dma exclusively, |
1119 | * unless it uses pio or talks to another transport. | 1203 | * unless it uses pio or talks to another transport. |
1120 | */ | 1204 | */ |
1121 | if (hcd->self.uses_dma && !is_root_hub(urb->dev)) { | 1205 | if (is_root_hub(urb->dev)) |
1122 | if (usb_endpoint_xfer_control(&urb->ep->desc) | 1206 | return 0; |
1123 | && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) | 1207 | |
1124 | urb->setup_dma = dma_map_single ( | 1208 | if (usb_endpoint_xfer_control(&urb->ep->desc) |
1209 | && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) { | ||
1210 | if (hcd->self.uses_dma) | ||
1211 | urb->setup_dma = dma_map_single( | ||
1125 | hcd->self.controller, | 1212 | hcd->self.controller, |
1126 | urb->setup_packet, | 1213 | urb->setup_packet, |
1127 | sizeof (struct usb_ctrlrequest), | 1214 | sizeof(struct usb_ctrlrequest), |
1128 | DMA_TO_DEVICE); | 1215 | DMA_TO_DEVICE); |
1129 | if (urb->transfer_buffer_length != 0 | 1216 | else if (hcd->driver->flags & HCD_LOCAL_MEM) |
1130 | && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) | 1217 | ret = hcd_alloc_coherent( |
1218 | urb->dev->bus, mem_flags, | ||
1219 | &urb->setup_dma, | ||
1220 | (void **)&urb->setup_packet, | ||
1221 | sizeof(struct usb_ctrlrequest), | ||
1222 | DMA_TO_DEVICE); | ||
1223 | } | ||
1224 | |||
1225 | dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; | ||
1226 | if (ret == 0 && urb->transfer_buffer_length != 0 | ||
1227 | && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { | ||
1228 | if (hcd->self.uses_dma) | ||
1131 | urb->transfer_dma = dma_map_single ( | 1229 | urb->transfer_dma = dma_map_single ( |
1132 | hcd->self.controller, | 1230 | hcd->self.controller, |
1133 | urb->transfer_buffer, | 1231 | urb->transfer_buffer, |
1134 | urb->transfer_buffer_length, | 1232 | urb->transfer_buffer_length, |
1135 | usb_urb_dir_in(urb) | 1233 | dir); |
1136 | ? DMA_FROM_DEVICE | 1234 | else if (hcd->driver->flags & HCD_LOCAL_MEM) { |
1137 | : DMA_TO_DEVICE); | 1235 | ret = hcd_alloc_coherent( |
1236 | urb->dev->bus, mem_flags, | ||
1237 | &urb->transfer_dma, | ||
1238 | &urb->transfer_buffer, | ||
1239 | urb->transfer_buffer_length, | ||
1240 | dir); | ||
1241 | |||
1242 | if (ret && usb_endpoint_xfer_control(&urb->ep->desc) | ||
1243 | && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) | ||
1244 | hcd_free_coherent(urb->dev->bus, | ||
1245 | &urb->setup_dma, | ||
1246 | (void **)&urb->setup_packet, | ||
1247 | sizeof(struct usb_ctrlrequest), | ||
1248 | DMA_TO_DEVICE); | ||
1249 | } | ||
1138 | } | 1250 | } |
1251 | return ret; | ||
1139 | } | 1252 | } |
1140 | 1253 | ||
1141 | static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) | 1254 | static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) |
1142 | { | 1255 | { |
1143 | if (hcd->self.uses_dma && !is_root_hub(urb->dev)) { | 1256 | enum dma_data_direction dir; |
1144 | if (usb_endpoint_xfer_control(&urb->ep->desc) | 1257 | |
1145 | && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) | 1258 | if (is_root_hub(urb->dev)) |
1259 | return; | ||
1260 | |||
1261 | if (usb_endpoint_xfer_control(&urb->ep->desc) | ||
1262 | && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) { | ||
1263 | if (hcd->self.uses_dma) | ||
1146 | dma_unmap_single(hcd->self.controller, urb->setup_dma, | 1264 | dma_unmap_single(hcd->self.controller, urb->setup_dma, |
1147 | sizeof(struct usb_ctrlrequest), | 1265 | sizeof(struct usb_ctrlrequest), |
1148 | DMA_TO_DEVICE); | 1266 | DMA_TO_DEVICE); |
1149 | if (urb->transfer_buffer_length != 0 | 1267 | else if (hcd->driver->flags & HCD_LOCAL_MEM) |
1150 | && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) | 1268 | hcd_free_coherent(urb->dev->bus, &urb->setup_dma, |
1269 | (void **)&urb->setup_packet, | ||
1270 | sizeof(struct usb_ctrlrequest), | ||
1271 | DMA_TO_DEVICE); | ||
1272 | } | ||
1273 | |||
1274 | dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; | ||
1275 | if (urb->transfer_buffer_length != 0 | ||
1276 | && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { | ||
1277 | if (hcd->self.uses_dma) | ||
1151 | dma_unmap_single(hcd->self.controller, | 1278 | dma_unmap_single(hcd->self.controller, |
1152 | urb->transfer_dma, | 1279 | urb->transfer_dma, |
1153 | urb->transfer_buffer_length, | 1280 | urb->transfer_buffer_length, |
1154 | usb_urb_dir_in(urb) | 1281 | dir); |
1155 | ? DMA_FROM_DEVICE | 1282 | else if (hcd->driver->flags & HCD_LOCAL_MEM) |
1156 | : DMA_TO_DEVICE); | 1283 | hcd_free_coherent(urb->dev->bus, &urb->transfer_dma, |
1284 | &urb->transfer_buffer, | ||
1285 | urb->transfer_buffer_length, | ||
1286 | dir); | ||
1157 | } | 1287 | } |
1158 | } | 1288 | } |
1159 | 1289 | ||
@@ -1185,7 +1315,12 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) | |||
1185 | * URBs must be submitted in process context with interrupts | 1315 | * URBs must be submitted in process context with interrupts |
1186 | * enabled. | 1316 | * enabled. |
1187 | */ | 1317 | */ |
1188 | map_urb_for_dma(hcd, urb); | 1318 | status = map_urb_for_dma(hcd, urb, mem_flags); |
1319 | if (unlikely(status)) { | ||
1320 | usbmon_urb_submit_error(&hcd->self, urb, status); | ||
1321 | goto error; | ||
1322 | } | ||
1323 | |||
1189 | if (is_root_hub(urb->dev)) | 1324 | if (is_root_hub(urb->dev)) |
1190 | status = rh_urb_enqueue(hcd, urb); | 1325 | status = rh_urb_enqueue(hcd, urb); |
1191 | else | 1326 | else |
@@ -1194,6 +1329,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) | |||
1194 | if (unlikely(status)) { | 1329 | if (unlikely(status)) { |
1195 | usbmon_urb_submit_error(&hcd->self, urb, status); | 1330 | usbmon_urb_submit_error(&hcd->self, urb, status); |
1196 | unmap_urb_for_dma(hcd, urb); | 1331 | unmap_urb_for_dma(hcd, urb); |
1332 | error: | ||
1197 | urb->hcpriv = NULL; | 1333 | urb->hcpriv = NULL; |
1198 | INIT_LIST_HEAD(&urb->urb_list); | 1334 | INIT_LIST_HEAD(&urb->urb_list); |
1199 | atomic_dec(&urb->use_count); | 1335 | atomic_dec(&urb->use_count); |
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 080298ad5990..0095641c51fa 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h | |||
@@ -165,6 +165,7 @@ struct hc_driver { | |||
165 | 165 | ||
166 | int flags; | 166 | int flags; |
167 | #define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */ | 167 | #define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */ |
168 | #define HCD_LOCAL_MEM 0x0002 /* HC needs local memory */ | ||
168 | #define HCD_USB11 0x0010 /* USB 1.1 */ | 169 | #define HCD_USB11 0x0010 /* USB 1.1 */ |
169 | #define HCD_USB2 0x0020 /* USB 2.0 */ | 170 | #define HCD_USB2 0x0020 /* USB 2.0 */ |
170 | 171 | ||