Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6.git] / drivers / net / wireless / iwmc3200wifi / rx.c
index 3257d4f..ad53987 100644 (file)
@@ -343,15 +343,17 @@ static void iwm_rx_ticket_node_free(struct iwm_rx_ticket_node *ticket_node)
 static struct iwm_rx_packet *iwm_rx_packet_get(struct iwm_priv *iwm, u16 id)
 {
        u8 id_hash = IWM_RX_ID_GET_HASH(id);
-       struct list_head *packet_list;
-       struct iwm_rx_packet *packet, *next;
-
-       packet_list = &iwm->rx_packets[id_hash];
+       struct iwm_rx_packet *packet;
 
-       list_for_each_entry_safe(packet, next, packet_list, node)
-               if (packet->id == id)
+       spin_lock(&iwm->packet_lock[id_hash]);
+       list_for_each_entry(packet, &iwm->rx_packets[id_hash], node)
+               if (packet->id == id) {
+                       list_del(&packet->node);
+                       spin_unlock(&iwm->packet_lock[id_hash]);
                        return packet;
+               }
 
+       spin_unlock(&iwm->packet_lock[id_hash]);
        return NULL;
 }
 
@@ -389,18 +391,22 @@ void iwm_rx_free(struct iwm_priv *iwm)
        struct iwm_rx_packet *packet, *np;
        int i;
 
+       spin_lock(&iwm->ticket_lock);
        list_for_each_entry_safe(ticket, nt, &iwm->rx_tickets, node) {
                list_del(&ticket->node);
                iwm_rx_ticket_node_free(ticket);
        }
+       spin_unlock(&iwm->ticket_lock);
 
        for (i = 0; i < IWM_RX_ID_HASH; i++) {
+               spin_lock(&iwm->packet_lock[i]);
                list_for_each_entry_safe(packet, np, &iwm->rx_packets[i],
                                         node) {
                        list_del(&packet->node);
                        kfree_skb(packet->skb);
                        kfree(packet);
                }
+               spin_unlock(&iwm->packet_lock[i]);
        }
 }
 
@@ -428,7 +434,9 @@ static int iwm_ntf_rx_ticket(struct iwm_priv *iwm, u8 *buf,
                                   ticket->action ==  IWM_RX_TICKET_RELEASE ?
                                   "RELEASE" : "DROP",
                                   ticket->id);
+                       spin_lock(&iwm->ticket_lock);
                        list_add_tail(&ticket_node->node, &iwm->rx_tickets);
+                       spin_unlock(&iwm->ticket_lock);
 
                        /*
                         * We received an Rx ticket, most likely there's
@@ -461,6 +469,7 @@ static int iwm_ntf_rx_packet(struct iwm_priv *iwm, u8 *buf,
        struct iwm_rx_packet *packet;
        u16 id, buf_offset;
        u32 packet_size;
+       u8 id_hash;
 
        IWM_DBG_RX(iwm, DBG, "\n");
 
@@ -478,7 +487,10 @@ static int iwm_ntf_rx_packet(struct iwm_priv *iwm, u8 *buf,
        if (IS_ERR(packet))
                return PTR_ERR(packet);
 
-       list_add_tail(&packet->node, &iwm->rx_packets[IWM_RX_ID_GET_HASH(id)]);
+       id_hash = IWM_RX_ID_GET_HASH(id);
+       spin_lock(&iwm->packet_lock[id_hash]);
+       list_add_tail(&packet->node, &iwm->rx_packets[id_hash]);
+       spin_unlock(&iwm->packet_lock[id_hash]);
 
        /* We might (unlikely) have received the packet _after_ the ticket */
        queue_work(iwm->rx_wq, &iwm->rx_worker);
@@ -519,6 +531,8 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
                                   unsigned long buf_size,
                                   struct iwm_wifi_cmd *cmd)
 {
+       struct wiphy *wiphy = iwm_to_wiphy(iwm);
+       struct ieee80211_channel *chan;
        struct iwm_umac_notif_assoc_complete *complete =
                (struct iwm_umac_notif_assoc_complete *)buf;
 
@@ -527,6 +541,18 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
 
        switch (le32_to_cpu(complete->status)) {
        case UMAC_ASSOC_COMPLETE_SUCCESS:
+               chan = ieee80211_get_channel(wiphy,
+                       ieee80211_channel_to_frequency(complete->channel));
+               if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
+                       /* Associated to a unallowed channel, disassociate. */
+                       __iwm_invalidate_mlme_profile(iwm);
+                       IWM_WARN(iwm, "Couldn't associate with %pM due to "
+                                "channel %d is disabled. Check your local "
+                                "regulatory setting.\n",
+                                complete->bssid, complete->channel);
+                       goto failure;
+               }
+
                set_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
                memcpy(iwm->bssid, complete->bssid, ETH_ALEN);
                iwm->channel = complete->channel;
@@ -563,6 +589,7 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
                                        GFP_KERNEL);
                break;
        case UMAC_ASSOC_COMPLETE_FAILURE:
+ failure:
                clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
                memset(iwm->bssid, 0, ETH_ALEN);
                iwm->channel = 0;
