]>
Commit | Line | Data |
---|---|---|
5e6e3a92 BZ |
1 | /* |
2 | * Marvell Wireless LAN device driver: utility functions | |
3 | * | |
65da33f5 | 4 | * Copyright (C) 2011-2014, Marvell International Ltd. |
5e6e3a92 BZ |
5 | * |
6 | * This software file (the "File") is distributed by Marvell International | |
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | |
8 | * (the "License"). You may use, redistribute and/or modify this File in | |
9 | * accordance with the terms and conditions of the License, a copy of which | |
10 | * is available by writing to the Free Software Foundation, Inc., | |
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | |
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | |
13 | * | |
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | |
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | |
17 | * this warranty disclaimer. | |
18 | */ | |
19 | ||
20 | #include "decl.h" | |
21 | #include "ioctl.h" | |
22 | #include "util.h" | |
23 | #include "fw.h" | |
24 | #include "main.h" | |
25 | #include "wmm.h" | |
26 | #include "11n.h" | |
27 | ||
28 | /* | |
29 | * Firmware initialization complete callback handler. | |
30 | * | |
31 | * This function wakes up the function waiting on the init | |
32 | * wait queue for the firmware initialization to complete. | |
33 | */ | |
34 | int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter) | |
35 | { | |
36 | ||
37 | adapter->init_wait_q_woken = true; | |
38 | wake_up_interruptible(&adapter->init_wait_q); | |
39 | return 0; | |
40 | } | |
41 | ||
42 | /* | |
43 | * Firmware shutdown complete callback handler. | |
44 | * | |
45 | * This function sets the hardware status to not ready and wakes up | |
46 | * the function waiting on the init wait queue for the firmware | |
47 | * shutdown to complete. | |
48 | */ | |
49 | int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter) | |
50 | { | |
51 | adapter->hw_status = MWIFIEX_HW_STATUS_NOT_READY; | |
52 | adapter->init_wait_q_woken = true; | |
53 | wake_up_interruptible(&adapter->init_wait_q); | |
54 | return 0; | |
55 | } | |
56 | ||
57 | /* | |
600f5d90 | 58 | * This function sends init/shutdown command |
5e6e3a92 | 59 | * to firmware. |
5e6e3a92 | 60 | */ |
600f5d90 AK |
61 | int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, |
62 | u32 func_init_shutdown) | |
5e6e3a92 | 63 | { |
5e6e3a92 BZ |
64 | u16 cmd; |
65 | ||
66 | if (func_init_shutdown == MWIFIEX_FUNC_INIT) { | |
67 | cmd = HostCmd_CMD_FUNC_INIT; | |
68 | } else if (func_init_shutdown == MWIFIEX_FUNC_SHUTDOWN) { | |
69 | cmd = HostCmd_CMD_FUNC_SHUTDOWN; | |
70 | } else { | |
600f5d90 | 71 | dev_err(priv->adapter->dev, "unsupported parameter\n"); |
5e6e3a92 BZ |
72 | return -1; |
73 | } | |
74 | ||
fa0ecbb9 | 75 | return mwifiex_send_cmd(priv, cmd, HostCmd_ACT_GEN_SET, 0, NULL, true); |
5e6e3a92 | 76 | } |
600f5d90 | 77 | EXPORT_SYMBOL_GPL(mwifiex_init_shutdown_fw); |
5e6e3a92 BZ |
78 | |
79 | /* | |
80 | * IOCTL request handler to set/get debug information. | |
81 | * | |
82 | * This function collates/sets the information from/to different driver | |
83 | * structures. | |
84 | */ | |
85 | int mwifiex_get_debug_info(struct mwifiex_private *priv, | |
86 | struct mwifiex_debug_info *info) | |
87 | { | |
88 | struct mwifiex_adapter *adapter = priv->adapter; | |
89 | ||
90 | if (info) { | |
91 | memcpy(info->packets_out, | |
92 | priv->wmm.packets_out, | |
93 | sizeof(priv->wmm.packets_out)); | |
5e3175fb | 94 | info->curr_tx_buf_size = (u32) adapter->curr_tx_buf_size; |
5e6e3a92 | 95 | info->tx_buf_size = (u32) adapter->tx_buf_size; |
c65a30f3 YAP |
96 | info->rx_tbl_num = mwifiex_get_rx_reorder_tbl(priv, |
97 | info->rx_tbl); | |
98 | info->tx_tbl_num = mwifiex_get_tx_ba_stream_tbl(priv, | |
99 | info->tx_tbl); | |
5e6e3a92 BZ |
100 | info->ps_mode = adapter->ps_mode; |
101 | info->ps_state = adapter->ps_state; | |
102 | info->is_deep_sleep = adapter->is_deep_sleep; | |
103 | info->pm_wakeup_card_req = adapter->pm_wakeup_card_req; | |
104 | info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try; | |
105 | info->is_hs_configured = adapter->is_hs_configured; | |
106 | info->hs_activated = adapter->hs_activated; | |
0c9c4a09 | 107 | info->is_cmd_timedout = adapter->is_cmd_timedout; |
5e6e3a92 | 108 | info->num_cmd_host_to_card_failure |
c65a30f3 | 109 | = adapter->dbg.num_cmd_host_to_card_failure; |
5e6e3a92 BZ |
110 | info->num_cmd_sleep_cfm_host_to_card_failure |
111 | = adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; | |
112 | info->num_tx_host_to_card_failure | |
c65a30f3 | 113 | = adapter->dbg.num_tx_host_to_card_failure; |
5e6e3a92 BZ |
114 | info->num_event_deauth = adapter->dbg.num_event_deauth; |
115 | info->num_event_disassoc = adapter->dbg.num_event_disassoc; | |
116 | info->num_event_link_lost = adapter->dbg.num_event_link_lost; | |
117 | info->num_cmd_deauth = adapter->dbg.num_cmd_deauth; | |
118 | info->num_cmd_assoc_success = | |
c65a30f3 | 119 | adapter->dbg.num_cmd_assoc_success; |
5e6e3a92 | 120 | info->num_cmd_assoc_failure = |
c65a30f3 | 121 | adapter->dbg.num_cmd_assoc_failure; |
5e6e3a92 | 122 | info->num_tx_timeout = adapter->dbg.num_tx_timeout; |
5e6e3a92 BZ |
123 | info->timeout_cmd_id = adapter->dbg.timeout_cmd_id; |
124 | info->timeout_cmd_act = adapter->dbg.timeout_cmd_act; | |
125 | memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id, | |
126 | sizeof(adapter->dbg.last_cmd_id)); | |
127 | memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act, | |
128 | sizeof(adapter->dbg.last_cmd_act)); | |
129 | info->last_cmd_index = adapter->dbg.last_cmd_index; | |
130 | memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id, | |
131 | sizeof(adapter->dbg.last_cmd_resp_id)); | |
132 | info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index; | |
133 | memcpy(info->last_event, adapter->dbg.last_event, | |
134 | sizeof(adapter->dbg.last_event)); | |
135 | info->last_event_index = adapter->dbg.last_event_index; | |
136 | info->data_sent = adapter->data_sent; | |
137 | info->cmd_sent = adapter->cmd_sent; | |
138 | info->cmd_resp_received = adapter->cmd_resp_received; | |
139 | } | |
140 | ||
141 | return 0; | |
142 | } | |
143 | ||
2dbaf751 SP |
144 | /* |
145 | * This function processes the received management packet and send it | |
146 | * to the kernel. | |
147 | */ | |
148 | int | |
f3b369e4 | 149 | mwifiex_process_mgmt_packet(struct mwifiex_private *priv, |
2dbaf751 SP |
150 | struct sk_buff *skb) |
151 | { | |
152 | struct rxpd *rx_pd; | |
2dbaf751 SP |
153 | u16 pkt_len; |
154 | ||
155 | if (!skb) | |
156 | return -1; | |
157 | ||
158 | rx_pd = (struct rxpd *)skb->data; | |
2dbaf751 SP |
159 | |
160 | skb_pull(skb, le16_to_cpu(rx_pd->rx_pkt_offset)); | |
161 | skb_pull(skb, sizeof(pkt_len)); | |
162 | ||
163 | pkt_len = le16_to_cpu(rx_pd->rx_pkt_length); | |
164 | ||
165 | /* Remove address4 */ | |
166 | memmove(skb->data + sizeof(struct ieee80211_hdr_3addr), | |
167 | skb->data + sizeof(struct ieee80211_hdr), | |
168 | pkt_len - sizeof(struct ieee80211_hdr)); | |
169 | ||
170 | pkt_len -= ETH_ALEN + sizeof(pkt_len); | |
171 | rx_pd->rx_pkt_length = cpu_to_le16(pkt_len); | |
172 | ||
7feb4c48 | 173 | cfg80211_rx_mgmt(priv->wdev, priv->roc_cfg.chan.center_freq, |
19504cf5 VK |
174 | CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len, |
175 | 0, GFP_ATOMIC); | |
2dbaf751 SP |
176 | |
177 | return 0; | |
178 | } | |
179 | ||
5e6e3a92 BZ |
180 | /* |
181 | * This function processes the received packet before sending it to the | |
182 | * kernel. | |
183 | * | |
184 | * It extracts the SKB from the received buffer and sends it to kernel. | |
185 | * In case the received buffer does not contain the data in SKB format, | |
186 | * the function creates a blank SKB, fills it with the data from the | |
187 | * received buffer and then sends this new SKB to the kernel. | |
188 | */ | |
f3b369e4 | 189 | int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb) |
5e6e3a92 | 190 | { |
5e6e3a92 BZ |
191 | if (!skb) |
192 | return -1; | |
193 | ||
f49c6e81 UR |
194 | priv->stats.rx_bytes += skb->len; |
195 | priv->stats.rx_packets++; | |
196 | ||
5e6e3a92 BZ |
197 | skb->dev = priv->netdev; |
198 | skb->protocol = eth_type_trans(skb, priv->netdev); | |
199 | skb->ip_summed = CHECKSUM_NONE; | |
4daffe35 | 200 | |
f553e1aa | 201 | /* This is required only in case of 11n and USB/PCIE as we alloc |
4daffe35 AK |
202 | * a buffer of 4K only if its 11N (to be able to receive 4K |
203 | * AMSDU packets). In case of SD we allocate buffers based | |
204 | * on the size of packet and hence this is not needed. | |
205 | * | |
206 | * Modifying the truesize here as our allocation for each | |
207 | * skb is 4K but we only receive 2K packets and this cause | |
208 | * the kernel to start dropping packets in case where | |
209 | * application has allocated buffer based on 2K size i.e. | |
210 | * if there a 64K packet received (in IP fragments and | |
211 | * application allocates 64K to receive this packet but | |
212 | * this packet would almost double up because we allocate | |
213 | * each 1.5K fragment in 4K and pass it up. As soon as the | |
214 | * 64K limit hits kernel will start to drop rest of the | |
215 | * fragments. Currently we fail the Filesndl-ht.scr script | |
216 | * for UDP, hence this fix | |
217 | */ | |
f553e1aa AP |
218 | if ((priv->adapter->iface_type == MWIFIEX_USB || |
219 | priv->adapter->iface_type == MWIFIEX_PCIE) && | |
4daffe35 AK |
220 | (skb->truesize > MWIFIEX_RX_DATA_BUF_SIZE)) |
221 | skb->truesize += (skb->len - MWIFIEX_RX_DATA_BUF_SIZE); | |
222 | ||
5e6e3a92 BZ |
223 | if (in_interrupt()) |
224 | netif_rx(skb); | |
225 | else | |
226 | netif_rx_ni(skb); | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
5e6e3a92 BZ |
231 | /* |
232 | * IOCTL completion callback handler. | |
233 | * | |
234 | * This function is called when a pending IOCTL is completed. | |
235 | * | |
236 | * If work queue support is enabled, the function wakes up the | |
237 | * corresponding waiting function. Otherwise, it processes the | |
238 | * IOCTL response and frees the response buffer. | |
239 | */ | |
efaaa8b8 AK |
240 | int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, |
241 | struct cmd_ctrl_node *cmd_node) | |
5e6e3a92 | 242 | { |
600f5d90 | 243 | dev_dbg(adapter->dev, "cmd completed: status=%d\n", |
c65a30f3 | 244 | adapter->cmd_wait_q.status); |
5e6e3a92 | 245 | |
efaaa8b8 | 246 | *(cmd_node->condition) = true; |
5e6e3a92 | 247 | |
600f5d90 AK |
248 | if (adapter->cmd_wait_q.status == -ETIMEDOUT) |
249 | dev_err(adapter->dev, "cmd timeout\n"); | |
250 | else | |
251 | wake_up_interruptible(&adapter->cmd_wait_q.wait); | |
5e6e3a92 BZ |
252 | |
253 | return 0; | |
254 | } | |
4bcf93d3 AP |
255 | |
256 | /* This function will return the pointer to station entry in station list | |
257 | * table which matches specified mac address. | |
258 | * This function should be called after acquiring RA list spinlock. | |
259 | * NULL is returned if station entry is not found in associated STA list. | |
260 | */ | |
261 | struct mwifiex_sta_node * | |
3b3a0162 | 262 | mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac) |
4bcf93d3 AP |
263 | { |
264 | struct mwifiex_sta_node *node; | |
265 | ||
266 | if (!mac) | |
267 | return NULL; | |
268 | ||
269 | list_for_each_entry(node, &priv->sta_list, list) { | |
270 | if (!memcmp(node->mac_addr, mac, ETH_ALEN)) | |
271 | return node; | |
272 | } | |
273 | ||
274 | return NULL; | |
275 | } | |
276 | ||
277 | /* This function will add a sta_node entry to associated station list | |
278 | * table with the given mac address. | |
279 | * If entry exist already, existing entry is returned. | |
280 | * If received mac address is NULL, NULL is returned. | |
281 | */ | |
282 | struct mwifiex_sta_node * | |
3b3a0162 | 283 | mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac) |
4bcf93d3 AP |
284 | { |
285 | struct mwifiex_sta_node *node; | |
286 | unsigned long flags; | |
287 | ||
288 | if (!mac) | |
289 | return NULL; | |
290 | ||
291 | spin_lock_irqsave(&priv->sta_list_spinlock, flags); | |
292 | node = mwifiex_get_sta_entry(priv, mac); | |
293 | if (node) | |
294 | goto done; | |
295 | ||
296 | node = kzalloc(sizeof(*node), GFP_ATOMIC); | |
297 | if (!node) | |
298 | goto done; | |
299 | ||
300 | memcpy(node->mac_addr, mac, ETH_ALEN); | |
301 | list_add_tail(&node->list, &priv->sta_list); | |
302 | ||
303 | done: | |
304 | spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); | |
305 | return node; | |
306 | } | |
307 | ||
308 | /* This function will search for HT IE in association request IEs | |
309 | * and set station HT parameters accordingly. | |
310 | */ | |
311 | void | |
312 | mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, | |
313 | int ies_len, struct mwifiex_sta_node *node) | |
314 | { | |
315 | const struct ieee80211_ht_cap *ht_cap; | |
316 | ||
317 | if (!ies) | |
318 | return; | |
319 | ||
320 | ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len); | |
321 | if (ht_cap) { | |
322 | node->is_11n_enabled = 1; | |
323 | node->max_amsdu = le16_to_cpu(ht_cap->cap_info) & | |
324 | IEEE80211_HT_CAP_MAX_AMSDU ? | |
325 | MWIFIEX_TX_DATA_BUF_SIZE_8K : | |
326 | MWIFIEX_TX_DATA_BUF_SIZE_4K; | |
327 | } else { | |
328 | node->is_11n_enabled = 0; | |
329 | } | |
330 | ||
331 | return; | |
332 | } | |
333 | ||
334 | /* This function will delete a station entry from station list */ | |
3b3a0162 | 335 | void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac) |
4bcf93d3 AP |
336 | { |
337 | struct mwifiex_sta_node *node; | |
338 | unsigned long flags; | |
339 | ||
340 | spin_lock_irqsave(&priv->sta_list_spinlock, flags); | |
341 | ||
342 | node = mwifiex_get_sta_entry(priv, mac); | |
343 | if (node) { | |
344 | list_del(&node->list); | |
345 | kfree(node); | |
346 | } | |
347 | ||
348 | spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); | |
349 | return; | |
350 | } | |
351 | ||
352 | /* This function will delete all stations from associated station list. */ | |
353 | void mwifiex_del_all_sta_list(struct mwifiex_private *priv) | |
354 | { | |
355 | struct mwifiex_sta_node *node, *tmp; | |
356 | unsigned long flags; | |
357 | ||
358 | spin_lock_irqsave(&priv->sta_list_spinlock, flags); | |
359 | ||
360 | list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { | |
361 | list_del(&node->list); | |
362 | kfree(node); | |
363 | } | |
364 | ||
365 | INIT_LIST_HEAD(&priv->sta_list); | |
366 | spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); | |
367 | return; | |
368 | } |