|
|
@@ -3,61 +3,13 @@
|
|
|
#include <storage/storage.h>
|
|
|
#include "main.h"
|
|
|
#include "mtp.h"
|
|
|
+#include "mtp_ops.h"
|
|
|
+#include "mtp_support.h"
|
|
|
+#include "device_props.h"
|
|
|
+#include "storage_ops.h"
|
|
|
#include "usb_desc.h"
|
|
|
#include "utils.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,
|
|
|
- MTP_OP_GET_OBJECT_PROPS_SUPPORTED,
|
|
|
- MTP_OP_SET_OBJECT_PROP_VALUE,
|
|
|
- MTP_OP_MOVE_OBJECT,
|
|
|
- MTP_OP_POWER_DOWN,
|
|
|
-};
|
|
|
-
|
|
|
-uint16_t supported_object_props[] = {
|
|
|
- MTP_PROP_STORAGE_ID,
|
|
|
- MTP_PROP_OBJECT_FORMAT,
|
|
|
- MTP_PROP_OBJECT_FILE_NAME,
|
|
|
-};
|
|
|
-
|
|
|
-// Supported device properties (example, add as needed)
|
|
|
-uint16_t supported_device_properties[] = {
|
|
|
- 0xd402, // Device friendly name
|
|
|
- 0x5001, // Battery level
|
|
|
-};
|
|
|
-
|
|
|
-uint16_t supported_playback_formats[] = {
|
|
|
- MTP_FORMAT_UNDEFINED,
|
|
|
- MTP_FORMAT_ASSOCIATION,
|
|
|
-};
|
|
|
-
|
|
|
-void merge_path(char* target, char* base, char* name) {
|
|
|
- // implement this way since strcat is unavailable
|
|
|
-
|
|
|
- char* ptr = target;
|
|
|
- strcpy(target, base);
|
|
|
- ptr += strlen(base);
|
|
|
- strcpy(ptr, "/");
|
|
|
- ptr++;
|
|
|
- strcpy(ptr, name);
|
|
|
-}
|
|
|
-
|
|
|
// temporary storage for buffer
|
|
|
MTPDataPersistence persistence;
|
|
|
|
|
|
@@ -124,7 +76,7 @@ void handle_mtp_data_packet(AppMTP* mtp, uint8_t* buffer, int32_t length, bool c
|
|
|
switch(header->op) {
|
|
|
case MTP_OP_SEND_OBJECT_INFO:
|
|
|
case MTP_OP_SET_OBJECT_PROP_VALUE: {
|
|
|
- persistence.global_buffer = malloc(sizeof(uint8_t) * 256);
|
|
|
+ persistence.global_buffer = malloc(sizeof(uint8_t) * MTP_BUFFER_SIZE);
|
|
|
persistence.buffer_offset = 0;
|
|
|
break;
|
|
|
}
|
|
|
@@ -163,20 +115,34 @@ void handle_mtp_data_packet(AppMTP* mtp, uint8_t* buffer, int32_t length, bool c
|
|
|
}
|
|
|
|
|
|
File* file = persistence.current_file;
|
|
|
- // no need since the file is already opened
|
|
|
- // storage_file_seek(file, offset, SEEK_SET);
|
|
|
|
|
|
- storage_file_write(file, ptr, bytes_length);
|
|
|
+ // Write with error handling
|
|
|
+ uint16_t bytes_written = storage_file_write(file, ptr, bytes_length);
|
|
|
+ if(bytes_written != bytes_length) {
|
|
|
+ FURI_LOG_E("MTP", "Write failed: %d/%d bytes written", bytes_written, bytes_length);
|
|
|
+ send_mtp_response(mtp, 3, MTP_RESP_STORE_FULL, persistence.transaction_id, NULL);
|
|
|
+ storage_file_close(file);
|
|
|
+ storage_file_free(file);
|
|
|
+ persistence.current_file = NULL;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ persistence.buffer_offset += bytes_written;
|
|
|
+ persistence.left_bytes -= bytes_written;
|
|
|
|
|
|
- persistence.buffer_offset += bytes_length;
|
|
|
- persistence.left_bytes -= bytes_length;
|
|
|
+ // Sync periodically based on configured interval
|
|
|
+ if(persistence.buffer_offset % MTP_FILE_SYNC_INTERVAL == 0) {
|
|
|
+ storage_file_sync(file);
|
|
|
+ FURI_LOG_I("MTP", "Progress: %ld bytes remaining", persistence.left_bytes);
|
|
|
+ }
|
|
|
|
|
|
if(persistence.left_bytes <= 0) {
|
|
|
+ storage_file_sync(file);
|
|
|
send_mtp_response(mtp, 3, MTP_RESP_OK, persistence.transaction_id, NULL);
|
|
|
storage_file_close(file);
|
|
|
storage_file_free(file);
|
|
|
-
|
|
|
persistence.current_file = NULL;
|
|
|
+ FURI_LOG_I("MTP", "File transfer completed successfully");
|
|
|
}
|
|
|
} else if(persistence.op == MTP_OP_SET_OBJECT_PROP_VALUE) {
|
|
|
uint32_t handle = persistence.params[0];
|
|
|
@@ -195,7 +161,7 @@ void handle_mtp_data_packet(AppMTP* mtp, uint8_t* buffer, int32_t length, bool c
|
|
|
print_bytes("MTP FileName", ptr, length - sizeof(struct MTPHeader));
|
|
|
|
|
|
char* name = ReadMTPString(ptr);
|
|
|
- char* full_path = malloc(sizeof(char) * 256);
|
|
|
+ char* full_path = malloc(sizeof(char) * MTP_PATH_SIZE);
|
|
|
|
|
|
merge_path(full_path, get_base_path_from_storage_id(persistence.params[2]), name);
|
|
|
|
|
|
@@ -297,7 +263,7 @@ void handle_mtp_data_complete(AppMTP* mtp) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- char* full_path = malloc(sizeof(char) * 256);
|
|
|
+ char* full_path = malloc(sizeof(char) * MTP_PATH_SIZE);
|
|
|
merge_path(full_path, base_path, name);
|
|
|
|
|
|
FURI_LOG_I("MTP", "Format: %04x", info->format);
|
|
|
@@ -380,7 +346,7 @@ void handle_mtp_command(AppMTP* mtp, struct MTPContainer* container) {
|
|
|
break;
|
|
|
case MTP_OP_GET_STORAGE_INFO: {
|
|
|
FURI_LOG_I("MTP", "GetStorageInfo operation");
|
|
|
- uint8_t* info = malloc(sizeof(uint8_t) * 256);
|
|
|
+ uint8_t* info = malloc(sizeof(uint8_t) * MTP_BUFFER_SIZE);
|
|
|
int length = GetStorageInfo(mtp, container->params[0], info);
|
|
|
send_mtp_response_buffer(
|
|
|
mtp,
|
|
|
@@ -406,7 +372,7 @@ void handle_mtp_command(AppMTP* mtp, struct MTPContainer* container) {
|
|
|
0);
|
|
|
break;
|
|
|
} else {
|
|
|
- uint8_t* buffer = malloc(sizeof(uint8_t) * 256);
|
|
|
+ uint8_t* buffer = malloc(sizeof(uint8_t) * MTP_BUFFER_SIZE);
|
|
|
int length = GetObjectHandles(mtp, container->params[0], container->params[2], buffer);
|
|
|
send_mtp_response_buffer(
|
|
|
mtp,
|
|
|
@@ -423,7 +389,7 @@ void handle_mtp_command(AppMTP* mtp, struct MTPContainer* container) {
|
|
|
break;
|
|
|
case MTP_OP_GET_OBJECT_INFO: {
|
|
|
FURI_LOG_I("MTP", "GetObjectInfo operation");
|
|
|
- uint8_t* buffer = malloc(sizeof(uint8_t) * 512);
|
|
|
+ uint8_t* buffer = malloc(sizeof(uint8_t) * MTP_BUFFER_SIZE);
|
|
|
int length = GetObjectInfo(mtp, container->params[0], buffer);
|
|
|
|
|
|
if(length < 0) {
|
|
|
@@ -451,7 +417,7 @@ void handle_mtp_command(AppMTP* mtp, struct MTPContainer* container) {
|
|
|
}
|
|
|
case MTP_OP_GET_OBJECT_PROPS_SUPPORTED: {
|
|
|
FURI_LOG_I("MTP", "GetObjectPropsSupported operation");
|
|
|
- uint8_t* buffer = malloc(sizeof(uint8_t) * 256);
|
|
|
+ uint8_t* buffer = malloc(sizeof(uint8_t) * MTP_BUFFER_SIZE);
|
|
|
uint32_t count = sizeof(supported_object_props) / sizeof(uint16_t);
|
|
|
memcpy(buffer, &count, sizeof(uint32_t));
|
|
|
memcpy(buffer + sizeof(uint32_t), supported_object_props, sizeof(uint16_t) * count);
|
|
|
@@ -481,12 +447,12 @@ void handle_mtp_command(AppMTP* mtp, struct MTPContainer* container) {
|
|
|
|
|
|
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]);
|
|
|
+ GetDevicePropValue(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]);
|
|
|
+ GetDevicePropDesc(mtp, container->header.transaction_id, container->params[0]);
|
|
|
// Process the GetDevicePropDesc operation
|
|
|
break;
|
|
|
// Handle bulk transfer specific operations
|
|
|
@@ -562,7 +528,7 @@ void send_storage_ids(AppMTP* mtp, uint32_t transaction_id) {
|
|
|
}
|
|
|
|
|
|
void send_device_info(AppMTP* mtp, uint32_t transaction_id) {
|
|
|
- uint8_t* response = malloc(sizeof(uint8_t) * 256);
|
|
|
+ uint8_t* response = malloc(sizeof(uint8_t) * MTP_BUFFER_SIZE);
|
|
|
int length = BuildDeviceInfo(response);
|
|
|
send_mtp_response_buffer(
|
|
|
mtp, MTP_TYPE_DATA, MTP_OP_GET_DEVICE_INFO, transaction_id, response, length);
|
|
|
@@ -570,446 +536,9 @@ void send_device_info(AppMTP* mtp, uint32_t transaction_id) {
|
|
|
free(response);
|
|
|
}
|
|
|
|
|
|
-void send_device_prop_value(AppMTP* mtp, uint32_t transaction_id, uint32_t prop_code) {
|
|
|
- uint8_t* response = malloc(sizeof(uint8_t) * 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);
|
|
|
- free(response);
|
|
|
-}
|
|
|
-
|
|
|
-void send_device_prop_desc(AppMTP* mtp, uint32_t transaction_id, uint32_t prop_code) {
|
|
|
- uint8_t* response = malloc(sizeof(uint8_t) * 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);
|
|
|
- free(response);
|
|
|
-}
|
|
|
-
|
|
|
-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 = 1;
|
|
|
- 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;
|
|
|
-}
|
|
|
-
|
|
|
-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;
|
|
|
- } else if(storage_id == EXTERNAL_STORAGE_ID) {
|
|
|
- return STORAGE_EXT_PATH_PREFIX;
|
|
|
- }
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-int list_and_issue_handles(
|
|
|
- AppMTP* mtp,
|
|
|
- uint32_t storage_id,
|
|
|
- uint32_t association,
|
|
|
- uint32_t* handles) {
|
|
|
- Storage* storage = mtp->storage;
|
|
|
- char* base_path = get_base_path_from_storage_id(storage_id);
|
|
|
- if(base_path == NULL) {
|
|
|
- base_path = "";
|
|
|
- }
|
|
|
-
|
|
|
- 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);
|
|
|
- FURI_LOG_I("MTP", "Association path: %s", path);
|
|
|
- if(path == NULL) {
|
|
|
- return 0;
|
|
|
- }
|
|
|
- storage_dir_open(dir, path);
|
|
|
- base_path = 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);
|
|
|
- }
|
|
|
-
|
|
|
- merge_path(full_path, base_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_dir_close(dir);
|
|
|
- storage_file_free(dir);
|
|
|
- free(file_name);
|
|
|
- free(full_path);
|
|
|
- return count;
|
|
|
-}
|
|
|
-
|
|
|
-struct GetObjectContext {
|
|
|
- File* file;
|
|
|
-};
|
|
|
-
|
|
|
-int GetObject_callback(void* ctx, uint8_t* buffer, int length) {
|
|
|
- struct GetObjectContext* obj_ctx = (struct GetObjectContext*)ctx;
|
|
|
- return storage_file_read(obj_ctx->file, buffer, length);
|
|
|
-}
|
|
|
-
|
|
|
-void GetObject(AppMTP* mtp, uint32_t transaction_id, uint32_t handle) {
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- FURI_LOG_I("MTP", "Getting object: %s", path);
|
|
|
-
|
|
|
- Storage* storage = mtp->storage;
|
|
|
- File* file = storage_file_alloc(storage);
|
|
|
- if(!storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
|
|
- FURI_LOG_E("MTP", "Failed to open file: %s", path);
|
|
|
- send_mtp_response(
|
|
|
- mtp, MTP_TYPE_RESPONSE, MTP_RESP_INVALID_OBJECT_HANDLE, transaction_id, NULL);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- uint32_t size = storage_file_size(file);
|
|
|
-
|
|
|
- struct GetObjectContext ctx = {
|
|
|
- .file = file,
|
|
|
- };
|
|
|
-
|
|
|
- send_mtp_response_stream(
|
|
|
- mtp, MTP_TYPE_DATA, MTP_OP_GET_OBJECT, transaction_id, &ctx, GetObject_callback, size);
|
|
|
-
|
|
|
- send_mtp_response(mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, transaction_id, NULL);
|
|
|
-
|
|
|
- storage_file_close(file);
|
|
|
- storage_file_free(file);
|
|
|
-}
|
|
|
-
|
|
|
-int GetObjectInfo(AppMTP* mtp, uint32_t handle, uint8_t* buffer) {
|
|
|
- ObjectInfoHeader* header = (ObjectInfoHeader*)buffer;
|
|
|
- uint8_t* ptr = buffer + sizeof(ObjectInfoHeader);
|
|
|
-
|
|
|
- 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;
|
|
|
- FS_Error err = storage_common_stat(storage, path, &fileinfo);
|
|
|
- if(err != FSE_OK) {
|
|
|
- FURI_LOG_E("MTP", "Failed to get file info: %s", filesystem_api_error_get_desc(err));
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- if(memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
|
|
|
- FURI_LOG_I("MTP", "Object in Internal storage");
|
|
|
- header->storage_id = INTERNAL_STORAGE_ID;
|
|
|
- } else if(memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
|
|
|
- FURI_LOG_I("MTP", "Object in External storage");
|
|
|
- header->storage_id = EXTERNAL_STORAGE_ID;
|
|
|
- } else {
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- if(file_info_is_dir(&fileinfo)) {
|
|
|
- FURI_LOG_I("MTP", "Directory");
|
|
|
-
|
|
|
- header->format = MTP_FORMAT_ASSOCIATION;
|
|
|
- header->association_type = 0x0001; // Generic folder
|
|
|
- } else {
|
|
|
- FURI_LOG_I("MTP", "Undefined type");
|
|
|
-
|
|
|
- header->format = MTP_FORMAT_UNDEFINED;
|
|
|
- }
|
|
|
-
|
|
|
- header->compressed_size = fileinfo.size;
|
|
|
-
|
|
|
- header->thumb_format = 0;
|
|
|
- header->thumb_compressed_size = 0;
|
|
|
- header->thumb_pix_width = 0;
|
|
|
- header->thumb_pix_height = 0;
|
|
|
- header->image_pix_width = 0;
|
|
|
- header->image_pix_height = 0;
|
|
|
- header->image_bit_depth = 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);
|
|
|
- }
|
|
|
- */
|
|
|
-
|
|
|
- char* file_name = strrchr(path, '/');
|
|
|
- FURI_LOG_I("MTP", "File name: %s", file_name + 1);
|
|
|
-
|
|
|
- WriteMTPString(ptr, file_name + 1, &length);
|
|
|
- ptr += length;
|
|
|
-
|
|
|
- // get created
|
|
|
- WriteMTPString(ptr, "20240608T010702", &length);
|
|
|
- ptr += length;
|
|
|
-
|
|
|
- // get last modified
|
|
|
- WriteMTPString(ptr, "20240608T010702", &length);
|
|
|
- ptr += length;
|
|
|
-
|
|
|
- // get keywords
|
|
|
- WriteMTPString(ptr, "", &length);
|
|
|
- ptr += length;
|
|
|
-
|
|
|
- storage_file_free(file);
|
|
|
-
|
|
|
- return ptr - buffer;
|
|
|
-}
|
|
|
-
|
|
|
-bool CheckMTPStringHasUnicode(uint8_t* buffer) {
|
|
|
- uint8_t* base = buffer;
|
|
|
- int len = *base;
|
|
|
-
|
|
|
- uint16_t* ptr = (uint16_t*)(base + 1);
|
|
|
-
|
|
|
- for(int i = 0; i < len; i++) {
|
|
|
- if(ptr[i] > 0x7F) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-char* ReadMTPString(uint8_t* buffer) {
|
|
|
- int len16 = *(uint8_t*)buffer;
|
|
|
- if(len16 == 0) {
|
|
|
- return "";
|
|
|
- }
|
|
|
-
|
|
|
- char* str = malloc(sizeof(char) * len16);
|
|
|
-
|
|
|
- uint8_t* base = buffer + 1;
|
|
|
- uint16_t* ptr = (uint16_t*)base;
|
|
|
-
|
|
|
- for(int i = 0; i < len16; i++) {
|
|
|
- str[i] = *ptr++;
|
|
|
- }
|
|
|
-
|
|
|
- return str;
|
|
|
-}
|
|
|
-
|
|
|
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: {
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- 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
|
|
|
- *(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;
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- return ptr - buffer;
|
|
|
-}
|
|
|
-
|
|
|
void send_mtp_response_stream(
|
|
|
AppMTP* mtp,
|
|
|
uint16_t resp_type,
|
|
|
@@ -1155,415 +684,3 @@ 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 += sizeof(uint16_t);
|
|
|
-
|
|
|
- // Vendor extension ID
|
|
|
- *(uint32_t*)ptr = MTP_VENDOR_EXTENSION_ID;
|
|
|
- ptr += sizeof(uint32_t);
|
|
|
-
|
|
|
- // Vendor extension version
|
|
|
- *(uint16_t*)ptr = MTP_VENDOR_EXTENSION_VERSION;
|
|
|
- ptr += sizeof(uint16_t);
|
|
|
-
|
|
|
- // Vendor extension description
|
|
|
- WriteMTPString(ptr, "microsoft.com: 1.0;", &length);
|
|
|
- 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);
|
|
|
- }
|
|
|
-
|
|
|
- // Supported capture formats (example, add as needed)
|
|
|
- *(uint32_t*)ptr = 0; // Number of supported capture formats
|
|
|
- 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);
|
|
|
-
|
|
|
- for(int i = 0; i < length; i++) {
|
|
|
- *(uint16_t*)ptr = supported_playback_formats[i];
|
|
|
- ptr += sizeof(uint16_t);
|
|
|
- }
|
|
|
-
|
|
|
- // Manufacturer
|
|
|
- WriteMTPString(ptr, USB_MANUFACTURER_STRING, &length);
|
|
|
- ptr += length;
|
|
|
-
|
|
|
- // Model
|
|
|
- WriteMTPString(ptr, USB_DEVICE_MODEL, &length);
|
|
|
- ptr += length;
|
|
|
-
|
|
|
- // Device version
|
|
|
- const Version* ver = furi_hal_version_get_firmware_version();
|
|
|
- WriteMTPString(ptr, version_get_version(ver), &length);
|
|
|
- ptr += length;
|
|
|
-
|
|
|
- // Serial number
|
|
|
- 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;
|
|
|
-}
|
|
|
-
|
|
|
-void GetStorageIDs(AppMTP* mtp, uint32_t* storage_ids, uint32_t* count) {
|
|
|
- SDInfo sd_info;
|
|
|
- FS_Error err = storage_sd_info(mtp->storage, &sd_info);
|
|
|
-
|
|
|
- // Due to filesystem change, we only have external storage.
|
|
|
- // storage_ids[0] = INTERNAL_STORAGE_ID;
|
|
|
- // // Check if SD card is present
|
|
|
- // if(err != FSE_OK) {
|
|
|
- // FURI_LOG_E("MTP", "SD Card not found");
|
|
|
- // *count = 1; // We have only one storage
|
|
|
- // return;
|
|
|
- // }
|
|
|
-
|
|
|
- // Check if SD card is present
|
|
|
- if(err != FSE_OK) {
|
|
|
- FURI_LOG_E("MTP", "SD Card not found");
|
|
|
- *count = 0; // No storage.
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- storage_ids[0] = EXTERNAL_STORAGE_ID;
|
|
|
- *count = 1;
|
|
|
-}
|
|
|
-
|
|
|
-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
|
|
|
- FURI_LOG_I("MTP", "Getting storage info for storage ID %04lx", storage_id);
|
|
|
-
|
|
|
- if(storage_id == INTERNAL_STORAGE_ID) {
|
|
|
- // Fill in details for internal storage
|
|
|
- info->storage_type = 0x0003; // Fixed RAM
|
|
|
-
|
|
|
- // Fill in details for internal storage
|
|
|
- uint64_t total_space;
|
|
|
- uint64_t 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;
|
|
|
- }
|
|
|
-
|
|
|
- WriteMTPString(ptr, "Internal Storage", &length);
|
|
|
- ptr += length;
|
|
|
-
|
|
|
- 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
|
|
|
-
|
|
|
- if(err != FSE_OK) {
|
|
|
- info->max_capacity = 0;
|
|
|
- info->free_space_in_bytes = 0;
|
|
|
-
|
|
|
- FURI_LOG_E("MTP", "SD Card not found");
|
|
|
- } 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;
|
|
|
- }
|
|
|
-
|
|
|
- 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) {
|
|
|
- uint8_t* ptr = buffer;
|
|
|
- uint8_t str_len = strlen(str);
|
|
|
-
|
|
|
- FURI_LOG_I("MTP", "Writing MTP string: %s", str);
|
|
|
- FURI_LOG_I("MTP", "String length: %d", str_len);
|
|
|
-
|
|
|
- // extra handling for empty string
|
|
|
- if(str_len == 0) {
|
|
|
- *ptr = 0x00;
|
|
|
-
|
|
|
- // that's it!
|
|
|
- *length = 1;
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- *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;
|
|
|
-
|
|
|
- FURI_LOG_I("MTP", "String byte length: %d", ptr - buffer);
|
|
|
- *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; // Null terminator (UTF-16LE)
|
|
|
- *ptr++ = 0x00;
|
|
|
- *length = ptr - buffer;
|
|
|
-}
|
|
|
-
|
|
|
-bool DeleteObject(AppMTP* mtp, uint32_t handle) {
|
|
|
- UNUSED(mtp);
|
|
|
- FURI_LOG_I("MTP", "Deleting object %ld", handle);
|
|
|
-
|
|
|
- char* path = get_path_from_handle(mtp, handle);
|
|
|
- if(path == NULL) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- FileInfo fileinfo;
|
|
|
- FS_Error err = storage_common_stat(mtp->storage, path, &fileinfo);
|
|
|
-
|
|
|
- if(err == FSE_OK) {
|
|
|
- if(file_info_is_dir(&fileinfo)) {
|
|
|
- if(!storage_simply_remove_recursive(mtp->storage, path)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- } else {
|
|
|
- if(storage_common_remove(mtp->storage, path) != FSE_OK) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- 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);
|
|
|
-}
|