aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/csr/csr_wifi_hip_card_sdio_intr.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-06-19 16:15:42 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-06-19 16:37:01 -0700
commit635d2b00e5070378e7bf812acf47fb135c6ab928 (patch)
tree7048a0a511f3d221aa2dfe40aa3a401991f1b175 /drivers/staging/csr/csr_wifi_hip_card_sdio_intr.c
parent15a4bc17b7f4e85cb019e683f14e834078ec2208 (diff)
Staging: add CSR wifi module
This consists of two modules, the driver, and a "helper" module that is just a wrapper around common kernel functions. The wrapper module will be removed soon, but for now it's needed. These files were based on the csr-linux-wifi-5.0.3-oss.tar.gz package provided by CSR and Blue Giga, and is covered under the license specified in the LICENSE.txt file (basically dual BSD and GPLv2). The files were flattened out of the deep directory mess they were originally in, and a few EXPORT_SYMBOL_GPL() were added in order for everything to link properly with the helper module setup. Cc: Mikko Virkkilä <mikko.virkkila@bluegiga.com> Cc: Lauri Hintsala <Lauri.Hintsala@bluegiga.com> Cc: Riku Mettälä <riku.mettala@bluegiga.com> Cc: Veli-Pekka Peltola <veli-pekka.peltola@bluegiga.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/staging/csr/csr_wifi_hip_card_sdio_intr.c')
-rw-r--r--drivers/staging/csr/csr_wifi_hip_card_sdio_intr.c2561
1 files changed, 2561 insertions, 0 deletions
diff --git a/drivers/staging/csr/csr_wifi_hip_card_sdio_intr.c b/drivers/staging/csr/csr_wifi_hip_card_sdio_intr.c
new file mode 100644
index 00000000000..3d563c13ff5
--- /dev/null
+++ b/drivers/staging/csr/csr_wifi_hip_card_sdio_intr.c
@@ -0,0 +1,2561 @@
+/*****************************************************************************
+
+ (c) Cambridge Silicon Radio Limited 2011
+ All rights reserved and confidential information of CSR
+
+ Refer to LICENSE.txt included with this source for details
+ on the license terms.
+
+*****************************************************************************/
+
+/*
+ * ---------------------------------------------------------------------------
+ * FILE: csr_wifi_hip_card_sdio_intr.c
+ *
+ * PURPOSE:
+ * Interrupt processing for the UniFi SDIO driver.
+ *
+ * We may need another signal queue of responses to UniFi to hold
+ * bulk data commands generated by read_to_host_signals().
+ *
+ * ---------------------------------------------------------------------------
+ */
+#undef CSR_WIFI_HIP_NOISY
+
+#include "csr_wifi_hip_unifi.h"
+#include "csr_wifi_hip_conversions.h"
+#include "csr_wifi_hip_card.h"
+#include "csr_wifi_hip_xbv.h"
+
+
+/*
+ * If the SDIO link is idle for this time (in milliseconds),
+ * signal UniFi to go into Deep Sleep.
+ * Valid return value of unifi_bh().
+ */
+#define UNIFI_DEFAULT_HOST_IDLE_TIMEOUT 5
+/*
+ * If the UniFi has not woken up for this time (in milliseconds),
+ * signal the bottom half to take action.
+ * Valid return value of unifi_bh().
+ */
+#define UNIFI_DEFAULT_WAKE_TIMEOUT 1000
+
+
+static CsrResult process_bh(card_t *card);
+static CsrResult handle_host_protocol(card_t *card, CsrBool *processed_something);
+
+static CsrResult flush_fh_buffer(card_t *card);
+
+static CsrResult check_fh_sig_slots(card_t *card, CsrUint16 needed, CsrInt32 *space);
+
+static CsrResult read_to_host_signals(card_t *card, CsrInt32 *processed);
+static CsrResult process_to_host_signals(card_t *card, CsrInt32 *processed);
+
+static CsrResult process_bulk_data_command(card_t *card,
+ const CsrUint8 *cmdptr,
+ CsrInt16 cmd, CsrUint16 len);
+static CsrResult process_clear_slot_command(card_t *card,
+ const CsrUint8 *cmdptr);
+static CsrResult process_fh_cmd_queue(card_t *card, CsrInt32 *processed);
+static CsrResult process_fh_traffic_queue(card_t *card, CsrInt32 *processed);
+static void restart_packet_flow(card_t *card);
+static CsrResult process_clock_request(card_t *card);
+
+#ifdef CSR_WIFI_HIP_NOISY
+CsrInt16 dump_fh_buf = 0;
+#endif /* CSR_WIFI_HIP_NOISY */
+
+#ifdef CSR_WIFI_HIP_DEBUG_OFFLINE
+
+/*
+ * The unifi_debug_output buffer can be used to debug the HIP behaviour offline
+ * i.e. without using the tracing functions that change the timing.
+ *
+ * Call unifi_debug_log_to_buf() with printf arguments to store a string into
+ * unifi_debug_output. When unifi_debug_buf_dump() is called, the contents of the
+ * buffer are dumped with dump_str() which has to be implemented in the
+ * OS layer, during the porting exercise. The offset printed, holds the
+ * offset where the last character is (always a zero).
+ *
+ */
+
+#define UNIFI_DEBUG_GBUFFER_SIZE 8192
+static CsrCharString unifi_debug_output[UNIFI_DEBUG_GBUFFER_SIZE];
+static CsrCharString *unifi_dbgbuf_ptr = unifi_debug_output;
+static CsrCharString *unifi_dbgbuf_start = unifi_debug_output;
+
+static void append_char(CsrCharString c)
+{
+ /* write char and advance pointer */
+ *unifi_dbgbuf_ptr++ = c;
+ /* wrap pointer at end of buffer */
+ if ((unifi_dbgbuf_ptr - unifi_debug_output) >= UNIFI_DEBUG_GBUFFER_SIZE)
+ {
+ unifi_dbgbuf_ptr = unifi_debug_output;
+ }
+} /* append_char() */
+
+
+void unifi_debug_string_to_buf(const CsrCharString *str)
+{
+ const CsrCharString *p = str;
+ while (*p)
+ {
+ append_char(*p);
+ p++;
+ }
+ /* Update start-of-buffer pointer */
+ unifi_dbgbuf_start = unifi_dbgbuf_ptr + 1;
+ if ((unifi_dbgbuf_start - unifi_debug_output) >= UNIFI_DEBUG_GBUFFER_SIZE)
+ {
+ unifi_dbgbuf_start = unifi_debug_output;
+ }
+}
+
+
+void unifi_debug_log_to_buf(const CsrCharString *fmt, ...)
+{
+#define DEBUG_BUFFER_SIZE 80
+ static CsrCharString s[DEBUG_BUFFER_SIZE];
+ va_list args;
+
+ va_start(args, fmt);
+ CsrVsnprintf(s, DEBUG_BUFFER_SIZE, fmt, args);
+ va_end(args);
+
+ unifi_debug_string_to_buf(s);
+} /* unifi_debug_log_to_buf() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_debug_hex_to_buf
+ *
+ * puts the contents of the passed buffer into the debug buffer as a hex string
+ *
+ * Arguments:
+ * buff buffer to print as hex
+ * length number of chars to print
+ *
+ * Returns:
+ * None.
+ *
+ * ---------------------------------------------------------------------------
+ */
+void unifi_debug_hex_to_buf(const CsrCharString *buff, CsrUint16 length)
+{
+ CsrCharString s[5];
+ CsrUint16 i;
+
+ for (i = 0; i < length; i++)
+ {
+ CsrUInt16ToHex(0xff & buff[i], s);
+ unifi_debug_string_to_buf(s);
+ }
+}
+
+
+void unifi_debug_buf_dump(void)
+{
+ CsrInt32 offset = unifi_dbgbuf_ptr - unifi_debug_output;
+
+ unifi_error(NULL, "HIP debug buffer offset=%d\n", offset);
+ dump_str(unifi_debug_output + offset, UNIFI_DEBUG_GBUFFER_SIZE - offset);
+ dump_str(unifi_debug_output, offset);
+} /* unifi_debug_buf_dump() */
+
+
+#endif /* CSR_WIFI_HIP_DEBUG_OFFLINE */
+
+#ifdef CSR_PRE_ALLOC_NET_DATA
+#define NETDATA_PRE_ALLOC_BUF_SIZE 8000
+
+void prealloc_netdata_free(card_t *card)
+{
+ unifi_warning(card->ospriv, "prealloc_netdata_free: IN: w=%d r=%d\n", card->prealloc_netdata_w, card->prealloc_netdata_r);
+
+ while (card->bulk_data_desc_list[card->prealloc_netdata_r].data_length != 0)
+ {
+ unifi_warning(card->ospriv, "prealloc_netdata_free: r=%d\n", card->prealloc_netdata_r);
+
+ unifi_net_data_free(card->ospriv, &card->bulk_data_desc_list[card->prealloc_netdata_r]);
+ card->prealloc_netdata_r++;
+ card->prealloc_netdata_r %= BULK_DATA_PRE_ALLOC_NUM;
+ }
+ card->prealloc_netdata_r = card->prealloc_netdata_w = 0;
+
+ unifi_warning(card->ospriv, "prealloc_netdata_free: OUT: w=%d r=%d\n", card->prealloc_netdata_w, card->prealloc_netdata_r);
+}
+
+
+CsrResult prealloc_netdata_alloc(card_t *card)
+{
+ CsrResult r;
+
+ unifi_trace(card->ospriv, UDBG5, "prealloc_netdata_alloc: IN: w=%d r=%d\n", card->prealloc_netdata_w, card->prealloc_netdata_r);
+
+ while (card->bulk_data_desc_list[card->prealloc_netdata_w].data_length == 0)
+ {
+ r = unifi_net_data_malloc(card->ospriv, &card->bulk_data_desc_list[card->prealloc_netdata_w], NETDATA_PRE_ALLOC_BUF_SIZE);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "prealloc_netdata_alloc: Failed to allocate t-h bulk data\n");
+ return CSR_RESULT_FAILURE;
+ }
+ card->prealloc_netdata_w++;
+ card->prealloc_netdata_w %= BULK_DATA_PRE_ALLOC_NUM;
+ }
+ unifi_trace(card->ospriv, UDBG5, "prealloc_netdata_alloc: OUT: w=%d r=%d\n", card->prealloc_netdata_w, card->prealloc_netdata_r);
+
+ return CSR_RESULT_SUCCESS;
+}
+
+
+static CsrResult prealloc_netdata_get(card_t *card, bulk_data_desc_t *bulk_data_slot, CsrUint32 size)
+{
+ CsrResult r;
+
+ unifi_trace(card->ospriv, UDBG5, "prealloc_netdata_get: IN: w=%d r=%d\n", card->prealloc_netdata_w, card->prealloc_netdata_r);
+
+ if (card->bulk_data_desc_list[card->prealloc_netdata_r].data_length == 0)
+ {
+ unifi_error(card->ospriv, "prealloc_netdata_get: data_length = 0\n");
+ }
+
+ if ((size > NETDATA_PRE_ALLOC_BUF_SIZE) || (card->bulk_data_desc_list[card->prealloc_netdata_r].data_length == 0))
+ {
+ unifi_warning(card->ospriv, "prealloc_netdata_get: Calling net_data_malloc\n");
+
+ r = unifi_net_data_malloc(card->ospriv, bulk_data_slot, size);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "prealloc_netdata_get: Failed to allocate t-h bulk data\n");
+ return CSR_RESULT_FAILURE;
+ }
+ return CSR_RESULT_SUCCESS;
+ }
+
+ *bulk_data_slot = card->bulk_data_desc_list[card->prealloc_netdata_r];
+ card->bulk_data_desc_list[card->prealloc_netdata_r].os_data_ptr = NULL;
+ card->bulk_data_desc_list[card->prealloc_netdata_r].os_net_buf_ptr = NULL;
+ card->bulk_data_desc_list[card->prealloc_netdata_r].net_buf_length = 0;
+ card->bulk_data_desc_list[card->prealloc_netdata_r].data_length = 0;
+
+ card->prealloc_netdata_r++;
+ card->prealloc_netdata_r %= BULK_DATA_PRE_ALLOC_NUM;
+
+ unifi_trace(card->ospriv, UDBG5, "prealloc_netdata_get: OUT: w=%d r=%d\n", card->prealloc_netdata_w, card->prealloc_netdata_r);
+
+ return CSR_RESULT_SUCCESS;
+}
+
+
+#endif
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_sdio_interrupt_handler
+ *
+ * This function should be called by the OS-dependent code to handle
+ * an SDIO interrupt from the UniFi.
+ *
+ * Arguments:
+ * card Pointer to card context structure.
+ *
+ * Returns:
+ * None.
+ *
+ * Notes: This function may be called in DRS context. In this case,
+ * tracing with the unifi_trace(), etc, is not allowed.
+ * ---------------------------------------------------------------------------
+ */
+void unifi_sdio_interrupt_handler(card_t *card)
+{
+ /*
+ * Set the flag to say reason for waking was SDIO interrupt.
+ * Then ask the OS layer to run the unifi_bh to give attention to the UniFi.
+ */
+ card->bh_reason_unifi = 1;
+ unifi_run_bh(card->ospriv);
+} /* sdio_interrupt_handler() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_configure_low_power_mode
+ *
+ * This function should be called by the OS-dependent when
+ * the deep sleep signaling needs to be enabled or disabled.
+ *
+ * Arguments:
+ * card Pointer to card context structure.
+ * low_power_mode Disable/Enable the deep sleep signaling
+ * periodic_wake_mode UniFi wakes host periodically.
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success or a CSR error code.
+ * ---------------------------------------------------------------------------
+ */
+CsrResult unifi_configure_low_power_mode(card_t *card,
+ enum unifi_low_power_mode low_power_mode,
+ enum unifi_periodic_wake_mode periodic_wake_mode)
+{
+ card->low_power_mode = low_power_mode;
+ card->periodic_wake_mode = periodic_wake_mode;
+
+ unifi_trace(card->ospriv, UDBG1,
+ "unifi_configure_low_power_mode: new mode = %s, wake_host = %s\n",
+ (low_power_mode == UNIFI_LOW_POWER_DISABLED)?"disabled" : "enabled",
+ (periodic_wake_mode == UNIFI_PERIODIC_WAKE_HOST_DISABLED)?"FALSE" : "TRUE");
+
+ unifi_run_bh(card->ospriv);
+ return CSR_RESULT_SUCCESS;
+} /* unifi_configure_low_power_mode() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_force_low_power_mode
+ *
+ * This function should be called by the OS-dependent when
+ * UniFi needs to be set to the low power mode (e.g. on suspend)
+ *
+ * Arguments:
+ * card Pointer to card context structure.
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success or a CSR error code.
+ * ---------------------------------------------------------------------------
+ */
+CsrResult unifi_force_low_power_mode(card_t *card)
+{
+ if (card->low_power_mode == UNIFI_LOW_POWER_DISABLED)
+ {
+ unifi_error(card->ospriv, "Attempt to set mode to TORPID when lower power mode is disabled\n");
+ return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
+ }
+
+ return unifi_set_host_state(card, UNIFI_HOST_STATE_TORPID);
+} /* unifi_force_low_power_mode() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_bh
+ *
+ * This function should be called by the OS-dependent code when
+ * host and/or UniFi has requested an exchange of messages.
+ *
+ * Arguments:
+ * card Pointer to card context structure.
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success or a CSR error code.
+ * ---------------------------------------------------------------------------
+ */
+CsrResult unifi_bh(card_t *card, CsrUint32 *remaining)
+{
+ CsrResult r;
+ CsrResult csrResult;
+ CsrBool pending;
+ CsrInt32 iostate, j;
+ const enum unifi_low_power_mode low_power_mode = card->low_power_mode;
+ CsrUint16 data_slots_used = 0;
+
+
+ /* Process request to raise the maximum SDIO clock */
+ r = process_clock_request(card);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Error setting maximum SDIO clock\n");
+ goto exit;
+ }
+
+ /*
+ * Why was the BH thread woken?
+ * If it was an SDIO interrupt, UniFi is awake and we need to process it.
+ * If it was a host process queueing data, then we need to awaken UniFi.
+ *
+ * Priority of flags is top down.
+ *
+ * ----------------------------------------------------------+
+ * \state| AWAKE | DROWSY | TORPID |
+ * flag\ | | | |
+ * ---------+--------------+----------------+----------------|
+ * | do the host | go to AWAKE and| go to AWAKE and|
+ * unifi | protocol | do the host | do the host |
+ * | | protocol | protocol |
+ * ---------+--------------+----------------+----------------|
+ * | do the host | | |
+ * host | protocol | do nothing | go to DROWSY |
+ * | | | |
+ * ---------+--------------+----------------+----------------|
+ * | | | should not |
+ * timeout | go to TORPID | error, unifi | occur |
+ * | | didn't wake up | do nothing |
+ * ----------------------------------------------------------+
+ *
+ * Note that if we end up in the AWAKE state we always do the host protocol.
+ */
+
+ do
+ {
+ /*
+ * When the host state is set to DROWSY, then we can not disable the
+ * interrupts as UniFi can generate an interrupt even when the INT_ENABLE
+ * register has the interrupts disabled. This interrupt will be lost.
+ */
+ if (card->host_state == UNIFI_HOST_STATE_DROWSY || card->host_state == UNIFI_HOST_STATE_TORPID)
+ {
+ CsrUint8 reason_unifi;
+
+ /*
+ * An interrupt may occur while or after we cache the reason.
+ * This interrupt will cause the unifi_bh() to be scheduled again.
+ * Any interrupt that has happened before the register is read
+ * and is considered spurious has to acknowledged.
+ */
+ reason_unifi = card->bh_reason_unifi;
+
+ /*
+ * If an interrupt is received, check if it was a real one,
+ * set the host state to AWAKE and run the BH.
+ */
+ r = CardPendingInt(card, &pending);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ goto exit;
+ }
+
+ if (pending)
+ {
+ unifi_trace(card->ospriv, UDBG5,
+ "UNIFI_HOST_STATE_%s: Set state to AWAKE.\n",
+ (card->host_state == UNIFI_HOST_STATE_TORPID)?"TORPID" : "DROWSY");
+
+ r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE);
+ if (r == CSR_RESULT_SUCCESS)
+ {
+ (*remaining) = 0;
+ break;
+ }
+ }
+ else if (reason_unifi)
+ {
+ CsrSdioInterruptAcknowledge(card->sdio_if);
+ }
+
+ /*
+ * If an chip is in TORPID, and the host wants to wake it up,
+ * set the host state to DROWSY and wait for the wake-up interrupt.
+ */
+ if ((card->host_state == UNIFI_HOST_STATE_TORPID) && card->bh_reason_host)
+ {
+ r = unifi_set_host_state(card, UNIFI_HOST_STATE_DROWSY);
+ if (r == CSR_RESULT_SUCCESS)
+ {
+ /*
+ * set the timeout value to UNIFI_DEFAULT_WAKE_TIMEOUT
+ * to capture a wake error.
+ */
+ card->bh_reason_host = 0;
+ (*remaining) = UNIFI_DEFAULT_WAKE_TIMEOUT;
+ return CSR_RESULT_SUCCESS;
+ }
+
+ goto exit;
+ }
+
+ /*
+ * If the chip is in DROWSY, and the timeout expires,
+ * we need to reset the chip. This should never occur.
+ * (If it does, check that the calling thread set "remaining"
+ * according to the time remaining when unifi_bh() was called).
+ */
+ if ((card->host_state == UNIFI_HOST_STATE_DROWSY) && ((*remaining) == 0))
+ {
+ unifi_error(card->ospriv, "UniFi did not wake up on time...\n");
+
+ /*
+ * Check if Function1 has gone away or
+ * if we missed an SDIO interrupt.
+ */
+ r = unifi_check_io_status(card, &iostate);
+ if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
+ {
+ goto exit;
+ }
+ /* Need to reset and reboot */
+ return CSR_RESULT_FAILURE;
+ }
+ }
+ else
+ {
+ if (card->bh_reason_unifi || card->bh_reason_host)
+ {
+ break;
+ }
+
+ if (((*remaining) == 0) && (low_power_mode == UNIFI_LOW_POWER_ENABLED))
+ {
+ r = unifi_set_host_state(card, UNIFI_HOST_STATE_TORPID);
+ if (r == CSR_RESULT_SUCCESS)
+ {
+ (*remaining) = 0;
+ return CSR_RESULT_SUCCESS;
+ }
+
+ goto exit;
+ }
+ }
+
+ /* No need to run the host protocol */
+ return CSR_RESULT_SUCCESS;
+ } while (0);
+
+
+ /* Disable the SDIO interrupts while doing SDIO ops */
+ csrResult = CsrSdioInterruptDisable(card->sdio_if);
+ if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
+ {
+ r = CSR_WIFI_HIP_RESULT_NO_DEVICE;
+ goto exit;
+ }
+ if (csrResult != CSR_RESULT_SUCCESS)
+ {
+ r = ConvertCsrSdioToCsrHipResult(card, csrResult);
+ unifi_error(card->ospriv, "Failed to disable SDIO interrupts. unifi_bh queues error.\n");
+ goto exit;
+ }
+
+ /* Now that the interrupts are disabled, ack the interrupt */
+ CsrSdioInterruptAcknowledge(card->sdio_if);
+
+ /* Run the HIP */
+ r = process_bh(card);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ goto exit;
+ }
+
+ /*
+ * If host is now idle, schedule a timer for the delay before we
+ * let UniFi go into deep sleep.
+ * If the timer goes off, we will move to TORPID state.
+ * If UniFi raises an interrupt in the meantime, we will cancel
+ * the timer and start a new one when we become idle.
+ */
+ for (j = 0; j < UNIFI_NO_OF_TX_QS; j++)
+ {
+ data_slots_used += CSR_WIFI_HIP_Q_SLOTS_USED(&card->fh_traffic_queue[j]);
+ }
+
+ if ((low_power_mode == UNIFI_LOW_POWER_ENABLED) && (data_slots_used == 0))
+ {
+#ifndef CSR_WIFI_HIP_TA_DISABLE
+ if (card->ta_sampling.traffic_type != CSR_WIFI_ROUTER_CTRL_TRAFFIC_TYPE_PERIODIC)
+ {
+#endif
+ /* return the UNIFI_DEFAULT_HOST_IDLE_TIMEOUT, so we can go to sleep. */
+ unifi_trace(card->ospriv, UDBG5,
+ "Traffic is not periodic, set timer for TORPID.\n");
+ (*remaining) = UNIFI_DEFAULT_HOST_IDLE_TIMEOUT;
+#ifndef CSR_WIFI_HIP_TA_DISABLE
+ }
+ else
+ {
+ unifi_trace(card->ospriv, UDBG5,
+ "Traffic is periodic, set unifi to TORPID immediately.\n");
+ if (CardAreAllFromHostDataSlotsEmpty(card) == 1)
+ {
+ r = unifi_set_host_state(card, UNIFI_HOST_STATE_TORPID);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ goto exit;
+ }
+ }
+ }
+#endif
+ }
+
+ csrResult = CsrSdioInterruptEnable(card->sdio_if);
+ if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
+ {
+ r = CSR_WIFI_HIP_RESULT_NO_DEVICE;
+ }
+ if (csrResult != CSR_RESULT_SUCCESS)
+ {
+ r = ConvertCsrSdioToCsrHipResult(card, csrResult);
+ unifi_error(card->ospriv, "Failed to enable SDIO interrupt\n");
+ }
+
+exit:
+
+ unifi_trace(card->ospriv, UDBG4, "New state=%d\n", card->host_state);
+
+ if (r != CSR_RESULT_SUCCESS)
+ {
+#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE)
+ unifi_debug_buf_dump();
+#endif
+ /* If an interrupt has been raised, ack it here */
+ if (card->bh_reason_unifi)
+ {
+ CsrSdioInterruptAcknowledge(card->sdio_if);
+ }
+
+ unifi_error(card->ospriv,
+ "unifi_bh: state=%d %c, clock=%dkHz, interrupt=%d host=%d, power_save=%s\n",
+ card->host_state,
+ (card->host_state == UNIFI_HOST_STATE_AWAKE)?'A' : (card->host_state == UNIFI_HOST_STATE_DROWSY)?'D' : 'T',
+ card->sdio_clock_speed / 1000,
+ card->bh_reason_unifi, card->bh_reason_host,
+ (low_power_mode == UNIFI_LOW_POWER_DISABLED)?"disabled" : "enabled");
+
+ /* Try to capture firmware panic codes */
+ unifi_capture_panic(card);
+
+ /* Ask for a mini-coredump when the driver has reset UniFi */
+ unifi_coredump_request_at_next_reset(card, 1);
+ }
+
+ return r;
+} /* unifi_bh() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * process_clock_request
+ *
+ * Handle request from the OS layer to increase the SDIO clock speed.
+ * The fast clock is limited until the firmware has indicated that it has
+ * completed initialisation to the OS layer.
+ *
+ * Arguments:
+ * card Pointer to card context structure.
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success or CSR error code.
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult process_clock_request(card_t *card)
+{
+ CsrResult r = CSR_RESULT_SUCCESS;
+ CsrResult csrResult;
+
+ if (!card->request_max_clock)
+ {
+ return CSR_RESULT_SUCCESS; /* No pending request */
+ }
+
+ /*
+ * The SDIO clock speed request from the OS layer is only acted upon if
+ * the UniFi is awake. If it was in any other state, the clock speed will
+ * transition through SAFE to MAX while the host wakes it up, and the
+ * final speed reached will be UNIFI_SDIO_CLOCK_MAX_HZ.
+ * This assumes that the SME never requests low power mode while the f/w
+ * initialisation takes place.
+ */
+ if (card->host_state == UNIFI_HOST_STATE_AWAKE)
+ {
+ unifi_trace(card->ospriv, UDBG1, "Set SDIO max clock\n");
+ csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_MAX_HZ);
+ if (csrResult != CSR_RESULT_SUCCESS)
+ {
+ r = ConvertCsrSdioToCsrHipResult(card, csrResult);
+ }
+ else
+ {
+ card->sdio_clock_speed = UNIFI_SDIO_CLOCK_MAX_HZ; /* log the new freq */
+ }
+ }
+ else
+ {
+ unifi_trace(card->ospriv, UDBG1, "Will set SDIO max clock after wakeup\n");
+ }
+
+ /* Cancel the request now that it has been acted upon, or is about to be
+ * by the wakeup mechanism
+ */
+ card->request_max_clock = 0;
+
+ return r;
+}
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * process_bh
+ *
+ * Exchange messages with UniFi
+ *
+ * Arguments:
+ * card Pointer to card context structure.
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success or CSR error code.
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult process_bh(card_t *card)
+{
+ CsrResult r;
+ CsrBool more;
+ more = FALSE;
+
+ /* Process the reasons (interrupt, signals) */
+ do
+ {
+ /*
+ * Run in a while loop, to save clearing the interrupts
+ * every time around the outside loop.
+ */
+ do
+ {
+ /* If configured to run the HIP just once, skip first loop */
+ if (card->intmode & CSR_WIFI_INTMODE_RUN_BH_ONCE)
+ {
+ break;
+ }
+
+ r = handle_host_protocol(card, &more);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ return r;
+ }
+
+#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE)
+ unifi_debug_log_to_buf("c52=%d c53=%d tx=%d txc=%d rx=%d s=%d t=%d fc=%d\n",
+ card->cmd_prof.cmd52_count,
+ card->cmd_prof.cmd53_count,
+ card->cmd_prof.tx_count,
+ card->cmd_prof.tx_cfm_count,
+ card->cmd_prof.rx_count,
+ card->cmd_prof.sdio_cmd_signal,
+ card->cmd_prof.sdio_cmd_to_host,
+ card->cmd_prof.sdio_cmd_from_host_and_clear
+ );
+
+ card->cmd_prof.cmd52_count = card->cmd_prof.cmd53_count = 0;
+ card->cmd_prof.tx_count = card->cmd_prof.tx_cfm_count = card->cmd_prof.rx_count = 0;
+
+ card->cmd_prof.cmd52_f0_r_count = 0;
+ card->cmd_prof.cmd52_f0_w_count = 0;
+ card->cmd_prof.cmd52_r8or16_count = 0;
+ card->cmd_prof.cmd52_w8or16_count = 0;
+ card->cmd_prof.cmd52_r16_count = 0;
+ card->cmd_prof.cmd52_w16_count = 0;
+ card->cmd_prof.cmd52_r32_count = 0;
+
+ card->cmd_prof.sdio_cmd_signal = 0;
+ card->cmd_prof.sdio_cmd_clear_slot = 0;
+ card->cmd_prof.sdio_cmd_to_host = 0;
+ card->cmd_prof.sdio_cmd_from_host = 0;
+ card->cmd_prof.sdio_cmd_from_host_and_clear = 0;
+#endif
+
+
+ } while (more || card->bh_reason_unifi || card->bh_reason_host);
+
+ /* Acknowledge the h/w interrupt */
+ r = CardClearInt(card);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to acknowledge interrupt.\n");
+ return r;
+ }
+
+ /*
+ * UniFi may have tried to generate an interrupt during the
+ * CardClearInt() was running. So, we need to run the host
+ * protocol again, to check if there are any pending requests.
+ */
+ r = handle_host_protocol(card, &more);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ return r;
+ }
+
+#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE)
+ unifi_debug_log_to_buf("c52=%d c53=%d tx=%d txc=%d rx=%d s=%d t=%d fc=%d\n",
+ card->cmd_prof.cmd52_count,
+ card->cmd_prof.cmd53_count,
+ card->cmd_prof.tx_count,
+ card->cmd_prof.tx_cfm_count,
+ card->cmd_prof.rx_count,
+ card->cmd_prof.sdio_cmd_signal,
+ card->cmd_prof.sdio_cmd_to_host,
+ card->cmd_prof.sdio_cmd_from_host_and_clear
+ );
+
+ card->cmd_prof.cmd52_count = card->cmd_prof.cmd53_count = 0;
+ card->cmd_prof.tx_count = card->cmd_prof.tx_cfm_count = card->cmd_prof.rx_count = 0;
+
+ card->cmd_prof.cmd52_f0_r_count = 0;
+ card->cmd_prof.cmd52_f0_w_count = 0;
+ card->cmd_prof.cmd52_r8or16_count = 0;
+ card->cmd_prof.cmd52_w8or16_count = 0;
+ card->cmd_prof.cmd52_r16_count = 0;
+ card->cmd_prof.cmd52_w16_count = 0;
+ card->cmd_prof.cmd52_r32_count = 0;
+
+ card->cmd_prof.sdio_cmd_signal = 0;
+ card->cmd_prof.sdio_cmd_clear_slot = 0;
+ card->cmd_prof.sdio_cmd_to_host = 0;
+ card->cmd_prof.sdio_cmd_from_host = 0;
+ card->cmd_prof.sdio_cmd_from_host_and_clear = 0;
+#endif
+ /* If configured to run the HIP just once, work is now done */
+ if (card->intmode & CSR_WIFI_INTMODE_RUN_BH_ONCE)
+ {
+ break;
+ }
+
+ } while (more || card->bh_reason_unifi || card->bh_reason_host);
+
+#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE)
+ if ((card->intmode & CSR_WIFI_INTMODE_RUN_BH_ONCE) == 0)
+ {
+ unifi_debug_log_to_buf("proc=%d\n",
+ card->cmd_prof.process_count);
+ }
+#endif
+
+ return CSR_RESULT_SUCCESS;
+} /* process_bh() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * handle_host_protocol
+ *
+ * This function implements the Host Interface Protocol (HIP) as
+ * described in the Host Interface Protocol Specification.
+ *
+ * Arguments:
+ * card Pointer to card context structure.
+ * processed_something Pointer to location to update processing status:
+ * TRUE when data was transferred
+ * FALSE when no data was transferred (queues empty)
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success or CSR error code.
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult handle_host_protocol(card_t *card, CsrBool *processed_something)
+{
+ CsrResult r;
+ CsrInt32 done;
+
+ *processed_something = FALSE;
+
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, " ======================== \n");
+#endif /* CSR_WIFI_HIP_NOISY */
+
+#ifdef CSR_WIFI_HIP_DATA_PLANE_PROFILE
+ card->cmd_prof.process_count++;
+#endif
+
+ card->bh_reason_unifi = card->bh_reason_host = 0;
+ card->generate_interrupt = 0;
+
+
+ /*
+ * (Re)fill the T-H signal buffer
+ */
+ r = read_to_host_signals(card, &done);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Error occured reading to-host signals\n");
+ return r;
+ }
+ if (done > 0)
+ {
+ *processed_something = TRUE;
+ }
+
+ /*
+ * Process any to-host signals.
+ * Perform any requested CMD53 transfers here, but just queue any
+ * bulk data command responses.
+ */
+ r = process_to_host_signals(card, &done);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Error occured processing to-host signals\n");
+ return r;
+ }
+
+ /* Now send any signals in the F-H queues */
+ /* Give precedence to the command queue */
+ r = process_fh_cmd_queue(card, &done);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Error occured processing from-host signals\n");
+ return r;
+ }
+ if (done > 0)
+ {
+ *processed_something = TRUE;
+ }
+
+ r = process_fh_traffic_queue(card, &done);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Error occured processing from-host data signals\n");
+ return r;
+ }
+ if (done > 0)
+ {
+ *processed_something = TRUE;
+ }
+
+ /* Flush out the batch of signals to the UniFi. */
+ r = flush_fh_buffer(card);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to copy from-host signals to UniFi\n");
+ return r;
+ }
+
+
+ /*
+ * Send the host interrupt to say the queues have been modified.
+ */
+ if (card->generate_interrupt)
+ {
+ r = CardGenInt(card);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to notify UniFi that queues have been modified.\n");
+ return r;
+ }
+ }
+#ifdef CSR_WIFI_RX_PATH_SPLIT_DONT_USE_WQ
+ unifi_rx_queue_flush(card->ospriv);
+#endif
+ /* See if we can re-enable transmission now */
+ restart_packet_flow(card);
+
+#ifdef CSR_PRE_ALLOC_NET_DATA
+ r = prealloc_netdata_alloc(card);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "prealloc_netdata failed\n");
+ return r;
+ }
+#endif
+
+ /*
+ * Don't put the thread sleep if we just interacted with the chip,
+ * there might be more to do if we look again.
+ */
+ return r;
+} /* handle_host_protocol() */
+
+
+/*
+ * Rounds the given signal length in bytes to a whole number
+ * of sig_frag_size.
+ */
+#define GET_CHUNKS_FOR(SIG_FRAG_SIZE, LENGTH) (((LENGTH) + ((SIG_FRAG_SIZE)-1)) / (SIG_FRAG_SIZE))
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * read_to_host_signals
+ *
+ * Read everything pending in the UniFi TH signal buffer.
+ * Only do it if the local buffer is empty.
+ *
+ * Arguments:
+ * card Pointer to card context struct
+ * processed Number of signals read:
+ * 0 if there were no signals pending,
+ * 1 if we read at least one signal
+ * Returns:
+ * CSR error code if an error occurred.
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult read_to_host_signals(card_t *card, CsrInt32 *processed)
+{
+ CsrInt32 count_thw, count_thr;
+ CsrInt32 unread_chunks, unread_bytes;
+ CsrResult r;
+
+ *processed = 0;
+
+ /* Read any pending signals or bulk data commands */
+ count_thw = unifi_read_shared_count(card, card->sdio_ctrl_addr + 4);
+ if (count_thw < 0)
+ {
+ unifi_error(card->ospriv, "Failed to read to-host sig written count\n");
+ return CSR_RESULT_FAILURE;
+ }
+ card->to_host_signals_w = count_thw; /* diag */
+
+ count_thr = card->to_host_signals_r;
+
+ if (count_thw == count_thr)
+ {
+ return CSR_RESULT_SUCCESS;
+ }
+
+ unread_chunks =
+ (((count_thw - count_thr) + 128) % 128) - card->th_buffer.count;
+
+ if (unread_chunks == 0)
+ {
+ return CSR_RESULT_SUCCESS;
+ }
+
+ unread_bytes = card->config_data.sig_frag_size * unread_chunks;
+
+
+ r = unifi_bulk_rw(card,
+ card->config_data.tohost_sigbuf_handle,
+ card->th_buffer.ptr,
+ unread_bytes,
+ UNIFI_SDIO_READ);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to read ToHost signal\n");
+ return r;
+ }
+
+ card->th_buffer.ptr += unread_bytes;
+ card->th_buffer.count += (CsrUint16)unread_chunks;
+
+ *processed = 1;
+
+ return CSR_RESULT_SUCCESS;
+} /* read_to_host_signals() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * update_to_host_signals_r
+ *
+ * Advance the shared-memory count of chunks read from the to-host
+ * signal buffer.
+ * Raise a UniFi internal interrupt to tell the firmware that the
+ * count has changed.
+ *
+ * Arguments:
+ * card Pointer to card context struct
+ * pending Number of chunks remaining
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success or CSR error code
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult update_to_host_signals_r(card_t *card, CsrInt16 pending)
+{
+ CsrResult r;
+
+ card->to_host_signals_r =
+ (card->to_host_signals_r + (card->th_buffer.count - pending)) % 128;
+ card->th_buffer.count = pending;
+
+ /* Update the count of signals read */
+ r = unifi_write_8_or_16(card, card->sdio_ctrl_addr + 6,
+ (CsrUint8)card->to_host_signals_r);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to update to-host signals read\n");
+ return r;
+ }
+
+ r = CardGenInt(card);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to notify UniFi that we processed to-host signals.\n");
+ return r;
+ }
+
+ card->generate_interrupt = 0;
+
+ return CSR_RESULT_SUCCESS;
+} /* update_to_host_signals_r() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * read_unpack_cmd
+ *
+ * Converts a wire-formatted command to the host bulk_data_cmd_t structure.
+ *
+ * Arguments:
+ * ptr Pointer to the command
+ * bulk_data_cmd Pointer to the host structure
+ *
+ * Returns:
+ * None.
+ * ---------------------------------------------------------------------------
+ */
+static void read_unpack_cmd(const CsrUint8 *ptr, bulk_data_cmd_t *bulk_data_cmd)
+{
+ CsrInt16 index = 0;
+ bulk_data_cmd->cmd_and_len = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(ptr + index);
+ index += SIZEOF_UINT16;
+ bulk_data_cmd->data_slot = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(ptr + index);
+ index += SIZEOF_UINT16;
+ bulk_data_cmd->offset = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(ptr + index);
+ index += SIZEOF_UINT16;
+ bulk_data_cmd->buffer_handle = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(ptr + index);
+ index += SIZEOF_UINT16;
+} /* read_unpack_cmd */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * process_to_host_signals
+ *
+ * Read and dispatch signals from the UniFi
+ *
+ * Arguments:
+ * card Pointer to card context struct
+ * processed Pointer to location to write processing result:
+ * 0 if there were no signals pending,
+ * 1 if we read at least one signal
+ *
+ * Returns:
+ * CSR error code if there was an error
+ *
+ * Notes:
+ * Since bulk data transfers can take a long time, if we wait until
+ * all are done before we acknowledge the signals, the UniFi runs out
+ * of buffer space. Therefore we keep a count of the bytes transferred
+ * in bulk data commands, and update the to-host-signals-read count
+ * if we've done a large transfer.
+ *
+ * All data in the f/w is stored in a little endian format, without any
+ * padding bytes. Every read from the memory has to be transformed in
+ * host (cpu specific) format, before we can process it. Therefore we
+ * use read_unpack_cmd() and read_unpack_signal() to convert the raw data
+ * contained in the card->th_buffer.buf to host structures.
+ * Important: UDI clients use wire-formatted structures, so we need to
+ * indicate all data, as we have read it from the device.
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult process_to_host_signals(card_t *card, CsrInt32 *processed)
+{
+ CsrInt16 pending;
+ CsrInt16 remaining;
+ CsrUint8 *bufptr;
+ bulk_data_param_t data_ptrs;
+ CsrInt16 cmd;
+ CsrUint16 sig_len;
+ CsrInt16 i;
+ CsrUint16 chunks_in_buf;
+ CsrUint16 bytes_transferred = 0;
+ CsrResult r = CSR_RESULT_SUCCESS;
+
+ *processed = 0;
+
+ pending = card->th_buffer.count;
+
+ /* Are there new to-host signals? */
+ unifi_trace(card->ospriv, UDBG4, "handling %d to-host chunks\n", pending);
+
+ if (!pending)
+ {
+ return CSR_RESULT_SUCCESS;
+ }
+
+ /*
+ * This is a pointer to the raw data we have read from the f/w.
+ * Can be a signal or a command. Note that we need to convert
+ * it to a host structure before we process it.
+ */
+ bufptr = card->th_buffer.buf;
+
+ while (pending > 0)
+ {
+ CsrInt16 f_flush_count = 0;
+
+ /*
+ * Command and length are common to signal and bulk data msgs.
+ * If command == 0 (i.e. a signal), len is number of bytes
+ * *following* the 2-byte header.
+ */
+ cmd = bufptr[1] >> 4;
+ sig_len = bufptr[0] + ((bufptr[1] & 0x0F) << 8);
+
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, "Received UniFi msg cmd=%d, len=%d\n",
+ cmd, sig_len);
+#endif /* CSR_WIFI_HIP_NOISY */
+
+ if ((sig_len == 0) &&
+ ((cmd != SDIO_CMD_CLEAR_SLOT) && (cmd != SDIO_CMD_PADDING)))
+ {
+ unifi_error(card->ospriv, "incomplete signal or command: has size zero\n");
+ return CSR_RESULT_FAILURE;
+ }
+ /*
+ * Make sure the buffer contains a complete message.
+ * Signals may occupy multiple chunks, bulk-data commands occupy
+ * one chunk.
+ */
+ if (cmd == SDIO_CMD_SIGNAL)
+ {
+ chunks_in_buf = GET_CHUNKS_FOR(card->config_data.sig_frag_size, (CsrUint16)(sig_len + 2));
+ }
+ else
+ {
+ chunks_in_buf = 1;
+ }
+
+ if (chunks_in_buf > (CsrUint16)pending)
+ {
+ unifi_error(card->ospriv, "incomplete signal (0x%x?): need %d chunks, got %d\n",
+ GET_SIGNAL_ID(bufptr + 2),
+ chunks_in_buf, pending);
+ unifi_error(card->ospriv, " thsw=%d, thsr=%d\n",
+ card->to_host_signals_w,
+ card->to_host_signals_r);
+ return CSR_RESULT_FAILURE;
+ }
+
+
+ switch (cmd)
+ {
+ case SDIO_CMD_SIGNAL:
+ /* This is a signal. Read the rest of it and then handle it. */
+#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE)
+ card->cmd_prof.sdio_cmd_signal++;
+#endif
+
+ for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++)
+ {
+ /* Retrieve dataRefs[i].DataLength */
+ CsrUint16 data_len = GET_PACKED_DATAREF_LEN(bufptr + 2, i);
+
+ /*
+ * The bulk data length in the signal can not be greater than
+ * the maximun length allowed by the SDIO config structure.
+ */
+ if (data_len > card->config_data.data_slot_size)
+ {
+ unifi_error(card->ospriv,
+ "Bulk Data length (%d) exceeds Maximum Bulk Data length (%d)\n",
+ data_len, card->config_data.data_slot_size);
+ return CSR_RESULT_FAILURE;
+ }
+
+ /*
+ * Len here might not be the same as the length in the
+ * bulk data slot. The slot length will always be even,
+ * but len could be odd.
+ */
+ if (data_len != 0)
+ {
+ /* Retrieve dataRefs[i].SlotNumber */
+ CsrInt16 slot = GET_PACKED_DATAREF_SLOT(bufptr + 2, i);
+
+ if (slot >= card->config_data.num_tohost_data_slots)
+ {
+ unifi_error(card->ospriv, "!!!bad slot number in to-host signal: %d, sig 0x%X\n",
+ slot, cmd);
+ return CSR_RESULT_FAILURE;
+ }
+
+ data_ptrs.d[i].os_data_ptr = card->to_host_data[slot].os_data_ptr;
+ data_ptrs.d[i].os_net_buf_ptr = card->to_host_data[slot].os_net_buf_ptr;
+ data_ptrs.d[i].net_buf_length = card->to_host_data[slot].net_buf_length;
+ data_ptrs.d[i].data_length = data_len;
+ }
+ else
+ {
+ UNIFI_INIT_BULK_DATA(&data_ptrs.d[i]);
+ }
+ }
+
+ /*
+ * Log the signal to the UDI, before call unifi_receive_event() as
+ * it can modify the bulk data.
+ */
+ if (card->udi_hook)
+ {
+ (*card->udi_hook)(card->ospriv, bufptr + 2, sig_len,
+ &data_ptrs, UDI_LOG_TO_HOST);
+ }
+
+#ifdef CSR_WIFI_HIP_DATA_PLANE_PROFILE
+ if (GET_SIGNAL_ID(bufptr + 2) == CSR_MA_PACKET_CONFIRM_ID)
+ {
+ card->cmd_prof.tx_cfm_count++;
+ }
+ else if (GET_SIGNAL_ID(bufptr + 2) == CSR_MA_PACKET_INDICATION_ID)
+ {
+ if (data_ptrs.d[0].os_data_ptr)
+ {
+ if ((*data_ptrs.d[0].os_data_ptr) & 0x08)
+ {
+ card->cmd_prof.rx_count++;
+ }
+ }
+ }
+#endif
+ /*
+ * Check if the signal is MA-PACKET.cfm and if so check the status.
+ * If the status is failure, search through the slot records to find
+ * if any slots are occupied for this host tag. This can happen if
+ * f/w has not downloaded the bulkdata and before that itself it has
+ * signalled the confirm with failure. If it finds a slot with that
+ * host tag then, it clears the corresponding slot
+ */
+
+ if (GET_SIGNAL_ID(bufptr + 2) == CSR_MA_PACKET_CONFIRM_ID)
+ {
+ /* Get host tag and transmission status */
+ CsrUint32 host_tag = GET_PACKED_MA_PACKET_CONFIRM_HOST_TAG(bufptr + 2);
+ CsrUint16 status = GET_PACKED_MA_PACKET_CONFIRM_TRANSMISSION_STATUS(bufptr + 2);
+
+ unifi_trace(card->ospriv, UDBG4, "process_to_host_signals signal ID=%x host Tag=%x status=%x\n",
+ GET_SIGNAL_ID(bufptr + 2), host_tag, status);
+
+ /* If transmission status is failure then search through the slot records
+ * and if for any slot records the clear slot is not done then do it now
+ */
+
+ if (status && (card->fh_slot_host_tag_record))
+ {
+ CsrUint16 num_fh_slots = card->config_data.num_fromhost_data_slots;
+ CsrUint16 i = 0;
+
+ /* search through the list of slot records and match with host tag
+ * If a slot is not yet cleared then clear the slot from here
+ */
+ for (i = 0; i < num_fh_slots; i++)
+ {
+ if (card->fh_slot_host_tag_record[i] == host_tag)
+ {
+ unifi_trace(card->ospriv, UDBG4, "process_to_host_signals Clear slot=%x host tag=%x\n", i, host_tag);
+ card->fh_slot_host_tag_record[i] = CSR_WIFI_HIP_RESERVED_HOST_TAG;
+
+ /* Set length field in from_host_data array to 0 */
+ CardClearFromHostDataSlot(card, i);
+
+ break;
+ }
+ }
+ }
+ }
+
+ /* Pass event to OS layer */
+ unifi_receive_event(card->ospriv, bufptr + 2, sig_len, &data_ptrs);
+
+ /* Initialise the to_host data, so it can be re-used. */
+ for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++)
+ {
+ /* The slot is only valid if the length is non-zero. */
+ if (GET_PACKED_DATAREF_LEN(bufptr + 2, i) != 0)
+ {
+ CsrInt16 slot = GET_PACKED_DATAREF_SLOT(bufptr + 2, i);
+ if (slot < card->config_data.num_tohost_data_slots)
+ {
+ UNIFI_INIT_BULK_DATA(&card->to_host_data[slot]);
+ }
+ }
+ }
+
+#ifndef CSR_WIFI_DEFER_TH_FLUSH
+ /*
+ * If we have previously transferred a lot of data, ack
+ * the signals read so far, so f/w can reclaim the buffer
+ * memory sooner.
+ */
+ if (bytes_transferred >= TO_HOST_FLUSH_THRESHOLD)
+ {
+ f_flush_count = 1;
+ }
+#endif
+ break;
+
+
+ case SDIO_CMD_CLEAR_SLOT:
+#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE)
+ card->cmd_prof.sdio_cmd_clear_slot++;
+#endif
+ /* This is a clear slot command. */
+ if (sig_len != 0)
+ {
+ unifi_error(card->ospriv, "process_to_host_signals: clear slot, bad data len: 0x%X at offset %d\n",
+ sig_len, bufptr - card->th_buffer.buf);
+ return CSR_RESULT_FAILURE;
+ }
+
+ r = process_clear_slot_command(card, bufptr);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to process clear slot\n");
+ return r;
+ }
+ break;
+
+ case SDIO_CMD_TO_HOST_TRANSFER:
+ case SDIO_CMD_FROM_HOST_TRANSFER:
+ case SDIO_CMD_FROM_HOST_AND_CLEAR:
+ case SDIO_CMD_OVERLAY_TRANSFER:
+ /* This is a bulk data command. */
+ if (sig_len & 1)
+ {
+ unifi_error(card->ospriv, "process_to_host_signals: bulk data, bad data len: 0x%X at offset %d\n",
+ sig_len, bufptr - card->th_buffer.buf);
+ return CSR_RESULT_FAILURE;
+ }
+
+ r = process_bulk_data_command(card, bufptr, cmd, sig_len);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to process bulk cmd\n");
+ return r;
+ }
+ /* Count the bytes transferred */
+ bytes_transferred += sig_len;
+
+ if (cmd == SDIO_CMD_FROM_HOST_AND_CLEAR)
+ {
+#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE)
+ card->cmd_prof.sdio_cmd_from_host_and_clear++;
+#endif
+#ifndef CSR_WIFI_DEFER_TH_FLUSH
+ f_flush_count = 1;
+#endif
+ }
+#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE)
+ else if (cmd == SDIO_CMD_FROM_HOST_TRANSFER)
+ {
+ card->cmd_prof.sdio_cmd_from_host++;
+ }
+ else if (cmd == SDIO_CMD_TO_HOST_TRANSFER)
+ {
+ card->cmd_prof.sdio_cmd_to_host++;
+ }
+#endif
+ break;
+
+ case SDIO_CMD_PADDING:
+ break;
+
+ default:
+ unifi_error(card->ospriv, "Unrecognised to-host command: %d\n", cmd);
+ break;
+ }
+
+ bufptr += chunks_in_buf * card->config_data.sig_frag_size;
+ pending -= chunks_in_buf;
+
+ /*
+ * Write out the host signal count when a significant
+ * number of bytes of bulk data have been transferred or
+ * when we have performed a CopyFromHostAndClear.
+ */
+ if (f_flush_count)
+ {
+ r = update_to_host_signals_r(card, pending);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ return r;
+ }
+ bytes_transferred = 0;
+ }
+ }
+
+ if (pending)
+ {
+ unifi_warning(card->ospriv, "proc_th_sigs: %d unprocessed\n", pending);
+ }
+
+ /* If we processed any signals, write the updated count to UniFi */
+ if (card->th_buffer.count != pending)
+ {
+ r = update_to_host_signals_r(card, pending);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ return r;
+ }
+ }
+
+ /*
+ * Reset the buffer pointer, copying down any un-processed signals.
+ * This can happen if we enable the optimisation in read_to_host_signals()
+ * that limits the length to whole blocks.
+ */
+ remaining = card->th_buffer.ptr - bufptr;
+ if (remaining < 0)
+ {
+ unifi_error(card->ospriv, "Processing TH signals overran the buffer\n");
+ return CSR_RESULT_FAILURE;
+ }
+ if (remaining > 0)
+ {
+ /* Use a safe copy because source and destination may overlap */
+ CsrUint8 *d = card->th_buffer.buf;
+ CsrUint8 *s = bufptr;
+ CsrInt32 n = remaining;
+ while (n--)
+ {
+ *d++ = *s++;
+ }
+ }
+ card->th_buffer.ptr = card->th_buffer.buf + remaining;
+
+
+ /* If we reach here then we processed something */
+ *processed = 1;
+ return CSR_RESULT_SUCCESS;
+} /* process_to_host_signals() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * process_clear_slot_command
+ *
+ * Process a clear slot command fom the UniFi.
+ *
+ * Arguments:
+ * card Pointer to card context struct
+ * bdcmd Pointer to bulk-data command msg from UniFi
+ *
+ * Returns:
+ * 0 on success, CSR error code on error
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult process_clear_slot_command(card_t *card, const CsrUint8 *cmdptr)
+{
+ CsrUint16 data_slot;
+ CsrInt16 slot;
+
+ data_slot = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cmdptr + SIZEOF_UINT16);
+
+ unifi_trace(card->ospriv, UDBG4, "Processing clear slot cmd, slot=0x%X\n",
+ data_slot);
+
+ slot = data_slot & 0x7FFF;
+
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, "CMD clear data slot 0x%04x\n", data_slot);
+#endif /* CSR_WIFI_HIP_NOISY */
+
+ if (data_slot & SLOT_DIR_TO_HOST)
+ {
+ if (slot >= card->config_data.num_tohost_data_slots)
+ {
+ unifi_error(card->ospriv,
+ "Invalid to-host data slot in SDIO_CMD_CLEAR_SLOT: %d\n",
+ slot);
+ return CSR_RESULT_FAILURE;
+ }
+ /* clear to-host data slot */
+ unifi_warning(card->ospriv, "Unexpected clear to-host data slot cmd: 0x%04x\n",
+ data_slot);
+ }
+ else
+ {
+ if (slot >= card->config_data.num_fromhost_data_slots)
+ {
+ unifi_error(card->ospriv,
+ "Invalid from-host data slot in SDIO_CMD_CLEAR_SLOT: %d\n",
+ slot);
+ return CSR_RESULT_FAILURE;
+ }
+
+ /*
+ * The driver is the owner to clear all slots now
+ * Ref - comment in process_fh_traffic_queue
+ * so it will just ignore the clear slot command from firmware
+ * and return success
+ */
+ return CSR_RESULT_SUCCESS;
+
+ /* Set length field in from_host_data array to 0 */
+ /* CardClearFromHostDataSlot(card, slot); */
+ }
+
+ return CSR_RESULT_SUCCESS;
+} /* process_clear_slot_command() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * process_bulk_data_command
+ *
+ * Process a bulk data request from the UniFi.
+ *
+ * Arguments:
+ * card Pointer to card context struct
+ * bdcmd Pointer to bulk-data command msg from UniFi
+ * cmd, len Decoded values of command and length from the msg header
+ * Cmd will only be one of:
+ * SDIO_CMD_TO_HOST_TRANSFER
+ * SDIO_CMD_FROM_HOST_TRANSFER
+ * SDIO_CMD_FROM_HOST_AND_CLEAR
+ * SDIO_CMD_OVERLAY_TRANSFER
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success, CSR error code on error
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult process_bulk_data_command(card_t *card, const CsrUint8 *cmdptr,
+ CsrInt16 cmd, CsrUint16 len)
+{
+ bulk_data_desc_t *bdslot;
+#ifdef CSR_WIFI_ALIGNMENT_WORKAROUND
+ CsrUint8 *host_bulk_data_slot;
+#endif
+ bulk_data_cmd_t bdcmd;
+ CsrInt16 offset;
+ CsrInt16 slot;
+ CsrInt16 dir;
+ CsrResult r;
+
+ read_unpack_cmd(cmdptr, &bdcmd);
+
+ unifi_trace(card->ospriv, UDBG4, "Processing bulk data cmd %d %s, len=%d, slot=0x%X\n",
+ cmd, lookup_bulkcmd_name(cmd), len, bdcmd.data_slot);
+
+ /*
+ * Round up the transfer length if required.
+ * This is useful to force all transfers to be a multiple of the SDIO block
+ * size, so the SDIO driver won't try to use a byte-mode CMD53. These are
+ * broken on some hardware platforms.
+ */
+ if (card->sdio_io_block_pad)
+ {
+ len = (len + card->sdio_io_block_size - 1) & ~(card->sdio_io_block_size - 1);
+ unifi_trace(card->ospriv, UDBG4, "Rounded bulk data length up to %d\n", len);
+ }
+
+ slot = bdcmd.data_slot & 0x7FFF;
+
+ if (cmd == SDIO_CMD_OVERLAY_TRANSFER)
+ {
+ return CSR_WIFI_HIP_RESULT_INVALID_VALUE; /* Not used on CSR6xxx */
+ }
+ else
+ {
+ if (bdcmd.data_slot & SLOT_DIR_TO_HOST)
+ {
+ /* Request is for to-host bulk data */
+
+ /* Check sanity of slot number */
+ if (slot >= card->config_data.num_tohost_data_slots)
+ {
+ unifi_error(card->ospriv,
+ "Invalid to-host data slot in SDIO bulk xfr req: %d\n",
+ slot);
+ return CSR_RESULT_FAILURE;
+ }
+
+ /* Allocate memory for card->to_host_data[slot] bulk data here. */
+#ifdef CSR_PRE_ALLOC_NET_DATA
+ r = prealloc_netdata_get(card, &card->to_host_data[slot], len);
+#else
+ r = unifi_net_data_malloc(card->ospriv, &card->to_host_data[slot], len);
+#endif
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to allocate t-h bulk data\n");
+ return CSR_RESULT_FAILURE;
+ }
+
+ bdslot = &card->to_host_data[slot];
+
+ /* Make sure that the buffer is 4-bytes aligned */
+ r = unifi_net_dma_align(card->ospriv, bdslot);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to align t-h bulk data buffer for DMA\n");
+ return CSR_RESULT_FAILURE;
+ }
+ }
+ else
+ {
+ /* Request is for from-host bulk data */
+
+ if (slot >= card->config_data.num_fromhost_data_slots)
+ {
+ unifi_error(card->ospriv,
+ "Invalid from-host data slot in SDIO bulk xfr req: %d\n",
+ slot);
+ return CSR_RESULT_FAILURE;
+ }
+ bdslot = &card->from_host_data[slot].bd;
+ }
+ offset = bdcmd.offset;
+ }
+ /* Do the transfer */
+ dir = (cmd == SDIO_CMD_TO_HOST_TRANSFER)?
+ UNIFI_SDIO_READ : UNIFI_SDIO_WRITE;
+
+ unifi_trace(card->ospriv, UDBG4,
+ "Bulk %c %s len=%d, handle %d - slot=%d %p+(%d)\n",
+ (dir == UNIFI_SDIO_READ)?'R' : 'W',
+ lookup_bulkcmd_name(cmd),
+ len,
+ bdcmd.buffer_handle,
+ slot, bdslot->os_data_ptr, offset);
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, "Bulk %s len=%d, handle %d - slot=%d %p+(%d)\n",
+ lookup_bulkcmd_name(cmd),
+ len,
+ bdcmd.buffer_handle,
+ slot, bdslot->os_data_ptr, offset);
+#endif /* CSR_WIFI_HIP_NOISY */
+
+
+ if (bdslot->os_data_ptr == NULL)
+ {
+ unifi_error(card->ospriv, "Null os_data_ptr - Bulk %s handle %d - slot=%d o=(%d)\n",
+ lookup_bulkcmd_name(cmd),
+ bdcmd.buffer_handle,
+ slot,
+ offset);
+ return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
+ }
+
+#ifdef CSR_WIFI_ALIGNMENT_WORKAROUND
+ /* if os_data_ptr is not 4-byte aligned, then allocate a new buffer and copy data
+ to new buffer to ensure the address passed to unifi_bulk_rw is 4-byte aligned */
+
+ if (len != 0 && (dir == UNIFI_SDIO_WRITE) && (((CsrIntptr)bdslot->os_data_ptr + offset) & 3))
+ {
+ host_bulk_data_slot = CsrMemAlloc(len);
+
+ if (!host_bulk_data_slot)
+ {
+ unifi_error(card->ospriv, " failed to allocate request_data before unifi_bulk_rw\n");
+ return -1;
+ }
+
+ CsrMemCpy((void *)host_bulk_data_slot,
+ (void *)(bdslot->os_data_ptr + offset), len);
+
+ r = unifi_bulk_rw(card,
+ bdcmd.buffer_handle,
+ (void *)host_bulk_data_slot,
+ len,
+ dir);
+ }
+ else
+#endif
+ {
+ r = unifi_bulk_rw(card,
+ bdcmd.buffer_handle,
+ (void *)(bdslot->os_data_ptr + offset),
+ len,
+ dir);
+ }
+
+ if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
+ {
+ return r;
+ }
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv,
+ "Failed: %s hlen=%d, slen=%d, handle %d - slot=%d %p+0x%X\n",
+ lookup_bulkcmd_name(cmd),
+ len, /* Header length */
+ bdslot->data_length, /* Length stored in slot */
+ bdcmd.buffer_handle,
+ slot, bdslot->os_data_ptr, offset);
+ return r;
+ }
+
+ bdslot->data_length = len;
+
+ if (cmd == SDIO_CMD_FROM_HOST_AND_CLEAR)
+ {
+ if (slot >= card->config_data.num_fromhost_data_slots)
+ {
+ unifi_error(card->ospriv,
+ "Invalid from-host data slot in SDIO_CMD_FROM_HOST_AND_CLEAR: %d\n",
+ slot);
+ return CSR_RESULT_FAILURE;
+ }
+
+#ifdef CSR_WIFI_ALIGNMENT_WORKAROUND
+ /* moving this check before we clear host data slot */
+ if ((len != 0) && (dir == UNIFI_SDIO_WRITE) && (((CsrIntptr)bdslot->os_data_ptr + offset) & 3))
+ {
+ CsrMemFree(host_bulk_data_slot);
+ }
+#endif
+
+ if (card->fh_slot_host_tag_record)
+ {
+ unifi_trace(card->ospriv, UDBG5, "CopyFromHostAndClearSlot Reset entry for slot=%d\n", slot);
+
+ /* reset the host tag entry for the corresponding slot */
+ card->fh_slot_host_tag_record[slot] = CSR_WIFI_HIP_RESERVED_HOST_TAG;
+ }
+
+
+ /* Set length field in from_host_data array to 0 */
+ CardClearFromHostDataSlot(card, slot);
+ }
+
+ return CSR_RESULT_SUCCESS;
+} /* process_bulk_data_command() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * check_fh_sig_slots
+ *
+ * Check whether there are <n> free signal slots available on UniFi.
+ * This takes into account the signals already batched since the
+ * from_host_signal counts were last read.
+ * If the from_host_signal counts indicate not enough space, we read
+ * the latest count from UniFi to see if some more have been freed.
+ *
+ * Arguments:
+ * None.
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS, otherwise CSR error code on error.
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult check_fh_sig_slots(card_t *card, CsrUint16 needed, CsrInt32 *space_fh)
+{
+ CsrUint32 count_fhw;
+ CsrUint32 occupied_fh, slots_fh;
+ CsrInt32 count_fhr;
+
+ count_fhw = card->from_host_signals_w;
+ count_fhr = card->from_host_signals_r;
+ slots_fh = card->config_data.num_fromhost_sig_frags;
+
+ /* Only read the space in from-host queue if necessary */
+ occupied_fh = (count_fhw - count_fhr) % 128;
+
+ if (slots_fh < occupied_fh)
+ {
+ *space_fh = 0;
+ }
+ else
+ {
+ *space_fh = slots_fh - occupied_fh;
+ }
+
+ if ((occupied_fh != 0) && (*space_fh < needed))
+ {
+ count_fhr = unifi_read_shared_count(card, card->sdio_ctrl_addr + 2);
+ if (count_fhr < 0)
+ {
+ unifi_error(card->ospriv, "Failed to read from-host sig read count\n");
+ return CSR_RESULT_FAILURE;
+ }
+ card->from_host_signals_r = count_fhr; /* diag */
+
+ occupied_fh = (count_fhw - count_fhr) % 128;
+ *space_fh = slots_fh - occupied_fh;
+ }
+
+ return CSR_RESULT_SUCCESS;
+} /* check_fh_sig_slots() */
+
+
+/*
+* If we are padding the From-Host signals to the SDIO block size,
+* we need to round up the needed_chunks to the SDIO block size.
+*/
+#define ROUND_UP_NEEDED_CHUNKS(_card, _needed_chunks) \
+ { \
+ CsrUint16 _chunks_per_block; \
+ CsrUint16 _chunks_in_last_block; \
+ \
+ if (_card->sdio_io_block_pad) \
+ { \
+ _chunks_per_block = _card->sdio_io_block_size / _card->config_data.sig_frag_size; \
+ _chunks_in_last_block = _needed_chunks % _chunks_per_block; \
+ if (_chunks_in_last_block != 0) \
+ { \
+ _needed_chunks = _needed_chunks + (_chunks_per_block - _chunks_in_last_block); \
+ } \
+ } \
+ }
+
+
+#define ROUND_UP_SPACE_CHUNKS(_card, _space_chunks) \
+ { \
+ CsrUint16 _chunks_per_block; \
+ \
+ if (_card->sdio_io_block_pad) \
+ { \
+ _chunks_per_block = _card->sdio_io_block_size / _card->config_data.sig_frag_size; \
+ _space_chunks = ((_space_chunks / _chunks_per_block) * _chunks_per_block); \
+ } \
+ }
+
+
+
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * process_fh_cmd_queue
+ *
+ * Take one signal off the from-host queue and copy it to the UniFi.
+ * Does nothing if the UniFi has no slots free.
+ *
+ * Arguments:
+ * card Pointer to card context struct
+ * processed Location to write:
+ * 0 if there is nothing on the queue to process
+ * 1 if a signal was successfully processed
+ *
+ * Returns:
+ * CSR error code if an error occurred.
+ *
+ * Notes:
+ * The from-host queue contains signal requests from the network driver
+ * and any UDI clients interspersed. UDI clients' requests have been stored
+ * in the from-host queue using the wire-format structures, as they arrive.
+ * All other requests are stored in the from-host queue using the host
+ * (cpu specific) structures. We use the is_packed member of the card_signal_t
+ * structure that describes the queue to make the distiction.
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult process_fh_cmd_queue(card_t *card, CsrInt32 *processed)
+{
+ q_t *sigq = &card->fh_command_queue;
+
+ CsrResult r;
+ CsrUint16 pending_sigs;
+ CsrUint16 pending_chunks;
+ CsrUint16 needed_chunks;
+ CsrInt32 space_chunks;
+ CsrUint16 q_index;
+
+ *processed = 0;
+
+ /* Get the number of pending signals. */
+ pending_sigs = CSR_WIFI_HIP_Q_SLOTS_USED(sigq);
+ unifi_trace(card->ospriv, UDBG5, "proc_fh: %d pending\n", pending_sigs);
+ if (pending_sigs == 0)
+ {
+ /* Nothing to do */
+ return CSR_RESULT_SUCCESS;
+ }
+
+ /* Work out how many chunks we have waiting to send */
+ for (pending_chunks = 0, q_index = CSR_WIFI_HIP_Q_NEXT_R_SLOT(sigq);
+ q_index != CSR_WIFI_HIP_Q_NEXT_W_SLOT(sigq);
+ q_index = CSR_WIFI_HIP_Q_WRAP(sigq, q_index + 1))
+ {
+ card_signal_t *csptr = CSR_WIFI_HIP_Q_SLOT_DATA(sigq, q_index);
+
+ /*
+ * Note that GET_CHUNKS_FOR() needs the size of the packed
+ * (wire-formatted) structure
+ */
+ pending_chunks += GET_CHUNKS_FOR(card->config_data.sig_frag_size, (CsrUint16)(csptr->signal_length + 2));
+ }
+
+ /*
+ * Check whether UniFi has space for all the buffered bulk-data
+ * commands and signals as well.
+ */
+ needed_chunks = pending_chunks + card->fh_buffer.count;
+
+ /* Round up to the block size if necessary */
+ ROUND_UP_NEEDED_CHUNKS(card, needed_chunks);
+
+ r = check_fh_sig_slots(card, needed_chunks, &space_chunks);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ /* Error */
+ unifi_error(card->ospriv, "Failed to read fh sig count\n");
+ return r;
+ }
+
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, "proc_fh: %d chunks free, need %d\n",
+ space_chunks, needed_chunks);
+#endif /* CSR_WIFI_HIP_NOISY */
+
+
+ /*
+ * Coalesce as many from-host signals as possible
+ * into a single block and write using a single CMD53
+ */
+ if (needed_chunks > (CsrUint16)space_chunks)
+ {
+ /* Round up to the block size if necessary */
+ ROUND_UP_SPACE_CHUNKS(card, space_chunks);
+
+ /*
+ * If the f/w has less free chunks than those already pending
+ * return immediately.
+ */
+ if ((CsrUint16)space_chunks <= card->fh_buffer.count)
+ {
+ /*
+ * No room in UniFi for any signals after the buffered bulk
+ * data commands have been sent.
+ */
+ unifi_error(card->ospriv, "not enough room to send signals, need %d chunks, %d free\n",
+ card->fh_buffer.count, space_chunks);
+ card->generate_interrupt = 1;
+ return CSR_RESULT_SUCCESS;
+ }
+ pending_chunks = (CsrUint16)(space_chunks - card->fh_buffer.count);
+ }
+
+ while (pending_sigs-- && pending_chunks > 0)
+ {
+ card_signal_t *csptr;
+ CsrInt16 i;
+ CsrUint16 sig_chunks, total_length, free_chunks_in_fh_buffer;
+ bulk_data_param_t bulkdata;
+ CsrUint8 *packed_sigptr;
+ CsrUint16 signal_length = 0;
+
+ /* Retrieve the entry at the head of the queue */
+ q_index = CSR_WIFI_HIP_Q_NEXT_R_SLOT(sigq);
+
+ /* Get a pointer to the containing card_signal_t struct */
+ csptr = CSR_WIFI_HIP_Q_SLOT_DATA(sigq, q_index);
+
+ /* Get the new length of the packed signal */
+ signal_length = csptr->signal_length;
+
+ if ((signal_length & 1) || (signal_length > UNIFI_PACKED_SIGBUF_SIZE))
+ {
+ unifi_error(card->ospriv, "process_fh_queue: Bad len: %d\n", signal_length);
+ return CSR_RESULT_FAILURE;
+ }
+
+ /* Need space for 2-byte SDIO protocol header + signal */
+ sig_chunks = GET_CHUNKS_FOR(card->config_data.sig_frag_size, (CsrUint16)(signal_length + 2));
+
+ free_chunks_in_fh_buffer = GET_CHUNKS_FOR(card->config_data.sig_frag_size,
+ (CsrUint16)((card->fh_buffer.buf + UNIFI_FH_BUF_SIZE) - card->fh_buffer.ptr));
+ if (free_chunks_in_fh_buffer < sig_chunks)
+ {
+ /* No more room */
+ unifi_notice(card->ospriv, "proc_fh_cmd_q: no room in fh buffer for 0x%.4X, deferring\n",
+ (CsrUint16)(GET_SIGNAL_ID(csptr->sigbuf)));
+ break;
+ }
+
+ packed_sigptr = csptr->sigbuf;
+
+ /* Claim and set up a from-host data slot */
+ if (CSR_RESULT_FAILURE == CardWriteBulkData(card, csptr, UNIFI_TRAFFIC_Q_MLME))
+ {
+ unifi_notice(card->ospriv, "proc_fh_cmd_q: no fh data slots for 0x%.4X, deferring\n",
+ (CsrUint16)(GET_SIGNAL_ID(csptr->sigbuf)));
+ break;
+ }
+
+ for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++)
+ {
+ if (csptr->bulkdata[i].data_length == 0)
+ {
+ UNIFI_INIT_BULK_DATA(&bulkdata.d[i]);
+ }
+ else
+ {
+ bulkdata.d[i].os_data_ptr = csptr->bulkdata[i].os_data_ptr;
+ bulkdata.d[i].data_length = csptr->bulkdata[i].data_length;
+ }
+
+ /* Pass the free responsibility to the lower layer. */
+ UNIFI_INIT_BULK_DATA(&csptr->bulkdata[i]);
+ }
+
+ unifi_trace(card->ospriv, UDBG2, "Sending signal 0x%.4X\n",
+ GET_SIGNAL_ID(packed_sigptr));
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, "Sending signal 0x%.4X\n",
+ GET_SIGNAL_ID(packed_sigptr));
+#endif /* CSR_WIFI_HIP_NOISY */
+
+
+ /* Append packed signal to F-H buffer */
+ total_length = sig_chunks * card->config_data.sig_frag_size;
+
+ card->fh_buffer.ptr[0] = (CsrUint8)(signal_length & 0xff);
+ card->fh_buffer.ptr[1] =
+ (CsrUint8)(((signal_length >> 8) & 0xf) | (SDIO_CMD_SIGNAL << 4));
+
+ CsrMemCpy(card->fh_buffer.ptr + 2, packed_sigptr, signal_length);
+ CsrMemSet(card->fh_buffer.ptr + 2 + signal_length, 0,
+ total_length - (2 + signal_length));
+
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, "proc_fh: fh_buffer %d bytes \n",
+ signal_length + 2);
+ dump(card->fh_buffer.ptr, signal_length + 2);
+ unifi_trace(card->ospriv, UDBG1, " \n");
+#endif /* CSR_WIFI_HIP_NOISY */
+
+ card->fh_buffer.ptr += total_length;
+ card->fh_buffer.count += sig_chunks;
+
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, "Added %d to fh buf, len now %d, count %d\n",
+ signal_length,
+ card->fh_buffer.ptr - card->fh_buffer.buf,
+ card->fh_buffer.count);
+#endif /* CSR_WIFI_HIP_NOISY */
+
+ (*processed)++;
+ pending_chunks -= sig_chunks;
+
+ /* Log the signal to the UDI. */
+ /* UDI will get the packed structure */
+ /* Can not log the unpacked signal, unless we reconstruct it! */
+ if (card->udi_hook)
+ {
+ (*card->udi_hook)(card->ospriv, packed_sigptr, signal_length,
+ &bulkdata, UDI_LOG_FROM_HOST);
+ }
+
+ /* Remove entry from q */
+ csptr->signal_length = 0;
+ CSR_WIFI_HIP_Q_INC_R(sigq);
+ }
+
+ return CSR_RESULT_SUCCESS;
+} /* process_fh_cmd_queue() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * process_fh_traffic_queue
+ *
+ * Take signals off the from-host queue and copy them to the UniFi.
+ * Does nothing if the UniFi has no slots free.
+ *
+ * Arguments:
+ * card Pointer to card context struct
+ * sigq Pointer to the traffic queue
+ * processed Pointer to location to write:
+ * 0 if there is nothing on the queue to process
+ * 1 if a signal was successfully processed
+ *
+ * Returns:
+ * CSR error code if an error occurred.
+ *
+ * Notes:
+ * The from-host queue contains signal requests from the network driver
+ * and any UDI clients interspersed.
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult process_fh_traffic_queue(card_t *card, CsrInt32 *processed)
+{
+ q_t *sigq = card->fh_traffic_queue;
+
+ CsrResult r;
+ CsrInt16 n = 0;
+ CsrInt32 q_no;
+ CsrUint16 pending_sigs = 0;
+ CsrUint16 pending_chunks = 0;
+ CsrUint16 needed_chunks;
+ CsrInt32 space_chunks;
+ CsrUint16 q_index;
+ CsrUint32 host_tag = 0;
+ CsrUint16 slot_num = 0;
+
+ *processed = 0;
+
+ /* calculate how many signals are in queues and how many chunks are needed. */
+ for (n = UNIFI_NO_OF_TX_QS - 1; n >= 0; n--)
+ {
+ /* Get the number of pending signals. */
+ pending_sigs += CSR_WIFI_HIP_Q_SLOTS_USED(&sigq[n]);
+ unifi_trace(card->ospriv, UDBG5, "proc_fh%d: %d pending\n", n, pending_sigs);
+
+ /* Work out how many chunks we have waiting to send */
+ for (q_index = CSR_WIFI_HIP_Q_NEXT_R_SLOT(&sigq[n]);
+ q_index != CSR_WIFI_HIP_Q_NEXT_W_SLOT(&sigq[n]);
+ q_index = CSR_WIFI_HIP_Q_WRAP(&sigq[n], q_index + 1))
+ {
+ card_signal_t *csptr = CSR_WIFI_HIP_Q_SLOT_DATA(&sigq[n], q_index);
+
+ /*
+ * Note that GET_CHUNKS_FOR() needs the size of the packed
+ * (wire-formatted) structure
+ */
+ pending_chunks += GET_CHUNKS_FOR(card->config_data.sig_frag_size, (CsrUint16)(csptr->signal_length + 2));
+ }
+ }
+
+ /* If there are no pending signals, just return */
+ if (pending_sigs == 0)
+ {
+ /* Nothing to do */
+ return CSR_RESULT_SUCCESS;
+ }
+
+ /*
+ * Check whether UniFi has space for all the buffered bulk-data
+ * commands and signals as well.
+ */
+ needed_chunks = pending_chunks + card->fh_buffer.count;
+
+ /* Round up to the block size if necessary */
+ ROUND_UP_NEEDED_CHUNKS(card, needed_chunks);
+
+ r = check_fh_sig_slots(card, needed_chunks, &space_chunks);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ /* Error */
+ unifi_error(card->ospriv, "Failed to read fh sig count\n");
+ return r;
+ }
+
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv,
+ "process_fh_traffic_queue: %d chunks free, need %d\n",
+ space_chunks, needed_chunks);
+ read_fhsr(card); /* debugging only */
+#endif /* CSR_WIFI_HIP_NOISY */
+
+ /* Coalesce as many from-host signals as possible
+ into a single block and write using a single CMD53 */
+ if (needed_chunks > (CsrUint16)space_chunks)
+ {
+ /* Round up to the block size if necessary */
+ ROUND_UP_SPACE_CHUNKS(card, space_chunks);
+
+ if ((CsrUint16)space_chunks <= card->fh_buffer.count)
+ {
+ /*
+ * No room in UniFi for any signals after the buffered bulk
+ * data commands have been sent.
+ */
+ unifi_error(card->ospriv, "not enough room to send signals, need %d chunks, %d free\n",
+ card->fh_buffer.count, space_chunks);
+ card->generate_interrupt = 1;
+ return 0;
+ }
+
+ pending_chunks = (CsrUint16)space_chunks - card->fh_buffer.count;
+ }
+
+ q_no = UNIFI_NO_OF_TX_QS - 1;
+
+ /*
+ * pending_sigs will be exhausted if there are is no restriction to the pending
+ * signals per queue. pending_chunks may be exhausted if there is a restriction.
+ * q_no check will be exhausted if there is a restriction and our round-robin
+ * algorith fails to fill all chunks.
+ */
+ do
+ {
+ card_signal_t *csptr;
+ CsrUint16 sig_chunks, total_length, free_chunks_in_fh_buffer;
+ bulk_data_param_t bulkdata;
+ CsrUint8 *packed_sigptr;
+ CsrUint16 signal_length = 0;
+
+ /* if this queue is empty go to next one. */
+ if (CSR_WIFI_HIP_Q_SLOTS_USED(&sigq[q_no]) == 0)
+ {
+ q_no--;
+ continue;
+ }
+
+ /* Retrieve the entry at the head of the queue */
+ q_index = CSR_WIFI_HIP_Q_NEXT_R_SLOT(&sigq[q_no]);
+
+ /* Get a pointer to the containing card_signal_t struct */
+ csptr = CSR_WIFI_HIP_Q_SLOT_DATA(&sigq[q_no], q_index);
+
+ /* Get the new length of the packed signal */
+ signal_length = csptr->signal_length;
+
+ if ((signal_length & 1) || (signal_length > UNIFI_PACKED_SIGBUF_SIZE))
+ {
+ unifi_error(card->ospriv, "process_fh_traffic_queue: Bad len: %d\n", signal_length);
+ return CSR_RESULT_FAILURE;
+ }
+
+ /* Need space for 2-byte SDIO protocol header + signal */
+ sig_chunks = GET_CHUNKS_FOR(card->config_data.sig_frag_size, (CsrUint16)(signal_length + 2));
+ free_chunks_in_fh_buffer = GET_CHUNKS_FOR(card->config_data.sig_frag_size,
+ (CsrUint16)((card->fh_buffer.buf + UNIFI_FH_BUF_SIZE) - card->fh_buffer.ptr));
+ if (free_chunks_in_fh_buffer < sig_chunks)
+ {
+ /* No more room */
+ unifi_notice(card->ospriv, "process_fh_traffic_queue: no more chunks.\n");
+ break;
+ }
+
+ packed_sigptr = csptr->sigbuf;
+ /* Claim and set up a from-host data slot */
+ if (CSR_RESULT_FAILURE == CardWriteBulkData(card, csptr, (unifi_TrafficQueue)q_no))
+ {
+ q_no--;
+ continue;
+ }
+
+ /* Sanity check: MA-PACKET.req must have a valid bulk data */
+ if ((csptr->bulkdata[0].data_length == 0) || (csptr->bulkdata[0].os_data_ptr == NULL))
+ {
+ unifi_error(card->ospriv, "MA-PACKET.req with empty bulk data (%d bytes in %p)\n",
+ csptr->bulkdata[0].data_length, csptr->bulkdata[0].os_data_ptr);
+ dump(packed_sigptr, signal_length);
+ return CSR_RESULT_FAILURE;
+ }
+
+ bulkdata.d[0].os_data_ptr = csptr->bulkdata[0].os_data_ptr;
+ bulkdata.d[0].data_length = csptr->bulkdata[0].data_length;
+ bulkdata.d[0].os_net_buf_ptr = csptr->bulkdata[0].os_net_buf_ptr;
+ bulkdata.d[0].net_buf_length = csptr->bulkdata[0].net_buf_length;
+
+ /* The driver owns clearing of HIP slots for following scenario
+ * - driver has requested a MA-PACKET.req signal
+ * - The f/w after receiving the signal decides it can't send it out due to various reasons
+ * - So the f/w without downloading the bulk data decides to just send a confirmation with fail
+ * - and then sends a clear slot signal to HIP
+ *
+ * But in some cases the clear slot signal never comes and the slot remains --NOT-- freed for ever
+ *
+ * To handle this, HIP will keep the record of host tag for each occupied slot
+ * and then based on status of that Host tag and slot the driver will decide if the slot is
+ * cleared by f/w signal or the slot has to be freed by driver
+ */
+
+ if (card->fh_slot_host_tag_record)
+ {
+ /* Update the f-h slot record for the corresponding host tag */
+ host_tag = GET_PACKED_MA_PACKET_REQUEST_HOST_TAG(packed_sigptr);
+ slot_num = GET_PACKED_DATAREF_SLOT(packed_sigptr, 0) & 0x00FF;
+
+ unifi_trace(card->ospriv, UDBG5,
+ "process_fh_traffic_queue signal ID =%x fh slot=%x Host tag =%x\n",
+ GET_SIGNAL_ID(packed_sigptr), slot_num, host_tag);
+ card->fh_slot_host_tag_record[slot_num] = host_tag;
+ }
+ UNIFI_INIT_BULK_DATA(&bulkdata.d[1]);
+ UNIFI_INIT_BULK_DATA(&csptr->bulkdata[0]);
+ UNIFI_INIT_BULK_DATA(&csptr->bulkdata[1]);
+
+#ifdef CSR_WIFI_HIP_DATA_PLANE_PROFILE
+ if (bulkdata.d[0].os_data_ptr)
+ {
+ if ((*bulkdata.d[0].os_data_ptr) & 0x08)
+ {
+ card->cmd_prof.tx_count++;
+ }
+ }
+#endif
+ unifi_trace(card->ospriv, UDBG3, "Sending signal 0x%.4X\n",
+ GET_SIGNAL_ID(packed_sigptr));
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, "Sending signal 0x%.4X\n",
+ GET_SIGNAL_ID(packed_sigptr));
+#endif /* CSR_WIFI_HIP_NOISY */
+
+ /* Append packed signal to F-H buffer */
+ total_length = sig_chunks * card->config_data.sig_frag_size;
+
+ card->fh_buffer.ptr[0] = (CsrUint8)(signal_length & 0xff);
+ card->fh_buffer.ptr[1] =
+ (CsrUint8)(((signal_length >> 8) & 0xf) | (SDIO_CMD_SIGNAL << 4));
+
+ CsrMemCpy(card->fh_buffer.ptr + 2, packed_sigptr, signal_length);
+ CsrMemSet(card->fh_buffer.ptr + 2 + signal_length, 0,
+ total_length - (2 + signal_length));
+
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, "proc_fh: fh_buffer %d bytes \n",
+ signal_length + 2);
+ dump(card->fh_buffer.ptr, signal_length + 2);
+ unifi_trace(card->ospriv, UDBG1, " \n");
+#endif /* CSR_WIFI_HIP_NOISY */
+
+ card->fh_buffer.ptr += total_length;
+ card->fh_buffer.count += sig_chunks;
+
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, "Added %d to fh buf, len now %d, count %d\n",
+ signal_length,
+ card->fh_buffer.ptr - card->fh_buffer.buf,
+ card->fh_buffer.count);
+#endif /* CSR_WIFI_HIP_NOISY */
+
+ (*processed)++;
+ pending_sigs--;
+ pending_chunks -= sig_chunks;
+
+ /* Log the signal to the UDI. */
+ /* UDI will get the packed structure */
+ /* Can not log the unpacked signal, unless we reconstruct it! */
+ if (card->udi_hook)
+ {
+ (*card->udi_hook)(card->ospriv, packed_sigptr, signal_length,
+ &bulkdata, UDI_LOG_FROM_HOST);
+ }
+
+ /* Remove entry from q */
+ csptr->signal_length = 0;
+ /* Note that the traffic queue has only one valid bulk data buffer. */
+ csptr->bulkdata[0].data_length = 0;
+
+ CSR_WIFI_HIP_Q_INC_R(&sigq[q_no]);
+ } while ((pending_sigs > 0) && (pending_chunks > 0) && (q_no >= 0));
+
+ return CSR_RESULT_SUCCESS;
+} /* process_fh_traffic_queue() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * flush_fh_buffer
+ *
+ * Write out the cache from-hosts signals to the UniFi.
+ *
+ * Arguments:
+ * card Pointer to card context struct
+ *
+ * Returns:
+ * CSR error code if an SDIO error occurred.
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult flush_fh_buffer(card_t *card)
+{
+ CsrResult r;
+ CsrUint16 len;
+ CsrUint16 sig_units;
+ CsrUint16 data_round;
+ CsrUint16 chunks_in_last_block;
+ CsrUint16 padding_chunks;
+ CsrUint16 i;
+
+ len = card->fh_buffer.ptr - card->fh_buffer.buf;
+
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, "fh_buffer is at %p, ptr= %p\n",
+ card->fh_buffer.buf, card->fh_buffer.ptr);
+#endif /* CSR_WIFI_HIP_NOISY */
+
+ if (len == 0)
+ {
+ return CSR_RESULT_SUCCESS;
+ }
+
+#ifdef CSR_WIFI_HIP_NOISY
+ if (dump_fh_buf)
+ {
+ dump(card->fh_buffer.buf, len);
+ dump_fh_buf = 0;
+ }
+#endif /* CSR_WIFI_HIP_NOISY */
+
+ if (card->sdio_io_block_pad)
+ {
+ /* Both of these are powers of 2 */
+ sig_units = card->config_data.sig_frag_size;
+ data_round = card->sdio_io_block_size;
+
+ if (data_round > sig_units)
+ {
+ chunks_in_last_block = (len % data_round) / sig_units;
+
+ if (chunks_in_last_block != 0)
+ {
+ padding_chunks = (data_round / sig_units) - chunks_in_last_block;
+
+ CsrMemSet(card->fh_buffer.ptr, 0, padding_chunks * sig_units);
+ for (i = 0; i < padding_chunks; i++)
+ {
+ card->fh_buffer.ptr[1] = SDIO_CMD_PADDING << 4;
+ card->fh_buffer.ptr += sig_units;
+ }
+
+ card->fh_buffer.count += padding_chunks;
+ len += padding_chunks * sig_units;
+ }
+ }
+ }
+
+ r = unifi_bulk_rw(card,
+ card->config_data.fromhost_sigbuf_handle,
+ card->fh_buffer.buf,
+ len, UNIFI_SDIO_WRITE);
+ if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
+ {
+ return r;
+ }
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to write fh signals: %u bytes, error %d\n", len, r);
+ return r;
+ }
+
+ /* Update from-host-signals-written signal count */
+ card->from_host_signals_w =
+ (card->from_host_signals_w + card->fh_buffer.count) % 128u;
+ r = unifi_write_8_or_16(card, card->sdio_ctrl_addr + 0,
+ (CsrUint8)card->from_host_signals_w);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to write fh signal count %u with error %d\n",
+ card->from_host_signals_w, r);
+ return r;
+ }
+ card->generate_interrupt = 1;
+
+ /* Reset the fh buffer pointer */
+ card->fh_buffer.ptr = card->fh_buffer.buf;
+ card->fh_buffer.count = 0;
+
+#ifdef CSR_WIFI_HIP_NOISY
+ unifi_error(card->ospriv, "END flush: fh len %d, count %d\n",
+ card->fh_buffer.ptr - card->fh_buffer.buf,
+ card->fh_buffer.count);
+#endif /* CSR_WIFI_HIP_NOISY */
+
+ return CSR_RESULT_SUCCESS;
+} /* flush_fh_buffer() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * restart_packet_flow
+ *
+ * This function is called before the bottom-half thread sleeps.
+ * It checks whether both data and signal resources are available and
+ * then calls the OS-layer function to re-enable packet transmission.
+ *
+ * Arguments:
+ * card Pointer to card context struct
+ *
+ * Returns:
+ * None.
+ * ---------------------------------------------------------------------------
+ */
+static void restart_packet_flow(card_t *card)
+{
+ CsrUint8 q;
+
+ /*
+ * We only look at the fh_traffic_queue, because that is where packets from
+ * the network stack are placed.
+ */
+ for (q = 0; q <= UNIFI_TRAFFIC_Q_VO; q++)
+ {
+ if (card_is_tx_q_paused(card, q) &&
+ CSR_WIFI_HIP_Q_SLOTS_FREE(&card->fh_traffic_queue[q]) >= RESUME_XMIT_THRESHOLD)
+ {
+#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE)
+ unifi_debug_log_to_buf("U");
+#endif
+ card_tx_q_unpause(card, q);
+ unifi_restart_xmit(card->ospriv, (unifi_TrafficQueue)q);
+ }
+ }
+} /* restart_packet_flow() */
+
+