From patchwork Fri Jan 15 08:14:16 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Diamand X-Patchwork-Id: 8039141 Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id B113CBEEE5 for ; Fri, 15 Jan 2016 08:14:50 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 4079620426 for ; Fri, 15 Jan 2016 08:14:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id AADDB2041C for ; Fri, 15 Jan 2016 08:14:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751360AbcAOIOq (ORCPT ); Fri, 15 Jan 2016 03:14:46 -0500 Received: from mail-wm0-f66.google.com ([74.125.82.66]:36826 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751105AbcAOIOp (ORCPT ); Fri, 15 Jan 2016 03:14:45 -0500 Received: by mail-wm0-f66.google.com with SMTP id l65so1571956wmf.3 for ; Fri, 15 Jan 2016 00:14:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=diamand.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=YddBD71M/KM6Qwxgx9S4CiWQIDMjhLpyN3uPvrJ9RIg=; b=WavNIm2wmVkpF0YGgBdpQW0RnIc54vNVcdzWPMad9DX60RozPVbQt/uJwi9FpT3Tgt 29zsYMJmix5L/b4MS6HrLglK8Qc4JHKMLjpX48LVsbOFTkUjt5pkk5KBTcBRbTzd+dDV UoQ1L1ZhHp/JwTbsI31kIyhkwqi+mMixBp72o= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=YddBD71M/KM6Qwxgx9S4CiWQIDMjhLpyN3uPvrJ9RIg=; b=bUUc9OtostLKpNSywH8tqKAevIKksqIdAKI4tJC0W7eXkliYxZxGK4h3jvYuGxeQ+/ 8hJ4Caur0nrtVl/I6FqVQ6X4IfRDv3EkPDah2e9BbLI1n/lJnyzAV3sYSCVdR6zHozR+ XnjJZD4nUVxWxoyJwt1QjX52xVTGhsGMBKj7IyM3yIrewLj/IiS8Vch5w55VYrWUJx6a szWnEPgGeIJlMA00gMFWCiX4wXToMYxeUp7mXiYWPrhy2mt8QB6BSL7s14rbZKF30Wz6 KXvc4c7uuqHPc5P3QPc3hqR/BHe52yuw8N+6zQwbcLdnSsir3HtS4cRJkiJyc8V6uOUg XVZg== X-Gm-Message-State: ALoCoQk26UEgOM8NzyrTvmr21BSWazdg8dxk2FZyX323oWvgXM5obbzYw89Wmy9ajEKgQ/l8gAiq7J/8HVHiMZjjenUBmGZ5YQ== X-Received: by 10.194.113.102 with SMTP id ix6mr9725351wjb.143.1452845684122; Fri, 15 Jan 2016 00:14:44 -0800 (PST) Received: from nemo.local.diamand.org (cpc92798-cmbg19-2-0-cust327.5-4.cable.virginm.net. [80.1.41.72]) by smtp.gmail.com with ESMTPSA id l67sm1397612wmf.11.2016.01.15.00.14.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 15 Jan 2016 00:14:43 -0800 (PST) From: Chris Diamand To: dmitry.torokhov@gmail.com, linux-input@vger.kernel.org Cc: Chris Diamand Subject: [PATCH v2] Input: byd - add BYD PS/2 touchpad driver Date: Fri, 15 Jan 2016 08:14:16 +0000 Message-Id: <1452845656-25564-1-git-send-email-chris@diamand.org> X-Mailer: git-send-email 2.1.4 In-Reply-To: <20160115081110.GA10700@smaug> References: <20160115081110.GA10700@smaug> Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD,T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Driver for the BYD BTP10463 touchpad, found in PC Specialist `Lafite' laptops. This patch sends the magic command sequence which causes the touchpad to stream intellimouse-style packets. Gestures are detected inside the touchpad, and exposed as special values in the Z component of each packet - absolute coordinates are not supported, even in the Windows driver. At present, this supports two-finger vertical and horizontal scrolling, and provides the framework to expose the other gestures it can recognize. Signed-off-by: Chris Diamand --- Changed in v2: - Applies to linux-next. - Combine movement and button reporting. - Use BIT and ARRAY_SIZE macros. - Change Z values from enum to #define, and document the meanings. - Check result of ps2_command during intellimouse initialisation. - psmouse_dbg instead of psmouse_info. - Mention the machine and lack of absolute mode in commit message. drivers/input/mouse/Kconfig | 10 ++ drivers/input/mouse/Makefile | 1 + drivers/input/mouse/byd.c | 341 +++++++++++++++++++++++++++++++++++++ drivers/input/mouse/byd.h | 18 ++ drivers/input/mouse/psmouse-base.c | 14 ++ drivers/input/mouse/psmouse.h | 1 + 6 files changed, 385 insertions(+) create mode 100644 drivers/input/mouse/byd.c create mode 100644 drivers/input/mouse/byd.h diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 17f97e5..096abb4 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -48,6 +48,16 @@ config MOUSE_PS2_ALPS If unsure, say Y. +config MOUSE_PS2_BYD + bool "BYD PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have a BYD PS/2 touchpad connected to + your system. + + If unsure, say Y. + config MOUSE_PS2_LOGIPS2PP bool "Logitech PS/2++ mouse protocol extension" if EXPERT default y diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index ee6a6e9..6168b13 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -28,6 +28,7 @@ cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o cyapa_gen6.o psmouse-objs := psmouse-base.o synaptics.o focaltech.o psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o +psmouse-$(CONFIG_MOUSE_PS2_BYD) += byd.o psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o diff --git a/drivers/input/mouse/byd.c b/drivers/input/mouse/byd.c new file mode 100644 index 0000000..8d39c96 --- /dev/null +++ b/drivers/input/mouse/byd.c @@ -0,0 +1,341 @@ +/* + * BYD TouchPad PS/2 mouse driver + * + * Copyright (C) 2015 Chris Diamand + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "psmouse.h" +#include "byd.h" + +#define PS2_Y_OVERFLOW BIT_MASK(7) +#define PS2_X_OVERFLOW BIT_MASK(6) +#define PS2_Y_SIGN BIT_MASK(5) +#define PS2_X_SIGN BIT_MASK(4) +#define PS2_ALWAYS_1 BIT_MASK(3) +#define PS2_MIDDLE BIT_MASK(2) +#define PS2_RIGHT BIT_MASK(1) +#define PS2_LEFT BIT_MASK(0) + +/* + * The touchpad reports gestures in the last byte of each packet. It can take + * any of the following values: + */ +/* One-finger scrolling in one of the edge scroll zones. */ +#define BYD_SCROLLUP 0xCA +#define BYD_SCROLLDOWN 0x36 +#define BYD_SCROLLLEFT 0xCB +#define BYD_SCROLLRIGHT 0x35 +/* Two-finger scrolling. */ +#define BYD_2DOWN 0x2B +#define BYD_2UP 0xD5 +#define BYD_2LEFT 0xD6 +#define BYD_2RIGHT 0x2A +/* Pinching in or out. */ +#define BYD_ZOOMOUT 0xD8 +#define BYD_ZOOMIN 0x28 +/* Three-finger swipe. */ +#define BYD_3UP 0xD3 +#define BYD_3DOWN 0x2D +#define BYD_3LEFT 0xD4 +#define BYD_3RIGHT 0x2C +/* Four-finger swipe. */ +#define BYD_4UP 0xCD +#define BYD_4DOWN 0x33 + +int byd_detect(struct psmouse *psmouse, bool set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + + param[0] = 0x03; + param[1] = 0x00; + param[2] = 0x00; + param[3] = 0x00; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -1; + + if (param[1] != 0x03 || param[2] != 0x64) + return -ENODEV; + + psmouse_dbg(psmouse, "BYD touchpad detected\n"); + + if (set_properties) { + psmouse->vendor = "BYD"; + psmouse->name = "TouchPad"; + } + + return 0; +} + +static int interpret_scroll(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + u8 scroll = psmouse->packet[3]; + + switch (scroll) { + case BYD_SCROLLDOWN: + case BYD_2DOWN: + input_report_rel(dev, REL_WHEEL, -1); + return 1; + case BYD_SCROLLUP: + case BYD_2UP: + input_report_rel(dev, REL_WHEEL, 1); + return 1; + case BYD_SCROLLLEFT: + case BYD_2LEFT: + input_report_rel(dev, REL_HWHEEL, -1); + return 1; + case BYD_SCROLLRIGHT: + case BYD_2RIGHT: + input_report_rel(dev, REL_HWHEEL, 1); + return 1; + case BYD_ZOOMOUT: + case BYD_ZOOMIN: + case BYD_3UP: + case BYD_3DOWN: + case BYD_3LEFT: + case BYD_3RIGHT: + case BYD_4UP: + case BYD_4DOWN: + return 1; + } + + if (scroll != 0) { + psmouse_warn(psmouse, + "Unrecognized Z: pkt = %02x %02x %02x %02x\n", + psmouse->packet[0], psmouse->packet[1], + psmouse->packet[2], psmouse->packet[3]); + return 1; + } + return 0; +} + +static psmouse_ret_t byd_process_byte(struct psmouse *psmouse) +{ + u8 *pkt = psmouse->packet; + + if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) { + psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n", + pkt[0]); + return PSMOUSE_BAD_DATA; + } + + if (psmouse->pktcnt < psmouse->pktsize) + return PSMOUSE_GOOD_DATA; + + /* Otherwise, a full packet has been received */ + if (!interpret_scroll(psmouse)) { + /* Sign-extend if a sign bit is set. */ + unsigned int signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0; + unsigned int signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0; + int dx = signx | (int) pkt[1]; + int dy = signy | (int) pkt[2]; + + input_report_rel(psmouse->dev, REL_X, dx); + input_report_rel(psmouse->dev, REL_Y, -dy); + + input_report_key(psmouse->dev, BTN_LEFT, pkt[0] & PS2_LEFT); + input_report_key(psmouse->dev, BTN_RIGHT, pkt[0] & PS2_RIGHT); + input_report_key(psmouse->dev, BTN_MIDDLE, pkt[0] & PS2_MIDDLE); + } + + input_sync(psmouse->dev); + + return PSMOUSE_FULL_PACKET; +} + +/* Send a sequence of bytes, where each is ACKed before the next is sent. */ +static int send_sequence(struct psmouse *psmouse, const u8 *seq, size_t len) +{ + unsigned int i; + + for (i = 0; i < len; ++i) { + if (ps2_command(&psmouse->ps2dev, NULL, seq[i])) + return -1; + } + return 0; +} + +/* Keep scrolling after fingers are removed. */ +#define SCROLL_INERTIAL 0x01 +#define SCROLL_NO_INERTIAL 0x02 + +/* Clicking can be done by tapping or pressing. */ +#define CLICK_BOTH 0x01 +/* Clicking can only be done by pressing. */ +#define CLICK_PRESS_ONLY 0x02 + +static int byd_enable(struct psmouse *psmouse) +{ + const u8 seq1[] = { 0xE2, 0x00, 0xE0, 0x02, 0xE0 }; + const u8 seq2[] = { + 0xD3, 0x01, + 0xD0, 0x00, + 0xD0, 0x04, + /* Whether clicking is done by tapping or pressing. */ + 0xD4, CLICK_PRESS_ONLY, + 0xD5, 0x01, + 0xD7, 0x03, + /* Vertical and horizontal one-finger scroll zone inertia. */ + 0xD8, SCROLL_INERTIAL, + 0xDA, 0x05, + 0xDB, 0x02, + 0xE4, 0x05, + 0xD6, 0x01, + 0xDE, 0x04, + 0xE3, 0x01, + 0xCF, 0x00, + 0xD2, 0x03, + /* Vertical and horizontal two-finger scrolling inertia. */ + 0xE5, SCROLL_INERTIAL, + 0xD9, 0x02, + 0xD9, 0x07, + 0xDC, 0x03, + 0xDD, 0x03, + 0xDF, 0x03, + 0xE1, 0x03, + 0xD1, 0x00, + 0xCE, 0x00, + 0xCC, 0x00, + 0xE0, 0x00, + 0xE2, 0x01 + }; + u8 param[4]; + + if (send_sequence(psmouse, seq1, ARRAY_SIZE(seq1))) + return -1; + + /* Send a 0x01 command, which should return 4 bytes. */ + if (ps2_command(&psmouse->ps2dev, param, 0x0401)) + return -1; + + if (send_sequence(psmouse, seq2, ARRAY_SIZE(seq2))) + return -1; + + return 0; +} + +/* + * Send the set of PS/2 commands required to make it identify as an + * intellimouse with 4-byte instead of 3-byte packets. + */ +static int send_intellimouse_sequence(struct psmouse *psmouse) +{ + int i; + struct ps2dev *ps2dev = &psmouse->ps2dev; + u8 param[4]; + + const struct { + u16 command; + u8 arg; + } seq[] = { + { PSMOUSE_CMD_RESET_BAT, 0 }, + { PSMOUSE_CMD_RESET_BAT, 0 }, + { PSMOUSE_CMD_GETID, 0 }, + { PSMOUSE_CMD_SETSCALE11, 0 }, + { PSMOUSE_CMD_SETSCALE11, 0 }, + { PSMOUSE_CMD_SETSCALE11, 0 }, + { PSMOUSE_CMD_GETINFO, 0 }, + { PSMOUSE_CMD_SETRES, 0x03 }, + { PSMOUSE_CMD_SETRATE, 0xC8 }, + { PSMOUSE_CMD_SETRATE, 0x64 }, + { PSMOUSE_CMD_SETRATE, 0x50 }, + { PSMOUSE_CMD_GETID, 0 }, + { PSMOUSE_CMD_SETRATE, 0xC8 }, + { PSMOUSE_CMD_SETRATE, 0xC8 }, + { PSMOUSE_CMD_SETRATE, 0x50 }, + { PSMOUSE_CMD_GETID, 0 }, + { PSMOUSE_CMD_SETRATE, 0x64 }, + { PSMOUSE_CMD_SETRES, 0x03 }, + { PSMOUSE_CMD_ENABLE, 0 } + }; + + memset(param, 0, sizeof(param)); + for (i = 0; i < ARRAY_SIZE(seq); ++i) { + param[0] = seq[i].arg; + if (ps2_command(ps2dev, param, seq[i].command)) + return -1; + } + + return 0; +} + +static int reset_touchpad(struct psmouse *psmouse) +{ + if (send_intellimouse_sequence(psmouse)) + return -EIO; + + if (byd_enable(psmouse)) + return -EIO; + + return 0; +} + +static int byd_reconnect(struct psmouse *psmouse) +{ + int retry = 0, error = 0; + + psmouse_dbg(psmouse, "Reconnect\n"); + do { + psmouse_reset(psmouse); + if (retry) + ssleep(1); + error = byd_detect(psmouse, 0); + } while (error && ++retry < 3); + + if (error) + return error; + psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry); + + error = reset_touchpad(psmouse); + if (error) { + psmouse_err(psmouse, "Unable to initialize device\n"); + return error; + } + + return 0; +} + +int byd_init(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + + if (psmouse_reset(psmouse)) + return -EIO; + if (reset_touchpad(psmouse)) + return -EIO; + + psmouse->reconnect = byd_reconnect; + psmouse->protocol_handler = byd_process_byte; + psmouse->pktsize = 4; + psmouse->resync_time = 0; + + __set_bit(EV_KEY, dev->evbit); + __set_bit(BTN_LEFT, dev->keybit); + __set_bit(BTN_MIDDLE, dev->keybit); + __set_bit(BTN_RIGHT, dev->keybit); + + __set_bit(EV_REL, dev->evbit); + __set_bit(REL_WHEEL, dev->relbit); + __set_bit(REL_HWHEEL, dev->relbit); + + return 0; +} diff --git a/drivers/input/mouse/byd.h b/drivers/input/mouse/byd.h new file mode 100644 index 0000000..d6c120c --- /dev/null +++ b/drivers/input/mouse/byd.h @@ -0,0 +1,18 @@ +#ifndef _BYD_H +#define _BYD_H + +#ifdef CONFIG_MOUSE_PS2_BYD +int byd_detect(struct psmouse *psmouse, bool set_properties); +int byd_init(struct psmouse *psmouse); +#else +static inline int byd_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +static inline int byd_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_BYD */ + +#endif /* _BYD_H */ diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index b9e4ee3..39d1bec 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -37,6 +37,7 @@ #include "cypress_ps2.h" #include "focaltech.h" #include "vmmouse.h" +#include "byd.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -842,6 +843,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { .init = vmmouse_init, }, #endif +#ifdef CONFIG_MOUSE_PS2_BYD + { + .type = PSMOUSE_BYD, + .name = "BydPS/2", + .alias = "byd", + .detect = byd_detect, + .init = byd_init, + }, +#endif { .type = PSMOUSE_AUTO, .name = "auto", @@ -1105,6 +1115,10 @@ static int psmouse_extensions(struct psmouse *psmouse, if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2, &max_proto, set_properties, true)) return PSMOUSE_TOUCHKIT_PS2; + + if (psmouse_try_protocol(psmouse, PSMOUSE_BYD, + &max_proto, set_properties, true)) + return PSMOUSE_BYD; } /* diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index ad5a5a1..e0ca6cd 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -104,6 +104,7 @@ enum psmouse_type { PSMOUSE_CYPRESS, PSMOUSE_FOCALTECH, PSMOUSE_VMMOUSE, + PSMOUSE_BYD, PSMOUSE_AUTO /* This one should always be last */ };