From cb21ca1b0cb3221341dca2559109c0bb4dc18e6f Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 27 Sep 2025 16:15:04 +0700 Subject: [PATCH] implement control request --- examples/device/mtp/src/mtp_fs_example.c | 41 ++++++-- examples/device/mtp/src/tusb_config.h | 5 +- examples/device/mtp/src/usb_descriptors.c | 10 +- src/class/mtp/mtp.h | 13 +-- src/class/mtp/mtp_device.c | 120 ++++++++++++++++------ src/class/mtp/mtp_device.h | 44 +++++++- src/class/mtp/mtp_device_storage.h | 93 ----------------- 7 files changed, 169 insertions(+), 157 deletions(-) delete mode 100644 src/class/mtp/mtp_device_storage.h diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index a13acfcdd..9d90f7663 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -23,7 +23,6 @@ * */ -#include "class/mtp/mtp_device_storage.h" #include "tusb.h" #include "tinyusb_logo_png.h" @@ -188,6 +187,40 @@ static inline uint8_t* fs_malloc(size_t size) { #endif } +//--------------------------------------------------------------------+ +// Control Request callback +//--------------------------------------------------------------------+ +bool tud_mtp_request_cancel_cb(tud_mtp_request_cb_data_t* cb_data) { + mtp_request_reset_cancel_data_t cancel_data; + memcpy(&cancel_data, cb_data->buf, sizeof(cancel_data)); + (void) cancel_data.code; + (void ) cancel_data.transaction_id; + return true; +} + +// 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) { + (void) cb_data; + return true; +} + +// 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) { + (void) cb_data; + return false; // not implemented yet +} + +// 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) { + uint16_t* buf16 = (uint16_t*)(uintptr_t) cb_data->buf; + buf16[0] = 4; // length + buf16[1] = MTP_RESP_OK; // status + return 4; +} + //--------------------------------------------------------------------+ // Bulk Only Protocol //--------------------------------------------------------------------+ @@ -531,9 +564,3 @@ int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) { (void) cb_data; return 0; // nothing to do } - -void tud_mtp_storage_cancel(void) { -} - -void tud_mtp_storage_reset(void) { -} diff --git a/examples/device/mtp/src/tusb_config.h b/examples/device/mtp/src/tusb_config.h index 62a5729bf..4d166aa63 100644 --- a/examples/device/mtp/src/tusb_config.h +++ b/examples/device/mtp/src/tusb_config.h @@ -93,6 +93,7 @@ //------------- CLASS -------------// #define CFG_TUD_MTP 1 #define CFG_TUD_MTP_EP_BUFSIZE 512 +#define CFG_TUD_MTP_EP_CONTROL_BUFSIZE 16 // should be enough to hold data in MTP control request //------------- MTP device info -------------// #define CFG_TUD_MTP_DEVICEINFO_EXTENSIONS "microsoft.com: 1.0; " @@ -131,10 +132,6 @@ MTP_OBJ_FORMAT_TEXT, \ MTP_OBJ_FORMAT_PNG -#define CFG_TUD_MANUFACTURER "TinyUsb Manufacturer" -#define CFG_TUD_MODEL "TinyUsb Device" -#define CFG_MTP_INTERFACE (CFG_TUD_MODEL " MTP") - #ifdef __cplusplus } #endif diff --git a/examples/device/mtp/src/usb_descriptors.c b/examples/device/mtp/src/usb_descriptors.c index 4a43a0dcc..810137c46 100644 --- a/examples/device/mtp/src/usb_descriptors.c +++ b/examples/device/mtp/src/usb_descriptors.c @@ -140,10 +140,10 @@ enum { char const *string_desc_arr[] = { (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) - CFG_TUD_MANUFACTURER, // 1: Manufacturer - CFG_TUD_MODEL, // 2: Product + "TinyUsb", // 1: Manufacturer + "TinyUsb Device", // 2: Product NULL, // 3: Serials will use unique ID if possible - CFG_MTP_INTERFACE, // 4: MTP Interface + "TinyUSBB MTP", // 4: MTP Interface }; static uint16_t _desc_str[32 + 1]; @@ -168,7 +168,9 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors - if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL; + if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) { + return NULL; + } const char *str = string_desc_arr[index]; 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_ */