Skip to content
Open
27 changes: 27 additions & 0 deletions lib/usbd/backend/dwc_otg_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,19 @@ struct dwc_otg_private_data {

/* FIXME: used for all endpoint setup_data. */
struct usb_setup_data setup_data;

/* Premature terminated OUT URBs are probably have followed by
* COMP_OUT cause IRQ RXC - that should be ommited,
* else next URB will terminate by this loosed RXC */
uint32_t ep_prematured;
};

#define USBD_DEVICE_EXTRA \
struct dwc_otg_private_data private_data;



#include "../usbd_private.h"
void dwc_otg_init(usbd_device *dev);

void dwc_otg_set_address(usbd_device *dev, uint8_t addr);
Expand All @@ -97,6 +105,8 @@ void dwc_otg_ep_prepare(usbd_device *dev, uint8_t addr, usbd_ep_type type,
uint16_t max_size, uint16_t internval,
usbd_ep_flags flags);
void dwc_otg_ep_prepare_end(usbd_device *dev);
void dwc_otg_stop_ep(usbd_device *dev, uint8_t ep_num);
bool dwc_otg_is_active_ep(usbd_device *dev, uint8_t ep_addr);
void dwc_otg_set_ep_dtog(usbd_device *dev, uint8_t addr, bool dtog);
bool dwc_otg_get_ep_dtog(usbd_device *dev, uint8_t addr);
void dwc_otg_set_ep_stall(usbd_device *dev, uint8_t addr, bool stall);
Expand All @@ -112,6 +122,23 @@ typedef struct usbd_urb usbd_urb;
void dwc_otg_urb_submit(usbd_device *dev, usbd_urb *urb);
void dwc_otg_urb_cancel(usbd_device *dev, usbd_urb *urb);

static inline
bool is_ep_prematured(usbd_device *dev, uint8_t ep_addr)
{
return (dev->private_data.ep_prematured & ep_free_mask(ep_addr));
}

static inline
void mark_ep_as_prematured(usbd_device *dev, uint8_t ep_addr, bool yes)
{
uint32_t mask = ep_free_mask(ep_addr);
if (yes) {
dev->private_data.ep_prematured |= mask;
} else {
dev->private_data.ep_prematured &= ~mask;
}
}

END_DECLS

#endif
106 changes: 94 additions & 12 deletions lib/usbd/backend/usbd_dwc_otg.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,34 @@ static void flush_fifo(usbd_device *dev)
(DWC_OTG_GRSTCTL_RXFFLSH | DWC_OTG_GRSTCTL_TXFFLSH));
}

void fifo_flush_ep(usbd_device *dev, uint8_t ep_addr){

uint8_t num = ENDPOINT_NUMBER(ep_addr);

if (IS_IN_ENDPOINT(ep_addr)) {
REBASE(DWC_OTG_DIEPxCTL, num) |= DWC_OTG_DIEPCTL_SNAK;
/* Wait for AHB idle. */
while (!(REBASE(DWC_OTG_GRSTCTL) & DWC_OTG_GRSTCTL_AHBIDL));

/* Flush RX, TX FIFO */
REBASE(DWC_OTG_GRSTCTL) = DWC_OTG_GRSTCTL_TXFFLSH | DWC_OTG_GRSTCTL_TXFNUM(num);

const uint32_t XFLUSH = DWC_OTG_GRSTCTL_TXFFLSH;
while ( (REBASE(DWC_OTG_GRSTCTL) & XFLUSH) != 0 );
}
else {
REBASE(DWC_OTG_DOEPxCTL, num) |= DWC_OTG_DOEPCTL_SNAK;
/* Wait for AHB idle. */
while (!(REBASE(DWC_OTG_GRSTCTL) & DWC_OTG_GRSTCTL_AHBIDL));

/* Flush RX, TX FIFO */
REBASE(DWC_OTG_GRSTCTL) = DWC_OTG_GRSTCTL_RXFFLSH;

const uint32_t XFLUSH = DWC_OTG_GRSTCTL_RXFFLSH;
while ( (REBASE(DWC_OTG_GRSTCTL) & XFLUSH) != 0 );
}
}

