BlueSCSI_platform_network.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /*
  2. * Copyright (c) 2023 joshua stein <jcs@jcs.org>
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #ifdef BLUESCSI_NETWORK
  17. #include <Arduino.h>
  18. #include "BlueSCSI_platform_network.h"
  19. #include "BlueSCSI_log.h"
  20. #include "BlueSCSI_config.h"
  21. #include <scsi.h>
  22. #include <network.h>
  23. extern "C" {
  24. #include <cyw43.h>
  25. #include <pico/cyw43_arch.h>
  26. #include <pico/unique_id.h>
  27. #ifndef CYW43_IOCTL_GET_RSSI
  28. #define CYW43_IOCTL_GET_RSSI (0xfe)
  29. #endif
  30. #define PICO_W_GPIO_LED_PIN 0
  31. #define PICO_W_LED_ON() cyw43_arch_gpio_put(PICO_W_GPIO_LED_PIN, 1)
  32. #define PICO_W_LED_OFF() cyw43_arch_gpio_put(PICO_W_GPIO_LED_PIN, 0)
  33. #define PICO_W_LONG_BLINK_DELAY 200
  34. #define PICO_W_SHORT_BLINK_DELAY 75
  35. // A default DaynaPort-compatible MAC
  36. static const char defaultMAC[] = { 0x00, 0x80, 0x19, 0xc0, 0xff, 0xee };
  37. static bool network_in_use = false;
  38. bool platform_network_supported()
  39. {
  40. /* from cores/rp2040/RP2040Support.h */
  41. #if !defined(PICO_CYW43_SUPPORTED)
  42. return false;
  43. #elif defined(BLUESCSI_BLASTER)
  44. return true;
  45. #else
  46. extern bool __isPicoW;
  47. return __isPicoW;
  48. #endif
  49. }
  50. int platform_network_init(char *mac)
  51. {
  52. pico_unique_board_id_t board_id;
  53. uint8_t set_mac[6], read_mac[6];
  54. if (!platform_network_supported())
  55. return -1;
  56. // long signal blink at network initialization
  57. PICO_W_LED_OFF();
  58. PICO_W_LED_ON();
  59. delay(PICO_W_LONG_BLINK_DELAY);
  60. PICO_W_LED_OFF();
  61. logmsg(" ");
  62. logmsg("=== Network Initialization ===");
  63. memset(wifi_network_list, 0, sizeof(wifi_network_list));
  64. cyw43_deinit(&cyw43_state);
  65. cyw43_init(&cyw43_state);
  66. if (mac == NULL || (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 && mac[4] == 0 && mac[5] == 0))
  67. {
  68. mac = (char *)&set_mac;
  69. // char octal_strings[8][4] = {0};
  70. memcpy(mac, defaultMAC, sizeof(set_mac));
  71. // retain Dayna vendor but use a device id specific to this board
  72. pico_get_unique_board_id(&board_id);
  73. dbgmsg("Unique board id: ", board_id.id[0], " ", board_id.id[1], " ", board_id.id[2], " ", board_id.id[3], " ",
  74. board_id.id[4], " ", board_id.id[5], " ", board_id.id[6], " ", board_id.id[7]);
  75. if (board_id.id[3] != 0 && board_id.id[4] != 0 && board_id.id[5] != 0)
  76. {
  77. mac[3] = board_id.id[3];
  78. mac[4] = board_id.id[4];
  79. mac[5] = board_id.id[5];
  80. }
  81. memcpy(scsiDev.boardCfg.wifiMACAddress, mac, sizeof(scsiDev.boardCfg.wifiMACAddress));
  82. }
  83. // setting the MAC requires libpico to be compiled with CYW43_USE_OTP_MAC=0
  84. memcpy(cyw43_state.mac, mac, sizeof(cyw43_state.mac));
  85. cyw43_arch_enable_sta_mode();
  86. cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, read_mac);
  87. char mac_hex_string[4*6] = {0};
  88. sprintf(mac_hex_string, "%02X:%02X:%02X:%02X:%02X:%02X", read_mac[0], read_mac[1], read_mac[2], read_mac[3], read_mac[4], read_mac[5]);
  89. logmsg("Wi-Fi MAC: ", mac_hex_string);
  90. if (memcmp(mac, read_mac, sizeof(read_mac)) != 0)
  91. logmsg("WARNING: Wi-Fi MAC is not what was requested (", mac_hex_string,"), is libpico not compiled with CYW43_USE_OTP_MAC=0?");
  92. network_in_use = true;
  93. return 0;
  94. }
  95. void platform_network_add_multicast_address(uint8_t *mac)
  96. {
  97. int ret;
  98. if ((ret = cyw43_wifi_update_multicast_filter(&cyw43_state, mac, true)) != 0)
  99. logmsg( __func__, ": cyw43_wifi_update_multicast_filter: ", ret);
  100. }
  101. bool platform_network_wifi_join(char *ssid, char *password)
  102. {
  103. int ret;
  104. if (!platform_network_supported())
  105. return false;
  106. if (password == NULL || password[0] == 0)
  107. {
  108. logmsg("Connecting to Wi-Fi SSID \"", ssid, "\" with no authentication");
  109. ret = cyw43_arch_wifi_connect_async(ssid, NULL, CYW43_AUTH_OPEN);
  110. }
  111. else
  112. {
  113. logmsg("Connecting to Wi-Fi SSID \"", ssid, "\" with WPA/WPA2 PSK");
  114. ret = cyw43_arch_wifi_connect_async(ssid, password, CYW43_AUTH_WPA2_MIXED_PSK);
  115. }
  116. if (ret == 0)
  117. {
  118. // Short single blink at start of connection sequence
  119. PICO_W_LED_OFF();
  120. delay(PICO_W_SHORT_BLINK_DELAY);
  121. PICO_W_LED_ON();
  122. delay(PICO_W_SHORT_BLINK_DELAY);
  123. PICO_W_LED_OFF();
  124. }
  125. else
  126. {
  127. logmsg("Error occurred starting the Wi-Fi interface to SSID ", ssid);
  128. }
  129. return (ret == 0);
  130. }
  131. void platform_network_poll()
  132. {
  133. static int last_network_status = CYW43_LINK_DOWN;
  134. if (!network_in_use)
  135. return;
  136. int status = cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA);
  137. char * ssid = scsiDev.boardCfg.wifiSSID;
  138. if ((last_network_status != status) && (status == CYW43_LINK_BADAUTH || status == CYW43_LINK_NONET || status == CYW43_LINK_FAIL || status == CYW43_LINK_NOIP))
  139. {
  140. switch (status)
  141. {
  142. case CYW43_LINK_NOIP:
  143. logmsg("WiFi connected to ", ssid, " but was not assigned in an IP");
  144. break;
  145. case CYW43_LINK_BADAUTH:
  146. logmsg("Wi-Fi authentication failure connecting to \"", ssid, "\", please check the setting \"WiFiPassword\" in ", CONFIGFILE);
  147. break;
  148. case CYW43_LINK_NONET:
  149. logmsg("Wi-Fi SSID ", ssid, " not found, possible of out range or down");
  150. break;
  151. case CYW43_LINK_FAIL:
  152. logmsg("WiFi connection to ", ssid, " failed");
  153. break;
  154. }
  155. last_network_status = status;
  156. }
  157. scsiNetworkPurge();
  158. cyw43_arch_poll();
  159. }
  160. int platform_network_send(uint8_t *buf, size_t len)
  161. {
  162. int ret = cyw43_send_ethernet(&cyw43_state, 0, len, buf, 0);
  163. if (ret != 0)
  164. logmsg("cyw43_send_ethernet failed: ", ret);
  165. return ret;
  166. }
  167. bool platform_network_iface_check()
  168. {
  169. cyw43_deinit(&cyw43_state);
  170. cyw43_init(&cyw43_state);
  171. return 0 == cyw43_gpio_set(&cyw43_state, PICO_W_GPIO_LED_PIN, 1);
  172. }
  173. void platform_network_deinit()
  174. {
  175. cyw43_init(&cyw43_state);
  176. cyw43_gpio_set(&cyw43_state, PICO_W_GPIO_LED_PIN, 0);
  177. cyw43_deinit(&cyw43_state);
  178. }
  179. static int platform_network_wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result)
  180. {
  181. struct wifi_network_entry *entry = NULL;
  182. if (!result || !result->ssid_len || !result->ssid[0])
  183. return 0;
  184. for (int i = 0; i < WIFI_NETWORK_LIST_ENTRY_COUNT; i++)
  185. {
  186. // take first available
  187. if (wifi_network_list[i].ssid[0] == '\0')
  188. {
  189. entry = &wifi_network_list[i];
  190. break;
  191. }
  192. // or if we've seen this network before, use this slot
  193. else if (strcmp((char *)result->ssid, wifi_network_list[i].ssid) == 0)
  194. {
  195. entry = &wifi_network_list[i];
  196. break;
  197. }
  198. }
  199. if (!entry)
  200. {
  201. // no available slots, insert according to our RSSI
  202. for (int i = 0; i < WIFI_NETWORK_LIST_ENTRY_COUNT; i++)
  203. {
  204. if (result->rssi > wifi_network_list[i].rssi)
  205. {
  206. // shift everything else down
  207. for (int j = WIFI_NETWORK_LIST_ENTRY_COUNT - 1; j > i; j--)
  208. wifi_network_list[j] = wifi_network_list[j - 1];
  209. entry = &wifi_network_list[i];
  210. memset(entry, 0, sizeof(struct wifi_network_entry));
  211. break;
  212. }
  213. }
  214. }
  215. if (entry == NULL)
  216. return 0;
  217. if (entry->rssi == 0 || result->rssi > entry->rssi)
  218. {
  219. entry->channel = result->channel;
  220. entry->rssi = result->rssi;
  221. }
  222. if (result->auth_mode & 7)
  223. entry->flags = WIFI_NETWORK_FLAG_AUTH;
  224. strncpy(entry->ssid, (const char *)result->ssid, sizeof(entry->ssid));
  225. entry->ssid[sizeof(entry->ssid) - 1] = '\0';
  226. memcpy(entry->bssid, result->bssid, sizeof(entry->bssid));
  227. return 0;
  228. }
  229. int platform_network_wifi_start_scan()
  230. {
  231. if (cyw43_wifi_scan_active(&cyw43_state))
  232. return -1;
  233. cyw43_wifi_scan_options_t scan_options = { 0 };
  234. memset(wifi_network_list, 0, sizeof(wifi_network_list));
  235. return cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, platform_network_wifi_scan_result);
  236. }
  237. int platform_network_wifi_scan_finished()
  238. {
  239. return !cyw43_wifi_scan_active(&cyw43_state);
  240. }
  241. void platform_network_wifi_dump_scan_list()
  242. {
  243. struct wifi_network_entry *entry = NULL;
  244. for (int i = 0; i < WIFI_NETWORK_LIST_ENTRY_COUNT; i++)
  245. {
  246. entry = &wifi_network_list[i];
  247. if (entry->ssid[0] == '\0')
  248. break;
  249. logmsg("wifi[",i,"] = ",entry->ssid,", channel ",(int)entry->channel,", rssi ",(int)entry->rssi,
  250. ", bssid ",(uint8_t) entry->bssid[0],":",(uint8_t) entry->bssid[1],":",(uint8_t) entry->bssid[2],":",
  251. (uint8_t) entry->bssid[3],":",(uint8_t) entry->bssid[4],":",(uint8_t) entry->bssid[5],", flags ", entry->flags);
  252. }
  253. }
  254. int platform_network_wifi_rssi()
  255. {
  256. int32_t rssi = 0;
  257. cyw43_ioctl(&cyw43_state, CYW43_IOCTL_GET_RSSI, sizeof(rssi), (uint8_t *)&rssi, CYW43_ITF_STA);
  258. return rssi;
  259. }
  260. char * platform_network_wifi_ssid()
  261. {
  262. struct ssid_t {
  263. uint32_t ssid_len;
  264. uint8_t ssid[32 + 1];
  265. } ssid;
  266. static char cur_ssid[32 + 1];
  267. memset(cur_ssid, 0, sizeof(cur_ssid));
  268. int ret = cyw43_ioctl(&cyw43_state, CYW43_IOCTL_GET_SSID, sizeof(ssid), (uint8_t *)&ssid, CYW43_ITF_STA);
  269. if (ret)
  270. {
  271. logmsg("Failed getting Wi-Fi SSID: ", ret);
  272. return NULL;
  273. }
  274. ssid.ssid[sizeof(ssid.ssid) - 1] = '\0';
  275. if (ssid.ssid_len < sizeof(ssid.ssid))
  276. ssid.ssid[ssid.ssid_len] = '\0';
  277. strlcpy(cur_ssid, (char *)ssid.ssid, sizeof(cur_ssid));
  278. return cur_ssid;
  279. }
  280. char * platform_network_wifi_bssid()
  281. {
  282. static char bssid[6];
  283. memset(bssid, 0, sizeof(bssid));
  284. /* TODO */
  285. return bssid;
  286. }
  287. int platform_network_wifi_channel()
  288. {
  289. int32_t channel = 0;
  290. cyw43_ioctl(&cyw43_state, CYW43_IOCTL_GET_CHANNEL, sizeof(channel), (uint8_t *)&channel, CYW43_ITF_STA);
  291. return channel;
  292. }
  293. // these override weakly-defined functions in pico-sdk
  294. void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf)
  295. {
  296. scsiNetworkEnqueue(buf, len);
  297. }
  298. void cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf)
  299. {
  300. logmsg("Disassociated from Wi-Fi SSID \"", (char *)self->ap_ssid,"\"");
  301. }
  302. void cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf)
  303. {
  304. char *ssid = platform_network_wifi_ssid();
  305. if (ssid)
  306. {
  307. logmsg("Successfully connected to Wi-Fi SSID \"",ssid,"\"");
  308. // blink LED 3 times when connected
  309. PICO_W_LED_OFF();
  310. for (uint8_t i = 0; i < 3; i++)
  311. {
  312. delay(PICO_W_SHORT_BLINK_DELAY);
  313. PICO_W_LED_ON();
  314. delay(PICO_W_SHORT_BLINK_DELAY);
  315. PICO_W_LED_OFF();
  316. }
  317. }
  318. }
  319. }
  320. #else
  321. bool platform_network_supported() { return false; }
  322. #endif // BLUESCSI_NETWORK