Alex4386 před 1 rokem
rodič
revize
924f84afd7

+ 9 - 2
src/scenes/mtp/main.c

@@ -17,7 +17,6 @@ AppMTP* MTP_alloc() {
     view_set_context(about->view, about);
     view_set_draw_callback(about->view, MTP_on_draw);
 
-    about->usb_connected = false;
     tmp = about;
 
     return about;
@@ -86,7 +85,15 @@ void MTP_on_enter(void* context) {
     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);
+
+        // copy serial number
+        usb_mtp_interface.str_serial_descr = mtp->old_usb->str_serial_descr;
+
+        // set new usb mode for MTP mode
+        if(!furi_hal_usb_set_config(&usb_mtp_interface, mtp)) {
+            FURI_LOG_E("MTP", "Failed to set MTP mode");
+            return;
+        }
     }
 }
 

+ 11 - 0
src/scenes/mtp/main.h

@@ -5,6 +5,14 @@
 #include <gui/scene_manager.h>
 #include <furi_hal.h>
 
+typedef enum MTPState {
+    MTPStateOffline,
+    MTPStateReady,
+    MTPStateBusy,
+    MTPStateCanceled,
+    MTPStateError
+} MTPState;
+
 typedef struct AppMTP {
     Submenu* menu;
     View* view;
@@ -15,6 +23,9 @@ typedef struct AppMTP {
     FuriHalUsbInterface* old_usb;
     FuriThread* worker_thread;
     usbd_device* dev;
+
+    MTPState state;
+    bool is_working;
 } AppMTP;
 
 AppMTP* MTP_alloc();

+ 17 - 0
src/scenes/mtp/mtp.h

@@ -0,0 +1,17 @@
+#pragma once
+
+#define MTP_REQ_CANCEL 0x64
+#define MTP_REQ_GET_EXT_EVENT_DATA 0x65
+#define MTP_REQ_RESET 0x66
+#define MTP_REQ_GET_DEVICE_STATUS 0x67
+
+#define MTP_OP_GET_DEVICE_INFO 0x1001
+#define MTP_OP_OPEN_SESSION 0x1002
+#define MTP_OP_CLOSE_SESSION 0x1003
+#define MTP_OP_GET_STORAGE_IDS 0x1004
+#define MTP_OP_GET_STORAGE_INFO 0x1005
+#define MTP_OP_GET_OBJECT_HANDLES 0x1007
+#define MTP_OP_GET_OBJECT_INFO 0x1008
+#define MTP_OP_GET_OBJECT 0x1009
+#define MTP_OP_SEND_OBJECT_INFO 0x100C
+#define MTP_OP_SEND_OBJECT 0x100D

+ 163 - 19
src/scenes/mtp/usb.c

@@ -1,14 +1,13 @@
 #include "main.h"
 #include "usb.h"
+#include "usbd_core.h"
+#include "usb_std.h"
 #include "usb_desc.h"
 #include <furi.h>
 #include <furi_hal.h>
 #include <furi_hal_usb.h>
 
-// Define MTP specific request constants
-#define MTP_REQ_GET_DEVICE_STATUS 0x67
-#define MTP_REQ_SEND_DATA 0x68
-#define MTP_REQ_GET_DATA 0x69
+#include "mtp.h"
 
 AppMTP* global_mtp;
 
@@ -16,7 +15,6 @@ typedef enum {
     EventExit = 1 << 0,
     EventReset = 1 << 1,
     EventRxTx = 1 << 2,
-
     EventAll = EventExit | EventReset | EventRxTx,
 } MTPEvent;
 
@@ -24,59 +22,192 @@ int32_t usb_mtp_worker(void* ctx) {
     AppMTP* mtp = ctx;
     usbd_device* dev = mtp->dev;
 
-    // temporary!!!
     UNUSED(dev);
 
     while(true) {
-        furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever);
-        if(furi_thread_flags_get() & EventExit) break;
+        MTPEvent flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever);
+        if(flags & EventExit) {
+            FURI_LOG_W("MTP", "Worker thread exit");
+            break;
+        }
 
-        if(furi_thread_flags_get() & EventReset) {
-            furi_thread_flags_clear(EventReset);
-            furi_hal_usb_set_config(mtp->old_usb, NULL);
+        if(flags & EventReset) {
+            FURI_LOG_W("MTP", "USB reset");
+            // Handle USB reset if necessary
         }
 
-        if(furi_thread_flags_get() & EventRxTx) {
-            furi_thread_flags_clear(EventRxTx);
+        if(flags & EventRxTx) {
+            FURI_LOG_W("MTP", "USB Rx/Tx event");
             // Handle MTP RX/TX data here
             // Implement the logic for processing MTP requests, sending responses, etc.
         }
     }
+
     return 0;
 }
 
 usbd_respond usb_mtp_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
     UNUSED(dev);
     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;
+
+    AppMTP* mtp = global_mtp;
+    int value = -1;
+    uint16_t w_index = req->wIndex;
+    uint16_t w_value = req->wValue;
+    uint16_t w_length = req->wLength;
+
+    FURI_LOG_I(
+        "MTP",
+        "Control Request: bmRequestType=0x%02x, bRequest=0x%02x, wValue=0x%04x, wIndex=0x%04x, wLength=0x%04x",
+        req->bmRequestType,
+        req->bRequest,
+        w_value,
+        w_index,
+        w_length);
+
+    if(req->bRequest == USB_STD_GET_DESCRIPTOR) {
+        if((w_value >> 8) == USB_DTYPE_STRING && (w_value & 0xff) == 0xee) {
+            FURI_LOG_I("MTP", "GET_DESCRIPTOR OS_STRING");
+            value = (w_length < usb_mtp_os_string_len ? w_length : usb_mtp_os_string_len);
+            memcpy(req->data, usb_mtp_os_string, value);
+            return usbd_ack;
         }
-        return usbd_fail;
+    } else if((req->bmRequestType & USB_REQ_TYPE) == USB_REQ_VENDOR) {
+        if(req->bRequest == 1 && (req->bmRequestType & USB_EPDIR_IN) &&
+           (w_index == 4 || w_index == 5)) {
+            FURI_LOG_I("MTP", "REQ_VENDOR - OS Descriptor");
+            value =
+                (w_length < sizeof(mtp_ext_config_desc) ? w_length : sizeof(mtp_ext_config_desc));
+            memcpy(req->data, &mtp_ext_config_desc, value);
+            return usbd_ack;
+        }
+    } else if((req->bmRequestType & USB_REQ_TYPE) == USB_REQ_CLASS) {
+        switch(req->bRequest) {
+        case MTP_REQ_RESET:
+            if(w_index == 0 && w_value == 0) {
+                FURI_LOG_I("MTP", "MTP_REQ_RESET");
+                if(!mtp || mtp->dev != dev) return usbd_fail;
+                furi_thread_flags_set(furi_thread_get_id(mtp->worker_thread), EventRxTx);
+                value = w_length;
+            }
+            break;
+        case MTP_REQ_CANCEL:
+            if(w_index == 0 && w_value == 0) {
+                FURI_LOG_I("MTP", "MTP_REQ_CANCEL");
+                if(mtp->state == MTPStateBusy) {
+                    mtp->state = MTPStateCanceled;
+                }
+                value = w_length;
+            }
+            break;
+        case MTP_REQ_GET_DEVICE_STATUS:
+            if(w_index == 0 && w_value == 0) {
+                FURI_LOG_I("MTP", "MTP_REQ_GET_DEVICE_STATUS");
+                struct mtp_device_status* status = (struct mtp_device_status*)req->data;
+                status->wLength = sizeof(*status);
+                if(mtp->state == MTPStateBusy) {
+                    status->wCode = 0x2019; // Device Busy
+                } else {
+                    status->wCode = 0x2001; // Device Ready
+                }
+                value = sizeof(*status);
+            }
+            break;
+        default:
+            FURI_LOG_W("MTP", "Unsupported CLASS request: bRequest=0x%02x", req->bRequest);
+            break;
+        }
+    }
+
+    if(value >= 0) {
+        if(value > w_length) {
+            value = w_length;
+        }
+        dev->driver->ep_write(0x00, req->data, value);
+        return usbd_ack;
     }
-    switch(req->bRequest) {};
+
     return usbd_fail;
 }
 
