Merge tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.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  * ---------------------------------------------------------------------------
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
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
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