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