소스 검색

Merge mtp from https://github.com/Alex4386/f0-mtp

# Conflicts:
#	mtp/src/scenes/mtp/mtp.c
#	mtp/src/scenes/mtp/mtp.h
Willy-JL 1 년 전
부모
커밋
b6c770d75c
6개의 변경된 파일394개의 추가작업 그리고 33개의 파일을 삭제
  1. 17 0
      mtp/.clangd
  2. 34 22
      mtp/README.md
  3. 321 9
      mtp/src/scenes/mtp/mtp.c
  4. 16 2
      mtp/src/scenes/mtp/mtp.h
  5. 4 0
      mtp/src/scenes/mtp/utils.c
  6. 2 0
      mtp/src/scenes/mtp/utils.h

+ 17 - 0
mtp/.clangd

@@ -0,0 +1,17 @@
+CompileFlags:
+    Add: 
+        - -Wno-unknown-warning-option
+        - -Wno-format
+    Remove: 
+        - -mword-relocations
+
+Diagnostics:
+    ClangTidy:
+        FastCheckFilter: None
+
+---
+
+If:
+    PathMatch: .*\.h
+Diagnostics:
+    UnusedIncludes: None

+ 34 - 22
mtp/README.md

@@ -9,18 +9,18 @@ See this in action on YouTube!<br>
 - **Latest Release**: [Download](https://github.com/Alex4386/f0-mtp/releases/latest)
 - **Latest Nightly**: [Download](https://github.com/Alex4386/f0-mtp/actions/workflows/nightly.yml) _(GitHub Login Required)_
 
-|                                           Nightly Build                                           |                                           Release Build                                           |
-| :-----------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------: |
+|                                        Nightly Build                                         |                                        Release Build                                         |
+| :------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------: |
 | ![Nightly Build](https://github.com/Alex4386/f0-mtp/actions/workflows/nightly.yml/badge.svg) | ![Release Build](https://github.com/Alex4386/f0-mtp/actions/workflows/release.yml/badge.svg) |
 
 ## What is this?
 
 `f0-mtp` is a application that implements [`MTP (Media Transfer Protocol)` defined by `USB-IF`](https://www.usb.org/document-library/media-transfer-protocol-v11-spec-and-mtp-v11-adopters-agreement) on Flipper Zero.  
-This allows you to access the Flipper Zero's internal and SD card storages from your computer without the need of any additional drivers like [`HID-File-Transfer`](https://github.com/Kavakuo/HID-File-Transfer).  
-  
-If your computer can handle Android devices, it should be able to handle Flipper Zero as well with this application.  
+This allows you to access the Flipper Zero's internal and SD card storages from your computer without the need of any additional drivers like [`HID-File-Transfer`](https://github.com/Kavakuo/HID-File-Transfer).
 
-Due to limitation of the Flipper Zero's hardware, The MTP connection utilizes `Full Speed` USB, which is USB 1.1 (12Mbps). which may be slow&trade;.  
+If your computer can handle Android devices, it should be able to handle Flipper Zero as well with this application.
+
+Due to limitation of the Flipper Zero's hardware, The MTP connection utilizes `Full Speed` USB, which is USB 1.1 (12Mbps). which may be slow&trade;.
 
 > [!NOTE]
 > Flipper Zero utilizes `SPI` for SD card, which speed is limited to around 1MB/s.  
@@ -28,53 +28,65 @@ Due to limitation of the Flipper Zero's hardware, The MTP connection utilizes `F
 > <b>So, If you need quick copy, Use a proper SD Card Reader.</b>
 
 ### Before using...
+
 Here are some things you should know before using this application:
 
-> [!WARNING]
+> [!WARNING]  
 > **DO NOT transfer files over 64K** in one go.  
 > This will:
+>
 > - **Crash the Flipper** if the file is too big.
-> 
-> And _might_ :  
+>
+> And _might_ :
+>
 > - **Corrupt the SD Card filesystem** due to current implementation's limitation.
 >   (If you know how to fix this issue, feel free to give me a PR!, quick `chkdsk` will fix the corruption though)
->
 
-> [!WARNING]
+> [!WARNING]  
 > **DO NOT** use `UNICODE` characters in the file/directory names.  
 > Flipper Zero's filesystem isn't designed to handle `UNICODE` characters. such as:
+>
 > - `한글.txt`
 > - `日本語.txt`
 > - `中文.txt`
 
 ### Features
-* Access Internal and SD card storages
-* List files and directories
-   - Navigate directories
-* Opening Files (Downloading Flipper files into Computer)
+
+- Access Internal and SD card storages
+- List files and directories
+  - Navigate directories
+- Opening Files (Downloading Flipper files into Computer)
   - Large file transfer now **WORKS**!
     - It didn't work since the header did not have proper `size` defined, ignoring the further packets.
     - Now utilizing even less memory via `streaming` support!
-* Move Files into Flipper
+- Move Files into Flipper
   - **NEW!** Now you can upload files to Flipper Zero!
   - **Note:** Flipper Zero has limited memory, please refrain from moving files bigger than 64K on one go. (for me 80K was a hard limit)
-* Deleting Files/Directories
+- Deleting Files/Directories
+- Move Files/Directories
+- Create Directories (Renaming not supported)
+- Actual **Serial Number**, **Model Name**, **Firmware Version** is now shown in the MTP client.
 
 ### Known Issues
-* Renaming directories, files are not supported yet.
-* Fix "memory leaks"  
+
+- Renaming directories, files are not supported yet.
+- Fix "memory leaks"
   - I'm currently busy working on code cleanup. So, I can't afford to do this right now.
 
 ## How to build
-See [HOW_TO_BUILD.md](HOW_TO_BUILD.md) for more information.  
+
+See [HOW_TO_BUILD.md](HOW_TO_BUILD.md) for more information.
 
 ## Special Thanks
+
 - [viveris/uMTP-Responder](https://github.com/viveris/uMTP-Responder)
 - [yoonghm/MTP](https://github.com/yoonghm/MTP)
 
-**and Special NOT Thanks to**:  
-- [Microsoft](https://microsoft.com) for making the [MTP Spec](https://www.usb.org/document-library/media-transfer-protocol-v11-spec-and-mtp-v11-adopters-agreement) so hard to understand. `>:(`  
+**and Special NOT Thanks to**:
+
+- [Microsoft](https://microsoft.com) for making the [MTP Spec](https://www.usb.org/document-library/media-transfer-protocol-v11-spec-and-mtp-v11-adopters-agreement) so hard to understand. `>:(`
 - [Microsoft](https://microsoft.com) for reserving place for programmers to live in `WideChar` and `MultiByte` hellscape. `>:(`
 
 ## License
+
 This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

+ 321 - 9
mtp/src/scenes/mtp/mtp.c

@@ -1,4 +1,5 @@
 #include <furi.h>
+#include <power/power_service/power.h>
 #include <storage/storage.h>
 #include "main.h"
 #include "mtp.h"
@@ -24,7 +25,10 @@ uint16_t supported_operations[] = {
     MTP_OP_GET_DEVICE_PROP_DESC,
     MTP_OP_GET_DEVICE_PROP_VALUE,
     MTP_OP_GET_OBJECT_PROPS_SUPPORTED,
-    MTP_OP_SET_OBJECT_PROP_VALUE};
+    MTP_OP_SET_OBJECT_PROP_VALUE,
+    MTP_OP_MOVE_OBJECT,
+    MTP_OP_POWER_DOWN,
+};
 
 uint16_t supported_object_props[] = {
     MTP_PROP_STORAGE_ID,
@@ -35,6 +39,7 @@ uint16_t supported_object_props[] = {
 // Supported device properties (example, add as needed)
 uint16_t supported_device_properties[] = {
     0xd402, // Device friendly name
+    0x5001, // Battery level
 };
 
 uint16_t supported_playback_formats[] = {
@@ -173,6 +178,47 @@ void handle_mtp_data_packet(AppMTP* mtp, uint8_t* buffer, int32_t length, bool c
 
             persistence.current_file = NULL;
         }
+    } else if(persistence.op == MTP_OP_SET_OBJECT_PROP_VALUE) {
+        uint32_t handle = persistence.params[0];
+        uint32_t prop_code = persistence.params[1];
+
+        char* path = get_path_from_handle(mtp, handle);
+        if(path == NULL) {
+            FURI_LOG_E("MTP", "Invalid handle for MTP_OP_SET_OBJECT_PROP_VALUE");
+            send_mtp_response(
+                mtp, 3, MTP_RESP_INVALID_OBJECT_HANDLE, persistence.transaction_id, NULL);
+            return;
+        }
+
+        if(prop_code == MTP_PROP_OBJECT_FILE_NAME) {
+            // dump the ptr
+            print_bytes("MTP FileName", ptr, length - sizeof(struct MTPHeader));
+
+            char* name = ReadMTPString(ptr);
+            char* full_path = malloc(sizeof(char) * 256);
+
+            merge_path(full_path, get_base_path_from_storage_id(persistence.params[2]), name);
+
+            // if(!storage_file_exists(mtp->storage, full_path)) {
+            //     if(!storage_file_rename(mtp->storage, path, full_path)) {
+            //         FURI_LOG_E("MTP", "Failed to rename file: %s to %s", path, full_path);
+            //         send_mtp_response(
+            //             mtp, 3, MTP_RESP_GENERAL_ERROR, persistence.transaction_id, NULL);
+            //         free(full_path);
+            //         free(name);
+            //         return;
+            //     }
+            // }
+
+            update_object_handle_path(mtp, handle, full_path);
+            send_mtp_response(mtp, 3, MTP_RESP_OK, persistence.transaction_id, NULL);
+            free(full_path);
+            free(name);
+        } else {
+            FURI_LOG_W("MTP", "Unsupported property code: 0x%04lx", prop_code);
+            send_mtp_response(
+                mtp, 3, MTP_RESP_INVALID_OBJECT_PROP_CODE, persistence.transaction_id, NULL);
+        }
     }
 
     if(persistence.global_buffer != NULL) {
@@ -458,7 +504,39 @@ void handle_mtp_command(AppMTP* mtp, struct MTPContainer* container) {
 
         break;
     }
+    case MTP_OP_MOVE_OBJECT: {
+        FURI_LOG_I("MTP", "MoveObject operation");
+        // Process the MoveObject operation
+        MoveObject(
+            mtp,
+            container->header.transaction_id,
+            container->params[0],
+            container->params[1],
+            container->params[2]);
+        break;
+    }
+    case MTP_OP_POWER_DOWN: {
+        FURI_LOG_I("MTP", "PowerDown operation");
+
+        FURI_LOG_I("MTP", "Good night.");
+        send_mtp_response(mtp, 3, MTP_RESP_OK, container->header.transaction_id, NULL);
+
+        furi_hal_power_off();
+        break;
+    }
     // Handle other bulk operations here...
+    case MTP_OP_GET_OBJECT_PROP_VALUE: {
+        FURI_LOG_I("MTP", "GetObjectPropValue operation");
+        // Process the GetObjectPropValue operation
+        GetObjectPropValue(
+            mtp, container->header.transaction_id, container->params[0], container->params[1]);
+        break;
+    }
+    case MTP_OP_SET_OBJECT_PROP_VALUE: {
+        FURI_LOG_I("MTP", "SetObjectPropValue operation");
+        setup_persistence(container);
+        break;
+    }
     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);
@@ -522,7 +600,7 @@ char* get_path_from_handle(AppMTP* mtp, uint32_t handle) {
 }
 
 uint32_t issue_object_handle(AppMTP* mtp, char* path) {
-    int handle = 0;
+    int handle = 1;
     int length = strlen(path);
     char* path_store = malloc(sizeof(char) * (length + 1));
     strcpy(path_store, path);
@@ -558,6 +636,19 @@ uint32_t issue_object_handle(AppMTP* mtp, char* path) {
     return handle;
 }
 
+uint32_t update_object_handle_path(AppMTP* mtp, uint32_t handle, char* path) {
+    FileHandle* current = mtp->handles;
+    while(current != NULL) {
+        if(current->handle == handle) {
+            free(current->path);
+            current->path = path;
+            return handle;
+        }
+        current = current->next;
+    }
+    return 0;
+}
+
 char* get_base_path_from_storage_id(uint32_t storage_id) {
     if(storage_id == INTERNAL_STORAGE_ID) {
         return STORAGE_INT_PATH_PREFIX;
@@ -823,10 +914,34 @@ int GetDevicePropValue(uint32_t prop_code, uint8_t* buffer) {
     uint16_t length;
 
     switch(prop_code) {
-    case 0xd402:
-        WriteMTPString(ptr, "Flipper Zero", &length);
+    case 0xd402: {
+        const char* deviceName = furi_hal_version_get_name_ptr();
+        if(deviceName == NULL) {
+            deviceName = "Flipper Zero";
+        }
+
+        WriteMTPString(ptr, deviceName, &length);
         ptr += length;
         break;
+    }
+    case 0x5001: {
+        // Battery level
+        Power* power = furi_record_open(RECORD_POWER);
+        FURI_LOG_I("MTP", "Getting battery level");
+        if(power == NULL) {
+            *(uint8_t*)ptr = 0x00;
+        } else {
+            PowerInfo info;
+            power_get_info(power, &info);
+
+            FURI_LOG_I("MTP", "Battery level: %d", info.charge);
+
+            *(uint8_t*)ptr = info.charge;
+            furi_record_close(RECORD_POWER);
+        }
+        ptr += sizeof(uint8_t);
+        break;
+    }
     default:
         // Unsupported property
         break;
@@ -850,8 +965,8 @@ int GetDevicePropDesc(uint32_t prop_code, uint8_t* buffer) {
         ptr += 2;
 
         // read-only
-        *(uint16_t*)ptr = 0x0000;
-        ptr += 2;
+        *(uint8_t*)ptr = 0x00;
+        ptr += 1;
 
         length = GetDevicePropValue(prop_code, ptr);
         ptr += length;
@@ -860,9 +975,33 @@ int GetDevicePropDesc(uint32_t prop_code, uint8_t* buffer) {
         ptr += length;
 
         // no-form
-        *(uint16_t*)ptr = 0x0000;
+        *(uint8_t*)ptr = 0x00;
+        ptr += 1;
+        break;
+    case 0x5001:
+        // Device friendly name
+        *(uint16_t*)ptr = prop_code;
+        ptr += 2;
+
+        // type is uint8
+        *(uint16_t*)ptr = 0x0002;
         ptr += 2;
+
+        // read-only
+        *(uint8_t*)ptr = 0x00;
+        ptr += 1;
+
+        length = GetDevicePropValue(prop_code, ptr);
+        ptr += length;
+
+        length = GetDevicePropValue(prop_code, ptr);
+        ptr += length;
+
+        // no-form
+        *(uint8_t*)ptr = 0x00;
+        ptr += 1;
         break;
+
     default:
         // Unsupported property
         break;
@@ -1037,28 +1176,35 @@ int BuildDeviceInfo(uint8_t* buffer) {
     ptr += length;
 
     // Functional mode
+    FURI_LOG_I("MTP", "MTP Functional Mode");
     *(uint16_t*)ptr = MTP_FUNCTIONAL_MODE;
     ptr += sizeof(uint16_t);
 
     // Operations supported
+    FURI_LOG_I("MTP", "Writing Operation Supported");
     length = sizeof(supported_operations) / sizeof(uint16_t);
     *(uint32_t*)ptr = length; // Number of supported operations
+    FURI_LOG_I("MTP", "Supported Operations: %d", length);
     ptr += sizeof(uint32_t);
 
     for(int i = 0; i < length; i++) {
+        FURI_LOG_I("MTP", "Operation: %04x", supported_operations[i]);
         *(uint16_t*)ptr = supported_operations[i];
         ptr += sizeof(uint16_t);
     }
 
     // Supported events (example, add as needed)
+    FURI_LOG_I("MTP", "Supported Events");
     *(uint32_t*)ptr = 0; // Number of supported events
     ptr += sizeof(uint32_t);
 
+    FURI_LOG_I("MTP", "Supported Device Properties");
     length = sizeof(supported_device_properties) / sizeof(uint16_t);
     *(uint32_t*)ptr = length; // Number of supported device properties
     ptr += sizeof(uint32_t);
 
     for(int i = 0; i < length; i++) {
+        FURI_LOG_I("MTP", "Supported Device Props");
         *(uint16_t*)ptr = supported_device_properties[i];
         ptr += sizeof(uint16_t);
     }
@@ -1068,6 +1214,7 @@ int BuildDeviceInfo(uint8_t* buffer) {
     ptr += sizeof(uint32_t);
 
     // Supported playback formats (example, add as needed)
+    FURI_LOG_I("MTP", "Supported Playback Formats");
     length = sizeof(supported_playback_formats) / sizeof(uint16_t);
     *(uint32_t*)ptr = length; // Number of supported playback formats
     ptr += sizeof(uint32_t);
@@ -1086,13 +1233,24 @@ int BuildDeviceInfo(uint8_t* buffer) {
     ptr += length;
 
     // Device version
-    WriteMTPString(ptr, MTP_DEVICE_VERSION, &length);
+    const Version* ver = furi_hal_version_get_firmware_version();
+    WriteMTPString(ptr, version_get_version(ver), &length);
     ptr += length;
 
     // Serial number
-    WriteMTPString(ptr, MTP_DEVICE_SERIAL, &length);
+    char* serial = malloc(sizeof(char) * furi_hal_version_uid_size() * 2 + 1);
+    const uint8_t* uid = furi_hal_version_uid();
+    for(size_t i = 0; i < furi_hal_version_uid_size(); i++) {
+        serial[i * 2] = byte_to_hex(uid[i] >> 4);
+        serial[i * 2 + 1] = byte_to_hex(uid[i] & 0x0F);
+    }
+    serial[furi_hal_version_uid_size() * 2] = '\0';
+
+    WriteMTPString(ptr, serial, &length);
     ptr += length;
 
+    free(serial);
+
     return ptr - buffer;
 }
 
@@ -1255,3 +1413,157 @@ bool DeleteObject(AppMTP* mtp, uint32_t handle) {
 
     return false;
 }
+
+void MoveObject(
+    AppMTP* mtp,
+    uint32_t transaction_id,
+    uint32_t handle,
+    uint32_t storage_id,
+    uint32_t parent) {
+    UNUSED(storage_id);
+
+    FURI_LOG_I("MTP", "Moving object %ld to storage %ld, parent %ld", handle, storage_id, parent);
+    char* path = get_path_from_handle(mtp, handle);
+    if(path == NULL) {
+        send_mtp_response(
+            mtp, MTP_TYPE_RESPONSE, MTP_RESP_INVALID_OBJECT_HANDLE, transaction_id, NULL);
+        return;
+    }
+
+    char* parentPath;
+
+    if(parent != 0) {
+        parentPath = get_path_from_handle(mtp, parent);
+        if(parentPath == NULL) {
+            send_mtp_response(
+                mtp, MTP_TYPE_RESPONSE, MTP_RESP_INVALID_OBJECT_HANDLE, transaction_id, NULL);
+            return;
+        }
+    } else {
+        parentPath = get_base_path_from_storage_id(storage_id);
+    }
+
+    Storage* storage = mtp->storage;
+
+    char* filename = strrchr(path, '/');
+    // remove the beginning slash
+    char* realFilename = filename + 1;
+
+    char* newPath = malloc(sizeof(char) * 256);
+    merge_path(newPath, parentPath, realFilename);
+
+    FURI_LOG_I("MTP", "Moving object: %s to %s", path, newPath);
+
+    if(storage_common_rename(storage, path, newPath) != FSE_OK) {
+        FURI_LOG_E("MTP", "Failed to move object: %s", path);
+        send_mtp_response(
+            mtp, MTP_TYPE_RESPONSE, MTP_RESP_INVALID_OBJECT_HANDLE, transaction_id, NULL);
+        return;
+    }
+
+    FURI_LOG_I("MTP", "Object moved successfully");
+    send_mtp_response(mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, transaction_id, NULL);
+
+    update_object_handle_path(mtp, handle, newPath);
+    free(path);
+
+    return;
+}
+
+int GetObjectPropValueInternal(AppMTP* mtp, const char* path, uint32_t prop_code, uint8_t* buffer) {
+    UNUSED(mtp);
+    uint8_t* ptr = buffer;
+    uint16_t length;
+
+    switch(prop_code) {
+    case MTP_PROP_STORAGE_ID: {
+        FURI_LOG_I("MTP", "Getting storage ID for object: %s", path);
+
+        *(uint32_t*)ptr = prop_code;
+        ptr += sizeof(uint32_t);
+
+        *(uint32_t*)ptr = 0x0006;
+        ptr += sizeof(uint32_t);
+
+        *(uint8_t*)ptr = 0x00;
+        ptr += sizeof(uint8_t);
+
+        if(memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
+            *(uint32_t*)ptr = INTERNAL_STORAGE_ID;
+        } else if(memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
+            *(uint32_t*)ptr = EXTERNAL_STORAGE_ID;
+        } else {
+            *(uint32_t*)ptr = 0;
+        }
+        ptr += sizeof(uint32_t);
+
+        *(uint8_t*)ptr = 0x00;
+        break;
+    }
+    case MTP_PROP_OBJECT_FORMAT: {
+        FURI_LOG_I("MTP", "Getting object format for object: %s", path);
+
+        *(uint32_t*)ptr = prop_code;
+        ptr += sizeof(uint32_t);
+
+        *(uint32_t*)ptr = 0x0006;
+        ptr += sizeof(uint32_t);
+
+        *(uint8_t*)ptr = 0x00;
+        ptr += sizeof(uint8_t);
+
+        *(uint16_t*)ptr = MTP_FORMAT_UNDEFINED;
+        ptr += sizeof(uint16_t);
+
+        *(uint8_t*)ptr = 0x00;
+        break;
+    }
+    case MTP_PROP_OBJECT_FILE_NAME: {
+        FURI_LOG_I("MTP", "Getting object file name for object: %s", path);
+
+        *(uint32_t*)ptr = prop_code;
+        ptr += sizeof(uint32_t);
+
+        *(uint32_t*)ptr = 0xffff;
+        ptr += sizeof(uint32_t);
+
+        *(uint8_t*)ptr = 0x01;
+        ptr += sizeof(uint8_t);
+
+        char* file_name = strrchr(path, '/');
+        WriteMTPString(ptr, file_name + 1, &length);
+        ptr += length;
+
+        *(uint8_t*)ptr = 0x00;
+        break;
+    }
+    }
+
+    return ptr - buffer;
+}
+
+void GetObjectPropValue(AppMTP* mtp, uint32_t transaction_id, uint32_t handle, uint32_t prop_code) {
+    FURI_LOG_I(
+        "MTP", "Getting object property value for handle %ld, prop code %04lx", handle, prop_code);
+
+    char* path = get_path_from_handle(mtp, handle);
+    if(path == NULL) {
+        send_mtp_response(
+            mtp, MTP_TYPE_RESPONSE, MTP_RESP_INVALID_OBJECT_HANDLE, transaction_id, NULL);
+        return;
+    }
+
+    uint8_t* buffer = malloc(sizeof(uint8_t) * 256);
+    int length = GetObjectPropValueInternal(mtp, path, prop_code, buffer);
+    if(length < 0) {
+        send_mtp_response(
+            mtp, MTP_TYPE_RESPONSE, MTP_RESP_INVALID_OBJECT_HANDLE, transaction_id, NULL);
+        free(buffer);
+        return;
+    }
+
+    send_mtp_response_buffer(
+        mtp, MTP_TYPE_DATA, MTP_OP_GET_OBJECT_PROP_VALUE, transaction_id, buffer, length);
+    send_mtp_response(mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, transaction_id, NULL);
+    free(buffer);
+}

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

@@ -1,8 +1,8 @@
 #pragma once
 
 // MTP Device Serial
-#define MTP_DEVICE_SERIAL  "HakureiReimu"
-#define MTP_DEVICE_VERSION "1.0"
+#define MTP_DEVICE_FALLBACK_SERIAL \
+    "HakureiReimu" // If you found this, Thank you for checking my (shitty) code!
 
 #define MTP_STANDARD_VERSION         100
 #define MTP_VENDOR_EXTENSION_ID      0x6
@@ -32,9 +32,12 @@
 #define MTP_OP_DELETE_OBJECT         0x100B
 #define MTP_OP_SEND_OBJECT_INFO      0x100C
 #define MTP_OP_SEND_OBJECT           0x100D
+#define MTP_OP_POWER_DOWN            0x1013
 #define MTP_OP_GET_DEVICE_PROP_DESC  0x1014
 #define MTP_OP_GET_DEVICE_PROP_VALUE 0x1015
 
+#define MTP_OP_MOVE_OBJECT 0x1019
+
 #define MTP_OP_GET_OBJECT_PROPS_SUPPORTED 0x9801
 #define MTP_OP_GET_OBJECT_PROP_DESC       0x9802
 #define MTP_OP_GET_OBJECT_PROP_VALUE      0x9803
@@ -71,6 +74,7 @@
 #define MTP_RESP_UNKNOWN_VENDOR_CODE        0x2017
 #define MTP_RESP_CAPTURE_ALREADY_TERMINATED 0x2018
 #define MTP_RESP_DEVICE_BUSY                0x2019
+#define MTP_RESP_INVALID_OBJECT_PROP_CODE   0xA801
 
 // Storage IDs
 #define INTERNAL_STORAGE_ID 0x00010001
@@ -200,6 +204,16 @@ char* get_base_path_from_storage_id(uint32_t storage_id);
 char* get_path_from_handle(AppMTP* mtp, uint32_t handle);
 uint32_t issue_object_handle(AppMTP* mtp, char* path);
 void handle_mtp_data_complete(AppMTP* mtp);
+uint32_t update_object_handle_path(AppMTP* mtp, uint32_t handle, char* path);
+
+void MoveObject(
+    AppMTP* mtp,
+    uint32_t transaction_id,
+    uint32_t handle,
+    uint32_t storage_id,
+    uint32_t parent);
+
+void GetObjectPropValue(AppMTP* mtp, uint32_t transaction_id, uint32_t handle, uint32_t prop_code);
 
 struct MTPResponseBufferContext {
     uint8_t* buffer;

+ 4 - 0
mtp/src/scenes/mtp/utils.c

@@ -2,6 +2,10 @@
 
 const char hex[] = "0123456789ABCDEF";
 
+char byte_to_hex(uint8_t byte) {
+    return hex[byte];
+}
+
 void print_bytes(char* tag, uint8_t* bytes, size_t len) {
     FURI_LOG_I("MTP", "Dumping bytes - TAG: %s", tag);
     int lines = len > 0 ? (len / 16) + 1 : 0;

+ 2 - 0
mtp/src/scenes/mtp/utils.h

@@ -1,3 +1,5 @@
 #pragma once
 #include <furi.h>
+
+char byte_to_hex(uint8_t byte);
 void print_bytes(char* tag, uint8_t* bytes, size_t len);