refactor mtp examples, add phase and session id to callback data

This commit is contained in:
hathach
2025-09-29 15:02:51 +07:00
parent a09c65c4e4
commit 4f3a5f92fa
3 changed files with 358 additions and 292 deletions

View File

@@ -130,14 +130,50 @@ static fs_file_t fs_objects[FS_MAX_FILE_COUNT] = {
}
};
enum {
SUPPORTED_STORAGE_ID = 0x00010001u // physical = 1, logical = 1
};
static int32_t fs_get_device_info(tud_mtp_cb_data_t* cb_data);
static int32_t fs_open_close_session(tud_mtp_cb_data_t* cb_data);
static int32_t fs_get_storage_ids(tud_mtp_cb_data_t* cb_data);
static int32_t fs_get_storage_info(tud_mtp_cb_data_t* cb_data);
static int32_t fs_get_device_properties(tud_mtp_cb_data_t* cb_data);
static int32_t fs_get_object_handles(tud_mtp_cb_data_t* cb_data);
static int32_t fs_get_object_info(tud_mtp_cb_data_t* cb_data);
static int32_t fs_get_object(tud_mtp_cb_data_t* cb_data);
static int32_t fs_delete_object(tud_mtp_cb_data_t* cb_data);
static int32_t fs_send_object_info(tud_mtp_cb_data_t* cb_data);
static int32_t fs_send_object(tud_mtp_cb_data_t* cb_data);
typedef int32_t (*fs_op_handler_t)(tud_mtp_cb_data_t* cb_data);
typedef struct {
uint32_t op_code;
fs_op_handler_t handler;
}fs_op_handler_dict_t;
fs_op_handler_dict_t fs_op_handler_dict[] = {
{ MTP_OP_GET_DEVICE_INFO, fs_get_device_info },
{ MTP_OP_OPEN_SESSION, fs_open_close_session },
{ MTP_OP_CLOSE_SESSION, fs_open_close_session },
{ MTP_OP_GET_STORAGE_IDS, fs_get_storage_ids },
{ MTP_OP_GET_STORAGE_INFO, fs_get_storage_info },
{ MTP_OP_GET_DEVICE_PROP_DESC, fs_get_device_properties },
{ MTP_OP_GET_DEVICE_PROP_VALUE, fs_get_device_properties },
{ MTP_OP_GET_OBJECT_HANDLES, fs_get_object_handles },
{ MTP_OP_GET_OBJECT_INFO, fs_get_object_info },
{ MTP_OP_GET_OBJECT, fs_get_object },
{ MTP_OP_DELETE_OBJECT, fs_delete_object },
{ MTP_OP_SEND_OBJECT_INFO, fs_send_object_info },
{ MTP_OP_SEND_OBJECT, fs_send_object },
};
static bool is_session_opened = false;
static uint32_t send_obj_handle = 0;
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
// Get pointer to object info from handle
static inline fs_file_t* fs_get_file(uint32_t handle) {
if (handle == 0 || handle > FS_MAX_FILE_COUNT) {
@@ -227,314 +263,54 @@ int32_t tud_mtp_request_get_device_status_cb(tud_mtp_request_cb_data_t* cb_data)
int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) {
const mtp_container_command_t* command = cb_data->command_container;
mtp_container_info_t* io_container = &cb_data->io_container;
uint16_t resp_code = 0;
switch (command->header.code) {
case MTP_OP_GET_DEVICE_INFO: {
// Device info is already prepared up to playback formats. Application only need to add string fields
mtp_container_add_cstring(io_container, DEV_INFO_MANUFACTURER);
mtp_container_add_cstring(io_container, DEV_INFO_MODEL);
mtp_container_add_cstring(io_container, DEV_INFO_VERSION);
uint16_t serial_utf16[32];
board_usb_get_serial(serial_utf16, 32);
serial_utf16[31] = 0; // ensure null termination
mtp_container_add_string(io_container, serial_utf16);
tud_mtp_data_send(io_container);
fs_op_handler_t handler = NULL;
for (size_t i = 0; i < TU_ARRAY_SIZE(fs_op_handler_dict); i++) {
if (fs_op_handler_dict[i].op_code == command->header.code) {
handler = fs_op_handler_dict[i].handler;
break;
}
case MTP_OP_OPEN_SESSION:
if (is_session_opened) {
resp_code = MTP_RESP_SESSION_ALREADY_OPEN;
}else {
resp_code = MTP_RESP_OK;
}
is_session_opened = true;
break;
case MTP_OP_CLOSE_SESSION:
if (!is_session_opened) {
resp_code = MTP_RESP_SESSION_NOT_OPEN;
} else {
resp_code = MTP_RESP_OK;
}
is_session_opened = false;
break;
case MTP_OP_GET_STORAGE_IDS: {
uint32_t storage_ids [] = { SUPPORTED_STORAGE_ID };
mtp_container_add_auint32(io_container, 1, storage_ids);
tud_mtp_data_send(io_container);
break;
}
case MTP_OP_GET_STORAGE_INFO: {
const uint32_t storage_id = command->params[0];
TU_VERIFY(SUPPORTED_STORAGE_ID == storage_id, -1);
// update storage info with current free space
storage_info.max_capacity_in_bytes = sizeof(README_TXT_CONTENT) + logo_len + FS_MAX_CAPACITY_BYTES;
storage_info.free_space_in_objects = FS_MAX_FILE_COUNT - fs_get_file_count();
storage_info.free_space_in_bytes = FS_MAX_CAPACITY_BYTES-fs_buf_head;
mtp_container_add_raw(io_container, &storage_info, sizeof(storage_info));
tud_mtp_data_send(io_container);
break;
}
case MTP_OP_GET_DEVICE_PROP_DESC: {
const uint16_t dev_prop_code = (uint16_t) command->params[0];
mtp_device_prop_desc_header_t device_prop_header;
device_prop_header.device_property_code = dev_prop_code;
switch (dev_prop_code) {
case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME:
device_prop_header.datatype = MTP_DATA_TYPE_STR;
device_prop_header.get_set = MTP_MODE_GET;
mtp_container_add_raw(io_container, &device_prop_header, sizeof(device_prop_header));
mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // factory
mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // current
mtp_container_add_uint8(io_container, 0); // no form
tud_mtp_data_send(io_container);
break;
default:
resp_code = MTP_RESP_PARAMETER_NOT_SUPPORTED;
break;
}
break;
}
case MTP_OP_GET_DEVICE_PROP_VALUE: {
const uint16_t dev_prop_code = (uint16_t) command->params[0];
switch (dev_prop_code) {
case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME:
mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME);
tud_mtp_data_send(io_container);
break;
default:
resp_code = MTP_RESP_PARAMETER_NOT_SUPPORTED;
break;
}
break;
}
case MTP_OP_GET_OBJECT_HANDLES: {
const uint32_t storage_id = command->params[0];
const uint32_t obj_format = command->params[1]; // optional
(void) obj_format;
const uint32_t parent_handle = command->params[2]; // folder handle, 0xFFFFFFFF is root
if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) {
resp_code = MTP_RESP_INVALID_STORAGE_ID;
} else {
uint32_t handles[FS_MAX_FILE_COUNT] = { 0 };
uint32_t count = 0;
for (uint8_t i = 0; i < FS_MAX_FILE_COUNT; i++) {
fs_file_t* f = &fs_objects[i];
if (fs_file_exist(f) &&
(parent_handle == f->parent || (parent_handle == 0xFFFFFFFF && f->parent == 0))) {
handles[count++] = i + 1; // handle is index + 1
}
}
mtp_container_add_auint32(io_container, count, handles);
tud_mtp_data_send(io_container);
}
break;
}
case MTP_OP_GET_OBJECT_INFO: {
const uint32_t obj_handle = command->params[0];
fs_file_t* f = fs_get_file(obj_handle);
if (f == NULL) {
resp_code = MTP_RESP_INVALID_OBJECT_HANDLE;
} else {
mtp_object_info_header_t obj_info_header = {
.storage_id = SUPPORTED_STORAGE_ID,
.object_format = f->object_format,
.protection_status = f->protection_status,
.object_compressed_size = f->size,
.thumb_format = MTP_OBJ_FORMAT_UNDEFINED,
.thumb_compressed_size = 0,
.thumb_pix_width = 0,
.thumb_pix_height = 0,
.image_pix_width = f->image_pix_width,
.image_pix_height = f->image_pix_height,
.image_bit_depth = f->image_bit_depth,
.parent_object = f->parent,
.association_type = f->association_type,
.association_desc = 0,
.sequence_number = 0
};
mtp_container_add_raw(io_container, &obj_info_header, sizeof(obj_info_header));
mtp_container_add_string(io_container, f->name);
mtp_container_add_cstring(io_container, FS_FIXED_DATETIME);
mtp_container_add_cstring(io_container, FS_FIXED_DATETIME);
mtp_container_add_cstring(io_container, ""); // keywords, not used
tud_mtp_data_send(io_container);
}
break;
}
case MTP_OP_GET_OBJECT: {
const uint32_t obj_handle = command->params[0];
fs_file_t* f = fs_get_file(obj_handle);
if (f == NULL) {
resp_code = MTP_RESP_INVALID_OBJECT_HANDLE;
} else {
// If file contents is larger than CFG_TUD_MTP_EP_BUFSIZE, only partial data is added here
// the rest will be sent in tud_mtp_data_more_cb
mtp_container_add_raw(io_container, f->data, f->size);
tud_mtp_data_send(io_container);
}
break;
}
case MTP_OP_SEND_OBJECT_INFO: {
const uint32_t storage_id = command->params[0];
const uint32_t parent_handle = command->params[1]; // folder handle, 0xFFFFFFFF is root
(void) parent_handle;
if (!is_session_opened) {
resp_code = MTP_RESP_SESSION_NOT_OPEN;
} else if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) {
resp_code = MTP_RESP_INVALID_STORAGE_ID;
} else {
tud_mtp_data_receive(io_container);
}
break;
}
case MTP_OP_SEND_OBJECT: {
fs_file_t* f = fs_get_file(send_obj_handle);
if (f == NULL) {
resp_code = MTP_RESP_INVALID_OBJECT_HANDLE;
} else {
io_container->header->len += f->size;
tud_mtp_data_receive(io_container);
}
break;
}
case MTP_OP_DELETE_OBJECT: {
if (!is_session_opened) {
resp_code = MTP_RESP_SESSION_NOT_OPEN;
} else {
const uint32_t obj_handle = command->params[0];
const uint32_t obj_format = command->params[1]; // optional
(void) obj_format;
fs_file_t* f = fs_get_file(obj_handle);
if (f == NULL) {
resp_code = MTP_RESP_INVALID_OBJECT_HANDLE;
break;
}
// delete object by clear the name
f->name[0] = 0;
resp_code = MTP_RESP_OK;
}
break;
}
default:
resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED;
break;
}
// send response if needed
if (resp_code != 0) {
io_container->header->code = resp_code;
tud_mtp_response_send(io_container);
int32_t resp_code;
if (handler == NULL) {
resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED;
} else {
resp_code = handler(cb_data);
if (resp_code > MTP_RESP_UNDEFINED) {
// send response if needed
io_container->header->code = (uint16_t)resp_code;
tud_mtp_response_send(io_container);
}
}
return 0;
return resp_code;
}
int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) {
const mtp_container_command_t* command = cb_data->command_container;
mtp_container_info_t* io_container = &cb_data->io_container;
uint16_t resp_code = 0;
switch (command->header.code) {
case MTP_OP_GET_OBJECT: {
// File contents span over multiple xfers
const uint32_t obj_handle = command->params[0];
fs_file_t* f = fs_get_file(obj_handle);
if (f == NULL) {
resp_code = MTP_RESP_INVALID_OBJECT_HANDLE;
} else {
// file contents offset is xferred byte minus header size
const uint32_t offset = cb_data->total_xferred_bytes - sizeof(mtp_container_header_t);
const uint32_t xact_len = tu_min32(f->size - offset, io_container->payload_bytes);
memcpy(io_container->payload, f->data + offset, xact_len);
tud_mtp_data_send(io_container);
}
fs_op_handler_t handler = NULL;
for (size_t i = 0; i < TU_ARRAY_SIZE(fs_op_handler_dict); i++) {
if (fs_op_handler_dict[i].op_code == command->header.code) {
handler = fs_op_handler_dict[i].handler;
break;
}
case MTP_OP_SEND_OBJECT_INFO: {
mtp_object_info_header_t* obj_info = (mtp_object_info_header_t*) io_container->payload;
if (obj_info->storage_id != 0 && obj_info->storage_id != SUPPORTED_STORAGE_ID) {
resp_code = MTP_RESP_INVALID_STORAGE_ID;
break;
}
if (obj_info->parent_object) {
fs_file_t* parent = fs_get_file(obj_info->parent_object);
if (parent == NULL || !parent->association_type) {
resp_code = MTP_RESP_INVALID_PARENT_OBJECT;
break;
}
}
uint8_t* f_buf = fs_malloc(obj_info->object_compressed_size);
if (f_buf == NULL) {
resp_code = MTP_RESP_STORE_FULL;
break;
}
fs_file_t* f = fs_create_file();
if (f == NULL) {
resp_code = MTP_RESP_STORE_FULL;
break;
}
f->object_format = obj_info->object_format;
f->protection_status = obj_info->protection_status;
f->image_pix_width = obj_info->image_pix_width;
f->image_pix_height = obj_info->image_pix_height;
f->image_bit_depth = obj_info->image_bit_depth;
f->parent = obj_info->parent_object;
f->association_type = obj_info->association_type;
f->size = obj_info->object_compressed_size;
f->data = f_buf;
uint8_t* buf = io_container->payload + sizeof(mtp_object_info_header_t);
mtp_container_get_string(buf, f->name);
// ignore date created/modified/keywords
break;
}
case MTP_OP_SEND_OBJECT: {
fs_file_t* f = fs_get_file(send_obj_handle);
if (f == NULL) {
resp_code = MTP_RESP_INVALID_OBJECT_HANDLE;
} else {
// file contents offset is total xferred minus header size minus last received chunk
const uint32_t offset = cb_data->total_xferred_bytes - sizeof(mtp_container_header_t) - io_container->payload_bytes;
memcpy(f->data + offset, io_container->payload, io_container->payload_bytes);
tud_mtp_data_receive(io_container); // receive more data if needed
}
break;
}
default:
resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED;
break;
}
// send response if needed
if (resp_code != 0) {
io_container->header->code = resp_code;
tud_mtp_response_send(io_container);
int32_t resp_code;
if (handler == NULL) {
resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED;
} else {
resp_code = handler(cb_data);
if (resp_code > MTP_RESP_UNDEFINED) {
// send response if needed
io_container->header->code = (uint16_t)resp_code;
tud_mtp_response_send(io_container);
}
}
return 0; // 0 mean data/response is sent already
return 0;
}
int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) {
@@ -568,3 +344,284 @@ int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) {
(void) cb_data;
return 0; // nothing to do
}
//--------------------------------------------------------------------+
// File System Handlers
//--------------------------------------------------------------------+
static int32_t fs_get_device_info(tud_mtp_cb_data_t* cb_data) {
// Device info is already prepared up to playback formats. Application only need to add string fields
mtp_container_info_t* io_container = &cb_data->io_container;
mtp_container_add_cstring(io_container, DEV_INFO_MANUFACTURER);
mtp_container_add_cstring(io_container, DEV_INFO_MODEL);
mtp_container_add_cstring(io_container, DEV_INFO_VERSION);
uint16_t serial_utf16[32];
board_usb_get_serial(serial_utf16, 32);
serial_utf16[31] = 0; // ensure null termination
mtp_container_add_string(io_container, serial_utf16);
tud_mtp_data_send(io_container);
return 0;
}
static int32_t fs_open_close_session(tud_mtp_cb_data_t* cb_data) {
const mtp_container_command_t* command = cb_data->command_container;
if (command->header.code == MTP_OP_OPEN_SESSION) {
if (is_session_opened) {
return MTP_RESP_SESSION_ALREADY_OPEN;
}
is_session_opened = true;
} else { // close session
if (!is_session_opened) {
return MTP_RESP_SESSION_NOT_OPEN;
}
is_session_opened = false;
}
return MTP_RESP_OK;
}
static int32_t fs_get_storage_ids(tud_mtp_cb_data_t* cb_data) {
mtp_container_info_t* io_container = &cb_data->io_container;
uint32_t storage_ids [] = { SUPPORTED_STORAGE_ID };
mtp_container_add_auint32(io_container, 1, storage_ids);
tud_mtp_data_send(io_container);
return 0;
}
static int32_t fs_get_storage_info(tud_mtp_cb_data_t* cb_data) {
const mtp_container_command_t* command = cb_data->command_container;
mtp_container_info_t* io_container = &cb_data->io_container;
const uint32_t storage_id = command->params[0];
TU_VERIFY(SUPPORTED_STORAGE_ID == storage_id, -1);
// update storage info with current free space
storage_info.max_capacity_in_bytes = sizeof(README_TXT_CONTENT) + logo_len + FS_MAX_CAPACITY_BYTES;
storage_info.free_space_in_objects = FS_MAX_FILE_COUNT - fs_get_file_count();
storage_info.free_space_in_bytes = FS_MAX_CAPACITY_BYTES-fs_buf_head;
mtp_container_add_raw(io_container, &storage_info, sizeof(storage_info));
tud_mtp_data_send(io_container);
return 0;
}
static int32_t fs_get_device_properties(tud_mtp_cb_data_t* cb_data) {
const mtp_container_command_t* command = cb_data->command_container;
mtp_container_info_t* io_container = &cb_data->io_container;
const uint16_t dev_prop_code = (uint16_t) command->params[0];
if (command->header.code == MTP_OP_GET_DEVICE_PROP_DESC) {
// get describing dataset
mtp_device_prop_desc_header_t device_prop_header;
device_prop_header.device_property_code = dev_prop_code;
switch (dev_prop_code) {
case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME:
device_prop_header.datatype = MTP_DATA_TYPE_STR;
device_prop_header.get_set = MTP_MODE_GET;
mtp_container_add_raw(io_container, &device_prop_header, sizeof(device_prop_header));
mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // factory
mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // current
mtp_container_add_uint8(io_container, 0); // no form
tud_mtp_data_send(io_container);
break;
default:
return MTP_RESP_PARAMETER_NOT_SUPPORTED;
}
} else {
// get value
switch (dev_prop_code) {
case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME:
mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME);
tud_mtp_data_send(io_container);
break;
default:
return MTP_RESP_PARAMETER_NOT_SUPPORTED;
}
}
return 0;
}
static int32_t fs_get_object_handles(tud_mtp_cb_data_t* cb_data) {
const mtp_container_command_t* command = cb_data->command_container;
mtp_container_info_t* io_container = &cb_data->io_container;
const uint32_t storage_id = command->params[0];
const uint32_t obj_format = command->params[1]; // optional
const uint32_t parent_handle = command->params[2]; // folder handle, 0xFFFFFFFF is root
(void)obj_format;
if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) {
return MTP_RESP_INVALID_STORAGE_ID;
}
uint32_t handles[FS_MAX_FILE_COUNT] = { 0 };
uint32_t count = 0;
for (uint8_t i = 0; i < FS_MAX_FILE_COUNT; i++) {
fs_file_t* f = &fs_objects[i];
if (fs_file_exist(f) &&
(parent_handle == f->parent || (parent_handle == 0xFFFFFFFF && f->parent == 0))) {
handles[count++] = i + 1; // handle is index + 1
}
}
mtp_container_add_auint32(io_container, count, handles);
tud_mtp_data_send(io_container);
return 0;
}
static int32_t fs_get_object_info(tud_mtp_cb_data_t* cb_data) {
const mtp_container_command_t* command = cb_data->command_container;
mtp_container_info_t* io_container = &cb_data->io_container;
const uint32_t obj_handle = command->params[0];
fs_file_t* f = fs_get_file(obj_handle);
if (f == NULL) {
return MTP_RESP_INVALID_OBJECT_HANDLE;
}
mtp_object_info_header_t obj_info_header = {
.storage_id = SUPPORTED_STORAGE_ID,
.object_format = f->object_format,
.protection_status = f->protection_status,
.object_compressed_size = f->size,
.thumb_format = MTP_OBJ_FORMAT_UNDEFINED,
.thumb_compressed_size = 0,
.thumb_pix_width = 0,
.thumb_pix_height = 0,
.image_pix_width = f->image_pix_width,
.image_pix_height = f->image_pix_height,
.image_bit_depth = f->image_bit_depth,
.parent_object = f->parent,
.association_type = f->association_type,
.association_desc = 0,
.sequence_number = 0
};
mtp_container_add_raw(io_container, &obj_info_header, sizeof(obj_info_header));
mtp_container_add_string(io_container, f->name);
mtp_container_add_cstring(io_container, FS_FIXED_DATETIME);
mtp_container_add_cstring(io_container, FS_FIXED_DATETIME);
mtp_container_add_cstring(io_container, ""); // keywords, not used
tud_mtp_data_send(io_container);
return 0;
}
static int32_t fs_get_object(tud_mtp_cb_data_t* cb_data) {
const mtp_container_command_t* command = cb_data->command_container;
mtp_container_info_t* io_container = &cb_data->io_container;
const uint32_t obj_handle = command->params[0];
const fs_file_t* f = fs_get_file(obj_handle);
if (f == NULL) {
return MTP_RESP_INVALID_OBJECT_HANDLE;
}
if (cb_data->phase == MTP_PHASE_COMMAND) {
// If file contents is larger than CFG_TUD_MTP_EP_BUFSIZE, data may only partially is added here
// the rest will be sent in tud_mtp_data_more_cb
mtp_container_add_raw(io_container, f->data, f->size);
tud_mtp_data_send(io_container);
} else if (cb_data->phase == MTP_PHASE_DATA) {
// continue sending remaining data: file contents offset is xferred byte minus header size
const uint32_t offset = cb_data->total_xferred_bytes - sizeof(mtp_container_header_t);
const uint32_t xact_len = tu_min32(f->size - offset, io_container->payload_bytes);
if (xact_len > 0) {
memcpy(io_container->payload, f->data + offset, xact_len);
tud_mtp_data_send(io_container);
}
}
return 0;
}
static int32_t fs_send_object_info(tud_mtp_cb_data_t* cb_data) {
const mtp_container_command_t* command = cb_data->command_container;
mtp_container_info_t* io_container = &cb_data->io_container;
const uint32_t storage_id = command->params[0];
const uint32_t parent_handle = command->params[1]; // folder handle, 0xFFFFFFFF is root
(void) parent_handle;
if (!is_session_opened) {
return MTP_RESP_SESSION_NOT_OPEN;
}
if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) {
return MTP_RESP_INVALID_STORAGE_ID;
}
if (cb_data->phase == MTP_PHASE_COMMAND) {
tud_mtp_data_receive(io_container);
} else if (cb_data->phase == MTP_PHASE_DATA) {
mtp_object_info_header_t* obj_info = (mtp_object_info_header_t*) io_container->payload;
if (obj_info->storage_id != 0 && obj_info->storage_id != SUPPORTED_STORAGE_ID) {
return MTP_RESP_INVALID_STORAGE_ID;
}
if (obj_info->parent_object) {
fs_file_t* parent = fs_get_file(obj_info->parent_object);
if (parent == NULL || !parent->association_type) {
return MTP_RESP_INVALID_PARENT_OBJECT;
}
}
uint8_t* f_buf = fs_malloc(obj_info->object_compressed_size);
if (f_buf == NULL) {
return MTP_RESP_STORE_FULL;
}
fs_file_t* f = fs_create_file();
if (f == NULL) {
return MTP_RESP_STORE_FULL;
}
f->object_format = obj_info->object_format;
f->protection_status = obj_info->protection_status;
f->image_pix_width = obj_info->image_pix_width;
f->image_pix_height = obj_info->image_pix_height;
f->image_bit_depth = obj_info->image_bit_depth;
f->parent = obj_info->parent_object;
f->association_type = obj_info->association_type;
f->size = obj_info->object_compressed_size;
f->data = f_buf;
uint8_t* buf = io_container->payload + sizeof(mtp_object_info_header_t);
mtp_container_get_string(buf, f->name);
// ignore date created/modified/keywords
}
return 0;
}
static int32_t fs_send_object(tud_mtp_cb_data_t* cb_data) {
mtp_container_info_t* io_container = &cb_data->io_container;
fs_file_t* f = fs_get_file(send_obj_handle);
if (f == NULL) {
return MTP_RESP_INVALID_OBJECT_HANDLE;
}
if (cb_data->phase == MTP_PHASE_COMMAND) {
io_container->header->len += f->size;
tud_mtp_data_receive(io_container);
} else {
// file contents offset is total xferred minus header size minus last received chunk
const uint32_t offset = cb_data->total_xferred_bytes - sizeof(mtp_container_header_t) - io_container->payload_bytes;
memcpy(f->data + offset, io_container->payload, io_container->payload_bytes);
if (cb_data->total_xferred_bytes - sizeof(mtp_container_header_t) < f->size) {
tud_mtp_data_receive(io_container);
}
}
return 0;
}
static int32_t fs_delete_object(tud_mtp_cb_data_t* cb_data) {
const mtp_container_command_t* command = cb_data->command_container;
const uint32_t obj_handle = command->params[0];
const uint32_t obj_format = command->params[1]; // optional
(void) obj_format;
if (!is_session_opened) {
return MTP_RESP_SESSION_NOT_OPEN;
}
fs_file_t* f = fs_get_file(obj_handle);
if (f == NULL) {
return MTP_RESP_INVALID_OBJECT_HANDLE;
}
// delete object by clear the name
f->name[0] = 0;
return MTP_RESP_OK;
}

View File

@@ -307,6 +307,7 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
tud_mtp_request_cb_data_t cb_data = {
.idx = 0,
.stage = stage,
.session_id = p_mtp->session_id,
.request = request,
.buf = p_mtp->control_buf,
.bufsize = tu_le16toh(request->wLength),
@@ -395,6 +396,8 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
tud_mtp_cb_data_t cb_data;
cb_data.idx = 0;
cb_data.phase = p_mtp->phase;
cb_data.session_id = p_mtp->session_id;
cb_data.command_container = &p_mtp->command;
cb_data.io_container = headered_packet;
cb_data.total_xferred_bytes = 0;

View File

@@ -39,6 +39,9 @@
// callback data for Bulk Only Transfer (BOT) protocol
typedef struct {
uint8_t idx; // mtp instance
uint8_t phase; // current phase
uint32_t session_id;
const mtp_container_command_t* command_container;
mtp_container_info_t io_container;
@@ -50,6 +53,8 @@ typedef struct {
typedef struct {
uint8_t idx;
uint8_t stage; // control stage
uint32_t session_id;
const tusb_control_request_t* request;
// buffer for data stage
uint8_t* buf;
@@ -95,6 +100,7 @@ bool tud_mtp_data_receive(mtp_container_info_t* p_container);
// send response
bool tud_mtp_response_send(mtp_container_info_t* p_container);
// send event notification on event endpoint
bool tud_mtp_event_send(mtp_event_t* event);
//--------------------------------------------------------------------+