Skip to content

File ns_usb.c

File List > neuralSPOT > neuralspot > ns-usb > src > ns_usb.c

Go to the documentation of this file

#include "ns_usb.h"
#include "ns_ambiqsuite_harness.h"
#include "ns_core.h"
#include "ns_timer.h"
#include "tusb.h"

const ns_core_api_t ns_usb_V0_0_1 = {.apiId = NS_USB_API_ID, .version = NS_USB_V0_0_1};

const ns_core_api_t ns_usb_V1_0_0 = {.apiId = NS_USB_API_ID, .version = NS_USB_V1_0_0};

const ns_core_api_t ns_usb_oldest_supported_version = {
    .apiId = NS_USB_API_ID, .version = NS_USB_V0_0_1};

const ns_core_api_t ns_usb_current_version = {.apiId = NS_USB_API_ID, .version = NS_USB_V1_0_0};

ns_usb_config_t usb_config = {
    .api = &ns_usb_V1_0_0,
    .deviceType = NS_USB_CDC_DEVICE,
    .rx_buffer = NULL,
    .rx_bufferLength = 0,
    .tx_buffer = NULL,
    .tx_bufferLength = 0,
    .rx_cb = NULL,
    .tx_cb = NULL,
    .service_cb = NULL,
    .desc_url = NULL};

volatile uint8_t gGotUSBRx = 0;

// volatile const void *pTUSB_WeakFcnPointers[] =
// {
//     (void *)tud_mount_cb,
// #ifndef TUSB_ADDED_FUNCTIONS
//     (void *)tud_umount_cb,
// #endif
//     (void *)tud_suspend_cb,
//     (void *)tud_resume_cb,
//     (void *)tud_cdc_line_state_cb,
//     (void *)tud_cdc_rx_cb,
//     (void *)tud_vendor_rx_cb,
//     (void *)tud_vendor_control_xfer_cb,
//     (void *)tud_descriptor_bos_cb,
//     (void *)tud_descriptor_device_cb,
//     (void *)tud_descriptor_configuration_cb,
//     (void *)tud_descriptor_string_cb,
// };

bool ns_usb_data_available(usb_handle_t handle) { return (gGotUSBRx == 1); }

uint32_t ns_get_cdc_rx_bufferLength() { return usb_config.rx_bufferLength; }
uint32_t ns_get_cdc_tx_bufferLength() { return usb_config.tx_bufferLength; }

uint8_t *ns_usb_get_rx_buffer() { return usb_config.rx_buffer; }

uint8_t *ns_usb_get_tx_buffer() { return usb_config.tx_buffer; }

static void ns_usb_service_callback(ns_timer_config_t *c) {
    // Invoked in ISR context
    // ns_lp_printf("U");
    tud_task();
    if (usb_config.service_cb != NULL) {
        usb_config.service_cb(gGotUSBRx);
        // ns_lp_printf("got usb rx %d\n", gGotUSBRx);
    }
}

ns_timer_config_t g_ns_usbTimer = {
    .api = &ns_timer_V1_0_0,
    .timer = NS_TIMER_USB,
    .enableInterrupt = true,
    .periodInMicroseconds = 1000,
    .callback = ns_usb_service_callback};

uint32_t ns_usb_init(ns_usb_config_t *cfg, usb_handle_t *h) {

#ifndef NS_DISABLE_API_VALIDATION
    if (cfg == NULL) {
        return NS_STATUS_INVALID_HANDLE;
    }

    if (ns_core_check_api(cfg->api, &ns_usb_oldest_supported_version, &ns_usb_current_version)) {
        return NS_STATUS_INVALID_VERSION;
    }

    if ((cfg->rx_buffer == NULL) || (cfg->tx_buffer == NULL)) {
        return NS_STATUS_INVALID_CONFIG;
    }
#endif

    usb_config.deviceType = cfg->deviceType;
    usb_config.rx_buffer = cfg->rx_buffer;
    usb_config.rx_bufferLength = cfg->rx_bufferLength;
    usb_config.tx_buffer = cfg->tx_buffer;
    usb_config.tx_bufferLength = cfg->tx_bufferLength;
    usb_config.rx_cb = cfg->rx_cb;
    usb_config.tx_cb = cfg->tx_cb;
    usb_config.service_cb = cfg->service_cb;
    usb_config.desc_url = cfg->desc_url;
    *h = (void *)&usb_config;
    tusb_init();

    // Set up a timer to service usb

    NS_TRY(ns_timer_init(&g_ns_usbTimer), "Timer Init Failed.\n");

    return NS_STATUS_SUCCESS;
}

