Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  wlan.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
 * All rights reserved.
 */


#include <linux/if_ether.h>
#include <linux/ip.h>
#include <net/dsfield.h>
#include "cfg80211.h"
#include "wlan_cfg.h"

#define WAKE_UP_TRIAL_RETRY  10000

static void wilc_wlan_txq_remove(struct wilc *wilc, u8 q_num,
     struct txq_entry_t *tqe)
{
 list_del(&tqe->list);
 wilc->txq_entries -= 1;
 wilc->txq[q_num].count--;
}

static struct txq_entry_t *
wilc_wlan_txq_remove_from_head(struct wilc *wilc, u8 q_num)
{
 struct txq_entry_t *tqe = NULL;
 unsigned long flags;

 spin_lock_irqsave(&wilc->txq_spinlock, flags);

 if (!list_empty(&wilc->txq[q_num].txq_head.list)) {
  tqe = list_first_entry(&wilc->txq[q_num].txq_head.list,
           struct txq_entry_t, list);
  list_del(&tqe->list);
  wilc->txq_entries -= 1;
  wilc->txq[q_num].count--;
 }
 spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
 return tqe;
}

static void wilc_wlan_txq_add_to_tail(struct net_device *dev, u8 q_num,
          struct txq_entry_t *tqe)
{
 unsigned long flags;
 struct wilc_vif *vif = netdev_priv(dev);
 struct wilc *wilc = vif->wilc;

 spin_lock_irqsave(&wilc->txq_spinlock, flags);

 list_add_tail(&tqe->list, &wilc->txq[q_num].txq_head.list);
 wilc->txq_entries += 1;
 wilc->txq[q_num].count++;

 spin_unlock_irqrestore(&wilc->txq_spinlock, flags);

 complete(&wilc->txq_event);
}

static void wilc_wlan_txq_add_to_head(struct wilc_vif *vif, u8 q_num,
          struct txq_entry_t *tqe)
{
 unsigned long flags;
 struct wilc *wilc = vif->wilc;

 mutex_lock(&wilc->txq_add_to_head_cs);

 spin_lock_irqsave(&wilc->txq_spinlock, flags);

 list_add(&tqe->list, &wilc->txq[q_num].txq_head.list);
 wilc->txq_entries += 1;
 wilc->txq[q_num].count++;

 spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
 mutex_unlock(&wilc->txq_add_to_head_cs);
 complete(&wilc->txq_event);
}

#define NOT_TCP_ACK   (-1)

static inline void add_tcp_session(struct wilc_vif *vif, u32 src_prt,
       u32 dst_prt, u32 seq)
{
 struct tcp_ack_filter *f = &vif->ack_filter;

 if (f->tcp_session < 2 * MAX_TCP_SESSION) {
  f->ack_session_info[f->tcp_session].seq_num = seq;
  f->ack_session_info[f->tcp_session].bigger_ack_num = 0;
  f->ack_session_info[f->tcp_session].src_port = src_prt;
  f->ack_session_info[f->tcp_session].dst_port = dst_prt;
  f->tcp_session++;
 }
}

static inline void update_tcp_session(struct wilc_vif *vif, u32 index, u32 ack)
{
 struct tcp_ack_filter *f = &vif->ack_filter;

 if (index < 2 * MAX_TCP_SESSION &&
     ack > f->ack_session_info[index].bigger_ack_num)
  f->ack_session_info[index].bigger_ack_num = ack;
}

static inline void add_tcp_pending_ack(struct wilc_vif *vif, u32 ack,
           u32 session_index,
           struct txq_entry_t *txqe)
{
 struct tcp_ack_filter *f = &vif->ack_filter;
 u32 i = f->pending_base + f->pending_acks_idx;

 if (i < MAX_PENDING_ACKS) {
  f->pending_acks[i].ack_num = ack;
  f->pending_acks[i].txqe = txqe;
  f->pending_acks[i].session_index = session_index;
  txqe->ack_idx = i;
  f->pending_acks_idx++;
 }
}

static inline void tcp_process(struct net_device *dev, struct txq_entry_t *tqe)
{
 void *buffer = tqe->buffer;
 const struct ethhdr *eth_hdr_ptr = buffer;
 int i;
 unsigned long flags;
 struct wilc_vif *vif = netdev_priv(dev);
 struct wilc *wilc = vif->wilc;
 struct tcp_ack_filter *f = &vif->ack_filter;
 const struct iphdr *ip_hdr_ptr;
 const struct tcphdr *tcp_hdr_ptr;
 u32 ihl, total_length, data_offset;

 spin_lock_irqsave(&wilc->txq_spinlock, flags);

 if (eth_hdr_ptr->h_proto != htons(ETH_P_IP))
  goto out;

 ip_hdr_ptr = buffer + ETH_HLEN;

 if (ip_hdr_ptr->protocol != IPPROTO_TCP)
  goto out;

 ihl = ip_hdr_ptr->ihl << 2;
 tcp_hdr_ptr = buffer + ETH_HLEN + ihl;
 total_length = ntohs(ip_hdr_ptr->tot_len);

 data_offset = tcp_hdr_ptr->doff << 2;
 if (total_length == (ihl + data_offset)) {
  u32 seq_no, ack_no;

  seq_no = ntohl(tcp_hdr_ptr->seq);
  ack_no = ntohl(tcp_hdr_ptr->ack_seq);
  for (i = 0; i < f->tcp_session; i++) {
   u32 j = f->ack_session_info[i].seq_num;

   if (i < 2 * MAX_TCP_SESSION &&
       j == seq_no) {
    update_tcp_session(vif, i, ack_no);
    break;
   }
  }
  if (i == f->tcp_session)
   add_tcp_session(vif, 0, 0, seq_no);

  add_tcp_pending_ack(vif, ack_no, i, tqe);
 }

out:
 spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
}

static void wilc_wlan_txq_filter_dup_tcp_ack(struct net_device *dev)
{
 struct wilc_vif *vif = netdev_priv(dev);
 struct wilc *wilc = vif->wilc;
 struct tcp_ack_filter *f = &vif->ack_filter;
 u32 i = 0;
 u32 dropped = 0;
 unsigned long flags;

 spin_lock_irqsave(&wilc->txq_spinlock, flags);
 for (i = f->pending_base;
      i < (f->pending_base + f->pending_acks_idx); i++) {
  u32 index;
  u32 bigger_ack_num;

  if (i >= MAX_PENDING_ACKS)
   break;

  index = f->pending_acks[i].session_index;

  if (index >= 2 * MAX_TCP_SESSION)
   break;

  bigger_ack_num = f->ack_session_info[index].bigger_ack_num;

  if (f->pending_acks[i].ack_num < bigger_ack_num) {
   struct txq_entry_t *tqe;

   tqe = f->pending_acks[i].txqe;
   if (tqe) {
    wilc_wlan_txq_remove(wilc, tqe->q_num, tqe);
    tqe->status = 1;
    if (tqe->tx_complete_func)
     tqe->tx_complete_func(tqe->priv,
             tqe->status);
    kfree(tqe);
    dropped++;
   }
  }
 }
 f->pending_acks_idx = 0;
 f->tcp_session = 0;

 if (f->pending_base == 0)
  f->pending_base = MAX_TCP_SESSION;
 else
  f->pending_base = 0;

 spin_unlock_irqrestore(&wilc->txq_spinlock, flags);

 while (dropped > 0) {
  wait_for_completion_timeout(&wilc->txq_event,
         msecs_to_jiffies(1));
  dropped--;
 }
}

