diff options
author | Dmitry Torokhov <dtor_core@ameritech.net> | 2005-11-20 00:49:42 -0500 |
---|---|---|
committer | Dmitry Torokhov <dtor_core@ameritech.net> | 2005-11-20 00:49:42 -0500 |
commit | 0d4c859734a818721b2d5ac712283ba8f92bd23a (patch) | |
tree | d9735f6da17572f45ebe47f8f200ac7161129f79 /drivers/input | |
parent | 3bedff1d73b86e0cf52634efb447e9ada08f2cc6 (diff) |
Input: atkbd - speed up setting leds/repeat state
Changing led state is pretty slow operation; when there are multiple
requests coming at a high rate they may interfere with normal typing.
Try optimize (skip) changing hardware state when multiple requests
are coming back-to-back.
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/keyboard/atkbd.c | 99 |
1 files changed, 68 insertions, 31 deletions
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 820c7fd9a604..a0256f8de8ef 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c | |||
@@ -166,6 +166,9 @@ static unsigned char atkbd_unxlate_table[128] = { | |||
166 | 166 | ||
167 | #define ATKBD_SPECIAL 248 | 167 | #define ATKBD_SPECIAL 248 |
168 | 168 | ||
169 | #define ATKBD_LED_EVENT_BIT 0 | ||
170 | #define ATKBD_REP_EVENT_BIT 1 | ||
171 | |||
169 | static struct { | 172 | static struct { |
170 | unsigned char keycode; | 173 | unsigned char keycode; |
171 | unsigned char set2; | 174 | unsigned char set2; |
@@ -211,6 +214,10 @@ struct atkbd { | |||
211 | unsigned char err_xl; | 214 | unsigned char err_xl; |
212 | unsigned int last; | 215 | unsigned int last; |
213 | unsigned long time; | 216 | unsigned long time; |
217 | |||
218 | struct work_struct event_work; | ||
219 | struct semaphore event_sem; | ||
220 | unsigned long event_mask; | ||
214 | }; | 221 | }; |
215 | 222 | ||
216 | static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, | 223 | static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, |
@@ -424,58 +431,86 @@ out: | |||
424 | } | 431 | } |
425 | 432 | ||
426 | /* | 433 | /* |
427 | * Event callback from the input module. Events that change the state of | 434 | * atkbd_event_work() is used to complete processing of events that |
428 | * the hardware are processed here. | 435 | * can not be processed by input_event() which is often called from |
436 | * interrupt context. | ||
429 | */ | 437 | */ |
430 | 438 | ||
431 | static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) | 439 | static void atkbd_event_work(void *data) |
432 | { | 440 | { |
433 | struct atkbd *atkbd = dev->private; | ||
434 | const short period[32] = | 441 | const short period[32] = |
435 | { 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125, | 442 | { 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125, |
436 | 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 }; | 443 | 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 }; |
437 | const short delay[4] = | 444 | const short delay[4] = |
438 | { 250, 500, 750, 1000 }; | 445 | { 250, 500, 750, 1000 }; |
446 | |||
447 | struct atkbd *atkbd = data; | ||
448 | struct input_dev *dev = atkbd->dev; | ||
439 | unsigned char param[2]; | 449 | unsigned char param[2]; |
440 | int i, j; | 450 | int i, j; |
441 | 451 | ||
452 | down(&atkbd->event_sem); | ||
453 | |||
454 | if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) { | ||
455 | param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0) | ||
456 | | (test_bit(LED_NUML, dev->led) ? 2 : 0) | ||
457 | | (test_bit(LED_CAPSL, dev->led) ? 4 : 0); | ||
458 | ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS); | ||
459 | |||
460 | if (atkbd->extra) { | ||
461 | param[0] = 0; | ||
462 | param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0) | ||
463 | | (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0) | ||
464 | | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0) | ||
465 | | (test_bit(LED_MISC, dev->led) ? 0x10 : 0) | ||
466 | | (test_bit(LED_MUTE, dev->led) ? 0x20 : 0); | ||
467 | ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS); | ||
468 | } | ||
469 | } | ||
470 | |||
471 | if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask)) { | ||
472 | i = j = 0; | ||
473 | while (i < 31 && period[i] < dev->rep[REP_PERIOD]) | ||
474 | i++; | ||
475 | while (j < 3 && delay[j] < dev->rep[REP_DELAY]) | ||
476 | j++; | ||
477 | dev->rep[REP_PERIOD] = period[i]; | ||
478 | dev->rep[REP_DELAY] = delay[j]; | ||
479 | param[0] = i | (j << 5); | ||
480 | ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETREP); | ||
481 | } | ||
482 | |||
483 | up(&atkbd->event_sem); | ||
484 | } | ||
485 | |||
486 | /* | ||
487 | * Event callback from the input module. Events that change the state of | ||
488 | * the hardware are processed here. If action can not be performed in | ||
489 | * interrupt context it is offloaded to atkbd_event_work. | ||
490 | */ | ||
491 | |||
492 | static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) | ||
493 | { | ||
494 | struct atkbd *atkbd = dev->private; | ||
495 | |||
442 | if (!atkbd->write) | 496 | if (!atkbd->write) |
443 | return -1; | 497 | return -1; |
444 | 498 | ||
445 | switch (type) { | 499 | switch (type) { |
446 | 500 | ||
447 | case EV_LED: | 501 | case EV_LED: |
448 | 502 | set_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask); | |
449 | param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0) | 503 | wmb(); |
450 | | (test_bit(LED_NUML, dev->led) ? 2 : 0) | 504 | schedule_work(&atkbd->event_work); |
451 | | (test_bit(LED_CAPSL, dev->led) ? 4 : 0); | ||
452 | ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS); | ||
453 | |||
454 | if (atkbd->extra) { | ||
455 | param[0] = 0; | ||
456 | param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0) | ||
457 | | (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0) | ||
458 | | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0) | ||
459 | | (test_bit(LED_MISC, dev->led) ? 0x10 : 0) | ||
460 | | (test_bit(LED_MUTE, dev->led) ? 0x20 : 0); | ||
461 | ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS); | ||
462 | } | ||
463 | |||
464 | return 0; | 505 | return 0; |
465 | 506 | ||
466 | case EV_REP: | 507 | case EV_REP: |
467 | 508 | ||
468 | if (atkbd->softrepeat) return 0; | 509 | if (!atkbd->softrepeat) { |
469 | 510 | set_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask); | |
470 | i = j = 0; | 511 | wmb(); |
471 | while (i < 31 && period[i] < dev->rep[REP_PERIOD]) | 512 | schedule_work(&atkbd->event_work); |
472 | i++; | 513 | } |
473 | while (j < 3 && delay[j] < dev->rep[REP_DELAY]) | ||
474 | j++; | ||
475 | dev->rep[REP_PERIOD] = period[i]; | ||
476 | dev->rep[REP_DELAY] = delay[j]; | ||
477 | param[0] = i | (j << 5); | ||
478 | ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_SETREP); | ||
479 | 514 | ||
480 | return 0; | 515 | return 0; |
481 | } | 516 | } |
@@ -810,6 +845,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) | |||
810 | 845 | ||
811 | atkbd->dev = dev; | 846 | atkbd->dev = dev; |
812 | ps2_init(&atkbd->ps2dev, serio); | 847 | ps2_init(&atkbd->ps2dev, serio); |
848 | INIT_WORK(&atkbd->event_work, atkbd_event_work, atkbd); | ||
849 | init_MUTEX(&atkbd->event_sem); | ||
813 | 850 | ||
814 | switch (serio->id.type) { | 851 | switch (serio->id.type) { |
815 | 852 | ||