|
@@ -14,6 +14,7 @@
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
#define psmouse_fmt(fmt) fmt
|
|
|
|
|
|
+#include <linux/bitops.h>
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/slab.h>
|
|
@@ -23,6 +24,7 @@
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/libps2.h>
|
|
|
#include <linux/mutex.h>
|
|
|
+#include <linux/types.h>
|
|
|
|
|
|
#include "psmouse.h"
|
|
|
#include "synaptics.h"
|
|
@@ -68,6 +70,10 @@ static bool psmouse_smartscroll = true;
|
|
|
module_param_named(smartscroll, psmouse_smartscroll, bool, 0644);
|
|
|
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;
|
|
|
module_param_named(resetafter, psmouse_resetafter, uint, 0644);
|
|
|
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 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_MIDDLE, buttons & BIT(2));
|
|
|
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
|
|
|
* 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)
|
|
|
{
|
|
|
struct input_dev *dev = psmouse->dev;
|
|
|
- unsigned char *packet = psmouse->packet;
|
|
|
+ u8 *packet = psmouse->packet;
|
|
|
+ int wheel;
|
|
|
|
|
|
if (psmouse->pktcnt < psmouse->pktsize)
|
|
|
return PSMOUSE_GOOD_DATA;
|
|
@@ -140,39 +164,52 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
|
|
|
switch (psmouse->protocol->type) {
|
|
|
case PSMOUSE_IMPS:
|
|
|
/* IntelliMouse has scroll wheel */
|
|
|
- input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
|
|
|
+ input_report_rel(dev, REL_WHEEL, -(s8) packet[3]);
|
|
|
break;
|
|
|
|
|
|
case PSMOUSE_IMEX:
|
|
|
/* Scroll wheel and buttons on IntelliMouse Explorer */
|
|
|
switch (packet[3] & 0xC0) {
|
|
|
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;
|
|
|
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;
|
|
|
case 0x00:
|
|
|
case 0xC0:
|
|
|
- input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7));
|
|
|
- input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
|
|
|
- input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
|
|
|
+ wheel = sign_extend32(packet[3], 3);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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;
|
|
|
|
|
|
case PSMOUSE_GENPS:
|
|
|
/* 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 */
|
|
|
- input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1);
|
|
|
- input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1);
|
|
|
+ input_report_key(dev, BTN_SIDE, BIT(6));
|
|
|
+ input_report_key(dev, BTN_EXTRA, BIT(7));
|
|
|
break;
|
|
|
|
|
|
case PSMOUSE_THINKPS:
|
|
|
/* 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
|
|
@@ -186,8 +223,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
|
|
|
* Cortron PS2 Trackball reports SIDE button in the
|
|
|
* 4th bit of the first byte.
|
|
|
*/
|
|
|
- input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1);
|
|
|
- packet[0] |= 0x08;
|
|
|
+ input_report_key(dev, BTN_SIDE, BIT(3));
|
|
|
+ packet[0] |= BIT(3);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
@@ -195,11 +232,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
|
|
|
}
|
|
|
|
|
|
/* Generic PS/2 Mouse */
|
|
|
- psmouse_report_standard_buttons(dev,
|
|
|
- packet[0] | psmouse->extra_buttons);
|
|
|
-
|
|
|
- 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);
|
|
|
+ packet[0] |= psmouse->extra_buttons;
|
|
|
+ psmouse_report_standard_packet(dev, packet);
|
|
|
|
|
|
input_sync(dev);
|
|
|
|
|
@@ -255,7 +289,7 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
|
|
|
psmouse_notice(psmouse,
|
|
|
"issuing reconnect request\n");
|
|
|
serio_reconnect(psmouse->ps2dev.serio);
|
|
|
- return -1;
|
|
|
+ return -EIO;
|
|
|
}
|
|
|
}
|
|
|
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.
|
|
|
*/
|
|
|
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);
|
|
|
|
|
@@ -397,41 +431,20 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
|
|
|
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.
|
|
|
*/
|
|
|
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))
|
|
|
- return -1;
|
|
|
+ error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
|
|
|
if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID)
|
|
|
- return -1;
|
|
|
+ return -EIO;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -441,8 +454,8 @@ int psmouse_reset(struct psmouse *psmouse)
|
|
|
*/
|
|
|
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
|
|
|
{
|
|
|
- static const unsigned char params[] = { 0, 1, 2, 2, 3 };
|
|
|
- unsigned char p;
|
|
|
+ static const u8 params[] = { 0, 1, 2, 2, 3 };
|
|
|
+ u8 p;
|
|
|
|
|
|
if (resolution == 0 || 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 const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
|
|
|
- unsigned char r;
|
|
|
+ static const u8 rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
|
|
|
+ u8 r;
|
|
|
int i = 0;
|
|
|
|
|
|
- while (rates[i] > rate) i++;
|
|
|
+ while (rates[i] > rate)
|
|
|
+ i++;
|
|
|
r = rates[i];
|
|
|
ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE);
|
|
|
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)
|
|
|
{
|
|
|
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
|
|
- unsigned char param[4];
|
|
|
+ u8 param[4];
|
|
|
|
|
|
param[0] = 3;
|
|
|
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);
|
|
|
|
|
|
if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55)
|
|
|
- return -1;
|
|
|
+ return -ENODEV;
|
|
|
|
|
|
if (set_properties) {
|
|
|
__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)
|
|
|
{
|
|
|
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
|
|
- unsigned char param[2];
|
|
|
+ u8 param[2];
|
|
|
|
|
|
param[0] = 200;
|
|
|
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);
|
|
|
|
|
|
if (param[0] != 3)
|
|
|
- return -1;
|
|
|
+ return -ENODEV;
|
|
|
|
|
|
if (set_properties) {
|
|
|
__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)
|
|
|
{
|
|
|
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
|
|
- unsigned char param[2];
|
|
|
+ u8 param[2];
|
|
|
|
|
|
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);
|
|
|
|
|
|
if (param[0] != 4)
|
|
|
- return -1;
|
|
|
+ return -ENODEV;
|
|
|
|
|
|
/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
|
|
|
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)
|
|
|
{
|
|
|
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
|
|
- unsigned char param[2];
|
|
|
- static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
|
|
|
+ u8 param[2];
|
|
|
+ static const u8 seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
|
|
|
int i;
|
|
|
|
|
|
param[0] = 10;
|
|
@@ -659,7 +673,7 @@ static int thinking_detect(struct psmouse *psmouse, bool set_properties)
|
|
|
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
|
|
|
|
|
|
if (param[0] != 2)
|
|
|
- return -1;
|
|
|
+ return -ENODEV;
|
|
|
|
|
|
if (set_properties) {
|
|
|
__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
|
|
|
* assume that the device has 3.
|
|
|
*/
|
|
|
- __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
|
|
|
+ input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -942,20 +956,17 @@ static void psmouse_apply_defaults(struct psmouse *psmouse)
|
|
|
{
|
|
|
struct input_dev *input_dev = psmouse->dev;
|
|
|
|
|
|
- memset(input_dev->evbit, 0, sizeof(input_dev->evbit));
|
|
|
- memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
|
|
|
- memset(input_dev->relbit, 0, sizeof(input_dev->relbit));
|
|
|
- memset(input_dev->absbit, 0, sizeof(input_dev->absbit));
|
|
|
- memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit));
|
|
|
-
|
|
|
- __set_bit(EV_KEY, input_dev->evbit);
|
|
|
- __set_bit(EV_REL, input_dev->evbit);
|
|
|
+ bitmap_zero(input_dev->evbit, EV_CNT);
|
|
|
+ bitmap_zero(input_dev->keybit, KEY_CNT);
|
|
|
+ bitmap_zero(input_dev->relbit, REL_CNT);
|
|
|
+ bitmap_zero(input_dev->absbit, ABS_CNT);
|
|
|
+ bitmap_zero(input_dev->mscbit, MSC_CNT);
|
|
|
|
|
|
- __set_bit(BTN_LEFT, input_dev->keybit);
|
|
|
- __set_bit(BTN_RIGHT, input_dev->keybit);
|
|
|
+ input_set_capability(input_dev, EV_KEY, BTN_LEFT);
|
|
|
+ input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
|
|
|
|
|
|
- __set_bit(REL_X, input_dev->relbit);
|
|
|
- __set_bit(REL_Y, input_dev->relbit);
|
|
|
+ input_set_capability(input_dev, EV_REL, REL_X);
|
|
|
+ input_set_capability(input_dev, EV_REL, REL_Y);
|
|
|
|
|
|
__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)
|
|
|
{
|
|
|
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
|
|
@@ -1234,20 +1246,22 @@ static int psmouse_probe(struct psmouse *psmouse)
|
|
|
* subsequent ID queries, probably due to a firmware bug.
|
|
|
*/
|
|
|
param[0] = 0xa5;
|
|
|
- if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
|
|
|
- return -1;
|
|
|
+ error = ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
|
|
|
if (param[0] != 0x00 && param[0] != 0x03 &&
|
|
|
param[0] != 0x04 && param[0] != 0xff)
|
|
|
- return -1;
|
|
|
+ return -ENODEV;
|
|
|
|
|
|
/*
|
|
|
* Then we reset and disable the mouse so that it doesn't generate
|
|
|
* events.
|
|
|
*/
|
|
|
- if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS))
|
|
|
- psmouse_warn(psmouse, "Failed to reset mouse on %s\n",
|
|
|
- ps2dev->serio->phys);
|
|
|
+ error = ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
|
|
|
+ if (error)
|
|
|
+ psmouse_warn(psmouse, "Failed to reset mouse on %s: %d\n",
|
|
|
+ ps2dev->serio->phys, error);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1288,10 +1302,13 @@ int psmouse_activate(struct psmouse *psmouse)
|
|
|
*/
|
|
|
int psmouse_deactivate(struct psmouse *psmouse)
|
|
|
{
|
|
|
- if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) {
|
|
|
- psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n",
|
|
|
- psmouse->ps2dev.serio->phys);
|
|
|
- return -1;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ error = ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE);
|
|
|
+ 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);
|