void wilc_enable_tcp_ack_filter(struct wilc_vif *vif, bool value)
{
 vif->ack_filter.enabled = value;
}

static int wilc_wlan_txq_add_cfg_pkt(struct wilc_vif *vif, u8 *buffer,
         u32 buffer_size)
{
 struct txq_entry_t *tqe;
 struct wilc *wilc = vif->wilc;

 netdev_dbg(vif->ndev, "Adding config packet ...\n");
 if (wilc->quit) {
  netdev_dbg(vif->ndev, "Return due to clear function\n");
  complete(&wilc->cfg_event);
  return 0;
 }

 tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);
 if (!tqe) {
  complete(&wilc->cfg_event);
  return 0;
 }

 tqe->type = WILC_CFG_PKT;
 tqe->buffer = buffer;
 tqe->buffer_size = buffer_size;
 tqe->tx_complete_func = NULL;
 tqe->priv = NULL;
 tqe->q_num = AC_VO_Q;
 tqe->ack_idx = NOT_TCP_ACK;
 tqe->vif = vif;

 wilc_wlan_txq_add_to_head(vif, AC_VO_Q, tqe);

 return 1;
}

static bool is_ac_q_limit(struct wilc *wl, u8 q_num)
{
 u8 factors[NQUEUES] = {1, 1, 1, 1};
 u16 i;
 unsigned long flags;
 struct wilc_tx_queue_status *q = &wl->tx_q_limit;
 u8 end_index;
 u8 q_limit;
 bool ret = false;

 spin_lock_irqsave(&wl->txq_spinlock, flags);
 if (!q->initialized) {
  for (i = 0; i < AC_BUFFER_SIZE; i++)
   q->buffer[i] = i % NQUEUES;

  for (i = 0; i < NQUEUES; i++) {
   q->cnt[i] = AC_BUFFER_SIZE * factors[i] / NQUEUES;
   q->sum += q->cnt[i];
  }
  q->end_index = AC_BUFFER_SIZE - 1;
  q->initialized = 1;
 }

 end_index = q->end_index;
 q->cnt[q->buffer[end_index]] -= factors[q->buffer[end_index]];
 q->cnt[q_num] += factors[q_num];
 q->sum += (factors[q_num] - factors[q->buffer[end_index]]);

 q->buffer[end_index] = q_num;
 if (end_index > 0)
  q->end_index--;
 else
  q->end_index = AC_BUFFER_SIZE - 1;

 if (!q->sum)
  q_limit = 1;
 else
  q_limit = (q->cnt[q_num] * FLOW_CONTROL_UPPER_THRESHOLD / q->sum) + 1;

 if (wl->txq[q_num].count <= q_limit)
  ret = true;

 spin_unlock_irqrestore(&wl->txq_spinlock, flags);

 return ret;
}

static inline u8 ac_classify(struct wilc *wilc, struct sk_buff *skb)
{
 u8 q_num = AC_BE_Q;
 u8 dscp;

 switch (skb->protocol) {
 case htons(ETH_P_IP):
  dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
  break;
 case htons(ETH_P_IPV6):
  dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc;
  break;
 default:
  return q_num;
 }

 switch (dscp) {
 case 0x08:
 case 0x20:
 case 0x40:
  q_num = AC_BK_Q;
  break;
 case 0x80:
 case 0xA0:
 case 0x28:
  q_num = AC_VI_Q;
  break;
 case 0xC0:
 case 0xD0:
 case 0xE0:
 case 0x88:
 case 0xB8:
  q_num = AC_VO_Q;
  break;
 }

 return q_num;
}

static inline int ac_balance(struct wilc *wl, u8 *ratio)
{
 u8 i, max_count = 0;

 if (!ratio)
  return -EINVAL;

 for (i = 0; i < NQUEUES; i++)
  if (wl->txq[i].fw.count > max_count)
   max_count = wl->txq[i].fw.count;

 for (i = 0; i < NQUEUES; i++)
  ratio[i] = max_count - wl->txq[i].fw.count;

 return 0;
}

static inline void ac_update_fw_ac_pkt_info(struct wilc *wl, u32 reg)
{
 wl->txq[AC_BK_Q].fw.count = FIELD_GET(BK_AC_COUNT_FIELD, reg);
 wl->txq[AC_BE_Q].fw.count = FIELD_GET(BE_AC_COUNT_FIELD, reg);
 wl->txq[AC_VI_Q].fw.count = FIELD_GET(VI_AC_COUNT_FIELD, reg);
 wl->txq[AC_VO_Q].fw.count = FIELD_GET(VO_AC_COUNT_FIELD, reg);

 wl->txq[AC_BK_Q].fw.acm = FIELD_GET(BK_AC_ACM_STAT_FIELD, reg);
 wl->txq[AC_BE_Q].fw.acm = FIELD_GET(BE_AC_ACM_STAT_FIELD, reg);
 wl->txq[AC_VI_Q].fw.acm = FIELD_GET(VI_AC_ACM_STAT_FIELD, reg);
 wl->txq[AC_VO_Q].fw.acm = FIELD_GET(VO_AC_ACM_STAT_FIELD, reg);
}

static inline u8 ac_change(struct wilc *wilc, u8 *ac)
{
 do {
  if (wilc->txq[*ac].fw.acm == 0)
   return 0;
  (*ac)++;
 } while (*ac < NQUEUES);

 return 1;
}

int wilc_wlan_txq_add_net_pkt(struct net_device *dev,
         struct tx_complete_data *tx_data, u8 *buffer,
         u32 buffer_size,
         void (*tx_complete_fn)(void *, int))
{
 struct txq_entry_t *tqe;
 struct wilc_vif *vif = netdev_priv(dev);
 struct wilc *wilc;
 u8 q_num;

 wilc = vif->wilc;

 if (wilc->quit) {
  tx_complete_fn(tx_data, 0);
  return 0;
 }

 if (!wilc->initialized) {
  tx_complete_fn(tx_data, 0);
  return 0;
 }

 tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);

 if (!tqe) {
  tx_complete_fn(tx_data, 0);
  return 0;
 }
 tqe->type = WILC_NET_PKT;
 tqe->buffer = buffer;
 tqe->buffer_size = buffer_size;
 tqe->tx_complete_func = tx_complete_fn;
 tqe->priv = tx_data;
 tqe->vif = vif;

 q_num = ac_classify(wilc, tx_data->skb);
 tqe->q_num = q_num;
 if (ac_change(wilc, &q_num)) {
  tx_complete_fn(tx_data, 0);
  kfree(tqe);
  return 0;
 }

 if (is_ac_q_limit(wilc, q_num)) {
  tqe->ack_idx = NOT_TCP_ACK;
  if (vif->ack_filter.enabled)
   tcp_process(dev, tqe);
  wilc_wlan_txq_add_to_tail(dev, q_num, tqe);
 } else {
  tx_complete_fn(tx_data, 0);
  kfree(tqe);
 }

 return wilc->txq_entries;
}

