Merge branch 'psmouse' into next

Merge various PS/2 handling improvements.
This commit is contained in:
Dmitry Torokhov 2018-03-17 10:58:05 -07:00
commit ba521f1bd2
11 changed files with 522 additions and 397 deletions

View file

@ -827,7 +827,7 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet; unsigned char *packet = psmouse->packet;
struct input_dev *dev = psmouse->dev; struct input_dev *dev = psmouse->dev;
struct input_dev *dev2 = priv->dev2; struct input_dev *dev2 = priv->dev2;
int x, y, z, left, right, middle; int x, y, z;
/* /*
* We can use Byte5 to distinguish if the packet is from Touchpad * We can use Byte5 to distinguish if the packet is from Touchpad
@ -847,9 +847,6 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
x = packet[1] | ((packet[3] & 0x20) << 2); x = packet[1] | ((packet[3] & 0x20) << 2);
y = packet[2] | ((packet[3] & 0x40) << 1); y = packet[2] | ((packet[3] & 0x40) << 1);
z = packet[4]; z = packet[4];
left = packet[3] & 0x01;
right = packet[3] & 0x02;
middle = packet[3] & 0x04;
/* To prevent the cursor jump when finger lifted */ /* To prevent the cursor jump when finger lifted */
if (x == 0x7F && y == 0x7F && z == 0x7F) if (x == 0x7F && y == 0x7F && z == 0x7F)
@ -859,9 +856,7 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
input_report_rel(dev2, REL_X, (char)x / 4); input_report_rel(dev2, REL_X, (char)x / 4);
input_report_rel(dev2, REL_Y, -((char)y / 4)); input_report_rel(dev2, REL_Y, -((char)y / 4));
input_report_key(dev2, BTN_LEFT, left); psmouse_report_standard_buttons(dev2, packet[3]);
input_report_key(dev2, BTN_RIGHT, right);
input_report_key(dev2, BTN_MIDDLE, middle);
input_sync(dev2); input_sync(dev2);
return; return;
@ -871,8 +866,6 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
x = packet[1] | ((packet[3] & 0x78) << 4); x = packet[1] | ((packet[3] & 0x78) << 4);
y = packet[2] | ((packet[4] & 0x78) << 4); y = packet[2] | ((packet[4] & 0x78) << 4);
z = packet[5]; z = packet[5];
left = packet[3] & 0x01;
right = packet[3] & 0x02;
if (z > 30) if (z > 30)
input_report_key(dev, BTN_TOUCH, 1); input_report_key(dev, BTN_TOUCH, 1);
@ -888,8 +881,8 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
input_report_key(dev, BTN_TOOL_FINGER, z > 0); input_report_key(dev, BTN_TOOL_FINGER, z > 0);
/* v6 touchpad does not have middle button */ /* v6 touchpad does not have middle button */
input_report_key(dev, BTN_LEFT, left); packet[3] &= ~BIT(2);
input_report_key(dev, BTN_RIGHT, right); psmouse_report_standard_buttons(dev2, packet[3]);
input_sync(dev); input_sync(dev);
} }
@ -1098,7 +1091,7 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
struct alps_data *priv = psmouse->private; struct alps_data *priv = psmouse->private;
unsigned char *packet = psmouse->packet; unsigned char *packet = psmouse->packet;
struct input_dev *dev2 = priv->dev2; struct input_dev *dev2 = priv->dev2;
int x, y, z, left, right, middle; int x, y, z;
/* It should be a DualPoint when received trackstick packet */ /* It should be a DualPoint when received trackstick packet */
if (!(priv->flags & ALPS_DUALPOINT)) { if (!(priv->flags & ALPS_DUALPOINT)) {
@ -1112,16 +1105,10 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
((packet[3] & 0x20) << 1); ((packet[3] & 0x20) << 1);
z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1); z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1);
left = (packet[1] & 0x01);
right = (packet[1] & 0x02) >> 1;
middle = (packet[1] & 0x04) >> 2;
input_report_rel(dev2, REL_X, (char)x); input_report_rel(dev2, REL_X, (char)x);
input_report_rel(dev2, REL_Y, -((char)y)); input_report_rel(dev2, REL_Y, -((char)y));
input_report_key(dev2, BTN_LEFT, left); psmouse_report_standard_buttons(dev2, packet[1]);
input_report_key(dev2, BTN_RIGHT, right);
input_report_key(dev2, BTN_MIDDLE, middle);
input_sync(dev2); input_sync(dev2);
} }
@ -1503,10 +1490,7 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
alps_report_buttons(dev, dev2, alps_report_buttons(dev, dev2,
packet[0] & 1, packet[0] & 2, packet[0] & 4); packet[0] & 1, packet[0] & 2, packet[0] & 4);
input_report_rel(dev, REL_X, psmouse_report_standard_motion(dev, packet);
packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
input_report_rel(dev, REL_Y,
packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
input_sync(dev); input_sync(dev);
} }

View file

