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()31 void cyw43_arch_enable_sta_mode() {
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_enable_ap_mode(const char * ssid,const char * password,uint32_t auth)36 void cyw43_arch_enable_ap_mode(const char *ssid, const char *password, uint32_t auth) {
37     assert(cyw43_is_initialized(&cyw43_state));
38     cyw43_wifi_ap_set_ssid(&cyw43_state, strlen(ssid), (const uint8_t *) ssid);
39     if (password) {
40         cyw43_wifi_ap_set_password(&cyw43_state, strlen(password), (const uint8_t *) password);
41         cyw43_wifi_ap_set_auth(&cyw43_state, auth);
42     } else {
43         cyw43_wifi_ap_set_auth(&cyw43_state, CYW43_AUTH_OPEN);
44     }
45     cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_AP, true, cyw43_arch_get_country_code());
46 }
47 
48 #if PICO_CYW43_ARCH_DEBUG_ENABLED
49 // Return a string for the wireless state
cyw43_tcpip_link_status_name(int status)50 static const char* cyw43_tcpip_link_status_name(int status)
51 {
52     switch (status) {
53     case CYW43_LINK_DOWN:
54         return "link down";
55     case CYW43_LINK_JOIN:
56         return "joining";
57     case CYW43_LINK_NOIP:
58         return "no ip";
59     case CYW43_LINK_UP:
60         return "link up";
61     case CYW43_LINK_FAIL:
62         return "link fail";
63     case CYW43_LINK_NONET:
64         return "network fail";
65     case CYW43_LINK_BADAUTH:
66         return "bad auth";
67     }
68     return "unknown";
69 }
70 #endif
71 
72 
cyw43_arch_wifi_connect_bssid_async(const char * ssid,const uint8_t * bssid,const char * pw,uint32_t auth)73 int cyw43_arch_wifi_connect_bssid_async(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth) {
74     if (!pw) auth = CYW43_AUTH_OPEN;
75     // Connect to wireless
76     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);
77 }
78 
cyw43_arch_wifi_connect_async(const char * ssid,const char * pw,uint32_t auth)79 int cyw43_arch_wifi_connect_async(const char *ssid, const char *pw, uint32_t auth) {
80     return cyw43_arch_wifi_connect_bssid_async(ssid, NULL, pw, auth);
81 }
82 
cyw43_arch_wifi_connect_bssid_until(const char * ssid,const uint8_t * bssid,const char * pw,uint32_t auth,absolute_time_t until)83 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) {
84     int err = cyw43_arch_wifi_connect_bssid_async(ssid, bssid, pw, auth);
85     if (err) return err;
86 
87     int status = CYW43_LINK_UP + 1;
88     while(status >= 0 && status != CYW43_LINK_UP) {
89         int new_status = cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA);
90         // If there was no network, keep trying
91         if (new_status == CYW43_LINK_NONET) {
92             new_status = CYW43_LINK_JOIN;
93             err = cyw43_arch_wifi_connect_bssid_async(ssid, bssid, pw, auth);
94             if (err) return err;
95         }
96         if (new_status != status) {
97             status = new_status;
98             CYW43_ARCH_DEBUG("connect status: %s\n", cyw43_tcpip_link_status_name(status));
99         }
100         if (time_reached(until)) {
101             return PICO_ERROR_TIMEOUT;
102         }
103         // Do polling
104         cyw43_arch_poll();
105         cyw43_arch_wait_for_work_until(until);
106     }
107     // Turn status into a pico_error_codes, CYW43_LINK_NONET shouldn't happen as we fail with PICO_ERROR_TIMEOUT instead
108     assert(status == CYW43_LINK_UP || status == CYW43_LINK_BADAUTH || status == CYW43_LINK_FAIL);
109     if (status == CYW43_LINK_UP) {
110         return PICO_OK; // success
111     } else if (status == CYW43_LINK_BADAUTH) {
112         return PICO_ERROR_BADAUTH;
113     } else {
114         return PICO_ERROR_CONNECT_FAILED;
115     }
116 }
117 
118 // 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)119 static int cyw43_arch_wifi_connect_until(const char *ssid, const char *pw, uint32_t auth, absolute_time_t until) {
120     return cyw43_arch_wifi_connect_bssid_until(ssid, NULL, pw, auth, until);
121 }
122 
cyw43_arch_wifi_connect_blocking(const char * ssid,const char * pw,uint32_t auth)123 int cyw43_arch_wifi_connect_blocking(const char *ssid, const char *pw, uint32_t auth) {
124     return cyw43_arch_wifi_connect_until(ssid, pw, auth, at_the_end_of_time);
125 }
126 
cyw43_arch_wifi_connect_bssid_blocking(const char * ssid,const uint8_t * bssid,const char * pw,uint32_t auth)127 int cyw43_arch_wifi_connect_bssid_blocking(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth) {
128     return cyw43_arch_wifi_connect_bssid_until(ssid, bssid, pw, auth, at_the_end_of_time);
129 }
130 
cyw43_arch_wifi_connect_timeout_ms(const char * ssid,const char * pw,uint32_t auth,uint32_t timeout_ms)131 int cyw43_arch_wifi_connect_timeout_ms(const char *ssid, const char *pw, uint32_t auth, uint32_t timeout_ms) {
132     return cyw43_arch_wifi_connect_until(ssid, pw, auth, make_timeout_time_ms(timeout_ms));
133 }
134 
cyw43_arch_wifi_connect_bssid_timeout_ms(const char * ssid,const uint8_t * bssid,const char * pw,uint32_t auth,uint32_t timeout_ms)135 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) {
136     return cyw43_arch_wifi_connect_bssid_until(ssid, bssid, pw, auth, make_timeout_time_ms(timeout_ms));
137 }
138 
cyw43_arch_get_country_code(void)139 uint32_t cyw43_arch_get_country_code(void) {
140     return country_code;
141 }
142 
cyw43_arch_init_with_country(uint32_t country)143 int cyw43_arch_init_with_country(uint32_t country) {
144     country_code = country;
145     return cyw43_arch_init();
146 }
147 
cyw43_arch_gpio_put(uint wl_gpio,bool value)148 void cyw43_arch_gpio_put(uint wl_gpio, bool value) {
149     invalid_params_if(CYW43_ARCH, wl_gpio >= CYW43_WL_GPIO_COUNT);
150     cyw43_gpio_set(&cyw43_state, (int)wl_gpio, value);
151 }
152 
cyw43_arch_gpio_get(uint wl_gpio)153 bool cyw43_arch_gpio_get(uint wl_gpio) {
154     invalid_params_if(CYW43_ARCH, wl_gpio >= CYW43_WL_GPIO_COUNT);
155     bool value = false;
156     cyw43_gpio_get(&cyw43_state, (int)wl_gpio, &value);
157     return value;
158 }
159 
cyw43_arch_async_context(void)160 async_context_t *cyw43_arch_async_context(void) {
161     return async_context;
162 }
163 
cyw43_arch_poll(void)164 void cyw43_arch_poll(void)
165 {
166     async_context_poll(async_context);
167 }
168 
cyw43_arch_wait_for_work_until(absolute_time_t until)169 void cyw43_arch_wait_for_work_until(absolute_time_t until) {
170     async_context_wait_for_work_until(async_context, until);
171 }
172