diff options
| author | Linus Torvalds <torvalds@home.osdl.org> | 2003-09-20 04:18:59 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.osdl.org> | 2003-09-20 04:18:59 -0700 |
| commit | 834ae7f12afa0d4350e3874a135aa5a4c24a87d7 (patch) | |
| tree | 59478adff11dc8cd1aa0654dc5475a8216b29e80 /drivers/input | |
| parent | 4f3a1d592e8dc6d7327fc7ca915b9b764883d5b7 (diff) | |
| parent | 24a2d11aa804699923077b0e13d1b65e36217db0 (diff) | |
Merge bk://kernel.bkbits.net/gregkh/linux/linus-2.6
into home.osdl.org:/home/torvalds/v2.5/linux
Diffstat (limited to 'drivers/input')
| -rw-r--r-- | drivers/input/evdev.c | 12 | ||||
| -rw-r--r-- | drivers/input/input.c | 6 | ||||
| -rw-r--r-- | drivers/input/joystick/db9.c | 313 | ||||
| -rw-r--r-- | drivers/input/joystick/iforce/iforce-packets.c | 5 | ||||
| -rw-r--r-- | drivers/input/joystick/iforce/iforce-usb.c | 6 | ||||
| -rw-r--r-- | drivers/input/keyboard/Kconfig | 3 | ||||
| -rw-r--r-- | drivers/input/keyboard/atkbd.c | 173 | ||||
| -rw-r--r-- | drivers/input/mouse/Kconfig | 20 | ||||
| -rw-r--r-- | drivers/input/mouse/psmouse-base.c | 103 | ||||
| -rw-r--r-- | drivers/input/mouse/psmouse.h | 22 | ||||
| -rw-r--r-- | drivers/input/mouse/synaptics.c | 363 | ||||
| -rw-r--r-- | drivers/input/mouse/synaptics.h | 27 | ||||
| -rw-r--r-- | drivers/input/serio/serio.c | 31 |
13 files changed, 835 insertions, 249 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 10a13ac90c48..ca56ac39e2b8 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -208,7 +208,7 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, struct evdev *evdev = list->evdev; struct input_dev *dev = evdev->handle.dev; struct input_absinfo abs; - int i, t, u; + int i, t, u, v; if (!evdev->exist) return -ENODEV; @@ -239,14 +239,12 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, case EVIOCSKEYCODE: if (get_user(t, ((int *) arg) + 0)) return -EFAULT; if (t < 0 || t > dev->keycodemax || !dev->keycodesize) return -EINVAL; + if (get_user(v, ((int *) arg) + 1)) return -EFAULT; u = INPUT_KEYCODE(dev, t); - if (get_user(INPUT_KEYCODE(dev, t), ((int *) arg) + 1)) return -EFAULT; - - for (i = 0; i < dev->keycodemax; i++) - if(INPUT_KEYCODE(dev, t) == u) break; + INPUT_KEYCODE(dev, t) = v; + for (i = 0; i < dev->keycodemax; i++) if (v == u) break; if (i == dev->keycodemax) clear_bit(u, dev->keybit); - set_bit(INPUT_KEYCODE(dev, t), dev->keybit); - + set_bit(v, dev->keybit); return 0; case EVIOCSFF: diff --git a/drivers/input/input.c b/drivers/input/input.c index 843c342864f5..45d9bdffdbe4 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -426,8 +426,10 @@ void input_register_device(struct input_dev *dev) init_timer(&dev->timer); dev->timer.data = (long) dev; dev->timer.function = input_repeat_key; - dev->rep[REP_DELAY] = HZ/4; - dev->rep[REP_PERIOD] = HZ/33; + if (!dev->rep[REP_DELAY]) + dev->rep[REP_DELAY] = HZ/4; + if (!dev->rep[REP_PERIOD]) + dev->rep[REP_PERIOD] = HZ/33; INIT_LIST_HEAD(&dev->h_list); list_add_tail(&dev->node, &input_dev_list); diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index 091fe2e887fa..9d757ce3f8ec 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -55,7 +55,9 @@ MODULE_PARM(db9_3, "2i"); #define DB9_MULTI_0802 0x08 #define DB9_MULTI_0802_2 0x09 #define DB9_CD32_PAD 0x0A -#define DB9_MAX_PAD 0x0B +#define DB9_SATURN_DPP 0x0B +#define DB9_SATURN_DPP_2 0x0C +#define DB9_MAX_PAD 0x0D #define DB9_UP 0x01 #define DB9_DOWN 0x02 @@ -69,10 +71,7 @@ MODULE_PARM(db9_3, "2i"); #define DB9_NORMAL 0x0a #define DB9_NOSELECT 0x08 -#define DB9_SATURN0 0x00 -#define DB9_SATURN1 0x02 -#define DB9_SATURN2 0x04 -#define DB9_SATURN3 0x06 +#define DB9_MAX_DEVICES 2 #define DB9_GENESIS6_DELAY 14 #define DB9_REFRESH_TIME HZ/100 @@ -82,7 +81,7 @@ static int db9_2[] __initdata = { -1, 0 }; static int db9_3[] __initdata = { -1, 0 }; struct db9 { - struct input_dev dev[2]; + struct input_dev dev[DB9_MAX_DEVICES]; struct timer_list timer; struct pardevice *pd; int mode; @@ -96,12 +95,247 @@ static short db9_multi_btn[] = { BTN_TRIGGER, BTN_THUMB }; static short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE }; static short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START }; -static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 8, 1, 1, 7 }; +static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 9, 1, 1, 7, 9, 9 }; static short *db9_btn[DB9_MAX_PAD] = { NULL, db9_multi_btn, db9_multi_btn, db9_genesis_btn, NULL, db9_genesis_btn, - db9_genesis_btn, db9_cd32_btn, db9_multi_btn, db9_multi_btn, db9_cd32_btn }; + db9_genesis_btn, db9_cd32_btn, db9_multi_btn, db9_multi_btn, db9_cd32_btn, + db9_cd32_btn, db9_cd32_btn }; static char *db9_name[DB9_MAX_PAD] = { NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad", NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick", - "Multisystem (0.8.0.2-dual) joystick", "Amiga CD-32 pad" }; + "Multisystem (0.8.0.2-dual) joystick", "Amiga CD-32 pad", "Saturn dpp", "Saturn dpp dual" }; + +static const int db9_max_pads[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 6, 1, 2, 1, 6, 12 }; +static const int db9_num_axis[DB9_MAX_PAD] = { 0, 2, 2, 2, 0, 2, 2, 7, 2, 2, 2 ,7, 7 }; +static const short db9_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_RZ, ABS_Z, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y }; +static const int db9_bidirectional[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0 }; +static const int db9_reverse[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0 }; + +/* + * Saturn controllers + */ +#define DB9_SATURN_DELAY 300 +static const int db9_saturn_byte[] = { 1, 1, 1, 2, 2, 2, 2, 2, 1 }; +static const unsigned char db9_saturn_mask[] = { 0x04, 0x01, 0x02, 0x40, 0x20, 0x10, 0x08, 0x80, 0x08 }; + +/* + * db9_saturn_write_sub() writes 2 bit data. + */ +static void db9_saturn_write_sub(struct parport *port, int type, unsigned char data, int powered, int pwr_sub) +{ + unsigned char c; + + switch (type) { + case 1: /* DPP1 */ + c = 0x80 | 0x30 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | data; + parport_write_data(port, c); + break; + case 2: /* DPP2 */ + c = 0x40 | data << 4 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | 0x03; + parport_write_data(port, c); + break; + case 0: /* DB9 */ + c = ((((data & 2) ? 2 : 0) | ((data & 1) ? 4 : 0)) ^ 0x02) | !powered; + parport_write_control(port, c); + break; + } +} + +/* + * gc_saturn_read_sub() reads 4 bit data. + */ +static unsigned char db9_saturn_read_sub(struct parport *port, int type) +{ + unsigned char data; + + if (type) { + /* DPP */ + data = parport_read_status(port) ^ 0x80; + return (data & 0x80 ? 1 : 0) | (data & 0x40 ? 2 : 0) + | (data & 0x20 ? 4 : 0) | (data & 0x10 ? 8 : 0); + } else { + /* DB9 */ + data = parport_read_data(port) & 0x0f; + return (data & 0x8 ? 1 : 0) | (data & 0x4 ? 2 : 0) + | (data & 0x2 ? 4 : 0) | (data & 0x1 ? 8 : 0); + } +} + +/* + * db9_saturn_read_analog() sends clock and reads 8 bit data. + */ +static unsigned char db9_saturn_read_analog(struct parport *port, int type, int powered) +{ + unsigned char data; + + db9_saturn_write_sub(port, type, 0, powered, 0); + udelay(DB9_SATURN_DELAY); + data = db9_saturn_read_sub(port, type) << 4; + db9_saturn_write_sub(port, type, 2, powered, 0); + udelay(DB9_SATURN_DELAY); + data |= db9_saturn_read_sub(port, type); + return data; +} + +/* + * db9_saturn_read_packet() reads whole saturn packet at connector + * and returns device identifier code. + */ +static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char *data, int type, int powered) +{ + int i, j; + unsigned char tmp; + + db9_saturn_write_sub(port, type, 3, powered, 0); + data[0] = db9_saturn_read_sub(port, type); + switch (data[0] & 0x0f) { + case 0xf: + /* 1111 no pad */ + return data[0] = 0xff; + case 0x4: case 0x4 | 0x8: + /* ?100 : digital controller */ + db9_saturn_write_sub(port, type, 0, powered, 1); + data[2] = db9_saturn_read_sub(port, type) << 4; + db9_saturn_write_sub(port, type, 2, powered, 1); + data[1] = db9_saturn_read_sub(port, type) << 4; + db9_saturn_write_sub(port, type, 1, powered, 1); + data[1] |= db9_saturn_read_sub(port, type); + db9_saturn_write_sub(port, type, 3, powered, 1); + /* data[2] |= db9_saturn_read_sub(port, type); */ + data[2] |= data[0]; + return data[0] = 0x02; + case 0x1: + /* 0001 : analog controller or multitap */ + db9_saturn_write_sub(port, type, 2, powered, 0); + udelay(DB9_SATURN_DELAY); + data[0] = db9_saturn_read_analog(port, type, powered); + if (data[0] != 0x41) { + /* read analog controller */ + for (i = 0; i < (data[0] & 0x0f); i++) + data[i + 1] = db9_saturn_read_analog(port, type, powered); + db9_saturn_write_sub(port, type, 3, powered, 0); + return data[0]; + } else { + /* read multitap */ + if (db9_saturn_read_analog(port, type, powered) != 0x60) + return data[0] = 0xff; + for (i = 0; i < 60; i += 10) { + data[i] = db9_saturn_read_analog(port, type, powered); + if (data[i] != 0xff) + /* read each pad */ + for (j = 0; j < (data[i] & 0x0f); j++) + data[i + j + 1] = db9_saturn_read_analog(port, type, powered); + } + db9_saturn_write_sub(port, type, 3, powered, 0); + return 0x41; + } + case 0x0: + /* 0000 : mouse */ + db9_saturn_write_sub(port, type, 2, powered, 0); + udelay(DB9_SATURN_DELAY); + tmp = db9_saturn_read_analog(port, type, powered); + if (tmp == 0xff) { + for (i = 0; i < 3; i++) + data[i + 1] = db9_saturn_read_analog(port, type, powered); + db9_saturn_write_sub(port, type, 3, powered, 0); + return data[0] = 0xe3; + } + default: + return data[0]; + } +} + +/* + * db9_saturn_report() analyzes packet and reports. + */ +static int db9_saturn_report(unsigned char id, unsigned char data[60], struct input_dev *dev, int n, int max_pads) +{ + int tmp, i, j; + + tmp = (id == 0x41) ? 60 : 10; + for (j = 0; (j < tmp) && (n < max_pads); j += 10, n++) { + switch (data[j]) { + case 0x16: /* multi controller (analog 4 axis) */ + input_report_abs(dev + n, db9_abs[5], data[j + 6]); + case 0x15: /* mission stick (analog 3 axis) */ + input_report_abs(dev + n, db9_abs[3], data[j + 4]); + input_report_abs(dev + n, db9_abs[4], data[j + 5]); + case 0x13: /* racing controller (analog 1 axis) */ + input_report_abs(dev + n, db9_abs[2], data[j + 3]); + case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */ + case 0x02: /* digital pad (digital 2 axis + buttons) */ + input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64)); + input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16)); + for (i = 0; i < 9; i++) + input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]); + break; + case 0x19: /* mission stick x2 (analog 6 axis + buttons) */ + input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64)); + input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16)); + for (i = 0; i < 9; i++) + input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]); + input_report_abs(dev + n, db9_abs[2], data[j + 3]); + input_report_abs(dev + n, db9_abs[3], data[j + 4]); + input_report_abs(dev + n, db9_abs[4], data[j + 5]); + /* + input_report_abs(dev + n, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1)); + input_report_abs(dev + n, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1)); + */ + input_report_abs(dev + n, db9_abs[6], data[j + 7]); + input_report_abs(dev + n, db9_abs[7], data[j + 8]); + input_report_abs(dev + n, db9_abs[5], data[j + 9]); + break; + case 0xd3: /* sankyo ff (analog 1 axis + stop btn) */ + input_report_key(dev + n, BTN_A, data[j + 3] & 0x80); + input_report_abs(dev + n, db9_abs[2], data[j + 3] & 0x7f); + break; + case 0xe3: /* shuttle mouse (analog 2 axis + buttons. signed value) */ + input_report_key(dev + n, BTN_START, data[j + 1] & 0x08); + input_report_key(dev + n, BTN_A, data[j + 1] & 0x04); + input_report_key(dev + n, BTN_C, data[j + 1] & 0x02); + input_report_key(dev + n, BTN_B, data[j + 1] & 0x01); + input_report_abs(dev + n, db9_abs[2], data[j + 2] ^ 0x80); + input_report_abs(dev + n, db9_abs[3], (0xff-(data[j + 3] ^ 0x80))+1); /* */ + break; + case 0xff: + default: /* no pad */ + input_report_abs(dev + n, db9_abs[0], 0); + input_report_abs(dev + n, db9_abs[1], 0); + for (i = 0; i < 9; i++) + input_report_key(dev + n, db9_cd32_btn[i], 0); + break; + } + } + return n; +} + +static int db9_saturn(int mode, struct parport *port, struct input_dev *dev) +{ + unsigned char id, data[60]; + int type, n, max_pads; + int tmp, i; + + switch (mode) { + case DB9_SATURN_PAD: + type = 0; + n = 1; + break; + case DB9_SATURN_DPP: + type = 1; + n = 1; + break; + case DB9_SATURN_DPP_2: + type = 1; + n = 2; + break; + default: + return -1; + } + max_pads = min(db9_max_pads[mode], DB9_MAX_DEVICES); + for (tmp = 0, i = 0; i < n; i++) { + id = db9_saturn_read_packet(port, data, type + i, 1); + tmp = db9_saturn_report(id, data, dev, tmp, max_pads); + } + return 0; +} static void db9_timer(unsigned long private) { @@ -222,28 +456,10 @@ static void db9_timer(unsigned long private) break; case DB9_SATURN_PAD: + case DB9_SATURN_DPP: + case DB9_SATURN_DPP_2: - parport_write_control(port, DB9_SATURN0); - data = parport_read_data(port); - - input_report_key(dev, BTN_Y, ~data & DB9_LEFT); - input_report_key(dev, BTN_Z, ~data & DB9_DOWN); - input_report_key(dev, BTN_TL, ~data & DB9_UP); - input_report_key(dev, BTN_TR, ~data & DB9_RIGHT); - - parport_write_control(port, DB9_SATURN2); - data = parport_read_data(port); - - input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); - input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); - - parport_write_control(port, DB9_NORMAL); - data = parport_read_data(port); - - input_report_key(dev, BTN_A, ~data & DB9_LEFT); - input_report_key(dev, BTN_B, ~data & DB9_UP); - input_report_key(dev, BTN_C, ~data & DB9_DOWN); - input_report_key(dev, BTN_X, ~data & DB9_RIGHT); + db9_saturn(db9->mode, port, dev); break; case DB9_CD32_PAD: @@ -279,8 +495,10 @@ static int db9_open(struct input_dev *dev) if (!db9->used++) { parport_claim(db9->pd); parport_write_data(port, 0xff); - parport_data_reverse(port); - parport_write_control(port, DB9_NORMAL); + if (db9_reverse[db9->mode]) { + parport_data_reverse(port); + parport_write_control(port, DB9_NORMAL); + } mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); } @@ -321,11 +539,13 @@ static struct db9 __init *db9_probe(int *config) return NULL; } - if (!(pp->modes & PARPORT_MODE_TRISTATE) && config[1] != DB9_MULTI_0802) { - printk(KERN_ERR "db9.c: specified parport is not bidirectional\n"); - return NULL; + if (db9_bidirectional[config[1]]) { + if (!(pp->modes & PARPORT_MODE_TRISTATE)) { + printk(KERN_ERR "db9.c: specified parport is not bidirectional\n"); + return NULL; + } } - + if (!(db9 = kmalloc(sizeof(struct db9), GFP_KERNEL))) return NULL; memset(db9, 0, sizeof(struct db9)); @@ -343,7 +563,7 @@ static struct db9 __init *db9_probe(int *config) return NULL; } - for (i = 0; i < 1 + (db9->mode == DB9_MULTI_0802_2); i++) { + for (i = 0; i < (min(db9_max_pads[db9->mode], DB9_MAX_DEVICES)); i++) { sprintf(db9->phys[i], "%s/input%d", db9->pd->port->name, i); @@ -359,14 +579,19 @@ static struct db9 __init *db9_probe(int *config) db9->dev[i].id.version = 0x0100; db9->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - db9->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y); - for (j = 0; j < db9_buttons[db9->mode]; j++) set_bit(db9_btn[db9->mode][j], db9->dev[i].keybit); - - db9->dev[i].absmin[ABS_X] = -1; db9->dev[i].absmax[ABS_X] = 1; - db9->dev[i].absmin[ABS_Y] = -1; db9->dev[i].absmax[ABS_Y] = 1; - + for (j = 0; j < db9_num_axis[db9->mode]; j++) { + set_bit(db9_abs[j], db9->dev[i].absbit); + if (j < 2) { + db9->dev[i].absmin[db9_abs[j]] = -1; + db9->dev[i].absmax[db9_abs[j]] = 1; + } else { + db9->dev[i].absmin[db9_abs[j]] = 1; + db9->dev[i].absmax[db9_abs[j]] = 255; + db9->dev[i].absflat[db9_abs[j]] = 0; + } + } input_register_device(db9->dev + i); printk(KERN_INFO "input: %s on %s\n", db9->dev[i].name, db9->pd->port->name); } @@ -419,7 +644,7 @@ void __exit db9_exit(void) for (i = 0; i < 3; i++) if (db9_base[i]) { - for (j = 0; j < 1 + (db9_base[i]->mode == DB9_MULTI_0802_2); j++) + for (j = 0; j < min(db9_max_pads[db9_base[i]->mode], DB9_MAX_DEVICES); j++) input_unregister_device(db9_base[i]->dev + j); parport_unregister_device(db9_base[i]->pd); } diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index 176c5e889186..0c37044ef85a 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -166,8 +166,7 @@ void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data, iforce->expect_packet = 0; iforce->ecmd = cmd; memcpy(iforce->edata, data, IFORCE_MAX_LENGTH); - if (waitqueue_active(&iforce->wait)) - wake_up(&iforce->wait); + wake_up(&iforce->wait); } #endif @@ -264,7 +263,7 @@ int iforce_get_id_packet(struct iforce *iforce, char *packet) set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&iforce->wait, &wait); - if (usb_submit_urb(iforce->ctrl, GFP_KERNEL)) { + if (usb_submit_urb(iforce->ctrl, GFP_ATOMIC)) { set_current_state(TASK_RUNNING); remove_wait_queue(&iforce->wait, &wait); return -1; diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index 0acb3f9b6c1d..a2cefd078385 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -116,8 +116,7 @@ static void iforce_usb_out(struct urb *urb, struct pt_regs *regs) iforce_usb_xmit(iforce); - if (waitqueue_active(&iforce->wait)) - wake_up(&iforce->wait); + wake_up(&iforce->wait); } static void iforce_usb_ctrl(struct urb *urb, struct pt_regs *regs) @@ -125,8 +124,7 @@ static void iforce_usb_ctrl(struct urb *urb, struct pt_regs *regs) struct iforce *iforce = urb->context; if (urb->status) return; iforce->ecmd = 0xff00 | urb->actual_length; - if (waitqueue_active(&iforce->wait)) - wake_up(&iforce->wait); + wake_up(&iforce->wait); } static int iforce_usb_probe(struct usb_interface *intf, diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index d210fba2b127..9288a60dff5e 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -13,7 +13,8 @@ config INPUT_KEYBOARD config KEYBOARD_ATKBD tristate "AT keyboard support" if EMBEDDED || !X86 - default y + default y if INPUT=y && INPUT_KEYBOARD=y && SERIO=y + default m depends on INPUT && INPUT_KEYBOARD && SERIO help Say Y here if you want to use a standard AT or PS/2 keyboard. Usually diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 719def669f8f..9f7726d91402 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -18,6 +18,7 @@ #include <linux/input.h> #include <linux/serio.h> #include <linux/workqueue.h> +#include <linux/timer.h> MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_DESCRIPTION("AT and PS/2 keyboard driver"); @@ -40,8 +41,8 @@ static int atkbd_reset = 1; static unsigned char atkbd_set2_keycode[512] = { 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41, 85, 0, 56, 42,182, 29, 16, 2, 89, 0, 0, 44, 31, 30, 17, 3, 90, - 0, 46, 45, 32, 18, 5, 4, 91, 0, 57, 47, 33, 20, 19, 6, 0, - 0, 49, 48, 35, 34, 21, 7, 0, 0, 0, 50, 36, 22, 8, 9, 0, + 0, 46, 45, 32, 18, 5, 4, 91, 90, 57, 47, 33, 20, 19, 6, 0, + 91, 49, 48, 35, 34, 21, 7, 0, 0, 0, 50, 36, 22, 8, 9, 0, 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, 122, 89, 40,120, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 0, 85, 86, 90, 91, 92, 93, 14, 94, 95, 79,183, 75, 71,121, 0,123, @@ -87,10 +88,10 @@ static unsigned char atkbd_set3_keycode[512] = { #define ATKBD_CMD_GSCANSET 0x11f0 #define ATKBD_CMD_SSCANSET 0x10f0 #define ATKBD_CMD_GETID 0x02f2 +#define ATKBD_CMD_SETREP 0x10f3 #define ATKBD_CMD_ENABLE 0x00f4 #define ATKBD_CMD_RESET_DIS 0x00f5 #define ATKBD_CMD_RESET_BAT 0x02ff -#define ATKBD_CMD_SETALL_MB 0x00f8 #define ATKBD_CMD_RESEND 0x00fe #define ATKBD_CMD_EX_ENABLE 0x10ea #define ATKBD_CMD_EX_SETLEDS 0x20eb @@ -114,12 +115,14 @@ struct atkbd { unsigned char keycode[512]; struct input_dev dev; struct serio *serio; + struct timer_list timer; char name[64]; char phys[32]; unsigned char cmdbuf[4]; unsigned char cmdcnt; unsigned char set; unsigned char release; + int lastkey; volatile signed char ack; unsigned char emul; unsigned short id; @@ -142,6 +145,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags); #endif +#if !defined(__i386__) && !defined (__x86_64__) if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) { printk("atkbd.c: frame/parity error: %02x\n", flags); serio_write(serio, ATKBD_CMD_RESEND); @@ -151,6 +155,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, if (!flags) atkbd->resend = 0; +#endif switch (code) { case ATKBD_RET_ACK: @@ -195,6 +200,14 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, atkbd->set, code, serio->phys, atkbd->release ? "released" : "pressed"); break; default: + + if (!atkbd->release) { + mod_timer(&atkbd->timer, + jiffies + (test_bit(atkbd->keycode[code], + atkbd->dev.key) ? HZ/33 : HZ/4) + HZ/100); + atkbd->lastkey = atkbd->keycode[code]; + } + input_regs(&atkbd->dev, regs); input_report_key(&atkbd->dev, atkbd->keycode[code], !atkbd->release); input_sync(&atkbd->dev); @@ -205,6 +218,13 @@ out: return IRQ_HANDLED; } +static void atkbd_force_key_up(unsigned long data) +{ + struct atkbd *atkbd = (void *) data; + input_report_key(&atkbd->dev, atkbd->lastkey, 0); + input_sync(&atkbd->dev); +} + /* * atkbd_sendbyte() sends a byte to the keyboard, and waits for * acknowledge. It doesn't handle resends according to the keyboard @@ -214,7 +234,7 @@ out: static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte) { - int timeout = 10000; /* 100 msec */ + int timeout = 20000; /* 200 msec */ atkbd->ack = 0; #ifdef ATKBD_DEBUG @@ -322,13 +342,50 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co } /* - * Enable keyboard. + * atkbd_probe() probes for an AT keyboard on a serio port. */ -static void atkbd_enable(struct atkbd *atkbd) + +static int atkbd_probe(struct atkbd *atkbd) { - if (atkbd_command(atkbd, NULL, ATKBD_CMD_ENABLE)) - printk(KERN_ERR "atkbd.c: Failed to enable keyboard on %s\n", - atkbd->serio->phys); + unsigned char param[2]; + +/* + * Some systems, where the bit-twiddling when testing the io-lines of the + * controller may confuse the keyboard need a full reset of the keyboard. On + * these systems the BIOS also usually doesn't do it for us. + */ + + if (atkbd_reset) + if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_BAT)) + printk(KERN_WARNING "atkbd.c: keyboard reset failed on %s\n", atkbd->serio->phys); + +/* + * Then we check the keyboard ID. We should get 0xab83 under normal conditions. + * Some keyboards report different values, but the first byte is always 0xab or + * 0xac. Some old AT keyboards don't report anything. If a mouse is connected, this + * should make sure we don't try to set the LEDs on it. + */ + + if (atkbd_command(atkbd, param, ATKBD_CMD_GETID)) { + +/* + * If the get ID command failed, we check if we can at least set the LEDs on + * the keyboard. This should work on every keyboard out there. It also turns + * the LEDs off, which we want anyway. + */ + param[0] = 0; + if (atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS)) + return -1; + atkbd->id = 0xabba; + return 0; + } + + if (param[0] != 0xab && param[0] != 0xac) + return -1; + atkbd->id = (param[0] << 8) | param[1]; + + + return 0; } /* @@ -365,103 +422,57 @@ static int atkbd_set_3(struct atkbd *atkbd) return 4; } -/* - * Try to set the set we want. - */ + if (atkbd_set != 3) + return 2; - param[0] = atkbd_set; + param[0] = 3; if (atkbd_command(atkbd, param, ATKBD_CMD_SSCANSET)) return 2; -/* - * Read set number. Beware here. Some keyboards always send '2' - * or some other number regardless into what mode they have been - * attempted to be set. Other keyboards treat the '0' command as - * 'set to set 0', and not 'report current set' as they should. - * In that case we time out, and return 2. - */ - param[0] = 0; if (atkbd_command(atkbd, param, ATKBD_CMD_GSCANSET)) return 2; -/* - * Here we return the set number the keyboard reports about - * itself. - */ + if (param[0] != 3) + return 2; - return (param[0] == 3) ? 3 : 2; + return 3; } -/* - * atkbd_probe() probes for an AT keyboard on a serio port. - */ - -static int atkbd_probe(struct atkbd *atkbd) +static int atkbd_enable(struct atkbd *atkbd) { - unsigned char param[2]; - -/* - * Some systems, where the bit-twiddling when testing the io-lines of the - * controller may confuse the keyboard need a full reset of the keyboard. On - * these systems the BIOS also usually doesn't do it for us. - */ - - if (atkbd_reset) - if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_BAT)) - printk(KERN_WARNING - "atkbd.c: keyboard reset failed on %s\n", - atkbd->serio->phys); + unsigned char param[1]; /* - * Then we check the keyboard ID. We should get 0xab83 under normal conditions. - * Some keyboards report different values, but the first byte is always 0xab or - * 0xac. Some old AT keyboards don't report anything. + * Set the LEDs to a defined state. */ - if (atkbd_command(atkbd, param, ATKBD_CMD_GETID)) { + param[0] = 0; + if (atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS)) + return -1; /* - * If the get ID command failed, we check if we can at least set the LEDs on - * the keyboard. This should work on every keyboard out there. It also turns - * the LEDs off, which we want anyway. + * Set autorepeat to fastest possible. */ - param[0] = 0; - if (atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS)) - return -1; - atkbd->id = 0xabba; - return 0; - } - if (param[0] != 0xab && param[0] != 0xac) + param[0] = 0; + if (atkbd_command(atkbd, param, ATKBD_CMD_SETREP)) return -1; - atkbd->id = (param[0] << 8) | param[1]; /* - * Set the LEDs to a defined state. + * Enable the keyboard to receive keystrokes. */ - param[0] = 0; - if (atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS)) + if (atkbd_command(atkbd, NULL, ATKBD_CMD_ENABLE)) { + printk(KERN_ERR "atkbd.c: Failed to enable keyboard on %s\n", + atkbd->serio->phys); return -1; + } return 0; } /* - * Disable autorepeat. We don't need it, as we do it in software anyway, - * because that way can get faster repeat, and have less system load (less - * accesses to the slow ISA hardware). If this fails, we don't care, and will - * just ignore the repeated keys. - * - * This command is for scancode set 3 only. - */ -static void atkbd_disable_autorepeat(struct atkbd *atkbd) -{ - atkbd_command(atkbd, NULL, ATKBD_CMD_SETALL_MB); -} - -/* * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a * reboot. */ @@ -485,7 +496,7 @@ static void atkbd_disconnect(struct serio *serio) } /* - * atkbd_connect() is called when the serio module finds an interface + * atkbd_connect() is called when the serio module finds and interface * that isn't handled yet by an appropriate device driver. We check if * there is an AT keyboard out there and if yes, we register ourselves * to the input module. @@ -513,6 +524,9 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev) atkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); } else atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + atkbd->dev.rep[REP_DELAY] = HZ/4 + HZ/50; + atkbd->dev.rep[REP_PERIOD] = HZ/33; + atkbd->serio = serio; init_input_dev(&atkbd->dev); @@ -525,6 +539,10 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev) serio->private = atkbd; + init_timer(&atkbd->timer); + atkbd->timer.data = (long) atkbd; + atkbd->timer.function = atkbd_force_key_up; + if (serio_open(serio, dev)) { kfree(atkbd); return; @@ -539,9 +557,8 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev) } atkbd->set = atkbd_set_3(atkbd); - if (atkbd->set == 3) - atkbd_disable_autorepeat(atkbd); atkbd_enable(atkbd); + } else { atkbd->set = 2; atkbd->id = 0xab00; diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index a6b481994163..9105f6c697b3 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -19,9 +19,7 @@ config MOUSE_PS2 Say Y here if you have a PS/2 mouse connected to your system. This includes the standard 2 or 3-button PS/2 mouse, as well as PS/2 mice with wheels and extra buttons, Microsoft, Logitech or Genius - compatible. Support for Synaptics TouchPads is also included. - For Synaptics TouchPad support in XFree86 you'll need this XFree86 - driver: http://w1.894.telia.com/~u89404340/touchpad/index.html + compatible. If unsure, say Y. @@ -30,6 +28,22 @@ config MOUSE_PS2 The module will be called psmouse. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. +config MOUSE_PS2_SYNAPTICS + bool "Synaptics TouchPad" + default n + depends on INPUT && INPUT_MOUSE && SERIO && MOUSE_PS2 + ---help--- + Say Y here if you have a Synaptics TouchPad connected to your system. + This touchpad is found on many modern laptop computers. + + Note that you also need a user space driver to interpret the data + generated by the kernel. A compatible driver for XFree86 is available + from http://w1.894.telia.com/~u89404340/touchpad/index.html + + The gpm program is not yet able to interpret the data from this + driver, so if you need to use the touchpad in the console, you have to + say N for now. + config MOUSE_SERIAL tristate "Serial mouse" depends on INPUT && INPUT_MOUSE && SERIO diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 030d23149e2e..4ed0da4e7d7e 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -17,6 +17,7 @@ #include <linux/input.h> #include <linux/serio.h> #include <linux/init.h> +#include <linux/pm.h> #include "psmouse.h" #include "synaptics.h" #include "logips2pp.h" @@ -29,6 +30,8 @@ MODULE_PARM(psmouse_resolution, "i"); MODULE_PARM_DESC(psmouse_resolution, "Resolution, in dpi."); MODULE_PARM(psmouse_smartscroll, "i"); MODULE_PARM_DESC(psmouse_smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); +MODULE_PARM(psmouse_resetafter, "i"); +MODULE_PARM_DESC(psmouse_resetafter, "Reset Synaptics Touchpad after so many bad packets (0 = never)."); MODULE_LICENSE("GPL"); #define PSMOUSE_LOGITECH_SMARTSCROLL 1 @@ -36,11 +39,12 @@ MODULE_LICENSE("GPL"); static int psmouse_noext; int psmouse_resolution; int psmouse_smartscroll = PSMOUSE_LOGITECH_SMARTSCROLL; +unsigned int psmouse_resetafter; -static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "PS2T++", "GenPS/2", "ImPS/2", "ImExPS/2", "Synaptics"}; +static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "PS2T++", "GenPS/2", "ImPS/2", "ImExPS/2", "SynPS/2"}; /* - * psmouse_process_packet() anlyzes the PS/2 mouse packet contents and + * psmouse_process_packet() analyzes the PS/2 mouse packet contents and * reports relevant events to the input module. */ @@ -108,6 +112,9 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, { struct psmouse *psmouse = serio->private; + if (psmouse->state == PSMOUSE_IGNORE) + goto out; + if (psmouse->acking) { switch (data) { case PSMOUSE_RET_ACK: @@ -132,20 +139,35 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, } if (psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) { - printk(KERN_WARNING "psmouse.c: Lost synchronization, throwing %d bytes away.\n", psmouse->pktcnt); + printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n", + psmouse->name, psmouse->phys, psmouse->pktcnt); psmouse->pktcnt = 0; } psmouse->last = jiffies; psmouse->packet[psmouse->pktcnt++] = data; - if (psmouse->pktcnt == 3 + (psmouse->type >= PSMOUSE_GENPS)) { - psmouse_process_packet(psmouse, regs); - psmouse->pktcnt = 0; - goto out; + if (psmouse->packet[0] == PSMOUSE_RET_BAT) { + if (psmouse->pktcnt == 1) + goto out; + + if (psmouse->pktcnt == 2) { + if (psmouse->packet[1] == PSMOUSE_RET_ID) { + psmouse->state = PSMOUSE_IGNORE; + serio_rescan(serio); + goto out; + } + if (psmouse->type == PSMOUSE_SYNAPTICS) { + /* neither 0xAA nor 0x00 are valid first bytes + * for a packet in absolute mode + */ + psmouse->pktcnt = 0; + goto out; + } + } } - if (psmouse->pktcnt == 1 && psmouse->type == PSMOUSE_SYNAPTICS) { + if (psmouse->type == PSMOUSE_SYNAPTICS) { /* * The synaptics driver has its own resync logic, * so it needs to receive all bytes one at a time. @@ -155,8 +177,9 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, goto out; } - if (psmouse->pktcnt == 1 && psmouse->packet[0] == PSMOUSE_RET_BAT) { - serio_rescan(serio); + if (psmouse->pktcnt == 3 + (psmouse->type >= PSMOUSE_GENPS)) { + psmouse_process_packet(psmouse, regs); + psmouse->pktcnt = 0; goto out; } out: @@ -200,7 +223,7 @@ int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command) psmouse->cmdcnt = receive; if (command == PSMOUSE_CMD_RESET_BAT) - timeout = 2000000; /* 2 sec */ + timeout = 4000000; /* 4 sec */ if (command & 0xff) if (psmouse_sendbyte(psmouse, command & 0xff)) @@ -227,7 +250,7 @@ int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command) for (i = 0; i < receive; i++) param[i] = psmouse->cmdbuf[(receive - 1) - i]; - if (psmouse->cmdcnt) + if (psmouse->cmdcnt) return (psmouse->cmdcnt = 0) - 1; return 0; @@ -450,14 +473,18 @@ static void psmouse_initialize(struct psmouse *psmouse) */ psmouse_command(psmouse, param, PSMOUSE_CMD_SETSTREAM); +} /* - * Last, we enable the mouse so that we get reports from it. + * psmouse_activate() enables the mouse so that we get motion reports from it. */ +static void psmouse_activate(struct psmouse *psmouse) +{ if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE)) printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys); + psmouse->state = PSMOUSE_ACTIVATED; } /* @@ -478,13 +505,39 @@ static void psmouse_cleanup(struct serio *serio) static void psmouse_disconnect(struct serio *serio) { struct psmouse *psmouse = serio->private; + + psmouse->state = PSMOUSE_IGNORE; + synaptics_disconnect(psmouse); input_unregister_device(&psmouse->dev); serio_close(serio); - synaptics_disconnect(psmouse); kfree(psmouse); } /* + * Reinitialize mouse hardware after software suspend. + */ + +static int psmouse_pm_callback(struct pm_dev *dev, pm_request_t request, void *data) +{ + struct psmouse *psmouse = dev->data; + struct serio_dev *ser_dev = psmouse->serio->dev; + + synaptics_disconnect(psmouse); + + /* We need to reopen the serio port to reinitialize the i8042 controller */ + serio_close(psmouse->serio); + serio_open(psmouse->serio, ser_dev); + + /* Probe and re-initialize the mouse */ + psmouse_probe(psmouse); + psmouse_initialize(psmouse); + synaptics_pt_init(psmouse); + psmouse_activate(psmouse); + + return 0; +} + +/* * psmouse_connect() is a callback from the serio module when * an unhandled serio port is found. */ @@ -492,8 +545,10 @@ static void psmouse_disconnect(struct serio *serio) static void psmouse_connect(struct serio *serio, struct serio_dev *dev) { struct psmouse *psmouse; + struct pm_dev *pmdev; - if ((serio->type & SERIO_TYPE) != SERIO_8042) + if ((serio->type & SERIO_TYPE) != SERIO_8042 && + (serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU) return; if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL))) @@ -506,6 +561,7 @@ static void psmouse_connect(struct serio *serio, struct serio_dev *dev) psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); + psmouse->state = PSMOUSE_NEW_DEVICE; psmouse->serio = serio; psmouse->dev.private = psmouse; @@ -522,6 +578,12 @@ static void psmouse_connect(struct serio *serio, struct serio_dev *dev) return; } + pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, psmouse_pm_callback); + if (pmdev) { + psmouse->dev.pm_dev = pmdev; + pmdev->data = psmouse; + } + sprintf(psmouse->devname, "%s %s %s", psmouse_protocols[psmouse->type], psmouse->vendor, psmouse->name); sprintf(psmouse->phys, "%s/input0", @@ -539,6 +601,10 @@ static void psmouse_connect(struct serio *serio, struct serio_dev *dev) printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); psmouse_initialize(psmouse); + + synaptics_pt_init(psmouse); + + psmouse_activate(psmouse); } static struct serio_dev psmouse_dev = { @@ -567,9 +633,16 @@ static int __init psmouse_smartscroll_setup(char *str) return 1; } +static int __init psmouse_resetafter_setup(char *str) +{ + get_option(&str, &psmouse_resetafter); + return 1; +} + __setup("psmouse_noext", psmouse_noext_setup); __setup("psmouse_resolution=", psmouse_resolution_setup); __setup("psmouse_smartscroll=", psmouse_smartscroll_setup); +__setup("psmouse_resetafter=", psmouse_resetafter_setup); #endif diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 05a24de18d7d..a69cb72d8a80 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -13,9 +13,15 @@ #define PSMOUSE_CMD_RESET_BAT 0x02ff #define PSMOUSE_RET_BAT 0xaa +#define PSMOUSE_RET_ID 0x00 #define PSMOUSE_RET_ACK 0xfa #define PSMOUSE_RET_NAK 0xfe +/* psmouse states */ +#define PSMOUSE_NEW_DEVICE 0 +#define PSMOUSE_ACTIVATED 1 +#define PSMOUSE_IGNORE 2 + struct psmouse { void *private; struct input_dev dev; @@ -29,6 +35,7 @@ struct psmouse { unsigned char type; unsigned char model; unsigned long last; + unsigned char state; char acking; volatile char ack; char error; @@ -36,16 +43,17 @@ struct psmouse { char phys[32]; }; -#define PSMOUSE_PS2 1 -#define PSMOUSE_PS2PP 2 -#define PSMOUSE_PS2TPP 3 -#define PSMOUSE_GENPS 4 -#define PSMOUSE_IMPS 5 -#define PSMOUSE_IMEX 6 -#define PSMOUSE_SYNAPTICS 7 +#define PSMOUSE_PS2 1 +#define PSMOUSE_PS2PP 2 +#define PSMOUSE_PS2TPP 3 +#define PSMOUSE_GENPS 4 +#define PSMOUSE_IMPS 5 +#define PSMOUSE_IMEX 6 +#define PSMOUSE_SYNAPTICS 7 int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command); extern int psmouse_smartscroll; +extern unsigned int psmouse_resetafter; #endif /* _PSMOUSE_H */ diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 5fa695eb774e..9ba48609d77e 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1,6 +1,9 @@ /* * Synaptics TouchPad PS/2 mouse driver * + * 2003 Dmitry Torokhov <dtor@mail.ru> + * Added support for pass-through port + * * 2003 Peter Osterlund <petero2@telia.com> * Ported to 2.5 input device infrastructure. * @@ -21,6 +24,7 @@ #include <linux/module.h> #include <linux/input.h> +#include <linux/serio.h> #include "psmouse.h" #include "synaptics.h" @@ -71,7 +75,7 @@ static int synaptics_set_mode(struct psmouse *psmouse, unsigned char mode) if (synaptics_special_cmd(psmouse, mode)) return -1; - param[0] = 0x14; + param[0] = SYN_PS_SET_MODE2; if (psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE)) return -1; return 0; @@ -83,7 +87,7 @@ static int synaptics_reset(struct psmouse *psmouse) if (psmouse_command(psmouse, r, PSMOUSE_CMD_RESET_BAT)) return -1; - if (r[0] == 0xAA && r[1] == 0x00) + if (r[0] == PSMOUSE_RET_BAT && r[1] == PSMOUSE_RET_ID) return 0; return -1; } @@ -106,16 +110,25 @@ static int synaptics_model_id(struct psmouse *psmouse, unsigned long int *model_ * Read the capability-bits from the touchpad * see also the SYN_CAP_* macros */ -static int synaptics_capability(struct psmouse *psmouse, unsigned long int *capability) +static int synaptics_capability(struct psmouse *psmouse, unsigned long int *capability, unsigned long int *ext_cap) { unsigned char cap[3]; if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap)) return -1; *capability = (cap[0]<<16) | (cap[1]<<8) | cap[2]; - if (SYN_CAP_VALID(*capability)) - return 0; - return -1; + *ext_cap = 0; + if (!SYN_CAP_VALID(*capability)) + return -1; + + if (SYN_EXT_CAP_REQUESTS(*capability)) { + if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) { + printk(KERN_ERR "Synaptics claims to have extended capabilities," + " but I'm not able to read them."); + } else + *ext_cap = (cap[0]<<16) | (cap[1]<<8) | cap[2]; + } + return 0; } /* @@ -134,19 +147,11 @@ static int synaptics_identify(struct psmouse *psmouse, unsigned long int *ident) return -1; } -static int synaptics_enable_device(struct psmouse *psmouse) -{ - if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE)) - return -1; - return 0; -} - static void print_ident(struct synaptics_data *priv) { printk(KERN_INFO "Synaptics Touchpad, model: %ld\n", SYN_ID_MODEL(priv->identity)); - printk(KERN_INFO " Firware: %ld.%ld\n", SYN_ID_MAJOR(priv->identity), + printk(KERN_INFO " Firmware: %ld.%ld\n", SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity)); - if (SYN_MODEL_ROT180(priv->model_id)) printk(KERN_INFO " 180 degree mounted touchpad\n"); if (SYN_MODEL_PORTRAIT(priv->model_id)) @@ -159,12 +164,17 @@ static void print_ident(struct synaptics_data *priv) if (SYN_CAP_EXTENDED(priv->capabilities)) { printk(KERN_INFO " Touchpad has extended capability bits\n"); - if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) + printk(KERN_INFO " -> %d multi-buttons, i.e. besides standard buttons\n", + (int)(SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))); + else if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) printk(KERN_INFO " -> four buttons\n"); if (SYN_CAP_MULTIFINGER(priv->capabilities)) printk(KERN_INFO " -> multifinger detection\n"); if (SYN_CAP_PALMDETECT(priv->capabilities)) printk(KERN_INFO " -> palm detection\n"); + if (SYN_CAP_PASS_THROUGH(priv->capabilities)) + printk(KERN_INFO " -> pass-through port\n"); } } @@ -172,6 +182,7 @@ static int query_hardware(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; int retries = 0; + int mode; while ((retries++ < 3) && synaptics_reset(psmouse)) printk(KERN_ERR "synaptics reset failed\n"); @@ -180,17 +191,107 @@ static int query_hardware(struct psmouse *psmouse) return -1; if (synaptics_model_id(psmouse, &priv->model_id)) return -1; - if (synaptics_capability(psmouse, &priv->capabilities)) + if (synaptics_capability(psmouse, &priv->capabilities, &priv->ext_cap)) return -1; - if (synaptics_set_mode(psmouse, (SYN_BIT_ABSOLUTE_MODE | - SYN_BIT_HIGH_RATE | - SYN_BIT_DISABLE_GESTURE | - SYN_BIT_W_MODE))) + + mode = SYN_BIT_ABSOLUTE_MODE | SYN_BIT_HIGH_RATE; + if (SYN_ID_MAJOR(priv->identity) >= 4) + mode |= SYN_BIT_DISABLE_GESTURE; + if (SYN_CAP_EXTENDED(priv->capabilities)) + mode |= SYN_BIT_W_MODE; + if (synaptics_set_mode(psmouse, mode)) return -1; - synaptics_enable_device(psmouse); + return 0; +} - print_ident(priv); +/***************************************************************************** + * Synaptics pass-through PS/2 port support + ****************************************************************************/ +static int synaptics_pt_open(struct serio *port) +{ + return 0; +} + +static void synaptics_pt_close(struct serio *port) +{ +} + +static int synaptics_pt_write(struct serio *port, unsigned char c) +{ + struct psmouse *parent = port->driver; + char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */ + + if (synaptics_special_cmd(parent, c)) + return -1; + if (psmouse_command(parent, &rate_param, PSMOUSE_CMD_SETRATE)) + return -1; + return 0; +} + +static inline int synaptics_is_pt_packet(unsigned char *buf) +{ + return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; +} + +static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet) +{ + struct psmouse *child = ptport->private; + + if (child) { + if (child->state == PSMOUSE_ACTIVATED) { + serio_interrupt(ptport, packet[1], 0, NULL); + serio_interrupt(ptport, packet[4], 0, NULL); + serio_interrupt(ptport, packet[5], 0, NULL); + if (child->type >= PSMOUSE_GENPS) + serio_interrupt(ptport, packet[2], 0, NULL); + } else if (child->state != PSMOUSE_IGNORE) { + serio_interrupt(ptport, packet[1], 0, NULL); + } + } +} + +int synaptics_pt_init(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + struct serio *port; + struct psmouse *child; + + if (psmouse->type != PSMOUSE_SYNAPTICS) + return -1; + if (!SYN_CAP_EXTENDED(priv->capabilities)) + return -1; + if (!SYN_CAP_PASS_THROUGH(priv->capabilities)) + return -1; + + priv->ptport = port = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (!port) { + printk(KERN_ERR "synaptics: not enough memory to allocate serio port\n"); + return -1; + } + + memset(port, 0, sizeof(struct serio)); + port->type = SERIO_PS_PSTHRU; + port->name = "Synaptics pass-through"; + port->phys = "synaptics-pt/serio0"; + port->write = synaptics_pt_write; + port->open = synaptics_pt_open; + port->close = synaptics_pt_close; + port->driver = psmouse; + + printk(KERN_INFO "serio: %s port at %s\n", port->name, psmouse->phys); + serio_register_slave_port(port); + + /* adjust the touchpad to child's choice of protocol */ + child = port->private; + if (child && child->type >= PSMOUSE_GENPS) { + if (synaptics_set_mode(psmouse, (SYN_BIT_ABSOLUTE_MODE | + SYN_BIT_HIGH_RATE | + SYN_BIT_DISABLE_GESTURE | + SYN_BIT_FOUR_BYTE_CLIENT | + SYN_BIT_W_MODE))) + printk(KERN_INFO "synaptics: failed to enable 4-byte guest protocol\n"); + } return 0; } @@ -213,22 +314,27 @@ int synaptics_init(struct psmouse *psmouse) { struct synaptics_data *priv; +#ifndef CONFIG_MOUSE_PS2_SYNAPTICS + return -1; +#endif psmouse->private = priv = kmalloc(sizeof(struct synaptics_data), GFP_KERNEL); if (!priv) return -1; memset(priv, 0, sizeof(struct synaptics_data)); - priv->inSync = 1; + priv->out_of_sync = 0; if (query_hardware(psmouse)) { printk(KERN_ERR "Unable to query/initialize Synaptics hardware.\n"); goto init_fail; } + print_ident(priv); + /* * The x/y limits are taken from the Synaptics TouchPad interfacing Guide, * which says that they should be valid regardless of the actual size of - * the senser. + * the sensor. */ set_bit(EV_ABS, psmouse->dev.evbit); set_abs_params(&psmouse->dev, ABS_X, 1472, 5472, 0, 0); @@ -243,7 +349,24 @@ int synaptics_init(struct psmouse *psmouse) set_bit(BTN_RIGHT, psmouse->dev.keybit); set_bit(BTN_FORWARD, psmouse->dev.keybit); set_bit(BTN_BACK, psmouse->dev.keybit); - + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) + switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { + default: + printk(KERN_ERR "This touchpad reports more than 8 multi-buttons, don't know how to handle.\n"); + case 8: + set_bit(BTN_7, psmouse->dev.keybit); + set_bit(BTN_6, psmouse->dev.keybit); + case 6: + set_bit(BTN_5, psmouse->dev.keybit); + set_bit(BTN_4, psmouse->dev.keybit); + case 4: + set_bit(BTN_3, psmouse->dev.keybit); + set_bit(BTN_2, psmouse->dev.keybit); + case 2: + set_bit(BTN_1, psmouse->dev.keybit); + set_bit(BTN_0, psmouse->dev.keybit); + break; + } clear_bit(EV_REL, psmouse->dev.evbit); clear_bit(REL_X, psmouse->dev.relbit); clear_bit(REL_Y, psmouse->dev.relbit); @@ -259,42 +382,85 @@ void synaptics_disconnect(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; - kfree(priv); + if (psmouse->type == PSMOUSE_SYNAPTICS && priv) { + synaptics_set_mode(psmouse, 0); + if (priv->ptport) { + serio_unregister_slave_port(priv->ptport); + kfree(priv->ptport); + } + kfree(priv); + } } /***************************************************************************** * Functions to interpret the absolute mode packets ****************************************************************************/ -static void synaptics_parse_hw_state(struct synaptics_data *priv, struct synaptics_hw_state *hw) +static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) { - unsigned char *buf = priv->proto_buf; - - hw->x = (((buf[3] & 0x10) << 8) | - ((buf[1] & 0x0f) << 8) | - buf[4]); - hw->y = (((buf[3] & 0x20) << 7) | - ((buf[1] & 0xf0) << 4) | - buf[5]); - - hw->z = buf[2]; - hw->w = (((buf[0] & 0x30) >> 2) | - ((buf[0] & 0x04) >> 1) | - ((buf[3] & 0x04) >> 2)); - - hw->left = (buf[0] & 0x01) ? 1 : 0; - hw->right = (buf[0] & 0x2) ? 1 : 0; hw->up = 0; hw->down = 0; + hw->b0 = 0; + hw->b1 = 0; + hw->b2 = 0; + hw->b3 = 0; + hw->b4 = 0; + hw->b5 = 0; + hw->b6 = 0; + hw->b7 = 0; + + if (SYN_MODEL_NEWABS(priv->model_id)) { + hw->x = (((buf[3] & 0x10) << 8) | + ((buf[1] & 0x0f) << 8) | + buf[4]); + hw->y = (((buf[3] & 0x20) << 7) | + ((buf[1] & 0xf0) << 4) | + buf[5]); + + hw->z = buf[2]; + hw->w = (((buf[0] & 0x30) >> 2) | + ((buf[0] & 0x04) >> 1) | + ((buf[3] & 0x04) >> 2)); + + hw->left = (buf[0] & 0x01) ? 1 : 0; + hw->right = (buf[0] & 0x02) ? 1 : 0; + if (SYN_CAP_EXTENDED(priv->capabilities) && + (SYN_CAP_FOUR_BUTTON(priv->capabilities))) { + hw->up = ((buf[3] & 0x01)) ? 1 : 0; + if (hw->left) + hw->up = !hw->up; + hw->down = ((buf[3] & 0x02)) ? 1 : 0; + if (hw->right) + hw->down = !hw->down; + } + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) && + ((buf[3] & 2) ? !hw->right : hw->right)) { + switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { + default: + ; /* we did comment while initialising... */ + case 8: + hw->b7 = ((buf[5] & 0x08)) ? 1 : 0; + hw->b6 = ((buf[4] & 0x08)) ? 1 : 0; + case 6: + hw->b5 = ((buf[5] & 0x04)) ? 1 : 0; + hw->b4 = ((buf[4] & 0x04)) ? 1 : 0; + case 4: + hw->b3 = ((buf[5] & 0x02)) ? 1 : 0; + hw->b2 = ((buf[4] & 0x02)) ? 1 : 0; + case 2: + hw->b1 = ((buf[5] & 0x01)) ? 1 : 0; + hw->b0 = ((buf[4] & 0x01)) ? 1 : 0; + } + } + } else { + hw->x = (((buf[1] & 0x1f) << 8) | buf[2]); + hw->y = (((buf[4] & 0x1f) << 8) | buf[5]); + + hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F)); + hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1)); - if (SYN_CAP_EXTENDED(priv->capabilities) && - (SYN_CAP_FOUR_BUTTON(priv->capabilities))) { - hw->up = ((buf[3] & 0x01)) ? 1 : 0; - if (hw->left) - hw->up = !hw->up; - hw->down = ((buf[3] & 0x02)) ? 1 : 0; - if (hw->right) - hw->down = !hw->down; + hw->left = (buf[0] & 0x01) ? 1 : 0; + hw->right = (buf[0] & 0x02) ? 1 : 0; } } @@ -307,7 +473,7 @@ static void synaptics_process_packet(struct psmouse *psmouse) struct synaptics_data *priv = psmouse->private; struct synaptics_hw_state hw; - synaptics_parse_hw_state(priv, &hw); + synaptics_parse_hw_state(psmouse->packet, priv, &hw); if (hw.z > 0) { int w_ok = 0; @@ -347,7 +513,24 @@ static void synaptics_process_packet(struct psmouse *psmouse) input_report_key(dev, BTN_RIGHT, hw.right); input_report_key(dev, BTN_FORWARD, hw.up); input_report_key(dev, BTN_BACK, hw.down); - + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) + switch(SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { + default: + ; /* we did comment while initialising... */ + case 8: + input_report_key(dev, BTN_7, hw.b7); + input_report_key(dev, BTN_6, hw.b6); + case 6: + input_report_key(dev, BTN_5, hw.b5); + input_report_key(dev, BTN_4, hw.b4); + case 4: + input_report_key(dev, BTN_3, hw.b3); + input_report_key(dev, BTN_2, hw.b2); + case 2: + input_report_key(dev, BTN_1, hw.b1); + input_report_key(dev, BTN_0, hw.b0); + break; + } input_sync(dev); } @@ -355,35 +538,59 @@ void synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs) { struct input_dev *dev = &psmouse->dev; struct synaptics_data *priv = psmouse->private; - unsigned char *pBuf = priv->proto_buf; - unsigned char u = psmouse->packet[0]; + unsigned char data = psmouse->packet[psmouse->pktcnt - 1]; + int newabs = SYN_MODEL_NEWABS(priv->model_id); input_regs(dev, regs); - pBuf[priv->proto_buf_tail++] = u; - - /* check first byte */ - if ((priv->proto_buf_tail == 1) && ((u & 0xC8) != 0x80)) { - priv->inSync = 0; - priv->proto_buf_tail = 0; - printk(KERN_WARNING "Synaptics driver lost sync at 1st byte\n"); - return; - } + switch (psmouse->pktcnt) { + case 1: + if (newabs ? ((data & 0xC8) != 0x80) : ((data & 0xC0) != 0xC0)) { + printk(KERN_WARNING "Synaptics driver lost sync at 1st byte\n"); + goto bad_sync; + } + break; + case 2: + if (!newabs && ((data & 0x60) != 0x00)) { + printk(KERN_WARNING "Synaptics driver lost sync at 2nd byte\n"); + goto bad_sync; + } + break; + case 4: + if (newabs ? ((data & 0xC8) != 0xC0) : ((data & 0xC0) != 0x80)) { + printk(KERN_WARNING "Synaptics driver lost sync at 4th byte\n"); + goto bad_sync; + } + break; + case 5: + if (!newabs && ((data & 0x60) != 0x00)) { + printk(KERN_WARNING "Synaptics driver lost sync at 5th byte\n"); + goto bad_sync; + } + break; + default: + if (psmouse->pktcnt >= 6) { /* Full packet received */ + if (priv->out_of_sync) { + priv->out_of_sync = 0; + printk(KERN_NOTICE "Synaptics driver resynced.\n"); + } - /* check 4th byte */ - if ((priv->proto_buf_tail == 4) && ((u & 0xc8) != 0xc0)) { - priv->inSync = 0; - priv->proto_buf_tail = 0; - printk(KERN_WARNING "Synaptics driver lost sync at 4th byte\n"); - return; - } + if (priv->ptport && synaptics_is_pt_packet(psmouse->packet)) + synaptics_pass_pt_packet(priv->ptport, psmouse->packet); + else + synaptics_process_packet(psmouse); - if (priv->proto_buf_tail >= 6) { /* Full packet received */ - if (!priv->inSync) { - priv->inSync = 1; - printk(KERN_NOTICE "Synaptics driver resynced.\n"); + psmouse->pktcnt = 0; } - synaptics_process_packet(psmouse); - priv->proto_buf_tail = 0; + break; + } + return; + + bad_sync: + priv->out_of_sync++; + psmouse->pktcnt = 0; + if (psmouse_resetafter > 0 && priv->out_of_sync == psmouse_resetafter) { + psmouse->state = PSMOUSE_IGNORE; + serio_rescan(psmouse->serio); } } diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 6e3e029a3bdd..4dad98abd54e 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -12,6 +12,7 @@ extern void synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs); extern int synaptics_init(struct psmouse *psmouse); +extern int synaptics_pt_init(struct psmouse *psmouse); extern void synaptics_disconnect(struct psmouse *psmouse); /* synaptics queries */ @@ -22,12 +23,14 @@ extern void synaptics_disconnect(struct psmouse *psmouse); #define SYN_QUE_SERIAL_NUMBER_PREFIX 0x06 #define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07 #define SYN_QUE_RESOLUTION 0x08 +#define SYN_QUE_EXT_CAPAB 0x09 /* synatics modes */ #define SYN_BIT_ABSOLUTE_MODE (1 << 7) #define SYN_BIT_HIGH_RATE (1 << 6) #define SYN_BIT_SLEEP_MODE (1 << 3) #define SYN_BIT_DISABLE_GESTURE (1 << 2) +#define SYN_BIT_FOUR_BYTE_CLIENT (1 << 1) #define SYN_BIT_W_MODE (1 << 0) /* synaptics model ID bits */ @@ -42,11 +45,14 @@ extern void synaptics_disconnect(struct psmouse *psmouse); /* synaptics capability bits */ #define SYN_CAP_EXTENDED(c) ((c) & (1 << 23)) +#define SYN_CAP_PASS_THROUGH(c) ((c) & (1 << 7)) #define SYN_CAP_SLEEP(c) ((c) & (1 << 4)) #define SYN_CAP_FOUR_BUTTON(c) ((c) & (1 << 3)) #define SYN_CAP_MULTIFINGER(c) ((c) & (1 << 1)) #define SYN_CAP_PALMDETECT(c) ((c) & (1 << 0)) #define SYN_CAP_VALID(c) ((((c) & 0x00ff00) >> 8) == 0x47) +#define SYN_EXT_CAP_REQUESTS(c) ((((c) & 0x700000) >> 20) == 1) +#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12) /* synaptics modes query bits */ #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) @@ -62,6 +68,10 @@ extern void synaptics_disconnect(struct psmouse *psmouse); #define SYN_ID_MINOR(i) (((i) >> 16) & 0xff) #define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47) +/* synaptics special commands */ +#define SYN_PS_SET_MODE2 0x14 +#define SYN_PS_CLIENT_CMD 0x28 + /* * A structure to describe the state of the touchpad hardware (buttons and pad) */ @@ -75,21 +85,28 @@ struct synaptics_hw_state { int right; int up; int down; + int b0; + int b1; + int b2; + int b3; + int b4; + int b5; + int b6; + int b7; }; struct synaptics_data { /* Data read from the touchpad */ unsigned long int model_id; /* Model-ID */ unsigned long int capabilities; /* Capabilities */ + unsigned long int ext_cap; /* Extended Capabilities */ unsigned long int identity; /* Identification */ /* Data for normal processing */ - unsigned char proto_buf[6]; /* Buffer for Packet */ - unsigned char last_byte; /* last received byte */ - int inSync; /* Packets in sync */ - int proto_buf_tail; - + unsigned int out_of_sync; /* # of packets out of sync */ int old_w; /* Previous w value */ + + struct serio *ptport; /* pass-through port */ }; #endif /* _SYNAPTICS_H */ diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 78c51d96dea6..7c818a32628a 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -49,7 +49,9 @@ MODULE_LICENSE("GPL"); EXPORT_SYMBOL(serio_interrupt); EXPORT_SYMBOL(serio_register_port); +EXPORT_SYMBOL(serio_register_slave_port); EXPORT_SYMBOL(serio_unregister_port); +EXPORT_SYMBOL(serio_unregister_slave_port); EXPORT_SYMBOL(serio_register_device); EXPORT_SYMBOL(serio_unregister_device); EXPORT_SYMBOL(serio_open); @@ -166,6 +168,17 @@ void serio_register_port(struct serio *serio) up(&serio_sem); } +/* + * Same as serio_register_port but does not try to acquire serio_sem. + * Should be used when registering a serio from other input device's + * connect() function. + */ +void serio_register_slave_port(struct serio *serio) +{ + list_add_tail(&serio->node, &serio_list); + serio_find_dev(serio); +} + void serio_unregister_port(struct serio *serio) { down(&serio_sem); @@ -175,6 +188,18 @@ void serio_unregister_port(struct serio *serio) up(&serio_sem); } +/* + * Same as serio_unregister_port but does not try to acquire serio_sem. + * Should be used when unregistering a serio from other input device's + * disconnect() function. + */ +void serio_unregister_slave_port(struct serio *serio) +{ + list_del_init(&serio->node); + if (serio->dev && serio->dev->disconnect) + serio->dev->disconnect(serio); +} + void serio_register_device(struct serio_dev *dev) { struct serio *serio; @@ -204,9 +229,11 @@ void serio_unregister_device(struct serio_dev *dev) /* called from serio_dev->connect/disconnect methods under serio_sem */ int serio_open(struct serio *serio, struct serio_dev *dev) { - if (serio->open(serio)) - return -1; serio->dev = dev; + if (serio->open(serio)) { + serio->dev = NULL; + return -1; + } return 0; } |
