Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 177090
b: refs/heads/master
c: 1d9f262
h: refs/heads/master
v: v3
  • Loading branch information
Sebastian Kapfer authored and Dmitry Torokhov committed Dec 16, 2009
1 parent dce7d84 commit c7eebd1
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 28 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 18f7ad59b0ef341fb9390cb79b2a39707c48257d
refs/heads/master: 1d9f26262aef6d63ff65eba0fd5f1583f342b69b
254 changes: 227 additions & 27 deletions trunk/drivers/input/mouse/alps.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com>
* Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
* Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2009 Sebastian Kapfer <sebastian_kapfer@gmx.net>
*
* ALPS detection, tap switching and status querying info is taken from
* tpconfig utility (by C. Scott Ananian and Bruce Kall).
Expand All @@ -28,7 +29,6 @@
#define dbg(format, arg...) do {} while (0)
#endif


#define ALPS_OLDPROTO 0x01 /* old style input */
#define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */
#define ALPS_PASS 0x04 /* device has a pass-through port */
Expand All @@ -37,7 +37,8 @@
#define ALPS_FW_BK_1 0x10 /* front & back buttons present */
#define ALPS_FW_BK_2 0x20 /* front & back buttons present */
#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */

#define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with
6-byte ALPS packet */

static const struct alps_model_info alps_model_data[] = {
{ { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */
Expand All @@ -58,7 +59,9 @@ static const struct alps_model_info alps_model_data[] = {
{ { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */
{ { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
{ { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
{ { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude E6500 */
/* Dell Latitude E5500, E6400, E6500, Precision M4400 */
{ { 0x62, 0x02, 0x14 }, 0xcf, 0xcf,
ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },
{ { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */
};

Expand All @@ -69,20 +72,88 @@ static const struct alps_model_info alps_model_data[] = {
*/

/*
* ALPS abolute Mode - new format
* PS/2 packet format
*
* byte 0: 0 0 YSGN XSGN 1 M R L
* byte 1: X7 X6 X5 X4 X3 X2 X1 X0
* byte 2: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
*
* Note that the device never signals overflow condition.
*
* ALPS absolute Mode - new format
*
* byte 0: 1 ? ? ? 1 ? ? ?
* byte 1: 0 x6 x5 x4 x3 x2 x1 x0
* byte 2: 0 x10 x9 x8 x7 ? fin ges
* byte 2: 0 x10 x9 x8 x7 ? fin ges
* byte 3: 0 y9 y8 y7 1 M R L
* byte 4: 0 y6 y5 y4 y3 y2 y1 y0
* byte 5: 0 z6 z5 z4 z3 z2 z1 z0
*
* Dualpoint device -- interleaved packet format
*
* byte 0: 1 1 0 0 1 1 1 1
* byte 1: 0 x6 x5 x4 x3 x2 x1 x0
* byte 2: 0 x10 x9 x8 x7 0 fin ges
* byte 3: 0 0 YSGN XSGN 1 1 1 1
* byte 4: X7 X6 X5 X4 X3 X2 X1 X0
* byte 5: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
* byte 6: 0 y9 y8 y7 1 m r l
* byte 7: 0 y6 y5 y4 y3 y2 y1 y0
* byte 8: 0 z6 z5 z4 z3 z2 z1 z0
*
* CAPITALS = stick, miniscules = touchpad
*
* ?'s can have different meanings on different models,
* such as wheel rotation, extra buttons, stick buttons
* on a dualpoint, etc.
*/

static bool alps_is_valid_first_byte(const struct alps_model_info *model,
unsigned char data)
{
return (data & model->mask0) == model->byte0;
}

static void alps_report_buttons(struct psmouse *psmouse,
struct input_dev *dev1, struct input_dev *dev2,
int left, int right, int middle)
{
struct alps_data *priv = psmouse->private;
const struct alps_model_info *model = priv->i;

if (model->flags & ALPS_PS2_INTERLEAVED) {
struct input_dev *dev;

/*
* If shared button has already been reported on the
* other device (dev2) then this event should be also
* sent through that device.
*/
dev = test_bit(BTN_LEFT, dev2->key) ? dev2 : dev1;
input_report_key(dev, BTN_LEFT, left);

dev = test_bit(BTN_RIGHT, dev2->key) ? dev2 : dev1;
input_report_key(dev, BTN_RIGHT, right);

dev = test_bit(BTN_MIDDLE, dev2->key) ? dev2 : dev1;
input_report_key(dev, BTN_MIDDLE, middle);

/*
* Sync the _other_ device now, we'll do the first
* device later once we report the rest of the events.
*/
input_sync(dev2);
} else {
/*
* For devices with non-interleaved packets we know what
* device buttons belong to so we can simply report them.
*/
input_report_key(dev1, BTN_LEFT, left);
input_report_key(dev1, BTN_RIGHT, right);
input_report_key(dev1, BTN_MIDDLE, middle);
}
}

static void alps_process_packet(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
Expand All @@ -93,18 +164,6 @@ static void alps_process_packet(struct psmouse *psmouse)
int x, y, z, ges, fin, left, right, middle;
int back = 0, forward = 0;

if ((packet[0] & 0xc8) == 0x08) { /* 3-byte PS/2 packet */
input_report_key(dev2, BTN_LEFT, packet[0] & 1);
input_report_key(dev2, BTN_RIGHT, packet[0] & 2);
input_report_key(dev2, BTN_MIDDLE, packet[0] & 4);
input_report_rel(dev2, REL_X,
packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
input_report_rel(dev2, REL_Y,
packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
input_sync(dev2);
return;
}

if (model->flags & ALPS_OLDPROTO) {
left = packet[2] & 0x10;
right = packet[2] & 0x08;
Expand Down Expand Up @@ -140,18 +199,13 @@ static void alps_process_packet(struct psmouse *psmouse)
input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x));
input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));

input_report_key(dev2, BTN_LEFT, left);
input_report_key(dev2, BTN_RIGHT, right);
input_report_key(dev2, BTN_MIDDLE, middle);
alps_report_buttons(psmouse, dev2, dev, left, right, middle);

input_sync(dev);
input_sync(dev2);
return;
}

input_report_key(dev, BTN_LEFT, left);
input_report_key(dev, BTN_RIGHT, right);
input_report_key(dev, BTN_MIDDLE, middle);
alps_report_buttons(psmouse, dev, dev2, left, right, middle);

/* Convert hardware tap to a reasonable Z value */
if (ges && !fin)
Expand Down Expand Up @@ -202,25 +256,168 @@ static void alps_process_packet(struct psmouse *psmouse)
input_sync(dev);
}

static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
unsigned char packet[],
bool report_buttons)
{
struct alps_data *priv = psmouse->private;
struct input_dev *dev2 = priv->dev2;

if (report_buttons)
alps_report_buttons(psmouse, dev2, psmouse->dev,
packet[0] & 1, packet[0] & 2, packet[0] & 4);

input_report_rel(dev2, REL_X,
packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
input_report_rel(dev2, REL_Y,
packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);

input_sync(dev2);
}

static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;

if (psmouse->pktcnt < 6)
return PSMOUSE_GOOD_DATA;

if (psmouse->pktcnt == 6) {
/*
* Start a timer to flush the packet if it ends up last
* 6-byte packet in the stream. Timer needs to fire
* psmouse core times out itself. 20 ms should be enough
* to decide if we are getting more data or not.
*/
mod_timer(&priv->timer, jiffies + msecs_to_jiffies(20));
return PSMOUSE_GOOD_DATA;
}

del_timer(&priv->timer);

if (psmouse->packet[6] & 0x80) {

/*
* Highest bit is set - that means we either had
* complete ALPS packet and this is start of the
* next packet or we got garbage.
*/

if (((psmouse->packet[3] |
psmouse->packet[4] |
psmouse->packet[5]) & 0x80) ||
(!alps_is_valid_first_byte(priv->i, psmouse->packet[6]))) {
dbg("refusing packet %x %x %x %x "
"(suspected interleaved ps/2)\n",
psmouse->packet[3], psmouse->packet[4],
psmouse->packet[5], psmouse->packet[6]);
return PSMOUSE_BAD_DATA;
}

alps_process_packet(psmouse);

/* Continue with the next packet */
psmouse->packet[0] = psmouse->packet[6];
psmouse->pktcnt = 1;

} else {

/*
* High bit is 0 - that means that we indeed got a PS/2
* packet in the middle of ALPS packet.
*
* There is also possibility that we got 6-byte ALPS
* packet followed by 3-byte packet from trackpoint. We
* can not distinguish between these 2 scenarios but
* becase the latter is unlikely to happen in course of
* normal operation (user would need to press all
* buttons on the pad and start moving trackpoint
* without touching the pad surface) we assume former.
* Even if we are wrong the wost thing that would happen
* the cursor would jump but we should not get protocol
* desynchronization.
*/

alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3],
false);

/*
* Continue with the standard ALPS protocol handling,
* but make sure we won't process it as an interleaved
* packet again, which may happen if all buttons are
* pressed. To avoid this let's reset the 4th bit which
* is normally 1.
*/
psmouse->packet[3] = psmouse->packet[6] & 0xf7;
psmouse->pktcnt = 4;
}

return PSMOUSE_GOOD_DATA;
}

static void alps_flush_packet(unsigned long data)
{
struct psmouse *psmouse = (struct psmouse *)data;

serio_pause_rx(psmouse->ps2dev.serio);

if (psmouse->pktcnt == 6) {

/*
* We did not any more data in reasonable amount of time.
* Validate the last 3 bytes and process as a standard
* ALPS packet.
*/
if ((psmouse->packet[3] |
psmouse->packet[4] |
psmouse->packet[5]) & 0x80) {
dbg("refusing packet %x %x %x "
"(suspected interleaved ps/2)\n",
psmouse->packet[3], psmouse->packet[4],
psmouse->packet[5]);
} else {
alps_process_packet(psmouse);
}
psmouse->pktcnt = 0;
}

serio_continue_rx(psmouse->ps2dev.serio);
}

static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
const struct alps_model_info *model = priv->i;

if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
if (psmouse->pktcnt == 3) {
alps_process_packet(psmouse);
alps_report_bare_ps2_packet(psmouse, psmouse->packet,
true);
return PSMOUSE_FULL_PACKET;
}
return PSMOUSE_GOOD_DATA;
}

if ((psmouse->packet[0] & priv->i->mask0) != priv->i->byte0)
/* Check for PS/2 packet stuffed in the middle of ALPS packet. */

if ((model->flags & ALPS_PS2_INTERLEAVED) &&
psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) {
return alps_handle_interleaved_ps2(psmouse);
}

if (!alps_is_valid_first_byte(model, psmouse->packet[0])) {
dbg("refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n",
psmouse->packet[0], model->mask0, model->byte0);
return PSMOUSE_BAD_DATA;
}

/* Bytes 2 - 6 should have 0 in the highest bit */
if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 &&
(psmouse->packet[psmouse->pktcnt - 1] & 0x80))
(psmouse->packet[psmouse->pktcnt - 1] & 0x80)) {
dbg("refusing packet[%i] = %x\n",
psmouse->pktcnt - 1, psmouse->packet[psmouse->pktcnt - 1]);
return PSMOUSE_BAD_DATA;
}

if (psmouse->pktcnt == 6) {
alps_process_packet(psmouse);
Expand Down Expand Up @@ -459,6 +656,7 @@ static void alps_disconnect(struct psmouse *psmouse)
struct alps_data *priv = psmouse->private;

psmouse_reset(psmouse);
del_timer_sync(&priv->timer);
input_unregister_device(priv->dev2);
kfree(priv);
}
Expand All @@ -476,6 +674,8 @@ int alps_init(struct psmouse *psmouse)
goto init_fail;

priv->dev2 = dev2;
setup_timer(&priv->timer, alps_flush_packet, (unsigned long)psmouse);

psmouse->private = priv;

model = alps_get_model(psmouse, &version);
Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/input/mouse/alps.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct alps_data {
char phys[32]; /* Phys */
const struct alps_model_info *i;/* Info */
int prev_fin; /* Finger bit from previous packet */
struct timer_list timer;
};

#ifdef CONFIG_MOUSE_PS2_ALPS
Expand Down

0 comments on commit c7eebd1

Please sign in to comment.