@ -35,7 +35,7 @@
static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,
unsigned char *param) unsigned char *param)
{ {
if (psmouse_sliced_command(psmouse, c) || if (ps2_sliced_command(&psmouse->ps2dev, c) ||
ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c);
return -1; return -1;
@ -107,8 +107,8 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
switch (etd->hw_version) { switch (etd->hw_version) {
case 1: case 1:
if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) || if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_READ) ||
psmouse_sliced_command(psmouse, reg) || ps2_sliced_command(&psmouse->ps2dev, reg) ||
ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
rc = -1; rc = -1;
} }
@ -162,9 +162,9 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
switch (etd->hw_version) { switch (etd->hw_version) {
case 1: case 1:
if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) || if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_WRITE) ||
psmouse_sliced_command(psmouse, reg) || ps2_sliced_command(&psmouse->ps2dev, reg) ||
psmouse_sliced_command(psmouse, val) || ps2_sliced_command(&psmouse->ps2dev, val) ||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) {
rc = -1; rc = -1;
} }
@ -279,8 +279,8 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); psmouse_report_standard_buttons(dev, packet[0]);
if (etd->fw_version < 0x020000 && if (etd->fw_version < 0x020000 &&
(etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) { (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
@ -390,8 +390,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
input_report_key(dev, BTN_LEFT, packet[0] & 0x01); psmouse_report_standard_buttons(dev, packet[0]);
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
if (etd->reports_pressure) { if (etd->reports_pressure) {
input_report_abs(dev, ABS_PRESSURE, pres); input_report_abs(dev, ABS_PRESSURE, pres);
input_report_abs(dev, ABS_TOOL_WIDTH, width); input_report_abs(dev, ABS_TOOL_WIDTH, width);
@ -434,9 +433,7 @@ static void elantech_report_trackpoint(struct psmouse *psmouse,
x = packet[4] - (int)((packet[1]^0x80) << 1); x = packet[4] - (int)((packet[1]^0x80) << 1);
y = (int)((packet[2]^0x80) << 1) - packet[5]; y = (int)((packet[2]^0x80) << 1) - packet[5];
input_report_key(tp_dev, BTN_LEFT, packet[0] & 0x01); psmouse_report_standard_buttons(tp_dev, packet[0]);
input_report_key(tp_dev, BTN_RIGHT, packet[0] & 0x02);
input_report_key(tp_dev, BTN_MIDDLE, packet[0] & 0x04);
input_report_rel(tp_dev, REL_X, x); input_report_rel(tp_dev, REL_X, x);
input_report_rel(tp_dev, REL_Y, y); input_report_rel(tp_dev, REL_Y, y);
@ -526,12 +523,10 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
/* For clickpads map both buttons to BTN_LEFT */ /* For clickpads map both buttons to BTN_LEFT */
if (etd->fw_version & 0x001000) { if (etd->fw_version & 0x001000)
input_report_key(dev, BTN_LEFT, packet[0] & 0x03); input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
} else { else
input_report_key(dev, BTN_LEFT, packet[0] & 0x01); psmouse_report_standard_buttons(dev, packet[0]);
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
}
input_report_abs(dev, ABS_PRESSURE, pres); input_report_abs(dev, ABS_PRESSURE, pres);
input_report_abs(dev, ABS_TOOL_WIDTH, width); input_report_abs(dev, ABS_TOOL_WIDTH, width);
@ -546,13 +541,10 @@ static void elantech_input_sync_v4(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet; unsigned char *packet = psmouse->packet;
/* For clickpads map both buttons to BTN_LEFT */ /* For clickpads map both buttons to BTN_LEFT */
if (etd->fw_version & 0x001000) { if (etd->fw_version & 0x001000)
input_report_key(dev, BTN_LEFT, packet[0] & 0x03); input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
} else { else
input_report_key(dev, BTN_LEFT, packet[0] & 0x01); psmouse_report_standard_buttons(dev, packet[0]);
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
input_report_key(dev, BTN_MIDDLE, packet[0] & 0x04);
}
input_mt_report_pointer_emulation(dev, true); input_mt_report_pointer_emulation(dev, true);
input_sync(dev); input_sync(dev);

View file

@ -17,6 +17,7 @@
#include <linux/libps2.h> #include <linux/libps2.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h>
#include "psmouse.h" #include "psmouse.h"
#include "lifebook.h" #include "lifebook.h"
@ -136,7 +137,7 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
struct lifebook_data *priv = psmouse->private; struct lifebook_data *priv = psmouse->private;
struct input_dev *dev1 = psmouse->dev; struct input_dev *dev1 = psmouse->dev;
struct input_dev *dev2 = priv ? priv->dev2 : NULL; struct input_dev *dev2 = priv ? priv->dev2 : NULL;
unsigned char *packet = psmouse->packet; u8 *packet = psmouse->packet;
bool relative_packet = packet[0] & 0x08; bool relative_packet = packet[0] & 0x08;
if (relative_packet || !lifebook_use_6byte_proto) { if (relative_packet || !lifebook_use_6byte_proto) {
@ -188,14 +189,10 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
} }
if (dev2) { if (dev2) {
if (relative_packet) { if (relative_packet)
input_report_rel(dev2, REL_X, psmouse_report_standard_motion(dev2, packet);
((packet[0] & 0x10) ? packet[1] - 256 : packet[1]));
input_report_rel(dev2, REL_Y, psmouse_report_standard_buttons(dev2, packet[0]);
-(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2]));
}
input_report_key(dev2, BTN_LEFT, packet[0] & 0x01);
input_report_key(dev2, BTN_RIGHT, packet[0] & 0x02);
input_sync(dev2); input_sync(dev2);
} }
@ -205,10 +202,12 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
static int lifebook_absolute_mode(struct psmouse *psmouse) static int lifebook_absolute_mode(struct psmouse *psmouse)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param; u8 param;
int error;
if (psmouse_reset(psmouse)) error = psmouse_reset(psmouse);
return -1; if (error)
return error;
/* /*
* Enable absolute output -- ps2_command fails always but if * Enable absolute output -- ps2_command fails always but if
@ -224,15 +223,15 @@ static int lifebook_absolute_mode(struct psmouse *psmouse)
static void lifebook_relative_mode(struct psmouse *psmouse) static void lifebook_relative_mode(struct psmouse *psmouse)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param = 0x06; u8 param = 0x06;
ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES); ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
} }
static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution) static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{ {
static const unsigned char params[] = { 0, 1, 2, 2, 3 }; static const u8 params[] = { 0, 1, 2, 2, 3 };
unsigned char p; u8 p;
if (resolution == 0 || resolution > 400) if (resolution == 0 || resolution > 400)
resolution = 400; resolution = 400;
@ -257,11 +256,11 @@ static void lifebook_disconnect(struct psmouse *psmouse)
int lifebook_detect(struct psmouse *psmouse, bool set_properties) int lifebook_detect(struct psmouse *psmouse, bool set_properties)
{ {
if (!lifebook_present) if (!lifebook_present)
return -1; return -ENXIO;
if (desired_serio_phys && if (desired_serio_phys &&
strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys)) strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys))
return -1; return -ENXIO;
if (set_properties) { if (set_properties) {
psmouse->vendor = "Fujitsu"; psmouse->vendor = "Fujitsu";
@ -294,10 +293,10 @@ static int lifebook_create_relative_device(struct psmouse *psmouse)
dev2->id.version = 0x0000; dev2->id.version = 0x0000;
dev2->dev.parent = &psmouse->ps2dev.serio->dev; dev2->dev.parent = &psmouse->ps2dev.serio->dev;
dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); input_set_capability(dev2, EV_REL, REL_X);
dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); input_set_capability(dev2, EV_REL, REL_Y);
dev2->keybit[BIT_WORD(BTN_LEFT)] = input_set_capability(dev2, EV_KEY, BTN_LEFT);
BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); input_set_capability(dev2, EV_KEY, BTN_RIGHT);
error = input_register_device(priv->dev2); error = input_register_device(priv->dev2);
if (error) if (error)
@ -316,21 +315,26 @@ int lifebook_init(struct psmouse *psmouse)
{ {
struct input_dev *dev1 = psmouse->dev; struct input_dev *dev1 = psmouse->dev;
int max_coord = lifebook_use_6byte_proto ? 4096 : 1024; int max_coord = lifebook_use_6byte_proto ? 4096 : 1024;
int error;
if (lifebook_absolute_mode(psmouse)) error = lifebook_absolute_mode(psmouse);
return -1; if (error)
return error;
dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); /* Clear default capabilities */
dev1->relbit[0] = 0; bitmap_zero(dev1->evbit, EV_CNT);
dev1->keybit[BIT_WORD(BTN_MOUSE)] = 0; bitmap_zero(dev1->relbit, REL_CNT);
dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); bitmap_zero(dev1->keybit, KEY_CNT);
input_set_capability(dev1, EV_KEY, BTN_TOUCH);
input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0); input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0);
input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0); input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0);
if (!desired_serio_phys) { if (!desired_serio_phys) {
if (lifebook_create_relative_device(psmouse)) { error = lifebook_create_relative_device(psmouse);
if (error) {
lifebook_relative_mode(psmouse); lifebook_relative_mode(psmouse);
return -1; return error;
} }
} }

View file

@ -9,9 +9,11 @@
* the Free Software Foundation. * the Free Software Foundation.
*/ */
#include <linux/bitops.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/serio.h> #include <linux/serio.h>
#include <linux/libps2.h> #include <linux/libps2.h>
#include <linux/types.h>
#include "psmouse.h" #include "psmouse.h"
#include "logips2pp.h" #include "logips2pp.h"
@ -22,12 +24,12 @@
#define PS2PP_KIND_TRACKMAN 4 #define PS2PP_KIND_TRACKMAN 4
/* Logitech mouse features */ /* Logitech mouse features */
#define PS2PP_WHEEL 0x01 #define PS2PP_WHEEL BIT(0)
#define PS2PP_HWHEEL 0x02 #define PS2PP_HWHEEL BIT(1)
#define PS2PP_SIDE_BTN 0x04 #define PS2PP_SIDE_BTN BIT(2)
#define PS2PP_EXTRA_BTN 0x08 #define PS2PP_EXTRA_BTN BIT(3)
#define PS2PP_TASK_BTN 0x10 #define PS2PP_TASK_BTN BIT(4)
#define PS2PP_NAV_BTN 0x20 #define PS2PP_NAV_BTN BIT(5)
struct ps2pp_info { struct ps2pp_info {
u8 model; u8 model;
@ -42,7 +44,7 @@ struct ps2pp_info {
static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse) static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
{ {
struct input_dev *dev = psmouse->dev; struct input_dev *dev = psmouse->dev;
unsigned char *packet = psmouse->packet; u8 *packet = psmouse->packet;
if (psmouse->pktcnt < 3) if (psmouse->pktcnt < 3)
return PSMOUSE_GOOD_DATA; return PSMOUSE_GOOD_DATA;
@ -58,28 +60,30 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
case 0x0d: /* Mouse extra info */ case 0x0d: /* Mouse extra info */
input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, input_report_rel(dev,
(int) (packet[2] & 8) - (int) (packet[2] & 7)); packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1); -sign_extend32(packet[2], 3));
input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1); input_report_key(dev, BTN_SIDE, packet[2] & BIT(4));
input_report_key(dev, BTN_EXTRA, packet[2] & BIT(5));
break; break;
case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */ case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
input_report_key(dev, BTN_SIDE, (packet[2]) & 1); input_report_key(dev, BTN_SIDE, packet[2] & BIT(0));
input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1); input_report_key(dev, BTN_EXTRA, packet[2] & BIT(1));
input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1); input_report_key(dev, BTN_TASK, packet[2] & BIT(2));
input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1); input_report_key(dev, BTN_BACK, packet[2] & BIT(3));
input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1); input_report_key(dev, BTN_FORWARD, packet[2] & BIT(4));
break; break;
case 0x0f: /* TouchPad extra info */ case 0x0f: /* TouchPad extra info */
input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, input_report_rel(dev,
(int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7)); packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
packet[0] = packet[2] | 0x08; -sign_extend32(packet[2] >> 4, 3));
packet[0] = packet[2] | BIT(3);
break; break;
default: default:
@ -88,16 +92,14 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
(packet[1] >> 4) | (packet[0] & 0x30)); (packet[1] >> 4) | (packet[0] & 0x30));
break; break;
} }
psmouse_report_standard_buttons(dev, packet[0]);
} else { } else {
/* Standard PS/2 motion data */ /* Standard PS/2 motion data */
input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); psmouse_report_standard_packet(dev, packet);
input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
} }
input_report_key(dev, BTN_LEFT, packet[0] & 1);
input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
input_sync(dev); input_sync(dev);
return PSMOUSE_FULL_PACKET; return PSMOUSE_FULL_PACKET;
@ -111,13 +113,17 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
* Ugly. * Ugly.
*/ */
static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command) static int ps2pp_cmd(struct psmouse *psmouse, u8 *param, u8 command)
{ {
if (psmouse_sliced_command(psmouse, command)) int error;
return -1;
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300)) error = ps2_sliced_command(&psmouse->ps2dev, command);
return -1; if (error)
return error;
error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300);
if (error)
return error;
return 0; return 0;
} }
@ -133,7 +139,7 @@ static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned cha
static void ps2pp_set_smartscroll(struct psmouse *psmouse, bool smartscroll) static void ps2pp_set_smartscroll(struct psmouse *psmouse, bool smartscroll)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4]; u8 param[4];
ps2pp_cmd(psmouse, param, 0x32); ps2pp_cmd(psmouse, param, 0x32);
@ -171,7 +177,7 @@ static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data,
} }
PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL, PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL,
ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll); ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll);
/* /*
* Support 800 dpi resolution _only_ if the user wants it (there are good * Support 800 dpi resolution _only_ if the user wants it (there are good
@ -179,11 +185,12 @@ PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL,
* also good reasons to use it, let the user decide). * also good reasons to use it, let the user decide).
*/ */
static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolution) static void ps2pp_set_resolution(struct psmouse *psmouse,
unsigned int resolution)
{ {
if (resolution > 400) { if (resolution > 400) {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param = 3; u8 param = 3;
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
@ -196,7 +203,8 @@ static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolutio
static void ps2pp_disconnect(struct psmouse *psmouse) static void ps2pp_disconnect(struct psmouse *psmouse)
{ {
device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll.dattr); device_remove_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_smartscroll.dattr);
} }
static const struct ps2pp_info *get_model_info(unsigned char model) static const struct ps2pp_info *get_model_info(unsigned char model)
@ -269,24 +277,24 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
struct input_dev *input_dev = psmouse->dev; struct input_dev *input_dev = psmouse->dev;
if (model_info->features & PS2PP_SIDE_BTN) if (model_info->features & PS2PP_SIDE_BTN)
__set_bit(BTN_SIDE, input_dev->keybit); input_set_capability(input_dev, EV_KEY, BTN_SIDE);
if (model_info->features & PS2PP_EXTRA_BTN) if (model_info->features & PS2PP_EXTRA_BTN)
__set_bit(BTN_EXTRA, input_dev->keybit); input_set_capability(input_dev, EV_KEY, BTN_EXTRA);
if (model_info->features & PS2PP_TASK_BTN) if (model_info->features & PS2PP_TASK_BTN)
__set_bit(BTN_TASK, input_dev->keybit); input_set_capability(input_dev, EV_KEY, BTN_TASK);
if (model_info->features & PS2PP_NAV_BTN) { if (model_info->features & PS2PP_NAV_BTN) {
__set_bit(BTN_FORWARD, input_dev->keybit); input_set_capability(input_dev, EV_KEY, BTN_FORWARD);
__set_bit(BTN_BACK, input_dev->keybit); input_set_capability(input_dev, EV_KEY, BTN_BACK);
} }
if (model_info->features & PS2PP_WHEEL) if (model_info->features & PS2PP_WHEEL)
__set_bit(REL_WHEEL, input_dev->relbit); input_set_capability(input_dev, EV_REL, REL_WHEEL);
if (model_info->features & PS2PP_HWHEEL) if (model_info->features & PS2PP_HWHEEL)
__set_bit(REL_HWHEEL, input_dev->relbit); input_set_capability(input_dev, EV_REL, REL_HWHEEL);
switch (model_info->kind) { switch (model_info->kind) {
@ -318,6 +326,30 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
} }
} }
static int ps2pp_setup_protocol(struct psmouse *psmouse,
const struct ps2pp_info *model_info)
{
int error;
psmouse->protocol_handler = ps2pp_process_byte;
psmouse->pktsize = 3;
if (model_info->kind != PS2PP_KIND_TP3) {
psmouse->set_resolution = ps2pp_set_resolution;
psmouse->disconnect = ps2pp_disconnect;
error = device_create_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_smartscroll.dattr);
if (error) {
psmouse_err(psmouse,
"failed to create smartscroll sysfs attribute, error: %d\n",
error);
return error;
}
}
return 0;
}
/* /*
* Logitech magic init. Detect whether the mouse is a Logitech one * Logitech magic init. Detect whether the mouse is a Logitech one
@ -328,9 +360,9 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
int ps2pp_detect(struct psmouse *psmouse, bool set_properties) int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
unsigned char model, buttons;
const struct ps2pp_info *model_info; const struct ps2pp_info *model_info;
u8 param[4];
u8 model, buttons;
bool use_ps2pp = false; bool use_ps2pp = false;
int error; int error;
@ -346,7 +378,7 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
buttons = param[1]; buttons = param[1];
if (!model || !buttons) if (!model || !buttons)
return -1; return -ENXIO;
model_info = get_model_info(model); model_info = get_model_info(model);
if (model_info) { if (model_info) {
@ -368,7 +400,8 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
param[0] = 0; param[0] = 0;
if (!ps2_command(ps2dev, param, 0x13d1) && if (!ps2_command(ps2dev, param, 0x13d1) &&
param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) { param[0] == 0x06 && param[1] == 0x00 &&
param[2] == 0x14) {
use_ps2pp = true; use_ps2pp = true;
} }
@ -387,7 +420,9 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
} }
} else { } else {
psmouse_warn(psmouse, "Detected unknown Logitech mouse model %d\n", model); psmouse_warn(psmouse,
"Detected unknown Logitech mouse model %d\n",
model);
} }
if (set_properties) { if (set_properties) {
@ -395,31 +430,18 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
psmouse->model = model; psmouse->model = model;
if (use_ps2pp) { if (use_ps2pp) {
psmouse->protocol_handler = ps2pp_process_byte; error = ps2pp_setup_protocol(psmouse, model_info);
psmouse->pktsize = 3; if (error)
return error;
if (model_info->kind != PS2PP_KIND_TP3) {
psmouse->set_resolution = ps2pp_set_resolution;
psmouse->disconnect = ps2pp_disconnect;
error = device_create_file(&ps2dev->serio->dev,
&psmouse_attr_smartscroll.dattr);
if (error) {
psmouse_err(psmouse,
"failed to create smartscroll sysfs attribute, error: %d\n",
error);
return -1;
}
}
} }
if (buttons >= 3) if (buttons >= 3)
__set_bit(BTN_MIDDLE, psmouse->dev->keybit); input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE);
if (model_info) if (model_info)
ps2pp_set_model_properties(psmouse, model_info, use_ps2pp); ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
} }
return use_ps2pp ? 0 : -1; return use_ps2pp ? 0 : -ENXIO;
} }

View file

@ -14,6 +14,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define psmouse_fmt(fmt) fmt #define psmouse_fmt(fmt) fmt
#include <linux/bitops.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -23,6 +24,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/libps2.h> #include <linux/libps2.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/types.h>
#include "psmouse.h" #include "psmouse.h"
#include "synaptics.h" #include "synaptics.h"
@ -68,6 +70,10 @@ static bool psmouse_smartscroll = true;
module_param_named(smartscroll, psmouse_smartscroll, bool, 0644); module_param_named(smartscroll, psmouse_smartscroll, bool, 0644);
MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled.");
static bool psmouse_a4tech_2wheels;
module_param_named(a4tech_workaround, psmouse_a4tech_2wheels, bool, 0644);
MODULE_PARM_DESC(a4tech_workaround, "A4Tech second scroll wheel workaround, 1 = enabled, 0 = disabled (default).");
static unsigned int psmouse_resetafter = 5; static unsigned int psmouse_resetafter = 5;
module_param_named(resetafter, psmouse_resetafter, uint, 0644); module_param_named(resetafter, psmouse_resetafter, uint, 0644);
MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never)."); MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");
@ -116,13 +122,30 @@ static DEFINE_MUTEX(psmouse_mutex);
static struct workqueue_struct *kpsmoused_wq; static struct workqueue_struct *kpsmoused_wq;
static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons) void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
{ {
input_report_key(dev, BTN_LEFT, buttons & BIT(0)); input_report_key(dev, BTN_LEFT, buttons & BIT(0));
input_report_key(dev, BTN_MIDDLE, buttons & BIT(2)); input_report_key(dev, BTN_MIDDLE, buttons & BIT(2));
input_report_key(dev, BTN_RIGHT, buttons & BIT(1)); input_report_key(dev, BTN_RIGHT, buttons & BIT(1));
} }
void psmouse_report_standard_motion(struct input_dev *dev, u8 *packet)
{
int x, y;
x = packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0;
y = packet[2] ? packet[2] - ((packet[0] << 3) & 0x100) : 0;
input_report_rel(dev, REL_X, x);
input_report_rel(dev, REL_Y, -y);
}
void psmouse_report_standard_packet(struct input_dev *dev, u8 *packet)
{
psmouse_report_standard_buttons(dev, packet[0]);
psmouse_report_standard_motion(dev, packet);
}
/* /*
* psmouse_process_byte() analyzes the PS/2 data stream and reports * psmouse_process_byte() analyzes the PS/2 data stream and reports
* relevant events to the input module once full packet has arrived. * relevant events to the input module once full packet has arrived.
@ -130,7 +153,8 @@ static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
{ {
struct input_dev *dev = psmouse->dev; struct input_dev *dev = psmouse->dev;
unsigned char *packet = psmouse->packet; u8 *packet = psmouse->packet;
int wheel;
if (psmouse->pktcnt < psmouse->pktsize) if (psmouse->pktcnt < psmouse->pktsize)
return PSMOUSE_GOOD_DATA; return PSMOUSE_GOOD_DATA;
@ -140,39 +164,52 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
switch (psmouse->protocol->type) { switch (psmouse->protocol->type) {
case PSMOUSE_IMPS: case PSMOUSE_IMPS:
/* IntelliMouse has scroll wheel */ /* IntelliMouse has scroll wheel */
input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); input_report_rel(dev, REL_WHEEL, -(s8) packet[3]);
break; break;
case PSMOUSE_IMEX: case PSMOUSE_IMEX:
/* Scroll wheel and buttons on IntelliMouse Explorer */ /* Scroll wheel and buttons on IntelliMouse Explorer */
switch (packet[3] & 0xC0) { switch (packet[3] & 0xC0) {
case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */ case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); input_report_rel(dev, REL_WHEEL,
-sign_extend32(packet[3], 5));
break; break;
case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */ case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */
input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); input_report_rel(dev, REL_HWHEEL,
-sign_extend32(packet[3], 5));
break; break;
case 0x00: case 0x00:
case 0xC0: case 0xC0:
input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7)); wheel = sign_extend32(packet[3], 3);
input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); /*
* Some A4Tech mice have two scroll wheels, with first
* one reporting +/-1 in the lower nibble, and second
* one reporting +/-2.
*/
if (psmouse_a4tech_2wheels && abs(wheel) > 1)
input_report_rel(dev, REL_HWHEEL, wheel / 2);
else
input_report_rel(dev, REL_WHEEL, -wheel);
input_report_key(dev, BTN_SIDE, BIT(4));
input_report_key(dev, BTN_EXTRA, BIT(5));
break; break;
} }
break; break;
case PSMOUSE_GENPS: case PSMOUSE_GENPS:
/* Report scroll buttons on NetMice */ /* Report scroll buttons on NetMice */
input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); input_report_rel(dev, REL_WHEEL, -(s8) packet[3]);
/* Extra buttons on Genius NewNet 3D */ /* Extra buttons on Genius NewNet 3D */
input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1); input_report_key(dev, BTN_SIDE, BIT(6));
input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1); input_report_key(dev, BTN_EXTRA, BIT(7));
break; break;
case PSMOUSE_THINKPS: case PSMOUSE_THINKPS:
/* Extra button on ThinkingMouse */ /* Extra button on ThinkingMouse */
input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1); input_report_key(dev, BTN_EXTRA, BIT(3));
/* /*
* Without this bit of weirdness moving up gives wildly * Without this bit of weirdness moving up gives wildly
@ -186,8 +223,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
* Cortron PS2 Trackball reports SIDE button in the * Cortron PS2 Trackball reports SIDE button in the
* 4th bit of the first byte. * 4th bit of the first byte.
*/ */
input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1); input_report_key(dev, BTN_SIDE, BIT(3));
packet[0] |= 0x08; packet[0] |= BIT(3);
break; break;
default: default:
@ -195,11 +232,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
} }
/* Generic PS/2 Mouse */ /* Generic PS/2 Mouse */
psmouse_report_standard_buttons(dev, packet[0] |= psmouse->extra_buttons;
packet[0] | psmouse->extra_buttons); psmouse_report_standard_packet(dev, packet);
input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
input_sync(dev); input_sync(dev);
@ -255,7 +289,7 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
psmouse_notice(psmouse, psmouse_notice(psmouse,
"issuing reconnect request\n"); "issuing reconnect request\n");
serio_reconnect(psmouse->ps2dev.serio); serio_reconnect(psmouse->ps2dev.serio);
return -1; return -EIO;
} }
} }
psmouse->pktcnt = 0; psmouse->pktcnt = 0;
@ -306,7 +340,7 @@ static void psmouse_handle_oob_data(struct psmouse *psmouse, u8 data)
* for normal processing or gathering them as command response. * for normal processing or gathering them as command response.
*/ */
static irqreturn_t psmouse_interrupt(struct serio *serio, static irqreturn_t psmouse_interrupt(struct serio *serio,
unsigned char data, unsigned int flags) u8 data, unsigned int flags)
{ {
struct psmouse *psmouse = serio_get_drvdata(serio); struct psmouse *psmouse = serio_get_drvdata(serio);
@ -397,41 +431,20 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/*
* psmouse_sliced_command() sends an extended PS/2 command to the mouse
* using sliced syntax, understood by advanced devices, such as Logitech
* or Synaptics touchpads. The command is encoded as:
* 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
* is the command.
*/
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command)
{
int i;
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
return -1;
for (i = 6; i >= 0; i -= 2) {
unsigned char d = (command >> i) & 3;
if (ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES))
return -1;
}
return 0;
}
/* /*
* psmouse_reset() resets the mouse into power-on state. * psmouse_reset() resets the mouse into power-on state.
*/ */
int psmouse_reset(struct psmouse *psmouse) int psmouse_reset(struct psmouse *psmouse)
{ {
unsigned char param[2]; u8 param[2];
int error;
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT)) error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT);
return -1; if (error)
return error;
if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID) if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID)
return -1; return -EIO;
return 0; return 0;
} }
@ -441,8 +454,8 @@ int psmouse_reset(struct psmouse *psmouse)
*/ */
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{ {
static const unsigned char params[] = { 0, 1, 2, 2, 3 }; static const u8 params[] = { 0, 1, 2, 2, 3 };
unsigned char p; u8 p;
if (resolution == 0 || resolution > 200) if (resolution == 0 || resolution > 200)
resolution = 200; resolution = 200;
@ -457,11 +470,12 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
*/ */
static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
{ {
static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; static const u8 rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
unsigned char r; u8 r;
int i = 0; int i = 0;
while (rates[i] > rate) i++; while (rates[i] > rate)
i++;
r = rates[i]; r = rates[i];
ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE); ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE);
psmouse->rate = r; psmouse->rate = r;
@ -533,7 +547,7 @@ bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[])
static int genius_detect(struct psmouse *psmouse, bool set_properties) static int genius_detect(struct psmouse *psmouse, bool set_properties)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4]; u8 param[4];
param[0] = 3; param[0] = 3;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
@ -543,7 +557,7 @@ static int genius_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55) if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55)
return -1; return -ENODEV;
if (set_properties) { if (set_properties) {
__set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
@ -565,7 +579,7 @@ static int genius_detect(struct psmouse *psmouse, bool set_properties)
static int intellimouse_detect(struct psmouse *psmouse, bool set_properties) static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2]; u8 param[2];
param[0] = 200; param[0] = 200;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
@ -576,7 +590,7 @@ static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 3) if (param[0] != 3)
return -1; return -ENODEV;
if (set_properties) { if (set_properties) {
__set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
@ -598,7 +612,7 @@ static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2]; u8 param[2];
intellimouse_detect(psmouse, 0); intellimouse_detect(psmouse, 0);
@ -611,7 +625,7 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 4) if (param[0] != 4)
return -1; return -ENODEV;
/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */ /* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
param[0] = 200; param[0] = 200;
@ -644,8 +658,8 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
static int thinking_detect(struct psmouse *psmouse, bool set_properties) static int thinking_detect(struct psmouse *psmouse, bool set_properties)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2]; u8 param[2];
static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 }; static const u8 seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
int i; int i;
param[0] = 10; param[0] = 10;
@ -659,7 +673,7 @@ static int thinking_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 2) if (param[0] != 2)
return -1; return -ENODEV;
if (set_properties) { if (set_properties) {
__set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
@ -687,7 +701,7 @@ static int ps2bare_detect(struct psmouse *psmouse, bool set_properties)
* We have no way of figuring true number of buttons so let's * We have no way of figuring true number of buttons so let's
* assume that the device has 3. * assume that the device has 3.
*/ */
__set_bit(BTN_MIDDLE, psmouse->dev->keybit); input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE);
} }
return 0; return 0;
@ -942,20 +956,17 @@ static void psmouse_apply_defaults(struct psmouse *psmouse)
{ {
struct input_dev *input_dev = psmouse->dev; struct input_dev *input_dev = psmouse->dev;
memset(input_dev->evbit, 0, sizeof(input_dev->evbit)); bitmap_zero(input_dev->evbit, EV_CNT);
memset(input_dev->keybit, 0, sizeof(input_dev->keybit)); bitmap_zero(input_dev->keybit, KEY_CNT);
memset(input_dev->relbit, 0, sizeof(input_dev->relbit)); bitmap_zero(input_dev->relbit, REL_CNT);
memset(input_dev->absbit, 0, sizeof(input_dev->absbit)); bitmap_zero(input_dev->absbit, ABS_CNT);
memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit)); bitmap_zero(input_dev->mscbit, MSC_CNT);
__set_bit(EV_KEY, input_dev->evbit); input_set_capability(input_dev, EV_KEY, BTN_LEFT);
__set_bit(EV_REL, input_dev->evbit); input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
__set_bit(BTN_LEFT, input_dev->keybit); input_set_capability(input_dev, EV_REL, REL_X);
__set_bit(BTN_RIGHT, input_dev->keybit); input_set_capability(input_dev, EV_REL, REL_Y);
__set_bit(REL_X, input_dev->relbit);
__set_bit(REL_Y, input_dev->relbit);
__set_bit(INPUT_PROP_POINTER, input_dev->propbit); __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
@ -1225,7 +1236,8 @@ static int psmouse_extensions(struct psmouse *psmouse,
static int psmouse_probe(struct psmouse *psmouse) static int psmouse_probe(struct psmouse *psmouse)
{ {
struct ps2dev *ps2dev = &psmouse->ps2dev; struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2]; u8 param[2];
int error;
/* /*
* First, we check if it's a mouse. It should send 0x00 or 0x03 in * First, we check if it's a mouse. It should send 0x00 or 0x03 in
@ -1234,20 +1246,22 @@ static int psmouse_probe(struct psmouse *psmouse)
* subsequent ID queries, probably due to a firmware bug. * subsequent ID queries, probably due to a firmware bug.
*/ */
param[0] = 0xa5; param[0] = 0xa5;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) error = ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
return -1; if (error)
return error;
if (param[0] != 0x00 && param[0] != 0x03 && if (param[0] != 0x00 && param[0] != 0x03 &&
param[0] != 0x04 && param[0] != 0xff) param[0] != 0x04 && param[0] != 0xff)
return -1; return -ENODEV;
/* /*
* Then we reset and disable the mouse so that it doesn't generate * Then we reset and disable the mouse so that it doesn't generate
* events. * events.
*/ */
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS)) error = ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
psmouse_warn(psmouse, "Failed to reset mouse on %s\n", if (error)
ps2dev->serio->phys); psmouse_warn(psmouse, "Failed to reset mouse on %s: %d\n",
ps2dev->serio->phys, error);
return 0; return 0;
} }
@ -1288,10 +1302,13 @@ int psmouse_activate(struct psmouse *psmouse)
*/ */
int psmouse_deactivate(struct psmouse *psmouse) int psmouse_deactivate(struct psmouse *psmouse)
{ {
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) { int error;
psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n",
psmouse->ps2dev.serio->phys); error = ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE);
return -1; if (error) {
psmouse_warn(psmouse, "Failed to deactivate mouse on %s: %d\n",
psmouse->ps2dev.serio->phys, error);
return error;
} }
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);

