Merge branches 'battery-2.6.34', 'bugzilla-10805', 'bugzilla-14668', 'bugzilla-531916...
[sfrench/cifs-2.6.git] / drivers / platform / x86 / sony-laptop.c
index 3f71a605a49243e6a0ca121c5d5ec57e2a8e6757..5a3d8514c66dafbd57c4d1900151626a55550ae5 100644 (file)
@@ -145,7 +145,7 @@ struct sony_laptop_input_s {
        struct input_dev        *key_dev;
        struct kfifo            fifo;
        spinlock_t              fifo_lock;
-       struct workqueue_struct *wq;
+       struct timer_list       release_key_timer;
 };
 
 static struct sony_laptop_input_s sony_laptop_input = {
@@ -299,20 +299,26 @@ static int sony_laptop_input_keycode_map[] = {
 };
 
 /* release buttons after a short delay if pressed */
-static void do_sony_laptop_release_key(struct work_struct *work)
+static void do_sony_laptop_release_key(unsigned long unused)
 {
        struct sony_laptop_keypress kp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sony_laptop_input.fifo_lock, flags);
 
-       while (kfifo_out_locked(&sony_laptop_input.fifo, (unsigned char *)&kp,
-                       sizeof(kp), &sony_laptop_input.fifo_lock)
-                       == sizeof(kp)) {
-               msleep(10);
+       if (kfifo_out(&sony_laptop_input.fifo,
+                     (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) {
                input_report_key(kp.dev, kp.key, 0);
                input_sync(kp.dev);
        }
+
+       /* If there is something in the fifo schedule next release. */
+       if (kfifo_len(&sony_laptop_input.fifo) != 0)
+               mod_timer(&sony_laptop_input.release_key_timer,
+                         jiffies + msecs_to_jiffies(10));
+
+       spin_unlock_irqrestore(&sony_laptop_input.fifo_lock, flags);
 }
-static DECLARE_WORK(sony_laptop_release_key_work,
-               do_sony_laptop_release_key);
 
 /* forward event to the input subsystem */
 static void sony_laptop_report_input_event(u8 event)
@@ -366,13 +372,13 @@ static void sony_laptop_report_input_event(u8 event)
                /* we emit the scancode so we can always remap the key */
                input_event(kp.dev, EV_MSC, MSC_SCAN, event);
                input_sync(kp.dev);
-               kfifo_in_locked(&sony_laptop_input.fifo,
-                         (unsigned char *)&kp, sizeof(kp),
-                         &sony_laptop_input.fifo_lock);
 
-               if (!work_pending(&sony_laptop_release_key_work))
-                       queue_work(sony_laptop_input.wq,
-                                       &sony_laptop_release_key_work);
+               /* schedule key release */
+               kfifo_in_locked(&sony_laptop_input.fifo,
+                               (unsigned char *)&kp, sizeof(kp),
+                               &sony_laptop_input.fifo_lock);
+               mod_timer(&sony_laptop_input.release_key_timer,
+                         jiffies + msecs_to_jiffies(10));
        } else
                dprintk("unknown input event %.2x\n", event);
 }
@@ -390,27 +396,21 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
 
        /* kfifo */
        spin_lock_init(&sony_laptop_input.fifo_lock);
-       error =
-        kfifo_alloc(&sony_laptop_input.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);
+       error = kfifo_alloc(&sony_laptop_input.fifo,
+                           SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);
        if (error) {
                printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
                goto err_dec_users;
        }
 
-       /* init workqueue */
-       sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop");
-       if (!sony_laptop_input.wq) {
-               printk(KERN_ERR DRV_PFX
-                               "Unable to create workqueue.\n");
-               error = -ENXIO;
-               goto err_free_kfifo;
-       }
+       setup_timer(&sony_laptop_input.release_key_timer,
+                   do_sony_laptop_release_key, 0);
 
        /* input keys */
        key_dev = input_allocate_device();
        if (!key_dev) {
                error = -ENOMEM;
-               goto err_destroy_wq;
+               goto err_free_kfifo;
        }
 
        key_dev->name = "Sony Vaio Keys";
@@ -419,18 +419,15 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
        key_dev->dev.parent = &acpi_device->dev;
 
        /* Initialize the Input Drivers: special keys */
-       set_bit(EV_KEY, key_dev->evbit);
-       set_bit(EV_MSC, key_dev->evbit);
-       set_bit(MSC_SCAN, key_dev->mscbit);
+       input_set_capability(key_dev, EV_MSC, MSC_SCAN);
+
+       __set_bit(EV_KEY, key_dev->evbit);
        key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]);
        key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map);
        key_dev->keycode = &sony_laptop_input_keycode_map;
-       for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) {
-               if (sony_laptop_input_keycode_map[i] != KEY_RESERVED) {
-                       set_bit(sony_laptop_input_keycode_map[i],
-                               key_dev->keybit);
-               }
-       }
+       for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++)
+               __set_bit(sony_laptop_input_keycode_map[i], key_dev->keybit);
+       __clear_bit(KEY_RESERVED, key_dev->keybit);
 
        error = input_register_device(key_dev);
        if (error)
@@ -450,9 +447,8 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
        jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
        key_dev->dev.parent = &acpi_device->dev;
 
-       jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
-       jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE);
-       jog_dev->relbit[0] = BIT_MASK(REL_WHEEL);
+       input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE);
+       input_set_capability(jog_dev, EV_REL, REL_WHEEL);
 
        error = input_register_device(jog_dev);
        if (error)
@@ -473,9 +469,6 @@ err_unregister_keydev:
 err_free_keydev:
        input_free_device(key_dev);
 
-err_destroy_wq:
-       destroy_workqueue(sony_laptop_input.wq);
-
 err_free_kfifo:
        kfifo_free(&sony_laptop_input.fifo);
 
@@ -486,12 +479,23 @@ err_dec_users:
 
 static void sony_laptop_remove_input(void)
 {
-       /* cleanup only after the last user has gone */
+       struct sony_laptop_keypress kp = { NULL };
+
+       /* Cleanup only after the last user has gone */
        if (!atomic_dec_and_test(&sony_laptop_input.users))
                return;
 
-       /* flush workqueue first */
-       flush_workqueue(sony_laptop_input.wq);
+       del_timer_sync(&sony_laptop_input.release_key_timer);
+
+       /*
+        * Generate key-up events for remaining keys. Note that we don't
+        * need locking since nobody is adding new events to the kfifo.
+        */
+       while (kfifo_out(&sony_laptop_input.fifo,
+                        (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) {
+               input_report_key(kp.dev, kp.key, 0);
+               input_sync(kp.dev);
+       }
 
        /* destroy input devs */
        input_unregister_device(sony_laptop_input.key_dev);
@@ -502,7 +506,6 @@ static void sony_laptop_remove_input(void)
                sony_laptop_input.jog_dev = NULL;
        }
 
-       destroy_workqueue(sony_laptop_input.wq);
        kfifo_free(&sony_laptop_input.fifo);
 }