Alex4386 1 год назад
Родитель
Сommit
00587b4061
5 измененных файлов с 576 добавлено и 169 удалено
  1. 8 0
      src/scenes/mtp/main.h
  2. 509 143
      src/scenes/mtp/mtp.c
  3. 40 11
      src/scenes/mtp/mtp.h
  4. 17 13
      src/scenes/mtp/usb.c
  5. 2 2
      src/scenes/mtp/usb_desc.h

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

@@ -19,6 +19,12 @@ typedef struct {
     bool session_open;
 } MTPSession;
 
+typedef struct FileHandle {
+    uint32_t handle;
+    char* path;
+    struct FileHandle* next;
+} FileHandle;
+
 typedef struct AppMTP {
     Submenu* menu;
     View* view;
@@ -36,6 +42,8 @@ typedef struct AppMTP {
     MTPSession session;
 
     bool write_pending;
+
+    FileHandle* handles;
 } AppMTP;
 
 AppMTP* MTP_alloc();

+ 509 - 143
src/scenes/mtp/mtp.c

@@ -4,6 +4,35 @@
 #include "mtp.h"
 #include "usb_desc.h"
 
+#define BLOCK_SIZE 65536
+
+// Supported operations (example, add as needed)
+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};
+
+// Supported device properties (example, add as needed)
+uint16_t supported_device_properties[] = {
+    0xd402, // Device friendly name
+};
+
+uint16_t supported_playback_formats[] = {
+    MTP_FORMAT_UNDEFINED,
+    MTP_FORMAT_ASSOCIATION,
+};
+
 void mtp_handle_bulk(AppMTP* mtp, uint8_t* buffer, int32_t length) {
     UNUSED(mtp);
 
@@ -26,6 +55,86 @@ void mtp_handle_bulk(AppMTP* mtp, uint8_t* buffer, int32_t length) {
         FURI_LOG_I("MTP", "Open/CloseSession operation (STUB)");
         send_mtp_response(mtp, 3, MTP_RESP_OK, container->header.transaction_id, NULL);
         break;
+    case MTP_OP_GET_STORAGE_IDS:
+        FURI_LOG_I("MTP", "GetStorageIDs operation");
+        send_storage_ids(mtp, container->header.transaction_id);
+        break;
+    case MTP_OP_GET_STORAGE_INFO: {
+        FURI_LOG_I("MTP", "GetStorageInfo operation");
+        uint8_t info[256];
+        int length = GetStorageInfo(mtp, container->params[0], info);
+        send_mtp_response_buffer(
+            mtp,
+            MTP_TYPE_DATA,
+            MTP_OP_GET_STORAGE_INFO,
+            container->header.transaction_id,
+            (uint8_t*)&info,
+            length);
+        send_mtp_response_buffer(
+            mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, container->header.transaction_id, NULL, 0);
+        break;
+    }
+    case MTP_OP_GET_OBJECT_HANDLES:
+        FURI_LOG_I("MTP", "GetObjectHandles operation");
+        if(container->params[1]) {
+            send_mtp_response_buffer(
+                mtp,
+                MTP_TYPE_RESPONSE,
+                MTP_RESP_SPEC_BY_FORMAT_UNSUPPORTED,
+                container->header.transaction_id,
+                NULL,
+                0);
+            break;
+        } else {
+            uint8_t buffer[256];
+            int length = GetObjectHandles(mtp, container->params[0], container->params[2], buffer);
+            send_mtp_response_buffer(
+                mtp,
+                MTP_TYPE_DATA,
+                MTP_OP_GET_OBJECT_HANDLES,
+                container->header.transaction_id,
+                buffer,
+                length);
+
+            send_mtp_response_buffer(
+                mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, container->header.transaction_id, NULL, 0);
+        }
+        break;
+    case MTP_OP_GET_OBJECT_INFO: {
+        FURI_LOG_I("MTP", "GetObjectInfo operation");
+        uint8_t* buffer = malloc(sizeof(uint8_t) * 512);
+        int length = GetObjectInfo(mtp, container->params[0], buffer);
+
+        if(length < 0) {
+            send_mtp_response(
+                mtp, 3, MTP_RESP_INVALID_OBJECT_HANDLE, container->header.transaction_id, NULL);
+            break;
+        }
+
+        send_mtp_response_buffer(
+            mtp,
+            MTP_TYPE_DATA,
+            MTP_OP_GET_OBJECT_INFO,
+            container->header.transaction_id,
+            buffer,
+            length);
+        free(buffer);
+
+        send_mtp_response_buffer(
+            mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, container->header.transaction_id, NULL, 0);
+        break;
+    }
+
+    case MTP_OP_GET_DEVICE_PROP_VALUE:
+        FURI_LOG_I("MTP", "GetDevicePropValue operation");
+        send_device_prop_value(mtp, container->header.transaction_id, container->params[0]);
+        // Process the GetDevicePropValue operation
+        break;
+    case MTP_OP_GET_DEVICE_PROP_DESC:
+        FURI_LOG_I("MTP", "GetDevicePropDesc operation");
+        send_device_prop_desc(mtp, container->header.transaction_id, container->params[0]);
+        // Process the GetDevicePropDesc operation
+        break;
     // Handle bulk transfer specific operations
     case MTP_OP_SEND_OBJECT_INFO:
         FURI_LOG_I("MTP", "SendObjectInfo operation");
@@ -38,14 +147,306 @@ void mtp_handle_bulk(AppMTP* mtp, uint8_t* buffer, int32_t length) {
     // Handle other bulk operations here...
     default:
         FURI_LOG_W("MTP", "Unsupported MTP operation in bulk transfer: 0x%04x", mtp_op);
+        send_mtp_response(mtp, 3, MTP_RESP_UNKNOWN, container->header.transaction_id, NULL);
         break;
     }
 }
 
+void send_storage_ids(AppMTP* mtp, uint32_t transaction_id) {
+    uint32_t count;
+    uint32_t storage_ids[3];
+    GetStorageIDs(mtp, storage_ids, &count);
+
+    FURI_LOG_I("MTP", "Sending storage IDs: %ld storages", count);
+    uint32_t payload[3] = {count, storage_ids[0], storage_ids[1]};
+    send_mtp_response_buffer(
+        mtp,
+        MTP_TYPE_DATA,
+        MTP_OP_GET_STORAGE_IDS,
+        transaction_id,
+        (uint8_t*)payload,
+        sizeof(uint32_t) * (count + 1));
+    send_mtp_response_buffer(mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, transaction_id, NULL, 0);
+}
+
 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);
+    send_mtp_response_buffer(
+        mtp, MTP_TYPE_DATA, MTP_OP_GET_DEVICE_INFO, transaction_id, response, length);
+    send_mtp_response_buffer(mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, transaction_id, NULL, 0);
+}
+
+void send_device_prop_value(AppMTP* mtp, uint32_t transaction_id, uint32_t prop_code) {
+    uint8_t response[256];
+    int length = GetDevicePropValue(prop_code, response);
+    send_mtp_response_buffer(
+        mtp, MTP_TYPE_DATA, MTP_OP_GET_DEVICE_PROP_VALUE, transaction_id, response, length);
+    send_mtp_response_buffer(mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, transaction_id, NULL, 0);
+}
+
+void send_device_prop_desc(AppMTP* mtp, uint32_t transaction_id, uint32_t prop_code) {
+    uint8_t response[256];
+    int length = GetDevicePropDesc(prop_code, response);
+    send_mtp_response_buffer(
+        mtp, MTP_TYPE_DATA, MTP_OP_GET_DEVICE_PROP_DESC, transaction_id, response, length);
+    send_mtp_response_buffer(mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, transaction_id, NULL, 0);
+}
+
+char* get_path_from_handle(AppMTP* mtp, uint32_t handle) {
+    FileHandle* current = mtp->handles;
+    while(current != NULL) {
+        if(current->handle == handle) {
+            return current->path;
+        }
+        current = current->next;
+    }
+    return NULL;
+}
+
+uint32_t issue_object_handle(AppMTP* mtp, char* path) {
+    int handle = 0;
+    int length = strlen(path);
+    char* path_store = malloc(sizeof(char) * (length + 1));
+    strcpy(path_store, path);
+
+    if(mtp->handles == NULL) {
+        mtp->handles = malloc(sizeof(FileHandle));
+        mtp->handles->handle = handle;
+        mtp->handles->path = path_store;
+        mtp->handles->next = NULL;
+        return handle;
+    }
+
+    FileHandle* current = mtp->handles;
+    if(strcmp(current->path, path) == 0) {
+        return current->handle;
+    }
+
+    while(current->next != NULL) {
+        if(strcmp(current->path, path) == 0) {
+            return current->handle;
+        }
+        current = current->next;
+        handle++;
+    }
+
+    current->next = malloc(sizeof(FileHandle));
+    current = current->next;
+    handle++;
+
+    current->handle = handle;
+    current->path = path_store;
+    current->next = NULL;
+    return handle;
+}
+
+int list_and_issue_handles(
+    AppMTP* mtp,
+    uint32_t storage_id,
+    uint32_t association,
+    uint32_t* handles) {
+    Storage* storage = mtp->storage;
+    char* base_path = "";
+    if(storage_id == INTERNAL_STORAGE_ID) {
+        base_path = STORAGE_INT_PATH_PREFIX;
+    } else if(storage_id == EXTERNAL_STORAGE_ID) {
+        base_path = STORAGE_EXT_PATH_PREFIX;
+    }
+
+    File* dir = storage_file_alloc(storage);
+    if(association == 0xffffffff) {
+        // count the objects in the root directory
+        storage_dir_open(dir, base_path);
+    } else {
+        char* path = get_path_from_handle(mtp, association);
+        if(path == NULL) {
+            return 0;
+        }
+        storage_dir_open(dir, path);
+    }
+
+    int count = 0;
+    FileInfo fileinfo;
+    char* file_name = malloc(sizeof(char) * 256);
+    char* full_path = malloc(sizeof(char) * 256);
+    while(storage_dir_read(dir, &fileinfo, file_name, 256)) {
+        if(file_info_is_dir(&fileinfo)) {
+            FURI_LOG_I("MTP", "Found directory: %s", file_name);
+        } else {
+            FURI_LOG_I("MTP", "Found file: %s", file_name);
+        }
+
+        strcpy(full_path, base_path);
+        strcat(full_path, "/");
+        strcat(full_path, file_name);
+
+        FURI_LOG_I("MTP", "Full path: %s", full_path);
+
+        uint32_t handle = issue_object_handle(mtp, full_path);
+        if(handles != NULL) {
+            handles[count] = handle;
+        }
+        count++;
+    }
+
+    FURI_LOG_I("MTP", "Getting number of objects in storage %ld", storage_id);
+    FURI_LOG_I("MTP", "Base path: %s", base_path);
+    FURI_LOG_I("MTP", "Association: %ld", association);
+    FURI_LOG_I("MTP", "Number of objects: %d", count);
+
+    storage_file_free(dir);
+    free(file_name);
+    free(full_path);
+    return count;
+}
+
+int GetObjectInfo(AppMTP* mtp, uint32_t handle, uint8_t* buffer) {
+    ObjectInfoHeader* header = (ObjectInfoHeader*)buffer;
+    char* path = get_path_from_handle(mtp, handle);
+    if(path == NULL) {
+        return -1;
+    }
+
+    FURI_LOG_I("MTP", "Getting object info for handle %ld", handle);
+    FURI_LOG_I("MTP", "Path: %s", path);
+
+    header->protection_status = 0;
+
+    Storage* storage = mtp->storage;
+    File* file = storage_file_alloc(storage);
+    uint16_t length;
+
+    FileInfo fileinfo;
+    storage_common_stat(storage, path, &fileinfo);
+
+    if(memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
+        header->storage_id = INTERNAL_STORAGE_ID;
+    } else if(memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
+        header->storage_id = EXTERNAL_STORAGE_ID;
+    } else {
+        return -1;
+    }
+
+    if(file_info_is_dir(&fileinfo)) {
+        header->format = MTP_FORMAT_ASSOCIATION;
+        header->association_type = 0x0001; // Generic folder
+    } else {
+        header->format = MTP_FORMAT_UNDEFINED;
+    }
+
+    header->compressed_size = 0;
+
+    /*
+    // is on root directory (/int or /ext)
+    if(strchr(path + 1, '/') == NULL) {
+        header->parent_object = 0;
+    } else {
+        char* parent_path = malloc(sizeof(char) * 256);
+        strcpy(parent_path, path);
+        char* last_slash = strrchr(parent_path, '/');
+        *last_slash = '\0';
+        header->parent_object = issue_object_handle(mtp, parent_path);
+
+        free(parent_path);
+    }
+    */
+
+    uint8_t* ptr = buffer + sizeof(ObjectInfoHeader);
+
+    char* file_name = strrchr(path, '/');
+    FURI_LOG_I("MTP", "File name: %s", file_name + 1);
+
+    WriteMTPString(ptr, file_name + 1, &length);
+    ptr += length;
+
+    // get last modified
+    WriteMTPString(ptr, "20231222T190925", &length);
+    ptr += length;
+
+    // get last modified
+    WriteMTPString(ptr, "20231222T190925", &length);
+    ptr += length;
+
+    storage_file_free(file);
+
+    return ptr - buffer;
+}
+
+int GetNumObjects(AppMTP* mtp, uint32_t storage_id, uint32_t association) {
+    return list_and_issue_handles(mtp, storage_id, association, NULL);
+}
+
+int GetObjectHandles(AppMTP* mtp, uint32_t storage_id, uint32_t association, uint8_t* buffer) {
+    uint8_t* ptr = buffer;
+    uint16_t length;
+
+    UNUSED(length);
+
+    // For now, just return a single object handle
+    int count = GetNumObjects(mtp, storage_id, association);
+
+    *(uint32_t*)ptr = count;
+    ptr += sizeof(uint32_t);
+
+    uint32_t* handles = (uint32_t*)ptr;
+    length = list_and_issue_handles(mtp, storage_id, association, handles);
+    ptr += length * sizeof(uint32_t);
+
+    return ptr - buffer;
+}
+
+int GetDevicePropValue(uint32_t prop_code, uint8_t* buffer) {
+    uint8_t* ptr = buffer;
+    uint16_t length;
+
+    switch(prop_code) {
+    case 0xd402:
+        WriteMTPString(ptr, "Flipper Zero", &length);
+        ptr += length;
+        break;
+    default:
+        // Unsupported property
+        break;
+    }
+
+    return ptr - buffer;
+}
+
+int GetDevicePropDesc(uint32_t prop_code, uint8_t* buffer) {
+    uint8_t* ptr = buffer;
+    uint16_t length;
+
+    switch(prop_code) {
+    case 0xd402:
+        // Device friendly name
+        *(uint16_t*)ptr = prop_code;
+        ptr += 2;
+
+        // type is string
+        *(uint16_t*)ptr = 0xffff;
+        ptr += 2;
+
+        // read-only
+        *(uint16_t*)ptr = 0x0000;
+        ptr += 2;
+
+        length = GetDevicePropValue(prop_code, ptr);
+        ptr += length;
+
+        length = GetDevicePropValue(prop_code, ptr);
+        ptr += length;
+
+        // no-form
+        *(uint16_t*)ptr = 0x0000;
+        ptr += 2;
+        break;
+    default:
+        // Unsupported property
+        break;
+    }
+
+    return ptr - buffer;
 }
 
 void send_mtp_response_buffer(
@@ -77,6 +478,8 @@ void send_mtp_response_buffer(
     FURI_LOG_I("MTP", "Sending MTP response: %d bytes in %d chunks", target_size, chunk_count);
     uint8_t* ptr = buffer;
 
+    mtp->write_pending = true;
+
     for(int i = 0; i < chunk_count; i++) {
         FURI_LOG_I("MTP", "Sending chunk %d/%d", i + 1, chunk_count);
         mtp->write_pending = true;
@@ -88,12 +491,13 @@ void send_mtp_response_buffer(
         if(i == 0) {
             // first send header
             buf = malloc(chunk_size);
+            FURI_LOG_I("MTP", "1st chunk! size = %d", 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);
+            memcpy(buf + sizeof(header), ptr, chunk_size - sizeof(header));
+            usbd_ep_write(mtp->dev, MTP_EP_IN_ADDR, buf, chunk_size);
             ptr += chunk_size - sizeof(header);
         } else {
-            usbd_ep_write(mtp->dev, MTP_EP_OUT_ADDR, ptr, chunk_size);
+            usbd_ep_write(mtp->dev, MTP_EP_IN_ADDR, ptr, chunk_size);
             ptr += chunk_size;
         }
 
@@ -116,21 +520,14 @@ void send_mtp_response(
     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,
-            },
-    };
+    uint32_t response[5] = {0};
 
     if(params != NULL) {
-        memcpy(response.params, params, sizeof(response.params));
+        memcpy(response, params, sizeof(uint32_t) * 5);
     }
 
-    usbd_ep_write(mtp->dev, MTP_EP_OUT_ADDR, (uint8_t*)&response, sizeof(response));
+    send_mtp_response_buffer(
+        mtp, resp_type, resp_code, transaction_id, (uint8_t*)response, sizeof(response));
 }
 
 int mtp_handle_class_control(AppMTP* mtp, usbd_device* dev, usbd_ctlreq* req) {
@@ -141,20 +538,24 @@ int mtp_handle_class_control(AppMTP* mtp, usbd_device* dev, usbd_ctlreq* req) {
     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);
+    UNUSED(handle);
+    UNUSED(buffer);
+    UNUSED(size);
 
-    switch(mtp_op) {
+    switch(req->bRequest) {
+    case MTP_REQ_CANCEL:
+        // Handle Cancel request
+        value = 0;
+        break;
+    case MTP_REQ_GET_EXT_EVENT_DATA:
+        // Handle GetExtEventData request
+        value = 0;
+        break;
+    case MTP_REQ_RESET:
+        // Handle Reset request
+        mtp->state = MTPStateReady;
+        value = 0;
+        break;
     case MTP_REQ_GET_DEVICE_STATUS:
         // Handle GetDeviceStatus request
         struct mtp_device_status* status = (struct mtp_device_status*)req->data;
@@ -162,58 +563,6 @@ int mtp_handle_class_control(AppMTP* mtp, usbd_device* dev, usbd_ctlreq* req) {
         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;
@@ -221,22 +570,21 @@ int mtp_handle_class_control(AppMTP* mtp, usbd_device* dev, usbd_ctlreq* req) {
 
     return value;
 }
-
 int BuildDeviceInfo(uint8_t* buffer) {
     uint8_t* ptr = buffer;
     uint16_t length;
 
     // Standard version
     *(uint16_t*)ptr = 100;
-    ptr += 2;
+    ptr += sizeof(uint16_t);
 
     // Vendor extension ID
     *(uint32_t*)ptr = MTP_VENDOR_EXTENSION_ID;
-    ptr += 4;
+    ptr += sizeof(uint32_t);
 
     // Vendor extension version
     *(uint16_t*)ptr = MTP_VENDOR_EXTENSION_VERSION;
-    ptr += 2;
+    ptr += sizeof(uint16_t);
 
     // Vendor extension description
     WriteMTPString(ptr, "microsoft.com: 1.0;", &length);
@@ -244,57 +592,43 @@ int BuildDeviceInfo(uint8_t* buffer) {
 
     // 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++) {
+    ptr += sizeof(uint16_t);
+
+    // Operations supported
+    length = sizeof(supported_operations) / sizeof(uint16_t);
+    *(uint32_t*)ptr = length; // Number of supported operations
+    ptr += sizeof(uint32_t);
+
+    for(int i = 0; i < length; i++) {
         *(uint16_t*)ptr = supported_operations[i];
-        ptr += 2;
+        ptr += sizeof(uint16_t);
     }
 
     // Supported events (example, add as needed)
     *(uint32_t*)ptr = 0; // Number of supported events
-    ptr += 4;
+    ptr += sizeof(uint32_t);
 
-    // Supported device properties (example, add as needed)
-    *(uint32_t*)ptr = 1; // Number of supported device properties
-    ptr += 4;
+    length = sizeof(supported_device_properties) / sizeof(uint16_t);
+    *(uint32_t*)ptr = length; // Number of supported device properties
+    ptr += sizeof(uint32_t);
 
-    uint16_t supported_device_properties[] = {0xd402}; // Device friendly name
-    for(int i = 0; i < 1; i++) {
+    for(int i = 0; i < length; i++) {
         *(uint16_t*)ptr = supported_device_properties[i];
-        ptr += 2;
+        ptr += sizeof(uint16_t);
     }
 
     // Supported capture formats (example, add as needed)
     *(uint32_t*)ptr = 0; // Number of supported capture formats
-    ptr += 4;
+    ptr += sizeof(uint32_t);
 
     // Supported playback formats (example, add as needed)
-    *(uint32_t*)ptr = 2; // Number of supported playback formats
-    ptr += 4;
+    length = sizeof(supported_playback_formats) / sizeof(uint16_t);
+    *(uint32_t*)ptr = length; // Number of supported playback formats
+    ptr += sizeof(uint32_t);
 
-    uint16_t supported_playback_formats[] = {MTP_FORMAT_UNDEFINED, MTP_FORMAT_ASSOCIATION};
-    for(int i = 0; i < 2; i++) {
+    for(int i = 0; i < length; i++) {
         *(uint16_t*)ptr = supported_playback_formats[i];
-        ptr += 2;
+        ptr += sizeof(uint16_t);
     }
 
     // Manufacturer
@@ -310,7 +644,7 @@ int BuildDeviceInfo(uint8_t* buffer) {
     ptr += length;
 
     // Serial number
-    WriteMTPString(ptr, "hakurei", &length);
+    WriteMTPString(ptr, "HakureiReimu", &length);
     ptr += length;
 
     return ptr - buffer;
@@ -330,61 +664,93 @@ void GetStorageIDs(AppMTP* mtp, uint32_t* storage_ids, uint32_t* count) {
     *count = 2; // We have two storages: internal and external
 }
 
-void GetStorageInfo(AppMTP* mtp, uint32_t storage_id, MTPStorageInfo* info) {
+int GetStorageInfo(AppMTP* mtp, uint32_t storage_id, uint8_t* buf) {
+    MTPStorageInfoHeader* info = (MTPStorageInfoHeader*)buf;
+    uint8_t* ptr = buf + sizeof(MTPStorageInfoHeader);
+    uint16_t length;
+
+    info->free_space_in_objects = 20ul;
+    info->filesystem_type = 0x0002; // Generic hierarchical
+    info->access_capability = 0x0000; // Read-write
+
     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);
+        FS_Error err = storage_common_fs_info(
+            mtp->storage, STORAGE_INT_PATH_PREFIX, &total_space, &free_space);
+        if(err != FSE_OK) {
+            info->max_capacity = 0;
+            info->free_space_in_bytes = 0;
+        } else {
+            info->max_capacity = total_space / BLOCK_SIZE;
+            info->free_space_in_bytes = free_space / BLOCK_SIZE;
+        }
 
-        info->max_capacity = total_space;
-        info->free_space_in_bytes = free_space;
-        info->free_space_in_objects = 0xFFFFFFFF;
+        WriteMTPString(ptr, "Internal Storage", &length);
+        ptr += length;
 
-        strcpy(info->storage_description, "Internal Storage");
-        strcpy(info->volume_identifier, "int");
+        WriteMTPString(ptr, "INT_STORAGE", &length);
+        ptr += length;
     } 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;
+        } else {
+            // Fill in details for external storage
+            info->max_capacity = (uint64_t)sd_info.kb_total * 1024 / BLOCK_SIZE;
+            info->free_space_in_bytes = (uint64_t)sd_info.kb_free * 1024 / BLOCK_SIZE;
         }
 
-        // 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");
+        WriteMTPString(ptr, "SD Card", &length);
+        ptr += length;
+
+        WriteMTPString(ptr, "SD_CARD", &length);
+        ptr += length;
     }
+
+    // try to convert into big endian???
+    //info->max_capacity = __builtin_bswap64(info->max_capacity);
+    //info->free_space_in_bytes = __builtin_bswap64(info->free_space_in_bytes);
+
+    return ptr - buf;
 }
 
 // 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)
+    *ptr = str_len + 1; // Length byte (number of characters including the null terminator)
+    ptr++;
+    while(*str) {
+        *ptr++ = *str++;
+        *ptr++ = 0x00; // UTF-16LE encoding (add null byte for each character)
+    }
+    *ptr++ = 0x00; // Null terminator (UTF-16LE)
+    *ptr++ = 0x00;
+    *length = ptr - buffer;
+}
+
+void WriteMTPBEString(uint8_t* buffer, const char* str, uint16_t* length) {
+    uint8_t* ptr = buffer;
+    uint8_t str_len = strlen(str);
+    *ptr++ = str_len + 1; // Length byte (number of characters including the null terminator)
     while(*str) {
+        *ptr++ = 0x00; // UTF-16BE encoding (add null byte for each character)
         *ptr++ = *str++;
-        *ptr++ = 0x00; // UTF-16LE null byte
     }
-    *ptr++ = 0x00; // Null terminator
+    *ptr++ = 0x00; // Null terminator (UTF-16LE)
     *ptr++ = 0x00;
     *length = ptr - buffer;
 }

+ 40 - 11
src/scenes/mtp/mtp.h

@@ -5,6 +5,10 @@
 #define MTP_VENDOR_EXTENSION_VERSION 100
 #define MTP_FUNCTIONAL_MODE 0x0
 
+#define MTP_TYPE_COMMAND 0x1
+#define MTP_TYPE_DATA 0x2
+#define MTP_TYPE_RESPONSE 0x3
+
 #define MTP_REQ_CANCEL 0x64
 #define MTP_REQ_GET_EXT_EVENT_DATA 0x65
 #define MTP_REQ_RESET 0x66
@@ -27,6 +31,7 @@
 #define MTP_OP_GET_DEVICE_PROP_VALUE 0x1015
 
 // MTP Response Codes
+#define MTP_RESP_UNKNOWN 0x2000
 #define MTP_RESP_OK 0x2001
 #define MTP_RESP_GENERAL_ERROR 0x2002
 #define MTP_RESP_SESSION_NOT_OPEN 0x2003
@@ -52,14 +57,9 @@
 #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
+#define INTERNAL_STORAGE_ID 0x00010001
+#define EXTERNAL_STORAGE_ID 0x00020001
 
 // MTP Playback formats
 #define MTP_FORMAT_UNDEFINED 0x3000
@@ -86,9 +86,25 @@ typedef struct {
     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;
+} MTPStorageInfoHeader;
+
+typedef struct ObjectInfoHeader {
+    uint32_t storage_id;
+    uint16_t format;
+    uint16_t protection_status;
+    uint32_t compressed_size;
+    uint16_t thumb_format;
+    uint32_t thumb_compressed_size;
+    uint32_t thumb_pix_width;
+    uint32_t thumb_pix_height;
+    uint32_t image_pix_width;
+    uint32_t image_pix_height;
+    uint32_t image_bit_depth;
+    uint32_t parent_object;
+    uint16_t association_type;
+    uint32_t association_desc;
+    uint32_t sequence_number;
+} ObjectInfoHeader;
 
 struct MTPHeader {
     uint32_t len; // 0
@@ -102,10 +118,14 @@ struct MTPContainer {
     uint32_t params[5]; // 12
 };
 
+extern uint16_t supported_operations[];
+extern uint16_t supported_device_properties[];
+extern uint16_t supported_playback_formats[];
+
 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);
+int GetStorageInfo(AppMTP* mtp, uint32_t storage_id, uint8_t* buf);
 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);
@@ -128,4 +148,13 @@ void send_mtp_response_buffer(
     uint32_t size);
 int BuildDeviceInfo(uint8_t* buffer);
 void WriteMTPString(uint8_t* buffer, const char* str, uint16_t* length);
+void WriteMTPBEString(uint8_t* buffer, const char* str, uint16_t* length);
 void send_device_info(AppMTP* mtp, uint32_t transaction_id);
+void send_storage_ids(AppMTP* mtp, uint32_t transaction_id);
+
+void send_device_prop_value(AppMTP* mtp, uint32_t transaction_id, uint32_t prop_code);
+void send_device_prop_desc(AppMTP* mtp, uint32_t transaction_id, uint32_t prop_code);
+int GetDevicePropValue(uint32_t prop_code, uint8_t* buffer);
+int GetDevicePropDesc(uint32_t prop_code, uint8_t* buffer);
+int GetObjectHandles(AppMTP* mtp, uint32_t storage_id, uint32_t association, uint8_t* buffer);
+int GetObjectInfo(AppMTP* mtp, uint32_t handle, uint8_t* buffer);

+ 17 - 13
src/scenes/mtp/usb.c

@@ -10,14 +10,16 @@
 #include "mtp.h"
 
 AppMTP* global_mtp;
+uint8_t last_ep;
 
 typedef enum {
     EventExit = 1 << 0,
     EventReset = 1 << 1,
-    EventTx = 1 << 2,
-    EventRx = 1 << 3,
-    EventInterrupt = 1 << 4,
-    EventAll = EventExit | EventReset | EventRx | EventTx | EventInterrupt,
+    EventRx = 1 << 2,
+    EventTx = 1 << 3,
+    EventRxOther = 1 << 4,
+    EventInterrupt = 1 << 5,
+    EventAll = EventExit | EventReset | EventRx | EventTx | EventRxOther | EventInterrupt,
 } MTPEvent;
 
 int32_t usb_mtp_worker(void* ctx) {
@@ -41,15 +43,14 @@ int32_t usb_mtp_worker(void* ctx) {
             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);
+            int32_t readBytes = usbd_ep_read(dev, MTP_EP_OUT_ADDR, buffer, MTP_MAX_PACKET_SIZE);
+            FURI_LOG_I("MTP", "Received %ld bytes on RX", readBytes);
 
             if(readBytes > 0) {
                 mtp_handle_bulk(mtp, buffer, readBytes);
@@ -59,6 +60,10 @@ int32_t usb_mtp_worker(void* ctx) {
             // Implement the logic for processing MTP requests, sending responses, etc.
         }
 
+        if(flags & EventRxOther) {
+            FURI_LOG_W("MTP", "USB Rx Other event. ep: %02x", last_ep);
+        }
+
         if(flags & EventInterrupt) {
             FURI_LOG_W("MTP", "USB Interrupt event");
             // Handle MTP RX/TX data here
@@ -89,7 +94,6 @@ usbd_respond usb_mtp_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callba
 
     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;
@@ -97,7 +101,6 @@ usbd_respond usb_mtp_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callba
     } 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);
@@ -122,15 +125,16 @@ 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);
+    } else if(ep == MTP_EP_IN_ADDR) {
+        furi_thread_flags_set(furi_thread_get_id(mtp->worker_thread), EventTx);
+    } else {
+        furi_thread_flags_set(furi_thread_get_id(mtp->worker_thread), EventRxOther);
+        last_ep = ep;
     }
 }
 

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

@@ -12,8 +12,8 @@
 #define USB_CONF_VAL_MTP 1
 
 // Endpoint Addresses
-#define MTP_EP_IN_ADDR 0x01
-#define MTP_EP_OUT_ADDR 0x82
+#define MTP_EP_IN_ADDR 0x81
+#define MTP_EP_OUT_ADDR 0x02
 #define MTP_EP_INT_IN_ADDR 0x03
 
 #define MTP_MAX_PACKET_SIZE 64