mirror of https://github.com/ARMmbed/mbed-os.git
				
				
				
			
		
			
				
	
	
		
			726 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
			
		
		
	
	
			726 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
/* mbed Microcontroller Library
 | 
						|
 * Copyright (c) 2018-2018 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 && \
 | 
						|
    (defined(TARGET_KL25Z) | defined(TARGET_KL43Z) | \
 | 
						|
     defined(TARGET_KL46Z) | \
 | 
						|
     defined(TARGET_K64F) | defined(TARGET_K22F) | defined(TARGET_K82F))
 | 
						|
 | 
						|
#if defined(TARGET_KSDK2_MCUS)
 | 
						|
#include "fsl_common.h"
 | 
						|
#endif
 | 
						|
#include "USBPhyHw.h"
 | 
						|
#include "USBEndpoints_Kinetis.h"
 | 
						|
#include "mbed_critical.h"
 | 
						|
#include "platform/mbed_power_mgmt.h"
 | 
						|
#include "cmsis.h"
 | 
						|
 | 
						|
 | 
						|
static USBPhyHw *instance;
 | 
						|
 | 
						|
static volatile int epComplete = 0;
 | 
						|
 | 
						|
// 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 BD_OWN_MASK        (1<<7)
 | 
						|
#define BD_DATA01_MASK     (1<<6)
 | 
						|
#define BD_KEEP_MASK       (1<<5)
 | 
						|
#define BD_NINC_MASK       (1<<4)
 | 
						|
#define BD_DTS_MASK        (1<<3)
 | 
						|
#define BD_STALL_MASK      (1<<2)
 | 
						|
 | 
						|
#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)))
 | 
						|
 | 
						|
#define SETUP_TOKEN    0x0D
 | 
						|
#define IN_TOKEN       0x09
 | 
						|
#define OUT_TOKEN      0x01
 | 
						|
#define TOK_PID(idx)   ((bdt[idx].info >> 2) & 0x0F)
 | 
						|
 | 
						|
// for each endpt: 8 bytes
 | 
						|
typedef struct BDT {
 | 
						|
    uint8_t   info;       // BD[0:7]
 | 
						|
    uint8_t   dummy;      // RSVD: BD[8:15]
 | 
						|
    uint16_t  byte_count; // BD[16:32]
 | 
						|
    uint32_t  address;    // Addr
 | 
						|
} BDT;
 | 
						|
 | 
						|
typedef enum {
 | 
						|
    CTRL_XFER_READY,
 | 
						|
    CTRL_XFER_IN,
 | 
						|
    CTRL_XFER_NONE,
 | 
						|
    CTRL_XFER_OUT
 | 
						|
} ctrl_xfer_t;
 | 
						|
 | 
						|
// there are:
 | 
						|
//    * 4 bidirectionnal endpt -> 8 physical endpt
 | 
						|
//    * as there are ODD and EVEN buffer -> 8*2 bdt
 | 
						|
MBED_ALIGN(512) BDT bdt[NUMBER_OF_PHYSICAL_ENDPOINTS * 2];  // 512 bytes aligned!
 | 
						|
 | 
						|
uint8_t *endpoint_buffer[NUMBER_OF_PHYSICAL_ENDPOINTS * 2];
 | 
						|
uint8_t ep0_buffer[2][MAX_PACKET_SIZE_EP0];
 | 
						|
uint8_t ep1_buffer[2][MAX_PACKET_SIZE_EP1];
 | 
						|
uint8_t ep2_buffer[2][MAX_PACKET_SIZE_EP2];
 | 
						|
uint8_t ep3_buffer[2][MAX_PACKET_SIZE_EP3];
 | 
						|
 | 
						|
static bool setup_suspend = false;
 | 
						|
static uint8_t set_addr = 0;
 | 
						|
static uint8_t addr = 0;
 | 
						|
static ctrl_xfer_t ctrl_xfer = CTRL_XFER_READY;
 | 
						|
 | 
						|
static uint32_t Data1  = 0x55555555;
 | 
						|
 | 
						|
static uint32_t frameNumber()
 | 
						|
{
 | 
						|
    return ((USB0->FRMNUML | (USB0->FRMNUMH << 8)) & 0x07FF);
 | 
						|
}
 | 
						|
 | 
						|
USBPhy *get_usb_phy()
 | 
						|
{
 | 
						|
    static USBPhyHw usbphy;
 | 
						|
    return &usbphy;
 | 
						|
}
 | 
						|
 | 
						|
USBPhyHw::USBPhyHw(): events(NULL)
 | 
						|
{
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
USBPhyHw::~USBPhyHw()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::init(USBPhyEvents *events)
 | 
						|
{
 | 
						|
    if (this->events == NULL) {
 | 
						|
        sleep_manager_lock_deep_sleep();
 | 
						|
    }
 | 
						|
    this->events = events;
 | 
						|
 | 
						|
    // Disable IRQ
 | 
						|
    NVIC_DisableIRQ(USB0_IRQn);
 | 
						|
 | 
						|
#if (defined(FSL_FEATURE_SOC_MPU_COUNT) && (FSL_FEATURE_SOC_MPU_COUNT > 0U))
 | 
						|
    MPU->CESR = 0;
 | 
						|
#endif
 | 
						|
 | 
						|
#if (defined(FSL_FEATURE_SOC_SYSMPU_COUNT) && (FSL_FEATURE_SOC_SYSMPU_COUNT > 0U))
 | 
						|
    SYSMPU->CESR = 0;
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(TARGET_KL43Z) || defined(TARGET_K22F) || defined(TARGET_K64F) || defined(TARGET_K82F)
 | 
						|
    // enable USBFS clock
 | 
						|
    CLOCK_EnableUsbfs0Clock(kCLOCK_UsbSrcIrc48M, 48000000U);
 | 
						|
#else
 | 
						|
    // choose usb src as PLL
 | 
						|
    SIM->SOPT2 &= ~SIM_SOPT2_PLLFLLSEL_MASK;
 | 
						|
    SIM->SOPT2 |= (SIM_SOPT2_USBSRC_MASK | (1 << SIM_SOPT2_PLLFLLSEL_SHIFT));
 | 
						|
 | 
						|
    // enable OTG clock
 | 
						|
    SIM->SCGC4 |= SIM_SCGC4_USBOTG_MASK;
 | 
						|
#endif
 | 
						|
 | 
						|
    // Attach IRQ
 | 
						|
    instance = this;
 | 
						|
 | 
						|
    // USB Module Configuration
 | 
						|
    // Set BDT Base Register
 | 
						|
    USB0->BDTPAGE1 = (uint8_t)((uint32_t)bdt >> 8);
 | 
						|
    USB0->BDTPAGE2 = (uint8_t)((uint32_t)bdt >> 16);
 | 
						|
    USB0->BDTPAGE3 = (uint8_t)((uint32_t)bdt >> 24);
 | 
						|
 | 
						|
    // Clear interrupt flag
 | 
						|
    USB0->ISTAT = 0xff;
 | 
						|
 | 
						|
    // USB Interrupt Enablers
 | 
						|
    USB0->INTEN |= USB_INTEN_TOKDNEEN_MASK |
 | 
						|
                   USB_INTEN_ERROREN_MASK  |
 | 
						|
                   USB_INTEN_USBRSTEN_MASK;
 | 
						|
 | 
						|
    // Disable weak pull downs
 | 
						|
    USB0->USBCTRL &= ~(USB_USBCTRL_PDE_MASK | USB_USBCTRL_SUSP_MASK);
 | 
						|
 | 
						|
    USB0->USBTRC0 |= 0x40;
 | 
						|
 | 
						|
    /* Allocate control endpoint buffers */
 | 
						|
    endpoint_buffer[EP_BDT_IDX(0, TX, ODD)] = ep0_buffer[TX];
 | 
						|
    endpoint_buffer[EP_BDT_IDX(0, RX, ODD)] = ep0_buffer[RX];
 | 
						|
    endpoint_buffer[EP_BDT_IDX(1, TX, ODD)] = ep1_buffer[TX];
 | 
						|
    endpoint_buffer[EP_BDT_IDX(1, RX, ODD)] = ep1_buffer[RX];
 | 
						|
    endpoint_buffer[EP_BDT_IDX(2, TX, ODD)] = ep2_buffer[TX];
 | 
						|
    endpoint_buffer[EP_BDT_IDX(2, RX, ODD)] = ep2_buffer[RX];
 | 
						|
    endpoint_buffer[EP_BDT_IDX(3, TX, ODD)] = ep3_buffer[TX];
 | 
						|
    endpoint_buffer[EP_BDT_IDX(3, RX, ODD)] = ep3_buffer[RX];
 | 
						|
 | 
						|
    NVIC_SetVector(USB0_IRQn, (uint32_t)&_usbisr);
 | 
						|
    NVIC_EnableIRQ(USB0_IRQn);
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::deinit()
 | 
						|
{
 | 
						|
    disconnect();
 | 
						|
    NVIC_DisableIRQ(USB0_IRQn);
 | 
						|
    USB0->INTEN = 0;
 | 
						|
 | 
						|
    if (events != NULL) {
 | 
						|
        sleep_manager_unlock_deep_sleep();
 | 
						|
    }
 | 
						|
    events = NULL;
 | 
						|
}
 | 
						|
 | 
						|
