From e5af7766bb3fbe6d575937b0995e7b2568f7a632 Mon Sep 17 00:00:00 2001 From: Alexander Lunev Date: Thu, 16 Sep 2021 06:19:44 +0300 Subject: [PATCH] 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 --- .../wireless/ieee80211/bcm43xxx/bcmf_sdio.c | 1 + .../wireless/ieee80211/bcm43xxx/bcmf_sdio.h | 6 +- .../wireless/ieee80211/bcm43xxx/bcmf_sdpcm.c | 139 +++++++++++++----- 3 files changed, 108 insertions(+), 38 deletions(-) diff --git a/drivers/wireless/ieee80211/bcm43xxx/bcmf_sdio.c b/drivers/wireless/ieee80211/bcm43xxx/bcmf_sdio.c index d46d7c193a..4a57749296 100644 --- a/drivers/wireless/ieee80211/bcm43xxx/bcmf_sdio.c +++ b/drivers/wireless/ieee80211/bcm43xxx/bcmf_sdio.c @@ -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; diff --git a/drivers/wireless/ieee80211/bcm43xxx/bcmf_sdio.h b/drivers/wireless/ieee80211/bcm43xxx/bcmf_sdio.h index 33b0c74c77..ba5af0b958 100644 --- a/drivers/wireless/ieee80211/bcm43xxx/bcmf_sdio.h +++ b/drivers/wireless/ieee80211/bcm43xxx/bcmf_sdio.h @@ -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 */ diff --git a/drivers/wireless/ieee80211/bcm43xxx/bcmf_sdpcm.c b/drivers/wireless/ieee80211/bcm43xxx/bcmf_sdpcm.c index ce3cba319c..94ecd9a07f 100644 --- a/drivers/wireless/ieee80211/bcm43xxx/bcmf_sdpcm.c +++ b/drivers/wireless/ieee80211/bcm43xxx/bcmf_sdpcm.c @@ -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 */