View file

@ -131,7 +131,6 @@ struct psmouse {
void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
unsigned long delay); unsigned long delay);
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
int psmouse_reset(struct psmouse *psmouse); int psmouse_reset(struct psmouse *psmouse);
void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state); void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state);
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
@ -140,6 +139,10 @@ int psmouse_activate(struct psmouse *psmouse);
int psmouse_deactivate(struct psmouse *psmouse); int psmouse_deactivate(struct psmouse *psmouse);
bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]); bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]);
void psmouse_report_standard_buttons(struct input_dev *, u8 buttons);
void psmouse_report_standard_motion(struct input_dev *, u8 *packet);
void psmouse_report_standard_packet(struct input_dev *, u8 *packet);
struct psmouse_attribute { struct psmouse_attribute {
struct device_attribute dattr; struct device_attribute dattr;
void *data; void *data;

View file

@ -710,7 +710,6 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet; unsigned char *packet = psmouse->packet;
unsigned char button_status = 0, lscroll = 0, rscroll = 0; unsigned char button_status = 0, lscroll = 0, rscroll = 0;
unsigned short abs_x, abs_y, fgrs = 0; unsigned short abs_x, abs_y, fgrs = 0;
int rel_x, rel_y;
if (psmouse->pktcnt < 4) if (psmouse->pktcnt < 4)
return PSMOUSE_GOOD_DATA; return PSMOUSE_GOOD_DATA;
@ -840,15 +839,7 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
/* /*
* Standard PS/2 Mouse * Standard PS/2 Mouse
*/ */
input_report_key(dev, BTN_LEFT, packet[0] & 1); psmouse_report_standard_packet(dev, packet);
input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
rel_x = packet[1] ? (int)packet[1] - (int)((packet[0] << 4) & 0x100) : 0;
rel_y = packet[2] ? (int)((packet[0] << 3) & 0x100) - (int)packet[2] : 0;
input_report_rel(dev, REL_X, rel_x);
input_report_rel(dev, REL_Y, rel_y);
break; break;
} }

