|
@@ -1,1415 +0,0 @@
|
|
|
-#include "cyw.h"
|
|
|
|
|
-#include "net.h"
|
|
|
|
|
-
|
|
|
|
|
-#if MG_ENABLE_TCPIP && \
|
|
|
|
|
- ((defined(MG_ENABLE_DRIVER_CYW) && MG_ENABLE_DRIVER_CYW) || \
|
|
|
|
|
- (defined(MG_ENABLE_DRIVER_CYW_SDIO) && MG_ENABLE_DRIVER_CYW_SDIO))
|
|
|
|
|
-
|
|
|
|
|
-#ifndef MG_ENABLE_DRIVER_CYW
|
|
|
|
|
-#define MG_ENABLE_DRIVER_CYW 0
|
|
|
|
|
-#endif
|
|
|
|
|
-#ifndef MG_ENABLE_DRIVER_CYW_SDIO
|
|
|
|
|
-#define MG_ENABLE_DRIVER_CYW_SDIO 0
|
|
|
|
|
-#endif
|
|
|
|
|
-
|
|
|
|
|
-static struct mg_tcpip_if *s_ifp;
|
|
|
|
|
-static uint32_t s_ip, s_mask;
|
|
|
|
|
-static bool s_link, s_auth, s_join;
|
|
|
|
|
-
|
|
|
|
|
-static void wifi_cb(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
|
|
|
|
|
- struct mg_wifi_data *wifi = &((struct mg_tcpip_driver_cyw_data *) ifp->driver_data)->wifi;
|
|
|
|
|
- if (wifi->apmode && ev == MG_TCPIP_EV_STATE_CHANGE && *(uint8_t *) ev_data == MG_TCPIP_STATE_UP) {
|
|
|
|
|
- MG_DEBUG(("Access Point started"));
|
|
|
|
|
- s_ip = ifp->ip, ifp->ip = wifi->apip;
|
|
|
|
|
- s_mask = ifp->mask, ifp->mask = wifi->apmask;
|
|
|
|
|
- ifp->enable_dhcp_client = false;
|
|
|
|
|
- ifp->enable_dhcp_server = true;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_init(uint8_t *mac);
|
|
|
|
|
-static void cyw_poll(void);
|
|
|
|
|
-
|
|
|
|
|
-static bool mg_tcpip_driver_cyw_init(struct mg_tcpip_if *ifp) {
|
|
|
|
|
- struct mg_tcpip_driver_cyw_data *d =
|
|
|
|
|
- (struct mg_tcpip_driver_cyw_data *) ifp->driver_data;
|
|
|
|
|
- struct mg_wifi_data *wifi = &d->wifi;
|
|
|
|
|
- if (MG_BIG_ENDIAN) {
|
|
|
|
|
- MG_ERROR(("Big-endian host"));
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- s_ifp = ifp;
|
|
|
|
|
- s_ip = ifp->ip;
|
|
|
|
|
- s_mask = ifp->mask;
|
|
|
|
|
- s_link = s_auth = s_join = false;
|
|
|
|
|
- ifp->pfn = wifi_cb;
|
|
|
|
|
- if (!cyw_init(ifp->mac)) return false;
|
|
|
|
|
-
|
|
|
|
|
- if (wifi->apmode) {
|
|
|
|
|
- return mg_wifi_ap_start(wifi);
|
|
|
|
|
- } else if (wifi->ssid != NULL && wifi->pass != NULL) {
|
|
|
|
|
- return mg_wifi_connect(wifi);
|
|
|
|
|
- }
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static size_t mg_cyw_tx(unsigned int ifc, void *data, size_t len);
|
|
|
|
|
-size_t mg_tcpip_driver_cyw_output(const void *buf, size_t len,
|
|
|
|
|
- struct mg_tcpip_if *ifp) {
|
|
|
|
|
- struct mg_tcpip_driver_cyw_data *d =
|
|
|
|
|
- (struct mg_tcpip_driver_cyw_data *) ifp->driver_data;
|
|
|
|
|
- return mg_cyw_tx(d->wifi.apmode ? 1 : 0, (void *) buf, len) >= len ? len : 0;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static bool mg_tcpip_driver_cyw_poll(struct mg_tcpip_if *ifp, bool s1) {
|
|
|
|
|
- cyw_poll();
|
|
|
|
|
- if (!s1) return false;
|
|
|
|
|
- struct mg_tcpip_driver_cyw_data *d =
|
|
|
|
|
- (struct mg_tcpip_driver_cyw_data *) ifp->driver_data;
|
|
|
|
|
- return d->wifi.apmode ? s_link : s_link && s_auth && s_join;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-struct mg_tcpip_driver mg_tcpip_driver_cyw = {mg_tcpip_driver_cyw_init,
|
|
|
|
|
- mg_tcpip_driver_cyw_output, NULL,
|
|
|
|
|
- mg_tcpip_driver_cyw_poll};
|
|
|
|
|
-
|
|
|
|
|
-// - DS:
|
|
|
|
|
-// https://www.mouser.com/datasheet/2/196/Infineon_CYW43439_DataSheet_v03_00_EN-3074791.pdf
|
|
|
|
|
-// - WHD: https://github.com/Infineon/wifi-host-driver
|
|
|
|
|
-//
|
|
|
|
|
-// | e <-- event data
|
|
|
|
|
-// |-----
|
|
|
|
|
-// net | vnd <-- network (TCP/IP) | vendor header (Broadcom (bcm))
|
|
|
|
|
-// -----|-----
|
|
|
|
|
-// IOCTL | ETH | ETH <-- IOCTL/IOVAR: chip control | ETH: Ethernet header
|
|
|
|
|
-// -------|-----|-----
|
|
|
|
|
-// CDC | BDC | BDC
|
|
|
|
|
-// ------- ----- -----
|
|
|
|
|
-// SDPCM <-- includes SDIO bus arbitration, not used in SPI
|
|
|
|
|
-// -------------------
|
|
|
|
|
-// SPI | SDIO <-- padded to 32-bit | 64-bytes
|
|
|
|
|
-//
|
|
|
|
|
-// - SDPCM has 3 channels (control, data, and asynchronous data)
|
|
|
|
|
-// - SPI has 4 "functions", F0 to F3, to access different blocks in the chip,
|
|
|
|
|
-// like the SPI/SDIO controller, chip backplane, and 2 DMA I/Os; these are
|
|
|
|
|
-// usually handled by SDPCM but we need to explicitly access the I/O controller
|
|
|
|
|
-// and chip backplane during initialization
|
|
|
|
|
-// - SDIO has 3 functions (proper SDIO terminology), F0 to F2, coincident with
|
|
|
|
|
-// those for SPI, accessed through standard SDIO practices. There is no F3.
|
|
|
|
|
-
|
|
|
|
|
-// Processor core firmware is loaded to TCM RAM, along with module-dependent
|
|
|
|
|
-// (hardware design) NVRAM data, via the chip backplane access through the bus
|
|
|
|
|
-// Once the chip has been initialized, information regarding regulatory
|
|
|
|
|
-// constraints (CLM blob, “Country Locale Matrix”), is loaded as an IOVAR. This
|
|
|
|
|
-// is tied to the module being certified, hence it is also module-dependent.
|
|
|
|
|
-// - Result: chip firmware + module NVRAM data + module CLM blob
|
|
|
|
|
-
|
|
|
|
|
-#pragma pack(push, 1)
|
|
|
|
|
-// all little endian
|
|
|
|
|
-
|
|
|
|
|
-struct cdc_hdr {
|
|
|
|
|
- uint32_t cmd; // ioctl command value
|
|
|
|
|
- uint16_t olen; // output buflen
|
|
|
|
|
- uint16_t ilen; // input buflen (excludes header)
|
|
|
|
|
- uint32_t flags;
|
|
|
|
|
- uint32_t status;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct bdc_hdr {
|
|
|
|
|
- uint8_t flags; // Flags
|
|
|
|
|
- uint8_t priority; // 802.1d Priority (low 3 bits)
|
|
|
|
|
- uint8_t flags2;
|
|
|
|
|
- uint8_t data_offset; // Offset from end of BDC header to packet data, in
|
|
|
|
|
- // 4-uint8_t words. Leaves room for optional headers.
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct sdpcm_sw_hdr {
|
|
|
|
|
- uint8_t sequence; // Sequence number of pkt
|
|
|
|
|
- uint8_t channel_and_flags; // IOCTL/IOVAR or User Data or Event
|
|
|
|
|
- uint8_t next_length;
|
|
|
|
|
- uint8_t header_length; // Offset to BDC or CDC header
|
|
|
|
|
- uint8_t wireless_flow_control;
|
|
|
|
|
- uint8_t bus_data_credit; // Credit from WLAN Chip
|
|
|
|
|
- uint8_t _reserved[2];
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct sdpcm_hdr {
|
|
|
|
|
- uint16_t len;
|
|
|
|
|
- uint16_t _len; // ~len
|
|
|
|
|
- struct sdpcm_sw_hdr sw_hdr;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct data_hdr {
|
|
|
|
|
- struct sdpcm_hdr sdpcm;
|
|
|
|
|
- uint8_t pad[2];
|
|
|
|
|
- struct bdc_hdr bdc;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-// gSPI, CYW43439 DS 4.2.1 Fig.12, 2-bit field
|
|
|
|
|
-#define CYW_SPID_FUNC_BUS 0 // F0
|
|
|
|
|
-#define CYW_SPID_FUNC_CHIP 1 // F1
|
|
|
|
|
-#define CYW_SPID_FUNC_WLAN 2 // F2
|
|
|
|
|
-
|
|
|
|
|
-// SDIO functions, 3-bit field; CYW4343W and CYW43439 DS 4.1
|
|
|
|
|
-#define CYW_SDIO_FUNC_BUS 0 // F0
|
|
|
|
|
-#define CYW_SDIO_FUNC_CHIP 1 // F1
|
|
|
|
|
-#define CYW_SDIO_FUNC_WLAN 2 // F2
|
|
|
|
|
-
|
|
|
|
|
-#define CYW_SDPCM_CTRL_HDR 0
|
|
|
|
|
-#define CYW_SDPCM_ASYNC_HDR 1
|
|
|
|
|
-#define CYW_SDPCM_DATA_HDR 2
|
|
|
|
|
-
|
|
|
|
|
-#pragma pack(pop)
|
|
|
|
|
-
|
|
|
|
|
-static uint8_t s_tx_seqno;
|
|
|
|
|
-static uint32_t txdata[2048 / 4], resp[2048 / 4];
|
|
|
|
|
-
|
|
|
|
|
-static void cyw_handle_cdc(struct cdc_hdr *cdc, size_t len);
|
|
|
|
|
-static void cyw_handle_bdc(struct bdc_hdr *bdc, size_t len);
|
|
|
|
|
-static void cyw_handle_bdc_evnt(struct bdc_hdr *bdc, size_t len);
|
|
|
|
|
-
|
|
|
|
|
-static size_t cyw_bus_specific_poll(uint32_t *dest);
|
|
|
|
|
-static void cyw_update_hash_table(void);
|
|
|
|
|
-
|
|
|
|
|
-// High-level comm stuff
|
|
|
|
|
-
|
|
|
|
|
-static void cyw_poll(void) {
|
|
|
|
|
- struct sdpcm_hdr *sdpcm = (struct sdpcm_hdr *) resp;
|
|
|
|
|
- unsigned int channel;
|
|
|
|
|
- if (s_ifp->update_mac_hash_table) {
|
|
|
|
|
- // first call to _poll() is after _init(), so this is safe
|
|
|
|
|
- cyw_update_hash_table();
|
|
|
|
|
- s_ifp->update_mac_hash_table = false;
|
|
|
|
|
- }
|
|
|
|
|
- if (cyw_bus_specific_poll(resp) == 0) return;
|
|
|
|
|
- if ((sdpcm->len ^ sdpcm->_len) != 0xffff || sdpcm->len < sizeof(*sdpcm) ||
|
|
|
|
|
- sdpcm->len > 2048 - sizeof(*sdpcm))
|
|
|
|
|
- return;
|
|
|
|
|
- channel = sdpcm->sw_hdr.channel_and_flags & 0x0F;
|
|
|
|
|
- if (channel == CYW_SDPCM_CTRL_HDR) {
|
|
|
|
|
- if (sdpcm->len >= sizeof(*sdpcm) + sizeof(struct cdc_hdr)) {
|
|
|
|
|
- struct cdc_hdr *cdc =
|
|
|
|
|
- (struct cdc_hdr *) ((size_t) sdpcm + sdpcm->sw_hdr.header_length);
|
|
|
|
|
- size_t len = sdpcm->len - sdpcm->sw_hdr.header_length;
|
|
|
|
|
- cyw_handle_cdc(cdc, len);
|
|
|
|
|
- }
|
|
|
|
|
- } else if (channel == CYW_SDPCM_DATA_HDR) {
|
|
|
|
|
- if (sdpcm->len >= sizeof(*sdpcm) + sizeof(struct bdc_hdr)) {
|
|
|
|
|
- struct bdc_hdr *bdc =
|
|
|
|
|
- (struct bdc_hdr *) ((size_t) sdpcm + sdpcm->sw_hdr.header_length);
|
|
|
|
|
- size_t len = sdpcm->len - sdpcm->sw_hdr.header_length;
|
|
|
|
|
- cyw_handle_bdc(bdc, len);
|
|
|
|
|
- }
|
|
|
|
|
- } else if (channel == CYW_SDPCM_ASYNC_HDR) {
|
|
|
|
|
- struct bdc_hdr *bdc =
|
|
|
|
|
- (struct bdc_hdr *) ((size_t) sdpcm + sdpcm->sw_hdr.header_length);
|
|
|
|
|
- size_t len_ = sdpcm->len - sdpcm->sw_hdr.header_length;
|
|
|
|
|
- cyw_handle_bdc_evnt(bdc, len_);
|
|
|
|
|
- } // else silently discard
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// WLAN frame reception
|
|
|
|
|
-static void cyw_handle_bdc(struct bdc_hdr *bdc, size_t len) {
|
|
|
|
|
- uint8_t *payload = (uint8_t *) &bdc[bdc->data_offset + 1];
|
|
|
|
|
- mg_tcpip_qwrite(payload, len - (payload - (uint8_t *) bdc), s_ifp);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static size_t cyw_bus_specific_tx(uint32_t *data, uint16_t len);
|
|
|
|
|
-
|
|
|
|
|
-// WLAN frame transmission
|
|
|
|
|
-static size_t mg_cyw_tx(unsigned int ifc, void *data, size_t len) {
|
|
|
|
|
- struct data_hdr *hdr = (struct data_hdr *) txdata;
|
|
|
|
|
- uint16_t txlen = (uint16_t) (len + sizeof(*hdr));
|
|
|
|
|
- memset(txdata, 0, sizeof(*hdr));
|
|
|
|
|
- memcpy((uint8_t *) txdata + sizeof(*hdr), data, len);
|
|
|
|
|
- // TODO(): hdr->bdc.priority = map IP to TOS if supporting QoS/ToS
|
|
|
|
|
- hdr->bdc.flags = 2 << 4; // BDC version 2
|
|
|
|
|
- hdr->bdc.flags2 = (uint8_t) ifc; // 0 -> STA, 1 -> AP
|
|
|
|
|
- // hdr->bdc.data_offset = 0; // actually zeroed above
|
|
|
|
|
- hdr->sdpcm.len = txlen;
|
|
|
|
|
- hdr->sdpcm._len = (uint16_t) ~txlen;
|
|
|
|
|
- hdr->sdpcm.sw_hdr.sequence = ++s_tx_seqno;
|
|
|
|
|
- hdr->sdpcm.sw_hdr.channel_and_flags = CYW_SDPCM_DATA_HDR,
|
|
|
|
|
- hdr->sdpcm.sw_hdr.header_length = offsetof(struct data_hdr, bdc);
|
|
|
|
|
- return cyw_bus_specific_tx(txdata, txlen);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// WLAN event handling
|
|
|
|
|
-#pragma pack(push, 1)
|
|
|
|
|
-// all in network order
|
|
|
|
|
-
|
|
|
|
|
-struct eth_hdr { // TODO(scaprile) reuse 'eth' in net_builtin.c
|
|
|
|
|
- uint8_t dest[6];
|
|
|
|
|
- uint8_t src[6];
|
|
|
|
|
- uint16_t type;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct bcm_vendor_hdr {
|
|
|
|
|
- uint16_t subtype; // vendor specific: 0x8001
|
|
|
|
|
- uint16_t length; // bytes following this field
|
|
|
|
|
- uint8_t version; // 0
|
|
|
|
|
- uint8_t oui[3]; // vendor specific: 0x00 0x10 0x18
|
|
|
|
|
- uint16_t usr_subtype;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct bcm_evnt_hdr {
|
|
|
|
|
- uint16_t version; // 1: fields up to ifname; 2: as shown
|
|
|
|
|
- uint16_t flags;
|
|
|
|
|
- uint32_t event_type;
|
|
|
|
|
- uint32_t status;
|
|
|
|
|
- uint32_t reason;
|
|
|
|
|
- uint32_t auth_type;
|
|
|
|
|
- uint32_t datalen;
|
|
|
|
|
- uint8_t addr[6]; // Station address (if applicable)
|
|
|
|
|
- char ifname[16];
|
|
|
|
|
- uint8_t ifidx;
|
|
|
|
|
- uint8_t bss_cfg_idx;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct evnt_msg {
|
|
|
|
|
- struct eth_hdr eth;
|
|
|
|
|
- // struct vendor_hdr; but we only handle Broadcom (Wi-Fi processor) events
|
|
|
|
|
- struct bcm_vendor_hdr bcm;
|
|
|
|
|
- struct bcm_evnt_hdr event;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-#pragma pack(pop)
|
|
|
|
|
-
|
|
|
|
|
-struct scan_result;
|
|
|
|
|
-static void cyw_handle_scan_result(uint32_t status, struct scan_result *data,
|
|
|
|
|
- size_t len);
|
|
|
|
|
-
|
|
|
|
|
-// Do not call any IOCTL functions here, otherwise revise cyw_ioctl_wait()
|
|
|
|
|
-static void cyw_handle_bdc_evnt(struct bdc_hdr *bdc, size_t len) {
|
|
|
|
|
- struct evnt_msg *msg = (struct evnt_msg *) &bdc[bdc->data_offset + 1];
|
|
|
|
|
- MG_VERBOSE(("%u bytes event", len));
|
|
|
|
|
- if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump((void *) bdc, len);
|
|
|
|
|
- if (mg_ntohs(msg->eth.type) != 0x886C || msg->bcm.oui[0] != 0x00 ||
|
|
|
|
|
- msg->bcm.oui[1] != 0x10 || msg->bcm.oui[2] != 0x18)
|
|
|
|
|
- return; // discard if not Broadcom
|
|
|
|
|
- if (mg_ntohl(msg->event.datalen) <=
|
|
|
|
|
- len - ((uint8_t *) msg - (uint8_t *) bdc)) {
|
|
|
|
|
- uint32_t event_type = mg_ntohl(msg->event.event_type);
|
|
|
|
|
- uint32_t status = mg_ntohl(msg->event.status);
|
|
|
|
|
- uint32_t reason = mg_ntohl(msg->event.reason);
|
|
|
|
|
- uint16_t flags = mg_ntohs(msg->event.flags);
|
|
|
|
|
- MG_VERBOSE(("BCM evt %lu %lu %lu %p", event_type, status, reason, flags));
|
|
|
|
|
- if (event_type == 16 && status == 0) { // Link
|
|
|
|
|
- s_link = flags & 1;
|
|
|
|
|
- } else if (event_type == 46 && s_link) { // PSK sup with link up
|
|
|
|
|
- if (status == 6) { // Keyed
|
|
|
|
|
- } else if ((status == 4 || status == 8 || status == 10) &&
|
|
|
|
|
- reason == 15) { // Wait M1/M3/G1
|
|
|
|
|
- MG_ERROR(("AUTH TIMEOUT"));
|
|
|
|
|
- s_auth = false;
|
|
|
|
|
- } else {
|
|
|
|
|
- MG_ERROR(("AUTH FAILED"));
|
|
|
|
|
- s_auth = false;
|
|
|
|
|
- }
|
|
|
|
|
- } else if (event_type == 3 && status != 6) { // Auth (not unsolicited)
|
|
|
|
|
- if (status == 0) { // Success
|
|
|
|
|
- s_auth = true;
|
|
|
|
|
- } else {
|
|
|
|
|
- MG_ERROR(("AUTH TIMEOUT"));
|
|
|
|
|
- s_auth = false;
|
|
|
|
|
- }
|
|
|
|
|
- } else if (event_type == 1) { // Join
|
|
|
|
|
- if (status == 0) { // Success
|
|
|
|
|
- s_join = true;
|
|
|
|
|
- } else {
|
|
|
|
|
- MG_ERROR(("%s", status == 3 /* No networks */ ? "SSID NOT FOUND"
|
|
|
|
|
- : "JOIN FAILED"));
|
|
|
|
|
- s_join = false;
|
|
|
|
|
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_CONNECT_ERR, &status);
|
|
|
|
|
- }
|
|
|
|
|
- } else if (event_type == 12 || event_type == 5) { // Disassoc, Deauth
|
|
|
|
|
- s_auth = false;
|
|
|
|
|
- } else if (event_type == 69) { // Scan result
|
|
|
|
|
- struct scan_result *data = (struct scan_result *) (&msg->event + 1);
|
|
|
|
|
- size_t dlen = mg_ntohl(msg->event.datalen);
|
|
|
|
|
- if (dlen > len - ((uint8_t *) data - (uint8_t *) bdc)) return;
|
|
|
|
|
- cyw_handle_scan_result(status, data, dlen);
|
|
|
|
|
- }
|
|
|
|
|
- } // else silently discard
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_ioctl_get_(unsigned int ifc, unsigned int cmd, void *data,
|
|
|
|
|
- size_t len);
|
|
|
|
|
-static bool cyw_ioctl_set_(unsigned int ifc, unsigned int cmd, void *data,
|
|
|
|
|
- size_t len);
|
|
|
|
|
-static bool cyw_ioctl_iovar_get_(unsigned int ifc, char *var, void *data,
|
|
|
|
|
- size_t len);
|
|
|
|
|
-static bool cyw_ioctl_iovar_set_(unsigned int ifc, char *var, void *data,
|
|
|
|
|
- size_t len);
|
|
|
|
|
-// clang-format off
|
|
|
|
|
-// convenience: ioctl funcs on default ifc (0), as only AP needs ifc 1
|
|
|
|
|
-__attribute__((unused)) static bool cyw_ioctl_get(unsigned int cmd, void *data, size_t len) { return cyw_ioctl_get_(0, cmd, data, len); }
|
|
|
|
|
-static bool cyw_ioctl_set(unsigned int cmd, void *data, size_t len) { return cyw_ioctl_set_(0, cmd, data, len); }
|
|
|
|
|
-static bool cyw_ioctl_iovar_get(char *var, void *data, size_t len) { return cyw_ioctl_iovar_get_(0, var, data, len); }
|
|
|
|
|
-static bool cyw_ioctl_iovar_set(char *var, void *data, size_t len) { return cyw_ioctl_iovar_set_(0, var, data, len); }
|
|
|
|
|
-// clang-format on
|
|
|
|
|
-
|
|
|
|
|
-// Wi-Fi network stuff
|
|
|
|
|
-
|
|
|
|
|
-// clang-format off
|
|
|
|
|
-static bool cyw_wifi_connect(char *ssid, char *pass) {
|
|
|
|
|
- uint32_t sup_wpa[2] = {0, 1}; // bss index 0 = STA, not open
|
|
|
|
|
- static const uint32_t eapver[2] = {0, (uint32_t) -1}, // accept AP version
|
|
|
|
|
- tmo[2] = {0, 2500};
|
|
|
|
|
- uint32_t data[64/4 + 1]; // max pass length: 64 for WPA, 128 for WPA3 SAE
|
|
|
|
|
- uint16_t *da = (uint16_t *) data;
|
|
|
|
|
- unsigned int len;
|
|
|
|
|
- uint32_t val;
|
|
|
|
|
- val = 4; // security type: 0 for none, 2 for WPA, 4 for WPA2/WPA3, 6 for mixed WPA/WPA2
|
|
|
|
|
- // sup_wpa[1] = 0 if not using security
|
|
|
|
|
- if (!(cyw_ioctl_set(134 /* SET_WSEC */, (uint8_t *)&val, sizeof(val))
|
|
|
|
|
- && cyw_ioctl_iovar_set("bsscfg:sup_wpa", (void *)sup_wpa, sizeof(sup_wpa))
|
|
|
|
|
- && cyw_ioctl_iovar_set("bsscfg:sup_wpa2_eapver", (void *)eapver, sizeof(eapver))
|
|
|
|
|
- && cyw_ioctl_iovar_set("bsscfg:sup_wpa_tmo", (void *)tmo, sizeof(tmo)))
|
|
|
|
|
- ) return false;
|
|
|
|
|
- mg_delayms(2); // allow radio firmware to be ready
|
|
|
|
|
- // skip if not using auth
|
|
|
|
|
- memset(data, 0, sizeof(data));
|
|
|
|
|
- len = strlen(pass);
|
|
|
|
|
- da[0] = (uint16_t) len;
|
|
|
|
|
- da[1] = 1; // indicates wireless security key, skip for WPA3 SAE
|
|
|
|
|
- memcpy((uint8_t *)data + 2 * sizeof(uint16_t), pass, len); // skip for WPA3 SAE
|
|
|
|
|
- if (!cyw_ioctl_set(268 /* SET_WSEC_PMK */, data, sizeof(data))) return false; // skip for WPA3 SAE, sizeof/2 if supporting SAE but using WPA
|
|
|
|
|
- // for WPA3 SAE: memcpy((uint8_t *)data + sizeof(uint16_t), pass, len); cyw_ioctl_iovar_set("sae_password", data, sizeof(data));
|
|
|
|
|
- // resume if not using auth
|
|
|
|
|
- val = 1; if (!cyw_ioctl_set(20 /* SET_INFRA */, (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- val = 0; // auth type: 0 for open, 3 for SAE
|
|
|
|
|
- if (!cyw_ioctl_set(22 /* SET_AUTH */, (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- val = 1; // MFP capable: 1 for yes, 0 for no; recommended to be set for WPA2+ (2 for 'required', WPA3)
|
|
|
|
|
- cyw_ioctl_iovar_set("mfp", (uint8_t *)&val, sizeof(val)); // Old chipsets do not support MFP
|
|
|
|
|
- val = 0x80; // auth type: 0 for none, 4 for WPA PSK, 0x80 for WPA2 PSK, 0x40000 for WPA3 SAE PSK
|
|
|
|
|
- if (!cyw_ioctl_set(165 /* SET_WPA_AUTH */, (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- len = strlen(ssid);
|
|
|
|
|
- data[0] = (uint32_t) len;
|
|
|
|
|
- memcpy((uint8_t *)&data[1], ssid, len);
|
|
|
|
|
- if (!cyw_ioctl_set(26 /* SET_SSID */, data, len + sizeof(uint32_t))) return false;
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_wifi_disconnect(void) {
|
|
|
|
|
- return cyw_ioctl_set(52 /* DISASSOC */, NULL, 0);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// For AP functions, we use explicit ifc selection; both for clarity and maintenance, as some actions are performed on ifc 0, with or without a bss_index, and others are performed on ifc 1
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
|
|
|
|
|
- uint32_t data[64/4 + 2]; // max pass length: 64 for WPA, 128 for WPA3 SAE
|
|
|
|
|
- uint16_t *da = (uint16_t *) data;
|
|
|
|
|
- unsigned int len;
|
|
|
|
|
- uint32_t val;
|
|
|
|
|
- // CHIP DEPENDENCY
|
|
|
|
|
- // RPi set the AMPDU parameter for AP (window size = 2) *****************
|
|
|
|
|
- // val = 2 ; cyw_ioctl_iovar_set_(0, "ampdu_ba_wsize", (uint8_t *)&val, sizeof(val));
|
|
|
|
|
- // some chips might require to turn APSTA off and issue a SET_AP IOCTL
|
|
|
|
|
- len = strlen(ssid);
|
|
|
|
|
- data[0] = 1; // bss index 1 = AP
|
|
|
|
|
- data[1] = (uint32_t) len;
|
|
|
|
|
- memcpy((uint8_t *)&data[2], ssid, len);
|
|
|
|
|
- // TODO(scaprile): this takes some time to process, or requires a delay before doing it
|
|
|
|
|
- if (!cyw_ioctl_iovar_set_(0, "bsscfg:ssid", (uint8_t *)&data, len + 2 * sizeof(uint32_t))) return false;
|
|
|
|
|
- // TODO(scaprile): but sometimes this one takes some time to process
|
|
|
|
|
- val = (uint32_t) channel; if (!cyw_ioctl_set_(0, 30 /* SET_CHANNEL */, (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- data[0] = 1; // bss index 1 = AP
|
|
|
|
|
- data[1] = 0x00400004; // security type: 0 for none, 0x00200002 for WPA, 0x00400004 for WPA2, 0x01000004 for WPA3, 0x01400004 for mixed WPA2/WPA3, 0x00400006 for mixed WPA/WPA2
|
|
|
|
|
- // NOTE(): WHD writes & 0xFF if WPS is not enabled (?)
|
|
|
|
|
- if (!cyw_ioctl_iovar_set_(0, "bsscfg:wsec", (uint8_t *)&data, 2 * sizeof(uint32_t))) return false;
|
|
|
|
|
- val = 1; // MFP capable: 1 for yes, 0 for no; recommended to be set for WPA2+ (2 for 'required', WPA3)
|
|
|
|
|
- cyw_ioctl_iovar_set_(1, "mfp", (uint8_t *)&val, sizeof(val)); // Old chipsets do not support MFP
|
|
|
|
|
- mg_delayms(2); // allow radio firmware to be ready
|
|
|
|
|
- // skip if not using auth
|
|
|
|
|
- // WPA, WPA2, mixed WPA/WPA2, mixed WPA2/WPA3
|
|
|
|
|
- // NOTE(): WHD does not set SAE password for shared WPA2/WPA3, same do we
|
|
|
|
|
- memset(data, 0, sizeof(data));
|
|
|
|
|
- len = strlen(pass);
|
|
|
|
|
- da[0] = (uint16_t) len; // skip for WPA3 SAE (43430 does NOT support WPA3 in AP)
|
|
|
|
|
- da[1] = 1; // indicates wireless security key, skip for WPA3 SAE
|
|
|
|
|
- memcpy((uint8_t *)data + 2 * sizeof(uint16_t), pass, len); // skip for WPA3 SAE
|
|
|
|
|
- if (!cyw_ioctl_set_(1, 268 /* SET_WSEC_PMK */, data, sizeof(data))) return false; // skip for WPA3 SAE, sizeof/2 if supporting SAE but using WPA
|
|
|
|
|
- /* for WPA3 SAE:
|
|
|
|
|
- memcpy((uint8_t *)data + sizeof(uint16_t), pass, len);
|
|
|
|
|
- cyw_ioctl_iovar_set_(1, "sae_password", data, sizeof(data)); */
|
|
|
|
|
- /* for WPA3 or mixed WPA2/WPA3:
|
|
|
|
|
- val = 5 ; cyw_ioctl_iovar_set_(1, "sae_max_pwe_loop", (uint8_t *)&val, sizeof(val)); // Some chipsets do not support this */
|
|
|
|
|
- // resume if not using auth
|
|
|
|
|
-
|
|
|
|
|
- data[0] = 1; // bss index 1 = AP
|
|
|
|
|
- data[1] = 0x80; // auth type: 0 for none, 4 for WPA PSK, 0x80 for WPA2 PSK, 0x40000 for WPA3 SAE PSK; ored if mixed
|
|
|
|
|
- if (!cyw_ioctl_iovar_set_(0, "bsscfg:wpa_auth", (uint8_t *)&data, 2 * sizeof(uint32_t))) return false;
|
|
|
|
|
-
|
|
|
|
|
- val = 1 /* auto */; if (!cyw_ioctl_set_(1, 110 /* SET_GMODE */, (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- // Set multicast tx rate to 11Mbps, may fail in some chipsets, we are enforcing it
|
|
|
|
|
- val = 11000000 / 500000; if (!cyw_ioctl_iovar_set_(1, "2g_mrate", (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- val = 1; if (!cyw_ioctl_set_(1, 78 /* SET_DTIMPRD */, (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- data[0] = 1; // bss index 1 = AP
|
|
|
|
|
- data[1] = 1; // UP
|
|
|
|
|
- // TODO(scaprile): this takes a long time to process
|
|
|
|
|
- if (!cyw_ioctl_iovar_set_(0, "bss", (uint8_t *)&data, 2 * sizeof(uint32_t))) return false;
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_wifi_ap_stop(void) {
|
|
|
|
|
- uint32_t data[2];
|
|
|
|
|
- data[0] = 1; // bss index 1 = AP
|
|
|
|
|
- data[1] = 0; // DOWN
|
|
|
|
|
- if (!cyw_ioctl_iovar_set_(0, "bss", (uint8_t *)&data, 2 * sizeof(uint32_t))) return false;
|
|
|
|
|
- // DO WE NEED TO CLEAR CHANNEL ???
|
|
|
|
|
- // CHIP DEPENDENCY
|
|
|
|
|
- //val = 8 ; cyw_ioctl_iovar_set_(0, "ampdu_ba_wsize", (uint8_t *)&val, sizeof(val));
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// WLAN scan handling
|
|
|
|
|
-
|
|
|
|
|
-#pragma pack(push, 1)
|
|
|
|
|
-// in little endian
|
|
|
|
|
-
|
|
|
|
|
-struct wifi_scan_opt {
|
|
|
|
|
- uint32_t version;
|
|
|
|
|
- uint16_t action;
|
|
|
|
|
- uint16_t _;
|
|
|
|
|
- uint32_t ssid_len;
|
|
|
|
|
- uint8_t ssid[32];
|
|
|
|
|
- uint8_t bssid[6];
|
|
|
|
|
- int8_t bss_type;
|
|
|
|
|
- int8_t scan_type;
|
|
|
|
|
- int32_t nprobes;
|
|
|
|
|
- int32_t active_time;
|
|
|
|
|
- int32_t passive_time;
|
|
|
|
|
- int32_t home_time;
|
|
|
|
|
- int32_t channel_num;
|
|
|
|
|
- uint16_t channel_list[1];
|
|
|
|
|
-};
|
|
|
|
|
-#pragma pack(pop)
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_wifi_scan(void) {
|
|
|
|
|
- struct wifi_scan_opt opts;
|
|
|
|
|
- memset(&opts, 0, sizeof(opts));
|
|
|
|
|
- opts.version = 1;
|
|
|
|
|
- opts.action = 1; // start
|
|
|
|
|
- opts._ = 0;
|
|
|
|
|
- memset(opts.bssid, 0xff, sizeof(opts.bssid));
|
|
|
|
|
- opts.bss_type = 2; // any
|
|
|
|
|
- opts.nprobes = -1;
|
|
|
|
|
- opts.active_time = -1;
|
|
|
|
|
- opts.passive_time = -1;
|
|
|
|
|
- opts.home_time = -1;
|
|
|
|
|
- opts.channel_num = 0;
|
|
|
|
|
- opts.channel_list[0] = 0;
|
|
|
|
|
- return cyw_ioctl_iovar_set("escan", (uint8_t *)&opts, sizeof(opts));
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-#pragma pack(push, 1)
|
|
|
|
|
-// in little endian
|
|
|
|
|
-
|
|
|
|
|
-struct scan_bss {
|
|
|
|
|
- uint32_t version; // version field
|
|
|
|
|
- uint32_t length; // byte length of data in this record, starting at version and including IEs
|
|
|
|
|
- uint8_t BSSID[6]; // Unique 6-byte MAC address
|
|
|
|
|
- uint16_t beacon_period; // Interval between two consecutive beacon frames. Units are Kusec
|
|
|
|
|
- uint16_t capability; // Capability information
|
|
|
|
|
- uint8_t SSID_len; // SSID length
|
|
|
|
|
- uint8_t SSID[32]; // Array to store SSID
|
|
|
|
|
- uint8_t reserved1[1]; // Reserved(padding)
|
|
|
|
|
- uint32_t rateset_count; // Count of rates in this set
|
|
|
|
|
- uint8_t rateset_rates[16]; // rates in 500kbps units, higher bit set if basic
|
|
|
|
|
- uint16_t chanspec; // Channel specification for basic service set
|
|
|
|
|
- uint16_t atim_window; // Announcement traffic indication message window size. Units are Kusec
|
|
|
|
|
- uint8_t dtim_period; // Delivery traffic indication message period
|
|
|
|
|
- uint8_t reserved2[1]; // Reserved(padding)
|
|
|
|
|
- int16_t RSSI; // receive signal strength (in dBm)
|
|
|
|
|
- int8_t phy_noise; // noise (in dBm)
|
|
|
|
|
- uint8_t n_cap; // BSS is 802.11n Capable
|
|
|
|
|
- uint8_t reserved3[2]; // Reserved(padding)
|
|
|
|
|
- uint32_t nbss_cap; // 802.11n BSS Capabilities (based on HT_CAP_*)
|
|
|
|
|
- uint8_t ctl_ch; // 802.11n BSS control channel number
|
|
|
|
|
- uint8_t reserved4[3]; // Reserved(padding)
|
|
|
|
|
- uint32_t reserved32[1]; // Reserved for expansion of BSS properties
|
|
|
|
|
- uint8_t flags; // flags
|
|
|
|
|
- uint8_t vht_cap; // BSS is vht capable
|
|
|
|
|
- uint8_t reserved5[2]; // Reserved(padding)
|
|
|
|
|
- uint8_t basic_mcs[16]; // 802.11N BSS required MCS set
|
|
|
|
|
- uint16_t ie_offset; // offset at which IEs start, from beginning
|
|
|
|
|
- uint16_t reserved16[1]; // Reserved(padding)
|
|
|
|
|
- uint32_t ie_length; // byte length of Information Elements
|
|
|
|
|
- int16_t SNR; // Average SNR during frame reception
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct scan_result {
|
|
|
|
|
- uint32_t buflen;
|
|
|
|
|
- uint32_t version;
|
|
|
|
|
- uint16_t sync_id;
|
|
|
|
|
- uint16_t bss_count;
|
|
|
|
|
- struct scan_bss bss[1];
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-#pragma pack(pop)
|
|
|
|
|
-
|
|
|
|
|
-// CHIP DEPENDENCY
|
|
|
|
|
-#define CYW_BSS_BANDMASK 0xc000
|
|
|
|
|
-#define CYW_BSS_BAND2G 0
|
|
|
|
|
-//
|
|
|
|
|
-
|
|
|
|
|
-static void cyw_handle_scan_result(uint32_t status, struct scan_result *data, size_t len) {
|
|
|
|
|
- MG_VERBOSE(("scan event, status: %ld", status));
|
|
|
|
|
- if (status == 0) { // SUCCESS
|
|
|
|
|
- MG_VERBOSE(("scan complete"));
|
|
|
|
|
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_END, NULL);
|
|
|
|
|
- } else if (status == 8) { // PARTIAL
|
|
|
|
|
- struct mg_wifi_scan_bss_data bss;
|
|
|
|
|
- struct scan_bss *sbss = data->bss;
|
|
|
|
|
- unsigned int band = sbss->chanspec & CYW_BSS_BANDMASK;
|
|
|
|
|
- if (data->version != 109 || data->bss_count != 1) {
|
|
|
|
|
- MG_ERROR(("Unsupported: %lu %u", data->version, data->bss_count));
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (sbss->length > len - offsetof(struct scan_result, bss) || sbss->SSID_len > sizeof(sbss->SSID) || sbss->ie_offset < sizeof(*sbss) || sbss->ie_offset > (sizeof(*sbss) + sbss->ie_length) || sbss->ie_offset + sbss->ie_length > sbss->length)
|
|
|
|
|
- return; // silently discard malformed data
|
|
|
|
|
- if (!(sbss->flags & MG_BIT(2))) return; // RSSI_ONCHANNEL, ignore off-channel results
|
|
|
|
|
- bss.SSID = mg_str_n((char *)sbss->SSID, sbss->SSID_len);
|
|
|
|
|
- bss.BSSID = (char *)sbss->BSSID;
|
|
|
|
|
- bss.RSSI = (int8_t)sbss->RSSI;
|
|
|
|
|
- bss.has_n = sbss->n_cap != 0;
|
|
|
|
|
- bss.channel = bss.has_n ? sbss->ctl_ch : (uint8_t)(sbss->chanspec & 0xff); // n 40MHz vs a/b/g and 20MHz
|
|
|
|
|
- bss.band = band & CYW_BSS_BAND2G ? MG_WIFI_BAND_2G : MG_WIFI_BAND_5G;
|
|
|
|
|
- bss.security = (sbss->capability & MG_BIT(4) /* CAP_PRIVACY */) ? MG_WIFI_SECURITY_WEP : MG_WIFI_SECURITY_OPEN;
|
|
|
|
|
- { // travel IEs (Information Elements) in search of security definitions
|
|
|
|
|
- const uint8_t wot1[4] = {0x00, 0x50, 0xf2, 0x01}; // WPA_OUI_TYPE1
|
|
|
|
|
- uint8_t *ie = (uint8_t *)sbss + sbss->ie_offset;
|
|
|
|
|
- int bytes = (int) sbss->ie_length;
|
|
|
|
|
- while (bytes > 0 && ie[1] + 2 < bytes) { // ie[0] -> type, ie[1] -> bytes from ie[2]
|
|
|
|
|
- if (ie[0] == 48 /* IE_ID_RSN */) bss.security |= MG_WIFI_SECURITY_WPA2;
|
|
|
|
|
- if (ie[0] == 221 /* IE_ID_VENDOR_SPECIFIC */ && memcmp(&ie[2], wot1, 4) == 0)
|
|
|
|
|
- bss.security |= MG_WIFI_SECURITY_WPA;
|
|
|
|
|
- ie += ie[1] + 2;
|
|
|
|
|
- bytes -= ie[1] + 2;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf, bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI, bss.security));
|
|
|
|
|
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_RESULT, &bss);
|
|
|
|
|
- } else {
|
|
|
|
|
- MG_ERROR(("scan error"));
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-// clang-format on
|
|
|
|
|
-
|
|
|
|
|
-// IOCTL stuff. All values read and written are in little endian format
|
|
|
|
|
-
|
|
|
|
|
-static uint16_t s_ioctl_reqid;
|
|
|
|
|
-
|
|
|
|
|
-// CDC handler for waiting loop
|
|
|
|
|
-static uint8_t *s_ioctl_resp;
|
|
|
|
|
-static bool s_ioctl_err;
|
|
|
|
|
-
|
|
|
|
|
-static void cyw_handle_cdc(struct cdc_hdr *cdc, size_t len) {
|
|
|
|
|
- uint8_t *r = (uint8_t *) cdc + sizeof(*cdc);
|
|
|
|
|
- MG_VERBOSE(("%u bytes CDC frame", len));
|
|
|
|
|
- if ((cdc->flags >> 16) != s_ioctl_reqid) return;
|
|
|
|
|
- if (cdc->flags & 1) {
|
|
|
|
|
- MG_ERROR(("IOCTL error: %ld", -cdc->status));
|
|
|
|
|
- s_ioctl_err = true;
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump((void *) cdc, len);
|
|
|
|
|
- MG_VERBOSE(("IOCTL result: %02x %02x %02x %02x ..", r[0], r[1], r[2], r[3]));
|
|
|
|
|
- s_ioctl_resp = r;
|
|
|
|
|
-}
|
|
|
|
|
-// NOTE(): alt no loop handler dispatching IOCTL response to current handler:
|
|
|
|
|
-// static void *s_ioctl_hnd; *s_ioctl_hnd(ioctl, len);
|
|
|
|
|
-// app is a state machine calling get/sets and advancing via these callbacks
|
|
|
|
|
-
|
|
|
|
|
-#pragma pack(push, 1)
|
|
|
|
|
-// all little endian
|
|
|
|
|
-
|
|
|
|
|
-struct ctrl_hdr {
|
|
|
|
|
- struct sdpcm_hdr sdpcm;
|
|
|
|
|
- struct cdc_hdr cdc;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-#pragma pack(pop)
|
|
|
|
|
-
|
|
|
|
|
-// IOCTL command send
|
|
|
|
|
-static void cyw_ioctl_send_cmd(unsigned int ifc, unsigned int cmd, bool set,
|
|
|
|
|
- size_t len) {
|
|
|
|
|
- struct ctrl_hdr *hdr = (struct ctrl_hdr *) txdata;
|
|
|
|
|
- uint16_t txlen = (uint16_t) (len + sizeof(*hdr));
|
|
|
|
|
- memset(txdata, 0, sizeof(*hdr));
|
|
|
|
|
- hdr->cdc.cmd = cmd;
|
|
|
|
|
- hdr->cdc.olen = (uint16_t) len;
|
|
|
|
|
- // hdr->cdc.ilen = 0; // actually zeroed above
|
|
|
|
|
- hdr->cdc.flags = ((uint32_t) ++s_ioctl_reqid << 16) | ((ifc & 0xf) << 12) |
|
|
|
|
|
- (set ? MG_BIT(1) : 0);
|
|
|
|
|
- hdr->sdpcm.len = txlen;
|
|
|
|
|
- hdr->sdpcm._len = (uint16_t) ~txlen;
|
|
|
|
|
- hdr->sdpcm.sw_hdr.sequence = ++s_tx_seqno;
|
|
|
|
|
- hdr->sdpcm.sw_hdr.channel_and_flags = CYW_SDPCM_CTRL_HDR;
|
|
|
|
|
- hdr->sdpcm.sw_hdr.header_length = offsetof(struct ctrl_hdr, cdc);
|
|
|
|
|
- cyw_bus_specific_tx(txdata, txlen);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// just send respective commands, response handled via CDC handler
|
|
|
|
|
-static void cyw_ioctl_send_get(unsigned int ifc, unsigned int cmd) {
|
|
|
|
|
- cyw_ioctl_send_cmd(ifc, cmd, false, 0);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static void cyw_ioctl_send_set(unsigned int ifc, unsigned int cmd, void *data,
|
|
|
|
|
- size_t len) {
|
|
|
|
|
- if (data != NULL && len > 0)
|
|
|
|
|
- memcpy((uint8_t *) txdata + sizeof(struct ctrl_hdr), data, len);
|
|
|
|
|
- cyw_ioctl_send_cmd(ifc, cmd, true, (uint16_t) len);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static void cyw_ioctl_send_iovar_get(unsigned int ifc, char *var, size_t len) {
|
|
|
|
|
- unsigned int namelen = strlen(var) + 1; // include '\0'
|
|
|
|
|
- // cmd = GET IOVAR, "set" the name...
|
|
|
|
|
- cyw_ioctl_send_set(ifc, 262, var, len > namelen ? len : namelen);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static void cyw_ioctl_send_iovar_set2(unsigned int ifc, char *var, void *data1,
|
|
|
|
|
- size_t len1, void *data2, size_t len2) {
|
|
|
|
|
- struct ctrl_hdr *hdr = (struct ctrl_hdr *) txdata;
|
|
|
|
|
- unsigned int namelen = strlen(var) + 1; // include '\0'
|
|
|
|
|
- uint16_t txlen, payload_len = (uint16_t) (namelen + len1 + len2);
|
|
|
|
|
- memcpy((uint8_t *) txdata + sizeof(*hdr), var, namelen);
|
|
|
|
|
- memcpy((uint8_t *) txdata + namelen + sizeof(*hdr), data1, len1);
|
|
|
|
|
- if (data2 != NULL)
|
|
|
|
|
- memcpy((uint8_t *) txdata + namelen + sizeof(*hdr) + len1, data2, len2);
|
|
|
|
|
- txlen = (uint16_t) (payload_len + sizeof(*hdr));
|
|
|
|
|
- cyw_ioctl_send_cmd(ifc, 263, true, txlen); // cmd = SET IOVAR
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-__attribute__((unused)) static void cyw_ioctl_send_iovar_set(unsigned int ifc,
|
|
|
|
|
- char *var,
|
|
|
|
|
- void *data,
|
|
|
|
|
- size_t len) {
|
|
|
|
|
- cyw_ioctl_send_iovar_set2(ifc, var, data, len, NULL, 0);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static inline bool delayms(unsigned int ms) {
|
|
|
|
|
- mg_delayms(ms);
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// wait for a response, meanwhile delivering received frames and events
|
|
|
|
|
-static bool cyw_ioctl_wait(void) {
|
|
|
|
|
- unsigned int times = 100;
|
|
|
|
|
- s_ioctl_resp = NULL;
|
|
|
|
|
- s_ioctl_err = false;
|
|
|
|
|
- do { // IOCTL response processing does not call any other IOCTL function
|
|
|
|
|
- cyw_poll(); // otherwise we can't allow them to pile up here
|
|
|
|
|
- // network frames will be pushed to the queue so that is safe
|
|
|
|
|
- } while (s_ioctl_resp == NULL && !s_ioctl_err && times-- > 0 && delayms(1));
|
|
|
|
|
- MG_VERBOSE(("resp: %lp, err: %c, times: %d", s_ioctl_resp,
|
|
|
|
|
- s_ioctl_err ? '1' : '0', (int) times));
|
|
|
|
|
- return s_ioctl_resp != NULL;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_ioctl_waitdata(void *data, size_t len) {
|
|
|
|
|
- if (!cyw_ioctl_wait()) return false;
|
|
|
|
|
- memcpy(data, s_ioctl_resp, len);
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// send respective commands, wait for a response or timeout
|
|
|
|
|
-static bool cyw_ioctl_get_(unsigned int ifc, unsigned int cmd, void *data,
|
|
|
|
|
- size_t len) {
|
|
|
|
|
- cyw_ioctl_send_get(ifc, cmd);
|
|
|
|
|
- return cyw_ioctl_waitdata(data, len);
|
|
|
|
|
-}
|
|
|
|
|
-static bool cyw_ioctl_set_(unsigned int ifc, unsigned int cmd, void *data,
|
|
|
|
|
- size_t len) {
|
|
|
|
|
- cyw_ioctl_send_set(ifc, cmd, data, len);
|
|
|
|
|
- return cyw_ioctl_wait();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_ioctl_iovar_get_(unsigned int ifc, char *var, void *data,
|
|
|
|
|
- size_t len) {
|
|
|
|
|
- cyw_ioctl_send_iovar_get(ifc, var, len);
|
|
|
|
|
- return cyw_ioctl_waitdata(data, len);
|
|
|
|
|
-}
|
|
|
|
|
-static bool cyw_ioctl_iovar_set2_(unsigned int ifc, char *var, void *data1,
|
|
|
|
|
- size_t len1, void *data2, size_t len2) {
|
|
|
|
|
- cyw_ioctl_send_iovar_set2(ifc, var, data1, len1, data2, len2);
|
|
|
|
|
- return cyw_ioctl_wait();
|
|
|
|
|
-}
|
|
|
|
|
-static bool cyw_ioctl_iovar_set_(unsigned int ifc, char *var, void *data,
|
|
|
|
|
- size_t len) {
|
|
|
|
|
- return cyw_ioctl_iovar_set2_(ifc, var, data, len, NULL, 0);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// CYW43 chipset specifics. All values read and written are in little endian
|
|
|
|
|
-// format
|
|
|
|
|
-
|
|
|
|
|
-#pragma pack(push, 1)
|
|
|
|
|
-// all little endian
|
|
|
|
|
-
|
|
|
|
|
-struct cyw_country {
|
|
|
|
|
- uint32_t a;
|
|
|
|
|
- int32_t rev;
|
|
|
|
|
- uint32_t c;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct clm_hdr {
|
|
|
|
|
- uint16_t flag;
|
|
|
|
|
- uint16_t type;
|
|
|
|
|
- uint32_t len;
|
|
|
|
|
- uint32_t crc;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-#pragma pack(pop)
|
|
|
|
|
-
|
|
|
|
|
-// worlwide rev0, TODO(): try rev 17 for 4343W
|
|
|
|
|
-static const uint32_t country_code = 'X' + ('X' << 8) + (0 << 16);
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_bus_specific_init();
|
|
|
|
|
-static bool cyw_load_clmll(void *data, size_t len);
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_load_clm(struct mg_tcpip_driver_cyw_firmware *fw) {
|
|
|
|
|
- return cyw_load_clmll((void *) fw->clm_addr, fw->clm_len);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// clang-format off
|
|
|
|
|
-static bool cyw_init(uint8_t *mac) {
|
|
|
|
|
- struct mg_tcpip_driver_cyw_data *d = (struct mg_tcpip_driver_cyw_data *) s_ifp->driver_data;
|
|
|
|
|
- uint32_t val = 0;
|
|
|
|
|
- if (!cyw_bus_specific_init()) return false;
|
|
|
|
|
- if (!cyw_load_clm(d->fw)) return false; // Load CLM blob
|
|
|
|
|
- // BT-ENABLED DEPENDENCY
|
|
|
|
|
- // set Wi-Fi up
|
|
|
|
|
- val = 0 /* disable */; cyw_ioctl_iovar_set("bus:txglom", (uint8_t *)&val, sizeof(val));
|
|
|
|
|
- val = 1 /* on */; cyw_ioctl_iovar_set("apsta", (uint8_t *)&val, sizeof(val));
|
|
|
|
|
- // CHIP DEPENDENCY
|
|
|
|
|
- val = 8 ; cyw_ioctl_iovar_set("ampdu_ba_wsize", (uint8_t *)&val, sizeof(val));
|
|
|
|
|
- val = 4 ; cyw_ioctl_iovar_set("ampdu_mpdu", (uint8_t *)&val, sizeof(val));
|
|
|
|
|
- val = 0 /* 8K */; cyw_ioctl_iovar_set("ampdu_rx_factor", (uint8_t *)&val, sizeof(val));
|
|
|
|
|
- //
|
|
|
|
|
- {
|
|
|
|
|
- struct cyw_country c;
|
|
|
|
|
- unsigned int rev = (unsigned int) (country_code >> 16) & 0xffff;
|
|
|
|
|
- c.c = c.a = country_code & 0xffff;
|
|
|
|
|
- c.rev = rev == 0 ? -1 : (int32_t) rev; // if rev is 0, set it to -1, the chip will use any NVRAM/OTP configured aggregate or default to rev 0
|
|
|
|
|
- cyw_ioctl_iovar_set("country", (void *)&c, sizeof(c));
|
|
|
|
|
- } // this takes some time to process
|
|
|
|
|
- { // so do some retries while enabling events of interest
|
|
|
|
|
- // we care for SET_SSID(0), JOIN(1), AUTH(3), DEAUTH(5), DISASSOC_IND(12), LINK(16), PSK_SUP(46), SCAN_RESULT(69); all < 128
|
|
|
|
|
- uint32_t data[128/8/4 + 1];
|
|
|
|
|
- data[0] = 0; // bss index: 0 = STA
|
|
|
|
|
- memset(&data[1], 0, 128/8); // mark all as not desired
|
|
|
|
|
- data[1] = MG_BIT(0) | MG_BIT(1) | MG_BIT(3) | MG_BIT(5) | MG_BIT(12) | MG_BIT(16); // events 0 to 31
|
|
|
|
|
- data[2] = MG_BIT(46 - 32); // events 32 to 63
|
|
|
|
|
- data[3] = MG_BIT(69 - 64); // events 64 to 95
|
|
|
|
|
- unsigned int times = 100;
|
|
|
|
|
- while (times --)
|
|
|
|
|
- if (cyw_ioctl_iovar_set("bsscfg:event_msgs", (uint8_t *)data, sizeof(data))) break;
|
|
|
|
|
- if (times == (unsigned int) ~0) return false;
|
|
|
|
|
- }
|
|
|
|
|
- val = 0; if (!cyw_ioctl_set(64 /* SET_ANTDIV */, (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- if (!cyw_ioctl_set(2 /* UP, interface up */, NULL, 0)) return false;
|
|
|
|
|
- // use PM2 power saving for max throughput
|
|
|
|
|
- val = 200 /* ms */; if (!cyw_ioctl_iovar_set("pm2_sleep_ret", (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- // set beacon intervals to reduce power consumption while associated to an AP but idle
|
|
|
|
|
- val = 1; if (!cyw_ioctl_iovar_set("bcn_li_bcn", (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- val = 1; if (!cyw_ioctl_iovar_set("bcn_li_dtim", (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- val = 10; if (!cyw_ioctl_iovar_set("assoc_listen", (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- val = 1 /* auto */; if (!cyw_ioctl_set(110 /* SET_GMODE */, (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- val = 0 /* any */; if (!cyw_ioctl_set(142 /* SET_BAND */, (uint8_t *)&val, sizeof(val))) return false;
|
|
|
|
|
- if (mg_log_level >= MG_LL_DEBUG) {
|
|
|
|
|
- char text[256]; // this is huge, but we're just starting up
|
|
|
|
|
- if (cyw_ioctl_iovar_get("ver", (uint8_t *)text, sizeof(text))) {
|
|
|
|
|
- unsigned int len = strnlen(text, sizeof(text));
|
|
|
|
|
- MG_DEBUG(("Firmware:\n%.*s", len, text));
|
|
|
|
|
- }
|
|
|
|
|
- text[0] = '\0';
|
|
|
|
|
- if (cyw_ioctl_iovar_get("clmver", (uint8_t *)text, sizeof(text)) && text[0] != '\0') {
|
|
|
|
|
- unsigned int len = strnlen(text, sizeof(text));
|
|
|
|
|
- MG_DEBUG(("CLM:\n%.*s", len, text));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- {
|
|
|
|
|
- if(cyw_ioctl_iovar_get("cur_etheraddr", mac, 6)) {
|
|
|
|
|
- MG_DEBUG(("MAC: %M", mg_print_mac, mac));
|
|
|
|
|
- } else {
|
|
|
|
|
- MG_ERROR(("read MAC failed"));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-// clang-format on
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_load_fwll(void *fwdata, size_t fwlen, void *nvramdata,
|
|
|
|
|
- size_t nvramlen);
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_load_firmware(struct mg_tcpip_driver_cyw_firmware *fw) {
|
|
|
|
|
- return cyw_load_fwll((void *) fw->code_addr, fw->code_len,
|
|
|
|
|
- (void *) fw->nvram_addr, fw->nvram_len);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// clang-format off
|
|
|
|
|
-static bool cyw_load_clmll(void *data, size_t len) {
|
|
|
|
|
- unsigned int sent = 0, offset = 0;
|
|
|
|
|
- struct clm_hdr hdr = {
|
|
|
|
|
- .flag = 1 << 12 /* DLOAD_HANDLER_VER */ | MG_BIT(1) /* DL_BEGIN */,
|
|
|
|
|
- .type = 2,
|
|
|
|
|
- .crc = 0};
|
|
|
|
|
- while (sent < len) {
|
|
|
|
|
- unsigned int bytes = len - sent;
|
|
|
|
|
- if (bytes > 1024) bytes = 1024;
|
|
|
|
|
- if (sent + bytes >= len) hdr.flag |= MG_BIT(2); // DL_END;
|
|
|
|
|
- hdr.len = bytes;
|
|
|
|
|
- if (!cyw_ioctl_iovar_set2_(0, "clmload", (void *) &hdr, sizeof(hdr), (uint8_t *) data + offset, bytes))
|
|
|
|
|
- break;
|
|
|
|
|
- sent += bytes;
|
|
|
|
|
- offset += bytes;
|
|
|
|
|
- hdr.flag &= (uint16_t)~MG_BIT(1); // DL_BEGIN
|
|
|
|
|
- }
|
|
|
|
|
- return sent >= len;
|
|
|
|
|
-}
|
|
|
|
|
-// clang-format on
|
|
|
|
|
-
|
|
|
|
|
-static void cyw_update_hash_table(void) {
|
|
|
|
|
- // TODO(): read database, rebuild hash table
|
|
|
|
|
- uint32_t val = 0;
|
|
|
|
|
- val = 1;
|
|
|
|
|
- cyw_ioctl_iovar_set2_(0, "mcast_list", (uint8_t *) &val, sizeof(val),
|
|
|
|
|
- (uint8_t *) mcast_addr, sizeof(mcast_addr));
|
|
|
|
|
- mg_delayms(50);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// CYW43 chip backplane specifics. All values read and written are in little
|
|
|
|
|
-// endian format
|
|
|
|
|
-
|
|
|
|
|
-// Access to chip backplane is done windowed in 32KB banks
|
|
|
|
|
-// - addr = area base address + register offset
|
|
|
|
|
-// - set the window address to addr & ~ADDRMSK
|
|
|
|
|
-// - access addr & ADDRMSK for non-32-bit quantities
|
|
|
|
|
-// - if accesing 32-bit quantities, do it on (addr & ADDRMSK) | ACCSS4B
|
|
|
|
|
-#define CYW_CHIP_CHIPCOMMON 0x18000000
|
|
|
|
|
-#define CYW_CHIP_BCKPLN_WINSZ 0x8000
|
|
|
|
|
-#define CYW_CHIP_BCKPLN_ADDRMSK 0x7fff
|
|
|
|
|
-#define CYW_CHIP_BCKPLN_ACCSS4B MG_BIT(15)
|
|
|
|
|
-#define CYW_CHIP_BCKPLN_WRAPPOFF 0x100000
|
|
|
|
|
-// BUS DEPENDENCY: max bus to backplane transfer size, bus function id
|
|
|
|
|
-#define CYW_CHIP_BCKPLN_SPIMAX 64
|
|
|
|
|
-#define CYW_CHIP_BCKPLN_SDIOMAX 1536
|
|
|
|
|
-#if MG_ENABLE_DRIVER_CYW_SDIO
|
|
|
|
|
-#define CYW_CHIP_BCKPLN_BUSMAX CYW_CHIP_BCKPLN_SDIOMAX
|
|
|
|
|
-#define CYW_BUS_FUNC_CHIP CYW_SDIO_FUNC_CHIP
|
|
|
|
|
-#else
|
|
|
|
|
-#define CYW_CHIP_BCKPLN_BUSMAX CYW_CHIP_BCKPLN_SPIMAX
|
|
|
|
|
-#define CYW_BUS_FUNC_CHIP CYW_SPID_FUNC_CHIP
|
|
|
|
|
-#endif
|
|
|
|
|
-
|
|
|
|
|
-// CHIP DEPENDENCY
|
|
|
|
|
-#define CYW_CHIP_ARMCORE_BASE (CYW_CHIP_CHIPCOMMON + 0x3000)
|
|
|
|
|
-#define CYW_CHIP_SOCSRAM_BASE (CYW_CHIP_CHIPCOMMON + 0x4000)
|
|
|
|
|
-#define CYW_CHIP_ARMCORE (CYW_CHIP_ARMCORE_BASE + CYW_CHIP_BCKPLN_WRAPPOFF)
|
|
|
|
|
-#define CYW_CHIP_SOCSRAM (CYW_CHIP_SOCSRAM_BASE + CYW_CHIP_BCKPLN_WRAPPOFF)
|
|
|
|
|
-#define CYW_CHIP_ATCMRAM_BASE 0
|
|
|
|
|
-#define CYW_CHIP_RAM_SIZE 0x80000
|
|
|
|
|
-//
|
|
|
|
|
-
|
|
|
|
|
-#define CYW_CHIP_ADDRLOW 0x1000a
|
|
|
|
|
-#define CYW_CHIP_ADDRMID 0x1000b
|
|
|
|
|
-#define CYW_CHIP_ADDRHIGH 0x1000c
|
|
|
|
|
-#define CYW_CHIP_SPIFRCTRL 0x1000d
|
|
|
|
|
-#define CYW_CHIP_CLOCKCSR 0x1000e
|
|
|
|
|
-#define CYW_CHIP_PULLUP 0x1000f
|
|
|
|
|
-#define CYW_CHIP_WAKEUPCTL 0x1001e
|
|
|
|
|
-#define CYW_CHIP_SLEEPCSR 0x1001f
|
|
|
|
|
-
|
|
|
|
|
-#define CYW_CHIP_SOCSRAM_BANKXIDX 0x010
|
|
|
|
|
-#define CYW_CHIP_SOCSRAM_BANKXPDA 0x044
|
|
|
|
|
-#define CYW_CHIP_AI_IOCTRL 0x408
|
|
|
|
|
-#define CYW_CHIP_AI_RESETCTRL 0x800
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_bus_write(unsigned int f, uint32_t addr, void *data,
|
|
|
|
|
- uint16_t len);
|
|
|
|
|
-static bool cyw_bus_read(unsigned int f, uint32_t addr, void *data,
|
|
|
|
|
- uint16_t len);
|
|
|
|
|
-
|
|
|
|
|
-// clang-format off
|
|
|
|
|
-// set backplane window to requested area.
|
|
|
|
|
-static void cyw_set_backplane_window(uint32_t addr) {
|
|
|
|
|
- uint32_t val;
|
|
|
|
|
- addr &= ~CYW_CHIP_BCKPLN_ADDRMSK;
|
|
|
|
|
- val = (addr >> 24) & 0xff; cyw_bus_write(CYW_BUS_FUNC_CHIP, CYW_CHIP_ADDRHIGH, &val, 1);
|
|
|
|
|
- val = (addr >> 16) & 0xff; cyw_bus_write(CYW_BUS_FUNC_CHIP, CYW_CHIP_ADDRMID, &val, 1);
|
|
|
|
|
- val = (addr >> 8) & 0xff; cyw_bus_write(CYW_BUS_FUNC_CHIP, CYW_CHIP_ADDRLOW, &val, 1);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_core_reset(uint32_t core_base, bool check) {
|
|
|
|
|
- uint32_t val = 0;
|
|
|
|
|
- // core disabled after chip reset
|
|
|
|
|
- cyw_set_backplane_window(core_base); // set backplane window for requested area; we do know offsets fall within that window
|
|
|
|
|
- // possible CHIP DEPENDENCY: AI_RESETSTATUS check and wait (instead of these cool reads) to ensure backplane operations end
|
|
|
|
|
- cyw_bus_read(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_IOCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1); // ensure backplane operations end
|
|
|
|
|
- val = MG_BIT(1) | MG_BIT(0) /* SICF_FGC | SICF_CLOCK_EN */; cyw_bus_write(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_IOCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1); // reset
|
|
|
|
|
- cyw_bus_read(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_IOCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1); // ensure backplane operations end
|
|
|
|
|
- val = 0x00; cyw_bus_write(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_RESETCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1); // release reset
|
|
|
|
|
- mg_delayms(1);
|
|
|
|
|
- val = MG_BIT(0) /* SICF_CLOCK_EN */; cyw_bus_write(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_IOCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1);
|
|
|
|
|
- cyw_bus_read(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_IOCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1); // ensure backplane operations end
|
|
|
|
|
- mg_delayms(1);
|
|
|
|
|
-
|
|
|
|
|
- if (check) {
|
|
|
|
|
- // Verify only clock is enabled
|
|
|
|
|
- cyw_bus_read(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_IOCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1);
|
|
|
|
|
- if ((val & (MG_BIT(1) | MG_BIT(0)) /* SICF_FGC | SICF_CLOCK_EN) */) != MG_BIT(0)) return false;
|
|
|
|
|
- // Verify it is not in reset state
|
|
|
|
|
- cyw_bus_read(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_RESETCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1);
|
|
|
|
|
- if (val & MG_BIT(0)) return false; // AIRC_RESET
|
|
|
|
|
- }
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static void cyw_socram_init(void) {
|
|
|
|
|
- uint32_t val;
|
|
|
|
|
- // CHIP DEPENDENCY: disable remap for SRAM_3: 43430 and 43439 only
|
|
|
|
|
- cyw_set_backplane_window(CYW_CHIP_SOCSRAM_BASE); // set backplane window for requested area; we do know offsets fall within that window
|
|
|
|
|
- val = 0x03; cyw_bus_write(CYW_BUS_FUNC_CHIP, ((CYW_CHIP_SOCSRAM_BASE + CYW_CHIP_SOCSRAM_BANKXIDX) & CYW_CHIP_BCKPLN_ADDRMSK), &val, sizeof(val));
|
|
|
|
|
- val = 0x00; cyw_bus_write(CYW_BUS_FUNC_CHIP, ((CYW_CHIP_SOCSRAM_BASE + CYW_CHIP_SOCSRAM_BANKXPDA) & CYW_CHIP_BCKPLN_ADDRMSK), &val, sizeof(val));
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// transfer is fractioned in bus-to-backplane-size units within backplane windows
|
|
|
|
|
-static void cyw_load_data(uint32_t dest, void *data, size_t len) {
|
|
|
|
|
- size_t sent = 0, offset = 0;
|
|
|
|
|
- uint32_t last_addr = (uint32_t) ~0;
|
|
|
|
|
- while (sent < len) {
|
|
|
|
|
- size_t bytes = len - sent, avail;
|
|
|
|
|
- uint32_t addr = dest + offset;
|
|
|
|
|
- if (addr - last_addr >= CYW_CHIP_BCKPLN_WINSZ || last_addr == (uint32_t) ~0) {
|
|
|
|
|
- cyw_set_backplane_window(addr); // set backplane window for requested area
|
|
|
|
|
- last_addr = addr & ~CYW_CHIP_BCKPLN_ADDRMSK;
|
|
|
|
|
- }
|
|
|
|
|
- addr &= CYW_CHIP_BCKPLN_ADDRMSK;
|
|
|
|
|
- avail = CYW_CHIP_BCKPLN_WINSZ - (unsigned int) addr; // internal backplane limit
|
|
|
|
|
- if (bytes > avail) bytes = avail;
|
|
|
|
|
- if (bytes > CYW_CHIP_BCKPLN_BUSMAX) bytes = CYW_CHIP_BCKPLN_BUSMAX; // bus to backplane transfer limit
|
|
|
|
|
- cyw_bus_write(CYW_BUS_FUNC_CHIP, addr, (uint8_t *)data + offset, (uint16_t) bytes);
|
|
|
|
|
- sent += bytes;
|
|
|
|
|
- offset += bytes;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// CHIP DEPENDENCY: no SOCSRAM base address; start address in fwdata image (Cortex-R4 chips)
|
|
|
|
|
-static bool cyw_load_fwll(void *fwdata, size_t fwlen, void *nvramdata, size_t nvramlen) {
|
|
|
|
|
- uint32_t val = ((~(nvramlen / 4) & 0xffff) << 16) | (nvramlen / 4); // ~len len in 32-bit words
|
|
|
|
|
- cyw_core_reset(CYW_CHIP_SOCSRAM, false); // cores were disabled at chip reset
|
|
|
|
|
- cyw_socram_init();
|
|
|
|
|
- cyw_load_data(CYW_CHIP_ATCMRAM_BASE, fwdata, fwlen);
|
|
|
|
|
- mg_delayms(5); // TODO(scaprile): CHECK IF THIS IS ACTUALLY NEEDED
|
|
|
|
|
- // Load NVRAM and place 'length ~length' at the end; end of chip RAM
|
|
|
|
|
- {
|
|
|
|
|
- const uint32_t start = CYW_CHIP_RAM_SIZE - 4 - nvramlen;
|
|
|
|
|
- cyw_load_data(start, nvramdata, nvramlen); // nvramlen must be a multiple of 4
|
|
|
|
|
- // RAM_SIZE is a multiple of WINSZ, so the place for len ~len will be at the end of the window
|
|
|
|
|
- cyw_bus_write(CYW_BUS_FUNC_CHIP, (CYW_CHIP_BCKPLN_WINSZ - 4), &val, sizeof(val));
|
|
|
|
|
- }
|
|
|
|
|
- // Reset ARM core and check it starts
|
|
|
|
|
- if (!cyw_core_reset(CYW_CHIP_ARMCORE, true)) return false;
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-// clang-format on
|
|
|
|
|
-
|
|
|
|
|
-#if !MG_ENABLE_DRIVER_CYW_SDIO
|
|
|
|
|
-
|
|
|
|
|
-// CYW43 SPI bus specifics
|
|
|
|
|
-
|
|
|
|
|
-#define CYW_BUS_SPI_BUSCTRL 0x00 // 4 regs, 0 to 3
|
|
|
|
|
-#define CYW_BUS_SPI_INT 0x04 // 2 regs, 4 to 5
|
|
|
|
|
-#define CYW_BUS_SPI_INTEN 0x06 // 16-bit register
|
|
|
|
|
-#define CYW_BUS_SPI_STATUS 0x08 // 32-bit register
|
|
|
|
|
-#define CYW_BUS_SPI_TEST 0x14 // 32-bit register
|
|
|
|
|
-#define CYW_BUS_SPI_RESPDLY_F1 0x1d // 8-bit register, F1: chip
|
|
|
|
|
-
|
|
|
|
|
-#define CYW_BUS_STS_LEN(x) ((x >> 9) & 0x7ff)
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_spi_write(unsigned int f, uint32_t addr, void *data,
|
|
|
|
|
- uint16_t len);
|
|
|
|
|
-static void cyw_spi_read(unsigned int f, uint32_t addr, void *data,
|
|
|
|
|
- uint16_t len);
|
|
|
|
|
-
|
|
|
|
|
-// clang-format off
|
|
|
|
|
-static size_t cyw_spi_poll(uint8_t *response) {
|
|
|
|
|
- size_t len;
|
|
|
|
|
- uint32_t res;
|
|
|
|
|
- // SPI poll
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_STATUS, &res, sizeof(res));
|
|
|
|
|
- if (res == (uint32_t) ~0 || !(res & MG_BIT(8) /* packet available */ )) return 0;
|
|
|
|
|
- len = CYW_BUS_STS_LEN(res);
|
|
|
|
|
- if (len == 0) { // just ack IRQ
|
|
|
|
|
- uint16_t val = 1;
|
|
|
|
|
- cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_SPIFRCTRL, &val, 1);
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INT, &val, sizeof(val));
|
|
|
|
|
- cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INT, &val, sizeof(val));
|
|
|
|
|
- return 0;
|
|
|
|
|
- }
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_WLAN, 0, response, (uint16_t)len);
|
|
|
|
|
- return len;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static size_t cyw_spi_tx(uint32_t *data, uint16_t len) {
|
|
|
|
|
- while (len & 3) ((uint8_t *)data)[len++] = 0; // SPI 32-bit padding
|
|
|
|
|
- return cyw_spi_write(CYW_SPID_FUNC_WLAN, 0, data, len) ? len: 0;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// this can be integrated in lowest level SPI read/write _driver_ functions
|
|
|
|
|
-// (those calling hal SPI transaction functions), though is only used at start
|
|
|
|
|
-uint32_t sw16_2(uint32_t data) {
|
|
|
|
|
- return ((uint32_t)mg_htons((uint16_t)(data >> 16)) << 16) + mg_htons((uint16_t)data);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// DS 4.2.2 Table 6: signal we're working in 16-bit mode
|
|
|
|
|
-#define CYW_SPI_16bMODE MG_BIT(2) // arbitrary bit out of the FUNC space
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_spi_init() {
|
|
|
|
|
- struct mg_tcpip_driver_cyw_data *d = (struct mg_tcpip_driver_cyw_data *) s_ifp->driver_data;
|
|
|
|
|
- uint32_t val = 0;
|
|
|
|
|
- // DS 4.2.3 Boot-Up Sequence; WHD: other chips might require more effort
|
|
|
|
|
- unsigned int times = 51;
|
|
|
|
|
- while (times--) {
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_BUS | CYW_SPI_16bMODE, CYW_BUS_SPI_TEST, &val, sizeof(val));
|
|
|
|
|
- if (sw16_2(val) == 0xFEEDBEAD) break;
|
|
|
|
|
- mg_delayms(1);
|
|
|
|
|
- }
|
|
|
|
|
- if (times == (unsigned int) ~0) return false;
|
|
|
|
|
- // DS 4.2.3 Table 6. Chip starts in 16-bit little-endian mode.
|
|
|
|
|
- // Configure SPI and switch to 32-bit big-endian mode:
|
|
|
|
|
- // - High-speed mode: d->hs true
|
|
|
|
|
- // - IRQ POLARITY high
|
|
|
|
|
- // - SPI RESPONSE DELAY 4 bytes time [not in DS] TODO(scaprile): logic ana
|
|
|
|
|
- // - Status not sent after command, IRQ with status
|
|
|
|
|
- val = sw16_2(0x000204a3 | (d->hs ? MG_BIT(4) : 0)); // 4 reg content
|
|
|
|
|
- cyw_spi_write(CYW_SPID_FUNC_BUS | CYW_SPI_16bMODE, CYW_BUS_SPI_BUSCTRL, &val, sizeof(val));
|
|
|
|
|
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_DRIVER, NULL);
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_TEST, &val, sizeof(val));
|
|
|
|
|
- if (val != 0xFEEDBEAD) return false;
|
|
|
|
|
- val = 4; cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_RESPDLY_F1, &val, 1);
|
|
|
|
|
- val = 0x99; // clear error bits DATA_UNAVAILABLE, COMMAND_ERROR, DATA_ERROR, F1_OVERFLOW
|
|
|
|
|
- cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INT, &val, 1);
|
|
|
|
|
- val = 0x00be; // Enable IRQs F2_F3_FIFO_RD_UNDERFLOW, F2_F3_FIFO_WR_OVERFLOW, COMMAND_ERROR, DATA_ERROR, F2_PACKET_AVAILABLE, F1_OVERFLOW
|
|
|
|
|
- // BT-ENABLED DEPENDENCY: add F1_INTR (bit 13)
|
|
|
|
|
- cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INTEN, &val, sizeof(uint16_t));
|
|
|
|
|
-
|
|
|
|
|
- // chip backplane is ready, initialize it
|
|
|
|
|
- // request ALP (Active Low Power) clock
|
|
|
|
|
- val = MG_BIT(3) /* ALP_REQ */; cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
|
|
|
|
|
- // BT-ENABLED DEPENDENCY
|
|
|
|
|
- times = 10;
|
|
|
|
|
- while (times--) {
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
|
|
|
|
|
- if (val & MG_BIT(6)) break; // ALP_AVAIL
|
|
|
|
|
- mg_delayms(1);
|
|
|
|
|
- }
|
|
|
|
|
- if (times == (unsigned int) ~0) return false;
|
|
|
|
|
- // clear request
|
|
|
|
|
- val = 0; cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
|
|
|
|
|
- cyw_set_backplane_window(CYW_CHIP_CHIPCOMMON); // set backplane window to start of CHIPCOMMON area
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_CHIP, (CYW_CHIP_CHIPCOMMON + 0x00) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 2);
|
|
|
|
|
- if (val == 43430) val = 4343;
|
|
|
|
|
- MG_INFO(("WLAN chip is CYW%u%c", val, val == 4343 ? 'W' : ' '));
|
|
|
|
|
-
|
|
|
|
|
- // Load firmware (code and NVRAM)
|
|
|
|
|
- if (!cyw_load_firmware(d->fw)) return false;
|
|
|
|
|
-
|
|
|
|
|
- // Wait for High Throughput (HT) clock ready
|
|
|
|
|
- times = 50;
|
|
|
|
|
- while (times--) {
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
|
|
|
|
|
- if (val & MG_BIT(7)) break; // HT_AVAIL
|
|
|
|
|
- mg_delayms(1);
|
|
|
|
|
- }
|
|
|
|
|
- if (times == (unsigned int) ~0) return false;
|
|
|
|
|
- // Wait for backplane ready
|
|
|
|
|
- times = 1000;
|
|
|
|
|
- while (times--) {
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_STATUS, &val, sizeof(val));
|
|
|
|
|
- if (val & MG_BIT(5)) break; // F2_RX_READY
|
|
|
|
|
- mg_delayms(1);
|
|
|
|
|
- }
|
|
|
|
|
- if (times == (unsigned int) ~0) return false;
|
|
|
|
|
-
|
|
|
|
|
- // CHIP DEPENDENCY
|
|
|
|
|
- // Enable save / restore
|
|
|
|
|
- // Configure WakeupCtrl, set HT_AVAIL in CLOCK_CSR
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_CHIP, CYW_CHIP_WAKEUPCTL, &val, 1);
|
|
|
|
|
- val |= MG_BIT(1) /* WAKE_TILL_HT_AVAIL */; cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_WAKEUPCTL, &val, 1);
|
|
|
|
|
-#if 0
|
|
|
|
|
- // Set BRCM_CARDCAP to CMD_NODEC. NOTE(): This is probably only necessary for SDIO, not SPI
|
|
|
|
|
- val = MG_BIT(3); cyw_spi_write(CYW_SPID_FUNC_BUS, 0xf0 /* SDIOD_CCCR_BRCM_CARDCAP */, &val, 1);
|
|
|
|
|
-#endif
|
|
|
|
|
- // Force HT request to chip backplane
|
|
|
|
|
- val = MG_BIT(1) /* FORCE_HT */; cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
|
|
|
|
|
- // Enable Keep SDIO On (KSO)
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_CHIP, CYW_CHIP_SLEEPCSR, &val, 1);
|
|
|
|
|
- if (!(val & MG_BIT(0))) {
|
|
|
|
|
- val |= MG_BIT(0); cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_SLEEPCSR, &val, 1);
|
|
|
|
|
- }
|
|
|
|
|
- // The SPI bus can be configured for sleep (KSO controls wlan block sleep)
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_BUSCTRL, &val, sizeof(val));
|
|
|
|
|
- val &= ~MG_BIT(7) /* WAKE_UP */; cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_BUSCTRL, &val, sizeof(val));
|
|
|
|
|
- // Set SPI bus sleep
|
|
|
|
|
- val = 0x0f; cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_PULLUP, &val, 1);
|
|
|
|
|
-
|
|
|
|
|
- // Clear pullups. NOTE(): ?
|
|
|
|
|
- val = 0x00; cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_PULLUP, &val, 1);
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_CHIP, CYW_CHIP_PULLUP, &val, 1);
|
|
|
|
|
- // Clear possible data unavailable error
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INTEN, &val, sizeof(uint16_t));
|
|
|
|
|
- if (val & MG_BIT(0)) cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INTEN, &val, sizeof(uint16_t));
|
|
|
|
|
-
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-// clang-format on
|
|
|
|
|
-
|
|
|
|
|
-// gSPI, CYW43439 DS 4.2.1 Fig.12
|
|
|
|
|
-#define CYW_SPID_LEN(x) ((x) &0x7FF) // bits 0-10
|
|
|
|
|
-#define CYW_SPID_ADDR(x) (((x) &0x1FFFF) << 11) // bits 11-27,
|
|
|
|
|
-#define CYW_SPID_FUNC(x) (((x) &3) << 28) // bits 28-29
|
|
|
|
|
-#define CYW_SPID_INC MG_BIT(30)
|
|
|
|
|
-#define CYW_SPID_WR MG_BIT(31)
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_spi_write(unsigned int f, uint32_t addr, void *data,
|
|
|
|
|
- uint16_t len) {
|
|
|
|
|
- struct mg_tcpip_driver_cyw_data *d =
|
|
|
|
|
- (struct mg_tcpip_driver_cyw_data *) s_ifp->driver_data;
|
|
|
|
|
- struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) d->bus;
|
|
|
|
|
- uint32_t hdr = CYW_SPID_WR | CYW_SPID_INC | CYW_SPID_FUNC(f) |
|
|
|
|
|
- CYW_SPID_ADDR(addr) | CYW_SPID_LEN(len); // gSPI header
|
|
|
|
|
- // TODO(scaprile): check spin in between and timeout values, return false
|
|
|
|
|
- if (f == CYW_SPID_FUNC_WLAN) {
|
|
|
|
|
- uint32_t val = 0;
|
|
|
|
|
- while ((val & MG_BIT(5)) != MG_BIT(5)) // F2 rx ready (FIFO ready)
|
|
|
|
|
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_STATUS, &val, sizeof(val));
|
|
|
|
|
- }
|
|
|
|
|
- if (f & CYW_SPI_16bMODE)
|
|
|
|
|
- hdr = sw16_2(hdr); // swap half-words in 16-bit little-endian mode
|
|
|
|
|
-
|
|
|
|
|
- s->begin(NULL);
|
|
|
|
|
- s->txn(NULL, (uint8_t *) &hdr, NULL, sizeof(hdr));
|
|
|
|
|
- if (len <= 4) {
|
|
|
|
|
- uint32_t pad = 0;
|
|
|
|
|
- memcpy(&pad, data, len);
|
|
|
|
|
- s->txn(NULL, (uint8_t *) &pad, NULL, sizeof(pad));
|
|
|
|
|
- } else {
|
|
|
|
|
- s->txn(NULL, (uint8_t *) data, NULL, len);
|
|
|
|
|
- }
|
|
|
|
|
- s->end(NULL);
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// will write 32-bit aligned quantities to data if f == CYW_SPID_FUNC_WLAN
|
|
|
|
|
-static void cyw_spi_read(unsigned int f, uint32_t addr, void *data,
|
|
|
|
|
- uint16_t len) {
|
|
|
|
|
- struct mg_tcpip_driver_cyw_data *d =
|
|
|
|
|
- (struct mg_tcpip_driver_cyw_data *) s_ifp->driver_data;
|
|
|
|
|
- struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) d->bus;
|
|
|
|
|
- uint32_t padding =
|
|
|
|
|
- f == CYW_SPID_FUNC_CHIP
|
|
|
|
|
- ? 4
|
|
|
|
|
- : 0; // add padding to chip backplane reads as a response delay
|
|
|
|
|
- uint32_t hdr = CYW_SPID_INC | CYW_SPID_FUNC(f) | CYW_SPID_ADDR(addr) |
|
|
|
|
|
- CYW_SPID_LEN(len + padding); // gSPI header
|
|
|
|
|
- if (f == CYW_SPID_FUNC_WLAN && (len & 3))
|
|
|
|
|
- len = (len + 4) & ~3; // align WLAN transfers to 32-bit
|
|
|
|
|
- if (f & CYW_SPI_16bMODE)
|
|
|
|
|
- hdr = sw16_2(hdr); // swap half-words in 16-bit little-endian mode
|
|
|
|
|
-
|
|
|
|
|
- s->begin(NULL);
|
|
|
|
|
- s->txn(NULL, (uint8_t *) &hdr, NULL, sizeof(hdr));
|
|
|
|
|
- if (f == CYW_SPID_FUNC_CHIP) {
|
|
|
|
|
- uint32_t pad;
|
|
|
|
|
- s->txn(NULL, NULL, (uint8_t *) &pad, 4); // read padding back and discard
|
|
|
|
|
- }
|
|
|
|
|
- s->txn(NULL, NULL, (uint8_t *) data, len);
|
|
|
|
|
- s->end(NULL);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_bus_specific_init(void) {
|
|
|
|
|
- return cyw_spi_init();
|
|
|
|
|
-}
|
|
|
|
|
-static size_t cyw_bus_specific_poll(uint32_t *response) {
|
|
|
|
|
- return cyw_spi_poll((uint8_t *) response);
|
|
|
|
|
-}
|
|
|
|
|
-static size_t cyw_bus_specific_tx(uint32_t *data, uint16_t len) {
|
|
|
|
|
- return cyw_spi_tx(data, len);
|
|
|
|
|
-}
|
|
|
|
|
-static bool cyw_bus_write(unsigned int f, uint32_t addr, void *data,
|
|
|
|
|
- uint16_t len) {
|
|
|
|
|
- if (f == CYW_SPID_FUNC_CHIP && len >= 4) addr |= CYW_CHIP_BCKPLN_ACCSS4B;
|
|
|
|
|
- return cyw_spi_write(f, addr, data, len);
|
|
|
|
|
-}
|
|
|
|
|
-static bool cyw_bus_read(unsigned int f, uint32_t addr, void *data,
|
|
|
|
|
- uint16_t len) {
|
|
|
|
|
- cyw_spi_read(f, addr, data, len);
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-#else // MG_ENABLE_DRIVER_CYW_SDIO
|
|
|
|
|
-
|
|
|
|
|
-#include "sdio.h"
|
|
|
|
|
-
|
|
|
|
|
-// CYW43 SDIO bus specifics
|
|
|
|
|
-
|
|
|
|
|
-// CYW4343W and CYW43439 DS 4.1 SDIO v2.0:
|
|
|
|
|
-//- F0: max block size is 32 bytes
|
|
|
|
|
-//- F1: max block size is 64 bytes
|
|
|
|
|
-//- F2: max block size is 512 bytes
|
|
|
|
|
-
|
|
|
|
|
-// clang-format off
|
|
|
|
|
-static bool cyw_sdio_transfer(bool write, unsigned int f, uint32_t addr, void *data, uint32_t len) {
|
|
|
|
|
- struct mg_tcpip_driver_cyw_data *d = (struct mg_tcpip_driver_cyw_data *) s_ifp->driver_data;
|
|
|
|
|
- struct mg_tcpip_sdio *s = (struct mg_tcpip_sdio *) d->bus;
|
|
|
|
|
- uint32_t *ptr = (uint32_t *) data; // assume 32-bit aligned data (all except firmware)
|
|
|
|
|
- if (write && (size_t) data & 3) { // missed, source data is not 32-bit aligned
|
|
|
|
|
- memcpy(txdata, data, len); // copy to an aligned buffer, we know it fits
|
|
|
|
|
- ptr = txdata;
|
|
|
|
|
- } // all possible read destinations are 32-bit aligned
|
|
|
|
|
- // mg_sdio_transfer requires 32-bit alignment for > 1 byte transfers
|
|
|
|
|
- return mg_sdio_transfer(s, write, f, addr, ptr, len);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static size_t cyw_sdio_poll(uint32_t *response) {
|
|
|
|
|
- uint32_t res;
|
|
|
|
|
- uint16_t *len = (uint16_t *)&res;
|
|
|
|
|
- // WHD: internal docs, "tag" hinting a possible packet.
|
|
|
|
|
- // This is actually the len / ~len field of a possible struct sdpcm_hdr, if there is a packet available, or 0 if there is none.
|
|
|
|
|
- cyw_sdio_transfer(false, CYW_SDIO_FUNC_WLAN, 0, &res, sizeof(res)); // read "the tag"
|
|
|
|
|
- if ((len[0] | len[1]) == 0 || (len[0] ^ len[1]) != 0xffff || *len <= 4) return 0;
|
|
|
|
|
- response[0] = res; // copy what we already read, then read the rest
|
|
|
|
|
- cyw_sdio_transfer(false, CYW_SDIO_FUNC_WLAN, 0, response + 1, *len - sizeof(res));
|
|
|
|
|
- return (size_t)*len;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static size_t cyw_sdio_tx(uint32_t *data, uint16_t len) {
|
|
|
|
|
- return cyw_sdio_transfer(true, CYW_SDIO_FUNC_WLAN, 0, data, len) ? len: 0;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_sdio_init() {
|
|
|
|
|
- struct mg_tcpip_driver_cyw_data *d = (struct mg_tcpip_driver_cyw_data *) s_ifp->driver_data;
|
|
|
|
|
- struct mg_tcpip_sdio *s = (struct mg_tcpip_sdio *) d->bus;
|
|
|
|
|
- uint32_t val = 0;
|
|
|
|
|
- if (!mg_sdio_init(s)) return false;
|
|
|
|
|
- // no block transfers on F0. if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_BUS, 32)) return false;
|
|
|
|
|
- if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_CHIP, 64)) return false;
|
|
|
|
|
- if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_WLAN, 64)) return false;
|
|
|
|
|
- // TODO(scaprile): we don't handle SDIO interrupts, study CCCR INTEN and SDIO support (SDIO 6.3, 8)
|
|
|
|
|
- // Enable chip backplane (F1)
|
|
|
|
|
- if (!mg_sdio_enable_f(s, CYW_SDIO_FUNC_CHIP)) return false;
|
|
|
|
|
- // Wait for F1 to be ready
|
|
|
|
|
- if (!mg_sdio_waitready_f(s, CYW_SDIO_FUNC_CHIP)) return false;
|
|
|
|
|
- // chip backplane is ready, initialize it
|
|
|
|
|
- // request ALP (Active Low Power) clock
|
|
|
|
|
- val = MG_BIT(5) | MG_BIT(3) | MG_BIT(0); // HW_CLKREQ_OFF ALP_REQ FORCE_ALP
|
|
|
|
|
- cyw_sdio_transfer(true, CYW_SDIO_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
|
|
|
|
|
- // BT-ENABLED DEPENDENCY
|
|
|
|
|
- unsigned int times = 10;
|
|
|
|
|
- while (times--) {
|
|
|
|
|
- if(!cyw_sdio_transfer(false, CYW_SDIO_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1)) return false;
|
|
|
|
|
- if (val & MG_BIT(6)) break; // ALP_AVAIL
|
|
|
|
|
- mg_delayms(1);
|
|
|
|
|
- }
|
|
|
|
|
- if (times == (unsigned int) ~0) return false;
|
|
|
|
|
- // clear request
|
|
|
|
|
- val = 0; cyw_sdio_transfer(true, CYW_SDIO_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
|
|
|
|
|
- // Enable WLAN (F2)
|
|
|
|
|
- if (!mg_sdio_enable_f(s, CYW_SDIO_FUNC_WLAN)) return false;
|
|
|
|
|
- // Clear pullups. NOTE(): ?
|
|
|
|
|
- val = 0x00; cyw_sdio_transfer(true, CYW_SDIO_FUNC_CHIP, CYW_CHIP_PULLUP, &val, 1);
|
|
|
|
|
- // we don't handle wake nor OOB interrupts; SEP_INT_CTL is a vendor specific SDIO register
|
|
|
|
|
- // SDIO interrupts: enable F2 interrupt only
|
|
|
|
|
-
|
|
|
|
|
- cyw_set_backplane_window(CYW_CHIP_CHIPCOMMON); // set backplane window to start of CHIPCOMMON area
|
|
|
|
|
- cyw_sdio_transfer(false, CYW_SDIO_FUNC_CHIP, (CYW_CHIP_CHIPCOMMON + 0x00) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 2);
|
|
|
|
|
- if (val == 43430) val = 4343;
|
|
|
|
|
- MG_INFO(("WLAN chip is CYW%u%c", val, val == 4343 ? 'W' : ' '));
|
|
|
|
|
- // Load firmware (code and NVRAM)
|
|
|
|
|
- if (!cyw_load_firmware(d->fw)) return false;
|
|
|
|
|
-
|
|
|
|
|
- // Wait for High Throughput (HT) clock ready
|
|
|
|
|
- times = 50;
|
|
|
|
|
- while (times--) {
|
|
|
|
|
- if(!cyw_sdio_transfer(false, CYW_SDIO_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1)) return false;
|
|
|
|
|
- if (val & MG_BIT(7)) break; // HT_AVAIL
|
|
|
|
|
- mg_delayms(1);
|
|
|
|
|
- }
|
|
|
|
|
- if (times == (unsigned int) ~0) return false;
|
|
|
|
|
- // Wait for WLAN ready
|
|
|
|
|
- if (!mg_sdio_waitready_f(s, CYW_SDIO_FUNC_WLAN)) return false;
|
|
|
|
|
-
|
|
|
|
|
- // CHIP DEPENDENCY
|
|
|
|
|
- // Enable save / restore
|
|
|
|
|
- // Configure WakeupCtrl, set HT_AVAIL in CLOCK_CSR
|
|
|
|
|
- if(!cyw_sdio_transfer(false, CYW_SDIO_FUNC_CHIP, CYW_CHIP_WAKEUPCTL, &val, 1)) return false;
|
|
|
|
|
- val |= MG_BIT(1) /* WAKE_TILL_HT_AVAIL */; cyw_sdio_transfer(true, CYW_SDIO_FUNC_CHIP, CYW_CHIP_WAKEUPCTL, &val, 1);
|
|
|
|
|
-#if 0 // TODO(scaprile): Check if this is actually necessary
|
|
|
|
|
- // Set BRCM_CARDCAP to CMD_NODEC. This is a vendor specific SDIO register
|
|
|
|
|
- val = MG_BIT(3); cyw_sdio_transfer(true, CYW_SDIO_FUNC_BUS, 0xf0 /* SDIOD_CCCR_BRCM_CARDCAP */, &val, 1);
|
|
|
|
|
-#endif
|
|
|
|
|
- // Force HT request to chip backplane
|
|
|
|
|
- val = MG_BIT(1) /* FORCE_HT */; if(!cyw_sdio_transfer(true, CYW_SDIO_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1)) return false;
|
|
|
|
|
- // Enable Keep SDIO On (KSO)
|
|
|
|
|
- cyw_sdio_transfer(false, CYW_SDIO_FUNC_CHIP, CYW_CHIP_SLEEPCSR, &val, 1);
|
|
|
|
|
- if (!(val & MG_BIT(0))) {
|
|
|
|
|
- val |= MG_BIT(0); cyw_sdio_transfer(true, CYW_SDIO_FUNC_CHIP, CYW_CHIP_SLEEPCSR, &val, 1);
|
|
|
|
|
- }
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// clang-format on
|
|
|
|
|
-
|
|
|
|
|
-static bool cyw_bus_specific_init(void) {
|
|
|
|
|
- return cyw_sdio_init();
|
|
|
|
|
-}
|
|
|
|
|
-static size_t cyw_bus_specific_poll(uint32_t *response) {
|
|
|
|
|
- return cyw_sdio_poll(response);
|
|
|
|
|
-}
|
|
|
|
|
-static size_t cyw_bus_specific_tx(uint32_t *data, uint16_t len) {
|
|
|
|
|
- return cyw_sdio_tx(data, len);
|
|
|
|
|
-}
|
|
|
|
|
-static bool cyw_bus_write(unsigned int f, uint32_t addr, void *data,
|
|
|
|
|
- uint16_t len) {
|
|
|
|
|
- if (f == CYW_SDIO_FUNC_CHIP && len == 4) addr |= CYW_CHIP_BCKPLN_ACCSS4B;
|
|
|
|
|
- return cyw_sdio_transfer(true, f, addr, data, (uint32_t) len);
|
|
|
|
|
-}
|
|
|
|
|
-static bool cyw_bus_read(unsigned int f, uint32_t addr, void *data,
|
|
|
|
|
- uint16_t len) {
|
|
|
|
|
- return cyw_sdio_transfer(false, f, addr, data, (uint32_t) len);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-#endif
|
|
|
|
|
-
|
|
|
|
|
-// Mongoose Wi-Fi API functions
|
|
|
|
|
-
|
|
|
|
|
-bool mg_wifi_scan(void) {
|
|
|
|
|
- return cyw_wifi_scan();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-bool mg_wifi_connect(struct mg_wifi_data *wifi) {
|
|
|
|
|
- s_ifp->ip = s_ip;
|
|
|
|
|
- s_ifp->mask = s_mask;
|
|
|
|
|
- if (s_ifp->ip == 0) s_ifp->enable_dhcp_client = true;
|
|
|
|
|
- s_ifp->enable_dhcp_server = false;
|
|
|
|
|
- MG_DEBUG(("Connecting to '%s'", wifi->ssid));
|
|
|
|
|
- return cyw_wifi_connect(wifi->ssid, wifi->pass);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-bool mg_wifi_disconnect(void) {
|
|
|
|
|
- return cyw_wifi_disconnect();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-bool mg_wifi_ap_start(struct mg_wifi_data *wifi) {
|
|
|
|
|
- MG_DEBUG(("Starting AP '%s' (%u)", wifi->apssid, wifi->apchannel));
|
|
|
|
|
- return cyw_wifi_ap_start(wifi->apssid, wifi->appass, wifi->apchannel);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-bool mg_wifi_ap_stop(void) {
|
|
|
|
|
- return cyw_wifi_ap_stop();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-#endif
|
|
|