| /***************************************************************************** |
| |
| (c) Cambridge Silicon Radio Limited 2012 |
| 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, u8 *processed_something); |
| |
| static CsrResult flush_fh_buffer(card_t *card); |
| |
| static CsrResult check_fh_sig_slots(card_t *card, u16 needed, s32 *space); |
| |
| static CsrResult read_to_host_signals(card_t *card, s32 *processed); |
| static CsrResult process_to_host_signals(card_t *card, s32 *processed); |
| |
| static CsrResult process_bulk_data_command(card_t *card, |
| const u8 *cmdptr, |
| s16 cmd, u16 len); |
| static CsrResult process_clear_slot_command(card_t *card, |
| const u8 *cmdptr); |
| static CsrResult process_fh_cmd_queue(card_t *card, s32 *processed); |
| static CsrResult process_fh_traffic_queue(card_t *card, s32 *processed); |
| static void restart_packet_flow(card_t *card); |
| static CsrResult process_clock_request(card_t *card); |
| |
| #ifdef CSR_WIFI_HIP_NOISY |
| s16 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 char unifi_debug_output[UNIFI_DEBUG_GBUFFER_SIZE]; |
| static char *unifi_dbgbuf_ptr = unifi_debug_output; |
| static char *unifi_dbgbuf_start = unifi_debug_output; |
| |
| static void append_char(char 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 char *str) |
| { |
| const char *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 char *fmt, ...) |
| { |
| #define DEBUG_BUFFER_SIZE 80 |
| static char s[DEBUG_BUFFER_SIZE]; |
| va_list args; |
| |
| va_start(args, fmt); |
| vsnprintf(s, DEBUG_BUFFER_SIZE, fmt, args); |
| va_end(args); |
| |
| unifi_debug_string_to_buf(s); |
| } /* unifi_debug_log_to_buf() */ |
| |
| |
| /* Convert signed 32 bit (or less) integer to string */ |
| static void CsrUInt16ToHex(u16 number, char *str) |
| { |
| u16 index; |
| u16 currentValue; |
| |
| for (index = 0; index < 4; index++) |
| { |
| currentValue = (u16) (number & 0x000F); |
| number >>= 4; |
| str[3 - index] = (char) (currentValue > 9 ? currentValue + 55 : currentValue + '0'); |
| } |
| str[4] = '\0'; |
| } |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * 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 char *buff, u16 length) |
| { |
| char s[5]; |
| u16 i; |
| |
| for (i = 0; i < length; i = i + 2) |
| { |
| CsrUInt16ToHex(*((u16 *)(buff + i)), s); |
| unifi_debug_string_to_buf(s); |
| } |
| } |
| |
| |
| void unifi_debug_buf_dump(void) |
| { |
| s32 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, u32 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; |
| (void)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"); |
| |
| (void)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, u32 *remaining) |
| { |
| CsrResult r; |
| CsrResult csrResult; |
| u8 pending; |
| s32 iostate, j; |
| const enum unifi_low_power_mode low_power_mode = card->low_power_mode; |
| u16 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) |
| { |
| u8 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 */ |
| (void)unifi_capture_panic(card); |
| |
| /* Ask for a mini-coredump when the driver has reset UniFi */ |
| (void)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; |
| u8 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, u8 *processed_something) |
| { |
| CsrResult r; |
| s32 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 |
| #ifdef CSR_WIFI_RX_PATH_SPLIT_DONT_USE_WQ |
| unifi_rx_queue_flush(card->ospriv); |
| #endif |
| #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, s32 *processed) |
| { |
| s32 count_thw, count_thr; |
| s32 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 += (u16)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, s16 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, |
| (u8)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 u8 *ptr, bulk_data_cmd_t *bulk_data_cmd) |
| { |
| s16 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, s32 *processed) |
| { |
| s16 pending; |
| s16 remaining; |
| u8 *bufptr; |
| bulk_data_param_t data_ptrs; |
| s16 cmd; |
| u16 sig_len; |
| s16 i; |
| u16 chunks_in_buf; |
| u16 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) |
| { |
| s16 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, (u16)(sig_len + 2)); |
| } |
| else |
| { |
| chunks_in_buf = 1; |
| } |
| |
| if (chunks_in_buf > (u16)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 */ |
| u16 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 */ |
| s16 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 */ |
| u32 host_tag = GET_PACKED_MA_PACKET_CONFIRM_HOST_TAG(bufptr + 2); |
| u16 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)) |
| { |
| u16 num_fh_slots = card->config_data.num_fromhost_data_slots; |
| |
| /* 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) |
| { |
| #ifdef CSR_WIFI_REQUEUE_PACKET_TO_HAL |
| /* Invoke the HAL module function to requeue it back to HAL Queues */ |
| r = unifi_reque_ma_packet_request(card->ospriv, host_tag, status, &card->from_host_data[i].bd); |
| card->fh_slot_host_tag_record[i] = CSR_WIFI_HIP_RESERVED_HOST_TAG; |
| if (CSR_RESULT_SUCCESS != r) |
| { |
| unifi_trace(card->ospriv, UDBG5, "process_to_host_signals: Failed to requeue Packet(hTag:%x) back to HAL \n", host_tag); |
| CardClearFromHostDataSlot(card, i); |
| } |
| else |
| { |
| CardClearFromHostDataSlotWithoutFreeingBulkData(card, i); |
| } |
| |
| #else |
| 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); |
| #endif |
| 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) |
| { |
| s16 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 */ |
| u8 *d = card->th_buffer.buf; |
| u8 *s = bufptr; |
| s32 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 u8 *cmdptr) |
| { |
| u16 data_slot; |
| s16 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 u8 *cmdptr, |
| s16 cmd, u16 len) |
| { |
| bulk_data_desc_t *bdslot; |
| #ifdef CSR_WIFI_ALIGNMENT_WORKAROUND |
| u8 *host_bulk_data_slot; |
| #endif |
| bulk_data_cmd_t bdcmd; |
| s16 offset; |
| s16 slot; |
| s16 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) && (((ptrdiff_t)bdslot->os_data_ptr + offset) & 3)) |
| { |
| host_bulk_data_slot = kmalloc(len, GFP_KERNEL); |
| |
| if (!host_bulk_data_slot) |
| { |
| unifi_error(card->ospriv, " failed to allocate request_data before unifi_bulk_rw\n"); |
| return -1; |
| } |
| |
| memcpy((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) && (((ptrdiff_t)bdslot->os_data_ptr + offset) & 3)) |
| { |
| kfree(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, u16 needed, s32 *space_fh) |
| { |
| u32 count_fhw; |
| u32 occupied_fh, slots_fh; |
| s32 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) \ |
| { \ |
| u16 _chunks_per_block; \ |
| u16 _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) \ |
| { \ |
| u16 _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, s32 *processed) |
| { |
| q_t *sigq = &card->fh_command_queue; |
| |
| CsrResult r; |
| u16 pending_sigs; |
| u16 pending_chunks; |
| u16 needed_chunks; |
| s32 space_chunks; |
| u16 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, (u16)(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 > (u16)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 ((u16)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 = (u16)(space_chunks - card->fh_buffer.count); |
| } |
| |
| while (pending_sigs-- && pending_chunks > 0) |
| { |
| card_signal_t *csptr; |
| s16 i; |
| u16 sig_chunks, total_length, free_chunks_in_fh_buffer; |
| bulk_data_param_t bulkdata; |
| u8 *packed_sigptr; |
| u16 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, (u16)(signal_length + 2)); |
| |
| free_chunks_in_fh_buffer = GET_CHUNKS_FOR(card->config_data.sig_frag_size, |
| (u16)((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", |
| (u16)(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", |
| (u16)(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] = (u8)(signal_length & 0xff); |
| card->fh_buffer.ptr[1] = |
| (u8)(((signal_length >> 8) & 0xf) | (SDIO_CMD_SIGNAL << 4)); |
| |
| memcpy(card->fh_buffer.ptr + 2, packed_sigptr, signal_length); |
| memset(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, s32 *processed) |
| { |
| q_t *sigq = card->fh_traffic_queue; |
| |
| CsrResult r; |
| s16 n = 0; |
| s32 q_no; |
| u16 pending_sigs = 0; |
| u16 pending_chunks = 0; |
| u16 needed_chunks; |
| s32 space_chunks; |
| u16 q_index; |
| u32 host_tag = 0; |
| u16 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, (u16)(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 > (u16)space_chunks) |
| { |
| /* Round up to the block size if necessary */ |
| ROUND_UP_SPACE_CHUNKS(card, space_chunks); |
| |
| if ((u16)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 = (u16)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; |
| u16 sig_chunks, total_length, free_chunks_in_fh_buffer; |
| bulk_data_param_t bulkdata; |
| u8 *packed_sigptr; |
| u16 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, (u16)(signal_length + 2)); |
| free_chunks_in_fh_buffer = GET_CHUNKS_FOR(card->config_data.sig_frag_size, |
| (u16)((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] = (u8)(signal_length & 0xff); |
| card->fh_buffer.ptr[1] = |
| (u8)(((signal_length >> 8) & 0xf) | (SDIO_CMD_SIGNAL << 4)); |
| |
| memcpy(card->fh_buffer.ptr + 2, packed_sigptr, signal_length); |
| memset(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; |
| u16 len; |
| u16 sig_units; |
| u16 data_round; |
| u16 chunks_in_last_block; |
| u16 padding_chunks; |
| u16 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; |
| |
| memset(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, |
| (u8)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) |
| { |
| u8 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() */ |
| |
| |