From patchwork Wed Oct 13 07:52:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?SsO8cmdlbiBHcm/Dnw==?= X-Patchwork-Id: 12555023 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 91828C433F5 for ; Wed, 13 Oct 2021 07:52:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 722B960241 for ; Wed, 13 Oct 2021 07:52:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238812AbhJMHyS (ORCPT ); Wed, 13 Oct 2021 03:54:18 -0400 Received: from smtp-out1.suse.de ([195.135.220.28]:54626 "EHLO smtp-out1.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238531AbhJMHyR (ORCPT ); Wed, 13 Oct 2021 03:54:17 -0400 Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 7D72022243; Wed, 13 Oct 2021 07:52:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1634111533; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=zse2pNf3Nqq9ce0C5luvO4nVc/A6VAHCPFmY1kxIMr0=; b=tMfFTMvJgUugjH9Ns8NG0AXFOvMcxTfx0U50De6R5WNza2GxCp+a5sLuG2KGjaAy1m1GGk cqCr5uxOi3Gf6FLFTXrUPEnoNwB89CREhIz9uSm/v0s3hMZ9etlpTFvXUf0/AR+aeMLkpT 2oiR5LvXs8BEJT2mnALuE1NfRKWmzPM= Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id 47B8613CBE; Wed, 13 Oct 2021 07:52:13 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id yOhSEC2QZmEjfwAAMHmgww (envelope-from ); Wed, 13 Oct 2021 07:52:13 +0000 From: Juergen Gross To: linux-usb@vger.kernel.org, xen-devel@lists.xenproject.org, linux-kernel@vger.kernel.org Cc: Juergen Gross , Boris Ostrovsky , Stefano Stabellini Subject: [PATCH v6 1/3] usb: Add Xen pvUSB protocol description Date: Wed, 13 Oct 2021 09:52:05 +0200 Message-Id: <20211013075207.13910-2-jgross@suse.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20211013075207.13910-1-jgross@suse.com> References: <20211013075207.13910-1-jgross@suse.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Add the definition of pvUSB protocol used between the pvUSB frontend in a Xen domU and the pvUSB backend in a Xen driver domain (usually Dom0). This header was originally provided by Fujitsu for Xen based on Linux 2.6.18. Changes are: - adapt to Linux kernel style guide - use Xen namespace - add lots of comments - don't use kernel internal defines Signed-off-by: Juergen Gross Reviewed-by: Boris Ostrovsky --- include/xen/interface/io/usbif.h | 421 +++++++++++++++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 include/xen/interface/io/usbif.h diff --git a/include/xen/interface/io/usbif.h b/include/xen/interface/io/usbif.h new file mode 100644 index 000000000000..9494b1c9be99 --- /dev/null +++ b/include/xen/interface/io/usbif.h @@ -0,0 +1,421 @@ +/* + * usbif.h + * + * USB I/O interface for Xen guest OSes. + * + * Copyright (C) 2009, FUJITSU LABORATORIES LTD. + * Author: Noboru Iwamatsu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef __XEN_PUBLIC_IO_USBIF_H__ +#define __XEN_PUBLIC_IO_USBIF_H__ + +#include "ring.h" +#include "../grant_table.h" + +/* + * Detailed Interface Description + * ============================== + * The pvUSB interface is using a split driver design: a frontend driver in + * the guest and a backend driver in a driver domain (normally dom0) having + * access to the physical USB device(s) being passed to the guest. + * + * The frontend and backend drivers use XenStore to initiate the connection + * between them, the I/O activity is handled via two shared ring pages and an + * event channel. As the interface between frontend and backend is at the USB + * host connector level, multiple (up to 31) physical USB devices can be + * handled by a single connection. + * + * The Xen pvUSB device name is "qusb", so the frontend's XenStore entries are + * to be found under "device/qusb", while the backend's XenStore entries are + * under "backend//qusb". + * + * When a new pvUSB connection is established, the frontend needs to setup the + * two shared ring pages for communication and the event channel. The ring + * pages need to be made available to the backend via the grant table + * interface. + * + * One of the shared ring pages is used by the backend to inform the frontend + * about USB device plug events (device to be added or removed). This is the + * "conn-ring". + * + * The other ring page is used for USB I/O communication (requests and + * responses). This is the "urb-ring". + * + * Feature and Parameter Negotiation + * ================================= + * The two halves of a Xen pvUSB driver utilize nodes within the XenStore to + * communicate capabilities and to negotiate operating parameters. This + * section enumerates these nodes which reside in the respective front and + * backend portions of the XenStore, following the XenBus convention. + * + * Any specified default value is in effect if the corresponding XenBus node + * is not present in the XenStore. + * + * XenStore nodes in sections marked "PRIVATE" are solely for use by the + * driver side whose XenBus tree contains them. + * + ***************************************************************************** + * Backend XenBus Nodes + ***************************************************************************** + * + *------------------ Backend Device Identification (PRIVATE) ------------------ + * + * num-ports + * Values: unsigned [1...31] + * + * Number of ports for this (virtual) USB host connector. + * + * usb-ver + * Values: unsigned [1...2] + * + * USB version of this host connector: 1 = USB 1.1, 2 = USB 2.0. + * + * port/[1...31] + * Values: string + * + * Physical USB device connected to the given port, e.g. "3-1.5". + * + ***************************************************************************** + * Frontend XenBus Nodes + ***************************************************************************** + * + *----------------------- Request Transport Parameters ----------------------- + * + * event-channel + * Values: unsigned + * + * The identifier of the Xen event channel used to signal activity + * in the ring buffer. + * + * urb-ring-ref + * Values: unsigned + * + * The Xen grant reference granting permission for the backend to map + * the sole page in a single page sized ring buffer. This is the ring + * buffer for urb requests. + * + * conn-ring-ref + * Values: unsigned + * + * The Xen grant reference granting permission for the backend to map + * the sole page in a single page sized ring buffer. This is the ring + * buffer for connection/disconnection requests. + * + * protocol + * Values: string (XEN_IO_PROTO_ABI_*) + * Default Value: XEN_IO_PROTO_ABI_NATIVE + * + * The machine ABI rules governing the format of all ring request and + * response structures. + * + * Protocol Description + * ==================== + * + *-------------------------- USB device plug events -------------------------- + * + * USB device plug events are send via the "conn-ring" shared page. As only + * events are being sent, the respective requests from the frontend to the + * backend are just dummy ones. + * The events sent to the frontend have the following layout: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | id | portnum | speed | 4 + * +----------------+----------------+----------------+----------------+ + * id - uint16_t, event id (taken from the actual frontend dummy request) + * portnum - uint8_t, port number (1 ... 31) + * speed - uint8_t, device XENUSB_SPEED_*, XENUSB_SPEED_NONE == unplug + * + * The dummy request: + * 0 1 octet + * +----------------+----------------+ + * | id | 2 + * +----------------+----------------+ + * id - uint16_t, guest supplied value (no need for being unique) + * + *-------------------------- USB I/O request --------------------------------- + * + * A single USB I/O request on the "urb-ring" has the following layout: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | id | nr_buffer_segs | 4 + * +----------------+----------------+----------------+----------------+ + * | pipe | 8 + * +----------------+----------------+----------------+----------------+ + * | transfer_flags | buffer_length | 12 + * +----------------+----------------+----------------+----------------+ + * | request type specific | 16 + * | data | 20 + * +----------------+----------------+----------------+----------------+ + * | seg[0] | 24 + * | data | 28 + * +----------------+----------------+----------------+----------------+ + * |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/| + * +----------------+----------------+----------------+----------------+ + * | seg[XENUSB_MAX_SEGMENTS_PER_REQUEST - 1] | 144 + * | data | 148 + * +----------------+----------------+----------------+----------------+ + * Bit field bit number 0 is always least significant bit, undefined bits must + * be zero. + * id - uint16_t, guest supplied value + * nr_buffer_segs - uint16_t, number of segment entries in seg[] array + * pipe - uint32_t, bit field with multiple information: + * bits 0-4: port request to send to + * bit 5: unlink request with specified id (cancel I/O) if set (see below) + * bit 7: direction (1 = read from device) + * bits 8-14: device number on port + * bits 15-18: endpoint of device + * bits 30-31: request type: 00 = isochronous, 01 = interrupt, + * 10 = control, 11 = bulk + * transfer_flags - uint16_t, bit field with processing flags: + * bit 0: less data than specified allowed + * buffer_length - uint16_t, total length of data + * request type specific data - 8 bytes, see below + * seg[] - array with 8 byte elements, see below + * + * Request type specific data for isochronous request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | interval | start_frame | 4 + * +----------------+----------------+----------------+----------------+ + * | number_of_packets | nr_frame_desc_segs | 8 + * +----------------+----------------+----------------+----------------+ + * interval - uint16_t, time interval in msecs between frames + * start_frame - uint16_t, start frame number + * number_of_packets - uint16_t, number of packets to transfer + * nr_frame_desc_segs - uint16_t number of seg[] frame descriptors elements + * + * Request type specific data for interrupt request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | interval | 0 | 4 + * +----------------+----------------+----------------+----------------+ + * | 0 | 8 + * +----------------+----------------+----------------+----------------+ + * interval - uint16_t, time in msecs until interruption + * + * Request type specific data for control request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | data of setup packet | 4 + * | | 8 + * +----------------+----------------+----------------+----------------+ + * + * Request type specific data for bulk request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | 0 | 4 + * | 0 | 8 + * +----------------+----------------+----------------+----------------+ + * + * Request type specific data for unlink request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | unlink_id | 0 | 4 + * +----------------+----------------+----------------+----------------+ + * | 0 | 8 + * +----------------+----------------+----------------+----------------+ + * unlink_id - uint16_t, request id of request to terminate + * + * seg[] array element layout: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | gref | 4 + * +----------------+----------------+----------------+----------------+ + * | offset | length | 8 + * +----------------+----------------+----------------+----------------+ + * gref - uint32_t, grant reference of buffer page + * offset - uint16_t, offset of buffer start in page + * length - uint16_t, length of buffer in page + * + *-------------------------- USB I/O response -------------------------------- + * + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | id | start_frame | 4 + * +----------------+----------------+----------------+----------------+ + * | status | 8 + * +----------------+----------------+----------------+----------------+ + * | actual_length | 12 + * +----------------+----------------+----------------+----------------+ + * | error_count | 16 + * +----------------+----------------+----------------+----------------+ + * id - uint16_t, id of the request this response belongs to + * start_frame - uint16_t, start_frame this response (iso requests only) + * status - int32_t, XENUSB_STATUS_* (non-iso requests) + * actual_length - uint32_t, actual size of data transferred + * error_count - uint32_t, number of errors (iso requests) + */ + +enum xenusb_spec_version { + XENUSB_VER_UNKNOWN = 0, + XENUSB_VER_USB11, + XENUSB_VER_USB20, + XENUSB_VER_USB30, /* not supported yet */ +}; + +/* + * USB pipe in xenusb_request + * + * - port number: bits 0-4 + * (USB_MAXCHILDREN is 31) + * + * - operation flag: bit 5 + * (0 = submit urb, + * 1 = unlink urb) + * + * - direction: bit 7 + * (0 = Host-to-Device [Out] + * 1 = Device-to-Host [In]) + * + * - device address: bits 8-14 + * + * - endpoint: bits 15-18 + * + * - pipe type: bits 30-31 + * (00 = isochronous, 01 = interrupt, + * 10 = control, 11 = bulk) + */ + +#define XENUSB_PIPE_PORT_MASK 0x0000001f +#define XENUSB_PIPE_UNLINK 0x00000020 +#define XENUSB_PIPE_DIR 0x00000080 +#define XENUSB_PIPE_DEV_MASK 0x0000007f +#define XENUSB_PIPE_DEV_SHIFT 8 +#define XENUSB_PIPE_EP_MASK 0x0000000f +#define XENUSB_PIPE_EP_SHIFT 15 +#define XENUSB_PIPE_TYPE_MASK 0x00000003 +#define XENUSB_PIPE_TYPE_SHIFT 30 +#define XENUSB_PIPE_TYPE_ISOC 0 +#define XENUSB_PIPE_TYPE_INT 1 +#define XENUSB_PIPE_TYPE_CTRL 2 +#define XENUSB_PIPE_TYPE_BULK 3 + +#define xenusb_pipeportnum(pipe) ((pipe) & XENUSB_PIPE_PORT_MASK) +#define xenusb_setportnum_pipe(pipe, portnum) ((pipe) | (portnum)) + +#define xenusb_pipeunlink(pipe) ((pipe) & XENUSB_PIPE_UNLINK) +#define xenusb_pipesubmit(pipe) (!xenusb_pipeunlink(pipe)) +#define xenusb_setunlink_pipe(pipe) ((pipe) | XENUSB_PIPE_UNLINK) + +#define xenusb_pipein(pipe) ((pipe) & XENUSB_PIPE_DIR) +#define xenusb_pipeout(pipe) (!xenusb_pipein(pipe)) + +#define xenusb_pipedevice(pipe) \ + (((pipe) >> XENUSB_PIPE_DEV_SHIFT) & XENUSB_PIPE_DEV_MASK) + +#define xenusb_pipeendpoint(pipe) \ + (((pipe) >> XENUSB_PIPE_EP_SHIFT) & XENUSB_PIPE_EP_MASK) + +#define xenusb_pipetype(pipe) \ + (((pipe) >> XENUSB_PIPE_TYPE_SHIFT) & XENUSB_PIPE_TYPE_MASK) +#define xenusb_pipeisoc(pipe) (xenusb_pipetype(pipe) == XENUSB_PIPE_TYPE_ISOC) +#define xenusb_pipeint(pipe) (xenusb_pipetype(pipe) == XENUSB_PIPE_TYPE_INT) +#define xenusb_pipectrl(pipe) (xenusb_pipetype(pipe) == XENUSB_PIPE_TYPE_CTRL) +#define xenusb_pipebulk(pipe) (xenusb_pipetype(pipe) == XENUSB_PIPE_TYPE_BULK) + +#define XENUSB_MAX_SEGMENTS_PER_REQUEST (16) +#define XENUSB_MAX_PORTNR 31 +#define XENUSB_RING_SIZE 4096 + +/* + * RING for transferring urbs. + */ +struct xenusb_request_segment { + grant_ref_t gref; + uint16_t offset; + uint16_t length; +}; + +struct xenusb_urb_request { + uint16_t id; /* request id */ + uint16_t nr_buffer_segs; /* number of urb->transfer_buffer segments */ + + /* basic urb parameter */ + uint32_t pipe; + uint16_t transfer_flags; +#define XENUSB_SHORT_NOT_OK 0x0001 + uint16_t buffer_length; + union { + uint8_t ctrl[8]; /* setup_packet (Ctrl) */ + + struct { + uint16_t interval; /* maximum (1024*8) in usb core */ + uint16_t start_frame; /* start frame */ + uint16_t number_of_packets; /* number of ISO packet */ + uint16_t nr_frame_desc_segs; /* number of iso_frame_desc segments */ + } isoc; + + struct { + uint16_t interval; /* maximum (1024*8) in usb core */ + uint16_t pad[3]; + } intr; + + struct { + uint16_t unlink_id; /* unlink request id */ + uint16_t pad[3]; + } unlink; + + } u; + + /* urb data segments */ + struct xenusb_request_segment seg[XENUSB_MAX_SEGMENTS_PER_REQUEST]; +}; + +struct xenusb_urb_response { + uint16_t id; /* request id */ + uint16_t start_frame; /* start frame (ISO) */ + int32_t status; /* status (non-ISO) */ +#define XENUSB_STATUS_OK 0 +#define XENUSB_STATUS_NODEV (-19) +#define XENUSB_STATUS_INVAL (-22) +#define XENUSB_STATUS_STALL (-32) +#define XENUSB_STATUS_IOERROR (-71) +#define XENUSB_STATUS_BABBLE (-75) +#define XENUSB_STATUS_SHUTDOWN (-108) + int32_t actual_length; /* actual transfer length */ + int32_t error_count; /* number of ISO errors */ +}; + +DEFINE_RING_TYPES(xenusb_urb, struct xenusb_urb_request, struct xenusb_urb_response); +#define XENUSB_URB_RING_SIZE __CONST_RING_SIZE(xenusb_urb, XENUSB_RING_SIZE) + +/* + * RING for notifying connect/disconnect events to frontend + */ +struct xenusb_conn_request { + uint16_t id; +}; + +struct xenusb_conn_response { + uint16_t id; /* request id */ + uint8_t portnum; /* port number */ + uint8_t speed; /* usb_device_speed */ +#define XENUSB_SPEED_NONE 0 +#define XENUSB_SPEED_LOW 1 +#define XENUSB_SPEED_FULL 2 +#define XENUSB_SPEED_HIGH 3 +}; + +DEFINE_RING_TYPES(xenusb_conn, struct xenusb_conn_request, struct xenusb_conn_response); +#define XENUSB_CONN_RING_SIZE __CONST_RING_SIZE(xenusb_conn, XENUSB_RING_SIZE) + +#endif /* __XEN_PUBLIC_IO_USBIF_H__ */ From patchwork Wed Oct 13 07:52:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?SsO8cmdlbiBHcm/Dnw==?= X-Patchwork-Id: 12555027 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 89ED0C433FE for ; Wed, 13 Oct 2021 07:52:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6990460462 for ; Wed, 13 Oct 2021 07:52:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238531AbhJMHyT (ORCPT ); Wed, 13 Oct 2021 03:54:19 -0400 Received: from smtp-out2.suse.de ([195.135.220.29]:58758 "EHLO smtp-out2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238790AbhJMHyR (ORCPT ); Wed, 13 Oct 2021 03:54:17 -0400 Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id B8ED920106; Wed, 13 Oct 2021 07:52:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1634111533; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=d/eSTT2AxRYa7ZAP4NgwwqIwkZd90o8OYMcT+I3l7bw=; b=rJ7kPyyGgCYZ9KXHYvlj5ONLKDmsP9K3uXB2UJtC4brXFONql0euM4uulEPpa9nuQ1jRX9 rq6tXOVckqDIfgm0o313fFz9RyeYjeRtPsGz+EQl9JFOPR5tdFYhrvXx9E7cnKrjw18eyH AARjqpnfuGzsYChfRZNjMiE9S9D+NlU= Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id 8558113CBE; Wed, 13 Oct 2021 07:52:13 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id WMt2Hy2QZmEjfwAAMHmgww (envelope-from ); Wed, 13 Oct 2021 07:52:13 +0000 From: Juergen Gross To: linux-usb@vger.kernel.org, xen-devel@lists.xenproject.org, linux-kernel@vger.kernel.org Cc: Juergen Gross , Greg Kroah-Hartman Subject: [PATCH v6 2/3] usb: Introduce Xen pvUSB frontend (xen hcd) Date: Wed, 13 Oct 2021 09:52:06 +0200 Message-Id: <20211013075207.13910-3-jgross@suse.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20211013075207.13910-1-jgross@suse.com> References: <20211013075207.13910-1-jgross@suse.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Introduces the Xen pvUSB frontend. With pvUSB it is possible for a Xen domU to communicate with a USB device assigned to that domU. The communication is all done via the pvUSB backend in a driver domain (usually Dom0) which is owner of the physical device. The pvUSB frontend is a USB hcd for a virtual USB host connector. The code is taken from the pvUSB implementation in Xen done by Fujitsu based on Linux kernel 2.6.18. Changes from the original version are: - port to upstream kernel - put all code in just one source file - move module to appropriate location in kernel tree - adapt to Linux style guide - minor code modifications to increase readability Signed-off-by: Juergen Gross --- drivers/usb/host/Kconfig | 11 + drivers/usb/host/Makefile | 1 + drivers/usb/host/xen-hcd.c | 1606 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1618 insertions(+) create mode 100644 drivers/usb/host/xen-hcd.c diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index c4736d1d020c..c0a398aeb7c6 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -770,3 +770,14 @@ config USB_HCD_TEST_MODE This option is of interest only to developers who need to validate their USB hardware designs. It is not needed for normal use. If unsure, say N. + +config USB_XEN_HCD + tristate "Xen usb virtual host driver" + depends on XEN + select XEN_XENBUS_FRONTEND + help + The Xen usb virtual host driver serves as a frontend driver enabling + a Xen guest system to access USB Devices passed through to the guest + by the Xen host (usually Dom0). + Only needed if the kernel is running in a Xen guest and generic + access to a USB device is needed. diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 171de4df50bd..2948983618fb 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -85,3 +85,4 @@ obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o obj-$(CONFIG_USB_MAX3421_HCD) += max3421-hcd.o +obj-$(CONFIG_USB_XEN_HCD) += xen-hcd.o diff --git a/drivers/usb/host/xen-hcd.c b/drivers/usb/host/xen-hcd.c new file mode 100644 index 000000000000..7801dde6f5ee --- /dev/null +++ b/drivers/usb/host/xen-hcd.c @@ -0,0 +1,1606 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * xen-hcd.c + * + * Xen USB Virtual Host Controller driver + * + * Copyright (C) 2009, FUJITSU LABORATORIES LTD. + * Author: Noboru Iwamatsu + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +/* Private per-URB data */ +struct urb_priv { + struct list_head list; + struct urb *urb; + int req_id; /* RING_REQUEST id for submitting */ + int unlink_req_id; /* RING_REQUEST id for unlinking */ + int status; + bool unlinked; /* dequeued marker */ +}; + +/* virtual roothub port status */ +struct rhport_status { + __u32 status; + bool resuming; /* in resuming */ + bool c_connection; /* connection changed */ + unsigned long timeout; +}; + +/* status of attached device */ +struct vdevice_status { + int devnum; + enum usb_device_state status; + enum usb_device_speed speed; +}; + +/* RING request shadow */ +struct usb_shadow { + struct xenusb_urb_request req; + struct urb *urb; +}; + +struct xenhcd_info { + /* Virtual Host Controller has 4 urb queues */ + struct list_head pending_submit_list; + struct list_head pending_unlink_list; + struct list_head in_progress_list; + struct list_head giveback_waiting_list; + + spinlock_t lock; + + /* timer that kick pending and giveback waiting urbs */ + struct timer_list watchdog; + unsigned long actions; + + /* virtual root hub */ + int rh_numports; + struct rhport_status ports[XENUSB_MAX_PORTNR]; + struct vdevice_status devices[XENUSB_MAX_PORTNR]; + + /* Xen related staff */ + struct xenbus_device *xbdev; + int urb_ring_ref; + int conn_ring_ref; + struct xenusb_urb_front_ring urb_ring; + struct xenusb_conn_front_ring conn_ring; + + unsigned int evtchn; + unsigned int irq; + struct usb_shadow shadow[XENUSB_URB_RING_SIZE]; + unsigned int shadow_free; + + bool error; +}; + +#define GRANT_INVALID_REF 0 + +#define XENHCD_RING_JIFFIES (HZ/200) +#define XENHCD_SCAN_JIFFIES 1 + +enum xenhcd_timer_action { + TIMER_RING_WATCHDOG, + TIMER_SCAN_PENDING_URBS, +}; + +static struct kmem_cache *xenhcd_urbp_cachep; + +static inline struct xenhcd_info *xenhcd_hcd_to_info(struct usb_hcd *hcd) +{ + return (struct xenhcd_info *)hcd->hcd_priv; +} + +static inline struct usb_hcd *xenhcd_info_to_hcd(struct xenhcd_info *info) +{ + return container_of((void *)info, struct usb_hcd, hcd_priv); +} + +static void xenhcd_set_error(struct xenhcd_info *info, const char *msg) +{ + info->error = true; + + pr_alert("xen-hcd: protocol error: %s!\n", msg); +} + +static inline void xenhcd_timer_action_done(struct xenhcd_info *info, + enum xenhcd_timer_action action) +{ + clear_bit(action, &info->actions); +} + +static void xenhcd_timer_action(struct xenhcd_info *info, + enum xenhcd_timer_action action) +{ + if (timer_pending(&info->watchdog) && + test_bit(TIMER_SCAN_PENDING_URBS, &info->actions)) + return; + + if (!test_and_set_bit(action, &info->actions)) { + unsigned long t; + + switch (action) { + case TIMER_RING_WATCHDOG: + t = XENHCD_RING_JIFFIES; + break; + default: + t = XENHCD_SCAN_JIFFIES; + break; + } + mod_timer(&info->watchdog, t + jiffies); + } +} + +/* + * set virtual port connection status + */ +static void xenhcd_set_connect_state(struct xenhcd_info *info, int portnum) +{ + int port; + + port = portnum - 1; + if (info->ports[port].status & USB_PORT_STAT_POWER) { + switch (info->devices[port].speed) { + case XENUSB_SPEED_NONE: + info->ports[port].status &= + ~(USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_HIGH_SPEED | + USB_PORT_STAT_SUSPEND); + break; + case XENUSB_SPEED_LOW: + info->ports[port].status |= USB_PORT_STAT_CONNECTION; + info->ports[port].status |= USB_PORT_STAT_LOW_SPEED; + break; + case XENUSB_SPEED_FULL: + info->ports[port].status |= USB_PORT_STAT_CONNECTION; + break; + case XENUSB_SPEED_HIGH: + info->ports[port].status |= USB_PORT_STAT_CONNECTION; + info->ports[port].status |= USB_PORT_STAT_HIGH_SPEED; + break; + default: /* error */ + return; + } + info->ports[port].status |= (USB_PORT_STAT_C_CONNECTION << 16); + } +} + +/* + * set virtual device connection status + */ +static int xenhcd_rhport_connect(struct xenhcd_info *info, __u8 portnum, + __u8 speed) +{ + int port; + + if (portnum < 1 || portnum > info->rh_numports) + return -EINVAL; /* invalid port number */ + + port = portnum - 1; + if (info->devices[port].speed != speed) { + switch (speed) { + case XENUSB_SPEED_NONE: /* disconnect */ + info->devices[port].status = USB_STATE_NOTATTACHED; + break; + case XENUSB_SPEED_LOW: + case XENUSB_SPEED_FULL: + case XENUSB_SPEED_HIGH: + info->devices[port].status = USB_STATE_ATTACHED; + break; + default: /* error */ + return -EINVAL; + } + info->devices[port].speed = speed; + info->ports[port].c_connection = true; + + xenhcd_set_connect_state(info, portnum); + } + + return 0; +} + +/* + * SetPortFeature(PORT_SUSPENDED) + */ +static void xenhcd_rhport_suspend(struct xenhcd_info *info, int portnum) +{ + int port; + + port = portnum - 1; + info->ports[port].status |= USB_PORT_STAT_SUSPEND; + info->devices[port].status = USB_STATE_SUSPENDED; +} + +/* + * ClearPortFeature(PORT_SUSPENDED) + */ +static void xenhcd_rhport_resume(struct xenhcd_info *info, int portnum) +{ + int port; + + port = portnum - 1; + if (info->ports[port].status & USB_PORT_STAT_SUSPEND) { + info->ports[port].resuming = true; + info->ports[port].timeout = jiffies + msecs_to_jiffies(20); + } +} + +/* + * SetPortFeature(PORT_POWER) + */ +static void xenhcd_rhport_power_on(struct xenhcd_info *info, int portnum) +{ + int port; + + port = portnum - 1; + if ((info->ports[port].status & USB_PORT_STAT_POWER) == 0) { + info->ports[port].status |= USB_PORT_STAT_POWER; + if (info->devices[port].status != USB_STATE_NOTATTACHED) + info->devices[port].status = USB_STATE_POWERED; + if (info->ports[port].c_connection) + xenhcd_set_connect_state(info, portnum); + } +} + +/* + * ClearPortFeature(PORT_POWER) + * SetConfiguration(non-zero) + * Power_Source_Off + * Over-current + */ +static void xenhcd_rhport_power_off(struct xenhcd_info *info, int portnum) +{ + int port; + + port = portnum - 1; + if (info->ports[port].status & USB_PORT_STAT_POWER) { + info->ports[port].status = 0; + if (info->devices[port].status != USB_STATE_NOTATTACHED) + info->devices[port].status = USB_STATE_ATTACHED; + } +} + +/* + * ClearPortFeature(PORT_ENABLE) + */ +static void xenhcd_rhport_disable(struct xenhcd_info *info, int portnum) +{ + int port; + + port = portnum - 1; + info->ports[port].status &= ~USB_PORT_STAT_ENABLE; + info->ports[port].status &= ~USB_PORT_STAT_SUSPEND; + info->ports[port].resuming = false; + if (info->devices[port].status != USB_STATE_NOTATTACHED) + info->devices[port].status = USB_STATE_POWERED; +} + +/* + * SetPortFeature(PORT_RESET) + */ +static void xenhcd_rhport_reset(struct xenhcd_info *info, int portnum) +{ + int port; + + port = portnum - 1; + info->ports[port].status &= ~(USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_HIGH_SPEED); + info->ports[port].status |= USB_PORT_STAT_RESET; + + if (info->devices[port].status != USB_STATE_NOTATTACHED) + info->devices[port].status = USB_STATE_ATTACHED; + + /* 10msec reset signaling */ + info->ports[port].timeout = jiffies + msecs_to_jiffies(10); +} + +#ifdef CONFIG_PM +static int xenhcd_bus_suspend(struct usb_hcd *hcd) +{ + struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); + int ret = 0; + int i, ports; + + ports = info->rh_numports; + + spin_lock_irq(&info->lock); + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + ret = -ESHUTDOWN; + } else { + /* suspend any active ports*/ + for (i = 1; i <= ports; i++) + xenhcd_rhport_suspend(info, i); + } + spin_unlock_irq(&info->lock); + + del_timer_sync(&info->watchdog); + + return ret; +} + +static int xenhcd_bus_resume(struct usb_hcd *hcd) +{ + struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); + int ret = 0; + int i, ports; + + ports = info->rh_numports; + + spin_lock_irq(&info->lock); + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + ret = -ESHUTDOWN; + } else { + /* resume any suspended ports*/ + for (i = 1; i <= ports; i++) + xenhcd_rhport_resume(info, i); + } + spin_unlock_irq(&info->lock); + + return ret; +} +#endif + +static void xenhcd_hub_descriptor(struct xenhcd_info *info, + struct usb_hub_descriptor *desc) +{ + __u16 temp; + int ports = info->rh_numports; + + desc->bDescriptorType = 0x29; + desc->bPwrOn2PwrGood = 10; /* EHCI says 20ms max */ + desc->bHubContrCurrent = 0; + desc->bNbrPorts = ports; + + /* size of DeviceRemovable and PortPwrCtrlMask fields */ + temp = 1 + (ports / 8); + desc->bDescLength = 7 + 2 * temp; + + /* bitmaps for DeviceRemovable and PortPwrCtrlMask */ + memset(&desc->u.hs.DeviceRemovable[0], 0, temp); + memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); + + /* per-port over current reporting and no power switching */ + temp = 0x000a; + desc->wHubCharacteristics = cpu_to_le16(temp); +} + +/* port status change mask for hub_status_data */ +#define PORT_C_MASK ((USB_PORT_STAT_C_CONNECTION | \ + USB_PORT_STAT_C_ENABLE | \ + USB_PORT_STAT_C_SUSPEND | \ + USB_PORT_STAT_C_OVERCURRENT | \ + USB_PORT_STAT_C_RESET) << 16) + +/* + * See USB 2.0 Spec, 11.12.4 Hub and Port Status Change Bitmap. + * If port status changed, writes the bitmap to buf and return + * that length(number of bytes). + * If Nothing changed, return 0. + */ +static int xenhcd_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); + int ports; + int i; + unsigned long flags; + int ret; + int changed = 0; + + /* initialize the status to no-changes */ + ports = info->rh_numports; + ret = 1 + (ports / 8); + memset(buf, 0, ret); + + spin_lock_irqsave(&info->lock, flags); + + for (i = 0; i < ports; i++) { + /* check status for each port */ + if (info->ports[i].status & PORT_C_MASK) { + buf[(i + 1) / 8] |= 1 << (i + 1) % 8; + changed = 1; + } + } + + if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1)) + usb_hcd_resume_root_hub(hcd); + + spin_unlock_irqrestore(&info->lock, flags); + + return changed ? ret : 0; +} + +static int xenhcd_hub_control(struct usb_hcd *hcd, __u16 typeReq, __u16 wValue, + __u16 wIndex, char *buf, __u16 wLength) +{ + struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); + int ports = info->rh_numports; + unsigned long flags; + int ret = 0; + int i; + int changed = 0; + + spin_lock_irqsave(&info->lock, flags); + switch (typeReq) { + case ClearHubFeature: + /* ignore this request */ + break; + case ClearPortFeature: + if (!wIndex || wIndex > ports) + goto error; + + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + xenhcd_rhport_resume(info, wIndex); + break; + case USB_PORT_FEAT_POWER: + xenhcd_rhport_power_off(info, wIndex); + break; + case USB_PORT_FEAT_ENABLE: + xenhcd_rhport_disable(info, wIndex); + break; + case USB_PORT_FEAT_C_CONNECTION: + info->ports[wIndex - 1].c_connection = false; + fallthrough; + default: + info->ports[wIndex - 1].status &= ~(1 << wValue); + break; + } + break; + case GetHubDescriptor: + xenhcd_hub_descriptor(info, (struct usb_hub_descriptor *)buf); + break; + case GetHubStatus: + /* always local power supply good and no over-current exists. */ + *(__le32 *)buf = cpu_to_le32(0); + break; + case GetPortStatus: + if (!wIndex || wIndex > ports) + goto error; + + wIndex--; + + /* resume completion */ + if (info->ports[wIndex].resuming && + time_after_eq(jiffies, info->ports[wIndex].timeout)) { + info->ports[wIndex].status |= + USB_PORT_STAT_C_SUSPEND << 16; + info->ports[wIndex].status &= ~USB_PORT_STAT_SUSPEND; + } + + /* reset completion */ + if ((info->ports[wIndex].status & USB_PORT_STAT_RESET) != 0 && + time_after_eq(jiffies, info->ports[wIndex].timeout)) { + info->ports[wIndex].status |= + USB_PORT_STAT_C_RESET << 16; + info->ports[wIndex].status &= ~USB_PORT_STAT_RESET; + + if (info->devices[wIndex].status != + USB_STATE_NOTATTACHED) { + info->ports[wIndex].status |= + USB_PORT_STAT_ENABLE; + info->devices[wIndex].status = + USB_STATE_DEFAULT; + } + + switch (info->devices[wIndex].speed) { + case XENUSB_SPEED_LOW: + info->ports[wIndex].status |= + USB_PORT_STAT_LOW_SPEED; + break; + case XENUSB_SPEED_HIGH: + info->ports[wIndex].status |= + USB_PORT_STAT_HIGH_SPEED; + break; + default: + break; + } + } + + *(__le32 *)buf = cpu_to_le32(info->ports[wIndex].status); + break; + case SetPortFeature: + if (!wIndex || wIndex > ports) + goto error; + + switch (wValue) { + case USB_PORT_FEAT_POWER: + xenhcd_rhport_power_on(info, wIndex); + break; + case USB_PORT_FEAT_RESET: + xenhcd_rhport_reset(info, wIndex); + break; + case USB_PORT_FEAT_SUSPEND: + xenhcd_rhport_suspend(info, wIndex); + break; + default: + if (info->ports[wIndex-1].status & USB_PORT_STAT_POWER) + info->ports[wIndex-1].status |= (1 << wValue); + } + break; + + case SetHubFeature: + /* not supported */ + default: +error: + ret = -EPIPE; + } + spin_unlock_irqrestore(&info->lock, flags); + + /* check status for each port */ + for (i = 0; i < ports; i++) { + if (info->ports[i].status & PORT_C_MASK) + changed = 1; + } + if (changed) + usb_hcd_poll_rh_status(hcd); + + return ret; +} + +static void xenhcd_free_urb_priv(struct urb_priv *urbp) +{ + urbp->urb->hcpriv = NULL; + kmem_cache_free(xenhcd_urbp_cachep, urbp); +} + +static inline unsigned int xenhcd_get_id_from_freelist(struct xenhcd_info *info) +{ + unsigned int free; + + free = info->shadow_free; + info->shadow_free = info->shadow[free].req.id; + info->shadow[free].req.id = 0x0fff; /* debug */ + return free; +} + +static inline void xenhcd_add_id_to_freelist(struct xenhcd_info *info, + unsigned int id) +{ + info->shadow[id].req.id = info->shadow_free; + info->shadow[id].urb = NULL; + info->shadow_free = id; +} + +static inline int xenhcd_count_pages(void *addr, int length) +{ + unsigned long vaddr = (unsigned long)addr; + + return PFN_UP(vaddr + length) - PFN_DOWN(vaddr); +} + +static void xenhcd_gnttab_map(struct xenhcd_info *info, void *addr, int length, + grant_ref_t *gref_head, + struct xenusb_request_segment *seg, + int nr_pages, int flags) +{ + grant_ref_t ref; + unsigned long buffer_mfn; + unsigned int offset; + unsigned int len = length; + unsigned int bytes; + int i; + + for (i = 0; i < nr_pages; i++) { + buffer_mfn = PFN_DOWN(arbitrary_virt_to_machine(addr).maddr); + offset = offset_in_page(addr); + + bytes = PAGE_SIZE - offset; + if (bytes > len) + bytes = len; + + ref = gnttab_claim_grant_reference(gref_head); + gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id, + buffer_mfn, flags); + seg[i].gref = ref; + seg[i].offset = (__u16)offset; + seg[i].length = (__u16)bytes; + + addr += bytes; + len -= bytes; + } +} + +static __u32 xenhcd_pipe_urb_to_xenusb(__u32 urb_pipe, __u8 port) +{ + static __u32 pipe; + + pipe = usb_pipedevice(urb_pipe) << XENUSB_PIPE_DEV_SHIFT; + pipe |= usb_pipeendpoint(urb_pipe) << XENUSB_PIPE_EP_SHIFT; + if (usb_pipein(urb_pipe)) + pipe |= XENUSB_PIPE_DIR; + switch (usb_pipetype(urb_pipe)) { + case PIPE_ISOCHRONOUS: + pipe |= XENUSB_PIPE_TYPE_ISOC << XENUSB_PIPE_TYPE_SHIFT; + break; + case PIPE_INTERRUPT: + pipe |= XENUSB_PIPE_TYPE_INT << XENUSB_PIPE_TYPE_SHIFT; + break; + case PIPE_CONTROL: + pipe |= XENUSB_PIPE_TYPE_CTRL << XENUSB_PIPE_TYPE_SHIFT; + break; + case PIPE_BULK: + pipe |= XENUSB_PIPE_TYPE_BULK << XENUSB_PIPE_TYPE_SHIFT; + break; + } + pipe = xenusb_setportnum_pipe(pipe, port); + + return pipe; +} + +static int xenhcd_map_urb_for_request(struct xenhcd_info *info, struct urb *urb, + struct xenusb_urb_request *req) +{ + grant_ref_t gref_head; + int nr_buff_pages = 0; + int nr_isodesc_pages = 0; + int nr_grants = 0; + + if (urb->transfer_buffer_length) { + nr_buff_pages = xenhcd_count_pages(urb->transfer_buffer, + urb->transfer_buffer_length); + + if (usb_pipeisoc(urb->pipe)) + nr_isodesc_pages = xenhcd_count_pages( + &urb->iso_frame_desc[0], + sizeof(struct usb_iso_packet_descriptor) * + urb->number_of_packets); + + nr_grants = nr_buff_pages + nr_isodesc_pages; + if (nr_grants > XENUSB_MAX_SEGMENTS_PER_REQUEST) { + pr_err("xenhcd: error: %d grants\n", nr_grants); + return -E2BIG; + } + + if (gnttab_alloc_grant_references(nr_grants, &gref_head)) { + pr_err("xenhcd: gnttab_alloc_grant_references() error\n"); + return -ENOMEM; + } + + xenhcd_gnttab_map(info, urb->transfer_buffer, + urb->transfer_buffer_length, &gref_head, + &req->seg[0], nr_buff_pages, + usb_pipein(urb->pipe) ? 0 : GTF_readonly); + } + + req->pipe = xenhcd_pipe_urb_to_xenusb(urb->pipe, urb->dev->portnum); + req->transfer_flags = 0; + if (urb->transfer_flags & URB_SHORT_NOT_OK) + req->transfer_flags |= XENUSB_SHORT_NOT_OK; + req->buffer_length = urb->transfer_buffer_length; + req->nr_buffer_segs = nr_buff_pages; + + switch (usb_pipetype(urb->pipe)) { + case PIPE_ISOCHRONOUS: + req->u.isoc.interval = urb->interval; + req->u.isoc.start_frame = urb->start_frame; + req->u.isoc.number_of_packets = urb->number_of_packets; + req->u.isoc.nr_frame_desc_segs = nr_isodesc_pages; + + xenhcd_gnttab_map(info, &urb->iso_frame_desc[0], + sizeof(struct usb_iso_packet_descriptor) * + urb->number_of_packets, + &gref_head, &req->seg[nr_buff_pages], + nr_isodesc_pages, 0); + break; + case PIPE_INTERRUPT: + req->u.intr.interval = urb->interval; + break; + case PIPE_CONTROL: + if (urb->setup_packet) + memcpy(req->u.ctrl, urb->setup_packet, 8); + break; + case PIPE_BULK: + break; + default: + break; + } + + if (nr_grants) + gnttab_free_grant_references(gref_head); + + return 0; +} + +static void xenhcd_gnttab_done(struct usb_shadow *shadow) +{ + int nr_segs = 0; + int i; + + nr_segs = shadow->req.nr_buffer_segs; + + if (xenusb_pipeisoc(shadow->req.pipe)) + nr_segs += shadow->req.u.isoc.nr_frame_desc_segs; + + for (i = 0; i < nr_segs; i++) + gnttab_end_foreign_access(shadow->req.seg[i].gref, 0, 0UL); + + shadow->req.nr_buffer_segs = 0; + shadow->req.u.isoc.nr_frame_desc_segs = 0; +} + +static int xenhcd_translate_status(int status) +{ + switch (status) { + case XENUSB_STATUS_OK: + return 0; + case XENUSB_STATUS_NODEV: + return -ENODEV; + case XENUSB_STATUS_INVAL: + return -EINVAL; + case XENUSB_STATUS_STALL: + return -EPIPE; + case XENUSB_STATUS_IOERROR: + return -EPROTO; + case XENUSB_STATUS_BABBLE: + return -EOVERFLOW; + default: + return -ESHUTDOWN; + } +} + +static void xenhcd_giveback_urb(struct xenhcd_info *info, struct urb *urb, + int status) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + int priv_status = urbp->status; + + list_del_init(&urbp->list); + xenhcd_free_urb_priv(urbp); + + if (urb->status == -EINPROGRESS) + urb->status = xenhcd_translate_status(status); + + spin_unlock(&info->lock); + usb_hcd_giveback_urb(xenhcd_info_to_hcd(info), urb, + priv_status <= 0 ? priv_status : urb->status); + spin_lock(&info->lock); +} + +static int xenhcd_do_request(struct xenhcd_info *info, struct urb_priv *urbp) +{ + struct xenusb_urb_request *req; + struct urb *urb = urbp->urb; + unsigned int id; + int notify; + int ret; + + id = xenhcd_get_id_from_freelist(info); + req = &info->shadow[id].req; + req->id = id; + + if (unlikely(urbp->unlinked)) { + req->u.unlink.unlink_id = urbp->req_id; + req->pipe = xenusb_setunlink_pipe(xenhcd_pipe_urb_to_xenusb( + urb->pipe, urb->dev->portnum)); + urbp->unlink_req_id = id; + } else { + ret = xenhcd_map_urb_for_request(info, urb, req); + if (ret) { + xenhcd_add_id_to_freelist(info, id); + return ret; + } + urbp->req_id = id; + } + + req = RING_GET_REQUEST(&info->urb_ring, info->urb_ring.req_prod_pvt); + *req = info->shadow[id].req; + + info->urb_ring.req_prod_pvt++; + info->shadow[id].urb = urb; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->urb_ring, notify); + if (notify) + notify_remote_via_irq(info->irq); + + return 0; +} + +static void xenhcd_kick_pending_urbs(struct xenhcd_info *info) +{ + struct urb_priv *urbp; + + while (!list_empty(&info->pending_submit_list)) { + if (RING_FULL(&info->urb_ring)) { + xenhcd_timer_action(info, TIMER_RING_WATCHDOG); + return; + } + + urbp = list_entry(info->pending_submit_list.next, + struct urb_priv, list); + if (!xenhcd_do_request(info, urbp)) + list_move_tail(&urbp->list, &info->in_progress_list); + else + xenhcd_giveback_urb(info, urbp->urb, -ESHUTDOWN); + } + xenhcd_timer_action_done(info, TIMER_SCAN_PENDING_URBS); +} + +/* + * caller must lock info->lock + */ +static void xenhcd_cancel_all_enqueued_urbs(struct xenhcd_info *info) +{ + struct urb_priv *urbp, *tmp; + int req_id; + + list_for_each_entry_safe(urbp, tmp, &info->in_progress_list, list) { + req_id = urbp->req_id; + if (!urbp->unlinked) { + xenhcd_gnttab_done(&info->shadow[req_id]); + if (urbp->urb->status == -EINPROGRESS) + /* not dequeued */ + xenhcd_giveback_urb(info, urbp->urb, + -ESHUTDOWN); + else /* dequeued */ + xenhcd_giveback_urb(info, urbp->urb, + urbp->urb->status); + } + info->shadow[req_id].urb = NULL; + } + + list_for_each_entry_safe(urbp, tmp, &info->pending_submit_list, list) + xenhcd_giveback_urb(info, urbp->urb, -ESHUTDOWN); +} + +/* + * caller must lock info->lock + */ +static void xenhcd_giveback_unlinked_urbs(struct xenhcd_info *info) +{ + struct urb_priv *urbp, *tmp; + + list_for_each_entry_safe(urbp, tmp, &info->giveback_waiting_list, list) + xenhcd_giveback_urb(info, urbp->urb, urbp->urb->status); +} + +static int xenhcd_submit_urb(struct xenhcd_info *info, struct urb_priv *urbp) +{ + int ret; + + if (RING_FULL(&info->urb_ring)) { + list_add_tail(&urbp->list, &info->pending_submit_list); + xenhcd_timer_action(info, TIMER_RING_WATCHDOG); + return 0; + } + + if (!list_empty(&info->pending_submit_list)) { + list_add_tail(&urbp->list, &info->pending_submit_list); + xenhcd_timer_action(info, TIMER_SCAN_PENDING_URBS); + return 0; + } + + ret = xenhcd_do_request(info, urbp); + if (ret == 0) + list_add_tail(&urbp->list, &info->in_progress_list); + + return ret; +} + +static int xenhcd_unlink_urb(struct xenhcd_info *info, struct urb_priv *urbp) +{ + int ret; + + /* already unlinked? */ + if (urbp->unlinked) + return -EBUSY; + + urbp->unlinked = true; + + /* the urb is still in pending_submit queue */ + if (urbp->req_id == ~0) { + list_move_tail(&urbp->list, &info->giveback_waiting_list); + xenhcd_timer_action(info, TIMER_SCAN_PENDING_URBS); + return 0; + } + + /* send unlink request to backend */ + if (RING_FULL(&info->urb_ring)) { + list_move_tail(&urbp->list, &info->pending_unlink_list); + xenhcd_timer_action(info, TIMER_RING_WATCHDOG); + return 0; + } + + if (!list_empty(&info->pending_unlink_list)) { + list_move_tail(&urbp->list, &info->pending_unlink_list); + xenhcd_timer_action(info, TIMER_SCAN_PENDING_URBS); + return 0; + } + + ret = xenhcd_do_request(info, urbp); + if (ret == 0) + list_move_tail(&urbp->list, &info->in_progress_list); + + return ret; +} + +static int xenhcd_urb_request_done(struct xenhcd_info *info) +{ + struct xenusb_urb_response res; + struct urb *urb; + RING_IDX i, rp; + __u16 id; + int more_to_do = 0; + unsigned long flags; + + spin_lock_irqsave(&info->lock, flags); + + rp = info->urb_ring.sring->rsp_prod; + if (RING_RESPONSE_PROD_OVERFLOW(&info->urb_ring, rp)) { + xenhcd_set_error(info, "Illegal index on urb-ring"); + return 0; + } + rmb(); /* ensure we see queued responses up to "rp" */ + + for (i = info->urb_ring.rsp_cons; i != rp; i++) { + RING_COPY_RESPONSE(&info->urb_ring, i, &res); + id = res.id; + if (id >= XENUSB_URB_RING_SIZE) { + xenhcd_set_error(info, "Illegal data on urb-ring"); + continue; + } + + if (likely(xenusb_pipesubmit(info->shadow[id].req.pipe))) { + xenhcd_gnttab_done(&info->shadow[id]); + urb = info->shadow[id].urb; + if (likely(urb)) { + urb->actual_length = res.actual_length; + urb->error_count = res.error_count; + urb->start_frame = res.start_frame; + xenhcd_giveback_urb(info, urb, res.status); + } + } + + xenhcd_add_id_to_freelist(info, id); + } + info->urb_ring.rsp_cons = i; + + if (i != info->urb_ring.req_prod_pvt) + RING_FINAL_CHECK_FOR_RESPONSES(&info->urb_ring, more_to_do); + else + info->urb_ring.sring->rsp_event = i + 1; + + spin_unlock_irqrestore(&info->lock, flags); + + return more_to_do; +} + +static int xenhcd_conn_notify(struct xenhcd_info *info) +{ + struct xenusb_conn_response res; + struct xenusb_conn_request *req; + RING_IDX rc, rp; + __u16 id; + __u8 portnum, speed; + int more_to_do = 0; + int notify; + int port_changed = 0; + unsigned long flags; + + spin_lock_irqsave(&info->lock, flags); + + rc = info->conn_ring.rsp_cons; + rp = info->conn_ring.sring->rsp_prod; + if (RING_RESPONSE_PROD_OVERFLOW(&info->conn_ring, rp)) { + xenhcd_set_error(info, "Illegal index on conn-ring"); + return 0; + } + rmb(); /* ensure we see queued responses up to "rp" */ + + while (rc != rp) { + RING_COPY_RESPONSE(&info->conn_ring, rc, &res); + id = res.id; + portnum = res.portnum; + speed = res.speed; + info->conn_ring.rsp_cons = ++rc; + + if (xenhcd_rhport_connect(info, portnum, speed)) { + xenhcd_set_error(info, "Illegal data on conn-ring"); + return 0; + } + + if (info->ports[portnum - 1].c_connection) + port_changed = 1; + + barrier(); + + req = RING_GET_REQUEST(&info->conn_ring, + info->conn_ring.req_prod_pvt); + req->id = id; + info->conn_ring.req_prod_pvt++; + } + + if (rc != info->conn_ring.req_prod_pvt) + RING_FINAL_CHECK_FOR_RESPONSES(&info->conn_ring, more_to_do); + else + info->conn_ring.sring->rsp_event = rc + 1; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->conn_ring, notify); + if (notify) + notify_remote_via_irq(info->irq); + + spin_unlock_irqrestore(&info->lock, flags); + + if (port_changed) + usb_hcd_poll_rh_status(xenhcd_info_to_hcd(info)); + + return more_to_do; +} + +static irqreturn_t xenhcd_int(int irq, void *dev_id) +{ + struct xenhcd_info *info = (struct xenhcd_info *)dev_id; + + if (unlikely(info->error)) + return IRQ_HANDLED; + + while (xenhcd_urb_request_done(info) | xenhcd_conn_notify(info)) + /* Yield point for this unbounded loop. */ + cond_resched(); + + return IRQ_HANDLED; +} + +static void xenhcd_destroy_rings(struct xenhcd_info *info) +{ + if (info->irq) + unbind_from_irqhandler(info->irq, info); + info->irq = 0; + + if (info->urb_ring_ref != GRANT_INVALID_REF) { + gnttab_end_foreign_access(info->urb_ring_ref, 0, + (unsigned long)info->urb_ring.sring); + info->urb_ring_ref = GRANT_INVALID_REF; + } + info->urb_ring.sring = NULL; + + if (info->conn_ring_ref != GRANT_INVALID_REF) { + gnttab_end_foreign_access(info->conn_ring_ref, 0, + (unsigned long)info->conn_ring.sring); + info->conn_ring_ref = GRANT_INVALID_REF; + } + info->conn_ring.sring = NULL; +} + +static int xenhcd_setup_rings(struct xenbus_device *dev, + struct xenhcd_info *info) +{ + struct xenusb_urb_sring *urb_sring; + struct xenusb_conn_sring *conn_sring; + grant_ref_t gref; + int err; + + info->urb_ring_ref = GRANT_INVALID_REF; + info->conn_ring_ref = GRANT_INVALID_REF; + + urb_sring = (struct xenusb_urb_sring *)get_zeroed_page( + GFP_NOIO | __GFP_HIGH); + if (!urb_sring) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating urb ring"); + return -ENOMEM; + } + SHARED_RING_INIT(urb_sring); + FRONT_RING_INIT(&info->urb_ring, urb_sring, PAGE_SIZE); + + err = xenbus_grant_ring(dev, urb_sring, 1, &gref); + if (err < 0) { + free_page((unsigned long)urb_sring); + info->urb_ring.sring = NULL; + goto fail; + } + info->urb_ring_ref = gref; + + conn_sring = (struct xenusb_conn_sring *)get_zeroed_page( + GFP_NOIO | __GFP_HIGH); + if (!conn_sring) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating conn ring"); + err = -ENOMEM; + goto fail; + } + SHARED_RING_INIT(conn_sring); + FRONT_RING_INIT(&info->conn_ring, conn_sring, PAGE_SIZE); + + err = xenbus_grant_ring(dev, conn_sring, 1, &gref); + if (err < 0) { + free_page((unsigned long)conn_sring); + info->conn_ring.sring = NULL; + goto fail; + } + info->conn_ring_ref = gref; + + err = xenbus_alloc_evtchn(dev, &info->evtchn); + if (err) { + xenbus_dev_fatal(dev, err, "xenbus_alloc_evtchn"); + goto fail; + } + + err = bind_evtchn_to_irq(info->evtchn); + if (err <= 0) { + xenbus_dev_fatal(dev, err, "bind_evtchn_to_irq"); + goto fail; + } + + info->irq = err; + + err = request_threaded_irq(info->irq, NULL, xenhcd_int, + IRQF_ONESHOT, "xenhcd", info); + if (err) { + xenbus_dev_fatal(dev, err, "request_threaded_irq"); + goto free_irq; + } + + return 0; + +free_irq: + unbind_from_irqhandler(info->irq, info); +fail: + xenhcd_destroy_rings(info); + return err; +} + +static int xenhcd_talk_to_backend(struct xenbus_device *dev, + struct xenhcd_info *info) +{ + const char *message; + struct xenbus_transaction xbt; + int err; + + err = xenhcd_setup_rings(dev, info); + if (err) + return err; + +again: + err = xenbus_transaction_start(&xbt); + if (err) { + xenbus_dev_fatal(dev, err, "starting transaction"); + goto destroy_ring; + } + + err = xenbus_printf(xbt, dev->nodename, "urb-ring-ref", "%u", + info->urb_ring_ref); + if (err) { + message = "writing urb-ring-ref"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, dev->nodename, "conn-ring-ref", "%u", + info->conn_ring_ref); + if (err) { + message = "writing conn-ring-ref"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", + info->evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + + err = xenbus_transaction_end(xbt, 0); + if (err) { + if (err == -EAGAIN) + goto again; + xenbus_dev_fatal(dev, err, "completing transaction"); + goto destroy_ring; + } + + return 0; + +abort_transaction: + xenbus_transaction_end(xbt, 1); + xenbus_dev_fatal(dev, err, "%s", message); + +destroy_ring: + xenhcd_destroy_rings(info); + + return err; +} + +static int xenhcd_connect(struct xenbus_device *dev) +{ + struct xenhcd_info *info = dev_get_drvdata(&dev->dev); + struct xenusb_conn_request *req; + int idx, err; + int notify; + char name[TASK_COMM_LEN]; + struct usb_hcd *hcd; + + hcd = xenhcd_info_to_hcd(info); + snprintf(name, TASK_COMM_LEN, "xenhcd.%d", hcd->self.busnum); + + err = xenhcd_talk_to_backend(dev, info); + if (err) + return err; + + /* prepare ring for hotplug notification */ + for (idx = 0; idx < XENUSB_CONN_RING_SIZE; idx++) { + req = RING_GET_REQUEST(&info->conn_ring, idx); + req->id = idx; + } + info->conn_ring.req_prod_pvt = idx; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->conn_ring, notify); + if (notify) + notify_remote_via_irq(info->irq); + + return 0; +} + +static void xenhcd_disconnect(struct xenbus_device *dev) +{ + struct xenhcd_info *info = dev_get_drvdata(&dev->dev); + struct usb_hcd *hcd = xenhcd_info_to_hcd(info); + + usb_remove_hcd(hcd); + xenbus_frontend_closed(dev); +} + +static void xenhcd_watchdog(struct timer_list *timer) +{ + struct xenhcd_info *info = from_timer(info, timer, watchdog); + unsigned long flags; + + spin_lock_irqsave(&info->lock, flags); + if (likely(HC_IS_RUNNING(xenhcd_info_to_hcd(info)->state))) { + xenhcd_timer_action_done(info, TIMER_RING_WATCHDOG); + xenhcd_giveback_unlinked_urbs(info); + xenhcd_kick_pending_urbs(info); + } + spin_unlock_irqrestore(&info->lock, flags); +} + +/* + * one-time HC init + */ +static int xenhcd_setup(struct usb_hcd *hcd) +{ + struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); + + spin_lock_init(&info->lock); + INIT_LIST_HEAD(&info->pending_submit_list); + INIT_LIST_HEAD(&info->pending_unlink_list); + INIT_LIST_HEAD(&info->in_progress_list); + INIT_LIST_HEAD(&info->giveback_waiting_list); + timer_setup(&info->watchdog, xenhcd_watchdog, 0); + + hcd->has_tt = (hcd->driver->flags & HCD_MASK) != HCD_USB11; + + return 0; +} + +/* + * start HC running + */ +static int xenhcd_run(struct usb_hcd *hcd) +{ + hcd->uses_new_polling = 1; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); + hcd->state = HC_STATE_RUNNING; + return 0; +} + +/* + * stop running HC + */ +static void xenhcd_stop(struct usb_hcd *hcd) +{ + struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); + + del_timer_sync(&info->watchdog); + spin_lock_irq(&info->lock); + /* cancel all urbs */ + hcd->state = HC_STATE_HALT; + xenhcd_cancel_all_enqueued_urbs(info); + xenhcd_giveback_unlinked_urbs(info); + spin_unlock_irq(&info->lock); +} + +/* + * called as .urb_enqueue() + * non-error returns are promise to giveback the urb later + */ +static int xenhcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); + struct urb_priv *urbp; + unsigned long flags; + int ret; + + if (unlikely(info->error)) + return -ESHUTDOWN; + + urbp = kmem_cache_zalloc(xenhcd_urbp_cachep, mem_flags); + if (!urbp) + return -ENOMEM; + + spin_lock_irqsave(&info->lock, flags); + + urbp->urb = urb; + urb->hcpriv = urbp; + urbp->req_id = ~0; + urbp->unlink_req_id = ~0; + INIT_LIST_HEAD(&urbp->list); + urbp->status = 1; + urb->unlinked = false; + + ret = xenhcd_submit_urb(info, urbp); + + if (ret) + xenhcd_free_urb_priv(urbp); + + spin_unlock_irqrestore(&info->lock, flags); + + return ret; +} + +/* + * called as .urb_dequeue() + */ +static int xenhcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); + struct urb_priv *urbp; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&info->lock, flags); + + urbp = urb->hcpriv; + if (urbp) { + urbp->status = status; + ret = xenhcd_unlink_urb(info, urbp); + } + + spin_unlock_irqrestore(&info->lock, flags); + + return ret; +} + +/* + * called from usb_get_current_frame_number(), + * but, almost all drivers not use such function. + */ +static int xenhcd_get_frame(struct usb_hcd *hcd) +{ + /* it means error, but probably no problem :-) */ + return 0; +} + +static struct hc_driver xenhcd_usb20_hc_driver = { + .description = "xen-hcd", + .product_desc = "Xen USB2.0 Virtual Host Controller", + .hcd_priv_size = sizeof(struct xenhcd_info), + .flags = HCD_USB2, + + /* basic HC lifecycle operations */ + .reset = xenhcd_setup, + .start = xenhcd_run, + .stop = xenhcd_stop, + + /* managing urb I/O */ + .urb_enqueue = xenhcd_urb_enqueue, + .urb_dequeue = xenhcd_urb_dequeue, + .get_frame_number = xenhcd_get_frame, + + /* root hub operations */ + .hub_status_data = xenhcd_hub_status_data, + .hub_control = xenhcd_hub_control, +#ifdef CONFIG_PM + .bus_suspend = xenhcd_bus_suspend, + .bus_resume = xenhcd_bus_resume, +#endif +}; + +static struct hc_driver xenhcd_usb11_hc_driver = { + .description = "xen-hcd", + .product_desc = "Xen USB1.1 Virtual Host Controller", + .hcd_priv_size = sizeof(struct xenhcd_info), + .flags = HCD_USB11, + + /* basic HC lifecycle operations */ + .reset = xenhcd_setup, + .start = xenhcd_run, + .stop = xenhcd_stop, + + /* managing urb I/O */ + .urb_enqueue = xenhcd_urb_enqueue, + .urb_dequeue = xenhcd_urb_dequeue, + .get_frame_number = xenhcd_get_frame, + + /* root hub operations */ + .hub_status_data = xenhcd_hub_status_data, + .hub_control = xenhcd_hub_control, +#ifdef CONFIG_PM + .bus_suspend = xenhcd_bus_suspend, + .bus_resume = xenhcd_bus_resume, +#endif +}; + +static struct usb_hcd *xenhcd_create_hcd(struct xenbus_device *dev) +{ + int i; + int err = 0; + int num_ports; + int usb_ver; + struct usb_hcd *hcd = NULL; + struct xenhcd_info *info; + + err = xenbus_scanf(XBT_NIL, dev->otherend, "num-ports", "%d", + &num_ports); + if (err != 1) { + xenbus_dev_fatal(dev, err, "reading num-ports"); + return ERR_PTR(-EINVAL); + } + if (num_ports < 1 || num_ports > XENUSB_MAX_PORTNR) { + xenbus_dev_fatal(dev, err, "invalid num-ports"); + return ERR_PTR(-EINVAL); + } + + err = xenbus_scanf(XBT_NIL, dev->otherend, "usb-ver", "%d", &usb_ver); + if (err != 1) { + xenbus_dev_fatal(dev, err, "reading usb-ver"); + return ERR_PTR(-EINVAL); + } + switch (usb_ver) { + case XENUSB_VER_USB11: + hcd = usb_create_hcd(&xenhcd_usb11_hc_driver, &dev->dev, + dev_name(&dev->dev)); + break; + case XENUSB_VER_USB20: + hcd = usb_create_hcd(&xenhcd_usb20_hc_driver, &dev->dev, + dev_name(&dev->dev)); + break; + default: + xenbus_dev_fatal(dev, err, "invalid usb-ver"); + return ERR_PTR(-EINVAL); + } + if (!hcd) { + xenbus_dev_fatal(dev, err, + "fail to allocate USB host controller"); + return ERR_PTR(-ENOMEM); + } + + info = xenhcd_hcd_to_info(hcd); + info->xbdev = dev; + info->rh_numports = num_ports; + + for (i = 0; i < XENUSB_URB_RING_SIZE; i++) { + info->shadow[i].req.id = i + 1; + info->shadow[i].urb = NULL; + } + info->shadow[XENUSB_URB_RING_SIZE - 1].req.id = 0x0fff; + + return hcd; +} + +static void xenhcd_backend_changed(struct xenbus_device *dev, + enum xenbus_state backend_state) +{ + switch (backend_state) { + case XenbusStateInitialising: + case XenbusStateReconfiguring: + case XenbusStateReconfigured: + case XenbusStateUnknown: + break; + + case XenbusStateInitWait: + case XenbusStateInitialised: + case XenbusStateConnected: + if (dev->state != XenbusStateInitialising) + break; + if (!xenhcd_connect(dev)) + xenbus_switch_state(dev, XenbusStateConnected); + break; + + case XenbusStateClosed: + if (dev->state == XenbusStateClosed) + break; + fallthrough; /* Missed the backend's Closing state. */ + case XenbusStateClosing: + xenhcd_disconnect(dev); + break; + + default: + xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend", + backend_state); + break; + } +} + +static int xenhcd_remove(struct xenbus_device *dev) +{ + struct xenhcd_info *info = dev_get_drvdata(&dev->dev); + struct usb_hcd *hcd = xenhcd_info_to_hcd(info); + + xenhcd_destroy_rings(info); + usb_put_hcd(hcd); + + return 0; +} + +static int xenhcd_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + int err; + struct usb_hcd *hcd; + struct xenhcd_info *info; + + if (usb_disabled()) + return -ENODEV; + + hcd = xenhcd_create_hcd(dev); + if (IS_ERR(hcd)) { + err = PTR_ERR(hcd); + xenbus_dev_fatal(dev, err, + "fail to create usb host controller"); + return err; + } + + info = xenhcd_hcd_to_info(hcd); + dev_set_drvdata(&dev->dev, info); + + err = usb_add_hcd(hcd, 0, 0); + if (err) { + xenbus_dev_fatal(dev, err, "fail to add USB host controller"); + usb_put_hcd(hcd); + dev_set_drvdata(&dev->dev, NULL); + } + + return err; +} + +static const struct xenbus_device_id xenhcd_ids[] = { + { "vusb" }, + { "" }, +}; + +static struct xenbus_driver xenhcd_driver = { + .ids = xenhcd_ids, + .probe = xenhcd_probe, + .otherend_changed = xenhcd_backend_changed, + .remove = xenhcd_remove, +}; + +static int __init xenhcd_init(void) +{ + if (!xen_domain()) + return -ENODEV; + + xenhcd_urbp_cachep = kmem_cache_create("xenhcd_urb_priv", + sizeof(struct urb_priv), 0, 0, NULL); + if (!xenhcd_urbp_cachep) { + pr_err("xenhcd failed to create kmem cache\n"); + return -ENOMEM; + } + + return xenbus_register_frontend(&xenhcd_driver); +} +module_init(xenhcd_init); + +static void __exit xenhcd_exit(void) +{ + kmem_cache_destroy(xenhcd_urbp_cachep); + xenbus_unregister_driver(&xenhcd_driver); +} +module_exit(xenhcd_exit); + +MODULE_ALIAS("xen:vusb"); +MODULE_AUTHOR("Juergen Gross "); +MODULE_DESCRIPTION("Xen USB Virtual Host Controller driver (xen-hcd)"); +MODULE_LICENSE("Dual BSD/GPL"); From patchwork Wed Oct 13 07:52:07 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?SsO8cmdlbiBHcm/Dnw==?= X-Patchwork-Id: 12555025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BE40DC4332F for ; Wed, 13 Oct 2021 07:52:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9EC3C60E97 for ; Wed, 13 Oct 2021 07:52:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238816AbhJMHyS (ORCPT ); Wed, 13 Oct 2021 03:54:18 -0400 Received: from smtp-out1.suse.de ([195.135.220.28]:54638 "EHLO smtp-out1.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238764AbhJMHyR (ORCPT ); Wed, 13 Oct 2021 03:54:17 -0400 Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id E5C88222C1; Wed, 13 Oct 2021 07:52:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1634111533; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=po+hf+FNagQm6i1f8al0BNtylY1+PBG7DwkLS/oaAWs=; b=mddxD0CD/4UuclYtMsHxMsipqzzcIolby96n8sagPPPa1soKyWpYO0BVx1ufBskC7UUWWc 4Th5pE9aLhfLjHDvK/lfrBXTpRp6qbLqyy5nRdScdU7wBrJCDej3KGBDE5M7YSxrbLkkAd ZjJQdUQyjRC3VV/+Avt8b7V+T8FugGY= Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id BD79513E64; Wed, 13 Oct 2021 07:52:13 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id 8HwkLS2QZmEjfwAAMHmgww (envelope-from ); Wed, 13 Oct 2021 07:52:13 +0000 From: Juergen Gross To: linux-usb@vger.kernel.org, xen-devel@lists.xenproject.org, linux-kernel@vger.kernel.org Cc: Juergen Gross , Konrad Rzeszutek Wilk Subject: [PATCH v6 3/3] xen: add Xen pvUSB maintainer Date: Wed, 13 Oct 2021 09:52:07 +0200 Message-Id: <20211013075207.13910-4-jgross@suse.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20211013075207.13910-1-jgross@suse.com> References: <20211013075207.13910-1-jgross@suse.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Add myself as maintainer for the Xen pvUSB stuff. Signed-off-by: Juergen Gross Acked-by: Konrad Rzeszutek Wilk --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e0bca0de0df7..7c211195be7e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20535,6 +20535,14 @@ F: drivers/scsi/xen-scsifront.c F: drivers/xen/xen-scsiback.c F: include/xen/interface/io/vscsiif.h +XEN PVUSB DRIVER +M: Juergen Gross +L: xen-devel@lists.xenproject.org (moderated for non-subscribers) +L: linux-usb@vger.kernel.org +S: Supported +F: divers/usb/host/xen* +F: include/xen/interface/io/usbif.h + XEN SOUND FRONTEND DRIVER M: Oleksandr Andrushchenko L: xen-devel@lists.xenproject.org (moderated for non-subscribers)