From patchwork Mon Jan 25 13:43:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Peng Fan X-Patchwork-Id: 370227 Delivered-To: patch@linaro.org Received: by 2002:a02:a60d:0:0:0:0:0 with SMTP id c13csp3993074jam; Mon, 25 Jan 2021 05:27:58 -0800 (PST) X-Google-Smtp-Source: ABdhPJzJxkUAuy9uuduk9rcC1s7yi5HHgbjrzalEVZJarZGKtwjqDzNvtoPvdGqc0KKUB1r6HNyC X-Received: by 2002:a17:906:29d4:: with SMTP id y20mr372162eje.294.1611581277988; Mon, 25 Jan 2021 05:27:57 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1611581277; cv=pass; d=google.com; s=arc-20160816; b=XU10fellmrW8x36EbHO7txLvNH2ySh9s7PWQ0MUpI1iWtpgMlj/K7oTUli5MwnWEwg LXnjUhgCR3zMMWy6SXoOM7j18mTKwnuwBPjicSos+kpA7Uhal+XTIqAuLir9pmAEy9JA 0TbClmih/yA3+LIxKHN8zVhnIL7n3Q1XJfOKJDNrGEREcAfC0rHSjlakKT9YmVSX5Jeo NdPitXBU72KNFxK3td7ix+k5ZGdBJw9xFA1xKmoJCafj7tkhrUJiBXgo8ybIobQc+Od5 C3NMjBciqKUr9/j45r4Qm7z9G7ptg8lBmPYDm8/DzMd0M0UzYgyzOIR/gDTvhgkr8k5r FYQg== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:mime-version :content-transfer-encoding:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature; bh=I6G0h/1XIlqA3H4fInNm7RCe69ZZbHRmhuaDE7lXD3g=; b=RHY2zqZJpHbncgefaCnVJi+9rYaKem5N3HyrdIQl/Fn5aOYoiLo4lEO1hIUOK0Z9Rp /YsvnUiP+u611qxURJpXtCddT4bw9Jr4CDLXa0zPFufsqZlJ0wCHfjLsOhYFvfu1D5YJ fT7rIjmGQkkcvRlOviXjvFyYJmG2A8JLRdkIp9QMiH8s1H6kJ1gvITkiSaSunEb2j3hq Xom93Y0hwJavf7cXvCEshnvzDJtkZrgza7RH5qPp1pODyj53ZIbWCKO2T9P2dZfE0HjK Xo58MdYLFghzQYo5mOCDBhH1w2rH4KAvk3omSMDw+856crik6LmnXTLUj2uahsRhdiUE mf7Q== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@nxp.com header.s=selector2 header.b=o8ItBRKD; arc=pass (i=1 spf=pass spfdomain=nxp.com dkim=pass dkdomain=nxp.com dmarc=pass fromdomain=nxp.com); spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 85.214.62.61 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=nxp.com Return-Path: Received: from phobos.denx.de (phobos.denx.de. [85.214.62.61]) by mx.google.com with ESMTPS id d3si7387570edy.581.2021.01.25.05.27.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 25 Jan 2021 05:27:57 -0800 (PST) Received-SPF: pass (google.com: domain of u-boot-bounces@lists.denx.de designates 85.214.62.61 as permitted sender) client-ip=85.214.62.61; Authentication-Results: mx.google.com; dkim=pass header.i=@nxp.com header.s=selector2 header.b=o8ItBRKD; arc=pass (i=1 spf=pass spfdomain=nxp.com dkim=pass dkdomain=nxp.com dmarc=pass fromdomain=nxp.com); spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 85.214.62.61 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=nxp.com Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 55A148274E; Mon, 25 Jan 2021 14:26:43 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=nxp.com header.i=@nxp.com header.b="o8ItBRKD"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 346BD8267A; Mon, 25 Jan 2021 14:14:54 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=0.3 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FORGED_SPF_HELO,MSGID_FROM_MTA_HEADER, SPF_HELO_PASS autolearn=no autolearn_force=no version=3.4.2 Received: from EUR01-VE1-obe.outbound.protection.outlook.com (mail-ve1eur01on061d.outbound.protection.outlook.com [IPv6:2a01:111:f400:fe1f::61d]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id DD78482679 for ; Mon, 25 Jan 2021 14:14:49 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=peng.fan@nxp.com ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=WJBMuDfKWoj2QLd7DEGHxGsVqCdT1bAgBAx4ngdkmKWgEFCzxdIV9xM7x60DL7l0PAVvJpHY0WVzZw/WUVVYjn825KbxrR/mBKfBhdR2d/ON82gSoBUdbYl7IrLZ1h4pzE53RzuLwUd0cvWVdx0gL7m5+ShKgSTf1rECPo6akW2N3U4EShDXu65BFfJAZ+rfbUF2WTsnkGOSRS0BvLE4DfQlOiWo5voPJhyMNJfEd7IKKPM5mMmvEspwZcx+HHJGvUvFdJKT3PpeHLTkfNYkmDJauqhN4xLSdQpBZswyWPkcTQ3APogHvmjkMrWdY2gkJZmqppS7K3ZNFsYQjkKzjQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=I6G0h/1XIlqA3H4fInNm7RCe69ZZbHRmhuaDE7lXD3g=; b=Vbz/LbCClNQiYW8ptM6NkfN2UvudGsvAF13aL/6BA4aX3lyzmWG/2jZPGopIVmm1E7LP2CzyLFrSVd9kyeJEbFdI1BRCgNVwh3PeWvBHsPiHyNY9X4QiJCXabWRiQ7ipZRLPXL/FNFFJ4847kOMfcDxUXDKc3msxX+/X+w8S9ZBi6lBjs1wQWSQ4LCDRHdTrQZXQO+uongnh9vZz843w98qm8vBURiz2PhOQ7J8LOONLMuwb0Dqhj9Hj7fDpeuA9Pzuyft/8FFHev7M8I3s8tqUE/6QOVs5/9sLEB5XgVDim+qEkz+z4zoNMvdmlxLoodiJl0VQ9GOMjWQrKkLQAzw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=I6G0h/1XIlqA3H4fInNm7RCe69ZZbHRmhuaDE7lXD3g=; b=o8ItBRKDaOKDtNJ2Qcqg1XggQ3EuzUQO7VQAxJrs9KLumlPBe99xTjMwYKlj81oOStiZT8NytB3gQTNwOII8jwvgO4ZFqHe+FTBa7+X5gezQIX4C7135cvVWqtPD1VwSj2P3oLImIbEo9NnY7dOyIlBq9YyDyWnbwx7yDz1a+Zc= Authentication-Results: denx.de; dkim=none (message not signed) header.d=none;denx.de; dmarc=none action=none header.from=nxp.com; Received: from DB6PR0402MB2760.eurprd04.prod.outlook.com (2603:10a6:4:a1::14) by DB6PR04MB3128.eurprd04.prod.outlook.com (2603:10a6:6:10::31) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3784.13; Mon, 25 Jan 2021 13:14:48 +0000 Received: from DB6PR0402MB2760.eurprd04.prod.outlook.com ([fe80::d58c:d479:d094:43d0]) by DB6PR0402MB2760.eurprd04.prod.outlook.com ([fe80::d58c:d479:d094:43d0%9]) with mapi id 15.20.3784.019; Mon, 25 Jan 2021 13:14:48 +0000 From: peng.fan@nxp.com To: sbabic@denx.de, lukma@denx.de, marex@denx.de Cc: uboot-imx@nxp.com, u-boot@lists.denx.de, Li Jun , Peng Fan Subject: [PATCH V2 06/17] usb: gadget: OS Feature Descriptors support Date: Mon, 25 Jan 2021 21:43:49 +0800 Message-Id: <20210125134400.780-7-peng.fan@nxp.com> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20210125134400.780-1-peng.fan@nxp.com> References: <20210125134400.780-1-peng.fan@nxp.com> X-Originating-IP: [119.31.174.71] X-ClientProxiedBy: SG2PR03CA0117.apcprd03.prod.outlook.com (2603:1096:4:91::21) To DB6PR0402MB2760.eurprd04.prod.outlook.com (2603:10a6:4:a1::14) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from linux-1xn6.ap.freescale.net (119.31.174.71) by SG2PR03CA0117.apcprd03.prod.outlook.com (2603:1096:4:91::21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3805.6 via Frontend Transport; Mon, 25 Jan 2021 13:14:45 +0000 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 2d53bb0f-d7b8-4ce1-6e1d-08d8c13330a4 X-MS-TrafficTypeDiagnostic: DB6PR04MB3128: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:962; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: HcDKWc+V5Taby/dsQTpnmroZtVqv1LFEhgf0qAd2v8oxa3WxZq9PXoT5+FojlCVKaw/szayzVKmcwSUyf4oTDQGDss1hrYyHWr7r3wH8IH7CTqMkguufkqi3Lbdc3iY7eOmrn660emPlPtonhgsQejb7jnIF2J23S4LF3IBDDnBle4ghZAUNaJI0xKpGXETx3ZiUcGQxK6Ln8OQCEIK99xWHWg2skiybsVR3XX9zcRwHegHd3ar0KHGHibMSRTDQeFDmNh1W1s2U1CRLp2axFYX9SreeOPzift+ruAm2/XwmWxU52av02NUYtYjZpaF9do2iY1iwnM9GpyelIfCIJ/kv0bAyAdQUpjfgt4gKZbEk4C8u5GoRrIPaBNohuZ0P6W5nZ0cO0Ll+cPOgTYVudr4TbajNfYZScJsqgLZugm+oeQHsmaPQvTaCRAuERyxaTFfQRlSJgp5rbtDfSz/49kJwW3ZIT/M48EqDIXtV3DfO5TrVN1FqmzI3X1LVrcfNNX6ffBV/7+6buxTptMut2Q== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:DB6PR0402MB2760.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(4636009)(346002)(136003)(39860400002)(376002)(366004)(396003)(6506007)(1076003)(30864003)(26005)(316002)(6512007)(45080400002)(186003)(4326008)(54906003)(83380400001)(478600001)(966005)(8936002)(8676002)(86362001)(6486002)(2906002)(36756003)(5660300002)(9686003)(66556008)(2616005)(66476007)(16526019)(956004)(66946007)(52116002)(2004002); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData: =?utf-8?q?KqMXo8lty9+6SmxT6+uRRSs53e4?= =?utf-8?q?e++PQddvMSFYfRoTlAUCRbiMjGmnPEmNtiG8Jboc25mKqd4e+hZ4Q6/W?= =?utf-8?q?rnarD+XlVx23jLAizjEpUQJXYaneJqmWcVFi6jM2NOgBoyyAJgnllG9L?= =?utf-8?q?tE4n58wn94J8FGGjdQTQ9HSntz2jEIvcGX8dW8ePdC80r8mOKiW5i76X?= =?utf-8?q?/BjVcUAW7qdeciwHL5UXbKslodAqlwB7VHCVuWyhbvX/MPXGt8pAcY3r?= =?utf-8?q?+o34muHWTnXySyIz+1bgTqDG4DKwKU10IQHg2bTUYcKx+7WDXZYztf3A?= =?utf-8?q?5phQl0g33DCR11iPY2XGS63TxR8veWI/mORXn12qZ38eitdFI1Ml1fBA?= =?utf-8?q?W3Rrx/vUHQXMJ0tEYVuCDytT8/7NCWioGEfV+BKXAiOhSl8QIGH2ZVae?= =?utf-8?q?TIdDFf76wqjtx4gWQ7jY24yq6AbtsB0TG5VzY1cVrzv+CW/5uOv/hF4L?= =?utf-8?q?f+Ds648X14AxqvvVE55ua74/GFMf8eLfSO+YA4wGF6F1Pg4ZmLspJaF6?= =?utf-8?q?Ru9VKmIMn0HKP3oFMYszxNkDvUU/gEvBKot0gpJ5tvgPOeYrM/Kdq4r7?= =?utf-8?q?kthA9zsr9KgnuMfPzEK9dTOu7HaL9ikVjJfLmvEKbesFTqRKBwxI+Q/J?= =?utf-8?q?S6qc2Z7UN5m29kQ0s/UPnzvw7o4V9yQK1aiBp9TcjjjG4rbZ98rZICs5?= =?utf-8?q?MPtMpItV1W2P8zVWtr/G5IgUI5ZJNr+vNR14s7xkoL4Bo4/aj4qirVw4?= =?utf-8?q?C9jOk1csXgrZxcf7mh+ltM1DeTAf0gPmUpyexCDE/hNpISQcBggTNaYy?= =?utf-8?q?wDwYduRIt1xmHVAnUUZJLtHEzVLJss6ojhZG45NyKoWstfs/5JrYLeIp?= =?utf-8?q?3EAxdkRb3LrJtAFdtMJc8Bri4PLv4M6VXBcHT1TPIPqhJiOdNXsmfVtf?= =?utf-8?q?Bvs7uMSFnmDvImHGGaLqc7zpYdF0U+8C/EqQDFO1Q+aIbt0iXUsu4tJu?= =?utf-8?q?udh1kzYlLXEAlLBzgS75N7ek5sdEAWmL1smy0tXVpYDwi?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 2d53bb0f-d7b8-4ce1-6e1d-08d8c13330a4 X-MS-Exchange-CrossTenant-AuthSource: DB6PR0402MB2760.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 25 Jan 2021 13:14:48.1458 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: KvGgCQijnpsNrcRuZg3KQr0a85zv0bxN+od3/Y0iiphV+xUEdDXiyMWqzlOxCLexDQqG6t5uZb9uTG5O/KXUHA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB6PR04MB3128 X-Mailman-Approved-At: Mon, 25 Jan 2021 14:26:22 +0100 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.3 at phobos.denx.de X-Virus-Status: Clean From: Li Jun This is a proting patch from linux kernel: 37a3a533429e ("usb: gadget: OS Feature Descriptors support"), the original commit log see below: There is a custom (non-USB IF) extension to the USB standard: http://msdn.microsoft.com/library/windows/hardware/gg463182 They grant permission to use the specification - there is "Microsoft OS Descriptor Specification License Agreement" under the link mentioned above, and its Section 2 "Grant of License", letter (b) reads: "Patent license. Microsoft hereby grants to You a nonexclusive, royalty-free, nontransferable, worldwide license under Microsoft鈥檚 patents embodied solely within the Specification and that are owned or licensable by Microsoft to make, use, import, offer to sell, sell and distribute directly or indirectly to Your Licensees Your Implementation. You may sublicense this patent license to Your Licensees under the same terms and conditions." The said extension is maintained by Microsoft for Microsoft. Yet it is fairly common for various devices to use it, and a popular proprietary operating system expects devices to provide "OS descriptors", so Linux-based USB gadgets whishing to be able to talk to a variety of operating systems should be able to provide the "OS descriptors". This patch adds optional support for gadgets whishing to expose the so called "OS Feature Descriptors", that is "Extended Compatibility ID" and "Extended Properties". Hosts which do request "OS descriptors" from gadgets do so during the enumeration phase and before the configuration is set with SET_CONFIGURATION. What is more, those hosts never ask for configurations at indices other than 0. Therefore, gadgets whishing to provide "OS descriptors" must designate one configuration to be used with this kind of hosts - this is what os_desc_config is added for in struct usb_composite_dev. There is an additional advantage to it: if a gadget provides "OS descriptors" and designates one configuration to be used with such non-USB-compliant hosts it can invoke "usb_add_config" in any order because the designated configuration will be reported to be at index 0 anyway. This patch also adds handling vendor-specific requests addressed at device or interface and related to handling "OS descriptors"." Signed-off-by: Li Jun Signed-off-by: Peng Fan --- drivers/usb/gadget/composite.c | 252 ++++++++++++++++++++++++++++++++- drivers/usb/gadget/u_os_desc.h | 123 ++++++++++++++++ include/linux/usb/composite.h | 57 ++++++++ 3 files changed, 431 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/gadget/u_os_desc.h -- 2.28.0 diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 63855af52e..a0c28dbe59 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -12,6 +12,7 @@ #include #include #include +#include "u_os_desc.h" #define USB_BUFSIZ 4096 @@ -244,6 +245,7 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) u8 type = w_value >> 8; int hs = 0; struct usb_configuration *c; + struct list_head *pos; if (gadget_is_dualspeed(gadget)) { if (gadget->speed == USB_SPEED_HIGH) @@ -255,7 +257,20 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) } w_value &= 0xff; - list_for_each_entry(c, &cdev->configs, list) { + + pos = &cdev->configs; + c = cdev->os_desc_config; + if (c) + goto check_config; + + while ((pos = pos->next) != &cdev->configs) { + c = list_entry(pos, typeof(*c), list); + + /* skip OS Descriptors config which is handled separately */ + if (c == cdev->os_desc_config) + continue; + +check_config: if (speed == USB_SPEED_HIGH) { if (!c->highspeed) continue; @@ -779,6 +794,156 @@ static int bos_desc(struct usb_composite_dev *cdev) return le16_to_cpu(bos->wTotalLength); } +static int count_ext_compat(struct usb_configuration *c) +{ + int i, res; + + res = 0; + for (i = 0; i < c->next_interface_id; ++i) { + struct usb_function *f; + int j; + + f = c->interface[i]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (i != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) + ++res; + } + } + BUG_ON(res > 255); + return res; +} + +static void fill_ext_compat(struct usb_configuration *c, u8 *buf) +{ + int i, count; + + count = 16; + for (i = 0; i < c->next_interface_id; ++i) { + struct usb_function *f; + int j; + + f = c->interface[i]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (i != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) { + *buf++ = i; + *buf++ = 0x01; + memcpy(buf, d->ext_compat_id, 16); + buf += 22; + } else { + ++buf; + *buf = 0x01; + buf += 23; + } + count += 24; + if (count >= 4096) + return; + } + } +} + +static int count_ext_prop(struct usb_configuration *c, int interface) +{ + struct usb_function *f; + int j; + + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) + return d->ext_prop_count; + } + return 0; +} + +static int len_ext_prop(struct usb_configuration *c, int interface) +{ + struct usb_function *f; + struct usb_os_desc *d; + int j, res; + + res = 10; /* header length */ + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d) + return min(res + d->ext_prop_len, 4096); + } + return res; +} + +static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf) +{ + struct usb_function *f; + struct usb_os_desc *d; + struct usb_os_desc_ext_prop *ext_prop; + int j, count, n, ret; + u8 *start = buf; + + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d) + list_for_each_entry(ext_prop, &d->ext_prop, entry) { + /* 4kB minus header length */ + n = buf - start; + if (n >= 4086) + return 0; + + count = ext_prop->data_len + + ext_prop->name_len + 14; + if (count > 4086 - n) + return -EINVAL; + usb_ext_prop_put_size(buf, count); + usb_ext_prop_put_type(buf, ext_prop->type); + ret = usb_ext_prop_put_name(buf, ext_prop->name, + ext_prop->name_len); + if (ret < 0) + return ret; + switch (ext_prop->type) { + case USB_EXT_PROP_UNICODE: + case USB_EXT_PROP_UNICODE_ENV: + case USB_EXT_PROP_UNICODE_LINK: + usb_ext_prop_put_unicode(buf, ret, + ext_prop->data, + ext_prop->data_len); + break; + case USB_EXT_PROP_BINARY: + usb_ext_prop_put_binary(buf, ret, + ext_prop->data, + ext_prop->data_len); + break; + case USB_EXT_PROP_LE32: + /* not implemented */ + case USB_EXT_PROP_BE32: + /* not implemented */ + default: + return -EINVAL; + } + buf += count; + } + } + + return 0; +} + /* * The setup() callback implements all the ep0 functionality that's * not handled lower down, in hardware or the hardware driver(like @@ -935,6 +1100,91 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) break; default: unknown: + /* + * OS descriptors handling + */ + if (CONFIG_IS_ENABLED(USB_GADGET_OS_DESCRIPTORS) && cdev->use_os_string && + cdev->os_desc_config && (ctrl->bRequestType & USB_TYPE_VENDOR) && + ctrl->bRequest == cdev->b_vendor_code) { + struct usb_configuration *os_desc_cfg; + u8 *buf; + int interface; + int count = 0; + + buf = req->buf; + os_desc_cfg = cdev->os_desc_config; + memset(buf, 0, w_length); + buf[5] = 0x01; + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + if (w_index != 0x4 || (w_value >> 8)) + break; + buf[6] = w_index; + if (w_length == 0x10) { + /* Number of ext compat interfaces */ + count = count_ext_compat(os_desc_cfg); + buf[8] = count; + count *= 24; /* 24 B/ext compat desc */ + count += 16; /* header */ + put_unaligned_le32(count, buf); + value = w_length; + } else { + /* "extended compatibility ID"s */ + count = count_ext_compat(os_desc_cfg); + buf[8] = count; + count *= 24; /* 24 B/ext compat desc */ + count += 16; /* header */ + put_unaligned_le32(count, buf); + buf += 16; + fill_ext_compat(os_desc_cfg, buf); + value = w_length; + } + break; + case USB_RECIP_INTERFACE: + if (w_index != 0x5 || (w_value >> 8)) + break; + interface = w_value & 0xFF; + buf[6] = w_index; + if (w_length == 0x0A) { + count = count_ext_prop(os_desc_cfg, + interface); + put_unaligned_le16(count, buf + 8); + count = len_ext_prop(os_desc_cfg, + interface); + put_unaligned_le32(count, buf); + + value = w_length; + } else { + count = count_ext_prop(os_desc_cfg, + interface); + put_unaligned_le16(count, buf + 8); + count = len_ext_prop(os_desc_cfg, + interface); + put_unaligned_le32(count, buf); + buf += 10; + value = fill_ext_prop(os_desc_cfg, + interface, buf); + if (value < 0) + return value; + + value = w_length; + } + break; + } + + if (value >= 0) { + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(gadget->ep0, req, GFP_KERNEL); + if (value < 0) { + debug("ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(gadget->ep0, req); + } + } + return value; + } + debug("non-core control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h new file mode 100644 index 0000000000..4dab4814a3 --- /dev/null +++ b/drivers/usb/gadget/u_os_desc.h @@ -0,0 +1,123 @@ +/* + * u_os_desc.h + * + * Utility definitions for "OS Descriptors" support + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * 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. + */ + +#ifndef __U_OS_DESC_H__ +#define __U_OS_DESC_H__ + +#include + +#define USB_EXT_PROP_DW_SIZE 0 +#define USB_EXT_PROP_DW_PROPERTY_DATA_TYPE 4 +#define USB_EXT_PROP_W_PROPERTY_NAME_LENGTH 8 +#define USB_EXT_PROP_B_PROPERTY_NAME 10 +#define USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH 10 +#define USB_EXT_PROP_B_PROPERTY_DATA 14 + +#define USB_EXT_PROP_RESERVED 0 +#define USB_EXT_PROP_UNICODE 1 +#define USB_EXT_PROP_UNICODE_ENV 2 +#define USB_EXT_PROP_BINARY 3 +#define USB_EXT_PROP_LE32 4 +#define USB_EXT_PROP_BE32 5 +#define USB_EXT_PROP_UNICODE_LINK 6 +#define USB_EXT_PROP_UNICODE_MULTI 7 + +static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset) +{ + return buf + offset; +} + +static inline u8 *usb_ext_prop_size_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE); +} + +static inline u8 *usb_ext_prop_type_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE); +} + +static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH); +} + +static inline u8 *usb_ext_prop_name_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME); +} + +static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off) +{ + return __usb_ext_prop_ptr(buf, + USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off); +} + +static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off); +} + +static inline void usb_ext_prop_put_size(u8 *buf, int dw_size) +{ + put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf)); +} + +static inline void usb_ext_prop_put_type(u8 *buf, int type) +{ + put_unaligned_le32(type, usb_ext_prop_type_ptr(buf)); +} + +static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl) +{ + int result; + + put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf)); + memset(usb_ext_prop_name_ptr(buf), 0, 2 * strlen(name)); + result = utf8_to_utf16le(name, (__le16 *)usb_ext_prop_name_ptr(buf), + strlen(name)); + if (result < 0) + return result; + + put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]); + + return pnl; +} + +static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const char *data, + int data_len) +{ + put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl)); + memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len); +} + +static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string, + int data_len) +{ + int result; + put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl)); + memset(usb_ext_prop_data_ptr(buf, pnl), 0, 2 * (data_len >> 1)); + result = utf8_to_utf16le(string, (__le16 *) usb_ext_prop_data_ptr(buf, pnl), + data_len >> 1); + if (result < 0) + return result; + + put_unaligned_le16(0, + &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]); + + return data_len; +} + +#endif /* __U_OS_DESC_H__ */ diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index d4f2a49869..d75a0bc4c4 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -37,6 +37,53 @@ struct usb_configuration; +/** + * struct usb_os_desc_ext_prop - describes one "Extended Property" + * @entry: used to keep a list of extended properties + * @type: Extended Property type + * @name_len: Extended Property unicode name length, including terminating '\0' + * @name: Extended Property name + * @data_len: Length of Extended Property blob (for unicode store double len) + * @data: Extended Property blob + */ +struct usb_os_desc_ext_prop { + struct list_head entry; + u8 type; + int name_len; + char *name; + int data_len; + char *data; +}; + +/** + * struct usb_os_desc - describes OS descriptors associated with one interface + * @ext_compat_id: 16 bytes of "Compatible ID" and "Subcompatible ID" + * @ext_prop: Extended Properties list + * @ext_prop_len: Total length of Extended Properties blobs + * @ext_prop_count: Number of Extended Properties + */ +struct usb_os_desc { + char *ext_compat_id; + struct list_head ext_prop; + int ext_prop_len; + int ext_prop_count; +}; + +/** + * struct usb_os_desc_table - describes OS descriptors associated with one + * interface of a usb_function + * @if_id: Interface id + * @os_desc: "Extended Compatibility ID" and "Extended Properties" of the + * interface + * + * Each interface can have at most one "Extended Compatibility ID" and a + * number of "Extended Properties". + */ +struct usb_os_desc_table { + int if_id; + struct usb_os_desc *os_desc; +}; + /** * struct usb_function - describes one function of a configuration * @name: For diagnostics, identifies the function. @@ -50,6 +97,10 @@ struct usb_configuration; * the function will not be available at high speed. * @config: assigned when @usb_add_function() is called; this is the * configuration with which this function is associated. + * @os_desc_table: Table of (interface id, os descriptors) pairs. The function + * can expose more than one interface. If an interface is a member of + * an IAD, only the first interface of IAD has its entry in the table. + * @os_desc_n: Number of entries in os_desc_table * @bind: Before the gadget can register, all of its functions bind() to the * available resources including string and interface identifiers used * in interface or class descriptors; endpoints; I/O buffers; and so on. @@ -98,6 +149,9 @@ struct usb_function { struct usb_configuration *config; + struct usb_os_desc_table *os_desc_table; + unsigned os_desc_n; + /* REVISIT: bind() functions can be marked __init, which * makes trouble for section mismatch analysis. See if * we can't restructure things to avoid mismatching. @@ -292,10 +346,12 @@ extern void usb_composite_unregister(struct usb_composite_driver *); * @gadget: read-only, abstracts the gadget's usb peripheral controller * @req: used for control responses; buffer is pre-allocated * @bufsiz: size of buffer pre-allocated in @req + * @os_desc_req: used for OS descriptors responses; buffer is pre-allocated * @config: the currently active configuration * @qw_sign: qwSignature part of the OS string * @b_vendor_code: bMS_VendorCode part of the OS string * @use_os_string: false by default, interested gadgets set it + * @os_desc_config: the configuration to be used with OS descriptors * * One of these devices is allocated and initialized before the * associated device driver's bind() is called. @@ -332,6 +388,7 @@ struct usb_composite_dev { /* OS String is a custom (yet popular) extension to the USB standard. */ u8 qw_sign[OS_STRING_QW_SIGN_LEN]; u8 b_vendor_code; + struct usb_configuration *os_desc_config; unsigned int use_os_string:1; /* private: */