402 lines
14 KiB
C
402 lines
14 KiB
C
/*
|
|
Copyright (c) 2015 Arduino LLC. All right reserved.
|
|
Copyright (c) 2015 Atmel Corporation/Thibaut VIARD. All right reserved.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
See the GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "board_driver_usb.h"
|
|
#include "sam_ba_usb.h"
|
|
#include "sam_ba_cdc.h"
|
|
|
|
#define NVM_USB_PAD_TRANSN_POS (45)
|
|
#define NVM_USB_PAD_TRANSN_SIZE (5)
|
|
#define NVM_USB_PAD_TRANSP_POS (50)
|
|
#define NVM_USB_PAD_TRANSP_SIZE (5)
|
|
#define NVM_USB_PAD_TRIM_POS (55)
|
|
#define NVM_USB_PAD_TRIM_SIZE (3)
|
|
|
|
__attribute__((__aligned__(4))) UsbDeviceDescriptor usb_endpoint_table[MAX_EP]; // Initialized to zero in USB_Init
|
|
__attribute__((__aligned__(4))) uint8_t udd_ep_out_cache_buffer[2][64]; //1 for CTRL, 1 for BULK
|
|
__attribute__((__aligned__(4))) uint8_t udd_ep_in_cache_buffer[2][64]; //1 for CTRL, 1 for BULK
|
|
|
|
static volatile bool read_job = false;
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* \brief
|
|
*/
|
|
P_USB_CDC USB_Open(P_USB_CDC pCdc, Usb *pUsb)
|
|
{
|
|
pCdc->pUsb = pUsb;
|
|
pCdc->currentConfiguration = 0;
|
|
pCdc->currentConnection = 0;
|
|
pCdc->IsConfigured = USB_IsConfigured;
|
|
// pCdc->Write = USB_Write;
|
|
// pCdc->Read = USB_Read;
|
|
|
|
pCdc->pUsb->HOST.CTRLA.bit.ENABLE = true;
|
|
|
|
return pCdc;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* \brief Initializes USB
|
|
*/
|
|
void USB_Init(void)
|
|
{
|
|
uint32_t pad_transn, pad_transp, pad_trim;
|
|
|
|
/* Enable USB clock */
|
|
PM->APBBMASK.reg |= PM_APBBMASK_USB;
|
|
|
|
/* Set up the USB DP/DN pins */
|
|
PORT->Group[0].PINCFG[PIN_PA24G_USB_DM].bit.PMUXEN = 1;
|
|
PORT->Group[0].PMUX[PIN_PA24G_USB_DM/2].reg &= ~(0xF << (4 * (PIN_PA24G_USB_DM & 0x01u)));
|
|
PORT->Group[0].PMUX[PIN_PA24G_USB_DM/2].reg |= MUX_PA24G_USB_DM << (4 * (PIN_PA24G_USB_DM & 0x01u));
|
|
PORT->Group[0].PINCFG[PIN_PA25G_USB_DP].bit.PMUXEN = 1;
|
|
PORT->Group[0].PMUX[PIN_PA25G_USB_DP/2].reg &= ~(0xF << (4 * (PIN_PA25G_USB_DP & 0x01u)));
|
|
PORT->Group[0].PMUX[PIN_PA25G_USB_DP/2].reg |= MUX_PA25G_USB_DP << (4 * (PIN_PA25G_USB_DP & 0x01u));
|
|
|
|
/* ----------------------------------------------------------------------------------------------
|
|
* Put Generic Clock Generator 0 as source for Generic Clock Multiplexer 6 (USB reference)
|
|
*/
|
|
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID( 6 ) | // Generic Clock Multiplexer 6
|
|
GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
|
|
GCLK_CLKCTRL_CLKEN ;
|
|
|
|
while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY )
|
|
{
|
|
/* Wait for synchronization */
|
|
}
|
|
|
|
/* Reset */
|
|
USB->DEVICE.CTRLA.bit.SWRST = 1;
|
|
while (USB->DEVICE.SYNCBUSY.bit.SWRST)
|
|
{
|
|
/* Sync wait */
|
|
}
|
|
|
|
/* Load Pad Calibration */
|
|
pad_transn =( *((uint32_t *)(NVMCTRL_OTP4)
|
|
+ (NVM_USB_PAD_TRANSN_POS / 32))
|
|
>> (NVM_USB_PAD_TRANSN_POS % 32))
|
|
& ((1 << NVM_USB_PAD_TRANSN_SIZE) - 1);
|
|
|
|
if (pad_transn == 0x1F)
|
|
{
|
|
pad_transn = 5;
|
|
}
|
|
|
|
USB->HOST.PADCAL.bit.TRANSN = pad_transn;
|
|
|
|
pad_transp =( *((uint32_t *)(NVMCTRL_OTP4)
|
|
+ (NVM_USB_PAD_TRANSP_POS / 32))
|
|
>> (NVM_USB_PAD_TRANSP_POS % 32))
|
|
& ((1 << NVM_USB_PAD_TRANSP_SIZE) - 1);
|
|
|
|
if (pad_transp == 0x1F)
|
|
{
|
|
pad_transp = 29;
|
|
}
|
|
|
|
USB->HOST.PADCAL.bit.TRANSP = pad_transp;
|
|
pad_trim =( *((uint32_t *)(NVMCTRL_OTP4)
|
|
+ (NVM_USB_PAD_TRIM_POS / 32))
|
|
>> (NVM_USB_PAD_TRIM_POS % 32))
|
|
& ((1 << NVM_USB_PAD_TRIM_SIZE) - 1);
|
|
|
|
if (pad_trim == 0x7)
|
|
{
|
|
pad_trim = 3;
|
|
}
|
|
|
|
USB->HOST.PADCAL.bit.TRIM = pad_trim;
|
|
|
|
/* Set the configuration */
|
|
/* Set mode to Device mode */
|
|
USB->HOST.CTRLA.bit.MODE = 0;
|
|
/* Enable Run in Standby */
|
|
USB->HOST.CTRLA.bit.RUNSTDBY = true;
|
|
/* Set the descriptor address */
|
|
USB->HOST.DESCADD.reg = (uint32_t)(&usb_endpoint_table[0]);
|
|
/* Set speed configuration to Full speed */
|
|
USB->DEVICE.CTRLB.bit.SPDCONF = USB_DEVICE_CTRLB_SPDCONF_FS_Val;
|
|
/* Attach to the USB host */
|
|
USB->DEVICE.CTRLB.reg &= ~USB_DEVICE_CTRLB_DETACH;
|
|
|
|
/* Initialize endpoint table RAM location to a known value 0 */
|
|
memset((uint8_t *)(&usb_endpoint_table[0]), 0, sizeof(usb_endpoint_table));
|
|
}
|
|
|
|
uint32_t USB_Write(Usb *pUsb, const char *pData, uint32_t length, uint8_t ep_num)
|
|
{
|
|
uint32_t data_address;
|
|
uint8_t buf_index;
|
|
|
|
/* Set buffer index */
|
|
buf_index = (ep_num == 0) ? 0 : 1;
|
|
|
|
/* Check for requirement for multi-packet or auto zlp */
|
|
if (length >= (1 << (usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.bit.SIZE + 3)))
|
|
{
|
|
/* Update the EP data address */
|
|
data_address = (uint32_t) pData;
|
|
/* Enable auto zlp */
|
|
usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.bit.AUTO_ZLP = true;
|
|
}
|
|
else
|
|
{
|
|
/* Copy to local buffer */
|
|
memcpy(udd_ep_in_cache_buffer[buf_index], pData, length);
|
|
/* Update the EP data address */
|
|
data_address = (uint32_t) &udd_ep_in_cache_buffer[buf_index];
|
|
}
|
|
|
|
/* Set the buffer address for ep data */
|
|
usb_endpoint_table[ep_num].DeviceDescBank[1].ADDR.reg = data_address;
|
|
/* Set the byte count as zero */
|
|
usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.bit.BYTE_COUNT = length;
|
|
/* Set the multi packet size as zero for multi-packet transfers where length > ep size */
|
|
usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
|
|
/* Clear the transfer complete flag */
|
|
|
|
#ifdef __SAMR21G18A__
|
|
pUsb->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.bit.TRCPT1 = true;
|
|
#else
|
|
pUsb->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.bit.TRCPT |= (1<<1);
|
|
#endif
|
|
|
|
/* Set the bank as ready */
|
|
pUsb->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.bit.BK1RDY = true;
|
|
|
|
/* Wait for transfer to complete */
|
|
#ifdef __SAMR21G18A__
|
|
while ( (pUsb->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.bit.TRCPT1) == 0 );
|
|
#else
|
|
while ( (pUsb->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.bit.TRCPT & (1<<1)) == 0 );
|
|
#endif
|
|
|
|
return length;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* \brief Read available data from Endpoint OUT
|
|
*/
|
|
uint32_t USB_Read(Usb *pUsb, char *pData, uint32_t length)
|
|
{
|
|
uint32_t packetSize = 0;
|
|
|
|
if (!read_job)
|
|
{
|
|
/* Set the buffer address for ep data */
|
|
usb_endpoint_table[USB_EP_OUT].DeviceDescBank[0].ADDR.reg = (uint32_t)&udd_ep_out_cache_buffer[USB_EP_OUT-1];
|
|
/* Set the byte count as zero */
|
|
usb_endpoint_table[USB_EP_OUT].DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0;
|
|
/* Set the byte count as zero */
|
|
usb_endpoint_table[USB_EP_OUT].DeviceDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
|
|
/* Start the reception by clearing the bank 0 ready bit */
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPSTATUSCLR.bit.BK0RDY = true;
|
|
/* set the user flag */
|
|
read_job = true;
|
|
}
|
|
|
|
/* Check for Transfer Complete 0 flag */
|
|
#ifdef __SAMR21G18A__
|
|
if ( pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPINTFLAG.bit.TRCPT0 )
|
|
#else
|
|
if ( pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPINTFLAG.bit.TRCPT & (1<<0) )
|
|
#endif
|
|
{
|
|
/* Set packet size */
|
|
packetSize = SAM_BA_MIN(usb_endpoint_table[USB_EP_OUT].DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT, length);
|
|
/* Copy read data to user buffer */
|
|
memcpy(pData, udd_ep_out_cache_buffer[USB_EP_OUT-1], packetSize);
|
|
/* Clear the Transfer Complete 0 flag */
|
|
#ifdef __SAMR21G18A__
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPINTFLAG.bit.TRCPT0 = true;
|
|
#else
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPINTFLAG.bit.TRCPT |= (1 << 0);
|
|
#endif
|
|
/* Clear the user flag */
|
|
read_job = false;
|
|
}
|
|
|
|
return packetSize;
|
|
}
|
|
|
|
uint32_t USB_Read_blocking(Usb *pUsb, char *pData, uint32_t length)
|
|
{
|
|
if (read_job)
|
|
{
|
|
/* Stop the reception by setting the bank 0 ready bit */
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPSTATUSSET.bit.BK0RDY = true;
|
|
/* Clear the user flag */
|
|
read_job = false;
|
|
}
|
|
|
|
/* Set the buffer address for ep data */
|
|
usb_endpoint_table[USB_EP_OUT].DeviceDescBank[0].ADDR.reg = ((uint32_t)pData);
|
|
/* Set the byte count as zero */
|
|
usb_endpoint_table[USB_EP_OUT].DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0;
|
|
/* Set the multi packet size as zero for multi-packet transfers where length > ep size */
|
|
usb_endpoint_table[USB_EP_OUT].DeviceDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = length;
|
|
/* Clear the bank 0 ready flag */
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPSTATUSCLR.bit.BK0RDY = true;
|
|
/* Wait for transfer to complete */
|
|
|
|
#ifdef __SAMR21G18A__
|
|
while (!( pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPINTFLAG.bit.TRCPT0 ));
|
|
/* Clear Transfer complete 0 flag */
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPINTFLAG.bit.TRCPT0 = true;
|
|
#else
|
|
while (!( pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPINTFLAG.bit.TRCPT & (1<<0) ));
|
|
/* Clear Transfer complete 0 flag */
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPINTFLAG.bit.TRCPT |= (1 << 0);
|
|
#endif
|
|
|
|
return length;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* \brief Test if the device is configured and handle enumeration
|
|
*/
|
|
uint8_t USB_IsConfigured(P_USB_CDC pCdc)
|
|
{
|
|
Usb *pUsb = pCdc->pUsb;
|
|
|
|
/* Check for End of Reset flag */
|
|
if (pUsb->DEVICE.INTFLAG.reg & USB_DEVICE_INTFLAG_EORST)
|
|
{
|
|
/* Clear the flag */
|
|
pUsb->DEVICE.INTFLAG.bit.EORST = true;
|
|
/* Set Device address as 0 */
|
|
pUsb->DEVICE.DADD.reg = USB_DEVICE_DADD_ADDEN | 0;
|
|
/* Configure endpoint 0 */
|
|
/* Configure Endpoint 0 for Control IN and Control OUT */
|
|
pUsb->DEVICE.DeviceEndpoint[0].EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(1) | USB_DEVICE_EPCFG_EPTYPE1(1);
|
|
pUsb->DEVICE.DeviceEndpoint[0].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY;
|
|
pUsb->DEVICE.DeviceEndpoint[0].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY;
|
|
/* Configure control OUT Packet size to 64 bytes */
|
|
usb_endpoint_table[0].DeviceDescBank[0].PCKSIZE.bit.SIZE = 3;
|
|
/* Configure control IN Packet size to 64 bytes */
|
|
usb_endpoint_table[0].DeviceDescBank[1].PCKSIZE.bit.SIZE = 3;
|
|
/* Configure the data buffer address for control OUT */
|
|
usb_endpoint_table[0].DeviceDescBank[0].ADDR.reg = (uint32_t)&udd_ep_out_cache_buffer[0];
|
|
/* Configure the data buffer address for control IN */
|
|
usb_endpoint_table[0].DeviceDescBank[1].ADDR.reg = (uint32_t)&udd_ep_in_cache_buffer[0];
|
|
/* Set Multipacket size to 8 for control OUT and byte count to 0*/
|
|
usb_endpoint_table[0].DeviceDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = 8;
|
|
usb_endpoint_table[0].DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0;
|
|
pUsb->DEVICE.DeviceEndpoint[0].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK0RDY;
|
|
|
|
// Reset current configuration value to 0
|
|
pCdc->currentConfiguration = 0;
|
|
}
|
|
else
|
|
{
|
|
if (pUsb->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_RXSTP)
|
|
{
|
|
sam_ba_usb_CDC_Enumerate(pCdc);
|
|
}
|
|
}
|
|
|
|
return pCdc->currentConfiguration;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* \brief Stall the control endpoint
|
|
*/
|
|
void USB_SendStall(Usb *pUsb, bool direction_in)
|
|
{
|
|
/* Check the direction */
|
|
if (direction_in)
|
|
{
|
|
/* Set STALL request on IN direction */
|
|
#ifdef __SAMR21G18A__
|
|
pUsb->DEVICE.DeviceEndpoint[0].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1;
|
|
#else
|
|
pUsb->DEVICE.DeviceEndpoint[0].EPSTATUSSET.bit.STALLRQ = (1<<1);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* Set STALL request on OUT direction */
|
|
#ifdef __SAMR21G18A__
|
|
pUsb->DEVICE.DeviceEndpoint[0].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0;
|
|
#else
|
|
pUsb->DEVICE.DeviceEndpoint[0].EPSTATUSSET.bit.STALLRQ = (1<<0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* \brief Send zero length packet through the control endpoint
|
|
*/
|
|
void USB_SendZlp(Usb *pUsb)
|
|
{
|
|
/* Set the byte count as zero */
|
|
usb_endpoint_table[0].DeviceDescBank[1].PCKSIZE.bit.BYTE_COUNT = 0;
|
|
/* Clear the transfer complete flag */
|
|
#ifdef __SAMR21G18A__
|
|
pUsb->DEVICE.DeviceEndpoint[0].EPINTFLAG.bit.TRCPT1 = true;
|
|
#else
|
|
pUsb->DEVICE.DeviceEndpoint[0].EPINTFLAG.bit.TRCPT |= (1 << 1);
|
|
#endif
|
|
/* Set the bank as ready */
|
|
pUsb->DEVICE.DeviceEndpoint[0].EPSTATUSSET.bit.BK1RDY = true;
|
|
/* Wait for transfer to complete */
|
|
#ifdef __SAMR21G18A__
|
|
while (!( pUsb->DEVICE.DeviceEndpoint[0].EPINTFLAG.bit.TRCPT1 ));
|
|
#else
|
|
while (!( pUsb->DEVICE.DeviceEndpoint[0].EPINTFLAG.bit.TRCPT & (1<<1) ));
|
|
#endif
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* \brief Set USB device address obtained from host
|
|
*/
|
|
void USB_SetAddress(Usb *pUsb, uint16_t wValue)
|
|
{
|
|
pUsb->DEVICE.DADD.reg = USB_DEVICE_DADD_ADDEN | wValue;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* \brief Configure USB device
|
|
*/
|
|
void USB_Configure(Usb *pUsb)
|
|
{
|
|
/* Configure BULK OUT endpoint for CDC Data interface*/
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(3);
|
|
/* Set maximum packet size as 64 bytes */
|
|
usb_endpoint_table[USB_EP_OUT].DeviceDescBank[0].PCKSIZE.bit.SIZE = 3;
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_OUT].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY;
|
|
/* Configure the data buffer */
|
|
usb_endpoint_table[USB_EP_OUT].DeviceDescBank[0].ADDR.reg = (uint32_t)&udd_ep_out_cache_buffer[1];
|
|
|
|
/* Configure BULK IN endpoint for CDC Data interface */
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_IN].EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE1(3);
|
|
/* Set maximum packet size as 64 bytes */
|
|
usb_endpoint_table[USB_EP_IN].DeviceDescBank[1].PCKSIZE.bit.SIZE = 3;
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_IN].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY;
|
|
/* Configure the data buffer */
|
|
usb_endpoint_table[USB_EP_IN].DeviceDescBank[1].ADDR.reg = (uint32_t)&udd_ep_in_cache_buffer[1];
|
|
|
|
/* Configure INTERRUPT IN endpoint for CDC COMM interface*/
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_COMM].EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE1(4);
|
|
/* Set maximum packet size as 64 bytes */
|
|
usb_endpoint_table[USB_EP_COMM].DeviceDescBank[1].PCKSIZE.bit.SIZE = 0;
|
|
pUsb->DEVICE.DeviceEndpoint[USB_EP_COMM].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY;
|
|
}
|