From patchwork Fri Jan 19 19:41:10 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Torokhov X-Patchwork-Id: 10175923 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 81A126055D for ; Fri, 19 Jan 2018 19:43:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 729BF212BE for ; Fri, 19 Jan 2018 19:43:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6790B28767; Fri, 19 Jan 2018 19:43:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,FREEMAIL_FROM,RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E83DD212BE for ; Fri, 19 Jan 2018 19:43:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756334AbeASTlh (ORCPT ); Fri, 19 Jan 2018 14:41:37 -0500 Received: from mail-pg0-f65.google.com ([74.125.83.65]:43838 "EHLO mail-pg0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756107AbeASTlW (ORCPT ); Fri, 19 Jan 2018 14:41:22 -0500 Received: by mail-pg0-f65.google.com with SMTP id n17so2161940pgf.10; Fri, 19 Jan 2018 11:41:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=5WQApq1rq7CW5Q3kz63MtBJ2tOQgLMREtVNXOmz0r58=; b=sUHVv8SRGzaRVPktSLmuMzKY0vUGmjGZonSmkn/mks2VWTi++/wuVKhD7tgW2t1Y73 yO+ZZtft+NiIa3yqZw4OQw2e3qHfmS2mZZvrAdzag/WO1hgRDoQknKs+gt1bT3XnYNEy B2CiG83TDk9ld5V+1T4pLnrHRDvFD94S45Pc3kf90KPjgikw56BYe/Iq41QPtkG/Bkgd K0osKJBuHEKAhtNVvG8rc84jgkkI1GamYe34Na1KTai23TlHq8VimtxroHJyzCN+YuOB cfeq63RPoWXDDMjrleQXwTk2SOSB+13Ob6NEMeOVx5rfYWF/J4RAjy8OQqELD4EcUlRn 9kmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=5WQApq1rq7CW5Q3kz63MtBJ2tOQgLMREtVNXOmz0r58=; b=LoXusHausSZ3/zhYWMXn/qND7/z6kGT7odagqB5HamHg3frPrGxC9V7OQqxAbYWY8E tNyEQEp6/vIbo/ubKzn91Pg94Au1P6A3KRCpxRi8qT0pwjyXG9sO2wRklNBaVTLj6xg5 IpTdhLjMxuHTV3VmV9roXxGBT4bBEQNv561lpTPapKZIpmJaHWbkVHurDkSK7EeuTPXB p9Ojq/N+rKmUqbRst1Wv3fwa58atWHrfHCIqa0rvscXPnDsYZWzFLKtYlfs19unGHSXM qcRvANzg8LtYv0NJ3YQQ6wfJ003DlSoIzm3pWfZ+NZ/uUSek6Efl8cbLLTCQFihy4fhV sx0Q== X-Gm-Message-State: AKwxyteehAxNkS1+7pNhr/6AbCPUMkxKCalBblIfIhsLroVdEk4kECDW UEQGbTZUortGxkMdhIlOZNo= X-Google-Smtp-Source: ACJfBovDw3StM/v7aRPMaByI7ZuOKO4UNehSxCbEns4/dr6YS+/b2/wBf9Zfjrce3D+fTTrnnVpZaA== X-Received: by 2002:a17:902:5305:: with SMTP id b5-v6mr2139658pli.325.1516390881110; Fri, 19 Jan 2018 11:41:21 -0800 (PST) Received: from dtor-ws.mtv.corp.google.com ([2620:0:1000:1611:da80:8749:c06f:9515]) by smtp.gmail.com with ESMTPSA id p1sm18947830pgr.44.2018.01.19.11.41.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 19 Jan 2018 11:41:20 -0800 (PST) From: Dmitry Torokhov To: Benjamin Tissoires , Hans de Goede , Lyude Paul Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 6/7] Input: libps2 - support retransmission of command data Date: Fri, 19 Jan 2018 11:41:10 -0800 Message-Id: <20180119194111.185590-7-dmitry.torokhov@gmail.com> X-Mailer: git-send-email 2.16.0.rc1.238.g530d649a79-goog In-Reply-To: <20180119194111.185590-1-dmitry.torokhov@gmail.com> References: <20180119194111.185590-1-dmitry.torokhov@gmail.com> Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The devices are allowed to respond to either command byte or command parameter with a NAK (0xfe), and the host is supposed to resend the "correct" byte. The device then will either respond with ACK or ERR (0xfc). Let's teach libps2 to handle the NAK responses properly, so that individual drivers do not need to handle them. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 103 +++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 32 deletions(-) diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 82befae4dab04..f05c407b31f3d 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -26,35 +26,63 @@ MODULE_AUTHOR("Dmitry Torokhov "); MODULE_DESCRIPTION("PS/2 driver library"); MODULE_LICENSE("GPL"); -static int ps2_do_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout) +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; - serio_pause_rx(ps2dev->serio); - ps2dev->nak = 1; - ps2dev->flags |= PS2_FLAG_ACK; - serio_continue_rx(ps2dev->serio); + lockdep_assert_held(&ps2dev->serio->lock); - 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)); + 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); - serio_pause_rx(ps2dev->serio); ps2dev->flags &= ~PS2_FLAG_ACK; - serio_continue_rx(ps2dev->serio); - return -ps2dev->nak; + 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. - * It doesn't handle retransmission, though it could - because if there - * is a need for retransmissions device has to be replaced anyway. + * It doesn't handle retransmission, the caller is expected to handle + * it when needed. * * ps2_sendbyte() can only be called from a process context. */ @@ -63,9 +91,13 @@ int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout) { int retval; - retval = ps2_do_sendbyte(ps2dev, byte, timeout); + serio_pause_rx(ps2dev->serio); + + retval = ps2_do_sendbyte(ps2dev, byte, timeout, 1); dev_dbg(&ps2dev->serio->dev, "%02x - %x\n", byte, ps2dev->nak); + serio_continue_rx(ps2dev->serio); + return retval; } EXPORT_SYMBOL(ps2_sendbyte); @@ -200,48 +232,48 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) unsigned int timeout; unsigned int send = (command >> 12) & 0xf; unsigned int receive = (command >> 8) & 0xf; - int rc = -1; + int rc; int i; u8 send_param[16]; if (receive > sizeof(ps2dev->cmdbuf)) { WARN_ON(1); - return -1; + return -EINVAL; } if (send && !param) { WARN_ON(1); - return -1; + return -EINVAL; } memcpy(send_param, param, send); serio_pause_rx(ps2dev->serio); + ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; ps2dev->cmdcnt = receive; if (receive && param) for (i = 0; i < receive; i++) ps2dev->cmdbuf[(receive - 1) - i] = param[i]; - serio_continue_rx(ps2dev->serio); /* * Some devices (Synaptics) peform the reset before * ACKing the reset command, and so it can take a long * time before the ACK arrives. */ - if (ps2_do_sendbyte(ps2dev, command & 0xff, - command == PS2_CMD_RESET_BAT ? 1000 : 200)) { - serio_pause_rx(ps2dev->serio); + rc = ps2_do_sendbyte(ps2dev, command & 0xff, + command == PS2_CMD_RESET_BAT ? 1000 : 200, 2); + if (rc) goto out_reset_flags; - } for (i = 0; i < send; i++) { - if (ps2_do_sendbyte(ps2dev, param[i], 200)) { - serio_pause_rx(ps2dev->serio); + rc = ps2_do_sendbyte(ps2dev, param[i], 200, 2); + if (rc) goto out_reset_flags; - } } + serio_continue_rx(ps2dev->serio); + /* * The reset command takes a long time to execute. */ @@ -263,8 +295,11 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) for (i = 0; i < receive; 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; + } rc = 0; @@ -278,7 +313,11 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) ps2dev->nak, ps2dev->flags, receive, param ?: send_param); - return rc; + /* + * ps_command() handles resends itself, so do not leak -EAGAIN + * to the callers. + */ + return rc != -EAGAIN ? rc : -EPROTO; } EXPORT_SYMBOL(__ps2_command);