Commit 0d4c8597 authored by Dmitry Torokhov's avatar Dmitry Torokhov

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: default avatarDmitry Torokhov <dtor@mail.ru>
parent 3bedff1d
...@@ -166,6 +166,9 @@ static unsigned char atkbd_unxlate_table[128] = { ...@@ -166,6 +166,9 @@ static unsigned char atkbd_unxlate_table[128] = {
#define ATKBD_SPECIAL 248 #define ATKBD_SPECIAL 248
#define ATKBD_LED_EVENT_BIT 0
#define ATKBD_REP_EVENT_BIT 1
static struct { static struct {
unsigned char keycode; unsigned char keycode;
unsigned char set2; unsigned char set2;
...@@ -211,6 +214,10 @@ struct atkbd { ...@@ -211,6 +214,10 @@ struct atkbd {
unsigned char err_xl; unsigned char err_xl;
unsigned int last; unsigned int last;
unsigned long time; unsigned long time;
struct work_struct event_work;
struct semaphore event_sem;
unsigned long event_mask;
}; };
static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
...@@ -424,58 +431,86 @@ out: ...@@ -424,58 +431,86 @@ out:
} }
/* /*
* Event callback from the input module. Events that change the state of * atkbd_event_work() is used to complete processing of events that
* the hardware are processed here. * can not be processed by input_event() which is often called from
* interrupt context.
*/ */
static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) static void atkbd_event_work(void *data)
{ {
struct atkbd *atkbd = dev->private;
const short period[32] = const short period[32] =
{ 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125, { 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125,
133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 }; 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 };
const short delay[4] = const short delay[4] =
{ 250, 500, 750, 1000 }; { 250, 500, 750, 1000 };
struct atkbd *atkbd = data;
struct input_dev *dev = atkbd->dev;
unsigned char param[2]; unsigned char param[2];
int i, j; int i, j;
down(&atkbd->event_sem);
if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) {
param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
| (test_bit(LED_NUML, dev->led) ? 2 : 0)
| (test_bit(LED_CAPSL, dev->led) ? 4 : 0);
ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS);
if (atkbd->extra) {
param[0] = 0;
param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0)
| (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0)
| (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
| (test_bit(LED_MISC, dev->led) ? 0x10 : 0)
| (test_bit(LED_MUTE, dev->led) ? 0x20 : 0);
ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS);
}
}
if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask)) {
i = j = 0;
while (i < 31 && period[i] < dev->rep[REP_PERIOD])
i++;
while (j < 3 && delay[j] < dev->rep[REP_DELAY])
j++;
dev->rep[REP_PERIOD] = period[i];
dev->rep[REP_DELAY] = delay[j];
param[0] = i | (j << 5);
ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETREP);
}
up(&atkbd->event_sem);
}
/*
* Event callback from the input module. Events that change the state of
* the hardware are processed here. If action can not be performed in
* interrupt context it is offloaded to atkbd_event_work.
*/
static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct atkbd *atkbd = dev->private;
if (!atkbd->write) if (!atkbd->write)
return -1; return -1;
switch (type) { switch (type) {
case EV_LED: case EV_LED:
set_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask);
param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0) wmb();
| (test_bit(LED_NUML, dev->led) ? 2 : 0) schedule_work(&atkbd->event_work);
| (test_bit(LED_CAPSL, dev->led) ? 4 : 0);
ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS);
if (atkbd->extra) {
param[0] = 0;
param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0)
| (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0)
| (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
| (test_bit(LED_MISC, dev->led) ? 0x10 : 0)
| (test_bit(LED_MUTE, dev->led) ? 0x20 : 0);
ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS);
}
return 0; return 0;
case EV_REP: case EV_REP:
if (atkbd->softrepeat) return 0; if (!atkbd->softrepeat) {
set_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask);
i = j = 0; wmb();
while (i < 31 && period[i] < dev->rep[REP_PERIOD]) schedule_work(&atkbd->event_work);
i++; }
while (j < 3 && delay[j] < dev->rep[REP_DELAY])
j++;
dev->rep[REP_PERIOD] = period[i];
dev->rep[REP_DELAY] = delay[j];
param[0] = i | (j << 5);
ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_SETREP);
return 0; return 0;
} }
...@@ -810,6 +845,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) ...@@ -810,6 +845,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
atkbd->dev = dev; atkbd->dev = dev;
ps2_init(&atkbd->ps2dev, serio); ps2_init(&atkbd->ps2dev, serio);
INIT_WORK(&atkbd->event_work, atkbd_event_work, atkbd);
init_MUTEX(&atkbd->event_sem);
switch (serio->id.type) { switch (serio->id.type) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment