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