Changeset 1d758fc in mainline for uspace/lib/usbhost
- Timestamp:
- 2018-02-12T10:11:47Z (7 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 5fe3f954
- Parents:
- 2f762a7
- git-author:
- Ondřej Hlavatý <aearsis@…> (2018-02-05 03:28:50)
- git-committer:
- Ondřej Hlavatý <aearsis@…> (2018-02-12 10:11:47)
- Location:
- uspace/lib/usbhost
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/usbhost/include/usb/host/bus.h
r2f762a7 r1d758fc 153 153 int bus_device_offline(device_t *); 154 154 155 int bus_device_send_batch(device_t *, usb_target_t, 156 usb_direction_t direction, char *, size_t, uint64_t, 157 usbhc_iface_transfer_callback_t, void *, const char *); 155 /** 156 * A proforma to USB transfer batch. As opposed to transfer batch, which is 157 * supposed to be a dynamic structrure, this one is static and descriptive only. 158 * Its fields are copied to the final batch. 159 */ 160 typedef struct transfer_request { 161 usb_target_t target; 162 usb_direction_t dir; 163 164 dma_buffer_t buffer; 165 size_t offset, size; 166 uint64_t setup; 167 168 usbhc_iface_transfer_callback_t on_complete; 169 void *arg; 170 171 const char *name; 172 } transfer_request_t; 173 174 int bus_issue_transfer(device_t *, const transfer_request_t *); 158 175 159 176 errno_t bus_device_send_batch_sync(device_t *, usb_target_t, -
uspace/lib/usbhost/include/usb/host/endpoint.h
r2f762a7 r1d758fc 51 51 typedef struct bus bus_t; 52 52 typedef struct device device_t; 53 typedef struct transfer_request transfer_request_t; 53 54 typedef struct usb_transfer_batch usb_transfer_batch_t; 54 55 … … 98 99 /** Maximum size of one transfer */ 99 100 size_t max_transfer_size; 100 /** Policy for transfer buffers */ 101 dma_policy_t transfer_buffer_policy; 101 102 /* Policies for transfer buffers */ 103 dma_policy_t transfer_buffer_policy; /**< A hint for optimal performance. */ 104 dma_policy_t required_transfer_buffer_policy; /**< Enforced by the library. */ 102 105 103 106 /** … … 122 125 extern void endpoint_deactivate_locked(endpoint_t *); 123 126 124 int endpoint_send_batch(endpoint_t *, usb_target_t, usb_direction_t, 125 char *, size_t, uint64_t, usbhc_iface_transfer_callback_t, void *, 126 const char *); 127 int endpoint_send_batch(endpoint_t *, const transfer_request_t *); 127 128 128 129 static inline bus_t *endpoint_get_bus(endpoint_t *ep) -
uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h
r2f762a7 r1d758fc 70 70 } setup; 71 71 72 /** DMA buffer with enforced policy */ 73 dma_buffer_t dma_buffer; 74 /** Size of memory buffer */ 75 size_t offset, size; 76 72 77 /** 73 78 * In case a bounce buffer is allocated, the original buffer must to be 74 79 * stored to be filled after the IN transaction is finished. 75 80 */ 76 char *buffer; 77 /** Size of memory buffer */ 78 size_t buffer_size; 79 80 /** DMA buffer with enforced policy */ 81 dma_buffer_t dma_buffer; 81 char *original_buffer; 82 82 bool is_bounced; 83 83 … … 107 107 usb_str_transfer_type_short((batch).ep->transfer_type), \ 108 108 usb_str_direction((batch).dir), \ 109 (batch). buffer_size, (batch).ep->max_packet_size109 (batch).size, (batch).ep->max_packet_size 110 110 111 111 /** Wrapper for bus operation. */ … … 115 115 void usb_transfer_batch_init(usb_transfer_batch_t *, endpoint_t *); 116 116 117 /** Buffer handling */ 118 bool usb_transfer_batch_bounce_required(usb_transfer_batch_t *); 117 119 errno_t usb_transfer_batch_bounce(usb_transfer_batch_t *); 118 /** Buffer preparation */119 errno_t usb_transfer_batch_prepare_buffer(usb_transfer_batch_t *, char *);120 120 121 121 /** Batch finalization. */ -
uspace/lib/usbhost/src/bus.c
r2f762a7 r1d758fc 46 46 #include <str_error.h> 47 47 #include <usb/debug.h> 48 #include <usb/dma_buffer.h> 48 49 49 50 #include "endpoint.h" … … 389 390 endpoint_init(ep, device, desc); 390 391 } 392 393 assert((ep->required_transfer_buffer_policy & ~ep->transfer_buffer_policy) == 0); 391 394 392 395 /* Bus reference */ … … 557 560 558 561 /** 559 * Initiate a transfer on the bus. Finds the target endpoint, creates 560 * a transfer batch and schedules it. 561 * 562 * @param device Device for which to send the batch 563 * @param target The target of the transfer. 564 * @param direction A direction of the transfer. 565 * @param data A pointer to the data buffer. 566 * @param size Size of the data buffer. 567 * @param setup_data Data to use in the setup stage (Control communication type) 568 * @param on_complete Callback which is called after the batch is complete 569 * @param arg Callback parameter. 570 * @param name Communication identifier (for nicer output). 562 * Assert some conditions on transfer request. As the request is an entity of 563 * HC driver only, we can force these conditions harder. Invalid values from 564 * devices shall be caught on DDF interface already. 565 */ 566 static void check_request(const transfer_request_t *request) 567 { 568 assert(usb_target_is_valid(&request->target)); 569 assert(request->dir != USB_DIRECTION_BOTH); 570 /* Non-zero offset => size is non-zero */ 571 assert(request->offset == 0 || request->size != 0); 572 /* Non-zero size => buffer is set */ 573 assert(request->size == 0 || dma_buffer_is_set(&request->buffer)); 574 /* Non-null arg => callback is set */ 575 assert(request->arg == NULL || request->on_complete != NULL); 576 assert(request->name); 577 } 578 579 /** 580 * Initiate a transfer with given device. 581 * 571 582 * @return Error code. 572 583 */ 573 int bus_device_send_batch(device_t *device, usb_target_t target, 574 usb_direction_t direction, char *data, size_t size, uint64_t setup_data, 575 usbhc_iface_transfer_callback_t on_complete, void *arg, const char *name) 576 { 577 assert(device->address == target.address); 584 int bus_issue_transfer(device_t *device, const transfer_request_t *request) 585 { 586 assert(device); 587 assert(request); 588 589 check_request(request); 590 assert(device->address == request->target.address); 578 591 579 592 /* Temporary reference */ 580 endpoint_t *ep = bus_find_endpoint(device, target.endpoint, direction);593 endpoint_t *ep = bus_find_endpoint(device, request->target.endpoint, request->dir); 581 594 if (ep == NULL) { 582 595 usb_log_error("Endpoint(%d:%d) not registered for %s.", 583 device->address, target.endpoint,name);596 device->address, request->target.endpoint, request->name); 584 597 return ENOENT; 585 598 } … … 587 600 assert(ep->device == device); 588 601 589 /* 590 * This method is already callable from HC only, so we can force these 591 * conditions harder. 592 * Invalid values from devices shall be caught on DDF interface already. 593 */ 594 assert(usb_target_is_valid(&target)); 595 assert(direction != USB_DIRECTION_BOTH); 596 assert(size == 0 || data != NULL); 597 assert(arg == NULL || on_complete != NULL); 598 assert(name); 599 600 const int err = endpoint_send_batch(ep, target, direction, 601 data, size, setup_data, on_complete, arg, name); 602 const int err = endpoint_send_batch(ep, request); 602 603 603 604 /* Temporary reference */ … … 650 651 const char *name, size_t *transferred_size) 651 652 { 653 int err; 652 654 sync_data_t sd = { .done = false }; 653 655 fibril_mutex_initialize(&sd.done_mtx); 654 656 fibril_condvar_initialize(&sd.done_cv); 655 657 656 const int ret = bus_device_send_batch(device, target, direction, 657 data, size, setup_data, sync_transfer_complete, &sd, name); 658 if (ret != EOK) 659 return ret; 658 transfer_request_t request = { 659 .target = target, 660 .dir = direction, 661 .offset = ((uintptr_t) data) % PAGE_SIZE, 662 .size = size, 663 .setup = setup_data, 664 .on_complete = sync_transfer_complete, 665 .arg = &sd, 666 .name = name, 667 }; 668 669 if (data && 670 (err = dma_buffer_lock(&request.buffer, data - request.offset, size))) 671 return err; 672 673 if ((err = bus_issue_transfer(device, &request))) { 674 dma_buffer_unlock(&request.buffer, size); 675 return err; 676 } 660 677 661 678 /* … … 668 685 fibril_mutex_unlock(&sd.done_mtx); 669 686 687 dma_buffer_unlock(&request.buffer, size); 688 670 689 if (transferred_size) 671 690 *transferred_size = sd.transferred_size; -
uspace/lib/usbhost/src/ddf_helpers.c
r2f762a7 r1d758fc 46 46 #include <usb/descriptor.h> 47 47 #include <usb/usb.h> 48 #include <usb/dma_buffer.h> 48 49 #include <usb_iface.h> 49 50 #include <usbhc_iface.h> … … 271 272 * @return Error code. 272 273 */ 273 static errno_t transfer(ddf_fun_t *fun, const usbhc_iface_transfer_request_t *req, 274 static errno_t transfer(ddf_fun_t *fun, 275 const usbhc_iface_transfer_request_t *ifreq, 274 276 usbhc_iface_transfer_callback_t callback, void *arg) 275 277 { … … 280 282 const usb_target_t target = {{ 281 283 .address = dev->address, 282 .endpoint = req->endpoint,283 .stream = req->stream,284 .endpoint = ifreq->endpoint, 285 .stream = ifreq->stream, 284 286 }}; 285 287 … … 287 289 return EINVAL; 288 290 289 if (req->size > 0 && req->base == NULL) 291 if (ifreq->offset > 0 && ifreq->size == 0) 292 return EINVAL; 293 294 if (ifreq->size > 0 && !dma_buffer_is_set(&ifreq->buffer)) 290 295 return EBADMEM; 291 296 … … 293 298 return EBADMEM; 294 299 295 const char *name = (req->dir == USB_DIRECTION_IN) ? "READ" : "WRITE"; 296 297 char *buffer = req->base + req->offset; 298 299 return bus_device_send_batch(dev, target, req->dir, 300 buffer, req->size, req->setup, 301 callback, arg, name); 300 const transfer_request_t request = { 301 .target = target, 302 .dir = ifreq->dir, 303 .buffer = ifreq->buffer, 304 .offset = ifreq->offset, 305 .size = ifreq->size, 306 .setup = ifreq->setup, 307 .on_complete = callback, 308 .arg = arg, 309 .name = (ifreq->dir == USB_DIRECTION_IN) ? "READ" : "WRITE", 310 }; 311 312 return bus_issue_transfer(dev, &request); 302 313 } 303 314 -
uspace/lib/usbhost/src/endpoint.c
r2f762a7 r1d758fc 75 75 ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe; 76 76 ep->transfer_buffer_policy = DMA_POLICY_STRICT; 77 ep->required_transfer_buffer_policy = DMA_POLICY_STRICT; 77 78 } 78 79 … … 207 208 * 208 209 * @param endpoint Endpoint for which to send the batch 209 * @param target The target of the transfer. 210 * @param direction A direction of the transfer. 211 * @param data A pointer to the data buffer. 212 * @param size Size of the data buffer. 213 * @param setup_data Data to use in the setup stage (Control communication type) 214 * @param on_complete Callback which is called after the batch is complete 215 * @param arg Callback parameter. 216 * @param name Communication identifier (for nicer output). 217 */ 218 errno_t endpoint_send_batch(endpoint_t *ep, usb_target_t target, 219 usb_direction_t direction, char *data, size_t size, uint64_t setup_data, 220 usbhc_iface_transfer_callback_t on_complete, void *arg, const char *name) 221 { 222 if (!ep) 223 return EBADMEM; 210 */ 211 errno_t endpoint_send_batch(endpoint_t *ep, const transfer_request_t *req) 212 { 213 assert(ep); 214 assert(req); 224 215 225 216 if (ep->transfer_type == USB_TRANSFER_CONTROL) { 226 usb_log_debug("%s %d:%d %zu/%zuB, setup %#016" PRIx64, name, 227 target.address, target.endpoint, size, ep->max_packet_size, 228 setup_data); 217 usb_log_debug("%s %d:%d %zu/%zuB, setup %#016" PRIx64, req->name, 218 req->target.address, req->target.endpoint, 219 req->size, ep->max_packet_size, 220 req->setup); 229 221 } else { 230 usb_log_debug("%s %d:%d %zu/%zuB", name, target.address, 231 target.endpoint, size, ep->max_packet_size); 222 usb_log_debug("%s %d:%d %zu/%zuB", req->name, 223 req->target.address, req->target.endpoint, 224 req->size, ep->max_packet_size); 232 225 } 233 226 … … 244 237 } 245 238 239 size_t size = req->size; 246 240 /* 247 241 * Limit transfers with reserved bandwidth to the amount reserved. 248 242 * OUT transfers are rejected, IN can be just trimmed in advance. 249 243 */ 250 if ((ep->transfer_type == USB_TRANSFER_INTERRUPT || ep->transfer_type == USB_TRANSFER_ISOCHRONOUS) && size > ep->max_transfer_size) { 251 if (direction == USB_DIRECTION_OUT) 244 if (size > ep->max_transfer_size && 245 (ep->transfer_type == USB_TRANSFER_INTERRUPT 246 || ep->transfer_type == USB_TRANSFER_ISOCHRONOUS)) { 247 if (req->dir == USB_DIRECTION_OUT) 252 248 return ENOSPC; 253 249 else 254 250 size = ep->max_transfer_size; 255 256 251 } 257 252 … … 266 261 } 267 262 268 batch->target = target; 269 batch->setup.packed = setup_data; 270 batch->dir = direction; 271 batch->buffer_size = size; 272 273 errno_t err; 274 if ((err = usb_transfer_batch_prepare_buffer(batch, data))) { 275 usb_log_warning("Failed to prepare buffer for batch: %s", str_error(err)); 276 usb_transfer_batch_destroy(batch); 277 return err; 278 } 279 280 batch->on_complete = on_complete; 281 batch->on_complete_data = arg; 263 batch->target = req->target; 264 batch->setup.packed = req->setup; 265 batch->dir = req->dir; 266 batch->size = size; 267 batch->offset = req->offset; 268 batch->dma_buffer = req->buffer; 269 270 dma_buffer_acquire(&batch->dma_buffer); 271 272 if (batch->offset != 0) { 273 usb_log_debug("A transfer with nonzero offset requested."); 274 usb_transfer_batch_bounce(batch); 275 } 276 277 if (usb_transfer_batch_bounce_required(batch)) 278 usb_transfer_batch_bounce(batch); 279 280 batch->on_complete = req->on_complete; 281 batch->on_complete_data = req->arg; 282 282 283 283 const int ret = ops->batch_schedule(batch); -
uspace/lib/usbhost/src/usb_transfer_batch.c
r2f762a7 r1d758fc 103 103 } 104 104 105 bool usb_transfer_batch_bounce_required(usb_transfer_batch_t *batch) 106 { 107 if (!batch->size) 108 return false; 109 110 unsigned flags = batch->dma_buffer.policy & DMA_POLICY_FLAGS_MASK; 111 unsigned required_flags = 112 batch->ep->required_transfer_buffer_policy & DMA_POLICY_FLAGS_MASK; 113 114 if (required_flags & ~flags) 115 return true; 116 117 size_t chunk_mask = dma_policy_chunk_mask(batch->dma_buffer.policy); 118 size_t required_chunk_mask = 119 dma_policy_chunk_mask(batch->ep->required_transfer_buffer_policy); 120 121 /* If the chunks are at least as large as required, we're good */ 122 if ((required_chunk_mask & ~chunk_mask) == 0) 123 return false; 124 125 size_t start_chunk = batch->offset & ~chunk_mask; 126 size_t end_chunk = (batch->offset + batch->size - 1) & ~chunk_mask; 127 128 /* The requested area crosses a chunk boundary */ 129 if (start_chunk != end_chunk) 130 return true; 131 132 return false; 133 } 134 105 135 errno_t usb_transfer_batch_bounce(usb_transfer_batch_t *batch) 106 136 { … … 108 138 assert(!batch->is_bounced); 109 139 110 if (dma_buffer_is_set(&batch->dma_buffer)) 111 dma_buffer_unlock(&batch->dma_buffer, batch->buffer_size); 140 dma_buffer_release(&batch->dma_buffer); 141 142 batch->original_buffer = batch->dma_buffer.virt + batch->offset; 112 143 113 144 usb_log_debug("Batch(%p): Buffer cannot be used directly, " … … 115 146 116 147 const errno_t err = dma_buffer_alloc_policy(&batch->dma_buffer, 117 batch-> buffer_size, batch->ep->transfer_buffer_policy);148 batch->size, batch->ep->transfer_buffer_policy); 118 149 if (err) 119 150 return err; … … 121 152 /* Copy the data out */ 122 153 if (batch->dir == USB_DIRECTION_OUT) 123 memcpy(batch->dma_buffer.virt, batch->buffer, batch->buffer_size); 154 memcpy(batch->dma_buffer.virt, 155 batch->original_buffer, 156 batch->size); 124 157 125 158 batch->is_bounced = true; 159 batch->offset = 0; 160 126 161 return err; 127 }128 129 /**130 * Prepare a DMA buffer according to endpoint policy.131 *132 * If the buffer is suitable to be used directly, it is. Otherwise, a bounce133 * buffer is created.134 */135 errno_t usb_transfer_batch_prepare_buffer(usb_transfer_batch_t *batch, char *buf)136 {137 /* Empty transfers do not need a buffer */138 if (batch->buffer_size == 0)139 return EOK;140 141 batch->buffer = buf;142 143 const dma_policy_t policy = batch->ep->transfer_buffer_policy;144 145 /*146 * We don't have enough information (yet, WIP) to know if we can skip147 * the bounce, so check the conditions carefully.148 */149 if (!dma_buffer_check_policy(buf, batch->buffer_size, policy))150 return usb_transfer_batch_bounce(batch);151 152 /* Fill the buffer with virtual address and lock it for DMA */153 return dma_buffer_lock(&batch->dma_buffer, buf, batch->buffer_size);154 162 } 155 163 … … 167 175 batch, USB_TRANSFER_BATCH_ARGS(*batch)); 168 176 169 if (batch->error == EOK && batch->buffer_size > 0) { 170 if (!batch->is_bounced) { 171 /* Unlock the buffer for DMA */ 172 dma_buffer_unlock(&batch->dma_buffer, 173 batch->buffer_size); 177 if (batch->error == EOK && batch->size > 0) { 178 if (batch->is_bounced) { 179 /* We we're forced to use bounce buffer, copy it back */ 180 if (batch->dir == USB_DIRECTION_IN) 181 memcpy(batch->original_buffer, 182 batch->dma_buffer.virt, 183 batch->transferred_size); 184 185 dma_buffer_free(&batch->dma_buffer); 174 186 } 175 187 else { 176 /* We we're forced to use bounce buffer, copy it back */ 177 if (batch->dir == USB_DIRECTION_IN) 178 memcpy(batch->buffer, 179 batch->dma_buffer.virt, 180 batch->transferred_size); 181 182 dma_buffer_free(&batch->dma_buffer); 188 dma_buffer_release(&batch->dma_buffer); 183 189 } 184 190 }
Note:
See TracChangeset
for help on using the changeset viewer.