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