CLSH1001-Firmware/managed_components/espressif__esp_tinyusb/tinyusb_net.c
2024-03-05 16:09:49 -06:00

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);
}
}