initial
This commit is contained in:
commit
10a8df94db
|
@ -0,0 +1 @@
|
|||
build/
|
|
@ -0,0 +1,104 @@
|
|||
# use directory name for project id
|
||||
option(USE_USBCDC_FOR_STDIO "Export an extra USB-CDC interface for stdio, instead of echoing it to a UART port (and requiring UART loopback for receiving stdio output on a host computer)." OFF)
|
||||
set(FAMILY "rp2040" CACHE STRING "Board/MCU family, decides which drivers to use. Set to RP2040 by default.")
|
||||
set(BOARD "raspberry_pi_pico" CACHE STRING "Board used, determines the pinout. Defaults to the Raspberry Pi Pico.")
|
||||
|
||||
set(PROJECT "picoftdi")
|
||||
#set(PROJECT ${BOARD}-${PROJECT})
|
||||
|
||||
# TOP is absolute path to root directory of TinyUSB git repo
|
||||
|
||||
# Check for -DFAMILY=
|
||||
if(FAMILY STREQUAL "rp2040")
|
||||
option(PICO_NO_FLASH "Disable writing the compiled program to flash, and only load it to RAM. Useful for testing, but not much else (OFF by default)." ON)
|
||||
option(PICO_COPY_TO_RAM "Run all code in RAM, while the program is also stored on flash. On bootup, everything will be copied to RAM (OFF by default)." OFF)
|
||||
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
set(TOP "$ENV{PICO_SDK_PATH}/lib/tinyusb")
|
||||
get_filename_component(TOP "${TOP}" REALPATH)
|
||||
include(cmake/pico_sdk_import.cmake)
|
||||
|
||||
include(${TOP}/hw/bsp/${FAMILY}/family.cmake)
|
||||
|
||||
#family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
|
||||
project(${PROJECT})
|
||||
family_initialize_project(PROJECT ${CMAKE_CURRENT_LIST_DIR}) # calls pico_sdk_init()
|
||||
#pico_sdk_init()
|
||||
|
||||
add_executable(${PROJECT})
|
||||
|
||||
if(USE_USBCDC_FOR_STDIO)
|
||||
# we're going to manually implement this case
|
||||
#pico_enable_stdio_uart(${PROJECT} 0)
|
||||
target_compile_definitions(${PROJECT} PUBLIC USE_USBCDC_FOR_STDIO=1)
|
||||
else()
|
||||
#pico_enable_stdio_uart(${PROJECT} 1)
|
||||
endif()
|
||||
|
||||
# TODO: separate flag for disabling this one?
|
||||
pico_enable_stdio_uart(${PROJECT} 1)
|
||||
pico_enable_stdio_usb(${PROJECT} 0)
|
||||
# Example source
|
||||
target_sources(${PROJECT} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libco/libco.S
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/thread.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ftdi.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/cdc_stdio.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/unique.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ftdi/asyncbb.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ftdi/cpufifo.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ftdi/fifo.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ftdi/ftdi_hw.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ftdi/ftdi_proto.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ftdi/mcuhost.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ftdi/mpsse.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ftdi/syncbb.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ftdi/uart.c
|
||||
)
|
||||
|
||||
# Example include
|
||||
target_include_directories(${PROJECT} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libco/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ftdi/
|
||||
)
|
||||
|
||||
# Example defines
|
||||
target_compile_definitions(${PROJECT} PUBLIC
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT} pico_stdlib pico_unique_id hardware_spi
|
||||
hardware_i2c hardware_adc hardware_pio hardware_dma hardware_pwm
|
||||
pico_fix_rp2040_usb_device_enumeration
|
||||
tinyusb_device tinyusb_board tinyusb_additions)
|
||||
|
||||
if(USE_USBCDC_FOR_STDIO)
|
||||
target_include_directories(${PROJECT} PUBLIC
|
||||
${PICO_SDK_PATH}/src/rp2_common/pico_stdio_usb/include/
|
||||
)
|
||||
|
||||
# extremely ugly hack to prevent the Pico SDK to declare *its* TinyUSB config
|
||||
# and also to modify the tinyusb board.h file a bit
|
||||
target_compile_definitions(${PROJECT} PUBLIC
|
||||
_PICO_STDIO_USB_TUSB_CONFIG_H=1
|
||||
BOARD_H_=1
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT} pico_stdio)
|
||||
endif()
|
||||
|
||||
pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ftdi/ftdi_uart_rx.pio)
|
||||
pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ftdi/ftdi_uart_tx.pio)
|
||||
|
||||
pico_add_extra_outputs(${PROJECT})
|
||||
else()
|
||||
message(FATAL_ERROR "Invalid FAMILY specified")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror=implicit-function-declaration -Werror=return-type -Werror=maybe-uninitialized")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--cref")
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# PicoFTDI
|
||||
|
||||
WIP reimplementation of the FT2232D chip from FTDI on the Raspberry Pico.
|
|
@ -0,0 +1,61 @@
|
|||
// vim: set et:
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021, Ha Thach (tinyusb.org)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef BOARD_H_MOD
|
||||
#define BOARD_H_MOD
|
||||
|
||||
#include <pico/bootrom.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
#define LED_PIN PICO_DEFAULT_LED_PIN
|
||||
#define LED_STATE_ON (!(PICO_DEFAULT_LED_PIN_INVERTED))
|
||||
#endif
|
||||
|
||||
// Button pin is BOOTSEL which is flash CS pin
|
||||
#define BUTTON_BOOTSEL
|
||||
#define BUTTON_STATE_ACTIVE 0
|
||||
|
||||
#if !defined(USE_USBCDC_FOR_STDIO) && defined(PICO_DEFAULT_UART_TX_PIN) && \
|
||||
defined(PICO_DEFAULT_UART_RX_PIN) && defined(PICO_DEFAULT_UART)
|
||||
#define UART_DEV PICO_DEFAULT_UART
|
||||
#define UART_TX_PIN PICO_DEFAULT_UART_TX_PIN
|
||||
#define UART_RX_PIN PICO_DEFAULT_UART_RX_PIN
|
||||
#endif
|
||||
|
||||
// (not actually for TinyUSB overrides)
|
||||
// Reset to bootloader
|
||||
#define bsp_reset_bootloader() reset_usb_boot(0, 0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BOARD_H_ */
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
#ifndef BSP_FEATURE_M_FTDI_H_
|
||||
#define BSP_FEATURE_M_FTDI_H_
|
||||
|
||||
#define DBOARD_HAS_FTDI
|
||||
|
||||
/* TODO: more fine-grained FTDI support/not-support stuff? */
|
||||
|
||||
#include "bsp-info.h"
|
||||
|
||||
// not all that much here
|
||||
|
||||
enum {
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
CDC_N_STDIO = 0,
|
||||
#endif
|
||||
|
||||
CDC_N__NITF
|
||||
};
|
||||
enum {
|
||||
VND_N_FTDI_IFA = 0,
|
||||
VND_N_FTDI_IFB = 1,
|
||||
|
||||
VND_N__NITF
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,30 @@
|
|||
// vim: set et:
|
||||
|
||||
#ifndef BSP_INFO_H_
|
||||
#define BSP_INFO_H_
|
||||
|
||||
/*#define USB_VID 0x2e8a*/ /* Raspberry Pi */
|
||||
#define USB_VID 0xcafe /* TinyUSB */
|
||||
/*#define USB_VID 0x1209*/ /* Generic */
|
||||
/*#define USB_VID 0x1d50*/ /* OpenMoko */
|
||||
#define USB_PID 0x1312
|
||||
|
||||
// TODO: other RP2040 boards
|
||||
#define INFO_BOARDNAME "RP2040 Pico"
|
||||
|
||||
/* each CFG_TUD_xxx macro must be the max across all modes */
|
||||
// TODO: have this depend on the DBOARD_HAS_xxx macros?
|
||||
/*#define CFG_TUD_HID 0
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
#define CFG_TUD_CDC 1
|
||||
#else
|
||||
#define CFG_TUD_CDC 0
|
||||
#endif
|
||||
#define CFG_TUD_VENDOR 2*/
|
||||
|
||||
/* don't access storage for RAM-only builds */
|
||||
#if !PICO_NO_FLASH
|
||||
#define DBOARD_HAS_STORAGE
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,166 @@
|
|||
// vim: set et:
|
||||
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
|
||||
#include <hardware/irq.h>
|
||||
#include <pico/binary_info.h>
|
||||
#include <pico/bootrom.h>
|
||||
#include <pico/mutex.h>
|
||||
#include <pico/stdio.h>
|
||||
#include <pico/stdio/driver.h>
|
||||
#include <pico/time.h>
|
||||
|
||||
#include "tusb_config.h"
|
||||
#include <tusb.h>
|
||||
|
||||
#include "board.h"
|
||||
|
||||
|
||||
// PICO_CONFIG: PICO_STDIO_USB_STDOUT_TIMEOUT_US, Number of microseconds to be blocked trying to write USB output before assuming the host has disappeared and discarding data, default=500000, group=pico_stdio_usb
|
||||
#ifndef PICO_STDIO_USB_STDOUT_TIMEOUT_US
|
||||
#define PICO_STDIO_USB_STDOUT_TIMEOUT_US 500000
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE, Enable/disable resetting into BOOTSEL mode if the host sets the baud rate to a magic value (PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE), type=bool, default=1, group=pico_stdio_usb
|
||||
#ifndef PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE
|
||||
#define PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE 1
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE, baud rate that if selected causes a reset into BOOTSEL mode (if PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE is set), default=1200, group=pico_stdio_usb
|
||||
#ifndef PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE
|
||||
#define PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE 1200
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED, Optionally define a pin to use as bootloader activity LED when BOOTSEL mode is entered via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE), type=int, min=0, max=29, group=pico_stdio_usb
|
||||
|
||||
// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED, Whether the pin specified by PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED is fixed or can be modified by picotool over the VENDOR USB interface, type=bool, default=0, group=pico_stdio_usb
|
||||
#ifndef PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED
|
||||
#define PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED 0
|
||||
#endif
|
||||
|
||||
// Any modes disabled here can't be re-enabled by picotool via VENDOR_INTERFACE.
|
||||
// PICO_CONFIG: PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK, Optionally disable either the mass storage interface (bit 0) or the PICOBOOT interface (bit 1) when entering BOOTSEL mode via USB (either VIA_BAUD_RATE or VIA_VENDOR_INTERFACE), type=int, min=0, max=3, default=0, group=pico_stdio_usb
|
||||
#ifndef PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK
|
||||
#define PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK 0u
|
||||
#endif
|
||||
|
||||
|
||||
// *mostly* the same as the SDK code, *except* we have to explicitely pass the
|
||||
// CDC interface number to the tusb functions, making the SDK code itself very
|
||||
// non-reusable >__>
|
||||
|
||||
static mutex_t stdio_usb_mutex;
|
||||
static int CDC_N_STDIO = 0;
|
||||
|
||||
void stdio_usb_set_itf_num(int itf) {
|
||||
CDC_N_STDIO = itf;
|
||||
}
|
||||
|
||||
static void stdio_usb_out_chars(const char* buf, int length) {
|
||||
static uint64_t last_avail_time;
|
||||
uint32_t owner;
|
||||
|
||||
if (!mutex_try_enter(&stdio_usb_mutex, &owner)) {
|
||||
if (owner == get_core_num()) return; // would deadlock otherwise
|
||||
mutex_enter_blocking(&stdio_usb_mutex);
|
||||
}
|
||||
|
||||
if (tud_cdc_n_connected(CDC_N_STDIO)) {
|
||||
for (int i = 0; i < length;) {
|
||||
int n = length - i;
|
||||
int avail = tud_cdc_n_write_available(CDC_N_STDIO);
|
||||
|
||||
if (n > avail) n = avail;
|
||||
if (n) {
|
||||
int n2 = tud_cdc_n_write(CDC_N_STDIO, buf + i, n);
|
||||
tud_task();
|
||||
tud_cdc_n_write_flush(CDC_N_STDIO);
|
||||
i += n2;
|
||||
last_avail_time = time_us_64();
|
||||
} else {
|
||||
tud_task();
|
||||
tud_cdc_n_write_flush(CDC_N_STDIO);
|
||||
|
||||
if (!tud_cdc_n_connected(CDC_N_STDIO) ||
|
||||
(!tud_cdc_n_write_available(CDC_N_STDIO) &&
|
||||
time_us_64() > last_avail_time + PICO_STDIO_USB_STDOUT_TIMEOUT_US)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// reset our timeout
|
||||
last_avail_time = 0;
|
||||
}
|
||||
|
||||
mutex_exit(&stdio_usb_mutex);
|
||||
}
|
||||
|
||||
static int stdio_usb_in_chars(char* buf, int length) {
|
||||
uint32_t owner;
|
||||
|
||||
// tricky thing
|
||||
/*for (size_t i = 0; i < length; ++i)
|
||||
if (buf[i] == 'R')
|
||||
bsp_reset_bootloader();*/
|
||||
|
||||
if (!mutex_try_enter(&stdio_usb_mutex, &owner)) {
|
||||
if (owner == get_core_num()) return PICO_ERROR_NO_DATA; // would deadlock otherwise
|
||||
mutex_enter_blocking(&stdio_usb_mutex);
|
||||
}
|
||||
|
||||
int rc = PICO_ERROR_NO_DATA;
|
||||
|
||||
if (tud_cdc_n_connected(CDC_N_STDIO) && tud_cdc_n_available(CDC_N_STDIO)) {
|
||||
int count = tud_cdc_n_read(CDC_N_STDIO, buf, length);
|
||||
|
||||
rc = count ? count : PICO_ERROR_NO_DATA;
|
||||
}
|
||||
|
||||
mutex_exit(&stdio_usb_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
extern stdio_driver_t stdio_usb;
|
||||
// clang-format off
|
||||
stdio_driver_t stdio_usb = {
|
||||
.out_chars = stdio_usb_out_chars,
|
||||
.in_chars = stdio_usb_in_chars,
|
||||
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
|
||||
.crlf_enabled = PICO_STDIO_DEFAULT_CRLF
|
||||
#endif
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
bool stdio_usb_init(void) {
|
||||
//#if !PICO_NO_BI_STDIO_USB
|
||||
bi_decl_if_func_used(bi_program_feature("USB stdin / stdout"));
|
||||
//#endif
|
||||
|
||||
mutex_init(&stdio_usb_mutex);
|
||||
|
||||
// unlike with the SDK code, we don't need to add IRQ stuff for the USB
|
||||
// task, as our main function handles this automatically
|
||||
|
||||
stdio_set_driver_enabled(&stdio_usb, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void stdio_usb_line_coding_cb(cdc_line_coding_t const* line_coding) {
|
||||
#if PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE
|
||||
if (line_coding->bit_rate == PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE) {
|
||||
uint32_t gpio = 0;
|
||||
|
||||
#ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED
|
||||
gpio = 1u << PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED;
|
||||
#endif
|
||||
|
||||
reset_usb_boot(gpio, PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// vim: set et:
|
||||
|
||||
#include "ftdi.h"
|
||||
|
||||
void ftdi_if_asyncbb_init(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
|
||||
}
|
||||
void ftdi_if_asyncbb_deinit(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
|
||||
}
|
||||
void ftdi_if_asyncbb_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
|
||||
(void)itf; (void)baudrate;
|
||||
|
||||
}
|
||||
|
||||
void ftdi_if_asyncbb_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) {
|
||||
(void)itf; (void)data; (void)datasize;
|
||||
|
||||
}
|
||||
size_t ftdi_if_asyncbb_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) {
|
||||
(void)itf; (void)data; (void)maxsize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// vim: set et:
|
||||
|
||||
#include "ftdi.h"
|
||||
|
||||
void ftdi_if_cpufifo_init(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
}
|
||||
void ftdi_if_cpufifo_deinit(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
}
|
||||
void ftdi_if_cpufifo_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
|
||||
(void)itf; (void)baudrate;
|
||||
}
|
||||
|
||||
void ftdi_if_cpufifo_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) {
|
||||
(void)itf; (void)data; (void)datasize;
|
||||
}
|
||||
size_t ftdi_if_cpufifo_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) {
|
||||
(void)itf; (void)data; (void)maxsize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// vim: set et:
|
||||
|
||||
#include "ftdi.h"
|
||||
|
||||
void ftdi_if_fifo_init(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
}
|
||||
void ftdi_if_fifo_deinit(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
}
|
||||
void ftdi_if_fifo_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
|
||||
(void)itf; (void)baudrate;
|
||||
}
|
||||
|
||||
void ftdi_if_fifo_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) {
|
||||
(void)itf; (void)data; (void)datasize;
|
||||
}
|
||||
size_t ftdi_if_fifo_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) {
|
||||
(void)itf; (void)data; (void)maxsize;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
// vim: set et:
|
||||
|
||||
/* include order matters here */
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/irq.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <hardware/structs/dma.h>
|
||||
|
||||
#include "ftdi/ftdi_hw.h"
|
||||
#include "ftdi_uart_tx.pio.h"
|
||||
|
||||
struct ftdi_hw itf_bsp_data[2];
|
||||
|
||||
static int piosm_to_dreq(int pio, int sm) {
|
||||
return DREQ_PIO0_TX0 + pio * 8 + sm;
|
||||
}
|
||||
|
||||
static void ftdihw_dma_isr();
|
||||
|
||||
void ftdihw_init(struct ftdi_hw* fr, struct ftdi_interface* itf) {
|
||||
memset(fr, 0, sizeof *fr);
|
||||
|
||||
fr->itf = itf;
|
||||
fr->pio = ftdihw_itf_to_pio(itf);
|
||||
fr->pinbase = ftdihw_itf_to_base(itf);
|
||||
|
||||
fr->rx.prg_off = 0xff;
|
||||
fr->rx.piosm = 0xff;
|
||||
fr->rx.dmach = 0xff;
|
||||
fr->tx.prg_off = 0xff;
|
||||
fr->tx.piosm = 0xff;
|
||||
fr->tx.dmach = 0xff;
|
||||
|
||||
// we start with nothing to write out, but we can start filling a buffer already
|
||||
fr->rx.dmabuf_dend = sizeof(fr->dma_in_buf) - 1;
|
||||
fr->rx.dmabuf_dataend = sizeof(fr->dma_in_buf) - 1;
|
||||
|
||||
/*irq_set_enabled(DMA_IRQ_0 + (fr->itf->index & 1), false);
|
||||
irq_set_exclusive_handler(DMA_IRQ_0 + (fr->itf->index & 1), ftdihw_dma_isr);
|
||||
irq_set_enabled(DMA_IRQ_0 + (fr->itf->index & 1), true);*/
|
||||
printf("inited hw\n");
|
||||
}
|
||||
void ftdihw_deinit(struct ftdi_hw* hw) {
|
||||
/*irq_set_enabled(DMA_IRQ_0 + (hw->itf->index & 1), false);
|
||||
irq_remove_handler(DMA_IRQ_0 + (hw->itf->index & 1), ftdihw_dma_isr);*/
|
||||
}
|
||||
|
||||
bool ftdihw_dma_ch_init(struct ftdi_hw_ch* ch, struct ftdi_hw* hw, const void* prg) {
|
||||
int off, sm, dmach;
|
||||
sm = pio_claim_unused_sm(hw->pio, false);
|
||||
if (sm == -1) return false;
|
||||
|
||||
/*dmach = dma_claim_unused_channel(false);
|
||||
if (dmach == -1) {
|
||||
pio_sm_unclaim(hw->pio, sm);
|
||||
return false;
|
||||
}*/
|
||||
|
||||
if (!pio_can_add_program(hw->pio, prg)) {
|
||||
//dma_channel_unclaim(dmach);
|
||||
pio_sm_unclaim(hw->pio, sm);
|
||||
return false;
|
||||
}
|
||||
|
||||
off = pio_add_program(hw->pio, prg);
|
||||
|
||||
ch->prg = prg;
|
||||
ch->prg_off = off;
|
||||
ch->piosm = sm;
|
||||
//ch->dmach = dmach;
|
||||
|
||||
printf("inited ch\n");
|
||||
return true;
|
||||
}
|
||||
void ftdihw_dma_ch_deinit(struct ftdi_hw_ch* ch, struct ftdi_hw* hw) {
|
||||
//dma_channel_unclaim(ch->dmach);
|
||||
pio_sm_set_enabled(hw->pio, ch->piosm, false);
|
||||
pio_sm_unclaim(hw->pio, ch->piosm);
|
||||
pio_remove_program(hw->pio, ch->prg, ch->prg_off);
|
||||
|
||||
ch->dmach = 0xff;
|
||||
ch->piosm = 0xff;
|
||||
ch->prg_off = 0xff;
|
||||
ch->prg = NULL;
|
||||
}
|
||||
|
||||
void ftdihw_dma_rx_setup(struct ftdi_hw* hw, bool start) {
|
||||
return;
|
||||
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->rx.dmach, false);
|
||||
|
||||
if (hw->rx.dmabuf_dend == hw->rx.dmabuf_dstart)
|
||||
--hw->rx.dmabuf_dend; // mod 256 automatically
|
||||
|
||||
dma_channel_config dcfg = dma_channel_get_default_config(hw->rx.dmach);
|
||||
channel_config_set_read_increment(&dcfg, false);
|
||||
channel_config_set_write_increment(&dcfg, true);
|
||||
channel_config_set_dreq(&dcfg, piosm_to_dreq(hw->itf->index, hw->rx.piosm));
|
||||
channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8);
|
||||
channel_config_set_ring(&dcfg, true, 8); // 1<<8 -sized ring buffer on write end
|
||||
dma_channel_configure(hw->rx.dmach, &dcfg,
|
||||
&hw->dma_in_buf[hw->rx.dmabuf_dstart], &hw->pio->rxf[hw->rx.piosm],
|
||||
(hw->rx.dmabuf_dend - hw->rx.dmabuf_dstart) % sizeof(hw->dma_in_buf), start);
|
||||
|
||||
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->rx.dmach, true);
|
||||
}
|
||||
void ftdihw_dma_rx_stop(struct ftdi_hw* hw) {
|
||||
return;
|
||||
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->rx.dmach, false);
|
||||
dma_channel_abort(hw->rx.dmach);
|
||||
}
|
||||
|
||||
void ftdihw_dma_tx_setup(struct ftdi_hw* hw, bool start) {
|
||||
return;
|
||||
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->tx.dmach, false);
|
||||
|
||||
if (hw->tx.dmabuf_dend == hw->tx.dmabuf_dstart)
|
||||
--hw->tx.dmabuf_dend; // mod 256 automatically
|
||||
|
||||
dma_channel_config dcfg = dma_channel_get_default_config(hw->tx.dmach);
|
||||
channel_config_set_read_increment(&dcfg, true);
|
||||
channel_config_set_write_increment(&dcfg, false);
|
||||
channel_config_set_dreq(&dcfg, piosm_to_dreq(hw->itf->index, hw->tx.piosm));
|
||||
channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8);
|
||||
channel_config_set_ring(&dcfg, false, 8); // 1<<8 -sized ring buffer on read end
|
||||
dma_channel_configure(hw->tx.dmach, &dcfg,
|
||||
&hw->pio->txf[hw->tx.piosm], &hw->dma_out_buf[hw->tx.dmabuf_dstart],
|
||||
(hw->tx.dmabuf_dend - hw->tx.dmabuf_dstart) % sizeof(hw->dma_out_buf), start);
|
||||
|
||||
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->tx.dmach, true);
|
||||
}
|
||||
void ftdihw_dma_tx_stop(struct ftdi_hw* hw) {
|
||||
return;
|
||||
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->tx.dmach, false);
|
||||
dma_channel_abort(hw->tx.dmach);
|
||||
}
|
||||
|
||||
size_t ftdihw_dma_read(struct ftdi_hw* hw, uint8_t* dest, size_t maxsize) {
|
||||
// DMA is sending data from PIO to between dstart and dend, and is planned
|
||||
// to continue up to dataend. when it finishes an xfer (between dstart and
|
||||
// dend), it looks at dataend to see if it should continue sending more. if
|
||||
// not, it pauses itself as tehre's no more work to be done, and sets a
|
||||
// FIFO overrun flag in the FTDI error things
|
||||
//
|
||||
// here we read from dataend to dstart, careful not to touch the area the
|
||||
// DMA is currently writing to (while holding the channel paused), and then
|
||||
// moving the dataend marker. the channel is always reenabled, as either it
|
||||
// was busy and now it has more space to put data in, or needs to be
|
||||
// restarted as there is space now
|
||||
//
|
||||
// pausing the DMA channel is ok as it'll pause the PIO FIFO a bit, but
|
||||
// nothing drastic
|
||||
|
||||
if (maxsize == 0) return 0;
|
||||
|
||||
size_t rv = 0;
|
||||
|
||||
io_ro_8* rx = (io_ro_8*)&hw->pio->rxf[hw->rx.piosm];
|
||||
for (; !pio_sm_is_rx_fifo_empty(hw->pio, hw->rx.piosm) && rv < maxsize; ++rv) {
|
||||
dest[rv] = *rx;
|
||||
}
|
||||
return rv;
|
||||
|
||||
// TODO: make time between pause & resume shorter by moving stuff around?
|
||||
bool wasbusy = dma_hw->ch[hw->rx.dmach].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS;
|
||||
hw_clear_bits(&dma_hw->ch[hw->rx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // pause (also prevents IRQ)
|
||||
|
||||
// dstart can get modified by the IRQ; IRQ reads dataend
|
||||
uint8_t dstart = hw->rx.dmabuf_dstart, dataend = hw->rx.dmabuf_dataend;
|
||||
if (dataend > dstart) dstart += sizeof(hw->dma_in_buf);
|
||||
// nothing ready yet - bail out
|
||||
if (dstart == (dataend + 1) % sizeof(hw->dma_in_buf)) {
|
||||
goto END;
|
||||
}
|
||||
|
||||
__compiler_memory_barrier();
|
||||
|
||||
// copy from data in ringbuffer that was read in by PIO
|
||||
rv = (size_t)dstart - (size_t)dataend - 1;
|
||||
if (rv > maxsize) rv = maxsize;
|
||||
for (size_t i = 0; i < rv; ++i) {
|
||||
dest[i] = hw->dma_in_buf[(dataend + i) % sizeof(hw->dma_in_buf)];
|
||||
}
|
||||
uint8_t dataend_new = (dataend + rv) % sizeof(hw->dma_in_buf);
|
||||
|
||||
hw->rx.dmabuf_dataend = dataend_new;
|
||||
|
||||
hw->itf->modemstat &= ~sio_modem_fifoerr;
|
||||
|
||||
END:
|
||||
if (!wasbusy) {
|
||||
hw->rx.dmabuf_dstart = hw->rx.dmabuf_dend; // not required to set in DMA hw, but need to keep track of it in code
|
||||
hw->rx.dmabuf_dend = hw->rx.dmabuf_dataend;
|
||||
ftdihw_dma_rx_setup(hw, true);
|
||||
} else // if already busy easlier, simply reenable
|
||||
hw_set_bits(&dma_hw->ch[hw->rx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // resume (also reenables IRQ)
|
||||
return rv;
|
||||
}
|
||||
bool ftdihw_dma_write(struct ftdi_hw* hw, const uint8_t* src, size_t size) {
|
||||
// DMA is sending data between dstart and dend to PIO, and is planned to
|
||||
// continue up to dataend. when it finishes an xfer (between dstart and
|
||||
// dend), it looks at dataned to see if it should continue sending more. if
|
||||
// not, it pauses itself as there's no more work to be done
|
||||
//
|
||||
// here we insert some data in the ring buffer, careful to not overwrite
|
||||
// what the DMA is currently transferring (while holding the channel
|
||||
// paused), and then moving the dataend marker. the channel is always
|
||||
// reenabled, as either it was busy and now has more work to do, or needs
|
||||
// to be restarted as there's data now
|
||||
//
|
||||
// pausing the DMA channel is ok as it'll pause the PIO FIFO a bit, but
|
||||
// nothing drastic
|
||||
|
||||
if (size >= 255) return false; // don't even bother
|
||||
|
||||
//io_wo_32/*8*/* tx = (io_wo_32/*8*/*)hw->pio->txf[hw->tx.piosm];
|
||||
for (size_t i = 0; !pio_sm_is_tx_fifo_full(hw->pio, hw->tx.piosm) && i < size; ++i) {
|
||||
printf("send %02x=%c\n", src[i], src[i]);
|
||||
ftdi_uart_tx_program_putc(hw->pio, hw->tx.piosm, src[i]);
|
||||
//*tx = (uint32_t)src[i];
|
||||
}
|
||||
return true;
|
||||
|
||||
bool rv = true;
|
||||
|
||||
// TODO: make time between pause & resume shorter by moving stuff around?
|
||||
bool wasbusy = dma_hw->ch[hw->tx.dmach].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS;
|
||||
hw_clear_bits(&dma_hw->ch[hw->tx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // pause (also prevents IRQ)
|
||||
|
||||
// dstart can get modified by the IRQ; IRQ reads dataend
|
||||
uint8_t dstart = hw->tx.dmabuf_dstart, dataend = hw->tx.dmabuf_dataend;
|
||||
if (dataend > dstart) dstart += sizeof(hw->dma_out_buf);
|
||||
// no space to put it in - overrun error
|
||||
if ((size_t)dstart - (size_t)dataend < size + 1) {
|
||||
hw->itf->modemstat |= sio_modem_fifoerr;
|
||||
rv = false;
|
||||
goto END;
|
||||
}
|
||||
|
||||
__compiler_memory_barrier();
|
||||
|
||||
// copy data to buffer, to be copied next
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
hw->dma_out_buf[(dataend + i) % sizeof(hw->dma_out_buf)] = src[i];
|
||||
}
|
||||
uint8_t dataend_new = (dataend + size) % sizeof(hw->dma_out_buf);
|
||||
|
||||
hw->tx.dmabuf_dataend = dataend_new;
|
||||
|
||||
hw->itf->modemstat &= ~sio_modem_temt;
|
||||
|
||||
END:
|
||||
if (!wasbusy) {
|
||||
hw->tx.dmabuf_dstart = hw->tx.dmabuf_dend; // not required to set in DMA hw, but need to keep track of it in code
|
||||
hw->tx.dmabuf_dend = hw->tx.dmabuf_dataend;
|
||||
ftdihw_dma_tx_setup(hw, true);
|
||||
} else // if already busy easlier, simply reenable
|
||||
hw_set_bits(&dma_hw->ch[hw->tx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // resume (also reenables IRQ)
|
||||
return rv;
|
||||
}
|
||||
|
||||
void ftdihw_dma_rx_flush(struct ftdi_hw* hw) {
|
||||
ftdihw_dma_rx_stop(hw);
|
||||
hw->rx.dmabuf_dstart = 0;
|
||||
hw->rx.dmabuf_dend = 0;
|
||||
hw->rx.dmabuf_dataend = 0;
|
||||
}
|
||||
void ftdihw_dma_tx_flush(struct ftdi_hw* hw) {
|
||||
ftdihw_dma_tx_stop(hw);
|
||||
hw->tx.dmabuf_dstart = 0;
|
||||
hw->tx.dmabuf_dend = 0;
|
||||
hw->tx.dmabuf_dataend = 0;
|
||||
}
|
||||
|
||||
static void ftdihw_dma_rx_irq(struct ftdi_hw* hw) {
|
||||
// an rx DMA transfer has finished. if in the meantime (between DMA xfer
|
||||
// start and now) more room has become available, restart the DMA channel
|
||||
// to write to the new space instead. otherwise, if there's no more space
|
||||
// left, we're in a data overrun condition, so don't restart, and set the
|
||||
// relevant FTDI error flags
|
||||
|
||||
uint8_t dend = hw->rx.dmabuf_dend, dataend = hw->rx.dmabuf_dataend;
|
||||
|
||||
if (dend == dataend) {
|
||||
// data overrun, stop stuff (until read() restarts the DMA)
|
||||
hw->itf->modemstat |= sio_modem_fifoerr;
|
||||
} else {
|
||||
hw->rx.dmabuf_dstart = dend;
|
||||
hw->rx.dmabuf_dend = dataend;
|
||||
ftdihw_dma_rx_setup(hw, true);
|
||||
}
|
||||
}
|
||||
static void ftdihw_dma_tx_irq(struct ftdi_hw* hw) {
|
||||
// a tx DMA transfer has finished. if in the meantile (between DMA xfer
|
||||
// start and now) more data has become available, restart the DMA channel
|
||||
// to read from the new data instead. otherwise, if there's no more data to
|
||||
// be read, we have nothing to do and we can leave the DMA channel in its
|
||||
// idle state.
|
||||
|
||||
uint8_t dend = hw->tx.dmabuf_dend, dataend = hw->tx.dmabuf_dataend;
|
||||
|
||||
if (dend == dataend) {
|
||||
// nothing to do
|
||||
hw->itf->modemstat |= sio_modem_temt;
|
||||
} else {
|
||||
hw->tx.dmabuf_dstart = dend;
|
||||
hw->tx.dmabuf_dend = dataend;
|
||||
ftdihw_dma_tx_setup(hw, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void ftdihw_dma_isr() {
|
||||
// interrupt service routine: dispatch to functions above depending on INTS0
|
||||
uint32_t flags = dma_hw->ints0;
|
||||
uint32_t allflg = 1u << itf_bsp_data[0].rx.dmach;
|
||||
allflg |= 1u << itf_bsp_data[0].tx.dmach;
|
||||
allflg |= 1u << itf_bsp_data[1].rx.dmach;
|
||||
allflg |= 1u << itf_bsp_data[1].tx.dmach;
|
||||
|
||||
for (size_t i = 0; (i < 2) && (flags & allflg); ++i) {
|
||||
uint32_t flg = 1u << itf_bsp_data[i].rx.dmach;
|
||||
if (flags & flg) {
|
||||
flags &= ~flg;
|
||||
dma_hw->ints0 = flg; // ack int
|
||||
ftdihw_dma_rx_irq(&itf_bsp_data[i]);
|
||||
}
|
||||
|
||||
flg = 1u << itf_bsp_data[i].tx.dmach;
|
||||
if (flags & flg) {
|
||||
flags &= ~flg;
|
||||
dma_hw->ints0 = flg; // ack int
|
||||
ftdihw_dma_tx_irq(&itf_bsp_data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
// vim: set et:
|
||||
|
||||
#ifndef FTDI_BASE_H_
|
||||
#define FTDI_BASE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "pinout.h"
|
||||
#include "ftdi.h"
|
||||
|
||||
struct ftdi_hw_ch {
|
||||
const void* prg;
|
||||
|
||||
uint8_t prg_off, piosm, dmach;
|
||||
|
||||
volatile uint8_t dmabuf_dstart, dmabuf_dend, dmabuf_dataend;
|
||||
};
|
||||
|
||||
struct ftdi_hw {
|
||||
struct ftdi_interface* itf;
|
||||
PIO pio;
|
||||
|
||||
struct ftdi_hw_ch rx, tx;
|
||||
uint8_t pinbase;
|
||||
|
||||
volatile uint8_t dma_in_buf[256/*CFG_TUD_VENDOR_TX_BUFSIZE*/];
|
||||
volatile uint8_t dma_out_buf[256/*CFG_TUD_VENDOR_RX_BUFSIZE*/];
|
||||
};
|
||||
|
||||
extern struct ftdi_hw itf_bsp_data[2];
|
||||
|
||||
void ftdihw_init(struct ftdi_hw* fr, struct ftdi_interface* itf);
|
||||
void ftdihw_deinit(struct ftdi_hw* fr);
|
||||
|
||||
bool ftdihw_dma_ch_init(struct ftdi_hw_ch* ch, struct ftdi_hw* fr, const void* prg);
|
||||
void ftdihw_dma_ch_deinit(struct ftdi_hw_ch* fr, struct ftdi_hw* hw);
|
||||
|
||||
void ftdihw_dma_rx_setup(struct ftdi_hw* fr, bool start);
|
||||
void ftdihw_dma_rx_stop(struct ftdi_hw* fr);
|
||||
|
||||
void ftdihw_dma_tx_setup(struct ftdi_hw* fr, bool start);
|
||||
void ftdihw_dma_tx_stop(struct ftdi_hw* fr);
|
||||
|
||||
size_t ftdihw_dma_read(struct ftdi_hw* fr, uint8_t* dest, size_t maxsize);
|
||||
bool ftdihw_dma_write(struct ftdi_hw* fr, const uint8_t* src, size_t size);
|
||||
|
||||
void ftdihw_dma_rx_flush(struct ftdi_hw* fr);
|
||||
void ftdihw_dma_tx_flush(struct ftdi_hw* fr);
|
||||
|
||||
static inline int ftdihw_idx_to_base(int itf_idx) {
|
||||
return itf_idx ? PINOUT_ITF_B_BASE : PINOUT_ITF_A_BASE;
|
||||
}
|
||||
static inline int ftdihw_itf_to_base(struct ftdi_interface* itf) {
|
||||
return ftdihw_idx_to_base(itf->index);
|
||||
}
|
||||
|
||||
static inline PIO ftdihw_idx_to_pio(int itf_idx) {
|
||||
return itf_idx ? PINOUT_ITF_A_PIO : PINOUT_ITF_B_PIO;
|
||||
}
|
||||
static inline PIO ftdihw_itf_to_pio(struct ftdi_interface* itf) {
|
||||
return ftdihw_idx_to_pio(itf->index);
|
||||
}
|
||||
|
||||
static inline struct ftdi_hw* ftdihw_idx_to_hw(int itf_idx) {
|
||||
return &itf_bsp_data[itf_idx & 1];
|
||||
}
|
||||
static inline struct ftdi_hw* ftdihw_itf_to_hw(struct ftdi_interface* itf) {
|
||||
return ftdihw_idx_to_hw(itf->index);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
// vim: set et:
|
||||
|
||||
#include <hardware/pio.h>
|
||||
|
||||
#include "ftdi.h"
|
||||
#include "ftdi/ftdi_hw.h"
|
||||
|
||||
static void init_mode(struct ftdi_interface* itf, enum ftdi_mode mode) {
|
||||
switch (mode) {
|
||||
case ftmode_uart: ftdi_if_uart_init(itf); ftdi_if_uart_set_baudrate(itf, itf->baudrate); break;
|
||||
case ftmode_mpsse: ftdi_if_mpsse_init(itf); ftdi_if_mpsse_set_baudrate(itf, itf->baudrate); break;
|
||||
case ftmode_asyncbb: ftdi_if_asyncbb_init(itf); ftdi_if_asyncbb_set_baudrate(itf, itf->baudrate); break;
|
||||
case ftmode_syncbb: ftdi_if_syncbb_init(itf); ftdi_if_syncbb_set_baudrate(itf, itf->baudrate); break;
|
||||
case ftmode_mcuhost: ftdi_if_mcuhost_init(itf); ftdi_if_mcuhost_set_baudrate(itf, itf->baudrate); break;
|
||||
case ftmode_fifo: ftdi_if_fifo_init(itf); ftdi_if_fifo_set_baudrate(itf, itf->baudrate); break;
|
||||
case ftmode_cpufifo: ftdi_if_cpufifo_init(itf); ftdi_if_cpufifo_set_baudrate(itf, itf->baudrate); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
static void deinit_mode(struct ftdi_interface* itf, enum ftdi_mode mode) {
|
||||
switch (mode) {
|
||||
case ftmode_uart: ftdi_if_uart_deinit(itf); break;
|
||||
case ftmode_mpsse: ftdi_if_mpsse_deinit(itf); break;
|
||||
case ftmode_asyncbb: ftdi_if_asyncbb_deinit(itf); break;
|
||||
case ftmode_syncbb: ftdi_if_syncbb_deinit(itf); break;
|
||||
case ftmode_mcuhost: ftdi_if_mcuhost_deinit(itf); break;
|
||||
case ftmode_fifo: ftdi_if_fifo_deinit(itf); break;
|
||||
case ftmode_cpufifo: ftdi_if_cpufifo_deinit(itf); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void ftdi_if_init(struct ftdi_interface* itf) {
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
ftdihw_init(hw, itf);
|
||||
|
||||
init_mode(itf, ftdi_if_get_mode(itf));
|
||||
}
|
||||
void ftdi_if_deinit(struct ftdi_interface* itf) {
|
||||
deinit_mode(itf, ftdi_if_get_mode(itf));
|
||||
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
ftdihw_deinit(hw);
|
||||
}
|
||||
|
||||
void ftdi_if_set_modemctrl(struct ftdi_interface* itf, uint8_t mask, uint8_t data) {
|
||||
(void)itf; (void)mask; (void)data;
|
||||
// TODO: what's this?
|
||||
}
|
||||
void ftdi_if_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
|
||||
switch (ftdi_if_get_mode(itf)) {
|
||||
case ftmode_uart: ftdi_if_uart_set_baudrate(itf, baudrate); break;
|
||||
case ftmode_mpsse: ftdi_if_mpsse_set_baudrate(itf, baudrate); break;
|
||||
case ftmode_asyncbb: ftdi_if_asyncbb_set_baudrate(itf, baudrate); break;
|
||||
case ftmode_syncbb: ftdi_if_syncbb_set_baudrate(itf, baudrate); break;
|
||||
case ftmode_mcuhost: ftdi_if_mcuhost_set_baudrate(itf, baudrate); break;
|
||||
case ftmode_fifo: ftdi_if_fifo_set_baudrate(itf, baudrate); break;
|
||||
case ftmode_cpufifo: ftdi_if_cpufifo_set_baudrate(itf, baudrate); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
enum ftdi_sio_modemstat ftdi_if_poll_modemstat(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
|
||||
return sio_modem_cts | sio_modem_dts; // TODO: use this to read part of UART flow ctrl?
|
||||
}
|
||||
void ftdi_if_set_eventchar(struct ftdi_interface* itf, bool enable, uint8_t evchar) {
|
||||
(void)itf; (void)enable; (void)evchar;
|
||||
// TODO: when is this used? bitmode0-only? also ftd2xx headers make this look like its not just an "event on char" thing
|
||||
}
|
||||
void ftdi_if_set_errorchar(struct ftdi_interface* itf, bool enable, uint8_t erchar) {
|
||||
(void)itf; (void)enable; (void)erchar;
|
||||
// TODO: when is this used? bitmode0-only? also ftd2xx headers make this look like its not just an "error on char" thing
|
||||
}
|
||||
uint8_t ftdi_if_read_pins(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
|
||||
return 0; // TODO: which pins does this return?
|
||||
}
|
||||
|
||||
void ftdi_if_set_bitbang(struct ftdi_interface* itf, uint8_t dirmask,
|
||||
enum ftdi_sio_bitmode bitmode, uint8_t olddir, enum ftdi_sio_bitmode oldmode) {
|
||||
if (bitmode == oldmode && dirmask == olddir) return; // nothing to do
|
||||
|
||||
deinit_mode(itf, ftdi_get_mode_of(oldmode, itf->index ? FTDI_EEP_IFB_MODE : FTDI_EEP_IFA_MODE));
|
||||
init_mode(itf, ftdi_if_get_mode(itf));
|
||||
}
|
||||
|
||||
void ftdi_if_sio_reset(struct ftdi_interface* itf) { (void)itf; /* TODO: ? */ }
|
||||
void ftdi_if_sio_tciflush(struct ftdi_interface* itf) {
|
||||
(void)itf; /* TODO: ? */
|
||||
}
|
||||
void ftdi_if_sio_tcoflush(struct ftdi_interface* itf) {
|
||||
(void)itf; /* TODO: ? */
|
||||
}
|
||||
void ftdi_if_set_latency(struct ftdi_interface* itf, uint8_t latency) { (void)itf; (void)latency; /* TODO: ? */ }
|
||||
uint8_t ftdi_if_get_latency(struct ftdi_interface* itf) { return itf->latency; /* TODO: ? */ }
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
;
|
||||
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
|
||||
.program ftdi_uart_rx_mini
|
||||
|
||||
; Minimum viable 8n1 UART receiver. Wait for the start bit, then sample 8 bits
|
||||
; with the correct timing.
|
||||
; IN pin 0 is mapped to the GPIO used as UART RX.
|
||||
; Autopush must be enabled, with a threshold of 8.
|
||||
|
||||
wait 0 pin 0 ; Wait for start bit
|
||||
set x, 7 [10] ; Preload bit counter, delay until eye of first data bit
|
||||
bitloop: ; Loop 8 times
|
||||
in pins, 1 ; Sample data
|
||||
jmp x-- bitloop [6] ; Each iteration is 8 cycles
|
||||
|
||||
% c-sdk {
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
static inline void ftdi_uart_rx_mini_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) {
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
|
||||
pio_gpio_init(pio, pin);
|
||||
gpio_pull_up(pin);
|
||||
|
||||
pio_sm_config c = ftdi_uart_rx_mini_program_get_default_config(offset);
|
||||
sm_config_set_in_pins(&c, pin); // for WAIT, IN
|
||||
// Shift to right, autopush enabled
|
||||
sm_config_set_in_shift(&c, true, true, 8);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
|
||||
// SM transmits 1 bit per 8 execution cycles.
|
||||
float div = (float)clock_get_hz(clk_sys) / (8 * baud);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
%}
|
||||
|
||||
.program ftdi_uart_rx
|
||||
|
||||
; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
|
||||
; break conditions more gracefully.
|
||||
; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
|
||||
|
||||
start:
|
||||
wait 0 pin 0 ; Stall until start bit is asserted
|
||||
set x, 7 [10] ; Preload bit counter, then delay until halfway through
|
||||
bitloop: ; the first data bit (12 cycles incl wait, set).
|
||||
in pins, 1 ; Shift data bit into ISR
|
||||
jmp x-- bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles
|
||||
jmp pin good_stop ; Check stop bit (should be high)
|
||||
|
||||
irq 4 rel ; Either a framing error or a break. Set a sticky flag,
|
||||
wait 1 pin 0 ; and wait for line to return to idle state.
|
||||
jmp start ; Don't push data if we didn't see good framing.
|
||||
|
||||
good_stop: ; No delay before returning to start; a little slack is
|
||||
push ; important in case the TX clock is slightly too fast.
|
||||
|
||||
|
||||
% c-sdk {
|
||||
static inline void ftdi_uart_rx_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) {
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
|
||||
pio_gpio_init(pio, pin);
|
||||
gpio_pull_up(pin);
|
||||
|
||||
pio_sm_config c = ftdi_uart_rx_program_get_default_config(offset);
|
||||
sm_config_set_in_pins(&c, pin); // for WAIT, IN
|
||||
sm_config_set_jmp_pin(&c, pin); // for JMP
|
||||
// Shift to right, autopull disabled
|
||||
sm_config_set_in_shift(&c, true, false, 32);
|
||||
// Deeper FIFO as we're not doing any TX
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
|
||||
// SM transmits 1 bit per 8 execution cycles.
|
||||
float div = (float)clock_get_hz(clk_sys) / (8 * baud);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
|
||||
static inline char ftdi_uart_rx_program_getc(PIO pio, uint sm) {
|
||||
// 8-bit read from the uppermost byte of the FIFO, as data is left-justified
|
||||
io_rw_8 *rxfifo_shift = (io_rw_8*)&pio->rxf[sm] + 3;
|
||||
while (pio_sm_is_rx_fifo_empty(pio, sm))
|
||||
tight_loop_contents();
|
||||
return (char)*rxfifo_shift;
|
||||
}
|
||||
|
||||
%}
|
|
@ -0,0 +1,61 @@
|
|||
;
|
||||
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
|
||||
.program ftdi_uart_tx
|
||||
.side_set 1 opt
|
||||
|
||||
; An 8n1 UART transmit program.
|
||||
; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
|
||||
|
||||
pull side 1 [7] ; Assert stop bit, or stall with line in idle state
|
||||
set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks
|
||||
bitloop: ; This loop will run 8 times (8n1 UART)
|
||||
out pins, 1 ; Shift 1 bit from OSR to the first OUT pin
|
||||
jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
|
||||
|
||||
|
||||
% c-sdk {
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
static inline void ftdi_uart_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx, uint baud) {
|
||||
// Tell PIO to initially drive output-high on the selected pin, then map PIO
|
||||
// onto that pin with the IO muxes.
|
||||
pio_sm_set_pins_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
|
||||
pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
|
||||
pio_gpio_init(pio, pin_tx);
|
||||
|
||||
pio_sm_config c = ftdi_uart_tx_program_get_default_config(offset);
|
||||
|
||||
// OUT shifts to right, no autopull
|
||||
sm_config_set_out_shift(&c, true, false, 32);
|
||||
|
||||
// We are mapping both OUT and side-set to the same pin, because sometimes
|
||||
// we need to assert user data onto the pin (with OUT) and sometimes
|
||||
// assert constant values (start/stop bit)
|
||||
sm_config_set_out_pins(&c, pin_tx, 1);
|
||||
sm_config_set_sideset_pins(&c, pin_tx);
|
||||
|
||||
// We only need TX, so get an 8-deep FIFO!
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
|
||||
// SM transmits 1 bit per 8 execution cycles.
|
||||
float div = (float)clock_get_hz(clk_sys) / (8 * baud);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
|
||||
static inline void ftdi_uart_tx_program_putc(PIO pio, uint sm, char c) {
|
||||
pio_sm_put_blocking(pio, sm, (uint32_t)c);
|
||||
}
|
||||
|
||||
static inline void ftdi_uart_tx_program_puts(PIO pio, uint sm, const char *s) {
|
||||
while (*s)
|
||||
ftdi_uart_tx_program_putc(pio, sm, *s++);
|
||||
}
|
||||
|
||||
%}
|
|
@ -0,0 +1,38 @@
|
|||
// vim: set et:
|
||||
|
||||
#include "ftdi.h"
|
||||
|
||||
void ftdi_if_mcuhost_init(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
}
|
||||
void ftdi_if_mcuhost_deinit(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
}
|
||||
void ftdi_if_mcuhost_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
|
||||
(void)itf; (void)baudrate;
|
||||
}
|
||||
|
||||
void ftdi_if_mcuhost_flush(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
}
|
||||
void ftdi_if_mcuhost_wait_io(struct ftdi_interface* itf, bool level) {
|
||||
(void)itf; (void)level;
|
||||
}
|
||||
|
||||
uint8_t ftdi_if_mcuhost_read8(struct ftdi_interface* itf, uint8_t addr) {
|
||||
(void)itf; (void)addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
uint8_t ftdi_if_mcuhost_read16(struct ftdi_interface* itf, uint16_t addr) {
|
||||
(void)itf; (void)addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
void ftdi_if_mcuhost_write8(struct ftdi_interface* itf, uint8_t addr, uint8_t value) {
|
||||
(void)itf; (void)addr; (void)value;
|
||||
}
|
||||
void ftdi_if_mcuhost_write16(struct ftdi_interface* itf, uint16_t addr, uint8_t value) {
|
||||
(void)itf; (void)addr; (void)value;
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
// vim: set et:
|
||||
|
||||
#include "ftdi.h"
|
||||
|
||||
void ftdi_if_mpsse_init(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
}
|
||||
void ftdi_if_mpsse_deinit(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
}
|
||||
void ftdi_if_mpsse_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
|
||||
(void)itf; (void)baudrate;
|
||||
}
|
||||
|
||||
|
||||
void ftdi_if_mpsse_flush(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
}
|
||||
void ftdi_if_mpsse_wait_io(struct ftdi_interface* itf, bool level) {
|
||||
(void)itf; (void)level;
|
||||
}
|
||||
|
||||
void ftdi_if_mpsse_set_dirval_lo(struct ftdi_interface* itf, uint8_t dir, uint8_t val) {
|
||||
(void)itf; (void)dir; (void)val;
|
||||
}
|
||||
void ftdi_if_mpsse_set_dirval_hi(struct ftdi_interface* itf, uint8_t dir, uint8_t val) {
|
||||
(void)itf; (void)dir; (void)val;
|
||||
}
|
||||
uint8_t ftdi_if_mpsse_read_lo(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
uint8_t ftdi_if_mpsse_read_hi(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
void ftdi_if_mpsse_loopback(struct ftdi_interface* itf, bool enable) {
|
||||
(void)itf; (void)enable;
|
||||
}
|
||||
void ftdi_if_mpsse_set_clkdiv(struct ftdi_interface* itf, uint16_t div) {
|
||||
(void)itf; (void)div;
|
||||
}
|
||||
|
||||
uint8_t ftdi_if_mpsse_xfer_bits(struct ftdi_interface* itf, int flags, size_t nbits, uint8_t value) {
|
||||
(void)itf; (void)flags; (void)nbits; (void)value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
void ftdi_if_mpsse_xfer_bytes(struct ftdi_interface* itf, int flags, size_t nbytes, uint8_t* dst, const uint8_t* src) {
|
||||
(void)itf; (void)flags; (void)nbytes; (void)dst; (void)src;
|
||||
}
|
||||
uint8_t ftdi_if_mpsse_tms_xfer(struct ftdi_interface* itf, int flags, size_t nbits, uint8_t value) {
|
||||
(void)itf; (void)flags; (void)nbits; (void)value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ftdi_if_mpsse_div5(struct ftdi_interface* itf, bool enable) {
|
||||
(void)itf; (void)enable;
|
||||
}
|
||||
void ftdi_if_mpsse_data_3ph(struct ftdi_interface* itf, bool enable) {
|
||||
(void)itf; (void)enable;
|
||||
}
|
||||
void ftdi_if_mpsse_adaptive(struct ftdi_interface* itf, bool enable) {
|
||||
(void)itf; (void)enable;
|
||||
}
|
||||
|
||||
void ftdi_if_mpsse_clockonly(struct ftdi_interface* itf, uint32_t cycles) {
|
||||
(void)itf; (void)cycles;
|
||||
}
|
||||
void ftdi_if_mpsse_clock_wait_io(struct ftdi_interface* itf, bool level) {
|
||||
(void)itf; (void)level;
|
||||
}
|
||||
void ftdi_if_mpsse_clockonly_wait_io(struct ftdi_interface* itf, bool level, uint32_t cycles) {
|
||||
(void)itf; (void)level; (void)cycles;
|
||||
}
|
||||
void ftdi_if_mpsse_hi_is_tristate(struct ftdi_interface* itf, uint16_t pinmask) {
|
||||
(void)itf; (void)pinmask;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// vim: set et:
|
||||
|
||||
#include "ftdi.h"
|
||||
|
||||
void ftdi_if_syncbb_init(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
}
|
||||
void ftdi_if_syncbb_deinit(struct ftdi_interface* itf) {
|
||||
(void)itf;
|
||||
}
|
||||
void ftdi_if_syncbb_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
|
||||
(void)itf; (void)baudrate;
|
||||
}
|
||||
|
||||
|
||||
void ftdi_if_syncbb_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) {
|
||||
|
||||
}
|
||||
size_t ftdi_if_syncbb_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
// vim: set et:
|
||||
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <hardware/structs/dma.h>
|
||||
|
||||
#include "pinout.h"
|
||||
#include "ftdi/ftdi_hw.h"
|
||||
|
||||
#include "ftdi_uart_rx.pio.h"
|
||||
#include "ftdi_uart_tx.pio.h"
|
||||
|
||||
struct uart_state {
|
||||
uint32_t baudrate;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static struct uart_state state[2] = {
|
||||
(struct uart_state){ .baudrate = 115200, .enabled = false },
|
||||
(struct uart_state){ .baudrate = 115200, .enabled = false },
|
||||
};
|
||||
|
||||
#define STATEOF(itf) (state[(itf)->index & 1])
|
||||
|
||||
// set up PIO->dma_in_buf DMA:
|
||||
// * src=pio dst=buf 8bit 256words pacing=pio
|
||||
// * IRQ: set overrun bit
|
||||
// * start it
|
||||
// * SETUP AT MODE ENTER, STOP AT MODE EXIT
|
||||
//
|
||||
// set up dma_out_buf->PIO DMA:
|
||||
// * src=buf dst=pio 8bit <num>words pacing=pio
|
||||
// * ~~no IRQ I think?~~ IRQ: next ringbuffer part (see below)
|
||||
// * DO NOT start it on mode enter!
|
||||
// * STOP AT MODE EXIT
|
||||
//
|
||||
// read routine:
|
||||
// * abort DMA
|
||||
// * copy data from dma_in_buf (read xfer len? == remaining of 256 bytes)
|
||||
// * resetup & start DMA:
|
||||
//
|
||||
// write routine:
|
||||
// * if DMA running: set overrun bit, bail out?
|
||||
// * should use ringbuffer-like structure
|
||||
// * pointers: dma start, dma end, data end (after dma, contiguous)
|
||||
// * dma end can be calculated from DMA MMIO, but, race conditions so no
|
||||
// * use DMA IRQ for next block (and wraparound: dma cannot do wraparound manually)
|
||||
// * do not start next block if data end == dma start
|
||||
// * can we set DMA xfer len while in-flight? datasheet p92 2.5.1: nope. sad
|
||||
// * only bail out when data end == dma start - 1
|
||||
// * copy data to dma_out_buf
|
||||
// * set up & start DMA
|
||||
//
|
||||
// * what with buffers larger than 256 bytes?
|
||||
// * just drop for now ig
|
||||
// * what is the actual FTDI buffer size??
|
||||
// * device-dependent so aaaa
|
||||
// * which bits get set on errors?
|
||||
// * do TCIFLUSH/TCOFLUSH influence these buffers, or only the USB proto
|
||||
// handling buffers?
|
||||
|
||||
void ftdi_if_uart_init(struct ftdi_interface* itf) {
|
||||
if (STATEOF(itf).enabled) return; // shrug
|
||||
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
if (!ftdihw_dma_ch_init(&hw->rx, hw, &ftdi_uart_rx_program)) return;
|
||||
if (!ftdihw_dma_ch_init(&hw->tx, hw, &ftdi_uart_tx_program)) {
|
||||
ftdihw_dma_ch_deinit(&hw->rx, hw);
|
||||
return;
|
||||
}
|
||||
|
||||
int pin_rx = hw->pinbase + PINOUT_UART_RXD_OFF,
|
||||
pin_tx = hw->pinbase + PINOUT_UART_TXD_OFF;
|
||||
|
||||
ftdi_uart_rx_program_init(hw->pio, hw->rx.piosm, hw->rx.prg_off,
|
||||
pin_rx, STATEOF(itf).baudrate);
|
||||
ftdi_uart_tx_program_init(hw->pio, hw->tx.piosm, hw->tx.prg_off,
|
||||
pin_tx, STATEOF(itf).baudrate);
|
||||
printf("inited baudrate=%lu\n", STATEOF(itf).baudrate);
|
||||
|
||||
/*gpio_set_function(pin_rx, GPIO_FUNC_PIO0 + itf->index);
|
||||
gpio_set_function(pin_tx, GPIO_FUNC_PIO0 + itf->index);*/
|
||||
|
||||
ftdihw_dma_rx_setup(hw, true);
|
||||
ftdihw_dma_tx_setup(hw, false);
|
||||
|
||||
STATEOF(itf).enabled = true;
|
||||
}
|
||||
void ftdi_if_uart_deinit(struct ftdi_interface* itf) {
|
||||
if (!STATEOF(itf).enabled) return; // shrug
|
||||
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
int pin_rx = hw->pinbase + PINOUT_UART_RXD_OFF,
|
||||
pin_tx = hw->pinbase + PINOUT_UART_TXD_OFF;
|
||||
|
||||
ftdihw_dma_rx_flush(hw);
|
||||
ftdihw_dma_tx_flush(hw);
|
||||
|
||||
ftdihw_dma_ch_deinit(&hw->rx, hw);
|
||||
ftdihw_dma_ch_deinit(&hw->tx, hw);
|
||||
|
||||
gpio_set_function(pin_rx, GPIO_FUNC_NULL);
|
||||
gpio_set_function(pin_tx, GPIO_FUNC_NULL);
|
||||
gpio_set_pulls(pin_rx, false, false);
|
||||
gpio_set_pulls(pin_tx, false, false);
|
||||
|
||||
STATEOF(itf).enabled = false;
|
||||
}
|
||||
void ftdi_if_uart_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
|
||||
if (baudrate == 0) return;
|
||||
if (!STATEOF(itf).enabled) return;
|
||||
return; // always ignore - decoding is broken atm
|
||||
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
int pin_rx = ftdihw_itf_to_base(itf) + PINOUT_UART_RXD_OFF,
|
||||
pin_tx = ftdihw_itf_to_base(itf) + PINOUT_UART_TXD_OFF;
|
||||
|
||||
// TODO: directly set clock divider
|
||||
ftdi_uart_rx_program_init(hw->pio, hw->rx.piosm, hw->rx.prg_off, pin_rx, baudrate);
|
||||
ftdi_uart_tx_program_init(hw->pio, hw->tx.piosm, hw->tx.prg_off, pin_tx, baudrate);
|
||||
|
||||
STATEOF(itf).baudrate = baudrate;
|
||||
printf("set baudrate=%lu\n", baudrate);
|
||||
}
|
||||
|
||||
|
||||
void ftdi_if_set_flowctrl(struct ftdi_interface* itf, enum ftdi_flowctrl flow) {
|
||||
(void)itf; (void)flow; // TODO: bluh
|
||||
}
|
||||
void ftdi_if_set_lineprop(struct ftdi_interface* itf, enum ftdi_sio_lineprop lineprop) {
|
||||
(void)itf; (void)lineprop; // TODO: break, stop, parity, #bits
|
||||
}
|
||||
|
||||
void ftdi_if_uart_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) {
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
ftdihw_dma_write(hw, data, datasize);
|
||||
}
|
||||
size_t ftdi_if_uart_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) {
|
||||
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
|
||||
|
||||
return ftdihw_dma_read(hw, data, maxsize);
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
|
||||
#ifndef BSP_PINOUT_M_FTDI_H_
|
||||
#define BSP_PINOUT_M_FTDI_H_
|
||||
|
||||
/* NOTE: no C code here! needs to be include-able from PIO asm! */
|
||||
|
||||
#define PINOUT_ITF_A_BASE 2
|
||||
#define PINOUT_ITF_B_BASE 14
|
||||
|
||||
// TODO: ? or just different SMs on the same PIO? would complicate things tho
|
||||
#define PINOUT_ITF_A_PIO pio0
|
||||
#define PINOUT_ITF_B_PIO pio1
|
||||
|
||||
#define PINOUT_DBUS0_OFF 0
|
||||
#define PINOUT_DBUS1_OFF 1
|
||||
#define PINOUT_DBUS2_OFF 2
|
||||
#define PINOUT_DBUS3_OFF 3
|
||||
#define PINOUT_DBUS4_OFF 4
|
||||
#define PINOUT_DBUS5_OFF 5
|
||||
#define PINOUT_DBUS6_OFF 6
|
||||
#define PINOUT_DBUS7_OFF 7
|
||||
#define PINOUT_CBUS0_OFF 8
|
||||
#define PINOUT_CBUS1_OFF 9
|
||||
#define PINOUT_CBUS2_OFF 10
|
||||
#define PINOUT_CBUS3_OFF 11
|
||||
|
||||
#define PINOUT_UART_TXD_OFF 0
|
||||
#define PINOUT_UART_RXD_OFF 1
|
||||
#define PINOUT_UART_nRTS_OFF 2
|
||||
#define PINOUT_UART_nCTS_OFF 3
|
||||
#define PINOUT_UART_nDTR_OFF 4
|
||||
#define PINOUT_UART_nDSR_OFF 5
|
||||
#define PINOUT_UART_nDCD_OFF 6
|
||||
#define PINOUT_UART_nRI_OFF 7
|
||||
#define PINOUT_UART_TXDEN_OFF 8
|
||||
#define PINOUT_UART_nSLEEP_OFF 9
|
||||
#define PINOUT_UART_nRXLED_OFF 10
|
||||
#define PINOUT_UART_nTXLED_OFF 11
|
||||
|
||||
#define PINOUT_FIFO_D0_OFF 0
|
||||
#define PINOUT_FIFO_D1_OFF 1
|
||||
#define PINOUT_FIFO_D2_OFF 2
|
||||
#define PINOUT_FIFO_D3_OFF 3
|
||||
#define PINOUT_FIFO_D4_OFF 4
|
||||
#define PINOUT_FIFO_D5_OFF 5
|
||||
#define PINOUT_FIFO_D6_OFF 6
|
||||
#define PINOUT_FIFO_D7_OFF 7
|
||||
#define PINOUT_FIFO_nRXF_OFF 8
|
||||
#define PINOUT_FIFO_nTXE_OFF 9
|
||||
#define PINOUT_FIFO_nRD_OFF 10
|
||||
#define PINOUT_FIFO_WR_OFF 11
|
||||
|
||||
#define PINOUT_BBANG_D0_OFF 0
|
||||
#define PINOUT_BBANG_D1_OFF 1
|
||||
#define PINOUT_BBANG_D2_OFF 2
|
||||
#define PINOUT_BBANG_D3_OFF 3
|
||||
#define PINOUT_BBANG_D4_OFF 4
|
||||
#define PINOUT_BBANG_D5_OFF 5
|
||||
#define PINOUT_BBANG_D6_OFF 6
|
||||
#define PINOUT_BBANG_D7_OFF 7
|
||||
#define PINOUT_BBANG_nWR0_OFF 8
|
||||
#define PINOUT_BBANG_nRD0_OFF 9
|
||||
#define PINOUT_BBANG_nWR1_OFF 10
|
||||
#define PINOUT_BBANG_nRD1_OFF 11
|
||||
|
||||
#define PINOUT_MPSSE_TCK_CK_OFF 0
|
||||
#define PINOUT_MPSSE_TDI_DO_OFF 1
|
||||
#define PINOUT_MPSSE_TDO_DI_OFF 2
|
||||
#define PINOUT_MPSSE_TMS_CS_OFF 3
|
||||
#define PINOUT_MPSSE_GPIOL0_OFF 4
|
||||
#define PINOUT_MPSSE_GPIOL1_OFF 5
|
||||
#define PINOUT_MPSSE_GPIOL2_OFF 6
|
||||
#define PINOUT_MPSSE_GPIOL3_OFF 7
|
||||
#define PINOUT_MPSSE_GPIOH0_OFF 8
|
||||
#define PINOUT_MPSSE_GPIOH1_OFF 9
|
||||
#define PINOUT_MPSSE_GPIOH2_OFF 10
|
||||
#define PINOUT_MPSSE_GPIOH3_OFF 11
|
||||
|
||||
#define PINOUT_MCUHOST_AD0_OFF 0
|
||||
#define PINOUT_MCUHOST_AD1_OFF 1
|
||||
#define PINOUT_MCUHOST_AD2_OFF 2
|
||||
#define PINOUT_MCUHOST_AD3_OFF 3
|
||||
#define PINOUT_MCUHOST_AD4_OFF 4
|
||||
#define PINOUT_MCUHOST_AD5_OFF 5
|
||||
#define PINOUT_MCUHOST_AD6_OFF 6
|
||||
#define PINOUT_MCUHOST_AD7_OFF 7
|
||||
#define PINOUT_MCUHOST_IO0_OFF 8
|
||||
#define PINOUT_MCUHOST_IO1_OFF 9
|
||||
#define PINOUT_MCUHOST_IORDY_OFF 10
|
||||
#define PINOUT_MCUHOST_OSC_OFF 11
|
||||
// ---
|
||||
#define PINOUT_MCUHOST_A8_OFF 0
|
||||
#define PINOUT_MCUHOST_A9_OFF 1
|
||||
#define PINOUT_MCUHOST_AA_OFF 2
|
||||
#define PINOUT_MCUHOST_AB_OFF 3
|
||||
#define PINOUT_MCUHOST_AC_OFF 4
|
||||
#define PINOUT_MCUHOST_AD_OFF 5
|
||||
#define PINOUT_MCUHOST_AE_OFF 6
|
||||
#define PINOUT_MCUHOST_AF_OFF 7
|
||||
#define PINOUT_MCUHOST_nCS_OFF 8
|
||||
#define PINOUT_MCUHOST_ALE_OFF 9
|
||||
#define PINOUT_MCUHOST_nRD_OFF 10
|
||||
#define PINOUT_MCUHOST_nWR_OFF 11
|
||||
|
||||
#define PINOUT_CPUFIFO_D0_OFF 0
|
||||
#define PINOUT_CPUFIFO_D1_OFF 1
|
||||
#define PINOUT_CPUFIFO_D2_OFF 2
|
||||
#define PINOUT_CPUFIFO_D3_OFF 3
|
||||
#define PINOUT_CPUFIFO_D4_OFF 4
|
||||
#define PINOUT_CPUFIFO_D5_OFF 5
|
||||
#define PINOUT_CPUFIFO_D6_OFF 6
|
||||
#define PINOUT_CPUFIFO_D7_OFF 7
|
||||
#define PINOUT_CPUFIFO_nCS_OFF 8
|
||||
#define PINOUT_CPUFIFO_A0_OFF 9
|
||||
#define PINOUT_CPUFIFO_nRD_OFF 10
|
||||
#define PINOUT_CPUFIFO_nWR_OFF 11
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// vim: set et:
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <pico/binary_info.h>
|
||||
#include <pico/stdlib.h>
|
||||
#include <pico/unique_id.h>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "util.h"
|
||||
|
||||
uint8_t get_unique_id_u8(uint8_t* desc_str) {
|
||||
pico_unique_board_id_t uid;
|
||||
|
||||
uint8_t chr_count = 0;
|
||||
|
||||
pico_get_unique_board_id(&uid);
|
||||
|
||||
for (size_t byte = 0; byte < PICO_UNIQUE_BOARD_ID_SIZE_BYTES/*TU_ARRAY_SIZE(uid.id)*/; byte++) {
|
||||
uint8_t tmp = uid.id[byte];
|
||||
for (int digit = 0; digit < 2; digit++) {
|
||||
desc_str[chr_count++] = nyb2hex(tmp >> 4);
|
||||
tmp <<= 4;
|
||||
}
|
||||
}
|
||||
|
||||
return chr_count;
|
||||
}
|
||||
|
||||
uint8_t get_unique_id_u16(uint16_t* desc_str) {
|
||||
pico_unique_board_id_t uid;
|
||||
|
||||
uint8_t chr_count = 0;
|
||||
|
||||
pico_get_unique_board_id(&uid);
|
||||
|
||||
for (size_t byte = 0; byte < PICO_UNIQUE_BOARD_ID_SIZE_BYTES/*TU_ARRAY_SIZE(uid.id)*/; byte++) {
|
||||
uint8_t tmp = uid.id[byte];
|
||||
for (int digit = 0; digit < 2; digit++) {
|
||||
desc_str[chr_count++] = nyb2hex(tmp >> 4);
|
||||
tmp <<= 4;
|
||||
}
|
||||
}
|
||||
|
||||
return chr_count;
|
||||
}
|
||||
|
||||
// IDK, let's just put this somewhere
|
||||
|
||||
#ifdef PICO_NO_FLASH
|
||||
bi_decl(bi_program_build_attribute("Not in flash"));
|
||||
#elif defined(PICO_COPY_TO_RAM)
|
||||
bi_decl(bi_program_build_attribute("Copy-to-RAM"));
|
||||
#endif
|
||||
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
bi_decl(bi_program_build_attribute("USB-CDC stdio debug interface"));
|
||||
#endif
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||
|
||||
# This can be dropped into an external project to help locate this SDK
|
||||
# It should be include()ed prior to project()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||
|
||||
if (NOT PICO_SDK_PATH)
|
||||
if (PICO_SDK_FETCH_FROM_GIT)
|
||||
include(FetchContent)
|
||||
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||
endif ()
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
)
|
||||
if (NOT pico_sdk)
|
||||
message("Downloading Raspberry Pi Pico SDK")
|
||||
FetchContent_Populate(pico_sdk)
|
||||
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||
endif ()
|
||||
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||
else ()
|
||||
message(FATAL_ERROR
|
||||
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||
|
||||
include(${PICO_SDK_INIT_CMAKE_FILE})
|
|
@ -0,0 +1,132 @@
|
|||
@ vim: set ft=armv5:
|
||||
|
||||
.cpu cortex-m0
|
||||
.thumb
|
||||
|
||||
/*.section .bss.co_active_buffer, "aw", %nobits
|
||||
.type co_active_buffer, %object
|
||||
.size co_active_buffer, 4*64
|
||||
co_active_buffer:
|
||||
.space 4*64
|
||||
|
||||
.section .bss.co_active_handle, "aw", %nobits
|
||||
.type co_active_handle, %object
|
||||
.size co_active_handle, 4
|
||||
co_active_handle:
|
||||
.space 4*/
|
||||
|
||||
.extern co_active_handle
|
||||
.extern co_active_buffer
|
||||
|
||||
.section .text.co_active, "ax", %progbits
|
||||
.type co_active, %function
|
||||
.thumb_func
|
||||
.global co_active
|
||||
co_active:@()
|
||||
ldr r1, =co_active_handle
|
||||
ldr r0, [r1]
|
||||
cmp r0, #0
|
||||
bne 1f
|
||||
|
||||
ldr r0, =co_active_buffer
|
||||
str r0, [r1]
|
||||
|
||||
1: bx lr
|
||||
|
||||
.pool
|
||||
|
||||
.section .text.co_switch, "ax", %progbits
|
||||
.type co_switch, %function
|
||||
.thumb_func
|
||||
.global co_switch
|
||||
co_switch:@(cothread_t handle r0)
|
||||
@ co_previous_handle(r1) = co_active_handle
|
||||
@ co_active_handle = handle(r0)
|
||||
ldr r2, =co_active_handle
|
||||
ldr r1, [r2]
|
||||
str r0, [r2]
|
||||
|
||||
@ co_swap()
|
||||
|
||||
@ NOTE: we're assuming that the hw divider's state persistance won't be
|
||||
@ used
|
||||
|
||||
@ original ARM version:
|
||||
@stmia r1!, {r4-r11,sp,lr}
|
||||
@ldmia r0!, {r4-r11,sp,pc}
|
||||
@bx lr
|
||||
|
||||
stmia r1!, {r4-r7}
|
||||
mov r2, r8
|
||||
mov r3, r9
|
||||
mov r4, r10
|
||||
mov r5, r11
|
||||
mov r6, r12
|
||||
mov r7, sp
|
||||
stmia r1!, {r2-r7}
|
||||
mov r2, lr
|
||||
stmia r1!, {r2}
|
||||
|
||||
@ TODO: this could be done better, but oh well
|
||||
ldr r2, [r0, #0x10] @ r8
|
||||
ldr r3, [r0, #0x14] @ r9
|
||||
ldr r4, [r0, #0x18] @ r10
|
||||
ldr r5, [r0, #0x1c] @ r11
|
||||
ldr r6, [r0, #0x20] @ r12
|
||||
ldr r7, [r0, #0x24] @ sp
|
||||
mov r8, r2
|
||||
mov r9, r3
|
||||
mov r10, r4
|
||||
mov r11, r5
|
||||
mov r12, r6
|
||||
mov sp, r7
|
||||
|
||||
ldmia r0!, {r4-r7}
|
||||
|
||||
ldr r2, [r0, #(0x28-0x10)] @ pc
|
||||
@ldr r0, [r0, #(0x2c-0x10)] @ ud/r0 / retval
|
||||
mov pc, r2
|
||||
|
||||
bx lr
|
||||
|
||||
.pool
|
||||
|
||||
.section .text.co_derive, "ax", %progbits
|
||||
.type co_derive, %function
|
||||
.thumb_func
|
||||
.global co_derive
|
||||
co_derive:@(void* memory r0, unsigned int size r1, void(*entrypoint)(void*) r2, void* ud r3)
|
||||
push {r4}
|
||||
|
||||
@mov r12, r3 @ save ud for later
|
||||
|
||||
@ if (!co_active_handle) co_active_handle = &co_active_buffer
|
||||
ldr r3, =co_active_handle
|
||||
ldr r4, [r3]
|
||||
cmp r4, #0
|
||||
bne 1f
|
||||
|
||||
ldr r4, =co_active_buffer
|
||||
str r4, [r3]
|
||||
|
||||
@ if (!memory) return 0;
|
||||
1: cmp r0, #0
|
||||
beq .Lret
|
||||
|
||||
@ offset(r3) = size(r1) & ~15
|
||||
mov r3, r1
|
||||
mov r4, #0xf
|
||||
bic r3, r4 @ doesn't want to do an immediate, sigh...
|
||||
@ p(r3) = handle(r0) + offset(r3)
|
||||
add r3, r0
|
||||
@ initialize stack, entrypoint
|
||||
@str r12, [r0, #(11*4)] @ init r0: user argument
|
||||
str r3 , [r0, #( 9*4)] @ init sp/r13
|
||||
str r2 , [r0, #(10*4)] @ init pc/r15
|
||||
|
||||
.Lret:
|
||||
pop {r4}
|
||||
bx lr
|
||||
|
||||
.pool
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// vim: set et:
|
||||
|
||||
/* derived from libco v20, by byuu (ISC) */
|
||||
|
||||
#ifndef LIBCO_H_
|
||||
#define LIBCO_H_
|
||||
|
||||
typedef void* cothread_t;
|
||||
|
||||
cothread_t co_active(void);
|
||||
cothread_t co_derive(void* memory, unsigned int heapsize, void (*coentry)(void/***/)/*, void* ud*/);
|
||||
void co_switch(cothread_t); // should we make this return void* (and thus `ud`)?
|
||||
int co_serializable(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,495 @@
|
|||
// vim: set et:
|
||||
|
||||
#include "tusb_config.h"
|
||||
#include <tusb.h>
|
||||
|
||||
#include "thread.h"
|
||||
|
||||
#include "bsp-feature.h"
|
||||
#include "ftdi.h"
|
||||
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
// index = interface number (A/B)
|
||||
// out/in bRequest wValue wIndex data wLength
|
||||
// RESET: out 0 0 index
|
||||
// TCIFLUSH: out 0 2 index
|
||||
// TCOFLUSH: out 0 1 index
|
||||
// SETMODEMCTRL:out 1 (mask:8<<8 | data:8) // bit0=dtr bit1=rts
|
||||
// SETFLOWCTRL: out 2 xon?1:0 (flowctrl | index) // flowctrl: 0=disable, 1=ctsrts, 2=dtrdsr 4=xonxoff FROM VALUE, not set/reset!
|
||||
// SETBAUDRATE: out 3 brate brate24|index // 48 MHz clocks, /16? , baudrate is 24bit, highest byte in index MSB
|
||||
// SETLINEPROP: out 4 (break:1<<14 | stop:2<<11 | parity:3<<8 | bits:8) ; break: off/on, stop=1/15/2, parity=none/odd/even/mark/space; bits=7/8
|
||||
// POLLMODEMSTAT:in 5 0 index &modemstat len=2 // first byte: bit0..3=0 bit4=cts bit5=dts bit6=ri bit7=rlsd ; second byte: bit0=dr bit1=oe bit2=pe bit3=fe bit4=bi bit5=thre bit6=temt bit7=fifoerr
|
||||
// SETEVENTCHAR:out 6 (endis:1<<8 | char)
|
||||
// SETERRORCHAR:out 7 (endir:1<<8 | char)
|
||||
// <there is no bReqest 8>
|
||||
// SETLATENCY: out 9 latency(1..255)
|
||||
// GETLATENCY: in 0xa 0 index &latency len=1
|
||||
// SETBITBANG: out 0xb dirmask:8<<8 | mode:8 // mode: MPSSE mode: 0=serial/fifo, 1=bitbang, 2=mpsse, 4=syncbb, 8=mcu, 16=opto
|
||||
// READPINS: in 0xc 0 index &pins len=1
|
||||
// READEEPROM: in 0x90 0 eepaddr &val 2 // eepaddr in 16-bit units(?)
|
||||
// WRITEEEPROM: out 0x91 value eepaddr
|
||||
// ERASEEEPROM: out 0x92 0 0
|
||||
|
||||
// eeprom layout:
|
||||
// max size is 128 words (256 bytes), little-endian
|
||||
// 00: type, driver, .. stuff (chanA byte, chanB byte) -> TODO: what does this mean exactly?
|
||||
// 0=UART 1=FIFO 2=opto 4=CPUFIFO 8=FT1284
|
||||
// 01: VID
|
||||
// 02: PID
|
||||
// 03: bcdDevice
|
||||
// 04: byte08: flags: bit7=1 bit6=1=selfpowered bit5=1=remotewakeup ; byte09=max power, 2mA units
|
||||
// 05: bit0=epin isochr, bit1=epout isochr, bit2=suspend pulldn, bit3=serialno use, bit4=usbver change, bit5..7=0
|
||||
// 06: bcdUSB
|
||||
// 07: byte0E=manufstr.off-0x80, byte0F=manufstr.len (in bytes, but 16-bit ascii)
|
||||
// 08: ^ but product
|
||||
// 09: ^ but serial
|
||||
// 0a: chip type (66 etc -> we emulate '00', internal)
|
||||
// "strings start at 0x96 for ft2232c" (byte addr)
|
||||
// checksum:
|
||||
// initial: 0xaaaa
|
||||
// digest 1 byte:
|
||||
// checksum = (checksum ^ word) <<< 1;
|
||||
// placed at last word of EEPROM
|
||||
|
||||
// ftdi_write_data(), ftdi_read_data(): bulk xfer
|
||||
|
||||
// input/tristate: 200K pullup
|
||||
// SI/WU: let's ignore this
|
||||
// UART mode: standard stuff
|
||||
// FIFO mode: RDF#=0: enable output. RD# rising when RXF#=0: fetch next byte
|
||||
// TXF#=0: enable input. WR falling when TXF#=0: write byte
|
||||
// bitbang mode: ^ similar, no RDF#/TXF#, RD#/WR# (now WR# jenai WR) pos depends on UART mode std (UART vs FIFO)
|
||||
// sync bitbang: doesn't use RD#/WR#, clocked by baudrate control
|
||||
// MPSSE: lots of magic. TCK/SK, TDI/DO, TDO/DI, TMS/CS, GPIOL0..3, GPIOH0..3(7?)
|
||||
|
||||
// bulk xfer formats: (interface index <-> bulk epno)
|
||||
// * UART: ok I guess
|
||||
// * FIFO: same
|
||||
// * bitbang, sync bitbang: ???? (just data or also clock stuff?? ?)
|
||||
// * MPSSE, MCU: see separate PDF
|
||||
// * CPUFIFIO: same as FIFO ig
|
||||
// * opto, FT1284: not supported
|
||||
|
||||
#ifdef DBOARD_HAS_FTDI
|
||||
|
||||
int __builtin_ctz(unsigned int v);
|
||||
|
||||
uint16_t ftdi_eeprom[128];
|
||||
|
||||
struct ftdi_interface ftdi_ifa, ftdi_ifb;
|
||||
|
||||
void ftdi_init(void) {
|
||||
// init eeprom defaults
|
||||
memset(ftdi_eeprom, 0xff, sizeof ftdi_eeprom);
|
||||
ftdi_eeprom[ 0] = 0x0000; // both default to UART
|
||||
ftdi_eeprom[ 1] = 0x0403; // idVendor
|
||||
ftdi_eeprom[ 2] = 0x6010; // idProduct
|
||||
ftdi_eeprom[ 3] = 0x0500; // bcdDevice
|
||||
ftdi_eeprom[ 4] = 0x5080; // 100 mA, no flags
|
||||
ftdi_eeprom[ 5] = 0x0000; // more flags
|
||||
ftdi_eeprom[ 6] = 0x0110; // bcdUSB
|
||||
ftdi_eeprom[ 7] = 0x0000; // no manuf. str (TODO?)
|
||||
ftdi_eeprom[ 8] = 0x0000; // no prod. str (TODO?)
|
||||
ftdi_eeprom[ 9] = 0x0000; // no serial str (TODO?)
|
||||
ftdi_eeprom[10] = 0x0000; // internal chip
|
||||
ftdi_eeprom[0x7f] = ftdi_eeprom_checksum_calc(ftdi_eeprom, 0x7f);
|
||||
|
||||
memset(&ftdi_ifa, 0, sizeof ftdi_ifa);
|
||||
memset(&ftdi_ifb, 0, sizeof ftdi_ifb);
|
||||
ftdi_ifa.lineprop = sio_bits_8 | sio_stop_1; // 8n1
|
||||
ftdi_ifb.lineprop = sio_bits_8 | sio_stop_1; // 8n1
|
||||
ftdi_ifa.index = 0;
|
||||
ftdi_ifb.index = 1;
|
||||
ftdi_ifa.modemstat = sio_modem_temt;
|
||||
ftdi_ifb.modemstat = sio_modem_temt;
|
||||
ftdi_if_init(&ftdi_ifa);
|
||||
ftdi_if_init(&ftdi_ifb);
|
||||
}
|
||||
void ftdi_deinit(void) {
|
||||
ftdi_if_deinit(&ftdi_ifa);
|
||||
ftdi_if_deinit(&ftdi_ifb);
|
||||
}
|
||||
|
||||
static uint8_t vnd_read_byte(struct ftdi_interface* itf, int itfnum) {
|
||||
while (itf->rxavail <= 0) {
|
||||
if (!tud_vendor_n_mounted(itfnum) || !tud_vendor_n_available(itfnum)) {
|
||||
thread_yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
itf->rxpos = 0;
|
||||
itf->rxavail = tud_vendor_n_read(itfnum, itf->bufbuf, sizeof itf->bufbuf);
|
||||
|
||||
if (itf->rxavail == 0) thread_yield();
|
||||
}
|
||||
|
||||
uint8_t rv = itf->bufbuf[itf->rxpos];
|
||||
++itf->rxpos;
|
||||
--itf->rxavail;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
typedef void (*ftfifo_write_fn)(struct ftdi_interface*, const uint8_t*, size_t);
|
||||
typedef size_t (*ftfifo_read_fn)(struct ftdi_interface*, uint8_t*, size_t);
|
||||
struct ftfifo_fns {
|
||||
ftfifo_write_fn write;
|
||||
ftfifo_read_fn read ;
|
||||
};
|
||||
static const struct ftfifo_fns fifocbs[] = {
|
||||
{ ftdi_if_uart_write, ftdi_if_uart_read }, // technically mpsse
|
||||
{ ftdi_if_asyncbb_write, ftdi_if_asyncbb_read },
|
||||
{ ftdi_if_syncbb_write, ftdi_if_syncbb_read },
|
||||
{ NULL, NULL }, // mcuhost
|
||||
{ ftdi_if_fifo_write, ftdi_if_fifo_read },
|
||||
{ NULL, NULL }, // opto
|
||||
{ ftdi_if_cpufifo_write, ftdi_if_cpufifo_read },
|
||||
{ NULL, NULL }, // ft1284
|
||||
};
|
||||
|
||||
// for handling USB bulk commands
|
||||
static void ftdi_task(int itfnum) {
|
||||
if (!tud_vendor_n_mounted(itfnum)) return; // can't do much in this case
|
||||
|
||||
struct ftdi_interface* itf;
|
||||
if (itfnum == VND_N_FTDI_IFA) itf = &ftdi_ifa;
|
||||
else if (itfnum == VND_N_FTDI_IFB) itf = &ftdi_ifb;
|
||||
else return;
|
||||
|
||||
|
||||
// for UART, FIFO, asyncbb, syncbb, and CPUfifo modes, operation is
|
||||
// relatively straightforward: the device acts like some sort of FIFO, so
|
||||
// it's just shoving bytes to the output pins. MPSSE and MCU host emulation
|
||||
// modes are more difficult, as the bulk data actually has some form of
|
||||
// protocol.
|
||||
enum ftdi_mode mode = ftdi_if_get_mode(itf);
|
||||
struct ftfifo_fns fifocb = fifocbs[(mode == 0) ? 0 : __builtin_ctz(mode)];
|
||||
uint32_t avail;
|
||||
uint8_t cmdbyte = 0;
|
||||
switch (mode) {
|
||||
case ftmode_uart : case ftmode_fifo : case ftmode_cpufifo:
|
||||
case ftmode_asyncbb: case ftmode_syncbb:
|
||||
if (fifocb.read == NULL || fifocb.write == NULL) goto CASE_DEFAULT; // welp
|
||||
|
||||
avail = tud_vendor_n_available(itfnum);
|
||||
if (avail) {
|
||||
tud_vendor_n_read(itfnum, itf->writebuf, avail);
|
||||
fifocb.write(itf, itf->writebuf, avail);
|
||||
}
|
||||
|
||||
do {
|
||||
avail = fifocb.read(itf, itf->readbuf, sizeof itf->readbuf);
|
||||
|
||||
if (avail) tud_vendor_n_write(itfnum, itf->readbuf, avail);
|
||||
} while (avail == sizeof itf->readbuf);
|
||||
break;
|
||||
case ftmode_mpsse:
|
||||
avail = 0;
|
||||
|
||||
switch ((cmdbyte = vnd_read_byte(itf, itfnum))) {
|
||||
case ftmpsse_set_dirval_lo: // low byte of output gpio, not to low level
|
||||
itf->writebuf[0] = vnd_read_byte(itf, itfnum); // val
|
||||
itf->writebuf[1] = vnd_read_byte(itf, itfnum); // dir
|
||||
ftdi_if_mpsse_set_dirval_lo(itf, itf->writebuf[1], itf->writebuf[0]);
|
||||
break;
|
||||
case ftmpsse_set_dirval_hi:
|
||||
itf->writebuf[0] = vnd_read_byte(itf, itfnum); // val
|
||||
itf->writebuf[1] = vnd_read_byte(itf, itfnum); // dir
|
||||
ftdi_if_mpsse_set_dirval_hi(itf, itf->writebuf[1], itf->writebuf[0]);
|
||||
break;
|
||||
case ftmpsse_read_lo:
|
||||
itf->readbuf[0] = ftdi_if_mpsse_read_lo(itf);
|
||||
avail = 1;
|
||||
break;
|
||||
case ftmpsse_read_hi:
|
||||
itf->readbuf[0] = ftdi_if_mpsse_read_hi(itf);
|
||||
avail = 1;
|
||||
break;
|
||||
case ftmpsse_loopback_on : ftdi_if_mpsse_loopback(itf, true ); break;
|
||||
case ftmpsse_loopback_off: ftdi_if_mpsse_loopback(itf, false); break;
|
||||
case ftmpsse_set_clkdiv:
|
||||
avail = vnd_read_byte(itf, itfnum);
|
||||
avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8;
|
||||
ftdi_if_mpsse_set_clkdiv(itf, (uint16_t)avail);
|
||||
avail = 0;
|
||||
break;
|
||||
|
||||
case ftmpsse_flush: ftdi_if_mpsse_flush(itf); break;
|
||||
case ftmpsse_wait_io_hi: ftdi_if_mpsse_wait_io(itf, true ); break;
|
||||
case ftmpsse_wait_io_lo: ftdi_if_mpsse_wait_io(itf, false); break;
|
||||
|
||||
case ftmpsse_div5_disable: ftdi_if_mpsse_div5(itf, false); break;
|
||||
case ftmpsse_div5_enable : ftdi_if_mpsse_div5(itf, true ); break;
|
||||
case ftmpsse_data_3ph_en : ftdi_if_mpsse_data_3ph(itf, true ); break;
|
||||
case ftmpsse_data_3ph_dis: ftdi_if_mpsse_data_3ph(itf, false); break;
|
||||
case ftmpsse_clockonly_bits: ftdi_if_mpsse_clockonly(itf, vnd_read_byte(itf, itfnum)); break;
|
||||
case ftmpsse_clockonly_bytes:
|
||||
avail = vnd_read_byte(itf, itfnum);
|
||||
avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8;
|
||||
ftdi_if_mpsse_clockonly(itf, avail);
|
||||
avail = 0;
|
||||
break;
|
||||
case ftmpsse_clock_wait_io_hi: ftdi_if_mpsse_clock_wait_io(itf, true ); break;
|
||||
case ftmpsse_clock_wait_io_lo: ftdi_if_mpsse_clock_wait_io(itf, false); break;
|
||||
case ftmpsse_adapclk_enable : ftdi_if_mpsse_adaptive(itf, true ); break;
|
||||
case ftmpsse_adapclk_disable: ftdi_if_mpsse_adaptive(itf, false); break;
|
||||
case ftmpsse_clock_bits_wait_io_hi:
|
||||
avail = vnd_read_byte(itf, itfnum);
|
||||
avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8;
|
||||
ftdi_if_mpsse_clockonly_wait_io(itf, true, avail);
|
||||
avail = 0;
|
||||
break;
|
||||
case ftmpsse_clock_bits_wait_io_lo:
|
||||
avail = vnd_read_byte(itf, itfnum);
|
||||
avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8;
|
||||
ftdi_if_mpsse_clockonly_wait_io(itf, false, avail);
|
||||
avail = 0;
|
||||
break;
|
||||
case ftmpsse_hi_is_tristate:
|
||||
avail = vnd_read_byte(itf, itfnum);
|
||||
avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8;
|
||||
ftdi_if_mpsse_hi_is_tristate(itf, avail);
|
||||
avail = 0;
|
||||
break;
|
||||
|
||||
default: CASE_DEFAULT:
|
||||
if (!(cmdbyte & ftmpsse_specialcmd)) {
|
||||
if (cmdbyte & ftmpsse_tmswrite) {
|
||||
if (cmdbyte & ftmpsse_bitmode) {
|
||||
itf->writebuf[0] = vnd_read_byte(itf, itfnum); // number of bits
|
||||
itf->writebuf[1] = vnd_read_byte(itf, itfnum); // data bits to output
|
||||
itf->readbuf[0] = ftdi_if_mpsse_tms_xfer(itf, cmdbyte, itf->writebuf[0], itf->writebuf[1]);
|
||||
if (cmdbyte & ftmpsse_tdoread) avail = 1;
|
||||
break;
|
||||
}
|
||||
// else: fallthru to error code
|
||||
} else {
|
||||
if (cmdbyte & ftmpsse_bitmode) {
|
||||
itf->writebuf[0] = vnd_read_byte(itf, itfnum); // number of bits
|
||||
if (cmdbyte & ftmpsse_tdiwrite)
|
||||
itf->writebuf[1] = vnd_read_byte(itf, itfnum); // data bits to output
|
||||
itf->readbuf[0] = ftdi_if_mpsse_xfer_bits(itf, cmdbyte, itf->writebuf[0], itf->writebuf[1]);
|
||||
if (cmdbyte & ftmpsse_tdoread) avail = 1;
|
||||
break;
|
||||
} else {
|
||||
avail = vnd_read_byte(itf, itfnum);
|
||||
avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8;
|
||||
|
||||
for (size_t i = 0; i < avail; i += 64) {
|
||||
uint32_t thisbatch = avail - i;
|
||||
if (thisbatch > 64) thisbatch = 64;
|
||||
for (size_t j = 0; j < thisbatch; ++j)
|
||||
itf->writebuf[j] = vnd_read_byte(itf, itfnum);
|
||||
ftdi_if_mpsse_xfer_bytes(itf, cmdbyte, thisbatch, itf->readbuf, itf->writebuf);
|
||||
tud_vendor_n_write(itfnum, itf->readbuf, thisbatch);
|
||||
}
|
||||
|
||||
avail = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
itf->readbuf[0] = 0xfa;
|
||||
itf->readbuf[1] = cmdbyte;
|
||||
avail = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (avail) tud_vendor_n_write(itfnum, itf->readbuf, avail);
|
||||
break;
|
||||
case ftmode_mcuhost:
|
||||
avail = 0;
|
||||
switch ((cmdbyte = vnd_read_byte(itf, itfnum))) {
|
||||
case ftmcu_flush: ftdi_if_mcuhost_flush(itf); break;
|
||||
case ftmcu_wait_io_hi: ftdi_if_mcuhost_wait_io(itf, true ); break;
|
||||
case ftmcu_wait_io_lo: ftdi_if_mcuhost_wait_io(itf, false); break;
|
||||
|
||||
case ftmcu_read8:
|
||||
itf->readbuf[0] = ftdi_if_mcuhost_read8(itf, vnd_read_byte(itf, itfnum));
|
||||
avail = 1;
|
||||
break;
|
||||
case ftmcu_read16:
|
||||
avail = (uint32_t)vnd_read_byte(itf, itfnum) << 8;
|
||||
avail |= vnd_read_byte(itf, itfnum);
|
||||
itf->readbuf[0] = ftdi_if_mcuhost_read16(itf, (uint16_t)avail);
|
||||
avail = 1;
|
||||
break;
|
||||
case ftmcu_write8:
|
||||
itf->writebuf[0] = vnd_read_byte(itf, itfnum);
|
||||
itf->writebuf[1] = vnd_read_byte(itf, itfnum);
|
||||
ftdi_if_mcuhost_write8(itf, itf->writebuf[0], itf->writebuf[1]);
|
||||
break;
|
||||
case ftmcu_write16:
|
||||
avail = (uint32_t)vnd_read_byte(itf, itfnum) << 8;
|
||||
avail |= vnd_read_byte(itf, itfnum);
|
||||
itf->writebuf[0] = vnd_read_byte(itf, itfnum);
|
||||
ftdi_if_mcuhost_write8(itf, avail, itf->writebuf[0]);
|
||||
avail = 0;
|
||||
break;
|
||||
|
||||
default: // send error response when command doesn't exist
|
||||
itf->readbuf[0] = 0xfa;
|
||||
itf->readbuf[1] = cmdbyte;
|
||||
avail = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (avail) tud_vendor_n_write(itfnum, itf->readbuf, avail);
|
||||
break;
|
||||
default: // drop incoming data so that the pipes don't get clogged. can't do much else
|
||||
avail = tud_vendor_n_available(itfnum);
|
||||
if (avail) tud_vendor_n_read(itfnum, itf->writebuf, avail);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void ftdi_task_ifa(void) { ftdi_task(VND_N_FTDI_IFA); }
|
||||
void ftdi_task_ifb(void) { ftdi_task(VND_N_FTDI_IFB); }
|
||||
|
||||
#define FT2232D_CLOCK (48*1000*1000)
|
||||
|
||||
uint32_t ftdi_if_decode_baudrate(uint32_t enc_brate) { // basically reversing libftdi ftdi_to_clkbits
|
||||
static const uint8_t ftdi_brate_frac_lut[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
|
||||
|
||||
// special cases
|
||||
if (enc_brate == 0) return FT2232D_CLOCK >> 4;
|
||||
else if (enc_brate == 1) return FT2232D_CLOCK / 24;
|
||||
else if (enc_brate == 2) return FT2232D_CLOCK >> 5;
|
||||
|
||||
uint32_t div = (enc_brate & 0x7fff) << 3; // integer part
|
||||
uint32_t frac = ftdi_brate_frac_lut[(enc_brate >> 14) & 7];
|
||||
//printf("enc: div=%lu frac=%lu\n", div, frac);
|
||||
div = div | frac;
|
||||
uint32_t baud = FT2232D_CLOCK / div;
|
||||
//printf("rawbaud=%lu\n", baud);
|
||||
|
||||
if (baud & 1) baud = (baud >> 1) + 1; // rounding
|
||||
else baud >>= 1;
|
||||
return baud;
|
||||
}
|
||||
|
||||
static uint8_t control_buf[2];
|
||||
|
||||
bool ftdi_control_xfer_cb(uint8_t rhport, uint8_t stage,
|
||||
tusb_control_request_t const* req) {
|
||||
// return true: don't stall
|
||||
// return false: stall
|
||||
|
||||
// not a vendor request -> not meant for this code
|
||||
if (req->bmRequestType_bit.type != TUSB_REQ_TYPE_VENDOR) return true;
|
||||
|
||||
// data stage not needed: data stages only for device->host xfers, never host->device
|
||||
if (stage != CONTROL_STAGE_SETUP) return true;
|
||||
|
||||
// tud_control_status(rhport, req); : acknowledge
|
||||
// tud_control_xfer(rhport, req, bufaddr, size); : ack + there's a data phase with stuff to do
|
||||
// write: send bufaddr value. read: data stage will have bufaddr filled out
|
||||
|
||||
// do EEPROM stuff first, as these don't use wIndex as channel select
|
||||
uint16_t tmp16;
|
||||
switch (req->bRequest) {
|
||||
case sio_readeeprom:
|
||||
tmp16 = ftdi_eeprom[req->wIndex & 0x7f];
|
||||
control_buf[0] = tmp16 & 0xff;
|
||||
control_buf[1] = tmp16 >> 8;
|
||||
return tud_control_xfer(rhport, req, control_buf, 2);
|
||||
case sio_writeeeprom:
|
||||
ftdi_eeprom[req->wIndex & 0x7f] = req->wValue;
|
||||
return tud_control_status(rhport, req);
|
||||
case sio_eraseeeprom:
|
||||
memset(ftdi_eeprom, 0xff, sizeof ftdi_eeprom);
|
||||
return tud_control_status(rhport, req);
|
||||
}
|
||||
|
||||
int itfnum = req->wIndex & 0xff;
|
||||
struct ftdi_interface* itf = &ftdi_ifa; // default
|
||||
|
||||
if (itfnum > 2) return false; // bad interface number
|
||||
else if (itfnum == 1) itf = &ftdi_ifa;
|
||||
else if (itfnum == 2) itf = &ftdi_ifb;
|
||||
|
||||
switch (req->bRequest) {
|
||||
case sio_cmd:
|
||||
if (req->wValue == sio_reset) {
|
||||
itf->modem_mask = 0;
|
||||
itf->modem_data = 0;
|
||||
itf->flow = ftflow_none;
|
||||
itf->lineprop = sio_bits_8 | sio_stop_1; // 8n1
|
||||
itf->charen = 0;
|
||||
itf->bb_dir = 0;
|
||||
itf->bb_mode = sio_mode_reset;
|
||||
itf->mcu_addr_latch = 0;
|
||||
itf->rxavail = 0; itf->rxpos = 0;
|
||||
ftdi_if_sio_reset(itf);
|
||||
} else if (req->wValue == sio_tciflush) {
|
||||
itf->rxavail = 0; itf->rxpos = 0;
|
||||
ftdi_if_sio_tciflush(itf);
|
||||
} else if (req->wValue == sio_tcoflush) {
|
||||
// nothing extra to clear here I think
|
||||
ftdi_if_sio_tcoflush(itf);
|
||||
} else return false; // unk
|
||||
return tud_control_status(rhport, req);
|
||||
case sio_setmodemctrl:
|
||||
ftdi_if_set_modemctrl(itf,
|
||||
itf->modem_mask = (req->wValue >> 8),
|
||||
itf->modem_data = (req->wValue & 0xff));
|
||||
return tud_control_status(rhport, req);
|
||||
case sio_setflowctrl: {
|
||||
enum ftdi_flowctrl flow = (req->wIndex >> 8);
|
||||
if (!req->wValue)
|
||||
flow = (flow & ~ftflow_xonxoff) | (itf->flow & ftflow_xonxoff);
|
||||
ftdi_if_set_flowctrl(itf, itf->flow = flow);
|
||||
} return tud_control_status(rhport, req);
|
||||
case sio_setbaudrate: {
|
||||
// BUG: somehow, req->wValue gets completely mangled over USB? should
|
||||
// be 0x001a for 115200 but I'm getting 0x4183 or so
|
||||
//printf("value=%04x index=%04x\n", req->wValue, req->wIndex);
|
||||
uint32_t enc_brate = (uint32_t)req->wValue | ((uint32_t)(req->wIndex & 0xff00) << 8);
|
||||
uint32_t brate = ftdi_if_decode_baudrate(enc_brate);
|
||||
//printf("br dec: %lu -> %lu\n", enc_brate, brate);
|
||||
ftdi_if_set_baudrate(itf, itf->baudrate = brate);
|
||||
} return tud_control_status(rhport, req);
|
||||
case sio_setlineprop:
|
||||
ftdi_if_set_lineprop(itf, itf->lineprop = req->wValue);
|
||||
return tud_control_status(rhport, req);
|
||||
case sio_pollmodemstat:
|
||||
tmp16 = ftdi_if_poll_modemstat(itf);
|
||||
control_buf[0] = tmp16 & 0xff;
|
||||
control_buf[1] = tmp16 >> 8;
|
||||
return tud_control_xfer(rhport, req, control_buf, 2);
|
||||
case sio_seteventchar:
|
||||
if (req->wValue >> 8) itf->charen |= eventchar_enable;
|
||||
else if (itf->charen & eventchar_enable) itf->charen ^= eventchar_enable;
|
||||
ftdi_if_set_eventchar(itf, req->wValue >> 8,
|
||||
itf->eventchar = (req->wValue & 0xff));
|
||||
return tud_control_status(rhport, req);
|
||||
case sio_seterrorchar:
|
||||
if (req->wValue >> 8) itf->charen |= errorchar_enable;
|
||||
else if (itf->charen & errorchar_enable) itf->charen ^= errorchar_enable;
|
||||
ftdi_if_set_errorchar(itf, req->wValue >> 8,
|
||||
itf->errorchar = (req->wValue & 0xff));
|
||||
return tud_control_status(rhport, req);
|
||||
case sio_setlatency:
|
||||
ftdi_if_set_latency(itf, itf->latency = (req->wValue & 0xff));
|
||||
return tud_control_status(rhport, req);
|
||||
case sio_getlatency:
|
||||
control_buf[0] = ftdi_if_get_latency(itf);
|
||||
return tud_control_xfer(rhport, req, control_buf, 1);
|
||||
case sio_setbitbang: {
|
||||
uint8_t olddir = itf->bb_dir;
|
||||
enum ftdi_sio_bitmode oldmode = itf->bb_mode;
|
||||
ftdi_if_set_bitbang(itf,
|
||||
itf->bb_dir = (req->wValue >> 8),
|
||||
itf->bb_mode = (req->wValue & 0xff),
|
||||
olddir, oldmode);
|
||||
} return tud_control_status(rhport, req);
|
||||
case sio_readpins:
|
||||
control_buf[0] = ftdi_if_read_pins(itf);
|
||||
return tud_control_xfer(rhport, req, control_buf, 1);
|
||||
default: return false; // stall if not recognised
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DBOARD_HAS_FTDI */
|
||||
|
|
@ -0,0 +1,375 @@
|
|||
// vim: set et:
|
||||
|
||||
#ifndef FTDI_H_
|
||||
#define FTDI_H_
|
||||
|
||||
#include "tusb_config.h"
|
||||
#include <tusb.h>
|
||||
|
||||
// USB command handling, and mode interfacing stuff
|
||||
|
||||
void ftdi_init(void);
|
||||
void ftdi_deinit(void);
|
||||
|
||||
// have separate tasks for the separate bulk endpoints so that a wait-for-data
|
||||
// in one ep won't cause the otehr to stall, too
|
||||
void ftdi_task_ifa(void);
|
||||
void ftdi_task_ifb(void);
|
||||
|
||||
bool ftdi_control_xfer_cb(uint8_t rhport, uint8_t ep_addr,
|
||||
tusb_control_request_t const* req);
|
||||
|
||||
void ftdi_eeprom_dirty_set(bool v);
|
||||
bool ftdi_eeprom_dirty_get(void);
|
||||
|
||||
extern uint16_t ftdi_eeprom[128];
|
||||
|
||||
#define FTDI_EEP_IFA_MODE (ftdi_eeprom[0] & 0xff)
|
||||
#define FTDI_EEP_IFB_MODE (ftdi_eeprom[0] >> 8)
|
||||
#define FTDI_EEP_IDVENDOR (ftdi_eeprom[1])
|
||||
#define FTDI_EEP_IDPRODUCT (ftdi_eeprom[2])
|
||||
#define FTDI_EEP_BCDDEVICE (ftdi_eeprom[3])
|
||||
#define FTDI_EEP_PWFLAGS (ftdi_eeprom[4] & 0xff)
|
||||
#define FTDI_EEP_MAXAMP (ftdi_eeprom[4] >> 8)
|
||||
#define FTDI_EEP_USBFLAGS (ftdi_eeprom[5])
|
||||
#define FTDI_EEP_BCDUSB (ftdi_eeprom[6])
|
||||
#define FTDI_EEP_MANUF_OFF (ftdi_eeprom[7] & 0xff)
|
||||
#define FTDI_EEP_MANUF_LEN (ftdi_eeprom[7] >> 8)
|
||||
#define FTDI_EEP_PROD_OFF (ftdi_eeprom[8] & 0xff)
|
||||
#define FTDI_EEP_PROD_LEN (ftdi_eeprom[8] >> 8)
|
||||
#define FTDI_EEP_SERIAL_OFF (ftdi_eeprom[9] & 0xff)
|
||||
#define FTDI_EEP_SERIAL_LEN (ftdi_eeprom[9] >> 8)
|
||||
#define FTDI_EEP_CHIPTYPE (ftdi_eeprom[10])
|
||||
|
||||
static inline uint16_t ftdi_eeprom_checksum_init(void) {
|
||||
return 0xaaaa;
|
||||
}
|
||||
static inline uint16_t ftdi_eeprom_checksum_digest(uint16_t val, const uint16_t* data, size_t len) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
val = (val ^ data[i]);
|
||||
val = (val << 1) | (val >> 15);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
static inline uint16_t ftdi_eeprom_checksum_calc(const uint16_t* data, size_t len) {
|
||||
return ftdi_eeprom_checksum_digest(ftdi_eeprom_checksum_init(), data, len);
|
||||
}
|
||||
|
||||
// USB protocol stuff
|
||||
|
||||
enum ftdi_request {
|
||||
sio_cmd = 0,
|
||||
sio_setmodemctrl = 1,
|
||||
sio_setflowctrl = 2,
|
||||
sio_setbaudrate = 3,
|
||||
sio_setlineprop = 4,
|
||||
sio_pollmodemstat = 5,
|
||||
sio_seteventchar = 6,
|
||||
sio_seterrorchar = 7,
|
||||
|
||||
sio_setlatency = 9,
|
||||
sio_getlatency = 10,
|
||||
sio_setbitbang = 11,
|
||||
sio_readpins = 12,
|
||||
|
||||
sio_readeeprom = 0x90,
|
||||
sio_writeeeprom = 0x91,
|
||||
sio_eraseeeprom = 0x92
|
||||
};
|
||||
|
||||
enum ftdi_sio_cmd {
|
||||
sio_reset = 0,
|
||||
sio_tciflush = 2,
|
||||
sio_tcoflush = 1
|
||||
};
|
||||
|
||||
enum ftdi_sio_lineprop {
|
||||
sio_break_on = 1<<14,
|
||||
sio_break_off = 0<<14,
|
||||
sio_break__mask = 1<<14,
|
||||
|
||||
sio_stop_1 = 0<<11,
|
||||
sio_stop_15 = 1<<11, // deprecated?
|
||||
sio_stop_2 = 2<<11,
|
||||
sio_stop__mask = 3<<11,
|
||||
|
||||
sio_parity_none = 0<<8,
|
||||
sio_parity_odd = 1<<8,
|
||||
sio_parity_even = 2<<8,
|
||||
sio_parity_mark = 3<<8,
|
||||
sio_parity_space = 4<<8,
|
||||
sio_parity__mask = 7<<8,
|
||||
|
||||
sio_bits_7 = 7<<0,
|
||||
sio_bits_8 = 8<<0,
|
||||
sio_bits__mask = 0xff<<0,
|
||||
};
|
||||
|
||||
enum ftdi_sio_modemstat {
|
||||
sio_modem_cts = 1<< 4, // Clear to Send active
|
||||
sio_modem_dts = 1<< 5, // Data Set Ready active
|
||||
sio_modem_ri = 1<< 6, // Ring Indicator active
|
||||
sio_modem_rlsd = 1<< 7, // Receive Line Signal Detect active
|
||||
|
||||
sio_modem_dr = 1<< 8, // Data Ready
|
||||
sio_modem_oe = 1<< 9, // Overrun Error
|
||||
sio_modem_pe = 1<<10, // Parity Error
|
||||
sio_modem_fe = 1<<11, // Framing Error
|
||||
sio_modem_bi = 1<<12, // Break Interrupt
|
||||
sio_modem_thre = 1<<13, // Transmitter Holding REgister
|
||||
sio_modem_temt = 1<<14, // Transmitter Empty
|
||||
sio_modem_fifoerr = 1<<15 // Error in receive FIFO
|
||||
};
|
||||
|
||||
enum ftdi_sio_bitmode {
|
||||
sio_mode_reset = 0, // i.e. from EEPROM
|
||||
sio_mode_bitbang = 1,
|
||||
sio_mode_mpsse = 2,
|
||||
sio_mode_syncbb = 4,
|
||||
sio_mode_mcu = 8,
|
||||
|
||||
// 0x10: opto
|
||||
// 0x20: cbus bitbang (R-type only)
|
||||
// 0x40: sync fifo (2232h) // like regular fifo mode, but with clock output
|
||||
// 0x80: ft1284 (232h, not 2232d)
|
||||
};
|
||||
|
||||
enum ftdi_eep_defmode {
|
||||
fteep_mode_uart = 0,
|
||||
fteep_mode_fifo = 1,
|
||||
fteep_mode_opto = 2, // not implementing this here
|
||||
fteep_mode_cpu = 4,
|
||||
fteep_mode_ft1284 = 8, // not impl. on 2232d, 232h-only
|
||||
};
|
||||
|
||||
// mpsse, mcuhost commands
|
||||
|
||||
// if bit 7 of an MPSSE command byte
|
||||
|
||||
enum ftdi_mpsse_cflg {
|
||||
ftmpsse_negedge_wr = 1<<0, // if 0, output bits on positive clock edige
|
||||
ftmpsse_bitmode = 1<<1, // if 0, byte mode
|
||||
ftmpsse_negedge_rd = 1<<2, // if 0, input bits on positive clock edge
|
||||
ftmpsse_lsbfirst = 1<<3, // if 0, msb first
|
||||
ftmpsse_tdiwrite = 1<<4, // 1 == do perform output
|
||||
ftmpsse_tdoread = 1<<5, // 1 == do perform input
|
||||
ftmpsse_tmswrite = 1<<6, // 1 == do perform output?
|
||||
|
||||
ftmpsse_specialcmd = 1<<7 // see below enum if set
|
||||
};
|
||||
// bitmode: 1 length byte, max=7 (#bits = length+1) for separate bits
|
||||
// bytemode: 2 length bytes == number of bytes that follow
|
||||
// both tdiwrite and tdoread high: only one length value/equal number of bits in/out!
|
||||
// if both tdiwrite and tdoread are high, negedge_wr and negedge_rd must differ
|
||||
// tms read/writes: readback is from tdo, bit7 in databyte is tdi output, held constant
|
||||
// tdiwrite always 0, bitmode 1 in impls, can be ignored I guess
|
||||
// also always lsbfirst, but not too hard to support msbfirst too
|
||||
// idle levels (of eg. tms/cs) -> set_dirval?
|
||||
|
||||
enum ftdi_mpssemcu_cmd {
|
||||
ftmpsse_set_dirval_lo = 0x80, // sets initial clock level!
|
||||
ftmpsse_set_dirval_hi = 0x82,
|
||||
ftmpsse_read_lo = 0x81,
|
||||
ftmpsse_read_hi = 0x83,
|
||||
ftmpsse_loopback_on = 0x84,
|
||||
ftmpsse_loopback_off = 0x85,
|
||||
ftmpsse_set_clkdiv = 0x86, // period = 12MHz / ((1 + value16) * 2)
|
||||
|
||||
ftmpsse_flush = 0x87, // flush dev->host usb buffer
|
||||
ftmpsse_wait_io_hi = 0x88, // wait for gpiol1/io1 to be high
|
||||
ftmpsse_wait_io_lo = 0x89, // wait for gpiol1/io1 to be low
|
||||
|
||||
// technically ft2232h-only but we can support these, too
|
||||
ftmpsse_div5_disable = 0x8a, // ft2232h internally has a 5x faster clock, but slows it down by default
|
||||
ftmpsse_div5_enable = 0x8b, // for backwards compat. these two commands enable/disable that slowdown
|
||||
ftmpsse_data_3ph_en = 0x8c, // enable 3-phase data
|
||||
ftmpsse_data_3ph_dis = 0x8d, // disable 3-phase data
|
||||
ftmpsse_clockonly_bits = 0x8e, // enable clock for n bits, no data xfer
|
||||
ftmpsse_clockonly_bytes = 0x8f, // enable clock for n bytes, no data xfer
|
||||
ftmpsse_clock_wait_io_hi = 0x94, // wait_io_hi + clockonly
|
||||
ftmpsse_clock_wait_io_lo = 0x95, // wait_io_lo + clockonly
|
||||
ftmpsse_adapclk_enable = 0x96, // enable ARM JTAG adaptive clocking (rtck gpiol3 input)
|
||||
ftmpsse_adapclk_disable = 0x97, // disable ARM JTAG adaptive clocking (rtck gpiol3 input)
|
||||
ftmpsse_clock_bits_wait_io_hi = 0x9c, // clock_wait_io_hi + clockonly_bits
|
||||
ftmpsse_clock_bits_wait_io_lo = 0x9d, // clock_wait_io_lo + clockonly_bits
|
||||
ftmpsse_hi_is_tristate = 0x9e, // turns 1 output to tristate for selected outputs
|
||||
|
||||
ftmcu_flush = 0x87, // flush dev->host usb buffer
|
||||
ftmcu_wait_io_hi = 0x88, // wait for gpiol1/io1 to be high
|
||||
ftmcu_wait_io_lo = 0x89, // wait for gpiol1/io1 to be low
|
||||
|
||||
ftmcu_read8 = 0x90,
|
||||
ftmcu_read16 = 0x91,
|
||||
ftmcu_write8 = 0x92,
|
||||
ftmcu_write16 = 0x93
|
||||
};
|
||||
|
||||
// internal use only types
|
||||
|
||||
enum ftdi_mode { // combines EEPROM setting + bitmode
|
||||
ftmode_uart = 0,
|
||||
|
||||
ftmode_mpsse = 1,
|
||||
ftmode_asyncbb = 2,
|
||||
ftmode_syncbb = 4,
|
||||
ftmode_mcuhost = 8,
|
||||
|
||||
ftmode_fifo = 0x10,
|
||||
ftmode_opto = 0x20, // not implementing this here
|
||||
ftmode_cpufifo = 0x40,
|
||||
ftmode_ft1284 = 0x80, // not impl. on 2232d
|
||||
};
|
||||
|
||||
enum ftdi_flowctrl {
|
||||
ftflow_none = 0,
|
||||
ftflow_ctsrts = 1,
|
||||
ftflow_dtrdts = 2,
|
||||
ftflow_xonxoff = 4,
|
||||
};
|
||||
|
||||
struct ftdi_interface {
|
||||
// TODO soft fields maybe, because it's a mess with lots of padding right now
|
||||
int index;
|
||||
|
||||
uint8_t modem_mask;
|
||||
uint8_t modem_data;
|
||||
|
||||
enum ftdi_flowctrl flow;
|
||||
|
||||
uint32_t baudrate;
|
||||
enum ftdi_sio_lineprop lineprop;
|
||||
|
||||
enum ftdi_sio_modemstat modemstat;
|
||||
|
||||
uint8_t eventchar, errorchar;
|
||||
enum { eventchar_enable = 1<<0, errorchar_enable = 1<<1 } charen;
|
||||
|
||||
uint8_t latency; // latency timer. TODO: implement this
|
||||
|
||||
uint8_t bb_dir; // high/1 bit = output, 0=input
|
||||
enum ftdi_sio_bitmode bb_mode;
|
||||
|
||||
uint16_t mcu_addr_latch;
|
||||
|
||||
// these are for USB bulk cmds etc.
|
||||
// "write" means write to hardware output pins
|
||||
// "read" means read from hardware input pins
|
||||
uint8_t writebuf[CFG_TUD_VENDOR_RX_BUFSIZE];
|
||||
uint8_t readbuf [CFG_TUD_VENDOR_TX_BUFSIZE];
|
||||
uint8_t bufbuf [CFG_TUD_VENDOR_RX_BUFSIZE]; // for buffered IO
|
||||
uint32_t rxavail, rxpos;
|
||||
};
|
||||
|
||||
extern struct ftdi_interface ftdi_ifa, ftdi_ifb;
|
||||
|
||||
// interface control stuff
|
||||
|
||||
static inline enum ftdi_mode ftdi_get_mode_of(enum ftdi_sio_bitmode bb_mode, uint8_t eepmode) {
|
||||
if (bb_mode == 0x10) return ftmode_opto;
|
||||
|
||||
if (bb_mode == sio_mode_reset) {
|
||||
return (eepmode << 4) & 0xf0;
|
||||
} else return bb_mode & 0xf;
|
||||
}
|
||||
static inline enum ftdi_mode ftdi_if_get_mode(struct ftdi_interface* itf) {
|
||||
return ftdi_get_mode_of(itf->bb_mode, itf->index ? FTDI_EEP_IFB_MODE : FTDI_EEP_IFA_MODE);
|
||||
}
|
||||
uint32_t ftdi_if_decode_baudrate(uint32_t enc_brate);
|
||||
|
||||
// control request stuff. implemented by bsp driver
|
||||
void ftdi_if_init(struct ftdi_interface* itf);
|
||||
void ftdi_if_deinit(struct ftdi_interface* itf);
|
||||
|
||||
void ftdi_if_sio_reset(struct ftdi_interface* itf);
|
||||
void ftdi_if_sio_tciflush(struct ftdi_interface* itf);
|
||||
void ftdi_if_sio_tcoflush(struct ftdi_interface* itf);
|
||||
void ftdi_if_set_modemctrl(struct ftdi_interface* itf, uint8_t mask, uint8_t data);
|
||||
void ftdi_if_set_flowctrl(struct ftdi_interface* itf, enum ftdi_flowctrl flow);
|
||||
void ftdi_if_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
||||
void ftdi_if_set_lineprop(struct ftdi_interface* itf, enum ftdi_sio_lineprop lineprop);
|
||||
enum ftdi_sio_modemstat ftdi_if_poll_modemstat(struct ftdi_interface* itf);
|
||||
void ftdi_if_set_eventchar(struct ftdi_interface* itf, bool enable, uint8_t evchar);
|
||||
void ftdi_if_set_errorchar(struct ftdi_interface* itf, bool enable, uint8_t erchar);
|
||||
void ftdi_if_set_latency(struct ftdi_interface* itf, uint8_t latency);
|
||||
uint8_t ftdi_if_get_latency(struct ftdi_interface* itf);
|
||||
void ftdi_if_set_bitbang(struct ftdi_interface* itf, uint8_t dirmask, enum ftdi_sio_bitmode,
|
||||
uint8_t olddir, enum ftdi_sio_bitmode oldmode);
|
||||
uint8_t ftdi_if_read_pins(struct ftdi_interface* itf);
|
||||
|
||||
// bulk commands (also implemented by bsp driver)
|
||||
|
||||
// "write" means write to hardware output pins
|
||||
// "read" means read from hardware input pins
|
||||
void ftdi_if_uart_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
|
||||
size_t ftdi_if_uart_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
|
||||
|
||||
void ftdi_if_fifo_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
|
||||
size_t ftdi_if_fifo_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
|
||||
void ftdi_if_cpufifo_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
|
||||
size_t ftdi_if_cpufifo_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
|
||||
|
||||
void ftdi_if_asyncbb_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
|
||||
size_t ftdi_if_asyncbb_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
|
||||
void ftdi_if_syncbb_write (struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
|
||||
size_t ftdi_if_syncbb_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
|
||||
|
||||
|
||||
void ftdi_if_mpsse_flush(struct ftdi_interface* itf);
|
||||
void ftdi_if_mpsse_wait_io(struct ftdi_interface* itf, bool level);
|
||||
|
||||
void ftdi_if_mpsse_set_dirval_lo(struct ftdi_interface* itf, uint8_t dir, uint8_t val);
|
||||
void ftdi_if_mpsse_set_dirval_hi(struct ftdi_interface* itf, uint8_t dir, uint8_t val);
|
||||
uint8_t ftdi_if_mpsse_read_lo(struct ftdi_interface* itf);
|
||||
uint8_t ftdi_if_mpsse_read_hi(struct ftdi_interface* itf);
|
||||
void ftdi_if_mpsse_loopback(struct ftdi_interface* itf, bool enable);
|
||||
void ftdi_if_mpsse_set_clkdiv(struct ftdi_interface* itf, uint16_t div);
|
||||
|
||||
uint8_t ftdi_if_mpsse_xfer_bits(struct ftdi_interface* itf, int flags, size_t nbits, uint8_t value);
|
||||
void ftdi_if_mpsse_xfer_bytes(struct ftdi_interface* itf, int flags, size_t nbytes, uint8_t* dst, const uint8_t* src);
|
||||
uint8_t ftdi_if_mpsse_tms_xfer(struct ftdi_interface* itf, int flags, size_t nbits, uint8_t value);
|
||||
|
||||
void ftdi_if_mpsse_div5(struct ftdi_interface* itf, bool enable);
|
||||
void ftdi_if_mpsse_data_3ph(struct ftdi_interface* itf, bool enable);
|
||||
void ftdi_if_mpsse_adaptive(struct ftdi_interface* itf, bool enable);
|
||||
|
||||
void ftdi_if_mpsse_clockonly(struct ftdi_interface* itf, uint32_t cycles);
|
||||
void ftdi_if_mpsse_clock_wait_io(struct ftdi_interface* itf, bool level);
|
||||
void ftdi_if_mpsse_clockonly_wait_io(struct ftdi_interface* itf, bool level, uint32_t cycles);
|
||||
void ftdi_if_mpsse_hi_is_tristate(struct ftdi_interface* itf, uint16_t pinmask);
|
||||
|
||||
|
||||
void ftdi_if_mcuhost_flush(struct ftdi_interface* itf);
|
||||
void ftdi_if_mcuhost_wait_io(struct ftdi_interface* itf, bool level);
|
||||
|
||||
uint8_t ftdi_if_mcuhost_read8 (struct ftdi_interface* itf, uint8_t addr);
|
||||
uint8_t ftdi_if_mcuhost_read16(struct ftdi_interface* itf, uint16_t addr);
|
||||
void ftdi_if_mcuhost_write8 (struct ftdi_interface* itf, uint8_t addr, uint8_t value);
|
||||
void ftdi_if_mcuhost_write16(struct ftdi_interface* itf, uint16_t addr, uint8_t value);
|
||||
|
||||
void ftdi_if_uart_init(struct ftdi_interface* itf);
|
||||
void ftdi_if_mpsse_init(struct ftdi_interface* itf);
|
||||
void ftdi_if_asyncbb_init(struct ftdi_interface* itf);
|
||||
void ftdi_if_syncbb_init(struct ftdi_interface* itf);
|
||||
void ftdi_if_mcuhost_init(struct ftdi_interface* itf);
|
||||
void ftdi_if_fifo_init(struct ftdi_interface* itf);
|
||||
void ftdi_if_cpufifo_init(struct ftdi_interface* itf);
|
||||
|
||||
void ftdi_if_uart_deinit(struct ftdi_interface* itf);
|
||||
void ftdi_if_mpsse_deinit(struct ftdi_interface* itf);
|
||||
void ftdi_if_asyncbb_deinit(struct ftdi_interface* itf);
|
||||
void ftdi_if_syncbb_deinit(struct ftdi_interface* itf);
|
||||
void ftdi_if_mcuhost_deinit(struct ftdi_interface* itf);
|
||||
void ftdi_if_fifo_deinit(struct ftdi_interface* itf);
|
||||
void ftdi_if_cpufifo_deinit(struct ftdi_interface* itf);
|
||||
|
||||
void ftdi_if_uart_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
||||
void ftdi_if_mpsse_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
||||
void ftdi_if_asyncbb_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
||||
void ftdi_if_syncbb_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
||||
void ftdi_if_mcuhost_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
||||
void ftdi_if_fifo_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
||||
void ftdi_if_cpufifo_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
#include "bsp/board.h"
|
||||
#include "bsp-feature.h"
|
||||
#include "tusb.h"
|
||||
#include "thread.h"
|
||||
#include "ftdi.h"
|
||||
#include "usbstdio.h"
|
||||
|
||||
static cothread_t ifathread, ifbthread;
|
||||
static uint8_t ifastack[0x800], ifbstack[0x800];
|
||||
|
||||
static void ifa_fn(void) {
|
||||
while (1) {
|
||||
ftdi_task_ifa();
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
|
||||
static void ifb_fn(void) {
|
||||
while (1) {
|
||||
ftdi_task_ifb();
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
board_init();
|
||||
|
||||
tusb_init();
|
||||
|
||||
// TODO: to main
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
stdio_usb_set_itf_num(CDC_N_STDIO);
|
||||
#endif
|
||||
ftdi_init();
|
||||
|
||||
thread_init();
|
||||
|
||||
ifathread = co_derive(ifastack, sizeof ifastack, ifa_fn);
|
||||
ifbthread = co_derive(ifbstack, sizeof ifbstack, ifb_fn);
|
||||
|
||||
gpio_init(25);
|
||||
gpio_set_function(25,GPIO_FUNC_SIO);
|
||||
gpio_set_dir(25,true);
|
||||
|
||||
while (1) {
|
||||
tud_task(); // tinyusb device task
|
||||
thread_enter(ifathread);
|
||||
/*tud_task(); // tinyusb device task
|
||||
thread_enter(ifbthread);*/
|
||||
|
||||
/*if (tud_vendor_n_mounted(VND_N_FTDI_IFA)) {
|
||||
tud_task();
|
||||
}*/
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// vim: set et:
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "thread.h"
|
||||
|
||||
extern uint32_t co_active_buffer[64];
|
||||
uint32_t co_active_buffer[64];
|
||||
extern cothread_t co_active_handle;
|
||||
cothread_t co_active_handle;
|
||||
|
||||
static cothread_t mainthread;
|
||||
|
||||
static cothread_t threadarr[16]; /* 16 nested threads should be enough... */
|
||||
static size_t threadind;
|
||||
|
||||
void thread_init(void) {
|
||||
memset(threadarr, 0, sizeof threadarr);
|
||||
|
||||
mainthread = co_active();
|
||||
threadarr[0] = mainthread;
|
||||
threadind = 0;
|
||||
}
|
||||
|
||||
void thread_yield(void) {
|
||||
/*cothread_t newthrd = threadarr[threadind];
|
||||
if (threadind > 0) --threadind;*/
|
||||
|
||||
co_switch(mainthread/*newthrd*/);
|
||||
}
|
||||
|
||||
void thread_enter(cothread_t thrid) {
|
||||
/*if (threadind + 1 == sizeof(threadarr) / sizeof(threadarr[0])) {
|
||||
// TODO: PANIC!
|
||||
}
|
||||
|
||||
threadarr[threadind] = co_active();
|
||||
++threadind;*/
|
||||
|
||||
co_switch(thrid);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// vim: set et:
|
||||
|
||||
#ifndef THREAD_H_
|
||||
#define THREAD_H_
|
||||
|
||||
#include <libco.h>
|
||||
|
||||
#define THREAD_STACK_SIZE 512
|
||||
|
||||
void thread_init (void);
|
||||
void thread_yield(void);
|
||||
|
||||
/* thread_enter + thread_yield can be used to "call" threads in a stack-like
|
||||
* way, much like functions. this is needed because vnd_cfg might call mode
|
||||
* stuff which might do stuff in its own tasks and so on. */
|
||||
void thread_enter(cothread_t thrid);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// defined by board.mk
|
||||
#ifndef CFG_TUSB_MCU
|
||||
#error CFG_TUSB_MCU must be defined
|
||||
#endif
|
||||
|
||||
// RHPort number used for device can be defined by board.mk, default to port 0
|
||||
#ifndef BOARD_DEVICE_RHPORT_NUM
|
||||
#define BOARD_DEVICE_RHPORT_NUM 0
|
||||
#endif
|
||||
|
||||
// RHPort max operational speed can defined by board.mk
|
||||
// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed
|
||||
#ifndef BOARD_DEVICE_RHPORT_SPEED
|
||||
#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \
|
||||
CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56)
|
||||
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED
|
||||
#else
|
||||
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Device mode with rhport and speed defined by board.mk
|
||||
#if BOARD_DEVICE_RHPORT_NUM == 0
|
||||
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||
#elif BOARD_DEVICE_RHPORT_NUM == 1
|
||||
#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||
#else
|
||||
#error "Incorrect RHPort configuration"
|
||||
#endif
|
||||
|
||||
// This example doesn't use an RTOS
|
||||
#define CFG_TUSB_OS OPT_OS_PICO
|
||||
|
||||
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
|
||||
// #define CFG_TUSB_DEBUG 0
|
||||
|
||||
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
|
||||
* Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||
* into those specific section.
|
||||
* e.g
|
||||
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||
*/
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
#define CFG_TUD_CDC 1
|
||||
#else
|
||||
#define CFG_TUD_CDC 0
|
||||
#endif
|
||||
#define CFG_TUD_MSC 0
|
||||
#define CFG_TUD_HID 0
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 2
|
||||
#define CFG_TUD_NET 0
|
||||
|
||||
#define CFG_TUD_HID_EP_BUFSIZE 64
|
||||
|
||||
// CDC FIFO size of TX and RX
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#define CFG_TUD_VENDOR_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#define CFG_TUD_VENDOR_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* 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 "tusb.h"
|
||||
#include "util.h"
|
||||
#include "ftdi.h"
|
||||
#include "bsp-feature.h"
|
||||
#include "usbstdio.h"
|
||||
|
||||
// String Descriptor Index
|
||||
enum
|
||||
{
|
||||
STRID_LANGID = 0,
|
||||
STRID_MANUFACTURER,
|
||||
STRID_PRODUCT,
|
||||
STRID_SERIAL,
|
||||
|
||||
STRID_CONFIG,
|
||||
|
||||
STRID_IF_VND_FTDI_IFA,
|
||||
STRID_IF_VND_FTDI_IFB,
|
||||
STRID_IF_CDC_STDIO,
|
||||
};
|
||||
|
||||
enum {
|
||||
ITF_NUM_VND_FTDI_IFA,
|
||||
ITF_NUM_VND_FTDI_IFB,
|
||||
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
ITF_NUM_CDC_STDIO_COM,
|
||||
ITF_NUM_CDC_STDIO_DATA,
|
||||
#endif
|
||||
|
||||
ITF_NUM__TOTAL
|
||||
};
|
||||
enum {
|
||||
CONFIG_TOTAL_LEN
|
||||
= TUD_CONFIG_DESC_LEN
|
||||
+ TUD_VENDOR_DESC_LEN
|
||||
+ TUD_VENDOR_DESC_LEN
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
+ TUD_CDC_DESC_LEN
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
tusb_desc_device_t const desc_device =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0110,
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = 0x0403,
|
||||
.idProduct = 0x6010,
|
||||
.bcdDevice = 0x0500,
|
||||
|
||||
.iManufacturer = STRID_MANUFACTURER,
|
||||
.iProduct = STRID_PRODUCT,
|
||||
.iSerialNumber = STRID_SERIAL,
|
||||
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const * tud_descriptor_device_cb(void)
|
||||
{
|
||||
return (uint8_t const *) &desc_device;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define EPNUM_VND_FTDI_IFA_OUT 0x02
|
||||
#define EPNUM_VND_FTDI_IFA_IN 0x81
|
||||
#define EPNUM_VND_FTDI_IFB_OUT 0x04
|
||||
#define EPNUM_VND_FTDI_IFB_IN 0x83
|
||||
|
||||
#define EPNUM_CDC_STDIO_OUT 0x05
|
||||
#define EPNUM_CDC_STDIO_IN 0x85
|
||||
#define EPNUM_CDC_STDIO_NOTIF 0x86
|
||||
|
||||
uint8_t const desc_configuration[] =
|
||||
{
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM__TOTAL, STRID_CONFIG, CONFIG_TOTAL_LEN,
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_NUM_VND_FTDI_IFA, STRID_IF_VND_FTDI_IFA,
|
||||
EPNUM_VND_FTDI_IFA_OUT, EPNUM_VND_FTDI_IFA_IN, CFG_TUD_VENDOR_RX_BUFSIZE),
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_NUM_VND_FTDI_IFB, STRID_IF_VND_FTDI_IFB,
|
||||
EPNUM_VND_FTDI_IFB_OUT, EPNUM_VND_FTDI_IFB_IN, CFG_TUD_VENDOR_RX_BUFSIZE),
|
||||
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_STDIO_COM, STRID_IF_CDC_STDIO, EPNUM_CDC_STDIO_NOTIF,
|
||||
CFG_TUD_CDC_RX_BUFSIZE, EPNUM_CDC_STDIO_OUT, EPNUM_CDC_STDIO_IN, CFG_TUD_CDC_RX_BUFSIZE),
|
||||
#endif
|
||||
};
|
||||
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void) index; // for multiple configurations
|
||||
return desc_configuration;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// String Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// array of pointer to string descriptors
|
||||
char const* string_desc_arr [] =
|
||||
{
|
||||
[STRID_LANGID] = (const char[]) { 0x09, 0x04 }, // supported language is English (0x0409)
|
||||
[STRID_MANUFACTURER] = "not-FTDI", // Manufacturer
|
||||
[STRID_PRODUCT] = "not-FT2232D", // Product
|
||||
[STRID_CONFIG] = "Configuration descriptor",
|
||||
// max string length check: |||||||||||||||||||||||||||||||
|
||||
[STRID_IF_VND_FTDI_IFA] = "FT2232D interface A",
|
||||
[STRID_IF_VND_FTDI_IFB] = "FT2232D interface B",
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
[STRID_IF_CDC_STDIO] = "stdio CDC interface (debug)",
|
||||
#endif
|
||||
};
|
||||
static uint16_t _desc_str[32];
|
||||
|
||||
// Invoked when received GET STRING DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
(void) langid;
|
||||
|
||||
uint8_t chr_count = 0;
|
||||
|
||||
if (STRID_LANGID == index)
|
||||
{
|
||||
memcpy(&_desc_str[1], string_desc_arr[STRID_LANGID], 2);
|
||||
chr_count = 1;
|
||||
} else if (STRID_SERIAL == index)
|
||||
{
|
||||
chr_count = get_unique_id_u16(_desc_str + 1);
|
||||
} else
|
||||
{
|
||||
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
|
||||
|
||||
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
|
||||
|
||||
const char* str = string_desc_arr[index];
|
||||
|
||||
// Cap at max char
|
||||
chr_count = TU_MIN(strlen(str), 31);
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for(uint8_t i=0; i<chr_count; i++)
|
||||
{
|
||||
_desc_str[1+i] = str[i];
|
||||
}
|
||||
}
|
||||
|
||||
// first byte is length (including header), second byte is string type
|
||||
_desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
|
||||
|
||||
return _desc_str;
|
||||
}
|
||||
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) {
|
||||
switch (itf) {
|
||||
case CDC_N_STDIO:
|
||||
stdio_usb_line_coding_cb(line_coding);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t ep_addr,
|
||||
tusb_control_request_t const* req) {
|
||||
return ftdi_control_xfer_cb(rhport, ep_addr, req);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
#ifndef USBSTDIO_H_
|
||||
#define USBSTDIO_H_
|
||||
|
||||
#include "tusb_config.h"
|
||||
#include <tusb.h>
|
||||
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
void stdio_usb_init(void);
|
||||
|
||||
void stdio_usb_set_itf_num(int itf);
|
||||
|
||||
void stdio_usb_line_coding_cb(cdc_line_coding_t const* line_coding);
|
||||
#endif
|
||||
|
||||
void stdio_uart_init(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// vim: set et:
|
||||
|
||||
#ifndef UTIL_H_
|
||||
#define UTIL_H_
|
||||
|
||||
static inline char nyb2hex(int x) {
|
||||
if (x < 0xa)
|
||||
return '0' + (x - 0x0);
|
||||
else
|
||||
return 'A' + (x - 0xa);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
uint8_t get_unique_id_u8 (uint8_t * desc_str);
|
||||
uint8_t get_unique_id_u16(uint16_t* desc_str);
|
||||
// clang-format on
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue