headers: remove sched.h from interrupt.h
[safe/jmp/linux-2.6] / drivers / net / wireless / iwmc3200wifi / rx.c
index 3909477..771a301 100644 (file)
@@ -38,6 +38,7 @@
 
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
+#include <linux/sched.h>
 #include <linux/etherdevice.h>
 #include <linux/wireless.h>
 #include <linux/ieee80211.h>
@@ -102,6 +103,7 @@ static int iwm_ntf_error(struct iwm_priv *iwm, u8 *buf,
        error = (struct iwm_umac_notif_error *)buf;
        fw_err = &error->err;
 
+       memcpy(iwm->last_fw_err, fw_err, sizeof(struct iwm_fw_error_hdr));
 
        IWM_ERR(iwm, "%cMAC FW ERROR:\n",
         (le32_to_cpu(fw_err->category) == UMAC_SYS_ERR_CAT_LMAC) ? 'L' : 'U');
@@ -119,6 +121,8 @@ static int iwm_ntf_error(struct iwm_priv *iwm, u8 *buf,
        IWM_ERR(iwm, "\tLMAC status: 0x%x\n", le32_to_cpu(fw_err->lmac_status));
        IWM_ERR(iwm, "\tSDIO status: 0x%x\n", le32_to_cpu(fw_err->sdio_status));
 
+       iwm_resetting(iwm);
+
        return 0;
 }
 
@@ -219,17 +223,17 @@ static int iwm_ntf_tx(struct iwm_priv *iwm, u8 *buf,
                (buf + sizeof(struct iwm_umac_wifi_in_hdr));
        hdr = (struct iwm_umac_wifi_in_hdr *)buf;
 
-       IWM_DBG_NTF(iwm, DBG, "REPLY_TX, buf size: %lu\n", buf_size);
+       IWM_DBG_TX(iwm, DBG, "REPLY_TX, buf size: %lu\n", buf_size);
 
-       IWM_DBG_NTF(iwm, DBG, "Seqnum: %d\n",
-                   le16_to_cpu(hdr->sw_hdr.cmd.seq_num));
-       IWM_DBG_NTF(iwm, DBG, "\tFrame cnt: %d\n", tx_resp->frame_cnt);
-       IWM_DBG_NTF(iwm, DBG, "\tRetry cnt: %d\n",
-                   le16_to_cpu(tx_resp->retry_cnt));
-       IWM_DBG_NTF(iwm, DBG, "\tSeq ctl: %d\n", le16_to_cpu(tx_resp->seq_ctl));
-       IWM_DBG_NTF(iwm, DBG, "\tByte cnt: %d\n",
-                   le16_to_cpu(tx_resp->byte_cnt));
-       IWM_DBG_NTF(iwm, DBG, "\tStatus: 0x%x\n", le32_to_cpu(tx_resp->status));
+       IWM_DBG_TX(iwm, DBG, "Seqnum: %d\n",
+                  le16_to_cpu(hdr->sw_hdr.cmd.seq_num));
+       IWM_DBG_TX(iwm, DBG, "\tFrame cnt: %d\n", tx_resp->frame_cnt);
+       IWM_DBG_TX(iwm, DBG, "\tRetry cnt: %d\n",
+                  le16_to_cpu(tx_resp->retry_cnt));
+       IWM_DBG_TX(iwm, DBG, "\tSeq ctl: %d\n", le16_to_cpu(tx_resp->seq_ctl));
+       IWM_DBG_TX(iwm, DBG, "\tByte cnt: %d\n",
+                  le16_to_cpu(tx_resp->byte_cnt));
+       IWM_DBG_TX(iwm, DBG, "\tStatus: 0x%x\n", le32_to_cpu(tx_resp->status));
 
        return 0;
 }
@@ -419,8 +423,8 @@ static int iwm_ntf_rx_ticket(struct iwm_priv *iwm, u8 *buf,
                        if (IS_ERR(ticket_node))
                                return PTR_ERR(ticket_node);
 
-                       IWM_DBG_NTF(iwm, DBG, "TICKET RELEASE(%d)\n",
-                                   ticket->id);
+                       IWM_DBG_RX(iwm, DBG, "TICKET RELEASE(%d)\n",
+                                  ticket->id);
                        list_add_tail(&ticket_node->node, &iwm->rx_tickets);
 
                        /*
@@ -455,15 +459,15 @@ static int iwm_ntf_rx_packet(struct iwm_priv *iwm, u8 *buf,
        u16 id, buf_offset;
        u32 packet_size;
 
-       IWM_DBG_NTF(iwm, DBG, "\n");
+       IWM_DBG_RX(iwm, DBG, "\n");
 
        wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf;
        id = le16_to_cpu(wifi_hdr->sw_hdr.cmd.seq_num);
        buf_offset = sizeof(struct iwm_umac_wifi_in_hdr);
        packet_size = buf_size - sizeof(struct iwm_umac_wifi_in_hdr);
 
-       IWM_DBG_NTF(iwm, DBG, "CMD:0x%x, seqnum: %d, packet size: %d\n",
-                   wifi_hdr->sw_hdr.cmd.cmd, id, packet_size);
+       IWM_DBG_RX(iwm, DBG, "CMD:0x%x, seqnum: %d, packet size: %d\n",
+                  wifi_hdr->sw_hdr.cmd.cmd, id, packet_size);
        IWM_DBG_RX(iwm, DBG, "Packet id: %d\n", id);
        IWM_HEXDUMP(iwm, DBG, RX, "PACKET: ", buf + buf_offset, packet_size);
 
@@ -488,8 +492,6 @@ static int iwm_mlme_assoc_start(struct iwm_priv *iwm, u8 *buf,
 
        start = (struct iwm_umac_notif_assoc_start *)buf;
 
-       set_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
-
        IWM_DBG_MLME(iwm, INFO, "Association with %pM Started, reason: %d\n",
                     start->bssid, le32_to_cpu(start->roam_reason));
 
@@ -504,43 +506,84 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
 {
        struct iwm_umac_notif_assoc_complete *complete =
                (struct iwm_umac_notif_assoc_complete *)buf;
-       union iwreq_data wrqu;
 
        IWM_DBG_MLME(iwm, INFO, "Association with %pM completed, status: %d\n",
                     complete->bssid, complete->status);
 
-       memset(&wrqu, 0, sizeof(wrqu));
-
-       clear_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
-
        switch (le32_to_cpu(complete->status)) {
        case UMAC_ASSOC_COMPLETE_SUCCESS:
                set_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
                memcpy(iwm->bssid, complete->bssid, ETH_ALEN);
                iwm->channel = complete->channel;
 
+               /* Internal roaming state, avoid notifying SME. */
+               if (!test_and_clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status)
+                   && iwm->conf.mode == UMAC_MODE_BSS) {
+                       cancel_delayed_work(&iwm->disconnect);
+                       cfg80211_roamed(iwm_to_ndev(iwm),
+                                       complete->bssid,
+                                       iwm->req_ie, iwm->req_ie_len,
+                                       iwm->resp_ie, iwm->resp_ie_len,
+                                       GFP_KERNEL);
+                       break;
+               }
+
                iwm_link_on(iwm);
 
-               memcpy(wrqu.ap_addr.sa_data, complete->bssid, ETH_ALEN);
+               if (iwm->conf.mode == UMAC_MODE_IBSS)
+                       goto ibss;
+
+               if (!test_bit(IWM_STATUS_RESETTING, &iwm->status))
+                       cfg80211_connect_result(iwm_to_ndev(iwm),
+                                               complete->bssid,
+                                               iwm->req_ie, iwm->req_ie_len,
+                                               iwm->resp_ie, iwm->resp_ie_len,
+                                               WLAN_STATUS_SUCCESS,
+                                               GFP_KERNEL);
+               else
+                       cfg80211_roamed(iwm_to_ndev(iwm),
+                                       complete->bssid,
+                                       iwm->req_ie, iwm->req_ie_len,
+                                       iwm->resp_ie, iwm->resp_ie_len,
+                                       GFP_KERNEL);
                break;
        case UMAC_ASSOC_COMPLETE_FAILURE:
                clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
                memset(iwm->bssid, 0, ETH_ALEN);
                iwm->channel = 0;
 
+               /* Internal roaming state, avoid notifying SME. */
+               if (!test_and_clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status)
+                   && iwm->conf.mode == UMAC_MODE_BSS) {
+                       cancel_delayed_work(&iwm->disconnect);
+                       break;
+               }
+
                iwm_link_off(iwm);
