/* * Copyright (c) 2018-2019, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "USBDevice.h" #include "USBDescriptor.h" #include "usb_phy_api.h" #include "mbed_assert.h" //#define DEBUG /* Device status */ #define DEVICE_STATUS_SELF_POWERED (1U<<0) #define DEVICE_STATUS_REMOTE_WAKEUP (1U<<1) /* Endpoint status */ #define ENDPOINT_STATUS_HALT (1U<<0) /* Standard feature selectors */ #define DEVICE_REMOTE_WAKEUP (1) #define ENDPOINT_HALT (0) /* Endpoint macros */ #define EP_INDEXABLE(endpoint) (EP_VALID(endpoint) && !EP_CONTROL(endpoint)) #define EP_TO_INDEX(endpoint) ((((endpoint & 0xf) << 1) | (endpoint & 0x80 ? 1 : 0)) - 2) #define INDEX_TO_EP(index) ((usb_ep_t)((((index) >> 1) | (index & 1 ? 0x80 : 0)) + 1)) #define EP_VALID(endpoint) (((endpoint) & ~0x8F) == 0) #define EP_CONTROL(endpoint) (((endpoint) & 0xF) == 0) #define EP_RX(endpoint) ((endpoint) & 0x80) /* Other defines */ #define ENDPOINT_ENABLED (1 << 0) #define ENDPOINT_STALLED (1 << 1) /* The maximum wMaxPacketSize for endpoint 0 */ #if defined(MAX_PACKET_SIZE_EP0) #undef MAX_PACKET_SIZE_EP0 #endif #define MAX_PACKET_SIZE_EP0 64 #define USB_MIN(a, b) ((a) > (b) ? (b) : (a)) bool USBDevice::_request_get_descriptor() { assert_locked(); bool success = false; #ifdef DEBUG printf("get descr: type: %d\r\n", DESCRIPTOR_TYPE(_transfer.setup.wValue)); #endif switch (DESCRIPTOR_TYPE(_transfer.setup.wValue)) { case DEVICE_DESCRIPTOR: { if (device_desc() != NULL) { if ((device_desc()[0] == DEVICE_DESCRIPTOR_LENGTH) \ && (device_desc()[1] == DEVICE_DESCRIPTOR)) { #ifdef DEBUG printf("device descr\r\n"); #endif _transfer.remaining = DEVICE_DESCRIPTOR_LENGTH; _transfer.ptr = (uint8_t *)device_desc(); _transfer.direction = Send; success = true; } } break; } case CONFIGURATION_DESCRIPTOR: { const uint8_t idx = DESCRIPTOR_INDEX(_transfer.setup.wValue); if (configuration_desc(idx) != NULL) { if ((configuration_desc(idx)[0] == CONFIGURATION_DESCRIPTOR_LENGTH) \ && (configuration_desc(idx)[1] == CONFIGURATION_DESCRIPTOR)) { #ifdef DEBUG printf("conf descr request\r\n"); #endif /* Get wTotalLength */ _transfer.remaining = configuration_desc(idx)[2] \ | (configuration_desc(idx)[3] << 8); _transfer.ptr = (uint8_t *)configuration_desc(idx); _transfer.direction = Send; success = true; } } break; } case STRING_DESCRIPTOR: { #ifdef DEBUG printf("str descriptor\r\n"); #endif switch (DESCRIPTOR_INDEX(_transfer.setup.wValue)) { case STRING_OFFSET_LANGID: #ifdef DEBUG printf("1\r\n"); #endif _transfer.remaining = string_langid_desc()[0]; _transfer.ptr = (uint8_t *)string_langid_desc(); _transfer.direction = Send; success = true; break; case STRING_OFFSET_IMANUFACTURER: #ifdef DEBUG printf("2\r\n"); #endif _transfer.remaining = string_imanufacturer_desc()[0]; _transfer.ptr = (uint8_t *)string_imanufacturer_desc(); _transfer.direction = Send; success = true; break; case STRING_OFFSET_IPRODUCT: #ifdef DEBUG printf("3\r\n"); #endif _transfer.remaining = string_iproduct_desc()[0]; _transfer.ptr = (uint8_t *)string_iproduct_desc(); _transfer.direction = Send; success = true; break; case STRING_OFFSET_ISERIAL: #ifdef DEBUG printf("4\r\n"); #endif _transfer.remaining = string_iserial_desc()[0]; _transfer.ptr = (uint8_t *)string_iserial_desc(); _transfer.direction = Send; success = true; break; case STRING_OFFSET_ICONFIGURATION: #ifdef DEBUG printf("5\r\n"); #endif _transfer.remaining = string_iconfiguration_desc()[0]; _transfer.ptr = (uint8_t *)string_iconfiguration_desc(); _transfer.direction = Send; success = true; break; case STRING_OFFSET_IINTERFACE: #ifdef DEBUG printf("6\r\n"); #endif _transfer.remaining = string_iinterface_desc()[0]; _transfer.ptr = (uint8_t *)string_iinterface_desc(); _transfer.direction = Send; success = true; break; } break; } case INTERFACE_DESCRIPTOR: { #ifdef DEBUG printf("interface descr\r\n"); #endif break; } case ENDPOINT_DESCRIPTOR: { #ifdef DEBUG printf("endpoint descr\r\n"); #endif /* TODO: Support is optional, not implemented here */ break; } default: { #ifdef DEBUG printf("ERROR\r\n"); #endif break; } } return success; } void USBDevice::_decode_setup_packet(uint8_t *data, setup_packet_t *packet) { // No lock needed - stateless function /* Fill in the elements of a setup_packet_t structure from raw data */ packet->bmRequestType.dataTransferDirection = (data[0] & 0x80) >> 7; packet->bmRequestType.Type = (data[0] & 0x60) >> 5; packet->bmRequestType.Recipient = data[0] & 0x1f; packet->bRequest = data[1]; packet->wValue = (data[2] | (uint16_t)data[3] << 8); packet->wIndex = (data[4] | (uint16_t)data[5] << 8); packet->wLength = (data[6] | (uint16_t)data[7] << 8); } bool USBDevice::_control_out() { assert_locked(); /* Control transfer data OUT stage */ uint32_t packetSize; /* Check we should be transferring data OUT */ if (_transfer.direction != Receive) { /* for other platforms, count on the HAL to handle this case */ return false; } /* Read from endpoint */ packetSize = _phy->ep0_read_result(); /* Check if transfer size is valid */ if (packetSize > _transfer.remaining) { /* Too big */ return false; } /* Update transfer */ _transfer.ptr += packetSize; _transfer.remaining -= packetSize; /* Check if transfer has completed */ if (_transfer.remaining == 0) { /* Transfer completed */ _transfer.user_callback = RequestXferDone; if (_transfer.notify) { /* Notify class layer. */ _transfer.notify = false; callback_request_xfer_done(&_transfer.setup, false); } else { complete_request_xfer_done(true); } } else { _phy->ep0_read(_transfer.ptr, USB_MIN(_transfer.remaining, _max_packet_size_ep0)); } return true; } bool USBDevice::_control_in() { assert_locked(); /* Control transfer data IN stage */ uint32_t packetSize; /* Check we should be transferring data IN */ if (_transfer.direction != Send) { return false; } if (_transfer.remaining == 0) { if (!_transfer.zlp) { /* Status as already been sent so ignore this IN. */ return true; } /* ZLP will be sent below */ _transfer.zlp = false; } packetSize = _transfer.remaining; if (packetSize > _max_packet_size_ep0) { packetSize = _max_packet_size_ep0; } /* Write to endpoint */ _phy->ep0_write(_transfer.ptr, packetSize); /* Update transfer */ _transfer.ptr += packetSize; _transfer.remaining -= packetSize; /* Send status if all the data has been sent * NOTE - Start the status stage immediately * after writing the last packet. Do not wait * for the next IN event, as this can be dropped * if the ACK by the host is corrupted. * * For more info on this see section * 8.5.3.2 of the USB2.0 specification. */ if ((_transfer.remaining == 0) && !_transfer.zlp) { /* Transfer completed */ _transfer.user_callback = RequestXferDone; if (_transfer.notify) { /* Notify class layer. */ _transfer.notify = false; callback_request_xfer_done(&_transfer.setup, false); } else { complete_request_xfer_done(true); } /* Completed */ return true; } return true; } void USBDevice::complete_request_xfer_done(bool success) { lock(); MBED_ASSERT(_transfer.user_callback == RequestXferDone); _transfer.args.status = success; _run_later(&USBDevice::_complete_request_xfer_done); unlock(); } void USBDevice::_complete_request_xfer_done() { assert_locked(); bool success = _transfer.args.status; _transfer.user_callback = None; if (_abort_control) { _control_abort(); return; } if (!success) { _phy->ep0_stall(); return; } /* Status stage */ if (_transfer.stage == DataOut) { _transfer.stage = Status; _phy->ep0_write(NULL, 0); } else if (_transfer.stage == DataIn) { _transfer.stage = Status; _phy->ep0_read(NULL, 0); } } bool USBDevice::_request_set_address() { assert_locked(); /* Set the device address */ _phy->set_address(_transfer.setup.wValue); if (_transfer.setup.wValue == 0) { _change_state(Default); } else { _change_state(Address); } return true; } bool USBDevice::_request_set_configuration() { assert_locked(); _device.configuration = _transfer.setup.wValue; /* Set the device configuration */ if (_device.configuration == 0) { /* Not configured */ _phy->unconfigure(); _change_state(Address); } else { _endpoint_add_remove_allowed = true; _transfer.user_callback = SetConfiguration; callback_set_configuration(_device.configuration); } return true; } void USBDevice::complete_set_configuration(bool success) { lock(); MBED_ASSERT(_transfer.user_callback == SetConfiguration); _transfer.args.status = success; _run_later(&USBDevice::_complete_set_configuration); unlock(); } void USBDevice::_complete_set_configuration() { assert_locked(); bool success = _transfer.args.status; if ((_abort_control || !success) && !configured()) { // The set configuration request was aborted or failed so // reset any endpoints which may have been added. memset(_endpoint_info, 0, sizeof(_endpoint_info)); _device.configuration = 0; _endpoint_add_remove_allowed = false; } _transfer.user_callback = None; if (_abort_control) { _control_abort(); return; } if (success) { /* Valid configuration */ _phy->configure(); _change_state(Configured); _control_setup_continue(); } else { _phy->ep0_stall(); return; } } bool USBDevice::_request_get_configuration() { assert_locked(); /* Send the device configuration */ _transfer.ptr = &_device.configuration; _transfer.remaining = sizeof(_device.configuration); _transfer.direction = Send; return true; } bool USBDevice::_request_get_interface() { assert_locked(); /* Return the selected alternate setting for an interface */ if (_device.state != Configured) { return false; } /* Send the alternate setting */ _transfer.setup.wIndex = _current_interface; _transfer.ptr = &_current_alternate; _transfer.remaining = sizeof(_current_alternate); _transfer.direction = Send; return true; } bool USBDevice::_request_set_interface() { assert_locked(); _transfer.user_callback = SetInterface; callback_set_interface(_transfer.setup.wIndex, _transfer.setup.wValue); return true; } void USBDevice::complete_set_interface(bool success) { lock(); MBED_ASSERT(_transfer.user_callback == SetInterface); _transfer.args.status = success; _run_later(&USBDevice::_complete_set_interface); unlock(); } void USBDevice::_complete_set_interface() { assert_locked(); bool success = _transfer.args.status; _transfer.user_callback = None; if (_abort_control) { _control_abort(); return; } if (success) { _current_interface = _transfer.setup.wIndex; _current_alternate = _transfer.setup.wValue; _control_setup_continue(); } else { _phy->ep0_stall(); return; } } bool USBDevice::_request_set_feature() { assert_locked(); bool success = false; if (_device.state != Configured) { /* Endpoint or interface must be zero */ if (_transfer.setup.wIndex != 0) { return false; } } switch (_transfer.setup.bmRequestType.Recipient) { case DEVICE_RECIPIENT: /* TODO: Remote wakeup feature not supported */ break; case ENDPOINT_RECIPIENT: if (!EP_INDEXABLE(_transfer.setup.wIndex)) { break; } else if (_transfer.setup.wValue == ENDPOINT_HALT) { endpoint_stall(_transfer.setup.wIndex); success = true; } break; default: break; } return success; } bool USBDevice::_request_clear_feature() { assert_locked(); bool success = false; if (_device.state != Configured) { /* Endpoint or interface must be zero */ if (_transfer.setup.wIndex != 0) { return false; } } switch (_transfer.setup.bmRequestType.Recipient) { case DEVICE_RECIPIENT: /* TODO: Remote wakeup feature not supported */ break; case ENDPOINT_RECIPIENT: if (!EP_INDEXABLE(_transfer.setup.wIndex)) { break; } else if (_transfer.setup.wValue == ENDPOINT_HALT) { endpoint_unstall(_transfer.setup.wIndex); success = true; } break; default: break; } return success; } bool USBDevice::_request_get_status() { assert_locked(); static uint16_t status; bool success = false; if (_device.state != Configured) { /* Endpoint or interface must be zero */ if (_transfer.setup.wIndex != 0) { return false; } } switch (_transfer.setup.bmRequestType.Recipient) { case DEVICE_RECIPIENT: /* TODO: Currently only supports self powered devices */ status = DEVICE_STATUS_SELF_POWERED; success = true; break; case INTERFACE_RECIPIENT: status = 0; success = true; break; case ENDPOINT_RECIPIENT: if (!EP_VALID(_transfer.setup.wIndex)) { break; } else if (EP_CONTROL(_transfer.setup.wIndex)) { /* Control endpoint can't be halted */ status = 0; } else if (_endpoint_info[EP_TO_INDEX(_transfer.setup.wIndex & 0xFF)].flags & ENDPOINT_STALLED) { status = ENDPOINT_STATUS_HALT; } else { status = 0; } success = true; break; default: break; } if (success) { /* Send the status */ _transfer.ptr = (uint8_t *)&status; /* Assumes little endian */ _transfer.remaining = sizeof(status); _transfer.direction = Send; } return success; } bool USBDevice::_request_setup() { assert_locked(); bool success = false; /* Process standard requests */ if ((_transfer.setup.bmRequestType.Type == STANDARD_TYPE)) { switch (_transfer.setup.bRequest) { case GET_STATUS: success = _request_get_status(); break; case CLEAR_FEATURE: success = _request_clear_feature(); break; case SET_FEATURE: success = _request_set_feature(); break; case SET_ADDRESS: success = _request_set_address(); break; case GET_DESCRIPTOR: success = _request_get_descriptor(); break; case SET_DESCRIPTOR: /* TODO: Support is optional, not implemented here */ success = false; break; case GET_CONFIGURATION: success = _request_get_configuration(); break; case SET_CONFIGURATION: success = _request_set_configuration(); break; case GET_INTERFACE: success = _request_get_interface(); break; case SET_INTERFACE: success = _request_set_interface(); break; default: break; } } return success; } void USBDevice::_control_setup() { assert_locked(); /* Control transfer setup stage */ uint8_t buffer[MAX_PACKET_SIZE_EP0]; _phy->ep0_setup_read_result(buffer, _max_packet_size_ep0); /* Initialise control transfer state */ _decode_setup_packet(buffer, &_transfer.setup); _transfer.ptr = NULL; _transfer.remaining = 0; _transfer.direction = 0; _transfer.zlp = false; _transfer.notify = false; _transfer.stage = Setup; _transfer.user_callback = Request; #ifdef DEBUG printf("dataTransferDirection: %d\r\nType: %d\r\nRecipient: %d\r\nbRequest: %d\r\nwValue: %d\r\nwIndex: %d\r\nwLength: %d\r\n", _transfer.setup.bmRequestType.dataTransferDirection, _transfer.setup.bmRequestType.Type, _transfer.setup.bmRequestType.Recipient, _transfer.setup.bRequest, _transfer.setup.wValue, _transfer.setup.wIndex, _transfer.setup.wLength); #endif /* Class / vendor specific */ callback_request(&_transfer.setup); } void USBDevice::complete_request(RequestResult direction, uint8_t *data, uint32_t size) { lock(); MBED_ASSERT(_transfer.user_callback == Request); _transfer.args.request.result = direction; _transfer.args.request.data = data; _transfer.args.request.size = size; _run_later(&USBDevice::_complete_request); unlock(); } void USBDevice::_complete_request() { assert_locked(); RequestResult direction = _transfer.args.request.result; uint8_t *data = _transfer.args.request.data; uint32_t size = _transfer.args.request.size; _transfer.user_callback = None; if (_abort_control) { if ((direction == Receive) || (direction == Send)) { _transfer.user_callback = RequestXferDone; callback_request_xfer_done(&_transfer.setup, true); } else { _control_abort(); } return; } if (direction == PassThrough) { /* Standard requests */ if (!_request_setup()) { _phy->ep0_stall(); return; } /* user_callback may be set by _request_setup() */ if (_transfer.user_callback == None) { _control_setup_continue(); } } else if (direction == Failure) { _phy->ep0_stall(); return; } else { _transfer.notify = true; _transfer.remaining = size; _transfer.ptr = data; _transfer.direction = direction; _control_setup_continue(); } } void USBDevice::_control_abort_start() { assert_locked(); _setup_ready = false; if (_transfer.user_callback == None) { _control_abort(); } else { _abort_control = true; } } void USBDevice::_control_setup_continue() { assert_locked(); /* Check transfer size and direction */ if (_transfer.setup.wLength > 0) { if (_transfer.setup.bmRequestType.dataTransferDirection \ == Send) { /* IN data stage is required */ if (_transfer.direction != Send) { _phy->ep0_stall(); return; } /* Transfer must be less than or equal to the size */ /* requested by the host */ if (_transfer.remaining > _transfer.setup.wLength) { _transfer.remaining = _transfer.setup.wLength; } } else { /* OUT data stage is required */ if (_transfer.direction != Receive) { _phy->ep0_stall(); return; } /* Transfer must be equal to the size requested by the host */ if (_transfer.remaining != _transfer.setup.wLength) { _phy->ep0_stall(); return; } } } else { /* No data stage; transfer size must be zero */ if (_transfer.remaining != 0) { _phy->ep0_stall(); return; } } /* Data or status stage if applicable */ if (_transfer.setup.wLength > 0) { if (_transfer.setup.bmRequestType.dataTransferDirection \ == Send) { /* Check if we'll need to send a zero length packet at */ /* the end of this transfer */ if (_transfer.setup.wLength > _transfer.remaining) { /* Device wishes to transfer less than host requested */ if ((_transfer.remaining % _max_packet_size_ep0) == 0) { /* Transfer is a multiple of EP0 max packet size */ _transfer.zlp = true; } } /* IN stage */ _transfer.stage = DataIn; _control_in(); } else { /* OUT stage */ _transfer.stage = DataOut; _phy->ep0_read(_transfer.ptr, USB_MIN(_transfer.remaining, _max_packet_size_ep0)); } } else { /* Status stage */ _transfer.stage = Status; _phy->ep0_write(NULL, 0); } } void USBDevice::_control_abort() { assert_locked(); _abort_control = false; _transfer.stage = Status; } void USBDevice::reset() { assert_locked(); _change_state(Default); _device.suspended = false; _control_abort_start(); /* Call class / vendor specific busReset function */ callback_reset(); } void USBDevice::ep0_setup() { assert_locked(); if (_device.state < Default) { MBED_ASSERT(0); return; } _setup_ready = true; /* Endpoint 0 setup event */ if (_transfer.user_callback == None) { _control_setup(); } else { /* A new setup packet has arrived so abort the current control transfer */ _abort_control = true; } } void USBDevice::ep0_out() { assert_locked(); if (_device.state < Default) { MBED_ASSERT(0); return; } if (_transfer.user_callback != None) { /* EP0 OUT should not receive data if the stack is waiting on a user callback for the buffer to fill or status */ MBED_ASSERT(0); return; } if (_transfer.stage == Status) { // No action needed on status stage return; } /* Endpoint 0 OUT data event */ if (!_control_out()) { /* Protocol stall; this will stall both endpoints */ _phy->ep0_stall(); return; } } void USBDevice::ep0_in() { assert_locked(); if (_device.state < Default) { MBED_ASSERT(0); return; } #ifdef DEBUG printf("ep0_in\r\n"); #endif if (_transfer.stage == Status) { // No action needed on status stage return; } /* Endpoint 0 IN data event */ if (!_control_in()) { /* Protocol stall; this will stall both endpoints */ _phy->ep0_stall(); return; } } void USBDevice::out(usb_ep_t endpoint) { assert_locked(); if (!EP_INDEXABLE(endpoint)) { MBED_ASSERT(0); return; } endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; MBED_ASSERT(info->pending >= 1); info->pending -= 1; if (info->callback) { (this->*(info->callback))(); } } void USBDevice::in(usb_ep_t endpoint) { assert_locked(); if (!EP_INDEXABLE(endpoint)) { MBED_ASSERT(0); return; } endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; MBED_ASSERT(info->pending >= 1); info->pending -= 1; if (info->callback) { (this->*(info->callback))(); } } void USBDevice::init() { lock(); if (!_initialized) { this->_phy->init(this); _max_packet_size_ep0 = this->_phy->ep0_set_max_packet(MAX_PACKET_SIZE_EP0); _initialized = true; } unlock(); } void USBDevice::deinit() { lock(); if (_initialized) { disconnect(); this->_phy->deinit(); _initialized = false; } unlock(); } bool USBDevice::configured() { lock(); /* Returns true if device is in the Configured state */ bool ret = (_device.state == Configured); unlock(); return ret; } void USBDevice::connect() { lock(); /* Ensure device has been initialized */ init(); /* Connect device */ if (!_connected) { _phy->connect(); _connected = true; } unlock(); } void USBDevice::disconnect() { lock(); /* Disconnect device */ if (_connected) { _phy->disconnect(); _connected = false; } /* Set initial device state */ if (_device.state > Powered) { _change_state(Powered); } //TODO - remove these? _device.configuration = 0; _device.suspended = false; unlock(); } void USBDevice::sof_enable() { lock(); _phy->sof_enable(); unlock(); } void USBDevice::sof_disable() { lock(); _phy->sof_disable(); unlock(); } bool USBDevice::endpoint_add(usb_ep_t endpoint, uint32_t max_packet_size, usb_ep_type_t type, ep_cb_t callback) { lock(); if (!EP_INDEXABLE(endpoint)) { MBED_ASSERT(0); unlock(); return false; } if (!_endpoint_add_remove_allowed) { unlock(); return false; } endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; MBED_ASSERT(!(info->flags & ENDPOINT_ENABLED)); MBED_ASSERT(max_packet_size <= 1024); bool ret = _phy->endpoint_add(endpoint, max_packet_size, type); if (ret) { info->callback = callback; info->flags |= ENDPOINT_ENABLED; info->pending = 0; info->max_packet_size = max_packet_size; } unlock(); return ret; } void USBDevice::endpoint_remove(usb_ep_t endpoint) { lock(); if (!EP_INDEXABLE(endpoint)) { MBED_ASSERT(0); unlock(); return; } if (!_endpoint_add_remove_allowed) { unlock(); return; } endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; MBED_ASSERT(info->flags & ENDPOINT_ENABLED); if (info->pending) { _phy->endpoint_abort(endpoint); } info->callback = NULL; info->flags = 0; info->pending = 0; info->max_packet_size = 0; _phy->endpoint_remove(endpoint); unlock(); } void USBDevice::endpoint_remove_all() { lock(); for (uint32_t i = 0; i < sizeof(_endpoint_info) / sizeof(_endpoint_info[0]); i++) { endpoint_info_t *info = _endpoint_info + i; if (info->flags & ENDPOINT_ENABLED) { endpoint_remove(INDEX_TO_EP(i)); } } unlock(); } void USBDevice::endpoint_stall(usb_ep_t endpoint) { lock(); if (!EP_INDEXABLE(endpoint)) { MBED_ASSERT(0); unlock(); return; } endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; if (!(info->flags & ENDPOINT_ENABLED)) { // Invalid endpoint is being used MBED_ASSERT(!configured()); unlock(); return; } info->flags |= ENDPOINT_STALLED; _phy->endpoint_stall(endpoint); if (info->pending) { endpoint_abort(endpoint); } unlock(); } void USBDevice::endpoint_unstall(usb_ep_t endpoint) { lock(); if (!EP_INDEXABLE(endpoint)) { MBED_ASSERT(0); unlock(); return; } endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; if (!(info->flags & ENDPOINT_ENABLED)) { // Invalid endpoint is being used MBED_ASSERT(!configured()); unlock(); return; } if (info->pending) { endpoint_abort(endpoint); } info->flags &= ~ENDPOINT_STALLED; _phy->endpoint_unstall(endpoint); unlock(); } uint8_t *USBDevice::find_descriptor(uint8_t descriptorType, uint8_t index) { /* Find a descriptor within the list of descriptors */ /* following a configuration descriptor. */ uint16_t wTotalLength; uint8_t *ptr; if (configuration_desc(index) == NULL) { return NULL; } /* Check this is a configuration descriptor */ if ((configuration_desc(index)[0] != CONFIGURATION_DESCRIPTOR_LENGTH) \ || (configuration_desc(index)[1] != CONFIGURATION_DESCRIPTOR)) { return NULL; } wTotalLength = configuration_desc(index)[2] | (configuration_desc(index)[3] << 8); /* Check there are some more descriptors to follow */ if (wTotalLength <= (CONFIGURATION_DESCRIPTOR_LENGTH + 2)) /* +2 is for bLength and bDescriptorType of next descriptor */ { return NULL; } /* Start at first descriptor after the configuration descriptor */ ptr = &(((uint8_t *)configuration_desc(index))[CONFIGURATION_DESCRIPTOR_LENGTH]); do { if (ptr[1] /* bDescriptorType */ == descriptorType) { /* Found */ return ptr; } /* Skip to next descriptor */ ptr += ptr[0]; /* bLength */ } while (ptr < (configuration_desc(index) + wTotalLength)); /* Reached end of the descriptors - not found */ return NULL; } const usb_ep_table_t *USBDevice::endpoint_table() { return _phy->endpoint_table(); } void USBDevice::power(bool powered) { assert_locked(); if (!powered && _device.state > Attached) { _change_state(Attached); } } void USBDevice::suspend(bool suspended) { } void USBDevice::sof(int frame_number) { callback_sof(frame_number); } USBDevice::USBDevice(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) { this->vendor_id = vendor_id; this->product_id = product_id; this->product_release = product_release; memset(_endpoint_info, 0, sizeof(_endpoint_info)); memset(&_transfer, 0, sizeof(_transfer)); _transfer.user_callback = None; _setup_ready = false; _abort_control = false; _phy = phy; _initialized = false; _connected = false; _endpoint_add_remove_allowed = false; _current_interface = 0; _current_alternate = 0; _locked = 0; _post_process = NULL; /* Set initial device state */ _device.state = Powered; _device.configuration = 0; _device.suspended = false; } USBDevice::~USBDevice() { MBED_ASSERT(!_initialized); deinit(); } uint32_t USBDevice::endpoint_max_packet_size(usb_ep_t endpoint) { lock(); uint32_t size = 0; if (EP_CONTROL(endpoint)) { size = _max_packet_size_ep0; } else { endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; size = info->max_packet_size; } unlock(); return size; } void USBDevice::endpoint_abort(usb_ep_t endpoint) { lock(); if (!EP_INDEXABLE(endpoint)) { MBED_ASSERT(0); unlock(); return; } endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; if (!(info->flags & ENDPOINT_ENABLED)) { // Assert that only valid endpoints are used when in the configured state MBED_ASSERT(!configured()); unlock(); return; } if (info->pending) { _phy->endpoint_abort(endpoint); info->pending = 0; } unlock(); } bool USBDevice::read_start(usb_ep_t endpoint, uint8_t *buffer, uint32_t max_size) { lock(); if (!EP_INDEXABLE(endpoint)) { MBED_ASSERT(0); unlock(); return false; } endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; if (!(info->flags & ENDPOINT_ENABLED)) { // Assert that only valid endpoints are used when in the configured state MBED_ASSERT(!configured()); unlock(); return false; } if (max_size < info->max_packet_size) { MBED_ASSERT(0); unlock(); return false; } if (info->pending) { // Only allow 1 packet unlock(); return false; } bool ret = _phy->endpoint_read(endpoint, buffer, info->max_packet_size); if (ret) { info->pending += 1; } unlock(); return ret; } uint32_t USBDevice::read_finish(usb_ep_t endpoint) { lock(); if (!EP_INDEXABLE(endpoint)) { MBED_ASSERT(0); unlock(); return 0; } endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; if (!(info->flags & ENDPOINT_ENABLED)) { // Assert that only valid endpoints are used when in the configured state MBED_ASSERT(!configured()); unlock(); return 0; } uint32_t size = 0; size = _phy->endpoint_read_result(endpoint); unlock(); return size; } bool USBDevice::write_start(usb_ep_t endpoint, uint8_t *buffer, uint32_t size) { lock(); if (!EP_INDEXABLE(endpoint)) { MBED_ASSERT(0); unlock(); return false; } endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; if (!(info->flags & ENDPOINT_ENABLED)) { // Assert that only valid endpoints are used when in the configured state MBED_ASSERT(!configured()); unlock(); return false; } if (size > info->max_packet_size) { // Size being written is too large MBED_ASSERT(0); unlock(); return false; } if (info->pending) { // Only allow 1 packet unlock(); return false; } /* Send report */ bool ret = _phy->endpoint_write(endpoint, buffer, size); if (ret) { info->transfer_size = size; info->pending += 1; } else { info->transfer_size = 0; } unlock(); return ret; } uint32_t USBDevice::write_finish(usb_ep_t endpoint) { uint32_t ret = 0; lock(); if (!EP_INDEXABLE(endpoint)) { MBED_ASSERT(0); unlock(); return 0; } endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; if (!(info->flags & ENDPOINT_ENABLED)) { // Assert that only valid endpoints are used when in the configured state MBED_ASSERT(!configured()); unlock(); return 0; } ret = info->transfer_size; unlock(); return ret; } const uint8_t *USBDevice::device_desc() { uint8_t device_descriptor_temp[] = { DEVICE_DESCRIPTOR_LENGTH, /* bLength */ DEVICE_DESCRIPTOR, /* bDescriptorType */ LSB(USB_VERSION_2_0), /* bcdUSB (LSB) */ MSB(USB_VERSION_2_0), /* bcdUSB (MSB) */ 0x00, /* bDeviceClass */ 0x00, /* bDeviceSubClass */ 0x00, /* bDeviceprotocol */ (uint8_t)_max_packet_size_ep0, /* bMaxPacketSize0 */ (uint8_t)(LSB(vendor_id)), /* idVendor (LSB) */ (uint8_t)(MSB(vendor_id)), /* idVendor (MSB) */ (uint8_t)(LSB(product_id)), /* idProduct (LSB) */ (uint8_t)(MSB(product_id)), /* idProduct (MSB) */ (uint8_t)(LSB(product_release)), /* bcdDevice (LSB) */ (uint8_t)(MSB(product_release)), /* bcdDevice (MSB) */ STRING_OFFSET_IMANUFACTURER, /* iManufacturer */ STRING_OFFSET_IPRODUCT, /* iProduct */ STRING_OFFSET_ISERIAL, /* iSerialNumber */ 0x01 /* bNumConfigurations */ }; MBED_ASSERT(sizeof(device_descriptor_temp) == sizeof(device_descriptor)); memcpy(device_descriptor, device_descriptor_temp, sizeof(device_descriptor)); return device_descriptor; } const uint8_t *USBDevice::string_langid_desc() { static const uint8_t string_langid_descriptor[] = { 0x04, /*bLength*/ STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ 0x09, 0x04, /*bString Lang ID - 0x0409 - English*/ }; return string_langid_descriptor; } const uint8_t *USBDevice::string_imanufacturer_desc() { static const uint8_t string_imanufacturer_descriptor[] = { 0x12, /*bLength*/ STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ 'm', 0, 'b', 0, 'e', 0, 'd', 0, '.', 0, 'o', 0, 'r', 0, 'g', 0, /*bString iManufacturer - mbed.org*/ }; return string_imanufacturer_descriptor; } const uint8_t *USBDevice::string_iserial_desc() { static const uint8_t string_iserial_descriptor[] = { 0x16, /*bLength*/ STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ '0', 0, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0, '9', 0, /*bString iSerial - 0123456789*/ }; return string_iserial_descriptor; } const uint8_t *USBDevice::string_iconfiguration_desc() { static const uint8_t string_iconfiguration_descriptor[] = { 0x06, /*bLength*/ STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ '0', 0, '1', 0, /*bString iConfiguration - 01*/ }; return string_iconfiguration_descriptor; } const uint8_t *USBDevice::string_iinterface_desc() { static const uint8_t string_iinterface_descriptor[] = { 0x08, /*bLength*/ STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ 'U', 0, 'S', 0, 'B', 0, /*bString iInterface - USB*/ }; return string_iinterface_descriptor; } const uint8_t *USBDevice::string_iproduct_desc() { static const uint8_t string_iproduct_descriptor[] = { 0x16, /*bLength*/ STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ 'U', 0, 'S', 0, 'B', 0, ' ', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0 /*bString iProduct - USB DEVICE*/ }; return string_iproduct_descriptor; } void USBDevice::start_process() { lock(); _phy->process(); unlock(); } void USBDevice::lock() { core_util_critical_section_enter(); _locked++; MBED_ASSERT(_locked > 0); } void USBDevice::unlock() { if (_locked == 1) { // Perform post processing before fully unlocking while (_post_process != NULL) { void (USBDevice::*call)() = _post_process; _post_process = NULL; (this->*call)(); } } MBED_ASSERT(_locked > 0); _locked--; core_util_critical_section_exit(); } void USBDevice::assert_locked() { MBED_ASSERT(_locked > 0); } void USBDevice::_change_state(DeviceState new_state) { assert_locked(); DeviceState old_state = _device.state; _device.state = new_state; if (old_state == new_state) { return; } bool leaving_configured_state = (old_state >= Configured) && (new_state < Configured); bool leaving_default_state = (old_state >= Default) && (new_state < Default); if (leaving_configured_state) { memset(_endpoint_info, 0, sizeof(_endpoint_info)); _device.configuration = 0; _endpoint_add_remove_allowed = false; } if (leaving_default_state) { /* Abort any pending control transfers */ _control_abort_start(); } callback_state_change(new_state); } void USBDevice::_run_later(void (USBDevice::*function)()) { _post_process = function; }