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" 27463581a8SMichael S. Tsirkin #include "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, 5053735befSBandan 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, 7153735befSBandan Das RES_STORE_NOT_AVAILABLE = 0x2013, 72840a178cSGerd Hoffmann RES_SPEC_BY_FORMAT_UNSUPPORTED = 0x2014, 7388d5f381SBandan Das RES_INVALID_OBJECTINFO = 0x2015, 7453735befSBandan 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 */ 8547bff13cSBandan Das EVT_CANCEL_TRANSACTION = 0x4001, 868e3e3897SBandan Das EVT_OBJ_ADDED = 0x4002, 878e3e3897SBandan Das EVT_OBJ_REMOVED = 0x4003, 888e3e3897SBandan Das EVT_OBJ_INFO_CHANGED = 0x4007, 8967f3ef0cSIsaac Lozano 9067f3ef0cSIsaac Lozano /* object properties */ 9167f3ef0cSIsaac Lozano PROP_STORAGE_ID = 0xDC01, 9267f3ef0cSIsaac Lozano PROP_OBJECT_FORMAT = 0xDC02, 9367f3ef0cSIsaac Lozano PROP_OBJECT_COMPRESSED_SIZE = 0xDC04, 9467f3ef0cSIsaac Lozano PROP_PARENT_OBJECT = 0xDC0B, 9567f3ef0cSIsaac Lozano PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER = 0xDC41, 9667f3ef0cSIsaac Lozano PROP_NAME = 0xDC44, 9767f3ef0cSIsaac Lozano }; 9867f3ef0cSIsaac Lozano 9967f3ef0cSIsaac Lozano enum mtp_data_type { 10067f3ef0cSIsaac Lozano DATA_TYPE_UINT16 = 0x0004, 10167f3ef0cSIsaac Lozano DATA_TYPE_UINT32 = 0x0006, 10267f3ef0cSIsaac Lozano DATA_TYPE_UINT64 = 0x0008, 10367f3ef0cSIsaac Lozano DATA_TYPE_UINT128 = 0x000a, 10467f3ef0cSIsaac Lozano DATA_TYPE_STRING = 0xffff, 105840a178cSGerd Hoffmann }; 106840a178cSGerd Hoffmann 107840a178cSGerd Hoffmann typedef struct { 108840a178cSGerd Hoffmann uint32_t length; 109840a178cSGerd Hoffmann uint16_t type; 110840a178cSGerd Hoffmann uint16_t code; 111840a178cSGerd Hoffmann uint32_t trans; 112840a178cSGerd Hoffmann } QEMU_PACKED mtp_container; 113840a178cSGerd Hoffmann 114840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 115840a178cSGerd Hoffmann 116840a178cSGerd Hoffmann typedef struct MTPState MTPState; 117840a178cSGerd Hoffmann typedef struct MTPControl MTPControl; 118840a178cSGerd Hoffmann typedef struct MTPData MTPData; 119840a178cSGerd Hoffmann typedef struct MTPObject MTPObject; 120840a178cSGerd Hoffmann 121840a178cSGerd Hoffmann enum { 122840a178cSGerd Hoffmann EP_DATA_IN = 1, 123840a178cSGerd Hoffmann EP_DATA_OUT, 124840a178cSGerd Hoffmann EP_EVENT, 125840a178cSGerd Hoffmann }; 126840a178cSGerd Hoffmann 127983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 1288e3e3897SBandan Das typedef struct MTPMonEntry MTPMonEntry; 1298e3e3897SBandan Das 1308e3e3897SBandan Das struct MTPMonEntry { 1318e3e3897SBandan Das uint32_t event; 1328e3e3897SBandan Das uint32_t handle; 1338e3e3897SBandan Das 1348e3e3897SBandan Das QTAILQ_ENTRY(MTPMonEntry) next; 1358e3e3897SBandan Das }; 1368e3e3897SBandan Das #endif 1378e3e3897SBandan Das 138840a178cSGerd Hoffmann struct MTPControl { 139840a178cSGerd Hoffmann uint16_t code; 140840a178cSGerd Hoffmann uint32_t trans; 141840a178cSGerd Hoffmann int argc; 142840a178cSGerd Hoffmann uint32_t argv[5]; 143840a178cSGerd Hoffmann }; 144840a178cSGerd Hoffmann 145840a178cSGerd Hoffmann struct MTPData { 146840a178cSGerd Hoffmann uint16_t code; 147840a178cSGerd Hoffmann uint32_t trans; 1488a5865f3SIsaac Lozano uint64_t offset; 1498a5865f3SIsaac Lozano uint64_t length; 150840a178cSGerd Hoffmann uint32_t alloc; 151840a178cSGerd Hoffmann uint8_t *data; 152840a178cSGerd Hoffmann bool first; 153840a178cSGerd Hoffmann int fd; 154840a178cSGerd Hoffmann }; 155840a178cSGerd Hoffmann 156840a178cSGerd Hoffmann struct MTPObject { 157840a178cSGerd Hoffmann uint32_t handle; 158840a178cSGerd Hoffmann uint16_t format; 159840a178cSGerd Hoffmann char *name; 160840a178cSGerd Hoffmann char *path; 161840a178cSGerd Hoffmann struct stat stat; 162983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 1638e3e3897SBandan Das /* inotify watch cookie */ 1648e3e3897SBandan Das int watchfd; 1658e3e3897SBandan Das #endif 166840a178cSGerd Hoffmann MTPObject *parent; 16736084d7eSGerd Hoffmann uint32_t nchildren; 1684c7a67f5SBandan Das QLIST_HEAD(, MTPObject) children; 1694c7a67f5SBandan Das QLIST_ENTRY(MTPObject) list; 17036084d7eSGerd Hoffmann bool have_children; 171840a178cSGerd Hoffmann QTAILQ_ENTRY(MTPObject) next; 172840a178cSGerd Hoffmann }; 173840a178cSGerd Hoffmann 174840a178cSGerd Hoffmann struct MTPState { 175840a178cSGerd Hoffmann USBDevice dev; 176840a178cSGerd Hoffmann char *root; 177840a178cSGerd Hoffmann char *desc; 178840a178cSGerd Hoffmann uint32_t flags; 179840a178cSGerd Hoffmann 180840a178cSGerd Hoffmann MTPData *data_in; 181840a178cSGerd Hoffmann MTPData *data_out; 182840a178cSGerd Hoffmann MTPControl *result; 183840a178cSGerd Hoffmann uint32_t session; 184840a178cSGerd Hoffmann uint32_t next_handle; 185ec6206a6SBandan Das bool readonly; 186840a178cSGerd Hoffmann 187840a178cSGerd Hoffmann QTAILQ_HEAD(, MTPObject) objects; 188983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 1898e3e3897SBandan Das /* inotify descriptor */ 1908e3e3897SBandan Das int inotifyfd; 1918e3e3897SBandan Das QTAILQ_HEAD(events, MTPMonEntry) events; 1928e3e3897SBandan Das #endif 19388d5f381SBandan Das /* Responder is expecting a write operation */ 19488d5f381SBandan Das bool write_pending; 19588d5f381SBandan Das struct { 19688d5f381SBandan Das uint32_t parent_handle; 19788d5f381SBandan Das uint16_t format; 19888d5f381SBandan Das uint32_t size; 19988d5f381SBandan Das char *filename; 20088d5f381SBandan Das } dataset; 201840a178cSGerd Hoffmann }; 202840a178cSGerd Hoffmann 20353735befSBandan Das /* 20453735befSBandan Das * ObjectInfo dataset received from initiator 20553735befSBandan Das * Fields we don't care about are ignored 20653735befSBandan Das */ 20753735befSBandan Das typedef struct { 20853735befSBandan Das uint32_t storage_id; /*unused*/ 20953735befSBandan Das uint16_t format; 21053735befSBandan Das uint16_t protection_status; /*unused*/ 21153735befSBandan Das uint32_t size; 21253735befSBandan Das uint16_t thumb_format; /*unused*/ 21353735befSBandan Das uint32_t thumb_comp_sz; /*unused*/ 21453735befSBandan Das uint32_t thumb_pix_width; /*unused*/ 21553735befSBandan Das uint32_t thumb_pix_height; /*unused*/ 21653735befSBandan Das uint32_t image_pix_width; /*unused*/ 21753735befSBandan Das uint32_t image_pix_height; /*unused*/ 21853735befSBandan Das uint32_t image_bit_depth; /*unused*/ 21953735befSBandan Das uint32_t parent; /*unused*/ 22053735befSBandan Das uint16_t assoc_type; 22153735befSBandan Das uint32_t assoc_desc; 22253735befSBandan Das uint32_t seq_no; /*unused*/ 22353735befSBandan Das uint8_t length; /*part of filename field*/ 22453735befSBandan Das uint16_t filename[0]; 22553735befSBandan Das char date_created[0]; /*unused*/ 22653735befSBandan Das char date_modified[0]; /*unused*/ 22753735befSBandan Das char keywords[0]; /*unused*/ 22853735befSBandan Das /* string and other data follows */ 22953735befSBandan Das } QEMU_PACKED ObjectInfo; 23053735befSBandan Das 2317c03a899SGonglei #define TYPE_USB_MTP "usb-mtp" 2327c03a899SGonglei #define USB_MTP(obj) OBJECT_CHECK(MTPState, (obj), TYPE_USB_MTP) 2337c03a899SGonglei 234840a178cSGerd Hoffmann #define QEMU_STORAGE_ID 0x00010001 235840a178cSGerd Hoffmann 236840a178cSGerd Hoffmann #define MTP_FLAG_WRITABLE 0 237840a178cSGerd Hoffmann 238840a178cSGerd Hoffmann #define FLAG_SET(_mtp, _flag) ((_mtp)->flags & (1 << (_flag))) 239840a178cSGerd Hoffmann 240840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 241840a178cSGerd Hoffmann 242840a178cSGerd Hoffmann #define MTP_MANUFACTURER "QEMU" 243840a178cSGerd Hoffmann #define MTP_PRODUCT "QEMU filesharing" 244840a178cSGerd Hoffmann 245840a178cSGerd Hoffmann enum { 246840a178cSGerd Hoffmann STR_MANUFACTURER = 1, 247840a178cSGerd Hoffmann STR_PRODUCT, 248840a178cSGerd Hoffmann STR_SERIALNUMBER, 24913d54125SGerd Hoffmann STR_MTP, 250840a178cSGerd Hoffmann STR_CONFIG_FULL, 251840a178cSGerd Hoffmann STR_CONFIG_HIGH, 252840a178cSGerd Hoffmann STR_CONFIG_SUPER, 253840a178cSGerd Hoffmann }; 254840a178cSGerd Hoffmann 255840a178cSGerd Hoffmann static const USBDescStrings desc_strings = { 256840a178cSGerd Hoffmann [STR_MANUFACTURER] = MTP_MANUFACTURER, 257840a178cSGerd Hoffmann [STR_PRODUCT] = MTP_PRODUCT, 258840a178cSGerd Hoffmann [STR_SERIALNUMBER] = "34617", 25913d54125SGerd Hoffmann [STR_MTP] = "MTP", 260840a178cSGerd Hoffmann [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", 261840a178cSGerd Hoffmann [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", 262840a178cSGerd Hoffmann [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", 263840a178cSGerd Hoffmann }; 264840a178cSGerd Hoffmann 265840a178cSGerd Hoffmann static const USBDescIface desc_iface_full = { 266840a178cSGerd Hoffmann .bInterfaceNumber = 0, 267840a178cSGerd Hoffmann .bNumEndpoints = 3, 268840a178cSGerd Hoffmann .bInterfaceClass = USB_CLASS_STILL_IMAGE, 269840a178cSGerd Hoffmann .bInterfaceSubClass = 0x01, 270840a178cSGerd Hoffmann .bInterfaceProtocol = 0x01, 27113d54125SGerd Hoffmann .iInterface = STR_MTP, 272840a178cSGerd Hoffmann .eps = (USBDescEndpoint[]) { 273840a178cSGerd Hoffmann { 274840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | EP_DATA_IN, 275840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 276840a178cSGerd Hoffmann .wMaxPacketSize = 64, 277840a178cSGerd Hoffmann },{ 278840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | EP_DATA_OUT, 279840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 280840a178cSGerd Hoffmann .wMaxPacketSize = 64, 281840a178cSGerd Hoffmann },{ 282840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | EP_EVENT, 283840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_INT, 28493d592e3SBandan Das .wMaxPacketSize = 64, 285840a178cSGerd Hoffmann .bInterval = 0x0a, 286840a178cSGerd Hoffmann }, 287840a178cSGerd Hoffmann } 288840a178cSGerd Hoffmann }; 289840a178cSGerd Hoffmann 290840a178cSGerd Hoffmann static const USBDescDevice desc_device_full = { 291840a178cSGerd Hoffmann .bcdUSB = 0x0200, 292840a178cSGerd Hoffmann .bMaxPacketSize0 = 8, 293840a178cSGerd Hoffmann .bNumConfigurations = 1, 294840a178cSGerd Hoffmann .confs = (USBDescConfig[]) { 295840a178cSGerd Hoffmann { 296840a178cSGerd Hoffmann .bNumInterfaces = 1, 297840a178cSGerd Hoffmann .bConfigurationValue = 1, 298840a178cSGerd Hoffmann .iConfiguration = STR_CONFIG_FULL, 299840a178cSGerd Hoffmann .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, 300840a178cSGerd Hoffmann .bMaxPower = 2, 301840a178cSGerd Hoffmann .nif = 1, 302840a178cSGerd Hoffmann .ifs = &desc_iface_full, 303840a178cSGerd Hoffmann }, 304840a178cSGerd Hoffmann }, 305840a178cSGerd Hoffmann }; 306840a178cSGerd Hoffmann 307840a178cSGerd Hoffmann static const USBDescIface desc_iface_high = { 308840a178cSGerd Hoffmann .bInterfaceNumber = 0, 309840a178cSGerd Hoffmann .bNumEndpoints = 3, 310840a178cSGerd Hoffmann .bInterfaceClass = USB_CLASS_STILL_IMAGE, 311840a178cSGerd Hoffmann .bInterfaceSubClass = 0x01, 312840a178cSGerd Hoffmann .bInterfaceProtocol = 0x01, 31313d54125SGerd Hoffmann .iInterface = STR_MTP, 314840a178cSGerd Hoffmann .eps = (USBDescEndpoint[]) { 315840a178cSGerd Hoffmann { 316840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | EP_DATA_IN, 317840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 318840a178cSGerd Hoffmann .wMaxPacketSize = 512, 319840a178cSGerd Hoffmann },{ 320840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_OUT | EP_DATA_OUT, 321840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_BULK, 322840a178cSGerd Hoffmann .wMaxPacketSize = 512, 323840a178cSGerd Hoffmann },{ 324840a178cSGerd Hoffmann .bEndpointAddress = USB_DIR_IN | EP_EVENT, 325840a178cSGerd Hoffmann .bmAttributes = USB_ENDPOINT_XFER_INT, 32693d592e3SBandan Das .wMaxPacketSize = 64, 327840a178cSGerd Hoffmann .bInterval = 0x0a, 328840a178cSGerd Hoffmann }, 329840a178cSGerd Hoffmann } 330840a178cSGerd Hoffmann }; 331840a178cSGerd Hoffmann 332840a178cSGerd Hoffmann static const USBDescDevice desc_device_high = { 333840a178cSGerd Hoffmann .bcdUSB = 0x0200, 334840a178cSGerd Hoffmann .bMaxPacketSize0 = 64, 335840a178cSGerd Hoffmann .bNumConfigurations = 1, 336840a178cSGerd Hoffmann .confs = (USBDescConfig[]) { 337840a178cSGerd Hoffmann { 338840a178cSGerd Hoffmann .bNumInterfaces = 1, 339840a178cSGerd Hoffmann .bConfigurationValue = 1, 340840a178cSGerd Hoffmann .iConfiguration = STR_CONFIG_HIGH, 341840a178cSGerd Hoffmann .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, 342840a178cSGerd Hoffmann .bMaxPower = 2, 343840a178cSGerd Hoffmann .nif = 1, 344840a178cSGerd Hoffmann .ifs = &desc_iface_high, 345840a178cSGerd Hoffmann }, 346840a178cSGerd Hoffmann }, 347840a178cSGerd Hoffmann }; 348840a178cSGerd Hoffmann 349840a178cSGerd Hoffmann static const USBDescMSOS desc_msos = { 350840a178cSGerd Hoffmann .CompatibleID = "MTP", 351840a178cSGerd Hoffmann .SelectiveSuspendEnabled = true, 352840a178cSGerd Hoffmann }; 353840a178cSGerd Hoffmann 354840a178cSGerd Hoffmann static const USBDesc desc = { 355840a178cSGerd Hoffmann .id = { 356840a178cSGerd Hoffmann .idVendor = 0x46f4, /* CRC16() of "QEMU" */ 357840a178cSGerd Hoffmann .idProduct = 0x0004, 358840a178cSGerd Hoffmann .bcdDevice = 0, 359840a178cSGerd Hoffmann .iManufacturer = STR_MANUFACTURER, 360840a178cSGerd Hoffmann .iProduct = STR_PRODUCT, 361840a178cSGerd Hoffmann .iSerialNumber = STR_SERIALNUMBER, 362840a178cSGerd Hoffmann }, 363840a178cSGerd Hoffmann .full = &desc_device_full, 364840a178cSGerd Hoffmann .high = &desc_device_high, 365840a178cSGerd Hoffmann .str = desc_strings, 366840a178cSGerd Hoffmann .msos = &desc_msos, 367840a178cSGerd Hoffmann }; 368840a178cSGerd Hoffmann 369840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 370840a178cSGerd Hoffmann 371840a178cSGerd Hoffmann static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle, 372840a178cSGerd Hoffmann MTPObject *parent, char *name) 373840a178cSGerd Hoffmann { 374840a178cSGerd Hoffmann MTPObject *o = g_new0(MTPObject, 1); 375840a178cSGerd Hoffmann 376840a178cSGerd Hoffmann if (name[0] == '.') { 377840a178cSGerd Hoffmann goto ignore; 378840a178cSGerd Hoffmann } 379840a178cSGerd Hoffmann 380840a178cSGerd Hoffmann o->handle = handle; 381840a178cSGerd Hoffmann o->parent = parent; 382840a178cSGerd Hoffmann o->name = g_strdup(name); 383840a178cSGerd Hoffmann if (parent == NULL) { 384840a178cSGerd Hoffmann o->path = g_strdup(name); 385840a178cSGerd Hoffmann } else { 386840a178cSGerd Hoffmann o->path = g_strdup_printf("%s/%s", parent->path, name); 387840a178cSGerd Hoffmann } 388840a178cSGerd Hoffmann 389840a178cSGerd Hoffmann if (lstat(o->path, &o->stat) != 0) { 390840a178cSGerd Hoffmann goto ignore; 391840a178cSGerd Hoffmann } 392840a178cSGerd Hoffmann if (S_ISREG(o->stat.st_mode)) { 393840a178cSGerd Hoffmann o->format = FMT_UNDEFINED_OBJECT; 394840a178cSGerd Hoffmann } else if (S_ISDIR(o->stat.st_mode)) { 395840a178cSGerd Hoffmann o->format = FMT_ASSOCIATION; 396840a178cSGerd Hoffmann } else { 397840a178cSGerd Hoffmann goto ignore; 398840a178cSGerd Hoffmann } 399840a178cSGerd Hoffmann 400840a178cSGerd Hoffmann if (access(o->path, R_OK) != 0) { 401840a178cSGerd Hoffmann goto ignore; 402840a178cSGerd Hoffmann } 403840a178cSGerd Hoffmann 4041c76551fSGerd Hoffmann trace_usb_mtp_object_alloc(s->dev.addr, o->handle, o->path); 405840a178cSGerd Hoffmann 406840a178cSGerd Hoffmann QTAILQ_INSERT_TAIL(&s->objects, o, next); 407840a178cSGerd Hoffmann return o; 408840a178cSGerd Hoffmann 409840a178cSGerd Hoffmann ignore: 410840a178cSGerd Hoffmann g_free(o->name); 411840a178cSGerd Hoffmann g_free(o->path); 412840a178cSGerd Hoffmann g_free(o); 413840a178cSGerd Hoffmann return NULL; 414840a178cSGerd Hoffmann } 415840a178cSGerd Hoffmann 416840a178cSGerd Hoffmann static void usb_mtp_object_free(MTPState *s, MTPObject *o) 417840a178cSGerd Hoffmann { 4184c7a67f5SBandan Das MTPObject *iter; 4194c7a67f5SBandan Das 4204c7a67f5SBandan Das if (!o) { 4214c7a67f5SBandan Das return; 4224c7a67f5SBandan Das } 423840a178cSGerd Hoffmann 4241c76551fSGerd Hoffmann trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path); 425840a178cSGerd Hoffmann 426840a178cSGerd Hoffmann QTAILQ_REMOVE(&s->objects, o, next); 4274c7a67f5SBandan Das if (o->parent) { 4284c7a67f5SBandan Das QLIST_REMOVE(o, list); 4294c7a67f5SBandan Das o->parent->nchildren--; 430840a178cSGerd Hoffmann } 4314c7a67f5SBandan Das 4324c7a67f5SBandan Das while (!QLIST_EMPTY(&o->children)) { 4334c7a67f5SBandan Das iter = QLIST_FIRST(&o->children); 4344c7a67f5SBandan Das usb_mtp_object_free(s, iter); 4354c7a67f5SBandan Das } 436840a178cSGerd Hoffmann g_free(o->name); 437840a178cSGerd Hoffmann g_free(o->path); 438840a178cSGerd Hoffmann g_free(o); 439840a178cSGerd Hoffmann } 440840a178cSGerd Hoffmann 441840a178cSGerd Hoffmann static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle) 442840a178cSGerd Hoffmann { 443840a178cSGerd Hoffmann MTPObject *o; 444840a178cSGerd Hoffmann 445840a178cSGerd Hoffmann QTAILQ_FOREACH(o, &s->objects, next) { 446840a178cSGerd Hoffmann if (o->handle == handle) { 447840a178cSGerd Hoffmann return o; 448840a178cSGerd Hoffmann } 449840a178cSGerd Hoffmann } 450840a178cSGerd Hoffmann return NULL; 451840a178cSGerd Hoffmann } 452840a178cSGerd Hoffmann 4534c7a67f5SBandan Das static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, 4544c7a67f5SBandan Das char *name) 4554c7a67f5SBandan Das { 4564c7a67f5SBandan Das MTPObject *child = 4574c7a67f5SBandan Das usb_mtp_object_alloc(s, s->next_handle++, o, name); 4584c7a67f5SBandan Das 4594c7a67f5SBandan Das if (child) { 4604c7a67f5SBandan Das trace_usb_mtp_add_child(s->dev.addr, child->handle, child->path); 4614c7a67f5SBandan Das QLIST_INSERT_HEAD(&o->children, child, list); 4624c7a67f5SBandan Das o->nchildren++; 4634c7a67f5SBandan Das 4644c7a67f5SBandan Das if (child->format == FMT_ASSOCIATION) { 4654c7a67f5SBandan Das QLIST_INIT(&child->children); 4664c7a67f5SBandan Das } 4674c7a67f5SBandan Das } 4684c7a67f5SBandan Das 4694c7a67f5SBandan Das return child; 4704c7a67f5SBandan Das } 4714c7a67f5SBandan Das 4728e3e3897SBandan Das static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, 4738e3e3897SBandan Das char *name, int len) 4748e3e3897SBandan Das { 4758e3e3897SBandan Das MTPObject *iter; 4768e3e3897SBandan Das 4778e3e3897SBandan Das QLIST_FOREACH(iter, &parent->children, list) { 4788e3e3897SBandan Das if (strncmp(iter->name, name, len) == 0) { 4798e3e3897SBandan Das return iter; 4808e3e3897SBandan Das } 4818e3e3897SBandan Das } 4828e3e3897SBandan Das 4838e3e3897SBandan Das return NULL; 4848e3e3897SBandan Das } 4858e3e3897SBandan Das 48653735befSBandan Das #ifdef CONFIG_INOTIFY1 4878e3e3897SBandan Das static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd) 4888e3e3897SBandan Das { 4898e3e3897SBandan Das MTPObject *iter; 4908e3e3897SBandan Das 4918e3e3897SBandan Das QTAILQ_FOREACH(iter, &s->objects, next) { 4928e3e3897SBandan Das if (iter->watchfd == wd) { 4938e3e3897SBandan Das return iter; 4948e3e3897SBandan Das } 4958e3e3897SBandan Das } 4968e3e3897SBandan Das 4978e3e3897SBandan Das return NULL; 4988e3e3897SBandan Das } 4998e3e3897SBandan Das 5008e3e3897SBandan Das static void inotify_watchfn(void *arg) 5018e3e3897SBandan Das { 5028e3e3897SBandan Das MTPState *s = arg; 5038e3e3897SBandan Das ssize_t bytes; 5048e3e3897SBandan Das /* From the man page: atleast one event can be read */ 5058e3e3897SBandan Das int pos; 506f34d57d3SPeter Xu char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; 5078e3e3897SBandan Das 5088e3e3897SBandan Das for (;;) { 509f34d57d3SPeter Xu bytes = read(s->inotifyfd, buf, sizeof(buf)); 5108e3e3897SBandan Das pos = 0; 5118e3e3897SBandan Das 5128e3e3897SBandan Das if (bytes <= 0) { 5138e3e3897SBandan Das /* Better luck next time */ 5148e3e3897SBandan Das return; 5158e3e3897SBandan Das } 5168e3e3897SBandan Das 5178e3e3897SBandan Das /* 5188e3e3897SBandan Das * TODO: Ignore initiator initiated events. 5198e3e3897SBandan Das * For now we are good because the store is RO 5208e3e3897SBandan Das */ 5218e3e3897SBandan Das while (bytes > 0) { 5228e3e3897SBandan Das char *p = buf + pos; 5238e3e3897SBandan Das struct inotify_event *event = (struct inotify_event *)p; 5248e3e3897SBandan Das int watchfd = 0; 5258e3e3897SBandan Das uint32_t mask = event->mask & (IN_CREATE | IN_DELETE | 5268e3e3897SBandan Das IN_MODIFY | IN_IGNORED); 5278e3e3897SBandan Das MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd); 5288e3e3897SBandan Das MTPMonEntry *entry = NULL; 5298e3e3897SBandan Das MTPObject *o; 5308e3e3897SBandan Das 5318e3e3897SBandan Das pos = pos + sizeof(struct inotify_event) + event->len; 5328e3e3897SBandan Das bytes = bytes - pos; 5338e3e3897SBandan Das 5348e3e3897SBandan Das if (!parent) { 5358e3e3897SBandan Das continue; 5368e3e3897SBandan Das } 5378e3e3897SBandan Das 5388e3e3897SBandan Das switch (mask) { 5398e3e3897SBandan Das case IN_CREATE: 5408e3e3897SBandan Das if (usb_mtp_object_lookup_name 5418e3e3897SBandan Das (parent, event->name, event->len)) { 5428e3e3897SBandan Das /* Duplicate create event */ 5438e3e3897SBandan Das continue; 5448e3e3897SBandan Das } 5458e3e3897SBandan Das entry = g_new0(MTPMonEntry, 1); 5468e3e3897SBandan Das entry->handle = s->next_handle; 5478e3e3897SBandan Das entry->event = EVT_OBJ_ADDED; 5488e3e3897SBandan Das o = usb_mtp_add_child(s, parent, event->name); 5498e3e3897SBandan Das if (!o) { 5508e3e3897SBandan Das g_free(entry); 5518e3e3897SBandan Das continue; 5528e3e3897SBandan Das } 5538e3e3897SBandan Das o->watchfd = watchfd; 5548e3e3897SBandan Das trace_usb_mtp_inotify_event(s->dev.addr, event->name, 5558e3e3897SBandan Das event->mask, "Obj Added"); 5568e3e3897SBandan Das break; 5578e3e3897SBandan Das 5588e3e3897SBandan Das case IN_DELETE: 5598e3e3897SBandan Das /* 5608e3e3897SBandan Das * The kernel issues a IN_IGNORED event 5618e3e3897SBandan Das * when a dir containing a watchpoint is 5628e3e3897SBandan Das * deleted, so we don't have to delete the 5638e3e3897SBandan Das * watchpoint 5648e3e3897SBandan Das */ 5658e3e3897SBandan Das o = usb_mtp_object_lookup_name(parent, event->name, event->len); 5668e3e3897SBandan Das if (!o) { 5678e3e3897SBandan Das continue; 5688e3e3897SBandan Das } 5698e3e3897SBandan Das entry = g_new0(MTPMonEntry, 1); 5708e3e3897SBandan Das entry->handle = o->handle; 5718e3e3897SBandan Das entry->event = EVT_OBJ_REMOVED; 5728e3e3897SBandan Das trace_usb_mtp_inotify_event(s->dev.addr, o->path, 5738e3e3897SBandan Das event->mask, "Obj Deleted"); 574ec93e158SBandan Das usb_mtp_object_free(s, o); 5758e3e3897SBandan Das break; 5768e3e3897SBandan Das 5778e3e3897SBandan Das case IN_MODIFY: 5788e3e3897SBandan Das o = usb_mtp_object_lookup_name(parent, event->name, event->len); 5798e3e3897SBandan Das if (!o) { 5808e3e3897SBandan Das continue; 5818e3e3897SBandan Das } 5828e3e3897SBandan Das entry = g_new0(MTPMonEntry, 1); 5838e3e3897SBandan Das entry->handle = o->handle; 5848e3e3897SBandan Das entry->event = EVT_OBJ_INFO_CHANGED; 5858e3e3897SBandan Das trace_usb_mtp_inotify_event(s->dev.addr, o->path, 5868e3e3897SBandan Das event->mask, "Obj Modified"); 5878e3e3897SBandan Das break; 5888e3e3897SBandan Das 5898e3e3897SBandan Das case IN_IGNORED: 5905d13ebeaSBandan Das trace_usb_mtp_inotify_event(s->dev.addr, parent->path, 5915d13ebeaSBandan Das event->mask, "Obj parent dir ignored"); 5928e3e3897SBandan Das break; 5938e3e3897SBandan Das 5948e3e3897SBandan Das default: 5958e3e3897SBandan Das fprintf(stderr, "usb-mtp: failed to parse inotify event\n"); 5968e3e3897SBandan Das continue; 5978e3e3897SBandan Das } 5988e3e3897SBandan Das 5998e3e3897SBandan Das if (entry) { 6008e3e3897SBandan Das QTAILQ_INSERT_HEAD(&s->events, entry, next); 6018e3e3897SBandan Das } 6028e3e3897SBandan Das } 6038e3e3897SBandan Das } 6048e3e3897SBandan Das } 6058e3e3897SBandan Das 6068e3e3897SBandan Das static int usb_mtp_inotify_init(MTPState *s) 6078e3e3897SBandan Das { 6088e3e3897SBandan Das int fd; 6098e3e3897SBandan Das 6108e3e3897SBandan Das fd = inotify_init1(IN_NONBLOCK); 6118e3e3897SBandan Das if (fd == -1) { 6128e3e3897SBandan Das return 1; 6138e3e3897SBandan Das } 6148e3e3897SBandan Das 6158e3e3897SBandan Das QTAILQ_INIT(&s->events); 6168e3e3897SBandan Das s->inotifyfd = fd; 6178e3e3897SBandan Das 6188e3e3897SBandan Das qemu_set_fd_handler(fd, inotify_watchfn, NULL, s); 6198e3e3897SBandan Das 6208e3e3897SBandan Das return 0; 6218e3e3897SBandan Das } 6228e3e3897SBandan Das 6238e3e3897SBandan Das static void usb_mtp_inotify_cleanup(MTPState *s) 6248e3e3897SBandan Das { 625c22d5dcdSBandan Das MTPMonEntry *e, *p; 6268e3e3897SBandan Das 6278e3e3897SBandan Das if (!s->inotifyfd) { 6288e3e3897SBandan Das return; 6298e3e3897SBandan Das } 6308e3e3897SBandan Das 6318e3e3897SBandan Das qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s); 6328e3e3897SBandan Das close(s->inotifyfd); 6338e3e3897SBandan Das 634c22d5dcdSBandan Das QTAILQ_FOREACH_SAFE(e, &s->events, next, p) { 6358e3e3897SBandan Das QTAILQ_REMOVE(&s->events, e, next); 6368e3e3897SBandan Das g_free(e); 6378e3e3897SBandan Das } 6388e3e3897SBandan Das } 6398e3e3897SBandan Das 6408e3e3897SBandan Das static int usb_mtp_add_watch(int inotifyfd, char *path) 6418e3e3897SBandan Das { 6428e3e3897SBandan Das uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY | 6438e3e3897SBandan Das IN_ISDIR; 6448e3e3897SBandan Das 6458e3e3897SBandan Das return inotify_add_watch(inotifyfd, path, mask); 6468e3e3897SBandan Das } 6478e3e3897SBandan Das #endif 6488e3e3897SBandan Das 649840a178cSGerd Hoffmann static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) 650840a178cSGerd Hoffmann { 651840a178cSGerd Hoffmann struct dirent *entry; 652840a178cSGerd Hoffmann DIR *dir; 653840a178cSGerd Hoffmann 65436084d7eSGerd Hoffmann if (o->have_children) { 65536084d7eSGerd Hoffmann return; 65636084d7eSGerd Hoffmann } 65736084d7eSGerd Hoffmann o->have_children = true; 65836084d7eSGerd Hoffmann 659840a178cSGerd Hoffmann dir = opendir(o->path); 660840a178cSGerd Hoffmann if (!dir) { 661840a178cSGerd Hoffmann return; 662840a178cSGerd Hoffmann } 663983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 6648e3e3897SBandan Das int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path); 6658e3e3897SBandan Das if (watchfd == -1) { 6668e3e3897SBandan Das fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path); 6678e3e3897SBandan Das } else { 6688e3e3897SBandan Das trace_usb_mtp_inotify_event(s->dev.addr, o->path, 6698e3e3897SBandan Das 0, "Watch Added"); 6708e3e3897SBandan Das o->watchfd = watchfd; 6718e3e3897SBandan Das } 6728e3e3897SBandan Das #endif 673840a178cSGerd Hoffmann while ((entry = readdir(dir)) != NULL) { 6744c7a67f5SBandan Das usb_mtp_add_child(s, o, entry->d_name); 675840a178cSGerd Hoffmann } 676840a178cSGerd Hoffmann closedir(dir); 677840a178cSGerd Hoffmann } 678840a178cSGerd Hoffmann 679840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 680840a178cSGerd Hoffmann 681840a178cSGerd Hoffmann static MTPData *usb_mtp_data_alloc(MTPControl *c) 682840a178cSGerd Hoffmann { 683840a178cSGerd Hoffmann MTPData *data = g_new0(MTPData, 1); 684840a178cSGerd Hoffmann 685840a178cSGerd Hoffmann data->code = c->code; 686840a178cSGerd Hoffmann data->trans = c->trans; 687840a178cSGerd Hoffmann data->fd = -1; 688840a178cSGerd Hoffmann data->first = true; 689840a178cSGerd Hoffmann return data; 690840a178cSGerd Hoffmann } 691840a178cSGerd Hoffmann 692840a178cSGerd Hoffmann static void usb_mtp_data_free(MTPData *data) 693840a178cSGerd Hoffmann { 694840a178cSGerd Hoffmann if (data == NULL) { 695840a178cSGerd Hoffmann return; 696840a178cSGerd Hoffmann } 697840a178cSGerd Hoffmann if (data->fd != -1) { 698840a178cSGerd Hoffmann close(data->fd); 699840a178cSGerd Hoffmann } 700840a178cSGerd Hoffmann g_free(data->data); 701840a178cSGerd Hoffmann g_free(data); 702840a178cSGerd Hoffmann } 703840a178cSGerd Hoffmann 704840a178cSGerd Hoffmann static void usb_mtp_realloc(MTPData *data, uint32_t bytes) 705840a178cSGerd Hoffmann { 706840a178cSGerd Hoffmann if (data->length + bytes <= data->alloc) { 707840a178cSGerd Hoffmann return; 708840a178cSGerd Hoffmann } 709840a178cSGerd Hoffmann data->alloc = (data->length + bytes + 0xff) & ~0xff; 710840a178cSGerd Hoffmann data->data = g_realloc(data->data, data->alloc); 711840a178cSGerd Hoffmann } 712840a178cSGerd Hoffmann 713840a178cSGerd Hoffmann static void usb_mtp_add_u8(MTPData *data, uint8_t val) 714840a178cSGerd Hoffmann { 715840a178cSGerd Hoffmann usb_mtp_realloc(data, 1); 716840a178cSGerd Hoffmann data->data[data->length++] = val; 717840a178cSGerd Hoffmann } 718840a178cSGerd Hoffmann 719840a178cSGerd Hoffmann static void usb_mtp_add_u16(MTPData *data, uint16_t val) 720840a178cSGerd Hoffmann { 721840a178cSGerd Hoffmann usb_mtp_realloc(data, 2); 722840a178cSGerd Hoffmann data->data[data->length++] = (val >> 0) & 0xff; 723840a178cSGerd Hoffmann data->data[data->length++] = (val >> 8) & 0xff; 724840a178cSGerd Hoffmann } 725840a178cSGerd Hoffmann 726840a178cSGerd Hoffmann static void usb_mtp_add_u32(MTPData *data, uint32_t val) 727840a178cSGerd Hoffmann { 728840a178cSGerd Hoffmann usb_mtp_realloc(data, 4); 729840a178cSGerd Hoffmann data->data[data->length++] = (val >> 0) & 0xff; 730840a178cSGerd Hoffmann data->data[data->length++] = (val >> 8) & 0xff; 731840a178cSGerd Hoffmann data->data[data->length++] = (val >> 16) & 0xff; 732840a178cSGerd Hoffmann data->data[data->length++] = (val >> 24) & 0xff; 733840a178cSGerd Hoffmann } 734840a178cSGerd Hoffmann 735840a178cSGerd Hoffmann static void usb_mtp_add_u64(MTPData *data, uint64_t val) 736840a178cSGerd Hoffmann { 737ada435f4SGerd Hoffmann usb_mtp_realloc(data, 8); 738840a178cSGerd Hoffmann data->data[data->length++] = (val >> 0) & 0xff; 739840a178cSGerd Hoffmann data->data[data->length++] = (val >> 8) & 0xff; 740840a178cSGerd Hoffmann data->data[data->length++] = (val >> 16) & 0xff; 741840a178cSGerd Hoffmann data->data[data->length++] = (val >> 24) & 0xff; 742840a178cSGerd Hoffmann data->data[data->length++] = (val >> 32) & 0xff; 743840a178cSGerd Hoffmann data->data[data->length++] = (val >> 40) & 0xff; 744840a178cSGerd Hoffmann data->data[data->length++] = (val >> 48) & 0xff; 745ada435f4SGerd Hoffmann data->data[data->length++] = (val >> 56) & 0xff; 746840a178cSGerd Hoffmann } 747840a178cSGerd Hoffmann 748840a178cSGerd Hoffmann static void usb_mtp_add_u16_array(MTPData *data, uint32_t len, 749840a178cSGerd Hoffmann const uint16_t *vals) 750840a178cSGerd Hoffmann { 751840a178cSGerd Hoffmann int i; 752840a178cSGerd Hoffmann 753840a178cSGerd Hoffmann usb_mtp_add_u32(data, len); 754840a178cSGerd Hoffmann for (i = 0; i < len; i++) { 755840a178cSGerd Hoffmann usb_mtp_add_u16(data, vals[i]); 756840a178cSGerd Hoffmann } 757840a178cSGerd Hoffmann } 758840a178cSGerd Hoffmann 759840a178cSGerd Hoffmann static void usb_mtp_add_u32_array(MTPData *data, uint32_t len, 760840a178cSGerd Hoffmann const uint32_t *vals) 761840a178cSGerd Hoffmann { 762840a178cSGerd Hoffmann int i; 763840a178cSGerd Hoffmann 764840a178cSGerd Hoffmann usb_mtp_add_u32(data, len); 765840a178cSGerd Hoffmann for (i = 0; i < len; i++) { 766840a178cSGerd Hoffmann usb_mtp_add_u32(data, vals[i]); 767840a178cSGerd Hoffmann } 768840a178cSGerd Hoffmann } 769840a178cSGerd Hoffmann 770840a178cSGerd Hoffmann static void usb_mtp_add_wstr(MTPData *data, const wchar_t *str) 771840a178cSGerd Hoffmann { 772840a178cSGerd Hoffmann uint32_t len = wcslen(str); 773840a178cSGerd Hoffmann int i; 774840a178cSGerd Hoffmann 775840a178cSGerd Hoffmann if (len > 0) { 776840a178cSGerd Hoffmann len++; /* include terminating L'\0' */ 777840a178cSGerd Hoffmann } 778840a178cSGerd Hoffmann 779840a178cSGerd Hoffmann usb_mtp_add_u8(data, len); 780840a178cSGerd Hoffmann for (i = 0; i < len; i++) { 781840a178cSGerd Hoffmann usb_mtp_add_u16(data, str[i]); 782840a178cSGerd Hoffmann } 783840a178cSGerd Hoffmann } 784840a178cSGerd Hoffmann 785840a178cSGerd Hoffmann static void usb_mtp_add_str(MTPData *data, const char *str) 786840a178cSGerd Hoffmann { 787840a178cSGerd Hoffmann uint32_t len = strlen(str)+1; 788e3d60bc7SPeter Xu wchar_t *wstr = g_new(wchar_t, len); 789840a178cSGerd Hoffmann size_t ret; 790840a178cSGerd Hoffmann 791840a178cSGerd Hoffmann ret = mbstowcs(wstr, str, len); 792840a178cSGerd Hoffmann if (ret == -1) { 793840a178cSGerd Hoffmann usb_mtp_add_wstr(data, L"Oops"); 794840a178cSGerd Hoffmann } else { 795840a178cSGerd Hoffmann usb_mtp_add_wstr(data, wstr); 796840a178cSGerd Hoffmann } 797e3d60bc7SPeter Xu 798e3d60bc7SPeter Xu g_free(wstr); 799840a178cSGerd Hoffmann } 800840a178cSGerd Hoffmann 801840a178cSGerd Hoffmann static void usb_mtp_add_time(MTPData *data, time_t time) 802840a178cSGerd Hoffmann { 803840a178cSGerd Hoffmann char buf[16]; 804840a178cSGerd Hoffmann struct tm tm; 805840a178cSGerd Hoffmann 806840a178cSGerd Hoffmann gmtime_r(&time, &tm); 807840a178cSGerd Hoffmann strftime(buf, sizeof(buf), "%Y%m%dT%H%M%S", &tm); 808840a178cSGerd Hoffmann usb_mtp_add_str(data, buf); 809840a178cSGerd Hoffmann } 810840a178cSGerd Hoffmann 811840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 812840a178cSGerd Hoffmann 813840a178cSGerd Hoffmann static void usb_mtp_queue_result(MTPState *s, uint16_t code, uint32_t trans, 8149c727584SBandan Das int argc, uint32_t arg0, uint32_t arg1, 8159c727584SBandan Das uint32_t arg2) 816840a178cSGerd Hoffmann { 817840a178cSGerd Hoffmann MTPControl *c = g_new0(MTPControl, 1); 818840a178cSGerd Hoffmann 819840a178cSGerd Hoffmann c->code = code; 820840a178cSGerd Hoffmann c->trans = trans; 821840a178cSGerd Hoffmann c->argc = argc; 822840a178cSGerd Hoffmann if (argc > 0) { 823840a178cSGerd Hoffmann c->argv[0] = arg0; 824840a178cSGerd Hoffmann } 825840a178cSGerd Hoffmann if (argc > 1) { 826840a178cSGerd Hoffmann c->argv[1] = arg1; 827840a178cSGerd Hoffmann } 8289c727584SBandan Das if (argc > 2) { 8299c727584SBandan Das c->argv[2] = arg2; 8309c727584SBandan Das } 831840a178cSGerd Hoffmann 832840a178cSGerd Hoffmann assert(s->result == NULL); 833840a178cSGerd Hoffmann s->result = c; 834840a178cSGerd Hoffmann } 835840a178cSGerd Hoffmann 836840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 837840a178cSGerd Hoffmann 838840a178cSGerd Hoffmann static MTPData *usb_mtp_get_device_info(MTPState *s, MTPControl *c) 839840a178cSGerd Hoffmann { 840840a178cSGerd Hoffmann static const uint16_t ops[] = { 841840a178cSGerd Hoffmann CMD_GET_DEVICE_INFO, 842840a178cSGerd Hoffmann CMD_OPEN_SESSION, 843840a178cSGerd Hoffmann CMD_CLOSE_SESSION, 844840a178cSGerd Hoffmann CMD_GET_STORAGE_IDS, 845840a178cSGerd Hoffmann CMD_GET_STORAGE_INFO, 846840a178cSGerd Hoffmann CMD_GET_NUM_OBJECTS, 847840a178cSGerd Hoffmann CMD_GET_OBJECT_HANDLES, 848840a178cSGerd Hoffmann CMD_GET_OBJECT_INFO, 849ec6206a6SBandan Das CMD_DELETE_OBJECT, 85053735befSBandan Das CMD_SEND_OBJECT_INFO, 85188d5f381SBandan Das CMD_SEND_OBJECT, 852840a178cSGerd Hoffmann CMD_GET_OBJECT, 853840a178cSGerd Hoffmann CMD_GET_PARTIAL_OBJECT, 85467f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROPS_SUPPORTED, 85567f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROP_DESC, 85667f3ef0cSIsaac Lozano CMD_GET_OBJECT_PROP_VALUE, 857840a178cSGerd Hoffmann }; 858840a178cSGerd Hoffmann static const uint16_t fmt[] = { 859840a178cSGerd Hoffmann FMT_UNDEFINED_OBJECT, 860840a178cSGerd Hoffmann FMT_ASSOCIATION, 861840a178cSGerd Hoffmann }; 862840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 863840a178cSGerd Hoffmann 864840a178cSGerd Hoffmann trace_usb_mtp_op_get_device_info(s->dev.addr); 865840a178cSGerd Hoffmann 866f7eaed85SGerd Hoffmann usb_mtp_add_u16(d, 100); 8671f66fe57SIsaac Lozano usb_mtp_add_u32(d, 0x00000006); 8681f66fe57SIsaac Lozano usb_mtp_add_u16(d, 0x0064); 869840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L""); 870840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0000); 871840a178cSGerd Hoffmann 872840a178cSGerd Hoffmann usb_mtp_add_u16_array(d, ARRAY_SIZE(ops), ops); 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, 0, NULL); 876840a178cSGerd Hoffmann usb_mtp_add_u16_array(d, ARRAY_SIZE(fmt), fmt); 877840a178cSGerd Hoffmann 878840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L"" MTP_MANUFACTURER); 879840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L"" MTP_PRODUCT); 880840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L"0.1"); 8819e4eff5bSGerd Hoffmann usb_mtp_add_wstr(d, L"0123456789abcdef0123456789abcdef"); 882840a178cSGerd Hoffmann 883840a178cSGerd Hoffmann return d; 884840a178cSGerd Hoffmann } 885840a178cSGerd Hoffmann 886840a178cSGerd Hoffmann static MTPData *usb_mtp_get_storage_ids(MTPState *s, MTPControl *c) 887840a178cSGerd Hoffmann { 888840a178cSGerd Hoffmann static const uint32_t ids[] = { 889840a178cSGerd Hoffmann QEMU_STORAGE_ID, 890840a178cSGerd Hoffmann }; 891840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 892840a178cSGerd Hoffmann 893840a178cSGerd Hoffmann trace_usb_mtp_op_get_storage_ids(s->dev.addr); 894840a178cSGerd Hoffmann 895840a178cSGerd Hoffmann usb_mtp_add_u32_array(d, ARRAY_SIZE(ids), ids); 896840a178cSGerd Hoffmann 897840a178cSGerd Hoffmann return d; 898840a178cSGerd Hoffmann } 899840a178cSGerd Hoffmann 900840a178cSGerd Hoffmann static MTPData *usb_mtp_get_storage_info(MTPState *s, MTPControl *c) 901840a178cSGerd Hoffmann { 902840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 903840a178cSGerd Hoffmann struct statvfs buf; 904840a178cSGerd Hoffmann int rc; 905840a178cSGerd Hoffmann 906840a178cSGerd Hoffmann trace_usb_mtp_op_get_storage_info(s->dev.addr); 907840a178cSGerd Hoffmann 908840a178cSGerd Hoffmann if (FLAG_SET(s, MTP_FLAG_WRITABLE)) { 909840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0003); 910840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0002); 911840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0000); 912840a178cSGerd Hoffmann } else { 913840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0001); 914840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0002); 915840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0001); 916840a178cSGerd Hoffmann } 917840a178cSGerd Hoffmann 918840a178cSGerd Hoffmann rc = statvfs(s->root, &buf); 919840a178cSGerd Hoffmann if (rc == 0) { 920840a178cSGerd Hoffmann usb_mtp_add_u64(d, (uint64_t)buf.f_frsize * buf.f_blocks); 921840a178cSGerd Hoffmann usb_mtp_add_u64(d, (uint64_t)buf.f_bavail * buf.f_blocks); 922840a178cSGerd Hoffmann usb_mtp_add_u32(d, buf.f_ffree); 923840a178cSGerd Hoffmann } else { 924840a178cSGerd Hoffmann usb_mtp_add_u64(d, 0xffffffff); 925840a178cSGerd Hoffmann usb_mtp_add_u64(d, 0xffffffff); 926840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0xffffffff); 927840a178cSGerd Hoffmann } 928840a178cSGerd Hoffmann 929840a178cSGerd Hoffmann usb_mtp_add_str(d, s->desc); 930840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L"123456789abcdef"); 931840a178cSGerd Hoffmann return d; 932840a178cSGerd Hoffmann } 933840a178cSGerd Hoffmann 934840a178cSGerd Hoffmann static MTPData *usb_mtp_get_object_handles(MTPState *s, MTPControl *c, 935840a178cSGerd Hoffmann MTPObject *o) 936840a178cSGerd Hoffmann { 937840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 9384c7a67f5SBandan Das uint32_t i = 0, handles[o->nchildren]; 9394c7a67f5SBandan Das MTPObject *iter; 940840a178cSGerd Hoffmann 941840a178cSGerd Hoffmann trace_usb_mtp_op_get_object_handles(s->dev.addr, o->handle, o->path); 942840a178cSGerd Hoffmann 9434c7a67f5SBandan Das QLIST_FOREACH(iter, &o->children, list) { 9444c7a67f5SBandan Das handles[i++] = iter->handle; 945840a178cSGerd Hoffmann } 9464c7a67f5SBandan Das assert(i == o->nchildren); 947840a178cSGerd Hoffmann usb_mtp_add_u32_array(d, o->nchildren, handles); 948840a178cSGerd Hoffmann 949840a178cSGerd Hoffmann return d; 950840a178cSGerd Hoffmann } 951840a178cSGerd Hoffmann 952840a178cSGerd Hoffmann static MTPData *usb_mtp_get_object_info(MTPState *s, MTPControl *c, 953840a178cSGerd Hoffmann MTPObject *o) 954840a178cSGerd Hoffmann { 955840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 956840a178cSGerd Hoffmann 957840a178cSGerd Hoffmann trace_usb_mtp_op_get_object_info(s->dev.addr, o->handle, o->path); 958840a178cSGerd Hoffmann 959840a178cSGerd Hoffmann usb_mtp_add_u32(d, QEMU_STORAGE_ID); 960840a178cSGerd Hoffmann usb_mtp_add_u16(d, o->format); 961840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0); 9628a5865f3SIsaac Lozano 9638a5865f3SIsaac Lozano if (o->stat.st_size > 0xFFFFFFFF) { 9648a5865f3SIsaac Lozano usb_mtp_add_u32(d, 0xFFFFFFFF); 9658a5865f3SIsaac Lozano } else { 966840a178cSGerd Hoffmann usb_mtp_add_u32(d, o->stat.st_size); 9678a5865f3SIsaac Lozano } 968840a178cSGerd Hoffmann 969840a178cSGerd Hoffmann usb_mtp_add_u16(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 usb_mtp_add_u32(d, 0); 976840a178cSGerd Hoffmann 977840a178cSGerd Hoffmann if (o->parent) { 978840a178cSGerd Hoffmann usb_mtp_add_u32(d, o->parent->handle); 979840a178cSGerd Hoffmann } else { 980840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 981840a178cSGerd Hoffmann } 982840a178cSGerd Hoffmann if (o->format == FMT_ASSOCIATION) { 983840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0x0001); 984840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0x00000001); 985840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 986840a178cSGerd Hoffmann } else { 987840a178cSGerd Hoffmann usb_mtp_add_u16(d, 0); 988840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 989840a178cSGerd Hoffmann usb_mtp_add_u32(d, 0); 990840a178cSGerd Hoffmann } 991840a178cSGerd Hoffmann 992840a178cSGerd Hoffmann usb_mtp_add_str(d, o->name); 993840a178cSGerd Hoffmann usb_mtp_add_time(d, o->stat.st_ctime); 994840a178cSGerd Hoffmann usb_mtp_add_time(d, o->stat.st_mtime); 995840a178cSGerd Hoffmann usb_mtp_add_wstr(d, L""); 996840a178cSGerd Hoffmann 997840a178cSGerd Hoffmann return d; 998840a178cSGerd Hoffmann } 999840a178cSGerd Hoffmann 1000840a178cSGerd Hoffmann static MTPData *usb_mtp_get_object(MTPState *s, MTPControl *c, 1001840a178cSGerd Hoffmann MTPObject *o) 1002840a178cSGerd Hoffmann { 1003840a178cSGerd Hoffmann MTPData *d = usb_mtp_data_alloc(c); 1004840a178cSGerd Hoffmann 1005840a178cSGerd Hoffmann trace_usb_mtp_op_get_object(s->dev.addr, o->handle, o->path); 1006840a178cSGerd Hoffmann 1007840a178cSGerd Hoffmann d->fd = open(o->path, O_RDONLY); 1008840a178cSGerd Hoffmann if (d->fd == -1) { 10092dc7fdf3SGerd Hoffmann usb_mtp_data_free(d); 1010840a178cSGerd Hoffmann return NULL; 1011840a178cSGerd Hoffmann } 1012840a178cSGerd Hoffmann d->length = o->stat.st_size; 1013840a178cSGerd Hoffmann d->alloc = 512; 1014840a178cSGerd Hoffmann d->data = g_malloc(d->alloc); 1015840a178cSGerd Hoffmann return d; 1016840a178cSGerd Hoffmann } 1017840a178cSGerd Hoffmann 1018840a178cSGerd Hoffmann static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c, 1019840a178cSGerd Hoffmann MTPObject *o) 1020840a178cSGerd Hoffmann { 102162713a2eSPhilippe Mathieu-Daudé MTPData *d; 1022840a178cSGerd Hoffmann off_t offset; 1023840a178cSGerd Hoffmann 102462713a2eSPhilippe Mathieu-Daudé if (c->argc <= 2) { 102562713a2eSPhilippe Mathieu-Daudé return NULL; 102662713a2eSPhilippe Mathieu-Daudé } 1027840a178cSGerd Hoffmann trace_usb_mtp_op_get_partial_object(s->dev.addr, o->handle, o->path, 1028840a178cSGerd Hoffmann c->argv[1], c->argv[2]); 1029840a178cSGerd Hoffmann 103062713a2eSPhilippe Mathieu-Daudé d = usb_mtp_data_alloc(c); 1031840a178cSGerd Hoffmann d->fd = open(o->path, O_RDONLY); 1032840a178cSGerd Hoffmann if (d->fd == -1) { 10332dc7fdf3SGerd Hoffmann usb_mtp_data_free(d); 1034840a178cSGerd Hoffmann return NULL; 1035840a178cSGerd Hoffmann } 1036840a178cSGerd Hoffmann 1037840a178cSGerd Hoffmann offset = c->argv[1]; 1038840a178cSGerd Hoffmann if (offset > o->stat.st_size) { 1039840a178cSGerd Hoffmann offset = o->stat.st_size; 1040840a178cSGerd Hoffmann } 104168206d73SGerd Hoffmann if (lseek(d->fd, offset, SEEK_SET) < 0) { 104268206d73SGerd Hoffmann usb_mtp_data_free(d); 104368206d73SGerd Hoffmann return NULL; 104468206d73SGerd Hoffmann } 1045840a178cSGerd Hoffmann 1046840a178cSGerd Hoffmann d->length = c->argv[2]; 1047840a178cSGerd Hoffmann if (d->length > o->stat.st_size - offset) { 1048840a178cSGerd Hoffmann d->length = o->stat.st_size - offset; 1049840a178cSGerd Hoffmann } 1050840a178cSGerd Hoffmann 1051840a178cSGerd Hoffmann return d; 1052840a178cSGerd Hoffmann } 1053840a178cSGerd Hoffmann 105467f3ef0cSIsaac Lozano static MTPData *usb_mtp_get_object_props_supported(MTPState *s, MTPControl *c) 105567f3ef0cSIsaac Lozano { 105667f3ef0cSIsaac Lozano static const uint16_t props[] = { 105767f3ef0cSIsaac Lozano PROP_STORAGE_ID, 105867f3ef0cSIsaac Lozano PROP_OBJECT_FORMAT, 105967f3ef0cSIsaac Lozano PROP_OBJECT_COMPRESSED_SIZE, 106067f3ef0cSIsaac Lozano PROP_PARENT_OBJECT, 106167f3ef0cSIsaac Lozano PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER, 106267f3ef0cSIsaac Lozano PROP_NAME, 106367f3ef0cSIsaac Lozano }; 106467f3ef0cSIsaac Lozano MTPData *d = usb_mtp_data_alloc(c); 106567f3ef0cSIsaac Lozano usb_mtp_add_u16_array(d, ARRAY_SIZE(props), props); 106667f3ef0cSIsaac Lozano 106767f3ef0cSIsaac Lozano return d; 106867f3ef0cSIsaac Lozano } 106967f3ef0cSIsaac Lozano 107067f3ef0cSIsaac Lozano static MTPData *usb_mtp_get_object_prop_desc(MTPState *s, MTPControl *c) 107167f3ef0cSIsaac Lozano { 107267f3ef0cSIsaac Lozano MTPData *d = usb_mtp_data_alloc(c); 107367f3ef0cSIsaac Lozano switch (c->argv[0]) { 107467f3ef0cSIsaac Lozano case PROP_STORAGE_ID: 107567f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_STORAGE_ID); 107667f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT32); 107767f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 107867f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 107967f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 108067f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 108167f3ef0cSIsaac Lozano break; 108267f3ef0cSIsaac Lozano case PROP_OBJECT_FORMAT: 108367f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_OBJECT_FORMAT); 108467f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT16); 108567f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 108667f3ef0cSIsaac Lozano usb_mtp_add_u16(d, 0x0000); 108767f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 108867f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 108967f3ef0cSIsaac Lozano break; 109067f3ef0cSIsaac Lozano case PROP_OBJECT_COMPRESSED_SIZE: 109167f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_OBJECT_COMPRESSED_SIZE); 109267f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT64); 109367f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 109467f3ef0cSIsaac Lozano usb_mtp_add_u64(d, 0x0000000000000000); 109567f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 109667f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 109767f3ef0cSIsaac Lozano break; 109867f3ef0cSIsaac Lozano case PROP_PARENT_OBJECT: 109967f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_PARENT_OBJECT); 110067f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT32); 110167f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 110267f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 110367f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 110467f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 110567f3ef0cSIsaac Lozano break; 110667f3ef0cSIsaac Lozano case PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER: 110767f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER); 110867f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_UINT128); 110967f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 111067f3ef0cSIsaac Lozano usb_mtp_add_u64(d, 0x0000000000000000); 111167f3ef0cSIsaac Lozano usb_mtp_add_u64(d, 0x0000000000000000); 111267f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 111367f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 111467f3ef0cSIsaac Lozano break; 111567f3ef0cSIsaac Lozano case PROP_NAME: 111667f3ef0cSIsaac Lozano usb_mtp_add_u16(d, PROP_NAME); 111767f3ef0cSIsaac Lozano usb_mtp_add_u16(d, DATA_TYPE_STRING); 111867f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 111967f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 112067f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 112167f3ef0cSIsaac Lozano usb_mtp_add_u8(d, 0x00); 112267f3ef0cSIsaac Lozano break; 112367f3ef0cSIsaac Lozano default: 112467f3ef0cSIsaac Lozano usb_mtp_data_free(d); 112567f3ef0cSIsaac Lozano return NULL; 112667f3ef0cSIsaac Lozano } 112767f3ef0cSIsaac Lozano 112867f3ef0cSIsaac Lozano return d; 112967f3ef0cSIsaac Lozano } 113067f3ef0cSIsaac Lozano 113167f3ef0cSIsaac Lozano static MTPData *usb_mtp_get_object_prop_value(MTPState *s, MTPControl *c, 113267f3ef0cSIsaac Lozano MTPObject *o) 113367f3ef0cSIsaac Lozano { 113467f3ef0cSIsaac Lozano MTPData *d = usb_mtp_data_alloc(c); 113567f3ef0cSIsaac Lozano switch (c->argv[1]) { 113667f3ef0cSIsaac Lozano case PROP_STORAGE_ID: 113767f3ef0cSIsaac Lozano usb_mtp_add_u32(d, QEMU_STORAGE_ID); 113867f3ef0cSIsaac Lozano break; 113967f3ef0cSIsaac Lozano case PROP_OBJECT_FORMAT: 114067f3ef0cSIsaac Lozano usb_mtp_add_u16(d, o->format); 114167f3ef0cSIsaac Lozano break; 114267f3ef0cSIsaac Lozano case PROP_OBJECT_COMPRESSED_SIZE: 114367f3ef0cSIsaac Lozano usb_mtp_add_u64(d, o->stat.st_size); 114467f3ef0cSIsaac Lozano break; 114567f3ef0cSIsaac Lozano case PROP_PARENT_OBJECT: 114667f3ef0cSIsaac Lozano if (o->parent == NULL) { 114767f3ef0cSIsaac Lozano usb_mtp_add_u32(d, 0x00000000); 114867f3ef0cSIsaac Lozano } else { 114967f3ef0cSIsaac Lozano usb_mtp_add_u32(d, o->parent->handle); 115067f3ef0cSIsaac Lozano } 115167f3ef0cSIsaac Lozano break; 115267f3ef0cSIsaac Lozano case PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER: 1153b12227afSStefan Weil /* Should be persistent between sessions, 115467f3ef0cSIsaac Lozano * but using our objedt ID is "good enough" 115567f3ef0cSIsaac Lozano * for now */ 115667f3ef0cSIsaac Lozano usb_mtp_add_u64(d, 0x0000000000000000); 115767f3ef0cSIsaac Lozano usb_mtp_add_u64(d, o->handle); 115867f3ef0cSIsaac Lozano break; 115967f3ef0cSIsaac Lozano case PROP_NAME: 116067f3ef0cSIsaac Lozano usb_mtp_add_str(d, o->name); 116167f3ef0cSIsaac Lozano break; 116267f3ef0cSIsaac Lozano default: 116367f3ef0cSIsaac Lozano usb_mtp_data_free(d); 116467f3ef0cSIsaac Lozano return NULL; 116567f3ef0cSIsaac Lozano } 116667f3ef0cSIsaac Lozano 116767f3ef0cSIsaac Lozano return d; 116867f3ef0cSIsaac Lozano } 116967f3ef0cSIsaac Lozano 1170ec6206a6SBandan Das /* Return correct return code for a delete event */ 1171ec6206a6SBandan Das enum { 1172ec6206a6SBandan Das ALL_DELETE, 1173ec6206a6SBandan Das PARTIAL_DELETE, 1174ec6206a6SBandan Das READ_ONLY, 1175ec6206a6SBandan Das }; 1176ec6206a6SBandan Das 1177ec6206a6SBandan Das /* Assumes that children, if any, have been already freed */ 1178ec6206a6SBandan Das static void usb_mtp_object_free_one(MTPState *s, MTPObject *o) 1179ec6206a6SBandan Das { 1180ec6206a6SBandan Das #ifndef CONFIG_INOTIFY1 1181ec6206a6SBandan Das assert(o->nchildren == 0); 1182ec6206a6SBandan Das QTAILQ_REMOVE(&s->objects, o, next); 1183ec6206a6SBandan Das g_free(o->name); 1184ec6206a6SBandan Das g_free(o->path); 1185ec6206a6SBandan Das g_free(o); 1186ec6206a6SBandan Das #endif 1187ec6206a6SBandan Das } 1188ec6206a6SBandan Das 1189ec6206a6SBandan Das static int usb_mtp_deletefn(MTPState *s, MTPObject *o, uint32_t trans) 1190ec6206a6SBandan Das { 1191ec6206a6SBandan Das MTPObject *iter, *iter2; 1192ec6206a6SBandan Das bool partial_delete = false; 1193ec6206a6SBandan Das bool success = false; 1194ec6206a6SBandan Das 1195ec6206a6SBandan Das /* 1196ec6206a6SBandan Das * TODO: Add support for Protection Status 1197ec6206a6SBandan Das */ 1198ec6206a6SBandan Das 1199ec6206a6SBandan Das QLIST_FOREACH(iter, &o->children, list) { 1200ec6206a6SBandan Das if (iter->format == FMT_ASSOCIATION) { 1201ec6206a6SBandan Das QLIST_FOREACH(iter2, &iter->children, list) { 1202ec6206a6SBandan Das usb_mtp_deletefn(s, iter2, trans); 1203ec6206a6SBandan Das } 1204ec6206a6SBandan Das } 1205ec6206a6SBandan Das } 1206ec6206a6SBandan Das 1207ec6206a6SBandan Das if (o->format == FMT_UNDEFINED_OBJECT) { 1208ec6206a6SBandan Das if (remove(o->path)) { 1209ec6206a6SBandan Das partial_delete = true; 1210ec6206a6SBandan Das } else { 1211ec6206a6SBandan Das usb_mtp_object_free_one(s, o); 1212ec6206a6SBandan Das success = true; 1213ec6206a6SBandan Das } 1214ec6206a6SBandan Das } 1215ec6206a6SBandan Das 1216ec6206a6SBandan Das if (o->format == FMT_ASSOCIATION) { 1217ec6206a6SBandan Das if (rmdir(o->path)) { 1218ec6206a6SBandan Das partial_delete = true; 1219ec6206a6SBandan Das } else { 1220ec6206a6SBandan Das usb_mtp_object_free_one(s, o); 1221ec6206a6SBandan Das success = true; 1222ec6206a6SBandan Das } 1223ec6206a6SBandan Das } 1224ec6206a6SBandan Das 1225ec6206a6SBandan Das if (success && partial_delete) { 1226ec6206a6SBandan Das return PARTIAL_DELETE; 1227ec6206a6SBandan Das } 1228ec6206a6SBandan Das if (!success && partial_delete) { 1229ec6206a6SBandan Das return READ_ONLY; 1230ec6206a6SBandan Das } 1231ec6206a6SBandan Das return ALL_DELETE; 1232ec6206a6SBandan Das } 1233ec6206a6SBandan Das 1234ec6206a6SBandan Das static void usb_mtp_object_delete(MTPState *s, uint32_t handle, 1235ec6206a6SBandan Das uint32_t format_code, uint32_t trans) 1236ec6206a6SBandan Das { 1237ec6206a6SBandan Das MTPObject *o; 1238ec6206a6SBandan Das int ret; 1239ec6206a6SBandan Das 1240ec6206a6SBandan Das /* Return error if store is read-only */ 1241ec6206a6SBandan Das if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { 1242ec6206a6SBandan Das usb_mtp_queue_result(s, RES_STORE_READ_ONLY, 1243ec6206a6SBandan Das trans, 0, 0, 0, 0); 1244ec6206a6SBandan Das return; 1245ec6206a6SBandan Das } 1246ec6206a6SBandan Das 1247ec6206a6SBandan Das if (format_code != 0) { 1248ec6206a6SBandan Das usb_mtp_queue_result(s, RES_SPEC_BY_FORMAT_UNSUPPORTED, 1249ec6206a6SBandan Das trans, 0, 0, 0, 0); 1250ec6206a6SBandan Das return; 1251ec6206a6SBandan Das } 1252ec6206a6SBandan Das 1253ec6206a6SBandan Das if (handle == 0xFFFFFFF) { 1254ec6206a6SBandan Das o = QTAILQ_FIRST(&s->objects); 1255ec6206a6SBandan Das } else { 1256ec6206a6SBandan Das o = usb_mtp_object_lookup(s, handle); 1257ec6206a6SBandan Das } 1258ec6206a6SBandan Das if (o == NULL) { 1259ec6206a6SBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 1260ec6206a6SBandan Das trans, 0, 0, 0, 0); 1261ec6206a6SBandan Das return; 1262ec6206a6SBandan Das } 1263ec6206a6SBandan Das 1264ec6206a6SBandan Das ret = usb_mtp_deletefn(s, o, trans); 1265ec6206a6SBandan Das if (ret == PARTIAL_DELETE) { 1266ec6206a6SBandan Das usb_mtp_queue_result(s, RES_PARTIAL_DELETE, 1267ec6206a6SBandan Das trans, 0, 0, 0, 0); 1268ec6206a6SBandan Das return; 1269ec6206a6SBandan Das } else if (ret == READ_ONLY) { 1270ec6206a6SBandan Das usb_mtp_queue_result(s, RES_STORE_READ_ONLY, trans, 1271ec6206a6SBandan Das 0, 0, 0, 0); 1272ec6206a6SBandan Das return; 1273ec6206a6SBandan Das } else { 1274ec6206a6SBandan Das usb_mtp_queue_result(s, RES_OK, trans, 1275ec6206a6SBandan Das 0, 0, 0, 0); 1276ec6206a6SBandan Das return; 1277ec6206a6SBandan Das } 1278ec6206a6SBandan Das } 1279ec6206a6SBandan Das 1280840a178cSGerd Hoffmann static void usb_mtp_command(MTPState *s, MTPControl *c) 1281840a178cSGerd Hoffmann { 1282840a178cSGerd Hoffmann MTPData *data_in = NULL; 128353735befSBandan Das MTPObject *o = NULL; 1284840a178cSGerd Hoffmann uint32_t nres = 0, res0 = 0; 1285840a178cSGerd Hoffmann 1286840a178cSGerd Hoffmann /* sanity checks */ 1287840a178cSGerd Hoffmann if (c->code >= CMD_CLOSE_SESSION && s->session == 0) { 1288840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_SESSION_NOT_OPEN, 12899c727584SBandan Das c->trans, 0, 0, 0, 0); 1290840a178cSGerd Hoffmann return; 1291840a178cSGerd Hoffmann } 1292840a178cSGerd Hoffmann 1293840a178cSGerd Hoffmann /* process commands */ 1294840a178cSGerd Hoffmann switch (c->code) { 1295840a178cSGerd Hoffmann case CMD_GET_DEVICE_INFO: 1296840a178cSGerd Hoffmann data_in = usb_mtp_get_device_info(s, c); 1297840a178cSGerd Hoffmann break; 1298840a178cSGerd Hoffmann case CMD_OPEN_SESSION: 1299840a178cSGerd Hoffmann if (s->session) { 1300840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_SESSION_ALREADY_OPEN, 13019c727584SBandan Das c->trans, 1, s->session, 0, 0); 1302840a178cSGerd Hoffmann return; 1303840a178cSGerd Hoffmann } 1304840a178cSGerd Hoffmann if (c->argv[0] == 0) { 1305840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_PARAMETER, 13069c727584SBandan Das c->trans, 0, 0, 0, 0); 1307840a178cSGerd Hoffmann return; 1308840a178cSGerd Hoffmann } 1309840a178cSGerd Hoffmann trace_usb_mtp_op_open_session(s->dev.addr); 1310840a178cSGerd Hoffmann s->session = c->argv[0]; 1311840a178cSGerd Hoffmann usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root); 1312983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 13138e3e3897SBandan Das if (usb_mtp_inotify_init(s)) { 13148e3e3897SBandan Das fprintf(stderr, "usb-mtp: file monitoring init failed\n"); 13158e3e3897SBandan Das } 13168e3e3897SBandan Das #endif 1317840a178cSGerd Hoffmann break; 1318840a178cSGerd Hoffmann case CMD_CLOSE_SESSION: 1319840a178cSGerd Hoffmann trace_usb_mtp_op_close_session(s->dev.addr); 1320840a178cSGerd Hoffmann s->session = 0; 1321840a178cSGerd Hoffmann s->next_handle = 0; 1322983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 13238e3e3897SBandan Das usb_mtp_inotify_cleanup(s); 13248e3e3897SBandan Das #endif 1325840a178cSGerd Hoffmann usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); 1326840a178cSGerd Hoffmann assert(QTAILQ_EMPTY(&s->objects)); 1327840a178cSGerd Hoffmann break; 1328840a178cSGerd Hoffmann case CMD_GET_STORAGE_IDS: 1329840a178cSGerd Hoffmann data_in = usb_mtp_get_storage_ids(s, c); 1330840a178cSGerd Hoffmann break; 1331840a178cSGerd Hoffmann case CMD_GET_STORAGE_INFO: 1332840a178cSGerd Hoffmann if (c->argv[0] != QEMU_STORAGE_ID && 1333840a178cSGerd Hoffmann c->argv[0] != 0xffffffff) { 1334840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_STORAGE_ID, 13359c727584SBandan Das c->trans, 0, 0, 0, 0); 1336840a178cSGerd Hoffmann return; 1337840a178cSGerd Hoffmann } 1338840a178cSGerd Hoffmann data_in = usb_mtp_get_storage_info(s, c); 1339840a178cSGerd Hoffmann break; 1340840a178cSGerd Hoffmann case CMD_GET_NUM_OBJECTS: 1341840a178cSGerd Hoffmann case CMD_GET_OBJECT_HANDLES: 1342840a178cSGerd Hoffmann if (c->argv[0] != QEMU_STORAGE_ID && 1343840a178cSGerd Hoffmann c->argv[0] != 0xffffffff) { 1344840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_STORAGE_ID, 13459c727584SBandan Das c->trans, 0, 0, 0, 0); 1346840a178cSGerd Hoffmann return; 1347840a178cSGerd Hoffmann } 1348840a178cSGerd Hoffmann if (c->argv[1] != 0x00000000) { 1349840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_SPEC_BY_FORMAT_UNSUPPORTED, 13509c727584SBandan Das c->trans, 0, 0, 0, 0); 1351840a178cSGerd Hoffmann return; 1352840a178cSGerd Hoffmann } 1353840a178cSGerd Hoffmann if (c->argv[2] == 0x00000000 || 1354840a178cSGerd Hoffmann c->argv[2] == 0xffffffff) { 1355840a178cSGerd Hoffmann o = QTAILQ_FIRST(&s->objects); 1356840a178cSGerd Hoffmann } else { 1357840a178cSGerd Hoffmann o = usb_mtp_object_lookup(s, c->argv[2]); 1358840a178cSGerd Hoffmann } 1359840a178cSGerd Hoffmann if (o == NULL) { 1360840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13619c727584SBandan Das c->trans, 0, 0, 0, 0); 1362840a178cSGerd Hoffmann return; 1363840a178cSGerd Hoffmann } 1364840a178cSGerd Hoffmann if (o->format != FMT_ASSOCIATION) { 1365840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, 13669c727584SBandan Das c->trans, 0, 0, 0, 0); 1367840a178cSGerd Hoffmann return; 1368840a178cSGerd Hoffmann } 1369840a178cSGerd Hoffmann usb_mtp_object_readdir(s, o); 1370840a178cSGerd Hoffmann if (c->code == CMD_GET_NUM_OBJECTS) { 1371840a178cSGerd Hoffmann trace_usb_mtp_op_get_num_objects(s->dev.addr, o->handle, o->path); 1372840a178cSGerd Hoffmann nres = 1; 1373840a178cSGerd Hoffmann res0 = o->nchildren; 1374840a178cSGerd Hoffmann } else { 1375840a178cSGerd Hoffmann data_in = usb_mtp_get_object_handles(s, c, o); 1376840a178cSGerd Hoffmann } 1377840a178cSGerd Hoffmann break; 1378840a178cSGerd Hoffmann case CMD_GET_OBJECT_INFO: 1379840a178cSGerd Hoffmann o = usb_mtp_object_lookup(s, c->argv[0]); 1380840a178cSGerd Hoffmann if (o == NULL) { 1381840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13829c727584SBandan Das c->trans, 0, 0, 0, 0); 1383840a178cSGerd Hoffmann return; 1384840a178cSGerd Hoffmann } 1385840a178cSGerd Hoffmann data_in = usb_mtp_get_object_info(s, c, o); 1386840a178cSGerd Hoffmann break; 1387840a178cSGerd Hoffmann case CMD_GET_OBJECT: 1388840a178cSGerd Hoffmann o = usb_mtp_object_lookup(s, c->argv[0]); 1389840a178cSGerd Hoffmann if (o == NULL) { 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 if (o->format == FMT_ASSOCIATION) { 1395840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 13969c727584SBandan Das c->trans, 0, 0, 0, 0); 1397840a178cSGerd Hoffmann return; 1398840a178cSGerd Hoffmann } 1399840a178cSGerd Hoffmann data_in = usb_mtp_get_object(s, c, o); 1400d0657b2aSGonglei if (data_in == NULL) { 140122513a9bSGerd Hoffmann usb_mtp_queue_result(s, RES_GENERAL_ERROR, 14029c727584SBandan Das c->trans, 0, 0, 0, 0); 140322513a9bSGerd Hoffmann return; 1404840a178cSGerd Hoffmann } 1405840a178cSGerd Hoffmann break; 1406ec6206a6SBandan Das case CMD_DELETE_OBJECT: 1407ec6206a6SBandan Das usb_mtp_object_delete(s, c->argv[0], c->argv[1], c->trans); 1408ec6206a6SBandan Das return; 1409840a178cSGerd Hoffmann case CMD_GET_PARTIAL_OBJECT: 1410840a178cSGerd Hoffmann o = usb_mtp_object_lookup(s, c->argv[0]); 1411840a178cSGerd Hoffmann if (o == NULL) { 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 if (o->format == FMT_ASSOCIATION) { 1417840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 14189c727584SBandan Das c->trans, 0, 0, 0, 0); 1419840a178cSGerd Hoffmann return; 1420840a178cSGerd Hoffmann } 1421840a178cSGerd Hoffmann data_in = usb_mtp_get_partial_object(s, c, o); 1422d0657b2aSGonglei if (data_in == NULL) { 142322513a9bSGerd Hoffmann usb_mtp_queue_result(s, RES_GENERAL_ERROR, 14249c727584SBandan Das c->trans, 0, 0, 0, 0); 142522513a9bSGerd Hoffmann return; 1426840a178cSGerd Hoffmann } 1427840a178cSGerd Hoffmann nres = 1; 1428840a178cSGerd Hoffmann res0 = data_in->length; 1429840a178cSGerd Hoffmann break; 143053735befSBandan Das case CMD_SEND_OBJECT_INFO: 143153735befSBandan Das /* Return error if store is read-only */ 143253735befSBandan Das if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { 143353735befSBandan Das usb_mtp_queue_result(s, RES_STORE_READ_ONLY, 143453735befSBandan Das c->trans, 0, 0, 0, 0); 143553735befSBandan Das } else if (c->argv[0] && (c->argv[0] != QEMU_STORAGE_ID)) { 143653735befSBandan Das /* First parameter points to storage id or is 0 */ 143753735befSBandan Das usb_mtp_queue_result(s, RES_STORE_NOT_AVAILABLE, c->trans, 143853735befSBandan Das 0, 0, 0, 0); 143953735befSBandan Das } else if (c->argv[1] && !c->argv[0]) { 144053735befSBandan Das /* If second parameter is specified, first must also be specified */ 144153735befSBandan Das usb_mtp_queue_result(s, RES_DESTINATION_UNSUPPORTED, c->trans, 144253735befSBandan Das 0, 0, 0, 0); 144353735befSBandan Das } else { 144453735befSBandan Das uint32_t handle = c->argv[1]; 144553735befSBandan Das if (handle == 0xFFFFFFFF || handle == 0) { 144653735befSBandan Das /* root object */ 144753735befSBandan Das o = QTAILQ_FIRST(&s->objects); 144853735befSBandan Das } else { 144953735befSBandan Das o = usb_mtp_object_lookup(s, handle); 145053735befSBandan Das } 145153735befSBandan Das if (o == NULL) { 145253735befSBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, c->trans, 145353735befSBandan Das 0, 0, 0, 0); 145424e8d1faSBandan Das } else if (o->format != FMT_ASSOCIATION) { 145553735befSBandan Das usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, c->trans, 145653735befSBandan Das 0, 0, 0, 0); 145753735befSBandan Das } 145853735befSBandan Das } 145953735befSBandan Das if (o) { 146053735befSBandan Das s->dataset.parent_handle = o->handle; 146153735befSBandan Das } 146253735befSBandan Das s->data_out = usb_mtp_data_alloc(c); 146353735befSBandan Das return; 146488d5f381SBandan Das case CMD_SEND_OBJECT: 146588d5f381SBandan Das if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { 146688d5f381SBandan Das usb_mtp_queue_result(s, RES_STORE_READ_ONLY, 146788d5f381SBandan Das c->trans, 0, 0, 0, 0); 146888d5f381SBandan Das return; 146988d5f381SBandan Das } 147088d5f381SBandan Das if (!s->write_pending) { 147188d5f381SBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, 147288d5f381SBandan Das c->trans, 0, 0, 0, 0); 147388d5f381SBandan Das return; 147488d5f381SBandan Das } 147588d5f381SBandan Das s->data_out = usb_mtp_data_alloc(c); 147688d5f381SBandan Das return; 147767f3ef0cSIsaac Lozano case CMD_GET_OBJECT_PROPS_SUPPORTED: 147867f3ef0cSIsaac Lozano if (c->argv[0] != FMT_UNDEFINED_OBJECT && 147967f3ef0cSIsaac Lozano c->argv[0] != FMT_ASSOCIATION) { 148067f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_FORMAT_CODE, 14819c727584SBandan Das c->trans, 0, 0, 0, 0); 148267f3ef0cSIsaac Lozano return; 148367f3ef0cSIsaac Lozano } 148467f3ef0cSIsaac Lozano data_in = usb_mtp_get_object_props_supported(s, c); 148567f3ef0cSIsaac Lozano break; 148667f3ef0cSIsaac Lozano case CMD_GET_OBJECT_PROP_DESC: 148767f3ef0cSIsaac Lozano if (c->argv[1] != FMT_UNDEFINED_OBJECT && 148867f3ef0cSIsaac Lozano c->argv[1] != FMT_ASSOCIATION) { 148967f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_FORMAT_CODE, 14909c727584SBandan Das c->trans, 0, 0, 0, 0); 149167f3ef0cSIsaac Lozano return; 149267f3ef0cSIsaac Lozano } 149367f3ef0cSIsaac Lozano data_in = usb_mtp_get_object_prop_desc(s, c); 149467f3ef0cSIsaac Lozano if (data_in == NULL) { 149567f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_PROP_CODE, 14969c727584SBandan Das c->trans, 0, 0, 0, 0); 149767f3ef0cSIsaac Lozano return; 149867f3ef0cSIsaac Lozano } 149967f3ef0cSIsaac Lozano break; 150067f3ef0cSIsaac Lozano case CMD_GET_OBJECT_PROP_VALUE: 150167f3ef0cSIsaac Lozano o = usb_mtp_object_lookup(s, c->argv[0]); 150267f3ef0cSIsaac Lozano if (o == NULL) { 150367f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, 15049c727584SBandan Das c->trans, 0, 0, 0, 0); 150567f3ef0cSIsaac Lozano return; 150667f3ef0cSIsaac Lozano } 150767f3ef0cSIsaac Lozano data_in = usb_mtp_get_object_prop_value(s, c, o); 150867f3ef0cSIsaac Lozano if (data_in == NULL) { 150967f3ef0cSIsaac Lozano usb_mtp_queue_result(s, RES_INVALID_OBJECT_PROP_CODE, 15109c727584SBandan Das c->trans, 0, 0, 0, 0); 151167f3ef0cSIsaac Lozano return; 151267f3ef0cSIsaac Lozano } 151367f3ef0cSIsaac Lozano break; 1514840a178cSGerd Hoffmann default: 15151c76551fSGerd Hoffmann trace_usb_mtp_op_unknown(s->dev.addr, c->code); 1516840a178cSGerd Hoffmann usb_mtp_queue_result(s, RES_OPERATION_NOT_SUPPORTED, 15179c727584SBandan Das c->trans, 0, 0, 0, 0); 1518840a178cSGerd Hoffmann return; 1519840a178cSGerd Hoffmann } 1520840a178cSGerd Hoffmann 1521840a178cSGerd Hoffmann /* return results on success */ 1522840a178cSGerd Hoffmann if (data_in) { 1523840a178cSGerd Hoffmann assert(s->data_in == NULL); 1524840a178cSGerd Hoffmann s->data_in = data_in; 1525840a178cSGerd Hoffmann } 15269c727584SBandan Das usb_mtp_queue_result(s, RES_OK, c->trans, nres, res0, 0, 0); 1527840a178cSGerd Hoffmann } 1528840a178cSGerd Hoffmann 1529840a178cSGerd Hoffmann /* ----------------------------------------------------------------------- */ 1530840a178cSGerd Hoffmann 1531840a178cSGerd Hoffmann static void usb_mtp_handle_reset(USBDevice *dev) 1532840a178cSGerd Hoffmann { 15337c03a899SGonglei MTPState *s = USB_MTP(dev); 1534840a178cSGerd Hoffmann 1535840a178cSGerd Hoffmann trace_usb_mtp_reset(s->dev.addr); 1536840a178cSGerd Hoffmann 1537983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 15388e3e3897SBandan Das usb_mtp_inotify_cleanup(s); 15398e3e3897SBandan Das #endif 1540b3c4d425SBandan Das usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); 1541840a178cSGerd Hoffmann s->session = 0; 1542840a178cSGerd Hoffmann usb_mtp_data_free(s->data_in); 1543840a178cSGerd Hoffmann s->data_in = NULL; 1544840a178cSGerd Hoffmann usb_mtp_data_free(s->data_out); 1545840a178cSGerd Hoffmann s->data_out = NULL; 1546840a178cSGerd Hoffmann g_free(s->result); 1547840a178cSGerd Hoffmann s->result = NULL; 1548840a178cSGerd Hoffmann } 1549840a178cSGerd Hoffmann 1550840a178cSGerd Hoffmann static void usb_mtp_handle_control(USBDevice *dev, USBPacket *p, 1551840a178cSGerd Hoffmann int request, int value, int index, 1552840a178cSGerd Hoffmann int length, uint8_t *data) 1553840a178cSGerd Hoffmann { 1554840a178cSGerd Hoffmann int ret; 155547bff13cSBandan Das MTPState *s = USB_MTP(dev); 155647bff13cSBandan Das uint16_t *event = (uint16_t *)data; 1557840a178cSGerd Hoffmann 155847bff13cSBandan Das switch (request) { 155947bff13cSBandan Das case ClassInterfaceOutRequest | 0x64: 156047bff13cSBandan Das if (*event == EVT_CANCEL_TRANSACTION) { 156147bff13cSBandan Das g_free(s->result); 156247bff13cSBandan Das s->result = NULL; 156347bff13cSBandan Das usb_mtp_data_free(s->data_in); 156447bff13cSBandan Das s->data_in = NULL; 156547bff13cSBandan Das if (s->write_pending) { 156647bff13cSBandan Das g_free(s->dataset.filename); 156747bff13cSBandan Das s->write_pending = false; 156847bff13cSBandan Das } 156947bff13cSBandan Das usb_mtp_data_free(s->data_out); 157047bff13cSBandan Das s->data_out = NULL; 157147bff13cSBandan Das } else { 157247bff13cSBandan Das p->status = USB_RET_STALL; 157347bff13cSBandan Das } 157447bff13cSBandan Das break; 157547bff13cSBandan Das default: 157647bff13cSBandan Das ret = usb_desc_handle_control(dev, p, request, 157747bff13cSBandan Das value, index, length, data); 1578840a178cSGerd Hoffmann if (ret >= 0) { 1579840a178cSGerd Hoffmann return; 1580840a178cSGerd Hoffmann } 158147bff13cSBandan Das } 1582840a178cSGerd Hoffmann 1583840a178cSGerd Hoffmann trace_usb_mtp_stall(dev->addr, "unknown control request"); 1584840a178cSGerd Hoffmann } 1585840a178cSGerd Hoffmann 1586840a178cSGerd Hoffmann static void usb_mtp_cancel_packet(USBDevice *dev, USBPacket *p) 1587840a178cSGerd Hoffmann { 15881c76551fSGerd Hoffmann /* we don't use async packets, so this should never be called */ 1589840a178cSGerd Hoffmann fprintf(stderr, "%s\n", __func__); 1590840a178cSGerd Hoffmann } 1591840a178cSGerd Hoffmann 159253735befSBandan Das static void utf16_to_str(uint8_t len, uint16_t *arr, char *name) 159353735befSBandan Das { 159453735befSBandan Das int count; 159553735befSBandan Das wchar_t *wstr = g_new0(wchar_t, len); 159653735befSBandan Das 159753735befSBandan Das for (count = 0; count < len; count++) { 159853735befSBandan Das wstr[count] = (wchar_t)arr[count]; 159953735befSBandan Das } 160053735befSBandan Das 160153735befSBandan Das wcstombs(name, wstr, len); 160253735befSBandan Das g_free(wstr); 160353735befSBandan Das } 160453735befSBandan Das 160588d5f381SBandan Das static void usb_mtp_write_data(MTPState *s) 160688d5f381SBandan Das { 160788d5f381SBandan Das MTPData *d = s->data_out; 160888d5f381SBandan Das MTPObject *parent = 160988d5f381SBandan Das usb_mtp_object_lookup(s, s->dataset.parent_handle); 161088d5f381SBandan Das char *path = NULL; 161188d5f381SBandan Das int rc = -1; 161288d5f381SBandan Das mode_t mask = 0644; 161388d5f381SBandan Das 161488d5f381SBandan Das assert(d != NULL); 161588d5f381SBandan Das 161688d5f381SBandan Das if (parent == NULL || !s->write_pending) { 161788d5f381SBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans, 161888d5f381SBandan Das 0, 0, 0, 0); 161988d5f381SBandan Das return; 162088d5f381SBandan Das } 162188d5f381SBandan Das 162288d5f381SBandan Das if (s->dataset.filename) { 162388d5f381SBandan Das path = g_strdup_printf("%s/%s", parent->path, s->dataset.filename); 162488d5f381SBandan Das if (s->dataset.format == FMT_ASSOCIATION) { 162588d5f381SBandan Das d->fd = mkdir(path, mask); 162688d5f381SBandan Das goto free; 162788d5f381SBandan Das } 162888d5f381SBandan Das if (s->dataset.size < d->length) { 162988d5f381SBandan Das usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 163088d5f381SBandan Das 0, 0, 0, 0); 163188d5f381SBandan Das goto done; 163288d5f381SBandan Das } 163388d5f381SBandan Das d->fd = open(path, O_CREAT | O_WRONLY, mask); 163488d5f381SBandan Das if (d->fd == -1) { 163588d5f381SBandan Das usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 163688d5f381SBandan Das 0, 0, 0, 0); 163788d5f381SBandan Das goto done; 163888d5f381SBandan Das } 163988d5f381SBandan Das 164088d5f381SBandan Das /* 164188d5f381SBandan Das * Return success if initiator sent 0 sized data 164288d5f381SBandan Das */ 164388d5f381SBandan Das if (!s->dataset.size) { 164488d5f381SBandan Das goto success; 164588d5f381SBandan Das } 164688d5f381SBandan Das 164788d5f381SBandan Das rc = write(d->fd, d->data, s->dataset.size); 164888d5f381SBandan Das if (rc == -1) { 164988d5f381SBandan Das usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 165088d5f381SBandan Das 0, 0, 0, 0); 165188d5f381SBandan Das goto done; 165288d5f381SBandan Das } 165388d5f381SBandan Das if (rc != s->dataset.size) { 165488d5f381SBandan Das usb_mtp_queue_result(s, RES_INCOMPLETE_TRANSFER, d->trans, 165588d5f381SBandan Das 0, 0, 0, 0); 165688d5f381SBandan Das goto done; 165788d5f381SBandan Das } 165888d5f381SBandan Das } 165988d5f381SBandan Das 166088d5f381SBandan Das success: 166188d5f381SBandan Das usb_mtp_queue_result(s, RES_OK, d->trans, 166288d5f381SBandan Das 0, 0, 0, 0); 166388d5f381SBandan Das 166488d5f381SBandan Das done: 166588d5f381SBandan Das /* 166688d5f381SBandan Das * The write dataset is kept around and freed only 166788d5f381SBandan Das * on success or if another write request comes in 166888d5f381SBandan Das */ 166988d5f381SBandan Das if (d->fd != -1) { 167088d5f381SBandan Das close(d->fd); 167188d5f381SBandan Das } 167288d5f381SBandan Das free: 167388d5f381SBandan Das g_free(s->dataset.filename); 167488d5f381SBandan Das g_free(path); 167588d5f381SBandan Das s->write_pending = false; 167688d5f381SBandan Das } 167788d5f381SBandan Das 167853735befSBandan Das static void usb_mtp_write_metadata(MTPState *s) 167953735befSBandan Das { 168053735befSBandan Das MTPData *d = s->data_out; 168153735befSBandan Das ObjectInfo *dataset = (ObjectInfo *)d->data; 168253735befSBandan Das char *filename = g_new0(char, dataset->length); 168353735befSBandan Das MTPObject *o; 168453735befSBandan Das MTPObject *p = usb_mtp_object_lookup(s, s->dataset.parent_handle); 168553735befSBandan Das uint32_t next_handle = s->next_handle; 168653735befSBandan Das 168753735befSBandan Das assert(!s->write_pending); 168824e8d1faSBandan Das assert(p != NULL); 168953735befSBandan Das 169053735befSBandan Das utf16_to_str(dataset->length, dataset->filename, filename); 169153735befSBandan Das 169253735befSBandan Das o = usb_mtp_object_lookup_name(p, filename, dataset->length); 169353735befSBandan Das if (o != NULL) { 169453735befSBandan Das next_handle = o->handle; 169553735befSBandan Das } 169653735befSBandan Das 169753735befSBandan Das s->dataset.filename = filename; 169853735befSBandan Das s->dataset.format = dataset->format; 169953735befSBandan Das s->dataset.size = dataset->size; 170053735befSBandan Das s->dataset.filename = filename; 170153735befSBandan Das s->write_pending = true; 170253735befSBandan Das 170353735befSBandan Das if (s->dataset.format == FMT_ASSOCIATION) { 170453735befSBandan Das usb_mtp_write_data(s); 170553735befSBandan Das /* next_handle will be allocated to the newly created dir */ 170653735befSBandan Das if (d->fd == -1) { 170753735befSBandan Das usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 170853735befSBandan Das 0, 0, 0, 0); 170953735befSBandan Das return; 171053735befSBandan Das } 171153735befSBandan Das d->fd = -1; 171253735befSBandan Das } 171353735befSBandan Das 171453735befSBandan Das usb_mtp_queue_result(s, RES_OK, d->trans, 3, QEMU_STORAGE_ID, 171553735befSBandan Das s->dataset.parent_handle, next_handle); 171653735befSBandan Das } 171753735befSBandan Das 171888d5f381SBandan Das static void usb_mtp_get_data(MTPState *s, mtp_container *container, 171988d5f381SBandan Das USBPacket *p) 172088d5f381SBandan Das { 172188d5f381SBandan Das MTPData *d = s->data_out; 172288d5f381SBandan Das uint64_t dlen; 172388d5f381SBandan Das uint32_t data_len = p->iov.size; 1724*406f35d7SBandan Das uint64_t total_len; 172588d5f381SBandan Das 17263c969a60SBandan Das if (!d) { 17273c969a60SBandan Das usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, 0, 17283c969a60SBandan Das 0, 0, 0, 0); 17293c969a60SBandan Das return; 17303c969a60SBandan Das } 173188d5f381SBandan Das if (d->first) { 173288d5f381SBandan Das /* Total length of incoming data */ 1733*406f35d7SBandan Das total_len = cpu_to_le32(container->length) - sizeof(mtp_container); 173488d5f381SBandan Das /* Length of data in this packet */ 173588d5f381SBandan Das data_len -= sizeof(mtp_container); 1736*406f35d7SBandan Das usb_mtp_realloc(d, total_len); 1737*406f35d7SBandan Das d->length += total_len; 173888d5f381SBandan Das d->offset = 0; 173988d5f381SBandan Das d->first = false; 174088d5f381SBandan Das } 174188d5f381SBandan Das 174288d5f381SBandan Das if (d->length - d->offset > data_len) { 174388d5f381SBandan Das dlen = data_len; 174488d5f381SBandan Das } else { 174588d5f381SBandan Das dlen = d->length - d->offset; 174688d5f381SBandan Das } 174788d5f381SBandan Das 174888d5f381SBandan Das switch (d->code) { 174953735befSBandan Das case CMD_SEND_OBJECT_INFO: 175053735befSBandan Das usb_packet_copy(p, d->data + d->offset, dlen); 175153735befSBandan Das d->offset += dlen; 175253735befSBandan Das if (d->offset == d->length) { 175353735befSBandan Das /* The operation might have already failed */ 175453735befSBandan Das if (!s->result) { 175553735befSBandan Das usb_mtp_write_metadata(s); 175653735befSBandan Das } 175753735befSBandan Das usb_mtp_data_free(s->data_out); 175853735befSBandan Das s->data_out = NULL; 175953735befSBandan Das return; 176053735befSBandan Das } 176153735befSBandan Das break; 176288d5f381SBandan Das case CMD_SEND_OBJECT: 176388d5f381SBandan Das usb_packet_copy(p, d->data + d->offset, dlen); 176488d5f381SBandan Das d->offset += dlen; 176588d5f381SBandan Das if (d->offset == d->length) { 176688d5f381SBandan Das usb_mtp_write_data(s); 176788d5f381SBandan Das usb_mtp_data_free(s->data_out); 176888d5f381SBandan Das s->data_out = NULL; 176988d5f381SBandan Das return; 177088d5f381SBandan Das } 177188d5f381SBandan Das break; 177288d5f381SBandan Das default: 177388d5f381SBandan Das p->status = USB_RET_STALL; 177488d5f381SBandan Das return; 177588d5f381SBandan Das } 177688d5f381SBandan Das } 177788d5f381SBandan Das 1778840a178cSGerd Hoffmann static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) 1779840a178cSGerd Hoffmann { 17807c03a899SGonglei MTPState *s = USB_MTP(dev); 1781840a178cSGerd Hoffmann MTPControl cmd; 1782840a178cSGerd Hoffmann mtp_container container; 1783840a178cSGerd Hoffmann uint32_t params[5]; 178488d5f381SBandan Das uint16_t container_type; 1785840a178cSGerd Hoffmann int i, rc; 1786840a178cSGerd Hoffmann 1787840a178cSGerd Hoffmann switch (p->ep->nr) { 1788840a178cSGerd Hoffmann case EP_DATA_IN: 1789840a178cSGerd Hoffmann if (s->data_out != NULL) { 1790840a178cSGerd Hoffmann /* guest bug */ 1791840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "awaiting data-out"); 1792840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1793840a178cSGerd Hoffmann return; 1794840a178cSGerd Hoffmann } 1795840a178cSGerd Hoffmann if (p->iov.size < sizeof(container)) { 1796840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "packet too small"); 1797840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1798840a178cSGerd Hoffmann return; 1799840a178cSGerd Hoffmann } 1800840a178cSGerd Hoffmann if (s->data_in != NULL) { 1801840a178cSGerd Hoffmann MTPData *d = s->data_in; 18028a5865f3SIsaac Lozano uint64_t dlen = d->length - d->offset; 1803840a178cSGerd Hoffmann if (d->first) { 1804840a178cSGerd Hoffmann trace_usb_mtp_data_in(s->dev.addr, d->trans, d->length); 18058a5865f3SIsaac Lozano if (d->length + sizeof(container) > 0xFFFFFFFF) { 18068a5865f3SIsaac Lozano container.length = cpu_to_le32(0xFFFFFFFF); 18078a5865f3SIsaac Lozano } else { 18088a5865f3SIsaac Lozano container.length = 18098a5865f3SIsaac Lozano cpu_to_le32(d->length + sizeof(container)); 18108a5865f3SIsaac Lozano } 1811840a178cSGerd Hoffmann container.type = cpu_to_le16(TYPE_DATA); 1812840a178cSGerd Hoffmann container.code = cpu_to_le16(d->code); 1813840a178cSGerd Hoffmann container.trans = cpu_to_le32(d->trans); 1814840a178cSGerd Hoffmann usb_packet_copy(p, &container, sizeof(container)); 1815840a178cSGerd Hoffmann d->first = false; 1816840a178cSGerd Hoffmann if (dlen > p->iov.size - sizeof(container)) { 1817840a178cSGerd Hoffmann dlen = p->iov.size - sizeof(container); 1818840a178cSGerd Hoffmann } 1819840a178cSGerd Hoffmann } else { 1820840a178cSGerd Hoffmann if (dlen > p->iov.size) { 1821840a178cSGerd Hoffmann dlen = p->iov.size; 1822840a178cSGerd Hoffmann } 1823840a178cSGerd Hoffmann } 1824840a178cSGerd Hoffmann if (d->fd == -1) { 1825840a178cSGerd Hoffmann usb_packet_copy(p, d->data + d->offset, dlen); 1826840a178cSGerd Hoffmann } else { 1827840a178cSGerd Hoffmann if (d->alloc < p->iov.size) { 1828840a178cSGerd Hoffmann d->alloc = p->iov.size; 1829840a178cSGerd Hoffmann d->data = g_realloc(d->data, d->alloc); 1830840a178cSGerd Hoffmann } 1831840a178cSGerd Hoffmann rc = read(d->fd, d->data, dlen); 1832840a178cSGerd Hoffmann if (rc != dlen) { 18338ebb8763SGerd Hoffmann memset(d->data, 0, dlen); 18348ebb8763SGerd Hoffmann s->result->code = RES_INCOMPLETE_TRANSFER; 1835840a178cSGerd Hoffmann } 1836840a178cSGerd Hoffmann usb_packet_copy(p, d->data, dlen); 1837840a178cSGerd Hoffmann } 1838840a178cSGerd Hoffmann d->offset += dlen; 1839840a178cSGerd Hoffmann if (d->offset == d->length) { 1840840a178cSGerd Hoffmann usb_mtp_data_free(s->data_in); 1841840a178cSGerd Hoffmann s->data_in = NULL; 1842840a178cSGerd Hoffmann } 1843840a178cSGerd Hoffmann } else if (s->result != NULL) { 1844840a178cSGerd Hoffmann MTPControl *r = s->result; 1845840a178cSGerd Hoffmann int length = sizeof(container) + r->argc * sizeof(uint32_t); 1846840a178cSGerd Hoffmann if (r->code == RES_OK) { 1847840a178cSGerd Hoffmann trace_usb_mtp_success(s->dev.addr, r->trans, 1848840a178cSGerd Hoffmann (r->argc > 0) ? r->argv[0] : 0, 1849840a178cSGerd Hoffmann (r->argc > 1) ? r->argv[1] : 0); 1850840a178cSGerd Hoffmann } else { 1851840a178cSGerd Hoffmann trace_usb_mtp_error(s->dev.addr, r->code, r->trans, 1852840a178cSGerd Hoffmann (r->argc > 0) ? r->argv[0] : 0, 1853840a178cSGerd Hoffmann (r->argc > 1) ? r->argv[1] : 0); 1854840a178cSGerd Hoffmann } 1855840a178cSGerd Hoffmann container.length = cpu_to_le32(length); 1856840a178cSGerd Hoffmann container.type = cpu_to_le16(TYPE_RESPONSE); 1857840a178cSGerd Hoffmann container.code = cpu_to_le16(r->code); 1858840a178cSGerd Hoffmann container.trans = cpu_to_le32(r->trans); 1859840a178cSGerd Hoffmann for (i = 0; i < r->argc; i++) { 1860840a178cSGerd Hoffmann params[i] = cpu_to_le32(r->argv[i]); 1861840a178cSGerd Hoffmann } 1862840a178cSGerd Hoffmann usb_packet_copy(p, &container, sizeof(container)); 1863840a178cSGerd Hoffmann usb_packet_copy(p, ¶ms, length - sizeof(container)); 1864840a178cSGerd Hoffmann g_free(s->result); 1865840a178cSGerd Hoffmann s->result = NULL; 1866840a178cSGerd Hoffmann } 1867840a178cSGerd Hoffmann break; 1868840a178cSGerd Hoffmann case EP_DATA_OUT: 1869840a178cSGerd Hoffmann if (p->iov.size < sizeof(container)) { 1870840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "packet too small"); 1871840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1872840a178cSGerd Hoffmann return; 1873840a178cSGerd Hoffmann } 187424e8d1faSBandan Das if ((s->data_out != NULL) && !s->data_out->first) { 187588d5f381SBandan Das container_type = TYPE_DATA; 187688d5f381SBandan Das } else { 1877840a178cSGerd Hoffmann usb_packet_copy(p, &container, sizeof(container)); 187888d5f381SBandan Das container_type = le16_to_cpu(container.type); 187988d5f381SBandan Das } 188088d5f381SBandan Das switch (container_type) { 1881840a178cSGerd Hoffmann case TYPE_COMMAND: 1882840a178cSGerd Hoffmann if (s->data_in || s->data_out || s->result) { 1883840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "transaction inflight"); 1884840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1885840a178cSGerd Hoffmann return; 1886840a178cSGerd Hoffmann } 1887840a178cSGerd Hoffmann cmd.code = le16_to_cpu(container.code); 1888840a178cSGerd Hoffmann cmd.argc = (le32_to_cpu(container.length) - sizeof(container)) 1889840a178cSGerd Hoffmann / sizeof(uint32_t); 1890840a178cSGerd Hoffmann cmd.trans = le32_to_cpu(container.trans); 1891afa82dafSGerd Hoffmann if (cmd.argc > ARRAY_SIZE(cmd.argv)) { 1892afa82dafSGerd Hoffmann cmd.argc = ARRAY_SIZE(cmd.argv); 1893afa82dafSGerd Hoffmann } 1894afa82dafSGerd Hoffmann if (p->iov.size < sizeof(container) + cmd.argc * sizeof(uint32_t)) { 1895afa82dafSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "packet too small"); 1896afa82dafSGerd Hoffmann p->status = USB_RET_STALL; 1897afa82dafSGerd Hoffmann return; 1898afa82dafSGerd Hoffmann } 1899840a178cSGerd Hoffmann usb_packet_copy(p, ¶ms, cmd.argc * sizeof(uint32_t)); 1900840a178cSGerd Hoffmann for (i = 0; i < cmd.argc; i++) { 1901840a178cSGerd Hoffmann cmd.argv[i] = le32_to_cpu(params[i]); 1902840a178cSGerd Hoffmann } 1903840a178cSGerd Hoffmann trace_usb_mtp_command(s->dev.addr, cmd.code, cmd.trans, 1904840a178cSGerd Hoffmann (cmd.argc > 0) ? cmd.argv[0] : 0, 1905840a178cSGerd Hoffmann (cmd.argc > 1) ? cmd.argv[1] : 0, 1906840a178cSGerd Hoffmann (cmd.argc > 2) ? cmd.argv[2] : 0, 1907840a178cSGerd Hoffmann (cmd.argc > 3) ? cmd.argv[3] : 0, 1908840a178cSGerd Hoffmann (cmd.argc > 4) ? cmd.argv[4] : 0); 1909840a178cSGerd Hoffmann usb_mtp_command(s, &cmd); 1910840a178cSGerd Hoffmann break; 191188d5f381SBandan Das case TYPE_DATA: 191288d5f381SBandan Das /* One of the previous transfers has already errored but the 191388d5f381SBandan Das * responder is still sending data associated with it 191488d5f381SBandan Das */ 191588d5f381SBandan Das if (s->result != NULL) { 191688d5f381SBandan Das return; 191788d5f381SBandan Das } 191888d5f381SBandan Das usb_mtp_get_data(s, &container, p); 191988d5f381SBandan Das break; 1920840a178cSGerd Hoffmann default: 19219cd04ccfSGerd Hoffmann /* not needed as long as the mtp device is read-only */ 1922840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1923840a178cSGerd Hoffmann return; 1924840a178cSGerd Hoffmann } 1925840a178cSGerd Hoffmann break; 1926840a178cSGerd Hoffmann case EP_EVENT: 1927983bff35SMatthew Fortune #ifdef CONFIG_INOTIFY1 192893d592e3SBandan Das if (!QTAILQ_EMPTY(&s->events)) { 192993d592e3SBandan Das struct MTPMonEntry *e = QTAILQ_LAST(&s->events, events); 193093d592e3SBandan Das uint32_t handle; 193193d592e3SBandan Das int len = sizeof(container) + sizeof(uint32_t); 193293d592e3SBandan Das 193393d592e3SBandan Das if (p->iov.size < len) { 193493d592e3SBandan Das trace_usb_mtp_stall(s->dev.addr, 193593d592e3SBandan Das "packet too small to send event"); 193693d592e3SBandan Das p->status = USB_RET_STALL; 193793d592e3SBandan Das return; 193893d592e3SBandan Das } 193993d592e3SBandan Das 194093d592e3SBandan Das QTAILQ_REMOVE(&s->events, e, next); 194193d592e3SBandan Das container.length = cpu_to_le32(len); 194293d592e3SBandan Das container.type = cpu_to_le32(TYPE_EVENT); 194393d592e3SBandan Das container.code = cpu_to_le16(e->event); 194493d592e3SBandan Das container.trans = 0; /* no trans specific events */ 194593d592e3SBandan Das handle = cpu_to_le32(e->handle); 194693d592e3SBandan Das usb_packet_copy(p, &container, sizeof(container)); 194793d592e3SBandan Das usb_packet_copy(p, &handle, sizeof(uint32_t)); 194893d592e3SBandan Das g_free(e); 194993d592e3SBandan Das return; 195093d592e3SBandan Das } 195193d592e3SBandan Das #endif 1952840a178cSGerd Hoffmann p->status = USB_RET_NAK; 1953840a178cSGerd Hoffmann return; 1954840a178cSGerd Hoffmann default: 1955840a178cSGerd Hoffmann trace_usb_mtp_stall(s->dev.addr, "invalid endpoint"); 1956840a178cSGerd Hoffmann p->status = USB_RET_STALL; 1957840a178cSGerd Hoffmann return; 1958840a178cSGerd Hoffmann } 1959840a178cSGerd Hoffmann 1960840a178cSGerd Hoffmann if (p->actual_length == 0) { 1961840a178cSGerd Hoffmann trace_usb_mtp_nak(s->dev.addr, p->ep->nr); 1962840a178cSGerd Hoffmann p->status = USB_RET_NAK; 1963840a178cSGerd Hoffmann return; 1964840a178cSGerd Hoffmann } else { 1965840a178cSGerd Hoffmann trace_usb_mtp_xfer(s->dev.addr, p->ep->nr, p->actual_length, 1966840a178cSGerd Hoffmann p->iov.size); 1967840a178cSGerd Hoffmann return; 1968840a178cSGerd Hoffmann } 1969840a178cSGerd Hoffmann } 1970840a178cSGerd Hoffmann 1971df9bb666SGonglei static void usb_mtp_realize(USBDevice *dev, Error **errp) 1972840a178cSGerd Hoffmann { 19737c03a899SGonglei MTPState *s = USB_MTP(dev); 1974840a178cSGerd Hoffmann 1975840a178cSGerd Hoffmann usb_desc_create_serial(dev); 1976840a178cSGerd Hoffmann usb_desc_init(dev); 1977840a178cSGerd Hoffmann QTAILQ_INIT(&s->objects); 1978840a178cSGerd Hoffmann if (s->desc == NULL) { 1979e60baebdSGonglei if (s->root == NULL) { 1980e60baebdSGonglei error_setg(errp, "usb-mtp: x-root property must be configured"); 1981e60baebdSGonglei return; 1982e60baebdSGonglei } 1983840a178cSGerd Hoffmann s->desc = strrchr(s->root, '/'); 1984457d397aSGerd Hoffmann if (s->desc && s->desc[0]) { 1985840a178cSGerd Hoffmann s->desc = g_strdup(s->desc + 1); 1986840a178cSGerd Hoffmann } else { 1987840a178cSGerd Hoffmann s->desc = g_strdup("none"); 1988840a178cSGerd Hoffmann } 1989840a178cSGerd Hoffmann } 19902392ae6bSBandan Das /* Mark store as RW */ 19912392ae6bSBandan Das if (!s->readonly) { 19922392ae6bSBandan Das s->flags |= (1 << MTP_FLAG_WRITABLE); 19932392ae6bSBandan Das } 19942392ae6bSBandan Das 1995840a178cSGerd Hoffmann } 1996840a178cSGerd Hoffmann 1997840a178cSGerd Hoffmann static const VMStateDescription vmstate_usb_mtp = { 1998840a178cSGerd Hoffmann .name = "usb-mtp", 1999840a178cSGerd Hoffmann .unmigratable = 1, 2000840a178cSGerd Hoffmann .version_id = 1, 2001840a178cSGerd Hoffmann .minimum_version_id = 1, 2002840a178cSGerd Hoffmann .fields = (VMStateField[]) { 2003840a178cSGerd Hoffmann VMSTATE_USB_DEVICE(dev, MTPState), 2004840a178cSGerd Hoffmann VMSTATE_END_OF_LIST() 2005840a178cSGerd Hoffmann } 2006840a178cSGerd Hoffmann }; 2007840a178cSGerd Hoffmann 2008840a178cSGerd Hoffmann static Property mtp_properties[] = { 2009cf679cafSGerd Hoffmann DEFINE_PROP_STRING("x-root", MTPState, root), 2010840a178cSGerd Hoffmann DEFINE_PROP_STRING("desc", MTPState, desc), 2011ec6206a6SBandan Das DEFINE_PROP_BOOL("readonly", MTPState, readonly, true), 2012840a178cSGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 2013840a178cSGerd Hoffmann }; 2014840a178cSGerd Hoffmann 2015840a178cSGerd Hoffmann static void usb_mtp_class_initfn(ObjectClass *klass, void *data) 2016840a178cSGerd Hoffmann { 2017840a178cSGerd Hoffmann DeviceClass *dc = DEVICE_CLASS(klass); 2018840a178cSGerd Hoffmann USBDeviceClass *uc = USB_DEVICE_CLASS(klass); 2019840a178cSGerd Hoffmann 2020df9bb666SGonglei uc->realize = usb_mtp_realize; 2021840a178cSGerd Hoffmann uc->product_desc = "QEMU USB MTP"; 2022840a178cSGerd Hoffmann uc->usb_desc = &desc; 2023840a178cSGerd Hoffmann uc->cancel_packet = usb_mtp_cancel_packet; 2024840a178cSGerd Hoffmann uc->handle_attach = usb_desc_attach; 2025840a178cSGerd Hoffmann uc->handle_reset = usb_mtp_handle_reset; 2026840a178cSGerd Hoffmann uc->handle_control = usb_mtp_handle_control; 2027840a178cSGerd Hoffmann uc->handle_data = usb_mtp_handle_data; 2028cdab4dc0SThomas Huth set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); 2029cdab4dc0SThomas Huth dc->desc = "USB Media Transfer Protocol device"; 2030840a178cSGerd Hoffmann dc->fw_name = "mtp"; 2031840a178cSGerd Hoffmann dc->vmsd = &vmstate_usb_mtp; 2032840a178cSGerd Hoffmann dc->props = mtp_properties; 2033840a178cSGerd Hoffmann } 2034840a178cSGerd Hoffmann 2035840a178cSGerd Hoffmann static TypeInfo mtp_info = { 20367c03a899SGonglei .name = TYPE_USB_MTP, 2037840a178cSGerd Hoffmann .parent = TYPE_USB_DEVICE, 2038840a178cSGerd Hoffmann .instance_size = sizeof(MTPState), 2039840a178cSGerd Hoffmann .class_init = usb_mtp_class_initfn, 2040840a178cSGerd Hoffmann }; 2041840a178cSGerd Hoffmann 2042840a178cSGerd Hoffmann static void usb_mtp_register_types(void) 2043840a178cSGerd Hoffmann { 2044840a178cSGerd Hoffmann type_register_static(&mtp_info); 2045840a178cSGerd Hoffmann } 2046840a178cSGerd Hoffmann 2047840a178cSGerd Hoffmann type_init(usb_mtp_register_types) 2048