bool USBPhyHw::powered()
 | 
						|
{
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::connect()
 | 
						|
{
 | 
						|
    // enable USB
 | 
						|
    USB0->CTL |= USB_CTL_USBENSOFEN_MASK;
 | 
						|
    // Pull up enable
 | 
						|
    USB0->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::disconnect()
 | 
						|
{
 | 
						|
    // disable all endpoints to prevent them from nacking when disconnected
 | 
						|
    for (int i = 0; i < 16; i++) {
 | 
						|
        USB0->ENDPOINT[i].ENDPT = 0x00;
 | 
						|
    }
 | 
						|
    // disable USB
 | 
						|
    USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK;
 | 
						|
    // Pull up disable
 | 
						|
    USB0->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
 | 
						|
 | 
						|
    while (USB0->ISTAT) {
 | 
						|
        USB0->ISTAT = 0xFF;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::configure()
 | 
						|
{
 | 
						|
    // not needed
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::unconfigure()
 | 
						|
{
 | 
						|
    // not needed
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::sof_enable()
 | 
						|
{
 | 
						|
    USB0->INTEN |= USB_INTEN_SOFTOKEN_MASK;
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::sof_disable()
 | 
						|
{
 | 
						|
    USB0->INTEN &= ~USB_INTEN_SOFTOKEN_MASK;
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::set_address(uint8_t address)
 | 
						|
{
 | 
						|
    // we don't set the address now otherwise the usb controller does not ack
 | 
						|
    // we set a flag instead
 | 
						|
    // see usbisr when an IN token is received
 | 
						|
    set_addr = 1;
 | 
						|
    addr = address;
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::remote_wakeup()
 | 
						|
{
 | 
						|
    // TODO
 | 
						|
}
 | 
						|
 | 
						|
#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 MAX_PACKET_SIZE_EP0;
 | 
						|
}
 | 
						|
 | 
						|
// read setup packet
 | 
						|
void USBPhyHw::ep0_setup_read_result(uint8_t *buffer, uint32_t size)
 | 
						|
{
 | 
						|
    uint32_t sz;
 | 
						|
    endpoint_read_result_core(EP0OUT, buffer, size, &sz);
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::ep0_read(uint8_t *data, uint32_t size)
 | 
						|
{
 | 
						|
    if (ctrl_xfer == CTRL_XFER_READY) {
 | 
						|
        // Transfer is done so ignore call
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    if (ctrl_xfer == CTRL_XFER_IN) {
 | 
						|
        ctrl_xfer = CTRL_XFER_READY;
 | 
						|
        // Control transfer with a data IN stage.
 | 
						|
        // The next packet received will be the status packet - an OUT packet using DATA1
 | 
						|
        //
 | 
						|
        // PROBLEM:
 | 
						|
        // If a Setup packet is received after status packet of
 | 
						|
        // a Control In transfer has been received in the RX buffer
 | 
						|
        // but before the processor has had a chance the prepare
 | 
						|
        // this buffer for the Setup packet, the Setup packet
 | 
						|
        // will be dropped.
 | 
						|
        //
 | 
						|
        // WORKAROUND:
 | 
						|
        // Set data toggle to DATA0 so if the status stage of a
 | 
						|
        // Control In transfer arrives it will be ACKed by hardware
 | 
						|
        // but will be discarded without filling the RX buffer.
 | 
						|
        // This allows a subsequent SETUP packet to be stored
 | 
						|
        // without any processor intervention.
 | 
						|
        Data1 &= ~1UL;  // set DATA0
 | 
						|
        endpoint_read_core(EP0OUT, MAX_PACKET_SIZE_EP0);
 | 
						|
    } else {
 | 
						|
        endpoint_read(EP0OUT, data, size);
 | 
						|
    }
 | 
						|
 | 
						|
    // Clear suspend after the setup stage
 | 
						|
    if (setup_suspend) {
 | 
						|
        USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
 | 
						|
        setup_suspend = false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
uint32_t USBPhyHw::ep0_read_result()
 | 
						|
{
 | 
						|
    return endpoint_read_result(EP0OUT);
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::ep0_write(uint8_t *buffer, uint32_t size)
 | 
						|
{
 | 
						|
    if (ctrl_xfer == CTRL_XFER_READY) {
 | 
						|
        // Transfer is done so ignore call
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    if ((ctrl_xfer == CTRL_XFER_NONE) || (ctrl_xfer == CTRL_XFER_OUT)) {
 | 
						|
        // Prepare for next setup packet
 | 
						|
        endpoint_read_core(EP0OUT, MAX_PACKET_SIZE_EP0);
 | 
						|
        ctrl_xfer = CTRL_XFER_READY;
 | 
						|
    }
 | 
						|
    endpoint_write(EP0IN, buffer, size);
 | 
						|
 | 
						|
    // Clear suspend after the setup stage
 | 
						|
    if (setup_suspend) {
 | 
						|
        USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
 | 
						|
        setup_suspend = false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::ep0_stall()
 | 
						|
{
 | 
						|
    if (ctrl_xfer == CTRL_XFER_READY) {
 | 
						|
        // Transfer is done so ignore call
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    ctrl_xfer = CTRL_XFER_READY;
 | 
						|
 | 
						|
    // Clear suspend after the setup stage
 | 
						|
    if (setup_suspend) {
 | 
						|
        USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
 | 
						|
        setup_suspend = false;
 | 
						|
    }
 | 
						|
 | 
						|
    core_util_critical_section_enter();
 | 
						|
    endpoint_stall(EP0OUT);
 | 
						|
    // Prepare for next setup packet
 | 
						|
    // Note - time between stalling and setting up the endpoint
 | 
						|
    //      must be kept to a minimum to prevent a dropped SETUP
 | 
						|
    //      packet.
 | 
						|
    endpoint_read_core(EP0OUT, MAX_PACKET_SIZE_EP0);
 | 
						|
    core_util_critical_section_exit();
 | 
						|
}
 | 
						|
 | 
						|
bool USBPhyHw::endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type)
 | 
						|
{
 | 
						|
    uint32_t handshake_flag = 0;
 | 
						|
    uint8_t *buf;
 | 
						|
 | 
						|
    if (DESC_TO_PHY(endpoint) > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    uint32_t log_endpoint = DESC_TO_LOG(endpoint);
 | 
						|
 | 
						|
    if (type != USB_EP_TYPE_ISO) {
 | 
						|
        handshake_flag = USB_ENDPT_EPHSHK_MASK;
 | 
						|
    }
 | 
						|
 | 
						|
    if (DESC_EP_IN(endpoint)) {
 | 
						|
        buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)][0];
 | 
						|
    } else {
 | 
						|
        buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)][0];
 | 
						|
    }
 | 
						|
 | 
						|
    // IN endpt -> device to host (TX)
 | 
						|
    if (DESC_EP_IN(endpoint)) {
 | 
						|
        bdt[EP_BDT_IDX(log_endpoint, TX, ODD)].address = (uint32_t) buf;
 | 
						|
        bdt[EP_BDT_IDX(log_endpoint, TX, ODD)].info       = 0;
 | 
						|
        bdt[EP_BDT_IDX(log_endpoint, TX, EVEN)].address = 0;
 | 
						|
        USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag |        // ep handshaking (not if iso endpoint)
 | 
						|
                                              USB_ENDPT_EPTXEN_MASK;  // en TX (IN) tran
 | 
						|
    }
 | 
						|
    // OUT endpt -> host to device (RX)
 | 
						|
    else {
 | 
						|
        bdt[EP_BDT_IDX(log_endpoint, RX, ODD)].byte_count = max_packet;
 | 
						|
        bdt[EP_BDT_IDX(log_endpoint, RX, ODD)].address    = (uint32_t) buf;
 | 
						|
        bdt[EP_BDT_IDX(log_endpoint, RX, ODD)].info       = BD_DTS_MASK;
 | 
						|
        bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].info       = 0;
 | 
						|
        if (log_endpoint == 0) {
 | 
						|
            // Prepare for setup packet
 | 
						|
            bdt[EP_BDT_IDX(log_endpoint, RX, ODD)].info |= BD_OWN_MASK;
 | 
						|
        }
 | 
						|
        USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag |        // ep handshaking (not if iso endpoint)
 | 
						|
                                              USB_ENDPT_EPRXEN_MASK;  // en RX (OUT) tran.
 | 
						|
    }
 | 
						|
 | 
						|
    // First transfer will be a DATA0 packet
 | 
						|
    Data1 &= ~(1 << DESC_TO_PHY(endpoint));
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::endpoint_remove(usb_ep_t endpoint)
 | 
						|
{
 | 
						|
    USB0->ENDPOINT[DESC_TO_LOG(endpoint)].ENDPT = 0;
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::endpoint_stall(usb_ep_t endpoint)
 | 
						|
{
 | 
						|
    USB0->ENDPOINT[DESC_TO_LOG(endpoint)].ENDPT |= USB_ENDPT_EPSTALL_MASK;
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::endpoint_unstall(usb_ep_t endpoint)
 | 
						|
{
 | 
						|
    // Next transfer will be a DATA0 packet
 | 
						|
    Data1 &= ~(1 << DESC_TO_PHY(endpoint));
 | 
						|
    USB0->ENDPOINT[DESC_TO_LOG(endpoint)].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
 | 
						|
}
 | 
						|
 | 
						|
bool USBPhyHw::endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size)
 | 
						|
{
 | 
						|
    uint8_t log = DESC_TO_LOG(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);
 | 
						|
 | 
						|
    uint32_t idx = EP_BDT_IDX(log_endpoint, RX, 0);
 | 
						|
    bdt[idx].byte_count = max_packet;
 | 
						|
    if ((Data1 >> DESC_TO_PHY(endpoint)) & 1) {
 | 
						|
        bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | BD_DATA01_MASK;
 | 
						|
    } else {
 | 
						|
        bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK;
 | 
						|
    }
 | 
						|
 | 
						|
    Data1 ^= (1 << DESC_TO_PHY(endpoint));
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
uint32_t USBPhyHw::endpoint_read_result(usb_ep_t endpoint)
 | 
						|
{
 | 
						|
    uint8_t log = DESC_TO_LOG(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, sz, idx, setup = 0;
 | 
						|
    uint8_t not_iso;
 | 
						|
    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);
 | 
						|
    sz  = bdt[idx].byte_count;
 | 
						|
    not_iso = USB0->ENDPOINT[log_endpoint].ENDPT & USB_ENDPT_EPHSHK_MASK;
 | 
						|
 | 
						|
    //for isochronous endpoint, we don't wait an interrupt
 | 
						|
    if ((log_endpoint != 0) && not_iso && !(epComplete & EP(DESC_TO_PHY(endpoint)))) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((log_endpoint == 0) && (TOK_PID(idx) == SETUP_TOKEN)) {
 | 
						|
        setup = 1;
 | 
						|
    }
 | 
						|
 | 
						|
    ep_buf = endpoint_buffer[idx];
 | 
						|
 | 
						|
    for (n = 0; n < sz; n++) {
 | 
						|
        data[n] = ep_buf[n];
 | 
						|
    }
 | 
						|
 | 
						|
    if (setup) {
 | 
						|
        // Record the setup type
 | 
						|
        if ((data[6] == 0) && (data[7] == 0))  {
 | 
						|
            ctrl_xfer = CTRL_XFER_NONE;
 | 
						|
        } else {
 | 
						|
            uint8_t in_xfer = (data[0] >> 7) & 1;
 | 
						|
            ctrl_xfer = in_xfer ? CTRL_XFER_IN : CTRL_XFER_OUT;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    *bytes_read = sz;
 | 
						|
 | 
						|
    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);
 | 
						|
    bdt[idx].byte_count = size;
 | 
						|
 | 
						|
    ep_buf = endpoint_buffer[idx];
 | 
						|
 | 
						|
    for (n = 0; n < size; n++) {
 | 
						|
        ep_buf[n] = data[n];
 | 
						|
    }
 | 
						|
 | 
						|
    if ((Data1 >> DESC_TO_PHY(endpoint)) & 1) {
 | 
						|
        bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | BD_DATA01_MASK;
 | 
						|
    } else {
 | 
						|
        bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK;
 | 
						|
    }
 | 
						|
 | 
						|
    Data1 ^= (1 << DESC_TO_PHY(endpoint));
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::endpoint_abort(usb_ep_t endpoint)
 | 
						|
{
 | 
						|
    uint8_t dir = DESC_EP_IN(endpoint) ? TX : RX;
 | 
						|
    uint32_t idx = EP_BDT_IDX(DESC_TO_LOG(endpoint), dir, 0);
 | 
						|
    bdt[idx].info &= ~BD_OWN_MASK;
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::process()
 | 
						|
{
 | 
						|
    uint8_t i;
 | 
						|
    uint8_t istat = USB0->ISTAT & USB0->INTEN;
 | 
						|
 | 
						|
    // reset interrupt
 | 
						|
    if (istat & USB_ISTAT_USBRST_MASK) {
 | 
						|
        // disable all endpt
 | 
						|
        for (i = 0; i < 16; i++) {
 | 
						|
            USB0->ENDPOINT[i].ENDPT = 0x00;
 | 
						|
        }
 | 
						|
 | 
						|
        // enable control endpoint
 | 
						|
        setup_suspend = false;
 | 
						|
        endpoint_add(EP0OUT, MAX_PACKET_SIZE_EP0, USB_EP_TYPE_CTRL);
 | 
						|
        endpoint_add(EP0IN, MAX_PACKET_SIZE_EP0, USB_EP_TYPE_CTRL);
 | 
						|
 | 
						|
        Data1 = 0x55555555;
 | 
						|
        USB0->CTL |=  USB_CTL_ODDRST_MASK;
 | 
						|
        USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
 | 
						|
 | 
						|
        USB0->ISTAT   =  0xFF;  // clear all interrupt status flags
 | 
						|
        USB0->ERRSTAT =  0xFF;  // clear all error flags
 | 
						|
        USB0->ERREN   =  0xFF;  // enable error interrupt sources
 | 
						|
        USB0->ADDR    =  0x00;  // set default address
 | 
						|
 | 
						|
        memset(read_buffers, 0, sizeof(read_buffers));
 | 
						|
        memset(read_sizes, 0, sizeof(read_sizes));
 | 
						|
 | 
						|
        // reset bus for USBDevice layer
 | 
						|
        events->reset();
 | 
						|
 | 
						|
        NVIC_ClearPendingIRQ(USB0_IRQn);
 | 
						|
        NVIC_EnableIRQ(USB0_IRQn);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // resume interrupt
 | 
						|
    if (istat & USB_ISTAT_RESUME_MASK) {
 | 
						|
        USB0->ISTAT = USB_ISTAT_RESUME_MASK;
 | 
						|
        events->suspend(false);
 | 
						|
    }
 | 
						|
 | 
						|
    // SOF interrupt
 | 
						|
    if (istat & USB_ISTAT_SOFTOK_MASK) {
 | 
						|
        USB0->ISTAT = USB_ISTAT_SOFTOK_MASK;
 | 
						|
        // SOF event, read frame number
 | 
						|
        events->sof(frameNumber());
 | 
						|
    }
 | 
						|
 | 
						|
    // stall interrupt
 | 
						|
    if (istat & 1 << 7) {
 | 
						|
        if (USB0->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK) {
 | 
						|
            USB0->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
 | 
						|
        }
 | 
						|
        USB0->ISTAT = USB_ISTAT_STALL_MASK;
 | 
						|
    }
 | 
						|
 | 
						|
    // token interrupt
 | 
						|
    if (istat & USB_ISTAT_TOKDNE_MASK) {
 | 
						|
        uint32_t num  = (USB0->STAT >> 4) & 0x0F;
 | 
						|
        uint32_t dir  = (USB0->STAT >> 3) & 0x01;
 | 
						|
        uint32_t ev_odd = (USB0->STAT >> 2) & 0x01;
 | 
						|
        int phy_ep = (num << 1) | dir;
 | 
						|
 | 
						|
        bool tx_en = (USB0->ENDPOINT[PHY_TO_LOG(phy_ep)].ENDPT & USB_ENDPT_EPTXEN_MASK) ? true : false;
 | 
						|
        bool rx_en = (USB0->ENDPOINT[PHY_TO_LOG(phy_ep)].ENDPT & USB_ENDPT_EPRXEN_MASK) ? true : false;
 | 
						|
 | 
						|
        // setup packet
 | 
						|
        if (tx_en && (num == 0) && (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == SETUP_TOKEN)) {
 | 
						|
            setup_suspend = true;
 | 
						|
            Data1 |= 0x02 | 0x01; // set DATA1 for TX and RX
 | 
						|
            bdt[EP_BDT_IDX(0, TX, EVEN)].info &= ~BD_OWN_MASK;
 | 
						|
            bdt[EP_BDT_IDX(0, TX, ODD)].info  &= ~BD_OWN_MASK;
 | 
						|
 | 
						|
            // EP0 SETUP event (SETUP data received)
 | 
						|
            events->ep0_setup();
 | 
						|
 | 
						|
        } else {
 | 
						|
            // OUT packet
 | 
						|
            if (rx_en && (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == OUT_TOKEN)) {
 | 
						|
                if (num == 0) {
 | 
						|
                    events->ep0_out();
 | 
						|
                } else {
 | 
						|
                    epComplete |= EP(phy_ep);
 | 
						|
                    events->out(PHY_TO_DESC(phy_ep));
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // IN packet
 | 
						|
            if (tx_en && (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == IN_TOKEN)) {
 | 
						|
                if (num == 0) {
 | 
						|
                    events->ep0_in();
 | 
						|
                    if (set_addr == 1) {
 | 
						|
                        USB0->ADDR = addr & 0x7F;
 | 
						|
                        set_addr = 0;
 | 
						|
                    }
 | 
						|
                } else {
 | 
						|
                    epComplete |= EP(phy_ep);
 | 
						|
                    events->in(PHY_TO_DESC(phy_ep));
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        USB0->ISTAT = USB_ISTAT_TOKDNE_MASK;
 | 
						|
    }
 | 
						|
 | 
						|
    // sleep interrupt
 | 
						|
    if (istat & 1 << 4) {
 | 
						|
        USB0->ISTAT = USB_ISTAT_SLEEP_MASK;
 | 
						|
        events->suspend(true);
 | 
						|
    }
 | 
						|
 | 
						|
    // error interrupt
 | 
						|
    if (istat & USB_ISTAT_ERROR_MASK) {
 | 
						|
        USB0->ERRSTAT = 0xFF;
 | 
						|
        USB0->ISTAT = USB_ISTAT_ERROR_MASK;
 | 
						|
    }
 | 
						|
 | 
						|
    // Check if the suspend condition should be removed here
 | 
						|
    // 1. Don't attempt to clear USB_CTL_TXSUSPENDTOKENBUSY_MASK if it isn't set. This
 | 
						|
    //      is to avoid potential race conditions.
 | 
						|
    // 2. If a setup packet is being processed then remove suspend on the next control transfer rather than here
 | 
						|
    // 3. Process all pending packets before removing suspend
 | 
						|
    bool suspended = (USB0->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) != 0;
 | 
						|
    if (suspended && !setup_suspend && ((USB0->ISTAT & USB_ISTAT_TOKDNE_MASK) == 0)) {
 | 
						|
        USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
 | 
						|
    }
 | 
						|
 | 
						|
    NVIC_ClearPendingIRQ(USB0_IRQn);
 | 
						|
    NVIC_EnableIRQ(USB0_IRQn);
 | 
						|
}
 | 
						|
 | 
						|
void USBPhyHw::_usbisr(void)
 | 
						|
{
 | 
						|
    NVIC_DisableIRQ(USB0_IRQn);
 | 
						|
    instance->events->start_process();
 | 
						|
}
 | 
						|
 | 
						|
#endif
 |