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 0237E7D048 for ; Sat, 26 May 2018 20:35:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1032377AbeEZUfM (ORCPT ); Sat, 26 May 2018 16:35:12 -0400 Received: from mail-wr0-f194.google.com ([209.85.128.194]:36424 "EHLO mail-wr0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1032365AbeEZUfK (ORCPT ); Sat, 26 May 2018 16:35:10 -0400 Received: by mail-wr0-f194.google.com with SMTP id k5-v6so14408916wrn.3; Sat, 26 May 2018 13:35:09 -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=K3iJtMob/tFE/vTiLQpzNswgPCv3gb0e8raJxUce/T8=; b=mre1ATEsS9B/MceMRsVhzZL/97CLab1XIrfod85p5/4XMQWWtI5DgqYJGeAkjpa3PL VHLr4+LuUkc7XyYPPCHhvPHqnQki2ccwnGSTo6D3Jf/dbxMgQ2FIBoL/YljJAC84c6Ty Y8DrSUzRcLqZT23YBdcgLfH9ufFN+D8/LgQtsyRrNEsHZash4Qjn+QSWvu4HSlvR3C47 2ihFP7wWxLir84wy7rTP+iTq5wULkK/vr4E58XZhLX7eP+USxBdGEYCQQa8hyuOsbZ5u pmyE52vkbRwzB3xRgDHvhDz8c2jxh63JWbzQLwiQ+dOUfVlq4GhiTnFGFMLqDdKRM9zG eccA== 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=K3iJtMob/tFE/vTiLQpzNswgPCv3gb0e8raJxUce/T8=; b=obCE6RVUPz+KqMcfZRJWKj2IUT7XOzSNolZJIItob+UGmi7CfiTrxZTIOFkkrGAck2 Mb9s+tGZgoR9XYbie+1Qc0T/6Qfe1lkg4J7WoqDuLQuy2iVyWub/I7svtXyk3Cmh9XgE gGypDgwebwySmOr77C02GMNKQoQCujXQ2HUiNxeJDqCXiFIwC4OB3rdVPMlE5tn0PUGZ pvC7a0sRI9OF+NWNDYLryseSG4q76sSP5RRMtvWDAh9+XH9CGgccLYw6iewldtd/6a1n 1CFEGeoLNqpYS6j4zhKGtBtDuSmRdgJUCl7jmidEL5YZxMdf0pLRc3ccGCAYvSBBXDty NUfA== X-Gm-Message-State: ALKqPwcjg2XWbO4duvauYsT6+brJzv6Unv/Mhr8IOv1u0t+SX34xq5ZF pnZXju05EmWgEBs18mN4vmk= X-Google-Smtp-Source: ADUXVKJv6xzbKxrdguUdJqL5Ts4K6kJ8SSUo+258yiPxgp31gU9+PaejQgQqGh19H/pDvcB4IN4i0Q== X-Received: by 2002:a19:5388:: with SMTP id h8-v6mr3914697lfl.92.1527366908467; Sat, 26 May 2018 13:35:08 -0700 (PDT) Received: from localhost.localdomain (c-2ec2c701-74736162.cust.telenor.se. [46.194.199.1]) by smtp.gmail.com with ESMTPSA id d7-v6sm4953381ljc.45.2018.05.26.13.35.06 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 26 May 2018 13:35:07 -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 1/3] usb: gadget: ccid: add support for USB CCID Gadget Device Date: Sat, 26 May 2018 22:33:59 +0200 Message-Id: <20180526203401.16586-1-marcus.folkesson@gmail.com> X-Mailer: git-send-email 2.16.2 Sender: linux-doc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-doc@vger.kernel.org Signed-off-by: Marcus Folkesson --- drivers/usb/gadget/Kconfig | 17 + drivers/usb/gadget/function/Makefile | 1 + drivers/usb/gadget/function/f_ccid.c | 988 +++++++++++++++++++++++++++++++++++ drivers/usb/gadget/function/f_ccid.h | 91 ++++ include/uapi/linux/usb/ccid.h | 93 ++++ 5 files changed, 1190 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..9ff8615ca303 --- /dev/null +++ b/drivers/usb/gadget/function/f_ccid.c @@ -0,0 +1,988 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * f_ccid.c -- Chip Card Interface Device (CCID) function Driver + * + * Copyright (C) 2018 Marcus Folkesson + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f_ccid.h" +#include "u_f.h" + +/* Number of tx requests to allocate */ +#define N_TX_REQS 4 + +/* Maximum number of devices */ +#define CCID_MINORS 4 + +struct ccidg_bulk_dev { + atomic_t is_open; + atomic_t rx_req_busy; + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct usb_request *rx_req; + atomic_t rx_done; + struct list_head tx_idle; +}; + +struct f_ccidg { + struct usb_function_instance func_inst; + struct usb_function function; + spinlock_t lock; + atomic_t online; + + /* Character device */ + struct cdev cdev; + int minor; + + /* Dynamic attributes */ + u32 features; + u32 protocols; + u8 pinsupport; + u8 nslots; + u8 lcdlayout; + + /* Endpoints */ + struct usb_ep *in; + struct usb_ep *out; + struct ccidg_bulk_dev bulk_dev; +}; + +/* Interface Descriptor: */ +static struct usb_interface_descriptor ccid_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CSCID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, +}; + +/* CCID Class Descriptor */ +static struct ccid_class_descriptor ccid_class_desc = { + .bLength = sizeof(ccid_class_desc), + .bDescriptorType = CCID_DECRIPTOR_TYPE, + .bcdCCID = CCID1_10, + /* .bMaxSlotIndex = DYNAMIC */ + .bVoltageSupport = CCID_VOLTS_3_0, + /* .dwProtocols = DYNAMIC */ + .dwDefaultClock = 3580, + .dwMaximumClock = 3580, + .bNumClockSupported = 0, + .dwDataRate = 9600, + .dwMaxDataRate = 9600, + .bNumDataRatesSupported = 0, + .dwMaxIFSD = 0, + .dwSynchProtocols = 0, + .dwMechanical = 0, + /* .dwFeatures = DYNAMIC */ + + /* extended APDU level Message Length */ + .dwMaxCCIDMessageLength = 0x200, + .bClassGetResponse = 0x0, + .bClassEnvelope = 0x0, + /* .wLcdLayout = DYNAMIC */ + /* .bPINSupport = DYNAMIC */ + .bMaxCCIDBusySlots = 1 +}; + +/* Full speed support: */ +static struct usb_endpoint_descriptor ccid_fs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_endpoint_descriptor ccid_fs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_descriptor_header *ccid_fs_descs[] = { + (struct usb_descriptor_header *) &ccid_interface_desc, + (struct usb_descriptor_header *) &ccid_class_desc, + (struct usb_descriptor_header *) &ccid_fs_in_desc, + (struct usb_descriptor_header *) &ccid_fs_out_desc, + NULL, +}; + +/* High speed support: */ +static struct usb_endpoint_descriptor ccid_hs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor ccid_hs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_descriptor_header *ccid_hs_descs[] = { + (struct usb_descriptor_header *) &ccid_interface_desc, + (struct usb_descriptor_header *) &ccid_class_desc, + (struct usb_descriptor_header *) &ccid_hs_in_desc, + (struct usb_descriptor_header *) &ccid_hs_out_desc, + NULL, +}; + +static DEFINE_IDA(ccidg_ida); +static int major; +static DEFINE_MUTEX(ccidg_ida_lock); /* protects access to ccidg_ida */ +static struct class *ccidg_class; + +static inline struct f_ccidg_opts *to_f_ccidg_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_ccidg_opts, + func_inst.group); +} + +static inline struct f_ccidg *func_to_ccidg(struct usb_function *f) +{ + return container_of(f, struct f_ccidg, function); +} + +static inline int ccidg_get_minor(void) +{ + int ret; + + ret = ida_simple_get(&ccidg_ida, 0, 0, GFP_KERNEL); + if (ret >= CCID_MINORS) { + ida_simple_remove(&ccidg_ida, ret); + ret = -ENODEV; + } + + return ret; +} + +static inline void ccidg_put_minor(int minor) +{ + ida_simple_remove(&ccidg_ida, minor); +} + +static int ccidg_setup(void) +{ + int ret; + dev_t dev; + + ccidg_class = class_create(THIS_MODULE, "ccidg"); + if (IS_ERR(ccidg_class)) { + ccidg_class = NULL; + return PTR_ERR(ccidg_class); + } + + ret = alloc_chrdev_region(&dev, 0, CCID_MINORS, "ccidg"); + if (ret) { + class_destroy(ccidg_class); + ccidg_class = NULL; + return ret; + } + + major = MAJOR(dev); + + return 0; +} + +static void ccidg_cleanup(void) +{ + if (major) { + unregister_chrdev_region(MKDEV(major, 0), CCID_MINORS); + major = 0; + } + + class_destroy(ccidg_class); + ccidg_class = NULL; +} + +static void ccidg_attr_release(struct config_item *item) +{ + struct f_ccidg_opts *opts = to_f_ccidg_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations ccidg_item_ops = { + .release = ccidg_attr_release, +}; + +#define F_CCIDG_OPT(name, prec, limit) \ +static ssize_t f_ccidg_opts_##name##_show(struct config_item *item, char *page)\ +{ \ + struct f_ccidg_opts *opts = to_f_ccidg_opts(item); \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%x\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_ccidg_opts_##name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct f_ccidg_opts *opts = to_f_ccidg_opts(item); \ + int ret; \ + u##prec num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou##prec(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + opts->name = num; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +CONFIGFS_ATTR(f_ccidg_opts_, name) + +F_CCIDG_OPT(features, 32, 0xffffffff); +F_CCIDG_OPT(protocols, 32, 0x03); +F_CCIDG_OPT(pinsupport, 8, 0x03); +F_CCIDG_OPT(lcdlayout, 16, 0xffff); +F_CCIDG_OPT(nslots, 8, 0xff); + +static struct configfs_attribute *ccidg_attrs[] = { + &f_ccidg_opts_attr_features, + &f_ccidg_opts_attr_protocols, + &f_ccidg_opts_attr_pinsupport, + &f_ccidg_opts_attr_lcdlayout, + &f_ccidg_opts_attr_nslots, + NULL, +}; + +static struct config_item_type ccidg_func_type = { + .ct_item_ops = &ccidg_item_ops, + .ct_attrs = ccidg_attrs, + .ct_owner = THIS_MODULE, +}; + +static void ccidg_req_put(struct f_ccidg *ccidg, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&ccidg->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&ccidg->lock, flags); +} + +static struct usb_request *ccidg_req_get(struct f_ccidg *ccidg, + struct list_head *head) +{ + unsigned long flags; + struct usb_request *req = NULL; + + spin_lock_irqsave(&ccidg->lock, flags); + if (!list_empty(head)) { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&ccidg->lock, flags); + + return req; +} + +static void ccidg_bulk_complete_tx(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ccidg *ccidg = (struct f_ccidg *)ep->driver_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_composite_dev *cdev = ccidg->function.config->cdev; + + switch (req->status) { + default: + VDBG(cdev, "ccid: tx err %d\n", req->status); + /* FALLTHROUGH */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + break; + case 0: + break; + } + + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + wake_up(&bulk_dev->write_wq); +} + +static void ccidg_bulk_complete_rx(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ccidg *ccidg = (struct f_ccidg *)ep->driver_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_composite_dev *cdev = ccidg->function.config->cdev; + + switch (req->status) { + + /* normal completion */ + case 0: + /* We only cares about packets with nonzero length */ + if (req->actual > 0) + atomic_set(&bulk_dev->rx_done, 1); + break; + + /* software-driven interface shutdown */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + VDBG(cdev, "ccid: rx shutdown, code %d\n", req->status); + break; + + /* for hardware automagic (such as pxa) */ + case -ECONNABORTED: /* endpoint reset */ + DBG(cdev, "ccid: rx %s reset\n", ep->name); + break; + + /* data overrun */ + case -EOVERFLOW: + /* FALLTHROUGH */ + default: + DBG(cdev, "ccid: rx status %d\n", req->status); + break; + } + + wake_up(&bulk_dev->read_wq); +} + +static struct usb_request * +ccidg_request_alloc(struct usb_ep *ep, unsigned int len) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (!req) + return ERR_PTR(-ENOMEM); + + req->length = len; + req->buf = kmalloc(len, GFP_ATOMIC); + if (req->buf == NULL) { + usb_ep_free_request(ep, req); + return ERR_PTR(-ENOMEM); + } + + return req; +} + +static void ccidg_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static int ccidg_function_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_ccidg *ccidg = container_of(f, struct f_ccidg, function); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int ret = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + if (!atomic_read(&ccidg->online)) + return -ENOTCONN; + + switch (ctrl->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_CLASS: + { + switch (ctrl->bRequest) { + case CCIDGENERICREQ_GET_CLOCK_FREQUENCIES: + *(u32 *) req->buf = cpu_to_le32(ccid_class_desc.dwDefaultClock); + ret = min_t(u32, w_length, + sizeof(ccid_class_desc.dwDefaultClock)); + break; + + case CCIDGENERICREQ_GET_DATA_RATES: + *(u32 *) req->buf = cpu_to_le32(ccid_class_desc.dwDataRate); + ret = min_t(u32, w_length, 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); +} + +int ccidg_start_ep(struct f_ccidg *ccidg, struct usb_function *f, + struct usb_ep *ep) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + usb_ep_disable(ep); + + ret = config_ep_by_speed(cdev->gadget, f, ep); + if (ret) { + ERROR(cdev, "ccid: can't configure %s: %d\n", ep->name, ret); + return ret; + } + + ret = usb_ep_enable(ep); + if (ret) { + ERROR(cdev, "ccid: can't start %s: %d\n", ep->name, ret); + return ret; + } + + ep->driver_data = ccidg; + + return ret; +} + +static int ccidg_function_set_alt(struct usb_function *f, + unsigned int intf, unsigned int alt) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req; + int ret; + int i; + + /* Allocate requests for our endpoints */ + req = ccidg_request_alloc(ccidg->out, + sizeof(struct ccidg_bulk_out_header)); + if (IS_ERR(req)) { + ERROR(cdev, "ccid: uname to allocate memory for out req\n"); + return PTR_ERR(req); + } + req->complete = ccidg_bulk_complete_rx; + req->context = ccidg; + bulk_dev->rx_req = req; + + /* Allocate bunch of in requests */ + for (i = 0; i < N_TX_REQS; i++) { + req = ccidg_request_alloc(ccidg->in, + sizeof(struct ccidg_bulk_in_header)); + + if (IS_ERR(req)) { + ret = PTR_ERR(req); + ERROR(cdev, + "ccid: uname to allocate memory for in req\n"); + goto free_bulk_out; + } + req->complete = ccidg_bulk_complete_tx; + req->context = ccidg; + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + } + + /* choose the descriptors and enable endpoints */ + ret = ccidg_start_ep(ccidg, f, ccidg->in); + if (ret) + goto free_bulk_in; + + ret = ccidg_start_ep(ccidg, f, ccidg->out); + if (ret) + goto disable_ep_in; + + atomic_set(&ccidg->online, 1); + return ret; + +disable_ep_in: + usb_ep_disable(ccidg->in); +free_bulk_in: + while ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle))) + ccidg_request_free(req, ccidg->in); +free_bulk_out: + ccidg_request_free(bulk_dev->rx_req, ccidg->out); + return ret; +} + +static int ccidg_bulk_open(struct inode *inode, struct file *file) +{ + struct f_ccidg *ccidg; + struct ccidg_bulk_dev *bulk_dev; + + ccidg = container_of(inode->i_cdev, struct f_ccidg, cdev); + bulk_dev = &ccidg->bulk_dev; + + if (!atomic_read(&ccidg->online)) { + DBG(ccidg->function.config->cdev, "ccid: device not online\n"); + return -ENODEV; + } + + if (atomic_read(&bulk_dev->is_open)) { + DBG(ccidg->function.config->cdev, + "ccid: device already opened\n"); + return -EBUSY; + } + + atomic_set(&bulk_dev->is_open, 1); + + file->private_data = ccidg; + + return 0; +} + +static int ccidg_bulk_release(struct inode *inode, struct file *file) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + + atomic_set(&bulk_dev->is_open, 0); + return 0; +} + +static ssize_t ccidg_bulk_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req; + int r = count, xfer; + int ret; + + /* Make sure we have enough space for a whole package */ + if (count < sizeof(struct ccidg_bulk_out_header)) { + DBG(ccidg->function.config->cdev, + "ccid: too small buffer size. %i provided, need at least %i\n", + count, sizeof(struct ccidg_bulk_out_header)); + return -ENOMEM; + } + + if (!atomic_read(&ccidg->online)) + return -ENODEV; + + /* queue a request */ + req = bulk_dev->rx_req; + req->length = count; + atomic_set(&bulk_dev->rx_done, 0); + + ret = usb_ep_queue(ccidg->out, req, GFP_KERNEL); + if (ret < 0) { + ERROR(ccidg->function.config->cdev, + "ccid: usb ep queue failed\n"); + return -EIO; + } + + if (!atomic_read(&bulk_dev->rx_done) && + file->f_flags & (O_NONBLOCK | O_NDELAY)) + return -EAGAIN; + + /* wait for a request to complete */ + ret = wait_event_interruptible(bulk_dev->read_wq, + atomic_read(&bulk_dev->rx_done) || + !atomic_read(&ccidg->online)); + if (ret < 0) { + usb_ep_dequeue(ccidg->out, req); + return -ERESTARTSYS; + } + + /* Still online? */ + if (!atomic_read(&ccidg->online)) + return -ENODEV; + + atomic_set(&bulk_dev->rx_req_busy, 1); + xfer = (req->actual < count) ? req->actual : count; + + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + + atomic_set(&bulk_dev->rx_req_busy, 0); + if (!atomic_read(&ccidg->online)) { + ccidg_request_free(bulk_dev->rx_req, ccidg->out); + return -ENODEV; + } + + return xfer; +} + +static ssize_t ccidg_bulk_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req = 0; + 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. %i provided, but we can only handle %i\n", + count, sizeof(struct ccidg_bulk_out_header)); + return -ENOMEM; + } + + if (list_empty(&bulk_dev->tx_idle) && + file->f_flags & (O_NONBLOCK | O_NDELAY)) + return -EAGAIN; + + /* get an idle tx request to use */ + ret = wait_event_interruptible(bulk_dev->write_wq, + ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle)))); + + if (ret < 0) + return -ERESTARTSYS; + + if (copy_from_user(req->buf, buf, count)) { + if (!atomic_read(&ccidg->online)) { + ccidg_request_free(req, ccidg->in); + return -ENODEV; + } else { + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + return -EFAULT; + } + } + + req->length = count; + ret = usb_ep_queue(ccidg->in, req, GFP_KERNEL); + if (ret < 0) { + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + + if (!atomic_read(&ccidg->online)) { + /* Free up all requests if we are not online */ + while ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle))) + ccidg_request_free(req, ccidg->in); + + return -ENODEV; + } + return -EIO; + } + + return count; +} + +static __poll_t ccidg_bulk_poll(struct file *file, poll_table * wait) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + __poll_t ret = 0; + + poll_wait(file, &bulk_dev->read_wq, wait); + poll_wait(file, &bulk_dev->write_wq, wait); + + if (list_empty(&bulk_dev->tx_idle)) + ret |= EPOLLOUT | EPOLLWRNORM; + + if (atomic_read(&bulk_dev->rx_done)) + ret |= EPOLLIN | EPOLLRDNORM; + + return ret; +} + +static const struct file_operations f_ccidg_fops = { + .owner = THIS_MODULE, + .read = ccidg_bulk_read, + .write = ccidg_bulk_write, + .open = ccidg_bulk_open, + .poll = ccidg_bulk_poll, + .release = ccidg_bulk_release, +}; + +static int ccidg_bulk_device_init(struct f_ccidg *dev) +{ + struct ccidg_bulk_dev *bulk_dev = &dev->bulk_dev; + + init_waitqueue_head(&bulk_dev->read_wq); + init_waitqueue_head(&bulk_dev->write_wq); + INIT_LIST_HEAD(&bulk_dev->tx_idle); + + return 0; +} + +static void ccidg_function_free(struct usb_function *f) +{ + struct f_ccidg *ccidg; + struct f_ccidg_opts *opts; + + ccidg = func_to_ccidg(f); + opts = container_of(f->fi, struct f_ccidg_opts, func_inst); + + kfree(ccidg); + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); +} + +static void ccidg_function_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + + device_destroy(ccidg_class, MKDEV(major, ccidg->minor)); + cdev_del(&ccidg->cdev); + + /* disable/free request and end point */ + usb_free_all_descriptors(f); +} + +static int ccidg_function_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + struct usb_ep *ep; + struct usb_composite_dev *cdev = c->cdev; + struct device *device; + dev_t dev; + int ifc_id; + int ret; + + /* allocate instance-specific interface IDs, and patch descriptors */ + ifc_id = usb_interface_id(c, f); + if (ifc_id < 0) { + ERROR(cdev, "ccid: unable to allocate ifc id, err:%d\n", + ifc_id); + return ifc_id; + } + ccid_interface_desc.bInterfaceNumber = ifc_id; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_in_desc); + if (!ep) { + ERROR(cdev, "ccid: usb epin autoconfig failed\n"); + ret = -ENODEV; + goto ep_auto_in_fail; + } + ccidg->in = ep; + ep->driver_data = ccidg; + + ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_out_desc); + if (!ep) { + ERROR(cdev, "ccid: usb epout autoconfig failed\n"); + ret = -ENODEV; + goto ep_auto_out_fail; + } + ccidg->out = ep; + ep->driver_data = ccidg; + + /* set descriptor dynamic values */ + ccid_class_desc.dwFeatures = cpu_to_le32(ccidg->features); + ccid_class_desc.bPINSupport = ccidg->pinsupport; + ccid_class_desc.wLcdLayout = cpu_to_le16(ccidg->lcdlayout); + ccid_class_desc.bMaxSlotIndex = ccidg->nslots; + ccid_class_desc.dwProtocols = cpu_to_le32(ccidg->protocols); + + if (ccidg->protocols == CCID_PROTOCOL_NOT_SEL) { + ccidg->protocols = CCID_PROTOCOL_T0 | CCID_PROTOCOL_T1; + INFO(ccidg->function.config->cdev, + "ccid: No protocol selected. Support both T0 and T1.\n"); + } + + + ccid_hs_in_desc.bEndpointAddress = + ccid_fs_in_desc.bEndpointAddress; + ccid_hs_out_desc.bEndpointAddress = + ccid_fs_out_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, ccid_fs_descs, + ccid_hs_descs, NULL, NULL); + if (ret) + goto ep_auto_out_fail; + + /* create char device */ + cdev_init(&ccidg->cdev, &f_ccidg_fops); + dev = MKDEV(major, ccidg->minor); + ret = cdev_add(&ccidg->cdev, dev, 1); + if (ret) + goto fail_free_descs; + + device = device_create(ccidg_class, NULL, dev, NULL, + "%s%d", "ccidg", ccidg->minor); + if (IS_ERR(device)) { + ret = PTR_ERR(device); + goto del; + } + + return 0; + +del: + cdev_del(&ccidg->cdev); +fail_free_descs: + usb_free_all_descriptors(f); +ep_auto_out_fail: + ccidg->out->driver_data = NULL; + ccidg->out = NULL; +ep_auto_in_fail: + ccidg->in->driver_data = NULL; + ccidg->in = NULL; + ERROR(f->config->cdev, "ccidg_bind FAILED\n"); + + return ret; +} + +static struct usb_function *ccidg_alloc(struct usb_function_instance *fi) +{ + struct f_ccidg *ccidg; + struct f_ccidg_opts *opts; + int ret; + + ccidg = kzalloc(sizeof(*ccidg), GFP_KERNEL); + if (!ccidg) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&ccidg->lock); + + ret = ccidg_bulk_device_init(ccidg); + if (ret) { + kfree(ccidg); + return ERR_PTR(ret); + } + + opts = container_of(fi, struct f_ccidg_opts, func_inst); + + mutex_lock(&opts->lock); + ++opts->refcnt; + + ccidg->minor = opts->minor; + ccidg->features = opts->features; + ccidg->protocols = opts->protocols; + ccidg->pinsupport = opts->pinsupport; + ccidg->nslots = opts->nslots; + mutex_unlock(&opts->lock); + + ccidg->function.name = "ccid"; + ccidg->function.bind = ccidg_function_bind; + ccidg->function.unbind = ccidg_function_unbind; + ccidg->function.set_alt = ccidg_function_set_alt; + ccidg->function.disable = ccidg_function_disable; + ccidg->function.setup = ccidg_function_setup; + ccidg->function.free_func = ccidg_function_free; + + return &ccidg->function; +} + +static void ccidg_free_inst(struct usb_function_instance *f) +{ + struct f_ccidg_opts *opts; + + opts = container_of(f, struct f_ccidg_opts, func_inst); + mutex_lock(&ccidg_ida_lock); + + ccidg_put_minor(opts->minor); + if (ida_is_empty(&ccidg_ida)) + ccidg_cleanup(); + + mutex_unlock(&ccidg_ida_lock); + + kfree(opts); +} + +static struct usb_function_instance *ccidg_alloc_inst(void) +{ + struct f_ccidg_opts *opts; + struct usb_function_instance *ret; + int status = 0; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = ccidg_free_inst; + ret = &opts->func_inst; + + mutex_lock(&ccidg_ida_lock); + + if (ida_is_empty(&ccidg_ida)) { + status = ccidg_setup(); + if (status) { + ret = ERR_PTR(status); + kfree(opts); + goto unlock; + } + } + + opts->minor = ccidg_get_minor(); + if (opts->minor < 0) { + ret = ERR_PTR(opts->minor); + kfree(opts); + if (ida_is_empty(&ccidg_ida)) + ccidg_cleanup(); + goto unlock; + } + + config_group_init_type_name(&opts->func_inst.group, + "", &ccidg_func_type); + +unlock: + mutex_unlock(&ccidg_ida_lock); + return ret; +} + +DECLARE_USB_FUNCTION_INIT(ccid, ccidg_alloc_inst, ccidg_alloc); + +MODULE_DESCRIPTION("USB CCID Gadget driver"); +MODULE_AUTHOR("Marcus Folkesson "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/function/f_ccid.h b/drivers/usb/gadget/function/f_ccid.h new file mode 100644 index 000000000000..9dc182d1f819 --- /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; + __u32 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; + __u32 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; + __u16 bcdCCID; + __u8 bMaxSlotIndex; + __u8 bVoltageSupport; + __u32 dwProtocols; + __u32 dwDefaultClock; + __u32 dwMaximumClock; + __u8 bNumClockSupported; + __u32 dwDataRate; + __u32 dwMaxDataRate; + __u8 bNumDataRatesSupported; + __u32 dwMaxIFSD; + __u32 dwSynchProtocols; + __u32 dwMechanical; + __u32 dwFeatures; + __u32 dwMaxCCIDMessageLength; + __u8 bClassGetResponse; + __u8 bClassEnvelope; + __u16 wLcdLayout; + __u8 bPINSupport; + __u8 bMaxCCIDBusySlots; +} __packed; + + +#endif diff --git a/include/uapi/linux/usb/ccid.h b/include/uapi/linux/usb/ccid.h new file mode 100644 index 000000000000..517897201563 --- /dev/null +++ b/include/uapi/linux/usb/ccid.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Marcus Folkesson + * + * This file holds USB constants defined by the CCID Specification. + */ + +#ifndef CCID_H +#define CCID_H + +/* Slot error register when bmCommandStatus = 1 */ +#define CCID_CMD_ABORTED 0xFF +#define CCID_ICC_MUTE 0xFE +#define CCID_XFR_PARITY_ERROR 0xFD +#define CCID_XFR_OVERRUN 0xFC +#define CCID_HW_ERROR 0xFB +#define CCID_BAD_ATR_TS 0xF8 +#define CCID_BAD_ATR_TCK 0xF7 +#define CCID_ICC_PROTOCOL_NOT_SUPPORTED 0xF6 +#define CCID_ICC_CLASS_NOT_SUPPORTED 0xF5 +#define CCID_PROCEDURE_BYTE_CONFLICT 0xF4 +#define CCID_DEACTIVATED_PROTOCOL 0xF3 +#define CCID_BUSY_WITH_AUTO_SEQUENCE 0xF2 +#define CCID_PIN_TIMEOUT 0xF0 +#define CCID_PIN_CANCELLED 0xEF +#define CCID_CMD_SLOT_BUSY 0xE0 + +/* PC to RDR messages (bulk out) */ +#define CCID_PC_TO_RDR_ICCPOWERON 0x62 +#define CCID_PC_TO_RDR_ICCPOWEROFF 0x63 +#define CCID_PC_TO_RDR_GETSLOTSTATUS 0x65 +#define CCID_PC_TO_RDR_XFRBLOCK 0x6F +#define CCID_PC_TO_RDR_GETPARAMETERS 0x6C +#define CCID_PC_TO_RDR_RESETPARAMETERS 0x6D +#define CCID_PC_TO_RDR_SETPARAMETERS 0x61 +#define CCID_PC_TO_RDR_ESCAPE 0x6B +#define CCID_PC_TO_RDR_ICCCLOCK 0x6E +#define CCID_PC_TO_RDR_T0APDU 0x6A +#define CCID_PC_TO_RDR_SECURE 0x69 +#define CCID_PC_TO_RDR_MECHANICAL 0x71 +#define CCID_PC_TO_RDR_ABORT 0x72 +#define CCID_PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY 0x73 + +/* RDR to PC messages (bulk in) */ +#define CCID_RDR_TO_PC_DATABLOCK 0x80 +#define CCID_RDR_TO_PC_SLOTSTATUS 0x81 +#define CCID_RDR_TO_PC_PARAMETERS 0x82 +#define CCID_RDR_TO_PC_ESCAPE 0x83 +#define CCID_RDR_TO_PC_DATARATEANDCLOCKFREQUENCY 0x84 + +/* Class Features */ + +/* No special characteristics */ +#define CCID_FEATURES_NADA 0x00000000 +/* Automatic parameter configuration based on ATR data */ +#define CCID_FEATURES_AUTO_PCONF 0x00000002 +/* Automatic activation of ICC on inserting */ +#define CCID_FEATURES_AUTO_ACTIV 0x00000004 +/* Automatic ICC voltage selection */ +#define CCID_FEATURES_AUTO_VOLT 0x00000008 +/* Automatic ICC clock frequency change */ +#define CCID_FEATURES_AUTO_CLOCK 0x00000010 +/* Automatic baud rate change */ +#define CCID_FEATURES_AUTO_BAUD 0x00000020 +/*Automatic parameters negotiation made by the CCID */ +#define CCID_FEATURES_AUTO_PNEGO 0x00000040 +/* Automatic PPS made by the CCID according to the active parameters */ +#define CCID_FEATURES_AUTO_PPS 0x00000080 +/* CCID can set ICC in clock stop mode */ +#define CCID_FEATURES_ICCSTOP 0x00000100 +/* NAD value other than 00 accepted (T=1 protocol in use) */ +#define CCID_FEATURES_NAD 0x00000200 +/* Automatic IFSD exchange as first exchange (T=1 protocol in use) */ +#define CCID_FEATURES_AUTO_IFSD 0x00000400 +/* TPDU level exchanges with CCID */ +#define CCID_FEATURES_EXC_TPDU 0x00010000 +/* Short APDU level exchange with CCID */ +#define CCID_FEATURES_EXC_SAPDU 0x00020000 +/* Short and Extended APDU level exchange with CCID */ +#define CCID_FEATURES_EXC_APDU 0x00040000 +/* USB Wake up signaling supported on card insertion and removal */ +#define CCID_FEATURES_WAKEUP 0x00100000 + +/* Supported protocols */ +#define CCID_PROTOCOL_NOT_SEL 0x00 +#define CCID_PROTOCOL_T0 0x01 +#define CCID_PROTOCOL_T1 0x02 + +#define CCID_PINSUPOORT_NONE 0x00 +#define CCID_PINSUPOORT_VERIFICATION (1 << 1) +#define CCID_PINSUPOORT_MODIFICATION (1 << 2) + +#endif -- 2.16.2 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Subject: [1/3] usb: gadget: ccid: add support for USB CCID Gadget Device From: Marcus Folkesson Message-Id: <20180526203401.16586-1-marcus.folkesson@gmail.com> Date: Sat, 26 May 2018 22:33:59 +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: U2lnbmVkLW9mZi1ieTogTWFyY3VzIEZvbGtlc3NvbiA8bWFyY3VzLmZvbGtlc3NvbkBnbWFpbC5j b20+Ci0tLQogZHJpdmVycy91c2IvZ2FkZ2V0L0tjb25maWcgICAgICAgICAgIHwgIDE3ICsKIGRy aXZlcnMvdXNiL2dhZGdldC9mdW5jdGlvbi9NYWtlZmlsZSB8ICAgMSArCiBkcml2ZXJzL3VzYi9n YWRnZXQvZnVuY3Rpb24vZl9jY2lkLmMgfCA5ODggKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysKIGRyaXZlcnMvdXNiL2dhZGdldC9mdW5jdGlvbi9mX2NjaWQuaCB8ICA5MSArKysr CiBpbmNsdWRlL3VhcGkvbGludXgvdXNiL2NjaWQuaCAgICAgICAgfCAgOTMgKysrKwogNSBmaWxl cyBjaGFuZ2VkLCAxMTkwIGluc2VydGlvbnMoKykKIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJz L3VzYi9nYWRnZXQvZnVuY3Rpb24vZl9jY2lkLmMKIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJz L3VzYi9nYWRnZXQvZnVuY3Rpb24vZl9jY2lkLmgKIGNyZWF0ZSBtb2RlIDEwMDY0NCBpbmNsdWRl L3VhcGkvbGludXgvdXNiL2NjaWQuaAoKZGlmZiAtLWdpdCBhL2RyaXZlcnMvdXNiL2dhZGdldC9L Y29uZmlnIGIvZHJpdmVycy91c2IvZ2FkZ2V0L0tjb25maWcKaW5kZXggMzFjY2U3ODA1ZWIyLi5i ZGViZGYxZmZhMmIgMTAwNjQ0Ci0tLSBhL2RyaXZlcnMvdXNiL2dhZGdldC9LY29uZmlnCisrKyBi L2RyaXZlcnMvdXNiL2dhZGdldC9LY29uZmlnCkBAIC0xNDksNiArMTQ5LDkgQEAgY29uZmlnIFVT Ql9MSUJDT01QT1NJVEUKIGNvbmZpZyBVU0JfRl9BQ00KIAl0cmlzdGF0ZQogCitjb25maWcgVVNC X0ZfQ0NJRAorCXRyaXN0YXRlCisKIGNvbmZpZyBVU0JfRl9TU19MQgogCXRyaXN0YXRlCiAKQEAg LTI0OCw2ICsyNTEsMjAgQEAgY29uZmlnIFVTQl9DT05GSUdGU19BQ00KIAkgIEFDTSBzZXJpYWwg bGluay4gIFRoaXMgZnVuY3Rpb24gY2FuIGJlIHVzZWQgdG8gaW50ZXJvcGVyYXRlIHdpdGgKIAkg IE1TLVdpbmRvd3MgaG9zdHMgb3Igd2l0aCB0aGUgTGludXgtVVNCICJjZGMtYWNtIiBkcml2ZXIu CiAKK2NvbmZpZyBVU0JfQ09ORklHRlNfQ0NJRAorCWJvb2wgIkNoaXAgQ2FyZCBJbnRlcmZhY2Ug RGV2aWNlIChDQ0lEKSIKKwlkZXBlbmRzIG9uIFVTQl9DT05GSUdGUworCXNlbGVjdCBVU0JfRl9D Q0lECisJaGVscAorCSAgVGhlIENDSUQgZnVuY3Rpb24gZHJpdmVyIHByb3ZpZGVzIGdlbmVyaWMg ZW11bGF0aW9uIG9mIGEKKwkgIENoaXAgQ2FyZCBJbnRlcmZhY2UgRGV2aWNlIChDQ0lEKS4KKwor CSAgWW91IHdpbGwgbmVlZCBhIHVzZXIgc3BhY2Ugc2VydmVyIHRhbGtpbmcgdG8gL2Rldi9jY2lk ZyosCisJICBzaW5jZSB0aGUga2VybmVsIGl0c2VsZiBkb2VzIG5vdCBpbXBsZW1lbnQgQ0NJRC9U UERVL0FQRFUKKwkgIHByb3RvY29sLgorCisJICBGb3IgbW9yZSBpbmZvcm1hdGlvbiwgc2VlIERv Y3VtZW50YXRpb24vdXNiL2dhZGdldF9jY2lkLnJzdC4KKwogY29uZmlnIFVTQl9DT05GSUdGU19P QkVYCiAJYm9vbCAiT2JqZWN0IEV4Y2hhbmdlIE1vZGVsIChDREMgT0JFWCkiCiAJZGVwZW5kcyBv biBVU0JfQ09ORklHRlMKZGlmZiAtLWdpdCBhL2RyaXZlcnMvdXNiL2dhZGdldC9mdW5jdGlvbi9N YWtlZmlsZSBiL2RyaXZlcnMvdXNiL2dhZGdldC9mdW5jdGlvbi9NYWtlZmlsZQppbmRleCA1ZDNh NmNmMDIyMTguLjYyOTg1MTAwOWUxYSAxMDA2NDQKLS0tIGEvZHJpdmVycy91c2IvZ2FkZ2V0L2Z1 bmN0aW9uL01ha2VmaWxlCisrKyBiL2RyaXZlcnMvdXNiL2dhZGdldC9mdW5jdGlvbi9NYWtlZmls ZQpAQCAtOSw2ICs5LDcgQEAgY2NmbGFncy15CQkJKz0gLUkkKHNyY3RyZWUpL2RyaXZlcnMvdXNi L2dhZGdldC91ZGMvCiAjIFVTQiBGdW5jdGlvbnMKIHVzYl9mX2FjbS15CQkJOj0gZl9hY20ubwog b2JqLSQoQ09ORklHX1VTQl9GX0FDTSkJCSs9IHVzYl9mX2FjbS5vCitvYmotJChDT05GSUdfVVNC X0ZfQ0NJRCkJKz0gZl9jY2lkLm8KIHVzYl9mX3NzX2xiLXkJCQk6PSBmX2xvb3BiYWNrLm8gZl9z b3VyY2VzaW5rLm8KIG9iai0kKENPTkZJR19VU0JfRl9TU19MQikJKz0gdXNiX2Zfc3NfbGIubwog b2JqLSQoQ09ORklHX1VTQl9VX1NFUklBTCkJKz0gdV9zZXJpYWwubwpkaWZmIC0tZ2l0IGEvZHJp dmVycy91c2IvZ2FkZ2V0L2Z1bmN0aW9uL2ZfY2NpZC5jIGIvZHJpdmVycy91c2IvZ2FkZ2V0L2Z1 bmN0aW9uL2ZfY2NpZC5jCm5ldyBmaWxlIG1vZGUgMTAwNjQ0CmluZGV4IDAwMDAwMDAwMDAwMC4u OWZmODYxNWNhMzAzCi0tLSAvZGV2L251bGwKKysrIGIvZHJpdmVycy91c2IvZ2FkZ2V0L2Z1bmN0 aW9uL2ZfY2NpZC5jCkBAIC0wLDAgKzEsOTg4IEBACisvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmll cjogR1BMLTIuMAorLyoKKyAqIGZfY2NpZC5jIC0tIENoaXAgQ2FyZCBJbnRlcmZhY2UgRGV2aWNl IChDQ0lEKSBmdW5jdGlvbiBEcml2ZXIKKyAqCisgKiBDb3B5cmlnaHQgKEMpIDIwMTggTWFyY3Vz IEZvbGtlc3NvbiA8bWFyY3VzLmZvbGtlc3NvbkBnbWFpbC5jb20+CisgKgorICovCisjaW5jbHVk ZSA8bGludXgvY2Rldi5oPgorI2luY2x1ZGUgPGxpbnV4L2RldmljZS5oPgorI2luY2x1ZGUgPGxp bnV4L2ZzLmg+CisjaW5jbHVkZSA8bGludXgva2VybmVsLmg+CisjaW5jbHVkZSA8bGludXgvbW9k dWxlLmg+CisjaW5jbHVkZSA8bGludXgvcG9sbC5oPgorI2luY2x1ZGUgPGxpbnV4L3NsYWIuaD4K KyNpbmNsdWRlIDxsaW51eC91YWNjZXNzLmg+CisjaW5jbHVkZSA8bGludXgvdXNiL2NvbXBvc2l0 ZS5oPgorI2luY2x1ZGUgPHVhcGkvbGludXgvdXNiL2NjaWQuaD4KKworI2luY2x1ZGUgImZfY2Np ZC5oIgorI2luY2x1ZGUgInVfZi5oIgorCisvKiBOdW1iZXIgb2YgdHggcmVxdWVzdHMgdG8gYWxs b2NhdGUgKi8KKyNkZWZpbmUgTl9UWF9SRVFTIDQKKworLyogTWF4aW11bSBudW1iZXIgb2YgZGV2 aWNlcyAqLworI2RlZmluZSBDQ0lEX01JTk9SUyA0CisKK3N0cnVjdCBjY2lkZ19idWxrX2RldiB7 CisJYXRvbWljX3QgaXNfb3BlbjsKKwlhdG9taWNfdCByeF9yZXFfYnVzeTsKKwl3YWl0X3F1ZXVl X2hlYWRfdCByZWFkX3dxOworCXdhaXRfcXVldWVfaGVhZF90IHdyaXRlX3dxOworCXN0cnVjdCB1 c2JfcmVxdWVzdCAqcnhfcmVxOworCWF0b21pY190IHJ4X2RvbmU7CisJc3RydWN0IGxpc3RfaGVh ZCB0eF9pZGxlOworfTsKKworc3RydWN0IGZfY2NpZGcgeworCXN0cnVjdCB1c2JfZnVuY3Rpb25f aW5zdGFuY2UJZnVuY19pbnN0OworCXN0cnVjdCB1c2JfZnVuY3Rpb24gZnVuY3Rpb247CisJc3Bp bmxvY2tfdCBsb2NrOworCWF0b21pY190IG9ubGluZTsKKworCS8qIENoYXJhY3RlciBkZXZpY2Ug Ki8KKwlzdHJ1Y3QgY2RldiBjZGV2OworCWludCBtaW5vcjsKKworCS8qIER5bmFtaWMgYXR0cmli dXRlcyAqLworCXUzMiBmZWF0dXJlczsKKwl1MzIgcHJvdG9jb2xzOworCXU4IHBpbnN1cHBvcnQ7 CisJdTggbnNsb3RzOworCXU4IGxjZGxheW91dDsKKworCS8qIEVuZHBvaW50cyAqLworCXN0cnVj dCB1c2JfZXAgKmluOworCXN0cnVjdCB1c2JfZXAgKm91dDsKKwlzdHJ1Y3QgY2NpZGdfYnVsa19k ZXYgYnVsa19kZXY7Cit9OworCisvKiBJbnRlcmZhY2UgRGVzY3JpcHRvcjogKi8KK3N0YXRpYyBz dHJ1Y3QgdXNiX2ludGVyZmFjZV9kZXNjcmlwdG9yIGNjaWRfaW50ZXJmYWNlX2Rlc2MgPSB7CisJ LmJMZW5ndGggPQkJVVNCX0RUX0lOVEVSRkFDRV9TSVpFLAorCS5iRGVzY3JpcHRvclR5cGUgPQlV U0JfRFRfSU5URVJGQUNFLAorCS5iTnVtRW5kcG9pbnRzID0JMiwKKwkuYkludGVyZmFjZUNsYXNz ID0JVVNCX0NMQVNTX0NTQ0lELAorCS5iSW50ZXJmYWNlU3ViQ2xhc3MgPQkwLAorCS5iSW50ZXJm YWNlUHJvdG9jb2wgPQkwLAorfTsKKworLyogQ0NJRCBDbGFzcyBEZXNjcmlwdG9yICovCitzdGF0 aWMgc3RydWN0IGNjaWRfY2xhc3NfZGVzY3JpcHRvciBjY2lkX2NsYXNzX2Rlc2MgPSB7CisJLmJM ZW5ndGggPQkJc2l6ZW9mKGNjaWRfY2xhc3NfZGVzYyksCisJLmJEZXNjcmlwdG9yVHlwZSA9CUND SURfREVDUklQVE9SX1RZUEUsCisJLmJjZENDSUQgPQkJQ0NJRDFfMTAsCisJLyogLmJNYXhTbG90 SW5kZXggPQlEWU5BTUlDICovCisJLmJWb2x0YWdlU3VwcG9ydCA9CUNDSURfVk9MVFNfM18wLAor CS8qIC5kd1Byb3RvY29scyA9CURZTkFNSUMgKi8KKwkuZHdEZWZhdWx0Q2xvY2sgPQkzNTgwLAor CS5kd01heGltdW1DbG9jayA9CTM1ODAsCisJLmJOdW1DbG9ja1N1cHBvcnRlZCA9CTAsCisJLmR3 RGF0YVJhdGUgPQkJOTYwMCwKKwkuZHdNYXhEYXRhUmF0ZSA9CTk2MDAsCisJLmJOdW1EYXRhUmF0 ZXNTdXBwb3J0ZWQgPSAwLAorCS5kd01heElGU0QgPQkJMCwKKwkuZHdTeW5jaFByb3RvY29scyA9 CTAsCisJLmR3TWVjaGFuaWNhbCA9CQkwLAorCS8qIC5kd0ZlYXR1cmVzID0JRFlOQU1JQyAqLwor CisJLyogZXh0ZW5kZWQgQVBEVSBsZXZlbCBNZXNzYWdlIExlbmd0aCAqLworCS5kd01heENDSURN ZXNzYWdlTGVuZ3RoID0gMHgyMDAsCisJLmJDbGFzc0dldFJlc3BvbnNlID0JMHgwLAorCS5iQ2xh c3NFbnZlbG9wZSA9CTB4MCwKKwkvKiAud0xjZExheW91dCA9CURZTkFNSUMgKi8KKwkvKiAuYlBJ TlN1cHBvcnQgPQlEWU5BTUlDICovCisJLmJNYXhDQ0lEQnVzeVNsb3RzID0JMQorfTsKKworLyog RnVsbCBzcGVlZCBzdXBwb3J0OiAqLworc3RhdGljIHN0cnVjdCB1c2JfZW5kcG9pbnRfZGVzY3Jp cHRvciBjY2lkX2ZzX2luX2Rlc2MgPSB7CisJLmJMZW5ndGggPQkJVVNCX0RUX0VORFBPSU5UX1NJ WkUsCisJLmJEZXNjcmlwdG9yVHlwZSA9CVVTQl9EVF9FTkRQT0lOVCwKKwkuYkVuZHBvaW50QWRk cmVzcyA9CVVTQl9ESVJfSU4sCisJLmJtQXR0cmlidXRlcyA9CQlVU0JfRU5EUE9JTlRfWEZFUl9C VUxLLAorCS53TWF4UGFja2V0U2l6ZSAgID0JY3B1X3RvX2xlMTYoNjQpLAorfTsKKworc3RhdGlj IHN0cnVjdCB1c2JfZW5kcG9pbnRfZGVzY3JpcHRvciBjY2lkX2ZzX291dF9kZXNjID0geworCS5i TGVuZ3RoID0JCVVTQl9EVF9FTkRQT0lOVF9TSVpFLAorCS5iRGVzY3JpcHRvclR5cGUgPQlVU0Jf RFRfRU5EUE9JTlQsCisJLmJFbmRwb2ludEFkZHJlc3MgPQlVU0JfRElSX09VVCwKKwkuYm1BdHRy aWJ1dGVzID0JCVVTQl9FTkRQT0lOVF9YRkVSX0JVTEssCisJLndNYXhQYWNrZXRTaXplICAgPQkg Y3B1X3RvX2xlMTYoNjQpLAorfTsKKworc3RhdGljIHN0cnVjdCB1c2JfZGVzY3JpcHRvcl9oZWFk ZXIgKmNjaWRfZnNfZGVzY3NbXSA9IHsKKwkoc3RydWN0IHVzYl9kZXNjcmlwdG9yX2hlYWRlciAq KSAmY2NpZF9pbnRlcmZhY2VfZGVzYywKKwkoc3RydWN0IHVzYl9kZXNjcmlwdG9yX2hlYWRlciAq KSAmY2NpZF9jbGFzc19kZXNjLAorCShzdHJ1Y3QgdXNiX2Rlc2NyaXB0b3JfaGVhZGVyICopICZj Y2lkX2ZzX2luX2Rlc2MsCisJKHN0cnVjdCB1c2JfZGVzY3JpcHRvcl9oZWFkZXIgKikgJmNjaWRf ZnNfb3V0X2Rlc2MsCisJTlVMTCwKK307CisKKy8qIEhpZ2ggc3BlZWQgc3VwcG9ydDogKi8KK3N0 YXRpYyBzdHJ1Y3QgdXNiX2VuZHBvaW50X2Rlc2NyaXB0b3IgY2NpZF9oc19pbl9kZXNjID0gewor CS5iTGVuZ3RoID0JCVVTQl9EVF9FTkRQT0lOVF9TSVpFLAorCS5iRGVzY3JpcHRvclR5cGUgPQlV U0JfRFRfRU5EUE9JTlQsCisJLmJFbmRwb2ludEFkZHJlc3MgPQlVU0JfRElSX0lOLAorCS5ibUF0 dHJpYnV0ZXMgPQkJVVNCX0VORFBPSU5UX1hGRVJfQlVMSywKKwkud01heFBhY2tldFNpemUgPQlj cHVfdG9fbGUxNig1MTIpLAorfTsKKworc3RhdGljIHN0cnVjdCB1c2JfZW5kcG9pbnRfZGVzY3Jp cHRvciBjY2lkX2hzX291dF9kZXNjID0geworCS5iTGVuZ3RoID0JCVVTQl9EVF9FTkRQT0lOVF9T SVpFLAorCS5iRGVzY3JpcHRvclR5cGUgPQlVU0JfRFRfRU5EUE9JTlQsCisJLmJFbmRwb2ludEFk ZHJlc3MgPQlVU0JfRElSX09VVCwKKwkuYm1BdHRyaWJ1dGVzID0JCVVTQl9FTkRQT0lOVF9YRkVS X0JVTEssCisJLndNYXhQYWNrZXRTaXplID0JY3B1X3RvX2xlMTYoNTEyKSwKK307CisKK3N0YXRp YyBzdHJ1Y3QgdXNiX2Rlc2NyaXB0b3JfaGVhZGVyICpjY2lkX2hzX2Rlc2NzW10gPSB7CisJKHN0 cnVjdCB1c2JfZGVzY3JpcHRvcl9oZWFkZXIgKikgJmNjaWRfaW50ZXJmYWNlX2Rlc2MsCisJKHN0 cnVjdCB1c2JfZGVzY3JpcHRvcl9oZWFkZXIgKikgJmNjaWRfY2xhc3NfZGVzYywKKwkoc3RydWN0 IHVzYl9kZXNjcmlwdG9yX2hlYWRlciAqKSAmY2NpZF9oc19pbl9kZXNjLAorCShzdHJ1Y3QgdXNi X2Rlc2NyaXB0b3JfaGVhZGVyICopICZjY2lkX2hzX291dF9kZXNjLAorCU5VTEwsCit9OworCitz dGF0aWMgREVGSU5FX0lEQShjY2lkZ19pZGEpOworc3RhdGljIGludCBtYWpvcjsKK3N0YXRpYyBE RUZJTkVfTVVURVgoY2NpZGdfaWRhX2xvY2spOyAvKiBwcm90ZWN0cyBhY2Nlc3MgdG8gY2NpZGdf aWRhICovCitzdGF0aWMgc3RydWN0IGNsYXNzICpjY2lkZ19jbGFzczsKKworc3RhdGljIGlubGlu ZSBzdHJ1Y3QgZl9jY2lkZ19vcHRzICp0b19mX2NjaWRnX29wdHMoc3RydWN0IGNvbmZpZ19pdGVt ICppdGVtKQoreworCXJldHVybiBjb250YWluZXJfb2YodG9fY29uZmlnX2dyb3VwKGl0ZW0pLCBz dHJ1Y3QgZl9jY2lkZ19vcHRzLAorCQkJICAgIGZ1bmNfaW5zdC5ncm91cCk7Cit9CisKK3N0YXRp YyBpbmxpbmUgc3RydWN0IGZfY2NpZGcgKmZ1bmNfdG9fY2NpZGcoc3RydWN0IHVzYl9mdW5jdGlv biAqZikKK3sKKwlyZXR1cm4gY29udGFpbmVyX29mKGYsIHN0cnVjdCBmX2NjaWRnLCBmdW5jdGlv bik7Cit9CisKK3N0YXRpYyBpbmxpbmUgaW50IGNjaWRnX2dldF9taW5vcih2b2lkKQoreworCWlu dCByZXQ7CisKKwlyZXQgPSBpZGFfc2ltcGxlX2dldCgmY2NpZGdfaWRhLCAwLCAwLCBHRlBfS0VS TkVMKTsKKwlpZiAocmV0ID49IENDSURfTUlOT1JTKSB7CisJCWlkYV9zaW1wbGVfcmVtb3ZlKCZj Y2lkZ19pZGEsIHJldCk7CisJCXJldCA9IC1FTk9ERVY7CisJfQorCisJcmV0dXJuIHJldDsKK30K Kworc3RhdGljIGlubGluZSB2b2lkIGNjaWRnX3B1dF9taW5vcihpbnQgbWlub3IpCit7CisJaWRh X3NpbXBsZV9yZW1vdmUoJmNjaWRnX2lkYSwgbWlub3IpOworfQorCitzdGF0aWMgaW50IGNjaWRn X3NldHVwKHZvaWQpCit7CisJaW50IHJldDsKKwlkZXZfdCBkZXY7CisKKwljY2lkZ19jbGFzcyA9 IGNsYXNzX2NyZWF0ZShUSElTX01PRFVMRSwgImNjaWRnIik7CisJaWYgKElTX0VSUihjY2lkZ19j bGFzcykpIHsKKwkJY2NpZGdfY2xhc3MgPSBOVUxMOworCQlyZXR1cm4gUFRSX0VSUihjY2lkZ19j bGFzcyk7CisJfQorCisJcmV0ID0gYWxsb2NfY2hyZGV2X3JlZ2lvbigmZGV2LCAwLCBDQ0lEX01J Tk9SUywgImNjaWRnIik7CisJaWYgKHJldCkgeworCQljbGFzc19kZXN0cm95KGNjaWRnX2NsYXNz KTsKKwkJY2NpZGdfY2xhc3MgPSBOVUxMOworCQlyZXR1cm4gcmV0OworCX0KKworCW1ham9yID0g TUFKT1IoZGV2KTsKKworCXJldHVybiAwOworfQorCitzdGF0aWMgdm9pZCBjY2lkZ19jbGVhbnVw KHZvaWQpCit7CisJaWYgKG1ham9yKSB7CisJCXVucmVnaXN0ZXJfY2hyZGV2X3JlZ2lvbihNS0RF VihtYWpvciwgMCksIENDSURfTUlOT1JTKTsKKwkJbWFqb3IgPSAwOworCX0KKworCWNsYXNzX2Rl c3Ryb3koY2NpZGdfY2xhc3MpOworCWNjaWRnX2NsYXNzID0gTlVMTDsKK30KKworc3RhdGljIHZv aWQgY2NpZGdfYXR0cl9yZWxlYXNlKHN0cnVjdCBjb25maWdfaXRlbSAqaXRlbSkKK3sKKwlzdHJ1 Y3QgZl9jY2lkZ19vcHRzICpvcHRzID0gdG9fZl9jY2lkZ19vcHRzKGl0ZW0pOworCisJdXNiX3B1 dF9mdW5jdGlvbl9pbnN0YW5jZSgmb3B0cy0+ZnVuY19pbnN0KTsKK30KKworc3RhdGljIHN0cnVj dCBjb25maWdmc19pdGVtX29wZXJhdGlvbnMgY2NpZGdfaXRlbV9vcHMgPSB7CisJLnJlbGVhc2UJ PSBjY2lkZ19hdHRyX3JlbGVhc2UsCit9OworCisjZGVmaW5lIEZfQ0NJREdfT1BUKG5hbWUsIHBy ZWMsIGxpbWl0KQkJCQkJXAorc3RhdGljIHNzaXplX3QgZl9jY2lkZ19vcHRzXyMjbmFtZSMjX3No b3coc3RydWN0IGNvbmZpZ19pdGVtICppdGVtLCBjaGFyICpwYWdlKVwKK3sJCQkJCQkJCQlcCisJ c3RydWN0IGZfY2NpZGdfb3B0cyAqb3B0cyA9IHRvX2ZfY2NpZGdfb3B0cyhpdGVtKTsJCVwKKwlp bnQgcmVzdWx0OwkJCQkJCQlcCisJCQkJCQkJCQlcCisJbXV0ZXhfbG9jaygmb3B0cy0+bG9jayk7 CQkJCQlcCisJcmVzdWx0ID0gc3ByaW50ZihwYWdlLCAiJXhcbiIsIG9wdHMtPm5hbWUpOwkJCVwK KwltdXRleF91bmxvY2soJm9wdHMtPmxvY2spOwkJCQkJXAorCQkJCQkJCQkJXAorCXJldHVybiBy ZXN1bHQ7CQkJCQkJCVwKK30JCQkJCQkJCQlcCisJCQkJCQkJCQlcCitzdGF0aWMgc3NpemVfdCBm X2NjaWRnX29wdHNfIyNuYW1lIyNfc3RvcmUoc3RydWN0IGNvbmZpZ19pdGVtICppdGVtLAlcCisJ CQkJCSBjb25zdCBjaGFyICpwYWdlLCBzaXplX3QgbGVuKQlcCit7CQkJCQkJCQkJXAorCXN0cnVj dCBmX2NjaWRnX29wdHMgKm9wdHMgPSB0b19mX2NjaWRnX29wdHMoaXRlbSk7CQlcCisJaW50IHJl dDsJCQkJCQkJXAorCXUjI3ByZWMgbnVtOwkJCQkJCQlcCisJCQkJCQkJCQlcCisJbXV0ZXhfbG9j aygmb3B0cy0+bG9jayk7CQkJCQlcCisJaWYgKG9wdHMtPnJlZmNudCkgewkJCQkJCVwKKwkJcmV0 ID0gLUVCVVNZOwkJCQkJCVwKKwkJZ290byBlbmQ7CQkJCQkJXAorCX0JCQkJCQkJCVwKKwkJCQkJ CQkJCVwKKwlyZXQgPSBrc3RydG91IyNwcmVjKHBhZ2UsIDAsICZudW0pOwkJCQlcCisJaWYgKHJl dCkJCQkJCQkJXAorCQlnb3RvIGVuZDsJCQkJCQlcCisJCQkJCQkJCQlcCisJaWYgKG51bSA+IGxp bWl0KSB7CQkJCQkJXAorCQlyZXQgPSAtRUlOVkFMOwkJCQkJCVwKKwkJZ290byBlbmQ7CQkJCQkJ XAorCX0JCQkJCQkJCVwKKwlvcHRzLT5uYW1lID0gbnVtOwkJCQkJCVwKKwlyZXQgPSBsZW47CQkJ CQkJCVwKKwkJCQkJCQkJCVwKK2VuZDoJCQkJCQkJCQlcCisJbXV0ZXhfdW5sb2NrKCZvcHRzLT5s b2NrKTsJCQkJCVwKKwlyZXR1cm4gcmV0OwkJCQkJCQlcCit9CQkJCQkJCQkJXAorCQkJCQkJCQkJ XAorQ09ORklHRlNfQVRUUihmX2NjaWRnX29wdHNfLCBuYW1lKQorCitGX0NDSURHX09QVChmZWF0 dXJlcywgMzIsIDB4ZmZmZmZmZmYpOworRl9DQ0lER19PUFQocHJvdG9jb2xzLCAzMiwgMHgwMyk7 CitGX0NDSURHX09QVChwaW5zdXBwb3J0LCA4LCAweDAzKTsKK0ZfQ0NJREdfT1BUKGxjZGxheW91 dCwgMTYsIDB4ZmZmZik7CitGX0NDSURHX09QVChuc2xvdHMsIDgsIDB4ZmYpOworCitzdGF0aWMg c3RydWN0IGNvbmZpZ2ZzX2F0dHJpYnV0ZSAqY2NpZGdfYXR0cnNbXSA9IHsKKwkmZl9jY2lkZ19v cHRzX2F0dHJfZmVhdHVyZXMsCisJJmZfY2NpZGdfb3B0c19hdHRyX3Byb3RvY29scywKKwkmZl9j Y2lkZ19vcHRzX2F0dHJfcGluc3VwcG9ydCwKKwkmZl9jY2lkZ19vcHRzX2F0dHJfbGNkbGF5b3V0 LAorCSZmX2NjaWRnX29wdHNfYXR0cl9uc2xvdHMsCisJTlVMTCwKK307CisKK3N0YXRpYyBzdHJ1 Y3QgY29uZmlnX2l0ZW1fdHlwZSBjY2lkZ19mdW5jX3R5cGUgPSB7CisJLmN0X2l0ZW1fb3BzCT0g JmNjaWRnX2l0ZW1fb3BzLAorCS5jdF9hdHRycwk9IGNjaWRnX2F0dHJzLAorCS5jdF9vd25lcgk9 IFRISVNfTU9EVUxFLAorfTsKKworc3RhdGljIHZvaWQgY2NpZGdfcmVxX3B1dChzdHJ1Y3QgZl9j Y2lkZyAqY2NpZGcsIHN0cnVjdCBsaXN0X2hlYWQgKmhlYWQsCisJCXN0cnVjdCB1c2JfcmVxdWVz dCAqcmVxKQoreworCXVuc2lnbmVkIGxvbmcgZmxhZ3M7CisKKwlzcGluX2xvY2tfaXJxc2F2ZSgm Y2NpZGctPmxvY2ssIGZsYWdzKTsKKwlsaXN0X2FkZF90YWlsKCZyZXEtPmxpc3QsIGhlYWQpOwor CXNwaW5fdW5sb2NrX2lycXJlc3RvcmUoJmNjaWRnLT5sb2NrLCBmbGFncyk7Cit9CisKK3N0YXRp YyBzdHJ1Y3QgdXNiX3JlcXVlc3QgKmNjaWRnX3JlcV9nZXQoc3RydWN0IGZfY2NpZGcgKmNjaWRn LAorCQkJCQlzdHJ1Y3QgbGlzdF9oZWFkICpoZWFkKQoreworCXVuc2lnbmVkIGxvbmcgZmxhZ3M7 CisJc3RydWN0IHVzYl9yZXF1ZXN0ICpyZXEgPSBOVUxMOworCisJc3Bpbl9sb2NrX2lycXNhdmUo JmNjaWRnLT5sb2NrLCBmbGFncyk7CisJaWYgKCFsaXN0X2VtcHR5KGhlYWQpKSB7CisJCXJlcSA9 IGxpc3RfZmlyc3RfZW50cnkoaGVhZCwgc3RydWN0IHVzYl9yZXF1ZXN0LCBsaXN0KTsKKwkJbGlz dF9kZWwoJnJlcS0+bGlzdCk7CisJfQorCXNwaW5fdW5sb2NrX2lycXJlc3RvcmUoJmNjaWRnLT5s b2NrLCBmbGFncyk7CisKKwlyZXR1cm4gcmVxOworfQorCitzdGF0aWMgdm9pZCBjY2lkZ19idWxr X2NvbXBsZXRlX3R4KHN0cnVjdCB1c2JfZXAgKmVwLCBzdHJ1Y3QgdXNiX3JlcXVlc3QgKnJlcSkK K3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2NpZGcgPSAoc3RydWN0IGZfY2NpZGcgKillcC0+ZHJpdmVy X2RhdGE7CisJc3RydWN0IGNjaWRnX2J1bGtfZGV2ICpidWxrX2RldiA9ICZjY2lkZy0+YnVsa19k ZXY7CisJc3RydWN0IHVzYl9jb21wb3NpdGVfZGV2ICpjZGV2CT0gY2NpZGctPmZ1bmN0aW9uLmNv bmZpZy0+Y2RldjsKKworCXN3aXRjaCAocmVxLT5zdGF0dXMpIHsKKwlkZWZhdWx0OgorCQlWREJH KGNkZXYsICJjY2lkOiB0eCBlcnIgJWRcbiIsIHJlcS0+c3RhdHVzKTsKKwkJLyogRkFMTFRIUk9V R0ggKi8KKwljYXNlIC1FQ09OTlJFU0VUOgkJLyogdW5saW5rICovCisJY2FzZSAtRVNIVVRET1dO OgkJLyogZGlzY29ubmVjdCBldGMgKi8KKwkJYnJlYWs7CisJY2FzZSAwOgorCQlicmVhazsKKwl9 CisKKwljY2lkZ19yZXFfcHV0KGNjaWRnLCAmYnVsa19kZXYtPnR4X2lkbGUsIHJlcSk7CisJd2Fr ZV91cCgmYnVsa19kZXYtPndyaXRlX3dxKTsKK30KKworc3RhdGljIHZvaWQgY2NpZGdfYnVsa19j b21wbGV0ZV9yeChzdHJ1Y3QgdXNiX2VwICplcCwgc3RydWN0IHVzYl9yZXF1ZXN0ICpyZXEpCit7 CisJc3RydWN0IGZfY2NpZGcgKmNjaWRnID0gKHN0cnVjdCBmX2NjaWRnICopZXAtPmRyaXZlcl9k YXRhOworCXN0cnVjdCBjY2lkZ19idWxrX2RldiAqYnVsa19kZXYgPSAmY2NpZGctPmJ1bGtfZGV2 OworCXN0cnVjdCB1c2JfY29tcG9zaXRlX2RldiAqY2Rldgk9IGNjaWRnLT5mdW5jdGlvbi5jb25m aWctPmNkZXY7CisKKwlzd2l0Y2ggKHJlcS0+c3RhdHVzKSB7CisKKwkvKiBub3JtYWwgY29tcGxl dGlvbiAqLworCWNhc2UgMDoKKwkJLyogV2Ugb25seSBjYXJlcyBhYm91dCBwYWNrZXRzIHdpdGgg bm9uemVybyBsZW5ndGggKi8KKwkJaWYgKHJlcS0+YWN0dWFsID4gMCkKKwkJCWF0b21pY19zZXQo JmJ1bGtfZGV2LT5yeF9kb25lLCAxKTsKKwkJYnJlYWs7CisKKwkvKiBzb2Z0d2FyZS1kcml2ZW4g aW50ZXJmYWNlIHNodXRkb3duICovCisJY2FzZSAtRUNPTk5SRVNFVDoJCS8qIHVubGluayAqLwor CWNhc2UgLUVTSFVURE9XTjoJCS8qIGRpc2Nvbm5lY3QgZXRjICovCisJCVZEQkcoY2RldiwgImNj aWQ6IHJ4IHNodXRkb3duLCBjb2RlICVkXG4iLCByZXEtPnN0YXR1cyk7CisJCWJyZWFrOworCisJ LyogZm9yIGhhcmR3YXJlIGF1dG9tYWdpYyAoc3VjaCBhcyBweGEpICovCisJY2FzZSAtRUNPTk5B Qk9SVEVEOgkJLyogZW5kcG9pbnQgcmVzZXQgKi8KKwkJREJHKGNkZXYsICJjY2lkOiByeCAlcyBy ZXNldFxuIiwgZXAtPm5hbWUpOworCQlicmVhazsKKworCS8qIGRhdGEgb3ZlcnJ1biAqLworCWNh c2UgLUVPVkVSRkxPVzoKKwkJLyogRkFMTFRIUk9VR0ggKi8KKwlkZWZhdWx0OgorCQlEQkcoY2Rl diwgImNjaWQ6IHJ4IHN0YXR1cyAlZFxuIiwgcmVxLT5zdGF0dXMpOworCQlicmVhazsKKwl9CisK Kwl3YWtlX3VwKCZidWxrX2Rldi0+cmVhZF93cSk7Cit9CisKK3N0YXRpYyBzdHJ1Y3QgdXNiX3Jl cXVlc3QgKgorY2NpZGdfcmVxdWVzdF9hbGxvYyhzdHJ1Y3QgdXNiX2VwICplcCwgdW5zaWduZWQg aW50IGxlbikKK3sKKwlzdHJ1Y3QgdXNiX3JlcXVlc3QgKnJlcTsKKworCXJlcSA9IHVzYl9lcF9h bGxvY19yZXF1ZXN0KGVwLCBHRlBfQVRPTUlDKTsKKwlpZiAoIXJlcSkKKwkJcmV0dXJuIEVSUl9Q VFIoLUVOT01FTSk7CisKKwlyZXEtPmxlbmd0aCA9IGxlbjsKKwlyZXEtPmJ1ZiA9IGttYWxsb2Mo bGVuLCBHRlBfQVRPTUlDKTsKKwlpZiAocmVxLT5idWYgPT0gTlVMTCkgeworCQl1c2JfZXBfZnJl ZV9yZXF1ZXN0KGVwLCByZXEpOworCQlyZXR1cm4gRVJSX1BUUigtRU5PTUVNKTsKKwl9CisKKwly ZXR1cm4gcmVxOworfQorCitzdGF0aWMgdm9pZCBjY2lkZ19yZXF1ZXN0X2ZyZWUoc3RydWN0IHVz Yl9yZXF1ZXN0ICpyZXEsIHN0cnVjdCB1c2JfZXAgKmVwKQoreworCWlmIChyZXEpIHsKKwkJa2Zy ZWUocmVxLT5idWYpOworCQl1c2JfZXBfZnJlZV9yZXF1ZXN0KGVwLCByZXEpOworCX0KK30KKwor c3RhdGljIGludCBjY2lkZ19mdW5jdGlvbl9zZXR1cChzdHJ1Y3QgdXNiX2Z1bmN0aW9uICpmLAor CQljb25zdCBzdHJ1Y3QgdXNiX2N0cmxyZXF1ZXN0ICpjdHJsKQoreworCXN0cnVjdCBmX2NjaWRn ICpjY2lkZyA9IGNvbnRhaW5lcl9vZihmLCBzdHJ1Y3QgZl9jY2lkZywgZnVuY3Rpb24pOworCXN0 cnVjdCB1c2JfY29tcG9zaXRlX2RldiAqY2Rldgk9IGYtPmNvbmZpZy0+Y2RldjsKKwlzdHJ1Y3Qg dXNiX3JlcXVlc3QgKnJlcQkJPSBjZGV2LT5yZXE7CisJaW50IHJldAkJCQk9IC1FT1BOT1RTVVBQ OworCXUxNiB3X2luZGV4CQkJPSBsZTE2X3RvX2NwdShjdHJsLT53SW5kZXgpOworCXUxNiB3X3Zh bHVlCQkJPSBsZTE2X3RvX2NwdShjdHJsLT53VmFsdWUpOworCXUxNiB3X2xlbmd0aAkJCT0gbGUx Nl90b19jcHUoY3RybC0+d0xlbmd0aCk7CisKKwlpZiAoIWF0b21pY19yZWFkKCZjY2lkZy0+b25s aW5lKSkKKwkJcmV0dXJuIC1FTk9UQ09OTjsKKworCXN3aXRjaCAoY3RybC0+YlJlcXVlc3RUeXBl ICYgVVNCX1RZUEVfTUFTSykgeworCWNhc2UgVVNCX1RZUEVfQ0xBU1M6CisJCXsKKwkJc3dpdGNo IChjdHJsLT5iUmVxdWVzdCkgeworCQljYXNlIENDSURHRU5FUklDUkVRX0dFVF9DTE9DS19GUkVR VUVOQ0lFUzoKKwkJCSoodTMyICopIHJlcS0+YnVmID0gY3B1X3RvX2xlMzIoY2NpZF9jbGFzc19k ZXNjLmR3RGVmYXVsdENsb2NrKTsKKwkJCXJldCA9IG1pbl90KHUzMiwgd19sZW5ndGgsCisJCQkJ CXNpemVvZihjY2lkX2NsYXNzX2Rlc2MuZHdEZWZhdWx0Q2xvY2spKTsKKwkJCWJyZWFrOworCisJ CWNhc2UgQ0NJREdFTkVSSUNSRVFfR0VUX0RBVEFfUkFURVM6CisJCQkqKHUzMiAqKSByZXEtPmJ1 ZiA9IGNwdV90b19sZTMyKGNjaWRfY2xhc3NfZGVzYy5kd0RhdGFSYXRlKTsKKwkJCXJldCA9IG1p bl90KHUzMiwgd19sZW5ndGgsIHNpemVvZihjY2lkX2NsYXNzX2Rlc2MuZHdEYXRhUmF0ZSkpOwor CQkJYnJlYWs7CisKKwkJZGVmYXVsdDoKKwkJCVZEQkcoZi0+Y29uZmlnLT5jZGV2LAorCQkJCSJj Y2lkOiBpbnZhbGlkIGNvbnRyb2wgcmVxJTAyeC4lMDJ4IHYlMDR4IGklMDR4IGwlZFxuIiwKKwkJ CQljdHJsLT5iUmVxdWVzdFR5cGUsIGN0cmwtPmJSZXF1ZXN0LAorCQkJCXdfdmFsdWUsIHdfaW5k ZXgsIHdfbGVuZ3RoKTsKKwkJfQorCQl9CisJfQorCisJLyogcmVzcG9uZGVkIHdpdGggZGF0YSB0 cmFuc2ZlciBvciBzdGF0dXMgcGhhc2U/ICovCisJaWYgKHJldCA+PSAwKSB7CisJCVZEQkcoZi0+ Y29uZmlnLT5jZGV2LCAiY2NpZDogcmVxJTAyeC4lMDJ4IHYlMDR4IGklMDR4IGwlZFxuIiwKKwkJ CWN0cmwtPmJSZXF1ZXN0VHlwZSwgY3RybC0+YlJlcXVlc3QsCisJCQl3X3ZhbHVlLCB3X2luZGV4 LCB3X2xlbmd0aCk7CisKKwkJcmVxLT5sZW5ndGggPSByZXQ7CisJCXJldCA9IHVzYl9lcF9xdWV1 ZShjZGV2LT5nYWRnZXQtPmVwMCwgcmVxLCBHRlBfQVRPTUlDKTsKKwkJaWYgKHJldCA8IDApCisJ CQlFUlJPUihmLT5jb25maWctPmNkZXYsCisJCQkJImNjaWQ6IGVwMCBlbnF1ZXVlIGVyciAlZFxu IiwgcmV0KTsKKwl9CisKKwlyZXR1cm4gcmV0OworfQorCitzdGF0aWMgdm9pZCBjY2lkZ19mdW5j dGlvbl9kaXNhYmxlKHN0cnVjdCB1c2JfZnVuY3Rpb24gKmYpCit7CisJc3RydWN0IGZfY2NpZGcg KmNjaWRnID0gZnVuY190b19jY2lkZyhmKTsKKwlzdHJ1Y3QgY2NpZGdfYnVsa19kZXYgKmJ1bGtf ZGV2ID0gJmNjaWRnLT5idWxrX2RldjsKKwlzdHJ1Y3QgdXNiX3JlcXVlc3QgKnJlcTsKKworCS8q IERpc2FibGUgZW5kcG9pbnRzICovCisJdXNiX2VwX2Rpc2FibGUoY2NpZGctPmluKTsKKwl1c2Jf ZXBfZGlzYWJsZShjY2lkZy0+b3V0KTsKKworCS8qIEZyZWUgZW5kcG9pbnQgcmVsYXRlZCByZXF1 ZXN0cyAqLworCWlmICghYXRvbWljX3JlYWQoJmJ1bGtfZGV2LT5yeF9yZXFfYnVzeSkpCisJCWNj aWRnX3JlcXVlc3RfZnJlZShidWxrX2Rldi0+cnhfcmVxLCBjY2lkZy0+b3V0KTsKKwl3aGlsZSAo KHJlcSA9IGNjaWRnX3JlcV9nZXQoY2NpZGcsICZidWxrX2Rldi0+dHhfaWRsZSkpKQorCQljY2lk Z19yZXF1ZXN0X2ZyZWUocmVxLCBjY2lkZy0+aW4pOworCisJYXRvbWljX3NldCgmY2NpZGctPm9u bGluZSwgMCk7CisKKwkvKiBXYWtlIHVwIHRocmVhZHMgKi8KKwl3YWtlX3VwKCZidWxrX2Rldi0+ d3JpdGVfd3EpOworCXdha2VfdXAoJmJ1bGtfZGV2LT5yZWFkX3dxKTsKK30KKworaW50IGNjaWRn X3N0YXJ0X2VwKHN0cnVjdCBmX2NjaWRnICpjY2lkZywgc3RydWN0IHVzYl9mdW5jdGlvbiAqZiwK KwkJCXN0cnVjdCB1c2JfZXAgKmVwKQoreworCXN0cnVjdCB1c2JfY29tcG9zaXRlX2RldiAqY2Rl diA9IGYtPmNvbmZpZy0+Y2RldjsKKwlpbnQgcmV0OworCisJdXNiX2VwX2Rpc2FibGUoZXApOwor CisJcmV0ID0gY29uZmlnX2VwX2J5X3NwZWVkKGNkZXYtPmdhZGdldCwgZiwgZXApOworCWlmIChy ZXQpIHsKKwkJRVJST1IoY2RldiwgImNjaWQ6IGNhbid0IGNvbmZpZ3VyZSAlczogJWRcbiIsIGVw LT5uYW1lLCByZXQpOworCQlyZXR1cm4gcmV0OworCX0KKworCXJldCA9IHVzYl9lcF9lbmFibGUo ZXApOworCWlmIChyZXQpIHsKKwkJRVJST1IoY2RldiwgImNjaWQ6IGNhbid0IHN0YXJ0ICVzOiAl ZFxuIiwgZXAtPm5hbWUsIHJldCk7CisJCXJldHVybiByZXQ7CisJfQorCisJZXAtPmRyaXZlcl9k YXRhID0gY2NpZGc7CisKKwlyZXR1cm4gcmV0OworfQorCitzdGF0aWMgaW50IGNjaWRnX2Z1bmN0 aW9uX3NldF9hbHQoc3RydWN0IHVzYl9mdW5jdGlvbiAqZiwKKwkJdW5zaWduZWQgaW50IGludGYs IHVuc2lnbmVkIGludCBhbHQpCit7CisJc3RydWN0IGZfY2NpZGcgKmNjaWRnCQk9IGZ1bmNfdG9f Y2NpZGcoZik7CisJc3RydWN0IHVzYl9jb21wb3NpdGVfZGV2ICpjZGV2CT0gZi0+Y29uZmlnLT5j ZGV2OworCXN0cnVjdCBjY2lkZ19idWxrX2RldiAqYnVsa19kZXYJPSAmY2NpZGctPmJ1bGtfZGV2 OworCXN0cnVjdCB1c2JfcmVxdWVzdCAqcmVxOworCWludCByZXQ7CisJaW50IGk7CisKKwkvKiBB bGxvY2F0ZSByZXF1ZXN0cyBmb3Igb3VyIGVuZHBvaW50cyAqLworCXJlcSA9IGNjaWRnX3JlcXVl c3RfYWxsb2MoY2NpZGctPm91dCwKKwkJCXNpemVvZihzdHJ1Y3QgY2NpZGdfYnVsa19vdXRfaGVh ZGVyKSk7CisJaWYgKElTX0VSUihyZXEpKSB7CisJCUVSUk9SKGNkZXYsICJjY2lkOiB1bmFtZSB0 byBhbGxvY2F0ZSBtZW1vcnkgZm9yIG91dCByZXFcbiIpOworCQlyZXR1cm4gUFRSX0VSUihyZXEp OworCX0KKwlyZXEtPmNvbXBsZXRlID0gY2NpZGdfYnVsa19jb21wbGV0ZV9yeDsKKwlyZXEtPmNv bnRleHQgPSBjY2lkZzsKKwlidWxrX2Rldi0+cnhfcmVxID0gcmVxOworCisJLyogQWxsb2NhdGUg YnVuY2ggb2YgaW4gcmVxdWVzdHMgKi8KKwlmb3IgKGkgPSAwOyBpIDwgTl9UWF9SRVFTOyBpKysp IHsKKwkJcmVxID0gY2NpZGdfcmVxdWVzdF9hbGxvYyhjY2lkZy0+aW4sCisJCQkJc2l6ZW9mKHN0 cnVjdCBjY2lkZ19idWxrX2luX2hlYWRlcikpOworCisJCWlmIChJU19FUlIocmVxKSkgeworCQkJ cmV0ID0gUFRSX0VSUihyZXEpOworCQkJRVJST1IoY2RldiwKKwkJCQkiY2NpZDogdW5hbWUgdG8g YWxsb2NhdGUgbWVtb3J5IGZvciBpbiByZXFcbiIpOworCQkJZ290byBmcmVlX2J1bGtfb3V0Owor CQl9CisJCXJlcS0+Y29tcGxldGUgPSBjY2lkZ19idWxrX2NvbXBsZXRlX3R4OworCQlyZXEtPmNv bnRleHQgPSBjY2lkZzsKKwkJY2NpZGdfcmVxX3B1dChjY2lkZywgJmJ1bGtfZGV2LT50eF9pZGxl LCByZXEpOworCX0KKworCS8qIGNob29zZSB0aGUgZGVzY3JpcHRvcnMgYW5kIGVuYWJsZSBlbmRw b2ludHMgKi8KKwlyZXQgPSBjY2lkZ19zdGFydF9lcChjY2lkZywgZiwgY2NpZGctPmluKTsKKwlp ZiAocmV0KQorCQlnb3RvIGZyZWVfYnVsa19pbjsKKworCXJldCA9IGNjaWRnX3N0YXJ0X2VwKGNj aWRnLCBmLCBjY2lkZy0+b3V0KTsKKwlpZiAocmV0KQorCQlnb3RvIGRpc2FibGVfZXBfaW47CisK KwlhdG9taWNfc2V0KCZjY2lkZy0+b25saW5lLCAxKTsKKwlyZXR1cm4gcmV0OworCitkaXNhYmxl X2VwX2luOgorCXVzYl9lcF9kaXNhYmxlKGNjaWRnLT5pbik7CitmcmVlX2J1bGtfaW46CisJd2hp bGUgKChyZXEgPSBjY2lkZ19yZXFfZ2V0KGNjaWRnLCAmYnVsa19kZXYtPnR4X2lkbGUpKSkKKwkJ Y2NpZGdfcmVxdWVzdF9mcmVlKHJlcSwgY2NpZGctPmluKTsKK2ZyZWVfYnVsa19vdXQ6CisJY2Np ZGdfcmVxdWVzdF9mcmVlKGJ1bGtfZGV2LT5yeF9yZXEsIGNjaWRnLT5vdXQpOworCXJldHVybiBy ZXQ7Cit9CisKK3N0YXRpYyBpbnQgY2NpZGdfYnVsa19vcGVuKHN0cnVjdCBpbm9kZSAqaW5vZGUs IHN0cnVjdCBmaWxlICpmaWxlKQoreworCXN0cnVjdCBmX2NjaWRnICpjY2lkZzsKKwlzdHJ1Y3Qg Y2NpZGdfYnVsa19kZXYgKmJ1bGtfZGV2OworCisJY2NpZGcgPSBjb250YWluZXJfb2YoaW5vZGUt PmlfY2Rldiwgc3RydWN0IGZfY2NpZGcsIGNkZXYpOworCWJ1bGtfZGV2ID0gJmNjaWRnLT5idWxr X2RldjsKKworCWlmICghYXRvbWljX3JlYWQoJmNjaWRnLT5vbmxpbmUpKSB7CisJCURCRyhjY2lk Zy0+ZnVuY3Rpb24uY29uZmlnLT5jZGV2LCAiY2NpZDogZGV2aWNlIG5vdCBvbmxpbmVcbiIpOwor CQlyZXR1cm4gLUVOT0RFVjsKKwl9CisKKwlpZiAoYXRvbWljX3JlYWQoJmJ1bGtfZGV2LT5pc19v cGVuKSkgeworCQlEQkcoY2NpZGctPmZ1bmN0aW9uLmNvbmZpZy0+Y2RldiwKKwkJCQkiY2NpZDog ZGV2aWNlIGFscmVhZHkgb3BlbmVkXG4iKTsKKwkJcmV0dXJuIC1FQlVTWTsKKwl9CisKKwlhdG9t aWNfc2V0KCZidWxrX2Rldi0+aXNfb3BlbiwgMSk7CisKKwlmaWxlLT5wcml2YXRlX2RhdGEgPSBj Y2lkZzsKKworCXJldHVybiAwOworfQorCitzdGF0aWMgaW50IGNjaWRnX2J1bGtfcmVsZWFzZShz dHJ1Y3QgaW5vZGUgKmlub2RlLCBzdHJ1Y3QgZmlsZSAqZmlsZSkKK3sKKwlzdHJ1Y3QgZl9jY2lk ZyAqY2NpZGcgPSAgZmlsZS0+cHJpdmF0ZV9kYXRhOworCXN0cnVjdCBjY2lkZ19idWxrX2RldiAq YnVsa19kZXYgPSAmY2NpZGctPmJ1bGtfZGV2OworCisJYXRvbWljX3NldCgmYnVsa19kZXYtPmlz X29wZW4sIDApOworCXJldHVybiAwOworfQorCitzdGF0aWMgc3NpemVfdCBjY2lkZ19idWxrX3Jl YWQoc3RydWN0IGZpbGUgKmZpbGUsIGNoYXIgX191c2VyICpidWYsCisJCQkJc2l6ZV90IGNvdW50 LCBsb2ZmX3QgKnBvcykKK3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2NpZGcgPSAgZmlsZS0+cHJpdmF0 ZV9kYXRhOworCXN0cnVjdCBjY2lkZ19idWxrX2RldiAqYnVsa19kZXYgPSAmY2NpZGctPmJ1bGtf ZGV2OworCXN0cnVjdCB1c2JfcmVxdWVzdCAqcmVxOworCWludCByID0gY291bnQsIHhmZXI7CisJ aW50IHJldDsKKworCS8qIE1ha2Ugc3VyZSB3ZSBoYXZlIGVub3VnaCBzcGFjZSBmb3IgYSB3aG9s ZSBwYWNrYWdlICovCisJaWYgKGNvdW50IDwgc2l6ZW9mKHN0cnVjdCBjY2lkZ19idWxrX291dF9o ZWFkZXIpKSB7CisJCURCRyhjY2lkZy0+ZnVuY3Rpb24uY29uZmlnLT5jZGV2LAorCQkJCSJjY2lk OiB0b28gc21hbGwgYnVmZmVyIHNpemUuICVpIHByb3ZpZGVkLCBuZWVkIGF0IGxlYXN0ICVpXG4i LAorCQkJCWNvdW50LCBzaXplb2Yoc3RydWN0IGNjaWRnX2J1bGtfb3V0X2hlYWRlcikpOworCQly ZXR1cm4gLUVOT01FTTsKKwl9CisKKwlpZiAoIWF0b21pY19yZWFkKCZjY2lkZy0+b25saW5lKSkK KwkJcmV0dXJuIC1FTk9ERVY7CisKKwkvKiBxdWV1ZSBhIHJlcXVlc3QgKi8KKwlyZXEgPSBidWxr X2Rldi0+cnhfcmVxOworCXJlcS0+bGVuZ3RoID0gY291bnQ7CisJYXRvbWljX3NldCgmYnVsa19k ZXYtPnJ4X2RvbmUsIDApOworCisJcmV0ID0gdXNiX2VwX3F1ZXVlKGNjaWRnLT5vdXQsIHJlcSwg R0ZQX0tFUk5FTCk7CisJaWYgKHJldCA8IDApIHsKKwkJRVJST1IoY2NpZGctPmZ1bmN0aW9uLmNv bmZpZy0+Y2RldiwKKwkJCQkiY2NpZDogdXNiIGVwIHF1ZXVlIGZhaWxlZFxuIik7CisJCXJldHVy biAtRUlPOworCX0KKworCWlmICghYXRvbWljX3JlYWQoJmJ1bGtfZGV2LT5yeF9kb25lKSAmJgor CQkJZmlsZS0+Zl9mbGFncyAmIChPX05PTkJMT0NLIHwgT19OREVMQVkpKQorCQlyZXR1cm4gLUVB R0FJTjsKKworCS8qIHdhaXQgZm9yIGEgcmVxdWVzdCB0byBjb21wbGV0ZSAqLworCXJldCA9IHdh aXRfZXZlbnRfaW50ZXJydXB0aWJsZShidWxrX2Rldi0+cmVhZF93cSwKKwkJCWF0b21pY19yZWFk KCZidWxrX2Rldi0+cnhfZG9uZSkgfHwKKwkJCSFhdG9taWNfcmVhZCgmY2NpZGctPm9ubGluZSkp OworCWlmIChyZXQgPCAwKSB7CisJCXVzYl9lcF9kZXF1ZXVlKGNjaWRnLT5vdXQsIHJlcSk7CisJ CXJldHVybiAtRVJFU1RBUlRTWVM7CisJfQorCisJLyogU3RpbGwgb25saW5lPyAqLworCWlmICgh YXRvbWljX3JlYWQoJmNjaWRnLT5vbmxpbmUpKQorCQlyZXR1cm4gLUVOT0RFVjsKKworCWF0b21p Y19zZXQoJmJ1bGtfZGV2LT5yeF9yZXFfYnVzeSwgMSk7CisJeGZlciA9IChyZXEtPmFjdHVhbCA8 IGNvdW50KSA/IHJlcS0+YWN0dWFsIDogY291bnQ7CisKKwlpZiAoY29weV90b191c2VyKGJ1Ziwg cmVxLT5idWYsIHhmZXIpKQorCQlyID0gLUVGQVVMVDsKKworCWF0b21pY19zZXQoJmJ1bGtfZGV2 LT5yeF9yZXFfYnVzeSwgMCk7CisJaWYgKCFhdG9taWNfcmVhZCgmY2NpZGctPm9ubGluZSkpIHsK KwkJY2NpZGdfcmVxdWVzdF9mcmVlKGJ1bGtfZGV2LT5yeF9yZXEsIGNjaWRnLT5vdXQpOworCQly ZXR1cm4gLUVOT0RFVjsKKwl9CisKKwlyZXR1cm4geGZlcjsKK30KKworc3RhdGljIHNzaXplX3Qg Y2NpZGdfYnVsa193cml0ZShzdHJ1Y3QgZmlsZSAqZmlsZSwgY29uc3QgY2hhciBfX3VzZXIgKmJ1 ZiwKKwkJCQkgc2l6ZV90IGNvdW50LCBsb2ZmX3QgKnBvcykKK3sKKwlzdHJ1Y3QgZl9jY2lkZyAq Y2NpZGcgPSAgZmlsZS0+cHJpdmF0ZV9kYXRhOworCXN0cnVjdCBjY2lkZ19idWxrX2RldiAqYnVs a19kZXYgPSAmY2NpZGctPmJ1bGtfZGV2OworCXN0cnVjdCB1c2JfcmVxdWVzdCAqcmVxID0gMDsK KwlpbnQgcmV0OworCisJLyogQXJlIHdlIG9ubGluZT8gKi8KKwlpZiAoIWF0b21pY19yZWFkKCZj Y2lkZy0+b25saW5lKSkKKwkJcmV0dXJuIC1FTk9ERVY7CisKKwkvKiBBdm9pZCBaZXJvIExlbmd0 aCBQYWNrZXRzIChaTFApICovCisJaWYgKCFjb3VudCkKKwkJcmV0dXJuIDA7CisKKwkvKiBNYWtl IHN1cmUgd2UgaGF2ZSBlbm91Z2ggc3BhY2UgZm9yIGEgd2hvbGUgcGFja2FnZSAqLworCWlmIChj b3VudCA+IHNpemVvZihzdHJ1Y3QgY2NpZGdfYnVsa19vdXRfaGVhZGVyKSkgeworCQlEQkcoY2Np ZGctPmZ1bmN0aW9uLmNvbmZpZy0+Y2RldiwKKwkJCQkiY2NpZDogdG9vIG11Y2ggZGF0YS4gJWkg cHJvdmlkZWQsIGJ1dCB3ZSBjYW4gb25seSBoYW5kbGUgJWlcbiIsCisJCQkJY291bnQsIHNpemVv ZihzdHJ1Y3QgY2NpZGdfYnVsa19vdXRfaGVhZGVyKSk7CisJCXJldHVybiAtRU5PTUVNOworCX0K KworCWlmIChsaXN0X2VtcHR5KCZidWxrX2Rldi0+dHhfaWRsZSkgJiYKKwkJCWZpbGUtPmZfZmxh Z3MgJiAoT19OT05CTE9DSyB8IE9fTkRFTEFZKSkKKwkJcmV0dXJuIC1FQUdBSU47CisKKwkvKiBn ZXQgYW4gaWRsZSB0eCByZXF1ZXN0IHRvIHVzZSAqLworCXJldCA9IHdhaXRfZXZlbnRfaW50ZXJy dXB0aWJsZShidWxrX2Rldi0+d3JpdGVfd3EsCisJCSgocmVxID0gY2NpZGdfcmVxX2dldChjY2lk ZywgJmJ1bGtfZGV2LT50eF9pZGxlKSkpKTsKKworCWlmIChyZXQgPCAwKQorCQlyZXR1cm4gLUVS RVNUQVJUU1lTOworCisJaWYgKGNvcHlfZnJvbV91c2VyKHJlcS0+YnVmLCBidWYsIGNvdW50KSkg eworCQlpZiAoIWF0b21pY19yZWFkKCZjY2lkZy0+b25saW5lKSkgeworCQkJY2NpZGdfcmVxdWVz dF9mcmVlKHJlcSwgY2NpZGctPmluKTsKKwkJCXJldHVybiAtRU5PREVWOworCQl9IGVsc2Ugewor CQkJY2NpZGdfcmVxX3B1dChjY2lkZywgJmJ1bGtfZGV2LT50eF9pZGxlLCByZXEpOworCQkJcmV0 dXJuIC1FRkFVTFQ7CisJCX0KKwl9CisKKwlyZXEtPmxlbmd0aCA9IGNvdW50OworCXJldCA9IHVz Yl9lcF9xdWV1ZShjY2lkZy0+aW4sIHJlcSwgR0ZQX0tFUk5FTCk7CisJaWYgKHJldCA8IDApIHsK KwkJY2NpZGdfcmVxX3B1dChjY2lkZywgJmJ1bGtfZGV2LT50eF9pZGxlLCByZXEpOworCisJCWlm ICghYXRvbWljX3JlYWQoJmNjaWRnLT5vbmxpbmUpKSB7CisJCQkvKiBGcmVlIHVwIGFsbCByZXF1 ZXN0cyBpZiB3ZSBhcmUgbm90IG9ubGluZSAqLworCQkJd2hpbGUgKChyZXEgPSBjY2lkZ19yZXFf Z2V0KGNjaWRnLCAmYnVsa19kZXYtPnR4X2lkbGUpKSkKKwkJCQljY2lkZ19yZXF1ZXN0X2ZyZWUo cmVxLCBjY2lkZy0+aW4pOworCisJCQlyZXR1cm4gLUVOT0RFVjsKKwkJfQorCQlyZXR1cm4gLUVJ TzsKKwl9CisKKwlyZXR1cm4gY291bnQ7Cit9CisKK3N0YXRpYyBfX3BvbGxfdCBjY2lkZ19idWxr X3BvbGwoc3RydWN0IGZpbGUgKmZpbGUsIHBvbGxfdGFibGUgKiB3YWl0KQoreworCXN0cnVjdCBm X2NjaWRnICpjY2lkZyA9ICBmaWxlLT5wcml2YXRlX2RhdGE7CisJc3RydWN0IGNjaWRnX2J1bGtf ZGV2ICpidWxrX2RldiA9ICZjY2lkZy0+YnVsa19kZXY7CisJX19wb2xsX3QJcmV0ID0gMDsKKwor CXBvbGxfd2FpdChmaWxlLCAmYnVsa19kZXYtPnJlYWRfd3EsIHdhaXQpOworCXBvbGxfd2FpdChm aWxlLCAmYnVsa19kZXYtPndyaXRlX3dxLCB3YWl0KTsKKworCWlmIChsaXN0X2VtcHR5KCZidWxr X2Rldi0+dHhfaWRsZSkpCisJCXJldCB8PSBFUE9MTE9VVCB8IEVQT0xMV1JOT1JNOworCisJaWYg KGF0b21pY19yZWFkKCZidWxrX2Rldi0+cnhfZG9uZSkpCisJCXJldCB8PSBFUE9MTElOIHwgRVBP TExSRE5PUk07CisKKwlyZXR1cm4gcmV0OworfQorCitzdGF0aWMgY29uc3Qgc3RydWN0IGZpbGVf b3BlcmF0aW9ucyBmX2NjaWRnX2ZvcHMgPSB7CisJLm93bmVyID0gVEhJU19NT0RVTEUsCisJLnJl YWQgPSBjY2lkZ19idWxrX3JlYWQsCisJLndyaXRlID0gY2NpZGdfYnVsa193cml0ZSwKKwkub3Bl biA9IGNjaWRnX2J1bGtfb3BlbiwKKwkucG9sbCA9IGNjaWRnX2J1bGtfcG9sbCwKKwkucmVsZWFz ZSA9IGNjaWRnX2J1bGtfcmVsZWFzZSwKK307CisKK3N0YXRpYyBpbnQgY2NpZGdfYnVsa19kZXZp Y2VfaW5pdChzdHJ1Y3QgZl9jY2lkZyAqZGV2KQoreworCXN0cnVjdCBjY2lkZ19idWxrX2RldiAq YnVsa19kZXYgPSAmZGV2LT5idWxrX2RldjsKKworCWluaXRfd2FpdHF1ZXVlX2hlYWQoJmJ1bGtf ZGV2LT5yZWFkX3dxKTsKKwlpbml0X3dhaXRxdWV1ZV9oZWFkKCZidWxrX2Rldi0+d3JpdGVfd3Ep OworCUlOSVRfTElTVF9IRUFEKCZidWxrX2Rldi0+dHhfaWRsZSk7CisKKwlyZXR1cm4gMDsKK30K Kworc3RhdGljIHZvaWQgY2NpZGdfZnVuY3Rpb25fZnJlZShzdHJ1Y3QgdXNiX2Z1bmN0aW9uICpm KQoreworCXN0cnVjdCBmX2NjaWRnICpjY2lkZzsKKwlzdHJ1Y3QgZl9jY2lkZ19vcHRzICpvcHRz OworCisJY2NpZGcgPSBmdW5jX3RvX2NjaWRnKGYpOworCW9wdHMgPSBjb250YWluZXJfb2YoZi0+ ZmksIHN0cnVjdCBmX2NjaWRnX29wdHMsIGZ1bmNfaW5zdCk7CisKKwlrZnJlZShjY2lkZyk7CisJ bXV0ZXhfbG9jaygmb3B0cy0+bG9jayk7CisJLS1vcHRzLT5yZWZjbnQ7CisJbXV0ZXhfdW5sb2Nr KCZvcHRzLT5sb2NrKTsKK30KKworc3RhdGljIHZvaWQgY2NpZGdfZnVuY3Rpb25fdW5iaW5kKHN0 cnVjdCB1c2JfY29uZmlndXJhdGlvbiAqYywKKwkJCQkJc3RydWN0IHVzYl9mdW5jdGlvbiAqZikK K3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2NpZGcgPSBmdW5jX3RvX2NjaWRnKGYpOworCisJZGV2aWNl X2Rlc3Ryb3koY2NpZGdfY2xhc3MsIE1LREVWKG1ham9yLCBjY2lkZy0+bWlub3IpKTsKKwljZGV2 X2RlbCgmY2NpZGctPmNkZXYpOworCisJLyogZGlzYWJsZS9mcmVlIHJlcXVlc3QgYW5kIGVuZCBw b2ludCAqLworCXVzYl9mcmVlX2FsbF9kZXNjcmlwdG9ycyhmKTsKK30KKworc3RhdGljIGludCBj Y2lkZ19mdW5jdGlvbl9iaW5kKHN0cnVjdCB1c2JfY29uZmlndXJhdGlvbiAqYywKKwkJCQkJc3Ry dWN0IHVzYl9mdW5jdGlvbiAqZikKK3sKKwlzdHJ1Y3QgZl9jY2lkZyAqY2NpZGcgPSBmdW5jX3Rv X2NjaWRnKGYpOworCXN0cnVjdCB1c2JfZXAgKmVwOworCXN0cnVjdCB1c2JfY29tcG9zaXRlX2Rl diAqY2RldiA9IGMtPmNkZXY7CisJc3RydWN0IGRldmljZSAqZGV2aWNlOworCWRldl90IGRldjsK KwlpbnQgaWZjX2lkOworCWludCByZXQ7CisKKwkvKiBhbGxvY2F0ZSBpbnN0YW5jZS1zcGVjaWZp YyBpbnRlcmZhY2UgSURzLCBhbmQgcGF0Y2ggZGVzY3JpcHRvcnMgKi8KKwlpZmNfaWQgPSB1c2Jf aW50ZXJmYWNlX2lkKGMsIGYpOworCWlmIChpZmNfaWQgPCAwKSB7CisJCUVSUk9SKGNkZXYsICJj Y2lkOiB1bmFibGUgdG8gYWxsb2NhdGUgaWZjIGlkLCBlcnI6JWRcbiIsCisJCQkJaWZjX2lkKTsK KwkJcmV0dXJuIGlmY19pZDsKKwl9CisJY2NpZF9pbnRlcmZhY2VfZGVzYy5iSW50ZXJmYWNlTnVt YmVyID0gaWZjX2lkOworCisJLyogYWxsb2NhdGUgaW5zdGFuY2Utc3BlY2lmaWMgZW5kcG9pbnRz ICovCisJZXAgPSB1c2JfZXBfYXV0b2NvbmZpZyhjZGV2LT5nYWRnZXQsICZjY2lkX2ZzX2luX2Rl c2MpOworCWlmICghZXApIHsKKwkJRVJST1IoY2RldiwgImNjaWQ6IHVzYiBlcGluIGF1dG9jb25m aWcgZmFpbGVkXG4iKTsKKwkJcmV0ID0gLUVOT0RFVjsKKwkJZ290byBlcF9hdXRvX2luX2ZhaWw7 CisJfQorCWNjaWRnLT5pbiA9IGVwOworCWVwLT5kcml2ZXJfZGF0YSA9IGNjaWRnOworCisJZXAg PSB1c2JfZXBfYXV0b2NvbmZpZyhjZGV2LT5nYWRnZXQsICZjY2lkX2ZzX291dF9kZXNjKTsKKwlp ZiAoIWVwKSB7CisJCUVSUk9SKGNkZXYsICJjY2lkOiB1c2IgZXBvdXQgYXV0b2NvbmZpZyBmYWls ZWRcbiIpOworCQlyZXQgPSAtRU5PREVWOworCQlnb3RvIGVwX2F1dG9fb3V0X2ZhaWw7CisJfQor CWNjaWRnLT5vdXQgPSBlcDsKKwllcC0+ZHJpdmVyX2RhdGEgPSBjY2lkZzsKKworCS8qIHNldCBk ZXNjcmlwdG9yIGR5bmFtaWMgdmFsdWVzICovCisJY2NpZF9jbGFzc19kZXNjLmR3RmVhdHVyZXMJ PSBjcHVfdG9fbGUzMihjY2lkZy0+ZmVhdHVyZXMpOworCWNjaWRfY2xhc3NfZGVzYy5iUElOU3Vw cG9ydAk9IGNjaWRnLT5waW5zdXBwb3J0OworCWNjaWRfY2xhc3NfZGVzYy53TGNkTGF5b3V0CT0g Y3B1X3RvX2xlMTYoY2NpZGctPmxjZGxheW91dCk7CisJY2NpZF9jbGFzc19kZXNjLmJNYXhTbG90 SW5kZXgJPSBjY2lkZy0+bnNsb3RzOworCWNjaWRfY2xhc3NfZGVzYy5kd1Byb3RvY29scwk9IGNw dV90b19sZTMyKGNjaWRnLT5wcm90b2NvbHMpOworCisJaWYgKGNjaWRnLT5wcm90b2NvbHMgPT0g Q0NJRF9QUk9UT0NPTF9OT1RfU0VMKSB7CisJCWNjaWRnLT5wcm90b2NvbHMgPSBDQ0lEX1BST1RP Q09MX1QwIHwgQ0NJRF9QUk9UT0NPTF9UMTsKKwkJSU5GTyhjY2lkZy0+ZnVuY3Rpb24uY29uZmln LT5jZGV2LAorCQkJImNjaWQ6IE5vIHByb3RvY29sIHNlbGVjdGVkLiBTdXBwb3J0IGJvdGggVDAg YW5kIFQxLlxuIik7CisJfQorCisKKwljY2lkX2hzX2luX2Rlc2MuYkVuZHBvaW50QWRkcmVzcyA9 CisJCQljY2lkX2ZzX2luX2Rlc2MuYkVuZHBvaW50QWRkcmVzczsKKwljY2lkX2hzX291dF9kZXNj LmJFbmRwb2ludEFkZHJlc3MgPQorCQkJY2NpZF9mc19vdXRfZGVzYy5iRW5kcG9pbnRBZGRyZXNz OworCisJcmV0ICA9IHVzYl9hc3NpZ25fZGVzY3JpcHRvcnMoZiwgY2NpZF9mc19kZXNjcywKKwkJ CWNjaWRfaHNfZGVzY3MsIE5VTEwsIE5VTEwpOworCWlmIChyZXQpCisJCWdvdG8gZXBfYXV0b19v dXRfZmFpbDsKKworCS8qIGNyZWF0ZSBjaGFyIGRldmljZSAqLworCWNkZXZfaW5pdCgmY2NpZGct PmNkZXYsICZmX2NjaWRnX2ZvcHMpOworCWRldiA9IE1LREVWKG1ham9yLCBjY2lkZy0+bWlub3Ip OworCXJldCA9IGNkZXZfYWRkKCZjY2lkZy0+Y2RldiwgZGV2LCAxKTsKKwlpZiAocmV0KQorCQln b3RvIGZhaWxfZnJlZV9kZXNjczsKKworCWRldmljZSA9IGRldmljZV9jcmVhdGUoY2NpZGdfY2xh c3MsIE5VTEwsIGRldiwgTlVMTCwKKwkJCSAgICAgICAiJXMlZCIsICJjY2lkZyIsIGNjaWRnLT5t aW5vcik7CisJaWYgKElTX0VSUihkZXZpY2UpKSB7CisJCXJldCA9IFBUUl9FUlIoZGV2aWNlKTsK KwkJZ290byBkZWw7CisJfQorCisJcmV0dXJuIDA7CisKK2RlbDoKKwljZGV2X2RlbCgmY2NpZGct PmNkZXYpOworZmFpbF9mcmVlX2Rlc2NzOgorCXVzYl9mcmVlX2FsbF9kZXNjcmlwdG9ycyhmKTsK K2VwX2F1dG9fb3V0X2ZhaWw6CisJY2NpZGctPm91dC0+ZHJpdmVyX2RhdGEgPSBOVUxMOworCWNj aWRnLT5vdXQgPSBOVUxMOworZXBfYXV0b19pbl9mYWlsOgorCWNjaWRnLT5pbi0+ZHJpdmVyX2Rh dGEgPSBOVUxMOworCWNjaWRnLT5pbiA9IE5VTEw7CisJRVJST1IoZi0+Y29uZmlnLT5jZGV2LCAi Y2NpZGdfYmluZCBGQUlMRURcbiIpOworCisJcmV0dXJuIHJldDsKK30KKworc3RhdGljIHN0cnVj dCB1c2JfZnVuY3Rpb24gKmNjaWRnX2FsbG9jKHN0cnVjdCB1c2JfZnVuY3Rpb25faW5zdGFuY2Ug KmZpKQoreworCXN0cnVjdCBmX2NjaWRnICpjY2lkZzsKKwlzdHJ1Y3QgZl9jY2lkZ19vcHRzICpv cHRzOworCWludCByZXQ7CisKKwljY2lkZyA9IGt6YWxsb2Moc2l6ZW9mKCpjY2lkZyksIEdGUF9L RVJORUwpOworCWlmICghY2NpZGcpCisJCXJldHVybiBFUlJfUFRSKC1FTk9NRU0pOworCisJc3Bp bl9sb2NrX2luaXQoJmNjaWRnLT5sb2NrKTsKKworCXJldCA9IGNjaWRnX2J1bGtfZGV2aWNlX2lu aXQoY2NpZGcpOworCWlmIChyZXQpIHsKKwkJa2ZyZWUoY2NpZGcpOworCQlyZXR1cm4gRVJSX1BU UihyZXQpOworCX0KKworCW9wdHMgPSBjb250YWluZXJfb2YoZmksIHN0cnVjdCBmX2NjaWRnX29w dHMsIGZ1bmNfaW5zdCk7CisKKwltdXRleF9sb2NrKCZvcHRzLT5sb2NrKTsKKwkrK29wdHMtPnJl ZmNudDsKKworCWNjaWRnLT5taW5vciA9IG9wdHMtPm1pbm9yOworCWNjaWRnLT5mZWF0dXJlcyA9 IG9wdHMtPmZlYXR1cmVzOworCWNjaWRnLT5wcm90b2NvbHMgPSBvcHRzLT5wcm90b2NvbHM7CisJ Y2NpZGctPnBpbnN1cHBvcnQgPSBvcHRzLT5waW5zdXBwb3J0OworCWNjaWRnLT5uc2xvdHMgPSBv cHRzLT5uc2xvdHM7CisJbXV0ZXhfdW5sb2NrKCZvcHRzLT5sb2NrKTsKKworCWNjaWRnLT5mdW5j dGlvbi5uYW1lCT0gImNjaWQiOworCWNjaWRnLT5mdW5jdGlvbi5iaW5kCT0gY2NpZGdfZnVuY3Rp b25fYmluZDsKKwljY2lkZy0+ZnVuY3Rpb24udW5iaW5kCT0gY2NpZGdfZnVuY3Rpb25fdW5iaW5k OworCWNjaWRnLT5mdW5jdGlvbi5zZXRfYWx0CT0gY2NpZGdfZnVuY3Rpb25fc2V0X2FsdDsKKwlj Y2lkZy0+ZnVuY3Rpb24uZGlzYWJsZQk9IGNjaWRnX2Z1bmN0aW9uX2Rpc2FibGU7CisJY2NpZGct PmZ1bmN0aW9uLnNldHVwCT0gY2NpZGdfZnVuY3Rpb25fc2V0dXA7CisJY2NpZGctPmZ1bmN0aW9u LmZyZWVfZnVuYyA9IGNjaWRnX2Z1bmN0aW9uX2ZyZWU7CisKKwlyZXR1cm4gJmNjaWRnLT5mdW5j dGlvbjsKK30KKworc3RhdGljIHZvaWQgY2NpZGdfZnJlZV9pbnN0KHN0cnVjdCB1c2JfZnVuY3Rp b25faW5zdGFuY2UgKmYpCit7CisJc3RydWN0IGZfY2NpZGdfb3B0cyAqb3B0czsKKworCW9wdHMg PSBjb250YWluZXJfb2YoZiwgc3RydWN0IGZfY2NpZGdfb3B0cywgZnVuY19pbnN0KTsKKwltdXRl eF9sb2NrKCZjY2lkZ19pZGFfbG9jayk7CisKKwljY2lkZ19wdXRfbWlub3Iob3B0cy0+bWlub3Ip OworCWlmIChpZGFfaXNfZW1wdHkoJmNjaWRnX2lkYSkpCisJCWNjaWRnX2NsZWFudXAoKTsKKwor CW11dGV4X3VubG9jaygmY2NpZGdfaWRhX2xvY2spOworCisJa2ZyZWUob3B0cyk7Cit9CisKK3N0 YXRpYyBzdHJ1Y3QgdXNiX2Z1bmN0aW9uX2luc3RhbmNlICpjY2lkZ19hbGxvY19pbnN0KHZvaWQp Cit7CisJc3RydWN0IGZfY2NpZGdfb3B0cyAqb3B0czsKKwlzdHJ1Y3QgdXNiX2Z1bmN0aW9uX2lu c3RhbmNlICpyZXQ7CisJaW50IHN0YXR1cyA9IDA7CisKKwlvcHRzID0ga3phbGxvYyhzaXplb2Yo Km9wdHMpLCBHRlBfS0VSTkVMKTsKKwlpZiAoIW9wdHMpCisJCXJldHVybiBFUlJfUFRSKC1FTk9N RU0pOworCisJbXV0ZXhfaW5pdCgmb3B0cy0+bG9jayk7CisJb3B0cy0+ZnVuY19pbnN0LmZyZWVf ZnVuY19pbnN0ID0gY2NpZGdfZnJlZV9pbnN0OworCXJldCA9ICZvcHRzLT5mdW5jX2luc3Q7CisK KwltdXRleF9sb2NrKCZjY2lkZ19pZGFfbG9jayk7CisKKwlpZiAoaWRhX2lzX2VtcHR5KCZjY2lk Z19pZGEpKSB7CisJCXN0YXR1cyA9IGNjaWRnX3NldHVwKCk7CisJCWlmIChzdGF0dXMpICB7CisJ CQlyZXQgPSBFUlJfUFRSKHN0YXR1cyk7CisJCQlrZnJlZShvcHRzKTsKKwkJCWdvdG8gdW5sb2Nr OworCQl9CisJfQorCisJb3B0cy0+bWlub3IgPSBjY2lkZ19nZXRfbWlub3IoKTsKKwlpZiAob3B0 cy0+bWlub3IgPCAwKSB7CisJCXJldCA9IEVSUl9QVFIob3B0cy0+bWlub3IpOworCQlrZnJlZShv cHRzKTsKKwkJaWYgKGlkYV9pc19lbXB0eSgmY2NpZGdfaWRhKSkKKwkJCWNjaWRnX2NsZWFudXAo KTsKKwkJZ290byB1bmxvY2s7CisJfQorCisJY29uZmlnX2dyb3VwX2luaXRfdHlwZV9uYW1lKCZv cHRzLT5mdW5jX2luc3QuZ3JvdXAsCisJCQkiIiwgJmNjaWRnX2Z1bmNfdHlwZSk7CisKK3VubG9j azoKKwltdXRleF91bmxvY2soJmNjaWRnX2lkYV9sb2NrKTsKKwlyZXR1cm4gcmV0OworfQorCitE RUNMQVJFX1VTQl9GVU5DVElPTl9JTklUKGNjaWQsIGNjaWRnX2FsbG9jX2luc3QsIGNjaWRnX2Fs bG9jKTsKKworTU9EVUxFX0RFU0NSSVBUSU9OKCJVU0IgQ0NJRCBHYWRnZXQgZHJpdmVyIik7CitN T0RVTEVfQVVUSE9SKCJNYXJjdXMgRm9sa2Vzc29uIDxtYXJjdXMuZm9sa2Vzc29uQGdtYWlsLmNv bT4iKTsKK01PRFVMRV9MSUNFTlNFKCJHUEwgdjIiKTsKZGlmZiAtLWdpdCBhL2RyaXZlcnMvdXNi L2dhZGdldC9mdW5jdGlvbi9mX2NjaWQuaCBiL2RyaXZlcnMvdXNiL2dhZGdldC9mdW5jdGlvbi9m X2NjaWQuaApuZXcgZmlsZSBtb2RlIDEwMDY0NAppbmRleCAwMDAwMDAwMDAwMDAuLjlkYzE4MmQx ZjgxOQotLS0gL2Rldi9udWxsCisrKyBiL2RyaXZlcnMvdXNiL2dhZGdldC9mdW5jdGlvbi9mX2Nj aWQuaApAQCAtMCwwICsxLDkxIEBACisvKiBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogR1BMLTIu MCAqLworLyoKKyAqIENvcHlyaWdodCAoQykgMjAxOCBNYXJjdXMgRm9sa2Vzc29uIDxtYXJjdXMu Zm9sa2Vzc29uQGdtYWlsLmNvbT4KKyAqLworCisjaWZuZGVmIEZfQ0NJRF9ICisjZGVmaW5lIEZf Q0NJRF9ICisKKyNkZWZpbmUgQ0NJRDFfMTAgICAgICAgICAgICAgICAgMHgwMTEwCisjZGVmaW5l IENDSURfREVDUklQVE9SX1RZUEUgICAgIDB4MjEKKyNkZWZpbmUgQUJEQVRBX1NJWkUJCTUxMgor I2RlZmluZSBTTUFSVF9DQVJEX0RFVklDRV9DTEFTUwkweDBCCisKKy8qIENDSUQgQ2xhc3MgU3Bl Y2lmaWMgUmVxdWVzdCAqLworI2RlZmluZSBDQ0lER0VORVJJQ1JFUV9BQk9SVCAgICAgICAgICAg ICAgICAgICAgMHgwMQorI2RlZmluZSBDQ0lER0VORVJJQ1JFUV9HRVRfQ0xPQ0tfRlJFUVVFTkNJ RVMgICAgMHgwMgorI2RlZmluZSBDQ0lER0VORVJJQ1JFUV9HRVRfREFUQV9SQVRFUyAgICAgICAg ICAgMHgwMworCisvKiBTdXBwb3J0ZWQgdm9sdGFnZXMgKi8KKyNkZWZpbmUgQ0NJRF9WT0xUU19B VVRPICAgICAgICAgICAgICAgICAgICAgICAgICAgICAweDAwCisjZGVmaW5lIENDSURfVk9MVFNf NV8wICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMHgwMQorI2RlZmluZSBDQ0lEX1ZPTFRT XzNfMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDB4MDIKKyNkZWZpbmUgQ0NJRF9WT0xU U18xXzggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAweDAzCisKK3N0cnVjdCBmX2NjaWRn X29wdHMgeworCXN0cnVjdCB1c2JfZnVuY3Rpb25faW5zdGFuY2UgZnVuY19pbnN0OworCWludAlt aW5vcjsKKwlfX3UzMglmZWF0dXJlczsKKwlfX3UzMglwcm90b2NvbHM7CisJX191OAlwaW5zdXBw b3J0OworCV9fdTgJbnNsb3RzOworCV9fdTgJbGNkbGF5b3V0OworCisJLyoKKwkgKiBQcm90ZWN0 IHRoZSBkYXRhIGZvcm0gY29uY3VycmVudCBhY2Nlc3MgYnkgcmVhZC93cml0ZQorCSAqIGFuZCBj cmVhdGUgc3ltbGluay9yZW1vdmUgc3ltbGluay4KKwkgKi8KKwlzdHJ1Y3QgbXV0ZXgJbG9jazsK KwlpbnQJCXJlZmNudDsKK307CisKK3N0cnVjdCBjY2lkZ19idWxrX2luX2hlYWRlciB7CisJX191 OAliTWVzc2FnZVR5cGU7CisJX191MzIJd0xlbmd0aDsKKwlfX3U4CWJTbG90OworCV9fdTgJYlNl cTsKKwlfX3U4CWJTdGF0dXM7CisJX191OAliRXJyb3I7CisJX191OAliU3BlY2lmaWM7CisJX191 OAlhYkRhdGFbQUJEQVRBX1NJWkVdOworCV9fdTgJYlNpemVUb1NlbmQ7Cit9IF9fcGFja2VkOwor CitzdHJ1Y3QgY2NpZGdfYnVsa19vdXRfaGVhZGVyIHsKKwlfX3U4CSBiTWVzc2FnZVR5cGU7CisJ X191MzIJIHdMZW5ndGg7CisJX191OAkgYlNsb3Q7CisJX191OAkgYlNlcTsKKwlfX3U4CSBiU3Bl Y2lmaWNfMDsKKwlfX3U4CSBiU3BlY2lmaWNfMTsKKwlfX3U4CSBiU3BlY2lmaWNfMjsKKwlfX3U4 CSBBUERVW0FCREFUQV9TSVpFXTsKK30gX19wYWNrZWQ7CisKK3N0cnVjdCBjY2lkX2NsYXNzX2Rl c2NyaXB0b3IgeworCV9fdTgJYkxlbmd0aDsKKwlfX3U4CWJEZXNjcmlwdG9yVHlwZTsKKwlfX3Ux NgliY2RDQ0lEOworCV9fdTgJYk1heFNsb3RJbmRleDsKKwlfX3U4CWJWb2x0YWdlU3VwcG9ydDsK KwlfX3UzMglkd1Byb3RvY29sczsKKwlfX3UzMglkd0RlZmF1bHRDbG9jazsKKwlfX3UzMglkd01h eGltdW1DbG9jazsKKwlfX3U4CWJOdW1DbG9ja1N1cHBvcnRlZDsKKwlfX3UzMglkd0RhdGFSYXRl OworCV9fdTMyCWR3TWF4RGF0YVJhdGU7CisJX191OAliTnVtRGF0YVJhdGVzU3VwcG9ydGVkOwor CV9fdTMyCWR3TWF4SUZTRDsKKwlfX3UzMglkd1N5bmNoUHJvdG9jb2xzOworCV9fdTMyCWR3TWVj aGFuaWNhbDsKKwlfX3UzMglkd0ZlYXR1cmVzOworCV9fdTMyCWR3TWF4Q0NJRE1lc3NhZ2VMZW5n dGg7CisJX191OAliQ2xhc3NHZXRSZXNwb25zZTsKKwlfX3U4CWJDbGFzc0VudmVsb3BlOworCV9f dTE2CXdMY2RMYXlvdXQ7CisJX191OAliUElOU3VwcG9ydDsKKwlfX3U4CWJNYXhDQ0lEQnVzeVNs b3RzOworfSBfX3BhY2tlZDsKKworCisjZW5kaWYKZGlmZiAtLWdpdCBhL2luY2x1ZGUvdWFwaS9s aW51eC91c2IvY2NpZC5oIGIvaW5jbHVkZS91YXBpL2xpbnV4L3VzYi9jY2lkLmgKbmV3IGZpbGUg bW9kZSAxMDA2NDQKaW5kZXggMDAwMDAwMDAwMDAwLi41MTc4OTcyMDE1NjMKLS0tIC9kZXYvbnVs bAorKysgYi9pbmNsdWRlL3VhcGkvbGludXgvdXNiL2NjaWQuaApAQCAtMCwwICsxLDkzIEBACisv KiBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogR1BMLTIuMCAqLworLyoKKyAqIENvcHlyaWdodCAo QykgMjAxOCBNYXJjdXMgRm9sa2Vzc29uIDxtYXJjdXMuZm9sa2Vzc29uQGdtYWlsLmNvbT4KKyAq CisgKiBUaGlzIGZpbGUgaG9sZHMgVVNCIGNvbnN0YW50cyBkZWZpbmVkIGJ5IHRoZSBDQ0lEIFNw ZWNpZmljYXRpb24uCisgKi8KKworI2lmbmRlZiBDQ0lEX0gKKyNkZWZpbmUgQ0NJRF9ICisKKy8q IFNsb3QgZXJyb3IgcmVnaXN0ZXIgd2hlbiBibUNvbW1hbmRTdGF0dXMgPSAxICovCisjZGVmaW5l IENDSURfQ01EX0FCT1JURUQgICAgICAgICAgICAgICAgICAgICAgICAgICAgMHhGRgorI2RlZmlu ZSBDQ0lEX0lDQ19NVVRFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDB4RkUKKyNkZWZp bmUgQ0NJRF9YRlJfUEFSSVRZX0VSUk9SICAgICAgICAgICAgICAgICAgICAgICAweEZECisjZGVm aW5lIENDSURfWEZSX09WRVJSVU4gICAgICAgICAgICAgICAgICAgICAgICAgICAgMHhGQworI2Rl ZmluZSBDQ0lEX0hXX0VSUk9SICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDB4RkIKKyNk ZWZpbmUgQ0NJRF9CQURfQVRSX1RTICAgICAgICAgICAgICAgICAgICAgICAgICAgICAweEY4Cisj ZGVmaW5lIENDSURfQkFEX0FUUl9UQ0sgICAgICAgICAgICAgICAgICAgICAgICAgICAgMHhGNwor I2RlZmluZSBDQ0lEX0lDQ19QUk9UT0NPTF9OT1RfU1VQUE9SVEVEICAgICAgICAgICAgIDB4RjYK KyNkZWZpbmUgQ0NJRF9JQ0NfQ0xBU1NfTk9UX1NVUFBPUlRFRCAgICAgICAgICAgICAgICAweEY1 CisjZGVmaW5lIENDSURfUFJPQ0VEVVJFX0JZVEVfQ09ORkxJQ1QgICAgICAgICAgICAgICAgMHhG NAorI2RlZmluZSBDQ0lEX0RFQUNUSVZBVEVEX1BST1RPQ09MICAgICAgICAgICAgICAgICAgIDB4 RjMKKyNkZWZpbmUgQ0NJRF9CVVNZX1dJVEhfQVVUT19TRVFVRU5DRSAgICAgICAgICAgICAgICAw eEYyCisjZGVmaW5lIENDSURfUElOX1RJTUVPVVQgICAgICAgICAgICAgICAgICAgICAgICAgICAg MHhGMAorI2RlZmluZSBDQ0lEX1BJTl9DQU5DRUxMRUQgICAgICAgICAgICAgICAgICAgICAgICAg IDB4RUYKKyNkZWZpbmUgQ0NJRF9DTURfU0xPVF9CVVNZICAgICAgICAgICAgICAgICAgICAgICAg ICAweEUwCisKKy8qIFBDIHRvIFJEUiBtZXNzYWdlcyAoYnVsayBvdXQpICovCisjZGVmaW5lIEND SURfUENfVE9fUkRSX0lDQ1BPV0VST04gICAgICAgICAgICAgICAgICAgMHg2MgorI2RlZmluZSBD Q0lEX1BDX1RPX1JEUl9JQ0NQT1dFUk9GRiAgICAgICAgICAgICAgICAgIDB4NjMKKyNkZWZpbmUg Q0NJRF9QQ19UT19SRFJfR0VUU0xPVFNUQVRVUyAgICAgICAgICAgICAgICAweDY1CisjZGVmaW5l IENDSURfUENfVE9fUkRSX1hGUkJMT0NLICAgICAgICAgICAgICAgICAgICAgMHg2RgorI2RlZmlu ZSBDQ0lEX1BDX1RPX1JEUl9HRVRQQVJBTUVURVJTICAgICAgICAgICAgICAgIDB4NkMKKyNkZWZp bmUgQ0NJRF9QQ19UT19SRFJfUkVTRVRQQVJBTUVURVJTICAgICAgICAgICAgICAweDZECisjZGVm aW5lIENDSURfUENfVE9fUkRSX1NFVFBBUkFNRVRFUlMgICAgICAgICAgICAgICAgMHg2MQorI2Rl ZmluZSBDQ0lEX1BDX1RPX1JEUl9FU0NBUEUgICAgICAgICAgICAgICAgICAgICAgIDB4NkIKKyNk ZWZpbmUgQ0NJRF9QQ19UT19SRFJfSUNDQ0xPQ0sgICAgICAgICAgICAgICAgICAgICAweDZFCisj ZGVmaW5lIENDSURfUENfVE9fUkRSX1QwQVBEVSAgICAgICAgICAgICAgICAgICAgICAgMHg2QQor I2RlZmluZSBDQ0lEX1BDX1RPX1JEUl9TRUNVUkUgICAgICAgICAgICAgICAgICAgICAgIDB4NjkK KyNkZWZpbmUgQ0NJRF9QQ19UT19SRFJfTUVDSEFOSUNBTCAgICAgICAgICAgICAgICAgICAweDcx CisjZGVmaW5lIENDSURfUENfVE9fUkRSX0FCT1JUICAgICAgICAgICAgICAgICAgICAgICAgMHg3 MgorI2RlZmluZSBDQ0lEX1BDX1RPX1JEUl9TRVREQVRBUkFURUFORENMT0NLRlJFUVVFTkNZIDB4 NzMKKworLyogUkRSIHRvIFBDIG1lc3NhZ2VzIChidWxrIGluKSAqLworI2RlZmluZSBDQ0lEX1JE Ul9UT19QQ19EQVRBQkxPQ0sgICAgICAgICAgICAgICAgICAgIDB4ODAKKyNkZWZpbmUgQ0NJRF9S RFJfVE9fUENfU0xPVFNUQVRVUyAgICAgICAgICAgICAgICAgICAweDgxCisjZGVmaW5lIENDSURf UkRSX1RPX1BDX1BBUkFNRVRFUlMgICAgICAgICAgICAgICAgICAgMHg4MgorI2RlZmluZSBDQ0lE X1JEUl9UT19QQ19FU0NBUEUgICAgICAgICAgICAgICAgICAgICAgIDB4ODMKKyNkZWZpbmUgQ0NJ RF9SRFJfVE9fUENfREFUQVJBVEVBTkRDTE9DS0ZSRVFVRU5DWSAgICAweDg0CisKKy8qIENsYXNz IEZlYXR1cmVzICovCisKKy8qIE5vIHNwZWNpYWwgY2hhcmFjdGVyaXN0aWNzICovCisjZGVmaW5l IENDSURfRkVBVFVSRVNfTkFEQSAgICAgICAweDAwMDAwMDAwCisvKiBBdXRvbWF0aWMgcGFyYW1l dGVyIGNvbmZpZ3VyYXRpb24gYmFzZWQgb24gQVRSIGRhdGEgKi8KKyNkZWZpbmUgQ0NJRF9GRUFU VVJFU19BVVRPX1BDT05GIDB4MDAwMDAwMDIKKy8qIEF1dG9tYXRpYyBhY3RpdmF0aW9uIG9mIElD QyBvbiBpbnNlcnRpbmcgKi8KKyNkZWZpbmUgQ0NJRF9GRUFUVVJFU19BVVRPX0FDVElWIDB4MDAw MDAwMDQKKy8qIEF1dG9tYXRpYyBJQ0Mgdm9sdGFnZSBzZWxlY3Rpb24gKi8KKyNkZWZpbmUgQ0NJ RF9GRUFUVVJFU19BVVRPX1ZPTFQgIDB4MDAwMDAwMDgKKy8qIEF1dG9tYXRpYyBJQ0MgY2xvY2sg ZnJlcXVlbmN5IGNoYW5nZSAqLworI2RlZmluZSBDQ0lEX0ZFQVRVUkVTX0FVVE9fQ0xPQ0sgMHgw MDAwMDAxMAorLyogQXV0b21hdGljIGJhdWQgcmF0ZSBjaGFuZ2UgKi8KKyNkZWZpbmUgQ0NJRF9G RUFUVVJFU19BVVRPX0JBVUQgIDB4MDAwMDAwMjAKKy8qQXV0b21hdGljIHBhcmFtZXRlcnMgbmVn b3RpYXRpb24gbWFkZSBieSB0aGUgQ0NJRCAqLworI2RlZmluZSBDQ0lEX0ZFQVRVUkVTX0FVVE9f UE5FR08gMHgwMDAwMDA0MAorLyogQXV0b21hdGljIFBQUyBtYWRlIGJ5IHRoZSBDQ0lEIGFjY29y ZGluZyB0byB0aGUgYWN0aXZlIHBhcmFtZXRlcnMgKi8KKyNkZWZpbmUgQ0NJRF9GRUFUVVJFU19B VVRPX1BQUyAgIDB4MDAwMDAwODAKKy8qIENDSUQgY2FuIHNldCBJQ0MgaW4gY2xvY2sgc3RvcCBt b2RlICovCisjZGVmaW5lIENDSURfRkVBVFVSRVNfSUNDU1RPUCAgICAweDAwMDAwMTAwCisvKiBO QUQgdmFsdWUgb3RoZXIgdGhhbiAwMCBhY2NlcHRlZCAoVD0xIHByb3RvY29sIGluIHVzZSkgKi8K KyNkZWZpbmUgQ0NJRF9GRUFUVVJFU19OQUQgICAgICAgIDB4MDAwMDAyMDAKKy8qIEF1dG9tYXRp YyBJRlNEIGV4Y2hhbmdlIGFzIGZpcnN0IGV4Y2hhbmdlIChUPTEgcHJvdG9jb2wgaW4gdXNlKSAq LworI2RlZmluZSBDQ0lEX0ZFQVRVUkVTX0FVVE9fSUZTRCAgMHgwMDAwMDQwMAorLyogVFBEVSBs ZXZlbCBleGNoYW5nZXMgd2l0aCBDQ0lEICovCisjZGVmaW5lIENDSURfRkVBVFVSRVNfRVhDX1RQ RFUgICAweDAwMDEwMDAwCisvKiBTaG9ydCBBUERVIGxldmVsIGV4Y2hhbmdlIHdpdGggQ0NJRCAq LworI2RlZmluZSBDQ0lEX0ZFQVRVUkVTX0VYQ19TQVBEVSAgMHgwMDAyMDAwMAorLyogU2hvcnQg YW5kIEV4dGVuZGVkIEFQRFUgbGV2ZWwgZXhjaGFuZ2Ugd2l0aCBDQ0lEICovCisjZGVmaW5lIEND SURfRkVBVFVSRVNfRVhDX0FQRFUgICAweDAwMDQwMDAwCisvKiBVU0IgV2FrZSB1cCBzaWduYWxp bmcgc3VwcG9ydGVkIG9uIGNhcmQgaW5zZXJ0aW9uIGFuZCByZW1vdmFsICovCisjZGVmaW5lIEND SURfRkVBVFVSRVNfV0FLRVVQCTB4MDAxMDAwMDAKKworLyogU3VwcG9ydGVkIHByb3RvY29scyAq LworI2RlZmluZSBDQ0lEX1BST1RPQ09MX05PVF9TRUwJMHgwMAorI2RlZmluZSBDQ0lEX1BST1RP Q09MX1QwCTB4MDEKKyNkZWZpbmUgQ0NJRF9QUk9UT0NPTF9UMQkweDAyCisKKyNkZWZpbmUgQ0NJ RF9QSU5TVVBPT1JUX05PTkUJCTB4MDAKKyNkZWZpbmUgQ0NJRF9QSU5TVVBPT1JUX1ZFUklGSUNB VElPTgkoMSA8PCAxKQorI2RlZmluZSBDQ0lEX1BJTlNVUE9PUlRfTU9ESUZJQ0FUSU9OCSgxIDw8 IDIpCisKKyNlbmRpZgo= From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1032415AbeEZUfO (ORCPT ); Sat, 26 May 2018 16:35:14 -0400 Received: from mail-wr0-f194.google.com ([209.85.128.194]:36424 "EHLO mail-wr0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1032365AbeEZUfK (ORCPT ); Sat, 26 May 2018 16:35:10 -0400 X-Google-Smtp-Source: ADUXVKJv6xzbKxrdguUdJqL5Ts4K6kJ8SSUo+258yiPxgp31gU9+PaejQgQqGh19H/pDvcB4IN4i0Q== 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 1/3] usb: gadget: ccid: add support for USB CCID Gadget Device Date: Sat, 26 May 2018 22:33:59 +0200 Message-Id: <20180526203401.16586-1-marcus.folkesson@gmail.com> X-Mailer: git-send-email 2.16.2 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Signed-off-by: Marcus Folkesson --- drivers/usb/gadget/Kconfig | 17 + drivers/usb/gadget/function/Makefile | 1 + drivers/usb/gadget/function/f_ccid.c | 988 +++++++++++++++++++++++++++++++++++ drivers/usb/gadget/function/f_ccid.h | 91 ++++ include/uapi/linux/usb/ccid.h | 93 ++++ 5 files changed, 1190 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..9ff8615ca303 --- /dev/null +++ b/drivers/usb/gadget/function/f_ccid.c @@ -0,0 +1,988 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * f_ccid.c -- Chip Card Interface Device (CCID) function Driver + * + * Copyright (C) 2018 Marcus Folkesson + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f_ccid.h" +#include "u_f.h" + +/* Number of tx requests to allocate */ +#define N_TX_REQS 4 + +/* Maximum number of devices */ +#define CCID_MINORS 4 + +struct ccidg_bulk_dev { + atomic_t is_open; + atomic_t rx_req_busy; + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct usb_request *rx_req; + atomic_t rx_done; + struct list_head tx_idle; +}; + +struct f_ccidg { + struct usb_function_instance func_inst; + struct usb_function function; + spinlock_t lock; + atomic_t online; + + /* Character device */ + struct cdev cdev; + int minor; + + /* Dynamic attributes */ + u32 features; + u32 protocols; + u8 pinsupport; + u8 nslots; + u8 lcdlayout; + + /* Endpoints */ + struct usb_ep *in; + struct usb_ep *out; + struct ccidg_bulk_dev bulk_dev; +}; + +/* Interface Descriptor: */ +static struct usb_interface_descriptor ccid_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CSCID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, +}; + +/* CCID Class Descriptor */ +static struct ccid_class_descriptor ccid_class_desc = { + .bLength = sizeof(ccid_class_desc), + .bDescriptorType = CCID_DECRIPTOR_TYPE, + .bcdCCID = CCID1_10, + /* .bMaxSlotIndex = DYNAMIC */ + .bVoltageSupport = CCID_VOLTS_3_0, + /* .dwProtocols = DYNAMIC */ + .dwDefaultClock = 3580, + .dwMaximumClock = 3580, + .bNumClockSupported = 0, + .dwDataRate = 9600, + .dwMaxDataRate = 9600, + .bNumDataRatesSupported = 0, + .dwMaxIFSD = 0, + .dwSynchProtocols = 0, + .dwMechanical = 0, + /* .dwFeatures = DYNAMIC */ + + /* extended APDU level Message Length */ + .dwMaxCCIDMessageLength = 0x200, + .bClassGetResponse = 0x0, + .bClassEnvelope = 0x0, + /* .wLcdLayout = DYNAMIC */ + /* .bPINSupport = DYNAMIC */ + .bMaxCCIDBusySlots = 1 +}; + +/* Full speed support: */ +static struct usb_endpoint_descriptor ccid_fs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_endpoint_descriptor ccid_fs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_descriptor_header *ccid_fs_descs[] = { + (struct usb_descriptor_header *) &ccid_interface_desc, + (struct usb_descriptor_header *) &ccid_class_desc, + (struct usb_descriptor_header *) &ccid_fs_in_desc, + (struct usb_descriptor_header *) &ccid_fs_out_desc, + NULL, +}; + +/* High speed support: */ +static struct usb_endpoint_descriptor ccid_hs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor ccid_hs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_descriptor_header *ccid_hs_descs[] = { + (struct usb_descriptor_header *) &ccid_interface_desc, + (struct usb_descriptor_header *) &ccid_class_desc, + (struct usb_descriptor_header *) &ccid_hs_in_desc, + (struct usb_descriptor_header *) &ccid_hs_out_desc, + NULL, +}; + +static DEFINE_IDA(ccidg_ida); +static int major; +static DEFINE_MUTEX(ccidg_ida_lock); /* protects access to ccidg_ida */ +static struct class *ccidg_class; + +static inline struct f_ccidg_opts *to_f_ccidg_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_ccidg_opts, + func_inst.group); +} + +static inline struct f_ccidg *func_to_ccidg(struct usb_function *f) +{ + return container_of(f, struct f_ccidg, function); +} + +static inline int ccidg_get_minor(void) +{ + int ret; + + ret = ida_simple_get(&ccidg_ida, 0, 0, GFP_KERNEL); + if (ret >= CCID_MINORS) { + ida_simple_remove(&ccidg_ida, ret); + ret = -ENODEV; + } + + return ret; +} + +static inline void ccidg_put_minor(int minor) +{ + ida_simple_remove(&ccidg_ida, minor); +} + +static int ccidg_setup(void) +{ + int ret; + dev_t dev; + + ccidg_class = class_create(THIS_MODULE, "ccidg"); + if (IS_ERR(ccidg_class)) { + ccidg_class = NULL; + return PTR_ERR(ccidg_class); + } + + ret = alloc_chrdev_region(&dev, 0, CCID_MINORS, "ccidg"); + if (ret) { + class_destroy(ccidg_class); + ccidg_class = NULL; + return ret; + } + + major = MAJOR(dev); + + return 0; +} + +static void ccidg_cleanup(void) +{ + if (major) { + unregister_chrdev_region(MKDEV(major, 0), CCID_MINORS); + major = 0; + } + + class_destroy(ccidg_class); + ccidg_class = NULL; +} + +static void ccidg_attr_release(struct config_item *item) +{ + struct f_ccidg_opts *opts = to_f_ccidg_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations ccidg_item_ops = { + .release = ccidg_attr_release, +}; + +#define F_CCIDG_OPT(name, prec, limit) \ +static ssize_t f_ccidg_opts_##name##_show(struct config_item *item, char *page)\ +{ \ + struct f_ccidg_opts *opts = to_f_ccidg_opts(item); \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%x\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_ccidg_opts_##name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct f_ccidg_opts *opts = to_f_ccidg_opts(item); \ + int ret; \ + u##prec num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou##prec(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + opts->name = num; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +CONFIGFS_ATTR(f_ccidg_opts_, name) + +F_CCIDG_OPT(features, 32, 0xffffffff); +F_CCIDG_OPT(protocols, 32, 0x03); +F_CCIDG_OPT(pinsupport, 8, 0x03); +F_CCIDG_OPT(lcdlayout, 16, 0xffff); +F_CCIDG_OPT(nslots, 8, 0xff); + +static struct configfs_attribute *ccidg_attrs[] = { + &f_ccidg_opts_attr_features, + &f_ccidg_opts_attr_protocols, + &f_ccidg_opts_attr_pinsupport, + &f_ccidg_opts_attr_lcdlayout, + &f_ccidg_opts_attr_nslots, + NULL, +}; + +static struct config_item_type ccidg_func_type = { + .ct_item_ops = &ccidg_item_ops, + .ct_attrs = ccidg_attrs, + .ct_owner = THIS_MODULE, +}; + +static void ccidg_req_put(struct f_ccidg *ccidg, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&ccidg->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&ccidg->lock, flags); +} + +static struct usb_request *ccidg_req_get(struct f_ccidg *ccidg, + struct list_head *head) +{ + unsigned long flags; + struct usb_request *req = NULL; + + spin_lock_irqsave(&ccidg->lock, flags); + if (!list_empty(head)) { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&ccidg->lock, flags); + + return req; +} + +static void ccidg_bulk_complete_tx(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ccidg *ccidg = (struct f_ccidg *)ep->driver_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_composite_dev *cdev = ccidg->function.config->cdev; + + switch (req->status) { + default: + VDBG(cdev, "ccid: tx err %d\n", req->status); + /* FALLTHROUGH */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + break; + case 0: + break; + } + + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + wake_up(&bulk_dev->write_wq); +} + +static void ccidg_bulk_complete_rx(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ccidg *ccidg = (struct f_ccidg *)ep->driver_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_composite_dev *cdev = ccidg->function.config->cdev; + + switch (req->status) { + + /* normal completion */ + case 0: + /* We only cares about packets with nonzero length */ + if (req->actual > 0) + atomic_set(&bulk_dev->rx_done, 1); + break; + + /* software-driven interface shutdown */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + VDBG(cdev, "ccid: rx shutdown, code %d\n", req->status); + break; + + /* for hardware automagic (such as pxa) */ + case -ECONNABORTED: /* endpoint reset */ + DBG(cdev, "ccid: rx %s reset\n", ep->name); + break; + + /* data overrun */ + case -EOVERFLOW: + /* FALLTHROUGH */ + default: + DBG(cdev, "ccid: rx status %d\n", req->status); + break; + } + + wake_up(&bulk_dev->read_wq); +} + +static struct usb_request * +ccidg_request_alloc(struct usb_ep *ep, unsigned int len) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (!req) + return ERR_PTR(-ENOMEM); + + req->length = len; + req->buf = kmalloc(len, GFP_ATOMIC); + if (req->buf == NULL) { + usb_ep_free_request(ep, req); + return ERR_PTR(-ENOMEM); + } + + return req; +} + +static void ccidg_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static int ccidg_function_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_ccidg *ccidg = container_of(f, struct f_ccidg, function); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int ret = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + if (!atomic_read(&ccidg->online)) + return -ENOTCONN; + + switch (ctrl->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_CLASS: + { + switch (ctrl->bRequest) { + case CCIDGENERICREQ_GET_CLOCK_FREQUENCIES: + *(u32 *) req->buf = cpu_to_le32(ccid_class_desc.dwDefaultClock); + ret = min_t(u32, w_length, + sizeof(ccid_class_desc.dwDefaultClock)); + break; + + case CCIDGENERICREQ_GET_DATA_RATES: + *(u32 *) req->buf = cpu_to_le32(ccid_class_desc.dwDataRate); + ret = min_t(u32, w_length, 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); +} + +int ccidg_start_ep(struct f_ccidg *ccidg, struct usb_function *f, + struct usb_ep *ep) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + usb_ep_disable(ep); + + ret = config_ep_by_speed(cdev->gadget, f, ep); + if (ret) { + ERROR(cdev, "ccid: can't configure %s: %d\n", ep->name, ret); + return ret; + } + + ret = usb_ep_enable(ep); + if (ret) { + ERROR(cdev, "ccid: can't start %s: %d\n", ep->name, ret); + return ret; + } + + ep->driver_data = ccidg; + + return ret; +} + +static int ccidg_function_set_alt(struct usb_function *f, + unsigned int intf, unsigned int alt) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req; + int ret; + int i; + + /* Allocate requests for our endpoints */ + req = ccidg_request_alloc(ccidg->out, + sizeof(struct ccidg_bulk_out_header)); + if (IS_ERR(req)) { + ERROR(cdev, "ccid: uname to allocate memory for out req\n"); + return PTR_ERR(req); + } + req->complete = ccidg_bulk_complete_rx; + req->context = ccidg; + bulk_dev->rx_req = req; + + /* Allocate bunch of in requests */ + for (i = 0; i < N_TX_REQS; i++) { + req = ccidg_request_alloc(ccidg->in, + sizeof(struct ccidg_bulk_in_header)); + + if (IS_ERR(req)) { + ret = PTR_ERR(req); + ERROR(cdev, + "ccid: uname to allocate memory for in req\n"); + goto free_bulk_out; + } + req->complete = ccidg_bulk_complete_tx; + req->context = ccidg; + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + } + + /* choose the descriptors and enable endpoints */ + ret = ccidg_start_ep(ccidg, f, ccidg->in); + if (ret) + goto free_bulk_in; + + ret = ccidg_start_ep(ccidg, f, ccidg->out); + if (ret) + goto disable_ep_in; + + atomic_set(&ccidg->online, 1); + return ret; + +disable_ep_in: + usb_ep_disable(ccidg->in); +free_bulk_in: + while ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle))) + ccidg_request_free(req, ccidg->in); +free_bulk_out: + ccidg_request_free(bulk_dev->rx_req, ccidg->out); + return ret; +} + +static int ccidg_bulk_open(struct inode *inode, struct file *file) +{ + struct f_ccidg *ccidg; + struct ccidg_bulk_dev *bulk_dev; + + ccidg = container_of(inode->i_cdev, struct f_ccidg, cdev); + bulk_dev = &ccidg->bulk_dev; + + if (!atomic_read(&ccidg->online)) { + DBG(ccidg->function.config->cdev, "ccid: device not online\n"); + return -ENODEV; + } + + if (atomic_read(&bulk_dev->is_open)) { + DBG(ccidg->function.config->cdev, + "ccid: device already opened\n"); + return -EBUSY; + } + + atomic_set(&bulk_dev->is_open, 1); + + file->private_data = ccidg; + + return 0; +} + +static int ccidg_bulk_release(struct inode *inode, struct file *file) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + + atomic_set(&bulk_dev->is_open, 0); + return 0; +} + +static ssize_t ccidg_bulk_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req; + int r = count, xfer; + int ret; + + /* Make sure we have enough space for a whole package */ + if (count < sizeof(struct ccidg_bulk_out_header)) { + DBG(ccidg->function.config->cdev, + "ccid: too small buffer size. %i provided, need at least %i\n", + count, sizeof(struct ccidg_bulk_out_header)); + return -ENOMEM; + } + + if (!atomic_read(&ccidg->online)) + return -ENODEV; + + /* queue a request */ + req = bulk_dev->rx_req; + req->length = count; + atomic_set(&bulk_dev->rx_done, 0); + + ret = usb_ep_queue(ccidg->out, req, GFP_KERNEL); + if (ret < 0) { + ERROR(ccidg->function.config->cdev, + "ccid: usb ep queue failed\n"); + return -EIO; + } + + if (!atomic_read(&bulk_dev->rx_done) && + file->f_flags & (O_NONBLOCK | O_NDELAY)) + return -EAGAIN; + + /* wait for a request to complete */ + ret = wait_event_interruptible(bulk_dev->read_wq, + atomic_read(&bulk_dev->rx_done) || + !atomic_read(&ccidg->online)); + if (ret < 0) { + usb_ep_dequeue(ccidg->out, req); + return -ERESTARTSYS; + } + + /* Still online? */ + if (!atomic_read(&ccidg->online)) + return -ENODEV; + + atomic_set(&bulk_dev->rx_req_busy, 1); + xfer = (req->actual < count) ? req->actual : count; + + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + + atomic_set(&bulk_dev->rx_req_busy, 0); + if (!atomic_read(&ccidg->online)) { + ccidg_request_free(bulk_dev->rx_req, ccidg->out); + return -ENODEV; + } + + return xfer; +} + +static ssize_t ccidg_bulk_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + struct usb_request *req = 0; + 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. %i provided, but we can only handle %i\n", + count, sizeof(struct ccidg_bulk_out_header)); + return -ENOMEM; + } + + if (list_empty(&bulk_dev->tx_idle) && + file->f_flags & (O_NONBLOCK | O_NDELAY)) + return -EAGAIN; + + /* get an idle tx request to use */ + ret = wait_event_interruptible(bulk_dev->write_wq, + ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle)))); + + if (ret < 0) + return -ERESTARTSYS; + + if (copy_from_user(req->buf, buf, count)) { + if (!atomic_read(&ccidg->online)) { + ccidg_request_free(req, ccidg->in); + return -ENODEV; + } else { + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + return -EFAULT; + } + } + + req->length = count; + ret = usb_ep_queue(ccidg->in, req, GFP_KERNEL); + if (ret < 0) { + ccidg_req_put(ccidg, &bulk_dev->tx_idle, req); + + if (!atomic_read(&ccidg->online)) { + /* Free up all requests if we are not online */ + while ((req = ccidg_req_get(ccidg, &bulk_dev->tx_idle))) + ccidg_request_free(req, ccidg->in); + + return -ENODEV; + } + return -EIO; + } + + return count; +} + +static __poll_t ccidg_bulk_poll(struct file *file, poll_table * wait) +{ + struct f_ccidg *ccidg = file->private_data; + struct ccidg_bulk_dev *bulk_dev = &ccidg->bulk_dev; + __poll_t ret = 0; + + poll_wait(file, &bulk_dev->read_wq, wait); + poll_wait(file, &bulk_dev->write_wq, wait); + + if (list_empty(&bulk_dev->tx_idle)) + ret |= EPOLLOUT | EPOLLWRNORM; + + if (atomic_read(&bulk_dev->rx_done)) + ret |= EPOLLIN | EPOLLRDNORM; + + return ret; +} + +static const struct file_operations f_ccidg_fops = { + .owner = THIS_MODULE, + .read = ccidg_bulk_read, + .write = ccidg_bulk_write, + .open = ccidg_bulk_open, + .poll = ccidg_bulk_poll, + .release = ccidg_bulk_release, +}; + +static int ccidg_bulk_device_init(struct f_ccidg *dev) +{ + struct ccidg_bulk_dev *bulk_dev = &dev->bulk_dev; + + init_waitqueue_head(&bulk_dev->read_wq); + init_waitqueue_head(&bulk_dev->write_wq); + INIT_LIST_HEAD(&bulk_dev->tx_idle); + + return 0; +} + +static void ccidg_function_free(struct usb_function *f) +{ + struct f_ccidg *ccidg; + struct f_ccidg_opts *opts; + + ccidg = func_to_ccidg(f); + opts = container_of(f->fi, struct f_ccidg_opts, func_inst); + + kfree(ccidg); + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); +} + +static void ccidg_function_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + + device_destroy(ccidg_class, MKDEV(major, ccidg->minor)); + cdev_del(&ccidg->cdev); + + /* disable/free request and end point */ + usb_free_all_descriptors(f); +} + +static int ccidg_function_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct f_ccidg *ccidg = func_to_ccidg(f); + struct usb_ep *ep; + struct usb_composite_dev *cdev = c->cdev; + struct device *device; + dev_t dev; + int ifc_id; + int ret; + + /* allocate instance-specific interface IDs, and patch descriptors */ + ifc_id = usb_interface_id(c, f); + if (ifc_id < 0) { + ERROR(cdev, "ccid: unable to allocate ifc id, err:%d\n", + ifc_id); + return ifc_id; + } + ccid_interface_desc.bInterfaceNumber = ifc_id; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_in_desc); + if (!ep) { + ERROR(cdev, "ccid: usb epin autoconfig failed\n"); + ret = -ENODEV; + goto ep_auto_in_fail; + } + ccidg->in = ep; + ep->driver_data = ccidg; + + ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_out_desc); + if (!ep) { + ERROR(cdev, "ccid: usb epout autoconfig failed\n"); + ret = -ENODEV; + goto ep_auto_out_fail; + } + ccidg->out = ep; + ep->driver_data = ccidg; + + /* set descriptor dynamic values */ + ccid_class_desc.dwFeatures = cpu_to_le32(ccidg->features); + ccid_class_desc.bPINSupport = ccidg->pinsupport; + ccid_class_desc.wLcdLayout = cpu_to_le16(ccidg->lcdlayout); + ccid_class_desc.bMaxSlotIndex = ccidg->nslots; + ccid_class_desc.dwProtocols = cpu_to_le32(ccidg->protocols); + + if (ccidg->protocols == CCID_PROTOCOL_NOT_SEL) { + ccidg->protocols = CCID_PROTOCOL_T0 | CCID_PROTOCOL_T1; + INFO(ccidg->function.config->cdev, + "ccid: No protocol selected. Support both T0 and T1.\n"); + } + + + ccid_hs_in_desc.bEndpointAddress = + ccid_fs_in_desc.bEndpointAddress; + ccid_hs_out_desc.bEndpointAddress = + ccid_fs_out_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, ccid_fs_descs, + ccid_hs_descs, NULL, NULL); + if (ret) + goto ep_auto_out_fail; + + /* create char device */ + cdev_init(&ccidg->cdev, &f_ccidg_fops); + dev = MKDEV(major, ccidg->minor); + ret = cdev_add(&ccidg->cdev, dev, 1); + if (ret) + goto fail_free_descs; + + device = device_create(ccidg_class, NULL, dev, NULL, + "%s%d", "ccidg", ccidg->minor); + if (IS_ERR(device)) { + ret = PTR_ERR(device); + goto del; + } + + return 0; + +del: + cdev_del(&ccidg->cdev); +fail_free_descs: + usb_free_all_descriptors(f); +ep_auto_out_fail: + ccidg->out->driver_data = NULL; + ccidg->out = NULL; +ep_auto_in_fail: + ccidg->in->driver_data = NULL; + ccidg->in = NULL; + ERROR(f->config->cdev, "ccidg_bind FAILED\n"); + + return ret; +} + +static struct usb_function *ccidg_alloc(struct usb_function_instance *fi) +{ + struct f_ccidg *ccidg; + struct f_ccidg_opts *opts; + int ret; + + ccidg = kzalloc(sizeof(*ccidg), GFP_KERNEL); + if (!ccidg) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&ccidg->lock); + + ret = ccidg_bulk_device_init(ccidg); + if (ret) { + kfree(ccidg); + return ERR_PTR(ret); + } + + opts = container_of(fi, struct f_ccidg_opts, func_inst); + + mutex_lock(&opts->lock); + ++opts->refcnt; + + ccidg->minor = opts->minor; + ccidg->features = opts->features; + ccidg->protocols = opts->protocols; + ccidg->pinsupport = opts->pinsupport; + ccidg->nslots = opts->nslots; + mutex_unlock(&opts->lock); + + ccidg->function.name = "ccid"; + ccidg->function.bind = ccidg_function_bind; + ccidg->function.unbind = ccidg_function_unbind; + ccidg->function.set_alt = ccidg_function_set_alt; + ccidg->function.disable = ccidg_function_disable; + ccidg->function.setup = ccidg_function_setup; + ccidg->function.free_func = ccidg_function_free; + + return &ccidg->function; +} + +static void ccidg_free_inst(struct usb_function_instance *f) +{ + struct f_ccidg_opts *opts; + + opts = container_of(f, struct f_ccidg_opts, func_inst); + mutex_lock(&ccidg_ida_lock); + + ccidg_put_minor(opts->minor); + if (ida_is_empty(&ccidg_ida)) + ccidg_cleanup(); + + mutex_unlock(&ccidg_ida_lock); + + kfree(opts); +} + +static struct usb_function_instance *ccidg_alloc_inst(void) +{ + struct f_ccidg_opts *opts; + struct usb_function_instance *ret; + int status = 0; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = ccidg_free_inst; + ret = &opts->func_inst; + + mutex_lock(&ccidg_ida_lock); + + if (ida_is_empty(&ccidg_ida)) { + status = ccidg_setup(); + if (status) { + ret = ERR_PTR(status); + kfree(opts); + goto unlock; + } + } + + opts->minor = ccidg_get_minor(); + if (opts->minor < 0) { + ret = ERR_PTR(opts->minor); + kfree(opts); + if (ida_is_empty(&ccidg_ida)) + ccidg_cleanup(); + goto unlock; + } + + config_group_init_type_name(&opts->func_inst.group, + "", &ccidg_func_type); + +unlock: + mutex_unlock(&ccidg_ida_lock); + return ret; +} + +DECLARE_USB_FUNCTION_INIT(ccid, ccidg_alloc_inst, ccidg_alloc); + +MODULE_DESCRIPTION("USB CCID Gadget driver"); +MODULE_AUTHOR("Marcus Folkesson "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/function/f_ccid.h b/drivers/usb/gadget/function/f_ccid.h new file mode 100644 index 000000000000..9dc182d1f819 --- /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; + __u32 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; + __u32 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; + __u16 bcdCCID; + __u8 bMaxSlotIndex; + __u8 bVoltageSupport; + __u32 dwProtocols; + __u32 dwDefaultClock; + __u32 dwMaximumClock; + __u8 bNumClockSupported; + __u32 dwDataRate; + __u32 dwMaxDataRate; + __u8 bNumDataRatesSupported; + __u32 dwMaxIFSD; + __u32 dwSynchProtocols; + __u32 dwMechanical; + __u32 dwFeatures; + __u32 dwMaxCCIDMessageLength; + __u8 bClassGetResponse; + __u8 bClassEnvelope; + __u16 wLcdLayout; + __u8 bPINSupport; + __u8 bMaxCCIDBusySlots; +} __packed; + + +#endif diff --git a/include/uapi/linux/usb/ccid.h b/include/uapi/linux/usb/ccid.h new file mode 100644 index 000000000000..517897201563 --- /dev/null +++ b/include/uapi/linux/usb/ccid.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Marcus Folkesson + * + * This file holds USB constants defined by the CCID Specification. + */ + +#ifndef CCID_H +#define CCID_H + +/* Slot error register when bmCommandStatus = 1 */ +#define CCID_CMD_ABORTED 0xFF +#define CCID_ICC_MUTE 0xFE +#define CCID_XFR_PARITY_ERROR 0xFD +#define CCID_XFR_OVERRUN 0xFC +#define CCID_HW_ERROR 0xFB +#define CCID_BAD_ATR_TS 0xF8 +#define CCID_BAD_ATR_TCK 0xF7 +#define CCID_ICC_PROTOCOL_NOT_SUPPORTED 0xF6 +#define CCID_ICC_CLASS_NOT_SUPPORTED 0xF5 +#define CCID_PROCEDURE_BYTE_CONFLICT 0xF4 +#define CCID_DEACTIVATED_PROTOCOL 0xF3 +#define CCID_BUSY_WITH_AUTO_SEQUENCE 0xF2 +#define CCID_PIN_TIMEOUT 0xF0 +#define CCID_PIN_CANCELLED 0xEF +#define CCID_CMD_SLOT_BUSY 0xE0 + +/* PC to RDR messages (bulk out) */ +#define CCID_PC_TO_RDR_ICCPOWERON 0x62 +#define CCID_PC_TO_RDR_ICCPOWEROFF 0x63 +#define CCID_PC_TO_RDR_GETSLOTSTATUS 0x65 +#define CCID_PC_TO_RDR_XFRBLOCK 0x6F +#define CCID_PC_TO_RDR_GETPARAMETERS 0x6C +#define CCID_PC_TO_RDR_RESETPARAMETERS 0x6D +#define CCID_PC_TO_RDR_SETPARAMETERS 0x61 +#define CCID_PC_TO_RDR_ESCAPE 0x6B +#define CCID_PC_TO_RDR_ICCCLOCK 0x6E +#define CCID_PC_TO_RDR_T0APDU 0x6A +#define CCID_PC_TO_RDR_SECURE 0x69 +#define CCID_PC_TO_RDR_MECHANICAL 0x71 +#define CCID_PC_TO_RDR_ABORT 0x72 +#define CCID_PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY 0x73 + +/* RDR to PC messages (bulk in) */ +#define CCID_RDR_TO_PC_DATABLOCK 0x80 +#define CCID_RDR_TO_PC_SLOTSTATUS 0x81 +#define CCID_RDR_TO_PC_PARAMETERS 0x82 +#define CCID_RDR_TO_PC_ESCAPE 0x83 +#define CCID_RDR_TO_PC_DATARATEANDCLOCKFREQUENCY 0x84 + +/* Class Features */ + +/* No special characteristics */ +#define CCID_FEATURES_NADA 0x00000000 +/* Automatic parameter configuration based on ATR data */ +#define CCID_FEATURES_AUTO_PCONF 0x00000002 +/* Automatic activation of ICC on inserting */ +#define CCID_FEATURES_AUTO_ACTIV 0x00000004 +/* Automatic ICC voltage selection */ +#define CCID_FEATURES_AUTO_VOLT 0x00000008 +/* Automatic ICC clock frequency change */ +#define CCID_FEATURES_AUTO_CLOCK 0x00000010 +/* Automatic baud rate change */ +#define CCID_FEATURES_AUTO_BAUD 0x00000020 +/*Automatic parameters negotiation made by the CCID */ +#define CCID_FEATURES_AUTO_PNEGO 0x00000040 +/* Automatic PPS made by the CCID according to the active parameters */ +#define CCID_FEATURES_AUTO_PPS 0x00000080 +/* CCID can set ICC in clock stop mode */ +#define CCID_FEATURES_ICCSTOP 0x00000100 +/* NAD value other than 00 accepted (T=1 protocol in use) */ +#define CCID_FEATURES_NAD 0x00000200 +/* Automatic IFSD exchange as first exchange (T=1 protocol in use) */ +#define CCID_FEATURES_AUTO_IFSD 0x00000400 +/* TPDU level exchanges with CCID */ +#define CCID_FEATURES_EXC_TPDU 0x00010000 +/* Short APDU level exchange with CCID */ +#define CCID_FEATURES_EXC_SAPDU 0x00020000 +/* Short and Extended APDU level exchange with CCID */ +#define CCID_FEATURES_EXC_APDU 0x00040000 +/* USB Wake up signaling supported on card insertion and removal */ +#define CCID_FEATURES_WAKEUP 0x00100000 + +/* Supported protocols */ +#define CCID_PROTOCOL_NOT_SEL 0x00 +#define CCID_PROTOCOL_T0 0x01 +#define CCID_PROTOCOL_T1 0x02 + +#define CCID_PINSUPOORT_NONE 0x00 +#define CCID_PINSUPOORT_VERIFICATION (1 << 1) +#define CCID_PINSUPOORT_MODIFICATION (1 << 2) + +#endif -- 2.16.2