+void usb_mtp_txrx(usbd_device* dev, uint8_t event, uint8_t ep) {
+    UNUSED(ep);
+    UNUSED(event);
+
+    AppMTP* mtp = global_mtp;
+    if(!mtp || mtp->dev != dev) return;
+
+    furi_thread_flags_set(furi_thread_get_id(mtp->worker_thread), EventRxTx);
+}
+
+usbd_respond usb_mtp_ep_config(usbd_device* dev, uint8_t cfg) {
+    AppMTP* mtp = global_mtp;
+    FURI_LOG_I("MTP", "USB EP config: cfg=%d", cfg);
+
+    switch(cfg) {
+    case 0: // deconfigure
+        FURI_LOG_I("MTP", "USB deconfigure");
+        usbd_ep_deconfig(dev, MTP_EP_IN_ADDR);
+        usbd_ep_deconfig(dev, MTP_EP_OUT_ADDR);
+        usbd_ep_deconfig(dev, MTP_EP_INT_IN_ADDR);
+        usbd_reg_endpoint(dev, MTP_EP_IN_ADDR, NULL);
+        usbd_reg_endpoint(dev, MTP_EP_OUT_ADDR, NULL);
+        usbd_reg_endpoint(dev, MTP_EP_INT_IN_ADDR, NULL);
+        if(mtp != NULL) mtp->usb_connected = false;
+        break;
+    case 1: // configure
+        FURI_LOG_I("MTP", "USB configure");
+        usbd_ep_config(dev, MTP_EP_OUT_ADDR, USB_EPTYPE_BULK, MTP_MAX_PACKET_SIZE);
+        usbd_ep_config(dev, MTP_EP_IN_ADDR, USB_EPTYPE_BULK, MTP_MAX_PACKET_SIZE);
+        usbd_ep_config(dev, MTP_EP_INT_IN_ADDR, USB_EPTYPE_INTERRUPT, USB_MAX_INTERRUPT_SIZE);
+        usbd_reg_endpoint(dev, MTP_EP_OUT_ADDR, usb_mtp_txrx);
+        usbd_reg_endpoint(dev, MTP_EP_IN_ADDR, usb_mtp_txrx);
+        usbd_reg_endpoint(dev, MTP_EP_INT_IN_ADDR, usb_mtp_txrx);
+        if(mtp != NULL) mtp->usb_connected = true;
+        break;
+    default:
+        return usbd_fail;
+    }
+
+    return usbd_ack;
+}
+
 void usb_mtp_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) {
     UNUSED(intf);
+    if(dev == NULL) {
+        FURI_LOG_E("MTP", "dev is NULL");
+    }
+
     AppMTP* mtp = ctx;
     global_mtp = mtp;
+    mtp->dev = dev;
+
+    FURI_LOG_I("MTP", "Initializing MTP device");
 
+    // Register the configuration and control handlers
+
+    usbd_reg_config(dev, usb_mtp_ep_config);
     usbd_reg_control(dev, usb_mtp_control);
+    FURI_LOG_I("MTP", "Registered configuration and control handlers");
+
+    // Connect the device
     usbd_connect(dev, true);
+    FURI_LOG_I("MTP", "Connected device");
 
+    // Initialize worker thread
     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);
