diff options
Diffstat (limited to 'qemu/hw/usb')
34 files changed, 1378 insertions, 190 deletions
diff --git a/qemu/hw/usb/Makefile.objs b/qemu/hw/usb/Makefile.objs index 7443e386b..2717027d3 100644 --- a/qemu/hw/usb/Makefile.objs +++ b/qemu/hw/usb/Makefile.objs @@ -10,6 +10,8 @@ common-obj-$(CONFIG_USB_EHCI_SYSBUS) += hcd-ehci-sysbus.o common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o common-obj-$(CONFIG_USB_MUSB) += hcd-musb.o +obj-$(CONFIG_TUSB6010) += tusb6010.o + # emulated usb devices common-obj-$(CONFIG_USB) += dev-hub.o common-obj-$(CONFIG_USB) += dev-hid.o @@ -23,9 +25,8 @@ common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o ifeq ($(CONFIG_USB_SMARTCARD),y) common-obj-y += dev-smartcard-reader.o -common-obj-y += ccid-card-passthru.o -common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o -ccid-card-emulated.o-cflags := -I$(SRC_PATH)/libcacard +common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o +common-obj-$(CONFIG_SMARTCARD) += ccid-card-emulated.o endif ifeq ($(CONFIG_POSIX),y) diff --git a/qemu/hw/usb/bus.c b/qemu/hw/usb/bus.c index 5f39e1e3a..16c3461d9 100644 --- a/qemu/hw/usb/bus.c +++ b/qemu/hw/usb/bus.c @@ -1,10 +1,13 @@ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/usb.h" #include "hw/qdev.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "monitor/monitor.h" #include "trace.h" +#include "qemu/cutils.h" static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); @@ -329,9 +332,9 @@ static USBDevice *usb_try_create_simple(USBBus *bus, const char *name, } object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err) { - error_setg(errp, "Failed to initialize USB device '%s': %s", - name, error_get_pretty(err)); - error_free(err); + error_propagate(errp, err); + error_prepend(errp, "Failed to initialize USB device '%s': ", + name); object_unparent(OBJECT(dev)); return NULL; } @@ -655,9 +658,12 @@ void hmp_info_usb(Monitor *mon, const QDict *qdict) dev = port->dev; if (!dev) continue; - monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n", - bus->busnr, dev->addr, port->path, usb_speed(dev->speed), - dev->product_desc); + monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, " + "Product %s%s%s\n", + bus->busnr, dev->addr, port->path, + usb_speed(dev->speed), dev->product_desc, + dev->qdev.id ? ", ID: " : "", + dev->qdev.id ?: ""); } } } @@ -722,9 +728,8 @@ USBDevice *usbdevice_create(const char *cmdline) } object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err) { - error_report("Failed to initialize USB device '%s': %s", - f->name, error_get_pretty(err)); - error_free(err); + error_reportf_err(err, "Failed to initialize USB device '%s': ", + f->name); object_unparent(OBJECT(dev)); return NULL; } diff --git a/qemu/hw/usb/ccid-card-emulated.c b/qemu/hw/usb/ccid-card-emulated.c index 72329ed7d..3213f9f8a 100644 --- a/qemu/hw/usb/ccid-card-emulated.c +++ b/qemu/hw/usb/ccid-card-emulated.c @@ -26,6 +26,7 @@ * the db parameter. */ +#include "qemu/osdep.h" #include <eventt.h> #include <vevent.h> #include <vreader.h> @@ -42,7 +43,10 @@ do {\ } \ } while (0) -#define EMULATED_DEV_NAME "ccid-card-emulated" + +#define TYPE_EMULATED_CCID "ccid-card-emulated" +#define EMULATED_CCID_CARD(obj) \ + OBJECT_CHECK(EmulatedState, (obj), TYPE_EMULATED_CCID) #define BACKEND_NSS_EMULATED_NAME "nss-emulated" #define BACKEND_CERTIFICATES_NAME "certificates" @@ -133,7 +137,7 @@ struct EmulatedState { static void emulated_apdu_from_guest(CCIDCardState *base, const uint8_t *apdu, uint32_t len) { - EmulatedState *card = DO_UPCAST(EmulatedState, base, base); + EmulatedState *card = EMULATED_CCID_CARD(base); EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); assert(event); @@ -150,7 +154,7 @@ static void emulated_apdu_from_guest(CCIDCardState *base, static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len) { - EmulatedState *card = DO_UPCAST(EmulatedState, base, base); + EmulatedState *card = EMULATED_CCID_CARD(base); *len = card->atr_length; return card->atr; @@ -166,7 +170,7 @@ static void emulated_push_event(EmulatedState *card, EmulEvent *event) static void emulated_push_type(EmulatedState *card, uint32_t type) { - EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent)); + EmulEvent *event = g_new(EmulEvent, 1); assert(event); event->p.gen.type = type; @@ -175,7 +179,7 @@ static void emulated_push_type(EmulatedState *card, uint32_t type) static void emulated_push_error(EmulatedState *card, uint64_t code) { - EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent)); + EmulEvent *event = g_new(EmulEvent, 1); assert(event); event->p.error.type = EMUL_ERROR; @@ -403,7 +407,7 @@ static int init_event_notifier(EmulatedState *card) DPRINTF(card, 2, "event notifier creation failed\n"); return -1; } - event_notifier_set_handler(&card->notifier, card_event_handler); + event_notifier_set_handler(&card->notifier, false, card_event_handler); return 0; } @@ -478,7 +482,7 @@ static uint32_t parse_enumeration(char *str, static int emulated_initfn(CCIDCardState *base) { - EmulatedState *card = DO_UPCAST(EmulatedState, base, base); + EmulatedState *card = EMULATED_CCID_CARD(base); VCardEmulError ret; const EnumTable *ptable; @@ -514,26 +518,26 @@ static int emulated_initfn(CCIDCardState *base) ret = emulated_initialize_vcard_from_certificates(card); } else { printf("%s: you must provide all three certs for" - " certificates backend\n", EMULATED_DEV_NAME); + " certificates backend\n", TYPE_EMULATED_CCID); return -1; } } else { if (card->backend != BACKEND_NSS_EMULATED) { printf("%s: bad backend specified. The options are:\n%s (default)," - " %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME, + " %s.\n", TYPE_EMULATED_CCID, BACKEND_NSS_EMULATED_NAME, BACKEND_CERTIFICATES_NAME); return -1; } if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) { printf("%s: unexpected cert parameters to nss emulated backend\n", - EMULATED_DEV_NAME); + TYPE_EMULATED_CCID); return -1; } /* default to mirroring the local hardware readers */ ret = wrap_vcard_emul_init(NULL); } if (ret != VCARD_EMUL_OK) { - printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME); + printf("%s: failed to initialize vcard\n", TYPE_EMULATED_CCID); return -1; } qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread, @@ -545,7 +549,7 @@ static int emulated_initfn(CCIDCardState *base) static int emulated_exitfn(CCIDCardState *base) { - EmulatedState *card = DO_UPCAST(EmulatedState, base, base); + EmulatedState *card = EMULATED_CCID_CARD(base); VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); vevent_queue_vevent(vevent); /* stop vevent thread */ @@ -588,7 +592,7 @@ static void emulated_class_initfn(ObjectClass *klass, void *data) } static const TypeInfo emulated_card_info = { - .name = EMULATED_DEV_NAME, + .name = TYPE_EMULATED_CCID, .parent = TYPE_CCID_CARD, .instance_size = sizeof(EmulatedState), .class_init = emulated_class_initfn, diff --git a/qemu/hw/usb/ccid-card-passthru.c b/qemu/hw/usb/ccid-card-passthru.c index 85a4fc3e5..c0e90e501 100644 --- a/qemu/hw/usb/ccid-card-passthru.c +++ b/qemu/hw/usb/ccid-card-passthru.c @@ -8,11 +8,12 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "sysemu/char.h" #include "qemu/error-report.h" #include "qemu/sockets.h" #include "ccid.h" -#include "libcacard/vscard_common.h" +#include "cacard/vscard_common.h" #define DPRINTF(card, lvl, fmt, ...) \ do { \ @@ -38,8 +39,6 @@ static const uint8_t DEFAULT_ATR[] = { 0x13, 0x08 }; - -#define PASSTHRU_DEV_NAME "ccid-card-passthru" #define VSCARD_IN_SIZE 65536 /* maximum size of ATR - from 7816-3 */ @@ -58,6 +57,10 @@ struct PassthruState { uint8_t debug; }; +#define TYPE_CCID_PASSTHRU "ccid-card-passthru" +#define PASSTHRU_CCID_CARD(obj) \ + OBJECT_CHECK(PassthruState, (obj), TYPE_CCID_PASSTHRU) + /* * VSCard protocol over chardev * This code should not depend on the card type. @@ -316,7 +319,7 @@ static void ccid_card_vscard_event(void *opaque, int event) static void passthru_apdu_from_guest( CCIDCardState *base, const uint8_t *apdu, uint32_t len) { - PassthruState *card = DO_UPCAST(PassthruState, base, base); + PassthruState *card = PASSTHRU_CCID_CARD(base); if (!card->cs) { printf("ccid-passthru: no chardev, discarding apdu length %d\n", len); @@ -327,7 +330,7 @@ static void passthru_apdu_from_guest( static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len) { - PassthruState *card = DO_UPCAST(PassthruState, base, base); + PassthruState *card = PASSTHRU_CCID_CARD(base); *len = card->atr_length; return card->atr; @@ -335,7 +338,7 @@ static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len) static int passthru_initfn(CCIDCardState *base) { - PassthruState *card = DO_UPCAST(PassthruState, base, base); + PassthruState *card = PASSTHRU_CCID_CARD(base); card->vscard_in_pos = 0; card->vscard_in_hdr = 0; @@ -399,7 +402,7 @@ static void passthru_class_initfn(ObjectClass *klass, void *data) } static const TypeInfo passthru_card_info = { - .name = PASSTHRU_DEV_NAME, + .name = TYPE_CCID_PASSTHRU, .parent = TYPE_CCID_CARD, .instance_size = sizeof(PassthruState), .class_init = passthru_class_initfn, diff --git a/qemu/hw/usb/combined-packet.c b/qemu/hw/usb/combined-packet.c index ad77705f8..48cac87f6 100644 --- a/qemu/hw/usb/combined-packet.c +++ b/qemu/hw/usb/combined-packet.c @@ -19,6 +19,7 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/usb.h" #include "qemu/iov.h" diff --git a/qemu/hw/usb/core.c b/qemu/hw/usb/core.c index d0025db60..45fa00c51 100644 --- a/qemu/hw/usb/core.c +++ b/qemu/hw/usb/core.c @@ -23,6 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/usb.h" #include "qemu/iov.h" @@ -128,9 +129,16 @@ static void do_token_setup(USBDevice *s, USBPacket *p) } usb_packet_copy(p, s->setup_buf, p->iov.size); + s->setup_index = 0; p->actual_length = 0; s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; - s->setup_index = 0; + if (s->setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", + s->setup_len, sizeof(s->data_buf)); + p->status = USB_RET_STALL; + return; + } request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; @@ -151,13 +159,6 @@ static void do_token_setup(USBDevice *s, USBPacket *p) } s->setup_state = SETUP_STATE_DATA; } else { - if (s->setup_len > sizeof(s->data_buf)) { - fprintf(stderr, - "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", - s->setup_len, sizeof(s->data_buf)); - p->status = USB_RET_STALL; - return; - } if (s->setup_len == 0) s->setup_state = SETUP_STATE_ACK; else @@ -176,7 +177,7 @@ static void do_token_in(USBDevice *s, USBPacket *p) request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; index = (s->setup_buf[5] << 8) | s->setup_buf[4]; - + switch(s->setup_state) { case SETUP_STATE_ACK: if (!(s->setup_buf[0] & USB_DIR_IN)) { diff --git a/qemu/hw/usb/desc-msos.c b/qemu/hw/usb/desc-msos.c index 32c3600df..365291981 100644 --- a/qemu/hw/usb/desc-msos.c +++ b/qemu/hw/usb/desc-msos.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "hw/usb.h" #include "hw/usb/desc.h" diff --git a/qemu/hw/usb/desc.c b/qemu/hw/usb/desc.c index b82c397ef..adb026e43 100644 --- a/qemu/hw/usb/desc.c +++ b/qemu/hw/usb/desc.c @@ -1,4 +1,4 @@ -#include <ctype.h> +#include "qemu/osdep.h" #include "hw/usb.h" #include "hw/usb/desc.h" diff --git a/qemu/hw/usb/desc.h b/qemu/hw/usb/desc.h index 8e8db03a0..4d81c68e0 100644 --- a/qemu/hw/usb/desc.h +++ b/qemu/hw/usb/desc.h @@ -1,7 +1,6 @@ #ifndef QEMU_HW_USB_DESC_H #define QEMU_HW_USB_DESC_H -#include <inttypes.h> #include <wchar.h> /* binary representation */ diff --git a/qemu/hw/usb/dev-audio.c b/qemu/hw/usb/dev-audio.c index f092bb849..87cab0a3d 100644 --- a/qemu/hw/usb/dev-audio.c +++ b/qemu/hw/usb/dev-audio.c @@ -29,6 +29,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/usb.h" #include "hw/usb/desc.h" @@ -664,7 +665,7 @@ static const VMStateDescription vmstate_usb_audio = { static Property usb_audio_properties[] = { DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), DEFINE_PROP_UINT32("buffer", USBAudioState, buffer, - 8 * USBAUDIO_PACKET_SIZE), + 32 * USBAUDIO_PACKET_SIZE), DEFINE_PROP_END_OF_LIST(), }; diff --git a/qemu/hw/usb/dev-bluetooth.c b/qemu/hw/usb/dev-bluetooth.c index b19ec76b0..91a4a0b8b 100644 --- a/qemu/hw/usb/dev-bluetooth.c +++ b/qemu/hw/usb/dev-bluetooth.c @@ -18,6 +18,7 @@ * with this program; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/dev-hid.c b/qemu/hw/usb/dev-hid.c index 2e7dcd96c..24d05f76f 100644 --- a/qemu/hw/usb/dev-hid.c +++ b/qemu/hw/usb/dev-hid.c @@ -22,10 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "hw/usb.h" #include "hw/usb/desc.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "hw/input/hid.h" diff --git a/qemu/hw/usb/dev-hub.c b/qemu/hw/usb/dev-hub.c index c8c685550..a33f21cb3 100644 --- a/qemu/hw/usb/dev-hub.c +++ b/qemu/hw/usb/dev-hub.c @@ -21,6 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "trace.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/dev-mtp.c b/qemu/hw/usb/dev-mtp.c index 809b1cb11..bda84a64b 100644 --- a/qemu/hw/usb/dev-mtp.c +++ b/qemu/hw/usb/dev-mtp.c @@ -9,12 +9,17 @@ * This code is licensed under the GPL v2 or later. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include <wchar.h> #include <dirent.h> -#include <unistd.h> -#include <sys/stat.h> #include <sys/statvfs.h> +#ifdef CONFIG_INOTIFY1 +#include <sys/inotify.h> +#include "qapi/error.h" +#include "qemu/main-loop.h" +#endif #include "qemu-common.h" #include "qemu/iov.h" @@ -62,6 +67,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 +94,17 @@ enum { EP_EVENT, }; +#ifdef CONFIG_INOTIFY1 +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 +129,14 @@ struct MTPObject { char *name; char *path; struct stat stat; +#ifdef CONFIG_INOTIFY1 + /* 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 +154,11 @@ struct MTPState { uint32_t next_handle; QTAILQ_HEAD(, MTPObject) objects; +#ifdef CONFIG_INOTIFY1 + /* inotify descriptor */ + int inotifyfd; + QTAILQ_HEAD(events, MTPMonEntry) events; +#endif }; #define TYPE_USB_MTP "usb-mtp" @@ -183,7 +214,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 +256,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 +348,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 +383,203 @@ 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 CONFIG_INOTIFY1 +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 pos; + char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; + + for (;;) { + bytes = read(s->inotifyfd, buf, sizeof(buf)); + 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; + trace_usb_mtp_inotify_event(s->dev.addr, o->path, + event->mask, "Obj Deleted"); + usb_mtp_object_free(s, o); + 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, *p; + + if (!s->inotifyfd) { + return; + } + + qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s); + close(s->inotifyfd); + + QTAILQ_FOREACH_SAFE(e, &s->events, next, p) { + 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,16 +594,18 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) if (!dir) { return; } +#ifdef CONFIG_INOTIFY1 + 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_realloc(o->children, - (o->nchildren + 32) * sizeof(MTPObject *)); - } - 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); } @@ -480,7 +719,7 @@ static void usb_mtp_add_wstr(MTPData *data, const wchar_t *str) static void usb_mtp_add_str(MTPData *data, const char *str) { uint32_t len = strlen(str)+1; - wchar_t wstr[len]; + wchar_t *wstr = g_new(wchar_t, len); size_t ret; ret = mbstowcs(wstr, str, len); @@ -489,6 +728,8 @@ static void usb_mtp_add_str(MTPData *data, const char *str) } else { usb_mtp_add_wstr(data, wstr); } + + g_free(wstr); } static void usb_mtp_add_time(MTPData *data, time_t time) @@ -618,13 +859,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; @@ -755,11 +998,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 CONFIG_INOTIFY1 + 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 CONFIG_INOTIFY1 + usb_mtp_inotify_cleanup(s); +#endif usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); assert(QTAILQ_EMPTY(&s->objects)); break; @@ -885,6 +1136,10 @@ static void usb_mtp_handle_reset(USBDevice *dev) trace_usb_mtp_reset(s->dev.addr); +#ifdef CONFIG_INOTIFY1 + 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; @@ -1044,6 +1299,31 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) } break; case EP_EVENT: +#ifdef CONFIG_INOTIFY1 + 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/qemu/hw/usb/dev-network.c b/qemu/hw/usb/dev-network.c index 7800ceea5..74306b58e 100644 --- a/qemu/hw/usb/dev-network.c +++ b/qemu/hw/usb/dev-network.c @@ -23,6 +23,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "hw/usb.h" #include "hw/usb/desc.h" @@ -32,6 +34,7 @@ #include "qemu/config-file.h" #include "sysemu/sysemu.h" #include "qemu/iov.h" +#include "qemu/cutils.h" /*#define TRAFFIC_DEBUG*/ /* Thanks to NetChip Technologies for donating this product ID. @@ -653,7 +656,8 @@ typedef struct USBNetState { static int is_rndis(USBNetState *s) { - return s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE; + return s->dev.config ? + s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE : 0; } static int ndis_query(USBNetState *s, uint32_t oid, @@ -914,8 +918,9 @@ static int rndis_query_response(USBNetState *s, bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; buflen = le32_to_cpu(buf->InformationBufferLength); - if (bufoffs + buflen > length) + if (buflen > length || bufoffs >= length || bufoffs + buflen > length) { return USB_RET_STALL; + } infobuflen = ndis_query(s, le32_to_cpu(buf->OID), bufoffs + (uint8_t *) buf, buflen, infobuf, @@ -960,8 +965,9 @@ static int rndis_set_response(USBNetState *s, bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; buflen = le32_to_cpu(buf->InformationBufferLength); - if (bufoffs + buflen > length) + if (buflen > length || bufoffs >= length || bufoffs + buflen > length) { return USB_RET_STALL; + } ret = ndis_set(s, le32_to_cpu(buf->OID), bufoffs + (uint8_t *) buf, buflen); @@ -1211,8 +1217,9 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) { uint32_t offs = 8 + le32_to_cpu(msg->DataOffset); uint32_t size = le32_to_cpu(msg->DataLength); - if (offs + size <= len) + if (offs < len && size < len && offs + size <= len) { qemu_send_packet(qemu_get_queue(s->nic), s->out_buf + offs, size); + } } s->out_ptr -= len; memmove(s->out_buf, &s->out_buf[len], s->out_ptr); diff --git a/qemu/hw/usb/dev-serial.c b/qemu/hw/usb/dev-serial.c index a6a66008e..ba8538e60 100644 --- a/qemu/hw/usb/dev-serial.c +++ b/qemu/hw/usb/dev-serial.c @@ -8,7 +8,10 @@ * This code is licensed under the LGPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #include "hw/usb.h" #include "hw/usb/desc.h" diff --git a/qemu/hw/usb/dev-smartcard-reader.c b/qemu/hw/usb/dev-smartcard-reader.c index 8952efffa..af4b85135 100644 --- a/qemu/hw/usb/dev-smartcard-reader.c +++ b/qemu/hw/usb/dev-smartcard-reader.c @@ -34,6 +34,8 @@ * Not sure which messages trigger this. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/dev-storage.c b/qemu/hw/usb/dev-storage.c index 9a4e7dc0c..248a58045 100644 --- a/qemu/hw/usb/dev-storage.c +++ b/qemu/hw/usb/dev-storage.c @@ -7,6 +7,8 @@ * This code is licensed under the LGPL. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -20,6 +22,7 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "qapi/visitor.h" +#include "qemu/cutils.h" //#define DEBUG_MSD @@ -613,20 +616,22 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp) return; } - bdrv_add_key(blk_bs(blk), NULL, &err); - if (err) { - if (monitor_cur_is_qmp()) { - error_propagate(errp, err); - return; - } - error_free(err); - err = NULL; - if (cur_mon) { - monitor_read_bdrv_key_start(cur_mon, blk_bs(blk), - usb_msd_password_cb, s); - s->dev.auto_attach = 0; - } else { - autostart = 0; + if (blk_bs(blk)) { + bdrv_add_key(blk_bs(blk), NULL, &err); + if (err) { + if (monitor_cur_is_qmp()) { + error_propagate(errp, err); + return; + } + error_free(err); + err = NULL; + if (cur_mon) { + monitor_read_bdrv_key_start(cur_mon, blk_bs(blk), + usb_msd_password_cb, s); + s->dev.auto_attach = 0; + } else { + autostart = 0; + } } } @@ -778,24 +783,24 @@ static void usb_msd_class_initfn_storage(ObjectClass *klass, void *data) dc->props = msd_properties; } -static void usb_msd_get_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { USBDevice *dev = USB_DEVICE(obj); MSDState *s = USB_STORAGE_DEV(dev); - visit_type_int32(v, &s->conf.bootindex, name, errp); + visit_type_int32(v, name, &s->conf.bootindex, errp); } -static void usb_msd_set_bootindex(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { USBDevice *dev = USB_DEVICE(obj); MSDState *s = USB_STORAGE_DEV(dev); int32_t boot_index; Error *local_err = NULL; - visit_type_int32(v, &boot_index, name, &local_err); + visit_type_int32(v, name, &boot_index, &local_err); if (local_err) { goto out; } diff --git a/qemu/hw/usb/dev-uas.c b/qemu/hw/usb/dev-uas.c index 38b26c586..0678b1b05 100644 --- a/qemu/hw/usb/dev-uas.c +++ b/qemu/hw/usb/dev-uas.c @@ -9,6 +9,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/option.h" #include "qemu/config-file.h" diff --git a/qemu/hw/usb/dev-wacom.c b/qemu/hw/usb/dev-wacom.c index c2450e729..c4702dbba 100644 --- a/qemu/hw/usb/dev-wacom.c +++ b/qemu/hw/usb/dev-wacom.c @@ -25,6 +25,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "ui/console.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/hcd-ehci-pci.c b/qemu/hw/usb/hcd-ehci-pci.c index 7afa5f9d6..56577051e 100644 --- a/qemu/hw/usb/hcd-ehci-pci.c +++ b/qemu/hw/usb/hcd-ehci-pci.c @@ -15,6 +15,7 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" #include "hw/usb/hcd-ehci.h" #include "qemu/range.h" @@ -95,10 +96,8 @@ static void usb_ehci_pci_exit(PCIDevice *dev) usb_ehci_unrealize(s, DEVICE(dev), NULL); - if (s->irq) { - g_free(s->irq); - s->irq = NULL; - } + g_free(s->irq); + s->irq = NULL; } static void usb_ehci_pci_reset(DeviceState *dev) diff --git a/qemu/hw/usb/hcd-ehci-sysbus.c b/qemu/hw/usb/hcd-ehci-sysbus.c index cd1cc142a..6c20604d0 100644 --- a/qemu/hw/usb/hcd-ehci-sysbus.c +++ b/qemu/hw/usb/hcd-ehci-sysbus.c @@ -15,6 +15,7 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" #include "hw/usb/hcd-ehci.h" static const VMStateDescription vmstate_ehci_sysbus = { diff --git a/qemu/hw/usb/hcd-ehci.c b/qemu/hw/usb/hcd-ehci.c index 64a54c6e8..43a8f7abc 100644 --- a/qemu/hw/usb/hcd-ehci.c +++ b/qemu/hw/usb/hcd-ehci.c @@ -27,6 +27,8 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/usb/ehci-regs.h" #include "hw/usb/hcd-ehci.h" #include "trace.h" @@ -726,7 +728,7 @@ static void ehci_detach(USBPort *port) ehci_queues_rip_device(s, port->dev, 0); ehci_queues_rip_device(s, port->dev, 1); - *portsc &= ~(PORTSC_CONNECT|PORTSC_PED); + *portsc &= ~(PORTSC_CONNECT|PORTSC_PED|PORTSC_SUSPEND); *portsc |= PORTSC_CSC; ehci_raise_irq(s, USBSTS_PCD); @@ -865,6 +867,7 @@ void ehci_reset(void *opaque) s->usbsts = USBSTS_HALT; s->usbsts_pending = 0; s->usbsts_frindex = 0; + ehci_update_irq(s); s->astate = EST_INACTIVE; s->pstate = EST_INACTIVE; @@ -893,6 +896,11 @@ static uint64_t ehci_caps_read(void *ptr, hwaddr addr, return s->caps[addr]; } +static void ehci_caps_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ +} + static uint64_t ehci_opreg_read(void *ptr, hwaddr addr, unsigned size) { @@ -1404,21 +1412,23 @@ static int ehci_process_itd(EHCIState *ehci, if (itd->transact[i] & ITD_XACT_ACTIVE) { pg = get_field(itd->transact[i], ITD_XACT_PGSEL); off = itd->transact[i] & ITD_XACT_OFFSET_MASK; - ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK); - ptr2 = (itd->bufptr[pg+1] & ITD_BUFPTR_MASK); len = get_field(itd->transact[i], ITD_XACT_LENGTH); if (len > max * mult) { len = max * mult; } - - if (len > BUFF_SIZE) { + if (len > BUFF_SIZE || pg > 6) { return -1; } + ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK); qemu_sglist_init(&ehci->isgl, ehci->device, 2, ehci->as); if (off + len > 4096) { /* transfer crosses page border */ + if (pg == 6) { + return -1; /* avoid page pg + 1 */ + } + ptr2 = (itd->bufptr[pg + 1] & ITD_BUFPTR_MASK); uint32_t len2 = off + len - 4096; uint32_t len1 = len - len2; qemu_sglist_add(&ehci->isgl, ptr1 + off, len1); @@ -2000,6 +2010,7 @@ static int ehci_state_writeback(EHCIQueue *q) static void ehci_advance_state(EHCIState *ehci, int async) { EHCIQueue *q = NULL; + int itd_count = 0; int again; do { @@ -2024,10 +2035,12 @@ static void ehci_advance_state(EHCIState *ehci, int async) case EST_FETCHITD: again = ehci_state_fetchitd(ehci, async); + itd_count++; break; case EST_FETCHSITD: again = ehci_state_fetchsitd(ehci, async); + itd_count++; break; case EST_ADVANCEQUEUE: @@ -2076,7 +2089,8 @@ static void ehci_advance_state(EHCIState *ehci, int async) break; } - if (again < 0) { + if (again < 0 || itd_count > 16) { + /* TODO: notify guest (raise HSE irq?) */ fprintf(stderr, "processing error - resetting ehci HC\n"); ehci_reset(ehci); again = 0; @@ -2298,10 +2312,11 @@ static void ehci_frame_timer(void *opaque) /* If we've raised int, we speed up the timer, so that we quickly * notice any new packets queued up in response */ if (ehci->int_req_by_async && (ehci->usbsts & USBSTS_INT)) { - expire_time = t_now + get_ticks_per_sec() / (FRAME_TIMER_FREQ * 4); + expire_time = t_now + + NANOSECONDS_PER_SECOND / (FRAME_TIMER_FREQ * 4); ehci->int_req_by_async = false; } else { - expire_time = t_now + (get_ticks_per_sec() + expire_time = t_now + (NANOSECONDS_PER_SECOND * (ehci->async_stepdown+1) / FRAME_TIMER_FREQ); } timer_mod(ehci->frame_timer, expire_time); @@ -2310,6 +2325,7 @@ static void ehci_frame_timer(void *opaque) static const MemoryRegionOps ehci_mmio_caps_ops = { .read = ehci_caps_read, + .write = ehci_caps_write, .valid.min_access_size = 1, .valid.max_access_size = 4, .impl.min_access_size = 1, diff --git a/qemu/hw/usb/hcd-musb.c b/qemu/hw/usb/hcd-musb.c index 61cc87894..27d9d0bd8 100644 --- a/qemu/hw/usb/hcd-musb.c +++ b/qemu/hw/usb/hcd-musb.c @@ -20,6 +20,7 @@ * * Only host-mode and non-DMA accesses are currently supported. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" #include "hw/usb.h" @@ -563,7 +564,7 @@ static void musb_schedule_cb(USBPort *port, USBPacket *packey) ep->intv_timer[dir] = timer_new_ns(QEMU_CLOCK_VIRTUAL, musb_cb_tick, ep); timer_mod(ep->intv_timer[dir], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(timeout, get_ticks_per_sec(), 8000)); + muldiv64(timeout, NANOSECONDS_PER_SECOND, 8000)); } static int musb_timeout(int ttype, int speed, int val) diff --git a/qemu/hw/usb/hcd-ohci.c b/qemu/hw/usb/hcd-ohci.c index 7d6581806..ffab561cf 100644 --- a/qemu/hw/usb/hcd-ohci.c +++ b/qemu/hw/usb/hcd-ohci.c @@ -25,7 +25,9 @@ * o BIOS work to boot from USB storage */ +#include "qemu/osdep.h" #include "hw/hw.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "hw/usb.h" #include "hw/pci/pci.h" @@ -439,15 +441,37 @@ static void ohci_stop_endpoints(OHCIState *ohci) } } -/* Reset the controller */ -static void ohci_reset(void *opaque) +static void ohci_roothub_reset(OHCIState *ohci) { - OHCIState *ohci = opaque; OHCIPort *port; int i; ohci_bus_stop(ohci); - ohci->ctl = 0; + ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports; + ohci->rhdesc_b = 0x0; /* Impl. specific */ + ohci->rhstatus = 0; + + for (i = 0; i < ohci->num_ports; i++) { + port = &ohci->rhport[i]; + port->ctrl = 0; + if (port->port.dev && port->port.dev->attached) { + usb_port_reset(&port->port); + } + } + if (ohci->async_td) { + usb_cancel_packet(&ohci->usb_packet); + ohci->async_td = 0; + } + ohci_stop_endpoints(ohci); +} + +/* Reset the controller */ +static void ohci_soft_reset(OHCIState *ohci) +{ + trace_usb_ohci_reset(ohci->name); + + ohci_bus_stop(ohci); + ohci->ctl = (ohci->ctl & OHCI_CTL_IR) | OHCI_USB_SUSPEND; ohci->old_ctl = 0; ohci->status = 0; ohci->intr_status = 0; @@ -470,25 +494,13 @@ static void ohci_reset(void *opaque) ohci->frame_number = 0; ohci->pstart = 0; ohci->lst = OHCI_LS_THRESH; +} - ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports; - ohci->rhdesc_b = 0x0; /* Impl. specific */ - ohci->rhstatus = 0; - - for (i = 0; i < ohci->num_ports; i++) - { - port = &ohci->rhport[i]; - port->ctrl = 0; - if (port->port.dev && port->port.dev->attached) { - usb_port_reset(&port->port); - } - } - if (ohci->async_td) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; - } - ohci_stop_endpoints(ohci); - trace_usb_ohci_reset(ohci->name); +static void ohci_hard_reset(OHCIState *ohci) +{ + ohci_soft_reset(ohci); + ohci->ctl = 0; + ohci_roothub_reset(ohci); } /* Get an array of dwords from main memory */ @@ -1231,11 +1243,16 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) return active; } -/* Generate a SOF event, and set a timer for EOF */ -static void ohci_sof(OHCIState *ohci) +/* set a timer for EOF */ +static void ohci_eof_timer(OHCIState *ohci) { ohci->sof_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); timer_mod(ohci->eof_timer, ohci->sof_time + usb_frame_time); +} +/* Set a timer for EOF and generate a SOF event */ +static void ohci_sof(OHCIState *ohci) +{ + ohci_eof_timer(ohci); ohci_set_interrupt(ohci, OHCI_INTR_SF); } @@ -1331,19 +1348,14 @@ static void ohci_frame_boundary(void *opaque) */ static int ohci_bus_start(OHCIState *ohci) { - ohci->eof_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - ohci_frame_boundary, - ohci); - - if (ohci->eof_timer == NULL) { - trace_usb_ohci_bus_eof_timer_failed(ohci->name); - ohci_die(ohci); - return 0; - } - trace_usb_ohci_start(ohci->name); - ohci_sof(ohci); + /* Delay the first SOF event by one frame time as + * linux driver is not ready to receive it and + * can meet some race conditions + */ + + ohci_eof_timer(ohci); return 1; } @@ -1352,11 +1364,7 @@ static int ohci_bus_start(OHCIState *ohci) static void ohci_bus_stop(OHCIState *ohci) { trace_usb_ohci_stop(ohci->name); - if (ohci->eof_timer) { - timer_del(ohci->eof_timer); - timer_free(ohci->eof_timer); - } - ohci->eof_timer = NULL; + timer_del(ohci->eof_timer); } /* Sets a flag in a port status register but only set it if the port is @@ -1436,12 +1444,15 @@ static void ohci_set_ctl(OHCIState *ohci, uint32_t val) break; case OHCI_USB_SUSPEND: ohci_bus_stop(ohci); + /* clear pending SF otherwise linux driver loops in ohci_irq() */ + ohci->intr_status &= ~OHCI_INTR_SF; + ohci_intr_update(ohci); break; case OHCI_USB_RESUME: trace_usb_ohci_resume(ohci->name); break; case OHCI_USB_RESET: - ohci_reset(ohci); + ohci_roothub_reset(ohci); break; } } @@ -1704,7 +1715,7 @@ static void ohci_mem_write(void *opaque, ohci->status |= val; if (ohci->status & OHCI_STATUS_HCR) - ohci_reset(ohci); + ohci_soft_reset(ohci); break; case 3: /* HcInterruptStatus */ @@ -1783,7 +1794,7 @@ static void ohci_mem_write(void *opaque, case 25: /* HcHReset */ ohci->hreset = val & ~OHCI_HRESET_FSBIR; if (val & OHCI_HRESET_FSBIR) - ohci_reset(ohci); + ohci_hard_reset(ohci); break; case 26: /* HcHInterruptEnable */ @@ -1839,12 +1850,12 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, if (usb_frame_time == 0) { #ifdef OHCI_TIME_WARP - usb_frame_time = get_ticks_per_sec(); - usb_bit_time = muldiv64(1, get_ticks_per_sec(), USB_HZ/1000); + usb_frame_time = NANOSECONDS_PER_SECOND; + usb_bit_time = NANOSECONDS_PER_SECOND / (USB_HZ / 1000); #else - usb_frame_time = muldiv64(1, get_ticks_per_sec(), 1000); - if (get_ticks_per_sec() >= USB_HZ) { - usb_bit_time = muldiv64(1, get_ticks_per_sec(), USB_HZ); + usb_frame_time = NANOSECONDS_PER_SECOND / 1000; + if (NANOSECONDS_PER_SECOND >= USB_HZ) { + usb_bit_time = NANOSECONDS_PER_SECOND / USB_HZ; } else { usb_bit_time = 1; } @@ -1883,6 +1894,9 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, usb_packet_init(&ohci->usb_packet); ohci->async_td = 0; + + ohci->eof_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + ohci_frame_boundary, ohci); } #define TYPE_PCI_OHCI "pci-ohci" @@ -1952,6 +1966,9 @@ static void usb_ohci_exit(PCIDevice *dev) if (!ohci->masterbus) { usb_bus_release(&s->bus); } + + timer_del(s->eof_timer); + timer_free(s->eof_timer); } static void usb_ohci_reset_pci(DeviceState *d) @@ -1960,7 +1977,7 @@ static void usb_ohci_reset_pci(DeviceState *d) OHCIPCIState *ohci = PCI_OHCI(dev); OHCIState *s = &ohci->state; - ohci_reset(s); + ohci_hard_reset(s); } #define TYPE_SYSBUS_OHCI "sysbus-ohci" @@ -1993,7 +2010,7 @@ static void usb_ohci_reset_sysbus(DeviceState *dev) OHCISysBusState *s = SYSBUS_OHCI(dev); OHCIState *ohci = &s->ohci; - ohci_reset(ohci); + ohci_hard_reset(ohci); } static Property ohci_pci_properties[] = { @@ -2017,23 +2034,13 @@ static bool ohci_eof_timer_needed(void *opaque) { OHCIState *ohci = opaque; - return ohci->eof_timer != NULL; -} - -static int ohci_eof_timer_pre_load(void *opaque) -{ - OHCIState *ohci = opaque; - - ohci_bus_start(ohci); - - return 0; + return timer_pending(ohci->eof_timer); } static const VMStateDescription vmstate_ohci_eof_timer = { .name = "ohci-core/eof-timer", .version_id = 1, .minimum_version_id = 1, - .pre_load = ohci_eof_timer_pre_load, .needed = ohci_eof_timer_needed, .fields = (VMStateField[]) { VMSTATE_TIMER_PTR(eof_timer, OHCIState), diff --git a/qemu/hw/usb/hcd-uhci.c b/qemu/hw/usb/hcd-uhci.c index 3f0ed6268..ca72a80f2 100644 --- a/qemu/hw/usb/hcd-uhci.c +++ b/qemu/hw/usb/hcd-uhci.c @@ -25,10 +25,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/usb.h" #include "hw/usb/uhci-regs.h" #include "hw/pci/pci.h" +#include "qapi/error.h" #include "qemu/timer.h" #include "qemu/iov.h" #include "sysemu/dma.h" @@ -401,7 +403,7 @@ static int uhci_post_load(void *opaque, int version_id) if (version_id < 2) { s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (get_ticks_per_sec() / FRAME_TIMER_FREQ); + (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ); } return 0; } @@ -443,7 +445,7 @@ static void uhci_port_write(void *opaque, hwaddr addr, /* start frame processing */ trace_usb_uhci_schedule_start(); s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (get_ticks_per_sec() / FRAME_TIMER_FREQ); + (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ); timer_mod(s->frame_timer, s->expire_time); s->status &= ~UHCI_STS_HCHALTED; } else if (!(val & UHCI_CMD_RS)) { @@ -772,8 +774,9 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, bool spd; bool queuing = (q != NULL); uint8_t pid = td->token & 0xff; - UHCIAsync *async = uhci_async_find_td(s, td_addr); + UHCIAsync *async; + async = uhci_async_find_td(s, td_addr); if (async) { if (uhci_queue_verify(async->queue, qh_addr, td, td_addr, queuing)) { assert(q == NULL || q == async->queue); @@ -812,6 +815,19 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, return TD_RESULT_NEXT_QH; } + switch (pid) { + case USB_TOKEN_OUT: + case USB_TOKEN_SETUP: + case USB_TOKEN_IN: + break; + default: + /* invalid pid : frame interrupted */ + s->status |= UHCI_STS_HCPERR; + s->cmd &= ~UHCI_CMD_RS; + uhci_update_irq(s); + return TD_RESULT_STOP_FRAME; + } + if (async) { if (queuing) { /* we are busy filling the queue, we are not prepared @@ -879,11 +895,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, break; default: - /* invalid pid : frame interrupted */ - uhci_async_free(async); - s->status |= UHCI_STS_HCPERR; - uhci_update_irq(s); - return TD_RESULT_STOP_FRAME; + abort(); /* Never to execute */ } if (async->packet.status == USB_RET_ASYNC) { @@ -1119,7 +1131,7 @@ static void uhci_frame_timer(void *opaque) UHCIState *s = opaque; uint64_t t_now, t_last_run; int i, frames; - const uint64_t frame_t = get_ticks_per_sec() / FRAME_TIMER_FREQ; + const uint64_t frame_t = NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ; s->completions_only = false; qemu_bh_cancel(s->bh); diff --git a/qemu/hw/usb/hcd-xhci.c b/qemu/hw/usb/hcd-xhci.c index c673bed4c..bcde8a2f4 100644 --- a/qemu/hw/usb/hcd-xhci.c +++ b/qemu/hw/usb/hcd-xhci.c @@ -18,6 +18,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/timer.h" #include "hw/usb.h" @@ -697,11 +698,13 @@ static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr, uint32_t *buf, size_t len) { int i; - uint32_t tmp[len / sizeof(uint32_t)]; + uint32_t tmp[5]; + uint32_t n = len / sizeof(uint32_t); assert((len % sizeof(uint32_t)) == 0); + assert(n <= ARRAY_SIZE(tmp)); - for (i = 0; i < (len / sizeof(uint32_t)); i++) { + for (i = 0; i < n; i++) { tmp[i] = cpu_to_le32(buf[i]); } pci_dma_write(PCI_DEVICE(xhci), addr, tmp, len); @@ -1453,9 +1456,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) t->running_retry = 0; killed = 1; } - if (t->trbs) { - g_free(t->trbs); - } + g_free(t->trbs); t->trbs = NULL; t->trb_count = t->trb_alloced = 0; @@ -2190,7 +2191,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, xfer->trbs = NULL; } if (!xfer->trbs) { - xfer->trbs = g_malloc(sizeof(XHCITRB) * length); + xfer->trbs = g_new(XHCITRB, length); xfer->trb_alloced = length; } xfer->trb_count = length; diff --git a/qemu/hw/usb/host-legacy.c b/qemu/hw/usb/host-legacy.c index 422ed9a65..3b57e21b5 100644 --- a/qemu/hw/usb/host-legacy.c +++ b/qemu/hw/usb/host-legacy.c @@ -30,6 +30,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/usb.h" #include "hw/usb/host.h" diff --git a/qemu/hw/usb/host-libusb.c b/qemu/hw/usb/host-libusb.c index 11429f5e7..6458a9448 100644 --- a/qemu/hw/usb/host-libusb.c +++ b/qemu/hw/usb/host-libusb.c @@ -33,9 +33,11 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include <poll.h> #include <libusb.h> +#include "qapi/error.h" #include "qemu-common.h" #include "monitor/monitor.h" #include "qemu/error-report.h" @@ -451,6 +453,7 @@ static void usb_host_req_complete_iso(struct libusb_transfer *transfer) } if (xfer->ring->ep->pid == USB_TOKEN_IN) { QTAILQ_INSERT_TAIL(&xfer->ring->copy, xfer, next); + usb_wakeup(xfer->ring->ep, 0); } else { QTAILQ_INSERT_TAIL(&xfer->ring->unused, xfer, next); } @@ -1239,7 +1242,7 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p, /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices * to work redirected to a not superspeed capable hcd */ - if (udev->speed == USB_SPEED_SUPER && + if ((udev->speedmask & USB_SPEED_MASK_SUPER) && !(udev->port->speedmask & USB_SPEED_MASK_SUPER) && request == 0x8006 && value == 0x100 && index == 0) { r->usb3ep0quirk = true; @@ -1429,7 +1432,7 @@ static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps, * still present in the first place. Attemping to contine where we * left off is impossible. * - * What we are going to to to here is emulate a surprise removal of + * What we are going to do here is emulate a surprise removal of * the usb device passed through, then kick host scan so the device * will get re-attached (and re-initialized by the guest) in case it * is still present. diff --git a/qemu/hw/usb/host-stub.c b/qemu/hw/usb/host-stub.c index 2eaaa8341..6ba65a1f6 100644 --- a/qemu/hw/usb/host-stub.c +++ b/qemu/hw/usb/host-stub.c @@ -30,6 +30,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/libhw.c b/qemu/hw/usb/libhw.c index 8df11c461..73cdf0c97 100644 --- a/qemu/hw/usb/libhw.c +++ b/qemu/hw/usb/libhw.c @@ -19,6 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "hw/hw.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/quirks.c b/qemu/hw/usb/quirks.c index a761a9603..38a9c5634 100644 --- a/qemu/hw/usb/quirks.c +++ b/qemu/hw/usb/quirks.c @@ -12,6 +12,7 @@ * (at your option) any later version. */ +#include "qemu/osdep.h" #include "quirks.h" #include "hw/usb.h" diff --git a/qemu/hw/usb/redirect.c b/qemu/hw/usb/redirect.c index 25df25fd0..8d8054037 100644 --- a/qemu/hw/usb/redirect.c +++ b/qemu/hw/usb/redirect.c @@ -25,6 +25,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" @@ -33,14 +35,14 @@ #include "qemu/iov.h" #include "sysemu/char.h" -#include <dirent.h> -#include <sys/ioctl.h> -#include <signal.h> #include <usbredirparser.h> #include <usbredirfilter.h> #include "hw/usb.h" +/* ERROR is defined below. Remove any previous definition. */ +#undef ERROR + #define MAX_ENDPOINTS 32 #define NO_INTERFACE_INFO 255 /* Valid interface_count always <= 32 */ #define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) @@ -324,7 +326,7 @@ static void packet_id_queue_add(struct PacketIdQueue *q, uint64_t id) DPRINTF("adding packet id %"PRIu64" to %s queue\n", id, q->name); - e = g_malloc0(sizeof(struct PacketIdQueueEntry)); + e = g_new0(struct PacketIdQueueEntry, 1); e->id = id; QTAILQ_INSERT_TAIL(&q->head, e, next); q->size++; @@ -448,7 +450,7 @@ static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, return p; } -static void bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len, +static int bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len, uint8_t status, uint8_t ep, void *free_on_destroy) { struct buf_packet *bufp; @@ -465,12 +467,12 @@ static void bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len, if (dev->endpoint[EP2I(ep)].bufpq_size > dev->endpoint[EP2I(ep)].bufpq_target_size) { free(data); - return; + return -1; } dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; } - bufp = g_malloc(sizeof(struct buf_packet)); + bufp = g_new(struct buf_packet, 1); bufp->data = data; bufp->len = len; bufp->offset = 0; @@ -478,6 +480,7 @@ static void bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len, bufp->free_on_destroy = free_on_destroy; QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); dev->endpoint[EP2I(ep)].bufpq_size++; + return 0; } static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp, @@ -2083,13 +2086,17 @@ static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, status = usb_redir_success; free_on_destroy = NULL; for (i = 0; i < data_len; i += len) { + int r; if (len >= (data_len - i)) { len = data_len - i; status = buffered_bulk_packet->status; free_on_destroy = data; } /* bufp_alloc also adds the packet to the ep queue */ - bufp_alloc(dev, data + i, len, status, ep, free_on_destroy); + r = bufp_alloc(dev, data + i, len, status, ep, free_on_destroy); + if (r) { + break; + } } if (dev->endpoint[EP2I(ep)].pending_async_packet) { @@ -2236,7 +2243,7 @@ static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused) endp->bufpq_size = qemu_get_be32(f); for (i = 0; i < endp->bufpq_size; i++) { - bufp = g_malloc(sizeof(struct buf_packet)); + bufp = g_new(struct buf_packet, 1); bufp->len = qemu_get_be32(f); bufp->status = qemu_get_be32(f); bufp->offset = 0; diff --git a/qemu/hw/usb/tusb6010.c b/qemu/hw/usb/tusb6010.c new file mode 100644 index 000000000..8f593a6fd --- /dev/null +++ b/qemu/hw/usb/tusb6010.c @@ -0,0 +1,817 @@ +/* + * Texas Instruments TUSB6010 emulation. + * Based on reverse-engineering of a linux driver. + * + * Copyright (C) 2008 Nokia Corporation + * Written by Andrzej Zaborowski <andrew@openedhand.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/timer.h" +#include "hw/usb.h" +#include "hw/arm/omap.h" +#include "hw/irq.h" +#include "hw/devices.h" +#include "hw/sysbus.h" + +#define TYPE_TUSB6010 "tusb6010" +#define TUSB(obj) OBJECT_CHECK(TUSBState, (obj), TYPE_TUSB6010) + +typedef struct TUSBState { + SysBusDevice parent_obj; + + MemoryRegion iomem[2]; + qemu_irq irq; + MUSBState *musb; + QEMUTimer *otg_timer; + QEMUTimer *pwr_timer; + + int power; + uint32_t scratch; + uint16_t test_reset; + uint32_t prcm_config; + uint32_t prcm_mngmt; + uint16_t otg_status; + uint32_t dev_config; + int host_mode; + uint32_t intr; + uint32_t intr_ok; + uint32_t mask; + uint32_t usbip_intr; + uint32_t usbip_mask; + uint32_t gpio_intr; + uint32_t gpio_mask; + uint32_t gpio_config; + uint32_t dma_intr; + uint32_t dma_mask; + uint32_t dma_map; + uint32_t dma_config; + uint32_t ep0_config; + uint32_t rx_config[15]; + uint32_t tx_config[15]; + uint32_t wkup_mask; + uint32_t pullup[2]; + uint32_t control_config; + uint32_t otg_timer_val; +} TUSBState; + +#define TUSB_DEVCLOCK 60000000 /* 60 MHz */ + +#define TUSB_VLYNQ_CTRL 0x004 + +/* Mentor Graphics OTG core registers. */ +#define TUSB_BASE_OFFSET 0x400 + +/* FIFO registers, 32-bit. */ +#define TUSB_FIFO_BASE 0x600 + +/* Device System & Control registers, 32-bit. */ +#define TUSB_SYS_REG_BASE 0x800 + +#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000) +#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16) +#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15) +#define TUSB_DEV_CONF_SOFT_ID (1 << 1) +#define TUSB_DEV_CONF_ID_SEL (1 << 0) + +#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004) +#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008) +#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24) +#define TUSB_PHY_OTG_CTRL_O_ID_PULLUP (1 << 23) +#define TUSB_PHY_OTG_CTRL_O_VBUS_DET_EN (1 << 19) +#define TUSB_PHY_OTG_CTRL_O_SESS_END_EN (1 << 18) +#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17) +#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16) +#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15) +#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14) +#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13) +#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12) +#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11) +#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10) +#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9) +#define TUSB_PHY_OTG_CTRL_PHYREF_CLK(v) (((v) & 3) << 7) +#define TUSB_PHY_OTG_CTRL_PD (1 << 6) +#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5) +#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4) +#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3) +#define TUSB_PHY_OTG_CTRL_RESET (1 << 2) +#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1) +#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0) + +/* OTG status register */ +#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c) +#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8) +#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7) +#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6) +#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5) +#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4) +#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3) +#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2) +#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0) +#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1) +#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0) + +#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010) +#define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31) +#define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff) +#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014) + +/* PRCM configuration register */ +#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018) +#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24) +#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16) + +/* PRCM management register */ +#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c) +#define TUSB_PRCM_MNGMT_SRP_FIX_TMR(v) (((v) & 0xf) << 25) +#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24) +#define TUSB_PRCM_MNGMT_VBUS_VAL_TMR(v) (((v) & 0xf) << 20) +#define TUSB_PRCM_MNGMT_VBUS_VAL_FLT_EN (1 << 19) +#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18) +#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17) +#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10) +#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9) +#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8) +#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4) +#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3) +#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2) +#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1) +#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0) + +/* Wake-up source clear and mask registers */ +#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020) +#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028) +#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c) +#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13) +#define TUSB_PRCM_WGPIO_7 (1 << 12) +#define TUSB_PRCM_WGPIO_6 (1 << 11) +#define TUSB_PRCM_WGPIO_5 (1 << 10) +#define TUSB_PRCM_WGPIO_4 (1 << 9) +#define TUSB_PRCM_WGPIO_3 (1 << 8) +#define TUSB_PRCM_WGPIO_2 (1 << 7) +#define TUSB_PRCM_WGPIO_1 (1 << 6) +#define TUSB_PRCM_WGPIO_0 (1 << 5) +#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */ +#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */ +#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */ +#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */ +#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */ + +#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030) +#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034) +#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038) +#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c) +#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040) +#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044) +#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048) +#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c) +#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050) +#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054) +#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058) +#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c) +#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060) +#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064) +#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068) +#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c) + +/* NOR flash interrupt source registers */ +#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070) +#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074) +#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078) +#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c) +#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24) +#define TUSB_INT_SRC_USB_IP_CORE (1 << 17) +#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16) +#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15) +#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14) +#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13) +#define TUSB_INT_SRC_DEV_READY (1 << 12) +#define TUSB_INT_SRC_USB_IP_TX (1 << 9) +#define TUSB_INT_SRC_USB_IP_RX (1 << 8) +#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7) +#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6) +#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5) +#define TUSB_INT_SRC_USB_IP_CONN (1 << 4) +#define TUSB_INT_SRC_USB_IP_SOF (1 << 3) +#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2) +#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1) +#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0) + +#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080) +#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084) +#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100) +#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104) +#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108) +#define TUSB_EP_IN_SIZE (TUSB_SYS_REG_BASE + 0x10c) +#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148) +#define TUSB_EP_OUT_SIZE (TUSB_SYS_REG_BASE + 0x14c) +#define TUSB_EP_MAX_PACKET_SIZE_OFFSET (TUSB_SYS_REG_BASE + 0x188) +#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4) +#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8) +#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8) + +#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8) +#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc) + +/* Device System & Control register bitfields */ +#define TUSB_INT_CTRL_CONF_INT_RLCYC(v) (((v) & 0x7) << 18) +#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17) +#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16) +#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24) +#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26) +#define TUSB_DMA_REQ_CONF_DMA_RQ_EN(v) (((v) & 0x3f) << 20) +#define TUSB_DMA_REQ_CONF_DMA_RQ_ASR(v) (((v) & 0xf) << 16) +#define TUSB_EP0_CONFIG_SW_EN (1 << 8) +#define TUSB_EP0_CONFIG_DIR_TX (1 << 7) +#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f) +#define TUSB_EP_CONFIG_SW_EN (1 << 31) +#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff) +#define TUSB_PROD_TEST_RESET_VAL 0xa596 + +static void tusb_intr_update(TUSBState *s) +{ + if (s->control_config & TUSB_INT_CTRL_CONF_INT_POLARITY) + qemu_set_irq(s->irq, s->intr & ~s->mask & s->intr_ok); + else + qemu_set_irq(s->irq, (!(s->intr & ~s->mask)) & s->intr_ok); +} + +static void tusb_usbip_intr_update(TUSBState *s) +{ + /* TX interrupt in the MUSB */ + if (s->usbip_intr & 0x0000ffff & ~s->usbip_mask) + s->intr |= TUSB_INT_SRC_USB_IP_TX; + else + s->intr &= ~TUSB_INT_SRC_USB_IP_TX; + + /* RX interrupt in the MUSB */ + if (s->usbip_intr & 0xffff0000 & ~s->usbip_mask) + s->intr |= TUSB_INT_SRC_USB_IP_RX; + else + s->intr &= ~TUSB_INT_SRC_USB_IP_RX; + + /* XXX: What about TUSB_INT_SRC_USB_IP_CORE? */ + + tusb_intr_update(s); +} + +static void tusb_dma_intr_update(TUSBState *s) +{ + if (s->dma_intr & ~s->dma_mask) + s->intr |= TUSB_INT_SRC_TXRX_DMA_DONE; + else + s->intr &= ~TUSB_INT_SRC_TXRX_DMA_DONE; + + tusb_intr_update(s); +} + +static void tusb_gpio_intr_update(TUSBState *s) +{ + /* TODO: How is this signalled? */ +} + +static uint32_t tusb_async_readb(void *opaque, hwaddr addr) +{ + TUSBState *s = (TUSBState *) opaque; + + switch (addr & 0xfff) { + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + return musb_read[0](s->musb, addr & 0x1ff); + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + return musb_read[0](s->musb, 0x20 + ((addr >> 3) & 0x3c)); + } + + printf("%s: unknown register at %03x\n", + __FUNCTION__, (int) (addr & 0xfff)); + return 0; +} + +static uint32_t tusb_async_readh(void *opaque, hwaddr addr) +{ + TUSBState *s = (TUSBState *) opaque; + + switch (addr & 0xfff) { + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + return musb_read[1](s->musb, addr & 0x1ff); + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + return musb_read[1](s->musb, 0x20 + ((addr >> 3) & 0x3c)); + } + + printf("%s: unknown register at %03x\n", + __FUNCTION__, (int) (addr & 0xfff)); + return 0; +} + +static uint32_t tusb_async_readw(void *opaque, hwaddr addr) +{ + TUSBState *s = (TUSBState *) opaque; + int offset = addr & 0xfff; + int epnum; + uint32_t ret; + + switch (offset) { + case TUSB_DEV_CONF: + return s->dev_config; + + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + return musb_read[2](s->musb, offset & 0x1ff); + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + return musb_read[2](s->musb, 0x20 + ((addr >> 3) & 0x3c)); + + case TUSB_PHY_OTG_CTRL_ENABLE: + case TUSB_PHY_OTG_CTRL: + return 0x00; /* TODO */ + + case TUSB_DEV_OTG_STAT: + ret = s->otg_status; +#if 0 + if (!(s->prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) + ret &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; +#endif + return ret; + case TUSB_DEV_OTG_TIMER: + return s->otg_timer_val; + + case TUSB_PRCM_REV: + return 0x20; + case TUSB_PRCM_CONF: + return s->prcm_config; + case TUSB_PRCM_MNGMT: + return s->prcm_mngmt; + case TUSB_PRCM_WAKEUP_SOURCE: + case TUSB_PRCM_WAKEUP_CLEAR: /* TODO: What does this one return? */ + return 0x00000000; + case TUSB_PRCM_WAKEUP_MASK: + return s->wkup_mask; + + case TUSB_PULLUP_1_CTRL: + return s->pullup[0]; + case TUSB_PULLUP_2_CTRL: + return s->pullup[1]; + + case TUSB_INT_CTRL_REV: + return 0x20; + case TUSB_INT_CTRL_CONF: + return s->control_config; + + case TUSB_USBIP_INT_SRC: + case TUSB_USBIP_INT_SET: /* TODO: What do these two return? */ + case TUSB_USBIP_INT_CLEAR: + return s->usbip_intr; + case TUSB_USBIP_INT_MASK: + return s->usbip_mask; + + case TUSB_DMA_INT_SRC: + case TUSB_DMA_INT_SET: /* TODO: What do these two return? */ + case TUSB_DMA_INT_CLEAR: + return s->dma_intr; + case TUSB_DMA_INT_MASK: + return s->dma_mask; + + case TUSB_GPIO_INT_SRC: /* TODO: What do these two return? */ + case TUSB_GPIO_INT_SET: + case TUSB_GPIO_INT_CLEAR: + return s->gpio_intr; + case TUSB_GPIO_INT_MASK: + return s->gpio_mask; + + case TUSB_INT_SRC: + case TUSB_INT_SRC_SET: /* TODO: What do these two return? */ + case TUSB_INT_SRC_CLEAR: + return s->intr; + case TUSB_INT_MASK: + return s->mask; + + case TUSB_GPIO_REV: + return 0x30; + case TUSB_GPIO_CONF: + return s->gpio_config; + + case TUSB_DMA_CTRL_REV: + return 0x30; + case TUSB_DMA_REQ_CONF: + return s->dma_config; + case TUSB_EP0_CONF: + return s->ep0_config; + case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): + epnum = (offset - TUSB_EP_IN_SIZE) >> 2; + return s->tx_config[epnum]; + case TUSB_DMA_EP_MAP: + return s->dma_map; + case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): + epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; + return s->rx_config[epnum]; + case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... + (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): + return 0x00000000; /* TODO */ + case TUSB_WAIT_COUNT: + return 0x00; /* TODO */ + + case TUSB_SCRATCH_PAD: + return s->scratch; + + case TUSB_PROD_TEST_RESET: + return s->test_reset; + + /* DIE IDs */ + case TUSB_DIDR1_LO: + return 0xa9453c59; + case TUSB_DIDR1_HI: + return 0x54059adf; + } + + printf("%s: unknown register at %03x\n", __FUNCTION__, offset); + return 0; +} + +static void tusb_async_writeb(void *opaque, hwaddr addr, + uint32_t value) +{ + TUSBState *s = (TUSBState *) opaque; + + switch (addr & 0xfff) { + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + musb_write[0](s->musb, addr & 0x1ff, value); + break; + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); + break; + + default: + printf("%s: unknown register at %03x\n", + __FUNCTION__, (int) (addr & 0xfff)); + return; + } +} + +static void tusb_async_writeh(void *opaque, hwaddr addr, + uint32_t value) +{ + TUSBState *s = (TUSBState *) opaque; + + switch (addr & 0xfff) { + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + musb_write[1](s->musb, addr & 0x1ff, value); + break; + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); + break; + + default: + printf("%s: unknown register at %03x\n", + __FUNCTION__, (int) (addr & 0xfff)); + return; + } +} + +static void tusb_async_writew(void *opaque, hwaddr addr, + uint32_t value) +{ + TUSBState *s = (TUSBState *) opaque; + int offset = addr & 0xfff; + int epnum; + + switch (offset) { + case TUSB_VLYNQ_CTRL: + break; + + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + musb_write[2](s->musb, offset & 0x1ff, value); + break; + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + musb_write[2](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); + break; + + case TUSB_DEV_CONF: + s->dev_config = value; + s->host_mode = (value & TUSB_DEV_CONF_USB_HOST_MODE); + if (value & TUSB_DEV_CONF_PROD_TEST_MODE) + hw_error("%s: Product Test mode not allowed\n", __FUNCTION__); + break; + + case TUSB_PHY_OTG_CTRL_ENABLE: + case TUSB_PHY_OTG_CTRL: + return; /* TODO */ + case TUSB_DEV_OTG_TIMER: + s->otg_timer_val = value; + if (value & TUSB_DEV_OTG_TIMER_ENABLE) + timer_mod(s->otg_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + muldiv64(TUSB_DEV_OTG_TIMER_VAL(value), + NANOSECONDS_PER_SECOND, TUSB_DEVCLOCK)); + else + timer_del(s->otg_timer); + break; + + case TUSB_PRCM_CONF: + s->prcm_config = value; + break; + case TUSB_PRCM_MNGMT: + s->prcm_mngmt = value; + break; + case TUSB_PRCM_WAKEUP_CLEAR: + break; + case TUSB_PRCM_WAKEUP_MASK: + s->wkup_mask = value; + break; + + case TUSB_PULLUP_1_CTRL: + s->pullup[0] = value; + break; + case TUSB_PULLUP_2_CTRL: + s->pullup[1] = value; + break; + case TUSB_INT_CTRL_CONF: + s->control_config = value; + tusb_intr_update(s); + break; + + case TUSB_USBIP_INT_SET: + s->usbip_intr |= value; + tusb_usbip_intr_update(s); + break; + case TUSB_USBIP_INT_CLEAR: + s->usbip_intr &= ~value; + tusb_usbip_intr_update(s); + musb_core_intr_clear(s->musb, ~value); + break; + case TUSB_USBIP_INT_MASK: + s->usbip_mask = value; + tusb_usbip_intr_update(s); + break; + + case TUSB_DMA_INT_SET: + s->dma_intr |= value; + tusb_dma_intr_update(s); + break; + case TUSB_DMA_INT_CLEAR: + s->dma_intr &= ~value; + tusb_dma_intr_update(s); + break; + case TUSB_DMA_INT_MASK: + s->dma_mask = value; + tusb_dma_intr_update(s); + break; + + case TUSB_GPIO_INT_SET: + s->gpio_intr |= value; + tusb_gpio_intr_update(s); + break; + case TUSB_GPIO_INT_CLEAR: + s->gpio_intr &= ~value; + tusb_gpio_intr_update(s); + break; + case TUSB_GPIO_INT_MASK: + s->gpio_mask = value; + tusb_gpio_intr_update(s); + break; + + case TUSB_INT_SRC_SET: + s->intr |= value; + tusb_intr_update(s); + break; + case TUSB_INT_SRC_CLEAR: + s->intr &= ~value; + tusb_intr_update(s); + break; + case TUSB_INT_MASK: + s->mask = value; + tusb_intr_update(s); + break; + + case TUSB_GPIO_CONF: + s->gpio_config = value; + break; + case TUSB_DMA_REQ_CONF: + s->dma_config = value; + break; + case TUSB_EP0_CONF: + s->ep0_config = value & 0x1ff; + musb_set_size(s->musb, 0, TUSB_EP0_CONFIG_XFR_SIZE(value), + value & TUSB_EP0_CONFIG_DIR_TX); + break; + case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): + epnum = (offset - TUSB_EP_IN_SIZE) >> 2; + s->tx_config[epnum] = value; + musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 1); + break; + case TUSB_DMA_EP_MAP: + s->dma_map = value; + break; + case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): + epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; + s->rx_config[epnum] = value; + musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 0); + break; + case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... + (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): + return; /* TODO */ + case TUSB_WAIT_COUNT: + return; /* TODO */ + + case TUSB_SCRATCH_PAD: + s->scratch = value; + break; + + case TUSB_PROD_TEST_RESET: + s->test_reset = value; + break; + + default: + printf("%s: unknown register at %03x\n", __FUNCTION__, offset); + return; + } +} + +static const MemoryRegionOps tusb_async_ops = { + .old_mmio = { + .read = { tusb_async_readb, tusb_async_readh, tusb_async_readw, }, + .write = { tusb_async_writeb, tusb_async_writeh, tusb_async_writew, }, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void tusb_otg_tick(void *opaque) +{ + TUSBState *s = (TUSBState *) opaque; + + s->otg_timer_val = 0; + s->intr |= TUSB_INT_SRC_OTG_TIMEOUT; + tusb_intr_update(s); +} + +static void tusb_power_tick(void *opaque) +{ + TUSBState *s = (TUSBState *) opaque; + + if (s->power) { + s->intr_ok = ~0; + tusb_intr_update(s); + } +} + +static void tusb_musb_core_intr(void *opaque, int source, int level) +{ + TUSBState *s = (TUSBState *) opaque; + uint16_t otg_status = s->otg_status; + + switch (source) { + case musb_set_vbus: + if (level) + otg_status |= TUSB_DEV_OTG_STAT_VBUS_VALID; + else + otg_status &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; + + /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN set? */ + /* XXX: only if TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN set? */ + if (s->otg_status != otg_status) { + s->otg_status = otg_status; + s->intr |= TUSB_INT_SRC_VBUS_SENSE_CHNG; + tusb_intr_update(s); + } + break; + + case musb_set_session: + /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN set? */ + /* XXX: only if TUSB_PRCM_MNGMT_OTG_SESS_END_EN set? */ + if (level) { + s->otg_status |= TUSB_DEV_OTG_STAT_SESS_VALID; + s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_END; + } else { + s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_VALID; + s->otg_status |= TUSB_DEV_OTG_STAT_SESS_END; + } + + /* XXX: some IRQ or anything? */ + break; + + case musb_irq_tx: + case musb_irq_rx: + s->usbip_intr = musb_core_intr_get(s->musb); + /* Fall through. */ + default: + if (level) + s->intr |= 1 << source; + else + s->intr &= ~(1 << source); + tusb_intr_update(s); + break; + } +} + +static void tusb6010_power(TUSBState *s, int on) +{ + if (!on) { + s->power = 0; + } else if (!s->power && on) { + s->power = 1; + /* Pull the interrupt down after TUSB6010 comes up. */ + s->intr_ok = 0; + tusb_intr_update(s); + timer_mod(s->pwr_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + NANOSECONDS_PER_SECOND / 2); + } +} + +static void tusb6010_irq(void *opaque, int source, int level) +{ + if (source) { + tusb_musb_core_intr(opaque, source - 1, level); + } else { + tusb6010_power(opaque, level); + } +} + +static void tusb6010_reset(DeviceState *dev) +{ + TUSBState *s = TUSB(dev); + int i; + + s->test_reset = TUSB_PROD_TEST_RESET_VAL; + s->host_mode = 0; + s->dev_config = 0; + s->otg_status = 0; /* !TUSB_DEV_OTG_STAT_ID_STATUS means host mode */ + s->power = 0; + s->mask = 0xffffffff; + s->intr = 0x00000000; + s->otg_timer_val = 0; + s->scratch = 0; + s->prcm_config = 0; + s->prcm_mngmt = 0; + s->intr_ok = 0; + s->usbip_intr = 0; + s->usbip_mask = 0; + s->gpio_intr = 0; + s->gpio_mask = 0; + s->gpio_config = 0; + s->dma_intr = 0; + s->dma_mask = 0; + s->dma_map = 0; + s->dma_config = 0; + s->ep0_config = 0; + s->wkup_mask = 0; + s->pullup[0] = s->pullup[1] = 0; + s->control_config = 0; + for (i = 0; i < 15; i++) { + s->rx_config[i] = s->tx_config[i] = 0; + } + musb_reset(s->musb); +} + +static int tusb6010_init(SysBusDevice *sbd) +{ + DeviceState *dev = DEVICE(sbd); + TUSBState *s = TUSB(dev); + + s->otg_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tusb_otg_tick, s); + s->pwr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tusb_power_tick, s); + memory_region_init_io(&s->iomem[1], OBJECT(s), &tusb_async_ops, s, + "tusb-async", UINT32_MAX); + sysbus_init_mmio(sbd, &s->iomem[0]); + sysbus_init_mmio(sbd, &s->iomem[1]); + sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_in(dev, tusb6010_irq, musb_irq_max + 1); + s->musb = musb_init(dev, 1); + return 0; +} + +static void tusb6010_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = tusb6010_init; + dc->reset = tusb6010_reset; +} + +static const TypeInfo tusb6010_info = { + .name = TYPE_TUSB6010, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TUSBState), + .class_init = tusb6010_class_init, +}; + +static void tusb6010_register_types(void) +{ + type_register_static(&tusb6010_info); +} + +type_init(tusb6010_register_types) |