int wilc_wlan_txq_add_mgmt_pkt(struct net_device *dev, void *priv, u8 *buffer,
          u32 buffer_size,
          void (*tx_complete_fn)(void *, int))
{
 struct txq_entry_t *tqe;
 struct wilc_vif *vif = netdev_priv(dev);
 struct wilc *wilc;

 wilc = vif->wilc;

 if (wilc->quit) {
  tx_complete_fn(priv, 0);
  return 0;
 }

 if (!wilc->initialized) {
  tx_complete_fn(priv, 0);
  return 0;
 }
 tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);

 if (!tqe) {
  tx_complete_fn(priv, 0);
  return 0;
 }
 tqe->type = WILC_MGMT_PKT;
 tqe->buffer = buffer;
 tqe->buffer_size = buffer_size;
 tqe->tx_complete_func = tx_complete_fn;
 tqe->priv = priv;
 tqe->q_num = AC_BE_Q;
 tqe->ack_idx = NOT_TCP_ACK;
 tqe->vif = vif;
 wilc_wlan_txq_add_to_tail(dev, AC_VO_Q, tqe);
 return 1;
}

static struct txq_entry_t *wilc_wlan_txq_get_first(struct wilc *wilc, u8 q_num)
{
 struct txq_entry_t *tqe = NULL;
 unsigned long flags;

 spin_lock_irqsave(&wilc->txq_spinlock, flags);

 if (!list_empty(&wilc->txq[q_num].txq_head.list))
  tqe = list_first_entry(&wilc->txq[q_num].txq_head.list,
           struct txq_entry_t, list);

 spin_unlock_irqrestore(&wilc->txq_spinlock, flags);

 return tqe;
}

static struct txq_entry_t *wilc_wlan_txq_get_next(struct wilc *wilc,
        struct txq_entry_t *tqe,
        u8 q_num)
{
 unsigned long flags;

 spin_lock_irqsave(&wilc->txq_spinlock, flags);

 if (!list_is_last(&tqe->list, &wilc->txq[q_num].txq_head.list))
  tqe = list_next_entry(tqe, list);
 else
  tqe = NULL;
 spin_unlock_irqrestore(&wilc->txq_spinlock, flags);

 return tqe;
}

static void wilc_wlan_rxq_add(struct wilc *wilc, struct rxq_entry_t *rqe)
{
 if (wilc->quit)
  return;

 mutex_lock(&wilc->rxq_cs);
 list_add_tail(&rqe->list, &wilc->rxq_head.list);
 mutex_unlock(&wilc->rxq_cs);
}

static struct rxq_entry_t *wilc_wlan_rxq_remove(struct wilc *wilc)
{
 struct rxq_entry_t *rqe = NULL;

 mutex_lock(&wilc->rxq_cs);
 if (!list_empty(&wilc->rxq_head.list)) {
  rqe = list_first_entry(&wilc->rxq_head.list, struct rxq_entry_t,
           list);
  list_del(&rqe->list);
 }
 mutex_unlock(&wilc->rxq_cs);
 return rqe;
}

static int chip_allow_sleep_wilc1000(struct wilc *wilc)
{
 u32 reg = 0;
 const struct wilc_hif_func *hif_func = wilc->hif_func;
 u32 wakeup_reg, wakeup_bit;
 u32 to_host_from_fw_reg, to_host_from_fw_bit;
 u32 from_host_to_fw_reg, from_host_to_fw_bit;
 u32 trials = 100;
 int ret;

 if (wilc->io_type == WILC_HIF_SDIO) {
  wakeup_reg = WILC_SDIO_WAKEUP_REG;
  wakeup_bit = WILC_SDIO_WAKEUP_BIT;
  from_host_to_fw_reg = WILC_SDIO_HOST_TO_FW_REG;
  from_host_to_fw_bit = WILC_SDIO_HOST_TO_FW_BIT;
  to_host_from_fw_reg = WILC_SDIO_FW_TO_HOST_REG;
  to_host_from_fw_bit = WILC_SDIO_FW_TO_HOST_BIT;
 } else {
  wakeup_reg = WILC_SPI_WAKEUP_REG;
  wakeup_bit = WILC_SPI_WAKEUP_BIT;
  from_host_to_fw_reg = WILC_SPI_HOST_TO_FW_REG;
  from_host_to_fw_bit = WILC_SPI_HOST_TO_FW_BIT;
  to_host_from_fw_reg = WILC_SPI_FW_TO_HOST_REG;
  to_host_from_fw_bit = WILC_SPI_FW_TO_HOST_BIT;
 }

 while (--trials) {
  ret = hif_func->hif_read_reg(wilc, to_host_from_fw_reg, ®);
  if (ret)
   return ret;
  if ((reg & to_host_from_fw_bit) == 0)
   break;
 }
 if (!trials)
  pr_warn("FW not responding\n");

 /* Clear bit 1 */
 ret = hif_func->hif_read_reg(wilc, wakeup_reg, ®);
 if (ret)
  return ret;
 if (reg & wakeup_bit) {
  reg &= ~wakeup_bit;
  ret = hif_func->hif_write_reg(wilc, wakeup_reg, reg);
  if (ret)
   return ret;
 }

 ret = hif_func->hif_read_reg(wilc, from_host_to_fw_reg, ®);
 if (ret)
  return ret;
 if (reg & from_host_to_fw_bit) {
  reg &= ~from_host_to_fw_bit;
  ret = hif_func->hif_write_reg(wilc, from_host_to_fw_reg, reg);
  if (ret)
   return ret;
 }

 return 0;
}

static int chip_allow_sleep_wilc3000(struct wilc *wilc)
{
 u32 reg = 0;
 int ret;
 const struct wilc_hif_func *hif_func = wilc->hif_func;

 if (wilc->io_type == WILC_HIF_SDIO) {
  ret = hif_func->hif_read_reg(wilc, WILC_SDIO_WAKEUP_REG, ®);
  if (ret)
   return ret;
  ret = hif_func->hif_write_reg(wilc, WILC_SDIO_WAKEUP_REG,
           reg & ~WILC_SDIO_WAKEUP_BIT);
  if (ret)
   return ret;
 } else {
  ret = hif_func->hif_read_reg(wilc, WILC_SPI_WAKEUP_REG, ®);
  if (ret)
   return ret;
  ret = hif_func->hif_write_reg(wilc, WILC_SPI_WAKEUP_REG,
           reg & ~WILC_SPI_WAKEUP_BIT);
  if (ret)
   return ret;
 }
 return 0;
}

static int chip_allow_sleep(struct wilc *wilc)
{
 if (is_wilc1000(wilc->chipid))
  return chip_allow_sleep_wilc1000(wilc);
 else
  return chip_allow_sleep_wilc3000(wilc);
}

