From patchwork Thu Nov 10 08:00:06 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Albert Wang X-Patchwork-Id: 18004 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:6687:0:0:0:0:0 with SMTP id l7csp11274wru; Thu, 10 Nov 2022 00:08:46 -0800 (PST) X-Google-Smtp-Source: AMsMyM6qQnCbQdkWQVAwSI/GhXW9t1XCzR/XzzO/3p1mp0WJlcZ9Ze4RHk/rUFAJmgQRLRQ+4pug X-Received: by 2002:a17:906:a17:b0:78d:9d06:c7cc with SMTP id w23-20020a1709060a1700b0078d9d06c7ccmr2431614ejf.154.1668067725820; Thu, 10 Nov 2022 00:08:45 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1668067725; cv=none; d=google.com; s=arc-20160816; b=M+TVQNPuTM4Fn63fRMKmOx5w1ZpuWXEIPCCLbPn8Duj6XYV8dzZdrNLpUWEpqD7VRj aqXBSdhcAzt86sFBT5jUOzJj1Gpf28ROT3C+idfoP21euF4Sfnx+V/Bm83Tj8YWXZRne TqMgftKQi5CXr1uNNLvhz25hsjdTOPeD+iCkXS6d9YZ9JYlybZ/nm83gJT9niqAhMcqf JHuV2gmbXMPcMbvAZXAudLUPVzkU5PnAAgNcchtN5npt7u6wRe5EfBn858dOxDxLRfgz nezHwMH1owVDKqluNfeIVx0srKT8g2cFe7EbXzV6sKEWfWIpMdMujWOzUYVY2Hr6JozC CT5A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:from:subject:references:mime-version :message-id:in-reply-to:date:dkim-signature; bh=5kJlT7ntgoYuCv4/yBaIZRIBzCyj+KBksQs6ck5uymo=; b=n5WDr1HT8/ED5KAdUVXL5e+N64glDR+7hjSmRlUwMEaHE7LEM7wDkMrHFTl3k5nZFa /LWQ3JjfNGI8/jDu/hZE6zBSUON5Hdjh/JxJwKB95L4qIrMDisJlDn7vBcu6ydkkEFyR WSywxB1BB/qPEtXIDL172nV3qGn793NhWBxwnH/bVtssp1YTZx8BeVWPH+LEI5LdSR7x q7141pGFWlv8RzcLKvgZGF9UmmrNCYYNOrURCEBLg1B/S0ZyvzD9NhHp4YGQ/78ZqEwG WXyKcLVyDwP6ldHNjEXKYW0l3oYqE6RYD5zvxmwUr78SxPKcHV2UkWa2UnImB9pXFqB1 85mQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20210112 header.b=fDaFqsnC; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id hq42-20020a1709073f2a00b007ae17fd2f19si18231298ejc.734.2022.11.10.00.08.03; Thu, 10 Nov 2022 00:08:45 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20210112 header.b=fDaFqsnC; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232766AbiKJIBN (ORCPT + 99 others); Thu, 10 Nov 2022 03:01:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34184 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232920AbiKJIBG (ORCPT ); Thu, 10 Nov 2022 03:01:06 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5CD251C41F for ; Thu, 10 Nov 2022 00:01:05 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id f126-20020a255184000000b006cb2aebd124so1153775ybb.11 for ; Thu, 10 Nov 2022 00:01:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:references:mime-version:message-id:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=5kJlT7ntgoYuCv4/yBaIZRIBzCyj+KBksQs6ck5uymo=; b=fDaFqsnCmWfOp2sCyDZ2LXluglt+9BGNMH4fv3LPb752IiaQvNyMynq5/u3trO/Smi tfXaDJ2X6KIcnFJ2Nu2wcLSWATosEnrNDsJM5GX8w+tzy2GF//YXWHuG6ADTj3O+e69h /LqgVmJX3+Gix6KAlje0eQ2G6CZM9GYxpqTWzpduHnEmaCKcVcBH0Ne0bSd79d2y7xn1 yLhJxBn2FdrKj/jDI9l2z1W0pQibOQf936dhCJ9hxtVdTe8XJcHa40jJPp2HiCpuUiiF jUZTY1uh8yLyx65sSHOjvCw0Oo936YiuN2Hmm8cBN+QMZ2CgXiUIF/VxGlYmihlUgsyD SBGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:references:mime-version:message-id:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=5kJlT7ntgoYuCv4/yBaIZRIBzCyj+KBksQs6ck5uymo=; b=DmEg9yOykQnJlUmkCERef5jAPKD6We11kLjXgVH8pkYIExHLE2aDjV1dZb23hF4n9G 1OoJHoFWy+fAv3JV/9NU7oK6y4vIy51YKo98MUntXUHw86ltPczrcoy8tjZtlLK7Z0KT 9L8bCx34hg4kUGbzDqfUMrzasAjRl7LwYLH/K0KYK1HvLQzLNlFHAf77cX07k1gkS/Vw E2EgL6OkblpaG1pJtub4fsNz8Juqyry9dGX7ABtPke81FLvDlIafvc5eUGVRljfVs/1D REDO7XMexQ/stDN4sf8cYbGPUp5lp2uT7iuGbR8SooppQou603OAY85b+C2Wa6SU1HYE N3mg== X-Gm-Message-State: ACrzQf2rFk34VPgVvnB/MUqBrd3wTwsUiyQ4w2AG72LfXR5dx9oa6ra+ zfW/NfTVApsgXJIM7Ih345hi4Cc+YgXB1YTDJbM= X-Received: from albertccwang.ntc.corp.google.com ([2401:fa00:fc:202:2c6f:5c28:5579:9e27]) (user=albertccwang job=sendgmr) by 2002:a05:690c:601:b0:36b:b6f3:f0f4 with SMTP id bq1-20020a05690c060100b0036bb6f3f0f4mr1194837ywb.116.1668067264475; Thu, 10 Nov 2022 00:01:04 -0800 (PST) Date: Thu, 10 Nov 2022 16:00:06 +0800 In-Reply-To: <20221110080006.3563429-1-albertccwang@google.com> Message-Id: <20221110080006.3563429-4-albertccwang@google.com> Mime-Version: 1.0 References: <20221110080006.3563429-1-albertccwang@google.com> X-Mailer: git-send-email 2.38.1.431.g37b22c650d-goog Subject: [PATCH v2 3/3] usb: host: add the xhci offload hooks implementations From: Albert Wang To: mathias.nyman@intel.com, gregkh@linuxfoundation.org Cc: badhri@google.com, howardyen@google.com, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org, Albert Wang X-Spam-Status: No, score=-8.6 required=5.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,HK_RANDOM_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,USER_IN_DEF_DKIM_WL autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1749095783305126260?= X-GMAIL-MSGID: =?utf-8?q?1749095783305126260?= Add the offload hooks implementations which are used in the xHCI driver for vendor offload function, and some functions will call to co-processor driver for further offload operations. Signed-off-by: Albert Wang Signed-off-by: Howard Yen --- Changes in v2: - New in v2 drivers/usb/host/xhci-offload-impl.c | 492 +++++++++++++++++++++++++++ 1 file changed, 492 insertions(+) create mode 100644 drivers/usb/host/xhci-offload-impl.c diff --git a/drivers/usb/host/xhci-offload-impl.c b/drivers/usb/host/xhci-offload-impl.c new file mode 100644 index 000000000000..90e546d63fbe --- /dev/null +++ b/drivers/usb/host/xhci-offload-impl.c @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Google Corp. + * + * Author: + * Howard.Yen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xhci.h" +#include "xhci-plat.h" + +enum usb_offload_op_mode { + USB_OFFLOAD_STOP, + USB_OFFLOAD_DRAM +}; + +enum usb_state { + USB_DISCONNECTED, + USB_CONNECTED +}; + +enum usb_offload_msg { + SET_DCBAA_PTR, + SETUP_DONE, + SET_ISOC_TR_INFO, + SYNC_CONN_STAT, + SET_OFFLOAD_STATE +}; + +struct conn_stat_args { + u16 bus_id; + u16 dev_num; + u16 slot_id; + u32 conn_stat; +}; + +struct get_isoc_tr_info_args { + u16 ep_id; + u16 dir; + u32 type; + u32 num_segs; + u32 seg_ptr; + u32 max_packet; + u32 deq_ptr; + u32 enq_ptr; + u32 cycle_state; + u32 num_trbs_free; +}; + +struct xhci_offload_data { + struct xhci_hcd *xhci; + + bool usb_accessory_enabled; + bool usb_audio_offload; + bool dt_direct_usb_access; + bool offload_state; + + enum usb_offload_op_mode op_mode; +}; + +static struct xhci_offload_data *offload_data; +struct xhci_offload_data *xhci_get_offload_data(void) +{ + return offload_data; +} + +/* + * Determine if an USB device is a compatible devices: + * True: Devices are audio class and they contain ISOC endpoint + * False: Devices are not audio class or they're audio class but no ISOC endpoint or + * they have at least one interface is video class + */ +static bool is_compatible_with_usb_audio_offload(struct usb_device *udev) +{ + struct usb_endpoint_descriptor *epd; + struct usb_host_config *config; + struct usb_host_interface *alt; + struct usb_interface_cache *intfc; + int i, j, k; + bool is_audio = false; + + config = udev->config; + for (i = 0; i < config->desc.bNumInterfaces; i++) { + intfc = config->intf_cache[i]; + for (j = 0; j < intfc->num_altsetting; j++) { + alt = &intfc->altsetting[j]; + + if (alt->desc.bInterfaceClass == USB_CLASS_VIDEO) { + is_audio = false; + goto out; + } + + if (alt->desc.bInterfaceClass == USB_CLASS_AUDIO) { + for (k = 0; k < alt->desc.bNumEndpoints; k++) { + epd = &alt->endpoint[k].desc; + if (usb_endpoint_xfer_isoc(epd)) { + is_audio = true; + break; + } + } + } + } + } + +out: + return is_audio; +} + +/* + * check the usb device including the video class: + * True: Devices contain video class + * False: Device doesn't contain video class + */ +static bool is_usb_video_device(struct usb_device *udev) +{ + struct usb_host_config *config; + struct usb_host_interface *alt; + struct usb_interface_cache *intfc; + int i, j; + bool is_video = false; + + if (!udev || !udev->config) + return is_video; + + config = udev->config; + + for (i = 0; i < config->desc.bNumInterfaces; i++) { + intfc = config->intf_cache[i]; + for (j = 0; j < intfc->num_altsetting; j++) { + alt = &intfc->altsetting[j]; + + if (alt->desc.bInterfaceClass == USB_CLASS_VIDEO) { + is_video = true; + goto out; + } + } + } + +out: + return is_video; +} + +/* + * This is the driver call to co-processor for offload operations. + */ +int offload_driver_call(enum usb_offload_msg msg, void *ptr) +{ + enum usb_offload_msg offload_msg; + void *argptr; + + offload_msg = msg; + argptr = ptr; + + return 0; +} + +static int xhci_sync_conn_stat(unsigned int bus_id, unsigned int dev_num, unsigned int slot_id, + unsigned int conn_stat) +{ + struct conn_stat_args conn_args; + + conn_args.bus_id = bus_id; + conn_args.dev_num = dev_num; + conn_args.slot_id = slot_id; + conn_args.conn_stat = conn_stat; + + return offload_driver_call(SYNC_CONN_STAT, &conn_args); +} + +static int usb_host_mode_state_notify(enum usb_state usb_state) +{ + return xhci_sync_conn_stat(0, 0, 0, usb_state); +} + +static int xhci_udev_notify(struct notifier_block *self, unsigned long action, + void *dev) +{ + struct usb_device *udev = dev; + struct xhci_offload_data *offload_data = xhci_get_offload_data(); + + switch (action) { + case USB_DEVICE_ADD: + if (is_compatible_with_usb_audio_offload(udev)) { + dev_dbg(&udev->dev, "Compatible with usb audio offload\n"); + if (offload_data->op_mode == USB_OFFLOAD_DRAM) { + xhci_sync_conn_stat(udev->bus->busnum, udev->devnum, udev->slot_id, + USB_CONNECTED); + } + } + offload_data->usb_accessory_enabled = false; + break; + case USB_DEVICE_REMOVE: + if (is_compatible_with_usb_audio_offload(udev) && + (offload_data->op_mode == USB_OFFLOAD_DRAM)) { + xhci_sync_conn_stat(udev->bus->busnum, udev->devnum, udev->slot_id, + USB_DISCONNECTED); + } + offload_data->usb_accessory_enabled = false; + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block xhci_udev_nb = { + .notifier_call = xhci_udev_notify, +}; + +static int usb_audio_offload_init(struct xhci_hcd *xhci) +{ + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct xhci_offload_data *offload_data = xhci_get_offload_data(); + int ret; + u32 out_val; + + offload_data = kzalloc(sizeof(struct xhci_offload_data), GFP_KERNEL); + if (!offload_data) + return -ENOMEM; + + if (!of_property_read_u32(dev->of_node, "offload", &out_val)) + offload_data->usb_audio_offload = (out_val == 1) ? true : false; + + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_err(dev, "Could not get reserved memory\n"); + kfree(offload_data); + return ret; + } + + offload_data->dt_direct_usb_access = + of_property_read_bool(dev->of_node, "direct-usb-access") ? true : false; + if (!offload_data->dt_direct_usb_access) + dev_warn(dev, "Direct USB access is not supported\n"); + + offload_data->offload_state = true; + + usb_register_notify(&xhci_udev_nb); + offload_data->op_mode = USB_OFFLOAD_DRAM; + offload_data->xhci = xhci; + + return 0; +} + +static void usb_audio_offload_cleanup(struct xhci_hcd *xhci) +{ + struct xhci_offload_data *offload_data = xhci_get_offload_data(); + + offload_data->usb_audio_offload = false; + offload_data->op_mode = USB_OFFLOAD_STOP; + offload_data->xhci = NULL; + + usb_unregister_notify(&xhci_udev_nb); + + /* Notification for xhci driver removing */ + usb_host_mode_state_notify(USB_DISCONNECTED); + + kfree(offload_data); + offload_data = NULL; +} + +static bool is_offload_enabled(struct xhci_hcd *xhci, + struct xhci_virt_device *vdev, unsigned int ep_index) +{ + struct usb_device *udev; + struct xhci_offload_data *offload_data = xhci_get_offload_data(); + bool global_enabled = offload_data->op_mode != USB_OFFLOAD_STOP; + struct xhci_ring *ep_ring; + + if (vdev == NULL || vdev->eps[ep_index].ring == NULL) + return global_enabled; + + udev = vdev->udev; + + if (global_enabled) { + ep_ring = vdev->eps[ep_index].ring; + if (offload_data->op_mode == USB_OFFLOAD_DRAM) { + if (is_usb_video_device(udev)) + return false; + else if (ep_ring->type == TYPE_ISOC) + return offload_data->offload_state; + } + } + + return false; +} + +static bool is_usb_bulk_transfer_enabled(struct xhci_hcd *xhci, struct urb *urb) +{ + struct xhci_offload_data *offload_data = xhci_get_offload_data(); + struct usb_endpoint_descriptor *desc = &urb->ep->desc; + int ep_type = usb_endpoint_type(desc); + struct usb_ctrlrequest *cmd; + bool skip_bulk = false; + + cmd = (struct usb_ctrlrequest *) urb->setup_packet; + + if (ep_type == USB_ENDPOINT_XFER_CONTROL) { + if (!usb_endpoint_dir_in(desc) && cmd->bRequest == 0x35) + offload_data->usb_accessory_enabled = true; + else + offload_data->usb_accessory_enabled = false; + } + + if (ep_type == USB_ENDPOINT_XFER_BULK && !usb_endpoint_dir_in(desc)) + skip_bulk = offload_data->usb_accessory_enabled; + + return skip_bulk; +} + +static int xhci_set_dcbaa_ptr(u64 dcbaa_ptr) +{ + return offload_driver_call(SET_DCBAA_PTR, &dcbaa_ptr); +} + +static int xhci_setup_done(void) +{ + return offload_driver_call(SETUP_DONE, NULL); +} + +static void alloc_dcbaa(struct xhci_hcd *xhci, gfp_t flags) +{ + dma_addr_t dma; + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct xhci_offload_data *offload_data = xhci_get_offload_data(); + + if (offload_data->op_mode == USB_OFFLOAD_DRAM) { + xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), + &dma, flags); + if (!xhci->dcbaa) + return; + + xhci->dcbaa->dma = dma; + if (xhci_set_dcbaa_ptr(xhci->dcbaa->dma) != 0) { + xhci_err(xhci, "Set DCBAA pointer failed\n"); + xhci->dcbaa = NULL; + return; + } + xhci_setup_done(); + + xhci_dbg(xhci, "Set dcbaa_ptr=%llx to AoC\n", xhci->dcbaa->dma); + } else { + xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), + &dma, flags); + if (!xhci->dcbaa) + return; + + xhci->dcbaa->dma = dma; + } +} + +static void free_dcbaa(struct xhci_hcd *xhci) +{ + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + + if (!xhci->dcbaa) + return; + + dma_free_coherent(dev, sizeof(*xhci->dcbaa), + xhci->dcbaa, xhci->dcbaa->dma); + + xhci->dcbaa = NULL; +} + +static int xhci_set_isoc_tr_info(u16 ep_id, u16 dir, struct xhci_ring *ep_ring) +{ + struct get_isoc_tr_info_args tr_info; + + tr_info.ep_id = ep_id; + tr_info.dir = dir; + tr_info.num_segs = ep_ring->num_segs; + tr_info.max_packet = ep_ring->bounce_buf_len; + tr_info.type = ep_ring->type; + tr_info.seg_ptr = ep_ring->first_seg->dma; + tr_info.cycle_state = ep_ring->cycle_state; + tr_info.num_trbs_free = ep_ring->num_trbs_free; + + return offload_driver_call(SET_ISOC_TR_INFO, &tr_info); +} + +static struct xhci_ring *alloc_transfer_ring(struct xhci_hcd *xhci, + u32 endpoint_type, enum xhci_ring_type ring_type, + unsigned int max_packet, gfp_t mem_flags) +{ + struct xhci_ring *ep_ring; + u16 dir; + + ep_ring = xhci_ring_alloc(xhci, 1, 1, ring_type, max_packet, mem_flags); + dir = endpoint_type == ISOC_IN_EP ? 0 : 1; + + xhci_set_isoc_tr_info(0, dir, ep_ring); + + return ep_ring; +} + +static void free_transfer_ring(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, + unsigned int ep_index) +{ + struct xhci_ring *ring, *new_ring; + struct xhci_ep_ctx *ep_ctx; + struct xhci_input_control_ctx *ctrl_ctx; + u32 ep_type; + u32 ep_is_added, ep_is_dropped; + + ring = virt_dev->eps[ep_index].ring; + new_ring = virt_dev->eps[ep_index].new_ring; + ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->out_ctx, ep_index); + ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2)); + + ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx); + if (!ctrl_ctx) { + xhci_warn(xhci, "%s: Could not get input context, bad type.\n", __func__); + return; + } + ep_is_added = EP_IS_ADDED(ctrl_ctx, ep_index); + ep_is_dropped = EP_IS_DROPPED(ctrl_ctx, ep_index); + + xhci_dbg(xhci, "%s: ep %u is added(0x%x), is dropped(0x%x)\n", __func__, ep_index, + ep_is_added, ep_is_dropped); + + if (ring) { + xhci_dbg(xhci, "%s: ep_index=%u, ep_type=%u, ring type=%u, new_ring=%pK\n", + __func__, ep_index, ep_type, ring->type, new_ring); + + xhci_ring_free(xhci, virt_dev->eps[ep_index].ring); + + virt_dev->eps[ep_index].ring = NULL; + + if (ep_is_added == 0 && ep_is_dropped == 0) + return; + } + + if (new_ring) { + xhci_dbg(xhci, "%s: ep_index=%u, ep_type=%u, new_ring type=%u\n", __func__, + ep_index, ep_type, new_ring->type); + + xhci_ring_free(xhci, virt_dev->eps[ep_index].new_ring); + + virt_dev->eps[ep_index].new_ring = NULL; + + return; + } +} + +static bool offload_skip_urb(struct xhci_hcd *xhci, struct urb *urb) +{ + struct xhci_virt_device *vdev = xhci->devs[urb->dev->slot_id]; + struct usb_endpoint_descriptor *desc = &urb->ep->desc; + int ep_type = usb_endpoint_type(desc); + unsigned int ep_index; + + if (ep_type == USB_ENDPOINT_XFER_CONTROL) + ep_index = (unsigned int)(usb_endpoint_num(desc)*2); + else + ep_index = (unsigned int)(usb_endpoint_num(desc)*2) + + (usb_endpoint_dir_in(desc) ? 1 : 0) - 1; + + xhci_dbg(xhci, "%s: ep_index=%u, ep_type=%d\n", __func__, ep_index, ep_type); + + if (is_offload_enabled(xhci, vdev, ep_index)) + return true; + + if (is_usb_bulk_transfer_enabled(xhci, urb)) + return true; + + return false; +} + +static struct xhci_offload_ops offload_ops = { + .offload_init = usb_audio_offload_init, + .offload_cleanup = usb_audio_offload_cleanup, + .is_offload_enabled = is_offload_enabled, + .alloc_dcbaa = alloc_dcbaa, + .free_dcbaa = free_dcbaa, + .alloc_transfer_ring = alloc_transfer_ring, + .free_transfer_ring = free_transfer_ring, + .usb_offload_skip_urb = offload_skip_urb, +}; + +int xhci_offload_helper_init(void) +{ + return xhci_plat_register_offload_ops(&offload_ops); +}