+
+               if (iwm->conf.mode == UMAC_MODE_IBSS)
+                       goto ibss;
+
+               if (!test_bit(IWM_STATUS_RESETTING, &iwm->status))
+                       cfg80211_connect_result(iwm_to_ndev(iwm),
+                                               complete->bssid,
+                                               NULL, 0, NULL, 0,
+                                               WLAN_STATUS_UNSPECIFIED_FAILURE,
+                                               GFP_KERNEL);
+               else
+                       cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0,
+                                             GFP_KERNEL);
+               break;
        default:
                break;
        }
 
-       if (iwm->conf.mode == UMAC_MODE_IBSS) {
-               cfg80211_ibss_joined(iwm_to_ndev(iwm), iwm->bssid, GFP_KERNEL);
-               return 0;
-       }
-
-       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
-       wireless_send_event(iwm_to_ndev(iwm), SIOCGIWAP, &wrqu, NULL);
+       clear_bit(IWM_STATUS_RESETTING, &iwm->status);
+       return 0;
 
+ ibss:
+       cfg80211_ibss_joined(iwm_to_ndev(iwm), iwm->bssid, GFP_KERNEL);
+       clear_bit(IWM_STATUS_RESETTING, &iwm->status);
        return 0;
 }
 
@@ -549,13 +592,20 @@ static int iwm_mlme_profile_invalidate(struct iwm_priv *iwm, u8 *buf,
                                       struct iwm_wifi_cmd *cmd)
 {
        struct iwm_umac_notif_profile_invalidate *invalid;
+       u32 reason;
 
        invalid = (struct iwm_umac_notif_profile_invalidate *)buf;
+       reason = le32_to_cpu(invalid->reason);
 
-       IWM_DBG_MLME(iwm, INFO, "Profile Invalidated. Reason: %d\n",
-                    le32_to_cpu(invalid->reason));
+       IWM_DBG_MLME(iwm, INFO, "Profile Invalidated. Reason: %d\n", reason);
 
-       clear_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
+       if (reason != UMAC_PROFILE_INVALID_REQUEST &&
+           test_bit(IWM_STATUS_SME_CONNECTING, &iwm->status))
+               cfg80211_connect_result(iwm_to_ndev(iwm), NULL, NULL, 0, NULL,
+                                       0, WLAN_STATUS_UNSPECIFIED_FAILURE,
+                                       GFP_KERNEL);
+
+       clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status);
        clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
 
        iwm->umac_profile_active = 0;
