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