mwl8k: dma header manipulations can't fail
[pandora-kernel.git] / drivers / net / wireless / mwl8k.c
index 33d7ab5..5aa8042 100644 (file)
@@ -111,17 +111,6 @@ struct mwl8k_rx_queue {
        struct sk_buff **rx_skb;
 };
 
-struct mwl8k_skb {
-       /*
-        * The DMA engine requires a modification to the payload.
-        * If the skbuff is shared/cloned, it needs to be unshared.
-        * This method is used to ensure the stack always gets back
-        * the skbuff it sent for transmission.
-        */
-       struct sk_buff *clone;
-       struct sk_buff *skb;
-};
-
 struct mwl8k_tx_queue {
        /* hw transmits here */
        int tx_head;
@@ -132,7 +121,7 @@ struct mwl8k_tx_queue {
        struct ieee80211_tx_queue_stats tx_stats;
        struct mwl8k_tx_desc *tx_desc_area;
        dma_addr_t tx_desc_dma;
-       struct mwl8k_skb *tx_skb;
+       struct sk_buff **tx_skb;
 };
 
 /* Pointers to the firmware data and meta information about it.  */
@@ -274,14 +263,6 @@ static const struct ieee80211_rate mwl8k_rates[] = {
        { .bitrate = 540, .hw_value = 108, },
 };
 
-/* Slot time */
-
-/* Short Slot: 9us slot time */
-#define MWL8K_SHORT_SLOTTIME           1
-
-/* Long slot: 20us slot time */
-#define MWL8K_LONG_SLOTTIME            0
-
 /* Set or get info from Firmware */
 #define MWL8K_CMD_SET                  0x0001
 #define MWL8K_CMD_GET                  0x0000
@@ -722,12 +703,11 @@ struct mwl8k_dma_data {
 } __attribute__((packed));
 
 /* Routines to add/remove DMA header from skb.  */
-static inline int mwl8k_remove_dma_header(struct sk_buff *skb)
+static inline void mwl8k_remove_dma_header(struct sk_buff *skb)
 {
-       struct mwl8k_dma_data *tr = (struct mwl8k_dma_data *)(skb->data);
+       struct mwl8k_dma_data *tr = (struct mwl8k_dma_data *)skb->data;
        void *dst, *src = &tr->wh;
-       __le16 fc = tr->wh.frame_control;
-       int hdrlen = ieee80211_hdrlen(fc);
+       int hdrlen = ieee80211_hdrlen(tr->wh.frame_control);
        u16 space = sizeof(struct mwl8k_dma_data) - hdrlen;
 
        dst = (void *)tr + space;
@@ -735,11 +715,9 @@ static inline int mwl8k_remove_dma_header(struct sk_buff *skb)
                memmove(dst, src, hdrlen);
                skb_pull(skb, space);
        }
-
-       return 0;
 }
 
-static inline struct sk_buff *mwl8k_add_dma_header(struct sk_buff *skb)
+static inline void mwl8k_add_dma_header(struct sk_buff *skb)
 {
        struct ieee80211_hdr *wh;
        u32 hdrlen, pktlen;
@@ -771,8 +749,6 @@ static inline struct sk_buff *mwl8k_add_dma_header(struct sk_buff *skb)
         * This includes all crypto material including the MIC.
         */
        tr->fwlen = cpu_to_le16(pktlen - hdrlen);
-
-       return skb;
 }
 
 
@@ -975,10 +951,7 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
                                        MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE);
 
                skb_put(skb, le16_to_cpu(rx_desc->pkt_len));
-               if (mwl8k_remove_dma_header(skb)) {
-                       dev_kfree_skb(skb);
-                       continue;
-               }
+               mwl8k_remove_dma_header(skb);
 
                wh = (struct ieee80211_hdr *)skb->data;
 
@@ -1232,7 +1205,6 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force)
 
        while (txq->tx_stats.len > 0) {
                int tx;
-               int rc;
                struct mwl8k_tx_desc *tx_desc;
                unsigned long addr;
                int size;
@@ -1240,7 +1212,6 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force)
                struct ieee80211_tx_info *info;
                u32 status;
 
