/* * Synaptics touchpad driver * * Copyright (c) 2003 Jens Taprogge * * based on psmouse.c by Vojetech Pavlik, * http:// * and the Synaptics XFree86 driver by Stefan Gmeiner et. al. */ /* * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */ #include #include #include #include #include #include #include MODULE_AUTHOR("Jens Taprogge packet; synap->buttons |= packet[0] & 1; synap->buttons |= (packet[0] << 1) & (1 << 2); if (synap->caps & SYN_ECAP_4BUTTON) { if (packet[3] == 0xc2) { /* 6 buttons. not in doc */ synap->buttons |= (packet[4] << 2) & (1 << 3); synap->buttons |= (packet[5] << 3) & (1 << 4); synap->buttons |= (packet[4] << 5) & (1 << 5); synap->buttons |= (packet[5] << 6) & (1 << 6); return 0; } else { synap->buttons |= (((packet[3] & 1) && (packet[0] & 1)) << 3) & (1 << 3); synap->buttons |= (((packet[3] & 2) && (packet[0] & 2)) << 3) & (1 << 4); } } return 1; } inline static void read_pos(struct synap *synap) { unsigned char *packet = synap->packet; synap->x = ((packet[3] & 0x10) << 8) | ((packet[1] & 0x0f) << 8) | packet[4]; synap->y = ((packet[3] & 0x20) << 7) | ((packet[1] & 0xf0) << 4) | packet[5]; synap->z = packet[2]; synap->w = ((packet[0] & 0x30) >> 2) | ((packet[0] & 0x04) >> 1) | ((packet[3] & 0x04) >> 2); } #if 0 inline static void check_multifinger(struct synap *synap) { switch (synap->w) { case 0: /* two fingers */ if (synap->caps & SYN_ECAP_MULTIFINGER) input_report_abs(&(synap->dev), ABS_GESTURE, GES_TWOFINGER); break; case 1: /* three or more fingers */ if (synap->caps & SYN_ECAP_MULTIFINGER) input_report_abs(&(synap->dev), ABS_GESTURE, GES_THREEFINGER); break; case 2: /* pen */ if (synap->model & SYN_CAP_PEN) input_report_abs(&(synap->dev), ABS_GESTURE, GES_PEN); break; case 4: case 5: case 6: case 7: case 8: /* finger normal width */ if (synap->caps & SYN_ECAP_PALMDETECT) input_report_abs(&(synap->dev), ABS_GESTURE, GES_ONEFINGER); /* FIXME: 9..14 */ case 15: /* palm */ if (synap->caps & SYN_ECAP_PALMDETECT) input_report_abs(&(synap->dev), ABS_GESTURE, GES_PALM); } } #endif inline static void report_pos(struct synap *synap) { input_report_abs(&(synap->dev), ABS_X, synap->x); input_report_abs(&(synap->dev), ABS_Y, SYN_MAXCOORD - synap->y); input_report_abs(&(synap->dev), ABS_PRESSURE, synap->z); } /* * try to send as few events as possible */ static void do_buttons(struct synap *synap) { struct input_dev *dev = &synap->dev; static int oldbuttons = 0; int i; int mask; for (i = 0; i < 5; i++) { mask = 1 << i; if ((synap->buttons) & mask) { if (!(oldbuttons & mask)) input_report_key(dev, BTN_MOUSE + i, 1); } else if (oldbuttons & mask) input_report_key(dev, BTN_MOUSE + i, 0); } oldbuttons = synap->buttons; } /* * synap_process_packet() anlyzes the PS/2 mouse packet contents and * reports relevant events to the input module. */ static void synap_process_packet(struct synap *synap) { struct input_dev *dev = &synap->dev; synap->buttons = synap->buttons >> 8; if (check_buttons(synap)) { read_pos(synap); // check_multifinger(synap); report_pos(synap); } do_buttons(synap); input_sync(dev); } /* * synap_interrupt() handles incoming characters, either gathering them into * packets or passing them to the command routine as command output. */ static void synap_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { struct synap *synap = serio->private; if (synap->acking) { switch (data) { case PSMOUSE_RET_ACK: synap->ack = 1; break; case PSMOUSE_RET_NAK: synap->ack = -1; break; default: synap->ack = 1; /* Workaround for mice which don't ACK the Get ID command */ if (synap->cmdcnt) synap->cmdbuf[--synap->cmdcnt] = data; break; } synap->acking = 0; return; } if (synap->cmdcnt) { synap->cmdbuf[--synap->cmdcnt] = data; return; } if (synap->pktcnt && time_after(jiffies, synap->last + HZ/2)) { printk(KERN_WARNING "synap.c: Lost synchronization, throwing %d bytes away.\n", synap->pktcnt); synap->pktcnt = 0; } synap->last = jiffies; synap->packet[synap->pktcnt++] = data; if (synap->pktcnt == 6) { synap_process_packet(synap); synap->pktcnt = 0; return; } if (synap->pktcnt == 1 && synap->packet[0] == PSMOUSE_RET_BAT) { serio_rescan(serio); return; } } /* * synap_sendbyte() sends a byte to the mouse, and waits for acknowledge. * It doesn't handle retransmission, though it could - because when there would * be need for retransmissions, the mouse has to be replaced anyway. */ static int synap_sendbyte(struct synap *synap, unsigned char byte) { int timeout = 10000; /* 100 msec */ synap->ack = 0; synap->acking = 1; if (serio_write(synap->serio, byte)) { synap->acking = 0; return -1; } while (!synap->ack && timeout--) udelay(10); return -(synap->ack <= 0); } /* * synap_command() sends a command and its parameters to the mouse, * then waits for the response and puts it in the param array. */ static int synap_command(struct synap *synap, unsigned char *param, int command) { int timeout = 500000; /* 500 msec */ int send = (command >> 12) & 0xf; int receive = (command >> 8) & 0xf; int i; synap->cmdcnt = receive; if (command & 0xff) if (synap_sendbyte(synap, command & 0xff)) return (synap->cmdcnt = 0) - 1; for (i = 0; i < send; i++) if (synap_sendbyte(synap, param[i])) return (synap->cmdcnt = 0) - 1; while (synap->cmdcnt && timeout--) udelay(1); for (i = 0; i < receive; i++) param[i] = synap->cmdbuf[(receive - 1) - i]; if (synap->cmdcnt) return (synap->cmdcnt = 0) - 1; return 0; } /* * synap_ps2pp_cmd() sends a PS2++ command, sliced into two bit * pieces through the SETRES command. This is needed to send extended * commands to mice on notebooks that try to understand the PS/2 protocol * Ugly. */ static int synap_syn_cmd(struct synap *synap, unsigned char *param, unsigned char command) { unsigned char d; int i; if (synap_command(synap, NULL, PSMOUSE_CMD_SETSCALE11)) return -1; for (i = 6; i >= 0; i -= 2) { d = (command >> i) & 3; if (synap_command(synap, &d, PSMOUSE_CMD_SETRES)) return -1; } return 0; } /* * synap_probe() probes for a PS/2 mouse. */ static int synaptics_probe(struct synap *synap) { unsigned char param[4]; /* * First, we check if it's a mouse. It should send 0x00 or 0x03 * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. */ param[0] = param[1] = 0xa5; if (synap_command(synap, param, PSMOUSE_CMD_GETID)) return -1; if (param[0] == 0xab || param[0] == 0xac) { synap_command(synap, param, PSMOUSE_CMD_GETID2); return -1; } if (param[0] != 0x00 && param[0] != 0x03 && param[0] != 0x04) return -1; /* * Then we reset and disable the mouse so that it doesn't generate events. */ if (synap_command(synap, NULL, PSMOUSE_CMD_RESET_DIS)) return -1; param[0] = 0; synap_syn_cmd(synap, param, SYN_QUE_IDENTIFY); synap_command(synap, param, PSMOUSE_CMD_GETINFO); if (param[1] == 0x47) { synap->vendor = "Synaptics"; synap->name = "TouchPad "; synap->version = param[0] | ((param[2] & 0xf) << 8); printk(KERN_INFO "synaptics: version %d.%d\n", param[2] & 0xf, param[0]); synap_syn_cmd(synap, param, SYN_QUE_MODEL); synap_command(synap, param, PSMOUSE_CMD_GETINFO); synap->model = param[0] << 16 | param[1] << 8 | param[2]; if (param[1] == 0x47) { /* Synaptics prior to V3.2 */ synap->model = 0; } else if (param[1] & 1) /* no Synaptics at all */ return -1; printk(KERN_INFO "synaptics: model %x\n", synap->model); /* Starting with version 3.0 it is safe to query the * capabilities. If bit 7 of the first byte is set capabilities * are supported. */ if (synap->version > 0x0300) { synap_syn_cmd(synap, param, SYN_QUE_CAPABILITIES); synap_command(synap, param, PSMOUSE_CMD_GETINFO); if (param[0] & (1<<7)) synap->caps = param[0] << 8 | param[2]; else synap->caps = 0; } else synap->caps = 0; printk(KERN_INFO "synaptics: caps %x\n", synap->caps); return PSMOUSE_SYNAPTICS; } else return -1; } /* * synap_initialize() initializes the mouse to a sane state. */ static void synaptics_initialize(struct synap *synap) { unsigned char param[2]; /* * We set the mouse report rate to a highest possible value. * We try 100 first in case mouse fails to set 200. */ param[0] = 100; synap_command(synap, param, PSMOUSE_CMD_SETRATE); param[0] = 200; synap_command(synap, param, PSMOUSE_CMD_SETRATE); /* * We also set the resolution and scaling. */ param[0] = 3; synap_command(synap, param, PSMOUSE_CMD_SETRES); synap_command(synap, NULL, PSMOUSE_CMD_SETSCALE11); /* * special synaptics initilaization */ synap_syn_cmd(synap, param, SYN_MODE_HIGH_RATE | SYN_MODE_WMODE | SYN_MODE_ABSOLUTE | SYN_MODE_DISABLE_GESTURE); param[0] = 0x14; synap_command(synap, param, PSMOUSE_CMD_SETRATE); /* * We set the mouse into streaming mode. */ synap_command(synap, param, PSMOUSE_CMD_SETSTREAM); /* * Last, we enable the mouse so that we get reports from it. */ if (synap_command(synap, NULL, PSMOUSE_CMD_ENABLE)) printk(KERN_WARNING "synap.c: Failed to enable mouse on %s\n", synap->serio->phys); } /* * synap_cleanup() resets the mouse into power-on state. */ static void synap_cleanup(struct serio *serio) { struct synap *synap = serio->private; unsigned char param[2]; synap_command(synap, param, PSMOUSE_CMD_RESET_BAT); } /* * synap_disconnect() closes and frees. */ static void synap_disconnect(struct serio *serio) { struct synap *synap = serio->private; input_unregister_device(&synap->dev); serio_close(serio); kfree(synap); } /* * synap_connect() is a callback form the serio module when * an unhandled serio port is found. */ static void synaptics_connect(struct serio *serio, struct serio_dev *dev) { struct synap *synap; if ((serio->type & SERIO_TYPE) != SERIO_8042) return; if (!(synap = kmalloc(sizeof(struct synap), GFP_KERNEL))) return; memset(synap, 0, sizeof(struct synap)); init_input_dev(&synap->dev); synap->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); synap->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT); //| BIT(BTN_EXTRA) | BIT(BTN_SIDE); synap->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y); synap->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y); synap->dev.absbit[LONG(ABS_PRESSURE)] |= BIT(ABS_PRESSURE); synap->dev.absmin[ABS_X] = 0; synap->dev.absmax[ABS_X] = SYN_MAXCOORD; synap->dev.absfuzz[ABS_X] = 0x2f; synap->dev.absmin[ABS_Y] = 0; synap->dev.absmax[ABS_Y] = SYN_MAXCOORD; synap->dev.absfuzz[ABS_Y] = 0x2f; synap->dev.absmin[ABS_PRESSURE] = 0x0; synap->dev.absmax[ABS_PRESSURE] = 0xff; synap->serio = serio; synap->dev.private = synap; serio->private = synap; if (serio_open(serio, dev)) { kfree(synap); return; } if (synaptics_probe(synap) <= 0) { serio_close(serio); kfree(synap); return; } sprintf(synap->devname, "%s %s %s", synap_protocols[synap->type], synap->vendor, synap->name); sprintf(synap->phys, "%s/input0", serio->phys); synap->dev.name = synap->devname; synap->dev.phys = synap->phys; synap->dev.id.bustype = BUS_I8042; synap->dev.id.vendor = 0x0002; synap->dev.id.product = synap->type; synap->dev.id.version = synap->model; input_register_device(&synap->dev); printk(KERN_INFO "input: %s on %s\n", synap->devname, serio->phys); synaptics_initialize(synap); } static struct serio_dev synap_dev = { .interrupt = synap_interrupt, .connect = synaptics_connect, .disconnect = synap_disconnect, .cleanup = synap_cleanup, }; #ifndef MODULE static int __init synap_setup(char *str) { return 1; } #endif int __init synap_init(void) { serio_register_device(&synap_dev); return 0; } void __exit synap_exit(void) { serio_unregister_device(&synap_dev); } module_init(synap_init); module_exit(synap_exit);