@@ -569,6 +619,19 @@ static int iwm_mlme_profile_invalidate(struct iwm_priv *iwm, u8 *buf,
        return 0;
 }
 
+#define IWM_DISCONNECT_INTERVAL        (5 * HZ)
+
+static int iwm_mlme_connection_terminated(struct iwm_priv *iwm, u8 *buf,
+                                         unsigned long buf_size,
+                                         struct iwm_wifi_cmd *cmd)
+{
+       IWM_DBG_MLME(iwm, DBG, "Connection terminated\n");
+
+       schedule_delayed_work(&iwm->disconnect, IWM_DISCONNECT_INTERVAL);
+
+       return 0;
+}
+
 static int iwm_mlme_scan_complete(struct iwm_priv *iwm, u8 *buf,
                                  unsigned long buf_size,
                                  struct iwm_wifi_cmd *cmd)
@@ -770,37 +833,47 @@ static int iwm_mlme_mgt_frame(struct iwm_priv *iwm, u8 *buf,
                              unsigned long buf_size, struct iwm_wifi_cmd *cmd)
 {
        struct iwm_umac_notif_mgt_frame *mgt_frame =
-       (struct iwm_umac_notif_mgt_frame *)buf;
+                       (struct iwm_umac_notif_mgt_frame *)buf;
        struct ieee80211_mgmt *mgt = (struct ieee80211_mgmt *)mgt_frame->frame;
        u8 *ie;
-       unsigned int event;
-       union iwreq_data wrqu;
 
        IWM_HEXDUMP(iwm, DBG, MLME, "MGT: ", mgt_frame->frame,
                    le16_to_cpu(mgt_frame->len));
 
        if (ieee80211_is_assoc_req(mgt->frame_control)) {
                ie = mgt->u.assoc_req.variable;;
-               event = IWEVASSOCREQIE;
+               iwm->req_ie_len =
+                               le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt);
+               kfree(iwm->req_ie);
+               iwm->req_ie = kmemdup(mgt->u.assoc_req.variable,
+                                     iwm->req_ie_len, GFP_KERNEL);
        } else if (ieee80211_is_reassoc_req(mgt->frame_control)) {
                ie = mgt->u.reassoc_req.variable;;
-               event = IWEVASSOCREQIE;
+               iwm->req_ie_len =
+                               le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt);
+               kfree(iwm->req_ie);
+               iwm->req_ie = kmemdup(mgt->u.reassoc_req.variable,
+                                     iwm->req_ie_len, GFP_KERNEL);
        } else if (ieee80211_is_assoc_resp(mgt->frame_control)) {
                ie = mgt->u.assoc_resp.variable;;
-               event = IWEVASSOCRESPIE;
+               iwm->resp_ie_len =
+                               le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt);
+               kfree(iwm->resp_ie);
+               iwm->resp_ie = kmemdup(mgt->u.assoc_resp.variable,
+                                      iwm->resp_ie_len, GFP_KERNEL);
        } else if (ieee80211_is_reassoc_resp(mgt->frame_control)) {
                ie = mgt->u.reassoc_resp.variable;;
-               event = IWEVASSOCRESPIE;
+               iwm->resp_ie_len =
+                               le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt);
+               kfree(iwm->resp_ie);
+               iwm->resp_ie = kmemdup(mgt->u.reassoc_resp.variable,
+                                      iwm->resp_ie_len, GFP_KERNEL);
        } else {
-               IWM_ERR(iwm, "Unsupported management frame");
+               IWM_ERR(iwm, "Unsupported management frame: 0x%x",
+                       le16_to_cpu(mgt->frame_control));
                return 0;
        }
 
