/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "MtpDescriptors.h" #include "MtpDebug.h" const struct usb_interface_descriptor mtp_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bNumEndpoints = 3, .bInterfaceClass = USB_CLASS_STILL_IMAGE, .bInterfaceSubClass = 1, .bInterfaceProtocol = 1, .iInterface = 1, }; const struct usb_interface_descriptor ptp_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bNumEndpoints = 3, .bInterfaceClass = USB_CLASS_STILL_IMAGE, .bInterfaceSubClass = 1, .bInterfaceProtocol = 1, }; const struct usb_endpoint_descriptor_no_audio fs_sink = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 1 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = MAX_PACKET_SIZE_FS, }; const struct usb_endpoint_descriptor_no_audio fs_source = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = MAX_PACKET_SIZE_FS, }; const struct usb_endpoint_descriptor_no_audio intr = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 3 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = MAX_PACKET_SIZE_EV, .bInterval = 6, }; const struct usb_endpoint_descriptor_no_audio hs_sink = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 1 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = MAX_PACKET_SIZE_HS, }; const struct usb_endpoint_descriptor_no_audio hs_source = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = MAX_PACKET_SIZE_HS, }; const struct usb_endpoint_descriptor_no_audio ss_sink = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 1 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = MAX_PACKET_SIZE_SS, }; const struct usb_endpoint_descriptor_no_audio ss_source = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = MAX_PACKET_SIZE_SS, }; const struct usb_ss_ep_comp_descriptor ss_sink_comp = { .bLength = sizeof(ss_sink_comp), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, .bMaxBurst = 6, }; const struct usb_ss_ep_comp_descriptor ss_source_comp = { .bLength = sizeof(ss_source_comp), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, .bMaxBurst = 6, }; const struct usb_ss_ep_comp_descriptor ss_intr_comp = { .bLength = sizeof(ss_intr_comp), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, }; const struct func_desc mtp_fs_descriptors = { .intf = mtp_interface_desc, .sink = fs_sink, .source = fs_source, .intr = intr, }; const struct func_desc mtp_hs_descriptors = { .intf = mtp_interface_desc, .sink = hs_sink, .source = hs_source, .intr = intr, }; const struct ss_func_desc mtp_ss_descriptors = { .intf = mtp_interface_desc, .sink = ss_sink, .sink_comp = ss_sink_comp, .source = ss_source, .source_comp = ss_source_comp, .intr = intr, .intr_comp = ss_intr_comp, }; const struct func_desc ptp_fs_descriptors = { .intf = ptp_interface_desc, .sink = fs_sink, .source = fs_source, .intr = intr, }; const struct func_desc ptp_hs_descriptors = { .intf = ptp_interface_desc, .sink = hs_sink, .source = hs_source, .intr = intr, }; const struct ss_func_desc ptp_ss_descriptors = { .intf = ptp_interface_desc, .sink = ss_sink, .sink_comp = ss_sink_comp, .source = ss_source, .source_comp = ss_source_comp, .intr = intr, .intr_comp = ss_intr_comp, }; const struct functionfs_strings mtp_strings = { .header = { .magic = htole32(FUNCTIONFS_STRINGS_MAGIC), .length = htole32(sizeof(mtp_strings)), .str_count = htole32(1), .lang_count = htole32(1), }, .lang0 = { .code = htole16(0x0409), .str1 = STR_INTERFACE, }, }; const struct usb_os_desc_header mtp_os_desc_header = { .interface = htole32(1), .dwLength = htole32(sizeof(usb_os_desc_header) + sizeof(usb_ext_compat_desc)), .bcdVersion = htole16(1), .wIndex = htole16(4), .bCount = htole16(1), .Reserved = htole16(0), }; const struct usb_ext_compat_desc mtp_os_desc_compat = { .bFirstInterfaceNumber = 0, .Reserved1 = htole32(1), .CompatibleID = { 'M', 'T', 'P' }, .SubCompatibleID = {0}, .Reserved2 = {0}, }; const struct usb_ext_compat_desc ptp_os_desc_compat = { .bFirstInterfaceNumber = 0, .Reserved1 = htole32(1), .CompatibleID = { 'P', 'T', 'P' }, .SubCompatibleID = {0}, .Reserved2 = {0}, }; const struct desc_v2 mtp_desc_v2 = { .header = { .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), .length = htole32(sizeof(struct desc_v2)), .flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC, }, .fs_count = 4, .hs_count = 4, .ss_count = 7, .os_count = 1, .fs_descs = mtp_fs_descriptors, .hs_descs = mtp_hs_descriptors, .ss_descs = mtp_ss_descriptors, .os_header = mtp_os_desc_header, .os_desc = mtp_os_desc_compat, }; const struct desc_v2 ptp_desc_v2 = { .header = { .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), .length = htole32(sizeof(struct desc_v2)), .flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC, }, .fs_count = 4, .hs_count = 4, .ss_count = 7, .os_count = 1, .fs_descs = ptp_fs_descriptors, .hs_descs = ptp_hs_descriptors, .ss_descs = ptp_ss_descriptors, .os_header = mtp_os_desc_header, .os_desc = ptp_os_desc_compat, }; const struct desc_v1 mtp_desc_v1 = { .header = { .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC), .length = htole32(sizeof(struct desc_v1)), .fs_count = 4, .hs_count = 4, }, .fs_descs = mtp_fs_descriptors, .hs_descs = mtp_hs_descriptors, }; const struct desc_v1 ptp_desc_v1 = { .header = { .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC), .length = htole32(sizeof(struct desc_v1)), .fs_count = 4, .hs_count = 4, }, .fs_descs = ptp_fs_descriptors, .hs_descs = ptp_hs_descriptors, }; bool writeDescriptors(int fd, bool ptp) { ssize_t ret = TEMP_FAILURE_RETRY(write(fd, &(ptp ? ptp_desc_v2 : mtp_desc_v2), sizeof(desc_v2))); if (ret < 0) { MTPE("Switching to V1 descriptor format\n"); ret = TEMP_FAILURE_RETRY(write(fd, &(ptp ? ptp_desc_v1 : mtp_desc_v1), sizeof(desc_v1))); if (ret < 0) { MTPE("Writing descriptors failed\n"); return false; } } ret = TEMP_FAILURE_RETRY(write(fd, &mtp_strings, sizeof(mtp_strings))); if (ret < 0) { MTPE("Writing strings failed\n"); return false; } property_set("sys.usb.ffs.mtp.ready", "1"); return true; }