From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on archive.lwn.net X-Spam-Level: X-Spam-Status: No, score=-5.4 required=5.0 tests=DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=ham autolearn_force=no version=3.4.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by archive.lwn.net (Postfix) with ESMTP id 855047D09D for ; Tue, 29 May 2018 18:50:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965311AbeE2Suk (ORCPT ); Tue, 29 May 2018 14:50:40 -0400 Received: from mail-lf0-f68.google.com ([209.85.215.68]:39417 "EHLO mail-lf0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S964830AbeE2Sui (ORCPT ); Tue, 29 May 2018 14:50:38 -0400 Received: by mail-lf0-f68.google.com with SMTP id t134-v6so347090lff.6; Tue, 29 May 2018 11:50:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=7eWPmSEyvVL2KT8S0pv6ppDCoDVl9dtdqpleNTAtOTk=; b=uytV5XGvqDyrGqJTOtz53H3TgDMGvDZjRhoh6/xKE4kI4YbeH7/7PyNuW6CEUFVGNi ioKz2djNNxceKryD7JC9VeA7iG3jEEduVloEAVKtKnnuUw3CE9T+JxMn6rOqoDx+ygqJ ihhOc2cFOgD4gIHnHqg9LzL4vM3X8dTKujKdCpvc9+D/5CBwrHi1IvS1ZZpbgyrK8W1a F0GhhuZg7FpSLwTINrjfw73zZ+hHEWcppavIMO0r9FkPQHtPDIfX5r2aOSd74QJgPXOL jXXiv3ZRdlwD2i/8C9uk7LGenLrT5+NiGjq2Dju7criwznPYvpghZ6QKTKqH/CKUmbr4 cTgw== 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; bh=7eWPmSEyvVL2KT8S0pv6ppDCoDVl9dtdqpleNTAtOTk=; b=gw6H9qsjqiNLX5SkL+SjH3NMEqB5ewMcTng91aNDCgk35Tpo4DgTev62+JvyaTXrX1 fjBVNdFymT5+CSR4a8zw1ayKC21POcpxkogpNMBXQ9/ZKLxWJ6cQE4TgK2A7AfbRddgs Gf0saEyxRucHYslWXK1EQNgBYft0lTBI/7ulT0erOAwGrk6q5YskPYchWbvsTSolkmaG QFPFYBfm2J+SsoMhGCEpcnU0iXt/JAuy+Sd8iGImjeFLvQf3/LpyR8fbvThGUewMGUjk +lZD/zdm+yOJoyKzoiYjs/qWvjpnNwBXOoEzP+at0PffNImIqq6JzJFlI/1UbGrEqZdE uwcg== X-Gm-Message-State: ALKqPwcCPjvdSqTKrWNcXV/0cUgA5++HyC08oHOykYVX5EVRUa5V1rq7 8wi9Du7xvvykk17EIVKJwIA= X-Google-Smtp-Source: ADUXVKKrFtCmnPbK4AOcWhx/VF3/qlIQ48sLg3yoL0+GkMCGuJPVjTVPn/o8puC6RR3YfM0bjHoqBg== X-Received: by 2002:a2e:29cf:: with SMTP id p76-v6mr7689314ljp.12.1527619835841; Tue, 29 May 2018 11:50:35 -0700 (PDT) Received: from localhost.localdomain (c-2ec2d783-74736162.cust.telenor.se. [46.194.215.131]) by smtp.gmail.com with ESMTPSA id i29-v6sm7488306lfb.89.2018.05.29.11.50.32 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 29 May 2018 11:50:34 -0700 (PDT) From: Marcus Folkesson To: Greg Kroah-Hartman , Jonathan Corbet , Felipe Balbi , davem@davemloft.net, Mauro Carvalho Chehab , Andrew Morton , Randy Dunlap , Ruslan Bilovol , Thomas Gleixner , Kate Stewart Cc: linux-usb@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Marcus Folkesson Subject: [PATCH v3 1/3] usb: gadget: ccid: add support for USB CCID Gadget Device Date: Tue, 29 May 2018 20:50:19 +0200 Message-Id: <20180529185021.13738-1-marcus.folkesson@gmail.com> X-Mailer: git-send-email 2.16.2 Sender: linux-doc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-doc@vger.kernel.org Chip Card Interface Device (CCID) protocol is a USB protocol that allows a smartcard device to be connected to a computer via a card reader using a standard USB interface, without the need for each manufacturer of smartcards to provide its own reader or protocol. This gadget driver makes Linux show up as a CCID device to the host and let a userspace daemon act as the smartcard. This is useful when the Linux gadget itself should act as a cryptographic device or forward APDUs to an embedded smartcard device. Signed-off-by: Marcus Folkesson --- Notes: v3: - fix sparse warnings reported by kbuild test robot v2: - add the missing changelog text drivers/usb/gadget/Kconfig | 17 + drivers/usb/gadget/function/Makefile | 1 + drivers/usb/gadget/function/f_ccid.c | 993 +++++++++++++++++++++++++++++++++++ drivers/usb/gadget/function/f_ccid.h | 91 ++++ include/uapi/linux/usb/ccid.h | 93 ++++ 5 files changed, 1195 insertions(+) create mode 100644 drivers/usb/gadget/function/f_ccid.c create mode 100644 drivers/usb/gadget/function/f_ccid.h create mode 100644 include/uapi/linux/usb/ccid.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 31cce7805eb2..bdebdf1ffa2b 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -149,6 +149,9 @@ config USB_LIBCOMPOSITE config USB_F_ACM tristate +config USB_F_CCID + tristate + config USB_F_SS_LB tristate @@ -248,6 +251,20 @@ config USB_CONFIGFS_ACM ACM serial link. This function can be used to interoperate with MS-Windows hosts or with the Linux-USB "cdc-acm" driver. +config USB_CONFIGFS_CCID + bool "Chip Card Interface Device (CCID)" + depends on USB_CONFIGFS + select USB_F_CCID + help + The CCID function driver provides generic emulation of a + Chip Card Interface Device (CCID). + + You will need a user space server talking to /dev/ccidg*, + since the kernel itself does not implement CCID/TPDU/APDU + protocol. + + For more information, see Documentation/usb/gadget_ccid.rst. + config USB_CONFIGFS_OBEX bool "Object Exchange Model (CDC OBEX)" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 5d3a6cf02218..629851009e1a 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -9,6 +9,7 @@ ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/ # USB Functions usb_f_acm-y := f_acm.o obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o +obj-$(CONFIG_USB_F_CCID) += f_ccid.o usb_f_ss_lb-y := f_loopback.o f_sourcesink.o obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o obj-$(CONFIG_USB_U_SERIAL) += u_serial.o diff --git a/drivers/usb/gadget/function/f_ccid.c b/drivers/usb/gadget/function/f_ccid.c new file mode 100644 index 000000000000..47fb229a06db --- /dev/null +++ b/drivers/usb/gadget/function/f_ccid.c @@ -0,0 +1,993 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * f_ccid.c -- Chip Card Interface Device (CCID) function Driver + * + * Copyright (C) 2018 Marcus Folkesson + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f_ccid.h" +#include "u_f.h" + +/* Number of tx requests to allocate */ +#define N_TX_REQS 4 + +/* Maximum number of devices */ +#define CCID_MINORS 4 + +struct ccidg_bulk_dev { + atomic_t is_open; + atomic_t rx_req_busy; + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct usb_request *rx_req; + atomic_t rx_done; + struct list_head tx_idle; +}; + +struct f_ccidg { + struct usb_function_instance func_inst; + struct usb_function function; + spinlock_t lock; + atomic_t online; + + /* Character device */ + struct cdev cdev; + int minor; + + /* Dynamic attributes */ + u32 features; + u32 protocols; + u8 pinsupport; + u8 nslots; + u8 lcdlayout; + + /* Endpoints */ + struct usb_ep *in; + struct usb_ep *out; + struct ccidg_bulk_dev bulk_dev; +}; + +/* Interface Descriptor: */ +static struct usb_interface_descriptor ccid_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CSCID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, +}; + +/* CCID Class Descriptor */ +static struct ccid_class_descriptor ccid_class_desc = { + .bLength = sizeof(ccid_class_desc), + .bDescriptorType = CCID_DECRIPTOR_TYPE, + .bcdCCID = cpu_to_le16(CCID1_10), + /* .bMaxSlotIndex = DYNAMIC */ + .bVoltageSupport = CCID_VOLTS_3_0, + /* .dwProtocols = DYNAMIC */ + .dwDefaultClock = cpu_to_le32(3580), + .dwMaximumClock = cpu_to_le32(3580), + .bNumClockSupported = 0, + .dwDataRate = cpu_to_le32(9600), + .dwMaxDataRate = cpu_to_le32(9600), + .bNumDataRatesSupported = 0, + .dwMaxIFSD = 0, + .dwSynchProtocols = 0, + .dwMechanical = 0, + /* .dwFeatures = DYNAMIC */ + + /* extended APDU level Message Length */ + .dwMaxCCIDMessageLength = cpu_to_le32(0x200), + .bClassGetResponse = 0x0, + .bClassEnvelope = 0x0, + /* .wLcdLayout = DYNAMIC */ + /* .bPINSupport = DYNAMIC */ + .bMaxCCIDBusySlots = 1 +}; + +/* Full speed support: */ +static struct usb_endpoint_descriptor ccid_fs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_endpoint_descriptor ccid_fs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_descriptor_header *ccid_fs_descs[] = { + (struct usb_descriptor_header *) &ccid_interface_desc, + (struct usb_descriptor_header *) &ccid_class_desc, + (struct usb_descriptor_header *) &ccid_fs_in_desc, + (struct usb_descriptor_header *) &ccid_fs_out_desc, + NULL, +}; + +/* High speed support: */ +static struct usb_endpoint_descriptor ccid_hs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor ccid_hs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_descriptor_header *ccid_hs_descs[] = { + (struct usb_descriptor_header *) &ccid_interface_desc, + (struct usb_descriptor_header *) &ccid_class_desc, + (struct usb_descriptor_header *) &ccid_hs_in_desc, + (struct usb_descriptor_header *) &ccid_hs_out_desc, + NULL, +}; + +static DEFINE_IDA(ccidg_ida); +static int major; +static DEFINE_MUTEX(ccidg_ida_lock); /* protects access to ccidg_ida */ +static struct class *ccidg_class; + +static inline struct f_ccidg_opts *to_f_ccidg_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_ccidg_opts, + func_inst.group); +} + +static inline struct f_ccidg *func_to_ccidg(struct usb_function *f) +{ + return container_of(f, struct f_ccidg, function); +} + +static inline int ccidg_get_minor(void) +{ + int ret; + + ret = ida_simple_get(&ccidg_ida, 0, 0, GFP_KERNEL); + if (ret >= CCID_MINORS) { + ida_simple_remove(&ccidg_ida, ret); + ret = -ENODEV; + } + + return ret; +} + +static inline void ccidg_put_minor(int minor) +{ + ida_simple_remove(&ccidg_ida, minor); +} + +static int ccidg_setup(void) +{ + int ret; + dev_t dev; + + ccidg_class = class_create(THIS_MODULE, "ccidg"); + if (IS_ERR(ccidg_class)) { + ccidg_class = NULL; + return PTR_ERR(ccidg_class); + } + + ret = alloc_chrdev_region(&dev, 0, CCID_MINORS, "ccidg"); + if (ret) { + class_destroy(ccidg_class); + ccidg_class = NULL; + return ret; + } + + major = MAJOR(dev); + + return 0; +} + +static void ccidg_cleanup(void) +{ + if (major) { + unregister_chrdev_region(MKDEV(major, 0), CCID_MINORS); + major = 0; + } + + class_destroy(ccidg_class); + ccidg_class = NULL; +} + +static void ccidg_attr_release(struct config_item *item) +{ + struct f_ccidg_opts *opts = to_f_ccidg_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations ccidg_item_ops = { + .release = ccidg_attr_release, +}; + +#define F_CCIDG_OPT(name, prec, limit) \ +static ssize_t f_ccidg_opts_##name##_show(struct config_item *item, char *page)\ +{ \ + struct f_ccidg_opts *opts = to_f_ccidg_opts(item); \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%x\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_ccidg_opts_##name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct f_ccidg_opts *opts = to_f_ccidg_opts(item); \ + int ret; \ + u##prec num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou##prec(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + opts->name = num; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +CONFIGFS_ATTR(f_ccidg_opts_, name) + +F_CCIDG_OPT(features, 32, 0xffffffff); +F_CCIDG_OPT(protocols, 32, 0x03); +F_CCIDG_OPT(pinsupport, 8, 0x03); +F_CCIDG_OPT(lcdlayout, 16, 0xffff); +F_CCIDG_OPT(nslots, 8, 0xff); + +static struct configfs_attribute *ccidg_attrs[] = { + &f_ccidg_opts_attr_features, + &f_ccidg_opts_attr_protocols, + &f_ccidg_opts_attr_pinsupport, + &f_ccidg_opts_attr_lcdlayout, + &f_ccidg_opts_attr_nslots, + NULL, +}; + +static struct config_item_type ccidg_func_type = { + .ct_item_ops = &ccidg_item_ops, + .ct_attrs = ccidg_attrs, + .ct_owner = THIS_MODULE, +}; + +static void ccidg_req_put(struct f_ccidg *ccidg, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&ccidg->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&ccidg->lock, flags); +} + +static struct usb_request *ccidg_req_get(struct f_ccidg *ccidg, + struct list_head *head) +{ + unsigned long flags; + struct usb_request *req = NULL; + + spin_lock_irqsave(&ccidg->lock, flags); + if (!list_empty(head)) { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&ccidg->lock, flags); + + return req; +} + +static void ccidg_bulk_complete_tx(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ccidg *ccidg = (struct f_ccidg *)ep->driver_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_composite_dev *cdev = ccidg->function.config->cdev; + + switch (req->status) { + default: + VDBG(cdev, "ccid: tx err %d\n", req->status); + /* FALLTHROUGH */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + break; + case 0: + break; + } + + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + wake_up(&bulk_dev->write_wq); +} + +static void ccidg_bulk_complete_rx(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ccidg *ccidg = (struct f_ccidg *)ep->driver_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_composite_dev *cdev = ccidg->function.config->cdev; + + switch (req->status) { + + /* normal completion */ + case 0: + /* We only cares about packets with nonzero length */ + if (req->actual > 0) + atomic_set(&bulk_dev->rx_done, 1); + break; + + /* software-driven interface shutdown */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + VDBG(cdev, "ccid: rx shutdown, code %d\n", req->status); + break; + + /* for hardware automagic (such as pxa) */ + case -ECONNABORTED: /* endpoint reset */ + DBG(cdev, "ccid: rx %s reset\n", ep->name); + break; + + /* data overrun */ + case -EOVERFLOW: + /* FALLTHROUGH */ + default: + DBG(cdev, "ccid: rx status %d\n", req->status); + break; + } + + wake_up(&bulk_dev->read_wq); +} + +static struct usb_request * +ccidg_request_alloc(struct usb_ep *ep, unsigned int len) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (!req) + return ERR_PTR(-ENOMEM); + + req->length = len; + req->buf = kmalloc(len, GFP_ATOMIC); + if (req->buf == NULL) { + usb_ep_free_request(ep, req); + return ERR_PTR(-ENOMEM); + } + + return req; +} + +static void ccidg_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static int ccidg_function_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_ccidg *ccidg = container_of(f, struct f_ccidg, function); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int ret = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + if (!atomic_read(&ccidg->online)) + return -ENOTCONN; + + switch (ctrl->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_CLASS: + { + switch (ctrl->bRequest) { + case CCIDGENERICREQ_GET_CLOCK_FREQUENCIES: + if (w_length > sizeof(ccid_class_desc.dwDefaultClock)) + break; + + *(__le32 *) req->buf = ccid_class_desc.dwDefaultClock; + ret = sizeof(ccid_class_desc.dwDefaultClock); + break; + + case CCIDGENERICREQ_GET_DATA_RATES: + if (w_length > sizeof(ccid_class_desc.dwDataRate)) + break; + + *(__le32 *) req->buf = ccid_class_desc.dwDataRate; + ret = sizeof(ccid_class_desc.dwDataRate); + break; + + default: + VDBG(f->config->cdev, + "ccid: invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + } + } + + /* responded with data transfer or status phase? */ + if (ret >= 0) { + VDBG(f->config->cdev, "ccid: req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + req->length = ret; + ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (ret < 0) + ERROR(f->config->cdev, + "ccid: ep0 enqueue err %d\n", ret); + } + + return ret; +} + +static void ccidg_function_disable(struct usb_function *f) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req; + + /* Disable endpoints */ + usb_ep_disable(ccidg->in); + usb_ep_disable(ccidg->out); + + /* Free endpoint related requests */ + if (!atomic_read(&bulk_dev->rx_req_busy)) + ccidg_request_free(bulk_dev->rx_req, ccidg->out); + while ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle))) + ccidg_request_free(req, ccidg->in); + + atomic_set(&ccidg->online, 0); + + /* Wake up threads */ + wake_up(&bulk_dev->write_wq); + wake_up(&bulk_dev->read_wq); +} + +static int ccidg_start_ep(struct f_ccidg *ccidg, struct usb_function *f, + struct usb_ep *ep) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + usb_ep_disable(ep); + + ret = config_ep_by_speed(cdev->gadget, f, ep); + if (ret) { + ERROR(cdev, "ccid: can't configure %s: %d\n", ep->name, ret); + return ret; + } + + ret = usb_ep_enable(ep); + if (ret) { + ERROR(cdev, "ccid: can't start %s: %d\n", ep->name, ret); + return ret; + } + + ep->driver_data = ccidg; + + return ret; +} + +static int ccidg_function_set_alt(struct usb_function *f, + unsigned int intf, unsigned int alt) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req; + int ret; + int i; + + /* Allocate requests for our endpoints */ + req = ccidg_request_alloc(ccidg->out, + sizeof(struct ccidg_bulk_out_header)); + if (IS_ERR(req)) { + ERROR(cdev, "ccid: uname to allocate memory for out req\n"); + return PTR_ERR(req); + } + req->complete = ccidg_bulk_complete_rx; + req->context = ccidg; + bulk_dev->rx_req = req; + + /* Allocate bunch of in requests */ + for (i = 0; i < N_TX_REQS; i++) { + req = ccidg_request_alloc(ccidg->in, + sizeof(struct ccidg_bulk_in_header)); + + if (IS_ERR(req)) { + ret = PTR_ERR(req); + ERROR(cdev, + "ccid: uname to allocate memory for in req\n"); + goto free_bulk_out; + } + req->complete = ccidg_bulk_complete_tx; + req->context = ccidg; + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + } + + /* choose the descriptors and enable endpoints */ + ret = ccidg_start_ep(ccidg, f, ccidg->in); + if (ret) + goto free_bulk_in; + + ret = ccidg_start_ep(ccidg, f, ccidg->out); + if (ret) + goto disable_ep_in; + + atomic_set(&ccidg->online, 1); + return ret; + +disable_ep_in: + usb_ep_disable(ccidg->in); +free_bulk_in: + while ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle))) + ccidg_request_free(req, ccidg->in); +free_bulk_out: + ccidg_request_free(bulk_dev->rx_req, ccidg->out); + return ret; +} + +static int ccidg_bulk_open(struct inode *inode, struct file *file) +{ + struct f_ccidg *ccidg; + struct ccidg_bulk_dev *bulk_dev; + + ccidg = container_of(inode->i_cdev, struct f_ccidg, cdev); + bulk_dev = &ccidg->bulk_dev; + + if (!atomic_read(&ccidg->online)) { + DBG(ccidg->function.config->cdev, "ccid: device not online\n"); + return -ENODEV; + } + + if (atomic_read(&bulk_dev->is_open)) { + DBG(ccidg->function.config->cdev, + "ccid: device already opened\n"); + return -EBUSY; + } + + atomic_set(&bulk_dev->is_open, 1); + + file->private_data = ccidg; + + return 0; +} + +static int ccidg_bulk_release(struct inode *inode, struct file *file) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + + atomic_set(&bulk_dev->is_open, 0); + return 0; +} + +static ssize_t ccidg_bulk_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req; + int r = count, xfer; + int ret; + + /* Make sure we have enough space for a whole package */ + if (count < sizeof(struct ccidg_bulk_out_header)) { + DBG(ccidg->function.config->cdev, + "ccid: too small buffer size. %zu provided, need at least %zu\n", + count, sizeof(struct ccidg_bulk_out_header)); + return -ENOMEM; + } + + if (!atomic_read(&ccidg->online)) + return -ENODEV; + + /* queue a request */ + req = bulk_dev->rx_req; + req->length = count; + atomic_set(&bulk_dev->rx_done, 0); + + ret = usb_ep_queue(ccidg->out, req, GFP_KERNEL); + if (ret < 0) { + ERROR(ccidg->function.config->cdev, + "ccid: usb ep queue failed\n"); + return -EIO; + } + + if (!atomic_read(&bulk_dev->rx_done) && + file->f_flags & (O_NONBLOCK | O_NDELAY)) + return -EAGAIN; + + /* wait for a request to complete */ + ret = wait_event_interruptible(bulk_dev->read_wq, + atomic_read(&bulk_dev->rx_done) || + !atomic_read(&ccidg->online)); + if (ret < 0) { + usb_ep_dequeue(ccidg->out, req); + return -ERESTARTSYS; + } + + /* Still online? */ + if (!atomic_read(&ccidg->online)) + return -ENODEV; + + atomic_set(&bulk_dev->rx_req_busy, 1); + xfer = (req->actual < count) ? req->actual : count; + + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + + atomic_set(&bulk_dev->rx_req_busy, 0); + if (!atomic_read(&ccidg->online)) { + ccidg_request_free(bulk_dev->rx_req, ccidg->out); + return -ENODEV; + } + + return xfer; +} + +static ssize_t ccidg_bulk_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req = NULL; + int ret; + + /* Are we online? */ + if (!atomic_read(&ccidg->online)) + return -ENODEV; + + /* Avoid Zero Length Packets (ZLP) */ + if (!count) + return 0; + + /* Make sure we have enough space for a whole package */ + if (count > sizeof(struct ccidg_bulk_out_header)) { + DBG(ccidg->function.config->cdev, + "ccid: too much data. %zu provided, but we can only handle %zu\n", + count, sizeof(struct ccidg_bulk_out_header)); + return -ENOMEM; + } + + if (list_empty(&bulk_dev->tx_idle) && + file->f_flags & (O_NONBLOCK | O_NDELAY)) + return -EAGAIN; + + /* get an idle tx request to use */ + ret = wait_event_interruptible(bulk_dev->write_wq, + ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle)))); + + if (ret < 0) + return -ERESTARTSYS; + + if (copy_from_user(req->buf, buf, count)) { + if (!atomic_read(&ccidg->online)) { + ccidg_request_free(req, ccidg->in); + return -ENODEV; + } else { + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + return -EFAULT; + } + } + + req->length = count; + ret = usb_ep_queue(ccidg->in, req, GFP_KERNEL); + if (ret < 0) { + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + + if (!atomic_read(&ccidg->online)) { + /* Free up all requests if we are not online */ + while ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle))) + ccidg_request_free(req, ccidg->in); + + return -ENODEV; + } + return -EIO; + } + + return count; +} + +static __poll_t ccidg_bulk_poll(struct file *file, poll_table * wait) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + __poll_t ret = 0; + + poll_wait(file, &bulk_dev->read_wq, wait); + poll_wait(file, &bulk_dev->write_wq, wait); + + if (list_empty(&bulk_dev->tx_idle)) + ret |= EPOLLOUT | EPOLLWRNORM; + + if (atomic_read(&bulk_dev->rx_done)) + ret |= EPOLLIN | EPOLLRDNORM; + + return ret; +} + +static const struct file_operations f_ccidg_fops = { + .owner = THIS_MODULE, + .read = ccidg_bulk_read, + .write = ccidg_bulk_write, + .open = ccidg_bulk_open, + .poll = ccidg_bulk_poll, + .release = ccidg_bulk_release, +}; + +static int ccidg_bulk_device_init(struct f_ccidg *dev) +{ + struct ccidg_bulk_dev *bulk_dev = &dev->bulk_dev; + + init_waitqueue_head(&bulk_dev->read_wq); + init_waitqueue_head(&bulk_dev->write_wq); + INIT_LIST_HEAD(&bulk_dev->tx_idle); + + return 0; +} + +static void ccidg_function_free(struct usb_function *f) +{ + struct f_ccidg *ccidg; + struct f_ccidg_opts *opts; + + ccidg = func_to_ccidg(f); + opts = container_of(f->fi, struct f_ccidg_opts, func_inst); + + kfree(ccidg); + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); +} + +static void ccidg_function_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + + device_destroy(ccidg_class, MKDEV(major, ccidg->minor)); + cdev_del(&ccidg->cdev); + + /* disable/free request and end point */ + usb_free_all_descriptors(f); +} + +static int ccidg_function_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + struct usb_ep *ep; + struct usb_composite_dev *cdev = c->cdev; + struct device *device; + dev_t dev; + int ifc_id; + int ret; + + /* allocate instance-specific interface IDs, and patch descriptors */ + ifc_id = usb_interface_id(c, f); + if (ifc_id < 0) { + ERROR(cdev, "ccid: unable to allocate ifc id, err:%d\n", + ifc_id); + return ifc_id; + } + ccid_interface_desc.bInterfaceNumber = ifc_id; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_in_desc); + if (!ep) { + ERROR(cdev, "ccid: usb epin autoconfig failed\n"); + ret = -ENODEV; + goto ep_auto_in_fail; + } + ccidg->in = ep; + ep->driver_data = ccidg; + + ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_out_desc); + if (!ep) { + ERROR(cdev, "ccid: usb epout autoconfig failed\n"); + ret = -ENODEV; + goto ep_auto_out_fail; + } + ccidg->out = ep; + ep->driver_data = ccidg; + + /* set descriptor dynamic values */ + ccid_class_desc.dwFeatures = cpu_to_le32(ccidg->features); + ccid_class_desc.bPINSupport = ccidg->pinsupport; + ccid_class_desc.wLcdLayout = cpu_to_le16(ccidg->lcdlayout); + ccid_class_desc.bMaxSlotIndex = ccidg->nslots; + ccid_class_desc.dwProtocols = cpu_to_le32(ccidg->protocols); + + if (ccidg->protocols == CCID_PROTOCOL_NOT_SEL) { + ccidg->protocols = CCID_PROTOCOL_T0 | CCID_PROTOCOL_T1; + INFO(ccidg->function.config->cdev, + "ccid: No protocol selected. Support both T0 and T1.\n"); + } + + + ccid_hs_in_desc.bEndpointAddress = + ccid_fs_in_desc.bEndpointAddress; + ccid_hs_out_desc.bEndpointAddress = + ccid_fs_out_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, ccid_fs_descs, + ccid_hs_descs, NULL, NULL); + if (ret) + goto ep_auto_out_fail; + + /* create char device */ + cdev_init(&ccidg->cdev, &f_ccidg_fops); + dev = MKDEV(major, ccidg->minor); + ret = cdev_add(&ccidg->cdev, dev, 1); + if (ret) + goto fail_free_descs; + + device = device_create(ccidg_class, NULL, dev, NULL, + "%s%d", "ccidg", ccidg->minor); + if (IS_ERR(device)) { + ret = PTR_ERR(device); + goto del; + } + + return 0; + +del: + cdev_del(&ccidg->cdev); +fail_free_descs: + usb_free_all_descriptors(f); +ep_auto_out_fail: + ccidg->out->driver_data = NULL; + ccidg->out = NULL; +ep_auto_in_fail: + ccidg->in->driver_data = NULL; + ccidg->in = NULL; + ERROR(f->config->cdev, "ccidg_bind FAILED\n"); + + return ret; +} + +static struct usb_function *ccidg_alloc(struct usb_function_instance *fi) +{ + struct f_ccidg *ccidg; + struct f_ccidg_opts *opts; + int ret; + + ccidg = kzalloc(sizeof(*ccidg), GFP_KERNEL); + if (!ccidg) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&ccidg->lock); + + ret = ccidg_bulk_device_init(ccidg); + if (ret) { + kfree(ccidg); + return ERR_PTR(ret); + } + + opts = container_of(fi, struct f_ccidg_opts, func_inst); + + mutex_lock(&opts->lock); + ++opts->refcnt; + + ccidg->minor = opts->minor; + ccidg->features = opts->features; + ccidg->protocols = opts->protocols; + ccidg->pinsupport = opts->pinsupport; + ccidg->nslots = opts->nslots; + mutex_unlock(&opts->lock); + + ccidg->function.name = "ccid"; + ccidg->function.bind = ccidg_function_bind; + ccidg->function.unbind = ccidg_function_unbind; + ccidg->function.set_alt = ccidg_function_set_alt; + ccidg->function.disable = ccidg_function_disable; + ccidg->function.setup = ccidg_function_setup; + ccidg->function.free_func = ccidg_function_free; + + return &ccidg->function; +} + +static void ccidg_free_inst(struct usb_function_instance *f) +{ + struct f_ccidg_opts *opts; + + opts = container_of(f, struct f_ccidg_opts, func_inst); + mutex_lock(&ccidg_ida_lock); + + ccidg_put_minor(opts->minor); + if (ida_is_empty(&ccidg_ida)) + ccidg_cleanup(); + + mutex_unlock(&ccidg_ida_lock); + + kfree(opts); +} + +static struct usb_function_instance *ccidg_alloc_inst(void) +{ + struct f_ccidg_opts *opts; + struct usb_function_instance *ret; + int status = 0; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = ccidg_free_inst; + ret = &opts->func_inst; + + mutex_lock(&ccidg_ida_lock); + + if (ida_is_empty(&ccidg_ida)) { + status = ccidg_setup(); + if (status) { + ret = ERR_PTR(status); + kfree(opts); + goto unlock; + } + } + + opts->minor = ccidg_get_minor(); + if (opts->minor < 0) { + ret = ERR_PTR(opts->minor); + kfree(opts); + if (ida_is_empty(&ccidg_ida)) + ccidg_cleanup(); + goto unlock; + } + + config_group_init_type_name(&opts->func_inst.group, + "", &ccidg_func_type); + +unlock: + mutex_unlock(&ccidg_ida_lock); + return ret; +} + +DECLARE_USB_FUNCTION_INIT(ccid, ccidg_alloc_inst, ccidg_alloc); + +MODULE_DESCRIPTION("USB CCID Gadget driver"); +MODULE_AUTHOR("Marcus Folkesson "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/function/f_ccid.h b/drivers/usb/gadget/function/f_ccid.h new file mode 100644 index 000000000000..f1053ec5c4d9 --- /dev/null +++ b/drivers/usb/gadget/function/f_ccid.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Marcus Folkesson + */ + +#ifndef F_CCID_H +#define F_CCID_H + +#define CCID1_10 0x0110 +#define CCID_DECRIPTOR_TYPE 0x21 +#define ABDATA_SIZE 512 +#define SMART_CARD_DEVICE_CLASS 0x0B + +/* CCID Class Specific Request */ +#define CCIDGENERICREQ_ABORT 0x01 +#define CCIDGENERICREQ_GET_CLOCK_FREQUENCIES 0x02 +#define CCIDGENERICREQ_GET_DATA_RATES 0x03 + +/* Supported voltages */ +#define CCID_VOLTS_AUTO 0x00 +#define CCID_VOLTS_5_0 0x01 +#define CCID_VOLTS_3_0 0x02 +#define CCID_VOLTS_1_8 0x03 + +struct f_ccidg_opts { + struct usb_function_instance func_inst; + int minor; + __u32 features; + __u32 protocols; + __u8 pinsupport; + __u8 nslots; + __u8 lcdlayout; + + /* + * Protect the data form concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +struct ccidg_bulk_in_header { + __u8 bMessageType; + __le32 wLength; + __u8 bSlot; + __u8 bSeq; + __u8 bStatus; + __u8 bError; + __u8 bSpecific; + __u8 abData[ABDATA_SIZE]; + __u8 bSizeToSend; +} __packed; + +struct ccidg_bulk_out_header { + __u8 bMessageType; + __le32 wLength; + __u8 bSlot; + __u8 bSeq; + __u8 bSpecific_0; + __u8 bSpecific_1; + __u8 bSpecific_2; + __u8 APDU[ABDATA_SIZE]; +} __packed; + +struct ccid_class_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __le16 bcdCCID; + __u8 bMaxSlotIndex; + __u8 bVoltageSupport; + __le32 dwProtocols; + __le32 dwDefaultClock; + __le32 dwMaximumClock; + __u8 bNumClockSupported; + __le32 dwDataRate; + __le32 dwMaxDataRate; + __u8 bNumDataRatesSupported; + __le32 dwMaxIFSD; + __le32 dwSynchProtocols; + __le32 dwMechanical; + __le32 dwFeatures; + __le32 dwMaxCCIDMessageLength; + __u8 bClassGetResponse; + __u8 bClassEnvelope; + __le16 wLcdLayout; + __u8 bPINSupport; + __u8 bMaxCCIDBusySlots; +} __packed; + + +#endif diff --git a/include/uapi/linux/usb/ccid.h b/include/uapi/linux/usb/ccid.h new file mode 100644 index 000000000000..517897201563 --- /dev/null +++ b/include/uapi/linux/usb/ccid.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Marcus Folkesson + * + * This file holds USB constants defined by the CCID Specification. + */ + +#ifndef CCID_H +#define CCID_H + +/* Slot error register when bmCommandStatus = 1 */ +#define CCID_CMD_ABORTED 0xFF +#define CCID_ICC_MUTE 0xFE +#define CCID_XFR_PARITY_ERROR 0xFD +#define CCID_XFR_OVERRUN 0xFC +#define CCID_HW_ERROR 0xFB +#define CCID_BAD_ATR_TS 0xF8 +#define CCID_BAD_ATR_TCK 0xF7 +#define CCID_ICC_PROTOCOL_NOT_SUPPORTED 0xF6 +#define CCID_ICC_CLASS_NOT_SUPPORTED 0xF5 +#define CCID_PROCEDURE_BYTE_CONFLICT 0xF4 +#define CCID_DEACTIVATED_PROTOCOL 0xF3 +#define CCID_BUSY_WITH_AUTO_SEQUENCE 0xF2 +#define CCID_PIN_TIMEOUT 0xF0 +#define CCID_PIN_CANCELLED 0xEF +#define CCID_CMD_SLOT_BUSY 0xE0 + +/* PC to RDR messages (bulk out) */ +#define CCID_PC_TO_RDR_ICCPOWERON 0x62 +#define CCID_PC_TO_RDR_ICCPOWEROFF 0x63 +#define CCID_PC_TO_RDR_GETSLOTSTATUS 0x65 +#define CCID_PC_TO_RDR_XFRBLOCK 0x6F +#define CCID_PC_TO_RDR_GETPARAMETERS 0x6C +#define CCID_PC_TO_RDR_RESETPARAMETERS 0x6D +#define CCID_PC_TO_RDR_SETPARAMETERS 0x61 +#define CCID_PC_TO_RDR_ESCAPE 0x6B +#define CCID_PC_TO_RDR_ICCCLOCK 0x6E +#define CCID_PC_TO_RDR_T0APDU 0x6A +#define CCID_PC_TO_RDR_SECURE 0x69 +#define CCID_PC_TO_RDR_MECHANICAL 0x71 +#define CCID_PC_TO_RDR_ABORT 0x72 +#define CCID_PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY 0x73 + +/* RDR to PC messages (bulk in) */ +#define CCID_RDR_TO_PC_DATABLOCK 0x80 +#define CCID_RDR_TO_PC_SLOTSTATUS 0x81 +#define CCID_RDR_TO_PC_PARAMETERS 0x82 +#define CCID_RDR_TO_PC_ESCAPE 0x83 +#define CCID_RDR_TO_PC_DATARATEANDCLOCKFREQUENCY 0x84 + +/* Class Features */ + +/* No special characteristics */ +#define CCID_FEATURES_NADA 0x00000000 +/* Automatic parameter configuration based on ATR data */ +#define CCID_FEATURES_AUTO_PCONF 0x00000002 +/* Automatic activation of ICC on inserting */ +#define CCID_FEATURES_AUTO_ACTIV 0x00000004 +/* Automatic ICC voltage selection */ +#define CCID_FEATURES_AUTO_VOLT 0x00000008 +/* Automatic ICC clock frequency change */ +#define CCID_FEATURES_AUTO_CLOCK 0x00000010 +/* Automatic baud rate change */ +#define CCID_FEATURES_AUTO_BAUD 0x00000020 +/*Automatic parameters negotiation made by the CCID */ +#define CCID_FEATURES_AUTO_PNEGO 0x00000040 +/* Automatic PPS made by the CCID according to the active parameters */ +#define CCID_FEATURES_AUTO_PPS 0x00000080 +/* CCID can set ICC in clock stop mode */ +#define CCID_FEATURES_ICCSTOP 0x00000100 +/* NAD value other than 00 accepted (T=1 protocol in use) */ +#define CCID_FEATURES_NAD 0x00000200 +/* Automatic IFSD exchange as first exchange (T=1 protocol in use) */ +#define CCID_FEATURES_AUTO_IFSD 0x00000400 +/* TPDU level exchanges with CCID */ +#define CCID_FEATURES_EXC_TPDU 0x00010000 +/* Short APDU level exchange with CCID */ +#define CCID_FEATURES_EXC_SAPDU 0x00020000 +/* Short and Extended APDU level exchange with CCID */ +#define CCID_FEATURES_EXC_APDU 0x00040000 +/* USB Wake up signaling supported on card insertion and removal */ +#define CCID_FEATURES_WAKEUP 0x00100000 + +/* Supported protocols */ +#define CCID_PROTOCOL_NOT_SEL 0x00 +#define CCID_PROTOCOL_T0 0x01 +#define CCID_PROTOCOL_T1 0x02 + +#define CCID_PINSUPOORT_NONE 0x00 +#define CCID_PINSUPOORT_VERIFICATION (1 << 1) +#define CCID_PINSUPOORT_MODIFICATION (1 << 2) + +#endif -- 2.16.2 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Subject: [v3,1/3] usb: gadget: ccid: add support for USB CCID Gadget Device From: Marcus Folkesson Message-Id: <20180529185021.13738-1-marcus.folkesson@gmail.com> Date: Tue, 29 May 2018 20:50:19 +0200 To: Greg Kroah-Hartman , Jonathan Corbet , Felipe Balbi , davem@davemloft.net, Mauro Carvalho Chehab , Andrew Morton , Randy Dunlap , Ruslan Bilovol , Thomas Gleixner , Kate Stewart Cc: linux-usb@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Marcus Folkesson List-ID: Q2hpcCBDYXJkIEludGVyZmFjZSBEZXZpY2UgKENDSUQpIHByb3RvY29sIGlzIGEgVVNCIHByb3Rv Y29sIHRoYXQKYWxsb3dzIGEgc21hcnRjYXJkIGRldmljZSB0byBiZSBjb25uZWN0ZWQgdG8gYSBj b21wdXRlciB2aWEgYSBjYXJkCnJlYWRlciB1c2luZyBhIHN0YW5kYXJkIFVTQiBpbnRlcmZhY2Us IHdpdGhvdXQgdGhlIG5lZWQgZm9yIGVhY2ggbWFudWZhY3R1cmVyCm9mIHNtYXJ0Y2FyZHMgdG8g cHJvdmlkZSBpdHMgb3duIHJlYWRlciBvciBwcm90b2NvbC4KClRoaXMgZ2FkZ2V0IGRyaXZlciBt YWtlcyBMaW51eCBzaG93IHVwIGFzIGEgQ0NJRCBkZXZpY2UgdG8gdGhlIGhvc3QgYW5kIGxldCBh CnVzZXJzcGFjZSBkYWVtb24gYWN0IGFzIHRoZSBzbWFydGNhcmQuCgpUaGlzIGlzIHVzZWZ1bCB3 aGVuIHRoZSBMaW51eCBnYWRnZXQgaXRzZWxmIHNob3VsZCBhY3QgYXMgYSBjcnlwdG9ncmFwaGlj CmRldmljZSBvciBmb3J3YXJkIEFQRFVzIHRvIGFuIGVtYmVkZGVkIHNtYXJ0Y2FyZCBkZXZpY2Uu CgpTaWduZWQtb2ZmLWJ5OiBNYXJjdXMgRm9sa2Vzc29uIDxtYXJjdXMuZm9sa2Vzc29uQGdtYWls LmNvbT4KLS0tCgpOb3RlczoKICAgIHYzOgogICAgCS0gZml4IHNwYXJzZSB3YXJuaW5ncyByZXBv cnRlZCBieSBrYnVpbGQgdGVzdCByb2JvdAogICAgdjI6CiAgICAJLSBhZGQgdGhlIG1pc3Npbmcg Y2hhbmdlbG9nIHRleHQKCiBkcml2ZXJzL3VzYi9nYWRnZXQvS2NvbmZpZyAgICAgICAgICAgfCAg MTcgKwogZHJpdmVycy91c2IvZ2FkZ2V0L2Z1bmN0aW9uL01ha2VmaWxlIHwgICAxICsKIGRyaXZl cnMvdXNiL2dhZGdldC9mdW5jdGlvbi9mX2NjaWQuYyB8IDk5MyArKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKwogZHJpdmVycy91c2IvZ2FkZ2V0L2Z1bmN0aW9uL2ZfY2NpZC5oIHwg IDkxICsrKysKIGluY2x1ZGUvdWFwaS9saW51eC91c2IvY2NpZC5oICAgICAgICB8ICA5MyArKysr CiA1IGZpbGVzIGNoYW5nZWQsIDExOTUgaW5zZXJ0aW9ucygrKQogY3JlYXRlIG1vZGUgMTAwNjQ0 IGRyaXZlcnMvdXNiL2dhZGdldC9mdW5jdGlvbi9mX2NjaWQuYwogY3JlYXRlIG1vZGUgMTAwNjQ0 IGRyaXZlcnMvdXNiL2dhZGdldC9mdW5jdGlvbi9mX2NjaWQuaAogY3JlYXRlIG1vZGUgMTAwNjQ0 IGluY2x1ZGUvdWFwaS9saW51eC91c2IvY2NpZC5oCgpkaWZmIC0tZ2l0IGEvZHJpdmVycy91c2Iv Z2FkZ2V0L0tjb25maWcgYi9kcml2ZXJzL3VzYi9nYWRnZXQvS2NvbmZpZwppbmRleCAzMWNjZTc4 MDVlYjIuLmJkZWJkZjFmZmEyYiAxMDA2NDQKLS0tIGEvZHJpdmVycy91c2IvZ2FkZ2V0L0tjb25m aWcKKysrIGIvZHJpdmVycy91c2IvZ2FkZ2V0L0tjb25maWcKQEAgLTE0OSw2ICsxNDksOSBAQCBj b25maWcgVVNCX0xJQkNPTVBPU0lURQogY29uZmlnIFVTQl9GX0FDTQogCXRyaXN0YXRlCiAKK2Nv bmZpZyBVU0JfRl9DQ0lECisJdHJpc3RhdGUKKwogY29uZmlnIFVTQl9GX1NTX0xCCiAJdHJpc3Rh dGUKIApAQCAtMjQ4LDYgKzI1MSwyMCBAQCBjb25maWcgVVNCX0NPTkZJR0ZTX0FDTQogCSAgQUNN IHNlcmlhbCBsaW5rLiAgVGhpcyBmdW5jdGlvbiBjYW4gYmUgdXNlZCB0byBpbnRlcm9wZXJhdGUg d2l0aAogCSAgTVMtV2luZG93cyBob3N0cyBvciB3aXRoIHRoZSBMaW51eC1VU0IgImNkYy1hY20i IGRyaXZlci4KIAorY29uZmlnIFVTQl9DT05GSUdGU19DQ0lECisJYm9vbCAiQ2hpcCBDYXJkIElu dGVyZmFjZSBEZXZpY2UgKENDSUQpIgorCWRlcGVuZHMgb24gVVNCX0NPTkZJR0ZTCisJc2VsZWN0 IFVTQl9GX0NDSUQKKwloZWxwCisJICBUaGUgQ0NJRCBmdW5jdGlvbiBkcml2ZXIgcHJvdmlkZXMg Z2VuZXJpYyBlbXVsYXRpb24gb2YgYQorCSAgQ2hpcCBDYXJkIEludGVyZmFjZSBEZXZpY2UgKEND SUQpLgorCisJICBZb3Ugd2lsbCBuZWVkIGEgdXNlciBzcGFjZSBzZXJ2ZXIgdGFsa2luZyB0byAv ZGV2L2NjaWRnKiwKKwkgIHNpbmNlIHRoZSBrZXJuZWwgaXRzZWxmIGRvZXMgbm90IGltcGxlbWVu dCBDQ0lEL1RQRFUvQVBEVQorCSAgcHJvdG9jb2wuCisKKwkgIEZvciBtb3JlIGluZm9ybWF0aW9u LCBzZWUgRG9jdW1lbnRhdGlvbi91c2IvZ2FkZ2V0X2NjaWQucnN0LgorCiBjb25maWcgVVNCX0NP TkZJR0ZTX09CRVgKIAlib29sICJPYmplY3QgRXhjaGFuZ2UgTW9kZWwgKENEQyBPQkVYKSIKIAlk ZXBlbmRzIG9uIFVTQl9DT05GSUdGUwpkaWZmIC0tZ2l0IGEvZHJpdmVycy91c2IvZ2FkZ2V0L2Z1 bmN0aW9uL01ha2VmaWxlIGIvZHJpdmVycy91c2IvZ2FkZ2V0L2Z1bmN0aW9uL01ha2VmaWxlCmlu ZGV4IDVkM2E2Y2YwMjIxOC4uNjI5ODUxMDA5ZTFhIDEwMDY0NAotLS0gYS9kcml2ZXJzL3VzYi9n YWRnZXQvZnVuY3Rpb24vTWFrZWZpbGUKKysrIGIvZHJpdmVycy91c2IvZ2FkZ2V0L2Z1bmN0aW9u L01ha2VmaWxlCkBAIC05LDYgKzksNyBAQCBjY2ZsYWdzLXkJCQkrPSAtSSQoc3JjdHJlZSkvZHJp dmVycy91c2IvZ2FkZ2V0L3VkYy8KICMgVVNCIEZ1bmN0aW9ucwogdXNiX2ZfYWNtLXkJCQk6PSBm X2FjbS5vCiBvYmotJChDT05GSUdfVVNCX0ZfQUNNKQkJKz0gdXNiX2ZfYWNtLm8KK29iai0kKENP TkZJR19VU0JfRl9DQ0lEKQkrPSBmX2NjaWQubwogdXNiX2Zfc3NfbGIteQkJCTo9IGZfbG9vcGJh Y2subyBmX3NvdXJjZXNpbmsubwogb2JqLSQoQ09ORklHX1VTQl9GX1NTX0xCKQkrPSB1c2JfZl9z c19sYi5vCiBvYmotJChDT05GSUdfVVNCX1VfU0VSSUFMKQkrPSB1X3NlcmlhbC5vCmRpZmYgLS1n aXQgYS9kcml2ZXJzL3VzYi9nYWRnZXQvZnVuY3Rpb24vZl9jY2lkLmMgYi9kcml2ZXJzL3VzYi9n YWRnZXQvZnVuY3Rpb24vZl9jY2lkLmMKbmV3IGZpbGUgbW9kZSAxMDA2NDQKaW5kZXggMDAwMDAw MDAwMDAwLi40N2ZiMjI5YTA2ZGIKLS0tIC9kZXYvbnVsbAorKysgYi9kcml2ZXJzL3VzYi9nYWRn ZXQvZnVuY3Rpb24vZl9jY2lkLmMKQEAgLTAsMCArMSw5OTMgQEAKKy8vIFNQRFgtTGljZW5zZS1J ZGVudGlmaWVyOiBHUEwtMi4wCisvKgorICogZl9jY2lkLmMgLS0gQ2hpcCBDYXJkIEludGVyZmFj ZSBEZXZpY2UgKENDSUQpIGZ1bmN0aW9uIERyaXZlcgorICoKKyAqIENvcHlyaWdodCAoQykgMjAx OCBNYXJjdXMgRm9sa2Vzc29uIDxtYXJjdXMuZm9sa2Vzc29uQGdtYWlsLmNvbT4KKyAqCisgKi8K KyNpbmNsdWRlIDxsaW51eC9jZGV2Lmg+CisjaW5jbHVkZSA8bGludXgvZGV2aWNlLmg+CisjaW5j bHVkZSA8bGludXgvZnMuaD4KKyNpbmNsdWRlIDxsaW51eC9rZXJuZWwuaD4KKyNpbmNsdWRlIDxs aW51eC9tb2R1bGUuaD4KKyNpbmNsdWRlIDxsaW51eC9wb2xsLmg+CisjaW5jbHVkZSA8bGludXgv c2xhYi5oPgorI2luY2x1ZGUgPGxpbnV4L3VhY2Nlc3MuaD4KKyNpbmNsdWRlIDxsaW51eC91c2Iv Y29tcG9zaXRlLmg+CisjaW5jbHVkZSA8dWFwaS9saW51eC91c2IvY2NpZC5oPgorCisjaW5jbHVk ZSAiZl9jY2lkLmgiCisjaW5jbHVkZSAidV9mLmgiCisKKy8qIE51bWJlciBvZiB0eCByZXF1ZXN0 cyB0byBhbGxvY2F0ZSAqLworI2RlZmluZSBOX1RYX1JFUVMgNAorCisvKiBNYXhpbXVtIG51bWJl ciBvZiBkZXZpY2VzICovCisjZGVmaW5lIENDSURfTUlOT1JTIDQKKworc3RydWN0IGNjaWRnX2J1 bGtfZGV2IHsKKwlhdG9taWNfdCBpc19vcGVuOworCWF0b21pY190IHJ4X3JlcV9idXN5OworCXdh aXRfcXVldWVfaGVhZF90IHJlYWRfd3E7CisJd2FpdF9xdWV1ZV9oZWFkX3Qgd3JpdGVfd3E7CisJ c3RydWN0IHVzYl9yZXF1ZXN0ICpyeF9yZXE7CisJYXRvbWljX3QgcnhfZG9uZTsKKwlzdHJ1Y3Qg bGlzdF9oZWFkIHR4X2lkbGU7Cit9OworCitzdHJ1Y3QgZl9jY2lkZyB7CisJc3RydWN0IHVzYl9m dW5jdGlvbl9pbnN0YW5jZQlmdW5jX2luc3Q7CisJc3RydWN0IHVzYl9mdW5jdGlvbiBmdW5jdGlv bjsKKwlzcGlubG9ja190IGxvY2s7CisJYXRvbWljX3Qgb25saW5lOworCisJLyogQ2hhcmFjdGVy IGRldmljZSAqLworCXN0cnVjdCBjZGV2IGNkZXY7CisJaW50IG1pbm9yOworCisJLyogRHluYW1p YyBhdHRyaWJ1dGVzICovCisJdTMyIGZlYXR1cmVzOworCXUzMiBwcm90b2NvbHM7CisJdTggcGlu c3VwcG9ydDsKKwl1OCBuc2xvdHM7CisJdTggbGNkbGF5b3V0OworCisJLyogRW5kcG9pbnRzICov CisJc3RydWN0IHVzYl9lcCAqaW47CisJc3RydWN0IHVzYl9lcCAqb3V0OworCXN0cnVjdCBjY2lk Z19idWxrX2RldiBidWxrX2RldjsKK307CisKKy8qIEludGVyZmFjZSBEZXNjcmlwdG9yOiAqLwor c3RhdGljIHN0cnVjdCB1c2JfaW50ZXJmYWNlX2Rlc2NyaXB0b3IgY2NpZF9pbnRlcmZhY2VfZGVz YyA9IHsKKwkuYkxlbmd0aCA9CQlVU0JfRFRfSU5URVJGQUNFX1NJWkUsCisJLmJEZXNjcmlwdG9y VHlwZSA9CVVTQl9EVF9JTlRFUkZBQ0UsCisJLmJOdW1FbmRwb2ludHMgPQkyLAorCS5iSW50ZXJm YWNlQ2xhc3MgPQlVU0JfQ0xBU1NfQ1NDSUQsCisJLmJJbnRlcmZhY2VTdWJDbGFzcyA9CTAsCisJ LmJJbnRlcmZhY2VQcm90b2NvbCA9CTAsCit9OworCisvKiBDQ0lEIENsYXNzIERlc2NyaXB0b3Ig Ki8KK3N0YXRpYyBzdHJ1Y3QgY2NpZF9jbGFzc19kZXNjcmlwdG9yIGNjaWRfY2xhc3NfZGVzYyA9 IHsKKwkuYkxlbmd0aCA9CQlzaXplb2YoY2NpZF9jbGFzc19kZXNjKSwKKwkuYkRlc2NyaXB0b3JU eXBlID0JQ0NJRF9ERUNSSVBUT1JfVFlQRSwKKwkuYmNkQ0NJRCA9CQljcHVfdG9fbGUxNihDQ0lE MV8xMCksCisJLyogLmJNYXhTbG90SW5kZXggPQlEWU5BTUlDICovCisJLmJWb2x0YWdlU3VwcG9y dCA9CUNDSURfVk9MVFNfM18wLAorCS8qIC5kd1Byb3RvY29scyA9CURZTkFNSUMgKi8KKwkuZHdE ZWZhdWx0Q2xvY2sgPQljcHVfdG9fbGUzMigzNTgwKSwKKwkuZHdNYXhpbXVtQ2xvY2sgPQljcHVf dG9fbGUzMigzNTgwKSwKKwkuYk51bUNsb2NrU3VwcG9ydGVkID0JMCwKKwkuZHdEYXRhUmF0ZSA9 CQljcHVfdG9fbGUzMig5NjAwKSwKKwkuZHdNYXhEYXRhUmF0ZSA9CWNwdV90b19sZTMyKDk2MDAp LAorCS5iTnVtRGF0YVJhdGVzU3VwcG9ydGVkID0gMCwKKwkuZHdNYXhJRlNEID0JCTAsCisJLmR3 U3luY2hQcm90b2NvbHMgPQkwLAorCS5kd01lY2hhbmljYWwgPQkJMCwKKwkvKiAuZHdGZWF0dXJl cyA9CURZTkFNSUMgKi8KKworCS8qIGV4dGVuZGVkIEFQRFUgbGV2ZWwgTWVzc2FnZSBMZW5ndGgg Ki8KKwkuZHdNYXhDQ0lETWVzc2FnZUxlbmd0aCA9IGNwdV90b19sZTMyKDB4MjAwKSwKKwkuYkNs YXNzR2V0UmVzcG9uc2UgPQkweDAsCisJLmJDbGFzc0VudmVsb3BlID0JMHgwLAorCS8qIC53TGNk TGF5b3V0ID0JRFlOQU1JQyAqLworCS8qIC5iUElOU3VwcG9ydCA9CURZTkFNSUMgKi8KKwkuYk1h eENDSURCdXN5U2xvdHMgPQkxCit9OworCisvKiBGdWxsIHNwZWVkIHN1cHBvcnQ6ICovCitzdGF0 aWMgc3RydWN0IHVzYl9lbmRwb2ludF9kZXNjcmlwdG9yIGNjaWRfZnNfaW5fZGVzYyA9IHsKKwku Ykxlbmd0aCA9CQlVU0JfRFRfRU5EUE9JTlRfU0laRSwKKwkuYkRlc2NyaXB0b3JUeXBlID0JVVNC X0RUX0VORFBPSU5ULAorCS5iRW5kcG9pbnRBZGRyZXNzID0JVVNCX0RJUl9JTiwKKwkuYm1BdHRy aWJ1dGVzID0JCVVTQl9FTkRQT0lOVF9YRkVSX0JVTEssCisJLndNYXhQYWNrZXRTaXplICAgPQlj cHVfdG9fbGUxNig2NCksCit9OworCitzdGF0aWMgc3RydWN0IHVzYl9lbmRwb2ludF9kZXNjcmlw dG9yIGNjaWRfZnNfb3V0X2Rlc2MgPSB7CisJLmJMZW5ndGggPQkJVVNCX0RUX0VORFBPSU5UX1NJ WkUsCisJLmJEZXNjcmlwdG9yVHlwZSA9CVVTQl9EVF9FTkRQT0lOVCwKKwkuYkVuZHBvaW50QWRk cmVzcyA9CVVTQl9ESVJfT1VULAorCS5ibUF0dHJpYnV0ZXMgPQkJVVNCX0VORFBPSU5UX1hGRVJf QlVMSywKKwkud01heFBhY2tldFNpemUgICA9CSBjcHVfdG9fbGUxNig2NCksCit9OworCitzdGF0 aWMgc3RydWN0IHVzYl9kZXNjcmlwdG9yX2hlYWRlciAqY2NpZF9mc19kZXNjc1tdID0geworCShz dHJ1Y3QgdXNiX2Rlc2NyaXB0b3JfaGVhZGVyICopICZjY2lkX2ludGVyZmFjZV9kZXNjLAorCShz dHJ1Y3QgdXNiX2Rlc2NyaXB0b3JfaGVhZGVyICopICZjY2lkX2NsYXNzX2Rlc2MsCisJKHN0cnVj dCB1c2JfZGVzY3JpcHRvcl9oZWFkZXIgKikgJmNjaWRfZnNfaW5fZGVzYywKKwkoc3RydWN0IHVz Yl9kZXNjcmlwdG9yX2hlYWRlciAqKSAmY2NpZF9mc19vdXRfZGVzYywKKwlOVUxMLAorfTsKKwor LyogSGlnaCBzcGVlZCBzdXBwb3J0OiAqLworc3RhdGljIHN0cnVjdCB1c2JfZW5kcG9pbnRfZGVz Y3JpcHRvciBjY2lkX2hzX2luX2Rlc2MgPSB7CisJLmJMZW5ndGggPQkJVVNCX0RUX0VORFBPSU5U X1NJWkUsCisJLmJEZXNjcmlwdG9yVHlwZSA9CVVTQl9EVF9FTkRQT0lOVCwKKwkuYkVuZHBvaW50 QWRkcmVzcyA9CVVTQl9ESVJfSU4sCisJLmJtQXR0cmlidXRlcyA9CQlVU0JfRU5EUE9JTlRfWEZF Ul9CVUxLLAorCS53TWF4UGFja2V0U2l6ZSA9CWNwdV90b19sZTE2KDUxMiksCit9OworCitzdGF0 aWMgc3RydWN0IHVzYl9lbmRwb2ludF9kZXNjcmlwdG9yIGNjaWRfaHNfb3V0X2Rlc2MgPSB7CisJ LmJMZW5ndGggPQkJVVNCX0RUX0VORFBPSU5UX1NJWkUsCisJLmJEZXNjcmlwdG9yVHlwZSA9CVVT Ql9EVF9FTkRQT0lOVCwKKwkuYkVuZHBvaW50QWRkcmVzcyA9CVVTQl9ESVJfT1VULAorCS5ibUF0 dHJpYnV0ZXMgPQkJVVNCX0VORFBPSU5UX1hGRVJfQlVMSywKKwkud01heFBhY2tldFNpemUgPQlj cHVfdG9fbGUxNig1MTIpLAorfTsKKworc3RhdGljIHN0cnVjdCB1c2JfZGVzY3JpcHRvcl9oZWFk ZXIgKmNjaWRfaHNfZGVzY3NbXSA9IHsKKwkoc3RydWN0IHVzYl9kZXNjcmlwdG9yX2hlYWRlciAq KSAmY2NpZF9pbnRlcmZhY2VfZGVzYywKKwkoc3RydWN0IHVzYl9kZXNjcmlwdG9yX2hlYWRlciAq KSAmY2NpZF9jbGFzc19kZXNjLAorCShzdHJ1Y3QgdXNiX2Rlc2NyaXB0b3JfaGVhZGVyICopICZj Y2lkX2hzX2luX2Rlc2MsCisJKHN0cnVjdCB1c2JfZGVzY3JpcHRvcl9oZWFkZXIgKikgJmNjaWRf aHNfb3V0X2Rlc2MsCisJTlVMTCwKK307CisKK3N0YXRpYyBERUZJTkVfSURBKGNjaWRnX2lkYSk7 CitzdGF0aWMgaW50IG1ham9yOworc3RhdGljIERFRklORV9NVVRFWChjY2lkZ19pZGFfbG9jayk7 IC8qIHByb3RlY3RzIGFjY2VzcyB0byBjY2lkZ19pZGEgKi8KK3N0YXRpYyBzdHJ1Y3QgY2xhc3Mg KmNjaWRnX2NsYXNzOworCitzdGF0aWMgaW5saW5lIHN0cnVjdCBmX2NjaWRnX29wdHMgKnRvX2Zf Y2NpZGdfb3B0cyhzdHJ1Y3QgY29uZmlnX2l0ZW0gKml0ZW0pCit7CisJcmV0dXJuIGNvbnRhaW5l cl9vZih0b19jb25maWdfZ3JvdXAoaXRlbSksIHN0cnVjdCBmX2NjaWRnX29wdHMsCisJCQkgICAg ZnVuY19pbnN0Lmdyb3VwKTsKK30KKworc3RhdGljIGlubGluZSBzdHJ1Y3QgZl9jY2lkZyAqZnVu Y190b19jY2lkZyhzdHJ1Y3QgdXNiX2Z1bmN0aW9uICpmKQoreworCXJldHVybiBjb250YWluZXJf b2YoZiwgc3RydWN0IGZfY2NpZGcsIGZ1bmN0aW9uKTsKK30KKworc3RhdGljIGlubGluZSBpbnQg Y2NpZGdfZ2V0X21pbm9yKHZvaWQpCit7CisJaW50IHJldDsKKworCXJldCA9IGlkYV9zaW1wbGVf Z2V0KCZjY2lkZ19pZGEsIDAsIDAsIEdGUF9LRVJORUwpOworCWlmIChyZXQgPj0gQ0NJRF9NSU5P UlMpIHsKKwkJaWRhX3NpbXBsZV9yZW1vdmUoJmNjaWRnX2lkYSwgcmV0KTsKKwkJcmV0ID0gLUVO T0RFVjsKKwl9CisKKwlyZXR1cm4gcmV0OworfQorCitzdGF0aWMgaW5saW5lIHZvaWQgY2NpZGdf cHV0X21pbm9yKGludCBtaW5vcikKK3sKKwlpZGFfc2ltcGxlX3JlbW92ZSgmY2NpZGdfaWRhLCBt aW5vcik7Cit9CisKK3N0YXRpYyBpbnQgY2NpZGdfc2V0dXAodm9pZCkKK3sKKwlpbnQgcmV0Owor CWRldl90IGRldjsKKworCWNjaWRnX2NsYXNzID0gY2xhc3NfY3JlYXRlKFRISVNfTU9EVUxFLCAi Y2NpZGciKTsKKwlpZiAoSVNfRVJSKGNjaWRnX2NsYXNzKSkgeworCQljY2lkZ19jbGFzcyA9IE5V TEw7CisJCXJldHVybiBQVFJfRVJSKGNjaWRnX2NsYXNzKTsKKwl9CisKKwlyZXQgPSBhbGxvY19j aHJkZXZfcmVnaW9uKCZkZXYsIDAsIENDSURfTUlOT1JTLCAiY2NpZGciKTsKKwlpZiAocmV0KSB7 CisJCWNsYXNzX2Rlc3Ryb3koY2NpZGdfY2xhc3MpOworCQljY2lkZ19jbGFzcyA9IE5VTEw7CisJ CXJldHVybiByZXQ7CisJfQorCisJbWFqb3IgPSBNQUpPUihkZXYpOworCisJcmV0dXJuIDA7Cit9 CisKK3N0YXRpYyB2b2lkIGNjaWRnX2NsZWFudXAodm9pZCkKK3sKKwlpZiAobWFqb3IpIHsKKwkJ dW5yZWdpc3Rlcl9jaHJkZXZfcmVnaW9uKE1LREVWKG1ham9yLCAwKSwgQ0NJRF9NSU5PUlMpOwor CQltYWpvciA9IDA7CisJfQorCisJY2xhc3NfZGVzdHJveShjY2lkZ19jbGFzcyk7CisJY2NpZGdf Y2xhc3MgPSBOVUxMOworfQorCitzdGF0aWMgdm9pZCBjY2lkZ19hdHRyX3JlbGVhc2Uoc3RydWN0 IGNvbmZpZ19pdGVtICppdGVtKQoreworCXN0cnVjdCBmX2NjaWRnX29wdHMgKm9wdHMgPSB0b19m X2NjaWRnX29wdHMoaXRlbSk7CisKKwl1c2JfcHV0X2Z1bmN0aW9uX2luc3RhbmNlKCZvcHRzLT5m dW5jX2luc3QpOworfQorCitzdGF0aWMgc3RydWN0IGNvbmZpZ2ZzX2l0ZW1fb3BlcmF0aW9ucyBj Y2lkZ19pdGVtX29wcyA9IHsKKwkucmVsZWFzZQk9IGNjaWRnX2F0dHJfcmVsZWFzZSwKK307CisK KyNkZWZpbmUgRl9DQ0lER19PUFQobmFtZSwgcHJlYywgbGltaXQpCQkJCQlcCitzdGF0aWMgc3Np emVfdCBmX2NjaWRnX29wdHNfIyNuYW1lIyNfc2hvdyhzdHJ1Y3QgY29uZmlnX2l0ZW0gKml0ZW0s IGNoYXIgKnBhZ2UpXAorewkJCQkJCQkJCVwKKwlzdHJ1Y3QgZl9jY2lkZ19vcHRzICpvcHRzID0g dG9fZl9jY2lkZ19vcHRzKGl0ZW0pOwkJXAorCWludCByZXN1bHQ7CQkJCQkJCVwKKwkJCQkJCQkJ CVwKKwltdXRleF9sb2NrKCZvcHRzLT5sb2NrKTsJCQkJCVwKKwlyZXN1bHQgPSBzcHJpbnRmKHBh Z2UsICIleFxuIiwgb3B0cy0+bmFtZSk7CQkJXAorCW11dGV4X3VubG9jaygmb3B0cy0+bG9jayk7 CQkJCQlcCisJCQkJCQkJCQlcCisJcmV0dXJuIHJlc3VsdDsJCQkJCQkJXAorfQkJCQkJCQkJCVwK KwkJCQkJCQkJCVwKK3N0YXRpYyBzc2l6ZV90IGZfY2NpZGdfb3B0c18jI25hbWUjI19zdG9yZShz dHJ1Y3QgY29uZmlnX2l0ZW0gKml0ZW0sCVwKKwkJCQkJIGNvbnN0IGNoYXIgKnBhZ2UsIHNpemVf dCBsZW4pCVwKK3sJCQkJCQkJCQlcCisJc3RydWN0IGZfY2NpZGdfb3B0cyAqb3B0cyA9IHRvX2Zf Y2NpZGdfb3B0cyhpdGVtKTsJCVwKKwlpbnQgcmV0OwkJCQkJCQlcCisJdSMjcHJlYyBudW07CQkJ CQkJCVwKKwkJCQkJCQkJCVwKKwltdXRleF9sb2NrKCZvcHRzLT5sb2NrKTsJCQkJCVwKKwlpZiAo b3B0cy0+cmVmY250KSB7CQkJCQkJXAorCQlyZXQgPSAtRUJVU1k7CQkJCQkJXAorCQlnb3RvIGVu ZDsJCQkJCQlcCisJfQkJCQkJCQkJXAorCQkJCQkJCQkJXAorCXJldCA9IGtzdHJ0b3UjI3ByZWMo cGFnZSwgMCwgJm51bSk7CQkJCVwKKwlpZiAocmV0KQkJCQkJCQlcCisJCWdvdG8gZW5kOwkJCQkJ CVwKKwkJCQkJCQkJCVwKKwlpZiAobnVtID4gbGltaXQpIHsJCQkJCQlcCisJCXJldCA9IC1FSU5W QUw7CQkJCQkJXAorCQlnb3RvIGVuZDsJCQkJCQlcCisJfQkJCQkJCQkJXAorCW9wdHMtPm5hbWUg PSBudW07CQkJCQkJXAorCXJldCA9IGxlbjsJCQkJCQkJXAorCQkJCQkJCQkJXAorZW5kOgkJCQkJ CQkJCVwKKwltdXRleF91bmxvY2soJm9wdHMtPmxvY2spOwkJCQkJXAorCXJldHVybiByZXQ7CQkJ CQkJCVwKK30JCQkJCQkJCQlcCisJCQkJCQkJCQlcCitDT05GSUdGU19BVFRSKGZfY2NpZGdfb3B0 c18sIG5hbWUpCisKK0ZfQ0NJREdfT1BUKGZlYXR1cmVzLCAzMiwgMHhmZmZmZmZmZik7CitGX0ND SURHX09QVChwcm90b2NvbHMsIDMyLCAweDAzKTsKK0ZfQ0NJREdfT1BUKHBpbnN1cHBvcnQsIDgs IDB4MDMpOworRl9DQ0lER19PUFQobGNkbGF5b3V0LCAxNiwgMHhmZmZmKTsKK0ZfQ0NJREdfT1BU KG5zbG90cywgOCwgMHhmZik7CisKK3N0YXRpYyBzdHJ1Y3QgY29uZmlnZnNfYXR0cmlidXRlICpj Y2lkZ19hdHRyc1tdID0geworCSZmX2NjaWRnX29wdHNfYXR0cl9mZWF0dXJlcywKKwkmZl9jY2lk Z19vcHRzX2F0dHJfcHJvdG9jb2xzLAorCSZmX2NjaWRnX29wdHNfYXR0cl9waW5zdXBwb3J0LAor CSZmX2NjaWRnX29wdHNfYXR0cl9sY2RsYXlvdXQsCisJJmZfY2NpZGdfb3B0c19hdHRyX25zbG90 cywKKwlOVUxMLAorfTsKKworc3RhdGljIHN0cnVjdCBjb25maWdfaXRlbV90eXBlIGNjaWRnX2Z1 bmNfdHlwZSA9IHsKKwkuY3RfaXRlbV9vcHMJPSAmY2NpZGdfaXRlbV9vcHMsCisJLmN0X2F0dHJz CT0gY2NpZGdfYXR0cnMsCisJLmN0X293bmVyCT0gVEhJU19NT0RVTEUsCit9OworCitzdGF0aWMg dm9pZCBjY2lkZ19yZXFfcHV0KHN0cnVjdCBmX2NjaWRnICpjY2lkZywgc3RydWN0IGxpc3RfaGVh ZCAqaGVhZCwKKwkJc3RydWN0IHVzYl9yZXF1ZXN0ICpyZXEpCit7CisJdW5zaWduZWQgbG9uZyBm bGFnczsKKworCXNwaW5fbG9ja19pcnFzYXZlKCZjY2lkZy0+bG9jaywgZmxhZ3MpOworCWxpc3Rf YWRkX3RhaWwoJnJlcS0+bGlzdCwgaGVhZCk7CisJc3Bpbl91bmxvY2tfaXJxcmVzdG9yZSgmY2Np ZGctPmxvY2ssIGZsYWdzKTsKK30KKworc3RhdGljIHN0cnVjdCB1c2JfcmVxdWVzdCAqY2NpZGdf cmVxX2dldChzdHJ1Y3QgZl9jY2lkZyAqY2NpZGcsCisJCQkJCXN0cnVjdCBsaXN0X2hlYWQgKmhl YWQpCit7CisJdW5zaWduZWQgbG9uZyBmbGFnczsKKwlzdHJ1Y3QgdXNiX3JlcXVlc3QgKnJlcSA9 IE5VTEw7CisKKwlzcGluX2xvY2tfaXJxc2F2ZSgmY2NpZGctPmxvY2ssIGZsYWdzKTsKKwlpZiAo IWxpc3RfZW1wdHkoaGVhZCkpIHsKKwkJcmVxID0gbGlzdF9maXJzdF9lbnRyeShoZWFkLCBzdHJ1 Y3QgdXNiX3JlcXVlc3QsIGxpc3QpOworCQlsaXN0X2RlbCgmcmVxLT5saXN0KTsKKwl9CisJc3Bp bl91bmxvY2tfaXJxcmVzdG9yZSgmY2NpZGctPmxvY2ssIGZsYWdzKTsKKworCXJldHVybiByZXE7 Cit9CisKK3N0YXRpYyB2b2lkIGNjaWRnX2J1bGtfY29tcGxldGVfdHgoc3RydWN0IHVzYl9lcCAq ZXAsIHN0cnVjdCB1c2JfcmVxdWVzdCAqcmVxKQoreworCXN0cnVjdCBmX2NjaWRnICpjY2lkZyA9 IChzdHJ1Y3QgZl9jY2lkZyAqKWVwLT5kcml2ZXJfZGF0YTsKKwlzdHJ1Y3QgY2NpZGdfYnVsa19k ZXYgKmJ1bGtfZGV2ID0gJmNjaWRnLT5idWxrX2RldjsKKwlzdHJ1Y3QgdXNiX2NvbXBvc2l0ZV9k ZXYgKmNkZXYJPSBjY2lkZy0+ZnVuY3Rpb24uY29uZmlnLT5jZGV2OworCisJc3dpdGNoIChyZXEt PnN0YXR1cykgeworCWRlZmF1bHQ6CisJCVZEQkcoY2RldiwgImNjaWQ6IHR4IGVyciAlZFxuIiwg cmVxLT5zdGF0dXMpOworCQkvKiBGQUxMVEhST1VHSCAqLworCWNhc2UgLUVDT05OUkVTRVQ6CQkv KiB1bmxpbmsgKi8KKwljYXNlIC1FU0hVVERPV046CQkvKiBkaXNjb25uZWN0IGV0YyAqLworCQli cmVhazsKKwljYXNlIDA6CisJCWJyZWFrOworCX0KKworCWNjaWRnX3JlcV9wdXQoY2NpZGcsICZi dWxrX2Rldi0+dHhfaWRsZSwgcmVxKTsKKwl3YWtlX3VwKCZidWxrX2Rldi0+d3JpdGVfd3EpOwor fQorCitzdGF0aWMgdm9pZCBjY2lkZ19idWxrX2NvbXBsZXRlX3J4KHN0cnVjdCB1c2JfZXAgKmVw LCBzdHJ1Y3QgdXNiX3JlcXVlc3QgKnJlcSkKK3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2NpZGcgPSAo c3RydWN0IGZfY2NpZGcgKillcC0+ZHJpdmVyX2RhdGE7CisJc3RydWN0IGNjaWRnX2J1bGtfZGV2 ICpidWxrX2RldiA9ICZjY2lkZy0+YnVsa19kZXY7CisJc3RydWN0IHVzYl9jb21wb3NpdGVfZGV2 ICpjZGV2CT0gY2NpZGctPmZ1bmN0aW9uLmNvbmZpZy0+Y2RldjsKKworCXN3aXRjaCAocmVxLT5z dGF0dXMpIHsKKworCS8qIG5vcm1hbCBjb21wbGV0aW9uICovCisJY2FzZSAwOgorCQkvKiBXZSBv bmx5IGNhcmVzIGFib3V0IHBhY2tldHMgd2l0aCBub256ZXJvIGxlbmd0aCAqLworCQlpZiAocmVx LT5hY3R1YWwgPiAwKQorCQkJYXRvbWljX3NldCgmYnVsa19kZXYtPnJ4X2RvbmUsIDEpOworCQli cmVhazsKKworCS8qIHNvZnR3YXJlLWRyaXZlbiBpbnRlcmZhY2Ugc2h1dGRvd24gKi8KKwljYXNl IC1FQ09OTlJFU0VUOgkJLyogdW5saW5rICovCisJY2FzZSAtRVNIVVRET1dOOgkJLyogZGlzY29u bmVjdCBldGMgKi8KKwkJVkRCRyhjZGV2LCAiY2NpZDogcnggc2h1dGRvd24sIGNvZGUgJWRcbiIs IHJlcS0+c3RhdHVzKTsKKwkJYnJlYWs7CisKKwkvKiBmb3IgaGFyZHdhcmUgYXV0b21hZ2ljIChz dWNoIGFzIHB4YSkgKi8KKwljYXNlIC1FQ09OTkFCT1JURUQ6CQkvKiBlbmRwb2ludCByZXNldCAq LworCQlEQkcoY2RldiwgImNjaWQ6IHJ4ICVzIHJlc2V0XG4iLCBlcC0+bmFtZSk7CisJCWJyZWFr OworCisJLyogZGF0YSBvdmVycnVuICovCisJY2FzZSAtRU9WRVJGTE9XOgorCQkvKiBGQUxMVEhS T1VHSCAqLworCWRlZmF1bHQ6CisJCURCRyhjZGV2LCAiY2NpZDogcnggc3RhdHVzICVkXG4iLCBy ZXEtPnN0YXR1cyk7CisJCWJyZWFrOworCX0KKworCXdha2VfdXAoJmJ1bGtfZGV2LT5yZWFkX3dx KTsKK30KKworc3RhdGljIHN0cnVjdCB1c2JfcmVxdWVzdCAqCitjY2lkZ19yZXF1ZXN0X2FsbG9j KHN0cnVjdCB1c2JfZXAgKmVwLCB1bnNpZ25lZCBpbnQgbGVuKQoreworCXN0cnVjdCB1c2JfcmVx dWVzdCAqcmVxOworCisJcmVxID0gdXNiX2VwX2FsbG9jX3JlcXVlc3QoZXAsIEdGUF9BVE9NSUMp OworCWlmICghcmVxKQorCQlyZXR1cm4gRVJSX1BUUigtRU5PTUVNKTsKKworCXJlcS0+bGVuZ3Ro ID0gbGVuOworCXJlcS0+YnVmID0ga21hbGxvYyhsZW4sIEdGUF9BVE9NSUMpOworCWlmIChyZXEt PmJ1ZiA9PSBOVUxMKSB7CisJCXVzYl9lcF9mcmVlX3JlcXVlc3QoZXAsIHJlcSk7CisJCXJldHVy biBFUlJfUFRSKC1FTk9NRU0pOworCX0KKworCXJldHVybiByZXE7Cit9CisKK3N0YXRpYyB2b2lk IGNjaWRnX3JlcXVlc3RfZnJlZShzdHJ1Y3QgdXNiX3JlcXVlc3QgKnJlcSwgc3RydWN0IHVzYl9l cCAqZXApCit7CisJaWYgKHJlcSkgeworCQlrZnJlZShyZXEtPmJ1Zik7CisJCXVzYl9lcF9mcmVl X3JlcXVlc3QoZXAsIHJlcSk7CisJfQorfQorCitzdGF0aWMgaW50IGNjaWRnX2Z1bmN0aW9uX3Nl dHVwKHN0cnVjdCB1c2JfZnVuY3Rpb24gKmYsCisJCWNvbnN0IHN0cnVjdCB1c2JfY3RybHJlcXVl c3QgKmN0cmwpCit7CisJc3RydWN0IGZfY2NpZGcgKmNjaWRnID0gY29udGFpbmVyX29mKGYsIHN0 cnVjdCBmX2NjaWRnLCBmdW5jdGlvbik7CisJc3RydWN0IHVzYl9jb21wb3NpdGVfZGV2ICpjZGV2 CT0gZi0+Y29uZmlnLT5jZGV2OworCXN0cnVjdCB1c2JfcmVxdWVzdCAqcmVxCQk9IGNkZXYtPnJl cTsKKwlpbnQgcmV0CQkJCT0gLUVPUE5PVFNVUFA7CisJdTE2IHdfaW5kZXgJCQk9IGxlMTZfdG9f Y3B1KGN0cmwtPndJbmRleCk7CisJdTE2IHdfdmFsdWUJCQk9IGxlMTZfdG9fY3B1KGN0cmwtPndW YWx1ZSk7CisJdTE2IHdfbGVuZ3RoCQkJPSBsZTE2X3RvX2NwdShjdHJsLT53TGVuZ3RoKTsKKwor CWlmICghYXRvbWljX3JlYWQoJmNjaWRnLT5vbmxpbmUpKQorCQlyZXR1cm4gLUVOT1RDT05OOwor CisJc3dpdGNoIChjdHJsLT5iUmVxdWVzdFR5cGUgJiBVU0JfVFlQRV9NQVNLKSB7CisJY2FzZSBV U0JfVFlQRV9DTEFTUzoKKwkJeworCQlzd2l0Y2ggKGN0cmwtPmJSZXF1ZXN0KSB7CisJCWNhc2Ug Q0NJREdFTkVSSUNSRVFfR0VUX0NMT0NLX0ZSRVFVRU5DSUVTOgorCQkJaWYgKHdfbGVuZ3RoID4g c2l6ZW9mKGNjaWRfY2xhc3NfZGVzYy5kd0RlZmF1bHRDbG9jaykpCisJCQkJYnJlYWs7CisKKwkJ CSooX19sZTMyICopIHJlcS0+YnVmID0gY2NpZF9jbGFzc19kZXNjLmR3RGVmYXVsdENsb2NrOwor CQkJcmV0ID0gc2l6ZW9mKGNjaWRfY2xhc3NfZGVzYy5kd0RlZmF1bHRDbG9jayk7CisJCQlicmVh azsKKworCQljYXNlIENDSURHRU5FUklDUkVRX0dFVF9EQVRBX1JBVEVTOgorCQkJaWYgKHdfbGVu Z3RoID4gc2l6ZW9mKGNjaWRfY2xhc3NfZGVzYy5kd0RhdGFSYXRlKSkKKwkJCQlicmVhazsKKwor CQkJKihfX2xlMzIgKikgcmVxLT5idWYgPSBjY2lkX2NsYXNzX2Rlc2MuZHdEYXRhUmF0ZTsKKwkJ CXJldCA9IHNpemVvZihjY2lkX2NsYXNzX2Rlc2MuZHdEYXRhUmF0ZSk7CisJCQlicmVhazsKKwor CQlkZWZhdWx0OgorCQkJVkRCRyhmLT5jb25maWctPmNkZXYsCisJCQkJImNjaWQ6IGludmFsaWQg Y29udHJvbCByZXElMDJ4LiUwMnggdiUwNHggaSUwNHggbCVkXG4iLAorCQkJCWN0cmwtPmJSZXF1 ZXN0VHlwZSwgY3RybC0+YlJlcXVlc3QsCisJCQkJd192YWx1ZSwgd19pbmRleCwgd19sZW5ndGgp OworCQl9CisJCX0KKwl9CisKKwkvKiByZXNwb25kZWQgd2l0aCBkYXRhIHRyYW5zZmVyIG9yIHN0 YXR1cyBwaGFzZT8gKi8KKwlpZiAocmV0ID49IDApIHsKKwkJVkRCRyhmLT5jb25maWctPmNkZXYs ICJjY2lkOiByZXElMDJ4LiUwMnggdiUwNHggaSUwNHggbCVkXG4iLAorCQkJY3RybC0+YlJlcXVl c3RUeXBlLCBjdHJsLT5iUmVxdWVzdCwKKwkJCXdfdmFsdWUsIHdfaW5kZXgsIHdfbGVuZ3RoKTsK KworCQlyZXEtPmxlbmd0aCA9IHJldDsKKwkJcmV0ID0gdXNiX2VwX3F1ZXVlKGNkZXYtPmdhZGdl dC0+ZXAwLCByZXEsIEdGUF9BVE9NSUMpOworCQlpZiAocmV0IDwgMCkKKwkJCUVSUk9SKGYtPmNv bmZpZy0+Y2RldiwKKwkJCQkiY2NpZDogZXAwIGVucXVldWUgZXJyICVkXG4iLCByZXQpOworCX0K KworCXJldHVybiByZXQ7Cit9CisKK3N0YXRpYyB2b2lkIGNjaWRnX2Z1bmN0aW9uX2Rpc2FibGUo c3RydWN0IHVzYl9mdW5jdGlvbiAqZikKK3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2NpZGcgPSBmdW5j X3RvX2NjaWRnKGYpOworCXN0cnVjdCBjY2lkZ19idWxrX2RldiAqYnVsa19kZXYgPSAmY2NpZGct PmJ1bGtfZGV2OworCXN0cnVjdCB1c2JfcmVxdWVzdCAqcmVxOworCisJLyogRGlzYWJsZSBlbmRw b2ludHMgKi8KKwl1c2JfZXBfZGlzYWJsZShjY2lkZy0+aW4pOworCXVzYl9lcF9kaXNhYmxlKGNj aWRnLT5vdXQpOworCisJLyogRnJlZSBlbmRwb2ludCByZWxhdGVkIHJlcXVlc3RzICovCisJaWYg KCFhdG9taWNfcmVhZCgmYnVsa19kZXYtPnJ4X3JlcV9idXN5KSkKKwkJY2NpZGdfcmVxdWVzdF9m cmVlKGJ1bGtfZGV2LT5yeF9yZXEsIGNjaWRnLT5vdXQpOworCXdoaWxlICgocmVxID0gY2NpZGdf cmVxX2dldChjY2lkZywgJmJ1bGtfZGV2LT50eF9pZGxlKSkpCisJCWNjaWRnX3JlcXVlc3RfZnJl ZShyZXEsIGNjaWRnLT5pbik7CisKKwlhdG9taWNfc2V0KCZjY2lkZy0+b25saW5lLCAwKTsKKwor CS8qIFdha2UgdXAgdGhyZWFkcyAqLworCXdha2VfdXAoJmJ1bGtfZGV2LT53cml0ZV93cSk7CisJ d2FrZV91cCgmYnVsa19kZXYtPnJlYWRfd3EpOworfQorCitzdGF0aWMgaW50IGNjaWRnX3N0YXJ0 X2VwKHN0cnVjdCBmX2NjaWRnICpjY2lkZywgc3RydWN0IHVzYl9mdW5jdGlvbiAqZiwKKwkJCXN0 cnVjdCB1c2JfZXAgKmVwKQoreworCXN0cnVjdCB1c2JfY29tcG9zaXRlX2RldiAqY2RldiA9IGYt PmNvbmZpZy0+Y2RldjsKKwlpbnQgcmV0OworCisJdXNiX2VwX2Rpc2FibGUoZXApOworCisJcmV0 ID0gY29uZmlnX2VwX2J5X3NwZWVkKGNkZXYtPmdhZGdldCwgZiwgZXApOworCWlmIChyZXQpIHsK KwkJRVJST1IoY2RldiwgImNjaWQ6IGNhbid0IGNvbmZpZ3VyZSAlczogJWRcbiIsIGVwLT5uYW1l LCByZXQpOworCQlyZXR1cm4gcmV0OworCX0KKworCXJldCA9IHVzYl9lcF9lbmFibGUoZXApOwor CWlmIChyZXQpIHsKKwkJRVJST1IoY2RldiwgImNjaWQ6IGNhbid0IHN0YXJ0ICVzOiAlZFxuIiwg ZXAtPm5hbWUsIHJldCk7CisJCXJldHVybiByZXQ7CisJfQorCisJZXAtPmRyaXZlcl9kYXRhID0g Y2NpZGc7CisKKwlyZXR1cm4gcmV0OworfQorCitzdGF0aWMgaW50IGNjaWRnX2Z1bmN0aW9uX3Nl dF9hbHQoc3RydWN0IHVzYl9mdW5jdGlvbiAqZiwKKwkJdW5zaWduZWQgaW50IGludGYsIHVuc2ln bmVkIGludCBhbHQpCit7CisJc3RydWN0IGZfY2NpZGcgKmNjaWRnCQk9IGZ1bmNfdG9fY2NpZGco Zik7CisJc3RydWN0IHVzYl9jb21wb3NpdGVfZGV2ICpjZGV2CT0gZi0+Y29uZmlnLT5jZGV2Owor CXN0cnVjdCBjY2lkZ19idWxrX2RldiAqYnVsa19kZXYJPSAmY2NpZGctPmJ1bGtfZGV2OworCXN0 cnVjdCB1c2JfcmVxdWVzdCAqcmVxOworCWludCByZXQ7CisJaW50IGk7CisKKwkvKiBBbGxvY2F0 ZSByZXF1ZXN0cyBmb3Igb3VyIGVuZHBvaW50cyAqLworCXJlcSA9IGNjaWRnX3JlcXVlc3RfYWxs b2MoY2NpZGctPm91dCwKKwkJCXNpemVvZihzdHJ1Y3QgY2NpZGdfYnVsa19vdXRfaGVhZGVyKSk7 CisJaWYgKElTX0VSUihyZXEpKSB7CisJCUVSUk9SKGNkZXYsICJjY2lkOiB1bmFtZSB0byBhbGxv Y2F0ZSBtZW1vcnkgZm9yIG91dCByZXFcbiIpOworCQlyZXR1cm4gUFRSX0VSUihyZXEpOworCX0K KwlyZXEtPmNvbXBsZXRlID0gY2NpZGdfYnVsa19jb21wbGV0ZV9yeDsKKwlyZXEtPmNvbnRleHQg PSBjY2lkZzsKKwlidWxrX2Rldi0+cnhfcmVxID0gcmVxOworCisJLyogQWxsb2NhdGUgYnVuY2gg b2YgaW4gcmVxdWVzdHMgKi8KKwlmb3IgKGkgPSAwOyBpIDwgTl9UWF9SRVFTOyBpKyspIHsKKwkJ cmVxID0gY2NpZGdfcmVxdWVzdF9hbGxvYyhjY2lkZy0+aW4sCisJCQkJc2l6ZW9mKHN0cnVjdCBj Y2lkZ19idWxrX2luX2hlYWRlcikpOworCisJCWlmIChJU19FUlIocmVxKSkgeworCQkJcmV0ID0g UFRSX0VSUihyZXEpOworCQkJRVJST1IoY2RldiwKKwkJCQkiY2NpZDogdW5hbWUgdG8gYWxsb2Nh dGUgbWVtb3J5IGZvciBpbiByZXFcbiIpOworCQkJZ290byBmcmVlX2J1bGtfb3V0OworCQl9CisJ CXJlcS0+Y29tcGxldGUgPSBjY2lkZ19idWxrX2NvbXBsZXRlX3R4OworCQlyZXEtPmNvbnRleHQg PSBjY2lkZzsKKwkJY2NpZGdfcmVxX3B1dChjY2lkZywgJmJ1bGtfZGV2LT50eF9pZGxlLCByZXEp OworCX0KKworCS8qIGNob29zZSB0aGUgZGVzY3JpcHRvcnMgYW5kIGVuYWJsZSBlbmRwb2ludHMg Ki8KKwlyZXQgPSBjY2lkZ19zdGFydF9lcChjY2lkZywgZiwgY2NpZGctPmluKTsKKwlpZiAocmV0 KQorCQlnb3RvIGZyZWVfYnVsa19pbjsKKworCXJldCA9IGNjaWRnX3N0YXJ0X2VwKGNjaWRnLCBm LCBjY2lkZy0+b3V0KTsKKwlpZiAocmV0KQorCQlnb3RvIGRpc2FibGVfZXBfaW47CisKKwlhdG9t aWNfc2V0KCZjY2lkZy0+b25saW5lLCAxKTsKKwlyZXR1cm4gcmV0OworCitkaXNhYmxlX2VwX2lu OgorCXVzYl9lcF9kaXNhYmxlKGNjaWRnLT5pbik7CitmcmVlX2J1bGtfaW46CisJd2hpbGUgKChy ZXEgPSBjY2lkZ19yZXFfZ2V0KGNjaWRnLCAmYnVsa19kZXYtPnR4X2lkbGUpKSkKKwkJY2NpZGdf cmVxdWVzdF9mcmVlKHJlcSwgY2NpZGctPmluKTsKK2ZyZWVfYnVsa19vdXQ6CisJY2NpZGdfcmVx dWVzdF9mcmVlKGJ1bGtfZGV2LT5yeF9yZXEsIGNjaWRnLT5vdXQpOworCXJldHVybiByZXQ7Cit9 CisKK3N0YXRpYyBpbnQgY2NpZGdfYnVsa19vcGVuKHN0cnVjdCBpbm9kZSAqaW5vZGUsIHN0cnVj dCBmaWxlICpmaWxlKQoreworCXN0cnVjdCBmX2NjaWRnICpjY2lkZzsKKwlzdHJ1Y3QgY2NpZGdf YnVsa19kZXYgKmJ1bGtfZGV2OworCisJY2NpZGcgPSBjb250YWluZXJfb2YoaW5vZGUtPmlfY2Rl diwgc3RydWN0IGZfY2NpZGcsIGNkZXYpOworCWJ1bGtfZGV2ID0gJmNjaWRnLT5idWxrX2RldjsK KworCWlmICghYXRvbWljX3JlYWQoJmNjaWRnLT5vbmxpbmUpKSB7CisJCURCRyhjY2lkZy0+ZnVu Y3Rpb24uY29uZmlnLT5jZGV2LCAiY2NpZDogZGV2aWNlIG5vdCBvbmxpbmVcbiIpOworCQlyZXR1 cm4gLUVOT0RFVjsKKwl9CisKKwlpZiAoYXRvbWljX3JlYWQoJmJ1bGtfZGV2LT5pc19vcGVuKSkg eworCQlEQkcoY2NpZGctPmZ1bmN0aW9uLmNvbmZpZy0+Y2RldiwKKwkJCQkiY2NpZDogZGV2aWNl IGFscmVhZHkgb3BlbmVkXG4iKTsKKwkJcmV0dXJuIC1FQlVTWTsKKwl9CisKKwlhdG9taWNfc2V0 KCZidWxrX2Rldi0+aXNfb3BlbiwgMSk7CisKKwlmaWxlLT5wcml2YXRlX2RhdGEgPSBjY2lkZzsK KworCXJldHVybiAwOworfQorCitzdGF0aWMgaW50IGNjaWRnX2J1bGtfcmVsZWFzZShzdHJ1Y3Qg aW5vZGUgKmlub2RlLCBzdHJ1Y3QgZmlsZSAqZmlsZSkKK3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2Np ZGcgPSAgZmlsZS0+cHJpdmF0ZV9kYXRhOworCXN0cnVjdCBjY2lkZ19idWxrX2RldiAqYnVsa19k ZXYgPSAmY2NpZGctPmJ1bGtfZGV2OworCisJYXRvbWljX3NldCgmYnVsa19kZXYtPmlzX29wZW4s IDApOworCXJldHVybiAwOworfQorCitzdGF0aWMgc3NpemVfdCBjY2lkZ19idWxrX3JlYWQoc3Ry dWN0IGZpbGUgKmZpbGUsIGNoYXIgX191c2VyICpidWYsCisJCQkJc2l6ZV90IGNvdW50LCBsb2Zm X3QgKnBvcykKK3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2NpZGcgPSAgZmlsZS0+cHJpdmF0ZV9kYXRh OworCXN0cnVjdCBjY2lkZ19idWxrX2RldiAqYnVsa19kZXYgPSAmY2NpZGctPmJ1bGtfZGV2Owor CXN0cnVjdCB1c2JfcmVxdWVzdCAqcmVxOworCWludCByID0gY291bnQsIHhmZXI7CisJaW50IHJl dDsKKworCS8qIE1ha2Ugc3VyZSB3ZSBoYXZlIGVub3VnaCBzcGFjZSBmb3IgYSB3aG9sZSBwYWNr YWdlICovCisJaWYgKGNvdW50IDwgc2l6ZW9mKHN0cnVjdCBjY2lkZ19idWxrX291dF9oZWFkZXIp KSB7CisJCURCRyhjY2lkZy0+ZnVuY3Rpb24uY29uZmlnLT5jZGV2LAorCQkJCSJjY2lkOiB0b28g c21hbGwgYnVmZmVyIHNpemUuICV6dSBwcm92aWRlZCwgbmVlZCBhdCBsZWFzdCAlenVcbiIsCisJ CQkJY291bnQsIHNpemVvZihzdHJ1Y3QgY2NpZGdfYnVsa19vdXRfaGVhZGVyKSk7CisJCXJldHVy biAtRU5PTUVNOworCX0KKworCWlmICghYXRvbWljX3JlYWQoJmNjaWRnLT5vbmxpbmUpKQorCQly ZXR1cm4gLUVOT0RFVjsKKworCS8qIHF1ZXVlIGEgcmVxdWVzdCAqLworCXJlcSA9IGJ1bGtfZGV2 LT5yeF9yZXE7CisJcmVxLT5sZW5ndGggPSBjb3VudDsKKwlhdG9taWNfc2V0KCZidWxrX2Rldi0+ cnhfZG9uZSwgMCk7CisKKwlyZXQgPSB1c2JfZXBfcXVldWUoY2NpZGctPm91dCwgcmVxLCBHRlBf S0VSTkVMKTsKKwlpZiAocmV0IDwgMCkgeworCQlFUlJPUihjY2lkZy0+ZnVuY3Rpb24uY29uZmln LT5jZGV2LAorCQkJCSJjY2lkOiB1c2IgZXAgcXVldWUgZmFpbGVkXG4iKTsKKwkJcmV0dXJuIC1F SU87CisJfQorCisJaWYgKCFhdG9taWNfcmVhZCgmYnVsa19kZXYtPnJ4X2RvbmUpICYmCisJCQlm aWxlLT5mX2ZsYWdzICYgKE9fTk9OQkxPQ0sgfCBPX05ERUxBWSkpCisJCXJldHVybiAtRUFHQUlO OworCisJLyogd2FpdCBmb3IgYSByZXF1ZXN0IHRvIGNvbXBsZXRlICovCisJcmV0ID0gd2FpdF9l dmVudF9pbnRlcnJ1cHRpYmxlKGJ1bGtfZGV2LT5yZWFkX3dxLAorCQkJYXRvbWljX3JlYWQoJmJ1 bGtfZGV2LT5yeF9kb25lKSB8fAorCQkJIWF0b21pY19yZWFkKCZjY2lkZy0+b25saW5lKSk7CisJ aWYgKHJldCA8IDApIHsKKwkJdXNiX2VwX2RlcXVldWUoY2NpZGctPm91dCwgcmVxKTsKKwkJcmV0 dXJuIC1FUkVTVEFSVFNZUzsKKwl9CisKKwkvKiBTdGlsbCBvbmxpbmU/ICovCisJaWYgKCFhdG9t aWNfcmVhZCgmY2NpZGctPm9ubGluZSkpCisJCXJldHVybiAtRU5PREVWOworCisJYXRvbWljX3Nl dCgmYnVsa19kZXYtPnJ4X3JlcV9idXN5LCAxKTsKKwl4ZmVyID0gKHJlcS0+YWN0dWFsIDwgY291 bnQpID8gcmVxLT5hY3R1YWwgOiBjb3VudDsKKworCWlmIChjb3B5X3RvX3VzZXIoYnVmLCByZXEt PmJ1ZiwgeGZlcikpCisJCXIgPSAtRUZBVUxUOworCisJYXRvbWljX3NldCgmYnVsa19kZXYtPnJ4 X3JlcV9idXN5LCAwKTsKKwlpZiAoIWF0b21pY19yZWFkKCZjY2lkZy0+b25saW5lKSkgeworCQlj Y2lkZ19yZXF1ZXN0X2ZyZWUoYnVsa19kZXYtPnJ4X3JlcSwgY2NpZGctPm91dCk7CisJCXJldHVy biAtRU5PREVWOworCX0KKworCXJldHVybiB4ZmVyOworfQorCitzdGF0aWMgc3NpemVfdCBjY2lk Z19idWxrX3dyaXRlKHN0cnVjdCBmaWxlICpmaWxlLCBjb25zdCBjaGFyIF9fdXNlciAqYnVmLAor CQkJCSBzaXplX3QgY291bnQsIGxvZmZfdCAqcG9zKQoreworCXN0cnVjdCBmX2NjaWRnICpjY2lk ZyA9ICBmaWxlLT5wcml2YXRlX2RhdGE7CisJc3RydWN0IGNjaWRnX2J1bGtfZGV2ICpidWxrX2Rl diA9ICZjY2lkZy0+YnVsa19kZXY7CisJc3RydWN0IHVzYl9yZXF1ZXN0ICpyZXEgPSBOVUxMOwor CWludCByZXQ7CisKKwkvKiBBcmUgd2Ugb25saW5lPyAqLworCWlmICghYXRvbWljX3JlYWQoJmNj aWRnLT5vbmxpbmUpKQorCQlyZXR1cm4gLUVOT0RFVjsKKworCS8qIEF2b2lkIFplcm8gTGVuZ3Ro IFBhY2tldHMgKFpMUCkgKi8KKwlpZiAoIWNvdW50KQorCQlyZXR1cm4gMDsKKworCS8qIE1ha2Ug c3VyZSB3ZSBoYXZlIGVub3VnaCBzcGFjZSBmb3IgYSB3aG9sZSBwYWNrYWdlICovCisJaWYgKGNv dW50ID4gc2l6ZW9mKHN0cnVjdCBjY2lkZ19idWxrX291dF9oZWFkZXIpKSB7CisJCURCRyhjY2lk Zy0+ZnVuY3Rpb24uY29uZmlnLT5jZGV2LAorCQkJCSJjY2lkOiB0b28gbXVjaCBkYXRhLiAlenUg cHJvdmlkZWQsIGJ1dCB3ZSBjYW4gb25seSBoYW5kbGUgJXp1XG4iLAorCQkJCWNvdW50LCBzaXpl b2Yoc3RydWN0IGNjaWRnX2J1bGtfb3V0X2hlYWRlcikpOworCQlyZXR1cm4gLUVOT01FTTsKKwl9 CisKKwlpZiAobGlzdF9lbXB0eSgmYnVsa19kZXYtPnR4X2lkbGUpICYmCisJCQlmaWxlLT5mX2Zs YWdzICYgKE9fTk9OQkxPQ0sgfCBPX05ERUxBWSkpCisJCXJldHVybiAtRUFHQUlOOworCisJLyog Z2V0IGFuIGlkbGUgdHggcmVxdWVzdCB0byB1c2UgKi8KKwlyZXQgPSB3YWl0X2V2ZW50X2ludGVy cnVwdGlibGUoYnVsa19kZXYtPndyaXRlX3dxLAorCQkoKHJlcSA9IGNjaWRnX3JlcV9nZXQoY2Np ZGcsICZidWxrX2Rldi0+dHhfaWRsZSkpKSk7CisKKwlpZiAocmV0IDwgMCkKKwkJcmV0dXJuIC1F UkVTVEFSVFNZUzsKKworCWlmIChjb3B5X2Zyb21fdXNlcihyZXEtPmJ1ZiwgYnVmLCBjb3VudCkp IHsKKwkJaWYgKCFhdG9taWNfcmVhZCgmY2NpZGctPm9ubGluZSkpIHsKKwkJCWNjaWRnX3JlcXVl c3RfZnJlZShyZXEsIGNjaWRnLT5pbik7CisJCQlyZXR1cm4gLUVOT0RFVjsKKwkJfSBlbHNlIHsK KwkJCWNjaWRnX3JlcV9wdXQoY2NpZGcsICZidWxrX2Rldi0+dHhfaWRsZSwgcmVxKTsKKwkJCXJl dHVybiAtRUZBVUxUOworCQl9CisJfQorCisJcmVxLT5sZW5ndGggPSBjb3VudDsKKwlyZXQgPSB1 c2JfZXBfcXVldWUoY2NpZGctPmluLCByZXEsIEdGUF9LRVJORUwpOworCWlmIChyZXQgPCAwKSB7 CisJCWNjaWRnX3JlcV9wdXQoY2NpZGcsICZidWxrX2Rldi0+dHhfaWRsZSwgcmVxKTsKKworCQlp ZiAoIWF0b21pY19yZWFkKCZjY2lkZy0+b25saW5lKSkgeworCQkJLyogRnJlZSB1cCBhbGwgcmVx dWVzdHMgaWYgd2UgYXJlIG5vdCBvbmxpbmUgKi8KKwkJCXdoaWxlICgocmVxID0gY2NpZGdfcmVx X2dldChjY2lkZywgJmJ1bGtfZGV2LT50eF9pZGxlKSkpCisJCQkJY2NpZGdfcmVxdWVzdF9mcmVl KHJlcSwgY2NpZGctPmluKTsKKworCQkJcmV0dXJuIC1FTk9ERVY7CisJCX0KKwkJcmV0dXJuIC1F SU87CisJfQorCisJcmV0dXJuIGNvdW50OworfQorCitzdGF0aWMgX19wb2xsX3QgY2NpZGdfYnVs a19wb2xsKHN0cnVjdCBmaWxlICpmaWxlLCBwb2xsX3RhYmxlICogd2FpdCkKK3sKKwlzdHJ1Y3Qg Zl9jY2lkZyAqY2NpZGcgPSAgZmlsZS0+cHJpdmF0ZV9kYXRhOworCXN0cnVjdCBjY2lkZ19idWxr X2RldiAqYnVsa19kZXYgPSAmY2NpZGctPmJ1bGtfZGV2OworCV9fcG9sbF90CXJldCA9IDA7CisK Kwlwb2xsX3dhaXQoZmlsZSwgJmJ1bGtfZGV2LT5yZWFkX3dxLCB3YWl0KTsKKwlwb2xsX3dhaXQo ZmlsZSwgJmJ1bGtfZGV2LT53cml0ZV93cSwgd2FpdCk7CisKKwlpZiAobGlzdF9lbXB0eSgmYnVs a19kZXYtPnR4X2lkbGUpKQorCQlyZXQgfD0gRVBPTExPVVQgfCBFUE9MTFdSTk9STTsKKworCWlm IChhdG9taWNfcmVhZCgmYnVsa19kZXYtPnJ4X2RvbmUpKQorCQlyZXQgfD0gRVBPTExJTiB8IEVQ T0xMUkROT1JNOworCisJcmV0dXJuIHJldDsKK30KKworc3RhdGljIGNvbnN0IHN0cnVjdCBmaWxl X29wZXJhdGlvbnMgZl9jY2lkZ19mb3BzID0geworCS5vd25lciA9IFRISVNfTU9EVUxFLAorCS5y ZWFkID0gY2NpZGdfYnVsa19yZWFkLAorCS53cml0ZSA9IGNjaWRnX2J1bGtfd3JpdGUsCisJLm9w ZW4gPSBjY2lkZ19idWxrX29wZW4sCisJLnBvbGwgPSBjY2lkZ19idWxrX3BvbGwsCisJLnJlbGVh c2UgPSBjY2lkZ19idWxrX3JlbGVhc2UsCit9OworCitzdGF0aWMgaW50IGNjaWRnX2J1bGtfZGV2 aWNlX2luaXQoc3RydWN0IGZfY2NpZGcgKmRldikKK3sKKwlzdHJ1Y3QgY2NpZGdfYnVsa19kZXYg KmJ1bGtfZGV2ID0gJmRldi0+YnVsa19kZXY7CisKKwlpbml0X3dhaXRxdWV1ZV9oZWFkKCZidWxr X2Rldi0+cmVhZF93cSk7CisJaW5pdF93YWl0cXVldWVfaGVhZCgmYnVsa19kZXYtPndyaXRlX3dx KTsKKwlJTklUX0xJU1RfSEVBRCgmYnVsa19kZXYtPnR4X2lkbGUpOworCisJcmV0dXJuIDA7Cit9 CisKK3N0YXRpYyB2b2lkIGNjaWRnX2Z1bmN0aW9uX2ZyZWUoc3RydWN0IHVzYl9mdW5jdGlvbiAq ZikKK3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2NpZGc7CisJc3RydWN0IGZfY2NpZGdfb3B0cyAqb3B0 czsKKworCWNjaWRnID0gZnVuY190b19jY2lkZyhmKTsKKwlvcHRzID0gY29udGFpbmVyX29mKGYt PmZpLCBzdHJ1Y3QgZl9jY2lkZ19vcHRzLCBmdW5jX2luc3QpOworCisJa2ZyZWUoY2NpZGcpOwor CW11dGV4X2xvY2soJm9wdHMtPmxvY2spOworCS0tb3B0cy0+cmVmY250OworCW11dGV4X3VubG9j aygmb3B0cy0+bG9jayk7Cit9CisKK3N0YXRpYyB2b2lkIGNjaWRnX2Z1bmN0aW9uX3VuYmluZChz dHJ1Y3QgdXNiX2NvbmZpZ3VyYXRpb24gKmMsCisJCQkJCXN0cnVjdCB1c2JfZnVuY3Rpb24gKmYp Cit7CisJc3RydWN0IGZfY2NpZGcgKmNjaWRnID0gZnVuY190b19jY2lkZyhmKTsKKworCWRldmlj ZV9kZXN0cm95KGNjaWRnX2NsYXNzLCBNS0RFVihtYWpvciwgY2NpZGctPm1pbm9yKSk7CisJY2Rl dl9kZWwoJmNjaWRnLT5jZGV2KTsKKworCS8qIGRpc2FibGUvZnJlZSByZXF1ZXN0IGFuZCBlbmQg cG9pbnQgKi8KKwl1c2JfZnJlZV9hbGxfZGVzY3JpcHRvcnMoZik7Cit9CisKK3N0YXRpYyBpbnQg Y2NpZGdfZnVuY3Rpb25fYmluZChzdHJ1Y3QgdXNiX2NvbmZpZ3VyYXRpb24gKmMsCisJCQkJCXN0 cnVjdCB1c2JfZnVuY3Rpb24gKmYpCit7CisJc3RydWN0IGZfY2NpZGcgKmNjaWRnID0gZnVuY190 b19jY2lkZyhmKTsKKwlzdHJ1Y3QgdXNiX2VwICplcDsKKwlzdHJ1Y3QgdXNiX2NvbXBvc2l0ZV9k ZXYgKmNkZXYgPSBjLT5jZGV2OworCXN0cnVjdCBkZXZpY2UgKmRldmljZTsKKwlkZXZfdCBkZXY7 CisJaW50IGlmY19pZDsKKwlpbnQgcmV0OworCisJLyogYWxsb2NhdGUgaW5zdGFuY2Utc3BlY2lm aWMgaW50ZXJmYWNlIElEcywgYW5kIHBhdGNoIGRlc2NyaXB0b3JzICovCisJaWZjX2lkID0gdXNi X2ludGVyZmFjZV9pZChjLCBmKTsKKwlpZiAoaWZjX2lkIDwgMCkgeworCQlFUlJPUihjZGV2LCAi Y2NpZDogdW5hYmxlIHRvIGFsbG9jYXRlIGlmYyBpZCwgZXJyOiVkXG4iLAorCQkJCWlmY19pZCk7 CisJCXJldHVybiBpZmNfaWQ7CisJfQorCWNjaWRfaW50ZXJmYWNlX2Rlc2MuYkludGVyZmFjZU51 bWJlciA9IGlmY19pZDsKKworCS8qIGFsbG9jYXRlIGluc3RhbmNlLXNwZWNpZmljIGVuZHBvaW50 cyAqLworCWVwID0gdXNiX2VwX2F1dG9jb25maWcoY2Rldi0+Z2FkZ2V0LCAmY2NpZF9mc19pbl9k ZXNjKTsKKwlpZiAoIWVwKSB7CisJCUVSUk9SKGNkZXYsICJjY2lkOiB1c2IgZXBpbiBhdXRvY29u ZmlnIGZhaWxlZFxuIik7CisJCXJldCA9IC1FTk9ERVY7CisJCWdvdG8gZXBfYXV0b19pbl9mYWls OworCX0KKwljY2lkZy0+aW4gPSBlcDsKKwllcC0+ZHJpdmVyX2RhdGEgPSBjY2lkZzsKKworCWVw ID0gdXNiX2VwX2F1dG9jb25maWcoY2Rldi0+Z2FkZ2V0LCAmY2NpZF9mc19vdXRfZGVzYyk7CisJ aWYgKCFlcCkgeworCQlFUlJPUihjZGV2LCAiY2NpZDogdXNiIGVwb3V0IGF1dG9jb25maWcgZmFp bGVkXG4iKTsKKwkJcmV0ID0gLUVOT0RFVjsKKwkJZ290byBlcF9hdXRvX291dF9mYWlsOworCX0K KwljY2lkZy0+b3V0ID0gZXA7CisJZXAtPmRyaXZlcl9kYXRhID0gY2NpZGc7CisKKwkvKiBzZXQg ZGVzY3JpcHRvciBkeW5hbWljIHZhbHVlcyAqLworCWNjaWRfY2xhc3NfZGVzYy5kd0ZlYXR1cmVz CT0gY3B1X3RvX2xlMzIoY2NpZGctPmZlYXR1cmVzKTsKKwljY2lkX2NsYXNzX2Rlc2MuYlBJTlN1 cHBvcnQJPSBjY2lkZy0+cGluc3VwcG9ydDsKKwljY2lkX2NsYXNzX2Rlc2Mud0xjZExheW91dAk9 IGNwdV90b19sZTE2KGNjaWRnLT5sY2RsYXlvdXQpOworCWNjaWRfY2xhc3NfZGVzYy5iTWF4U2xv dEluZGV4CT0gY2NpZGctPm5zbG90czsKKwljY2lkX2NsYXNzX2Rlc2MuZHdQcm90b2NvbHMJPSBj cHVfdG9fbGUzMihjY2lkZy0+cHJvdG9jb2xzKTsKKworCWlmIChjY2lkZy0+cHJvdG9jb2xzID09 IENDSURfUFJPVE9DT0xfTk9UX1NFTCkgeworCQljY2lkZy0+cHJvdG9jb2xzID0gQ0NJRF9QUk9U T0NPTF9UMCB8IENDSURfUFJPVE9DT0xfVDE7CisJCUlORk8oY2NpZGctPmZ1bmN0aW9uLmNvbmZp Zy0+Y2RldiwKKwkJCSJjY2lkOiBObyBwcm90b2NvbCBzZWxlY3RlZC4gU3VwcG9ydCBib3RoIFQw IGFuZCBUMS5cbiIpOworCX0KKworCisJY2NpZF9oc19pbl9kZXNjLmJFbmRwb2ludEFkZHJlc3Mg PQorCQkJY2NpZF9mc19pbl9kZXNjLmJFbmRwb2ludEFkZHJlc3M7CisJY2NpZF9oc19vdXRfZGVz Yy5iRW5kcG9pbnRBZGRyZXNzID0KKwkJCWNjaWRfZnNfb3V0X2Rlc2MuYkVuZHBvaW50QWRkcmVz czsKKworCXJldCAgPSB1c2JfYXNzaWduX2Rlc2NyaXB0b3JzKGYsIGNjaWRfZnNfZGVzY3MsCisJ CQljY2lkX2hzX2Rlc2NzLCBOVUxMLCBOVUxMKTsKKwlpZiAocmV0KQorCQlnb3RvIGVwX2F1dG9f b3V0X2ZhaWw7CisKKwkvKiBjcmVhdGUgY2hhciBkZXZpY2UgKi8KKwljZGV2X2luaXQoJmNjaWRn LT5jZGV2LCAmZl9jY2lkZ19mb3BzKTsKKwlkZXYgPSBNS0RFVihtYWpvciwgY2NpZGctPm1pbm9y KTsKKwlyZXQgPSBjZGV2X2FkZCgmY2NpZGctPmNkZXYsIGRldiwgMSk7CisJaWYgKHJldCkKKwkJ Z290byBmYWlsX2ZyZWVfZGVzY3M7CisKKwlkZXZpY2UgPSBkZXZpY2VfY3JlYXRlKGNjaWRnX2Ns YXNzLCBOVUxMLCBkZXYsIE5VTEwsCisJCQkgICAgICAgIiVzJWQiLCAiY2NpZGciLCBjY2lkZy0+ bWlub3IpOworCWlmIChJU19FUlIoZGV2aWNlKSkgeworCQlyZXQgPSBQVFJfRVJSKGRldmljZSk7 CisJCWdvdG8gZGVsOworCX0KKworCXJldHVybiAwOworCitkZWw6CisJY2Rldl9kZWwoJmNjaWRn LT5jZGV2KTsKK2ZhaWxfZnJlZV9kZXNjczoKKwl1c2JfZnJlZV9hbGxfZGVzY3JpcHRvcnMoZik7 CitlcF9hdXRvX291dF9mYWlsOgorCWNjaWRnLT5vdXQtPmRyaXZlcl9kYXRhID0gTlVMTDsKKwlj Y2lkZy0+b3V0ID0gTlVMTDsKK2VwX2F1dG9faW5fZmFpbDoKKwljY2lkZy0+aW4tPmRyaXZlcl9k YXRhID0gTlVMTDsKKwljY2lkZy0+aW4gPSBOVUxMOworCUVSUk9SKGYtPmNvbmZpZy0+Y2Rldiwg ImNjaWRnX2JpbmQgRkFJTEVEXG4iKTsKKworCXJldHVybiByZXQ7Cit9CisKK3N0YXRpYyBzdHJ1 Y3QgdXNiX2Z1bmN0aW9uICpjY2lkZ19hbGxvYyhzdHJ1Y3QgdXNiX2Z1bmN0aW9uX2luc3RhbmNl ICpmaSkKK3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2NpZGc7CisJc3RydWN0IGZfY2NpZGdfb3B0cyAq b3B0czsKKwlpbnQgcmV0OworCisJY2NpZGcgPSBremFsbG9jKHNpemVvZigqY2NpZGcpLCBHRlBf S0VSTkVMKTsKKwlpZiAoIWNjaWRnKQorCQlyZXR1cm4gRVJSX1BUUigtRU5PTUVNKTsKKworCXNw aW5fbG9ja19pbml0KCZjY2lkZy0+bG9jayk7CisKKwlyZXQgPSBjY2lkZ19idWxrX2RldmljZV9p bml0KGNjaWRnKTsKKwlpZiAocmV0KSB7CisJCWtmcmVlKGNjaWRnKTsKKwkJcmV0dXJuIEVSUl9Q VFIocmV0KTsKKwl9CisKKwlvcHRzID0gY29udGFpbmVyX29mKGZpLCBzdHJ1Y3QgZl9jY2lkZ19v cHRzLCBmdW5jX2luc3QpOworCisJbXV0ZXhfbG9jaygmb3B0cy0+bG9jayk7CisJKytvcHRzLT5y ZWZjbnQ7CisKKwljY2lkZy0+bWlub3IgPSBvcHRzLT5taW5vcjsKKwljY2lkZy0+ZmVhdHVyZXMg PSBvcHRzLT5mZWF0dXJlczsKKwljY2lkZy0+cHJvdG9jb2xzID0gb3B0cy0+cHJvdG9jb2xzOwor CWNjaWRnLT5waW5zdXBwb3J0ID0gb3B0cy0+cGluc3VwcG9ydDsKKwljY2lkZy0+bnNsb3RzID0g b3B0cy0+bnNsb3RzOworCW11dGV4X3VubG9jaygmb3B0cy0+bG9jayk7CisKKwljY2lkZy0+ZnVu Y3Rpb24ubmFtZQk9ICJjY2lkIjsKKwljY2lkZy0+ZnVuY3Rpb24uYmluZAk9IGNjaWRnX2Z1bmN0 aW9uX2JpbmQ7CisJY2NpZGctPmZ1bmN0aW9uLnVuYmluZAk9IGNjaWRnX2Z1bmN0aW9uX3VuYmlu ZDsKKwljY2lkZy0+ZnVuY3Rpb24uc2V0X2FsdAk9IGNjaWRnX2Z1bmN0aW9uX3NldF9hbHQ7CisJ Y2NpZGctPmZ1bmN0aW9uLmRpc2FibGUJPSBjY2lkZ19mdW5jdGlvbl9kaXNhYmxlOworCWNjaWRn LT5mdW5jdGlvbi5zZXR1cAk9IGNjaWRnX2Z1bmN0aW9uX3NldHVwOworCWNjaWRnLT5mdW5jdGlv bi5mcmVlX2Z1bmMgPSBjY2lkZ19mdW5jdGlvbl9mcmVlOworCisJcmV0dXJuICZjY2lkZy0+ZnVu Y3Rpb247Cit9CisKK3N0YXRpYyB2b2lkIGNjaWRnX2ZyZWVfaW5zdChzdHJ1Y3QgdXNiX2Z1bmN0 aW9uX2luc3RhbmNlICpmKQoreworCXN0cnVjdCBmX2NjaWRnX29wdHMgKm9wdHM7CisKKwlvcHRz ID0gY29udGFpbmVyX29mKGYsIHN0cnVjdCBmX2NjaWRnX29wdHMsIGZ1bmNfaW5zdCk7CisJbXV0 ZXhfbG9jaygmY2NpZGdfaWRhX2xvY2spOworCisJY2NpZGdfcHV0X21pbm9yKG9wdHMtPm1pbm9y KTsKKwlpZiAoaWRhX2lzX2VtcHR5KCZjY2lkZ19pZGEpKQorCQljY2lkZ19jbGVhbnVwKCk7CisK KwltdXRleF91bmxvY2soJmNjaWRnX2lkYV9sb2NrKTsKKworCWtmcmVlKG9wdHMpOworfQorCitz dGF0aWMgc3RydWN0IHVzYl9mdW5jdGlvbl9pbnN0YW5jZSAqY2NpZGdfYWxsb2NfaW5zdCh2b2lk KQoreworCXN0cnVjdCBmX2NjaWRnX29wdHMgKm9wdHM7CisJc3RydWN0IHVzYl9mdW5jdGlvbl9p bnN0YW5jZSAqcmV0OworCWludCBzdGF0dXMgPSAwOworCisJb3B0cyA9IGt6YWxsb2Moc2l6ZW9m KCpvcHRzKSwgR0ZQX0tFUk5FTCk7CisJaWYgKCFvcHRzKQorCQlyZXR1cm4gRVJSX1BUUigtRU5P TUVNKTsKKworCW11dGV4X2luaXQoJm9wdHMtPmxvY2spOworCW9wdHMtPmZ1bmNfaW5zdC5mcmVl X2Z1bmNfaW5zdCA9IGNjaWRnX2ZyZWVfaW5zdDsKKwlyZXQgPSAmb3B0cy0+ZnVuY19pbnN0Owor CisJbXV0ZXhfbG9jaygmY2NpZGdfaWRhX2xvY2spOworCisJaWYgKGlkYV9pc19lbXB0eSgmY2Np ZGdfaWRhKSkgeworCQlzdGF0dXMgPSBjY2lkZ19zZXR1cCgpOworCQlpZiAoc3RhdHVzKSAgewor CQkJcmV0ID0gRVJSX1BUUihzdGF0dXMpOworCQkJa2ZyZWUob3B0cyk7CisJCQlnb3RvIHVubG9j azsKKwkJfQorCX0KKworCW9wdHMtPm1pbm9yID0gY2NpZGdfZ2V0X21pbm9yKCk7CisJaWYgKG9w dHMtPm1pbm9yIDwgMCkgeworCQlyZXQgPSBFUlJfUFRSKG9wdHMtPm1pbm9yKTsKKwkJa2ZyZWUo b3B0cyk7CisJCWlmIChpZGFfaXNfZW1wdHkoJmNjaWRnX2lkYSkpCisJCQljY2lkZ19jbGVhbnVw KCk7CisJCWdvdG8gdW5sb2NrOworCX0KKworCWNvbmZpZ19ncm91cF9pbml0X3R5cGVfbmFtZSgm b3B0cy0+ZnVuY19pbnN0Lmdyb3VwLAorCQkJIiIsICZjY2lkZ19mdW5jX3R5cGUpOworCit1bmxv Y2s6CisJbXV0ZXhfdW5sb2NrKCZjY2lkZ19pZGFfbG9jayk7CisJcmV0dXJuIHJldDsKK30KKwor REVDTEFSRV9VU0JfRlVOQ1RJT05fSU5JVChjY2lkLCBjY2lkZ19hbGxvY19pbnN0LCBjY2lkZ19h bGxvYyk7CisKK01PRFVMRV9ERVNDUklQVElPTigiVVNCIENDSUQgR2FkZ2V0IGRyaXZlciIpOwor TU9EVUxFX0FVVEhPUigiTWFyY3VzIEZvbGtlc3NvbiA8bWFyY3VzLmZvbGtlc3NvbkBnbWFpbC5j b20+Iik7CitNT0RVTEVfTElDRU5TRSgiR1BMIHYyIik7CmRpZmYgLS1naXQgYS9kcml2ZXJzL3Vz Yi9nYWRnZXQvZnVuY3Rpb24vZl9jY2lkLmggYi9kcml2ZXJzL3VzYi9nYWRnZXQvZnVuY3Rpb24v Zl9jY2lkLmgKbmV3IGZpbGUgbW9kZSAxMDA2NDQKaW5kZXggMDAwMDAwMDAwMDAwLi5mMTA1M2Vj NWM0ZDkKLS0tIC9kZXYvbnVsbAorKysgYi9kcml2ZXJzL3VzYi9nYWRnZXQvZnVuY3Rpb24vZl9j Y2lkLmgKQEAgLTAsMCArMSw5MSBAQAorLyogU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEdQTC0y LjAgKi8KKy8qCisgKiBDb3B5cmlnaHQgKEMpIDIwMTggTWFyY3VzIEZvbGtlc3NvbiA8bWFyY3Vz LmZvbGtlc3NvbkBnbWFpbC5jb20+CisgKi8KKworI2lmbmRlZiBGX0NDSURfSAorI2RlZmluZSBG X0NDSURfSAorCisjZGVmaW5lIENDSUQxXzEwICAgICAgICAgICAgICAgIDB4MDExMAorI2RlZmlu ZSBDQ0lEX0RFQ1JJUFRPUl9UWVBFICAgICAweDIxCisjZGVmaW5lIEFCREFUQV9TSVpFCQk1MTIK KyNkZWZpbmUgU01BUlRfQ0FSRF9ERVZJQ0VfQ0xBU1MJMHgwQgorCisvKiBDQ0lEIENsYXNzIFNw ZWNpZmljIFJlcXVlc3QgKi8KKyNkZWZpbmUgQ0NJREdFTkVSSUNSRVFfQUJPUlQgICAgICAgICAg ICAgICAgICAgIDB4MDEKKyNkZWZpbmUgQ0NJREdFTkVSSUNSRVFfR0VUX0NMT0NLX0ZSRVFVRU5D SUVTICAgIDB4MDIKKyNkZWZpbmUgQ0NJREdFTkVSSUNSRVFfR0VUX0RBVEFfUkFURVMgICAgICAg ICAgIDB4MDMKKworLyogU3VwcG9ydGVkIHZvbHRhZ2VzICovCisjZGVmaW5lIENDSURfVk9MVFNf QVVUTyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMHgwMAorI2RlZmluZSBDQ0lEX1ZPTFRT XzVfMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDB4MDEKKyNkZWZpbmUgQ0NJRF9WT0xU U18zXzAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAweDAyCisjZGVmaW5lIENDSURfVk9M VFNfMV84ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMHgwMworCitzdHJ1Y3QgZl9jY2lk Z19vcHRzIHsKKwlzdHJ1Y3QgdXNiX2Z1bmN0aW9uX2luc3RhbmNlIGZ1bmNfaW5zdDsKKwlpbnQJ bWlub3I7CisJX191MzIJZmVhdHVyZXM7CisJX191MzIJcHJvdG9jb2xzOworCV9fdTgJcGluc3Vw cG9ydDsKKwlfX3U4CW5zbG90czsKKwlfX3U4CWxjZGxheW91dDsKKworCS8qCisJICogUHJvdGVj dCB0aGUgZGF0YSBmb3JtIGNvbmN1cnJlbnQgYWNjZXNzIGJ5IHJlYWQvd3JpdGUKKwkgKiBhbmQg Y3JlYXRlIHN5bWxpbmsvcmVtb3ZlIHN5bWxpbmsuCisJICovCisJc3RydWN0IG11dGV4CWxvY2s7 CisJaW50CQlyZWZjbnQ7Cit9OworCitzdHJ1Y3QgY2NpZGdfYnVsa19pbl9oZWFkZXIgeworCV9f dTgJYk1lc3NhZ2VUeXBlOworCV9fbGUzMgl3TGVuZ3RoOworCV9fdTgJYlNsb3Q7CisJX191OAli U2VxOworCV9fdTgJYlN0YXR1czsKKwlfX3U4CWJFcnJvcjsKKwlfX3U4CWJTcGVjaWZpYzsKKwlf X3U4CWFiRGF0YVtBQkRBVEFfU0laRV07CisJX191OAliU2l6ZVRvU2VuZDsKK30gX19wYWNrZWQ7 CisKK3N0cnVjdCBjY2lkZ19idWxrX291dF9oZWFkZXIgeworCV9fdTgJIGJNZXNzYWdlVHlwZTsK KwlfX2xlMzIJIHdMZW5ndGg7CisJX191OAkgYlNsb3Q7CisJX191OAkgYlNlcTsKKwlfX3U4CSBi U3BlY2lmaWNfMDsKKwlfX3U4CSBiU3BlY2lmaWNfMTsKKwlfX3U4CSBiU3BlY2lmaWNfMjsKKwlf X3U4CSBBUERVW0FCREFUQV9TSVpFXTsKK30gX19wYWNrZWQ7CisKK3N0cnVjdCBjY2lkX2NsYXNz X2Rlc2NyaXB0b3IgeworCV9fdTgJYkxlbmd0aDsKKwlfX3U4CWJEZXNjcmlwdG9yVHlwZTsKKwlf X2xlMTYJYmNkQ0NJRDsKKwlfX3U4CWJNYXhTbG90SW5kZXg7CisJX191OAliVm9sdGFnZVN1cHBv cnQ7CisJX19sZTMyCWR3UHJvdG9jb2xzOworCV9fbGUzMglkd0RlZmF1bHRDbG9jazsKKwlfX2xl MzIJZHdNYXhpbXVtQ2xvY2s7CisJX191OAliTnVtQ2xvY2tTdXBwb3J0ZWQ7CisJX19sZTMyCWR3 RGF0YVJhdGU7CisJX19sZTMyCWR3TWF4RGF0YVJhdGU7CisJX191OAliTnVtRGF0YVJhdGVzU3Vw cG9ydGVkOworCV9fbGUzMglkd01heElGU0Q7CisJX19sZTMyCWR3U3luY2hQcm90b2NvbHM7CisJ X19sZTMyCWR3TWVjaGFuaWNhbDsKKwlfX2xlMzIJZHdGZWF0dXJlczsKKwlfX2xlMzIJZHdNYXhD Q0lETWVzc2FnZUxlbmd0aDsKKwlfX3U4CWJDbGFzc0dldFJlc3BvbnNlOworCV9fdTgJYkNsYXNz RW52ZWxvcGU7CisJX19sZTE2CXdMY2RMYXlvdXQ7CisJX191OAliUElOU3VwcG9ydDsKKwlfX3U4 CWJNYXhDQ0lEQnVzeVNsb3RzOworfSBfX3BhY2tlZDsKKworCisjZW5kaWYKZGlmZiAtLWdpdCBh L2luY2x1ZGUvdWFwaS9saW51eC91c2IvY2NpZC5oIGIvaW5jbHVkZS91YXBpL2xpbnV4L3VzYi9j Y2lkLmgKbmV3IGZpbGUgbW9kZSAxMDA2NDQKaW5kZXggMDAwMDAwMDAwMDAwLi41MTc4OTcyMDE1 NjMKLS0tIC9kZXYvbnVsbAorKysgYi9pbmNsdWRlL3VhcGkvbGludXgvdXNiL2NjaWQuaApAQCAt MCwwICsxLDkzIEBACisvKiBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogR1BMLTIuMCAqLworLyoK KyAqIENvcHlyaWdodCAoQykgMjAxOCBNYXJjdXMgRm9sa2Vzc29uIDxtYXJjdXMuZm9sa2Vzc29u QGdtYWlsLmNvbT4KKyAqCisgKiBUaGlzIGZpbGUgaG9sZHMgVVNCIGNvbnN0YW50cyBkZWZpbmVk IGJ5IHRoZSBDQ0lEIFNwZWNpZmljYXRpb24uCisgKi8KKworI2lmbmRlZiBDQ0lEX0gKKyNkZWZp bmUgQ0NJRF9ICisKKy8qIFNsb3QgZXJyb3IgcmVnaXN0ZXIgd2hlbiBibUNvbW1hbmRTdGF0dXMg PSAxICovCisjZGVmaW5lIENDSURfQ01EX0FCT1JURUQgICAgICAgICAgICAgICAgICAgICAgICAg ICAgMHhGRgorI2RlZmluZSBDQ0lEX0lDQ19NVVRFICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgIDB4RkUKKyNkZWZpbmUgQ0NJRF9YRlJfUEFSSVRZX0VSUk9SICAgICAgICAgICAgICAgICAg ICAgICAweEZECisjZGVmaW5lIENDSURfWEZSX09WRVJSVU4gICAgICAgICAgICAgICAgICAgICAg ICAgICAgMHhGQworI2RlZmluZSBDQ0lEX0hXX0VSUk9SICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgIDB4RkIKKyNkZWZpbmUgQ0NJRF9CQURfQVRSX1RTICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAweEY4CisjZGVmaW5lIENDSURfQkFEX0FUUl9UQ0sgICAgICAgICAgICAgICAgICAg ICAgICAgICAgMHhGNworI2RlZmluZSBDQ0lEX0lDQ19QUk9UT0NPTF9OT1RfU1VQUE9SVEVEICAg ICAgICAgICAgIDB4RjYKKyNkZWZpbmUgQ0NJRF9JQ0NfQ0xBU1NfTk9UX1NVUFBPUlRFRCAgICAg ICAgICAgICAgICAweEY1CisjZGVmaW5lIENDSURfUFJPQ0VEVVJFX0JZVEVfQ09ORkxJQ1QgICAg ICAgICAgICAgICAgMHhGNAorI2RlZmluZSBDQ0lEX0RFQUNUSVZBVEVEX1BST1RPQ09MICAgICAg ICAgICAgICAgICAgIDB4RjMKKyNkZWZpbmUgQ0NJRF9CVVNZX1dJVEhfQVVUT19TRVFVRU5DRSAg ICAgICAgICAgICAgICAweEYyCisjZGVmaW5lIENDSURfUElOX1RJTUVPVVQgICAgICAgICAgICAg ICAgICAgICAgICAgICAgMHhGMAorI2RlZmluZSBDQ0lEX1BJTl9DQU5DRUxMRUQgICAgICAgICAg ICAgICAgICAgICAgICAgIDB4RUYKKyNkZWZpbmUgQ0NJRF9DTURfU0xPVF9CVVNZICAgICAgICAg ICAgICAgICAgICAgICAgICAweEUwCisKKy8qIFBDIHRvIFJEUiBtZXNzYWdlcyAoYnVsayBvdXQp ICovCisjZGVmaW5lIENDSURfUENfVE9fUkRSX0lDQ1BPV0VST04gICAgICAgICAgICAgICAgICAg MHg2MgorI2RlZmluZSBDQ0lEX1BDX1RPX1JEUl9JQ0NQT1dFUk9GRiAgICAgICAgICAgICAgICAg IDB4NjMKKyNkZWZpbmUgQ0NJRF9QQ19UT19SRFJfR0VUU0xPVFNUQVRVUyAgICAgICAgICAgICAg ICAweDY1CisjZGVmaW5lIENDSURfUENfVE9fUkRSX1hGUkJMT0NLICAgICAgICAgICAgICAgICAg ICAgMHg2RgorI2RlZmluZSBDQ0lEX1BDX1RPX1JEUl9HRVRQQVJBTUVURVJTICAgICAgICAgICAg ICAgIDB4NkMKKyNkZWZpbmUgQ0NJRF9QQ19UT19SRFJfUkVTRVRQQVJBTUVURVJTICAgICAgICAg ICAgICAweDZECisjZGVmaW5lIENDSURfUENfVE9fUkRSX1NFVFBBUkFNRVRFUlMgICAgICAgICAg ICAgICAgMHg2MQorI2RlZmluZSBDQ0lEX1BDX1RPX1JEUl9FU0NBUEUgICAgICAgICAgICAgICAg ICAgICAgIDB4NkIKKyNkZWZpbmUgQ0NJRF9QQ19UT19SRFJfSUNDQ0xPQ0sgICAgICAgICAgICAg ICAgICAgICAweDZFCisjZGVmaW5lIENDSURfUENfVE9fUkRSX1QwQVBEVSAgICAgICAgICAgICAg ICAgICAgICAgMHg2QQorI2RlZmluZSBDQ0lEX1BDX1RPX1JEUl9TRUNVUkUgICAgICAgICAgICAg ICAgICAgICAgIDB4NjkKKyNkZWZpbmUgQ0NJRF9QQ19UT19SRFJfTUVDSEFOSUNBTCAgICAgICAg ICAgICAgICAgICAweDcxCisjZGVmaW5lIENDSURfUENfVE9fUkRSX0FCT1JUICAgICAgICAgICAg ICAgICAgICAgICAgMHg3MgorI2RlZmluZSBDQ0lEX1BDX1RPX1JEUl9TRVREQVRBUkFURUFORENM T0NLRlJFUVVFTkNZIDB4NzMKKworLyogUkRSIHRvIFBDIG1lc3NhZ2VzIChidWxrIGluKSAqLwor I2RlZmluZSBDQ0lEX1JEUl9UT19QQ19EQVRBQkxPQ0sgICAgICAgICAgICAgICAgICAgIDB4ODAK KyNkZWZpbmUgQ0NJRF9SRFJfVE9fUENfU0xPVFNUQVRVUyAgICAgICAgICAgICAgICAgICAweDgx CisjZGVmaW5lIENDSURfUkRSX1RPX1BDX1BBUkFNRVRFUlMgICAgICAgICAgICAgICAgICAgMHg4 MgorI2RlZmluZSBDQ0lEX1JEUl9UT19QQ19FU0NBUEUgICAgICAgICAgICAgICAgICAgICAgIDB4 ODMKKyNkZWZpbmUgQ0NJRF9SRFJfVE9fUENfREFUQVJBVEVBTkRDTE9DS0ZSRVFVRU5DWSAgICAw eDg0CisKKy8qIENsYXNzIEZlYXR1cmVzICovCisKKy8qIE5vIHNwZWNpYWwgY2hhcmFjdGVyaXN0 aWNzICovCisjZGVmaW5lIENDSURfRkVBVFVSRVNfTkFEQSAgICAgICAweDAwMDAwMDAwCisvKiBB dXRvbWF0aWMgcGFyYW1ldGVyIGNvbmZpZ3VyYXRpb24gYmFzZWQgb24gQVRSIGRhdGEgKi8KKyNk ZWZpbmUgQ0NJRF9GRUFUVVJFU19BVVRPX1BDT05GIDB4MDAwMDAwMDIKKy8qIEF1dG9tYXRpYyBh Y3RpdmF0aW9uIG9mIElDQyBvbiBpbnNlcnRpbmcgKi8KKyNkZWZpbmUgQ0NJRF9GRUFUVVJFU19B VVRPX0FDVElWIDB4MDAwMDAwMDQKKy8qIEF1dG9tYXRpYyBJQ0Mgdm9sdGFnZSBzZWxlY3Rpb24g Ki8KKyNkZWZpbmUgQ0NJRF9GRUFUVVJFU19BVVRPX1ZPTFQgIDB4MDAwMDAwMDgKKy8qIEF1dG9t YXRpYyBJQ0MgY2xvY2sgZnJlcXVlbmN5IGNoYW5nZSAqLworI2RlZmluZSBDQ0lEX0ZFQVRVUkVT X0FVVE9fQ0xPQ0sgMHgwMDAwMDAxMAorLyogQXV0b21hdGljIGJhdWQgcmF0ZSBjaGFuZ2UgKi8K KyNkZWZpbmUgQ0NJRF9GRUFUVVJFU19BVVRPX0JBVUQgIDB4MDAwMDAwMjAKKy8qQXV0b21hdGlj IHBhcmFtZXRlcnMgbmVnb3RpYXRpb24gbWFkZSBieSB0aGUgQ0NJRCAqLworI2RlZmluZSBDQ0lE X0ZFQVRVUkVTX0FVVE9fUE5FR08gMHgwMDAwMDA0MAorLyogQXV0b21hdGljIFBQUyBtYWRlIGJ5 IHRoZSBDQ0lEIGFjY29yZGluZyB0byB0aGUgYWN0aXZlIHBhcmFtZXRlcnMgKi8KKyNkZWZpbmUg Q0NJRF9GRUFUVVJFU19BVVRPX1BQUyAgIDB4MDAwMDAwODAKKy8qIENDSUQgY2FuIHNldCBJQ0Mg aW4gY2xvY2sgc3RvcCBtb2RlICovCisjZGVmaW5lIENDSURfRkVBVFVSRVNfSUNDU1RPUCAgICAw eDAwMDAwMTAwCisvKiBOQUQgdmFsdWUgb3RoZXIgdGhhbiAwMCBhY2NlcHRlZCAoVD0xIHByb3Rv Y29sIGluIHVzZSkgKi8KKyNkZWZpbmUgQ0NJRF9GRUFUVVJFU19OQUQgICAgICAgIDB4MDAwMDAy MDAKKy8qIEF1dG9tYXRpYyBJRlNEIGV4Y2hhbmdlIGFzIGZpcnN0IGV4Y2hhbmdlIChUPTEgcHJv dG9jb2wgaW4gdXNlKSAqLworI2RlZmluZSBDQ0lEX0ZFQVRVUkVTX0FVVE9fSUZTRCAgMHgwMDAw MDQwMAorLyogVFBEVSBsZXZlbCBleGNoYW5nZXMgd2l0aCBDQ0lEICovCisjZGVmaW5lIENDSURf RkVBVFVSRVNfRVhDX1RQRFUgICAweDAwMDEwMDAwCisvKiBTaG9ydCBBUERVIGxldmVsIGV4Y2hh bmdlIHdpdGggQ0NJRCAqLworI2RlZmluZSBDQ0lEX0ZFQVRVUkVTX0VYQ19TQVBEVSAgMHgwMDAy MDAwMAorLyogU2hvcnQgYW5kIEV4dGVuZGVkIEFQRFUgbGV2ZWwgZXhjaGFuZ2Ugd2l0aCBDQ0lE ICovCisjZGVmaW5lIENDSURfRkVBVFVSRVNfRVhDX0FQRFUgICAweDAwMDQwMDAwCisvKiBVU0Ig V2FrZSB1cCBzaWduYWxpbmcgc3VwcG9ydGVkIG9uIGNhcmQgaW5zZXJ0aW9uIGFuZCByZW1vdmFs ICovCisjZGVmaW5lIENDSURfRkVBVFVSRVNfV0FLRVVQCTB4MDAxMDAwMDAKKworLyogU3VwcG9y dGVkIHByb3RvY29scyAqLworI2RlZmluZSBDQ0lEX1BST1RPQ09MX05PVF9TRUwJMHgwMAorI2Rl ZmluZSBDQ0lEX1BST1RPQ09MX1QwCTB4MDEKKyNkZWZpbmUgQ0NJRF9QUk9UT0NPTF9UMQkweDAy CisKKyNkZWZpbmUgQ0NJRF9QSU5TVVBPT1JUX05PTkUJCTB4MDAKKyNkZWZpbmUgQ0NJRF9QSU5T VVBPT1JUX1ZFUklGSUNBVElPTgkoMSA8PCAxKQorI2RlZmluZSBDQ0lEX1BJTlNVUE9PUlRfTU9E SUZJQ0FUSU9OCSgxIDw8IDIpCisKKyNlbmRpZgo= From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S966033AbeE2Sum (ORCPT ); Tue, 29 May 2018 14:50:42 -0400 Received: from mail-lf0-f68.google.com ([209.85.215.68]:39417 "EHLO mail-lf0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S964830AbeE2Sui (ORCPT ); Tue, 29 May 2018 14:50:38 -0400 X-Google-Smtp-Source: ADUXVKKrFtCmnPbK4AOcWhx/VF3/qlIQ48sLg3yoL0+GkMCGuJPVjTVPn/o8puC6RR3YfM0bjHoqBg== From: Marcus Folkesson To: Greg Kroah-Hartman , Jonathan Corbet , Felipe Balbi , davem@davemloft.net, Mauro Carvalho Chehab , Andrew Morton , Randy Dunlap , Ruslan Bilovol , Thomas Gleixner , Kate Stewart Cc: linux-usb@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Marcus Folkesson Subject: [PATCH v3 1/3] usb: gadget: ccid: add support for USB CCID Gadget Device Date: Tue, 29 May 2018 20:50:19 +0200 Message-Id: <20180529185021.13738-1-marcus.folkesson@gmail.com> X-Mailer: git-send-email 2.16.2 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Chip Card Interface Device (CCID) protocol is a USB protocol that allows a smartcard device to be connected to a computer via a card reader using a standard USB interface, without the need for each manufacturer of smartcards to provide its own reader or protocol. This gadget driver makes Linux show up as a CCID device to the host and let a userspace daemon act as the smartcard. This is useful when the Linux gadget itself should act as a cryptographic device or forward APDUs to an embedded smartcard device. Signed-off-by: Marcus Folkesson --- Notes: v3: - fix sparse warnings reported by kbuild test robot v2: - add the missing changelog text drivers/usb/gadget/Kconfig | 17 + drivers/usb/gadget/function/Makefile | 1 + drivers/usb/gadget/function/f_ccid.c | 993 +++++++++++++++++++++++++++++++++++ drivers/usb/gadget/function/f_ccid.h | 91 ++++ include/uapi/linux/usb/ccid.h | 93 ++++ 5 files changed, 1195 insertions(+) create mode 100644 drivers/usb/gadget/function/f_ccid.c create mode 100644 drivers/usb/gadget/function/f_ccid.h create mode 100644 include/uapi/linux/usb/ccid.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 31cce7805eb2..bdebdf1ffa2b 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -149,6 +149,9 @@ config USB_LIBCOMPOSITE config USB_F_ACM tristate +config USB_F_CCID + tristate + config USB_F_SS_LB tristate @@ -248,6 +251,20 @@ config USB_CONFIGFS_ACM ACM serial link. This function can be used to interoperate with MS-Windows hosts or with the Linux-USB "cdc-acm" driver. +config USB_CONFIGFS_CCID + bool "Chip Card Interface Device (CCID)" + depends on USB_CONFIGFS + select USB_F_CCID + help + The CCID function driver provides generic emulation of a + Chip Card Interface Device (CCID). + + You will need a user space server talking to /dev/ccidg*, + since the kernel itself does not implement CCID/TPDU/APDU + protocol. + + For more information, see Documentation/usb/gadget_ccid.rst. + config USB_CONFIGFS_OBEX bool "Object Exchange Model (CDC OBEX)" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 5d3a6cf02218..629851009e1a 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -9,6 +9,7 @@ ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/ # USB Functions usb_f_acm-y := f_acm.o obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o +obj-$(CONFIG_USB_F_CCID) += f_ccid.o usb_f_ss_lb-y := f_loopback.o f_sourcesink.o obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o obj-$(CONFIG_USB_U_SERIAL) += u_serial.o diff --git a/drivers/usb/gadget/function/f_ccid.c b/drivers/usb/gadget/function/f_ccid.c new file mode 100644 index 000000000000..47fb229a06db --- /dev/null +++ b/drivers/usb/gadget/function/f_ccid.c @@ -0,0 +1,993 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * f_ccid.c -- Chip Card Interface Device (CCID) function Driver + * + * Copyright (C) 2018 Marcus Folkesson + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f_ccid.h" +#include "u_f.h" + +/* Number of tx requests to allocate */ +#define N_TX_REQS 4 + +/* Maximum number of devices */ +#define CCID_MINORS 4 + +struct ccidg_bulk_dev { + atomic_t is_open; + atomic_t rx_req_busy; + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct usb_request *rx_req; + atomic_t rx_done; + struct list_head tx_idle; +}; + +struct f_ccidg { + struct usb_function_instance func_inst; + struct usb_function function; + spinlock_t lock; + atomic_t online; + + /* Character device */ + struct cdev cdev; + int minor; + + /* Dynamic attributes */ + u32 features; + u32 protocols; + u8 pinsupport; + u8 nslots; + u8 lcdlayout; + + /* Endpoints */ + struct usb_ep *in; + struct usb_ep *out; + struct ccidg_bulk_dev bulk_dev; +}; + +/* Interface Descriptor: */ +static struct usb_interface_descriptor ccid_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CSCID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, +}; + +/* CCID Class Descriptor */ +static struct ccid_class_descriptor ccid_class_desc = { + .bLength = sizeof(ccid_class_desc), + .bDescriptorType = CCID_DECRIPTOR_TYPE, + .bcdCCID = cpu_to_le16(CCID1_10), + /* .bMaxSlotIndex = DYNAMIC */ + .bVoltageSupport = CCID_VOLTS_3_0, + /* .dwProtocols = DYNAMIC */ + .dwDefaultClock = cpu_to_le32(3580), + .dwMaximumClock = cpu_to_le32(3580), + .bNumClockSupported = 0, + .dwDataRate = cpu_to_le32(9600), + .dwMaxDataRate = cpu_to_le32(9600), + .bNumDataRatesSupported = 0, + .dwMaxIFSD = 0, + .dwSynchProtocols = 0, + .dwMechanical = 0, + /* .dwFeatures = DYNAMIC */ + + /* extended APDU level Message Length */ + .dwMaxCCIDMessageLength = cpu_to_le32(0x200), + .bClassGetResponse = 0x0, + .bClassEnvelope = 0x0, + /* .wLcdLayout = DYNAMIC */ + /* .bPINSupport = DYNAMIC */ + .bMaxCCIDBusySlots = 1 +}; + +/* Full speed support: */ +static struct usb_endpoint_descriptor ccid_fs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_endpoint_descriptor ccid_fs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_descriptor_header *ccid_fs_descs[] = { + (struct usb_descriptor_header *) &ccid_interface_desc, + (struct usb_descriptor_header *) &ccid_class_desc, + (struct usb_descriptor_header *) &ccid_fs_in_desc, + (struct usb_descriptor_header *) &ccid_fs_out_desc, + NULL, +}; + +/* High speed support: */ +static struct usb_endpoint_descriptor ccid_hs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor ccid_hs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_descriptor_header *ccid_hs_descs[] = { + (struct usb_descriptor_header *) &ccid_interface_desc, + (struct usb_descriptor_header *) &ccid_class_desc, + (struct usb_descriptor_header *) &ccid_hs_in_desc, + (struct usb_descriptor_header *) &ccid_hs_out_desc, + NULL, +}; + +static DEFINE_IDA(ccidg_ida); +static int major; +static DEFINE_MUTEX(ccidg_ida_lock); /* protects access to ccidg_ida */ +static struct class *ccidg_class; + +static inline struct f_ccidg_opts *to_f_ccidg_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_ccidg_opts, + func_inst.group); +} + +static inline struct f_ccidg *func_to_ccidg(struct usb_function *f) +{ + return container_of(f, struct f_ccidg, function); +} + +static inline int ccidg_get_minor(void) +{ + int ret; + + ret = ida_simple_get(&ccidg_ida, 0, 0, GFP_KERNEL); + if (ret >= CCID_MINORS) { + ida_simple_remove(&ccidg_ida, ret); + ret = -ENODEV; + } + + return ret; +} + +static inline void ccidg_put_minor(int minor) +{ + ida_simple_remove(&ccidg_ida, minor); +} + +static int ccidg_setup(void) +{ + int ret; + dev_t dev; + + ccidg_class = class_create(THIS_MODULE, "ccidg"); + if (IS_ERR(ccidg_class)) { + ccidg_class = NULL; + return PTR_ERR(ccidg_class); + } + + ret = alloc_chrdev_region(&dev, 0, CCID_MINORS, "ccidg"); + if (ret) { + class_destroy(ccidg_class); + ccidg_class = NULL; + return ret; + } + + major = MAJOR(dev); + + return 0; +} + +static void ccidg_cleanup(void) +{ + if (major) { + unregister_chrdev_region(MKDEV(major, 0), CCID_MINORS); + major = 0; + } + + class_destroy(ccidg_class); + ccidg_class = NULL; +} + +static void ccidg_attr_release(struct config_item *item) +{ + struct f_ccidg_opts *opts = to_f_ccidg_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations ccidg_item_ops = { + .release = ccidg_attr_release, +}; + +#define F_CCIDG_OPT(name, prec, limit) \ +static ssize_t f_ccidg_opts_##name##_show(struct config_item *item, char *page)\ +{ \ + struct f_ccidg_opts *opts = to_f_ccidg_opts(item); \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%x\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_ccidg_opts_##name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct f_ccidg_opts *opts = to_f_ccidg_opts(item); \ + int ret; \ + u##prec num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou##prec(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + opts->name = num; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +CONFIGFS_ATTR(f_ccidg_opts_, name) + +F_CCIDG_OPT(features, 32, 0xffffffff); +F_CCIDG_OPT(protocols, 32, 0x03); +F_CCIDG_OPT(pinsupport, 8, 0x03); +F_CCIDG_OPT(lcdlayout, 16, 0xffff); +F_CCIDG_OPT(nslots, 8, 0xff); + +static struct configfs_attribute *ccidg_attrs[] = { + &f_ccidg_opts_attr_features, + &f_ccidg_opts_attr_protocols, + &f_ccidg_opts_attr_pinsupport, + &f_ccidg_opts_attr_lcdlayout, + &f_ccidg_opts_attr_nslots, + NULL, +}; + +static struct config_item_type ccidg_func_type = { + .ct_item_ops = &ccidg_item_ops, + .ct_attrs = ccidg_attrs, + .ct_owner = THIS_MODULE, +}; + +static void ccidg_req_put(struct f_ccidg *ccidg, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&ccidg->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&ccidg->lock, flags); +} + +static struct usb_request *ccidg_req_get(struct f_ccidg *ccidg, + struct list_head *head) +{ + unsigned long flags; + struct usb_request *req = NULL; + + spin_lock_irqsave(&ccidg->lock, flags); + if (!list_empty(head)) { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&ccidg->lock, flags); + + return req; +} + +static void ccidg_bulk_complete_tx(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ccidg *ccidg = (struct f_ccidg *)ep->driver_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_composite_dev *cdev = ccidg->function.config->cdev; + + switch (req->status) { + default: + VDBG(cdev, "ccid: tx err %d\n", req->status); + /* FALLTHROUGH */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + break; + case 0: + break; + } + + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + wake_up(&bulk_dev->write_wq); +} + +static void ccidg_bulk_complete_rx(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ccidg *ccidg = (struct f_ccidg *)ep->driver_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_composite_dev *cdev = ccidg->function.config->cdev; + + switch (req->status) { + + /* normal completion */ + case 0: + /* We only cares about packets with nonzero length */ + if (req->actual > 0) + atomic_set(&bulk_dev->rx_done, 1); + break; + + /* software-driven interface shutdown */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + VDBG(cdev, "ccid: rx shutdown, code %d\n", req->status); + break; + + /* for hardware automagic (such as pxa) */ + case -ECONNABORTED: /* endpoint reset */ + DBG(cdev, "ccid: rx %s reset\n", ep->name); + break; + + /* data overrun */ + case -EOVERFLOW: + /* FALLTHROUGH */ + default: + DBG(cdev, "ccid: rx status %d\n", req->status); + break; + } + + wake_up(&bulk_dev->read_wq); +} + +static struct usb_request * +ccidg_request_alloc(struct usb_ep *ep, unsigned int len) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (!req) + return ERR_PTR(-ENOMEM); + + req->length = len; + req->buf = kmalloc(len, GFP_ATOMIC); + if (req->buf == NULL) { + usb_ep_free_request(ep, req); + return ERR_PTR(-ENOMEM); + } + + return req; +} + +static void ccidg_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static int ccidg_function_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_ccidg *ccidg = container_of(f, struct f_ccidg, function); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int ret = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + if (!atomic_read(&ccidg->online)) + return -ENOTCONN; + + switch (ctrl->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_CLASS: + { + switch (ctrl->bRequest) { + case CCIDGENERICREQ_GET_CLOCK_FREQUENCIES: + if (w_length > sizeof(ccid_class_desc.dwDefaultClock)) + break; + + *(__le32 *) req->buf = ccid_class_desc.dwDefaultClock; + ret = sizeof(ccid_class_desc.dwDefaultClock); + break; + + case CCIDGENERICREQ_GET_DATA_RATES: + if (w_length > sizeof(ccid_class_desc.dwDataRate)) + break; + + *(__le32 *) req->buf = ccid_class_desc.dwDataRate; + ret = sizeof(ccid_class_desc.dwDataRate); + break; + + default: + VDBG(f->config->cdev, + "ccid: invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + } + } + + /* responded with data transfer or status phase? */ + if (ret >= 0) { + VDBG(f->config->cdev, "ccid: req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + req->length = ret; + ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (ret < 0) + ERROR(f->config->cdev, + "ccid: ep0 enqueue err %d\n", ret); + } + + return ret; +} + +static void ccidg_function_disable(struct usb_function *f) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req; + + /* Disable endpoints */ + usb_ep_disable(ccidg->in); + usb_ep_disable(ccidg->out); + + /* Free endpoint related requests */ + if (!atomic_read(&bulk_dev->rx_req_busy)) + ccidg_request_free(bulk_dev->rx_req, ccidg->out); + while ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle))) + ccidg_request_free(req, ccidg->in); + + atomic_set(&ccidg->online, 0); + + /* Wake up threads */ + wake_up(&bulk_dev->write_wq); + wake_up(&bulk_dev->read_wq); +} + +static int ccidg_start_ep(struct f_ccidg *ccidg, struct usb_function *f, + struct usb_ep *ep) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + usb_ep_disable(ep); + + ret = config_ep_by_speed(cdev->gadget, f, ep); + if (ret) { + ERROR(cdev, "ccid: can't configure %s: %d\n", ep->name, ret); + return ret; + } + + ret = usb_ep_enable(ep); + if (ret) { + ERROR(cdev, "ccid: can't start %s: %d\n", ep->name, ret); + return ret; + } + + ep->driver_data = ccidg; + + return ret; +} + +static int ccidg_function_set_alt(struct usb_function *f, + unsigned int intf, unsigned int alt) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req; + int ret; + int i; + + /* Allocate requests for our endpoints */ + req = ccidg_request_alloc(ccidg->out, + sizeof(struct ccidg_bulk_out_header)); + if (IS_ERR(req)) { + ERROR(cdev, "ccid: uname to allocate memory for out req\n"); + return PTR_ERR(req); + } + req->complete = ccidg_bulk_complete_rx; + req->context = ccidg; + bulk_dev->rx_req = req; + + /* Allocate bunch of in requests */ + for (i = 0; i < N_TX_REQS; i++) { + req = ccidg_request_alloc(ccidg->in, + sizeof(struct ccidg_bulk_in_header)); + + if (IS_ERR(req)) { + ret = PTR_ERR(req); + ERROR(cdev, + "ccid: uname to allocate memory for in req\n"); + goto free_bulk_out; + } + req->complete = ccidg_bulk_complete_tx; + req->context = ccidg; + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + } + + /* choose the descriptors and enable endpoints */ + ret = ccidg_start_ep(ccidg, f, ccidg->in); + if (ret) + goto free_bulk_in; + + ret = ccidg_start_ep(ccidg, f, ccidg->out); + if (ret) + goto disable_ep_in; + + atomic_set(&ccidg->online, 1); + return ret; + +disable_ep_in: + usb_ep_disable(ccidg->in); +free_bulk_in: + while ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle))) + ccidg_request_free(req, ccidg->in); +free_bulk_out: + ccidg_request_free(bulk_dev->rx_req, ccidg->out); + return ret; +} + +static int ccidg_bulk_open(struct inode *inode, struct file *file) +{ + struct f_ccidg *ccidg; + struct ccidg_bulk_dev *bulk_dev; + + ccidg = container_of(inode->i_cdev, struct f_ccidg, cdev); + bulk_dev = &ccidg->bulk_dev; + + if (!atomic_read(&ccidg->online)) { + DBG(ccidg->function.config->cdev, "ccid: device not online\n"); + return -ENODEV; + } + + if (atomic_read(&bulk_dev->is_open)) { + DBG(ccidg->function.config->cdev, + "ccid: device already opened\n"); + return -EBUSY; + } + + atomic_set(&bulk_dev->is_open, 1); + + file->private_data = ccidg; + + return 0; +} + +static int ccidg_bulk_release(struct inode *inode, struct file *file) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + + atomic_set(&bulk_dev->is_open, 0); + return 0; +} + +static ssize_t ccidg_bulk_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req; + int r = count, xfer; + int ret; + + /* Make sure we have enough space for a whole package */ + if (count < sizeof(struct ccidg_bulk_out_header)) { + DBG(ccidg->function.config->cdev, + "ccid: too small buffer size. %zu provided, need at least %zu\n", + count, sizeof(struct ccidg_bulk_out_header)); + return -ENOMEM; + } + + if (!atomic_read(&ccidg->online)) + return -ENODEV; + + /* queue a request */ + req = bulk_dev->rx_req; + req->length = count; + atomic_set(&bulk_dev->rx_done, 0); + + ret = usb_ep_queue(ccidg->out, req, GFP_KERNEL); + if (ret < 0) { + ERROR(ccidg->function.config->cdev, + "ccid: usb ep queue failed\n"); + return -EIO; + } + + if (!atomic_read(&bulk_dev->rx_done) && + file->f_flags & (O_NONBLOCK | O_NDELAY)) + return -EAGAIN; + + /* wait for a request to complete */ + ret = wait_event_interruptible(bulk_dev->read_wq, + atomic_read(&bulk_dev->rx_done) || + !atomic_read(&ccidg->online)); + if (ret < 0) { + usb_ep_dequeue(ccidg->out, req); + return -ERESTARTSYS; + } + + /* Still online? */ + if (!atomic_read(&ccidg->online)) + return -ENODEV; + + atomic_set(&bulk_dev->rx_req_busy, 1); + xfer = (req->actual < count) ? req->actual : count; + + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + + atomic_set(&bulk_dev->rx_req_busy, 0); + if (!atomic_read(&ccidg->online)) { + ccidg_request_free(bulk_dev->rx_req, ccidg->out); + return -ENODEV; + } + + return xfer; +} + +static ssize_t ccidg_bulk_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req = NULL; + int ret; + + /* Are we online? */ + if (!atomic_read(&ccidg->online)) + return -ENODEV; + + /* Avoid Zero Length Packets (ZLP) */ + if (!count) + return 0; + + /* Make sure we have enough space for a whole package */ + if (count > sizeof(struct ccidg_bulk_out_header)) { + DBG(ccidg->function.config->cdev, + "ccid: too much data. %zu provided, but we can only handle %zu\n", + count, sizeof(struct ccidg_bulk_out_header)); + return -ENOMEM; + } + + if (list_empty(&bulk_dev->tx_idle) && + file->f_flags & (O_NONBLOCK | O_NDELAY)) + return -EAGAIN; + + /* get an idle tx request to use */ + ret = wait_event_interruptible(bulk_dev->write_wq, + ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle)))); + + if (ret < 0) + return -ERESTARTSYS; + + if (copy_from_user(req->buf, buf, count)) { + if (!atomic_read(&ccidg->online)) { + ccidg_request_free(req, ccidg->in); + return -ENODEV; + } else { + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + return -EFAULT; + } + } + + req->length = count; + ret = usb_ep_queue(ccidg->in, req, GFP_KERNEL); + if (ret < 0) { + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + + if (!atomic_read(&ccidg->online)) { + /* Free up all requests if we are not online */ + while ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle))) + ccidg_request_free(req, ccidg->in); + + return -ENODEV; + } + return -EIO; + } + + return count; +} + +static __poll_t ccidg_bulk_poll(struct file *file, poll_table * wait) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + __poll_t ret = 0; + + poll_wait(file, &bulk_dev->read_wq, wait); + poll_wait(file, &bulk_dev->write_wq, wait); + + if (list_empty(&bulk_dev->tx_idle)) + ret |= EPOLLOUT | EPOLLWRNORM; + + if (atomic_read(&bulk_dev->rx_done)) + ret |= EPOLLIN | EPOLLRDNORM; + + return ret; +} + +static const struct file_operations f_ccidg_fops = { + .owner = THIS_MODULE, + .read = ccidg_bulk_read, + .write = ccidg_bulk_write, + .open = ccidg_bulk_open, + .poll = ccidg_bulk_poll, + .release = ccidg_bulk_release, +}; + +static int ccidg_bulk_device_init(struct f_ccidg *dev) +{ + struct ccidg_bulk_dev *bulk_dev = &dev->bulk_dev; + + init_waitqueue_head(&bulk_dev->read_wq); + init_waitqueue_head(&bulk_dev->write_wq); + INIT_LIST_HEAD(&bulk_dev->tx_idle); + + return 0; +} + +static void ccidg_function_free(struct usb_function *f) +{ + struct f_ccidg *ccidg; + struct f_ccidg_opts *opts; + + ccidg = func_to_ccidg(f); + opts = container_of(f->fi, struct f_ccidg_opts, func_inst); + + kfree(ccidg); + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); +} + +static void ccidg_function_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + + device_destroy(ccidg_class, MKDEV(major, ccidg->minor)); + cdev_del(&ccidg->cdev); + + /* disable/free request and end point */ + usb_free_all_descriptors(f); +} + +static int ccidg_function_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + struct usb_ep *ep; + struct usb_composite_dev *cdev = c->cdev; + struct device *device; + dev_t dev; + int ifc_id; + int ret; + + /* allocate instance-specific interface IDs, and patch descriptors */ + ifc_id = usb_interface_id(c, f); + if (ifc_id < 0) { + ERROR(cdev, "ccid: unable to allocate ifc id, err:%d\n", + ifc_id); + return ifc_id; + } + ccid_interface_desc.bInterfaceNumber = ifc_id; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_in_desc); + if (!ep) { + ERROR(cdev, "ccid: usb epin autoconfig failed\n"); + ret = -ENODEV; + goto ep_auto_in_fail; + } + ccidg->in = ep; + ep->driver_data = ccidg; + + ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_out_desc); + if (!ep) { + ERROR(cdev, "ccid: usb epout autoconfig failed\n"); + ret = -ENODEV; + goto ep_auto_out_fail; + } + ccidg->out = ep; + ep->driver_data = ccidg; + + /* set descriptor dynamic values */ + ccid_class_desc.dwFeatures = cpu_to_le32(ccidg->features); + ccid_class_desc.bPINSupport = ccidg->pinsupport; + ccid_class_desc.wLcdLayout = cpu_to_le16(ccidg->lcdlayout); + ccid_class_desc.bMaxSlotIndex = ccidg->nslots; + ccid_class_desc.dwProtocols = cpu_to_le32(ccidg->protocols); + + if (ccidg->protocols == CCID_PROTOCOL_NOT_SEL) { + ccidg->protocols = CCID_PROTOCOL_T0 | CCID_PROTOCOL_T1; + INFO(ccidg->function.config->cdev, + "ccid: No protocol selected. Support both T0 and T1.\n"); + } + + + ccid_hs_in_desc.bEndpointAddress = + ccid_fs_in_desc.bEndpointAddress; + ccid_hs_out_desc.bEndpointAddress = + ccid_fs_out_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, ccid_fs_descs, + ccid_hs_descs, NULL, NULL); + if (ret) + goto ep_auto_out_fail; + + /* create char device */ + cdev_init(&ccidg->cdev, &f_ccidg_fops); + dev = MKDEV(major, ccidg->minor); + ret = cdev_add(&ccidg->cdev, dev, 1); + if (ret) + goto fail_free_descs; + + device = device_create(ccidg_class, NULL, dev, NULL, + "%s%d", "ccidg", ccidg->minor); + if (IS_ERR(device)) { + ret = PTR_ERR(device); + goto del; + } + + return 0; + +del: + cdev_del(&ccidg->cdev); +fail_free_descs: + usb_free_all_descriptors(f); +ep_auto_out_fail: + ccidg->out->driver_data = NULL; + ccidg->out = NULL; +ep_auto_in_fail: + ccidg->in->driver_data = NULL; + ccidg->in = NULL; + ERROR(f->config->cdev, "ccidg_bind FAILED\n"); + + return ret; +} + +static struct usb_function *ccidg_alloc(struct usb_function_instance *fi) +{ + struct f_ccidg *ccidg; + struct f_ccidg_opts *opts; + int ret; + + ccidg = kzalloc(sizeof(*ccidg), GFP_KERNEL); + if (!ccidg) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&ccidg->lock); + + ret = ccidg_bulk_device_init(ccidg); + if (ret) { + kfree(ccidg); + return ERR_PTR(ret); + } + + opts = container_of(fi, struct f_ccidg_opts, func_inst); + + mutex_lock(&opts->lock); + ++opts->refcnt; + + ccidg->minor = opts->minor; + ccidg->features = opts->features; + ccidg->protocols = opts->protocols; + ccidg->pinsupport = opts->pinsupport; + ccidg->nslots = opts->nslots; + mutex_unlock(&opts->lock); + + ccidg->function.name = "ccid"; + ccidg->function.bind = ccidg_function_bind; + ccidg->function.unbind = ccidg_function_unbind; + ccidg->function.set_alt = ccidg_function_set_alt; + ccidg->function.disable = ccidg_function_disable; + ccidg->function.setup = ccidg_function_setup; + ccidg->function.free_func = ccidg_function_free; + + return &ccidg->function; +} + +static void ccidg_free_inst(struct usb_function_instance *f) +{ + struct f_ccidg_opts *opts; + + opts = container_of(f, struct f_ccidg_opts, func_inst); + mutex_lock(&ccidg_ida_lock); + + ccidg_put_minor(opts->minor); + if (ida_is_empty(&ccidg_ida)) + ccidg_cleanup(); + + mutex_unlock(&ccidg_ida_lock); + + kfree(opts); +} + +static struct usb_function_instance *ccidg_alloc_inst(void) +{ + struct f_ccidg_opts *opts; + struct usb_function_instance *ret; + int status = 0; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = ccidg_free_inst; + ret = &opts->func_inst; + + mutex_lock(&ccidg_ida_lock); + + if (ida_is_empty(&ccidg_ida)) { + status = ccidg_setup(); + if (status) { + ret = ERR_PTR(status); + kfree(opts); + goto unlock; + } + } + + opts->minor = ccidg_get_minor(); + if (opts->minor < 0) { + ret = ERR_PTR(opts->minor); + kfree(opts); + if (ida_is_empty(&ccidg_ida)) + ccidg_cleanup(); + goto unlock; + } + + config_group_init_type_name(&opts->func_inst.group, + "", &ccidg_func_type); + +unlock: + mutex_unlock(&ccidg_ida_lock); + return ret; +} + +DECLARE_USB_FUNCTION_INIT(ccid, ccidg_alloc_inst, ccidg_alloc); + +MODULE_DESCRIPTION("USB CCID Gadget driver"); +MODULE_AUTHOR("Marcus Folkesson "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/function/f_ccid.h b/drivers/usb/gadget/function/f_ccid.h new file mode 100644 index 000000000000..f1053ec5c4d9 --- /dev/null +++ b/drivers/usb/gadget/function/f_ccid.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Marcus Folkesson + */ + +#ifndef F_CCID_H +#define F_CCID_H + +#define CCID1_10 0x0110 +#define CCID_DECRIPTOR_TYPE 0x21 +#define ABDATA_SIZE 512 +#define SMART_CARD_DEVICE_CLASS 0x0B + +/* CCID Class Specific Request */ +#define CCIDGENERICREQ_ABORT 0x01 +#define CCIDGENERICREQ_GET_CLOCK_FREQUENCIES 0x02 +#define CCIDGENERICREQ_GET_DATA_RATES 0x03 + +/* Supported voltages */ +#define CCID_VOLTS_AUTO 0x00 +#define CCID_VOLTS_5_0 0x01 +#define CCID_VOLTS_3_0 0x02 +#define CCID_VOLTS_1_8 0x03 + +struct f_ccidg_opts { + struct usb_function_instance func_inst; + int minor; + __u32 features; + __u32 protocols; + __u8 pinsupport; + __u8 nslots; + __u8 lcdlayout; + + /* + * Protect the data form concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +struct ccidg_bulk_in_header { + __u8 bMessageType; + __le32 wLength; + __u8 bSlot; + __u8 bSeq; + __u8 bStatus; + __u8 bError; + __u8 bSpecific; + __u8 abData[ABDATA_SIZE]; + __u8 bSizeToSend; +} __packed; + +struct ccidg_bulk_out_header { + __u8 bMessageType; + __le32 wLength; + __u8 bSlot; + __u8 bSeq; + __u8 bSpecific_0; + __u8 bSpecific_1; + __u8 bSpecific_2; + __u8 APDU[ABDATA_SIZE]; +} __packed; + +struct ccid_class_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __le16 bcdCCID; + __u8 bMaxSlotIndex; + __u8 bVoltageSupport; + __le32 dwProtocols; + __le32 dwDefaultClock; + __le32 dwMaximumClock; + __u8 bNumClockSupported; + __le32 dwDataRate; + __le32 dwMaxDataRate; + __u8 bNumDataRatesSupported; + __le32 dwMaxIFSD; + __le32 dwSynchProtocols; + __le32 dwMechanical; + __le32 dwFeatures; + __le32 dwMaxCCIDMessageLength; + __u8 bClassGetResponse; + __u8 bClassEnvelope; + __le16 wLcdLayout; + __u8 bPINSupport; + __u8 bMaxCCIDBusySlots; +} __packed; + + +#endif diff --git a/include/uapi/linux/usb/ccid.h b/include/uapi/linux/usb/ccid.h new file mode 100644 index 000000000000..517897201563 --- /dev/null +++ b/include/uapi/linux/usb/ccid.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Marcus Folkesson + * + * This file holds USB constants defined by the CCID Specification. + */ + +#ifndef CCID_H +#define CCID_H + +/* Slot error register when bmCommandStatus = 1 */ +#define CCID_CMD_ABORTED 0xFF +#define CCID_ICC_MUTE 0xFE +#define CCID_XFR_PARITY_ERROR 0xFD +#define CCID_XFR_OVERRUN 0xFC +#define CCID_HW_ERROR 0xFB +#define CCID_BAD_ATR_TS 0xF8 +#define CCID_BAD_ATR_TCK 0xF7 +#define CCID_ICC_PROTOCOL_NOT_SUPPORTED 0xF6 +#define CCID_ICC_CLASS_NOT_SUPPORTED 0xF5 +#define CCID_PROCEDURE_BYTE_CONFLICT 0xF4 +#define CCID_DEACTIVATED_PROTOCOL 0xF3 +#define CCID_BUSY_WITH_AUTO_SEQUENCE 0xF2 +#define CCID_PIN_TIMEOUT 0xF0 +#define CCID_PIN_CANCELLED 0xEF +#define CCID_CMD_SLOT_BUSY 0xE0 + +/* PC to RDR messages (bulk out) */ +#define CCID_PC_TO_RDR_ICCPOWERON 0x62 +#define CCID_PC_TO_RDR_ICCPOWEROFF 0x63 +#define CCID_PC_TO_RDR_GETSLOTSTATUS 0x65 +#define CCID_PC_TO_RDR_XFRBLOCK 0x6F +#define CCID_PC_TO_RDR_GETPARAMETERS 0x6C +#define CCID_PC_TO_RDR_RESETPARAMETERS 0x6D +#define CCID_PC_TO_RDR_SETPARAMETERS 0x61 +#define CCID_PC_TO_RDR_ESCAPE 0x6B +#define CCID_PC_TO_RDR_ICCCLOCK 0x6E +#define CCID_PC_TO_RDR_T0APDU 0x6A +#define CCID_PC_TO_RDR_SECURE 0x69 +#define CCID_PC_TO_RDR_MECHANICAL 0x71 +#define CCID_PC_TO_RDR_ABORT 0x72 +#define CCID_PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY 0x73 + +/* RDR to PC messages (bulk in) */ +#define CCID_RDR_TO_PC_DATABLOCK 0x80 +#define CCID_RDR_TO_PC_SLOTSTATUS 0x81 +#define CCID_RDR_TO_PC_PARAMETERS 0x82 +#define CCID_RDR_TO_PC_ESCAPE 0x83 +#define CCID_RDR_TO_PC_DATARATEANDCLOCKFREQUENCY 0x84 + +/* Class Features */ + +/* No special characteristics */ +#define CCID_FEATURES_NADA 0x00000000 +/* Automatic parameter configuration based on ATR data */ +#define CCID_FEATURES_AUTO_PCONF 0x00000002 +/* Automatic activation of ICC on inserting */ +#define CCID_FEATURES_AUTO_ACTIV 0x00000004 +/* Automatic ICC voltage selection */ +#define CCID_FEATURES_AUTO_VOLT 0x00000008 +/* Automatic ICC clock frequency change */ +#define CCID_FEATURES_AUTO_CLOCK 0x00000010 +/* Automatic baud rate change */ +#define CCID_FEATURES_AUTO_BAUD 0x00000020 +/*Automatic parameters negotiation made by the CCID */ +#define CCID_FEATURES_AUTO_PNEGO 0x00000040 +/* Automatic PPS made by the CCID according to the active parameters */ +#define CCID_FEATURES_AUTO_PPS 0x00000080 +/* CCID can set ICC in clock stop mode */ +#define CCID_FEATURES_ICCSTOP 0x00000100 +/* NAD value other than 00 accepted (T=1 protocol in use) */ +#define CCID_FEATURES_NAD 0x00000200 +/* Automatic IFSD exchange as first exchange (T=1 protocol in use) */ +#define CCID_FEATURES_AUTO_IFSD 0x00000400 +/* TPDU level exchanges with CCID */ +#define CCID_FEATURES_EXC_TPDU 0x00010000 +/* Short APDU level exchange with CCID */ +#define CCID_FEATURES_EXC_SAPDU 0x00020000 +/* Short and Extended APDU level exchange with CCID */ +#define CCID_FEATURES_EXC_APDU 0x00040000 +/* USB Wake up signaling supported on card insertion and removal */ +#define CCID_FEATURES_WAKEUP 0x00100000 + +/* Supported protocols */ +#define CCID_PROTOCOL_NOT_SEL 0x00 +#define CCID_PROTOCOL_T0 0x01 +#define CCID_PROTOCOL_T1 0x02 + +#define CCID_PINSUPOORT_NONE 0x00 +#define CCID_PINSUPOORT_VERIFICATION (1 << 1) +#define CCID_PINSUPOORT_MODIFICATION (1 << 2) + +#endif -- 2.16.2