From f9d4bc798177e63532cd5a2ef058400d6f0ef5e4 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 26 Sep 2025 15:52:37 +0700 Subject: [PATCH] implement tud_mtp_mounted() and tud_mtp_event_send() refactor phase state --- src/class/mtp/mtp.h | 33 ++++------ src/class/mtp/mtp_device.c | 132 +++++++++++++++++++++---------------- src/class/mtp/mtp_device.h | 5 ++ 3 files changed, 94 insertions(+), 76 deletions(-) diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index 073e6c470..b73bd200a 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -56,18 +56,14 @@ typedef enum { // PTP/MTP protocol phases typedef enum { - MTP_PHASE_IDLE = 0, - MTP_PHASE_COMMAND, + MTP_PHASE_COMMAND = 0, MTP_PHASE_DATA, MTP_PHASE_RESPONSE, - MTP_PHASE_RESPONSE_QUEUED, - MTP_PHASE_ERROR, - MTP_PHASE_NONE, + MTP_PHASE_ERROR } mtp_phase_type_t; // PTP/MTP Class requests, PIMA 15740-2000: D.5.2 -typedef enum -{ +typedef enum { MTP_REQ_CANCEL = 0x64, MTP_REQ_GET_EXT_EVENT_DATA = 0x65, MTP_REQ_RESET = 0x66, @@ -75,8 +71,7 @@ typedef enum } mtp_class_request_t; // PTP/MTP Container type -typedef enum -{ +typedef enum { MTP_CONTAINER_TYPE_UNDEFINED = 0, MTP_CONTAINER_TYPE_COMMAND_BLOCK = 1, MTP_CONTAINER_TYPE_DATA_BLOCK = 2, @@ -85,8 +80,7 @@ typedef enum } mtp_container_type_t; // MTP 1.1 Appendix A: Object formats -typedef enum -{ +typedef enum { // ---- Base formats ---- MTP_OBJ_FORMAT_UNDEFINED = 0x3000u, // Undefined object MTP_OBJ_FORMAT_ASSOCIATION = 0x3001u, // Association (for example, a folder) @@ -572,12 +566,6 @@ typedef enum { MTP_EVENT_OBJECT_REFERENCES_CHANGED = 0xC803u, } mtp_event_code_t; -// Predefined Object handles -typedef enum -{ - MTP_OBJH_ROOT = 0x0000, -} mtp_object_handles_t; - // Datatypes typedef enum { MTP_DATA_TYPE_UNDEFINED = 0x0000u, @@ -607,8 +595,7 @@ typedef enum { } mtp_data_type_t; // Get/Set -typedef enum -{ +typedef enum { MTP_MODE_GET = 0x00u, MTP_MODE_GET_SET = 0x01u, } mtp_mode_get_set_t; @@ -688,6 +675,14 @@ typedef struct { uint32_t payload_bytes; // available bytes for read/write } mtp_container_info_t; +typedef struct TU_ATTR_PACKED { + uint16_t code; + uint32_t session_id; + uint32_t transaction_id; + uint32_t params[3]; +} mtp_event_t; +TU_VERIFY_STATIC(sizeof(mtp_event_t) == 22, "size is not correct"); + #define mtp_string_t(_nchars) \ struct TU_ATTR_PACKED { \ uint8_t count; /* in characters including null */ \ diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index b3d982323..4e468458d 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -70,6 +70,7 @@ typedef struct { typedef struct { TUD_EPBUF_DEF(buf, CFG_TUD_MTP_EP_BUFSIZE); + TUD_EPBUF_TYPE_DEF(mtp_event_t, buf_event); } mtpd_epbuf_t; //--------------------------------------------------------------------+ @@ -142,18 +143,84 @@ TU_ATTR_UNUSED static tu_lookup_table_t const _mtp_op_table = { .items = _mpt_op_lookup }; +TU_ATTR_UNUSED static const char* _mtp_phase_str[] = { + "Command", + "Data", + "Response", + "Error" +}; + #endif //--------------------------------------------------------------------+ // Helper //--------------------------------------------------------------------+ - static bool prepare_new_command(mtpd_interface_t* p_mtp) { - p_mtp->phase = MTP_PHASE_IDLE; + p_mtp->phase = MTP_PHASE_COMMAND; return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_out, _mtpd_epbuf.buf, CFG_TUD_MTP_EP_BUFSIZE); } +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->xferred_len = 0; + + 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 { + // OUT transfer: total length is at least max packet size + p_mtp->total_len = tu_max32(p_container->header->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_min16((uint16_t) (p_mtp->total_len - p_mtp->xferred_len), CFG_TUD_MTP_EP_BUFSIZE); + if (xact_len) { + // already transferred all bytes in header's length. Application make an unnecessary extra call + 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; + p_container->header->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->header->transaction_id = p_mtp->command.transaction_id; + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, (uint16_t)p_container->header->len)); + return true; +} + +bool tud_mtp_mounted(void) { + mtpd_interface_t* p_mtp = &_mtpd_itf; + return p_mtp->ep_out != 0 && p_mtp->ep_in != 0; +} + +bool tud_mtp_event_send(mtp_event_t* event) { + mtpd_interface_t* p_mtp = &_mtpd_itf; + TU_VERIFY(p_mtp->ep_event != 0); + _mtpd_epbuf.buf_event = *event; + TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, p_mtp->ep_event)); // Claim the endpoint + + return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_event, (uint8_t*) &_mtpd_epbuf.buf_event, sizeof(mtp_event_t)); +} + //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ @@ -246,52 +313,6 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t return true; } -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->xferred_len = 0; - - 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 { - // OUT transfer: total length is at least max packet size - p_mtp->total_len = tu_max32(p_container->header->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_min16((uint16_t) (p_mtp->total_len - p_mtp->xferred_len), CFG_TUD_MTP_EP_BUFSIZE); - if (xact_len) { - // already transferred all bytes in header's length. Application make an unnecessary extra call - 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; - p_container->header->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->header->transaction_id = p_mtp->command.transaction_id; - TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, (uint16_t)p_container->header->len)); - return true; -} - // Transfer on bulk endpoints bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) { if (ep_addr == _mtpd_itf.ep_event) { @@ -304,7 +325,8 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t #if CFG_TUSB_DEBUG >= CFG_TUD_MTP_LOG_LEVEL tu_lookup_find(&_mtp_op_table, p_mtp->command.code); - TU_LOG_DRV(" MTP %s phase = %u\r\n", (const char *) tu_lookup_find(&_mtp_op_table, p_mtp->command.code), p_mtp->phase); + TU_LOG_DRV(" MTP %s: %s phase\r\n", (const char *) tu_lookup_find(&_mtp_op_table, p_mtp->command.code), + _mtp_phase_str[p_mtp->phase]); #endif const mtp_container_info_t headered_packet = { @@ -327,13 +349,9 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t cb_data.xfer_result = event; switch (p_mtp->phase) { - case MTP_PHASE_IDLE: + case MTP_PHASE_COMMAND: { // received new command 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 p_container->header.len = sizeof(mtp_container_header_t); // default container to header only process_cmd(p_mtp, &cb_data); @@ -389,16 +407,15 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t break; } - case MTP_PHASE_RESPONSE_QUEUED: + case MTP_PHASE_RESPONSE: // response phase is complete -> prepare for new command TU_ASSERT(ep_addr == p_mtp->ep_in); tud_mtp_response_complete_cb(&cb_data); prepare_new_command(p_mtp); break; - case MTP_PHASE_RESPONSE: case MTP_PHASE_ERROR: - // supposedly to be empty + // handled after switch, supposedly to be empty break; default: return false; } @@ -412,6 +429,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t return true; } + //--------------------------------------------------------------------+ // MTPD Internal functionality //--------------------------------------------------------------------+ diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index b10c3abda..4956628d3 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -72,6 +72,9 @@ typedef MTP_DEVICE_INFO_STRUCT( // Application API //--------------------------------------------------------------------+ +// check if mtp interface is mounted +bool tud_mtp_mounted(void); + // send data phase bool tud_mtp_data_send(mtp_container_info_t* p_container); @@ -81,6 +84,8 @@ bool tud_mtp_data_receive(mtp_container_info_t* p_container); // send response bool tud_mtp_response_send(mtp_container_info_t* p_container); +bool tud_mtp_event_send(mtp_event_t* event); + //--------------------------------------------------------------------+ // Control request Callbacks //--------------------------------------------------------------------+