implement tud_mtp_mounted() and tud_mtp_event_send()

refactor phase state
This commit is contained in:
hathach
2025-09-26 15:52:37 +07:00
parent 5c630ee0d0
commit f9d4bc7981
3 changed files with 94 additions and 76 deletions

View File

@ -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 */ \

View File

@ -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
//--------------------------------------------------------------------+

View File

@ -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
//--------------------------------------------------------------------+