Commit | Line | Data |
---|---|---|
635d2b00 GKH |
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 | |
42 | ul_init_clients(unifi_priv_t *priv) | |
43 | { | |
44 | int id; | |
45 | ul_client_t *ul_clients; | |
46 | ||
635d2b00 | 47 | sema_init(&priv->udi_logging_mutex, 1); |
635d2b00 GKH |
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 * | |
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.. */ | |
786eeeb3 | 104 | ul_clients[id].reply_signal = kmalloc(sizeof(CSR_SIGNAL), GFP_KERNEL); |
635d2b00 GKH |
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 ++) { | |
786eeeb3 | 111 | ul_clients[id].reply_bulkdata[ref] = kmalloc(sizeof(bulk_data_t), GFP_KERNEL); |
635d2b00 GKH |
112 | /* If allocation fails, free allocated memory. */ |
113 | if (ul_clients[id].reply_bulkdata[ref] == NULL) { | |
114 | for (; ref > 0; ref --) { | |
55a27055 | 115 | kfree(ul_clients[id].reply_bulkdata[ref - 1]); |
635d2b00 | 116 | } |
55a27055 | 117 | kfree(ul_clients[id].reply_signal); |
635d2b00 GKH |
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 | |
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. */ | |
55a27055 | 162 | kfree(ul_client->reply_signal); |
635d2b00 | 163 | for (ref = 0; ref < UNIFI_MAX_DATA_REFERENCES; ref ++) { |
55a27055 | 164 | kfree(ul_client->reply_bulkdata[ref]); |
635d2b00 GKH |
165 | } |
166 | ||
167 | if (ul_client->snap_filter.count) { | |
168 | ul_client->snap_filter.count = 0; | |
55a27055 | 169 | kfree(ul_client->snap_filter.protocols); |
635d2b00 GKH |
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 | |
210 | logging_handler(void *ospriv, | |
26a6b2e1 | 211 | u8 *sigdata, u32 signal_len, |
635d2b00 GKH |
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 | |
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) */ | |
02c37a6b | 261 | CsrWifiRouterCtrlWifiOffIndSend(priv->CSR_WIFI_SME_IFACEQUEUE, 0, (CsrWifiRouterCtrlControlIndication)(*conf_param)); |
635d2b00 | 262 | } |
95edd09e GKH |
263 | #ifdef CSR_WIFI_HIP_DEBUG_OFFLINE |
264 | unifi_debug_buf_dump(); | |
265 | #endif | |
635d2b00 GKH |
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 | ||
7e6f5794 | 283 | sme_native_log_event(priv->sme_cli, conf_param, sizeof(u8), |
635d2b00 GKH |
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 | |
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 | |
7e6f5794 | 322 | _align_bulk_data_buffers(unifi_priv_t *priv, u8 *signal, |
635d2b00 GKH |
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 | */ | |
26a6b2e1 | 338 | u32 align_offset = (u32)(long)(bulkdata->d[i].os_data_ptr) & (CSR_WIFI_ALIGN_BYTES-1); |
635d2b00 GKH |
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 | |
412 | ul_send_signal_unpacked(unifi_priv_t *priv, CSR_SIGNAL *sigptr, | |
413 | bulk_data_param_t *bulkdata) | |
414 | { | |
7e6f5794 | 415 | u8 sigbuf[UNIFI_PACKED_SIGBUF_SIZE]; |
8c87f69a | 416 | u16 packed_siglen; |
635d2b00 GKH |
417 | CsrResult csrResult; |
418 | unsigned long lock_flags; | |
419 | int r; | |
635d2b00 GKH |
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 | } | |
635d2b00 GKH |
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 | } | |
635d2b00 GKH |
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 | |
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 | |
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 | */ | |
7e6f5794 | 502 | r = _align_bulk_data_buffers(priv, (u8*)sigptr, bulkdata); |
635d2b00 GKH |
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 |