/* * Copyright (c) 2022, sakumisu * * SPDX-License-Identifier: Apache-2.0 */ #include "usbd_core.h" #include "usbd_mtp.h" #include "usbd_mtp_config.h" /* MTP Stage */ enum Stage { MTP_READ_COMMAND = 0, MTP_DATA_OUT = 1, MTP_DATA_IN = 2, MTP_SEND_RESPONSE = 3, MTP_WAIT_RESPONSE = 4, }; USB_NOCACHE_RAM_SECTION struct usbd_mtp_priv { USB_MEM_ALIGNX struct mtp_container_command con_command; USB_MEM_ALIGNX struct mtp_container_data con_data; USB_MEM_ALIGNX struct mtp_container_response con_response; enum Stage stage; uint8_t session_state; uint32_t response_code; } g_usbd_mtp; /* Max USB packet size */ #ifndef CONFIG_USB_HS #define MTP_BULK_EP_MPS 64 #else #define MTP_BULK_EP_MPS 512 #endif #define MTP_OUT_EP_IDX 0 #define MTP_IN_EP_IDX 1 #define MTP_INT_EP_IDX 2 /* Describe EndPoints configuration */ static struct usbd_endpoint mtp_ep_data[3]; static int mtp_class_interface_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) { USB_LOG_DBG("MTP Class request: " "bRequest 0x%02x\r\n", setup->bRequest); switch (setup->bRequest) { case MTP_REQUEST_CANCEL: break; case MTP_REQUEST_GET_EXT_EVENT_DATA: break; case MTP_REQUEST_RESET: break; case MTP_REQUEST_GET_DEVICE_STATUS: break; default: USB_LOG_WRN("Unhandled MTP Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } return 0; } static void usbd_mtp_send_response(uint32_t code) { USB_LOG_DBG("Send response\r\n"); g_usbd_mtp.stage = MTP_WAIT_RESPONSE; g_usbd_mtp.con_response.conlen = 12; g_usbd_mtp.con_response.contype = MTP_CONTAINER_TYPE_RESPONSE; g_usbd_mtp.con_response.code = code; g_usbd_mtp.con_response.trans_id = g_usbd_mtp.con_command.trans_id; usbd_ep_start_write(mtp_ep_data[MTP_IN_EP_IDX].ep_addr, (uint8_t *)&g_usbd_mtp.con_response, 12); } static void usbd_mtp_send_info(uint8_t *data, uint32_t len) { USB_LOG_DBG("Send info\r\n"); g_usbd_mtp.stage = MTP_SEND_RESPONSE; g_usbd_mtp.con_data.conlen = 12 + len; g_usbd_mtp.con_data.contype = MTP_CONTAINER_TYPE_DATA; g_usbd_mtp.con_data.code = MTP_RESPONSE_OK; g_usbd_mtp.con_data.trans_id = g_usbd_mtp.con_command.trans_id; memcpy(g_usbd_mtp.con_data.data, data, len); usbd_ep_start_write(mtp_ep_data[MTP_IN_EP_IDX].ep_addr, (uint8_t *)&g_usbd_mtp.con_data, 12 + len); } static void usbd_mtp_get_device_info(void) { struct mtp_device_info device_info; uint16_t i; device_info.StandardVersion = 100; device_info.VendorExtensionID = 0x06; device_info.VendorExtensionVersion = 100; device_info.VendorExtensionDesc_len = (uint8_t)CONFIG_MTP_VEND_EXT_DESC_LEN; for (i = 0; i < CONFIG_MTP_VEND_EXT_DESC_LEN; i++) { device_info.VendorExtensionDesc[i] = VendExtDesc[i]; } /* device supports one mode , standard mode */ device_info.FunctionalMode = 0x0000; /* All supported operation */ device_info.OperationsSupported_len = CONFIG_MTP_SUPP_OP_LEN; for (i = 0U; i < CONFIG_MTP_SUPP_OP_LEN; i++) { device_info.OperationsSupported[i] = SuppOP[i]; } /* event that are currently generated by the device*/ device_info.EventsSupported_len = CONFIG_MTP_SUPP_EVENTS_LEN; for (i = 0U; i < CONFIG_MTP_SUPP_EVENTS_LEN; i++) { device_info.EventsSupported[i] = SuppEvents[i]; } device_info.DevicePropertiesSupported_len = CONFIG_MTP_SUPP_DEVICE_PROP_LEN; for (i = 0U; i < CONFIG_MTP_SUPP_DEVICE_PROP_LEN; i++) { device_info.DevicePropertiesSupported[i] = DevicePropSupp[i]; } device_info.CaptureFormats_len = CONFIG_MTP_SUPP_CAPT_FORMAT_LEN; for (i = 0U; i < CONFIG_MTP_SUPP_CAPT_FORMAT_LEN; i++) { device_info.CaptureFormats[i] = SuppCaptFormat[i]; } device_info.ImageFormats_len = CONFIG_MTP_SUPP_IMG_FORMAT_LEN; /* number of image formats that are supported by the device*/ for (i = 0U; i < CONFIG_MTP_SUPP_IMG_FORMAT_LEN; i++) { device_info.ImageFormats[i] = SuppImgFormat[i]; } device_info.Manufacturer_len = (uint8_t)CONFIG_MTP_MANUF_LEN; for (i = 0U; i < CONFIG_MTP_MANUF_LEN; i++) { device_info.Manufacturer[i] = Manuf[i]; } device_info.Model_len = (uint8_t)CONFIG_MTP_MODEL_LEN; for (i = 0U; i < CONFIG_MTP_MODEL_LEN; i++) { device_info.Model[i] = Model[i]; } device_info.DeviceVersion_len = (uint8_t)CONFIG_MTP_DEVICE_VERSION_LEN; for (i = 0U; i < CONFIG_MTP_DEVICE_VERSION_LEN; i++) { device_info.DeviceVersion[i] = DeviceVers[i]; } device_info.SerialNumber_len = (uint8_t)CONFIG_MTP_SERIAL_NBR_LEN; for (i = 0U; i < CONFIG_MTP_SERIAL_NBR_LEN; i++) { device_info.SerialNumber[i] = SerialNbr[i]; } usbd_mtp_send_info((uint8_t *)&device_info, sizeof(struct mtp_device_info)); } static void usbd_mtp_open_session(void) { usbd_mtp_send_response(MTP_RESPONSE_OK); } static void usbd_mtp_get_storage_ids(void) { struct mtp_storage_id storage_id; storage_id.StorageIDS_len = CONFIG_MTP_STORAGE_ID_LEN; storage_id.StorageIDS[0] = MTP_STORAGE_ID; usbd_mtp_send_info((uint8_t *)&storage_id, sizeof(struct mtp_storage_id)); } static void usbd_mtp_get_storage_info(void) { struct mtp_storage_info storage_info; storage_info.StorageType = MTP_STORAGE_REMOVABLE_RAM; storage_info.FilesystemType = MTP_FILESYSTEM_GENERIC_FLAT; storage_info.AccessCapability = MTP_ACCESS_CAP_RW; storage_info.MaxCapability = 0x0080DFA81A000000; // todo storage_info.FreeSpaceInBytes = 0x00007EEB0D000000; // todo storage_info.FreeSpaceInObjects = 0xFFFFFFFFU; /* not used */ storage_info.StorageDescription = 0U; storage_info.VolumeLabel = 0U; usbd_mtp_send_info((uint8_t *)&storage_info, sizeof(struct mtp_storage_info)); } static void usbd_mtp_get_object_handles(void) { struct mtp_object_handle object_handle; // todo usbd_mtp_send_info((uint8_t *)&object_handle, sizeof(struct mtp_object_handle)); } static void usbd_mtp_get_object_info(void) { struct mtp_object_info object_info; object_info.Storage_id = MTP_STORAGE_ID; object_info.ObjectFormat = 0; // todo object_info.ObjectCompressedSize = 0; //todo object_info.ProtectionStatus = 0U; object_info.ThumbFormat = MTP_OBJ_FORMAT_UNDEFINED; object_info.ThumbCompressedSize = 0U; object_info.ThumbPixWidth = 0U; /* not supported or not an image */ object_info.ThumbPixHeight = 0U; object_info.ImagePixWidth = 0U; object_info.ImagePixHeight = 0U; object_info.ImageBitDepth = 0U; object_info.ParentObject = 0; // todo object_info.AssociationType = 0U; object_info.AssociationDesc = 0U; object_info.SequenceNumber = 0U; /* we have to get this value before object_info.Filename */ object_info.Filename_len = sizeof(DefaultFileName); memcpy(object_info.Filename, DefaultFileName, (uint32_t)object_info.Filename_len + 1U); object_info.CaptureDate = 0U; object_info.ModificationDate = 0U; object_info.Keywords = 0U; usbd_mtp_send_info((uint8_t *)&object_info, sizeof(struct mtp_object_info)); } static void usbd_mtp_get_object_prop_desc(void) { struct mtp_object_prop_desc object_prop_desc; uint16_t undef_format = MTP_OBJ_FORMAT_UNDEFINED; uint32_t storageid = MTP_STORAGE_ID; switch (g_usbd_mtp.con_command.param1) /* switch obj prop code */ { case MTP_OB_PROP_OBJECT_FORMAT: object_prop_desc.ObjectPropertyCode = (uint16_t)(g_usbd_mtp.con_command.param1); object_prop_desc.DataType = MTP_DATATYPE_UINT16; object_prop_desc.GetSet = MTP_PROP_GET; object_prop_desc.DefValue = (uint8_t *)&undef_format; object_prop_desc.GroupCode = 0U; object_prop_desc.FormFlag = 0U; break; case MTP_OB_PROP_STORAGE_ID: object_prop_desc.ObjectPropertyCode = (uint16_t)(g_usbd_mtp.con_command.param1); object_prop_desc.DataType = MTP_DATATYPE_UINT32; object_prop_desc.GetSet = MTP_PROP_GET; object_prop_desc.DefValue = (uint8_t *)&storageid; object_prop_desc.GroupCode = 0U; object_prop_desc.FormFlag = 0U; break; case MTP_OB_PROP_OBJ_FILE_NAME: object_prop_desc.ObjectPropertyCode = (uint16_t)(g_usbd_mtp.con_command.param1); object_prop_desc.DataType = MTP_DATATYPE_STR; object_prop_desc.GetSet = MTP_PROP_GET; object_prop_desc.DefValue = 0U; object_prop_desc.GroupCode = 0U; object_prop_desc.FormFlag = 0U; break; case MTP_OB_PROP_PARENT_OBJECT: object_prop_desc.ObjectPropertyCode = (uint16_t)(g_usbd_mtp.con_command.param1); object_prop_desc.DataType = MTP_DATATYPE_STR; object_prop_desc.GetSet = MTP_PROP_GET; object_prop_desc.DefValue = 0U; object_prop_desc.GroupCode = 0U; object_prop_desc.FormFlag = 0U; break; case MTP_OB_PROP_OBJECT_SIZE: object_prop_desc.ObjectPropertyCode = (uint16_t)(g_usbd_mtp.con_command.param1); object_prop_desc.DataType = MTP_DATATYPE_UINT64; object_prop_desc.GetSet = MTP_PROP_GET; object_prop_desc.DefValue = 0U; object_prop_desc.GroupCode = 0U; object_prop_desc.FormFlag = 0U; break; case MTP_OB_PROP_NAME: object_prop_desc.ObjectPropertyCode = (uint16_t)(g_usbd_mtp.con_command.param1); object_prop_desc.DataType = MTP_DATATYPE_STR; object_prop_desc.GetSet = MTP_PROP_GET; object_prop_desc.DefValue = NULL; object_prop_desc.GroupCode = 0U; object_prop_desc.FormFlag = 0U; break; case MTP_OB_PROP_PERS_UNIQ_OBJ_IDEN: object_prop_desc.ObjectPropertyCode = (uint16_t)(g_usbd_mtp.con_command.param1); object_prop_desc.DataType = MTP_DATATYPE_UINT128; object_prop_desc.GetSet = MTP_PROP_GET; object_prop_desc.DefValue = 0U; object_prop_desc.GroupCode = 0U; object_prop_desc.FormFlag = 0U; break; case MTP_OB_PROP_PROTECTION_STATUS: object_prop_desc.ObjectPropertyCode = (uint16_t)(g_usbd_mtp.con_command.param1); object_prop_desc.DataType = MTP_DATATYPE_UINT16; object_prop_desc.GetSet = MTP_PROP_GET_SET; object_prop_desc.DefValue = 0U; object_prop_desc.GroupCode = 0U; object_prop_desc.FormFlag = 0U; break; default: break; } // todo usbd_mtp_send_info((uint8_t *)&object_prop_desc, sizeof(struct mtp_object_prop_desc)); } static void usbd_mtp_get_object_props_supported(void) { struct mtp_object_props_support object_props_support; uint32_t i; object_props_support.ObjectPropCode_len = CONFIG_MTP_SUPP_OBJ_PROP_LEN; for (i = 0U; i < CONFIG_MTP_SUPP_OBJ_PROP_LEN; i++) { object_props_support.ObjectPropCode[i] = ObjectPropCode[i]; } usbd_mtp_send_info((uint8_t *)&object_props_support, sizeof(struct mtp_object_props_support)); } static void usbd_mtp_get_object_prop_list(void) { struct mtp_object_prop_list object_prop_list; uint16_t filename[255]; uint32_t storageid = MTP_STORAGE_ID; uint32_t default_val = 0U; uint32_t i; uint16_t format; uint64_t objsize; uint32_t parent_proval; object_prop_list.Properties_len = CONFIG_MTP_SUPP_OBJ_PROP_LEN; for (i = 0U; i < CONFIG_MTP_SUPP_OBJ_PROP_LEN; i++) { object_prop_list.Properties[i].ObjectHandle = g_usbd_mtp.con_command.param1; switch (ObjectPropCode[i]) { case MTP_OB_PROP_STORAGE_ID: object_prop_list.Properties[i].PropertyCode = MTP_OB_PROP_STORAGE_ID; object_prop_list.Properties[i].Datatype = MTP_DATATYPE_UINT32; object_prop_list.Properties[i].propval = (uint8_t *)&storageid; break; case MTP_OB_PROP_OBJECT_FORMAT: object_prop_list.Properties[i].PropertyCode = MTP_OB_PROP_OBJECT_FORMAT; object_prop_list.Properties[i].Datatype = MTP_DATATYPE_UINT16; object_prop_list.Properties[i].propval = (uint8_t *)&format; break; case MTP_OB_PROP_OBJ_FILE_NAME: object_prop_list.Properties[i].PropertyCode = MTP_OB_PROP_OBJ_FILE_NAME; object_prop_list.Properties[i].Datatype = MTP_DATATYPE_STR; object_prop_list.Properties[i].propval = NULL; break; case MTP_OB_PROP_PARENT_OBJECT: object_prop_list.Properties[i].PropertyCode = MTP_OB_PROP_PARENT_OBJECT; object_prop_list.Properties[i].Datatype = MTP_DATATYPE_UINT32; object_prop_list.Properties[i].propval = (uint8_t *)&parent_proval; break; case MTP_OB_PROP_OBJECT_SIZE: object_prop_list.Properties[i].PropertyCode = MTP_OB_PROP_OBJECT_SIZE; object_prop_list.Properties[i].Datatype = MTP_DATATYPE_UINT64; object_prop_list.Properties[i].propval = (uint8_t *)&objsize; break; case MTP_OB_PROP_NAME: object_prop_list.Properties[i].PropertyCode = MTP_OB_PROP_NAME; object_prop_list.Properties[i].Datatype = MTP_DATATYPE_STR; object_prop_list.Properties[i].propval = NULL; break; case MTP_OB_PROP_PERS_UNIQ_OBJ_IDEN: object_prop_list.Properties[i].PropertyCode = MTP_OB_PROP_PERS_UNIQ_OBJ_IDEN; object_prop_list.Properties[i].Datatype = MTP_DATATYPE_UINT128; object_prop_list.Properties[i].propval = (uint8_t *)&g_usbd_mtp.con_command.param1; break; case MTP_OB_PROP_PROTECTION_STATUS: object_prop_list.Properties[i].PropertyCode = MTP_OB_PROP_PROTECTION_STATUS; object_prop_list.Properties[i].Datatype = MTP_DATATYPE_UINT16; object_prop_list.Properties[i].propval = (uint8_t *)&default_val; break; default: break; } } // todo usbd_mtp_send_info((uint8_t *)&object_prop_list, sizeof(struct mtp_object_prop_list)); } static void usbd_mtp_get_device_prop_desc(void) { struct mtp_device_prop_desc device_prop_desc; uint32_t i; device_prop_desc.DevicePropertyCode = MTP_DEV_PROP_DEVICE_FRIENDLY_NAME; device_prop_desc.DataType = MTP_DATATYPE_STR; device_prop_desc.GetSet = MTP_PROP_GET_SET; device_prop_desc.DefaultValue_len = CONFIG_MTP_DEVICE_PROP_DESC_DEF_LEN; for (i = 0U; i < (sizeof(DevicePropDefVal) / 2U); i++) { device_prop_desc.DefaultValue[i] = DevicePropDefVal[i]; } device_prop_desc.CurrentValue_len = CONFIG_MTP_DEVICE_PROP_DESC_CUR_LEN; for (i = 0U; i < (sizeof(DevicePropCurDefVal) / 2U); i++) { device_prop_desc.CurrentValue[i] = DevicePropCurDefVal[i]; } device_prop_desc.FormFlag = 0U; usbd_mtp_send_info((uint8_t *)&device_prop_desc, sizeof(struct mtp_device_prop_desc)); } static int usbd_mtp_decode_command(struct mtp_container_command *command) { printf("code:%04x\r\n", command->code); switch (command->code) { case MTP_OP_GET_DEVICE_INFO: usbd_mtp_get_device_info(); break; case MTP_OP_OPEN_SESSION: usbd_mtp_open_session(); break; case MTP_OP_CLOSE_SESSION: break; case MTP_OP_GET_STORAGE_IDS: usbd_mtp_get_storage_ids(); break; case MTP_OP_GET_STORAGE_INFO: usbd_mtp_get_storage_info(); break; case MTP_OP_GET_OBJECT_HANDLES: usbd_mtp_get_object_handles(); break; case MTP_OP_GET_OBJECT_INFO: usbd_mtp_get_object_info(); break; case MTP_OP_GET_OBJECT_PROP_REFERENCES: break; case MTP_OP_GET_OBJECT_PROPS_SUPPORTED: usbd_mtp_get_object_props_supported(); break; case MTP_OP_GET_OBJECT_PROP_DESC: usbd_mtp_get_object_prop_desc(); break; case MTP_OP_GET_OBJECT_PROPLIST: usbd_mtp_get_object_prop_list(); break; case MTP_OP_GET_OBJECT_PROP_VALUE: break; case MTP_OP_GET_DEVICE_PROP_DESC: usbd_mtp_get_device_prop_desc(); break; case MTP_OP_GET_OBJECT: break; case MTP_OP_SEND_OBJECT_INFO: break; case MTP_OP_SEND_OBJECT: break; case MTP_OP_DELETE_OBJECT: break; default: break; } return 0; } static void usbd_mtp_bulk_out(uint8_t ep, uint32_t nbytes) { switch (g_usbd_mtp.stage) { case MTP_READ_COMMAND: usbd_mtp_decode_command(&g_usbd_mtp.con_command); break; case MTP_DATA_OUT: break; default: break; } } static void usbd_mtp_bulk_in(uint8_t ep, uint32_t nbytes) { printf("send:%d\r\n", nbytes); switch (g_usbd_mtp.stage) { case MTP_DATA_IN: break; case MTP_SEND_RESPONSE: usbd_mtp_send_response(MTP_RESPONSE_OK); break; case MTP_WAIT_RESPONSE: USB_LOG_DBG("Start reading command\r\n"); g_usbd_mtp.stage = MTP_READ_COMMAND; usbd_ep_start_read(mtp_ep_data[MTP_OUT_EP_IDX].ep_addr, (uint8_t *)&g_usbd_mtp.con_command, MTP_BULK_EP_MPS); break; default: break; } } static void mtp_notify_handler(uint8_t event, void *arg) { switch (event) { case USBD_EVENT_RESET: break; case USBD_EVENT_CONFIGURED: USB_LOG_DBG("Start reading command\r\n"); g_usbd_mtp.stage = MTP_READ_COMMAND; usbd_ep_start_read(mtp_ep_data[MTP_OUT_EP_IDX].ep_addr, (uint8_t *)&g_usbd_mtp.con_command, MTP_BULK_EP_MPS); break; default: break; } } struct usbd_interface *usbd_mtp_init_intf(struct usbd_interface *intf, const uint8_t out_ep, const uint8_t in_ep, const uint8_t int_ep) { intf->class_interface_handler = mtp_class_interface_request_handler; intf->class_endpoint_handler = NULL; intf->vendor_handler = NULL; intf->notify_handler = mtp_notify_handler; mtp_ep_data[MTP_OUT_EP_IDX].ep_addr = out_ep; mtp_ep_data[MTP_OUT_EP_IDX].ep_cb = usbd_mtp_bulk_out; mtp_ep_data[MTP_IN_EP_IDX].ep_addr = in_ep; mtp_ep_data[MTP_IN_EP_IDX].ep_cb = usbd_mtp_bulk_in; mtp_ep_data[MTP_INT_EP_IDX].ep_addr = int_ep; mtp_ep_data[MTP_INT_EP_IDX].ep_cb = NULL; usbd_add_endpoint(&mtp_ep_data[MTP_OUT_EP_IDX]); usbd_add_endpoint(&mtp_ep_data[MTP_IN_EP_IDX]); usbd_add_endpoint(&mtp_ep_data[MTP_INT_EP_IDX]); return intf; }