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 /** @file
19  *  Provides an implementation of the Broadcom SDPCM protocol.
20  *  The Broadcom SDPCM protocol provides multiplexing of Wireless Data frames,
21  *  I/O Control functions (IOCTL), and Asynchronous Event signalling.
22  *  It is required when communicating with Broadcom 802.11 devices.
23  *
24  */
25 #include "whd_sdpcm.h"
26 #include "bus_protocols/whd_bus_protocol_interface.h"
27 #include "whd_endian.h"
28 #include "whd_chip_constants.h"
29 #include "whd_chip.h"
30 #include "whd_thread_internal.h"
31 #include "whd_debug.h"
32 #include "whd_events_int.h"
33 #include "whd_wifi_api.h"
34 #include "whd_buffer_api.h"
35 #include "whd_network_if.h"
36 #include "whd_wlioctl.h"
37 #include "whd_types_int.h"
38 #include "whd_endian.h"
39 
40 /******************************************************
41 * @cond       Constants
42 ******************************************************/
43 
44 #define ETHER_TYPE_BRCM           (0x886C)      /** Broadcom Ethertype for identifying event packets - Copied from DHD include/proto/ethernet.h */
45 #define BRCM_OUI            "\x00\x10\x18"      /** Broadcom OUI (Organizationally Unique Identifier): Used in the proprietary(221) IE (Information Element) in all Broadcom devices */
46 #define BCM_MSG_IFNAME_MAX            (16)      /** Maximum length of an interface name in a wl_event_msg_t structure*/
47 
48 #define BDC_FLAG2_IF_MASK           (0x0f)
49 
50 #define SDPCM_HEADER_LEN              (12)
51 
52 /* Event flags */
53 #define WLC_EVENT_MSG_LINK      (0x01)    /** link is up */
54 #define WLC_EVENT_MSG_FLUSHTXQ  (0x02)    /** flush tx queue on MIC error */
55 #define WLC_EVENT_MSG_GROUP     (0x04)    /** group MIC error */
56 #define WLC_EVENT_MSG_UNKBSS    (0x08)    /** unknown source bsscfg */
57 #define WLC_EVENT_MSG_UNKIF     (0x10)    /** unknown source OS i/f */
58 
59 /* WMM constants */
60 #define MAX_8021P_PRIO 8
61 #define MAX_WMM_AC     4
62 #define AC_QUEUE_SIZE  64
63 
64 /******************************************************
65 *             Macros
66 ******************************************************/
67 
68 /******************************************************
69 *             Local Structures
70 ******************************************************/
71 
72 #pragma pack(1)
73 
74 /*
75  * Sdio bus specific header consists of 3 parts:
76  * hardware header, hardware extension header and software header
77  *  - hardware header (frame tag) - 4 bytes
78  *  - hardware extension header - 8 bytes
79  *  - software header - 8 bytes
80  */
81 /*
82  * Sdio bus specific header - Software header
83  */
84 typedef struct
85 {
86     uint8_t sequence;               /* Rx/Tx sequence number */
87     uint8_t channel_and_flags;      /*  4 MSB Channel number, 4 LSB arbitrary flag */
88     uint8_t next_length;            /* Length of next data frame, reserved for Tx */
89     uint8_t header_length;          /* Data offset */
90     uint8_t wireless_flow_control;  /* Flow control bits, reserved for Tx */
91     uint8_t bus_data_credit;        /* Maximum Sequence number allowed by firmware for Tx */
92     uint8_t _reserved[2];           /* Reserved */
93 } sdpcm_sw_header_t;
94 
95 /*
96  * SDPCM header definitions
97  */
98 typedef struct
99 {
100     uint16_t frametag[2];
101     sdpcm_sw_header_t sw_header;
102 } sdpcm_header_t;
103 
104 typedef struct bcmeth_hdr
105 {
106     uint16_t subtype;      /** Vendor specific..32769 */
107     uint16_t length;
108     uint8_t version;       /** Version is 0 */
109     uint8_t oui[3];        /** Broadcom OUI */
110     uint16_t usr_subtype;  /** user specific Data */
111 } sdpcm_bcmeth_header_t;
112 
113 /* these fields are stored in network order */
114 typedef struct
115 {
116 
117     uint16_t version;                         /** Version 1 has fields up to ifname.
118                                                * Version 2 has all fields including ifidx and bss_cfg_idx */
119     uint16_t flags;                           /** see flags */
120     uint32_t event_type;                      /** Message */
121     uint32_t status;                          /** Status code */
122     uint32_t reason;                          /** Reason code (if applicable) */
123     uint32_t auth_type;                       /** WLC_E_AUTH */
124     uint32_t datalen;                         /** data buf */
125     whd_mac_t addr;                         /** Station address (if applicable) */
126     char ifname[BCM_MSG_IFNAME_MAX];          /** name of the packet incoming interface */
127     uint8_t ifidx;                            /** destination OS i/f index */
128     uint8_t bss_cfg_idx;                      /** source bsscfg index */
129 } sdpcm_raw_event_header_t;
130 
131 /* used by driver msgs */
132 typedef struct bcm_event
133 {
134     ethernet_header_t ether;
135     sdpcm_bcmeth_header_t bcmeth;
136     union
137     {
138         whd_event_header_t whd;
139         sdpcm_raw_event_header_t raw;
140     } event;
141 } sdpcm_bcm_event_t;
142 
143 #pragma pack()
144 
145 /******************************************************
146 *             Static Variables
147 ******************************************************/
148 /** 802.1p Priority to WMM AC Mapping
149  *
150  *  prio 0, 3: Background(0)
151  *  prio 1, 2: Best Effor(1)
152  *  prio 4, 5: Video(2)
153  *  prio 6, 7: Voice(3)
154  *  prio 8   : Control(4)(ex: IOVAR/IOCTL)
155  */
156 static const uint8_t prio_to_ac[9] = {1, 0, 0, 1, 2, 2, 3, 3, 4};
157 
158 /******************************************************
159 *             SDPCM Logging
160 *
161 * Enable this section to allow logging of SDPCM packets
162 * into a buffer for later perusal
163 *
164 * See sdpcm_log  and  next_sdpcm_log_pos
165 *
166 ******************************************************/
167 /** @cond */
168 
169 #if 0
170 
171 #define SDPCM_LOG_SIZE 30
172 #define SDPCM_LOG_HEADER_SIZE (0x60)
173 
174 typedef enum { UNUSED, LOG_TX, LOG_RX } sdpcm_log_direction_t;
175 typedef enum { IOCTL, DATA, EVENT } sdpcm_log_type_t;
176 
177 typedef struct SDPCM_log_entry_struct
178 {
179     sdpcm_log_direction_t direction;
180     sdpcm_log_type_t type;
181     unsigned long time;
182     unsigned long length;
183     unsigned char header[SDPCM_LOG_HEADER_SIZE];
184 }sdpcm_log_entry_t;
185 
186 static int next_sdpcm_log_pos = 0;
187 static sdpcm_log_entry_t sdpcm_log[SDPCM_LOG_SIZE];
188 
189 static void add_sdpcm_log_entry(sdpcm_log_direction_t dir, sdpcm_log_type_t type, unsigned long length, char *eth_data)
190 {
191 
192     sdpcm_log[next_sdpcm_log_pos].direction = dir;
193     sdpcm_log[next_sdpcm_log_pos].type = type;
194     cy_rtos_get_time(&sdpcm_log[next_sdpcm_log_pos].time);
195     sdpcm_log[next_sdpcm_log_pos].length = length;
196     memcpy(sdpcm_log[next_sdpcm_log_pos].header, eth_data, SDPCM_LOG_HEADER_SIZE);
197     next_sdpcm_log_pos++;
198     if (next_sdpcm_log_pos >= SDPCM_LOG_SIZE)
199     {
200         next_sdpcm_log_pos = 0;
201     }
202 }
203 
204 #else
205 #define add_sdpcm_log_entry(dir, type, length, eth_data)
206 #endif
207 
208 /** @endcond */
209 
210 /******************************************************
211 *             Static Function Prototypes
212 ******************************************************/
213 static whd_buffer_t  whd_sdpcm_get_next_buffer_in_queue(whd_driver_t whd_driver, whd_buffer_t buffer);
214 static void            whd_sdpcm_set_next_buffer_in_queue(whd_driver_t whd_driver, whd_buffer_t buffer,
215                                                           whd_buffer_t prev_buffer);
216 extern void whd_wifi_log_event(whd_driver_t whd_driver, const whd_event_header_t *event_header,
217                                const uint8_t *event_data);
218 /******************************************************
219 *             Function definitions
220 ******************************************************/
221 
222 /** Initialises the SDPCM protocol handler
223  *
224  *  Initialises mutex and semaphore flags needed by the SDPCM handler.
225  *  Also initialises the list of event handlers. This function is called
226  *  from the @ref whd_thread_init function.
227  *
228  * @return    WHD result code
229  */
230 
whd_sdpcm_init(whd_driver_t whd_driver)231 whd_result_t whd_sdpcm_init(whd_driver_t whd_driver)
232 {
233     whd_sdpcm_info_t *sdpcm_info = &whd_driver->sdpcm_info;
234     int ac;
235 
236     /* Create the sdpcm packet queue semaphore */
237     if (cy_rtos_init_semaphore(&sdpcm_info->send_queue_mutex, 1, 0) != WHD_SUCCESS)
238     {
239         return WHD_SEMAPHORE_ERROR;
240     }
241     if (cy_rtos_set_semaphore(&sdpcm_info->send_queue_mutex, WHD_FALSE) != WHD_SUCCESS)
242     {
243         WPRINT_WHD_ERROR( ("Error setting semaphore in %s at %d \n", __func__, __LINE__) );
244         return WHD_SEMAPHORE_ERROR;
245     }
246 
247     /* Packet send queue variables */
248     for (ac = 0; ac <= MAX_WMM_AC; ac++)
249     {
250         sdpcm_info->send_queue_head[ac] = (whd_buffer_t)NULL;
251         sdpcm_info->send_queue_tail[ac] = (whd_buffer_t)NULL;
252         sdpcm_info->npkt_in_q[ac] = 0;
253     }
254     sdpcm_info->totpkt_in_q = 0;
255 
256     whd_sdpcm_bus_vars_init(whd_driver);
257 
258     return WHD_SUCCESS;
259 }
260 
261 /* Re-initialize the bus variables after deep sleep */
whd_sdpcm_bus_vars_init(whd_driver_t whd_driver)262 void whd_sdpcm_bus_vars_init(whd_driver_t whd_driver)
263 {
264     whd_sdpcm_info_t *sdpcm_info = &whd_driver->sdpcm_info;
265 
266     /* Bus data credit variables */
267     sdpcm_info->tx_seq = 0;
268     sdpcm_info->tx_max = (uint8_t)1;
269 }
270 
271 /** Initialises the SDPCM protocol handler
272  *
273  *  De-initialises mutex and semaphore flags needed by the SDPCM handler.
274  *  This function is called from the @ref whd_thread_func function when it is exiting.
275  */
whd_sdpcm_quit(whd_driver_t whd_driver)276 void whd_sdpcm_quit(whd_driver_t whd_driver)
277 {
278     whd_sdpcm_info_t *sdpcm_info = &whd_driver->sdpcm_info;
279     whd_result_t result;
280     int ac;
281 
282     /* Delete the SDPCM queue mutex */
283     (void)cy_rtos_deinit_semaphore(&sdpcm_info->send_queue_mutex);    /* Ignore return - not much can be done about failure */
284 
285     /* Free any left over packets in the queue */
286     for (ac = 0; ac <= MAX_WMM_AC; ac++)
287     {
288         while (sdpcm_info->send_queue_head[ac] != NULL)
289         {
290             whd_buffer_t buf = whd_sdpcm_get_next_buffer_in_queue(whd_driver, sdpcm_info->send_queue_head[ac]);
291             result = whd_buffer_release(whd_driver, sdpcm_info->send_queue_head[ac], WHD_NETWORK_TX);
292             if (result != WHD_SUCCESS)
293                 WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
294             sdpcm_info->send_queue_head[ac] = buf;
295         }
296         sdpcm_info->npkt_in_q[ac] = 0;
297     }
298     sdpcm_info->totpkt_in_q = 0;
299 }
300 
whd_sdpcm_update_credit(whd_driver_t whd_driver,uint8_t * data)301 void whd_sdpcm_update_credit(whd_driver_t whd_driver, uint8_t *data)
302 {
303     sdpcm_sw_header_t *header = (sdpcm_sw_header_t *)(data + 4);
304     whd_sdpcm_info_t *sdpcm_info = &whd_driver->sdpcm_info;
305     uint8_t tx_seq_max;
306 
307     if ( (header->channel_and_flags & 0x0f) < (uint8_t)3 )
308     {
309         tx_seq_max = header->bus_data_credit;
310         WPRINT_WHD_DATA_LOG( ("credit update to %d\n ", tx_seq_max) );
311         if (tx_seq_max - sdpcm_info->tx_seq > 0x40)
312         {
313             WPRINT_WHD_ERROR( ("update credit error\n") );
314             tx_seq_max = sdpcm_info->tx_seq + 2;
315         }
316         sdpcm_info->tx_max = tx_seq_max;
317     }
318 
319     whd_bus_set_flow_control(whd_driver, header->wireless_flow_control);
320 }
321 
322 /** Processes and directs incoming SDPCM packets
323  *
324  *  This function receives SDPCM packets from the Broadcom 802.11 device and decodes the SDPCM header
325  *  to determine where the packet should be directed.
326  *
327  *  Control packets (IOCTL/IOVAR) / Data Packets/ Event Packets are passed to CDC/BDC layer
328  *  and the appropriate event handler is called.
329  *
330  * @param buffer  : The SDPCM packet buffer received from the Broadcom 802.11 device
331  *
332  */
whd_sdpcm_process_rx_packet(whd_driver_t whd_driver,whd_buffer_t buffer)333 void whd_sdpcm_process_rx_packet(whd_driver_t whd_driver, whd_buffer_t buffer)
334 {
335     bus_common_header_t *packet;
336     uint16_t size;
337     uint16_t size_inv;
338     sdpcm_header_t sdpcm_header;
339     whd_result_t result;
340 
341     packet = (bus_common_header_t *)whd_buffer_get_current_piece_data_pointer(whd_driver, buffer);
342     CHECK_PACKET_WITH_NULL_RETURN(packet);
343     memcpy(&sdpcm_header, packet->bus_header, BUS_HEADER_LEN);
344 
345     sdpcm_header.frametag[0] = dtoh16(sdpcm_header.frametag[0]);
346     sdpcm_header.frametag[1] = dtoh16(sdpcm_header.frametag[1]);
347 
348     /* Extract the total SDPCM packet size from the first two frametag bytes */
349     size = sdpcm_header.frametag[0];
350 
351     /* Check that the second two frametag bytes are the binary inverse of the size */
352     size_inv = (uint16_t) ~size;  /* Separate variable due to GCC Bug 38341 */
353     if (sdpcm_header.frametag[1] != size_inv)
354     {
355         WPRINT_WHD_DEBUG( ("Received a packet with a frametag which is wrong\n") );
356         result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
357         if (result != WHD_SUCCESS)
358             WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
359         return;
360     }
361 
362     /* Check whether the packet is big enough to contain the SDPCM header (or) it's too big to handle */
363     if ( (size < (uint16_t)SDPCM_HEADER_LEN) || (size > whd_buffer_get_current_piece_size(whd_driver, buffer) ) )
364     {
365         whd_minor_assert("Packet size invalid", 0 == 1);
366         WPRINT_WHD_DEBUG( (
367                               "Received a packet that is too small to contain anything useful (or) too big. Packet Size = [%d]\n",
368                               size) );
369         result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
370         if (result != WHD_SUCCESS)
371             WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
372         return;
373     }
374 
375     /* Get address of packet->sdpcm_header.frametag indirectly to avoid IAR's unaligned address warning */
376     whd_sdpcm_update_credit(whd_driver,
377                             (uint8_t *)&sdpcm_header.sw_header - sizeof(sdpcm_header.frametag) );
378 
379     if (size == (uint16_t)SDPCM_HEADER_LEN)
380     {
381         /* This is a flow control update packet with no data - release it. */
382         result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
383         if (result != WHD_SUCCESS)
384             WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
385 
386         return;
387     }
388 
389     /* Check the SDPCM channel to decide what to do with packet. */
390     switch (sdpcm_header.sw_header.channel_and_flags & 0x0f)
391     {
392         case CONTROL_HEADER:  /* IOCTL/IOVAR reply packet */
393         {
394             add_sdpcm_log_entry(LOG_RX, IOCTL, whd_buffer_get_current_piece_size(whd_driver, buffer),
395                                 (char *)whd_buffer_get_current_piece_data_pointer(whd_driver, buffer) );
396 
397             /* Check that packet size is big enough to contain the CDC header as well as the SDPCM header */
398             if (sdpcm_header.frametag[0] <
399                 (sizeof(sdpcm_header.frametag) + sizeof(sdpcm_sw_header_t) + sizeof(cdc_header_t) ) )
400             {
401                 /* Received a too-short SDPCM packet! */
402                 WPRINT_WHD_DEBUG( ("Received a too-short SDPCM packet!\n") );
403                 result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
404                 if (result != WHD_SUCCESS)
405                     WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
406 
407                 break;
408             }
409 
410             /* Move SDPCM header and Buffer header to pass onto next layer */
411             whd_buffer_add_remove_at_front(whd_driver, &buffer,
412                                            (int32_t)(sizeof(whd_buffer_header_t) +
413                                                      sdpcm_header.sw_header.header_length) );
414 
415             whd_process_cdc(whd_driver, buffer);
416         }
417 
418         break;
419 
420         case DATA_HEADER:
421         {
422             /* Check that the packet is big enough to contain SDPCM & BDC headers */
423             if (sdpcm_header.frametag[0] <=
424                 (sizeof(sdpcm_header.frametag) + sizeof(sdpcm_sw_header_t) + sizeof(bdc_header_t) ) )
425             {
426                 WPRINT_WHD_ERROR( ("Packet too small to contain SDPCM + BDC headers\n") );
427                 result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
428                 if (result != WHD_SUCCESS)
429                     WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
430 
431                 break;
432             }
433 
434             /* Move SDPCM header and Buffer header to pass onto next layer */
435             whd_buffer_add_remove_at_front(whd_driver, &buffer,
436                                            (int32_t)(sizeof(whd_buffer_header_t) +
437                                                      sdpcm_header.sw_header.header_length) );
438 
439             whd_process_bdc(whd_driver, buffer);
440 
441         }
442         break;
443 
444         case ASYNCEVENT_HEADER:
445         {
446 
447             /* Move SDPCM header and Buffer header to pass onto next layer */
448             whd_buffer_add_remove_at_front(whd_driver, &buffer,
449                                            (int32_t)(sizeof(whd_buffer_header_t) +
450                                                      sdpcm_header.sw_header.header_length) );
451 
452             whd_process_bdc_event(whd_driver, buffer, size);
453         }
454         break;
455 
456         default:
457             whd_minor_assert("SDPCM packet of unknown channel received - dropping packet", 0 != 0);
458             result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_RX);
459             if (result != WHD_SUCCESS)
460                 WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
461             break;
462     }
463 }
464 
whd_sdpcm_has_tx_packet(whd_driver_t whd_driver)465 whd_bool_t whd_sdpcm_has_tx_packet(whd_driver_t whd_driver)
466 {
467     if (whd_driver->sdpcm_info.totpkt_in_q > 0)
468     {
469         return WHD_TRUE;
470     }
471 
472     return WHD_FALSE;
473 }
474 
whd_sdpcm_get_packet_to_send(whd_driver_t whd_driver,whd_buffer_t * buffer)475 whd_result_t whd_sdpcm_get_packet_to_send(whd_driver_t whd_driver, whd_buffer_t *buffer)
476 {
477     bus_common_header_t *packet;
478     sdpcm_header_t sdpcm_header;
479     whd_sdpcm_info_t *sdpcm_info = &whd_driver->sdpcm_info;
480     whd_result_t result;
481     int ac;
482 
483     if (sdpcm_info->totpkt_in_q <= 0)
484     {
485         return WHD_NO_PACKET_TO_SEND;
486     }
487 
488     /* Check if we're being flow controlled for Data packet only. */
489     if ( (whd_bus_is_flow_controlled(whd_driver) == WHD_TRUE) && (sdpcm_info->npkt_in_q[MAX_WMM_AC] == 0) )
490     {
491         WHD_STATS_INCREMENT_VARIABLE(whd_driver, flow_control);
492         return WHD_FLOW_CONTROLLED;
493     }
494 
495     /* Check if we have enough bus data credits spare */
496     if ( ( (uint8_t)(sdpcm_info->tx_max - sdpcm_info->tx_seq) == 0 ) ||
497          ( ( (uint8_t)(sdpcm_info->tx_max - sdpcm_info->tx_seq) & 0x80 ) != 0 ) )
498     {
499         WHD_STATS_INCREMENT_VARIABLE(whd_driver, no_credit);
500         return WHD_NO_CREDITS;
501     }
502 
503     /* There is a packet waiting to be sent - send it then fix up queue and release packet */
504     if (cy_rtos_get_semaphore(&sdpcm_info->send_queue_mutex, CY_RTOS_NEVER_TIMEOUT, WHD_FALSE) != WHD_SUCCESS)
505     {
506         /* Could not obtain mutex, push back the flow control semaphore */
507         WPRINT_WHD_ERROR( ("Error manipulating a semaphore, %s failed at %d \n", __func__, __LINE__) );
508         return WHD_SEMAPHORE_ERROR;
509     }
510 
511     for (ac = MAX_WMM_AC; ac >= 0; ac--)
512     {
513         if (sdpcm_info->send_queue_head[ac] != NULL)
514         {
515             break;
516         }
517     }
518     if (ac < 0)
519     {
520         WPRINT_WHD_ERROR( ("NO pkt available in queue, %s failed at %d\n", __func__, __LINE__) );
521         return WHD_NO_PACKET_TO_SEND;
522     }
523     /* Pop the head off and set the new send_queue head */
524     *buffer = sdpcm_info->send_queue_head[ac];
525     sdpcm_info->send_queue_head[ac] = whd_sdpcm_get_next_buffer_in_queue(whd_driver, *buffer);
526     if (sdpcm_info->send_queue_head[ac] == NULL)
527     {
528         sdpcm_info->send_queue_tail[ac] = NULL;
529     }
530     sdpcm_info->npkt_in_q[ac]--;
531     sdpcm_info->totpkt_in_q--;
532     result = cy_rtos_set_semaphore(&sdpcm_info->send_queue_mutex, WHD_FALSE);
533     if (result != WHD_SUCCESS)
534     {
535         WPRINT_WHD_ERROR( ("Error setting semaphore in %s at %d \n", __func__, __LINE__) );
536     }
537 
538     /* Set the sequence number */
539     packet = (bus_common_header_t *)whd_buffer_get_current_piece_data_pointer(whd_driver, *buffer);
540     CHECK_PACKET_NULL(packet, WHD_NO_REGISTER_FUNCTION_POINTER);
541     memcpy(&sdpcm_header, packet->bus_header, BUS_HEADER_LEN);
542     sdpcm_header.sw_header.sequence = sdpcm_info->tx_seq;
543     memcpy(packet->bus_header, &sdpcm_header, BUS_HEADER_LEN);
544     sdpcm_info->tx_seq++;
545 
546     return WHD_SUCCESS;
547 }
548 
549 /** Returns the number of bus credits available
550  *
551  * @return The number of bus credits available
552  */
whd_sdpcm_get_available_credits(whd_driver_t whd_driver)553 uint8_t whd_sdpcm_get_available_credits(whd_driver_t whd_driver)
554 {
555     uint8_t tx_max = whd_driver->sdpcm_info.tx_max;
556     uint8_t tx_seq = whd_driver->sdpcm_info.tx_seq;
557     if ( ( (uint8_t)(tx_max - tx_seq) & 0x80 ) != 0 )
558     {
559         return 0;
560     }
561     return (uint8_t)(tx_max - tx_seq);
562 }
563 
564 /** Writes SDPCM headers and sends packet to WHD Thread
565  *
566  *  Prepends the given packet with a new SDPCM header,
567  *  then passes the packet to the WHD thread via a queue
568  *
569  *  This function is called by @ref whd_network_send_ethernet_data and @ref whd_cdc_send_ioctl
570  *
571  *  @param buffer     : The handle of the packet buffer to send
572  *  @param header_type  : DATA_HEADER, ASYNCEVENT_HEADER or CONTROL_HEADER - indicating what type of SDPCM packet this is.
573  *
574  *  @return WHD result code
575  */
whd_send_to_bus(whd_driver_t whd_driver,whd_buffer_t buffer,sdpcm_header_type_t header_type,uint8_t prio)576 whd_result_t whd_send_to_bus(whd_driver_t whd_driver, whd_buffer_t buffer,
577                              sdpcm_header_type_t header_type, uint8_t prio)
578 {
579     uint16_t size;
580     uint8_t *data = NULL;
581     bus_common_header_t *packet =
582         (bus_common_header_t *)whd_buffer_get_current_piece_data_pointer(whd_driver, buffer);
583     sdpcm_header_t sdpcm_header;
584     whd_sdpcm_info_t *sdpcm_info = &whd_driver->sdpcm_info;
585     whd_result_t result;
586     int ac;
587 
588     CHECK_PACKET_NULL(packet, WHD_NO_REGISTER_FUNCTION_POINTER);
589     size = whd_buffer_get_current_piece_size(whd_driver, buffer);
590 
591     size = (uint16_t)(size - (uint16_t)sizeof(whd_buffer_header_t) );
592 
593     /* Prepare the SDPCM header */
594     memset( (uint8_t *)&sdpcm_header, 0, sizeof(sdpcm_header_t) );
595     sdpcm_header.sw_header.channel_and_flags = (uint8_t)header_type;
596     sdpcm_header.sw_header.header_length =
597         (header_type == DATA_HEADER) ? sizeof(sdpcm_header_t) + 2 : sizeof(sdpcm_header_t);
598     sdpcm_header.sw_header.sequence = 0; /* Note: The real sequence will be written later */
599     sdpcm_header.frametag[0] = size;
600     sdpcm_header.frametag[1] = (uint16_t) ~size;
601 
602     memcpy(packet->bus_header, &sdpcm_header, BUS_HEADER_LEN);
603     data = whd_buffer_get_current_piece_data_pointer(whd_driver, buffer);
604     CHECK_PACKET_NULL(data, WHD_NO_REGISTER_FUNCTION_POINTER);
605     add_sdpcm_log_entry(LOG_TX, (header_type == DATA_HEADER) ? DATA : (header_type == CONTROL_HEADER) ? IOCTL : EVENT,
606                         whd_buffer_get_current_piece_size(whd_driver, buffer),
607                         (char *)data);
608 
609     /* Add the length of the SDPCM header and pass "down" */
610     if (cy_rtos_get_semaphore(&sdpcm_info->send_queue_mutex, CY_RTOS_NEVER_TIMEOUT, WHD_FALSE) != WHD_SUCCESS)
611     {
612         /* Could not obtain mutex */
613         /* Fatal error */
614         result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_TX);
615         if (result != WHD_SUCCESS)
616             WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
617         return WHD_SEMAPHORE_ERROR;
618     }
619 
620     /* The input priority should not higher than MAX_8021P_PRIO(7) */
621     if (prio > MAX_8021P_PRIO)
622     {
623         prio = MAX_8021P_PRIO;
624     }
625     ac = prio_to_ac[prio];
626 
627     if ( (header_type == DATA_HEADER) && (sdpcm_info->npkt_in_q[ac] > AC_QUEUE_SIZE) )
628     {
629         result = whd_buffer_release(whd_driver, buffer, WHD_NETWORK_TX);
630         if (result != WHD_SUCCESS)
631         {
632             WPRINT_WHD_ERROR( ("buffer release failed in %s at %d \n", __func__, __LINE__) );
633         }
634         result = cy_rtos_set_semaphore(&sdpcm_info->send_queue_mutex, WHD_FALSE);
635         if (result != WHD_SUCCESS)
636         {
637             WPRINT_WHD_ERROR( ("Error setting semaphore in %s at %d \n", __func__, __LINE__) );
638         }
639         whd_thread_notify(whd_driver);
640         return WHD_BUFFER_ALLOC_FAIL;
641     }
642 
643     whd_sdpcm_set_next_buffer_in_queue(whd_driver, NULL, buffer);
644     if (sdpcm_info->send_queue_tail[ac] != NULL)
645     {
646         whd_sdpcm_set_next_buffer_in_queue(whd_driver, buffer, sdpcm_info->send_queue_tail[ac]);
647     }
648     sdpcm_info->send_queue_tail[ac] = buffer;
649     if (sdpcm_info->send_queue_head[ac] == NULL)
650     {
651         sdpcm_info->send_queue_head[ac] = buffer;
652     }
653     sdpcm_info->npkt_in_q[ac]++;
654     sdpcm_info->totpkt_in_q++;
655     result = cy_rtos_set_semaphore(&sdpcm_info->send_queue_mutex, WHD_FALSE);
656     if (result != WHD_SUCCESS)
657         WPRINT_WHD_ERROR( ("Error setting semaphore in %s at %d \n", __func__, __LINE__) );
658 
659     whd_thread_notify(whd_driver);
660 
661     return WHD_SUCCESS;
662 }
663 
664 /******************************************************
665 *             Static Functions
666 ******************************************************/
667 
whd_sdpcm_get_next_buffer_in_queue(whd_driver_t whd_driver,whd_buffer_t buffer)668 static whd_buffer_t whd_sdpcm_get_next_buffer_in_queue(whd_driver_t whd_driver, whd_buffer_t buffer)
669 {
670     whd_buffer_header_t *packet = (whd_buffer_header_t *)whd_buffer_get_current_piece_data_pointer(whd_driver, buffer);
671     return packet->queue_next;
672 }
673 
674 /** Sets the next buffer in the send queue
675  *
676  *  The send queue is a linked list of packet buffers where the 'next' pointer
677  *  is stored in the first 4 bytes of the buffer content.
678  *  This function sets that pointer.
679  *
680  * @param buffer       : handle of packet in the send queue
681  *        prev_buffer  : handle of new packet whose 'next' pointer will point to 'buffer'
682  */
whd_sdpcm_set_next_buffer_in_queue(whd_driver_t whd_driver,whd_buffer_t buffer,whd_buffer_t prev_buffer)683 static void whd_sdpcm_set_next_buffer_in_queue(whd_driver_t whd_driver, whd_buffer_t buffer, whd_buffer_t prev_buffer)
684 {
685     whd_buffer_header_t *packet =
686         (whd_buffer_header_t *)whd_buffer_get_current_piece_data_pointer(whd_driver, prev_buffer);
687     packet->queue_next = buffer;
688 }
689 
690