/*
 * ---------------------------------------------------------------------------
 *  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() */