Commit | Line | Data |
---|---|---|
635d2b00 GKH |
1 | /* |
2 | * --------------------------------------------------------------------------- | |
3 | * FILE: mlme.c | |
4 | * | |
5 | * PURPOSE: | |
6 | * This file provides functions to send MLME requests to the UniFi. | |
7 | * | |
8 | * Copyright (C) 2007-2008 by Cambridge Silicon Radio Ltd. | |
9 | * | |
10 | * Refer to LICENSE.txt included with this source code for details on | |
11 | * the license terms. | |
12 | * | |
13 | * --------------------------------------------------------------------------- | |
14 | */ | |
15 | #include "csr_wifi_hip_unifi.h" | |
16 | #include "unifi_priv.h" | |
17 | ||
635d2b00 GKH |
18 | /* |
19 | * --------------------------------------------------------------------------- | |
20 | * unifi_mlme_wait_for_reply | |
21 | * | |
22 | * Wait for a reply after sending a signal. | |
23 | * | |
24 | * Arguments: | |
25 | * priv Pointer to device private context struct | |
26 | * ul_client Pointer to linux client | |
27 | * sig_reply_id ID of the expected reply (defined in sigs.h). | |
28 | * timeout timeout in ms | |
29 | * | |
30 | * Returns: | |
31 | * 0 on success, -ve POSIX code on error. | |
32 | * | |
33 | * Notes: | |
34 | * This function waits for a specific (sig_reply_id) signal from UniFi. | |
35 | * It also match the sequence number of the received (cfm) signal, with | |
36 | * the latest sequence number of the signal (req) we have sent. | |
37 | * These two number match be equal. | |
38 | * Should only be used for waiting xxx.cfm signals and only after | |
39 | * we have sent the matching xxx.req signal to UniFi. | |
40 | * If no response is received within the expected time (timeout), we assume | |
41 | * that the UniFi is busy and return an error. | |
42 | * If the wait is aborted by a kernel signal arriving, we stop waiting. | |
43 | * If a response from UniFi is not what we expected, we discard it and | |
44 | * wait again. This could be a response from an aborted request. If we | |
45 | * see several bad responses we assume we have lost synchronisation with | |
46 | * UniFi. | |
47 | * --------------------------------------------------------------------------- | |
48 | */ | |
49 | static int | |
50 | unifi_mlme_wait_for_reply(unifi_priv_t *priv, ul_client_t *pcli, int sig_reply_id, int timeout) | |
51 | { | |
52 | int retries = 0; | |
53 | long r; | |
54 | long t = timeout; | |
55 | unsigned int sent_seq_no; | |
56 | ||
57 | /* Convert t in ms to jiffies */ | |
58 | t = msecs_to_jiffies(t); | |
59 | ||
60 | do { | |
61 | /* Wait for the confirm or timeout. */ | |
62 | r = wait_event_interruptible_timeout(pcli->udi_wq, | |
63 | (pcli->wake_up_wq_id) || (priv->io_aborted == 1), | |
64 | t); | |
65 | /* Check for general i/o error */ | |
66 | if (priv->io_aborted) { | |
67 | unifi_error(priv, "MLME operation aborted\n"); | |
68 | return -EIO; | |
69 | } | |
70 | ||
71 | /* | |
72 | * If r=0 the request has timed-out. | |
73 | * If r>0 the request has completed successfully. | |
74 | * If r=-ERESTARTSYS an event (kill signal) has interrupted the wait_event. | |
75 | */ | |
76 | if ((r == 0) && (pcli->wake_up_wq_id == 0)) { | |
77 | unifi_error(priv, "mlme_wait: timed-out waiting for 0x%.4X, after %lu msec.\n", | |
78 | sig_reply_id, jiffies_to_msecs(t)); | |
79 | pcli->wake_up_wq_id = 0; | |
80 | return -ETIMEDOUT; | |
81 | } else if (r == -ERESTARTSYS) { | |
82 | unifi_error(priv, "mlme_wait: waiting for 0x%.4X was aborted.\n", sig_reply_id); | |
83 | pcli->wake_up_wq_id = 0; | |
84 | return -EINTR; | |
85 | } else { | |
86 | /* Get the sequence number of the signal that we previously set. */ | |
87 | if (pcli->seq_no != 0) { | |
88 | sent_seq_no = pcli->seq_no - 1; | |
89 | } else { | |
90 | sent_seq_no = 0x0F; | |
91 | } | |
92 | ||
93 | unifi_trace(priv, UDBG5, "Received 0x%.4X, seq: (r:%d, s:%d)\n", | |
94 | pcli->wake_up_wq_id, | |
95 | pcli->wake_seq_no, sent_seq_no); | |
96 | ||
97 | /* The two sequence ids must match. */ | |
98 | if (pcli->wake_seq_no == sent_seq_no) { | |
99 | /* and the signal ids must match. */ | |
100 | if (sig_reply_id == pcli->wake_up_wq_id) { | |
101 | /* Found the expected signal */ | |
102 | break; | |
103 | } else { | |
104 | /* This should never happen ... */ | |
105 | unifi_error(priv, "mlme_wait: mismatching signal id (0x%.4X - exp 0x%.4X) (seq %d)\n", | |
106 | pcli->wake_up_wq_id, | |
107 | sig_reply_id, | |
108 | pcli->wake_seq_no); | |
109 | pcli->wake_up_wq_id = 0; | |
110 | return -EIO; | |
111 | } | |
112 | } | |
113 | /* Wait for the next signal. */ | |
114 | pcli->wake_up_wq_id = 0; | |
115 | ||
116 | retries ++; | |
117 | if (retries >= 3) { | |
118 | unifi_error(priv, "mlme_wait: confirm wait retries exhausted (0x%.4X - exp 0x%.4X)\n", | |
119 | pcli->wake_up_wq_id, | |
120 | sig_reply_id); | |
121 | pcli->wake_up_wq_id = 0; | |
122 | return -EIO; | |
123 | } | |
124 | } | |
125 | } while (1); | |
126 | ||
127 | pcli->wake_up_wq_id = 0; | |
128 | ||
129 | return 0; | |
130 | } /* unifi_mlme_wait_for_reply() */ | |
131 | ||
132 | ||
133 | /* | |
134 | * --------------------------------------------------------------------------- | |
135 | * unifi_mlme_blocking_request | |
136 | * | |
137 | * Send a MLME request signal to UniFi. | |
138 | * | |
139 | * Arguments: | |
140 | * priv Pointer to device private context struct | |
141 | * pcli Pointer to context of calling process | |
142 | * sig Pointer to the signal to send | |
143 | * data_ptrs Pointer to the bulk data of the signal | |
144 | * timeout The request's timeout. | |
145 | * | |
146 | * Returns: | |
147 | * 0 on success, 802.11 result code on error. | |
148 | * --------------------------------------------------------------------------- | |
149 | */ | |
150 | int | |
151 | unifi_mlme_blocking_request(unifi_priv_t *priv, ul_client_t *pcli, | |
152 | CSR_SIGNAL *sig, bulk_data_param_t *data_ptrs, | |
153 | int timeout) | |
154 | { | |
155 | int r; | |
156 | ||
635d2b00 GKH |
157 | if (sig->SignalPrimitiveHeader.SignalId == 0) { |
158 | unifi_error(priv, "unifi_mlme_blocking_request: Invalid Signal Id (0x%x)\n", | |
159 | sig->SignalPrimitiveHeader.SignalId); | |
160 | return -EINVAL; | |
161 | } | |
162 | ||
163 | down(&priv->mlme_blocking_mutex); | |
164 | ||
165 | sig->SignalPrimitiveHeader.ReceiverProcessId = 0; | |
166 | sig->SignalPrimitiveHeader.SenderProcessId = pcli->sender_id | pcli->seq_no; | |
167 | ||
168 | unifi_trace(priv, UDBG2, "Send client=%d, S:0x%04X, sig 0x%.4X\n", | |
169 | pcli->client_id, | |
170 | sig->SignalPrimitiveHeader.SenderProcessId, | |
171 | sig->SignalPrimitiveHeader.SignalId); | |
172 | /* Send the signal to UniFi */ | |
173 | r = ul_send_signal_unpacked(priv, sig, data_ptrs); | |
174 | if (r) { | |
175 | up(&priv->mlme_blocking_mutex); | |
176 | unifi_error(priv, "Error queueing MLME REQUEST signal\n"); | |
177 | return r; | |
178 | } | |
179 | ||
180 | unifi_trace(priv, UDBG5, "Send 0x%.4X, seq = %d\n", | |
181 | sig->SignalPrimitiveHeader.SignalId, pcli->seq_no); | |
182 | ||
183 | /* | |
184 | * Advance the sequence number of the last sent signal, only | |
185 | * if the signal has been successfully set. | |
186 | */ | |
187 | pcli->seq_no++; | |
188 | if (pcli->seq_no > 0x0F) { | |
189 | pcli->seq_no = 0; | |
190 | } | |
191 | ||
192 | r = unifi_mlme_wait_for_reply(priv, pcli, (sig->SignalPrimitiveHeader.SignalId + 1), timeout); | |
193 | up(&priv->mlme_blocking_mutex); | |
194 | ||
195 | if (r) { | |
196 | unifi_error(priv, "Error waiting for MLME CONFIRM signal\n"); | |
197 | return r; | |
198 | } | |
199 | ||
635d2b00 GKH |
200 | return 0; |
201 | } /* unifi_mlme_blocking_request() */ | |
202 | ||
203 | ||
204 | /* | |
205 | * --------------------------------------------------------------------------- | |
206 | * unifi_mlme_copy_reply_and_wakeup_client | |
207 | * | |
208 | * Copy the reply signal from UniFi to the client's structure | |
209 | * and wake up the waiting client. | |
210 | * | |
211 | * Arguments: | |
212 | * None. | |
213 | * | |
214 | * Returns: | |
215 | * None. | |
216 | * --------------------------------------------------------------------------- | |
217 | */ | |
218 | void | |
219 | unifi_mlme_copy_reply_and_wakeup_client(ul_client_t *pcli, | |
220 | CSR_SIGNAL *signal, int signal_len, | |
221 | const bulk_data_param_t *bulkdata) | |
222 | { | |
223 | int i; | |
224 | ||
225 | /* Copy the signal to the reply */ | |
226 | memcpy(pcli->reply_signal, signal, signal_len); | |
227 | ||
228 | /* Get the sequence number of the signal that woke us up. */ | |
229 | pcli->wake_seq_no = pcli->reply_signal->SignalPrimitiveHeader.ReceiverProcessId & 0x0F; | |
230 | ||
231 | /* Append any bulk data */ | |
232 | for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++) { | |
233 | if (bulkdata->d[i].data_length > 0) { | |
234 | if (bulkdata->d[i].os_data_ptr) { | |
235 | memcpy(pcli->reply_bulkdata[i]->ptr, bulkdata->d[i].os_data_ptr, bulkdata->d[i].data_length); | |
236 | pcli->reply_bulkdata[i]->length = bulkdata->d[i].data_length; | |
237 | } else { | |
238 | pcli->reply_bulkdata[i]->length = 0; | |
239 | } | |
240 | } | |
241 | } | |
242 | ||
243 | /* Wake the requesting MLME function. */ | |
244 | pcli->wake_up_wq_id = pcli->reply_signal->SignalPrimitiveHeader.SignalId; | |
245 | wake_up_interruptible(&pcli->udi_wq); | |
246 | ||
247 | } /* unifi_mlme_copy_reply_and_wakeup_client() */ | |
248 | ||
249 | ||
250 | /* | |
251 | * --------------------------------------------------------------------------- | |
252 | * uf_abort_mlme | |
253 | * | |
254 | * Abort any MLME operation in progress. | |
255 | * This is used in the error recovery mechanism. | |
256 | * | |
257 | * Arguments: | |
258 | * priv Pointer to driver context. | |
259 | * | |
260 | * Returns: | |
261 | * 0 on success. | |
262 | * --------------------------------------------------------------------------- | |
263 | */ | |
264 | int | |
265 | uf_abort_mlme(unifi_priv_t *priv) | |
266 | { | |
267 | ul_client_t *ul_cli; | |
268 | ||
269 | /* Ensure no MLME functions are waiting on a the mlme_event semaphore. */ | |
270 | priv->io_aborted = 1; | |
271 | ||
272 | ul_cli = priv->netdev_client; | |
273 | if (ul_cli) { | |
274 | wake_up_interruptible(&ul_cli->udi_wq); | |
275 | } | |
276 | ||
277 | ul_cli = priv->wext_client; | |
278 | if (ul_cli) { | |
279 | wake_up_interruptible(&ul_cli->udi_wq); | |
280 | } | |
281 | ||
282 | return 0; | |
283 | } /* uf_abort_mlme() */ | |
284 | ||
285 | ||
286 | ||
287 | /* | |
288 | * --------------------------------------------------------------------------- | |
289 | * | |
290 | * Human-readable decoding of Reason and Result codes. | |
291 | * | |
292 | * --------------------------------------------------------------------------- | |
293 | */ | |
294 | ||
295 | struct mlme_code { | |
296 | const char *name; | |
297 | int id; | |
298 | }; | |
299 | ||
300 | static const struct mlme_code Result_codes[] = { | |
301 | { "Success", 0x0000 }, | |
302 | { "Unspecified Failure", 0x0001 }, | |
303 | /* (Reserved) 0x0002 - 0x0009 */ | |
304 | { "Refused Capabilities Mismatch", 0x000A }, | |
305 | /* (Reserved) 0x000B */ | |
306 | { "Refused External Reason", 0x000C }, | |
307 | /* (Reserved) 0x000D - 0x0010 */ | |
308 | { "Refused AP Out Of Memory", 0x0011 }, | |
309 | { "Refused Basic Rates Mismatch", 0x0012 }, | |
310 | /* (Reserved) 0x0013 - 0x001F */ | |
311 | { "Failure", 0x0020 }, | |
312 | /* (Reserved) 0x0021 - 0x0024 */ | |
313 | { "Refused Reason Unspecified", 0x0025 }, | |
314 | { "Invalid Parameters", 0x0026 }, | |
315 | { "Rejected With Suggested Changes", 0x0027 }, | |
316 | /* (Reserved) 0x0028 - 0x002E */ | |
317 | { "Rejected For Delay Period", 0x002F }, | |
318 | { "Not Allowed", 0x0030 }, | |
319 | { "Not Present", 0x0031 }, | |
320 | { "Not QSTA", 0x0032 }, | |
321 | /* (Reserved) 0x0033 - 0x7FFF */ | |
322 | { "Timeout", 0x8000 }, | |
323 | { "Too Many Simultaneous Requests", 0x8001 }, | |
324 | { "BSS Already Started Or Joined", 0x8002 }, | |
325 | { "Not Supported", 0x8003 }, | |
326 | { "Transmission Failure", 0x8004 }, | |
327 | { "Refused Not Authenticated", 0x8005 }, | |
328 | { "Reset Required Before Start", 0x8006 }, | |
329 | { "LM Info Unavailable", 0x8007 }, | |
330 | { NULL, -1 } | |
331 | }; | |
332 | ||
333 | static const struct mlme_code Reason_codes[] = { | |
334 | /* (Reserved) 0x0000 */ | |
335 | { "Unspecified Reason", 0x0001 }, | |
336 | { "Authentication Not Valid", 0x0002 }, | |
337 | { "Deauthenticated Leave BSS", 0x0003 }, | |
338 | { "Disassociated Inactivity", 0x0004 }, | |
339 | { "AP Overload", 0x0005 }, | |
340 | { "Class2 Frame Error", 0x0006 }, | |
341 | { "Class3 Frame Error", 0x0007 }, | |
342 | { "Disassociated Leave BSS", 0x0008 }, | |
343 | { "Association Not Authenticated", 0x0009 }, | |
344 | { "Disassociated Power Capability", 0x000A }, | |
345 | { "Disassociated Supported Channels", 0x000B }, | |
346 | /* (Reserved) 0x000C */ | |
347 | { "Invalid Information Element", 0x000D }, | |
348 | { "Michael MIC Failure", 0x000E }, | |
349 | { "Fourway Handshake Timeout", 0x000F }, | |
350 | { "Group Key Update Timeout", 0x0010 }, | |
351 | { "Handshake Element Different", 0x0011 }, | |
352 | { "Invalid Group Cipher", 0x0012 }, | |
353 | { "Invalid Pairwise Cipher", 0x0013 }, | |
354 | { "Invalid AKMP", 0x0014 }, | |
355 | { "Unsupported RSN IE Version", 0x0015 }, | |
356 | { "Invalid RSN IE Capabilities", 0x0016 }, | |
357 | { "Dot1X Auth Failed", 0x0017 }, | |
358 | { "Cipher Rejected By Policy", 0x0018 }, | |
359 | /* (Reserved) 0x0019 - 0x001F */ | |
360 | { "QoS Unspecified Reason", 0x0020 }, | |
361 | { "QoS Insufficient Bandwidth", 0x0021 }, | |
362 | { "QoS Excessive Not Ack", 0x0022 }, | |
363 | { "QoS TXOP Limit Exceeded", 0x0023 }, | |
364 | { "QSTA Leaving", 0x0024 }, | |
365 | { "End TS, End DLS, End BA", 0x0025 }, | |
366 | { "Unknown TS, Unknown DLS, Unknown BA", 0x0026 }, | |
367 | { "Timeout", 0x0027 }, | |
368 | /* (Reserved) 0x0028 - 0x002C */ | |
369 | { "STAKey Mismatch", 0x002D }, | |
370 | { NULL, -1 } | |
371 | }; | |
372 | ||
373 | ||
374 | static const char * | |
375 | lookup_something(const struct mlme_code *n, int id) | |
376 | { | |
377 | for (; n->name; n++) { | |
378 | if (n->id == id) { | |
379 | return n->name; | |
380 | } | |
381 | } | |
382 | ||
383 | /* not found */ | |
384 | return NULL; | |
385 | } /* lookup_something() */ | |
386 | ||
387 | ||
388 | const char * | |
389 | lookup_result_code(int result) | |
390 | { | |
391 | static char fallback[16]; | |
392 | const char *str; | |
393 | ||
394 | str = lookup_something(Result_codes, result); | |
395 | ||
396 | if (str == NULL) { | |
397 | snprintf(fallback, 16, "%d", result); | |
398 | str = fallback; | |
399 | } | |
400 | ||
401 | return str; | |
402 | } /* lookup_result_code() */ | |
403 | ||
404 | ||
405 | /* | |
406 | * --------------------------------------------------------------------------- | |
407 | * lookup_reason | |
408 | * | |
409 | * Return a description string for a WiFi MLME ReasonCode. | |
410 | * | |
411 | * Arguments: | |
412 | * reason The ReasonCode to interpret. | |
413 | * | |
414 | * Returns: | |
415 | * Pointer to description string. | |
416 | * --------------------------------------------------------------------------- | |
417 | */ | |
418 | const char * | |
419 | lookup_reason_code(int reason) | |
420 | { | |
421 | static char fallback[16]; | |
422 | const char *str; | |
423 | ||
424 | str = lookup_something(Reason_codes, reason); | |
425 | ||
426 | if (str == NULL) { | |
427 | snprintf(fallback, 16, "%d", reason); | |
428 | str = fallback; | |
429 | } | |
430 | ||
431 | return str; | |
432 | } /* lookup_reason_code() */ | |
433 |