diff options
| author | Damien George <damien.p.george@gmail.com> | 2019-06-01 16:00:56 +1000 | 
|---|---|---|
| committer | Damien George <damien.p.george@gmail.com> | 2019-06-03 16:47:35 +1000 | 
| commit | 7b70ab7258986567dd970d5108cd45c706b36a96 (patch) | |
| tree | 37b8c45b5451551936d59d5ad55bd980a4485bc5 | |
| parent | 53f2ac9017e616975194b509dd1b29c4a30cfb1d (diff) | |
drivers: Add driver for CYW43xx WiFi SoCs.
| -rw-r--r-- | drivers/cyw43/README.md | 17 | ||||
| -rw-r--r-- | drivers/cyw43/cyw43.h | 135 | ||||
| -rw-r--r-- | drivers/cyw43/cyw43_ctrl.c | 588 | ||||
| -rw-r--r-- | drivers/cyw43/cyw43_ll.h | 135 | ||||
| -rw-r--r-- | drivers/cyw43/cyw43_lwip.c | 197 | ||||
| -rw-r--r-- | drivers/cyw43/libcyw43.a | bin | 0 -> 437538 bytes | 
6 files changed, 1072 insertions, 0 deletions
| diff --git a/drivers/cyw43/README.md b/drivers/cyw43/README.md new file mode 100644 index 000000000..5af6f6558 --- /dev/null +++ b/drivers/cyw43/README.md @@ -0,0 +1,17 @@ +CYW43xx WiFi SoC driver +======================= + +This is a driver for the CYW43xx WiFi SoC. + +There are four layers to the driver: + +1. SDIO bus interface, provided by the host device/system. + +2. Low-level CYW43xx interface, managing the bus, control messages, Ethernet +   frames and asynchronous events.  Includes download of SoC firmware.  The +   header file `cyw43_ll.h` defines the interface to this layer. + +3. Mid-level CYW43xx control, to control and set WiFi parameters and manage +   events.  See `cyw43_ctrl.c`. + +4. TCP/IP bindings to lwIP.  See `cyw43_lwip.c`. diff --git a/drivers/cyw43/cyw43.h b/drivers/cyw43/cyw43.h new file mode 100644 index 000000000..d7f08cb5d --- /dev/null +++ b/drivers/cyw43/cyw43.h @@ -0,0 +1,135 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_STM32_CYW43_H +#define MICROPY_INCLUDED_STM32_CYW43_H + +#include "lwip/netif.h" +#include "lwip/dhcp.h" +#include "lib/netutils/dhcpserver.h" +#include "drivers/cyw43/cyw43_ll.h" + +// For trace_flags +#define CYW43_TRACE_ASYNC_EV    (0x0001) +#define CYW43_TRACE_ETH_TX      (0x0002) +#define CYW43_TRACE_ETH_RX      (0x0004) +#define CYW43_TRACE_ETH_FULL    (0x0008) +#define CYW43_TRACE_MAC         (0x0010) + +// Return value of cyw43_wifi_link_status +#define CYW43_LINK_DOWN         (0) +#define CYW43_LINK_JOIN         (1) +#define CYW43_LINK_NOIP         (2) +#define CYW43_LINK_UP           (3) +#define CYW43_LINK_FAIL         (-1) +#define CYW43_LINK_NONET        (-2) +#define CYW43_LINK_BADAUTH      (-3) + +typedef struct _cyw43_t { +    cyw43_ll_t cyw43_ll; + +    uint8_t itf_state; +    uint32_t trace_flags; + +    // State for async events +    volatile uint32_t wifi_scan_state; +    uint32_t wifi_join_state; +    void *wifi_scan_env; +    int (*wifi_scan_cb)(void*, const cyw43_ev_scan_result_t*); + +    // Pending things to do +    bool pend_disassoc; +    bool pend_rejoin; +    bool pend_rejoin_wpa; + +    // AP settings +    uint8_t ap_channel; +    uint8_t ap_auth; +    uint8_t ap_ssid_len; +    uint8_t ap_key_len; +    uint8_t ap_ssid[32]; +    uint8_t ap_key[64]; + +    // lwIP data +    struct netif netif[2]; +    struct dhcp dhcp_client; +    dhcp_server_t dhcp_server; +} cyw43_t; + +extern cyw43_t cyw43_state; +extern void (*cyw43_poll)(void); +extern uint32_t cyw43_sleep; + +void cyw43_init(cyw43_t *self); +void cyw43_deinit(cyw43_t *self); + +int cyw43_ioctl(cyw43_t *self, uint32_t cmd, size_t len, uint8_t *buf, uint32_t iface); +int cyw43_send_ethernet(cyw43_t *self, int itf, size_t len, const void *buf, bool is_pbuf); + +int cyw43_wifi_pm(cyw43_t *self, uint32_t pm); +int cyw43_wifi_link_status(cyw43_t *self, int itf); +void cyw43_wifi_set_up(cyw43_t *self, int itf, bool up); +int cyw43_wifi_get_mac(cyw43_t *self, int itf, uint8_t mac[6]); +int cyw43_wifi_scan(cyw43_t *self, cyw43_wifi_scan_options_t *opts, void *env, int (*result_cb)(void*, const cyw43_ev_scan_result_t*)); + +static inline bool cyw43_wifi_scan_active(cyw43_t *self) { +    return self->wifi_scan_state == 1; +} + +int cyw43_wifi_join(cyw43_t *self, size_t ssid_len, const uint8_t *ssid, size_t key_len, const uint8_t *key, uint32_t auth_type, const uint8_t *bssid, uint32_t channel); +int cyw43_wifi_leave(cyw43_t *self, int itf); + +static inline void cyw43_wifi_ap_get_ssid(cyw43_t *self, size_t *len, const uint8_t **buf) { +    *len = self->ap_ssid_len; +    *buf = self->ap_ssid; +} + +static inline void cyw43_wifi_ap_set_channel(cyw43_t *self, uint32_t channel) { +    self->ap_channel = channel; +} + +static inline void cyw43_wifi_ap_set_ssid(cyw43_t *self, size_t len, const uint8_t *buf) { +    self->ap_ssid_len = MIN(len, sizeof(self->ap_ssid)); +    memcpy(self->ap_ssid, buf, self->ap_ssid_len); +} + +static inline void cyw43_wifi_ap_set_password(cyw43_t *self, size_t len, const uint8_t *buf) { +    self->ap_key_len = MIN(len, sizeof(self->ap_key)); +    memcpy(self->ap_key, buf, self->ap_key_len); +} + +static inline void cyw43_wifi_ap_set_auth(cyw43_t *self, uint32_t auth) { +    self->ap_auth = auth; +} + +void cyw43_wifi_ap_get_stas(cyw43_t *self, int *num_stas, uint8_t *macs); + +void cyw43_tcpip_init(cyw43_t *self, int itf); +void cyw43_tcpip_deinit(cyw43_t *self, int itf); +void cyw43_tcpip_set_link_up(cyw43_t *self, int itf); +void cyw43_tcpip_set_link_down(cyw43_t *self, int itf); +int cyw43_tcpip_link_status(cyw43_t *self, int itf); + +#endif // MICROPY_INCLUDED_STM32_CYW43_H diff --git a/drivers/cyw43/cyw43_ctrl.c b/drivers/cyw43/cyw43_ctrl.c new file mode 100644 index 000000000..3a8bbf864 --- /dev/null +++ b/drivers/cyw43/cyw43_ctrl.c @@ -0,0 +1,588 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <string.h> + +#include "py/mphal.h" +#include "drivers/cyw43/cyw43.h" +#include "pendsv.h" +#include "sdio.h" + +#define CYW_ENTER MICROPY_PY_LWIP_ENTER +#define CYW_EXIT MICROPY_PY_LWIP_EXIT + +#ifdef pyb_pin_WL_HOST_WAKE +#define USE_SDIOIT (0) +#else +#define USE_SDIOIT (1) +#endif + +#define CYW43_SLEEP_MAX (50) + +#define WIFI_JOIN_STATE_ACTIVE  (0x0001) +#define WIFI_JOIN_STATE_FAIL    (0x0002) +#define WIFI_JOIN_STATE_NONET   (0x0003) +#define WIFI_JOIN_STATE_BADAUTH (0x0004) +#define WIFI_JOIN_STATE_AUTH    (0x0200) +#define WIFI_JOIN_STATE_LINK    (0x0400) +#define WIFI_JOIN_STATE_KEYED   (0x0800) +#define WIFI_JOIN_STATE_ALL     (0x0e01) + +cyw43_t cyw43_state; +void (*cyw43_poll)(void); +uint32_t cyw43_sleep; + +STATIC void cyw43_poll_func(void); +STATIC void cyw43_wifi_ap_init(cyw43_t *self); +STATIC void cyw43_wifi_ap_set_up(cyw43_t *self, bool up); + +static inline uint32_t cyw43_get_be16(const uint8_t *buf) { +    return buf[0] << 8 | buf[1]; +} + +static inline uint32_t cyw43_get_be32(const uint8_t *buf) { +    return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; +} + +static inline void cyw43_delay_ms(uint32_t ms) { +    mp_hal_delay_ms(ms); +} + +/*******************************************************************************/ +// Initialisation and polling + +void cyw43_init(cyw43_t *self) { +    #ifdef pyb_pin_WL_HOST_WAKE +    mp_hal_pin_config(pyb_pin_WL_HOST_WAKE, MP_HAL_PIN_MODE_INPUT, MP_HAL_PIN_PULL_NONE, 0); +    #endif +    mp_hal_pin_config(pyb_pin_WL_REG_ON, MP_HAL_PIN_MODE_OUTPUT, MP_HAL_PIN_PULL_NONE, 0); +    mp_hal_pin_low(pyb_pin_WL_REG_ON); +    #ifdef pyb_pin_WL_RFSW_VDD +    mp_hal_pin_config(pyb_pin_WL_RFSW_VDD, MP_HAL_PIN_MODE_OUTPUT, MP_HAL_PIN_PULL_NONE, 0); // RF-switch power +    mp_hal_pin_low(pyb_pin_WL_RFSW_VDD); +    #endif + +    cyw43_ll_init(&self->cyw43_ll, self); + +    self->itf_state = 0; +    self->wifi_scan_state = 0; +    self->wifi_join_state = 0; +    self->pend_disassoc = false; +    self->pend_rejoin= false; +    self->pend_rejoin_wpa = false; +    self->ap_channel = 3; +    self->ap_ssid_len = 0; +    self->ap_key_len = 0; + +    cyw43_poll = NULL; +} + +void cyw43_deinit(cyw43_t *self) { +    CYW_ENTER + +    cyw43_ll_bus_sleep(&self->cyw43_ll, true); +    cyw43_delay_ms(2); +    cyw43_tcpip_deinit(self, 0); +    cyw43_tcpip_deinit(self, 1); + +    self->itf_state = 0; + +    // Disable async polling +    SDMMC1->MASK &= ~SDMMC_MASK_SDIOITIE; +    cyw43_poll = NULL; + +    #ifdef pyb_pin_WL_RFSW_VDD +    // Turn the RF-switch off +    mp_hal_pin_low(pyb_pin_WL_RFSW_VDD); +    #endif + +    // Power down the WL chip and the SDIO bus +    mp_hal_pin_low(pyb_pin_WL_REG_ON); +    sdio_deinit(); + +    CYW_EXIT +} + +STATIC int cyw43_ensure_up(cyw43_t *self) { +    if (cyw43_poll != NULL) { +        cyw43_ll_bus_sleep(&self->cyw43_ll, false); +        return 0; +    } + +    CYW_ENTER + +    // Disable the netif if it was previously up +    cyw43_tcpip_deinit(self, CYW43_ITF_STA); +    cyw43_tcpip_deinit(self, CYW43_ITF_AP); +    self->itf_state = 0; + +    // Reset and power up the WL chip +    mp_hal_pin_low(pyb_pin_WL_REG_ON); +    cyw43_delay_ms(20); +    mp_hal_pin_high(pyb_pin_WL_REG_ON); +    cyw43_delay_ms(50); + +    // Initialise SDIO bus +    // IRQ priority only needs to be higher than CYW_ENTER/EXIT protection (PENDSV) +    sdio_init(NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 14, 0)); + +    // Initialise the low-level driver +    uint8_t mac[6]; +    mp_hal_get_mac(MP_HAL_MAC_WLAN0, mac); +    int ret = cyw43_ll_bus_init(&self->cyw43_ll, mac); + +    if (ret != 0) { +        CYW_EXIT +        return ret; +    } + +    // Enable async events from low-level driver +    cyw43_sleep = CYW43_SLEEP_MAX; +    cyw43_poll = cyw43_poll_func; +    #if USE_SDIOIT +    SDMMC1->MASK |= SDMMC_MASK_SDIOITIE; +    #else +    extern void extint_set(const pin_obj_t *pin, uint32_t mode); +    extint_set(pyb_pin_WL_HOST_WAKE, GPIO_MODE_IT_FALLING); +    #endif + +    CYW_EXIT + +    return ret; +} + +// This function must always be executed at the level where CYW_ENTER is effectively active +STATIC void cyw43_poll_func(void) { +    if (cyw43_poll == NULL) { +        // Poll scheduled during deinit, just ignore it +        return; +    } + +    cyw43_t *self = &cyw43_state; +    cyw43_ll_process_packets(&self->cyw43_ll); + +    if (self->pend_disassoc) { +        self->pend_disassoc = false; +        cyw43_ll_ioctl(&self->cyw43_ll, CYW43_IOCTL_SET_DISASSOC, 0, NULL, CYW43_ITF_STA); +    } + +    if (self->pend_rejoin_wpa) { +        self->pend_rejoin_wpa = false; +        cyw43_ll_wifi_set_wpa_auth(&self->cyw43_ll); +    } + +    if (self->pend_rejoin) { +        self->pend_rejoin = false; +        cyw43_ll_wifi_rejoin(&self->cyw43_ll); +        self->wifi_join_state = WIFI_JOIN_STATE_ACTIVE; +    } + +    if (cyw43_sleep == 0) { +        cyw43_ll_bus_sleep(&self->cyw43_ll, true); +        #if !USE_SDIOIT +        sdio_deinit(); // save power while WLAN bus sleeps +        #endif +    } + +    #if USE_SDIOIT +    SDMMC1->MASK |= SDMMC_MASK_SDIOITIE; +    #endif +} + +/*******************************************************************************/ +// Callback interface to low-level driver + +int cyw43_cb_read_host_interrupt_pin(void *cb_data) { +    #ifdef pyb_pin_WL_HOST_WAKE +    return mp_hal_pin_read(pyb_pin_WL_HOST_WAKE); +    #else +    return mp_hal_pin_read(pyb_pin_WL_SDIO_1); +    #endif +} + +void cyw43_cb_ensure_awake(void *cb_data) { +    cyw43_sleep = CYW43_SLEEP_MAX; +    #if !USE_SDIOIT +    if (__HAL_RCC_SDMMC1_IS_CLK_DISABLED()) { +        __HAL_RCC_SDMMC1_CLK_ENABLE(); // enable SDIO peripheral +        sdio_enable_high_speed_4bit(); +    } +    #endif +} + +STATIC const char *cyw43_async_event_name_table[89] = { +    [0 ... 88] = NULL, +    [CYW43_EV_SET_SSID] = "SET_SSID", +    [CYW43_EV_JOIN] = "JOIN", +    [CYW43_EV_AUTH] = "AUTH", +    [CYW43_EV_DEAUTH_IND] = "DEAUTH_IND", +    [CYW43_EV_ASSOC] = "ASSOC", +    [CYW43_EV_DISASSOC] = "DISASSOC", +    [CYW43_EV_DISASSOC_IND] = "DISASSOC_IND", +    [CYW43_EV_LINK] = "LINK", +    [CYW43_EV_PSK_SUP] = "PSK_SUP", +    [CYW43_EV_ESCAN_RESULT] = "ESCAN_RESULT", +    [CYW43_EV_CSA_COMPLETE_IND] = "CSA_COMPLETE_IND", +    [CYW43_EV_ASSOC_REQ_IE] = "ASSOC_REQ_IE", +    [CYW43_EV_ASSOC_RESP_IE] = "ASSOC_RESP_IE", +}; + +STATIC void cyw43_dump_async_event(const cyw43_async_event_t *ev) { +    printf("[% 8d] ASYNC(%04x,", +        mp_hal_ticks_ms(), +        (unsigned int)ev->flags +    ); +    if (ev->event_type < MP_ARRAY_SIZE(cyw43_async_event_name_table) +        && cyw43_async_event_name_table[ev->event_type] != NULL) { +        printf("%s", cyw43_async_event_name_table[ev->event_type]); +    } else { +        printf("%u", (unsigned int)ev->event_type); +    } +    printf(",%u,%u,%u)\n", +        (unsigned int)ev->status, +        (unsigned int)ev->reason, +        (unsigned int)ev->interface +    ); +} + +void cyw43_cb_process_async_event(void *cb_data, const cyw43_async_event_t *ev) { +    cyw43_t *self = cb_data; + +    if (self->trace_flags & CYW43_TRACE_ASYNC_EV) { +        cyw43_dump_async_event(ev); +    } + +    if (ev->event_type == CYW43_EV_ESCAN_RESULT && self->wifi_scan_state == 1) { +        // Escan result event +        if (ev->status == 8) { +            // Partial result +            int ret = self->wifi_scan_cb(self->wifi_scan_env, &ev->u.scan_result); +            if (ret != 0) { +                // TODO need to abort scan, or just ignore any more results +            } +        } else if (ev->status == 0) { +            // Scan complete +            self->wifi_scan_state = 2; +        } + +    } else if (ev->event_type == CYW43_EV_DISASSOC) { +        cyw43_tcpip_set_link_down(self, CYW43_ITF_STA); +        self->wifi_join_state = 0x0000; + +    /* +    } else if (ev->event_type == CYW43_EV_DISASSOC_IND) { +        if (ev->interface == CYW43_ITF_AP) { +            // Station disassociated with our AP, let DHCP server know so it can free the IP address +            dhcp_server_disassoc(&self->dhcp_server, buf + 24); +        } +    */ + +    // WiFi join events +    } else if (ev->event_type == CYW43_EV_PRUNE) { +        if (ev->status == 0 && ev->reason == 8) { +            // RSN mismatch, retry join with WPA auth +            self->pend_rejoin = true; +            self->pend_rejoin_wpa = true; +            pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, cyw43_poll_func); +        } +    } else if (ev->event_type == CYW43_EV_SET_SSID) { +        if (ev->status == 0) { +            // Success setting SSID +        } else if (ev->status == 3 && ev->reason == 0) { +            self->wifi_join_state = WIFI_JOIN_STATE_NONET; +            // No matching SSID found (could be out of range, or down) +        } else { +            // Other failure setting SSID +            self->wifi_join_state = WIFI_JOIN_STATE_FAIL; +        } +    } else if (ev->event_type == CYW43_EV_AUTH) { +        if (ev->status == 0) { +            self->wifi_join_state |= WIFI_JOIN_STATE_AUTH; +        } else if (ev->status == 6) { +            // Unsolicited auth packet, ignore it +        } else { +            // Cannot authenticate +            self->wifi_join_state = WIFI_JOIN_STATE_BADAUTH; +        } +    } else if (ev->event_type == CYW43_EV_DEAUTH_IND) { +        if (ev->status == 0 && ev->reason == 2) { +            // Deauth, probably because password was wrong; disassociate +            self->pend_disassoc = true; +            pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, cyw43_poll_func); +        } +    } else if (ev->event_type == CYW43_EV_LINK) { +        if (ev->status == 0) { +            if (ev->flags & 1) { +                // Link is up +                if (ev->interface == CYW43_ITF_STA) { +                    self->wifi_join_state |= WIFI_JOIN_STATE_LINK; +                } else { +                    cyw43_tcpip_set_link_up(self, ev->interface); +                } +            } else { +                // Link is down +                cyw43_tcpip_set_link_down(self, ev->interface); +            } +        } +    } else if (ev->event_type == CYW43_EV_PSK_SUP) { +        if (ev->status == 6) { // WLC_SUP_KEYED +            self->wifi_join_state |= WIFI_JOIN_STATE_KEYED; +        } else if ((ev->status == 4 || ev->status == 8 || ev->status == 11) && ev->reason == 15) { +            // Timeout waiting for key exchange M1/M3/G1 +            // Probably at edge of the cell, retry +            self->pend_rejoin = true; +            pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, cyw43_poll_func); +        } else { +            // PSK_SUP failure +            self->wifi_join_state = WIFI_JOIN_STATE_BADAUTH; +        } +    } + +    if (self->wifi_join_state == WIFI_JOIN_STATE_ALL) { +        // STA connected +        self->wifi_join_state = WIFI_JOIN_STATE_ACTIVE; +        cyw43_tcpip_set_link_up(self, CYW43_ITF_STA); +    } +} + +/*******************************************************************************/ +// Ioctl and Ethernet interface + +int cyw43_ioctl(cyw43_t *self, uint32_t cmd, size_t len, uint8_t *buf, uint32_t iface) { +    int ret = cyw43_ensure_up(self); +    if (ret) { +        return ret; +    } + +    CYW_ENTER +    ret = cyw43_ll_ioctl(&self->cyw43_ll, cmd, len, buf, iface); +    CYW_EXIT + +    return ret; +} + +int cyw43_send_ethernet(cyw43_t *self, int itf, size_t len, const void *buf, bool is_pbuf) { +    int ret = cyw43_ensure_up(self); +    if (ret) { +        return ret; +    } + +    CYW_ENTER +    ret = cyw43_ll_send_ethernet(&self->cyw43_ll, itf, len, buf, is_pbuf); +    CYW_EXIT + +    return ret; +} + +/*******************************************************************************/ +// WiFi control + +STATIC int cyw43_wifi_on(cyw43_t *self, uint32_t country) { +    int ret = cyw43_ensure_up(self); +    if (ret) { +        return ret; +    } + +    #ifdef pyb_pin_WL_RFSW_VDD +    // Turn the RF-switch on +    mp_hal_pin_high(pyb_pin_WL_RFSW_VDD); +    #endif + +    CYW_ENTER +    ret = cyw43_ll_wifi_on(&self->cyw43_ll, country); +    CYW_EXIT + +    return ret; +} + +int cyw43_wifi_pm(cyw43_t *self, uint32_t pm_in) { +    int ret = cyw43_ensure_up(self); +    if (ret) { +        return ret; +    } + +    // pm_in: 0x00adbrrm +    uint32_t pm = pm_in & 0xf; +    uint32_t pm_sleep_ret = (pm_in >> 4) & 0xff; +    uint32_t li_bcn = (pm_in >> 12) & 0xf; +    uint32_t li_dtim = (pm_in >> 16) & 0xf; +    uint32_t li_assoc = (pm_in >> 20) & 0xf; + +    CYW_ENTER +    ret = cyw43_ll_wifi_pm(&self->cyw43_ll, pm, pm_sleep_ret, li_bcn, li_dtim, li_assoc); +    CYW_EXIT + +    return ret; +} + +int cyw43_wifi_get_mac(cyw43_t *self, int itf, uint8_t mac[6]) { +    mp_hal_get_mac(MP_HAL_MAC_WLAN0, &mac[0]); +    return 0; +} + +#define MAKE_COUNTRY(a, b, rev) ((a) | (b) << 8 | (rev) << 16) + +void cyw43_wifi_set_up(cyw43_t *self, int itf, bool up) { +    if (up) { +        if (self->itf_state == 0) { +            uint32_t country; +            extern char pyb_country_code[2]; +            if (pyb_country_code[0] == '\0' || pyb_country_code[1] == '\0') { +                country = MAKE_COUNTRY('X', 'X', 17); // default to world-wide (passive ch 12-14) +            } else { +                country = MAKE_COUNTRY(pyb_country_code[0], pyb_country_code[1], 0); +            } +            cyw43_wifi_on(self, country); +            cyw43_wifi_pm(self, 10 << 20 | 1 << 16 | 1 << 12 | 20 << 4 | 2); +        } +        if (itf == CYW43_ITF_AP) { +            cyw43_wifi_ap_init(self); +            cyw43_wifi_ap_set_up(self, true); +        } +        if ((self->itf_state & (1 << itf)) == 0) { +            CYW_ENTER +            cyw43_tcpip_deinit(self, itf); +            cyw43_tcpip_init(self, itf); +            self->itf_state |= 1 << itf; +            CYW_EXIT +        } +    } else { +        if (itf == CYW43_ITF_AP) { +            cyw43_wifi_ap_set_up(self, false); +        } +    } +} + +int cyw43_wifi_scan(cyw43_t *self, cyw43_wifi_scan_options_t *opts, void *env, int (*result_cb)(void*, const cyw43_ev_scan_result_t*)) { +    if (self->itf_state == 0) { +        return -1; +    } + +    cyw43_ensure_up(self); + +    CYW_ENTER + +    // Set state and callback data +    self->wifi_scan_state = 1; +    self->wifi_scan_env = env; +    self->wifi_scan_cb = result_cb; + +    // Start the scan +    int ret = cyw43_ll_wifi_scan(&self->cyw43_ll, opts); + +    CYW_EXIT + +    return ret; +} + +int cyw43_wifi_link_status(cyw43_t *self, int itf) { +    if (itf == CYW43_ITF_STA) { +        int s = self->wifi_join_state & 0xf; +        if (s == WIFI_JOIN_STATE_ACTIVE) { +            return CYW43_LINK_JOIN; +        } else if (s == WIFI_JOIN_STATE_FAIL) { +            return CYW43_LINK_FAIL; +        } else if (s == WIFI_JOIN_STATE_NONET) { +            return CYW43_LINK_NONET; +        } else if (s == WIFI_JOIN_STATE_BADAUTH) { +            return CYW43_LINK_BADAUTH; +        } else { +            return CYW43_LINK_DOWN; +        } +    } else { +        return CYW43_LINK_DOWN; +    } +} + +/*******************************************************************************/ +// WiFi STA + +int cyw43_wifi_join(cyw43_t *self, size_t ssid_len, const uint8_t *ssid, size_t key_len, const uint8_t *key, uint32_t auth_type, const uint8_t *bssid, uint32_t channel) { +    int ret = cyw43_ensure_up(self); +    if (ret) { +        return ret; +    } + +    CYW_ENTER + +    ret = cyw43_ll_wifi_join(&self->cyw43_ll, ssid_len, ssid, key_len, key, auth_type, bssid, channel); + +    if (ret == 0) { +        // Wait for responses: EV_AUTH, EV_LINK, EV_SET_SSID, EV_PSK_SUP +        // Will get EV_DEAUTH_IND if password is invalid +        self->wifi_join_state = WIFI_JOIN_STATE_ACTIVE; + +        if (auth_type == 0) { +            // For open security we don't need EV_PSK_SUP, so set that flag indicator now +            self->wifi_join_state |= WIFI_JOIN_STATE_KEYED; +        } +    } + +    CYW_EXIT + +    return ret; +} + +int cyw43_wifi_leave(cyw43_t *self, int itf) { +    // Disassociate with SSID +    return cyw43_ioctl(self, CYW43_IOCTL_SET_DISASSOC, 0, NULL, itf); +} + +/*******************************************************************************/ +// WiFi AP + +STATIC void cyw43_wifi_ap_init(cyw43_t *self) { +    int ret = cyw43_ensure_up(self); +    if (ret) { +        return; +    } + +    CYW_ENTER +    cyw43_ll_wifi_ap_init(&self->cyw43_ll, self->ap_ssid_len, self->ap_ssid, self->ap_auth, self->ap_key_len, self->ap_key, self->ap_channel); +    CYW_EXIT +} + +STATIC void cyw43_wifi_ap_set_up(cyw43_t *self, bool up) { +    int ret = cyw43_ensure_up(self); +    if (ret) { +        return; +    } + +    CYW_ENTER +    cyw43_ll_wifi_ap_set_up(&self->cyw43_ll, up); +    CYW_EXIT +} + +void cyw43_wifi_ap_get_stas(cyw43_t *self, int *num_stas, uint8_t *macs) { +    int ret = cyw43_ensure_up(self); +    if (ret) { +        return; +    } + +    CYW_ENTER +    cyw43_ll_wifi_ap_get_stas(&self->cyw43_ll, num_stas, macs); +    CYW_EXIT +} diff --git a/drivers/cyw43/cyw43_ll.h b/drivers/cyw43/cyw43_ll.h new file mode 100644 index 000000000..879367a2e --- /dev/null +++ b/drivers/cyw43/cyw43_ll.h @@ -0,0 +1,135 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_STM32_CYW43_LL_H +#define MICROPY_INCLUDED_STM32_CYW43_LL_H + +// IOCTL commands +#define CYW43_IOCTL_GET_SSID            (0x32) +#define CYW43_IOCTL_GET_CHANNEL         (0x3a) +#define CYW43_IOCTL_SET_DISASSOC        (0x69) +#define CYW43_IOCTL_GET_ANTDIV          (0x7e) +#define CYW43_IOCTL_SET_ANTDIV          (0x81) +#define CYW43_IOCTL_SET_MONITOR         (0xd9) +#define CYW43_IOCTL_GET_VAR             (0x20c) +#define CYW43_IOCTL_SET_VAR             (0x20f) + +// Async events, event_type field +#define CYW43_EV_SET_SSID               (0) +#define CYW43_EV_JOIN                   (1) +#define CYW43_EV_AUTH                   (3) +#define CYW43_EV_DEAUTH_IND             (6) +#define CYW43_EV_ASSOC                  (7) +#define CYW43_EV_DISASSOC               (11) +#define CYW43_EV_DISASSOC_IND           (12) +#define CYW43_EV_LINK                   (16) +#define CYW43_EV_PRUNE                  (23) +#define CYW43_EV_PSK_SUP                (46) +#define CYW43_EV_ESCAN_RESULT           (69) +#define CYW43_EV_CSA_COMPLETE_IND       (80) +#define CYW43_EV_ASSOC_REQ_IE           (87) +#define CYW43_EV_ASSOC_RESP_IE          (88) + +enum { +    CYW43_ITF_STA, +    CYW43_ITF_AP, +}; + +typedef struct _cyw43_ev_scan_result_t { +    uint32_t _0[5]; +    uint8_t bssid[6]; +    uint16_t _1[2]; +    uint8_t ssid_len; +    uint8_t ssid[32]; +    uint32_t _2[5]; +    uint16_t channel; +    uint16_t _3; +    uint8_t auth_mode; +    int16_t rssi; +} cyw43_ev_scan_result_t; + +typedef struct _cyw43_async_event_t { +    uint16_t _0; +    uint16_t flags; +    uint32_t event_type; +    uint32_t status; +    uint32_t reason; +    uint8_t _1[30]; +    uint8_t interface; +    uint8_t _2; +    union { +        cyw43_ev_scan_result_t scan_result; +    } u; +} cyw43_async_event_t; + +typedef struct _cyw43_wifi_scan_options_t { +    uint32_t version; +    uint16_t action; +    uint16_t _; +    uint32_t ssid_len; // 0 to select all +    uint8_t ssid[32]; +    uint8_t bssid[6]; +    int8_t bss_type; // fill with 0xff to select all +    int8_t scan_type; // 0=active, 1=passive +    int32_t nprobes; +    int32_t active_time; +    int32_t passive_time; +    int32_t home_time; +    int32_t channel_num; +    uint16_t channel_list[1]; +} cyw43_wifi_scan_options_t; + +typedef struct _cyw43_ll_t { +    uint32_t opaque[528]; +} cyw43_ll_t; + +void cyw43_ll_init(cyw43_ll_t *self, void *cb_data); +void cyw43_ll_deinit(cyw43_ll_t *self); + +int cyw43_ll_bus_init(cyw43_ll_t *self, const uint8_t *mac); +void cyw43_ll_bus_sleep(cyw43_ll_t *self, bool can_sleep); +void cyw43_ll_process_packets(cyw43_ll_t *self); +int cyw43_ll_ioctl(cyw43_ll_t *self, uint32_t cmd, size_t len, uint8_t *buf, uint32_t iface); +int cyw43_ll_send_ethernet(cyw43_ll_t *self, int itf, size_t len, const void *buf, bool is_pbuf); + +int cyw43_ll_wifi_on(cyw43_ll_t *self, uint32_t country); +int cyw43_ll_wifi_pm(cyw43_ll_t *self, uint32_t pm, uint32_t pm_sleep_ret, uint32_t li_bcn, uint32_t li_dtim, uint32_t li_assoc); +int cyw43_ll_wifi_scan(cyw43_ll_t *self, cyw43_wifi_scan_options_t *opts); + +int cyw43_ll_wifi_join(cyw43_ll_t *self, size_t ssid_len, const uint8_t *ssid, size_t key_len, const uint8_t *key, uint32_t auth_type, const uint8_t *bssid, uint32_t channel); +void cyw43_ll_wifi_set_wpa_auth(cyw43_ll_t *self); +void cyw43_ll_wifi_rejoin(cyw43_ll_t *self); + +int cyw43_ll_wifi_ap_init(cyw43_ll_t *self, size_t ssid_len, const uint8_t *ssid, uint32_t auth, size_t key_len, const uint8_t *key, uint32_t channel); +int cyw43_ll_wifi_ap_set_up(cyw43_ll_t *self, bool up); +int cyw43_ll_wifi_ap_get_stas(cyw43_ll_t *self, int *num_stas, uint8_t *macs); + +// Callbacks to be provided by mid-level interface +int cyw43_cb_read_host_interrupt_pin(void *cb_data); +void cyw43_cb_ensure_awake(void *cb_data); +void cyw43_cb_process_async_event(void *cb_data, const cyw43_async_event_t *ev); +void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf); + +#endif // MICROPY_INCLUDED_STM32_CYW43_LL_H diff --git a/drivers/cyw43/cyw43_lwip.c b/drivers/cyw43/cyw43_lwip.c new file mode 100644 index 000000000..8f4223029 --- /dev/null +++ b/drivers/cyw43/cyw43_lwip.c @@ -0,0 +1,197 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <string.h> + +#include "py/mphal.h" +#include "lib/netutils/netutils.h" +#include "lwip/etharp.h" +#include "lwip/dns.h" +#include "lwip/apps/mdns.h" +#include "drivers/cyw43/cyw43.h" + +STATIC void cyw43_ethernet_trace(cyw43_t *self, struct netif *netif, size_t len, const void *data, unsigned int flags) { +    bool is_tx = flags & NETUTILS_TRACE_IS_TX; +    if ((is_tx && (self->trace_flags & CYW43_TRACE_ETH_TX)) +        || (!is_tx && (self->trace_flags & CYW43_TRACE_ETH_RX))) { +        const uint8_t *buf; +        if (len == (size_t)-1) { +            // data is a pbuf +            const struct pbuf *pbuf = data; +            buf = pbuf->payload; +            len = pbuf->len; // restricted to print only the first chunk of the pbuf +        } else { +            // data is actual data buffer +            buf = data; +        } + +        if (self->trace_flags & CYW43_TRACE_MAC) { +            printf("[% 8d] ETH%cX itf=%c%c len=%u", mp_hal_ticks_ms(), is_tx ? 'T' : 'R', netif->name[0], netif->name[1], len); +            printf(" MAC type=%d subtype=%d data=", buf[0] >> 2 & 3, buf[0] >> 4); +            for (size_t i = 0; i < len; ++i) { +                printf(" %02x", buf[i]); +            } +            printf("\n"); +            return; +        } + +        if (self->trace_flags & CYW43_TRACE_ETH_FULL) { +            flags |= NETUTILS_TRACE_PAYLOAD; +        } +        netutils_ethernet_trace(MP_PYTHON_PRINTER, len, buf, flags); +    } +} + +STATIC err_t cyw43_netif_output(struct netif *netif, struct pbuf *p) { +    cyw43_t *self = netif->state; +    if (self->trace_flags != 0) { +        cyw43_ethernet_trace(self, netif, (size_t)-1, p, NETUTILS_TRACE_IS_TX | NETUTILS_TRACE_NEWLINE); +    } +    int itf = netif->name[1] - '0'; +    int ret = cyw43_send_ethernet(self, itf, p->tot_len, (void*)p, true); +    if (ret) { +        printf("[CYW43] send_ethernet failed: %d\n", ret); +        return ERR_IF; +    } +    return ERR_OK; +} + +STATIC err_t cyw43_netif_init(struct netif *netif) { +    netif->linkoutput = cyw43_netif_output; +    netif->output = etharp_output; +    netif->mtu = 1500; +    netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP; +    cyw43_wifi_get_mac(netif->state, netif->name[1] - '0', netif->hwaddr); +    netif->hwaddr_len = sizeof(netif->hwaddr); +    return ERR_OK; +} + +void cyw43_tcpip_init(cyw43_t *self, int itf) { +    ip_addr_t ipconfig[4]; +    #if LWIP_IPV6 +    #define IP(x) ((x).u_addr.ip4) +    #else +    #define IP(x) (x) +    #endif +    if (itf == 0) { +        // need to zero out to get isconnected() working +        IP4_ADDR(&IP(ipconfig[0]), 0, 0, 0, 0); +        IP4_ADDR(&IP(ipconfig[2]), 192, 168, 0, 1); +    } else { +        IP4_ADDR(&IP(ipconfig[0]), 192, 168, 4, 1); +        IP4_ADDR(&IP(ipconfig[2]), 192, 168, 4, 1); +    } +    IP4_ADDR(&IP(ipconfig[1]), 255, 255, 255, 0); +    IP4_ADDR(&IP(ipconfig[3]), 8, 8, 8, 8); +    #undef IP + +    struct netif *n = &self->netif[itf]; +    n->name[0] = 'w'; +    n->name[1] = '0' + itf; +    #if LWIP_IPV6 +    netif_add(n, &ipconfig[0].u_addr.ip4, &ipconfig[1].u_addr.ip4, &ipconfig[2].u_addr.ip4, self, cyw43_netif_init, ethernet_input); +    #else +    netif_add(n, &ipconfig[0], &ipconfig[1], &ipconfig[2], self, cyw43_netif_init, netif_input); +    #endif +    netif_set_hostname(n, "PYBD"); +    netif_set_default(n); +    netif_set_up(n); + +    if (itf == CYW43_ITF_STA) { +        dns_setserver(0, &ipconfig[3]); +        dhcp_set_struct(n, &self->dhcp_client); +        dhcp_start(n); +    } else { +        dhcp_server_init(&self->dhcp_server, &ipconfig[0], &ipconfig[1]); +    } + +    #if LWIP_MDNS_RESPONDER +    // TODO better to call after IP address is set +    char mdns_hostname[9]; +    memcpy(&mdns_hostname[0], "PYBD", 4); +    mp_hal_get_mac_ascii(MP_HAL_MAC_WLAN0, 8, 4, &mdns_hostname[4]); +    mdns_hostname[8] = '\0'; +    mdns_resp_add_netif(n, mdns_hostname, 60); +    #endif +} + +void cyw43_tcpip_deinit(cyw43_t *self, int itf) { +    struct netif *n = &self->netif[itf]; +    if (itf == CYW43_ITF_STA) { +        dhcp_stop(n); +    } else { +        dhcp_server_deinit(&self->dhcp_server); +    } +    #if LWIP_MDNS_RESPONDER +    mdns_resp_remove_netif(n); +    #endif +    for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) { +        if (netif == n) { +            netif_remove(netif); +            netif->ip_addr.addr = 0; +            netif->flags = 0; +        } +    } +} + +void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) { +    cyw43_t *self = cb_data; +    struct netif *netif = &self->netif[itf]; +    if (self->trace_flags) { +        cyw43_ethernet_trace(self, netif, len, buf, NETUTILS_TRACE_NEWLINE); +    } +    if (netif->flags & NETIF_FLAG_LINK_UP) { +        struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); +        if (p != NULL) { +            pbuf_take(p, buf, len); +            if (netif->input(p, netif) != ERR_OK) { +                pbuf_free(p); +            } +        } +    } +} + +void cyw43_tcpip_set_link_up(cyw43_t *self, int itf) { +    netif_set_link_up(&self->netif[itf]); +} + +void cyw43_tcpip_set_link_down(cyw43_t *self, int itf) { +    netif_set_link_down(&self->netif[itf]); +} + +int cyw43_tcpip_link_status(cyw43_t *self, int itf) { +    struct netif *netif = &self->netif[itf]; +    if ((netif->flags & (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP)) +        == (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP)) { +        if (netif->ip_addr.addr != 0) { +            return CYW43_LINK_UP; +        } else { +            return CYW43_LINK_NOIP; +        } +    } else { +        return cyw43_wifi_link_status(self, itf); +    } +} diff --git a/drivers/cyw43/libcyw43.a b/drivers/cyw43/libcyw43.aBinary files differ new file mode 100644 index 000000000..7d0ff93dc --- /dev/null +++ b/drivers/cyw43/libcyw43.a | 
