Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
authorDavid S. Miller <davem@davemloft.net>
Tue, 8 May 2012 03:35:40 +0000 (23:35 -0400)
committerDavid S. Miller <davem@davemloft.net>
Tue, 8 May 2012 03:35:40 +0000 (23:35 -0400)
Conflicts:
drivers/net/ethernet/intel/e1000e/param.c
drivers/net/wireless/iwlwifi/iwl-agn-rx.c
drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
drivers/net/wireless/iwlwifi/iwl-trans.h

Resolved the iwlwifi conflict with mainline using 3-way diff posted
by John Linville and Stephen Rothwell.  In 'net' we added a bug
fix to make iwlwifi report a more accurate skb->truesize but this
conflicted with RX path changes that happened meanwhile in net-next.

In e1000e a conflict arose in the validation code for settings of
adapter->itr.  'net-next' had more sophisticated logic so that
logic was used.

Signed-off-by: David S. Miller <davem@davemloft.net>
61 files changed:
1  2 
Documentation/networking/ip-sysctl.txt
MAINTAINERS
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/dlink/dl2k.c
drivers/net/ethernet/dlink/dl2k.h
drivers/net/ethernet/intel/e1000/e1000_main.c
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/e1000e/param.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c
drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/marvell/sky2.c
drivers/net/ethernet/sun/sungem.c
drivers/net/ethernet/ti/davinci_emac.c
drivers/net/ethernet/ti/tlan.c
drivers/net/usb/usbnet.c
drivers/net/wireless/ath/ath9k/ar5008_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/eeprom_9287.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/b43/main.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/ipw2x00/ipw2200.c
drivers/net/wireless/iwlwifi/iwl-1000.c
drivers/net/wireless/iwlwifi/iwl-2000.c
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-agn-rx.c
drivers/net/wireless/iwlwifi/iwl-agn.c
drivers/net/wireless/iwlwifi/iwl-mac80211.c
drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/rtlwifi/pci.c
drivers/net/wireless/ti/wl1251/main.c
drivers/net/wireless/ti/wl1251/sdio.c
drivers/staging/octeon/ethernet-tx.c
include/linux/skbuff.h
include/net/ip_vs.h
include/net/sock.h
net/bridge/br_forward.c
net/bridge/br_netfilter.c
net/ieee802154/6lowpan.c
net/ipv4/inet_diag.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/l2tp/l2tp_ip.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/mlme.c
net/mac80211/tx.c
net/netfilter/ipvs/ip_vs_core.c
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/ipvs/ip_vs_ftp.c
net/netfilter/ipvs/ip_vs_lblc.c
net/netfilter/ipvs/ip_vs_lblcr.c
net/netfilter/ipvs/ip_vs_proto.c
net/sched/sch_netem.c

diff --cc MAINTAINERS
Simple merge
Simple merge
Simple merge
Simple merge
index feb6eebb002172d87d486640eee964e168307a54,16adeb9418a8903e5ccbb7503e513f1918978930..42444d14aae6b21d8693f2b7e62f0156b3b64240
@@@ -344,53 -344,60 +344,94 @@@ void __devinit e1000e_check_options(str
  
                if (num_InterruptThrottleRate > bd) {
                        adapter->itr = InterruptThrottleRate[bd];
 -
 -                      /*
 -                       * Make sure a message is printed for non-special
 -                       * values.  And in case of an invalid option, display
 -                       * warning, use default and got through itr/itr_setting
 -                       * adjustment logic below
 -                       */
 -                      if ((adapter->itr > 4) &&
 -                          e1000_validate_option(&adapter->itr, &opt, adapter))
 -                              adapter->itr = opt.def;
 +                      switch (adapter->itr) {
 +                      case 0:
 +                              e_info("%s turned off\n", opt.name);
 +                              break;
 +                      case 1:
 +                              e_info("%s set to dynamic mode\n", opt.name);
 +                              adapter->itr_setting = adapter->itr;
 +                              adapter->itr = 20000;
 +                              break;
 +                      case 3:
 +                              e_info("%s set to dynamic conservative mode\n",
 +                                      opt.name);
 +                              adapter->itr_setting = adapter->itr;
 +                              adapter->itr = 20000;
 +                              break;
 +                      case 4:
 +                              e_info("%s set to simplified (2000-8000 ints) mode\n",
 +                                     opt.name);
 +                              adapter->itr_setting = 4;
 +                              break;
 +                      default:
 +                              /*
 +                               * Save the setting, because the dynamic bits
 +                               * change itr.
 +                               */
 +                              if (e1000_validate_option(&adapter->itr, &opt,
 +                                                        adapter) &&
 +                                  (adapter->itr == 3)) {
 +                                      /*
 +                                       * In case of invalid user value,
 +                                       * default to conservative mode.
 +                                       */
 +                                      adapter->itr_setting = adapter->itr;
 +                                      adapter->itr = 20000;
 +                              } else {
 +                                      /*
 +                                       * Clear the lower two bits because
 +                                       * they are used as control.
 +                                       */
 +                                      adapter->itr_setting =
 +                                              adapter->itr & ~3;
 +                              }
 +                              break;
 +                      }
                } else {
-                       adapter->itr_setting = opt.def;
+                       /*
+                        * If no option specified, use default value and go
+                        * through the logic below to adjust itr/itr_setting
+                        */
+                       adapter->itr = opt.def;
+                       /*
+                        * Make sure a message is printed for non-special
+                        * default values
+                        */
+                       if (adapter->itr > 40)
+                               e_info("%s set to default %d\n", opt.name,
+                                      adapter->itr);
+               }
+               adapter->itr_setting = adapter->itr;
+               switch (adapter->itr) {
+               case 0:
+                       e_info("%s turned off\n", opt.name);
+                       break;
+               case 1:
+                       e_info("%s set to dynamic mode\n", opt.name);
+                       adapter->itr = 20000;
+                       break;
+               case 3:
+                       e_info("%s set to dynamic conservative mode\n",
+                              opt.name);
                        adapter->itr = 20000;
+                       break;
+               case 4:
+                       e_info("%s set to simplified (2000-8000 ints) mode\n",
+                              opt.name);
+                       break;
+               default:
+                       /*
+                        * Save the setting, because the dynamic bits
+                        * change itr.
+                        *
+                        * Clear the lower two bits because
+                        * they are used as control.
+                        */
+                       adapter->itr_setting &= ~3;
+                       break;
                }
        }
        { /* Interrupt Mode */
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 8c7a0cd13854ee1eb3e467a611df4b5a55195248,8d80e233bc7a73aec74b267edb61bd191bdf5eb2..2629a6602dfaa88465e869f265ff58ce4feae1ec
   *
   *****************************************************************************/
  
 -#include <linux/kernel.h>
  #include <linux/module.h>
 -#include <linux/init.h>
 -#include <linux/delay.h>
 -#include <linux/skbuff.h>
 -#include <linux/netdevice.h>
 -#include <net/mac80211.h>
 -#include <linux/etherdevice.h>
 -#include <asm/unaligned.h>
  #include <linux/stringify.h>
 -
 -#include "iwl-eeprom.h"
 -#include "iwl-dev.h"
 -#include "iwl-core.h"
 -#include "iwl-io.h"
 -#include "iwl-agn.h"
 -#include "iwl-agn-hw.h"
 -#include "iwl-shared.h"
 +#include "iwl-config.h"
  #include "iwl-cfg.h"
 -#include "iwl-prph.h"
 +#include "iwl-csr.h"
 +#include "iwl-agn-hw.h"
  
  /* Highest firmware API version supported */
- #define IWL1000_UCODE_API_MAX 6
- #define IWL100_UCODE_API_MAX 6
+ #define IWL1000_UCODE_API_MAX 5
+ #define IWL100_UCODE_API_MAX 5
  
  /* Oldest version we won't warn about */
  #define IWL1000_UCODE_API_OK 5
index 67d4ef26043b4aa76b679bfb09d9ed0d9c52597c,de0920c74cdd2369b7a528de29536163aba01f50..8e26bc825f23bdae076eec51ab3d218cb531704f
@@@ -70,8 -330,8 +74,9 @@@ static const struct iwl_ht_params iwl50
  #define IWL_DEVICE_5000                                               \
        .fw_name_pre = IWL5000_FW_PRE,                          \
        .ucode_api_max = IWL5000_UCODE_API_MAX,                 \
+       .ucode_api_ok = IWL5000_UCODE_API_OK,                   \
        .ucode_api_min = IWL5000_UCODE_API_MIN,                 \
 +      .device_family = IWL_DEVICE_FAMILY_5000,                \
        .max_inst_size = IWLAGN_RTC_INST_SIZE,                  \
        .max_data_size = IWLAGN_RTC_DATA_SIZE,                  \
        .eeprom_ver = EEPROM_5000_EEPROM_VERSION,               \
@@@ -115,8 -376,8 +120,9 @@@ const struct iwl_cfg iwl5350_agn_cfg = 
        .name = "Intel(R) WiMAX/WiFi Link 5350 AGN",
        .fw_name_pre = IWL5000_FW_PRE,
        .ucode_api_max = IWL5000_UCODE_API_MAX,
+       .ucode_api_ok = IWL5000_UCODE_API_OK,
        .ucode_api_min = IWL5000_UCODE_API_MIN,
 +      .device_family = IWL_DEVICE_FAMILY_5000,
        .max_inst_size = IWLAGN_RTC_INST_SIZE,
        .max_data_size = IWLAGN_RTC_DATA_SIZE,
        .eeprom_ver = EEPROM_5050_EEPROM_VERSION,
  #define IWL_DEVICE_5150                                               \
        .fw_name_pre = IWL5150_FW_PRE,                          \
        .ucode_api_max = IWL5150_UCODE_API_MAX,                 \
+       .ucode_api_ok = IWL5150_UCODE_API_OK,                   \
        .ucode_api_min = IWL5150_UCODE_API_MIN,                 \
 +      .device_family = IWL_DEVICE_FAMILY_5150,                \
        .max_inst_size = IWLAGN_RTC_INST_SIZE,                  \
        .max_data_size = IWLAGN_RTC_DATA_SIZE,                  \
        .eeprom_ver = EEPROM_5050_EEPROM_VERSION,               \
index 08afedf4b3ec82f10de43a861fb9c08060235ba3,f0c91505a7f775ac0e8cb819dca0be1bfc983094..381b02cf339c46e0353b07bf04f201b70f49a4bf
@@@ -190,9 -390,8 +192,9 @@@ const struct iwl_cfg iwl6005_2agn_mow2_
  #define IWL_DEVICE_6030                                               \
        .fw_name_pre = IWL6030_FW_PRE,                          \
        .ucode_api_max = IWL6000G2_UCODE_API_MAX,               \
-       .ucode_api_ok = IWL6000G2_UCODE_API_OK,                 \
+       .ucode_api_ok = IWL6000G2B_UCODE_API_OK,                \
        .ucode_api_min = IWL6000G2_UCODE_API_MIN,               \
 +      .device_family = IWL_DEVICE_FAMILY_6030,                \
        .max_inst_size = IWL60_RTC_INST_SIZE,                   \
        .max_data_size = IWL60_RTC_DATA_SIZE,                   \
        .eeprom_ver = EEPROM_6030_EEPROM_VERSION,               \
index f94122387e25bcbc9a065f5e5bd4f96519682538,22474608a70b343843980f2ad08d4edaa5cad59c..0c252c5d8bf13543f3f40121ce671812b6559c95
@@@ -752,15 -787,25 +751,24 @@@ static void iwlagn_pass_packet_to_mac80
            iwlagn_set_decrypted_flag(priv, hdr, ampdu_status, stats))
                return;
  
