/* * --------------------------------------------------------------------------- * unifi_send_resources_available * * Examines whether there is available space to queue * a signal in the command or traffic queue * * Arguments: * card Pointer to card context struct * sigptr Pointer to signal. * * Returns: * CSR_RESULT_SUCCESS if resources available * CSR_WIFI_HIP_RESULT_NO_SPACE if there was no free signal queue entry * * Notes: * --------------------------------------------------------------------------- */ CsrResult unifi_send_resources_available(card_t *card, const CsrUint8 *sigptr) { q_t *sig_soft_q; CsrUint16 signal_id = GET_SIGNAL_ID(sigptr); /* * If the signal is a CSR_MA_PACKET_REQUEST , * we send it using the traffic soft queue. Else we use the command soft queue. */ if (signal_id == CSR_MA_PACKET_REQUEST_ID) { CsrUint16 frame_priority; CsrUint32 priority_q; /* Map the frame priority to a traffic queue index. */ frame_priority = GET_PACKED_MA_PACKET_REQUEST_FRAME_PRIORITY(sigptr); priority_q = unifi_frame_priority_to_queue((CSR_PRIORITY)frame_priority); sig_soft_q = &card->fh_traffic_queue[priority_q]; } else { sig_soft_q = &card->fh_command_queue; } /* Check that the fh_data_queue has a free slot */ if (!CSR_WIFI_HIP_Q_SLOTS_FREE(sig_soft_q)) { unifi_notice(card->ospriv, "unifi_send_resources_available: %s full\n", sig_soft_q->name); return CSR_WIFI_HIP_RESULT_NO_SPACE; } return CSR_RESULT_SUCCESS; } /* unifi_send_resources_available() */
/* * --------------------------------------------------------------------------- * unifi_print_status * * Print status info to given character buffer. * * Arguments: * None. * * Returns: * None. * --------------------------------------------------------------------------- */ s32 unifi_print_status(card_t *card, char *str, s32 *remain) { char *p = str; sdio_config_data_t *cfg; u16 i, n; s32 remaining = *remain; s32 written; #ifdef CSR_UNSAFE_SDIO_ACCESS s32 iostate; CsrResult r; static const char *const states[] = { "AWAKE", "DROWSY", "TORPID" }; #define SHARED_READ_RETRY_LIMIT 10 u8 b; #endif if (remaining <= 0) { return 0; } i = n = 0; written = CsrSnprintf(p, remaining, "Chip ID %u\n", (u16)card->chip_id); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "Chip Version %04X\n", card->chip_version); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "HIP v%u.%u\n", (card->config_data.version >> 8) & 0xFF, card->config_data.version & 0xFF); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "Build %lu: %s\n", card->build_id, card->build_id_string); UNIFI_SNPRINTF_RET(p, remaining, written); cfg = &card->config_data; written = CsrSnprintf(p, remaining, "sdio ctrl offset %u\n", cfg->sdio_ctrl_offset); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "fromhost sigbuf handle %u\n", cfg->fromhost_sigbuf_handle); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "tohost_sigbuf_handle %u\n", cfg->tohost_sigbuf_handle); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "num_fromhost_sig_frags %u\n", cfg->num_fromhost_sig_frags); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "num_tohost_sig_frags %u\n", cfg->num_tohost_sig_frags); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "num_fromhost_data_slots %u\n", cfg->num_fromhost_data_slots); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "num_tohost_data_slots %u\n", cfg->num_tohost_data_slots); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "data_slot_size %u\n", cfg->data_slot_size); UNIFI_SNPRINTF_RET(p, remaining, written); /* Added by protocol version 0x0001 */ written = CsrSnprintf(p, remaining, "overlay_size %u\n", (u16)cfg->overlay_size); UNIFI_SNPRINTF_RET(p, remaining, written); /* Added by protocol version 0x0300 */ written = CsrSnprintf(p, remaining, "data_slot_round %u\n", cfg->data_slot_round); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "sig_frag_size %u\n", cfg->sig_frag_size); UNIFI_SNPRINTF_RET(p, remaining, written); /* Added by protocol version 0x0300 */ written = CsrSnprintf(p, remaining, "tohost_sig_pad %u\n", cfg->tohost_signal_padding); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "\nInternal state:\n"); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "Last PHY PANIC: %04x:%04x\n", card->last_phy_panic_code, card->last_phy_panic_arg); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "Last MAC PANIC: %04x:%04x\n", card->last_mac_panic_code, card->last_mac_panic_arg); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "fhsr: %u\n", (u16)card->from_host_signals_r); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "fhsw: %u\n", (u16)card->from_host_signals_w); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "thsr: %u\n", (u16)card->to_host_signals_r); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "thsw: %u\n", (u16)card->to_host_signals_w); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "fh buffer contains: %u signals, %u bytes\n", card->fh_buffer.count, card->fh_buffer.ptr - card->fh_buffer.buf); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "paused: "); UNIFI_SNPRINTF_RET(p, remaining, written); for (i = 0; i < sizeof(card->tx_q_paused_flag) / sizeof(card->tx_q_paused_flag[0]); i++) { written = CsrSnprintf(p, remaining, card->tx_q_paused_flag[i]?"1" : "0"); UNIFI_SNPRINTF_RET(p, remaining, written); } written = CsrSnprintf(p, remaining, "\n"); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "fh command q: %u waiting, %u free of %u:\n", CSR_WIFI_HIP_Q_SLOTS_USED(&card->fh_command_queue), CSR_WIFI_HIP_Q_SLOTS_FREE(&card->fh_command_queue), UNIFI_SOFT_COMMAND_Q_LENGTH); UNIFI_SNPRINTF_RET(p, remaining, written); for (i = 0; i < UNIFI_NO_OF_TX_QS; i++) { written = CsrSnprintf(p, remaining, "fh traffic q[%u]: %u waiting, %u free of %u:\n", i, CSR_WIFI_HIP_Q_SLOTS_USED(&card->fh_traffic_queue[i]), CSR_WIFI_HIP_Q_SLOTS_FREE(&card->fh_traffic_queue[i]), UNIFI_SOFT_TRAFFIC_Q_LENGTH); UNIFI_SNPRINTF_RET(p, remaining, written); } written = CsrSnprintf(p, remaining, "fh data slots free: %u\n", card->from_host_data?CardGetFreeFromHostDataSlots(card) : 0); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "From host data slots:"); UNIFI_SNPRINTF_RET(p, remaining, written); n = card->config_data.num_fromhost_data_slots; for (i = 0; i < n && card->from_host_data; i++) { written = CsrSnprintf(p, remaining, " %u", (u16)card->from_host_data[i].bd.data_length); UNIFI_SNPRINTF_RET(p, remaining, written); } written = CsrSnprintf(p, remaining, "\n"); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "To host data slots:"); UNIFI_SNPRINTF_RET(p, remaining, written); n = card->config_data.num_tohost_data_slots; for (i = 0; i < n && card->to_host_data; i++) { written = CsrSnprintf(p, remaining, " %u", (u16)card->to_host_data[i].data_length); UNIFI_SNPRINTF_RET(p, remaining, written); } written = CsrSnprintf(p, remaining, "\n"); UNIFI_SNPRINTF_RET(p, remaining, written); #ifdef CSR_UNSAFE_SDIO_ACCESS written = CsrSnprintf(p, remaining, "Host State: %s\n", states[card->host_state]); UNIFI_SNPRINTF_RET(p, remaining, written); r = unifi_check_io_status(card, &iostate); if (iostate == 1) { written = CsrSnprintf(p, remaining, "I/O Check: F1 disabled\n"); UNIFI_SNPRINTF_RET(p, remaining, written); } else { if (iostate == 1) { written = CsrSnprintf(p, remaining, "I/O Check: pending interrupt\n"); UNIFI_SNPRINTF_RET(p, remaining, written); } written = CsrSnprintf(p, remaining, "BH reason interrupt = %d\n", card->bh_reason_unifi); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "BH reason host = %d\n", card->bh_reason_host); UNIFI_SNPRINTF_RET(p, remaining, written); for (i = 0; i < SHARED_READ_RETRY_LIMIT; i++) { r = unifi_read_8_or_16(card, card->sdio_ctrl_addr + 2, &b); if ((r == CSR_RESULT_SUCCESS) && (!(b & 0x80))) { written = CsrSnprintf(p, remaining, "fhsr: %u (driver thinks is %u)\n", b, card->from_host_signals_r); UNIFI_SNPRINTF_RET(p, remaining, written); break; } } iostate = unifi_read_shared_count(card, card->sdio_ctrl_addr + 4); written = CsrSnprintf(p, remaining, "thsw: %u (driver thinks is %u)\n", iostate, card->to_host_signals_w); UNIFI_SNPRINTF_RET(p, remaining, written); } #endif written = CsrSnprintf(p, remaining, "\nStats:\n"); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "Total SDIO bytes: R=%lu W=%lu\n", card->sdio_bytes_read, card->sdio_bytes_written); UNIFI_SNPRINTF_RET(p, remaining, written); written = CsrSnprintf(p, remaining, "Interrupts generated on card: %lu\n", card->unifi_interrupt_seq); UNIFI_SNPRINTF_RET(p, remaining, written); *remain = remaining; return (p - str); } /* unifi_print_status() */
/* * --------------------------------------------------------------------------- * send_signal * * This function queues a signal for sending to UniFi. It first checks * that there is space on the fh_signal_queue for another entry, then * claims any bulk data slots required and copies data into them. Then * increments the fh_signal_queue write count. * * The fh_signal_queue is later processed by the driver bottom half * (in unifi_bh()). * * This function call unifi_pause_xmit() to pause the flow of data plane * packets when: * - the fh_signal_queue ring buffer is full * - there are less than UNIFI_MAX_DATA_REFERENCES (2) bulk data * slots available. * * Arguments: * card Pointer to card context structure * sigptr Pointer to the signal to write to UniFi. * siglen Number of bytes pointer to by sigptr. * bulkdata Array of pointers to an associated bulk data. * sigq To which from-host queue to add the signal. * * Returns: * CSR_RESULT_SUCCESS on success * CSR_WIFI_HIP_RESULT_NO_SPACE if there were insufficient data slots or * no free signal queue entry * * Notes: * Calls unifi_pause_xmit() when the last slots are used. * --------------------------------------------------------------------------- */ static CsrResult send_signal(card_t *card, const CsrUint8 *sigptr, CsrUint32 siglen, const bulk_data_param_t *bulkdata, q_t *sigq, CsrUint32 priority_q, CsrUint32 run_bh) { CsrUint16 i, data_slot_size; card_signal_t *csptr; CsrInt16 qe; CsrResult r; CsrInt16 debug_print = 0; #ifdef CSR_WIFI_RUN_BH_CHECK CsrUint8 from_host_used_slots = 0; #endif data_slot_size = CardGetDataSlotSize(card); /* Check that the fh_data_queue has a free slot */ if (!CSR_WIFI_HIP_Q_SLOTS_FREE(sigq)) { unifi_trace(card->ospriv, UDBG3, "send_signal: %s full\n", sigq->name); return CSR_WIFI_HIP_RESULT_NO_SPACE; } /* * Now add the signal to the From Host signal queue */ /* Get next slot on queue */ qe = CSR_WIFI_HIP_Q_NEXT_W_SLOT(sigq); csptr = CSR_WIFI_HIP_Q_SLOT_DATA(sigq, qe); /* Make up the card_signal struct */ csptr->signal_length = (CsrUint16)siglen; CsrMemCpy((void *)csptr->sigbuf, (void *)sigptr, siglen); for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; ++i) { if ((bulkdata != NULL) && (bulkdata->d[i].data_length != 0)) { CsrUint32 datalen = bulkdata->d[i].data_length; /* Make sure data will fit in a bulk data slot */ if (bulkdata->d[i].os_data_ptr == NULL) { unifi_error(card->ospriv, "send_signal - NULL bulkdata[%d]\n", i); debug_print++; csptr->bulkdata[i].data_length = 0; } else { if (datalen > data_slot_size) { unifi_error(card->ospriv, "send_signal - Invalid data length %u (@%p), " "truncating\n", datalen, bulkdata->d[i].os_data_ptr); datalen = data_slot_size; debug_print++; } /* Store the bulk data info in the soft queue. */ csptr->bulkdata[i].os_data_ptr = (CsrUint8 *)bulkdata->d[i].os_data_ptr; csptr->bulkdata[i].os_net_buf_ptr = (CsrUint8 *)bulkdata->d[i].os_net_buf_ptr; csptr->bulkdata[i].net_buf_length = bulkdata->d[i].net_buf_length; csptr->bulkdata[i].data_length = datalen; } } else { UNIFI_INIT_BULK_DATA(&csptr->bulkdata[i]); } } if (debug_print) { const CsrUint8 *sig = sigptr; unifi_error(card->ospriv, "Signal(%d): %02x %02x %02x %02x %02x %02x %02x %02x" " %02x %02x %02x %02x %02x %02x %02x %02x\n", siglen, sig[0], sig[1], sig[2], sig[3], sig[4], sig[5], sig[6], sig[7], sig[8], sig[9], sig[10], sig[11], sig[12], sig[13], sig[14], sig[15]); unifi_error(card->ospriv, "Bulkdata pointer %p(%d), %p(%d)\n", bulkdata != NULL?bulkdata->d[0].os_data_ptr : NULL, bulkdata != NULL?bulkdata->d[0].data_length : 0, bulkdata != NULL?bulkdata->d[1].os_data_ptr : NULL, bulkdata != NULL?bulkdata->d[1].data_length : 0); } /* Advance the written count to say there is a new entry */ CSR_WIFI_HIP_Q_INC_W(sigq); #ifdef CSR_WIFI_RUN_BH_CHECK FROM_HOST_USED_SLOTS(card, from_host_used_slots); if (from_host_used_slots) { RUN_BH(sigq, card, priority_q, run_bh); } #endif /* * Set the flag to say reason for waking was a host request. * Then ask the OS layer to run the unifi_bh. */ if (run_bh == 1) { card->bh_reason_host = 1; r = unifi_run_bh(card->ospriv); if (r != CSR_RESULT_SUCCESS) { unifi_error(card->ospriv, "failed to run bh.\n"); card->bh_reason_host = 0; /* * The bulk data buffer will be freed by the caller. * We need to invalidate the description of the bulk data in our * soft queue, to prevent the core freeing the bulk data again later. */ for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; ++i) { if (csptr->bulkdata[i].data_length != 0) { csptr->bulkdata[i].os_data_ptr = csptr->bulkdata[i].os_net_buf_ptr = NULL; csptr->bulkdata[i].net_buf_length = csptr->bulkdata[i].data_length = 0; } } return r; } } #ifndef CSR_WIFI_RUN_BH_CHECK else { unifi_error(card->ospriv, "run_bh=%d, bh not called.\n", run_bh); } #endif /* * Have we used up all the fh signal list entries? */ if (CSR_WIFI_HIP_Q_SLOTS_FREE(sigq) == 0) { /* We have filled the queue, so stop the upper layer. The command queue * is an exception, as suspending due to that being full could delay * resume/retry until new commands or data are received. */ if (sigq != &card->fh_command_queue) { /* * Must call unifi_pause_xmit() *before* setting the paused flag. * (the unifi_pause_xmit call should not be after setting the flag because of the possibility of being interrupted * by the bh thread between our setting the flag and the call to unifi_pause_xmit() * If bh thread then cleared the flag, we would end up paused, but without the flag set) * Instead, setting it afterwards means that if this thread is interrupted by the bh thread * the pause flag is still guaranteed to end up set * However the potential deadlock now is that if bh thread emptied the queue and cleared the flag before this thread's * call to unifi_pause_xmit(), then bh thread may not run again because it will be waiting for * a packet to appear in the queue but nothing ever will because xmit is paused. * So we will end up with the queue paused, and the flag set to say it is paused, but bh never runs to unpause it. * (Note even this bad situation would not persist long in practice, because something else (eg rx, or tx in different queue) * is likely to wake bh thread quite soon) * But to avoid this deadlock completely, after setting the flag we check that there is something left in the queue. * If there is, we know that bh thread has not emptied the queue yet. * Since bh thread checks to unpause the queue *after* taking packets from the queue, we know that it is still going to make at * least one more check to see whether it needs to unpause the queue. So all is well. * If there are no packets in the queue, then the deadlock described above might happen. To make sure it does not, we * unpause the queue here. A possible side effect is that unifi_restart_xmit() may (rarely) be called for second time * unnecessarily, which is harmless */ #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) unifi_debug_log_to_buf("P"); #endif unifi_pause_xmit(card->ospriv, (unifi_TrafficQueue)priority_q); card_tx_q_pause(card, priority_q); if (CSR_WIFI_HIP_Q_SLOTS_USED(sigq) == 0) { card_tx_q_unpause(card, priority_q); unifi_restart_xmit(card->ospriv, (unifi_TrafficQueue) priority_q); } } else { unifi_warning(card->ospriv, "send_signal: fh_cmd_q full, not pausing (run_bh=%d)\n", run_bh); } } func_exit(); return CSR_RESULT_SUCCESS; } /* send_signal() */