From nobody Tue Apr 16 16:25:08 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=quarantine dis=none) header.from=suse.com ARC-Seal: i=1; a=rsa-sha256; t=1637673680; cv=none; d=zohomail.com; s=zohoarc; b=c2wswz+VUTRzhDlM0g/9NYvWiW4L2kM3ZF5jhRKjKZaYYh8LfbCxZH+Wfo/SYYeVCLQnPEf2Cwxk9EbDihg2FNNCTeKmddUFMTd1nSdocw4AYothHmYsbWARBccRb0F5Cozejs6kw9UxfTZX2sv0cVUThthz8BHxY1fFQE+OLOw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1637673680; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=ieEEwCqF83XsavfVRgMrEJimWYSiFfUd5VpcSLoCdEY=; b=LaDN98wEexs96RKsmji4oQxDpzXxDRiXwh740a8xqBKi3PQv8S0RnEXjQGN21RF4KsZgUsYOorbAqOGTR+S9SBs0u/iCc7gosUGgvN8W5p+VotIESdPkkRQrn0ztCutil9ELXOkXfT0DlY8M1FjBRK/FTqzz/9KWNNzeucZb9aw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 16376736806831.6983244464214522; Tue, 23 Nov 2021 05:21:20 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.229582.397015 (Exim 4.92) (envelope-from ) id 1mpVjE-00033m-Dz; Tue, 23 Nov 2021 13:20:56 +0000 Received: by outflank-mailman (output) from mailman id 229582.397015; Tue, 23 Nov 2021 13:20:56 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1mpVjE-00033f-A8; Tue, 23 Nov 2021 13:20:56 +0000 Received: by outflank-mailman (input) for mailman id 229582; Tue, 23 Nov 2021 13:20:54 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1mpVjC-0002Xw-D2 for xen-devel@lists.xenproject.org; Tue, 23 Nov 2021 13:20:54 +0000 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.220.29]) by se1-gles-flk1.inumbo.com (Halon) with ESMTPS id 2dc28a32-4c60-11ec-a9d2-d9f7a1cc8784; Tue, 23 Nov 2021 14:20:51 +0100 (CET) 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 D8F801FD58; Tue, 23 Nov 2021 13:20:50 +0000 (UTC) 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 A211213E08; Tue, 23 Nov 2021 13:20:50 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id 6GVZJrLqnGH9CgAAMHmgww (envelope-from ); Tue, 23 Nov 2021 13:20:50 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 2dc28a32-4c60-11ec-a9d2-d9f7a1cc8784 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1637673650; 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=ieEEwCqF83XsavfVRgMrEJimWYSiFfUd5VpcSLoCdEY=; b=sRIC4z1bRM5m/QVgrHY+BZjnUKUpq8jXkjUg04ElhpOK69xFQMY+rob1P3ExyxOzyqhA1H RXiMny/8xF059C43OxWMetDQYHdaO8LUFUzPW1FJgSm7fVZRNrV2YqEjw6ZjIlU45hOXGA F6AfYXem5fqlJ749mMsy3vyoUwxhVLg= From: Juergen Gross To: xen-devel@lists.xenproject.org, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Juergen Gross , Boris Ostrovsky , Stefano Stabellini , Greg Kroah-Hartman Subject: [PATCH v7 1/3] usb: Add Xen pvUSB protocol description Date: Tue, 23 Nov 2021 14:20:46 +0100 Message-Id: <20211123132048.5335-2-jgross@suse.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20211123132048.5335-1-jgross@suse.com> References: <20211123132048.5335-1-jgross@suse.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @suse.com) X-ZM-MESSAGEID: 1637673682079100005 Content-Type: text/plain; charset="utf-8" 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 | 405 +++++++++++++++++++++++++++++++ 1 file changed, 405 insertions(+) create mode 100644 include/xen/interface/io/usbif.h diff --git a/include/xen/interface/io/usbif.h b/include/xen/interface/io/us= bif.h new file mode 100644 index 000000000000..a70e0b93178b --- /dev/null +++ b/include/xen/interface/io/usbif.h @@ -0,0 +1,405 @@ +/* SPDX-License-Identifier: MIT */ + +/* + * usbif.h + * + * USB I/O interface for Xen guest OSes. + * + * Copyright (C) 2009, FUJITSU LABORATORIES LTD. + * Author: Noboru Iwamatsu + */ + +#ifndef __XEN_PUBLIC_IO_USBIF_H__ +#define __XEN_PUBLIC_IO_USBIF_H__ + +#include "ring.h" +#include "../grant_table.h" + +/* + * Detailed Interface Description + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D + * 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 a= re + * 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 front= end + * about USB device plug events (device to be added or removed). This is t= he + * "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 + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * 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 no= de + * 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 =3D USB 1.1, 2 =3D 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 + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + *-------------------------- 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 oct= et + * +----------------+----------------+----------------+----------------+ + * | 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 =3D=3D unpl= ug + * + * 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 oct= et + * +----------------+----------------+----------------+----------------+ + * | 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] | 1= 44 + * | data | 1= 48 + * +----------------+----------------+----------------+----------------+ + * 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 be= low) + * bit 7: direction (1 =3D read from device) + * bits 8-14: device number on port + * bits 15-18: endpoint of device + * bits 30-31: request type: 00 =3D isochronous, 01 =3D interrupt, + * 10 =3D control, 11 =3D 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 oct= et + * +----------------+----------------+----------------+----------------+ + * | 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 eleme= nts + * + * Request type specific data for interrupt request: + * 0 1 2 3 oct= et + * +----------------+----------------+----------------+----------------+ + * | interval | 0 | 4 + * +----------------+----------------+----------------+----------------+ + * | 0 | 8 + * +----------------+----------------+----------------+----------------+ + * interval - uint16_t, time in msecs until interruption + * + * Request type specific data for control request: + * 0 1 2 3 oct= et + * +----------------+----------------+----------------+----------------+ + * | data of setup packet | 4 + * | | 8 + * +----------------+----------------+----------------+----------------+ + * + * Request type specific data for bulk request: + * 0 1 2 3 oct= et + * +----------------+----------------+----------------+----------------+ + * | 0 | 4 + * | 0 | 8 + * +----------------+----------------+----------------+----------------+ + * + * Request type specific data for unlink request: + * 0 1 2 3 oct= et + * +----------------+----------------+----------------+----------------+ + * | unlink_id | 0 | 4 + * +----------------+----------------+----------------+----------------+ + * | 0 | 8 + * +----------------+----------------+----------------+----------------+ + * unlink_id - uint16_t, request id of request to terminate + * + * seg[] array element layout: + * 0 1 2 3 oct= et + * +----------------+----------------+----------------+----------------+ + * | 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 oct= et + * +----------------+----------------+----------------+----------------+ + * | 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 =3D 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 =3D submit urb, + * 1 =3D unlink urb) + * + * - direction: bit 7 + * (0 =3D Host-to-Device [Out] + * 1 =3D Device-to-Host [In]) + * + * - device address: bits 8-14 + * + * - endpoint: bits 15-18 + * + * - pipe type: bits 30-31 + * (00 =3D isochronous, 01 =3D interrupt, + * 10 =3D control, 11 =3D 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) =3D=3D XENUSB_PIPE_TY= PE_ISOC) +#define xenusb_pipeint(pipe) (xenusb_pipetype(pipe) =3D=3D XENUSB_PIPE_TYP= E_INT) +#define xenusb_pipectrl(pipe) (xenusb_pipetype(pipe) =3D=3D XENUSB_PIPE_TY= PE_CTRL) +#define xenusb_pipebulk(pipe) (xenusb_pipetype(pipe) =3D=3D XENUSB_PIPE_TY= PE_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_SIZ= E) + +/* + * 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_c= onn_response); +#define XENUSB_CONN_RING_SIZE __CONST_RING_SIZE(xenusb_conn, XENUSB_RING_S= IZE) + +#endif /* __XEN_PUBLIC_IO_USBIF_H__ */ --=20 2.26.2 From nobody Tue Apr 16 16:25:08 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=quarantine dis=none) header.from=suse.com ARC-Seal: i=1; a=rsa-sha256; t=1637673683; cv=none; d=zohomail.com; s=zohoarc; b=CrAvdrXdG6ge7ogOa73rAe6kkOgmlu/3o0zMr+PwJNwKODAhjLLERDs4nqgOOYjnycRuqQZmp9/jfb/Ui44CvJaKn4xhvK5e/2aNY7Nx2338OpQ3TzoMD2n8Drhiax8wi4HwhNiXljZ1EMjkA+pi+I9+Y5kbA0FItpu3zk+u76U= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1637673683; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=owJuxZKWJugJfZXS+nM0bWvS5UDcs7WZjS9gLkzp2rI=; b=JedduTKoxye7t6rfnlBHDWrtD+7QUkX5e/yzJG9AnqVOq2KjHV9sy9/8yh3frPLkDdd6hvDarBTZAbOSS6V9XsG8Yh3b3DoURQjYFgk7lOeXJ2BFNlij4HjwUtQQIeWN975VEyst8nSK6wy4ppb/M8WFrECVoZBPr/EQy6QMSLY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1637673683606613.2827101752727; Tue, 23 Nov 2021 05:21:23 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.229583.397021 (Exim 4.92) (envelope-from ) id 1mpVjE-0003Au-Up; Tue, 23 Nov 2021 13:20:56 +0000 Received: by outflank-mailman (output) from mailman id 229583.397021; Tue, 23 Nov 2021 13:20:56 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1mpVjE-00039l-Qc; Tue, 23 Nov 2021 13:20:56 +0000 Received: by outflank-mailman (input) for mailman id 229583; Tue, 23 Nov 2021 13:20:56 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1mpVjD-0002Xw-D9 for xen-devel@lists.xenproject.org; Tue, 23 Nov 2021 13:20:55 +0000 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.220.28]) by se1-gles-flk1.inumbo.com (Halon) with ESMTPS id 2dec7e17-4c60-11ec-a9d2-d9f7a1cc8784; Tue, 23 Nov 2021 14:20:51 +0100 (CET) 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 1F55321941; Tue, 23 Nov 2021 13:20:51 +0000 (UTC) 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 E1A3E13DE9; Tue, 23 Nov 2021 13:20:50 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id gL70NbLqnGH9CgAAMHmgww (envelope-from ); Tue, 23 Nov 2021 13:20:50 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 2dec7e17-4c60-11ec-a9d2-d9f7a1cc8784 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1637673651; 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=owJuxZKWJugJfZXS+nM0bWvS5UDcs7WZjS9gLkzp2rI=; b=R182SVf8qA+Ls4TYuv6LCygnNvuWkDmdF7pypy+AGb4peOYTQ6inBafL3E6zrrES/RItzu pGjWRVcPRibjmot5tLbrmWSgN/ZBMhLAbru4NpxjO4rGrNVRaU2LPHatcDOM+45eWbpmsm YjvZ9m7Y8LMAvxphvmC02XEduxnPH98= From: Juergen Gross To: xen-devel@lists.xenproject.org, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Juergen Gross , Greg Kroah-Hartman Subject: [PATCH v7 2/3] usb: Introduce Xen pvUSB frontend (xen hcd) Date: Tue, 23 Nov 2021 14:20:47 +0100 Message-Id: <20211123132048.5335-3-jgross@suse.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20211123132048.5335-1-jgross@suse.com> References: <20211123132048.5335-1-jgross@suse.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @suse.com) X-ZM-MESSAGEID: 1637673684956100001 Content-Type: text/plain; charset="utf-8" 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 d1d926f8f9c2..57ca5f97a3dc 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -772,3 +772,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) +=3D bcma-hcd.o obj-$(CONFIG_USB_HCD_SSB) +=3D ssb-hcd.o obj-$(CONFIG_USB_FOTG210_HCD) +=3D fotg210-hcd.o obj-$(CONFIG_USB_MAX3421_HCD) +=3D max3421-hcd.o +obj-$(CONFIG_USB_XEN_HCD) +=3D 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 =3D 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 =3D XENHCD_RING_JIFFIES; + break; + default: + t =3D 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 =3D portnum - 1; + if (info->ports[port].status & USB_PORT_STAT_POWER) { + switch (info->devices[port].speed) { + case XENUSB_SPEED_NONE: + info->ports[port].status &=3D + ~(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 |=3D USB_PORT_STAT_CONNECTION; + info->ports[port].status |=3D USB_PORT_STAT_LOW_SPEED; + break; + case XENUSB_SPEED_FULL: + info->ports[port].status |=3D USB_PORT_STAT_CONNECTION; + break; + case XENUSB_SPEED_HIGH: + info->ports[port].status |=3D USB_PORT_STAT_CONNECTION; + info->ports[port].status |=3D USB_PORT_STAT_HIGH_SPEED; + break; + default: /* error */ + return; + } + info->ports[port].status |=3D (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 =3D portnum - 1; + if (info->devices[port].speed !=3D speed) { + switch (speed) { + case XENUSB_SPEED_NONE: /* disconnect */ + info->devices[port].status =3D USB_STATE_NOTATTACHED; + break; + case XENUSB_SPEED_LOW: + case XENUSB_SPEED_FULL: + case XENUSB_SPEED_HIGH: + info->devices[port].status =3D USB_STATE_ATTACHED; + break; + default: /* error */ + return -EINVAL; + } + info->devices[port].speed =3D speed; + info->ports[port].c_connection =3D 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 =3D portnum - 1; + info->ports[port].status |=3D USB_PORT_STAT_SUSPEND; + info->devices[port].status =3D USB_STATE_SUSPENDED; +} + +/* + * ClearPortFeature(PORT_SUSPENDED) + */ +static void xenhcd_rhport_resume(struct xenhcd_info *info, int portnum) +{ + int port; + + port =3D portnum - 1; + if (info->ports[port].status & USB_PORT_STAT_SUSPEND) { + info->ports[port].resuming =3D true; + info->ports[port].timeout =3D jiffies + msecs_to_jiffies(20); + } +} + +/* + * SetPortFeature(PORT_POWER) + */ +static void xenhcd_rhport_power_on(struct xenhcd_info *info, int portnum) +{ + int port; + + port =3D portnum - 1; + if ((info->ports[port].status & USB_PORT_STAT_POWER) =3D=3D 0) { + info->ports[port].status |=3D USB_PORT_STAT_POWER; + if (info->devices[port].status !=3D USB_STATE_NOTATTACHED) + info->devices[port].status =3D 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 =3D portnum - 1; + if (info->ports[port].status & USB_PORT_STAT_POWER) { + info->ports[port].status =3D 0; + if (info->devices[port].status !=3D USB_STATE_NOTATTACHED) + info->devices[port].status =3D USB_STATE_ATTACHED; + } +} + +/* + * ClearPortFeature(PORT_ENABLE) + */ +static void xenhcd_rhport_disable(struct xenhcd_info *info, int portnum) +{ + int port; + + port =3D portnum - 1; + info->ports[port].status &=3D ~USB_PORT_STAT_ENABLE; + info->ports[port].status &=3D ~USB_PORT_STAT_SUSPEND; + info->ports[port].resuming =3D false; + if (info->devices[port].status !=3D USB_STATE_NOTATTACHED) + info->devices[port].status =3D USB_STATE_POWERED; +} + +/* + * SetPortFeature(PORT_RESET) + */ +static void xenhcd_rhport_reset(struct xenhcd_info *info, int portnum) +{ + int port; + + port =3D portnum - 1; + info->ports[port].status &=3D ~(USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_HIGH_SPEED); + info->ports[port].status |=3D USB_PORT_STAT_RESET; + + if (info->devices[port].status !=3D USB_STATE_NOTATTACHED) + info->devices[port].status =3D USB_STATE_ATTACHED; + + /* 10msec reset signaling */ + info->ports[port].timeout =3D jiffies + msecs_to_jiffies(10); +} + +#ifdef CONFIG_PM +static int xenhcd_bus_suspend(struct usb_hcd *hcd) +{ + struct xenhcd_info *info =3D xenhcd_hcd_to_info(hcd); + int ret =3D 0; + int i, ports; + + ports =3D info->rh_numports; + + spin_lock_irq(&info->lock); + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + ret =3D -ESHUTDOWN; + } else { + /* suspend any active ports*/ + for (i =3D 1; i <=3D 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 =3D xenhcd_hcd_to_info(hcd); + int ret =3D 0; + int i, ports; + + ports =3D info->rh_numports; + + spin_lock_irq(&info->lock); + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + ret =3D -ESHUTDOWN; + } else { + /* resume any suspended ports*/ + for (i =3D 1; i <=3D 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 =3D info->rh_numports; + + desc->bDescriptorType =3D 0x29; + desc->bPwrOn2PwrGood =3D 10; /* EHCI says 20ms max */ + desc->bHubContrCurrent =3D 0; + desc->bNbrPorts =3D ports; + + /* size of DeviceRemovable and PortPwrCtrlMask fields */ + temp =3D 1 + (ports / 8); + desc->bDescLength =3D 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 =3D 0x000a; + desc->wHubCharacteristics =3D 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 =3D xenhcd_hcd_to_info(hcd); + int ports; + int i; + unsigned long flags; + int ret; + int changed =3D 0; + + /* initialize the status to no-changes */ + ports =3D info->rh_numports; + ret =3D 1 + (ports / 8); + memset(buf, 0, ret); + + spin_lock_irqsave(&info->lock, flags); + + for (i =3D 0; i < ports; i++) { + /* check status for each port */ + if (info->ports[i].status & PORT_C_MASK) { + buf[(i + 1) / 8] |=3D 1 << (i + 1) % 8; + changed =3D 1; + } + } + + if ((hcd->state =3D=3D HC_STATE_SUSPENDED) && (changed =3D=3D 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 wV= alue, + __u16 wIndex, char *buf, __u16 wLength) +{ + struct xenhcd_info *info =3D xenhcd_hcd_to_info(hcd); + int ports =3D info->rh_numports; + unsigned long flags; + int ret =3D 0; + int i; + int changed =3D 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 =3D false; + fallthrough; + default: + info->ports[wIndex - 1].status &=3D ~(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 =3D 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 |=3D + USB_PORT_STAT_C_SUSPEND << 16; + info->ports[wIndex].status &=3D ~USB_PORT_STAT_SUSPEND; + } + + /* reset completion */ + if ((info->ports[wIndex].status & USB_PORT_STAT_RESET) !=3D 0 && + time_after_eq(jiffies, info->ports[wIndex].timeout)) { + info->ports[wIndex].status |=3D + USB_PORT_STAT_C_RESET << 16; + info->ports[wIndex].status &=3D ~USB_PORT_STAT_RESET; + + if (info->devices[wIndex].status !=3D + USB_STATE_NOTATTACHED) { + info->ports[wIndex].status |=3D + USB_PORT_STAT_ENABLE; + info->devices[wIndex].status =3D + USB_STATE_DEFAULT; + } + + switch (info->devices[wIndex].speed) { + case XENUSB_SPEED_LOW: + info->ports[wIndex].status |=3D + USB_PORT_STAT_LOW_SPEED; + break; + case XENUSB_SPEED_HIGH: + info->ports[wIndex].status |=3D + USB_PORT_STAT_HIGH_SPEED; + break; + default: + break; + } + } + + *(__le32 *)buf =3D 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 |=3D (1 << wValue); + } + break; + + case SetHubFeature: + /* not supported */ + default: +error: + ret =3D -EPIPE; + } + spin_unlock_irqrestore(&info->lock, flags); + + /* check status for each port */ + for (i =3D 0; i < ports; i++) { + if (info->ports[i].status & PORT_C_MASK) + changed =3D 1; + } + if (changed) + usb_hcd_poll_rh_status(hcd); + + return ret; +} + +static void xenhcd_free_urb_priv(struct urb_priv *urbp) +{ + urbp->urb->hcpriv =3D 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 =3D info->shadow_free; + info->shadow_free =3D info->shadow[free].req.id; + info->shadow[free].req.id =3D 0x0fff; /* debug */ + return free; +} + +static inline void xenhcd_add_id_to_freelist(struct xenhcd_info *info, + unsigned int id) +{ + info->shadow[id].req.id =3D info->shadow_free; + info->shadow[id].urb =3D NULL; + info->shadow_free =3D id; +} + +static inline int xenhcd_count_pages(void *addr, int length) +{ + unsigned long vaddr =3D (unsigned long)addr; + + return PFN_UP(vaddr + length) - PFN_DOWN(vaddr); +} + +static void xenhcd_gnttab_map(struct xenhcd_info *info, void *addr, int le= ngth, + 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 =3D length; + unsigned int bytes; + int i; + + for (i =3D 0; i < nr_pages; i++) { + buffer_mfn =3D PFN_DOWN(arbitrary_virt_to_machine(addr).maddr); + offset =3D offset_in_page(addr); + + bytes =3D PAGE_SIZE - offset; + if (bytes > len) + bytes =3D len; + + ref =3D gnttab_claim_grant_reference(gref_head); + gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id, + buffer_mfn, flags); + seg[i].gref =3D ref; + seg[i].offset =3D (__u16)offset; + seg[i].length =3D (__u16)bytes; + + addr +=3D bytes; + len -=3D bytes; + } +} + +static __u32 xenhcd_pipe_urb_to_xenusb(__u32 urb_pipe, __u8 port) +{ + static __u32 pipe; + + pipe =3D usb_pipedevice(urb_pipe) << XENUSB_PIPE_DEV_SHIFT; + pipe |=3D usb_pipeendpoint(urb_pipe) << XENUSB_PIPE_EP_SHIFT; + if (usb_pipein(urb_pipe)) + pipe |=3D XENUSB_PIPE_DIR; + switch (usb_pipetype(urb_pipe)) { + case PIPE_ISOCHRONOUS: + pipe |=3D XENUSB_PIPE_TYPE_ISOC << XENUSB_PIPE_TYPE_SHIFT; + break; + case PIPE_INTERRUPT: + pipe |=3D XENUSB_PIPE_TYPE_INT << XENUSB_PIPE_TYPE_SHIFT; + break; + case PIPE_CONTROL: + pipe |=3D XENUSB_PIPE_TYPE_CTRL << XENUSB_PIPE_TYPE_SHIFT; + break; + case PIPE_BULK: + pipe |=3D XENUSB_PIPE_TYPE_BULK << XENUSB_PIPE_TYPE_SHIFT; + break; + } + pipe =3D 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 =3D 0; + int nr_isodesc_pages =3D 0; + int nr_grants =3D 0; + + if (urb->transfer_buffer_length) { + nr_buff_pages =3D xenhcd_count_pages(urb->transfer_buffer, + urb->transfer_buffer_length); + + if (usb_pipeisoc(urb->pipe)) + nr_isodesc_pages =3D xenhcd_count_pages( + &urb->iso_frame_desc[0], + sizeof(struct usb_iso_packet_descriptor) * + urb->number_of_packets); + + nr_grants =3D 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 =3D xenhcd_pipe_urb_to_xenusb(urb->pipe, urb->dev->portnum); + req->transfer_flags =3D 0; + if (urb->transfer_flags & URB_SHORT_NOT_OK) + req->transfer_flags |=3D XENUSB_SHORT_NOT_OK; + req->buffer_length =3D urb->transfer_buffer_length; + req->nr_buffer_segs =3D nr_buff_pages; + + switch (usb_pipetype(urb->pipe)) { + case PIPE_ISOCHRONOUS: + req->u.isoc.interval =3D urb->interval; + req->u.isoc.start_frame =3D urb->start_frame; + req->u.isoc.number_of_packets =3D urb->number_of_packets; + req->u.isoc.nr_frame_desc_segs =3D 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 =3D 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 =3D 0; + int i; + + nr_segs =3D shadow->req.nr_buffer_segs; + + if (xenusb_pipeisoc(shadow->req.pipe)) + nr_segs +=3D shadow->req.u.isoc.nr_frame_desc_segs; + + for (i =3D 0; i < nr_segs; i++) + gnttab_end_foreign_access(shadow->req.seg[i].gref, 0, 0UL); + + shadow->req.nr_buffer_segs =3D 0; + shadow->req.u.isoc.nr_frame_desc_segs =3D 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 =3D (struct urb_priv *)urb->hcpriv; + int priv_status =3D urbp->status; + + list_del_init(&urbp->list); + xenhcd_free_urb_priv(urbp); + + if (urb->status =3D=3D -EINPROGRESS) + urb->status =3D xenhcd_translate_status(status); + + spin_unlock(&info->lock); + usb_hcd_giveback_urb(xenhcd_info_to_hcd(info), urb, + priv_status <=3D 0 ? priv_status : urb->status); + spin_lock(&info->lock); +} + +static int xenhcd_do_request(struct xenhcd_info *info, struct urb_priv *ur= bp) +{ + struct xenusb_urb_request *req; + struct urb *urb =3D urbp->urb; + unsigned int id; + int notify; + int ret; + + id =3D xenhcd_get_id_from_freelist(info); + req =3D &info->shadow[id].req; + req->id =3D id; + + if (unlikely(urbp->unlinked)) { + req->u.unlink.unlink_id =3D urbp->req_id; + req->pipe =3D xenusb_setunlink_pipe(xenhcd_pipe_urb_to_xenusb( + urb->pipe, urb->dev->portnum)); + urbp->unlink_req_id =3D id; + } else { + ret =3D xenhcd_map_urb_for_request(info, urb, req); + if (ret) { + xenhcd_add_id_to_freelist(info, id); + return ret; + } + urbp->req_id =3D id; + } + + req =3D RING_GET_REQUEST(&info->urb_ring, info->urb_ring.req_prod_pvt); + *req =3D info->shadow[id].req; + + info->urb_ring.req_prod_pvt++; + info->shadow[id].urb =3D 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 =3D 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 =3D urbp->req_id; + if (!urbp->unlinked) { + xenhcd_gnttab_done(&info->shadow[req_id]); + if (urbp->urb->status =3D=3D -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 =3D 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 *ur= bp) +{ + 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 =3D xenhcd_do_request(info, urbp); + if (ret =3D=3D 0) + list_add_tail(&urbp->list, &info->in_progress_list); + + return ret; +} + +static int xenhcd_unlink_urb(struct xenhcd_info *info, struct urb_priv *ur= bp) +{ + int ret; + + /* already unlinked? */ + if (urbp->unlinked) + return -EBUSY; + + urbp->unlinked =3D true; + + /* the urb is still in pending_submit queue */ + if (urbp->req_id =3D=3D ~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 =3D xenhcd_do_request(info, urbp); + if (ret =3D=3D 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 =3D 0; + unsigned long flags; + + spin_lock_irqsave(&info->lock, flags); + + rp =3D 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 =3D info->urb_ring.rsp_cons; i !=3D rp; i++) { + RING_COPY_RESPONSE(&info->urb_ring, i, &res); + id =3D res.id; + if (id >=3D 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 =3D info->shadow[id].urb; + if (likely(urb)) { + urb->actual_length =3D res.actual_length; + urb->error_count =3D res.error_count; + urb->start_frame =3D res.start_frame; + xenhcd_giveback_urb(info, urb, res.status); + } + } + + xenhcd_add_id_to_freelist(info, id); + } + info->urb_ring.rsp_cons =3D i; + + if (i !=3D info->urb_ring.req_prod_pvt) + RING_FINAL_CHECK_FOR_RESPONSES(&info->urb_ring, more_to_do); + else + info->urb_ring.sring->rsp_event =3D 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 =3D 0; + int notify; + int port_changed =3D 0; + unsigned long flags; + + spin_lock_irqsave(&info->lock, flags); + + rc =3D info->conn_ring.rsp_cons; + rp =3D 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 !=3D rp) { + RING_COPY_RESPONSE(&info->conn_ring, rc, &res); + id =3D res.id; + portnum =3D res.portnum; + speed =3D res.speed; + info->conn_ring.rsp_cons =3D ++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 =3D 1; + + barrier(); + + req =3D RING_GET_REQUEST(&info->conn_ring, + info->conn_ring.req_prod_pvt); + req->id =3D id; + info->conn_ring.req_prod_pvt++; + } + + if (rc !=3D info->conn_ring.req_prod_pvt) + RING_FINAL_CHECK_FOR_RESPONSES(&info->conn_ring, more_to_do); + else + info->conn_ring.sring->rsp_event =3D 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 =3D (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 =3D 0; + + if (info->urb_ring_ref !=3D GRANT_INVALID_REF) { + gnttab_end_foreign_access(info->urb_ring_ref, 0, + (unsigned long)info->urb_ring.sring); + info->urb_ring_ref =3D GRANT_INVALID_REF; + } + info->urb_ring.sring =3D NULL; + + if (info->conn_ring_ref !=3D GRANT_INVALID_REF) { + gnttab_end_foreign_access(info->conn_ring_ref, 0, + (unsigned long)info->conn_ring.sring); + info->conn_ring_ref =3D GRANT_INVALID_REF; + } + info->conn_ring.sring =3D 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 =3D GRANT_INVALID_REF; + info->conn_ring_ref =3D GRANT_INVALID_REF; + + urb_sring =3D (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 =3D xenbus_grant_ring(dev, urb_sring, 1, &gref); + if (err < 0) { + free_page((unsigned long)urb_sring); + info->urb_ring.sring =3D NULL; + goto fail; + } + info->urb_ring_ref =3D gref; + + conn_sring =3D (struct xenusb_conn_sring *)get_zeroed_page( + GFP_NOIO | __GFP_HIGH); + if (!conn_sring) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating conn ring"); + err =3D -ENOMEM; + goto fail; + } + SHARED_RING_INIT(conn_sring); + FRONT_RING_INIT(&info->conn_ring, conn_sring, PAGE_SIZE); + + err =3D xenbus_grant_ring(dev, conn_sring, 1, &gref); + if (err < 0) { + free_page((unsigned long)conn_sring); + info->conn_ring.sring =3D NULL; + goto fail; + } + info->conn_ring_ref =3D gref; + + err =3D xenbus_alloc_evtchn(dev, &info->evtchn); + if (err) { + xenbus_dev_fatal(dev, err, "xenbus_alloc_evtchn"); + goto fail; + } + + err =3D bind_evtchn_to_irq(info->evtchn); + if (err <=3D 0) { + xenbus_dev_fatal(dev, err, "bind_evtchn_to_irq"); + goto fail; + } + + info->irq =3D err; + + err =3D 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 =3D xenhcd_setup_rings(dev, info); + if (err) + return err; + +again: + err =3D xenbus_transaction_start(&xbt); + if (err) { + xenbus_dev_fatal(dev, err, "starting transaction"); + goto destroy_ring; + } + + err =3D xenbus_printf(xbt, dev->nodename, "urb-ring-ref", "%u", + info->urb_ring_ref); + if (err) { + message =3D "writing urb-ring-ref"; + goto abort_transaction; + } + + err =3D xenbus_printf(xbt, dev->nodename, "conn-ring-ref", "%u", + info->conn_ring_ref); + if (err) { + message =3D "writing conn-ring-ref"; + goto abort_transaction; + } + + err =3D xenbus_printf(xbt, dev->nodename, "event-channel", "%u", + info->evtchn); + if (err) { + message =3D "writing event-channel"; + goto abort_transaction; + } + + err =3D xenbus_transaction_end(xbt, 0); + if (err) { + if (err =3D=3D -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 =3D 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 =3D xenhcd_info_to_hcd(info); + snprintf(name, TASK_COMM_LEN, "xenhcd.%d", hcd->self.busnum); + + err =3D xenhcd_talk_to_backend(dev, info); + if (err) + return err; + + /* prepare ring for hotplug notification */ + for (idx =3D 0; idx < XENUSB_CONN_RING_SIZE; idx++) { + req =3D RING_GET_REQUEST(&info->conn_ring, idx); + req->id =3D idx; + } + info->conn_ring.req_prod_pvt =3D 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 =3D dev_get_drvdata(&dev->dev); + struct usb_hcd *hcd =3D 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 =3D 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 =3D 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 =3D (hcd->driver->flags & HCD_MASK) !=3D HCD_USB11; + + return 0; +} + +/* + * start HC running + */ +static int xenhcd_run(struct usb_hcd *hcd) +{ + hcd->uses_new_polling =3D 1; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); + hcd->state =3D HC_STATE_RUNNING; + return 0; +} + +/* + * stop running HC + */ +static void xenhcd_stop(struct usb_hcd *hcd) +{ + struct xenhcd_info *info =3D xenhcd_hcd_to_info(hcd); + + del_timer_sync(&info->watchdog); + spin_lock_irq(&info->lock); + /* cancel all urbs */ + hcd->state =3D 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 =3D xenhcd_hcd_to_info(hcd); + struct urb_priv *urbp; + unsigned long flags; + int ret; + + if (unlikely(info->error)) + return -ESHUTDOWN; + + urbp =3D kmem_cache_zalloc(xenhcd_urbp_cachep, mem_flags); + if (!urbp) + return -ENOMEM; + + spin_lock_irqsave(&info->lock, flags); + + urbp->urb =3D urb; + urb->hcpriv =3D urbp; + urbp->req_id =3D ~0; + urbp->unlink_req_id =3D ~0; + INIT_LIST_HEAD(&urbp->list); + urbp->status =3D 1; + urb->unlinked =3D false; + + ret =3D 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 st= atus) +{ + struct xenhcd_info *info =3D xenhcd_hcd_to_info(hcd); + struct urb_priv *urbp; + unsigned long flags; + int ret =3D 0; + + spin_lock_irqsave(&info->lock, flags); + + urbp =3D urb->hcpriv; + if (urbp) { + urbp->status =3D status; + ret =3D 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 =3D { + .description =3D "xen-hcd", + .product_desc =3D "Xen USB2.0 Virtual Host Controller", + .hcd_priv_size =3D sizeof(struct xenhcd_info), + .flags =3D HCD_USB2, + + /* basic HC lifecycle operations */ + .reset =3D xenhcd_setup, + .start =3D xenhcd_run, + .stop =3D xenhcd_stop, + + /* managing urb I/O */ + .urb_enqueue =3D xenhcd_urb_enqueue, + .urb_dequeue =3D xenhcd_urb_dequeue, + .get_frame_number =3D xenhcd_get_frame, + + /* root hub operations */ + .hub_status_data =3D xenhcd_hub_status_data, + .hub_control =3D xenhcd_hub_control, +#ifdef CONFIG_PM + .bus_suspend =3D xenhcd_bus_suspend, + .bus_resume =3D xenhcd_bus_resume, +#endif +}; + +static struct hc_driver xenhcd_usb11_hc_driver =3D { + .description =3D "xen-hcd", + .product_desc =3D "Xen USB1.1 Virtual Host Controller", + .hcd_priv_size =3D sizeof(struct xenhcd_info), + .flags =3D HCD_USB11, + + /* basic HC lifecycle operations */ + .reset =3D xenhcd_setup, + .start =3D xenhcd_run, + .stop =3D xenhcd_stop, + + /* managing urb I/O */ + .urb_enqueue =3D xenhcd_urb_enqueue, + .urb_dequeue =3D xenhcd_urb_dequeue, + .get_frame_number =3D xenhcd_get_frame, + + /* root hub operations */ + .hub_status_data =3D xenhcd_hub_status_data, + .hub_control =3D xenhcd_hub_control, +#ifdef CONFIG_PM + .bus_suspend =3D xenhcd_bus_suspend, + .bus_resume =3D xenhcd_bus_resume, +#endif +}; + +static struct usb_hcd *xenhcd_create_hcd(struct xenbus_device *dev) +{ + int i; + int err =3D 0; + int num_ports; + int usb_ver; + struct usb_hcd *hcd =3D NULL; + struct xenhcd_info *info; + + err =3D xenbus_scanf(XBT_NIL, dev->otherend, "num-ports", "%d", + &num_ports); + if (err !=3D 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 =3D xenbus_scanf(XBT_NIL, dev->otherend, "usb-ver", "%d", &usb_ver); + if (err !=3D 1) { + xenbus_dev_fatal(dev, err, "reading usb-ver"); + return ERR_PTR(-EINVAL); + } + switch (usb_ver) { + case XENUSB_VER_USB11: + hcd =3D usb_create_hcd(&xenhcd_usb11_hc_driver, &dev->dev, + dev_name(&dev->dev)); + break; + case XENUSB_VER_USB20: + hcd =3D 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 =3D xenhcd_hcd_to_info(hcd); + info->xbdev =3D dev; + info->rh_numports =3D num_ports; + + for (i =3D 0; i < XENUSB_URB_RING_SIZE; i++) { + info->shadow[i].req.id =3D i + 1; + info->shadow[i].urb =3D NULL; + } + info->shadow[XENUSB_URB_RING_SIZE - 1].req.id =3D 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 !=3D XenbusStateInitialising) + break; + if (!xenhcd_connect(dev)) + xenbus_switch_state(dev, XenbusStateConnected); + break; + + case XenbusStateClosed: + if (dev->state =3D=3D 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 =3D dev_get_drvdata(&dev->dev); + struct usb_hcd *hcd =3D 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 =3D xenhcd_create_hcd(dev); + if (IS_ERR(hcd)) { + err =3D PTR_ERR(hcd); + xenbus_dev_fatal(dev, err, + "fail to create usb host controller"); + return err; + } + + info =3D xenhcd_hcd_to_info(hcd); + dev_set_drvdata(&dev->dev, info); + + err =3D 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[] =3D { + { "vusb" }, + { "" }, +}; + +static struct xenbus_driver xenhcd_driver =3D { + .ids =3D xenhcd_ids, + .probe =3D xenhcd_probe, + .otherend_changed =3D xenhcd_backend_changed, + .remove =3D xenhcd_remove, +}; + +static int __init xenhcd_init(void) +{ + if (!xen_domain()) + return -ENODEV; + + xenhcd_urbp_cachep =3D 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"); --=20 2.26.2 From nobody Tue Apr 16 16:25:08 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=quarantine dis=none) header.from=suse.com ARC-Seal: i=1; a=rsa-sha256; t=1637673679; cv=none; d=zohomail.com; s=zohoarc; b=Z3NaNo1K9cP5YJCLAmOv9Gn+n52Bdxv4WvBHCIe3sB31/CTOP7J4u+760D9ZRqD52MD81zlrGr6ekMD9mgy3pzYHldQaQr3wo98h/KeRVKhZ7aCKdKCMScT8Ejd7/cDcpDQJ+aFZrEQ76NHlQBunKRFb6/1p6dgPnmkfEvWMcHY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1637673679; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=ydq29op3Uut8kGUc+uMZHwfZgLUyDFkJcJa0BA/hp2o=; b=detPB6ai1yY0ZJ+3fcf0X9r0BZwTZtDavI+bDbBKAQkM9jhabcI3Vv5dETceJ5S86E/Zw8mzAJbSBE0ZjIaaKcosf5XsMIOOWt3xXVrBzyUhTBbx4ueOhew2Nue4h7YnQJuj/oP1CELLm9h3LNA1c4l3D7FEuNgShFK8N9nYJ4M= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1637673679652948.3086170878715; Tue, 23 Nov 2021 05:21:19 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.229581.397004 (Exim 4.92) (envelope-from ) id 1mpVjD-0002nx-5W; Tue, 23 Nov 2021 13:20:55 +0000 Received: by outflank-mailman (output) from mailman id 229581.397004; Tue, 23 Nov 2021 13:20:55 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1mpVjD-0002nq-1q; Tue, 23 Nov 2021 13:20:55 +0000 Received: by outflank-mailman (input) for mailman id 229581; Tue, 23 Nov 2021 13:20:53 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1mpVjB-0002Xw-Cx for xen-devel@lists.xenproject.org; Tue, 23 Nov 2021 13:20:53 +0000 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.220.28]) by se1-gles-flk1.inumbo.com (Halon) with ESMTPS id 2e06a899-4c60-11ec-a9d2-d9f7a1cc8784; Tue, 23 Nov 2021 14:20:51 +0100 (CET) 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 4E28321954; Tue, 23 Nov 2021 13:20:51 +0000 (UTC) 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 2401413E08; Tue, 23 Nov 2021 13:20:51 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id 8J6vB7PqnGH9CgAAMHmgww (envelope-from ); Tue, 23 Nov 2021 13:20:51 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 2e06a899-4c60-11ec-a9d2-d9f7a1cc8784 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1637673651; 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=ydq29op3Uut8kGUc+uMZHwfZgLUyDFkJcJa0BA/hp2o=; b=aE2lIQcvn8yWSfbm78fERFlbWdgumlWSpB0YoGZ7L/CIcN/LI4T9kEzzbVgNcrqrqvpWZx w1nQqngyJ2tp7D8IOWuMF6PnxBMO4Q66+jAZhkS673UjP59gjXs0HGzPMrHRHQc9dfy6IP rxr/pcv+pVL9A9WYShqOMfI28e3/hNU= From: Juergen Gross To: xen-devel@lists.xenproject.org, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Juergen Gross , Greg Kroah-Hartman , Konrad Rzeszutek Wilk Subject: [PATCH v7 3/3] xen: add Xen pvUSB maintainer Date: Tue, 23 Nov 2021 14:20:48 +0100 Message-Id: <20211123132048.5335-4-jgross@suse.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20211123132048.5335-1-jgross@suse.com> References: <20211123132048.5335-1-jgross@suse.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @suse.com) X-ZM-MESSAGEID: 1637673681668100001 Content-Type: text/plain; charset="utf-8" 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 5250298d2817..da7fd3d32dc4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20831,6 +20831,14 @@ F: drivers/scsi/xen-scsifront.c F: drivers/xen/xen-scsiback.c F: include/xen/interface/io/vscsiif.h =20 +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) --=20 2.26.2