static int chip_wakeup_wilc1000(struct wilc *wilc)
{
 u32 ret = 0;
 u32 clk_status_val = 0, trials = 0;
 u32 wakeup_reg, wakeup_bit;
 u32 clk_status_reg, clk_status_bit;
 u32 from_host_to_fw_reg, from_host_to_fw_bit;
 const struct wilc_hif_func *hif_func = wilc->hif_func;

 if (wilc->io_type == WILC_HIF_SDIO) {
  wakeup_reg = WILC_SDIO_WAKEUP_REG;
  wakeup_bit = WILC_SDIO_WAKEUP_BIT;
  clk_status_reg = WILC1000_SDIO_CLK_STATUS_REG;
  clk_status_bit = WILC1000_SDIO_CLK_STATUS_BIT;
  from_host_to_fw_reg = WILC_SDIO_HOST_TO_FW_REG;
  from_host_to_fw_bit = WILC_SDIO_HOST_TO_FW_BIT;
 } else {
  wakeup_reg = WILC_SPI_WAKEUP_REG;
  wakeup_bit = WILC_SPI_WAKEUP_BIT;
  clk_status_reg = WILC1000_SPI_CLK_STATUS_REG;
  clk_status_bit = WILC1000_SPI_CLK_STATUS_BIT;
  from_host_to_fw_reg = WILC_SPI_HOST_TO_FW_REG;
  from_host_to_fw_bit = WILC_SPI_HOST_TO_FW_BIT;
 }

 /* indicate host wakeup */
 ret = hif_func->hif_write_reg(wilc, from_host_to_fw_reg,
          from_host_to_fw_bit);
 if (ret)
  return ret;

 /* Set wake-up bit */
 ret = hif_func->hif_write_reg(wilc, wakeup_reg,
          wakeup_bit);
 if (ret)
  return ret;

 while (trials < WAKE_UP_TRIAL_RETRY) {
  ret = hif_func->hif_read_reg(wilc, clk_status_reg,
          &clk_status_val);
  if (ret) {
   pr_err("Bus error %d %x\n", ret, clk_status_val);
   return ret;
  }
  if (clk_status_val & clk_status_bit)
   break;

  trials++;
 }
 if (trials >= WAKE_UP_TRIAL_RETRY) {
  pr_err("Failed to wake-up the chip\n");
  return -ETIMEDOUT;
 }
 /* Sometimes spi fail to read clock regs after reading
 * writing clockless registers
 */

 if (wilc->io_type == WILC_HIF_SPI)
  wilc->hif_func->hif_reset(wilc);

 return 0;
}

static int chip_wakeup_wilc3000(struct wilc *wilc)
{
 u32 wakeup_reg_val, clk_status_reg_val, trials = 0;
 u32 wakeup_reg, wakeup_bit;
 u32 clk_status_reg, clk_status_bit;
 int wake_seq_trials = 5;
 const struct wilc_hif_func *hif_func = wilc->hif_func;

 if (wilc->io_type == WILC_HIF_SDIO) {
  wakeup_reg = WILC_SDIO_WAKEUP_REG;
  wakeup_bit = WILC_SDIO_WAKEUP_BIT;
  clk_status_reg = WILC3000_SDIO_CLK_STATUS_REG;
  clk_status_bit = WILC3000_SDIO_CLK_STATUS_BIT;
 } else {
  wakeup_reg = WILC_SPI_WAKEUP_REG;
  wakeup_bit = WILC_SPI_WAKEUP_BIT;
  clk_status_reg = WILC3000_SPI_CLK_STATUS_REG;
  clk_status_bit = WILC3000_SPI_CLK_STATUS_BIT;
 }

 hif_func->hif_read_reg(wilc, wakeup_reg, &wakeup_reg_val);
 do {
  hif_func->hif_write_reg(wilc, wakeup_reg, wakeup_reg_val |
         wakeup_bit);
  /* Check the clock status */
  hif_func->hif_read_reg(wilc, clk_status_reg,
           &clk_status_reg_val);

  /* In case of clocks off, wait 1ms, and check it again.
 * if still off, wait for another 1ms, for a total wait of 3ms.
 * If still off, redo the wake up sequence
 */

  while ((clk_status_reg_val & clk_status_bit) == 0 &&
         (++trials % 4) != 0) {
   /* Wait for the chip to stabilize*/
   usleep_range(1000, 1100);

   /* Make sure chip is awake. This is an extra step that
 * can be removed later to avoid the bus access
 * overhead
 */

   hif_func->hif_read_reg(wilc, clk_status_reg,
            &clk_status_reg_val);
  }
  /* in case of failure, Reset the wakeup bit to introduce a new
 * edge on the next loop
 */

  if ((clk_status_reg_val & clk_status_bit) == 0) {
   hif_func->hif_write_reg(wilc, wakeup_reg,
      wakeup_reg_val & (~wakeup_bit));
   /* added wait before wakeup sequence retry */
   usleep_range(200, 300);
  }
 } while ((clk_status_reg_val & clk_status_bit) == 0 && wake_seq_trials-- > 0);
 if (!wake_seq_trials)
  dev_err(wilc->dev, "clocks still OFF. Wake up failed\n");

 return 0;
}

static int chip_wakeup(struct wilc *wilc)
{
 if (is_wilc1000(wilc->chipid))
  return chip_wakeup_wilc1000(wilc);
 else
  return chip_wakeup_wilc3000(wilc);
}

static inline int acquire_bus(struct wilc *wilc, enum bus_acquire acquire)
{
 int ret = 0;

 mutex_lock(&wilc->hif_cs);
 if (acquire == WILC_BUS_ACQUIRE_AND_WAKEUP && wilc->power_save_mode) {
  ret = chip_wakeup(wilc);
  if (ret)
   mutex_unlock(&wilc->hif_cs);
 }

 return ret;
}

static inline int release_bus(struct wilc *wilc, enum bus_release release)
{
 int ret = 0;

 if (release == WILC_BUS_RELEASE_ALLOW_SLEEP && wilc->power_save_mode)
  ret = chip_allow_sleep(wilc);
 mutex_unlock(&wilc->hif_cs);

 return ret;
}

int host_wakeup_notify(struct wilc *wilc)
{
 int ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);

 if (ret)
  return ret;

 wilc->hif_func->hif_write_reg(wilc, is_wilc1000(wilc->chipid) ?
         WILC1000_CORTUS_INTERRUPT_2 :
         WILC3000_CORTUS_INTERRUPT_2, 1);
 return release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
}
EXPORT_SYMBOL_GPL(host_wakeup_notify);

int host_sleep_notify(struct wilc *wilc)
{
 int ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);

 if (ret)
  return ret;

 wilc->hif_func->hif_write_reg(wilc, is_wilc1000(wilc->chipid) ?
         WILC1000_CORTUS_INTERRUPT_1 :
         WILC3000_CORTUS_INTERRUPT_1, 1);
 return release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
}
EXPORT_SYMBOL_GPL(host_sleep_notify);

int wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count)
{
 int i, entries = 0;
 u8 k, ac;
 u32 sum;
 u32 reg;
 u8 ac_desired_ratio[NQUEUES] = {0, 0, 0, 0};
 u8 ac_preserve_ratio[NQUEUES] = {1, 1, 1, 1};
 u8 *num_pkts_to_add;
 u8 vmm_entries_ac[WILC_VMM_TBL_SIZE];
 u32 offset = 0;
 bool max_size_over = 0, ac_exist = 0;
 int vmm_sz = 0;
 struct txq_entry_t *tqe_q[NQUEUES];
 int ret = 0;
 int counter;
 int timeout;
 u32 *vmm_table = wilc->vmm_table;
 u8 ac_pkt_num_to_chip[NQUEUES] = {0, 0, 0, 0};
 const struct wilc_hif_func *func;
 int srcu_idx;
 u8 *txb = wilc->tx_buffer;
 struct wilc_vif *vif;
 int rv;

 if (wilc->quit)
  goto out_update_cnt;

 if (ac_balance(wilc, ac_desired_ratio))
  return -EINVAL;

 mutex_lock(&wilc->txq_add_to_head_cs);

 srcu_idx = srcu_read_lock(&wilc->srcu);
 wilc_for_each_vif(wilc, vif)
  wilc_wlan_txq_filter_dup_tcp_ack(vif->ndev);
 srcu_read_unlock(&wilc->srcu, srcu_idx);

 for (ac = 0; ac < NQUEUES; ac++)
  tqe_q[ac] = wilc_wlan_txq_get_first(wilc, ac);

 i = 0;
 sum = 0;
 max_size_over = 0;
 num_pkts_to_add = ac_desired_ratio;
 do {
  ac_exist = 0;
  for (ac = 0; (ac < NQUEUES) && (!max_size_over); ac++) {
   if (!tqe_q[ac])
    continue;

   ac_exist = 1;
   for (k = 0; (k < num_pkts_to_add[ac]) &&
        (!max_size_over) && tqe_q[ac]; k++) {
    if (i >= (WILC_VMM_TBL_SIZE - 1)) {
     max_size_over = 1;
     break;
    }

    if (tqe_q[ac]->type == WILC_CFG_PKT)
     vmm_sz = ETH_CONFIG_PKT_HDR_OFFSET;
    else if (tqe_q[ac]->type == WILC_NET_PKT)
     vmm_sz = ETH_ETHERNET_HDR_OFFSET;
    else
     vmm_sz = HOST_HDR_OFFSET;

    vmm_sz += tqe_q[ac]->buffer_size;
    vmm_sz = ALIGN(vmm_sz, 4);

    if ((sum + vmm_sz) > WILC_TX_BUFF_SIZE) {
     max_size_over = 1;
     break;
    }
    vmm_table[i] = vmm_sz / 4;
    if (tqe_q[ac]->type == WILC_CFG_PKT)
     vmm_table[i] |= BIT(10);

    cpu_to_le32s(&vmm_table[i]);
    vmm_entries_ac[i] = ac;

    i++;
    sum += vmm_sz;
    tqe_q[ac] = wilc_wlan_txq_get_next(wilc,
           tqe_q[ac],
           ac);
   }
  }
  num_pkts_to_add = ac_preserve_ratio;
 } while (!max_size_over && ac_exist);

 if (i == 0)
  goto out_unlock;
 vmm_table[i] = 0x0;

 ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
 if (ret)
  goto out_unlock;

 counter = 0;
 func = wilc->hif_func;
 do {
  ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, ®);
  if (ret)
   break;

  if ((reg & 0x1) == 0) {
   ac_update_fw_ac_pkt_info(wilc, reg);
   break;
  }

  counter++;
  if (counter > 200) {
   counter = 0;
   ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, 0);
   break;
  }
 } while (!wilc->quit);

 if (ret)
  goto out_release_bus;

 timeout = 200;
 do {
  ret = func->hif_block_tx(wilc,
      WILC_VMM_TBL_RX_SHADOW_BASE,
      (u8 *)vmm_table,
      ((i + 1) * 4));
  if (ret)
   break;

  if (is_wilc1000(wilc->chipid)) {
   ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x2);
   if (ret)
    break;

   do {
    ret = func->hif_read_reg(wilc, WILC_HOST_VMM_CTL, ®);
    if (ret)
     break;
    if (FIELD_GET(WILC_VMM_ENTRY_AVAILABLE, reg)) {
     entries = FIELD_GET(WILC_VMM_ENTRY_COUNT, reg);
     break;
    }
   } while (--timeout);
  } else {
   ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0);
   if (ret)
    break;

   /* interrupt firmware */
   ret = func->hif_write_reg(wilc, WILC_CORTUS_INTERRUPT_BASE, 1);
   if (ret)
    break;

   do {
    ret = func->hif_read_reg(wilc, WILC_CORTUS_INTERRUPT_BASE, ®);
    if (ret)
     break;
    if (reg == 0) {
     /* Get the entries */
     ret = func->hif_read_reg(wilc, WILC_HOST_VMM_CTL, ®);
     if (ret)
      break;

     entries = FIELD_GET(WILC_VMM_ENTRY_COUNT, reg);
     break;
    }
   } while (--timeout);
  }
  if (timeout <= 0) {
   ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x0);
   break;
  }

  if (ret)
   break;

  if (entries == 0) {
   ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, ®);
   if (ret)
    break;
   reg &= ~BIT(0);
   ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, reg);
  }
 } while (0);

 if (ret)
  goto out_release_bus;

 if (entries == 0) {
  /*
 * No VMM space available in firmware so retry to transmit
 * the packet from tx queue.
 */

  ret = WILC_VMM_ENTRY_FULL_RETRY;
  goto out_release_bus;
 }

 ret = release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
 if (ret)
  goto out_unlock;

 offset = 0;
 i = 0;
 do {
  struct txq_entry_t *tqe;
  u32 header, buffer_offset;
  char *bssid;
  u8 mgmt_ptk = 0;

  if (vmm_table[i] == 0 || vmm_entries_ac[i] >= NQUEUES)
   break;

  tqe = wilc_wlan_txq_remove_from_head(wilc, vmm_entries_ac[i]);
  if (!tqe)
   break;

  ac_pkt_num_to_chip[vmm_entries_ac[i]]++;
  vif = tqe->vif;

  le32_to_cpus(&vmm_table[i]);
  vmm_sz = FIELD_GET(WILC_VMM_BUFFER_SIZE, vmm_table[i]);
  vmm_sz *= 4;

  if (tqe->type == WILC_MGMT_PKT)
   mgmt_ptk = 1;

  header = (FIELD_PREP(WILC_VMM_HDR_TYPE, tqe->type) |
     FIELD_PREP(WILC_VMM_HDR_MGMT_FIELD, mgmt_ptk) |
     FIELD_PREP(WILC_VMM_HDR_PKT_SIZE, tqe->buffer_size) |
     FIELD_PREP(WILC_VMM_HDR_BUFF_SIZE, vmm_sz));

  cpu_to_le32s(&header);
  memcpy(&txb[offset], &header, 4);
  if (tqe->type == WILC_CFG_PKT) {
   buffer_offset = ETH_CONFIG_PKT_HDR_OFFSET;
  } else if (tqe->type == WILC_NET_PKT) {
   int prio = tqe->q_num;

   bssid = tqe->vif->bssid;
   buffer_offset = ETH_ETHERNET_HDR_OFFSET;
   memcpy(&txb[offset + 4], &prio, sizeof(prio));
   memcpy(&txb[offset + 8], bssid, 6);
  } else {
   buffer_offset = HOST_HDR_OFFSET;
  }

  memcpy(&txb[offset + buffer_offset],
         tqe->buffer, tqe->buffer_size);
  offset += vmm_sz;
  i++;
  tqe->status = 1;
  if (tqe->tx_complete_func)
   tqe->tx_complete_func(tqe->priv, tqe->status);
  if (tqe->ack_idx != NOT_TCP_ACK &&
      tqe->ack_idx < MAX_PENDING_ACKS)
   vif->ack_filter.pending_acks[tqe->ack_idx].txqe = NULL;
  kfree(tqe);
 } while (--entries);
 for (i = 0; i < NQUEUES; i++)
  wilc->txq[i].fw.count += ac_pkt_num_to_chip[i];

 ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
 if (ret)
  goto out_unlock;

 ret = func->hif_clear_int_ext(wilc, ENABLE_TX_VMM);
 if (ret)
  goto out_release_bus;

 ret = func->hif_block_tx_ext(wilc, 0, txb, offset);

out_release_bus:
 rv = release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
 if (!ret && rv)
  ret = rv;

out_unlock:
 mutex_unlock(&wilc->txq_add_to_head_cs);

out_update_cnt:
 *txq_count = wilc->txq_entries;
 return ret;
}