-               rc = 0;
                tx = txq->tx_head;
                tx_desc = txq->tx_desc_area + tx;
 
@@ -1260,40 +1231,18 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force)
 
                addr = le32_to_cpu(tx_desc->pkt_phys_addr);
                size = le16_to_cpu(tx_desc->pkt_len);
-               skb = txq->tx_skb[tx].skb;
-               txq->tx_skb[tx].skb = NULL;
+               skb = txq->tx_skb[tx];
+               txq->tx_skb[tx] = NULL;
 
                BUG_ON(skb == NULL);
                pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE);
 
-               rc = mwl8k_remove_dma_header(skb);
+               mwl8k_remove_dma_header(skb);
 
                /* Mark descriptor as unused */
                tx_desc->pkt_phys_addr = 0;
                tx_desc->pkt_len = 0;
 
-               if (txq->tx_skb[tx].clone) {
-                       /* Replace with original skb
-                        * before returning to stack
-                        * as buffer has been cloned
-                        */
-                       dev_kfree_skb(skb);
-                       skb = txq->tx_skb[tx].clone;
-                       txq->tx_skb[tx].clone = NULL;
-               }
-
-               if (rc) {
-                       /* Something has gone wrong here.
-                        * Failed to remove DMA header.
-                        * Print error message and drop packet.
-                        */
-                       printk(KERN_ERR "%s: Error removing DMA header from "
-                                       "tx skb 0x%p.\n", priv->name, skb);
-
-                       dev_kfree_skb(skb);
-                       continue;
-               }
-
                info = IEEE80211_SKB_CB(skb);
                ieee80211_tx_info_clear_status(info);
                if (MWL8K_TXD_SUCCESS(status))
@@ -1335,7 +1284,6 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
        struct mwl8k_tx_desc *tx;
        struct mwl8k_dma_data *tr;
        struct mwl8k_vif *mwl8k_vif;
-       struct sk_buff *org_skb = skb;
        dma_addr_t dma;
        u16 qos = 0;
        bool qosframe = false, ampduframe = false;
@@ -1346,21 +1294,12 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
        txq = priv->txq + index;
        tx = txq->tx_desc_area + txq->tx_tail;
 
-       BUG_ON(txq->tx_skb[txq->tx_tail].skb != NULL);
+       BUG_ON(txq->tx_skb[txq->tx_tail] != NULL);
 
        /*
-        * Append HW DMA header to start of packet.  Drop packet if
-        * there is not enough space or a failure to unshare/unclone
-        * the skb.
+        * Append HW DMA header to start of packet.
         */
-       skb = mwl8k_add_dma_header(skb);
-
-       if (skb == NULL) {
-               printk(KERN_DEBUG "%s: failed to prepend HW DMA "
-                       "header, dropping TX frame.\n", priv->name);
-               dev_kfree_skb(org_skb);
-               return NETDEV_TX_OK;
-       }
+       mwl8k_add_dma_header(skb);
 
        tx_info = IEEE80211_SKB_CB(skb);
        mwl8k_vif = MWL8K_VIF(tx_info->control.vif);
@@ -1388,8 +1327,6 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
                printk(KERN_DEBUG "%s: failed to dma map skb, "
                        "dropping TX frame.\n", priv->name);
 
-               if (org_skb != NULL)
-                       dev_kfree_skb(org_skb);
                if (skb != NULL)
                        dev_kfree_skb(skb);
                return NETDEV_TX_OK;
@@ -1445,9 +1382,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
        tx->pkt_phys_addr = cpu_to_le32(dma);
        tx->pkt_len = cpu_to_le16(skb->len);
 
-       txq->tx_skb[txq->tx_tail].skb = skb;
-       txq->tx_skb[txq->tx_tail].clone =
-               skb == org_skb ? NULL : org_skb;
+       txq->tx_skb[txq->tx_tail] = skb;
 
        spin_lock_bh(&priv->tx_lock);
 
