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