aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/csr/mlme.c
blob: 861d6b7687c769dab064455f2be8ddbf02f07fa1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
/*
 * ---------------------------------------------------------------------------
 * FILE:     mlme.c
 *
 * PURPOSE:
 *      This file provides functions to send MLME requests to the UniFi.
 *
 * Copyright (C) 2007-2008 by Cambridge Silicon Radio Ltd.
 *
 * Refer to LICENSE.txt included with this source code for details on
 * the license terms.
 *
 * ---------------------------------------------------------------------------
 */
#include "csr_wifi_hip_unifi.h"
#include "unifi_priv.h"

/*
 * ---------------------------------------------------------------------------
 * unifi_mlme_wait_for_reply
 *
 *      Wait for a reply after sending a signal.
 *
 * Arguments:
 *      priv            Pointer to device private context struct
 *      ul_client       Pointer to linux client
 *      sig_reply_id    ID of the expected reply (defined in sigs.h).
 *      timeout         timeout in ms
 *
 * Returns:
 *      0 on success, -ve POSIX code on error.
 *
 * Notes:
 *      This function waits for a specific (sig_reply_id) signal from UniFi.
 *      It also match the sequence number of the received (cfm) signal, with
 *      the latest sequence number of the signal (req) we have sent.
 *      These two number match be equal.
 *      Should only be used for waiting xxx.cfm signals and only after
 *      we have sent the matching xxx.req signal to UniFi.
 *      If no response is received within the expected time (timeout), we assume
 *      that the UniFi is busy and return an error.
 *      If the wait is aborted by a kernel signal arriving, we stop waiting.
 *      If a response from UniFi is not what we expected, we discard it and
 *      wait again. This could be a response from an aborted request. If we
 *      see several bad responses we assume we have lost synchronisation with
 *      UniFi.
 * ---------------------------------------------------------------------------
 */
static int
unifi_mlme_wait_for_reply(unifi_priv_t *priv, ul_client_t *pcli, int sig_reply_id, int timeout)
{
    int retries = 0;
    long r;
    long t = timeout;
    unsigned int sent_seq_no;

    /* Convert t in ms to jiffies */
    t = msecs_to_jiffies(t);

    do {
        /* Wait for the confirm or timeout. */
        r = wait_event_interruptible_timeout(pcli->udi_wq,
                                             (pcli->wake_up_wq_id) || (priv->io_aborted == 1),
                                             t);
        /* Check for general i/o error */
        if (priv->io_aborted) {
            unifi_error(priv, "MLME operation aborted\n");
            return -EIO;
        }

        /*
         * If r=0 the request has timed-out.
         * If r>0 the request has completed successfully.
         * If r=-ERESTARTSYS an event (kill signal) has interrupted the wait_event.
         */
        if ((r == 0) && (pcli->wake_up_wq_id == 0)) {
            unifi_error(priv, "mlme_wait: timed-out waiting for 0x%.4X, after %lu msec.\n",
                        sig_reply_id,  jiffies_to_msecs(t));
            pcli->wake_up_wq_id = 0;
            return -ETIMEDOUT;
        } else if (r == -ERESTARTSYS) {
            unifi_error(priv, "mlme_wait: waiting for 0x%.4X was aborted.\n", sig_reply_id);
            pcli->wake_up_wq_id = 0;
            return -EINTR;
        } else {
            /* Get the sequence number of the signal that we previously set. */
            if (pcli->seq_no != 0) {
                sent_seq_no = pcli->seq_no - 1;
            } else {
                sent_seq_no = 0x0F;
            }

            unifi_trace(priv, UDBG5, "Received 0x%.4X, seq: (r:%d, s:%d)\n",
                        pcli->wake_up_wq_id,
                        pcli->wake_seq_no, sent_seq_no);

            /* The two sequence ids must match. */
            if (pcli->wake_seq_no == sent_seq_no) {
                /* and the signal ids must match. */
                if (sig_reply_id == pcli->wake_up_wq_id) {
                    /* Found the expected signal */
                    break;
                } else {
                    /* This should never happen ... */
                    unifi_error(priv, "mlme_wait: mismatching signal id (0x%.4X - exp 0x%.4X) (seq %d)\n",
                                pcli->wake_up_wq_id,
                                sig_reply_id,
                                pcli->wake_seq_no);
                    pcli->wake_up_wq_id = 0;
                    return -EIO;
                }
            }
            /* Wait for the next signal. */
            pcli->wake_up_wq_id = 0;

            retries ++;
            if (retries >= 3) {
                unifi_error(priv, "mlme_wait: confirm wait retries exhausted (0x%.4X - exp 0x%.4X)\n",
                            pcli->wake_up_wq_id,
                            sig_reply_id);
                pcli->wake_up_wq_id = 0;
                return -EIO;
            }
        }
    } while (1);

    pcli->wake_up_wq_id = 0;

    return 0;
} /* unifi_mlme_wait_for_reply() */


/*
 * ---------------------------------------------------------------------------
 * unifi_mlme_blocking_request
 *
 *      Send a MLME request signal to UniFi.
 *
 * Arguments:
 *      priv            Pointer to device private context struct
 *      pcli            Pointer to context of calling process
 *      sig             Pointer to the signal to send
 *      data_ptrs       Pointer to the bulk data of the signal
 *      timeout         The request's timeout.
 *
 * Returns:
 *      0 on success, 802.11 result code on error.
 * ---------------------------------------------------------------------------
 */
int
unifi_mlme_blocking_request(unifi_priv_t *priv, ul_client_t *pcli,
                            CSR_SIGNAL *sig, bulk_data_param_t *data_ptrs,
                            int timeout)
{
    int r;

    if (sig->SignalPrimitiveHeader.SignalId == 0) {
        unifi_error(priv, "unifi_mlme_blocking_request: Invalid Signal Id (0x%x)\n",
                    sig->SignalPrimitiveHeader.SignalId);
        return -EINVAL;
    }

    down(&priv->mlme_blocking_mutex);

    sig->SignalPrimitiveHeader.ReceiverProcessId = 0;
    sig->SignalPrimitiveHeader.SenderProcessId = pcli->sender_id | pcli->seq_no;

    unifi_trace(priv, UDBG2, "Send client=%d, S:0x%04X, sig 0x%.4X\n",
                pcli->client_id,
                sig->SignalPrimitiveHeader.SenderProcessId,
                sig->SignalPrimitiveHeader.SignalId);
    /* Send the signal to UniFi */
    r = ul_send_signal_unpacked(priv, sig, data_ptrs);
    if (r) {
        up(&priv->mlme_blocking_mutex);
        unifi_error(priv, "Error queueing MLME REQUEST signal\n");
        return r;
    }

    unifi_trace(priv, UDBG5, "Send 0x%.4X, seq = %d\n",
                sig->SignalPrimitiveHeader.SignalId, pcli->seq_no);

    /*
     * Advance the sequence number of the last sent signal, only
     * if the signal has been successfully set.
     */
    pcli->seq_no++;
    if (pcli->seq_no > 0x0F) {
        pcli->seq_no = 0;
    }

    r = unifi_mlme_wait_for_reply(priv, pcli, (sig->SignalPrimitiveHeader.SignalId + 1), timeout);
    up(&priv->mlme_blocking_mutex);

    if (r) {
        unifi_error(priv, "Error waiting for MLME CONFIRM signal\n");
        return r;
    }

    return 0;
} /* unifi_mlme_blocking_request() */


/*
 * ---------------------------------------------------------------------------
 *  unifi_mlme_copy_reply_and_wakeup_client
 *
 *      Copy the reply signal from UniFi to the client's structure
 *      and wake up the waiting client.
 *
 *  Arguments:
 *      None.
 *
 *  Returns:
 *      None.
 * ---------------------------------------------------------------------------
 */
void
unifi_mlme_copy_reply_and_wakeup_client(ul_client_t *pcli,
                                        CSR_SIGNAL *signal, int signal_len,
                                        const bulk_data_param_t *bulkdata)
{
    int i;

    /* Copy the signal to the reply */
    memcpy(pcli->reply_signal, signal, signal_len);

    /* Get the sequence number of the signal that woke us up. */
    pcli->wake_seq_no = pcli->reply_signal->SignalPrimitiveHeader.ReceiverProcessId & 0x0F;

    /* Append any bulk data */
    for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++) {
        if (bulkdata->d[i].data_length > 0) {
            if (bulkdata->d[i].os_data_ptr) {
                memcpy(pcli->reply_bulkdata[i]->ptr, bulkdata->d[i].os_data_ptr, bulkdata->d[i].data_length);
                pcli->reply_bulkdata[i]->length = bulkdata->d[i].data_length;
            } else {
                pcli->reply_bulkdata[i]->length = 0;
            }
        }
    }

    /* Wake the requesting MLME function. */
    pcli->wake_up_wq_id = pcli->reply_signal->SignalPrimitiveHeader.SignalId;
    wake_up_interruptible(&pcli->udi_wq);

} /* unifi_mlme_copy_reply_and_wakeup_client() */


/*
 * ---------------------------------------------------------------------------
 *  uf_abort_mlme
 *
 *      Abort any MLME operation in progress.
 *      This is used in the error recovery mechanism.
 *
 *  Arguments:
 *      priv          Pointer to driver context.
 *
 *  Returns:
 *      0 on success.
 * ---------------------------------------------------------------------------
 */
int
uf_abort_mlme(unifi_priv_t *priv)
{
    ul_client_t *ul_cli;

    /* Ensure no MLME functions are waiting on a the mlme_event semaphore. */
    priv->io_aborted = 1;

    ul_cli = priv->netdev_client;
    if (ul_cli) {
        wake_up_interruptible(&ul_cli->udi_wq);
    }

    ul_cli = priv->wext_client;
    if (ul_cli) {
        wake_up_interruptible(&ul_cli->udi_wq);
    }

    return 0;
} /* uf_abort_mlme() */



/*
 * ---------------------------------------------------------------------------
 *
 *      Human-readable decoding of Reason and Result codes.
 *
 * ---------------------------------------------------------------------------
 */

struct mlme_code {
    const char *name;
    int id;
};

static const struct mlme_code Result_codes[] = {
    { "Success",                             0x0000 },
    { "Unspecified Failure",                 0x0001 },
    /* (Reserved)                      0x0002 - 0x0009 */
    { "Refused Capabilities Mismatch",       0x000A },
    /* (Reserved)                          0x000B */
    { "Refused External Reason",             0x000C },
    /* (Reserved)                      0x000D - 0x0010 */
    { "Refused AP Out Of Memory",            0x0011 },
    { "Refused Basic Rates Mismatch",        0x0012 },
    /* (Reserved)                      0x0013 - 0x001F */
    { "Failure",                             0x0020 },
    /* (Reserved)                      0x0021 - 0x0024 */
    { "Refused Reason Unspecified",          0x0025 },
    { "Invalid Parameters",                  0x0026 },
    { "Rejected With Suggested Changes",     0x0027 },
    /* (Reserved)                      0x0028 - 0x002E */
    { "Rejected For Delay Period",           0x002F },
    { "Not Allowed",                         0x0030 },
    { "Not Present",                         0x0031 },
    { "Not QSTA",                            0x0032 },
    /* (Reserved)                      0x0033 - 0x7FFF */
    { "Timeout",                             0x8000 },
    { "Too Many Simultaneous Requests",      0x8001 },
    { "BSS Already Started Or Joined",       0x8002 },
    { "Not Supported",                       0x8003 },
    { "Transmission Failure",                0x8004 },
    { "Refused Not Authenticated",           0x8005 },
    { "Reset Required Before Start",         0x8006 },
    { "LM Info Unavailable",                 0x8007 },
    { NULL, -1 }
};

static const struct mlme_code Reason_codes[] = {
    /* (Reserved)                      0x0000 */
    { "Unspecified Reason",              0x0001 },
    { "Authentication Not Valid",        0x0002 },
    { "Deauthenticated Leave BSS",       0x0003 },
    { "Disassociated Inactivity",        0x0004 },
    { "AP Overload",                     0x0005 },
    { "Class2 Frame Error",              0x0006 },
    { "Class3 Frame Error",              0x0007 },
    { "Disassociated Leave BSS",         0x0008 },
    { "Association Not Authenticated",   0x0009 },
    { "Disassociated Power Capability",  0x000A },
    { "Disassociated Supported Channels", 0x000B },
    /* (Reserved)                      0x000C */
    { "Invalid Information Element",     0x000D },
    { "Michael MIC Failure",             0x000E },
    { "Fourway Handshake Timeout",       0x000F },
    { "Group Key Update Timeout",        0x0010 },
    { "Handshake Element Different",     0x0011 },
    { "Invalid Group Cipher",            0x0012 },
    { "Invalid Pairwise Cipher",         0x0013 },
    { "Invalid AKMP",                    0x0014 },
    { "Unsupported RSN IE Version",      0x0015 },
    { "Invalid RSN IE Capabilities",     0x0016 },
    { "Dot1X Auth Failed",               0x0017 },
    { "Cipher Rejected By Policy",       0x0018 },
    /* (Reserved)                  0x0019 - 0x001F */
    { "QoS Unspecified Reason",          0x0020 },
    { "QoS Insufficient Bandwidth",      0x0021 },
    { "QoS Excessive Not Ack",           0x0022 },
    { "QoS TXOP Limit Exceeded",         0x0023 },
    { "QSTA Leaving",                    0x0024 },
    { "End TS, End DLS, End BA",         0x0025 },
    { "Unknown TS, Unknown DLS, Unknown BA", 0x0026 },
    { "Timeout",                         0x0027 },
    /* (Reserved)                  0x0028 - 0x002C */
    { "STAKey Mismatch",                 0x002D },
    { NULL, -1 }
};


static const char *
lookup_something(const struct mlme_code *n, int id)
{
    for (; n->name; n++) {
        if (n->id == id) {
            return n->name;
        }
    }

    /* not found */
    return NULL;
} /* lookup_something() */


const char *
lookup_result_code(int result)
{
    static char fallback[16];
    const char *str;

    str = lookup_something(Result_codes, result);

    if (str == NULL) {
        snprintf(fallback, 16, "%d", result);
        str = fallback;
    }

    return str;
} /* lookup_result_code() */


/*
 * ---------------------------------------------------------------------------
 *  lookup_reason
 *
 *      Return a description string for a WiFi MLME ReasonCode.
 *
 *  Arguments:
 *      reason          The ReasonCode to interpret.
 *
 *  Returns:
 *      Pointer to description string.
 * ---------------------------------------------------------------------------
 */
const char *
lookup_reason_code(int reason)
{
    static char fallback[16];
    const char *str;

    str = lookup_something(Reason_codes, reason);

    if (str == NULL) {
        snprintf(fallback, 16, "%d", reason);
        str = fallback;
    }

    return str;
} /* lookup_reason_code() */