1840a178cSGerd Hoffmann /* 2840a178cSGerd Hoffmann * Media Transfer Protocol implementation, backed by host filesystem. 3840a178cSGerd Hoffmann * 4840a178cSGerd Hoffmann * Copyright Red Hat, Inc 2014 5840a178cSGerd Hoffmann * 6840a178cSGerd Hoffmann * Author: 7840a178cSGerd Hoffmann * Gerd Hoffmann <kraxel@redhat.com> 8840a178cSGerd Hoffmann * 9840a178cSGerd Hoffmann * This code is licensed under the GPL v2 or later. 10840a178cSGerd Hoffmann */ 11840a178cSGerd Hoffmann 12e532b2e0SPeter Maydell #include "qemu/osdep.h" 13da34e65cSMarkus Armbruster #include "qapi/error.h" 1447287c27SDaniel P. Berrangé #include "qemu/error-report.h" 15840a178cSGerd Hoffmann #include <wchar.h> 16840a178cSGerd Hoffmann #include <dirent.h> 17*34b55848SBin Meng #include <glib/gstdio.h> 18840a178cSGerd Hoffmann #include <sys/statvfs.h> 1947287c27SDaniel P. Berrangé 20840a178cSGerd Hoffmann 21840a178cSGerd Hoffmann #include "qemu/iov.h" 220b8fa32fSMarkus Armbruster #include "qemu/module.h" 2347287c27SDaniel P. Berrangé #include "qemu/filemonitor.h" 24840a178cSGerd Hoffmann #include "trace.h" 25a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 26840a178cSGerd Hoffmann #include "hw/usb.h" 27d6454270SMarkus Armbruster #include "migration/vmstate.h" 28463581a8SMichael S. Tsirkin #include "desc.h" 29179fcf8aSBandan Das #include "qemu/units.h" 30db1015e9SEduardo Habkost #include "qom/object.h" 31840a178cSGerd Hoffmann 32840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 33840a178cSGerd Hoffmann 34840a178cSGerd Hoffmann enum mtp_container_type { 35840a178cSGerd Hoffmann TYPE_COMMAND = 1, 36840a178cSGerd Hoffmann TYPE_DATA = 2, 37840a178cSGerd Hoffmann TYPE_RESPONSE = 3, 38840a178cSGerd Hoffmann TYPE_EVENT = 4, 39840a178cSGerd Hoffmann }; 40840a178cSGerd Hoffmann 41c1ef0f25SBandan Das /* MTP write stage, for internal use only */ 42c1ef0f25SBandan Das enum mtp_write_status { 43c1ef0f25SBandan Das WRITE_START = 1, 44c1ef0f25SBandan Das WRITE_CONTINUE = 2, 45c1ef0f25SBandan Das WRITE_END = 3, 46c1ef0f25SBandan Das }; 47c1ef0f25SBandan Das 48840a178cSGerd Hoffmann enum mtp_code { 49840a178cSGerd Hoffmann /* command codes */ 50840a178cSGerd Hoffmann CMD_GET_DEVICE_INFO = 0x1001, 51840a178cSGerd Hoffmann CMD_OPEN_SESSION = 0x1002, 52840a178cSGerd Hoffmann CMD_CLOSE_SESSION = 0x1003, 53840a178cSGerd Hoffmann CMD_GET_STORAGE_IDS = 0x1004, 54840a178cSGerd Hoffmann CMD_GET_STORAGE_INFO = 0x1005, 55840a178cSGerd Hoffmann CMD_GET_NUM_OBJECTS = 0x1006, 56840a178cSGerd Hoffmann CMD_GET_OBJECT_HANDLES = 0x1007, 57840a178cSGerd Hoffmann CMD_GET_OBJECT_INFO = 0x1008, 58840a178cSGerd Hoffmann CMD_GET_OBJECT = 0x1009, 59ec6206a6SBandan Das CMD_DELETE_OBJECT = 0x100b, 6053735befSBandan Das CMD_SEND_OBJECT_INFO = 0x100c, 6188d5f381SBandan Das CMD_SEND_OBJECT = 0x100d, 62840a178cSGerd Hoffmann CMD_GET_PARTIAL_OBJECT = 0x101b, 6367f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROPS_SUPPORTED = 0x9801, 6467f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROP_DESC = 0x9802, 6567f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROP_VALUE = 0x9803, 66840a178cSGerd Hoffmann 67840a178cSGerd Hoffmann /* response codes */ 68840a178cSGerd Hoffmann RES_OK = 0x2001, 6922513a9bSGerd Hoffmann RES_GENERAL_ERROR = 0x2002, 70840a178cSGerd Hoffmann RES_SESSION_NOT_OPEN = 0x2003, 71840a178cSGerd Hoffmann RES_INVALID_TRANSACTION_ID = 0x2004, 72840a178cSGerd Hoffmann RES_OPERATION_NOT_SUPPORTED = 0x2005, 73840a178cSGerd Hoffmann RES_PARAMETER_NOT_SUPPORTED = 0x2006, 748ebb8763SGerd Hoffmann RES_INCOMPLETE_TRANSFER = 0x2007, 75840a178cSGerd Hoffmann RES_INVALID_STORAGE_ID = 0x2008, 76840a178cSGerd Hoffmann RES_INVALID_OBJECT_HANDLE = 0x2009, 7767f3ef0cSIsaac Lozano RES_INVALID_OBJECT_FORMAT_CODE = 0x200b, 7888d5f381SBandan Das RES_STORE_FULL = 0x200c, 79ec6206a6SBandan Das RES_STORE_READ_ONLY = 0x200e, 80ec6206a6SBandan Das RES_PARTIAL_DELETE = 0x2012, 8153735befSBandan Das RES_STORE_NOT_AVAILABLE = 0x2013, 82840a178cSGerd Hoffmann RES_SPEC_BY_FORMAT_UNSUPPORTED = 0x2014, 8388d5f381SBandan Das RES_INVALID_OBJECTINFO = 0x2015, 8453735befSBandan Das RES_DESTINATION_UNSUPPORTED = 0x2020, 85840a178cSGerd Hoffmann RES_INVALID_PARENT_OBJECT = 0x201a, 86840a178cSGerd Hoffmann RES_INVALID_PARAMETER = 0x201d, 87840a178cSGerd Hoffmann RES_SESSION_ALREADY_OPEN = 0x201e, 8867f3ef0cSIsaac Lozano RES_INVALID_OBJECT_PROP_CODE = 0xA801, 89840a178cSGerd Hoffmann 90840a178cSGerd Hoffmann /* format codes */ 91840a178cSGerd Hoffmann FMT_UNDEFINED_OBJECT = 0x3000, 92840a178cSGerd Hoffmann FMT_ASSOCIATION = 0x3001, 938e3e3897SBandan Das 948e3e3897SBandan Das /* event codes */ 9547bff13cSBandan Das EVT_CANCEL_TRANSACTION = 0x4001, 968e3e3897SBandan Das EVT_OBJ_ADDED = 0x4002, 978e3e3897SBandan Das EVT_OBJ_REMOVED = 0x4003, 988e3e3897SBandan Das EVT_OBJ_INFO_CHANGED = 0x4007, 9967f3ef0cSIsaac Lozano 10067f3ef0cSIsaac Lozano /* object properties */ 10167f3ef0cSIsaac Lozano PROP_STORAGE_ID = 0xDC01, 10267f3ef0cSIsaac Lozano PROP_OBJECT_FORMAT = 0xDC02, 10367f3ef0cSIsaac Lozano PROP_OBJECT_COMPRESSED_SIZE = 0xDC04, 10467f3ef0cSIsaac Lozano PROP_PARENT_OBJECT = 0xDC0B, 10567f3ef0cSIsaac Lozano PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER = 0xDC41, 10667f3ef0cSIsaac Lozano PROP_NAME = 0xDC44, 10767f3ef0cSIsaac Lozano }; 10867f3ef0cSIsaac Lozano 10967f3ef0cSIsaac Lozano enum mtp_data_type { 11067f3ef0cSIsaac Lozano DATA_TYPE_UINT16 = 0x0004, 11167f3ef0cSIsaac Lozano DATA_TYPE_UINT32 = 0x0006, 11267f3ef0cSIsaac Lozano DATA_TYPE_UINT64 = 0x0008, 11367f3ef0cSIsaac Lozano DATA_TYPE_UINT128 = 0x000a, 11467f3ef0cSIsaac Lozano DATA_TYPE_STRING = 0xffff, 115840a178cSGerd Hoffmann }; 116840a178cSGerd Hoffmann 117840a178cSGerd Hoffmann typedef struct { 118840a178cSGerd Hoffmann uint32_t length; 119840a178cSGerd Hoffmann uint16_t type; 120840a178cSGerd Hoffmann uint16_t code; 121840a178cSGerd Hoffmann uint32_t trans; 122840a178cSGerd Hoffmann } QEMU_PACKED mtp_container; 123840a178cSGerd Hoffmann 124840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 125840a178cSGerd Hoffmann 126840a178cSGerd Hoffmann typedef struct MTPState MTPState; 127840a178cSGerd Hoffmann typedef struct MTPControl MTPControl; 128840a178cSGerd Hoffmann typedef struct MTPData MTPData; 129840a178cSGerd Hoffmann typedef struct MTPObject MTPObject; 130840a178cSGerd Hoffmann 131840a178cSGerd Hoffmann enum { 132840a178cSGerd Hoffmann EP_DATA_IN = 1, 133840a178cSGerd Hoffmann EP_DATA_OUT, 134840a178cSGerd Hoffmann EP_EVENT, 135840a178cSGerd Hoffmann }; 136840a178cSGerd Hoffmann 1378e3e3897SBandan Das typedef struct MTPMonEntry MTPMonEntry; 1388e3e3897SBandan Das 1398e3e3897SBandan Das struct MTPMonEntry { 1408e3e3897SBandan Das uint32_t event; 1418e3e3897SBandan Das uint32_t handle; 1428e3e3897SBandan Das 1438e3e3897SBandan Das QTAILQ_ENTRY(MTPMonEntry) next; 1448e3e3897SBandan Das }; 1458e3e3897SBandan Das 146840a178cSGerd Hoffmann struct MTPControl { 147840a178cSGerd Hoffmann uint16_t code; 148840a178cSGerd Hoffmann uint32_t trans; 149840a178cSGerd Hoffmann int argc; 150840a178cSGerd Hoffmann uint32_t argv[5]; 151840a178cSGerd Hoffmann }; 152840a178cSGerd Hoffmann 153840a178cSGerd Hoffmann struct MTPData { 154840a178cSGerd Hoffmann uint16_t code; 155840a178cSGerd Hoffmann uint32_t trans; 1568a5865f3SIsaac Lozano uint64_t offset; 1578a5865f3SIsaac Lozano uint64_t length; 1583e096650SBandan Das uint64_t alloc; 159840a178cSGerd Hoffmann uint8_t *data; 160840a178cSGerd Hoffmann bool first; 1613e096650SBandan Das /* Used for >4G file sizes */ 1623e096650SBandan Das bool pending; 163840a178cSGerd Hoffmann int fd; 164c1ef0f25SBandan Das uint8_t write_status; 165c1ef0f25SBandan Das /* Internal pointer per every MTP_WRITE_BUF_SZ */ 166c1ef0f25SBandan Das uint64_t data_offset; 167840a178cSGerd Hoffmann }; 168840a178cSGerd Hoffmann 169840a178cSGerd Hoffmann struct MTPObject { 170840a178cSGerd Hoffmann uint32_t handle; 171840a178cSGerd Hoffmann uint16_t format; 172840a178cSGerd Hoffmann char *name; 173840a178cSGerd Hoffmann char *path; 174840a178cSGerd Hoffmann struct stat stat; 17547287c27SDaniel P. Berrangé /* file monitor watch id */ 176b4682a63SDaniel P. Berrangé int64_t watchid; 177840a178cSGerd Hoffmann MTPObject *parent; 17836084d7eSGerd Hoffmann uint32_t nchildren; 1794c7a67f5SBandan Das QLIST_HEAD(, MTPObject) children; 1804c7a67f5SBandan Das QLIST_ENTRY(MTPObject) list; 18136084d7eSGerd Hoffmann bool have_children; 182840a178cSGerd Hoffmann QTAILQ_ENTRY(MTPObject) next; 183840a178cSGerd Hoffmann }; 184840a178cSGerd Hoffmann 185840a178cSGerd Hoffmann struct MTPState { 186840a178cSGerd Hoffmann USBDevice dev; 187840a178cSGerd Hoffmann char *root; 188840a178cSGerd Hoffmann char *desc; 189840a178cSGerd Hoffmann uint32_t flags; 190840a178cSGerd Hoffmann 191840a178cSGerd Hoffmann MTPData *data_in; 192840a178cSGerd Hoffmann MTPData *data_out; 193840a178cSGerd Hoffmann MTPControl *result; 194840a178cSGerd Hoffmann uint32_t session; 195840a178cSGerd Hoffmann uint32_t next_handle; 196ec6206a6SBandan Das bool readonly; 197840a178cSGerd Hoffmann 198840a178cSGerd Hoffmann QTAILQ_HEAD(, MTPObject) objects; 19947287c27SDaniel P. Berrangé QFileMonitor *file_monitor; 200eae3eb3eSPaolo Bonzini QTAILQ_HEAD(, MTPMonEntry) events; 20188d5f381SBandan Das /* Responder is expecting a write operation */ 20288d5f381SBandan Das bool write_pending; 20388d5f381SBandan Das struct { 20488d5f381SBandan Das uint32_t parent_handle; 20588d5f381SBandan Das uint16_t format; 20688d5f381SBandan Das uint32_t size; 20788d5f381SBandan Das char *filename; 20888d5f381SBandan Das } dataset; 209840a178cSGerd Hoffmann }; 210840a178cSGerd Hoffmann 21153735befSBandan Das /* 21253735befSBandan Das * ObjectInfo dataset received from initiator 21353735befSBandan Das * Fields we don't care about are ignored 21453735befSBandan Das */ 21553735befSBandan Das typedef struct { 21653735befSBandan Das uint32_t storage_id; /*unused*/ 21753735befSBandan Das uint16_t format; 21853735befSBandan Das uint16_t protection_status; /*unused*/ 21953735befSBandan Das uint32_t size; 22053735befSBandan Das uint16_t thumb_format; /*unused*/ 22153735befSBandan Das uint32_t thumb_comp_sz; /*unused*/ 22253735befSBandan Das uint32_t thumb_pix_width; /*unused*/ 22353735befSBandan Das uint32_t thumb_pix_height; /*unused*/ 22453735befSBandan Das uint32_t image_pix_width; /*unused*/ 22553735befSBandan Das uint32_t image_pix_height; /*unused*/ 22653735befSBandan Das uint32_t image_bit_depth; /*unused*/ 22753735befSBandan Das uint32_t parent; /*unused*/ 22853735befSBandan Das uint16_t assoc_type; 22953735befSBandan Das uint32_t assoc_desc; 23053735befSBandan Das uint32_t seq_no; /*unused*/ 23153735befSBandan Das uint8_t length; /*part of filename field*/ 2321259f27eSDaniel P. Berrangé uint8_t filename[0]; /* UTF-16 encoded */ 23353735befSBandan Das char date_created[0]; /*unused*/ 23453735befSBandan Das char date_modified[0]; /*unused*/ 23553735befSBandan Das char keywords[0]; /*unused*/ 23653735befSBandan Das /* string and other data follows */ 23753735befSBandan Das } QEMU_PACKED ObjectInfo; 23853735befSBandan Das 2397c03a899SGonglei #define TYPE_USB_MTP "usb-mtp" 2408063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(MTPState, USB_MTP) 2417c03a899SGonglei 242840a178cSGerd Hoffmann #define QEMU_STORAGE_ID 0x00010001 243840a178cSGerd Hoffmann 244840a178cSGerd Hoffmann #define MTP_FLAG_WRITABLE 0 245840a178cSGerd Hoffmann 246840a178cSGerd Hoffmann #define FLAG_SET(_mtp, _flag) ((_mtp)->flags & (1 << (_flag))) 247840a178cSGerd Hoffmann 248840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 249840a178cSGerd Hoffmann 250840a178cSGerd Hoffmann #define MTP_MANUFACTURER "QEMU" 251840a178cSGerd Hoffmann #define MTP_PRODUCT "QEMU filesharing" 252179fcf8aSBandan Das #define MTP_WRITE_BUF_SZ (512 * KiB) 253840a178cSGerd Hoffmann 254840a178cSGerd Hoffmann enum { 255840a178cSGerd Hoffmann STR_MANUFACTURER = 1, 256840a178cSGerd Hoffmann STR_PRODUCT, 257840a178cSGerd Hoffmann STR_SERIALNUMBER, 25813d54125SGerd Hoffmann STR_MTP, 259840a178cSGerd Hoffmann STR_CONFIG_FULL, 260840a178cSGerd Hoffmann STR_CONFIG_HIGH, 261840a178cSGerd Hoffmann STR_CONFIG_SUPER, 262840a178cSGerd Hoffmann }; 263840a178cSGerd Hoffmann 264840a178cSGerd Hoffmann static const USBDescStrings desc_strings = { 265840a178cSGerd Hoffmann [STR_MANUFACTURER] = MTP_MANUFACTURER, 266840a178cSGerd Hoffmann [STR_PRODUCT] = MTP_PRODUCT, 267840a178cSGerd Hoffmann [STR_SERIALNUMBER] = "34617", 26813d54125SGerd Hoffmann [STR_MTP] = "MTP", 269840a178cSGerd Hoffmann [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", 270840a178cSGerd Hoffmann [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", 271840a178cSGerd Hoffmann [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", 272840a178cSGerd Hoffmann }; 273840a178cSGerd Hoffmann 274840a178cSGerd Hoffmann static const USBDescIface desc_iface_full = { 275840a178cSGerd Hoffmann .bInterfaceNumber = 0, 276840a178cSGerd Hoffmann .bNumEndpoints = 3, 277840a178cSGerd Hoffmann .bInterfaceClass = USB_CLASS_STILL_IMAGE, 278840a178cSGerd Hoffmann .bInterfaceSubClass = 0x01, 279840a178cSGerd Hoffmann .bInterfaceProtocol = 0x01, 28013d54125SGerd Hoffmann .iInterface = STR_MTP, 281840a178cSGerd Hoffmann .eps = (USBDescEndpoint[]) { 282840a178cSGerd Hoffmann { 283840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | EP_DATA_IN, 284840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 285840a178cSGerd Hoffmann .wMaxPacketSize = 64, 286840a178cSGerd Hoffmann },{ 287840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | EP_DATA_OUT, 288840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 289840a178cSGerd Hoffmann .wMaxPacketSize = 64, 290840a178cSGerd Hoffmann },{ 291840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | EP_EVENT, 292840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_INT, 29393d592e3SBandan Das .wMaxPacketSize = 64, 294840a178cSGerd Hoffmann .bInterval = 0x0a, 295840a178cSGerd Hoffmann }, 296840a178cSGerd Hoffmann } 297840a178cSGerd Hoffmann }; 298840a178cSGerd Hoffmann 299840a178cSGerd Hoffmann static const USBDescDevice desc_device_full = { 300840a178cSGerd Hoffmann .bcdUSB = 0x0200, 301840a178cSGerd Hoffmann .bMaxPacketSize0 = 8, 302840a178cSGerd Hoffmann .bNumConfigurations = 1, 303840a178cSGerd Hoffmann .confs = (USBDescConfig[]) { 304840a178cSGerd Hoffmann { 305840a178cSGerd Hoffmann .bNumInterfaces = 1, 306840a178cSGerd Hoffmann .bConfigurationValue = 1, 307840a178cSGerd Hoffmann .iConfiguration = STR_CONFIG_FULL, 308840a178cSGerd Hoffmann .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, 309840a178cSGerd Hoffmann .bMaxPower = 2, 310840a178cSGerd Hoffmann .nif = 1, 311840a178cSGerd Hoffmann .ifs = &desc_iface_full, 312840a178cSGerd Hoffmann }, 313840a178cSGerd Hoffmann }, 314840a178cSGerd Hoffmann }; 315840a178cSGerd Hoffmann 316840a178cSGerd Hoffmann static const USBDescIface desc_iface_high = { 317840a178cSGerd Hoffmann .bInterfaceNumber = 0, 318840a178cSGerd Hoffmann .bNumEndpoints = 3, 319840a178cSGerd Hoffmann .bInterfaceClass = USB_CLASS_STILL_IMAGE, 320840a178cSGerd Hoffmann .bInterfaceSubClass = 0x01, 321840a178cSGerd Hoffmann .bInterfaceProtocol = 0x01, 32213d54125SGerd Hoffmann .iInterface = STR_MTP, 323840a178cSGerd Hoffmann .eps = (USBDescEndpoint[]) { 324840a178cSGerd Hoffmann { 325840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | EP_DATA_IN, 326840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 327840a178cSGerd Hoffmann .wMaxPacketSize = 512, 328840a178cSGerd Hoffmann },{ 329840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | EP_DATA_OUT, 330840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 331840a178cSGerd Hoffmann .wMaxPacketSize = 512, 332840a178cSGerd Hoffmann },{ 333840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | EP_EVENT, 334840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_INT, 33593d592e3SBandan Das .wMaxPacketSize = 64, 336840a178cSGerd Hoffmann .bInterval = 0x0a, 337840a178cSGerd Hoffmann }, 338840a178cSGerd Hoffmann } 339840a178cSGerd Hoffmann }; 340840a178cSGerd Hoffmann 341840a178cSGerd Hoffmann static const USBDescDevice desc_device_high = { 342840a178cSGerd Hoffmann .bcdUSB = 0x0200, 343840a178cSGerd Hoffmann .bMaxPacketSize0 = 64, 344840a178cSGerd Hoffmann .bNumConfigurations = 1, 345840a178cSGerd Hoffmann .confs = (USBDescConfig[]) { 346840a178cSGerd Hoffmann { 347840a178cSGerd Hoffmann .bNumInterfaces = 1, 348840a178cSGerd Hoffmann .bConfigurationValue = 1, 349840a178cSGerd Hoffmann .iConfiguration = STR_CONFIG_HIGH, 350840a178cSGerd Hoffmann .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, 351840a178cSGerd Hoffmann .bMaxPower = 2, 352840a178cSGerd Hoffmann .nif = 1, 353840a178cSGerd Hoffmann .ifs = &desc_iface_high, 354840a178cSGerd Hoffmann }, 355840a178cSGerd Hoffmann }, 356840a178cSGerd Hoffmann }; 357840a178cSGerd Hoffmann 358840a178cSGerd Hoffmann static const USBDescMSOS desc_msos = { 359840a178cSGerd Hoffmann .CompatibleID = "MTP", 360840a178cSGerd Hoffmann .SelectiveSuspendEnabled = true, 361840a178cSGerd Hoffmann }; 362840a178cSGerd Hoffmann 363840a178cSGerd Hoffmann static const USBDesc desc = { 364840a178cSGerd Hoffmann .id = { 365840a178cSGerd Hoffmann .idVendor = 0x46f4, /* CRC16() of "QEMU" */ 366840a178cSGerd Hoffmann .idProduct = 0x0004, 367840a178cSGerd Hoffmann .bcdDevice = 0, 368840a178cSGerd Hoffmann .iManufacturer = STR_MANUFACTURER, 369840a178cSGerd Hoffmann .iProduct = STR_PRODUCT, 370840a178cSGerd Hoffmann .iSerialNumber = STR_SERIALNUMBER, 371840a178cSGerd Hoffmann }, 372840a178cSGerd Hoffmann .full = &desc_device_full, 373840a178cSGerd Hoffmann .high = &desc_device_high, 374840a178cSGerd Hoffmann .str = desc_strings, 375840a178cSGerd Hoffmann .msos = &desc_msos, 376840a178cSGerd Hoffmann }; 377840a178cSGerd Hoffmann 378840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 379840a178cSGerd Hoffmann 380840a178cSGerd Hoffmann static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle, 381888e0359SDaniel P. Berrangé MTPObject *parent, const char *name) 382840a178cSGerd Hoffmann { 383840a178cSGerd Hoffmann MTPObject *o = g_new0(MTPObject, 1); 384840a178cSGerd Hoffmann 385840a178cSGerd Hoffmann if (name[0] == '.') { 386840a178cSGerd Hoffmann goto ignore; 387840a178cSGerd Hoffmann } 388840a178cSGerd Hoffmann 38947287c27SDaniel P. Berrangé o->watchid = -1; 390840a178cSGerd Hoffmann o->handle = handle; 391840a178cSGerd Hoffmann o->parent = parent; 392840a178cSGerd Hoffmann o->name = g_strdup(name); 393840a178cSGerd Hoffmann if (parent == NULL) { 394840a178cSGerd Hoffmann o->path = g_strdup(name); 395840a178cSGerd Hoffmann } else { 396840a178cSGerd Hoffmann o->path = g_strdup_printf("%s/%s", parent->path, name); 397840a178cSGerd Hoffmann } 398840a178cSGerd Hoffmann 399840a178cSGerd Hoffmann if (lstat(o->path, &o->stat) != 0) { 400840a178cSGerd Hoffmann goto ignore; 401840a178cSGerd Hoffmann } 402840a178cSGerd Hoffmann if (S_ISREG(o->stat.st_mode)) { 403840a178cSGerd Hoffmann o->format = FMT_UNDEFINED_OBJECT; 404840a178cSGerd Hoffmann } else if (S_ISDIR(o->stat.st_mode)) { 405840a178cSGerd Hoffmann o->format = FMT_ASSOCIATION; 406840a178cSGerd Hoffmann } else { 407840a178cSGerd Hoffmann goto ignore; 408840a178cSGerd Hoffmann } 409840a178cSGerd Hoffmann 410840a178cSGerd Hoffmann if (access(o->path, R_OK) != 0) { 411840a178cSGerd Hoffmann goto ignore; 412840a178cSGerd Hoffmann } 413840a178cSGerd Hoffmann 4141c76551fSGerd Hoffmann trace_usb_mtp_object_alloc(s->dev.addr, o->handle, o->path); 415840a178cSGerd Hoffmann 416840a178cSGerd Hoffmann QTAILQ_INSERT_TAIL(&s->objects, o, next); 417840a178cSGerd Hoffmann return o; 418840a178cSGerd Hoffmann 419840a178cSGerd Hoffmann ignore: 420840a178cSGerd Hoffmann g_free(o->name); 421840a178cSGerd Hoffmann g_free(o->path); 422840a178cSGerd Hoffmann g_free(o); 423840a178cSGerd Hoffmann return NULL; 424840a178cSGerd Hoffmann } 425840a178cSGerd Hoffmann 426840a178cSGerd Hoffmann static void usb_mtp_object_free(MTPState *s, MTPObject *o) 427840a178cSGerd Hoffmann { 4284c7a67f5SBandan Das MTPObject *iter; 4294c7a67f5SBandan Das 4304c7a67f5SBandan Das if (!o) { 4314c7a67f5SBandan Das return; 4324c7a67f5SBandan Das } 433840a178cSGerd Hoffmann 4341c76551fSGerd Hoffmann trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path); 435840a178cSGerd Hoffmann 43647287c27SDaniel P. Berrangé if (o->watchid != -1 && s->file_monitor) { 43747287c27SDaniel P. Berrangé qemu_file_monitor_remove_watch(s->file_monitor, o->path, o->watchid); 43847287c27SDaniel P. Berrangé } 43947287c27SDaniel P. Berrangé 440840a178cSGerd Hoffmann QTAILQ_REMOVE(&s->objects, o, next); 4414c7a67f5SBandan Das if (o->parent) { 4424c7a67f5SBandan Das QLIST_REMOVE(o, list); 4434c7a67f5SBandan Das o->parent->nchildren--; 444840a178cSGerd Hoffmann } 4454c7a67f5SBandan Das 4464c7a67f5SBandan Das while (!QLIST_EMPTY(&o->children)) { 4474c7a67f5SBandan Das iter = QLIST_FIRST(&o->children); 4484c7a67f5SBandan Das usb_mtp_object_free(s, iter); 4494c7a67f5SBandan Das } 450840a178cSGerd Hoffmann g_free(o->name); 451840a178cSGerd Hoffmann g_free(o->path); 452840a178cSGerd Hoffmann g_free(o); 453840a178cSGerd Hoffmann } 454840a178cSGerd Hoffmann 455840a178cSGerd Hoffmann static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle) 456840a178cSGerd Hoffmann { 457840a178cSGerd Hoffmann MTPObject *o; 458840a178cSGerd Hoffmann 459840a178cSGerd Hoffmann QTAILQ_FOREACH(o, &s->objects, next) { 460840a178cSGerd Hoffmann if (o->handle == handle) { 461840a178cSGerd Hoffmann return o; 462840a178cSGerd Hoffmann } 463840a178cSGerd Hoffmann } 464840a178cSGerd Hoffmann return NULL; 465840a178cSGerd Hoffmann } 466840a178cSGerd Hoffmann 4674c7a67f5SBandan Das static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, 468888e0359SDaniel P. Berrangé const char *name) 4694c7a67f5SBandan Das { 4704c7a67f5SBandan Das MTPObject *child = 4714c7a67f5SBandan Das usb_mtp_object_alloc(s, s->next_handle++, o, name); 4724c7a67f5SBandan Das 4734c7a67f5SBandan Das if (child) { 4744c7a67f5SBandan Das trace_usb_mtp_add_child(s->dev.addr, child->handle, child->path); 4754c7a67f5SBandan Das QLIST_INSERT_HEAD(&o->children, child, list); 4764c7a67f5SBandan Das o->nchildren++; 4774c7a67f5SBandan Das 4784c7a67f5SBandan Das if (child->format == FMT_ASSOCIATION) { 4794c7a67f5SBandan Das QLIST_INIT(&child->children); 4804c7a67f5SBandan Das } 4814c7a67f5SBandan Das } 4824c7a67f5SBandan Das 4834c7a67f5SBandan Das return child; 4844c7a67f5SBandan Das } 4854c7a67f5SBandan Das 4868e3e3897SBandan Das static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, 487888e0359SDaniel P. Berrangé const char *name, int len) 4888e3e3897SBandan Das { 4898e3e3897SBandan Das MTPObject *iter; 4908e3e3897SBandan Das 49147287c27SDaniel P. Berrangé if (len == -1) { 49247287c27SDaniel P. Berrangé len = strlen(name); 49347287c27SDaniel P. Berrangé } 49447287c27SDaniel P. Berrangé 4958e3e3897SBandan Das QLIST_FOREACH(iter, &parent->children, list) { 4968e3e3897SBandan Das if (strncmp(iter->name, name, len) == 0) { 4978e3e3897SBandan Das return iter; 4988e3e3897SBandan Das } 4998e3e3897SBandan Das } 5008e3e3897SBandan Das 5018e3e3897SBandan Das return NULL; 5028e3e3897SBandan Das } 5038e3e3897SBandan Das 504b4682a63SDaniel P. Berrangé static MTPObject *usb_mtp_object_lookup_id(MTPState *s, int64_t id) 5058e3e3897SBandan Das { 5068e3e3897SBandan Das MTPObject *iter; 5078e3e3897SBandan Das 5088e3e3897SBandan Das QTAILQ_FOREACH(iter, &s->objects, next) { 50947287c27SDaniel P. Berrangé if (iter->watchid == id) { 5108e3e3897SBandan Das return iter; 5118e3e3897SBandan Das } 5128e3e3897SBandan Das } 5138e3e3897SBandan Das 5148e3e3897SBandan Das return NULL; 5158e3e3897SBandan Das } 5168e3e3897SBandan Das 517b4682a63SDaniel P. Berrangé static void file_monitor_event(int64_t id, 51847287c27SDaniel P. Berrangé QFileMonitorEvent ev, 51947287c27SDaniel P. Berrangé const char *name, 52047287c27SDaniel P. Berrangé void *opaque) 5218e3e3897SBandan Das { 52247287c27SDaniel P. Berrangé MTPState *s = opaque; 52347287c27SDaniel P. Berrangé MTPObject *parent = usb_mtp_object_lookup_id(s, id); 5248e3e3897SBandan Das MTPMonEntry *entry = NULL; 5258e3e3897SBandan Das MTPObject *o; 5268e3e3897SBandan Das 5278e3e3897SBandan Das if (!parent) { 52847287c27SDaniel P. Berrangé return; 5298e3e3897SBandan Das } 5308e3e3897SBandan Das 53147287c27SDaniel P. Berrangé switch (ev) { 53247287c27SDaniel P. Berrangé case QFILE_MONITOR_EVENT_CREATED: 53347287c27SDaniel P. Berrangé if (usb_mtp_object_lookup_name(parent, name, -1)) { 5348e3e3897SBandan Das /* Duplicate create event */ 53547287c27SDaniel P. Berrangé return; 5368e3e3897SBandan Das } 5378e3e3897SBandan Das entry = g_new0(MTPMonEntry, 1); 5388e3e3897SBandan Das entry->handle = s->next_handle; 5398e3e3897SBandan Das entry->event = EVT_OBJ_ADDED; 54047287c27SDaniel P. Berrangé o = usb_mtp_add_child(s, parent, name); 5418e3e3897SBandan Das if (!o) { 5428e3e3897SBandan Das g_free(entry); 54347287c27SDaniel P. Berrangé return; 5448e3e3897SBandan Das } 54547287c27SDaniel P. Berrangé trace_usb_mtp_file_monitor_event(s->dev.addr, name, "Obj Added"); 5468e3e3897SBandan Das break; 5478e3e3897SBandan Das 54847287c27SDaniel P. Berrangé case QFILE_MONITOR_EVENT_DELETED: 5498e3e3897SBandan Das /* 5508e3e3897SBandan Das * The kernel issues a IN_IGNORED event 5518e3e3897SBandan Das * when a dir containing a watchpoint is 5528e3e3897SBandan Das * deleted, so we don't have to delete the 5538e3e3897SBandan Das * watchpoint 5548e3e3897SBandan Das */ 55547287c27SDaniel P. Berrangé o = usb_mtp_object_lookup_name(parent, name, -1); 5568e3e3897SBandan Das if (!o) { 55747287c27SDaniel P. Berrangé return; 5588e3e3897SBandan Das } 5598e3e3897SBandan Das entry = g_new0(MTPMonEntry, 1); 5608e3e3897SBandan Das entry->handle = o->handle; 5618e3e3897SBandan Das entry->event = EVT_OBJ_REMOVED; 56247287c27SDaniel P. Berrangé trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, "Obj Deleted"); 563ec93e158SBandan Das usb_mtp_object_free(s, o); 5648e3e3897SBandan Das break; 5658e3e3897SBandan Das 56647287c27SDaniel P. Berrangé case QFILE_MONITOR_EVENT_MODIFIED: 56747287c27SDaniel P. Berrangé o = usb_mtp_object_lookup_name(parent, name, -1); 5688e3e3897SBandan Das if (!o) { 56947287c27SDaniel P. Berrangé return; 5708e3e3897SBandan Das } 5718e3e3897SBandan Das entry = g_new0(MTPMonEntry, 1); 5728e3e3897SBandan Das entry->handle = o->handle; 5738e3e3897SBandan Das entry->event = EVT_OBJ_INFO_CHANGED; 57447287c27SDaniel P. Berrangé trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, "Obj Modified"); 5758e3e3897SBandan Das break; 5768e3e3897SBandan Das 57747287c27SDaniel P. Berrangé case QFILE_MONITOR_EVENT_IGNORED: 57847287c27SDaniel P. Berrangé trace_usb_mtp_file_monitor_event(s->dev.addr, parent->path, 57947287c27SDaniel P. Berrangé "Obj parent dir ignored"); 58047287c27SDaniel P. Berrangé break; 58147287c27SDaniel P. Berrangé 58247287c27SDaniel P. Berrangé case QFILE_MONITOR_EVENT_ATTRIBUTES: 5838e3e3897SBandan Das break; 5848e3e3897SBandan Das 5858e3e3897SBandan Das default: 58647287c27SDaniel P. Berrangé g_assert_not_reached(); 5878e3e3897SBandan Das } 5888e3e3897SBandan Das 5898e3e3897SBandan Das if (entry) { 5908e3e3897SBandan Das QTAILQ_INSERT_HEAD(&s->events, entry, next); 5918e3e3897SBandan Das } 5928e3e3897SBandan Das } 5938e3e3897SBandan Das 59447287c27SDaniel P. Berrangé static void usb_mtp_file_monitor_cleanup(MTPState *s) 5958e3e3897SBandan Das { 596c22d5dcdSBandan Das MTPMonEntry *e, *p; 5978e3e3897SBandan Das 598c22d5dcdSBandan Das QTAILQ_FOREACH_SAFE(e, &s->events, next, p) { 5998e3e3897SBandan Das QTAILQ_REMOVE(&s->events, e, next); 6008e3e3897SBandan Das g_free(e); 6018e3e3897SBandan Das } 60247287c27SDaniel P. Berrangé 60347287c27SDaniel P. Berrangé qemu_file_monitor_free(s->file_monitor); 60447287c27SDaniel P. Berrangé s->file_monitor = NULL; 6058e3e3897SBandan Das } 6068e3e3897SBandan Das 6078e3e3897SBandan Das 608840a178cSGerd Hoffmann static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) 609840a178cSGerd Hoffmann { 610840a178cSGerd Hoffmann struct dirent *entry; 611840a178cSGerd Hoffmann DIR *dir; 612bab9df35SGerd Hoffmann int fd; 61347287c27SDaniel P. Berrangé Error *err = NULL; 614840a178cSGerd Hoffmann 61536084d7eSGerd Hoffmann if (o->have_children) { 61636084d7eSGerd Hoffmann return; 61736084d7eSGerd Hoffmann } 61836084d7eSGerd Hoffmann o->have_children = true; 61936084d7eSGerd Hoffmann 620bab9df35SGerd Hoffmann fd = open(o->path, O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); 621bab9df35SGerd Hoffmann if (fd < 0) { 622bab9df35SGerd Hoffmann return; 623bab9df35SGerd Hoffmann } 624bab9df35SGerd Hoffmann dir = fdopendir(fd); 625840a178cSGerd Hoffmann if (!dir) { 6266e3c1a68SLi Qiang close(fd); 627840a178cSGerd Hoffmann return; 628840a178cSGerd Hoffmann } 62947287c27SDaniel P. Berrangé 63047287c27SDaniel P. Berrangé if (s->file_monitor) { 631b4682a63SDaniel P. Berrangé int64_t id = qemu_file_monitor_add_watch(s->file_monitor, o->path, NULL, 63247287c27SDaniel P. Berrangé file_monitor_event, s, &err); 63347287c27SDaniel P. Berrangé if (id == -1) { 6345217f188SMarkus Armbruster error_reportf_err(err, 6355217f188SMarkus Armbruster "usb-mtp: failed to add watch for %s: ", 6365217f188SMarkus Armbruster o->path); 6378e3e3897SBandan Das } else { 63847287c27SDaniel P. Berrangé trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, 63947287c27SDaniel P. Berrangé "Watch Added"); 64047287c27SDaniel P. Berrangé o->watchid = id; 6418e3e3897SBandan Das } 64247287c27SDaniel P. Berrangé } 64347287c27SDaniel P. Berrangé 644840a178cSGerd Hoffmann while ((entry = readdir(dir)) != NULL) { 6454c7a67f5SBandan Das usb_mtp_add_child(s, o, entry->d_name); 646840a178cSGerd Hoffmann } 647840a178cSGerd Hoffmann closedir(dir); 648840a178cSGerd Hoffmann } 649840a178cSGerd Hoffmann 650840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 651840a178cSGerd Hoffmann 652840a178cSGerd Hoffmann static MTPData *usb_mtp_data_alloc(MTPControl *c) 653840a178cSGerd Hoffmann { 654840a178cSGerd Hoffmann MTPData *data = g_new0(MTPData, 1); 655840a178cSGerd Hoffmann 656840a178cSGerd Hoffmann data->code = c->code; 657840a178cSGerd Hoffmann data->trans = c->trans; 658840a178cSGerd Hoffmann data->fd = -1; 659840a178cSGerd Hoffmann data->first = true; 660840a178cSGerd Hoffmann return data; 661840a178cSGerd Hoffmann } 662840a178cSGerd Hoffmann 663840a178cSGerd Hoffmann static void usb_mtp_data_free(MTPData *data) 664840a178cSGerd Hoffmann { 665840a178cSGerd Hoffmann if (data == NULL) { 666840a178cSGerd Hoffmann return; 667840a178cSGerd Hoffmann } 668840a178cSGerd Hoffmann if (data->fd != -1) { 669840a178cSGerd Hoffmann close(data->fd); 670840a178cSGerd Hoffmann } 671840a178cSGerd Hoffmann g_free(data->data); 672840a178cSGerd Hoffmann g_free(data); 673840a178cSGerd Hoffmann } 674840a178cSGerd Hoffmann 675840a178cSGerd Hoffmann static void usb_mtp_realloc(MTPData *data, uint32_t bytes) 676840a178cSGerd Hoffmann { 677840a178cSGerd Hoffmann if (data->length + bytes <= data->alloc) { 678840a178cSGerd Hoffmann return; 679840a178cSGerd Hoffmann } 680840a178cSGerd Hoffmann data->alloc = (data->length + bytes + 0xff) & ~0xff; 681840a178cSGerd Hoffmann data->data = g_realloc(data->data, data->alloc); 682840a178cSGerd Hoffmann } 683840a178cSGerd Hoffmann 684840a178cSGerd Hoffmann static void usb_mtp_add_u8(MTPData *data, uint8_t val) 685840a178cSGerd Hoffmann { 686840a178cSGerd Hoffmann usb_mtp_realloc(data, 1); 687840a178cSGerd Hoffmann data->data[data->length++] = val; 688840a178cSGerd Hoffmann } 689840a178cSGerd Hoffmann 690840a178cSGerd Hoffmann static void usb_mtp_add_u16(MTPData *data, uint16_t val) 691840a178cSGerd Hoffmann { 692840a178cSGerd Hoffmann usb_mtp_realloc(data, 2); 693840a178cSGerd Hoffmann data->data[data->length++] = (val >> 0) & 0xff; 694840a178cSGerd Hoffmann data->data[data->length++] = (val >> 8) & 0xff; 695840a178cSGerd Hoffmann } 696840a178cSGerd Hoffmann 697840a178cSGerd Hoffmann static void usb_mtp_add_u32(MTPData *data, uint32_t val) 698840a178cSGerd Hoffmann { 699840a178cSGerd Hoffmann usb_mtp_realloc(data, 4); 700840a178cSGerd Hoffmann data->data[data->length++] = (val >> 0) & 0xff; 701840a178cSGerd Hoffmann data->data[data->length++] = (val >> 8) & 0xff; 702840a178cSGerd Hoffmann data->data[data->length++] = (val >> 16) & 0xff; 703840a178cSGerd Hoffmann data->data[data->length++] = (val >> 24) & 0xff; 704840a178cSGerd Hoffmann } 705840a178cSGerd Hoffmann 706840a178cSGerd Hoffmann static void usb_mtp_add_u64(MTPData *data, uint64_t val) 707840a178cSGerd Hoffmann { 708ada435f4SGerd Hoffmann usb_mtp_realloc(data, 8); 709840a178cSGerd Hoffmann data->data[data->length++] = (val >> 0) & 0xff; 710840a178cSGerd Hoffmann data->data[data->length++] = (val >> 8) & 0xff; 711840a178cSGerd Hoffmann data->data[data->length++] = (val >> 16) & 0xff; 712840a178cSGerd Hoffmann data->data[data->length++] = (val >> 24) & 0xff; 713840a178cSGerd Hoffmann data->data[data->length++] = (val >> 32) & 0xff; 714840a178cSGerd Hoffmann data->data[data->length++] = (val >> 40) & 0xff; 715840a178cSGerd Hoffmann data->data[data->length++] = (val >> 48) & 0xff; 716ada435f4SGerd Hoffmann data->data[data->length++] = (val >> 56) & 0xff; 717840a178cSGerd Hoffmann } 718840a178cSGerd Hoffmann 719840a178cSGerd Hoffmann static void usb_mtp_add_u16_array(MTPData *data, uint32_t len, 720840a178cSGerd Hoffmann const uint16_t *vals) 721840a178cSGerd Hoffmann { 722840a178cSGerd Hoffmann int i; 723840a178cSGerd Hoffmann 724840a178cSGerd Hoffmann usb_mtp_add_u32(data, len); 725840a178cSGerd Hoffmann for (i = 0; i < len; i++) { 726840a178cSGerd Hoffmann usb_mtp_add_u16(data, vals[i]); 727840a178cSGerd Hoffmann } 728840a178cSGerd Hoffmann } 729840a178cSGerd Hoffmann 730840a178cSGerd Hoffmann static void usb_mtp_add_u32_array(MTPData *data, uint32_t len, 731840a178cSGerd Hoffmann const uint32_t *vals) 732840a178cSGerd Hoffmann { 733840a178cSGerd Hoffmann int i; 734840a178cSGerd Hoffmann 735840a178cSGerd Hoffmann usb_mtp_add_u32(data, len); 736840a178cSGerd Hoffmann for (i = 0; i < len; i++) { 737840a178cSGerd Hoffmann usb_mtp_add_u32(data, vals[i]); 738840a178cSGerd Hoffmann } 739840a178cSGerd Hoffmann } 740840a178cSGerd Hoffmann 741840a178cSGerd Hoffmann static void usb_mtp_add_wstr(MTPData *data, const wchar_t *str) 742840a178cSGerd Hoffmann { 743840a178cSGerd Hoffmann uint32_t len = wcslen(str); 744840a178cSGerd Hoffmann int i; 745840a178cSGerd Hoffmann 746840a178cSGerd Hoffmann if (len > 0) { 747840a178cSGerd Hoffmann len++; /* include terminating L'\0' */ 748840a178cSGerd Hoffmann } 749840a178cSGerd Hoffmann 750840a178cSGerd Hoffmann usb_mtp_add_u8(data, len); 751840a178cSGerd Hoffmann for (i = 0; i < len; i++) { 752840a178cSGerd Hoffmann usb_mtp_add_u16(data, str[i]); 753840a178cSGerd Hoffmann } 754840a178cSGerd Hoffmann } 755840a178cSGerd Hoffmann 756840a178cSGerd Hoffmann static void usb_mtp_add_str(MTPData *data, const char *str) 757840a178cSGerd Hoffmann { 758840a178cSGerd Hoffmann uint32_t len = strlen(str)+1; 759e3d60bc7SPeter Xu wchar_t *wstr = g_new(wchar_t, len); 760840a178cSGerd Hoffmann size_t ret; 761840a178cSGerd Hoffmann 762840a178cSGerd Hoffmann ret = mbstowcs(wstr, str, len); 763840a178cSGerd Hoffmann if (ret == -1) { 764840a178cSGerd Hoffmann usb_mtp_add_wstr(data, L"Oops"); 765840a178cSGerd Hoffmann } else { 766840a178cSGerd Hoffmann usb_mtp_add_wstr(data, wstr); 767840a178cSGerd Hoffmann } 768e3d60bc7SPeter Xu 769e3d60bc7SPeter Xu g_free(wstr); 770840a178cSGerd Hoffmann } 771840a178cSGerd Hoffmann 772840a178cSGerd Hoffmann static void usb_mtp_add_time(MTPData *data, time_t time) 773840a178cSGerd Hoffmann { 774970bc16fSDaniel P. Berrangé g_autoptr(GDateTime) then = g_date_time_new_from_unix_utc(time); 775970bc16fSDaniel P. Berrangé g_autofree char *thenstr = g_date_time_format(then, "%Y%m%dT%H%M%S"); 776970bc16fSDaniel P. Berrangé usb_mtp_add_str(data, thenstr); 777840a178cSGerd Hoffmann } 778840a178cSGerd Hoffmann 779840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 780840a178cSGerd Hoffmann 781840a178cSGerd Hoffmann static void usb_mtp_queue_result(MTPState *s, uint16_t code, uint32_t trans, 7829c727584SBandan Das int argc, uint32_t arg0, uint32_t arg1, 7839c727584SBandan Das uint32_t arg2) 784840a178cSGerd Hoffmann { 785840a178cSGerd Hoffmann MTPControl *c = g_new0(MTPControl, 1); 786840a178cSGerd Hoffmann 787840a178cSGerd Hoffmann c->code = code; 788840a178cSGerd Hoffmann c->trans = trans; 789840a178cSGerd Hoffmann c->argc = argc; 790840a178cSGerd Hoffmann if (argc > 0) { 791840a178cSGerd Hoffmann c->argv[0] = arg0; 792840a178cSGerd Hoffmann } 793840a178cSGerd Hoffmann if (argc > 1) { 794840a178cSGerd Hoffmann c->argv[1] = arg1; 795840a178cSGerd Hoffmann } 7969c727584SBandan Das if (argc > 2) { 7979c727584SBandan Das c->argv[2] = arg2; 7989c727584SBandan Das } 799840a178cSGerd Hoffmann 800840a178cSGerd Hoffmann assert(s->result == NULL); 801840a178cSGerd Hoffmann s->result = c; 802840a178cSGerd Hoffmann } 803840a178cSGerd Hoffmann 804840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 805840a178cSGerd Hoffmann 806840a178cSGerd Hoffmann static MTPData *usb_mtp_get_device_info(MTPState *s, MTPControl *c) 807840a178cSGerd Hoffmann { 808840a178cSGerd Hoffmann static const uint16_t ops[] = { 809840a178cSGerd Hoffmann CMD_GET_DEVICE_INFO, 810840a178cSGerd Hoffmann CMD_OPEN_SESSION, 811840a178cSGerd Hoffmann CMD_CLOSE_SESSION, 812840a178cSGerd Hoffmann CMD_GET_STORAGE_IDS, 813840a178cSGerd Hoffmann CMD_GET_STORAGE_INFO, 814840a178cSGerd Hoffmann CMD_GET_NUM_OBJECTS, 815840a178cSGerd Hoffmann CMD_GET_OBJECT_HANDLES, 816840a178cSGerd Hoffmann CMD_GET_OBJECT_INFO, 817ec6206a6SBandan Das CMD_DELETE_OBJECT, 81853735befSBandan Das CMD_SEND_OBJECT_INFO, 81988d5f381SBandan Das CMD_SEND_OBJECT, 820840a178cSGerd Hoffmann CMD_GET_OBJECT, 821840a178cSGerd Hoffmann CMD_GET_PARTIAL_OBJECT, 82267f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROPS_SUPPORTED, 82367f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROP_DESC, 82467f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROP_VALUE, 825840a178cSGerd Hoffmann }; 826840a178cSGerd Hoffmann static const uint16_t fmt[] = { 827840a178cSGerd Hoffmann FMT_UNDEFINED_OBJECT, 828840a178cSGerd Hoffmann FMT_ASSOCIATION, 829840a178cSGerd Hoffmann }; 830840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 831840a178cSGerd Hoffmann 832840a178cSGerd Hoffmann trace_usb_mtp_op_get_device_info(s->dev.addr); 833840a178cSGerd Hoffmann 834f7eaed85SGerd Hoffmann usb_mtp_add_u16(d, 100); 8351f66fe57SIsaac Lozano usb_mtp_add_u32(d, 0x00000006); 8361f66fe57SIsaac Lozano usb_mtp_add_u16(d, 0x0064); 837840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L""); 838840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0000); 839840a178cSGerd Hoffmann 840840a178cSGerd Hoffmann usb_mtp_add_u16_array(d, ARRAY_SIZE(ops), ops); 841840a178cSGerd Hoffmann usb_mtp_add_u16_array(d, 0, NULL); 842840a178cSGerd Hoffmann usb_mtp_add_u16_array(d, 0, NULL); 843840a178cSGerd Hoffmann usb_mtp_add_u16_array(d, 0, NULL); 844840a178cSGerd Hoffmann usb_mtp_add_u16_array(d, ARRAY_SIZE(fmt), fmt); 845840a178cSGerd Hoffmann 846840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L"" MTP_MANUFACTURER); 847840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L"" MTP_PRODUCT); 848840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L"0.1"); 8499e4eff5bSGerd Hoffmann usb_mtp_add_wstr(d, L"0123456789abcdef0123456789abcdef"); 850840a178cSGerd Hoffmann 851840a178cSGerd Hoffmann return d; 852840a178cSGerd Hoffmann } 853840a178cSGerd Hoffmann 854840a178cSGerd Hoffmann static MTPData *usb_mtp_get_storage_ids(MTPState *s, MTPControl *c) 855840a178cSGerd Hoffmann { 856840a178cSGerd Hoffmann static const uint32_t ids[] = { 857840a178cSGerd Hoffmann QEMU_STORAGE_ID, 858840a178cSGerd Hoffmann }; 859840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 860840a178cSGerd Hoffmann 861840a178cSGerd Hoffmann trace_usb_mtp_op_get_storage_ids(s->dev.addr); 862840a178cSGerd Hoffmann 863840a178cSGerd Hoffmann usb_mtp_add_u32_array(d, ARRAY_SIZE(ids), ids); 864840a178cSGerd Hoffmann 865840a178cSGerd Hoffmann return d; 866840a178cSGerd Hoffmann } 867840a178cSGerd Hoffmann 868840a178cSGerd Hoffmann static MTPData *usb_mtp_get_storage_info(MTPState *s, MTPControl *c) 869840a178cSGerd Hoffmann { 870840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 871840a178cSGerd Hoffmann struct statvfs buf; 872840a178cSGerd Hoffmann int rc; 873840a178cSGerd Hoffmann 874840a178cSGerd Hoffmann trace_usb_mtp_op_get_storage_info(s->dev.addr); 875840a178cSGerd Hoffmann 876840a178cSGerd Hoffmann if (FLAG_SET(s, MTP_FLAG_WRITABLE)) { 877840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0003); 878840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0002); 879840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0000); 880840a178cSGerd Hoffmann } else { 881840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0001); 882840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0002); 883840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0001); 884840a178cSGerd Hoffmann } 885840a178cSGerd Hoffmann 886840a178cSGerd Hoffmann rc = statvfs(s->root, &buf); 887840a178cSGerd Hoffmann if (rc == 0) { 888840a178cSGerd Hoffmann usb_mtp_add_u64(d, (uint64_t)buf.f_frsize * buf.f_blocks); 889840a178cSGerd Hoffmann usb_mtp_add_u64(d, (uint64_t)buf.f_bavail * buf.f_blocks); 890840a178cSGerd Hoffmann usb_mtp_add_u32(d, buf.f_ffree); 891840a178cSGerd Hoffmann } else { 892840a178cSGerd Hoffmann usb_mtp_add_u64(d, 0xffffffff); 893840a178cSGerd Hoffmann usb_mtp_add_u64(d, 0xffffffff); 894840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0xffffffff); 895840a178cSGerd Hoffmann } 896840a178cSGerd Hoffmann 897840a178cSGerd Hoffmann usb_mtp_add_str(d, s->desc); 898840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L"123456789abcdef"); 899840a178cSGerd Hoffmann return d; 900840a178cSGerd Hoffmann } 901840a178cSGerd Hoffmann 902840a178cSGerd Hoffmann static MTPData *usb_mtp_get_object_handles(MTPState *s, MTPControl *c, 903840a178cSGerd Hoffmann MTPObject *o) 904840a178cSGerd Hoffmann { 905840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 90606aa50c0SGerd Hoffmann uint32_t i = 0; 90706aa50c0SGerd Hoffmann g_autofree uint32_t *handles = g_new(uint32_t, o->nchildren); 9084c7a67f5SBandan Das MTPObject *iter; 909840a178cSGerd Hoffmann 910840a178cSGerd Hoffmann trace_usb_mtp_op_get_object_handles(s->dev.addr, o->handle, o->path); 911840a178cSGerd Hoffmann 9124c7a67f5SBandan Das QLIST_FOREACH(iter, &o->children, list) { 9134c7a67f5SBandan Das handles[i++] = iter->handle; 914840a178cSGerd Hoffmann } 9154c7a67f5SBandan Das assert(i == o->nchildren); 916840a178cSGerd Hoffmann usb_mtp_add_u32_array(d, o->nchildren, handles); 917840a178cSGerd Hoffmann 918840a178cSGerd Hoffmann return d; 919840a178cSGerd Hoffmann } 920840a178cSGerd Hoffmann 921840a178cSGerd Hoffmann static MTPData *usb_mtp_get_object_info(MTPState *s, MTPControl *c, 922840a178cSGerd Hoffmann MTPObject *o) 923840a178cSGerd Hoffmann { 924840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 925840a178cSGerd Hoffmann 926840a178cSGerd Hoffmann trace_usb_mtp_op_get_object_info(s->dev.addr, o->handle, o->path); 927840a178cSGerd Hoffmann 928840a178cSGerd Hoffmann usb_mtp_add_u32(d, QEMU_STORAGE_ID); 929840a178cSGerd Hoffmann usb_mtp_add_u16(d, o->format); 930840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0); 9318a5865f3SIsaac Lozano 9328a5865f3SIsaac Lozano if (o->stat.st_size > 0xFFFFFFFF) { 9338a5865f3SIsaac Lozano usb_mtp_add_u32(d, 0xFFFFFFFF); 9348a5865f3SIsaac Lozano } else { 935840a178cSGerd Hoffmann usb_mtp_add_u32(d, o->stat.st_size); 9368a5865f3SIsaac Lozano } 937840a178cSGerd Hoffmann 938840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0); 939840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 940840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 941840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 942840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 943840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 944840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 945840a178cSGerd Hoffmann 946840a178cSGerd Hoffmann if (o->parent) { 947840a178cSGerd Hoffmann usb_mtp_add_u32(d, o->parent->handle); 948840a178cSGerd Hoffmann } else { 949840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 950840a178cSGerd Hoffmann } 951840a178cSGerd Hoffmann if (o->format == FMT_ASSOCIATION) { 952840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0001); 953840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0x00000001); 954840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 955840a178cSGerd Hoffmann } else { 956840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0); 957840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 958840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 959840a178cSGerd Hoffmann } 960840a178cSGerd Hoffmann 961840a178cSGerd Hoffmann usb_mtp_add_str(d, o->name); 962840a178cSGerd Hoffmann usb_mtp_add_time(d, o->stat.st_ctime); 963840a178cSGerd Hoffmann usb_mtp_add_time(d, o->stat.st_mtime); 964840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L""); 965840a178cSGerd Hoffmann 966840a178cSGerd Hoffmann return d; 967840a178cSGerd Hoffmann } 968840a178cSGerd Hoffmann 969840a178cSGerd Hoffmann static MTPData *usb_mtp_get_object(MTPState *s, MTPControl *c, 970840a178cSGerd Hoffmann MTPObject *o) 971840a178cSGerd Hoffmann { 972840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 973840a178cSGerd Hoffmann 974840a178cSGerd Hoffmann trace_usb_mtp_op_get_object(s->dev.addr, o->handle, o->path); 975840a178cSGerd Hoffmann 976bab9df35SGerd Hoffmann d->fd = open(o->path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); 977840a178cSGerd Hoffmann if (d->fd == -1) { 9782dc7fdf3SGerd Hoffmann usb_mtp_data_free(d); 979840a178cSGerd Hoffmann return NULL; 980840a178cSGerd Hoffmann } 981840a178cSGerd Hoffmann d->length = o->stat.st_size; 982840a178cSGerd Hoffmann d->alloc = 512; 983840a178cSGerd Hoffmann d->data = g_malloc(d->alloc); 984840a178cSGerd Hoffmann return d; 985840a178cSGerd Hoffmann } 986840a178cSGerd Hoffmann 987840a178cSGerd Hoffmann static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c, 988840a178cSGerd Hoffmann MTPObject *o) 989840a178cSGerd Hoffmann { 99062713a2eSPhilippe Mathieu-Daudé MTPData *d; 991840a178cSGerd Hoffmann off_t offset; 992840a178cSGerd Hoffmann 99362713a2eSPhilippe Mathieu-Daudé if (c->argc <= 2) { 99462713a2eSPhilippe Mathieu-Daudé return NULL; 99562713a2eSPhilippe Mathieu-Daudé } 996840a178cSGerd Hoffmann trace_usb_mtp_op_get_partial_object(s->dev.addr, o->handle, o->path, 997840a178cSGerd Hoffmann c->argv[1], c->argv[2]); 998840a178cSGerd Hoffmann 99962713a2eSPhilippe Mathieu-Daudé d = usb_mtp_data_alloc(c); 1000bab9df35SGerd Hoffmann d->fd = open(o->path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); 1001840a178cSGerd Hoffmann if (d->fd == -1) { 10022dc7fdf3SGerd Hoffmann usb_mtp_data_free(d); 1003840a178cSGerd Hoffmann return NULL; 1004840a178cSGerd Hoffmann } 1005840a178cSGerd Hoffmann 1006840a178cSGerd Hoffmann offset = c->argv[1]; 1007840a178cSGerd Hoffmann if (offset > o->stat.st_size) { 1008840a178cSGerd Hoffmann offset = o->stat.st_size; 1009840a178cSGerd Hoffmann } 101068206d73SGerd Hoffmann if (lseek(d->fd, offset, SEEK_SET) < 0) { 101168206d73SGerd Hoffmann usb_mtp_data_free(d); 101268206d73SGerd Hoffmann return NULL; 101368206d73SGerd Hoffmann } 1014840a178cSGerd Hoffmann 1015840a178cSGerd Hoffmann d->length = c->argv[2]; 1016840a178cSGerd Hoffmann if (d->length > o->stat.st_size - offset) { 1017840a178cSGerd Hoffmann d->length = o->stat.st_size - offset; 1018840a178cSGerd Hoffmann } 1019840a178cSGerd Hoffmann 1020840a178cSGerd Hoffmann return d; 1021840a178cSGerd Hoffmann } 1022840a178cSGerd Hoffmann 102367f3ef0cSIsaac Lozano static MTPData *usb_mtp_get_object_props_supported(MTPState *s, MTPControl *c) 102467f3ef0cSIsaac Lozano { 102567f3ef0cSIsaac Lozano static const uint16_t props[] = { 102667f3ef0cSIsaac Lozano PROP_STORAGE_ID, 102767f3ef0cSIsaac Lozano PROP_OBJECT_FORMAT, 102867f3ef0cSIsaac Lozano PROP_OBJECT_COMPRESSED_SIZE, 102967f3ef0cSIsaac Lozano PROP_PARENT_OBJECT, 103067f3ef0cSIsaac Lozano PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER, 103167f3ef0cSIsaac Lozano PROP_NAME, 103267f3ef0cSIsaac Lozano }; 103367f3ef0cSIsaac Lozano MTPData *d = usb_mtp_data_alloc(c); 103467f3ef0cSIsaac Lozano usb_mtp_add_u16_array(d, ARRAY_SIZE(props), props); 103567f3ef0cSIsaac Lozano 103667f3ef0cSIsaac Lozano return d; 103767f3ef0cSIsaac Lozano } 103867f3ef0cSIsaac Lozano 103967f3ef0cSIsaac Lozano static MTPData *usb_mtp_get_object_prop_desc(MTPState *s, MTPControl *c) 104067f3ef0cSIsaac Lozano { 104167f3ef0cSIsaac Lozano MTPData *d = usb_mtp_data_alloc(c); 104267f3ef0cSIsaac Lozano switch (c->argv[0]) { 104367f3ef0cSIsaac Lozano case PROP_STORAGE_ID: 104467f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_STORAGE_ID); 104567f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT32); 104667f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 104767f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 104867f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 104967f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 105067f3ef0cSIsaac Lozano break; 105167f3ef0cSIsaac Lozano case PROP_OBJECT_FORMAT: 105267f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_OBJECT_FORMAT); 105367f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT16); 105467f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 105567f3ef0cSIsaac Lozano usb_mtp_add_u16(d, 0x0000); 105667f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 105767f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 105867f3ef0cSIsaac Lozano break; 105967f3ef0cSIsaac Lozano case PROP_OBJECT_COMPRESSED_SIZE: 106067f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_OBJECT_COMPRESSED_SIZE); 106167f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT64); 106267f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 106367f3ef0cSIsaac Lozano usb_mtp_add_u64(d, 0x0000000000000000); 106467f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 106567f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 106667f3ef0cSIsaac Lozano break; 106767f3ef0cSIsaac Lozano case PROP_PARENT_OBJECT: 106867f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_PARENT_OBJECT); 106967f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT32); 107067f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 107167f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 107267f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 107367f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 107467f3ef0cSIsaac Lozano break; 107567f3ef0cSIsaac Lozano case PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER: 107667f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER); 107767f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT128); 107867f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 107967f3ef0cSIsaac Lozano usb_mtp_add_u64(d, 0x0000000000000000); 108067f3ef0cSIsaac Lozano usb_mtp_add_u64(d, 0x0000000000000000); 108167f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 108267f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 108367f3ef0cSIsaac Lozano break; 108467f3ef0cSIsaac Lozano case PROP_NAME: 108567f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_NAME); 108667f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_STRING); 108767f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 108867f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 108967f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 109067f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 109167f3ef0cSIsaac Lozano break; 109267f3ef0cSIsaac Lozano default: 109367f3ef0cSIsaac Lozano usb_mtp_data_free(d); 109467f3ef0cSIsaac Lozano return NULL; 109567f3ef0cSIsaac Lozano } 109667f3ef0cSIsaac Lozano 109767f3ef0cSIsaac Lozano return d; 109867f3ef0cSIsaac Lozano } 109967f3ef0cSIsaac Lozano 110067f3ef0cSIsaac Lozano static MTPData *usb_mtp_get_object_prop_value(MTPState *s, MTPControl *c, 110167f3ef0cSIsaac Lozano MTPObject *o) 110267f3ef0cSIsaac Lozano { 110367f3ef0cSIsaac Lozano MTPData *d = usb_mtp_data_alloc(c); 110467f3ef0cSIsaac Lozano switch (c->argv[1]) { 110567f3ef0cSIsaac Lozano case PROP_STORAGE_ID: 110667f3ef0cSIsaac Lozano usb_mtp_add_u32(d, QEMU_STORAGE_ID); 110767f3ef0cSIsaac Lozano break; 110867f3ef0cSIsaac Lozano case PROP_OBJECT_FORMAT: 110967f3ef0cSIsaac Lozano usb_mtp_add_u16(d, o->format); 111067f3ef0cSIsaac Lozano break; 111167f3ef0cSIsaac Lozano case PROP_OBJECT_COMPRESSED_SIZE: 111267f3ef0cSIsaac Lozano usb_mtp_add_u64(d, o->stat.st_size); 111367f3ef0cSIsaac Lozano break; 111467f3ef0cSIsaac Lozano case PROP_PARENT_OBJECT: 111567f3ef0cSIsaac Lozano if (o->parent == NULL) { 111667f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 111767f3ef0cSIsaac Lozano } else { 111867f3ef0cSIsaac Lozano usb_mtp_add_u32(d, o->parent->handle); 111967f3ef0cSIsaac Lozano } 112067f3ef0cSIsaac Lozano break; 112167f3ef0cSIsaac Lozano case PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER: 1122b12227afSStefan Weil /* Should be persistent between sessions, 112367f3ef0cSIsaac Lozano * but using our objedt ID is "good enough" 112467f3ef0cSIsaac Lozano * for now */ 112567f3ef0cSIsaac Lozano usb_mtp_add_u64(d, 0x0000000000000000); 112667f3ef0cSIsaac Lozano usb_mtp_add_u64(d, o->handle); 112767f3ef0cSIsaac Lozano break; 112867f3ef0cSIsaac Lozano case PROP_NAME: 112967f3ef0cSIsaac Lozano usb_mtp_add_str(d, o->name); 113067f3ef0cSIsaac Lozano break; 113167f3ef0cSIsaac Lozano default: 113267f3ef0cSIsaac Lozano usb_mtp_data_free(d); 113367f3ef0cSIsaac Lozano return NULL; 113467f3ef0cSIsaac Lozano } 113567f3ef0cSIsaac Lozano 113667f3ef0cSIsaac Lozano return d; 113767f3ef0cSIsaac Lozano } 113867f3ef0cSIsaac Lozano 11394bc15916SBandan Das /* 11404bc15916SBandan Das * Return values when object @o is deleted. 11414bc15916SBandan Das * If at least one of the deletions succeeded, 11424bc15916SBandan Das * DELETE_SUCCESS is set and if at least one 11434bc15916SBandan Das * of the deletions failed, DELETE_FAILURE is 11444bc15916SBandan Das * set. Both bits being set (DELETE_PARTIAL) 11454bc15916SBandan Das * signifies a RES_PARTIAL_DELETE being sent 11464bc15916SBandan Das * back to the initiator. 11474bc15916SBandan Das */ 1148ec6206a6SBandan Das enum { 11494bc15916SBandan Das DELETE_SUCCESS = (1 << 0), 11504bc15916SBandan Das DELETE_FAILURE = (1 << 1), 11514bc15916SBandan Das DELETE_PARTIAL = (DELETE_FAILURE | DELETE_SUCCESS), 1152ec6206a6SBandan Das }; 1153ec6206a6SBandan Das 1154ec6206a6SBandan Das static int usb_mtp_deletefn(MTPState *s, MTPObject *o, uint32_t trans) 1155ec6206a6SBandan Das { 1156ec6206a6SBandan Das MTPObject *iter, *iter2; 11574bc15916SBandan Das int ret = 0; 1158ec6206a6SBandan Das 1159ec6206a6SBandan Das /* 1160ec6206a6SBandan Das * TODO: Add support for Protection Status 1161ec6206a6SBandan Das */ 1162ec6206a6SBandan Das 1163ec6206a6SBandan Das QLIST_FOREACH(iter, &o->children, list) { 1164ec6206a6SBandan Das if (iter->format == FMT_ASSOCIATION) { 1165ec6206a6SBandan Das QLIST_FOREACH(iter2, &iter->children, list) { 11664bc15916SBandan Das ret |= usb_mtp_deletefn(s, iter2, trans); 1167ec6206a6SBandan Das } 1168ec6206a6SBandan Das } 1169ec6206a6SBandan Das } 1170ec6206a6SBandan Das 1171ec6206a6SBandan Das if (o->format == FMT_UNDEFINED_OBJECT) { 1172ec6206a6SBandan Das if (remove(o->path)) { 11734bc15916SBandan Das ret |= DELETE_FAILURE; 1174ec6206a6SBandan Das } else { 1175b396733dSBandan Das usb_mtp_object_free(s, o); 11764bc15916SBandan Das ret |= DELETE_SUCCESS; 1177ec6206a6SBandan Das } 11787ddf8374SBandan Das } else if (o->format == FMT_ASSOCIATION) { 1179ec6206a6SBandan Das if (rmdir(o->path)) { 11804bc15916SBandan Das ret |= DELETE_FAILURE; 1181ec6206a6SBandan Das } else { 1182b396733dSBandan Das usb_mtp_object_free(s, o); 11834bc15916SBandan Das ret |= DELETE_SUCCESS; 1184ec6206a6SBandan Das } 1185ec6206a6SBandan Das } 1186ec6206a6SBandan Das 11874bc15916SBandan Das return ret; 1188ec6206a6SBandan Das } 1189ec6206a6SBandan Das 1190ec6206a6SBandan Das static void usb_mtp_object_delete(MTPState *s, uint32_t handle, 1191ec6206a6SBandan Das uint32_t format_code, uint32_t trans) 1192ec6206a6SBandan Das { 1193ec6206a6SBandan Das MTPObject *o; 1194ec6206a6SBandan Das int ret; 1195ec6206a6SBandan Das 1196ec6206a6SBandan Das /* Return error if store is read-only */ 1197ec6206a6SBandan Das if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { 1198ec6206a6SBandan Das usb_mtp_queue_result(s, RES_STORE_READ_ONLY, 1199ec6206a6SBandan Das trans, 0, 0, 0, 0); 1200ec6206a6SBandan Das return; 1201ec6206a6SBandan Das } 1202ec6206a6SBandan Das 1203ec6206a6SBandan Das if (format_code != 0) { 1204ec6206a6SBandan Das usb_mtp_queue_result(s, RES_SPEC_BY_FORMAT_UNSUPPORTED, 1205ec6206a6SBandan Das trans, 0, 0, 0, 0); 1206ec6206a6SBandan Das return; 1207ec6206a6SBandan Das } 1208ec6206a6SBandan Das 1209ec6206a6SBandan Das if (handle == 0xFFFFFFF) { 1210ec6206a6SBandan Das o = QTAILQ_FIRST(&s->objects); 1211ec6206a6SBandan Das } else { 1212ec6206a6SBandan Das o = usb_mtp_object_lookup(s, handle); 1213ec6206a6SBandan Das } 1214ec6206a6SBandan Das if (o == NULL) { 1215ec6206a6SBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 1216ec6206a6SBandan Das trans, 0, 0, 0, 0); 1217ec6206a6SBandan Das return; 1218ec6206a6SBandan Das } 1219ec6206a6SBandan Das 1220ec6206a6SBandan Das ret = usb_mtp_deletefn(s, o, trans); 12214bc15916SBandan Das switch (ret) { 12224bc15916SBandan Das case DELETE_SUCCESS: 1223ec6206a6SBandan Das usb_mtp_queue_result(s, RES_OK, trans, 1224ec6206a6SBandan Das 0, 0, 0, 0); 12254bc15916SBandan Das break; 12264bc15916SBandan Das case DELETE_FAILURE: 12274bc15916SBandan Das usb_mtp_queue_result(s, RES_PARTIAL_DELETE, 12284bc15916SBandan Das trans, 0, 0, 0, 0); 12294bc15916SBandan Das break; 12304bc15916SBandan Das case DELETE_PARTIAL: 12314bc15916SBandan Das usb_mtp_queue_result(s, RES_PARTIAL_DELETE, 12324bc15916SBandan Das trans, 0, 0, 0, 0); 12334bc15916SBandan Das break; 12344bc15916SBandan Das default: 12354bc15916SBandan Das g_assert_not_reached(); 1236ec6206a6SBandan Das } 12374bc15916SBandan Das 12384bc15916SBandan Das return; 1239ec6206a6SBandan Das } 1240ec6206a6SBandan Das 1241840a178cSGerd Hoffmann static void usb_mtp_command(MTPState *s, MTPControl *c) 1242840a178cSGerd Hoffmann { 1243840a178cSGerd Hoffmann MTPData *data_in = NULL; 124453735befSBandan Das MTPObject *o = NULL; 1245840a178cSGerd Hoffmann uint32_t nres = 0, res0 = 0; 124647287c27SDaniel P. Berrangé Error *err = NULL; 1247840a178cSGerd Hoffmann 1248840a178cSGerd Hoffmann /* sanity checks */ 1249840a178cSGerd Hoffmann if (c->code >= CMD_CLOSE_SESSION && s->session == 0) { 1250840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_SESSION_NOT_OPEN, 12519c727584SBandan Das c->trans, 0, 0, 0, 0); 1252840a178cSGerd Hoffmann return; 1253840a178cSGerd Hoffmann } 1254840a178cSGerd Hoffmann 1255840a178cSGerd Hoffmann /* process commands */ 1256840a178cSGerd Hoffmann switch (c->code) { 1257840a178cSGerd Hoffmann case CMD_GET_DEVICE_INFO: 1258840a178cSGerd Hoffmann data_in = usb_mtp_get_device_info(s, c); 1259840a178cSGerd Hoffmann break; 1260840a178cSGerd Hoffmann case CMD_OPEN_SESSION: 1261840a178cSGerd Hoffmann if (s->session) { 1262840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_SESSION_ALREADY_OPEN, 12639c727584SBandan Das c->trans, 1, s->session, 0, 0); 1264840a178cSGerd Hoffmann return; 1265840a178cSGerd Hoffmann } 1266840a178cSGerd Hoffmann if (c->argv[0] == 0) { 1267840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_PARAMETER, 12689c727584SBandan Das c->trans, 0, 0, 0, 0); 1269840a178cSGerd Hoffmann return; 1270840a178cSGerd Hoffmann } 1271840a178cSGerd Hoffmann trace_usb_mtp_op_open_session(s->dev.addr); 1272840a178cSGerd Hoffmann s->session = c->argv[0]; 1273840a178cSGerd Hoffmann usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root); 127447287c27SDaniel P. Berrangé 127547287c27SDaniel P. Berrangé s->file_monitor = qemu_file_monitor_new(&err); 127647287c27SDaniel P. Berrangé if (err) { 12775217f188SMarkus Armbruster error_reportf_err(err, 12785217f188SMarkus Armbruster "usb-mtp: file monitoring init failed: "); 127947287c27SDaniel P. Berrangé } else { 128047287c27SDaniel P. Berrangé QTAILQ_INIT(&s->events); 12818e3e3897SBandan Das } 1282840a178cSGerd Hoffmann break; 1283840a178cSGerd Hoffmann case CMD_CLOSE_SESSION: 1284840a178cSGerd Hoffmann trace_usb_mtp_op_close_session(s->dev.addr); 1285840a178cSGerd Hoffmann s->session = 0; 1286840a178cSGerd Hoffmann s->next_handle = 0; 128747287c27SDaniel P. Berrangé usb_mtp_file_monitor_cleanup(s); 1288840a178cSGerd Hoffmann usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); 1289840a178cSGerd Hoffmann assert(QTAILQ_EMPTY(&s->objects)); 1290840a178cSGerd Hoffmann break; 1291840a178cSGerd Hoffmann case CMD_GET_STORAGE_IDS: 1292840a178cSGerd Hoffmann data_in = usb_mtp_get_storage_ids(s, c); 1293840a178cSGerd Hoffmann break; 1294840a178cSGerd Hoffmann case CMD_GET_STORAGE_INFO: 1295840a178cSGerd Hoffmann if (c->argv[0] != QEMU_STORAGE_ID && 1296840a178cSGerd Hoffmann c->argv[0] != 0xffffffff) { 1297840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_STORAGE_ID, 12989c727584SBandan Das c->trans, 0, 0, 0, 0); 1299840a178cSGerd Hoffmann return; 1300840a178cSGerd Hoffmann } 1301840a178cSGerd Hoffmann data_in = usb_mtp_get_storage_info(s, c); 1302840a178cSGerd Hoffmann break; 1303840a178cSGerd Hoffmann case CMD_GET_NUM_OBJECTS: 1304840a178cSGerd Hoffmann case CMD_GET_OBJECT_HANDLES: 1305840a178cSGerd Hoffmann if (c->argv[0] != QEMU_STORAGE_ID && 1306840a178cSGerd Hoffmann c->argv[0] != 0xffffffff) { 1307840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_STORAGE_ID, 13089c727584SBandan Das c->trans, 0, 0, 0, 0); 1309840a178cSGerd Hoffmann return; 1310840a178cSGerd Hoffmann } 1311840a178cSGerd Hoffmann if (c->argv[1] != 0x00000000) { 1312840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_SPEC_BY_FORMAT_UNSUPPORTED, 13139c727584SBandan Das c->trans, 0, 0, 0, 0); 1314840a178cSGerd Hoffmann return; 1315840a178cSGerd Hoffmann } 1316840a178cSGerd Hoffmann if (c->argv[2] == 0x00000000 || 1317840a178cSGerd Hoffmann c->argv[2] == 0xffffffff) { 1318840a178cSGerd Hoffmann o = QTAILQ_FIRST(&s->objects); 1319840a178cSGerd Hoffmann } else { 1320840a178cSGerd Hoffmann o = usb_mtp_object_lookup(s, c->argv[2]); 1321840a178cSGerd Hoffmann } 1322840a178cSGerd Hoffmann if (o == NULL) { 1323840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13249c727584SBandan Das c->trans, 0, 0, 0, 0); 1325840a178cSGerd Hoffmann return; 1326840a178cSGerd Hoffmann } 1327840a178cSGerd Hoffmann if (o->format != FMT_ASSOCIATION) { 1328840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, 13299c727584SBandan Das c->trans, 0, 0, 0, 0); 1330840a178cSGerd Hoffmann return; 1331840a178cSGerd Hoffmann } 1332840a178cSGerd Hoffmann usb_mtp_object_readdir(s, o); 1333840a178cSGerd Hoffmann if (c->code == CMD_GET_NUM_OBJECTS) { 1334840a178cSGerd Hoffmann trace_usb_mtp_op_get_num_objects(s->dev.addr, o->handle, o->path); 1335840a178cSGerd Hoffmann nres = 1; 1336840a178cSGerd Hoffmann res0 = o->nchildren; 1337840a178cSGerd Hoffmann } else { 1338840a178cSGerd Hoffmann data_in = usb_mtp_get_object_handles(s, c, o); 1339840a178cSGerd Hoffmann } 1340840a178cSGerd Hoffmann break; 1341840a178cSGerd Hoffmann case CMD_GET_OBJECT_INFO: 1342840a178cSGerd Hoffmann o = usb_mtp_object_lookup(s, c->argv[0]); 1343840a178cSGerd Hoffmann if (o == NULL) { 1344840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13459c727584SBandan Das c->trans, 0, 0, 0, 0); 1346840a178cSGerd Hoffmann return; 1347840a178cSGerd Hoffmann } 1348840a178cSGerd Hoffmann data_in = usb_mtp_get_object_info(s, c, o); 1349840a178cSGerd Hoffmann break; 1350840a178cSGerd Hoffmann case CMD_GET_OBJECT: 1351840a178cSGerd Hoffmann o = usb_mtp_object_lookup(s, c->argv[0]); 1352840a178cSGerd Hoffmann if (o == NULL) { 1353840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13549c727584SBandan Das c->trans, 0, 0, 0, 0); 1355840a178cSGerd Hoffmann return; 1356840a178cSGerd Hoffmann } 1357840a178cSGerd Hoffmann if (o->format == FMT_ASSOCIATION) { 1358840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13599c727584SBandan Das c->trans, 0, 0, 0, 0); 1360840a178cSGerd Hoffmann return; 1361840a178cSGerd Hoffmann } 1362840a178cSGerd Hoffmann data_in = usb_mtp_get_object(s, c, o); 1363d0657b2aSGonglei if (data_in == NULL) { 136422513a9bSGerd Hoffmann usb_mtp_queue_result(s, RES_GENERAL_ERROR, 13659c727584SBandan Das c->trans, 0, 0, 0, 0); 136622513a9bSGerd Hoffmann return; 1367840a178cSGerd Hoffmann } 1368840a178cSGerd Hoffmann break; 1369ec6206a6SBandan Das case CMD_DELETE_OBJECT: 1370ec6206a6SBandan Das usb_mtp_object_delete(s, c->argv[0], c->argv[1], c->trans); 1371ec6206a6SBandan Das return; 1372840a178cSGerd Hoffmann case CMD_GET_PARTIAL_OBJECT: 1373840a178cSGerd Hoffmann o = usb_mtp_object_lookup(s, c->argv[0]); 1374840a178cSGerd Hoffmann if (o == NULL) { 1375840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13769c727584SBandan Das c->trans, 0, 0, 0, 0); 1377840a178cSGerd Hoffmann return; 1378840a178cSGerd Hoffmann } 1379840a178cSGerd Hoffmann if (o->format == FMT_ASSOCIATION) { 1380840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13819c727584SBandan Das c->trans, 0, 0, 0, 0); 1382840a178cSGerd Hoffmann return; 1383840a178cSGerd Hoffmann } 1384840a178cSGerd Hoffmann data_in = usb_mtp_get_partial_object(s, c, o); 1385d0657b2aSGonglei if (data_in == NULL) { 138622513a9bSGerd Hoffmann usb_mtp_queue_result(s, RES_GENERAL_ERROR, 13879c727584SBandan Das c->trans, 0, 0, 0, 0); 138822513a9bSGerd Hoffmann return; 1389840a178cSGerd Hoffmann } 1390840a178cSGerd Hoffmann nres = 1; 1391840a178cSGerd Hoffmann res0 = data_in->length; 1392840a178cSGerd Hoffmann break; 139353735befSBandan Das case CMD_SEND_OBJECT_INFO: 139453735befSBandan Das /* Return error if store is read-only */ 139553735befSBandan Das if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { 139653735befSBandan Das usb_mtp_queue_result(s, RES_STORE_READ_ONLY, 139753735befSBandan Das c->trans, 0, 0, 0, 0); 139853735befSBandan Das } else if (c->argv[0] && (c->argv[0] != QEMU_STORAGE_ID)) { 139953735befSBandan Das /* First parameter points to storage id or is 0 */ 140053735befSBandan Das usb_mtp_queue_result(s, RES_STORE_NOT_AVAILABLE, c->trans, 140153735befSBandan Das 0, 0, 0, 0); 140253735befSBandan Das } else if (c->argv[1] && !c->argv[0]) { 140353735befSBandan Das /* If second parameter is specified, first must also be specified */ 140453735befSBandan Das usb_mtp_queue_result(s, RES_DESTINATION_UNSUPPORTED, c->trans, 140553735befSBandan Das 0, 0, 0, 0); 140653735befSBandan Das } else { 140753735befSBandan Das uint32_t handle = c->argv[1]; 140853735befSBandan Das if (handle == 0xFFFFFFFF || handle == 0) { 140953735befSBandan Das /* root object */ 141053735befSBandan Das o = QTAILQ_FIRST(&s->objects); 141153735befSBandan Das } else { 141253735befSBandan Das o = usb_mtp_object_lookup(s, handle); 141353735befSBandan Das } 141453735befSBandan Das if (o == NULL) { 141553735befSBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, c->trans, 141653735befSBandan Das 0, 0, 0, 0); 141724e8d1faSBandan Das } else if (o->format != FMT_ASSOCIATION) { 141853735befSBandan Das usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, c->trans, 141953735befSBandan Das 0, 0, 0, 0); 142053735befSBandan Das } 142153735befSBandan Das } 142253735befSBandan Das if (o) { 142353735befSBandan Das s->dataset.parent_handle = o->handle; 142453735befSBandan Das } 142553735befSBandan Das s->data_out = usb_mtp_data_alloc(c); 142653735befSBandan Das return; 142788d5f381SBandan Das case CMD_SEND_OBJECT: 142888d5f381SBandan Das if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { 142988d5f381SBandan Das usb_mtp_queue_result(s, RES_STORE_READ_ONLY, 143088d5f381SBandan Das c->trans, 0, 0, 0, 0); 143188d5f381SBandan Das return; 143288d5f381SBandan Das } 143388d5f381SBandan Das if (!s->write_pending) { 143488d5f381SBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, 143588d5f381SBandan Das c->trans, 0, 0, 0, 0); 143688d5f381SBandan Das return; 143788d5f381SBandan Das } 143888d5f381SBandan Das s->data_out = usb_mtp_data_alloc(c); 143988d5f381SBandan Das return; 144067f3ef0cSIsaac Lozano case CMD_GET_OBJECT_PROPS_SUPPORTED: 144167f3ef0cSIsaac Lozano if (c->argv[0] != FMT_UNDEFINED_OBJECT && 144267f3ef0cSIsaac Lozano c->argv[0] != FMT_ASSOCIATION) { 144367f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_FORMAT_CODE, 14449c727584SBandan Das c->trans, 0, 0, 0, 0); 144567f3ef0cSIsaac Lozano return; 144667f3ef0cSIsaac Lozano } 144767f3ef0cSIsaac Lozano data_in = usb_mtp_get_object_props_supported(s, c); 144867f3ef0cSIsaac Lozano break; 144967f3ef0cSIsaac Lozano case CMD_GET_OBJECT_PROP_DESC: 145067f3ef0cSIsaac Lozano if (c->argv[1] != FMT_UNDEFINED_OBJECT && 145167f3ef0cSIsaac Lozano c->argv[1] != FMT_ASSOCIATION) { 145267f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_FORMAT_CODE, 14539c727584SBandan Das c->trans, 0, 0, 0, 0); 145467f3ef0cSIsaac Lozano return; 145567f3ef0cSIsaac Lozano } 145667f3ef0cSIsaac Lozano data_in = usb_mtp_get_object_prop_desc(s, c); 145767f3ef0cSIsaac Lozano if (data_in == NULL) { 145867f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_PROP_CODE, 14599c727584SBandan Das c->trans, 0, 0, 0, 0); 146067f3ef0cSIsaac Lozano return; 146167f3ef0cSIsaac Lozano } 146267f3ef0cSIsaac Lozano break; 146367f3ef0cSIsaac Lozano case CMD_GET_OBJECT_PROP_VALUE: 146467f3ef0cSIsaac Lozano o = usb_mtp_object_lookup(s, c->argv[0]); 146567f3ef0cSIsaac Lozano if (o == NULL) { 146667f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 14679c727584SBandan Das c->trans, 0, 0, 0, 0); 146867f3ef0cSIsaac Lozano return; 146967f3ef0cSIsaac Lozano } 147067f3ef0cSIsaac Lozano data_in = usb_mtp_get_object_prop_value(s, c, o); 147167f3ef0cSIsaac Lozano if (data_in == NULL) { 147267f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_PROP_CODE, 14739c727584SBandan Das c->trans, 0, 0, 0, 0); 147467f3ef0cSIsaac Lozano return; 147567f3ef0cSIsaac Lozano } 147667f3ef0cSIsaac Lozano break; 1477840a178cSGerd Hoffmann default: 14781c76551fSGerd Hoffmann trace_usb_mtp_op_unknown(s->dev.addr, c->code); 1479840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_OPERATION_NOT_SUPPORTED, 14809c727584SBandan Das c->trans, 0, 0, 0, 0); 1481840a178cSGerd Hoffmann return; 1482840a178cSGerd Hoffmann } 1483840a178cSGerd Hoffmann 1484840a178cSGerd Hoffmann /* return results on success */ 1485840a178cSGerd Hoffmann if (data_in) { 1486840a178cSGerd Hoffmann assert(s->data_in == NULL); 1487840a178cSGerd Hoffmann s->data_in = data_in; 1488840a178cSGerd Hoffmann } 14899c727584SBandan Das usb_mtp_queue_result(s, RES_OK, c->trans, nres, res0, 0, 0); 1490840a178cSGerd Hoffmann } 1491840a178cSGerd Hoffmann 1492840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 1493840a178cSGerd Hoffmann 1494840a178cSGerd Hoffmann static void usb_mtp_handle_reset(USBDevice *dev) 1495840a178cSGerd Hoffmann { 14967c03a899SGonglei MTPState *s = USB_MTP(dev); 1497840a178cSGerd Hoffmann 1498840a178cSGerd Hoffmann trace_usb_mtp_reset(s->dev.addr); 1499840a178cSGerd Hoffmann 150047287c27SDaniel P. Berrangé usb_mtp_file_monitor_cleanup(s); 1501b3c4d425SBandan Das usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); 1502840a178cSGerd Hoffmann s->session = 0; 1503840a178cSGerd Hoffmann usb_mtp_data_free(s->data_in); 1504840a178cSGerd Hoffmann s->data_in = NULL; 1505840a178cSGerd Hoffmann usb_mtp_data_free(s->data_out); 1506840a178cSGerd Hoffmann s->data_out = NULL; 1507840a178cSGerd Hoffmann g_free(s->result); 1508840a178cSGerd Hoffmann s->result = NULL; 1509840a178cSGerd Hoffmann } 1510840a178cSGerd Hoffmann 1511840a178cSGerd Hoffmann static void usb_mtp_handle_control(USBDevice *dev, USBPacket *p, 1512840a178cSGerd Hoffmann int request, int value, int index, 1513840a178cSGerd Hoffmann int length, uint8_t *data) 1514840a178cSGerd Hoffmann { 1515840a178cSGerd Hoffmann int ret; 151647bff13cSBandan Das MTPState *s = USB_MTP(dev); 151747bff13cSBandan Das uint16_t *event = (uint16_t *)data; 1518840a178cSGerd Hoffmann 151947bff13cSBandan Das switch (request) { 152047bff13cSBandan Das case ClassInterfaceOutRequest | 0x64: 152147bff13cSBandan Das if (*event == EVT_CANCEL_TRANSACTION) { 152247bff13cSBandan Das g_free(s->result); 152347bff13cSBandan Das s->result = NULL; 152447bff13cSBandan Das usb_mtp_data_free(s->data_in); 152547bff13cSBandan Das s->data_in = NULL; 152647bff13cSBandan Das if (s->write_pending) { 152747bff13cSBandan Das g_free(s->dataset.filename); 152847bff13cSBandan Das s->write_pending = false; 1529f7c36a75SBandan Das s->dataset.size = 0; 153047bff13cSBandan Das } 153147bff13cSBandan Das usb_mtp_data_free(s->data_out); 153247bff13cSBandan Das s->data_out = NULL; 153347bff13cSBandan Das } else { 153447bff13cSBandan Das p->status = USB_RET_STALL; 153547bff13cSBandan Das } 153647bff13cSBandan Das break; 153747bff13cSBandan Das default: 153847bff13cSBandan Das ret = usb_desc_handle_control(dev, p, request, 153947bff13cSBandan Das value, index, length, data); 1540840a178cSGerd Hoffmann if (ret >= 0) { 1541840a178cSGerd Hoffmann return; 1542840a178cSGerd Hoffmann } 154347bff13cSBandan Das } 1544840a178cSGerd Hoffmann 1545840a178cSGerd Hoffmann trace_usb_mtp_stall(dev->addr, "unknown control request"); 1546840a178cSGerd Hoffmann } 1547840a178cSGerd Hoffmann 1548840a178cSGerd Hoffmann static void usb_mtp_cancel_packet(USBDevice *dev, USBPacket *p) 1549840a178cSGerd Hoffmann { 15501c76551fSGerd Hoffmann /* we don't use async packets, so this should never be called */ 1551840a178cSGerd Hoffmann fprintf(stderr, "%s\n", __func__); 1552840a178cSGerd Hoffmann } 1553840a178cSGerd Hoffmann 15541259f27eSDaniel P. Berrangé static char *utf16_to_str(uint8_t len, uint8_t *str16) 155553735befSBandan Das { 15566de02a13SGerd Hoffmann wchar_t *wstr = g_new0(wchar_t, len + 1); 15576de02a13SGerd Hoffmann int count, dlen; 15586de02a13SGerd Hoffmann char *dest; 155953735befSBandan Das 156053735befSBandan Das for (count = 0; count < len; count++) { 15616de02a13SGerd Hoffmann /* FIXME: not working for surrogate pairs */ 15621259f27eSDaniel P. Berrangé wstr[count] = lduw_le_p(str16 + (count * 2)); 156353735befSBandan Das } 15646de02a13SGerd Hoffmann wstr[count] = 0; 156553735befSBandan Das 15666de02a13SGerd Hoffmann dlen = wcstombs(NULL, wstr, 0) + 1; 15676de02a13SGerd Hoffmann dest = g_malloc(dlen); 15686de02a13SGerd Hoffmann wcstombs(dest, wstr, dlen); 156953735befSBandan Das g_free(wstr); 15706de02a13SGerd Hoffmann return dest; 157153735befSBandan Das } 157253735befSBandan Das 1573d33e3e4bSBandan Das /* Wrapper around write, returns 0 on failure */ 1574c1ef0f25SBandan Das static uint64_t write_retry(int fd, void *buf, uint64_t size, off_t offset) 1575d33e3e4bSBandan Das { 157649f9e8d6SBandan Das uint64_t ret = 0; 1577d33e3e4bSBandan Das 1578c1ef0f25SBandan Das if (lseek(fd, offset, SEEK_SET) < 0) { 1579c1ef0f25SBandan Das goto done; 1580c1ef0f25SBandan Das } 1581c1ef0f25SBandan Das 158249f9e8d6SBandan Das ret = qemu_write_full(fd, buf, size); 1583d33e3e4bSBandan Das 1584c1ef0f25SBandan Das done: 158549f9e8d6SBandan Das return ret; 1586d33e3e4bSBandan Das } 1587d33e3e4bSBandan Das 1588c5ead51fSBandan Das static int usb_mtp_update_object(MTPObject *parent, char *name) 1589c1ef0f25SBandan Das { 159083c44b44SBandan Das int ret = 0; 1591c5ead51fSBandan Das 1592c1ef0f25SBandan Das MTPObject *o = 1593c1ef0f25SBandan Das usb_mtp_object_lookup_name(parent, name, strlen(name)); 1594c1ef0f25SBandan Das 1595c1ef0f25SBandan Das if (o) { 1596c5ead51fSBandan Das ret = lstat(o->path, &o->stat); 1597c1ef0f25SBandan Das } 1598c5ead51fSBandan Das 1599c5ead51fSBandan Das return ret; 1600c1ef0f25SBandan Das } 1601c1ef0f25SBandan Das 1602e39b8a66SBandan Das static void usb_mtp_write_data(MTPState *s, uint32_t handle) 160388d5f381SBandan Das { 160488d5f381SBandan Das MTPData *d = s->data_out; 160588d5f381SBandan Das MTPObject *parent = 160688d5f381SBandan Das usb_mtp_object_lookup(s, s->dataset.parent_handle); 160788d5f381SBandan Das char *path = NULL; 16083e096650SBandan Das uint64_t rc; 16097c204e96SVolker Rümelin mode_t mask = 0755; 1610298ac63cSBandan Das int ret = 0; 161188d5f381SBandan Das 161288d5f381SBandan Das assert(d != NULL); 161388d5f381SBandan Das 1614c1ef0f25SBandan Das switch (d->write_status) { 1615c1ef0f25SBandan Das case WRITE_START: 1616c1ef0f25SBandan Das if (!parent || !s->write_pending) { 161788d5f381SBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans, 161888d5f381SBandan Das 0, 0, 0, 0); 1619e39b8a66SBandan Das return; 162088d5f381SBandan Das } 162188d5f381SBandan Das 162288d5f381SBandan Das if (s->dataset.filename) { 162388d5f381SBandan Das path = g_strdup_printf("%s/%s", parent->path, s->dataset.filename); 162488d5f381SBandan Das if (s->dataset.format == FMT_ASSOCIATION) { 1625*34b55848SBin Meng ret = g_mkdir(path, mask); 1626e39b8a66SBandan Das if (!ret) { 1627e39b8a66SBandan Das usb_mtp_queue_result(s, RES_OK, d->trans, 3, 1628e39b8a66SBandan Das QEMU_STORAGE_ID, 1629e39b8a66SBandan Das s->dataset.parent_handle, 1630e39b8a66SBandan Das handle); 1631e39b8a66SBandan Das goto close; 163288d5f381SBandan Das } 1633e39b8a66SBandan Das goto done; 1634e39b8a66SBandan Das } 1635e39b8a66SBandan Das 1636c1ef0f25SBandan Das d->fd = open(path, O_CREAT | O_WRONLY | 16377c204e96SVolker Rümelin O_CLOEXEC | O_NOFOLLOW, mask & 0666); 163888d5f381SBandan Das if (d->fd == -1) { 1639e39b8a66SBandan Das ret = 1; 164088d5f381SBandan Das goto done; 164188d5f381SBandan Das } 164288d5f381SBandan Das 1643c1ef0f25SBandan Das /* Return success if initiator sent 0 sized data */ 164488d5f381SBandan Das if (!s->dataset.size) { 1645e39b8a66SBandan Das goto done; 164688d5f381SBandan Das } 1647c1ef0f25SBandan Das if (d->length != MTP_WRITE_BUF_SZ && !d->pending) { 1648c1ef0f25SBandan Das d->write_status = WRITE_END; 1649c1ef0f25SBandan Das } 1650c1ef0f25SBandan Das } 1651c1ef0f25SBandan Das /* fall through */ 1652c1ef0f25SBandan Das case WRITE_CONTINUE: 1653c1ef0f25SBandan Das case WRITE_END: 1654c1ef0f25SBandan Das rc = write_retry(d->fd, d->data, d->data_offset, 1655c1ef0f25SBandan Das d->offset - d->data_offset); 1656c1ef0f25SBandan Das if (rc != d->data_offset) { 1657e39b8a66SBandan Das ret = 1; 165888d5f381SBandan Das goto done; 165988d5f381SBandan Das } 1660c1ef0f25SBandan Das if (d->write_status != WRITE_END) { 1661298ac63cSBandan Das g_free(path); 1662e39b8a66SBandan Das return; 1663c1ef0f25SBandan Das } else { 1664c5ead51fSBandan Das /* 1665c5ead51fSBandan Das * Return an incomplete transfer if file size doesn't match 1666c5ead51fSBandan Das * for < 4G file or if lstat fails which will result in an incorrect 1667c5ead51fSBandan Das * file size 1668c5ead51fSBandan Das */ 1669c5ead51fSBandan Das if ((s->dataset.size != 0xFFFFFFFF && 1670c5ead51fSBandan Das d->offset != s->dataset.size) || 1671c5ead51fSBandan Das usb_mtp_update_object(parent, s->dataset.filename)) { 167288d5f381SBandan Das usb_mtp_queue_result(s, RES_INCOMPLETE_TRANSFER, d->trans, 167388d5f381SBandan Das 0, 0, 0, 0); 1674e39b8a66SBandan Das goto close; 167588d5f381SBandan Das } 1676c1ef0f25SBandan Das } 167788d5f381SBandan Das } 167888d5f381SBandan Das 167988d5f381SBandan Das done: 1680e39b8a66SBandan Das if (ret) { 1681e39b8a66SBandan Das usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 1682e39b8a66SBandan Das 0, 0, 0, 0); 1683e39b8a66SBandan Das } else { 1684e39b8a66SBandan Das usb_mtp_queue_result(s, RES_OK, d->trans, 1685e39b8a66SBandan Das 0, 0, 0, 0); 1686e39b8a66SBandan Das } 1687e39b8a66SBandan Das close: 168888d5f381SBandan Das /* 168988d5f381SBandan Das * The write dataset is kept around and freed only 169088d5f381SBandan Das * on success or if another write request comes in 169188d5f381SBandan Das */ 169288d5f381SBandan Das if (d->fd != -1) { 169388d5f381SBandan Das close(d->fd); 1694298ac63cSBandan Das d->fd = -1; 169588d5f381SBandan Das } 169688d5f381SBandan Das g_free(s->dataset.filename); 1697f7c36a75SBandan Das s->dataset.size = 0; 169888d5f381SBandan Das g_free(path); 169988d5f381SBandan Das s->write_pending = false; 170088d5f381SBandan Das } 170188d5f381SBandan Das 170290c1a742SMichael Hanselmann static void usb_mtp_write_metadata(MTPState *s, uint64_t dlen) 170353735befSBandan Das { 170453735befSBandan Das MTPData *d = s->data_out; 170553735befSBandan Das ObjectInfo *dataset = (ObjectInfo *)d->data; 17066de02a13SGerd Hoffmann char *filename; 170753735befSBandan Das MTPObject *o; 170853735befSBandan Das MTPObject *p = usb_mtp_object_lookup(s, s->dataset.parent_handle); 170953735befSBandan Das uint32_t next_handle = s->next_handle; 1710375cb86dSDaniel P. Berrangé size_t filename_chars = dlen - offsetof(ObjectInfo, filename); 1711375cb86dSDaniel P. Berrangé 1712375cb86dSDaniel P. Berrangé /* 1713375cb86dSDaniel P. Berrangé * filename is utf-16. We're intentionally doing 1714375cb86dSDaniel P. Berrangé * integer division to truncate if malicious guest 1715375cb86dSDaniel P. Berrangé * sent an odd number of bytes. 1716375cb86dSDaniel P. Berrangé */ 1717375cb86dSDaniel P. Berrangé filename_chars /= 2; 171853735befSBandan Das 171953735befSBandan Das assert(!s->write_pending); 172024e8d1faSBandan Das assert(p != NULL); 172153735befSBandan Das 1722375cb86dSDaniel P. Berrangé filename = utf16_to_str(MIN(dataset->length, filename_chars), 172390c1a742SMichael Hanselmann dataset->filename); 172453735befSBandan Das 1725c52d46e0SGerd Hoffmann if (strchr(filename, '/')) { 1726c52d46e0SGerd Hoffmann usb_mtp_queue_result(s, RES_PARAMETER_NOT_SUPPORTED, d->trans, 1727c52d46e0SGerd Hoffmann 0, 0, 0, 0); 17288e3759efSLi Qiang g_free(filename); 1729c52d46e0SGerd Hoffmann return; 1730c52d46e0SGerd Hoffmann } 1731c52d46e0SGerd Hoffmann 17323541cd48SDaniel P. Berrangé o = usb_mtp_object_lookup_name(p, filename, -1); 173353735befSBandan Das if (o != NULL) { 173453735befSBandan Das next_handle = o->handle; 173553735befSBandan Das } 173653735befSBandan Das 173753735befSBandan Das s->dataset.filename = filename; 173853735befSBandan Das s->dataset.format = dataset->format; 173953735befSBandan Das s->dataset.size = dataset->size; 174053735befSBandan Das s->write_pending = true; 174153735befSBandan Das 174253735befSBandan Das if (s->dataset.format == FMT_ASSOCIATION) { 1743e39b8a66SBandan Das usb_mtp_write_data(s, next_handle); 1744e39b8a66SBandan Das } else { 174553735befSBandan Das usb_mtp_queue_result(s, RES_OK, d->trans, 3, QEMU_STORAGE_ID, 174653735befSBandan Das s->dataset.parent_handle, next_handle); 174753735befSBandan Das } 1748e39b8a66SBandan Das } 174953735befSBandan Das 175088d5f381SBandan Das static void usb_mtp_get_data(MTPState *s, mtp_container *container, 175188d5f381SBandan Das USBPacket *p) 175288d5f381SBandan Das { 175388d5f381SBandan Das MTPData *d = s->data_out; 175488d5f381SBandan Das uint64_t dlen; 175588d5f381SBandan Das uint32_t data_len = p->iov.size; 1756406f35d7SBandan Das uint64_t total_len; 175788d5f381SBandan Das 17583c969a60SBandan Das if (!d) { 17593c969a60SBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, 0, 17603c969a60SBandan Das 0, 0, 0, 0); 17613c969a60SBandan Das return; 17623c969a60SBandan Das } 176388d5f381SBandan Das if (d->first) { 176488d5f381SBandan Das /* Total length of incoming data */ 1765406f35d7SBandan Das total_len = cpu_to_le32(container->length) - sizeof(mtp_container); 176688d5f381SBandan Das /* Length of data in this packet */ 176788d5f381SBandan Das data_len -= sizeof(mtp_container); 1768179fcf8aSBandan Das if (total_len < MTP_WRITE_BUF_SZ) { 1769406f35d7SBandan Das usb_mtp_realloc(d, total_len); 1770406f35d7SBandan Das d->length += total_len; 1771179fcf8aSBandan Das } else { 1772179fcf8aSBandan Das usb_mtp_realloc(d, MTP_WRITE_BUF_SZ - sizeof(mtp_container)); 1773179fcf8aSBandan Das d->length += MTP_WRITE_BUF_SZ - sizeof(mtp_container); 1774179fcf8aSBandan Das } 177588d5f381SBandan Das d->offset = 0; 177688d5f381SBandan Das d->first = false; 17773e096650SBandan Das d->pending = false; 1778c1ef0f25SBandan Das d->data_offset = 0; 1779c1ef0f25SBandan Das d->write_status = WRITE_START; 17803e096650SBandan Das } 17813e096650SBandan Das 17823e096650SBandan Das if (d->pending) { 1783c1ef0f25SBandan Das memset(d->data, 0, d->length); 1784c1ef0f25SBandan Das if (d->length != MTP_WRITE_BUF_SZ) { 1785c1ef0f25SBandan Das usb_mtp_realloc(d, MTP_WRITE_BUF_SZ - d->length); 1786c1ef0f25SBandan Das d->length += (MTP_WRITE_BUF_SZ - d->length); 1787c1ef0f25SBandan Das } 17883e096650SBandan Das d->pending = false; 1789c1ef0f25SBandan Das d->write_status = WRITE_CONTINUE; 1790c1ef0f25SBandan Das d->data_offset = 0; 179188d5f381SBandan Das } 179288d5f381SBandan Das 1793c1ef0f25SBandan Das if (d->length - d->data_offset > data_len) { 179488d5f381SBandan Das dlen = data_len; 179588d5f381SBandan Das } else { 1796c1ef0f25SBandan Das dlen = d->length - d->data_offset; 179788d5f381SBandan Das } 179888d5f381SBandan Das 179988d5f381SBandan Das switch (d->code) { 180053735befSBandan Das case CMD_SEND_OBJECT_INFO: 1801c1ef0f25SBandan Das usb_packet_copy(p, d->data + d->data_offset, dlen); 180253735befSBandan Das d->offset += dlen; 1803c1ef0f25SBandan Das d->data_offset += dlen; 1804c1ef0f25SBandan Das if (d->data_offset == d->length) { 180553735befSBandan Das /* The operation might have already failed */ 180653735befSBandan Das if (!s->result) { 180790c1a742SMichael Hanselmann usb_mtp_write_metadata(s, dlen); 180853735befSBandan Das } 180953735befSBandan Das usb_mtp_data_free(s->data_out); 181053735befSBandan Das s->data_out = NULL; 181153735befSBandan Das return; 181253735befSBandan Das } 181353735befSBandan Das break; 181488d5f381SBandan Das case CMD_SEND_OBJECT: 1815c1ef0f25SBandan Das usb_packet_copy(p, d->data + d->data_offset, dlen); 181688d5f381SBandan Das d->offset += dlen; 1817c1ef0f25SBandan Das d->data_offset += dlen; 18183e096650SBandan Das if ((p->iov.size % 64) || !p->iov.size) { 18193e096650SBandan Das assert((s->dataset.size == 0xFFFFFFFF) || 1820179fcf8aSBandan Das (s->dataset.size == d->offset)); 18213e096650SBandan Das 1822c1ef0f25SBandan Das if (d->length == MTP_WRITE_BUF_SZ) { 1823c1ef0f25SBandan Das d->write_status = WRITE_END; 1824c1ef0f25SBandan Das } else { 1825c1ef0f25SBandan Das d->write_status = WRITE_START; 1826c1ef0f25SBandan Das } 1827e39b8a66SBandan Das usb_mtp_write_data(s, 0); 182888d5f381SBandan Das usb_mtp_data_free(s->data_out); 182988d5f381SBandan Das s->data_out = NULL; 183088d5f381SBandan Das return; 183188d5f381SBandan Das } 1832c1ef0f25SBandan Das if (d->data_offset == d->length) { 18333e096650SBandan Das d->pending = true; 1834e39b8a66SBandan Das usb_mtp_write_data(s, 0); 18353e096650SBandan Das } 183688d5f381SBandan Das break; 183788d5f381SBandan Das default: 183888d5f381SBandan Das p->status = USB_RET_STALL; 183988d5f381SBandan Das return; 184088d5f381SBandan Das } 184188d5f381SBandan Das } 184288d5f381SBandan Das 1843840a178cSGerd Hoffmann static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) 1844840a178cSGerd Hoffmann { 18457c03a899SGonglei MTPState *s = USB_MTP(dev); 1846840a178cSGerd Hoffmann MTPControl cmd; 1847840a178cSGerd Hoffmann mtp_container container; 1848840a178cSGerd Hoffmann uint32_t params[5]; 184988d5f381SBandan Das uint16_t container_type; 1850840a178cSGerd Hoffmann int i, rc; 1851840a178cSGerd Hoffmann 1852840a178cSGerd Hoffmann switch (p->ep->nr) { 1853840a178cSGerd Hoffmann case EP_DATA_IN: 1854840a178cSGerd Hoffmann if (s->data_out != NULL) { 1855840a178cSGerd Hoffmann /* guest bug */ 1856840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "awaiting data-out"); 1857840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1858840a178cSGerd Hoffmann return; 1859840a178cSGerd Hoffmann } 1860840a178cSGerd Hoffmann if (p->iov.size < sizeof(container)) { 1861840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "packet too small"); 1862840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1863840a178cSGerd Hoffmann return; 1864840a178cSGerd Hoffmann } 1865840a178cSGerd Hoffmann if (s->data_in != NULL) { 1866840a178cSGerd Hoffmann MTPData *d = s->data_in; 18678a5865f3SIsaac Lozano uint64_t dlen = d->length - d->offset; 1868840a178cSGerd Hoffmann if (d->first) { 1869840a178cSGerd Hoffmann trace_usb_mtp_data_in(s->dev.addr, d->trans, d->length); 18708a5865f3SIsaac Lozano if (d->length + sizeof(container) > 0xFFFFFFFF) { 18718a5865f3SIsaac Lozano container.length = cpu_to_le32(0xFFFFFFFF); 18728a5865f3SIsaac Lozano } else { 18738a5865f3SIsaac Lozano container.length = 18748a5865f3SIsaac Lozano cpu_to_le32(d->length + sizeof(container)); 18758a5865f3SIsaac Lozano } 1876840a178cSGerd Hoffmann container.type = cpu_to_le16(TYPE_DATA); 1877840a178cSGerd Hoffmann container.code = cpu_to_le16(d->code); 1878840a178cSGerd Hoffmann container.trans = cpu_to_le32(d->trans); 1879840a178cSGerd Hoffmann usb_packet_copy(p, &container, sizeof(container)); 1880840a178cSGerd Hoffmann d->first = false; 1881840a178cSGerd Hoffmann if (dlen > p->iov.size - sizeof(container)) { 1882840a178cSGerd Hoffmann dlen = p->iov.size - sizeof(container); 1883840a178cSGerd Hoffmann } 1884840a178cSGerd Hoffmann } else { 1885840a178cSGerd Hoffmann if (dlen > p->iov.size) { 1886840a178cSGerd Hoffmann dlen = p->iov.size; 1887840a178cSGerd Hoffmann } 1888840a178cSGerd Hoffmann } 1889840a178cSGerd Hoffmann if (d->fd == -1) { 1890840a178cSGerd Hoffmann usb_packet_copy(p, d->data + d->offset, dlen); 1891840a178cSGerd Hoffmann } else { 1892840a178cSGerd Hoffmann if (d->alloc < p->iov.size) { 1893840a178cSGerd Hoffmann d->alloc = p->iov.size; 1894840a178cSGerd Hoffmann d->data = g_realloc(d->data, d->alloc); 1895840a178cSGerd Hoffmann } 1896840a178cSGerd Hoffmann rc = read(d->fd, d->data, dlen); 1897840a178cSGerd Hoffmann if (rc != dlen) { 18988ebb8763SGerd Hoffmann memset(d->data, 0, dlen); 18998ebb8763SGerd Hoffmann s->result->code = RES_INCOMPLETE_TRANSFER; 1900840a178cSGerd Hoffmann } 1901840a178cSGerd Hoffmann usb_packet_copy(p, d->data, dlen); 1902840a178cSGerd Hoffmann } 1903840a178cSGerd Hoffmann d->offset += dlen; 1904840a178cSGerd Hoffmann if (d->offset == d->length) { 1905840a178cSGerd Hoffmann usb_mtp_data_free(s->data_in); 1906840a178cSGerd Hoffmann s->data_in = NULL; 1907840a178cSGerd Hoffmann } 1908840a178cSGerd Hoffmann } else if (s->result != NULL) { 1909840a178cSGerd Hoffmann MTPControl *r = s->result; 1910840a178cSGerd Hoffmann int length = sizeof(container) + r->argc * sizeof(uint32_t); 1911840a178cSGerd Hoffmann if (r->code == RES_OK) { 1912840a178cSGerd Hoffmann trace_usb_mtp_success(s->dev.addr, r->trans, 1913840a178cSGerd Hoffmann (r->argc > 0) ? r->argv[0] : 0, 1914840a178cSGerd Hoffmann (r->argc > 1) ? r->argv[1] : 0); 1915840a178cSGerd Hoffmann } else { 1916840a178cSGerd Hoffmann trace_usb_mtp_error(s->dev.addr, r->code, r->trans, 1917840a178cSGerd Hoffmann (r->argc > 0) ? r->argv[0] : 0, 1918840a178cSGerd Hoffmann (r->argc > 1) ? r->argv[1] : 0); 1919840a178cSGerd Hoffmann } 1920840a178cSGerd Hoffmann container.length = cpu_to_le32(length); 1921840a178cSGerd Hoffmann container.type = cpu_to_le16(TYPE_RESPONSE); 1922840a178cSGerd Hoffmann container.code = cpu_to_le16(r->code); 1923840a178cSGerd Hoffmann container.trans = cpu_to_le32(r->trans); 1924840a178cSGerd Hoffmann for (i = 0; i < r->argc; i++) { 1925840a178cSGerd Hoffmann params[i] = cpu_to_le32(r->argv[i]); 1926840a178cSGerd Hoffmann } 1927840a178cSGerd Hoffmann usb_packet_copy(p, &container, sizeof(container)); 1928840a178cSGerd Hoffmann usb_packet_copy(p, ¶ms, length - sizeof(container)); 1929840a178cSGerd Hoffmann g_free(s->result); 1930840a178cSGerd Hoffmann s->result = NULL; 1931840a178cSGerd Hoffmann } 1932840a178cSGerd Hoffmann break; 1933840a178cSGerd Hoffmann case EP_DATA_OUT: 1934840a178cSGerd Hoffmann if (p->iov.size < sizeof(container)) { 1935840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "packet too small"); 1936840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1937840a178cSGerd Hoffmann return; 1938840a178cSGerd Hoffmann } 193924e8d1faSBandan Das if ((s->data_out != NULL) && !s->data_out->first) { 194088d5f381SBandan Das container_type = TYPE_DATA; 194188d5f381SBandan Das } else { 1942840a178cSGerd Hoffmann usb_packet_copy(p, &container, sizeof(container)); 194388d5f381SBandan Das container_type = le16_to_cpu(container.type); 194488d5f381SBandan Das } 194588d5f381SBandan Das switch (container_type) { 1946840a178cSGerd Hoffmann case TYPE_COMMAND: 1947840a178cSGerd Hoffmann if (s->data_in || s->data_out || s->result) { 1948840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "transaction inflight"); 1949840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1950840a178cSGerd Hoffmann return; 1951840a178cSGerd Hoffmann } 1952840a178cSGerd Hoffmann cmd.code = le16_to_cpu(container.code); 1953840a178cSGerd Hoffmann cmd.argc = (le32_to_cpu(container.length) - sizeof(container)) 1954840a178cSGerd Hoffmann / sizeof(uint32_t); 1955840a178cSGerd Hoffmann cmd.trans = le32_to_cpu(container.trans); 1956afa82dafSGerd Hoffmann if (cmd.argc > ARRAY_SIZE(cmd.argv)) { 1957afa82dafSGerd Hoffmann cmd.argc = ARRAY_SIZE(cmd.argv); 1958afa82dafSGerd Hoffmann } 1959afa82dafSGerd Hoffmann if (p->iov.size < sizeof(container) + cmd.argc * sizeof(uint32_t)) { 1960afa82dafSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "packet too small"); 1961afa82dafSGerd Hoffmann p->status = USB_RET_STALL; 1962afa82dafSGerd Hoffmann return; 1963afa82dafSGerd Hoffmann } 1964840a178cSGerd Hoffmann usb_packet_copy(p, ¶ms, cmd.argc * sizeof(uint32_t)); 1965840a178cSGerd Hoffmann for (i = 0; i < cmd.argc; i++) { 1966840a178cSGerd Hoffmann cmd.argv[i] = le32_to_cpu(params[i]); 1967840a178cSGerd Hoffmann } 1968840a178cSGerd Hoffmann trace_usb_mtp_command(s->dev.addr, cmd.code, cmd.trans, 1969840a178cSGerd Hoffmann (cmd.argc > 0) ? cmd.argv[0] : 0, 1970840a178cSGerd Hoffmann (cmd.argc > 1) ? cmd.argv[1] : 0, 1971840a178cSGerd Hoffmann (cmd.argc > 2) ? cmd.argv[2] : 0, 1972840a178cSGerd Hoffmann (cmd.argc > 3) ? cmd.argv[3] : 0, 1973840a178cSGerd Hoffmann (cmd.argc > 4) ? cmd.argv[4] : 0); 1974840a178cSGerd Hoffmann usb_mtp_command(s, &cmd); 1975840a178cSGerd Hoffmann break; 197688d5f381SBandan Das case TYPE_DATA: 197788d5f381SBandan Das /* One of the previous transfers has already errored but the 197888d5f381SBandan Das * responder is still sending data associated with it 197988d5f381SBandan Das */ 198088d5f381SBandan Das if (s->result != NULL) { 198188d5f381SBandan Das return; 198288d5f381SBandan Das } 198388d5f381SBandan Das usb_mtp_get_data(s, &container, p); 198488d5f381SBandan Das break; 1985840a178cSGerd Hoffmann default: 19869cd04ccfSGerd Hoffmann /* not needed as long as the mtp device is read-only */ 1987840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1988840a178cSGerd Hoffmann return; 1989840a178cSGerd Hoffmann } 1990840a178cSGerd Hoffmann break; 1991840a178cSGerd Hoffmann case EP_EVENT: 199293d592e3SBandan Das if (!QTAILQ_EMPTY(&s->events)) { 1993eae3eb3eSPaolo Bonzini struct MTPMonEntry *e = QTAILQ_LAST(&s->events); 199493d592e3SBandan Das uint32_t handle; 199593d592e3SBandan Das int len = sizeof(container) + sizeof(uint32_t); 199693d592e3SBandan Das 199793d592e3SBandan Das if (p->iov.size < len) { 199893d592e3SBandan Das trace_usb_mtp_stall(s->dev.addr, 199993d592e3SBandan Das "packet too small to send event"); 200093d592e3SBandan Das p->status = USB_RET_STALL; 200193d592e3SBandan Das return; 200293d592e3SBandan Das } 200393d592e3SBandan Das 200493d592e3SBandan Das QTAILQ_REMOVE(&s->events, e, next); 200593d592e3SBandan Das container.length = cpu_to_le32(len); 200693d592e3SBandan Das container.type = cpu_to_le32(TYPE_EVENT); 200793d592e3SBandan Das container.code = cpu_to_le16(e->event); 200893d592e3SBandan Das container.trans = 0; /* no trans specific events */ 200993d592e3SBandan Das handle = cpu_to_le32(e->handle); 201093d592e3SBandan Das usb_packet_copy(p, &container, sizeof(container)); 201193d592e3SBandan Das usb_packet_copy(p, &handle, sizeof(uint32_t)); 201293d592e3SBandan Das g_free(e); 201393d592e3SBandan Das return; 201493d592e3SBandan Das } 2015840a178cSGerd Hoffmann p->status = USB_RET_NAK; 2016840a178cSGerd Hoffmann return; 2017840a178cSGerd Hoffmann default: 2018840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "invalid endpoint"); 2019840a178cSGerd Hoffmann p->status = USB_RET_STALL; 2020840a178cSGerd Hoffmann return; 2021840a178cSGerd Hoffmann } 2022840a178cSGerd Hoffmann 2023840a178cSGerd Hoffmann if (p->actual_length == 0) { 2024840a178cSGerd Hoffmann trace_usb_mtp_nak(s->dev.addr, p->ep->nr); 2025840a178cSGerd Hoffmann p->status = USB_RET_NAK; 2026840a178cSGerd Hoffmann return; 2027840a178cSGerd Hoffmann } else { 2028840a178cSGerd Hoffmann trace_usb_mtp_xfer(s->dev.addr, p->ep->nr, p->actual_length, 2029840a178cSGerd Hoffmann p->iov.size); 2030840a178cSGerd Hoffmann return; 2031840a178cSGerd Hoffmann } 2032840a178cSGerd Hoffmann } 2033840a178cSGerd Hoffmann 2034df9bb666SGonglei static void usb_mtp_realize(USBDevice *dev, Error **errp) 2035840a178cSGerd Hoffmann { 20367c03a899SGonglei MTPState *s = USB_MTP(dev); 2037840a178cSGerd Hoffmann 2038e4c1c641SBandan Das if ((s->root == NULL) || !g_path_is_absolute(s->root)) { 2039e4c1c641SBandan Das error_setg(errp, "usb-mtp: rootdir must be configured and be an absolute path"); 2040e60baebdSGonglei return; 2041e60baebdSGonglei } 2042e4c1c641SBandan Das 2043e4c1c641SBandan Das if (access(s->root, R_OK) != 0) { 2044e4c1c641SBandan Das error_setg(errp, "usb-mtp: rootdir does not exist/not readable"); 2045e4c1c641SBandan Das return; 2046e4c1c641SBandan Das } else if (!s->readonly && access(s->root, W_OK) != 0) { 2047e4c1c641SBandan Das error_setg(errp, "usb-mtp: rootdir does not have write permissions"); 2048e4c1c641SBandan Das return; 2049840a178cSGerd Hoffmann } 2050e4c1c641SBandan Das 20512392ae6bSBandan Das /* Mark store as RW */ 20522392ae6bSBandan Das if (!s->readonly) { 20532392ae6bSBandan Das s->flags |= (1 << MTP_FLAG_WRITABLE); 20542392ae6bSBandan Das } 20552392ae6bSBandan Das 2056e4c1c641SBandan Das if (s->desc == NULL) { 2057e4c1c641SBandan Das /* 2058e4c1c641SBandan Das * This does not check if path exists 2059e4c1c641SBandan Das * but we have the checks above 2060e4c1c641SBandan Das */ 2061e4c1c641SBandan Das s->desc = g_path_get_basename(s->root); 2062e4c1c641SBandan Das } 2063e4c1c641SBandan Das 2064e4c1c641SBandan Das usb_desc_create_serial(dev); 2065e4c1c641SBandan Das usb_desc_init(dev); 2066e4c1c641SBandan Das QTAILQ_INIT(&s->objects); 2067e4c1c641SBandan Das 2068840a178cSGerd Hoffmann } 2069840a178cSGerd Hoffmann 2070840a178cSGerd Hoffmann static const VMStateDescription vmstate_usb_mtp = { 2071840a178cSGerd Hoffmann .name = "usb-mtp", 2072840a178cSGerd Hoffmann .unmigratable = 1, 2073840a178cSGerd Hoffmann .version_id = 1, 2074840a178cSGerd Hoffmann .minimum_version_id = 1, 2075840a178cSGerd Hoffmann .fields = (VMStateField[]) { 2076840a178cSGerd Hoffmann VMSTATE_USB_DEVICE(dev, MTPState), 2077840a178cSGerd Hoffmann VMSTATE_END_OF_LIST() 2078840a178cSGerd Hoffmann } 2079840a178cSGerd Hoffmann }; 2080840a178cSGerd Hoffmann 2081840a178cSGerd Hoffmann static Property mtp_properties[] = { 208215aa757dSBandan Das DEFINE_PROP_STRING("rootdir", MTPState, root), 2083840a178cSGerd Hoffmann DEFINE_PROP_STRING("desc", MTPState, desc), 2084ec6206a6SBandan Das DEFINE_PROP_BOOL("readonly", MTPState, readonly, true), 2085840a178cSGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 2086840a178cSGerd Hoffmann }; 2087840a178cSGerd Hoffmann 2088840a178cSGerd Hoffmann static void usb_mtp_class_initfn(ObjectClass *klass, void *data) 2089840a178cSGerd Hoffmann { 2090840a178cSGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 2091840a178cSGerd Hoffmann USBDeviceClass *uc = USB_DEVICE_CLASS(klass); 2092840a178cSGerd Hoffmann 2093df9bb666SGonglei uc->realize = usb_mtp_realize; 2094840a178cSGerd Hoffmann uc->product_desc = "QEMU USB MTP"; 2095840a178cSGerd Hoffmann uc->usb_desc = &desc; 2096840a178cSGerd Hoffmann uc->cancel_packet = usb_mtp_cancel_packet; 2097840a178cSGerd Hoffmann uc->handle_attach = usb_desc_attach; 2098840a178cSGerd Hoffmann uc->handle_reset = usb_mtp_handle_reset; 2099840a178cSGerd Hoffmann uc->handle_control = usb_mtp_handle_control; 2100840a178cSGerd Hoffmann uc->handle_data = usb_mtp_handle_data; 2101cdab4dc0SThomas Huth set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); 2102cdab4dc0SThomas Huth dc->desc = "USB Media Transfer Protocol device"; 2103840a178cSGerd Hoffmann dc->fw_name = "mtp"; 2104840a178cSGerd Hoffmann dc->vmsd = &vmstate_usb_mtp; 21054f67d30bSMarc-André Lureau device_class_set_props(dc, mtp_properties); 2106840a178cSGerd Hoffmann } 2107840a178cSGerd Hoffmann 21085e78c98bSBernhard Beschow static const TypeInfo mtp_info = { 21097c03a899SGonglei .name = TYPE_USB_MTP, 2110840a178cSGerd Hoffmann .parent = TYPE_USB_DEVICE, 2111840a178cSGerd Hoffmann .instance_size = sizeof(MTPState), 2112840a178cSGerd Hoffmann .class_init = usb_mtp_class_initfn, 2113840a178cSGerd Hoffmann }; 2114840a178cSGerd Hoffmann 2115840a178cSGerd Hoffmann static void usb_mtp_register_types(void) 2116840a178cSGerd Hoffmann { 2117840a178cSGerd Hoffmann type_register_static(&mtp_info); 2118840a178cSGerd Hoffmann } 2119840a178cSGerd Hoffmann 2120840a178cSGerd Hoffmann type_init(usb_mtp_register_types) 2121