mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-17 10:49:47 +00:00
Introduce ST67W6 driver
This commit is contained in:
1 parent
abe19d2367
commit
3d094ec921
6 files changed
+1140
-2
No files matched your search
+534
-1
@@ -22555,7 +22555,8 @@ void mg_free(void *ptr) {
|
||||
#if (!defined(MG_ENABLE_DRIVER_PICO_W) || !MG_ENABLE_DRIVER_PICO_W) && \
|
||||
(!defined(MG_ENABLE_DRIVER_CYW) || !MG_ENABLE_DRIVER_CYW) && \
|
||||
(!defined(MG_ENABLE_DRIVER_CYW_SDIO) || !MG_ENABLE_DRIVER_CYW_SDIO) && \
|
||||
(!defined(MG_ENABLE_DRIVER_NXP_WIFI) || !MG_ENABLE_DRIVER_NXP_WIFI)
|
||||
(!defined(MG_ENABLE_DRIVER_NXP_WIFI) || !MG_ENABLE_DRIVER_NXP_WIFI) && \
|
||||
(!defined(MG_ENABLE_DRIVER_ST67W6) || !MG_ENABLE_DRIVER_ST67W6)
|
||||
|
||||
|
||||
bool mg_wifi_scan(void) {
|
||||
@@ -26356,6 +26357,538 @@ bool mg_sdio_init(struct mg_tcpip_sdio *sdio) {
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
#line 1 "src/drivers/st67w6.c"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_ST67W6) && \
|
||||
MG_ENABLE_DRIVER_ST67W6
|
||||
|
||||
static struct mg_tcpip_if *s_ifp;
|
||||
static uint32_t s_ip, s_mask;
|
||||
static bool s_link = false, s_connecting = false;
|
||||
|
||||
static void wifi_cb(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
|
||||
struct mg_wifi_data *wifi =
|
||||
&((struct mg_tcpip_driver_st67w6_data *) ifp->driver_data)->wifi;
|
||||
if (wifi->apmode && ev == MG_TCPIP_EV_ST_CHG &&
|
||||
*(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 st67w6_init(uint8_t *mac);
|
||||
static void st67w6_poll(bool is_at);
|
||||
|
||||
static bool mg_tcpip_driver_st67w6_init(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_st67w6_data *d =
|
||||
(struct mg_tcpip_driver_st67w6_data *) ifp->driver_data;
|
||||
struct mg_wifi_data *wifi = &d->wifi;
|
||||
if (MG_BIG_ENDIAN) {
|
||||
MG_ERROR(("Big-endian host"));
|
||||
return false;
|
||||
}
|
||||
if (d->is_ready == NULL) return false;
|
||||
s_ifp = ifp;
|
||||
s_ip = ifp->ip;
|
||||
s_mask = ifp->mask;
|
||||
s_link = false;
|
||||
ifp->pfn = wifi_cb;
|
||||
if (!st67w6_init(ifp->mac)) return false;
|
||||
|
||||
if (d->send_queue.size == 0) d->send_queue.size = 8192;
|
||||
d->send_queue.buf = (char *) mg_calloc(1, d->send_queue.size);
|
||||
if (d->send_queue.buf == NULL) {
|
||||
MG_ERROR(("OOM"));
|
||||
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;
|
||||
}
|
||||
|
||||
// Decouple; module access depends on it being RDY. See st67w6_poll()
|
||||
size_t mg_tcpip_driver_st67w6_output(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_st67w6_data *d =
|
||||
(struct mg_tcpip_driver_st67w6_data *) ifp->driver_data;
|
||||
char *p;
|
||||
if (mg_queue_book(&d->send_queue, &p, len) < len) return 0;
|
||||
memcpy(p, buf, len);
|
||||
mg_queue_add(&d->send_queue, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool mg_tcpip_driver_st67w6_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
struct mg_tcpip_driver_st67w6_data *d =
|
||||
(struct mg_tcpip_driver_st67w6_data *) ifp->driver_data;
|
||||
if (d->is_ready == NULL) return false;
|
||||
st67w6_poll(false);
|
||||
if (!s1) return false;
|
||||
return s_link;
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_st67w6 = {
|
||||
mg_tcpip_driver_st67w6_init, mg_tcpip_driver_st67w6_output, NULL,
|
||||
mg_tcpip_driver_st67w6_poll};
|
||||
|
||||
// AT | STA | AP | HCI | OT
|
||||
// --------------------------
|
||||
// framing <-- includes rx stall indication
|
||||
// --------------------------
|
||||
// SPI <-- padded to 32-bit
|
||||
//
|
||||
// Transactions take place when the module signals RDY, 'tx' and 'write'
|
||||
// functions actually just write to memory
|
||||
// - AT: handles configuration and events (unsolicited +foo:). Responses may
|
||||
// come as:
|
||||
// - 1 frame: just OK or ERROR
|
||||
// - 2 frames: 1 text line each, a response and OK
|
||||
// - 3 frames: 2 text lines: 1 frame with text and no CR/LF, 1 frame with
|
||||
// binary and CR/LF, 1 text line with OK
|
||||
// - STA and AP contain plain Ethernet frames to/from the STA and AP networks,
|
||||
// respectively
|
||||
// - HCI: BLE stuff
|
||||
// - OT: ?
|
||||
|
||||
#pragma pack(push, 1)
|
||||
// little endian
|
||||
|
||||
struct spi_hdr {
|
||||
uint16_t magic;
|
||||
uint16_t len;
|
||||
uint8_t vflags; // version :2, rx_stall :1, flags :5
|
||||
uint8_t type;
|
||||
uint16_t reserved;
|
||||
};
|
||||
|
||||
#define ST67W6_SPI_TYPE_AT 0
|
||||
#define ST67W6_SPI_TYPE_STA 1
|
||||
#define ST67W6_SPI_TYPE_AP 2
|
||||
#define ST67W6_SPI_TYPE_HCI 3
|
||||
#define ST67W6_SPI_TYPE_OT 4
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
static uint32_t txdata[2048 / 4], rxdata[2048 / 4];
|
||||
static uint8_t at_resp[300];
|
||||
|
||||
static bool s_at_ok, s_at_err;
|
||||
static size_t s_at_resp_len;
|
||||
static void st67w6_handle_wifi_evnt(char *, size_t len);
|
||||
static void st67w6_handle_scan_result(char *, size_t len);
|
||||
|
||||
static size_t st67w6_spi_poll(uint8_t *write, uint8_t *read);
|
||||
static void st67w6_write(unsigned int f, void *data, uint16_t len);
|
||||
static void st67w6_update_hash_table(void);
|
||||
|
||||
// High-level comm stuff
|
||||
|
||||
static void st67w6_poll(bool is_at) {
|
||||
struct mg_tcpip_driver_st67w6_data *d =
|
||||
(struct mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
|
||||
struct spi_hdr *h = (struct spi_hdr *) rxdata;
|
||||
unsigned int type;
|
||||
uint8_t *txsource = (uint8_t *) txdata;
|
||||
if (s_ifp->update_mac_hash_table) {
|
||||
// first call to _poll() is after _init(), so this is safe
|
||||
st67w6_update_hash_table();
|
||||
s_ifp->update_mac_hash_table = false;
|
||||
}
|
||||
if (!is_at) { // send outstanding WLAN frames in the queue
|
||||
char *buf;
|
||||
size_t len;
|
||||
// NOTE(): have traffic-dependent queues or queue traffic type with data
|
||||
if ((len = mg_queue_next(&d->send_queue, &buf)) > 0) {
|
||||
st67w6_write(d->wifi.apmode ? ST67W6_SPI_TYPE_AP : ST67W6_SPI_TYPE_STA,
|
||||
buf, (uint16_t) len);
|
||||
mg_queue_del(&d->send_queue, len);
|
||||
} else { // nothing to send
|
||||
txsource = NULL;
|
||||
}
|
||||
}
|
||||
if (st67w6_spi_poll(txsource, (uint8_t *) rxdata) == 0) return;
|
||||
if (h->len == 0) return;
|
||||
type = h->type;
|
||||
if (type == ST67W6_SPI_TYPE_AT) {
|
||||
char *p = (char *) (h + 1);
|
||||
size_t len = h->len;
|
||||
if (len > 7 && strncmp(p, "+CWLAP:", 7) == 0) { // scan result
|
||||
st67w6_handle_scan_result(p + 7, len - 7);
|
||||
} else if (len > 4 && strncmp(p, "+CW:", 4) == 0) {
|
||||
st67w6_handle_wifi_evnt(p + 4, len - 4);
|
||||
} else {
|
||||
MG_VERBOSE(("AT partial: %.*s", (int) len, p));
|
||||
if (s_at_resp_len + len >= sizeof(at_resp))
|
||||
s_at_resp_len = 0; // truncate response, error will be caught later
|
||||
memcpy(at_resp + s_at_resp_len, p, len);
|
||||
s_at_resp_len += len;
|
||||
if (mg_match(mg_str_n((char *) at_resp, s_at_resp_len),
|
||||
mg_str_n("*ERROR*", 7), NULL)) {
|
||||
s_at_err = true;
|
||||
} else if (mg_match(mg_str_n((char *) at_resp, s_at_resp_len),
|
||||
mg_str_n("*OK*", 4), NULL)) {
|
||||
s_at_ok = true;
|
||||
}
|
||||
}
|
||||
} else if (type == ST67W6_SPI_TYPE_STA || type == ST67W6_SPI_TYPE_AP) {
|
||||
// WLAN frame reception
|
||||
mg_tcpip_qwrite(h + 1, h->len, s_ifp);
|
||||
} // else silently discard
|
||||
}
|
||||
|
||||
// WLAN event handling
|
||||
|
||||
// - Do not call any AT functions here, otherwise revise st67w6_at_wait()
|
||||
// - The module likes to send ERROR along with other events
|
||||
static void st67w6_handle_wifi_evnt(char *p, size_t len) {
|
||||
struct mg_str data[2];
|
||||
MG_VERBOSE(("event: %.*s", (int) len, p));
|
||||
if (len > 9 && strncmp(p, "CONNECTED", 9) == 0) {
|
||||
s_link = true;
|
||||
s_connecting = false;
|
||||
} else if (len > 12 && strncmp(p, "DISCONNECTED", 12) == 0) {
|
||||
s_link = false;
|
||||
s_connecting = false; // should not be needed
|
||||
} else if (s_connecting && len > 6 &&
|
||||
mg_match(mg_str_n(p, len), mg_str_n("ERROR,*\r\n*", 10), data)) {
|
||||
size_t reason = 0;
|
||||
bool ok = mg_to_size_t(data[0], &reason);
|
||||
s_connecting = false;
|
||||
MG_ERROR(("CONNECT FAILED"));
|
||||
mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_CONNECT_ERR, ok ? &reason : NULL);
|
||||
} else if (len > 9 && strncmp(p, "SCAN_DONE", 9) == 0) {
|
||||
MG_VERBOSE(("scan complete"));
|
||||
mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_END, NULL);
|
||||
} // else silently discard: CONNECTING; STA_CONNECTED,"MAC";
|
||||
// STA_DISCONNECTED,"MAC"; DIST_STA_IP,"MAC","IP"
|
||||
}
|
||||
|
||||
static bool st67w6_at_cmd(char *cmd, size_t len);
|
||||
|
||||
// Wi-Fi network stuff
|
||||
|
||||
static bool st67w6_wifi_connect(char *ssid, char *pass) {
|
||||
char cmd[90]; // ssid + pass + AT
|
||||
size_t cmd_len;
|
||||
if (!st67w6_at_cmd("AT+CWMODE=1,0\r\n", 15)) return false;
|
||||
cmd_len = mg_snprintf(cmd, sizeof(cmd), "AT+CWJAP=\"%s\",\"%s\",,0\r\n", ssid,
|
||||
pass); // takes >700ms
|
||||
if (!st67w6_at_cmd(cmd, cmd_len)) return false;
|
||||
st67w6_at_cmd("AT+CWRECONNCFG=0,0\r\n", 20); // disregard error, connecting
|
||||
s_connecting = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool st67w6_wifi_disconnect(void) {
|
||||
s_connecting = false;
|
||||
if (!st67w6_at_cmd("AT+CWQAP=0\r\n", 12)) return false; // takes >800ms
|
||||
return st67w6_at_cmd("AT+CWMODE=0,0\r\n", 15); // takes >550ms
|
||||
}
|
||||
|
||||
static bool st67w6_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
|
||||
char cmd[90]; // ssid + pass + AT
|
||||
size_t cmd_len;
|
||||
if (!st67w6_at_cmd("AT+CWMODE=2,0\r\n", 15)) return false; // takes >800ms
|
||||
cmd_len = mg_snprintf(
|
||||
cmd, sizeof(cmd), "AT+CWSAP=\"%s\",\"%s\",%u,3,2,0\r\n", ssid, pass,
|
||||
channel); // 3: WPA2_PSK; 2: max stations // takes >350ms
|
||||
s_link = true;
|
||||
return st67w6_at_cmd(cmd, cmd_len);
|
||||
}
|
||||
|
||||
static bool st67w6_wifi_ap_stop(void) {
|
||||
s_link = false;
|
||||
return st67w6_at_cmd("AT+CWMODE=0,0\r\n", 15); // takes >550ms
|
||||
}
|
||||
|
||||
// WLAN scan handling
|
||||
|
||||
// +CWLAP:(security,"SSID",RSSI,"BSSID",channel,cipher,proto,wps)\r\n
|
||||
// security: OPEN, WEP, WPA, WPA2, WPA-WPA2, WPA-EAP, WPA3-SAE, WPA2-WPA3-SAE
|
||||
// cipher: NONE, WEP, AES/CCMP, TKIP, TKIP and AES/CCMP
|
||||
// proto: 4-bit bitmap AX,N,G,B; all set from right to left
|
||||
|
||||
static bool st67w6_wifi_scan(void) {
|
||||
return st67w6_at_cmd("AT+CWLAPOPT=1,1695,-100,255,50\r\n", 32) &&
|
||||
st67w6_at_cmd("AT+CWLAP=0,,,0\r\n", 16);
|
||||
}
|
||||
|
||||
static void st67w6_handle_scan_result(char *data, size_t len) {
|
||||
struct mg_wifi_scan_bss_data bss;
|
||||
struct mg_str fields[2];
|
||||
char mac[6];
|
||||
uint8_t val;
|
||||
unsigned int i;
|
||||
MG_VERBOSE(("scan result event: %.*s", (int) len, data));
|
||||
++data, --len; // skip '('
|
||||
if (!mg_span(mg_str_n(data, len), &fields[0], &fields[1], ',') ||
|
||||
!mg_str_to_num(fields[0], 10, &val, 1))
|
||||
return;
|
||||
bss.security =
|
||||
(val == 0)
|
||||
? MG_WIFI_SECURITY_OPEN
|
||||
: MG_WIFI_SECURITY_WEP |
|
||||
((val == 2 || val == 4 || val == 5) ? MG_WIFI_SECURITY_WPA
|
||||
: 0) |
|
||||
((val == 3 || val == 4 || val == 7) ? MG_WIFI_SECURITY_WPA2
|
||||
: 0) |
|
||||
((val == 6 || val == 7) ? MG_WIFI_SECURITY_WPA3 : 0);
|
||||
if (!mg_span(fields[1], &fields[0], &fields[1], ',')) return;
|
||||
bss.SSID.buf = fields[0].buf + 1, bss.SSID.len = fields[0].len - 2;
|
||||
if (!mg_span(fields[1], &fields[0], &fields[1], ',')) return;
|
||||
while (fields[0].buf[0] == ' ') ++fields[0].buf, --fields[0].len;
|
||||
if (fields[0].buf[0] != '-') return; // positive RSSI would be great
|
||||
++fields[0].buf, --fields[0].len;
|
||||
if (!mg_str_to_num(fields[0], 10, &val, 1)) return;
|
||||
bss.RSSI = (int8_t) - (int8_t) val;
|
||||
if (!mg_span(fields[1], &fields[0], &fields[1], ',')) return;
|
||||
if (fields[0].len < 19) return;
|
||||
++fields[0].buf, --fields[0].len; // skip '"'
|
||||
for (i = 0; i < 6; i++) {
|
||||
struct mg_str str;
|
||||
str.buf = fields[0].buf + 3 * i;
|
||||
str.len = 2;
|
||||
if (!mg_str_to_num(str, 16, &mac[i], 1)) return;
|
||||
}
|
||||
bss.BSSID = mac;
|
||||
if (!mg_span(fields[1], &fields[0], &fields[1], ',') ||
|
||||
!mg_str_to_num(fields[0], 10, &bss.channel, 1))
|
||||
return;
|
||||
if (!mg_span(fields[1], &fields[0], &fields[1], ',') ||
|
||||
!mg_str_to_num(fields[0], 10, &val, 1))
|
||||
return;
|
||||
// ignore cypher
|
||||
if (!mg_span(fields[1], &fields[0], &fields[1], ',') ||
|
||||
!mg_str_to_num(fields[0], 10, &val, 1))
|
||||
return;
|
||||
bss.has_n = (val & 4) != 0;
|
||||
// bss.has_ax = (val & 8) != 0;
|
||||
bss.band = MG_WIFI_BAND_2G; // NOT INFORMED with default options, no docs
|
||||
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);
|
||||
}
|
||||
|
||||
// AT stuff
|
||||
|
||||
static inline bool delayms(unsigned int ms) {
|
||||
mg_delayms(ms);
|
||||
return true;
|
||||
}
|
||||
|
||||
// send AT command, wait for a response or timeout, meanwhile delivering
|
||||
// received frames and events
|
||||
|
||||
static bool st67w6_at_cmd(char *cmd, size_t len) {
|
||||
bool is_at = true;
|
||||
unsigned int times = 1000;
|
||||
s_at_resp_len = 0;
|
||||
st67w6_write(ST67W6_SPI_TYPE_AT, cmd, (uint16_t) len);
|
||||
s_at_ok = false, s_at_err = false;
|
||||
do { // AT response processing does not call any other AT function
|
||||
st67w6_poll(is_at); // otherwise we can't allow them to pile up here
|
||||
is_at = false; // avoid repeating, allow queued WLAN frames to be sent
|
||||
// network frames will be pushed to the queue so that is safe
|
||||
} while (!s_at_ok && !s_at_err && times-- > 0 && delayms(1));
|
||||
MG_VERBOSE(("AT response:\n%.*s", s_at_resp_len, at_resp));
|
||||
MG_VERBOSE(("ok: %c, err: %c, times: %d", s_at_ok ? '1' : '0',
|
||||
s_at_err ? '1' : '0', (int) times));
|
||||
return s_at_ok;
|
||||
}
|
||||
|
||||
static bool st67w6_spi_init(void);
|
||||
bool mg_to_size_t(struct mg_str str, size_t *val);
|
||||
|
||||
static bool st67w6_init(uint8_t *mac) {
|
||||
// struct mg_tcpip_driver_st67w6_data *d = (struct
|
||||
// mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
|
||||
struct mg_str data[3];
|
||||
size_t val;
|
||||
bool is_b = false;
|
||||
if (!st67w6_spi_init()) return false;
|
||||
if (!st67w6_at_cmd("AT\r\n", 4)) return false;
|
||||
if (!st67w6_at_cmd("AT+CWNETMODE?\r\n", 15) ||
|
||||
!mg_match(mg_str_n((char *) at_resp, s_at_resp_len),
|
||||
mg_str_n("*:*\r\n*", 6), data) ||
|
||||
!mg_to_size_t(data[1], &val))
|
||||
return false;
|
||||
if (val != 0) {
|
||||
MG_ERROR(("Wrong firmware, T02 is needed"));
|
||||
return false;
|
||||
}
|
||||
// set clock, who cares ???
|
||||
if (!st67w6_at_cmd("AT+GET_CLOCK\r\n", 14)) return false;
|
||||
MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // TODO(scaprile): --> VERBOSE
|
||||
// BT-ENABLED DEPENDENCY
|
||||
|
||||
// MODULE DEPENDENCY
|
||||
if (!st67w6_at_cmd("AT+EFUSE-R=24,\"0x100\"\r\n", 23) ||
|
||||
!mg_match(mg_str_n((char *) at_resp, s_at_resp_len), mg_str_n("*,*", 3),
|
||||
data))
|
||||
return false;
|
||||
if (data[1].buf[0] == 'C' && data[1].buf[1] == '6') is_b = true;
|
||||
MG_DEBUG(("WLAN module is %sB type", is_b ? "" : "not")); // --> VERBOSE
|
||||
if (is_b) {
|
||||
// Disable the antenna diversity pin
|
||||
if (!st67w6_at_cmd("AT+IORST=0\r\n", 12)) return false;
|
||||
// Apparently they intend to disable some antenna ...
|
||||
if (!st67w6_at_cmd("AT+CWANTENABLE?\r\n", 17)) return false;
|
||||
MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // --> VERBOSE
|
||||
}
|
||||
|
||||
// Do not set wake-up pin (AT+SLWKIO)
|
||||
// Disable power save mode
|
||||
// NOTE(scaprile): (no response if in hibernate mode, though I guess we
|
||||
// wouldn't have reached this point in that case either)
|
||||
if (!st67w6_at_cmd("AT+PWR=0\r\n", 12)) return false;
|
||||
// set Wi-Fi
|
||||
// set country code
|
||||
if (!st67w6_at_cmd("AT+CWCOUNTRY=0,\"00\"\r\n", 21)) return false;
|
||||
#if 0
|
||||
// set DTIM
|
||||
if (!st67w6_at_cmd("AT+SLWKDTIM=1\r\n", 15)) return false;
|
||||
#endif
|
||||
// Read only default MAC, ignore set bit count (data[6] & 0x3F). Custom MACs
|
||||
// reside at @0x64 and 0x70
|
||||
if (st67w6_at_cmd("AT+EFUSE-R=7,\"0x014\"\r\n", 22) &&
|
||||
mg_match(mg_str_n((char *) at_resp, s_at_resp_len), mg_str_n("*,*", 3),
|
||||
data)) {
|
||||
int i;
|
||||
for (i = 0; i < 6; i++) mac[i] = data[1].buf[5 - i];
|
||||
MG_DEBUG(("MAC: %M", mg_print_mac, mac));
|
||||
} else {
|
||||
MG_ERROR(("read MAC failed"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void st67w6_update_hash_table(void) {
|
||||
// TODO(): read database, rebuild hash table
|
||||
// uint32_t val = 0;
|
||||
// val = 1;
|
||||
// st67w6_at_iovar_set2_(0, "mcast_list", (uint8_t *) &val, sizeof(val),
|
||||
// (uint8_t *) mcast_addr, sizeof(mcast_addr)); mg_delayms(50);
|
||||
}
|
||||
|
||||
// SPI specifics
|
||||
|
||||
#define ST67W6_SPI_MAGIC 0x55AA
|
||||
// #define ST67W6_SPI_VERSION(x), IS_STALL, FLAGS(x), ...
|
||||
|
||||
static const uint8_t idlehdr[sizeof(struct spi_hdr)] = {0xaa, 0x55, 0, 0,
|
||||
0, 0, 0, 0};
|
||||
|
||||
static void st67w6_write(unsigned int f, void *data, uint16_t len) {
|
||||
struct spi_hdr *h = (struct spi_hdr *) txdata;
|
||||
h->magic = ST67W6_SPI_MAGIC;
|
||||
h->type = (uint8_t) f;
|
||||
h->vflags = 0;
|
||||
h->len = len;
|
||||
h->reserved = 0;
|
||||
memmove(h + 1, data, len);
|
||||
}
|
||||
|
||||
static size_t st67w6_spi_poll(uint8_t *write, uint8_t *read) {
|
||||
struct mg_tcpip_driver_st67w6_data *d =
|
||||
(struct mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
|
||||
struct spi_hdr *th, *rh = (struct spi_hdr *) read;
|
||||
struct mg_tcpip_spi_ *s = (struct mg_tcpip_spi_ *) d->spi;
|
||||
size_t padded;
|
||||
unsigned int times;
|
||||
|
||||
th = (write != NULL) ? (struct spi_hdr *) write : (struct spi_hdr *) idlehdr;
|
||||
s->begin(s->spi);
|
||||
times = 50;
|
||||
while (times--) {
|
||||
if (d->is_ready()) break;
|
||||
if (times == 0) {
|
||||
MG_ERROR(("RDY TIMEOUT"));
|
||||
s->end(s->spi);
|
||||
return 0;
|
||||
}
|
||||
mg_delayms(1);
|
||||
}
|
||||
|
||||
padded = (th->len + 3) & ~3;
|
||||
s->txn(s->spi, (uint8_t *) th, (uint8_t *) rh, sizeof(*th) + padded);
|
||||
if (rh->magic == ST67W6_SPI_MAGIC && rh->len > padded) {
|
||||
size_t remaining_padded = (rh->len - padded + 3) & ~3;
|
||||
if (remaining_padded > (2048 - sizeof(*rh) - padded))
|
||||
remaining_padded = 2048 - sizeof(*rh) - padded;
|
||||
s->txn(s->spi, NULL, read + sizeof(*rh) + padded, remaining_padded);
|
||||
}
|
||||
times = 50;
|
||||
while (times--) {
|
||||
if (!d->is_ready()) break;
|
||||
if (times == 0) {
|
||||
MG_ERROR(("!RDY TIMEOUT"));
|
||||
break;
|
||||
}
|
||||
mg_delayms(1);
|
||||
}
|
||||
s->end(s->spi);
|
||||
return (size_t) rh->len;
|
||||
}
|
||||
|
||||
static bool st67w6_spi_init(void) {
|
||||
struct mg_tcpip_driver_st67w6_data *d =
|
||||
(struct mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
|
||||
size_t len;
|
||||
unsigned int times = 1000;
|
||||
while (times--) {
|
||||
if (d->is_ready()) break;
|
||||
if (times == 0) return false;
|
||||
mg_delayms(1);
|
||||
}
|
||||
if (((len = st67w6_spi_poll(NULL, (uint8_t *) rxdata)) == 0) ||
|
||||
!mg_match(mg_str_n(((char *) rxdata) + sizeof(struct spi_hdr), len),
|
||||
mg_str_n("*ready*", 7), NULL))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mongoose Wi-Fi API functions
|
||||
|
||||
bool mg_wifi_scan(void) {
|
||||
return st67w6_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 st67w6_wifi_connect(wifi->ssid, wifi->pass);
|
||||
}
|
||||
|
||||
bool mg_wifi_disconnect(void) {
|
||||
return st67w6_wifi_disconnect();
|
||||
}
|
||||
|
||||
bool mg_wifi_ap_start(struct mg_wifi_data *wifi) {
|
||||
MG_DEBUG(("Starting AP '%s' (%u)", wifi->apssid, wifi->apchannel));
|
||||
return st67w6_wifi_ap_start(wifi->apssid, wifi->appass, wifi->apchannel);
|
||||
}
|
||||
|
||||
bool mg_wifi_ap_stop(void) {
|
||||
return st67w6_wifi_ap_stop();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
#line 1 "src/drivers/stm32f.c"
|
||||
#endif
|
||||
|
||||
+38
@@ -3336,6 +3336,7 @@ extern struct mg_tcpip_driver mg_tcpip_driver_pico_w;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_rw612;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_cyw;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
|
||||
|
||||
// Drivers that require SPI, can use this SPI abstraction
|
||||
struct mg_tcpip_spi {
|
||||
@@ -3743,6 +3744,43 @@ bool mg_sdio_transfer(struct mg_tcpip_sdio *sdio, bool write, unsigned int f,
|
||||
#endif
|
||||
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_ST67W6) && \
|
||||
MG_ENABLE_DRIVER_ST67W6
|
||||
|
||||
struct mg_tcpip_spi_ {
|
||||
void *spi; // Opaque SPI bus descriptor
|
||||
void (*begin)(void *); // SPI begin: slave select low
|
||||
void (*end)(void *); // SPI end: slave select high
|
||||
void (*txn)(void *, uint8_t *, uint8_t *,
|
||||
size_t len); // SPI transaction: write-read len bytes
|
||||
};
|
||||
|
||||
struct mg_tcpip_driver_st67w6_data {
|
||||
struct mg_wifi_data wifi;
|
||||
void *spi;
|
||||
bool (*is_ready)(void); // return state of module RDY pin
|
||||
struct mg_queue send_queue; // decouple tx calls from module polls
|
||||
};
|
||||
|
||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||
do { \
|
||||
static struct mg_tcpip_driver_st67w6_data driver_data_; \
|
||||
static struct mg_tcpip_if mif_; \
|
||||
MG_SET_WIFI_CONFIG(&driver_data_); \
|
||||
mif_.ip = MG_TCPIP_IP; \
|
||||
mif_.mask = MG_TCPIP_MASK; \
|
||||
mif_.gw = MG_TCPIP_GW; \
|
||||
mif_.driver = &mg_tcpip_driver_st67w6; \
|
||||
mif_.driver_data = &driver_data_; \
|
||||
mif_.recv_queue.size = 8192; \
|
||||
mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
|
||||
mg_tcpip_init(mgr, &mif_); \
|
||||
MG_INFO(("Driver: st67w6, MAC: %M", mg_print_mac, mif_.mac)); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32F) && \
|
||||
MG_ENABLE_DRIVER_STM32F
|
||||
|
||||
|
||||
@@ -0,0 +1,528 @@
|
||||
#include "st67w6.h"
|
||||
#include "net.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_ST67W6) && \
|
||||
MG_ENABLE_DRIVER_ST67W6
|
||||
|
||||
static struct mg_tcpip_if *s_ifp;
|
||||
static uint32_t s_ip, s_mask;
|
||||
static bool s_link = false, s_connecting = false;
|
||||
|
||||
static void wifi_cb(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
|
||||
struct mg_wifi_data *wifi =
|
||||
&((struct mg_tcpip_driver_st67w6_data *) ifp->driver_data)->wifi;
|
||||
if (wifi->apmode && ev == MG_TCPIP_EV_ST_CHG &&
|
||||
*(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 st67w6_init(uint8_t *mac);
|
||||
static void st67w6_poll(bool is_at);
|
||||
|
||||
static bool mg_tcpip_driver_st67w6_init(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_st67w6_data *d =
|
||||
(struct mg_tcpip_driver_st67w6_data *) ifp->driver_data;
|
||||
struct mg_wifi_data *wifi = &d->wifi;
|
||||
if (MG_BIG_ENDIAN) {
|
||||
MG_ERROR(("Big-endian host"));
|
||||
return false;
|
||||
}
|
||||
if (d->is_ready == NULL) return false;
|
||||
s_ifp = ifp;
|
||||
s_ip = ifp->ip;
|
||||
s_mask = ifp->mask;
|
||||
s_link = false;
|
||||
ifp->pfn = wifi_cb;
|
||||
if (!st67w6_init(ifp->mac)) return false;
|
||||
|
||||
if (d->send_queue.size == 0) d->send_queue.size = 8192;
|
||||
d->send_queue.buf = (char *) mg_calloc(1, d->send_queue.size);
|
||||
if (d->send_queue.buf == NULL) {
|
||||
MG_ERROR(("OOM"));
|
||||
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;
|
||||
}
|
||||
|
||||
// Decouple; module access depends on it being RDY. See st67w6_poll()
|
||||
size_t mg_tcpip_driver_st67w6_output(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_st67w6_data *d =
|
||||
(struct mg_tcpip_driver_st67w6_data *) ifp->driver_data;
|
||||
char *p;
|
||||
if (mg_queue_book(&d->send_queue, &p, len) < len) return 0;
|
||||
memcpy(p, buf, len);
|
||||
mg_queue_add(&d->send_queue, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool mg_tcpip_driver_st67w6_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
struct mg_tcpip_driver_st67w6_data *d =
|
||||
(struct mg_tcpip_driver_st67w6_data *) ifp->driver_data;
|
||||
if (d->is_ready == NULL) return false;
|
||||
st67w6_poll(false);
|
||||
if (!s1) return false;
|
||||
return s_link;
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_st67w6 = {
|
||||
mg_tcpip_driver_st67w6_init, mg_tcpip_driver_st67w6_output, NULL,
|
||||
mg_tcpip_driver_st67w6_poll};
|
||||
|
||||
// AT | STA | AP | HCI | OT
|
||||
// --------------------------
|
||||
// framing <-- includes rx stall indication
|
||||
// --------------------------
|
||||
// SPI <-- padded to 32-bit
|
||||
//
|
||||
// Transactions take place when the module signals RDY, 'tx' and 'write'
|
||||
// functions actually just write to memory
|
||||
// - AT: handles configuration and events (unsolicited +foo:). Responses may
|
||||
// come as:
|
||||
// - 1 frame: just OK or ERROR
|
||||
// - 2 frames: 1 text line each, a response and OK
|
||||
// - 3 frames: 2 text lines: 1 frame with text and no CR/LF, 1 frame with
|
||||
// binary and CR/LF, 1 text line with OK
|
||||
// - STA and AP contain plain Ethernet frames to/from the STA and AP networks,
|
||||
// respectively
|
||||
// - HCI: BLE stuff
|
||||
// - OT: ?
|
||||
|
||||
#pragma pack(push, 1)
|
||||
// little endian
|
||||
|
||||
struct spi_hdr {
|
||||
uint16_t magic;
|
||||
uint16_t len;
|
||||
uint8_t vflags; // version :2, rx_stall :1, flags :5
|
||||
uint8_t type;
|
||||
uint16_t reserved;
|
||||
};
|
||||
|
||||
#define ST67W6_SPI_TYPE_AT 0
|
||||
#define ST67W6_SPI_TYPE_STA 1
|
||||
#define ST67W6_SPI_TYPE_AP 2
|
||||
#define ST67W6_SPI_TYPE_HCI 3
|
||||
#define ST67W6_SPI_TYPE_OT 4
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
static uint32_t txdata[2048 / 4], rxdata[2048 / 4];
|
||||
static uint8_t at_resp[300];
|
||||
|
||||
static bool s_at_ok, s_at_err;
|
||||
static size_t s_at_resp_len;
|
||||
static void st67w6_handle_wifi_evnt(char *, size_t len);
|
||||
static void st67w6_handle_scan_result(char *, size_t len);
|
||||
|
||||
static size_t st67w6_spi_poll(uint8_t *write, uint8_t *read);
|
||||
static void st67w6_write(unsigned int f, void *data, uint16_t len);
|
||||
static void st67w6_update_hash_table(void);
|
||||
|
||||
// High-level comm stuff
|
||||
|
||||
static void st67w6_poll(bool is_at) {
|
||||
struct mg_tcpip_driver_st67w6_data *d =
|
||||
(struct mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
|
||||
struct spi_hdr *h = (struct spi_hdr *) rxdata;
|
||||
unsigned int type;
|
||||
uint8_t *txsource = (uint8_t *) txdata;
|
||||
if (s_ifp->update_mac_hash_table) {
|
||||
// first call to _poll() is after _init(), so this is safe
|
||||
st67w6_update_hash_table();
|
||||
s_ifp->update_mac_hash_table = false;
|
||||
}
|
||||
if (!is_at) { // send outstanding WLAN frames in the queue
|
||||
char *buf;
|
||||
size_t len;
|
||||
// NOTE(): have traffic-dependent queues or queue traffic type with data
|
||||
if ((len = mg_queue_next(&d->send_queue, &buf)) > 0) {
|
||||
st67w6_write(d->wifi.apmode ? ST67W6_SPI_TYPE_AP : ST67W6_SPI_TYPE_STA,
|
||||
buf, (uint16_t) len);
|
||||
mg_queue_del(&d->send_queue, len);
|
||||
} else { // nothing to send
|
||||
txsource = NULL;
|
||||
}
|
||||
}
|
||||
if (st67w6_spi_poll(txsource, (uint8_t *) rxdata) == 0) return;
|
||||
if (h->len == 0) return;
|
||||
type = h->type;
|
||||
if (type == ST67W6_SPI_TYPE_AT) {
|
||||
char *p = (char *) (h + 1);
|
||||
size_t len = h->len;
|
||||
if (len > 7 && strncmp(p, "+CWLAP:", 7) == 0) { // scan result
|
||||
st67w6_handle_scan_result(p + 7, len - 7);
|
||||
} else if (len > 4 && strncmp(p, "+CW:", 4) == 0) {
|
||||
st67w6_handle_wifi_evnt(p + 4, len - 4);
|
||||
} else {
|
||||
MG_VERBOSE(("AT partial: %.*s", (int) len, p));
|
||||
if (s_at_resp_len + len >= sizeof(at_resp))
|
||||
s_at_resp_len = 0; // truncate response, error will be caught later
|
||||
memcpy(at_resp + s_at_resp_len, p, len);
|
||||
s_at_resp_len += len;
|
||||
if (mg_match(mg_str_n((char *) at_resp, s_at_resp_len),
|
||||
mg_str_n("*ERROR*", 7), NULL)) {
|
||||
s_at_err = true;
|
||||
} else if (mg_match(mg_str_n((char *) at_resp, s_at_resp_len),
|
||||
mg_str_n("*OK*", 4), NULL)) {
|
||||
s_at_ok = true;
|
||||
}
|
||||
}
|
||||
} else if (type == ST67W6_SPI_TYPE_STA || type == ST67W6_SPI_TYPE_AP) {
|
||||
// WLAN frame reception
|
||||
mg_tcpip_qwrite(h + 1, h->len, s_ifp);
|
||||
} // else silently discard
|
||||
}
|
||||
|
||||
// WLAN event handling
|
||||
|
||||
// - Do not call any AT functions here, otherwise revise st67w6_at_wait()
|
||||
// - The module likes to send ERROR along with other events
|
||||
static void st67w6_handle_wifi_evnt(char *p, size_t len) {
|
||||
struct mg_str data[2];
|
||||
MG_VERBOSE(("event: %.*s", (int) len, p));
|
||||
if (len > 9 && strncmp(p, "CONNECTED", 9) == 0) {
|
||||
s_link = true;
|
||||
s_connecting = false;
|
||||
} else if (len > 12 && strncmp(p, "DISCONNECTED", 12) == 0) {
|
||||
s_link = false;
|
||||
s_connecting = false; // should not be needed
|
||||
} else if (s_connecting && len > 6 &&
|
||||
mg_match(mg_str_n(p, len), mg_str_n("ERROR,*\r\n*", 10), data)) {
|
||||
size_t reason = 0;
|
||||
bool ok = mg_to_size_t(data[0], &reason);
|
||||
s_connecting = false;
|
||||
MG_ERROR(("CONNECT FAILED"));
|
||||
mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_CONNECT_ERR, ok ? &reason : NULL);
|
||||
} else if (len > 9 && strncmp(p, "SCAN_DONE", 9) == 0) {
|
||||
MG_VERBOSE(("scan complete"));
|
||||
mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_END, NULL);
|
||||
} // else silently discard: CONNECTING; STA_CONNECTED,"MAC";
|
||||
// STA_DISCONNECTED,"MAC"; DIST_STA_IP,"MAC","IP"
|
||||
}
|
||||
|
||||
static bool st67w6_at_cmd(char *cmd, size_t len);
|
||||
|
||||
// Wi-Fi network stuff
|
||||
|
||||
static bool st67w6_wifi_connect(char *ssid, char *pass) {
|
||||
char cmd[90]; // ssid + pass + AT
|
||||
size_t cmd_len;
|
||||
if (!st67w6_at_cmd("AT+CWMODE=1,0\r\n", 15)) return false;
|
||||
cmd_len = mg_snprintf(cmd, sizeof(cmd), "AT+CWJAP=\"%s\",\"%s\",,0\r\n", ssid,
|
||||
pass); // takes >700ms
|
||||
if (!st67w6_at_cmd(cmd, cmd_len)) return false;
|
||||
st67w6_at_cmd("AT+CWRECONNCFG=0,0\r\n", 20); // disregard error, connecting
|
||||
s_connecting = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool st67w6_wifi_disconnect(void) {
|
||||
s_connecting = false;
|
||||
if (!st67w6_at_cmd("AT+CWQAP=0\r\n", 12)) return false; // takes >800ms
|
||||
return st67w6_at_cmd("AT+CWMODE=0,0\r\n", 15); // takes >550ms
|
||||
}
|
||||
|
||||
static bool st67w6_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
|
||||
char cmd[90]; // ssid + pass + AT
|
||||
size_t cmd_len;
|
||||
if (!st67w6_at_cmd("AT+CWMODE=2,0\r\n", 15)) return false; // takes >800ms
|
||||
cmd_len = mg_snprintf(
|
||||
cmd, sizeof(cmd), "AT+CWSAP=\"%s\",\"%s\",%u,3,2,0\r\n", ssid, pass,
|
||||
channel); // 3: WPA2_PSK; 2: max stations // takes >350ms
|
||||
s_link = true;
|
||||
return st67w6_at_cmd(cmd, cmd_len);
|
||||
}
|
||||
|
||||
static bool st67w6_wifi_ap_stop(void) {
|
||||
s_link = false;
|
||||
return st67w6_at_cmd("AT+CWMODE=0,0\r\n", 15); // takes >550ms
|
||||
}
|
||||
|
||||
// WLAN scan handling
|
||||
|
||||
// +CWLAP:(security,"SSID",RSSI,"BSSID",channel,cipher,proto,wps)\r\n
|
||||
// security: OPEN, WEP, WPA, WPA2, WPA-WPA2, WPA-EAP, WPA3-SAE, WPA2-WPA3-SAE
|
||||
// cipher: NONE, WEP, AES/CCMP, TKIP, TKIP and AES/CCMP
|
||||
// proto: 4-bit bitmap AX,N,G,B; all set from right to left
|
||||
|
||||
static bool st67w6_wifi_scan(void) {
|
||||
return st67w6_at_cmd("AT+CWLAPOPT=1,1695,-100,255,50\r\n", 32) &&
|
||||
st67w6_at_cmd("AT+CWLAP=0,,,0\r\n", 16);
|
||||
}
|
||||
|
||||
static void st67w6_handle_scan_result(char *data, size_t len) {
|
||||
struct mg_wifi_scan_bss_data bss;
|
||||
struct mg_str fields[2];
|
||||
char mac[6];
|
||||
uint8_t val;
|
||||
unsigned int i;
|
||||
MG_VERBOSE(("scan result event: %.*s", (int) len, data));
|
||||
++data, --len; // skip '('
|
||||
if (!mg_span(mg_str_n(data, len), &fields[0], &fields[1], ',') ||
|
||||
!mg_str_to_num(fields[0], 10, &val, 1))
|
||||
return;
|
||||
bss.security =
|
||||
(val == 0)
|
||||
? MG_WIFI_SECURITY_OPEN
|
||||
: MG_WIFI_SECURITY_WEP |
|
||||
((val == 2 || val == 4 || val == 5) ? MG_WIFI_SECURITY_WPA
|
||||
: 0) |
|
||||
((val == 3 || val == 4 || val == 7) ? MG_WIFI_SECURITY_WPA2
|
||||
: 0) |
|
||||
((val == 6 || val == 7) ? MG_WIFI_SECURITY_WPA3 : 0);
|
||||
if (!mg_span(fields[1], &fields[0], &fields[1], ',')) return;
|
||||
bss.SSID.buf = fields[0].buf + 1, bss.SSID.len = fields[0].len - 2;
|
||||
if (!mg_span(fields[1], &fields[0], &fields[1], ',')) return;
|
||||
while (fields[0].buf[0] == ' ') ++fields[0].buf, --fields[0].len;
|
||||
if (fields[0].buf[0] != '-') return; // positive RSSI would be great
|
||||
++fields[0].buf, --fields[0].len;
|
||||
if (!mg_str_to_num(fields[0], 10, &val, 1)) return;
|
||||
bss.RSSI = (int8_t) - (int8_t) val;
|
||||
if (!mg_span(fields[1], &fields[0], &fields[1], ',')) return;
|
||||
if (fields[0].len < 19) return;
|
||||
++fields[0].buf, --fields[0].len; // skip '"'
|
||||
for (i = 0; i < 6; i++) {
|
||||
struct mg_str str;
|
||||
str.buf = fields[0].buf + 3 * i;
|
||||
str.len = 2;
|
||||
if (!mg_str_to_num(str, 16, &mac[i], 1)) return;
|
||||
}
|
||||
bss.BSSID = mac;
|
||||
if (!mg_span(fields[1], &fields[0], &fields[1], ',') ||
|
||||
!mg_str_to_num(fields[0], 10, &bss.channel, 1))
|
||||
return;
|
||||
if (!mg_span(fields[1], &fields[0], &fields[1], ',') ||
|
||||
!mg_str_to_num(fields[0], 10, &val, 1))
|
||||
return;
|
||||
// ignore cypher
|
||||
if (!mg_span(fields[1], &fields[0], &fields[1], ',') ||
|
||||
!mg_str_to_num(fields[0], 10, &val, 1))
|
||||
return;
|
||||
bss.has_n = (val & 4) != 0;
|
||||
// bss.has_ax = (val & 8) != 0;
|
||||
bss.band = MG_WIFI_BAND_2G; // NOT INFORMED with default options, no docs
|
||||
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);
|
||||
}
|
||||
|
||||
// AT stuff
|
||||
|
||||
static inline bool delayms(unsigned int ms) {
|
||||
mg_delayms(ms);
|
||||
return true;
|
||||
}
|
||||
|
||||
// send AT command, wait for a response or timeout, meanwhile delivering
|
||||
// received frames and events
|
||||
|
||||
static bool st67w6_at_cmd(char *cmd, size_t len) {
|
||||
bool is_at = true;
|
||||
unsigned int times = 1000;
|
||||
s_at_resp_len = 0;
|
||||
st67w6_write(ST67W6_SPI_TYPE_AT, cmd, (uint16_t) len);
|
||||
s_at_ok = false, s_at_err = false;
|
||||
do { // AT response processing does not call any other AT function
|
||||
st67w6_poll(is_at); // otherwise we can't allow them to pile up here
|
||||
is_at = false; // avoid repeating, allow queued WLAN frames to be sent
|
||||
// network frames will be pushed to the queue so that is safe
|
||||
} while (!s_at_ok && !s_at_err && times-- > 0 && delayms(1));
|
||||
MG_VERBOSE(("AT response:\n%.*s", s_at_resp_len, at_resp));
|
||||
MG_VERBOSE(("ok: %c, err: %c, times: %d", s_at_ok ? '1' : '0',
|
||||
s_at_err ? '1' : '0', (int) times));
|
||||
return s_at_ok;
|
||||
}
|
||||
|
||||
static bool st67w6_spi_init(void);
|
||||
bool mg_to_size_t(struct mg_str str, size_t *val);
|
||||
|
||||
static bool st67w6_init(uint8_t *mac) {
|
||||
// struct mg_tcpip_driver_st67w6_data *d = (struct
|
||||
// mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
|
||||
struct mg_str data[3];
|
||||
size_t val;
|
||||
bool is_b = false;
|
||||
if (!st67w6_spi_init()) return false;
|
||||
if (!st67w6_at_cmd("AT\r\n", 4)) return false;
|
||||
if (!st67w6_at_cmd("AT+CWNETMODE?\r\n", 15) ||
|
||||
!mg_match(mg_str_n((char *) at_resp, s_at_resp_len),
|
||||
mg_str_n("*:*\r\n*", 6), data) ||
|
||||
!mg_to_size_t(data[1], &val))
|
||||
return false;
|
||||
if (val != 0) {
|
||||
MG_ERROR(("Wrong firmware, T02 is needed"));
|
||||
return false;
|
||||
}
|
||||
// set clock, who cares ???
|
||||
if (!st67w6_at_cmd("AT+GET_CLOCK\r\n", 14)) return false;
|
||||
MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // TODO(scaprile): --> VERBOSE
|
||||
// BT-ENABLED DEPENDENCY
|
||||
|
||||
// MODULE DEPENDENCY
|
||||
if (!st67w6_at_cmd("AT+EFUSE-R=24,\"0x100\"\r\n", 23) ||
|
||||
!mg_match(mg_str_n((char *) at_resp, s_at_resp_len), mg_str_n("*,*", 3),
|
||||
data))
|
||||
return false;
|
||||
if (data[1].buf[0] == 'C' && data[1].buf[1] == '6') is_b = true;
|
||||
MG_DEBUG(("WLAN module is %sB type", is_b ? "" : "not")); // --> VERBOSE
|
||||
if (is_b) {
|
||||
// Disable the antenna diversity pin
|
||||
if (!st67w6_at_cmd("AT+IORST=0\r\n", 12)) return false;
|
||||
// Apparently they intend to disable some antenna ...
|
||||
if (!st67w6_at_cmd("AT+CWANTENABLE?\r\n", 17)) return false;
|
||||
MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // --> VERBOSE
|
||||
}
|
||||
|
||||
// Do not set wake-up pin (AT+SLWKIO)
|
||||
// Disable power save mode
|
||||
// NOTE(scaprile): (no response if in hibernate mode, though I guess we
|
||||
// wouldn't have reached this point in that case either)
|
||||
if (!st67w6_at_cmd("AT+PWR=0\r\n", 12)) return false;
|
||||
// set Wi-Fi
|
||||
// set country code
|
||||
if (!st67w6_at_cmd("AT+CWCOUNTRY=0,\"00\"\r\n", 21)) return false;
|
||||
#if 0
|
||||
// set DTIM
|
||||
if (!st67w6_at_cmd("AT+SLWKDTIM=1\r\n", 15)) return false;
|
||||
#endif
|
||||
// Read only default MAC, ignore set bit count (data[6] & 0x3F). Custom MACs
|
||||
// reside at @0x64 and 0x70
|
||||
if (st67w6_at_cmd("AT+EFUSE-R=7,\"0x014\"\r\n", 22) &&
|
||||
mg_match(mg_str_n((char *) at_resp, s_at_resp_len), mg_str_n("*,*", 3),
|
||||
data)) {
|
||||
int i;
|
||||
for (i = 0; i < 6; i++) mac[i] = data[1].buf[5 - i];
|
||||
MG_DEBUG(("MAC: %M", mg_print_mac, mac));
|
||||
} else {
|
||||
MG_ERROR(("read MAC failed"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void st67w6_update_hash_table(void) {
|
||||
// TODO(): read database, rebuild hash table
|
||||
// uint32_t val = 0;
|
||||
// val = 1;
|
||||
// st67w6_at_iovar_set2_(0, "mcast_list", (uint8_t *) &val, sizeof(val),
|
||||
// (uint8_t *) mcast_addr, sizeof(mcast_addr)); mg_delayms(50);
|
||||
}
|
||||
|
||||
// SPI specifics
|
||||
|
||||
#define ST67W6_SPI_MAGIC 0x55AA
|
||||
// #define ST67W6_SPI_VERSION(x), IS_STALL, FLAGS(x), ...
|
||||
|
||||
static const uint8_t idlehdr[sizeof(struct spi_hdr)] = {0xaa, 0x55, 0, 0,
|
||||
0, 0, 0, 0};
|
||||
|
||||
static void st67w6_write(unsigned int f, void *data, uint16_t len) {
|
||||
struct spi_hdr *h = (struct spi_hdr *) txdata;
|
||||
h->magic = ST67W6_SPI_MAGIC;
|
||||
h->type = (uint8_t) f;
|
||||
h->vflags = 0;
|
||||
h->len = len;
|
||||
h->reserved = 0;
|
||||
memmove(h + 1, data, len);
|
||||
}
|
||||
|
||||
static size_t st67w6_spi_poll(uint8_t *write, uint8_t *read) {
|
||||
struct mg_tcpip_driver_st67w6_data *d =
|
||||
(struct mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
|
||||
struct spi_hdr *th, *rh = (struct spi_hdr *) read;
|
||||
struct mg_tcpip_spi_ *s = (struct mg_tcpip_spi_ *) d->spi;
|
||||
size_t padded;
|
||||
unsigned int times;
|
||||
|
||||
th = (write != NULL) ? (struct spi_hdr *) write : (struct spi_hdr *) idlehdr;
|
||||
s->begin(s->spi);
|
||||
times = 50;
|
||||
while (times--) {
|
||||
if (d->is_ready()) break;
|
||||
if (times == 0) {
|
||||
MG_ERROR(("RDY TIMEOUT"));
|
||||
s->end(s->spi);
|
||||
return 0;
|
||||
}
|
||||
mg_delayms(1);
|
||||
}
|
||||
|
||||
padded = (th->len + 3) & ~3;
|
||||
s->txn(s->spi, (uint8_t *) th, (uint8_t *) rh, sizeof(*th) + padded);
|
||||
if (rh->magic == ST67W6_SPI_MAGIC && rh->len > padded) {
|
||||
size_t remaining_padded = (rh->len - padded + 3) & ~3;
|
||||
if (remaining_padded > (2048 - sizeof(*rh) - padded))
|
||||
remaining_padded = 2048 - sizeof(*rh) - padded;
|
||||
s->txn(s->spi, NULL, read + sizeof(*rh) + padded, remaining_padded);
|
||||
}
|
||||
times = 50;
|
||||
while (times--) {
|
||||
if (!d->is_ready()) break;
|
||||
if (times == 0) {
|
||||
MG_ERROR(("!RDY TIMEOUT"));
|
||||
break;
|
||||
}
|
||||
mg_delayms(1);
|
||||
}
|
||||
s->end(s->spi);
|
||||
return (size_t) rh->len;
|
||||
}
|
||||
|
||||
static bool st67w6_spi_init(void) {
|
||||
struct mg_tcpip_driver_st67w6_data *d =
|
||||
(struct mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
|
||||
size_t len;
|
||||
unsigned int times = 1000;
|
||||
while (times--) {
|
||||
if (d->is_ready()) break;
|
||||
if (times == 0) return false;
|
||||
mg_delayms(1);
|
||||
}
|
||||
if (((len = st67w6_spi_poll(NULL, (uint8_t *) rxdata)) == 0) ||
|
||||
!mg_match(mg_str_n(((char *) rxdata) + sizeof(struct spi_hdr), len),
|
||||
mg_str_n("*ready*", 7), NULL))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mongoose Wi-Fi API functions
|
||||
|
||||
bool mg_wifi_scan(void) {
|
||||
return st67w6_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 st67w6_wifi_connect(wifi->ssid, wifi->pass);
|
||||
}
|
||||
|
||||
bool mg_wifi_disconnect(void) {
|
||||
return st67w6_wifi_disconnect();
|
||||
}
|
||||
|
||||
bool mg_wifi_ap_start(struct mg_wifi_data *wifi) {
|
||||
MG_DEBUG(("Starting AP '%s' (%u)", wifi->apssid, wifi->apchannel));
|
||||
return st67w6_wifi_ap_start(wifi->apssid, wifi->appass, wifi->apchannel);
|
||||
}
|
||||
|
||||
bool mg_wifi_ap_stop(void) {
|
||||
return st67w6_wifi_ap_stop();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_ST67W6) && \
|
||||
MG_ENABLE_DRIVER_ST67W6
|
||||
|
||||
struct mg_tcpip_spi_ {
|
||||
void *spi; // Opaque SPI bus descriptor
|
||||
void (*begin)(void *); // SPI begin: slave select low
|
||||
void (*end)(void *); // SPI end: slave select high
|
||||
void (*txn)(void *, uint8_t *, uint8_t *,
|
||||
size_t len); // SPI transaction: write-read len bytes
|
||||
};
|
||||
|
||||
struct mg_tcpip_driver_st67w6_data {
|
||||
struct mg_wifi_data wifi;
|
||||
void *spi;
|
||||
bool (*is_ready)(void); // return state of module RDY pin
|
||||
struct mg_queue send_queue; // decouple tx calls from module polls
|
||||
};
|
||||
|
||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||
do { \
|
||||
static struct mg_tcpip_driver_st67w6_data driver_data_; \
|
||||
static struct mg_tcpip_if mif_; \
|
||||
MG_SET_WIFI_CONFIG(&driver_data_); \
|
||||
mif_.ip = MG_TCPIP_IP; \
|
||||
mif_.mask = MG_TCPIP_MASK; \
|
||||
mif_.gw = MG_TCPIP_GW; \
|
||||
mif_.driver = &mg_tcpip_driver_st67w6; \
|
||||
mif_.driver_data = &driver_data_; \
|
||||
mif_.recv_queue.size = 8192; \
|
||||
mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
|
||||
mg_tcpip_init(mgr, &mif_); \
|
||||
MG_INFO(("Driver: st67w6, MAC: %M", mg_print_mac, mif_.mac)); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
@@ -116,6 +116,7 @@ extern struct mg_tcpip_driver mg_tcpip_driver_pico_w;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_rw612;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_cyw;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
|
||||
|
||||
// Drivers that require SPI, can use this SPI abstraction
|
||||
struct mg_tcpip_spi {
|
||||
|
||||
+2
-1
@@ -3,7 +3,8 @@
|
||||
#if (!defined(MG_ENABLE_DRIVER_PICO_W) || !MG_ENABLE_DRIVER_PICO_W) && \
|
||||
(!defined(MG_ENABLE_DRIVER_CYW) || !MG_ENABLE_DRIVER_CYW) && \
|
||||
(!defined(MG_ENABLE_DRIVER_CYW_SDIO) || !MG_ENABLE_DRIVER_CYW_SDIO) && \
|
||||
(!defined(MG_ENABLE_DRIVER_NXP_WIFI) || !MG_ENABLE_DRIVER_NXP_WIFI)
|
||||
(!defined(MG_ENABLE_DRIVER_NXP_WIFI) || !MG_ENABLE_DRIVER_NXP_WIFI) && \
|
||||
(!defined(MG_ENABLE_DRIVER_ST67W6) || !MG_ENABLE_DRIVER_ST67W6)
|
||||
|
||||
|
||||
bool mg_wifi_scan(void) {
|
||||
|
||||
Reference in new issue
Block a user