/* *-------------------------------------- * Program Name: TRANSFER * Author: Jacob "jacobly" Young * License: Public Domain * Description: File Transfer Program *-------------------------------------- */ /* MTP Structure Forward Declarations */ typedef struct mtp_global mtp_global_t; #define usb_callback_data_t mtp_global_t #define usb_transfer_data_t mtp_global_t /* Includes */ #include "ui.h" #include "var.h" #include #include #include #include #include #include #include #include #include #include #include #include /* Macros */ #define lengthof(array) (sizeof(array) / sizeof(*(array))) #define COUNT_EACH(...) +1 #define OBJECT_BUFFER \ ((void *)((mtp_byte_t *)lcd_Ram + LCD_WIDTH * LCD_HEIGHT)) #define FOR_EACH_SUPP_OPR(X) \ X(GET_DEVICE_INFO) \ X(OPEN_SESSION) \ X(CLOSE_SESSION) \ X(GET_STORAGE_IDS) \ X(GET_STORAGE_INFO) \ X(GET_NUM_OBJECTS) \ X(GET_OBJECT_HANDLES) \ X(GET_OBJECT_INFO) \ X(GET_OBJECT) \ X(DELETE_OBJECT) \ X(SEND_OBJECT_INFO) \ X(SEND_OBJECT) \ X(GET_DEVICE_PROP_DESC) \ X(GET_DEVICE_PROP_VALUE) \ X(SET_DEVICE_PROP_VALUE) \ X(MOVE_OBJECT) #define FOR_EACH_SUPP_EVT(X) #define FOR_EACH_SUPP_DP(X) \ X(uint8, BATTERY_LEVEL, RANGE) \ X(datetime, DATE_TIME, NONE) \ X(uint32, PERCEIVED_DEVICE_TYPE, NONE) #define FOR_EACH_SUPP_CF(X) \ X(UNDEFINED) #define FOR_EACH_SUPP_PF(X) \ FOR_EACH_SUPP_CF(X) #define FOR_EACH_STRING_DESCRIPTOR(X) \ X(const, manufacturer) \ X(const, product) \ X( , serial_number) \ X(const, charging_cfg) \ X(const, mtp_interface) #define DEFINE_STRING_DESCRIPTOR(const, name) \ static const usb_string_descriptor_t name = { \ .bLength = sizeof(L##name), \ .bDescriptorType = USB_STRING_DESCRIPTOR, \ .bString = L##name, \ }; #define FOR_EACH_STORAGE(X) \ X(ram) \ X(arc) #define FOR_EACH_STORAGE_NONE(X) \ X(none) \ FOR_EACH_STORAGE(X) #define FOR_EACH_STORAGE_TOTAL(X) \ FOR_EACH_STORAGE(X) \ X(total) #define FOR_EACH_STORAGE_NONE_TOTAL(X) \ X(none) \ FOR_EACH_STORAGE(X) \ X(total) #define get_ram_free_space_in_bytes \ os_MemChk(NULL) #define get_arc_free_space_in_bytes \ (os_ArcChk(), os_TempFreeArc) #define BATTERY_LEVEL_MIN 0 #define BATTERY_LEVEL_MAX 4 #define BATTERY_LEVEL_STEP 1 #define BATTERY_LEVEL_DEF 0 #define BATTERY_LEVEL_GET_SET 0 #define BATTERY_LEVEL_GET(current) \ current = boot_GetBatteryStatus() #define BATTERY_LEVEL_SET(new) #define DATE_TIME_DEF { \ .length = lengthof(Lfactory_datetime), \ .string = Lfactory_datetime, \ } #define DATE_TIME_GET_SET 1 #define DATE_TIME_GET(current) \ get_datetime(current.string) #define DATE_TIME_SET(new) #define PERCEIVED_DEVICE_TYPE_DEF 0 #define PERCEIVED_DEVICE_TYPE_GET_SET 0 #define PERCEIVED_DEVICE_TYPE_GET(current) \ current = 3 #define PERCEIVED_DEVICE_TYPE_SET(new) /* MTP Types */ typedef uint8_t mtp_byte_t; typedef uint16_t mtp_version_t; typedef uint16_t mtp_enum_t; typedef uint32_t mtp_size_t; typedef uint32_t mtp_id_t; typedef uint32_t mtp_param_t; typedef uint64_t mtp_uint64_t; #define DECLARE_TRUNC_TYPE(name) \ typedef union mtp_trunc_##name { \ mtp_##name##_t name; \ size_t word; \ mtp_byte_t byte; \ } mtp_trunc_##name##_t; DECLARE_TRUNC_TYPE(size) DECLARE_TRUNC_TYPE(id) DECLARE_TRUNC_TYPE(param) DECLARE_TRUNC_TYPE(uint64) typedef usb_error_t(*mtp_transaction_callback_t)( mtp_global_t global); /* MTP Constants */ #define MTP_ROOT_OBJECT_HANDLE UINT32_C(0xFFFFFFFF) #define Los_specific L"MSFT100\1" #define Lmtp_extensions L"microsoft.com: 1.0;" #define Lmanufacturer L"Texas Instruments Incorporated" #define Lproduct L"TI-83 Premium CE" /* default must be longer than alt */ #define Lproduct84 L"TI-84 Plus CE" #define Ldevice_version L"2.20" #define Lserial_number L"0000000000000000" #define Lcharging_cfg L"Charging" #define Lmtp_interface L"MTP" /* magic string to aid detection */ #define Lram_storage_desc L"RAM" #define Lram_volume_id Lserial_number"R" #define Larc_storage_desc L"Archive" #define Larc_volume_id Lserial_number"A" #define Lfactory_datetime L"20150101T000000" typedef enum string_id { Imissing, #define DEFINE_STRING_DESCRIPTOR_ID(const, name) I##name, FOR_EACH_STRING_DESCRIPTOR(DEFINE_STRING_DESCRIPTOR_ID) Inum_strings, } string_id_t; #define Inone UINT32_C(0x00000000) #define Iram UINT32_C(0x00010001) #define Iarc UINT32_C(0x00020001) #define Itotal UINT32_C(0xFFFFFFFF) #define storage_count 2 #define storage_flag 0x80 #define reserved_flag 0x40 #define none_mask 0x00 #define ram_mask 0x80 #define arc_mask 0x80 #define total_mask 0x00 #define none_flag 0x80 #define ram_flag 0x00 #define arc_flag 0x80 #define total_flag 0x00 #define ram_max_capacity 0x0002577Fu #define arc_max_capacity 0x002F0000u #define MTP_EP_CONTROL (0) #define MTP_EP_DATA_IN (USB_DEVICE_TO_HOST | 1) #define MTP_EP_DATA_OUT (USB_HOST_TO_DEVICE | 2) #define MTP_EP_INT (USB_DEVICE_TO_HOST | 3) #define MTP_MAX_EVT_PARAMS (3) #define MTP_MAX_SOI_PARAMS (3) #define MTP_MAX_PARAMS (5) #define MTP_MAX_PENDING_EVENTS (2) #define MTP_MAX_BULK_PKT_SZ (0x40) #define MTP_MAX_INT_PKT_SZ (sizeof(mtp_event_t) + 1) /* MTP Enumerations */ typedef enum mtp_request { GET_EXTENDED_EVENT_DATA = 0x65, DEVICE_RESET_REQUEST, GET_DEVICE_STATUS, } mtp_request_t; typedef enum mtp_block_type { MTP_BT_UNDEFINED, MTP_BT_COMMAND, MTP_BT_DATA, MTP_BT_RESPONSE, MTP_BT_EVENT, } mtp_block_type_t; typedef enum mtp_form { MTP_FORM_NONE, MTP_FORM_RANGE, MTP_FORM_ENUM, MTP_FORM_DATE_TIME, MTP_FORM_FIXED_LENGTH_ARRAY, MTP_FORM_REGULAR_EXPRESSION, MTP_FORM_BYTE_ARRAY, MTP_FORM_LONG_STRING = 0xFF, } mtp_form_t; typedef enum mtp_type_code { MTP_TC_undef /* Undefined */ = 0x0000, MTP_TC_int8 /* Signed 8-bit integer */ = 0x0001, MTP_TC_uint8 /* Unsigned 8-bit integer */ = 0x0002, MTP_TC_int16 /* Signed 16-bit integer */ = 0x0003, MTP_TC_uint16 /* Unsigned 16-bit integer */ = 0x0004, MTP_TC_int32 /* Signed 32-bit integer */ = 0x0005, MTP_TC_uint32 /* Unsigned 32-bit integer */ = 0x0006, MTP_TC_int64 /* Signed 64-bit integer */ = 0x0007, MTP_TC_uint64 /* Unsigned 64-bit integer */ = 0x0008, MTP_TC_int128 /* Signed 128-bit integer */ = 0x0009, MTP_TC_uint128 /* Unsigned 128-bit integer */ = 0x000A, MTP_TC_aint8 /* Array of signed 8-bit integers */ = 0x4001, MTP_TC_auint8 /* Array of unsigned 8-bit integers */ = 0x4002, MTP_TC_aint16 /* Array of signed 16-bit integers */ = 0x4003, MTP_TC_auint16 /* Array of unsigned 16-bit integers */ = 0x4004, MTP_TC_aint32 /* Array of signed 32-bit integers */ = 0x4005, MTP_TC_auint32 /* Array of unsigned 32-bit integers */ = 0x4006, MTP_TC_aint64 /* Array of signed 64-bit integers */ = 0x4007, MTP_TC_auint64 /* Array of unsigned 64-bit integers */ = 0x4008, MTP_TC_aint128 /* Array of signed 128-bit integers */ = 0x4009, MTP_TC_auint128 /* Array of unsigned 128-bit integers */ = 0x400A, MTP_TC_str /* Variable-length Unicode string */ = 0xFFFF, MTP_TC_datetime /* ISO 8601 Unicode string */ = 0xFFFF, } mtp_type_code_t; typedef struct datetime { mtp_byte_t length; wchar_t string[lengthof(Lfactory_datetime)]; } datetime_t; typedef enum mtp_functional_mode { MTP_MODE_STANDARD_MODE = 0x0000, MTP_MODE_SLEEP_STATE = 0x0001, MTP_MODE_NON_RESPONSIVE_PLAYBACK = 0xC001, MTP_MODE_RESPONSIVE_PLAYBACK = 0xC002, } mtp_functional_mode_t; typedef enum mtp_storage_type { MTP_ST_UNDEFINED = 0x0000, MTP_ST_FIXED_ROM = 0x0001, MTP_ST_REMOVABLE_ROM = 0x0002, MTP_ST_FIXED_RAM = 0x0003, MTP_ST_REMOVABLE_RAM = 0x0004, } mtp_storage_type_t; typedef enum mtp_filesystem_type { MTP_FT_UNDEFINED = 0x0000, MTP_FT_GENERIC_FLAT = 0x0001, MTP_FT_GENERIC_HIERARCHICAL = 0x0002, MTP_FT_DCF = 0x0003, } mtp_filesystem_type_t; typedef enum mtp_access_capability { MTP_AC_READ_WRITE = 0x0000, MTP_AC_READ_ONLY_WITHOUT_OBJECT_DELETION = 0x0001, MTP_AC_READ_ONLY_WITH_OBJECT_DELETION = 0x0002, } mtp_access_capability_t; typedef enum mtp_protection_status { MTP_PS_NO_PROTECTION = 0x0000, MTP_PS_READ_ONLY = 0x0001, MTP_PS_READ_ONLY_DATA = 0x8002, MTP_PS_NOT_TRANSFERABLE_DATA = 0x8003, } mtp_protection_status_t; typedef enum mtp_operation_code { MTP_OPR_GET_DEVICE_INFO = 0x1001, MTP_OPR_OPEN_SESSION = 0x1002, MTP_OPR_CLOSE_SESSION = 0x1003, MTP_OPR_GET_STORAGE_IDS = 0x1004, MTP_OPR_GET_STORAGE_INFO = 0x1005, MTP_OPR_GET_NUM_OBJECTS = 0x1006, MTP_OPR_GET_OBJECT_HANDLES = 0x1007, MTP_OPR_GET_OBJECT_INFO = 0x1008, MTP_OPR_GET_OBJECT = 0x1009, MTP_OPR_GET_THUMB = 0x100A, MTP_OPR_DELETE_OBJECT = 0x100B, MTP_OPR_SEND_OBJECT_INFO = 0x100C, MTP_OPR_SEND_OBJECT = 0x100D, MTP_OPR_INITIATE_CAPTURE = 0x100E, MTP_OPR_FORMAT_STORE = 0x100F, MTP_OPR_RESET_DEVICE = 0x1010, MTP_OPR_SELF_TEST = 0x1011, MTP_OPR_SET_OBJECT_PROTECTION = 0x1012, MTP_OPR_POWER_DOWN = 0x1013, MTP_OPR_GET_DEVICE_PROP_DESC = 0x1014, MTP_OPR_GET_DEVICE_PROP_VALUE = 0x1015, MTP_OPR_SET_DEVICE_PROP_VALUE = 0x1016, MTP_OPR_RESET_DEVICE_PROP_VALUE = 0x1017, MTP_OPR_TERMINATE_OPEN_CAPTURE = 0x1018, MTP_OPR_MOVE_OBJECT = 0x1019, MTP_OPR_COPY_OBJECT = 0x101A, MTP_OPR_GET_PARTIAL_OBJECT = 0x101B, MTP_OPR_INITIATE_OPEN_CAPTURE = 0x101C, MTP_OPR_GET_OBJECT_PROPS_SUPPORTED = 0x9801, MTP_OPR_GET_OBJECT_PROP_DESC = 0x9802, MTP_OPR_GET_OBJECT_PROP_VALUE = 0x9803, MTP_OPR_SET_OBJECT_PROP_VALUE = 0x9804, MTP_OPR_GET_OBJECT_PROP_LIST = 0x9805, MTP_OPR_SET_OBJECT_PROP_LIST = 0x9806, MTP_OPR_GET_INTERDEPENDENT_PROP_DESC = 0x9807, MTP_OPR_SEND_OBJECT_PROP_LIST = 0x9808, MTP_OPR_GET_OBJECT_REFERENCES = 0x9810, MTP_OPR_SET_OBJECT_REFERENCES = 0x9811, MTP_OPR_SKIP = 0x9820, } mtp_operation_code_t; typedef enum mtp_response_code { MTP_RSP_UNDEFINED = 0x2000, MTP_RSP_OK = 0x2001, MTP_RSP_GENERAL_ERROR = 0x2002, MTP_RSP_SESSION_NOT_OPEN = 0x2003, MTP_RSP_INVALID_TRANSACTION_ID = 0x2004, MTP_RSP_OPERATION_NOT_SUPPORTED = 0x2005, MTP_RSP_PARAMETER_NOT_SUPPORTED = 0x2006, MTP_RSP_INCOMPLETE_TRANSFER = 0x2007, MTP_RSP_INVALID_STORAGE_ID = 0x2008, MTP_RSP_INVALID_OBJECT_HANDLE = 0x2009, MTP_RSP_DEVICE_PROP_NOT_SUPPORTED = 0x200A, MTP_RSP_INVALID_OBJECT_FORMAT_CODE = 0x200B, MTP_RSP_STORE_FULL = 0x200C, MTP_RSP_OBJECT_WRITE_PROTECTED = 0x200D, MTP_RSP_STORE_READ_ONLY = 0x200E, MTP_RSP_ACCESS_DENIED = 0x200F, MTP_RSP_NO_THUMBNAIL_PRESENT = 0x2010, MTP_RSP_SELF_TEST_FAILED = 0x2011, MTP_RSP_PARTIAL_DELETION = 0x2012, MTP_RSP_STORE_NOT_AVAILABLE = 0x2013, MTP_RSP_SPECIFICATION_BY_FORMAT_UNSUPPORTED = 0x2014, MTP_RSP_NO_VALID_OBJECT_INFO = 0x2015, MTP_RSP_INVALID_CODE_FORMAT = 0x2016, MTP_RSP_UNKNOWN_VENDOR_CODE = 0x2017, MTP_RSP_CAPTURE_ALREADY_TERMINATED = 0x2018, MTP_RSP_DEVICE_BUSY = 0x2019, MTP_RSP_INVALID_PARENT_OBJECT = 0x201A, MTP_RSP_INVALID_DEVICE_PROP_FORMAT = 0x201B, MTP_RSP_INVALID_DEVICE_PROP_VALUE = 0x201C, MTP_RSP_INVALID_PARAMETER = 0x201D, MTP_RSP_SESSION_ALREADY_OPEN = 0x201E, MTP_RSP_TRANSACTION_CANCELLED = 0x201F, MTP_RSP_SPECIFICATION_OF_DESTINATION_UNSUPPORTED = 0x2020, MTP_RSP_INVALID_OBJECT_PROPCODE = 0xA801, MTP_RSP_INVALID_OBJECT_PROP_FORMAT = 0xA802, MTP_RSP_INVALID_OBJECT_PROP_VALUE = 0xA803, MTP_RSP_INVALID_OBJECT_REFERENCE = 0xA804, MTP_RSP_GROUP_NOT_SUPPORTED = 0xA805, MTP_RSP_INVALID_DATASET = 0xA806, MTP_RSP_SPECIFICATION_BY_GROUP_UNSUPPORTED = 0xA807, MTP_RSP_SPECIFICATION_BY_DEPTH_UNSUPPORTED = 0xA808, MTP_RSP_OBJECT_TOO_LARGE = 0xA809, MTP_RSP_OBJECT_PROP_NOT_SUPPORTED = 0xA80A, } mtp_response_code_t; typedef enum mtp_object_format_code { MTP_OF_UNDEFINED = 0x3000, MTP_OF_ASSOCIATION = 0x3001, } mtp_object_format_code_t; typedef enum mtp_event_code { MTP_EVT_UNDEFINED = 0x4000, MTP_EVT_CANCEL_TRANSACTION = 0x4001, MTP_EVT_OBJECT_ADDED = 0x4002, MTP_EVT_OBJECT_REMOVED = 0x4003, MTP_EVT_STORE_ADDED = 0x4004, MTP_EVT_STORE_REMOVED = 0x4005, MTP_EVT_DEVICE_PROP_CHANGED = 0x4006, MTP_EVT_OBJECT_INFO_CHANGED = 0x4007, MTP_EVT_DEVICE_INFO_CHANGED = 0x4008, MTP_EVT_REQUEST_OBJECT_TRANSFER = 0x4009, MTP_EVT_STORE_FULL = 0x400A, MTP_EVT_DEVICE_RESET = 0x400B, MTP_EVT_STORAGE_INFO_CHANGED = 0x400C, MTP_EVT_CAPTURE_COMPLETE = 0x400D, MTP_EVT_UNREPORTED_STATUS = 0x400E, MTP_EVT_OBJECT_PROP_CHANGED = 0xC801, MTP_EVT_OBJECT_PROP_DESC_CHANGED = 0xC802, MTP_EVT_OBJECT_REFERENCES_CHANGED = 0xC803, } mtp_event_code_t; typedef enum mtp_device_property_code { MTP_DP_UNDEFINED = 0x5000, MTP_DP_BATTERY_LEVEL = 0x5001, MTP_DP_FUNCTIONAL_MODE = 0x5002, MTP_DP_IMAGE_SIZE = 0x5003, MTP_DP_COMPRESSION_SETTING = 0x5004, MTP_DP_WHITE_BALANCE = 0x5005, MTP_DP_RGB_GAIN = 0x5006, MTP_DP_F_NUMBER = 0x5007, MTP_DP_FOCAL_LENGTH = 0x5008, MTP_DP_FOCUS_DISTANCE = 0x5009, MTP_DP_FOCUS_MODE = 0x500A, MTP_DP_EXPOSURE_METERING_MODE = 0x500B, MTP_DP_FLASH_MODE = 0x500C, MTP_DP_EXPOSURE_TIME = 0x500D, MTP_DP_EXPOSURE_PROGRAM_MODE = 0x500E, MTP_DP_EXPOSURE_INDEX = 0x500F, MTP_DP_EXPOSURE_BIAS_COMPENSATION = 0x5010, MTP_DP_DATE_TIME = 0x5011, MTP_DP_CAPTURE_DELAY = 0x5012, MTP_DP_STILL_CAPTURE_MODE = 0x5013, MTP_DP_CONTRAST = 0x5014, MTP_DP_SHARPNESS = 0x5015, MTP_DP_DIGITAL_ZOOM = 0x5016, MTP_DP_EFFECT_MODE = 0x5017, MTP_DP_BURST_NUMBER = 0x5018, MTP_DP_BURST_INTERVAL = 0x5019, MTP_DP_TIMELAPSE_NUMBER = 0x501A, MTP_DP_TIMELAPSE_INTERVAL = 0x501B, MTP_DP_FOCUS_METERING_MODE = 0x501C, MTP_DP_UPLOAD_URL = 0x501D, MTP_DP_ARTIST = 0x501E, MTP_DP_COPYRIGHT_INFO = 0x501F, MTP_DP_SYNCHRONIZATION_PARTNER = 0xD401, MTP_DP_DEVICE_FRIENDLY_NAME = 0xD402, MTP_DP_VOLUME = 0xD403, MTP_DP_SUPPORTED_FORMATSORDERED = 0xD404, MTP_DP_DEVICE_ICON = 0xD405, MTP_DP_PLAYBACK_RATE = 0xD410, MTP_DP_PLAYBACK_OBJECT = 0xD411, MTP_DP_PLAYBACK_CONTAINER_INDEX = 0xD412, MTP_DP_SESSION_INITIATOR_VERSION_INFO = 0xD406, MTP_DP_PERCEIVED_DEVICE_TYPE = 0xD407, } mtp_device_property_code_t; typedef enum mtp_object_property_code { MTP_OP_STORAGE_ID = 0xDC01, MTP_OP_OBJECT_FORMAT = 0xDC02, MTP_OP_PROTECTION_STATUS = 0xDC03, MTP_OP_OBJECT_SIZE = 0xDC04, MTP_OP_ASSOCIATION_TYPE = 0xDC05, MTP_OP_ASSOCIATION_DESC = 0xDC06, MTP_OP_OBJECT_FILE_NAME = 0xDC07, MTP_OP_DATE_CREATED = 0xDC08, MTP_OP_DATE_MODIFIED = 0xDC09, MTP_OP_KEYWORDS = 0xDC0A, MTP_OP_PARENT_OBJECT = 0xDC0B, MTP_OP_ALLOWED_FOLDER_CONTENTS = 0xDC0C, MTP_OP_HIDDEN = 0xDC0D, MTP_OP_SYSTEM_OBJECT = 0xDC0E, MTP_OP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER = 0xDC41, MTP_OP_SYNC_ID = 0xDC42, MTP_OP_PROPERTY_BAG = 0xDC43, MTP_OP_NAME = 0xDC44, MTP_OP_CREATED_BY = 0xDC45, MTP_OP_ARTIST = 0xDC46, MTP_OP_DATE_AUTHORED = 0xDC47, MTP_OP_DESCRIPTION = 0xDC48, MTP_OP_URL_REFERENCE = 0xDC49, MTP_OP_LANGUAGE_LOCALE = 0xDC4A, MTP_OP_COPYRIGHT_INFORMATION = 0xDC4B, MTP_OP_SOURCE = 0xDC4C, MTP_OP_ORIGIN_LOCATION = 0xDC4D, MTP_OP_DATE_ADDED = 0xDC4E, MTP_OP_NON_CONSUMABLE = 0xDC4F, MTP_OP_CORRUPT_UNPLAYABLE = 0xDC50, MTP_OP_PRODUCER_SERIAL_NUMBER = 0xDC51, MTP_OP_REPRESENTATIVE_SAMPLE_FORMAT = 0xDC81, MTP_OP_REPRESENTATIVE_SAMPLE_SIZE = 0xDC82, MTP_OP_REPRESENTATIVE_SAMPLE_HEIGHT = 0xDC83, MTP_OP_REPRESENTATIVE_SAMPLE_WIDTH = 0xDC84, MTP_OP_REPRESENTATIVE_SAMPLE_DURATION = 0xDC85, MTP_OP_REPRESENTATIVE_SAMPLE_DATA = 0xDC86, MTP_OP_WIDTH = 0xDC87, MTP_OP_HEIGHT = 0xDC88, MTP_OP_DURATION = 0xDC89, MTP_OP_RATING = 0xDC8A, MTP_OP_TRACK = 0xDC8B, MTP_OP_GENRE = 0xDC8C, MTP_OP_CREDITS = 0xDC8D, MTP_OP_LYRICS = 0xDC8E, MTP_OP_SUBSCRIPTION_CONTENT_ID = 0xDC8F, MTP_OP_PRODUCED_BY = 0xDC90, MTP_OP_USE_COUNT = 0xDC91, MTP_OP_SKIP_COUNT = 0xDC92, MTP_OP_LAST_ACCESSED = 0xDC93, MTP_OP_PARENTAL_RATING = 0xDC94, MTP_OP_META_GENRE = 0xDC95, MTP_OP_COMPOSER = 0xDC96, MTP_OP_EFFECTIVE_RATING = 0xDC97, MTP_OP_SUBTITLE = 0xDC98, MTP_OP_ORIGINAL_RELEASE_DATE = 0xDC99, MTP_OP_ALBUM_NAME = 0xDC9A, MTP_OP_ALBUM_ARTIST = 0xDC9B, MTP_OP_MOOD = 0xDC9C, MTP_OP_DRM_STATUS = 0xDC9D, MTP_OP_SUB_DESCRIPTION = 0xDC9E, MTP_OP_IS_CROPPED = 0xDCD1, MTP_OP_IS_COLOUR_CORRECTED = 0xDCD2, MTP_OP_IMAGE_BIT_DEPTH = 0xDCD3, MTP_OP_F_NUMBER = 0xDCD4, MTP_OP_EXPOSURE_TIME = 0xDCD5, MTP_OP_EXPOSURE_INDEX = 0xDCD6, MTP_OP_TOTAL_BIT_RATE = 0xDE91, MTP_OP_BIT_RATE_TYPE = 0xDE92, MTP_OP_SAMPLE_RATE = 0xDE93, MTP_OP_NUMBER_OF_CHANNELS = 0xDE94, MTP_OP_AUDIO_BIT_DEPTH = 0xDE95, MTP_OP_SCAN_TYPE = 0xDE97, MTP_OP_AUDIO_WAVE_CODEC = 0xDE99, MTP_OP_AUDIO_BIT_RATE = 0xDE9A, MTP_OP_VIDEO_FOURCC_CODEC = 0xDE9B, MTP_OP_VIDEO_BIT_RATE = 0xDE9C, MTP_OP_FRAMES_PER_THOUSAND_SECONDS = 0xDE9D, MTP_OP_KEY_FRAME_DISTANCE = 0xDE9E, MTP_OP_BUFFER_SIZE = 0xDE9F, MTP_OP_ENCODING_QUALITY = 0xDEA0, MTP_OP_ENCODING_PROFILE = 0xDEA1, MTP_OP_DISPLAY_NAME = 0xDCE0, MTP_OP_BODY_TEXT = 0xDCE1, MTP_OP_SUBJECT = 0xDCE2, MTP_OP_PRIORITY = 0xDCE3, MTP_OP_GIVEN_NAME = 0xDD00, MTP_OP_MIDDLE_NAMES = 0xDD01, MTP_OP_FAMILY_NAME = 0xDD02, MTP_OP_PREFIX = 0xDD03, MTP_OP_SUFFIX = 0xDD04, MTP_OP_PHONETIC_GIVEN_NAME = 0xDD05, MTP_OP_PHONETIC_FAMILY_NAME = 0xDD06, MTP_OP_EMAIL_PRIMARY = 0xDD07, MTP_OP_EMAIL_PERSONAL_1 = 0xDD08, MTP_OP_EMAIL_PERSONAL_2 = 0xDD09, MTP_OP_EMAIL_BUSINESS_1 = 0xDD0A, MTP_OP_EMAIL_BUSINESS_2 = 0xDD0B, MTP_OP_EMAIL_OTHERS = 0xDD0C, MTP_OP_PHONE_NUMBER_PRIMARY = 0xDD0D, MTP_OP_PHONE_NUMBER_PERSONAL = 0xDD0E, MTP_OP_PHONE_NUMBER_PERSONAL_2 = 0xDD0F, MTP_OP_PHONE_NUMBER_BUSINESS = 0xDD10, MTP_OP_PHONE_NUMBER_BUSINESS_2 = 0xDD11, MTP_OP_PHONE_NUMBER_MOBILE = 0xDD12, MTP_OP_PHONE_NUMBER_MOBILE_2 = 0xDD13, MTP_OP_FAX_NUMBER_PRIMARY = 0xDD14, MTP_OP_FAX_NUMBER_PERSONAL = 0xDD15, MTP_OP_FAX_NUMBER_BUSINESS = 0xDD16, MTP_OP_PAGER_NUMBER = 0xDD17, MTP_OP_PHONE_NUMBER_OTHERS = 0xDD18, MTP_OP_PRIMARY_WEB_ADDRESS = 0xDD19, MTP_OP_PERSONAL_WEB_ADDRESS = 0xDD1A, MTP_OP_BUSINESS_WEB_ADDRESS = 0xDD1B, MTP_OP_INSTANT_MESSENGER_ADDRESS = 0xDD1C, MTP_OP_INSTANT_MESSENGER_ADDRESS_2 = 0xDD1D, MTP_OP_INSTANT_MESSENGER_ADDRESS_3 = 0xDD1E, MTP_OP_POSTAL_ADDRESS_PERSONAL_FULL = 0xDD1F, MTP_OP_POSTAL_ADDRESS_PERSONAL_LINE_1 = 0xDD20, MTP_OP_POSTAL_ADDRESS_PERSONAL_LINE_2 = 0xDD21, MTP_OP_POSTAL_ADDRESS_PERSONAL_CITY = 0xDD22, MTP_OP_POSTAL_ADDRESS_PERSONAL_REGION = 0xDD23, MTP_OP_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE = 0xDD24, MTP_OP_POSTAL_ADDRESS_PERSONAL_COUNTRY = 0xDD25, MTP_OP_POSTAL_ADDRESS_BUSINESS_FULL = 0xDD26, MTP_OP_POSTAL_ADDRESS_BUSINESS_LINE_1 = 0xDD27, MTP_OP_POSTAL_ADDRESS_BUSINESS_LINE_2 = 0xDD28, MTP_OP_POSTAL_ADDRESS_BUSINESS_CITY = 0xDD29, MTP_OP_POSTAL_ADDRESS_BUSINESS_REGION = 0xDD2A, MTP_OP_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE = 0xDD2B, MTP_OP_POSTAL_ADDRESS_BUSINESS_COUNTRY = 0xDD2C, MTP_OP_POSTAL_ADDRESS_OTHER_FULL = 0xDD2D, MTP_OP_POSTAL_ADDRESS_OTHER_LINE_1 = 0xDD2E, MTP_OP_POSTAL_ADDRESS_OTHER_LINE_2 = 0xDD2F, MTP_OP_POSTAL_ADDRESS_OTHER_CITY = 0xDD30, MTP_OP_POSTAL_ADDRESS_OTHER_REGION = 0xDD31, MTP_OP_POSTAL_ADDRESS_OTHER_POSTAL_CODE = 0xDD32, MTP_OP_POSTAL_ADDRESS_OTHER_COUNTRY = 0xDD33, MTP_OP_ORGANIZATION_NAME = 0xDD34, MTP_OP_PHONETIC_ORGANIZATION_NAME = 0xDD35, MTP_OP_ROLE = 0xDD36, MTP_OP_BIRTHDATE = 0xDD37, MTP_OP_MESSAGE_TO = 0xDD40, MTP_OP_MESSAGE_CC = 0xDD41, MTP_OP_MESSAGE_BCC = 0xDD42, MTP_OP_MESSAGE_READ = 0xDD43, MTP_OP_MESSAGE_RECEIVED_TIME = 0xDD44, MTP_OP_MESSAGE_SENDER = 0xDD45, MTP_OP_ACTIVITY_BEGIN_TIME = 0xDD50, MTP_OP_ACTIVITY_END_TIME = 0xDD51, MTP_OP_ACTIVITY_LOCATION = 0xDD52, MTP_OP_ACTIVITY_REQUIRED_ATTENDEES = 0xDD54, MTP_OP_ACTIVITY_OPTIONAL_ATTENDEES = 0xDD55, MTP_OP_ACTIVITY_RESOURCES = 0xDD56, MTP_OP_ACTIVITY_ACCEPTED = 0xDD57, MTP_OP_ACTIVITY_TENTATIVE = 0xDD58, MTP_OP_ACTIVITY_DECLINED = 0xDD59, MTP_OP_ACTIVITY_REMINDER_TIME = 0xDD5A, MTP_OP_ACTIVITY_OWNER = 0xDD5B, MTP_OP_ACTIVITY_STATUS = 0xDD5C, MTP_OP_OWNER = 0xDD5D, MTP_OP_EDITOR = 0xDD5E, MTP_OP_WEBMASTER = 0xDD5F, MTP_OP_URL_SOURCE = 0xDD60, MTP_OP_URL_DESTINATION = 0xDD61, MTP_OP_TIME_BOOKMARK = 0xDD62, MTP_OP_OBJECT_BOOKMARK = 0xDD63, MTP_OP_BYTE_BOOKMARK = 0xDD64, MTP_OP_LAST_BUILD_DATE = 0xDD70, MTP_OP_TIME_TO_LIVE = 0xDD71, MTP_OP_MEDIA_GUID = 0xDD72, } mtp_object_property_code_t; /* MTP Structures */ typedef struct mtp_object_info_header { mtp_trunc_id_t storage_id; mtp_enum_t object_format; mtp_enum_t protection_status; mtp_size_t object_compressed_size; mtp_enum_t thumb_format; mtp_size_t thumb_compressed_size; mtp_size_t thumb_pix_width; mtp_size_t thumb_pix_height; mtp_size_t image_pix_width; mtp_size_t image_pix_height; mtp_size_t image_bit_depth; mtp_id_t parent_object; mtp_enum_t association_type; mtp_id_t association_description; mtp_size_t sequence_number; } mtp_object_info_header_t; #define DECLARE_OBJECT_INFO(name, file_name_alloc) \ typedef struct mtp_object_info_##name { \ mtp_object_info_header_t header; \ mtp_byte_t file_name_length; \ wchar_t file_name[file_name_alloc]; \ mtp_byte_t date_created_length; \ wchar_t date_created[0]; \ mtp_byte_t date_modified_length; \ wchar_t date_modified[0]; \ mtp_byte_t keywords_length; \ wchar_t keywords[0]; \ } mtp_object_info_##name##_t; DECLARE_OBJECT_INFO(min, 0) DECLARE_OBJECT_INFO(max, MAX_FILE_NAME_LENGTH) #define DECLARE_STORAGE_INFO_TYPE(name) \ typedef struct name##_mtp_storage_info { \ mtp_enum_t storage_type; \ mtp_enum_t filesystem_type; \ mtp_enum_t access_capability; \ mtp_trunc_uint64_t max_capacity; \ mtp_trunc_uint64_t free_space_in_bytes; \ mtp_size_t free_space_in_objects; \ mtp_byte_t storage_description_length; \ wchar_t storage_description[ \ lengthof(L##name##_storage_desc)]; \ mtp_byte_t volume_identifier_length; \ wchar_t volume_identifier[ \ lengthof(L##name##_volume_id)]; \ } name##_mtp_storage_info_t; FOR_EACH_STORAGE(DECLARE_STORAGE_INFO_TYPE) typedef struct mtp_device_info { mtp_version_t standard_version; mtp_id_t mtp_vendor_extension_id; mtp_version_t mtp_version; mtp_byte_t mtp_extensions_length; wchar_t mtp_extensions[lengthof(Lmtp_extensions)]; mtp_enum_t functional_mode; mtp_size_t operations_supported_length; mtp_enum_t operations_supported[0 FOR_EACH_SUPP_OPR(COUNT_EACH)]; mtp_size_t events_supported_length; mtp_enum_t events_supported[0 FOR_EACH_SUPP_EVT(COUNT_EACH)]; mtp_size_t device_properties_length; mtp_enum_t device_properties[0 FOR_EACH_SUPP_DP(COUNT_EACH)]; mtp_size_t capture_formats_length; mtp_enum_t capture_formats[0 FOR_EACH_SUPP_CF(COUNT_EACH)]; mtp_size_t playback_formats_length; mtp_enum_t playback_formats[0 FOR_EACH_SUPP_PF(COUNT_EACH)]; mtp_byte_t manufacturer_length; wchar_t manufacturer[lengthof(Lmanufacturer)]; mtp_byte_t model_length; wchar_t model[lengthof(Lproduct)]; mtp_byte_t device_version_length; wchar_t device_version[lengthof(Ldevice_version)]; mtp_byte_t serial_number_length; wchar_t serial_number[lengthof(Lserial_number Lserial_number)]; } mtp_device_info_t; typedef struct mtp_container { mtp_trunc_size_t length; mtp_enum_t type, code; mtp_id_t transaction; } mtp_container_t; typedef union mtp_transaction_payload { mtp_param_t params[MTP_MAX_PARAMS]; mtp_byte_t buffer[MTP_MAX_BULK_PKT_SZ]; mtp_size_t count; mtp_id_t handles[MTP_MAX_BULK_PKT_SZ / sizeof(mtp_id_t)]; mtp_object_info_header_t object_info; } mtp_transaction_payload_t; enum mtp_send_object_info_state { SEND_OBJECT_INFO_CONTAINER_STATE, SEND_OBJECT_INFO_DATA_STATE, SEND_OBJECT_INFO_REST_STATE, } mtp_send_object_info_state_t; typedef union mtp_transaction_state { struct { size_t id; mtp_byte_t mask; mtp_byte_t flag; } get_object_handles; struct { mtp_byte_t *data; size_t remaining; unsigned checksum; } get_object; struct { mtp_byte_t state; mtp_enum_t response; mtp_param_t params[MTP_MAX_SOI_PARAMS]; } send_object_info; struct { mtp_byte_t extra; mtp_enum_t response; } send_object; } mtp_transaction_state_t; typedef struct mtp_transaction_pending { struct { mtp_byte_t flag; mtp_byte_t mask; size_t handle; } send_object; } mtp_transaction_pending_t; typedef struct mtp_transaction { mtp_container_t container; mtp_transaction_payload_t payload; mtp_transaction_state_t state; mtp_transaction_pending_t pending; } mtp_transaction_t; typedef struct mtp_event { mtp_container_t container; mtp_param_t params[MTP_MAX_EVT_PARAMS]; } mtp_event_t; struct mtp_global { /* Other State */ bool exiting; /* MTP State */ bool reset; mtp_id_t session; mtp_transaction_t transaction; mtp_event_t events[MTP_MAX_PENDING_EVENTS]; /* MTP Info */ size_t device_info_size; const mtp_device_info_t *device_info; #define DECLARE_STORAGE_INFO(name) \ name##_mtp_storage_info_t *name##_storage_info; FOR_EACH_STORAGE(DECLARE_STORAGE_INFO) /* Name List */ #define DECLARE_STORAGE_NAME_COUNT(name) \ mtp_trunc_param_t name##_name_count; FOR_EACH_STORAGE_NONE_TOTAL(DECLARE_STORAGE_NAME_COUNT) size_t max_name_id, free_name; var_name_t var_names[5000]; }; /* MTP Forward Function Declarations */ #define DECLARE_CALLBACK(name) \ static usb_error_t name##_complete( \ usb_endpoint_t endpoint, \ usb_transfer_status_t status, \ size_t transferred, \ mtp_global_t *global) DECLARE_CALLBACK(control); DECLARE_CALLBACK(command); DECLARE_CALLBACK(get_object_handles); DECLARE_CALLBACK(get_object); DECLARE_CALLBACK(send_object_info); DECLARE_CALLBACK(send_object_container); DECLARE_CALLBACK(send_object); DECLARE_CALLBACK(final_data_in); DECLARE_CALLBACK(response); DECLARE_CALLBACK(event); /* Other Forward Function Declarations */ usb_error_t wait_for_usb(mtp_global_t *global); /* MTP Function Definitions */ static usb_endpoint_t get_endpoint( usb_endpoint_t endpoint, mtp_byte_t address) { return usb_GetDeviceEndpoint( usb_GetEndpointDevice(endpoint), address); } static usb_error_t stall_data_endpoints( usb_endpoint_t endpoint) { printf("stalling data endpoints\n"); #if 0 usb_error_t error; error = usb_StallEndpoint( get_endpoint(endpoint, MTP_EP_DATA_IN)); if (error == USB_SUCCESS) error = usb_StallEndpoint( get_endpoint(endpoint, MTP_EP_DATA_OUT)); return error; #else (void)endpoint; return USB_ERROR_FAILED; #endif } static usb_error_t schedule_event( usb_endpoint_t endpoint, mtp_event_code_t code, mtp_param_t *params, mtp_byte_t param_count, mtp_global_t *global) { usb_error_t error = USB_SUCCESS; bool notified = false; while (!global->exiting && error == USB_SUCCESS) { for (mtp_byte_t i = 0; i != MTP_MAX_PENDING_EVENTS; ++i) { if (global->events[i].container.length.byte) continue; mtp_byte_t params_size = param_count * sizeof(mtp_param_t); mtp_byte_t block_size = sizeof(mtp_container_t) + params_size; global->events[i].container.length.byte = block_size; global->events[i].container.code = code; global->events[i].container.transaction = global->transaction.container.transaction; memcpy(global->events[i].params, params, params_size); return usb_ScheduleInterruptTransfer( get_endpoint(endpoint, MTP_EP_INT), &global->events[i], block_size, event_complete, (mtp_global_t *)&global->events[i]); } if (!notified) { printf("event queue full"); notified = true; } error = wait_for_usb(global); } return error; } static usb_error_t schedule_command( usb_endpoint_t endpoint, mtp_global_t *global) { global->transaction.container.transaction = 0; return usb_ScheduleBulkTransfer( get_endpoint( endpoint, MTP_EP_DATA_OUT), &global->transaction, MTP_MAX_BULK_PKT_SZ, command_complete, global); } static usb_error_t schedule_response( usb_endpoint_t endpoint, mtp_enum_t code, mtp_param_t *params, mtp_byte_t param_count, mtp_global_t *global) { mtp_byte_t params_size = param_count * sizeof(mtp_param_t); mtp_byte_t container_size = sizeof(mtp_container_t) + params_size; global->transaction.container.length.size = container_size; global->transaction.container.type = MTP_BT_RESPONSE; global->transaction.container.code = code; memcpy(global->transaction.payload.params, params, params_size); if (code != MTP_RSP_OK) printf("response: %04X\n", code); return usb_ScheduleBulkTransfer( get_endpoint(endpoint, MTP_EP_DATA_IN), &global->transaction, container_size, response_complete, global); } static usb_error_t schedule_error_response( usb_endpoint_t endpoint, mtp_enum_t code, mtp_global_t *global) { return schedule_response( endpoint, code, NULL, 0, global); } static usb_error_t schedule_ok_response( usb_endpoint_t endpoint, mtp_param_t *params, mtp_byte_t param_count, mtp_global_t *global) { return schedule_response( endpoint, MTP_RSP_OK, params, param_count, global); } static usb_error_t schedule_data_in_response( usb_endpoint_t endpoint, const void *data, size_t data_size, mtp_global_t *global) { usb_error_t error; global->transaction.container.length.size = sizeof(mtp_container_t) + data_size; global->transaction.container.type = MTP_BT_DATA; error = usb_ScheduleBulkTransfer( endpoint = get_endpoint( endpoint, MTP_EP_DATA_IN), &global->transaction, sizeof(mtp_container_t), NULL, global); if (error) return error; return usb_ScheduleBulkTransfer( endpoint, (void *)data, data_size, final_data_in_complete, global); } static usb_error_t schedule_data_in( usb_endpoint_t endpoint, size_t data_size, usb_transfer_callback_t complete, mtp_global_t *global) { global->transaction.container.length.size = sizeof(mtp_container_t) + data_size; global->transaction.container.type = MTP_BT_DATA; return usb_ScheduleBulkTransfer( get_endpoint(endpoint, MTP_EP_DATA_IN), &global->transaction, sizeof(mtp_container_t), complete, global); } static usb_error_t status_error( usb_transfer_status_t status) { printf("transfer status = %02X\n", status); return USB_SUCCESS; } static mtp_id_t alloc_object_handle( mtp_global_t *global) { mtp_id_t handle = global->free_name; if (handle) global->free_name = global->var_names[handle - 1].next; else if ((handle = ++global->max_name_id) > lengthof(global->var_names)) return --global->max_name_id, 0; return handle; } static void free_object_handle( mtp_id_t handle, var_name_t *var_name, mtp_global_t *global) { if (!(var_name->type & reserved_flag)) { --*(var_name->type & storage_flag ? &global->arc_name_count.word : &global->ram_name_count.word); --global->total_name_count.word; } var_name->type = 0; var_name->valid = '\0'; var_name->next = global->free_name; global->free_name = handle; } static var_name_t *lookup_object_handle( mtp_id_t handle, mtp_global_t *global) { if (!handle || handle > global->max_name_id) return NULL; var_name_t *var_name = &global->var_names[handle - 1]; return var_name->valid ? var_name : NULL; } static uint16_t compute_checksum( const void *data, size_t size) { unsigned sum = 0; const uint8_t *pointer = data; while (size--) sum += *pointer++; return sum; } static void get_datetime( wchar_t result[lengthof(Lfactory_datetime)]) { uint16_t year; uint8_t month, day, hour, minute, second, i = lengthof(Lfactory_datetime); char string[lengthof(Lfactory_datetime)], *pointer = string; boot_GetDate(&day, &month, &year); boot_GetTime(&second, &minute, &hour); sprintf(string, "%04u%02u%02uT%02u%02u%02u", year, month, day, hour, minute, second); do { *(char *)result = *pointer; ++result; ++pointer; } while (--i); } static int delete_object( usb_endpoint_t endpoint, mtp_id_t handle, mtp_global_t *global) { var_name_t *var_name = lookup_object_handle(handle, global); if (!var_name) return MTP_RSP_INVALID_OBJECT_HANDLE; if (var_name->type & reserved_flag) { free_object_handle( handle, var_name, global); return MTP_RSP_OK; } switch (delete_var(var_name)) { case DELETE_VAR_NOT_DELETED: return MTP_RSP_ACCESS_DENIED; case DELETE_VAR_DELETED: case DELETE_VAR_TRUNCATED: free_object_handle( handle, var_name, global); break; case DELETE_VAR_ZEROED: { usb_error_t error = schedule_event( endpoint, MTP_EVT_OBJECT_ADDED, &handle, 1, global); if (error != USB_SUCCESS) return error; break; } } return MTP_RSP_OK; } static int send_object( usb_endpoint_t endpoint, mtp_id_t handle, size_t size, mtp_global_t *global) { size += global->transaction.state .send_object.extra; const var_file_header_t *header = OBJECT_BUFFER; if (size <= offsetof(var_file_header_t, entry.data) + sizeof(uint16_t) || memcmp(header->signature, VAR_FILE_SIGNATURE, sizeof(header->signature))) return MTP_RSP_INCOMPLETE_TRANSFER; const mtp_byte_t *pointer = (const mtp_byte_t *)&header->entry; size -= offsetof(var_file_header_t, entry) + sizeof(uint16_t); if (header->file_data_length != size || compute_checksum(pointer, size) != *(uint16_t *)&pointer[size]) return MTP_RSP_INCOMPLETE_TRANSFER; size_t count = 0; while (size) { const var_entry_t *entry = (const var_entry_t *)pointer; if (size < offsetof(var_entry_t, data) - sizeof(entry->version) - sizeof(entry->flag) || entry->var_data_length_1 < 2) return MTP_RSP_INCOMPLETE_TRANSFER; size_t entry_size = sizeof(entry->var_header_length) + sizeof(entry->var_data_length_1) + entry->var_header_length + entry->var_data_length_1; switch (entry->var_header_length) { case offsetof(var_entry_t, data) - sizeof(entry->var_header_length) - sizeof(entry->var_data_length_1) - sizeof(entry->version) - sizeof(entry->flag): break; case offsetof(var_entry_t, data) - sizeof(entry->var_header_length) - sizeof(entry->var_data_length_1): if (entry->flag & ~storage_flag) return MTP_RSP_INCOMPLETE_TRANSFER; break; default: return MTP_RSP_INCOMPLETE_TRANSFER; } if (size < entry_size || *(const uint16_t *)&pointer[ sizeof(entry->var_header_length) + sizeof(entry->var_data_length_1) + entry->var_header_length - sizeof(entry->var_data_length_2)] != entry->var_data_length_1) return MTP_RSP_INCOMPLETE_TRANSFER; pointer += entry_size; size -= entry_size; ++count; } pointer = (const mtp_byte_t *)&header->entry; bool first = true; do { const var_entry_t *entry = (const var_entry_t *)pointer; pointer += sizeof(entry->var_header_length) + sizeof(entry->var_data_length_1) + entry->var_header_length; mtp_byte_t version = 0, flag = 0; if (entry->var_header_length == offsetof(var_entry_t, data) - sizeof(entry->var_header_length) - sizeof(entry->var_data_length_1)) { version = entry->version; flag = entry->flag; } flag &= global->transaction.pending .send_object.mask; flag |= global->transaction.pending .send_object.flag; usb_error_t error; switch (create_var(&entry->var_name, pointer, entry->var_data_length_1)) { case CREATE_VAR_NOT_CREATED: break; case CREATE_VAR_RECREATED: for (mtp_id_t id = 0; id != global->max_name_id; ) { var_name_t *var_name = &global->var_names[id++]; if (!(var_name->type & reserved_flag) && !var_name_cmp( &entry->var_name, var_name)) { free_object_handle( id, var_name, global); if ((error = schedule_event( endpoint, MTP_EVT_OBJECT_REMOVED, &id, 1, global)) != USB_SUCCESS) return error; break; } } __attribute__((fallthrough)); case CREATE_VAR_CREATED: { if (first) first = false; /* TODO: maybe update info? */ else if (!(handle = alloc_object_handle( global))) return MTP_RSP_OK; else if ((error = schedule_event( endpoint, MTP_EVT_OBJECT_ADDED, &handle, 1, global)) != USB_SUCCESS) return error; var_name_t *var_name = &global->var_names[handle - 1]; --*(var_name->type & storage_flag ? &global->arc_name_count.word : &global->ram_name_count.word); memcpy(var_name, &entry->var_name, sizeof(entry->var_name)); var_name->type &= type_mask; if (flag && !arc_unarc_var(var_name)) var_name->type |= flag; ++*(var_name->type & storage_flag ? &global->arc_name_count.word : &global->ram_name_count.word); break; } } pointer += entry->var_data_length_1; } while (--count); return first ? MTP_RSP_INCOMPLETE_TRANSFER : MTP_RSP_OK; } #define DEFINE_CALLBACK(name) \ static usb_error_t name##_complete( \ usb_endpoint_t endpoint, \ usb_transfer_status_t status, \ size_t transferred, \ mtp_global_t *global) DEFINE_CALLBACK(control) { (void)endpoint; (void)transferred; (void)global; if (status != USB_TRANSFER_COMPLETED) return status_error(status); return USB_SUCCESS; } DEFINE_CALLBACK(command) { if (status != USB_TRANSFER_COMPLETED) return status_error(status); global->reset = false; if (transferred < sizeof(mtp_container_t) || global->transaction.container .length.size != transferred || global->transaction.container.type != MTP_BT_COMMAND) return stall_data_endpoints(endpoint); if (!global->session && global->transaction.container.code > MTP_OPR_OPEN_SESSION) return schedule_error_response( endpoint, MTP_RSP_SESSION_NOT_OPEN, global); mtp_byte_t params_size = transferred - sizeof(mtp_container_t); if (params_size % sizeof(mtp_param_t) || params_size > MTP_MAX_PARAMS * sizeof(mtp_param_t)) return stall_data_endpoints(endpoint); memset(&global->transaction.payload.params[ params_size / sizeof(mtp_param_t)], 0, MTP_MAX_PARAMS * sizeof(mtp_param_t) - params_size); size_t pending_handle = global->transaction.pending .send_object.handle; global->transaction.pending .send_object.handle = 0; switch (global->transaction.container.code) { #define MAX_PARAMS(max) \ do \ for (mtp_byte_t i = max; i != MTP_MAX_PARAMS; ++i) \ if (global->transaction.payload.params[i]) \ return schedule_error_response( \ endpoint, \ MTP_RSP_PARAMETER_NOT_SUPPORTED, \ global); \ while (0) case MTP_OPR_GET_DEVICE_INFO: MAX_PARAMS(0); return schedule_data_in_response( endpoint, global->device_info, global->device_info_size, global); case MTP_OPR_OPEN_SESSION: MAX_PARAMS(1); if (!global->transaction.payload.params[0]) return schedule_error_response( endpoint, MTP_RSP_INVALID_PARAMETER, global); if (global->session) return schedule_response( endpoint, MTP_RSP_SESSION_ALREADY_OPEN, &global->session, 1, global); global->session = global->transaction.payload.params[0]; return schedule_ok_response( endpoint, NULL, 0, global); case MTP_OPR_CLOSE_SESSION: MAX_PARAMS(0); global->session = 0; return schedule_ok_response( endpoint, NULL, 0, global); case MTP_OPR_GET_STORAGE_IDS: { static const mtp_id_t storage_ids[] = { storage_count, #define LIST_STORAGE_ID(name) I##name, FOR_EACH_STORAGE(LIST_STORAGE_ID) }; MAX_PARAMS(0); return schedule_data_in_response( endpoint, storage_ids, sizeof(storage_ids), global); } case MTP_OPR_GET_STORAGE_INFO: MAX_PARAMS(1); #define GET_STORAGE_INFO_RESPONSE(name) \ if (global->transaction.payload.params[0] == I##name) { \ global->name##_storage_info->free_space_in_bytes.word = \ get_##name##_free_space_in_bytes; \ return schedule_data_in_response( \ endpoint, global->name##_storage_info, \ sizeof(name##_mtp_storage_info_t), global); \ } FOR_EACH_STORAGE(GET_STORAGE_INFO_RESPONSE) return schedule_error_response( endpoint, MTP_RSP_INVALID_STORAGE_ID, global); case MTP_OPR_GET_NUM_OBJECTS: MAX_PARAMS(3); if (global->transaction.payload.params[1] && global->transaction.payload.params[1] != MTP_OF_UNDEFINED) return schedule_ok_response( endpoint, &global->none_name_count.param, 1, global); if (global->transaction.payload.params[2] && global->transaction.payload.params[2] != MTP_ROOT_OBJECT_HANDLE) return schedule_error_response( endpoint, lookup_object_handle( global->transaction.payload.params[2], global) ? MTP_RSP_INVALID_PARENT_OBJECT : MTP_RSP_INVALID_OBJECT_HANDLE, global); #define GET_NUM_OBJECTS_RESPONSE(name) \ if (global->transaction.payload.params[0] == \ I##name) \ return schedule_ok_response( \ endpoint, \ &global->name##_name_count.param, \ 1, global); FOR_EACH_STORAGE_TOTAL(GET_NUM_OBJECTS_RESPONSE) return schedule_error_response( endpoint, MTP_RSP_INVALID_STORAGE_ID, global); case MTP_OPR_GET_OBJECT_HANDLES: { MAX_PARAMS(3); global->transaction.state.get_object_handles.id = 0; if (global->transaction.payload.params[2] && global->transaction.payload.params[2] != MTP_ROOT_OBJECT_HANDLE) return schedule_error_response( endpoint, lookup_object_handle( global->transaction.payload.params[2], global) ? MTP_RSP_INVALID_PARENT_OBJECT : MTP_RSP_INVALID_OBJECT_HANDLE, global); #define GET_OBJECT_HANDLES_RESPONSE(name) \ if (global->transaction.payload.params[0] == \ I##name) { \ size_t name_count = \ global->name##_name_count.word; \ global->transaction.state \ .get_object_handles.mask = name##_mask; \ global->transaction.state \ .get_object_handles.flag = name##_flag; \ if (global->transaction.payload.params[1] && \ global->transaction.payload.params[1] != \ MTP_OF_UNDEFINED) { \ name_count = 0; \ global->transaction.state. \ get_object_handles.mask = none_mask; \ global->transaction.state. \ get_object_handles.flag = none_flag; \ } \ global->transaction.payload.count = \ name_count; \ return schedule_data_in( \ endpoint, sizeof(mtp_size_t) + \ name_count * sizeof(mtp_id_t), \ get_object_handles_complete, \ global); \ } FOR_EACH_STORAGE_TOTAL(GET_OBJECT_HANDLES_RESPONSE) return schedule_error_response( endpoint, MTP_RSP_INVALID_STORAGE_ID, global); } case MTP_OPR_GET_OBJECT_INFO: { static mtp_object_info_max_t object_info; MAX_PARAMS(1); var_name_t *var_name = lookup_object_handle( global->transaction.payload.params[0], global); if (!var_name) return schedule_error_response( endpoint, MTP_RSP_INVALID_OBJECT_HANDLE, global); object_info.header.storage_id.word = var_name->type & storage_flag ? Iarc : Iram; object_info.header.object_format = MTP_OF_UNDEFINED; object_info.header.object_compressed_size = 0; mtp_byte_t file_name_length = 0; memset(object_info.file_name, 0, sizeof(object_info.file_name)); if (!(var_name->type & reserved_flag)) { object_info.header.object_compressed_size = offsetof(var_file_header_t, entry.data) + get_var_data_size(var_name) + sizeof(uint16_t); file_name_length = get_var_file_name( var_name, object_info.file_name); } object_info.file_name_length = file_name_length; return schedule_data_in_response( endpoint, &object_info, sizeof(object_info) - sizeof(object_info.file_name) + file_name_length * sizeof(*object_info.file_name), global); } case MTP_OPR_GET_OBJECT: { static var_file_header_t header = { .signature = VAR_FILE_SIGNATURE, .global_version = 0, .comment = VAR_FILE_COMMENT, .file_data_length = 0, .entry = { .var_header_length = offsetof(var_file_header_t, entry.var_data_length_2) - offsetof(var_file_header_t, entry.var_data_length_1), .var_data_length_1 = 0, .var_name = { .type = 0, .name = "", }, .version = 0, .flag = 0, .var_data_length_2 = 0, .data = {}, }, }; usb_error_t error; mtp_byte_t *data, size; size_t remaining; unsigned checksum = 0; MAX_PARAMS(1); var_name_t *var_name = lookup_object_handle( global->transaction.payload.params[0], global); if (!var_name) return schedule_error_response( endpoint, MTP_RSP_INVALID_OBJECT_HANDLE, global); if (var_name->type & reserved_flag) return schedule_data_in( endpoint, 0, final_data_in_complete, global); remaining = get_var_data_size(var_name); error = schedule_data_in( endpoint, offsetof(var_file_header_t, entry.data) + remaining + sizeof(uint16_t), NULL, global); if (error != USB_SUCCESS) return error; data = get_var_data_ptr(var_name); header.file_data_length = remaining + offsetof(var_entry_t, data); header.entry.var_name.type = var_name->type & type_mask; memcpy(header.entry.var_name.name, var_name->name, sizeof(header.entry.var_name.name)); header.entry.version = ((mtp_byte_t *)get_var_vat_ptr(var_name))[-2]; header.entry.flag = var_name->type & storage_flag; header.entry.var_data_length_1 = header.entry.var_data_length_2 = remaining; if (remaining < sizeof(header.entry.data)) size = remaining; else size = sizeof(header.entry.data); memcpy(header.entry.data, data, size); global->transaction.state .get_object.data = data + size; checksum += compute_checksum( &header.entry, offsetof(var_entry_t, data) + size); global->transaction.state .get_object.checksum = checksum; remaining -= size - sizeof(uint16_t); if (remaining == 2 && size < sizeof(header.entry.data)) { header.entry.data[size++] = checksum >> 0; --remaining; } if (remaining == 1 && size < sizeof(header.entry.data)) { header.entry.data[size++] = checksum >> 8; --remaining; } global->transaction.state .get_object.remaining = remaining; return usb_ScheduleBulkTransfer( get_endpoint(endpoint, MTP_EP_DATA_IN), &header, offsetof(var_file_header_t, entry.data) + size, size == sizeof(header.entry.data) ? get_object_complete : final_data_in_complete, global); } case MTP_OPR_GET_THUMB: printf("GetThumb"); break; case MTP_OPR_DELETE_OBJECT: { int response = MTP_RSP_OK; if (global->transaction.payload.params[0] != MTP_ROOT_OBJECT_HANDLE) { MAX_PARAMS(1); response = delete_object( endpoint, global->transaction.payload.params[0], global); if (response < MTP_RSP_UNDEFINED) return response; } else { MAX_PARAMS(2); if (!global->transaction.payload.params[1] || global->transaction.payload.params[1] == MTP_OF_UNDEFINED) for (size_t id = 0; id++ != global->max_name_id && response >= MTP_RSP_UNDEFINED; ) { int result = delete_object( endpoint, id, global); if (result < MTP_RSP_UNDEFINED) return result; else if (result == MTP_RSP_ACCESS_DENIED) response = MTP_RSP_PARTIAL_DELETION; } } return schedule_response( endpoint, response, NULL, 0, global); } case MTP_OPR_SEND_OBJECT_INFO: global->transaction.pending .send_object.handle = pending_handle; global->transaction.state .send_object_info.state = SEND_OBJECT_INFO_CONTAINER_STATE; global->transaction.state .send_object_info.response = MTP_RSP_OK; for (mtp_byte_t i = 2; i != MTP_MAX_PARAMS; ++i) if (global->transaction.payload.params[i]) global->transaction.state .send_object_info.response = MTP_RSP_PARAMETER_NOT_SUPPORTED; #define SEND_OBJECT_INFO_IS_STORAGE(name) \ if (global->transaction.payload \ .params[0] == I##name) { \ global->transaction.pending \ .send_object.flag = \ name##_flag & name##_mask; \ global->transaction.pending \ .send_object.mask = \ name##_mask ^ storage_flag; \ } else FOR_EACH_STORAGE_NONE(SEND_OBJECT_INFO_IS_STORAGE) if (global->transaction.state .send_object_info.response == MTP_RSP_OK) global->transaction.state .send_object_info.response = MTP_RSP_INVALID_STORAGE_ID; if (global->transaction.state .send_object_info.response == MTP_RSP_OK && global->transaction.payload.params[1] && global->transaction.payload.params[1] != MTP_ROOT_OBJECT_HANDLE) global->transaction.state .send_object_info.response = lookup_object_handle( global->transaction.payload.params[1], global) ? MTP_RSP_INVALID_PARENT_OBJECT : MTP_RSP_INVALID_OBJECT_HANDLE; global->transaction.state .send_object_info.params[0] = global->transaction.payload.params[0] == Inone ? Iram : global->transaction.payload.params[0]; global->transaction.state .send_object_info.params[1] = MTP_ROOT_OBJECT_HANDLE; return usb_ScheduleBulkTransfer( endpoint, &global->transaction, MTP_MAX_BULK_PKT_SZ, send_object_info_complete, global); case MTP_OPR_SEND_OBJECT: global->transaction.pending .send_object.handle = pending_handle; global->transaction.state .send_object.response = MTP_RSP_OK; for (mtp_byte_t i = 0; i != MTP_MAX_PARAMS; ++i) if (global->transaction.payload.params[i]) global->transaction.state .send_object.response = MTP_RSP_PARAMETER_NOT_SUPPORTED; if (global->transaction.state .send_object.response == MTP_RSP_OK && !global->transaction.pending .send_object.handle) global->transaction.state .send_object.response = MTP_RSP_NO_VALID_OBJECT_INFO; return usb_ScheduleBulkTransfer( endpoint, &global->transaction, MTP_MAX_BULK_PKT_SZ, send_object_container_complete, global); case MTP_OPR_INITIATE_CAPTURE: printf("InitiateCapture"); break; case MTP_OPR_FORMAT_STORE: printf("FormatStore"); break; case MTP_OPR_RESET_DEVICE: printf("ResetDevice"); break; case MTP_OPR_SELF_TEST: printf("SelfTest"); break; case MTP_OPR_SET_OBJECT_PROTECTION: printf("SetObjectProtection"); break; case MTP_OPR_POWER_DOWN: printf("PowerDown"); break; case MTP_OPR_GET_DEVICE_PROP_DESC: MAX_PARAMS(1); #define DECLARE_FORM_NONE(type) #define DECLARE_FORM_RANGE(type) \ type##_t minimum_value; \ type##_t maximum_value; \ type##_t step_size; #define DEFINE_FORM_NONE(name) #define DEFINE_FORM_RANGE(name) \ .minimum_value = name##_MIN, \ .maximum_value = name##_MAX, \ .step_size = name##_STEP, #define GET_DEVICE_PROP_DESC_RESPONSE(type, name, form) \ if (global->transaction.payload.params[0] == \ MTP_DP_##name) { \ static struct name##_DESC { \ mtp_enum_t device_property_code; \ mtp_enum_t datatype; \ mtp_byte_t get_set; \ type##_t factory_default_value; \ type##_t current_value; \ mtp_byte_t form_flag; \ DECLARE_FORM_##form(type) \ } name = { \ .device_property_code = MTP_DP_##name, \ .datatype = MTP_TC_##type, \ .get_set = name##_GET_SET, \ .factory_default_value = name##_DEF, \ .current_value = name##_DEF, \ .form_flag = MTP_FORM_##form, \ DEFINE_FORM_##form(name) \ }; \ name##_GET(name.current_value); \ return schedule_data_in_response( \ endpoint, &name, \ sizeof(name), global); \ } FOR_EACH_SUPP_DP(GET_DEVICE_PROP_DESC_RESPONSE) return schedule_error_response( endpoint, MTP_RSP_DEVICE_PROP_NOT_SUPPORTED, global); case MTP_OPR_GET_DEVICE_PROP_VALUE: #define GET_DEVICE_PROP_VALUE_RESPONSE(type, name, form) \ if (global->transaction.payload.params[0] == \ MTP_DP_##name) { \ type##_t current; \ name##_GET(current); \ return schedule_data_in_response( \ endpoint, ¤t, \ sizeof(current), global); \ } FOR_EACH_SUPP_DP(GET_DEVICE_PROP_VALUE_RESPONSE) return schedule_error_response( endpoint, MTP_RSP_DEVICE_PROP_NOT_SUPPORTED, global); case MTP_OPR_SET_DEVICE_PROP_VALUE: usb_ScheduleBulkTransfer( endpoint, &global->transaction.payload, MTP_MAX_BULK_PKT_SZ, NULL, global); return schedule_error_response( endpoint, MTP_RSP_ACCESS_DENIED, global); case MTP_OPR_RESET_DEVICE_PROP_VALUE: printf("ResetDevicePropValue"); break; case MTP_OPR_TERMINATE_OPEN_CAPTURE: printf("TerminateOpenCapture"); break; case MTP_OPR_MOVE_OBJECT: { mtp_enum_t response = MTP_RSP_INVALID_STORAGE_ID; MAX_PARAMS(3); var_name_t *var_name = lookup_object_handle( global->transaction.payload.params[0], global); if (!var_name) response = MTP_RSP_INVALID_OBJECT_HANDLE; else if (global->transaction.payload.params[2]) response = lookup_object_handle( global->transaction.payload.params[2], global) ? MTP_RSP_INVALID_PARENT_OBJECT : MTP_RSP_INVALID_OBJECT_HANDLE; #define MOVE_OBJECT_RESPONSE(name) \ else if (global->transaction.payload.params[1] == \ I##name) \ response = \ (var_name->type & name##_mask) != \ name##_flag && \ !arc_unarc_var(var_name) \ ? var_name->type ^= storage_flag, MTP_RSP_OK \ : MTP_RSP_STORE_FULL; FOR_EACH_STORAGE(MOVE_OBJECT_RESPONSE) return schedule_response( endpoint, response, NULL, 0, global); } case MTP_OPR_COPY_OBJECT: printf("CopyObject"); break; case MTP_OPR_GET_PARTIAL_OBJECT: printf("GetPartialObject"); break; case MTP_OPR_INITIATE_OPEN_CAPTURE: printf("InitiateOpenCapture"); break; case MTP_OPR_GET_OBJECT_PROPS_SUPPORTED: printf("GetObjectPropsSupported"); break; case MTP_OPR_GET_OBJECT_PROP_DESC: printf("GetObjectPropDesc"); break; case MTP_OPR_GET_OBJECT_PROP_VALUE: printf("GetObjectPropValue"); break; case MTP_OPR_SET_OBJECT_PROP_VALUE: printf("SetObjectPropValue"); break; case MTP_OPR_GET_OBJECT_REFERENCES: printf("GetObjectReferences"); break; case MTP_OPR_SET_OBJECT_REFERENCES: printf("SetObjectReferences"); break; case MTP_OPR_SKIP: printf("Skip"); break; } printf(": (%08" PRIX32 ")", global->transaction.container.length.size); for (mtp_byte_t i = 0; params_size -= 4; ++i) printf(" %08" PRIX32, global->transaction.payload.params[i]); printf("\n"); return schedule_error_response( endpoint, MTP_RSP_OPERATION_NOT_SUPPORTED, global); } DEFINE_CALLBACK(get_object_handles) { mtp_id_t *handles = global->transaction.payload.handles; mtp_byte_t size = 0; size_t id = global->transaction.state .get_object_handles.id; mtp_byte_t mask = global->transaction.state .get_object_handles.mask; mtp_byte_t flag = global->transaction.state .get_object_handles.flag; (void)transferred; if (status != USB_TRANSFER_COMPLETED) return status_error(status); if (global->reset) return schedule_command(endpoint, global); if (!id) { ++handles; size = sizeof(mtp_size_t); } while (size != MTP_MAX_BULK_PKT_SZ && id != global->max_name_id) { var_name_t *name = &global->var_names[id++]; if (name->valid && (name->type & mask) == flag) { *handles++ = id; size += sizeof(mtp_id_t); } } global->transaction.state .get_object_handles.id = id; return usb_ScheduleBulkTransfer( endpoint, &global->transaction.payload, size, size == MTP_MAX_BULK_PKT_SZ ? get_object_handles_complete : final_data_in_complete, global); } DEFINE_CALLBACK(get_object) { size_t remaining = global->transaction.state .get_object.remaining; unsigned checksum = global->transaction.state .get_object.checksum; mtp_byte_t size; (void)transferred; if (status != USB_TRANSFER_COMPLETED) return status_error(status); if (global->reset) return schedule_command(endpoint, global); if (remaining <= 2) size = 0; else if (remaining <= MTP_MAX_BULK_PKT_SZ + 2) size = remaining - sizeof(uint16_t); else size = MTP_MAX_BULK_PKT_SZ; memcpy(global->transaction.payload.buffer, global->transaction.state .get_object.data, size); global->transaction.state .get_object.data += size; checksum += compute_checksum( global->transaction.payload.buffer, size); global->transaction.state .get_object.checksum = checksum; remaining -= size; if (remaining == 2 && size < MTP_MAX_BULK_PKT_SZ) { global->transaction.payload.buffer[size++] = checksum >> 0; --remaining; } if (remaining == 1 && size < MTP_MAX_BULK_PKT_SZ) { global->transaction.payload.buffer[size++] = checksum >> 8; --remaining; } global->transaction.state .get_object.remaining = remaining; return usb_ScheduleBulkTransfer( endpoint, &global->transaction.payload, size, size == MTP_MAX_BULK_PKT_SZ ? get_object_complete : final_data_in_complete, global); } DEFINE_CALLBACK(send_object_info) { if (status != USB_TRANSFER_COMPLETED) return status_error(status); if (global->reset) return schedule_command(endpoint, global); mtp_byte_t object_info_size = transferred; switch (global->transaction.state .send_object_info.state) { case SEND_OBJECT_INFO_CONTAINER_STATE: if ((transferred != sizeof(mtp_container_t) && transferred != MTP_MAX_BULK_PKT_SZ) || global->transaction.container.length.size < sizeof(mtp_container_t) + sizeof(mtp_object_info_min_t) || global->transaction.container.type != MTP_BT_DATA || global->transaction.container.code != MTP_OPR_SEND_OBJECT_INFO) return stall_data_endpoints(endpoint); global->transaction.state .send_object_info.state = SEND_OBJECT_INFO_DATA_STATE; if (!(object_info_size -= sizeof(mtp_container_t))) { transferred = MTP_MAX_BULK_PKT_SZ; break; } __attribute__((fallthrough)); case SEND_OBJECT_INFO_DATA_STATE: if (global->transaction.state .send_object_info.response != MTP_RSP_OK); else if (object_info_size < sizeof(mtp_object_info_header_t)) global->transaction.state .send_object_info.response = MTP_RSP_INVALID_DATASET; else if (global->transaction.payload .object_info.object_format != MTP_OF_UNDEFINED) global->transaction.state .send_object_info.response = MTP_RSP_INVALID_OBJECT_FORMAT_CODE; else if (global->transaction.payload.object_info .object_compressed_size && global->transaction.payload.object_info .object_compressed_size < offsetof(var_file_header_t, entry.data) + sizeof(uint16_t)) global->transaction.state .send_object_info.response = MTP_RSP_INVALID_DATASET; else if (global->transaction.payload.object_info .object_compressed_size > offsetof(var_file_header_t, entry.data) + 0xFFFF + sizeof(uint16_t)) global->transaction.state .send_object_info.response = MTP_RSP_OBJECT_TOO_LARGE; else if (global->transaction.pending .send_object.handle) global->transaction.state .send_object_info.params[2] = global->transaction.pending .send_object.handle; else if (!(global->transaction.state .send_object_info.params[2] = alloc_object_handle(global))) global->transaction.state .send_object_info.response = MTP_RSP_STORE_FULL; else { var_name_t *var_name = &global->var_names[ global->transaction.state .send_object_info.params[2] - 1]; var_name->type = global->transaction.pending .send_object.flag | reserved_flag; var_name->valid = 1; ++*(var_name->type & storage_flag ? &global->arc_name_count.word : &global->ram_name_count.word); ++global->total_name_count.word; } global->transaction.state .send_object_info.state = SEND_OBJECT_INFO_REST_STATE; __attribute__((fallthrough)); case SEND_OBJECT_INFO_REST_STATE: break; } if (transferred == MTP_MAX_BULK_PKT_SZ) return usb_ScheduleBulkTransfer( endpoint, &global->transaction.payload, MTP_MAX_BULK_PKT_SZ, send_object_info_complete, global); if (global->transaction.state .send_object_info.response != MTP_RSP_OK) return schedule_error_response( endpoint, global->transaction.state .send_object_info.response, global); global->transaction.pending .send_object.handle = global->transaction.state .send_object_info.params[2]; return schedule_ok_response( endpoint, global->transaction.state .send_object_info.params, lengthof(global->transaction.state .send_object_info.params), global); } DEFINE_CALLBACK(send_object_container) { if (status != USB_TRANSFER_COMPLETED) return status_error(status); if (global->reset) return schedule_command(endpoint, global); if (transferred < sizeof(mtp_container_t) || global->transaction.container.length.size > sizeof(mtp_container_t) + offsetof(var_file_header_t, entry.data) + 0xFFFF + sizeof(uint16_t) || global->transaction.container.length.word < transferred || (transferred != sizeof(mtp_container_t) && transferred != MTP_MAX_BULK_PKT_SZ && transferred != global->transaction .container.length.word) || global->transaction.container.type != MTP_BT_DATA || global->transaction.container.code != MTP_OPR_SEND_OBJECT) return stall_data_endpoints(endpoint); if (global->transaction.container.length.word == sizeof(mtp_container_t)) return schedule_ok_response( endpoint, NULL, 0, global); mtp_byte_t extra = global->transaction.state .send_object.extra = transferred - sizeof(mtp_container_t); memcpy(OBJECT_BUFFER, global->transaction.payload.buffer, extra); if (extra && transferred != MTP_MAX_BULK_PKT_SZ) return send_object_complete( endpoint, status, extra, global); return usb_ScheduleBulkTransfer( endpoint, OBJECT_BUFFER + extra, global->transaction.container.length.word - sizeof(mtp_container_t) - extra, send_object_complete, global); } DEFINE_CALLBACK(send_object) { size_t handle = global->transaction.pending .send_object.handle; global->transaction.pending .send_object.handle = 0; if (status != USB_TRANSFER_COMPLETED) return status_error(status); if (global->reset) return schedule_command(endpoint, global); mtp_enum_t response = global->transaction.state .send_object.response; if (response == MTP_RSP_OK) response = send_object( endpoint, handle, transferred, global); if (response < MTP_RSP_UNDEFINED) return response; return schedule_response( endpoint, response, NULL, 0, global); } DEFINE_CALLBACK(final_data_in) { (void)transferred; if (status != USB_TRANSFER_COMPLETED) return status_error(status); if (global->reset) return schedule_command(endpoint, global); return schedule_ok_response(endpoint, NULL, 0, global); } DEFINE_CALLBACK(response) { (void)transferred; if (status != USB_TRANSFER_COMPLETED) return status_error(status); return schedule_command(endpoint, global); } DEFINE_CALLBACK(event) { (void)endpoint; (void)transferred; ((mtp_event_t *)global)->container.length.byte = 0; if (status != USB_TRANSFER_COMPLETED) return status_error(status); printf("event complete\n"); return USB_SUCCESS; } static usb_error_t usb_event(usb_event_t event, void *event_data, mtp_global_t *global) { usb_error_t error = USB_SUCCESS; if (!(usb_GetRole() & USB_ROLE_DEVICE)) return error; usb_endpoint_t control = usb_GetDeviceEndpoint( usb_FindDevice(NULL, NULL, USB_SKIP_HUBS), MTP_EP_CONTROL); if (event < USB_DEFAULT_SETUP_EVENT) printf("event: %d\n", event); if (event == 99) printf("debug: %06X\n", (unsigned)event_data); switch (event) { case USB_DEFAULT_SETUP_EVENT: { usb_control_setup_t *setup = event_data; if (setup->bmRequestType == (USB_DEVICE_TO_HOST | USB_STANDARD_REQUEST | USB_RECIPIENT_DEVICE) && setup->bRequest == USB_GET_DESCRIPTOR && setup->wValue == 0x03EE && !setup->wIndex) { DEFINE_STRING_DESCRIPTOR(const, os_specific); error = usb_ScheduleTransfer( control, (void *)&os_specific, sizeof(os_specific), control_complete, global); } else if (setup->bmRequestType == (USB_DEVICE_TO_HOST | USB_VENDOR_REQUEST | USB_RECIPIENT_DEVICE) && setup->bRequest == 1 && !setup->wValue && setup->wIndex == 4) { static const usb_descriptor_t control_message = { .bLength = sizeof(control_message), .bDescriptorType = 0, .data = { 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x4D, 0x54, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, }; error = usb_ScheduleTransfer( control, (void *)&control_message, sizeof(control_message), control_complete, global); } else if (setup->bmRequestType << 1 == (USB_CLASS_REQUEST | USB_RECIPIENT_INTERFACE) << 1 && !setup->wValue && !setup->wIndex) { if (!(setup->bmRequestType & USB_DEVICE_TO_HOST)) { if (setup->bRequest != DEVICE_RESET_REQUEST || setup->wLength) break; global->reset = true; global->session = 0; error = usb_ScheduleTransfer( control, NULL, 0, control_complete, global); } else if (setup->bRequest == GET_EXTENDED_EVENT_DATA) { printf("GET_EXTENDED_EVENT_DATA\n"); break; } else if (setup->bRequest == GET_DEVICE_STATUS) { printf("GET_DEVICE_STATUS\n"); break; } else break; } else break; if (error == USB_SUCCESS) error = USB_IGNORE; break; } case USB_HOST_CONFIGURE_EVENT: global->session = 0; error = schedule_command(control, global); break; default: break; } return error; } int main(void) { static mtp_global_t global; usb_error_t error; ui_Init(); printf(" TRANSFER v0.0.1b\n" " Connect USB to PC.\n" " Press [clear] to exit.\n" "--------------------------------"); static mtp_device_info_t device_info = { .standard_version = 100, /* 1.00 */ .mtp_vendor_extension_id = 6, .mtp_version = 101, /* 1.01 */ .mtp_extensions_length = lengthof(device_info.mtp_extensions), .mtp_extensions = Lmtp_extensions, .functional_mode = MTP_MODE_STANDARD_MODE, .operations_supported_length = lengthof(device_info.operations_supported), .operations_supported = { #define LIST_SUPP_OPR(name) \ MTP_OPR_##name, FOR_EACH_SUPP_OPR(LIST_SUPP_OPR) }, .events_supported_length = lengthof(device_info.events_supported), .events_supported = {}, .device_properties_length = lengthof(device_info.device_properties), .device_properties = { #define LIST_SUPP_DP(type, name, form) \ MTP_DP_##name, FOR_EACH_SUPP_DP(LIST_SUPP_DP) }, .capture_formats_length = lengthof(device_info.capture_formats), .capture_formats = { #define LIST_SUPP_CF(name) \ MTP_OF_##name, FOR_EACH_SUPP_CF(LIST_SUPP_CF) }, .playback_formats_length = lengthof(device_info.playback_formats), .playback_formats = { #define LIST_SUPP_PF(name) \ MTP_OF_##name, FOR_EACH_SUPP_PF(LIST_SUPP_PF) }, .manufacturer_length = lengthof(device_info.manufacturer), .manufacturer = Lmanufacturer, .model_length = lengthof(Lproduct), .model = Lproduct, .device_version_length = lengthof(device_info.device_version), .device_version = Ldevice_version, .serial_number_length = lengthof(device_info.serial_number), .serial_number = Lserial_number Lserial_number, }; #define DEFINE_STORAGE_INFO(name) \ static name##_mtp_storage_info_t name##_storage_info = { \ .storage_type = MTP_ST_FIXED_RAM, \ .filesystem_type = MTP_FT_GENERIC_FLAT, \ .access_capability = MTP_AC_READ_WRITE, \ .max_capacity = { .uint64 = name##_max_capacity }, \ .free_space_in_bytes = { .uint64 = 0 }, \ .free_space_in_objects = UINT32_MAX, \ .storage_description_length = \ lengthof(L##name##_storage_desc), \ .storage_description = L##name##_storage_desc, \ .volume_identifier_length = \ lengthof(L##name##_volume_id), \ .volume_identifier = L##name##_volume_id, \ }; FOR_EACH_STORAGE(DEFINE_STORAGE_INFO) /* Standard USB Descriptors */ FOR_EACH_STRING_DESCRIPTOR(DEFINE_STRING_DESCRIPTOR) DEFINE_STRING_DESCRIPTOR(const, product84) const static usb_string_descriptor_t *strings[] = { #define ADDRESSOF_STRING_DESCRIPTOR(const, name) &name, FOR_EACH_STRING_DESCRIPTOR(ADDRESSOF_STRING_DESCRIPTOR) }; static const usb_string_descriptor_t langids = { .bLength = sizeof(langids) + sizeof(wchar_t) * 1, .bDescriptorType = USB_STRING_DESCRIPTOR, .bString = { [0] = 0x0409u, }, }; static const struct configuration1 { usb_configuration_descriptor_t configuration; struct configuration1_interface0 { usb_interface_descriptor_t interface; usb_endpoint_descriptor_t endpoints[3]; } interface0; } configuration1 = { .configuration = { .bLength = sizeof(configuration1.configuration), .bDescriptorType = USB_CONFIGURATION_DESCRIPTOR, .wTotalLength = sizeof(configuration1), .bNumInterfaces = 1u, .bConfigurationValue = 1u, .iConfiguration = Icharging_cfg, .bmAttributes = USB_CONFIGURATION_ATTRIBUTES | USB_SELF_POWERED | USB_NO_REMOTE_WAKEUP, .bMaxPower = 500u / 2u, }, .interface0 = { .interface = { .bLength = sizeof(configuration1.interface0.interface), .bDescriptorType = USB_INTERFACE_DESCRIPTOR, .bInterfaceNumber = 0u, .bAlternateSetting = 0u, .bNumEndpoints = lengthof(configuration1.interface0.endpoints), .bInterfaceClass = USB_IMAGE_CLASS, .bInterfaceSubClass = 1u, .bInterfaceProtocol = 1u, .iInterface = Imtp_interface, }, .endpoints = { [0] = { .bLength = sizeof(configuration1.interface0.endpoints[0]), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR, .bEndpointAddress = MTP_EP_DATA_IN, .bmAttributes = USB_BULK_TRANSFER, .wMaxPacketSize = MTP_MAX_BULK_PKT_SZ, .bInterval = 0u, }, [1] = { .bLength = sizeof(configuration1.interface0.endpoints[1]), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR, .bEndpointAddress = MTP_EP_DATA_OUT, .bmAttributes = USB_BULK_TRANSFER, .wMaxPacketSize = MTP_MAX_BULK_PKT_SZ, .bInterval = 0u, }, [2] = { .bLength = sizeof(configuration1.interface0.endpoints[2]), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR, .bEndpointAddress = MTP_EP_INT, .bmAttributes = USB_INTERRUPT_TRANSFER, .wMaxPacketSize = MTP_MAX_INT_PKT_SZ, .bInterval = 6u, }, }, }, }; static const usb_configuration_descriptor_t *configurations[] = { [0] = &configuration1.configuration }; static usb_device_descriptor_t device = { .bLength = sizeof(device), .bDescriptorType = USB_DEVICE_DESCRIPTOR, .bcdUSB = 0x200u, /* 2.00 */ .bDeviceClass = USB_INTERFACE_SPECIFIC_CLASS, .bDeviceSubClass = 0u, .bDeviceProtocol = 0u, .bMaxPacketSize0 = 0x40u, .idVendor = 0x0451u, .idProduct = 0xE010u, .bcdDevice = 0x260u, /* 2.60 */ .iManufacturer = Imanufacturer, .iProduct = Iproduct, .iSerialNumber = Iserial_number, .bNumConfigurations = lengthof(configurations), }; static const usb_standard_descriptors_t descriptors = { .device = &device, .configurations = configurations, .langids = &langids, .numStrings = Inum_strings, .strings = strings, }; const system_info_t *info = os_GetSystemInfo(); wchar_t *serial_numbers[] = { &serial_number.bString[2 * lengthof(info->calcid)], &device_info.serial_number[4 * lengthof(info->calcid)], #define LIST_STORAGE_INFO_SERIAL(name) \ &name##_storage_info.volume_identifier[2 * lengthof(info->calcid)], FOR_EACH_STORAGE(LIST_STORAGE_INFO_SERIAL) }; for (mtp_byte_t i = 0; i != MTP_MAX_PENDING_EVENTS; ++i) global.events[i].container.type = MTP_BT_EVENT; if (info->hardwareType & 1) global.device_info_size = sizeof(mtp_device_info_t); else { *(mtp_byte_t *)&device.bcdDevice = 0x40; /* 2.40 */ device_info.model_length = lengthof(Lproduct84); memcpy(device_info.model, Lproduct84, sizeof(Lproduct84)); memmove(&device_info.model[lengthof(Lproduct84)], &device_info.device_version_length, sizeof(mtp_device_info_t) - offsetof(mtp_device_info_t, device_version_length)); global.device_info_size = sizeof(mtp_device_info_t) - sizeof(Lproduct) + sizeof(Lproduct84); strings[Iproduct - 1] = &product84; var_extensions[0x23][0] = 'e'; } global.device_info = &device_info; #define SET_STORAGE_INFO_PTR(name) \ global.name##_storage_info = &name##_storage_info; FOR_EACH_STORAGE(SET_STORAGE_INFO_PTR) for (mtp_byte_t i = 2 * lengthof(info->calcid); i; ) { mtp_byte_t nibble = info->calcid[--i >> 1]; if (!(i & 1)) nibble >>= 4; nibble &= 0xF; if (nibble >= 10) nibble += 'A' - '0' - 10; nibble += '0'; for (mtp_byte_t j = lengthof(serial_numbers); j; ) *--serial_numbers[--j] = nibble; } { void *entry = os_GetSymTablePtr(), *data; uint24_t type, name_length; char name[9]; var_name_t *var_name = global.var_names; do { entry = os_NextSymEntry( entry, &type, &name_length, name, &data); if ((type &= type_mask) == TI_EQU_TYPE && !((equ_t *)data)->len) continue; if (data >= (void *)os_RamStart) { var_name->type = type | ram_flag; ++global.ram_name_count.word; } else { var_name->type = type | arc_flag; ++global.arc_name_count.word; } memcpy(var_name->name, name, name_length); ++var_name; if (++global.max_name_id == lengthof(global.var_names)) break; } while (entry); global.total_name_count.word = global.max_name_id; } do { if (usb_Init(usb_event, &global, &descriptors, USB_DEFAULT_INIT_FLAGS) != USB_SUCCESS) break; do error = wait_for_usb(&global); while (!global.exiting && error == USB_SUCCESS); if (error != USB_SUCCESS) printf("error: %06X\n", error); } while (!global.exiting); usb_Cleanup(); ui_Cleanup(); return 0; } usb_error_t wait_for_usb(mtp_global_t *global) { usb_error_t error; kb_SetMode(MODE_2_SINGLE); do if ((error = usb_HandleEvents()) != USB_SUCCESS) return error; while (kb_GetMode() != MODE_0_IDLE); if (kb_IsDown(kb_KeyClear)) global->exiting = true; if (kb_IsDown(kb_KeyGraphVar)) { var_name_t var_name = { .type = TI_REAL_TYPE, .name = "A", }; mtp_byte_t *vat = get_var_vat_ptr(&var_name); mtp_byte_t *data = get_var_data_ptr(&var_name); size_t size = get_var_data_size(&var_name); printf("vat"); for (int i = -9; i <= 0; ++i) printf(" %02X", vat[i]); printf("\ndata"); for (size_t i = 0; i != size; ++i) printf(" %02X", data[i]); printf("\n"); } return USB_SUCCESS; }