forked from espressif/esp-idf
Merge branch 'experimental/mdns_with_sockets' into 'master'
mdns: Add optional networking layer on BSD socket Closes IDF-1849 See merge request espressif/esp-idf!9857
This commit is contained in:
@@ -1,3 +1,11 @@
|
|||||||
|
idf_build_get_property(target IDF_TARGET)
|
||||||
|
|
||||||
|
if(${target} STREQUAL "linux")
|
||||||
|
# Header only library for linux
|
||||||
|
idf_component_register(INCLUDE_DIRS include)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
set(srcs
|
set(srcs
|
||||||
"esp_netif_handlers.c"
|
"esp_netif_handlers.c"
|
||||||
"esp_netif_objects.c"
|
"esp_netif_objects.c"
|
||||||
|
@@ -1,7 +1,22 @@
|
|||||||
idf_component_register(SRCS "mdns.c"
|
if(CONFIG_MDNS_NETWORKING_SOCKET)
|
||||||
"mdns_console.c"
|
set(MDNS_NETWORKING "mdns_networking_socket.c")
|
||||||
"mdns_networking.c"
|
else()
|
||||||
|
set(MDNS_NETWORKING "mdns_networking_lwip.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
idf_build_get_property(target IDF_TARGET)
|
||||||
|
if(${target} STREQUAL "linux")
|
||||||
|
set(dependencies esp_system_protocols_linux)
|
||||||
|
set(srcs "mdns.c" ${MDNS_NETWORKING})
|
||||||
|
else()
|
||||||
|
set(dependencies lwip console esp_netif)
|
||||||
|
set(private_dependencies esp_timer)
|
||||||
|
set(srcs "mdns.c" ${MDNS_NETWORKING} "mdns_console.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
idf_component_register(
|
||||||
|
SRCS ${srcs}
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
PRIV_INCLUDE_DIRS "private_include"
|
PRIV_INCLUDE_DIRS "private_include"
|
||||||
REQUIRES lwip console esp_netif
|
REQUIRES ${dependencies}
|
||||||
PRIV_REQUIRES esp_timer)
|
PRIV_REQUIRES ${private_dependencies})
|
||||||
|
@@ -75,4 +75,13 @@ menu "mDNS"
|
|||||||
Configures period of mDNS timer, which periodically transmits packets
|
Configures period of mDNS timer, which periodically transmits packets
|
||||||
and schedules mDNS searches.
|
and schedules mDNS searches.
|
||||||
|
|
||||||
|
config MDNS_NETWORKING_SOCKET
|
||||||
|
bool "Use BSD sockets for mdns networking"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enables optional mdns networking implementation using BSD sockets
|
||||||
|
in UDP multicast mode.
|
||||||
|
This option creates a new thread to serve receiving packets (TODO).
|
||||||
|
This option uses additional N sockets, where N is number of interfaces.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
@@ -1,2 +1,7 @@
|
|||||||
|
ifdef CONFIG_MDNS_NETWORKING_SOCKET
|
||||||
|
COMPONENT_OBJEXCLUDE := mdns_networking_lwip.o
|
||||||
|
else
|
||||||
|
COMPONENT_OBJEXCLUDE := mdns_networking_socket.o
|
||||||
|
endif
|
||||||
COMPONENT_ADD_INCLUDEDIRS := include
|
COMPONENT_ADD_INCLUDEDIRS := include
|
||||||
COMPONENT_PRIV_INCLUDEDIRS := private_include
|
COMPONENT_PRIV_INCLUDEDIRS := private_include
|
||||||
|
5
components/mdns/host_test/CMakeLists.txt
Normal file
5
components/mdns/host_test/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
set(COMPONENTS main)
|
||||||
|
project(mdns_host)
|
25
components/mdns/host_test/README.md
Normal file
25
components/mdns/host_test/README.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Setup dummy network interfaces
|
||||||
|
```
|
||||||
|
sudo ip link add eth2 type dummy
|
||||||
|
sudo ip addr add 192.168.1.200/24 dev eth2
|
||||||
|
sudo ip link set eth2 up
|
||||||
|
sudo ifconfig eth2 multicast
|
||||||
|
```
|
||||||
|
|
||||||
|
# Dig on a specified interface
|
||||||
|
|
||||||
|
```
|
||||||
|
dig +short -b 192.168.1.200 -p 5353 @224.0.0.251 myesp.local
|
||||||
|
```
|
||||||
|
|
||||||
|
# Run avahi to browse services
|
||||||
|
|
||||||
|
Avahi needs the netif to have the "multicast" flag set
|
||||||
|
|
||||||
|
```bash
|
||||||
|
david@david-comp:~/esp/idf (feature/mdns_networking_socket)$ avahi-browse -a -r -p
|
||||||
|
+;eth2;IPv6;myesp-service2;Web Site;local
|
||||||
|
+;eth2;IPv4;myesp-service2;Web Site;local
|
||||||
|
=;eth2;IPv6;myesp-service2;Web Site;local;myesp.local;192.168.1.200;80;"board=esp32" "u=user" "p=password"
|
||||||
|
=;eth2;IPv4;myesp-service2;Web Site;local;myesp.local;192.168.1.200;80;"board=esp32" "u=user" "p=password"
|
||||||
|
```
|
@@ -0,0 +1,3 @@
|
|||||||
|
idf_component_register(SRCS esp_event_mock.c
|
||||||
|
INCLUDE_DIRS include
|
||||||
|
REQUIRES esp_system_protocols_linux)
|
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
|
||||||
|
const char * WIFI_EVENT = "WIFI_EVENT";
|
||||||
|
const char * IP_EVENT = "IP_EVENT";
|
||||||
|
|
||||||
|
esp_err_t esp_event_handler_register(const char * event_base, int32_t event_id, void* event_handler, void* event_handler_arg)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_event_handler_unregister(const char * event_base, int32_t event_id, void* event_handler)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "stdbool.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_event_base.h"
|
||||||
|
#include "bsd_strings.h"
|
||||||
|
|
||||||
|
#define ESP_EVENT_DECLARE_BASE(x)
|
||||||
|
#define ESP_EVENT_ANY_ID (-1)
|
||||||
|
|
||||||
|
typedef void * esp_event_base_t;
|
||||||
|
typedef void * system_event_t;
|
||||||
|
|
||||||
|
const char* WIFI_EVENT;
|
||||||
|
const char* IP_EVENT;
|
||||||
|
|
||||||
|
esp_err_t esp_event_handler_register(const char * event_base, int32_t event_id, void* event_handler, void* event_handler_arg);
|
||||||
|
|
||||||
|
esp_err_t esp_event_handler_unregister(const char * event_base, int32_t event_id, void* event_handler);
|
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
WIFI_EVENT_STA_CONNECTED, /**< ESP32 station connected to AP */
|
||||||
|
WIFI_EVENT_STA_DISCONNECTED, /**< ESP32 station disconnected from AP */
|
||||||
|
WIFI_EVENT_AP_START, /**< ESP32 soft-AP start */
|
||||||
|
WIFI_EVENT_AP_STOP, /**< ESP32 soft-AP stop */
|
||||||
|
} mdns_used_event_t;
|
@@ -0,0 +1,3 @@
|
|||||||
|
idf_component_register(SRCS esp_netif_linux.c
|
||||||
|
INCLUDE_DIRS include
|
||||||
|
REQUIRES esp_system_protocols_linux)
|
@@ -0,0 +1,9 @@
|
|||||||
|
menu "LWIP-MOCK-CONFIG"
|
||||||
|
|
||||||
|
config LWIP_IPV6
|
||||||
|
bool "Enable IPv6"
|
||||||
|
default true
|
||||||
|
help
|
||||||
|
Enable/disable IPv6
|
||||||
|
|
||||||
|
endmenu
|
@@ -0,0 +1,163 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include<stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "esp_netif.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include <string.h> //strlen
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h> //inet_addr
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include "esp_netif_types.h"
|
||||||
|
|
||||||
|
#define MAX_NETIFS 4
|
||||||
|
|
||||||
|
static esp_netif_t* s_netif_list[MAX_NETIFS] = { 0 };
|
||||||
|
|
||||||
|
struct esp_netif_obj
|
||||||
|
{
|
||||||
|
const char *if_key;
|
||||||
|
const char *if_desc;
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_netif_t *esp_netif_get_handle_from_ifkey(const char *if_key)
|
||||||
|
{
|
||||||
|
for (int i=0; i<MAX_NETIFS; ++i) {
|
||||||
|
if (s_netif_list[i] && strcmp(s_netif_list[i]->if_key, if_key) == 0) {
|
||||||
|
return s_netif_list[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_netif_get_ip_info(esp_netif_t *esp_netif, esp_netif_ip_info_t *ip_info)
|
||||||
|
{
|
||||||
|
if (esp_netif == NULL) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
struct ifaddrs *addrs, *tmp;
|
||||||
|
getifaddrs(&addrs);
|
||||||
|
tmp = addrs;
|
||||||
|
|
||||||
|
while (tmp) {
|
||||||
|
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) {
|
||||||
|
char addr[20];
|
||||||
|
struct sockaddr_in *pAddr = (struct sockaddr_in *) tmp->ifa_addr;
|
||||||
|
inet_ntop(AF_INET, &pAddr->sin_addr, addr, sizeof(addr) );
|
||||||
|
if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) {
|
||||||
|
printf("AF_INET: %s: %s\n", tmp->ifa_name, addr);
|
||||||
|
memcpy(&ip_info->ip.addr, &pAddr->sin_addr, 4);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp = tmp->ifa_next;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_netif_dhcpc_get_status(esp_netif_t *esp_netif, esp_netif_dhcp_status_t *status)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t esp_netif_get_ip6_linklocal(esp_netif_t *esp_netif, esp_ip6_addr_t *if_ip6)
|
||||||
|
{
|
||||||
|
if (esp_netif == NULL) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
struct ifaddrs *addrs, *tmp;
|
||||||
|
getifaddrs(&addrs);
|
||||||
|
tmp = addrs;
|
||||||
|
|
||||||
|
while (tmp)
|
||||||
|
{
|
||||||
|
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET6) {
|
||||||
|
char addr[64];
|
||||||
|
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)tmp->ifa_addr;
|
||||||
|
inet_ntop(AF_INET6, &pAddr->sin6_addr, addr, sizeof(addr) );
|
||||||
|
if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) {
|
||||||
|
printf("AF_INET6: %s: %s\n", tmp->ifa_name, addr);
|
||||||
|
memcpy(if_ip6->addr, &pAddr->sin6_addr, 4*4);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp = tmp->ifa_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeifaddrs(addrs);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int esp_netif_get_netif_impl_index(esp_netif_t *esp_netif)
|
||||||
|
{
|
||||||
|
if (esp_netif == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
uint32_t interfaceIndex = if_nametoindex(esp_netif->if_desc);
|
||||||
|
return interfaceIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_netif_get_netif_impl_name(esp_netif_t *esp_netif, char* name)
|
||||||
|
{
|
||||||
|
if (esp_netif == NULL) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
strcpy(name, esp_netif->if_desc);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *esp_netif_get_desc(esp_netif_t *esp_netif)
|
||||||
|
{
|
||||||
|
if (esp_netif == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return esp_netif->if_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_netif_t *esp_netif_new(const esp_netif_config_t *config)
|
||||||
|
{
|
||||||
|
if (esp_netif_get_handle_from_ifkey(config->base->if_key)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
esp_netif_t* netif = calloc(1, sizeof(struct esp_netif_obj));
|
||||||
|
if (netif) {
|
||||||
|
netif->if_desc = config->base->if_desc;
|
||||||
|
netif->if_key = config->base->if_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<MAX_NETIFS; ++i) {
|
||||||
|
if (s_netif_list[i] == NULL) {
|
||||||
|
s_netif_list[i] = netif;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return netif;
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_netif_destroy(esp_netif_t *esp_netif)
|
||||||
|
{
|
||||||
|
for (int i=0; i<MAX_NETIFS; ++i) {
|
||||||
|
if (s_netif_list[i] == esp_netif) {
|
||||||
|
s_netif_list[i] = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(esp_netif);
|
||||||
|
}
|
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
#include "esp_event.h"
|
@@ -0,0 +1,3 @@
|
|||||||
|
idf_component_register(SRCS esp_log_impl.c strlcat.c
|
||||||
|
INCLUDE_DIRS include
|
||||||
|
REQUIRES esp_netif_linux esp_timer_linux freertos_linux esp_event_mock esp_netif log esp_common)
|
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression)
|
||||||
|
{
|
||||||
|
ESP_LOGE("ESP_ERROR_CHECK", "Failed with esp_err_t: 0x%x", rc);
|
||||||
|
ESP_LOGE("ESP_ERROR_CHECK", "Expression: %s", expression);
|
||||||
|
ESP_LOGE("ESP_ERROR_CHECK", "Functions: %s %s(%d)", function, file, line);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_log_buffer_hexdump_internal(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t log_level)
|
||||||
|
{
|
||||||
|
if ( LOG_LOCAL_LEVEL >= log_level ) {
|
||||||
|
ESP_LOG_LEVEL(log_level, tag, "Buffer:%p length:%d", buffer, buff_len);
|
||||||
|
for (int i=0; i<buff_len; ++i) {
|
||||||
|
printf("%02x ", ((uint8_t*)buffer)[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
size_t strlcat(char *dest, const char *src, size_t size);
|
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include_next "endian.h"
|
@@ -0,0 +1,68 @@
|
|||||||
|
/* $OpenBSD: strlcat.c,v 1.2 1999/06/17 16:28:58 millert Exp $ */
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Appends src to string dst of size siz (unlike strncat, siz is the
|
||||||
|
* full size of dst, not space left). At most siz-1 characters
|
||||||
|
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
|
||||||
|
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
|
||||||
|
* If retval >= siz, truncation occurred.
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
strlcat(dst, src, siz)
|
||||||
|
char *dst;
|
||||||
|
const char *src;
|
||||||
|
size_t siz;
|
||||||
|
{
|
||||||
|
char *d = dst;
|
||||||
|
const char *s = src;
|
||||||
|
size_t n = siz;
|
||||||
|
size_t dlen;
|
||||||
|
|
||||||
|
/* Find the end of dst and adjust bytes left but don't go past end */
|
||||||
|
while (n-- != 0 && *d != '\0')
|
||||||
|
d++;
|
||||||
|
dlen = d - dst;
|
||||||
|
n = siz - dlen;
|
||||||
|
|
||||||
|
if (n == 0)
|
||||||
|
return(dlen + strlen(s));
|
||||||
|
while (*s != '\0') {
|
||||||
|
if (n != 1) {
|
||||||
|
*d++ = *s;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
*d = '\0';
|
||||||
|
|
||||||
|
return(dlen + (s - src)); /* count does not include NUL */
|
||||||
|
}
|
@@ -0,0 +1,5 @@
|
|||||||
|
idf_component_register(SRCS esp_timer_linux.c timer_task.cpp
|
||||||
|
INCLUDE_DIRS include
|
||||||
|
REQUIRES esp_system_protocols_linux freertos_linux)
|
||||||
|
|
||||||
|
target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)
|
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
void * create_tt(esp_timer_cb_t cb);
|
||||||
|
|
||||||
|
void destroy_tt(void* tt);
|
||||||
|
|
||||||
|
void set_tout(void* tt, uint32_t ms);
|
||||||
|
|
||||||
|
esp_err_t esp_timer_create(const esp_timer_create_args_t* create_args,
|
||||||
|
esp_timer_handle_t* out_handle)
|
||||||
|
{
|
||||||
|
*out_handle = (esp_timer_handle_t)create_tt(create_args->callback);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period)
|
||||||
|
{
|
||||||
|
set_tout(timer, period/1000);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_timer_stop(esp_timer_handle_t timer)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_timer_delete(esp_timer_handle_t timer)
|
||||||
|
{
|
||||||
|
destroy_tt(timer);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct esp_timer* esp_timer_handle_t;
|
||||||
|
|
||||||
|
typedef void (*esp_timer_cb_t)(void* arg);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ESP_TIMER_TASK,
|
||||||
|
} esp_timer_dispatch_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_timer_cb_t callback; //!< Function to call when timer expires
|
||||||
|
void* arg; //!< Argument to pass to the callback
|
||||||
|
esp_timer_dispatch_t dispatch_method; //!< Call the callback from task or from ISR
|
||||||
|
const char* name; //!< Timer name, used in esp_timer_dump function
|
||||||
|
bool skip_unhandled_events; //!< Skip unhandled events for periodic timers
|
||||||
|
} esp_timer_create_args_t;
|
||||||
|
|
||||||
|
esp_err_t esp_timer_create(const esp_timer_create_args_t* create_args,
|
||||||
|
esp_timer_handle_t* out_handle);
|
||||||
|
esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period);
|
||||||
|
|
||||||
|
esp_err_t esp_timer_stop(esp_timer_handle_t timer);
|
||||||
|
|
||||||
|
esp_err_t esp_timer_delete(esp_timer_handle_t timer);
|
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "timer_task.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
extern "C" void * create_tt(cb_t cb)
|
||||||
|
{
|
||||||
|
auto * tt = new TimerTaskMock(cb);
|
||||||
|
return tt;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void destroy_tt(void* tt)
|
||||||
|
{
|
||||||
|
auto * timer_task = static_cast<TimerTaskMock *>(tt);
|
||||||
|
delete(timer_task);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" void set_tout(void* tt, uint32_t ms)
|
||||||
|
{
|
||||||
|
auto * timer_task = static_cast<TimerTaskMock *>(tt);
|
||||||
|
timer_task->SetTimeout(ms);
|
||||||
|
}
|
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
typedef void (*cb_t)(void* arg);
|
||||||
|
|
||||||
|
class TimerTaskMock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TimerTaskMock(cb_t cb): cb(cb), t(run_static, this), active(false), ms(INT32_MAX) {}
|
||||||
|
~TimerTaskMock(void) { active = false; t.join(); }
|
||||||
|
|
||||||
|
void SetTimeout(uint32_t m)
|
||||||
|
{
|
||||||
|
ms = m;
|
||||||
|
active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static void run_static(TimerTaskMock* timer)
|
||||||
|
{
|
||||||
|
timer->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(void)
|
||||||
|
{
|
||||||
|
while (!active.load()) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (active.load()) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
||||||
|
cb(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cb_t cb;
|
||||||
|
std::thread t;
|
||||||
|
std::atomic<bool> active;
|
||||||
|
uint32_t ms;
|
||||||
|
|
||||||
|
};
|
@@ -0,0 +1,9 @@
|
|||||||
|
idf_component_register(SRCS freertos_linux.c queue_unique_ptr.cpp
|
||||||
|
INCLUDE_DIRS include
|
||||||
|
REQUIRES esp_system_protocols_linux)
|
||||||
|
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
target_link_libraries(${COMPONENT_LIB} PRIVATE Threads::Threads)
|
||||||
|
|
||||||
|
target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)
|
@@ -0,0 +1,7 @@
|
|||||||
|
menu "FreeRTOS"
|
||||||
|
|
||||||
|
config FREERTOS_NO_AFFINITY
|
||||||
|
hex
|
||||||
|
default 0x7FFFFFFF
|
||||||
|
|
||||||
|
endmenu
|
@@ -0,0 +1,192 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void * create_q(void);
|
||||||
|
|
||||||
|
void destroy_q(void* q);
|
||||||
|
|
||||||
|
bool send_q(void* q, uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
bool recv_q(void* q, uint8_t *data, size_t len, uint32_t ms);
|
||||||
|
|
||||||
|
static uint64_t s_semaphore_data = 0;
|
||||||
|
|
||||||
|
struct queue_handle {
|
||||||
|
size_t item_size;
|
||||||
|
void * q;
|
||||||
|
};
|
||||||
|
|
||||||
|
QueueHandle_t xQueueCreate( uint32_t uxQueueLength, uint32_t uxItemSize )
|
||||||
|
{
|
||||||
|
struct queue_handle * h = calloc(1, sizeof(struct queue_handle));
|
||||||
|
h->item_size = uxItemSize;
|
||||||
|
h->q = create_q();
|
||||||
|
return (QueueHandle_t)h;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t xQueueSend(QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait)
|
||||||
|
{
|
||||||
|
struct queue_handle * h = xQueue;
|
||||||
|
return send_q(h->q, (uint8_t*)pvItemToQueue, h->item_size) ? pdTRUE : pdFAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait)
|
||||||
|
{
|
||||||
|
struct queue_handle * h = xQueue;
|
||||||
|
return recv_q(h->q, (uint8_t*)pvBuffer, h->item_size, xTicksToWait) ? pdTRUE : pdFAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseType_t xSemaphoreGive( QueueHandle_t xQueue)
|
||||||
|
{
|
||||||
|
return xQueueSend(xQueue, &s_semaphore_data, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask )
|
||||||
|
{
|
||||||
|
return xQueueReceive(xQueue, &s_semaphore_data, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vQueueDelete( QueueHandle_t xQueue )
|
||||||
|
{
|
||||||
|
struct queue_handle * h = xQueue;
|
||||||
|
if (h->q) {
|
||||||
|
destroy_q(h->q);
|
||||||
|
}
|
||||||
|
free(xQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QueueHandle_t xSemaphoreCreateBinary(void)
|
||||||
|
{
|
||||||
|
QueueHandle_t sempaphore = xQueueCreate(1, 1);
|
||||||
|
return sempaphore;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueueHandle_t xSemaphoreCreateMutex(void)
|
||||||
|
{
|
||||||
|
QueueHandle_t sempaphore = xQueueCreate(1, 1);
|
||||||
|
if (sempaphore) {
|
||||||
|
xSemaphoreGive(sempaphore);
|
||||||
|
}
|
||||||
|
return sempaphore;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vTaskDelete(TaskHandle_t *task)
|
||||||
|
{
|
||||||
|
if (task == NULL) {
|
||||||
|
pthread_exit(0);
|
||||||
|
}
|
||||||
|
void *thread_rval = NULL;
|
||||||
|
pthread_join((pthread_t)task, &thread_rval);
|
||||||
|
}
|
||||||
|
|
||||||
|
TickType_t xTaskGetTickCount( void )
|
||||||
|
{
|
||||||
|
struct timespec spec;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &spec);
|
||||||
|
return spec.tv_nsec / 1000000 + spec.tv_sec * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vTaskDelay( const TickType_t xTicksToDelay )
|
||||||
|
{
|
||||||
|
usleep(xTicksToDelay*1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void * pthread_task(void * params)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
void * const param;
|
||||||
|
TaskFunction_t task;
|
||||||
|
bool started;
|
||||||
|
} *pthread_params = params;
|
||||||
|
|
||||||
|
void * const param = pthread_params->param;
|
||||||
|
TaskFunction_t task = pthread_params->task;
|
||||||
|
pthread_params->started = true;
|
||||||
|
|
||||||
|
task(param);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
|
||||||
|
const char * const pcName,
|
||||||
|
const uint32_t usStackDepth,
|
||||||
|
void * const pvParameters,
|
||||||
|
UBaseType_t uxPriority,
|
||||||
|
TaskHandle_t * const pvCreatedTask,
|
||||||
|
const BaseType_t xCoreID)
|
||||||
|
{
|
||||||
|
xTaskCreate(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask);
|
||||||
|
return pdTRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void xTaskCreate(TaskFunction_t pvTaskCode, const char * const pcName, const uint32_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pvCreatedTask)
|
||||||
|
{
|
||||||
|
pthread_t new_thread = (pthread_t)NULL;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
struct {
|
||||||
|
void * const param;
|
||||||
|
TaskFunction_t task;
|
||||||
|
bool started;
|
||||||
|
} pthread_params = { .param = pvParameters, .task = pvTaskCode};
|
||||||
|
int res = pthread_attr_init(&attr);
|
||||||
|
assert(res == 0);
|
||||||
|
res = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||||
|
assert(res == 0);
|
||||||
|
res = pthread_create(&new_thread, &attr, pthread_task, &pthread_params);
|
||||||
|
assert(res == 0);
|
||||||
|
|
||||||
|
if (pvCreatedTask) {
|
||||||
|
*pvCreatedTask = (void*)new_thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
// just wait till the task started so we can unwind params from the stack
|
||||||
|
while (pthread_params.started == false) {
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t esp_get_free_heap_size(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t esp_random(void)
|
||||||
|
{
|
||||||
|
return rand();
|
||||||
|
}
|
||||||
|
|
||||||
|
void xTaskNotifyGive(TaskHandle_t task)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskHandle_t xTaskGetCurrentTaskHandle(void)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#define ESP_TASK_PRIO_MAX 25
|
||||||
|
#define ESP_TASKD_EVENT_PRIO 5
|
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define portTICK_PERIOD_MS 1
|
||||||
|
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
|
||||||
|
|
||||||
|
typedef void * xSemaphoreHandle;
|
||||||
|
typedef void * SemaphoreHandle_t;
|
||||||
|
typedef void * xQueueHandle;
|
||||||
|
typedef void * QueueHandle_t;
|
||||||
|
typedef void * TaskHandle_t;
|
||||||
|
typedef uint32_t TickType_t;
|
||||||
|
typedef uint32_t portTickType;
|
||||||
|
|
||||||
|
typedef void (*TaskFunction_t)( void * );
|
||||||
|
typedef unsigned int UBaseType_t;
|
||||||
|
typedef int BaseType_t;
|
||||||
|
|
||||||
|
#define pdFALSE ( ( BaseType_t ) 0 )
|
||||||
|
#define pdTRUE ( ( BaseType_t ) 1 )
|
||||||
|
|
||||||
|
#define pdPASS ( pdTRUE )
|
||||||
|
#define pdFAIL ( pdFALSE )
|
||||||
|
|
||||||
|
#define portTICK_RATE_MS portTICK_PERIOD_MS
|
||||||
|
#define pdMS_TO_TICKS(tick) (tick)
|
||||||
|
|
||||||
|
uint32_t esp_get_free_heap_size(void);
|
||||||
|
uint32_t esp_random(void);
|
@@ -0,0 +1,58 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
|
||||||
|
#define xTaskHandle TaskHandle_t
|
||||||
|
#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )
|
||||||
|
|
||||||
|
void vTaskDelay( const TickType_t xTicksToDelay );
|
||||||
|
|
||||||
|
void xTaskNotifyGive(TaskHandle_t task);
|
||||||
|
|
||||||
|
TaskHandle_t xTaskGetCurrentTaskHandle(void);
|
||||||
|
|
||||||
|
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time );
|
||||||
|
|
||||||
|
BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
|
||||||
|
const char * const pcName,
|
||||||
|
const uint32_t usStackDepth,
|
||||||
|
void * const pvParameters,
|
||||||
|
UBaseType_t uxPriority,
|
||||||
|
TaskHandle_t * const pvCreatedTask,
|
||||||
|
const BaseType_t xCoreID);
|
||||||
|
|
||||||
|
void xTaskCreate(TaskFunction_t pvTaskCode, const char * const pcName, const uint32_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pvCreatedTask);
|
||||||
|
|
||||||
|
TickType_t xTaskGetTickCount( void );
|
||||||
|
|
||||||
|
void vQueueDelete( QueueHandle_t xQueue );
|
||||||
|
|
||||||
|
QueueHandle_t xSemaphoreCreateBinary(void);
|
||||||
|
|
||||||
|
QueueHandle_t xSemaphoreCreateMutex(void);
|
||||||
|
|
||||||
|
BaseType_t xSemaphoreGive( QueueHandle_t xQueue);
|
||||||
|
|
||||||
|
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask );
|
||||||
|
|
||||||
|
void vTaskDelete(TaskHandle_t *task);
|
||||||
|
|
||||||
|
QueueHandle_t xQueueCreate( uint32_t uxQueueLength,
|
||||||
|
uint32_t uxItemSize );
|
||||||
|
|
||||||
|
uint32_t xQueueSend(QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait);
|
||||||
|
|
||||||
|
uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait);
|
@@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "queue_unique_ptr.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
extern "C" void * create_q(void)
|
||||||
|
{
|
||||||
|
auto * q = new QueueMock<std::vector<uint8_t>>();
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void destroy_q(void* q)
|
||||||
|
{
|
||||||
|
auto * queue = static_cast<QueueMock<std::vector<uint8_t>> *>(q);
|
||||||
|
delete(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" bool send_q(void* q, uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
auto v = std::make_unique<std::vector<uint8_t>>(len);
|
||||||
|
v->assign(data, data+len);
|
||||||
|
auto queue = static_cast<QueueMock<std::vector<uint8_t>> *>(q);
|
||||||
|
queue->send(std::move(v));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" bool recv_q(void* q, uint8_t *data, size_t len, uint32_t ms)
|
||||||
|
{
|
||||||
|
auto queue = static_cast<QueueMock<std::vector<uint8_t>> *>(q);
|
||||||
|
auto v = queue->receive(ms);
|
||||||
|
if (v == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(data, (void *)v->data(), len);
|
||||||
|
return true;
|
||||||
|
}
|
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class QueueMock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QueueMock(void): q(), m(), c() {}
|
||||||
|
~QueueMock(void) {}
|
||||||
|
|
||||||
|
void send(std::unique_ptr<T> t)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
q.push(std::move(t));
|
||||||
|
c.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<T> receive(uint32_t ms)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
while(q.empty()) {
|
||||||
|
if (c.wait_for(lock, std::chrono::milliseconds(ms)) == std::cv_status::timeout) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::unique_ptr<T> val = std::move(q.front());
|
||||||
|
q.pop();
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::queue<std::unique_ptr<T>> q;
|
||||||
|
mutable std::mutex m;
|
||||||
|
std::condition_variable c;
|
||||||
|
};
|
4
components/mdns/host_test/main/CMakeLists.txt
Normal file
4
components/mdns/host_test/main/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
idf_component_register(SRCS "main.c"
|
||||||
|
INCLUDE_DIRS
|
||||||
|
"."
|
||||||
|
REQUIRES mdns)
|
59
components/mdns/host_test/main/main.c
Normal file
59
components/mdns/host_test/main/main.c
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "mdns.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
static const char *TAG = "mdns-test";
|
||||||
|
|
||||||
|
static void query_mdns_host(const char * host_name)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Query A: %s.local", host_name);
|
||||||
|
|
||||||
|
struct esp_ip4_addr addr;
|
||||||
|
addr.addr = 0;
|
||||||
|
|
||||||
|
esp_err_t err = mdns_query_a(host_name, 2000, &addr);
|
||||||
|
if(err){
|
||||||
|
if(err == ESP_ERR_NOT_FOUND){
|
||||||
|
ESP_LOGW(TAG, "%x: Host was not found!", (err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGE(TAG, "Query Failed: %x", (err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Query A: %s.local resolved to: " IPSTR, host_name, IP2STR(&addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc , char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
|
const esp_netif_inherent_config_t base_cg = { .if_key = "WIFI_STA_DEF", .if_desc = "eth2" };
|
||||||
|
esp_netif_config_t cfg = { .base = &base_cg };
|
||||||
|
esp_netif_t *sta = esp_netif_new(&cfg);
|
||||||
|
|
||||||
|
mdns_init();
|
||||||
|
|
||||||
|
mdns_hostname_set("myesp");
|
||||||
|
ESP_LOGI(TAG, "mdns hostname set to: [%s]", "myesp");
|
||||||
|
//set default mDNS instance name
|
||||||
|
mdns_instance_name_set("myesp-inst");
|
||||||
|
//structure with TXT records
|
||||||
|
mdns_txt_item_t serviceTxtData[3] = {
|
||||||
|
{"board","esp32"},
|
||||||
|
{"u","user"},
|
||||||
|
{"p","password"}
|
||||||
|
};
|
||||||
|
vTaskDelay(1000);
|
||||||
|
ESP_ERROR_CHECK(mdns_service_add("myesp-service2", "_http", "_tcp", 80, serviceTxtData, 3));
|
||||||
|
vTaskDelay(2000);
|
||||||
|
|
||||||
|
query_mdns_host("david-comp");
|
||||||
|
vTaskDelay(2000);
|
||||||
|
esp_netif_destroy(sta);
|
||||||
|
mdns_free();
|
||||||
|
ESP_LOGI(TAG, "Exit");
|
||||||
|
return 0;
|
||||||
|
}
|
504
components/mdns/mdns_networking_socket.c
Normal file
504
components/mdns/mdns_networking_socket.c
Normal file
@@ -0,0 +1,504 @@
|
|||||||
|
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MDNS Server Networking module implemented using BSD sockets
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "mdns_networking.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_LINUX)
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern mdns_server_t * _mdns_server;
|
||||||
|
|
||||||
|
static const char *TAG = "MDNS_Networking";
|
||||||
|
static bool s_run_sock_recv_task = false;
|
||||||
|
static int create_socket(esp_netif_t *netif);
|
||||||
|
static int join_mdns_multicast_group(int sock, esp_netif_t *netif, mdns_ip_protocol_t ip_protocol);
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_LINUX)
|
||||||
|
// Need to define packet buffer struct on linux
|
||||||
|
struct pbuf {
|
||||||
|
struct pbuf * next;
|
||||||
|
void * payload;
|
||||||
|
size_t tot_len;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
// Compatibility define to access sock-addr struct the same way for lwip and linux
|
||||||
|
#define s6_addr32 un.u32_addr
|
||||||
|
#endif // CONFIG_IDF_TARGET_LINUX
|
||||||
|
|
||||||
|
static void delete_socket(int sock)
|
||||||
|
{
|
||||||
|
close(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct udp_pcb* sock_to_pcb(int sock)
|
||||||
|
{
|
||||||
|
if (sock < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// Note: sock=0 is a valid descriptor, so save it as +1 ("1" is a valid pointer)
|
||||||
|
intptr_t sock_plus_one = sock + 1;
|
||||||
|
return (struct udp_pcb*)sock_plus_one;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcb_to_sock(struct udp_pcb* pcb)
|
||||||
|
{
|
||||||
|
if (pcb == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
intptr_t sock_plus_one = (intptr_t)pcb;
|
||||||
|
return sock_plus_one - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* _mdns_get_packet_data(mdns_rx_packet_t *packet)
|
||||||
|
{
|
||||||
|
return packet->pb->payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _mdns_get_packet_len(mdns_rx_packet_t *packet)
|
||||||
|
{
|
||||||
|
return packet->pb->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _mdns_packet_free(mdns_rx_packet_t *packet)
|
||||||
|
{
|
||||||
|
free(packet->pb->payload);
|
||||||
|
free(packet->pb);
|
||||||
|
free(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||||
|
{
|
||||||
|
struct udp_pcb * pcb = _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb;
|
||||||
|
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb = NULL;
|
||||||
|
if (_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].pcb == NULL &&
|
||||||
|
_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].pcb == NULL) {
|
||||||
|
// if the interface for both protocol uninitialized, close the interface socket
|
||||||
|
int sock = pcb_to_sock(pcb);
|
||||||
|
if (sock >= 0) {
|
||||||
|
delete_socket(sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<MDNS_IF_MAX; i++) {
|
||||||
|
for (int j=0; j<MDNS_IP_PROTOCOL_MAX; j++) {
|
||||||
|
if (_mdns_server->interfaces[i].pcbs[j].pcb)
|
||||||
|
// If any of the interfaces/protocol initialized
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no interface alive, stop the rx task
|
||||||
|
s_run_sock_recv_task = false;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_LINUX)
|
||||||
|
#ifdef CONFIG_LWIP_IPV6
|
||||||
|
static char* inet6_ntoa_r(struct in6_addr addr, char* ptr, size_t size)
|
||||||
|
{
|
||||||
|
inet_ntop(AF_INET6, &(addr.s6_addr32[0]), ptr, size);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_LWIP_IPV6
|
||||||
|
static char* inet_ntoa_r(struct in_addr addr, char* ptr, size_t size)
|
||||||
|
{
|
||||||
|
char * res = inet_ntoa(addr);
|
||||||
|
if (res && strlen(res) < size) {
|
||||||
|
strcpy(ptr, res);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_IDF_TARGET_LINUX
|
||||||
|
|
||||||
|
static inline char* get_string_address(struct sockaddr_storage *source_addr)
|
||||||
|
{
|
||||||
|
static char address_str[40]; // 40=(8*4+7+term) is the max size of ascii IPv6 addr "XXXX:XX...XX:XXXX"
|
||||||
|
char *res = NULL;
|
||||||
|
// Convert ip address to string
|
||||||
|
if (source_addr->ss_family == PF_INET) {
|
||||||
|
res = inet_ntoa_r(((struct sockaddr_in *)source_addr)->sin_addr, address_str, sizeof(address_str));
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_LWIP_IPV6
|
||||||
|
else if (source_addr->ss_family == PF_INET6) {
|
||||||
|
res = inet6_ntoa_r(((struct sockaddr_in6 *)source_addr)->sin6_addr, address_str, sizeof(address_str));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!res) {
|
||||||
|
address_str[0] = '\0'; // Returns empty string if conversion didn't succeed
|
||||||
|
}
|
||||||
|
return address_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline size_t espaddr_to_inet(const esp_ip_addr_t *addr, const uint16_t port, const mdns_ip_protocol_t ip_protocol, struct sockaddr_storage *in_addr)
|
||||||
|
{
|
||||||
|
size_t ss_addr_len = 0;
|
||||||
|
memset(in_addr, 0, sizeof(struct sockaddr_storage));
|
||||||
|
if (ip_protocol == MDNS_IP_PROTOCOL_V4 && addr->type == ESP_IPADDR_TYPE_V4) {
|
||||||
|
in_addr->ss_family = PF_INET;
|
||||||
|
#if !defined(CONFIG_IDF_TARGET_LINUX)
|
||||||
|
in_addr->s2_len = sizeof(struct sockaddr_in);
|
||||||
|
#endif
|
||||||
|
ss_addr_len = sizeof(struct sockaddr_in);
|
||||||
|
struct sockaddr_in *in_addr_ip4 = (struct sockaddr_in *) in_addr;
|
||||||
|
in_addr_ip4->sin_port = port;
|
||||||
|
in_addr_ip4->sin_addr.s_addr = addr->u_addr.ip4.addr;
|
||||||
|
}
|
||||||
|
#if CONFIG_LWIP_IPV6
|
||||||
|
else if (ip_protocol == MDNS_IP_PROTOCOL_V6 && addr->type == ESP_IPADDR_TYPE_V6) {
|
||||||
|
memset(in_addr, 0, sizeof(struct sockaddr_storage));
|
||||||
|
in_addr->ss_family = PF_INET6;
|
||||||
|
#if !defined(CONFIG_IDF_TARGET_LINUX)
|
||||||
|
in_addr->s2_len = sizeof(struct sockaddr_in6);
|
||||||
|
#endif
|
||||||
|
ss_addr_len = sizeof(struct sockaddr_in6);
|
||||||
|
struct sockaddr_in6 * in_addr_ip6 = (struct sockaddr_in6 *)in_addr;
|
||||||
|
uint32_t *u32_addr = in_addr_ip6->sin6_addr.s6_addr32;
|
||||||
|
in_addr_ip6->sin6_port = port;
|
||||||
|
u32_addr[0] = addr->u_addr.ip6.addr[0];
|
||||||
|
u32_addr[1] = addr->u_addr.ip6.addr[1];
|
||||||
|
u32_addr[2] = addr->u_addr.ip6.addr[2];
|
||||||
|
u32_addr[3] = addr->u_addr.ip6.addr[3];
|
||||||
|
}
|
||||||
|
#endif // CONFIG_LWIP_IPV6
|
||||||
|
return ss_addr_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t * data, size_t len)
|
||||||
|
{
|
||||||
|
int sock = pcb_to_sock(_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb);
|
||||||
|
if (sock < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct sockaddr_storage in_addr;
|
||||||
|
size_t ss_size = espaddr_to_inet(ip, htons(port), ip_protocol, &in_addr);
|
||||||
|
if (!ss_size) {
|
||||||
|
ESP_LOGE(TAG, "espaddr_to_inet() failed: Mismatch of IP protocols");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "[sock=%d]: Sending to IP %s port %d", sock, get_string_address(&in_addr), port);
|
||||||
|
ssize_t actual_len = sendto(sock, data, len, 0, (struct sockaddr *)&in_addr, ss_size);
|
||||||
|
if (actual_len < 0) {
|
||||||
|
ESP_LOGE(TAG, "[sock=%d]: _mdns_udp_pcb_write sendto() has failed\n error=%d: %s", sock, errno, strerror(errno));
|
||||||
|
}
|
||||||
|
return actual_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void inet_to_espaddr(const struct sockaddr_storage *in_addr, esp_ip_addr_t *addr, uint16_t *port)
|
||||||
|
{
|
||||||
|
if (in_addr->ss_family == PF_INET) {
|
||||||
|
struct sockaddr_in * in_addr_ip4 = (struct sockaddr_in *)in_addr;
|
||||||
|
memset(addr, 0, sizeof(esp_ip_addr_t));
|
||||||
|
*port = in_addr_ip4->sin_port;
|
||||||
|
addr->u_addr.ip4.addr = in_addr_ip4->sin_addr.s_addr;
|
||||||
|
addr->type = ESP_IPADDR_TYPE_V4;
|
||||||
|
}
|
||||||
|
#if CONFIG_LWIP_IPV6
|
||||||
|
else if (in_addr->ss_family == PF_INET6) {
|
||||||
|
struct sockaddr_in6 * in_addr_ip6 = (struct sockaddr_in6 *)in_addr;
|
||||||
|
memset(addr, 0, sizeof(esp_ip_addr_t));
|
||||||
|
*port = in_addr_ip6->sin6_port;
|
||||||
|
uint32_t *u32_addr = in_addr_ip6->sin6_addr.s6_addr32;
|
||||||
|
if (u32_addr[0] == 0 && u32_addr[1] == 0 && u32_addr[2] == esp_netif_htonl(0x0000FFFFUL)) {
|
||||||
|
// Mapped IPv4 address, convert directly to IPv4
|
||||||
|
addr->type = ESP_IPADDR_TYPE_V4;
|
||||||
|
addr->u_addr.ip4.addr = u32_addr[3];
|
||||||
|
} else {
|
||||||
|
addr->type = ESP_IPADDR_TYPE_V6;
|
||||||
|
addr->u_addr.ip6.addr[0] = u32_addr[0];
|
||||||
|
addr->u_addr.ip6.addr[1] = u32_addr[1];
|
||||||
|
addr->u_addr.ip6.addr[2] = u32_addr[2];
|
||||||
|
addr->u_addr.ip6.addr[3] = u32_addr[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // CONFIG_LWIP_IPV6
|
||||||
|
}
|
||||||
|
|
||||||
|
void sock_recv_task(void* arg)
|
||||||
|
{
|
||||||
|
while (s_run_sock_recv_task) {
|
||||||
|
struct timeval tv = {
|
||||||
|
.tv_sec = 1,
|
||||||
|
.tv_usec = 0,
|
||||||
|
};
|
||||||
|
fd_set rfds;
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
int max_sock = -1;
|
||||||
|
for (int i=0; i<MDNS_IF_MAX; i++) {
|
||||||
|
for (int j=0; j<MDNS_IP_PROTOCOL_MAX; j++) {
|
||||||
|
int sock = pcb_to_sock(_mdns_server->interfaces[i].pcbs[j].pcb);
|
||||||
|
if (sock >= 0) {
|
||||||
|
FD_SET(sock, &rfds);
|
||||||
|
max_sock = MAX(max_sock, sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (max_sock < 0) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
ESP_LOGI(TAG, "No sock!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int s = select(max_sock + 1, &rfds, NULL, NULL, &tv);
|
||||||
|
if (s < 0) {
|
||||||
|
ESP_LOGE(TAG, "Select failed: errno %d", errno);
|
||||||
|
break;
|
||||||
|
} else if (s > 0) {
|
||||||
|
for (int tcpip_if=0; tcpip_if<MDNS_IF_MAX; tcpip_if++) {
|
||||||
|
// Both protocols share once socket
|
||||||
|
int sock = pcb_to_sock(_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].pcb);
|
||||||
|
if (sock < 0) {
|
||||||
|
sock = pcb_to_sock(_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].pcb);
|
||||||
|
}
|
||||||
|
if (sock < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (FD_ISSET(sock, &rfds)) {
|
||||||
|
static char recvbuf[MDNS_MAX_PACKET_SIZE];
|
||||||
|
uint16_t port = 0;
|
||||||
|
|
||||||
|
struct sockaddr_storage raddr; // Large enough for both IPv4 or IPv6
|
||||||
|
socklen_t socklen = sizeof(struct sockaddr_storage);
|
||||||
|
esp_ip_addr_t addr = {0};
|
||||||
|
int len = recvfrom(sock, recvbuf, sizeof(recvbuf), 0,
|
||||||
|
(struct sockaddr *) &raddr, &socklen);
|
||||||
|
if (len < 0) {
|
||||||
|
ESP_LOGE(TAG, "multicast recvfrom failed: errno %d", errno);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "[sock=%d]: Received from IP:%s", sock, get_string_address(&raddr));
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(TAG, recvbuf, len, ESP_LOG_VERBOSE);
|
||||||
|
inet_to_espaddr(&raddr, &addr, &port);
|
||||||
|
|
||||||
|
// Allocate the packet structure and pass it to the mdns main engine
|
||||||
|
mdns_rx_packet_t *packet = (mdns_rx_packet_t *) calloc(1, sizeof(mdns_rx_packet_t));
|
||||||
|
struct pbuf *packet_pbuf = calloc(1, sizeof(struct pbuf));
|
||||||
|
uint8_t *buf = malloc(len);
|
||||||
|
if (packet == NULL || packet_pbuf == NULL || buf == NULL ) {
|
||||||
|
free(buf);
|
||||||
|
free(packet_pbuf);
|
||||||
|
free(packet);
|
||||||
|
HOOK_MALLOC_FAILED;
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate the mdns packet");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
memcpy(buf, recvbuf, len);
|
||||||
|
packet_pbuf->next = NULL;
|
||||||
|
packet_pbuf->payload = buf;
|
||||||
|
packet_pbuf->tot_len = len;
|
||||||
|
packet_pbuf->len = len;
|
||||||
|
packet->tcpip_if = tcpip_if;
|
||||||
|
packet->pb = packet_pbuf;
|
||||||
|
packet->src_port = ntohs(port);
|
||||||
|
memcpy(&packet->src, &addr, sizeof(esp_ip_addr_t));
|
||||||
|
// TODO(IDF-3651): Add the correct dest addr -- for mdns to decide multicast/unicast
|
||||||
|
// Currently it's enough to assume the packet is multicast and mdns to check the source port of the packet
|
||||||
|
memset(&packet->dest, 0, sizeof(esp_ip_addr_t));
|
||||||
|
packet->multicast = 1;
|
||||||
|
packet->dest.type = packet->src.type;
|
||||||
|
packet->ip_protocol =
|
||||||
|
packet->src.type == ESP_IPADDR_TYPE_V4 ? MDNS_IP_PROTOCOL_V4 : MDNS_IP_PROTOCOL_V6;
|
||||||
|
if (!_mdns_server || !_mdns_server->action_queue || _mdns_send_rx_action(packet) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "_mdns_send_rx_action failed!");
|
||||||
|
free(packet->pb->payload);
|
||||||
|
free(packet->pb);
|
||||||
|
free(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mdns_networking_init(void)
|
||||||
|
{
|
||||||
|
if (s_run_sock_recv_task == false) {
|
||||||
|
s_run_sock_recv_task = true;
|
||||||
|
xTaskCreate( sock_recv_task, "mdns recv task", 3*1024, NULL, 5, NULL );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct udp_pcb* create_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||||
|
{
|
||||||
|
if (_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb) {
|
||||||
|
return _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb;
|
||||||
|
}
|
||||||
|
mdns_ip_protocol_t other_ip_proto = ip_protocol==MDNS_IP_PROTOCOL_V4?MDNS_IP_PROTOCOL_V6:MDNS_IP_PROTOCOL_V4;
|
||||||
|
esp_netif_t *netif = _mdns_get_esp_netif(tcpip_if);
|
||||||
|
if (_mdns_server->interfaces[tcpip_if].pcbs[other_ip_proto].pcb) {
|
||||||
|
struct udp_pcb* other_pcb = _mdns_server->interfaces[tcpip_if].pcbs[other_ip_proto].pcb;
|
||||||
|
int err = join_mdns_multicast_group(pcb_to_sock(other_pcb), netif, ip_protocol);
|
||||||
|
if (err < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to add ipv6 multicast group for protocol %d", ip_protocol);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return other_pcb;
|
||||||
|
}
|
||||||
|
int sock = create_socket(netif);
|
||||||
|
if (sock < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create the socket!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
int err = join_mdns_multicast_group(sock, netif, ip_protocol);
|
||||||
|
if (err < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to add ipv6 multicast group for protocol %d", ip_protocol);
|
||||||
|
}
|
||||||
|
return sock_to_pcb(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "_mdns_pcb_init(tcpip_if=%d, ip_protocol=%d)", tcpip_if, ip_protocol);
|
||||||
|
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb = create_pcb(tcpip_if, ip_protocol);
|
||||||
|
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].failed_probes = 0;
|
||||||
|
|
||||||
|
mdns_networking_init();
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int create_socket(esp_netif_t *netif)
|
||||||
|
{
|
||||||
|
#if CONFIG_LWIP_IPV6
|
||||||
|
int sock = socket(PF_INET6, SOCK_DGRAM, 0);
|
||||||
|
#else
|
||||||
|
int sock = socket(PF_INET, SOCK_DGRAM, 0);
|
||||||
|
#endif
|
||||||
|
if (sock < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create socket. Error %d", errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int on = 1;
|
||||||
|
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ) < 0) {
|
||||||
|
ESP_LOGE(TAG, "setsockopt SO_REUSEADDR: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
// Bind the socket to any address
|
||||||
|
#if CONFIG_LWIP_IPV6
|
||||||
|
struct sockaddr_in6 saddr = { INADDR_ANY };
|
||||||
|
saddr.sin6_family = AF_INET6;
|
||||||
|
saddr.sin6_port = htons(5353);
|
||||||
|
bzero(&saddr.sin6_addr.s6_addr, sizeof(saddr.sin6_addr.s6_addr));
|
||||||
|
int err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6));
|
||||||
|
if (err < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to bind socket. Error %d", errno);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
struct sockaddr_in saddr = { 0 };
|
||||||
|
saddr.sin_family = AF_INET;
|
||||||
|
saddr.sin_port = htons(5353);
|
||||||
|
bzero(&saddr.sin_addr.s_addr, sizeof(saddr.sin_addr.s_addr));
|
||||||
|
int err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
|
||||||
|
if (err < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to bind socket. Error %d", errno);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_LWIP_IPV6
|
||||||
|
struct ifreq ifr;
|
||||||
|
esp_netif_get_netif_impl_name(netif, ifr.ifr_name);
|
||||||
|
int ret = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(struct ifreq));
|
||||||
|
if (ret < 0) {
|
||||||
|
ESP_LOGE(TAG, "\"%s\" Unable to bind socket to specified interface: errno %d", esp_netif_get_desc(netif), errno);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sock;
|
||||||
|
|
||||||
|
err:
|
||||||
|
close(sock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CONFIG_LWIP_IPV6
|
||||||
|
static int socket_add_ipv6_multicast_group(int sock, esp_netif_t *netif)
|
||||||
|
{
|
||||||
|
int ifindex = esp_netif_get_netif_impl_index(netif);
|
||||||
|
int err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex));
|
||||||
|
if (err < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set IPV6_MULTICAST_IF. Error %d", errno);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ipv6_mreq v6imreq = { 0 };
|
||||||
|
esp_ip_addr_t multi_addr = ESP_IP6ADDR_INIT(0x000002ff, 0, 0, 0xfb000000);
|
||||||
|
memcpy(&v6imreq.ipv6mr_multiaddr, &multi_addr.u_addr.ip6.addr, sizeof(v6imreq.ipv6mr_multiaddr));
|
||||||
|
v6imreq.ipv6mr_interface = ifindex;
|
||||||
|
err = setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &v6imreq, sizeof(struct ipv6_mreq));
|
||||||
|
if (err < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set IPV6_ADD_MEMBERSHIP. Error %d", errno);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_LWIP_IPV6
|
||||||
|
|
||||||
|
static int socket_add_ipv4_multicast_group(int sock, esp_netif_t *netif)
|
||||||
|
{
|
||||||
|
struct ip_mreq imreq = { 0 };
|
||||||
|
int err = 0;
|
||||||
|
esp_netif_ip_info_t ip_info = { 0 };
|
||||||
|
|
||||||
|
if (esp_netif_get_ip_info(netif, &ip_info) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to esp_netif_get_ip_info()");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
imreq.imr_interface.s_addr = ip_info.ip.addr;
|
||||||
|
|
||||||
|
esp_ip_addr_t multicast_addr = ESP_IP4ADDR_INIT(224, 0, 0, 251);
|
||||||
|
imreq.imr_multiaddr.s_addr = multicast_addr.u_addr.ip4.addr;
|
||||||
|
|
||||||
|
err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(struct ip_mreq));
|
||||||
|
if (err < 0) {
|
||||||
|
ESP_LOGE(TAG, "%d %s", sock, strerror(errno));
|
||||||
|
ESP_LOGE(TAG, "Failed to set IP_ADD_MEMBERSHIP. Error %d", errno);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int join_mdns_multicast_group(int sock, esp_netif_t *netif, mdns_ip_protocol_t ip_protocol)
|
||||||
|
{
|
||||||
|
if (ip_protocol == MDNS_IP_PROTOCOL_V4) {
|
||||||
|
return socket_add_ipv4_multicast_group(sock, netif);
|
||||||
|
}
|
||||||
|
#if CONFIG_LWIP_IPV6
|
||||||
|
if (ip_protocol == MDNS_IP_PROTOCOL_V6) {
|
||||||
|
return socket_add_ipv6_multicast_group(sock, netif);
|
||||||
|
}
|
||||||
|
#endif // CONFIG_LWIP_IPV6
|
||||||
|
return -1;
|
||||||
|
}
|
@@ -14,8 +14,10 @@
|
|||||||
#ifndef MDNS_PRIVATE_H_
|
#ifndef MDNS_PRIVATE_H_
|
||||||
#define MDNS_PRIVATE_H_
|
#define MDNS_PRIVATE_H_
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
#include "mdns.h"
|
#include "mdns.h"
|
||||||
#include "esp_task.h"
|
#include "esp_task.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
|
||||||
//#define MDNS_ENABLE_DEBUG
|
//#define MDNS_ENABLE_DEBUG
|
||||||
|
|
||||||
@@ -248,7 +250,6 @@ typedef struct mdns_parsed_record_s {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
mdns_if_t tcpip_if;
|
mdns_if_t tcpip_if;
|
||||||
mdns_ip_protocol_t ip_protocol;
|
mdns_ip_protocol_t ip_protocol;
|
||||||
//struct udp_pcb *pcb;
|
|
||||||
esp_ip_addr_t src;
|
esp_ip_addr_t src;
|
||||||
uint16_t src_port;
|
uint16_t src_port;
|
||||||
uint8_t multicast;
|
uint8_t multicast;
|
||||||
|
Reference in New Issue
Block a user