static void wilc_wlan_handle_rx_buff(struct wilc *wilc, u8 *buffer, int size)
{
 int offset = 0;
 u32 header;
 u32 pkt_len, pkt_offset, tp_len;
 int is_cfg_packet;
 u8 *buff_ptr;

 do {
  buff_ptr = buffer + offset;
  header = get_unaligned_le32(buff_ptr);

  is_cfg_packet = FIELD_GET(WILC_PKT_HDR_CONFIG_FIELD, header);
  pkt_offset = FIELD_GET(WILC_PKT_HDR_OFFSET_FIELD, header);
  tp_len = FIELD_GET(WILC_PKT_HDR_TOTAL_LEN_FIELD, header);
  pkt_len = FIELD_GET(WILC_PKT_HDR_LEN_FIELD, header);

  if (pkt_len == 0 || tp_len == 0)
   break;

  if (pkt_offset & IS_MANAGMEMENT) {
   buff_ptr += HOST_HDR_OFFSET;
   wilc_wfi_mgmt_rx(wilc, buff_ptr, pkt_len,
      pkt_offset & IS_MGMT_AUTH_PKT);
  } else {
   if (!is_cfg_packet) {
    wilc_frmw_to_host(wilc, buff_ptr, pkt_len,
        pkt_offset);
   } else {
    struct wilc_cfg_rsp rsp;

    buff_ptr += pkt_offset;

    wilc_wlan_cfg_indicate_rx(wilc, buff_ptr,
         pkt_len,
         &rsp);
    if (rsp.type == WILC_CFG_RSP) {
     if (wilc->cfg_seq_no == rsp.seq_no)
      complete(&wilc->cfg_event);
    } else if (rsp.type == WILC_CFG_RSP_STATUS) {
     wilc_mac_indicate(wilc);
    }
   }
  }
  offset += tp_len;
 } while (offset < size);
}

static void wilc_wlan_handle_rxq(struct wilc *wilc)
{
 int size;
 u8 *buffer;
 struct rxq_entry_t *rqe;

 while (!wilc->quit) {
  rqe = wilc_wlan_rxq_remove(wilc);
  if (!rqe)
   break;

  buffer = rqe->buffer;
  size = rqe->buffer_size;
  wilc_wlan_handle_rx_buff(wilc, buffer, size);

  kfree(rqe);
 }
 if (wilc->quit)
  complete(&wilc->cfg_event);
}

static void wilc_unknown_isr_ext(struct wilc *wilc)
{
 wilc->hif_func->hif_clear_int_ext(wilc, 0);
}

static void wilc_wlan_handle_isr_ext(struct wilc *wilc, u32 int_status)
{
 u32 offset = wilc->rx_buffer_offset;
 u8 *buffer = NULL;
 u32 size;
 u32 retries = 0;
 int ret = 0;
 struct rxq_entry_t *rqe;

 size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, int_status) << 2;

 while (!size && retries < 10) {
  wilc->hif_func->hif_read_size(wilc, &size);
  size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, size) << 2;
  retries++;
 }

 if (size <= 0)
  return;

 if (WILC_RX_BUFF_SIZE - offset < size)
  offset = 0;

 buffer = &wilc->rx_buffer[offset];

 wilc->hif_func->hif_clear_int_ext(wilc, DATA_INT_CLR | ENABLE_RX_VMM);
 ret = wilc->hif_func->hif_block_rx_ext(wilc, 0, buffer, size);
 if (ret)
  return;

 offset += size;
 wilc->rx_buffer_offset = offset;
 rqe = kmalloc(sizeof(*rqe), GFP_KERNEL);
 if (!rqe)
  return;

 rqe->buffer = buffer;
 rqe->buffer_size = size;
 wilc_wlan_rxq_add(wilc, rqe);
 wilc_wlan_handle_rxq(wilc);
}

void wilc_handle_isr(struct wilc *wilc)
{
 u32 int_status;
 int ret;

 ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
 if (ret) {
  dev_err_ratelimited(wilc->dev, "Cannot acquire bus\n");
  return;
 }

 wilc->hif_func->hif_read_int(wilc, &int_status);

 if (int_status & DATA_INT_EXT)
  wilc_wlan_handle_isr_ext(wilc, int_status);

 if (!(int_status & (ALL_INT_EXT)))
  wilc_unknown_isr_ext(wilc);

 ret = release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
 if (ret)
  dev_err_ratelimited(wilc->dev, "Cannot release bus\n");
}
EXPORT_SYMBOL_GPL(wilc_handle_isr);

int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer,
    u32 buffer_size)
{
 u32 offset;
 u32 addr, size, size2, blksz;
 u8 *dma_buffer;
 int ret = 0;
 u32 reg = 0;
 int rv;

 blksz = BIT(12);

 dma_buffer = kmalloc(blksz, GFP_KERNEL);
 if (!dma_buffer)
  return -EIO;

 offset = 0;
 pr_debug("%s: Downloading firmware size = %d\n", __func__, buffer_size);

 ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
 if (ret)
  return ret;

 wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®);
 reg &= ~BIT(10);
 ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
 wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®);
 if (reg & BIT(10))
  pr_err("%s: Failed to reset\n", __func__);

 ret = release_bus(wilc, WILC_BUS_RELEASE_ONLY);
 if (ret)
  goto fail;

 do {
  addr = get_unaligned_le32(&buffer[offset]);
  size = get_unaligned_le32(&buffer[offset + 4]);
  ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
  if (ret)
   goto fail;

  offset += 8;
  while (((int)size) && (offset < buffer_size)) {
   size2 = min(size, blksz);

   memcpy(dma_buffer, &buffer[offset], size2);
   ret = wilc->hif_func->hif_block_tx(wilc, addr,
          dma_buffer, size2);
   if (ret)
    break;

   addr += size2;
   offset += size2;
   size -= size2;
  }
  rv = release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
  if (!ret && rv)
   ret = rv;

  if (ret) {
   pr_err("%s Bus error\n", __func__);
   goto fail;
  }
  pr_debug("%s Offset = %d\n", __func__, offset);
 } while (offset < buffer_size);

fail:

 kfree(dma_buffer);

 return ret;
}

int wilc_wlan_start(struct wilc *wilc)
{
 u32 reg = 0;
 int ret, rv;
 u32 chipid;

 if (wilc->io_type == WILC_HIF_SDIO) {
  reg = 0;
  reg |= BIT(3);
 } else if (wilc->io_type == WILC_HIF_SPI) {
  reg = 1;
 }
 ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY);
 if (ret)
  return ret;

 ret = wilc->hif_func->hif_write_reg(wilc, WILC_VMM_CORE_CFG, reg);
 if (ret)
  goto release;

 reg = 0;
 if (wilc->io_type == WILC_HIF_SDIO && wilc->dev_irq_num)
  reg |= WILC_HAVE_SDIO_IRQ_GPIO;

 if (is_wilc3000(wilc->chipid))
  reg |= WILC_HAVE_SLEEP_CLK_SRC_RTC;

 ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_1, reg);
 if (ret)
  goto release;

 wilc->hif_func->hif_sync_ext(wilc, NUM_INT_EXT);

 ret = wilc->hif_func->hif_read_reg(wilc, WILC_CHIPID, &chipid);
 if (ret)
  goto release;

 wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®);
 if ((reg & BIT(10)) == BIT(10)) {
  reg &= ~BIT(10);
  wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
  wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®);
 }

 reg |= BIT(10);
 ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
 wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®);