void ns_usb_register_callbacks(usb_handle_t handle, ns_usb_rx_cb rxcb, ns_usb_tx_cb txcb) {
    ((ns_usb_config_t *)handle)->rx_cb = rxcb;
    ((ns_usb_config_t *)handle)->tx_cb = txcb;
}

// Blocking read from USB. Will loop until we get the full buffer or timeout
uint32_t ns_usb_recieve_data(usb_handle_t handle, void *buffer, uint32_t bufsize) {

    // USB reads one block at a time, loop until we get full
    // request
    uint32_t bytes_rx = 0;
    uint32_t retries = 100000;
    uint32_t block_retries = 15; // number of rx blocks we'll retry

    // if (gGotUSBRx == 0)
    //     ns_lp_printf("Kicking off read of %d, have %d, sem %d \n", bufsize, tud_cdc_available(),
    //             gGotUSBRx);
    // uint32_t before = tud_cdc_available();
    // uint8_t before_sem = gGotUSBRx;
    // ns_delay_us(10);
    // uint32_t after = tud_cdc_available();
    // uint8_t after_sem = gGotUSBRx;
    while (tud_cdc_available() < bufsize) {
        // If there isn't enough data to satisfy request, wait for a while

        // ns_lp_printf("Mystery path after %d %d %d\n", after, after_sem, gGotUSBRx);

        // We only care abot gGotUSBRx in order to count 'blocks' (i.e. USB RX interrupts)
        ns_interrupt_master_disable(); // critical region
        if (tud_cdc_available() < bufsize) {
            gGotUSBRx = 0; // set to 1 in IRQ context, need to disable IRQs for a bit
        }
        ns_interrupt_master_enable();

        // Wait for a block to come in
        while ((gGotUSBRx == 0) && (tud_cdc_available() < bufsize)) {
            ns_delay_us(750);
            retries--;
            if (retries == 0) {
                ns_lp_printf("[ERROR] ns_usb_recieve_data exhausted wait for sem\n");
                break;
            }
        };

        // Incoming blocks may be less than needed bytes, try up to 5 times
        if (block_retries == 0) {
            ns_lp_printf("[ERROR] ns_usb_recieve_data exhausted block retries\n");
            break;
        }
        block_retries--;
    }
    // uint32_t after2 = tud_cdc_available();
    // uint8_t after2_sem = gGotUSBRx;
    gGotUSBRx = 0;
    bytes_rx = tud_cdc_read((void *)buffer, bufsize);
    // if (retries != 10000)
    //     ns_lp_printf("rx_data ask %d got %d retries %d before cnt, sem: %d,%d, after cnt, sem:
    //     %d, %d, af2 cnt,sem: %d, %d\n",
    //         bufsize, bytes_rx, retries, before, before_sem, after, after_sem, after2,
    //         after2_sem);
    // ns_lp_printf("Got bytes %d\n", bytes_rx);
    ns_delay_us(200);

    // dontoptimizeme = after + after_sem + before + before_sem + after2 + after2_sem;
    if (bytes_rx != bufsize) {
        ns_lp_printf("[ERROR] RX error, asked for %d, got %d\n", bufsize, bytes_rx);
    }
    return bytes_rx;
}

void ns_usb_handle_read_error(usb_handle_t h) {
    int i;
    for (i = 0; i < 100; i++) {
        ns_delay_us(10000);
    }
    ns_lp_printf("In error after wait\n");
    tud_cdc_read_flush();
    gGotUSBRx = 0; // may be set by final RX
}

uint32_t ns_usb_send_data(usb_handle_t handle, void *buffer, uint32_t bufsize) {

    uint32_t bytes_tx = 0;
    // ns_lp_printf("NS USB  asked to send %d, \n", bufsize);

    while (bytes_tx < bufsize) {
        bytes_tx += tud_cdc_write((void *)(buffer + bytes_tx), bufsize - bytes_tx); // blocking
        tud_cdc_write_flush();
        // ns_lp_printf("NS USB  asked to send %d, sent %d bytes\n", bufsize, bytes_tx);
    }

    // uint32_t retval =  tud_cdc_write(buffer, bufsize);
    // tud_cdc_write_flush();
    return bytes_tx;
}

ns_tusb_desc_webusb_url_t * ns_get_desc_url() { 
    return usb_config.desc_url;
}