View file

@ -84,7 +84,7 @@ static int synaptics_mode_cmd(struct psmouse *psmouse, u8 mode)
u8 param[1]; u8 param[1];
int error; int error;
error = psmouse_sliced_command(psmouse, mode); error = ps2_sliced_command(&psmouse->ps2dev, mode);
if (error) if (error)
return error; return error;
@ -190,7 +190,7 @@ static int synaptics_send_cmd(struct psmouse *psmouse, u8 cmd, u8 *param)
{ {
int error; int error;
error = psmouse_sliced_command(psmouse, cmd); error = ps2_sliced_command(&psmouse->ps2dev, cmd);
if (error) if (error)
return error; return error;
@ -547,7 +547,7 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
static u8 param = 0xc8; static u8 param = 0xc8;
int error; int error;
error = psmouse_sliced_command(psmouse, SYN_QUE_MODEL); error = ps2_sliced_command(&psmouse->ps2dev, SYN_QUE_MODEL);
if (error) if (error)
return error; return error;
@ -614,7 +614,7 @@ static int synaptics_pt_write(struct serio *serio, u8 c)
u8 rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */ u8 rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
int error; int error;
error = psmouse_sliced_command(parent, c); error = ps2_sliced_command(&parent->ps2dev, c);
if (error) if (error)
return error; return error;

View file

@ -33,18 +33,15 @@ static const char * const trackpoint_variants[] = {
*/ */
static int trackpoint_power_on_reset(struct ps2dev *ps2dev) static int trackpoint_power_on_reset(struct ps2dev *ps2dev)
{ {
u8 results[2]; u8 param[2] = { TP_POR };
int tries = 0; int err;
/* Issue POR command, and repeat up to once if 0xFC00 received */ err = ps2_command(ps2dev, param, MAKE_PS2_CMD(1, 2, TP_COMMAND));
do { if (err)
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || return err;
ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 2, TP_POR)))
return -1;
} while (results[0] == 0xFC && results[1] == 0x00 && ++tries < 2);
/* Check for success response -- 0xAA00 */ /* Check for success response -- 0xAA00 */
if (results[0] != 0xAA || results[1] != 0x00) if (param[0] != 0xAA || param[1] != 0x00)
return -ENODEV; return -ENODEV;
return 0; return 0;
@ -55,49 +52,39 @@ static int trackpoint_power_on_reset(struct ps2dev *ps2dev)
*/ */
static int trackpoint_read(struct ps2dev *ps2dev, u8 loc, u8 *results) static int trackpoint_read(struct ps2dev *ps2dev, u8 loc, u8 *results)
{ {
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || results[0] = loc;
ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) {
return -1;
}
return 0; return ps2_command(ps2dev, results, MAKE_PS2_CMD(1, 1, TP_COMMAND));
} }
static int trackpoint_write(struct ps2dev *ps2dev, u8 loc, u8 val) static int trackpoint_write(struct ps2dev *ps2dev, u8 loc, u8 val)
{ {
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || u8 param[3] = { TP_WRITE_MEM, loc, val };
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, val))) {
return -1;
}
return 0; return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND));
} }
static int trackpoint_toggle_bit(struct ps2dev *ps2dev, u8 loc, u8 mask) static int trackpoint_toggle_bit(struct ps2dev *ps2dev, u8 loc, u8 mask)
{ {
u8 param[3] = { TP_TOGGLE, loc, mask };
/* Bad things will happen if the loc param isn't in this range */ /* Bad things will happen if the loc param isn't in this range */
if (loc < 0x20 || loc >= 0x2F) if (loc < 0x20 || loc >= 0x2F)
return -1; return -EINVAL;
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND));
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_TOGGLE)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, mask))) {
return -1;
}
return 0;
} }
static int trackpoint_update_bit(struct ps2dev *ps2dev, static int trackpoint_update_bit(struct ps2dev *ps2dev,
u8 loc, u8 mask, u8 value) u8 loc, u8 mask, u8 value)
{ {
int retval = 0; int retval;
u8 data; u8 data;
trackpoint_read(ps2dev, loc, &data); retval = trackpoint_read(ps2dev, loc, &data);
if (retval)
return retval;
if (((data & mask) == mask) != !!value) if (((data & mask) == mask) != !!value)
retval = trackpoint_toggle_bit(ps2dev, loc, mask); retval = trackpoint_toggle_bit(ps2dev, loc, mask);
@ -142,9 +129,9 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,
return err; return err;
*field = value; *field = value;
trackpoint_write(&psmouse->ps2dev, attr->command, value); err = trackpoint_write(&psmouse->ps2dev, attr->command, value);
return count; return err ?: count;
} }
#define TRACKPOINT_INT_ATTR(_name, _command, _default) \ #define TRACKPOINT_INT_ATTR(_name, _command, _default) \
@ -175,10 +162,11 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,
if (*field != value) { if (*field != value) {
*field = value; *field = value;
trackpoint_toggle_bit(&psmouse->ps2dev, attr->command, attr->mask); err = trackpoint_toggle_bit(&psmouse->ps2dev,
attr->command, attr->mask);
} }
return count; return err ?: count;
} }