release:
 rv = release_bus(wilc, WILC_BUS_RELEASE_ONLY);
 return ret ? ret : rv;
}

int wilc_wlan_stop(struct wilc *wilc, struct wilc_vif *vif)
{
 u32 reg = 0;
 int ret, rv;

 ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
 if (ret)
  return ret;

 ret = wilc->hif_func->hif_read_reg(wilc, GLOBAL_MODE_CONTROL, ®);
 if (ret)
  goto release;

 reg &= ~WILC_GLOBAL_MODE_ENABLE_WIFI;
 ret = wilc->hif_func->hif_write_reg(wilc, GLOBAL_MODE_CONTROL, reg);
 if (ret)
  goto release;

 ret = wilc->hif_func->hif_read_reg(wilc, PWR_SEQ_MISC_CTRL, ®);
 if (ret)
  goto release;

 reg &= ~WILC_PWR_SEQ_ENABLE_WIFI_SLEEP;
 ret = wilc->hif_func->hif_write_reg(wilc, PWR_SEQ_MISC_CTRL, reg);
 if (ret)
  goto release;

 ret = wilc->hif_func->hif_read_reg(wilc, WILC_GP_REG_0, ®);
 if (ret) {
  netdev_err(vif->ndev, "Error while reading reg\n");
  goto release;
 }

 ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_0,
     (reg | WILC_ABORT_REQ_BIT));
 if (ret) {
  netdev_err(vif->ndev, "Error while writing reg\n");
  goto release;
 }

 ret = 0;
release:
 /* host comm is disabled - we can't issue sleep command anymore: */
 rv = release_bus(wilc, WILC_BUS_RELEASE_ONLY);

 return ret ? ret : rv;
}

void wilc_wlan_cleanup(struct net_device *dev)
{
 struct txq_entry_t *tqe;
 struct rxq_entry_t *rqe;
 u8 ac;
 struct wilc_vif *vif = netdev_priv(dev);
 struct wilc *wilc = vif->wilc;

 wilc->quit = 1;
 for (ac = 0; ac < NQUEUES; ac++) {
  while ((tqe = wilc_wlan_txq_remove_from_head(wilc, ac))) {
   if (tqe->tx_complete_func)
    tqe->tx_complete_func(tqe->priv, 0);
   kfree(tqe);
  }
 }

 while ((rqe = wilc_wlan_rxq_remove(wilc)))
  kfree(rqe);

 kfree(wilc->vmm_table);
 wilc->vmm_table = NULL;
 kfree(wilc->rx_buffer);
 wilc->rx_buffer = NULL;
 kfree(wilc->tx_buffer);
 wilc->tx_buffer = NULL;
 wilc->hif_func->hif_deinit(wilc);
}

static int wilc_wlan_cfg_commit(struct wilc_vif *vif, int type,
    u32 drv_handler)
{
 struct wilc *wilc = vif->wilc;
 struct wilc_cfg_frame *cfg = &wilc->cfg_frame;
 int t_len = wilc->cfg_frame_offset + sizeof(struct wilc_cfg_cmd_hdr);

 if (type == WILC_CFG_SET)
  cfg->hdr.cmd_type = 'W';
 else
  cfg->hdr.cmd_type = 'Q';

 cfg->hdr.seq_no = wilc->cfg_seq_no % 256;
 cfg->hdr.total_len = cpu_to_le16(t_len);
 cfg->hdr.driver_handler = cpu_to_le32(drv_handler);
 wilc->cfg_seq_no = cfg->hdr.seq_no;

 if (!wilc_wlan_txq_add_cfg_pkt(vif, (u8 *)&cfg->hdr, t_len))
  return -1;

 return 0;
}

int wilc_wlan_cfg_set(struct wilc_vif *vif, int start, u16 wid, u8 *buffer,
        u32 buffer_size, int commit, u32 drv_handler)
{
 u32 offset;
 int ret_size;
 struct wilc *wilc = vif->wilc;

 mutex_lock(&wilc->cfg_cmd_lock);

 if (start)
  wilc->cfg_frame_offset = 0;

 offset = wilc->cfg_frame_offset;
 ret_size = wilc_wlan_cfg_set_wid(wilc->cfg_frame.frame, offset,
      wid, buffer, buffer_size);
 offset += ret_size;
 wilc->cfg_frame_offset = offset;

 if (!commit) {
  mutex_unlock(&wilc->cfg_cmd_lock);
  return ret_size;
 }

 netdev_dbg(vif->ndev, "%s: seqno[%d]\n", __func__, wilc->cfg_seq_no);

 if (wilc_wlan_cfg_commit(vif, WILC_CFG_SET, drv_handler))
  ret_size = 0;

 if (!wait_for_completion_timeout(&wilc->cfg_event,
      WILC_CFG_PKTS_TIMEOUT)) {
  netdev_dbg(vif->ndev, "%s: Timed Out\n", __func__);
  ret_size = 0;
 }

 wilc->cfg_frame_offset = 0;
 wilc->cfg_seq_no += 1;
 mutex_unlock(&wilc->cfg_cmd_lock);

 return ret_size;
}

int wilc_wlan_cfg_get(struct wilc_vif *vif, int start, u16 wid, int commit,
        u32 drv_handler)
{
 u32 offset;
 int ret_size;
 struct wilc *wilc = vif->wilc;

 mutex_lock(&wilc->cfg_cmd_lock);

 if (start)
  wilc->cfg_frame_offset = 0;

 offset = wilc->cfg_frame_offset;
 ret_size = wilc_wlan_cfg_get_wid(wilc->cfg_frame.frame, offset, wid);
 offset += ret_size;
 wilc->cfg_frame_offset = offset;

 if (!commit) {
  mutex_unlock(&wilc->cfg_cmd_lock);
  return ret_size;
 }

 if (wilc_wlan_cfg_commit(vif, WILC_CFG_QUERY, drv_handler))
  ret_size = 0;

 if (!wait_for_completion_timeout(&wilc->cfg_event,
      WILC_CFG_PKTS_TIMEOUT)) {
  netdev_dbg(vif->ndev, "%s: Timed Out\n", __func__);
  ret_size = 0;
 }
 wilc->cfg_frame_offset = 0;
 wilc->cfg_seq_no += 1;
 mutex_unlock(&wilc->cfg_cmd_lock);

 return ret_size;
}

int wilc_send_config_pkt(struct wilc_vif *vif, u8 mode, struct wid *wids,
    u32 count)
{
 int i;
 int ret = 0;
 u32 drv = wilc_get_vif_idx(vif);

 if (mode == WILC_GET_CFG) {
  for (i = 0; i < count; i++) {
   if (!wilc_wlan_cfg_get(vif, !i,
            wids[i].id,
            (i == count - 1),
            drv)) {
    ret = -ETIMEDOUT;
    break;
   }
  }
  for (i = 0; i < count; i++) {
   wids[i].size = wilc_wlan_cfg_get_val(vif->wilc,
            wids[i].id,
            wids[i].val,
            wids[i].size);
  }
 } else if (mode == WILC_SET_CFG) {
  for (i = 0; i < count; i++) {
   if (!wilc_wlan_cfg_set(vif, !i,
            wids[i].id,
            wids[i].val,
            wids[i].size,
            (i == count - 1),
            drv)) {
    ret = -ETIMEDOUT;
    break;
   }
  }
 }

 return ret;
}

