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