@@ -1621,38 +1556,39 @@ struct mwl8k_cmd_mac_multicast_adr {
 
 #define MWL8K_ENABLE_RX_MULTICAST 0x000F
 
-static int mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw,
-                                       int mc_count,
-                                       struct dev_addr_list *mclist)
+static struct mwl8k_cmd_pkt *
+__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw,
+                             int mc_count, struct dev_addr_list *mclist)
 {
+       struct mwl8k_priv *priv = hw->priv;
        struct mwl8k_cmd_mac_multicast_adr *cmd;
-       int index = 0;
-       int rc;
-       int size = sizeof(*cmd) + mc_count * ETH_ALEN;
+       int size;
+       int i;
+
+       if (mc_count > priv->num_mcaddrs)
+               mc_count = priv->num_mcaddrs;
 
-       cmd = kzalloc(size, GFP_KERNEL);
+       size = sizeof(*cmd) + mc_count * ETH_ALEN;
+
+       cmd = kzalloc(size, GFP_ATOMIC);
        if (cmd == NULL)
-               return -ENOMEM;
+               return NULL;
 
        cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR);
        cmd->header.length = cpu_to_le16(size);
        cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST);
        cmd->numaddr = cpu_to_le16(mc_count);
 
-       while (index < mc_count && mclist) {
+       for (i = 0; i < mc_count && mclist; i++) {
                if (mclist->da_addrlen != ETH_ALEN) {
-                       rc = -EINVAL;
-                       goto mwl8k_cmd_mac_multicast_adr_exit;
+                       kfree(cmd);
+                       return NULL;
                }
-               memcpy(cmd->addr[index++], mclist->da_addr, ETH_ALEN);
+               memcpy(cmd->addr[i], mclist->da_addr, ETH_ALEN);
                mclist = mclist->next;
        }
 
-       rc = mwl8k_post_cmd(hw, &cmd->header);
-
-mwl8k_cmd_mac_multicast_adr_exit:
-       kfree(cmd);
-       return rc;
+       return &cmd->header;
 }
 
 /*
@@ -1895,7 +1831,7 @@ struct mwl8k_cmd_set_slot {
        __u8 short_slot;
 } __attribute__((packed));
 
-static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, int slot_time)
+static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time)
 {
        struct mwl8k_cmd_set_slot *cmd;
        int rc;
@@ -1907,7 +1843,7 @@ static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, int slot_time)
        cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT);
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
        cmd->action = cpu_to_le16(MWL8K_CMD_SET);
-       cmd->short_slot = slot_time == MWL8K_SHORT_SLOTTIME ? 1 : 0;
+       cmd->short_slot = short_slot_time;
 
        rc = mwl8k_post_cmd(hw, &cmd->header);
        kfree(cmd);
@@ -2890,8 +2826,7 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
        /*
         * We only support managed interfaces for now.
         */
-       if (conf->type != NL80211_IFTYPE_STATION &&
-           conf->type != NL80211_IFTYPE_MONITOR)
+       if (conf->type != NL80211_IFTYPE_STATION)
                return -EINVAL;
 
        /* Clean out driver private area */
@@ -3038,8 +2973,7 @@ static int mwl8k_bss_info_changed_wt(struct work_struct *wt)
                        goto mwl8k_bss_info_changed_exit;
 
                /* Set slot time */
-               if (mwl8k_cmd_set_slot(hw, info->use_short_slot ?
-                               MWL8K_SHORT_SLOTTIME : MWL8K_LONG_SLOTTIME))
+               if (mwl8k_cmd_set_slot(hw, info->use_short_slot))
                        goto mwl8k_bss_info_changed_exit;
 
                /* Update peer rate info */
@@ -3100,12 +3034,21 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw,
                printk(KERN_ERR "%s() timed out\n", __func__);
 }
 
+static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw,
+                                  int mc_count, struct dev_addr_list *mclist)
+{
+       struct mwl8k_cmd_pkt *cmd;
+
+       cmd = __mwl8k_cmd_mac_multicast_adr(hw, mc_count, mclist);
+
+       return (unsigned long)cmd;
+}
+
 struct mwl8k_configure_filter_worker {
        struct mwl8k_work_struct header;
        unsigned int changed_flags;
-       unsigned int *total_flags;
-       int mc_count;
-       struct dev_addr_list *mclist;
+       unsigned int total_flags;
+       struct mwl8k_cmd_pkt *multicast_adr_cmd;
 };
 
 #define MWL8K_SUPPORTED_IF_FLAGS       FIF_BCN_PRBRESP_PROMISC
