Update Files

This commit is contained in:
2025-01-22 17:22:38 +01:00
parent 89b9349629
commit 4c5e729485
5132 changed files with 1195369 additions and 0 deletions

View File

@ -0,0 +1,96 @@
/*
* esp32 / esp-idf gpio
*
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
*
* 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 <libwebsockets.h>
static void
lws_gpio_esp32_mode(_lws_plat_gpio_t gpio, int flags)
{
int mode, pup = GPIO_FLOATING;
switch (flags & (LWSGGPIO_FL_READ | LWSGGPIO_FL_WRITE)) {
default:
lwsl_err("%s: neither read nor write\n", __func__);
return;
case LWSGGPIO_FL_READ:
mode = GPIO_MODE_INPUT;
break;
case LWSGGPIO_FL_WRITE:
mode = GPIO_MODE_OUTPUT;
break;
case LWSGGPIO_FL_READ | LWSGGPIO_FL_WRITE:
mode = GPIO_MODE_INPUT_OUTPUT;
break;
}
switch (flags & (LWSGGPIO_FL_PULLUP | LWSGGPIO_FL_PULLDOWN)) {
default:
break;
case LWSGGPIO_FL_PULLUP:
pup = GPIO_PULLUP_ONLY;
break;
case LWSGGPIO_FL_PULLDOWN:
pup = GPIO_PULLDOWN_ONLY;
break;
case LWSGGPIO_FL_PULLUP | LWSGGPIO_FL_PULLDOWN:
pup = GPIO_PULLUP_PULLDOWN;
break;
}
gpio_reset_pin(gpio);
gpio_set_direction(gpio, mode);
gpio_set_pull_mode(gpio, pup);
gpio_set_level(gpio, flags & LWSGGPIO_FL_START_LOW ? 0 : 1);
}
static int
lws_gpio_esp32_read(_lws_plat_gpio_t gpio)
{
return gpio_get_level(gpio);
}
static void
lws_gpio_esp32_set(_lws_plat_gpio_t gpio, int val)
{
gpio_set_level(gpio, val);
}
static int
lws_gpio_esp32_irq_mode(_lws_plat_gpio_t gpio, lws_gpio_irq_t irq_type,
lws_gpio_irq_cb_t cb, void *arg)
{
if (gpio_set_intr_type(gpio, irq_type))
return 1;
if (cb)
return gpio_isr_handler_add(gpio, cb, arg);
return gpio_isr_handler_remove(gpio);
}
const lws_gpio_ops_t lws_gpio_plat = {
.mode = lws_gpio_esp32_mode,
.read = lws_gpio_esp32_read,
.set = lws_gpio_esp32_set,
.irq_mode = lws_gpio_esp32_irq_mode,
};

View File

@ -0,0 +1,25 @@
/*
* lws generic gpio - esp32 platform wrapper
*
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
*
* 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.
*/
extern const lws_gpio_ops_t lws_gpio_plat;

View File

@ -0,0 +1,498 @@
/*
* libwebsockets - esp32 wifi -> lws_netdev_wifi
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* 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.
*
*
* These are the esp platform wifi-specific netdev pieces. Nothing else should
* know any esp-specific apis.
*
* Operations happen via the generic lws_detdev instantiation for the platform
* wifi device, which point in here for operations. We also set up native OS
* event hooks per device for wifi and IP stack events, and post them as lws_smd
* NETWORK events on the if in the "platform private" namespace. We then
* service the events in the lws event loop thread context, which may again
* generate lws_smd NETWORK events in the public namespace depending on what
* happened.
*
* Scan requests go through a sul to make sure we don't get "piling on" from
* scheduled, timed scans. Scan results go through the lws_smd "washing" and
* are actually parsed in lws thread context, where they are converted to lws
* netdev scan results and processed by generic code.
*/
#include "private-lib-core.h"
#include "esp_system.h"
#include "spi_flash_mmap.h"
#include "esp_wifi.h"
#include <nvs_flash.h>
#include <esp_netif.h>
/*
* lws_netdev_instance_t:
* lws_netdev_instance_wifi_t:
* lws_netdev_instance_wifi_esp32_t
*/
typedef struct lws_netdev_instance_wifi_esp32 {
lws_netdev_instance_wifi_t wnd;
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
wifi_config_t sta_config;
} lws_netdev_instance_wifi_esp32_t;
/*
static wifi_config_t config = {
.ap = {
.channel = 6,
.authmode = WIFI_AUTH_OPEN,
.max_connection = 1,
} };
*/
/*
* Platform-specific connect / associate
*/
int
lws_netdev_wifi_connect_plat(lws_netdev_instance_t *nd, const char *ssid,
const char *passphrase, uint8_t *bssid)
{
lws_netdev_instance_wifi_esp32_t *wnde32 =
(lws_netdev_instance_wifi_esp32_t *)nd;
wnde32->wnd.inst.ops->up(&wnde32->wnd.inst);
wnde32->wnd.flags |= LNDIW_MODE_STA;
esp_wifi_set_mode(WIFI_MODE_STA);
#if 0
/* we will do our own dhcp */
tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA);
#endif
lws_strncpy((char *)wnde32->sta_config.sta.ssid, ssid,
sizeof(wnde32->sta_config.sta.ssid));
lws_strncpy((char *)wnde32->sta_config.sta.password, passphrase,
sizeof(wnde32->sta_config.sta.password));
esp_wifi_set_config(WIFI_IF_STA, &wnde32->sta_config);
esp_wifi_connect();
return 0;
}
/*
* This is called from the SMD / lws thread context, after we heard there were
* scan results on this netdev
*/
static void
lws_esp32_scan_update(lws_netdev_instance_wifi_t *wnd)
{
// lws_netdevs_t *netdevs = lws_netdevs_from_ndi(&wnd->inst);
wifi_ap_record_t ap_records[LWS_WIFI_MAX_SCAN_TRACK], *ar;
uint32_t now = lws_now_secs();
uint16_t count_ap_records;
int n;
count_ap_records = LWS_ARRAY_SIZE(ap_records);
if (esp_wifi_scan_get_ap_records(&count_ap_records, ap_records)) {
lwsl_err("%s: failed\n", __func__);
return;
}
if (!count_ap_records)
return;
if (wnd->state != LWSNDVWIFI_STATE_SCAN)
return;
/*
* ... let's collect the OS-specific scan results, and convert then to
* lws_netdev sorted by rssi. If we already have it in the scan list,
* keep it and keep a little ringbuffer of its rssi along with an
* averaging. If it's new, add it into the linked-list sorted by rssi.
*/
ar = &ap_records[0];
for (n = 0; n < count_ap_records; n++) {
lws_wifi_sta_t *w;
int m;
m = strlen((const char *)ar->ssid);
if (!m)
goto next;
/*
* We know this guy from before?
*/
w = lws_netdev_wifi_scan_find(wnd, (const char *)ar->ssid,
ar->bssid);
if (!w) {
w = lws_zalloc(sizeof(*w) + m + 1, __func__);
if (!w)
goto next;
w->ssid = (char *)&w[1];
memcpy(w->ssid, ar->ssid, m + 1);
w->ssid_len = m;
memcpy(w->bssid, ar->bssid, 6);
lws_dll2_add_sorted(&w->list, &wnd->scan,
lws_netdev_wifi_rssi_sort_compare);
}
if (w->rssi_count == LWS_ARRAY_SIZE(w->rssi))
w->rssi_avg -= w->rssi[w->rssi_next];
else
w->rssi_count++;
w->rssi[w->rssi_next] = ar->rssi;
w->rssi_avg += w->rssi[w->rssi_next++];
w->rssi_next = w->rssi_next & (LWS_ARRAY_SIZE(w->rssi) - 1);
w->ch = ar->primary;
w->authmode = ar->authmode;
w->last_seen = now;
next:
ar++;
}
/*
* We can do the rest of it using the generic scan list and credentials
*/
lws_netdev_wifi_scan_select(wnd);
}
static wifi_scan_config_t scan_config = {
.ssid = 0,
.bssid = 0,
.channel = 0,
.show_hidden = true
};
void
lws_netdev_wifi_scan_plat(lws_netdev_instance_t *nd)
{
lws_netdev_instance_wifi_t *wnd = (lws_netdev_instance_wifi_t *)nd;
if (esp_wifi_scan_start(&scan_config, false))
lwsl_err("%s: %s scan failed\n", __func__, wnd->inst.name);
}
/*
* Platform-private interface events turn up here after going through SMD and
* passed down by matching network interface name via generic lws_netdev. All
* that messing around gets us from an OS-specific thread with an event to back
* here in lws event loop thread context, with the same event bound to a the
* netdev it belongs to.
*/
int
lws_netdev_wifi_event_plat(struct lws_netdev_instance *nd, lws_usec_t timestamp,
void *buf, size_t len)
{
lws_netdev_instance_wifi_t *wnd = (lws_netdev_instance_wifi_t *)nd;
struct lws_context *ctx = netdev_instance_to_ctx(&wnd->inst);
size_t al;
/*
* netdev-private sync messages?
*/
if (!lws_json_simple_strcmp(buf, len, "\"type\":", "priv")) {
const char *ev = lws_json_simple_find(buf, len, "\"ev\":", &al);
if (!ev)
return 0;
lwsl_notice("%s: smd priv ev %.*s\n", __func__, (int)al, ev);
switch (atoi(ev)) {
case WIFI_EVENT_STA_START:
wnd->state = LWSNDVWIFI_STATE_INITIAL;
if (!lws_netdev_wifi_redo_last(wnd))
break;
/*
* if the "try last successful" one fails, start the
* scan by falling through
*/
case WIFI_EVENT_STA_DISCONNECTED:
lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
"{\"type\":\"linkdown\","
"\"if\":\"%s\"}", wnd->inst.name);
wnd->state = LWSNDVWIFI_STATE_SCAN;
/*
* We do it via the sul so we don't get timed scans
* on top of each other
*/
lws_sul_schedule(ctx, 0, &wnd->sul_scan,
lws_netdev_wifi_scan, 1);
break;
case WIFI_EVENT_STA_CONNECTED:
lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
"{\"type\":\"linkup\","
"\"if\":\"%s\"}", wnd->inst.name);
break;
case WIFI_EVENT_SCAN_DONE:
lws_esp32_scan_update(wnd);
break;
default:
return 0;
}
return 0;
}
return 0;
}
/*
* This is coming from a thread context unrelated to lws... the first order is
* to turn these into lws_smd events synchronized on lws thread, since we want
* to change correspsonding lws netdev object states without locking.
*/
static void
_event_handler_wifi(void *arg, esp_event_base_t event_base, int32_t event_id,
void *event_data)
{
lws_netdev_instance_wifi_t *wnd = (lws_netdev_instance_wifi_t *)arg;
struct lws_context *ctx = netdev_instance_to_ctx(&wnd->inst);
switch (event_id) {
case WIFI_EVENT_STA_START:
case WIFI_EVENT_STA_DISCONNECTED:
case WIFI_EVENT_SCAN_DONE:
case WIFI_EVENT_STA_CONNECTED:
/*
* These are events in the platform's private namespace,
* interpreted only by the lws_smd handler above, ** in the lws
* event thread context **. The point of this is to requeue the
* event in the lws thread context like a bottom-half.
*
* To save on registrations, the context's NETWORK smd
* participant passes messages to lws_netdev, who passes ones
* that have if matching the netdev name to that netdev's
* (*event) handler.
*
* The other handler may emit generic network state SMD events
* for other things to consume.
*/
lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
"{\"type\":\"priv\",\"if\":\"%s\",\"ev\":%d}",
wnd->inst.name, (int)event_id);
break;
default:
return;
}
}
#if 0
static int
espip_to_sa46(lws_sockaddr46 *sa46, esp_ip_addr_t *eip)
{
memset(sa46, 0, sizeof(sa46));
switch (eip->type) {
case ESP_IPADDR_TYPE_V4:
sa46->sa4.sin_family = AF_INET;
memcpy(sa46->sa4.sin_addr, &eip->u_addr.ip4.addr, );
return;
case ESP_IPADDR_TYPE_V6:
}
}
#endif
/*
* This is coming from a thread context unrelated to lws
*/
static void
_event_handler_ip(void *arg, esp_event_base_t event_base, int32_t event_id,
void *event_data)
{
lws_netdev_instance_wifi_t *wnd = (lws_netdev_instance_wifi_t *)arg;
lws_netdevs_t *netdevs = lws_netdevs_from_ndi(&wnd->inst);
struct lws_context *ctx = lws_context_from_netdevs(netdevs);
if (event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *e = (ip_event_got_ip_t *)event_data;
char ip[16];
#if 0
tcpip_adapter_dns_info_t e32ip;
/*
* Since atm we get this via DHCP, presumably we can get ahold
* of related info set by the router
*/
if (tcpip_adapter_get_dns_info(TCPIP_ADAPTER_IF_STA,
TCPIP_ADAPTER_DNS_MAIN,
/* also _BACKUP, _FALLBACK */
&e32ip)) {
lwsl_err("%s: there's no dns server set\n", __func__);
e32ip.ip.u_addr.ipv4 = 0x08080808;
e32ip.ip.type = ESP_IPADDR_TYPE_V4;
}
netdevs->sa46_dns_resolver.
#endif
lws_write_numeric_address((void *)&e->ip_info.ip, 4, ip,
sizeof(ip));
lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
"{\"type\":\"ipacq\",\"if\":\"%s\","
"\"ipv4\":\"%s\"}", wnd->inst.name, ip);
}
}
/*
* This is the platform (esp-idf) init for any kind of networking to be
* available at all
*/
int
lws_netdev_plat_init(void)
{
nvs_flash_init();
esp_netif_init();
ESP_ERROR_CHECK(esp_event_loop_create_default());
return 0;
}
/*
* This is the platform (esp-idf) init for any wifi to be available at all
*/
int
lws_netdev_plat_wifi_init(void)
{
wifi_init_config_t wic = WIFI_INIT_CONFIG_DEFAULT();
int n;
esp_netif_create_default_wifi_sta();
n = esp_wifi_init(&wic);
if (n) {
lwsl_err("%s: wifi init fail: %d\n", __func__, n);
return 1;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
return 0;
}
struct lws_netdev_instance *
lws_netdev_wifi_create_plat(struct lws_context *ctx,
const lws_netdev_ops_t *ops,
const char *name, void *platinfo)
{
lws_netdev_instance_wifi_esp32_t *wnde32 = lws_zalloc(
sizeof(*wnde32), __func__);
if (!wnde32)
return NULL;
wnde32->wnd.inst.type = LWSNDTYP_WIFI;
lws_netdev_instance_create(&wnde32->wnd.inst, ctx, ops, name, platinfo);
return &wnde32->wnd.inst;
}
int
lws_netdev_wifi_configure_plat(struct lws_netdev_instance *nd,
lws_netdev_config_t *config)
{
return 0;
}
int
lws_netdev_wifi_up_plat(struct lws_netdev_instance *nd)
{
lws_netdev_instance_wifi_esp32_t *wnde32 =
(lws_netdev_instance_wifi_esp32_t *)nd;
struct lws_context *ctx = netdev_instance_to_ctx(&wnde32->wnd.inst);
if (wnde32->wnd.flags & LNDIW_UP)
return 0;
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP, _event_handler_ip, nd,
&wnde32->instance_got_ip));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID, _event_handler_wifi, nd,
&wnde32->instance_any_id));
esp_wifi_start();
wnde32->wnd.flags |= LNDIW_UP;
lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
"{\"type\":\"up\",\"if\":\"%s\"}",
wnde32->wnd.inst.name);
return 0;
}
int
lws_netdev_wifi_down_plat(struct lws_netdev_instance *nd)
{
lws_netdev_instance_wifi_esp32_t *wnde32 =
(lws_netdev_instance_wifi_esp32_t *)nd;
struct lws_context *ctx = netdev_instance_to_ctx(&wnde32->wnd.inst);
if (!(wnde32->wnd.flags & LNDIW_UP))
return 0;
lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
"{\"type\":\"down\",\"if\":\"%s\"}",
wnde32->wnd.inst.name);
esp_wifi_stop();
esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP,
&wnde32->instance_got_ip);
esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID,
&wnde32->instance_any_id);
wnde32->wnd.flags &= ~LNDIW_UP;
return 0;
}
void
lws_netdev_wifi_destroy_plat(struct lws_netdev_instance **pnd)
{
lws_free(*pnd);
*pnd = NULL;
}

View File

@ -0,0 +1,95 @@
/*
* esp32 / esp-idf pwm
*
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
*
* 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 "private-lib-core.h"
#include "soc/ledc_reg.h"
#include "driver/ledc.h"
#define _LEDC_HIGH_SPEED_MODE 0
static const ledc_timer_config_t tc = {
#if defined(CONFIG_IDF_TARGET_ESP32S2)
.speed_mode = LEDC_LOW_SPEED_MODE,
#else
.speed_mode = _LEDC_HIGH_SPEED_MODE,
#endif
.duty_resolution = LEDC_TIMER_13_BIT,
.timer_num = LEDC_TIMER_0,
#if defined(CONFIG_IDF_TARGET_ESP32S2)
.freq_hz = 1000,
#else
.freq_hz = 5000,
#endif
.clk_cfg = LEDC_AUTO_CLK
};
int
lws_pwm_plat_init(const struct lws_pwm_ops *lo)
{
ledc_channel_config_t lc = {
#if defined(CONFIG_IDF_TARGET_ESP32S2)
.speed_mode = LEDC_LOW_SPEED_MODE,
.duty = 8191,
#else
.speed_mode = _LEDC_HIGH_SPEED_MODE,
.duty = 8191,
#endif
.intr_type = LEDC_INTR_FADE_END,
.timer_sel = LEDC_TIMER_0,
};
size_t n;
ledc_timer_config(&tc);
for (n = 0; n < lo->count_pwm_map; n++) {
lc.channel = LEDC_CHANNEL_0 + lo->pwm_map[n].index;
lc.gpio_num = lo->pwm_map[n].gpio;
ledc_channel_config(&lc);
ledc_set_duty(_LEDC_HIGH_SPEED_MODE, lc.channel, 0);
ledc_update_duty(_LEDC_HIGH_SPEED_MODE, lc.channel);
}
return 0;
}
void
lws_pwm_plat_intensity(const struct lws_pwm_ops *lo, _lws_plat_gpio_t gpio,
lws_led_intensity_t inten)
{
size_t n;
for (n = 0; n < lo->count_pwm_map; n++) {
if (lo->pwm_map[n].gpio == gpio) {
if (!lo->pwm_map[n].active_level)
inten = 65535 - inten;
ledc_set_duty(_LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0 +
lo->pwm_map[n].index, inten >> 3);
ledc_update_duty(_LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0 +
lo->pwm_map[n].index);
return;
}
}
lwsl_err("%s: unknown gpio for pwm\n", __func__);
}

View File

@ -0,0 +1,75 @@
/*
* esp32 / esp-idf NV settings shim
*
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
*
* 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 <private-lib-core.h>
#include <nvs_flash.h>
int
lws_settings_plat_get(lws_settings_instance_t *si, const char *name,
uint8_t *dest, size_t *max_actual)
{
int n;
n = nvs_flash_init_partition((const char *)si->opaque_plat);
lwsl_notice("%s: init partition %d\n", __func__, n);
if (n == ESP_ERR_NOT_FOUND)
return 1;
if (nvs_open_from_partition((const char *)si->opaque_plat,
"_lws_settings", NVS_READONLY,
(nvs_handle_t *)&si->handle_plat))
return 1;
n = nvs_get_blob((nvs_handle_t)si->handle_plat,
name, dest, max_actual);
nvs_close((nvs_handle_t)si->handle_plat);
return !!n;
}
int
lws_settings_plat_set(lws_settings_instance_t *si, const char *name,
const uint8_t *src, size_t len)
{
int n = nvs_flash_init_partition((const char *)si->opaque_plat);
lwsl_notice("%s: init partition %d\n", __func__, n);
if (n == ESP_ERR_NOT_FOUND)
return 1;
if (nvs_open_from_partition((const char *)si->opaque_plat,
"_lws_settings", NVS_READWRITE,
(nvs_handle_t *)&si->handle_plat))
return 1;
n = nvs_set_blob((nvs_handle_t)si->handle_plat, name, src, len);
nvs_commit((nvs_handle_t)si->handle_plat);
nvs_close((nvs_handle_t)si->handle_plat);
return 0;
}

View File

@ -0,0 +1,297 @@
/*
* esp32 / esp-idf SPI
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* 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 <libwebsockets.h>
#include <driver/spi_master.h>
#include <esp_heap_caps.h>
typedef struct {
uint32_t *dma_stash;
size_t dma_stash_len;
spi_transaction_t esp_txn;
} lws_spi_async_txn_t;
static spi_device_handle_t sdh[4][4]; /* [unit][cs index] */
static volatile lws_spi_async_txn_t sat[7];
void *
lws_esp32_spi_alloc_dma(const struct lws_spi_ops *ctx, size_t size)
{
return heap_caps_malloc(size, MALLOC_CAP_32BIT | MALLOC_CAP_DMA);
}
void
lws_esp32_spi_free_dma(const struct lws_spi_ops *ctx, void **p)
{
if (*p) {
heap_caps_free(*p);
*p = NULL;
}
}
static void IRAM_ATTR
lcd_spi_pre_transfer_callback(spi_transaction_t *t)
{
int n = (int)(intptr_t)((volatile spi_transaction_t *)t)->user;
gpio_set_level((n >> 8) & 0xff, n & 1);
}
static void IRAM_ATTR
lcd_spi_post_transfer_callback(spi_transaction_t *t)
{
((volatile spi_transaction_t *)t)->user = NULL;
}
static lws_spi_async_txn_t *
find_idle_sat(void)
{
size_t n = 0;
for (n = 0; n < LWS_ARRAY_SIZE(sat); n++)
if (!sat[n].esp_txn.user) {
memset((void *)&sat[n].esp_txn, 0, sizeof(sat[0].esp_txn));
return (lws_spi_async_txn_t *)&sat[n];
}
return NULL;
}
int
lws_esp32_spi_in_flight(const struct lws_spi_ops *ctx)
{
size_t n = 0;
int inf = 0;
for (n = 0; n < LWS_ARRAY_SIZE(sat); n++)
if (sat[n].esp_txn.user)
inf++;
return inf;
}
int
lws_esp32_spi_init(const lws_spi_ops_t *spi_ops)
{
lws_bb_spi_t *bb = lws_container_of(spi_ops, lws_bb_spi_t, bb_ops);
spi_bus_config_t bc;
/* This inits the specified SPI BUS */
memset(&bc, 0, sizeof(bc));
bc.mosi_io_num = -1; // bb->mosi;
bc.miso_io_num = -1; // bb->miso;
bc.sclk_io_num = bb->clk;
bc.data0_io_num = bb->mosi;
bc.data1_io_num = -1;
bc.data2_io_num = -1;
bc.data3_io_num = -1;
bc.data4_io_num = -1;
bc.data5_io_num = -1;
bc.data6_io_num = -1;
bc.data7_io_num = -1;
bc.quadwp_io_num = -1;
bc.quadhd_io_num = -1;
bc.flags = SPICOMMON_BUSFLAG_MASTER;
if (spi_bus_initialize(bb->unit, &bc, SPI_DMA_CH_AUTO) != ESP_OK) {
lwsl_err("%s: SPI init failed\n", __func__);
return 1;
}
memset((void *)&sat, 0, sizeof(sat));
return 0;
}
int
lws_esp32_spi_queue(const lws_spi_ops_t *spi_ops, const lws_spi_desc_t *desc)
{
lws_bb_spi_t *bb = lws_container_of(spi_ops, lws_bb_spi_t, bb_ops);
spi_device_handle_t h = sdh[bb->unit][desc->channel];
uint8_t *d = (uint8_t *)desc->data;
size_t cw = desc->count_write;
// spi_transaction_t *ett;
esp_err_t e;
if (!h) {
spi_device_interface_config_t edic;
/* We need to create the device at these coordinates */
memset(&edic, 0, sizeof(edic));
edic.mode = spi_ops->bus_mode;
edic.clock_speed_hz = spi_ops->spi_clk_hz ?
spi_ops->spi_clk_hz : 16000000;
edic.input_delay_ns = 50;
edic.spics_io_num = bb->ncs[desc->channel];
edic.queue_size = 7;
edic.pre_cb = lcd_spi_pre_transfer_callback;
edic.post_cb = lcd_spi_post_transfer_callback;
edic.flags = SPI_DEVICE_NO_DUMMY;
/* we do these manually in callbacks */
bb->gpio->mode(bb->ncmd[desc->channel], LWSGGPIO_FL_WRITE);
bb->gpio->mode(bb->ncs[desc->channel], LWSGGPIO_FL_WRITE);
e = spi_bus_add_device(bb->unit, &edic, &h);
if (e != ESP_OK) {
lwsl_err("%s: failed to add device: 0x%x\n", __func__, e);
return 1;
}
sdh[bb->unit][desc->channel] = h;
}
if (desc->count_cmd) {
lws_spi_async_txn_t *at = NULL;
while (!at)
at = find_idle_sat();
if (at->dma_stash && at->dma_stash_len != 4) {
/* we lazily free these to avoid heap apis in IRQ ctx */
lws_esp32_spi_free_dma(NULL, (void **)&at->dma_stash);
at->dma_stash_len = 0;
}
if (at->dma_stash_len != 4) {
at->dma_stash = lws_esp32_spi_alloc_dma(NULL, 4);
if (!at->dma_stash) {
lwsl_err("%s: OOM getting DMA bounce\n", __func__);
return -1;
}
at->dma_stash_len = 4;
}
at->esp_txn.tx_buffer = at->dma_stash;
{
uint32_t u = 0;
size_t i;
for (i = 0; i < desc->count_cmd; i++) {
((uint8_t *)&u)[i & 3] = desc->src[i];
((uint32_t *)at->esp_txn.tx_buffer)[i >> 2] = u;
}
}
at->esp_txn.flags = 0;
at->esp_txn.length = desc->count_cmd * 8;
at->esp_txn.rx_buffer = NULL;
at->esp_txn.rxlength = 0;
at->esp_txn.user = (void *)((bb->ncs[desc->channel] << 16) |
(bb->ncmd[desc->channel] << 8) |
!!(desc->flags & LWS_SPI_FLAG_DC_CMD_IS_HIGH));
e = spi_device_queue_trans(h, &at->esp_txn, 50);
if (e != ESP_OK) {
lwsl_err("%s: failed to queue cmd trans: 0x%x\n",
__func__, e);
return 1;
}
#if 0
ett = &at->esp_txn;
e = spi_device_get_trans_result(h, &ett, 50);
if (e != ESP_OK) {
lwsl_err("%s: failed to get trans result\n", __func__);
return 1;
}
#endif
}
while (cw) {
size_t chunk = cw < 4000 ? cw : 4000;
lws_spi_async_txn_t *at = NULL;
while (!at)
at = find_idle_sat();
if (at->dma_stash && at->dma_stash_len != chunk) {
/* we lazily free these to avoid heap apis in IRQ ctx */
lws_esp32_spi_free_dma(NULL, (void **)&at->dma_stash);
at->dma_stash_len = 0;
}
if (at->dma_stash_len != chunk &&
!(desc->flags & LWS_SPI_FLAG_DMA_BOUNCE_NOT_NEEDED)) {
/* allocate a bounce buffer and fill it */
at->dma_stash = lws_esp32_spi_alloc_dma(NULL, chunk);
if (!at->dma_stash) {
lwsl_err("%s: OOM getting DMA bounce\n", __func__);
return -1;
}
at->dma_stash_len = chunk;
}
if (desc->flags & LWS_SPI_FLAG_DMA_BOUNCE_NOT_NEEDED) {
at->esp_txn.tx_buffer = d;
d += chunk;
} else {
uint32_t u = 0;
size_t i;
at->esp_txn.tx_buffer = at->dma_stash;
for (i = 0; i < chunk; i++) {
((uint8_t *)&u)[i & 3] = *d++;
((uint32_t *)at->esp_txn.tx_buffer)[i >> 2] = u;
}
}
at->esp_txn.rx_buffer = NULL;
at->esp_txn.rxlength = 0;
at->esp_txn.length = chunk * 8;
at->esp_txn.user = (void *)((bb->ncs[desc->channel] << 16) |
(bb->ncmd[desc->channel] << 8) |
!(desc->flags & LWS_SPI_FLAG_DC_CMD_IS_HIGH));
at->esp_txn.flags = 0;
e = spi_device_queue_trans(h, &at->esp_txn, 50);
if (e != ESP_OK) {
lwsl_err("%s: failed to queue data trans\n", __func__);
assert(0);
return 1;
}
#if 0
ett = &at->esp_txn;
e = spi_device_get_trans_result(h, &ett, 50);
if (e != ESP_OK) {
lwsl_err("%s: failed to get trans result\n", __func__);
return 1;
}
#endif
cw -= chunk;
}
return 0;
}

