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 5EDB47D043 for ; Tue, 26 Jun 2018 08:14:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752777AbeFZIOw (ORCPT ); Tue, 26 Jun 2018 04:14:52 -0400 Received: from mail-lf0-f65.google.com ([209.85.215.65]:37488 "EHLO mail-lf0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752708AbeFZIOl (ORCPT ); Tue, 26 Jun 2018 04:14:41 -0400 Received: by mail-lf0-f65.google.com with SMTP id g21-v6so16653813lfb.4; Tue, 26 Jun 2018 01:14:39 -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=Efa3XfD1ZMeOnSjqdTjFSY0MvTFWNgy1YAiIREQd5+U=; b=VO5VkWt/0D/jRF/bPdu5i5FK8o3jx4r7VIkNlELw/VvVKENXaGjrtsRgGYBsUBYLgh lyTTaIFCIA8dcCL5K6rKZLBJmcK8C7p0xR9d/jp5wF/jqccxx5rSRjqDOw4YUS676To1 U1Kem7wwLCWCcsTrp39WVEkxCbtgEDmJco+hajCQSRLG95+jATswoiuxUfwz9Gw2tLT8 uCo6E8Du9WCB0+9JjKBh1+cV8hqpIVEvwg1C1na/HAF7ubMQEr1FeruFLsHXbSj9n+ib sbSKpI96gliUBczKS5Vk+1JyMkzDAWkAM1unvRTcQKPM4IZgRUZMb6sXee209/6ECEjS 1KGg== 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=Efa3XfD1ZMeOnSjqdTjFSY0MvTFWNgy1YAiIREQd5+U=; b=hYE5yCd385+x6bwwUfG+8voyNUSJrYYScT6dJjxcQB6iooSIse007kkpgfBN8fQCzh kQxhiTVBAqbOcAJqqBXMA+BI02lrwYHGgdkFMMpaZbKWW5joOec2dqm0L3YoKd5PjFuQ SsqCGZnnck5woj84v+ar7Wjp5gr9sr4+/nU4Dmto2EsQplO6WGZRMvMnUL+p8p1vyeZs aS6yXvn5e1etVMhH84x6hN6RPpLFv+dLH0fb5nmYA9eKnKjbl4rfPxAkyBgXLAg2sJbq OTd9+eKF71K+kUEAOmcPsRKy0s2KQPlBCeKwOEMEJ0hu6RlcbTVbS1n1qVAntGB96rnT pGvQ== X-Gm-Message-State: APt69E3tzz5mdHpmONJORyhVTxYH/fMpUDPKiqI5j0SpgCntETicuSfZ 9nEF5A4wBFdzym9M5eflSws= X-Google-Smtp-Source: AAOMgpcI/9Zf6SSppzESChuvR4W07zkIK99gv1kdX7Ma7XK1TF+I3q1fK3CBa4oGcn9eJE2jGOaXDg== X-Received: by 2002:a19:4a53:: with SMTP id x80-v6mr466836lfa.69.1530000878629; Tue, 26 Jun 2018 01:14:38 -0700 (PDT) Received: from localhost.localdomain (c-2ec26030-74736162.cust.telenor.se. [46.194.96.48]) by smtp.gmail.com with ESMTPSA id p28-v6sm149783lja.13.2018.06.26.01.14.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 26 Jun 2018 01:14:37 -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 v4 1/3] usb: gadget: ccid: add support for USB CCID Gadget Device Date: Tue, 26 Jun 2018 10:14:20 +0200 Message-Id: <20180626081422.19237-1-marcus.folkesson@gmail.com> X-Mailer: git-send-email 2.18.0 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: v4: - fix comments from Andy, Thanks! - add "WITH Linux-syscall-note" in the SPDX identifier for uapi-header 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 | 989 +++++++++++++++++++++++++++ drivers/usb/gadget/function/f_ccid.h | 91 +++ include/uapi/linux/usb/ccid.h | 93 +++ 5 files changed, 1191 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..2a890dca31e1 --- /dev/null +++ b/drivers/usb/gadget/function/f_ccid.c @@ -0,0 +1,989 @@ +// 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 + +#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)) { + ret = PTR_ERR(ccidg_class); + ccidg_class = NULL; + return ret; + } + + 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; + ida_destroy(&ccidg_ida); +} + +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); + req = list_first_entry_or_null(head, struct usb_request, list); + if (req) + 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->buf = kmalloc(len, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + return ERR_PTR(-ENOMEM); + } + req->length = len; + + return req; +} + +static void ccidg_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (!req) + return; + + 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; + + put_unaligned(ccid_class_desc.dwDefaultClock, req->buf); + ret = sizeof(ccid_class_desc.dwDefaultClock); + break; + + case CCIDGENERICREQ_GET_DATA_RATES: + if (w_length > sizeof(ccid_class_desc.dwDataRate)) + break; + + put_unaligned(ccid_class_desc.dwDataRate, req->buf); + 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 0; + +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 ccidg_bulk_dev *bulk_dev; + struct f_ccidg *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; + size_t 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 = min_t(size_t, req->actual, count); + + if (copy_to_user(buf, req->buf, xfer)) + return -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 = func_to_ccidg(f); + struct f_ccidg_opts *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 = container_of(fi, struct f_ccidg_opts, + func_inst); + 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); + } + + 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 = 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..a9f8906f1948 --- /dev/null +++ b/include/uapi/linux/usb/ccid.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * 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 (0 << 0) +#define CCID_PINSUPOORT_VERIFICATION (1 << 1) +#define CCID_PINSUPOORT_MODIFICATION (1 << 2) + +#endif -- 2.18.0 -- 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: [v4,1/3] usb: gadget: ccid: add support for USB CCID Gadget Device From: Marcus Folkesson Message-Id: <20180626081422.19237-1-marcus.folkesson@gmail.com> Date: Tue, 26 Jun 2018 10:14:20 +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 LmNvbT4KLS0tCgpOb3RlczoKICAgIHY0OgogICAgCS0gZml4IGNvbW1lbnRzIGZyb20gQW5keSwg VGhhbmtzIQogICAgCS0gYWRkICJXSVRIIExpbnV4LXN5c2NhbGwtbm90ZSIgaW4gdGhlIFNQRFgg aWRlbnRpZmllciBmb3IgdWFwaS1oZWFkZXIKICAgIHYzOgogICAgCS0gZml4IHNwYXJzZSB3YXJu aW5ncyByZXBvcnRlZCBieSBrYnVpbGQgdGVzdCByb2JvdAogICAgdjI6CiAgICAJLSBhZGQgdGhl IG1pc3NpbmcgY2hhbmdlbG9nIHRleHQKCiBkcml2ZXJzL3VzYi9nYWRnZXQvS2NvbmZpZyAgICAg ICAgICAgfCAgMTcgKwogZHJpdmVycy91c2IvZ2FkZ2V0L2Z1bmN0aW9uL01ha2VmaWxlIHwgICAx ICsKIGRyaXZlcnMvdXNiL2dhZGdldC9mdW5jdGlvbi9mX2NjaWQuYyB8IDk4OSArKysrKysrKysr KysrKysrKysrKysrKysrKysKIGRyaXZlcnMvdXNiL2dhZGdldC9mdW5jdGlvbi9mX2NjaWQuaCB8 ICA5MSArKysKIGluY2x1ZGUvdWFwaS9saW51eC91c2IvY2NpZC5oICAgICAgICB8ICA5MyArKysK IDUgZmlsZXMgY2hhbmdlZCwgMTE5MSBpbnNlcnRpb25zKCspCiBjcmVhdGUgbW9kZSAxMDA2NDQg ZHJpdmVycy91c2IvZ2FkZ2V0L2Z1bmN0aW9uL2ZfY2NpZC5jCiBjcmVhdGUgbW9kZSAxMDA2NDQg ZHJpdmVycy91c2IvZ2FkZ2V0L2Z1bmN0aW9uL2ZfY2NpZC5oCiBjcmVhdGUgbW9kZSAxMDA2NDQg aW5jbHVkZS91YXBpL2xpbnV4L3VzYi9jY2lkLmgKCmRpZmYgLS1naXQgYS9kcml2ZXJzL3VzYi9n YWRnZXQvS2NvbmZpZyBiL2RyaXZlcnMvdXNiL2dhZGdldC9LY29uZmlnCmluZGV4IDMxY2NlNzgw NWViMi4uYmRlYmRmMWZmYTJiIDEwMDY0NAotLS0gYS9kcml2ZXJzL3VzYi9nYWRnZXQvS2NvbmZp ZworKysgYi9kcml2ZXJzL3VzYi9nYWRnZXQvS2NvbmZpZwpAQCAtMTQ5LDYgKzE0OSw5IEBAIGNv bmZpZyBVU0JfTElCQ09NUE9TSVRFCiBjb25maWcgVVNCX0ZfQUNNCiAJdHJpc3RhdGUKIAorY29u ZmlnIFVTQl9GX0NDSUQKKwl0cmlzdGF0ZQorCiBjb25maWcgVVNCX0ZfU1NfTEIKIAl0cmlzdGF0 ZQogCkBAIC0yNDgsNiArMjUxLDIwIEBAIGNvbmZpZyBVU0JfQ09ORklHRlNfQUNNCiAJICBBQ00g c2VyaWFsIGxpbmsuICBUaGlzIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIGludGVyb3BlcmF0ZSB3 aXRoCiAJICBNUy1XaW5kb3dzIGhvc3RzIG9yIHdpdGggdGhlIExpbnV4LVVTQiAiY2RjLWFjbSIg ZHJpdmVyLgogCitjb25maWcgVVNCX0NPTkZJR0ZTX0NDSUQKKwlib29sICJDaGlwIENhcmQgSW50 ZXJmYWNlIERldmljZSAoQ0NJRCkiCisJZGVwZW5kcyBvbiBVU0JfQ09ORklHRlMKKwlzZWxlY3Qg VVNCX0ZfQ0NJRAorCWhlbHAKKwkgIFRoZSBDQ0lEIGZ1bmN0aW9uIGRyaXZlciBwcm92aWRlcyBn ZW5lcmljIGVtdWxhdGlvbiBvZiBhCisJICBDaGlwIENhcmQgSW50ZXJmYWNlIERldmljZSAoQ0NJ RCkuCisKKwkgIFlvdSB3aWxsIG5lZWQgYSB1c2VyIHNwYWNlIHNlcnZlciB0YWxraW5nIHRvIC9k ZXYvY2NpZGcqLAorCSAgc2luY2UgdGhlIGtlcm5lbCBpdHNlbGYgZG9lcyBub3QgaW1wbGVtZW50 IENDSUQvVFBEVS9BUERVCisJICBwcm90b2NvbC4KKworCSAgRm9yIG1vcmUgaW5mb3JtYXRpb24s IHNlZSBEb2N1bWVudGF0aW9uL3VzYi9nYWRnZXRfY2NpZC5yc3QuCisKIGNvbmZpZyBVU0JfQ09O RklHRlNfT0JFWAogCWJvb2wgIk9iamVjdCBFeGNoYW5nZSBNb2RlbCAoQ0RDIE9CRVgpIgogCWRl cGVuZHMgb24gVVNCX0NPTkZJR0ZTCmRpZmYgLS1naXQgYS9kcml2ZXJzL3VzYi9nYWRnZXQvZnVu Y3Rpb24vTWFrZWZpbGUgYi9kcml2ZXJzL3VzYi9nYWRnZXQvZnVuY3Rpb24vTWFrZWZpbGUKaW5k ZXggNWQzYTZjZjAyMjE4Li42Mjk4NTEwMDllMWEgMTAwNjQ0Ci0tLSBhL2RyaXZlcnMvdXNiL2dh ZGdldC9mdW5jdGlvbi9NYWtlZmlsZQorKysgYi9kcml2ZXJzL3VzYi9nYWRnZXQvZnVuY3Rpb24v TWFrZWZpbGUKQEAgLTksNiArOSw3IEBAIGNjZmxhZ3MteQkJCSs9IC1JJChzcmN0cmVlKS9kcml2 ZXJzL3VzYi9nYWRnZXQvdWRjLwogIyBVU0IgRnVuY3Rpb25zCiB1c2JfZl9hY20teQkJCTo9IGZf YWNtLm8KIG9iai0kKENPTkZJR19VU0JfRl9BQ00pCQkrPSB1c2JfZl9hY20ubworb2JqLSQoQ09O RklHX1VTQl9GX0NDSUQpCSs9IGZfY2NpZC5vCiB1c2JfZl9zc19sYi15CQkJOj0gZl9sb29wYmFj ay5vIGZfc291cmNlc2luay5vCiBvYmotJChDT05GSUdfVVNCX0ZfU1NfTEIpCSs9IHVzYl9mX3Nz X2xiLm8KIG9iai0kKENPTkZJR19VU0JfVV9TRVJJQUwpCSs9IHVfc2VyaWFsLm8KZGlmZiAtLWdp dCBhL2RyaXZlcnMvdXNiL2dhZGdldC9mdW5jdGlvbi9mX2NjaWQuYyBiL2RyaXZlcnMvdXNiL2dh ZGdldC9mdW5jdGlvbi9mX2NjaWQuYwpuZXcgZmlsZSBtb2RlIDEwMDY0NAppbmRleCAwMDAwMDAw MDAwMDAuLjJhODkwZGNhMzFlMQotLS0gL2Rldi9udWxsCisrKyBiL2RyaXZlcnMvdXNiL2dhZGdl dC9mdW5jdGlvbi9mX2NjaWQuYwpAQCAtMCwwICsxLDk4OSBAQAorLy8gU1BEWC1MaWNlbnNlLUlk ZW50aWZpZXI6IEdQTC0yLjAKKy8qCisgKiBmX2NjaWQuYyAtLSBDaGlwIENhcmQgSW50ZXJmYWNl IERldmljZSAoQ0NJRCkgZnVuY3Rpb24gRHJpdmVyCisgKgorICogQ29weXJpZ2h0IChDKSAyMDE4 IE1hcmN1cyBGb2xrZXNzb24gPG1hcmN1cy5mb2xrZXNzb25AZ21haWwuY29tPgorICovCisKKyNp bmNsdWRlIDxhc20vdW5hbGlnbmVkLmg+CisjaW5jbHVkZSA8bGludXgvY2Rldi5oPgorI2luY2x1 ZGUgPGxpbnV4L2RldmljZS5oPgorI2luY2x1ZGUgPGxpbnV4L2ZzLmg+CisjaW5jbHVkZSA8bGlu dXgva2VybmVsLmg+CisjaW5jbHVkZSA8bGludXgvbW9kdWxlLmg+CisjaW5jbHVkZSA8bGludXgv cG9sbC5oPgorI2luY2x1ZGUgPGxpbnV4L3NsYWIuaD4KKyNpbmNsdWRlIDxsaW51eC91YWNjZXNz Lmg+CisjaW5jbHVkZSA8bGludXgvdXNiL2NvbXBvc2l0ZS5oPgorI2luY2x1ZGUgPHVhcGkvbGlu dXgvdXNiL2NjaWQuaD4KKworI2luY2x1ZGUgImZfY2NpZC5oIgorI2luY2x1ZGUgInVfZi5oIgor CisvKiBOdW1iZXIgb2YgdHggcmVxdWVzdHMgdG8gYWxsb2NhdGUgKi8KKyNkZWZpbmUgTl9UWF9S RVFTIDQKKworLyogTWF4aW11bSBudW1iZXIgb2YgZGV2aWNlcyAqLworI2RlZmluZSBDQ0lEX01J Tk9SUyA0CisKK3N0cnVjdCBjY2lkZ19idWxrX2RldiB7CisJYXRvbWljX3QgaXNfb3BlbjsKKwlh dG9taWNfdCByeF9yZXFfYnVzeTsKKwl3YWl0X3F1ZXVlX2hlYWRfdCByZWFkX3dxOworCXdhaXRf cXVldWVfaGVhZF90IHdyaXRlX3dxOworCXN0cnVjdCB1c2JfcmVxdWVzdCAqcnhfcmVxOworCWF0 b21pY190IHJ4X2RvbmU7CisJc3RydWN0IGxpc3RfaGVhZCB0eF9pZGxlOworfTsKKworc3RydWN0 IGZfY2NpZGcgeworCXN0cnVjdCB1c2JfZnVuY3Rpb25faW5zdGFuY2UJZnVuY19pbnN0OworCXN0 cnVjdCB1c2JfZnVuY3Rpb24gZnVuY3Rpb247CisJc3BpbmxvY2tfdCBsb2NrOworCWF0b21pY190 IG9ubGluZTsKKworCS8qIENoYXJhY3RlciBkZXZpY2UgKi8KKwlzdHJ1Y3QgY2RldiBjZGV2Owor CWludCBtaW5vcjsKKworCS8qIER5bmFtaWMgYXR0cmlidXRlcyAqLworCXUzMiBmZWF0dXJlczsK Kwl1MzIgcHJvdG9jb2xzOworCXU4IHBpbnN1cHBvcnQ7CisJdTggbnNsb3RzOworCXU4IGxjZGxh eW91dDsKKworCS8qIEVuZHBvaW50cyAqLworCXN0cnVjdCB1c2JfZXAgKmluOworCXN0cnVjdCB1 c2JfZXAgKm91dDsKKwlzdHJ1Y3QgY2NpZGdfYnVsa19kZXYgYnVsa19kZXY7Cit9OworCisvKiBJ bnRlcmZhY2UgRGVzY3JpcHRvcjogKi8KK3N0YXRpYyBzdHJ1Y3QgdXNiX2ludGVyZmFjZV9kZXNj cmlwdG9yIGNjaWRfaW50ZXJmYWNlX2Rlc2MgPSB7CisJLmJMZW5ndGggPQkJVVNCX0RUX0lOVEVS RkFDRV9TSVpFLAorCS5iRGVzY3JpcHRvclR5cGUgPQlVU0JfRFRfSU5URVJGQUNFLAorCS5iTnVt RW5kcG9pbnRzID0JMiwKKwkuYkludGVyZmFjZUNsYXNzID0JVVNCX0NMQVNTX0NTQ0lELAorCS5i SW50ZXJmYWNlU3ViQ2xhc3MgPQkwLAorCS5iSW50ZXJmYWNlUHJvdG9jb2wgPQkwLAorfTsKKwor LyogQ0NJRCBDbGFzcyBEZXNjcmlwdG9yICovCitzdGF0aWMgc3RydWN0IGNjaWRfY2xhc3NfZGVz Y3JpcHRvciBjY2lkX2NsYXNzX2Rlc2MgPSB7CisJLmJMZW5ndGggPQkJc2l6ZW9mKGNjaWRfY2xh c3NfZGVzYyksCisJLmJEZXNjcmlwdG9yVHlwZSA9CUNDSURfREVDUklQVE9SX1RZUEUsCisJLmJj ZENDSUQgPQkJY3B1X3RvX2xlMTYoQ0NJRDFfMTApLAorCS8qIC5iTWF4U2xvdEluZGV4ID0JRFlO QU1JQyAqLworCS5iVm9sdGFnZVN1cHBvcnQgPQlDQ0lEX1ZPTFRTXzNfMCwKKwkvKiAuZHdQcm90 b2NvbHMgPQlEWU5BTUlDICovCisJLmR3RGVmYXVsdENsb2NrID0JY3B1X3RvX2xlMzIoMzU4MCks CisJLmR3TWF4aW11bUNsb2NrID0JY3B1X3RvX2xlMzIoMzU4MCksCisJLmJOdW1DbG9ja1N1cHBv cnRlZCA9CTAsCisJLmR3RGF0YVJhdGUgPQkJY3B1X3RvX2xlMzIoOTYwMCksCisJLmR3TWF4RGF0 YVJhdGUgPQljcHVfdG9fbGUzMig5NjAwKSwKKwkuYk51bURhdGFSYXRlc1N1cHBvcnRlZCA9IDAs CisJLmR3TWF4SUZTRCA9CQkwLAorCS5kd1N5bmNoUHJvdG9jb2xzID0JMCwKKwkuZHdNZWNoYW5p Y2FsID0JCTAsCisJLyogLmR3RmVhdHVyZXMgPQlEWU5BTUlDICovCisKKwkvKiBleHRlbmRlZCBB UERVIGxldmVsIE1lc3NhZ2UgTGVuZ3RoICovCisJLmR3TWF4Q0NJRE1lc3NhZ2VMZW5ndGggPSBj cHVfdG9fbGUzMigweDIwMCksCisJLmJDbGFzc0dldFJlc3BvbnNlID0JMHgwLAorCS5iQ2xhc3NF bnZlbG9wZSA9CTB4MCwKKwkvKiAud0xjZExheW91dCA9CURZTkFNSUMgKi8KKwkvKiAuYlBJTlN1 cHBvcnQgPQlEWU5BTUlDICovCisJLmJNYXhDQ0lEQnVzeVNsb3RzID0JMQorfTsKKworLyogRnVs bCBzcGVlZCBzdXBwb3J0OiAqLworc3RhdGljIHN0cnVjdCB1c2JfZW5kcG9pbnRfZGVzY3JpcHRv ciBjY2lkX2ZzX2luX2Rlc2MgPSB7CisJLmJMZW5ndGggPQkJVVNCX0RUX0VORFBPSU5UX1NJWkUs CisJLmJEZXNjcmlwdG9yVHlwZSA9CVVTQl9EVF9FTkRQT0lOVCwKKwkuYkVuZHBvaW50QWRkcmVz cyA9CVVTQl9ESVJfSU4sCisJLmJtQXR0cmlidXRlcyA9CQlVU0JfRU5EUE9JTlRfWEZFUl9CVUxL LAorCS53TWF4UGFja2V0U2l6ZSAgID0JY3B1X3RvX2xlMTYoNjQpLAorfTsKKworc3RhdGljIHN0 cnVjdCB1c2JfZW5kcG9pbnRfZGVzY3JpcHRvciBjY2lkX2ZzX291dF9kZXNjID0geworCS5iTGVu Z3RoID0JCVVTQl9EVF9FTkRQT0lOVF9TSVpFLAorCS5iRGVzY3JpcHRvclR5cGUgPQlVU0JfRFRf RU5EUE9JTlQsCisJLmJFbmRwb2ludEFkZHJlc3MgPQlVU0JfRElSX09VVCwKKwkuYm1BdHRyaWJ1 dGVzID0JCVVTQl9FTkRQT0lOVF9YRkVSX0JVTEssCisJLndNYXhQYWNrZXRTaXplICAgPQkgY3B1 X3RvX2xlMTYoNjQpLAorfTsKKworc3RhdGljIHN0cnVjdCB1c2JfZGVzY3JpcHRvcl9oZWFkZXIg KmNjaWRfZnNfZGVzY3NbXSA9IHsKKwkoc3RydWN0IHVzYl9kZXNjcmlwdG9yX2hlYWRlciAqKSAm Y2NpZF9pbnRlcmZhY2VfZGVzYywKKwkoc3RydWN0IHVzYl9kZXNjcmlwdG9yX2hlYWRlciAqKSAm Y2NpZF9jbGFzc19kZXNjLAorCShzdHJ1Y3QgdXNiX2Rlc2NyaXB0b3JfaGVhZGVyICopICZjY2lk X2ZzX2luX2Rlc2MsCisJKHN0cnVjdCB1c2JfZGVzY3JpcHRvcl9oZWFkZXIgKikgJmNjaWRfZnNf b3V0X2Rlc2MsCisJTlVMTCwKK307CisKKy8qIEhpZ2ggc3BlZWQgc3VwcG9ydDogKi8KK3N0YXRp YyBzdHJ1Y3QgdXNiX2VuZHBvaW50X2Rlc2NyaXB0b3IgY2NpZF9oc19pbl9kZXNjID0geworCS5i TGVuZ3RoID0JCVVTQl9EVF9FTkRQT0lOVF9TSVpFLAorCS5iRGVzY3JpcHRvclR5cGUgPQlVU0Jf RFRfRU5EUE9JTlQsCisJLmJFbmRwb2ludEFkZHJlc3MgPQlVU0JfRElSX0lOLAorCS5ibUF0dHJp YnV0ZXMgPQkJVVNCX0VORFBPSU5UX1hGRVJfQlVMSywKKwkud01heFBhY2tldFNpemUgPQljcHVf dG9fbGUxNig1MTIpLAorfTsKKworc3RhdGljIHN0cnVjdCB1c2JfZW5kcG9pbnRfZGVzY3JpcHRv ciBjY2lkX2hzX291dF9kZXNjID0geworCS5iTGVuZ3RoID0JCVVTQl9EVF9FTkRQT0lOVF9TSVpF LAorCS5iRGVzY3JpcHRvclR5cGUgPQlVU0JfRFRfRU5EUE9JTlQsCisJLmJFbmRwb2ludEFkZHJl c3MgPQlVU0JfRElSX09VVCwKKwkuYm1BdHRyaWJ1dGVzID0JCVVTQl9FTkRQT0lOVF9YRkVSX0JV TEssCisJLndNYXhQYWNrZXRTaXplID0JY3B1X3RvX2xlMTYoNTEyKSwKK307CisKK3N0YXRpYyBz dHJ1Y3QgdXNiX2Rlc2NyaXB0b3JfaGVhZGVyICpjY2lkX2hzX2Rlc2NzW10gPSB7CisJKHN0cnVj dCB1c2JfZGVzY3JpcHRvcl9oZWFkZXIgKikgJmNjaWRfaW50ZXJmYWNlX2Rlc2MsCisJKHN0cnVj dCB1c2JfZGVzY3JpcHRvcl9oZWFkZXIgKikgJmNjaWRfY2xhc3NfZGVzYywKKwkoc3RydWN0IHVz Yl9kZXNjcmlwdG9yX2hlYWRlciAqKSAmY2NpZF9oc19pbl9kZXNjLAorCShzdHJ1Y3QgdXNiX2Rl c2NyaXB0b3JfaGVhZGVyICopICZjY2lkX2hzX291dF9kZXNjLAorCU5VTEwsCit9OworCitzdGF0 aWMgREVGSU5FX0lEQShjY2lkZ19pZGEpOworc3RhdGljIGludCBtYWpvcjsKK3N0YXRpYyBERUZJ TkVfTVVURVgoY2NpZGdfaWRhX2xvY2spOyAvKiBwcm90ZWN0cyBhY2Nlc3MgdG8gY2NpZGdfaWRh ICovCitzdGF0aWMgc3RydWN0IGNsYXNzICpjY2lkZ19jbGFzczsKKworc3RhdGljIGlubGluZSBz dHJ1Y3QgZl9jY2lkZ19vcHRzICp0b19mX2NjaWRnX29wdHMoc3RydWN0IGNvbmZpZ19pdGVtICpp dGVtKQoreworCXJldHVybiBjb250YWluZXJfb2YodG9fY29uZmlnX2dyb3VwKGl0ZW0pLCBzdHJ1 Y3QgZl9jY2lkZ19vcHRzLAorCQkJICAgIGZ1bmNfaW5zdC5ncm91cCk7Cit9CisKK3N0YXRpYyBp bmxpbmUgc3RydWN0IGZfY2NpZGcgKmZ1bmNfdG9fY2NpZGcoc3RydWN0IHVzYl9mdW5jdGlvbiAq ZikKK3sKKwlyZXR1cm4gY29udGFpbmVyX29mKGYsIHN0cnVjdCBmX2NjaWRnLCBmdW5jdGlvbik7 Cit9CisKK3N0YXRpYyBpbmxpbmUgaW50IGNjaWRnX2dldF9taW5vcih2b2lkKQoreworCWludCBy ZXQ7CisKKwlyZXQgPSBpZGFfc2ltcGxlX2dldCgmY2NpZGdfaWRhLCAwLCAwLCBHRlBfS0VSTkVM KTsKKwlpZiAocmV0ID49IENDSURfTUlOT1JTKSB7CisJCWlkYV9zaW1wbGVfcmVtb3ZlKCZjY2lk Z19pZGEsIHJldCk7CisJCXJldCA9IC1FTk9ERVY7CisJfQorCisJcmV0dXJuIHJldDsKK30KKwor c3RhdGljIGlubGluZSB2b2lkIGNjaWRnX3B1dF9taW5vcihpbnQgbWlub3IpCit7CisJaWRhX3Np bXBsZV9yZW1vdmUoJmNjaWRnX2lkYSwgbWlub3IpOworfQorCitzdGF0aWMgaW50IGNjaWRnX3Nl dHVwKHZvaWQpCit7CisJaW50IHJldDsKKwlkZXZfdCBkZXY7CisKKwljY2lkZ19jbGFzcyA9IGNs YXNzX2NyZWF0ZShUSElTX01PRFVMRSwgImNjaWRnIik7CisJaWYgKElTX0VSUihjY2lkZ19jbGFz cykpIHsKKwkJcmV0ID0gUFRSX0VSUihjY2lkZ19jbGFzcyk7CisJCWNjaWRnX2NsYXNzID0gTlVM TDsKKwkJcmV0dXJuIHJldDsKKwl9CisKKwlyZXQgPSBhbGxvY19jaHJkZXZfcmVnaW9uKCZkZXYs IDAsIENDSURfTUlOT1JTLCAiY2NpZGciKTsKKwlpZiAocmV0KSB7CisJCWNsYXNzX2Rlc3Ryb3ko Y2NpZGdfY2xhc3MpOworCQljY2lkZ19jbGFzcyA9IE5VTEw7CisJCXJldHVybiByZXQ7CisJfQor CisJbWFqb3IgPSBNQUpPUihkZXYpOworCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyB2b2lkIGNj aWRnX2NsZWFudXAodm9pZCkKK3sKKwlpZiAobWFqb3IpIHsKKwkJdW5yZWdpc3Rlcl9jaHJkZXZf cmVnaW9uKE1LREVWKG1ham9yLCAwKSwgQ0NJRF9NSU5PUlMpOworCQltYWpvciA9IDA7CisJfQor CisJY2xhc3NfZGVzdHJveShjY2lkZ19jbGFzcyk7CisJY2NpZGdfY2xhc3MgPSBOVUxMOworCWlk YV9kZXN0cm95KCZjY2lkZ19pZGEpOworfQorCitzdGF0aWMgdm9pZCBjY2lkZ19hdHRyX3JlbGVh c2Uoc3RydWN0IGNvbmZpZ19pdGVtICppdGVtKQoreworCXN0cnVjdCBmX2NjaWRnX29wdHMgKm9w dHMgPSB0b19mX2NjaWRnX29wdHMoaXRlbSk7CisKKwl1c2JfcHV0X2Z1bmN0aW9uX2luc3RhbmNl KCZvcHRzLT5mdW5jX2luc3QpOworfQorCitzdGF0aWMgc3RydWN0IGNvbmZpZ2ZzX2l0ZW1fb3Bl cmF0aW9ucyBjY2lkZ19pdGVtX29wcyA9IHsKKwkucmVsZWFzZQk9IGNjaWRnX2F0dHJfcmVsZWFz ZSwKK307CisKKyNkZWZpbmUgRl9DQ0lER19PUFQobmFtZSwgcHJlYywgbGltaXQpCQkJCQlcCitz dGF0aWMgc3NpemVfdCBmX2NjaWRnX29wdHNfIyNuYW1lIyNfc2hvdyhzdHJ1Y3QgY29uZmlnX2l0 ZW0gKml0ZW0sIGNoYXIgKnBhZ2UpXAorewkJCQkJCQkJCVwKKwlzdHJ1Y3QgZl9jY2lkZ19vcHRz ICpvcHRzID0gdG9fZl9jY2lkZ19vcHRzKGl0ZW0pOwkJXAorCWludCByZXN1bHQ7CQkJCQkJCVwK KwkJCQkJCQkJCVwKKwltdXRleF9sb2NrKCZvcHRzLT5sb2NrKTsJCQkJCVwKKwlyZXN1bHQgPSBz cHJpbnRmKHBhZ2UsICIleFxuIiwgb3B0cy0+bmFtZSk7CQkJXAorCW11dGV4X3VubG9jaygmb3B0 cy0+bG9jayk7CQkJCQlcCisJCQkJCQkJCQlcCisJcmV0dXJuIHJlc3VsdDsJCQkJCQkJXAorfQkJ CQkJCQkJCVwKKwkJCQkJCQkJCVwKK3N0YXRpYyBzc2l6ZV90IGZfY2NpZGdfb3B0c18jI25hbWUj I19zdG9yZShzdHJ1Y3QgY29uZmlnX2l0ZW0gKml0ZW0sCVwKKwkJCQkJIGNvbnN0IGNoYXIgKnBh Z2UsIHNpemVfdCBsZW4pCVwKK3sJCQkJCQkJCQlcCisJc3RydWN0IGZfY2NpZGdfb3B0cyAqb3B0 cyA9IHRvX2ZfY2NpZGdfb3B0cyhpdGVtKTsJCVwKKwlpbnQgcmV0OwkJCQkJCQlcCisJdSMjcHJl YyBudW07CQkJCQkJCVwKKwkJCQkJCQkJCVwKKwltdXRleF9sb2NrKCZvcHRzLT5sb2NrKTsJCQkJ CVwKKwlpZiAob3B0cy0+cmVmY250KSB7CQkJCQkJXAorCQlyZXQgPSAtRUJVU1k7CQkJCQkJXAor CQlnb3RvIGVuZDsJCQkJCQlcCisJfQkJCQkJCQkJXAorCQkJCQkJCQkJXAorCXJldCA9IGtzdHJ0 b3UjI3ByZWMocGFnZSwgMCwgJm51bSk7CQkJCVwKKwlpZiAocmV0KQkJCQkJCQlcCisJCWdvdG8g ZW5kOwkJCQkJCVwKKwkJCQkJCQkJCVwKKwlpZiAobnVtID4gbGltaXQpIHsJCQkJCQlcCisJCXJl dCA9IC1FSU5WQUw7CQkJCQkJXAorCQlnb3RvIGVuZDsJCQkJCQlcCisJfQkJCQkJCQkJXAorCW9w dHMtPm5hbWUgPSBudW07CQkJCQkJXAorCXJldCA9IGxlbjsJCQkJCQkJXAorCQkJCQkJCQkJXAor ZW5kOgkJCQkJCQkJCVwKKwltdXRleF91bmxvY2soJm9wdHMtPmxvY2spOwkJCQkJXAorCXJldHVy biByZXQ7CQkJCQkJCVwKK30JCQkJCQkJCQlcCisJCQkJCQkJCQlcCitDT05GSUdGU19BVFRSKGZf Y2NpZGdfb3B0c18sIG5hbWUpCisKK0ZfQ0NJREdfT1BUKGZlYXR1cmVzLCAzMiwgMHhmZmZmZmZm Zik7CitGX0NDSURHX09QVChwcm90b2NvbHMsIDMyLCAweDAzKTsKK0ZfQ0NJREdfT1BUKHBpbnN1 cHBvcnQsIDgsIDB4MDMpOworRl9DQ0lER19PUFQobGNkbGF5b3V0LCAxNiwgMHhmZmZmKTsKK0Zf Q0NJREdfT1BUKG5zbG90cywgOCwgMHhmZik7CisKK3N0YXRpYyBzdHJ1Y3QgY29uZmlnZnNfYXR0 cmlidXRlICpjY2lkZ19hdHRyc1tdID0geworCSZmX2NjaWRnX29wdHNfYXR0cl9mZWF0dXJlcywK KwkmZl9jY2lkZ19vcHRzX2F0dHJfcHJvdG9jb2xzLAorCSZmX2NjaWRnX29wdHNfYXR0cl9waW5z dXBwb3J0LAorCSZmX2NjaWRnX29wdHNfYXR0cl9sY2RsYXlvdXQsCisJJmZfY2NpZGdfb3B0c19h dHRyX25zbG90cywKKwlOVUxMLAorfTsKKworc3RhdGljIHN0cnVjdCBjb25maWdfaXRlbV90eXBl IGNjaWRnX2Z1bmNfdHlwZSA9IHsKKwkuY3RfaXRlbV9vcHMJPSAmY2NpZGdfaXRlbV9vcHMsCisJ LmN0X2F0dHJzCT0gY2NpZGdfYXR0cnMsCisJLmN0X293bmVyCT0gVEhJU19NT0RVTEUsCit9Owor CitzdGF0aWMgdm9pZCBjY2lkZ19yZXFfcHV0KHN0cnVjdCBmX2NjaWRnICpjY2lkZywgc3RydWN0 IGxpc3RfaGVhZCAqaGVhZCwKKwkJc3RydWN0IHVzYl9yZXF1ZXN0ICpyZXEpCit7CisJdW5zaWdu ZWQgbG9uZyBmbGFnczsKKworCXNwaW5fbG9ja19pcnFzYXZlKCZjY2lkZy0+bG9jaywgZmxhZ3Mp OworCWxpc3RfYWRkX3RhaWwoJnJlcS0+bGlzdCwgaGVhZCk7CisJc3Bpbl91bmxvY2tfaXJxcmVz dG9yZSgmY2NpZGctPmxvY2ssIGZsYWdzKTsKK30KKworc3RhdGljIHN0cnVjdCB1c2JfcmVxdWVz dCAqY2NpZGdfcmVxX2dldChzdHJ1Y3QgZl9jY2lkZyAqY2NpZGcsCisJCQkJCXN0cnVjdCBsaXN0 X2hlYWQgKmhlYWQpCit7CisJdW5zaWduZWQgbG9uZyBmbGFnczsKKwlzdHJ1Y3QgdXNiX3JlcXVl c3QgKnJlcSA9IE5VTEw7CisKKwlzcGluX2xvY2tfaXJxc2F2ZSgmY2NpZGctPmxvY2ssIGZsYWdz KTsKKwlyZXEgPSBsaXN0X2ZpcnN0X2VudHJ5X29yX251bGwoaGVhZCwgc3RydWN0IHVzYl9yZXF1 ZXN0LCBsaXN0KTsKKwlpZiAocmVxKQorCQlsaXN0X2RlbCgmcmVxLT5saXN0KTsKKwlzcGluX3Vu bG9ja19pcnFyZXN0b3JlKCZjY2lkZy0+bG9jaywgZmxhZ3MpOworCisJcmV0dXJuIHJlcTsKK30K Kworc3RhdGljIHZvaWQgY2NpZGdfYnVsa19jb21wbGV0ZV90eChzdHJ1Y3QgdXNiX2VwICplcCwg c3RydWN0IHVzYl9yZXF1ZXN0ICpyZXEpCit7CisJc3RydWN0IGZfY2NpZGcgKmNjaWRnID0gKHN0 cnVjdCBmX2NjaWRnICopZXAtPmRyaXZlcl9kYXRhOworCXN0cnVjdCBjY2lkZ19idWxrX2RldiAq YnVsa19kZXYgPSAmY2NpZGctPmJ1bGtfZGV2OworCXN0cnVjdCB1c2JfY29tcG9zaXRlX2RldiAq Y2Rldgk9IGNjaWRnLT5mdW5jdGlvbi5jb25maWctPmNkZXY7CisKKwlzd2l0Y2ggKHJlcS0+c3Rh dHVzKSB7CisJZGVmYXVsdDoKKwkJVkRCRyhjZGV2LCAiY2NpZDogdHggZXJyICVkXG4iLCByZXEt PnN0YXR1cyk7CisJCS8qIEZBTExUSFJPVUdIICovCisJY2FzZSAtRUNPTk5SRVNFVDoJCS8qIHVu bGluayAqLworCWNhc2UgLUVTSFVURE9XTjoJCS8qIGRpc2Nvbm5lY3QgZXRjICovCisJCWJyZWFr OworCWNhc2UgMDoKKwkJYnJlYWs7CisJfQorCisJY2NpZGdfcmVxX3B1dChjY2lkZywgJmJ1bGtf ZGV2LT50eF9pZGxlLCByZXEpOworCXdha2VfdXAoJmJ1bGtfZGV2LT53cml0ZV93cSk7Cit9CisK K3N0YXRpYyB2b2lkIGNjaWRnX2J1bGtfY29tcGxldGVfcngoc3RydWN0IHVzYl9lcCAqZXAsIHN0 cnVjdCB1c2JfcmVxdWVzdCAqcmVxKQoreworCXN0cnVjdCBmX2NjaWRnICpjY2lkZyA9IChzdHJ1 Y3QgZl9jY2lkZyAqKWVwLT5kcml2ZXJfZGF0YTsKKwlzdHJ1Y3QgY2NpZGdfYnVsa19kZXYgKmJ1 bGtfZGV2ID0gJmNjaWRnLT5idWxrX2RldjsKKwlzdHJ1Y3QgdXNiX2NvbXBvc2l0ZV9kZXYgKmNk ZXYJPSBjY2lkZy0+ZnVuY3Rpb24uY29uZmlnLT5jZGV2OworCisJc3dpdGNoIChyZXEtPnN0YXR1 cykgeworCisJLyogbm9ybWFsIGNvbXBsZXRpb24gKi8KKwljYXNlIDA6CisJCS8qIFdlIG9ubHkg Y2FyZXMgYWJvdXQgcGFja2V0cyB3aXRoIG5vbnplcm8gbGVuZ3RoICovCisJCWlmIChyZXEtPmFj dHVhbCA+IDApCisJCQlhdG9taWNfc2V0KCZidWxrX2Rldi0+cnhfZG9uZSwgMSk7CisJCWJyZWFr OworCisJLyogc29mdHdhcmUtZHJpdmVuIGludGVyZmFjZSBzaHV0ZG93biAqLworCWNhc2UgLUVD T05OUkVTRVQ6CQkvKiB1bmxpbmsgKi8KKwljYXNlIC1FU0hVVERPV046CQkvKiBkaXNjb25uZWN0 IGV0YyAqLworCQlWREJHKGNkZXYsICJjY2lkOiByeCBzaHV0ZG93biwgY29kZSAlZFxuIiwgcmVx LT5zdGF0dXMpOworCQlicmVhazsKKworCS8qIGZvciBoYXJkd2FyZSBhdXRvbWFnaWMgKHN1Y2gg YXMgcHhhKSAqLworCWNhc2UgLUVDT05OQUJPUlRFRDoJCS8qIGVuZHBvaW50IHJlc2V0ICovCisJ CURCRyhjZGV2LCAiY2NpZDogcnggJXMgcmVzZXRcbiIsIGVwLT5uYW1lKTsKKwkJYnJlYWs7CisK KwkvKiBkYXRhIG92ZXJydW4gKi8KKwljYXNlIC1FT1ZFUkZMT1c6CisJCS8qIEZBTExUSFJPVUdI ICovCisJZGVmYXVsdDoKKwkJREJHKGNkZXYsICJjY2lkOiByeCBzdGF0dXMgJWRcbiIsIHJlcS0+ c3RhdHVzKTsKKwkJYnJlYWs7CisJfQorCisJd2FrZV91cCgmYnVsa19kZXYtPnJlYWRfd3EpOwor fQorCitzdGF0aWMgc3RydWN0IHVzYl9yZXF1ZXN0ICoKK2NjaWRnX3JlcXVlc3RfYWxsb2Moc3Ry dWN0IHVzYl9lcCAqZXAsIHVuc2lnbmVkIGludCBsZW4pCit7CisJc3RydWN0IHVzYl9yZXF1ZXN0 ICpyZXE7CisKKwlyZXEgPSB1c2JfZXBfYWxsb2NfcmVxdWVzdChlcCwgR0ZQX0FUT01JQyk7CisJ aWYgKCFyZXEpCisJCXJldHVybiBFUlJfUFRSKC1FTk9NRU0pOworCisJcmVxLT5idWYgPSBrbWFs bG9jKGxlbiwgR0ZQX0FUT01JQyk7CisJaWYgKCFyZXEtPmJ1ZikgeworCQl1c2JfZXBfZnJlZV9y ZXF1ZXN0KGVwLCByZXEpOworCQlyZXR1cm4gRVJSX1BUUigtRU5PTUVNKTsKKwl9CisJcmVxLT5s ZW5ndGggPSBsZW47CisKKwlyZXR1cm4gcmVxOworfQorCitzdGF0aWMgdm9pZCBjY2lkZ19yZXF1 ZXN0X2ZyZWUoc3RydWN0IHVzYl9yZXF1ZXN0ICpyZXEsIHN0cnVjdCB1c2JfZXAgKmVwKQorewor CWlmICghcmVxKQorCQlyZXR1cm47CisKKwlrZnJlZShyZXEtPmJ1Zik7CisJdXNiX2VwX2ZyZWVf cmVxdWVzdChlcCwgcmVxKTsKK30KKworc3RhdGljIGludCBjY2lkZ19mdW5jdGlvbl9zZXR1cChz dHJ1Y3QgdXNiX2Z1bmN0aW9uICpmLAorCQljb25zdCBzdHJ1Y3QgdXNiX2N0cmxyZXF1ZXN0ICpj dHJsKQoreworCXN0cnVjdCBmX2NjaWRnICpjY2lkZyA9IGNvbnRhaW5lcl9vZihmLCBzdHJ1Y3Qg Zl9jY2lkZywgZnVuY3Rpb24pOworCXN0cnVjdCB1c2JfY29tcG9zaXRlX2RldiAqY2Rldgk9IGYt PmNvbmZpZy0+Y2RldjsKKwlzdHJ1Y3QgdXNiX3JlcXVlc3QgKnJlcQkJPSBjZGV2LT5yZXE7CisJ aW50IHJldAkJCQk9IC1FT1BOT1RTVVBQOworCXUxNiB3X2luZGV4CQkJPSBsZTE2X3RvX2NwdShj dHJsLT53SW5kZXgpOworCXUxNiB3X3ZhbHVlCQkJPSBsZTE2X3RvX2NwdShjdHJsLT53VmFsdWUp OworCXUxNiB3X2xlbmd0aAkJCT0gbGUxNl90b19jcHUoY3RybC0+d0xlbmd0aCk7CisKKwlpZiAo IWF0b21pY19yZWFkKCZjY2lkZy0+b25saW5lKSkKKwkJcmV0dXJuIC1FTk9UQ09OTjsKKworCXN3 aXRjaCAoY3RybC0+YlJlcXVlc3RUeXBlICYgVVNCX1RZUEVfTUFTSykgeworCWNhc2UgVVNCX1RZ UEVfQ0xBU1M6CisJCXN3aXRjaCAoY3RybC0+YlJlcXVlc3QpIHsKKwkJY2FzZSBDQ0lER0VORVJJ Q1JFUV9HRVRfQ0xPQ0tfRlJFUVVFTkNJRVM6CisJCQlpZiAod19sZW5ndGggPiBzaXplb2YoY2Np ZF9jbGFzc19kZXNjLmR3RGVmYXVsdENsb2NrKSkKKwkJCQlicmVhazsKKworCQkJcHV0X3VuYWxp Z25lZChjY2lkX2NsYXNzX2Rlc2MuZHdEZWZhdWx0Q2xvY2ssIHJlcS0+YnVmKTsKKwkJCXJldCA9 IHNpemVvZihjY2lkX2NsYXNzX2Rlc2MuZHdEZWZhdWx0Q2xvY2spOworCQkJYnJlYWs7CisKKwkJ Y2FzZSBDQ0lER0VORVJJQ1JFUV9HRVRfREFUQV9SQVRFUzoKKwkJCWlmICh3X2xlbmd0aCA+IHNp emVvZihjY2lkX2NsYXNzX2Rlc2MuZHdEYXRhUmF0ZSkpCisJCQkJYnJlYWs7CisKKwkJCXB1dF91 bmFsaWduZWQoY2NpZF9jbGFzc19kZXNjLmR3RGF0YVJhdGUsIHJlcS0+YnVmKTsKKwkJCXJldCA9 IHNpemVvZihjY2lkX2NsYXNzX2Rlc2MuZHdEYXRhUmF0ZSk7CisJCQlicmVhazsKKworCQlkZWZh dWx0OgorCQkJVkRCRyhmLT5jb25maWctPmNkZXYsCisJCQkJImNjaWQ6IGludmFsaWQgY29udHJv bCByZXElMDJ4LiUwMnggdiUwNHggaSUwNHggbCVkXG4iLAorCQkJCWN0cmwtPmJSZXF1ZXN0VHlw ZSwgY3RybC0+YlJlcXVlc3QsCisJCQkJd192YWx1ZSwgd19pbmRleCwgd19sZW5ndGgpOworCQl9 CisJfQorCisJLyogcmVzcG9uZGVkIHdpdGggZGF0YSB0cmFuc2ZlciBvciBzdGF0dXMgcGhhc2U/ ICovCisJaWYgKHJldCA+PSAwKSB7CisJCVZEQkcoZi0+Y29uZmlnLT5jZGV2LCAiY2NpZDogcmVx JTAyeC4lMDJ4IHYlMDR4IGklMDR4IGwlZFxuIiwKKwkJCWN0cmwtPmJSZXF1ZXN0VHlwZSwgY3Ry bC0+YlJlcXVlc3QsCisJCQl3X3ZhbHVlLCB3X2luZGV4LCB3X2xlbmd0aCk7CisKKwkJcmVxLT5s ZW5ndGggPSByZXQ7CisJCXJldCA9IHVzYl9lcF9xdWV1ZShjZGV2LT5nYWRnZXQtPmVwMCwgcmVx LCBHRlBfQVRPTUlDKTsKKwkJaWYgKHJldCA8IDApCisJCQlFUlJPUihmLT5jb25maWctPmNkZXYs CisJCQkJImNjaWQ6IGVwMCBlbnF1ZXVlIGVyciAlZFxuIiwgcmV0KTsKKwl9CisKKwlyZXR1cm4g cmV0OworfQorCitzdGF0aWMgdm9pZCBjY2lkZ19mdW5jdGlvbl9kaXNhYmxlKHN0cnVjdCB1c2Jf ZnVuY3Rpb24gKmYpCit7CisJc3RydWN0IGZfY2NpZGcgKmNjaWRnID0gZnVuY190b19jY2lkZyhm KTsKKwlzdHJ1Y3QgY2NpZGdfYnVsa19kZXYgKmJ1bGtfZGV2ID0gJmNjaWRnLT5idWxrX2RldjsK KwlzdHJ1Y3QgdXNiX3JlcXVlc3QgKnJlcTsKKworCS8qIERpc2FibGUgZW5kcG9pbnRzICovCisJ dXNiX2VwX2Rpc2FibGUoY2NpZGctPmluKTsKKwl1c2JfZXBfZGlzYWJsZShjY2lkZy0+b3V0KTsK KworCS8qIEZyZWUgZW5kcG9pbnQgcmVsYXRlZCByZXF1ZXN0cyAqLworCWlmICghYXRvbWljX3Jl YWQoJmJ1bGtfZGV2LT5yeF9yZXFfYnVzeSkpCisJCWNjaWRnX3JlcXVlc3RfZnJlZShidWxrX2Rl di0+cnhfcmVxLCBjY2lkZy0+b3V0KTsKKwl3aGlsZSAoKHJlcSA9IGNjaWRnX3JlcV9nZXQoY2Np ZGcsICZidWxrX2Rldi0+dHhfaWRsZSkpKQorCQljY2lkZ19yZXF1ZXN0X2ZyZWUocmVxLCBjY2lk Zy0+aW4pOworCisJYXRvbWljX3NldCgmY2NpZGctPm9ubGluZSwgMCk7CisKKwkvKiBXYWtlIHVw IHRocmVhZHMgKi8KKwl3YWtlX3VwKCZidWxrX2Rldi0+d3JpdGVfd3EpOworCXdha2VfdXAoJmJ1 bGtfZGV2LT5yZWFkX3dxKTsKK30KKworc3RhdGljIGludCBjY2lkZ19zdGFydF9lcChzdHJ1Y3Qg Zl9jY2lkZyAqY2NpZGcsIHN0cnVjdCB1c2JfZnVuY3Rpb24gKmYsCisJCQlzdHJ1Y3QgdXNiX2Vw ICplcCkKK3sKKwlzdHJ1Y3QgdXNiX2NvbXBvc2l0ZV9kZXYgKmNkZXYgPSBmLT5jb25maWctPmNk ZXY7CisJaW50IHJldDsKKworCXVzYl9lcF9kaXNhYmxlKGVwKTsKKworCXJldCA9IGNvbmZpZ19l cF9ieV9zcGVlZChjZGV2LT5nYWRnZXQsIGYsIGVwKTsKKwlpZiAocmV0KSB7CisJCUVSUk9SKGNk ZXYsICJjY2lkOiBjYW4ndCBjb25maWd1cmUgJXM6ICVkXG4iLCBlcC0+bmFtZSwgcmV0KTsKKwkJ cmV0dXJuIHJldDsKKwl9CisKKwlyZXQgPSB1c2JfZXBfZW5hYmxlKGVwKTsKKwlpZiAocmV0KSB7 CisJCUVSUk9SKGNkZXYsICJjY2lkOiBjYW4ndCBzdGFydCAlczogJWRcbiIsIGVwLT5uYW1lLCBy ZXQpOworCQlyZXR1cm4gcmV0OworCX0KKworCWVwLT5kcml2ZXJfZGF0YSA9IGNjaWRnOworCisJ cmV0dXJuIHJldDsKK30KKworc3RhdGljIGludCBjY2lkZ19mdW5jdGlvbl9zZXRfYWx0KHN0cnVj dCB1c2JfZnVuY3Rpb24gKmYsCisJCXVuc2lnbmVkIGludCBpbnRmLCB1bnNpZ25lZCBpbnQgYWx0 KQoreworCXN0cnVjdCBmX2NjaWRnICpjY2lkZwkJPSBmdW5jX3RvX2NjaWRnKGYpOworCXN0cnVj dCB1c2JfY29tcG9zaXRlX2RldiAqY2Rldgk9IGYtPmNvbmZpZy0+Y2RldjsKKwlzdHJ1Y3QgY2Np ZGdfYnVsa19kZXYgKmJ1bGtfZGV2CT0gJmNjaWRnLT5idWxrX2RldjsKKwlzdHJ1Y3QgdXNiX3Jl cXVlc3QgKnJlcTsKKwlpbnQgcmV0OworCWludCBpOworCisJLyogQWxsb2NhdGUgcmVxdWVzdHMg Zm9yIG91ciBlbmRwb2ludHMgKi8KKwlyZXEgPSBjY2lkZ19yZXF1ZXN0X2FsbG9jKGNjaWRnLT5v dXQsCisJCQlzaXplb2Yoc3RydWN0IGNjaWRnX2J1bGtfb3V0X2hlYWRlcikpOworCWlmIChJU19F UlIocmVxKSkgeworCQlFUlJPUihjZGV2LCAiY2NpZDogdW5hbWUgdG8gYWxsb2NhdGUgbWVtb3J5 IGZvciBvdXQgcmVxXG4iKTsKKwkJcmV0dXJuIFBUUl9FUlIocmVxKTsKKwl9CisJcmVxLT5jb21w bGV0ZSA9IGNjaWRnX2J1bGtfY29tcGxldGVfcng7CisJcmVxLT5jb250ZXh0ID0gY2NpZGc7CisJ YnVsa19kZXYtPnJ4X3JlcSA9IHJlcTsKKworCS8qIEFsbG9jYXRlIGJ1bmNoIG9mIGluIHJlcXVl c3RzICovCisJZm9yIChpID0gMDsgaSA8IE5fVFhfUkVRUzsgaSsrKSB7CisJCXJlcSA9IGNjaWRn X3JlcXVlc3RfYWxsb2MoY2NpZGctPmluLAorCQkJCXNpemVvZihzdHJ1Y3QgY2NpZGdfYnVsa19p bl9oZWFkZXIpKTsKKworCQlpZiAoSVNfRVJSKHJlcSkpIHsKKwkJCXJldCA9IFBUUl9FUlIocmVx KTsKKwkJCUVSUk9SKGNkZXYsCisJCQkJImNjaWQ6IHVuYW1lIHRvIGFsbG9jYXRlIG1lbW9yeSBm b3IgaW4gcmVxXG4iKTsKKwkJCWdvdG8gZnJlZV9idWxrX291dDsKKwkJfQorCQlyZXEtPmNvbXBs ZXRlID0gY2NpZGdfYnVsa19jb21wbGV0ZV90eDsKKwkJcmVxLT5jb250ZXh0ID0gY2NpZGc7CisJ CWNjaWRnX3JlcV9wdXQoY2NpZGcsICZidWxrX2Rldi0+dHhfaWRsZSwgcmVxKTsKKwl9CisKKwkv KiBjaG9vc2UgdGhlIGRlc2NyaXB0b3JzIGFuZCBlbmFibGUgZW5kcG9pbnRzICovCisJcmV0ID0g Y2NpZGdfc3RhcnRfZXAoY2NpZGcsIGYsIGNjaWRnLT5pbik7CisJaWYgKHJldCkKKwkJZ290byBm cmVlX2J1bGtfaW47CisKKwlyZXQgPSBjY2lkZ19zdGFydF9lcChjY2lkZywgZiwgY2NpZGctPm91 dCk7CisJaWYgKHJldCkKKwkJZ290byBkaXNhYmxlX2VwX2luOworCisJYXRvbWljX3NldCgmY2Np ZGctPm9ubGluZSwgMSk7CisJcmV0dXJuIDA7CisKK2Rpc2FibGVfZXBfaW46CisJdXNiX2VwX2Rp c2FibGUoY2NpZGctPmluKTsKK2ZyZWVfYnVsa19pbjoKKwl3aGlsZSAoKHJlcSA9IGNjaWRnX3Jl cV9nZXQoY2NpZGcsICZidWxrX2Rldi0+dHhfaWRsZSkpKQorCQljY2lkZ19yZXF1ZXN0X2ZyZWUo cmVxLCBjY2lkZy0+aW4pOworZnJlZV9idWxrX291dDoKKwljY2lkZ19yZXF1ZXN0X2ZyZWUoYnVs a19kZXYtPnJ4X3JlcSwgY2NpZGctPm91dCk7CisJcmV0dXJuIHJldDsKK30KKworc3RhdGljIGlu dCBjY2lkZ19idWxrX29wZW4oc3RydWN0IGlub2RlICppbm9kZSwgc3RydWN0IGZpbGUgKmZpbGUp Cit7CisJc3RydWN0IGNjaWRnX2J1bGtfZGV2ICpidWxrX2RldjsKKwlzdHJ1Y3QgZl9jY2lkZyAq Y2NpZGcgPSBjb250YWluZXJfb2YoaW5vZGUtPmlfY2RldiwKKwkJCXN0cnVjdCBmX2NjaWRnLCBj ZGV2KTsKKworCWJ1bGtfZGV2ID0gJmNjaWRnLT5idWxrX2RldjsKKworCWlmICghYXRvbWljX3Jl YWQoJmNjaWRnLT5vbmxpbmUpKSB7CisJCURCRyhjY2lkZy0+ZnVuY3Rpb24uY29uZmlnLT5jZGV2 LCAiY2NpZDogZGV2aWNlIG5vdCBvbmxpbmVcbiIpOworCQlyZXR1cm4gLUVOT0RFVjsKKwl9CisK KwlpZiAoYXRvbWljX3JlYWQoJmJ1bGtfZGV2LT5pc19vcGVuKSkgeworCQlEQkcoY2NpZGctPmZ1 bmN0aW9uLmNvbmZpZy0+Y2RldiwKKwkJCQkiY2NpZDogZGV2aWNlIGFscmVhZHkgb3BlbmVkXG4i KTsKKwkJcmV0dXJuIC1FQlVTWTsKKwl9CisKKwlhdG9taWNfc2V0KCZidWxrX2Rldi0+aXNfb3Bl biwgMSk7CisKKwlmaWxlLT5wcml2YXRlX2RhdGEgPSBjY2lkZzsKKworCXJldHVybiAwOworfQor CitzdGF0aWMgaW50IGNjaWRnX2J1bGtfcmVsZWFzZShzdHJ1Y3QgaW5vZGUgKmlub2RlLCBzdHJ1 Y3QgZmlsZSAqZmlsZSkKK3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2NpZGcgPSAgZmlsZS0+cHJpdmF0 ZV9kYXRhOworCXN0cnVjdCBjY2lkZ19idWxrX2RldiAqYnVsa19kZXYgPSAmY2NpZGctPmJ1bGtf ZGV2OworCisJYXRvbWljX3NldCgmYnVsa19kZXYtPmlzX29wZW4sIDApOworCXJldHVybiAwOwor fQorCitzdGF0aWMgc3NpemVfdCBjY2lkZ19idWxrX3JlYWQoc3RydWN0IGZpbGUgKmZpbGUsIGNo YXIgX191c2VyICpidWYsCisJCQkJc2l6ZV90IGNvdW50LCBsb2ZmX3QgKnBvcykKK3sKKwlzdHJ1 Y3QgZl9jY2lkZyAqY2NpZGcgPSAgZmlsZS0+cHJpdmF0ZV9kYXRhOworCXN0cnVjdCBjY2lkZ19i dWxrX2RldiAqYnVsa19kZXYgPSAmY2NpZGctPmJ1bGtfZGV2OworCXN0cnVjdCB1c2JfcmVxdWVz dCAqcmVxOworCXNpemVfdCAgeGZlcjsKKwlpbnQgcmV0OworCisJLyogTWFrZSBzdXJlIHdlIGhh dmUgZW5vdWdoIHNwYWNlIGZvciBhIHdob2xlIHBhY2thZ2UgKi8KKwlpZiAoY291bnQgPCBzaXpl b2Yoc3RydWN0IGNjaWRnX2J1bGtfb3V0X2hlYWRlcikpIHsKKwkJREJHKGNjaWRnLT5mdW5jdGlv bi5jb25maWctPmNkZXYsCisJCQkJImNjaWQ6IHRvbyBzbWFsbCBidWZmZXIgc2l6ZS4gJXp1IHBy b3ZpZGVkLCBuZWVkIGF0IGxlYXN0ICV6dVxuIiwKKwkJCQljb3VudCwgc2l6ZW9mKHN0cnVjdCBj Y2lkZ19idWxrX291dF9oZWFkZXIpKTsKKwkJcmV0dXJuIC1FTk9NRU07CisJfQorCisJaWYgKCFh dG9taWNfcmVhZCgmY2NpZGctPm9ubGluZSkpCisJCXJldHVybiAtRU5PREVWOworCisJLyogcXVl dWUgYSByZXF1ZXN0ICovCisJcmVxID0gYnVsa19kZXYtPnJ4X3JlcTsKKwlyZXEtPmxlbmd0aCA9 IGNvdW50OworCWF0b21pY19zZXQoJmJ1bGtfZGV2LT5yeF9kb25lLCAwKTsKKworCXJldCA9IHVz Yl9lcF9xdWV1ZShjY2lkZy0+b3V0LCByZXEsIEdGUF9LRVJORUwpOworCWlmIChyZXQgPCAwKSB7 CisJCUVSUk9SKGNjaWRnLT5mdW5jdGlvbi5jb25maWctPmNkZXYsCisJCQkJImNjaWQ6IHVzYiBl cCBxdWV1ZSBmYWlsZWRcbiIpOworCQlyZXR1cm4gLUVJTzsKKwl9CisKKwlpZiAoIWF0b21pY19y ZWFkKCZidWxrX2Rldi0+cnhfZG9uZSkgJiYKKwkJCWZpbGUtPmZfZmxhZ3MgJiAoT19OT05CTE9D SyB8IE9fTkRFTEFZKSkKKwkJcmV0dXJuIC1FQUdBSU47CisKKwkvKiB3YWl0IGZvciBhIHJlcXVl c3QgdG8gY29tcGxldGUgKi8KKwlyZXQgPSB3YWl0X2V2ZW50X2ludGVycnVwdGlibGUoYnVsa19k ZXYtPnJlYWRfd3EsCisJCQlhdG9taWNfcmVhZCgmYnVsa19kZXYtPnJ4X2RvbmUpIHx8CisJCQkh YXRvbWljX3JlYWQoJmNjaWRnLT5vbmxpbmUpKTsKKwlpZiAocmV0IDwgMCkgeworCQl1c2JfZXBf ZGVxdWV1ZShjY2lkZy0+b3V0LCByZXEpOworCQlyZXR1cm4gLUVSRVNUQVJUU1lTOworCX0KKwor CS8qIFN0aWxsIG9ubGluZT8gKi8KKwlpZiAoIWF0b21pY19yZWFkKCZjY2lkZy0+b25saW5lKSkK KwkJcmV0dXJuIC1FTk9ERVY7CisKKwlhdG9taWNfc2V0KCZidWxrX2Rldi0+cnhfcmVxX2J1c3ks IDEpOworCXhmZXIgPSBtaW5fdChzaXplX3QsIHJlcS0+YWN0dWFsLCBjb3VudCk7CisKKwlpZiAo Y29weV90b191c2VyKGJ1ZiwgcmVxLT5idWYsIHhmZXIpKQorCQlyZXR1cm4gLUVGQVVMVDsKKwor CWF0b21pY19zZXQoJmJ1bGtfZGV2LT5yeF9yZXFfYnVzeSwgMCk7CisJaWYgKCFhdG9taWNfcmVh ZCgmY2NpZGctPm9ubGluZSkpIHsKKwkJY2NpZGdfcmVxdWVzdF9mcmVlKGJ1bGtfZGV2LT5yeF9y ZXEsIGNjaWRnLT5vdXQpOworCQlyZXR1cm4gLUVOT0RFVjsKKwl9CisKKwlyZXR1cm4geGZlcjsK K30KKworc3RhdGljIHNzaXplX3QgY2NpZGdfYnVsa193cml0ZShzdHJ1Y3QgZmlsZSAqZmlsZSwg Y29uc3QgY2hhciBfX3VzZXIgKmJ1ZiwKKwkJCQkgc2l6ZV90IGNvdW50LCBsb2ZmX3QgKnBvcykK K3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2NpZGcgPSAgZmlsZS0+cHJpdmF0ZV9kYXRhOworCXN0cnVj dCBjY2lkZ19idWxrX2RldiAqYnVsa19kZXYgPSAmY2NpZGctPmJ1bGtfZGV2OworCXN0cnVjdCB1 c2JfcmVxdWVzdCAqcmVxID0gTlVMTDsKKwlpbnQgcmV0OworCisJLyogQXJlIHdlIG9ubGluZT8g Ki8KKwlpZiAoIWF0b21pY19yZWFkKCZjY2lkZy0+b25saW5lKSkKKwkJcmV0dXJuIC1FTk9ERVY7 CisKKwkvKiBBdm9pZCBaZXJvIExlbmd0aCBQYWNrZXRzIChaTFApICovCisJaWYgKCFjb3VudCkK KwkJcmV0dXJuIDA7CisKKwkvKiBNYWtlIHN1cmUgd2UgaGF2ZSBlbm91Z2ggc3BhY2UgZm9yIGEg d2hvbGUgcGFja2FnZSAqLworCWlmIChjb3VudCA+IHNpemVvZihzdHJ1Y3QgY2NpZGdfYnVsa19v dXRfaGVhZGVyKSkgeworCQlEQkcoY2NpZGctPmZ1bmN0aW9uLmNvbmZpZy0+Y2RldiwKKwkJCQki Y2NpZDogdG9vIG11Y2ggZGF0YS4gJXp1IHByb3ZpZGVkLCBidXQgd2UgY2FuIG9ubHkgaGFuZGxl ICV6dVxuIiwKKwkJCQljb3VudCwgc2l6ZW9mKHN0cnVjdCBjY2lkZ19idWxrX291dF9oZWFkZXIp KTsKKwkJcmV0dXJuIC1FTk9NRU07CisJfQorCisJaWYgKGxpc3RfZW1wdHkoJmJ1bGtfZGV2LT50 eF9pZGxlKSAmJgorCQkJZmlsZS0+Zl9mbGFncyAmIChPX05PTkJMT0NLIHwgT19OREVMQVkpKQor CQlyZXR1cm4gLUVBR0FJTjsKKworCS8qIGdldCBhbiBpZGxlIHR4IHJlcXVlc3QgdG8gdXNlICov CisJcmV0ID0gd2FpdF9ldmVudF9pbnRlcnJ1cHRpYmxlKGJ1bGtfZGV2LT53cml0ZV93cSwKKwkJ KChyZXEgPSBjY2lkZ19yZXFfZ2V0KGNjaWRnLCAmYnVsa19kZXYtPnR4X2lkbGUpKSkpOworCWlm IChyZXQgPCAwKQorCQlyZXR1cm4gLUVSRVNUQVJUU1lTOworCisJaWYgKGNvcHlfZnJvbV91c2Vy KHJlcS0+YnVmLCBidWYsIGNvdW50KSkgeworCQlpZiAoIWF0b21pY19yZWFkKCZjY2lkZy0+b25s aW5lKSkgeworCQkJY2NpZGdfcmVxdWVzdF9mcmVlKHJlcSwgY2NpZGctPmluKTsKKwkJCXJldHVy biAtRU5PREVWOworCQl9IGVsc2UgeworCQkJY2NpZGdfcmVxX3B1dChjY2lkZywgJmJ1bGtfZGV2 LT50eF9pZGxlLCByZXEpOworCQkJcmV0dXJuIC1FRkFVTFQ7CisJCX0KKwl9CisKKwlyZXEtPmxl bmd0aCA9IGNvdW50OworCXJldCA9IHVzYl9lcF9xdWV1ZShjY2lkZy0+aW4sIHJlcSwgR0ZQX0tF Uk5FTCk7CisJaWYgKHJldCA8IDApIHsKKwkJY2NpZGdfcmVxX3B1dChjY2lkZywgJmJ1bGtfZGV2 LT50eF9pZGxlLCByZXEpOworCisJCWlmICghYXRvbWljX3JlYWQoJmNjaWRnLT5vbmxpbmUpKSB7 CisJCQkvKiBGcmVlIHVwIGFsbCByZXF1ZXN0cyBpZiB3ZSBhcmUgbm90IG9ubGluZSAqLworCQkJ d2hpbGUgKChyZXEgPSBjY2lkZ19yZXFfZ2V0KGNjaWRnLCAmYnVsa19kZXYtPnR4X2lkbGUpKSkK KwkJCQljY2lkZ19yZXF1ZXN0X2ZyZWUocmVxLCBjY2lkZy0+aW4pOworCisJCQlyZXR1cm4gLUVO T0RFVjsKKwkJfQorCQlyZXR1cm4gLUVJTzsKKwl9CisKKwlyZXR1cm4gY291bnQ7Cit9CisKK3N0 YXRpYyBfX3BvbGxfdCBjY2lkZ19idWxrX3BvbGwoc3RydWN0IGZpbGUgKmZpbGUsIHBvbGxfdGFi bGUgKiB3YWl0KQoreworCXN0cnVjdCBmX2NjaWRnICpjY2lkZyA9ICBmaWxlLT5wcml2YXRlX2Rh dGE7CisJc3RydWN0IGNjaWRnX2J1bGtfZGV2ICpidWxrX2RldiA9ICZjY2lkZy0+YnVsa19kZXY7 CisJX19wb2xsX3QJcmV0ID0gMDsKKworCXBvbGxfd2FpdChmaWxlLCAmYnVsa19kZXYtPnJlYWRf d3EsIHdhaXQpOworCXBvbGxfd2FpdChmaWxlLCAmYnVsa19kZXYtPndyaXRlX3dxLCB3YWl0KTsK KworCWlmIChsaXN0X2VtcHR5KCZidWxrX2Rldi0+dHhfaWRsZSkpCisJCXJldCB8PSBFUE9MTE9V VCB8IEVQT0xMV1JOT1JNOworCisJaWYgKGF0b21pY19yZWFkKCZidWxrX2Rldi0+cnhfZG9uZSkp CisJCXJldCB8PSBFUE9MTElOIHwgRVBPTExSRE5PUk07CisKKwlyZXR1cm4gcmV0OworfQorCitz dGF0aWMgY29uc3Qgc3RydWN0IGZpbGVfb3BlcmF0aW9ucyBmX2NjaWRnX2ZvcHMgPSB7CisJLm93 bmVyID0gVEhJU19NT0RVTEUsCisJLnJlYWQgPSBjY2lkZ19idWxrX3JlYWQsCisJLndyaXRlID0g Y2NpZGdfYnVsa193cml0ZSwKKwkub3BlbiA9IGNjaWRnX2J1bGtfb3BlbiwKKwkucG9sbCA9IGNj aWRnX2J1bGtfcG9sbCwKKwkucmVsZWFzZSA9IGNjaWRnX2J1bGtfcmVsZWFzZSwKK307CisKK3N0 YXRpYyBpbnQgY2NpZGdfYnVsa19kZXZpY2VfaW5pdChzdHJ1Y3QgZl9jY2lkZyAqZGV2KQorewor CXN0cnVjdCBjY2lkZ19idWxrX2RldiAqYnVsa19kZXYgPSAmZGV2LT5idWxrX2RldjsKKworCWlu aXRfd2FpdHF1ZXVlX2hlYWQoJmJ1bGtfZGV2LT5yZWFkX3dxKTsKKwlpbml0X3dhaXRxdWV1ZV9o ZWFkKCZidWxrX2Rldi0+d3JpdGVfd3EpOworCUlOSVRfTElTVF9IRUFEKCZidWxrX2Rldi0+dHhf aWRsZSk7CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIHZvaWQgY2NpZGdfZnVuY3Rpb25fZnJl ZShzdHJ1Y3QgdXNiX2Z1bmN0aW9uICpmKQoreworCXN0cnVjdCBmX2NjaWRnICpjY2lkZyA9IGZ1 bmNfdG9fY2NpZGcoZik7CisJc3RydWN0IGZfY2NpZGdfb3B0cyAqb3B0cyA9IGNvbnRhaW5lcl9v ZihmLT5maSwgc3RydWN0IGZfY2NpZGdfb3B0cywKKwkJCQkJCWZ1bmNfaW5zdCk7CisKKwlrZnJl ZShjY2lkZyk7CisJbXV0ZXhfbG9jaygmb3B0cy0+bG9jayk7CisJb3B0cy0+cmVmY250LS07CisJ bXV0ZXhfdW5sb2NrKCZvcHRzLT5sb2NrKTsKK30KKworc3RhdGljIHZvaWQgY2NpZGdfZnVuY3Rp b25fdW5iaW5kKHN0cnVjdCB1c2JfY29uZmlndXJhdGlvbiAqYywKKwkJCQkJc3RydWN0IHVzYl9m dW5jdGlvbiAqZikKK3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2NpZGcgPSBmdW5jX3RvX2NjaWRnKGYp OworCisJZGV2aWNlX2Rlc3Ryb3koY2NpZGdfY2xhc3MsIE1LREVWKG1ham9yLCBjY2lkZy0+bWlu b3IpKTsKKwljZGV2X2RlbCgmY2NpZGctPmNkZXYpOworCisJLyogZGlzYWJsZS9mcmVlIHJlcXVl c3QgYW5kIGVuZCBwb2ludCAqLworCXVzYl9mcmVlX2FsbF9kZXNjcmlwdG9ycyhmKTsKK30KKwor c3RhdGljIGludCBjY2lkZ19mdW5jdGlvbl9iaW5kKHN0cnVjdCB1c2JfY29uZmlndXJhdGlvbiAq YywKKwkJCQkJc3RydWN0IHVzYl9mdW5jdGlvbiAqZikKK3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2Np ZGcgPSBmdW5jX3RvX2NjaWRnKGYpOworCXN0cnVjdCB1c2JfZXAgKmVwOworCXN0cnVjdCB1c2Jf Y29tcG9zaXRlX2RldiAqY2RldiA9IGMtPmNkZXY7CisJc3RydWN0IGRldmljZSAqZGV2aWNlOwor CWRldl90IGRldjsKKwlpbnQgaWZjX2lkOworCWludCByZXQ7CisKKwkvKiBhbGxvY2F0ZSBpbnN0 YW5jZS1zcGVjaWZpYyBpbnRlcmZhY2UgSURzLCBhbmQgcGF0Y2ggZGVzY3JpcHRvcnMgKi8KKwlp ZmNfaWQgPSB1c2JfaW50ZXJmYWNlX2lkKGMsIGYpOworCWlmIChpZmNfaWQgPCAwKSB7CisJCUVS Uk9SKGNkZXYsICJjY2lkOiB1bmFibGUgdG8gYWxsb2NhdGUgaWZjIGlkLCBlcnI6JWRcbiIsCisJ CQkJaWZjX2lkKTsKKwkJcmV0dXJuIGlmY19pZDsKKwl9CisJY2NpZF9pbnRlcmZhY2VfZGVzYy5i SW50ZXJmYWNlTnVtYmVyID0gaWZjX2lkOworCisJLyogYWxsb2NhdGUgaW5zdGFuY2Utc3BlY2lm aWMgZW5kcG9pbnRzICovCisJZXAgPSB1c2JfZXBfYXV0b2NvbmZpZyhjZGV2LT5nYWRnZXQsICZj Y2lkX2ZzX2luX2Rlc2MpOworCWlmICghZXApIHsKKwkJRVJST1IoY2RldiwgImNjaWQ6IHVzYiBl cGluIGF1dG9jb25maWcgZmFpbGVkXG4iKTsKKwkJcmV0ID0gLUVOT0RFVjsKKwkJZ290byBlcF9h dXRvX2luX2ZhaWw7CisJfQorCWNjaWRnLT5pbiA9IGVwOworCWVwLT5kcml2ZXJfZGF0YSA9IGNj aWRnOworCisJZXAgPSB1c2JfZXBfYXV0b2NvbmZpZyhjZGV2LT5nYWRnZXQsICZjY2lkX2ZzX291 dF9kZXNjKTsKKwlpZiAoIWVwKSB7CisJCUVSUk9SKGNkZXYsICJjY2lkOiB1c2IgZXBvdXQgYXV0 b2NvbmZpZyBmYWlsZWRcbiIpOworCQlyZXQgPSAtRU5PREVWOworCQlnb3RvIGVwX2F1dG9fb3V0 X2ZhaWw7CisJfQorCWNjaWRnLT5vdXQgPSBlcDsKKwllcC0+ZHJpdmVyX2RhdGEgPSBjY2lkZzsK KworCS8qIHNldCBkZXNjcmlwdG9yIGR5bmFtaWMgdmFsdWVzICovCisJY2NpZF9jbGFzc19kZXNj LmR3RmVhdHVyZXMJPSBjcHVfdG9fbGUzMihjY2lkZy0+ZmVhdHVyZXMpOworCWNjaWRfY2xhc3Nf ZGVzYy5iUElOU3VwcG9ydAk9IGNjaWRnLT5waW5zdXBwb3J0OworCWNjaWRfY2xhc3NfZGVzYy53 TGNkTGF5b3V0CT0gY3B1X3RvX2xlMTYoY2NpZGctPmxjZGxheW91dCk7CisJY2NpZF9jbGFzc19k ZXNjLmJNYXhTbG90SW5kZXgJPSBjY2lkZy0+bnNsb3RzOworCWNjaWRfY2xhc3NfZGVzYy5kd1By b3RvY29scwk9IGNwdV90b19sZTMyKGNjaWRnLT5wcm90b2NvbHMpOworCisJaWYgKGNjaWRnLT5w cm90b2NvbHMgPT0gQ0NJRF9QUk9UT0NPTF9OT1RfU0VMKSB7CisJCWNjaWRnLT5wcm90b2NvbHMg PSBDQ0lEX1BST1RPQ09MX1QwIHwgQ0NJRF9QUk9UT0NPTF9UMTsKKwkJSU5GTyhjY2lkZy0+ZnVu Y3Rpb24uY29uZmlnLT5jZGV2LAorCQkJImNjaWQ6IE5vIHByb3RvY29sIHNlbGVjdGVkLiBTdXBw b3J0IGJvdGggVDAgYW5kIFQxLlxuIik7CisJfQorCisKKwljY2lkX2hzX2luX2Rlc2MuYkVuZHBv aW50QWRkcmVzcyA9CisJCQljY2lkX2ZzX2luX2Rlc2MuYkVuZHBvaW50QWRkcmVzczsKKwljY2lk X2hzX291dF9kZXNjLmJFbmRwb2ludEFkZHJlc3MgPQorCQkJY2NpZF9mc19vdXRfZGVzYy5iRW5k cG9pbnRBZGRyZXNzOworCisJcmV0ICA9IHVzYl9hc3NpZ25fZGVzY3JpcHRvcnMoZiwgY2NpZF9m c19kZXNjcywKKwkJCWNjaWRfaHNfZGVzY3MsIE5VTEwsIE5VTEwpOworCWlmIChyZXQpCisJCWdv dG8gZXBfYXV0b19vdXRfZmFpbDsKKworCS8qIGNyZWF0ZSBjaGFyIGRldmljZSAqLworCWNkZXZf aW5pdCgmY2NpZGctPmNkZXYsICZmX2NjaWRnX2ZvcHMpOworCWRldiA9IE1LREVWKG1ham9yLCBj Y2lkZy0+bWlub3IpOworCXJldCA9IGNkZXZfYWRkKCZjY2lkZy0+Y2RldiwgZGV2LCAxKTsKKwlp ZiAocmV0KQorCQlnb3RvIGZhaWxfZnJlZV9kZXNjczsKKworCWRldmljZSA9IGRldmljZV9jcmVh dGUoY2NpZGdfY2xhc3MsIE5VTEwsIGRldiwgTlVMTCwKKwkJCSAgICAgICAiJXMlZCIsICJjY2lk ZyIsIGNjaWRnLT5taW5vcik7CisJaWYgKElTX0VSUihkZXZpY2UpKSB7CisJCXJldCA9IFBUUl9F UlIoZGV2aWNlKTsKKwkJZ290byBkZWw7CisJfQorCisJcmV0dXJuIDA7CisKK2RlbDoKKwljZGV2 X2RlbCgmY2NpZGctPmNkZXYpOworZmFpbF9mcmVlX2Rlc2NzOgorCXVzYl9mcmVlX2FsbF9kZXNj cmlwdG9ycyhmKTsKK2VwX2F1dG9fb3V0X2ZhaWw6CisJY2NpZGctPm91dC0+ZHJpdmVyX2RhdGEg PSBOVUxMOworCWNjaWRnLT5vdXQgPSBOVUxMOworZXBfYXV0b19pbl9mYWlsOgorCWNjaWRnLT5p bi0+ZHJpdmVyX2RhdGEgPSBOVUxMOworCWNjaWRnLT5pbiA9IE5VTEw7CisJRVJST1IoZi0+Y29u ZmlnLT5jZGV2LCAiY2NpZGdfYmluZCBGQUlMRURcbiIpOworCisJcmV0dXJuIHJldDsKK30KKwor c3RhdGljIHN0cnVjdCB1c2JfZnVuY3Rpb24gKmNjaWRnX2FsbG9jKHN0cnVjdCB1c2JfZnVuY3Rp b25faW5zdGFuY2UgKmZpKQoreworCXN0cnVjdCBmX2NjaWRnICpjY2lkZzsKKwlzdHJ1Y3QgZl9j Y2lkZ19vcHRzICpvcHRzID0gY29udGFpbmVyX29mKGZpLCBzdHJ1Y3QgZl9jY2lkZ19vcHRzLAor CQkJCQkJZnVuY19pbnN0KTsKKwlpbnQgcmV0OworCisJY2NpZGcgPSBremFsbG9jKHNpemVvZigq Y2NpZGcpLCBHRlBfS0VSTkVMKTsKKwlpZiAoIWNjaWRnKQorCQlyZXR1cm4gRVJSX1BUUigtRU5P TUVNKTsKKworCXNwaW5fbG9ja19pbml0KCZjY2lkZy0+bG9jayk7CisKKwlyZXQgPSBjY2lkZ19i dWxrX2RldmljZV9pbml0KGNjaWRnKTsKKwlpZiAocmV0KSB7CisJCWtmcmVlKGNjaWRnKTsKKwkJ cmV0dXJuIEVSUl9QVFIocmV0KTsKKwl9CisKKwltdXRleF9sb2NrKCZvcHRzLT5sb2NrKTsKKwlv cHRzLT5yZWZjbnQrKzsKKworCWNjaWRnLT5taW5vciA9IG9wdHMtPm1pbm9yOworCWNjaWRnLT5m ZWF0dXJlcyA9IG9wdHMtPmZlYXR1cmVzOworCWNjaWRnLT5wcm90b2NvbHMgPSBvcHRzLT5wcm90 b2NvbHM7CisJY2NpZGctPnBpbnN1cHBvcnQgPSBvcHRzLT5waW5zdXBwb3J0OworCWNjaWRnLT5u c2xvdHMgPSBvcHRzLT5uc2xvdHM7CisJbXV0ZXhfdW5sb2NrKCZvcHRzLT5sb2NrKTsKKworCWNj aWRnLT5mdW5jdGlvbi5uYW1lCT0gImNjaWQiOworCWNjaWRnLT5mdW5jdGlvbi5iaW5kCT0gY2Np ZGdfZnVuY3Rpb25fYmluZDsKKwljY2lkZy0+ZnVuY3Rpb24udW5iaW5kCT0gY2NpZGdfZnVuY3Rp b25fdW5iaW5kOworCWNjaWRnLT5mdW5jdGlvbi5zZXRfYWx0CT0gY2NpZGdfZnVuY3Rpb25fc2V0 X2FsdDsKKwljY2lkZy0+ZnVuY3Rpb24uZGlzYWJsZQk9IGNjaWRnX2Z1bmN0aW9uX2Rpc2FibGU7 CisJY2NpZGctPmZ1bmN0aW9uLnNldHVwCT0gY2NpZGdfZnVuY3Rpb25fc2V0dXA7CisJY2NpZGct PmZ1bmN0aW9uLmZyZWVfZnVuYyA9IGNjaWRnX2Z1bmN0aW9uX2ZyZWU7CisKKwlyZXR1cm4gJmNj aWRnLT5mdW5jdGlvbjsKK30KKworc3RhdGljIHZvaWQgY2NpZGdfZnJlZV9pbnN0KHN0cnVjdCB1 c2JfZnVuY3Rpb25faW5zdGFuY2UgKmYpCit7CisJc3RydWN0IGZfY2NpZGdfb3B0cyAqb3B0cyA9 IGNvbnRhaW5lcl9vZihmLCBzdHJ1Y3QgZl9jY2lkZ19vcHRzLAorCQkJCQkJZnVuY19pbnN0KTsK KwltdXRleF9sb2NrKCZjY2lkZ19pZGFfbG9jayk7CisKKwljY2lkZ19wdXRfbWlub3Iob3B0cy0+ bWlub3IpOworCWlmIChpZGFfaXNfZW1wdHkoJmNjaWRnX2lkYSkpCisJCWNjaWRnX2NsZWFudXAo KTsKKworCW11dGV4X3VubG9jaygmY2NpZGdfaWRhX2xvY2spOworCisJa2ZyZWUob3B0cyk7Cit9 CisKK3N0YXRpYyBzdHJ1Y3QgdXNiX2Z1bmN0aW9uX2luc3RhbmNlICpjY2lkZ19hbGxvY19pbnN0 KHZvaWQpCit7CisJc3RydWN0IGZfY2NpZGdfb3B0cyAqb3B0czsKKwlzdHJ1Y3QgdXNiX2Z1bmN0 aW9uX2luc3RhbmNlICpyZXQ7CisJaW50IHN0YXR1cyA9IDA7CisKKwlvcHRzID0ga3phbGxvYyhz aXplb2YoKm9wdHMpLCBHRlBfS0VSTkVMKTsKKwlpZiAoIW9wdHMpCisJCXJldHVybiBFUlJfUFRS KC1FTk9NRU0pOworCisJbXV0ZXhfaW5pdCgmb3B0cy0+bG9jayk7CisJb3B0cy0+ZnVuY19pbnN0 LmZyZWVfZnVuY19pbnN0ID0gY2NpZGdfZnJlZV9pbnN0OworCXJldCA9ICZvcHRzLT5mdW5jX2lu c3Q7CisKKwltdXRleF9sb2NrKCZjY2lkZ19pZGFfbG9jayk7CisKKwlpZiAoaWRhX2lzX2VtcHR5 KCZjY2lkZ19pZGEpKSB7CisJCXN0YXR1cyA9IGNjaWRnX3NldHVwKCk7CisJCWlmIChzdGF0dXMp ICB7CisJCQlyZXQgPSBFUlJfUFRSKHN0YXR1cyk7CisJCQlrZnJlZShvcHRzKTsKKwkJCWdvdG8g dW5sb2NrOworCQl9CisJfQorCisJb3B0cy0+bWlub3IgPSBjY2lkZ19nZXRfbWlub3IoKTsKKwlp ZiAob3B0cy0+bWlub3IgPCAwKSB7CisJCXJldCA9IEVSUl9QVFIob3B0cy0+bWlub3IpOworCQlr ZnJlZShvcHRzKTsKKwkJaWYgKGlkYV9pc19lbXB0eSgmY2NpZGdfaWRhKSkKKwkJCWNjaWRnX2Ns ZWFudXAoKTsKKwkJZ290byB1bmxvY2s7CisJfQorCisJY29uZmlnX2dyb3VwX2luaXRfdHlwZV9u YW1lKCZvcHRzLT5mdW5jX2luc3QuZ3JvdXAsCisJCQkiIiwgJmNjaWRnX2Z1bmNfdHlwZSk7CisK K3VubG9jazoKKwltdXRleF91bmxvY2soJmNjaWRnX2lkYV9sb2NrKTsKKwlyZXR1cm4gcmV0Owor fQorCitERUNMQVJFX1VTQl9GVU5DVElPTl9JTklUKGNjaWQsIGNjaWRnX2FsbG9jX2luc3QsIGNj aWRnX2FsbG9jKTsKKworTU9EVUxFX0RFU0NSSVBUSU9OKCJVU0IgQ0NJRCBHYWRnZXQgZHJpdmVy Iik7CitNT0RVTEVfQVVUSE9SKCJNYXJjdXMgRm9sa2Vzc29uIDxtYXJjdXMuZm9sa2Vzc29uQGdt YWlsLmNvbT4iKTsKK01PRFVMRV9MSUNFTlNFKCJHUEwgdjIiKTsKZGlmZiAtLWdpdCBhL2RyaXZl cnMvdXNiL2dhZGdldC9mdW5jdGlvbi9mX2NjaWQuaCBiL2RyaXZlcnMvdXNiL2dhZGdldC9mdW5j dGlvbi9mX2NjaWQuaApuZXcgZmlsZSBtb2RlIDEwMDY0NAppbmRleCAwMDAwMDAwMDAwMDAuLmYx MDUzZWM1YzRkOQotLS0gL2Rldi9udWxsCisrKyBiL2RyaXZlcnMvdXNiL2dhZGdldC9mdW5jdGlv bi9mX2NjaWQuaApAQCAtMCwwICsxLDkxIEBACisvKiBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjog R1BMLTIuMCAqLworLyoKKyAqIENvcHlyaWdodCAoQykgMjAxOCBNYXJjdXMgRm9sa2Vzc29uIDxt YXJjdXMuZm9sa2Vzc29uQGdtYWlsLmNvbT4KKyAqLworCisjaWZuZGVmIEZfQ0NJRF9ICisjZGVm aW5lIEZfQ0NJRF9ICisKKyNkZWZpbmUgQ0NJRDFfMTAgICAgICAgICAgICAgICAgMHgwMTEwCisj ZGVmaW5lIENDSURfREVDUklQVE9SX1RZUEUgICAgIDB4MjEKKyNkZWZpbmUgQUJEQVRBX1NJWkUJ CTUxMgorI2RlZmluZSBTTUFSVF9DQVJEX0RFVklDRV9DTEFTUwkweDBCCisKKy8qIENDSUQgQ2xh c3MgU3BlY2lmaWMgUmVxdWVzdCAqLworI2RlZmluZSBDQ0lER0VORVJJQ1JFUV9BQk9SVCAgICAg ICAgICAgICAgICAgICAgMHgwMQorI2RlZmluZSBDQ0lER0VORVJJQ1JFUV9HRVRfQ0xPQ0tfRlJF UVVFTkNJRVMgICAgMHgwMgorI2RlZmluZSBDQ0lER0VORVJJQ1JFUV9HRVRfREFUQV9SQVRFUyAg ICAgICAgICAgMHgwMworCisvKiBTdXBwb3J0ZWQgdm9sdGFnZXMgKi8KKyNkZWZpbmUgQ0NJRF9W T0xUU19BVVRPICAgICAgICAgICAgICAgICAgICAgICAgICAgICAweDAwCisjZGVmaW5lIENDSURf Vk9MVFNfNV8wICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMHgwMQorI2RlZmluZSBDQ0lE X1ZPTFRTXzNfMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDB4MDIKKyNkZWZpbmUgQ0NJ RF9WT0xUU18xXzggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAweDAzCisKK3N0cnVjdCBm X2NjaWRnX29wdHMgeworCXN0cnVjdCB1c2JfZnVuY3Rpb25faW5zdGFuY2UgZnVuY19pbnN0Owor CWludAltaW5vcjsKKwlfX3UzMglmZWF0dXJlczsKKwlfX3UzMglwcm90b2NvbHM7CisJX191OAlw aW5zdXBwb3J0OworCV9fdTgJbnNsb3RzOworCV9fdTgJbGNkbGF5b3V0OworCisJLyoKKwkgKiBQ cm90ZWN0IHRoZSBkYXRhIGZvcm0gY29uY3VycmVudCBhY2Nlc3MgYnkgcmVhZC93cml0ZQorCSAq IGFuZCBjcmVhdGUgc3ltbGluay9yZW1vdmUgc3ltbGluay4KKwkgKi8KKwlzdHJ1Y3QgbXV0ZXgJ bG9jazsKKwlpbnQJCXJlZmNudDsKK307CisKK3N0cnVjdCBjY2lkZ19idWxrX2luX2hlYWRlciB7 CisJX191OAliTWVzc2FnZVR5cGU7CisJX19sZTMyCXdMZW5ndGg7CisJX191OAliU2xvdDsKKwlf X3U4CWJTZXE7CisJX191OAliU3RhdHVzOworCV9fdTgJYkVycm9yOworCV9fdTgJYlNwZWNpZmlj OworCV9fdTgJYWJEYXRhW0FCREFUQV9TSVpFXTsKKwlfX3U4CWJTaXplVG9TZW5kOworfSBfX3Bh Y2tlZDsKKworc3RydWN0IGNjaWRnX2J1bGtfb3V0X2hlYWRlciB7CisJX191OAkgYk1lc3NhZ2VU eXBlOworCV9fbGUzMgkgd0xlbmd0aDsKKwlfX3U4CSBiU2xvdDsKKwlfX3U4CSBiU2VxOworCV9f dTgJIGJTcGVjaWZpY18wOworCV9fdTgJIGJTcGVjaWZpY18xOworCV9fdTgJIGJTcGVjaWZpY18y OworCV9fdTgJIEFQRFVbQUJEQVRBX1NJWkVdOworfSBfX3BhY2tlZDsKKworc3RydWN0IGNjaWRf Y2xhc3NfZGVzY3JpcHRvciB7CisJX191OAliTGVuZ3RoOworCV9fdTgJYkRlc2NyaXB0b3JUeXBl OworCV9fbGUxNgliY2RDQ0lEOworCV9fdTgJYk1heFNsb3RJbmRleDsKKwlfX3U4CWJWb2x0YWdl U3VwcG9ydDsKKwlfX2xlMzIJZHdQcm90b2NvbHM7CisJX19sZTMyCWR3RGVmYXVsdENsb2NrOwor CV9fbGUzMglkd01heGltdW1DbG9jazsKKwlfX3U4CWJOdW1DbG9ja1N1cHBvcnRlZDsKKwlfX2xl MzIJZHdEYXRhUmF0ZTsKKwlfX2xlMzIJZHdNYXhEYXRhUmF0ZTsKKwlfX3U4CWJOdW1EYXRhUmF0 ZXNTdXBwb3J0ZWQ7CisJX19sZTMyCWR3TWF4SUZTRDsKKwlfX2xlMzIJZHdTeW5jaFByb3RvY29s czsKKwlfX2xlMzIJZHdNZWNoYW5pY2FsOworCV9fbGUzMglkd0ZlYXR1cmVzOworCV9fbGUzMglk d01heENDSURNZXNzYWdlTGVuZ3RoOworCV9fdTgJYkNsYXNzR2V0UmVzcG9uc2U7CisJX191OAli Q2xhc3NFbnZlbG9wZTsKKwlfX2xlMTYJd0xjZExheW91dDsKKwlfX3U4CWJQSU5TdXBwb3J0Owor CV9fdTgJYk1heENDSURCdXN5U2xvdHM7Cit9IF9fcGFja2VkOworCisKKyNlbmRpZgpkaWZmIC0t Z2l0IGEvaW5jbHVkZS91YXBpL2xpbnV4L3VzYi9jY2lkLmggYi9pbmNsdWRlL3VhcGkvbGludXgv dXNiL2NjaWQuaApuZXcgZmlsZSBtb2RlIDEwMDY0NAppbmRleCAwMDAwMDAwMDAwMDAuLmE5Zjg5 MDZmMTk0OAotLS0gL2Rldi9udWxsCisrKyBiL2luY2x1ZGUvdWFwaS9saW51eC91c2IvY2NpZC5o CkBAIC0wLDAgKzEsOTMgQEAKKy8qIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBHUEwtMi4wIFdJ VEggTGludXgtc3lzY2FsbC1ub3RlICovCisvKgorICogQ29weXJpZ2h0IChDKSAyMDE4IE1hcmN1 cyBGb2xrZXNzb24gPG1hcmN1cy5mb2xrZXNzb25AZ21haWwuY29tPgorICoKKyAqIFRoaXMgZmls ZSBob2xkcyBVU0IgY29uc3RhbnRzIGRlZmluZWQgYnkgdGhlIENDSUQgU3BlY2lmaWNhdGlvbi4K KyAqLworCisjaWZuZGVmIENDSURfSAorI2RlZmluZSBDQ0lEX0gKKworLyogU2xvdCBlcnJvciBy ZWdpc3RlciB3aGVuIGJtQ29tbWFuZFN0YXR1cyA9IDEgKi8KKyNkZWZpbmUgQ0NJRF9DTURfQUJP UlRFRCAgICAgICAgICAgICAgICAgICAgICAgICAgICAweEZGCisjZGVmaW5lIENDSURfSUNDX01V VEUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMHhGRQorI2RlZmluZSBDQ0lEX1hGUl9Q QVJJVFlfRVJST1IgICAgICAgICAgICAgICAgICAgICAgIDB4RkQKKyNkZWZpbmUgQ0NJRF9YRlJf T1ZFUlJVTiAgICAgICAgICAgICAgICAgICAgICAgICAgICAweEZDCisjZGVmaW5lIENDSURfSFdf RVJST1IgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMHhGQgorI2RlZmluZSBDQ0lEX0JB RF9BVFJfVFMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDB4RjgKKyNkZWZpbmUgQ0NJRF9C QURfQVRSX1RDSyAgICAgICAgICAgICAgICAgICAgICAgICAgICAweEY3CisjZGVmaW5lIENDSURf SUNDX1BST1RPQ09MX05PVF9TVVBQT1JURUQgICAgICAgICAgICAgMHhGNgorI2RlZmluZSBDQ0lE X0lDQ19DTEFTU19OT1RfU1VQUE9SVEVEICAgICAgICAgICAgICAgIDB4RjUKKyNkZWZpbmUgQ0NJ RF9QUk9DRURVUkVfQllURV9DT05GTElDVCAgICAgICAgICAgICAgICAweEY0CisjZGVmaW5lIEND SURfREVBQ1RJVkFURURfUFJPVE9DT0wgICAgICAgICAgICAgICAgICAgMHhGMworI2RlZmluZSBD Q0lEX0JVU1lfV0lUSF9BVVRPX1NFUVVFTkNFICAgICAgICAgICAgICAgIDB4RjIKKyNkZWZpbmUg Q0NJRF9QSU5fVElNRU9VVCAgICAgICAgICAgICAgICAgICAgICAgICAgICAweEYwCisjZGVmaW5l IENDSURfUElOX0NBTkNFTExFRCAgICAgICAgICAgICAgICAgICAgICAgICAgMHhFRgorI2RlZmlu ZSBDQ0lEX0NNRF9TTE9UX0JVU1kgICAgICAgICAgICAgICAgICAgICAgICAgIDB4RTAKKworLyog UEMgdG8gUkRSIG1lc3NhZ2VzIChidWxrIG91dCkgKi8KKyNkZWZpbmUgQ0NJRF9QQ19UT19SRFJf SUNDUE9XRVJPTiAgICAgICAgICAgICAgICAgICAweDYyCisjZGVmaW5lIENDSURfUENfVE9fUkRS X0lDQ1BPV0VST0ZGICAgICAgICAgICAgICAgICAgMHg2MworI2RlZmluZSBDQ0lEX1BDX1RPX1JE Ul9HRVRTTE9UU1RBVFVTICAgICAgICAgICAgICAgIDB4NjUKKyNkZWZpbmUgQ0NJRF9QQ19UT19S RFJfWEZSQkxPQ0sgICAgICAgICAgICAgICAgICAgICAweDZGCisjZGVmaW5lIENDSURfUENfVE9f UkRSX0dFVFBBUkFNRVRFUlMgICAgICAgICAgICAgICAgMHg2QworI2RlZmluZSBDQ0lEX1BDX1RP X1JEUl9SRVNFVFBBUkFNRVRFUlMgICAgICAgICAgICAgIDB4NkQKKyNkZWZpbmUgQ0NJRF9QQ19U T19SRFJfU0VUUEFSQU1FVEVSUyAgICAgICAgICAgICAgICAweDYxCisjZGVmaW5lIENDSURfUENf VE9fUkRSX0VTQ0FQRSAgICAgICAgICAgICAgICAgICAgICAgMHg2QgorI2RlZmluZSBDQ0lEX1BD X1RPX1JEUl9JQ0NDTE9DSyAgICAgICAgICAgICAgICAgICAgIDB4NkUKKyNkZWZpbmUgQ0NJRF9Q Q19UT19SRFJfVDBBUERVICAgICAgICAgICAgICAgICAgICAgICAweDZBCisjZGVmaW5lIENDSURf UENfVE9fUkRSX1NFQ1VSRSAgICAgICAgICAgICAgICAgICAgICAgMHg2OQorI2RlZmluZSBDQ0lE X1BDX1RPX1JEUl9NRUNIQU5JQ0FMICAgICAgICAgICAgICAgICAgIDB4NzEKKyNkZWZpbmUgQ0NJ RF9QQ19UT19SRFJfQUJPUlQgICAgICAgICAgICAgICAgICAgICAgICAweDcyCisjZGVmaW5lIEND SURfUENfVE9fUkRSX1NFVERBVEFSQVRFQU5EQ0xPQ0tGUkVRVUVOQ1kgMHg3MworCisvKiBSRFIg dG8gUEMgbWVzc2FnZXMgKGJ1bGsgaW4pICovCisjZGVmaW5lIENDSURfUkRSX1RPX1BDX0RBVEFC TE9DSyAgICAgICAgICAgICAgICAgICAgMHg4MAorI2RlZmluZSBDQ0lEX1JEUl9UT19QQ19TTE9U U1RBVFVTICAgICAgICAgICAgICAgICAgIDB4ODEKKyNkZWZpbmUgQ0NJRF9SRFJfVE9fUENfUEFS QU1FVEVSUyAgICAgICAgICAgICAgICAgICAweDgyCisjZGVmaW5lIENDSURfUkRSX1RPX1BDX0VT Q0FQRSAgICAgICAgICAgICAgICAgICAgICAgMHg4MworI2RlZmluZSBDQ0lEX1JEUl9UT19QQ19E QVRBUkFURUFORENMT0NLRlJFUVVFTkNZICAgIDB4ODQKKworLyogQ2xhc3MgRmVhdHVyZXMgKi8K KworLyogTm8gc3BlY2lhbCBjaGFyYWN0ZXJpc3RpY3MgKi8KKyNkZWZpbmUgQ0NJRF9GRUFUVVJF U19OQURBICAgICAgIDB4MDAwMDAwMDAKKy8qIEF1dG9tYXRpYyBwYXJhbWV0ZXIgY29uZmlndXJh dGlvbiBiYXNlZCBvbiBBVFIgZGF0YSAqLworI2RlZmluZSBDQ0lEX0ZFQVRVUkVTX0FVVE9fUENP TkYgMHgwMDAwMDAwMgorLyogQXV0b21hdGljIGFjdGl2YXRpb24gb2YgSUNDIG9uIGluc2VydGlu ZyAqLworI2RlZmluZSBDQ0lEX0ZFQVRVUkVTX0FVVE9fQUNUSVYgMHgwMDAwMDAwNAorLyogQXV0 b21hdGljIElDQyB2b2x0YWdlIHNlbGVjdGlvbiAqLworI2RlZmluZSBDQ0lEX0ZFQVRVUkVTX0FV VE9fVk9MVCAgMHgwMDAwMDAwOAorLyogQXV0b21hdGljIElDQyBjbG9jayBmcmVxdWVuY3kgY2hh bmdlICovCisjZGVmaW5lIENDSURfRkVBVFVSRVNfQVVUT19DTE9DSyAweDAwMDAwMDEwCisvKiBB dXRvbWF0aWMgYmF1ZCByYXRlIGNoYW5nZSAqLworI2RlZmluZSBDQ0lEX0ZFQVRVUkVTX0FVVE9f QkFVRCAgMHgwMDAwMDAyMAorLypBdXRvbWF0aWMgcGFyYW1ldGVycyBuZWdvdGlhdGlvbiBtYWRl IGJ5IHRoZSBDQ0lEICovCisjZGVmaW5lIENDSURfRkVBVFVSRVNfQVVUT19QTkVHTyAweDAwMDAw MDQwCisvKiBBdXRvbWF0aWMgUFBTIG1hZGUgYnkgdGhlIENDSUQgYWNjb3JkaW5nIHRvIHRoZSBh Y3RpdmUgcGFyYW1ldGVycyAqLworI2RlZmluZSBDQ0lEX0ZFQVRVUkVTX0FVVE9fUFBTICAgMHgw MDAwMDA4MAorLyogQ0NJRCBjYW4gc2V0IElDQyBpbiBjbG9jayBzdG9wIG1vZGUgKi8KKyNkZWZp bmUgQ0NJRF9GRUFUVVJFU19JQ0NTVE9QICAgIDB4MDAwMDAxMDAKKy8qIE5BRCB2YWx1ZSBvdGhl ciB0aGFuIDAwIGFjY2VwdGVkIChUPTEgcHJvdG9jb2wgaW4gdXNlKSAqLworI2RlZmluZSBDQ0lE X0ZFQVRVUkVTX05BRCAgICAgICAgMHgwMDAwMDIwMAorLyogQXV0b21hdGljIElGU0QgZXhjaGFu Z2UgYXMgZmlyc3QgZXhjaGFuZ2UgKFQ9MSBwcm90b2NvbCBpbiB1c2UpICovCisjZGVmaW5lIEND SURfRkVBVFVSRVNfQVVUT19JRlNEICAweDAwMDAwNDAwCisvKiBUUERVIGxldmVsIGV4Y2hhbmdl cyB3aXRoIENDSUQgKi8KKyNkZWZpbmUgQ0NJRF9GRUFUVVJFU19FWENfVFBEVSAgIDB4MDAwMTAw MDAKKy8qIFNob3J0IEFQRFUgbGV2ZWwgZXhjaGFuZ2Ugd2l0aCBDQ0lEICovCisjZGVmaW5lIEND SURfRkVBVFVSRVNfRVhDX1NBUERVICAweDAwMDIwMDAwCisvKiBTaG9ydCBhbmQgRXh0ZW5kZWQg QVBEVSBsZXZlbCBleGNoYW5nZSB3aXRoIENDSUQgKi8KKyNkZWZpbmUgQ0NJRF9GRUFUVVJFU19F WENfQVBEVSAgIDB4MDAwNDAwMDAKKy8qIFVTQiBXYWtlIHVwIHNpZ25hbGluZyBzdXBwb3J0ZWQg b24gY2FyZCBpbnNlcnRpb24gYW5kIHJlbW92YWwgKi8KKyNkZWZpbmUgQ0NJRF9GRUFUVVJFU19X QUtFVVAJMHgwMDEwMDAwMAorCisvKiBTdXBwb3J0ZWQgcHJvdG9jb2xzICovCisjZGVmaW5lIEND SURfUFJPVE9DT0xfTk9UX1NFTAkweDAwCisjZGVmaW5lIENDSURfUFJPVE9DT0xfVDAJMHgwMQor I2RlZmluZSBDQ0lEX1BST1RPQ09MX1QxCTB4MDIKKworI2RlZmluZSBDQ0lEX1BJTlNVUE9PUlRf Tk9ORQkJKDAgPDwgMCkKKyNkZWZpbmUgQ0NJRF9QSU5TVVBPT1JUX1ZFUklGSUNBVElPTgkoMSA8 PCAxKQorI2RlZmluZSBDQ0lEX1BJTlNVUE9PUlRfTU9ESUZJQ0FUSU9OCSgxIDw8IDIpCisKKyNl bmRpZgo= From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.6 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 748AAC43142 for ; Tue, 26 Jun 2018 08:16:10 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 095B52668E for ; Tue, 26 Jun 2018 08:16:10 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="VO5VkWt/" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 095B52668E Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932191AbeFZIOy (ORCPT ); Tue, 26 Jun 2018 04:14:54 -0400 Received: from mail-lf0-f65.google.com ([209.85.215.65]:37488 "EHLO mail-lf0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752708AbeFZIOl (ORCPT ); Tue, 26 Jun 2018 04:14:41 -0400 Received: by mail-lf0-f65.google.com with SMTP id g21-v6so16653813lfb.4; Tue, 26 Jun 2018 01:14:39 -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=Efa3XfD1ZMeOnSjqdTjFSY0MvTFWNgy1YAiIREQd5+U=; b=VO5VkWt/0D/jRF/bPdu5i5FK8o3jx4r7VIkNlELw/VvVKENXaGjrtsRgGYBsUBYLgh lyTTaIFCIA8dcCL5K6rKZLBJmcK8C7p0xR9d/jp5wF/jqccxx5rSRjqDOw4YUS676To1 U1Kem7wwLCWCcsTrp39WVEkxCbtgEDmJco+hajCQSRLG95+jATswoiuxUfwz9Gw2tLT8 uCo6E8Du9WCB0+9JjKBh1+cV8hqpIVEvwg1C1na/HAF7ubMQEr1FeruFLsHXbSj9n+ib sbSKpI96gliUBczKS5Vk+1JyMkzDAWkAM1unvRTcQKPM4IZgRUZMb6sXee209/6ECEjS 1KGg== 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=Efa3XfD1ZMeOnSjqdTjFSY0MvTFWNgy1YAiIREQd5+U=; b=hYE5yCd385+x6bwwUfG+8voyNUSJrYYScT6dJjxcQB6iooSIse007kkpgfBN8fQCzh kQxhiTVBAqbOcAJqqBXMA+BI02lrwYHGgdkFMMpaZbKWW5joOec2dqm0L3YoKd5PjFuQ SsqCGZnnck5woj84v+ar7Wjp5gr9sr4+/nU4Dmto2EsQplO6WGZRMvMnUL+p8p1vyeZs aS6yXvn5e1etVMhH84x6hN6RPpLFv+dLH0fb5nmYA9eKnKjbl4rfPxAkyBgXLAg2sJbq OTd9+eKF71K+kUEAOmcPsRKy0s2KQPlBCeKwOEMEJ0hu6RlcbTVbS1n1qVAntGB96rnT pGvQ== X-Gm-Message-State: APt69E3tzz5mdHpmONJORyhVTxYH/fMpUDPKiqI5j0SpgCntETicuSfZ 9nEF5A4wBFdzym9M5eflSws= X-Google-Smtp-Source: AAOMgpcI/9Zf6SSppzESChuvR4W07zkIK99gv1kdX7Ma7XK1TF+I3q1fK3CBa4oGcn9eJE2jGOaXDg== X-Received: by 2002:a19:4a53:: with SMTP id x80-v6mr466836lfa.69.1530000878629; Tue, 26 Jun 2018 01:14:38 -0700 (PDT) Received: from localhost.localdomain (c-2ec26030-74736162.cust.telenor.se. [46.194.96.48]) by smtp.gmail.com with ESMTPSA id p28-v6sm149783lja.13.2018.06.26.01.14.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 26 Jun 2018 01:14:37 -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 v4 1/3] usb: gadget: ccid: add support for USB CCID Gadget Device Date: Tue, 26 Jun 2018 10:14:20 +0200 Message-Id: <20180626081422.19237-1-marcus.folkesson@gmail.com> X-Mailer: git-send-email 2.18.0 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk 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: v4: - fix comments from Andy, Thanks! - add "WITH Linux-syscall-note" in the SPDX identifier for uapi-header 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 | 989 +++++++++++++++++++++++++++ drivers/usb/gadget/function/f_ccid.h | 91 +++ include/uapi/linux/usb/ccid.h | 93 +++ 5 files changed, 1191 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..2a890dca31e1 --- /dev/null +++ b/drivers/usb/gadget/function/f_ccid.c @@ -0,0 +1,989 @@ +// 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 + +#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)) { + ret = PTR_ERR(ccidg_class); + ccidg_class = NULL; + return ret; + } + + 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; + ida_destroy(&ccidg_ida); +} + +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); + req = list_first_entry_or_null(head, struct usb_request, list); + if (req) + 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->buf = kmalloc(len, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + return ERR_PTR(-ENOMEM); + } + req->length = len; + + return req; +} + +static void ccidg_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (!req) + return; + + 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; + + put_unaligned(ccid_class_desc.dwDefaultClock, req->buf); + ret = sizeof(ccid_class_desc.dwDefaultClock); + break; + + case CCIDGENERICREQ_GET_DATA_RATES: + if (w_length > sizeof(ccid_class_desc.dwDataRate)) + break; + + put_unaligned(ccid_class_desc.dwDataRate, req->buf); + 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 0; + +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 ccidg_bulk_dev *bulk_dev; + struct f_ccidg *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; + size_t 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 = min_t(size_t, req->actual, count); + + if (copy_to_user(buf, req->buf, xfer)) + return -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 = func_to_ccidg(f); + struct f_ccidg_opts *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 = container_of(fi, struct f_ccidg_opts, + func_inst); + 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); + } + + 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 = 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..a9f8906f1948 --- /dev/null +++ b/include/uapi/linux/usb/ccid.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * 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 (0 << 0) +#define CCID_PINSUPOORT_VERIFICATION (1 << 1) +#define CCID_PINSUPOORT_MODIFICATION (1 << 2) + +#endif -- 2.18.0