@@ -3114,18 +3057,12 @@ static int mwl8k_configure_filter_wt(struct work_struct *wt)
 {
        struct mwl8k_configure_filter_worker *worker =
                (struct mwl8k_configure_filter_worker *)wt;
-
        struct ieee80211_hw *hw = worker->header.hw;
-       unsigned int changed_flags = worker->changed_flags;
-       unsigned int *total_flags = worker->total_flags;
-       int mc_count = worker->mc_count;
-       struct dev_addr_list *mclist = worker->mclist;
-
        struct mwl8k_priv *priv = hw->priv;
        int rc = 0;
 
-       if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
-               if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
+       if (worker->changed_flags & FIF_BCN_PRBRESP_PROMISC) {
+               if (worker->total_flags & FIF_BCN_PRBRESP_PROMISC)
                        rc = mwl8k_cmd_set_pre_scan(hw);
                else {
                        u8 *bssid;
@@ -3138,54 +3075,20 @@ static int mwl8k_configure_filter_wt(struct work_struct *wt)
                }
        }
 
-       if (rc)
-               goto mwl8k_configure_filter_exit;
-       if (mc_count) {
-               if (mc_count > priv->num_mcaddrs)
-                       mc_count = priv->num_mcaddrs;
-
-               rc = mwl8k_cmd_mac_multicast_adr(hw, mc_count, mclist);
-               if (rc)
-                       printk(KERN_ERR
-                       "%s()Error setting multicast addresses\n",
-                       __func__);
-       }
+       if (!rc && worker->multicast_adr_cmd != NULL)
+               rc = mwl8k_post_cmd(hw, worker->multicast_adr_cmd);
+       kfree(worker->multicast_adr_cmd);
 
-mwl8k_configure_filter_exit:
        return rc;
 }
 
-static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw,
-                                  int mc_count, struct dev_addr_list *mclist)
-{
-       struct mwl8k_configure_filter_worker *worker;
-
-       worker = kzalloc(sizeof(*worker), GFP_ATOMIC);
-
-       if (!worker)
-               return 0;
-
-       /*
-        * XXX: This is _HORRIBLY_ broken!!
-        *
-        *      No locking, the mclist pointer might be invalid as soon as this
-        *      function returns, something in the list might be invalidated
-        *      once we get to the worker, etc...
-        */
-       worker->mc_count = mc_count;
-       worker->mclist = mclist;
-
-       return (u64)worker;
-}
-
 static void mwl8k_configure_filter(struct ieee80211_hw *hw,
                                   unsigned int changed_flags,
                                   unsigned int *total_flags,
                                   u64 multicast)
 {
-
-       struct mwl8k_configure_filter_worker *worker = (void *)multicast;
        struct mwl8k_priv *priv = hw->priv;
+       struct mwl8k_configure_filter_worker *worker;
 
        /* Clear unsupported feature flags */
        *total_flags &= MWL8K_SUPPORTED_IF_FLAGS;
@@ -3193,12 +3096,13 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw,
        if (!(changed_flags & MWL8K_SUPPORTED_IF_FLAGS))
                return;
 
+       worker = kzalloc(sizeof(*worker), GFP_ATOMIC);
        if (worker == NULL)
                return;
 
-       worker->header.options = MWL8K_WQ_QUEUE_ONLY | MWL8K_WQ_TX_WAIT_EMPTY;
        worker->changed_flags = changed_flags;
-       worker->total_flags = total_flags;
+       worker->total_flags = *total_flags;
+       worker->multicast_adr_cmd = (void *)(unsigned long)multicast;
 
        mwl8k_queue_work(hw, &worker->header, priv->config_wq,
                         mwl8k_configure_filter_wt);
@@ -3478,8 +3382,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
 
        hw->queues = MWL8K_TX_QUEUES;
 
-       hw->wiphy->interface_modes =
-               BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_MONITOR);
+       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
 
        /* Set rssi and noise values to dBm */
        hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM;