diff options
| author | hathach <[email protected]> | 2025-09-25 15:30:37 +0700 |
|---|---|---|
| committer | hathach <[email protected]> | 2025-09-25 15:32:13 +0700 |
| commit | f8397717ea3f476c810ae3ce27ec4c866b305506 (patch) | |
| tree | 8d1dd7e4a7a0fe1ad77318843f9695b2fab0f120 | |
| parent | b8126d9c4e9f1fe55988e1789d1bc286a3c0cbb2 (diff) | |
implement MTP_OP_SEND_OBJECT_INFO, refactor fs example
| -rw-r--r-- | examples/device/mtp/src/mtp_fs_example.c | 300 | ||||
| -rw-r--r-- | src/class/mtp/mtp.h | 29 | ||||
| -rw-r--r-- | src/class/mtp/mtp_device.c | 96 | ||||
| -rw-r--r-- | src/class/mtp/mtp_device.h | 20 |
4 files changed, 301 insertions, 144 deletions
diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 7e19fa75c..d6d7f1907 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -51,41 +51,55 @@ typedef MTP_STORAGE_INFO_STRUCT(TU_ARRAY_SIZE((uint16_t[]) STORAGE_DESCRIPTRION) // RAM FILESYSTEM //--------------------------------------------------------------------+ #define FS_MAX_FILE_COUNT 5UL -#define FS_MAX_CAPACITY_BYTES (2 * 1024UL) -#define FS_MAX_FILENAME_LEN 16UL +#define FS_MAX_CAPACITY_BYTES (4 * 1024UL) +#define FS_MAX_FILENAME_LEN 16 #define FS_FIXED_DATETIME "20250808T173500.0" // "YYYYMMDDTHHMMSS.s" #define README_TXT_CONTENT "TinyUSB MTP on RAM Filesystem example" typedef struct { - // uint32_t handle; - char name[FS_MAX_FILENAME_LEN]; - mtp_object_formats_t format; + uint16_t name[FS_MAX_FILENAME_LEN]; + mtp_object_formats_t object_format; + uint16_t protection_status; + uint32_t image_pix_width; + uint32_t image_pix_height; + uint32_t image_bit_depth; + uint32_t parent; - bool association; + uint8_t association_type; + uint32_t size; uint8_t* data; -} fs_object_info_t; + +} fs_file_t; // object data buffer (excluding 2 predefined files) uint8_t fs_buf[FS_MAX_CAPACITY_BYTES]; uint8_t fs_buf_head = 0; // simple allocation pointer // Files system, handle is index + 1 -static fs_object_info_t fs_objects[FS_MAX_FILE_COUNT] = { +static fs_file_t fs_objects[FS_MAX_FILE_COUNT] = { { - .name = "readme.txt", - .format = MTP_OBJ_FORMAT_TEXT, + .name = { 'r', 'e', 'a', 'd', 'm', 'e', '.', 't', 'x', 't', 0 }, // readme.txt + .object_format = MTP_OBJ_FORMAT_TEXT, + .protection_status = MTP_PROTECTION_STATUS_READ_ONLY, + .image_pix_width = 0, + .image_pix_height = 0, + .image_bit_depth = 0, .parent = 0, - .association = false, + .association_type = MTP_ASSOCIATION_UNDEFINED, .data = (uint8_t*) README_TXT_CONTENT, .size = sizeof(README_TXT_CONTENT) }, { - .name = "tinyusb.png", - .format = MTP_OBJ_FORMAT_PNG, + .name = { 't', 'i', 'n', 'y', 'u', 's', 'b', '.', 'p', 'n', 'g', 0 }, // "tinyusb.png" + .object_format = MTP_OBJ_FORMAT_PNG, + .protection_status = MTP_PROTECTION_STATUS_READ_ONLY, + .image_pix_width = 128, + .image_pix_height = 64, + .image_bit_depth = 32, .parent = 0, - .association = false, + .association_type = MTP_ASSOCIATION_UNDEFINED, .data = logo_bin, .size = logo_len, } @@ -114,34 +128,77 @@ enum { }; static bool is_session_opened = false; - -//--------------------------------------------------------------------+ -// INTERNAL FUNCTIONS -//--------------------------------------------------------------------+ +static uint32_t send_obj_handle = 0; // Get pointer to object info from handle -static inline fs_object_info_t* fs_get_object(uint32_t handle) { +static inline fs_file_t* fs_get_file(uint32_t handle) { if (handle == 0 || handle > FS_MAX_FILE_COUNT) { return NULL; } return &fs_objects[handle-1]; } +static inline bool fs_file_exist(fs_file_t* f) { + return f->name[0] != 0; +} + // Get the number of allocated nodes in filesystem -uint32_t fs_get_object_count(void) { +static uint32_t fs_get_file_count(void) { uint32_t count = 0; - for (unsigned int i = 0; i < FS_MAX_FILE_COUNT; i++) { - if (fs_objects[i].name[0] != 0) { + for (size_t i = 0; i < FS_MAX_FILE_COUNT; i++) { + if (fs_file_exist(&fs_objects[i])) { count++; } } return count; } +static inline fs_file_t* fs_create_file(void) { + for (size_t i = 0; i < FS_MAX_FILE_COUNT; i++) { + fs_file_t* f = &fs_objects[i]; + if (!fs_file_exist(f)) { + send_obj_handle = i + 1; + return f; + } + } +} + +// simple malloc +static inline uint8_t* fs_malloc(size_t size) { + if (fs_buf_head + size > FS_MAX_CAPACITY_BYTES) { + return NULL; + } + uint8_t* ptr = &fs_buf[fs_buf_head]; + fs_buf_head += size; + return ptr; +} + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) { - mtp_container_info_t* reply = &cb_data->reply; - reply->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; - tud_mtp_response_send(reply); + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* resp = &cb_data->io_container; + switch (command->code) { + case MTP_OP_SEND_OBJECT_INFO: { + fs_file_t* f = fs_get_file(send_obj_handle); + if (f == NULL) { + resp->header->code = MTP_RESP_GENERAL_ERROR; + break; + } + // parameter is: storage id, parent handle, new handle + mtp_container_add_uint32(resp, SUPPORTED_STORAGE_ID); + mtp_container_add_uint32(resp, f->parent); + mtp_container_add_uint32(resp, send_obj_handle); + resp->header->code = MTP_RESP_OK; + break; + } + + default: + resp->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; + break; + } + tud_mtp_response_send(resp); return 0; } @@ -150,24 +207,59 @@ int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) { return 0; // nothing to do } -int32_t tud_mtp_data_more_cb(tud_mtp_cb_data_t* cb_data) { - // only a few command that need more data e.g GetObject and SendObject - const mtp_container_command_t* command = cb_data->command; - mtp_container_info_t* reply = &cb_data->reply; +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; uint32_t resp_code = 0; switch (command->code) { case MTP_OP_GET_OBJECT: { + // File contents span over multiple xfers const uint32_t obj_handle = command->params[0]; - fs_object_info_t* obj = fs_get_object(obj_handle); - if (obj == NULL) { + 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->xferred_bytes - sizeof(mtp_container_header_t); - const uint32_t xact_len = tu_min32(obj->size - offset, reply->payload_size); - memcpy(reply->payload, obj->data + offset, xact_len); - tud_mtp_data_send(&cb_data->reply); + const uint32_t xact_len = tu_min32(f->size - offset, io_container->payload_size); + memcpy(io_container->payload, f->data + offset, xact_len); + tud_mtp_data_send(&cb_data->io_container); + } + 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; + } + } + + fs_file_t* f = fs_create_file(); + 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 = fs_malloc(f->size); + if (f->data == NULL) { + resp_code = MTP_RESP_STORE_FULL; + break; } + 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; } @@ -178,26 +270,26 @@ int32_t tud_mtp_data_more_cb(tud_mtp_cb_data_t* cb_data) { // send response if needed if (resp_code != 0) { - reply->header->code = resp_code; - tud_mtp_response_send(reply); + io_container->header->code = resp_code; + tud_mtp_response_send(io_container); } return 0; // 0 mean data/response is sent already } int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { - const mtp_container_command_t* command = cb_data->command; - mtp_container_info_t* reply = &cb_data->reply; + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* io_container = &cb_data->io_container; uint32_t resp_code = 0; switch (command->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(&cb_data->reply, DEV_INFO_MANUFACTURER); - mtp_container_add_cstring(&cb_data->reply, DEV_INFO_MODEL); - mtp_container_add_cstring(&cb_data->reply, DEV_INFO_VERSION); - mtp_container_add_cstring(&cb_data->reply, DEV_INFO_SERIAL); + 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); + mtp_container_add_cstring(io_container, DEV_INFO_SERIAL); - tud_mtp_data_send(&cb_data->reply); + tud_mtp_data_send(io_container); break; } @@ -220,9 +312,9 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { break; case MTP_OP_GET_STORAGE_IDS: { - uint32_t storage_ids [] = { SUPPORTED_STORAGE_ID }; // physical = 1, logical = 1 - mtp_container_add_auint32(&cb_data->reply, 1, storage_ids); - tud_mtp_data_send(&cb_data->reply); + uint32_t storage_ids [] = { SUPPORTED_STORAGE_ID }; + mtp_container_add_auint32(io_container, 1, storage_ids); + tud_mtp_data_send(io_container); break; } @@ -230,10 +322,10 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { 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.free_space_in_objects = FS_MAX_FILE_COUNT - fs_get_object_count(); + 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(&cb_data->reply, &storage_info, sizeof(storage_info)); - tud_mtp_data_send(&cb_data->reply); + mtp_container_add_raw(io_container, &storage_info, sizeof(storage_info)); + tud_mtp_data_send(io_container); break; } @@ -245,11 +337,11 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { 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(&cb_data->reply, &device_prop_header, sizeof(device_prop_header)); - mtp_container_add_cstring(&cb_data->reply, DEV_PROP_FRIENDLY_NAME); // factory - mtp_container_add_cstring(&cb_data->reply, DEV_PROP_FRIENDLY_NAME); // current - mtp_container_add_uint8(&cb_data->reply, 0); // no form - tud_mtp_data_send(&cb_data->reply); + 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: @@ -263,8 +355,8 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { 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(&cb_data->reply, DEV_PROP_FRIENDLY_NAME); - tud_mtp_data_send(&cb_data->reply); + mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); + tud_mtp_data_send(io_container); break; default: @@ -285,66 +377,82 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { uint32_t handles[FS_MAX_FILE_COUNT] = { 0 }; uint32_t count = 0; for (uint8_t i = 0; i < FS_MAX_FILE_COUNT; i++) { - fs_object_info_t* obj = &fs_objects[i]; - if (obj->name[0] != 0 && - (parent_handle == obj->parent || (parent_handle == 0xFFFFFFFF && obj->parent == 0))) { - handles[count++] = i + 1; // handle is index + 1 + 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(&cb_data->reply, count, handles); - tud_mtp_data_send(&cb_data->reply); + 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_object_info_t* obj = fs_get_object(obj_handle); - if (obj == NULL) { + 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 = obj->format, + .object_format = f->object_format, .protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION, - .object_compressed_size = obj->size, + .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 = 128, - .image_pix_height = 64, - .image_bit_depth = 32, - .parent_object = obj->parent, - .association_type = MTP_ASSOCIATION_UNDEFINED, + .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(&cb_data->reply, &obj_info_header, sizeof(obj_info_header)); - mtp_container_add_cstring(&cb_data->reply, obj->name); - mtp_container_add_cstring(&cb_data->reply, FS_FIXED_DATETIME); - mtp_container_add_cstring(&cb_data->reply, FS_FIXED_DATETIME); - mtp_container_add_cstring(&cb_data->reply, ""); // keywords, not used + 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(&cb_data->reply); + tud_mtp_data_send(io_container); } break; } case MTP_OP_GET_OBJECT: { const uint32_t obj_handle = command->params[0]; - fs_object_info_t* obj = fs_get_object(obj_handle); + fs_file_t* obj = fs_get_file(obj_handle); if (obj == 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(&cb_data->reply, obj->data, obj->size); - tud_mtp_data_send(&cb_data->reply); + mtp_container_add_raw(io_container, obj->data, obj->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 + 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: + break; + default: resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED; break; @@ -352,8 +460,8 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { // send response if needed if (resp_code != 0) { - reply->header->code = resp_code; - tud_mtp_response_send(reply); + io_container->header->code = resp_code; + tud_mtp_response_send(io_container); } return 0; @@ -382,7 +490,7 @@ mtp_response_t tud_mtp_storage_format(uint32_t storage_id) { mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, uint32_t* new_object_handle, const mtp_object_info_header_t* info) { - fs_object_info_t* obj = NULL; + fs_file_t* obj = NULL; if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); @@ -405,12 +513,12 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p // Ensure we are not creating an orphaned object outside root if (parent_object != 0) { - obj = fs_get_object(parent_object); + obj = fs_get_file(parent_object); if (obj == NULL) { TU_LOG1("Parent %ld does not exist\r\n", parent_object); return MTP_RESP_INVALID_PARENT_OBJECT; } - if (!obj->association) { + if (!obj->association_type) { TU_LOG1("Parent %ld is not an association\r\n", parent_object); return MTP_RESP_INVALID_PARENT_OBJECT; } @@ -434,7 +542,7 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p obj->handle = ++_fs_operation.last_handle; obj->parent = parent_object; obj->size = info->object_compressed_size; - obj->association = info->object_format == MTP_OBJ_FORMAT_ASSOCIATION; + obj->association_type = info->object_format == MTP_OBJ_FORMAT_ASSOCIATION; // Extract variable data uint16_t offset_data = sizeof(mtp_object_info_header_t); @@ -443,7 +551,7 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p mtpd_gct_get_string(&offset_data, obj->modified, FS_ISODATETIME_LEN); TU_LOG1("Create %s %s with handle %ld, parent %ld and size %ld\r\n", - obj->association ? "association" : "object", + obj->association_type ? "association" : "object", obj->name, obj->handle, obj->parent, obj->size); *new_object_handle = obj->handle; // Initialize operation @@ -453,9 +561,9 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p } mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t* buffer, uint32_t size) { - fs_object_info_t* obj; + fs_file_t* obj; - obj = fs_get_object(object_handle); + obj = fs_get_file(object_handle); if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); return MTP_RESP_INVALID_OBJECT_HANDLE; @@ -481,25 +589,25 @@ mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_ } mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_parent_object_handle) { - fs_object_info_t* obj; + fs_file_t* obj; if (new_parent_object_handle == 0xFFFFFFFF) new_parent_object_handle = 0; // Ensure we are not moving to an nonexisting parent if (new_parent_object_handle != 0) { - obj = fs_get_object(new_parent_object_handle); + obj = fs_get_file(new_parent_object_handle); if (obj == NULL) { TU_LOG1("Parent %ld does not exist\r\n", new_parent_object_handle); return MTP_RESP_INVALID_PARENT_OBJECT; } - if (!obj->association) { + if (!obj->association_type) { TU_LOG1("Parent %ld is not an association\r\n", new_parent_object_handle); return MTP_RESP_INVALID_PARENT_OBJECT; } } - obj = fs_get_object(object_handle); + obj = fs_get_file(object_handle); if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); @@ -511,7 +619,7 @@ mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_ } mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) { - fs_object_info_t* obj; + fs_file_t* obj; if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); @@ -522,7 +630,7 @@ mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) { object_handle = 0; if (object_handle != 0) { - obj = fs_get_object(object_handle); + obj = fs_get_file(object_handle); if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); @@ -532,7 +640,7 @@ mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) { TU_LOG1("Delete object with handle %ld\r\n", object_handle); } - if (object_handle == 0 || obj->association) { + if (object_handle == 0 || obj->association_type) { // Delete also children for (unsigned int i = 0; i < FS_MAX_NODES; i++) { obj = &fs_objects[i]; diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index f1b1eaf23..e713d5c23 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -676,16 +676,12 @@ TU_VERIFY_STATIC(sizeof(mtp_container_command_t) == 32, "size is not correct"); // PTP/MTP Generic container typedef struct TU_ATTR_PACKED { - uint32_t len; - uint16_t type; - uint16_t code; - uint32_t transaction_id; + mtp_container_header_t header; // union { uint32_t data[(CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t)) / sizeof(uint32_t)]; // uint8_t data[CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t)]; // }; } mtp_generic_container_t; -TU_VERIFY_STATIC(sizeof(mtp_generic_container_t) == CFG_TUD_MTP_EP_BUFSIZE, "size is not correct"); typedef struct { mtp_container_header_t* header; @@ -801,7 +797,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t* mtp_container_payload_next(mtp_cont // only add_raw does partial copy TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_raw(mtp_container_info_t* p_container, const void* data, uint32_t len) { uint8_t* buf = mtp_container_payload_next(p_container); - const uint32_t added_len = tu_min32(len, sizeof(mtp_generic_container_t) - p_container->header->len); + const uint32_t added_len = tu_min32(len, CFG_TUD_MTP_EP_BUFSIZE - p_container->header->len); if (added_len > 0) { memcpy(buf, data, added_len); } @@ -811,7 +807,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_raw(mtp_container TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_array(mtp_container_info_t* p_container, uint8_t scalar_size, uint32_t count, const void* data) { const uint32_t added_len = 4 + count * scalar_size; - TU_ASSERT(p_container->header->len + added_len < sizeof(mtp_generic_container_t), 0); + TU_ASSERT(p_container->header->len + added_len < CFG_TUD_MTP_EP_BUFSIZE, 0); uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t); tu_unaligned_write32(buf, count); @@ -824,9 +820,13 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_array(mtp_contain return added_len; } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_container_info_t* p_container, uint8_t count, uint16_t* utf16) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_container_info_t* p_container, uint16_t* utf16) { + uint8_t count = 0; + while (utf16[count]) { + count++; + } const uint32_t added_len = 1 + 2 * count; - TU_ASSERT(p_container->header->len + added_len < sizeof(mtp_generic_container_t), 0); + TU_ASSERT(p_container->header->len + added_len < CFG_TUD_MTP_EP_BUFSIZE, 0); uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t); *buf++ = count; @@ -840,7 +840,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_contai TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_cstring(mtp_container_info_t* p_container, const char* str) { const uint8_t len = (uint8_t) (strlen(str) + 1); // include null - TU_ASSERT(p_container->header->len + 1 + 2 * len < sizeof(mtp_generic_container_t), 0); + TU_ASSERT(p_container->header->len + 1 + 2 * len < CFG_TUD_MTP_EP_BUFSIZE, 0); uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t); if (len == 1) { @@ -894,6 +894,15 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint32(mtp_conta return mtp_container_add_array(p_container, sizeof(uint32_t), count, data); } +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_get_string(uint8_t* buf, uint16_t utf16[]) { + uint8_t nchars = *buf++; + memcpy(buf, utf16, nchars * 2); + return 1 + nchars * 2; +} + #ifdef __cplusplus } #endif diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 358114655..db562d0c7 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -65,7 +65,7 @@ typedef struct { uint32_t session_id; mtp_container_command_t command; - mtp_container_header_t reply_header; + mtp_container_header_t io_header; } mtpd_interface_t; typedef struct { @@ -75,7 +75,7 @@ typedef struct { //--------------------------------------------------------------------+ // INTERNAL FUNCTION DECLARATION //--------------------------------------------------------------------+ -static int32_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data); +static void process_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data); static mtp_phase_type_t mtpd_handle_data(void); static mtp_phase_type_t mtpd_handle_cmd_delete_object(void); static mtp_phase_type_t mtpd_handle_cmd_send_object_info(void); @@ -253,28 +253,39 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t return true; } -bool tud_mtp_data_send(mtp_container_info_t* p_container) { +static bool mtpd_data_xfer(mtp_container_info_t* p_container, uint8_t ep_addr) { mtpd_interface_t* p_mtp = &_mtpd_itf; if (p_mtp->phase == MTP_PHASE_COMMAND) { // 1st data block: header + payload p_mtp->phase = MTP_PHASE_DATA; - p_mtp->total_len = p_container->header->len; p_mtp->xferred_len = 0; - p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->header->transaction_id = p_mtp->command.transaction_id; - p_mtp->reply_header = *p_container->header; // save header for subsequent data + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + p_mtp->total_len = p_container->header->len; + p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->header->transaction_id = p_mtp->command.transaction_id; + p_mtp->io_header = *p_container->header; // save header for subsequent data + } else { + p_mtp->total_len = CFG_TUD_MTP_EP_BUFSIZE; + } } else { // subsequent data block: payload only TU_ASSERT(p_mtp->phase == MTP_PHASE_DATA); } const uint16_t xact_len = tu_min32(p_mtp->total_len - p_mtp->xferred_len, CFG_TUD_MTP_EP_BUFSIZE); - TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, xact_len)); - + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, ep_addr, _mtpd_epbuf.buf, xact_len)); return true; } +bool tud_mtp_data_send(mtp_container_info_t* p_container) { + return mtpd_data_xfer(p_container, _mtpd_itf.ep_in); +} + +bool tud_mtp_data_receive(mtp_container_info_t* p_container) { + return mtpd_data_xfer(p_container, _mtpd_itf.ep_out); +} + bool tud_mtp_response_send(mtp_container_info_t* p_container) { mtpd_interface_t* p_mtp = &_mtpd_itf; p_mtp->phase = MTP_PHASE_RESPONSE_QUEUED; @@ -301,23 +312,25 @@ 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.command = &p_mtp->command; - cb_data.reply.header = (mtp_container_header_t*) p_container; - cb_data.reply.payload32 = p_container->data; - cb_data.reply.payload_size = CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t); + cb_data.command_container = &p_mtp->command; + cb_data.io_container.header = &p_container->header; + cb_data.io_container.payload32 = p_container->data; + cb_data.io_container.payload_size = CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t); cb_data.xferred_bytes = 0; cb_data.xfer_result = event; switch (p_mtp->phase) { case MTP_PHASE_IDLE: // received new command - TU_VERIFY(ep_addr == p_mtp->ep_out && p_container->type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); + TU_VERIFY(ep_addr == p_mtp->ep_out && p_container->header.type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); p_mtp->phase = MTP_PHASE_COMMAND; TU_ATTR_FALLTHROUGH; // handle in the next case case MTP_PHASE_COMMAND: { memcpy(&p_mtp->command, p_container, sizeof(mtp_container_command_t)); // save new command - if (mtpd_handle_cmd(p_mtp, &cb_data) < 0) { + p_container->header.len = sizeof(mtp_container_header_t); // default container to header only + process_cmd(p_mtp, &cb_data); + if (tud_mtp_command_received_cb(&cb_data) < 0) { p_mtp->phase = MTP_PHASE_ERROR; } break; @@ -328,18 +341,44 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t p_mtp->xferred_len += xferred_bytes; cb_data.xferred_bytes = p_mtp->xferred_len; - // transfer complete if ZLP or short packet or overflow + bool is_complete = false; + // complete if ZLP or short packet or overflow if (xferred_bytes == 0 || // ZLP (xferred_bytes & (bulk_mps - 1)) || // short packet p_mtp->xferred_len > p_mtp->total_len) { - cb_data.reply.header->len = sizeof(mtp_container_header_t); - tud_mtp_data_complete_cb(&cb_data); + is_complete = true; + } + + const mtp_container_info_t headerless_packet = { + .header = &p_mtp->io_header, + .payload = _mtpd_epbuf.buf, + .payload_size = CFG_TUD_MTP_EP_BUFSIZE + }; + + if (ep_addr == p_mtp->ep_in) { + // Data In + if (is_complete) { + cb_data.io_container.header->len = sizeof(mtp_container_header_t); + tud_mtp_data_complete_cb(&cb_data); + } else { + // 2nd+ packet: payload only + cb_data.io_container = headerless_packet; + tud_mtp_data_xfer_cb(&cb_data); + } } else { - // payload only packet - cb_data.reply.header = &p_mtp->reply_header; - cb_data.reply.payload = (uint8_t*) p_container; - cb_data.reply.payload_size = CFG_TUD_MTP_EP_BUFSIZE; - tud_mtp_data_more_cb(&cb_data); + // Data Out + if (p_mtp->xferred_len == xferred_bytes) { + // 1st OUT packet: header + payload + p_mtp->io_header = p_container->header; // save header for subsequent transaction + } else { + // 2nd+ packet: payload only + cb_data.io_container = headerless_packet; + } + tud_mtp_data_xfer_cb(&cb_data); + if (is_complete) { + cb_data.io_container.header->len = sizeof(mtp_container_header_t); + tud_mtp_data_complete_cb(&cb_data); + } } break; } @@ -445,11 +484,8 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // MTPD Internal functionality //--------------------------------------------------------------------+ -// Decode command and prepare response -int32_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { - cb_data->reply.header->len = sizeof(mtp_container_header_t); - - // pre-processed commands +// pre-processed commands +void process_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { switch (p_mtp->command.code) { case MTP_OP_GET_DEVICE_INFO: { tud_mtp_device_info_t dev_info = { @@ -491,15 +527,13 @@ int32_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { dev_info.mtp_extensions.utf16[i] = (uint16_t)CFG_TUD_MTP_DEVICEINFO_EXTENSIONS[i]; } #endif - mtp_container_add_raw(&cb_data->reply, &dev_info, sizeof(tud_mtp_device_info_t)); + mtp_container_add_raw(&cb_data->io_container, &dev_info, sizeof(tud_mtp_device_info_t)); break; } default: break; } - - return tud_mtp_command_received_cb(cb_data); } #if 0 diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index 28c01ddc6..7b6ae9e0f 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -38,11 +38,11 @@ typedef struct { uint8_t idx; // mtp instance - const mtp_container_command_t* command; - mtp_container_info_t reply; + const mtp_container_command_t* command_container; + mtp_container_info_t io_container; tusb_xfer_result_t xfer_result; - uint32_t xferred_bytes; + uint32_t xferred_bytes; // number of bytes transferred so far in this phase } tud_mtp_cb_data_t; // Number of supported operations, events, device properties, capture formats, playback formats @@ -94,8 +94,14 @@ typedef struct { //--------------------------------------------------------------------+ // Application API //--------------------------------------------------------------------+ + +// send data phase bool tud_mtp_data_send(mtp_container_info_t* p_container); -// bool tud_mtp_block_data_receive(); + +// receive data phase +bool tud_mtp_data_receive(mtp_container_info_t* p_container); + +// send response bool tud_mtp_response_send(mtp_container_info_t* p_container); //--------------------------------------------------------------------+ @@ -112,10 +118,10 @@ bool tud_mtp_response_send(mtp_container_info_t* p_container); */ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t * cb_data); -// Invoked when a data packet is received/sent, and more data is expected -int32_t tud_mtp_data_more_cb(tud_mtp_cb_data_t* cb_data); +// Invoked when a data packet is transferred, and more data is expected +int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data); -// Invoked when data phase is complete +// Invoked when all bytes in DATA phase is complete. A response packet is expected int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data); // Invoked when response phase is complete |