-       wrqu.data.length = le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt);
-
-       IWM_HEXDUMP(iwm, DBG, MLME, "EVT: ", ie, wrqu.data.length);
-       wireless_send_event(iwm_to_ndev(iwm), event, &wrqu, ie);
-
        return 0;
 }
 
@@ -818,8 +891,7 @@ static int iwm_ntf_mlme(struct iwm_priv *iwm, u8 *buf,
        case WIFI_IF_NTFY_PROFILE_INVALIDATE_COMPLETE:
                return iwm_mlme_profile_invalidate(iwm, buf, buf_size, cmd);
        case WIFI_IF_NTFY_CONNECTION_TERMINATED:
-               IWM_DBG_MLME(iwm, DBG, "Connection terminated\n");
-               break;
+               return iwm_mlme_connection_terminated(iwm, buf, buf_size, cmd);
        case WIFI_IF_NTFY_SCAN_COMPLETE:
                return iwm_mlme_scan_complete(iwm, buf, buf_size, cmd);
        case WIFI_IF_NTFY_STA_TABLE_CHANGE:
@@ -1360,7 +1432,7 @@ static void iwm_rx_process_packet(struct iwm_priv *iwm,
 
                skb->dev = iwm_to_ndev(iwm);
                skb->protocol = eth_type_trans(skb, ndev);
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               skb->ip_summed = CHECKSUM_NONE;
                memset(skb->cb, 0, sizeof(skb->cb));
 
                ndev->stats.rx_packets++;