diff --git a/flip_tdi/.catalog/README.md b/flip_tdi/.catalog/README.md new file mode 100644 index 0000000..efec847 --- /dev/null +++ b/flip_tdi/.catalog/README.md @@ -0,0 +1,7 @@ +# FlipTDI + +This is a FT232H device emulation application + +## Usage + +Сonnect Flipper to your computer and open the application, a new FT232H compatible device will appear in the system diff --git a/flip_tdi/.catalog/changelog.md b/flip_tdi/.catalog/changelog.md new file mode 100644 index 0000000..b6c1b56 --- /dev/null +++ b/flip_tdi/.catalog/changelog.md @@ -0,0 +1,2 @@ +## 1.0 + - Initial release diff --git a/flip_tdi/.catalog/screenshots/1.png b/flip_tdi/.catalog/screenshots/1.png new file mode 100644 index 0000000..955105f Binary files /dev/null and b/flip_tdi/.catalog/screenshots/1.png differ diff --git a/flip_tdi/.catalog/screenshots/2.png b/flip_tdi/.catalog/screenshots/2.png new file mode 100644 index 0000000..0ee7472 Binary files /dev/null and b/flip_tdi/.catalog/screenshots/2.png differ diff --git a/flip_tdi/.catalog/screenshots/3.png b/flip_tdi/.catalog/screenshots/3.png new file mode 100644 index 0000000..c936f12 Binary files /dev/null and b/flip_tdi/.catalog/screenshots/3.png differ diff --git a/flip_tdi/.catalog/screenshots/4.png b/flip_tdi/.catalog/screenshots/4.png new file mode 100644 index 0000000..2503f9e Binary files /dev/null and b/flip_tdi/.catalog/screenshots/4.png differ diff --git a/flip_tdi/application.fam b/flip_tdi/application.fam new file mode 100644 index 0000000..6ef58ab --- /dev/null +++ b/flip_tdi/application.fam @@ -0,0 +1,17 @@ +App( + appid="flip_tdi", + name="FlipTDI", + apptype=FlipperAppType.EXTERNAL, + entry_point="flip_tdi_app", + requires=[ + "gui", + "dialogs", + ], + stack_size=2 * 1024, + fap_description="Flipper FTDI232H emulator.", + fap_version="1.0", + fap_icon="flip_tdi_icon_10px.png", + fap_category="USB", + fap_icon_assets="images", + fap_author="SkorP", +) diff --git a/flip_tdi/flip_tdi_app.c b/flip_tdi/flip_tdi_app.c new file mode 100644 index 0000000..f98604c --- /dev/null +++ b/flip_tdi/flip_tdi_app.c @@ -0,0 +1,111 @@ +#include +#include "flip_tdi_app_i.h" + +static bool flip_tdi_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + FlipTDIApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool flip_tdi_app_back_event_callback(void* context) { + furi_assert(context); + FlipTDIApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void flip_tdi_app_tick_event_callback(void* context) { + furi_assert(context); + FlipTDIApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +FlipTDIApp* flip_tdi_app_alloc() { + FlipTDIApp* app = malloc(sizeof(FlipTDIApp)); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&flip_tdi_scene_handlers, app); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, flip_tdi_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, flip_tdi_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, flip_tdi_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // SubMenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, FlipTDIViewSubmenu, submenu_get_view(app->submenu)); + + // Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, FlipTDIViewWidget, widget_get_view(app->widget)); + + // Field Presence + app->flip_tdi_view_main_instance = flip_tdi_view_main_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FlipTDIViewMain, + flip_tdi_view_main_get_view(app->flip_tdi_view_main_instance)); + + // FTDI emulation Start + flip_tdi_start(app); + + scene_manager_next_scene(app->scene_manager, FlipTDISceneMain); + + return app; +} + +void flip_tdi_app_free(FlipTDIApp* app) { + furi_assert(app); + + // FTDI emulation Stop + flip_tdi_stop(app); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, FlipTDIViewSubmenu); + submenu_free(app->submenu); + + // Widget + view_dispatcher_remove_view(app->view_dispatcher, FlipTDIViewWidget); + widget_free(app->widget); + + // FlipTDIViewMain + view_dispatcher_remove_view(app->view_dispatcher, FlipTDIViewMain); + flip_tdi_view_main_free(app->flip_tdi_view_main_instance); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Close records + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t flip_tdi_app(void* p) { + UNUSED(p); + FlipTDIApp* flip_tdi_app = flip_tdi_app_alloc(); + + view_dispatcher_run(flip_tdi_app->view_dispatcher); + + flip_tdi_app_free(flip_tdi_app); + + return 0; +} \ No newline at end of file diff --git a/flip_tdi/flip_tdi_app_i.c b/flip_tdi/flip_tdi_app_i.c new file mode 100644 index 0000000..4a37fa2 --- /dev/null +++ b/flip_tdi/flip_tdi_app_i.c @@ -0,0 +1,17 @@ +#include "flip_tdi_app_i.h" + +#include + +#define TAG "FlipTDI" + +void flip_tdi_start(FlipTDIApp* app) { + furi_assert(app); + + app->ftdi_usb = ftdi_usb_start(); +} + +void flip_tdi_stop(FlipTDIApp* app) { + furi_assert(app); + + ftdi_usb_stop(app->ftdi_usb); +} diff --git a/flip_tdi/flip_tdi_app_i.h b/flip_tdi/flip_tdi_app_i.h new file mode 100644 index 0000000..907cfc3 --- /dev/null +++ b/flip_tdi/flip_tdi_app_i.h @@ -0,0 +1,34 @@ +#pragma once + +#include "helpers/flip_tdi_types.h" +#include "helpers/flip_tdi_event.h" + +#include "scenes/flip_tdi_scene.h" +#include +#include +#include +#include +#include +#include +#include "views/flip_tdi_view_main.h" +#include + +#include "helpers/ftdi_usb.h" + + +typedef struct FlipTDIApp FlipTDIApp; + +struct FlipTDIApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + Submenu* submenu; + Widget* widget; + FlipTDIViewMainType* flip_tdi_view_main_instance; + + FtdiUsb* ftdi_usb; +}; + +void flip_tdi_start(FlipTDIApp* app); +void flip_tdi_stop(FlipTDIApp* app); \ No newline at end of file diff --git a/flip_tdi/flip_tdi_icon_10px.png b/flip_tdi/flip_tdi_icon_10px.png new file mode 100644 index 0000000..3ea227b Binary files /dev/null and b/flip_tdi/flip_tdi_icon_10px.png differ diff --git a/flip_tdi/helpers/flip_tdi_event.h b/flip_tdi/helpers/flip_tdi_event.h new file mode 100644 index 0000000..a2caff6 --- /dev/null +++ b/flip_tdi/helpers/flip_tdi_event.h @@ -0,0 +1,14 @@ +#pragma once + +typedef enum { + //SubmenuIndex + SubmenuIndexWiringUart = 10, + SubmenuIndexWiringSpi, + SubmenuIndexWiringGpio, + SubmenuIndexAbout, + + //FlipTDICustomEvent + FlipTDICustomEventStartId = 100, + FlipTDICustomEventMainMore, + +} FlipTDICustomEvent; diff --git a/flip_tdi/helpers/flip_tdi_types.h b/flip_tdi/helpers/flip_tdi_types.h new file mode 100644 index 0000000..b1907ba --- /dev/null +++ b/flip_tdi/helpers/flip_tdi_types.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#define FLIP_TDI_DEVELOPED "SkorP" +#define FLIP_TDI_GITHUB "https://github.com/flipperdevices/flipperzero-good-faps" + +typedef enum { + FlipTDIViewVariableItemList, + FlipTDIViewSubmenu, + FlipTDIViewMain, + FlipTDIViewWidget, +} FlipTDIView; diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c new file mode 100644 index 0000000..cbaef9d --- /dev/null +++ b/flip_tdi/helpers/ftdi.c @@ -0,0 +1,321 @@ +#include "ftdi.h" +#include "furi.h" +#include "ftdi_uart.h" +#include "ftdi_bitbang.h" +#include "ftdi_latency_timer.h" + +#define TAG "FTDI" + +#define FTDI_TX_RX_BUF_SIZE (4096UL) +#define FTDI_INTERFACE_A (0x01UL) +#define FTDI_DRIVER_INTERFACE_A (0x00UL) +#define FTDI_UART_MAX_TX_SIZE (64UL) + +struct Ftdi { + FtdiModemStatus status; + FuriStreamBuffer* stream_rx; + FuriStreamBuffer* stream_tx; + uint32_t baudrate; + uint32_t bitband_speed; + FtdiDataConfig data_config; + FtdiBitMode bit_mode; + uint8_t bit_mode_mask; + + FtdiUart* ftdi_uart; + FtdiBitbang* ftdi_bitbang; + FtdiLatencyTimer* ftdi_latency_timer; + + FtdiCallbackTxImmediate callback_tx_immediate; + void* context_tx_immediate; +}; + +static bool ftdi_check_interface(Ftdi* ftdi, uint16_t index) { + UNUSED(ftdi); + uint8_t interface = index & 0xff; + return ((interface) == FTDI_INTERFACE_A) || ((interface) == FTDI_DRIVER_INTERFACE_A); +} + +static void ftdi_callback_tx_immediate(void* context) { + Ftdi* ftdi = context; + if(ftdi->callback_tx_immediate) { + ftdi->callback_tx_immediate(ftdi->context_tx_immediate); + } +} + +Ftdi* ftdi_alloc(void) { + Ftdi* ftdi = malloc(sizeof(Ftdi)); + ftdi->stream_rx = + furi_stream_buffer_alloc(sizeof(uint8_t) * FTDI_TX_RX_BUF_SIZE, sizeof(uint8_t)); + ftdi->stream_tx = + furi_stream_buffer_alloc(sizeof(uint8_t) * FTDI_TX_RX_BUF_SIZE, sizeof(uint8_t)); + ftdi->baudrate = 115200; + + FtdiModemStatus status = {0}; + ftdi->status = status; + ftdi->status.RESERVED1 = 1; + ftdi->status.TEMT = 1; + ftdi->status.THRE = 1; + + ftdi->ftdi_uart = ftdi_uart_alloc(ftdi); + ftdi->ftdi_bitbang = ftdi_bitbang_alloc(ftdi); + ftdi->ftdi_latency_timer = ftdi_latency_timer_alloc(); + + ftdi_latency_timer_set_callback(ftdi->ftdi_latency_timer, ftdi_callback_tx_immediate, ftdi); + return ftdi; +} + +void ftdi_free(Ftdi* ftdi) { + ftdi_uart_free(ftdi->ftdi_uart); + ftdi_bitbang_free(ftdi->ftdi_bitbang); + ftdi_latency_timer_free(ftdi->ftdi_latency_timer); + furi_stream_buffer_free(ftdi->stream_tx); + furi_stream_buffer_free(ftdi->stream_rx); + free(ftdi); +} + +void ftdi_switch_callback_tx_immediate(Ftdi* ftdi) { + FtdiMpsse* mpsse_handle = ftdi_bitbang_get_mpsse_handle(ftdi->ftdi_bitbang); + if(ftdi->bit_mode.MPSSE) { + ftdi_mpsse_gpio_set_callback(mpsse_handle, ftdi_callback_tx_immediate, ftdi); + ftdi_latency_timer_set_callback(ftdi->ftdi_latency_timer, NULL, NULL); + ftdi_latency_timer_enable(ftdi->ftdi_latency_timer, false); + } else { + ftdi_mpsse_gpio_set_callback(mpsse_handle, NULL, NULL); + ftdi_latency_timer_enable(ftdi->ftdi_latency_timer, true); + ftdi_latency_timer_set_callback( + ftdi->ftdi_latency_timer, ftdi_callback_tx_immediate, ftdi); + } +} + +void ftdi_set_callback_tx_immediate(Ftdi* ftdi, FtdiCallbackTxImmediate callback, void* context) { + ftdi->callback_tx_immediate = callback; + ftdi->context_tx_immediate = context; +} + +void ftdi_reset_purge_rx(Ftdi* ftdi) { + furi_stream_buffer_reset(ftdi->stream_rx); +} + +void ftdi_reset_purge_tx(Ftdi* ftdi) { + furi_stream_buffer_reset(ftdi->stream_tx); +} + +void ftdi_reset_sio(Ftdi* ftdi) { + UNUSED(ftdi); +} + +uint32_t ftdi_set_tx_buf(Ftdi* ftdi, const uint8_t* data, uint32_t size) { + uint32_t len = furi_stream_buffer_spaces_available(ftdi->stream_tx); + + if(len < size) { + size = len; + //ToDo: set error + //ftdi->status.DR = 1; + FURI_LOG_E(TAG, "FTDI TX buffer overflow"); + } + + return furi_stream_buffer_send(ftdi->stream_tx, data, size, 0); +} + +uint32_t ftdi_get_tx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size) { + return furi_stream_buffer_receive(ftdi->stream_tx, data, size, 0); +} + +uint32_t ftdi_available_tx_buf(Ftdi* ftdi) { + return furi_stream_buffer_bytes_available(ftdi->stream_tx); +} + +uint32_t ftdi_set_rx_buf(Ftdi* ftdi, const uint8_t* data, uint32_t size) { + uint32_t len = furi_stream_buffer_spaces_available(ftdi->stream_rx); + + if(len < size) { + size = len; + //Todo: set error + FURI_LOG_E(TAG, "FTDI RX buffer overflow"); + } + return furi_stream_buffer_send(ftdi->stream_rx, data, size, 0); +} + +uint32_t ftdi_get_rx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size, uint32_t timeout) { + return furi_stream_buffer_receive(ftdi->stream_rx, data, size, timeout); +} + +uint32_t ftdi_available_rx_buf(Ftdi* ftdi) { + return furi_stream_buffer_bytes_available(ftdi->stream_rx); +} + +void ftdi_loopback(Ftdi* ftdi) { + //todo fix size + uint8_t data[128]; + uint32_t len = ftdi_get_rx_buf(ftdi, data, 128, 0); + ftdi_set_tx_buf(ftdi, data, len); +} + +/* +https://www.ftdichip.com/Documents/AppNotes/AN_120_Aliasing_VCP_Baud_Rates.pdf + +Note: Divisor = 1 and Divisor = 0 are special cases. A divisor of 0 will give 12MBaud, and a +divisor of 1 will give 8MBaud. Sub-integer divisors are not allowed if the main divisor (n) is +either 0 or 1. + Divisor = 0 -> clk/1 + Divisor = 1 -> clk/1.5 + Divisor = 2 -> clk/2 + +To alias baud rates between 3MBaud and 12MBaud it is necessary to use driver version 2.4.20 or later +and the most significant bit (MSB) of the divisor must be a 1. This will ensure the divisor is dividing a +12MHz clock and not a 3MHz clock. +Example: +Each field consists of 4 bytes, ordered as follows: Byte0, Byte1, Byte2, Byte3. Bits 13 +through 0 denote the integer divisor while bits 16, 15 and 14 denote the sub-integer divisor, as +follows +16,15,14 = 000 - sub-integer divisor = 0 +16,15,14 = 001 - sub-integer divisor = 0.5 +16,15,14 = 010 - sub-integer divisor = 0.25 +16,15,14 = 011 - sub-integer divisor = 0.125 +16,15,14 = 100 - sub-integer divisor = 0.375 +16,15,14 = 101 - sub-integer divisor = 0.625 +16,15,14 = 110 - sub-integer divisor = 0.75 +16,15,14 = 111 - sub-integer divisor = 0.875 + +Step 1 - re-order the bytes: 35,40,01,00 => 00014035 Hex +Step 2 - extract the sub-integer divisor; 16 = 1, 15 = 0, 14 = 1 => sub-integer = 0.625 +Step 3 - extract the integer divisor: 13:0 = 0035 Hex = 53 Dec +Step 4 - combine the integer and sub-integer divisors: 53.625 Dec +Step 5 - divide 3000000 by the divisor => 3000000/53.625 = 55944 baud +*/ + +void ftdi_set_baudrate(Ftdi* ftdi, uint16_t value, uint16_t index) { + if(!ftdi_check_interface(ftdi, index)) { + return; + } + + uint32_t encoded_divisor = ((index & 0xff00) << 8) | value; + bool is_12mhz = (encoded_divisor & 0x20000) == 0x20000; + uint32_t clk = is_12mhz ? 12000000 : 3000000; + + uint32_t baudrate = 0; + float divisor[8] = {0, 0.5, 0.25, 0.125, 0.375, 0.625, 0.75, 0.875}; + uint16_t integer_divisor = encoded_divisor & 0x3FFF; + float sub_integer_divisor = divisor[(encoded_divisor >> 14) & 0x7]; + + if((sub_integer_divisor == 0) && (integer_divisor < 3)) { + if(integer_divisor == 0) { + baudrate = clk; + } else if(integer_divisor == 1) { + baudrate = (uint32_t)round((float)clk / 1.5f); + } else if(integer_divisor == 2) { + baudrate = clk / 2; + } + } else { + baudrate = + round((float)clk / ((float)integer_divisor + divisor[(encoded_divisor >> 14) & 0x7])); + } + + ftdi->baudrate = baudrate; + if(is_12mhz) { + ftdi->bitband_speed = ftdi->baudrate * FTDI_BITBANG_BAUDRATE_RATIO_HIGH; + } else { + ftdi->bitband_speed = ftdi->baudrate * FTDI_BITBANG_BAUDRATE_RATIO_BASE; + } + + ftdi_uart_set_baudrate(ftdi->ftdi_uart, baudrate); + ftdi_bitbang_set_speed(ftdi->ftdi_bitbang, ftdi->bitband_speed); +#ifdef FTDI_DEBUG + furi_log_puts("ftdi_set_baudrate="); + char tmp_str2[] = "4294967295"; + itoa(baudrate, tmp_str2, 10); + furi_log_puts(tmp_str2); + furi_log_puts(" bb_speed="); + itoa(ftdi->bitband_speed, tmp_str2, 10); + furi_log_puts(tmp_str2); + furi_log_puts("\r\n"); +#endif +} + +void ftdi_set_data_config(Ftdi* ftdi, uint16_t value, uint16_t index) { + if(!ftdi_check_interface(ftdi, index)) { + return; + } + memcpy(&ftdi->data_config, &value, sizeof(ftdi->data_config)); + ftdi_uart_set_data_config(ftdi->ftdi_uart, &ftdi->data_config); +} + +void ftdi_set_flow_ctrl(Ftdi* ftdi, uint16_t index) { + if(!ftdi_check_interface(ftdi, index)) { + return; + } + uint16_t flow_ctrl = index & 0xFF00; + if(flow_ctrl != FtdiFlowControlDisable) { + //ToDo: no implement FtdiFlowControl + } +} + +void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index) { + if(!ftdi_check_interface(ftdi, index)) { + return; + } + //todo no use mask value&0xFF + ftdi->bit_mode_mask = value & 0xFF; + uint8_t bit_mode = value >> 8; + memcpy(&ftdi->bit_mode, &bit_mode, sizeof(ftdi->bit_mode)); + + // ftdi_bitbang_set_gpio(ftdi->ftdi_bitbang, 0); + ftdi_latency_timer_set_callback(ftdi->ftdi_latency_timer, ftdi_callback_tx_immediate, ftdi); + + if(bit_mode == 0x00) { // Reset + ftdi_uart_enable(ftdi->ftdi_uart, true); // UART mode + ftdi->status.RESERVED1 = 1; + ftdi->status.CTS = 0; + ftdi->status.DTR = 0; + //todo: cts, dtr, ri, dcd is not implemented + } else { + ftdi_uart_enable(ftdi->ftdi_uart, false); + ftdi->status.CTS = 1; + ftdi->status.DTR = 1; + } + + if(ftdi->bit_mode.BITBANG || ftdi->bit_mode.SYNCBB || ftdi->bit_mode.MPSSE) { + ftdi_bitbang_set_gpio(ftdi->ftdi_bitbang, ftdi->bit_mode_mask); + ftdi_bitbang_enable(ftdi->ftdi_bitbang, ftdi->bit_mode); + } else { + ftdi_bitbang_enable(ftdi->ftdi_bitbang, ftdi->bit_mode); + } + + ftdi_switch_callback_tx_immediate(ftdi); +} + +void ftdi_set_latency_timer(Ftdi* ftdi, uint16_t value, uint16_t index) { + if(!ftdi_check_interface(ftdi, index)) { + return; + } + ftdi_latency_timer_set_speed(ftdi->ftdi_latency_timer, value); +} + +uint8_t ftdi_get_latency_timer(Ftdi* ftdi) { + return ftdi_latency_timer_get_speed(ftdi->ftdi_latency_timer); +} + +void ftdi_reset_latency_timer(Ftdi* ftdi) { + ftdi_latency_timer_reset(ftdi->ftdi_latency_timer); +} + +uint16_t* ftdi_get_modem_status_uint16_t(Ftdi* ftdi) { + return (uint16_t*)&ftdi->status; +} + +FtdiModemStatus ftdi_get_modem_status(Ftdi* ftdi) { + return ftdi->status; +} + +void ftdi_set_modem_status(Ftdi* ftdi, FtdiModemStatus status) { + ftdi->status = status; +} + +void ftdi_start_uart_tx(Ftdi* ftdi) { + ftdi_uart_tx(ftdi->ftdi_uart); +} + +uint8_t ftdi_get_bitbang_gpio(Ftdi* ftdi) { + ftdi_reset_purge_tx(ftdi); + return ftdi_bitbang_get_gpio(ftdi->ftdi_bitbang); +} diff --git a/flip_tdi/helpers/ftdi.h b/flip_tdi/helpers/ftdi.h new file mode 100644 index 0000000..91173c2 --- /dev/null +++ b/flip_tdi/helpers/ftdi.h @@ -0,0 +1,35 @@ +#pragma once +#include "ftdi_usb_define.h" + +//#define FTDI_DEBUG + +typedef struct Ftdi Ftdi; + +typedef void (*FtdiCallbackTxImmediate)(void* context); + +Ftdi* ftdi_alloc(void); +void ftdi_free(Ftdi* ftdi); +void ftdi_set_callback_tx_immediate(Ftdi* ftdi, FtdiCallbackTxImmediate callback, void* context); +void ftdi_reset_purge_rx(Ftdi* ftdi); +void ftdi_reset_purge_tx(Ftdi* ftdi); +void ftdi_reset_sio(Ftdi* ftdi); +uint32_t ftdi_set_tx_buf(Ftdi* ftdi, const uint8_t* data, uint32_t size); +uint32_t ftdi_get_tx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size); +uint32_t ftdi_available_tx_buf(Ftdi* ftdi); +uint32_t ftdi_set_rx_buf(Ftdi* ftdi, const uint8_t* data, uint32_t size); +uint32_t ftdi_get_rx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size, uint32_t timeout); +uint32_t ftdi_available_rx_buf(Ftdi* ftdi); +void ftdi_loopback(Ftdi* ftdi); +void ftdi_set_baudrate(Ftdi* ftdi, uint16_t value, uint16_t index); +void ftdi_set_data_config(Ftdi* ftdi, uint16_t value, uint16_t index); +void ftdi_set_flow_ctrl(Ftdi* ftdi, uint16_t index); +void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index); +void ftdi_set_latency_timer(Ftdi* ftdi, uint16_t value, uint16_t index); +uint8_t ftdi_get_latency_timer(Ftdi* ftdi); +void ftdi_reset_latency_timer(Ftdi* ftdi); +uint16_t* ftdi_get_modem_status_uint16_t(Ftdi* ftdi); +FtdiModemStatus ftdi_get_modem_status(Ftdi* ftdi); +void ftdi_set_modem_status(Ftdi* ftdi, FtdiModemStatus status); + +void ftdi_start_uart_tx(Ftdi* ftdi); +uint8_t ftdi_get_bitbang_gpio(Ftdi* ftdi); diff --git a/flip_tdi/helpers/ftdi_bitbang.c b/flip_tdi/helpers/ftdi_bitbang.c new file mode 100644 index 0000000..99e0dc8 --- /dev/null +++ b/flip_tdi/helpers/ftdi_bitbang.c @@ -0,0 +1,289 @@ +#pragma GCC optimize("O3") +#pragma GCC optimize("-funroll-all-loops") + +#include "ftdi_bitbang.h" +#include "furi.h" +#include +#include +#include +#include "ftdi_gpio.h" + +#define TAG "FTDI_BITBANG" + +typedef enum { + FtdiBitbangModeOff = (0UL), + FtdiBitbangModeBitbang = (1UL), + FtdiBitbangModeSyncbb = (2UL), + FtdiBitbangModeMpsse = (3UL), + FtdiBitbangModeReserved = (4UL), +} FtdiBitbangMode; + +typedef void (*FtdiBitbangGpioIO)(uint8_t state); +struct FtdiBitbang { + FuriThread* worker_thread; + Ftdi* ftdi; + FtdiMpsse* ftdi_mpsse; + uint32_t speed; + FtdiBitbangMode mode; + + uint8_t gpio_mask; + FtdiBitbangGpioIO gpio_o[8]; + uint8_t gpio_data; +}; + +typedef enum { + WorkerEventReserved = (1 << 0), + WorkerEventStop = (1 << 1), + WorkerEventTimerUpdate = (1 << 2), +} WorkerEvent; + +#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventTimerUpdate) + +static void ftdi_bitbang_tim_init(FtdiBitbang* ftdi_bitbang); +static void ftdi_bitbang_tim_deinit(FtdiBitbang* ftdi_bitbang); + +void ftdi_bitbang_gpio_set_direction(FtdiBitbang* ftdi_bitbang) { + ftdi_gpio_set_direction(ftdi_bitbang->gpio_mask); + if(ftdi_bitbang->gpio_mask & 0b00000001) { + ftdi_bitbang->gpio_o[0] = ftdi_gpio_set_b0; + } else { + ftdi_bitbang->gpio_o[0] = ftdi_gpio_set_noop; + } + + if(ftdi_bitbang->gpio_mask & 0b00000010) { + ftdi_bitbang->gpio_o[1] = ftdi_gpio_set_b1; + } else { + ftdi_bitbang->gpio_o[1] = ftdi_gpio_set_noop; + } + + if(ftdi_bitbang->gpio_mask & 0b00000100) { + ftdi_bitbang->gpio_o[2] = ftdi_gpio_set_b2; + } else { + ftdi_bitbang->gpio_o[2] = ftdi_gpio_set_noop; + } + + if(ftdi_bitbang->gpio_mask & 0b00001000) { + ftdi_bitbang->gpio_o[3] = ftdi_gpio_set_b3; + } else { + ftdi_bitbang->gpio_o[3] = ftdi_gpio_set_noop; + } + + if(ftdi_bitbang->gpio_mask & 0b00010000) { + ftdi_bitbang->gpio_o[4] = ftdi_gpio_set_b4; + } else { + ftdi_bitbang->gpio_o[4] = ftdi_gpio_set_noop; + } + + if(ftdi_bitbang->gpio_mask & 0b00100000) { + ftdi_bitbang->gpio_o[5] = ftdi_gpio_set_b5; + } else { + ftdi_bitbang->gpio_o[5] = ftdi_gpio_set_noop; + } + + if(ftdi_bitbang->gpio_mask & 0b01000000) { + ftdi_bitbang->gpio_o[6] = ftdi_gpio_set_b6; + } else { + ftdi_bitbang->gpio_o[6] = ftdi_gpio_set_noop; + } + + if(ftdi_bitbang->gpio_mask & 0b10000000) { + ftdi_bitbang->gpio_o[7] = ftdi_gpio_set_b7; + } else { + ftdi_bitbang->gpio_o[7] = ftdi_gpio_set_noop; + } +} + +void ftdi_bitbang_gpio_init(FtdiBitbang* ftdi_bitbang) { + ftdi_gpio_init(ftdi_bitbang->gpio_mask); + ftdi_bitbang_gpio_set_direction(ftdi_bitbang); +} + +static inline void ftdi_bitbang_gpio_set(FtdiBitbang* ftdi_bitbang, uint8_t gpio_data_out) { + ftdi_bitbang->gpio_o[0](gpio_data_out & 0b00000001); + ftdi_bitbang->gpio_o[1](gpio_data_out & 0b00000010); + ftdi_bitbang->gpio_o[2](gpio_data_out & 0b00000100); + ftdi_bitbang->gpio_o[3](gpio_data_out & 0b00001000); + ftdi_bitbang->gpio_o[4](gpio_data_out & 0b00010000); + ftdi_bitbang->gpio_o[5](gpio_data_out & 0b00100000); + ftdi_bitbang->gpio_o[6](gpio_data_out & 0b01000000); + ftdi_bitbang->gpio_o[7](gpio_data_out & 0b10000000); +} + +static inline uint8_t ftdi_bitbang_gpio_get(void) { + uint8_t gpio_data = 0; + gpio_data |= ftdi_gpio_get_b0(); + gpio_data |= ftdi_gpio_get_b1(); + gpio_data |= ftdi_gpio_get_b2(); + gpio_data |= ftdi_gpio_get_b3(); + gpio_data |= ftdi_gpio_get_b4(); + gpio_data |= ftdi_gpio_get_b5(); + gpio_data |= ftdi_gpio_get_b6(); + gpio_data |= ftdi_gpio_get_b7(); + return gpio_data; +} + +static int32_t ftdi_bitbang_worker(void* context) { + furi_assert(context); + FtdiBitbang* ftdi_bitbang = context; + + uint8_t buffer[64]; + + FURI_LOG_I(TAG, "Worker started"); + ftdi_bitbang_tim_init(ftdi_bitbang); + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + if(events & WorkerEventTimerUpdate) { + if(ftdi_bitbang->mode == FtdiBitbangModeMpsse) { + ftdi_mpsse_state_machine(ftdi_bitbang->ftdi_mpsse); + } else { + size_t length = ftdi_get_rx_buf(ftdi_bitbang->ftdi, buffer, 1, 0); + if(length > 0) { + ftdi_bitbang_gpio_set(ftdi_bitbang, buffer[0]); + if(ftdi_bitbang->mode == FtdiBitbangModeSyncbb) { + ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); + ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); + } + } + if(ftdi_bitbang->mode == FtdiBitbangModeBitbang) { + ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); + ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); + } + } + continue; + } + + if(events & WorkerEventStop) break; + } + ftdi_bitbang_tim_deinit(ftdi_bitbang); + + FURI_LOG_I(TAG, "Worker stopped"); + return 0; +} + +uint8_t ftdi_bitbang_get_gpio(FtdiBitbang* ftdi_bitbang) { + return ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); +} + +FtdiBitbang* ftdi_bitbang_alloc(Ftdi* ftdi) { + FtdiBitbang* ftdi_bitbang = malloc(sizeof(FtdiBitbang)); + ftdi_bitbang->ftdi = ftdi; + ftdi_bitbang->ftdi_mpsse = ftdi_mpsse_alloc(ftdi); + ftdi_bitbang->mode = FtdiBitbangModeOff; + ftdi_bitbang->speed = 10000; + ftdi_bitbang->gpio_mask = 0; + ftdi_bitbang_gpio_init(ftdi_bitbang); + ftdi_bitbang->worker_thread = + furi_thread_alloc_ex(TAG, 1024, ftdi_bitbang_worker, ftdi_bitbang); + + furi_thread_start(ftdi_bitbang->worker_thread); + return ftdi_bitbang; +} + +void ftdi_bitbang_free(FtdiBitbang* ftdi_bitbang) { + if(!ftdi_bitbang) return; + + ftdi_bitbang->mode = FtdiBitbangModeOff; + ftdi_mpsse_free(ftdi_bitbang->ftdi_mpsse); + furi_thread_flags_set(ftdi_bitbang->worker_thread, WorkerEventStop); + furi_thread_join(ftdi_bitbang->worker_thread); + furi_thread_free(ftdi_bitbang->worker_thread); + + ftdi_gpio_deinit(); + + free(ftdi_bitbang); + ftdi_bitbang = NULL; +} + +FtdiMpsse* ftdi_bitbang_get_mpsse_handle(FtdiBitbang* ftdi_bitbang) { + return ftdi_bitbang->ftdi_mpsse; +} + +void ftdi_bitbang_set_gpio(FtdiBitbang* ftdi_bitbang, uint8_t gpio_mask) { + ftdi_bitbang->gpio_mask = gpio_mask; + ftdi_bitbang_gpio_set_direction(ftdi_bitbang); +} + +void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, FtdiBitMode mode) { + if(mode.BITBANG) { + ftdi_bitbang->mode = FtdiBitbangModeBitbang; + } else if(mode.SYNCBB) { + ftdi_bitbang->mode = FtdiBitbangModeSyncbb; + } else if(mode.MPSSE) { + ftdi_bitbang->mode = FtdiBitbangModeMpsse; + } else { + ftdi_bitbang->mode = FtdiBitbangModeOff; + } + + if(ftdi_bitbang->mode != FtdiBitbangModeOff) { + LL_TIM_SetCounter(TIM17, 0); + LL_TIM_EnableCounter(TIM17); + } else { + LL_TIM_DisableCounter(TIM17); + } +} + +void ftdi_bitbang_set_speed(FtdiBitbang* ftdi_bitbang, uint32_t speed) { + UNUSED(ftdi_bitbang); + UNUSED(speed); + + //Todo limit speed + if(speed > 40000) { + speed = 40000; + } + + uint32_t freq_div = 64000000LU / speed; + uint32_t prescaler = freq_div / 0x10000LU; + uint32_t period = freq_div / (prescaler + 1); + LL_TIM_SetPrescaler(TIM17, prescaler); + LL_TIM_SetAutoReload(TIM17, period - 1); +} + +static void ftdi_bitbang_tim_isr(void* context) { + FtdiBitbang* ftdi_bitbang = context; + UNUSED(ftdi_bitbang); + if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { + LL_TIM_ClearFlag_UPDATE(TIM17); + //Speed max 40kHz + furi_thread_flags_set( + furi_thread_get_id(ftdi_bitbang->worker_thread), WorkerEventTimerUpdate); + //Todo Speed Max 70kHz + // uint8_t buffer[1]; + // size_t length = ftdi_get_rx_buf(ftdi_bitbang->ftdi, buffer, 1); + // if(length > 0) { + // ftdi_bitbang_gpio_set(ftdi_bitbang, buffer[0]); + // if(!ftdi_bitbang->async) { + // ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); + // ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); + // } + // } + // if(ftdi_bitbang->enable && ftdi_bitbang->async) { + // ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); + // ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); + // } + } +} + +static void ftdi_bitbang_tim_init(FtdiBitbang* ftdi_bitbang) { + furi_hal_bus_enable(FuriHalBusTIM17); + + LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetAutoReload(TIM17, 6400 - 1); + LL_TIM_SetPrescaler(TIM17, 0); // 10kHz + LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM17); + + furi_hal_interrupt_set_isr( + FuriHalInterruptIdTim1TrgComTim17, ftdi_bitbang_tim_isr, ftdi_bitbang); + + LL_TIM_EnableIT_UPDATE(TIM17); +} + +static void ftdi_bitbang_tim_deinit(FtdiBitbang* ftdi_bitbang) { + UNUSED(ftdi_bitbang); + LL_TIM_DisableCounter(TIM17); + furi_hal_bus_disable(FuriHalBusTIM17); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL); +} \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_bitbang.h b/flip_tdi/helpers/ftdi_bitbang.h new file mode 100644 index 0000000..5996c17 --- /dev/null +++ b/flip_tdi/helpers/ftdi_bitbang.h @@ -0,0 +1,13 @@ +#pragma once +#include "ftdi.h" +#include "ftdi_mpsse.h" + +typedef struct FtdiBitbang FtdiBitbang; + +FtdiBitbang* ftdi_bitbang_alloc(Ftdi* ftdi); +void ftdi_bitbang_free(FtdiBitbang* ftdi_bitbang); +FtdiMpsse* ftdi_bitbang_get_mpsse_handle(FtdiBitbang* ftdi_bitbang); +void ftdi_bitbang_set_gpio(FtdiBitbang* ftdi_bitbang, uint8_t gpio_mask); +void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, FtdiBitMode mode); +void ftdi_bitbang_set_speed(FtdiBitbang* ftdi_bitbang, uint32_t speed); +uint8_t ftdi_bitbang_get_gpio(FtdiBitbang* ftdi_bitbang); \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_gpio.c b/flip_tdi/helpers/ftdi_gpio.c new file mode 100644 index 0000000..c78ae3e --- /dev/null +++ b/flip_tdi/helpers/ftdi_gpio.c @@ -0,0 +1,91 @@ +#include "ftdi_latency_timer.h" +#include "furi.h" +#include + +#include + +#define TAG "FTDI_GPIO" + +#define gpio_b0 (gpio_ext_pa7) +#define gpio_b1 (gpio_ext_pa6) +#define gpio_b2 (gpio_ext_pa4) +#define gpio_b3 (gpio_ext_pb3) +#define gpio_b4 (gpio_ext_pb2) +#define gpio_b5 (gpio_ext_pc3) +#define gpio_b6 (gpio_ext_pc1) +#define gpio_b7 (gpio_ext_pc0) + +void ftdi_gpio_set_direction(uint8_t gpio_mask) { + // gpio_ext_pa7 + if(gpio_mask & 0b00000001) { + LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_7, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_7, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pa6 + if(gpio_mask & 0b00000010) { + LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_6, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_6, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pa4 + if(gpio_mask & 0b00000100) { + LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_4, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_4, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pb3 + if(gpio_mask & 0b00001000) { + LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_3, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_3, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pb2 + if(gpio_mask & 0b00010000) { + LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_2, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_2, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pc3 + if(gpio_mask & 0b00100000) { + LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_3, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_3, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pc1 + if(gpio_mask & 0b01000000) { + LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_1, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_1, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pc0 + if(gpio_mask & 0b10000000) { + LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_0, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_0, LL_GPIO_MODE_INPUT); + } +} + +void ftdi_gpio_init(uint8_t gpio_mask) { + furi_hal_gpio_init(&gpio_b0, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b1, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b2, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b3, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b4, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b5, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b6, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b7, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + + ftdi_gpio_set_direction(gpio_mask); +} + +void ftdi_gpio_deinit() { + furi_hal_gpio_init(&gpio_b0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b2, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b4, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b5, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} diff --git a/flip_tdi/helpers/ftdi_gpio.h b/flip_tdi/helpers/ftdi_gpio.h new file mode 100644 index 0000000..08242ba --- /dev/null +++ b/flip_tdi/helpers/ftdi_gpio.h @@ -0,0 +1,106 @@ +#pragma once +#include "ftdi.h" + +void ftdi_gpio_set_direction(uint8_t gpio_mask); +void ftdi_gpio_init(uint8_t gpio_mask); +void ftdi_gpio_deinit(); + +static inline uint8_t ftdi_gpio_get_b0(void) { // gpio_ext_pa7 + return (GPIOA->IDR & LL_GPIO_PIN_7) ? 0b00000001 : 0; +} + +static inline uint8_t ftdi_gpio_get_b1(void) { // gpio_ext_pa6 + return (GPIOA->IDR & LL_GPIO_PIN_6) ? 0b00000010 : 0; +} + +static inline uint8_t ftdi_gpio_get_b2(void) { // gpio_ext_pa4 + return (GPIOA->IDR & LL_GPIO_PIN_4) ? 0b00000100 : 0; +} + +static inline uint8_t ftdi_gpio_get_b3(void) { // gpio_ext_pb3 + return (GPIOB->IDR & LL_GPIO_PIN_3) ? 0b00001000 : 0; +} + +static inline uint8_t ftdi_gpio_get_b4(void) { // gpio_ext_pb2 + return (GPIOB->IDR & LL_GPIO_PIN_2) ? 0b00010000 : 0; +} + +static inline uint8_t ftdi_gpio_get_b5(void) { // gpio_ext_pc3 + return (GPIOC->IDR & LL_GPIO_PIN_3) ? 0b00100000 : 0; +} + +static inline uint8_t ftdi_gpio_get_b6(void) { // gpio_ext_pc1 + return (GPIOC->IDR & LL_GPIO_PIN_1) ? 0b01000000 : 0; +} + +static inline uint8_t ftdi_gpio_get_b7(void) { // gpio_ext_pc0 + return (GPIOC->IDR & LL_GPIO_PIN_0) ? 0b10000000 : 0; +} + +static inline void ftdi_gpio_set_noop(uint8_t state) { // gpio_ext_pa7 + UNUSED(state); +} + +static inline void ftdi_gpio_set_b0(uint8_t state) { // gpio_ext_pa7 + if(state) { + GPIOA->BSRR = LL_GPIO_PIN_7; + } else { + GPIOA->BRR = LL_GPIO_PIN_7; + } +} + +static inline void ftdi_gpio_set_b1(uint8_t state) { // gpio_ext_pa6 + if(state) { + GPIOA->BSRR = LL_GPIO_PIN_6; + } else { + GPIOA->BRR = LL_GPIO_PIN_6; + } +} + +static inline void ftdi_gpio_set_b2(uint8_t state) { // gpio_ext_pa4 + if(state) { + GPIOA->BSRR = LL_GPIO_PIN_4; + } else { + GPIOA->BRR = LL_GPIO_PIN_4; + } +} + +static inline void ftdi_gpio_set_b3(uint8_t state) { // gpio_ext_pb3 + if(state) { + GPIOB->BSRR = LL_GPIO_PIN_3; + } else { + GPIOB->BRR = LL_GPIO_PIN_3; + } +} + +static inline void ftdi_gpio_set_b4(uint8_t state) { // gpio_ext_pb2 + if(state) { + GPIOB->BSRR = LL_GPIO_PIN_2; + } else { + GPIOB->BRR = LL_GPIO_PIN_2; + } +} + +static inline void ftdi_gpio_set_b5(uint8_t state) { // gpio_ext_pc3 + if(state) { + GPIOC->BSRR = LL_GPIO_PIN_3; + } else { + GPIOC->BRR = LL_GPIO_PIN_3; + } +} + +static inline void ftdi_gpio_set_b6(uint8_t state) { // gpio_ext_pc1 + if(state) { + GPIOC->BSRR = LL_GPIO_PIN_1; + } else { + GPIOC->BRR = LL_GPIO_PIN_1; + } +} + +static inline void ftdi_gpio_set_b7(uint8_t state) { // gpio_ext_pc0 + if(state) { + GPIOC->BSRR = LL_GPIO_PIN_0; + } else { + GPIOC->BRR = LL_GPIO_PIN_0; + } +} diff --git a/flip_tdi/helpers/ftdi_latency_timer.c b/flip_tdi/helpers/ftdi_latency_timer.c new file mode 100644 index 0000000..a47e0c5 --- /dev/null +++ b/flip_tdi/helpers/ftdi_latency_timer.c @@ -0,0 +1,110 @@ +#include "ftdi_latency_timer.h" +#include "furi.h" +#include +#include + +#define TAG "FTDI_LATENCY_TIMER" + +#define FTDI_LATENCY_TIMER_SPEED_DEFALUT 16 // ms defalut + +struct FtdiLatencyTimer { + uint8_t speed; + bool enable; + FtdiLatencyTimerCallbackUp callback; + void* context; +}; + +static void ftdi_latency_timer_tim_isr(void* context); +static void ftdi_latency_timer_tim_init(FtdiLatencyTimer* ftdi_latency_timer); +static void ftdi_latency_timer_tim_deinit(FtdiLatencyTimer* ftdi_latency_timer); + +FtdiLatencyTimer* ftdi_latency_timer_alloc(void) { + FtdiLatencyTimer* ftdi_latency_timer = malloc(sizeof(FtdiLatencyTimer)); + ftdi_latency_timer->speed = FTDI_LATENCY_TIMER_SPEED_DEFALUT; + ftdi_latency_timer->enable = false; + + ftdi_latency_timer_tim_init(ftdi_latency_timer); + return ftdi_latency_timer; +} + +void ftdi_latency_timer_free(FtdiLatencyTimer* ftdi_latency_timer) { + if(!ftdi_latency_timer) return; + ftdi_latency_timer->enable = false; + ftdi_latency_timer_tim_deinit(ftdi_latency_timer); + free(ftdi_latency_timer); + ftdi_latency_timer = NULL; +} + +void ftdi_latency_timer_set_callback( + FtdiLatencyTimer* ftdi_latency_timer, + FtdiLatencyTimerCallbackUp callback, + void* context) { + ftdi_latency_timer->callback = callback; + ftdi_latency_timer->context = context; +} + +void ftdi_latency_timer_enable(FtdiLatencyTimer* ftdi_latency_timer, bool enable) { + ftdi_latency_timer->enable = enable; + + if(enable) { + LL_TIM_SetCounter(TIM1, 0); + LL_TIM_EnableCounter(TIM1); + } else { + LL_TIM_DisableCounter(TIM1); + } +} + +void ftdi_latency_timer_set_speed(FtdiLatencyTimer* ftdi_latency_timer, uint8_t speed) { + UNUSED(ftdi_latency_timer); + if(speed == 0) { + speed = FTDI_LATENCY_TIMER_SPEED_DEFALUT; + ftdi_latency_timer_enable(ftdi_latency_timer, false); + } else { + ftdi_latency_timer_enable(ftdi_latency_timer, true); + } + + LL_TIM_SetPrescaler(TIM1, speed - 1); +} + +uint8_t ftdi_latency_timer_get_speed(FtdiLatencyTimer* ftdi_latency_timer) { + return ftdi_latency_timer->speed; +} + +void ftdi_latency_timer_reset(FtdiLatencyTimer* ftdi_latency_timer) { + UNUSED(ftdi_latency_timer); + LL_TIM_SetCounter(TIM1, 0); +} + +static void ftdi_latency_timer_tim_isr(void* context) { + FtdiLatencyTimer* ftdi_latency_timer = context; + UNUSED(ftdi_latency_timer); + if(LL_TIM_IsActiveFlag_UPDATE(TIM1)) { + LL_TIM_ClearFlag_UPDATE(TIM1); + if(ftdi_latency_timer->callback) { + ftdi_latency_timer->callback(ftdi_latency_timer->context); + } + } +} + +static void ftdi_latency_timer_tim_init(FtdiLatencyTimer* ftdi_latency_timer) { + furi_hal_bus_enable(FuriHalBusTIM1); + + LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM1, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetAutoReload(TIM1, 64000 - 1); + LL_TIM_SetPrescaler(TIM1, FTDI_LATENCY_TIMER_SPEED_DEFALUT - 1); + LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM1); + + furi_hal_interrupt_set_isr( + FuriHalInterruptIdTim1UpTim16, ftdi_latency_timer_tim_isr, ftdi_latency_timer); + + LL_TIM_EnableIT_UPDATE(TIM1); +} + +static void ftdi_latency_timer_tim_deinit(FtdiLatencyTimer* ftdi_latency_timer) { + UNUSED(ftdi_latency_timer); + LL_TIM_DisableCounter(TIM1); + furi_hal_bus_disable(FuriHalBusTIM1); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1UpTim16, NULL, NULL); +} \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_latency_timer.h b/flip_tdi/helpers/ftdi_latency_timer.h new file mode 100644 index 0000000..6daba3c --- /dev/null +++ b/flip_tdi/helpers/ftdi_latency_timer.h @@ -0,0 +1,17 @@ +#pragma once +#include "ftdi.h" + +typedef struct FtdiLatencyTimer FtdiLatencyTimer; + +typedef void (*FtdiLatencyTimerCallbackUp)(void* context); + +FtdiLatencyTimer* ftdi_latency_timer_alloc(void); +void ftdi_latency_timer_free(FtdiLatencyTimer* ftdi_latency_timer); +void ftdi_latency_timer_set_callback( + FtdiLatencyTimer* ftdi_latency_timer, + FtdiLatencyTimerCallbackUp callback, + void* context); +void ftdi_latency_timer_enable(FtdiLatencyTimer* ftdi_latency_timer, bool enable); +void ftdi_latency_timer_reset(FtdiLatencyTimer* ftdi_latency_timer); +void ftdi_latency_timer_set_speed(FtdiLatencyTimer* ftdi_latency_timer, uint8_t speed); +uint8_t ftdi_latency_timer_get_speed(FtdiLatencyTimer* ftdi_latency_timer); diff --git a/flip_tdi/helpers/ftdi_mpsse.c b/flip_tdi/helpers/ftdi_mpsse.c new file mode 100644 index 0000000..054ee93 --- /dev/null +++ b/flip_tdi/helpers/ftdi_mpsse.c @@ -0,0 +1,528 @@ +#include "ftdi_mpsse.h" +#include "furi.h" +#include +#include "ftdi_gpio.h" +#include "ftdi_latency_timer.h" +#include "ftdi_mpsse_data.h" + +#define TAG "FTDI_MPSSE" + +typedef void (*FtdiMpsseGpioO)(uint8_t state); + +#define FTDI_MPSSE_TIMEOUT 5000 +#define FTDI_MPSSE_TX_RX_SIZE (4096UL) + +typedef enum { + FtdiMpsseErrorNone = 0, + FtdiMpsseErrorTimeout, + FtdiMpsseErrorTxOverflow, +} FtdiMpsseError; + +struct FtdiMpsse { + Ftdi* ftdi; + uint8_t gpio_state; + uint8_t gpio_mask; + uint16_t data_size; + bool is_loopback; + bool is_div5; + bool is_clk3phase; + bool is_adaptive; + FtdiMpsseError error; + + FtdiMpsseGpioO gpio_o[8]; + uint8_t gpio_mask_old; + + FtdiMpsseCallbackImmediate callback_immediate; + void* context_immediate; + + uint8_t* data_buf; + uint16_t data_buf_count_byte; +}; + +void ftdi_mpsse_gpio_set_callback( + FtdiMpsse* ftdi_mpsse, + FtdiMpsseCallbackImmediate callback, + void* context) { + ftdi_mpsse->callback_immediate = callback; + ftdi_mpsse->context_immediate = context; +} + +void ftdi_mpsse_gpio_set_direction(FtdiMpsse* ftdi_mpsse) { + ftdi_gpio_set_direction(ftdi_mpsse->gpio_mask); + if(ftdi_mpsse->gpio_mask & 0b00000001) { + ftdi_mpsse->gpio_o[0] = ftdi_gpio_set_b0; + } else { + ftdi_mpsse->gpio_o[0] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b00000010) { + ftdi_mpsse->gpio_o[1] = ftdi_gpio_set_b1; + } else { + ftdi_mpsse->gpio_o[1] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b00000100) { + ftdi_mpsse->gpio_o[2] = ftdi_gpio_set_b2; + } else { + ftdi_mpsse->gpio_o[2] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b00001000) { + ftdi_mpsse->gpio_o[3] = ftdi_gpio_set_b3; + } else { + ftdi_mpsse->gpio_o[3] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b00010000) { + ftdi_mpsse->gpio_o[4] = ftdi_gpio_set_b4; + } else { + ftdi_mpsse->gpio_o[4] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b00100000) { + ftdi_mpsse->gpio_o[5] = ftdi_gpio_set_b5; + } else { + ftdi_mpsse->gpio_o[5] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b01000000) { + ftdi_mpsse->gpio_o[6] = ftdi_gpio_set_b6; + } else { + ftdi_mpsse->gpio_o[6] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b10000000) { + ftdi_mpsse->gpio_o[7] = ftdi_gpio_set_b7; + } else { + ftdi_mpsse->gpio_o[7] = ftdi_gpio_set_noop; + } +} + +void ftdi_mpsse_gpio_init(FtdiMpsse* ftdi_mpsse) { + ftdi_gpio_init(ftdi_mpsse->gpio_mask); + ftdi_mpsse_gpio_set_direction(ftdi_mpsse); +} + +static inline void ftdi_mpsse_gpio_set(FtdiMpsse* ftdi_mpsse) { + ftdi_mpsse->gpio_o[0](ftdi_mpsse->gpio_state & 0b00000001); + ftdi_mpsse->gpio_o[1](ftdi_mpsse->gpio_state & 0b00000010); + ftdi_mpsse->gpio_o[2](ftdi_mpsse->gpio_state & 0b00000100); + ftdi_mpsse->gpio_o[3](ftdi_mpsse->gpio_state & 0b00001000); + ftdi_mpsse->gpio_o[4](ftdi_mpsse->gpio_state & 0b00010000); + ftdi_mpsse->gpio_o[5](ftdi_mpsse->gpio_state & 0b00100000); + ftdi_mpsse->gpio_o[6](ftdi_mpsse->gpio_state & 0b01000000); + ftdi_mpsse->gpio_o[7](ftdi_mpsse->gpio_state & 0b10000000); +} + +static inline uint8_t ftdi_mpsse_gpio_get(void) { + uint8_t gpio_data = 0; + gpio_data |= ftdi_gpio_get_b0(); + gpio_data |= ftdi_gpio_get_b1(); + gpio_data |= ftdi_gpio_get_b2(); + gpio_data |= ftdi_gpio_get_b3(); + gpio_data |= ftdi_gpio_get_b4(); + gpio_data |= ftdi_gpio_get_b5(); + gpio_data |= ftdi_gpio_get_b6(); + gpio_data |= ftdi_gpio_get_b7(); + return gpio_data; +} + +FtdiMpsse* ftdi_mpsse_alloc(Ftdi* ftdi) { + FtdiMpsse* ftdi_mpsse = malloc(sizeof(FtdiMpsse)); + ftdi_mpsse->ftdi = ftdi; + ftdi_mpsse->gpio_state = 0; + ftdi_mpsse->gpio_mask = 0; + ftdi_mpsse->data_size = 0; + ftdi_mpsse->is_loopback = false; + ftdi_mpsse->is_div5 = false; + ftdi_mpsse->is_clk3phase = false; + ftdi_mpsse->is_adaptive = false; + + ftdi_mpsse->error = FtdiMpsseErrorNone; + ftdi_mpsse->data_buf = malloc(FTDI_MPSSE_TX_RX_SIZE * sizeof(uint8_t)); + ftdi_mpsse->data_buf_count_byte = 0; + + ftdi_mpsse_gpio_init(ftdi_mpsse); + + return ftdi_mpsse; +} + +void ftdi_mpsse_free(FtdiMpsse* ftdi_mpsse) { + if(!ftdi_mpsse) return; + free(ftdi_mpsse->data_buf); + ftdi_gpio_deinit(); + free(ftdi_mpsse); + ftdi_mpsse = NULL; +} + +uint8_t ftdi_mpsse_get_data_stream(FtdiMpsse* ftdi_mpsse) { + uint8_t data = 0; + if(ftdi_get_rx_buf(ftdi_mpsse->ftdi, &data, 1, FTDI_MPSSE_TIMEOUT) != 1) { + FURI_LOG_E(TAG, "Timeout"); + ftdi_mpsse->error = FtdiMpsseErrorTimeout; + } +#ifdef FTDI_DEBUG + FURI_LOG_RAW_I("0x%02X ", data); +#endif + return data; +} + +void ftdi_mpssse_set_data_stream(FtdiMpsse* ftdi_mpsse, uint8_t* data, uint16_t size) { + ftdi_set_tx_buf(ftdi_mpsse->ftdi, data, size); +} + +static inline void ftdi_mpsse_skeep_data(FtdiMpsse* ftdi_mpsse) { + ftdi_mpsse->data_size++; + while(ftdi_mpsse->data_size--) { + if(ftdi_mpsse->error != FtdiMpsseErrorNone) return; + ftdi_mpsse_get_data_stream(ftdi_mpsse); + } +} + +void ftdi_mpsse_get_data(FtdiMpsse* ftdi_mpsse) { + //todo add support for tx buffer, data_size_max = 0xFF00 + ftdi_mpsse->data_size++; + if(ftdi_mpsse->data_size > FTDI_MPSSE_TX_RX_SIZE) { + ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; + FURI_LOG_E(TAG, "Tx buffer overflow"); + ftdi_mpsse_skeep_data(ftdi_mpsse); + ftdi_mpsse->data_size = 0; + } + + if(ftdi_get_rx_buf( + ftdi_mpsse->ftdi, ftdi_mpsse->data_buf, ftdi_mpsse->data_size, FTDI_MPSSE_TIMEOUT) != + ftdi_mpsse->data_size) { + FURI_LOG_E(TAG, "Timeout"); + ftdi_mpsse->error = FtdiMpsseErrorTimeout; + } + ftdi_mpsse->data_buf_count_byte = ftdi_mpsse->data_size; +} + +static uint16_t ftdi_mpsse_get_data_size(FtdiMpsse* ftdi_mpsse) { + return ftdi_mpsse_get_data_stream(ftdi_mpsse) | + (uint16_t)ftdi_mpsse_get_data_stream(ftdi_mpsse) << 8; +} + +static inline void ftdi_mpsse_immediate(FtdiMpsse* ftdi_mpsse) { + if(ftdi_mpsse->callback_immediate) { + ftdi_mpsse->callback_immediate(ftdi_mpsse->context_immediate); + } +} +void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { + ftdi_mpsse->error = FtdiMpsseErrorNone; + uint8_t data = ftdi_mpsse_get_data_stream(ftdi_mpsse); + uint8_t gpio_state_io = 0xFF; + switch(data) { + case FtdiMpsseCommandsSetBitsLow: // 0x80 Change LSB GPIO output */ + ftdi_mpsse->gpio_state = ftdi_mpsse_get_data_stream(ftdi_mpsse); + ftdi_mpsse->gpio_mask = ftdi_mpsse_get_data_stream(ftdi_mpsse); + if(ftdi_mpsse->gpio_mask_old != ftdi_mpsse->gpio_mask) { + ftdi_mpsse_gpio_set_direction(ftdi_mpsse); + ftdi_mpsse->gpio_mask_old = ftdi_mpsse->gpio_mask; + } + ftdi_mpsse_gpio_set(ftdi_mpsse); + //Write to GPIO + break; + case FtdiMpsseCommandsSetBitsHigh: // 0x82 Change MSB GPIO output */ + //Todo not supported + ftdi_mpsse_get_data_stream(ftdi_mpsse); + ftdi_mpsse_get_data_stream(ftdi_mpsse); + break; + case FtdiMpsseCommandsGetBitsLow: // 0x81 Get LSB GPIO output */ + //Read GPIO + gpio_state_io = ftdi_mpsse_gpio_get(); + ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); + break; + case FtdiMpsseCommandsGetBitsHigh: // 0x83 Get MSB GPIO output */ + //Todo not supported + gpio_state_io = 0xFF; + ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); + break; + case FtdiMpsseCommandsSendImmediate: // 0x87 Send immediate */ + //tx data to host callback + ftdi_mpsse_immediate(ftdi_mpsse); + break; + case FtdiMpsseCommandsWriteBytesPveMsb: // 0x10 Write bytes with positive edge clock, MSB first */ + //spi mode 1,3 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_get_data(ftdi_mpsse); + ftdi_mpsse_data_write_bytes_pve_msb(ftdi_mpsse->data_buf, ftdi_mpsse->data_buf_count_byte); + break; + case FtdiMpsseCommandsWriteBytesNveMsb: // 0x11 Write bytes with negative edge clock, MSB first */ + //spi mode 0,2 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_get_data(ftdi_mpsse); + ftdi_mpsse_data_write_bytes_nve_msb(ftdi_mpsse->data_buf, ftdi_mpsse->data_buf_count_byte); + break; + case FtdiMpsseCommandsWriteBitsPveMsb: // 0x12 Write bits with positive edge clock, MSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_skeep_data(ftdi_mpsse); + break; + case FtdiMpsseCommandsWriteBitsNveMsb: // 0x13 Write bits with negative edge clock, MSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_skeep_data(ftdi_mpsse); + break; + case FtdiMpsseCommandsWriteBytesPveLsb: // 0x18 Write bytes with positive edge clock, LSB first */ + //spi mode 1,3 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_get_data(ftdi_mpsse); + ftdi_mpsse_data_write_bytes_pve_lsb(ftdi_mpsse->data_buf, ftdi_mpsse->data_buf_count_byte); + break; + case FtdiMpsseCommandsWriteBytesNveLsb: // 0x19 Write bytes with negative edge clock, LSB first */ + //spi mode 0,2 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_get_data(ftdi_mpsse); + ftdi_mpsse_data_write_bytes_nve_lsb(ftdi_mpsse->data_buf, ftdi_mpsse->data_buf_count_byte); + break; + case FtdiMpsseCommandsWriteBitsPveLsb: // 0x1a Write bits with positive edge clock, LSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_skeep_data(ftdi_mpsse); + break; + case FtdiMpsseCommandsWriteBitsNveLsb: // 0x1b Write bits with negative edge clock, LSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_skeep_data(ftdi_mpsse); + break; + case FtdiMpsseCommandsReadBytesPveMsb: // 0x20 Read bytes with positive edge clock, MSB first */ + //spi mode 1,3 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + if(ftdi_mpsse->data_size >= FTDI_MPSSE_TX_RX_SIZE) { + ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; + FURI_LOG_E(TAG, "Tx buffer overflow"); + } else { + ftdi_mpsse->data_size++; + ftdi_mpsse_data_read_bytes_pve_msb(ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpssse_set_data_stream(ftdi_mpsse, ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpsse_immediate(ftdi_mpsse); + } + break; + case FtdiMpsseCommandsReadBytesNveMsb: // 0x24 Read bytes with negative edge clock, MSB first */ + //spi mode 0,2 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + if(ftdi_mpsse->data_size >= FTDI_MPSSE_TX_RX_SIZE) { + ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; + FURI_LOG_E(TAG, "Tx buffer overflow"); + } else { + ftdi_mpsse->data_size++; + ftdi_mpsse_data_read_bytes_nve_msb(ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpssse_set_data_stream(ftdi_mpsse, ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpsse_immediate(ftdi_mpsse); + } + + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsReadBitsPveMsb: // 0x22 Read bits with positive edge clock, MSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsReadBitsNveMsb: // 0x26 Read bits with negative edge clock, MSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsReadBytesPveLsb: // 0x28 Read bytes with positive edge clock, LSB first */ + //spi mode 1,3 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + if(ftdi_mpsse->data_size >= FTDI_MPSSE_TX_RX_SIZE) { + ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; + FURI_LOG_E(TAG, "Tx buffer overflow"); + } else { + ftdi_mpsse->data_size++; + ftdi_mpsse_data_read_bytes_pve_lsb(ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpssse_set_data_stream(ftdi_mpsse, ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpsse_immediate(ftdi_mpsse); + } + break; + case FtdiMpsseCommandsReadBytesNveLsb: // 0x2c Read bytes with negative edge clock, LSB first */ + //spi mode 0,2 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + if(ftdi_mpsse->data_size >= FTDI_MPSSE_TX_RX_SIZE) { + ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; + FURI_LOG_E(TAG, "Tx buffer overflow"); + } else { + ftdi_mpsse->data_size++; + ftdi_mpsse_data_read_bytes_nve_lsb(ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpssse_set_data_stream(ftdi_mpsse, ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpsse_immediate(ftdi_mpsse); + } + break; + case FtdiMpsseCommandsReadBitsPveLsb: // 0x2a Read bits with positive edge clock, LSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsReadBitsNveLsb: // 0x2e Read bits with negative edge clock, LSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBytesPveNveMsb: // 0x31 Read/Write bytes with positive edge clock, MSB first */ + //spi mode 1,3 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_get_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBytesNvePveMsb: // 0x34 Read/Write bytes with negative edge clock, MSB first */ + //spi mode 0,2 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_get_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBitsPveNveMsb: // 0x33 Read/Write bits with positive edge clock, MSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_skeep_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBitsNvePveMsb: // 0x36 Read/Write bits with negative edge clock, MSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_skeep_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBytesPveNveLsb: // 0x39 Read/Write bytes with positive edge clock, LSB first */ + //spi mode 1,3 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_get_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBytesNvePveLsb: // 0x3c Read/Write bytes with negative edge clock, LSB first */ + //spi mode 0,2 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_get_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBitsPveNveLsb: // 0x3b Read/Write bits with positive edge clock, LSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_skeep_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBitsNvePveLsb: // 0x3e Read/Write bits with negative edge clock, LSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_skeep_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsWriteBitsTmsPve: // 0x4a Write bits with TMS, positive edge clock */ + //not supported + break; + case FtdiMpsseCommandsWriteBitsTmsNve: // 0x4b Write bits with TMS, negative edge clock */ + //not supported + break; + case FtdiMpsseCommandsRwBitsTmsPvePve: // 0x6a Read/Write bits with TMS, positive edge clock, MSB first */ + //not supported + break; + case FtdiMpsseCommandsRwBitsTmsPveNve: // 0x6b Read/Write bits with TMS, positive edge clock, MSB first */ + //not supported + break; + case FtdiMpsseCommandsRwBitsTmsNvePve: // 0x6e Read/Write bits with TMS, negative edge clock, MSB first */ + //not supported + break; + case FtdiMpsseCommandsRwBitsTmsNveNve: // 0x6f Read/Write bits with TMS, negative edge clock, MSB first */ + //not supported + break; + case FtdiMpsseCommandsLoopbackStart: // 0x84 Enable loopback */ + ftdi_mpsse->is_loopback = true; + ftdi_mpsse_immediate(ftdi_mpsse); + break; + case FtdiMpsseCommandsLoopbackEnd: // 0x85 Disable loopback */ + ftdi_mpsse->is_loopback = false; + ftdi_mpsse_immediate(ftdi_mpsse); + break; + case FtdiMpsseCommandsSetTckDivisor: // 0x86 Set clock */ + ftdi_mpsse_immediate(ftdi_mpsse); + break; + case FtdiMpsseCommandsDisDiv5: // 0x8a Disable divide by 5 */ + ftdi_mpsse->is_div5 = false; + ftdi_mpsse_immediate(ftdi_mpsse); + break; + case FtdiMpsseCommandsEnDiv5: // 0x8b Enable divide by 5 */ + ftdi_mpsse->is_div5 = true; + ftdi_mpsse_immediate(ftdi_mpsse); + break; + case FtdiMpsseCommandsEnableClk3Phase: // 0x8c Enable 3-phase data clocking (I2C) */ + ftdi_mpsse->is_clk3phase = true; + ftdi_mpsse_immediate(ftdi_mpsse); + break; + case FtdiMpsseCommandsDisableClk3Phase: // 0x8d Disable 3-phase data clocking */ + ftdi_mpsse->is_clk3phase = false; + ftdi_mpsse_immediate(ftdi_mpsse); + break; + case FtdiMpsseCommandsClkBitsNoData: // 0x8e Allows JTAG clock to be output w/o data */ + //not supported + break; + case FtdiMpsseCommandsClkBytesNoData: // 0x8f Allows JTAG clock to be output w/o data */ + //not supported + break; + case FtdiMpsseCommandsClkWaitOnHigh: // 0x94 Clock until GPIOL1 is high */ + //not supported + break; + case FtdiMpsseCommandsClkWaitOnLow: // 0x95 Clock until GPIOL1 is low */ + //not supported + break; + case FtdiMpsseCommandsEnableClkAdaptive: // 0x96 Enable JTAG adaptive clock for ARM */ + ftdi_mpsse->is_adaptive = true; + ftdi_mpsse_immediate(ftdi_mpsse); + break; + case FtdiMpsseCommandsDisableClkAdaptive: // 0x97 Disable JTAG adaptive clock */ + ftdi_mpsse->is_adaptive = false; + ftdi_mpsse_immediate(ftdi_mpsse); + break; + case FtdiMpsseCommandsClkCountWaitOnHigh: // 0x9c Clock byte cycles until GPIOL1 is high */ + //not supported + break; + case FtdiMpsseCommandsClkCountWaitOnLow: // 0x9d Clock byte cycles until GPIOL1 is low */ + //not supported + break; + case FtdiMpsseCommandsDriveZero: // 0x9e Drive-zero mode */ + //not supported + break; + case FtdiMpsseCommandsWaitOnHigh: // 0x88 Wait until GPIOL1 is high */ + //not supported + break; + case FtdiMpsseCommandsWaitOnLow: // 0x89 Wait until GPIOL1 is low */ + //not supported + break; + case FtdiMpsseCommandsReadShort: // 0x90 Read short */ + //not supported + break; + case FtdiMpsseCommandsReadExtended: // 0x91 Read extended */ + //not supported + break; + case FtdiMpsseCommandsWriteShort: // 0x92 Write short */ + //not supported + break; + case FtdiMpsseCommandsWriteExtended: // 0x93 Write extended */ + //not supported + break; + + default: + break; + } +} diff --git a/flip_tdi/helpers/ftdi_mpsse.h b/flip_tdi/helpers/ftdi_mpsse.h new file mode 100644 index 0000000..4dc4a35 --- /dev/null +++ b/flip_tdi/helpers/ftdi_mpsse.h @@ -0,0 +1,11 @@ +#pragma once +#include "ftdi.h" + +typedef struct FtdiMpsse FtdiMpsse; + +typedef void (*FtdiMpsseCallbackImmediate)(void* context); +void ftdi_mpsse_gpio_set_callback(FtdiMpsse* ftdi_mpsse, FtdiMpsseCallbackImmediate callback, void* context); + +FtdiMpsse* ftdi_mpsse_alloc(Ftdi* ftdi); +void ftdi_mpsse_free(FtdiMpsse* ftdi_mpsse); +void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse); diff --git a/flip_tdi/helpers/ftdi_mpsse_data.h b/flip_tdi/helpers/ftdi_mpsse_data.h new file mode 100644 index 0000000..f92f20d --- /dev/null +++ b/flip_tdi/helpers/ftdi_mpsse_data.h @@ -0,0 +1,123 @@ +// #pragma GCC optimize("Ofast") +// #pragma GCC optimize("-funroll-all-loops") +#pragma once +#include "ftdi_gpio.h" + +//ftdi_gpio_set_b0 - CLK +//ftdi_gpio_set_b1 - MOSI +//ftdi_gpio_set_b2 - MISO +//ftdi_gpio_set_b3 - CS + +static inline void ftdi_mpsse_data_write_bytes_nve_msb(uint8_t* data, uint32_t size) { + uint8_t mask = 0x80; + for(uint32_t i = 0; i < size; i++) { + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b1(data[i] & mask); + ftdi_gpio_set_b0(1); + data[i] <<= 1; + ftdi_gpio_set_b0(0); + } + } + ftdi_gpio_set_b0(0); + ftdi_gpio_set_b1(0); +} + +static inline void ftdi_mpsse_data_read_bytes_nve_msb(uint8_t* data, uint32_t size) { + memset(data, 0, size); + for(uint32_t i = 0; i < size; i++) { + uint8_t mask = 0x80; + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b0(1); + data[i] |= ftdi_gpio_get_b2() ? mask : 0; + ftdi_gpio_set_b0(0); + mask >>= 1; + } + } +} + +static inline void ftdi_mpsse_data_write_bytes_nve_lsb(uint8_t* data, uint32_t size) { + uint8_t mask = 0x01; + for(uint32_t i = 0; i < size; i++) { + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b1(data[i] & mask); + ftdi_gpio_set_b0(1); + data[i] >>= 1; + ftdi_gpio_set_b0(0); + } + } + ftdi_gpio_set_b0(0); + ftdi_gpio_set_b1(0); +} + +static inline void ftdi_mpsse_data_read_bytes_nve_lsb(uint8_t* data, uint32_t size) { + memset(data, 0, size); + for(uint32_t i = 0; i < size; i++) { + uint8_t mask = 0x01; + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b0(1); + data[i] |= ftdi_gpio_get_b2() ? mask : 0; + ftdi_gpio_set_b0(0); + mask <<= 1; + } + } +} + +static inline void ftdi_mpsse_data_write_bytes_pve_msb(uint8_t* data, uint32_t size) { + uint8_t mask = 0x80; + ftdi_gpio_set_b0(1); + for(uint32_t i = 0; i < size; i++) { + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b1(data[i] & mask); + ftdi_gpio_set_b0(0); + data[i] <<= 1; + ftdi_gpio_set_b0(1); + } + } + ftdi_gpio_set_b0(1); + ftdi_gpio_set_b1(0); +} + +static inline void ftdi_mpsse_data_read_bytes_pve_msb(uint8_t* data, uint32_t size) { + memset(data, 0, size); + ftdi_gpio_set_b0(0); + for(uint32_t i = 0; i < size; i++) { + uint16_t mask = 0x80; + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b0(1); + data[i] |= ftdi_gpio_get_b2() ? mask : 0; + ftdi_gpio_set_b0(0); + mask >>= 1; + } + } + ftdi_gpio_set_b0(1); +} + +static inline void ftdi_mpsse_data_write_bytes_pve_lsb(uint8_t* data, uint32_t size) { + uint8_t mask = 0x01; + ftdi_gpio_set_b0(1); + for(uint32_t i = 0; i < size; i++) { + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b1(data[i] & mask); + ftdi_gpio_set_b0(0); + data[i] >>= 1; + ftdi_gpio_set_b0(1); + } + } + ftdi_gpio_set_b0(1); + ftdi_gpio_set_b1(0); +} + +static inline void ftdi_mpsse_data_read_bytes_pve_lsb(uint8_t* data, uint32_t size) { + memset(data, 0, size); + ftdi_gpio_set_b0(0); + for(uint32_t i = 0; i < size; i++) { + uint16_t mask = 0x01; + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b0(1); + data[i] |= ftdi_gpio_get_b2() ? mask : 0; + ftdi_gpio_set_b0(0); + mask <<= 1; + } + } + ftdi_gpio_set_b0(1); +} \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_uart.c b/flip_tdi/helpers/ftdi_uart.c new file mode 100644 index 0000000..d751815 --- /dev/null +++ b/flip_tdi/helpers/ftdi_uart.c @@ -0,0 +1,290 @@ +#include "ftdi_uart.h" +#include "furi.h" +#include + +#include +#include + +#define TAG "FTDI_UART" +#define FTDI_UART_MAX_TXRX_SIZE (256UL) + +#define FTDI_UART_LPUART_DMA_INSTANCE (DMA2) +#define FTDI_UART_LPUART_DMA_CHANNEL (LL_DMA_CHANNEL_1) + +struct FtdiUart { + FuriThread* worker_thread; + FuriHalSerialHandle* serial_handle; + uint32_t baudrate; + Ftdi* ftdi; + uint8_t* buffer_tx_ptr; + bool enable; +}; + +typedef enum { + WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event + WorkerEventStop = (1 << 1), + WorkerEventRxData = (1 << 2), + WorkerEventRxIdle = (1 << 3), + WorkerEventRxOverrunError = (1 << 4), + WorkerEventRxFramingError = (1 << 5), + WorkerEventRxNoiseError = (1 << 6), + WorkerEventTXData = (1 << 7), + WorkerEventTXDataDmaEnd = (1 << 8), +} WorkerEvent; + +#define WORKER_EVENTS_MASK \ + (WorkerEventStop | WorkerEventRxData | WorkerEventRxIdle | WorkerEventRxOverrunError | \ + WorkerEventRxFramingError | WorkerEventRxNoiseError | WorkerEventTXData | \ + WorkerEventTXDataDmaEnd) + +static void ftdi_uart_tx_dma_init(FtdiUart* ftdi_uart); +static void ftdi_uart_tx_dma_deinit(FtdiUart* ftdi_uart); +static void ftdi_uart_tx_dma(FtdiUart* ftdi_uart, uint8_t* data, size_t size); + +static void ftdi_uart_irq_cb( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent ev, + size_t size, + void* context) { + FtdiUart* ftdi_uart = context; + + if(ev & (FuriHalSerialRxEventData | FuriHalSerialRxEventIdle)) { + uint8_t data[FURI_HAL_SERIAL_DMA_BUFFER_SIZE] = {0}; + while(size) { + size_t ret = furi_hal_serial_dma_rx( + handle, + data, + (size > FURI_HAL_SERIAL_DMA_BUFFER_SIZE) ? FURI_HAL_SERIAL_DMA_BUFFER_SIZE : size); + ftdi_set_tx_buf(ftdi_uart->ftdi, data, ret); + size -= ret; + }; + } +} + +static int32_t ftdi_uart_echo_worker(void* context) { + furi_assert(context); + FtdiUart* ftdi_uart = context; + + FURI_LOG_I(TAG, "Worker started"); + + ftdi_uart_tx_dma_init(ftdi_uart); + bool is_dma_tx = false; + + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + + if(events & WorkerEventStop) break; + + if(events & WorkerEventTXData) { + if(!is_dma_tx) { + is_dma_tx = true; + events |= WorkerEventTXDataDmaEnd; + } + } + + if(events & WorkerEventTXDataDmaEnd) { + size_t length = 0; + length = ftdi_get_rx_buf( + ftdi_uart->ftdi, ftdi_uart->buffer_tx_ptr, FTDI_UART_MAX_TXRX_SIZE, 0); + if(length > 0) { + ftdi_uart_tx_dma(ftdi_uart, ftdi_uart->buffer_tx_ptr, length); + } else { + is_dma_tx = false; + } + } + } + ftdi_uart_tx_dma_deinit(ftdi_uart); + FURI_LOG_I(TAG, "Worker stopped"); + return 0; +} + +FtdiUart* ftdi_uart_alloc(Ftdi* ftdi) { + FtdiUart* ftdi_uart = malloc(sizeof(FtdiUart)); + ftdi_uart->baudrate = 115200; + + ftdi_uart->serial_handle = furi_hal_serial_control_acquire(FuriHalSerialIdLpuart); + furi_check(ftdi_uart->serial_handle); + furi_hal_serial_init(ftdi_uart->serial_handle, ftdi_uart->baudrate); + furi_hal_serial_enable_direction(ftdi_uart->serial_handle, FuriHalSerialDirectionRx); + furi_hal_serial_enable_direction(ftdi_uart->serial_handle, FuriHalSerialDirectionTx); + furi_hal_serial_dma_rx_start(ftdi_uart->serial_handle, ftdi_uart_irq_cb, ftdi_uart, false); + ftdi_uart->ftdi = ftdi; + + //do not change LPUART, functions that directly work with peripherals are used + ftdi_uart->worker_thread = furi_thread_alloc_ex(TAG, 1024, ftdi_uart_echo_worker, ftdi_uart); + ftdi_uart->enable = true; + + furi_thread_start(ftdi_uart->worker_thread); + + return ftdi_uart; +} + +void ftdi_uart_free(FtdiUart* ftdi_uart) { + ftdi_uart->enable = false; + furi_thread_flags_set(furi_thread_get_id(ftdi_uart->worker_thread), WorkerEventStop); + furi_thread_join(ftdi_uart->worker_thread); + furi_thread_free(ftdi_uart->worker_thread); + + furi_hal_serial_deinit(ftdi_uart->serial_handle); + furi_hal_serial_control_release(ftdi_uart->serial_handle); + ftdi_uart->serial_handle = NULL; + free(ftdi_uart); +} + +void ftdi_uart_tx(FtdiUart* ftdi_uart) { + if(ftdi_uart->enable) { + furi_thread_flags_set(furi_thread_get_id(ftdi_uart->worker_thread), WorkerEventTXData); + } +} + +void ftdi_uart_set_baudrate(FtdiUart* ftdi_uart, uint32_t baudrate) { + ftdi_uart->baudrate = baudrate; + furi_hal_serial_set_br(ftdi_uart->serial_handle, baudrate); +} + +void ftdi_uart_enable(FtdiUart* ftdi_uart, bool enable) { + if(enable) { + ftdi_uart->enable = true; + furi_hal_serial_resume(ftdi_uart->serial_handle); + furi_hal_serial_enable_direction(ftdi_uart->serial_handle, FuriHalSerialDirectionRx); + furi_hal_serial_enable_direction(ftdi_uart->serial_handle, FuriHalSerialDirectionTx); + ftdi_reset_purge_rx(ftdi_uart->ftdi); + ftdi_reset_purge_tx(ftdi_uart->ftdi); + } else { + ftdi_uart->enable = false; + furi_hal_serial_suspend(ftdi_uart->serial_handle); + furi_hal_serial_disable_direction(ftdi_uart->serial_handle, FuriHalSerialDirectionRx); + furi_hal_serial_disable_direction(ftdi_uart->serial_handle, FuriHalSerialDirectionTx); + } +} + +void ftdi_uart_set_data_config(FtdiUart* ftdi_uart, FtdiDataConfig* data_config) { + furi_assert(data_config); + UNUSED(ftdi_uart); + + bool is_uart_enabled = LL_LPUART_IsEnabled(LPUART1); + + if(is_uart_enabled) { + LL_LPUART_Disable(LPUART1); + } + + uint32_t data_width = LL_LPUART_DATAWIDTH_8B; + uint32_t parity_mode = LL_LPUART_PARITY_NONE; + uint32_t stop_bits_mode = LL_LPUART_STOPBITS_2; + + switch(data_config->BITS) { + case FtdiBits7: + data_width = LL_LPUART_DATAWIDTH_7B; + break; + case FtdiBits8: + data_width = LL_LPUART_DATAWIDTH_8B; + break; + default: + break; + } + + switch(data_config->PARITY) { + case FtdiParityNone: + parity_mode = LL_LPUART_PARITY_NONE; + break; + case FtdiParityOdd: + parity_mode = LL_LPUART_PARITY_ODD; + break; + case FtdiParityEven: + case FtdiParityMark: + case FtdiParitySpace: + parity_mode = LL_LPUART_PARITY_EVEN; + break; + default: + break; + } + + switch(data_config->STOP_BITS) { + case FtdiStopBits1: + stop_bits_mode = LL_LPUART_STOPBITS_1; + break; + case FtdiStopBits15: + case FtdiStopBits2: + stop_bits_mode = LL_LPUART_STOPBITS_2; + break; + default: + break; + } + + LL_LPUART_ConfigCharacter(LPUART1, data_width, parity_mode, stop_bits_mode); + if(is_uart_enabled) { + LL_LPUART_Enable(LPUART1); + } +} + +static void ftdi_uart_dma_tx_isr(void* context) { +#if FTDI_UART_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_1 + FtdiUart* ftdi_uart = context; + + if(LL_DMA_IsActiveFlag_TC1(FTDI_UART_LPUART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_TC1(FTDI_UART_LPUART_DMA_INSTANCE); + LL_DMA_DisableChannel(FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL); + //Todo It's a bad idea to wait for the end of the transfer in an interrupt... + while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) + ; + furi_thread_flags_set( + furi_thread_get_id(ftdi_uart->worker_thread), WorkerEventTXDataDmaEnd); + } + +#else +#error Update this code. Would you kindly? +#endif +} + +static void ftdi_uart_tx_dma_init(FtdiUart* ftdi_uart) { + ftdi_uart->buffer_tx_ptr = malloc(FTDI_UART_MAX_TXRX_SIZE); + + LL_DMA_SetPeriphAddress( + FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL, (uint32_t) & (LPUART1->TDR)); + + LL_DMA_ConfigTransfer( + FTDI_UART_LPUART_DMA_INSTANCE, + FTDI_UART_LPUART_DMA_CHANNEL, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_NORMAL | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetPeriphRequest( + FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL, LL_DMAMUX_REQ_LPUART1_TX); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma2Ch1, ftdi_uart_dma_tx_isr, ftdi_uart); + +#if FTDI_UART_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_HT1(FTDI_UART_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_HT1(FTDI_UART_LPUART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TC1(FTDI_UART_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TC1(FTDI_UART_LPUART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TE1(FTDI_UART_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TE1(FTDI_UART_LPUART_DMA_INSTANCE); +#else +#error Update this code. Would you kindly? +#endif + + LL_DMA_EnableIT_TC(FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL); + LL_DMA_ClearFlag_TC1(FTDI_UART_LPUART_DMA_INSTANCE); + LL_LPUART_EnableDMAReq_TX(LPUART1); +} + +static void ftdi_uart_tx_dma_deinit(FtdiUart* ftdi_uart) { + LL_DMA_DisableChannel(FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL); + LL_LPUART_DisableDMAReq_TX(LPUART1); + LL_DMA_DisableIT_TC(FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL); + LL_DMA_ClearFlag_TC1(FTDI_UART_LPUART_DMA_INSTANCE); + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma2Ch1, NULL, NULL); + free(ftdi_uart->buffer_tx_ptr); +} + +static void ftdi_uart_tx_dma(FtdiUart* ftdi_uart, uint8_t* data, size_t size) { + UNUSED(ftdi_uart); + LL_DMA_SetMemoryAddress( + FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL, (uint32_t)data); + LL_DMA_SetDataLength(FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL, size); + + LL_DMA_EnableChannel(FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL); +} \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_uart.h b/flip_tdi/helpers/ftdi_uart.h new file mode 100644 index 0000000..5a49b7a --- /dev/null +++ b/flip_tdi/helpers/ftdi_uart.h @@ -0,0 +1,11 @@ +#pragma once +#include "ftdi.h" + +typedef struct FtdiUart FtdiUart; + +FtdiUart* ftdi_uart_alloc(Ftdi* ftdi); +void ftdi_uart_free(FtdiUart* ftdi_uart); +void ftdi_uart_tx(FtdiUart* ftdi_uart); +void ftdi_uart_set_baudrate(FtdiUart* ftdi_uart, uint32_t baudrate); +void ftdi_uart_set_data_config(FtdiUart* ftdi_uart, FtdiDataConfig* data_config); +void ftdi_uart_enable(FtdiUart* ftdi_uart, bool enable); \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_usb.c b/flip_tdi/helpers/ftdi_usb.c new file mode 100644 index 0000000..fdb5a4c --- /dev/null +++ b/flip_tdi/helpers/ftdi_usb.c @@ -0,0 +1,509 @@ +#include "ftdi_usb.h" +#include +#include "ftdi.h" + +#define TAG "FTDI USB" + +#define FTDI_USB_VID (0x0403) +#define FTDI_USB_PID (0x6014) + +#define FTDI_USB_EP_IN (0x81) +#define FTDI_USB_EP_OUT (0x02) + +#define FTDI_USB_EP_IN_SIZE (64UL) +#define FTDI_USB_EP_OUT_SIZE (64UL) + +#define FTDI_USB_RX_MAX_SIZE (FTDI_USB_EP_OUT_SIZE) +#define FTDI_USB_MODEM_STATUS_SIZE (sizeof(uint16_t)) +#define FTDI_USB_TX_MAX_SIZE (FTDI_USB_EP_IN_SIZE - FTDI_USB_MODEM_STATUS_SIZE) + +typedef struct { + uint16_t status; + uint8_t data[FTDI_USB_TX_MAX_SIZE]; +} FtdiTxData; + +static usbd_respond ftdi_usb_ep_config(usbd_device* dev, uint8_t cfg); +static usbd_respond + ftdi_usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); +static void ftdi_usb_send(usbd_device* dev, uint8_t* buf, uint16_t len); +static int32_t ftdi_usb_receive(usbd_device* dev, uint8_t* buf, uint16_t max_len); + +typedef enum { + EventExit = (1 << 0), + EventReset = (1 << 1), + EventRx = (1 << 2), + EventTx = (1 << 3), + EventTxComplete = (1 << 4), + EventResetSio = (1 << 5), + EventTxImmediate = (1 << 6), + + EventAll = EventExit | EventReset | EventRx | EventTx | EventTxComplete | EventResetSio | + EventTxImmediate, +} FtdiEvent; + +struct FtdiUsb { + FuriHalUsbInterface usb; + FuriHalUsbInterface* usb_prev; + + FuriThread* thread; + usbd_device* dev; + Ftdi* ftdi; + uint8_t data_recvest[8]; + uint16_t data_recvest_len; + + bool tx_complete; + bool tx_immediate; +}; + +static int32_t ftdi_thread_worker(void* context) { + FtdiUsb* ftdi_usb = context; + usbd_device* dev = ftdi_usb->dev; + UNUSED(dev); + + uint32_t len_data = 0; + FtdiTxData tx_data = {0}; + uint16_t* status = ftdi_get_modem_status_uint16_t(ftdi_usb->ftdi); + + tx_data.status = status[0]; + ftdi_usb_send(dev, (uint8_t*)&tx_data, FTDI_USB_MODEM_STATUS_SIZE); + + while(true) { + uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever); + + if(flags & EventRx) { //fast flag + uint8_t buf[FTDI_USB_RX_MAX_SIZE]; + len_data = ftdi_usb_receive(dev, buf, FTDI_USB_RX_MAX_SIZE); + // if(len_data > 0) { + // for(uint32_t i = 0; i < len_data; i++) { + // FURI_LOG_RAW_I("%c", (char)buf[i]); + // } + // FURI_LOG_RAW_I("\r\n"); + // } + if(len_data > 0) { + ftdi_set_rx_buf(ftdi_usb->ftdi, buf, len_data); + ftdi_start_uart_tx(ftdi_usb->ftdi); + } + flags &= ~EventRx; // clear flag + } + + if(flags) { + if(flags & EventResetSio) { + ftdi_reset_sio(ftdi_usb->ftdi); + ftdi_usb_send(dev, (uint8_t*)&tx_data, FTDI_USB_MODEM_STATUS_SIZE); + } + if(flags & EventTxComplete) { + ftdi_usb->tx_complete = true; + if((ftdi_usb->tx_immediate) || (ftdi_available_tx_buf(ftdi_usb->ftdi) != 0)) { + ftdi_reset_latency_timer(ftdi_usb->ftdi); + flags |= EventTx; + } + } + + if(flags & EventTxImmediate) { + ftdi_usb->tx_immediate = true; + if(ftdi_usb->tx_complete) { + flags |= EventTx; + } + } + + if(flags & EventTx) { + ftdi_usb->tx_complete = false; + ftdi_usb->tx_immediate = false; + + tx_data.status = status[0]; + len_data = ftdi_available_tx_buf(ftdi_usb->ftdi); + + if(len_data > 0) { + if(len_data > FTDI_USB_TX_MAX_SIZE) { + len_data = FTDI_USB_TX_MAX_SIZE; + } + len_data = ftdi_get_tx_buf(ftdi_usb->ftdi, tx_data.data, len_data); + ftdi_usb_send(dev, (uint8_t*)&tx_data, len_data + FTDI_USB_MODEM_STATUS_SIZE); + } else { + ftdi_usb_send(dev, (uint8_t*)&tx_data, FTDI_USB_MODEM_STATUS_SIZE); + } + } + + if(flags & EventExit) { + FURI_LOG_I(TAG, "exit"); + break; + } + } + } + + return 0; +} + +static FtdiUsb* ftdi_cur = NULL; + +static void ftdi_usb_callback_tx_immediate(void* context) { + FtdiUsb* ftdi_usb = context; + furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventTxImmediate); +} + +static void ftdi_usb_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); + FtdiUsb* ftdi_usb = ctx; + ftdi_cur = ftdi_usb; + ftdi_usb->dev = dev; + + usbd_reg_config(dev, ftdi_usb_ep_config); + usbd_reg_control(dev, ftdi_usb_control); + usbd_connect(dev, true); + + ftdi_usb->thread = furi_thread_alloc(); + furi_thread_set_name(ftdi_usb->thread, "FtdiUsb"); + furi_thread_set_stack_size(ftdi_usb->thread, 1024); + furi_thread_set_context(ftdi_usb->thread, ctx); + furi_thread_set_callback(ftdi_usb->thread, ftdi_thread_worker); + + ftdi_usb->ftdi = ftdi_alloc(); + ftdi_set_callback_tx_immediate(ftdi_usb->ftdi, ftdi_usb_callback_tx_immediate, ftdi_usb); + + furi_thread_start(ftdi_usb->thread); +} + +static void ftdi_usb_deinit(usbd_device* dev) { + usbd_reg_config(dev, NULL); + usbd_reg_control(dev, NULL); + + FtdiUsb* ftdi_usb = ftdi_cur; + if(!ftdi_usb || ftdi_usb->dev != dev) { + return; + } + ftdi_cur = NULL; + + furi_assert(ftdi_usb->thread); + furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventExit); + furi_thread_join(ftdi_usb->thread); + furi_thread_free(ftdi_usb->thread); + ftdi_usb->thread = NULL; + + ftdi_free(ftdi_usb->ftdi); + + free(ftdi_usb->usb.str_prod_descr); + ftdi_usb->usb.str_prod_descr = NULL; + free(ftdi_usb->usb.str_serial_descr); + ftdi_usb->usb.str_serial_descr = NULL; + free(ftdi_usb); +} + +static void ftdi_usb_send(usbd_device* dev, uint8_t* buf, uint16_t len) { + usbd_ep_write(dev, FTDI_USB_EP_IN, buf, len); +} + +static int32_t ftdi_usb_receive(usbd_device* dev, uint8_t* buf, uint16_t max_len) { + int32_t len = usbd_ep_read(dev, FTDI_USB_EP_OUT, buf, max_len); + return ((len < 0) ? 0 : len); +} + +static void ftdi_usb_wakeup(usbd_device* dev) { + UNUSED(dev); +} + +static void ftdi_usb_suspend(usbd_device* dev) { + FtdiUsb* ftdi_usb = ftdi_cur; + if(!ftdi_usb || ftdi_usb->dev != dev) return; +} + +static void ftdi_usb_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(event); + UNUSED(ep); + FtdiUsb* ftdi_usb = ftdi_cur; + furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventRx); +} + +static void ftdi_usb_tx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(event); + UNUSED(ep); + FtdiUsb* ftdi_usb = ftdi_cur; + furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventTxComplete); +} + +static usbd_respond ftdi_usb_ep_config(usbd_device* dev, uint8_t cfg) { + switch(cfg) { + case 0: // deconfig + usbd_ep_deconfig(dev, FTDI_USB_EP_OUT); + usbd_ep_deconfig(dev, FTDI_USB_EP_IN); + usbd_reg_endpoint(dev, FTDI_USB_EP_OUT, NULL); + usbd_reg_endpoint(dev, FTDI_USB_EP_IN, NULL); + return usbd_ack; + case 1: // config + usbd_ep_config( + dev, FTDI_USB_EP_IN, USB_EPTYPE_BULK /* | USB_EPTYPE_DBLBUF*/, FTDI_USB_EP_IN_SIZE); + usbd_ep_config( + dev, FTDI_USB_EP_OUT, USB_EPTYPE_BULK /* | USB_EPTYPE_DBLBUF*/, FTDI_USB_EP_OUT_SIZE); + usbd_reg_endpoint(dev, FTDI_USB_EP_IN, ftdi_usb_tx_ep_callback); + usbd_reg_endpoint(dev, FTDI_USB_EP_OUT, ftdi_usb_rx_ep_callback); + return usbd_ack; + } + return usbd_fail; +} + +static usbd_respond + ftdi_usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(callback); + UNUSED(dev); + + if(!(req->bmRequestType & (FtdiControlTypeVendor | FtdiControlRecipientDevice))) { + return usbd_fail; + } + + FtdiUsb* ftdi_usb = ftdi_cur; + if(!ftdi_usb || ftdi_usb->dev != dev) { + return usbd_fail; + } + +#ifdef FTDI_DEBUG + furi_log_puts("-----------\r\n"); + char tmp_str[] = "0xFFFFFFFF"; + itoa(req->bmRequestType, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts(" "); + + itoa(req->bRequest, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts(" "); + + itoa(req->wValue, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts(" "); + + itoa(req->wIndex, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts(" "); + + itoa(req->wLength, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts(" \r\n"); +#endif + + switch(req->bmRequestType) { + case FtdiControlRequestsOut: + + switch(req->bRequest) { + case FtdiRequestsSiOReqReset: +#ifdef FTDI_DEBUG + furi_log_puts("ftdi_usb_control OUT\r\n"); +#endif + if(req->wValue == FtdiResetSio) { +#ifdef FTDI_DEBUG + furi_log_puts("FtdiResetSio\r\n"); +#endif + furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventResetSio); + } + if(req->wValue == FtdiResetPurgeRx) { +#ifdef FTDI_DEBUG + furi_log_puts("FtdiResetPurgeRx\r\n"); +#endif + ftdi_reset_purge_rx(ftdi_usb->ftdi); + } + if(req->wValue == FtdiResetPurgeTx) { +#ifdef FTDI_DEBUG + furi_log_puts("FtdiResetPurgeTx\r\n"); +#endif + ftdi_reset_purge_tx(ftdi_usb->ftdi); + } + return usbd_ack; + break; + case FtdiRequestsSiOReqSetBitmode: +#ifdef FTDI_DEBUG + furi_log_puts("FtdiRequestsSiOReqSetBitmode\r\n"); +#endif + ftdi_set_bitmode(ftdi_usb->ftdi, req->wValue, req->wIndex); + return usbd_ack; + break; + case FtdiRequestsSiOReqSetLatencyTimer: +#ifdef FTDI_DEBUG + furi_log_puts("FtdiRequestsSiOReqSetLatencyTimer\r\n"); +#endif + ftdi_set_latency_timer(ftdi_usb->ftdi, req->wValue, req->wIndex); + return usbd_ack; + break; + case FtdiRequestsSiOReqSetEventChar: +#ifdef FTDI_DEBUG + furi_log_puts("FtdiRequestsSiOReqSetEventChar\r\n"); +#endif + //value?????? bool enable: value |= 1 << 8 + return usbd_ack; + break; + case FtdiRequestsSiOReqSetErrorChar: +#ifdef FTDI_DEBUG + furi_log_puts("FtdiRequestsSiOReqSetErrorChar\r\n"); +#endif + //value?????? bool enable: value |= 1 << 8 + return usbd_ack; + break; + case FtdiRequestsSiOReqSetBaudrate: +#ifdef FTDI_DEBUG + furi_log_puts("FtdiRequestsSiOReqSetBaudrate\r\n"); +#endif + ftdi_set_baudrate(ftdi_usb->ftdi, req->wValue, req->wIndex); + return usbd_ack; + break; + case FtdiRequestsSiOReqSetData: +#ifdef FTDI_DEBUG + furi_log_puts("FtdiRequestsSiOReqSetData\r\n"); +#endif + ftdi_set_data_config(ftdi_usb->ftdi, req->wValue, req->wIndex); + return usbd_ack; + break; + case FtdiRequestsSiOReqSetFlowCtrl: +#ifdef FTDI_DEBUG + furi_log_puts("FtdiRequestsSiOReqSetFlowCtrl\r\n"); +#endif + ftdi_set_flow_ctrl(ftdi_usb->ftdi, req->wIndex); + return usbd_ack; + break; + default: + break; + } + + break; + case FtdiControlRequestsIn: +#ifdef FTDI_DEBUG + furi_log_puts("ftdi_usb_control IN\r\n"); +#endif + switch(req->bRequest) { + case FtdiRequestsSiOReqGetLatencyTimer: +#ifdef FTDI_DEBUG + furi_log_puts("FtdiRequestsSiOReqGetLatencyTimer\r\n"); +#endif + ftdi_usb->data_recvest[0] = ftdi_get_latency_timer(ftdi_usb->ftdi); + ftdi_usb->data_recvest_len = 1; + ftdi_usb->dev->status.data_ptr = ftdi_usb->data_recvest; + ftdi_usb->dev->status.data_count = ftdi_usb->data_recvest_len; + return usbd_ack; + break; + + case FtdiRequestsSiOReqPollModemStatus: +#ifdef FTDI_DEBUG + furi_log_puts("FtdiRequestsSiOReqPollModemStatus\r\n"); +#endif + uint16_t* status = ftdi_get_modem_status_uint16_t(ftdi_usb->ftdi); + memcpy(ftdi_usb->data_recvest, status, sizeof(uint16_t)); + ftdi_usb->data_recvest_len = req->wLength; + ftdi_usb->dev->status.data_ptr = ftdi_usb->data_recvest; + ftdi_usb->dev->status.data_count = ftdi_usb->data_recvest_len; + return usbd_ack; + break; + case FtdiRequestsSiOReqReadPins: +#ifdef FTDI_DEBUG + furi_log_puts("FtdiRequestsSiOReqReadPins\r\n"); +#endif + ftdi_usb->data_recvest[0] = ftdi_get_bitbang_gpio(ftdi_usb->ftdi); + ftdi_usb->data_recvest_len = 1; + ftdi_usb->dev->status.data_ptr = ftdi_usb->data_recvest; + ftdi_usb->dev->status.data_count = ftdi_usb->data_recvest_len; + return usbd_ack; + break; + default: + break; + } + break; + default: + break; + } + return usbd_fail; +} + +static const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("FTDI"); +static const struct usb_string_descriptor dev_product_desc = USB_STRING_DESC("FlipTDI"); + +struct FtdiUsbDescriptor { + struct usb_config_descriptor config; + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor ep_in; + struct usb_endpoint_descriptor ep_out; +} __attribute__((packed)); + +static const struct usb_device_descriptor usb_ftdi_dev_descr = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 0, 0), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = USB_SUBCLASS_NONE, + .bDeviceProtocol = USB_PROTO_NONE, + .bMaxPacketSize0 = 8, // USB_EP0_SIZE + .idVendor = FTDI_USB_VID, + .idProduct = FTDI_USB_PID, + .bcdDevice = VERSION_BCD(9, 0, 0), + .iManufacturer = 1, // UsbDevManuf + .iProduct = 2, // UsbDevProduct + .iSerialNumber = NO_DESCRIPTOR, + .bNumConfigurations = 1, +}; + +static const struct FtdiUsbDescriptor usb_ftdi_cfg_descr = { + .config = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct FtdiUsbDescriptor), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED, + .bMaxPower = USB_CFG_POWER_MA(500), + }, + .intf = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR, + .bInterfaceProtocol = USB_PROTO_VENDOR, + .iInterface = 2, + }, + .ep_in = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = FTDI_USB_EP_IN, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = FTDI_USB_EP_IN_SIZE, + .bInterval = 0, + }, + .ep_out = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = FTDI_USB_EP_OUT, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = FTDI_USB_EP_OUT_SIZE, + .bInterval = 0, + }, +}; + +FtdiUsb* ftdi_usb_start(void) { + FtdiUsb* ftdi_usb = malloc(sizeof(FtdiUsb)); + + ftdi_usb->usb_prev = furi_hal_usb_get_config(); + ftdi_usb->usb.init = ftdi_usb_init; + ftdi_usb->usb.deinit = ftdi_usb_deinit; + ftdi_usb->usb.wakeup = ftdi_usb_wakeup; + ftdi_usb->usb.suspend = ftdi_usb_suspend; + ftdi_usb->usb.dev_descr = (struct usb_device_descriptor*)&usb_ftdi_dev_descr; + ftdi_usb->usb.str_manuf_descr = (void*)&dev_manuf_desc; + ftdi_usb->usb.str_prod_descr = (void*)&dev_product_desc; + ftdi_usb->usb.str_serial_descr = NULL; + ftdi_usb->usb.cfg_descr = (void*)&usb_ftdi_cfg_descr; + + if(!furi_hal_usb_set_config(&ftdi_usb->usb, ftdi_usb)) { + FURI_LOG_E(TAG, "USB locked, cannot start Mass Storage"); + free(ftdi_usb->usb.str_prod_descr); + free(ftdi_usb->usb.str_serial_descr); + free(ftdi_usb); + return NULL; + } + return ftdi_usb; +} + +void ftdi_usb_stop(FtdiUsb* ftdi_usb) { + furi_hal_usb_set_config(ftdi_usb->usb_prev, NULL); +} diff --git a/flip_tdi/helpers/ftdi_usb.h b/flip_tdi/helpers/ftdi_usb.h new file mode 100644 index 0000000..dd75e71 --- /dev/null +++ b/flip_tdi/helpers/ftdi_usb.h @@ -0,0 +1,6 @@ +#pragma once + +typedef struct FtdiUsb FtdiUsb; + +FtdiUsb* ftdi_usb_start(void); +void ftdi_usb_stop(FtdiUsb* ftdi); diff --git a/flip_tdi/helpers/ftdi_usb_define.h b/flip_tdi/helpers/ftdi_usb_define.h new file mode 100644 index 0000000..fe2d959 --- /dev/null +++ b/flip_tdi/helpers/ftdi_usb_define.h @@ -0,0 +1,289 @@ +#pragma once +#include +#include +#include + +// # Clocks and baudrates +#define FTDIBUS_CLOCK_BASE 6000000UL +#define FTDIBUS_CLOCK_HIGH 30000000UL +#define FTDI_BITBANG_BAUDRATE_RATIO_BASE 16 +#define FTDI_BITBANG_BAUDRATE_RATIO_HIGH 5 +#define FTDI_BAUDRATE_REF_BASE 3000000UL +#define FTDI_BAUDRATE_REF_HIGH 12000000UL + +/*descriptor type*/ +typedef enum { + FtdiDescriptorTypeDevice = 0x01, + FtdiDescriptorTypeConfig = 0x02, + FtdiDescriptorTypeString = 0x03, + FtdiDescriptorTypeInterface = 0x04, + FtdiDescriptorTypeEndpoint = 0x05, +} FtdiDescriptorType; + +/*endpoint direction*/ +typedef enum { + FtdiEndpointIn = 0x80, + FtdiEndpointOut = 0x00, +} FtdiEndpointDirection; + +/*endpoint type*/ +typedef enum { + FtdiEndpointTypeCtrl = 0x00, + FtdiEndpointTypeIso = 0x01, + FtdiEndpointTypeBulk = 0x02, + FtdiEndpointTypeIntr = 0x03, +} FtdiEndpointType; + +/*control request type*/ +typedef enum { + FtdiControlTypeStandard = (0 << 5), + FtdiControlTypeClass = (1 << 5), + FtdiControlTypeVendor = (2 << 5), + FtdiControlTypeReserved = (3 << 5), +} FtdiControlType; + +/*control request recipient*/ +typedef enum { + FtdiControlRecipientDevice = 0, + FtdiControlRecipientInterface = 1, + FtdiControlRecipientEndpoint = 2, + FtdiControlRecipientOther = 3, +} FtdiControlRecipient; + +/*control request direction*/ +typedef enum { + FtdiControlOut = 0x00, + FtdiControlIn = 0x80, +} FtdiControlDirection; + +/*endpoint address mask*/ +typedef enum { + FtdiEndpointAddrMask = 0x0f, + FtdiEndpointDirMask = 0x80, + FtdiEndpointTransferTypeMask = 0x03, + FtdiCtrlDirMask = 0x80, +} FtdiEndpointMask; + +// typedef enum { +// FtdiBitModeReset = 0x00, /**< switch off bitbang mode, back to regular serial/FIFO */ +// FtdiBitModeBitbang = +// 0x01, /**< classical asynchronous bitbang mode, introduced with B-type chips */ +// FtdiBitModeMpsse = 0x02, /**< MPSSE mode, available on 2232x chips */ +// FtdiBitModeSyncbb = 0x04, /**< synchronous bitbang mode, available on 2232x and R-type chips */ +// FtdiBitModeMcu = 0x08, /**< MCU Host Bus Emulation mode, available on 2232x chips */ +// /* CPU-style fifo mode gets set via EEPROM */ +// FtdiBitModeOpto = +// 0x10, /**< Fast Opto-Isolated Serial Interface Mode, available on 2232x chips */ +// FtdiBitModeCbus = +// 0x20, /**< Bitbang on CBUS pins of R-type chips, configure in EEPROM before */ +// FtdiBitModeSyncff = +// 0x40, /**< Single Channel Synchronous FIFO mode, available on 2232H chips */ +// FtdiBitModeFt1284 = 0x80, /**< FT1284 mode, available on 232H chips */ +// } FtdiBitMode; + +//if(FtdiBitMode&0xff00==0) FtdiBitModeReset switch off bitbang mode, back to regular serial/FIFO +typedef struct { + //uint8_t MASK : 8; /*Mask*/ + uint8_t BITBANG : 1; /*classical asynchronous bitbang mode, introduced with B-type chips*/ + uint8_t MPSSE : 1; /*MPSSE mode, available on 2232x chips*/ + uint8_t SYNCBB : 1; /*synchronous bitbang mode, available on 2232x and R-type chips*/ + uint8_t MCU : 1; /*MCU Host Bus Emulation mode, available on 2232x chips*/ + uint8_t OPTO : 1; /*Fast Opto-Isolated Serial Interface Mode, available on 2232x chips*/ + uint8_t CBUS : 1; /*Bitbang on CBUS pins of R-type chips, configure in EEPROM before*/ + uint8_t SYNCFF : 1; /*Single Channel Synchronous FIFO mode, available on 2232H chips*/ + uint8_t FT1284 : 1; /*FT1284 mode, available on 232H chips*/ +} FtdiBitMode; +static_assert(sizeof(FtdiBitMode) == sizeof(uint8_t), "Wrong FtdiBitMode"); +/* FTDI MPSSE commands */ +typedef enum { + + // /* Shifting commands IN MPSSE Mode*/ + // #define MPSSE_WRITE_NEG 0x01 /* Write TDI/DO on negative TCK/SK edge*/ + // #define MPSSE_BITMODE 0x02 /* Write bits, not bytes */ + // #define MPSSE_READ_NEG 0x04 /* Sample TDO/DI on negative TCK/SK edge */ + // #define MPSSE_LSB 0x08 /* LSB first */ + // #define MPSSE_DO_WRITE 0x10 /* Write TDI/DO */ + // #define MPSSE_DO_READ 0x20 /* Read TDO/DI */ + // #define MPSSE_WRITE_TMS 0x40 /* Write TMS/CS */ + + /*MPSSE Commands*/ + FtdiMpsseCommandsWriteBytesPveMsb = 0x10, /**< Write bytes with positive edge clock, MSB first */ + FtdiMpsseCommandsWriteBytesNveMsb = 0x11, /**< Write bytes with negative edge clock, MSB first */ + FtdiMpsseCommandsWriteBitsPveMsb = 0x12, /**< Write bits with positive edge clock, MSB first */ + FtdiMpsseCommandsWriteBitsNveMsb = 0x13, /**< Write bits with negative edge clock, MSB first */ + FtdiMpsseCommandsWriteBytesPveLsb = 0x18, /**< Write bytes with positive edge clock, LSB first */ + FtdiMpsseCommandsWriteBytesNveLsb = 0x19, /**< Write bytes with negative edge clock, LSB first */ + FtdiMpsseCommandsWriteBitsPveLsb = 0x1a, /**< Write bits with positive edge clock, LSB first */ + FtdiMpsseCommandsWriteBitsNveLsb = 0x1b, /**< Write bits with negative edge clock, LSB first */ + FtdiMpsseCommandsReadBytesPveMsb = 0x20, /**< Read bytes with positive edge clock, MSB first */ + FtdiMpsseCommandsReadBytesNveMsb = 0x24, /**< Read bytes with negative edge clock, MSB first */ + FtdiMpsseCommandsReadBitsPveMsb = 0x22, /**< Read bits with positive edge clock, MSB first */ + FtdiMpsseCommandsReadBitsNveMsb = 0x26, /**< Read bits with negative edge clock, MSB first */ + FtdiMpsseCommandsReadBytesPveLsb = 0x28, /**< Read bytes with positive edge clock, LSB first */ + FtdiMpsseCommandsReadBytesNveLsb = 0x2c, /**< Read bytes with negative edge clock, LSB first */ + FtdiMpsseCommandsReadBitsPveLsb = 0x2a, /**< Read bits with positive edge clock, LSB first */ + FtdiMpsseCommandsReadBitsNveLsb = 0x2e, /**< Read bits with negative edge clock, LSB first */ + FtdiMpsseCommandsRwBytesPveNveMsb = 0x31, /**< Read/Write bytes with positive edge clock, MSB first */ + FtdiMpsseCommandsRwBytesNvePveMsb = 0x34, /**< Read/Write bytes with negative edge clock, MSB first */ + FtdiMpsseCommandsRwBitsPveNveMsb = 0x33, /**< Read/Write bits with positive edge clock, MSB first */ + FtdiMpsseCommandsRwBitsNvePveMsb = 0x36, /**< Read/Write bits with negative edge clock, MSB first */ + FtdiMpsseCommandsRwBytesPveNveLsb = 0x39, /**< Read/Write bytes with positive edge clock, LSB first */ + FtdiMpsseCommandsRwBytesNvePveLsb = 0x3c, /**< Read/Write bytes with negative edge clock, LSB first */ + FtdiMpsseCommandsRwBitsPveNveLsb = 0x3b, /**< Read/Write bits with positive edge clock, LSB first */ + FtdiMpsseCommandsRwBitsNvePveLsb = 0x3e, /**< Read/Write bits with negative edge clock, LSB first */ + FtdiMpsseCommandsWriteBitsTmsPve = 0x4a, /**< Write bits with TMS, positive edge clock */ + FtdiMpsseCommandsWriteBitsTmsNve = 0x4b, /**< Write bits with TMS, negative edge clock */ + FtdiMpsseCommandsRwBitsTmsPvePve = 0x6a, /**< Read/Write bits with TMS, positive edge clock, MSB first */ + FtdiMpsseCommandsRwBitsTmsPveNve = 0x6b, /**< Read/Write bits with TMS, positive edge clock, MSB first */ + FtdiMpsseCommandsRwBitsTmsNvePve = 0x6e, /**< Read/Write bits with TMS, negative edge clock, MSB first */ + FtdiMpsseCommandsRwBitsTmsNveNve = 0x6f, /**< Read/Write bits with TMS, negative edge clock, MSB first */ + + + FtdiMpsseCommandsSetBitsLow = 0x80, /**< Change LSB GPIO output */ + /*BYTE DATA*/ + /*BYTE Direction*/ + FtdiMpsseCommandsSetBitsHigh = 0x82, /**< Change MSB GPIO output */ + /*BYTE DATA*/ + /*BYTE Direction*/ + FtdiMpsseCommandsGetBitsLow = 0x81, /**< Get LSB GPIO output */ + FtdiMpsseCommandsGetBitsHigh = 0x83, /**< Get MSB GPIO output */ + FtdiMpsseCommandsLoopbackStart = 0x84, /**< Enable loopback */ + FtdiMpsseCommandsLoopbackEnd = 0x85, /**< Disable loopback */ + FtdiMpsseCommandsSetTckDivisor = 0x86, /**< Set clock */ + /* H Type specific commands */ + FtdiMpsseCommandsDisDiv5 = 0x8a, /**< Disable divide by 5 */ + FtdiMpsseCommandsEnDiv5 = 0x8b, /**< Enable divide by 5 */ + FtdiMpsseCommandsEnableClk3Phase = 0x8c, /**< Enable 3-phase data clocking (I2C) */ + FtdiMpsseCommandsDisableClk3Phase = 0x8d, /**< Disable 3-phase data clocking */ + FtdiMpsseCommandsClkBitsNoData = 0x8e, /**< Allows JTAG clock to be output w/o data */ + FtdiMpsseCommandsClkBytesNoData = 0x8f, /**< Allows JTAG clock to be output w/o data */ + FtdiMpsseCommandsClkWaitOnHigh = 0x94, /**< Clock until GPIOL1 is high */ + FtdiMpsseCommandsClkWaitOnLow = 0x95, /**< Clock until GPIOL1 is low */ + FtdiMpsseCommandsEnableClkAdaptive = 0x96, /**< Enable JTAG adaptive clock for ARM */ + FtdiMpsseCommandsDisableClkAdaptive = 0x97, /**< Disable JTAG adaptive clock */ + FtdiMpsseCommandsClkCountWaitOnHigh = 0x9c, /**< Clock byte cycles until GPIOL1 is high */ + FtdiMpsseCommandsClkCountWaitOnLow = 0x9d, /**< Clock byte cycles until GPIOL1 is low */ + //FT232H only + FtdiMpsseCommandsDriveZero = 0x9e, /**< Drive-zero mode */ + /* Commands in MPSSE and Host Emulation Mode */ + FtdiMpsseCommandsSendImmediate = 0x87, /**< Send immediate */ + FtdiMpsseCommandsWaitOnHigh = 0x88, /**< Wait until GPIOL1 is high */ + FtdiMpsseCommandsWaitOnLow = 0x89, /**< Wait until GPIOL1 is low */ + /* Commands in Host Emulation Mode */ + FtdiMpsseCommandsReadShort = 0x90, /**< Read short */ + FtdiMpsseCommandsReadExtended = 0x91, /**< Read extended */ + FtdiMpsseCommandsWriteShort = 0x92, /**< Write short */ + FtdiMpsseCommandsWriteExtended = 0x93, /**< Write extended */ + +} FtdiMpsseCommands; + +/* USB control requests */ +typedef enum { + FtdiControlRequestsOut = (FtdiControlTypeVendor | FtdiControlRecipientDevice | FtdiControlOut), + FtdiControlRequestsIn = (FtdiControlTypeVendor | FtdiControlRecipientDevice | FtdiControlIn), +} FtdiControlRequests; + +/* Requests */ +typedef enum { + FtdiRequestsSiOReqReset = 0x0, /**< Reset the port */ + FtdiRequestsSiOReqSetModemCtrl = 0x1, /**< Set the modem control register */ + FtdiRequestsSiOReqSetFlowCtrl = 0x2, /**< Set flow control register */ + FtdiRequestsSiOReqSetBaudrate = 0x3, /**< Set baud rate */ + FtdiRequestsSiOReqSetData = 0x4, /**< Set the data characteristics of the port */ + FtdiRequestsSiOReqPollModemStatus = 0x5, /**< Get line status */ + FtdiRequestsSiOReqSetEventChar = 0x6, /**< Change event character */ + FtdiRequestsSiOReqSetErrorChar = 0x7, /**< Change error character */ + FtdiRequestsSiOReqSetLatencyTimer = 0x9, /**< Change latency timer */ + FtdiRequestsSiOReqGetLatencyTimer = 0xa, /**< Get latency timer */ + FtdiRequestsSiOReqSetBitmode = 0xb, /**< Change bit mode */ + FtdiRequestsSiOReqReadPins = 0xc, /**< Read GPIO pin value (or "get bitmode") */ + FtdiRequestsSiOReqReadEeprom = 0x90, /**< Read EEPROM */ + FtdiRequestsSiOReqWriteEeprom = 0x91, /**< Write EEPROM */ + FtdiRequestsSiOReqEraseEeprom = 0x92, /**< Erase EEPROM */ +} FtdiRequests; + +/*Eeprom requests*/ +typedef enum { + FtdiEepromRequestsEeprom = 0x90, + FtdiEepromRequestsReadEeprom = FtdiEepromRequestsEeprom + 0, /**< Read EEPROM content */ + FtdiEepromRequestsWriteEeprom = FtdiEepromRequestsEeprom + 1, /**< Write EEPROM content */ + FtdiEepromRequestsEraseEeprom = FtdiEepromRequestsEeprom + 2, /**< Erase EEPROM content */ +} FtdiEepromRequests; + +/*Reset arguments*/ +typedef enum { + FtdiResetSio = 0, /**< Reset device */ + FtdiResetPurgeRx = 1, /**< Drain USB RX buffer (host-to-ftdi) */ + FtdiResetPurgeTx = 2, /**< Drain USB TX buffer (ftdi-to-host) */ +} FtdiReset; + +/*Flow control arguments*/ +typedef enum { + FtdiFlowControlDisable = 0x0, + FtdiFlowControlRtsCtsHs = 0x1 << 8, + FtdiFlowControlDtrDsrHs = 0x2 << 8, + FtdiFlowControlXonXoffHs = 0x4 << 8, + FtdiFlowControlSetDtrMask = 0x1, + FtdiFlowControlSetDtrHigh = FtdiFlowControlSetDtrMask | (FtdiFlowControlSetDtrMask << 8), + FtdiFlowControlSetDtrLow = 0x0 | (FtdiFlowControlSetDtrMask << 8), + FtdiFlowControlSetRtsMask = 0x2, + FtdiFlowControlSetRtsHigh = FtdiFlowControlSetRtsMask | (FtdiFlowControlSetRtsMask << 8), + FtdiFlowControlSetRtsLow = 0x0 | (FtdiFlowControlSetRtsMask << 8), +} FtdiFlowControl; + +/*Parity bits */ +typedef enum { + FtdiParityNone = 0, + FtdiParityOdd = 1, + FtdiParityEven = 2, + FtdiParityMark = 3, + FtdiParitySpace = 4, +} FtdiParity; + +/*Number of stop bits*/ +typedef enum { + FtdiStopBits1 = 0, + FtdiStopBits15 = 1, + FtdiStopBits2 = 2, +} FtdiStopBits; + +/*Number of bits*/ +typedef enum { + FtdiBits7 = 7, + FtdiBits8 = 8, +} FtdiBits; + +/*Break type*/ +typedef enum { + FtdiBreakOff = 0, + FtdiBreakOn = 1, +} FtdiBreak; + +typedef struct { + uint8_t BITS : 4; /*Cound data bits*/ + uint8_t RESERVED : 4; /*Reserved0*/ + uint8_t PARITY : 3; /*Parity*/ + uint8_t STOP_BITS : 2; /*Number of stop bits*/ + uint8_t BREAK : 1; /*Break type*/ +} FtdiDataConfig; +static_assert(sizeof(FtdiDataConfig) == sizeof(uint16_t), "Wrong FtdiDataConfig"); + +typedef struct { + uint8_t RESERVED0 : 1; /*Reserved0*/ + uint8_t RESERVED1 : 1; /*Reserved1*/ + uint8_t RESERVED2 : 1; /*Reserved2*/ + uint8_t RESERVED3 : 1; /*Reserved3*/ + uint8_t CTS : 1; /*Clear to send (CTS)*/ + uint8_t DTR : 1; /*Data set ready (DTR)*/ + uint8_t RI : 1; /*Ring indicator (RI)*/ + uint8_t RLSD : 1; /*Receive line signal detect (RLSD)*/ + uint8_t DR : 1; /*Data ready (DR)*/ + uint8_t OE : 1; /*Overrun error (OE)*/ + uint8_t PE : 1; /*Parity error (PE)*/ + uint8_t FE : 1; /*Framing error (FE)*/ + uint8_t BI : 1; /*Break interrupt (BI)*/ + uint8_t THRE : 1; /*Transmitter holding register (THRE)*/ + uint8_t TEMT : 1; /*Transmitter empty (TEMT)*/ + uint8_t RCVR_FIFO : 1; /*Error in RCVR FIFO*/ +} FtdiModemStatus; +static_assert(sizeof(FtdiModemStatus) == sizeof(uint16_t), "Wrong FtdiModemStatus"); diff --git a/flip_tdi/images/flip_tdi.png b/flip_tdi/images/flip_tdi.png new file mode 100644 index 0000000..d75ee75 Binary files /dev/null and b/flip_tdi/images/flip_tdi.png differ diff --git a/flip_tdi/images/flip_tdi_wiring_gpio.png b/flip_tdi/images/flip_tdi_wiring_gpio.png new file mode 100644 index 0000000..e80941a Binary files /dev/null and b/flip_tdi/images/flip_tdi_wiring_gpio.png differ diff --git a/flip_tdi/images/flip_tdi_wiring_spi.png b/flip_tdi/images/flip_tdi_wiring_spi.png new file mode 100644 index 0000000..ddb95e6 Binary files /dev/null and b/flip_tdi/images/flip_tdi_wiring_spi.png differ diff --git a/flip_tdi/images/flip_tdi_wiring_uart.png b/flip_tdi/images/flip_tdi_wiring_uart.png new file mode 100644 index 0000000..251641f Binary files /dev/null and b/flip_tdi/images/flip_tdi_wiring_uart.png differ diff --git a/flip_tdi/scenes/flip_tdi_scene.c b/flip_tdi/scenes/flip_tdi_scene.c new file mode 100644 index 0000000..17e9ed1 --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene.c @@ -0,0 +1,31 @@ +#include "../flip_tdi_app_i.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const flip_tdi_scene_on_enter_handlers[])(void*) = { +#include "flip_tdi_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const flip_tdi_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = + { +#include "flip_tdi_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const flip_tdi_scene_on_exit_handlers[])(void* context) = { +#include "flip_tdi_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers flip_tdi_scene_handlers = { + .on_enter_handlers = flip_tdi_scene_on_enter_handlers, + .on_event_handlers = flip_tdi_scene_on_event_handlers, + .on_exit_handlers = flip_tdi_scene_on_exit_handlers, + .scene_num = FlipTDISceneNum, +}; diff --git a/flip_tdi/scenes/flip_tdi_scene.h b/flip_tdi/scenes/flip_tdi_scene.h new file mode 100644 index 0000000..9f846b6 --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FlipTDIScene##id, +typedef enum { +#include "flip_tdi_scene_config.h" + FlipTDISceneNum, +} FlipTDIScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers flip_tdi_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "flip_tdi_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "flip_tdi_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "flip_tdi_scene_config.h" +#undef ADD_SCENE diff --git a/flip_tdi/scenes/flip_tdi_scene_about.c b/flip_tdi/scenes/flip_tdi_scene_about.c new file mode 100644 index 0000000..c8d077c --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene_about.c @@ -0,0 +1,74 @@ +#include "../flip_tdi_app_i.h" +#include "../helpers/flip_tdi_types.h" + +void flip_tdi_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void flip_tdi_scene_about_on_enter(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + FuriString* temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", FAP_VERSION); + furi_string_cat_printf(temp_str, "Developed by: %s\n", FLIP_TDI_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", FLIP_TDI_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf(temp_str, "Emulator FT232H.\n\n"); + + furi_string_cat_printf(temp_str, "\e#%s\n", "What it can do:"); + furi_string_cat_printf(temp_str, "- Emulate FT232H VCP mode\n"); + furi_string_cat_printf(temp_str, "- Emulate FT232H Async bitbang mode, max freq 50kHz \n"); + furi_string_cat_printf(temp_str, "- Emulate FT232H Sync bitbang mode, max freq 70kHz \n"); + furi_string_cat_printf(temp_str, "- Emulate FT232H MPSSE mode, max freq 70kHz \n"); + furi_string_cat_printf( + temp_str, + "- Emulate FT232H SPI mode (half duplex), fix freq 2MHz, max block size 4096 byte\n"); + + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + app->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! FlipTDI \e!\n", + false); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, FlipTDIViewWidget); +} + +bool flip_tdi_scene_about_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void flip_tdi_scene_about_on_exit(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + // Clear views + widget_reset(app->widget); +} diff --git a/flip_tdi/scenes/flip_tdi_scene_config.h b/flip_tdi/scenes/flip_tdi_scene_config.h new file mode 100644 index 0000000..188c373 --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene_config.h @@ -0,0 +1,6 @@ +ADD_SCENE(flip_tdi, main, Main) +ADD_SCENE(flip_tdi, menu, Menu) +ADD_SCENE(flip_tdi, wiring_uart, WiringUart) +ADD_SCENE(flip_tdi, wiring_spi, WiringSpi) +ADD_SCENE(flip_tdi, wiring_gpio, WiringGpio) +ADD_SCENE(flip_tdi, about, About) diff --git a/flip_tdi/scenes/flip_tdi_scene_main.c b/flip_tdi/scenes/flip_tdi_scene_main.c new file mode 100644 index 0000000..1e77b01 --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene_main.c @@ -0,0 +1,67 @@ +#include "../flip_tdi_app_i.h" +#include "../views/flip_tdi_view_main.h" + +void flip_tdi_scene_main_callback(FlipTDICustomEvent event, void* context) { + furi_assert(context); + FlipTDIApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +// static const NotificationSequence notification_app_display_on = { + +// &message_display_backlight_on, +// NULL, +// }; + +static void flip_tdi_scene_main_update(void* context) { + furi_assert(context); + FlipTDIApp* app = context; + UNUSED(app); + + // uint32_t frequency = 0; + // bool nfc_field = flip_tdi_app_field_presence_is_nfc(app); + // bool rfid_field = flip_tdi_app_field_presence_is_rfid(app, &frequency); + + // if(nfc_field || rfid_field) + // notification_message(app->notifications, ¬ification_app_display_on); + + // flip_tdi_view_field_presence_update( + // app->flip_tdi_field_presence, nfc_field, rfid_field, frequency); +} + +void flip_tdi_scene_main_on_enter(void* context) { + furi_assert(context); + FlipTDIApp* app = context; + + flip_tdi_view_main_set_callback( + app->flip_tdi_view_main_instance, flip_tdi_scene_main_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, FlipTDIViewMain); +} + +bool flip_tdi_scene_main_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + FlipTDIApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case FlipTDICustomEventMainMore: + scene_manager_next_scene(app->scene_manager, FlipTDISceneMenu); + break; + default: + break; + } + + } else if(event.type == SceneManagerEventTypeTick) { + flip_tdi_scene_main_update(app); + } + + return consumed; +} + +void flip_tdi_scene_main_on_exit(void* context) { + furi_assert(context); + FlipTDIApp* app = context; + UNUSED(app); +} diff --git a/flip_tdi/scenes/flip_tdi_scene_wiring_gpio.c b/flip_tdi/scenes/flip_tdi_scene_wiring_gpio.c new file mode 100644 index 0000000..a783851 --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene_wiring_gpio.c @@ -0,0 +1,21 @@ +#include "../flip_tdi_app_i.h" + +void flip_tdi_scene_wiring_gpio_on_enter(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + widget_add_icon_element(app->widget, 0, 0, &I_flip_tdi_wiring_gpio); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipTDIViewWidget); +} + +bool flip_tdi_scene_wiring_gpio_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} +void flip_tdi_scene_wiring_gpio_on_exit(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + widget_reset(app->widget); +} diff --git a/flip_tdi/scenes/flip_tdi_scene_wiring_spi.c b/flip_tdi/scenes/flip_tdi_scene_wiring_spi.c new file mode 100644 index 0000000..abdaf60 --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene_wiring_spi.c @@ -0,0 +1,21 @@ +#include "../flip_tdi_app_i.h" + +void flip_tdi_scene_wiring_spi_on_enter(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + widget_add_icon_element(app->widget, 0, 0, &I_flip_tdi_wiring_spi); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipTDIViewWidget); +} + +bool flip_tdi_scene_wiring_spi_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} +void flip_tdi_scene_wiring_spi_on_exit(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + widget_reset(app->widget); +} diff --git a/flip_tdi/scenes/flip_tdi_scene_wiring_uart.c b/flip_tdi/scenes/flip_tdi_scene_wiring_uart.c new file mode 100644 index 0000000..a849ae1 --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene_wiring_uart.c @@ -0,0 +1,21 @@ +#include "../flip_tdi_app_i.h" + +void flip_tdi_scene_wiring_uart_on_enter(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + widget_add_icon_element(app->widget, 0, 0, &I_flip_tdi_wiring_uart); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipTDIViewWidget); +} + +bool flip_tdi_scene_wiring_uart_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} +void flip_tdi_scene_wiring_uart_on_exit(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + widget_reset(app->widget); +} diff --git a/flip_tdi/scenes/subghz_scene_menu.c b/flip_tdi/scenes/subghz_scene_menu.c new file mode 100644 index 0000000..a1039d6 --- /dev/null +++ b/flip_tdi/scenes/subghz_scene_menu.c @@ -0,0 +1,60 @@ +#include "../flip_tdi_app_i.h" + +void flip_tdi_scene_menu_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + FlipTDIApp* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void flip_tdi_scene_menu_on_enter(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + Submenu* submenu = app->submenu; + submenu_add_item( + submenu, "WiringUart", SubmenuIndexWiringUart, flip_tdi_scene_menu_submenu_callback, app); + submenu_add_item( + submenu, "WiringSpi", SubmenuIndexWiringSpi, flip_tdi_scene_menu_submenu_callback, app); + submenu_add_item( + submenu, "WiringGpio", SubmenuIndexWiringGpio, flip_tdi_scene_menu_submenu_callback, app); + submenu_add_item( + submenu, "About", SubmenuIndexAbout, flip_tdi_scene_menu_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, FlipTDISceneMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, FlipTDIViewSubmenu); +} + +bool flip_tdi_scene_menu_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + + FlipTDIApp* app = context; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexAbout) { + scene_manager_next_scene(app->scene_manager, FlipTDISceneAbout); + consumed = true; + } else if(event.event == SubmenuIndexWiringUart) { + scene_manager_next_scene(app->scene_manager, FlipTDISceneWiringUart); + consumed = true; + } else if(event.event == SubmenuIndexWiringSpi) { + scene_manager_next_scene(app->scene_manager, FlipTDISceneWiringSpi); + consumed = true; + } else if(event.event == SubmenuIndexWiringGpio) { + scene_manager_next_scene(app->scene_manager, FlipTDISceneWiringGpio); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, FlipTDIViewSubmenu, event.event); + } + + return consumed; +} + +void flip_tdi_scene_menu_on_exit(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + submenu_reset(app->submenu); +} diff --git a/flip_tdi/views/flip_tdi_view_main.c b/flip_tdi/views/flip_tdi_view_main.c new file mode 100644 index 0000000..6705604 --- /dev/null +++ b/flip_tdi/views/flip_tdi_view_main.c @@ -0,0 +1,89 @@ +#include "flip_tdi_view_main.h" +#include "../flip_tdi_app_i.h" + +#include +#include + +struct FlipTDIViewMainType { + View* view; + FlipTDIViewMainTypeCallback callback; + void* context; +}; + +typedef struct { + uint32_t test; +} FlipTDIViewMainTypeModel; + +void flip_tdi_view_main_set_callback( + FlipTDIViewMainType* instance, + FlipTDIViewMainTypeCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void flip_tdi_view_main_draw(Canvas* canvas, FlipTDIViewMainTypeModel* model) { + UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_icon(canvas, 0, 0, &I_flip_tdi); + elements_button_right(canvas, "More"); +} + +bool flip_tdi_view_main_input(InputEvent* event, void* context) { + furi_assert(context); + FlipTDIViewMainType* instance = context; + UNUSED(instance); + + if(event->key == InputKeyBack) { + return false; + } else if(event->key == InputKeyRight && event->type == InputTypeShort) { + instance->callback(FlipTDICustomEventMainMore, instance->context); + } + return true; +} + +void flip_tdi_view_main_enter(void* context) { + furi_assert(context); + FlipTDIViewMainType* instance = context; + with_view_model(instance->view, FlipTDIViewMainTypeModel * model, { model->test = 0; }, true); +} + +void flip_tdi_view_main_exit(void* context) { + furi_assert(context); + FlipTDIViewMainType* instance = context; + UNUSED(instance); +} + +FlipTDIViewMainType* flip_tdi_view_main_alloc() { + FlipTDIViewMainType* instance = malloc(sizeof(FlipTDIViewMainType)); + + // View allocation and configuration + instance->view = view_alloc(); + + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipTDIViewMainTypeModel)); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)flip_tdi_view_main_draw); + view_set_input_callback(instance->view, flip_tdi_view_main_input); + view_set_enter_callback(instance->view, flip_tdi_view_main_enter); + view_set_exit_callback(instance->view, flip_tdi_view_main_exit); + + with_view_model(instance->view, FlipTDIViewMainTypeModel * model, { model->test = 0; }, true); + return instance; +} + +void flip_tdi_view_main_free(FlipTDIViewMainType* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* flip_tdi_view_main_get_view(FlipTDIViewMainType* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/flip_tdi/views/flip_tdi_view_main.h b/flip_tdi/views/flip_tdi_view_main.h new file mode 100644 index 0000000..bfb6e4b --- /dev/null +++ b/flip_tdi/views/flip_tdi_view_main.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "../helpers/flip_tdi_types.h" +#include "../helpers/flip_tdi_event.h" + +typedef struct FlipTDIViewMainType FlipTDIViewMainType; + +typedef void (*FlipTDIViewMainTypeCallback)(FlipTDICustomEvent event, void* context); + +void flip_tdi_view_main_set_callback( + FlipTDIViewMainType* instance, + FlipTDIViewMainTypeCallback callback, + void* context); + +FlipTDIViewMainType* flip_tdi_view_main_alloc(); + +void flip_tdi_view_main_free(FlipTDIViewMainType* instance); + +View* flip_tdi_view_main_get_view(FlipTDIViewMainType* instance);