SN32F24xB: Implement alternative handling of ACK/NAK and fix CONSOLE support

This commit is contained in:
stdvar 2021-05-01 00:05:36 -04:00 committed by Dimitris Mantzouranis
parent 52acdb5527
commit d32df9caa9
1 changed files with 216 additions and 142 deletions

View File

@ -51,6 +51,7 @@ USBDriver USBD1;
/*===========================================================================*/
static int address;
static uint8_t nakcnt[USB_MAX_ENDPOINTS + 1] = {0, 0, 0, 0, 0};
/**
* @brief EP0 state.
@ -82,6 +83,21 @@ static const USBEndpointConfig ep0config = {
&ep0_state.out
};
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. */
/*===========================================================================*/
@ -349,181 +365,51 @@ static void usb_lld_serve_interrupt(USBDriver *usbp)
/////////////////////////////////////////////////
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
if(iwIntFlag & mskEP1_ACK)
{
__USB_CLRINSTS(mskEP1_ACK);
ep = USB_EP1;
out = ( SN_USB->CFG & mskEP1_DIR ) == mskEP1_DIR;
cnt = SN_USB->EP1CTL & mskEPn_CNT;
handleACK(usbp, USB_EP1);
}
else if(iwIntFlag & mskEP2_ACK)
if(iwIntFlag & mskEP2_ACK)
{
__USB_CLRINSTS(mskEP2_ACK);
ep = USB_EP2;
out = ( SN_USB->CFG & mskEP2_DIR ) == mskEP2_DIR;
cnt = SN_USB->EP2CTL & mskEPn_CNT;
handleACK(usbp, USB_EP2);
}
else if(iwIntFlag & mskEP3_ACK)
if(iwIntFlag & mskEP3_ACK)
{
__USB_CLRINSTS(mskEP3_ACK);
ep = USB_EP3;
out = ( SN_USB->CFG & mskEP3_DIR ) == mskEP3_DIR;
cnt = SN_USB->EP3CTL & mskEPn_CNT;
handleACK(usbp, USB_EP3);
}
else if(iwIntFlag & mskEP4_ACK)
if(iwIntFlag & mskEP4_ACK)
{
__USB_CLRINSTS(mskEP4_ACK);
ep = USB_EP4;
out = ( SN_USB->CFG & mskEP4_DIR ) == mskEP4_DIR;
cnt = SN_USB->EP4CTL & mskEPn_CNT;
}
// Get the endpoint config and state
const USBEndpointConfig *epcp = usbp->epc[ep];
USBInEndpointState *isp = epcp->in_state;
USBOutEndpointState *osp = epcp->out_state;
// Process based on endpoint direction
if(out)
{
// Read size of received data
n = cnt;
if (n > epcp->out_maxsize)
n = epcp->out_maxsize;
//state is NAK already
//Just being paranoid here. keep here while debugging EP handling issue
//TODO: clean it up when packets are properly handled
if (epcp->out_state->rxsize >= n) {
//we are ok to copy n bytes to buf
sn32_usb_read_fifo(ep, osp->rxbuf, n, true);
epcp->out_state->rxsize -= n;
}
else if (epcp->out_state->rxsize > 0) {
//we dont have enough buffer to receive n bytes
//copy only size availabe on buffer
n = epcp->out_state->rxsize;
sn32_usb_read_fifo(ep, osp->rxbuf, n, true);
epcp->out_state->rxsize -= n;
}
else {
//well buffer is 0 size. strange. do nothing.
n = 0;
}
osp->rxbuf += n;
epcp->out_state->rxcnt += n;
if (epcp->out_state->rxpkts > 0) {
epcp->out_state->rxpkts -= 1;
}
if (n < epcp->out_maxsize || epcp->out_state->rxpkts == 0)
{
_usb_isr_invoke_out_cb(usbp, ep);
}
else
{
//not done. keep on receiving
USB_EPnAck(ep, 0);
}
}
else
{
// Process transmit queue
isp->txcnt += isp->txlast;
n = isp->txsize - isp->txcnt;
if (n > 0)
{
/* Transfer not completed, there are more packets to send.*/
if (n > epcp->in_maxsize)
{
n = epcp->in_maxsize;
}
/* Writes the packet from the defined buffer.*/
isp->txbuf += isp->txlast;
isp->txlast = n;
sn32_usb_write_fifo(ep, isp->txbuf, n, true);
USB_EPnAck(ep, n);
}
else
{
//USB_EPnNak(ep); //not needed here it is autoreset to NAK already
_usb_isr_invoke_in_cb(usbp, ep);
}
handleACK(usbp, USB_EP4);
}
}
else if (iwIntFlag & (mskEP4_NAK|mskEP3_NAK|mskEP2_NAK|mskEP1_NAK))
{
usbep_t ep = USB_EP1;
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;
handleNAK(usbp, USB_EP1);
}
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;
handleNAK(usbp, USB_EP2);
}
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;
handleNAK(usbp, USB_EP3);
}
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;
handleNAK(usbp, USB_EP4);
}
//handle it properly
if(out)
{
// By acking next OUT token from host we are allowing reception
// of the data from host
USB_EPnAck(ep, 0);
}
else
{
// This is not a retransmission
// NAK happens when host polls and device has nothing to send
// Callback below is really redundant unless it serves for syncronization of some sort
// Hera are test observations:
// - if both NAK and callback are called - keyboard works and orgb works
// - if only callback is called - orgb doesn't work, keyboard works
// - if only NAK is called - orgb doesn't work, keyboard haven't tested
// - if nothing is called - orgb works, keyboard occasionally locks up
// lock up shows up as:
// - usb packet received when key is pressed in
// - but usb packet for key is released
// - thus OS treats it as pressed key
USB_EPnNak(ep);
_usb_isr_invoke_in_cb(usbp, ep);
}
}
/////////////////////////////////////////////////
@ -537,6 +423,193 @@ static void usb_lld_serve_interrupt(USBDriver *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;
cnt = SN_USB->EP1CTL & mskEPn_CNT;
}
else if(ep == USB_EP2)
{
out = ( SN_USB->CFG & mskEP2_DIR ) == mskEP2_DIR;
cnt = SN_USB->EP2CTL & mskEPn_CNT;
}
else if(ep == USB_EP3)
{
out = ( SN_USB->CFG & mskEP3_DIR ) == mskEP3_DIR;
cnt = SN_USB->EP3CTL & mskEPn_CNT;
}
else if(ep == USB_EP4)
{
out = ( SN_USB->CFG & mskEP4_DIR ) == mskEP4_DIR;
cnt = SN_USB->EP4CTL & mskEPn_CNT;
}
else {
return;
}
nakcnt[ep] = 0;
// Get the endpoint config and state
const USBEndpointConfig *epcp = usbp->epc[ep];
USBInEndpointState *isp = epcp->in_state;
USBOutEndpointState *osp = epcp->out_state;
// Process based on endpoint direction
if(out)
{
// Read size of received data
n = cnt;
if (n > epcp->out_maxsize)
n = epcp->out_maxsize;
//state is NAK already
//Just being paranoid here. keep here while debugging EP handling issue
//TODO: clean it up when packets are properly handled
if (epcp->out_state->rxsize >= n) {
//we are ok to copy n bytes to buf
sn32_usb_read_fifo(ep, osp->rxbuf, n, true);
epcp->out_state->rxsize -= n;
}
else if (epcp->out_state->rxsize > 0) {
//we dont have enough buffer to receive n bytes
//copy only size availabe on buffer
n = epcp->out_state->rxsize;
sn32_usb_read_fifo(ep, osp->rxbuf, n, true);
epcp->out_state->rxsize -= n;
}
else {
//well buffer is 0 size. strange. do nothing.
n = 0;
}
osp->rxbuf += n;
epcp->out_state->rxcnt += n;
if (epcp->out_state->rxpkts > 0) {
epcp->out_state->rxpkts -= 1;
}
if (n < epcp->out_maxsize || epcp->out_state->rxpkts == 0)
{
_usb_isr_invoke_out_cb(usbp, ep);
}
else
{
//not done. keep on receiving
USB_EPnAck(ep, 0);
}
}
else
{
// Process transmit queue
isp->txcnt += isp->txlast;
n = isp->txsize - isp->txcnt;
if (n > 0)
{
/* Transfer not completed, there are more packets to send.*/
if (n > epcp->in_maxsize)
{
n = epcp->in_maxsize;
}
/* Writes the packet from the defined buffer.*/
isp->txbuf += isp->txlast;
isp->txlast = n;
sn32_usb_write_fifo(ep, isp->txbuf, n, true);
USB_EPnAck(ep, n);
}
else
{
//USB_EPnNak(ep); //not needed here it is autoreset to NAK already
_usb_isr_invoke_in_cb(usbp, ep);
}
}
}
void handleNAK(USBDriver *usbp, usbep_t ep) {
uint8_t out = 0;
//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)
{
// By acking next OUT token from host we are allowing reception
// of the data from host
USB_EPnAck(ep, 0);
}
else
{
// This is not a retransmission, retransmission is transparent and happens on phy layer
// NAK happens when host polls IN EP and device has nothing to send
// It has been observed that sometimes USB phy doesn't generate ACK (unknown why)
// (count ACK interrupts didn't match count of usb_lld_start_in calls per EP)
// However while USB transmitting and qmk thread wants to send another packet qmk goes to
// infinite sleep, expecting that successfull USB transmission will wake it up
// If USB transmission never completes (no ACK) then qmk never wakes up and keyboard locks up
// To prevent this every NAK (1ms or 8ms depending on host poll interval) was calling
// callbacks and wake up function to wake up qmk thread, however packet was not delivered to host
// (for unknown reason) and thus we have seen:
// 1) stuck keypresses when usb packets to press key delivered but key release packet lost
// 2) untyped key when usb packet to press key was lost but key release packet delivered
// 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);
}
else if (nakcnt[ep] > 2) {
//3-10
nakcnt[ep]++;
USB_EPnAck(ep, 0);
}
else {
//1-2
//give it sometime to deliver the packet
nakcnt[ep]++;
}
}
#endif
}
}
/*===========================================================================*/
/* Driver interrupt handlers and threads. */
/*===========================================================================*/
@ -855,7 +928,7 @@ void usb_lld_start_in(USBDriver *usbp, usbep_t ep)
//who handles 0 packet ack on setup?
n = isp->txsize;
if((n > 0) || (ep == 0))
if((n >= 0) || (ep == 0))
{
if (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);
nakcnt[ep] = 1;
USB_EPnAck(ep, n);
}
else