View file

@ -26,31 +26,79 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION("PS/2 driver library"); MODULE_DESCRIPTION("PS/2 driver library");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static int ps2_do_sendbyte(struct ps2dev *ps2dev, u8 byte,
unsigned int timeout, unsigned int max_attempts)
__releases(&ps2dev->serio->lock) __acquires(&ps2dev->serio->lock)
{
int attempt = 0;
int error;
lockdep_assert_held(&ps2dev->serio->lock);
do {
ps2dev->nak = 1;
ps2dev->flags |= PS2_FLAG_ACK;
serio_continue_rx(ps2dev->serio);
error = serio_write(ps2dev->serio, byte);
if (error)
dev_dbg(&ps2dev->serio->dev,
"failed to write %#02x: %d\n", byte, error);
else
wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_ACK),
msecs_to_jiffies(timeout));
serio_pause_rx(ps2dev->serio);
} while (ps2dev->nak == PS2_RET_NAK && ++attempt < max_attempts);
ps2dev->flags &= ~PS2_FLAG_ACK;
if (!error) {
switch (ps2dev->nak) {
case 0:
break;
case PS2_RET_NAK:
error = -EAGAIN;
break;
case PS2_RET_ERR:
error = -EPROTO;
break;
default:
error = -EIO;
break;
}
}
if (error || attempt > 1)
dev_dbg(&ps2dev->serio->dev,
"%02x - %d (%x), attempt %d\n",
byte, error, ps2dev->nak, attempt);
return error;
}
/* /*
* ps2_sendbyte() sends a byte to the device and waits for acknowledge. * ps2_sendbyte() sends a byte to the device and waits for acknowledge.
* It doesn't handle retransmission, though it could - because if there * It doesn't handle retransmission, the caller is expected to handle
* is a need for retransmissions device has to be replaced anyway. * it when needed.
* *
* ps2_sendbyte() can only be called from a process context. * ps2_sendbyte() can only be called from a process context.
*/ */
int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout)
{ {
serio_pause_rx(ps2dev->serio); int retval;
ps2dev->nak = 1;
ps2dev->flags |= PS2_FLAG_ACK;
serio_continue_rx(ps2dev->serio);
if (serio_write(ps2dev->serio, byte) == 0)
wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_ACK),
msecs_to_jiffies(timeout));
serio_pause_rx(ps2dev->serio); serio_pause_rx(ps2dev->serio);
ps2dev->flags &= ~PS2_FLAG_ACK;
retval = ps2_do_sendbyte(ps2dev, byte, timeout, 1);
dev_dbg(&ps2dev->serio->dev, "%02x - %x\n", byte, ps2dev->nak);
serio_continue_rx(ps2dev->serio); serio_continue_rx(ps2dev->serio);
return -ps2dev->nak; return retval;
} }
EXPORT_SYMBOL(ps2_sendbyte); EXPORT_SYMBOL(ps2_sendbyte);
@ -75,7 +123,7 @@ EXPORT_SYMBOL(ps2_end_command);
* and discards them. * and discards them.
*/ */
void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout)
{ {
if (maxbytes > sizeof(ps2dev->cmdbuf)) { if (maxbytes > sizeof(ps2dev->cmdbuf)) {
WARN_ON(1); WARN_ON(1);
@ -102,9 +150,9 @@ EXPORT_SYMBOL(ps2_drain);
* known keyboard IDs. * known keyboard IDs.
*/ */
int ps2_is_keyboard_id(char id_byte) bool ps2_is_keyboard_id(u8 id_byte)
{ {
static const char keyboard_ids[] = { static const u8 keyboard_ids[] = {
0xab, /* Regular keyboards */ 0xab, /* Regular keyboards */
0xac, /* NCD Sun keyboard */ 0xac, /* NCD Sun keyboard */
0x2b, /* Trust keyboard, translated */ 0x2b, /* Trust keyboard, translated */
@ -123,49 +171,50 @@ EXPORT_SYMBOL(ps2_is_keyboard_id);
* completion. * completion.
*/ */
static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) static int ps2_adjust_timeout(struct ps2dev *ps2dev,
unsigned int command, unsigned int timeout)
{ {
switch (command) { switch (command) {
case PS2_CMD_RESET_BAT: case PS2_CMD_RESET_BAT:
/* /*
* Device has sent the first response byte after * Device has sent the first response byte after
* reset command, reset is thus done, so we can * reset command, reset is thus done, so we can
* shorten the timeout. * shorten the timeout.
* The next byte will come soon (keyboard) or not * The next byte will come soon (keyboard) or not
* at all (mouse). * at all (mouse).
*/ */
if (timeout > msecs_to_jiffies(100)) if (timeout > msecs_to_jiffies(100))
timeout = msecs_to_jiffies(100); timeout = msecs_to_jiffies(100);
break; break;
case PS2_CMD_GETID: case PS2_CMD_GETID:
/* /*
* Microsoft Natural Elite keyboard responds to * Microsoft Natural Elite keyboard responds to
* the GET ID command as it were a mouse, with * the GET ID command as it were a mouse, with
* a single byte. Fail the command so atkbd will * a single byte. Fail the command so atkbd will
* use alternative probe to detect it. * use alternative probe to detect it.
*/ */
if (ps2dev->cmdbuf[1] == 0xaa) { if (ps2dev->cmdbuf[1] == 0xaa) {
serio_pause_rx(ps2dev->serio); serio_pause_rx(ps2dev->serio);
ps2dev->flags = 0; ps2dev->flags = 0;
serio_continue_rx(ps2dev->serio); serio_continue_rx(ps2dev->serio);
timeout = 0; timeout = 0;
} }
/* /*
* If device behind the port is not a keyboard there * If device behind the port is not a keyboard there
* won't be 2nd byte of ID response. * won't be 2nd byte of ID response.
*/ */
if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) { if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) {
serio_pause_rx(ps2dev->serio); serio_pause_rx(ps2dev->serio);
ps2dev->flags = ps2dev->cmdcnt = 0; ps2dev->flags = ps2dev->cmdcnt = 0;
serio_continue_rx(ps2dev->serio); serio_continue_rx(ps2dev->serio);
timeout = 0; timeout = 0;
} }
break; break;
default: default:
break; break;
} }
return timeout; return timeout;
@ -178,50 +227,60 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
* ps2_command() can only be called from a process context * ps2_command() can only be called from a process context
*/ */
int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)
{ {
int timeout; unsigned int timeout;
int send = (command >> 12) & 0xf; unsigned int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf; unsigned int receive = (command >> 8) & 0xf;
int rc = -1; int rc;
int i; int i;
u8 send_param[16];
if (receive > sizeof(ps2dev->cmdbuf)) { if (receive > sizeof(ps2dev->cmdbuf)) {
WARN_ON(1); WARN_ON(1);
return -1; return -EINVAL;
} }
if (send && !param) { if (send && !param) {
WARN_ON(1); WARN_ON(1);
return -1; return -EINVAL;
} }
memcpy(send_param, param, send);
serio_pause_rx(ps2dev->serio); serio_pause_rx(ps2dev->serio);
ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
ps2dev->cmdcnt = receive; ps2dev->cmdcnt = receive;
if (receive && param) if (receive && param)
for (i = 0; i < receive; i++) for (i = 0; i < receive; i++)
ps2dev->cmdbuf[(receive - 1) - i] = param[i]; ps2dev->cmdbuf[(receive - 1) - i] = param[i];
serio_continue_rx(ps2dev->serio);
/* Signal that we are sending the command byte */
ps2dev->flags |= PS2_FLAG_ACK_CMD;
/* /*
* Some devices (Synaptics) peform the reset before * Some devices (Synaptics) peform the reset before
* ACKing the reset command, and so it can take a long * ACKing the reset command, and so it can take a long
* time before the ACK arrives. * time before the ACK arrives.
*/ */
if (ps2_sendbyte(ps2dev, command & 0xff, timeout = command == PS2_CMD_RESET_BAT ? 1000 : 200;
command == PS2_CMD_RESET_BAT ? 1000 : 200)) {
serio_pause_rx(ps2dev->serio); rc = ps2_do_sendbyte(ps2dev, command & 0xff, timeout, 2);
if (rc)
goto out_reset_flags; goto out_reset_flags;
}
/* Now we are sending command parameters, if any */
ps2dev->flags &= ~PS2_FLAG_ACK_CMD;
for (i = 0; i < send; i++) { for (i = 0; i < send; i++) {
if (ps2_sendbyte(ps2dev, param[i], 200)) { rc = ps2_do_sendbyte(ps2dev, param[i], 200, 2);
serio_pause_rx(ps2dev->serio); if (rc)
goto out_reset_flags; goto out_reset_flags;
}
} }
serio_continue_rx(ps2dev->serio);
/* /*
* The reset command takes a long time to execute. * The reset command takes a long time to execute.
*/ */
@ -243,8 +302,11 @@ int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
for (i = 0; i < receive; i++) for (i = 0; i < receive; i++)
param[i] = ps2dev->cmdbuf[(receive - 1) - i]; param[i] = ps2dev->cmdbuf[(receive - 1) - i];
if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) if (ps2dev->cmdcnt &&
(command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) {
rc = -EPROTO;
goto out_reset_flags; goto out_reset_flags;
}
rc = 0; rc = 0;
@ -252,11 +314,21 @@ int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
ps2dev->flags = 0; ps2dev->flags = 0;
serio_continue_rx(ps2dev->serio); serio_continue_rx(ps2dev->serio);
return rc; dev_dbg(&ps2dev->serio->dev,
"%02x [%*ph] - %x/%08lx [%*ph]\n",
command & 0xff, send, send_param,
ps2dev->nak, ps2dev->flags,
receive, param ?: send_param);
/*
* ps_command() handles resends itself, so do not leak -EAGAIN
* to the callers.
*/
return rc != -EAGAIN ? rc : -EPROTO;
} }
EXPORT_SYMBOL(__ps2_command); EXPORT_SYMBOL(__ps2_command);
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) int ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)
{ {
int rc; int rc;
@ -268,6 +340,39 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
} }
EXPORT_SYMBOL(ps2_command); EXPORT_SYMBOL(ps2_command);
/*
* ps2_sliced_command() sends an extended PS/2 command to the mouse
* using sliced syntax, understood by advanced devices, such as Logitech
* or Synaptics touchpads. The command is encoded as:
* 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
* is the command.
*/
int ps2_sliced_command(struct ps2dev *ps2dev, u8 command)
{
int i;
int retval;
ps2_begin_command(ps2dev);
retval = __ps2_command(ps2dev, NULL, PS2_CMD_SETSCALE11);
if (retval)
goto out;
for (i = 6; i >= 0; i -= 2) {
u8 d = (command >> i) & 3;
retval = __ps2_command(ps2dev, &d, PS2_CMD_SETRES);
if (retval)
break;
}
out:
dev_dbg(&ps2dev->serio->dev, "%02x - %d\n", command, retval);
ps2_end_command(ps2dev);
return retval;
}
EXPORT_SYMBOL(ps2_sliced_command);
/* /*
* ps2_init() initializes ps2dev structure * ps2_init() initializes ps2dev structure
*/ */
@ -286,42 +391,53 @@ EXPORT_SYMBOL(ps2_init);
* to properly process ACK/NAK of a command from a PS/2 device. * to properly process ACK/NAK of a command from a PS/2 device.
*/ */
int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data)
{ {
switch (data) { switch (data) {
case PS2_RET_ACK: case PS2_RET_ACK:
ps2dev->nak = 0;
break;
case PS2_RET_NAK:
ps2dev->flags |= PS2_FLAG_NAK;
ps2dev->nak = PS2_RET_NAK;
break;
case PS2_RET_ERR:
if (ps2dev->flags & PS2_FLAG_NAK) {
ps2dev->flags &= ~PS2_FLAG_NAK;
ps2dev->nak = PS2_RET_ERR;
break;
}
/*
* Workaround for mice which don't ACK the Get ID command.
* These are valid mouse IDs that we recognize.
*/
case 0x00:
case 0x03:
case 0x04:
if (ps2dev->flags & PS2_FLAG_WAITID) {
ps2dev->nak = 0; ps2dev->nak = 0;
break; break;
}
case PS2_RET_NAK: /* Fall through */
ps2dev->flags |= PS2_FLAG_NAK; default:
ps2dev->nak = PS2_RET_NAK;
break;
case PS2_RET_ERR:
if (ps2dev->flags & PS2_FLAG_NAK) {
ps2dev->flags &= ~PS2_FLAG_NAK;
ps2dev->nak = PS2_RET_ERR;
break;
}
/* /*
* Workaround for mice which don't ACK the Get ID command. * Do not signal errors if we get unexpected reply while
* These are valid mouse IDs that we recognize. * waiting for an ACK to the initial (first) command byte:
* the device might not be quiesced yet and continue
* delivering data.
* Note that we reset PS2_FLAG_WAITID flag, so the workaround
* for mice not acknowledging the Get ID command only triggers
* on the 1st byte; if device spews data we really want to see
* a real ACK from it.
*/ */
case 0x00: dev_dbg(&ps2dev->serio->dev, "unexpected %#02x\n", data);
case 0x03: ps2dev->flags &= ~PS2_FLAG_WAITID;
case 0x04: return ps2dev->flags & PS2_FLAG_ACK_CMD;
if (ps2dev->flags & PS2_FLAG_WAITID) {
ps2dev->nak = 0;
break;
}
/* Fall through */
default:
return 0;
} }
if (!ps2dev->nak) { if (!ps2dev->nak) {
ps2dev->flags &= ~PS2_FLAG_NAK; ps2dev->flags &= ~PS2_FLAG_NAK;
if (ps2dev->cmdcnt) if (ps2dev->cmdcnt)
@ -334,7 +450,7 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
if (data != PS2_RET_ACK) if (data != PS2_RET_ACK)
ps2_handle_response(ps2dev, data); ps2_handle_response(ps2dev, data);
return 1; return true;
} }
EXPORT_SYMBOL(ps2_handle_ack); EXPORT_SYMBOL(ps2_handle_ack);
@ -344,7 +460,7 @@ EXPORT_SYMBOL(ps2_handle_ack);
* waiting for completion of the command. * waiting for completion of the command.
*/ */
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data) bool ps2_handle_response(struct ps2dev *ps2dev, u8 data)
{ {
if (ps2dev->cmdcnt) if (ps2dev->cmdcnt)
ps2dev->cmdbuf[--ps2dev->cmdcnt] = data; ps2dev->cmdbuf[--ps2dev->cmdcnt] = data;
@ -360,7 +476,7 @@ int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
wake_up(&ps2dev->wait); wake_up(&ps2dev->wait);
} }
return 1; return true;
} }
EXPORT_SYMBOL(ps2_handle_response); EXPORT_SYMBOL(ps2_handle_response);

View file

@ -10,7 +10,13 @@
* the Free Software Foundation. * the Free Software Foundation.
*/ */
#include <linux/bitops.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/wait.h>
#define PS2_CMD_SETSCALE11 0x00e6
#define PS2_CMD_SETRES 0x10e8
#define PS2_CMD_GETID 0x02f2 #define PS2_CMD_GETID 0x02f2
#define PS2_CMD_RESET_BAT 0x02ff #define PS2_CMD_RESET_BAT 0x02ff
@ -20,11 +26,12 @@
#define PS2_RET_NAK 0xfe #define PS2_RET_NAK 0xfe
#define PS2_RET_ERR 0xfc #define PS2_RET_ERR 0xfc
#define PS2_FLAG_ACK 1 /* Waiting for ACK/NAK */ #define PS2_FLAG_ACK BIT(0) /* Waiting for ACK/NAK */
#define PS2_FLAG_CMD 2 /* Waiting for command to finish */ #define PS2_FLAG_CMD BIT(1) /* Waiting for a command to finish */
#define PS2_FLAG_CMD1 4 /* Waiting for the first byte of command response */ #define PS2_FLAG_CMD1 BIT(2) /* Waiting for the first byte of command response */
#define PS2_FLAG_WAITID 8 /* Command execiting is GET ID */ #define PS2_FLAG_WAITID BIT(3) /* Command executing is GET ID */
#define PS2_FLAG_NAK 16 /* Last transmission was NAKed */ #define PS2_FLAG_NAK BIT(4) /* Last transmission was NAKed */
#define PS2_FLAG_ACK_CMD BIT(5) /* Waiting to ACK the command (first) byte */
struct ps2dev { struct ps2dev {
struct serio *serio; struct serio *serio;
@ -36,21 +43,22 @@ struct ps2dev {
wait_queue_head_t wait; wait_queue_head_t wait;
unsigned long flags; unsigned long flags;
unsigned char cmdbuf[8]; u8 cmdbuf[8];
unsigned char cmdcnt; u8 cmdcnt;
unsigned char nak; u8 nak;
}; };
void ps2_init(struct ps2dev *ps2dev, struct serio *serio); void ps2_init(struct ps2dev *ps2dev, struct serio *serio);
int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout); int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout);
void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout); void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout);
void ps2_begin_command(struct ps2dev *ps2dev); void ps2_begin_command(struct ps2dev *ps2dev);
void ps2_end_command(struct ps2dev *ps2dev); void ps2_end_command(struct ps2dev *ps2dev);
int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command);
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); int ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command);
int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data); int ps2_sliced_command(struct ps2dev *ps2dev, u8 command);
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data); bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data);
bool ps2_handle_response(struct ps2dev *ps2dev, u8 data);
void ps2_cmd_aborted(struct ps2dev *ps2dev); void ps2_cmd_aborted(struct ps2dev *ps2dev);
int ps2_is_keyboard_id(char id); bool ps2_is_keyboard_id(u8 id);
#endif /* _LIBPS2_H */ #endif /* _LIBPS2_H */