+static bool usb_hub_port_change(USBHubPort *port, uint16_t status)
+{
+ bool notify = false;
+
+ if (status & 0x1f) {
+ port->wPortChange |= status;
+ notify = true;
+ }
+ return notify;
+}
+
+static bool usb_hub_port_set(USBHubPort *port, uint16_t status)
+{
+ if (port->wPortStatus & status) {
+ return false;
+ }
+ port->wPortStatus |= status;
+ return usb_hub_port_change(port, status);
+}
+
+static bool usb_hub_port_clear(USBHubPort *port, uint16_t status)
+{
+ if (!(port->wPortStatus & status)) {
+ return false;
+ }
+ port->wPortStatus &= ~status;
+ return usb_hub_port_change(port, status);
+}
+
+static bool usb_hub_port_update(USBHubPort *port)
+{
+ bool notify = false;
+
+ if (port->port.dev && port->port.dev->attached) {
+ notify = usb_hub_port_set(port, PORT_STAT_CONNECTION);
+ if (port->port.dev->speed == USB_SPEED_LOW) {
+ usb_hub_port_set(port, PORT_STAT_LOW_SPEED);
+ } else {
+ usb_hub_port_clear(port, PORT_STAT_LOW_SPEED);
+ }
+ }
+ return notify;
+}
+
+static void usb_hub_port_update_timer(void *opaque)
+{
+ USBHubState *s = opaque;
+ bool notify = false;
+ int i;
+
+ for (i = 0; i < s->num_ports; i++) {
+ notify |= usb_hub_port_update(&s->ports[i]);
+ }
+ if (notify) {
+ usb_wakeup(s->intr, 0);
+ }
+}
+