+    FURI_LOG_I("MTP", "Started worker thread");
 }
 
 void usb_mtp_deinit(usbd_device* dev) {
+    usbd_reg_config(dev, NULL);
     usbd_reg_control(dev, NULL);
+    FURI_LOG_I("MTP", "Unregistered configuration and control handlers");
 
     AppMTP* mtp = global_mtp;
     if(!mtp || mtp->dev != dev) {
@@ -87,18 +218,31 @@ void usb_mtp_deinit(usbd_device* dev) {
     global_mtp = NULL;
 
     furi_assert(mtp->worker_thread);
+    FURI_LOG_I("MTP", "Worker thread Condition pass");
+
     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;
+
+    mtp->usb_connected = false;
+    mtp->is_working = false;
+
+    FURI_LOG_I("MTP", "Deinit worker thread");
 }
 
 void usb_mtp_wakeup(usbd_device* dev) {
+    AppMTP* mtp = global_mtp;
+    if(!mtp || mtp->dev != dev) return;
+
+    mtp->is_working = true;
     UNUSED(dev);
 }
 
 void usb_mtp_suspend(usbd_device* dev) {
     AppMTP* mtp = global_mtp;
     if(!mtp || mtp->dev != dev) return;
+
+    mtp->is_working = false;
     furi_thread_flags_set(furi_thread_get_id(mtp->worker_thread), EventReset);
 }

+ 1 - 8
src/scenes/mtp/usb.h

@@ -5,23 +5,16 @@
 
 /* === 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 64
+#define USB_EP0_SIZE 8
 
 enum UsbDevDescStr {
     UsbDevLang = 0,
     UsbDevManuf = 1,
     UsbDevProduct = 2,
     UsbDevSerial = 3,
-    UsbDevHighSpeed = 4,
-    UsbDevLowSpeed = 5,
 };
 /* ===   END furi_hal_usb_i.h === */
 
-#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
-
 void usb_mtp_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx);
 void usb_mtp_deinit(usbd_device* dev);
 void usb_mtp_wakeup(usbd_device* dev);

+ 75 - 14
src/scenes/mtp/usb_desc.c

@@ -5,7 +5,32 @@
 #include "usb_desc.h"
 
 const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("Flipper Devices Inc.");
-const struct usb_string_descriptor dev_prod_desc = USB_STRING_DESC("MTP Device");
+const struct usb_string_descriptor dev_prod_desc =
+    USB_STRING_DESC("Flipper Zero Virtual MTP Device");
+
+const uint8_t usb_mtp_os_string[] = {
+    18,
+    USB_DTYPE_STRING,
+    /* Signature field: "MSFT100" */
+    'M',
+    0,
+    'S',
+    0,
+    'F',
+    0,
+    'T',
+    0,
+    '1',
+    0,
+    '0',
+    0,
+    '0',
+    0,
+    /* vendor code */
+    1,
+    /* padding */
+    0};
+const uint8_t usb_mtp_os_string_len = sizeof(usb_mtp_os_string);
 
 const struct usb_device_descriptor usb_mtp_dev_descr = {
     .bLength = sizeof(struct usb_device_descriptor),
@@ -14,10 +39,10 @@ const struct usb_device_descriptor usb_mtp_dev_descr = {
     .bDeviceClass = USB_CLASS_STILL_IMAGE, // MTP falls under Still Image class
     .bDeviceSubClass = USB_SUBCLASS_MTP, // Subclass for MTP
     .bDeviceProtocol = USB_PROTO_MTP, // Protocol for MTP
-    .bMaxPacketSize0 = MTP_MAX_PACKET_SIZE,
+    .bMaxPacketSize0 = USB_EP0_SIZE,
     .idVendor = 0x0483, // STMicroelectronics
     .idProduct = 0x5741, // Custom Product ID
-    .bcdDevice = VERSION_BCD(1, 0, 0),
+    .bcdDevice = BCD_VERSION,
     .iManufacturer = UsbDevManuf, // UsbDevManuf
     .iProduct = UsbDevProduct, // UsbDevProduct
     .iSerialNumber = UsbDevSerial, // UsbDevSerial
@@ -32,7 +57,7 @@ const struct MtpDescriptor usb_mtp_cfg_descr = {
             .wTotalLength = sizeof(struct MtpDescriptor),
             .bNumInterfaces = 1,
             .bConfigurationValue = USB_CONF_VAL_MTP,
-            .iConfiguration = UsbDevHighSpeed,
+            .iConfiguration = NO_DESCRIPTOR,
             .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED,
             .bMaxPower = USB_CFG_POWER_MA(100),
         },
@@ -41,31 +66,67 @@ const struct MtpDescriptor usb_mtp_cfg_descr = {
             .bLength = sizeof(struct usb_interface_descriptor),
             .bDescriptorType = USB_DTYPE_INTERFACE,
             .bInterfaceNumber = 0,
+            .iInterface = UsbDevManuf,
             .bAlternateSetting = 0,
-            .bNumEndpoints = 2,
+            .bNumEndpoints = 3,
             .bInterfaceClass = USB_CLASS_STILL_IMAGE,
-            .bInterfaceSubClass = 1, // Subclass for MTP
-            .bInterfaceProtocol = 1, // Protocol for MTP
-            .iInterface = NO_DESCRIPTOR,
+            .bInterfaceSubClass = USB_SUBCLASS_MTP,
+            .bInterfaceProtocol = USB_PROTO_MTP,
         },
-    .ep_rx =
+    .ep_in =
         {
             .bLength = sizeof(struct usb_endpoint_descriptor),
             .bDescriptorType = USB_DTYPE_ENDPOINT,
-            .bEndpointAddress = USB_MTP_RX_EP,
+            .bEndpointAddress = MTP_EP_IN_ADDR,
             .bmAttributes = USB_EPTYPE_BULK,
-            .wMaxPacketSize = USB_MTP_RX_EP_SIZE,
+            .wMaxPacketSize = MTP_MAX_PACKET_SIZE,
             .bInterval = 0,
         },
-    .ep_tx =
+    .ep_out =
         {
             .bLength = sizeof(struct usb_endpoint_descriptor),
             .bDescriptorType = USB_DTYPE_ENDPOINT,
-            .bEndpointAddress = USB_MTP_TX_EP,
+            .bEndpointAddress = MTP_EP_OUT_ADDR,
             .bmAttributes = USB_EPTYPE_BULK,
-            .wMaxPacketSize = USB_MTP_TX_EP_SIZE,
+            .wMaxPacketSize = MTP_MAX_PACKET_SIZE,
             .bInterval = 0,
         },
+    .ep_int_in =
+        {
+            .bLength = sizeof(struct usb_endpoint_descriptor),
+            .bDescriptorType = USB_DTYPE_ENDPOINT,
+            .bEndpointAddress = MTP_EP_INT_IN_ADDR,
+            .bmAttributes = USB_EPTYPE_INTERRUPT,
+            .wMaxPacketSize = USB_MAX_INTERRUPT_SIZE,
+            .bInterval = 6,
+        },
+};
+
+/* MICROSOFT STUFF */
+const struct mtp_ext_config_desc mtp_ext_config_desc = {
+    .header =
+        {
+            .dwLength = sizeof(mtp_ext_config_desc),
+            .bcdVersion = BCD_VERSION,
+            .wIndex = 0x04,
+            .bCount = 1,
+        },
+    .function =
+        {
+            .bFirstInterfaceNumber = 0,
+            .bInterfaceCount = 1,
+            .compatibleID =
+                {
+                    'M',
+                    'T',
+                    'P',
+                    0,
+                    0,
+                    0,
+                    0,
+                    0,
+                },
+        },
 };
 
 FuriHalUsbInterface usb_mtp_interface = {

+ 44 - 2
src/scenes/mtp/usb_desc.h

@@ -7,13 +7,24 @@
 #define USB_PROTO_MTP 0x01
 #define USB_CONF_VAL_MTP 1
 
+#define MTP_EP_IN_ADDR 0x81
+#define MTP_EP_OUT_ADDR 0x01
+#define MTP_EP_INT_IN_ADDR 0x82
+
 #define MTP_MAX_PACKET_SIZE 512
+#define USB_MAX_INTERRUPT_SIZE 28
+#define BCD_VERSION VERSION_BCD(1, 0, 0)
 
 struct MtpDescriptor {
     struct usb_config_descriptor config;
     struct usb_interface_descriptor intf;
-    struct usb_endpoint_descriptor ep_rx;
-    struct usb_endpoint_descriptor ep_tx;
+
+    // I/O endpoints
+    struct usb_endpoint_descriptor ep_in;
+    struct usb_endpoint_descriptor ep_out;
+
+    // Interrupt endpoint
+    struct usb_endpoint_descriptor ep_int_in;
 } __attribute__((packed));
 
 extern const struct usb_string_descriptor dev_manuf_desc;
@@ -21,3 +32,34 @@ extern const struct usb_string_descriptor dev_prod_desc;
 extern const struct usb_device_descriptor usb_mtp_dev_descr;
 extern const struct MtpDescriptor usb_mtp_cfg_descr;
 extern FuriHalUsbInterface usb_mtp_interface;
+extern const uint8_t usb_mtp_os_string[];
+extern const uint8_t usb_mtp_os_string_len;
+
+/* Microsoft Extended Configuration Descriptor Header Section */
+struct mtp_ext_config_desc_header {
+    uint32_t dwLength;
+    uint16_t bcdVersion;
+    uint16_t wIndex;
+    uint8_t bCount;
+    uint8_t reserved[7];
+};
+/* Microsoft Extended Configuration Descriptor Function Section */
+struct mtp_ext_config_desc_function {
+    uint8_t bFirstInterfaceNumber;
+    uint8_t bInterfaceCount;
+    uint8_t compatibleID[8];
+    uint8_t subCompatibleID[8];
+    uint8_t reserved[6];
+};
+
+struct mtp_device_status {
+    uint16_t wLength;
+    uint16_t wCode;
+};
+
+struct mtp_ext_config_desc {
+    struct mtp_ext_config_desc_header header;
+    struct mtp_ext_config_desc_function function;
+};
+
+extern const struct mtp_ext_config_desc mtp_ext_config_desc;