void dwc_otg_init(usbd_device *dev)
{
/* Wait for AHB idle. */
Expand Down Expand Up @@ -173,11 +201,17 @@ static void disable_all_non_ep0(usbd_device *dev)

REBASE(DWC_OTG_DOEPxINT, i) = 0xFFFF;
REBASE(DWC_OTG_DOEPxTSIZ, i) = 0;
REBASE(DWC_OTG_DOEPxCTL, i) = DWC_OTG_DOEPCTL_SNAK;
if ((REBASE(DWC_OTG_DOEPxCTL, i) & DWC_OTG_DOEPCTL_EPENA) == 0)
REBASE(DWC_OTG_DOEPxCTL, i) = DWC_OTG_DOEPCTL_SNAK;
else
REBASE(DWC_OTG_DOEPxCTL, i) = DWC_OTG_DOEPCTL_SNAK | DWC_OTG_DOEPCTL_EPDIS;

REBASE(DWC_OTG_DIEPxINT, i) = 0xFFFF;
REBASE(DWC_OTG_DIEPxTSIZ, i) = 0;
REBASE(DWC_OTG_DIEPxCTL, i) = DWC_OTG_DIEPCTL_SNAK;
if ((REBASE(DWC_OTG_DIEPxCTL, i) & DWC_OTG_DIEPCTL_EPENA) == 0)
REBASE(DWC_OTG_DIEPxCTL, i) = DWC_OTG_DIEPCTL_SNAK;
else
REBASE(DWC_OTG_DIEPxCTL, i) = DWC_OTG_DIEPCTL_SNAK | DWC_OTG_DOEPCTL_EPDIS;
}

REBASE(DWC_OTG_DAINTMSK) &= ~(DWC_OTG_DAINTMSK_OEPM(0) | DWC_OTG_DAINTMSK_IEPM(0));
Expand Down Expand Up @@ -207,6 +241,7 @@ void dwc_otg_ep_prepare_start(usbd_device *dev)

dev->private_data.fifo_rx_usage_overall = 16; /* by EP0 and constant */
dev->private_data.fifo_rx_usage_packet = fifo_word + 1; /* EP0 */
dev->private_data.ep_prematured = 0;

disable_all_non_ep0(dev);

Expand Down Expand Up @@ -596,6 +631,12 @@ void dwc_otg_urb_cancel(usbd_device *dev, usbd_urb *urb)
{
(void) dev;
(void) urb;
uint8_t ep_addr = urb->transfer.ep_addr; //ENDPOINT_NUMBER
if (dwc_otg_is_active_ep(dev, ep_addr)){
USBD_LOGF_LN(USB_VIO, "usbd:stop ep%"PRIx8"\n", ep_addr);
dwc_otg_stop_ep(dev, ep_addr);
fifo_flush_ep(dev, ep_addr);
}
}

/**
Expand Down Expand Up @@ -662,6 +703,45 @@ static inline void fifo_read_and_throw(usbd_device *dev, unsigned bytes)
}
}

bool dwc_otg_is_active_ep(usbd_device *dev, uint8_t ep_addr){
uint8_t ep_num = ENDPOINT_NUMBER(ep_addr);
if (IS_IN_ENDPOINT(ep_addr)) {
if ( DWC_OTG_DIEPTSIZ_XFRSIZ_GET( REBASE(DWC_OTG_DIEPxTSIZ, ep_num) ) != 0 )
return true;
if ((REBASE(DWC_OTG_DIEPxCTL, ep_num) & DWC_OTG_DIEPCTL_NAKSTS) != 0)
return false;
//* looks that DIEPxTSIZ==0 not relyable, it still can be some data hunged in txfifo
//* waiting for transmition
//if ( DWC_OTG_DIEPTSIZ_XFRSIZ_GET( REBASE(DWC_OTG_DIEPxTSIZ, ep_num) ) == 0 )
// return false;
}
else{
if ((REBASE(DWC_OTG_DOEPxCTL, ep_num) & DWC_OTG_DOEPCTL_NAKSTS) != 0)
return false;
if (DWC_OTG_DOEPTSIZ_XFRSIZ_GET(REBASE(DWC_OTG_DOEPxTSIZ, ep_num)) == 0)
return false;
}
return true;
}

void dwc_otg_stop_ep(usbd_device *dev, uint8_t ep_addr){
uint8_t ep_num = ENDPOINT_NUMBER(ep_addr);
if (IS_IN_ENDPOINT(ep_addr)) {
if ((REBASE(DWC_OTG_DIEPxCTL, ep_num) & DWC_OTG_DIEPCTL_EPENA) == 0)
REBASE(DWC_OTG_DIEPxCTL, ep_num) |= DWC_OTG_DIEPCTL_SNAK;
else
REBASE(DWC_OTG_DIEPxCTL, ep_num) |= DWC_OTG_DIEPCTL_SNAK | DWC_OTG_DOEPCTL_EPDIS;
REBASE(DWC_OTG_DIEPxINT, ep_num) = 0xFFFF;
REBASE(DWC_OTG_DAINTMSK) &= ~DWC_OTG_DAINTMSK_IEPM(ep_num);
} else {
if ((REBASE(DWC_OTG_DOEPxCTL, ep_num) & DWC_OTG_DOEPCTL_EPENA) == 0)
REBASE(DWC_OTG_DOEPxCTL, ep_num) |= DWC_OTG_DOEPCTL_SNAK;
else
REBASE(DWC_OTG_DOEPxCTL, ep_num) |= DWC_OTG_DOEPCTL_SNAK | DWC_OTG_DOEPCTL_EPDIS;
REBASE(DWC_OTG_DOEPxINT, ep_num) = 0xFFFF ^ DWC_OTG_DOEPINT_STUP;
REBASE(DWC_OTG_DAINTMSK) &= ~DWC_OTG_DAINTMSK_OEPM(ep_num);
}
}
/**
* Perform a premature URB complete
* This is useful when some kind of unexpected things happen in between
Expand All @@ -673,17 +753,10 @@ static void premature_urb_complete(usbd_device *dev, usbd_urb *urb,
usbd_transfer_status status)
{
usbd_transfer *transfer = &urb->transfer;
uint8_t ep_num = ENDPOINT_NUMBER(transfer->ep_addr);
uint8_t ep_addr = transfer->ep_addr;

if (IS_IN_ENDPOINT(transfer->ep_addr)) {
REBASE(DWC_OTG_DIEPxCTL, ep_num) |= DWC_OTG_DIEPCTL_SNAK;
REBASE(DWC_OTG_DIEPxINT, ep_num) = 0xFFFF;
REBASE(DWC_OTG_DAINTMSK) &= ~DWC_OTG_DAINTMSK_IEPM(ep_num);
} else {
REBASE(DWC_OTG_DOEPxCTL, ep_num) |= DWC_OTG_DOEPCTL_SNAK;
REBASE(DWC_OTG_DOEPxINT, ep_num) = 0xFFFF ^ DWC_OTG_DOEPINT_STUP;
REBASE(DWC_OTG_DAINTMSK) &= ~DWC_OTG_DAINTMSK_OEPM(ep_num);
}
dwc_otg_stop_ep(dev, ep_addr);
mark_ep_as_prematured(dev, ep_addr, true);

usbd_urb_complete(dev, urb, status);
}
Expand Down Expand Up @@ -919,6 +992,15 @@ static void process_out_endpoint_interrupt(usbd_device *dev, uint8_t ep_num)
LOGF_LN("Transfer Complete: endpoint 0x%"PRIx8, ep_addr);
usbd_urb *urb = usbd_find_active_urb(dev, ep_addr);

if (is_ep_prematured(dev, ep_addr)) {
if (urb != NULL){
//* ommit this RXC and restart current URB
REBASE(DWC_OTG_DOEPxCTL, ep_num) |= DWC_OTG_DOEP0CTL_EPENA
| DWC_OTG_DOEP0CTL_CNAK;
}
mark_ep_as_prematured(dev, ep_addr, false);
}
else
if (!ep_num && urb != NULL && dev->private_data.ep0tsiz_pktcnt) {
/* We are still expecting data! */

Expand Down
25 changes: 21 additions & 4 deletions lib/usbd/class/usbd_msc.c
Original file line number Diff line number Diff line change
Expand Up @@ -664,15 +664,15 @@ static void buf_send_to_host_callback(usbd_device *dev,
usbd_urb_id urb_id)
{
(void) urb_id;
usbd_msc *ms = transfer->user_data;
struct usb_msc_trans *trans = &ms->trans;

if (status != USBD_SUCCESS) {
try_resubmit(dev, transfer, status);
trans->csw.bCSWStatus = USB_MSC_CSW_STATUS_FAILED;
csw_send_to_host(ms, trans);
return;
}

usbd_msc *ms = transfer->user_data;
struct usb_msc_trans *trans = &ms->trans;

trans->byte_count += transfer->transferred;

if (trans->byte_count < trans->bytes_to_send) {
Expand Down Expand Up @@ -804,6 +804,23 @@ bool usbd_msc_setup_ep0(usbd_msc *ms,
return true;
}}
}
switch (setup_data->bRequest) {
case USB_REQ_CLEAR_FEATURE:
if (setup_data->wValue == USB_FEAT_ENDPOINT_HALT) {
uint8_t ep_addr = setup_data->wIndex;
/* according to HAL:
* epIN now should terminate all URB and report CSW with fail
* epOUT here can reset ep and continue receive current URB
*/
//usbd_transfer_cancel_ep(dev, ep_addr);
usbd_urb* urb = usbd_find_active_urb(dev, ep_addr);
if (urb == 0)
return false;
usbd_transfer_cancel(dev, urb->id);
return true;
}
break;
}

return false;
}
Expand Down
3 changes: 3 additions & 0 deletions lib/usbd/usbd_transfer.c
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,9 @@ static void detach_from_active(usbd_device *dev, usbd_urb *item)
void usbd_urb_complete(usbd_device *dev, usbd_urb *urb,
usbd_transfer_status status)
{
//* this allows to ommit urb_cancel on ep in detaching
mark_ep_as_free(dev, urb->transfer.ep_addr, true);

detach_from_active(dev, urb);
urb_callback(dev, urb, status);
unused_push(dev, urb);
Expand Down