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