175 lines
5.4 KiB
C
175 lines
5.4 KiB
C
|
/*
|
||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||
|
*
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
*/
|
||
|
#include "freertos/FreeRTOS.h"
|
||
|
#include "freertos/event_groups.h"
|
||
|
#include "tinyusb_net.h"
|
||
|
#include "descriptors_control.h"
|
||
|
#include "usb_descriptors.h"
|
||
|
#include "device/usbd_pvt.h"
|
||
|
#include "esp_check.h"
|
||
|
|
||
|
#define MAC_ADDR_LEN 6
|
||
|
|
||
|
typedef struct packet {
|
||
|
void *buffer;
|
||
|
void *buff_free_arg;
|
||
|
uint16_t len;
|
||
|
esp_err_t result;
|
||
|
} packet_t;
|
||
|
|
||
|
struct tinyusb_net_handle {
|
||
|
bool initialized;
|
||
|
SemaphoreHandle_t buffer_sema;
|
||
|
EventGroupHandle_t tx_flags;
|
||
|
tusb_net_rx_cb_t rx_cb;
|
||
|
tusb_net_free_tx_cb_t tx_buff_free_cb;
|
||
|
tusb_net_init_cb_t init_cb;
|
||
|
char mac_str[2 * MAC_ADDR_LEN + 1];
|
||
|
void *ctx;
|
||
|
packet_t *packet_to_send;
|
||
|
};
|
||
|
|
||
|
const static int TX_FINISHED_BIT = BIT0;
|
||
|
static struct tinyusb_net_handle s_net_obj = { };
|
||
|
static const char *TAG = "tusb_net";
|
||
|
|
||
|
static void do_send_sync(void *ctx)
|
||
|
{
|
||
|
(void) ctx;
|
||
|
if (xSemaphoreTake(s_net_obj.buffer_sema, 0) != pdTRUE || s_net_obj.packet_to_send == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
packet_t *packet = s_net_obj.packet_to_send;
|
||
|
if (tud_network_can_xmit(packet->len)) {
|
||
|
tud_network_xmit(packet, packet->len);
|
||
|
packet->result = ESP_OK;
|
||
|
} else {
|
||
|
packet->result = ESP_FAIL;
|
||
|
}
|
||
|
xSemaphoreGive(s_net_obj.buffer_sema);
|
||
|
xEventGroupSetBits(s_net_obj.tx_flags, TX_FINISHED_BIT);
|
||
|
}
|
||
|
|
||
|
static void do_send_async(void *ctx)
|
||
|
{
|
||
|
packet_t *packet = ctx;
|
||
|
if (tud_network_can_xmit(packet->len)) {
|
||
|
tud_network_xmit(packet, packet->len);
|
||
|
} else if (s_net_obj.tx_buff_free_cb) {
|
||
|
ESP_LOGW(TAG, "Packet cannot be accepted on USB interface, dropping");
|
||
|
s_net_obj.tx_buff_free_cb(packet->buff_free_arg, s_net_obj.ctx);
|
||
|
}
|
||
|
free(packet);
|
||
|
}
|
||
|
|
||
|
esp_err_t tinyusb_net_send_async(void *buffer, uint16_t len, void *buff_free_arg)
|
||
|
{
|
||
|
if (!tud_ready()) {
|
||
|
return ESP_ERR_INVALID_STATE;
|
||
|
}
|
||
|
|
||
|
packet_t *packet = calloc(1, sizeof(packet_t));
|
||
|
packet->len = len;
|
||
|
packet->buffer = buffer;
|
||
|
packet->buff_free_arg = buff_free_arg;
|
||
|
ESP_RETURN_ON_FALSE(packet, ESP_ERR_NO_MEM, TAG, "Failed to allocate packet to send");
|
||
|
usbd_defer_func(do_send_async, packet, false);
|
||
|
return ESP_OK;
|
||
|
}
|
||
|
|
||
|
esp_err_t tinyusb_net_send_sync(void *buffer, uint16_t len, void *buff_free_arg, TickType_t timeout)
|
||
|
{
|
||
|
if (!tud_ready()) {
|
||
|
return ESP_ERR_INVALID_STATE;
|
||
|
}
|
||
|
|
||
|
// Lazy init the flags and semaphores, as they might not be needed (if async approach is used)
|
||
|
if (!s_net_obj.tx_flags) {
|
||
|
s_net_obj.tx_flags = xEventGroupCreate();
|
||
|
ESP_RETURN_ON_FALSE(s_net_obj.tx_flags, ESP_ERR_NO_MEM, TAG, "Failed to allocate event flags");
|
||
|
}
|
||
|
if (!s_net_obj.buffer_sema) {
|
||
|
s_net_obj.buffer_sema = xSemaphoreCreateBinary();
|
||
|
ESP_RETURN_ON_FALSE(s_net_obj.buffer_sema, ESP_ERR_NO_MEM, TAG, "Failed to allocate buffer semaphore");
|
||
|
}
|
||
|
|
||
|
packet_t packet = {
|
||
|
.buffer = buffer,
|
||
|
.len = len,
|
||
|
.buff_free_arg = buff_free_arg
|
||
|
};
|
||
|
s_net_obj.packet_to_send = &packet;
|
||
|
xSemaphoreGive(s_net_obj.buffer_sema); // now the packet is ready, let's mark it available to tusb send
|
||
|
|
||
|
// to execute the send function in tinyUSB task context
|
||
|
usbd_defer_func(do_send_sync, NULL, false); // arg=NULL -> sync send, we keep the packet inside the object
|
||
|
|
||
|
// wait wor completion with defined timeout
|
||
|
EventBits_t bits = xEventGroupWaitBits(s_net_obj.tx_flags, TX_FINISHED_BIT, pdTRUE, pdTRUE, timeout);
|
||
|
xSemaphoreTake(s_net_obj.buffer_sema, portMAX_DELAY); // if tusb sending already started, we have wait before ditching the packet
|
||
|
s_net_obj.packet_to_send = NULL; // invalidate the argument
|
||
|
if (bits & TX_FINISHED_BIT) { // If transaction finished, return error code
|
||
|
return packet.result;
|
||
|
}
|
||
|
return ESP_ERR_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
esp_err_t tinyusb_net_init(tinyusb_usbdev_t usb_dev, const tinyusb_net_config_t *cfg)
|
||
|
{
|
||
|
(void) usb_dev;
|
||
|
|
||
|
ESP_RETURN_ON_FALSE(s_net_obj.initialized == false, ESP_ERR_INVALID_STATE, TAG, "TinyUSB Net class is already initialized");
|
||
|
|
||
|
// the semaphore and event flags are initialized only if needed
|
||
|
s_net_obj.rx_cb = cfg->on_recv_callback;
|
||
|
s_net_obj.init_cb = cfg->on_init_callback;
|
||
|
s_net_obj.tx_buff_free_cb = cfg->free_tx_buffer;
|
||
|
s_net_obj.ctx = cfg->user_context;
|
||
|
|
||
|
const uint8_t *mac = &cfg->mac_addr[0];
|
||
|
snprintf(s_net_obj.mac_str, sizeof(s_net_obj.mac_str), "%02X%02X%02X%02X%02X%02X",
|
||
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||
|
uint8_t mac_id = tusb_get_mac_string_id();
|
||
|
// Pass it to Descriptor control module
|
||
|
tinyusb_set_str_descriptor(s_net_obj.mac_str, mac_id);
|
||
|
|
||
|
s_net_obj.initialized = true;
|
||
|
|
||
|
return ESP_OK;
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------+
|
||
|
// tinyusb callbacks
|
||
|
//--------------------------------------------------------------------+
|
||
|
bool tud_network_recv_cb(const uint8_t *src, uint16_t size)
|
||
|
{
|
||
|
if (s_net_obj.rx_cb) {
|
||
|
s_net_obj.rx_cb((void *)src, size, s_net_obj.ctx);
|
||
|
}
|
||
|
tud_network_recv_renew();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg)
|
||
|
{
|
||
|
packet_t *packet = ref;
|
||
|
uint16_t len = arg;
|
||
|
|
||
|
memcpy(dst, packet->buffer, packet->len);
|
||
|
if (s_net_obj.tx_buff_free_cb) {
|
||
|
s_net_obj.tx_buff_free_cb(packet->buff_free_arg, s_net_obj.ctx);
|
||
|
}
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
void tud_network_init_cb(void)
|
||
|
{
|
||
|
if (s_net_obj.init_cb) {
|
||
|
s_net_obj.init_cb(s_net_obj.ctx);
|
||
|
}
|
||
|
}
|