From patchwork Thu Jul 12 05:47:19 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pawel Laszczak X-Patchwork-Id: 10521007 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 F347A602C8 for ; Thu, 12 Jul 2018 05:49:48 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EADE6292DB for ; Thu, 12 Jul 2018 05:49:48 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DF155292F8; Thu, 12 Jul 2018 05:49:48 +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=-7.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, MAILING_LIST_MULTI, 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 DA206292DB for ; Thu, 12 Jul 2018 05:49:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732459AbeGLF5f (ORCPT ); Thu, 12 Jul 2018 01:57:35 -0400 Received: from mail-dm3nam03on0041.outbound.protection.outlook.com ([104.47.41.41]:40246 "EHLO NAM03-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1732191AbeGLF4F (ORCPT ); Thu, 12 Jul 2018 01:56:05 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cadence.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=uA7jx8eTzJgYlX2fCCU9r/WocTIs+mTsH62njhUpGSg=; b=K+QrEQbgp50yIEu6Vth9w3uc7MMkJjOdpOQAhds9bhgjKuAxytAvXomnsyLcq7dMc2F7tuL9ous9wV6wsJ/zeAju2rURne1TAT4ceI2nRa42gJVWF9kNVzFgv9PU24cHDgjWYl3j3gbbyvDQl3gNhR4maf8z691KsqEchNejmMk= Received: from BY2PR07CA0029.namprd07.prod.outlook.com (2a01:111:e400:7bff::24) by BN7PR07MB4708.namprd07.prod.outlook.com (2603:10b6:406:f0::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.952.18; Thu, 12 Jul 2018 05:48:02 +0000 Received: from DM3NAM05FT043.eop-nam05.prod.protection.outlook.com (2a01:111:f400:7e51::207) by BY2PR07CA0029.outlook.office365.com (2a01:111:e400:7bff::24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.20.952.18 via Frontend Transport; Thu, 12 Jul 2018 05:48:02 +0000 Authentication-Results: spf=softfail (sender IP is 158.140.1.28) smtp.mailfrom=cadence.com; vger.kernel.org; dkim=none (message not signed) header.d=none;vger.kernel.org; dmarc=fail action=none header.from=cadence.com; Received-SPF: SoftFail (protection.outlook.com: domain of transitioning cadence.com discourages use of 158.140.1.28 as permitted sender) Received: from sjmaillnx1.cadence.com (158.140.1.28) by DM3NAM05FT043.mail.protection.outlook.com (10.152.98.112) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.973.9 via Frontend Transport; Thu, 12 Jul 2018 05:48:01 +0000 Received: from maileu3.global.cadence.com (maileu3.cadence.com [10.160.88.99]) by sjmaillnx1.cadence.com (8.14.4/8.14.4) with ESMTP id w6C5lkwC032054 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=FAIL); Wed, 11 Jul 2018 22:48:00 -0700 X-CrossPremisesHeadersFilteredBySendConnector: maileu3.global.cadence.com Received: from maileu3.global.cadence.com (10.160.88.99) by maileu3.global.cadence.com (10.160.88.99) with Microsoft SMTP Server (TLS) id 15.0.1367.3; Thu, 12 Jul 2018 07:48:07 +0200 Received: from lvlogina.cadence.com (10.165.176.102) by maileu3.global.cadence.com (10.160.88.99) with Microsoft SMTP Server id 15.0.1367.3 via Frontend Transport; Thu, 12 Jul 2018 07:48:07 +0200 Received: from lvlogina.cadence.com (localhost.localdomain [127.0.0.1]) by lvlogina.cadence.com (8.14.4/8.14.4) with ESMTP id w6C5lpA1029816; Thu, 12 Jul 2018 06:47:51 +0100 Received: (from pawell@localhost) by lvlogina.cadence.com (8.14.4/8.14.4/Submit) id w6C5lpXK029803; Thu, 12 Jul 2018 06:47:51 +0100 From: Pawel Laszczak CC: Greg Kroah-Hartman , , Felipe Balbi , , , , Subject: [PATCH 22/31] usb: usbssp: added procedure removing request from transfer ring Date: Thu, 12 Jul 2018 06:47:19 +0100 Message-ID: <1531374448-26532-23-git-send-email-pawell@cadence.com> X-Mailer: git-send-email 1.7.11.2 In-Reply-To: <1531374448-26532-1-git-send-email-pawell@cadence.com> References: <1531374448-26532-1-git-send-email-pawell@cadence.com> MIME-Version: 1.0 X-OrganizationHeadersPreserved: maileu3.global.cadence.com X-EOPAttributedMessage: 0 X-Forefront-Antispam-Report: CIP:158.140.1.28; IPV:CAL; SCL:-1; CTRY:US; EFV:NLI; SFV:NSPM; SFS:(10009020)(136003)(376002)(39860400002)(396003)(346002)(2980300002)(199004)(189003)(36092001)(446003)(2906002)(11346002)(106466001)(51416003)(105596002)(50466002)(1671002)(48376002)(126002)(2616005)(476003)(87636003)(486006)(50226002)(86362001)(5660300001)(54906003)(36756003)(8676002)(246002)(4720700003)(6666003)(305945005)(478600001)(356003)(26826003)(7636002)(14444005)(186003)(4326008)(26005)(16586007)(76176011)(316002)(426003)(8936002)(109986005)(47776003)(42186006)(336012)(107886003)(266003); DIR:OUT; SFP:1101; SCL:1; SRVR:BN7PR07MB4708; H:sjmaillnx1.cadence.com; FPR:; SPF:SoftFail; LANG:en; PTR:corp.cadence.com; A:1; MX:1; X-Microsoft-Exchange-Diagnostics: 1; DM3NAM05FT043; 1:HjvNHypQNA/S9N2PWfxIaZWiOKbsooQu2w9irB0G4Evk8tBcyCimJnJy9TqHW4QY/D3gDPFoUkh6u9j29TNBBU67FFiupBliIKbp+Uj2/mlWNCBmdWWJSwnYYkui5DnM X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 679a5818-ff38-47d5-fab7-08d5e7bb07d8 X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(7020095)(4652040)(8989117)(5600053)(711020)(4534165)(4627221)(201703031133081)(201702281549075)(8990107)(2017052603328)(7153060); SRVR:BN7PR07MB4708; X-Microsoft-Exchange-Diagnostics: 1; BN7PR07MB4708; 3:v7I30rEFpLJzfWYVskW+IFmUnfcMSIwqx4Y13MhlbC+cXE0D2igqUnz58jNM6TPkU4Vd1MnrWtM3ZtnGidCuIJ9PxlhpumRSUmSreMMIzIhzvgMcLpjMXB/eX73lzcloG/CELU/Hjl3DCLlpD17Ya1HfSwmZGfh4RqfcGKyAcRFmn2cs9MLZoXXP+S/KYdXX06bw6GDk/ElM7d8nnZQFF6jrYJQXqXSQWWurEn2HDAO32YZvLxbdRllEOJGz0bkSKryCvgsgPrurXsvL1CvjkBb9EgXn5/zZWjWgTgXaXPWqIfsQEcwXIDiDhDCngbEE4g3FX1H3n8twZj6UMVkbKD8JpqxwhNpYu2L2nyPMRGA=; 25:g+q3owEwiXOxf6X4zPnNMHq/OyuBUAhAi3XM3KPISnafCQX1mPxCnWOUuE0vbteR6sjz3wKc6P/pLuNA8EEoqdlmbLG5XsWYcrXiDab9V3BlTDhZOGevC4uPsICUwfRUwbdD8KI7Z9G9FpSZWIMPw1V8p4SFc6UTn1uD4bCAtaiRstqSYzwnpc3gw7fzbZFssqn/ozQRg0Tce1+JyAXQqAJ7dfQ126i/oVhk7rxSZWEF6CxdzZEEpX/2TTFfOFhffth6KRWiOS5Iu8MzCct6SlfkPJiVEsP3MOZ4JaycSV2zCPo4+llmrqEoZoHMqFAJI1DMZzzzeHFZbahYMuqCvQ== X-MS-TrafficTypeDiagnostic: BN7PR07MB4708: X-Microsoft-Exchange-Diagnostics: 1; BN7PR07MB4708; 31:+DfBIrBlv7TeviOpolIQUVaQ7BuIcK88Xx/ibDh/rw/TI2DdEeAL6zPPKle+CAY+bz61qPUOscb0mIox7s+0ojUFuEkeeqiyZvhfYoIXCSpi18RW8H8tJmAZmec+YTZpAXX8O/DqX3UC6svTOUqIyRVNwNM1QZYYEv/xe75ZuYYnCRK+Er4wvzSN77++NuTsSNHJ8JXsSb0U8iAr/2MZHmzS6UhtM+uExa8rIZ6KN38=; 20:JrunIclr8x8IfN4drOerStGqTKNL7omn/UKi8uoX0VttQg7KtMg8hBOPySGUVpoySoyJfAffJvspV6BO/vRjSjRLl+rO1GkAU+MBgzny7NCWdLzQrtgPu5i2iWwlpwr498nuHoEGSpmviEOGMopRR8XUgVoDs8qWlgsg0zG1l0e+dkcUgKiDl9RabxgdeiFOe0f3UHZKfI4VU7nKcjSouQUIcXbvJdqlgbO9szl5mQ0fg+YMFVLwzqxqgw359OVGSMyLhQSWuYc4r33dDJ/9lqJeRts1VDXSl2f+xXlWymxvwzgq3mzAaXGhdbRHsplUMKBoy6rCtqdrhaT0UHbfCfs06ZWibgJw0OW5npL/26Ajde4Ju0OqZRJ3sqeEgiVOP15Tdn+bKLGPbiPidoATY6m61gsmu+CdLCaj0G7jcXEWa5Q9mxN1lIsPPahX1SdD0fTxjN7GtrwGNqpJLRlwOIiUvh3NwF7EqQoF8iWHxOR6Pr6OXVtquzgeOSctnDy4 X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(72806322054110); X-MS-Exchange-SenderADCheck: 1 X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(8211001083)(6040522)(2401047)(5005006)(8121501046)(93006095)(93003095)(3231311)(944501410)(52105095)(3002001)(10201501046)(149027)(150027)(6041310)(20161123560045)(20161123564045)(20161123562045)(20161123558120)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(6072148)(201708071742011)(7699016); SRVR:BN7PR07MB4708; BCL:0; PCL:0; RULEID:; SRVR:BN7PR07MB4708; X-Microsoft-Exchange-Diagnostics: 1; BN7PR07MB4708; 4:CpN2+slEp9yZ/uOJN6jyzkH2vc+YoVd4xVMmRhSRM/KKb8+loGlvHl4Zi3FPI+snb8vByH02iMPcuAdnDQksWwqhmxvIhsIB9loPBNO3Jy4NQzt3KUw7/fgIsS+5fbCvug1FbE8wvCtCKb5h8cnjt/AyVa2PfKmdWQqGhCeY+Ho32ZpcPlDFDi8AraJyaAMkGe6PLVXTY3yVEQKk+mbBdjwmz+QVGzVMyWg5JW7dgZj5VGzPANbqs02rz11bXe1Jk6E1U/Dh6vlq1wqvtZPqIzBO75jM+UkEdfmpROQL6Kd7Kc/27tG9VEz9B6XCjLJG X-Forefront-PRVS: 0731AA2DE6 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; BN7PR07MB4708; 23:3h5nvAPPOgcA12aDW3V6u8hZ+fRo96TPKDey7rCOn?= =?us-ascii?Q?DpxXFCKcYMz9+yjTIm6JMMJci4UPyDtzZCzx+DKpa2PnebvweZB86nxR/+NC?= =?us-ascii?Q?BFm0SfQ5AwacjidjsTFkECJeHfR0i7dsKseWrwH7vb/3wdpIv1oC/9k0Fdle?= =?us-ascii?Q?0HecW/rXeBfxtszH8Rl1zCOeXFIFpC5QcfVKcV7XWNs6emkrvKTZNmSm1k0r?= =?us-ascii?Q?2TEgFHRCSQlzsdCF5KDXieXykLOwpMQdz09gC7wt6wZYfaT0vEZp7+5EjoT3?= =?us-ascii?Q?PjE1l4pcDV0CYSMBXuABBybiL9Ig+aZOthCZLEMt2lngJ4JuqeaEtUALOKgZ?= =?us-ascii?Q?Ru7UHJ43rpQuzkPbiZf8L72R4g7fIxZS8L5PqxFPYZHu/jGGRGk1RfNVoDF2?= =?us-ascii?Q?/1CmM0aWja1AKDktHyFofbZxGvmKHW0zqB9XpN9hKC4KbJBYA5P/3lmt+XW7?= =?us-ascii?Q?Q0C1tMFdHNEUa8FDjjxH9vvXYtILTBApROa/X3UXUmWCU0Xi76CQ8go+Wprj?= =?us-ascii?Q?/WmHObgKqxbFKrtdM+Q+/AsjypLh+9VM0WUxxNmBgK0EanDdCLldKc8By1UZ?= =?us-ascii?Q?tGoh41TRkmrsBoZiMSs8nP+CFcqLtscInlD4KZWUYtNpEb00UKt6taKGfbj/?= =?us-ascii?Q?yJZVdQy/T9gAW9PTUh4SFGMDqECCr08Wb1BTtR6t/vIa7iUBUebk8d9tfJhP?= =?us-ascii?Q?bAqPJi7N3slC0Izav7FlqGMdk6b1odgKdpMqsgK6WIK8g0qMt8uWydLbW/U+?= =?us-ascii?Q?ZWOda4A8te0e0WiEMIW+uchZFhwRj2jPasxQBvsPvZwP73XebV6ck/r0/Ay3?= =?us-ascii?Q?tGCbc9BDM6lHnmtXlWAE8V6gIGLV/aYp0UBeGTqctD7r36zIsRM8Sfak4W46?= =?us-ascii?Q?JjMutMM1k8fRr2fQavV8VWx296OWvGaDnsvxwF7v9j0Uv3LJ0SdreO0+B99h?= =?us-ascii?Q?RpDklYh9woYsvC8FoUh33YrqtNEn+lpiqfRYr788sD+7UnJi9N2h+6+wSqYD?= =?us-ascii?Q?cjPpk2NSCuIVZtKw//2lGY9zax9mYgXvyxjz8D+w1QttzsXFPK6dOBx71AGn?= =?us-ascii?Q?+x2JG28g8KBgERP1WBIrmycpP20hc9Xri5HT9baGYa0ENoROzgi9W/fP9lgx?= =?us-ascii?Q?6665+m57dBkH3aQ3mDB2fL1O7G3PizpIpdr+EfEHR57qiBOTdEZug=3D=3D?= X-Microsoft-Antispam-Message-Info: KjeTllC2Fou9MxokCQ0RTstmw6zX3M/fIgjIjao5pc7R15UWH9X3Jy7BHCObKcIXUHGW+kT0dLemwqzhYeFht7gtAMwkGvDKu6hCfU1RcfWIGDNLJebtPSeu8UKc9rkOoB6e8b3AqKtVw7jgmVjpkbHX8c6tuC03eWdC+Wx+3ZCRoWCxuEH2inSulALZDQLjPliPJmDqAh6zYztZgr/Zt03e0dUacPB/JLCdENpH5Vxq/yvRefFRWHjfKPepkTWX9SPwqY6ZbNeHnpFHnkG85kuclFi6CD4UrKkGlvEfZfAJykAN9UWjptIYrBIIOHbOIpXEzqPp3F+aYsOPr2k2muevLp2Et02ahzGfarBWIZKJWYxjVeH2LiPOV5mTi0pB0ZU6F4fqt+WwCW8a4a62kg== X-Microsoft-Exchange-Diagnostics: 1; BN7PR07MB4708; 6:LwXvc33OwjBb4HF+AzNzbXLgX+NY+gIQeTmybFxpnZZnW+eG5RvOZpVUwwcH/3vUoyPY926TRcGNlqa2ipJqzwsgpB6gdpKXxzKl4evllZKu3Fae6PbEkWPO3lLTJRh7f/48wn+beZsg9cy/ukYv1mUWB++x0Tn7DvI1dNDhjIPAWAvXVENaS6x+YFYw3IrTNeRzGGs3gb1ObnrmJbiO/58i+X3txo+pEatyvs7VSZJxP4RR5Q412Msais0tdE3q6NrCk7aLB6zdlE28qKsr6hQCLtiYdmJxB+Pv78VvZL6DlAqbK3WkpXcZE66q8vpl3+laKg5OqOtm3+mLTB61zTT87/CC6gcscpyphEgrd+7rZEDB8YSovDlSQLfDcCWNAhklAT8SQv1Wxa4aBzkDy4fGe1Nc+dRoQmagjWSZJO3zK5iVBN6rzpaFt3BXrVTBfHgGDUff+pqIYQMA7cXdxw==; 5:B/OKqV+Ik03UJmOlYMSk795fkH2RrGRYkn/25yV+Se9Hvo1YOoGNa8P9bnL0nRJgPQi6hYAq0ymNJNEVD0eQ0nuUd7qVBjbWCqhGKeA3yV7OyZCIr1rmJcykYQmZ29K17yjzAgz7YV9+GM6pfZGRHzvyfJR+Ajf2wqjA9R9EwEE=; 24:vM73BnueVrTg/y/k6MGi9NEDPFvQmqDjZyRtIkZpBGQXcZ3nX5fkAlY66VIacpb7eJHaoNRD7dxfNSo++X/V3mtarySYnKnLytgYwgUoT1g= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1; BN7PR07MB4708; 7:mHwIi647920Rr5lv72nU9kPBkaQ5jDofImuLHbBQSultIlZNEbjGN0sxKS2mBZYgrAPBv3+m9cO6zPod+FRjbn3z+SzO5geCIEO/Bm5YVN/KLBQv3YtSxqaZwAg3yPFeJxg3d+gCd+EjgJ5nsr4z6Na6b2cZavT/i0qQcOVco/3naiSfOCIxA659TFHRlEAHFwYQx0D7CzGbup8dyj8IeMSwbqJtyJQtDvavrzHheywu6PeeaHZx0gWYqp7Xlors; 20:ymiweqxlWCDkkGzPoTzscFrkHPesWNkKEZnV5aqHk2CbrobvkQpKDe60Ya/BnGYgEWyBQMt9WHkr32D9x/39uNLcviAy2WMjApDQvEFULLgyOB4pNp+CIWG590+xwB6euNr2SE9CLz77NFWbD83JMwXl9cWmTzNjxIJMe6ON/7CMp02deMWynHev2SMN555qlkhg0/HVAltcFjlhA11gfFdjd5M24w1y2ncRN55qGxqow7TlSJyJG6Z/KBJS8keb X-OriginatorOrg: cadence.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Jul 2018 05:48:01.6697 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 679a5818-ff38-47d5-fab7-08d5e7bb07d8 X-MS-Exchange-CrossTenant-Id: d36035c5-6ce6-4662-a3dc-e762e61ae4c9 X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=d36035c5-6ce6-4662-a3dc-e762e61ae4c9; Ip=[158.140.1.28]; Helo=[sjmaillnx1.cadence.com] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BN7PR07MB4708 To: unlisted-recipients:; (no To-header on input) Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Patch adds functionality that allows to remove the request from the endpoint ring. This may cause the DC to stop USB transfers, potentially stopping in the middle of a TRB buffer. The DC should pick up where it left off in the TD, unless a Set Transfer Ring Dequeue Pointer is issued. Signed-off-by: Pawel Laszczak --- drivers/usb/usbssp/gadget-ring.c | 325 +++++++++++++++++++++++++++++++ drivers/usb/usbssp/gadget.c | 49 ++++- drivers/usb/usbssp/gadget.h | 10 + 3 files changed, 382 insertions(+), 2 deletions(-) diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c index 1ee6b056c87d..08597127d495 100644 --- a/drivers/usb/usbssp/gadget-ring.c +++ b/drivers/usb/usbssp/gadget-ring.c @@ -102,10 +102,50 @@ static bool link_trb_toggles_cycle(union usbssp_trb *trb) return le32_to_cpu(trb->link.control) & LINK_TOGGLE; } +static bool last_td_in_request(struct usbssp_td *td) +{ + struct usbssp_request *req_priv = td->priv_request; + + return req_priv->num_tds_done == req_priv->num_tds; +} + static void inc_td_cnt(struct usbssp_request *priv_req) { priv_req->num_tds_done++; } + +static void trb_to_noop(union usbssp_trb *trb, u32 noop_type) +{ + if (trb_is_link(trb)) { + /* unchain chained link TRBs */ + trb->link.control &= cpu_to_le32(~TRB_CHAIN); + } else { + trb->generic.field[0] = 0; + trb->generic.field[1] = 0; + trb->generic.field[2] = 0; + /* Preserve only the cycle bit of this TRB */ + trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE); + trb->generic.field[3] |= cpu_to_le32(TRB_TYPE(noop_type)); + } +} + +/* Updates trb to point to the next TRB in the ring, and updates seg if the next + * TRB is in a new segment. This does not skip over link TRBs, and it does not + * effect the ring dequeue or enqueue pointers. + */ +static void next_trb(struct usbssp_udc *usbssp_data, + struct usbssp_ring *ring, + struct usbssp_segment **seg, + union usbssp_trb **trb) +{ + if (trb_is_link(*trb)) { + *seg = (*seg)->next; + *trb = ((*seg)->trbs); + } else { + (*trb)++; + } +} + /* * See Cycle bit rules. SW is the consumer for the event ring only. * Don't make a ring full of link TRBs. That would be dumb and this would loop. @@ -344,6 +384,156 @@ struct usbssp_ring *usbssp_triad_to_transfer_ring( return NULL; } +/* + * Get the hw dequeue pointer DC stopped on, either directly from the + * endpoint context, or if streams are in use from the stream context. + * The returned hw_dequeue contains the lowest four bits with cycle state + * and possbile stream context type. + */ +u64 usbssp_get_hw_deq(struct usbssp_udc *usbssp_data, + struct usbssp_device *dev, + unsigned int ep_index, + unsigned int stream_id) +{ + struct usbssp_ep_ctx *ep_ctx; + struct usbssp_stream_ctx *st_ctx; + struct usbssp_ep *ep; + + ep = &dev->eps[ep_index]; + + if (ep->ep_state & EP_HAS_STREAMS) { + st_ctx = &ep->stream_info->stream_ctx_array[stream_id]; + return le64_to_cpu(st_ctx->stream_ring); + } + ep_ctx = usbssp_get_ep_ctx(usbssp_data, dev->out_ctx, ep_index); + return le64_to_cpu(ep_ctx->deq); +} + +/* + * Move the DC endpoint ring dequeue pointer past cur_td. + * Record the new state of the DC endpoint ring dequeue segment, + * dequeue pointer, and new consumer cycle state in state. + * Update our internal representation of the ring's dequeue pointer. + * + * We do this in three jumps: + * - First we update our new ring state to be the same as when the DC stopped. + * - Then we traverse the ring to find the segment that contains + * the last TRB in the TD. We toggle the DC new cycle state when we pass + * any link TRBs with the toggle cycle bit set. + * - Finally we move the dequeue state one TRB further, toggling the cycle bit + * if we've moved it past a link TRB with the toggle cycle bit set. + */ +void usbssp_find_new_dequeue_state(struct usbssp_udc *usbssp_data, + unsigned int ep_index, + unsigned int stream_id, + struct usbssp_td *cur_td, + struct usbssp_dequeue_state *state) +{ + struct usbssp_device *dev_priv = &usbssp_data->devs; + struct usbssp_ep *ep_priv = &dev_priv->eps[ep_index]; + struct usbssp_ring *ep_ring; + struct usbssp_segment *new_seg; + union usbssp_trb *new_deq; + dma_addr_t addr; + u64 hw_dequeue; + bool cycle_found = false; + bool td_last_trb_found = false; + + ep_ring = usbssp_triad_to_transfer_ring(usbssp_data, + ep_index, stream_id); + if (!ep_ring) { + usbssp_warn(usbssp_data, "WARN can't find new dequeue state " + "for invalid stream ID %u.\n", + stream_id); + return; + } + + /* Dig out the cycle state saved by the DC during the stop ep cmd */ + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request, + "Finding endpoint context"); + + hw_dequeue = usbssp_get_hw_deq(usbssp_data, dev_priv, + ep_index, stream_id); + new_seg = ep_ring->deq_seg; + new_deq = ep_ring->dequeue; + state->new_cycle_state = hw_dequeue & 0x1; + state->stream_id = stream_id; + + /* + * We want to find the pointer, segment and cycle state of the new trb + * (the one after current TD's last_trb). We know the cycle state at + * hw_dequeue, so walk the ring until both hw_dequeue and last_trb are + * found. + */ + do { + if (!cycle_found && usbssp_trb_virt_to_dma(new_seg, new_deq) + == (dma_addr_t)(hw_dequeue & ~0xf)) { + cycle_found = true; + if (td_last_trb_found) + break; + } + + if (new_deq == cur_td->last_trb) + td_last_trb_found = true; + + if (cycle_found && trb_is_link(new_deq) && + link_trb_toggles_cycle(new_deq)) + state->new_cycle_state ^= 0x1; + + next_trb(usbssp_data, ep_ring, &new_seg, &new_deq); + + /* Search wrapped around, bail out */ + if (new_deq == ep_priv->ring->dequeue) { + usbssp_err(usbssp_data, + "Error: Failed finding new dequeue state\n"); + state->new_deq_seg = NULL; + state->new_deq_ptr = NULL; + return; + } + + } while (!cycle_found || !td_last_trb_found); + + state->new_deq_seg = new_seg; + state->new_deq_ptr = new_deq; + + /* Don't update the ring cycle state for the producer (us). */ + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request, + "Cycle state = 0x%x", state->new_cycle_state); + + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request, + "New dequeue segment = %p (virtual)", + state->new_deq_seg); + addr = usbssp_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr); + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request, + "New dequeue pointer = 0x%llx (DMA)", + (unsigned long long) addr); +} + +/* flip_cycle means flip the cycle bit of all but the first and last TRB. + * (The last TRB actually points to the ring enqueue pointer, which is not part + * of this TD.) This is used to remove partially enqueued isoc TDs from a ring. + */ +static void td_to_noop(struct usbssp_udc *usbssp_data, + struct usbssp_ring *ep_ring, + struct usbssp_td *td, bool flip_cycle) +{ + struct usbssp_segment *seg = td->start_seg; + union usbssp_trb *trb = td->first_trb; + + while (1) { + trb_to_noop(trb, TRB_TR_NOOP); + + /* flip cycle if asked to */ + if (flip_cycle && trb != td->first_trb && trb != td->last_trb) + trb->generic.field[3] ^= cpu_to_le32(TRB_CYCLE); + + if (trb == td->last_trb) + break; + + next_trb(usbssp_data, ep_ring, &seg, &trb); + } +} + /* Must be called with usbssp_data->lock held in interrupt context * or usbssp_data->irq_thread_lock from thread conext (defered interrupt) */ @@ -362,6 +552,141 @@ void usbssp_giveback_request_in_irq(struct usbssp_udc *usbssp_data, usbssp_gadget_giveback(req_priv->dep, req_priv, status); } +void usbssp_unmap_td_bounce_buffer(struct usbssp_udc *usbssp_data, + struct usbssp_ring *ring, + struct usbssp_td *td) +{ + /*TODO: ??? */ +} + +void usbssp_remove_request(struct usbssp_udc *usbssp_data, + struct usbssp_request *req_priv, int ep_index) +{ + int i = 0; + struct usbssp_ring *ep_ring; + struct usbssp_ep *ep; + struct usbssp_td *cur_td = NULL; + struct usbssp_ep_ctx *ep_ctx; + struct usbssp_device *priv_dev; + u64 hw_deq; + struct usbssp_dequeue_state deq_state; + + memset(&deq_state, 0, sizeof(deq_state)); + ep = &usbssp_data->devs.eps[ep_index]; + + priv_dev = &usbssp_data->devs; + ep_ctx = usbssp_get_ep_ctx(usbssp_data, priv_dev->out_ctx, ep_index); + trace_usbssp_remove_request(ep_ctx); + /* + * We have the DC lock and disabled interrupt, so nothing can modify + * this list until we drop it. + */ + + i = req_priv->num_tds_done; + + for (; i < req_priv->num_tds; i++) { + cur_td = &req_priv->td[i]; + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request, + "Removing canceled TD starting at 0x%llx (dma).", + (unsigned long long)usbssp_trb_virt_to_dma( + cur_td->start_seg, cur_td->first_trb)); + + ep_ring = usbssp_request_to_transfer_ring(usbssp_data, + cur_td->priv_request); + + if (!ep_ring) { + /* This shouldn't happen unless a driver is mucking + * with the stream ID after submission. This will + * leave the TD on the hardware ring, and the hardware + * will try to execute it, and may access a buffer + * that has already been freed. In the best case, the + * hardware will execute it, and the event handler will + * ignore the completion event for that TD, since it was + * removed from the td_list for that endpoint. In + * short, don't muck with the stream ID after + * submission. + */ + usbssp_warn(usbssp_data, "WARN Cancelled USB Request %p" + " has invalid stream ID %u.\n", + cur_td->priv_request, + cur_td->priv_request->request.stream_id); + goto remove_finished_td; + } + + if (!(ep->ep_state & USBSSP_EP_ENABLED) || + ep->ep_state & USBSSP_EP_DISABLE_PENDING) { + goto remove_finished_td; + } + + /* + * If we stopped on the TD we need to cancel, then we have to + * move the DC endpoint ring dequeue pointer past this TD. + */ + hw_deq = usbssp_get_hw_deq(usbssp_data, priv_dev, ep_index, + cur_td->priv_request->request.stream_id); + hw_deq &= ~0xf; + + if (usbssp_trb_in_td(usbssp_data, cur_td->start_seg, + cur_td->first_trb, cur_td->last_trb, hw_deq, false)) { + usbssp_find_new_dequeue_state(usbssp_data, ep_index, + cur_td->priv_request->request.stream_id, + cur_td, &deq_state); + } else { + td_to_noop(usbssp_data, ep_ring, cur_td, false); + } + +remove_finished_td: + /* + * The event handler won't see a completion for this TD anymore, + * so remove it from the endpoint ring's TD list. + */ + list_del_init(&cur_td->td_list); + } + + ep->ep_state &= ~EP_STOP_CMD_PENDING; + + if (!(ep->ep_state & USBSSP_EP_DISABLE_PENDING) && + ep->ep_state & USBSSP_EP_ENABLED) { + /* If necessary, queue a Set Transfer Ring Dequeue Pointer + * command + */ + if (deq_state.new_deq_ptr && deq_state.new_deq_seg) { + usbssp_queue_new_dequeue_state(usbssp_data, ep_index, + &deq_state); + usbssp_ring_cmd_db(usbssp_data); + } else { + /* Otherwise ring the doorbell(s) to restart queued + * transfers + */ + ring_doorbell_for_active_rings(usbssp_data, ep_index); + } + } + + /* + * Complete the cancellation of USB request. + */ + i = req_priv->num_tds_done; + for (; i < req_priv->num_tds; i++) { + cur_td = &req_priv->td[i]; + + /* Clean up the cancelled USB Request */ + /* Doesn't matter what we pass for status, since the core will + * just overwrite it. + */ + ep_ring = usbssp_request_to_transfer_ring(usbssp_data, + cur_td->priv_request); + + usbssp_unmap_td_bounce_buffer(usbssp_data, ep_ring, cur_td); + + inc_td_cnt(cur_td->priv_request); + if (last_td_in_request(cur_td)) { + usbssp_giveback_request_in_irq(usbssp_data, + cur_td, -ECONNRESET); + } + } +} + + /* * When we get a command completion for a Stop Endpoint Command, we need to * stop timer and clear EP_STOP_CMD_PENDING flag. diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c index 2b16158c8bd8..b4e26aab1fb8 100644 --- a/drivers/usb/usbssp/gadget.c +++ b/drivers/usb/usbssp/gadget.c @@ -553,8 +553,53 @@ int usbssp_enqueue(struct usbssp_ep *dep, struct usbssp_request *req_priv) */ int usbssp_dequeue(struct usbssp_ep *ep_priv, struct usbssp_request *req_priv) { - /*TODO: this function must be implemented*/ - return 0; + int ret = 0, i; + struct usbssp_udc *usbssp_data; + unsigned int ep_index; + struct usbssp_ring *ep_ring; + struct usbssp_device *priv_dev; + struct usbssp_ep_ctx *ep_ctx; + + usbssp_data = ep_priv->usbssp_data; + trace_usbssp_request_dequeue(&req_priv->request); + + priv_dev = &usbssp_data->devs; + ep_index = usbssp_get_endpoint_index(req_priv->dep->endpoint.desc); + ep_priv = &usbssp_data->devs.eps[ep_index]; + ep_ring = usbssp_request_to_transfer_ring(usbssp_data, req_priv); + + if (!ep_ring) + goto err_giveback; + + i = req_priv->num_tds_done; + + if (i < req_priv->num_tds) + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request, + "Cancel request %p, dev %s, ep 0x%x, " + "starting at offset 0x%llx", + &req_priv->request, usbssp_data->gadget.name, + req_priv->dep->endpoint.desc->bEndpointAddress, + (unsigned long long) usbssp_trb_virt_to_dma( + req_priv->td[i].start_seg, + req_priv->td[i].first_trb)); + + /* Queue a stop endpoint command, but only if it is + * in EP_STATE_RUNNING state. + */ + ep_ctx = usbssp_get_ep_ctx(usbssp_data, priv_dev->out_ctx, ep_index); + if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) { + ret = usbssp_cmd_stop_ep(usbssp_data, &usbssp_data->gadget, + ep_priv); + if (ret) + return ret; + } + + usbssp_remove_request(usbssp_data, req_priv, ep_index); + return ret; + +err_giveback: + usbssp_giveback_request_in_irq(usbssp_data, req_priv->td, -ESHUTDOWN); + return ret; } int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data, struct usbssp_ep *dep, diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h index 0870635ef728..f1cf120a2f54 100644 --- a/drivers/usb/usbssp/gadget.h +++ b/drivers/usb/usbssp/gadget.h @@ -1766,6 +1766,13 @@ int usbssp_queue_halt_endpoint(struct usbssp_udc *usbssp_data, unsigned int ep_index); int usbssp_queue_reset_device(struct usbssp_udc *usbssp_data, struct usbssp_command *cmd); +void usbssp_find_new_dequeue_state(struct usbssp_udc *usbssp_data, + unsigned int ep_index, + unsigned int stream_id, struct usbssp_td *cur_td, + struct usbssp_dequeue_state *state); +void usbssp_queue_new_dequeue_state(struct usbssp_udc *usbssp_data, + unsigned int ep_index, + struct usbssp_dequeue_state *deq_state); void usbssp_handle_command_timeout(struct work_struct *work); void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data); @@ -2288,4 +2295,7 @@ __le32 __iomem *usbssp_get_port_io_addr(struct usbssp_udc *usbssp_data); void usbssp_giveback_request_in_irq(struct usbssp_udc *usbssp_data, struct usbssp_td *cur_td, int status); +void usbssp_remove_request(struct usbssp_udc *usbssp_data, + struct usbssp_request *req_priv, int ep_index); + #endif /* __LINUX_USBSSP_GADGET_H */