-       skb = dev_alloc_skb(128);
+       /* Dont use dev_alloc_skb(), we'll have enough headroom once
+        * ieee80211_hdr pulled.
+        */
+       skb = alloc_skb(128, GFP_ATOMIC);
        if (!skb) {
-               IWL_ERR(priv, "dev_alloc_skb failed\n");
+               IWL_ERR(priv, "alloc_skb failed\n");
                return;
        }
 -              int offset = (void *)hdr + hdrlen - rxb_addr(rxb);
+       hdrlen = min_t(unsigned int, len, skb_tailroom(skb));
+       memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
+       fraglen = len - hdrlen;
+       if (fraglen) {
++              int offset = (void *)hdr - rxb_addr(rxb) + rxb_offset(rxb);
  
-       offset = (void *)hdr - rxb_addr(rxb) + rxb_offset(rxb);
-       p = rxb_steal_page(rxb);
-       skb_add_rx_frag(skb, 0, p, offset, len, len);
+               skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
+                               fraglen, rxb->truesize);
+       }
 -      iwl_update_stats(priv, false, fc, len);
  
        /*
        * Wake any queues that were stopped due to a passive channel tx
index d2239aad7f114b4f44fa0a89d463b45d45a014f6,aa7aea1681387e37939ec0450ad633d8f6bf2c75..08517d3c80bb203e302825e6349814db9963b0bb
@@@ -373,89 -374,72 +373,90 @@@ static void iwl_rx_handle_rxbuf(struct 
        if (WARN_ON(!rxb))
                return;
  
 -      rxcb.truesize = PAGE_SIZE << hw_params(trans).rx_page_order;
 -      dma_unmap_page(trans->dev, rxb->page_dma,
 -                     rxcb.truesize,
 -                     DMA_FROM_DEVICE);
 -
 -      rxcb._page = rxb->page;
 -      pkt = rxb_addr(&rxcb);
 -
 -      IWL_DEBUG_RX(trans, "%s, 0x%02x\n",
 -                   get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
 +      dma_unmap_page(trans->dev, rxb->page_dma, max_len, DMA_FROM_DEVICE);
  
 +      while (offset + sizeof(u32) + sizeof(struct iwl_cmd_header) < max_len) {
 +              struct iwl_rx_packet *pkt;
 +              struct iwl_device_cmd *cmd;
 +              u16 sequence;
 +              bool reclaim;
 +              int index, cmd_index, err, len;
 +              struct iwl_rx_cmd_buffer rxcb = {
 +                      ._offset = offset,
 +                      ._page = rxb->page,
 +                      ._page_stolen = false,
++                      .truesize = max_len,
 +              };
  
 -      len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
 -      len += sizeof(u32); /* account for status word */
 -      trace_iwlwifi_dev_rx(trans->dev, pkt, len);
 +              pkt = rxb_addr(&rxcb);
  
 -      /* Reclaim a command buffer only if this packet is a response
 -       *   to a (driver-originated) command.
 -       * If the packet (e.g. Rx frame) originated from uCode,
 -       *   there is no command buffer to reclaim.
 -       * Ucode should set SEQ_RX_FRAME bit if ucode-originated,
 -       *   but apparently a few don't get set; catch them here. */
 -      reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME);
 -      if (reclaim) {
 -              int i;
 +              if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID))
 +                      break;
  
 -              for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) {
 -                      if (trans_pcie->no_reclaim_cmds[i] == pkt->hdr.cmd) {
 -                              reclaim = false;
 -                              break;
 +              IWL_DEBUG_RX(trans, "cmd at offset %d: %s (0x%.2x)\n",
 +                      rxcb._offset,
 +                      trans_pcie_get_cmd_string(trans_pcie, pkt->hdr.cmd),
 +                      pkt->hdr.cmd);
 +
 +              len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
 +              len += sizeof(u32); /* account for status word */
 +              trace_iwlwifi_dev_rx(trans->dev, pkt, len);
 +
 +              /* Reclaim a command buffer only if this packet is a response
 +               *   to a (driver-originated) command.
 +               * If the packet (e.g. Rx frame) originated from uCode,
 +               *   there is no command buffer to reclaim.
 +               * Ucode should set SEQ_RX_FRAME bit if ucode-originated,
 +               *   but apparently a few don't get set; catch them here. */
 +              reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME);
 +              if (reclaim) {
 +                      int i;
 +
 +                      for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) {
 +                              if (trans_pcie->no_reclaim_cmds[i] ==
 +                                                      pkt->hdr.cmd) {
 +                                      reclaim = false;
 +                                      break;
 +                              }
                        }
                }
 -      }
  
 -      sequence = le16_to_cpu(pkt->hdr.sequence);
 -      index = SEQ_TO_INDEX(sequence);
 -      cmd_index = get_cmd_index(&txq->q, index);
 +              sequence = le16_to_cpu(pkt->hdr.sequence);
 +              index = SEQ_TO_INDEX(sequence);
 +              cmd_index = get_cmd_index(&txq->q, index);
  
 -      if (reclaim)
 -              cmd = txq->cmd[cmd_index];
 -      else
 -              cmd = NULL;
 +              if (reclaim)
 +                      cmd = txq->entries[cmd_index].cmd;
 +              else
 +                      cmd = NULL;
  
 -      err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd);
 +              err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd);
  
 -      /*
 -       * XXX: After here, we should always check rxcb._page
 -       * against NULL before touching it or its virtual
 -       * memory (pkt). Because some rx_handler might have
 -       * already taken or freed the pages.
 -       */
 +              /*
 +               * After here, we should always check rxcb._page_stolen,
 +               * if it is true then one of the handlers took the page.
 +               */
  
 -      if (reclaim) {
 -              /* Invoke any callbacks, transfer the buffer to caller,
 -               * and fire off the (possibly) blocking
 -               * iwl_trans_send_cmd()
 -               * as we reclaim the driver command queue */
 -              if (rxcb._page)
 -                      iwl_tx_cmd_complete(trans, &rxcb, err);
 -              else
 -                      IWL_WARN(trans, "Claim null rxb?\n");
 +              if (reclaim) {
 +                      /* Invoke any callbacks, transfer the buffer to caller,
 +                       * and fire off the (possibly) blocking
 +                       * iwl_trans_send_cmd()
 +                       * as we reclaim the driver command queue */
 +                      if (!rxcb._page_stolen)
 +                              iwl_tx_cmd_complete(trans, &rxcb, err);
 +                      else
 +                              IWL_WARN(trans, "Claim null rxb?\n");
 +              }
 +
 +              page_stolen |= rxcb._page_stolen;
 +              offset += ALIGN(len, FH_RSCSR_FRAME_ALIGN);
        }
  
 -      /* page was stolen from us */
 -      if (rxcb._page == NULL)
 +      /* page was stolen from us -- free our reference */
 +      if (page_stolen) {
 +              __free_pages(rxb->page, trans_pcie->rx_page_order);
                rxb->page = NULL;
 +      }
  
        /* Reuse the page if possible. For notification packets and
         * SKBs that fail to Rx correctly, add them back into the
index 7018d313a4e03e99a9fb59ba7b3eefb9573c3c1d,fdf97886a5e40c2e74648a48c5670b25f3e3ccd9..79a1e7ae4995d615b2e46370bd786350239ecf73
@@@ -256,8 -260,7 +256,9 @@@ static inline void iwl_free_resp(struc
  
  struct iwl_rx_cmd_buffer {
        struct page *_page;
 +      int _offset;
 +      bool _page_stolen;
+       unsigned int truesize;
  };
  
  static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r)
Simple merge
index 41302c7b1ad0089480a495a26dac4da2552eecf2,0000000000000000000000000000000000000000..d1afb8e3b2ef0a3673b5ff9ef60f98379180bfa3
mode 100644,000000..100644
--- /dev/null
@@@ -1,1471 -1,0 +1,1472 @@@
 +/*
 + * This file is part of wl1251
 + *
 + * Copyright (C) 2008-2009 Nokia Corporation
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * version 2 as published by the Free Software Foundation.
 + *
 + * This program is distributed in the hope that it will be useful, but
 + * WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 + * 02110-1301 USA
 + *
 + */
 +
 +#include <linux/module.h>
 +#include <linux/interrupt.h>
 +#include <linux/firmware.h>
 +#include <linux/delay.h>
 +#include <linux/irq.h>
 +#include <linux/crc32.h>
 +#include <linux/etherdevice.h>
 +#include <linux/vmalloc.h>
 +#include <linux/slab.h>
 +
 +#include "wl1251.h"
 +#include "wl12xx_80211.h"
 +#include "reg.h"
 +#include "io.h"
 +#include "cmd.h"
 +#include "event.h"
 +#include "tx.h"
 +#include "rx.h"
 +#include "ps.h"
 +#include "init.h"
 +#include "debugfs.h"
 +#include "boot.h"
 +
 +void wl1251_enable_interrupts(struct wl1251 *wl)
 +{
 +      wl->if_ops->enable_irq(wl);
 +}
 +
 +void wl1251_disable_interrupts(struct wl1251 *wl)
 +{
 +      wl->if_ops->disable_irq(wl);
 +}
 +
 +static int wl1251_power_off(struct wl1251 *wl)
 +{
 +      return wl->if_ops->power(wl, false);
 +}
 +
 +static int wl1251_power_on(struct wl1251 *wl)
 +{
 +      return wl->if_ops->power(wl, true);
 +}
 +
 +static int wl1251_fetch_firmware(struct wl1251 *wl)
 +{
 +      const struct firmware *fw;
 +      struct device *dev = wiphy_dev(wl->hw->wiphy);
 +      int ret;
 +
 +      ret = request_firmware(&fw, WL1251_FW_NAME, dev);
 +
 +      if (ret < 0) {
 +              wl1251_error("could not get firmware: %d", ret);
 +              return ret;
 +      }
 +
 +      if (fw->size % 4) {
 +              wl1251_error("firmware size is not multiple of 32 bits: %zu",
 +                           fw->size);
 +              ret = -EILSEQ;
 +              goto out;
 +      }
 +
 +      wl->fw_len = fw->size;
 +      wl->fw = vmalloc(wl->fw_len);
 +
 +      if (!wl->fw) {
 +              wl1251_error("could not allocate memory for the firmware");
 +              ret = -ENOMEM;
 +              goto out;
 +      }
 +
 +      memcpy(wl->fw, fw->data, wl->fw_len);
 +
 +      ret = 0;
 +
 +out:
 +      release_firmware(fw);
 +
 +      return ret;
 +}
 +
 +static int wl1251_fetch_nvs(struct wl1251 *wl)
 +{
 +      const struct firmware *fw;
 +      struct device *dev = wiphy_dev(wl->hw->wiphy);
 +      int ret;
 +
 +      ret = request_firmware(&fw, WL1251_NVS_NAME, dev);
 +
 +      if (ret < 0) {
 +              wl1251_error("could not get nvs file: %d", ret);
 +              return ret;
 +      }
 +
 +      if (fw->size % 4) {
 +              wl1251_error("nvs size is not multiple of 32 bits: %zu",
 +                           fw->size);
 +              ret = -EILSEQ;
 +              goto out;
 +      }
 +
 +      wl->nvs_len = fw->size;
 +      wl->nvs = kmemdup(fw->data, wl->nvs_len, GFP_KERNEL);
 +
 +      if (!wl->nvs) {
 +              wl1251_error("could not allocate memory for the nvs file");
 +              ret = -ENOMEM;
 +              goto out;
 +      }
 +
 +      ret = 0;
 +
 +out:
 +      release_firmware(fw);
 +
 +      return ret;
 +}
 +
 +static void wl1251_fw_wakeup(struct wl1251 *wl)
 +{
 +      u32 elp_reg;
 +
 +      elp_reg = ELPCTRL_WAKE_UP;
 +      wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
 +      elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
 +
 +      if (!(elp_reg & ELPCTRL_WLAN_READY))
 +              wl1251_warning("WLAN not ready");
 +}
 +
 +static int wl1251_chip_wakeup(struct wl1251 *wl)
 +{
 +      int ret;
 +
 +      ret = wl1251_power_on(wl);
 +      if (ret < 0)
 +              return ret;
 +
 +      msleep(WL1251_POWER_ON_SLEEP);
 +      wl->if_ops->reset(wl);
 +
 +      /* We don't need a real memory partition here, because we only want
 +       * to use the registers at this point. */
 +      wl1251_set_partition(wl,
 +                           0x00000000,
 +                           0x00000000,
 +                           REGISTERS_BASE,
 +                           REGISTERS_DOWN_SIZE);
 +
 +      /* ELP module wake up */
 +      wl1251_fw_wakeup(wl);
 +
 +      /* whal_FwCtrl_BootSm() */
 +
 +      /* 0. read chip id from CHIP_ID */
 +      wl->chip_id = wl1251_reg_read32(wl, CHIP_ID_B);
 +
 +      /* 1. check if chip id is valid */
 +
 +      switch (wl->chip_id) {
 +      case CHIP_ID_1251_PG12:
 +              wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG12)",
 +                           wl->chip_id);
 +              break;
 +      case CHIP_ID_1251_PG11:
 +              wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG11)",
 +                           wl->chip_id);
 +              break;
 +      case CHIP_ID_1251_PG10:
 +      default:
 +              wl1251_error("unsupported chip id: 0x%x", wl->chip_id);
 +              ret = -ENODEV;
 +              goto out;
 +      }
 +
 +      if (wl->fw == NULL) {
 +              ret = wl1251_fetch_firmware(wl);
 +              if (ret < 0)
 +                      goto out;
 +      }
 +
 +      if (wl->nvs == NULL && !wl->use_eeprom) {
 +              /* No NVS from netlink, try to get it from the filesystem */
 +              ret = wl1251_fetch_nvs(wl);
 +              if (ret < 0)
 +                      goto out;
 +      }
 +
 +out:
 +      return ret;
 +}
 +
 +#define WL1251_IRQ_LOOP_COUNT 10
 +static void wl1251_irq_work(struct work_struct *work)
 +{
 +      u32 intr, ctr = WL1251_IRQ_LOOP_COUNT;
 +      struct wl1251 *wl =
 +              container_of(work, struct wl1251, irq_work);
 +      int ret;
 +
 +      mutex_lock(&wl->mutex);
 +
 +      wl1251_debug(DEBUG_IRQ, "IRQ work");
 +
 +      if (wl->state == WL1251_STATE_OFF)
 +              goto out;
 +
 +      ret = wl1251_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1251_ACX_INTR_ALL);
 +
 +      intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR);
 +      wl1251_debug(DEBUG_IRQ, "intr: 0x%x", intr);
 +
 +      do {
 +              if (wl->data_path) {
 +                      wl->rx_counter = wl1251_mem_read32(
 +                              wl, wl->data_path->rx_control_addr);
 +
 +                      /* We handle a frmware bug here */
 +                      switch ((wl->rx_counter - wl->rx_handled) & 0xf) {
 +                      case 0:
 +                              wl1251_debug(DEBUG_IRQ,
 +                                           "RX: FW and host in sync");
 +                              intr &= ~WL1251_ACX_INTR_RX0_DATA;
 +                              intr &= ~WL1251_ACX_INTR_RX1_DATA;
 +                              break;
 +                      case 1:
 +                              wl1251_debug(DEBUG_IRQ, "RX: FW +1");
 +                              intr |= WL1251_ACX_INTR_RX0_DATA;
 +                              intr &= ~WL1251_ACX_INTR_RX1_DATA;
 +                              break;
 +                      case 2:
 +                              wl1251_debug(DEBUG_IRQ, "RX: FW +2");
 +                              intr |= WL1251_ACX_INTR_RX0_DATA;
 +                              intr |= WL1251_ACX_INTR_RX1_DATA;
 +                              break;
 +                      default:
 +                              wl1251_warning(
 +                                      "RX: FW and host out of sync: %d",
 +                                      wl->rx_counter - wl->rx_handled);
 +                              break;
 +                      }
 +
 +                      wl->rx_handled = wl->rx_counter;
 +
 +                      wl1251_debug(DEBUG_IRQ, "RX counter: %d",
 +                                   wl->rx_counter);
 +              }
 +
 +              intr &= wl->intr_mask;
 +
 +              if (intr == 0) {
 +                      wl1251_debug(DEBUG_IRQ, "INTR is 0");
 +                      goto out_sleep;
 +              }
 +
 +              if (intr & WL1251_ACX_INTR_RX0_DATA) {
 +                      wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA");
 +                      wl1251_rx(wl);
 +              }
 +
 +              if (intr & WL1251_ACX_INTR_RX1_DATA) {
 +                      wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA");
 +                      wl1251_rx(wl);
 +              }
 +
 +              if (intr & WL1251_ACX_INTR_TX_RESULT) {
 +                      wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT");
 +                      wl1251_tx_complete(wl);
 +              }
 +
 +              if (intr & WL1251_ACX_INTR_EVENT_A) {
 +                      wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT_A");
 +                      wl1251_event_handle(wl, 0);
 +              }
 +
 +              if (intr & WL1251_ACX_INTR_EVENT_B) {
 +                      wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT_B");
 +                      wl1251_event_handle(wl, 1);
 +              }
 +
 +              if (intr & WL1251_ACX_INTR_INIT_COMPLETE)
 +                      wl1251_debug(DEBUG_IRQ,
 +                                   "WL1251_ACX_INTR_INIT_COMPLETE");
 +
 +              if (--ctr == 0)
 +                      break;
 +
 +              intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR);
 +      } while (intr);
 +
 +out_sleep:
 +      wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask));
 +      wl1251_ps_elp_sleep(wl);
 +
 +out:
 +      mutex_unlock(&wl->mutex);
 +}
 +
 +static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel,
 +                     u16 beacon_interval, u8 dtim_period)
 +{
 +      int ret;
 +
 +      ret = wl1251_acx_frame_rates(wl, DEFAULT_HW_GEN_TX_RATE,
 +                                   DEFAULT_HW_GEN_MODULATION_TYPE,
 +                                   wl->tx_mgmt_frm_rate,
 +                                   wl->tx_mgmt_frm_mod);
 +      if (ret < 0)
 +              goto out;
 +
 +
 +      ret = wl1251_cmd_join(wl, bss_type, channel, beacon_interval,
 +                            dtim_period);
 +      if (ret < 0)
 +              goto out;
 +
 +      ret = wl1251_event_wait(wl, JOIN_EVENT_COMPLETE_ID, 100);
 +      if (ret < 0)
 +              wl1251_warning("join timeout");
 +
 +out:
 +      return ret;
 +}
 +
 +static void wl1251_filter_work(struct work_struct *work)
 +{
 +      struct wl1251 *wl =
 +              container_of(work, struct wl1251, filter_work);
 +      int ret;
 +
 +      mutex_lock(&wl->mutex);
 +
 +      if (wl->state == WL1251_STATE_OFF)
 +              goto out;
 +
 +      ret = wl1251_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      ret = wl1251_join(wl, wl->bss_type, wl->channel, wl->beacon_int,
 +                        wl->dtim_period);
 +      if (ret < 0)
 +              goto out_sleep;
 +
 +out_sleep:
 +      wl1251_ps_elp_sleep(wl);
 +
 +out:
 +      mutex_unlock(&wl->mutex);
 +}
 +
 +static void wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 +{
 +      struct wl1251 *wl = hw->priv;
 +      unsigned long flags;
 +
 +      skb_queue_tail(&wl->tx_queue, skb);
 +
 +      /*
 +       * The chip specific setup must run before the first TX packet -
 +       * before that, the tx_work will not be initialized!
 +       */
 +
 +      ieee80211_queue_work(wl->hw, &wl->tx_work);
 +
 +      /*
 +       * The workqueue is slow to process the tx_queue and we need stop
 +       * the queue here, otherwise the queue will get too long.
 +       */
 +      if (skb_queue_len(&wl->tx_queue) >= WL1251_TX_QUEUE_HIGH_WATERMARK) {
 +              wl1251_debug(DEBUG_TX, "op_tx: tx_queue full, stop queues");
 +
 +              spin_lock_irqsave(&wl->wl_lock, flags);
 +              ieee80211_stop_queues(wl->hw);
 +              wl->tx_queue_stopped = true;
 +              spin_unlock_irqrestore(&wl->wl_lock, flags);
 +      }
 +}
 +
 +static int wl1251_op_start(struct ieee80211_hw *hw)
 +{
 +      struct wl1251 *wl = hw->priv;
 +      struct wiphy *wiphy = hw->wiphy;
 +      int ret = 0;
 +
 +      wl1251_debug(DEBUG_MAC80211, "mac80211 start");
 +
 +      mutex_lock(&wl->mutex);
 +
 +      if (wl->state != WL1251_STATE_OFF) {
 +              wl1251_error("cannot start because not in off state: %d",
 +                           wl->state);
 +              ret = -EBUSY;
 +              goto out;
 +      }
 +
 +      ret = wl1251_chip_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      ret = wl1251_boot(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      ret = wl1251_hw_init(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      ret = wl1251_acx_station_id(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      wl->state = WL1251_STATE_ON;
 +
 +      wl1251_info("firmware booted (%s)", wl->fw_ver);
 +
 +      /* update hw/fw version info in wiphy struct */
 +      wiphy->hw_version = wl->chip_id;
 +      strncpy(wiphy->fw_version, wl->fw_ver, sizeof(wiphy->fw_version));
 +
 +out:
 +      if (ret < 0)
 +              wl1251_power_off(wl);
 +
 +      mutex_unlock(&wl->mutex);
 +
 +      return ret;
 +}
 +
 +static void wl1251_op_stop(struct ieee80211_hw *hw)
 +{
 +      struct wl1251 *wl = hw->priv;
 +
 +      wl1251_info("down");
 +
 +      wl1251_debug(DEBUG_MAC80211, "mac80211 stop");
 +
 +      mutex_lock(&wl->mutex);
 +
 +      WARN_ON(wl->state != WL1251_STATE_ON);
 +
 +      if (wl->scanning) {
 +              ieee80211_scan_completed(wl->hw, true);
 +              wl->scanning = false;
 +      }
 +
 +      wl->state = WL1251_STATE_OFF;
 +
 +      wl1251_disable_interrupts(wl);
 +
 +      mutex_unlock(&wl->mutex);
 +
 +      cancel_work_sync(&wl->irq_work);
 +      cancel_work_sync(&wl->tx_work);
 +      cancel_work_sync(&wl->filter_work);
++      cancel_delayed_work_sync(&wl->elp_work);
 +
 +      mutex_lock(&wl->mutex);
 +
 +      /* let's notify MAC80211 about the remaining pending TX frames */
 +      wl1251_tx_flush(wl);
 +      wl1251_power_off(wl);
 +
 +      memset(wl->bssid, 0, ETH_ALEN);
 +      wl->listen_int = 1;
 +      wl->bss_type = MAX_BSS_TYPE;
 +
 +      wl->data_in_count = 0;
 +      wl->rx_counter = 0;
 +      wl->rx_handled = 0;
 +      wl->rx_current_buffer = 0;
 +      wl->rx_last_id = 0;
 +      wl->next_tx_complete = 0;
 +      wl->elp = false;
 +      wl->station_mode = STATION_ACTIVE_MODE;
 +      wl->tx_queue_stopped = false;
 +      wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
 +      wl->rssi_thold = 0;
 +      wl->channel = WL1251_DEFAULT_CHANNEL;
 +
 +      wl1251_debugfs_reset(wl);
 +
 +      mutex_unlock(&wl->mutex);
 +}
 +
 +static int wl1251_op_add_interface(struct ieee80211_hw *hw,
 +                                 struct ieee80211_vif *vif)
 +{
 +      struct wl1251 *wl = hw->priv;
 +      int ret = 0;
 +
 +      vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
 +                           IEEE80211_VIF_SUPPORTS_CQM_RSSI;
 +
 +      wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
 +                   vif->type, vif->addr);
 +
 +      mutex_lock(&wl->mutex);
 +      if (wl->vif) {
 +              ret = -EBUSY;
 +              goto out;
 +      }
 +
 +      wl->vif = vif;
 +
 +      switch (vif->type) {
 +      case NL80211_IFTYPE_STATION:
 +              wl->bss_type = BSS_TYPE_STA_BSS;
 +              break;
 +      case NL80211_IFTYPE_ADHOC:
 +              wl->bss_type = BSS_TYPE_IBSS;
 +              break;
 +      default:
 +              ret = -EOPNOTSUPP;
 +              goto out;
 +      }
 +
 +      if (memcmp(wl->mac_addr, vif->addr, ETH_ALEN)) {
 +              memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
 +              SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
 +              ret = wl1251_acx_station_id(wl);
 +              if (ret < 0)
 +                      goto out;
 +      }
 +
 +out:
 +      mutex_unlock(&wl->mutex);
 +      return ret;
 +}
 +
 +static void wl1251_op_remove_interface(struct ieee80211_hw *hw,
 +                                       struct ieee80211_vif *vif)
 +{
 +      struct wl1251 *wl = hw->priv;
 +
 +      mutex_lock(&wl->mutex);
 +      wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface");
 +      wl->vif = NULL;
 +      mutex_unlock(&wl->mutex);
 +}
 +
 +static int wl1251_build_qos_null_data(struct wl1251 *wl)
 +{
 +      struct ieee80211_qos_hdr template;
 +
 +      memset(&template, 0, sizeof(template));
 +
 +      memcpy(template.addr1, wl->bssid, ETH_ALEN);
 +      memcpy(template.addr2, wl->mac_addr, ETH_ALEN);
 +      memcpy(template.addr3, wl->bssid, ETH_ALEN);
 +
 +      template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
 +                                           IEEE80211_STYPE_QOS_NULLFUNC |
 +                                           IEEE80211_FCTL_TODS);
 +
 +      /* FIXME: not sure what priority to use here */
 +      template.qos_ctrl = cpu_to_le16(0);
 +
 +      return wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, &template,
 +                                     sizeof(template));
 +}
 +
 +static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
 +{
 +      struct wl1251 *wl = hw->priv;
 +      struct ieee80211_conf *conf = &hw->conf;
 +      int channel, ret = 0;
 +
 +      channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
 +
 +      wl1251_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d",
 +                   channel,
 +                   conf->flags & IEEE80211_CONF_PS ? "on" : "off",
 +                   conf->power_level);
 +
 +      mutex_lock(&wl->mutex);
 +
 +      ret = wl1251_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      if (channel != wl->channel) {
 +              wl->channel = channel;
 +
 +              ret = wl1251_join(wl, wl->bss_type, wl->channel,
 +                                wl->beacon_int, wl->dtim_period);
 +              if (ret < 0)
 +                      goto out_sleep;
 +      }
 +
 +      if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) {
 +              wl1251_debug(DEBUG_PSM, "psm enabled");
 +
 +              wl->psm_requested = true;
 +
 +              wl->dtim_period = conf->ps_dtim_period;
 +
 +              ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int,
 +                                                wl->dtim_period);
 +
 +              /*
 +               * mac80211 enables PSM only if we're already associated.
 +               */
 +              ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
 +              if (ret < 0)
 +                      goto out_sleep;
 +      } else if (!(conf->flags & IEEE80211_CONF_PS) &&
 +                 wl->psm_requested) {
 +              wl1251_debug(DEBUG_PSM, "psm disabled");
 +
 +              wl->psm_requested = false;
 +
 +              if (wl->station_mode != STATION_ACTIVE_MODE) {
 +                      ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE);
 +                      if (ret < 0)
 +                              goto out_sleep;
 +              }
 +      }
 +
 +      if (changed & IEEE80211_CONF_CHANGE_IDLE) {
 +              if (conf->flags & IEEE80211_CONF_IDLE) {
 +                      ret = wl1251_ps_set_mode(wl, STATION_IDLE);
 +                      if (ret < 0)
 +                              goto out_sleep;
 +              } else {
 +                      ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE);
 +                      if (ret < 0)
 +                              goto out_sleep;
 +                      ret = wl1251_join(wl, wl->bss_type, wl->channel,
 +                                        wl->beacon_int, wl->dtim_period);
 +                      if (ret < 0)
 +                              goto out_sleep;
 +              }
 +      }
 +
 +      if (conf->power_level != wl->power_level) {
 +              ret = wl1251_acx_tx_power(wl, conf->power_level);
 +              if (ret < 0)
 +                      goto out_sleep;
 +
 +              wl->power_level = conf->power_level;
 +      }
 +
 +out_sleep:
 +      wl1251_ps_elp_sleep(wl);
 +
 +out:
 +      mutex_unlock(&wl->mutex);
 +
 +      return ret;
 +}
 +
 +#define WL1251_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
 +                                FIF_ALLMULTI | \
 +                                FIF_FCSFAIL | \
 +                                FIF_BCN_PRBRESP_PROMISC | \
 +                                FIF_CONTROL | \
 +                                FIF_OTHER_BSS)
 +
 +static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
 +                                     unsigned int changed,
 +                                     unsigned int *total,u64 multicast)
 +{
 +      struct wl1251 *wl = hw->priv;
 +
 +      wl1251_debug(DEBUG_MAC80211, "mac80211 configure filter");
 +
 +      *total &= WL1251_SUPPORTED_FILTERS;
 +      changed &= WL1251_SUPPORTED_FILTERS;
 +
 +      if (changed == 0)
 +              /* no filters which we support changed */
 +              return;
 +
 +      /* FIXME: wl->rx_config and wl->rx_filter are not protected */
 +
 +      wl->rx_config = WL1251_DEFAULT_RX_CONFIG;
 +      wl->rx_filter = WL1251_DEFAULT_RX_FILTER;
 +
 +      if (*total & FIF_PROMISC_IN_BSS) {
 +              wl->rx_config |= CFG_BSSID_FILTER_EN;
 +              wl->rx_config |= CFG_RX_ALL_GOOD;
 +      }
 +      if (*total & FIF_ALLMULTI)
 +              /*
 +               * CFG_MC_FILTER_EN in rx_config needs to be 0 to receive
 +               * all multicast frames
 +               */
 +              wl->rx_config &= ~CFG_MC_FILTER_EN;
 +      if (*total & FIF_FCSFAIL)
 +              wl->rx_filter |= CFG_RX_FCS_ERROR;
 +      if (*total & FIF_BCN_PRBRESP_PROMISC) {
 +              wl->rx_config &= ~CFG_BSSID_FILTER_EN;
 +              wl->rx_config &= ~CFG_SSID_FILTER_EN;
 +      }
 +      if (*total & FIF_CONTROL)
 +              wl->rx_filter |= CFG_RX_CTL_EN;
 +      if (*total & FIF_OTHER_BSS)
 +              wl->rx_filter &= ~CFG_BSSID_FILTER_EN;
 +
 +      /*
 +       * FIXME: workqueues need to be properly cancelled on stop(), for
 +       * now let's just disable changing the filter settings. They will
 +       * be updated any on config().
 +       */
 +      /* schedule_work(&wl->filter_work); */
 +}
 +
 +/* HW encryption */
 +static int wl1251_set_key_type(struct wl1251 *wl,
 +                             struct wl1251_cmd_set_keys *key,
 +                             enum set_key_cmd cmd,
 +                             struct ieee80211_key_conf *mac80211_key,
 +                             const u8 *addr)
 +{
 +      switch (mac80211_key->cipher) {
 +      case WLAN_CIPHER_SUITE_WEP40:
 +      case WLAN_CIPHER_SUITE_WEP104:
 +              if (is_broadcast_ether_addr(addr))
 +                      key->key_type = KEY_WEP_DEFAULT;
 +              else
 +                      key->key_type = KEY_WEP_ADDR;
 +
 +              mac80211_key->hw_key_idx = mac80211_key->keyidx;
 +              break;
 +      case WLAN_CIPHER_SUITE_TKIP:
 +              if (is_broadcast_ether_addr(addr))
 +                      key->key_type = KEY_TKIP_MIC_GROUP;
 +              else
 +                      key->key_type = KEY_TKIP_MIC_PAIRWISE;
 +
 +              mac80211_key->hw_key_idx = mac80211_key->keyidx;
 +              break;
 +      case WLAN_CIPHER_SUITE_CCMP:
 +              if (is_broadcast_ether_addr(addr))
 +                      key->key_type = KEY_AES_GROUP;
 +              else
 +                      key->key_type = KEY_AES_PAIRWISE;
 +              mac80211_key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
 +              break;
 +      default:
 +              wl1251_error("Unknown key cipher 0x%x", mac80211_key->cipher);
 +              return -EOPNOTSUPP;
 +      }
 +
 +      return 0;
 +}
 +
 +static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 +                           struct ieee80211_vif *vif,
 +                           struct ieee80211_sta *sta,
 +                           struct ieee80211_key_conf *key)
 +{
 +      struct wl1251 *wl = hw->priv;
 +      struct wl1251_cmd_set_keys *wl_cmd;
 +      const u8 *addr;
 +      int ret;
 +
 +      static const u8 bcast_addr[ETH_ALEN] =
 +              { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 +
 +      wl1251_debug(DEBUG_MAC80211, "mac80211 set key");
 +
 +      wl_cmd = kzalloc(sizeof(*wl_cmd), GFP_KERNEL);
 +      if (!wl_cmd) {
 +              ret = -ENOMEM;
 +              goto out;
 +      }
 +
 +      addr = sta ? sta->addr : bcast_addr;
 +
 +      wl1251_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
 +      wl1251_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
 +      wl1251_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
 +                   key->cipher, key->keyidx, key->keylen, key->flags);
 +      wl1251_dump(DEBUG_CRYPT, "KEY: ", key->key, key->keylen);
 +
 +      if (is_zero_ether_addr(addr)) {
 +              /* We dont support TX only encryption */
 +              ret = -EOPNOTSUPP;
 +              goto out;
 +      }
 +
 +      mutex_lock(&wl->mutex);
 +
 +      ret = wl1251_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out_unlock;
 +
 +      switch (cmd) {
 +      case SET_KEY:
 +              wl_cmd->key_action = KEY_ADD_OR_REPLACE;
 +              break;
 +      case DISABLE_KEY:
 +              wl_cmd->key_action = KEY_REMOVE;
 +              break;
 +      default:
 +              wl1251_error("Unsupported key cmd 0x%x", cmd);
 +              break;
 +      }
 +
 +      ret = wl1251_set_key_type(wl, wl_cmd, cmd, key, addr);
 +      if (ret < 0) {
 +              wl1251_error("Set KEY type failed");
 +              goto out_sleep;
 +      }
 +
 +      if (wl_cmd->key_type != KEY_WEP_DEFAULT)
 +              memcpy(wl_cmd->addr, addr, ETH_ALEN);
 +
 +      if ((wl_cmd->key_type == KEY_TKIP_MIC_GROUP) ||
 +          (wl_cmd->key_type == KEY_TKIP_MIC_PAIRWISE)) {
 +              /*
 +               * We get the key in the following form:
 +               * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes)
 +               * but the target is expecting:
 +               * TKIP - RX MIC - TX MIC
 +               */
 +              memcpy(wl_cmd->key, key->key, 16);
 +              memcpy(wl_cmd->key + 16, key->key + 24, 8);
 +              memcpy(wl_cmd->key + 24, key->key + 16, 8);
 +
 +      } else {
 +              memcpy(wl_cmd->key, key->key, key->keylen);
 +      }
 +      wl_cmd->key_size = key->keylen;
 +
 +      wl_cmd->id = key->keyidx;
 +      wl_cmd->ssid_profile = 0;
 +
 +      wl1251_dump(DEBUG_CRYPT, "TARGET KEY: ", wl_cmd, sizeof(*wl_cmd));
 +
 +      ret = wl1251_cmd_send(wl, CMD_SET_KEYS, wl_cmd, sizeof(*wl_cmd));
 +      if (ret < 0) {
 +              wl1251_warning("could not set keys");
 +              goto out_sleep;
 +      }
 +
 +out_sleep:
 +      wl1251_ps_elp_sleep(wl);
 +
 +out_unlock:
 +      mutex_unlock(&wl->mutex);
 +
 +out:
 +      kfree(wl_cmd);
 +
 +      return ret;
 +}
 +
 +static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
 +                           struct ieee80211_vif *vif,
 +                           struct cfg80211_scan_request *req)
 +{
 +      struct wl1251 *wl = hw->priv;
 +      struct sk_buff *skb;
 +      size_t ssid_len = 0;
 +      u8 *ssid = NULL;
 +      int ret;
 +
 +      wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan");
 +
 +      if (req->n_ssids) {
 +              ssid = req->ssids[0].ssid;
 +              ssid_len = req->ssids[0].ssid_len;
 +      }
 +
 +      mutex_lock(&wl->mutex);
 +
 +      if (wl->scanning) {
 +              wl1251_debug(DEBUG_SCAN, "scan already in progress");
 +              ret = -EINVAL;
 +              goto out;
 +      }
 +
 +      ret = wl1251_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len,
 +                                   req->ie, req->ie_len);
 +      if (!skb) {
 +              ret = -ENOMEM;
 +              goto out;
 +      }
 +
 +      ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, skb->data,
 +                                    skb->len);
 +      dev_kfree_skb(skb);
 +      if (ret < 0)
 +              goto out_sleep;
 +
 +      ret = wl1251_cmd_trigger_scan_to(wl, 0);
 +      if (ret < 0)
 +              goto out_sleep;
 +
 +      wl->scanning = true;
 +
 +      ret = wl1251_cmd_scan(wl, ssid, ssid_len, req->channels,
 +                            req->n_channels, WL1251_SCAN_NUM_PROBES);
 +      if (ret < 0) {
 +              wl->scanning = false;
 +              goto out_sleep;
 +      }
 +
 +out_sleep:
 +      wl1251_ps_elp_sleep(wl);
 +
 +out:
 +      mutex_unlock(&wl->mutex);
 +
 +      return ret;
 +}
 +
 +static int wl1251_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 +{
 +      struct wl1251 *wl = hw->priv;
 +      int ret;
 +
 +      mutex_lock(&wl->mutex);
 +
 +      ret = wl1251_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      ret = wl1251_acx_rts_threshold(wl, (u16) value);
 +      if (ret < 0)
 +              wl1251_warning("wl1251_op_set_rts_threshold failed: %d", ret);
 +
 +      wl1251_ps_elp_sleep(wl);
 +
 +out:
 +      mutex_unlock(&wl->mutex);
 +
 +      return ret;
 +}
 +
 +static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
 +                                     struct ieee80211_vif *vif,
 +                                     struct ieee80211_bss_conf *bss_conf,
 +                                     u32 changed)
 +{
 +      struct wl1251 *wl = hw->priv;
 +      struct sk_buff *beacon, *skb;
 +      int ret;
 +
 +      wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed");
 +
 +      mutex_lock(&wl->mutex);
 +
 +      ret = wl1251_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      if (changed & BSS_CHANGED_CQM) {
 +              ret = wl1251_acx_low_rssi(wl, bss_conf->cqm_rssi_thold,
 +                                        WL1251_DEFAULT_LOW_RSSI_WEIGHT,
 +                                        WL1251_DEFAULT_LOW_RSSI_DEPTH,
 +                                        WL1251_ACX_LOW_RSSI_TYPE_EDGE);
 +              if (ret < 0)
 +                      goto out;
 +              wl->rssi_thold = bss_conf->cqm_rssi_thold;
 +      }
 +
 +      if (changed & BSS_CHANGED_BSSID) {
 +              memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
 +
 +              skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
 +              if (!skb)
 +                      goto out_sleep;
 +
 +              ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA,
 +                                            skb->data, skb->len);
 +              dev_kfree_skb(skb);
 +              if (ret < 0)
 +                      goto out_sleep;
 +
 +              ret = wl1251_build_qos_null_data(wl);
 +              if (ret < 0)
 +                      goto out;
 +
 +              if (wl->bss_type != BSS_TYPE_IBSS) {
 +                      ret = wl1251_join(wl, wl->bss_type, wl->channel,
 +                                        wl->beacon_int, wl->dtim_period);
 +                      if (ret < 0)
 +                              goto out_sleep;
 +              }
 +      }
 +
 +      if (changed & BSS_CHANGED_ASSOC) {
 +              if (bss_conf->assoc) {
 +                      wl->beacon_int = bss_conf->beacon_int;
 +
 +                      skb = ieee80211_pspoll_get(wl->hw, wl->vif);
 +                      if (!skb)
 +                              goto out_sleep;
 +
 +                      ret = wl1251_cmd_template_set(wl, CMD_PS_POLL,
 +                                                    skb->data,
 +                                                    skb->len);
 +                      dev_kfree_skb(skb);
 +                      if (ret < 0)
 +                              goto out_sleep;
 +
 +                      ret = wl1251_acx_aid(wl, bss_conf->aid);
 +                      if (ret < 0)
 +                              goto out_sleep;
 +              } else {
 +                      /* use defaults when not associated */
 +                      wl->beacon_int = WL1251_DEFAULT_BEACON_INT;
 +                      wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD;
 +              }
 +      }
 +      if (changed & BSS_CHANGED_ERP_SLOT) {
 +              if (bss_conf->use_short_slot)
 +                      ret = wl1251_acx_slot(wl, SLOT_TIME_SHORT);
 +              else
 +                      ret = wl1251_acx_slot(wl, SLOT_TIME_LONG);
 +              if (ret < 0) {
 +                      wl1251_warning("Set slot time failed %d", ret);
 +                      goto out_sleep;
 +              }
 +      }
 +
 +      if (changed & BSS_CHANGED_ERP_PREAMBLE) {
 +              if (bss_conf->use_short_preamble)
 +                      wl1251_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
 +              else
 +                      wl1251_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
 +      }
 +
 +      if (changed & BSS_CHANGED_ERP_CTS_PROT) {
 +              if (bss_conf->use_cts_prot)
 +                      ret = wl1251_acx_cts_protect(wl, CTSPROTECT_ENABLE);
 +              else
 +                      ret = wl1251_acx_cts_protect(wl, CTSPROTECT_DISABLE);
 +              if (ret < 0) {
 +                      wl1251_warning("Set ctsprotect failed %d", ret);
 +                      goto out_sleep;
 +              }
 +      }
 +
 +      if (changed & BSS_CHANGED_BEACON) {
 +              beacon = ieee80211_beacon_get(hw, vif);
 +              if (!beacon)
 +                      goto out_sleep;
 +
 +              ret = wl1251_cmd_template_set(wl, CMD_BEACON, beacon->data,
 +                                            beacon->len);
 +
 +              if (ret < 0) {
 +                      dev_kfree_skb(beacon);
 +                      goto out_sleep;
 +              }
 +
 +              ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, beacon->data,
 +                                            beacon->len);
 +
 +              dev_kfree_skb(beacon);
 +
 +              if (ret < 0)
 +                      goto out_sleep;
 +
 +              ret = wl1251_join(wl, wl->bss_type, wl->beacon_int,
 +                                wl->channel, wl->dtim_period);
 +
 +              if (ret < 0)
 +                      goto out_sleep;
 +      }
 +
 +out_sleep:
 +      wl1251_ps_elp_sleep(wl);
 +
 +out:
 +      mutex_unlock(&wl->mutex);
 +}
 +
 +
 +/* can't be const, mac80211 writes to this */
 +static struct ieee80211_rate wl1251_rates[] = {
 +      { .bitrate = 10,
 +        .hw_value = 0x1,
 +        .hw_value_short = 0x1, },
 +      { .bitrate = 20,
 +        .hw_value = 0x2,
 +        .hw_value_short = 0x2,
 +        .flags = IEEE80211_RATE_SHORT_PREAMBLE },
 +      { .bitrate = 55,
 +        .hw_value = 0x4,
 +        .hw_value_short = 0x4,
 +        .flags = IEEE80211_RATE_SHORT_PREAMBLE },
 +      { .bitrate = 110,
 +        .hw_value = 0x20,
 +        .hw_value_short = 0x20,
 +        .flags = IEEE80211_RATE_SHORT_PREAMBLE },
 +      { .bitrate = 60,
 +        .hw_value = 0x8,
 +        .hw_value_short = 0x8, },
 +      { .bitrate = 90,
 +        .hw_value = 0x10,
 +        .hw_value_short = 0x10, },
 +      { .bitrate = 120,
 +        .hw_value = 0x40,
 +        .hw_value_short = 0x40, },
 +      { .bitrate = 180,
 +        .hw_value = 0x80,
 +        .hw_value_short = 0x80, },
 +      { .bitrate = 240,
 +        .hw_value = 0x200,
 +        .hw_value_short = 0x200, },
 +      { .bitrate = 360,
 +       .hw_value = 0x400,
 +       .hw_value_short = 0x400, },
 +      { .bitrate = 480,
 +        .hw_value = 0x800,
 +        .hw_value_short = 0x800, },
 +      { .bitrate = 540,
 +        .hw_value = 0x1000,
 +        .hw_value_short = 0x1000, },
 +};
 +
 +/* can't be const, mac80211 writes to this */
 +static struct ieee80211_channel wl1251_channels[] = {
 +      { .hw_value = 1, .center_freq = 2412},
 +      { .hw_value = 2, .center_freq = 2417},
 +      { .hw_value = 3, .center_freq = 2422},
 +      { .hw_value = 4, .center_freq = 2427},
 +      { .hw_value = 5, .center_freq = 2432},
 +      { .hw_value = 6, .center_freq = 2437},
 +      { .hw_value = 7, .center_freq = 2442},
 +      { .hw_value = 8, .center_freq = 2447},
 +      { .hw_value = 9, .center_freq = 2452},
 +      { .hw_value = 10, .center_freq = 2457},
 +      { .hw_value = 11, .center_freq = 2462},
 +      { .hw_value = 12, .center_freq = 2467},
 +      { .hw_value = 13, .center_freq = 2472},
 +};
 +
 +static int wl1251_op_conf_tx(struct ieee80211_hw *hw,
 +                           struct ieee80211_vif *vif, u16 queue,
 +                           const struct ieee80211_tx_queue_params *params)
 +{
 +      enum wl1251_acx_ps_scheme ps_scheme;
 +      struct wl1251 *wl = hw->priv;
 +      int ret;
 +
 +      mutex_lock(&wl->mutex);
 +
 +      wl1251_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
 +
 +      ret = wl1251_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      /* mac80211 uses units of 32 usec */
 +      ret = wl1251_acx_ac_cfg(wl, wl1251_tx_get_queue(queue),
 +                              params->cw_min, params->cw_max,
 +                              params->aifs, params->txop * 32);
 +      if (ret < 0)
 +              goto out_sleep;
 +
 +      if (params->uapsd)
 +              ps_scheme = WL1251_ACX_PS_SCHEME_UPSD_TRIGGER;
 +      else
 +              ps_scheme = WL1251_ACX_PS_SCHEME_LEGACY;
 +
 +      ret = wl1251_acx_tid_cfg(wl, wl1251_tx_get_queue(queue),
 +                               CHANNEL_TYPE_EDCF,
 +                               wl1251_tx_get_queue(queue), ps_scheme,
 +                               WL1251_ACX_ACK_POLICY_LEGACY);
 +      if (ret < 0)
 +              goto out_sleep;
 +
 +out_sleep:
 +      wl1251_ps_elp_sleep(wl);
 +
 +out:
 +      mutex_unlock(&wl->mutex);
 +
 +      return ret;
 +}
 +
 +static int wl1251_op_get_survey(struct ieee80211_hw *hw, int idx,
 +                              struct survey_info *survey)
 +{
 +      struct wl1251 *wl = hw->priv;
 +      struct ieee80211_conf *conf = &hw->conf;
 + 
 +      if (idx != 0)
 +              return -ENOENT;
 + 
 +      survey->channel = conf->channel;
 +      survey->filled = SURVEY_INFO_NOISE_DBM;
 +      survey->noise = wl->noise;
 + 
 +      return 0;
 +}
 +
 +/* can't be const, mac80211 writes to this */
 +static struct ieee80211_supported_band wl1251_band_2ghz = {
 +      .channels = wl1251_channels,
 +      .n_channels = ARRAY_SIZE(wl1251_channels),
 +      .bitrates = wl1251_rates,
 +      .n_bitrates = ARRAY_SIZE(wl1251_rates),
 +};
 +
 +static const struct ieee80211_ops wl1251_ops = {
 +      .start = wl1251_op_start,
 +      .stop = wl1251_op_stop,
 +      .add_interface = wl1251_op_add_interface,
 +      .remove_interface = wl1251_op_remove_interface,
 +      .config = wl1251_op_config,
 +      .configure_filter = wl1251_op_configure_filter,
 +      .tx = wl1251_op_tx,
 +      .set_key = wl1251_op_set_key,
 +      .hw_scan = wl1251_op_hw_scan,
 +      .bss_info_changed = wl1251_op_bss_info_changed,
 +      .set_rts_threshold = wl1251_op_set_rts_threshold,
 +      .conf_tx = wl1251_op_conf_tx,
 +      .get_survey = wl1251_op_get_survey,
 +};
 +
 +static int wl1251_read_eeprom_byte(struct wl1251 *wl, off_t offset, u8 *data)
 +{
 +      unsigned long timeout;
 +
 +      wl1251_reg_write32(wl, EE_ADDR, offset);
 +      wl1251_reg_write32(wl, EE_CTL, EE_CTL_READ);
 +
 +      /* EE_CTL_READ clears when data is ready */
 +      timeout = jiffies + msecs_to_jiffies(100);
 +      while (1) {
 +              if (!(wl1251_reg_read32(wl, EE_CTL) & EE_CTL_READ))
 +                      break;
 +
 +              if (time_after(jiffies, timeout))
 +                      return -ETIMEDOUT;
 +
 +              msleep(1);
 +      }
 +
 +      *data = wl1251_reg_read32(wl, EE_DATA);
 +      return 0;
 +}
 +
 +static int wl1251_read_eeprom(struct wl1251 *wl, off_t offset,
 +                            u8 *data, size_t len)
 +{
 +      size_t i;
 +      int ret;
 +
 +      wl1251_reg_write32(wl, EE_START, 0);
 +
 +      for (i = 0; i < len; i++) {
 +              ret = wl1251_read_eeprom_byte(wl, offset + i, &data[i]);
 +              if (ret < 0)
 +                      return ret;
 +      }
 +
 +      return 0;
 +}
 +
 +static int wl1251_read_eeprom_mac(struct wl1251 *wl)
 +{
 +      u8 mac[ETH_ALEN];
 +      int i, ret;
 +
 +      wl1251_set_partition(wl, 0, 0, REGISTERS_BASE, REGISTERS_DOWN_SIZE);
 +
 +      ret = wl1251_read_eeprom(wl, 0x1c, mac, sizeof(mac));
 +      if (ret < 0) {
 +              wl1251_warning("failed to read MAC address from EEPROM");
 +              return ret;
 +      }
 +
 +      /* MAC is stored in reverse order */
 +      for (i = 0; i < ETH_ALEN; i++)
 +              wl->mac_addr[i] = mac[ETH_ALEN - i - 1];
 +
 +      return 0;
 +}
 +
 +static int wl1251_register_hw(struct wl1251 *wl)
 +{
 +      int ret;
 +
 +      if (wl->mac80211_registered)
 +              return 0;
 +
 +      SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
 +
 +      ret = ieee80211_register_hw(wl->hw);
 +      if (ret < 0) {
 +              wl1251_error("unable to register mac80211 hw: %d", ret);
 +              return ret;
 +      }
 +
 +      wl->mac80211_registered = true;
 +
 +      wl1251_notice("loaded");
 +
 +      return 0;
 +}
 +
 +int wl1251_init_ieee80211(struct wl1251 *wl)
 +{
 +      int ret;
 +
 +      /* The tx descriptor buffer and the TKIP space */
 +      wl->hw->extra_tx_headroom = sizeof(struct tx_double_buffer_desc)
 +              + WL1251_TKIP_IV_SPACE;
 +
 +      /* unit us */
 +      /* FIXME: find a proper value */
 +      wl->hw->channel_change_time = 10000;
 +
 +      wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
 +              IEEE80211_HW_SUPPORTS_PS |
 +              IEEE80211_HW_SUPPORTS_UAPSD;
 +
 +      wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
 +                                       BIT(NL80211_IFTYPE_ADHOC);
 +      wl->hw->wiphy->max_scan_ssids = 1;
 +      wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1251_band_2ghz;
 +
 +      wl->hw->queues = 4;
 +
 +      if (wl->use_eeprom)
 +              wl1251_read_eeprom_mac(wl);
 +
 +      ret = wl1251_register_hw(wl);
 +      if (ret)
 +              goto out;
 +
 +      wl1251_debugfs_init(wl);
 +      wl1251_notice("initialized");
 +
 +      ret = 0;
 +
 +out:
 +      return ret;
 +}
 +EXPORT_SYMBOL_GPL(wl1251_init_ieee80211);
 +
 +struct ieee80211_hw *wl1251_alloc_hw(void)
 +{
 +      struct ieee80211_hw *hw;
 +      struct wl1251 *wl;
 +      int i;
 +      static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
 +
 +      hw = ieee80211_alloc_hw(sizeof(*wl), &wl1251_ops);
 +      if (!hw) {
 +              wl1251_error("could not alloc ieee80211_hw");
 +              return ERR_PTR(-ENOMEM);
 +      }
 +
 +      wl = hw->priv;
 +      memset(wl, 0, sizeof(*wl));
 +
 +      wl->hw = hw;
 +
 +      wl->data_in_count = 0;
 +
 +      skb_queue_head_init(&wl->tx_queue);
 +
 +      INIT_WORK(&wl->filter_work, wl1251_filter_work);
 +      INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work);
 +      wl->channel = WL1251_DEFAULT_CHANNEL;
 +      wl->scanning = false;
 +      wl->default_key = 0;
 +      wl->listen_int = 1;
 +      wl->rx_counter = 0;
 +      wl->rx_handled = 0;
 +      wl->rx_current_buffer = 0;
 +      wl->rx_last_id = 0;
 +      wl->rx_config = WL1251_DEFAULT_RX_CONFIG;
 +      wl->rx_filter = WL1251_DEFAULT_RX_FILTER;
 +      wl->elp = false;
 +      wl->station_mode = STATION_ACTIVE_MODE;
 +      wl->psm_requested = false;
 +      wl->tx_queue_stopped = false;
 +      wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
 +      wl->rssi_thold = 0;
 +      wl->beacon_int = WL1251_DEFAULT_BEACON_INT;
 +      wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD;
 +      wl->vif = NULL;
 +
 +      for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
 +              wl->tx_frames[i] = NULL;
 +
 +      wl->next_tx_complete = 0;
 +
 +      INIT_WORK(&wl->irq_work, wl1251_irq_work);
 +      INIT_WORK(&wl->tx_work, wl1251_tx_work);
 +
 +      /*
 +       * In case our MAC address is not correctly set,
 +       * we use a random but Nokia MAC.
 +       */
 +      memcpy(wl->mac_addr, nokia_oui, 3);
 +      get_random_bytes(wl->mac_addr + 3, 3);
 +
 +      wl->state = WL1251_STATE_OFF;
 +      mutex_init(&wl->mutex);
 +
 +      wl->tx_mgmt_frm_rate = DEFAULT_HW_GEN_TX_RATE;
 +      wl->tx_mgmt_frm_mod = DEFAULT_HW_GEN_MODULATION_TYPE;
 +
 +      wl->rx_descriptor = kmalloc(sizeof(*wl->rx_descriptor), GFP_KERNEL);
 +      if (!wl->rx_descriptor) {
 +              wl1251_error("could not allocate memory for rx descriptor");
 +              ieee80211_free_hw(hw);
 +              return ERR_PTR(-ENOMEM);
 +      }
 +
 +      return hw;
 +}
 +EXPORT_SYMBOL_GPL(wl1251_alloc_hw);
 +
 +int wl1251_free_hw(struct wl1251 *wl)
 +{
 +      ieee80211_unregister_hw(wl->hw);
 +
 +      wl1251_debugfs_exit(wl);
 +
 +      kfree(wl->target_mem_map);
 +      kfree(wl->data_path);
 +      vfree(wl->fw);
 +      wl->fw = NULL;
 +      kfree(wl->nvs);
 +      wl->nvs = NULL;
 +
 +      kfree(wl->rx_descriptor);
 +      wl->rx_descriptor = NULL;
 +
 +      ieee80211_free_hw(wl->hw);
 +
 +      return 0;
 +}
 +EXPORT_SYMBOL_GPL(wl1251_free_hw);
 +
 +MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core");
 +MODULE_LICENSE("GPL");
 +MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>");
 +MODULE_FIRMWARE(WL1251_FW_NAME);
