diff options
| -rw-r--r-- | VERSION | 2 | ||||
| -rw-r--r-- | audio/coreaudio.c | 314 | ||||
| -rw-r--r-- | hw/usb/dev-mtp.c | 315 | ||||
| -rw-r--r-- | hw/usb/hcd-ehci.c | 5 | ||||
| -rw-r--r-- | trace-events | 2 |
5 files changed, 555 insertions, 83 deletions
diff --git a/VERSION b/VERSION index 437459cd94..f6bb220455 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.5.0 +2.5.50 diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 6dfd63eb42..7150604c53 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -32,6 +32,10 @@ #define AUDIO_CAP "coreaudio" #include "audio_int.h" +#ifndef MAC_OS_X_VERSION_10_6 +#define MAC_OS_X_VERSION_10_6 1060 +#endif + static int isAtexit; typedef struct { @@ -45,11 +49,233 @@ typedef struct coreaudioVoiceOut { AudioDeviceID outputDeviceID; UInt32 audioDevicePropertyBufferFrameSize; AudioStreamBasicDescription outputStreamBasicDescription; + AudioDeviceIOProcID ioprocid; int live; int decr; int rpos; } coreaudioVoiceOut; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +/* The APIs used here only become available from 10.6 */ + +static OSStatus coreaudio_get_voice(AudioDeviceID *id) +{ + UInt32 size = sizeof(*id); + AudioObjectPropertyAddress addr = { + kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectGetPropertyData(kAudioObjectSystemObject, + &addr, + 0, + NULL, + &size, + id); +} + +static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, + AudioValueRange *framerange) +{ + UInt32 size = sizeof(*framerange); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyBufferFrameSizeRange, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectGetPropertyData(id, + &addr, + 0, + NULL, + &size, + framerange); +} + +static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) +{ + UInt32 size = sizeof(*framesize); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyBufferFrameSize, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectGetPropertyData(id, + &addr, + 0, + NULL, + &size, + framesize); +} + +static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) +{ + UInt32 size = sizeof(*framesize); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyBufferFrameSize, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectSetPropertyData(id, + &addr, + 0, + NULL, + size, + framesize); +} + +static OSStatus coreaudio_get_streamformat(AudioDeviceID id, + AudioStreamBasicDescription *d) +{ + UInt32 size = sizeof(*d); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyStreamFormat, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectGetPropertyData(id, + &addr, + 0, + NULL, + &size, + d); +} + +static OSStatus coreaudio_set_streamformat(AudioDeviceID id, + AudioStreamBasicDescription *d) +{ + UInt32 size = sizeof(*d); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyStreamFormat, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectSetPropertyData(id, + &addr, + 0, + NULL, + size, + d); +} + +static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) +{ + UInt32 size = sizeof(*result); + AudioObjectPropertyAddress addr = { + kAudioDevicePropertyDeviceIsRunning, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + return AudioObjectGetPropertyData(id, + &addr, + 0, + NULL, + &size, + result); +} +#else +/* Legacy versions of functions using deprecated APIs */ + +static OSStatus coreaudio_get_voice(AudioDeviceID *id) +{ + UInt32 size = sizeof(*id); + + return AudioHardwareGetProperty( + kAudioHardwarePropertyDefaultOutputDevice, + &size, + id); +} + +static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, + AudioValueRange *framerange) +{ + UInt32 size = sizeof(*framerange); + + return AudioDeviceGetProperty( + id, + 0, + 0, + kAudioDevicePropertyBufferFrameSizeRange, + &size, + framerange); +} + +static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) +{ + UInt32 size = sizeof(*framesize); + + return AudioDeviceGetProperty( + id, + 0, + false, + kAudioDevicePropertyBufferFrameSize, + &size, + framesize); +} + +static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) +{ + UInt32 size = sizeof(*framesize); + + return AudioDeviceSetProperty( + id, + NULL, + 0, + false, + kAudioDevicePropertyBufferFrameSize, + size, + framesize); +} + +static OSStatus coreaudio_get_streamformat(AudioDeviceID id, + AudioStreamBasicDescription *d) +{ + UInt32 size = sizeof(*d); + + return AudioDeviceGetProperty( + id, + 0, + false, + kAudioDevicePropertyStreamFormat, + &size, + d); +} + +static OSStatus coreaudio_set_streamformat(AudioDeviceID id, + AudioStreamBasicDescription *d) +{ + UInt32 size = sizeof(*d); + + return AudioDeviceSetProperty( + id, + 0, + 0, + 0, + kAudioDevicePropertyStreamFormat, + size, + d); +} + +static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) +{ + UInt32 size = sizeof(*result); + + return AudioDeviceGetProperty( + id, + 0, + 0, + kAudioDevicePropertyDeviceIsRunning, + &size, + result); +} +#endif + static void coreaudio_logstatus (OSStatus status) { const char *str = "BUG"; @@ -144,10 +370,7 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) { OSStatus status; UInt32 result = 0; - UInt32 propertySize = sizeof(outputDeviceID); - status = AudioDeviceGetProperty( - outputDeviceID, 0, 0, - kAudioDevicePropertyDeviceIsRunning, &propertySize, &result); + status = coreaudio_get_isrunning(outputDeviceID, &result); if (status != kAudioHardwareNoError) { coreaudio_logerr(status, "Could not determine whether Device is playing\n"); @@ -288,7 +511,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, { OSStatus status; coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; - UInt32 propertySize; int err; const char *typ = "playback"; AudioValueRange frameRange; @@ -303,12 +525,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, audio_pcm_init_info (&hw->info, as); - /* open default output device */ - propertySize = sizeof(core->outputDeviceID); - status = AudioHardwareGetProperty( - kAudioHardwarePropertyDefaultOutputDevice, - &propertySize, - &core->outputDeviceID); + status = coreaudio_get_voice(&core->outputDeviceID); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not get default output Device\n"); @@ -320,14 +537,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, } /* get minimum and maximum buffer frame sizes */ - propertySize = sizeof(frameRange); - status = AudioDeviceGetProperty( - core->outputDeviceID, - 0, - 0, - kAudioDevicePropertyBufferFrameSizeRange, - &propertySize, - &frameRange); + status = coreaudio_get_framesizerange(core->outputDeviceID, + &frameRange); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not get device buffer frame range\n"); @@ -347,15 +558,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, } /* set Buffer Frame Size */ - propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); - status = AudioDeviceSetProperty( - core->outputDeviceID, - NULL, - 0, - false, - kAudioDevicePropertyBufferFrameSize, - propertySize, - &core->audioDevicePropertyBufferFrameSize); + status = coreaudio_set_framesize(core->outputDeviceID, + &core->audioDevicePropertyBufferFrameSize); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not set device buffer frame size %" PRIu32 "\n", @@ -364,14 +568,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, } /* get Buffer Frame Size */ - propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); - status = AudioDeviceGetProperty( - core->outputDeviceID, - 0, - false, - kAudioDevicePropertyBufferFrameSize, - &propertySize, - &core->audioDevicePropertyBufferFrameSize); + status = coreaudio_get_framesize(core->outputDeviceID, + &core->audioDevicePropertyBufferFrameSize); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not get device buffer frame size\n"); @@ -380,14 +578,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize; /* get StreamFormat */ - propertySize = sizeof(core->outputStreamBasicDescription); - status = AudioDeviceGetProperty( - core->outputDeviceID, - 0, - false, - kAudioDevicePropertyStreamFormat, - &propertySize, - &core->outputStreamBasicDescription); + status = coreaudio_get_streamformat(core->outputDeviceID, + &core->outputStreamBasicDescription); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not get Device Stream properties\n"); @@ -397,15 +589,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, /* set Samplerate */ core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; - propertySize = sizeof(core->outputStreamBasicDescription); - status = AudioDeviceSetProperty( - core->outputDeviceID, - 0, - 0, - 0, - kAudioDevicePropertyStreamFormat, - propertySize, - &core->outputStreamBasicDescription); + status = coreaudio_set_streamformat(core->outputDeviceID, + &core->outputStreamBasicDescription); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", as->freq); @@ -414,8 +599,12 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, } /* set Callback */ - status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw); - if (status != kAudioHardwareNoError) { + core->ioprocid = NULL; + status = AudioDeviceCreateIOProcID(core->outputDeviceID, + audioDeviceIOProc, + hw, + &core->ioprocid); + if (status != kAudioHardwareNoError || core->ioprocid == NULL) { coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); core->outputDeviceID = kAudioDeviceUnknown; return -1; @@ -423,10 +612,10 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, /* start Playback */ if (!isPlaying(core->outputDeviceID)) { - status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); + status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, "Could not start playback\n"); - AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc); + AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid); core->outputDeviceID = kAudioDeviceUnknown; return -1; } @@ -444,15 +633,15 @@ static void coreaudio_fini_out (HWVoiceOut *hw) if (!isAtexit) { /* stop playback */ if (isPlaying(core->outputDeviceID)) { - status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); + status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); if (status != kAudioHardwareNoError) { coreaudio_logerr (status, "Could not stop playback\n"); } } /* remove callback */ - status = AudioDeviceRemoveIOProc(core->outputDeviceID, - audioDeviceIOProc); + status = AudioDeviceDestroyIOProcID(core->outputDeviceID, + core->ioprocid); if (status != kAudioHardwareNoError) { coreaudio_logerr (status, "Could not remove IOProc\n"); } @@ -475,7 +664,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) case VOICE_ENABLE: /* start playback */ if (!isPlaying(core->outputDeviceID)) { - status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); + status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); if (status != kAudioHardwareNoError) { coreaudio_logerr (status, "Could not resume playback\n"); } @@ -486,7 +675,8 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) /* stop playback */ if (!isAtexit) { if (isPlaying(core->outputDeviceID)) { - status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); + status = AudioDeviceStop(core->outputDeviceID, + core->ioprocid); if (status != kAudioHardwareNoError) { coreaudio_logerr (status, "Could not pause playback\n"); } diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index a2762679eb..af056c7df9 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -15,6 +15,10 @@ #include <sys/stat.h> #include <sys/statvfs.h> +#ifdef __linux__ +#include <sys/inotify.h> +#include "qemu/main-loop.h" +#endif #include "qemu-common.h" #include "qemu/iov.h" @@ -62,6 +66,11 @@ enum mtp_code { /* format codes */ FMT_UNDEFINED_OBJECT = 0x3000, FMT_ASSOCIATION = 0x3001, + + /* event codes */ + EVT_OBJ_ADDED = 0x4002, + EVT_OBJ_REMOVED = 0x4003, + EVT_OBJ_INFO_CHANGED = 0x4007, }; typedef struct { @@ -84,6 +93,17 @@ enum { EP_EVENT, }; +#ifdef __linux__ +typedef struct MTPMonEntry MTPMonEntry; + +struct MTPMonEntry { + uint32_t event; + uint32_t handle; + + QTAILQ_ENTRY(MTPMonEntry) next; +}; +#endif + struct MTPControl { uint16_t code; uint32_t trans; @@ -108,9 +128,14 @@ struct MTPObject { char *name; char *path; struct stat stat; +#ifdef __linux__ + /* inotify watch cookie */ + int watchfd; +#endif MTPObject *parent; - MTPObject **children; uint32_t nchildren; + QLIST_HEAD(, MTPObject) children; + QLIST_ENTRY(MTPObject) list; bool have_children; QTAILQ_ENTRY(MTPObject) next; }; @@ -128,6 +153,11 @@ struct MTPState { uint32_t next_handle; QTAILQ_HEAD(, MTPObject) objects; +#ifdef __linux__ + /* inotify descriptor */ + int inotifyfd; + QTAILQ_HEAD(events, MTPMonEntry) events; +#endif }; #define TYPE_USB_MTP "usb-mtp" @@ -183,7 +213,7 @@ static const USBDescIface desc_iface_full = { },{ .bEndpointAddress = USB_DIR_IN | EP_EVENT, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, + .wMaxPacketSize = 64, .bInterval = 0x0a, }, } @@ -225,7 +255,7 @@ static const USBDescIface desc_iface_high = { },{ .bEndpointAddress = USB_DIR_IN | EP_EVENT, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, + .wMaxPacketSize = 64, .bInterval = 0x0a, }, } @@ -317,15 +347,24 @@ ignore: static void usb_mtp_object_free(MTPState *s, MTPObject *o) { - int i; + MTPObject *iter; + + if (!o) { + return; + } trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path); QTAILQ_REMOVE(&s->objects, o, next); - for (i = 0; i < o->nchildren; i++) { - usb_mtp_object_free(s, o->children[i]); + if (o->parent) { + QLIST_REMOVE(o, list); + o->parent->nchildren--; + } + + while (!QLIST_EMPTY(&o->children)) { + iter = QLIST_FIRST(&o->children); + usb_mtp_object_free(s, iter); } - g_free(o->children); g_free(o->name); g_free(o->path); g_free(o); @@ -343,6 +382,204 @@ static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle) return NULL; } +static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, + char *name) +{ + MTPObject *child = + usb_mtp_object_alloc(s, s->next_handle++, o, name); + + if (child) { + trace_usb_mtp_add_child(s->dev.addr, child->handle, child->path); + QLIST_INSERT_HEAD(&o->children, child, list); + o->nchildren++; + + if (child->format == FMT_ASSOCIATION) { + QLIST_INIT(&child->children); + } + } + + return child; +} + +#ifdef __linux__ +static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, + char *name, int len) +{ + MTPObject *iter; + + QLIST_FOREACH(iter, &parent->children, list) { + if (strncmp(iter->name, name, len) == 0) { + return iter; + } + } + + return NULL; +} + +static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd) +{ + MTPObject *iter; + + QTAILQ_FOREACH(iter, &s->objects, next) { + if (iter->watchfd == wd) { + return iter; + } + } + + return NULL; +} + +static void inotify_watchfn(void *arg) +{ + MTPState *s = arg; + ssize_t bytes; + /* From the man page: atleast one event can be read */ + int len = sizeof(struct inotify_event) + NAME_MAX + 1; + int pos; + char buf[len]; + + for (;;) { + bytes = read(s->inotifyfd, buf, len); + pos = 0; + + if (bytes <= 0) { + /* Better luck next time */ + return; + } + + /* + * TODO: Ignore initiator initiated events. + * For now we are good because the store is RO + */ + while (bytes > 0) { + char *p = buf + pos; + struct inotify_event *event = (struct inotify_event *)p; + int watchfd = 0; + uint32_t mask = event->mask & (IN_CREATE | IN_DELETE | + IN_MODIFY | IN_IGNORED); + MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd); + MTPMonEntry *entry = NULL; + MTPObject *o; + + pos = pos + sizeof(struct inotify_event) + event->len; + bytes = bytes - pos; + + if (!parent) { + continue; + } + + switch (mask) { + case IN_CREATE: + if (usb_mtp_object_lookup_name + (parent, event->name, event->len)) { + /* Duplicate create event */ + continue; + } + entry = g_new0(MTPMonEntry, 1); + entry->handle = s->next_handle; + entry->event = EVT_OBJ_ADDED; + o = usb_mtp_add_child(s, parent, event->name); + if (!o) { + g_free(entry); + continue; + } + o->watchfd = watchfd; + trace_usb_mtp_inotify_event(s->dev.addr, event->name, + event->mask, "Obj Added"); + break; + + case IN_DELETE: + /* + * The kernel issues a IN_IGNORED event + * when a dir containing a watchpoint is + * deleted, so we don't have to delete the + * watchpoint + */ + o = usb_mtp_object_lookup_name(parent, event->name, event->len); + if (!o) { + continue; + } + entry = g_new0(MTPMonEntry, 1); + entry->handle = o->handle; + entry->event = EVT_OBJ_REMOVED; + usb_mtp_object_free(s, o); + trace_usb_mtp_inotify_event(s->dev.addr, o->path, + event->mask, "Obj Deleted"); + break; + + case IN_MODIFY: + o = usb_mtp_object_lookup_name(parent, event->name, event->len); + if (!o) { + continue; + } + entry = g_new0(MTPMonEntry, 1); + entry->handle = o->handle; + entry->event = EVT_OBJ_INFO_CHANGED; + trace_usb_mtp_inotify_event(s->dev.addr, o->path, + event->mask, "Obj Modified"); + break; + + case IN_IGNORED: + o = usb_mtp_object_lookup_name(parent, event->name, event->len); + trace_usb_mtp_inotify_event(s->dev.addr, o->path, + event->mask, "Obj ignored"); + break; + + default: + fprintf(stderr, "usb-mtp: failed to parse inotify event\n"); + continue; + } + + if (entry) { + QTAILQ_INSERT_HEAD(&s->events, entry, next); + } + } + } +} + +static int usb_mtp_inotify_init(MTPState *s) +{ + int fd; + + fd = inotify_init1(IN_NONBLOCK); + if (fd == -1) { + return 1; + } + + QTAILQ_INIT(&s->events); + s->inotifyfd = fd; + + qemu_set_fd_handler(fd, inotify_watchfn, NULL, s); + + return 0; +} + +static void usb_mtp_inotify_cleanup(MTPState *s) +{ + MTPMonEntry *e; + + if (!s->inotifyfd) { + return; + } + + qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s); + close(s->inotifyfd); + + QTAILQ_FOREACH(e, &s->events, next) { + QTAILQ_REMOVE(&s->events, e, next); + g_free(e); + } +} + +static int usb_mtp_add_watch(int inotifyfd, char *path) +{ + uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY | + IN_ISDIR; + + return inotify_add_watch(inotifyfd, path, mask); +} +#endif + static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) { struct dirent *entry; @@ -357,15 +594,18 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) if (!dir) { return; } +#ifdef __linux__ + int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path); + if (watchfd == -1) { + fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path); + } else { + trace_usb_mtp_inotify_event(s->dev.addr, o->path, + 0, "Watch Added"); + o->watchfd = watchfd; + } +#endif while ((entry = readdir(dir)) != NULL) { - if ((o->nchildren % 32) == 0) { - o->children = g_renew(MTPObject *, o->children, o->nchildren + 32); - } - o->children[o->nchildren] = - usb_mtp_object_alloc(s, s->next_handle++, o, entry->d_name); - if (o->children[o->nchildren] != NULL) { - o->nchildren++; - } + usb_mtp_add_child(s, o, entry->d_name); } closedir(dir); } @@ -617,13 +857,15 @@ static MTPData *usb_mtp_get_object_handles(MTPState *s, MTPControl *c, MTPObject *o) { MTPData *d = usb_mtp_data_alloc(c); - uint32_t i, handles[o->nchildren]; + uint32_t i = 0, handles[o->nchildren]; + MTPObject *iter; trace_usb_mtp_op_get_object_handles(s->dev.addr, o->handle, o->path); - for (i = 0; i < o->nchildren; i++) { - handles[i] = o->children[i]->handle; + QLIST_FOREACH(iter, &o->children, list) { + handles[i++] = iter->handle; } + assert(i == o->nchildren); usb_mtp_add_u32_array(d, o->nchildren, handles); return d; @@ -754,11 +996,19 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) trace_usb_mtp_op_open_session(s->dev.addr); s->session = c->argv[0]; usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root); +#ifdef __linux__ + if (usb_mtp_inotify_init(s)) { + fprintf(stderr, "usb-mtp: file monitoring init failed\n"); + } +#endif break; case CMD_CLOSE_SESSION: trace_usb_mtp_op_close_session(s->dev.addr); s->session = 0; s->next_handle = 0; +#ifdef __linux__ + usb_mtp_inotify_cleanup(s); +#endif usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); assert(QTAILQ_EMPTY(&s->objects)); break; @@ -884,6 +1134,10 @@ static void usb_mtp_handle_reset(USBDevice *dev) trace_usb_mtp_reset(s->dev.addr); +#ifdef __linux__ + usb_mtp_inotify_cleanup(s); +#endif + usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); s->session = 0; usb_mtp_data_free(s->data_in); s->data_in = NULL; @@ -1043,6 +1297,31 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) } break; case EP_EVENT: +#ifdef __linux__ + if (!QTAILQ_EMPTY(&s->events)) { + struct MTPMonEntry *e = QTAILQ_LAST(&s->events, events); + uint32_t handle; + int len = sizeof(container) + sizeof(uint32_t); + + if (p->iov.size < len) { + trace_usb_mtp_stall(s->dev.addr, + "packet too small to send event"); + p->status = USB_RET_STALL; + return; + } + + QTAILQ_REMOVE(&s->events, e, next); + container.length = cpu_to_le32(len); + container.type = cpu_to_le32(TYPE_EVENT); + container.code = cpu_to_le16(e->event); + container.trans = 0; /* no trans specific events */ + handle = cpu_to_le32(e->handle); + usb_packet_copy(p, &container, sizeof(container)); + usb_packet_copy(p, &handle, sizeof(uint32_t)); + g_free(e); + return; + } +#endif p->status = USB_RET_NAK; return; default: diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 4e2161b5a7..d07f228df8 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1389,7 +1389,7 @@ static int ehci_process_itd(EHCIState *ehci, { USBDevice *dev; USBEndpoint *ep; - uint32_t i, len, pid, dir, devaddr, endp; + uint32_t i, len, pid, dir, devaddr, endp, xfers = 0; uint32_t pg, off, ptr1, ptr2, max, mult; ehci->periodic_sched_active = PERIODIC_ACTIVE; @@ -1479,9 +1479,10 @@ static int ehci_process_itd(EHCIState *ehci, ehci_raise_irq(ehci, USBSTS_INT); } itd->transact[i] &= ~ITD_XACT_ACTIVE; + xfers++; } } - return 0; + return xfers ? 0 : -1; } diff --git a/trace-events b/trace-events index 2fce98e762..3686e95d98 100644 --- a/trace-events +++ b/trace-events @@ -552,6 +552,8 @@ usb_mtp_op_get_partial_object(int dev, uint32_t handle, const char *path, uint32 usb_mtp_op_unknown(int dev, uint32_t code) "dev %d, command code 0x%x" usb_mtp_object_alloc(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" usb_mtp_object_free(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" +usb_mtp_add_child(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" +usb_mtp_inotify_event(int dev, const char *path, uint32_t mask, const char *s) "dev %d, path %s mask 0x%x event %s" # hw/usb/host-libusb.c usb_host_open_started(int bus, int addr) "dev %d:%d" |