aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/sony-laptop.c91
1 files changed, 47 insertions, 44 deletions
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index 3f71a605a492..5a3d8514c66d 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -145,7 +145,7 @@ struct sony_laptop_input_s {
145 struct input_dev *key_dev; 145 struct input_dev *key_dev;
146 struct kfifo fifo; 146 struct kfifo fifo;
147 spinlock_t fifo_lock; 147 spinlock_t fifo_lock;
148 struct workqueue_struct *wq; 148 struct timer_list release_key_timer;
149}; 149};
150 150
151static struct sony_laptop_input_s sony_laptop_input = { 151static struct sony_laptop_input_s sony_laptop_input = {
@@ -299,20 +299,26 @@ static int sony_laptop_input_keycode_map[] = {
299}; 299};
300 300
301/* release buttons after a short delay if pressed */ 301/* release buttons after a short delay if pressed */
302static void do_sony_laptop_release_key(struct work_struct *work) 302static void do_sony_laptop_release_key(unsigned long unused)
303{ 303{
304 struct sony_laptop_keypress kp; 304 struct sony_laptop_keypress kp;
305 unsigned long flags;
306
307 spin_lock_irqsave(&sony_laptop_input.fifo_lock, flags);
305 308
306 while (kfifo_out_locked(&sony_laptop_input.fifo, (unsigned char *)&kp, 309 if (kfifo_out(&sony_laptop_input.fifo,
307 sizeof(kp), &sony_laptop_input.fifo_lock) 310 (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) {
308 == sizeof(kp)) {
309 msleep(10);
310 input_report_key(kp.dev, kp.key, 0); 311 input_report_key(kp.dev, kp.key, 0);
311 input_sync(kp.dev); 312 input_sync(kp.dev);
312 } 313 }
314
315 /* If there is something in the fifo schedule next release. */
316 if (kfifo_len(&sony_laptop_input.fifo) != 0)
317 mod_timer(&sony_laptop_input.release_key_timer,
318 jiffies + msecs_to_jiffies(10));
319
320 spin_unlock_irqrestore(&sony_laptop_input.fifo_lock, flags);
313} 321}
314static DECLARE_WORK(sony_laptop_release_key_work,
315 do_sony_laptop_release_key);
316 322
317/* forward event to the input subsystem */ 323/* forward event to the input subsystem */
318static void sony_laptop_report_input_event(u8 event) 324static void sony_laptop_report_input_event(u8 event)
@@ -366,13 +372,13 @@ static void sony_laptop_report_input_event(u8 event)
366 /* we emit the scancode so we can always remap the key */ 372 /* we emit the scancode so we can always remap the key */
367 input_event(kp.dev, EV_MSC, MSC_SCAN, event); 373 input_event(kp.dev, EV_MSC, MSC_SCAN, event);
368 input_sync(kp.dev); 374 input_sync(kp.dev);
369 kfifo_in_locked(&sony_laptop_input.fifo,
370 (unsigned char *)&kp, sizeof(kp),
371 &sony_laptop_input.fifo_lock);
372 375
373 if (!work_pending(&sony_laptop_release_key_work)) 376 /* schedule key release */
374 queue_work(sony_laptop_input.wq, 377 kfifo_in_locked(&sony_laptop_input.fifo,
375 &sony_laptop_release_key_work); 378 (unsigned char *)&kp, sizeof(kp),
379 &sony_laptop_input.fifo_lock);
380 mod_timer(&sony_laptop_input.release_key_timer,
381 jiffies + msecs_to_jiffies(10));
376 } else 382 } else
377 dprintk("unknown input event %.2x\n", event); 383 dprintk("unknown input event %.2x\n", event);
378} 384}
@@ -390,27 +396,21 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
390 396
391 /* kfifo */ 397 /* kfifo */
392 spin_lock_init(&sony_laptop_input.fifo_lock); 398 spin_lock_init(&sony_laptop_input.fifo_lock);
393 error = 399 error = kfifo_alloc(&sony_laptop_input.fifo,
394 kfifo_alloc(&sony_laptop_input.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); 400 SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);
395 if (error) { 401 if (error) {
396 printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); 402 printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
397 goto err_dec_users; 403 goto err_dec_users;
398 } 404 }
399 405
400 /* init workqueue */ 406 setup_timer(&sony_laptop_input.release_key_timer,
401 sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop"); 407 do_sony_laptop_release_key, 0);
402 if (!sony_laptop_input.wq) {
403 printk(KERN_ERR DRV_PFX
404 "Unable to create workqueue.\n");
405 error = -ENXIO;
406 goto err_free_kfifo;
407 }
408 408
409 /* input keys */ 409 /* input keys */
410 key_dev = input_allocate_device(); 410 key_dev = input_allocate_device();
411 if (!key_dev) { 411 if (!key_dev) {
412 error = -ENOMEM; 412 error = -ENOMEM;
413 goto err_destroy_wq; 413 goto err_free_kfifo;
414 } 414 }
415 415
416 key_dev->name = "Sony Vaio Keys"; 416 key_dev->name = "Sony Vaio Keys";
@@ -419,18 +419,15 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
419 key_dev->dev.parent = &acpi_device->dev; 419 key_dev->dev.parent = &acpi_device->dev;
420 420
421 /* Initialize the Input Drivers: special keys */ 421 /* Initialize the Input Drivers: special keys */
422 set_bit(EV_KEY, key_dev->evbit); 422 input_set_capability(key_dev, EV_MSC, MSC_SCAN);
423 set_bit(EV_MSC, key_dev->evbit); 423
424 set_bit(MSC_SCAN, key_dev->mscbit); 424 __set_bit(EV_KEY, key_dev->evbit);
425 key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]); 425 key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]);
426 key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map); 426 key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map);
427 key_dev->keycode = &sony_laptop_input_keycode_map; 427 key_dev->keycode = &sony_laptop_input_keycode_map;
428 for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) { 428 for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++)
429 if (sony_laptop_input_keycode_map[i] != KEY_RESERVED) { 429 __set_bit(sony_laptop_input_keycode_map[i], key_dev->keybit);
430 set_bit(sony_laptop_input_keycode_map[i], 430 __clear_bit(KEY_RESERVED, key_dev->keybit);
431 key_dev->keybit);
432 }
433 }
434 431
435 error = input_register_device(key_dev); 432 error = input_register_device(key_dev);
436 if (error) 433 if (error)
@@ -450,9 +447,8 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
450 jog_dev->id.vendor = PCI_VENDOR_ID_SONY; 447 jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
451 key_dev->dev.parent = &acpi_device->dev; 448 key_dev->dev.parent = &acpi_device->dev;
452 449
453 jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); 450 input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE);
454 jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE); 451 input_set_capability(jog_dev, EV_REL, REL_WHEEL);
455 jog_dev->relbit[0] = BIT_MASK(REL_WHEEL);
456 452
457 error = input_register_device(jog_dev); 453 error = input_register_device(jog_dev);
458 if (error) 454 if (error)
@@ -473,9 +469,6 @@ err_unregister_keydev:
473err_free_keydev: 469err_free_keydev:
474 input_free_device(key_dev); 470 input_free_device(key_dev);
475 471
476err_destroy_wq:
477 destroy_workqueue(sony_laptop_input.wq);
478
479err_free_kfifo: 472err_free_kfifo:
480 kfifo_free(&sony_laptop_input.fifo); 473 kfifo_free(&sony_laptop_input.fifo);
481 474
@@ -486,12 +479,23 @@ err_dec_users:
486 479
487static void sony_laptop_remove_input(void) 480static void sony_laptop_remove_input(void)
488{ 481{
489 /* cleanup only after the last user has gone */ 482 struct sony_laptop_keypress kp = { NULL };
483
484 /* Cleanup only after the last user has gone */
490 if (!atomic_dec_and_test(&sony_laptop_input.users)) 485 if (!atomic_dec_and_test(&sony_laptop_input.users))
491 return; 486 return;
492 487
493 /* flush workqueue first */ 488 del_timer_sync(&sony_laptop_input.release_key_timer);
494 flush_workqueue(sony_laptop_input.wq); 489
490 /*
491 * Generate key-up events for remaining keys. Note that we don't
492 * need locking since nobody is adding new events to the kfifo.
493 */
494 while (kfifo_out(&sony_laptop_input.fifo,
495 (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) {
496 input_report_key(kp.dev, kp.key, 0);
497 input_sync(kp.dev);
498 }
495 499
496 /* destroy input devs */ 500 /* destroy input devs */
497 input_unregister_device(sony_laptop_input.key_dev); 501 input_unregister_device(sony_laptop_input.key_dev);
@@ -502,7 +506,6 @@ static void sony_laptop_remove_input(void)
502 sony_laptop_input.jog_dev = NULL; 506 sony_laptop_input.jog_dev = NULL;
503 } 507 }
504 508
505 destroy_workqueue(sony_laptop_input.wq);
506 kfifo_free(&sony_laptop_input.fifo); 509 kfifo_free(&sony_laptop_input.fifo);
507} 510}
508 511