index f78694295c397f053f4a00115ff2aff22d8a4379,0000000000000000000000000000000000000000..1b851f650e074eb795ffb708dd25b3c94a2cb9e1
mode 100644,000000..100644
--- /dev/null
@@@ -1,374 -1,0 +1,374 @@@
-       kfree(wl_sdio);
 +/*
 + * wl12xx SDIO routines
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License version 2 as
 + * published by the Free Software Foundation.
 + *
 + * This program is distributed in the hope that it will be useful, but
 + * WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 + * 02110-1301 USA
 + *
 + * Copyright (C) 2005 Texas Instruments Incorporated
 + * Copyright (C) 2008 Google Inc
 + * Copyright (C) 2009 Bob Copeland (me@bobcopeland.com)
 + */
 +#include <linux/interrupt.h>
 +#include <linux/module.h>
 +#include <linux/mod_devicetable.h>
 +#include <linux/mmc/sdio_func.h>
 +#include <linux/mmc/sdio_ids.h>
 +#include <linux/platform_device.h>
 +#include <linux/wl12xx.h>
 +#include <linux/irq.h>
 +#include <linux/pm_runtime.h>
 +
 +#include "wl1251.h"
 +
 +#ifndef SDIO_VENDOR_ID_TI
 +#define SDIO_VENDOR_ID_TI             0x104c
 +#endif
 +
 +#ifndef SDIO_DEVICE_ID_TI_WL1251
 +#define SDIO_DEVICE_ID_TI_WL1251      0x9066
 +#endif
 +
 +struct wl1251_sdio {
 +      struct sdio_func *func;
 +      u32 elp_val;
 +};
 +
 +static struct sdio_func *wl_to_func(struct wl1251 *wl)
 +{
 +      struct wl1251_sdio *wl_sdio = wl->if_priv;
 +      return wl_sdio->func;
 +}
 +
 +static void wl1251_sdio_interrupt(struct sdio_func *func)
 +{
 +      struct wl1251 *wl = sdio_get_drvdata(func);
 +
 +      wl1251_debug(DEBUG_IRQ, "IRQ");
 +
 +      /* FIXME should be synchronous for sdio */
 +      ieee80211_queue_work(wl->hw, &wl->irq_work);
 +}
 +
 +static const struct sdio_device_id wl1251_devices[] = {
 +      { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1251) },
 +      {}
 +};
 +MODULE_DEVICE_TABLE(sdio, wl1251_devices);
 +
 +
 +static void wl1251_sdio_read(struct wl1251 *wl, int addr,
 +                           void *buf, size_t len)
 +{
 +      int ret;
 +      struct sdio_func *func = wl_to_func(wl);
 +
 +      sdio_claim_host(func);
 +      ret = sdio_memcpy_fromio(func, buf, addr, len);
 +      if (ret)
 +              wl1251_error("sdio read failed (%d)", ret);
 +      sdio_release_host(func);
 +}
 +
 +static void wl1251_sdio_write(struct wl1251 *wl, int addr,
 +                            void *buf, size_t len)
 +{
 +      int ret;
 +      struct sdio_func *func = wl_to_func(wl);
 +
 +      sdio_claim_host(func);
 +      ret = sdio_memcpy_toio(func, addr, buf, len);
 +      if (ret)
 +              wl1251_error("sdio write failed (%d)", ret);
 +      sdio_release_host(func);
 +}
 +
 +static void wl1251_sdio_read_elp(struct wl1251 *wl, int addr, u32 *val)
 +{
 +      int ret = 0;
 +      struct wl1251_sdio *wl_sdio = wl->if_priv;
 +      struct sdio_func *func = wl_sdio->func;
 +
 +      /*
 +       * The hardware only supports RAW (read after write) access for
 +       * reading, regular sdio_readb won't work here (it interprets
 +       * the unused bits of CMD52 as write data even if we send read
 +       * request).
 +       */
 +      sdio_claim_host(func);
 +      *val = sdio_writeb_readb(func, wl_sdio->elp_val, addr, &ret);
 +      sdio_release_host(func);
 +
 +      if (ret)
 +              wl1251_error("sdio_readb failed (%d)", ret);
 +}
 +
 +static void wl1251_sdio_write_elp(struct wl1251 *wl, int addr, u32 val)
 +{
 +      int ret = 0;
 +      struct wl1251_sdio *wl_sdio = wl->if_priv;
 +      struct sdio_func *func = wl_sdio->func;
 +
 +      sdio_claim_host(func);
 +      sdio_writeb(func, val, addr, &ret);
 +      sdio_release_host(func);
 +
 +      if (ret)
 +              wl1251_error("sdio_writeb failed (%d)", ret);
 +      else
 +              wl_sdio->elp_val = val;
 +}
 +
 +static void wl1251_sdio_reset(struct wl1251 *wl)
 +{
 +}
 +
 +static void wl1251_sdio_enable_irq(struct wl1251 *wl)
 +{
 +      struct sdio_func *func = wl_to_func(wl);
 +
 +      sdio_claim_host(func);
 +      sdio_claim_irq(func, wl1251_sdio_interrupt);
 +      sdio_release_host(func);
 +}
 +
 +static void wl1251_sdio_disable_irq(struct wl1251 *wl)
 +{
 +      struct sdio_func *func = wl_to_func(wl);
 +
 +      sdio_claim_host(func);
 +      sdio_release_irq(func);
 +      sdio_release_host(func);
 +}
 +
 +/* Interrupts when using dedicated WLAN_IRQ pin */
 +static irqreturn_t wl1251_line_irq(int irq, void *cookie)
 +{
 +      struct wl1251 *wl = cookie;
 +
 +      ieee80211_queue_work(wl->hw, &wl->irq_work);
 +
 +      return IRQ_HANDLED;
 +}
 +
 +static void wl1251_enable_line_irq(struct wl1251 *wl)
 +{
 +      return enable_irq(wl->irq);
 +}
 +
 +static void wl1251_disable_line_irq(struct wl1251 *wl)
 +{
 +      return disable_irq(wl->irq);
 +}
 +
 +static int wl1251_sdio_set_power(struct wl1251 *wl, bool enable)
 +{
 +      struct sdio_func *func = wl_to_func(wl);
 +      int ret;
 +
 +      if (enable) {
 +              /*
 +               * Power is controlled by runtime PM, but we still call board
 +               * callback in case it wants to do any additional setup,
 +               * for example enabling clock buffer for the module.
 +               */
 +              if (wl->set_power)
 +                      wl->set_power(true);
 +
 +              ret = pm_runtime_get_sync(&func->dev);
 +              if (ret < 0)
 +                      goto out;
 +
 +              sdio_claim_host(func);
 +              sdio_enable_func(func);
 +              sdio_release_host(func);
 +      } else {
 +              sdio_claim_host(func);
 +              sdio_disable_func(func);
 +              sdio_release_host(func);
 +
 +              ret = pm_runtime_put_sync(&func->dev);
 +              if (ret < 0)
 +                      goto out;
 +
 +              if (wl->set_power)
 +                      wl->set_power(false);
 +      }
 +
 +out:
 +      return ret;
 +}
 +
 +static struct wl1251_if_operations wl1251_sdio_ops = {
 +      .read = wl1251_sdio_read,
 +      .write = wl1251_sdio_write,
 +      .write_elp = wl1251_sdio_write_elp,
 +      .read_elp = wl1251_sdio_read_elp,
 +      .reset = wl1251_sdio_reset,
 +      .power = wl1251_sdio_set_power,
 +};
 +
 +static int wl1251_sdio_probe(struct sdio_func *func,
 +                           const struct sdio_device_id *id)
 +{
 +      int ret;
 +      struct wl1251 *wl;
 +      struct ieee80211_hw *hw;
 +      struct wl1251_sdio *wl_sdio;
 +      const struct wl12xx_platform_data *wl12xx_board_data;
 +
 +      hw = wl1251_alloc_hw();
 +      if (IS_ERR(hw))
 +              return PTR_ERR(hw);
 +
 +      wl = hw->priv;
 +
 +      wl_sdio = kzalloc(sizeof(*wl_sdio), GFP_KERNEL);
 +      if (wl_sdio == NULL) {
 +              ret = -ENOMEM;
 +              goto out_free_hw;
 +      }
 +
 +      sdio_claim_host(func);
 +      ret = sdio_enable_func(func);
 +      if (ret)
 +              goto release;
 +
 +      sdio_set_block_size(func, 512);
 +      sdio_release_host(func);
 +
 +      SET_IEEE80211_DEV(hw, &func->dev);
 +      wl_sdio->func = func;
 +      wl->if_priv = wl_sdio;
 +      wl->if_ops = &wl1251_sdio_ops;
 +
 +      wl12xx_board_data = wl12xx_get_platform_data();
 +      if (!IS_ERR(wl12xx_board_data)) {
 +              wl->set_power = wl12xx_board_data->set_power;
 +              wl->irq = wl12xx_board_data->irq;
 +              wl->use_eeprom = wl12xx_board_data->use_eeprom;
 +      }
 +
 +      if (wl->irq) {
 +              ret = request_irq(wl->irq, wl1251_line_irq, 0, "wl1251", wl);
 +              if (ret < 0) {
 +                      wl1251_error("request_irq() failed: %d", ret);
 +                      goto disable;
 +              }
 +
 +              irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING);
 +              disable_irq(wl->irq);
 +
 +              wl1251_sdio_ops.enable_irq = wl1251_enable_line_irq;
 +              wl1251_sdio_ops.disable_irq = wl1251_disable_line_irq;
 +
 +              wl1251_info("using dedicated interrupt line");
 +      } else {
 +              wl1251_sdio_ops.enable_irq = wl1251_sdio_enable_irq;
 +              wl1251_sdio_ops.disable_irq = wl1251_sdio_disable_irq;
 +
 +              wl1251_info("using SDIO interrupt");
 +      }
 +
 +      ret = wl1251_init_ieee80211(wl);
 +      if (ret)
 +              goto out_free_irq;
 +
 +      sdio_set_drvdata(func, wl);
 +
 +      /* Tell PM core that we don't need the card to be powered now */
 +      pm_runtime_put_noidle(&func->dev);
 +
 +      return ret;
 +
 +out_free_irq:
 +      if (wl->irq)
 +              free_irq(wl->irq, wl);
 +disable:
 +      sdio_claim_host(func);
 +      sdio_disable_func(func);
 +release:
 +      sdio_release_host(func);
 +      kfree(wl_sdio);
 +out_free_hw:
 +      wl1251_free_hw(wl);
 +      return ret;
 +}
 +
 +static void __devexit wl1251_sdio_remove(struct sdio_func *func)
 +{
 +      struct wl1251 *wl = sdio_get_drvdata(func);
 +      struct wl1251_sdio *wl_sdio = wl->if_priv;
 +
 +      /* Undo decrement done above in wl1251_probe */
 +      pm_runtime_get_noresume(&func->dev);
 +
 +      if (wl->irq)
 +              free_irq(wl->irq, wl);
 +      wl1251_free_hw(wl);
++      kfree(wl_sdio);
 +
 +      sdio_claim_host(func);
 +      sdio_release_irq(func);
 +      sdio_disable_func(func);
 +      sdio_release_host(func);
 +}
 +
 +static int wl1251_suspend(struct device *dev)
 +{
 +      /*
 +       * Tell MMC/SDIO core it's OK to power down the card
 +       * (if it isn't already), but not to remove it completely.
 +       */
 +      return 0;
 +}
 +
 +static int wl1251_resume(struct device *dev)
 +{
 +      return 0;
 +}
 +
 +static const struct dev_pm_ops wl1251_sdio_pm_ops = {
 +      .suspend        = wl1251_suspend,
 +      .resume         = wl1251_resume,
 +};
 +
 +static struct sdio_driver wl1251_sdio_driver = {
 +      .name           = "wl1251_sdio",
 +      .id_table       = wl1251_devices,
 +      .probe          = wl1251_sdio_probe,
 +      .remove         = __devexit_p(wl1251_sdio_remove),
 +      .drv.pm         = &wl1251_sdio_pm_ops,
 +};
 +
 +static int __init wl1251_sdio_init(void)
 +{
 +      int err;
 +
 +      err = sdio_register_driver(&wl1251_sdio_driver);
 +      if (err)
 +              wl1251_error("failed to register sdio driver: %d", err);
 +      return err;
 +}
 +
 +static void __exit wl1251_sdio_exit(void)
 +{
 +      sdio_unregister_driver(&wl1251_sdio_driver);
 +      wl1251_notice("unloaded");
 +}
 +
 +module_init(wl1251_sdio_init);
 +module_exit(wl1251_sdio_exit);
 +
 +MODULE_LICENSE("GPL");
 +MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>");
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc net/ipv4/tcp.c
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
This page took 0.133785 seconds and 5 git commands to generate.