Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ul_int.c
Go to the documentation of this file.
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"
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
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  */
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
151 {
152  struct list_head *pos, *n;
153  udi_log_t *logptr;
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,
215 {
216  unifi_priv_t *priv = (unifi_priv_t*)ospriv;
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
414  bulk_data_param_t *bulkdata)
415 {
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