Patchwork [6/8] hid: Change idle handling to use a timer

login
register
mail settings
Submitter Hans de Goede
Date Nov. 6, 2012, 2:08 p.m.
Message ID <1352210901-1923-7-git-send-email-hdegoede@redhat.com>
Download mbox | patch
Permalink /patch/197483/
State New
Headers show

Comments

Hans de Goede - Nov. 6, 2012, 2:08 p.m.
This is a preparation patch for moving away from a polling model into async
packet handling for usb-hid.

As an added advantage this remove 1000 calls / sec to
qemu_get_clock_ns(vm_clock) while idle-time is set to 0.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 hw/hid.c         | 43 +++++++++++++++++++++++++++++++++++++------
 hw/hid.h         |  5 +++--
 hw/usb/dev-hid.c |  8 +++-----
 3 files changed, 43 insertions(+), 13 deletions(-)

Patch

diff --git a/hw/hid.c b/hw/hid.c
index 03761ab..f8c5393 100644
--- a/hw/hid.c
+++ b/hw/hid.c
@@ -71,12 +71,38 @@  static const uint8_t hid_usage_keys[0x100] = {
 
 bool hid_has_events(HIDState *hs)
 {
-    return hs->n > 0;
+    return hs->n > 0 || hs->idle_pending;
 }
 
-void hid_set_next_idle(HIDState *hs, int64_t curtime)
+static void hid_idle_timer(void *opaque)
 {
-    hs->next_idle_clock = curtime + (get_ticks_per_sec() * hs->idle * 4) / 1000;
+    HIDState *hs = opaque;
+
+    hs->idle_pending = true;
+    hs->event(hs);
+}
+
+static void hid_del_idle_timer(HIDState *hs)
+{
+    if (hs->idle_timer) {
+        qemu_del_timer(hs->idle_timer);
+        qemu_free_timer(hs->idle_timer);
+        hs->idle_timer = NULL;
+    }
+}
+
+void hid_set_next_idle(HIDState *hs)
+{
+    if (hs->idle) {
+        uint64_t expire_time = qemu_get_clock_ns(vm_clock) +
+                               get_ticks_per_sec() * hs->idle * 4 / 1000;
+        if (!hs->idle_timer) {
+            hs->idle_timer = qemu_new_timer_ns(vm_clock, hid_idle_timer, hs);
+        }
+        qemu_mod_timer_ns(hs->idle_timer, expire_time);
+    } else {
+        hid_del_idle_timer(hs);
+    }
 }
 
 static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
@@ -232,6 +258,8 @@  int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
     int index;
     HIDPointerEvent *e;
 
+    hs->idle_pending = false;
+
     hid_pointer_activate(hs);
 
     /* When the buffer is empty, return the last event.  Relative
@@ -319,6 +347,8 @@  int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
 
 int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
 {
+    hs->idle_pending = false;
+
     if (len < 2) {
         return 0;
     }
@@ -377,6 +407,8 @@  void hid_reset(HIDState *hs)
     hs->n = 0;
     hs->protocol = 1;
     hs->idle = 0;
+    hs->idle_pending = false;
+    hid_del_idle_timer(hs);
 }
 
 void hid_free(HIDState *hs)
@@ -390,6 +422,7 @@  void hid_free(HIDState *hs)
         qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
         break;
     }
+    hid_del_idle_timer(hs);
 }
 
 void hid_init(HIDState *hs, int kind, HIDEventFunc event)
@@ -412,9 +445,7 @@  static int hid_post_load(void *opaque, int version_id)
 {
     HIDState *s = opaque;
 
-    if (s->idle) {
-        hid_set_next_idle(s, qemu_get_clock_ns(vm_clock));
-    }
+    hid_set_next_idle(s);
     return 0;
 }
 
diff --git a/hw/hid.h b/hw/hid.h
index 5315cf7..1cae633 100644
--- a/hw/hid.h
+++ b/hw/hid.h
@@ -43,7 +43,8 @@  struct HIDState {
     int kind;
     int32_t protocol;
     uint8_t idle;
-    int64_t next_idle_clock;
+    bool idle_pending;
+    QEMUTimer *idle_timer;
     HIDEventFunc event;
 };
 
@@ -52,7 +53,7 @@  void hid_reset(HIDState *hs);
 void hid_free(HIDState *hs);
 
 bool hid_has_events(HIDState *hs);
-void hid_set_next_idle(HIDState *hs, int64_t curtime);
+void hid_set_next_idle(HIDState *hs);
 void hid_pointer_activate(HIDState *hs);
 int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len);
 int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len);
diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index 55266b1..aa59ec4 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -439,7 +439,7 @@  static void usb_hid_handle_control(USBDevice *dev, USBPacket *p,
         break;
     case SET_IDLE:
         hs->idle = (uint8_t) (value >> 8);
-        hid_set_next_idle(hs, qemu_get_clock_ns(vm_clock));
+        hid_set_next_idle(hs);
         if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
             hid_pointer_activate(hs);
         }
@@ -461,16 +461,14 @@  static void usb_hid_handle_data(USBDevice *dev, USBPacket *p)
     switch (p->pid) {
     case USB_TOKEN_IN:
         if (p->ep->nr == 1) {
-            int64_t curtime = qemu_get_clock_ns(vm_clock);
             if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
                 hid_pointer_activate(hs);
             }
-            if (!hid_has_events(hs) &&
-                (!hs->idle || hs->next_idle_clock - curtime > 0)) {
+            if (!hid_has_events(hs)) {
                 p->status = USB_RET_NAK;
                 return;
             }
-            hid_set_next_idle(hs, curtime);
+            hid_set_next_idle(hs);
             if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
                 len = hid_pointer_poll(hs, buf, p->iov.size);
             } else if (hs->kind == HID_KEYBOARD) {