View File

@ -0,0 +1,201 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2022 Andy Green <andy@warmcat.com>
*
* 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.
*
* lws_ota platform implementation for esp-idf
*
* The whole platform OTA implementation runs in its own task context, which
* is created in ota_start() and taken down in ota_finalize(). Async
* completions are passed back to the main code by lws_cancel_service().
*/
#include "private-lib-core.h"
#include "esp_ota_ops.h"
extern lws_settings_instance_t *si;
/*
* Our platform-specific single OTA process object, it knows the esp-idf OTA
* handle too after ota_start succeeds.
*/
typedef struct {
lws_ota_t *g;
esp_ota_handle_t ota; /* opaque platform ota handle */
TaskHandle_t th;
SemaphoreHandle_t sem;
const esp_partition_t *ep;
} _lws_ota_process_t;
static _lws_ota_process_t pop;
static void
ota_task(void *_g)
{
lws_ota_t *g = (lws_ota_t *)_g;
esp_err_t e;
uint32_t no;
while (1) {
xTaskNotifyWaitIndexed(0, 0, ULONG_MAX, &no, portMAX_DELAY);
/* something to do */
g->async_r = LWSOTARET_ONGOING;
switch (no) {
case LWS_OTA_ASYNC_START:
pop.ep = esp_ota_get_next_update_partition(NULL);
g->async_r = LWSOTARET_NOSLOT;
if (pop.ep) {
e = esp_ota_begin(pop.ep, g->expected_size,
&pop.ota);
if (e == ESP_OK)
g->async_r = LWSOTARET_OK;
else
printf("esp_ota_begin: %d\n", (int)e);
} else
lwsl_err("%s: no next update part\n", __func__);
g->async_completed = 1;
lws_cancel_service(g->cx);
break;
case LWS_OTA_ASYNC_WRITE:
/*
* g->flow has compressed data we can use when we
* need it
*/
g->async_r = LWSOTARET_FAILED;
e = esp_ota_write(pop.ota, g->buf, g->buf_len);
if (e == ESP_OK)
g->async_r = LWSOTARET_OK;
else
lwsl_cx_err(g->cx, "esp_ota_write: %d", (int)e);
g->async_completed = 1;
lws_cancel_service(g->cx);
break;
case LWS_OTA_ASYNC_ABORT:
case LWS_OTA_ASYNC_FINALIZE:
g->async_r = LWSOTARET_FAILED;
if (no == LWS_OTA_ASYNC_ABORT)
e = esp_ota_abort(pop.ota);
else {
e = esp_ota_end(pop.ota);
if (e == ESP_OK) {
struct timeval tv;
/*
* Mark that we want to boot into the
* updated firmware that we just
* installed
*/
e = esp_ota_set_boot_partition(pop.ep);
/*
* Set the latest fw unixtime to the new
* guy. Set the time we updated.
*/
lws_settings_plat_printf(si,
"ota.fw_unixtime", "%llu",
(unsigned long long)g->unixtime);
if (!gettimeofday(&tv, NULL))
lws_settings_plat_printf(si,
"ota.upd_unixtime", "%llu",
(unsigned long long)tv.tv_sec);
}
}
if (e == ESP_OK)
g->async_r = LWSOTARET_OK;
else
lwsl_cx_err(g->cx, "esp_ota_end: %d", (int)e);
g->async_completed = 1;
lws_cancel_service(g->cx);
pop.th = NULL;
vTaskDelete(0);
return;
}
}
}
void
lws_plat_ota_queue(lws_ota_t *g, lws_ota_async_t a)
{
g->async_last = a;
xTaskNotify(pop.th, a, eSetValueWithOverwrite);
}
int
lws_plat_ota_start(lws_ota_t *g)
{
g->op = (lws_ota_process_t)&pop;
xTaskCreate(ota_task, "ota", 3072, g, tskIDLE_PRIORITY, &pop.th);
if (!pop.th)
return 1;
lws_plat_ota_queue(g, LWS_OTA_ASYNC_START);
return 0;
}
int
lws_plat_ota_report_current(lws_ota_t *g, int bad)
{
if (bad)
esp_ota_mark_app_invalid_rollback_and_reboot();
else
esp_ota_mark_app_valid_cancel_rollback();
return LWSOTARET_OK;
}
int
lws_plat_ota_get_last_fw_unixtime(uint64_t *fw_unixtime)
{
uint8_t buf[20];
size_t l = sizeof(buf);
if (lws_settings_plat_get(si, "ota.fw_unixtime", buf, &l)) {
lwsl_notice("%s: not in settings\n", __func__);
return 1;
}
*fw_unixtime = atoll((const char *)buf);
return 0;
}