1bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi/*
2bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * Intel Wireless Multicomm 3200 WiFi driver
3bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
4bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * Copyright (C) 2009 Intel Corporation. All rights reserved.
5bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
6bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * Redistribution and use in source and binary forms, with or without
7bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * modification, are permitted provided that the following conditions
8bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * are met:
9bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
10bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *   * Redistributions of source code must retain the above copyright
11bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *     notice, this list of conditions and the following disclaimer.
12bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *   * Redistributions in binary form must reproduce the above copyright
13bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *     notice, this list of conditions and the following disclaimer in
14bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *     the documentation and/or other materials provided with the
15bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *     distribution.
16bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *   * Neither the name of Intel Corporation nor the names of its
17bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *     contributors may be used to endorse or promote products derived
18bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *     from this software without specific prior written permission.
19bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
20bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
32bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
33bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * Intel Corporation <ilw@linux.intel.com>
34bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * Samuel Ortiz <samuel.ortiz@intel.com>
35bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * Zhu Yi <yi.zhu@intel.com>
36bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
37bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi */
38bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
39bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include <linux/kernel.h>
40bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include <linux/netdevice.h>
41d43c36dc6b357fa1806800f18aa30123c747a6d1Alexey Dobriyan#include <linux/sched.h>
42bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include <linux/etherdevice.h>
43bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include <linux/wireless.h>
44bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include <linux/ieee80211.h>
45bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include <linux/if_arp.h>
46bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include <linux/list.h>
475a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
48bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include <net/iw_handler.h>
49bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
50bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include "iwm.h"
51bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include "debug.h"
52bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include "hal.h"
53bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include "umac.h"
54bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include "lmac.h"
55bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include "commands.h"
56bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include "rx.h"
57bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include "cfg80211.h"
58bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#include "eeprom.h"
59bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
60bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_rx_check_udma_hdr(struct iwm_udma_in_hdr *hdr)
61bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
62bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if ((le32_to_cpu(hdr->cmd) == UMAC_PAD_TERMINAL) ||
63bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	    (le32_to_cpu(hdr->size) == UMAC_PAD_TERMINAL))
64bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return -EINVAL;
65bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
66bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
67bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
68bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
69bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic inline int iwm_rx_resp_size(struct iwm_udma_in_hdr *hdr)
70bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
71bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return ALIGN(le32_to_cpu(hdr->size) + sizeof(struct iwm_udma_in_hdr),
72bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		     16);
73bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
74bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
75bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi/*
76bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * Notification handlers:
77bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
78bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * For every possible notification we can receive from the
79bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * target, we have a handler.
80bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * When we get a target notification, and there is no one
81bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * waiting for it, it's just processed through the rx code
82bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * path:
83bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
84bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * iwm_rx_handle()
85bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *  -> iwm_rx_handle_umac()
86bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *      -> iwm_rx_handle_wifi()
87bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *          -> iwm_rx_handle_resp()
88bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *              -> iwm_ntf_*()
89bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
90bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *      OR
91bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
92bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *      -> iwm_rx_handle_non_wifi()
93bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
94bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * If there are processes waiting for this notification, then
95bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * iwm_rx_handle_wifi() just wakes those processes up and they
96bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * grab the pending notification.
97bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi */
98bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_error(struct iwm_priv *iwm, u8 *buf,
99bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
100bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
101bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_error *error;
102bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_fw_error_hdr *fw_err;
103bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
104bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	error = (struct iwm_umac_notif_error *)buf;
105bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	fw_err = &error->err;
106bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
10704e715cd46ba523806070fbf9ded009f10e107cdSamuel Ortiz	memcpy(iwm->last_fw_err, fw_err, sizeof(struct iwm_fw_error_hdr));
10804e715cd46ba523806070fbf9ded009f10e107cdSamuel Ortiz
109bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "%cMAC FW ERROR:\n",
110bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 (le32_to_cpu(fw_err->category) == UMAC_SYS_ERR_CAT_LMAC) ? 'L' : 'U');
111bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tCategory:    %d\n", le32_to_cpu(fw_err->category));
112bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tStatus:      0x%x\n", le32_to_cpu(fw_err->status));
113bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tPC:          0x%x\n", le32_to_cpu(fw_err->pc));
114bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tblink1:      %d\n", le32_to_cpu(fw_err->blink1));
115bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tblink2:      %d\n", le32_to_cpu(fw_err->blink2));
116bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tilink1:      %d\n", le32_to_cpu(fw_err->ilink1));
117bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tilink2:      %d\n", le32_to_cpu(fw_err->ilink2));
118bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tData1:       0x%x\n", le32_to_cpu(fw_err->data1));
119bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tData2:       0x%x\n", le32_to_cpu(fw_err->data2));
120bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tLine number: %d\n", le32_to_cpu(fw_err->line_num));
121bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tUMAC status: 0x%x\n", le32_to_cpu(fw_err->umac_status));
122bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tLMAC status: 0x%x\n", le32_to_cpu(fw_err->lmac_status));
123bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_ERR(iwm, "\tSDIO status: 0x%x\n", le32_to_cpu(fw_err->sdio_status));
124bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
125d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz	iwm_resetting(iwm);
126d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz
127bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
128bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
129bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
130bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_umac_alive(struct iwm_priv *iwm, u8 *buf,
131bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
132bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
133bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_alive *alive_resp =
134bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			(struct iwm_umac_notif_alive *)(buf);
135bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u16 status = le16_to_cpu(alive_resp->status);
136bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
137bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (status == UMAC_NTFY_ALIVE_STATUS_ERR) {
138bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_ERR(iwm, "Receive error UMAC_ALIVE\n");
139bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return -EIO;
140bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
141bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
142bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	iwm_tx_credit_init_pools(iwm, alive_resp);
143bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
144bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
145bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
146bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
147bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_init_complete(struct iwm_priv *iwm, u8 *buf,
148bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				 unsigned long buf_size,
149bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				 struct iwm_wifi_cmd *cmd)
150bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
151257862f3faef397f1a677ae6a5a1828fa00a97b1Zhu Yi	struct wiphy *wiphy = iwm_to_wiphy(iwm);
152bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_init_complete *init_complete =
153bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			(struct iwm_umac_notif_init_complete *)(buf);
154bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u16 status = le16_to_cpu(init_complete->status);
155257862f3faef397f1a677ae6a5a1828fa00a97b1Zhu Yi	bool blocked = (status == UMAC_NTFY_INIT_COMPLETE_STATUS_ERR);
156bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
157257862f3faef397f1a677ae6a5a1828fa00a97b1Zhu Yi	if (blocked)
158bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_DBG_NTF(iwm, DBG, "Hardware rf kill is on (radio off)\n");
159257862f3faef397f1a677ae6a5a1828fa00a97b1Zhu Yi	else
160bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_DBG_NTF(iwm, DBG, "Hardware rf kill is off (radio on)\n");
161257862f3faef397f1a677ae6a5a1828fa00a97b1Zhu Yi
162257862f3faef397f1a677ae6a5a1828fa00a97b1Zhu Yi	wiphy_rfkill_set_hw_state(wiphy, blocked);
163bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
164bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
165bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
166bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
167bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_tx_credit_update(struct iwm_priv *iwm, u8 *buf,
168bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				    unsigned long buf_size,
169bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				    struct iwm_wifi_cmd *cmd)
170bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
171bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int pool_nr, total_freed_pages;
172bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	unsigned long pool_map;
173bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int i, id;
174bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_page_dealloc *dealloc =
175bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			(struct iwm_umac_notif_page_dealloc *)buf;
176bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
177bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	pool_nr = GET_VAL32(dealloc->changes, UMAC_DEALLOC_NTFY_CHANGES_CNT);
178bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	pool_map = GET_VAL32(dealloc->changes, UMAC_DEALLOC_NTFY_CHANGES_MSK);
179bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
180bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_TX(iwm, DBG, "UMAC dealloc notification: pool nr %d, "
181bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		   "update map 0x%lx\n", pool_nr, pool_map);
182bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
183bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	spin_lock(&iwm->tx_credit.lock);
184bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
185bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	for (i = 0; i < pool_nr; i++) {
186bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		id = GET_VAL32(dealloc->grp_info[i],
187bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			       UMAC_DEALLOC_NTFY_GROUP_NUM);
188bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (test_bit(id, &pool_map)) {
189bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			total_freed_pages = GET_VAL32(dealloc->grp_info[i],
190bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi					      UMAC_DEALLOC_NTFY_PAGE_CNT);
191bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			iwm_tx_credit_inc(iwm, id, total_freed_pages);
192bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		}
193bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
194bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
195bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	spin_unlock(&iwm->tx_credit.lock);
196bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
197bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
198bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
199bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
200bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_umac_reset(struct iwm_priv *iwm, u8 *buf,
201bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
202bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
203bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_NTF(iwm, DBG, "UMAC RESET done\n");
204bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
205bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
206bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
207bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
208bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_lmac_version(struct iwm_priv *iwm, u8 *buf,
209bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				unsigned long buf_size,
210bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				struct iwm_wifi_cmd *cmd)
211bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
212bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_NTF(iwm, INFO, "LMAC Version: %x.%x\n", buf[9], buf[8]);
213bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
214bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
215bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
216bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
217bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_tx(struct iwm_priv *iwm, u8 *buf,
218bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
219bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
220bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_lmac_tx_resp *tx_resp;
221bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_wifi_in_hdr *hdr;
222bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
223bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	tx_resp = (struct iwm_lmac_tx_resp *)
224bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		(buf + sizeof(struct iwm_umac_wifi_in_hdr));
225bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	hdr = (struct iwm_umac_wifi_in_hdr *)buf;
226bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
2274fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi	IWM_DBG_TX(iwm, DBG, "REPLY_TX, buf size: %lu\n", buf_size);
2284fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi
2294fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi	IWM_DBG_TX(iwm, DBG, "Seqnum: %d\n",
2304fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi		   le16_to_cpu(hdr->sw_hdr.cmd.seq_num));
2314fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi	IWM_DBG_TX(iwm, DBG, "\tFrame cnt: %d\n", tx_resp->frame_cnt);
2324fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi	IWM_DBG_TX(iwm, DBG, "\tRetry cnt: %d\n",
2334fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi		   le16_to_cpu(tx_resp->retry_cnt));
2344fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi	IWM_DBG_TX(iwm, DBG, "\tSeq ctl: %d\n", le16_to_cpu(tx_resp->seq_ctl));
2354fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi	IWM_DBG_TX(iwm, DBG, "\tByte cnt: %d\n",
2364fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi		   le16_to_cpu(tx_resp->byte_cnt));
2374fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi	IWM_DBG_TX(iwm, DBG, "\tStatus: 0x%x\n", le32_to_cpu(tx_resp->status));
238bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
239bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
240bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
241bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
242bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
243bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_calib_res(struct iwm_priv *iwm, u8 *buf,
244bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     unsigned long buf_size, struct iwm_wifi_cmd *cmd)
245bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
246bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u8 opcode;
247bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u8 *calib_buf;
248bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_lmac_calib_hdr *hdr = (struct iwm_lmac_calib_hdr *)
249bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				(buf + sizeof(struct iwm_umac_wifi_in_hdr));
250bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
251bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	opcode = hdr->opcode;
252bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
253bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	BUG_ON(opcode >= CALIBRATION_CMD_NUM ||
254bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	       opcode < PHY_CALIBRATE_OPCODES_NUM);
255bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
256bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_NTF(iwm, DBG, "Store calibration result for opcode: %d\n",
257bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		    opcode);
258bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
259bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	buf_size -= sizeof(struct iwm_umac_wifi_in_hdr);
260bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	calib_buf = iwm->calib_res[opcode].buf;
261bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
262bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (!calib_buf || (iwm->calib_res[opcode].size < buf_size)) {
263bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		kfree(calib_buf);
264bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		calib_buf = kzalloc(buf_size, GFP_KERNEL);
265bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (!calib_buf) {
266bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			IWM_ERR(iwm, "Memory allocation failed: calib_res\n");
267bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			return -ENOMEM;
268bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		}
269bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		iwm->calib_res[opcode].buf = calib_buf;
270bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		iwm->calib_res[opcode].size = buf_size;
271bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
272bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
273bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	memcpy(calib_buf, hdr, buf_size);
274bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	set_bit(opcode - PHY_CALIBRATE_OPCODES_NUM, &iwm->calib_done_map);
275bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
276bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
277bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
278bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
279bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_calib_complete(struct iwm_priv *iwm, u8 *buf,
280bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				  unsigned long buf_size,
281bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				  struct iwm_wifi_cmd *cmd)
282bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
283bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_NTF(iwm, DBG, "Calibration completed\n");
284bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
285bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
286bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
287bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
288bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_calib_cfg(struct iwm_priv *iwm, u8 *buf,
289bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     unsigned long buf_size, struct iwm_wifi_cmd *cmd)
290bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
291bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_lmac_cal_cfg_resp *cal_resp;
292bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
293bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	cal_resp = (struct iwm_lmac_cal_cfg_resp *)
294bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			(buf + sizeof(struct iwm_umac_wifi_in_hdr));
295bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
296bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_NTF(iwm, DBG, "Calibration CFG command status: %d\n",
297bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		    le32_to_cpu(cal_resp->status));
298bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
299bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
300bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
301bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
302bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_wifi_status(struct iwm_priv *iwm, u8 *buf,
303bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			       unsigned long buf_size, struct iwm_wifi_cmd *cmd)
304bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
305bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_wifi_status *status =
306bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		(struct iwm_umac_notif_wifi_status *)buf;
307bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
308bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	iwm->core_enabled |= le16_to_cpu(status->status);
309bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
310bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
311bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
312bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
313bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic struct iwm_rx_ticket_node *
314bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yiiwm_rx_ticket_node_alloc(struct iwm_priv *iwm, struct iwm_rx_ticket *ticket)
315bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
316bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_rx_ticket_node *ticket_node;
317bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
318bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	ticket_node = kzalloc(sizeof(struct iwm_rx_ticket_node), GFP_KERNEL);
319bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (!ticket_node) {
320bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_ERR(iwm, "Couldn't allocate ticket node\n");
321bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return ERR_PTR(-ENOMEM);
322bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
323bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
324ff020726a7e963c3b9fb71825b3c33885022a8f0Julia Lawall	ticket_node->ticket = kmemdup(ticket, sizeof(struct iwm_rx_ticket),
325ff020726a7e963c3b9fb71825b3c33885022a8f0Julia Lawall				      GFP_KERNEL);
326bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (!ticket_node->ticket) {
327bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_ERR(iwm, "Couldn't allocate RX ticket\n");
328bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		kfree(ticket_node);
329bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return ERR_PTR(-ENOMEM);
330bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
331bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
332bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	INIT_LIST_HEAD(&ticket_node->node);
333bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
334bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return ticket_node;
335bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
336bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
337bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic void iwm_rx_ticket_node_free(struct iwm_rx_ticket_node *ticket_node)
338bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
339bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	kfree(ticket_node->ticket);
340bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	kfree(ticket_node);
341bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
342bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
343bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic struct iwm_rx_packet *iwm_rx_packet_get(struct iwm_priv *iwm, u16 id)
344bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
345bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u8 id_hash = IWM_RX_ID_GET_HASH(id);
34604d1c22761f33ac8f345665e7ef809c875142425Zhu Yi	struct iwm_rx_packet *packet;
347bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
348c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi	spin_lock(&iwm->packet_lock[id_hash]);
34904d1c22761f33ac8f345665e7ef809c875142425Zhu Yi	list_for_each_entry(packet, &iwm->rx_packets[id_hash], node)
350c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi		if (packet->id == id) {
351c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi			list_del(&packet->node);
352c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi			spin_unlock(&iwm->packet_lock[id_hash]);
353bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			return packet;
354c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi		}
355bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
356c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi	spin_unlock(&iwm->packet_lock[id_hash]);
357bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return NULL;
358bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
359bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
360bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic struct iwm_rx_packet *iwm_rx_packet_alloc(struct iwm_priv *iwm, u8 *buf,
361bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi						 u32 size, u16 id)
362bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
363bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_rx_packet *packet;
364bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
365bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	packet = kzalloc(sizeof(struct iwm_rx_packet), GFP_KERNEL);
366bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (!packet) {
367bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_ERR(iwm, "Couldn't allocate packet\n");
368bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return ERR_PTR(-ENOMEM);
369bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
370bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
371bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	packet->skb = dev_alloc_skb(size);
372bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (!packet->skb) {
373bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_ERR(iwm, "Couldn't allocate packet SKB\n");
374bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		kfree(packet);
375bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return ERR_PTR(-ENOMEM);
376bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
377bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
378bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	packet->pkt_size = size;
379bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
380bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	skb_put(packet->skb, size);
381bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	memcpy(packet->skb->data, buf, size);
382bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	INIT_LIST_HEAD(&packet->node);
383bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	packet->id = id;
384bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
385bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return packet;
386bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
387bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
388bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yivoid iwm_rx_free(struct iwm_priv *iwm)
389bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
390bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_rx_ticket_node *ticket, *nt;
391bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_rx_packet *packet, *np;
392bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int i;
393bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
394c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi	spin_lock(&iwm->ticket_lock);
395bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	list_for_each_entry_safe(ticket, nt, &iwm->rx_tickets, node) {
396bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		list_del(&ticket->node);
397bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		iwm_rx_ticket_node_free(ticket);
398bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
399c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi	spin_unlock(&iwm->ticket_lock);
400bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
401bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	for (i = 0; i < IWM_RX_ID_HASH; i++) {
402c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi		spin_lock(&iwm->packet_lock[i]);
403bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		list_for_each_entry_safe(packet, np, &iwm->rx_packets[i],
404bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi					 node) {
405bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			list_del(&packet->node);
406bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			kfree_skb(packet->skb);
407bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			kfree(packet);
408bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		}
409c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi		spin_unlock(&iwm->packet_lock[i]);
410bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
411bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
412bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
413bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_rx_ticket(struct iwm_priv *iwm, u8 *buf,
414bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     unsigned long buf_size, struct iwm_wifi_cmd *cmd)
415bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
416bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_rx_ticket *ntf_rx_ticket =
417bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		(struct iwm_umac_notif_rx_ticket *)buf;
418bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_rx_ticket *ticket =
419bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		(struct iwm_rx_ticket *)ntf_rx_ticket->tickets;
420bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int i, schedule_rx = 0;
421bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
422bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	for (i = 0; i < ntf_rx_ticket->num_tickets; i++) {
423bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		struct iwm_rx_ticket_node *ticket_node;
424bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
425bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		switch (le16_to_cpu(ticket->action)) {
426bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		case IWM_RX_TICKET_RELEASE:
427bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		case IWM_RX_TICKET_DROP:
428bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			/* We can push the packet to the stack */
429bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			ticket_node = iwm_rx_ticket_node_alloc(iwm, ticket);
430bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			if (IS_ERR(ticket_node))
431bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				return PTR_ERR(ticket_node);
432bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
43369cf6e2d5bd90436a0fa6cef2f4d65583faef388Samuel Ortiz			IWM_DBG_RX(iwm, DBG, "TICKET %s(%d)\n",
4343c997e8849c5b982662f2a9b3e8cb64260382faaSamuel Ortiz				   __le16_to_cpu(ticket->action) ==
4353c997e8849c5b982662f2a9b3e8cb64260382faaSamuel Ortiz							IWM_RX_TICKET_RELEASE ?
43669cf6e2d5bd90436a0fa6cef2f4d65583faef388Samuel Ortiz				   "RELEASE" : "DROP",
4374fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi				   ticket->id);
438c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi			spin_lock(&iwm->ticket_lock);
439bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			list_add_tail(&ticket_node->node, &iwm->rx_tickets);
440c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi			spin_unlock(&iwm->ticket_lock);
441bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
442bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			/*
443bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			 * We received an Rx ticket, most likely there's
444bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			 * a packet pending for it, it's not worth going
445bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			 * through the packet hash list to double check.
446bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			 * Let's just fire the rx worker..
447bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			 */
448bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			schedule_rx = 1;
449bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
450bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			break;
451bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
452bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		default:
453bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			IWM_ERR(iwm, "Invalid RX ticket action: 0x%x\n",
454bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				ticket->action);
455bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		}
456bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
457bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		ticket++;
458bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
459bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
460bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (schedule_rx)
461bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		queue_work(iwm->rx_wq, &iwm->rx_worker);
462bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
463bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
464bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
465bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
466bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_rx_packet(struct iwm_priv *iwm, u8 *buf,
467bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     unsigned long buf_size, struct iwm_wifi_cmd *cmd)
468bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
469bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_wifi_in_hdr *wifi_hdr;
470bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_rx_packet *packet;
471bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u16 id, buf_offset;
472bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u32 packet_size;
473c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi	u8 id_hash;
474bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
4754fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi	IWM_DBG_RX(iwm, DBG, "\n");
476bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
477bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf;
478bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	id = le16_to_cpu(wifi_hdr->sw_hdr.cmd.seq_num);
479bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	buf_offset = sizeof(struct iwm_umac_wifi_in_hdr);
480bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	packet_size = buf_size - sizeof(struct iwm_umac_wifi_in_hdr);
481bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
4824fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi	IWM_DBG_RX(iwm, DBG, "CMD:0x%x, seqnum: %d, packet size: %d\n",
4834fdd81f5f2e6fc55b67938f09b3495d679428cd7Zhu Yi		   wifi_hdr->sw_hdr.cmd.cmd, id, packet_size);
484bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_RX(iwm, DBG, "Packet id: %d\n", id);
485bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_HEXDUMP(iwm, DBG, RX, "PACKET: ", buf + buf_offset, packet_size);
486bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
487bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	packet = iwm_rx_packet_alloc(iwm, buf + buf_offset, packet_size, id);
488bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (IS_ERR(packet))
489bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return PTR_ERR(packet);
490bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
491c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi	id_hash = IWM_RX_ID_GET_HASH(id);
492c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi	spin_lock(&iwm->packet_lock[id_hash]);
493c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi	list_add_tail(&packet->node, &iwm->rx_packets[id_hash]);
494c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi	spin_unlock(&iwm->packet_lock[id_hash]);
495bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
496bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	/* We might (unlikely) have received the packet _after_ the ticket */
497bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	queue_work(iwm->rx_wq, &iwm->rx_worker);
498bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
499bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
500bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
501bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
502bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi/* MLME handlers */
503bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_mlme_assoc_start(struct iwm_priv *iwm, u8 *buf,
504bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				unsigned long buf_size,
505bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				struct iwm_wifi_cmd *cmd)
506bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
507bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_assoc_start *start;
508bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
509bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	start = (struct iwm_umac_notif_assoc_start *)buf;
510bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
511bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_MLME(iwm, INFO, "Association with %pM Started, reason: %d\n",
512bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		     start->bssid, le32_to_cpu(start->roam_reason));
513bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
514bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wake_up_interruptible(&iwm->mlme_queue);
515bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
516bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
517bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
518bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
5199829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortizstatic u8 iwm_is_open_wep_profile(struct iwm_priv *iwm)
5209829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz{
5219829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz	if ((iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_40 ||
5229829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz	     iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_104) &&
5239829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz	    (iwm->umac_profile->sec.ucast_cipher ==
5249829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz	     iwm->umac_profile->sec.mcast_cipher) &&
5259829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz	    (iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN))
5269829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz	       return 1;
5279829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz
5289829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz       return 0;
5299829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz}
5309829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz
531bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
532bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				   unsigned long buf_size,
533bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				   struct iwm_wifi_cmd *cmd)
534bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
5357d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi	struct wiphy *wiphy = iwm_to_wiphy(iwm);
5367d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi	struct ieee80211_channel *chan;
537bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_assoc_complete *complete =
538bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		(struct iwm_umac_notif_assoc_complete *)buf;
539bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
540bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_MLME(iwm, INFO, "Association with %pM completed, status: %d\n",
541bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		     complete->bssid, complete->status);
542bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
543bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	switch (le32_to_cpu(complete->status)) {
544bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_ASSOC_COMPLETE_SUCCESS:
5457d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi		chan = ieee80211_get_channel(wiphy,
54659eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf			ieee80211_channel_to_frequency(complete->channel,
54759eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf				complete->band == UMAC_BAND_2GHZ ?
54859eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf					IEEE80211_BAND_2GHZ :
54959eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf					IEEE80211_BAND_5GHZ));
5507d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi		if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
5517d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi			/* Associated to a unallowed channel, disassociate. */
5527d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi			__iwm_invalidate_mlme_profile(iwm);
5537d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi			IWM_WARN(iwm, "Couldn't associate with %pM due to "
5547d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi				 "channel %d is disabled. Check your local "
5557d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi				 "regulatory setting.\n",
5567d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi				 complete->bssid, complete->channel);
5577d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi			goto failure;
5587d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi		}
5597d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi
560bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		set_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
561bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		memcpy(iwm->bssid, complete->bssid, ETH_ALEN);
562bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		iwm->channel = complete->channel;
563bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
564de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi		/* Internal roaming state, avoid notifying SME. */
565de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi		if (!test_and_clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status)
566de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi		    && iwm->conf.mode == UMAC_MODE_BSS) {
567c7436273889e0ce511b317041f35344e92344885Zhu Yi			cancel_delayed_work(&iwm->disconnect);
568ed9d01026f156db2d638cbb045231c7a8fde877dJouni Malinen			cfg80211_roamed(iwm_to_ndev(iwm), NULL,
569de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi					complete->bssid,
570de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi					iwm->req_ie, iwm->req_ie_len,
571de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi					iwm->resp_ie, iwm->resp_ie_len,
572de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi					GFP_KERNEL);
573de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi			break;
574de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi		}
575de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi
576bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		iwm_link_on(iwm);
577bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
5789c7c0cdd24e64f9aed39453a1bffc3b3fd16ef99Zhu Yi		if (iwm->conf.mode == UMAC_MODE_IBSS)
5799c7c0cdd24e64f9aed39453a1bffc3b3fd16ef99Zhu Yi			goto ibss;
5809c7c0cdd24e64f9aed39453a1bffc3b3fd16ef99Zhu Yi
581d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz		if (!test_bit(IWM_STATUS_RESETTING, &iwm->status))
582d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz			cfg80211_connect_result(iwm_to_ndev(iwm),
583d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz						complete->bssid,
584d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz						iwm->req_ie, iwm->req_ie_len,
585d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz						iwm->resp_ie, iwm->resp_ie_len,
586d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz						WLAN_STATUS_SUCCESS,
587d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz						GFP_KERNEL);
588d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz		else
589ed9d01026f156db2d638cbb045231c7a8fde877dJouni Malinen			cfg80211_roamed(iwm_to_ndev(iwm), NULL,
5909967d46aa5ba065650d3352ab5d906f56ba17648Samuel Ortiz					complete->bssid,
591b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi					iwm->req_ie, iwm->req_ie_len,
592b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi					iwm->resp_ie, iwm->resp_ie_len,
593d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz					GFP_KERNEL);
594bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
595bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_ASSOC_COMPLETE_FAILURE:
5967d49c6111c27f0e68b0310aeececf7ded53f7f94Zhu Yi failure:
597bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
598bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		memset(iwm->bssid, 0, ETH_ALEN);
599bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		iwm->channel = 0;
600bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
601de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi		/* Internal roaming state, avoid notifying SME. */
602de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi		if (!test_and_clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status)
603c7436273889e0ce511b317041f35344e92344885Zhu Yi		    && iwm->conf.mode == UMAC_MODE_BSS) {
604c7436273889e0ce511b317041f35344e92344885Zhu Yi			cancel_delayed_work(&iwm->disconnect);
605de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi			break;
606c7436273889e0ce511b317041f35344e92344885Zhu Yi		}
607de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi
608bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		iwm_link_off(iwm);
6099967d46aa5ba065650d3352ab5d906f56ba17648Samuel Ortiz
6109c7c0cdd24e64f9aed39453a1bffc3b3fd16ef99Zhu Yi		if (iwm->conf.mode == UMAC_MODE_IBSS)
6119c7c0cdd24e64f9aed39453a1bffc3b3fd16ef99Zhu Yi			goto ibss;
6129c7c0cdd24e64f9aed39453a1bffc3b3fd16ef99Zhu Yi
613d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz		if (!test_bit(IWM_STATUS_RESETTING, &iwm->status))
6149829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz			if (!iwm_is_open_wep_profile(iwm)) {
6159829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz				cfg80211_connect_result(iwm_to_ndev(iwm),
6169829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz					       complete->bssid,
6179829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz					       NULL, 0, NULL, 0,
6189829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz					       WLAN_STATUS_UNSPECIFIED_FAILURE,
6199829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz					       GFP_KERNEL);
6209829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz			} else {
6219829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz				/* Let's try shared WEP auth */
6229829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz				IWM_ERR(iwm, "Trying WEP shared auth\n");
6239829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz				schedule_work(&iwm->auth_retry_worker);
6249829e1b510214956bc9d5e278be49d781e1a6fbfSamuel Ortiz			}
625d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz		else
626d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz			cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0,
627d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz					      GFP_KERNEL);
628de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi		break;
629bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	default:
630bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
631bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
632bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
633d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz	clear_bit(IWM_STATUS_RESETTING, &iwm->status);
6349c7c0cdd24e64f9aed39453a1bffc3b3fd16ef99Zhu Yi	return 0;
635bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
6369c7c0cdd24e64f9aed39453a1bffc3b3fd16ef99Zhu Yi ibss:
6379c7c0cdd24e64f9aed39453a1bffc3b3fd16ef99Zhu Yi	cfg80211_ibss_joined(iwm_to_ndev(iwm), iwm->bssid, GFP_KERNEL);
638d210176eaaed0c7883caba52665bcfb5d420c660Samuel Ortiz	clear_bit(IWM_STATUS_RESETTING, &iwm->status);
639bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
640bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
641bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
642bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_mlme_profile_invalidate(struct iwm_priv *iwm, u8 *buf,
643bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				       unsigned long buf_size,
644bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				       struct iwm_wifi_cmd *cmd)
645bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
646bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_profile_invalidate *invalid;
647c7436273889e0ce511b317041f35344e92344885Zhu Yi	u32 reason;
648bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
649bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	invalid = (struct iwm_umac_notif_profile_invalidate *)buf;
650c7436273889e0ce511b317041f35344e92344885Zhu Yi	reason = le32_to_cpu(invalid->reason);
651c7436273889e0ce511b317041f35344e92344885Zhu Yi
652c7436273889e0ce511b317041f35344e92344885Zhu Yi	IWM_DBG_MLME(iwm, INFO, "Profile Invalidated. Reason: %d\n", reason);
653bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
654c7436273889e0ce511b317041f35344e92344885Zhu Yi	if (reason != UMAC_PROFILE_INVALID_REQUEST &&
655c7436273889e0ce511b317041f35344e92344885Zhu Yi	    test_bit(IWM_STATUS_SME_CONNECTING, &iwm->status))
656c7436273889e0ce511b317041f35344e92344885Zhu Yi		cfg80211_connect_result(iwm_to_ndev(iwm), NULL, NULL, 0, NULL,
657c7436273889e0ce511b317041f35344e92344885Zhu Yi					0, WLAN_STATUS_UNSPECIFIED_FAILURE,
658c7436273889e0ce511b317041f35344e92344885Zhu Yi					GFP_KERNEL);
659bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
660de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi	clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status);
661bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
662bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
6633db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell	iwm->umac_profile_active = false;
664bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	memset(iwm->bssid, 0, ETH_ALEN);
665bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	iwm->channel = 0;
666bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
667bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	iwm_link_off(iwm);
668bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
669bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wake_up_interruptible(&iwm->mlme_queue);
670bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
671bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
672bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
673bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
674c7436273889e0ce511b317041f35344e92344885Zhu Yi#define IWM_DISCONNECT_INTERVAL	(5 * HZ)
675c7436273889e0ce511b317041f35344e92344885Zhu Yi
676c7436273889e0ce511b317041f35344e92344885Zhu Yistatic int iwm_mlme_connection_terminated(struct iwm_priv *iwm, u8 *buf,
677c7436273889e0ce511b317041f35344e92344885Zhu Yi					  unsigned long buf_size,
678c7436273889e0ce511b317041f35344e92344885Zhu Yi					  struct iwm_wifi_cmd *cmd)
679c7436273889e0ce511b317041f35344e92344885Zhu Yi{
680c7436273889e0ce511b317041f35344e92344885Zhu Yi	IWM_DBG_MLME(iwm, DBG, "Connection terminated\n");
681c7436273889e0ce511b317041f35344e92344885Zhu Yi
682c7436273889e0ce511b317041f35344e92344885Zhu Yi	schedule_delayed_work(&iwm->disconnect, IWM_DISCONNECT_INTERVAL);
683c7436273889e0ce511b317041f35344e92344885Zhu Yi
684c7436273889e0ce511b317041f35344e92344885Zhu Yi	return 0;
685c7436273889e0ce511b317041f35344e92344885Zhu Yi}
686c7436273889e0ce511b317041f35344e92344885Zhu Yi
687bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_mlme_scan_complete(struct iwm_priv *iwm, u8 *buf,
688bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				  unsigned long buf_size,
689bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				  struct iwm_wifi_cmd *cmd)
690bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
691bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int ret;
692bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_scan_complete *scan_complete =
693bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		(struct iwm_umac_notif_scan_complete *)buf;
694bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u32 result = le32_to_cpu(scan_complete->result);
695bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
696bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_MLME(iwm, INFO, "type:0x%x result:0x%x seq:%d\n",
697bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		     le32_to_cpu(scan_complete->type),
698bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		     le32_to_cpu(scan_complete->result),
699bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		     scan_complete->seq_num);
700bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
701bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (!test_and_clear_bit(IWM_STATUS_SCANNING, &iwm->status)) {
702bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_ERR(iwm, "Scan complete while device not scanning\n");
703bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return -EIO;
704bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
705bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (!iwm->scan_request)
706bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return 0;
707bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
708bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	ret = iwm_cfg80211_inform_bss(iwm);
709bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
710bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	cfg80211_scan_done(iwm->scan_request,
711bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			   (result & UMAC_SCAN_RESULT_ABORTED) ? 1 : !!ret);
712bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	iwm->scan_request = NULL;
713bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
714bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return ret;
715bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
716bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
717bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_mlme_update_sta_table(struct iwm_priv *iwm, u8 *buf,
718bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				     unsigned long buf_size,
719bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				     struct iwm_wifi_cmd *cmd)
720bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
721bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_sta_info *umac_sta =
722bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			(struct iwm_umac_notif_sta_info *)buf;
723bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_sta_info *sta;
724bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int i;
725bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
726bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	switch (le32_to_cpu(umac_sta->opcode)) {
727bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_OPCODE_ADD_MODIFY:
728bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		sta = &iwm->sta_table[GET_VAL8(umac_sta->sta_id, LMAC_STA_ID)];
729bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
730bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_DBG_MLME(iwm, INFO, "%s STA: ID = %d, Color = %d, "
731bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     "addr = %pM, qos = %d\n",
732bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     sta->valid ? "Modify" : "Add",
733bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     GET_VAL8(umac_sta->sta_id, LMAC_STA_ID),
734bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     GET_VAL8(umac_sta->sta_id, LMAC_STA_COLOR),
735bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     umac_sta->mac_addr,
736bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     umac_sta->flags & UMAC_STA_FLAG_QOS);
737bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
7383db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell		sta->valid = true;
739bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		sta->qos = umac_sta->flags & UMAC_STA_FLAG_QOS;
740bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		sta->color = GET_VAL8(umac_sta->sta_id, LMAC_STA_COLOR);
741bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		memcpy(sta->addr, umac_sta->mac_addr, ETH_ALEN);
742bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
743bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_OPCODE_REMOVE:
744bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_DBG_MLME(iwm, INFO, "Remove STA: ID = %d, Color = %d, "
745bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     "addr = %pM\n",
746bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     GET_VAL8(umac_sta->sta_id, LMAC_STA_ID),
747bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     GET_VAL8(umac_sta->sta_id, LMAC_STA_COLOR),
748bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     umac_sta->mac_addr);
749bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
750bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		sta = &iwm->sta_table[GET_VAL8(umac_sta->sta_id, LMAC_STA_ID)];
751bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
752bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (!memcmp(sta->addr, umac_sta->mac_addr, ETH_ALEN))
7533db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell			sta->valid = false;
754bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
755bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
756bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_OPCODE_CLEAR_ALL:
757bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		for (i = 0; i < IWM_STA_TABLE_NUM; i++)
7583db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell			iwm->sta_table[i].valid = false;
759bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
760bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
761bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	default:
762bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
763bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
764bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
765bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
766bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
767bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
768b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yistatic int iwm_mlme_medium_lost(struct iwm_priv *iwm, u8 *buf,
769b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi				unsigned long buf_size,
770b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi				struct iwm_wifi_cmd *cmd)
771b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi{
772b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi	struct wiphy *wiphy = iwm_to_wiphy(iwm);
773b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi
774b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi	IWM_DBG_NTF(iwm, DBG, "WiFi/WiMax coexistence radio is OFF\n");
775b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi
776b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi	wiphy_rfkill_set_hw_state(wiphy, true);
777b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi
778b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi	return 0;
779b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi}
780b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi
781bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_mlme_update_bss_table(struct iwm_priv *iwm, u8 *buf,
782bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				     unsigned long buf_size,
783bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				     struct iwm_wifi_cmd *cmd)
784bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
785bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct wiphy *wiphy = iwm_to_wiphy(iwm);
786bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct ieee80211_mgmt *mgmt;
787bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_bss_info *umac_bss =
788bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			(struct iwm_umac_notif_bss_info *)buf;
789bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct ieee80211_channel *channel;
790bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct ieee80211_supported_band *band;
79104d1c22761f33ac8f345665e7ef809c875142425Zhu Yi	struct iwm_bss_info *bss;
792bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	s32 signal;
793bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int freq;
794bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u16 frame_len = le16_to_cpu(umac_bss->frame_len);
795bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	size_t bss_len = sizeof(struct iwm_umac_notif_bss_info) + frame_len;
796bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
797bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
798bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
799bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_MLME(iwm, DBG, "New BSS info entry: %pM\n", mgmt->bssid);
800bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_MLME(iwm, DBG, "\tType: 0x%x\n", le32_to_cpu(umac_bss->type));
801bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_MLME(iwm, DBG, "\tTimestamp: %d\n",
802bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		     le32_to_cpu(umac_bss->timestamp));
803bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_MLME(iwm, DBG, "\tTable Index: %d\n",
804bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		     le16_to_cpu(umac_bss->table_idx));
805bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_MLME(iwm, DBG, "\tBand: %d\n", umac_bss->band);
806bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_MLME(iwm, DBG, "\tChannel: %d\n", umac_bss->channel);
807bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_MLME(iwm, DBG, "\tRSSI: %d\n", umac_bss->rssi);
808bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_MLME(iwm, DBG, "\tFrame Length: %d\n", frame_len);
809bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
81004d1c22761f33ac8f345665e7ef809c875142425Zhu Yi	list_for_each_entry(bss, &iwm->bss_list, node)
811bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (bss->bss->table_idx == umac_bss->table_idx)
812bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			break;
813bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
814bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (&bss->node != &iwm->bss_list) {
815bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		/* Remove the old BSS entry, we will add it back later. */
816bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		list_del(&bss->node);
817bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		kfree(bss->bss);
818bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	} else {
819bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		/* New BSS entry */
820bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
821bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		bss = kzalloc(sizeof(struct iwm_bss_info), GFP_KERNEL);
822bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (!bss) {
823bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			IWM_ERR(iwm, "Couldn't allocate bss_info\n");
824bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			return -ENOMEM;
825bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		}
826bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
827bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
828bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	bss->bss = kzalloc(bss_len, GFP_KERNEL);
82933a5d083e786f0c3fb4efedb59b0e8e3de39963bRoel Kluin	if (!bss->bss) {
830bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		kfree(bss);
831bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_ERR(iwm, "Couldn't allocate bss\n");
832bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return -ENOMEM;
833bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
834bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
835bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	INIT_LIST_HEAD(&bss->node);
836bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	memcpy(bss->bss, umac_bss, bss_len);
837bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
838bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (umac_bss->band == UMAC_BAND_2GHZ)
839bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		band = wiphy->bands[IEEE80211_BAND_2GHZ];
840bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	else if (umac_bss->band == UMAC_BAND_5GHZ)
841bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		band = wiphy->bands[IEEE80211_BAND_5GHZ];
842bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	else {
843bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
844bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		goto err;
845bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
846bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
84759eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf	freq = ieee80211_channel_to_frequency(umac_bss->channel, band->band);
848bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	channel = ieee80211_get_channel(wiphy, freq);
849bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	signal = umac_bss->rssi * 100;
850bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
851bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	bss->cfg_bss = cfg80211_inform_bss_frame(wiphy, channel,
852bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi						 mgmt, frame_len,
853bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi						 signal, GFP_KERNEL);
854bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (!bss->cfg_bss)
855bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		goto err;
856bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
857bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	list_add_tail(&bss->node, &iwm->bss_list);
858bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
859bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
860bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi err:
861bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	kfree(bss->bss);
862bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	kfree(bss);
863bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
864bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return -EINVAL;
865bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
866bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
867bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_mlme_remove_bss(struct iwm_priv *iwm, u8 *buf,
868bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			       unsigned long buf_size, struct iwm_wifi_cmd *cmd)
869bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
870bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_bss_removed *bss_rm =
871bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		(struct iwm_umac_notif_bss_removed *)buf;
872bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_bss_info *bss, *next;
873bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u16 table_idx;
874bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int i;
875bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
876bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	for (i = 0; i < le32_to_cpu(bss_rm->count); i++) {
8771da3f88222579135569ad52d1c82a7393cf87178Zhu Yi		table_idx = le16_to_cpu(bss_rm->entries[i]) &
8781da3f88222579135569ad52d1c82a7393cf87178Zhu Yi			    IWM_BSS_REMOVE_INDEX_MSK;
879bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		list_for_each_entry_safe(bss, next, &iwm->bss_list, node)
880bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			if (bss->bss->table_idx == cpu_to_le16(table_idx)) {
881bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				struct ieee80211_mgmt *mgmt;
882bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
883bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				mgmt = (struct ieee80211_mgmt *)
884bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi					(bss->bss->frame_buf);
8851da3f88222579135569ad52d1c82a7393cf87178Zhu Yi				IWM_DBG_MLME(iwm, ERR, "BSS removed: %pM\n",
886bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi					     mgmt->bssid);
887bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				list_del(&bss->node);
888bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				kfree(bss->bss);
889bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				kfree(bss);
890bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			}
891bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
892bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
893bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
894bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
895bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
896bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_mlme_mgt_frame(struct iwm_priv *iwm, u8 *buf,
897bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
898bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
899bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_mgt_frame *mgt_frame =
900b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi			(struct iwm_umac_notif_mgt_frame *)buf;
901bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct ieee80211_mgmt *mgt = (struct ieee80211_mgmt *)mgt_frame->frame;
902bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
903bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_HEXDUMP(iwm, DBG, MLME, "MGT: ", mgt_frame->frame,
904bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		    le16_to_cpu(mgt_frame->len));
905bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
906bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (ieee80211_is_assoc_req(mgt->frame_control)) {
907d80050c4c321a74cea28c6ef9695d8427a928417Joe Perches		iwm->req_ie_len = le16_to_cpu(mgt_frame->len)
908d80050c4c321a74cea28c6ef9695d8427a928417Joe Perches				  - offsetof(struct ieee80211_mgmt,
909d80050c4c321a74cea28c6ef9695d8427a928417Joe Perches					     u.assoc_req.variable);
910b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi		kfree(iwm->req_ie);
911b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi		iwm->req_ie = kmemdup(mgt->u.assoc_req.variable,
912b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi				      iwm->req_ie_len, GFP_KERNEL);
913bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	} else if (ieee80211_is_reassoc_req(mgt->frame_control)) {
914d80050c4c321a74cea28c6ef9695d8427a928417Joe Perches		iwm->req_ie_len = le16_to_cpu(mgt_frame->len)
915d80050c4c321a74cea28c6ef9695d8427a928417Joe Perches				  - offsetof(struct ieee80211_mgmt,
916d80050c4c321a74cea28c6ef9695d8427a928417Joe Perches					     u.reassoc_req.variable);
917b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi		kfree(iwm->req_ie);
918b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi		iwm->req_ie = kmemdup(mgt->u.reassoc_req.variable,
919b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi				      iwm->req_ie_len, GFP_KERNEL);
920bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	} else if (ieee80211_is_assoc_resp(mgt->frame_control)) {
921d80050c4c321a74cea28c6ef9695d8427a928417Joe Perches		iwm->resp_ie_len = le16_to_cpu(mgt_frame->len)
922d80050c4c321a74cea28c6ef9695d8427a928417Joe Perches				   - offsetof(struct ieee80211_mgmt,
923d80050c4c321a74cea28c6ef9695d8427a928417Joe Perches					      u.assoc_resp.variable);
924b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi		kfree(iwm->resp_ie);
925b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi		iwm->resp_ie = kmemdup(mgt->u.assoc_resp.variable,
926b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi				       iwm->resp_ie_len, GFP_KERNEL);
927bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	} else if (ieee80211_is_reassoc_resp(mgt->frame_control)) {
928d80050c4c321a74cea28c6ef9695d8427a928417Joe Perches		iwm->resp_ie_len = le16_to_cpu(mgt_frame->len)
929d80050c4c321a74cea28c6ef9695d8427a928417Joe Perches				   - offsetof(struct ieee80211_mgmt,
930d80050c4c321a74cea28c6ef9695d8427a928417Joe Perches					      u.reassoc_resp.variable);
931b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi		kfree(iwm->resp_ie);
932b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi		iwm->resp_ie = kmemdup(mgt->u.reassoc_resp.variable,
933b68518fcbc6e0fe8c06a218cd2b92f62f3730cf9Zhu Yi				       iwm->resp_ie_len, GFP_KERNEL);
934bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	} else {
935de15fd31fcabb4b81a556736dd67ec4f71462f07Zhu Yi		IWM_ERR(iwm, "Unsupported management frame: 0x%x",
93631452420ca956f2cf37f705c869e265c33894f07Zhu Yi			le16_to_cpu(mgt->frame_control));
937bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return 0;
938bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
939bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
940bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
941bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
942bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
943bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_mlme(struct iwm_priv *iwm, u8 *buf,
944bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			unsigned long buf_size, struct iwm_wifi_cmd *cmd)
945bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
946bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_wifi_if *notif =
947bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		(struct iwm_umac_notif_wifi_if *)buf;
948bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
949bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	switch (notif->status) {
950bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_IF_NTFY_ASSOC_START:
951bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return iwm_mlme_assoc_start(iwm, buf, buf_size, cmd);
952bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_IF_NTFY_ASSOC_COMPLETE:
953bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return iwm_mlme_assoc_complete(iwm, buf, buf_size, cmd);
954bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_IF_NTFY_PROFILE_INVALIDATE_COMPLETE:
955bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return iwm_mlme_profile_invalidate(iwm, buf, buf_size, cmd);
956bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_IF_NTFY_CONNECTION_TERMINATED:
957c7436273889e0ce511b317041f35344e92344885Zhu Yi		return iwm_mlme_connection_terminated(iwm, buf, buf_size, cmd);
958bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_IF_NTFY_SCAN_COMPLETE:
959bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return iwm_mlme_scan_complete(iwm, buf, buf_size, cmd);
960bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_IF_NTFY_STA_TABLE_CHANGE:
961bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return iwm_mlme_update_sta_table(iwm, buf, buf_size, cmd);
962bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_IF_NTFY_EXTENDED_IE_REQUIRED:
963bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_DBG_MLME(iwm, DBG, "Extended IE required\n");
964bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
965b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi	case WIFI_IF_NTFY_RADIO_PREEMPTION:
966b8fcf590939f0aa24d43bdbdae7c963f31ba90bdZhu Yi		return iwm_mlme_medium_lost(iwm, buf, buf_size, cmd);
967bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_IF_NTFY_BSS_TRK_TABLE_CHANGED:
968bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return iwm_mlme_update_bss_table(iwm, buf, buf_size, cmd);
969bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_IF_NTFY_BSS_TRK_ENTRIES_REMOVED:
970bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return iwm_mlme_remove_bss(iwm, buf, buf_size, cmd);
971bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
972bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_IF_NTFY_MGMT_FRAME:
973bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return iwm_mlme_mgt_frame(iwm, buf, buf_size, cmd);
974bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_START:
975bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_COMPLETE:
976bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_DBG_IF_NTFY_SCAN_CHANNEL_START:
977bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_DBG_IF_NTFY_SCAN_CHANNEL_RESULT:
978bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_START:
979bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_COMPLETE:
980bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_DBG_IF_NTFY_CNCT_ATC_START:
981bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_DBG_IF_NTFY_COEX_NOTIFICATION:
982bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_DBG_IF_NTFY_COEX_HANDLE_ENVELOP:
983bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case WIFI_DBG_IF_NTFY_COEX_HANDLE_RELEASE_ENVELOP:
984bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_DBG_MLME(iwm, DBG, "MLME debug notification: 0x%x\n",
985bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			     notif->status);
986bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
987bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	default:
988bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_ERR(iwm, "Unhandled notification: 0x%x\n", notif->status);
989bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
990bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
991bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
992bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
993bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
994bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
995bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi#define IWM_STATS_UPDATE_INTERVAL		(2 * HZ)
996bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
997bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_statistics(struct iwm_priv *iwm, u8 *buf,
998bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
999bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1000bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_notif_stats *stats = (struct iwm_umac_notif_stats *)buf;
1001bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iw_statistics *wstats = &iwm->wstats;
1002bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u16 max_rate = 0;
1003bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int i;
1004bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1005bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_MLME(iwm, DBG, "Statistics notification received\n");
1006bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1007bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
1008bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		for (i = 0; i < UMAC_NTF_RATE_SAMPLE_NR; i++) {
1009bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			max_rate = max_t(u16, max_rate,
1010bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi					 max(le16_to_cpu(stats->tx_rate[i]),
1011bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi					     le16_to_cpu(stats->rx_rate[i])));
1012bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		}
1013bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		/* UMAC passes rate info multiplies by 2 */
1014bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		iwm->rate = max_rate >> 1;
1015bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1016257862f3faef397f1a677ae6a5a1828fa00a97b1Zhu Yi	iwm->txpower = le32_to_cpu(stats->tx_power);
1017bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1018bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wstats->status = 0;
1019bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1020bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wstats->discard.nwid = le32_to_cpu(stats->rx_drop_other_bssid);
1021bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wstats->discard.code = le32_to_cpu(stats->rx_drop_decode);
1022bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wstats->discard.fragment = le32_to_cpu(stats->rx_drop_reassembly);
1023bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wstats->discard.retries = le32_to_cpu(stats->tx_drop_max_retry);
1024bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1025bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wstats->miss.beacon = le32_to_cpu(stats->missed_beacons);
1026bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1027bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	/* according to cfg80211 */
1028bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (stats->rssi_dbm < -110)
1029bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		wstats->qual.qual = 0;
1030bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	else if (stats->rssi_dbm > -40)
1031bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		wstats->qual.qual = 70;
1032bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	else
1033bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		wstats->qual.qual = stats->rssi_dbm + 110;
1034bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1035bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wstats->qual.level = stats->rssi_dbm;
1036bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wstats->qual.noise = stats->noise_dbm;
1037bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
1038bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1039bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	schedule_delayed_work(&iwm->stats_request, IWM_STATS_UPDATE_INTERVAL);
1040bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1041bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	mod_timer(&iwm->watchdog, round_jiffies(jiffies + IWM_WATCHDOG_PERIOD));
1042bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1043bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
1044bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1045bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1046bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_eeprom_proxy(struct iwm_priv *iwm, u8 *buf,
1047bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				unsigned long buf_size,
1048bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				struct iwm_wifi_cmd *cmd)
1049bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1050bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_cmd_eeprom_proxy *eeprom_proxy =
1051bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		(struct iwm_umac_cmd_eeprom_proxy *)
1052bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		(buf + sizeof(struct iwm_umac_wifi_in_hdr));
1053bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_cmd_eeprom_proxy_hdr *hdr = &eeprom_proxy->hdr;
1054bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u32 hdr_offset = le32_to_cpu(hdr->offset);
1055bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u32 hdr_len = le32_to_cpu(hdr->len);
1056bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u32 hdr_type = le32_to_cpu(hdr->type);
1057bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1058bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_NTF(iwm, DBG, "type: 0x%x, len: %d, offset: 0x%x\n",
1059bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		    hdr_type, hdr_len, hdr_offset);
1060bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1061bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if ((hdr_offset + hdr_len) > IWM_EEPROM_LEN)
1062bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return -EINVAL;
1063bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1064bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	switch (hdr_type) {
1065bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case IWM_UMAC_CMD_EEPROM_TYPE_READ:
1066bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		memcpy(iwm->eeprom + hdr_offset, eeprom_proxy->buf, hdr_len);
1067bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
1068bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case IWM_UMAC_CMD_EEPROM_TYPE_WRITE:
1069bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	default:
1070bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return -ENOTSUPP;
1071bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1072bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1073bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
1074bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1075bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1076bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_channel_info_list(struct iwm_priv *iwm, u8 *buf,
1077bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				     unsigned long buf_size,
1078bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				     struct iwm_wifi_cmd *cmd)
1079bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1080bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_cmd_get_channel_list *ch_list =
1081bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			(struct iwm_umac_cmd_get_channel_list *)
1082bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			(buf + sizeof(struct iwm_umac_wifi_in_hdr));
1083bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct wiphy *wiphy = iwm_to_wiphy(iwm);
1084bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct ieee80211_supported_band *band;
1085bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int i;
1086bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1087bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	band = wiphy->bands[IEEE80211_BAND_2GHZ];
1088bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1089bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	for (i = 0; i < band->n_channels; i++) {
1090bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		unsigned long ch_mask_0 =
1091bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			le32_to_cpu(ch_list->ch[0].channels_mask);
1092bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		unsigned long ch_mask_2 =
1093bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			le32_to_cpu(ch_list->ch[2].channels_mask);
1094bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1095bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (!test_bit(i, &ch_mask_0))
1096bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			band->channels[i].flags |= IEEE80211_CHAN_DISABLED;
1097bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1098bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (!test_bit(i, &ch_mask_2))
1099bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			band->channels[i].flags |= IEEE80211_CHAN_NO_IBSS;
1100bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1101bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1102bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	band = wiphy->bands[IEEE80211_BAND_5GHZ];
1103bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1104bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	for (i = 0; i < min(band->n_channels, 32); i++) {
1105bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		unsigned long ch_mask_1 =
1106bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			le32_to_cpu(ch_list->ch[1].channels_mask);
1107bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		unsigned long ch_mask_3 =
1108bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			le32_to_cpu(ch_list->ch[3].channels_mask);
1109bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1110bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (!test_bit(i, &ch_mask_1))
1111bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			band->channels[i].flags |= IEEE80211_CHAN_DISABLED;
1112bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1113bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (!test_bit(i, &ch_mask_3))
1114bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			band->channels[i].flags |= IEEE80211_CHAN_NO_IBSS;
1115bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1116bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1117bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
1118bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1119bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1120a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortizstatic int iwm_ntf_stop_resume_tx(struct iwm_priv *iwm, u8 *buf,
1121a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz				  unsigned long buf_size,
1122a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz				  struct iwm_wifi_cmd *cmd)
1123a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz{
1124a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	struct iwm_umac_notif_stop_resume_tx *stp_res_tx =
1125a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		(struct iwm_umac_notif_stop_resume_tx *)buf;
1126a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	struct iwm_sta_info *sta_info;
1127a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	struct iwm_tid_info *tid_info;
1128a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	u8 sta_id = STA_ID_N_COLOR_ID(stp_res_tx->sta_id);
1129a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	u16 tid_msk = le16_to_cpu(stp_res_tx->stop_resume_tid_msk);
1130a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	int bit, ret = 0;
1131a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	bool stop = false;
1132a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1133a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	IWM_DBG_NTF(iwm, DBG, "stop/resume notification:\n"
1134a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		    "\tflags:       0x%x\n"
1135a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		    "\tSTA id:      %d\n"
1136a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		    "\tTID bitmask: 0x%x\n",
1137a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		    stp_res_tx->flags, stp_res_tx->sta_id,
1138a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		    stp_res_tx->stop_resume_tid_msk);
1139a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1140a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	if (stp_res_tx->flags & UMAC_STOP_TX_FLAG)
1141a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		stop = true;
1142a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1143a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	sta_info = &iwm->sta_table[sta_id];
1144a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	if (!sta_info->valid) {
1145a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		IWM_ERR(iwm, "Stoping an invalid STA: %d %d\n",
1146a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz			sta_id, stp_res_tx->sta_id);
1147a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		return -EINVAL;
1148a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	}
1149a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1150984b3f5746ed2cde3d184651dabf26980f2b66e5Akinobu Mita	for_each_set_bit(bit, (unsigned long *)&tid_msk, IWM_UMAC_TID_NR) {
1151a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		tid_info = &sta_info->tid_info[bit];
1152a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1153a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		mutex_lock(&tid_info->mutex);
1154a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		tid_info->stopped = stop;
1155a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		mutex_unlock(&tid_info->mutex);
1156a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1157a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		if (!stop) {
1158a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz			struct iwm_tx_queue *txq;
11598585c2b896861aacd15337c3c7e58ad114e6cf60Roel Kluin			int queue = iwm_tid_to_queue(bit);
1160a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1161a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz			if (queue < 0)
1162a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz				continue;
1163a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1164a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz			txq = &iwm->txq[queue];
1165a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz			/*
1166a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz			 * If we resume, we have to move our SKBs
1167a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz			 * back to the tx queue and queue some work.
1168a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz			 */
1169a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz			spin_lock_bh(&txq->lock);
1170a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz			skb_queue_splice_init(&txq->queue, &txq->stopped_queue);
1171a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz			spin_unlock_bh(&txq->lock);
1172a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1173a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz			queue_work(txq->wq, &txq->worker);
1174a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		}
1175a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1176a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	}
1177a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1178a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	/* We send an ACK only for the stop case */
1179a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	if (stop)
1180a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz		ret = iwm_send_umac_stop_resume_tx(iwm, stp_res_tx);
1181a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1182a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	return ret;
1183a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz}
1184a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz
1185bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf,
1186bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				   unsigned long buf_size,
1187bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				   struct iwm_wifi_cmd *cmd)
1188bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1189708567e0723f3a217286c2b60805af6de360ec50Samuel Ortiz	struct iwm_umac_wifi_if *hdr;
1190708567e0723f3a217286c2b60805af6de360ec50Samuel Ortiz
1191708567e0723f3a217286c2b60805af6de360ec50Samuel Ortiz	if (cmd == NULL) {
1192708567e0723f3a217286c2b60805af6de360ec50Samuel Ortiz		IWM_ERR(iwm, "Couldn't find expected wifi command\n");
1193708567e0723f3a217286c2b60805af6de360ec50Samuel Ortiz		return -EINVAL;
1194708567e0723f3a217286c2b60805af6de360ec50Samuel Ortiz	}
1195708567e0723f3a217286c2b60805af6de360ec50Samuel Ortiz
1196708567e0723f3a217286c2b60805af6de360ec50Samuel Ortiz	hdr = (struct iwm_umac_wifi_if *)cmd->buf.payload;
1197bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1198bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_NTF(iwm, DBG, "WIFI_IF_WRAPPER cmd is delivered to UMAC: "
1199a70742f167424bab794ca74b9e99b598b358bb7dSamuel Ortiz		    "oid is 0x%x\n", hdr->oid);
1200a70742f167424bab794ca74b9e99b598b358bb7dSamuel Ortiz
120113eb670c104e15e06d38f3a210cfaf467a9c66deJohn W. Linville	set_bit(hdr->oid, &iwm->wifi_ntfy[0]);
120213eb670c104e15e06d38f3a210cfaf467a9c66deJohn W. Linville	wake_up_interruptible(&iwm->wifi_ntfy_queue);
1203bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1204bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	switch (hdr->oid) {
1205bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_WIFI_IF_CMD_SET_PROFILE:
12063db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell		iwm->umac_profile_active = true;
1207bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
1208bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	default:
1209bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
1210bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1211bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1212bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
1213bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1214bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1215e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz#define CT_KILL_DELAY (30 * HZ)
1216bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_ntf_card_state(struct iwm_priv *iwm, u8 *buf,
1217bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
1218bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1219257862f3faef397f1a677ae6a5a1828fa00a97b1Zhu Yi	struct wiphy *wiphy = iwm_to_wiphy(iwm);
1220bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_lmac_card_state *state = (struct iwm_lmac_card_state *)
1221bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				(buf + sizeof(struct iwm_umac_wifi_in_hdr));
1222bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u32 flags = le32_to_cpu(state->flags);
1223bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1224bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_INFO(iwm, "HW RF Kill %s, CT Kill %s\n",
1225bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		 flags & IWM_CARD_STATE_HW_DISABLED ? "ON" : "OFF",
1226bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		 flags & IWM_CARD_STATE_CTKILL_DISABLED ? "ON" : "OFF");
1227bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1228e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz	if (flags & IWM_CARD_STATE_CTKILL_DISABLED) {
1229e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz		/*
1230e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz		 * We got a CTKILL event: We bring the interface down in
1231e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz		 * oder to cool the device down, and try to bring it up
1232e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz		 * 30 seconds later. If it's still too hot, we'll go through
1233e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz		 * this code path again.
1234e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz		 */
1235e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz		cancel_delayed_work_sync(&iwm->ct_kill_delay);
1236e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz		schedule_delayed_work(&iwm->ct_kill_delay, CT_KILL_DELAY);
1237e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz	}
1238e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz
1239e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz	wiphy_rfkill_set_hw_state(wiphy, flags &
1240e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz				  (IWM_CARD_STATE_HW_DISABLED |
1241e85498b21d0372a00f31ab9f7c0d215c32c9fad5Samuel Ortiz				   IWM_CARD_STATE_CTKILL_DISABLED));
1242bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1243bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
1244bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1245bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1246bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_rx_handle_wifi(struct iwm_priv *iwm, u8 *buf,
1247bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			      unsigned long buf_size)
1248bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1249bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_wifi_in_hdr *wifi_hdr;
1250bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_wifi_cmd *cmd;
1251bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u8 source, cmd_id;
1252bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u16 seq_num;
1253bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u32 count;
1254bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1255bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf;
1256bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	cmd_id = wifi_hdr->sw_hdr.cmd.cmd;
1257bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	source = GET_VAL32(wifi_hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE);
1258bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (source >= IWM_SRC_NUM) {
1259bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_CRIT(iwm, "invalid source %d\n", source);
1260bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return -EINVAL;
1261bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1262bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
126334dd5feb8b8b15654714731e1dbb34a6d37fb34eZhu Yi	if (cmd_id == REPLY_RX_MPDU_CMD)
126434dd5feb8b8b15654714731e1dbb34a6d37fb34eZhu Yi		trace_iwm_rx_packet(iwm, buf, buf_size);
126534dd5feb8b8b15654714731e1dbb34a6d37fb34eZhu Yi	else if ((cmd_id == UMAC_NOTIFY_OPCODE_RX_TICKET) &&
126634dd5feb8b8b15654714731e1dbb34a6d37fb34eZhu Yi		 (source == UMAC_HDI_IN_SOURCE_FW))
126734dd5feb8b8b15654714731e1dbb34a6d37fb34eZhu Yi		trace_iwm_rx_ticket(iwm, buf, buf_size);
126834dd5feb8b8b15654714731e1dbb34a6d37fb34eZhu Yi	else
126934dd5feb8b8b15654714731e1dbb34a6d37fb34eZhu Yi		trace_iwm_rx_wifi_cmd(iwm, wifi_hdr);
127034dd5feb8b8b15654714731e1dbb34a6d37fb34eZhu Yi
127134dd5feb8b8b15654714731e1dbb34a6d37fb34eZhu Yi	count = GET_VAL32(wifi_hdr->sw_hdr.meta_data, UMAC_FW_CMD_BYTE_COUNT);
1272bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	count += sizeof(struct iwm_umac_wifi_in_hdr) -
1273bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		 sizeof(struct iwm_dev_cmd_hdr);
1274bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (count > buf_size) {
1275bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_CRIT(iwm, "count %d, buf size:%ld\n", count, buf_size);
1276bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return -EINVAL;
1277bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1278bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1279bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	seq_num = le16_to_cpu(wifi_hdr->sw_hdr.cmd.seq_num);
1280bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1281bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_RX(iwm, DBG, "CMD:0x%x, source: 0x%x, seqnum: %d\n",
1282bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		   cmd_id, source, seq_num);
1283bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1284bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	/*
1285bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * If this is a response to a previously sent command, there must
1286bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * be a pending command for this sequence number.
1287bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 */
1288bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	cmd = iwm_get_pending_wifi_cmd(iwm, seq_num);
1289bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1290bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	/* Notify the caller only for sync commands. */
1291bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	switch (source) {
1292bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_HDI_IN_SOURCE_FHRX:
1293bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (iwm->lmac_handlers[cmd_id] &&
1294bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		    test_bit(cmd_id, &iwm->lmac_handler_map[0]))
1295bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			return iwm_notif_send(iwm, cmd, cmd_id, source,
1296bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi					      buf, count);
1297bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
1298bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_HDI_IN_SOURCE_FW:
1299bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (iwm->umac_handlers[cmd_id] &&
1300bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		    test_bit(cmd_id, &iwm->umac_handler_map[0]))
1301bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			return iwm_notif_send(iwm, cmd, cmd_id, source,
1302bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi					      buf, count);
1303bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
1304bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_HDI_IN_SOURCE_UDMA:
1305bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
1306bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1307bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1308bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return iwm_rx_handle_resp(iwm, buf, count, cmd);
1309bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1310bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1311bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yiint iwm_rx_handle_resp(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size,
1312bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		       struct iwm_wifi_cmd *cmd)
1313bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1314bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u8 source, cmd_id;
1315bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_umac_wifi_in_hdr *wifi_hdr;
1316bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int ret = 0;
1317bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1318bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf;
1319bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	cmd_id = wifi_hdr->sw_hdr.cmd.cmd;
1320bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1321bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	source = GET_VAL32(wifi_hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE);
1322bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1323bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_RX(iwm, DBG, "CMD:0x%x, source: 0x%x\n", cmd_id, source);
1324bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1325bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	switch (source) {
1326bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_HDI_IN_SOURCE_FHRX:
1327bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (iwm->lmac_handlers[cmd_id])
1328bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			ret = iwm->lmac_handlers[cmd_id]
1329bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi					(iwm, buf, buf_size, cmd);
1330bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
1331bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_HDI_IN_SOURCE_FW:
1332bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (iwm->umac_handlers[cmd_id])
1333bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			ret = iwm->umac_handlers[cmd_id]
1334bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi					(iwm, buf, buf_size, cmd);
1335bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
1336bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_HDI_IN_SOURCE_UDMA:
1337bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		ret = -EINVAL;
1338bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
1339bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1340bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1341bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	kfree(cmd);
1342bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1343bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return ret;
1344bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1345bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1346bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_rx_handle_nonwifi(struct iwm_priv *iwm, u8 *buf,
1347bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				 unsigned long buf_size)
1348bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1349bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u8 seq_num;
1350bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_udma_in_hdr *hdr = (struct iwm_udma_in_hdr *)buf;
135104d1c22761f33ac8f345665e7ef809c875142425Zhu Yi	struct iwm_nonwifi_cmd *cmd;
1352bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
135334dd5feb8b8b15654714731e1dbb34a6d37fb34eZhu Yi	trace_iwm_rx_nonwifi_cmd(iwm, buf, buf_size);
1354bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	seq_num = GET_VAL32(hdr->cmd, UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM);
1355bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1356bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	/*
1357bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * We received a non wifi answer.
1358bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * Let's check if there's a pending command for it, and if so
1359bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * replace the command payload with the buffer, and then wake the
1360bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * callers up.
1361bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * That means we only support synchronised non wifi command response
1362bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * schemes.
1363bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 */
136404d1c22761f33ac8f345665e7ef809c875142425Zhu Yi	list_for_each_entry(cmd, &iwm->nonwifi_pending_cmd, pending)
1365bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (cmd->seq_num == seq_num) {
13663db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell			cmd->resp_received = true;
1367bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			cmd->buf.len = buf_size;
1368bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			memcpy(cmd->buf.hdr, buf, buf_size);
1369bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			wake_up_interruptible(&iwm->nonwifi_queue);
1370bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		}
1371bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1372bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
1373bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1374bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1375bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic int iwm_rx_handle_umac(struct iwm_priv *iwm, u8 *buf,
1376bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			      unsigned long buf_size)
1377bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1378bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int ret = 0;
1379bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u8 op_code;
1380bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	unsigned long buf_offset = 0;
1381bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_udma_in_hdr *hdr;
1382bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1383bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	/*
1384bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * To allow for a more efficient bus usage, UMAC
1385bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * messages are encapsulated into UDMA ones. This
1386bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * way we can have several UMAC messages in one bus
1387bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * transfer.
1388bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * A UDMA frame size is always aligned on 16 bytes,
1389bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * and a UDMA frame must not start with a UMAC_PAD_TERMINAL
1390bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * word. This is how we parse a bus frame into several
1391bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * UDMA ones.
1392bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 */
1393bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	while (buf_offset < buf_size) {
1394bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1395bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		hdr = (struct iwm_udma_in_hdr *)(buf + buf_offset);
1396bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1397bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (iwm_rx_check_udma_hdr(hdr) < 0) {
1398bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			IWM_DBG_RX(iwm, DBG, "End of frame\n");
1399bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			break;
1400bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		}
1401bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1402bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		op_code = GET_VAL32(hdr->cmd, UMAC_HDI_IN_CMD_OPCODE);
1403bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1404bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_DBG_RX(iwm, DBG, "Op code: 0x%x\n", op_code);
1405bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1406bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (op_code == UMAC_HDI_IN_OPCODE_WIFI) {
1407bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			ret |= iwm_rx_handle_wifi(iwm, buf + buf_offset,
1408bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi						  buf_size - buf_offset);
1409bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		} else if (op_code < UMAC_HDI_IN_OPCODE_NONWIFI_MAX) {
1410bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			if (GET_VAL32(hdr->cmd,
1411bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				      UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG) !=
1412bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			    UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG) {
1413bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				IWM_ERR(iwm, "Incorrect hw signature\n");
1414bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				return -EINVAL;
1415bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			}
1416bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			ret |= iwm_rx_handle_nonwifi(iwm, buf + buf_offset,
1417bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi						     buf_size - buf_offset);
1418bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		} else {
1419bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			IWM_ERR(iwm, "Invalid RX opcode: 0x%x\n", op_code);
1420bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			ret |= -EINVAL;
1421bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		}
1422bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1423bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		buf_offset += iwm_rx_resp_size(hdr);
1424bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1425bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1426bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return ret;
1427bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1428bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1429bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yiint iwm_rx_handle(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size)
1430bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1431bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_udma_in_hdr *hdr;
1432bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1433bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	hdr = (struct iwm_udma_in_hdr *)buf;
1434bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1435bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	switch (le32_to_cpu(hdr->cmd)) {
1436bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_REBOOT_BARKER:
14377fd6b12f329b7ec1c1e3ad49d701d2ac7ab42d9eSamuel Ortiz		if (test_bit(IWM_STATUS_READY, &iwm->status)) {
14387fd6b12f329b7ec1c1e3ad49d701d2ac7ab42d9eSamuel Ortiz			IWM_ERR(iwm, "Unexpected BARKER\n");
14397fd6b12f329b7ec1c1e3ad49d701d2ac7ab42d9eSamuel Ortiz
14407fd6b12f329b7ec1c1e3ad49d701d2ac7ab42d9eSamuel Ortiz			schedule_work(&iwm->reset_worker);
14417fd6b12f329b7ec1c1e3ad49d701d2ac7ab42d9eSamuel Ortiz
14427fd6b12f329b7ec1c1e3ad49d701d2ac7ab42d9eSamuel Ortiz			return 0;
14437fd6b12f329b7ec1c1e3ad49d701d2ac7ab42d9eSamuel Ortiz		}
14447fd6b12f329b7ec1c1e3ad49d701d2ac7ab42d9eSamuel Ortiz
1445bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return iwm_notif_send(iwm, NULL, IWM_BARKER_REBOOT_NOTIFICATION,
1446bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				      IWM_SRC_UDMA, buf, buf_size);
1447bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case UMAC_ACK_BARKER:
1448bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return iwm_notif_send(iwm, NULL, IWM_ACK_BARKER_NOTIFICATION,
1449bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				      IWM_SRC_UDMA, NULL, 0);
1450bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	default:
1451bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_DBG_RX(iwm, DBG, "Received cmd: 0x%x\n", hdr->cmd);
1452bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return iwm_rx_handle_umac(iwm, buf, buf_size);
1453bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1454bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1455bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	return 0;
1456bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1457bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1458bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic const iwm_handler iwm_umac_handlers[] =
1459bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1460bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[UMAC_NOTIFY_OPCODE_ERROR]		= iwm_ntf_error,
1461bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[UMAC_NOTIFY_OPCODE_ALIVE]		= iwm_ntf_umac_alive,
1462bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[UMAC_NOTIFY_OPCODE_INIT_COMPLETE]	= iwm_ntf_init_complete,
1463bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS]	= iwm_ntf_wifi_status,
1464bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[UMAC_NOTIFY_OPCODE_WIFI_IF_WRAPPER]	= iwm_ntf_mlme,
1465bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[UMAC_NOTIFY_OPCODE_PAGE_DEALLOC]	= iwm_ntf_tx_credit_update,
1466bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[UMAC_NOTIFY_OPCODE_RX_TICKET]		= iwm_ntf_rx_ticket,
1467bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[UMAC_CMD_OPCODE_RESET]			= iwm_ntf_umac_reset,
1468bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[UMAC_NOTIFY_OPCODE_STATS]		= iwm_ntf_statistics,
1469bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[UMAC_CMD_OPCODE_EEPROM_PROXY]		= iwm_ntf_eeprom_proxy,
1470bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST]	= iwm_ntf_channel_info_list,
1471a7af530d45969a63e20708417b70c547596ce3a9Samuel Ortiz	[UMAC_CMD_OPCODE_STOP_RESUME_STA_TX]	= iwm_ntf_stop_resume_tx,
1472bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[REPLY_RX_MPDU_CMD]			= iwm_ntf_rx_packet,
1473bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[UMAC_CMD_OPCODE_WIFI_IF_WRAPPER]	= iwm_ntf_wifi_if_wrapper,
1474bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi};
1475bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1476bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic const iwm_handler iwm_lmac_handlers[] =
1477bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1478bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[REPLY_TX]				= iwm_ntf_tx,
1479bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[REPLY_ALIVE]				= iwm_ntf_lmac_version,
1480bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[CALIBRATION_RES_NOTIFICATION]		= iwm_ntf_calib_res,
1481bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[CALIBRATION_COMPLETE_NOTIFICATION]	= iwm_ntf_calib_complete,
1482bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[CALIBRATION_CFG_CMD]			= iwm_ntf_calib_cfg,
1483bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[REPLY_RX_MPDU_CMD]			= iwm_ntf_rx_packet,
1484bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	[CARD_STATE_NOTIFICATION]		= iwm_ntf_card_state,
1485bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi};
1486bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1487bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yivoid iwm_rx_setup_handlers(struct iwm_priv *iwm)
1488bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1489bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	iwm->umac_handlers = (iwm_handler *) iwm_umac_handlers;
1490bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	iwm->lmac_handlers = (iwm_handler *) iwm_lmac_handlers;
1491bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1492bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1493bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic void iwm_remove_iv(struct sk_buff *skb, u32 hdr_total_len)
1494bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1495bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct ieee80211_hdr *hdr;
1496bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	unsigned int hdr_len;
1497bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1498bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	hdr = (struct ieee80211_hdr *)skb->data;
1499bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1500bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (!ieee80211_has_protected(hdr->frame_control))
1501bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return;
1502bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1503bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	hdr_len = ieee80211_hdrlen(hdr->frame_control);
1504bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (hdr_total_len <= hdr_len)
1505bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		return;
1506bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1507bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	memmove(skb->data + (hdr_total_len - hdr_len), skb->data, hdr_len);
1508bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	skb_pull(skb, (hdr_total_len - hdr_len));
1509bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1510bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1511bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic void iwm_rx_adjust_packet(struct iwm_priv *iwm,
1512bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				 struct iwm_rx_packet *packet,
1513bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				 struct iwm_rx_ticket_node *ticket_node)
1514bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1515bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	u32 payload_offset = 0, payload_len;
1516bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_rx_ticket *ticket = ticket_node->ticket;
1517bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_rx_mpdu_hdr *mpdu_hdr;
1518bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct ieee80211_hdr *hdr;
1519bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1520bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	mpdu_hdr = (struct iwm_rx_mpdu_hdr *)packet->skb->data;
1521bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	payload_offset += sizeof(struct iwm_rx_mpdu_hdr);
1522bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	/* Padding is 0 or 2 bytes */
1523bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	payload_len = le16_to_cpu(mpdu_hdr->len) +
1524bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		(le16_to_cpu(ticket->flags) & IWM_RX_TICKET_PAD_SIZE_MSK);
1525bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	payload_len -= ticket->tail_len;
1526bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1527bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_RX(iwm, DBG, "Packet adjusted, len:%d, offset:%d, "
1528bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		   "ticket offset:%d ticket tail len:%d\n",
1529bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		   payload_len, payload_offset, ticket->payload_offset,
1530bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		   ticket->tail_len);
1531bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1532bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_HEXDUMP(iwm, DBG, RX, "RAW: ", packet->skb->data, packet->skb->len);
1533bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1534bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	skb_pull(packet->skb, payload_offset);
1535bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	skb_trim(packet->skb, payload_len);
1536bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1537bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	iwm_remove_iv(packet->skb, ticket->payload_offset);
1538bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1539bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	hdr = (struct ieee80211_hdr *) packet->skb->data;
1540bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (ieee80211_is_data_qos(hdr->frame_control)) {
1541bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		/* UMAC handed QOS_DATA frame with 2 padding bytes appended
1542bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		 * to the qos_ctl field in IEEE 802.11 headers. */
1543bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		memmove(packet->skb->data + IEEE80211_QOS_CTL_LEN + 2,
1544bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			packet->skb->data,
1545bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			ieee80211_hdrlen(hdr->frame_control) -
1546bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			IEEE80211_QOS_CTL_LEN);
1547bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		hdr = (struct ieee80211_hdr *) skb_pull(packet->skb,
1548bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				IEEE80211_QOS_CTL_LEN + 2);
1549bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
1550bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1551bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1552bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_HEXDUMP(iwm, DBG, RX, "ADJUSTED: ",
1553bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		    packet->skb->data, packet->skb->len);
1554bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1555bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1556bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic void classify8023(struct sk_buff *skb)
1557bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1558bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
1559bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1560bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	if (ieee80211_is_data_qos(hdr->frame_control)) {
1561bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		u8 *qc = ieee80211_get_qos_ctl(hdr);
1562bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		/* frame has qos control */
1563bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		skb->priority = *qc & IEEE80211_QOS_CTL_TID_MASK;
1564bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	} else {
1565bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		skb->priority = 0;
1566bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1567bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1568bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
156918974b5b0b5e758d416c550553b143e5c8038281Zhu Yistatic void iwm_rx_process_amsdu(struct iwm_priv *iwm, struct sk_buff *skb)
157018974b5b0b5e758d416c550553b143e5c8038281Zhu Yi{
157118974b5b0b5e758d416c550553b143e5c8038281Zhu Yi	struct wireless_dev *wdev = iwm_to_wdev(iwm);
157218974b5b0b5e758d416c550553b143e5c8038281Zhu Yi	struct net_device *ndev = iwm_to_ndev(iwm);
157318974b5b0b5e758d416c550553b143e5c8038281Zhu Yi	struct sk_buff_head list;
157418974b5b0b5e758d416c550553b143e5c8038281Zhu Yi	struct sk_buff *frame;
157518974b5b0b5e758d416c550553b143e5c8038281Zhu Yi
157618974b5b0b5e758d416c550553b143e5c8038281Zhu Yi	IWM_HEXDUMP(iwm, DBG, RX, "A-MSDU: ", skb->data, skb->len);
157718974b5b0b5e758d416c550553b143e5c8038281Zhu Yi
157818974b5b0b5e758d416c550553b143e5c8038281Zhu Yi	__skb_queue_head_init(&list);
15798b3becadc82de3b87a5c196239db3fef6caa9c82Yogesh Ashok Powar	ieee80211_amsdu_to_8023s(skb, &list, ndev->dev_addr, wdev->iftype, 0,
15808b3becadc82de3b87a5c196239db3fef6caa9c82Yogesh Ashok Powar									true);
158118974b5b0b5e758d416c550553b143e5c8038281Zhu Yi
158218974b5b0b5e758d416c550553b143e5c8038281Zhu Yi	while ((frame = __skb_dequeue(&list))) {
158318974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		ndev->stats.rx_packets++;
158418974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		ndev->stats.rx_bytes += frame->len;
158518974b5b0b5e758d416c550553b143e5c8038281Zhu Yi
158618974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		frame->protocol = eth_type_trans(frame, ndev);
158718974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		frame->ip_summed = CHECKSUM_NONE;
158818974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		memset(frame->cb, 0, sizeof(frame->cb));
158918974b5b0b5e758d416c550553b143e5c8038281Zhu Yi
159018974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		if (netif_rx_ni(frame) == NET_RX_DROP) {
159118974b5b0b5e758d416c550553b143e5c8038281Zhu Yi			IWM_ERR(iwm, "Packet dropped\n");
159218974b5b0b5e758d416c550553b143e5c8038281Zhu Yi			ndev->stats.rx_dropped++;
159318974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		}
159418974b5b0b5e758d416c550553b143e5c8038281Zhu Yi	}
159518974b5b0b5e758d416c550553b143e5c8038281Zhu Yi}
159618974b5b0b5e758d416c550553b143e5c8038281Zhu Yi
1597bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yistatic void iwm_rx_process_packet(struct iwm_priv *iwm,
1598bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				  struct iwm_rx_packet *packet,
1599bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				  struct iwm_rx_ticket_node *ticket_node)
1600bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1601bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	int ret;
1602bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct sk_buff *skb = packet->skb;
1603bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct wireless_dev *wdev = iwm_to_wdev(iwm);
1604bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct net_device *ndev = iwm_to_ndev(iwm);
1605bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1606bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	IWM_DBG_RX(iwm, DBG, "Processing packet ID %d\n", packet->id);
1607bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1608bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	switch (le16_to_cpu(ticket_node->ticket->action)) {
1609bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case IWM_RX_TICKET_RELEASE:
1610bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_DBG_RX(iwm, DBG, "RELEASE packet\n");
161118974b5b0b5e758d416c550553b143e5c8038281Zhu Yi
1612bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		iwm_rx_adjust_packet(iwm, packet, ticket_node);
161318974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		skb->dev = iwm_to_ndev(iwm);
161418974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		classify8023(skb);
161518974b5b0b5e758d416c550553b143e5c8038281Zhu Yi
161618974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		if (le16_to_cpu(ticket_node->ticket->flags) &
161718974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		    IWM_RX_TICKET_AMSDU_MSK) {
161818974b5b0b5e758d416c550553b143e5c8038281Zhu Yi			iwm_rx_process_amsdu(iwm, skb);
161918974b5b0b5e758d416c550553b143e5c8038281Zhu Yi			break;
162018974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		}
162118974b5b0b5e758d416c550553b143e5c8038281Zhu Yi
1622bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		ret = ieee80211_data_to_8023(skb, ndev->dev_addr, wdev->iftype);
1623bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (ret < 0) {
1624bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			IWM_DBG_RX(iwm, DBG, "Couldn't convert 802.11 header - "
1625bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				   "%d\n", ret);
162618974b5b0b5e758d416c550553b143e5c8038281Zhu Yi			kfree_skb(packet->skb);
1627bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			break;
1628bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		}
1629bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1630bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		IWM_HEXDUMP(iwm, DBG, RX, "802.3: ", skb->data, skb->len);
1631bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
163218974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		ndev->stats.rx_packets++;
163318974b5b0b5e758d416c550553b143e5c8038281Zhu Yi		ndev->stats.rx_bytes += skb->len;
163418974b5b0b5e758d416c550553b143e5c8038281Zhu Yi
1635bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		skb->protocol = eth_type_trans(skb, ndev);
16363a0e4851c97328ee455a57cb3e4097bb43934a87Zhu Yi		skb->ip_summed = CHECKSUM_NONE;
1637bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		memset(skb->cb, 0, sizeof(skb->cb));
1638bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1639dd13fd649879b6230be5d855e00c286c5e60f354Zhu Yi		if (netif_rx_ni(skb) == NET_RX_DROP) {
1640bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			IWM_ERR(iwm, "Packet dropped\n");
1641bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			ndev->stats.rx_dropped++;
1642bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		}
1643bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
1644bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	case IWM_RX_TICKET_DROP:
164569cf6e2d5bd90436a0fa6cef2f4d65583faef388Samuel Ortiz		IWM_DBG_RX(iwm, DBG, "DROP packet: 0x%x\n",
164669cf6e2d5bd90436a0fa6cef2f4d65583faef388Samuel Ortiz			   le16_to_cpu(ticket_node->ticket->flags));
1647bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		kfree_skb(packet->skb);
1648bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		break;
1649bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	default:
1650af901ca181d92aac3a7dc265144a9081a86d8f39André Goddard Rosa		IWM_ERR(iwm, "Unknown ticket action: %d\n",
1651bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			le16_to_cpu(ticket_node->ticket->action));
1652bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		kfree_skb(packet->skb);
1653bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1654bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1655bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	kfree(packet);
1656bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	iwm_rx_ticket_node_free(ticket_node);
1657bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1658bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1659bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi/*
1660bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * Rx data processing:
1661bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi *
1662bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * We're receiving Rx packet from the LMAC, and Rx ticket from
1663bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * the UMAC.
1664bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * To forward a target data packet upstream (i.e. to the
1665bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * kernel network stack), we must have received an Rx ticket
1666bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * that tells us we're allowed to release this packet (ticket
1667bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * action is IWM_RX_TICKET_RELEASE). The Rx ticket also indicates,
1668bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * among other things, where valid data actually starts in the Rx
1669bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi * packet.
1670bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi */
1671bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yivoid iwm_rx_worker(struct work_struct *work)
1672bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi{
1673bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_priv *iwm;
1674bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	struct iwm_rx_ticket_node *ticket, *next;
1675bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1676bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	iwm = container_of(work, struct iwm_priv, rx_worker);
1677bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1678bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	/*
1679bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * We go through the tickets list and if there is a pending
1680bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * packet for it, we push it upstream.
1681bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * We stop whenever a ticket is missing its packet, as we're
1682bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 * supposed to send the packets in order.
1683bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	 */
1684c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi	spin_lock(&iwm->ticket_lock);
1685bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) {
1686bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		struct iwm_rx_packet *packet =
1687bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			iwm_rx_packet_get(iwm, le16_to_cpu(ticket->ticket->id));
1688bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1689bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		if (!packet) {
1690bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi			IWM_DBG_RX(iwm, DBG, "Skip rx_work: Wait for ticket %d "
1691bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				   "to be handled first\n",
1692bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi				   le16_to_cpu(ticket->ticket->id));
1693c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi			break;
1694bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		}
1695bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1696bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		list_del(&ticket->node);
1697bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi		iwm_rx_process_packet(iwm, packet, ticket);
1698bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi	}
1699c03c6aefdc2c1f5785a5b0d1a3f7e48eeaae3505Zhu Yi	spin_unlock(&iwm->ticket_lock);
1700bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi}
1701bb9f8692f5043efef0dcef048cdd1db68299c2cbZhu Yi
1702