aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/sony-laptop.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/sony-laptop.c')
-rw-r--r--drivers/platform/x86/sony-laptop.c71
1 files changed, 39 insertions, 32 deletions
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index 5af53340da6..c42d35ba73d 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -145,7 +145,6 @@ 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;
149}; 148};
150 149
151static struct sony_laptop_input_s sony_laptop_input = { 150static struct sony_laptop_input_s sony_laptop_input = {
@@ -301,18 +300,28 @@ static int sony_laptop_input_keycode_map[] = {
301/* release buttons after a short delay if pressed */ 300/* release buttons after a short delay if pressed */
302static void do_sony_laptop_release_key(struct work_struct *work) 301static void do_sony_laptop_release_key(struct work_struct *work)
303{ 302{
303 struct delayed_work *dwork =
304 container_of(work, struct delayed_work, work);
304 struct sony_laptop_keypress kp; 305 struct sony_laptop_keypress kp;
306 unsigned long flags;
307
308 spin_lock_irqsave(&sony_laptop_input.fifo_lock, flags);
305 309
306 while (kfifo_out_locked(&sony_laptop_input.fifo, (unsigned char *)&kp, 310 if (kfifo_out(&sony_laptop_input.fifo,
307 sizeof(kp), &sony_laptop_input.fifo_lock) 311 (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) {
308 == sizeof(kp)) {
309 msleep(10);
310 input_report_key(kp.dev, kp.key, 0); 312 input_report_key(kp.dev, kp.key, 0);
311 input_sync(kp.dev); 313 input_sync(kp.dev);
312 } 314 }
315
316 /* If there is something in the fifo schedule next release. */
317 if (kfifo_len(&sony_laptop_input.fifo) != 0)
318 schedule_delayed_work(dwork, 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, 322
315 do_sony_laptop_release_key); 323static DECLARE_DELAYED_WORK(sony_laptop_release_key_work,
324 do_sony_laptop_release_key);
316 325
317/* forward event to the input subsystem */ 326/* forward event to the input subsystem */
318static void sony_laptop_report_input_event(u8 event) 327static void sony_laptop_report_input_event(u8 event)
@@ -366,13 +375,13 @@ static void sony_laptop_report_input_event(u8 event)
366 /* we emit the scancode so we can always remap the key */ 375 /* we emit the scancode so we can always remap the key */
367 input_event(kp.dev, EV_MSC, MSC_SCAN, event); 376 input_event(kp.dev, EV_MSC, MSC_SCAN, event);
368 input_sync(kp.dev); 377 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 378
373 if (!work_pending(&sony_laptop_release_key_work)) 379 /* schedule key release */
374 queue_work(sony_laptop_input.wq, 380 kfifo_in_locked(&sony_laptop_input.fifo,
375 &sony_laptop_release_key_work); 381 (unsigned char *)&kp, sizeof(kp),
382 &sony_laptop_input.fifo_lock);
383 schedule_delayed_work(&sony_laptop_release_key_work,
384 msecs_to_jiffies(10));
376 } else 385 } else
377 dprintk("unknown input event %.2x\n", event); 386 dprintk("unknown input event %.2x\n", event);
378} 387}
@@ -390,27 +399,18 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
390 399
391 /* kfifo */ 400 /* kfifo */
392 spin_lock_init(&sony_laptop_input.fifo_lock); 401 spin_lock_init(&sony_laptop_input.fifo_lock);
393 error = 402 error = kfifo_alloc(&sony_laptop_input.fifo,
394 kfifo_alloc(&sony_laptop_input.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); 403 SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);
395 if (error) { 404 if (error) {
396 printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); 405 printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
397 goto err_dec_users; 406 goto err_dec_users;
398 } 407 }
399 408
400 /* init workqueue */
401 sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop");
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
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";
@@ -473,9 +473,6 @@ err_unregister_keydev:
473err_free_keydev: 473err_free_keydev:
474 input_free_device(key_dev); 474 input_free_device(key_dev);
475 475
476err_destroy_wq:
477 destroy_workqueue(sony_laptop_input.wq);
478
479err_free_kfifo: 476err_free_kfifo:
480 kfifo_free(&sony_laptop_input.fifo); 477 kfifo_free(&sony_laptop_input.fifo);
481 478
@@ -486,12 +483,23 @@ err_dec_users:
486 483
487static void sony_laptop_remove_input(void) 484static void sony_laptop_remove_input(void)
488{ 485{
489 /* cleanup only after the last user has gone */ 486 struct sony_laptop_keypress kp = { NULL };
487
488 /* Cleanup only after the last user has gone */
490 if (!atomic_dec_and_test(&sony_laptop_input.users)) 489 if (!atomic_dec_and_test(&sony_laptop_input.users))
491 return; 490 return;
492 491
493 /* flush workqueue first */ 492 cancel_delayed_work_sync(&sony_laptop_release_key_work);
494 flush_workqueue(sony_laptop_input.wq); 493
494 /*
495 * Generate key-up events for remaining keys. Note that we don't
496 * need locking since nobody is adding new events to the kfifo.
497 */
498 while (kfifo_out(&sony_laptop_input.fifo,
499 (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) {
500 input_report_key(kp.dev, kp.key, 0);
501 input_sync(kp.dev);
502 }
495 503
496 /* destroy input devs */ 504 /* destroy input devs */
497 input_unregister_device(sony_laptop_input.key_dev); 505 input_unregister_device(sony_laptop_input.key_dev);
@@ -502,7 +510,6 @@ static void sony_laptop_remove_input(void)
502 sony_laptop_input.jog_dev = NULL; 510 sony_laptop_input.jog_dev = NULL;
503 } 511 }
504 512
505 destroy_workqueue(sony_laptop_input.wq);
506 kfifo_free(&sony_laptop_input.fifo); 513 kfifo_free(&sony_laptop_input.fifo);
507} 514}
508 515