blob: 19be417fbf570baa3dc986c13b1870b8652475ad [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2016 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/****************************************************************************/
/**
*
* @file xusbpsu_controltransfers.c
* @addtogroup usbpsu_v1_0
* @{
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- ---- -------- -------------------------------------------------------
* 1.0 sg 06/06/16 First release
* 1.3 vak 04/03/17 Added CCI support for USB
* 1.4 bk 12/01/18 Modify USBPSU driver code to fit USB common example code
* for all USB IPs.
*
* </pre>
*
*****************************************************************************/
/***************************** Include Files *********************************/
#include "xusbpsu_endpoint.h"
#include "sleep.h"
#include "xusb_wrapper.h"
/************************** Constant Definitions *****************************/
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
/************************** Variable Definitions *****************************/
/****************************************************************************/
/**
* Initiates DMA on Control Endpoint 0 to receive Setup packet.
*
* @param InstancePtr is a pointer to the XUsbPsu instance.
*
* @return XST_SUCCESS else XST_FAILURE.
*
* @note None.
*
*****************************************************************************/
s32 XUsbPsu_RecvSetup(struct XUsbPsu *InstancePtr)
{
struct XUsbPsu_EpParams *Params;
struct XUsbPsu_Trb *TrbPtr;
struct XUsbPsu_Ep *Ept;
s32 Ret;
Xil_AssertNonvoid(InstancePtr != NULL);
Params = XUsbPsu_GetEpParams(InstancePtr);
Xil_AssertNonvoid(Params != NULL);
/* Setup packet always on EP0 */
Ept = &InstancePtr->eps[0];
if ((Ept->EpStatus & XUSBPSU_EP_BUSY) != 0U) {
return (s32)XST_FAILURE;
}
TrbPtr = &InstancePtr->Ep0_Trb;
TrbPtr->BufferPtrLow = (UINTPTR)&InstancePtr->SetupData;
TrbPtr->BufferPtrHigh = ((UINTPTR)&InstancePtr->SetupData >> 16) >> 16;
TrbPtr->Size = 8U;
TrbPtr->Ctrl = XUSBPSU_TRBCTL_CONTROL_SETUP;
TrbPtr->Ctrl |= (XUSBPSU_TRB_CTRL_HWO
| XUSBPSU_TRB_CTRL_LST
| XUSBPSU_TRB_CTRL_IOC
| XUSBPSU_TRB_CTRL_ISP_IMI);
if (InstancePtr->ConfigPtr->IsCacheCoherent == 0) {
Xil_DCacheFlushRange((INTPTR)TrbPtr, sizeof(struct XUsbPsu_Trb));
Xil_DCacheFlushRange((UINTPTR)&InstancePtr->SetupData, sizeof(SetupPacket));
}
Params->Param0 = 0U;
Params->Param1 = (UINTPTR)TrbPtr;
InstancePtr->Ep0State = XUSBPSU_EP0_SETUP_PHASE;
Ret = XUsbPsu_SendEpCmd(InstancePtr, 0U, XUSBPSU_EP_DIR_OUT,
XUSBPSU_DEPCMD_STARTTRANSFER, Params);
if (Ret != XST_SUCCESS) {
return (s32)XST_FAILURE;
}
Ept->EpStatus |= XUSBPSU_EP_BUSY;
Ept->ResourceIndex = (u8)XUsbPsu_EpGetTransferIndex(InstancePtr,
Ept->UsbEpNum, Ept->Direction);
return XST_SUCCESS;
}
/****************************************************************************/
/**
* Stalls Control Endpoint and restarts to receive Setup packet.
*
* @param InstancePtr is a pointer to the XUsbPsu instance.
*
* @return None
*
* @note None.
*
*****************************************************************************/
void XUsbPsu_Ep0StallRestart(struct XUsbPsu *InstancePtr)
{
struct XUsbPsu_Ep *Ept;
Xil_AssertVoid(InstancePtr != NULL);
/* reinitialize physical ep1 */
Ept = &InstancePtr->eps[1];
Ept->EpStatus = XUSBPSU_EP_ENABLED;
/* stall is always issued on EP0 */
XUsbPsu_EpSetStall(InstancePtr, 0U, XUSBPSU_EP_DIR_OUT);
Ept = &InstancePtr->eps[0];
Ept->EpStatus = XUSBPSU_EP_ENABLED;
InstancePtr->Ep0State = XUSBPSU_EP0_SETUP_PHASE;
(void)XUsbPsu_RecvSetup(InstancePtr);
}
/****************************************************************************/
/**
* Checks the Data Phase and calls user Endpoint handler.
*
* @param InstancePtr is a pointer to the XUsbPsu instance.
* @param Event is a pointer to the Endpoint event occured in core.
*
* @return None.
*
* @note None.
*
*****************************************************************************/
void XUsbPsu_Ep0DataDone(struct XUsbPsu *InstancePtr,
const struct XUsbPsu_Event_Epevt *Event)
{
struct XUsbPsu_Ep *Ept;
struct XUsbPsu_Trb *TrbPtr;
u32 Status;
u32 Length;
u32 Epnum;
u8 Dir;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(Event != NULL);
Epnum = Event->Epnumber;
Dir = (u8)(!!Epnum);
Ept = &InstancePtr->eps[Epnum];
TrbPtr = &InstancePtr->Ep0_Trb;
if (InstancePtr->ConfigPtr->IsCacheCoherent == 0)
Xil_DCacheInvalidateRange((INTPTR)TrbPtr, sizeof(struct XUsbPsu_Trb));
Status = XUSBPSU_TRB_SIZE_TRBSTS(TrbPtr->Size);
if (Status == XUSBPSU_TRBSTS_SETUP_PENDING) {
return;
}
Length = TrbPtr->Size & XUSBPSU_TRB_SIZE_MASK;
if (Length == 0U) {
Ept->BytesTxed = Ept->RequestedBytes;
} else {
if (Dir == XUSBPSU_EP_DIR_IN) {
Ept->BytesTxed = Ept->RequestedBytes - Length;
} else {
if ((Dir == XUSBPSU_EP_DIR_OUT) && (Ept->UnalignedTx == 1U)) {
Ept->BytesTxed = Ept->RequestedBytes;
Ept->UnalignedTx = 0U;
}
}
}
if (Dir == XUSBPSU_EP_DIR_OUT) {
/* Invalidate Cache */
if (InstancePtr->ConfigPtr->IsCacheCoherent == 0)
Xil_DCacheInvalidateRange((INTPTR)Ept->BufferPtr, Ept->BytesTxed);
}
if (Ept->Handler != NULL) {
Ept->Handler(InstancePtr->AppData, Ept->RequestedBytes, Ept->BytesTxed);
}
}
/****************************************************************************/
/**
* Checks the Status Phase and starts next Control transfer.
*
* @param InstancePtr is a pointer to the XUsbPsu instance.
* @param Event is a pointer to the Endpoint event occured in core.
*
* @return None.
*
* @note None.
*
*****************************************************************************/
void XUsbPsu_Ep0StatusDone(struct XUsbPsu *InstancePtr,
const struct XUsbPsu_Event_Epevt *Event)
{
struct XUsbPsu_Trb *TrbPtr;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(Event != NULL);
TrbPtr = &InstancePtr->Ep0_Trb;
if (InstancePtr->IsInTestMode != 0U) {
s32 Ret;
Ret = XUsbPsu_SetTestMode(InstancePtr,
InstancePtr->TestMode);
if (Ret < 0) {
XUsbPsu_Ep0StallRestart(InstancePtr);
return;
}
}
if (InstancePtr->ConfigPtr->IsCacheCoherent == 0)
Xil_DCacheInvalidateRange((INTPTR)TrbPtr, sizeof(struct XUsbPsu_Trb));
(void)XUsbPsu_RecvSetup(InstancePtr);
}
/****************************************************************************/
/**
* Handles Transfer complete event of Control Endpoints EP0 OUT and EP0 IN.
*
* @param InstancePtr is a pointer to the XUsbPsu instance.
* @param Event is a pointer to the Endpoint event occured in core.
*
* @return None.
*
* @note None.
*
*****************************************************************************/
void XUsbPsu_Ep0XferComplete(struct XUsbPsu *InstancePtr,
const struct XUsbPsu_Event_Epevt *Event)
{
struct XUsbPsu_Ep *Ept;
SetupPacket *Ctrl;
u16 Length;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(Event != NULL);
Ept = &InstancePtr->eps[Event->Epnumber];
Ctrl = &InstancePtr->SetupData;
Ept->EpStatus &= ~XUSBPSU_EP_BUSY;
Ept->ResourceIndex = 0U;
switch (InstancePtr->Ep0State) {
case XUSBPSU_EP0_SETUP_PHASE:
if (InstancePtr->ConfigPtr->IsCacheCoherent == 0) {
Xil_DCacheInvalidateRange((INTPTR)&InstancePtr->SetupData,
sizeof(InstancePtr->SetupData));
}
Length = Ctrl->wLength;
if (Length == 0U) {
InstancePtr->IsThreeStage = 0U;
InstancePtr->ControlDir = XUSBPSU_EP_DIR_OUT;
} else {
InstancePtr->IsThreeStage = 1U;
InstancePtr->ControlDir = !!(Ctrl->bRequestType &
USB_DIR_IN);
}
Xil_AssertVoid(InstancePtr->Chapter9 != NULL);
InstancePtr->Chapter9(InstancePtr->AppData,
&InstancePtr->SetupData);
break;
case XUSBPSU_EP0_DATA_PHASE:
XUsbPsu_Ep0DataDone(InstancePtr, Event);
break;
case XUSBPSU_EP0_STATUS_PHASE:
XUsbPsu_Ep0StatusDone(InstancePtr, Event);
break;
default:
/* Default case is a required MISRA-C guideline. */
break;
}
}
/****************************************************************************/
/**
* Starts Status Phase of Control Transfer
*
* @param InstancePtr is a pointer to the XUsbPsu instance.
* @param Event is a pointer to the Endpoint event occured in core.
*
* @return XST_SUCCESS else XST_FAILURE
*
* @note None.
*
*****************************************************************************/
s32 XUsbPsu_Ep0StartStatus(struct XUsbPsu *InstancePtr,
const struct XUsbPsu_Event_Epevt *Event)
{
struct XUsbPsu_Ep *Ept;
struct XUsbPsu_EpParams *Params;
struct XUsbPsu_Trb *TrbPtr;
u32 Type;
s32 Ret;
u8 Dir;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(Event != NULL);
Ept = &InstancePtr->eps[Event->Epnumber];
Params = XUsbPsu_GetEpParams(InstancePtr);
Xil_AssertNonvoid(Params != NULL);
if ((Ept->EpStatus & XUSBPSU_EP_BUSY) != 0U) {
return (s32)XST_FAILURE;
}
Type = (InstancePtr->IsThreeStage != 0U) ? XUSBPSU_TRBCTL_CONTROL_STATUS3
: XUSBPSU_TRBCTL_CONTROL_STATUS2;
TrbPtr = &InstancePtr->Ep0_Trb;
/* we use same TrbPtr for setup packet */
TrbPtr->BufferPtrLow = (UINTPTR)&InstancePtr->SetupData;
TrbPtr->BufferPtrHigh = ((UINTPTR)&InstancePtr->SetupData >> 16) >> 16;
TrbPtr->Size = 0U;
TrbPtr->Ctrl = Type;
TrbPtr->Ctrl |= (XUSBPSU_TRB_CTRL_HWO
| XUSBPSU_TRB_CTRL_LST
| XUSBPSU_TRB_CTRL_IOC
| XUSBPSU_TRB_CTRL_ISP_IMI);
if (InstancePtr->ConfigPtr->IsCacheCoherent == 0) {
Xil_DCacheFlushRange((INTPTR)TrbPtr, sizeof(struct XUsbPsu_Trb));
}
Params->Param0 = 0U;
Params->Param1 = (UINTPTR)TrbPtr;
InstancePtr->Ep0State = XUSBPSU_EP0_STATUS_PHASE;
/*
* Control OUT transfer - Status stage happens on EP0 IN - EP1
* Control IN transfer - Status stage happens on EP0 OUT - EP0
*/
Dir = !InstancePtr->ControlDir;
Ret = XUsbPsu_SendEpCmd(InstancePtr, 0U, Dir,
XUSBPSU_DEPCMD_STARTTRANSFER, Params);
if (Ret != XST_SUCCESS) {
return (s32)XST_FAILURE;
}
Ept->EpStatus |= XUSBPSU_EP_BUSY;
Ept->ResourceIndex = (u8)XUsbPsu_EpGetTransferIndex(InstancePtr,
Ept->UsbEpNum, Ept->Direction);
return XST_SUCCESS;
}
/****************************************************************************/
/**
* Ends Data Phase - used incase of error.
*
* @param InstancePtr is a pointer to the XUsbPsu instance.
* @param Dep is a pointer to the Endpoint structure.
*
* @return None
*
* @note None.
*
*****************************************************************************/
void XUsbPsu_Ep0_EndControlData(struct XUsbPsu *InstancePtr,
struct XUsbPsu_Ep *Ept)
{
struct XUsbPsu_EpParams *Params;
u32 Cmd;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(Ept != NULL);
if (Ept->ResourceIndex == 0U) {
return;
}
Params = XUsbPsu_GetEpParams(InstancePtr);
Xil_AssertVoid(Params != NULL);
Cmd = XUSBPSU_DEPCMD_ENDTRANSFER;
Cmd |= XUSBPSU_DEPCMD_PARAM(Ept->ResourceIndex);
(void)XUsbPsu_SendEpCmd(InstancePtr, Ept->UsbEpNum, Ept->Direction,
Cmd, Params);
Ept->ResourceIndex = 0U;
XUsbSleep(200U);
}
/****************************************************************************/
/**
* Handles Transfer Not Ready event of Control Endpoints EP0 OUT and EP0 IN.
*
* @param InstancePtr is a pointer to the XUsbPsu instance.
* @param Event is a pointer to the Endpoint event occured in core.
*
* @return None.
*
* @note None.
*
*****************************************************************************/
void XUsbPsu_Ep0XferNotReady(struct XUsbPsu *InstancePtr,
const struct XUsbPsu_Event_Epevt *Event)
{
struct XUsbPsu_Ep *Ept;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(Event != NULL);
Ept = &InstancePtr->eps[Event->Epnumber];
switch (Event->Status) {
case DEPEVT_STATUS_CONTROL_DATA:
/*
* We already have a DATA transfer in the controller's cache,
* if we receive a XferNotReady(DATA) we will ignore it, unless
* it's for the wrong direction.
*
* In that case, we must issue END_TRANSFER command to the Data
* Phase we already have started and issue SetStall on the
* control endpoint.
*/
if (Event->Epnumber != InstancePtr->ControlDir) {
XUsbPsu_Ep0_EndControlData(InstancePtr, Ept);
XUsbPsu_Ep0StallRestart(InstancePtr);
}
break;
case DEPEVT_STATUS_CONTROL_STATUS:
(void)XUsbPsu_Ep0StartStatus(InstancePtr, Event);
break;
default:
/* Default case is a required MIRSA-C guideline. */
break;
}
}
/****************************************************************************/
/**
* Handles Interrupts of Control Endpoints EP0 OUT and EP0 IN.
*
* @param InstancePtr is a pointer to the XUsbPsu instance.
* @param Event is a pointer to the Endpoint event occured in core.
*
* @return None.
*
* @note None.
*
*****************************************************************************/
void XUsbPsu_Ep0Intr(struct XUsbPsu *InstancePtr,
const struct XUsbPsu_Event_Epevt *Event)
{
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(Event != NULL);
switch (Event->Endpoint_Event) {
case XUSBPSU_DEPEVT_XFERCOMPLETE:
XUsbPsu_Ep0XferComplete(InstancePtr, Event);
break;
case XUSBPSU_DEPEVT_XFERNOTREADY:
XUsbPsu_Ep0XferNotReady(InstancePtr, Event);
break;
case XUSBPSU_DEPEVT_XFERINPROGRESS:
case XUSBPSU_DEPEVT_STREAMEVT:
case XUSBPSU_DEPEVT_EPCMDCMPLT:
break;
default:
/* Default case is a required MIRSA-C guideline. */
break;
}
}
/****************************************************************************/
/**
* Initiates DMA to send data on Control Endpoint EP0 IN to Host.
*
* @param InstancePtr is a pointer to the XUsbPsu instance.
* @param BufferPtr is pointer to data.
* @param BufferLen is Length of data buffer.
*
* @return XST_SUCCESS else XST_FAILURE
*
* @note None.
*
*****************************************************************************/
s32 XUsbPsu_Ep0Send(struct XUsbPsu *InstancePtr, u8 *BufferPtr, u32 BufferLen)
{
/* Control IN - EP1 */
struct XUsbPsu_EpParams *Params;
struct XUsbPsu_Ep *Ept;
struct XUsbPsu_Trb *TrbPtr;
s32 Ret;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(BufferPtr != NULL);
Ept = &InstancePtr->eps[1];
Params = XUsbPsu_GetEpParams(InstancePtr);
Xil_AssertNonvoid(Params != NULL);
if ((Ept->EpStatus & XUSBPSU_EP_BUSY) != 0U) {
return (s32)XST_FAILURE;
}
Ept->RequestedBytes = BufferLen;
Ept->BytesTxed = 0U;
Ept->BufferPtr = BufferPtr;
TrbPtr = &InstancePtr->Ep0_Trb;
TrbPtr->BufferPtrLow = (UINTPTR)BufferPtr;
TrbPtr->BufferPtrHigh = ((UINTPTR)BufferPtr >> 16) >> 16;
TrbPtr->Size = BufferLen;
TrbPtr->Ctrl = XUSBPSU_TRBCTL_CONTROL_DATA;
TrbPtr->Ctrl |= (XUSBPSU_TRB_CTRL_HWO
| XUSBPSU_TRB_CTRL_LST
| XUSBPSU_TRB_CTRL_IOC
| XUSBPSU_TRB_CTRL_ISP_IMI);
Params->Param0 = 0U;
Params->Param1 = (UINTPTR)TrbPtr;
if (InstancePtr->ConfigPtr->IsCacheCoherent == 0) {
Xil_DCacheFlushRange((INTPTR)TrbPtr, sizeof(struct XUsbPsu_Trb));
Xil_DCacheFlushRange((INTPTR)BufferPtr, BufferLen);
}
InstancePtr->Ep0State = XUSBPSU_EP0_DATA_PHASE;
Ret = XUsbPsu_SendEpCmd(InstancePtr, 0U, XUSBPSU_EP_DIR_IN,
XUSBPSU_DEPCMD_STARTTRANSFER, Params);
if (Ret != XST_SUCCESS) {
return (s32)XST_FAILURE;
}
Ept->EpStatus |= XUSBPSU_EP_BUSY;
Ept->ResourceIndex = (u8)XUsbPsu_EpGetTransferIndex(InstancePtr,
Ept->UsbEpNum, Ept->Direction);
return XST_SUCCESS;
}
/****************************************************************************/
/**
* Initiates DMA to receive data on Control Endpoint EP0 OUT from Host.
*
* @param InstancePtr is a pointer to the XUsbPsu instance.
* @param BufferPtr is pointer to data.
* @param Length is Length of data to be received.
*
* @return XST_SUCCESS else XST_FAILURE
*
* @note None.
*
*****************************************************************************/
s32 XUsbPsu_Ep0Recv(struct XUsbPsu *InstancePtr, u8 *BufferPtr, u32 Length)
{
struct XUsbPsu_EpParams *Params;
struct XUsbPsu_Ep *Ept;
struct XUsbPsu_Trb *TrbPtr;
u32 Size;
s32 Ret;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(BufferPtr != NULL);
Ept = &InstancePtr->eps[0];
Params = XUsbPsu_GetEpParams(InstancePtr);
Xil_AssertNonvoid(Params != NULL);
if ((Ept->EpStatus & XUSBPSU_EP_BUSY) != 0U) {
return (s32)XST_FAILURE;
}
Ept->RequestedBytes = Length;
Size = Length;
Ept->BytesTxed = 0U;
Ept->BufferPtr = BufferPtr;
/*
* 8.2.5 - An OUT transfer size (Total TRB buffer allocation)
* must be a multiple of MaxPacketSize even if software is expecting a
* fixed non-multiple of MaxPacketSize transfer from the Host.
*/
if (!IS_ALIGNED(Length, Ept->MaxSize)) {
Size = (u32)roundup(Length, Ept->MaxSize);
Ept->UnalignedTx = 1U;
}
TrbPtr = &InstancePtr->Ep0_Trb;
TrbPtr->BufferPtrLow = (UINTPTR)BufferPtr;
TrbPtr->BufferPtrHigh = ((UINTPTR)BufferPtr >> 16) >> 16;
TrbPtr->Size = Size;
TrbPtr->Ctrl = XUSBPSU_TRBCTL_CONTROL_DATA;
TrbPtr->Ctrl |= (XUSBPSU_TRB_CTRL_HWO
| XUSBPSU_TRB_CTRL_LST
| XUSBPSU_TRB_CTRL_IOC
| XUSBPSU_TRB_CTRL_ISP_IMI);
if (InstancePtr->ConfigPtr->IsCacheCoherent == 0) {
Xil_DCacheFlushRange((INTPTR)TrbPtr, sizeof(struct XUsbPsu_Trb));
Xil_DCacheInvalidateRange((INTPTR)BufferPtr, Length);
}
Params->Param0 = 0U;
Params->Param1 = (UINTPTR)TrbPtr;
InstancePtr->Ep0State = XUSBPSU_EP0_DATA_PHASE;
Ret = XUsbPsu_SendEpCmd(InstancePtr, 0U, XUSBPSU_EP_DIR_OUT,
XUSBPSU_DEPCMD_STARTTRANSFER, Params);
if (Ret != XST_SUCCESS) {
return (s32)XST_FAILURE;
}
Ept->EpStatus |= XUSBPSU_EP_BUSY;
Ept->ResourceIndex = (u8)XUsbPsu_EpGetTransferIndex(InstancePtr,
Ept->UsbEpNum, Ept->Direction);
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* API for Sleep routine.
*
* @param USeconds is time in MicroSeconds.
*
* @return None.
*
* @note None.
*
******************************************************************************/
void XUsbSleep(u32 USeconds) {
(void)usleep(USeconds);
}
/** @} */