diff --git a/src/scenes/mtp/main.c b/src/scenes/mtp/main.c index 0d6613d..2380f06 100644 --- a/src/scenes/mtp/main.c +++ b/src/scenes/mtp/main.c @@ -3,6 +3,7 @@ #include "../../main.h" #include "main.h" #include +#include "usb.h" #define THIS_SCENE MTP @@ -15,7 +16,7 @@ AppMTP* MTP_alloc() { view_set_context(about->view, about); view_set_draw_callback(about->view, MTP_on_draw); - about->usb_connected = true; + about->usb_connected = false; tmp = about; return about; @@ -79,6 +80,13 @@ void MTP_on_enter(void* context) { App* app = (App*)context; view_dispatcher_switch_to_view(app->view_dispatcher, THIS_SCENE); + furi_assert(app->allocated_scenes != NULL, "App allocated scenes is NULL"); + + AppMTP* mtp = app->allocated_scenes[THIS_SCENE]; + if(mtp != NULL) { + mtp->old_usb = furi_hal_usb_get_config(); + furi_hal_usb_set_config(&usb_mtp_interface, mtp); + } } bool MTP_on_event(void* context, SceneManagerEvent event) { @@ -98,6 +106,9 @@ void MTP_on_exit(void* context) { return; } + // revert to old usb mode + furi_hal_usb_set_config(tmp->old_usb, NULL); + // if(app->view_dispatcher) view_dispatcher_switch_to_view(app->view_dispatcher, Home); // if(app->scene_manager) scene_manager_previous_scene(app->scene_manager); } diff --git a/src/scenes/mtp/main.h b/src/scenes/mtp/main.h index 95d27bc..667a7ab 100644 --- a/src/scenes/mtp/main.h +++ b/src/scenes/mtp/main.h @@ -2,12 +2,18 @@ #include #include #include +#include typedef struct AppMTP { Submenu* menu; View* view; bool usb_connected; + + // usb stuff + FuriHalUsbInterface* old_usb; + FuriThread* worker_thread; + usbd_device* dev; } AppMTP; AppMTP* MTP_alloc(); diff --git a/src/scenes/mtp/usb.c b/src/scenes/mtp/usb.c new file mode 100644 index 0000000..a67f989 --- /dev/null +++ b/src/scenes/mtp/usb.c @@ -0,0 +1,97 @@ +#include "main.h" +#include "usb.h" +#include +#include + +// Define MTP specific request constants +#define MTP_REQ_GET_DEVICE_STATUS 0x67 +#define MTP_REQ_SEND_DATA 0x68 +#define MTP_REQ_GET_DATA 0x69 + +AppMTP* global_mtp; + +typedef enum { + EventExit = 1 << 0, + EventReset = 1 << 1, + EventRxTx = 1 << 2, + + EventAll = EventExit | EventReset | EventRxTx, +} MTPEvent; + +int32_t usb_mtp_worker(void* ctx) { + AppMTP* mtp = ctx; + usbd_device* dev = mtp->dev; + while(true) { + furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever); + if(furi_thread_flags_get() & EventExit) break; + + if(furi_thread_flags_get() & EventReset) { + furi_thread_flags_clear(EventReset); + furi_hal_usb_set_config(mtp->old_usb, NULL); + } + + if(furi_thread_flags_get() & EventRxTx) { + furi_thread_flags_clear(EventRxTx); + // Handle MTP RX/TX data here + // Implement the logic for processing MTP requests, sending responses, etc. + } + } + return 0; +} + +void usb_mtp_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); + AppMTP* mtp = ctx; + global_mtp = mtp; + + usbd_reg_control(dev, usb_mtp_control); + usbd_connect(dev, true); + + mtp->worker_thread = furi_thread_alloc(); + furi_thread_set_name(mtp->worker_thread, "FlipperMTPUsb"); + furi_thread_set_stack_size(mtp->worker_thread, 1024); + furi_thread_set_context(mtp->worker_thread, ctx); + furi_thread_set_callback(mtp->worker_thread, usb_mtp_worker); + furi_thread_start(mtp->worker_thread); +} + +usbd_respond usb_mtp_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(callback); + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) != + (USB_REQ_INTERFACE | USB_REQ_CLASS)) { + if(global_mtp != NULL) { + global_mtp->usb_connected = false; + } + return usbd_fail; + } + switch(req->bRequest) {}; + return usbd_fail; +} + +void usb_deinit(usbd_device* dev) { + usbd_reg_control(dev, NULL); + + AppMTP* mtp = global_mtp; + if(!mtp || mtp->dev != dev) { + FURI_LOG_E("MTP", "deinit mtp_cur leak"); + return; + } + + global_mtp = NULL; + + furi_assert(mtp->worker_thread); + furi_thread_flags_set(furi_thread_get_id(mtp->worker_thread), EventExit); + furi_thread_join(mtp->worker_thread); + furi_thread_free(mtp->worker_thread); + mtp->worker_thread = NULL; +} + +void usb_wakeup(usbd_device* dev) { + UNUSED(dev); +} + +void usb_suspend(usbd_device* dev) { + AppMTP* mtp = global_mtp; + if(!mtp || mtp->dev != dev) return; + furi_thread_flags_set(furi_thread_get_id(mtp->worker_thread), EventReset); +} diff --git a/src/scenes/mtp/usb.h b/src/scenes/mtp/usb.h new file mode 100644 index 0000000..77a6556 --- /dev/null +++ b/src/scenes/mtp/usb.h @@ -0,0 +1,113 @@ +#pragma once +#include +#include +#include + +/* === START furi_hal_usb_i.h === */ +// https://github.com/flipperdevices/flipperzero-firmware/blob/03196fa11007c0f1e002cbb0b82102d8492456b5/targets/f7/furi_hal/furi_hal_usb_i.h#L5 +#define USB_EP0_SIZE 8 + +enum UsbDevDescStr { + UsbDevLang = 0, + UsbDevManuf = 1, + UsbDevProduct = 2, + UsbDevSerial = 3, +}; +/* === END furi_hal_usb_i.h === */ + +void usb_mtp_init(void); +void usb_mtp_deinit(void); +void usb_mtp_wakeup(void); +void usb_mtp_suspend(void); + +#define USB_EP0_SIZE 64 +#define USB_MTP_RX_EP 0x01 +#define USB_MTP_TX_EP 0x81 +#define USB_MTP_RX_EP_SIZE 64 +#define USB_MTP_TX_EP_SIZE 64 + +static const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("Flipper Devices Inc."); +static const struct usb_string_descriptor dev_prod_desc = USB_STRING_DESC("MTP Device"); + +struct MtpDescriptor { + struct usb_config_descriptor config; + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor ep_rx; + struct usb_endpoint_descriptor ep_tx; +} __attribute__((packed)); + +static const struct usb_device_descriptor usb_mtp_dev_descr = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 0, 0), + .bDeviceClass = USB_CLASS_STILL_IMAGE, // MTP falls under Still Image class + .bDeviceSubClass = 1, // Subclass for MTP + .bDeviceProtocol = 1, // Protocol for MTP + .bMaxPacketSize0 = USB_EP0_SIZE, + .idVendor = 0x0483, // STMicroelectronics + .idProduct = 0x5741, // Custom Product ID + .bcdDevice = VERSION_BCD(1, 0, 0), + .iManufacturer = UsbDevManuf, // UsbDevManuf + .iProduct = UsbDevProduct, // UsbDevProduct + .iSerialNumber = UsbDevSerial, // UsbDevSerial + .bNumConfigurations = 1, +}; + +static const struct MtpDescriptor usb_mtp_cfg_descr = { + .config = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct MtpDescriptor), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, + .bMaxPower = USB_CFG_POWER_MA(100), + }, + .intf = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_STILL_IMAGE, + .bInterfaceSubClass = 1, // Subclass for MTP + .bInterfaceProtocol = 1, // Protocol for MTP + .iInterface = NO_DESCRIPTOR, + }, + .ep_rx = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_MTP_RX_EP, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = USB_MTP_RX_EP_SIZE, + .bInterval = 0, + }, + .ep_tx = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_MTP_TX_EP, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = USB_MTP_TX_EP_SIZE, + .bInterval = 0, + }, +}; + +FuriHalUsbInterface usb_mtp_interface = { + .init = usb_mtp_init, + .deinit = usb_mtp_deinit, + .wakeup = usb_mtp_wakeup, + .suspend = usb_mtp_suspend, + + .dev_descr = (struct usb_device_descriptor*)&usb_mtp_dev_descr, + + .str_manuf_descr = (void*)&dev_manuf_desc, + .str_prod_descr = (void*)&dev_prod_desc, + .str_serial_descr = NULL, + + .cfg_descr = (void*)&usb_mtp_cfg_descr, +};