1 /*
2  * Copyright 2022, 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