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" 14840a178cSGerd Hoffmann #include <wchar.h> 15840a178cSGerd Hoffmann #include <dirent.h> 16840a178cSGerd Hoffmann 17840a178cSGerd Hoffmann #include <sys/statvfs.h> 18983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 198e3e3897SBandan Das #include <sys/inotify.h> 208e3e3897SBandan Das #include "qemu/main-loop.h" 218e3e3897SBandan Das #endif 22840a178cSGerd Hoffmann 23840a178cSGerd Hoffmann #include "qemu-common.h" 24840a178cSGerd Hoffmann #include "qemu/iov.h" 25840a178cSGerd Hoffmann #include "trace.h" 26840a178cSGerd Hoffmann #include "hw/usb.h" 27840a178cSGerd Hoffmann #include "hw/usb/desc.h" 28840a178cSGerd Hoffmann 29840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 30840a178cSGerd Hoffmann 31840a178cSGerd Hoffmann enum mtp_container_type { 32840a178cSGerd Hoffmann TYPE_COMMAND = 1, 33840a178cSGerd Hoffmann TYPE_DATA = 2, 34840a178cSGerd Hoffmann TYPE_RESPONSE = 3, 35840a178cSGerd Hoffmann TYPE_EVENT = 4, 36840a178cSGerd Hoffmann }; 37840a178cSGerd Hoffmann 38840a178cSGerd Hoffmann enum mtp_code { 39840a178cSGerd Hoffmann /* command codes */ 40840a178cSGerd Hoffmann CMD_GET_DEVICE_INFO = 0x1001, 41840a178cSGerd Hoffmann CMD_OPEN_SESSION = 0x1002, 42840a178cSGerd Hoffmann CMD_CLOSE_SESSION = 0x1003, 43840a178cSGerd Hoffmann CMD_GET_STORAGE_IDS = 0x1004, 44840a178cSGerd Hoffmann CMD_GET_STORAGE_INFO = 0x1005, 45840a178cSGerd Hoffmann CMD_GET_NUM_OBJECTS = 0x1006, 46840a178cSGerd Hoffmann CMD_GET_OBJECT_HANDLES = 0x1007, 47840a178cSGerd Hoffmann CMD_GET_OBJECT_INFO = 0x1008, 48840a178cSGerd Hoffmann CMD_GET_OBJECT = 0x1009, 49ec6206a6SBandan Das CMD_DELETE_OBJECT = 0x100b, 50*53735befSBandan Das CMD_SEND_OBJECT_INFO = 0x100c, 5188d5f381SBandan Das CMD_SEND_OBJECT = 0x100d, 52840a178cSGerd Hoffmann CMD_GET_PARTIAL_OBJECT = 0x101b, 5367f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROPS_SUPPORTED = 0x9801, 5467f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROP_DESC = 0x9802, 5567f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROP_VALUE = 0x9803, 56840a178cSGerd Hoffmann 57840a178cSGerd Hoffmann /* response codes */ 58840a178cSGerd Hoffmann RES_OK = 0x2001, 5922513a9bSGerd Hoffmann RES_GENERAL_ERROR = 0x2002, 60840a178cSGerd Hoffmann RES_SESSION_NOT_OPEN = 0x2003, 61840a178cSGerd Hoffmann RES_INVALID_TRANSACTION_ID = 0x2004, 62840a178cSGerd Hoffmann RES_OPERATION_NOT_SUPPORTED = 0x2005, 63840a178cSGerd Hoffmann RES_PARAMETER_NOT_SUPPORTED = 0x2006, 648ebb8763SGerd Hoffmann RES_INCOMPLETE_TRANSFER = 0x2007, 65840a178cSGerd Hoffmann RES_INVALID_STORAGE_ID = 0x2008, 66840a178cSGerd Hoffmann RES_INVALID_OBJECT_HANDLE = 0x2009, 6767f3ef0cSIsaac Lozano RES_INVALID_OBJECT_FORMAT_CODE = 0x200b, 6888d5f381SBandan Das RES_STORE_FULL = 0x200c, 69ec6206a6SBandan Das RES_STORE_READ_ONLY = 0x200e, 70ec6206a6SBandan Das RES_PARTIAL_DELETE = 0x2012, 71*53735befSBandan Das RES_STORE_NOT_AVAILABLE = 0x2013, 72840a178cSGerd Hoffmann RES_SPEC_BY_FORMAT_UNSUPPORTED = 0x2014, 7388d5f381SBandan Das RES_INVALID_OBJECTINFO = 0x2015, 74*53735befSBandan Das RES_DESTINATION_UNSUPPORTED = 0x2020, 75840a178cSGerd Hoffmann RES_INVALID_PARENT_OBJECT = 0x201a, 76840a178cSGerd Hoffmann RES_INVALID_PARAMETER = 0x201d, 77840a178cSGerd Hoffmann RES_SESSION_ALREADY_OPEN = 0x201e, 7867f3ef0cSIsaac Lozano RES_INVALID_OBJECT_PROP_CODE = 0xA801, 79840a178cSGerd Hoffmann 80840a178cSGerd Hoffmann /* format codes */ 81840a178cSGerd Hoffmann FMT_UNDEFINED_OBJECT = 0x3000, 82840a178cSGerd Hoffmann FMT_ASSOCIATION = 0x3001, 838e3e3897SBandan Das 848e3e3897SBandan Das /* event codes */ 858e3e3897SBandan Das EVT_OBJ_ADDED = 0x4002, 868e3e3897SBandan Das EVT_OBJ_REMOVED = 0x4003, 878e3e3897SBandan Das EVT_OBJ_INFO_CHANGED = 0x4007, 8867f3ef0cSIsaac Lozano 8967f3ef0cSIsaac Lozano /* object properties */ 9067f3ef0cSIsaac Lozano PROP_STORAGE_ID = 0xDC01, 9167f3ef0cSIsaac Lozano PROP_OBJECT_FORMAT = 0xDC02, 9267f3ef0cSIsaac Lozano PROP_OBJECT_COMPRESSED_SIZE = 0xDC04, 9367f3ef0cSIsaac Lozano PROP_PARENT_OBJECT = 0xDC0B, 9467f3ef0cSIsaac Lozano PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER = 0xDC41, 9567f3ef0cSIsaac Lozano PROP_NAME = 0xDC44, 9667f3ef0cSIsaac Lozano }; 9767f3ef0cSIsaac Lozano 9867f3ef0cSIsaac Lozano enum mtp_data_type { 9967f3ef0cSIsaac Lozano DATA_TYPE_UINT16 = 0x0004, 10067f3ef0cSIsaac Lozano DATA_TYPE_UINT32 = 0x0006, 10167f3ef0cSIsaac Lozano DATA_TYPE_UINT64 = 0x0008, 10267f3ef0cSIsaac Lozano DATA_TYPE_UINT128 = 0x000a, 10367f3ef0cSIsaac Lozano DATA_TYPE_STRING = 0xffff, 104840a178cSGerd Hoffmann }; 105840a178cSGerd Hoffmann 106840a178cSGerd Hoffmann typedef struct { 107840a178cSGerd Hoffmann uint32_t length; 108840a178cSGerd Hoffmann uint16_t type; 109840a178cSGerd Hoffmann uint16_t code; 110840a178cSGerd Hoffmann uint32_t trans; 111840a178cSGerd Hoffmann } QEMU_PACKED mtp_container; 112840a178cSGerd Hoffmann 113840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 114840a178cSGerd Hoffmann 115840a178cSGerd Hoffmann typedef struct MTPState MTPState; 116840a178cSGerd Hoffmann typedef struct MTPControl MTPControl; 117840a178cSGerd Hoffmann typedef struct MTPData MTPData; 118840a178cSGerd Hoffmann typedef struct MTPObject MTPObject; 119840a178cSGerd Hoffmann 120840a178cSGerd Hoffmann enum { 121840a178cSGerd Hoffmann EP_DATA_IN = 1, 122840a178cSGerd Hoffmann EP_DATA_OUT, 123840a178cSGerd Hoffmann EP_EVENT, 124840a178cSGerd Hoffmann }; 125840a178cSGerd Hoffmann 126983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 1278e3e3897SBandan Das typedef struct MTPMonEntry MTPMonEntry; 1288e3e3897SBandan Das 1298e3e3897SBandan Das struct MTPMonEntry { 1308e3e3897SBandan Das uint32_t event; 1318e3e3897SBandan Das uint32_t handle; 1328e3e3897SBandan Das 1338e3e3897SBandan Das QTAILQ_ENTRY(MTPMonEntry) next; 1348e3e3897SBandan Das }; 1358e3e3897SBandan Das #endif 1368e3e3897SBandan Das 137840a178cSGerd Hoffmann struct MTPControl { 138840a178cSGerd Hoffmann uint16_t code; 139840a178cSGerd Hoffmann uint32_t trans; 140840a178cSGerd Hoffmann int argc; 141840a178cSGerd Hoffmann uint32_t argv[5]; 142840a178cSGerd Hoffmann }; 143840a178cSGerd Hoffmann 144840a178cSGerd Hoffmann struct MTPData { 145840a178cSGerd Hoffmann uint16_t code; 146840a178cSGerd Hoffmann uint32_t trans; 1478a5865f3SIsaac Lozano uint64_t offset; 1488a5865f3SIsaac Lozano uint64_t length; 149840a178cSGerd Hoffmann uint32_t alloc; 150840a178cSGerd Hoffmann uint8_t *data; 151840a178cSGerd Hoffmann bool first; 152840a178cSGerd Hoffmann int fd; 153840a178cSGerd Hoffmann }; 154840a178cSGerd Hoffmann 155840a178cSGerd Hoffmann struct MTPObject { 156840a178cSGerd Hoffmann uint32_t handle; 157840a178cSGerd Hoffmann uint16_t format; 158840a178cSGerd Hoffmann char *name; 159840a178cSGerd Hoffmann char *path; 160840a178cSGerd Hoffmann struct stat stat; 161983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 1628e3e3897SBandan Das /* inotify watch cookie */ 1638e3e3897SBandan Das int watchfd; 1648e3e3897SBandan Das #endif 165840a178cSGerd Hoffmann MTPObject *parent; 16636084d7eSGerd Hoffmann uint32_t nchildren; 1674c7a67f5SBandan Das QLIST_HEAD(, MTPObject) children; 1684c7a67f5SBandan Das QLIST_ENTRY(MTPObject) list; 16936084d7eSGerd Hoffmann bool have_children; 170840a178cSGerd Hoffmann QTAILQ_ENTRY(MTPObject) next; 171840a178cSGerd Hoffmann }; 172840a178cSGerd Hoffmann 173840a178cSGerd Hoffmann struct MTPState { 174840a178cSGerd Hoffmann USBDevice dev; 175840a178cSGerd Hoffmann char *root; 176840a178cSGerd Hoffmann char *desc; 177840a178cSGerd Hoffmann uint32_t flags; 178840a178cSGerd Hoffmann 179840a178cSGerd Hoffmann MTPData *data_in; 180840a178cSGerd Hoffmann MTPData *data_out; 181840a178cSGerd Hoffmann MTPControl *result; 182840a178cSGerd Hoffmann uint32_t session; 183840a178cSGerd Hoffmann uint32_t next_handle; 184ec6206a6SBandan Das bool readonly; 185840a178cSGerd Hoffmann 186840a178cSGerd Hoffmann QTAILQ_HEAD(, MTPObject) objects; 187983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 1888e3e3897SBandan Das /* inotify descriptor */ 1898e3e3897SBandan Das int inotifyfd; 1908e3e3897SBandan Das QTAILQ_HEAD(events, MTPMonEntry) events; 1918e3e3897SBandan Das #endif 19288d5f381SBandan Das /* Responder is expecting a write operation */ 19388d5f381SBandan Das bool write_pending; 19488d5f381SBandan Das struct { 19588d5f381SBandan Das uint32_t parent_handle; 19688d5f381SBandan Das uint16_t format; 19788d5f381SBandan Das uint32_t size; 19888d5f381SBandan Das char *filename; 19988d5f381SBandan Das } dataset; 200840a178cSGerd Hoffmann }; 201840a178cSGerd Hoffmann 202*53735befSBandan Das /* 203*53735befSBandan Das * ObjectInfo dataset received from initiator 204*53735befSBandan Das * Fields we don't care about are ignored 205*53735befSBandan Das */ 206*53735befSBandan Das typedef struct { 207*53735befSBandan Das uint32_t storage_id; /*unused*/ 208*53735befSBandan Das uint16_t format; 209*53735befSBandan Das uint16_t protection_status; /*unused*/ 210*53735befSBandan Das uint32_t size; 211*53735befSBandan Das uint16_t thumb_format; /*unused*/ 212*53735befSBandan Das uint32_t thumb_comp_sz; /*unused*/ 213*53735befSBandan Das uint32_t thumb_pix_width; /*unused*/ 214*53735befSBandan Das uint32_t thumb_pix_height; /*unused*/ 215*53735befSBandan Das uint32_t image_pix_width; /*unused*/ 216*53735befSBandan Das uint32_t image_pix_height; /*unused*/ 217*53735befSBandan Das uint32_t image_bit_depth; /*unused*/ 218*53735befSBandan Das uint32_t parent; /*unused*/ 219*53735befSBandan Das uint16_t assoc_type; 220*53735befSBandan Das uint32_t assoc_desc; 221*53735befSBandan Das uint32_t seq_no; /*unused*/ 222*53735befSBandan Das uint8_t length; /*part of filename field*/ 223*53735befSBandan Das uint16_t filename[0]; 224*53735befSBandan Das char date_created[0]; /*unused*/ 225*53735befSBandan Das char date_modified[0]; /*unused*/ 226*53735befSBandan Das char keywords[0]; /*unused*/ 227*53735befSBandan Das /* string and other data follows */ 228*53735befSBandan Das } QEMU_PACKED ObjectInfo; 229*53735befSBandan Das 2307c03a899SGonglei #define TYPE_USB_MTP "usb-mtp" 2317c03a899SGonglei #define USB_MTP(obj) OBJECT_CHECK(MTPState, (obj), TYPE_USB_MTP) 2327c03a899SGonglei 233840a178cSGerd Hoffmann #define QEMU_STORAGE_ID 0x00010001 234840a178cSGerd Hoffmann 235840a178cSGerd Hoffmann #define MTP_FLAG_WRITABLE 0 236840a178cSGerd Hoffmann 237840a178cSGerd Hoffmann #define FLAG_SET(_mtp, _flag) ((_mtp)->flags & (1 << (_flag))) 238840a178cSGerd Hoffmann 239840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 240840a178cSGerd Hoffmann 241840a178cSGerd Hoffmann #define MTP_MANUFACTURER "QEMU" 242840a178cSGerd Hoffmann #define MTP_PRODUCT "QEMU filesharing" 243840a178cSGerd Hoffmann 244840a178cSGerd Hoffmann enum { 245840a178cSGerd Hoffmann STR_MANUFACTURER = 1, 246840a178cSGerd Hoffmann STR_PRODUCT, 247840a178cSGerd Hoffmann STR_SERIALNUMBER, 24813d54125SGerd Hoffmann STR_MTP, 249840a178cSGerd Hoffmann STR_CONFIG_FULL, 250840a178cSGerd Hoffmann STR_CONFIG_HIGH, 251840a178cSGerd Hoffmann STR_CONFIG_SUPER, 252840a178cSGerd Hoffmann }; 253840a178cSGerd Hoffmann 254840a178cSGerd Hoffmann static const USBDescStrings desc_strings = { 255840a178cSGerd Hoffmann [STR_MANUFACTURER] = MTP_MANUFACTURER, 256840a178cSGerd Hoffmann [STR_PRODUCT] = MTP_PRODUCT, 257840a178cSGerd Hoffmann [STR_SERIALNUMBER] = "34617", 25813d54125SGerd Hoffmann [STR_MTP] = "MTP", 259840a178cSGerd Hoffmann [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", 260840a178cSGerd Hoffmann [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", 261840a178cSGerd Hoffmann [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", 262840a178cSGerd Hoffmann }; 263840a178cSGerd Hoffmann 264840a178cSGerd Hoffmann static const USBDescIface desc_iface_full = { 265840a178cSGerd Hoffmann .bInterfaceNumber = 0, 266840a178cSGerd Hoffmann .bNumEndpoints = 3, 267840a178cSGerd Hoffmann .bInterfaceClass = USB_CLASS_STILL_IMAGE, 268840a178cSGerd Hoffmann .bInterfaceSubClass = 0x01, 269840a178cSGerd Hoffmann .bInterfaceProtocol = 0x01, 27013d54125SGerd Hoffmann .iInterface = STR_MTP, 271840a178cSGerd Hoffmann .eps = (USBDescEndpoint[]) { 272840a178cSGerd Hoffmann { 273840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | EP_DATA_IN, 274840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 275840a178cSGerd Hoffmann .wMaxPacketSize = 64, 276840a178cSGerd Hoffmann },{ 277840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | EP_DATA_OUT, 278840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 279840a178cSGerd Hoffmann .wMaxPacketSize = 64, 280840a178cSGerd Hoffmann },{ 281840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | EP_EVENT, 282840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_INT, 28393d592e3SBandan Das .wMaxPacketSize = 64, 284840a178cSGerd Hoffmann .bInterval = 0x0a, 285840a178cSGerd Hoffmann }, 286840a178cSGerd Hoffmann } 287840a178cSGerd Hoffmann }; 288840a178cSGerd Hoffmann 289840a178cSGerd Hoffmann static const USBDescDevice desc_device_full = { 290840a178cSGerd Hoffmann .bcdUSB = 0x0200, 291840a178cSGerd Hoffmann .bMaxPacketSize0 = 8, 292840a178cSGerd Hoffmann .bNumConfigurations = 1, 293840a178cSGerd Hoffmann .confs = (USBDescConfig[]) { 294840a178cSGerd Hoffmann { 295840a178cSGerd Hoffmann .bNumInterfaces = 1, 296840a178cSGerd Hoffmann .bConfigurationValue = 1, 297840a178cSGerd Hoffmann .iConfiguration = STR_CONFIG_FULL, 298840a178cSGerd Hoffmann .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, 299840a178cSGerd Hoffmann .bMaxPower = 2, 300840a178cSGerd Hoffmann .nif = 1, 301840a178cSGerd Hoffmann .ifs = &desc_iface_full, 302840a178cSGerd Hoffmann }, 303840a178cSGerd Hoffmann }, 304840a178cSGerd Hoffmann }; 305840a178cSGerd Hoffmann 306840a178cSGerd Hoffmann static const USBDescIface desc_iface_high = { 307840a178cSGerd Hoffmann .bInterfaceNumber = 0, 308840a178cSGerd Hoffmann .bNumEndpoints = 3, 309840a178cSGerd Hoffmann .bInterfaceClass = USB_CLASS_STILL_IMAGE, 310840a178cSGerd Hoffmann .bInterfaceSubClass = 0x01, 311840a178cSGerd Hoffmann .bInterfaceProtocol = 0x01, 31213d54125SGerd Hoffmann .iInterface = STR_MTP, 313840a178cSGerd Hoffmann .eps = (USBDescEndpoint[]) { 314840a178cSGerd Hoffmann { 315840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | EP_DATA_IN, 316840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 317840a178cSGerd Hoffmann .wMaxPacketSize = 512, 318840a178cSGerd Hoffmann },{ 319840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | EP_DATA_OUT, 320840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 321840a178cSGerd Hoffmann .wMaxPacketSize = 512, 322840a178cSGerd Hoffmann },{ 323840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | EP_EVENT, 324840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_INT, 32593d592e3SBandan Das .wMaxPacketSize = 64, 326840a178cSGerd Hoffmann .bInterval = 0x0a, 327840a178cSGerd Hoffmann }, 328840a178cSGerd Hoffmann } 329840a178cSGerd Hoffmann }; 330840a178cSGerd Hoffmann 331840a178cSGerd Hoffmann static const USBDescDevice desc_device_high = { 332840a178cSGerd Hoffmann .bcdUSB = 0x0200, 333840a178cSGerd Hoffmann .bMaxPacketSize0 = 64, 334840a178cSGerd Hoffmann .bNumConfigurations = 1, 335840a178cSGerd Hoffmann .confs = (USBDescConfig[]) { 336840a178cSGerd Hoffmann { 337840a178cSGerd Hoffmann .bNumInterfaces = 1, 338840a178cSGerd Hoffmann .bConfigurationValue = 1, 339840a178cSGerd Hoffmann .iConfiguration = STR_CONFIG_HIGH, 340840a178cSGerd Hoffmann .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, 341840a178cSGerd Hoffmann .bMaxPower = 2, 342840a178cSGerd Hoffmann .nif = 1, 343840a178cSGerd Hoffmann .ifs = &desc_iface_high, 344840a178cSGerd Hoffmann }, 345840a178cSGerd Hoffmann }, 346840a178cSGerd Hoffmann }; 347840a178cSGerd Hoffmann 348840a178cSGerd Hoffmann static const USBDescMSOS desc_msos = { 349840a178cSGerd Hoffmann .CompatibleID = "MTP", 350840a178cSGerd Hoffmann .SelectiveSuspendEnabled = true, 351840a178cSGerd Hoffmann }; 352840a178cSGerd Hoffmann 353840a178cSGerd Hoffmann static const USBDesc desc = { 354840a178cSGerd Hoffmann .id = { 355840a178cSGerd Hoffmann .idVendor = 0x46f4, /* CRC16() of "QEMU" */ 356840a178cSGerd Hoffmann .idProduct = 0x0004, 357840a178cSGerd Hoffmann .bcdDevice = 0, 358840a178cSGerd Hoffmann .iManufacturer = STR_MANUFACTURER, 359840a178cSGerd Hoffmann .iProduct = STR_PRODUCT, 360840a178cSGerd Hoffmann .iSerialNumber = STR_SERIALNUMBER, 361840a178cSGerd Hoffmann }, 362840a178cSGerd Hoffmann .full = &desc_device_full, 363840a178cSGerd Hoffmann .high = &desc_device_high, 364840a178cSGerd Hoffmann .str = desc_strings, 365840a178cSGerd Hoffmann .msos = &desc_msos, 366840a178cSGerd Hoffmann }; 367840a178cSGerd Hoffmann 368840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 369840a178cSGerd Hoffmann 370840a178cSGerd Hoffmann static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle, 371840a178cSGerd Hoffmann MTPObject *parent, char *name) 372840a178cSGerd Hoffmann { 373840a178cSGerd Hoffmann MTPObject *o = g_new0(MTPObject, 1); 374840a178cSGerd Hoffmann 375840a178cSGerd Hoffmann if (name[0] == '.') { 376840a178cSGerd Hoffmann goto ignore; 377840a178cSGerd Hoffmann } 378840a178cSGerd Hoffmann 379840a178cSGerd Hoffmann o->handle = handle; 380840a178cSGerd Hoffmann o->parent = parent; 381840a178cSGerd Hoffmann o->name = g_strdup(name); 382840a178cSGerd Hoffmann if (parent == NULL) { 383840a178cSGerd Hoffmann o->path = g_strdup(name); 384840a178cSGerd Hoffmann } else { 385840a178cSGerd Hoffmann o->path = g_strdup_printf("%s/%s", parent->path, name); 386840a178cSGerd Hoffmann } 387840a178cSGerd Hoffmann 388840a178cSGerd Hoffmann if (lstat(o->path, &o->stat) != 0) { 389840a178cSGerd Hoffmann goto ignore; 390840a178cSGerd Hoffmann } 391840a178cSGerd Hoffmann if (S_ISREG(o->stat.st_mode)) { 392840a178cSGerd Hoffmann o->format = FMT_UNDEFINED_OBJECT; 393840a178cSGerd Hoffmann } else if (S_ISDIR(o->stat.st_mode)) { 394840a178cSGerd Hoffmann o->format = FMT_ASSOCIATION; 395840a178cSGerd Hoffmann } else { 396840a178cSGerd Hoffmann goto ignore; 397840a178cSGerd Hoffmann } 398840a178cSGerd Hoffmann 399840a178cSGerd Hoffmann if (access(o->path, R_OK) != 0) { 400840a178cSGerd Hoffmann goto ignore; 401840a178cSGerd Hoffmann } 402840a178cSGerd Hoffmann 4031c76551fSGerd Hoffmann trace_usb_mtp_object_alloc(s->dev.addr, o->handle, o->path); 404840a178cSGerd Hoffmann 405840a178cSGerd Hoffmann QTAILQ_INSERT_TAIL(&s->objects, o, next); 406840a178cSGerd Hoffmann return o; 407840a178cSGerd Hoffmann 408840a178cSGerd Hoffmann ignore: 409840a178cSGerd Hoffmann g_free(o->name); 410840a178cSGerd Hoffmann g_free(o->path); 411840a178cSGerd Hoffmann g_free(o); 412840a178cSGerd Hoffmann return NULL; 413840a178cSGerd Hoffmann } 414840a178cSGerd Hoffmann 415840a178cSGerd Hoffmann static void usb_mtp_object_free(MTPState *s, MTPObject *o) 416840a178cSGerd Hoffmann { 4174c7a67f5SBandan Das MTPObject *iter; 4184c7a67f5SBandan Das 4194c7a67f5SBandan Das if (!o) { 4204c7a67f5SBandan Das return; 4214c7a67f5SBandan Das } 422840a178cSGerd Hoffmann 4231c76551fSGerd Hoffmann trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path); 424840a178cSGerd Hoffmann 425840a178cSGerd Hoffmann QTAILQ_REMOVE(&s->objects, o, next); 4264c7a67f5SBandan Das if (o->parent) { 4274c7a67f5SBandan Das QLIST_REMOVE(o, list); 4284c7a67f5SBandan Das o->parent->nchildren--; 429840a178cSGerd Hoffmann } 4304c7a67f5SBandan Das 4314c7a67f5SBandan Das while (!QLIST_EMPTY(&o->children)) { 4324c7a67f5SBandan Das iter = QLIST_FIRST(&o->children); 4334c7a67f5SBandan Das usb_mtp_object_free(s, iter); 4344c7a67f5SBandan Das } 435840a178cSGerd Hoffmann g_free(o->name); 436840a178cSGerd Hoffmann g_free(o->path); 437840a178cSGerd Hoffmann g_free(o); 438840a178cSGerd Hoffmann } 439840a178cSGerd Hoffmann 440840a178cSGerd Hoffmann static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle) 441840a178cSGerd Hoffmann { 442840a178cSGerd Hoffmann MTPObject *o; 443840a178cSGerd Hoffmann 444840a178cSGerd Hoffmann QTAILQ_FOREACH(o, &s->objects, next) { 445840a178cSGerd Hoffmann if (o->handle == handle) { 446840a178cSGerd Hoffmann return o; 447840a178cSGerd Hoffmann } 448840a178cSGerd Hoffmann } 449840a178cSGerd Hoffmann return NULL; 450840a178cSGerd Hoffmann } 451840a178cSGerd Hoffmann 4524c7a67f5SBandan Das static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, 4534c7a67f5SBandan Das char *name) 4544c7a67f5SBandan Das { 4554c7a67f5SBandan Das MTPObject *child = 4564c7a67f5SBandan Das usb_mtp_object_alloc(s, s->next_handle++, o, name); 4574c7a67f5SBandan Das 4584c7a67f5SBandan Das if (child) { 4594c7a67f5SBandan Das trace_usb_mtp_add_child(s->dev.addr, child->handle, child->path); 4604c7a67f5SBandan Das QLIST_INSERT_HEAD(&o->children, child, list); 4614c7a67f5SBandan Das o->nchildren++; 4624c7a67f5SBandan Das 4634c7a67f5SBandan Das if (child->format == FMT_ASSOCIATION) { 4644c7a67f5SBandan Das QLIST_INIT(&child->children); 4654c7a67f5SBandan Das } 4664c7a67f5SBandan Das } 4674c7a67f5SBandan Das 4684c7a67f5SBandan Das return child; 4694c7a67f5SBandan Das } 4704c7a67f5SBandan Das 4718e3e3897SBandan Das static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, 4728e3e3897SBandan Das char *name, int len) 4738e3e3897SBandan Das { 4748e3e3897SBandan Das MTPObject *iter; 4758e3e3897SBandan Das 4768e3e3897SBandan Das QLIST_FOREACH(iter, &parent->children, list) { 4778e3e3897SBandan Das if (strncmp(iter->name, name, len) == 0) { 4788e3e3897SBandan Das return iter; 4798e3e3897SBandan Das } 4808e3e3897SBandan Das } 4818e3e3897SBandan Das 4828e3e3897SBandan Das return NULL; 4838e3e3897SBandan Das } 4848e3e3897SBandan Das 485*53735befSBandan Das #ifdef CONFIG_INOTIFY1 4868e3e3897SBandan Das static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd) 4878e3e3897SBandan Das { 4888e3e3897SBandan Das MTPObject *iter; 4898e3e3897SBandan Das 4908e3e3897SBandan Das QTAILQ_FOREACH(iter, &s->objects, next) { 4918e3e3897SBandan Das if (iter->watchfd == wd) { 4928e3e3897SBandan Das return iter; 4938e3e3897SBandan Das } 4948e3e3897SBandan Das } 4958e3e3897SBandan Das 4968e3e3897SBandan Das return NULL; 4978e3e3897SBandan Das } 4988e3e3897SBandan Das 4998e3e3897SBandan Das static void inotify_watchfn(void *arg) 5008e3e3897SBandan Das { 5018e3e3897SBandan Das MTPState *s = arg; 5028e3e3897SBandan Das ssize_t bytes; 5038e3e3897SBandan Das /* From the man page: atleast one event can be read */ 5048e3e3897SBandan Das int pos; 505f34d57d3SPeter Xu char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; 5068e3e3897SBandan Das 5078e3e3897SBandan Das for (;;) { 508f34d57d3SPeter Xu bytes = read(s->inotifyfd, buf, sizeof(buf)); 5098e3e3897SBandan Das pos = 0; 5108e3e3897SBandan Das 5118e3e3897SBandan Das if (bytes <= 0) { 5128e3e3897SBandan Das /* Better luck next time */ 5138e3e3897SBandan Das return; 5148e3e3897SBandan Das } 5158e3e3897SBandan Das 5168e3e3897SBandan Das /* 5178e3e3897SBandan Das * TODO: Ignore initiator initiated events. 5188e3e3897SBandan Das * For now we are good because the store is RO 5198e3e3897SBandan Das */ 5208e3e3897SBandan Das while (bytes > 0) { 5218e3e3897SBandan Das char *p = buf + pos; 5228e3e3897SBandan Das struct inotify_event *event = (struct inotify_event *)p; 5238e3e3897SBandan Das int watchfd = 0; 5248e3e3897SBandan Das uint32_t mask = event->mask & (IN_CREATE | IN_DELETE | 5258e3e3897SBandan Das IN_MODIFY | IN_IGNORED); 5268e3e3897SBandan Das MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd); 5278e3e3897SBandan Das MTPMonEntry *entry = NULL; 5288e3e3897SBandan Das MTPObject *o; 5298e3e3897SBandan Das 5308e3e3897SBandan Das pos = pos + sizeof(struct inotify_event) + event->len; 5318e3e3897SBandan Das bytes = bytes - pos; 5328e3e3897SBandan Das 5338e3e3897SBandan Das if (!parent) { 5348e3e3897SBandan Das continue; 5358e3e3897SBandan Das } 5368e3e3897SBandan Das 5378e3e3897SBandan Das switch (mask) { 5388e3e3897SBandan Das case IN_CREATE: 5398e3e3897SBandan Das if (usb_mtp_object_lookup_name 5408e3e3897SBandan Das (parent, event->name, event->len)) { 5418e3e3897SBandan Das /* Duplicate create event */ 5428e3e3897SBandan Das continue; 5438e3e3897SBandan Das } 5448e3e3897SBandan Das entry = g_new0(MTPMonEntry, 1); 5458e3e3897SBandan Das entry->handle = s->next_handle; 5468e3e3897SBandan Das entry->event = EVT_OBJ_ADDED; 5478e3e3897SBandan Das o = usb_mtp_add_child(s, parent, event->name); 5488e3e3897SBandan Das if (!o) { 5498e3e3897SBandan Das g_free(entry); 5508e3e3897SBandan Das continue; 5518e3e3897SBandan Das } 5528e3e3897SBandan Das o->watchfd = watchfd; 5538e3e3897SBandan Das trace_usb_mtp_inotify_event(s->dev.addr, event->name, 5548e3e3897SBandan Das event->mask, "Obj Added"); 5558e3e3897SBandan Das break; 5568e3e3897SBandan Das 5578e3e3897SBandan Das case IN_DELETE: 5588e3e3897SBandan Das /* 5598e3e3897SBandan Das * The kernel issues a IN_IGNORED event 5608e3e3897SBandan Das * when a dir containing a watchpoint is 5618e3e3897SBandan Das * deleted, so we don't have to delete the 5628e3e3897SBandan Das * watchpoint 5638e3e3897SBandan Das */ 5648e3e3897SBandan Das o = usb_mtp_object_lookup_name(parent, event->name, event->len); 5658e3e3897SBandan Das if (!o) { 5668e3e3897SBandan Das continue; 5678e3e3897SBandan Das } 5688e3e3897SBandan Das entry = g_new0(MTPMonEntry, 1); 5698e3e3897SBandan Das entry->handle = o->handle; 5708e3e3897SBandan Das entry->event = EVT_OBJ_REMOVED; 5718e3e3897SBandan Das trace_usb_mtp_inotify_event(s->dev.addr, o->path, 5728e3e3897SBandan Das event->mask, "Obj Deleted"); 573ec93e158SBandan Das usb_mtp_object_free(s, o); 5748e3e3897SBandan Das break; 5758e3e3897SBandan Das 5768e3e3897SBandan Das case IN_MODIFY: 5778e3e3897SBandan Das o = usb_mtp_object_lookup_name(parent, event->name, event->len); 5788e3e3897SBandan Das if (!o) { 5798e3e3897SBandan Das continue; 5808e3e3897SBandan Das } 5818e3e3897SBandan Das entry = g_new0(MTPMonEntry, 1); 5828e3e3897SBandan Das entry->handle = o->handle; 5838e3e3897SBandan Das entry->event = EVT_OBJ_INFO_CHANGED; 5848e3e3897SBandan Das trace_usb_mtp_inotify_event(s->dev.addr, o->path, 5858e3e3897SBandan Das event->mask, "Obj Modified"); 5868e3e3897SBandan Das break; 5878e3e3897SBandan Das 5888e3e3897SBandan Das case IN_IGNORED: 5895d13ebeaSBandan Das trace_usb_mtp_inotify_event(s->dev.addr, parent->path, 5905d13ebeaSBandan Das event->mask, "Obj parent dir ignored"); 5918e3e3897SBandan Das break; 5928e3e3897SBandan Das 5938e3e3897SBandan Das default: 5948e3e3897SBandan Das fprintf(stderr, "usb-mtp: failed to parse inotify event\n"); 5958e3e3897SBandan Das continue; 5968e3e3897SBandan Das } 5978e3e3897SBandan Das 5988e3e3897SBandan Das if (entry) { 5998e3e3897SBandan Das QTAILQ_INSERT_HEAD(&s->events, entry, next); 6008e3e3897SBandan Das } 6018e3e3897SBandan Das } 6028e3e3897SBandan Das } 6038e3e3897SBandan Das } 6048e3e3897SBandan Das 6058e3e3897SBandan Das static int usb_mtp_inotify_init(MTPState *s) 6068e3e3897SBandan Das { 6078e3e3897SBandan Das int fd; 6088e3e3897SBandan Das 6098e3e3897SBandan Das fd = inotify_init1(IN_NONBLOCK); 6108e3e3897SBandan Das if (fd == -1) { 6118e3e3897SBandan Das return 1; 6128e3e3897SBandan Das } 6138e3e3897SBandan Das 6148e3e3897SBandan Das QTAILQ_INIT(&s->events); 6158e3e3897SBandan Das s->inotifyfd = fd; 6168e3e3897SBandan Das 6178e3e3897SBandan Das qemu_set_fd_handler(fd, inotify_watchfn, NULL, s); 6188e3e3897SBandan Das 6198e3e3897SBandan Das return 0; 6208e3e3897SBandan Das } 6218e3e3897SBandan Das 6228e3e3897SBandan Das static void usb_mtp_inotify_cleanup(MTPState *s) 6238e3e3897SBandan Das { 624c22d5dcdSBandan Das MTPMonEntry *e, *p; 6258e3e3897SBandan Das 6268e3e3897SBandan Das if (!s->inotifyfd) { 6278e3e3897SBandan Das return; 6288e3e3897SBandan Das } 6298e3e3897SBandan Das 6308e3e3897SBandan Das qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s); 6318e3e3897SBandan Das close(s->inotifyfd); 6328e3e3897SBandan Das 633c22d5dcdSBandan Das QTAILQ_FOREACH_SAFE(e, &s->events, next, p) { 6348e3e3897SBandan Das QTAILQ_REMOVE(&s->events, e, next); 6358e3e3897SBandan Das g_free(e); 6368e3e3897SBandan Das } 6378e3e3897SBandan Das } 6388e3e3897SBandan Das 6398e3e3897SBandan Das static int usb_mtp_add_watch(int inotifyfd, char *path) 6408e3e3897SBandan Das { 6418e3e3897SBandan Das uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY | 6428e3e3897SBandan Das IN_ISDIR; 6438e3e3897SBandan Das 6448e3e3897SBandan Das return inotify_add_watch(inotifyfd, path, mask); 6458e3e3897SBandan Das } 6468e3e3897SBandan Das #endif 6478e3e3897SBandan Das 648840a178cSGerd Hoffmann static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) 649840a178cSGerd Hoffmann { 650840a178cSGerd Hoffmann struct dirent *entry; 651840a178cSGerd Hoffmann DIR *dir; 652840a178cSGerd Hoffmann 65336084d7eSGerd Hoffmann if (o->have_children) { 65436084d7eSGerd Hoffmann return; 65536084d7eSGerd Hoffmann } 65636084d7eSGerd Hoffmann o->have_children = true; 65736084d7eSGerd Hoffmann 658840a178cSGerd Hoffmann dir = opendir(o->path); 659840a178cSGerd Hoffmann if (!dir) { 660840a178cSGerd Hoffmann return; 661840a178cSGerd Hoffmann } 662983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 6638e3e3897SBandan Das int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path); 6648e3e3897SBandan Das if (watchfd == -1) { 6658e3e3897SBandan Das fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path); 6668e3e3897SBandan Das } else { 6678e3e3897SBandan Das trace_usb_mtp_inotify_event(s->dev.addr, o->path, 6688e3e3897SBandan Das 0, "Watch Added"); 6698e3e3897SBandan Das o->watchfd = watchfd; 6708e3e3897SBandan Das } 6718e3e3897SBandan Das #endif 672840a178cSGerd Hoffmann while ((entry = readdir(dir)) != NULL) { 6734c7a67f5SBandan Das usb_mtp_add_child(s, o, entry->d_name); 674840a178cSGerd Hoffmann } 675840a178cSGerd Hoffmann closedir(dir); 676840a178cSGerd Hoffmann } 677840a178cSGerd Hoffmann 678840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 679840a178cSGerd Hoffmann 680840a178cSGerd Hoffmann static MTPData *usb_mtp_data_alloc(MTPControl *c) 681840a178cSGerd Hoffmann { 682840a178cSGerd Hoffmann MTPData *data = g_new0(MTPData, 1); 683840a178cSGerd Hoffmann 684840a178cSGerd Hoffmann data->code = c->code; 685840a178cSGerd Hoffmann data->trans = c->trans; 686840a178cSGerd Hoffmann data->fd = -1; 687840a178cSGerd Hoffmann data->first = true; 688840a178cSGerd Hoffmann return data; 689840a178cSGerd Hoffmann } 690840a178cSGerd Hoffmann 691840a178cSGerd Hoffmann static void usb_mtp_data_free(MTPData *data) 692840a178cSGerd Hoffmann { 693840a178cSGerd Hoffmann if (data == NULL) { 694840a178cSGerd Hoffmann return; 695840a178cSGerd Hoffmann } 696840a178cSGerd Hoffmann if (data->fd != -1) { 697840a178cSGerd Hoffmann close(data->fd); 698840a178cSGerd Hoffmann } 699840a178cSGerd Hoffmann g_free(data->data); 700840a178cSGerd Hoffmann g_free(data); 701840a178cSGerd Hoffmann } 702840a178cSGerd Hoffmann 703840a178cSGerd Hoffmann static void usb_mtp_realloc(MTPData *data, uint32_t bytes) 704840a178cSGerd Hoffmann { 705840a178cSGerd Hoffmann if (data->length + bytes <= data->alloc) { 706840a178cSGerd Hoffmann return; 707840a178cSGerd Hoffmann } 708840a178cSGerd Hoffmann data->alloc = (data->length + bytes + 0xff) & ~0xff; 709840a178cSGerd Hoffmann data->data = g_realloc(data->data, data->alloc); 710840a178cSGerd Hoffmann } 711840a178cSGerd Hoffmann 712840a178cSGerd Hoffmann static void usb_mtp_add_u8(MTPData *data, uint8_t val) 713840a178cSGerd Hoffmann { 714840a178cSGerd Hoffmann usb_mtp_realloc(data, 1); 715840a178cSGerd Hoffmann data->data[data->length++] = val; 716840a178cSGerd Hoffmann } 717840a178cSGerd Hoffmann 718840a178cSGerd Hoffmann static void usb_mtp_add_u16(MTPData *data, uint16_t val) 719840a178cSGerd Hoffmann { 720840a178cSGerd Hoffmann usb_mtp_realloc(data, 2); 721840a178cSGerd Hoffmann data->data[data->length++] = (val >> 0) & 0xff; 722840a178cSGerd Hoffmann data->data[data->length++] = (val >> 8) & 0xff; 723840a178cSGerd Hoffmann } 724840a178cSGerd Hoffmann 725840a178cSGerd Hoffmann static void usb_mtp_add_u32(MTPData *data, uint32_t val) 726840a178cSGerd Hoffmann { 727840a178cSGerd Hoffmann usb_mtp_realloc(data, 4); 728840a178cSGerd Hoffmann data->data[data->length++] = (val >> 0) & 0xff; 729840a178cSGerd Hoffmann data->data[data->length++] = (val >> 8) & 0xff; 730840a178cSGerd Hoffmann data->data[data->length++] = (val >> 16) & 0xff; 731840a178cSGerd Hoffmann data->data[data->length++] = (val >> 24) & 0xff; 732840a178cSGerd Hoffmann } 733840a178cSGerd Hoffmann 734840a178cSGerd Hoffmann static void usb_mtp_add_u64(MTPData *data, uint64_t val) 735840a178cSGerd Hoffmann { 736ada435f4SGerd Hoffmann usb_mtp_realloc(data, 8); 737840a178cSGerd Hoffmann data->data[data->length++] = (val >> 0) & 0xff; 738840a178cSGerd Hoffmann data->data[data->length++] = (val >> 8) & 0xff; 739840a178cSGerd Hoffmann data->data[data->length++] = (val >> 16) & 0xff; 740840a178cSGerd Hoffmann data->data[data->length++] = (val >> 24) & 0xff; 741840a178cSGerd Hoffmann data->data[data->length++] = (val >> 32) & 0xff; 742840a178cSGerd Hoffmann data->data[data->length++] = (val >> 40) & 0xff; 743840a178cSGerd Hoffmann data->data[data->length++] = (val >> 48) & 0xff; 744ada435f4SGerd Hoffmann data->data[data->length++] = (val >> 56) & 0xff; 745840a178cSGerd Hoffmann } 746840a178cSGerd Hoffmann 747840a178cSGerd Hoffmann static void usb_mtp_add_u16_array(MTPData *data, uint32_t len, 748840a178cSGerd Hoffmann const uint16_t *vals) 749840a178cSGerd Hoffmann { 750840a178cSGerd Hoffmann int i; 751840a178cSGerd Hoffmann 752840a178cSGerd Hoffmann usb_mtp_add_u32(data, len); 753840a178cSGerd Hoffmann for (i = 0; i < len; i++) { 754840a178cSGerd Hoffmann usb_mtp_add_u16(data, vals[i]); 755840a178cSGerd Hoffmann } 756840a178cSGerd Hoffmann } 757840a178cSGerd Hoffmann 758840a178cSGerd Hoffmann static void usb_mtp_add_u32_array(MTPData *data, uint32_t len, 759840a178cSGerd Hoffmann const uint32_t *vals) 760840a178cSGerd Hoffmann { 761840a178cSGerd Hoffmann int i; 762840a178cSGerd Hoffmann 763840a178cSGerd Hoffmann usb_mtp_add_u32(data, len); 764840a178cSGerd Hoffmann for (i = 0; i < len; i++) { 765840a178cSGerd Hoffmann usb_mtp_add_u32(data, vals[i]); 766840a178cSGerd Hoffmann } 767840a178cSGerd Hoffmann } 768840a178cSGerd Hoffmann 769840a178cSGerd Hoffmann static void usb_mtp_add_wstr(MTPData *data, const wchar_t *str) 770840a178cSGerd Hoffmann { 771840a178cSGerd Hoffmann uint32_t len = wcslen(str); 772840a178cSGerd Hoffmann int i; 773840a178cSGerd Hoffmann 774840a178cSGerd Hoffmann if (len > 0) { 775840a178cSGerd Hoffmann len++; /* include terminating L'\0' */ 776840a178cSGerd Hoffmann } 777840a178cSGerd Hoffmann 778840a178cSGerd Hoffmann usb_mtp_add_u8(data, len); 779840a178cSGerd Hoffmann for (i = 0; i < len; i++) { 780840a178cSGerd Hoffmann usb_mtp_add_u16(data, str[i]); 781840a178cSGerd Hoffmann } 782840a178cSGerd Hoffmann } 783840a178cSGerd Hoffmann 784840a178cSGerd Hoffmann static void usb_mtp_add_str(MTPData *data, const char *str) 785840a178cSGerd Hoffmann { 786840a178cSGerd Hoffmann uint32_t len = strlen(str)+1; 787e3d60bc7SPeter Xu wchar_t *wstr = g_new(wchar_t, len); 788840a178cSGerd Hoffmann size_t ret; 789840a178cSGerd Hoffmann 790840a178cSGerd Hoffmann ret = mbstowcs(wstr, str, len); 791840a178cSGerd Hoffmann if (ret == -1) { 792840a178cSGerd Hoffmann usb_mtp_add_wstr(data, L"Oops"); 793840a178cSGerd Hoffmann } else { 794840a178cSGerd Hoffmann usb_mtp_add_wstr(data, wstr); 795840a178cSGerd Hoffmann } 796e3d60bc7SPeter Xu 797e3d60bc7SPeter Xu g_free(wstr); 798840a178cSGerd Hoffmann } 799840a178cSGerd Hoffmann 800840a178cSGerd Hoffmann static void usb_mtp_add_time(MTPData *data, time_t time) 801840a178cSGerd Hoffmann { 802840a178cSGerd Hoffmann char buf[16]; 803840a178cSGerd Hoffmann struct tm tm; 804840a178cSGerd Hoffmann 805840a178cSGerd Hoffmann gmtime_r(&time, &tm); 806840a178cSGerd Hoffmann strftime(buf, sizeof(buf), "%Y%m%dT%H%M%S", &tm); 807840a178cSGerd Hoffmann usb_mtp_add_str(data, buf); 808840a178cSGerd Hoffmann } 809840a178cSGerd Hoffmann 810840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 811840a178cSGerd Hoffmann 812840a178cSGerd Hoffmann static void usb_mtp_queue_result(MTPState *s, uint16_t code, uint32_t trans, 8139c727584SBandan Das int argc, uint32_t arg0, uint32_t arg1, 8149c727584SBandan Das uint32_t arg2) 815840a178cSGerd Hoffmann { 816840a178cSGerd Hoffmann MTPControl *c = g_new0(MTPControl, 1); 817840a178cSGerd Hoffmann 818840a178cSGerd Hoffmann c->code = code; 819840a178cSGerd Hoffmann c->trans = trans; 820840a178cSGerd Hoffmann c->argc = argc; 821840a178cSGerd Hoffmann if (argc > 0) { 822840a178cSGerd Hoffmann c->argv[0] = arg0; 823840a178cSGerd Hoffmann } 824840a178cSGerd Hoffmann if (argc > 1) { 825840a178cSGerd Hoffmann c->argv[1] = arg1; 826840a178cSGerd Hoffmann } 8279c727584SBandan Das if (argc > 2) { 8289c727584SBandan Das c->argv[2] = arg2; 8299c727584SBandan Das } 830840a178cSGerd Hoffmann 831840a178cSGerd Hoffmann assert(s->result == NULL); 832840a178cSGerd Hoffmann s->result = c; 833840a178cSGerd Hoffmann } 834840a178cSGerd Hoffmann 835840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 836840a178cSGerd Hoffmann 837840a178cSGerd Hoffmann static MTPData *usb_mtp_get_device_info(MTPState *s, MTPControl *c) 838840a178cSGerd Hoffmann { 839840a178cSGerd Hoffmann static const uint16_t ops[] = { 840840a178cSGerd Hoffmann CMD_GET_DEVICE_INFO, 841840a178cSGerd Hoffmann CMD_OPEN_SESSION, 842840a178cSGerd Hoffmann CMD_CLOSE_SESSION, 843840a178cSGerd Hoffmann CMD_GET_STORAGE_IDS, 844840a178cSGerd Hoffmann CMD_GET_STORAGE_INFO, 845840a178cSGerd Hoffmann CMD_GET_NUM_OBJECTS, 846840a178cSGerd Hoffmann CMD_GET_OBJECT_HANDLES, 847840a178cSGerd Hoffmann CMD_GET_OBJECT_INFO, 848ec6206a6SBandan Das CMD_DELETE_OBJECT, 849*53735befSBandan Das CMD_SEND_OBJECT_INFO, 85088d5f381SBandan Das CMD_SEND_OBJECT, 851840a178cSGerd Hoffmann CMD_GET_OBJECT, 852840a178cSGerd Hoffmann CMD_GET_PARTIAL_OBJECT, 85367f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROPS_SUPPORTED, 85467f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROP_DESC, 85567f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROP_VALUE, 856840a178cSGerd Hoffmann }; 857840a178cSGerd Hoffmann static const uint16_t fmt[] = { 858840a178cSGerd Hoffmann FMT_UNDEFINED_OBJECT, 859840a178cSGerd Hoffmann FMT_ASSOCIATION, 860840a178cSGerd Hoffmann }; 861840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 862840a178cSGerd Hoffmann 863840a178cSGerd Hoffmann trace_usb_mtp_op_get_device_info(s->dev.addr); 864840a178cSGerd Hoffmann 865f7eaed85SGerd Hoffmann usb_mtp_add_u16(d, 100); 8661f66fe57SIsaac Lozano usb_mtp_add_u32(d, 0x00000006); 8671f66fe57SIsaac Lozano usb_mtp_add_u16(d, 0x0064); 868840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L""); 869840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0000); 870840a178cSGerd Hoffmann 871840a178cSGerd Hoffmann usb_mtp_add_u16_array(d, ARRAY_SIZE(ops), ops); 872840a178cSGerd Hoffmann usb_mtp_add_u16_array(d, 0, NULL); 873840a178cSGerd Hoffmann usb_mtp_add_u16_array(d, 0, NULL); 874840a178cSGerd Hoffmann usb_mtp_add_u16_array(d, 0, NULL); 875840a178cSGerd Hoffmann usb_mtp_add_u16_array(d, ARRAY_SIZE(fmt), fmt); 876840a178cSGerd Hoffmann 877840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L"" MTP_MANUFACTURER); 878840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L"" MTP_PRODUCT); 879840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L"0.1"); 8809e4eff5bSGerd Hoffmann usb_mtp_add_wstr(d, L"0123456789abcdef0123456789abcdef"); 881840a178cSGerd Hoffmann 882840a178cSGerd Hoffmann return d; 883840a178cSGerd Hoffmann } 884840a178cSGerd Hoffmann 885840a178cSGerd Hoffmann static MTPData *usb_mtp_get_storage_ids(MTPState *s, MTPControl *c) 886840a178cSGerd Hoffmann { 887840a178cSGerd Hoffmann static const uint32_t ids[] = { 888840a178cSGerd Hoffmann QEMU_STORAGE_ID, 889840a178cSGerd Hoffmann }; 890840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 891840a178cSGerd Hoffmann 892840a178cSGerd Hoffmann trace_usb_mtp_op_get_storage_ids(s->dev.addr); 893840a178cSGerd Hoffmann 894840a178cSGerd Hoffmann usb_mtp_add_u32_array(d, ARRAY_SIZE(ids), ids); 895840a178cSGerd Hoffmann 896840a178cSGerd Hoffmann return d; 897840a178cSGerd Hoffmann } 898840a178cSGerd Hoffmann 899840a178cSGerd Hoffmann static MTPData *usb_mtp_get_storage_info(MTPState *s, MTPControl *c) 900840a178cSGerd Hoffmann { 901840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 902840a178cSGerd Hoffmann struct statvfs buf; 903840a178cSGerd Hoffmann int rc; 904840a178cSGerd Hoffmann 905840a178cSGerd Hoffmann trace_usb_mtp_op_get_storage_info(s->dev.addr); 906840a178cSGerd Hoffmann 907840a178cSGerd Hoffmann if (FLAG_SET(s, MTP_FLAG_WRITABLE)) { 908840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0003); 909840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0002); 910840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0000); 911840a178cSGerd Hoffmann } else { 912840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0001); 913840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0002); 914840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0001); 915840a178cSGerd Hoffmann } 916840a178cSGerd Hoffmann 917840a178cSGerd Hoffmann rc = statvfs(s->root, &buf); 918840a178cSGerd Hoffmann if (rc == 0) { 919840a178cSGerd Hoffmann usb_mtp_add_u64(d, (uint64_t)buf.f_frsize * buf.f_blocks); 920840a178cSGerd Hoffmann usb_mtp_add_u64(d, (uint64_t)buf.f_bavail * buf.f_blocks); 921840a178cSGerd Hoffmann usb_mtp_add_u32(d, buf.f_ffree); 922840a178cSGerd Hoffmann } else { 923840a178cSGerd Hoffmann usb_mtp_add_u64(d, 0xffffffff); 924840a178cSGerd Hoffmann usb_mtp_add_u64(d, 0xffffffff); 925840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0xffffffff); 926840a178cSGerd Hoffmann } 927840a178cSGerd Hoffmann 928840a178cSGerd Hoffmann usb_mtp_add_str(d, s->desc); 929840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L"123456789abcdef"); 930840a178cSGerd Hoffmann return d; 931840a178cSGerd Hoffmann } 932840a178cSGerd Hoffmann 933840a178cSGerd Hoffmann static MTPData *usb_mtp_get_object_handles(MTPState *s, MTPControl *c, 934840a178cSGerd Hoffmann MTPObject *o) 935840a178cSGerd Hoffmann { 936840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 9374c7a67f5SBandan Das uint32_t i = 0, handles[o->nchildren]; 9384c7a67f5SBandan Das MTPObject *iter; 939840a178cSGerd Hoffmann 940840a178cSGerd Hoffmann trace_usb_mtp_op_get_object_handles(s->dev.addr, o->handle, o->path); 941840a178cSGerd Hoffmann 9424c7a67f5SBandan Das QLIST_FOREACH(iter, &o->children, list) { 9434c7a67f5SBandan Das handles[i++] = iter->handle; 944840a178cSGerd Hoffmann } 9454c7a67f5SBandan Das assert(i == o->nchildren); 946840a178cSGerd Hoffmann usb_mtp_add_u32_array(d, o->nchildren, handles); 947840a178cSGerd Hoffmann 948840a178cSGerd Hoffmann return d; 949840a178cSGerd Hoffmann } 950840a178cSGerd Hoffmann 951840a178cSGerd Hoffmann static MTPData *usb_mtp_get_object_info(MTPState *s, MTPControl *c, 952840a178cSGerd Hoffmann MTPObject *o) 953840a178cSGerd Hoffmann { 954840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 955840a178cSGerd Hoffmann 956840a178cSGerd Hoffmann trace_usb_mtp_op_get_object_info(s->dev.addr, o->handle, o->path); 957840a178cSGerd Hoffmann 958840a178cSGerd Hoffmann usb_mtp_add_u32(d, QEMU_STORAGE_ID); 959840a178cSGerd Hoffmann usb_mtp_add_u16(d, o->format); 960840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0); 9618a5865f3SIsaac Lozano 9628a5865f3SIsaac Lozano if (o->stat.st_size > 0xFFFFFFFF) { 9638a5865f3SIsaac Lozano usb_mtp_add_u32(d, 0xFFFFFFFF); 9648a5865f3SIsaac Lozano } else { 965840a178cSGerd Hoffmann usb_mtp_add_u32(d, o->stat.st_size); 9668a5865f3SIsaac Lozano } 967840a178cSGerd Hoffmann 968840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0); 969840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 970840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 971840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 972840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 973840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 974840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 975840a178cSGerd Hoffmann 976840a178cSGerd Hoffmann if (o->parent) { 977840a178cSGerd Hoffmann usb_mtp_add_u32(d, o->parent->handle); 978840a178cSGerd Hoffmann } else { 979840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 980840a178cSGerd Hoffmann } 981840a178cSGerd Hoffmann if (o->format == FMT_ASSOCIATION) { 982840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0001); 983840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0x00000001); 984840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 985840a178cSGerd Hoffmann } else { 986840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0); 987840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 988840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 989840a178cSGerd Hoffmann } 990840a178cSGerd Hoffmann 991840a178cSGerd Hoffmann usb_mtp_add_str(d, o->name); 992840a178cSGerd Hoffmann usb_mtp_add_time(d, o->stat.st_ctime); 993840a178cSGerd Hoffmann usb_mtp_add_time(d, o->stat.st_mtime); 994840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L""); 995840a178cSGerd Hoffmann 996840a178cSGerd Hoffmann return d; 997840a178cSGerd Hoffmann } 998840a178cSGerd Hoffmann 999840a178cSGerd Hoffmann static MTPData *usb_mtp_get_object(MTPState *s, MTPControl *c, 1000840a178cSGerd Hoffmann MTPObject *o) 1001840a178cSGerd Hoffmann { 1002840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 1003840a178cSGerd Hoffmann 1004840a178cSGerd Hoffmann trace_usb_mtp_op_get_object(s->dev.addr, o->handle, o->path); 1005840a178cSGerd Hoffmann 1006840a178cSGerd Hoffmann d->fd = open(o->path, O_RDONLY); 1007840a178cSGerd Hoffmann if (d->fd == -1) { 10082dc7fdf3SGerd Hoffmann usb_mtp_data_free(d); 1009840a178cSGerd Hoffmann return NULL; 1010840a178cSGerd Hoffmann } 1011840a178cSGerd Hoffmann d->length = o->stat.st_size; 1012840a178cSGerd Hoffmann d->alloc = 512; 1013840a178cSGerd Hoffmann d->data = g_malloc(d->alloc); 1014840a178cSGerd Hoffmann return d; 1015840a178cSGerd Hoffmann } 1016840a178cSGerd Hoffmann 1017840a178cSGerd Hoffmann static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c, 1018840a178cSGerd Hoffmann MTPObject *o) 1019840a178cSGerd Hoffmann { 1020840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 1021840a178cSGerd Hoffmann off_t offset; 1022840a178cSGerd Hoffmann 1023840a178cSGerd Hoffmann trace_usb_mtp_op_get_partial_object(s->dev.addr, o->handle, o->path, 1024840a178cSGerd Hoffmann c->argv[1], c->argv[2]); 1025840a178cSGerd Hoffmann 1026840a178cSGerd Hoffmann d->fd = open(o->path, O_RDONLY); 1027840a178cSGerd Hoffmann if (d->fd == -1) { 10282dc7fdf3SGerd Hoffmann usb_mtp_data_free(d); 1029840a178cSGerd Hoffmann return NULL; 1030840a178cSGerd Hoffmann } 1031840a178cSGerd Hoffmann 1032840a178cSGerd Hoffmann offset = c->argv[1]; 1033840a178cSGerd Hoffmann if (offset > o->stat.st_size) { 1034840a178cSGerd Hoffmann offset = o->stat.st_size; 1035840a178cSGerd Hoffmann } 103668206d73SGerd Hoffmann if (lseek(d->fd, offset, SEEK_SET) < 0) { 103768206d73SGerd Hoffmann usb_mtp_data_free(d); 103868206d73SGerd Hoffmann return NULL; 103968206d73SGerd Hoffmann } 1040840a178cSGerd Hoffmann 1041840a178cSGerd Hoffmann d->length = c->argv[2]; 1042840a178cSGerd Hoffmann if (d->length > o->stat.st_size - offset) { 1043840a178cSGerd Hoffmann d->length = o->stat.st_size - offset; 1044840a178cSGerd Hoffmann } 1045840a178cSGerd Hoffmann 1046840a178cSGerd Hoffmann return d; 1047840a178cSGerd Hoffmann } 1048840a178cSGerd Hoffmann 104967f3ef0cSIsaac Lozano static MTPData *usb_mtp_get_object_props_supported(MTPState *s, MTPControl *c) 105067f3ef0cSIsaac Lozano { 105167f3ef0cSIsaac Lozano static const uint16_t props[] = { 105267f3ef0cSIsaac Lozano PROP_STORAGE_ID, 105367f3ef0cSIsaac Lozano PROP_OBJECT_FORMAT, 105467f3ef0cSIsaac Lozano PROP_OBJECT_COMPRESSED_SIZE, 105567f3ef0cSIsaac Lozano PROP_PARENT_OBJECT, 105667f3ef0cSIsaac Lozano PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER, 105767f3ef0cSIsaac Lozano PROP_NAME, 105867f3ef0cSIsaac Lozano }; 105967f3ef0cSIsaac Lozano MTPData *d = usb_mtp_data_alloc(c); 106067f3ef0cSIsaac Lozano usb_mtp_add_u16_array(d, ARRAY_SIZE(props), props); 106167f3ef0cSIsaac Lozano 106267f3ef0cSIsaac Lozano return d; 106367f3ef0cSIsaac Lozano } 106467f3ef0cSIsaac Lozano 106567f3ef0cSIsaac Lozano static MTPData *usb_mtp_get_object_prop_desc(MTPState *s, MTPControl *c) 106667f3ef0cSIsaac Lozano { 106767f3ef0cSIsaac Lozano MTPData *d = usb_mtp_data_alloc(c); 106867f3ef0cSIsaac Lozano switch (c->argv[0]) { 106967f3ef0cSIsaac Lozano case PROP_STORAGE_ID: 107067f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_STORAGE_ID); 107167f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT32); 107267f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 107367f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 107467f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 107567f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 107667f3ef0cSIsaac Lozano break; 107767f3ef0cSIsaac Lozano case PROP_OBJECT_FORMAT: 107867f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_OBJECT_FORMAT); 107967f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT16); 108067f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 108167f3ef0cSIsaac Lozano usb_mtp_add_u16(d, 0x0000); 108267f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 108367f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 108467f3ef0cSIsaac Lozano break; 108567f3ef0cSIsaac Lozano case PROP_OBJECT_COMPRESSED_SIZE: 108667f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_OBJECT_COMPRESSED_SIZE); 108767f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT64); 108867f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 108967f3ef0cSIsaac Lozano usb_mtp_add_u64(d, 0x0000000000000000); 109067f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 109167f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 109267f3ef0cSIsaac Lozano break; 109367f3ef0cSIsaac Lozano case PROP_PARENT_OBJECT: 109467f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_PARENT_OBJECT); 109567f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT32); 109667f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 109767f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 109867f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 109967f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 110067f3ef0cSIsaac Lozano break; 110167f3ef0cSIsaac Lozano case PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER: 110267f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER); 110367f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT128); 110467f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 110567f3ef0cSIsaac Lozano usb_mtp_add_u64(d, 0x0000000000000000); 110667f3ef0cSIsaac Lozano usb_mtp_add_u64(d, 0x0000000000000000); 110767f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 110867f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 110967f3ef0cSIsaac Lozano break; 111067f3ef0cSIsaac Lozano case PROP_NAME: 111167f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_NAME); 111267f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_STRING); 111367f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 111467f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 111567f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 111667f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 111767f3ef0cSIsaac Lozano break; 111867f3ef0cSIsaac Lozano default: 111967f3ef0cSIsaac Lozano usb_mtp_data_free(d); 112067f3ef0cSIsaac Lozano return NULL; 112167f3ef0cSIsaac Lozano } 112267f3ef0cSIsaac Lozano 112367f3ef0cSIsaac Lozano return d; 112467f3ef0cSIsaac Lozano } 112567f3ef0cSIsaac Lozano 112667f3ef0cSIsaac Lozano static MTPData *usb_mtp_get_object_prop_value(MTPState *s, MTPControl *c, 112767f3ef0cSIsaac Lozano MTPObject *o) 112867f3ef0cSIsaac Lozano { 112967f3ef0cSIsaac Lozano MTPData *d = usb_mtp_data_alloc(c); 113067f3ef0cSIsaac Lozano switch (c->argv[1]) { 113167f3ef0cSIsaac Lozano case PROP_STORAGE_ID: 113267f3ef0cSIsaac Lozano usb_mtp_add_u32(d, QEMU_STORAGE_ID); 113367f3ef0cSIsaac Lozano break; 113467f3ef0cSIsaac Lozano case PROP_OBJECT_FORMAT: 113567f3ef0cSIsaac Lozano usb_mtp_add_u16(d, o->format); 113667f3ef0cSIsaac Lozano break; 113767f3ef0cSIsaac Lozano case PROP_OBJECT_COMPRESSED_SIZE: 113867f3ef0cSIsaac Lozano usb_mtp_add_u64(d, o->stat.st_size); 113967f3ef0cSIsaac Lozano break; 114067f3ef0cSIsaac Lozano case PROP_PARENT_OBJECT: 114167f3ef0cSIsaac Lozano if (o->parent == NULL) { 114267f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 114367f3ef0cSIsaac Lozano } else { 114467f3ef0cSIsaac Lozano usb_mtp_add_u32(d, o->parent->handle); 114567f3ef0cSIsaac Lozano } 114667f3ef0cSIsaac Lozano break; 114767f3ef0cSIsaac Lozano case PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER: 1148b12227afSStefan Weil /* Should be persistent between sessions, 114967f3ef0cSIsaac Lozano * but using our objedt ID is "good enough" 115067f3ef0cSIsaac Lozano * for now */ 115167f3ef0cSIsaac Lozano usb_mtp_add_u64(d, 0x0000000000000000); 115267f3ef0cSIsaac Lozano usb_mtp_add_u64(d, o->handle); 115367f3ef0cSIsaac Lozano break; 115467f3ef0cSIsaac Lozano case PROP_NAME: 115567f3ef0cSIsaac Lozano usb_mtp_add_str(d, o->name); 115667f3ef0cSIsaac Lozano break; 115767f3ef0cSIsaac Lozano default: 115867f3ef0cSIsaac Lozano usb_mtp_data_free(d); 115967f3ef0cSIsaac Lozano return NULL; 116067f3ef0cSIsaac Lozano } 116167f3ef0cSIsaac Lozano 116267f3ef0cSIsaac Lozano return d; 116367f3ef0cSIsaac Lozano } 116467f3ef0cSIsaac Lozano 1165ec6206a6SBandan Das /* Return correct return code for a delete event */ 1166ec6206a6SBandan Das enum { 1167ec6206a6SBandan Das ALL_DELETE, 1168ec6206a6SBandan Das PARTIAL_DELETE, 1169ec6206a6SBandan Das READ_ONLY, 1170ec6206a6SBandan Das }; 1171ec6206a6SBandan Das 1172ec6206a6SBandan Das /* Assumes that children, if any, have been already freed */ 1173ec6206a6SBandan Das static void usb_mtp_object_free_one(MTPState *s, MTPObject *o) 1174ec6206a6SBandan Das { 1175ec6206a6SBandan Das #ifndef CONFIG_INOTIFY1 1176ec6206a6SBandan Das assert(o->nchildren == 0); 1177ec6206a6SBandan Das QTAILQ_REMOVE(&s->objects, o, next); 1178ec6206a6SBandan Das g_free(o->name); 1179ec6206a6SBandan Das g_free(o->path); 1180ec6206a6SBandan Das g_free(o); 1181ec6206a6SBandan Das #endif 1182ec6206a6SBandan Das } 1183ec6206a6SBandan Das 1184ec6206a6SBandan Das static int usb_mtp_deletefn(MTPState *s, MTPObject *o, uint32_t trans) 1185ec6206a6SBandan Das { 1186ec6206a6SBandan Das MTPObject *iter, *iter2; 1187ec6206a6SBandan Das bool partial_delete = false; 1188ec6206a6SBandan Das bool success = false; 1189ec6206a6SBandan Das 1190ec6206a6SBandan Das /* 1191ec6206a6SBandan Das * TODO: Add support for Protection Status 1192ec6206a6SBandan Das */ 1193ec6206a6SBandan Das 1194ec6206a6SBandan Das QLIST_FOREACH(iter, &o->children, list) { 1195ec6206a6SBandan Das if (iter->format == FMT_ASSOCIATION) { 1196ec6206a6SBandan Das QLIST_FOREACH(iter2, &iter->children, list) { 1197ec6206a6SBandan Das usb_mtp_deletefn(s, iter2, trans); 1198ec6206a6SBandan Das } 1199ec6206a6SBandan Das } 1200ec6206a6SBandan Das } 1201ec6206a6SBandan Das 1202ec6206a6SBandan Das if (o->format == FMT_UNDEFINED_OBJECT) { 1203ec6206a6SBandan Das if (remove(o->path)) { 1204ec6206a6SBandan Das partial_delete = true; 1205ec6206a6SBandan Das } else { 1206ec6206a6SBandan Das usb_mtp_object_free_one(s, o); 1207ec6206a6SBandan Das success = true; 1208ec6206a6SBandan Das } 1209ec6206a6SBandan Das } 1210ec6206a6SBandan Das 1211ec6206a6SBandan Das if (o->format == FMT_ASSOCIATION) { 1212ec6206a6SBandan Das if (rmdir(o->path)) { 1213ec6206a6SBandan Das partial_delete = true; 1214ec6206a6SBandan Das } else { 1215ec6206a6SBandan Das usb_mtp_object_free_one(s, o); 1216ec6206a6SBandan Das success = true; 1217ec6206a6SBandan Das } 1218ec6206a6SBandan Das } 1219ec6206a6SBandan Das 1220ec6206a6SBandan Das if (success && partial_delete) { 1221ec6206a6SBandan Das return PARTIAL_DELETE; 1222ec6206a6SBandan Das } 1223ec6206a6SBandan Das if (!success && partial_delete) { 1224ec6206a6SBandan Das return READ_ONLY; 1225ec6206a6SBandan Das } 1226ec6206a6SBandan Das return ALL_DELETE; 1227ec6206a6SBandan Das } 1228ec6206a6SBandan Das 1229ec6206a6SBandan Das static void usb_mtp_object_delete(MTPState *s, uint32_t handle, 1230ec6206a6SBandan Das uint32_t format_code, uint32_t trans) 1231ec6206a6SBandan Das { 1232ec6206a6SBandan Das MTPObject *o; 1233ec6206a6SBandan Das int ret; 1234ec6206a6SBandan Das 1235ec6206a6SBandan Das /* Return error if store is read-only */ 1236ec6206a6SBandan Das if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { 1237ec6206a6SBandan Das usb_mtp_queue_result(s, RES_STORE_READ_ONLY, 1238ec6206a6SBandan Das trans, 0, 0, 0, 0); 1239ec6206a6SBandan Das return; 1240ec6206a6SBandan Das } 1241ec6206a6SBandan Das 1242ec6206a6SBandan Das if (format_code != 0) { 1243ec6206a6SBandan Das usb_mtp_queue_result(s, RES_SPEC_BY_FORMAT_UNSUPPORTED, 1244ec6206a6SBandan Das trans, 0, 0, 0, 0); 1245ec6206a6SBandan Das return; 1246ec6206a6SBandan Das } 1247ec6206a6SBandan Das 1248ec6206a6SBandan Das if (handle == 0xFFFFFFF) { 1249ec6206a6SBandan Das o = QTAILQ_FIRST(&s->objects); 1250ec6206a6SBandan Das } else { 1251ec6206a6SBandan Das o = usb_mtp_object_lookup(s, handle); 1252ec6206a6SBandan Das } 1253ec6206a6SBandan Das if (o == NULL) { 1254ec6206a6SBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 1255ec6206a6SBandan Das trans, 0, 0, 0, 0); 1256ec6206a6SBandan Das return; 1257ec6206a6SBandan Das } 1258ec6206a6SBandan Das 1259ec6206a6SBandan Das ret = usb_mtp_deletefn(s, o, trans); 1260ec6206a6SBandan Das if (ret == PARTIAL_DELETE) { 1261ec6206a6SBandan Das usb_mtp_queue_result(s, RES_PARTIAL_DELETE, 1262ec6206a6SBandan Das trans, 0, 0, 0, 0); 1263ec6206a6SBandan Das return; 1264ec6206a6SBandan Das } else if (ret == READ_ONLY) { 1265ec6206a6SBandan Das usb_mtp_queue_result(s, RES_STORE_READ_ONLY, trans, 1266ec6206a6SBandan Das 0, 0, 0, 0); 1267ec6206a6SBandan Das return; 1268ec6206a6SBandan Das } else { 1269ec6206a6SBandan Das usb_mtp_queue_result(s, RES_OK, trans, 1270ec6206a6SBandan Das 0, 0, 0, 0); 1271ec6206a6SBandan Das return; 1272ec6206a6SBandan Das } 1273ec6206a6SBandan Das } 1274ec6206a6SBandan Das 1275840a178cSGerd Hoffmann static void usb_mtp_command(MTPState *s, MTPControl *c) 1276840a178cSGerd Hoffmann { 1277840a178cSGerd Hoffmann MTPData *data_in = NULL; 1278*53735befSBandan Das MTPObject *o = NULL; 1279840a178cSGerd Hoffmann uint32_t nres = 0, res0 = 0; 1280840a178cSGerd Hoffmann 1281840a178cSGerd Hoffmann /* sanity checks */ 1282840a178cSGerd Hoffmann if (c->code >= CMD_CLOSE_SESSION && s->session == 0) { 1283840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_SESSION_NOT_OPEN, 12849c727584SBandan Das c->trans, 0, 0, 0, 0); 1285840a178cSGerd Hoffmann return; 1286840a178cSGerd Hoffmann } 1287840a178cSGerd Hoffmann 1288840a178cSGerd Hoffmann /* process commands */ 1289840a178cSGerd Hoffmann switch (c->code) { 1290840a178cSGerd Hoffmann case CMD_GET_DEVICE_INFO: 1291840a178cSGerd Hoffmann data_in = usb_mtp_get_device_info(s, c); 1292840a178cSGerd Hoffmann break; 1293840a178cSGerd Hoffmann case CMD_OPEN_SESSION: 1294840a178cSGerd Hoffmann if (s->session) { 1295840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_SESSION_ALREADY_OPEN, 12969c727584SBandan Das c->trans, 1, s->session, 0, 0); 1297840a178cSGerd Hoffmann return; 1298840a178cSGerd Hoffmann } 1299840a178cSGerd Hoffmann if (c->argv[0] == 0) { 1300840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_PARAMETER, 13019c727584SBandan Das c->trans, 0, 0, 0, 0); 1302840a178cSGerd Hoffmann return; 1303840a178cSGerd Hoffmann } 1304840a178cSGerd Hoffmann trace_usb_mtp_op_open_session(s->dev.addr); 1305840a178cSGerd Hoffmann s->session = c->argv[0]; 1306840a178cSGerd Hoffmann usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root); 1307983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 13088e3e3897SBandan Das if (usb_mtp_inotify_init(s)) { 13098e3e3897SBandan Das fprintf(stderr, "usb-mtp: file monitoring init failed\n"); 13108e3e3897SBandan Das } 13118e3e3897SBandan Das #endif 1312840a178cSGerd Hoffmann break; 1313840a178cSGerd Hoffmann case CMD_CLOSE_SESSION: 1314840a178cSGerd Hoffmann trace_usb_mtp_op_close_session(s->dev.addr); 1315840a178cSGerd Hoffmann s->session = 0; 1316840a178cSGerd Hoffmann s->next_handle = 0; 1317983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 13188e3e3897SBandan Das usb_mtp_inotify_cleanup(s); 13198e3e3897SBandan Das #endif 1320840a178cSGerd Hoffmann usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); 1321840a178cSGerd Hoffmann assert(QTAILQ_EMPTY(&s->objects)); 1322840a178cSGerd Hoffmann break; 1323840a178cSGerd Hoffmann case CMD_GET_STORAGE_IDS: 1324840a178cSGerd Hoffmann data_in = usb_mtp_get_storage_ids(s, c); 1325840a178cSGerd Hoffmann break; 1326840a178cSGerd Hoffmann case CMD_GET_STORAGE_INFO: 1327840a178cSGerd Hoffmann if (c->argv[0] != QEMU_STORAGE_ID && 1328840a178cSGerd Hoffmann c->argv[0] != 0xffffffff) { 1329840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_STORAGE_ID, 13309c727584SBandan Das c->trans, 0, 0, 0, 0); 1331840a178cSGerd Hoffmann return; 1332840a178cSGerd Hoffmann } 1333840a178cSGerd Hoffmann data_in = usb_mtp_get_storage_info(s, c); 1334840a178cSGerd Hoffmann break; 1335840a178cSGerd Hoffmann case CMD_GET_NUM_OBJECTS: 1336840a178cSGerd Hoffmann case CMD_GET_OBJECT_HANDLES: 1337840a178cSGerd Hoffmann if (c->argv[0] != QEMU_STORAGE_ID && 1338840a178cSGerd Hoffmann c->argv[0] != 0xffffffff) { 1339840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_STORAGE_ID, 13409c727584SBandan Das c->trans, 0, 0, 0, 0); 1341840a178cSGerd Hoffmann return; 1342840a178cSGerd Hoffmann } 1343840a178cSGerd Hoffmann if (c->argv[1] != 0x00000000) { 1344840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_SPEC_BY_FORMAT_UNSUPPORTED, 13459c727584SBandan Das c->trans, 0, 0, 0, 0); 1346840a178cSGerd Hoffmann return; 1347840a178cSGerd Hoffmann } 1348840a178cSGerd Hoffmann if (c->argv[2] == 0x00000000 || 1349840a178cSGerd Hoffmann c->argv[2] == 0xffffffff) { 1350840a178cSGerd Hoffmann o = QTAILQ_FIRST(&s->objects); 1351840a178cSGerd Hoffmann } else { 1352840a178cSGerd Hoffmann o = usb_mtp_object_lookup(s, c->argv[2]); 1353840a178cSGerd Hoffmann } 1354840a178cSGerd Hoffmann if (o == NULL) { 1355840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13569c727584SBandan Das c->trans, 0, 0, 0, 0); 1357840a178cSGerd Hoffmann return; 1358840a178cSGerd Hoffmann } 1359840a178cSGerd Hoffmann if (o->format != FMT_ASSOCIATION) { 1360840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, 13619c727584SBandan Das c->trans, 0, 0, 0, 0); 1362840a178cSGerd Hoffmann return; 1363840a178cSGerd Hoffmann } 1364840a178cSGerd Hoffmann usb_mtp_object_readdir(s, o); 1365840a178cSGerd Hoffmann if (c->code == CMD_GET_NUM_OBJECTS) { 1366840a178cSGerd Hoffmann trace_usb_mtp_op_get_num_objects(s->dev.addr, o->handle, o->path); 1367840a178cSGerd Hoffmann nres = 1; 1368840a178cSGerd Hoffmann res0 = o->nchildren; 1369840a178cSGerd Hoffmann } else { 1370840a178cSGerd Hoffmann data_in = usb_mtp_get_object_handles(s, c, o); 1371840a178cSGerd Hoffmann } 1372840a178cSGerd Hoffmann break; 1373840a178cSGerd Hoffmann case CMD_GET_OBJECT_INFO: 1374840a178cSGerd Hoffmann o = usb_mtp_object_lookup(s, c->argv[0]); 1375840a178cSGerd Hoffmann if (o == NULL) { 1376840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13779c727584SBandan Das c->trans, 0, 0, 0, 0); 1378840a178cSGerd Hoffmann return; 1379840a178cSGerd Hoffmann } 1380840a178cSGerd Hoffmann data_in = usb_mtp_get_object_info(s, c, o); 1381840a178cSGerd Hoffmann break; 1382840a178cSGerd Hoffmann case CMD_GET_OBJECT: 1383840a178cSGerd Hoffmann o = usb_mtp_object_lookup(s, c->argv[0]); 1384840a178cSGerd Hoffmann if (o == NULL) { 1385840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13869c727584SBandan Das c->trans, 0, 0, 0, 0); 1387840a178cSGerd Hoffmann return; 1388840a178cSGerd Hoffmann } 1389840a178cSGerd Hoffmann if (o->format == FMT_ASSOCIATION) { 1390840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13919c727584SBandan Das c->trans, 0, 0, 0, 0); 1392840a178cSGerd Hoffmann return; 1393840a178cSGerd Hoffmann } 1394840a178cSGerd Hoffmann data_in = usb_mtp_get_object(s, c, o); 1395d0657b2aSGonglei if (data_in == NULL) { 139622513a9bSGerd Hoffmann usb_mtp_queue_result(s, RES_GENERAL_ERROR, 13979c727584SBandan Das c->trans, 0, 0, 0, 0); 139822513a9bSGerd Hoffmann return; 1399840a178cSGerd Hoffmann } 1400840a178cSGerd Hoffmann break; 1401ec6206a6SBandan Das case CMD_DELETE_OBJECT: 1402ec6206a6SBandan Das usb_mtp_object_delete(s, c->argv[0], c->argv[1], c->trans); 1403ec6206a6SBandan Das return; 1404840a178cSGerd Hoffmann case CMD_GET_PARTIAL_OBJECT: 1405840a178cSGerd Hoffmann o = usb_mtp_object_lookup(s, c->argv[0]); 1406840a178cSGerd Hoffmann if (o == NULL) { 1407840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 14089c727584SBandan Das c->trans, 0, 0, 0, 0); 1409840a178cSGerd Hoffmann return; 1410840a178cSGerd Hoffmann } 1411840a178cSGerd Hoffmann if (o->format == FMT_ASSOCIATION) { 1412840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 14139c727584SBandan Das c->trans, 0, 0, 0, 0); 1414840a178cSGerd Hoffmann return; 1415840a178cSGerd Hoffmann } 1416840a178cSGerd Hoffmann data_in = usb_mtp_get_partial_object(s, c, o); 1417d0657b2aSGonglei if (data_in == NULL) { 141822513a9bSGerd Hoffmann usb_mtp_queue_result(s, RES_GENERAL_ERROR, 14199c727584SBandan Das c->trans, 0, 0, 0, 0); 142022513a9bSGerd Hoffmann return; 1421840a178cSGerd Hoffmann } 1422840a178cSGerd Hoffmann nres = 1; 1423840a178cSGerd Hoffmann res0 = data_in->length; 1424840a178cSGerd Hoffmann break; 1425*53735befSBandan Das case CMD_SEND_OBJECT_INFO: 1426*53735befSBandan Das /* Return error if store is read-only */ 1427*53735befSBandan Das if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { 1428*53735befSBandan Das usb_mtp_queue_result(s, RES_STORE_READ_ONLY, 1429*53735befSBandan Das c->trans, 0, 0, 0, 0); 1430*53735befSBandan Das } else if (c->argv[0] && (c->argv[0] != QEMU_STORAGE_ID)) { 1431*53735befSBandan Das /* First parameter points to storage id or is 0 */ 1432*53735befSBandan Das usb_mtp_queue_result(s, RES_STORE_NOT_AVAILABLE, c->trans, 1433*53735befSBandan Das 0, 0, 0, 0); 1434*53735befSBandan Das } else if (c->argv[1] && !c->argv[0]) { 1435*53735befSBandan Das /* If second parameter is specified, first must also be specified */ 1436*53735befSBandan Das usb_mtp_queue_result(s, RES_DESTINATION_UNSUPPORTED, c->trans, 1437*53735befSBandan Das 0, 0, 0, 0); 1438*53735befSBandan Das } else { 1439*53735befSBandan Das uint32_t handle = c->argv[1]; 1440*53735befSBandan Das if (handle == 0xFFFFFFFF || handle == 0) { 1441*53735befSBandan Das /* root object */ 1442*53735befSBandan Das o = QTAILQ_FIRST(&s->objects); 1443*53735befSBandan Das } else { 1444*53735befSBandan Das o = usb_mtp_object_lookup(s, handle); 1445*53735befSBandan Das } 1446*53735befSBandan Das if (o == NULL) { 1447*53735befSBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, c->trans, 1448*53735befSBandan Das 0, 0, 0, 0); 1449*53735befSBandan Das } 1450*53735befSBandan Das if (o->format != FMT_ASSOCIATION) { 1451*53735befSBandan Das usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, c->trans, 1452*53735befSBandan Das 0, 0, 0, 0); 1453*53735befSBandan Das } 1454*53735befSBandan Das } 1455*53735befSBandan Das if (o) { 1456*53735befSBandan Das s->dataset.parent_handle = o->handle; 1457*53735befSBandan Das } 1458*53735befSBandan Das s->data_out = usb_mtp_data_alloc(c); 1459*53735befSBandan Das return; 146088d5f381SBandan Das case CMD_SEND_OBJECT: 146188d5f381SBandan Das if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { 146288d5f381SBandan Das usb_mtp_queue_result(s, RES_STORE_READ_ONLY, 146388d5f381SBandan Das c->trans, 0, 0, 0, 0); 146488d5f381SBandan Das return; 146588d5f381SBandan Das } 146688d5f381SBandan Das if (!s->write_pending) { 146788d5f381SBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, 146888d5f381SBandan Das c->trans, 0, 0, 0, 0); 146988d5f381SBandan Das return; 147088d5f381SBandan Das } 147188d5f381SBandan Das s->data_out = usb_mtp_data_alloc(c); 147288d5f381SBandan Das return; 147367f3ef0cSIsaac Lozano case CMD_GET_OBJECT_PROPS_SUPPORTED: 147467f3ef0cSIsaac Lozano if (c->argv[0] != FMT_UNDEFINED_OBJECT && 147567f3ef0cSIsaac Lozano c->argv[0] != FMT_ASSOCIATION) { 147667f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_FORMAT_CODE, 14779c727584SBandan Das c->trans, 0, 0, 0, 0); 147867f3ef0cSIsaac Lozano return; 147967f3ef0cSIsaac Lozano } 148067f3ef0cSIsaac Lozano data_in = usb_mtp_get_object_props_supported(s, c); 148167f3ef0cSIsaac Lozano break; 148267f3ef0cSIsaac Lozano case CMD_GET_OBJECT_PROP_DESC: 148367f3ef0cSIsaac Lozano if (c->argv[1] != FMT_UNDEFINED_OBJECT && 148467f3ef0cSIsaac Lozano c->argv[1] != FMT_ASSOCIATION) { 148567f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_FORMAT_CODE, 14869c727584SBandan Das c->trans, 0, 0, 0, 0); 148767f3ef0cSIsaac Lozano return; 148867f3ef0cSIsaac Lozano } 148967f3ef0cSIsaac Lozano data_in = usb_mtp_get_object_prop_desc(s, c); 149067f3ef0cSIsaac Lozano if (data_in == NULL) { 149167f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_PROP_CODE, 14929c727584SBandan Das c->trans, 0, 0, 0, 0); 149367f3ef0cSIsaac Lozano return; 149467f3ef0cSIsaac Lozano } 149567f3ef0cSIsaac Lozano break; 149667f3ef0cSIsaac Lozano case CMD_GET_OBJECT_PROP_VALUE: 149767f3ef0cSIsaac Lozano o = usb_mtp_object_lookup(s, c->argv[0]); 149867f3ef0cSIsaac Lozano if (o == NULL) { 149967f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 15009c727584SBandan Das c->trans, 0, 0, 0, 0); 150167f3ef0cSIsaac Lozano return; 150267f3ef0cSIsaac Lozano } 150367f3ef0cSIsaac Lozano data_in = usb_mtp_get_object_prop_value(s, c, o); 150467f3ef0cSIsaac Lozano if (data_in == NULL) { 150567f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_PROP_CODE, 15069c727584SBandan Das c->trans, 0, 0, 0, 0); 150767f3ef0cSIsaac Lozano return; 150867f3ef0cSIsaac Lozano } 150967f3ef0cSIsaac Lozano break; 1510840a178cSGerd Hoffmann default: 15111c76551fSGerd Hoffmann trace_usb_mtp_op_unknown(s->dev.addr, c->code); 1512840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_OPERATION_NOT_SUPPORTED, 15139c727584SBandan Das c->trans, 0, 0, 0, 0); 1514840a178cSGerd Hoffmann return; 1515840a178cSGerd Hoffmann } 1516840a178cSGerd Hoffmann 1517840a178cSGerd Hoffmann /* return results on success */ 1518840a178cSGerd Hoffmann if (data_in) { 1519840a178cSGerd Hoffmann assert(s->data_in == NULL); 1520840a178cSGerd Hoffmann s->data_in = data_in; 1521840a178cSGerd Hoffmann } 15229c727584SBandan Das usb_mtp_queue_result(s, RES_OK, c->trans, nres, res0, 0, 0); 1523840a178cSGerd Hoffmann } 1524840a178cSGerd Hoffmann 1525840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 1526840a178cSGerd Hoffmann 1527840a178cSGerd Hoffmann static void usb_mtp_handle_reset(USBDevice *dev) 1528840a178cSGerd Hoffmann { 15297c03a899SGonglei MTPState *s = USB_MTP(dev); 1530840a178cSGerd Hoffmann 1531840a178cSGerd Hoffmann trace_usb_mtp_reset(s->dev.addr); 1532840a178cSGerd Hoffmann 1533983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 15348e3e3897SBandan Das usb_mtp_inotify_cleanup(s); 15358e3e3897SBandan Das #endif 1536b3c4d425SBandan Das usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); 1537840a178cSGerd Hoffmann s->session = 0; 1538840a178cSGerd Hoffmann usb_mtp_data_free(s->data_in); 1539840a178cSGerd Hoffmann s->data_in = NULL; 1540840a178cSGerd Hoffmann usb_mtp_data_free(s->data_out); 1541840a178cSGerd Hoffmann s->data_out = NULL; 1542840a178cSGerd Hoffmann g_free(s->result); 1543840a178cSGerd Hoffmann s->result = NULL; 1544840a178cSGerd Hoffmann } 1545840a178cSGerd Hoffmann 1546840a178cSGerd Hoffmann static void usb_mtp_handle_control(USBDevice *dev, USBPacket *p, 1547840a178cSGerd Hoffmann int request, int value, int index, 1548840a178cSGerd Hoffmann int length, uint8_t *data) 1549840a178cSGerd Hoffmann { 1550840a178cSGerd Hoffmann int ret; 1551840a178cSGerd Hoffmann 1552840a178cSGerd Hoffmann ret = usb_desc_handle_control(dev, p, request, value, index, length, data); 1553840a178cSGerd Hoffmann if (ret >= 0) { 1554840a178cSGerd Hoffmann return; 1555840a178cSGerd Hoffmann } 1556840a178cSGerd Hoffmann 1557840a178cSGerd Hoffmann trace_usb_mtp_stall(dev->addr, "unknown control request"); 1558840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1559840a178cSGerd Hoffmann } 1560840a178cSGerd Hoffmann 1561840a178cSGerd Hoffmann static void usb_mtp_cancel_packet(USBDevice *dev, USBPacket *p) 1562840a178cSGerd Hoffmann { 15631c76551fSGerd Hoffmann /* we don't use async packets, so this should never be called */ 1564840a178cSGerd Hoffmann fprintf(stderr, "%s\n", __func__); 1565840a178cSGerd Hoffmann } 1566840a178cSGerd Hoffmann 1567*53735befSBandan Das static void utf16_to_str(uint8_t len, uint16_t *arr, char *name) 1568*53735befSBandan Das { 1569*53735befSBandan Das int count; 1570*53735befSBandan Das wchar_t *wstr = g_new0(wchar_t, len); 1571*53735befSBandan Das 1572*53735befSBandan Das for (count = 0; count < len; count++) { 1573*53735befSBandan Das wstr[count] = (wchar_t)arr[count]; 1574*53735befSBandan Das } 1575*53735befSBandan Das 1576*53735befSBandan Das wcstombs(name, wstr, len); 1577*53735befSBandan Das g_free(wstr); 1578*53735befSBandan Das } 1579*53735befSBandan Das 158088d5f381SBandan Das static void usb_mtp_write_data(MTPState *s) 158188d5f381SBandan Das { 158288d5f381SBandan Das MTPData *d = s->data_out; 158388d5f381SBandan Das MTPObject *parent = 158488d5f381SBandan Das usb_mtp_object_lookup(s, s->dataset.parent_handle); 158588d5f381SBandan Das char *path = NULL; 158688d5f381SBandan Das int rc = -1; 158788d5f381SBandan Das mode_t mask = 0644; 158888d5f381SBandan Das 158988d5f381SBandan Das assert(d != NULL); 159088d5f381SBandan Das 159188d5f381SBandan Das if (parent == NULL || !s->write_pending) { 159288d5f381SBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans, 159388d5f381SBandan Das 0, 0, 0, 0); 159488d5f381SBandan Das return; 159588d5f381SBandan Das } 159688d5f381SBandan Das 159788d5f381SBandan Das if (s->dataset.filename) { 159888d5f381SBandan Das path = g_strdup_printf("%s/%s", parent->path, s->dataset.filename); 159988d5f381SBandan Das if (s->dataset.format == FMT_ASSOCIATION) { 160088d5f381SBandan Das d->fd = mkdir(path, mask); 160188d5f381SBandan Das goto free; 160288d5f381SBandan Das } 160388d5f381SBandan Das if (s->dataset.size < d->length) { 160488d5f381SBandan Das usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 160588d5f381SBandan Das 0, 0, 0, 0); 160688d5f381SBandan Das goto done; 160788d5f381SBandan Das } 160888d5f381SBandan Das d->fd = open(path, O_CREAT | O_WRONLY, mask); 160988d5f381SBandan Das if (d->fd == -1) { 161088d5f381SBandan Das usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 161188d5f381SBandan Das 0, 0, 0, 0); 161288d5f381SBandan Das goto done; 161388d5f381SBandan Das } 161488d5f381SBandan Das 161588d5f381SBandan Das /* 161688d5f381SBandan Das * Return success if initiator sent 0 sized data 161788d5f381SBandan Das */ 161888d5f381SBandan Das if (!s->dataset.size) { 161988d5f381SBandan Das goto success; 162088d5f381SBandan Das } 162188d5f381SBandan Das 162288d5f381SBandan Das rc = write(d->fd, d->data, s->dataset.size); 162388d5f381SBandan Das if (rc == -1) { 162488d5f381SBandan Das usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 162588d5f381SBandan Das 0, 0, 0, 0); 162688d5f381SBandan Das goto done; 162788d5f381SBandan Das } 162888d5f381SBandan Das if (rc != s->dataset.size) { 162988d5f381SBandan Das usb_mtp_queue_result(s, RES_INCOMPLETE_TRANSFER, d->trans, 163088d5f381SBandan Das 0, 0, 0, 0); 163188d5f381SBandan Das goto done; 163288d5f381SBandan Das } 163388d5f381SBandan Das } 163488d5f381SBandan Das 163588d5f381SBandan Das success: 163688d5f381SBandan Das usb_mtp_queue_result(s, RES_OK, d->trans, 163788d5f381SBandan Das 0, 0, 0, 0); 163888d5f381SBandan Das 163988d5f381SBandan Das done: 164088d5f381SBandan Das /* 164188d5f381SBandan Das * The write dataset is kept around and freed only 164288d5f381SBandan Das * on success or if another write request comes in 164388d5f381SBandan Das */ 164488d5f381SBandan Das if (d->fd != -1) { 164588d5f381SBandan Das close(d->fd); 164688d5f381SBandan Das } 164788d5f381SBandan Das free: 164888d5f381SBandan Das g_free(s->dataset.filename); 164988d5f381SBandan Das g_free(path); 165088d5f381SBandan Das s->write_pending = false; 165188d5f381SBandan Das } 165288d5f381SBandan Das 1653*53735befSBandan Das static void usb_mtp_write_metadata(MTPState *s) 1654*53735befSBandan Das { 1655*53735befSBandan Das MTPData *d = s->data_out; 1656*53735befSBandan Das ObjectInfo *dataset = (ObjectInfo *)d->data; 1657*53735befSBandan Das char *filename = g_new0(char, dataset->length); 1658*53735befSBandan Das MTPObject *o; 1659*53735befSBandan Das MTPObject *p = usb_mtp_object_lookup(s, s->dataset.parent_handle); 1660*53735befSBandan Das uint32_t next_handle = s->next_handle; 1661*53735befSBandan Das 1662*53735befSBandan Das assert(!s->write_pending); 1663*53735befSBandan Das 1664*53735befSBandan Das utf16_to_str(dataset->length, dataset->filename, filename); 1665*53735befSBandan Das 1666*53735befSBandan Das o = usb_mtp_object_lookup_name(p, filename, dataset->length); 1667*53735befSBandan Das if (o != NULL) { 1668*53735befSBandan Das next_handle = o->handle; 1669*53735befSBandan Das } 1670*53735befSBandan Das 1671*53735befSBandan Das s->dataset.filename = filename; 1672*53735befSBandan Das s->dataset.format = dataset->format; 1673*53735befSBandan Das s->dataset.size = dataset->size; 1674*53735befSBandan Das s->dataset.filename = filename; 1675*53735befSBandan Das s->write_pending = true; 1676*53735befSBandan Das 1677*53735befSBandan Das if (s->dataset.format == FMT_ASSOCIATION) { 1678*53735befSBandan Das usb_mtp_write_data(s); 1679*53735befSBandan Das /* next_handle will be allocated to the newly created dir */ 1680*53735befSBandan Das if (d->fd == -1) { 1681*53735befSBandan Das usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 1682*53735befSBandan Das 0, 0, 0, 0); 1683*53735befSBandan Das return; 1684*53735befSBandan Das } 1685*53735befSBandan Das d->fd = -1; 1686*53735befSBandan Das } 1687*53735befSBandan Das 1688*53735befSBandan Das usb_mtp_queue_result(s, RES_OK, d->trans, 3, QEMU_STORAGE_ID, 1689*53735befSBandan Das s->dataset.parent_handle, next_handle); 1690*53735befSBandan Das } 1691*53735befSBandan Das 169288d5f381SBandan Das static void usb_mtp_get_data(MTPState *s, mtp_container *container, 169388d5f381SBandan Das USBPacket *p) 169488d5f381SBandan Das { 169588d5f381SBandan Das MTPData *d = s->data_out; 169688d5f381SBandan Das uint64_t dlen; 169788d5f381SBandan Das uint32_t data_len = p->iov.size; 169888d5f381SBandan Das 169988d5f381SBandan Das if (d->first) { 170088d5f381SBandan Das /* Total length of incoming data */ 170188d5f381SBandan Das d->length = cpu_to_le32(container->length) - sizeof(mtp_container); 170288d5f381SBandan Das /* Length of data in this packet */ 170388d5f381SBandan Das data_len -= sizeof(mtp_container); 170488d5f381SBandan Das usb_mtp_realloc(d, d->length); 170588d5f381SBandan Das d->offset = 0; 170688d5f381SBandan Das d->first = false; 170788d5f381SBandan Das } 170888d5f381SBandan Das 170988d5f381SBandan Das if (d->length - d->offset > data_len) { 171088d5f381SBandan Das dlen = data_len; 171188d5f381SBandan Das } else { 171288d5f381SBandan Das dlen = d->length - d->offset; 171388d5f381SBandan Das } 171488d5f381SBandan Das 171588d5f381SBandan Das switch (d->code) { 1716*53735befSBandan Das case CMD_SEND_OBJECT_INFO: 1717*53735befSBandan Das usb_packet_copy(p, d->data + d->offset, dlen); 1718*53735befSBandan Das d->offset += dlen; 1719*53735befSBandan Das if (d->offset == d->length) { 1720*53735befSBandan Das /* The operation might have already failed */ 1721*53735befSBandan Das if (!s->result) { 1722*53735befSBandan Das usb_mtp_write_metadata(s); 1723*53735befSBandan Das } 1724*53735befSBandan Das usb_mtp_data_free(s->data_out); 1725*53735befSBandan Das s->data_out = NULL; 1726*53735befSBandan Das return; 1727*53735befSBandan Das } 1728*53735befSBandan Das break; 172988d5f381SBandan Das case CMD_SEND_OBJECT: 173088d5f381SBandan Das usb_packet_copy(p, d->data + d->offset, dlen); 173188d5f381SBandan Das d->offset += dlen; 173288d5f381SBandan Das if (d->offset == d->length) { 173388d5f381SBandan Das usb_mtp_write_data(s); 173488d5f381SBandan Das usb_mtp_data_free(s->data_out); 173588d5f381SBandan Das s->data_out = NULL; 173688d5f381SBandan Das return; 173788d5f381SBandan Das } 173888d5f381SBandan Das break; 173988d5f381SBandan Das default: 174088d5f381SBandan Das p->status = USB_RET_STALL; 174188d5f381SBandan Das return; 174288d5f381SBandan Das } 174388d5f381SBandan Das } 174488d5f381SBandan Das 1745840a178cSGerd Hoffmann static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) 1746840a178cSGerd Hoffmann { 17477c03a899SGonglei MTPState *s = USB_MTP(dev); 1748840a178cSGerd Hoffmann MTPControl cmd; 1749840a178cSGerd Hoffmann mtp_container container; 1750840a178cSGerd Hoffmann uint32_t params[5]; 175188d5f381SBandan Das uint16_t container_type; 1752840a178cSGerd Hoffmann int i, rc; 1753840a178cSGerd Hoffmann 1754840a178cSGerd Hoffmann switch (p->ep->nr) { 1755840a178cSGerd Hoffmann case EP_DATA_IN: 1756840a178cSGerd Hoffmann if (s->data_out != NULL) { 1757840a178cSGerd Hoffmann /* guest bug */ 1758840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "awaiting data-out"); 1759840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1760840a178cSGerd Hoffmann return; 1761840a178cSGerd Hoffmann } 1762840a178cSGerd Hoffmann if (p->iov.size < sizeof(container)) { 1763840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "packet too small"); 1764840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1765840a178cSGerd Hoffmann return; 1766840a178cSGerd Hoffmann } 1767840a178cSGerd Hoffmann if (s->data_in != NULL) { 1768840a178cSGerd Hoffmann MTPData *d = s->data_in; 17698a5865f3SIsaac Lozano uint64_t dlen = d->length - d->offset; 1770840a178cSGerd Hoffmann if (d->first) { 1771840a178cSGerd Hoffmann trace_usb_mtp_data_in(s->dev.addr, d->trans, d->length); 17728a5865f3SIsaac Lozano if (d->length + sizeof(container) > 0xFFFFFFFF) { 17738a5865f3SIsaac Lozano container.length = cpu_to_le32(0xFFFFFFFF); 17748a5865f3SIsaac Lozano } else { 17758a5865f3SIsaac Lozano container.length = 17768a5865f3SIsaac Lozano cpu_to_le32(d->length + sizeof(container)); 17778a5865f3SIsaac Lozano } 1778840a178cSGerd Hoffmann container.type = cpu_to_le16(TYPE_DATA); 1779840a178cSGerd Hoffmann container.code = cpu_to_le16(d->code); 1780840a178cSGerd Hoffmann container.trans = cpu_to_le32(d->trans); 1781840a178cSGerd Hoffmann usb_packet_copy(p, &container, sizeof(container)); 1782840a178cSGerd Hoffmann d->first = false; 1783840a178cSGerd Hoffmann if (dlen > p->iov.size - sizeof(container)) { 1784840a178cSGerd Hoffmann dlen = p->iov.size - sizeof(container); 1785840a178cSGerd Hoffmann } 1786840a178cSGerd Hoffmann } else { 1787840a178cSGerd Hoffmann if (dlen > p->iov.size) { 1788840a178cSGerd Hoffmann dlen = p->iov.size; 1789840a178cSGerd Hoffmann } 1790840a178cSGerd Hoffmann } 1791840a178cSGerd Hoffmann if (d->fd == -1) { 1792840a178cSGerd Hoffmann usb_packet_copy(p, d->data + d->offset, dlen); 1793840a178cSGerd Hoffmann } else { 1794840a178cSGerd Hoffmann if (d->alloc < p->iov.size) { 1795840a178cSGerd Hoffmann d->alloc = p->iov.size; 1796840a178cSGerd Hoffmann d->data = g_realloc(d->data, d->alloc); 1797840a178cSGerd Hoffmann } 1798840a178cSGerd Hoffmann rc = read(d->fd, d->data, dlen); 1799840a178cSGerd Hoffmann if (rc != dlen) { 18008ebb8763SGerd Hoffmann memset(d->data, 0, dlen); 18018ebb8763SGerd Hoffmann s->result->code = RES_INCOMPLETE_TRANSFER; 1802840a178cSGerd Hoffmann } 1803840a178cSGerd Hoffmann usb_packet_copy(p, d->data, dlen); 1804840a178cSGerd Hoffmann } 1805840a178cSGerd Hoffmann d->offset += dlen; 1806840a178cSGerd Hoffmann if (d->offset == d->length) { 1807840a178cSGerd Hoffmann usb_mtp_data_free(s->data_in); 1808840a178cSGerd Hoffmann s->data_in = NULL; 1809840a178cSGerd Hoffmann } 1810840a178cSGerd Hoffmann } else if (s->result != NULL) { 1811840a178cSGerd Hoffmann MTPControl *r = s->result; 1812840a178cSGerd Hoffmann int length = sizeof(container) + r->argc * sizeof(uint32_t); 1813840a178cSGerd Hoffmann if (r->code == RES_OK) { 1814840a178cSGerd Hoffmann trace_usb_mtp_success(s->dev.addr, r->trans, 1815840a178cSGerd Hoffmann (r->argc > 0) ? r->argv[0] : 0, 1816840a178cSGerd Hoffmann (r->argc > 1) ? r->argv[1] : 0); 1817840a178cSGerd Hoffmann } else { 1818840a178cSGerd Hoffmann trace_usb_mtp_error(s->dev.addr, r->code, r->trans, 1819840a178cSGerd Hoffmann (r->argc > 0) ? r->argv[0] : 0, 1820840a178cSGerd Hoffmann (r->argc > 1) ? r->argv[1] : 0); 1821840a178cSGerd Hoffmann } 1822840a178cSGerd Hoffmann container.length = cpu_to_le32(length); 1823840a178cSGerd Hoffmann container.type = cpu_to_le16(TYPE_RESPONSE); 1824840a178cSGerd Hoffmann container.code = cpu_to_le16(r->code); 1825840a178cSGerd Hoffmann container.trans = cpu_to_le32(r->trans); 1826840a178cSGerd Hoffmann for (i = 0; i < r->argc; i++) { 1827840a178cSGerd Hoffmann params[i] = cpu_to_le32(r->argv[i]); 1828840a178cSGerd Hoffmann } 1829840a178cSGerd Hoffmann usb_packet_copy(p, &container, sizeof(container)); 1830840a178cSGerd Hoffmann usb_packet_copy(p, ¶ms, length - sizeof(container)); 1831840a178cSGerd Hoffmann g_free(s->result); 1832840a178cSGerd Hoffmann s->result = NULL; 1833840a178cSGerd Hoffmann } 1834840a178cSGerd Hoffmann break; 1835840a178cSGerd Hoffmann case EP_DATA_OUT: 1836840a178cSGerd Hoffmann if (p->iov.size < sizeof(container)) { 1837840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "packet too small"); 1838840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1839840a178cSGerd Hoffmann return; 1840840a178cSGerd Hoffmann } 184188d5f381SBandan Das if (s->data_out && !s->data_out->first) { 184288d5f381SBandan Das container_type = TYPE_DATA; 184388d5f381SBandan Das } else { 1844840a178cSGerd Hoffmann usb_packet_copy(p, &container, sizeof(container)); 184588d5f381SBandan Das container_type = le16_to_cpu(container.type); 184688d5f381SBandan Das } 184788d5f381SBandan Das switch (container_type) { 1848840a178cSGerd Hoffmann case TYPE_COMMAND: 1849840a178cSGerd Hoffmann if (s->data_in || s->data_out || s->result) { 1850840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "transaction inflight"); 1851840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1852840a178cSGerd Hoffmann return; 1853840a178cSGerd Hoffmann } 1854840a178cSGerd Hoffmann cmd.code = le16_to_cpu(container.code); 1855840a178cSGerd Hoffmann cmd.argc = (le32_to_cpu(container.length) - sizeof(container)) 1856840a178cSGerd Hoffmann / sizeof(uint32_t); 1857840a178cSGerd Hoffmann cmd.trans = le32_to_cpu(container.trans); 1858afa82dafSGerd Hoffmann if (cmd.argc > ARRAY_SIZE(cmd.argv)) { 1859afa82dafSGerd Hoffmann cmd.argc = ARRAY_SIZE(cmd.argv); 1860afa82dafSGerd Hoffmann } 1861afa82dafSGerd Hoffmann if (p->iov.size < sizeof(container) + cmd.argc * sizeof(uint32_t)) { 1862afa82dafSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "packet too small"); 1863afa82dafSGerd Hoffmann p->status = USB_RET_STALL; 1864afa82dafSGerd Hoffmann return; 1865afa82dafSGerd Hoffmann } 1866840a178cSGerd Hoffmann usb_packet_copy(p, ¶ms, cmd.argc * sizeof(uint32_t)); 1867840a178cSGerd Hoffmann for (i = 0; i < cmd.argc; i++) { 1868840a178cSGerd Hoffmann cmd.argv[i] = le32_to_cpu(params[i]); 1869840a178cSGerd Hoffmann } 1870840a178cSGerd Hoffmann trace_usb_mtp_command(s->dev.addr, cmd.code, cmd.trans, 1871840a178cSGerd Hoffmann (cmd.argc > 0) ? cmd.argv[0] : 0, 1872840a178cSGerd Hoffmann (cmd.argc > 1) ? cmd.argv[1] : 0, 1873840a178cSGerd Hoffmann (cmd.argc > 2) ? cmd.argv[2] : 0, 1874840a178cSGerd Hoffmann (cmd.argc > 3) ? cmd.argv[3] : 0, 1875840a178cSGerd Hoffmann (cmd.argc > 4) ? cmd.argv[4] : 0); 1876840a178cSGerd Hoffmann usb_mtp_command(s, &cmd); 1877840a178cSGerd Hoffmann break; 187888d5f381SBandan Das case TYPE_DATA: 187988d5f381SBandan Das /* One of the previous transfers has already errored but the 188088d5f381SBandan Das * responder is still sending data associated with it 188188d5f381SBandan Das */ 188288d5f381SBandan Das if (s->result != NULL) { 188388d5f381SBandan Das return; 188488d5f381SBandan Das } 188588d5f381SBandan Das usb_mtp_get_data(s, &container, p); 188688d5f381SBandan Das break; 1887840a178cSGerd Hoffmann default: 18889cd04ccfSGerd Hoffmann /* not needed as long as the mtp device is read-only */ 1889840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1890840a178cSGerd Hoffmann return; 1891840a178cSGerd Hoffmann } 1892840a178cSGerd Hoffmann break; 1893840a178cSGerd Hoffmann case EP_EVENT: 1894983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 189593d592e3SBandan Das if (!QTAILQ_EMPTY(&s->events)) { 189693d592e3SBandan Das struct MTPMonEntry *e = QTAILQ_LAST(&s->events, events); 189793d592e3SBandan Das uint32_t handle; 189893d592e3SBandan Das int len = sizeof(container) + sizeof(uint32_t); 189993d592e3SBandan Das 190093d592e3SBandan Das if (p->iov.size < len) { 190193d592e3SBandan Das trace_usb_mtp_stall(s->dev.addr, 190293d592e3SBandan Das "packet too small to send event"); 190393d592e3SBandan Das p->status = USB_RET_STALL; 190493d592e3SBandan Das return; 190593d592e3SBandan Das } 190693d592e3SBandan Das 190793d592e3SBandan Das QTAILQ_REMOVE(&s->events, e, next); 190893d592e3SBandan Das container.length = cpu_to_le32(len); 190993d592e3SBandan Das container.type = cpu_to_le32(TYPE_EVENT); 191093d592e3SBandan Das container.code = cpu_to_le16(e->event); 191193d592e3SBandan Das container.trans = 0; /* no trans specific events */ 191293d592e3SBandan Das handle = cpu_to_le32(e->handle); 191393d592e3SBandan Das usb_packet_copy(p, &container, sizeof(container)); 191493d592e3SBandan Das usb_packet_copy(p, &handle, sizeof(uint32_t)); 191593d592e3SBandan Das g_free(e); 191693d592e3SBandan Das return; 191793d592e3SBandan Das } 191893d592e3SBandan Das #endif 1919840a178cSGerd Hoffmann p->status = USB_RET_NAK; 1920840a178cSGerd Hoffmann return; 1921840a178cSGerd Hoffmann default: 1922840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "invalid endpoint"); 1923840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1924840a178cSGerd Hoffmann return; 1925840a178cSGerd Hoffmann } 1926840a178cSGerd Hoffmann 1927840a178cSGerd Hoffmann if (p->actual_length == 0) { 1928840a178cSGerd Hoffmann trace_usb_mtp_nak(s->dev.addr, p->ep->nr); 1929840a178cSGerd Hoffmann p->status = USB_RET_NAK; 1930840a178cSGerd Hoffmann return; 1931840a178cSGerd Hoffmann } else { 1932840a178cSGerd Hoffmann trace_usb_mtp_xfer(s->dev.addr, p->ep->nr, p->actual_length, 1933840a178cSGerd Hoffmann p->iov.size); 1934840a178cSGerd Hoffmann return; 1935840a178cSGerd Hoffmann } 1936840a178cSGerd Hoffmann } 1937840a178cSGerd Hoffmann 1938df9bb666SGonglei static void usb_mtp_realize(USBDevice *dev, Error **errp) 1939840a178cSGerd Hoffmann { 19407c03a899SGonglei MTPState *s = USB_MTP(dev); 1941840a178cSGerd Hoffmann 1942840a178cSGerd Hoffmann usb_desc_create_serial(dev); 1943840a178cSGerd Hoffmann usb_desc_init(dev); 1944840a178cSGerd Hoffmann QTAILQ_INIT(&s->objects); 1945840a178cSGerd Hoffmann if (s->desc == NULL) { 1946e60baebdSGonglei if (s->root == NULL) { 1947e60baebdSGonglei error_setg(errp, "usb-mtp: x-root property must be configured"); 1948e60baebdSGonglei return; 1949e60baebdSGonglei } 1950840a178cSGerd Hoffmann s->desc = strrchr(s->root, '/'); 1951ec6206a6SBandan Das /* Mark store as RW */ 1952ec6206a6SBandan Das if (!s->readonly) { 1953ec6206a6SBandan Das s->flags |= (1 << MTP_FLAG_WRITABLE); 1954ec6206a6SBandan Das } 1955457d397aSGerd Hoffmann if (s->desc && s->desc[0]) { 1956840a178cSGerd Hoffmann s->desc = g_strdup(s->desc + 1); 1957840a178cSGerd Hoffmann } else { 1958840a178cSGerd Hoffmann s->desc = g_strdup("none"); 1959840a178cSGerd Hoffmann } 1960840a178cSGerd Hoffmann } 1961840a178cSGerd Hoffmann } 1962840a178cSGerd Hoffmann 1963840a178cSGerd Hoffmann static const VMStateDescription vmstate_usb_mtp = { 1964840a178cSGerd Hoffmann .name = "usb-mtp", 1965840a178cSGerd Hoffmann .unmigratable = 1, 1966840a178cSGerd Hoffmann .version_id = 1, 1967840a178cSGerd Hoffmann .minimum_version_id = 1, 1968840a178cSGerd Hoffmann .fields = (VMStateField[]) { 1969840a178cSGerd Hoffmann VMSTATE_USB_DEVICE(dev, MTPState), 1970840a178cSGerd Hoffmann VMSTATE_END_OF_LIST() 1971840a178cSGerd Hoffmann } 1972840a178cSGerd Hoffmann }; 1973840a178cSGerd Hoffmann 1974840a178cSGerd Hoffmann static Property mtp_properties[] = { 1975cf679cafSGerd Hoffmann DEFINE_PROP_STRING("x-root", MTPState, root), 1976840a178cSGerd Hoffmann DEFINE_PROP_STRING("desc", MTPState, desc), 1977ec6206a6SBandan Das DEFINE_PROP_BOOL("readonly", MTPState, readonly, true), 1978840a178cSGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 1979840a178cSGerd Hoffmann }; 1980840a178cSGerd Hoffmann 1981840a178cSGerd Hoffmann static void usb_mtp_class_initfn(ObjectClass *klass, void *data) 1982840a178cSGerd Hoffmann { 1983840a178cSGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 1984840a178cSGerd Hoffmann USBDeviceClass *uc = USB_DEVICE_CLASS(klass); 1985840a178cSGerd Hoffmann 1986df9bb666SGonglei uc->realize = usb_mtp_realize; 1987840a178cSGerd Hoffmann uc->product_desc = "QEMU USB MTP"; 1988840a178cSGerd Hoffmann uc->usb_desc = &desc; 1989840a178cSGerd Hoffmann uc->cancel_packet = usb_mtp_cancel_packet; 1990840a178cSGerd Hoffmann uc->handle_attach = usb_desc_attach; 1991840a178cSGerd Hoffmann uc->handle_reset = usb_mtp_handle_reset; 1992840a178cSGerd Hoffmann uc->handle_control = usb_mtp_handle_control; 1993840a178cSGerd Hoffmann uc->handle_data = usb_mtp_handle_data; 1994cdab4dc0SThomas Huth set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); 1995cdab4dc0SThomas Huth dc->desc = "USB Media Transfer Protocol device"; 1996840a178cSGerd Hoffmann dc->fw_name = "mtp"; 1997840a178cSGerd Hoffmann dc->vmsd = &vmstate_usb_mtp; 1998840a178cSGerd Hoffmann dc->props = mtp_properties; 1999840a178cSGerd Hoffmann } 2000840a178cSGerd Hoffmann 2001840a178cSGerd Hoffmann static TypeInfo mtp_info = { 20027c03a899SGonglei .name = TYPE_USB_MTP, 2003840a178cSGerd Hoffmann .parent = TYPE_USB_DEVICE, 2004840a178cSGerd Hoffmann .instance_size = sizeof(MTPState), 2005840a178cSGerd Hoffmann .class_init = usb_mtp_class_initfn, 2006840a178cSGerd Hoffmann }; 2007840a178cSGerd Hoffmann 2008840a178cSGerd Hoffmann static void usb_mtp_register_types(void) 2009840a178cSGerd Hoffmann { 2010840a178cSGerd Hoffmann type_register_static(&mtp_info); 2011840a178cSGerd Hoffmann } 2012840a178cSGerd Hoffmann 2013840a178cSGerd Hoffmann type_init(usb_mtp_register_types) 2014