Alex4386 1 rok temu
rodzic
commit
6aa2b8460a

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

@@ -12,30 +12,31 @@ AppMTP* tmp;
 
 void MTP_on_draw(Canvas* canvas, void* context);
 AppMTP* MTP_alloc() {
-    AppMTP* about = (AppMTP*)malloc(sizeof(AppMTP));
-    about->view = view_alloc();
-    view_set_context(about->view, about);
-    view_set_draw_callback(about->view, MTP_on_draw);
+    AppMTP* mtp = (AppMTP*)malloc(sizeof(AppMTP));
+    mtp->view = view_alloc();
+    view_set_context(mtp->view, mtp);
+    view_set_draw_callback(mtp->view, MTP_on_draw);
 
-    tmp = about;
+    mtp->storage = furi_record_open(RECORD_STORAGE);
+    tmp = mtp;
 
-    return about;
+    return mtp;
 }
 
 void MTP_on_draw(Canvas* canvas, void* context) {
-    AppMTP* about = (AppMTP*)context;
+    AppMTP* mtp = (AppMTP*)context;
 
     canvas_clear(canvas);
     canvas_set_bitmap_mode(canvas, true);
 
     bool usb_connected = false;
-    if(about == NULL) {
+    if(mtp == NULL) {
         // facepalm
         if(tmp != NULL) {
             usb_connected = tmp->usb_connected;
         }
     } else {
-        usb_connected = about->usb_connected;
+        usb_connected = mtp->usb_connected;
     }
 
     if(usb_connected) {
@@ -52,6 +53,7 @@ void MTP_on_draw(Canvas* canvas, void* context) {
     } else {
         canvas_draw_icon(canvas, 1, 31, &I_Connect_me);
         canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str(canvas, 43, 10, "MTP Connection");
         canvas_draw_str(canvas, 10, 25, "Plug me into computer!");
         canvas_draw_icon(canvas, 2, 2, &I_Pin_back_arrow);
         canvas_set_font(canvas, FontSecondary);

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

@@ -3,6 +3,7 @@
 #include <gui/modules/submenu.h>
 #include <gui/modules/popup.h>
 #include <gui/scene_manager.h>
+#include <storage/storage.h>
 #include <furi_hal.h>
 
 typedef enum MTPState {
@@ -13,6 +14,11 @@ typedef enum MTPState {
     MTPStateError
 } MTPState;
 
+typedef struct {
+    uint32_t session_id;
+    bool session_open;
+} MTPSession;
+
 typedef struct AppMTP {
     Submenu* menu;
     View* view;
@@ -26,6 +32,10 @@ typedef struct AppMTP {
 
     MTPState state;
     bool is_working;
+    Storage* storage;
+    MTPSession session;
+
+    bool write_pending;
 } AppMTP;
 
 AppMTP* MTP_alloc();

+ 424 - 0
src/scenes/mtp/mtp.c

@@ -0,0 +1,424 @@
+#include <furi.h>
+#include <storage/storage.h>
+#include "main.h"
+#include "mtp.h"
+#include "usb_desc.h"
+
+void mtp_handle_bulk(AppMTP* mtp, uint8_t* buffer, int32_t length) {
+    UNUSED(mtp);
+
+    if(length < 12) {
+        FURI_LOG_E("MTP", "Invalid MTP packet");
+        return;
+    }
+    struct MTPContainer* container = (struct MTPContainer*)buffer;
+    uint16_t mtp_op = container->header.op;
+    FURI_LOG_I("MTP", "Handling MTP operation: 0x%04x", mtp_op);
+
+    switch(mtp_op) {
+    case MTP_OP_GET_DEVICE_INFO:
+        FURI_LOG_I("MTP", "GetDeviceInfo operation");
+        send_device_info(mtp, container->header.transaction_id);
+        // Process the GetDeviceInfo operation
+        break;
+    case MTP_OP_OPEN_SESSION:
+    case MTP_OP_CLOSE_SESSION:
+        FURI_LOG_I("MTP", "Open/CloseSession operation (STUB)");
+        send_mtp_response(mtp, 3, MTP_RESP_OK, container->header.transaction_id, NULL);
+        break;
+    // Handle bulk transfer specific operations
+    case MTP_OP_SEND_OBJECT_INFO:
+        FURI_LOG_I("MTP", "SendObjectInfo operation");
+        // Process the SendObjectInfo operation
+        break;
+    case MTP_OP_GET_OBJECT:
+        FURI_LOG_I("MTP", "GetObject operation");
+        // Process the GetObject operation
+        break;
+    // Handle other bulk operations here...
+    default:
+        FURI_LOG_W("MTP", "Unsupported MTP operation in bulk transfer: 0x%04x", mtp_op);
+        break;
+    }
+}
+
+void send_device_info(AppMTP* mtp, uint32_t transaction_id) {
+    uint8_t response[256];
+    int length = BuildDeviceInfo(response);
+    send_mtp_response_buffer(mtp, 2, MTP_RESP_OK, transaction_id, response, length);
+}
+
+void send_mtp_response_buffer(
+    AppMTP* mtp,
+    uint16_t resp_type,
+    uint16_t resp_code,
+    uint32_t transaction_id,
+    uint8_t* buffer,
+    uint32_t size) {
+    size_t target_size = size + sizeof(struct MTPHeader);
+    struct MTPHeader header = {
+        .len = target_size,
+        .type = resp_type,
+        .op = resp_code,
+        .transaction_id = transaction_id,
+    };
+
+    // Send the response in one go
+    /*
+    uint8_t* response = malloc(target_size);
+    memcpy(response, &header, sizeof(header));
+    memcpy(response + sizeof(header), buffer, size);
+
+    usbd_ep_write(mtp->dev, MTP_EP_OUT_ADDR, response, target_size);
+    */
+
+    // Send the response in chunks
+    int chunk_count = (target_size / MTP_MAX_PACKET_SIZE) + 1;
+    FURI_LOG_I("MTP", "Sending MTP response: %d bytes in %d chunks", target_size, chunk_count);
+    uint8_t* ptr = buffer;
+
+    for(int i = 0; i < chunk_count; i++) {
+        FURI_LOG_I("MTP", "Sending chunk %d/%d", i + 1, chunk_count);
+        mtp->write_pending = true;
+        size_t chunk_size = (i == chunk_count - 1) ? target_size % MTP_MAX_PACKET_SIZE :
+                                                     MTP_MAX_PACKET_SIZE;
+
+        uint8_t* buf = NULL;
+
+        if(i == 0) {
+            // first send header
+            buf = malloc(chunk_size);
+            memcpy(buf, &header, sizeof(header));
+            memcpy(buf, ptr, chunk_size - sizeof(header));
+            usbd_ep_write(mtp->dev, MTP_EP_OUT_ADDR, buf, chunk_size);
+            ptr += chunk_size - sizeof(header);
+        } else {
+            usbd_ep_write(mtp->dev, MTP_EP_OUT_ADDR, ptr, chunk_size);
+            ptr += chunk_size;
+        }
+
+        FURI_LOG_I(
+            "MTP",
+            "Sent chunk %d/%d (offset: %d)",
+            i + 1,
+            chunk_count,
+            ptr - buffer + sizeof(header));
+
+        if(buf != NULL) {
+            free(buf);
+        }
+    }
+}
+
+void send_mtp_response(
+    AppMTP* mtp,
+    uint16_t resp_type,
+    uint16_t resp_code,
+    uint32_t transaction_id,
+    uint32_t* params) {
+    struct MTPContainer response = {
+        .header =
+            {
+                .len = sizeof(struct MTPContainer),
+                .type = resp_type,
+                .op = resp_code,
+                .transaction_id = transaction_id,
+            },
+    };
+
+    if(params != NULL) {
+        memcpy(response.params, params, sizeof(response.params));
+    }
+
+    usbd_ep_write(mtp->dev, MTP_EP_OUT_ADDR, (uint8_t*)&response, sizeof(response));
+}
+
+int mtp_handle_class_control(AppMTP* mtp, usbd_device* dev, usbd_ctlreq* req) {
+    UNUSED(dev);
+
+    int value = -1;
+    uint32_t handle = req->wIndex;
+    uint8_t* buffer = req->data;
+    uint32_t size = req->wLength;
+
+    // Extract the MTP operation code from the request
+    uint16_t mtp_op = (req->bRequest << 8) | (req->bmRequestType & 0xFF);
+
+    FURI_LOG_I(
+        "MTP",
+        "Control Request: bmRequestType=0x%02x, bRequest=0x%02x, mtp_op=0x%04x, wValue=0x%04x, wIndex=0x%04lx, wLength=%ld",
+        req->bmRequestType,
+        req->bRequest,
+        mtp_op,
+        req->wValue,
+        handle,
+        size);
+
+    switch(mtp_op) {
+    case MTP_REQ_GET_DEVICE_STATUS:
+        // Handle GetDeviceStatus request
+        struct mtp_device_status* status = (struct mtp_device_status*)req->data;
+        status->wLength = sizeof(*status);
+        status->wCode = (mtp->state == MTPStateBusy) ? MTP_RESP_DEVICE_BUSY : MTP_RESP_OK;
+        value = status->wLength;
+        break;
+        value = BuildDeviceInfo(buffer);
+        break;
+
+    case MTP_OP_GET_STORAGE_IDS:
+        // Handle GetStorageIDs request
+        uint32_t storage_ids[2];
+        uint32_t count;
+        GetStorageIDs(mtp, storage_ids, &count);
+        memcpy(buffer, storage_ids, count * sizeof(uint32_t));
+        value = count * sizeof(uint32_t);
+        break;
+
+    case MTP_OP_GET_STORAGE_INFO:
+        // Handle GetStorageInfo request
+        if(req->wIndex == INTERNAL_STORAGE_ID || req->wIndex == EXTERNAL_STORAGE_ID) {
+            MTPStorageInfo info;
+            GetStorageInfo(mtp, req->wIndex, &info);
+            memcpy(buffer, &info, sizeof(info));
+            value = sizeof(info);
+        } else {
+            // Invalid storage ID
+            value = 0;
+        }
+        break;
+    case MTP_OP_SEND_OBJECT_INFO:
+        // Handle SendObjectInfo request
+        uint32_t parent_handle = req->wIndex; // Assuming parent handle is in wIndex
+        char name[256];
+        // extract name from req
+        bool is_directory = false;
+        handle = CreateObject(mtp, parent_handle, name, is_directory);
+        // respond with the new handle
+        break;
+    case MTP_OP_GET_OBJECT:
+        ReadObject(mtp, handle, buffer, size);
+        // send buffer back in response
+        break;
+    case MTP_OP_SEND_OBJECT:
+        // Handle SendObject request
+        handle = req->wIndex; // Assuming handle is in wIndex
+        WriteObject(mtp, handle, buffer, size);
+        break;
+    case MTP_OP_DELETE_OBJECT:
+        // Handle DeleteObject request
+        handle = req->wIndex; // Assuming handle is in wIndex
+        if(!DeleteObject(mtp, handle))
+            value = -1;
+        else
+            value = 0;
+        // send confirmation response
+        break;
+    // Handle other MTP requests
+    default:
+        // Unsupported request
+        break;
+    }
+
+    return value;
+}
+
+int BuildDeviceInfo(uint8_t* buffer) {
+    uint8_t* ptr = buffer;
+    uint16_t length;
+
+    // Standard version
+    *(uint16_t*)ptr = 100;
+    ptr += 2;
+
+    // Vendor extension ID
+    *(uint32_t*)ptr = MTP_VENDOR_EXTENSION_ID;
+    ptr += 4;
+
+    // Vendor extension version
+    *(uint16_t*)ptr = MTP_VENDOR_EXTENSION_VERSION;
+    ptr += 2;
+
+    // Vendor extension description
+    WriteMTPString(ptr, "microsoft.com: 1.0;", &length);
+    ptr += length;
+
+    // Functional mode
+    *(uint16_t*)ptr = MTP_FUNCTIONAL_MODE;
+    ptr += 2;
+
+    // Supported operations (example, add as needed)
+    *(uint32_t*)ptr = 14; // Number of supported operations
+    ptr += 4;
+    uint16_t supported_operations[] = {
+        MTP_OP_GET_DEVICE_INFO,
+        MTP_OP_OPEN_SESSION,
+        MTP_OP_CLOSE_SESSION,
+        MTP_OP_GET_STORAGE_IDS,
+        MTP_OP_GET_STORAGE_INFO,
+        MTP_OP_GET_NUM_OBJECTS,
+        MTP_OP_GET_OBJECT_HANDLES,
+        MTP_OP_GET_OBJECT_INFO,
+        MTP_OP_GET_OBJECT,
+        MTP_OP_SEND_OBJECT_INFO,
+        MTP_OP_SEND_OBJECT,
+        MTP_OP_DELETE_OBJECT,
+        MTP_OP_GET_DEVICE_PROP_DESC,
+        MTP_OP_GET_DEVICE_PROP_VALUE};
+    for(int i = 0; i < 14; i++) {
+        *(uint16_t*)ptr = supported_operations[i];
+        ptr += 2;
+    }
+
+    // Supported events (example, add as needed)
+    *(uint32_t*)ptr = 0; // Number of supported events
+    ptr += 4;
+
+    // Supported device properties (example, add as needed)
+    *(uint32_t*)ptr = 1; // Number of supported device properties
+    ptr += 4;
+
+    uint16_t supported_device_properties[] = {0xd402}; // Device friendly name
+    for(int i = 0; i < 1; i++) {
+        *(uint16_t*)ptr = supported_device_properties[i];
+        ptr += 2;
+    }
+
+    // Supported capture formats (example, add as needed)
+    *(uint32_t*)ptr = 0; // Number of supported capture formats
+    ptr += 4;
+
+    // Supported playback formats (example, add as needed)
+    *(uint32_t*)ptr = 2; // Number of supported playback formats
+    ptr += 4;
+
+    uint16_t supported_playback_formats[] = {MTP_FORMAT_UNDEFINED, MTP_FORMAT_ASSOCIATION};
+    for(int i = 0; i < 2; i++) {
+        *(uint16_t*)ptr = supported_playback_formats[i];
+        ptr += 2;
+    }
+
+    // Manufacturer
+    WriteMTPString(ptr, USB_MANUFACTURER_STRING, &length);
+    ptr += length;
+
+    // Model
+    WriteMTPString(ptr, USB_DEVICE_MODEL, &length);
+    ptr += length;
+
+    // Device version
+    WriteMTPString(ptr, "1.0", &length);
+    ptr += length;
+
+    // Serial number
+    WriteMTPString(ptr, "hakurei", &length);
+    ptr += length;
+
+    return ptr - buffer;
+}
+
+void GetStorageIDs(AppMTP* mtp, uint32_t* storage_ids, uint32_t* count) {
+    SDInfo sd_info;
+    FS_Error err = storage_sd_info(mtp->storage, &sd_info);
+    storage_ids[0] = INTERNAL_STORAGE_ID;
+    if(err != FSE_OK) {
+        FURI_LOG_E("MTP", "SD Card not found");
+        *count = 1; // We have only one storage
+        return;
+    }
+
+    storage_ids[1] = EXTERNAL_STORAGE_ID;
+    *count = 2; // We have two storages: internal and external
+}
+
+void GetStorageInfo(AppMTP* mtp, uint32_t storage_id, MTPStorageInfo* info) {
+    if(storage_id == INTERNAL_STORAGE_ID) {
+        // Fill in details for internal storage
+        info->storage_type = 0x0003; // Fixed RAM
+        info->filesystem_type = 0x0002; // Generic hierarchical
+        info->access_capability = 0x0000; // Read-write
+
+        // Fill in details for internal storage
+        uint64_t total_space;
+        uint64_t free_space;
+        storage_common_fs_info(mtp->storage, STORAGE_INT_PATH_PREFIX, &total_space, &free_space);
+
+        info->max_capacity = total_space;
+        info->free_space_in_bytes = free_space;
+        info->free_space_in_objects = 0xFFFFFFFF;
+
+        strcpy(info->storage_description, "Internal Storage");
+        strcpy(info->volume_identifier, "int");
+    } else if(storage_id == EXTERNAL_STORAGE_ID) {
+        SDInfo sd_info;
+        FS_Error err = storage_sd_info(mtp->storage, &sd_info);
+
+        // Fill in details for internal storage
+        info->storage_type = 0x0004; // Removable RAM
+        info->filesystem_type = 0x0002; // Generic hierarchical
+        info->access_capability = 0x0000; // Read-write
+
+        if(err != FSE_OK) {
+            info->max_capacity = 0;
+            info->free_space_in_bytes = 0;
+            strcpy(info->storage_description, "External Storage");
+            strcpy(info->volume_identifier, "EXT_STORAGE");
+            FURI_LOG_E("MTP", "SD Card not found");
+            return;
+        }
+
+        // Fill in details for external storage
+        info->max_capacity = sd_info.kb_total * 1024;
+        info->free_space_in_bytes = sd_info.kb_free * 1024;
+        strcpy(info->storage_description, "External Storage");
+        strcpy(info->volume_identifier, "EXT_STORAGE");
+    }
+}
+
+// Microsoft-style UTF-16LE string:
+void WriteMTPString(uint8_t* buffer, const char* str, uint16_t* length) {
+    // MTP strings are UTF-16LE and prefixed with a length byte
+    uint8_t* ptr = buffer;
+    uint8_t str_len = strlen(str);
+    *ptr++ = str_len + 1; // Length byte (includes the length itself and a null terminator)
+    while(*str) {
+        *ptr++ = *str++;
+        *ptr++ = 0x00; // UTF-16LE null byte
+    }
+    *ptr++ = 0x00; // Null terminator
+    *ptr++ = 0x00;
+    *length = ptr - buffer;
+}
+
+uint32_t CreateObject(AppMTP* mtp, uint32_t parent_handle, const char* name, bool is_directory) {
+    UNUSED(mtp);
+    MTPObject new_object;
+    UNUSED(new_object);
+    UNUSED(parent_handle);
+    UNUSED(name);
+    UNUSED(is_directory);
+
+    FURI_LOG_I("MTP", "Creating object %s", name);
+    return 0;
+}
+
+uint32_t ReadObject(AppMTP* mtp, uint32_t handle, uint8_t* buffer, uint32_t size) {
+    UNUSED(mtp);
+    UNUSED(buffer);
+    UNUSED(size);
+    FURI_LOG_I("MTP", "Reading object %ld", handle);
+
+    return 0;
+}
+
+void WriteObject(AppMTP* mtp, uint32_t handle, uint8_t* data, uint32_t size) {
+    UNUSED(mtp);
+    UNUSED(data);
+    UNUSED(size);
+    FURI_LOG_I("MTP", "Writing object %ld", handle);
+}
+
+bool DeleteObject(AppMTP* mtp, uint32_t handle) {
+    UNUSED(mtp);
+    FURI_LOG_I("MTP", "Deleting object %ld", handle);
+    return true;
+}

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

@@ -1,17 +1,131 @@
 #pragma once
 
+#define MTP_STANDARD_VERSION 100
+#define MTP_VENDOR_EXTENSION_ID 0x6
+#define MTP_VENDOR_EXTENSION_VERSION 100
+#define MTP_FUNCTIONAL_MODE 0x0
+
 #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
 
+// MTP Operation Codes
 #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_NUM_OBJECTS 0x1006
 #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
+#define MTP_OP_DELETE_OBJECT 0x100B
+#define MTP_OP_GET_DEVICE_PROP_DESC 0x1014
+#define MTP_OP_GET_DEVICE_PROP_VALUE 0x1015
+
+// MTP Response Codes
+#define MTP_RESP_OK 0x2001
+#define MTP_RESP_GENERAL_ERROR 0x2002
+#define MTP_RESP_SESSION_NOT_OPEN 0x2003
+#define MTP_RESP_INVALID_TRANSACTION_ID 0x2004
+#define MTP_RESP_OPERATION_NOT_SUPPORTED 0x2005
+#define MTP_RESP_PARAMETER_NOT_SUPPORTED 0x2006
+#define MTP_RESP_INCOMPLETE_TRANSFER 0x2007
+#define MTP_RESP_INVALID_STORAGE_ID 0x2008
+#define MTP_RESP_INVALID_OBJECT_HANDLE 0x2009
+#define MTP_RESP_DEVICE_PROP_NOT_SUPPORTED 0x200A
+#define MTP_RESP_STORE_FULL 0x200C
+#define MTP_RESP_OBJECT_WRITE_PROTECTED 0x200D
+#define MTP_RESP_STORE_READ_ONLY 0x200E
+#define MTP_RESP_ACCESS_DENIED 0x200F
+#define MTP_RESP_NO_THUMBNAIL_PRESENT 0x2010
+#define MTP_RESP_SELF_TEST_FAILED 0x2011
+#define MTP_RESP_PARTIAL_DELETION 0x2012
+#define MTP_RESP_STORE_NOT_AVAILABLE 0x2013
+#define MTP_RESP_SPEC_BY_FORMAT_UNSUPPORTED 0x2014
+#define MTP_RESP_NO_VALID_OBJECT_INFO 0x2015
+#define MTP_RESP_INVALID_CODE_FORMAT 0x2016
+#define MTP_RESP_UNKNOWN_VENDOR_CODE 0x2017
+#define MTP_RESP_CAPTURE_ALREADY_TERMINATED 0x2018
+#define MTP_RESP_DEVICE_BUSY 0x2019
+
+// Endpoint Addresses
+#define MTP_EP_IN_ADDR 0x01
+#define MTP_EP_OUT_ADDR 0x82
+#define MTP_EP_INT_IN_ADDR 0x03
+
+// Storage IDs
+#define INTERNAL_STORAGE_ID 0x00000001
+#define EXTERNAL_STORAGE_ID 0x00000001
+
+// MTP Playback formats
+#define MTP_FORMAT_UNDEFINED 0x3000
+#define MTP_FORMAT_ASSOCIATION 0x3001
+
+typedef struct {
+    uint32_t handle;
+    char name[256];
+    uint32_t size;
+    uint32_t parent_handle;
+    bool is_directory;
+} MTPObject;
+
+struct mtp_device_status {
+    uint16_t wLength;
+    uint16_t wCode;
+};
+
+// Storage information structure based on MTP specification
+typedef struct {
+    uint16_t storage_type;
+    uint16_t filesystem_type;
+    uint16_t access_capability;
+    uint64_t max_capacity;
+    uint64_t free_space_in_bytes;
+    uint32_t free_space_in_objects;
+    char storage_description[64];
+    char volume_identifier[32];
+} MTPStorageInfo;
+
+struct MTPHeader {
+    uint32_t len; // 0
+    uint16_t type; // 4
+    uint16_t op; // 6
+    uint32_t transaction_id; // 8
+};
+
+struct MTPContainer {
+    struct MTPHeader header; // 0
+    uint32_t params[5]; // 12
+};
+
+void OpenSession(AppMTP* mtp, uint32_t session_id);
+void CloseSession(AppMTP* mtp);
+void GetStorageIDs(AppMTP* mtp, uint32_t* storage_ids, uint32_t* count);
+void GetStorageInfo(AppMTP* mtp, uint32_t storage_id, MTPStorageInfo* info);
+uint32_t CreateObject(AppMTP* mtp, uint32_t parent_handle, const char* name, bool is_directory);
+uint32_t ReadObject(AppMTP* mtp, uint32_t handle, uint8_t* buffer, uint32_t size);
+void WriteObject(AppMTP* mtp, uint32_t handle, uint8_t* data, uint32_t size);
+bool DeleteObject(AppMTP* mtp, uint32_t handle);
+
+void mtp_handle_bulk(AppMTP* mtp, uint8_t* buffer, int32_t length);
+int mtp_handle_class_control(AppMTP* mtp, usbd_device* dev, usbd_ctlreq* req);
+void send_mtp_response(
+    AppMTP* mtp,
+    uint16_t resp_type,
+    uint16_t resp_code,
+    uint32_t transaction_id,
+    uint32_t* params);
+void send_mtp_response_buffer(
+    AppMTP* mtp,
+    uint16_t resp_type,
+    uint16_t resp_code,
+    uint32_t transaction_id,
+    uint8_t* buffer,
+    uint32_t size);
+int BuildDeviceInfo(uint8_t* buffer);
+void WriteMTPString(uint8_t* buffer, const char* str, uint16_t* length);
+void send_device_info(AppMTP* mtp, uint32_t transaction_id);

+ 61 - 47
src/scenes/mtp/usb.c

@@ -14,15 +14,16 @@ AppMTP* global_mtp;
 typedef enum {
     EventExit = 1 << 0,
     EventReset = 1 << 1,
-    EventRxTx = 1 << 2,
-    EventAll = EventExit | EventReset | EventRxTx,
+    EventTx = 1 << 2,
+    EventRx = 1 << 3,
+    EventInterrupt = 1 << 4,
+    EventAll = EventExit | EventReset | EventRx | EventTx | EventInterrupt,
 } MTPEvent;
 
 int32_t usb_mtp_worker(void* ctx) {
     AppMTP* mtp = ctx;
     usbd_device* dev = mtp->dev;
-
-    UNUSED(dev);
+    uint8_t buffer[MTP_MAX_PACKET_SIZE];
 
     while(true) {
         MTPEvent flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever);
@@ -36,8 +37,30 @@ int32_t usb_mtp_worker(void* ctx) {
             // Handle USB reset if necessary
         }
 
-        if(flags & EventRxTx) {
-            FURI_LOG_W("MTP", "USB Rx/Tx event");
+        if(flags & EventTx) {
+            FURI_LOG_W("MTP", "USB Tx event");
+            // Handle MTP RX/TX data here
+            // Implement the logic for processing MTP requests, sending responses, etc.
+
+            mtp->write_pending = false;
+        }
+
+        if(flags & EventRx) {
+            FURI_LOG_W("MTP", "USB Rx event");
+
+            int32_t readBytes = usbd_ep_read(dev, MTP_EP_IN_ADDR, buffer, MTP_MAX_PACKET_SIZE);
+            FURI_LOG_I("MTP", "Received %ld bytes", readBytes);
+
+            if(readBytes > 0) {
+                mtp_handle_bulk(mtp, buffer, readBytes);
+            }
+
+            // Handle MTP RX/TX data here
+            // Implement the logic for processing MTP requests, sending responses, etc.
+        }
+
+        if(flags & EventInterrupt) {
+            FURI_LOG_W("MTP", "USB Interrupt event");
             // Handle MTP RX/TX data here
             // Implement the logic for processing MTP requests, sending responses, etc.
         }
@@ -47,7 +70,6 @@ int32_t usb_mtp_worker(void* ctx) {
 }
 
 usbd_respond usb_mtp_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
-    UNUSED(dev);
     UNUSED(callback);
 
     AppMTP* mtp = global_mtp;
@@ -82,48 +104,14 @@ usbd_respond usb_mtp_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callba
             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;
-        }
+        value = mtp_handle_class_control(mtp, dev, req);
     }
 
     if(value >= 0) {
         if(value > w_length) {
             value = w_length;
         }
-        dev->driver->ep_write(0x00, req->data, value);
+        usbd_ep_write(dev, 0x00, req->data, value);
         return usbd_ack;
     }
 
@@ -134,10 +122,28 @@ void usb_mtp_txrx(usbd_device* dev, uint8_t event, uint8_t ep) {
     UNUSED(ep);
     UNUSED(event);
 
+    FURI_LOG_I("MTP", "USB Tx/Rx event");
+
+    AppMTP* mtp = global_mtp;
+    if(!mtp || mtp->dev != dev) return;
+
+    if(ep == MTP_EP_OUT_ADDR) {
+        furi_thread_flags_set(furi_thread_get_id(mtp->worker_thread), EventTx);
+    } else if(ep == MTP_EP_IN_ADDR) {
+        furi_thread_flags_set(furi_thread_get_id(mtp->worker_thread), EventRx);
+    }
+}
+
+void usb_mtp_interrupt(usbd_device* dev, uint8_t event, uint8_t ep) {
+    UNUSED(ep);
+    UNUSED(event);
+
+    FURI_LOG_I("MTP", "USB Interrupt");
+
     AppMTP* mtp = global_mtp;
     if(!mtp || mtp->dev != dev) return;
 
-    furi_thread_flags_set(furi_thread_get_id(mtp->worker_thread), EventRxTx);
+    furi_thread_flags_set(furi_thread_get_id(mtp->worker_thread), EventInterrupt);
 }
 
 usbd_respond usb_mtp_ep_config(usbd_device* dev, uint8_t cfg) {
@@ -162,8 +168,9 @@ usbd_respond usb_mtp_ep_config(usbd_device* dev, uint8_t cfg) {
         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);
+        usbd_reg_endpoint(dev, MTP_EP_INT_IN_ADDR, usb_mtp_interrupt);
         if(mtp != NULL) mtp->usb_connected = true;
+        //usbd_ep_write(dev, MTP_MAX_PACKET_SIZE, 0, 0);
         break;
     default:
         return usbd_fail;
@@ -178,6 +185,9 @@ void usb_mtp_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) {
         FURI_LOG_E("MTP", "dev is NULL");
     }
 
+    // Disconnect the device first.
+    usbd_connect(dev, false);
+
     AppMTP* mtp = ctx;
     global_mtp = mtp;
     mtp->dev = dev;
@@ -235,7 +245,9 @@ void usb_mtp_wakeup(usbd_device* dev) {
     AppMTP* mtp = global_mtp;
     if(!mtp || mtp->dev != dev) return;
 
-    mtp->is_working = true;
+    FURI_LOG_I("MTP", "USB wakeup");
+
+    mtp->usb_connected = true;
     UNUSED(dev);
 }
 
@@ -243,6 +255,8 @@ void usb_mtp_suspend(usbd_device* dev) {
     AppMTP* mtp = global_mtp;
     if(!mtp || mtp->dev != dev) return;
 
-    mtp->is_working = false;
+    FURI_LOG_I("MTP", "USB suspend");
+
+    mtp->usb_connected = false;
     furi_thread_flags_set(furi_thread_get_id(mtp->worker_thread), EventReset);
 }

+ 2 - 3
src/scenes/mtp/usb_desc.c

@@ -4,9 +4,8 @@
 #include "usb.h"
 #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("Flipper Zero Virtual MTP Device");
+const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC(USB_MANUFACTURER_STRING);
+const struct usb_string_descriptor dev_prod_desc = USB_STRING_DESC(USB_PRODUCT_STRING);
 
 const uint8_t usb_mtp_os_string[] = {
     18,

+ 9 - 9
src/scenes/mtp/usb_desc.h

@@ -3,15 +3,20 @@
 #include <furi_hal.h>
 #include <furi_hal_usb.h>
 
+#define USB_MANUFACTURER_STRING "Flipper Devices Inc."
+#define USB_PRODUCT_STRING "Flipper Zero Virtual MTP Device"
+#define USB_DEVICE_MODEL "Flipper Zero"
+
 #define USB_SUBCLASS_MTP 0x01
 #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
+// Endpoint Addresses
+#define MTP_EP_IN_ADDR 0x01
+#define MTP_EP_OUT_ADDR 0x82
+#define MTP_EP_INT_IN_ADDR 0x03
 
-#define MTP_MAX_PACKET_SIZE 512
+#define MTP_MAX_PACKET_SIZE 64
 #define USB_MAX_INTERRUPT_SIZE 28
 #define BCD_VERSION VERSION_BCD(1, 0, 0)
 
@@ -52,11 +57,6 @@ struct mtp_ext_config_desc_function {
     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;