// SPDX-License-Identifier: GPL-2.0-only /* * ALPS touchpad PS/2 mouse driver * * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au> * 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).
*/
#define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ #define ALPS_PASS 0x04 /* device has a pass-through port */
#define ALPS_WHEEL 0x08 /* hardware wheel present */ #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 */ #define ALPS_STICK_BITS 0x100 /* separate stick button bits */ #define ALPS_BUTTONPAD 0x200 /* device is a clickpad */ #define ALPS_DUALPOINT_WITH_PRESSURE 0x400 /* device can report trackpoint pressure */
staticconststruct alps_model_info alps_model_data[] = { /* * XXX This entry is suspicious. First byte has zero lower nibble, * which is what a normal mouse would report. Also, the value 0x0e * isn't valid per PS/2 spec.
*/
{ { 0x20, 0x02, 0x0e }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
staticvoid alps_report_buttons(struct input_dev *dev1, struct input_dev *dev2, int left, int right, int middle)
{ 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 = (dev2 && test_bit(BTN_LEFT, dev2->key)) ? dev2 : dev1;
input_report_key(dev, BTN_LEFT, left);
/* Some models have separate stick button bits */ if (priv->flags & ALPS_STICK_BITS) {
left |= packet[0] & 1;
right |= packet[0] & 2;
middle |= packet[0] & 4;
}
/* Convert hardware tap to a reasonable Z value */ if (ges && !fin)
z = 40;
/* * A "tap and drag" operation is reported by the hardware as a transition * from (!fin && ges) to (fin && ges). This should be translated to the * sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually.
*/ if (ges && fin && !priv->prev_fin) {
input_report_abs(dev, ABS_X, x);
input_report_abs(dev, ABS_Y, y);
input_report_abs(dev, ABS_PRESSURE, 0);
input_report_key(dev, BTN_TOOL_FINGER, 0);
input_sync(dev);
}
priv->prev_fin = fin;
if (z > 30)
input_report_key(dev, BTN_TOUCH, 1); if (z < 25)
input_report_key(dev, BTN_TOUCH, 0);
if (z > 0) {
input_report_abs(dev, ABS_X, x);
input_report_abs(dev, ABS_Y, y);
}
input_report_abs(dev, ABS_PRESSURE, z);
input_report_key(dev, BTN_TOOL_FINGER, z > 0);
staticvoid alps_get_bitmap_points(unsignedint map, struct alps_bitmap_point *low, struct alps_bitmap_point *high, int *fingers)
{ struct alps_bitmap_point *point; int i, bit, prev_bit = 0;
point = low; for (i = 0; map != 0; i++, map >>= 1) {
bit = map & 1; if (bit) { if (!prev_bit) {
point->start_bit = i;
point->num_bits = 0;
(*fingers)++;
}
point->num_bits++;
} else { if (prev_bit)
point = high;
}
prev_bit = bit;
}
}
/* * Process bitmap data from semi-mt protocols. Returns the number of * fingers detected. A return value of 0 means at least one of the * bitmaps was empty. * * The bitmaps don't have enough data to track fingers, so this function * only generates points representing a bounding box of all contacts. * These points are returned in fields->mt when the return value * is greater than 0.
*/ staticint alps_process_bitmap(struct alps_data *priv, struct alps_fields *fields)
{ int i, fingers_x = 0, fingers_y = 0, fingers, closest; struct alps_bitmap_point x_low = {0,}, x_high = {0,}; struct alps_bitmap_point y_low = {0,}, y_high = {0,}; struct input_mt_pos corner[4];
/* * Fingers can overlap, so we use the maximum count of fingers * on either axis as the finger count.
*/
fingers = max(fingers_x, fingers_y);
/* * If an axis reports only a single contact, we have overlapping or * adjacent fingers. Divide the single contact between the two points.
*/ if (fingers_x == 1) {
i = (x_low.num_bits - 1) / 2;
x_low.num_bits = x_low.num_bits - i;
x_high.start_bit = x_low.start_bit + i;
x_high.num_bits = max(i, 1);
} if (fingers_y == 1) {
i = (y_low.num_bits - 1) / 2;
y_low.num_bits = y_low.num_bits - i;
y_high.start_bit = y_low.start_bit + i;
y_high.num_bits = max(i, 1);
}
/* x-bitmap order is reversed on v5 touchpads */ if (priv->proto_version == ALPS_PROTO_V5) { for (i = 0; i < 4; i++)
corner[i].x = priv->x_max - corner[i].x;
}
/* y-bitmap order is reversed on v3 and v4 touchpads */ if (priv->proto_version == ALPS_PROTO_V3 ||
priv->proto_version == ALPS_PROTO_V4) { for (i = 0; i < 4; i++)
corner[i].y = priv->y_max - corner[i].y;
}
/* * We only select a corner for the second touch once per 2 finger * touch sequence to avoid the chosen corner (and thus the coordinates) * jumping around when the first touch is in the middle.
*/ if (priv->second_touch == -1) { /* Find corner closest to our st coordinates */
closest = 0x7fffffff; for (i = 0; i < 4; i++) { int dx = fields->st.x - corner[i].x; int dy = fields->st.y - corner[i].y; int distance = dx * dx + dy * dy;
if (distance < closest) {
priv->second_touch = i;
closest = distance;
}
} /* And select the opposite corner to use for the 2nd touch */
priv->second_touch = (priv->second_touch + 2) % 4;
}
/* Use st data when we don't have mt data */ if (fingers < 2) {
f->mt[0].x = f->st.x;
f->mt[0].y = f->st.y;
fingers = f->pressure > 0 ? 1 : 0;
priv->second_touch = -1;
}
if (fingers >= 1)
alps_set_slot(dev, 0, f->mt[0].x, f->mt[0].y); if (fingers >= 2)
alps_set_slot(dev, 1, f->mt[1].x, f->mt[1].y);
input_mt_sync_frame(dev);
/* It should be a DualPoint when received trackstick packet */ if (!(priv->flags & ALPS_DUALPOINT)) {
psmouse_warn(psmouse, "Rejected trackstick packet from non DualPoint device"); return;
}
/* * There's a special packet that seems to indicate the end * of a stream of trackstick data. Filter these out.
*/ if (packet[1] == 0x7f && packet[2] == 0x7f && packet[4] == 0x7f) return;
x = (s8)(((packet[0] & 0x20) << 2) | (packet[1] & 0x7f));
y = (s8)(((packet[0] & 0x10) << 3) | (packet[2] & 0x7f));
z = packet[4] & 0x7f;
/* * The x and y values tend to be quite large, and when used * alone the trackstick is difficult to use. Scale them down * to compensate.
*/
x /= 8;
y /= 8;
/* * Most ALPS models report the trackstick buttons in the touchpad * packets, but a few report them here. No reliable way has been * found to differentiate between the models upfront, so we enable * the quirk in response to seeing a button press in the trackstick * packet.
*/
left = packet[3] & 0x01;
right = packet[3] & 0x02;
middle = packet[3] & 0x04;
if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) &&
(left || right || middle))
priv->quirks |= ALPS_QUIRK_TRACKSTICK_BUTTONS;
/* * There's no single feature of touchpad position and bitmap packets * that can be used to distinguish between them. We rely on the fact * that a bitmap packet should always follow a position packet with * bit 6 of packet[4] set.
*/ if (priv->multi_packet) { /* * Sometimes a position packet will indicate a multi-packet * sequence, but then what follows is another position * packet. Check for this, and when it happens process the * position packet as usual.
*/ if (f->is_mp) {
fingers = f->fingers; /* * Bitmap processing uses position packet's coordinate * data, so we need to do decode it first.
*/
priv->decode_fields(f, priv->multi_data, psmouse); if (alps_process_bitmap(priv, f) == 0)
fingers = 0; /* Use st data */
} else {
priv->multi_packet = 0;
}
}
/* * Bit 6 of byte 0 is not usually set in position packets. The only * times it seems to be set is in situations where the data is * suspect anyway, e.g. a palm resting flat on the touchpad. Given * this combined with the fact that this bit is useful for filtering * out misidentified bitmap packets, we reject anything with this * bit set.
*/ if (f->is_mp) return;
/* * Sometimes the hardware sends a single packet with z = 0 * in the middle of a stream. Real releases generate packets * with x, y, and z all zero, so these seem to be flukes. * Ignore them.
*/ if (f->st.x && f->st.y && !f->pressure) return;
/* * v3 protocol packets come in three types, two representing * touchpad data and one representing trackstick data. * Trackstick packets seem to be distinguished by always * having 0x3f in the last byte. This value has never been * observed in the last byte of either of the other types * of packets.
*/ if (packet[5] == 0x3f) {
alps_process_trackstick_packet_v3(psmouse); return;
}
/* * We can use Byte5 to distinguish if the packet is from Touchpad * or Trackpoint. * Touchpad: 0 - 0x7E * Trackpoint: 0x7F
*/ if (packet[5] == 0x7F) { /* It should be a DualPoint when received Trackpoint packet */ if (!(priv->flags & ALPS_DUALPOINT)) {
psmouse_warn(psmouse, "Rejected trackstick packet from non DualPoint device"); return;
}
/* Trackpoint packet */
x = packet[1] | ((packet[3] & 0x20) << 2);
y = packet[2] | ((packet[3] & 0x40) << 1);
z = packet[4];
/* To prevent the cursor jump when finger lifted */ if (x == 0x7F && y == 0x7F && z == 0x7F)
x = y = z = 0;
/* Divide 4 since trackpoint's speed is too fast */
input_report_rel(dev2, REL_X, (s8)x / 4);
input_report_rel(dev2, REL_Y, -((s8)y / 4));
psmouse_report_standard_buttons(dev2, packet[3]);
input_sync(dev2); return;
}
/* Touchpad packet */
x = packet[1] | ((packet[3] & 0x78) << 4);
y = packet[2] | ((packet[4] & 0x78) << 4);
z = packet[5];
if (z > 30)
input_report_key(dev, BTN_TOUCH, 1); if (z < 25)
input_report_key(dev, BTN_TOUCH, 0);
if (z > 0) {
input_report_abs(dev, ABS_X, x);
input_report_abs(dev, ABS_Y, y);
}
input_report_abs(dev, ABS_PRESSURE, z);
input_report_key(dev, BTN_TOOL_FINGER, z > 0);
/* v6 touchpad does not have middle button */
packet[3] &= ~BIT(2);
psmouse_report_standard_buttons(dev2, packet[3]);
/* * v4 has a 6-byte encoding for bitmap data, but this data is * broken up between 3 normal packets. Use priv->multi_packet to * track our position in the bitmap packet.
*/ if (packet[6] & 0x40) { /* sync, reset position */
priv->multi_packet = 0;
}
switch (pkt_id) { case V7_PACKET_ID_TWO:
mt[1].x &= ~0x000F;
mt[1].y |= 0x000F; /* Detect false-positive touches where x & y report max value */ if (mt[1].y == 0x7ff && mt[1].x == 0xff0) {
mt[1].x = 0; /* y gets set to 0 at the end of this function */
} break;
pkt_id = alps_get_packet_id_v7(p); if (pkt_id == V7_PACKET_ID_IDLE) return 0; if (pkt_id == V7_PACKET_ID_UNKNOWN) return -1; /* * NEW packets are send to indicate a discontinuity in the finger * coordinate reporting. Specifically a finger may have moved from * slot 0 to 1 or vice versa. INPUT_MT_TRACK takes care of this for * us. * * NEW packets have 3 problems: * 1) They do not contain middle / right button info (on non clickpads) * this can be worked around by preserving the old button state * 2) They do not contain an accurate fingercount, and they are * typically send when the number of fingers changes. We cannot use * the old finger count as that may mismatch with the amount of * touch coordinates we've available in the NEW packet * 3) Their x data for the second touch is inaccurate leading to * a possible jump of the x coordinate by 16 units when the first * non NEW packet comes in * Since problems 2 & 3 cannot be worked around, just ignore them.
*/ if (pkt_id == V7_PACKET_ID_NEW) return 1;
/* Sometimes a single touch is reported in mt[1] rather then mt[0] */ if (f->fingers == 1 && f->mt[0].x == 0 && f->mt[0].y == 0) {
f->mt[0].x = f->mt[1].x;
f->mt[0].y = f->mt[1].y;
f->mt[1].x = 0;
f->mt[1].y = 0;
}
/* It should be a DualPoint when received trackstick packet */ if (!(priv->flags & ALPS_DUALPOINT)) {
psmouse_warn(psmouse, "Rejected trackstick packet from non DualPoint device"); return;
}
/* Current packet is 1Finger coordinate packet */ switch (pkt_id) { case SS4_PACKET_ID_ONE:
f->mt[0].x = SS4_1F_X_V2(p);
f->mt[0].y = SS4_1F_Y_V2(p);
f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f; /* * When a button is held the device will give us events * with x, y, and pressure of 0. This causes annoying jumps * if a touch is released while the button is held. * Handle this by claiming zero contacts.
*/
f->fingers = f->pressure > 0 ? 1 : 0;
f->first_mp = 0;
f->is_mp = 0; break;
memset(f, 0, sizeof(struct alps_fields));
priv->decode_fields(f, packet, psmouse); if (priv->multi_packet) { /* * Sometimes the first packet will indicate a multi-packet * sequence, but sometimes the next multi-packet would not * come. Check for this, and when it happens process the * position packet as usual.
*/ if (f->is_mp) { /* Now process the 1st packet */
priv->decode_fields(f, priv->multi_data, psmouse);
} else {
priv->multi_packet = 0;
}
}
/* * "f.is_mp" would always be '0' after merging the 1st and 2nd packet. * When it is set, it means 2nd packet comes without 1st packet come.
*/ if (f->is_mp) return;
/* Save the first packet */ if (!priv->multi_packet && f->first_mp) {
priv->multi_packet = 1;
memcpy(priv->multi_data, packet, sizeof(priv->multi_data)); return;
}
priv->multi_packet = 0;
/* Report trackstick */ if (alps_get_pkt_id_ss4_v2(packet) == SS4_PACKET_ID_STICK) { if (!(priv->flags & ALPS_DUALPOINT)) {
psmouse_warn(psmouse, "Rejected trackstick packet from non DualPoint device"); return;
}
/* * format of input device name is: "protocol vendor name" * see function psmouse_switch_protocol() in psmouse-base.c
*/
dev3->name = "PS/2 ALPS Mouse";
if (!priv->dev3) {
error = alps_do_register_bare_ps2_mouse(priv); if (error) { /* * Save the error code so that we can detect that we * already tried to create the device.
*/
priv->dev3 = ERR_PTR(error);
}
}
}
/* Figure out which device to use to report the bare packet */ if (priv->proto_version == ALPS_PROTO_V2 &&
(priv->flags & ALPS_DUALPOINT)) { /* On V2 devices the DualPoint Stick reports bare packets */
dev = priv->dev2;
dev2 = psmouse->dev;
} elseif (unlikely(IS_ERR_OR_NULL(priv->dev3))) { /* Register dev3 mouse if we received PS/2 packet first time */ if (!IS_ERR(priv->dev3))
psmouse_queue_work(psmouse, &priv->dev3_register_work,
0); return;
} else {
dev = priv->dev3;
}
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;
}
timer_delete(&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.
*/
/* 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 * because 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 * de-synchronization.
*/
/* * 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;
}
/* * 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) {
psmouse_dbg(psmouse, "refusing packet %3ph (suspected interleaved ps/2)\n",
psmouse->packet + 3);
} else {
priv->process_packet(psmouse);
}
psmouse->pktcnt = 0;
}
}
/* * Check if we are dealing with a bare PS/2 packet, presumably from * a device connected to the external PS/2 port. Because bare PS/2 * protocol does not have enough constant bits to self-synchronize * properly we only do this if the device is fully synchronized. * Can not distinguish V8's first byte from PS/2 packet's
*/ if (priv->proto_version != ALPS_PROTO_V8 &&
!psmouse->out_of_sync_cnt &&
(psmouse->packet[0] & 0xc8) == 0x08) {
/* Bytes 2 - pktsize should have 0 in the highest bit */ if (priv->proto_version < ALPS_PROTO_V5 &&
psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize &&
(psmouse->packet[psmouse->pktcnt - 1] & 0x80)) {
psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
psmouse->pktcnt - 1,
psmouse->packet[psmouse->pktcnt - 1]);
if (priv->proto_version == ALPS_PROTO_V3_RUSHMORE &&
psmouse->pktcnt == psmouse->pktsize) { /* * Some Dell boxes, such as Latitude E6440 or E7440 * with closed lid, quite often smash last byte of * otherwise valid packet with 0xff. Given that the * next packet is very likely to be valid let's * report PSMOUSE_FULL_PACKET but not process data, * rather than reporting PSMOUSE_BAD_DATA and * filling the logs.
*/ return PSMOUSE_FULL_PACKET;
}
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) return -1;
/* * The address being read is returned in the first two bytes * of the result. Check that this address matches the expected * address.
*/ if (addr != ((param[0] << 8) | param[1])) return -1;
return param[2];
}
staticint alps_command_mode_read_reg(struct psmouse *psmouse, int addr)
{ if (alps_command_mode_set_addr(psmouse, addr)) return -1; return __alps_command_mode_read_reg(psmouse, addr);
}
staticint __alps_command_mode_write_reg(struct psmouse *psmouse, u8 value)
{ if (alps_command_mode_send_nibble(psmouse, (value >> 4) & 0xf)) return -1; if (alps_command_mode_send_nibble(psmouse, value & 0xf)) return -1; return 0;
}
staticint alps_command_mode_write_reg(struct psmouse *psmouse, int addr,
u8 value)
{ if (alps_command_mode_set_addr(psmouse, addr)) return -1; return __alps_command_mode_write_reg(psmouse, value);
}
staticint alps_rpt_cmd(struct psmouse *psmouse, int init_command, int repeated_command, unsignedchar *param)
{ struct ps2dev *ps2dev = &psmouse->ps2dev;
/* * For DualPoint devices select the device that should respond to * subsequent commands. It looks like glidepad is behind stickpointer, * I'd thought it would be other way around...
*/ staticint alps_passthrough_mode_v2(struct psmouse *psmouse, bool enable)
{ struct ps2dev *ps2dev = &psmouse->ps2dev; int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11;
/* * Switch mouse to poll (remote) mode so motion data will not * get in our way
*/ return ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL);
}
staticint alps_monitor_mode_send_word(struct psmouse *psmouse, u16 word)
{ int i, nibble;
/* * b0-b11 are valid bits, send sequence is inverse. * e.g. when word = 0x0123, nibble send sequence is 3, 2, 1
*/ for (i = 0; i <= 8; i += 4) {
nibble = (word >> i) & 0xf; if (alps_command_mode_send_nibble(psmouse, nibble)) return -1;
}
/* 0x0A0 is the command to write the word */ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE) ||
alps_monitor_mode_send_word(psmouse, 0x0A0) ||
alps_monitor_mode_send_word(psmouse, addr) ||
alps_monitor_mode_send_word(psmouse, value) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE)) return -1;
/* * Turn touchpad tapping on or off. The sequences are: * 0xE9 0xF5 0xF5 0xF3 0x0A to enable, * 0xE9 0xF5 0xF5 0xE8 0x00 to disable. * My guess that 0xE9 (GetInfo) is here as a sync point. * For models that also have stickpointer (DualPoints) its tapping * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but * we don't fiddle with it.
*/ staticint alps_tap_mode(struct psmouse *psmouse, int enable)
{ struct ps2dev *ps2dev = &psmouse->ps2dev; int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES; unsignedchar tap_arg = enable ? 0x0A : 0x00; unsignedchar param[4];
/* ALPS needs stream mode, otherwise it won't report any data */ if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) {
psmouse_err(psmouse, "Failed to enable stream mode\n"); return -1;
}
return 0;
}
/* Must be in passthrough mode when calling this function */ staticint alps_trackstick_enter_extended_mode_v3_v6(struct psmouse *psmouse)
{ unsignedchar param[2] = {0xC8, 0x14};
staticint alps_hw_init_v6(struct psmouse *psmouse)
{ int ret;
/* Enter passthrough mode to let trackpoint enter 6byte raw mode */ if (alps_passthrough_mode_v2(psmouse, true)) return -1;
ret = alps_trackstick_enter_extended_mode_v3_v6(psmouse);
if (alps_passthrough_mode_v2(psmouse, false)) return -1;
if (ret) return ret;
if (alps_absolute_mode_v6(psmouse)) {
psmouse_err(psmouse, "Failed to enable absolute mode\n"); return -1;
}
return 0;
}
/* * Enable or disable passthrough mode to the trackstick.
*/ staticint alps_passthrough_mode_v3(struct psmouse *psmouse, int reg_base, bool enable)
{ int reg_val, ret = -1;
staticint alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
{ int ret = 0; int reg_val; unsignedchar param[4];
/* * We need to configure trackstick to report data for touchpad in * extended format. And also we need to tell touchpad to expect data * from trackstick in extended format. Without this configuration * trackstick packets sent from touchpad are in basic format which is * different from what we expect.
*/
if (alps_passthrough_mode_v3(psmouse, reg_base, true)) return -EIO;
/* * E7 report for the trackstick * * There have been reports of failures to seem to trace back * to the above trackstick check failing. When these occur * this E7 report fails, so when that happens we continue * with the assumption that there isn't a trackstick after * all.
*/ if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_SETSCALE21, param)) {
psmouse_warn(psmouse, "Failed to initialize trackstick (E7 report failed)\n");
ret = -ENODEV;
} else {
psmouse_dbg(psmouse, "trackstick E7 report: %3ph\n", param); if (alps_trackstick_enter_extended_mode_v3_v6(psmouse)) {
psmouse_err(psmouse, "Failed to enter into trackstick extended mode\n");
ret = -EIO;
}
}
if (alps_passthrough_mode_v3(psmouse, reg_base, false)) return -EIO;
if (ret) return ret;
if (alps_enter_command_mode(psmouse)) return -EIO;
reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08); if (reg_val == -1) {
ret = -EIO;
} else { /* * Tell touchpad that trackstick is now in extended mode. * If bit 1 isn't set the packet format is different.
*/
reg_val |= BIT(1); if (__alps_command_mode_write_reg(psmouse, reg_val))
ret = -EIO;
}
if (alps_enter_command_mode(psmouse) ||
alps_absolute_mode_v3(psmouse)) {
psmouse_err(psmouse, "Failed to enter absolute mode\n"); goto error;
}
reg_val = alps_command_mode_read_reg(psmouse, 0x0006); if (reg_val == -1) goto error; if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) goto error;
reg_val = alps_command_mode_read_reg(psmouse, 0x0007); if (reg_val == -1) goto error; if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) goto error;
if (alps_command_mode_read_reg(psmouse, 0x0144) == -1) goto error; if (__alps_command_mode_write_reg(psmouse, 0x04)) goto error;
if (alps_command_mode_read_reg(psmouse, 0x0159) == -1) goto error; if (__alps_command_mode_write_reg(psmouse, 0x03)) goto error;
if (alps_command_mode_read_reg(psmouse, 0x0163) == -1) goto error; if (alps_command_mode_write_reg(psmouse, 0x0163, 0x03)) goto error;
if (alps_command_mode_read_reg(psmouse, 0x0162) == -1) goto error; if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04)) goto error;
alps_exit_command_mode(psmouse);
/* Set rate and enable data reporting */
param[0] = 0x64; if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
psmouse_err(psmouse, "Failed to enable data reporting\n"); return -1;
}
return 0;
error: /* * Leaving the touchpad in command mode will essentially render * it unusable until the machine reboots, so exit it here just * to be safe
*/
alps_exit_command_mode(psmouse); return -1;
}
staticint alps_get_v3_v7_resolution(struct psmouse *psmouse, int reg_pitch)
{ int reg, x_pitch, y_pitch, x_electrode, y_electrode, x_phys, y_phys; struct alps_data *priv = psmouse->private;
reg = alps_command_mode_read_reg(psmouse, reg_pitch); if (reg < 0) return reg;
x_pitch = (s8)(reg << 4) >> 4; /* sign extend lower 4 bits */
x_pitch = 50 + 2 * x_pitch; /* In 0.1 mm units */
y_pitch = (s8)reg >> 4; /* sign extend upper 4 bits */
y_pitch = 36 + 2 * y_pitch; /* In 0.1 mm units */
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.