| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <ddk/protocol/usb.h> |
| #include <ddk/usb-request.h> |
| #include <zircon/syscalls.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #define MIN(a, b) ((a) < (b) ? (a) : (b)) |
| |
| // Frees any resources allocated by the usb request, but not the usb request itself. |
| static void usb_request_release_static(usb_request_t* req) { |
| io_buffer_release(&req->buffer); |
| } |
| |
| // Frees any resources allocated by the usb request, as well as the usb request itself. |
| static void usb_request_release_free(usb_request_t* req) { |
| usb_request_release_static(req); |
| free(req); |
| } |
| |
| zx_status_t usb_request_alloc(usb_request_t** out, zx_handle_t bti_handle, uint64_t data_size, |
| uint8_t ep_address) { |
| usb_request_t* req = calloc(1, sizeof(usb_request_t)); |
| if (!req) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| if (data_size > 0) { |
| zx_status_t status = io_buffer_init(&req->buffer, bti_handle, data_size, IO_BUFFER_RW); |
| if (status != ZX_OK) { |
| free(req); |
| return status; |
| } |
| } |
| req->header.ep_address = ep_address; |
| req->header.length = data_size; |
| req->release_cb = usb_request_release_free; |
| *out = req; |
| return ZX_OK; |
| } |
| |
| // usb_request_alloc_vmo() creates a new usb request with the given VMO. |
| zx_status_t usb_request_alloc_vmo(usb_request_t** out, zx_handle_t bti_handle, |
| zx_handle_t vmo_handle, uint64_t vmo_offset, uint64_t length, |
| uint8_t ep_address) { |
| usb_request_t* req = calloc(1, sizeof(usb_request_t)); |
| if (!req) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| zx_status_t status = io_buffer_init_vmo(&req->buffer, bti_handle, vmo_handle, vmo_offset, |
| IO_BUFFER_RW); |
| if (status != ZX_OK) { |
| free(req); |
| return status; |
| } |
| req->header.ep_address = ep_address; |
| req->header.length = length; |
| req->release_cb = usb_request_release_free; |
| *out = req; |
| return ZX_OK; |
| } |
| |
| // usb_request_init() initializes the statically allocated usb request with the given VMO. |
| // This will free any resources allocated by the usb request but not the usb request itself. |
| zx_status_t usb_request_init(usb_request_t* req, zx_handle_t bti_handle, zx_handle_t vmo_handle, |
| uint64_t vmo_offset, uint64_t length, uint8_t ep_address) { |
| memset(req, 0, sizeof(*req)); |
| |
| zx_status_t status = io_buffer_init_vmo(&req->buffer, bti_handle, vmo_handle, vmo_offset, |
| IO_BUFFER_RW); |
| if (status != ZX_OK) { |
| return status; |
| } |
| req->header.ep_address = ep_address; |
| req->header.length = length; |
| req->release_cb = usb_request_release_static; |
| return ZX_OK; |
| } |
| |
| ssize_t usb_request_copyfrom(usb_request_t* req, void* data, size_t length, size_t offset) { |
| length = MIN(io_buffer_size(&req->buffer, offset), length); |
| memcpy(data, io_buffer_virt(&req->buffer) + offset, length); |
| return length; |
| } |
| |
| ssize_t usb_request_copyto(usb_request_t* req, const void* data, size_t length, size_t offset) { |
| length = MIN(io_buffer_size(&req->buffer, offset), length); |
| memcpy(io_buffer_virt(&req->buffer) + offset, data, length); |
| return length; |
| } |
| |
| zx_status_t usb_request_mmap(usb_request_t* req, void** data) { |
| *data = io_buffer_virt(&req->buffer); |
| // TODO(jocelyndang): modify this once we start passing usb requests across process boundaries. |
| return ZX_OK; |
| } |
| |
| zx_status_t usb_request_cacheop(usb_request_t* req, uint32_t op, size_t offset, size_t length) { |
| return io_buffer_cache_op(&req->buffer, op, offset, length); |
| } |
| |
| zx_status_t usb_request_cache_flush(usb_request_t* req, zx_off_t offset, size_t length) { |
| return io_buffer_cache_flush(&req->buffer, offset, length); |
| } |
| |
| zx_status_t usb_request_cache_flush_invalidate(usb_request_t* req, zx_off_t offset, size_t length) { |
| return io_buffer_cache_flush_invalidate(&req->buffer, offset, length); |
| } |
| |
| zx_status_t usb_request_physmap(usb_request_t* req) { |
| return io_buffer_physmap(&req->buffer); |
| } |
| |
| void usb_request_release(usb_request_t* req) { |
| if (req->release_cb) { |
| req->release_cb(req); |
| } |
| } |
| |
| void usb_request_complete(usb_request_t* req, zx_status_t status, zx_off_t actual) { |
| req->response.status = status; |
| req->response.actual = actual; |
| |
| if (req->complete_cb) { |
| req->complete_cb(req, req->cookie); |
| } |
| } |
| |
| void usb_request_phys_iter_init(phys_iter_t* iter, usb_request_t* req, size_t max_length) { |
| phys_iter_buffer_t buf = { |
| .length = req->header.length, |
| .vmo_offset = req->buffer.offset, |
| .phys = req->buffer.phys_list, |
| .phys_count = req->buffer.phys_count |
| }; |
| phys_iter_init(iter, &buf, max_length); |
| } |
| |
| size_t usb_request_phys_iter_next(phys_iter_t* iter, zx_paddr_t* out_paddr) { |
| return phys_iter_next(iter, out_paddr); |
| } |
| |
| void usb_request_pool_init(usb_request_pool_t* pool) { |
| mtx_init(&pool->lock, mtx_plain); |
| list_initialize(&pool->free_reqs); |
| } |
| |
| void usb_request_pool_add(usb_request_pool_t* pool, usb_request_t* req) { |
| mtx_lock(&pool->lock); |
| |
| list_add_tail(&pool->free_reqs, &req->node); |
| |
| mtx_unlock(&pool->lock); |
| } |
| |
| usb_request_t* usb_request_pool_get(usb_request_pool_t* pool, size_t length) { |
| usb_request_t* req = NULL; |
| bool found = false; |
| |
| mtx_lock(&pool->lock); |
| list_for_every_entry (&pool->free_reqs, req, usb_request_t, node) { |
| if (req->buffer.size == length) { |
| found = true; |
| break; |
| } |
| } |
| if (found) { |
| list_delete(&req->node); |
| } |
| mtx_unlock(&pool->lock); |
| |
| return found ? req : NULL; |
| } |
| |
| void usb_request_pool_release(usb_request_pool_t* pool) { |
| mtx_lock(&pool->lock); |
| |
| usb_request_t* req; |
| while ((req = list_remove_tail_type(&pool->free_reqs, usb_request_t, node)) != NULL) { |
| usb_request_release(req); |
| } |
| |
| mtx_unlock(&pool->lock); |
| } |