@@ -757,7 +784,7 @@ static int iwm_mlme_update_bss_table(struct iwm_priv *iwm, u8 *buf,
                        (struct iwm_umac_notif_bss_info *)buf;
        struct ieee80211_channel *channel;
        struct ieee80211_supported_band *band;
-       struct iwm_bss_info *bss, *next;
+       struct iwm_bss_info *bss;
        s32 signal;
        int freq;
        u16 frame_len = le16_to_cpu(umac_bss->frame_len);
@@ -776,7 +803,7 @@ static int iwm_mlme_update_bss_table(struct iwm_priv *iwm, u8 *buf,
        IWM_DBG_MLME(iwm, DBG, "\tRSSI: %d\n", umac_bss->rssi);
        IWM_DBG_MLME(iwm, DBG, "\tFrame Length: %d\n", frame_len);
 
-       list_for_each_entry_safe(bss, next, &iwm->bss_list, node)
+       list_for_each_entry(bss, &iwm->bss_list, node)
                if (bss->bss->table_idx == umac_bss->table_idx)
                        break;
 
@@ -843,16 +870,15 @@ static int iwm_mlme_remove_bss(struct iwm_priv *iwm, u8 *buf,
        int i;
 
        for (i = 0; i < le32_to_cpu(bss_rm->count); i++) {
-               table_idx = (le16_to_cpu(bss_rm->entries[i])
-                            & IWM_BSS_REMOVE_INDEX_MSK);
+               table_idx = le16_to_cpu(bss_rm->entries[i]) &
+                           IWM_BSS_REMOVE_INDEX_MSK;
                list_for_each_entry_safe(bss, next, &iwm->bss_list, node)
                        if (bss->bss->table_idx == cpu_to_le16(table_idx)) {
                                struct ieee80211_mgmt *mgmt;
 
                                mgmt = (struct ieee80211_mgmt *)
                                        (bss->bss->frame_buf);
-                               IWM_DBG_MLME(iwm, ERR,
-                                            "BSS removed: %pM\n",
+                               IWM_DBG_MLME(iwm, ERR, "BSS removed: %pM\n",
                                             mgmt->bssid);
                                list_del(&bss->node);
                                kfree(bss->bss);
@@ -1224,18 +1250,24 @@ static int iwm_rx_handle_wifi(struct iwm_priv *iwm, u8 *buf,
        u8 source, cmd_id;
        u16 seq_num;
        u32 count;
-       u8 resp;
 
        wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf;
        cmd_id = wifi_hdr->sw_hdr.cmd.cmd;
-
        source = GET_VAL32(wifi_hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE);
        if (source >= IWM_SRC_NUM) {
                IWM_CRIT(iwm, "invalid source %d\n", source);
                return -EINVAL;
        }
 
-       count = (GET_VAL32(wifi_hdr->sw_hdr.meta_data, UMAC_FW_CMD_BYTE_COUNT));
+       if (cmd_id == REPLY_RX_MPDU_CMD)
+               trace_iwm_rx_packet(iwm, buf, buf_size);
+       else if ((cmd_id == UMAC_NOTIFY_OPCODE_RX_TICKET) &&
+                (source == UMAC_HDI_IN_SOURCE_FW))
+               trace_iwm_rx_ticket(iwm, buf, buf_size);
+       else
+               trace_iwm_rx_wifi_cmd(iwm, wifi_hdr);
+
+       count = GET_VAL32(wifi_hdr->sw_hdr.meta_data, UMAC_FW_CMD_BYTE_COUNT);
        count += sizeof(struct iwm_umac_wifi_in_hdr) -
                 sizeof(struct iwm_dev_cmd_hdr);
        if (count > buf_size) {
@@ -1243,8 +1275,6 @@ static int iwm_rx_handle_wifi(struct iwm_priv *iwm, u8 *buf,
                return -EINVAL;
        }
 
-       resp = GET_VAL32(wifi_hdr->sw_hdr.meta_data, UMAC_FW_CMD_STATUS);
-
        seq_num = le16_to_cpu(wifi_hdr->sw_hdr.cmd.seq_num);
 
        IWM_DBG_RX(iwm, DBG, "CMD:0x%x, source: 0x%x, seqnum: %d\n",
@@ -1317,8 +1347,9 @@ static int iwm_rx_handle_nonwifi(struct iwm_priv *iwm, u8 *buf,
 {
        u8 seq_num;
        struct iwm_udma_in_hdr *hdr = (struct iwm_udma_in_hdr *)buf;
-       struct iwm_nonwifi_cmd *cmd, *next;
+       struct iwm_nonwifi_cmd *cmd;
 
+       trace_iwm_rx_nonwifi_cmd(iwm, buf, buf_size);
        seq_num = GET_VAL32(hdr->cmd, UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM);
 
        /*
@@ -1329,7 +1360,7 @@ static int iwm_rx_handle_nonwifi(struct iwm_priv *iwm, u8 *buf,
         * That means we only support synchronised non wifi command response
         * schemes.
         */
-       list_for_each_entry_safe(cmd, next, &iwm->nonwifi_pending_cmd, pending)
+       list_for_each_entry(cmd, &iwm->nonwifi_pending_cmd, pending)
                if (cmd->seq_num == seq_num) {
                        cmd->resp_received = 1;
                        cmd->buf.len = buf_size;
@@ -1648,6 +1679,7 @@ void iwm_rx_worker(struct work_struct *work)
         * We stop whenever a ticket is missing its packet, as we're
         * supposed to send the packets in order.
         */
+       spin_lock(&iwm->ticket_lock);
        list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) {
                struct iwm_rx_packet *packet =
                        iwm_rx_packet_get(iwm, le16_to_cpu(ticket->ticket->id));
@@ -1656,12 +1688,12 @@ void iwm_rx_worker(struct work_struct *work)
                        IWM_DBG_RX(iwm, DBG, "Skip rx_work: Wait for ticket %d "
                                   "to be handled first\n",
                                   le16_to_cpu(ticket->ticket->id));
-                       return;
+                       break;
                }
 
                list_del(&ticket->node);
-               list_del(&packet->node);
                iwm_rx_process_packet(iwm, packet, ticket);
        }
+       spin_unlock(&iwm->ticket_lock);
 }