bcm43xxx: fixed an issue with abrupt stall of receiving new credits (via sdpcm header)

from bcm43362 chip/firmware as soon as a high network traffic started
This commit is contained in:
Alexander Lunev 2021-09-16 06:19:44 +03:00 committed by Xiang Xiao
parent 51c185b3b5
commit e5af7766bb
3 changed files with 108 additions and 38 deletions

View File

@ -643,6 +643,7 @@ int bcmf_bus_sdio_initialize(FAR struct bcmf_dev_s *priv,
sbus->minor = minor;
sbus->ready = false;
sbus->sleeping = true;
sbus->flow_ctrl = false;
sbus->bus.txframe = bcmf_sdpcm_queue_frame;
sbus->bus.rxframe = bcmf_sdpcm_get_rx_frame;

View File

@ -41,8 +41,9 @@
* Pre-processor Definitions
****************************************************************************/
#define HEADER_SIZE 0x12 /* Default sdpcm + bdc header size */
#define FIRST_WORD_SIZE 4
#define HEADER_SIZE 0x12 /* Default sdpcm + bdc header size */
#define FIRST_WORD_SIZE 4
#define FC_UPDATE_PKT_LENGTH 12
/****************************************************************************
* Public Types
@ -97,6 +98,7 @@ struct bcmf_sdio_dev_s
uint8_t max_seq; /* Maximum transmit sequence allowed */
uint8_t tx_seq; /* Transmit sequence number (next) */
uint8_t rx_seq; /* Receive sequence number (expected) */
bool flow_ctrl; /* Current flow control status */
sem_t queue_mutex; /* Lock for TX/RX/free queues */
dq_queue_t free_queue; /* Queue of available frames */

View File

@ -125,6 +125,10 @@ int bcmf_sdpcm_process_header(FAR struct bcmf_sdio_dev_s *sbus,
sbus->max_seq = header->credit;
/* Update flow control status */
sbus->flow_ctrl = (header->flow_control != 0);
return OK;
}
@ -138,9 +142,66 @@ int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv)
uint16_t len;
uint16_t checksum;
struct bcmf_sdpcm_header *header;
struct bcmf_sdpcm_header tmp_hdr;
struct bcmf_sdio_frame *sframe;
FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s *)priv->bus;
/* Read the first 4 bytes of sdpcm header
* to get the length of the following data to be read
*/
ret = bcmf_transfer_bytes(sbus, false, 2, 0,
(uint8_t *)&tmp_hdr,
FIRST_WORD_SIZE);
if (ret != OK)
{
wlinfo("Failed to read size\n");
bcmf_sdpcm_rxfail(sbus, false);
return -EIO;
}
len = tmp_hdr.size;
checksum = tmp_hdr.checksum;
/* All zero means no more to read */
if (!(len | checksum))
{
return -ENODATA;
}
if (((~len & 0xffff) ^ checksum) || len < sizeof(struct bcmf_sdpcm_header))
{
wlerr("Invalid header checksum or len %x %x\n", len, checksum);
bcmf_sdpcm_rxfail(sbus, false);
return -EINVAL;
}
if (len == FC_UPDATE_PKT_LENGTH)
{
/* Flow control update packet with no data */
ret = bcmf_transfer_bytes(sbus, false, 2, 0,
(uint8_t *)&tmp_hdr + FIRST_WORD_SIZE,
FC_UPDATE_PKT_LENGTH - FIRST_WORD_SIZE);
if (ret != OK)
{
wlinfo("Failed to read the rest 8 bytes\n");
bcmf_sdpcm_rxfail(sbus, false);
return -EIO;
}
ret = bcmf_sdpcm_process_header(sbus, &tmp_hdr);
if (ret != OK)
{
wlerr("Error while processing header %d\n", ret);
return -EINVAL;
}
return OK;
}
/* Request free frame buffer */
sframe = bcmf_sdio_allocate_frame(priv, false, false);
@ -148,61 +209,62 @@ int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv)
if (sframe == NULL)
{
wlinfo("fail alloc\n");
/* Read out the rest of the header to get the bus credit information */
ret = bcmf_transfer_bytes(sbus, false, 2, 0,
(uint8_t *)&tmp_hdr + FIRST_WORD_SIZE,
FC_UPDATE_PKT_LENGTH - FIRST_WORD_SIZE);
if (ret != OK)
{
wlinfo("Failed to read the rest 8 bytes\n");
bcmf_sdpcm_rxfail(sbus, false);
return -EIO;
}
bcmf_sdpcm_rxfail(sbus, false);
ret = bcmf_sdpcm_process_header(sbus, &tmp_hdr);
if (ret != OK)
{
wlerr("Error while processing header %d\n", ret);
return -EINVAL;
}
return -EAGAIN;
}
header = (struct bcmf_sdpcm_header *)sframe->data;
/* Read the first 4 bytes of sdpcm header
* to get the length of the following data to be read
*/
/* Read the remaining frame data (the buffer is DMA aligned here) */
ret = bcmf_transfer_bytes(sbus, false, 2, 0,
(uint8_t *)header,
FIRST_WORD_SIZE);
if (ret != OK)
if (len <= FIRST_WORD_SIZE)
{
wlinfo("failread size\n");
ret = -EIO;
goto exit_abort;
}
len = header->size;
checksum = header->checksum;
/* All zero means no more to read */
if (!(len | checksum))
{
ret = -ENODATA;
ret = OK;
goto exit_free_frame;
}
if (((~len & 0xffff) ^ checksum) || len < sizeof(struct bcmf_sdpcm_header))
{
wlerr("Invalid header checksum or len %x %x\n", len, checksum);
ret = -EINVAL;
goto exit_abort;
}
if (len > sframe->header.len)
{
wlerr("Frame is too large, cancel %d %d\n", len, sframe->header.len);
ret = -ENOMEM;
goto exit_abort;
}
/* Read the remaining frame data (the buffer is DMA aligned here) */
ret = bcmf_transfer_bytes(sbus, false, 2, 0,
(uint8_t *)header + FIRST_WORD_SIZE,
len - FIRST_WORD_SIZE);
if (ret != OK)
{
wlinfo("Failed to read remaining frame data\n");
ret = -EIO;
goto exit_abort;
}
memcpy(header, &tmp_hdr, FIRST_WORD_SIZE);
if (len > sframe->header.len)
{
wlerr("Frame is too large, cancel %d %d\n", len, sframe->header.len);
ret = -ENOMEM;
goto exit_abort;
}
#if 0
wlinfo("Receive frame %p %d\n", sframe, len);
@ -296,6 +358,11 @@ int bcmf_sdpcm_sendframe(FAR struct bcmf_dev_s *priv)
return -ENODATA;
}
if (sbus->flow_ctrl)
{
return -EAGAIN;
}
if (sbus->tx_seq == sbus->max_seq)
{
/* TODO handle this case */