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 * Implements initialisation and other management functions for WHD system
20 *
21 */
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include "whd_utils.h"
26 #include "bus_protocols/whd_bus_common.h"
27 #include "bus_protocols/whd_bus_protocol_interface.h"
28 #include "whd_debug.h"
29 #include "whd_events_int.h"
30 #include "whd_int.h"
31 #include "whd_chip.h"
32 #include "whd_sdpcm.h"
33 #include "whd_wifi_api.h"
34 #include "whd_clm.h"
35 #include "whd_wlioctl.h"
36 #include "whd_types_int.h"
37 #include "whd_chip_constants.h"
38
39 /******************************************************
40 * Constants
41 ******************************************************/
42
43 #define MAX_POST_SET_COUNTRY_RETRY 3
44
45 #define MINIMUM_WHD_STACK_SIZE (1024 + 1200 + 2500)
46
47 #define DEFAULT_PM2_SLEEP_RET_TIME 200
48 /******************************************************
49 * Static Variables
50 ******************************************************/
51
52 /******************************************************
53 * Function definitions
54 ******************************************************/
55 whd_result_t whd_management_wifi_platform_init(whd_driver_t whd_driver, whd_country_code_t country,
56 whd_bool_t resume_after_deep_sleep);
57 void whd_wifi_bus_irq_handle(void *handler_arg, uint32_t event);
58
whd_get_primary_interface(whd_driver_t whd_driver)59 whd_interface_t whd_get_primary_interface(whd_driver_t whd_driver)
60 {
61 if (whd_driver->iflist[0] != NULL)
62 {
63 return whd_driver->iflist[0];
64 }
65 return NULL;
66 }
67
whd_add_interface(whd_driver_t whd_driver,uint8_t bsscfgidx,uint8_t ifidx,const char * name,whd_mac_t * mac_addr,whd_interface_t * ifpp)68 whd_result_t whd_add_interface(whd_driver_t whd_driver, uint8_t bsscfgidx, uint8_t ifidx,
69 const char *name, whd_mac_t *mac_addr, whd_interface_t *ifpp)
70 {
71 whd_interface_t ifp;
72
73 if (!whd_driver || !ifpp)
74 {
75 WPRINT_WHD_ERROR( ("Invalid param in func %s at line %d \n",
76 __func__, __LINE__) );
77 return WHD_WLAN_BADARG;
78 }
79
80 if (bsscfgidx < WHD_INTERFACE_MAX)
81 {
82 if (whd_driver->iflist[bsscfgidx] != NULL)
83 {
84 *ifpp = whd_driver->iflist[bsscfgidx];
85 return WHD_SUCCESS;
86 }
87
88 if ( (ifp = (whd_interface_t)whd_mem_malloc(sizeof(struct whd_interface) ) ) != NULL )
89 {
90 memset(ifp, 0, (sizeof(struct whd_interface) ) );
91 *ifpp = ifp;
92 ifp->whd_driver = whd_driver;
93
94 /* Add a interface name */
95 /* strncpy doesn't terminate with null if the src string is long */
96 ifp->if_name[WHD_MSG_IFNAME_MAX - 1] = '\0';
97 strncpy(ifp->if_name, name, sizeof(ifp->if_name) - 1);
98 memset(ifp->event_reg_list, WHD_EVENT_NOT_REGISTERED, sizeof(ifp->event_reg_list) );
99 /* Primary interface takes 0 as default */
100 ifp->ifidx = ifidx;
101 ifp->bsscfgidx = bsscfgidx;
102
103 if (mac_addr != NULL)
104 memcpy(ifp->mac_addr.octet, mac_addr->octet, sizeof(whd_mac_t) );
105 else
106 memset(ifp->mac_addr.octet, 0, sizeof(whd_mac_t) );
107
108 whd_driver->iflist[bsscfgidx] = ifp;
109 whd_driver->if2ifp[ifidx] = bsscfgidx;
110 }
111 else
112 {
113 return WHD_MALLOC_FAILURE;
114 }
115
116 return WHD_SUCCESS;
117 }
118 return WHD_INVALID_INTERFACE;
119 }
120
whd_add_primary_interface(whd_driver_t whd_driver,whd_interface_t * ifpp)121 whd_result_t whd_add_primary_interface(whd_driver_t whd_driver, whd_interface_t *ifpp)
122 {
123 return whd_add_interface(whd_driver, 0, 0, "wlan0", NULL, ifpp);
124 }
125
whd_add_secondary_interface(whd_driver_t whd_driver,whd_mac_t * mac_addr,whd_interface_t * ifpp)126 uint32_t whd_add_secondary_interface(whd_driver_t whd_driver, whd_mac_t *mac_addr, whd_interface_t *ifpp)
127 {
128 return whd_add_interface(whd_driver, 1, 1, "wlan1", mac_addr, ifpp);
129 }
130
whd_init(whd_driver_t * whd_driver_ptr,whd_init_config_t * whd_init_config,whd_resource_source_t * resource_ops,whd_buffer_funcs_t * buffer_ops,whd_netif_funcs_t * network_ops)131 uint32_t whd_init(whd_driver_t *whd_driver_ptr, whd_init_config_t *whd_init_config,
132 whd_resource_source_t *resource_ops, whd_buffer_funcs_t *buffer_ops,
133 whd_netif_funcs_t *network_ops)
134 {
135 whd_driver_t whd_drv;
136
137 if (!whd_driver_ptr || !buffer_ops || !network_ops || !resource_ops || !whd_init_config)
138 {
139 WPRINT_WHD_ERROR( ("Invalid param in func %s at line %d \n",
140 __func__, __LINE__) );
141 return WHD_WLAN_BADARG;
142 }
143 if (whd_init_config->thread_stack_size < MINIMUM_WHD_STACK_SIZE)
144 {
145 WPRINT_WHD_INFO( ("Stack size is less than minimum stack size required.\n") );
146 return WHD_WLAN_BUFTOOSHORT;
147 }
148
149 if ( (whd_drv = (whd_driver_t)whd_mem_malloc(sizeof(struct whd_driver) ) ) != NULL )
150 {
151 memset(whd_drv, 0, sizeof(struct whd_driver) );
152 *whd_driver_ptr = whd_drv;
153 whd_drv->buffer_if = buffer_ops;
154 whd_drv->network_if = network_ops;
155 whd_drv->resource_if = resource_ops;
156 whd_bus_common_info_init(whd_drv);
157 whd_thread_info_init(whd_drv, whd_init_config);
158 whd_cdc_bdc_info_init(whd_drv);
159 whd_internal_info_init(whd_drv);
160 whd_ap_info_init(whd_drv);
161 //whd_wifi_sleep_info_init(whd_drv);
162 whd_wifi_chip_info_init(whd_drv);
163
164 whd_drv->bus_gspi_32bit = WHD_FALSE;
165
166 if (whd_init_config->country == 0)
167 whd_drv->country = WHD_COUNTRY_UNITED_STATES;
168 else
169 whd_drv->country = whd_init_config->country;
170 }
171 else
172 {
173 return WHD_MALLOC_FAILURE;
174 }
175 return WHD_SUCCESS;
176 }
177
whd_deinit(whd_interface_t ifp)178 uint32_t whd_deinit(whd_interface_t ifp)
179 {
180 uint8_t i;
181 whd_driver_t whd_driver;
182
183 CHECK_IFP_NULL(ifp);
184 whd_driver = ifp->whd_driver;
185
186 if (whd_driver->internal_info.whd_wlan_status.state != WLAN_OFF)
187 {
188 WPRINT_WHD_ERROR( ("Could not deinit whd because wifi power is on\n") );
189 return WHD_WLAN_NOTDOWN;
190 }
191
192 if ( (whd_driver->bus_priv != NULL) || (whd_driver->bus_if != NULL) )
193 {
194 WPRINT_WHD_ERROR( ("Could not deinit whd because bus is attaced\n") );
195 return WHD_WLAN_NOTDOWN;
196 }
197
198 for (i = 0; i < WHD_INTERFACE_MAX; i++)
199 {
200 if (whd_driver->iflist[i] != NULL)
201 {
202 whd_mem_free(whd_driver->iflist[i]);
203 whd_driver->iflist[i] = NULL;
204 }
205 }
206
207 whd_cdc_bdc_info_deinit(whd_driver);
208 whd_internal_info_deinit(whd_driver);
209 whd_bus_common_info_deinit(whd_driver);
210 whd_mem_free(whd_driver);
211
212 return WHD_SUCCESS;
213 }
214
215 /**
216 * Initialize Wi-Fi platform
217 *
218 * - Initializes the required parts of the hardware platform
219 * i.e. pins for SDIO/SPI, interrupt, reset, power etc.
220 *
221 * - Initializes the WHD thread which arbitrates access
222 * to the SDIO/SPI bus
223 *
224 * @return WHD_SUCCESS if initialization is successful, error code otherwise
225 */
226
whd_management_wifi_platform_init(whd_driver_t whd_driver,whd_country_code_t country,whd_bool_t resume_after_deep_sleep)227 whd_result_t whd_management_wifi_platform_init(whd_driver_t whd_driver, whd_country_code_t country,
228 whd_bool_t resume_after_deep_sleep)
229 {
230 whd_result_t retval;
231
232 whd_driver->internal_info.whd_wlan_status.country_code = country;
233
234 if (resume_after_deep_sleep == WHD_TRUE)
235 {
236 retval = ( whd_result_t )whd_bus_resume_after_deep_sleep(whd_driver);
237 }
238 else
239 {
240 whd_bus_init_stats(whd_driver);
241 retval = ( whd_result_t )whd_bus_init(whd_driver);
242 }
243
244 if (retval != WHD_SUCCESS)
245 {
246 /* May have been due to user abort */
247 WPRINT_WHD_INFO( ("Could not initialize bus\n") );
248 return retval;
249 }
250
251 /* WLAN device is now powered up. Change state from OFF to DOWN */
252 whd_driver->internal_info.whd_wlan_status.state = WLAN_DOWN;
253
254
255 retval = whd_thread_init(whd_driver);
256 if (retval != WHD_SUCCESS)
257 {
258 WPRINT_WHD_ERROR( ("Could not initialize WHD thread\n") );
259 return retval;
260 }
261
262 return WHD_SUCCESS;
263 }
264
265 /**
266 * Turn on the Wi-Fi device
267 *
268 * - Initialize Wi-Fi device
269 *
270 * - Program various WiFi parameters and modes
271 *
272 * @return WHD_SUCCESS if initialization is successful, error code otherwise
273 */
whd_wifi_on(whd_driver_t whd_driver,whd_interface_t * ifpp)274 uint32_t whd_wifi_on(whd_driver_t whd_driver, whd_interface_t *ifpp)
275 {
276 wl_country_t *country_struct;
277 uint32_t *ptr;
278 whd_result_t retval;
279 whd_buffer_t buffer;
280 uint8_t *event_mask;
281 uint32_t *data;
282 uint32_t counter;
283 whd_interface_t ifp;
284 uint16_t wlan_chip_id = 0;
285
286 if (!whd_driver || !ifpp)
287 {
288 WPRINT_WHD_ERROR( ("Invalid param in func %s at line %d \n",
289 __func__, __LINE__) );
290 return WHD_WLAN_BADARG;
291 }
292
293 if (whd_driver->internal_info.whd_wlan_status.state == WLAN_UP)
294 {
295 return WHD_SUCCESS;
296 }
297
298 whd_init_stats(whd_driver);
299
300
301 retval = whd_management_wifi_platform_init(whd_driver, whd_driver->country, WHD_FALSE);
302 if (retval != WHD_SUCCESS)
303 {
304 WPRINT_WHD_INFO( ("Could not initialize wifi platform\n") );
305 return retval;
306 }
307
308 whd_add_primary_interface(whd_driver, ifpp);
309 ifp = *ifpp;
310
311 /* Download blob file if exists */
312 retval = whd_process_clm_data(ifp);
313 if (retval != WHD_SUCCESS)
314 {
315 WPRINT_MACRO( ("****************************************************\n"
316 "** ERROR: WLAN: could not download clm_blob file\n"
317 "** FATAL ERROR: system unusable, CLM blob file not found or corrupted.\n"
318 "****************************************************\n") );
319 return retval;
320 }
321
322 retval = whd_bus_share_bt_init(whd_driver);
323 if (retval != WHD_SUCCESS)
324 {
325 WPRINT_WHD_INFO( ("Shared bus for bt is fail\n") );
326 }
327
328 /* Get FW Capability */
329 retval = whd_wifi_read_fw_capabilities(ifp);
330 if (retval != WHD_SUCCESS)
331 {
332 WPRINT_WHD_INFO( ("Get FW Capabilities Fail\n") );
333 }
334
335 /* Turn off SDPCM TX Glomming */
336 /* Note: This is only required for later chips.
337 * The 4319 has glomming off by default however the 43362 has it on by default.
338 */
339 data = (uint32_t *)whd_cdc_get_iovar_buffer(whd_driver, &buffer, (uint16_t)4, IOVAR_STR_TX_GLOM);
340 if (data == NULL)
341 {
342 whd_assert("Could not get buffer for IOVAR", 0 != 0);
343 return WHD_BUFFER_ALLOC_FAIL;
344 }
345 *data = 0;
346 retval = whd_cdc_send_iovar(ifp, CDC_SET, buffer, 0);
347 if ( (retval != WHD_SUCCESS) && (retval != WHD_WLAN_UNSUPPORTED) )
348 {
349 /* Note: System may time out here if bus interrupts are not working properly */
350 WPRINT_WHD_ERROR( ("Could not turn off TX glomming\n") );
351 return retval;
352 }
353
354 /* Turn APSTA on */
355 data = (uint32_t *)whd_cdc_get_iovar_buffer(whd_driver, &buffer, (uint16_t)sizeof(*data), IOVAR_STR_APSTA);
356 if (data == NULL)
357 {
358 whd_assert("Could not get buffer for IOVAR", 0 != 0);
359 return WHD_BUFFER_ALLOC_FAIL;
360 }
361 *data = htod32( (uint32_t)1 );
362 /* This will fail on manufacturing test build since it does not have APSTA available */
363 retval = whd_cdc_send_iovar(ifp, CDC_SET, buffer, 0);
364 if (retval == WHD_WLAN_UNSUPPORTED)
365 {
366 WPRINT_WHD_DEBUG( ("Firmware does not support APSTA\n") );
367 }
368 else if (retval != WHD_SUCCESS)
369 {
370 WPRINT_WHD_ERROR( ("Could not turn on APSTA\n") );
371 return retval;
372 }
373
374 /* Send set country command */
375 /* This is the first time that the WLAN chip is required to respond
376 * in it's normal run mode.
377 * If you are porting a new system and it stalls here, it could
378 * be one of the following problems:
379 * - Bus interrupt not triggering properly - the WLAN chip is unable to signal the host that there is data available.
380 * - Timing problems - if the timeouts on semaphores are not working correctly, then the
381 * system might think that the IOCTL has timed out much faster than it should do.
382 *
383 */
384
385 country_struct = (wl_country_t *)whd_cdc_get_iovar_buffer(whd_driver, &buffer, (uint16_t)sizeof(wl_country_t),
386 IOVAR_STR_COUNTRY);
387 if (country_struct == NULL)
388 {
389 whd_assert("Could not get buffer for IOCTL", 0 != 0);
390 return WHD_BUFFER_ALLOC_FAIL;
391 }
392 memset(country_struct, 0, sizeof(wl_country_t) );
393
394 ptr = (uint32_t *)country_struct->ccode;
395 *ptr = (uint32_t)whd_driver->internal_info.whd_wlan_status.country_code & 0x0000ffff;
396 ptr = (uint32_t *)country_struct->country_abbrev;
397 *ptr = (uint32_t)whd_driver->internal_info.whd_wlan_status.country_code & 0x0000ffff;
398 country_struct->rev = (int32_t)( (whd_driver->internal_info.whd_wlan_status.country_code & 0xffff0000) >> 16 );
399
400 /* if regrev is 0 then set regrev to -1 so the FW will use any NVRAM/OTP configured aggregate
401 * to choose the regrev. If there is no aggregate configured then the FW will try to use regrev 0.
402 */
403 if (country_struct->rev == 0)
404 {
405 country_struct->rev = (int32_t)htod32(-1);
406 }
407 retval = whd_cdc_send_iovar(ifp, CDC_SET, buffer, 0);
408 if (retval != WHD_SUCCESS)
409 {
410 /* Could not set wifi country */
411 WPRINT_WHD_ERROR( ("Could not set Country code\n") );
412 return retval;
413 }
414
415 /* NOTE: The set country command requires time to process on the WLAN firmware and
416 * the following IOCTL may fail on initial attempts therefore we try a few times */
417
418 /* Set the event mask, indicating initially we do not want any asynchronous events */
419 for (counter = 0, retval = WHD_PENDING; retval != WHD_SUCCESS && counter < (uint32_t)MAX_POST_SET_COUNTRY_RETRY;
420 ++counter)
421 {
422 event_mask = (uint8_t *)whd_cdc_get_iovar_buffer(whd_driver, &buffer, (uint16_t)WL_EVENTING_MASK_LEN,
423 IOVAR_STR_EVENT_MSGS);
424 if (event_mask == NULL)
425 {
426 whd_assert("Could not get buffer for IOVAR", 0 != 0);
427 return WHD_BUFFER_ALLOC_FAIL;
428 }
429 memset(event_mask, 0, (size_t)WL_EVENTING_MASK_LEN);
430 retval = whd_cdc_send_iovar(ifp, CDC_SET, buffer, 0);
431 }
432 if (retval != WHD_SUCCESS)
433 {
434 WPRINT_WHD_ERROR( ("Could not set Event mask\n") );
435 return retval;
436 }
437
438 /* Send UP command */
439 CHECK_RETURN(whd_wifi_set_up(ifp) );
440
441 wlan_chip_id = whd_chip_get_chip_id(whd_driver);
442 /* WAR: Disable WLAN PM/mpc for 43907 low power issue */
443 if ( (wlan_chip_id == 43909) || (wlan_chip_id == 43907) || (wlan_chip_id == 54907) )
444 {
445 retval = whd_wifi_disable_powersave(ifp);
446 if (retval != WHD_SUCCESS)
447 {
448 WPRINT_WHD_ERROR( ("Failed to disable PM for 43907\n") );
449 return retval;
450 }
451 retval = whd_wifi_set_iovar_value(ifp, IOVAR_STR_MPC, 0);
452 if (retval != WHD_SUCCESS)
453 {
454 WPRINT_WHD_ERROR( ("Failed to disable mpc for 43907\n") );
455 return retval;
456 }
457 }
458 else
459 {
460 whd_wifi_enable_powersave_with_throughput(ifp, DEFAULT_PM2_SLEEP_RET_TIME);
461 }
462
463 /* Set the GMode */
464 data = (uint32_t *)whd_cdc_get_ioctl_buffer(whd_driver, &buffer, (uint16_t)4);
465 if (data == NULL)
466 {
467 whd_assert("Could not get buffer for IOCTL", 0 != 0);
468 return WHD_BUFFER_ALLOC_FAIL;
469 }
470 *data = htod32( (uint32_t)GMODE_AUTO );
471 retval = whd_cdc_send_ioctl(ifp, CDC_SET, WLC_SET_GMODE, buffer, 0);
472 if (retval != WHD_SUCCESS)
473 {
474 /* Note: System may time out here if bus interrupts are not working properly */
475 WPRINT_WHD_ERROR( ("Error setting gmode\n") );
476 return retval;
477 }
478
479 return WHD_SUCCESS;
480 }
481
482 /**
483 * Turn off the Wi-Fi device
484 *
485 * - De-Initialises the required parts of the hardware platform
486 * i.e. pins for SDIO/SPI, interrupt, reset, power etc.
487 *
488 * - De-Initialises the WHD thread which arbitrates access
489 * to the SDIO/SPI bus
490 *
491 * @return WHD_SUCCESS if deinitialization is successful, error code otherwise
492 */
whd_wifi_off(whd_interface_t ifp)493 uint32_t whd_wifi_off(whd_interface_t ifp)
494 {
495 whd_result_t retval;
496 whd_driver_t whd_driver;
497
498 CHECK_IFP_NULL(ifp);
499
500 whd_driver = ifp->whd_driver;
501 if (whd_driver->internal_info.whd_wlan_status.state == WLAN_OFF)
502 {
503 return WHD_SUCCESS;
504 }
505
506 /* Set wlc down before turning off the device */
507 CHECK_RETURN(whd_wifi_set_ioctl_buffer(ifp, WLC_DOWN, NULL, 0) );
508 whd_driver->internal_info.whd_wlan_status.state = WLAN_DOWN;
509
510 /* Disable SDIO/SPI interrupt */
511 whd_bus_irq_enable(whd_driver, WHD_FALSE);
512 whd_thread_quit(whd_driver);
513
514 retval = whd_bus_deinit(whd_driver);
515 if (retval != WHD_SUCCESS)
516 {
517 WPRINT_WHD_DEBUG( ("Error de-initializing bus\n") );
518 return retval;
519 }
520
521 whd_driver->internal_info.whd_wlan_status.state = WLAN_OFF;
522 return WHD_SUCCESS;
523 }
524
525