int wilc_get_chipid(struct wilc *wilc)
{
 u32 chipid = 0;
 u32 rfrevid = 0;

 if (wilc->chipid == 0) {
  wilc->hif_func->hif_read_reg(wilc, WILC3000_CHIP_ID, &chipid);
  if (!is_wilc3000(chipid)) {
   wilc->hif_func->hif_read_reg(wilc, WILC_CHIPID, &chipid);
   wilc->hif_func->hif_read_reg(wilc, WILC_RF_REVISION_ID,
           &rfrevid);

   if (!is_wilc1000(chipid)) {
    wilc->chipid = 0;
    return -EINVAL;
   }
   if (chipid == WILC_1000_BASE_ID_2A) { /* 0x1002A0 */
    if (rfrevid != 0x1)
     chipid = WILC_1000_BASE_ID_2A_REV1;
   } else if (chipid == WILC_1000_BASE_ID_2B) { /* 0x1002B0 */
    if (rfrevid == 0x4)
     chipid = WILC_1000_BASE_ID_2B_REV1;
    else if (rfrevid != 0x3)
     chipid = WILC_1000_BASE_ID_2B_REV2;
   }
  }

  wilc->chipid = chipid;
 }

 return 0;
}
EXPORT_SYMBOL_GPL(wilc_get_chipid);

static int init_chip(struct net_device *dev)
{
 u32 reg;
 int ret, rv;
 struct wilc_vif *vif = netdev_priv(dev);
 struct wilc *wilc = vif->wilc;

 ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
 if (ret)
  return ret;

 ret = wilc_get_chipid(wilc);
 if (ret)
  goto release;

 if ((wilc->chipid & 0xfff) != 0xa0) {
  ret = wilc->hif_func->hif_read_reg(wilc,
         WILC_CORTUS_RESET_MUX_SEL,
         ®);
  if (ret) {
   netdev_err(dev, "fail read reg 0x1118\n");
   goto release;
  }
  reg |= BIT(0);
  ret = wilc->hif_func->hif_write_reg(wilc,
          WILC_CORTUS_RESET_MUX_SEL,
          reg);
  if (ret) {
   netdev_err(dev, "fail write reg 0x1118\n");
   goto release;
  }
  ret = wilc->hif_func->hif_write_reg(wilc,
          WILC_CORTUS_BOOT_REGISTER,
          WILC_CORTUS_BOOT_FROM_IRAM);
  if (ret) {
   netdev_err(dev, "fail write reg 0xc0000\n");
   goto release;
  }
 }

 if (is_wilc3000(wilc->chipid)) {
  ret = wilc->hif_func->hif_read_reg(wilc, WILC3000_BOOTROM_STATUS, ®);
  if (ret) {
   netdev_err(dev, "failed to read WILC3000 BootROM status register\n");
   goto release;
  }

  ret = wilc->hif_func->hif_write_reg(wilc, WILC3000_CORTUS_BOOT_REGISTER_2,
          WILC_CORTUS_BOOT_FROM_IRAM);
  if (ret) {
   netdev_err(dev, "failed to write WILC3000 Boot register\n");
   goto release;
  }
 }

release:
 rv = release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);

 return ret ? ret : rv;
}

int wilc_load_mac_from_nv(struct wilc *wl)
{
 int ret, rv;
 unsigned int i;

 ret = acquire_bus(wl, WILC_BUS_ACQUIRE_AND_WAKEUP);
 if (ret)
  return ret;

 for (i = 0; i < WILC_NVMEM_MAX_NUM_BANK; i++) {
  int bank_offset = get_bank_offset_from_bank_index(i);
  u32 reg1, reg2;
  u8 invalid;
  u8 used;

  ret = wl->hif_func->hif_read_reg(wl,
       WILC_NVMEM_BANK_BASE + bank_offset,
       ®1);
  if (ret) {
   pr_err("Can not read address %d lower part", i);
   break;
  }
  ret = wl->hif_func->hif_read_reg(wl,
       WILC_NVMEM_BANK_BASE + bank_offset + 4,
       ®2);
  if (ret) {
   pr_err("Can not read address %d upper part", i);
   break;
  }

  used = FIELD_GET(WILC_NVMEM_IS_BANK_USED, reg1);
  invalid = FIELD_GET(WILC_NVMEM_IS_BANK_INVALID, reg1);
  if (!used || invalid)
   continue;

  wl->nv_mac_address[0] = FIELD_GET(GENMASK(23, 16), reg1);
  wl->nv_mac_address[1] = FIELD_GET(GENMASK(15, 8), reg1);
  wl->nv_mac_address[2] = FIELD_GET(GENMASK(7, 0), reg1);
  wl->nv_mac_address[3] = FIELD_GET(GENMASK(31, 24), reg2);
  wl->nv_mac_address[4] = FIELD_GET(GENMASK(23, 16), reg2);
  wl->nv_mac_address[5] = FIELD_GET(GENMASK(15, 8), reg2);

  ret = 0;
  break;
 }

 rv = release_bus(wl, WILC_BUS_RELEASE_ALLOW_SLEEP);
 return ret ? ret : rv;
}
EXPORT_SYMBOL_GPL(wilc_load_mac_from_nv);

int wilc_wlan_init(struct net_device *dev)
{
 int ret = 0, rv;
 struct wilc_vif *vif = netdev_priv(dev);
 struct wilc *wilc;

 wilc = vif->wilc;

 wilc->quit = 0;

 if (!wilc->hif_func->hif_is_init(wilc)) {
  ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY);
  if (ret)
   return ret;

  ret = wilc->hif_func->hif_init(wilc, false);
  if (!ret)
   ret = wilc_get_chipid(wilc);
  rv = release_bus(wilc, WILC_BUS_RELEASE_ONLY);
  if (!ret && rv)
   ret = rv;
  if (ret)
   goto fail;

  if (!is_wilc1000(wilc->chipid) && !is_wilc3000(wilc->chipid)) {
   netdev_err(dev, "Unsupported chipid: %x\n", wilc->chipid);
   ret = -EINVAL;
   goto fail;
  }

  netdev_dbg(dev, "chipid (%08x)\n", wilc->chipid);
 }

 if (!wilc->vmm_table)
  wilc->vmm_table = kcalloc(WILC_VMM_TBL_SIZE, sizeof(u32), GFP_KERNEL);

 if (!wilc->vmm_table) {
  ret = -ENOBUFS;
  goto fail;
 }

 if (!wilc->tx_buffer)
  wilc->tx_buffer = kmalloc(WILC_TX_BUFF_SIZE, GFP_KERNEL);

 if (!wilc->tx_buffer) {
  ret = -ENOBUFS;
  goto fail;
 }

 if (!wilc->rx_buffer)
  wilc->rx_buffer = kmalloc(WILC_RX_BUFF_SIZE, GFP_KERNEL);

 if (!wilc->rx_buffer) {
  ret = -ENOBUFS;
  goto fail;
 }

 if (init_chip(dev)) {
  ret = -EIO;
  goto fail;
 }

 return 0;

fail:
 kfree(wilc->vmm_table);
 wilc->vmm_table = NULL;
 kfree(wilc->rx_buffer);
 wilc->rx_buffer = NULL;
 kfree(wilc->tx_buffer);
 wilc->tx_buffer = NULL;

 return ret;
}

Messung V0.5
C=99 H=85 G=92

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge