Root/drivers/staging/csr/ul_int.c

1/*
2 * ***************************************************************************
3 * FILE: ul_int.c
4 *
5 * PURPOSE:
6 * Manage list of client applications using UniFi.
7 *
8 * Copyright (C) 2006-2009 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 "csr_wifi_hip_conversions.h"
17#include "unifi_priv.h"
18#include "unifiio.h"
19#include "unifi_os.h"
20
21static void free_bulkdata_buffers(unifi_priv_t *priv, bulk_data_param_t *bulkdata);
22static void reset_driver_status(unifi_priv_t *priv);
23
24/*
25 * ---------------------------------------------------------------------------
26 * ul_init_clients
27 *
28 * Initialise the clients array to empty.
29 *
30 * Arguments:
31 * priv Pointer to device private context struct
32 *
33 * Returns:
34 * None.
35 *
36 * Notes:
37 * This function needs to be called before priv is stored in
38 * Unifi_instances[].
39 * ---------------------------------------------------------------------------
40 */
41void
42ul_init_clients(unifi_priv_t *priv)
43{
44    int id;
45    ul_client_t *ul_clients;
46
47#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
48    sema_init(&priv->udi_logging_mutex, 1);
49#else
50    init_MUTEX(&priv->udi_logging_mutex);
51#endif
52    priv->logging_client = NULL;
53
54    ul_clients = priv->ul_clients;
55
56    for (id = 0; id < MAX_UDI_CLIENTS; id++) {
57        memset(&ul_clients[id], 0, sizeof(ul_client_t));
58
59        ul_clients[id].client_id = id;
60        ul_clients[id].sender_id = UDI_SENDER_ID_BASE + (id << UDI_SENDER_ID_SHIFT);
61        ul_clients[id].instance = -1;
62        ul_clients[id].event_hook = NULL;
63
64        INIT_LIST_HEAD(&ul_clients[id].udi_log);
65        init_waitqueue_head(&ul_clients[id].udi_wq);
66        sema_init(&ul_clients[id].udi_sem, 1);
67
68        ul_clients[id].wake_up_wq_id = 0;
69        ul_clients[id].seq_no = 0;
70        ul_clients[id].wake_seq_no = 0;
71        ul_clients[id].snap_filter.count = 0;
72    }
73} /* ul_init_clients() */
74
75
76/*
77 * ---------------------------------------------------------------------------
78 * ul_register_client
79 *
80 * This function registers a new ul client.
81 *
82 * Arguments:
83 * priv Pointer to device private context struct
84 * configuration Special configuration for the client.
85 * udi_event_clbk Callback for receiving event from unifi.
86 *
87 * Returns:
88 * 0 if a new clients is registered, -1 otherwise.
89 * ---------------------------------------------------------------------------
90 */
91ul_client_t *
92ul_register_client(unifi_priv_t *priv, unsigned int configuration,
93                   udi_event_t udi_event_clbk)
94{
95    unsigned char id, ref;
96    ul_client_t *ul_clients;
97
98    ul_clients = priv->ul_clients;
99
100    /* check for an unused entry */
101    for (id = 0; id < MAX_UDI_CLIENTS; id++) {
102        if (ul_clients[id].udi_enabled == 0) {
103            ul_clients[id].instance = priv->instance;
104            ul_clients[id].udi_enabled = 1;
105            ul_clients[id].configuration = configuration;
106
107            /* Allocate memory for the reply signal.. */
108            ul_clients[id].reply_signal = kmalloc(sizeof(CSR_SIGNAL), GFP_KERNEL);
109            if (ul_clients[id].reply_signal == NULL) {
110                unifi_error(priv, "Failed to allocate reply signal for client.\n");
111                return NULL;
112            }
113            /* .. and the bulk data of the reply signal. */
114            for (ref = 0; ref < UNIFI_MAX_DATA_REFERENCES; ref ++) {
115                ul_clients[id].reply_bulkdata[ref] = kmalloc(sizeof(bulk_data_t), GFP_KERNEL);
116                /* If allocation fails, free allocated memory. */
117                if (ul_clients[id].reply_bulkdata[ref] == NULL) {
118                    for (; ref > 0; ref --) {
119                        kfree(ul_clients[id].reply_bulkdata[ref - 1]);
120                    }
121                    kfree(ul_clients[id].reply_signal);
122                    unifi_error(priv, "Failed to allocate bulk data buffers for client.\n");
123                    return NULL;
124                }
125            }
126
127            /* Set the event callback. */
128            ul_clients[id].event_hook = udi_event_clbk;
129
130            unifi_trace(priv, UDBG2, "UDI %d (0x%x) registered. configuration = 0x%x\n",
131                        id, &ul_clients[id], configuration);
132            return &ul_clients[id];
133        }
134    }
135    return NULL;
136} /* ul_register_client() */
137
138
139/*
140 * ---------------------------------------------------------------------------
141 * ul_deregister_client
142 *
143 * This function deregisters a blocking UDI client.
144 *
145 * Arguments:
146 * client Pointer to the client we deregister.
147 *
148 * Returns:
149 * 0 if a new clients is deregistered.
150 * ---------------------------------------------------------------------------
151 */
152int
153ul_deregister_client(ul_client_t *ul_client)
154{
155    struct list_head *pos, *n;
156    udi_log_t *logptr;
157    unifi_priv_t *priv = uf_find_instance(ul_client->instance);
158    int ref;
159
160    ul_client->instance = -1;
161    ul_client->event_hook = NULL;
162    ul_client->udi_enabled = 0;
163    unifi_trace(priv, UDBG5, "UDI (0x%x) deregistered.\n", ul_client);
164
165    /* Free memory allocated for the reply signal and its bulk data. */
166    kfree(ul_client->reply_signal);
167    for (ref = 0; ref < UNIFI_MAX_DATA_REFERENCES; ref ++) {
168        kfree(ul_client->reply_bulkdata[ref]);
169    }
170
171    if (ul_client->snap_filter.count) {
172        ul_client->snap_filter.count = 0;
173        kfree(ul_client->snap_filter.protocols);
174    }
175
176    /* Free anything pending on the udi_log list */
177    down(&ul_client->udi_sem);
178    list_for_each_safe(pos, n, &ul_client->udi_log)
179    {
180        logptr = list_entry(pos, udi_log_t, q);
181        list_del(pos);
182        kfree(logptr);
183    }
184    up(&ul_client->udi_sem);
185
186    return 0;
187} /* ul_deregister_client() */
188
189
190
191/*
192 * ---------------------------------------------------------------------------
193 * logging_handler
194 *
195 * This function is registered with the driver core.
196 * It is called every time a UniFi HIP Signal is sent. It iterates over
197 * the list of processes interested in receiving log events and
198 * delivers the events to them.
199 *
200 * Arguments:
201 * ospriv Pointer to driver's private data.
202 * sigdata Pointer to the packed signal buffer.
203 * signal_len Length of the packed signal.
204 * bulkdata Pointer to the signal's bulk data.
205 * dir Direction of the signal
206 * 0 = from-host
207 * 1 = to-host
208 *
209 * Returns:
210 * None.
211 * ---------------------------------------------------------------------------
212 */
213void
214logging_handler(void *ospriv,
215                u8 *sigdata, u32 signal_len,
216                const bulk_data_param_t *bulkdata,
217                enum udi_log_direction direction)
218{
219    unifi_priv_t *priv = (unifi_priv_t*)ospriv;
220    ul_client_t *client;
221    int dir;
222
223    dir = (direction == UDI_LOG_FROM_HOST) ? UDI_FROM_HOST : UDI_TO_HOST;
224
225    down(&priv->udi_logging_mutex);
226    client = priv->logging_client;
227    if (client != NULL) {
228        client->event_hook(client, sigdata, signal_len,
229                           bulkdata, dir);
230    }
231    up(&priv->udi_logging_mutex);
232
233} /* logging_handler() */
234
235
236
237/*
238 * ---------------------------------------------------------------------------
239 * ul_log_config_ind
240 *
241 * This function uses the client's register callback
242 * to indicate configuration information e.g core errors.
243 *
244 * Arguments:
245 * priv Pointer to driver's private data.
246 * conf_param Pointer to the configuration data.
247 * len Length of the configuration data.
248 *
249 * Returns:
250 * None.
251 * ---------------------------------------------------------------------------
252 */
253void
254ul_log_config_ind(unifi_priv_t *priv, u8 *conf_param, int len)
255{
256#ifdef CSR_SUPPORT_SME
257    if (priv->smepriv == NULL)
258    {
259        return;
260    }
261    if ((CONFIG_IND_ERROR == (*conf_param)) && (priv->wifi_on_state == wifi_on_in_progress)) {
262        unifi_notice(priv, "ul_log_config_ind: wifi on in progress, suppress error\n");
263    } else {
264        /* wifi_off_ind (error or exit) */
265        CsrWifiRouterCtrlWifiOffIndSend(priv->CSR_WIFI_SME_IFACEQUEUE,0, (CsrWifiRouterCtrlControlIndication)(*conf_param));
266    }
267#ifdef CSR_WIFI_HIP_DEBUG_OFFLINE
268    unifi_debug_buf_dump();
269#endif
270#else
271    bulk_data_param_t bulkdata;
272
273    /*
274     * If someone killed unifi_managed before the driver was unloaded
275     * the g_drvpriv pointer is going to be NULL. In this case it is
276     * safe to assume that there is no client to get the indication.
277     */
278    if (!priv) {
279        unifi_notice(NULL, "uf_sme_event_ind: NULL priv\n");
280        return;
281    }
282
283    /* Create a null bulkdata structure. */
284    bulkdata.d[0].data_length = 0;
285    bulkdata.d[1].data_length = 0;
286
287    sme_native_log_event(priv->sme_cli, conf_param, sizeof(u8),
288                         &bulkdata, UDI_CONFIG_IND);
289
290#endif /* CSR_SUPPORT_SME */
291
292} /* ul_log_config_ind */
293
294
295/*
296 * ---------------------------------------------------------------------------
297 * free_bulkdata_buffers
298 *
299 * Free the bulkdata buffers e.g. after a failed unifi_send_signal().
300 *
301 * Arguments:
302 * priv Pointer to device private struct
303 * bulkdata Pointer to bulkdata parameter table
304 *
305 * Returns:
306 * None.
307 * ---------------------------------------------------------------------------
308 */
309static void
310free_bulkdata_buffers(unifi_priv_t *priv, bulk_data_param_t *bulkdata)
311{
312    int i;
313
314    if (bulkdata) {
315        for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; ++i) {
316            if (bulkdata->d[i].data_length != 0) {
317                unifi_net_data_free(priv, (bulk_data_desc_t *)(&bulkdata->d[i]));
318                /* data_length is now 0 */
319            }
320        }
321    }
322
323} /* free_bulkdata_buffers */
324
325static int
326_align_bulk_data_buffers(unifi_priv_t *priv, u8 *signal,
327                         bulk_data_param_t *bulkdata)
328{
329    unsigned int i;
330
331    if ((bulkdata == NULL) || (CSR_WIFI_ALIGN_BYTES == 0)) {
332        return 0;
333    }
334
335    for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++)
336    {
337        struct sk_buff *skb;
338        /*
339        * The following complex casting is in place in order to eliminate 64-bit compilation warning
340        * "cast to/from pointer from/to integer of different size"
341        */
342        u32 align_offset = (u32)(long)(bulkdata->d[i].os_data_ptr) & (CSR_WIFI_ALIGN_BYTES-1);
343        if (align_offset)
344        {
345            skb = (struct sk_buff*)bulkdata->d[i].os_net_buf_ptr;
346            if (skb == NULL) {
347                unifi_warning(priv,
348                              "_align_bulk_data_buffers: Align offset found (%d) but skb is NULL!\n",
349                              align_offset);
350                return -EINVAL;
351            }
352            if (bulkdata->d[i].data_length == 0) {
353                unifi_warning(priv,
354                              "_align_bulk_data_buffers: Align offset found (%d) but length is zero\n",
355                              align_offset);
356                return CSR_RESULT_SUCCESS;
357            }
358            unifi_trace(priv, UDBG5,
359                        "Align f-h buffer (0x%p) by %d bytes (skb->data: 0x%p)\n",
360                        bulkdata->d[i].os_data_ptr, align_offset, skb->data);
361
362
363            /* Check if there is enough headroom... */
364            if (unlikely(skb_headroom(skb) < align_offset))
365            {
366                struct sk_buff *tmp = skb;
367
368                unifi_trace(priv, UDBG5, "Headroom not enough - realloc it\n");
369                skb = skb_realloc_headroom(skb, align_offset);
370                if (skb == NULL) {
371                    unifi_error(priv,
372                                "_align_bulk_data_buffers: skb_realloc_headroom failed - signal is dropped\n");
373                    return -EFAULT;
374                }
375                /* Free the old bulk data only if allocation succeeds */
376                kfree_skb(tmp);
377                /* Bulkdata needs to point to the new skb */
378                bulkdata->d[i].os_net_buf_ptr = (const unsigned char*)skb;
379                bulkdata->d[i].os_data_ptr = (const void*)skb->data;
380            }
381            /* ... before pushing the data to the right alignment offset */
382            skb_push(skb, align_offset);
383
384        }
385        /* The direction bit is zero for the from-host */
386        signal[SIZEOF_SIGNAL_HEADER + (i * SIZEOF_DATAREF) + 1] = align_offset;
387
388    }
389    return 0;
390} /* _align_bulk_data_buffers() */
391
392
393/*
394 * ---------------------------------------------------------------------------
395 * ul_send_signal_unpacked
396 *
397 * This function sends a host formatted signal to unifi.
398 *
399 * Arguments:
400 * priv Pointer to driver's private data.
401 * sigptr Pointer to the signal.
402 * bulkdata Pointer to the signal's bulk data.
403 *
404 * Returns:
405 * O on success, error code otherwise.
406 *
407 * Notes:
408 * The signals have to be sent in the format described in the host interface
409 * specification, i.e wire formatted. Certain clients use the host formatted
410 * structures. The write_pack() transforms the host formatted signal
411 * into the wired formatted signal. The code is in the core, since the signals
412 * are defined therefore binded to the host interface specification.
413 * ---------------------------------------------------------------------------
414 */
415int
416ul_send_signal_unpacked(unifi_priv_t *priv, CSR_SIGNAL *sigptr,
417                        bulk_data_param_t *bulkdata)
418{
419    u8 sigbuf[UNIFI_PACKED_SIGBUF_SIZE];
420    u16 packed_siglen;
421    CsrResult csrResult;
422    unsigned long lock_flags;
423    int r;
424
425
426    csrResult = write_pack(sigptr, sigbuf, &packed_siglen);
427    if (csrResult != CSR_RESULT_SUCCESS) {
428        unifi_error(priv, "Malformed HIP signal in ul_send_signal_unpacked()\n");
429        return CsrHipResultToStatus(csrResult);
430    }
431    r = _align_bulk_data_buffers(priv, sigbuf, (bulk_data_param_t*)bulkdata);
432    if (r) {
433        return r;
434    }
435
436    spin_lock_irqsave(&priv->send_signal_lock, lock_flags);
437    csrResult = unifi_send_signal(priv->card, sigbuf, packed_siglen, bulkdata);
438    if (csrResult != CSR_RESULT_SUCCESS) {
439  /* free_bulkdata_buffers(priv, (bulk_data_param_t *)bulkdata); */
440        spin_unlock_irqrestore(&priv->send_signal_lock, lock_flags);
441        return CsrHipResultToStatus(csrResult);
442    }
443    spin_unlock_irqrestore(&priv->send_signal_lock, lock_flags);
444
445    return 0;
446} /* ul_send_signal_unpacked() */
447
448
449/*
450 * ---------------------------------------------------------------------------
451 * reset_driver_status
452 *
453 * This function is called from ul_send_signal_raw() when it detects
454 * that the SME has sent a MLME-RESET request.
455 *
456 * Arguments:
457 * priv Pointer to device private struct
458 *
459 * Returns:
460 * None.
461 * ---------------------------------------------------------------------------
462 */
463static void
464reset_driver_status(unifi_priv_t *priv)
465{
466    priv->sta_wmm_capabilities = 0;
467#ifdef CSR_NATIVE_LINUX
468#ifdef CSR_SUPPORT_WEXT
469    priv->wext_conf.flag_associated = 0;
470    priv->wext_conf.block_controlled_port = CSR_WIFI_ROUTER_PORT_ACTION_8021X_PORT_OPEN;
471    priv->wext_conf.bss_wmm_capabilities = 0;
472    priv->wext_conf.disable_join_on_ssid_set = 0;
473#endif
474#endif
475} /* reset_driver_status() */
476
477
478/*
479 * ---------------------------------------------------------------------------
480 * ul_send_signal_raw
481 *
482 * This function sends a wire formatted data signal to unifi.
483 *
484 * Arguments:
485 * priv Pointer to driver's private data.
486 * sigptr Pointer to the signal.
487 * siglen Length of the signal.
488 * bulkdata Pointer to the signal's bulk data.
489 *
490 * Returns:
491 * O on success, error code otherwise.
492 * ---------------------------------------------------------------------------
493 */
494int
495ul_send_signal_raw(unifi_priv_t *priv, unsigned char *sigptr, int siglen,
496                   bulk_data_param_t *bulkdata)
497{
498    CsrResult csrResult;
499    unsigned long lock_flags;
500    int r;
501
502    /*
503     * Make sure that the signal is updated with the bulk data
504     * alignment for DMA.
505     */
506    r = _align_bulk_data_buffers(priv, (u8*)sigptr, bulkdata);
507    if (r) {
508        return r;
509    }
510
511    spin_lock_irqsave(&priv->send_signal_lock, lock_flags);
512    csrResult = unifi_send_signal(priv->card, sigptr, siglen, bulkdata);
513    if (csrResult != CSR_RESULT_SUCCESS) {
514        free_bulkdata_buffers(priv, bulkdata);
515        spin_unlock_irqrestore(&priv->send_signal_lock, lock_flags);
516        return CsrHipResultToStatus(csrResult);
517    }
518    spin_unlock_irqrestore(&priv->send_signal_lock, lock_flags);
519
520    /*
521     * Since this is use by unicli, if we get an MLME reset request
522     * we need to initialize a few status parameters
523     * that the driver uses to make decisions.
524     */
525    if (GET_SIGNAL_ID(sigptr) == CSR_MLME_RESET_REQUEST_ID) {
526        reset_driver_status(priv);
527    }
528
529    return 0;
530} /* ul_send_signal_raw() */
531
532
533

Archive Download this file



interactive