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