summaryrefslogtreecommitdiff
path: root/src/class/mtp
diff options
context:
space:
mode:
authorhathach <[email protected]>2025-09-27 16:15:04 +0700
committerhathach <[email protected]>2025-09-27 17:08:21 +0700
commitcb21ca1b0cb3221341dca2559109c0bb4dc18e6f (patch)
treeaba4e31d802be53361d16c94150bf4fe110dbb0f /src/class/mtp
parentf9d4bc798177e63532cd5a2ef058400d6f0ef5e4 (diff)
implement control request
Diffstat (limited to 'src/class/mtp')
-rw-r--r--src/class/mtp/mtp.h13
-rw-r--r--src/class/mtp/mtp_device.c120
-rw-r--r--src/class/mtp/mtp_device.h44
-rw-r--r--src/class/mtp/mtp_device_storage.h93
4 files changed, 128 insertions, 142 deletions
diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h
index b73bd200a..0450c7e36 100644
--- a/src/class/mtp/mtp.h
+++ b/src/class/mtp/mtp.h
@@ -38,8 +38,6 @@
extern "C" {
#endif
-typedef uint16_t wchar16_t;
-
//--------------------------------------------------------------------+
// Media Transfer Protocol Class Constant
//--------------------------------------------------------------------+
@@ -759,15 +757,10 @@ typedef struct TU_ATTR_PACKED {
};
typedef struct TU_ATTR_PACKED {
- uint16_t wLength;
uint16_t code;
-} mtp_device_status_res_t;
-
-typedef struct TU_ATTR_PACKED {
- uint32_t object_handle;
- uint32_t storage_id;
- uint32_t parent_object_handle;
-} mtp_basic_object_info_t;
+ uint32_t transaction_id;
+} mtp_request_reset_cancel_data_t;
+TU_VERIFY_STATIC(sizeof(mtp_request_reset_cancel_data_t) == 6, "size is not correct");
//--------------------------------------------------------------------+
// Container helper function
diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c
index 4e468458d..a72712795 100644
--- a/src/class/mtp/mtp_device.c
+++ b/src/class/mtp/mtp_device.c
@@ -31,12 +31,11 @@
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
-#include "device/dcd.h" // for faking dcd_event_xfer_complete
+#include "device/dcd.h"
#include "device/usbd.h"
#include "device/usbd_pvt.h"
#include "mtp_device.h"
-#include "mtp_device_storage.h"
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
#ifndef CFG_TUD_MTP_LOG_LEVEL
@@ -45,7 +44,45 @@
#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_MTP_LOG_LEVEL, __VA_ARGS__)
-#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK bool tud_mtp_request_cancel_cb(tud_mtp_request_cb_data_t* cb_data) {
+ (void) cb_data;
+ return false;
+}
+TU_ATTR_WEAK bool tud_mtp_request_device_reset_cb(tud_mtp_request_cb_data_t* cb_data) {
+ (void) cb_data;
+ return false;
+}
+TU_ATTR_WEAK int32_t tud_mtp_request_get_extended_event_cb(tud_mtp_request_cb_data_t* cb_data) {
+ (void) cb_data;
+ return -1;
+}
+TU_ATTR_WEAK int32_t tud_mtp_request_get_device_status_cb(tud_mtp_request_cb_data_t* cb_data) {
+ (void) cb_data;
+ return -1;
+}
+TU_ATTR_WEAK bool tud_mtp_request_vendor_cb(tud_mtp_request_cb_data_t* cb_data) {
+ (void) cb_data;
+ return false;
+}
+TU_ATTR_WEAK int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t * cb_data) {
+ (void) cb_data;
+ return -1;
+}
+TU_ATTR_WEAK int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) {
+ (void) cb_data;
+ return -1;
+}
+TU_ATTR_WEAK int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) {
+ (void) cb_data;
+ return -1;
+}
+TU_ATTR_WEAK int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) {
+ (void) cb_data;
+ return -1;
+}
//--------------------------------------------------------------------+
// STRUCT
@@ -66,6 +103,8 @@ typedef struct {
uint32_t session_id;
mtp_container_command_t command;
mtp_container_header_t io_header;
+
+ TU_ATTR_ALIGNED(4) uint8_t control_buf[CFG_TUD_MTP_EP_CONTROL_BUFSIZE];
} mtpd_interface_t;
typedef struct {
@@ -76,16 +115,10 @@ typedef struct {
//--------------------------------------------------------------------+
// INTERNAL FUNCTION DECLARATION
//--------------------------------------------------------------------+
-static void process_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data);
-
-//--------------------------------------------------------------------+
-// MTP variable declaration
-//--------------------------------------------------------------------+
static mtpd_interface_t _mtpd_itf;
CFG_TUD_MEM_SECTION static mtpd_epbuf_t _mtpd_epbuf;
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_device_status_res_t _mtpd_device_status_res;
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_basic_object_info_t _mtpd_soi;
+static void preprocess_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data);
//--------------------------------------------------------------------+
// Debug
@@ -226,7 +259,6 @@ bool tud_mtp_event_send(mtp_event_t* event) {
//--------------------------------------------------------------------+
void mtpd_init(void) {
tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t));
- tu_memclr(&_mtpd_soi, sizeof(mtp_basic_object_info_t));
}
bool mtpd_deinit(void) {
@@ -236,8 +268,6 @@ bool mtpd_deinit(void) {
void mtpd_reset(uint8_t rhport) {
(void) rhport;
tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t));
- tu_memclr(&_mtpd_epbuf, sizeof(mtpd_epbuf_t));
- tu_memclr(&_mtpd_soi, sizeof(mtp_basic_object_info_t));
}
uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
@@ -264,7 +294,6 @@ uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16
// Open endpoint pair
TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(ep_desc), 2, TUSB_XFER_BULK, &p_mtp->ep_out, &p_mtp->ep_in), 0);
-
TU_ASSERT(prepare_new_command(p_mtp), 0);
return mtpd_itf_size;
@@ -274,40 +303,63 @@ uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
- if (stage != CONTROL_STAGE_SETUP) {
- return true; // nothing to do with DATA & ACK stage
- }
+ mtpd_interface_t* p_mtp = &_mtpd_itf;
+ tud_mtp_request_cb_data_t cb_data = {
+ .idx = 0,
+ .stage = stage,
+ .request = request,
+ .buf = p_mtp->control_buf,
+ .bufsize = tu_le16toh(request->wLength),
+ };
switch (request->bRequest) {
case MTP_REQ_CANCEL:
- TU_LOG_DRV(" MTP request: MTP_REQ_CANCEL\n");
- tud_mtp_storage_cancel();
+ TU_LOG_DRV(" MTP request: Cancel\n");
+ if (stage == CONTROL_STAGE_SETUP) {
+ return tud_control_xfer(rhport, request, p_mtp->control_buf, CFG_TUD_MTP_EP_CONTROL_BUFSIZE);
+ } else if (stage == CONTROL_STAGE_ACK) {
+ return tud_mtp_request_cancel_cb(&cb_data);
+ }
break;
case MTP_REQ_GET_EXT_EVENT_DATA:
- TU_LOG_DRV(" MTP request: MTP_REQ_GET_EXT_EVENT_DATA\n");
+ TU_LOG_DRV(" MTP request: Get Extended Event Data\n");
+ if (stage == CONTROL_STAGE_SETUP) {
+ const int32_t len = tud_mtp_request_get_extended_event_cb(&cb_data);
+ TU_VERIFY(len > 0);
+ return tud_control_xfer(rhport,request, p_mtp->control_buf, (uint16_t) len);
+ }
break;
case MTP_REQ_RESET:
- TU_LOG_DRV(" MTP request: MTP_REQ_RESET\n");
- tud_mtp_storage_reset();
- // Prepare for a new command
- TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, _mtpd_epbuf.buf, CFG_TUD_MTP_EP_BUFSIZE));
+ TU_LOG_DRV(" MTP request: Device Reset\n");
+ // used by the host to return the Still Image Capture Device to the Idle state after the Bulk-pipe has stalled
+ if (stage == CONTROL_STAGE_SETUP) {
+ // clear stalled
+ if (usbd_edpt_stalled(rhport, p_mtp->ep_out)) {
+ usbd_edpt_clear_stall(rhport, p_mtp->ep_out);
+ }
+ if (usbd_edpt_stalled(rhport, p_mtp->ep_in)) {
+ usbd_edpt_clear_stall(rhport, p_mtp->ep_in);
+ }
+ } else if (stage == CONTROL_STAGE_ACK) {
+ prepare_new_command(p_mtp);
+ return tud_mtp_request_device_reset_cb(&cb_data);
+ }
break;
case MTP_REQ_GET_DEVICE_STATUS: {
- TU_LOG_DRV(" MTP request: MTP_REQ_GET_DEVICE_STATUS\n");
- uint16_t len = 4;
- _mtpd_device_status_res.wLength = len;
- // Cancel is synchronous, always answer OK
- _mtpd_device_status_res.code = MTP_RESP_OK;
- TU_ASSERT(tud_control_xfer(rhport, request, (uint8_t *)&_mtpd_device_status_res , len));
+ TU_LOG_DRV(" MTP request: Get Device Status\n");
+ if (stage == CONTROL_STAGE_SETUP) {
+ const int32_t len = tud_mtp_request_get_device_status_cb(&cb_data);
+ TU_VERIFY(len > 0);
+ return tud_control_xfer(rhport, request, p_mtp->control_buf, (uint16_t) len);
+ }
break;
}
default:
- TU_LOG_DRV(" MTP request: invalid request\r\n");
- return false; // stall unsupported request
+ return tud_mtp_request_vendor_cb(&cb_data);
}
return true;
@@ -354,7 +406,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
TU_VERIFY(ep_addr == p_mtp->ep_out && p_container->header.type == MTP_CONTAINER_TYPE_COMMAND_BLOCK);
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);
+ preprocess_cmd(p_mtp, &cb_data);
if (tud_mtp_command_received_cb(&cb_data) < 0) {
p_mtp->phase = MTP_PHASE_ERROR;
}
@@ -435,7 +487,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
//--------------------------------------------------------------------+
// pre-processed commands
-void process_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) {
+void preprocess_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 = {
diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h
index 4956628d3..c69f6f12f 100644
--- a/src/class/mtp/mtp_device.h
+++ b/src/class/mtp/mtp_device.h
@@ -36,6 +36,7 @@
extern "C" {
#endif
+// callback data for Bulk Only Transfer (BOT) protocol
typedef struct {
uint8_t idx; // mtp instance
const mtp_container_command_t* command_container;
@@ -45,6 +46,16 @@ typedef struct {
uint32_t total_xferred_bytes; // number of bytes transferred so far in this phase
} tud_mtp_cb_data_t;
+// callback data for Control requests
+typedef struct {
+ uint8_t idx;
+ uint8_t stage; // control stage
+ const tusb_control_request_t* request;
+ // buffer for data stage
+ uint8_t* buf;
+ uint16_t bufsize;
+} tud_mtp_request_cb_data_t;
+
// Number of supported operations, events, device properties, capture formats, playback formats
// and max number of characters for strings manufacturer, model, device_version, serial_number
#define MTP_DEVICE_INFO_STRUCT(_extension_nchars, _op_count, _event_count, _devprop_count, _capture_count, _playback_count) \
@@ -89,24 +100,47 @@ bool tud_mtp_event_send(mtp_event_t* event);
//--------------------------------------------------------------------+
// Control request Callbacks
//--------------------------------------------------------------------+
-// bool tud_mtp_control_xfer_cb(uint8_t idx, uint8_t stage, tusb_control_request_t const *p_request);
+
+// Invoked when received Cancel request. Data is available in callback data's buffer
+// return false to stall the request
+bool tud_mtp_request_cancel_cb(tud_mtp_request_cb_data_t* cb_data);
+
+// Invoked when received Device Reset request
+// return false to stall the request
+bool tud_mtp_request_device_reset_cb(tud_mtp_request_cb_data_t* cb_data);
+
+// Invoked when received Get Extended Event request. Application fill callback data's buffer for response
+// return negative to stall the request
+int32_t tud_mtp_request_get_extended_event_cb(tud_mtp_request_cb_data_t* cb_data);
+
+// Invoked when received Get DeviceStatus request. Application fill callback data's buffer for response
+// return negative to stall the request
+int32_t tud_mtp_request_get_device_status_cb(tud_mtp_request_cb_data_t* cb_data);
+
+// Invoked when received vendor-specific request not in the above standard MTP requests
+// return false to stall the request
+bool tud_mtp_request_vendor_cb(tud_mtp_request_cb_data_t* cb_data);
//--------------------------------------------------------------------+
// Bulk only protocol Callbacks
//--------------------------------------------------------------------+
-/* Invoked when new command is received. Application fill the cb_data->reply with either DATA or RESPONSE and call
- * tud_mtp_data_send() or tud_mtp_response_send(). Return negative to stall the endpoints
- */
+// Invoked when new command is received. Application fill the cb_data->io_container and call tud_mtp_data_send() or
+// tud_mtp_response_send() for Data or Response phase.
+// Return negative to stall the endpoints
int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t * cb_data);
-// Invoked when a data packet is transferred, and more data is expected
+// Invoked when a data packet is transferred. If data spans over multiple packets, application can use
+// total_xferred_bytes and io_container's payload_bytes to determine the offset and remaining bytes to be transferred.
+// Return negative to stall the endpoints
int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data);
// Invoked when all bytes in DATA phase is complete. A response packet is expected
+// Return negative to stall the endpoints
int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data);
// Invoked when response phase is complete
+// Return negative to stall the endpoints
int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data);
//--------------------------------------------------------------------+
diff --git a/src/class/mtp/mtp_device_storage.h b/src/class/mtp/mtp_device_storage.h
deleted file mode 100644
index 30038978f..000000000
--- a/src/class/mtp/mtp_device_storage.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2025 Ennebi Elettronica (https://ennebielettronica.com)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#ifndef _TUSB_MTP_DEVICE_STORAGE_H_
-#define _TUSB_MTP_DEVICE_STORAGE_H_
-
-#include "common/tusb_common.h"
-#include "mtp.h"
-
-#if (CFG_TUD_ENABLED && CFG_TUD_MTP)
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-//--------------------------------------------------------------------+
-// Storage Application Callbacks
-//--------------------------------------------------------------------+
-
-/*
- * The entire MTP functionality is based on object handles, as described in MTP Specs v. 1.1 under 3.4.1.
- * The major weakness of the protocol is that those handles are supposed to be unique within a session
- * and for every enumerated object. There is no specified lifetime limit or way to control the expiration:
- * once given, they have to persist for an indefinite time and number of iterations.
- * If the filesystem does not provide unique persistent object handle for every entry, the best approach
- * would be to keep a full association between generated handles and full file paths. The suggested
- * approach with memory constrained devices is to keep a hard ID associated with each file or a volatile
- * ID generated on the fly and invalidated on each operation that may rearrange the order.
- * In order to invalidate existing IDS, it might be necessary to invalidate the whole session from
- * the device side.
- * Depending on the application, the handle could be also be the file name or a tag (i.e. host-only file access)
- */
-
-// Format the specified storage
-mtp_response_t tud_mtp_storage_format(uint32_t storage_id);
-
-// Called with the creation of a new object is requested.
-// The handle of the new object shall be returned in new_object_handle.
-// The structure info contains the information to be used for file creation, as passted by the host.
-// Note that the variable information (e.g. wstring file name, dates and tags shall be retrieved by using the library functions)
-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);
-
-// Write object data
-//
-// The function shall open the object for writing if not already open.
-// The binary data shall be written to the file in full before this function is returned.
-mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t *buffer, uint32_t buffer_size);
-
-// Move an object to a new parent
-mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_parent_object_handle);
-
-// Delete the specified object
-mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle);
-
-// Issued when IO operation has been terminated (e.g. read, traverse), close open file handles
-void tud_mtp_storage_object_done(void);
-
-// Cancel any pending operation. Current operation shall be discarded.
-void tud_mtp_storage_cancel(void);
-
-// Restore the operation out of reset. Cancel any pending operation and close the session.
-void tud_mtp_storage_reset(void);
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* CFG_TUD_ENABLED && CFG_TUD_MTP */
-
-#endif /* _TUSB_MTP_DEVICE_STORAGE_H_ */