1 /*
2  * Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 
11 #include "pico/unique_id.h"
12 #include "cyw43.h"
13 #include "pico/cyw43_arch.h"
14 #include "cyw43_ll.h"
15 #include "cyw43_stats.h"
16 
17 #if PICO_CYW43_ARCH_DEBUG_ENABLED
18 #define CYW43_ARCH_DEBUG(...) printf(__VA_ARGS__)
19 #else
20 #define CYW43_ARCH_DEBUG(...) ((void)0)
21 #endif
22 
23 static uint32_t country_code = PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE;
24 
25 static async_context_t *async_context;
26 
cyw43_arch_set_async_context(async_context_t * context)27 void cyw43_arch_set_async_context(async_context_t *context) {
28     async_context = context;
29 }
30 
cyw43_arch_enable_sta_mode(void)31 void cyw43_arch_enable_sta_mode(void) {
32     assert(cyw43_is_initialized(&cyw43_state));
33     cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_STA, true, cyw43_arch_get_country_code());
34 }
35 
cyw43_arch_disable_sta_mode(void)36 void cyw43_arch_disable_sta_mode(void) {
37     assert(cyw43_is_initialized(&cyw43_state));
38     if (cyw43_state.itf_state & (1 << CYW43_ITF_STA)) {
39         cyw43_cb_tcpip_deinit(&cyw43_state, CYW43_ITF_STA);
40         cyw43_state.itf_state &= ~(1 << CYW43_ITF_STA);
41     }
42     if (cyw43_state.wifi_join_state) {
43         cyw43_wifi_leave(&cyw43_state, CYW43_ITF_STA);
44     }
45 }
46 
cyw43_arch_enable_ap_mode(const char * ssid,const char * password,uint32_t auth)47 void cyw43_arch_enable_ap_mode(const char *ssid, const char *password, uint32_t auth) {
48     assert(cyw43_is_initialized(&cyw43_state));
49     cyw43_wifi_ap_set_ssid(&cyw43_state, strlen(ssid), (const uint8_t *) ssid);
50     if (password) {
51         cyw43_wifi_ap_set_password(&cyw43_state, strlen(password), (const uint8_t *) password);
52         cyw43_wifi_ap_set_auth(&cyw43_state, auth);
53     } else {
54         cyw43_wifi_ap_set_auth(&cyw43_state, CYW43_AUTH_OPEN);
55     }
56     cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_AP, true, cyw43_arch_get_country_code());
57 }
58 
cyw43_arch_disable_ap_mode(void)59 void cyw43_arch_disable_ap_mode(void) {
60     assert(cyw43_is_initialized(&cyw43_state));
61     cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_AP, false, cyw43_arch_get_country_code());
62     cyw43_state.itf_state &= ~(1 << CYW43_ITF_AP);
63 }
64 
65 #if PICO_CYW43_ARCH_DEBUG_ENABLED
66 // Return a string for the wireless state
cyw43_tcpip_link_status_name(int status)67 static const char* cyw43_tcpip_link_status_name(int status)
68 {
69     switch (status) {
70     case CYW43_LINK_DOWN:
71         return "link down";
72     case CYW43_LINK_JOIN:
73         return "joining";
74     case CYW43_LINK_NOIP:
75         return "no ip";
76     case CYW43_LINK_UP:
77         return "link up";
78     case CYW43_LINK_FAIL:
79         return "link fail";
80     case CYW43_LINK_NONET:
81         return "network fail";
82     case CYW43_LINK_BADAUTH:
83         return "bad auth";
84     }
85     return "unknown";
86 }
87 #endif
88 
89 
cyw43_arch_wifi_connect_bssid_async(const char * ssid,const uint8_t * bssid,const char * pw,uint32_t auth)90 int cyw43_arch_wifi_connect_bssid_async(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth) {
91     if (!pw) auth = CYW43_AUTH_OPEN;
92     // Connect to wireless
93     return cyw43_wifi_join(&cyw43_state, strlen(ssid), (const uint8_t *)ssid, pw ? strlen(pw) : 0, (const uint8_t *)pw, auth, bssid, CYW43_CHANNEL_NONE);
94 }
95 
cyw43_arch_wifi_connect_async(const char * ssid,const char * pw,uint32_t auth)96 int cyw43_arch_wifi_connect_async(const char *ssid, const char *pw, uint32_t auth) {
97     return cyw43_arch_wifi_connect_bssid_async(ssid, NULL, pw, auth);
98 }
99 
cyw43_arch_wifi_connect_bssid_until(const char * ssid,const uint8_t * bssid,const char * pw,uint32_t auth,absolute_time_t until)100 static int cyw43_arch_wifi_connect_bssid_until(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth, absolute_time_t until) {
101     int err = cyw43_arch_wifi_connect_bssid_async(ssid, bssid, pw, auth);
102     if (err) return err;
103 
104     int status = CYW43_LINK_UP + 1;
105     while(status >= 0 && status != CYW43_LINK_UP) {
106         int new_status = cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA);
107         // If there was no network, keep trying
108         if (new_status == CYW43_LINK_NONET) {
109             new_status = CYW43_LINK_JOIN;
110             err = cyw43_arch_wifi_connect_bssid_async(ssid, bssid, pw, auth);
111             if (err) return err;
112         }
113         if (new_status != status) {
114             status = new_status;
115             CYW43_ARCH_DEBUG("connect status: %s\n", cyw43_tcpip_link_status_name(status));
116         }
117         if (time_reached(until)) {
118             return PICO_ERROR_TIMEOUT;
119         }
120         // Do polling
121         cyw43_arch_poll();
122         cyw43_arch_wait_for_work_until(until);
123     }
124     // Turn status into a pico_error_codes, CYW43_LINK_NONET shouldn't happen as we fail with PICO_ERROR_TIMEOUT instead
125     assert(status == CYW43_LINK_UP || status == CYW43_LINK_BADAUTH || status == CYW43_LINK_FAIL);
126     if (status == CYW43_LINK_UP) {
127         return PICO_OK; // success
128     } else if (status == CYW43_LINK_BADAUTH) {
129         return PICO_ERROR_BADAUTH;
130     } else {
131         return PICO_ERROR_CONNECT_FAILED;
132     }
133 }
134 
135 // Connect to wireless, return with success when an IP address has been assigned
cyw43_arch_wifi_connect_until(const char * ssid,const char * pw,uint32_t auth,absolute_time_t until)136 static int cyw43_arch_wifi_connect_until(const char *ssid, const char *pw, uint32_t auth, absolute_time_t until) {
137     return cyw43_arch_wifi_connect_bssid_until(ssid, NULL, pw, auth, until);
138 }
139 
cyw43_arch_wifi_connect_blocking(const char * ssid,const char * pw,uint32_t auth)140 int cyw43_arch_wifi_connect_blocking(const char *ssid, const char *pw, uint32_t auth) {
141     return cyw43_arch_wifi_connect_until(ssid, pw, auth, at_the_end_of_time);
142 }
143 
cyw43_arch_wifi_connect_bssid_blocking(const char * ssid,const uint8_t * bssid,const char * pw,uint32_t auth)144 int cyw43_arch_wifi_connect_bssid_blocking(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth) {
145     return cyw43_arch_wifi_connect_bssid_until(ssid, bssid, pw, auth, at_the_end_of_time);
146 }
147 
cyw43_arch_wifi_connect_timeout_ms(const char * ssid,const char * pw,uint32_t auth,uint32_t timeout_ms)148 int cyw43_arch_wifi_connect_timeout_ms(const char *ssid, const char *pw, uint32_t auth, uint32_t timeout_ms) {
149     return cyw43_arch_wifi_connect_until(ssid, pw, auth, make_timeout_time_ms(timeout_ms));
150 }
151 
cyw43_arch_wifi_connect_bssid_timeout_ms(const char * ssid,const uint8_t * bssid,const char * pw,uint32_t auth,uint32_t timeout_ms)152 int cyw43_arch_wifi_connect_bssid_timeout_ms(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth, uint32_t timeout_ms) {
153     return cyw43_arch_wifi_connect_bssid_until(ssid, bssid, pw, auth, make_timeout_time_ms(timeout_ms));
154 }
155 
cyw43_arch_get_country_code(void)156 uint32_t cyw43_arch_get_country_code(void) {
157     return country_code;
158 }
159 
cyw43_arch_init_with_country(uint32_t country)160 int cyw43_arch_init_with_country(uint32_t country) {
161     country_code = country;
162     return cyw43_arch_init();
163 }
164 
cyw43_arch_gpio_put(uint wl_gpio,bool value)165 void cyw43_arch_gpio_put(uint wl_gpio, bool value) {
166     invalid_params_if(PICO_CYW43_ARCH, wl_gpio >= CYW43_WL_GPIO_COUNT);
167     cyw43_gpio_set(&cyw43_state, (int)wl_gpio, value);
168 }
169 
cyw43_arch_gpio_get(uint wl_gpio)170 bool cyw43_arch_gpio_get(uint wl_gpio) {
171     invalid_params_if(PICO_CYW43_ARCH, wl_gpio >= CYW43_WL_GPIO_COUNT);
172     bool value = false;
173     cyw43_gpio_get(&cyw43_state, (int)wl_gpio, &value);
174     return value;
175 }
176 
cyw43_arch_async_context(void)177 async_context_t *cyw43_arch_async_context(void) {
178     return async_context;
179 }
180 
cyw43_arch_poll(void)181 void cyw43_arch_poll(void)
182 {
183     async_context_poll(async_context);
184 }
185 
cyw43_arch_wait_for_work_until(absolute_time_t until)186 void cyw43_arch_wait_for_work_until(absolute_time_t until) {
187     async_context_wait_for_work_until(async_context, until);
188 }
189