text: /*****************************************************************************
  
              (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.c
   *
   * PURPOSE: Implementation of the Card API for SDIO.
   *
   * NOTES:
   *      CardInit() is called from the SDIO probe callback when a card is
   *      inserted. This performs the basic SDIO initialisation, enabling i/o
   *      etc.
   *
   * ---------------------------------------------------------------------------
   */
  #include <linux/slab.h>
  #include "csr_wifi_hip_unifi.h"
  #include "csr_wifi_hip_conversions.h"
  #include "csr_wifi_hip_unifiversion.h"
  #include "csr_wifi_hip_card.h"
  #include "csr_wifi_hip_card_sdio.h"
  #include "csr_wifi_hip_chiphelper.h"
  
  
  /* Time to wait between attempts to read MAILBOX0 */
  #define MAILBOX1_TIMEOUT                10  /* in millisecs */
  #define MAILBOX1_ATTEMPTS               200 /* 2 seconds */
  
  #define MAILBOX2_TIMEOUT                5   /* in millisecs */
  #define MAILBOX2_ATTEMPTS               10  /* 50ms */
  
  #define RESET_SETTLE_DELAY              25  /* in millisecs */
  
  static CsrResult card_init_slots(card_t *card);
  static CsrResult card_hw_init(card_t *card);
  static CsrResult firmware_present_in_flash(card_t *card);
  static void bootstrap_chip_hw(card_t *card);
  static CsrResult unifi_reset_hardware(card_t *card);
  static CsrResult unifi_hip_init(card_t *card);
  static CsrResult card_access_panic(card_t *card);
  static CsrResult unifi_read_chip_version(card_t *card);
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_alloc_card
   *
   *      Allocate and initialise the card context structure.
   *
   *  Arguments:
   *      sdio            Pointer to SDIO context pointer to pass to low
   *                      level i/o functions.
   *      ospriv          Pointer to O/S private struct to pass when calling
   *                      callbacks to the higher level system.
   *
   *  Returns:
   *      Pointer to card struct, which represents the driver context or
   *      NULL if the allocation failed.
   * ---------------------------------------------------------------------------
   */
  card_t* unifi_alloc_card(CsrSdioFunction *sdio, void *ospriv)
  {
      card_t *card;
      u32 i;
  
      func_enter();
  
  
      card = kzalloc(sizeof(card_t), GFP_KERNEL);
      if (card == NULL)
      {
          return NULL;
      }
  
      card->sdio_if = sdio;
      card->ospriv  = ospriv;
  
      card->unifi_interrupt_seq = 1;
  
      /* Make these invalid. */
      card->proc_select = (u32)(-1);
      card->dmem_page = (u32)(-1);
      card->pmem_page = (u32)(-1);
  
      card->bh_reason_host = 0;
      card->bh_reason_unifi = 0;
  
      for (i = 0; i < sizeof(card->tx_q_paused_flag) / sizeof(card->tx_q_paused_flag[0]); i++)
      {
          card->tx_q_paused_flag[i] = 0;
      }
      card->memory_resources_allocated = 0;
  
      card->low_power_mode = UNIFI_LOW_POWER_DISABLED;
      card->periodic_wake_mode = UNIFI_PERIODIC_WAKE_HOST_DISABLED;
  
      card->host_state = UNIFI_HOST_STATE_AWAKE;
      card->intmode = CSR_WIFI_INTMODE_DEFAULT;
  
      /*
       * Memory resources for buffers are allocated when the chip is initialised
       * because we need configuration information from the firmware.
       */
  
      /*
       * Initialise wait queues and lists
       */
      card->fh_command_queue.q_body = card->fh_command_q_body;
      card->fh_command_queue.q_length = UNIFI_SOFT_COMMAND_Q_LENGTH;
  
      for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
      {
          card->fh_traffic_queue[i].q_body = card->fh_traffic_q_body[i];
          card->fh_traffic_queue[i].q_length = UNIFI_SOFT_TRAFFIC_Q_LENGTH;
      }
  
  
      /* Initialise mini-coredump pointers in case no coredump buffers
       * are requested by the OS layer.
       */
      card->request_coredump_on_reset = 0;
      card->dump_next_write = NULL;
      card->dump_cur_read = NULL;
      card->dump_buf = NULL;
  
  #ifdef UNIFI_DEBUG
      /* Determine offset of LSB in pointer for later alignment sanity check.
       * Synergy integer types have specific widths, which cause compiler
       * warnings when casting pointer types, e.g. on 64-bit systems.
       */
      {
          u32 val = 0x01234567;
  
          if (*((u8 *)&val) == 0x01)
          {
              card->lsb = sizeof(void *) - 1;     /* BE */
          }
          else
          {
              card->lsb = 0;                      /* LE */
          }
      }
  #endif
      func_exit();
      return card;
  } /* unifi_alloc_card() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_init_card
   *
   *      Reset the hardware and perform HIP initialization
   *
   *  Arguments:
   *      card        Pointer to card struct
   *
   *  Returns:
   *      CsrResult code
   *      CSR_RESULT_SUCCESS if successful
   * ---------------------------------------------------------------------------
   */
  CsrResult unifi_init_card(card_t *card, s32 led_mask)
  {
      CsrResult r;
  
      func_enter();
  
      if (card == NULL)
      {
          func_exit_r(CSR_WIFI_HIP_RESULT_INVALID_VALUE);
          return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
      }
  
      r = unifi_init(card);
      if (r != CSR_RESULT_SUCCESS)
      {
          func_exit_r(r);
          return r;
      }
  
      r = unifi_hip_init(card);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          func_exit_r(r);
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to start host protocol.\n");
          func_exit_r(r);
          return r;
      }
  
      func_exit();
      return CSR_RESULT_SUCCESS;
  }
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_init
   *
   *      Init the hardware.
   *
   *  Arguments:
   *      card        Pointer to card struct
   *
   *  Returns:
   *      CsrResult code
   *      CSR_RESULT_SUCCESS if successful
   * ---------------------------------------------------------------------------
   */
  CsrResult unifi_init(card_t *card)
  {
      CsrResult r;
      CsrResult csrResult;
  
      func_enter();
  
      if (card == NULL)
      {
          func_exit_r(CSR_WIFI_HIP_RESULT_INVALID_VALUE);
          return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
      }
  
      /*
       * Disable the SDIO interrupts while initialising UniFi.
       * Re-enable them when f/w is running.
       */
      csrResult = CsrSdioInterruptDisable(card->sdio_if);
      if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
      {
          return CSR_WIFI_HIP_RESULT_NO_DEVICE;
      }
  
      /*
       * UniFi's PLL may start with a slow clock (~ 1 MHz) so initially
       * set the SDIO bus clock to a similar value or SDIO accesses may
       * fail.
       */
      csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_SAFE_HZ);
      if (csrResult != CSR_RESULT_SUCCESS)
      {
          r = ConvertCsrSdioToCsrHipResult(card, csrResult);
          func_exit_r(r);
          return r;
      }
      card->sdio_clock_speed = UNIFI_SDIO_CLOCK_SAFE_HZ;
  
      /*
       * Reset UniFi. Note, this only resets the WLAN function part of the chip,
       * the SDIO interface is not reset.
       */
      unifi_trace(card->ospriv, UDBG1, "Resetting UniFi\n");
      r = unifi_reset_hardware(card);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to reset UniFi\n");
          func_exit_r(r);
          return r;
      }
  
      /* Reset the power save mode, to be active until the MLME-reset is complete */
      r = unifi_configure_low_power_mode(card,
                                         UNIFI_LOW_POWER_DISABLED, UNIFI_PERIODIC_WAKE_HOST_DISABLED);
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to set power save mode\n");
          func_exit_r(r);
          return r;
      }
  
      /*
       * Set initial value of page registers.
       * The page registers will be maintained by unifi_read...() and
       * unifi_write...().
       */
      card->proc_select = (u32)(-1);
      card->dmem_page = (u32)(-1);
      card->pmem_page = (u32)(-1);
      r = unifi_write_direct16(card, ChipHelper_HOST_WINDOW3_PAGE(card->helper) * 2, 0);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to write SHARED_DMEM_PAGE\n");
          func_exit_r(r);
          return r;
      }
      r = unifi_write_direct16(card, ChipHelper_HOST_WINDOW2_PAGE(card->helper) * 2, 0);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to write PROG_MEM2_PAGE\n");
          func_exit_r(r);
          return r;
      }
  
      /*
       * If the driver has reset UniFi due to previous SDIO failure, this may
       * have been due to a chip watchdog reset. In this case, the driver may
       * have requested a mini-coredump which needs to be captured now the
       * SDIO interface is alive.
       */
      (void)unifi_coredump_handle_request(card);
  
      /*
       * Probe to see if the UniFi has ROM/flash to boot from. CSR6xxx should do.
       */
      r = firmware_present_in_flash(card);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r == CSR_WIFI_HIP_RESULT_NOT_FOUND)
      {
          unifi_error(card->ospriv, "No firmware found\n");
      }
      else if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Probe for Flash failed\n");
      }
  
      func_exit_r(r);
      return r;
  } /* unifi_init() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_download
   *
   *      Load the firmware.
   *
   *  Arguments:
   *      card        Pointer to card struct
   *      led_mask    Loader LED mask
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS on success
   *      CsrResult error code on failure.
   * ---------------------------------------------------------------------------
   */
  CsrResult unifi_download(card_t *card, s32 led_mask)
  {
      CsrResult r;
      void *dlpriv;
  
      func_enter();
  
      if (card == NULL)
      {
          func_exit_r(CSR_WIFI_HIP_RESULT_INVALID_VALUE);
          return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
      }
  
      /* Set the loader led mask */
      card->loader_led_mask = led_mask;
  
      /* Get the firmware file information */
      unifi_trace(card->ospriv, UDBG1, "downloading firmware...\n");
  
      dlpriv = unifi_dl_fw_read_start(card, UNIFI_FW_STA);
      if (dlpriv == NULL)
      {
          func_exit_r(CSR_WIFI_HIP_RESULT_NOT_FOUND);
          return CSR_WIFI_HIP_RESULT_NOT_FOUND;
      }
  
      /* Download the firmware. */
      r = unifi_dl_firmware(card, dlpriv);
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to download firmware\n");
          func_exit_r(r);
          return r;
      }
  
      /* Free the firmware file information. */
      unifi_fw_read_stop(card->ospriv, dlpriv);
  
      func_exit();
  
      return CSR_RESULT_SUCCESS;
  } /* unifi_download() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_hip_init
   *
   *      This function performs the f/w initialisation sequence as described
   *      in the Unifi Host Interface Protocol Specification.
   *      It allocates memory for host-side slot data and signal queues.
   *
   *  Arguments:
   *      card        Pointer to card struct
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS on success or else a CSR error code
   *
   *  Notes:
   *      The firmware must have been downloaded.
   * ---------------------------------------------------------------------------
   */
  static CsrResult unifi_hip_init(card_t *card)
  {
      CsrResult r;
      CsrResult csrResult;
  
      func_enter();
  
      r = card_hw_init(card);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to establish communication with UniFi\n");
          func_exit_r(r);
          return r;
      }
  #ifdef CSR_PRE_ALLOC_NET_DATA
      /* if there is any preallocated netdata left from the prev session free it now */
      prealloc_netdata_free(card);
  #endif
      /*
       * Allocate memory for host-side slot data and signal queues.
       * We need the config info read from the firmware to know how much
       * memory to allocate.
       */
      r = card_init_slots(card);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Init slots failed: %d\n", r);
          func_exit_r(r);
          return r;
      }
  
      unifi_trace(card->ospriv, UDBG2, "Sending first UniFi interrupt\n");
  
      r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE);
      if (r != CSR_RESULT_SUCCESS)
      {
          func_exit_r(r);
          return r;
      }
  
      /* Enable the SDIO interrupts now that the f/w is running. */
      csrResult = CsrSdioInterruptEnable(card->sdio_if);
      if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
      {
          return CSR_WIFI_HIP_RESULT_NO_DEVICE;
      }
  
      /* Signal the UniFi to start handling messages */
      r = CardGenInt(card);
      if (r != CSR_RESULT_SUCCESS)
      {
          func_exit_r(r);
          return r;
      }
  
      func_exit();
  
      return CSR_RESULT_SUCCESS;
  } /* unifi_hip_init() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  _build_sdio_config_data
   *
   *      Unpack the SDIO configuration information from a buffer read from
   *      UniFi into a host structure.
   *      The data is byte-swapped for a big-endian host if necessary by the
   *      UNPACK... macros.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *      cfg_data        Destination structure to unpack into.
   *      cfg_data_buf    Source buffer to read from. This should be the raw
   *                      data read from UniFi.
   *
   *  Returns:
   *      None.
   * ---------------------------------------------------------------------------
   */
  static void _build_sdio_config_data(sdio_config_data_t *cfg_data,
                                      const u8     *cfg_data_buf)
  {
      s16 offset = 0;
  
      cfg_data->version = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT16;
  
      cfg_data->sdio_ctrl_offset = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT16;
  
      cfg_data->fromhost_sigbuf_handle = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT16;
  
      cfg_data->tohost_sigbuf_handle = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT16;
  
      cfg_data->num_fromhost_sig_frags = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT16;
  
      cfg_data->num_tohost_sig_frags = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT16;
  
      cfg_data->num_fromhost_data_slots = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT16;
  
      cfg_data->num_tohost_data_slots = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT16;
  
      cfg_data->data_slot_size = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT16;
  
      cfg_data->initialised = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT16;
  
      cfg_data->overlay_size = CSR_GET_UINT32_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT32;
  
      cfg_data->data_slot_round = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT16;
  
      cfg_data->sig_frag_size = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
      offset += SIZEOF_UINT16;
  
      cfg_data->tohost_signal_padding = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
  } /* _build_sdio_config_data() */
  
  
  /*
   * - Function ----------------------------------------------------------------
   * card_hw_init()
   *
   *      Perform the initialisation procedure described in the UniFi Host
   *      Interface Protocol document (section 3.3.8) and read the run-time
   *      configuration information from the UniFi. This is stuff like number
   *      of bulk data slots etc.
   *
   *      The card enumeration and SD initialisation has already been done by
   *      the SDIO library, see card_sdio_init().
   *
   *      The initialisation is done when firmware is ready, i.e. this may need
   *      to be called after a f/w download operation.
   *
   *      The initialisation procedure goes like this:
   *       - Wait for UniFi to start-up by polling SHARED_MAILBOX1
   *       - Find the symbol table and look up SLT_SDIO_SLOT_CONFIG
   *       - Read the config structure
   *       - Check the "SDIO initialised" flag, if not zero do a h/w reset and
   *         start again
   *       - Decide the number of bulk data slots to allocate, allocate them and
   *         set "SDIO initialised" flag (and generate an interrupt) to say so.
   *
   * Arguments:
   *      card        Pointer to card struct
   *
   * Returns:
   *      CSR_RESULT_SUCEESS on success,
   *      a CSR error code on failure
   *
   * Notes:
   *      All data in the f/w is stored in a little endian format, without any
   *      padding bytes. Every read from this memory has to be transformed in
   *      host (cpu specific) format, before it is stored in driver's parameters
   *      or/and structures. Athough unifi_card_read16() and unifi_read32() do perform
   *      the convertion internally, unifi_readn() does not.
   * ---------------------------------------------------------------------------
   */
  static CsrResult card_hw_init(card_t *card)
  {
      u32 slut_address;
      u16 initialised;
      u16 finger_print;
      symbol_t slut;
      sdio_config_data_t *cfg_data;
      u8 cfg_data_buf[SDIO_CONFIG_DATA_SIZE];
      CsrResult r;
      void *dlpriv;
      s16 major, minor;
      s16 search_4slut_again;
      CsrResult csrResult;
  
      func_enter();
  
      /*
       * The device revision from the TPLMID_MANF and TPLMID_CARD fields
       * of the CIS are available as
       *   card->sdio_if->pDevice->ManfID
       *   card->sdio_if->pDevice->AppID
       */
  
      /*
       * Run in a loop so we can patch.
       */
      do
      {
          /* Reset these each time around the loop. */
          search_4slut_again = 0;
          cfg_data = NULL;
  
          r = card_wait_for_firmware_to_start(card, &slut_address);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_error(card->ospriv, "Firmware hasn't started\n");
              func_exit_r(r);
              return r;
          }
          unifi_trace(card->ospriv, UDBG4, "SLUT addr 0x%lX\n", slut_address);
  
          /*
           * Firmware has started, but doesn't know full clock configuration yet
           * as some of the information may be in the MIB. Therefore we set an
           * initial SDIO clock speed, faster than UNIFI_SDIO_CLOCK_SAFE_HZ, for
           * the patch download and subsequent firmware initialisation, and
           * full speed UNIFI_SDIO_CLOCK_MAX_HZ will be set once the f/w tells us
           * that it is ready.
           */
          csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_INIT_HZ);
          if (csrResult != CSR_RESULT_SUCCESS)
          {
              r = ConvertCsrSdioToCsrHipResult(card, csrResult);
              func_exit_r(r);
              return r;
          }
          card->sdio_clock_speed = UNIFI_SDIO_CLOCK_INIT_HZ;
  
          /*
           * Check the SLUT fingerprint.
           * The slut_address is a generic pointer so we must use unifi_card_read16().
           */
          unifi_trace(card->ospriv, UDBG4, "Looking for SLUT finger print\n");
          finger_print = 0;
          r = unifi_card_read16(card, slut_address, &finger_print);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_error(card->ospriv, "Failed to read SLUT finger print\n");
              func_exit_r(r);
              return r;
          }
  
          if (finger_print != SLUT_FINGERPRINT)
          {
              unifi_error(card->ospriv, "Failed to find Symbol lookup table fingerprint\n");
              func_exit_r(CSR_RESULT_FAILURE);
              return CSR_RESULT_FAILURE;
          }
  
          /* Symbol table starts imedately after the fingerprint */
          slut_address += 2;
  
          /* Search the table until either the end marker is found, or the
           * loading of patch firmware invalidates the current table.
           */
          while (!search_4slut_again)
          {
              u16 s;
              u32 l;
  
              r = unifi_card_read16(card, slut_address, &s);
              if (r != CSR_RESULT_SUCCESS)
              {
                  func_exit_r(r);
                  return r;
              }
              slut_address += 2;
  
              if (s == CSR_SLT_END)
              {
                  unifi_trace(card->ospriv, UDBG3, "  found CSR_SLT_END\n");
                  break;
              }
  
              r = unifi_read32(card, slut_address, &l);
              if (r != CSR_RESULT_SUCCESS)
              {
                  func_exit_r(r);
                  return r;
              }
              slut_address += 4;
  
              slut.id = s;
              slut.obj = l;
  
              unifi_trace(card->ospriv, UDBG3, "  found SLUT id %02d.%08lx\n", slut.id, slut.obj);
              switch (slut.id)
              {
                  case CSR_SLT_SDIO_SLOT_CONFIG:
                      cfg_data = &card->config_data;
                      /*
                       * unifi_card_readn reads n bytes from the card, where data is stored
                       * in a little endian format, without any padding bytes. So, we
                       * can not just pass the cfg_data pointer or use the
                       * sizeof(sdio_config_data_t) since the structure in the host can
                       * be big endian formatted or have padding bytes for alignment.
                       * We use a char buffer to read the data from the card.
                       */
                      r = unifi_card_readn(card, slut.obj, cfg_data_buf, SDIO_CONFIG_DATA_SIZE);
                      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
                      {
                          return r;
                      }
                      if (r != CSR_RESULT_SUCCESS)
                      {
                          unifi_error(card->ospriv, "Failed to read config data\n");
                          func_exit_r(r);
                          return r;
                      }
                      /* .. and then we copy the data to the host structure */
                      _build_sdio_config_data(cfg_data, cfg_data_buf);
  
                      /* Make sure the from host data slots are what we expect
                          we reserve 2 for commands and there should be at least
                          1 left for each access category */
                      if ((cfg_data->num_fromhost_data_slots < UNIFI_RESERVED_COMMAND_SLOTS)
                          || (cfg_data->num_fromhost_data_slots - UNIFI_RESERVED_COMMAND_SLOTS) / UNIFI_NO_OF_TX_QS == 0)
                      {
                          unifi_error(card->ospriv, "From host data slots %d\n", cfg_data->num_fromhost_data_slots);
                          unifi_error(card->ospriv, "need to be (queues * x + 2) (UNIFI_RESERVED_COMMAND_SLOTS for commands)\n");
                          func_exit_r(CSR_RESULT_FAILURE);
                          return CSR_RESULT_FAILURE;
                      }
  
                      /* Configure SDIO to-block-size padding */
                      if (card->sdio_io_block_pad)
                      {
                      /*
                       * Firmware limits the maximum padding size via data_slot_round.
                       * Therefore when padding to whole block sizes, the block size
                       * must be configured correctly by adjusting CSR_WIFI_HIP_SDIO_BLOCK_SIZE.
                       */
                          if (cfg_data->data_slot_round < card->sdio_io_block_size)
                          {
                              unifi_error(card->ospriv,
                                          "Configuration error: Block size of %d exceeds f/w data_slot_round of %d\n",
                                          card->sdio_io_block_size, cfg_data->data_slot_round);
                              return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
                          }
  
                          /*
                           * To force the To-Host signals to be rounded up to the SDIO block
                           * size, we need to write the To-Host Signal Padding Fragments
                           * field of the SDIO configuration in UniFi.
                           */
                          if ((card->sdio_io_block_size % cfg_data->sig_frag_size) != 0)
                          {
                              unifi_error(card->ospriv, "Configuration error: Can not pad to-host signals.\n");
                              func_exit_r(CSR_WIFI_HIP_RESULT_INVALID_VALUE);
                              return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
                          }
                          cfg_data->tohost_signal_padding = (u16) (card->sdio_io_block_size / cfg_data->sig_frag_size);
                          unifi_info(card->ospriv, "SDIO block size %d requires %d padding chunks\n",
                                     card->sdio_io_block_size, cfg_data->tohost_signal_padding);
                          r = unifi_card_write16(card, slut.obj + SDIO_TO_HOST_SIG_PADDING_OFFSET, cfg_data->tohost_signal_padding);
                          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
                          {
                              return r;
                          }
                          if (r != CSR_RESULT_SUCCESS)
                          {
                              unifi_error(card->ospriv, "Failed to write To-Host Signal Padding Fragments\n");
                              func_exit_r(r);
                              return r;
                          }
                      }
  
                      /* Reconstruct the Generic Pointer address of the
                       * SDIO Control Data Struct.
                       */
                      card->sdio_ctrl_addr = cfg_data->sdio_ctrl_offset | (UNIFI_SH_DMEM << 24);
                      card->init_flag_addr = slut.obj + SDIO_INIT_FLAG_OFFSET;
                      break;
  
                  case CSR_SLT_BUILD_ID_NUMBER:
                  {
                      u32 n;
                      r = unifi_read32(card, slut.obj, &n);
                      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
                      {
                          return r;
                      }
                      if (r != CSR_RESULT_SUCCESS)
                      {
                          unifi_error(card->ospriv, "Failed to read build id\n");
                          func_exit_r(r);
                          return r;
                      }
                      card->build_id = n;
                  }
                  break;
  
                  case CSR_SLT_BUILD_ID_STRING:
                      r = unifi_readnz(card, slut.obj, card->build_id_string,
                                       sizeof(card->build_id_string));
                      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
                      {
                          return r;
                      }
                      if (r != CSR_RESULT_SUCCESS)
                      {
                          unifi_error(card->ospriv, "Failed to read build string\n");
                          func_exit_r(r);
                          return r;
                      }
                      break;
  
                  case CSR_SLT_PERSISTENT_STORE_DB:
                      break;
  
                  case CSR_SLT_BOOT_LOADER_CONTROL:
  
                      /* This command copies most of the station firmware
                       * image from ROM into program RAM.  It also clears
                       * out the zerod data and sets up the initialised
                       * data. */
                      r = unifi_do_loader_op(card, slut.obj + 6, UNIFI_BOOT_LOADER_LOAD_STA);
                      if (r != CSR_RESULT_SUCCESS)
                      {
                          unifi_error(card->ospriv, "Failed to write loader load image command\n");
                          func_exit_r(r);
                          return r;
                      }
  
                      dlpriv = unifi_dl_fw_read_start(card, UNIFI_FW_STA);
  
                      /* dlpriv might be NULL, we still need to do the do_loader_op step. */
                      if (dlpriv != NULL)
                      {
                      /* Download the firmware. */
                          r = unifi_dl_patch(card, dlpriv, slut.obj);
  
                      /* Free the firmware file information. */
                          unifi_fw_read_stop(card->ospriv, dlpriv);
  
                          if (r != CSR_RESULT_SUCCESS)
                          {
                              unifi_error(card->ospriv, "Failed to patch firmware\n");
                              func_exit_r(r);
                              return r;
                          }
                      }
  
                      /* This command starts the firmware image that we want (the
                      * station by default) with any patches required applied. */
                      r = unifi_do_loader_op(card, slut.obj + 6, UNIFI_BOOT_LOADER_RESTART);
                      if (r != CSR_RESULT_SUCCESS)
                      {
                          unifi_error(card->ospriv, "Failed to write loader restart command\n");
                          func_exit_r(r);
                          return r;
                      }
  
                      /* The now running patch f/w defines a new SLUT data structure -
                       * the current one is no longer valid. We must drop out of the
                       * processing loop and enumerate the new SLUT (which may appear
                       * at a different offset).
                       */
                      search_4slut_again = 1;
                      break;
  
                  case CSR_SLT_PANIC_DATA_PHY:
                      card->panic_data_phy_addr = slut.obj;
                      break;
  
                  case CSR_SLT_PANIC_DATA_MAC:
                      card->panic_data_mac_addr = slut.obj;
                      break;
  
                  default:
                      /* do nothing */
                      break;
              }
          } /* while */
      } while (search_4slut_again);
  
      /* Did we find the Config Data ? */
      if (cfg_data == NULL)
      {
          unifi_error(card->ospriv, "Failed to find SDIO_SLOT_CONFIG Symbol\n");
          func_exit_r(CSR_RESULT_FAILURE);
          return CSR_RESULT_FAILURE;
      }
  
      /*
       * Has ths card already been initialised?
       * If so, return an error so we do a h/w reset and start again.
       */
      r = unifi_card_read16(card, card->init_flag_addr, &initialised);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to read init flag at %08lx\n",
                      card->init_flag_addr);
          func_exit_r(r);
          return r;
      }
      if (initialised != 0)
      {
          func_exit_r(CSR_RESULT_FAILURE);
          return CSR_RESULT_FAILURE;
      }
  
  
      /*
       * Now check the UniFi firmware version
       */
      major = (cfg_data->version >> 8) & 0xFF;
      minor = cfg_data->version & 0xFF;
      unifi_info(card->ospriv, "UniFi f/w protocol version %d.%d (driver %d.%d)\n",
                 major, minor,
                 UNIFI_HIP_MAJOR_VERSION, UNIFI_HIP_MINOR_VERSION);
  
      unifi_info(card->ospriv, "Firmware build %u: %s\n",
                 card->build_id, card->build_id_string);
  
      if (major != UNIFI_HIP_MAJOR_VERSION)
      {
          unifi_error(card->ospriv, "UniFi f/w protocol major version (%d) is different from driver (v%d.%d)\n",
                      major, UNIFI_HIP_MAJOR_VERSION, UNIFI_HIP_MINOR_VERSION);
  #ifndef CSR_WIFI_DISABLE_HIP_VERSION_CHECK
          func_exit_r(CSR_RESULT_FAILURE);
          return CSR_RESULT_FAILURE;
  #endif
      }
      if (minor < UNIFI_HIP_MINOR_VERSION)
      {
          unifi_error(card->ospriv, "UniFi f/w protocol version (v%d.%d) is older than minimum required by driver (v%d.%d).\n",
                      major, minor,
                      UNIFI_HIP_MAJOR_VERSION, UNIFI_HIP_MINOR_VERSION);
  #ifndef CSR_WIFI_DISABLE_HIP_VERSION_CHECK
          func_exit_r(CSR_RESULT_FAILURE);
          return CSR_RESULT_FAILURE;
  #endif
      }
  
      /* Read panic codes from a previous firmware panic. If the firmware has
       * not panicked since power was applied (e.g. power-off hard reset)
       * the stored panic codes will not be updated.
       */
      unifi_read_panic(card);
  
      func_exit();
      return CSR_RESULT_SUCCESS;
  } /* card_hw_init() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  card_wait_for_unifi_to_reset
   *
   *      Waits for a reset to complete by polling the WLAN function enable
   *      bit (which is cleared on reset).
   *
   *  Arguments:
   *      card            Pointer to card struct
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS on success, CSR error code on failure.
   * ---------------------------------------------------------------------------
   */
  static CsrResult card_wait_for_unifi_to_reset(card_t *card)
  {
      s16 i;
      CsrResult r;
      u8 io_enable;
      CsrResult csrResult;
  
      func_enter();
  
      r = CSR_RESULT_SUCCESS;
      for (i = 0; i < MAILBOX2_ATTEMPTS; i++)
      {
          unifi_trace(card->ospriv, UDBG1, "waiting for reset to complete, attempt %d\n", i);
          if (card->chip_id > SDIO_CARD_ID_UNIFI_2)
          {
              /* It's quite likely that this read will timeout for the
               * first few tries - especially if we have reset via
               * DBG_RESET.
               */
  #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE)
              unifi_debug_log_to_buf("m0@%02X=", SDIO_IO_READY);
  #endif
              csrResult = CsrSdioF0Read8(card->sdio_if, SDIO_IO_READY, &io_enable);
  #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE)
              if (csrResult != CSR_RESULT_SUCCESS)
              {
                  unifi_debug_log_to_buf("error=%X\n", csrResult);
              }
              else
              {
                  unifi_debug_log_to_buf("%X\n", io_enable);
              }
  #endif
              if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
              {
                  return CSR_WIFI_HIP_RESULT_NO_DEVICE;
              }
              r = CSR_RESULT_SUCCESS;
              if (csrResult != CSR_RESULT_SUCCESS)
              {
                  r = ConvertCsrSdioToCsrHipResult(card, csrResult);
              }
          }
          else
          {
              r = sdio_read_f0(card, SDIO_IO_ENABLE, &io_enable);
          }
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r == CSR_RESULT_SUCCESS)
          {
              u16 mbox2;
              s16 enabled = io_enable & (1 << card->function);
  
              if (!enabled)
              {
                  unifi_trace(card->ospriv, UDBG1,
                              "Reset complete (function %d is disabled) in ~ %u msecs\n",
                              card->function, i * MAILBOX2_TIMEOUT);
  
                  /* Enable WLAN function and verify MAILBOX2 is zero'd */
                  csrResult = CsrSdioFunctionEnable(card->sdio_if);
                  if (csrResult != CSR_RESULT_SUCCESS)
                  {
                      r = ConvertCsrSdioToCsrHipResult(card, csrResult);
                      unifi_error(card->ospriv, "CsrSdioFunctionEnable failed %d\n", r);
                      break;
                  }
              }
  
              r = unifi_read_direct16(card, ChipHelper_SDIO_HIP_HANDSHAKE(card->helper) * 2, &mbox2);
              if (r != CSR_RESULT_SUCCESS)
              {
                  unifi_error(card->ospriv, "read HIP_HANDSHAKE failed %d\n", r);
                  break;
              }
              if (mbox2 != 0)
              {
                  unifi_error(card->ospriv, "MAILBOX2 non-zero after reset (mbox2 = %04x)\n", mbox2);
                  r = CSR_RESULT_FAILURE;
              }
              break;
          }
          else
          {
              if (card->chip_id > SDIO_CARD_ID_UNIFI_2)
              {
                  /* We ignore read failures for the first few reads,
                   * they are probably benign. */
                  if (i > MAILBOX2_ATTEMPTS / 4)
                  {
                      unifi_trace(card->ospriv, UDBG1, "Failed to read CCCR IO Ready register while polling for reset\n");
                  }
              }
              else
              {
                  unifi_trace(card->ospriv, UDBG1, "Failed to read CCCR IO Enable register while polling for reset\n");
              }
          }
          CsrThreadSleep(MAILBOX2_TIMEOUT);
      }
  
      if (r == CSR_RESULT_SUCCESS && i == MAILBOX2_ATTEMPTS)
      {
          unifi_trace(card->ospriv, UDBG1, "Timeout waiting for UniFi to complete reset\n");
          r = CSR_RESULT_FAILURE;
      }
  
      func_exit();
      return r;
  } /* card_wait_for_unifi_to_reset() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  card_wait_for_unifi_to_disable
   *
   *      Waits for the function to become disabled by polling the
   *      IO_READY bit.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS on success, CSR error code on failure.
   *
   *  Notes: This function can only be used with
   *         card->chip_id > SDIO_CARD_ID_UNIFI_2
   * ---------------------------------------------------------------------------
   */
  static CsrResult card_wait_for_unifi_to_disable(card_t *card)
  {
      s16 i;
      CsrResult r;
      u8 io_enable;
      CsrResult csrResult;
  
      func_enter();
  
      if (card->chip_id <= SDIO_CARD_ID_UNIFI_2)
      {
          unifi_error(card->ospriv,
                      "Function reset method not supported for chip_id=%d\n",
                      card->chip_id);
          func_exit();
          return CSR_RESULT_FAILURE;
      }
  
      r = CSR_RESULT_SUCCESS;
      for (i = 0; i < MAILBOX2_ATTEMPTS; i++)
      {
          unifi_trace(card->ospriv, UDBG1, "waiting for disable to complete, attempt %d\n", i);
  
          /*
           * It's quite likely that this read will timeout for the
           * first few tries - especially if we have reset via
           * DBG_RESET.
           */
  #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE)
          unifi_debug_log_to_buf("r0@%02X=", SDIO_IO_READY);
  #endif
          csrResult = CsrSdioF0Read8(card->sdio_if, SDIO_IO_READY, &io_enable);
  #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE)
          if (csrResult != CSR_RESULT_SUCCESS)
          {
              unifi_debug_log_to_buf("error=%X\n", csrResult);
          }
          else
          {
              unifi_debug_log_to_buf("%X\n", io_enable);
          }
  #endif
          if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
          {
              return CSR_WIFI_HIP_RESULT_NO_DEVICE;
          }
          if (csrResult == CSR_RESULT_SUCCESS)
          {
              s16 enabled = io_enable & (1 << card->function);
              r = CSR_RESULT_SUCCESS;
              if (!enabled)
              {
                  unifi_trace(card->ospriv, UDBG1,
                              "Disable complete (function %d is disabled) in ~ %u msecs\n",
                              card->function, i * MAILBOX2_TIMEOUT);
  
                  break;
              }
          }
          else
          {
              /*
               * We ignore read failures for the first few reads,
               * they are probably benign.
               */
              r = ConvertCsrSdioToCsrHipResult(card, csrResult);
              if (i > (MAILBOX2_ATTEMPTS / 4))
              {
                  unifi_trace(card->ospriv, UDBG1,
                              "Failed to read CCCR IO Ready register while polling for disable\n");
              }
          }
          CsrThreadSleep(MAILBOX2_TIMEOUT);
      }
  
      if ((r == CSR_RESULT_SUCCESS) && (i == MAILBOX2_ATTEMPTS))
      {
          unifi_trace(card->ospriv, UDBG1, "Timeout waiting for UniFi to complete disable\n");
          r = CSR_RESULT_FAILURE;
      }
  
      func_exit();
      return r;
  } /* card_wait_for_unifi_to_reset() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  card_wait_for_firmware_to_start
   *
   *      Polls the MAILBOX1 register for a non-zero value.
   *      Then reads MAILBOX0 and forms the two values into a 32-bit address
   *      which is returned to the caller.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *      paddr           Pointer to receive the UniFi address formed
   *                      by concatenating MAILBOX1 and MAILBOX0.
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS on success, CSR error code on failure.
   * ---------------------------------------------------------------------------
   */
  CsrResult card_wait_for_firmware_to_start(card_t *card, u32 *paddr)
  {
      s32 i;
      u16 mbox0, mbox1;
      CsrResult r;
  
      func_enter();
  
      /*
       * Wait for UniFi to initialise its data structures by polling
       * the SHARED_MAILBOX1 register.
       * Experience shows this is typically 120ms.
       */
      CsrThreadSleep(MAILBOX1_TIMEOUT);
  
      mbox1 = 0;
      unifi_trace(card->ospriv, UDBG1, "waiting for MAILBOX1 to be non-zero...\n");
      for (i = 0; i < MAILBOX1_ATTEMPTS; i++)
      {
          r = unifi_read_direct16(card, ChipHelper_MAILBOX1(card->helper) * 2, &mbox1);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r != CSR_RESULT_SUCCESS)
          {
              /* These reads can fail if UniFi isn't up yet, so try again */
              unifi_warning(card->ospriv, "Failed to read UniFi Mailbox1 register\n");
          }
  
          if ((r == CSR_RESULT_SUCCESS) && (mbox1 != 0))
          {
              unifi_trace(card->ospriv, UDBG1, "MAILBOX1 ready (0x%04X) in %u millisecs\n",
                          mbox1, i * MAILBOX1_TIMEOUT);
  
              /* Read the MAILBOX1 again in case we caught the value as it
               * changed. */
              r = unifi_read_direct16(card, ChipHelper_MAILBOX1(card->helper) * 2, &mbox1);
              if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
              {
                  return r;
              }
              if (r != CSR_RESULT_SUCCESS)
              {
                  unifi_error(card->ospriv, "Failed to read UniFi Mailbox1 register for second time\n");
                  func_exit_r(r);
                  return r;
              }
              unifi_trace(card->ospriv, UDBG1, "MAILBOX1 value=0x%04X\n", mbox1);
  
              break;
          }
  
          CsrThreadSleep(MAILBOX1_TIMEOUT);
          if ((i % 100) == 99)
          {
              unifi_trace(card->ospriv, UDBG2, "MAILBOX1 not ready (0x%X), still trying...\n", mbox1);
          }
      }
  
      if ((r == CSR_RESULT_SUCCESS) && (mbox1 == 0))
      {
          unifi_trace(card->ospriv, UDBG1, "Timeout waiting for firmware to start, Mailbox1 still 0 after %d ms\n",
                      MAILBOX1_ATTEMPTS * MAILBOX1_TIMEOUT);
          func_exit_r(CSR_RESULT_FAILURE);
          return CSR_RESULT_FAILURE;
      }
  
  
      /*
       * Complete the reset handshake by setting MAILBOX2 to 0xFFFF
       */
      r = unifi_write_direct16(card, ChipHelper_SDIO_HIP_HANDSHAKE(card->helper) * 2, 0xFFFF);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to write f/w startup handshake to MAILBOX2\n");
          func_exit_r(r);
          return r;
      }
  
  
      /*
       * Read the Symbol Look Up Table (SLUT) offset.
       * Top 16 bits are in mbox1, read the lower 16 bits from mbox0.
       */
      mbox0 = 0;
      r = unifi_read_direct16(card, ChipHelper_MAILBOX0(card->helper) * 2, &mbox0);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to read UniFi Mailbox0 register\n");
          func_exit_r(r);
          return r;
      }
  
      *paddr = (((u32)mbox1 << 16) | mbox0);
  
      func_exit();
      return CSR_RESULT_SUCCESS;
  } /* card_wait_for_firmware_to_start() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_capture_panic
   *
   *      Attempt to capture panic codes from the firmware. This may involve
   *      warm reset of the chip to regain access following a watchdog reset.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS if panic codes were captured, or none available
   *      CSR_RESULT_FAILURE if the driver could not access function 1
   * ---------------------------------------------------------------------------
   */
  CsrResult unifi_capture_panic(card_t *card)
  {
      func_enter();
  
      /* The firmware must have previously initialised to read the panic addresses
       * from the SLUT
       */
      if (!card->panic_data_phy_addr || !card->panic_data_mac_addr)
      {
          func_exit();
          return CSR_RESULT_SUCCESS;
      }
  
      /* Ensure we can access function 1 following a panic/watchdog reset */
      if (card_access_panic(card) == CSR_RESULT_SUCCESS)
      {
          /* Read the panic codes */
          unifi_read_panic(card);
      }
      else
      {
          unifi_info(card->ospriv, "Unable to read panic codes");
      }
  
      func_exit();
      return CSR_RESULT_SUCCESS;
  }
  
  
  /*
   * ---------------------------------------------------------------------------
   *  card_access_panic
   *      Attempt to read the WLAN SDIO function in order to read panic codes
   *      and perform various reset steps to regain access if the read fails.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS if panic codes can be read
   *      CSR error code if panic codes can not be read
   * ---------------------------------------------------------------------------
   */
  static CsrResult card_access_panic(card_t *card)
  {
      u16 data_u16 = 0;
      s32 i;
      CsrResult r, sr;
  
      func_enter();
  
      /* A chip version of zero means that the version never got succesfully read
       * during reset. In this case give up because it will not be possible to
       * verify the chip version.
       */
      if (!card->chip_version)
      {
          unifi_info(card->ospriv, "Unknown chip version\n");
          return CSR_RESULT_FAILURE;
      }
  
      /* Ensure chip is awake or access to function 1 will fail */
      r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "unifi_set_host_state() failed %d\n", r);
          return CSR_RESULT_FAILURE; /* Card is probably unpowered */
      }
      CsrThreadSleep(20);
  
      for (i = 0; i < 3; i++)
      {
          sr = CsrSdioRead16(card->sdio_if, CHIP_HELPER_UNIFI_GBL_CHIP_VERSION * 2, &data_u16);
          if (sr != CSR_RESULT_SUCCESS || data_u16 != card->chip_version)
          {
              unifi_info(card->ospriv, "Failed to read valid chip version sr=%d (0x%04x want 0x%04x) try %d\n",
                         sr, data_u16, card->chip_version, i);
  
              /* Set clock speed low */
              sr = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_SAFE_HZ);
              if (sr != CSR_RESULT_SUCCESS)
              {
                  unifi_error(card->ospriv, "CsrSdioMaxBusClockFrequencySet() failed1 %d\n", sr);
                  r = ConvertCsrSdioToCsrHipResult(card, sr);
              }
              card->sdio_clock_speed = UNIFI_SDIO_CLOCK_SAFE_HZ;
  
              /* First try re-enabling function in case a f/w watchdog reset disabled it */
              if (i == 0)
              {
                  unifi_info(card->ospriv, "Try function enable\n");
                  sr = CsrSdioFunctionEnable(card->sdio_if);
                  if (sr != CSR_RESULT_SUCCESS)
                  {
                      r = ConvertCsrSdioToCsrHipResult(card, sr);
                      unifi_error(card->ospriv, "CsrSdioFunctionEnable failed %d (HIP %d)\n", sr, r);
                  }
                  continue;
              }
  
              /* Second try, set awake */
              unifi_info(card->ospriv, "Try set awake\n");
  
              /* Ensure chip is awake */
              r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE);
              if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
              {
                  return r;
              }
              if (r != CSR_RESULT_SUCCESS)
              {
                  unifi_error(card->ospriv, "unifi_set_host_state() failed2 %d\n", r);
              }
  
              /* Set clock speed low in case setting the host state raised it, which
               * would only happen if host state was previously TORPID
               */
              sr = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_SAFE_HZ);
              if (sr != CSR_RESULT_SUCCESS)
              {
                  unifi_error(card->ospriv, "CsrSdioMaxBusClockFrequencySet() failed2 %d\n", sr);
              }
              card->sdio_clock_speed = UNIFI_SDIO_CLOCK_SAFE_HZ;
  
              if (i == 1)
              {
                  continue;
              }
  
              /* Perform a s/w reset to preserve as much as the card state as possible,
               * (mainly the preserve RAM). The context will be lost for coredump - but as we
               * were unable to access the WLAN function for panic, the coredump would have
               * also failed without a reset.
               */
              unifi_info(card->ospriv, "Try s/w reset\n");
  
              r = unifi_card_hard_reset(card);
              if (r != CSR_RESULT_SUCCESS)
              {
                  unifi_error(card->ospriv, "unifi_card_hard_reset() failed %d\n", r);
              }
          }
          else
          {
              if (i > 0)
              {
                  unifi_info(card->ospriv, "Read chip version 0x%x after %d retries\n", data_u16, i);
              }
              break;
          }
      }
  
      r = ConvertCsrSdioToCsrHipResult(card, sr);
      func_exit_r(r);
      return r;
  }
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_read_panic
   *      Reads, saves and prints panic codes stored by the firmware in UniFi's
   *      preserve RAM by the last panic that occurred since chip was powered.
   *      Nothing is saved if the panic codes are read as zero.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *
   *  Returns:
   * ---------------------------------------------------------------------------
   */
  void unifi_read_panic(card_t *card)
  {
      CsrResult r;
      u16 p_code, p_arg;
  
      func_enter();
  
      /* The firmware must have previously initialised to read the panic addresses
       * from the SLUT
       */
      if (!card->panic_data_phy_addr || !card->panic_data_mac_addr)
      {
          return;
      }
  
      /* Get the panic data from PHY */
      r = unifi_card_read16(card, card->panic_data_phy_addr, &p_code);
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "capture_panic: unifi_read16 %08x failed %d\n", card->panic_data_phy_addr, r);
          p_code = 0;
      }
      if (p_code)
      {
          r = unifi_card_read16(card, card->panic_data_phy_addr + 2, &p_arg);
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_error(card->ospriv, "capture_panic: unifi_read16 %08x failed %d\n", card->panic_data_phy_addr + 2, r);
          }
          unifi_error(card->ospriv, "Last UniFi PHY PANIC %04x arg %04x\n", p_code, p_arg);
          card->last_phy_panic_code = p_code;
          card->last_phy_panic_arg = p_arg;
      }
  
      /* Get the panic data from MAC */
      r = unifi_card_read16(card, card->panic_data_mac_addr, &p_code);
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "capture_panic: unifi_read16 %08x failed %d\n", card->panic_data_mac_addr, r);
          p_code = 0;
      }
      if (p_code)
      {
          r = unifi_card_read16(card, card->panic_data_mac_addr + 2, &p_arg);
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_error(card->ospriv, "capture_panic: unifi_read16 %08x failed %d\n", card->panic_data_mac_addr + 2, r);
          }
          unifi_error(card->ospriv, "Last UniFi MAC PANIC %04x arg %04x\n", p_code, p_arg);
          card->last_mac_panic_code = p_code;
          card->last_mac_panic_arg = p_arg;
      }
  
      func_exit();
  }
  
  
  /*
   * ---------------------------------------------------------------------------
   *  card_allocate_memory_resources
   *
   *      Allocates memory for the from-host, to-host bulk data slots,
   *      soft queue buffers and bulk data buffers.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS on success, CSR error code on failure.
   * ---------------------------------------------------------------------------
   */
  static CsrResult card_allocate_memory_resources(card_t *card)
  {
      s16 n, i, k, r;
      sdio_config_data_t *cfg_data;
  
      func_enter();
  
      /* Reset any state carried forward from a previous life */
      card->fh_command_queue.q_rd_ptr = 0;
      card->fh_command_queue.q_wr_ptr = 0;
      (void)CsrSnprintf(card->fh_command_queue.name, UNIFI_QUEUE_NAME_MAX_LENGTH,
                        "fh_cmd_q");
      for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
      {
          card->fh_traffic_queue[i].q_rd_ptr = 0;
          card->fh_traffic_queue[i].q_wr_ptr = 0;
          (void)CsrSnprintf(card->fh_traffic_queue[i].name,
                            UNIFI_QUEUE_NAME_MAX_LENGTH, "fh_data_q%d", i);
      }
  #ifndef CSR_WIFI_HIP_TA_DISABLE
      unifi_ta_sampling_init(card);
  #endif
      /* Convenience short-cut */
      cfg_data = &card->config_data;
  
      /*
       * Allocate memory for the from-host and to-host signal buffers.
       */
      card->fh_buffer.buf = kmalloc(UNIFI_FH_BUF_SIZE, GFP_KERNEL);
      if (card->fh_buffer.buf == NULL)
      {
          unifi_error(card->ospriv, "Failed to allocate memory for F-H signals\n");
          func_exit_r(CSR_WIFI_HIP_RESULT_NO_MEMORY);
          return CSR_WIFI_HIP_RESULT_NO_MEMORY;
      }
      card->fh_buffer.bufsize = UNIFI_FH_BUF_SIZE;
      card->fh_buffer.ptr = card->fh_buffer.buf;
      card->fh_buffer.count = 0;
  
      card->th_buffer.buf = kmalloc(UNIFI_FH_BUF_SIZE, GFP_KERNEL);
      if (card->th_buffer.buf == NULL)
      {
          unifi_error(card->ospriv, "Failed to allocate memory for T-H signals\n");
          func_exit_r(CSR_WIFI_HIP_RESULT_NO_MEMORY);
          return CSR_WIFI_HIP_RESULT_NO_MEMORY;
      }
      card->th_buffer.bufsize = UNIFI_FH_BUF_SIZE;
      card->th_buffer.ptr = card->th_buffer.buf;
      card->th_buffer.count = 0;
  
  
      /*
       * Allocate memory for the from-host and to-host bulk data slots.
       * This is done as separate kmallocs because lots of smaller
       * allocations are more likely to succeed than one huge one.
       */
  
      /* Allocate memory for the array of pointers */
      n = cfg_data->num_fromhost_data_slots;
  
      unifi_trace(card->ospriv, UDBG3, "Alloc from-host resources, %d slots.\n", n);
      card->from_host_data = kmalloc(n * sizeof(slot_desc_t), GFP_KERNEL);
      if (card->from_host_data == NULL)
      {
          unifi_error(card->ospriv, "Failed to allocate memory for F-H bulk data array\n");
          func_exit_r(CSR_WIFI_HIP_RESULT_NO_MEMORY);
          return CSR_WIFI_HIP_RESULT_NO_MEMORY;
      }
  
      /* Initialise from-host bulk data slots */
      for (i = 0; i < n; i++)
      {
          UNIFI_INIT_BULK_DATA(&card->from_host_data[i].bd);
      }
  
      /* Allocate memory for the array used for slot host tag mapping */
      card->fh_slot_host_tag_record = kmalloc(n * sizeof(u32), GFP_KERNEL);
  
      if (card->fh_slot_host_tag_record == NULL)
      {
          unifi_error(card->ospriv, "Failed to allocate memory for F-H slot host tag mapping array\n");
          func_exit_r(CSR_WIFI_HIP_RESULT_NO_MEMORY);
          return CSR_WIFI_HIP_RESULT_NO_MEMORY;
      }
  
      /* Initialise host tag entries for from-host bulk data slots */
      for (i = 0; i < n; i++)
      {
          card->fh_slot_host_tag_record[i] = CSR_WIFI_HIP_RESERVED_HOST_TAG;
      }
  
  
      /* Allocate memory for the array of pointers */
      n = cfg_data->num_tohost_data_slots;
  
      unifi_trace(card->ospriv, UDBG3, "Alloc to-host resources, %d slots.\n", n);
      card->to_host_data = kmalloc(n * sizeof(bulk_data_desc_t), GFP_KERNEL);
      if (card->to_host_data == NULL)
      {
          unifi_error(card->ospriv, "Failed to allocate memory for T-H bulk data array\n");
          func_exit_r(CSR_WIFI_HIP_RESULT_NO_MEMORY);
          return CSR_WIFI_HIP_RESULT_NO_MEMORY;
      }
  
      /* Initialise to-host bulk data slots */
      for (i = 0; i < n; i++)
      {
          UNIFI_INIT_BULK_DATA(&card->to_host_data[i]);
      }
  
      /*
       * Initialise buffers for soft Q
       */
      for (i = 0; i < UNIFI_SOFT_COMMAND_Q_LENGTH; i++)
      {
          for (r = 0; r < UNIFI_MAX_DATA_REFERENCES; r++)
          {
              UNIFI_INIT_BULK_DATA(&card->fh_command_q_body[i].bulkdata[r]);
          }
      }
  
      for (k = 0; k < UNIFI_NO_OF_TX_QS; k++)
      {
          for (i = 0; i < UNIFI_SOFT_TRAFFIC_Q_LENGTH; i++)
          {
              for (r = 0; r < UNIFI_MAX_DATA_REFERENCES; r++)
              {
                  UNIFI_INIT_BULK_DATA(&card->fh_traffic_q_body[k][i].bulkdata[r]);
              }
          }
      }
  
      card->memory_resources_allocated = 1;
  
      func_exit();
      return CSR_RESULT_SUCCESS;
  } /* card_allocate_memory_resources() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_free_bulk_data
   *
   *      Free the data associated to a bulk data structure.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *      bulk_data_slot  Pointer to bulk data structure
   *
   *  Returns:
   *      None.
   *
   * ---------------------------------------------------------------------------
   */
  static void unifi_free_bulk_data(card_t *card, bulk_data_desc_t *bulk_data_slot)
  {
      if (bulk_data_slot->data_length != 0)
      {
          unifi_net_data_free(card->ospriv, bulk_data_slot);
      }
  } /* unifi_free_bulk_data() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  card_free_memory_resources
   *
   *      Frees memory allocated for the from-host, to-host bulk data slots,
   *      soft queue buffers and bulk data buffers.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *
   *  Returns:
   *      None.
   * ---------------------------------------------------------------------------
   */
  static void card_free_memory_resources(card_t *card)
  {
      func_enter();
  
      unifi_trace(card->ospriv, UDBG1, "Freeing card memory resources.\n");
  
      /* Clear our internal queues */
      unifi_cancel_pending_signals(card);
  
  
      kfree(card->to_host_data);
      card->to_host_data = NULL;
  
      kfree(card->from_host_data);
      card->from_host_data = NULL;
  
      /* free the memory for slot host tag mapping array */
      kfree(card->fh_slot_host_tag_record);
      card->fh_slot_host_tag_record = NULL;
  
      kfree(card->fh_buffer.buf);
      card->fh_buffer.ptr = card->fh_buffer.buf = NULL;
      card->fh_buffer.bufsize = 0;
      card->fh_buffer.count = 0;
  
      kfree(card->th_buffer.buf);
      card->th_buffer.ptr = card->th_buffer.buf = NULL;
      card->th_buffer.bufsize = 0;
      card->th_buffer.count = 0;
  
  
      card->memory_resources_allocated = 0;
  
      func_exit();
  } /* card_free_memory_resources() */
  
  
  static void card_init_soft_queues(card_t *card)
  {
      s16 i;
  
      func_enter();
  
      unifi_trace(card->ospriv, UDBG1, "Initialising internal signal queues.\n");
      /* Reset any state carried forward from a previous life */
      card->fh_command_queue.q_rd_ptr = 0;
      card->fh_command_queue.q_wr_ptr = 0;
      (void)CsrSnprintf(card->fh_command_queue.name, UNIFI_QUEUE_NAME_MAX_LENGTH,
                        "fh_cmd_q");
      for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
      {
          card->fh_traffic_queue[i].q_rd_ptr = 0;
          card->fh_traffic_queue[i].q_wr_ptr = 0;
          (void)CsrSnprintf(card->fh_traffic_queue[i].name,
                            UNIFI_QUEUE_NAME_MAX_LENGTH, "fh_data_q%d", i);
      }
  #ifndef CSR_WIFI_HIP_TA_DISABLE
      unifi_ta_sampling_init(card);
  #endif
      func_exit();
  }
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_cancel_pending_signals
   *
   *      Free the signals and associated bulk data, pending in the core.
   *
   *  Arguments:
   *      card        Pointer to card struct
   *
   *  Returns:
   *      None.
   * ---------------------------------------------------------------------------
   */
  void unifi_cancel_pending_signals(card_t *card)
  {
      s16 i, n, r;
      func_enter();
  
      unifi_trace(card->ospriv, UDBG1, "Canceling pending signals.\n");
  
      if (card->to_host_data)
      {
          /*
           * Free any bulk data buffers allocated for the t-h slots
           * This will clear all buffers that did not make it to
           * unifi_receive_event() before cancel was request.
           */
          n = card->config_data.num_tohost_data_slots;
          unifi_trace(card->ospriv, UDBG3, "Freeing to-host resources, %d slots.\n", n);
          for (i = 0; i < n; i++)
          {
              unifi_free_bulk_data(card, &card->to_host_data[i]);
          }
      }
  
      /*
       * If any of the from-host bulk data has reached the card->from_host_data
       * but not UniFi, we need to free the buffers here.
       */
      if (card->from_host_data)
      {
          /* Free any bulk data buffers allocated for the f-h slots */
          n = card->config_data.num_fromhost_data_slots;
          unifi_trace(card->ospriv, UDBG3, "Freeing from-host resources, %d slots.\n", n);
          for (i = 0; i < n; i++)
          {
              unifi_free_bulk_data(card, &card->from_host_data[i].bd);
          }
  
          for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
          {
              card->dynamic_slot_data.from_host_used_slots[i] = 0;
              card->dynamic_slot_data.from_host_max_slots[i] = 0;
              card->dynamic_slot_data.from_host_reserved_slots[i] = 0;
          }
      }
  
      /*
       * Free any bulk data buffers allocated in the soft queues.
       * This covers the case where a bulk data pointer has reached the soft queue
       * but not the card->from_host_data.
       */
      unifi_trace(card->ospriv, UDBG3, "Freeing cmd q resources.\n");
      for (i = 0; i < UNIFI_SOFT_COMMAND_Q_LENGTH; i++)
      {
          for (r = 0; r < UNIFI_MAX_DATA_REFERENCES; r++)
          {
              unifi_free_bulk_data(card, &card->fh_command_q_body[i].bulkdata[r]);
          }
      }
  
      unifi_trace(card->ospriv, UDBG3, "Freeing traffic q resources.\n");
      for (n = 0; n < UNIFI_NO_OF_TX_QS; n++)
      {
          for (i = 0; i < UNIFI_SOFT_TRAFFIC_Q_LENGTH; i++)
          {
              for (r = 0; r < UNIFI_MAX_DATA_REFERENCES; r++)
              {
                  unifi_free_bulk_data(card, &card->fh_traffic_q_body[n][i].bulkdata[r]);
              }
          }
      }
  
      card_init_soft_queues(card);
  
      func_exit();
  } /* unifi_cancel_pending_signals() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_free_card
   *
   *      Free the memory allocated for the card structure and buffers.
   *
   *  Notes:
   *      The porting layer is responsible for freeing any mini-coredump buffers
   *      allocated when it called unifi_coredump_init(), by calling
   *      unifi_coredump_free() before calling this function.
   *
   *  Arguments:
   *      card        Pointer to card struct
   *
   *  Returns:
   *      None.
   * ---------------------------------------------------------------------------
   */
  void unifi_free_card(card_t *card)
  {
      func_enter();
  #ifdef CSR_PRE_ALLOC_NET_DATA
      prealloc_netdata_free(card);
  #endif
      /* Free any memory allocated. */
      card_free_memory_resources(card);
  
      /* Warn if caller didn't free coredump buffers */
      if (card->dump_buf)
      {
          unifi_error(card->ospriv, "Caller should call unifi_coredump_free()\n");
          unifi_coredump_free(card); /* free anyway to prevent memory leak */
      }
  
      kfree(card);
  
      func_exit();
  } /* unifi_free_card() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  card_init_slots
   *
   *      Allocate memory for host-side slot data and signal queues.
   *
   * Arguments:
   *      card            Pointer to card object
   *
   * Returns:
   *      CSR error code.
   * ---------------------------------------------------------------------------
   */
  static CsrResult card_init_slots(card_t *card)
  {
      CsrResult r;
      u8 i;
  
      func_enter();
  
      /* Allocate the buffers we need, only once. */
      if (card->memory_resources_allocated == 1)
      {
          card_free_memory_resources(card);
      }
      else
      {
          /* Initialise our internal command and traffic queues */
          card_init_soft_queues(card);
      }
  
      r = card_allocate_memory_resources(card);
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to allocate card memory resources.\n");
          card_free_memory_resources(card);
          func_exit_r(r);
          return r;
      }
  
      if (card->sdio_ctrl_addr == 0)
      {
          unifi_error(card->ospriv, "Failed to find config struct!\n");
          func_exit_r(CSR_WIFI_HIP_RESULT_INVALID_VALUE);
          return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
      }
  
      /*
       * Set initial counts.
       */
  
      card->from_host_data_head = 0;
  
      /* Get initial signal counts from UniFi, in case it has not been reset. */
      {
          u16 s;
  
          /* Get the from-host-signals-written count */
          r = unifi_card_read16(card, card->sdio_ctrl_addr + 0, &s);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_error(card->ospriv, "Failed to read from-host sig written count\n");
              func_exit_r(r);
              return r;
          }
          card->from_host_signals_w = (s16)s;
  
          /* Get the to-host-signals-written count */
          r = unifi_card_read16(card, card->sdio_ctrl_addr + 6, &s);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_error(card->ospriv, "Failed to read to-host sig read count\n");
              func_exit_r(r);
              return r;
          }
          card->to_host_signals_r = (s16)s;
      }
  
      /* Set Initialised flag. */
      r = unifi_card_write16(card, card->init_flag_addr, 0x0001);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to write initialised flag\n");
          func_exit_r(r);
          return r;
      }
  
      /* Dynamic queue reservation */
      memset(&card->dynamic_slot_data, 0, sizeof(card_dynamic_slot_t));
  
      for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
      {
          card->dynamic_slot_data.from_host_max_slots[i] = card->config_data.num_fromhost_data_slots -
                                                           UNIFI_RESERVED_COMMAND_SLOTS;
          card->dynamic_slot_data.queue_stable[i] = FALSE;
      }
  
      card->dynamic_slot_data.packets_interval = UNIFI_PACKETS_INTERVAL;
  
      func_exit();
      return CSR_RESULT_SUCCESS;
  } /* card_init_slots() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_set_udi_hook
   *
   *      Registers the udi hook that reports the sent signals to the core.
   *
   *  Arguments:
   *      card            Pointer to the card context struct
   *      udi_fn          Pointer to the callback function.
   *
   *  Returns:
   *      CSR_WIFI_HIP_RESULT_INVALID_VALUE if the card pointer is invalid,
   *      CSR_RESULT_SUCCESS on success.
   * ---------------------------------------------------------------------------
   */
  CsrResult unifi_set_udi_hook(card_t *card, udi_func_t udi_fn)
  {
      if (card == NULL)
      {
          return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
      }
  
      if (card->udi_hook == NULL)
      {
          card->udi_hook = udi_fn;
      }
  
      return CSR_RESULT_SUCCESS;
  } /* unifi_set_udi_hook() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_remove_udi_hook
   *
   *      Removes the udi hook that reports the sent signals from the core.
   *
   *  Arguments:
   *      card            Pointer to the card context struct
   *      udi_fn          Pointer to the callback function.
   *
   *  Returns:
   *      CSR_WIFI_HIP_RESULT_INVALID_VALUE if the card pointer is invalid,
   *      CSR_RESULT_SUCCESS on success.
   * ---------------------------------------------------------------------------
   */
  CsrResult unifi_remove_udi_hook(card_t *card, udi_func_t udi_fn)
  {
      if (card == NULL)
      {
          return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
      }
  
      if (card->udi_hook == udi_fn)
      {
          card->udi_hook = NULL;
      }
  
      return CSR_RESULT_SUCCESS;
  } /* unifi_remove_udi_hook() */
  
  
  static void CardReassignDynamicReservation(card_t *card)
  {
      u8 i;
  
      func_enter();
  
      unifi_trace(card->ospriv, UDBG5, "Packets Txed %d %d %d %d\n",
                  card->dynamic_slot_data.packets_txed[0],
                  card->dynamic_slot_data.packets_txed[1],
                  card->dynamic_slot_data.packets_txed[2],
                  card->dynamic_slot_data.packets_txed[3]);
  
      /* Clear reservation and recalculate max slots */
      for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
      {
          card->dynamic_slot_data.queue_stable[i] = FALSE;
          card->dynamic_slot_data.from_host_reserved_slots[i] = 0;
          card->dynamic_slot_data.from_host_max_slots[i] = card->config_data.num_fromhost_data_slots -
                                                           UNIFI_RESERVED_COMMAND_SLOTS;
          card->dynamic_slot_data.packets_txed[i] = 0;
  
          unifi_trace(card->ospriv, UDBG5, "CardReassignDynamicReservation: queue %d reserved %d Max %d\n", i,
                      card->dynamic_slot_data.from_host_reserved_slots[i],
                      card->dynamic_slot_data.from_host_max_slots[i]);
      }
  
      card->dynamic_slot_data.total_packets_txed = 0;
      func_exit();
  }
  
  
  /* Algorithm to dynamically reserve slots. The logic is based mainly on the outstanding queue
   * length. Slots are reserved for particular queues during an interval and cleared after the interval.
   * Each queue has three associated variables.. a) used slots - the number of slots currently occupied
   * by the queue b) reserved slots - number of slots reserved specifically for the queue c) max slots - total
   * slots that this queue can actually use (may be higher than reserved slots and is dependent on reserved slots
   * for other queues).
   * This function is called when there are no slots available for a queue. It checks to see if there are enough
   * unreserved slots sufficient for this request. If available these slots are reserved for the queue.
   * If there are not enough unreserved slots, a fair share for each queue is calculated based on the total slots
   * and the number of active queues (any queue with existing reservation is considered active). Queues needing
   * less than their fair share are allowed to have the previously reserved slots. The remaining slots are
   * distributed evenly among queues that need more than the fair share
   *
   * A better scheme would take current bandwidth per AC into consideration when reserving slots. An
   * implementation scheme could consider the relative time/service period for slots in an AC. If the firmware
   * services other ACs faster than a particular AC (packets wait in the slots longer) then it is fair to reserve
   * less slots for the AC
   */
  static void CardCheckDynamicReservation(card_t *card, unifi_TrafficQueue queue)
  {
      u16 q_len, active_queues = 0, excess_queue_slots, div_extra_slots,
                queue_fair_share, reserved_slots = 0, q, excess_need_queues = 0, unmovable_slots = 0;
      s32 i;
      q_t *sigq;
      u16 num_data_slots = card->config_data.num_fromhost_data_slots - UNIFI_RESERVED_COMMAND_SLOTS;
  
      func_enter();
  
      /* Calculate the pending queue length */
      sigq = &card->fh_traffic_queue[queue];
      q_len = CSR_WIFI_HIP_Q_SLOTS_USED(sigq);
  
      if (q_len <= card->dynamic_slot_data.from_host_reserved_slots[queue])
      {
          unifi_trace(card->ospriv, UDBG5, "queue %d q_len %d already has that many reserved slots, exiting\n", queue, q_len);
          func_exit();
          return;
      }
  
      /* Upper limit */
      if (q_len > num_data_slots)
      {
          q_len = num_data_slots;
      }
  
      for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
      {
          if (i != (s32)queue)
          {
              reserved_slots += card->dynamic_slot_data.from_host_reserved_slots[i];
          }
          if ((i == (s32)queue) || (card->dynamic_slot_data.from_host_reserved_slots[i] > 0))
          {
              active_queues++;
          }
      }
  
      unifi_trace(card->ospriv, UDBG5, "CardCheckDynamicReservation: queue %d q_len %d\n", queue, q_len);
      unifi_trace(card->ospriv, UDBG5, "Active queues %d reserved slots on other queues %d\n",
                  active_queues, reserved_slots);
  
      if (reserved_slots + q_len <= num_data_slots)
      {
          card->dynamic_slot_data.from_host_reserved_slots[queue] = q_len;
          if (q_len == num_data_slots)
          {
              /* This is the common case when just 1 stream is going */
              card->dynamic_slot_data.queue_stable[queue] = TRUE;
          }
      }
      else
      {
          queue_fair_share = num_data_slots / active_queues;
          unifi_trace(card->ospriv, UDBG5, "queue fair share %d\n", queue_fair_share);
  
          /* Evenly distribute slots among active queues */
          /* Find out the queues that need excess of fair share. Also find slots allocated
           * to queues less than their fair share, these slots cannot be reallocated (unmovable slots) */
  
          card->dynamic_slot_data.from_host_reserved_slots[queue] = q_len;
  
          for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
          {
              if (card->dynamic_slot_data.from_host_reserved_slots[i] > queue_fair_share)
              {
                  excess_need_queues++;
              }
              else
              {
                  unmovable_slots += card->dynamic_slot_data.from_host_reserved_slots[i];
              }
          }
  
          unifi_trace(card->ospriv, UDBG5, "Excess need queues %d\n", excess_need_queues);
  
          /* Now find the slots per excess demand queue */
          excess_queue_slots = (num_data_slots - unmovable_slots) / excess_need_queues;
          div_extra_slots = (num_data_slots - unmovable_slots) - excess_queue_slots * excess_need_queues;
          for (i = UNIFI_NO_OF_TX_QS - 1; i >= 0; i--)
          {
              if (card->dynamic_slot_data.from_host_reserved_slots[i] > excess_queue_slots)
              {
                  card->dynamic_slot_data.from_host_reserved_slots[i] = excess_queue_slots;
                  if (div_extra_slots > 0)
                  {
                      card->dynamic_slot_data.from_host_reserved_slots[i]++;
                      div_extra_slots--;
                  }
                  /* No more slots will be allocated to this queue during the current interval */
                  card->dynamic_slot_data.queue_stable[i] = TRUE;
                  unifi_trace(card->ospriv, UDBG5, "queue stable %d\n", i);
              }
          }
      }
  
      /* Redistribute max slots */
      for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
      {
          reserved_slots = 0;
          for (q = 0; q < UNIFI_NO_OF_TX_QS; q++)
          {
              if (i != q)
              {
                  reserved_slots += card->dynamic_slot_data.from_host_reserved_slots[q];
              }
          }
  
          card->dynamic_slot_data.from_host_max_slots[i] = num_data_slots - reserved_slots;
          unifi_trace(card->ospriv, UDBG5, "queue %d reserved %d Max %d\n", i,
                      card->dynamic_slot_data.from_host_reserved_slots[i],
                      card->dynamic_slot_data.from_host_max_slots[i]);
      }
  
      func_exit();
  }
  
  
  /*
   * ---------------------------------------------------------------------------
   *  CardClearFromHostDataSlot
   *
   *      Clear a the given data slot, making it available again.
   *
   *  Arguments:
   *      card            Pointer to Card object
   *      slot            Index of the signal slot to clear.
   *
   *  Returns:
   *      None.
   * ---------------------------------------------------------------------------
   */
  void CardClearFromHostDataSlot(card_t *card, const s16 slot)
  {
      u8 queue = card->from_host_data[slot].queue;
      const void *os_data_ptr = card->from_host_data[slot].bd.os_data_ptr;
  
      func_enter();
  
      if (card->from_host_data[slot].bd.data_length == 0)
      {
          unifi_warning(card->ospriv,
                        "Surprise: request to clear an already free FH data slot: %d\n",
                        slot);
          func_exit();
          return;
      }
  
      if (os_data_ptr == NULL)
      {
          unifi_warning(card->ospriv,
                        "Clearing FH data slot %d: has null payload, len=%d\n",
                        slot, card->from_host_data[slot].bd.data_length);
      }
  
      /* Free card->from_host_data[slot].bd.os_net_ptr here. */
      /* Mark slot as free by setting length to 0. */
      unifi_free_bulk_data(card, &card->from_host_data[slot].bd);
      if (queue < UNIFI_NO_OF_TX_QS)
      {
          if (card->dynamic_slot_data.from_host_used_slots[queue] == 0)
          {
              unifi_error(card->ospriv, "Goofed up used slots q = %d used slots = %d\n",
                          queue,
                          card->dynamic_slot_data.from_host_used_slots[queue]);
          }
          else
          {
              card->dynamic_slot_data.from_host_used_slots[queue]--;
          }
          card->dynamic_slot_data.packets_txed[queue]++;
          card->dynamic_slot_data.total_packets_txed++;
          if (card->dynamic_slot_data.total_packets_txed >= card->dynamic_slot_data.packets_interval)
          {
              CardReassignDynamicReservation(card);
          }
      }
  
      unifi_trace(card->ospriv, UDBG4, "CardClearFromHostDataSlot: slot %d recycled %p\n", slot, os_data_ptr);
  
      func_exit();
  } /* CardClearFromHostDataSlot() */
  
  
  #ifdef CSR_WIFI_REQUEUE_PACKET_TO_HAL
  /*
   * ---------------------------------------------------------------------------
   *  CardClearFromHostDataSlotWithoutFreeingBulkData
   *
   *      Clear the given data slot with out freeing the bulk data.
   *
   *  Arguments:
   *      card            Pointer to Card object
   *      slot            Index of the signal slot to clear.
   *
   *  Returns:
   *      None.
   * ---------------------------------------------------------------------------
   */
  void CardClearFromHostDataSlotWithoutFreeingBulkData(card_t *card, const s16 slot)
  {
      u8 queue = card->from_host_data[slot].queue;
  
      /* Initialise the from_host data slot so it can be re-used,
       * Set length field in from_host_data array to 0.
       */
      UNIFI_INIT_BULK_DATA(&card->from_host_data[slot].bd);
  
      queue = card->from_host_data[slot].queue;
  
      if (queue < UNIFI_NO_OF_TX_QS)
      {
          if (card->dynamic_slot_data.from_host_used_slots[queue] == 0)
          {
              unifi_error(card->ospriv, "Goofed up used slots q = %d used slots = %d\n",
                          queue,
                          card->dynamic_slot_data.from_host_used_slots[queue]);
          }
          else
          {
              card->dynamic_slot_data.from_host_used_slots[queue]--;
          }
          card->dynamic_slot_data.packets_txed[queue]++;
          card->dynamic_slot_data.total_packets_txed++;
          if (card->dynamic_slot_data.total_packets_txed >=
              card->dynamic_slot_data.packets_interval)
          {
              CardReassignDynamicReservation(card);
          }
      }
  } /* CardClearFromHostDataSlotWithoutFreeingBulkData() */
  
  
  #endif
  
  u16 CardGetDataSlotSize(card_t *card)
  {
      return card->config_data.data_slot_size;
  } /* CardGetDataSlotSize() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  CardGetFreeFromHostDataSlots
   *
   *      Retrieve the number of from-host bulk data slots available.
   *
   *  Arguments:
   *      card            Pointer to the card context struct
   *
   *  Returns:
   *      Number of free from-host bulk data slots.
   * ---------------------------------------------------------------------------
   */
  u16 CardGetFreeFromHostDataSlots(card_t *card)
  {
      u16 i, n = 0;
  
      func_enter();
  
      /* First two slots reserved for MLME */
      for (i = 0; i < card->config_data.num_fromhost_data_slots; i++)
      {
          if (card->from_host_data[i].bd.data_length == 0)
          {
              /* Free slot */
              n++;
          }
      }
  
      func_exit();
      return n;
  } /* CardGetFreeFromHostDataSlots() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  CardAreAllFromHostDataSlotsEmpty
   *
   *      Returns the state of from-host bulk data slots.
   *
   *  Arguments:
   *      card            Pointer to the card context struct
   *
   *  Returns:
   *      1       The from-host bulk data slots are all empty (available).
   *      0       Some or all the from-host bulk data slots are in use.
   * ---------------------------------------------------------------------------
   */
  u16 CardAreAllFromHostDataSlotsEmpty(card_t *card)
  {
      u16 i;
  
      for (i = 0; i < card->config_data.num_fromhost_data_slots; i++)
      {
          if (card->from_host_data[i].bd.data_length != 0)
          {
              return 0;
          }
      }
  
      return 1;
  } /* CardGetFreeFromHostDataSlots() */
  
  
  static CsrResult unifi_identify_hw(card_t *card)
  {
      func_enter();
  
      card->chip_id = card->sdio_if->sdioId.cardId;
      card->function = card->sdio_if->sdioId.sdioFunction;
      card->sdio_io_block_size = card->sdio_if->blockSize;
  
      /* If SDIO controller doesn't support byte mode CMD53, pad transfers to block sizes */
      card->sdio_io_block_pad = (card->sdio_if->features & CSR_SDIO_FEATURE_BYTE_MODE)?FALSE : TRUE;
  
      /*
       * Setup the chip helper so that we can access the registers (and
       * also tell what sub-type of HIP we should use).
       */
      card->helper = ChipHelper_GetVersionSdio((u8)card->chip_id);
      if (!card->helper)
      {
          unifi_error(card->ospriv, "Null ChipHelper\n");
      }
  
      unifi_info(card->ospriv, "Chip ID 0x%02X  Function %u  Block Size %u  Name %s(%s)\n",
                 card->chip_id, card->function, card->sdio_io_block_size,
                 ChipHelper_MarketingName(card->helper),
                 ChipHelper_FriendlyName(card->helper));
  
      func_exit();
      return CSR_RESULT_SUCCESS;
  } /* unifi_identify_hw() */
  
  
  static CsrResult unifi_prepare_hw(card_t *card)
  {
      CsrResult r;
      CsrResult csrResult;
      enum unifi_host_state old_state = card->host_state;
  
      func_enter();
  
      r = unifi_identify_hw(card);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to identify hw\n");
          func_exit_r(r);
          return r;
      }
  
      unifi_trace(card->ospriv, UDBG1,
                  "%s mode SDIO\n", card->sdio_io_block_pad?"Block" : "Byte");
      /*
       * Chip must be a awake or blocks that are asleep may not get
       * reset.  We can only do this after we have read the chip_id.
       */
      r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
  
      if (old_state == UNIFI_HOST_STATE_TORPID)
      {
          /* Ensure the initial clock rate is set; if a reset occured when the chip was
           * TORPID, unifi_set_host_state() may have raised it to MAX.
           */
          csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_INIT_HZ);
          if (csrResult != CSR_RESULT_SUCCESS)
          {
              r = ConvertCsrSdioToCsrHipResult(card, csrResult);
              func_exit_r(r);
              return r;
          }
          card->sdio_clock_speed = UNIFI_SDIO_CLOCK_INIT_HZ;
      }
  
      /*
       * The WLAN function must be enabled to access MAILBOX2 and DEBUG_RST
       * registers.
       */
      csrResult = CsrSdioFunctionEnable(card->sdio_if);
      if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
      {
          return CSR_WIFI_HIP_RESULT_NO_DEVICE;
      }
      if (csrResult != CSR_RESULT_SUCCESS)
      {
          r = ConvertCsrSdioToCsrHipResult(card, csrResult);
          /* Can't enable WLAN function. Try resetting the SDIO block. */
          unifi_error(card->ospriv, "Failed to re-enable function %d.\n", card->function);
          func_exit_r(r);
          return r;
      }
  
      /*
       * Poke some registers to make sure the PLL has started,
       * otherwise memory accesses are likely to fail.
       */
      bootstrap_chip_hw(card);
  
      /* Try to read the chip version from register. */
      r = unifi_read_chip_version(card);
      if (r != CSR_RESULT_SUCCESS)
      {
          func_exit_r(r);
          return r;
      }
  
      func_exit();
      return CSR_RESULT_SUCCESS;
  } /* unifi_prepare_hw() */
  
  
  static CsrResult unifi_read_chip_version(card_t *card)
  {
      u32 gbl_chip_version;
      CsrResult r;
      u16 ver;
  
      func_enter();
  
      gbl_chip_version = ChipHelper_GBL_CHIP_VERSION(card->helper);
  
      /* Try to read the chip version from register. */
      if (gbl_chip_version != 0)
      {
          r = unifi_read_direct16(card, gbl_chip_version * 2, &ver);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_error(card->ospriv, "Failed to read GBL_CHIP_VERSION\n");
              func_exit_r(r);
              return r;
          }
          card->chip_version = ver;
      }
      else
      {
          unifi_info(card->ospriv, "Unknown Chip ID, cannot locate GBL_CHIP_VERSION\n");
          r = CSR_RESULT_FAILURE;
      }
  
      unifi_info(card->ospriv, "Chip Version 0x%04X\n", card->chip_version);
  
      func_exit_r(r);
      return r;
  } /* unifi_read_chip_version() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_reset_hardware
   *
   *      Execute the UniFi reset sequence.
   *
   *      Note: This may fail if the chip is going TORPID so retry at
   *      least once.
   *
   *  Arguments:
   *      card - pointer to card context structure
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS on success, CSR error otherwise.
   *
   *  Notes:
   *      Some platforms (e.g. Windows Vista) do not allow access to registers
   *      that are necessary for a software soft reset.
   * ---------------------------------------------------------------------------
   */
  static CsrResult unifi_reset_hardware(card_t *card)
  {
      CsrResult r;
      u16 new_block_size = UNIFI_IO_BLOCK_SIZE;
      CsrResult csrResult;
  
      func_enter();
  
      /* Errors returned by unifi_prepare_hw() are not critical at this point */
      r = unifi_prepare_hw(card);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
  
      /* First try SDIO controller reset, which may power cycle the UniFi, assert
       * its reset line, or not be implemented depending on the platform.
       */
      unifi_info(card->ospriv, "Calling CsrSdioHardReset\n");
      csrResult = CsrSdioHardReset(card->sdio_if);
      if (csrResult == CSR_RESULT_SUCCESS)
      {
          unifi_info(card->ospriv, "CsrSdioHardReset succeeded on reseting UniFi\n");
          r = unifi_prepare_hw(card);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_error(card->ospriv, "unifi_prepare_hw failed after hard reset\n");
              func_exit_r(r);
              return r;
          }
      }
      else if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
      {
          return CSR_WIFI_HIP_RESULT_NO_DEVICE;
      }
      else
      {
          /* Falling back to software hard reset methods */
          unifi_info(card->ospriv, "Falling back to software hard reset\n");
          r = unifi_card_hard_reset(card);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_error(card->ospriv, "software hard reset failed\n");
              func_exit_r(r);
              return r;
          }
  
          /* If we fell back to unifi_card_hard_reset() methods, chip version may
           * not have been read. (Note in the unlikely event that it is zero,
           * it will be harmlessly read again)
           */
          if (card->chip_version == 0)
          {
              r = unifi_read_chip_version(card);
              if (r != CSR_RESULT_SUCCESS)
              {
                  func_exit_r(r);
                  return r;
              }
          }
      }
  
  #ifdef CSR_WIFI_HIP_SDIO_BLOCK_SIZE
      new_block_size = CSR_WIFI_HIP_SDIO_BLOCK_SIZE;
  #endif
  
      /* After hard reset, we need to restore the SDIO block size */
      csrResult = CsrSdioBlockSizeSet(card->sdio_if, new_block_size);
      r = ConvertCsrSdioToCsrHipResult(card, csrResult);
  
      /* Warn if a different block size was achieved by the transport */
      if (card->sdio_if->blockSize != new_block_size)
      {
          unifi_info(card->ospriv,
                     "Actually got block size %d\n", card->sdio_if->blockSize);
      }
  
      /* sdio_io_block_size always needs be updated from the achieved block size,
       * as it is used by the OS layer to allocate memory in unifi_net_malloc().
       * Controllers which don't support block mode (e.g. CSPI) will report a
       * block size of zero.
       */
      if (card->sdio_if->blockSize == 0)
      {
          unifi_info(card->ospriv, "Block size 0, block mode not available\n");
  
          /* Set sdio_io_block_size to 1 so that unifi_net_data_malloc() has a
           * sensible rounding value. Elsewhere padding will already be
           * disabled because the controller supports byte mode.
           */
          card->sdio_io_block_size = 1;
  
          /* Controller features must declare support for byte mode */
          if (!(card->sdio_if->features & CSR_SDIO_FEATURE_BYTE_MODE))
          {
              unifi_error(card->ospriv, "Requires byte mode\n");
              r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
          }
      }
      else
      {
          /* Padding will be enabled if CSR_SDIO_FEATURE_BYTE_MODE isn't set */
          card->sdio_io_block_size = card->sdio_if->blockSize;
      }
  
  
      func_exit_r(r);
      return r;
  } /* unifi_reset_hardware() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  card_reset_method_io_enable
   *
   *      Issue a hard reset to the hw writing the IO_ENABLE.
   *
   *  Arguments:
   *      card            Pointer to Card object
   *
   *  Returns:
   *      0 on success,
   *      CSR_WIFI_HIP_RESULT_NO_DEVICE   if the card was ejected
   *      CSR_RESULT_FAILURE         if an SDIO error occurred or if a response
   *                                 was not seen in the expected time
   * ---------------------------------------------------------------------------
   */
  static CsrResult card_reset_method_io_enable(card_t *card)
  {
      CsrResult r;
      CsrResult csrResult;
  
      func_enter();
  
      /*
       * This resets only function 1, so should be used in
       * preference to the method below (CSR_FUNC_EN)
       */
      unifi_trace(card->ospriv, UDBG1, "Hard reset (IO_ENABLE)\n");
  
      csrResult = CsrSdioFunctionDisable(card->sdio_if);
      if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
      {
          return CSR_WIFI_HIP_RESULT_NO_DEVICE;
      }
      if (csrResult != CSR_RESULT_SUCCESS)
      {
          r = ConvertCsrSdioToCsrHipResult(card, csrResult);
          unifi_warning(card->ospriv, "SDIO error writing IO_ENABLE: %d\n", r);
      }
      else
      {
          /* Delay here to let the reset take affect. */
          CsrThreadSleep(RESET_SETTLE_DELAY);
  
          r = card_wait_for_unifi_to_disable(card);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
  
          if (r == CSR_RESULT_SUCCESS)
          {
              r = card_wait_for_unifi_to_reset(card);
              if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
              {
                  return r;
              }
          }
      }
  
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_trace(card->ospriv, UDBG1, "Hard reset (CSR_FUNC_EN)\n");
  
          r = sdio_write_f0(card, SDIO_CSR_FUNC_EN, 0);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_warning(card->ospriv, "SDIO error writing SDIO_CSR_FUNC_EN: %d\n", r);
              func_exit_r(r);
              return r;
          }
          else
          {
              /* Delay here to let the reset take affect. */
              CsrThreadSleep(RESET_SETTLE_DELAY);
  
              r = card_wait_for_unifi_to_reset(card);
              if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
              {
                  return r;
              }
          }
      }
  
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_warning(card->ospriv, "card_reset_method_io_enable failed to reset UniFi\n");
      }
  
      func_exit();
      return r;
  } /* card_reset_method_io_enable() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  card_reset_method_dbg_reset
   *
   *      Issue a hard reset to the hw writing the DBG_RESET.
   *
   *  Arguments:
   *      card            Pointer to Card object
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS         on success,
   *      CSR_WIFI_HIP_RESULT_NO_DEVICE   if the card was ejected
   *      CSR_RESULT_FAILURE         if an SDIO error occurred or if a response
   *                                 was not seen in the expected time
   * ---------------------------------------------------------------------------
   */
  static CsrResult card_reset_method_dbg_reset(card_t *card)
  {
      CsrResult r;
  
      func_enter();
  
      /*
       * Prepare UniFi for h/w reset
       */
      if (card->host_state == UNIFI_HOST_STATE_TORPID)
      {
          r = unifi_set_host_state(card, UNIFI_HOST_STATE_DROWSY);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_error(card->ospriv, "Failed to set UNIFI_HOST_STATE_DROWSY\n");
              func_exit_r(r);
              return r;
          }
          CsrThreadSleep(5);
      }
  
      r = unifi_card_stop_processor(card, UNIFI_PROC_BOTH);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Can't stop processors\n");
          func_exit();
          return r;
      }
  
      unifi_trace(card->ospriv, UDBG1, "Hard reset (DBG_RESET)\n");
  
      /*
       * This register write may fail. The debug reset resets
       * parts of the Function 0 sections of the chip, and
       * therefore the response cannot be sent back to the host.
       */
      r = unifi_write_direct_8_or_16(card, ChipHelper_DBG_RESET(card->helper) * 2, 1);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_warning(card->ospriv, "SDIO error writing DBG_RESET: %d\n", r);
          func_exit_r(r);
          return r;
      }
  
      /* Delay here to let the reset take affect. */
      CsrThreadSleep(RESET_SETTLE_DELAY);
  
      r = card_wait_for_unifi_to_reset(card);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_warning(card->ospriv, "card_reset_method_dbg_reset failed to reset UniFi\n");
      }
  
      func_exit();
      return r;
  } /* card_reset_method_dbg_reset() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_card_hard_reset
   *
   *      Issue reset to hardware, by writing to registers on the card.
   *      Power to the card is preserved.
   *
   *  Arguments:
   *      card            Pointer to Card object
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS         on success,
   *      CSR_WIFI_HIP_RESULT_NO_DEVICE   if the card was ejected
   *      CSR_RESULT_FAILURE         if an SDIO error occurred or if a response
   *                                 was not seen in the expected time
   * ---------------------------------------------------------------------------
   */
  CsrResult unifi_card_hard_reset(card_t *card)
  {
      CsrResult r;
      const struct chip_helper_reset_values *init_data;
      u32 chunks;
  
      func_enter();
  
      /* Clear cache of page registers */
      card->proc_select = (u32)(-1);
      card->dmem_page = (u32)(-1);
      card->pmem_page = (u32)(-1);
  
      /*
       * We need to have a valid card->helper before we use software hard reset.
       * If unifi_identify_hw() fails to get the card ID, it probably means
       * that there is no way to talk to the h/w.
       */
      r = unifi_identify_hw(card);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "unifi_card_hard_reset failed to identify h/w\n");
          func_exit();
          return r;
      }
  
      /* Search for some reset code. */
      chunks = ChipHelper_HostResetSequence(card->helper, &init_data);
      if (chunks != 0)
      {
          unifi_error(card->ospriv,
                      "Hard reset (Code download) is unsupported\n");
  
          func_exit_r(CSR_RESULT_FAILURE);
          return CSR_RESULT_FAILURE;
      }
  
      if (card->chip_id > SDIO_CARD_ID_UNIFI_2)
      {
          /* The HIP spec considers this a bus-specific reset.
           * This resets only function 1, so should be used in
           * preference to the method below (CSR_FUNC_EN)
           * If this method fails, it means that the f/w is probably
           * not running. In this case, try the DBG_RESET method.
           */
          r = card_reset_method_io_enable(card);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r == CSR_RESULT_SUCCESS)
          {
              func_exit();
              return r;
          }
      }
  
      /* Software hard reset */
      r = card_reset_method_dbg_reset(card);
  
      func_exit_r(r);
      return r;
  } /* unifi_card_hard_reset() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *
   *  CardGenInt
   *
   *      Prod the card.
   *      This function causes an internal interrupt to be raised in the
   *      UniFi chip. It is used to signal the firmware that some action has
   *      been completed.
   *      The UniFi Host Interface asks that the value used increments for
   *      debugging purposes.
   *
   *  Arguments:
   *      card            Pointer to Card object
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS         on success,
   *      CSR_WIFI_HIP_RESULT_NO_DEVICE   if the card was ejected
   *      CSR_RESULT_FAILURE         if an SDIO error occurred or if a response
   *                                 was not seen in the expected time
   * ---------------------------------------------------------------------------
   */
  CsrResult CardGenInt(card_t *card)
  {
      CsrResult r;
  
      func_enter();
  
      if (card->chip_id > SDIO_CARD_ID_UNIFI_2)
      {
          r = sdio_write_f0(card, SDIO_CSR_FROM_HOST_SCRATCH0,
                            (u8)card->unifi_interrupt_seq);
      }
      else
      {
          r = unifi_write_direct_8_or_16(card,
                                         ChipHelper_SHARED_IO_INTERRUPT(card->helper) * 2,
                                         (u8)card->unifi_interrupt_seq);
      }
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "SDIO error writing UNIFI_SHARED_IO_INTERRUPT: %d\n", r);
          func_exit_r(r);
          return r;
      }
  
      card->unifi_interrupt_seq++;
  
      func_exit();
      return CSR_RESULT_SUCCESS;
  } /* CardGenInt() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  CardEnableInt
   *
   *      Enable the outgoing SDIO interrupt from UniFi to the host.
   *
   *  Arguments:
   *      card            Pointer to Card object
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS            on success,
   *      CSR_WIFI_HIP_RESULT_NO_DEVICE      if the card was ejected
   *      CSR_RESULT_FAILURE            if an SDIO error occurred,
   * ---------------------------------------------------------------------------
   */
  CsrResult CardEnableInt(card_t *card)
  {
      CsrResult r;
      u8 int_enable;
  
      r = sdio_read_f0(card, SDIO_INT_ENABLE, &int_enable);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "SDIO error reading SDIO_INT_ENABLE\n");
          return r;
      }
  
      int_enable |= (1 << card->function) | UNIFI_SD_INT_ENABLE_IENM;
  
      r = sdio_write_f0(card, SDIO_INT_ENABLE, int_enable);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "SDIO error writing SDIO_INT_ENABLE\n");
          return r;
      }
  
      return CSR_RESULT_SUCCESS;
  } /* CardEnableInt() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  CardDisableInt
   *
   *      Disable the outgoing SDIO interrupt from UniFi to the host.
   *
   *  Arguments:
   *      card            Pointer to Card object
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS            on success,
   *      CSR_WIFI_HIP_RESULT_NO_DEVICE      if the card was ejected
   *      CSR_RESULT_FAILURE            if an SDIO error occurred,
   * ---------------------------------------------------------------------------
   */
  CsrResult CardDisableInt(card_t *card)
  {
      CsrResult r;
      u8 int_enable;
  
      r = sdio_read_f0(card, SDIO_INT_ENABLE, &int_enable);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "SDIO error reading SDIO_INT_ENABLE\n");
          return r;
      }
  
      int_enable &= ~(1 << card->function);
  
      r = sdio_write_f0(card, SDIO_INT_ENABLE, int_enable);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "SDIO error writing SDIO_INT_ENABLE\n");
          return r;
      }
  
      return CSR_RESULT_SUCCESS;
  } /* CardDisableInt() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  CardPendingInt
   *
   *      Determine whether UniFi is currently asserting the SDIO interrupt
   *      request.
   *
   *  Arguments:
   *      card            Pointer to Card object
   *      pintr           Pointer to location to write interrupt status,
   *                          TRUE if interrupt pending,
   *                          FALSE if no interrupt pending.
   *  Returns:
   *      CSR_RESULT_SUCCESS            interrupt status read successfully
   *      CSR_WIFI_HIP_RESULT_NO_DEVICE      if the card was ejected
   *      CSR_RESULT_FAILURE            if an SDIO error occurred,
   * ---------------------------------------------------------------------------
   */
  CsrResult CardPendingInt(card_t *card, u8 *pintr)
  {
      CsrResult r;
      u8 pending;
  
      *pintr = FALSE;
  
      r = sdio_read_f0(card, SDIO_INT_PENDING, &pending);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "SDIO error reading SDIO_INT_PENDING\n");
          return r;
      }
  
      *pintr = (pending & (1 << card->function))?TRUE : FALSE;
  
      return CSR_RESULT_SUCCESS;
  } /* CardPendingInt() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  CardClearInt
   *
   *      Clear the UniFi SDIO interrupt request.
   *
   *  Arguments:
   *      card            Pointer to Card object
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS          if pending interrupt was cleared, or no pending interrupt.
   *      CSR_WIFI_HIP_RESULT_NO_DEVICE    if the card was ejected
   *      CSR_RESULT_FAILURE          if an SDIO error occurred,
   * ---------------------------------------------------------------------------
   */
  CsrResult CardClearInt(card_t *card)
  {
      CsrResult r;
      u8 intr;
  
      if (card->chip_id > SDIO_CARD_ID_UNIFI_2)
      {
          /* CardPendingInt() sets intr, if there is a pending interrupt */
          r = CardPendingInt(card, &intr);
          if (intr == FALSE)
          {
              return r;
          }
  
          r = sdio_write_f0(card, SDIO_CSR_HOST_INT_CLEAR, 1);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_error(card->ospriv, "SDIO error writing SDIO_CSR_HOST_INT_CLEAR\n");
          }
      }
      else
      {
          r = unifi_write_direct_8_or_16(card,
                                         ChipHelper_SDIO_HOST_INT(card->helper) * 2,
                                         0);
          if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
          {
              return r;
          }
          if (r != CSR_RESULT_SUCCESS)
          {
              unifi_error(card->ospriv, "SDIO error writing UNIFI_SDIO_HOST_INT\n");
          }
      }
  
      return r;
  } /* CardClearInt() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  CardIntEnabled
   *
   *      Determine whether UniFi is currently asserting the SDIO interrupt
   *      request.
   *
   *  Arguments:
   *      card            Pointer to Card object
   *      enabled         Pointer to location to write interrupt enable status,
   *                          TRUE if interrupts enabled,
   *                          FALSE if interupts disabled.
   *
   *  Returns:
   *      CSR_WIFI_HIP_RESULT_NO_DEVICE      if the card was ejected
   *      CSR_RESULT_FAILURE            if an SDIO error occurred,
   * ---------------------------------------------------------------------------
   */
  CsrResult CardIntEnabled(card_t *card, u8 *enabled)
  {
      CsrResult r;
      u8 int_enable;
  
      r = sdio_read_f0(card, SDIO_INT_ENABLE, &int_enable);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "SDIO error reading SDIO_INT_ENABLE\n");
          return r;
      }
  
      *enabled = (int_enable & (1 << card->function))?TRUE : FALSE;
  
      return CSR_RESULT_SUCCESS;
  } /* CardIntEnabled() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  CardWriteBulkData
   *      Allocate slot in the pending bulkdata arrays and assign it to a signal's
   *      bulkdata reference. The slot is then ready for UniFi's bulkdata commands
   *      to transfer the data to/from the host.
   *
   *  Arguments:
   *      card            Pointer to Card object
   *      csptr           Pending signal pointer, including bulkdata ref
   *      queue           Traffic queue that this signal is using
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS if a free slot was assigned
   *      CSR_RESULT_FAILURE if no slot was available
   * ---------------------------------------------------------------------------
   */
  CsrResult CardWriteBulkData(card_t *card, card_signal_t *csptr, unifi_TrafficQueue queue)
  {
      u16 i, slots[UNIFI_MAX_DATA_REFERENCES], j = 0;
      u8 *packed_sigptr, num_slots_required = 0;
      bulk_data_desc_t *bulkdata = csptr->bulkdata;
      s16 h, nslots;
  
      func_enter();
  
      /* Count the number of slots required */
      for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++)
      {
          if (bulkdata[i].data_length != 0)
          {
              num_slots_required++;
          }
      }
  
      /* Get the slot numbers */
      if (num_slots_required != 0)
      {
          /* Last 2 slots for MLME */
          if (queue == UNIFI_TRAFFIC_Q_MLME)
          {
              h = card->config_data.num_fromhost_data_slots - UNIFI_RESERVED_COMMAND_SLOTS;
              for (i = 0; i < card->config_data.num_fromhost_data_slots; i++)
              {
                  if (card->from_host_data[h].bd.data_length == 0)
                  {
                      /* Free data slot, claim it */
                      slots[j++] = h;
                      if (j == num_slots_required)
                      {
                          break;
                      }
                  }
  
                  if (++h >= card->config_data.num_fromhost_data_slots)
                  {
                      h = 0;
                  }
              }
          }
          else
          {
              if (card->dynamic_slot_data.from_host_used_slots[queue]
                  < card->dynamic_slot_data.from_host_max_slots[queue])
              {
                  /* Data commands get a free slot only after a few checks */
                  nslots = card->config_data.num_fromhost_data_slots - UNIFI_RESERVED_COMMAND_SLOTS;
  
                  h = card->from_host_data_head;
  
                  for (i = 0; i < nslots; i++)
                  {
                      if (card->from_host_data[h].bd.data_length == 0)
                      {
                          /* Free data slot, claim it */
                          slots[j++] = h;
                          if (j == num_slots_required)
                          {
                              break;
                          }
                      }
  
                      if (++h >= nslots)
                      {
                          h = 0;
                      }
                  }
                  card->from_host_data_head = h;
              }
          }
  
          /* Required number of slots are not available, bail out */
          if (j != num_slots_required)
          {
              unifi_trace(card->ospriv, UDBG5, "CardWriteBulkData: didn't find free slot/s\n");
  
              /* If we haven't already reached the stable state we can ask for reservation */
              if ((queue != UNIFI_TRAFFIC_Q_MLME) && (card->dynamic_slot_data.queue_stable[queue] == FALSE))
              {
                  CardCheckDynamicReservation(card, queue);
              }
  
              for (i = 0; i < card->config_data.num_fromhost_data_slots; i++)
              {
                  unifi_trace(card->ospriv, UDBG5, "fh data slot %d: %d\n", i, card->from_host_data[i].bd.data_length);
              }
              func_exit();
              return CSR_RESULT_FAILURE;
          }
      }
  
      packed_sigptr = csptr->sigbuf;
  
      /* Fill in the slots with data */
      j = 0;
      for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++)
      {
          if (bulkdata[i].data_length == 0)
          {
              /* Zero-out the DATAREF in the signal */
              SET_PACKED_DATAREF_SLOT(packed_sigptr, i, 0);
              SET_PACKED_DATAREF_LEN(packed_sigptr, i, 0);
          }
          else
          {
              /*
               * Fill in the slot number in the SIGNAL structure but
               * preserve the offset already in there
               */
              SET_PACKED_DATAREF_SLOT(packed_sigptr, i, slots[j] | (((u16)packed_sigptr[SIZEOF_SIGNAL_HEADER + (i * SIZEOF_DATAREF) + 1]) << 8));
              SET_PACKED_DATAREF_LEN(packed_sigptr, i, bulkdata[i].data_length);
  
              /* Do not copy the data, just store the information to them */
              card->from_host_data[slots[j]].bd.os_data_ptr = bulkdata[i].os_data_ptr;
              card->from_host_data[slots[j]].bd.os_net_buf_ptr = bulkdata[i].os_net_buf_ptr;
              card->from_host_data[slots[j]].bd.data_length = bulkdata[i].data_length;
              card->from_host_data[slots[j]].bd.net_buf_length = bulkdata[i].net_buf_length;
              card->from_host_data[slots[j]].queue = queue;
  
              unifi_trace(card->ospriv, UDBG4, "CardWriteBulkData sig=0x%x, fh slot %d = %p\n",
                          GET_SIGNAL_ID(packed_sigptr), i, bulkdata[i].os_data_ptr);
  
              /* Sanity-check that the bulk data desc being assigned to the slot
               * actually has a payload.
               */
              if (!bulkdata[i].os_data_ptr)
              {
                  unifi_error(card->ospriv, "Assign null os_data_ptr (len=%d) fh slot %d, i=%d, q=%d, sig=0x%x",
                              bulkdata[i].data_length, slots[j], i, queue, GET_SIGNAL_ID(packed_sigptr));
              }
  
              j++;
              if (queue < UNIFI_NO_OF_TX_QS)
              {
                  card->dynamic_slot_data.from_host_used_slots[queue]++;
              }
          }
      }
  
      func_exit();
  
      return CSR_RESULT_SUCCESS;
  } /*  CardWriteBulkData() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  card_find_data_slot
   *
   *      Dereference references to bulk data slots into pointers to real data.
   *
   *  Arguments:
   *      card            Pointer to the card struct.
   *      slot            Slot number from a signal structure
   *
   *  Returns:
   *      Pointer to entry in bulk_data_slot array.
   * ---------------------------------------------------------------------------
   */
  bulk_data_desc_t* card_find_data_slot(card_t *card, s16 slot)
  {
      s16 sn;
      bulk_data_desc_t *bd;
  
      sn = slot & 0x7FFF;
  
      /* ?? check sanity of slot number ?? */
  
      if (slot & SLOT_DIR_TO_HOST)
      {
          bd = &card->to_host_data[sn];
      }
      else
      {
          bd = &card->from_host_data[sn].bd;
      }
  
      return bd;
  } /* card_find_data_slot() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  firmware_present_in_flash
   *
   *      Probe for external Flash that looks like it might contain firmware.
   *
   *      If Flash is not present, reads always return 0x0008.
   *      If Flash is present, but empty, reads return 0xFFFF.
   *      Anything else is considered to be firmware.
   *
   *  Arguments:
   *      card        Pointer to card struct
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS                 firmware is present in ROM or flash
   *      CSR_WIFI_HIP_RESULT_NOT_FOUND      firmware is not present in ROM or flash
   *      CSR_WIFI_HIP_RESULT_NO_DEVICE      if the card was ejected
   *      CSR_RESULT_FAILURE                 if an SDIO error occurred
   * ---------------------------------------------------------------------------
   */
  static CsrResult firmware_present_in_flash(card_t *card)
  {
      CsrResult r;
      u16 m1, m5;
  
      if (ChipHelper_HasRom(card->helper))
      {
          return CSR_RESULT_SUCCESS;
      }
      if (!ChipHelper_HasFlash(card->helper))
      {
          return CSR_WIFI_HIP_RESULT_NOT_FOUND;
      }
  
      /*
       * Examine the Flash locations that are the power-on default reset
       * vectors of the XAP processors.
       * These are words 1 and 5 in Flash.
       */
      r = unifi_card_read16(card, UNIFI_MAKE_GP(EXT_FLASH, 2), &m1);
      if (r != CSR_RESULT_SUCCESS)
      {
          return r;
      }
  
      r = unifi_card_read16(card, UNIFI_MAKE_GP(EXT_FLASH, 10), &m5);
      if (r != CSR_RESULT_SUCCESS)
      {
          return r;
      }
  
      /* Check for uninitialised/missing flash */
      if ((m1 == 0x0008) || (m1 == 0xFFFF) ||
          (m1 == 0x0004) || (m5 == 0x0004) ||
          (m5 == 0x0008) || (m5 == 0xFFFF))
      {
          return CSR_WIFI_HIP_RESULT_NOT_FOUND;
      }
  
      return CSR_RESULT_SUCCESS;
  } /* firmware_present_in_flash() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  bootstrap_chip_hw
   *
   *      Perform chip specific magic to "Get It Working" TM.  This will
   *      increase speed of PLLs in analogue and maybe enable some
   *      on-chip regulators.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *
   *  Returns:
   *      None.
   * ---------------------------------------------------------------------------
   */
  static void bootstrap_chip_hw(card_t *card)
  {
      const struct chip_helper_init_values *vals;
      u32 i, len;
      void *sdio = card->sdio_if;
      CsrResult csrResult;
  
      len = ChipHelper_ClockStartupSequence(card->helper, &vals);
      if (len != 0)
      {
          for (i = 0; i < len; i++)
          {
              csrResult = CsrSdioWrite16(sdio, vals[i].addr * 2, vals[i].value);
              if (csrResult != CSR_RESULT_SUCCESS)
              {
                  unifi_warning(card->ospriv, "Failed to write bootstrap value %d\n", i);
                  /* Might not be fatal */
              }
  
              CsrThreadSleep(1);
          }
      }
  } /* bootstrap_chip_hw() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_card_stop_processor
   *
   *      Stop the UniFi XAP processors.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *      which           One of UNIFI_PROC_MAC, UNIFI_PROC_PHY, UNIFI_PROC_BOTH
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS if successful, or CSR error code
   * ---------------------------------------------------------------------------
   */
  CsrResult unifi_card_stop_processor(card_t *card, enum unifi_dbg_processors_select which)
  {
      CsrResult r = CSR_RESULT_SUCCESS;
      u8 status;
      s16 retry = 100;
  
      while (retry--)
      {
          /* Select both XAPs */
          r = unifi_set_proc_select(card, which);
          if (r != CSR_RESULT_SUCCESS)
          {
              break;
          }
  
          /* Stop processors */
          r = unifi_write_direct16(card, ChipHelper_DBG_EMU_CMD(card->helper) * 2, 2);
          if (r != CSR_RESULT_SUCCESS)
          {
              break;
          }
  
          /* Read status */
          r = unifi_read_direct_8_or_16(card,
                                        ChipHelper_DBG_HOST_STOP_STATUS(card->helper) * 2,
                                        &status);
          if (r != CSR_RESULT_SUCCESS)
          {
              break;
          }
  
          if ((status & 1) == 1)
          {
              /* Success! */
              return CSR_RESULT_SUCCESS;
          }
  
          /* Processors didn't stop, try again */
      }
  
      if (r != CSR_RESULT_SUCCESS)
      {
          /* An SDIO error occurred */
          unifi_error(card->ospriv, "Failed to stop processors: SDIO error\n");
      }
      else
      {
          /* If we reach here, we didn't the status in time. */
          unifi_error(card->ospriv, "Failed to stop processors: timeout waiting for stopped status\n");
          r = CSR_RESULT_FAILURE;
      }
  
      return r;
  } /* unifi_card_stop_processor() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  card_start_processor
   *
   *      Start the UniFi XAP processors.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *      which           One of UNIFI_PROC_MAC, UNIFI_PROC_PHY, UNIFI_PROC_BOTH
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS or CSR error code
   * ---------------------------------------------------------------------------
   */
  CsrResult card_start_processor(card_t *card, enum unifi_dbg_processors_select which)
  {
      CsrResult r;
  
      /* Select both XAPs */
      r = unifi_set_proc_select(card, which);
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "unifi_set_proc_select failed: %d.\n", r);
          return r;
      }
  
  
      r = unifi_write_direct_8_or_16(card,
                                     ChipHelper_DBG_EMU_CMD(card->helper) * 2, 8);
      if (r != CSR_RESULT_SUCCESS)
      {
          return r;
      }
  
      r = unifi_write_direct_8_or_16(card,
                                     ChipHelper_DBG_EMU_CMD(card->helper) * 2, 0);
      if (r != CSR_RESULT_SUCCESS)
      {
          return r;
      }
  
      return CSR_RESULT_SUCCESS;
  } /* card_start_processor() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_set_interrupt_mode
   *
   *      Configure the interrupt processing mode used by the HIP
   *
   *  Arguments:
   *      card            Pointer to card struct
   *      mode            Interrupt mode to apply
   *
   *  Returns:
   *      None
   * ---------------------------------------------------------------------------
   */
  void unifi_set_interrupt_mode(card_t *card, u32 mode)
  {
      if (mode == CSR_WIFI_INTMODE_RUN_BH_ONCE)
      {
          unifi_info(card->ospriv, "Scheduled interrupt mode");
      }
      card->intmode = mode;
  } /* unifi_set_interrupt_mode() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_start_processors
   *
   *      Start all UniFi XAP processors.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS on success, CSR error code on error
   * ---------------------------------------------------------------------------
   */
  CsrResult unifi_start_processors(card_t *card)
  {
      return card_start_processor(card, UNIFI_PROC_BOTH);
  } /* unifi_start_processors() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_request_max_sdio_clock
   *
   *      Requests that the maximum SDIO clock rate is set at the next suitable
   *      opportunity (e.g. when the BH next runs, so as not to interfere with
   *      any current operation).
   *
   *  Arguments:
   *      card            Pointer to card struct
   *
   *  Returns:
   *      None
   * ---------------------------------------------------------------------------
   */
  void unifi_request_max_sdio_clock(card_t *card)
  {
      card->request_max_clock = 1;
  } /* unifi_request_max_sdio_clock() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_set_host_state
   *
   *      Set the host deep-sleep state.
   *
   *      If transitioning to TORPID, the SDIO driver will be notified
   *      that the SD bus will be unused (idle) and conversely, when
   *      transitioning from TORPID that the bus will be used (active).
   *
   *  Arguments:
   *      card            Pointer to card struct
   *      state           New deep-sleep state.
   *
   *  Returns:
   *      CSR_RESULT_SUCCESS            on success
   *      CSR_WIFI_HIP_RESULT_NO_DEVICE      if the card was ejected
   *      CSR_RESULT_FAILURE            if an SDIO error occurred
   *
   *  Notes:
   *      We need to reduce the SDIO clock speed before trying to wake up the
   *      chip. Actually, in the implementation below we reduce the clock speed
   *      not just before we try to wake up the chip, but when we put the chip to
   *      deep sleep. This means that if the f/w wakes up on its' own, we waste
   *      a reduce/increace cycle. However, trying to eliminate this overhead is
   *      proved difficult, as the current state machine in the HIP lib does at
   *      least a CMD52 to disable the interrupts before we configure the host
   *      state.
   * ---------------------------------------------------------------------------
   */
  CsrResult unifi_set_host_state(card_t *card, enum unifi_host_state state)
  {
      CsrResult r = CSR_RESULT_SUCCESS;
      CsrResult csrResult;
      static const char *const states[] = {
          "AWAKE", "DROWSY", "TORPID"
      };
      static const u8 state_csr_host_wakeup[] = {
          1, 3, 0
      };
      static const u8 state_io_abort[] = {
          0, 2, 3
      };
  
      unifi_trace(card->ospriv, UDBG4, "State %s to %s\n",
                  states[card->host_state], states[state]);
  
      if (card->host_state == UNIFI_HOST_STATE_TORPID)
      {
          CsrSdioFunctionActive(card->sdio_if);
      }
  
      /* Write the new state to UniFi. */
      if (card->chip_id > SDIO_CARD_ID_UNIFI_2)
      {
          r = sdio_write_f0(card, SDIO_CSR_HOST_WAKEUP,
                            (u8)((card->function << 4) | state_csr_host_wakeup[state]));
      }
      else
      {
          r = sdio_write_f0(card, SDIO_IO_ABORT, state_io_abort[state]);
      }
  
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to write UniFi deep sleep state\n");
      }
      else
      {
          /*
           * If the chip was in state TORPID then we can now increase
           * the maximum bus clock speed.
           */
          if (card->host_state == UNIFI_HOST_STATE_TORPID)
          {
              csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if,
                                                         UNIFI_SDIO_CLOCK_MAX_HZ);
              r = ConvertCsrSdioToCsrHipResult(card, csrResult);
              /* Non-fatal error */
              if (r != CSR_RESULT_SUCCESS && r != CSR_WIFI_HIP_RESULT_NO_DEVICE)
              {
                  unifi_warning(card->ospriv,
                                "Failed to increase the SDIO clock speed\n");
              }
              else
              {
                  card->sdio_clock_speed = UNIFI_SDIO_CLOCK_MAX_HZ;
              }
          }
  
          /*
           * Cache the current state in the card structure to avoid
           * unnecessary SDIO reads.
           */
          card->host_state = state;
  
          if (state == UNIFI_HOST_STATE_TORPID)
          {
              /*
               * If the chip is now in state TORPID then we must now decrease
               * the maximum bus clock speed.
               */
              csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if,
                                                         UNIFI_SDIO_CLOCK_SAFE_HZ);
              r = ConvertCsrSdioToCsrHipResult(card, csrResult);
              if (r != CSR_RESULT_SUCCESS && r != CSR_WIFI_HIP_RESULT_NO_DEVICE)
              {
                  unifi_warning(card->ospriv,
                                "Failed to decrease the SDIO clock speed\n");
              }
              else
              {
                  card->sdio_clock_speed = UNIFI_SDIO_CLOCK_SAFE_HZ;
              }
              CsrSdioFunctionIdle(card->sdio_if);
          }
      }
  
      return r;
  } /* unifi_set_host_state() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_card_info
   *
   *      Update the card information data structure
   *
   *  Arguments:
   *      card            Pointer to card struct
   *      card_info       Pointer to info structure to update
   *
   *  Returns:
   *      None
   * ---------------------------------------------------------------------------
   */
  void unifi_card_info(card_t *card, card_info_t *card_info)
  {
      card_info->chip_id = card->chip_id;
      card_info->chip_version = card->chip_version;
      card_info->fw_build = card->build_id;
      card_info->fw_hip_version = card->config_data.version;
      card_info->sdio_block_size = card->sdio_io_block_size;
  } /* unifi_card_info() */
  
  
  /*
   * ---------------------------------------------------------------------------
   *  unifi_check_io_status
   *
   *      Check UniFi for spontaneous reset and pending interrupt.
   *
   *  Arguments:
   *      card            Pointer to card struct
   *      status          Pointer to location to write chip status:
   *                        0 if UniFi is running, and no interrupt pending
   *                        1 if UniFi has spontaneously reset
   *                        2 if there is a pending interrupt
   *  Returns:
   *      CSR_RESULT_SUCCESS if OK, or CSR error
   * ---------------------------------------------------------------------------
   */
  CsrResult unifi_check_io_status(card_t *card, s32 *status)
  {
      u8 io_en;
      CsrResult r;
      u8 pending;
  
      *status = 0;
  
      r = sdio_read_f0(card, SDIO_IO_ENABLE, &io_en);
      if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
      {
          return r;
      }
      if (r != CSR_RESULT_SUCCESS)
      {
          unifi_error(card->ospriv, "Failed to read SDIO_IO_ENABLE to check for spontaneous reset\n");
          return r;
      }
  
      if ((io_en & (1 << card->function)) == 0)
      {
          s32 fw_count;
          *status = 1;
          unifi_error(card->ospriv, "UniFi has spontaneously reset.\n");
  
          /*
           * These reads are very likely to fail. We want to know if the function is really
           * disabled or the SDIO driver just returns rubbish.
           */
          fw_count = unifi_read_shared_count(card, card->sdio_ctrl_addr + 4);
          if (fw_count < 0)
          {
              unifi_error(card->ospriv, "Failed to read to-host sig written count\n");
          }
          else
          {
              unifi_error(card->ospriv, "thsw: %u (driver thinks is %u)\n",
                          fw_count, card->to_host_signals_w);
          }
          fw_count = unifi_read_shared_count(card, card->sdio_ctrl_addr + 2);
          if (fw_count < 0)
          {
              unifi_error(card->ospriv, "Failed to read from-host sig read count\n");
          }
          else
          {
              unifi_error(card->ospriv, "fhsr: %u (driver thinks is %u)\n",
                          fw_count, card->from_host_signals_r);
          }
  
          return r;
      }
  
      unifi_info(card->ospriv, "UniFi function %d is enabled.\n", card->function);
  
      /* See if we missed an SDIO interrupt */
      r = CardPendingInt(card, &pending);
      if (pending)
      {
          unifi_error(card->ospriv, "There is an unhandled pending interrupt.\n");
          *status = 2;
          return r;
      }
  
      return r;
  } /* unifi_check_io_status() */
  
  
  void unifi_get_hip_qos_info(card_t *card, unifi_HipQosInfo *hipqosinfo)
  {
      s32 count_fhr;
      s16 t;
      u32 occupied_fh;
  
      q_t *sigq;
      u16 nslots, i;
  
      memset(hipqosinfo, 0, sizeof(unifi_HipQosInfo));
  
      nslots = card->config_data.num_fromhost_data_slots;
  
      for (i = 0; i < nslots; i++)
      {
          if (card->from_host_data[i].bd.data_length == 0)
          {
              hipqosinfo->free_fh_bulkdata_slots++;
          }
      }
  
      for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
      {
          sigq = &card->fh_traffic_queue[i];
          t = sigq->q_wr_ptr - sigq->q_rd_ptr;
          if (t < 0)
          {
              t += sigq->q_length;
          }
          hipqosinfo->free_fh_sig_queue_slots[i] = (sigq->q_length - t) - 1;
      }
  
      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 - %d\n", count_fhr);
          hipqosinfo->free_fh_fw_slots = 0xfa;
          return;
      }
  
      occupied_fh = (card->from_host_signals_w - count_fhr) % 128;
  
      hipqosinfo->free_fh_fw_slots = (u16)(card->config_data.num_fromhost_sig_frags - occupied_fh);
  }
  
  
  
  CsrResult ConvertCsrSdioToCsrHipResult(card_t *card, CsrResult csrResult)
  {
      CsrResult r = CSR_RESULT_FAILURE;
  
      switch (csrResult)
      {
          case CSR_RESULT_SUCCESS:
              r = CSR_RESULT_SUCCESS;
              break;
          /* Timeout errors */
          case CSR_SDIO_RESULT_TIMEOUT:
          /* Integrity errors */
          case CSR_SDIO_RESULT_CRC_ERROR:
              r = CSR_RESULT_FAILURE;
              break;
          case CSR_SDIO_RESULT_NO_DEVICE:
              r = CSR_WIFI_HIP_RESULT_NO_DEVICE;
              break;
          case CSR_SDIO_RESULT_INVALID_VALUE:
              r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
              break;
          case CSR_RESULT_FAILURE:
              r = CSR_RESULT_FAILURE;
              break;
          default:
              unifi_warning(card->ospriv, "Unrecognised csrResult error code: %d\n", csrResult);
              break;
      }
  
      return r;
  } /* ConvertCsrSdioToCsrHipResult() */
  
  
  