1 /*
2 * Copyright 2023, Cypress Semiconductor Corporation (an Infineon company)
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include <limits.h>
19 #include "whd_int.h"
20 #include "whd_cdc_bdc.h"
21 #include "whd_events_int.h"
22 #include "cyabs_rtos.h"
23 #include "whd_network_types.h"
24 #include "whd_types_int.h"
25 #include "whd_wlioctl.h"
26 #include "whd_thread_internal.h"
27 #include "whd_buffer_api.h"
28 #include "whd_network_if.h"
29
30 /******************************************************
31 * Constants
32 ******************************************************/
33
34 #define BDC_PROTO_VER (2) /** Version number of BDC header */
35 #define BDC_FLAG_VER_SHIFT (4) /** Number of bits to shift BDC version number in the flags field */
36 #define BDC_FLAG2_IF_MASK (0x0f)
37
38 #define ETHER_TYPE_BRCM (0x886C) /** Broadcom Ethertype for identifying event packets - Copied from DHD include/proto/ethernet.h */
39 #define BRCM_OUI "\x00\x10\x18" /** Broadcom OUI (Organizationally Unique Identifier): Used in the proprietary(221) IE (Information Element) in all Broadcom devices */
40
41 /* QoS related definitions (type of service) */
42 #define IPV4_DSCP_OFFSET (15) /** Offset for finding the DSCP field in an IPv4 header */
43
44 #define IOCTL_OFFSET (sizeof(whd_buffer_header_t) + 12 + 16)
45 #define WHD_IOCTL_PACKET_TIMEOUT (0xFFFFFFFF)
46 #define WHD_IOCTL_TIMEOUT_MS (5000) /** Need to give enough time for coming out of Deep sleep (was 400) */
47 #define WHD_IOCTL_MAX_TX_PKT_LEN (1500)
48 #define ALIGNED_ADDRESS ( (uint32_t)0x3 )
49
50 /******************************************************
51 * Macros
52 ******************************************************/
53
54 /******************************************************
55 * Local Structures
56 ******************************************************/
57
58 /******************************************************
59 * Static Variables
60 ******************************************************/
61
62 static const uint8_t dscp_to_wmm_qos[] =
63 { 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */
64 1, 1, 1, 1, 1, 1, 1, /* 8 - 14 */
65 1, 1, 1, 1, 1, 1, 1, /* 15 - 21 */
66 1, 1, 0, 0, 0, 0, 0, /* 22 - 28 */
67 0, 0, 0, 5, 5, 5, 5, /* 29 - 35 */
68 5, 5, 5, 5, 5, 5, 5, /* 36 - 42 */
69 5, 5, 5, 5, 5, 7, 7, /* 43 - 49 */
70 7, 7, 7, 7, 7, 7, 7, /* 50 - 56 */
71 7, 7, 7, 7, 7, 7, 7, /* 57 - 63 */
72 };
73
74 /******************************************************
75 * Static Function Prototypes
76 ******************************************************/
77
78 static uint8_t whd_map_dscp_to_priority(whd_driver_t whd_driver, uint8_t dscp_val);
79
80 /******************************************************
81 * Static Functions
82 ******************************************************/
83
84 /** Map a DSCP value from an IP header to a WMM QoS priority
85 *
86 * @param dscp_val : DSCP value from IP header
87 *
88 * @return wmm_qos : WMM priority
89 *
90 */
whd_map_dscp_to_priority(whd_driver_t whd_driver,uint8_t val)91 static uint8_t whd_map_dscp_to_priority(whd_driver_t whd_driver, uint8_t val)
92 {
93 uint8_t dscp_val = (uint8_t)(val >> 2); /* DSCP field is the high 6 bits of the second byte of an IPv4 header */
94
95 return dscp_to_wmm_qos[dscp_val];
96 }
97
whd_cdc_bdc_info_deinit(whd_driver_t whd_driver)98 void whd_cdc_bdc_info_deinit(whd_driver_t whd_driver)
99 {
100 whd_cdc_bdc_info_t *cdc_bdc_info = &whd_driver->cdc_bdc_info;
101 whd_error_info_t *error_info = &whd_driver->error_info;
102
103 /* Delete the sleep mutex */
104 (void)cy_rtos_deinit_semaphore(&cdc_bdc_info->ioctl_sleep);
105
106 /* Delete the queue mutex. */
107 (void)cy_rtos_deinit_semaphore(&cdc_bdc_info->ioctl_mutex);
108
109 /* Delete the event list management mutex */
110 (void)cy_rtos_deinit_semaphore(&cdc_bdc_info->event_list_mutex);
111
112 /* Delete the error list management mutex */
113 (void)cy_rtos_deinit_semaphore(&error_info->event_list_mutex);
114 }
115
whd_cdc_bdc_info_init(whd_driver_t whd_driver)116 whd_result_t whd_cdc_bdc_info_init(whd_driver_t whd_driver)
117 {
118 whd_cdc_bdc_info_t *cdc_bdc_info = &whd_driver->cdc_bdc_info;
119 whd_error_info_t *error_info = &whd_driver->error_info;
120
121 /* Create the mutex protecting the packet send queue */
122 if (cy_rtos_init_semaphore(&cdc_bdc_info->ioctl_mutex, 1, 0) != WHD_SUCCESS)
123 {
124 return WHD_SEMAPHORE_ERROR;
125 }
126 if (cy_rtos_set_semaphore(&cdc_bdc_info->ioctl_mutex, WHD_FALSE) != WHD_SUCCESS)
127 {
128 WPRINT_WHD_ERROR( ("Error setting semaphore in %s at %d \n", __func__, __LINE__) );
129 return WHD_SEMAPHORE_ERROR;
130 }
131
132 /* Create the event flag which signals the whd thread needs to wake up */
133 if (cy_rtos_init_semaphore(&cdc_bdc_info->ioctl_sleep, 1, 0) != WHD_SUCCESS)
134 {
135 cy_rtos_deinit_semaphore(&cdc_bdc_info->ioctl_mutex);
136 return WHD_SEMAPHORE_ERROR;
137 }
138
139 /* Create semaphore to protect event list management */
140 if (cy_rtos_init_semaphore(&cdc_bdc_info->event_list_mutex, 1, 0) != WHD_SUCCESS)
141 {
142 cy_rtos_deinit_semaphore(&cdc_bdc_info->ioctl_sleep);
143 cy_rtos_deinit_semaphore(&cdc_bdc_info->ioctl_mutex);
144 return WHD_SEMAPHORE_ERROR;
145 }
146 if (cy_rtos_set_semaphore(&cdc_bdc_info->event_list_mutex, WHD_FALSE) != WHD_SUCCESS)
147 {
148 WPRINT_WHD_ERROR( ("Error setting semaphore in %s at %d \n", __func__, __LINE__) );
149 return WHD_SEMAPHORE_ERROR;
150 }
151
152 /* Initialise the list of event handler functions */
153 memset(cdc_bdc_info->whd_event_list, 0, sizeof(cdc_bdc_info->whd_event_list) );
154
155 /* Create semaphore to protect event list management */
156 if (cy_rtos_init_semaphore(&error_info->event_list_mutex, 1, 0) != WHD_SUCCESS)
157 {
158 return WHD_SEMAPHORE_ERROR;
159 }
160
161 if (cy_rtos_set_semaphore(&error_info->event_list_mutex, WHD_FALSE) != WHD_SUCCESS)
162 {
163 WPRINT_WHD_ERROR( ("Error setting semaphore in %s at %d \n", __func__, __LINE__) );
164 return WHD_SEMAPHORE_ERROR;
165 }
166
167 /* Initialise the list of error handler functions */
168 memset(error_info->whd_event_list, 0, sizeof(error_info->whd_event_list) );
169
170 return WHD_SUCCESS;
171 }
172
173 /** Sends an IOCTL command
174 *
175 * Sends a I/O Control command to the Broadcom 802.11 device.
176 * The data which is set or retrieved must be in a format structure which is appropriate for the particular
177 * I/O control being sent. These structures can only be found in the DHD source code such as wl/exe/wlu.c.
178 * The I/O control will always respond with a packet buffer which may contain data in a format specific to
179 * the I/O control being used.
180 *
181 * @Note: The caller is responsible for releasing the response buffer.
182 * @Note: The function blocks until the IOCTL has completed
183 * @Note: Only one IOCTL may happen simultaneously.
184 *
185 * @param type : CDC_SET or CDC_GET - indicating whether to set or get the I/O control
186 * @param send_buffer_hnd : A handle for a packet buffer containing the data value to be sent.
187 * @param response_buffer_hnd : A pointer which will receive the handle for the packet buffer
188 * containing the response data value received.
189 * @param interface : Which interface to send the iovar to (WHD_STA_INTERFACE or WHD_AP_INTERFACE)
190 *
191 * @return WHD result code
192 */
whd_cdc_send_ioctl(whd_interface_t ifp,cdc_command_type_t type,uint32_t command,whd_buffer_t send_buffer_hnd,whd_buffer_t * response_buffer_hnd)193 whd_result_t whd_cdc_send_ioctl(whd_interface_t ifp, cdc_command_type_t type, uint32_t command,
194 whd_buffer_t send_buffer_hnd,
195 whd_buffer_t *response_buffer_hnd)
196 {
197
198 uint32_t data_length;
199 uint32_t flags;
200 uint32_t requested_ioctl_id;
201 uint32_t status;
202 whd_result_t retval;
203 control_header_t *send_packet;
204 cdc_header_t *cdc_header;
205 uint32_t bss_index = ifp->bsscfgidx;
206 whd_driver_t whd_driver = ifp->whd_driver;
207 whd_cdc_bdc_info_t *cdc_bdc_info = &whd_driver->cdc_bdc_info;
208
209 /* Validate the command value */
210 if (command > INT_MAX)
211 {
212 WPRINT_WHD_ERROR( ("The ioctl command value is invalid\n") );
213 return WHD_BADARG;
214 }
215
216 /* Acquire mutex which prevents multiple simultaneous IOCTLs */
217 retval = cy_rtos_get_semaphore(&cdc_bdc_info->ioctl_mutex, CY_RTOS_NEVER_TIMEOUT, WHD_FALSE);
218 if (retval != WHD_SUCCESS)
219 {
220 CHECK_RETURN(whd_buffer_release(whd_driver, send_buffer_hnd, WHD_NETWORK_TX) );
221 return retval;
222 }
223
224 /* Count request ioctl ID after acquiring ioctl mutex */
225 requested_ioctl_id = (uint32_t)(++cdc_bdc_info->requested_ioctl_id);
226
227 /* Get the data length and cast packet to a CDC BUS header */
228 data_length =
229 (uint32_t)(whd_buffer_get_current_piece_size(whd_driver,
230 send_buffer_hnd) - sizeof(bus_common_header_t) -
231 sizeof(cdc_header_t) );
232
233 send_packet = (control_header_t *)whd_buffer_get_current_piece_data_pointer(whd_driver, send_buffer_hnd);
234 CHECK_PACKET_NULL(send_packet, WHD_NO_REGISTER_FUNCTION_POINTER);
235 WHD_IOCTL_LOG_ADD(ifp->whd_driver, command, send_buffer_hnd);
236
237 /* Check if IOCTL is actually IOVAR */
238 if ( (command == WLC_SET_VAR) || (command == WLC_GET_VAR) )
239 {
240 uint8_t *data = (uint8_t *)DATA_AFTER_HEADER(send_packet);
241 uint8_t *ptr = data;
242
243 /* Calculate the offset added to compensate for IOVAR string creating unaligned data section */
244 while (*ptr == 0)
245 {
246 ptr++;
247 }
248 if (data != ptr)
249 {
250 data_length -= (uint32_t)(ptr - data);
251 memmove(data, ptr, data_length);
252 CHECK_RETURN(whd_buffer_set_size(whd_driver, send_buffer_hnd,
253 (uint16_t)(data_length + sizeof(bus_common_header_t) +
254 sizeof(cdc_header_t) ) ) );
255 }
256 }
257
258 /* Prepare the CDC header */
259 send_packet->cdc_header.cmd = htod32(command);
260 send_packet->cdc_header.len = htod32(data_length);
261
262 send_packet->cdc_header.flags = ( (requested_ioctl_id << CDCF_IOC_ID_SHIFT)
263 & CDCF_IOC_ID_MASK ) | type | bss_index << CDCF_IOC_IF_SHIFT;
264 send_packet->cdc_header.flags = htod32(send_packet->cdc_header.flags);
265
266 send_packet->cdc_header.status = 0;
267
268 /* Manufacturing test can receive big buffers, but sending big buffers causes a wlan firmware error */
269 /* Even though data portion needs to be truncated, cdc_header should have the actual length of the ioctl packet */
270 if (whd_buffer_get_current_piece_size(whd_driver, send_buffer_hnd) > WHD_IOCTL_MAX_TX_PKT_LEN)
271 {
272 CHECK_RETURN(whd_buffer_set_size(whd_driver, send_buffer_hnd, WHD_IOCTL_MAX_TX_PKT_LEN) );
273 }
274
275 /* Store the length of the data and the IO control header and pass "down" */
276 CHECK_RETURN(whd_send_to_bus(whd_driver, send_buffer_hnd, CONTROL_HEADER, 8) );
277
278
279 /* Wait till response has been received */
280 retval = cy_rtos_get_semaphore(&cdc_bdc_info->ioctl_sleep, (uint32_t)WHD_IOCTL_TIMEOUT_MS, WHD_FALSE);
281 if (retval != WHD_SUCCESS)
282 {
283 /* Release the mutex since ioctl response will no longer be referenced. */
284 CHECK_RETURN(cy_rtos_set_semaphore(&cdc_bdc_info->ioctl_mutex, WHD_FALSE) );
285 return retval;
286 }
287
288 cdc_header = (cdc_header_t *)whd_buffer_get_current_piece_data_pointer(whd_driver, cdc_bdc_info->ioctl_response);
289 CHECK_PACKET_NULL(cdc_header, WHD_NO_REGISTER_FUNCTION_POINTER);
290 flags = dtoh32(cdc_header->flags);
291 status = dtoh32(cdc_header->status);
292 /* Check if the caller wants the response */
293 if (response_buffer_hnd != NULL)
294 {
295 *response_buffer_hnd = cdc_bdc_info->ioctl_response;
296 CHECK_RETURN(whd_buffer_add_remove_at_front(whd_driver, response_buffer_hnd, sizeof(cdc_header_t) ) );
297 }
298 else
299 {
300 CHECK_RETURN(whd_buffer_release(whd_driver, cdc_bdc_info->ioctl_response, WHD_NETWORK_RX) );
301 }
302
303 cdc_bdc_info->ioctl_response = NULL;
304
305 /* Release the mutex since ioctl response will no longer be referenced. */
306 CHECK_RETURN(cy_rtos_set_semaphore(&cdc_bdc_info->ioctl_mutex, WHD_FALSE) );
307
308 /* Check whether the IOCTL response indicates it failed. */
309 if ( (flags & CDCF_IOC_ERROR) != 0 )
310 {
311 if (response_buffer_hnd != NULL)
312 {
313 CHECK_RETURN(whd_buffer_release(whd_driver, *response_buffer_hnd, WHD_NETWORK_RX) );
314 *response_buffer_hnd = NULL;
315 }
316 if (status)
317 return WHD_RESULT_CREATE( (WLAN_ENUM_OFFSET - status) );
318 else
319 return WHD_IOCTL_FAIL;
320 }
321
322 return WHD_SUCCESS;
323 }
324
325 /** Sets/Gets an I/O Variable (IOVar)
326 *
327 * This function either sets or retrieves the value of an I/O variable from the Broadcom 802.11 device.
328 * The data which is set or retrieved must be in a format structure which is appropriate for the particular
329 * I/O variable being accessed. These structures can only be found in the DHD source code such as wl/exe/wlu.c.
330 *
331 * @Note: The function blocks until the I/O variable read/write has completed
332 *
333 * @param type : CDC_SET or CDC_GET - indicating whether to set or get the I/O variable value
334 * @param send_buffer_hnd : A handle for a packet buffer containing the data value to be sent.
335 * @param response_buffer_hnd : A pointer which will receive the handle for the packet buffer
336 * containing the response data value received.
337 * @param interface : Which interface to send the iovar to (AP or STA)
338 *
339 * @return WHD result code
340 */
whd_cdc_send_iovar(whd_interface_t ifp,cdc_command_type_t type,whd_buffer_t send_buffer_hnd,whd_buffer_t * response_buffer_hnd)341 whd_result_t whd_cdc_send_iovar(whd_interface_t ifp, cdc_command_type_t type,
342 whd_buffer_t send_buffer_hnd,
343 whd_buffer_t *response_buffer_hnd)
344 {
345 if (type == CDC_SET)
346 {
347 return whd_cdc_send_ioctl(ifp, CDC_SET, (uint32_t)WLC_SET_VAR, send_buffer_hnd, response_buffer_hnd);
348 }
349 else
350 {
351 return whd_cdc_send_ioctl(ifp, CDC_GET, (uint32_t)WLC_GET_VAR, send_buffer_hnd, response_buffer_hnd);
352 }
353 }
354
355 /** A helper function to easily acquire and initialise a buffer destined for use as an iovar
356 *
357 * @param buffer : A pointer to a whd_buffer_t object where the created buffer will be stored
358 * @param data_length : The length of space reserved for user data
359 * @param name : The name of the iovar
360 *
361 * @return A pointer to the start of user data with data_length space available
362 */
whd_cdc_get_iovar_buffer(whd_driver_t whd_driver,whd_buffer_t * buffer,uint16_t data_length,const char * name)363 void *whd_cdc_get_iovar_buffer(whd_driver_t whd_driver,
364 whd_buffer_t *buffer,
365 uint16_t data_length,
366 const char *name)
367 {
368 uint32_t name_length = (uint32_t)strlen(name) + 1; /* + 1 for terminating null */
369 uint32_t name_length_alignment_offset = (64 - name_length) % sizeof(uint32_t);
370
371 if (whd_host_buffer_get(whd_driver, buffer, WHD_NETWORK_TX,
372 (uint16_t)(IOCTL_OFFSET + data_length + name_length + name_length_alignment_offset),
373 (uint32_t)WHD_IOCTL_PACKET_TIMEOUT) == WHD_SUCCESS)
374 {
375 uint8_t *data = whd_buffer_get_current_piece_data_pointer(whd_driver, *buffer);
376 CHECK_PACKET_NULL(data, NULL);
377 data = data + IOCTL_OFFSET;
378 memset(data, 0, name_length_alignment_offset);
379 memcpy(data + name_length_alignment_offset, name, name_length);
380 return (data + name_length + name_length_alignment_offset);
381 }
382 else
383 {
384 WPRINT_WHD_ERROR( ("Error - failed to allocate a packet buffer for IOVAR\n") );
385 return NULL;
386 }
387 }
388
389 /** Sends a data packet.
390 *
391 * This function should be called by the bottom of the network stack in order for it
392 * to send an ethernet frame.
393 * The function prepends a BDC header, before sending to @ref whd_send_to_bus where
394 * the BUS header will be added
395 *
396 * @param buffer : The ethernet packet buffer to be sent
397 * @param interface : the interface over which to send the packet (AP or STA)
398 *
399 * @return WHD result code
400 */
whd_network_send_ethernet_data(whd_interface_t ifp,whd_buffer_t buffer)401 whd_result_t whd_network_send_ethernet_data(whd_interface_t ifp, whd_buffer_t buffer)
402 {
403 data_header_t *packet;
404 whd_result_t result;
405 uint8_t *dscp = NULL;
406 uint8_t priority = 0;
407 uint8_t whd_tos_map[8] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
408 whd_driver_t whd_driver = ifp->whd_driver;
409 ethernet_header_t *ethernet_header = (ethernet_header_t *)whd_buffer_get_current_piece_data_pointer(
410 whd_driver, buffer);
411 uint16_t ether_type;
412 CHECK_PACKET_NULL(ethernet_header, WHD_NO_REGISTER_FUNCTION_POINTER);
413 ether_type = ntoh16(ethernet_header->ethertype);
414 if ( (ether_type == WHD_ETHERTYPE_IPv4) || (ether_type == WHD_ETHERTYPE_DOT1AS) )
415 {
416 dscp = (uint8_t *)whd_buffer_get_current_piece_data_pointer(whd_driver, buffer) + IPV4_DSCP_OFFSET;
417 }
418
419 WPRINT_WHD_DATA_LOG( ("Wcd:> DATA pkt 0x%08lX len %d\n", (unsigned long)buffer,
420 (int)whd_buffer_get_current_piece_size(whd_driver, buffer) ) );
421
422
423 /* Add link space at front of packet */
424 result = whd_buffer_add_remove_at_front(whd_driver, &buffer, -(int)(sizeof(data_header_t) ) );
425 if (result != WHD_SUCCESS)
426 {
427 WPRINT_WHD_DEBUG( ("Unable to adjust header space\n") );
428 result = whd_buffer_release(ifp->whd_driver, buffer, WHD_NETWORK_TX);
429 if (result != WHD_SUCCESS)
430 WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
431 return WHD_BUFFER_ALLOC_FAIL;
432 }
433
434 packet = (data_header_t *)whd_buffer_get_current_piece_data_pointer(whd_driver, buffer);
435 CHECK_PACKET_NULL(packet, WHD_NO_REGISTER_FUNCTION_POINTER);
436 if (ifp->bsscfgidx > WHD_INTERFACE_MAX)
437 {
438 WPRINT_WHD_DEBUG( ("No interface for packet send\n") );
439 result = whd_buffer_release(ifp->whd_driver, buffer, WHD_NETWORK_TX);
440 if (result != WHD_SUCCESS)
441 WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
442 return WHD_UNKNOWN_INTERFACE;
443 }
444
445 /* Prepare the BDC header */
446 packet->bdc_header.flags = 0;
447 packet->bdc_header.flags = (uint8_t)(BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
448 /* If it's an IPv4 packet set the BDC header priority based on the DSCP field */
449 if ( ( (ether_type == WHD_ETHERTYPE_IPv4) || (ether_type == WHD_ETHERTYPE_DOT1AS) ) && (dscp != NULL) )
450 {
451 if (*dscp != 0) /* If it's equal 0 then it's best effort traffic and nothing needs to be done */
452 {
453 priority = whd_map_dscp_to_priority(whd_driver, *dscp);
454 }
455 }
456
457 /* If STA interface, re-map prio to the prio allowed by the AP, regardless of whether it's an IPv4 packet */
458 if (ifp->role == WHD_STA_ROLE)
459 {
460 packet->bdc_header.priority = whd_tos_map[priority];
461 }
462 else
463 {
464 packet->bdc_header.priority = priority;
465 }
466
467 packet->bdc_header.flags2 = ifp->bsscfgidx;
468 packet->bdc_header.data_offset = 0;
469
470 /* Add the length of the BDC header and pass "down" */
471 return whd_send_to_bus(whd_driver, buffer, DATA_HEADER, packet->bdc_header.priority);
472
473 }
474
475 /** A helper function to easily acquire and initialise a buffer destined for use as an ioctl
476 *
477 * @param buffer : A pointer to a whd_buffer_t object where the created buffer will be stored
478 * @param data_length : The length of space reserved for user data
479 *
480 * @return A pointer to the start of user data with data_length space available
481 */
whd_cdc_get_ioctl_buffer(whd_driver_t whd_driver,whd_buffer_t * buffer,uint16_t data_length)482 void *whd_cdc_get_ioctl_buffer(whd_driver_t whd_driver,
483 whd_buffer_t *buffer,
484 uint16_t data_length)
485 {
486 if ( (uint32_t)IOCTL_OFFSET + data_length > USHRT_MAX )
487 {
488 WPRINT_WHD_ERROR( ("The reserved ioctl buffer length is over %u\n", USHRT_MAX) );
489 return NULL;
490 }
491 if (whd_host_buffer_get(whd_driver, buffer, WHD_NETWORK_TX, (uint16_t)(IOCTL_OFFSET + data_length),
492 (uint32_t)WHD_IOCTL_PACKET_TIMEOUT) == WHD_SUCCESS)
493 {
494 return (whd_buffer_get_current_piece_data_pointer(whd_driver, *buffer) + IOCTL_OFFSET);
495 }
496 else
497 {
498 WPRINT_WHD_ERROR( ("Error - failed to allocate a packet buffer for IOCTL\n") );
499 return NULL;
500 }
501 }
502
503 /** Processes CDC header information received in the RX packet and sets IOCTL response buffer
504 *
505 * @param whd_driver : WHD driver instance
506 * @param buffer : A pointer to a whd_buffer_t object where the created buffer will be stored
507 */
508
whd_process_cdc(whd_driver_t whd_driver,whd_buffer_t buffer)509 void whd_process_cdc(whd_driver_t whd_driver, whd_buffer_t buffer)
510 {
511 uint32_t flags;
512 uint16_t id;
513 whd_cdc_bdc_info_t *cdc_bdc_info = &whd_driver->cdc_bdc_info;
514 whd_result_t result;
515 cdc_header_t *cdc_header = (cdc_header_t *)whd_buffer_get_current_piece_data_pointer(whd_driver, buffer);
516 whd_result_t ioctl_mutex_res;
517 CHECK_PACKET_WITH_NULL_RETURN(cdc_header);
518 flags = dtoh32(cdc_header->flags);
519 id = (uint16_t)( (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT );
520
521 /* Validate request ioctl ID and check if whd_cdc_send_ioctl is still waiting for response*/
522 if ( ( (ioctl_mutex_res = cy_rtos_get_semaphore(&cdc_bdc_info->ioctl_mutex, 0, WHD_FALSE) ) != WHD_SUCCESS ) &&
523 (id == cdc_bdc_info->requested_ioctl_id) )
524 {
525 /* Save the response packet in a variable */
526 cdc_bdc_info->ioctl_response = buffer;
527
528 WPRINT_WHD_DATA_LOG( ("Wcd:< Procd pkt 0x%08lX: IOCTL Response\n", (unsigned long)buffer) );
529
530 /* Wake the thread which sent the IOCTL/IOVAR so that it will resume */
531 result = cy_rtos_set_semaphore(&cdc_bdc_info->ioctl_sleep, WHD_FALSE);
532 if (result != WHD_SUCCESS)
533 WPRINT_WHD_ERROR( ("Error setting semaphore in %s at %d \n", __func__, __LINE__) );
534
535 }
536 else
537 {
538 WPRINT_WHD_ERROR( ("Received buffer request ID: %d (expectation: %d)\n",
539 id, cdc_bdc_info->requested_ioctl_id) );
540 if (ioctl_mutex_res == WHD_SUCCESS)
541 {
542 WPRINT_WHD_ERROR( ("whd_cdc_send_ioctl is already timed out, drop the buffer\n") );
543 result = cy_rtos_set_semaphore(&cdc_bdc_info->ioctl_mutex, WHD_FALSE);
544 if (result != WHD_SUCCESS)
545 {
546 WPRINT_WHD_ERROR( ("Error setting semaphore in %s at %d \n", __func__, __LINE__) );
547 }
548 }
549 else
550 {
551 WPRINT_WHD_ERROR( ("Received a response for a different IOCTL - retry\n") );
552 }
553
554 result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
555 if (result != WHD_SUCCESS)
556 WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
557 }
558 }
559
560 /** Processes BDC header information received in the RX packet and sends data to network stack
561 *
562 * @param whd_driver : WHD driver instance
563 * @param buffer : A pointer to a whd_buffer_t object where the created buffer will be stored
564 */
565
whd_process_bdc(whd_driver_t whd_driver,whd_buffer_t buffer)566 void whd_process_bdc(whd_driver_t whd_driver, whd_buffer_t buffer)
567 {
568 int32_t headers_len_below_payload;
569 uint32_t ip_data_start_add;
570 uint32_t bssid_index;
571 whd_interface_t ifp;
572 whd_result_t result;
573 bdc_header_t *bdc_header = (bdc_header_t *)whd_buffer_get_current_piece_data_pointer(whd_driver, buffer);
574 CHECK_PACKET_WITH_NULL_RETURN(bdc_header);
575 /* Calculate where the payload is */
576 headers_len_below_payload =
577 (int32_t)( (int32_t)BDC_HEADER_LEN + (int32_t)(bdc_header->data_offset << 2) );
578
579 /* Move buffer pointer past gSPI, BUS, BCD headers and padding,
580 * so that the network stack or 802.11 monitor sees only the payload */
581 if (WHD_SUCCESS != whd_buffer_add_remove_at_front(whd_driver, &buffer, headers_len_below_payload) )
582 {
583 WPRINT_WHD_ERROR( ("No space for headers without chaining. this should never happen\n") );
584 result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
585 if (result != WHD_SUCCESS)
586 WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
587
588 return;
589 }
590
591 /* It is preferable to have IP data at address aligned to 4 bytes. IP data startes after ethernet header */
592 ip_data_start_add =
593 (uint32_t )whd_buffer_get_current_piece_data_pointer(whd_driver, buffer) + WHD_ETHERNET_SIZE;
594 if ( ( (ip_data_start_add >> 2) << 2 ) != ip_data_start_add )
595 {
596 WPRINT_WHD_DATA_LOG( ("IP data not aligned to 4 bytes %lx\n", ip_data_start_add) );
597 }
598
599 WPRINT_WHD_DATA_LOG( ("Wcd:< Procd pkt 0x%08lX\n", (unsigned long)buffer) );
600 bssid_index = (uint32_t)(bdc_header->flags2 & BDC_FLAG2_IF_MASK);
601 ifp = whd_driver->iflist[bssid_index];
602
603 /* Send packet to bottom of network stack */
604 result = whd_network_process_ethernet_data(ifp, buffer);
605 if (result != WHD_SUCCESS)
606 WPRINT_WHD_ERROR( ("%s failed at %d \n", __func__, __LINE__) );
607 }
608
609 /** Processes BDC header information and extracts the event packets
610 * Event Packets are decoded to determine which event occurred, and the event handler list is consulted
611 * and the appropriate event handler is called
612 *
613 * @param whd_driver : WHD driver instance
614 * @param buffer : A pointer to a whd_buffer_t object where the created buffer will be stored
615 * @param size : Size of the complete packet received from WLAN device
616 */
whd_process_bdc_event(whd_driver_t whd_driver,whd_buffer_t buffer,uint16_t size)617 void whd_process_bdc_event(whd_driver_t whd_driver, whd_buffer_t buffer, uint16_t size)
618 {
619 uint16_t ether_type;
620 whd_event_header_t *whd_event;
621 whd_event_t *event, *aligned_event = (whd_event_t *)whd_driver->aligned_addr;
622 whd_cdc_bdc_info_t *cdc_bdc_info = &whd_driver->cdc_bdc_info;
623 whd_result_t result;
624 bdc_header_t *bdc_header = (bdc_header_t *)whd_buffer_get_current_piece_data_pointer(whd_driver, buffer);
625 uint16_t i;
626 uint16_t j;
627 uint32_t datalen, addr;
628
629 CHECK_PACKET_WITH_NULL_RETURN(bdc_header);
630 event = (whd_event_t *)&bdc_header[bdc_header->data_offset + 1];
631
632 ether_type = ntoh16(event->eth.ethertype);
633
634 /* If frame is truly an event, it should have EtherType equal to the Broadcom type. */
635 if (ether_type != (uint16_t)ETHER_TYPE_BRCM)
636 {
637 WPRINT_WHD_DEBUG( ("Error - received a channel 1 packet which was not BRCM ethertype\n") );
638 result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
639 if (result != WHD_SUCCESS)
640 WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
641 return;
642 }
643
644 /* If ethertype is correct, the contents of the ethernet packet
645 * are a structure of type bcm_event_t
646 */
647
648 /* Check that the OUI matches the Broadcom OUI */
649 if (0 != memcmp(BRCM_OUI, &event->eth_evt_hdr.oui[0], (size_t)DOT11_OUI_LEN) )
650 {
651 WPRINT_WHD_DEBUG( ("Event OUI mismatch\n") );
652 result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
653 if (result != WHD_SUCCESS)
654 WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
655 return;
656 }
657
658 whd_event = &event->whd_event;
659
660 /* Search for the event type in the list of event handler functions
661 * event data is stored in network endianness
662 */
663 whd_event->flags = ntoh16(whd_event->flags);
664 whd_event->event_type = (whd_event_num_t)ntoh32(whd_event->event_type);
665 whd_event->status = (whd_event_status_t)ntoh32(whd_event->status);
666 whd_event->reason = (whd_event_reason_t)ntoh32(whd_event->reason);
667 whd_event->auth_type = ntoh32(whd_event->auth_type);
668 whd_event->datalen = ntoh32(whd_event->datalen);
669
670 /* Ensure data length is correct */
671 if (whd_event->datalen >
672 (uint32_t)(size - ( (char *)DATA_AFTER_HEADER(event) - (char *)bdc_header ) ) )
673 {
674 WPRINT_WHD_ERROR( (
675 "Error - (data length received [%d] > expected data length [%d]). Bus header packet size = [%d]. Ignoring the packet\n",
676 (int)whd_event->datalen,
677 size - ( (char *)DATA_AFTER_HEADER(event) - (char *)bdc_header ),
678 size) );
679 result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
680 if (result != WHD_SUCCESS)
681 WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
682
683 return;
684 }
685
686 /* This is necessary because people who defined event statuses and reasons overlapped values. */
687 if (whd_event->event_type == WLC_E_PSK_SUP)
688 {
689 whd_event->status = (whd_event_status_t)( (int)whd_event->status + WLC_SUP_STATUS_OFFSET );
690 whd_event->reason = (whd_event_reason_t)( (int)whd_event->reason + WLC_E_SUP_REASON_OFFSET );
691 }
692 else if (whd_event->event_type == WLC_E_PRUNE)
693 {
694 whd_event->reason = (whd_event_reason_t)( (int)whd_event->reason + WLC_E_PRUNE_REASON_OFFSET );
695 }
696 else if ( (whd_event->event_type == WLC_E_DISASSOC) || (whd_event->event_type == WLC_E_DEAUTH) )
697 {
698 whd_event->status = (whd_event_status_t)( (int)whd_event->status + WLC_DOT11_SC_STATUS_OFFSET );
699 whd_event->reason = (whd_event_reason_t)( (int)whd_event->reason + WLC_E_DOT11_RC_REASON_OFFSET );
700 }
701
702 /* do any needed debug logging of event */
703 WHD_IOCTL_LOG_ADD_EVENT(whd_driver, whd_event->event_type, whd_event->status,
704 whd_event->reason);
705
706 if (cy_rtos_get_semaphore(&cdc_bdc_info->event_list_mutex, CY_RTOS_NEVER_TIMEOUT, WHD_FALSE) != WHD_SUCCESS)
707 {
708 WPRINT_WHD_DEBUG( ("Failed to obtain mutex for event list access!\n") );
709 result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
710 if (result != WHD_SUCCESS)
711 WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
712 return;
713 }
714
715 datalen = whd_event->datalen;
716 /* use memcpy to get aligned event message */
717 addr = (uint32_t )DATA_AFTER_HEADER(event);
718 if (aligned_event && (addr & ALIGNED_ADDRESS) )
719 {
720 memcpy(aligned_event, (whd_event_t *)addr, datalen);
721 }
722 else
723 {
724 aligned_event = (whd_event_t *)addr;
725 }
726 for (i = 0; i < (uint16_t)WHD_EVENT_HANDLER_LIST_SIZE; i++)
727 {
728 if (cdc_bdc_info->whd_event_list[i].event_set)
729 {
730 for (j = 0; cdc_bdc_info->whd_event_list[i].events[j] != WLC_E_NONE; ++j)
731 {
732 if ( (cdc_bdc_info->whd_event_list[i].events[j] == whd_event->event_type) &&
733 (cdc_bdc_info->whd_event_list[i].ifidx == whd_event->ifidx) )
734 {
735 /* Correct event type has been found - call the handler function and exit loop */
736 cdc_bdc_info->whd_event_list[i].handler_user_data =
737 cdc_bdc_info->whd_event_list[i].handler(whd_driver->iflist[whd_event->bsscfgidx],
738 whd_event,
739 (uint8_t *)aligned_event,
740 cdc_bdc_info->whd_event_list[i].handler_user_data);
741 break;
742 }
743 }
744 }
745 }
746
747 result = cy_rtos_set_semaphore(&cdc_bdc_info->event_list_mutex, WHD_FALSE);
748 if (result != WHD_SUCCESS)
749 WPRINT_WHD_ERROR( ("Error setting semaphore in %s at %d \n", __func__, __LINE__) );
750
751 WPRINT_WHD_DATA_LOG( ("Wcd:< Procd pkt 0x%08lX: Evnt %d (%d bytes)\n", (unsigned long)buffer,
752 (int)whd_event->event_type, size) );
753
754 /* Release the event packet buffer */
755 result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
756 if (result != WHD_SUCCESS)
757 WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
758
759 }
760
761