mirror of https://github.com/ARMmbed/mbed-os.git
515 lines
14 KiB
C++
515 lines
14 KiB
C++
/* mbed Microcontroller Library
|
|
* Copyright (c) 2018-2020 ARM Limited
|
|
* 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.
|
|
*/
|
|
|
|
#if defined(DEVICE_USBDEVICE) && DEVICE_USBDEVICE
|
|
|
|
#include "USBPhyHw_MCUXpresso.h"
|
|
#include "USBEndpoints_MCUXpresso.h"
|
|
#include "fsl_clock_config.h"
|
|
|
|
static USBPhyHw *instance;
|
|
|
|
static volatile int epComplete = 0;
|
|
|
|
static uint32_t usb_irq = 0;
|
|
|
|
usb_device_handle g_deviceHandle;
|
|
|
|
static USB_Type * const usb_addrs[] = USB_BASE_PTRS;
|
|
|
|
// Convert physical endpoint number to register bit
|
|
#define EP(endpoint) (1<<(endpoint))
|
|
|
|
// Conversion macros
|
|
#define PHY_TO_LOG(endpoint) ((endpoint)>>1)
|
|
#define DESC_TO_LOG(endpoint) ((endpoint) & 0xF)
|
|
#define DESC_TO_PHY(endpoint) ((((endpoint)&0x0F)<<1) | (((endpoint) & 0x80) ? 1:0))
|
|
#define PHY_TO_DESC(endpoint) (((endpoint)>>1)|(((endpoint)&1)?0x80:0))
|
|
|
|
// Get endpoint direction
|
|
#define DESC_EP_IN(endpoint) ((endpoint) & 0x80U ? true : false)
|
|
#define DESC_EP_OUT(endpoint) ((endpoint) & 0x80U ? false : true)
|
|
|
|
#define TX 1
|
|
#define RX 0
|
|
#define ODD 0
|
|
#define EVEN 1
|
|
// this macro waits a physical endpoint number
|
|
#define EP_BDT_IDX(ep, dir, odd) (((ep * 4) + (2 * dir) + (1 * odd)))
|
|
|
|
uint8_t *endpoint_buffer[NUMBER_OF_PHYSICAL_ENDPOINTS * 2];
|
|
uint16_t endpoint_packet_size[NUMBER_OF_PHYSICAL_ENDPOINTS];
|
|
USB_CONTROLLER_DATA uint32_t ep0_buffer[2][MAX_PACKET_SIZE_EP0 / 4];
|
|
USB_CONTROLLER_DATA uint32_t ep1_buffer[2][MAX_PACKET_SIZE_EP1 / 4];
|
|
USB_CONTROLLER_DATA uint32_t ep2_buffer[2][MAX_PACKET_SIZE_EP2 / 4];
|
|
USB_CONTROLLER_DATA uint32_t ep3_buffer[2][MAX_PACKET_SIZE_EP3 / 4];
|
|
|
|
static uint8_t set_addr = 0;
|
|
|
|
extern "C" uint32_t USB_DeviceGetIrqNumber(void);
|
|
extern "C" void USB_DeviceClockInit(void);
|
|
|
|
static uint32_t frameNumber()
|
|
{
|
|
return (usb_addrs[USB_INSTANCE]->FRINDEX >> 3);
|
|
}
|
|
|
|
USBPhy *get_usb_phy()
|
|
{
|
|
static USBPhyHw usbphy;
|
|
return &usbphy;
|
|
}
|
|
|
|
USBPhyHw::USBPhyHw(): events(NULL)
|
|
{
|
|
|
|
}
|
|
|
|
USBPhyHw::~USBPhyHw()
|
|
{
|
|
}
|
|
|
|
usb_status_t USBPhy_DeviceCallback(usb_device_handle handle, uint32_t event, void *param)
|
|
{
|
|
switch (event) {
|
|
// reset interrupt
|
|
case kUSB_DeviceEventBusReset:
|
|
instance->endpoint_add(EP0OUT, MAX_PACKET_SIZE_EP0, USB_EP_TYPE_CTRL);
|
|
instance->endpoint_add(EP0IN, MAX_PACKET_SIZE_EP0, USB_EP_TYPE_CTRL);
|
|
|
|
memset(instance->read_buffers, 0, sizeof(instance->read_buffers));
|
|
memset(instance->read_sizes, 0, sizeof(instance->read_sizes));
|
|
|
|
// reset bus for USBDevice layer
|
|
instance->events->reset();
|
|
break;
|
|
case kUSB_DeviceEventResume:
|
|
instance->events->suspend(false);
|
|
break;
|
|
case kUSB_DeviceEventSuspend:
|
|
instance->events->suspend(true);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return kStatus_USB_Success;
|
|
}
|
|
|
|
usb_status_t USBPhy_EndpointCallback(usb_device_handle handle, usb_device_endpoint_callback_message_struct_t *message,
|
|
void *callbackParam)
|
|
{
|
|
usb_ep_t endpoint = (usb_ep_t )((long)callbackParam);
|
|
int phy_ep = DESC_TO_PHY(endpoint);
|
|
uint8_t state;
|
|
|
|
if (message->length == USB_UNINITIALIZED_VAL_32) {
|
|
return kStatus_USB_Success;
|
|
}
|
|
|
|
/* Update the read size to what was actually read */
|
|
instance->read_sizes[phy_ep] = message->length;
|
|
|
|
// setup packet
|
|
if (message->isSetup) {
|
|
memcpy(&ep0_buffer[0][0], message->buffer, 8U);
|
|
|
|
// EP0 SETUP event (SETUP data received)
|
|
instance->events->ep0_setup();
|
|
} else {
|
|
if (DESC_EP_IN(endpoint)) {
|
|
if ((endpoint & USB_ENDPOINT_NUMBER_MASK) == 0) {
|
|
instance->events->ep0_in();
|
|
if (set_addr == 1) {
|
|
state = (uint8_t)kUSB_DeviceStateAddress;
|
|
USB_DeviceSetStatus(g_deviceHandle, kUSB_DeviceStatusAddress, &state);
|
|
set_addr = 0;
|
|
}
|
|
} else {
|
|
epComplete |= EP(phy_ep);
|
|
instance->events->in(endpoint);
|
|
}
|
|
} else {
|
|
if ((endpoint & USB_ENDPOINT_NUMBER_MASK) == 0) {
|
|
instance->events->ep0_out();
|
|
} else {
|
|
epComplete |= EP(phy_ep);
|
|
instance->events->out(endpoint);
|
|
}
|
|
}
|
|
}
|
|
|
|
return kStatus_USB_Success;
|
|
}
|
|
|
|
void USBPhyHw::init(USBPhyEvents *events)
|
|
{
|
|
if (this->events == NULL) {
|
|
sleep_manager_lock_deep_sleep();
|
|
}
|
|
|
|
this->events = events;
|
|
|
|
usb_irq = USB_DeviceGetIrqNumber();
|
|
|
|
// Disable IRQ
|
|
NVIC_DisableIRQ((IRQn_Type)usb_irq);
|
|
|
|
// Attach IRQ
|
|
instance = this;
|
|
|
|
USB_DeviceClockInit();
|
|
|
|
if (kStatus_USB_Success != USB_DeviceInit(CONTROLLER_ID, USBPhy_DeviceCallback, &g_deviceHandle)) {
|
|
return;
|
|
}
|
|
|
|
/* Allocate control endpoint buffers */
|
|
endpoint_buffer[EP_BDT_IDX(0, TX, ODD)] = (uint8_t*)ep0_buffer[TX];
|
|
endpoint_buffer[EP_BDT_IDX(0, RX, ODD)] = (uint8_t*)ep0_buffer[RX];
|
|
endpoint_buffer[EP_BDT_IDX(1, TX, ODD)] = (uint8_t*)ep1_buffer[TX];
|
|
endpoint_buffer[EP_BDT_IDX(1, RX, ODD)] = (uint8_t*)ep1_buffer[RX];
|
|
endpoint_buffer[EP_BDT_IDX(2, TX, ODD)] = (uint8_t*)ep2_buffer[TX];
|
|
endpoint_buffer[EP_BDT_IDX(2, RX, ODD)] = (uint8_t*)ep2_buffer[RX];
|
|
endpoint_buffer[EP_BDT_IDX(3, TX, ODD)] = (uint8_t*)ep3_buffer[TX];
|
|
endpoint_buffer[EP_BDT_IDX(3, RX, ODD)] = (uint8_t*)ep3_buffer[RX];
|
|
endpoint_packet_size[0] = sizeof (ep0_buffer[TX]);
|
|
endpoint_packet_size[1] = sizeof (ep0_buffer[RX]);
|
|
endpoint_packet_size[2] = sizeof (ep1_buffer[TX]);
|
|
endpoint_packet_size[3] = sizeof (ep1_buffer[RX]);
|
|
endpoint_packet_size[4] = sizeof (ep2_buffer[TX]);
|
|
endpoint_packet_size[5] = sizeof (ep2_buffer[RX]);
|
|
endpoint_packet_size[6] = sizeof (ep3_buffer[TX]);
|
|
endpoint_packet_size[7] = sizeof (ep3_buffer[RX]);
|
|
|
|
NVIC_SetVector((IRQn_Type)usb_irq, (uint32_t)&_usbisr);
|
|
NVIC_EnableIRQ((IRQn_Type)usb_irq);
|
|
}
|
|
|
|
void USBPhyHw::deinit()
|
|
{
|
|
USB_DeviceDeinit(g_deviceHandle);
|
|
|
|
disconnect();
|
|
NVIC_DisableIRQ((IRQn_Type)usb_irq);
|
|
|
|
if (events != NULL) {
|
|
sleep_manager_unlock_deep_sleep();
|
|
}
|
|
events = NULL;
|
|
}
|
|
|
|
bool USBPhyHw::powered()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void USBPhyHw::connect()
|
|
{
|
|
USB_DeviceRun(g_deviceHandle);
|
|
}
|
|
|
|
void USBPhyHw::disconnect()
|
|
{
|
|
// disable all endpoints to prevent them from nacking when disconnected
|
|
for (uint8_t i = 0; i < (USB_DEVICE_CONFIG_ENDPOINTS); i++) {
|
|
USB_DeviceDeinitEndpoint(g_deviceHandle,
|
|
(i & USB_ENDPOINT_NUMBER_MASK) |
|
|
(USB_IN << USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_SHIFT));
|
|
USB_DeviceDeinitEndpoint(g_deviceHandle,
|
|
(i & USB_ENDPOINT_NUMBER_MASK) |
|
|
(USB_OUT << USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_SHIFT));
|
|
|
|
}
|
|
|
|
USB_DeviceStop(g_deviceHandle);
|
|
}
|
|
|
|
void USBPhyHw::configure()
|
|
{
|
|
|
|
}
|
|
|
|
void USBPhyHw::unconfigure()
|
|
{
|
|
|
|
}
|
|
|
|
void USBPhyHw::sof_enable()
|
|
{
|
|
usb_addrs[USB_INSTANCE]->USBINTR |= USBHS_USBINTR_SRE_MASK;
|
|
}
|
|
|
|
void USBPhyHw::sof_disable()
|
|
{
|
|
usb_addrs[USB_INSTANCE]->USBINTR &= ~USBHS_USBINTR_SRE_MASK;
|
|
}
|
|
|
|
void USBPhyHw::set_address(uint8_t address)
|
|
{
|
|
uint8_t state;
|
|
|
|
// we don't set the address now, we set a flag instead.
|
|
// see usbisr when an IN token is received
|
|
set_addr = 1;
|
|
|
|
state = address & 0xFFU;
|
|
|
|
USB_DeviceSetStatus(g_deviceHandle, kUSB_DeviceStatusAddress, &state);
|
|
}
|
|
|
|
void USBPhyHw::remote_wakeup()
|
|
{
|
|
|
|
}
|
|
|
|
#define ALLOW_BULK_OR_INT_ENDPOINTS (USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_ALLOW_INT)
|
|
#define ALLOW_NO_ENDPOINTS 0
|
|
|
|
const usb_ep_table_t *USBPhyHw::endpoint_table()
|
|
{
|
|
static const usb_ep_table_t endpoint_table = {
|
|
1, // No cost per endpoint - everything allocated up front
|
|
{
|
|
{USB_EP_ATTR_ALLOW_CTRL | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_BULK_OR_INT_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_BULK_OR_INT_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
|
|
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0}
|
|
}
|
|
};
|
|
return &endpoint_table;
|
|
}
|
|
|
|
uint32_t USBPhyHw::ep0_set_max_packet(uint32_t max_packet)
|
|
{
|
|
return USB_CONTROL_MAX_PACKET_SIZE;
|
|
}
|
|
|
|
// read setup packet
|
|
void USBPhyHw::ep0_setup_read_result(uint8_t *buffer, uint32_t size)
|
|
{
|
|
memcpy(buffer, &ep0_buffer[0][0], 8U);
|
|
}
|
|
|
|
void USBPhyHw::ep0_read(uint8_t *data, uint32_t size)
|
|
{
|
|
endpoint_read(EP0OUT, data, size);
|
|
}
|
|
|
|
uint32_t USBPhyHw::ep0_read_result()
|
|
{
|
|
return endpoint_read_result(EP0OUT);
|
|
}
|
|
|
|
void USBPhyHw::ep0_write(uint8_t *buffer, uint32_t size)
|
|
{
|
|
endpoint_write(EP0IN, buffer, size);
|
|
}
|
|
|
|
void USBPhyHw::ep0_stall()
|
|
{
|
|
endpoint_stall(EP0IN);
|
|
endpoint_stall(EP0OUT);
|
|
}
|
|
|
|
bool USBPhyHw::endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type)
|
|
{
|
|
usb_device_endpoint_init_struct_t epInit;
|
|
usb_device_endpoint_callback_struct_t epCallback;
|
|
|
|
memset(&epInit, 0, sizeof(epInit));
|
|
memset(&epCallback, 0, sizeof(epCallback));
|
|
|
|
if (DESC_TO_PHY(endpoint) > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
|
|
return false;
|
|
}
|
|
|
|
epInit.transferType = (uint8_t)type;
|
|
epInit.maxPacketSize = (uint16_t)max_packet;
|
|
epInit.endpointAddress = endpoint;
|
|
if (type == USB_EP_TYPE_CTRL) {
|
|
epInit.zlt = 1;
|
|
}
|
|
|
|
epCallback.callbackFn = USBPhy_EndpointCallback;
|
|
epCallback.callbackParam = (void *)((long)endpoint);
|
|
|
|
if (USB_DeviceInitEndpoint(g_deviceHandle, &epInit, &epCallback) != kStatus_USB_Success) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void USBPhyHw::endpoint_remove(usb_ep_t endpoint)
|
|
{
|
|
USB_DeviceDeinitEndpoint(g_deviceHandle, endpoint);
|
|
}
|
|
|
|
void USBPhyHw::endpoint_stall(usb_ep_t endpoint)
|
|
{
|
|
USB_DeviceStallEndpoint(g_deviceHandle, endpoint);
|
|
}
|
|
|
|
void USBPhyHw::endpoint_unstall(usb_ep_t endpoint)
|
|
{
|
|
USB_DeviceUnstallEndpoint(g_deviceHandle, endpoint);
|
|
}
|
|
|
|
bool USBPhyHw::endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size)
|
|
{
|
|
uint8_t log = DESC_TO_PHY(endpoint);
|
|
|
|
read_buffers[log] = data;
|
|
read_sizes[log] = size;
|
|
return endpoint_read_core(endpoint, size);
|
|
}
|
|
|
|
bool USBPhyHw::endpoint_read_core(usb_ep_t endpoint, uint32_t max_packet)
|
|
{
|
|
uint8_t log_endpoint = DESC_TO_LOG(endpoint);
|
|
uint8_t *buf;
|
|
|
|
buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)][0];
|
|
|
|
if (USB_DeviceRecvRequest(g_deviceHandle, endpoint, buf, (max_packet > endpoint_packet_size[log_endpoint] ? endpoint_packet_size[log_endpoint] : max_packet)) != kStatus_USB_Success) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32_t USBPhyHw::endpoint_read_result(usb_ep_t endpoint)
|
|
{
|
|
uint8_t log = DESC_TO_PHY(endpoint);
|
|
|
|
uint32_t bytes_read = 0;
|
|
endpoint_read_result_core(endpoint, read_buffers[log], read_sizes[log], &bytes_read);
|
|
read_buffers[log] = NULL;
|
|
read_sizes[log] = 0;
|
|
|
|
return bytes_read;
|
|
}
|
|
|
|
|
|
bool USBPhyHw::endpoint_read_result_core(usb_ep_t endpoint, uint8_t *data, uint32_t size, uint32_t *bytes_read)
|
|
{
|
|
uint32_t n, idx;
|
|
uint8_t *ep_buf;
|
|
|
|
uint32_t log_endpoint = DESC_TO_LOG(endpoint);
|
|
|
|
if (DESC_TO_PHY(endpoint) > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
|
|
return false;
|
|
}
|
|
|
|
// if read on a IN endpoint -> error
|
|
if (DESC_EP_IN(endpoint)) {
|
|
return false;
|
|
}
|
|
|
|
idx = EP_BDT_IDX(log_endpoint, RX, 0);
|
|
|
|
if ((log_endpoint != 0) && !(epComplete & EP(DESC_TO_PHY(endpoint)))) {
|
|
return false;
|
|
}
|
|
|
|
ep_buf = endpoint_buffer[idx];
|
|
|
|
for (n = 0; n < read_sizes[DESC_TO_PHY(endpoint)]; n++) {
|
|
data[n] = ep_buf[n];
|
|
}
|
|
|
|
*bytes_read = read_sizes[DESC_TO_PHY(endpoint)];
|
|
|
|
epComplete &= ~EP(DESC_TO_PHY(endpoint));
|
|
return true;
|
|
}
|
|
|
|
bool USBPhyHw::endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size)
|
|
{
|
|
uint32_t idx, n;
|
|
uint8_t *ep_buf;
|
|
|
|
if (DESC_TO_PHY(endpoint) > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
|
|
return false;
|
|
}
|
|
|
|
// if write on a OUT endpoint -> error
|
|
if (DESC_EP_OUT(endpoint)) {
|
|
return false;
|
|
}
|
|
|
|
idx = EP_BDT_IDX(DESC_TO_LOG(endpoint), TX, 0);
|
|
|
|
ep_buf = endpoint_buffer[idx];
|
|
|
|
if (size > endpoint_packet_size[DESC_TO_LOG(endpoint)]) {
|
|
size = endpoint_packet_size[DESC_TO_LOG(endpoint)];
|
|
}
|
|
for (n = 0; n < size; n++) {
|
|
ep_buf[n] = data[n];
|
|
}
|
|
|
|
if (USB_DeviceSendRequest(g_deviceHandle, endpoint, ep_buf, size) != kStatus_USB_Success) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void USBPhyHw::endpoint_abort(usb_ep_t endpoint)
|
|
{
|
|
USB_DeviceCancel(g_deviceHandle, endpoint);
|
|
}
|
|
|
|
void USBPhyHw::process()
|
|
{
|
|
uint32_t istat = usb_addrs[USB_INSTANCE]->USBSTS & usb_addrs[USB_INSTANCE]->USBINTR;
|
|
|
|
// SOF interrupt
|
|
if (istat & USBHS_USBSTS_SRI_MASK) {
|
|
// SOF event, read frame number
|
|
events->sof(frameNumber());
|
|
}
|
|
|
|
USB_DeviceEhciIsrFunction(g_deviceHandle);
|
|
|
|
NVIC_ClearPendingIRQ((IRQn_Type)usb_irq);
|
|
NVIC_EnableIRQ((IRQn_Type)usb_irq);
|
|
}
|
|
|
|
void USBPhyHw::_usbisr(void)
|
|
{
|
|
NVIC_DisableIRQ((IRQn_Type)usb_irq);
|
|
instance->events->start_process();
|
|
}
|
|
|
|
#endif
|