SN32F24xB: Implement alternative handling of ACK/NAK and fix CONSOLE support
This commit is contained in:
parent
52acdb5527
commit
d32df9caa9
|
@ -51,6 +51,7 @@ USBDriver USBD1;
|
||||||
/*===========================================================================*/
|
/*===========================================================================*/
|
||||||
|
|
||||||
static int address;
|
static int address;
|
||||||
|
static uint8_t nakcnt[USB_MAX_ENDPOINTS + 1] = {0, 0, 0, 0, 0};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief EP0 state.
|
* @brief EP0 state.
|
||||||
|
@ -82,6 +83,21 @@ static const USBEndpointConfig ep0config = {
|
||||||
&ep0_state.out
|
&ep0_state.out
|
||||||
};
|
};
|
||||||
void rgb_matrix_toggle(void);
|
void rgb_matrix_toggle(void);
|
||||||
|
void handleACK(USBDriver* usbp, usbep_t ep);
|
||||||
|
void handleNAK(USBDriver* usbp, usbep_t ep);
|
||||||
|
#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__)
|
||||||
|
#define _usb_isr_invoke_tx_complete_cb(usbp, ep) { \
|
||||||
|
(usbp)->transmitting &= ~(1 << (ep)); \
|
||||||
|
osalSysLockFromISR(); \
|
||||||
|
osalThreadResumeI(&(usbp)->epc[ep]->in_state->thread, MSG_OK); \
|
||||||
|
osalSysUnlockFromISR(); \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define _usb_isr_invoke_tx_complete_cb(usbp, ep) { \
|
||||||
|
(usbp)->transmitting &= ~(1 << (ep)); \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*===========================================================================*/
|
/*===========================================================================*/
|
||||||
/* Driver local variables and types. */
|
/* Driver local variables and types. */
|
||||||
/*===========================================================================*/
|
/*===========================================================================*/
|
||||||
|
@ -349,39 +365,93 @@ static void usb_lld_serve_interrupt(USBDriver *usbp)
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
else if (iwIntFlag & (mskEP4_ACK|mskEP3_ACK|mskEP2_ACK|mskEP1_ACK))
|
else if (iwIntFlag & (mskEP4_ACK|mskEP3_ACK|mskEP2_ACK|mskEP1_ACK))
|
||||||
{
|
{
|
||||||
usbep_t ep = USB_EP1;
|
|
||||||
uint8_t out = 0;
|
|
||||||
uint8_t cnt = 0;
|
|
||||||
|
|
||||||
// Determine the interrupting endpoint, direction, and clear the interrupt flag
|
// Determine the interrupting endpoint, direction, and clear the interrupt flag
|
||||||
if(iwIntFlag & mskEP1_ACK)
|
if(iwIntFlag & mskEP1_ACK)
|
||||||
{
|
{
|
||||||
__USB_CLRINSTS(mskEP1_ACK);
|
__USB_CLRINSTS(mskEP1_ACK);
|
||||||
ep = USB_EP1;
|
handleACK(usbp, USB_EP1);
|
||||||
|
}
|
||||||
|
if(iwIntFlag & mskEP2_ACK)
|
||||||
|
{
|
||||||
|
__USB_CLRINSTS(mskEP2_ACK);
|
||||||
|
handleACK(usbp, USB_EP2);
|
||||||
|
}
|
||||||
|
if(iwIntFlag & mskEP3_ACK)
|
||||||
|
{
|
||||||
|
__USB_CLRINSTS(mskEP3_ACK);
|
||||||
|
handleACK(usbp, USB_EP3);
|
||||||
|
}
|
||||||
|
if(iwIntFlag & mskEP4_ACK)
|
||||||
|
{
|
||||||
|
__USB_CLRINSTS(mskEP4_ACK);
|
||||||
|
handleACK(usbp, USB_EP4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (iwIntFlag & (mskEP4_NAK|mskEP3_NAK|mskEP2_NAK|mskEP1_NAK))
|
||||||
|
{
|
||||||
|
// Determine the interrupting endpoint, direction, and clear the interrupt flag
|
||||||
|
if (iwIntFlag & mskEP1_NAK)
|
||||||
|
{
|
||||||
|
__USB_CLRINSTS(mskEP1_NAK);
|
||||||
|
handleNAK(usbp, USB_EP1);
|
||||||
|
}
|
||||||
|
if (iwIntFlag & mskEP2_NAK)
|
||||||
|
{
|
||||||
|
__USB_CLRINSTS(mskEP2_NAK);
|
||||||
|
handleNAK(usbp, USB_EP2);
|
||||||
|
}
|
||||||
|
if (iwIntFlag & mskEP3_NAK)
|
||||||
|
{
|
||||||
|
__USB_CLRINSTS(mskEP3_NAK);
|
||||||
|
handleNAK(usbp, USB_EP3);
|
||||||
|
}
|
||||||
|
if (iwIntFlag & mskEP4_NAK)
|
||||||
|
{
|
||||||
|
__USB_CLRINSTS(mskEP4_NAK);
|
||||||
|
handleNAK(usbp, USB_EP4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
/* Device Status Interrupt (SOF) */
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
if ((iwIntFlag & mskUSB_SOF) && (SN_USB->INTEN & mskUSB_SOF_IE))
|
||||||
|
{
|
||||||
|
/* SOF */
|
||||||
|
__USB_CLRINSTS(mskUSB_SOF);
|
||||||
|
_usb_isr_invoke_sof_cb(usbp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleACK(USBDriver* usbp, usbep_t ep) {
|
||||||
|
uint8_t out = 0;
|
||||||
|
uint8_t cnt = 0;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
if(ep == USB_EP1)
|
||||||
|
{
|
||||||
out = ( SN_USB->CFG & mskEP1_DIR ) == mskEP1_DIR;
|
out = ( SN_USB->CFG & mskEP1_DIR ) == mskEP1_DIR;
|
||||||
cnt = SN_USB->EP1CTL & mskEPn_CNT;
|
cnt = SN_USB->EP1CTL & mskEPn_CNT;
|
||||||
}
|
}
|
||||||
else if(iwIntFlag & mskEP2_ACK)
|
else if(ep == USB_EP2)
|
||||||
{
|
{
|
||||||
__USB_CLRINSTS(mskEP2_ACK);
|
|
||||||
ep = USB_EP2;
|
|
||||||
out = ( SN_USB->CFG & mskEP2_DIR ) == mskEP2_DIR;
|
out = ( SN_USB->CFG & mskEP2_DIR ) == mskEP2_DIR;
|
||||||
cnt = SN_USB->EP2CTL & mskEPn_CNT;
|
cnt = SN_USB->EP2CTL & mskEPn_CNT;
|
||||||
}
|
}
|
||||||
else if(iwIntFlag & mskEP3_ACK)
|
else if(ep == USB_EP3)
|
||||||
{
|
{
|
||||||
__USB_CLRINSTS(mskEP3_ACK);
|
|
||||||
ep = USB_EP3;
|
|
||||||
out = ( SN_USB->CFG & mskEP3_DIR ) == mskEP3_DIR;
|
out = ( SN_USB->CFG & mskEP3_DIR ) == mskEP3_DIR;
|
||||||
cnt = SN_USB->EP3CTL & mskEPn_CNT;
|
cnt = SN_USB->EP3CTL & mskEPn_CNT;
|
||||||
}
|
}
|
||||||
else if(iwIntFlag & mskEP4_ACK)
|
else if(ep == USB_EP4)
|
||||||
{
|
{
|
||||||
__USB_CLRINSTS(mskEP4_ACK);
|
|
||||||
ep = USB_EP4;
|
|
||||||
out = ( SN_USB->CFG & mskEP4_DIR ) == mskEP4_DIR;
|
out = ( SN_USB->CFG & mskEP4_DIR ) == mskEP4_DIR;
|
||||||
cnt = SN_USB->EP4CTL & mskEPn_CNT;
|
cnt = SN_USB->EP4CTL & mskEPn_CNT;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nakcnt[ep] = 0;
|
||||||
|
|
||||||
// Get the endpoint config and state
|
// Get the endpoint config and state
|
||||||
const USBEndpointConfig *epcp = usbp->epc[ep];
|
const USBEndpointConfig *epcp = usbp->epc[ep];
|
||||||
|
@ -462,44 +532,28 @@ static void usb_lld_serve_interrupt(USBDriver *usbp)
|
||||||
_usb_isr_invoke_in_cb(usbp, ep);
|
_usb_isr_invoke_in_cb(usbp, ep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (iwIntFlag & (mskEP4_NAK|mskEP3_NAK|mskEP2_NAK|mskEP1_NAK))
|
|
||||||
{
|
void handleNAK(USBDriver *usbp, usbep_t ep) {
|
||||||
usbep_t ep = USB_EP1;
|
|
||||||
uint8_t out = 0;
|
uint8_t out = 0;
|
||||||
//uint8_t cnt = 0;
|
|
||||||
|
|
||||||
// Determine the interrupting endpoint, direction, and clear the interrupt flag
|
|
||||||
if (iwIntFlag & mskEP1_NAK)
|
|
||||||
{
|
|
||||||
__USB_CLRINSTS(mskEP1_NAK);
|
|
||||||
ep = USB_EP1;
|
|
||||||
out = ( SN_USB->CFG & mskEP1_DIR ) == mskEP1_DIR;
|
|
||||||
//cnt = SN_USB->EP1CTL & mskEPn_CNT;
|
|
||||||
}
|
|
||||||
if (iwIntFlag & mskEP2_NAK)
|
|
||||||
{
|
|
||||||
__USB_CLRINSTS(mskEP2_NAK);
|
|
||||||
ep = USB_EP2;
|
|
||||||
out = ( SN_USB->CFG & mskEP2_DIR ) == mskEP2_DIR;
|
|
||||||
//cnt = SN_USB->EP2CTL & mskEPn_CNT;
|
|
||||||
}
|
|
||||||
if (iwIntFlag & mskEP3_NAK)
|
|
||||||
{
|
|
||||||
__USB_CLRINSTS(mskEP3_NAK);
|
|
||||||
ep = USB_EP3;
|
|
||||||
out = ( SN_USB->CFG & mskEP3_DIR ) == mskEP3_DIR;
|
|
||||||
//cnt = SN_USB->EP3CTL & mskEPn_CNT;
|
|
||||||
}
|
|
||||||
if (iwIntFlag & mskEP4_NAK)
|
|
||||||
{
|
|
||||||
__USB_CLRINSTS(mskEP4_NAK);
|
|
||||||
ep = USB_EP4;
|
|
||||||
out = ( SN_USB->CFG & mskEP4_DIR ) == mskEP4_DIR;
|
|
||||||
//cnt = SN_USB->EP4CTL & mskEPn_CNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
//handle it properly
|
//handle it properly
|
||||||
|
if (ep == USB_EP1) {
|
||||||
|
out = ( SN_USB->CFG & mskEP1_DIR ) == mskEP1_DIR;
|
||||||
|
}
|
||||||
|
else if (ep == USB_EP2) {
|
||||||
|
out = ( SN_USB->CFG & mskEP2_DIR ) == mskEP2_DIR;
|
||||||
|
}
|
||||||
|
else if (ep == USB_EP3) {
|
||||||
|
out = ( SN_USB->CFG & mskEP3_DIR ) == mskEP3_DIR;
|
||||||
|
}
|
||||||
|
else if (ep == USB_EP4) {
|
||||||
|
out = ( SN_USB->CFG & mskEP4_DIR ) == mskEP4_DIR;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if(out)
|
if(out)
|
||||||
{
|
{
|
||||||
// By acking next OUT token from host we are allowing reception
|
// By acking next OUT token from host we are allowing reception
|
||||||
|
@ -508,32 +562,51 @@ static void usb_lld_serve_interrupt(USBDriver *usbp)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This is not a retransmission
|
// This is not a retransmission, retransmission is transparent and happens on phy layer
|
||||||
// NAK happens when host polls and device has nothing to send
|
// NAK happens when host polls IN EP and device has nothing to send
|
||||||
// Callback below is really redundant unless it serves for syncronization of some sort
|
// It has been observed that sometimes USB phy doesn't generate ACK (unknown why)
|
||||||
// Hera are test observations:
|
// (count ACK interrupts didn't match count of usb_lld_start_in calls per EP)
|
||||||
// - if both NAK and callback are called - keyboard works and orgb works
|
// However while USB transmitting and qmk thread wants to send another packet qmk goes to
|
||||||
// - if only callback is called - orgb doesn't work, keyboard works
|
// infinite sleep, expecting that successfull USB transmission will wake it up
|
||||||
// - if only NAK is called - orgb doesn't work, keyboard haven't tested
|
// If USB transmission never completes (no ACK) then qmk never wakes up and keyboard locks up
|
||||||
// - if nothing is called - orgb works, keyboard occasionally locks up
|
// To prevent this every NAK (1ms or 8ms depending on host poll interval) was calling
|
||||||
// lock up shows up as:
|
// callbacks and wake up function to wake up qmk thread, however packet was not delivered to host
|
||||||
// - usb packet received when key is pressed in
|
// (for unknown reason) and thus we have seen:
|
||||||
// - but usb packet for key is released
|
// 1) stuck keypresses when usb packets to press key delivered but key release packet lost
|
||||||
// - thus OS treats it as pressed key
|
// 2) untyped key when usb packet to press key was lost but key release packet delivered
|
||||||
USB_EPnNak(ep);
|
// Because callback was called every NAK some qmk features didnt work such as CONSOLE
|
||||||
|
// since callback might release buffers and endup in deadlock via disabled interrupts
|
||||||
|
// callback for keyboard is empty thus its repated calling is harmless
|
||||||
|
#if defined(SN32_USB_ORIGINAL_NAK_HANDLING)
|
||||||
|
_usb_isr_invoke_in_cb(usbp, ep);
|
||||||
|
#else
|
||||||
|
//To fake missing ACK we can send 0 sized packet
|
||||||
|
//however (again for unknown reason) packets now being delivered to host as well!
|
||||||
|
//- value 2 has been selected to allow at least 2 NAK delivery (2ms or 16ms depending on
|
||||||
|
//host polling interval) between moment qmk called start_in and moment USB phy actually
|
||||||
|
//started transmission
|
||||||
|
//- value 10 was selected arbitrary.
|
||||||
|
//- values 3-10 we are delivering 0 sized packet trying to get at least one ack
|
||||||
|
if (nakcnt[ep] > 0) {
|
||||||
|
//qmk called start_in
|
||||||
|
if (nakcnt[ep] > 10) {
|
||||||
|
//11-....
|
||||||
|
//consider packet undeliverable but ack it to the qmk
|
||||||
|
nakcnt[ep] = 0;
|
||||||
_usb_isr_invoke_in_cb(usbp, ep);
|
_usb_isr_invoke_in_cb(usbp, ep);
|
||||||
}
|
}
|
||||||
|
else if (nakcnt[ep] > 2) {
|
||||||
|
//3-10
|
||||||
|
nakcnt[ep]++;
|
||||||
|
USB_EPnAck(ep, 0);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
/////////////////////////////////////////////////
|
//1-2
|
||||||
/* Device Status Interrupt (SOF) */
|
//give it sometime to deliver the packet
|
||||||
/////////////////////////////////////////////////
|
nakcnt[ep]++;
|
||||||
if ((iwIntFlag & mskUSB_SOF) && (SN_USB->INTEN & mskUSB_SOF_IE))
|
}
|
||||||
{
|
}
|
||||||
/* SOF */
|
#endif
|
||||||
__USB_CLRINSTS(mskUSB_SOF);
|
|
||||||
_usb_isr_invoke_sof_cb(usbp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -855,7 +928,7 @@ void usb_lld_start_in(USBDriver *usbp, usbep_t ep)
|
||||||
//who handles 0 packet ack on setup?
|
//who handles 0 packet ack on setup?
|
||||||
n = isp->txsize;
|
n = isp->txsize;
|
||||||
|
|
||||||
if((n > 0) || (ep == 0))
|
if((n >= 0) || (ep == 0))
|
||||||
{
|
{
|
||||||
if (n > (size_t)usbp->epc[ep]->in_maxsize)
|
if (n > (size_t)usbp->epc[ep]->in_maxsize)
|
||||||
n = (size_t)usbp->epc[ep]->in_maxsize;
|
n = (size_t)usbp->epc[ep]->in_maxsize;
|
||||||
|
@ -864,6 +937,7 @@ void usb_lld_start_in(USBDriver *usbp, usbep_t ep)
|
||||||
|
|
||||||
sn32_usb_write_fifo(ep, isp->txbuf, n, false);
|
sn32_usb_write_fifo(ep, isp->txbuf, n, false);
|
||||||
|
|
||||||
|
nakcnt[ep] = 1;
|
||||||
USB_EPnAck(ep, n);
|
USB_EPnAck(ep, n);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in New Issue