changeset 18357:11a2d819935f

Merge branch 'master' of git://github.com/illumos/illumos-gate
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Fri, 10 Mar 2017 18:57:44 -0500
parents 62650b1d47fb (current diff) bd4d8900145e (diff)
children 48522e7e119a ac1492488560
files include/sys/cmn_err.h include/sys/pci.h include/sys/policy.h include/sys/usb/clients/hid/hidvar.h include/sys/usb/hubd/hub.h include/sys/usb/hubd/hubdvar.h include/sys/usb/scsa2usb/scsa2usb.h include/sys/usb/usba/hcdi.h include/sys/usb/usba/hubdi.h include/sys/usb/usba/usba_types.h include/sys/usb/usba/usba_ugend.h include/sys/usb/usbai.h kernel/os/policy.c kernel/os/printf.c share/man/man1m/cfgadm_usb.1m share/man/man7d/hubd.7d share/man/man7d/ugen.7d share/man/man7d/usba.7d share/man/man9f/Makefile share/man/man9f/usb_client_attach.9f share/man/man9f/usb_ep_xdescr_fill.9f share/man/man9f/usb_get_addr.9f share/man/man9f/usb_get_cfg.9f share/man/man9f/usb_get_dev_data.9f share/man/man9f/usb_lookup_ep_data.9f share/man/man9f/usb_parse_data.9f share/man/man9f/usb_pipe_bulk_xfer.9f share/man/man9f/usb_pipe_close.9f share/man/man9f/usb_pipe_ctrl_xfer.9f share/man/man9f/usb_pipe_get_state.9f share/man/man9f/usb_pipe_intr_xfer.9f share/man/man9f/usb_pipe_isoc_xfer.9f share/man/man9f/usb_pipe_open.9f share/man/man9f/usb_pipe_reset.9f share/man/man9f/usb_pipe_set_private.9f share/man/man9f/usb_pipe_xopen.9f share/man/man9f/usb_reset_device.9f share/man/man9s/Makefile share/man/man9s/usb_bulk_req.9s share/man/man9s/usb_bulk_request.9s share/man/man9s/usb_client_dev_data.9s share/man/man9s/usb_ctrl_req.9s share/man/man9s/usb_ctrl_request.9s share/man/man9s/usb_ep_ss_comp_descr.9s share/man/man9s/usb_ep_xdescr.9s share/man/man9s/usb_intr_req.9s share/man/man9s/usb_intr_request.9s share/man/man9s/usb_isoc_req.9s share/man/man9s/usb_isoc_request.9s usr/src/boot/sys/boot/common/interp_forth.c usr/src/boot/sys/boot/efi/boot1/Makefile usr/src/boot/sys/boot/efi/loader/Makefile usr/src/boot/sys/boot/efi/loader/main.c usr/src/boot/sys/boot/efi/loader/version usr/src/boot/sys/boot/i386/gptzfsboot/Makefile usr/src/boot/sys/boot/i386/loader/Makefile usr/src/boot/sys/boot/i386/loader/main.c usr/src/boot/sys/boot/i386/loader/version usr/src/cmd/Makefile usr/src/cmd/mdb/common/kmdb/mapfile_skel usr/src/cmd/mdb/common/modules/usba/usb.c usr/src/cmd/mdb/intel/modules/xhci/Makefile usr/src/cmd/mdb/intel/modules/xhci/xhci.c usr/src/pkg/manifests/driver-usb.mf usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/usb/clients/hid/hid.c usr/src/uts/common/io/usb/scsa2usb/scsa2usb.c usr/src/uts/common/io/usb/usba/hcdi.c usr/src/uts/common/io/usb/usba/usba.c usr/src/uts/common/io/usb/usba/usbai_pipe_mgmt.c usr/src/uts/intel/Makefile.intel usr/src/uts/intel/xhci/Makefile
diffstat 111 files changed, 15352 insertions(+), 2153 deletions(-) [+]
line wrap: on
line diff
--- a/include/sys/cmn_err.h	Fri Mar 10 17:02:49 2017 +0200
+++ b/include/sys/cmn_err.h	Fri Mar 10 18:57:44 2017 -0500
@@ -28,6 +28,7 @@
  * Use is subject to license terms.
  *
  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef _SYS_CMN_ERR_H
@@ -63,6 +64,9 @@
 extern void dev_err(dev_info_t *, int, char *, ...)
     __KPRINTFLIKE(3);
 
+extern void vdev_err(dev_info_t *, int, const char *, __va_list)
+    __KVPRINTFLIKE(3);
+
 extern void vcmn_err(int, const char *, __va_list)
     __KVPRINTFLIKE(2);
 
--- a/include/sys/pci.h	Fri Mar 10 17:02:49 2017 +0200
+++ b/include/sys/pci.h	Fri Mar 10 18:57:44 2017 -0500
@@ -1234,6 +1234,13 @@
  */
 #define	PCI_BUS_CONF_MAP_PROP	"pci-conf-indirect"
 
+/*
+ * PCI returns all 1s for an invalid read.
+ */
+#define	PCI_EINVAL8	0xff
+#define	PCI_EINVAL16	0xffff
+#define	PCI_EINVAL32	0xffffffff
+
 #ifdef	__cplusplus
 }
 #endif
--- a/include/sys/policy.h	Fri Mar 10 17:02:49 2017 +0200
+++ b/include/sys/policy.h	Fri Mar 10 18:57:44 2017 -0500
@@ -162,6 +162,7 @@
 int secpolicy_vnode_setids_setgids(const cred_t *, gid_t);
 int secpolicy_vnode_stky_modify(const cred_t *);
 int secpolicy_vscan(const cred_t *);
+int secpolicy_xhci(const cred_t *);
 int secpolicy_zinject(const cred_t *);
 int secpolicy_zfs(const cred_t *);
 int secpolicy_ucode_update(const cred_t *);
--- a/include/sys/usb/clients/hid/hidvar.h	Fri Mar 10 17:02:49 2017 +0200
+++ b/include/sys/usb/clients/hid/hidvar.h	Fri Mar 10 18:57:44 2017 -0500
@@ -190,7 +190,7 @@
 
 	usb_if_descr_t		hid_if_descr;		/* interface descr */
 	usb_hid_descr_t		hid_hid_descr;		/* hid descriptor */
-	usb_ep_descr_t		hid_ep_intr_descr;
+	usb_ep_xdescr_t		hid_ep_intr_xdescr;	/* ep extended desc */
 	hidparser_handle_t	hid_report_descr;	/* report descr */
 
 	usb_pipe_handle_t	hid_default_pipe;	/* default pipe */
--- a/include/sys/usb/hubd/hub.h	Fri Mar 10 17:02:49 2017 +0200
+++ b/include/sys/usb/hubd/hub.h	Fri Mar 10 18:57:44 2017 -0500
@@ -21,6 +21,8 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef	_SYS_USB_HUB_H
@@ -31,7 +33,7 @@
 extern "C" {
 #endif
 
-#define	USB_DESCR_TYPE_SETUP_HUB	0x2900
+#define	HUBD_DEFAULT_DESC_INDEX		0
 
 /*
  * Section 11.11.2.1 allows up to 255 ports.
@@ -54,8 +56,30 @@
 					/* power control mask */
 } usb_hub_descr_t;
 
+/*
+ * In USB 3.x the format of the root hub description has changed. See USB 3.1 /
+ * 10.15.12.1.
+ */
+#pragma pack(1)
+typedef struct usb_ss_hub_descr {
+	uint8_t		bDescLength;		/* size of descriptor */
+	uint8_t		bDescriptorType;	/* descriptor type (0x2A) */
+	uint8_t		bNbrPorts;		/* number of ports */
+	uint16_t	wHubCharacteristics;	/* hub characteristics */
+	uint8_t		bPwrOn2PwrGood;	/* time in 2-ms from power on */
+						/* until the port is ready */
+	uint8_t		bHubContrCurrent;	/* max current requirements */
+	uint8_t		bHubHdrDecLat;		/* hub packet decode latency */
+	uint16_t	wHubDelay;		/* Forwarding delay in ns */
+	uint16_t	DeviceRemovable[32];	/* indicates per-port whether */
+					/* the device is removable with one */
+					/* bit per port, up to 255 ports */
+} usb_ss_hub_descr_t;
+#pragma pack()
+
 #define	ROOT_HUB_DESCRIPTOR_LENGTH	9
 #define	ROOT_HUB_DESCRIPTOR_TYPE	0x29
+#define	ROOT_HUB_SS_DESCRIPTOR_TYPE	0x2A
 #define	ROOT_HUB_ADDR			0x01	/* address of root hub */
 
 /* Values for wHubCharacteristics */
@@ -73,6 +97,8 @@
 #define	HUB_CHARS_TT_32FS_TIME		0x60
 #define	HUB_CHARS_PORT_INDICATOR	0x80
 
+#define	HUB_CHARS_TT_SHIFT		5
+
 /* Default Power On to Power Good time */
 #define	HUB_DEFAULT_POPG	10
 
@@ -93,25 +119,54 @@
 
 #define	HUB_HANDLE_HUB_FEATURE_TYPE	USB_DEV_REQ_TYPE_CLASS
 
+#define	HUB_SET_HUB_DEPTH_TYPE		(USB_DEV_REQ_HOST_TO_DEV \
+					|USB_DEV_REQ_TYPE_CLASS \
+					|USB_DEV_REQ_RCPT_DEV)
+
 /* bmRequestType for getting device status */
 #define	HUB_GET_DEVICE_STATUS_TYPE	(USB_DEV_REQ_DEV_TO_HOST \
 					|USB_DEV_REQ_TYPE_STANDARD \
 					|USB_DEV_REQ_RCPT_DEV)
 
-/* Port Status Field Bits - Table 11-15 */
+/*
+ * Class specific bRequest values that don't line up with standard requests. See
+ * USB 3.1 / Table 10-8.
+ */
+#define	HUB_REQ_SET_HUB_DEPTH		12
+
+/*
+ * Port Status Field Bits. While there is overlap between the USB 2.0 and USB
+ * 3.0 bits, they aren't entirely the same and some bits have different meanings
+ * across different versions of USB. Common bits are shared first and then this
+ * is broken down into device specific bits. The USB 3 version is in USB
+ * 3.1/10.16.2.6.1. The USB 2 version is in USB 2/11.24.2.7.1.
+ */
 #define	PORT_STATUS_CCS		0x0001	/* port connection status */
 #define	PORT_STATUS_PES		0x0002	/* port enable status */
 #define	PORT_STATUS_PSS		0x0004	/* port suspend status */
 #define	PORT_STATUS_POCI	0x0008	/* port over current indicator */
 #define	PORT_STATUS_PRS		0x0010	/* port reset status */
 #define	PORT_STATUS_PPS		0x0100	/* port power status */
+
+/* USB 2.0 specific bits */
 #define	PORT_STATUS_LSDA	0x0200	/* low speed device */
 #define	PORT_STATUS_HSDA	0x0400	/* high speed device */
 #define	PORT_STATUS_PIC		0x1000	/* port indicator control */
 
+/*
+ * The USB 2.0 and USB 3.0 port status bits are almost identical; however, the
+ * location of the port's power indicator is different for hubs. To deal with
+ * this, we have logic, hubd_status_unifornm, that transforms the USB 3 status
+ * to USB 2, hence why we only have one version of these macros below.
+ */
 #define	PORT_STATUS_MASK	0x171f
 #define	PORT_STATUS_OK		0x103	/* connected, enabled, power */
 
+/* USB 3 Specific bits */
+#define	PORT_STATUS_PPS_SS	0x0200	/* USB 3.0 port power status */
+#define	PORT_STATUS_SPMASK_SS	0x1c00
+#define	PORT_STATUS_SPSHIFT_SS	10
+
 /* Port Change Field Bits - Table 11-16 */
 #define	PORT_CHANGE_CSC		0x0001	/* connect status change */
 #define	PORT_CHANGE_PESC	0x0002	/* port enable change */
@@ -119,9 +174,34 @@
 #define	PORT_CHANGE_OCIC	0x0008	/* over current change */
 #define	PORT_CHANGE_PRSC	0x0010	/* port reset change */
 
-#define	PORT_CHANGE_MASK	0x001f
+/*
+ * USB 3.x additions. See USB 3.1/10.16.2.6.2.
+ */
+#define	PORT_CHANGE_BHPR	0x0020	/* warm reset (BH) */
+#define	PORT_CHANGE_PLSC	0x0040	/* port link state change */
+#define	PORT_CHANGE_PCE		0x0080	/* port config error */
 
-/* Hub status Field Bits - Table 11-14 */
+/*
+ * These represent masks for all of the change bits. Note that the USB 2 version
+ * has less than the USB 3. The _2X version of the macro is maintained for
+ * things that don't know about more than USB 2 (ehci).
+ */
+#define	PORT_CHANGE_MASK_2X	0x001f
+#define	PORT_CHANGE_MASK	0x00ff
+
+/*
+ * Port status types and sizes USB 3.1/Table 10-12.
+ */
+#define	PORT_GET_STATUS_PORT	0x00
+#define	PORT_GET_STATUS_PD	0x01
+#define	PORT_GET_STATUS_EXT	0x02
+
+#define	PORT_GET_STATUS_PORT_LEN	0x04
+#define	PORT_GET_STATUS_PD_LEN		0x08
+#define	PORT_GET_STATUS_EXT_LEN		0x08
+
+/* Hub status information USB 3.1/11.24.2.6 */
+#define	HUB_GET_STATUS_LEN	0x04
 #define	HUB_LOCAL_POWER_STATUS	0x0001	/* state of the power supply */
 #define	HUB_OVER_CURRENT	0x0002  /* global hub OC condition */
 
@@ -137,6 +217,7 @@
 #define	CFS_PORT_SUSPEND		2
 #define	CFS_PORT_OVER_CURRENT		3
 #define	CFS_PORT_RESET			4
+#define	CFS_PORT_LINK_STATE		5
 #define	CFS_PORT_POWER			8
 #define	CFS_PORT_LOW_SPEED		9
 #define	CFS_C_PORT_CONNECTION		16
@@ -144,6 +225,27 @@
 #define	CFS_C_PORT_SUSPEND		18
 #define	CFS_C_PORT_OVER_CURRENT 	19
 #define	CFS_C_PORT_RESET		20
+#define	CFS_PORT_TEST			21
+#define	CFS_PORT_INDICATOR		22
+
+/*
+ * SuperSpeed specific HUB features. See USB 3.1 / 10.16.2.
+ */
+#define	CFS_PORT_U1_TIMEOUT		23
+#define	CFS_PORT_U2_TIMEOUT		24
+#define	CFS_C_PORT_LINK_STATE		25
+#define	CFS_C_PORT_CONFIG_ERROR		26
+#define	CFS_PORT_REMOTE_WAKE_MASK	27
+#define	CFS_BH_PORT_RESET		28
+#define	CFS_C_BH_PORT_RESET		29
+#define	CFS_FORCE_LINKPM_ACCEPT		30
+
+/*
+ * Values for CFS_PORT_REMOTE_WAKE_MASK. See USB 3.1 / Table 10-18.
+ */
+#define	CFS_PRWM_CONN_ENABLE		0x01
+#define	CFS_PRWM_DISCONN_ENABLE		0x02
+#define	CFS_PRWM_OC_ENABLE		0x04
 
 #ifdef	__cplusplus
 }
--- a/include/sys/usb/hubd/hubdvar.h	Fri Mar 10 17:02:49 2017 +0200
+++ b/include/sys/usb/hubd/hubdvar.h	Fri Mar 10 18:57:44 2017 -0500
@@ -24,6 +24,7 @@
  */
 /*
  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef	_SYS_USB_HUBDVAR_H
@@ -156,14 +157,18 @@
 	 * pipe handle for ep1
 	 */
 	usb_pipe_handle_t	h_ep1_ph;
-	usb_ep_descr_t		h_ep1_descr;
+	usb_ep_xdescr_t		h_ep1_xdescr;
 	usb_pipe_policy_t	h_pipe_policy;
 	uint_t			h_intr_pipe_state;
 
 	/*
-	 * root hub descriptor
+	 * hub characteristics (normalized across various USB versions) from the
+	 * Hub class description.
 	 */
-	struct usb_hub_descr	h_hub_descr;
+	uint8_t			h_nports;	/* from bNbrPorts */
+	uint16_t		h_hub_chars;	/* from wHubCharacteristics */
+	uint_t			h_power_good;	/* from bPwrOn2PwrGood */
+	uint_t			h_current;	/* from bHubContrCurrent */
 
 	/*
 	 * hotplug handling
@@ -194,6 +199,9 @@
 	/* track event registration of children */
 	uint8_t			h_child_events[MAX_PORTS + 1];
 
+	/* track the raw port state for debugging purposes */
+	uint16_t		h_port_raw[MAX_PORTS + 1];
+
 	kcondvar_t		h_cv_reset_port;
 	kcondvar_t		h_cv_hotplug_dev;
 	uint_t			h_intr_completion_reason;
@@ -402,6 +410,16 @@
  */
 #define	USB_CFG_DESCR_PWR_UNIT	2
 
+/*
+ * USB 3.x devices have the notion of a 'route' which describes the series of
+ * hubs which must be passed through to reach a given device. The route string
+ * has support for a fixed number of nested hubs. Each USB 3.x hub has to be
+ * told what its depth in the route string is, effectively it's 4-bit index into
+ * the route string. The maximum number of nested hubs, in other words a hub's
+ * depth, is defined in USB 3.1 / 10.16.2.9.
+ */
+#define	HUBD_SS_MAX_DEPTH	5
+
 /* variables shared with wire adapter class drivers */
 extern uint_t hubd_errlevel;
 extern uint_t hubd_errmask;
--- a/include/sys/usb/scsa2usb/scsa2usb.h	Fri Mar 10 17:02:49 2017 +0200
+++ b/include/sys/usb/scsa2usb/scsa2usb.h	Fri Mar 10 18:57:44 2017 -0500
@@ -20,6 +20,8 @@
  *
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef _SYS_USB_SCSA2USB_H
@@ -303,9 +305,9 @@
 	struct scsi_inquiry	scsa2usb_lun_inquiry[SCSA2USB_MAX_LUNS];
 						/* store inquiry per LUN  */
 	usb_if_descr_t		scsa2usb_intfc_descr;	/* Interface descr    */
-	usb_ep_descr_t		scsa2usb_bulkin_ept;	/* Bulk In descriptor */
-	usb_ep_descr_t		scsa2usb_bulkout_ept;	/* Bulkout descriptor */
-	usb_ep_descr_t		scsa2usb_intr_ept;	/* Intr ept descr */
+	usb_ep_xdescr_t		scsa2usb_bulkin_xept;	/* Bulk In descriptor */
+	usb_ep_xdescr_t		scsa2usb_bulkout_xept;	/* Bulkout descriptor */
+	usb_ep_xdescr_t		scsa2usb_intr_xept;	/* Intr ept descr */
 
 	usb_pipe_handle_t	scsa2usb_default_pipe;	/* Default pipe	Hndle */
 	usb_pipe_handle_t	scsa2usb_intr_pipe;	/* Intr polling Hndle */
@@ -351,6 +353,15 @@
 	uint8_t			scsa2usb_clones[SCSA2USB_MAX_CLONE];
 } scsa2usb_state_t;
 
+/*
+ * These macros were added as part of updating scsa2usb to support USB 3.0 and
+ * newer devices to minimize driver changes. There's no reason these can't be
+ * expanded by someone who wants to.
+ */
+#define	scsa2usb_bulkin_ept	scsa2usb_bulkin_xept.uex_ep
+#define	scsa2usb_bulkout_ept	scsa2usb_bulkout_xept.uex_ep
+#define	scsa2usb_intr_ept	scsa2usb_intr_xept.uex_ep
+
 
 /* scsa2usb_pipe_state values */
 #define	SCSA2USB_PIPE_NORMAL		0x00	/* no reset or clearing	*/
--- a/include/sys/usb/usba/hcdi.h	Fri Mar 10 17:02:49 2017 +0200
+++ b/include/sys/usb/usba/hcdi.h	Fri Mar 10 18:57:44 2017 -0500
@@ -21,6 +21,8 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef	_SYS_USB_HCDI_H
@@ -50,7 +52,8 @@
  */
 #define	HCDI_OPS_VERSION_0 0
 #define	HCDI_OPS_VERSION_1 1
-#define	HCDI_OPS_VERSION	HCDI_OPS_VERSION_1
+#define	HCDI_OPS_VERSION_2 2
+#define	HCDI_OPS_VERSION	HCDI_OPS_VERSION_2
 
 typedef struct usba_hcdi_ops {
 	int	usba_hcdi_ops_version;	/* implementation version */
@@ -207,6 +210,26 @@
 
 	int	(*usba_hcdi_console_output_exit)(
 		usb_console_info_impl_t		*console_output_info);
+
+	/*
+	 * VERSION 2 ops: support for device initialization
+	 */
+	int	(*usba_hcdi_device_init)(
+		usba_device_t			*usba_device,
+		usb_port_t			port,
+		void				**);
+
+	void	(*usba_hcdi_device_fini)(
+		usba_device_t			*usba_device,
+		void				*);
+
+	int	(*usba_hcdi_device_address)(
+		usba_device_t			*usba_device);
+
+	int	(*usba_hcdi_hub_update)(
+		usba_device_t			*usba_device,
+		uint8_t				nports,
+		uint8_t				think_time);
 } usba_hcdi_ops_t;
 
 
@@ -224,8 +247,8 @@
  */
 void
 usba_hcdi_cb(usba_pipe_handle_data_t	*ph,
-		usb_opaque_t		req,
-		usb_cr_t		completion_reason);
+    usb_opaque_t		req,
+    usb_cr_t		completion_reason);
 
 /*
  * function to duplicate a interrupt/isoc request (for HCD)
@@ -266,7 +289,6 @@
 	usba_hcdi_ops_t		*usba_hcdi_register_ops;
 	ddi_dma_attr_t		*usba_hcdi_register_dma_attr;
 	ddi_iblock_cookie_t	usba_hcdi_register_iblock_cookie;
-
 } usba_hcdi_register_args_t;
 
 #define	HCDI_REGISTER_VERS_0		0
@@ -286,6 +308,11 @@
 void	usba_hcdi_unregister(dev_info_t *);
 
 /*
+ * HCD device private storage
+ */
+void	*usba_hcdi_get_device_private(usba_device_t *);
+
+/*
  * Hotplug kstats named structure
  *
  * Number of types of USB transfers
@@ -340,6 +367,12 @@
 	((hcdi_error_stats_t *)HCDI_ERROR_STATS((hcdi))->ks_data)
 
 
+/*
+ * The default timeout that should occur for non-periodic transfers if a timeout
+ * is not requested by a client driver. This is a time in seconds.
+ */
+#define	HCDI_DEFAULT_TIMEOUT	5
+
 #ifdef __cplusplus
 }
 #endif
--- a/include/sys/usb/usba/hubdi.h	Fri Mar 10 17:02:49 2017 +0200
+++ b/include/sys/usb/usba/hubdi.h	Fri Mar 10 18:57:44 2017 -0500
@@ -21,6 +21,8 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef	_SYS_USB_HUBDI_H
@@ -45,6 +47,7 @@
 int usba_hubdi_close(dev_info_t *, dev_t, int, int, cred_t *);
 int usba_hubdi_ioctl(dev_info_t *, dev_t, int, intptr_t, int,
 						cred_t *, int *);
+int usba_hubdi_root_hub_power(dev_info_t *, int, int);
 
 extern struct bus_ops usba_hubdi_busops;
 
--- a/include/sys/usb/usba/usba_types.h	Fri Mar 10 17:02:49 2017 +0200
+++ b/include/sys/usb/usba/usba_types.h	Fri Mar 10 18:57:44 2017 -0500
@@ -86,9 +86,16 @@
 	/* shared usba_device structure */
 	struct usba_device	*p_usba_device;	/* set on pipe open */
 
-	/* pipe policy and endpoint descriptor for this pipe */
+	/*
+	 * Pipe policy and endpoint descriptor for this pipe
+	 *
+	 * Both the basic and extended endpoints are kept around even though
+	 * we're duplicating data as most of the HCI drivers are relying on the
+	 * presence of p_ep.
+	 */
 	usb_pipe_policy_t	p_policy;	/* maintained by USBA */
 	usb_ep_descr_t		p_ep;
+	usb_ep_xdescr_t		p_xep;
 
 	/* passed during open. needed for reset etc. */
 	dev_info_t		*p_dip;
@@ -195,9 +202,15 @@
 typedef uint16_t usb_port_t;
 typedef uint32_t usb_port_mask_t;
 
+/*
+ * Note, faster speeds should always be in increasing values. Various parts of
+ * the stack use >= comparisons for things which are true for say anything equal
+ * to or greater than USB 2.0.
+ */
 #define	USBA_LOW_SPEED_DEV	0x1
 #define	USBA_FULL_SPEED_DEV	0x2
 #define	USBA_HIGH_SPEED_DEV	0x3
+#define	USBA_SUPER_SPEED_DEV	0x4
 
 /*
  * NDI event is registered on a per-dip basis. usba_device can be
@@ -334,6 +347,22 @@
 	taskq_t			*usb_shared_taskq[USBA_N_ENDPOINTS];
 	uchar_t			usb_shared_taskq_ref_count
 						[USBA_N_ENDPOINTS];
+
+	/*
+	 * Pointer to hub this is under. This is required for some HCDs to
+	 * accurately set up the device. Note that some usba_device_t's are
+	 * shared by multiple entries, so this is not strictly the parent
+	 * device. This would come up if the usb_mid driver was on the scene.
+	 * Importantly, this field is always read-only. While this is similar to
+	 * the usb_hs_hub_usba_dev, it's always set, regardless if it's a high
+	 * speed device or not.
+	 */
+	struct usba_device	*usb_parent_hub;
+
+	/*
+	 * Private data for HCD drivers
+	 */
+	void			*usb_hcd_private;
 } usba_device_t;
 
 #define	USBA_CLIENT_FLAG_SIZE		1
--- a/include/sys/usb/usba/usba_ugend.h	Fri Mar 10 17:02:49 2017 +0200
+++ b/include/sys/usb/usba/usba_ugend.h	Fri Mar 10 18:57:44 2017 -0500
@@ -181,6 +181,7 @@
 typedef struct ugen_ep {
 	uint_t		ep_state;	/* Endpoint state, see below */
 	usb_ep_descr_t	ep_descr;	/* Endpoint descriptor */
+	usb_ep_xdescr_t	ep_xdescr;	/* Extended endpoint descriptor */
 	uchar_t		ep_cfgidx;	/* cfg index */
 	uchar_t		ep_if;		/* Interface # */
 	uchar_t		ep_alt;		/* alternate # */
--- a/include/sys/usb/usbai.h	Fri Mar 10 17:02:49 2017 +0200
+++ b/include/sys/usb/usbai.h	Fri Mar 10 18:57:44 2017 -0500
@@ -23,6 +23,7 @@
  * Use is subject to license terms.
  *
  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef	_SYS_USB_USBAI_H
@@ -33,9 +34,9 @@
 extern "C" {
 #endif
 
-/* This header file is for USBA2.0 */
+/* This header file is for USBA2.1 */
 #define	USBA_MAJOR_VER 2
-#define	USBA_MINOR_VER 0
+#define	USBA_MINOR_VER 1
 
 /*
  * USBAI: Interfaces Between USBA and Client Driver
@@ -194,7 +195,7 @@
 
 /*
  * ***************************************************************************
- * Descriptor definitions (from USB 2.0 specification, chapter 9)
+ * Descriptor definitions (USB version and section noted with structure)
  * ***************************************************************************
  */
 
@@ -400,9 +401,9 @@
 /*
  * wMaxPacketSize values for endpoints (isoch and interrupt, high speed only)
  */
-#define	USB_EP_MAX_PKTSZ_MASK	0x03FF		/* Mask for packetsize bits */
-#define	USB_EP_MAX_XACTS_MASK	0x0C00		/* Max Transactns/microframe */
-#define	USB_EP_MAX_XACTS_SHIFT	10		/* Above is 10 bits from end */
+#define	USB_EP_MAX_PKTSZ_MASK	0x07FF		/* Mask for packetsize bits */
+#define	USB_EP_MAX_XACTS_MASK	0x1800		/* Max Transactns/microframe */
+#define	USB_EP_MAX_XACTS_SHIFT	11		/* Above is 10 bits from end */
 
 /*
  * Ranges for endpoint parameter values.
@@ -448,6 +449,21 @@
 #define	USB_MAXSTRINGLEN	255		/* max string descr length */
 
 /*
+ * usb_ep_ss_comp_descr:
+ * 	USB SuperSpeed endpoints are required to return this descriptor along
+ * 	with the general endpoint descriptor. Refer to USB 3.1/9.6.7.
+ */
+typedef struct usb_ep_ss_comp_descr {
+	uint8_t		bLength;		/* descriptor size */
+	uint8_t		bDescriptorType;	/* USB_DESCR_TYPE_SS_EP_COMP */
+	uint8_t		bMaxBurst;		/* max packets per burst */
+	uint8_t		bmAttributes;		/* more endpoint attributes */
+	uint16_t	wBytesPerInterval;	/* bytes per service interval */
+} usb_ep_ss_comp_descr_t;
+
+#define	USB_EP_SS_COMP_ISOC_MULT_MASK	0x03
+
+/*
  * ***************************************************************************
  * Client driver registration with USBA
  * ***************************************************************************
@@ -519,6 +535,8 @@
 	usb_ep_descr_t		ep_descr;	/* endpoint descriptor */
 	struct usb_cvs_data	*ep_cvs;	/* cv mod/extending this ep */
 	uint_t			ep_n_cvs;	/* #elements in ep_cvs[] */
+	boolean_t		ep_ss_valid;
+	usb_ep_ss_comp_descr_t	ep_ss_comp;	/* superspeed ep desc */
 } usb_ep_data_t;
 
 
@@ -830,6 +848,57 @@
 	char			*buf,
 	size_t			buflen);
 
+/*
+ * With the advent of USB 3.x, several endpoint compantion descriptors have been
+ * added. These provide additional information required by HCI drivers to
+ * properly open and configure the pipes.
+ */
+
+/*
+ * usb_ep_xdescr
+ *
+ * 	Versioned data structure that's used for usb_pipe_xopen() and should be
+ * 	filled in by a call to usb_ep_xdescr_fill(). Drivers should always use
+ * 	USB_EP_XDESCR_CURRENT_VERSION.
+ */
+
+#define	USB_EP_XDESCR_VERSION_ONE	1
+#define	USB_EP_XDESCR_CURRENT_VERSION	USB_EP_XDESCR_VERSION_ONE
+
+typedef enum usb_ep_xdescr_flags {
+	USB_EP_XFLAGS_SS_COMP	= (1 << 0)
+} usb_ep_xdescr_flags_t;
+
+typedef struct usb_ep_xdescr {
+	uint_t			uex_version;
+	usb_ep_xdescr_flags_t	uex_flags;
+	usb_ep_descr_t		uex_ep;
+	usb_ep_ss_comp_descr_t	uex_ep_ss;
+} usb_ep_xdescr_t;
+
+/*
+ * usb_ep_xdescr_fill:
+ *
+ * Fills in the extended endpoint descriptor based on data from the
+ * configuration tree.
+ *
+ * Arguments:
+ * 	version		- Should be USB_EP_XDESCR_CURRENT_VERSION
+ * 	dip		- devinfo pointer
+ * 	ep_data		- endpoint data pointer
+ * 	ep_xdesc	- An extended descriptor structure, filled upon
+ *                        successful completion.
+ *
+ * Return values:
+ *	USB_SUCCESS	 - filling data succeeded
+ *	USB_INVALID_ARGS - invalid arguments
+ */
+int usb_ep_xdescr_fill(
+	uint_t 		version,
+	dev_info_t	*dip,
+	usb_ep_data_t	*ep_data,
+	usb_ep_xdescr_t	*ep_xdesc);
+
 
 /*
  * ***************************************************************************
@@ -972,7 +1041,6 @@
 	usb_pipe_state_t	*pipe_state,
 	usb_flags_t		flags);
 
-
 /*
  * usb_pipe_policy
  *
@@ -991,7 +1059,7 @@
 
 
 /*
- * usb_pipe_open():
+ * usb_pipe_open() and usb_pipe_xopen():
  *
  * Before using any pipe including the default pipe, it must be opened.
  * On success, a pipe handle is returned for use in other usb_pipe_*()
@@ -1006,6 +1074,9 @@
  * excusively opened by default.  A pipe policy and endpoint descriptor
  * must always be provided except for default pipe.
  *
+ * usb_pipe_open() only functions for USB 2.0 and older devices. For USB 3.0
+ * "SuperSpeed" devices, usb_pipe_xopen() must be used.
+ *
  * Arguments:
  *	dip		- devinfo ptr.
  *	ep		- endpoint descriptor pointer.
@@ -1021,6 +1092,7 @@
  *	USB_FAILURE	 - unspecified open failure or pipe is already open.
  *	USB_NO_RESOURCES - no resources were available to complete the open.
  *	USB_NO_BANDWIDTH - no bandwidth available (isoc/intr pipes).
+ *	USB_NOT_SUPPORTED - USB 3.0 or greater device
  *	USB_*		 - refer to list of all possible return values in
  *			   this file
  */
@@ -1031,6 +1103,13 @@
 	usb_flags_t		flags,
 	usb_pipe_handle_t	*pipe_handle);
 
+int usb_pipe_xopen(
+	dev_info_t		*dip,
+	usb_ep_xdescr_t		*ep_xdesc,
+	usb_pipe_policy_t	*pipe_policy,
+	usb_flags_t		flags,
+	usb_pipe_handle_t	*pipe_handle);
+
 
 /*
  * usb_pipe_close():
@@ -1346,6 +1425,7 @@
 
 #define	USB_DESCR_TYPE_WA			0x21
 #define	USB_DESCR_TYPE_RPIPE			0x22
+#define	USB_DESCR_TYPE_HUB			0x29
 
 /* Wireless USB extension, refer to WUSB 1.0/7.4 */
 #define	USB_DESCR_TYPE_SECURITY			0x0c
@@ -1359,6 +1439,13 @@
 #define	USB_RPIPE_DESCR_SIZE			28
 
 /*
+ * USB 3.0 Super Speed specifics. See USB 3.1/9.4.
+ */
+#define	USB_DESCR_TYPE_SS_HUB			0x2A
+#define	USB_DESCR_TYPE_SS_EP_COMP		0x30
+#define	USB_DESCR_TYPE_SS_ISO_EP_COMP		0x31
+
+/*
  * device request type
  */
 #define	USB_DEV_REQ_HOST_TO_DEV		0x00
@@ -1543,8 +1630,16 @@
  */
 
 /*
- *
- * Status bits returned by a usb_get_status().
+ * USB STATUS request types and sizes.
+ */
+#define	USB_GET_STATUS_STANDARD		0
+#define	USB_GET_STATUS_PTM		1
+
+#define	USB_GET_STATUS_LEN		2
+#define	USB_GET_STATUS_PTM_LEN		4
+
+/*
+ * Status bits returned by a usb_get_status() for a STATUS_STANDARD request.
  */
 #define	USB_DEV_SLF_PWRD_STATUS	1	/* Supports Self Power	 */
 #define	USB_DEV_RWAKEUP_STATUS	2	/* Remote Wakeup Enabled */
@@ -1552,9 +1647,6 @@
 #define	USB_EP_HALT_STATUS	1	/* Endpoint is Halted	 */
 #define	USB_IF_STATUS		0	/* Interface Status is 0 */
 
-/* length of data returned by USB_REQ_GET_STATUS */
-#define	USB_GET_STATUS_LEN		2
-
 /*
  * wrapper function returning status of device, interface, or endpoint
  *
@@ -2568,6 +2660,12 @@
 #define	USB_PROTO_MS_ISD_1999_SILICN	0x02    /* ZIP Protocol */
 #define	USB_PROTO_MS_BULK_ONLY		0x50    /* USB Bulk Only Protocol */
 
+/* Hub subclass and protocols */
+#define	USB_PROTO_HUB_FULL		0x00	/* Full Speed Protocol */
+#define	USB_PROTO_HUB_HIGH_STT		0x01	/* High Speed with STT */
+#define	USB_PROTO_HUB_HIGH_MTT		0x02	/* High Speed with MTT */
+#define	USB_PROTO_HUB_SUPER		0x03	/* SuperSpeed Protocol */
+
 /* Application subclasses. */
 #define	USB_SUBCLS_APP_FIRMWARE		0x01	/* app spec f/w subclass */
 #define	USB_SUBCLS_APP_IRDA		0x02	/* app spec IrDa subclass */
--- a/kernel/os/policy.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/kernel/os/policy.c	Fri Mar 10 18:57:44 2017 -0500
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  * Copyright (c) 2016 by Delphix. All rights reserved.
  */
 
@@ -2381,6 +2381,18 @@
 }
 
 /*
+ * secpolicy_xhci
+ *
+ * Determine if the subject can observe and manipulate the xhci driver with a
+ * dangerous blunt hammer.  Requires all privileges.
+ */
+int
+secpolicy_xhci(const cred_t *cr)
+{
+	return (secpolicy_require_set(cr, PRIV_FULLSET, NULL, KLPDARG_NONE));
+}
+
+/*
  * secpolicy_zinject
  *
  * Determine if the subject can inject faults in the ZFS fault injection
--- a/kernel/os/printf.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/kernel/os/printf.c	Fri Mar 10 18:57:44 2017 -0500
@@ -24,6 +24,7 @@
  * Use is subject to license terms.
  *
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/param.h>
@@ -271,6 +272,13 @@
 	vzdcmn_err(GLOBAL_ZONEID, caller(), ce, fmt, adx, NULL);
 }
 
+/*PRINTFLIKE3*/
+void
+vdev_err(dev_info_t *dip, int ce, const char *fmt, va_list adx)
+{
+	vzdcmn_err(GLOBAL_ZONEID, caller(), ce, fmt, adx, dip);
+}
+
 /*PRINTFLIKE2*/
 void
 cmn_err(int ce, const char *fmt, ...)
--- a/share/man/man1m/cfgadm_usb.1m	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man1m/cfgadm_usb.1m	Fri Mar 10 18:57:44 2017 -0500
@@ -30,7 +30,6 @@
 .fi
 
 .SH DESCRIPTION
-.sp
 .LP
 The Universal Serial Bus (\fBUSB\fR) hardware-specific library
 \fB/usr/lib/cfgadm/usb.so.1\fR provides the functionality for administering
@@ -124,14 +123,14 @@
 
 .sp
 .LP
-\fBUSB2.0\fR chips have one \fBEHCI\fR host \fBUSB2.0\fR host controller and a
+\fBUSB 2.0\fR chips have one \fBEHCI\fR host \fBUSB 2.0\fR host controller and a
 number of companion \fBUSB 1.\fR\fIx\fR host controllers (either \fBOHCI\fR or
 \fBUHCI\fR host controllers).
 .sp
 .LP
-When a \fBUSB2.0\fR device has been plugged in, it shows up on the \fBEHCI\fR
+When a \fBUSB 2.0\fR device has been plugged in, it shows up on the \fBEHCI\fR
 logical ports which might not have a \fB1\fR to \fB1\fR mapping to external
-physical port numbers on the system.  When a \fBUSB1.\fR\fIx\fR device is
+physical port numbers on the system.  When a \fBUSB 1.\fR\fIx\fR device is
 plugged in, the \fBEHCI\fR host controller reroutes the device to a companion
 host controller and the device shows up on  the companion's logical port
 number.
@@ -170,11 +169,11 @@
 .sp
 .LP
 In this example \fBusb0\fR is the onboard USB 1.\fIx\fR host controller.
-\fBusb1\fR and \fBusb2\fR are companion \fBOHCI USB1.\fR\fIx\fR host
-controllers and \fBusb3\fR is an \fBEHCI USB2.0\fR host controller.
+\fBusb1\fR and \fBusb2\fR are companion \fBOHCI USB 1.\fR\fIx\fR host
+controllers and \fBusb3\fR is an \fBEHCI USB 2.0\fR host controller.
 .sp
 .LP
-The following table shows the somewhat confusing routing for this USB2.0 chip:
+The following table shows the somewhat confusing routing for this USB 2.0 chip:
 .sp
 .in +2
 .nf
@@ -333,7 +332,6 @@
 to hot-remove devices currently in use (such as removable disks currently
 opened by a volume manager or some other application).
 .SH OPTIONS
-.sp
 .LP
 \fBcfgadm\fR defines several types of operations. These operations include
 invoking configuration state changes (\fB-c\fR), invoking hardware-specific
@@ -957,7 +955,6 @@
 field.
 
 .SH FILES
-.sp
 .ne 2
 .na
 \fB\fB/usr/lib/cfgadm/usb.so.1\fR\fR
@@ -968,7 +965,6 @@
 .RE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBcfgadm\fR(1M), \fBconfig_admin\fR(3CFGADM), \fBattributes\fR(5),
 \fBscsa2usb\fR(7D), \fBusba\fR(7D)
@@ -979,7 +975,6 @@
 .LP
 \fI\fR
 .SH NOTES
-.sp
 .LP
 \fBcfgadm\fR(1M) can not unconfigure, disconnect, reset, or change the
 configuration of any \fBUSB\fR device currently opened by any application.
--- a/share/man/man7d/hubd.7d	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man7d/hubd.7d	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH HUBD 7D "Jul 6, 2006"
+.TH HUBD 7D "Oct 4, 2016"
 .SH NAME
 hubd \- USB hub driver
 .SH SYNOPSIS
@@ -13,11 +13,10 @@
 .fi
 
 .SH DESCRIPTION
-.sp
 .LP
-The \fBhubd\fR is a USBA (Solaris USB Architecture) compliant client driver
+The \fBhubd\fR is a USBA (illumos USB Architecture) compliant client driver
 that supports USB hubs conforming to the \fIUniversal Serial Bus Specification
-2.0\fR. The \fBhubd\fR driver supports bus-powered and self-powered hubs. The
+3.0\fR. The \fBhubd\fR driver supports bus-powered and self-powered hubs. The
 driver supports hubs with individual port power, ganged power and no power
 switching.
 .sp
@@ -30,7 +29,6 @@
 configuration. When the device is disconnected from the hub port, the
 \fBhubd\fR driver offlines any driver instance attached to the device.
 .SH FILES
-.sp
 .ne 2
 .na
 \fB\fB/kernel/drv/hubd\fR\fR
@@ -58,7 +56,6 @@
 .RE
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for a description of the following attributes:
 .sp
@@ -74,7 +71,6 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBcfgadm_usb\fR(1M), \fBattributes\fR(5), \fBusba\fR(7D)
 .sp
@@ -90,7 +86,6 @@
 .LP
 http://\fIwww.sun.com/io\fR
 .SH DIAGNOSTICS
-.sp
 .LP
 In addition to being logged, the following messages may also appear on the
 system console. Messages are formatted in the following manner:
@@ -292,7 +287,7 @@
 .sp
 .ne 2
 .na
-\fBPort <\fIn\fR> in over current condition, please check the attached device
+\fBPort <\fIn\fR\fB> in over current condition, please check the attached device
 to clear the condition. The system will try to recover the port, but if not
 successful, you need to re-connect the hub or reboot the system to bring the
 port back to work.\fR
@@ -306,3 +301,17 @@
 please re-connect the hub or reboot the system to enable the port again.
 .RE
 
+.sp
+.ne 2
+.na
+\fBUnable to attach USB 3.x hub <vendor> <device>. A maximum of 5 hubs
+may be cascaded.\fR
+.ad
+.sp .6
+.RS 4n
+USB 3.0 has an upper bound on the number of hubs that may be chained
+together. If this limit is exceeded, a hub may not be detected or
+attached. The hub will be functional if plugged into a different part of
+the USB topology. To determine the depth of the hub, count the number of
+devices between it and the host controller.
+.RE
--- a/share/man/man7d/ugen.7d	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man7d/ugen.7d	Fri Mar 10 18:57:44 2017 -0500
@@ -30,8 +30,7 @@
 .LP
 \fBugen\fR supports control, bulk, isochronous and interrupt (in and  out)
 transfers. \fBlibusb\fR(3LIB) uses \fBugen\fR to access devices that do not
-contain drivers (such as digital cameras and PDAs). Refer to
-\fB/usr/sfw/share/doc/libusb/libusb.txt\fR for details.
+contain drivers (such as digital cameras and PDAs).
 .SH BINDING
 .LP
 In general, no explicit binding of the \fBugen\fR driver is necessary because
@@ -66,6 +65,7 @@
 .LP
 where vid is the USB vendor identifier in hex and pid is the  product
 identifier in hex supplied by the device  descriptor \fBusb_dev_descr\fR(9S).
+Note, the USB device version may vary depending on the device.
 .sp
 .LP
 When using ugen for the first time, you must add the  driver utilizing
--- a/share/man/man7d/usba.7d	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man7d/usba.7d	Fri Mar 10 18:57:44 2017 -0500
@@ -1,190 +1,183 @@
-'\" te
-.\"  Copyright (c) 2009, Sun Microsystems, Inc.  All Rights Reserved
+.\" Copyright (c) 2009, Sun Microsystems, Inc.  All Rights Reserved
+.\" Copyright 2016 Joyent, Inc.
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
 .\"  See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with
 .\" the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USBA 7D "April 9, 2016"
-.SH NAME
-usba, usb \- Solaris USB Architecture (USBA)
-.SH DESCRIPTION
-.LP
+.Dd October 4, 2016
+.Dt USBA 7D
+.Os
+.Sh NAME
+.Nm usba ,
+.Nm usb
+.Nd illumos USB Architecture (USBA)
+.Sh DESCRIPTION
 USB provides a low-cost means for attaching peripheral devices, including
 mass-storage devices, keyboards, mice, and printers, to a system. For complete
 information on the USB architecture, visit the USB website at
 http://www.usb.org.
-.sp
-.LP
+.Pp
 USBA supports 126 hot-pluggable USB devices per USB bus. The maximum data
-transfer rate is 1.5 Mbits (low speed USB 1.x) or 12 Mbits (full speed USB 1.x)
-or 480 MBits (high speed USB 2.0) Mbits per second (Mbps).
-.sp
-.LP
-USBA adheres to the \fIUniversal Serial Bus 2.0\fR specification and provides a
-transport layer abstraction to USB client drivers.
-.sp
-.LP
-For information on how to write USB client drivers, see \fIWriting Device
-Drivers\fR. For the latest information on writing USB drivers, visit
-\fIhttp://developers.sun.com/solaris/developer/support/driver/usb.html\fR. For
-a complete list of USBA interfaces, see \fBIntro\fR(9F) and \fBIntro\fR(9S).
-.sp
-.LP
-Devices without a driver may have a  \fBlibusb\fR(3LIB) application. For more
-information, see \fB/usr/sfw/share/doc/libusb/libusb.txt\fR.
-.SH FILES
-.LP
+transfer rate is 5 Gbits (SuperSpeed USB 3.0), 480 Mbits (high speed USB
+2.0), 12 Mbits (full speed USB 1.x), or 1.5 Mbits (low speed USB 1.x).
+.Pp
+USBA adheres to the
+.Em Universal Serial Bus 3.0
+specification and provides a transport layer abstraction to USB client
+drivers.
+.Pp
+For information on how to write USB client drivers, see
+.Em Writing Device Drivers .
+For the latest information on writing USB drivers, visit
+.Em http://illumos.org/books/wdd .
+For a complete list of USBA interfaces, see
+.Xr Intro 9F
+or
+.Xr Intro 9S .
+.Pp
+Devices without a driver may be able to leverage
+.Xr libusb 3LIB .
+.Sh FILES
 Listed below are drivers and modules which either utilize or are utilized by
-USBA. Drivers in \fB/kernel/drv\fR are 32 bit drivers (x86 only). Drivers in
-\fB/kernel/drv/sparcv9\fR or \fB/kernel/drv/amd64\fR are 64 bit drivers.
-.sp
-.in +2
-.nf
-\fIClient Driver                           Function/Device\fR
-
-kernel/drv/[sparcv9|amd64/]hid          HID class
-kernel/drv/[sparcv9|amd64/]hubd         hub class
-kernel/drv/[sparcv9|amd64/]hwahc        HWA Host Controller class
-kernel/drv/[sparcv9|amd64/]hwarc        HWA Radio Controller class
-kernel/drv/[sparcv9|amd64/]scsa2usb     mass storage class
-kernel/drv/[sparcv9|amd64/]usbprn       printer class
-kernel/drv/[sparcv9|amd64/]usb_as       audio streaming class
-kernel/drv/[sparcv9|amd64/]usb_ac       audio control class
-kernel/drv/[sparcv9|amd64/]usbvc        video class
-kernel/drv/[sparcv9|amd64/]usb_mid      multi-interface device
-kernel/drv/[sparcv9|amd64/]usb_ia       interface-association driver
-kernel/drv/[sparcv9|amd64/]usbser_edge  Edgeport USB to serial port
-kernel/drv/[sparcv9|amd64/]usbsksp      Keyspan USB to serial port
-kernel/drv/[sparcv9|amd64/]usbsprl      pl2303 USB to serial port
-kernel/drv/[sparcv9|amd64/]usbsacm      CDC ACM class to serial port
-kernel/drv/[sparcv9|amd64/]ugen         generic USB driver
-kernel/drv/[sparcv9|amd64/]wusb_ca      WUSB Cable Association class
-kernel/drv/[sparcv9|amd64/]ohci         open host controller driver
-kernel/drv/[sparcv9|amd64/]uhci         universal host controller driver
-kernel/drv/[sparcv9|amd64/]ehci         enhanced host controller driver
-.fi
-.in -2
-.sp
-
-.sp
-.in +2
-.nf
-\fIClient Streams Modules                   Function/Device\fR
-/kernel/strmod/[sparcv9|amd64]usbkbm     Keyboad
-/kernel/strmod/[sparcv9|amd64]usbms      Mouse
-/kernel/strmod/[sparcv9|amd64]usb_ah     Audio HID
-.fi
-.in -2
-.sp
-
-.sp
-.in +2
-.nf
-\fIHost Controller Interface Drivers   Device\fR
-
-/kernel/drv/[sparcv9|amd64]ehci     Enhanced HCI
-/kernel/drv/[sparcv9|amd64]ohci     Open HCI
-/kernel/drv/[sparcv|amd64/]uhci     Univeral HCI
-.fi
-.in -2
-.sp
-
-.SH ATTRIBUTES
-.LP
-See \fBattributes\fR(5) for a description of the following attributes:
-.sp
-
-.sp
-.TS
-box;
-c | c
-l | l .
-ATTRIBUTE TYPE	ATTRIBUTE VALUE
-_
-Architecture	PCI-based  systems
-.TE
-
-.SH SEE ALSO
-.LP
-\fBcfgadm_usb\fR(1M), \fBlibusb\fR(3LIB), \fBattributes\fR(5), \fBehci\fR(7D),
-\fBhid\fR(7D), \fBhubd\fR(7D), \fBohci\fR(7D), \fBscsa2usb\fR(7D),
-\fBuhci\fR(7D), \fBusb_ac\fR(7D), \fBusb_as\fR(7D), \fBusb_ia\fR(7D),
-\fBusb_mid\fR(7D), \fBusbprn\fR(7D), \fBusbsacm\fR(7D), \fBusbser_edge\fR(7D),
-\fBusbsksp\fR(7D), \fBusbsprl\fR(7D), \fBusbvc\fR(7D), \fBugen\fR(7D),
-\fBvirtualkm\fR(7D). \fBIntro\fR(9F), \fBIntro\fR(9S)
-.sp
-.LP
-\fIWriting Device Drivers\fR
-.sp
-.LP
-\fIUniversal Serial Bus Specification 2.0\fR.
-.sp
-.LP
-\fIInterface Association Descriptor Engineering Change Notice (ECN)\fR
-.sp
-.LP
-\fISystem Administration Guide: Basic Administration\fR
-.sp
-.LP
-http://www.sun.com
-.SH NOTES
-.LP
+USBA. Drivers in
+.Pa /kernel/drv\
+are 32 bit drivers (x86 only). Drivers in
+.Pa /kernel/drv/sparcv9
+or
+.Pa kernel/drv/amd64
+are 64 bit drivers.
+.Bl -column -offset indent ".Pa kernel/drv/[sparcv9|amd64/]usbser_edge" "Edgeport USB to serial port"
+.It Em Client Driver
+.Ta Em Function/Device
+.It
+.Ta
+.It Pa kernel/drv/[sparcv9|amd64/]hid
+.Ta HID class
+.It Pa kernel/drv/[sparcv9|amd64/]hubd
+.Ta hub class
+.It Pa kernel/drv/[sparcv9|amd64/]scsa2usb
+.Ta mass storage class
+.It Pa kernel/drv/[sparcv9|amd64/]usbprn
+.Ta printer class
+.It Pa kernel/drv/[sparcv9|amd64/]usb_as
+.Ta audio streaming class
+.It Pa kernel/drv/[sparcv9|amd64/]usb_ac
+.Ta audio control class
+.It Pa kernel/drv/[sparcv9|amd64/]usbvc
+.Ta video class
+.It Pa kernel/drv/[sparcv9|amd64/]usb_mid
+.Ta multi-interface device
+.It Pa kernel/drv/[sparcv9|amd64/]usb_ia
+.Ta interface-association driver
+.It Pa kernel/drv/[sparcv9|amd64/]usbser_edge
+.Ta Edgeport USB to serial port
+.It Pa kernel/drv/[sparcv9|amd64/]usbsksp
+.Ta Keyspan USB to serial port
+.It Pa kernel/drv/[sparcv9|amd64/]usbsprl
+.Ta pl2303 USB to serial port
+.It Pa kernel/drv/[sparcv9|amd64/]usbsacm
+.Ta CDC ACM class to serial port
+.It Pa kernel/drv/[sparcv9|amd64/]ugen
+.Ta generic USB driver
+.It Pa kernel/drv/[sparcv9|amd64/]ohci
+.Ta open host controller driver
+.It Pa kernel/drv/[sparcv9|amd64/]uhci
+.Ta universal host controller driver
+.It Pa kernel/drv/[sparcv9|amd64/]ehci
+.Ta enhanced host controller driver
+.It Pa kernel/drv/[sparcv9|amd64/]xhci
+.Ta extensible host controller driver
+.El
+.Bl -column -offset indent ".Pa /kernel/strmod/[sparcv9|amd64/]usb_ah" "Function/Device"
+.It
+.Ta
+.It Em Client Streams Modules
+.Ta Em Function/Device
+.It
+.Ta
+.It Pa /kernel/strmod/[sparcv9|amd64/]usbkbm
+.Ta Keyboad
+.It Pa /kernel/strmod/[sparcv9|amd64/]usbms
+.Ta Mouse
+.It Pa /kernel/strmod/[sparcv9|amd64/]usb_ah
+.Ta Audio HID
+.El
+.Bl -column -offset indent ".Em Host Controller Interface Drivers" "Extensible HCI"
+.It Em Host Controller Interface Drivers
+.Ta Em Device
+.It
+.Ta
+.It Pa /kernel/drv/[amd64/]xhci
+.Ta Extensible HCI
+.It Pa /kernel/drv/[sparcv9|amd64/]ehci
+.Ta Enhanced HCI
+.It Pa /kernel/drv/[sparcv9|amd64/]ohci
+.Ta Open HCI
+.It Pa /kernel/drv/[sparcv|amd64/]uhci
+.Ta Universal HCI
+.El
+.Sh DIAGNOSTICS
+The messages described below may appear on the system console as well as being
+logged. All  messages are formatted in the following manner:
+.Bl -tag -width Sy -offset 2n
+.It WARNING: Error message...
+.El
+.Bl -tag -width Sy -offset 2n
+.It Sy no driver found for device <device_name> (interface <number> node
+name=<node_name>)
+The installed software does not contain a supported driver for this
+hardware. <number> is the interface number.  <name> is either the device path name or the device name.
+.It Sy Draining callbacks timed out!
+An internal error occurred.  Please reboot your system.  If this problem
+persists, contact your system vendor.
+.El
+.Pp
+The following messages may be logged into the system log. They are formatted in
+the following manner:
+.Bd -literal -offset 2n
+<device path><usba<instance number>): message...
+.Ed
+.Bl -tag -width Sy -offset 2n
+.It Sy Incorrect USB driver version for <n.m>.
+Driver is incompatible with USBA framework.
+.El
+.Sh SEE ALSO
+.Xr cfgadm_usb 1M ,
+.Xr libusb 3LIB ,
+.Xr attributes 5 ,
+.Xr ehci 7D ,
+.Xr hid 7D ,
+.Xr hubd 7D ,
+.Xr ohci 7D ,
+.Xr scsa2usb 7D ,
+.Xr ugen 7D ,
+.Xr uhci 7D ,
+.Xr usb_ac 7D ,
+.Xr usb_as 7D ,
+.Xr usb_ia 7D ,
+.Xr usb_mid 7D ,
+.Xr usbprn 7D ,
+.Xr usbsacm 7D ,
+.Xr usbser_edge 7D ,
+.Xr usbsksp 7D ,
+.Xr usbsprl 7D ,
+.Xr usbvc 7D ,
+.Xr virtualkm 7D ,
+.Xr xhci 7D ,
+.Xr Intro 9F ,
+.Xr Intro 9S)
+.Pp
+.Rs
+.%T Writing Device Drivers
+.Re
+.Rs
+.%T Universal Serial Bus Specification 3.0
+.Re
+.Rs
+.%T Interface Association Descriptor Engineering Change Notice (ECN)
+.Re
+.Rs
+.%T System Administration Guide: Basic Administration
+.Re
+.Sh NOTES
 Booting from USB mass-storage devices is not supported on SPARC, but is
 supported on X86.
-.SH DIAGNOSTICS
-.LP
-The messages described below may appear on the system console as well as being
-logged. All  messages are formatted in the following manner:
-.sp
-.in +2
-.nf
-WARNING: Error message...
-.fi
-.in -2
-.sp
-
-.sp
-.ne 2
-.na
-\fBNo driver found for device <device_name> (interface <number> node
-name=<node_name>)\fR
-.ad
-.sp .6
-.RS 4n
-The installed Solaris software does not contain a supported driver for this
-hardware. <number> is the interface number.  <name> is either the device path
-name or the device name.
-.RE
-
-.sp
-.ne 2
-.na
-\fBDraining callbacks timed out!\fR
-.ad
-.sp .6
-.RS 4n
-An internal error occurred.  Please reboot your system.  If this problem
-persists, contact your system vendor.
-.RE
-
-.sp
-.LP
-The following messages may be logged into the system log. They are formatted in
-the following manner:
-.sp
-.in +2
-.nf
-<device path><usba<instance number>): message...
-.fi
-.in -2
-.sp
-
-.sp
-.ne 2
-.na
-\fBIncorrect USB driver version for <n.m>.\fR
-.ad
-.sp .6
-.RS 4n
-Driver is incompatible with USBA framework.
-.RE
-
--- a/share/man/man9f/Makefile	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -418,6 +418,7 @@
       usb_client_attach.9f \
       usb_clr_feature.9f \
       usb_create_pm_components.9f \
+      usb_ep_xdescr_fill.9f \
       usb_get_addr.9f \
       usb_get_alt_if.9f \
       usb_get_cfg.9f \
@@ -437,9 +438,9 @@
       usb_pipe_get_state.9f \
       usb_pipe_intr_xfer.9f \
       usb_pipe_isoc_xfer.9f \
-      usb_pipe_open.9f \
       usb_pipe_reset.9f \
       usb_pipe_set_private.9f \
+      usb_pipe_xopen.9f \
       usb_register_hotplug_cbs.9f \
       usb_reset_device.9f \
       uwritec.9f \
--- a/share/man/man9f/usb_client_attach.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_client_attach.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -4,7 +4,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_CLIENT_ATTACH 9F "Jan 5, 2004"
+.TH USB_CLIENT_ATTACH 9F "Oct 30, 2016"
 .SH NAME
 usb_client_attach, usb_client_detach \- USBA framework registration of client
 USB drivers
@@ -28,11 +28,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .LP
 For \fBusb_client_attach()\fR:
 .sp
@@ -84,7 +82,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_client_attach()\fR function registers a driver with the USBA
 framework and must be called before any other USBA function. Usually,
@@ -98,7 +95,6 @@
 last USBA function a client calls before completing \fBdetach\fR(9E). It is not
 necessary to call \fBusb_client_detach()\fR during a suspend operation.
 .SS "VERSIONING"
-.sp
 .LP
 USBDRV_VERSION is a macro which creates a version number based on the
 USBDRV_MAJOR_VER and USBDRV_MINOR_VER definitions. It must be passed as the
@@ -112,15 +108,13 @@
 .sp
 .LP
 Version 0.8 drivers from previous releases are binary compatible and run on
-Solaris 10, but are not compilable. Version 0.8 binary compatibility will not
-be supported in subsequent Solaris OS releases.
+illumos, but are not compilable.
 .sp
 .LP
 Definitions of USBDRV_MAJOR_VERSION and USBDRV_MINOR_VERSION must appear in the
 client driver above the reference to <\fBsys/usb/usba.h\fR>. Note that
 different releases have different USBA_[MAJOR|MINOR]_VER numbers.
 .SH RETURN VALUES
-.sp
 .LP
 For \fBusb_client_attach()\fR:
 .sp
@@ -190,7 +184,6 @@
 .RE
 
 .SH CONTEXT
-.sp
 .LP
 The \fBusb_client_attach()\fR function may only be called from
 \fBattach\fR(9E).
@@ -199,7 +192,6 @@
 The \fBusb_client_detach()\fR function may be called only from \fBattach\fR(9E)
 or \fBdetach\fR(9E).
 .SH EXAMPLES
-.sp
 .in +2
 .nf
   if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
@@ -221,7 +213,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -239,7 +230,6 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBattach\fR(9E), \fBdetach\fR(9E),
 \fBusb_get_dev_data\fR(9F)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/man/man9f/usb_ep_xdescr_fill.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,106 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source.  A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2016 Joyent, Inc.
+.\"
+.Dd Aug 7, 2016
+.Dt USB_EP_XDESCR_FILL 9F
+.Os
+.Sh NAME
+.Nm usb_ep_xdescr_fill
+.Nd fill extended endpoint description from endpoint data
+.Sh SYNOPSIS
+.In sys/usb/usba.h
+.Ft int
+.Fo usb_ep_xdescr_fill
+.Fa "uint_t version"
+.Fa "dev_info_t *dip"
+.Fa "usb_ep_data_t *ep_data"
+.Fa "usb_ep_xdescr_t *ep_xdescr"
+.Fc
+.Sh INTERFACE STABILITY
+illumos DDI specific
+.Sh PARAMETESR
+.Bl -tag -width Fa
+.It Fa version
+Indicates the current version of the
+.Ft usb_ep_xdescr_t
+structure the driver is using. Callers should always specify
+.Sy USB_EP_XDESCR_CURRENT_VERSION .
+.It Fa dip
+Pointer to the device's
+.Sy dev_info
+structure.
+.It Fa ep_data
+Pointer to endpoint data retrieved by calling
+.Xr usb_lookup_ep_data 9F .
+.It Fa ep_xdescr
+Pointer to the extended endpoint descriptor that will be filled out.
+.El
+.Sh DESCRIPTION
+The
+.Fn usb_ep_xdescr_fill
+function is used to fill in the members of the extended endpoint
+descriptor
+.Fa ep_xdescr
+based on the endpoint descriptor data in
+.Fa ep_data .
+Once filled in,
+.Fa ep_xdescr
+can be used to open a pipe by calling
+.Xr usb_pipe_xopen 9F .
+.Pp
+Prior to USB 3.0, only one descriptor, the
+.Xr usb_ep_descr 9S ,
+was needed to describe an endpoint. However, with USB 3.0, additional
+companion descriptors have been added and are required to successfully
+open an endpoint. After calling this, all descriptors needed
+to successfully open a pipe will be placed into
+.Fa ep_xdescr
+and the endpoint data,
+.Fa ep_data ,
+is no longer required.
+.Sh CONTEXT
+The
+.Fn usb_ep_xdescr_fill
+is generally only called from a drivers
+.Xr attach 9E
+entry point; however, it may be called from either
+.Sy user
+or
+.Sy kernel
+context.
+.Sh RETURN VALUES
+Upon successful completion, the
+.Fn usb_ep_xdescr_fill
+function returns
+.Sy USB_SUCCESS .
+Otherwise an error number is returned.
+.Sh ERRORS
+.Bl -tag -width Er
+.It Er USB_INVALID_ARGS
+The value of
+.Fa version
+is unknown, or one of
+.Fa dip ,
+.Fa ep_data ,
+and
+.Fa ep_xdescr
+was an invalid pointer.
+.It Er USB_FAILURE
+An unknown error occurred.
+.El
+.Sh SEE ALSO
+.Xr usb_lookup_ep_data 9F ,
+.Xr usb_pipe_xopen 9F ,
+.Xr usb_ep_descr 9S ,
+.Xr usb_ep_ss_comp_descr 9S ,
+.Xr usb_ep_xdescr 9S
--- a/share/man/man9f/usb_get_addr.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_get_addr.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_GET_ADDR 9F "Feb 9, 2004"
+.TH USB_GET_ADDR 9F "Sep 16, 2016"
 .SH NAME
 usb_get_addr \- Retrieve device USB address
 .SH SYNOPSIS
@@ -15,11 +15,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .ne 2
 .na
 \fB\fIdip\fR\fR
@@ -29,25 +27,21 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_get_addr()\fR function returns the current USB bus address for
 debugging  purposes. The returned address is unique for a specific USB bus, and
 may be replicated if multiple host controller instances are present on the
 system.
 .SH RETURN VALUES
-.sp
 .LP
 On success: USB device address.
 .sp
 .LP
 On failure: returns 0. Fails if dip is NULL.
 .SH CONTEXT
-.sp
 .LP
 May be called from user, kernel or interrupt context.
 .SH EXAMPLES
-.sp
 .in +2
 .nf
 int usb_addr;
@@ -57,7 +51,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -75,6 +68,5 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
-\fBattributes\fR(5), \fBusb_pipe_open\fR(9F)
+\fBattributes\fR(5), \fBusb_pipe_xopen\fR(9F)
--- a/share/man/man9f/usb_get_cfg.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_get_cfg.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_GET_CFG 9F "Jan 5, 2004"
+.TH USB_GET_CFG 9F "Sep 16, 2016"
 .SH NAME
 usb_get_cfg, usb_set_cfg \- Get and set current USB device configuration
 .SH SYNOPSIS
@@ -25,11 +25,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .LP
 For \fBusb_get_cfg()\fR:
 .sp
@@ -110,7 +108,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_get_cfg()\fR function retrieves the current configuration. It
 ignores the flags argument and always blocks while contacting the device.
@@ -134,7 +131,6 @@
 This call blocks if USB_FLAGS_SLEEP is set in flags.  It returns immediately
 and calls the callback on completion if USB_FLAGS_SLEEP is not set.
 .SH RETURN VALUES
-.sp
 .LP
 For \fBusb_get_cfg()\fR:
 .sp
@@ -235,7 +231,6 @@
 .RE
 
 .SH CONTEXT
-.sp
 .LP
 The \fBusb_get_cfg()\fR function may be called from user or kernel context.
 .sp
@@ -250,7 +245,6 @@
 callback cannot block. Please see \fBusb_callback_flags\fR(9S) for more
 information on callbacks.
 .SH EXAMPLES
-.sp
 .in +2
 .nf
  Setting the configuration to the one at index 1 (in the
@@ -287,7 +281,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -305,9 +298,8 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBusb_get_alt_if\fR(9F), \fBusb_get_dev_data\fR(9F),
-\fBusb_get_string_descr\fR(9F), \fBusb_pipe_open\fR(9F),
+\fBusb_get_string_descr\fR(9F), \fBusb_pipe_xopen\fR(9F),
 \fBusb_callback_flags\fR(9S), \fBusb_cfg_descr\fR(9S), \fBusb_ep_descr\fR(9S),
 \fBusb_if_descr\fR(9S)
--- a/share/man/man9f/usb_get_dev_data.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_get_dev_data.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_GET_DEV_DATA 9F "Jan 5, 2004"
+.TH USB_GET_DEV_DATA 9F "Sep 16, 2016"
 .SH NAME
 usb_get_dev_data, usb_free_dev_data, usb_free_descr_tree, usb_print_descr_tree
 \- Retrieve device configuration information
@@ -34,11 +34,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .LP
 For \fBusb_get_dev_data()\fR:
 .sp
@@ -143,7 +141,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_get_dev_data()\fR function interrogates a device and returns its
 configuration information in a usb_client_dev_data_t structure. Most USBA
@@ -222,7 +219,6 @@
 (\fBboot\fR \fB-v\fR). Output is spaced with blank lines for readability and
 provides you with an on-screen look at what a device has to offer.
 .SH RETURN VALUES
-.sp
 .LP
 For \fBusb_get_dev_data()\fR:
 .sp
@@ -317,7 +313,6 @@
 .RE
 
 .SH CONTEXT
-.sp
 .LP
 The \fBusb_get_dev_data()\fR and \fBusb_print_descr_tree()\fR functions may be
 called from user or kernel context.
@@ -326,7 +321,6 @@
 The \fBusb_free_dev_data()\fR and \fBusb_free_descr_tree()\fR functions may be
 called from user, kernel or interrupt context.
 .SH EXAMPLES
-.sp
 .in +2
 .nf
 In this example, assume a device has the configuration shown
@@ -391,7 +385,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -409,11 +402,10 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBusb_client_attach\fR(9F), \fBusb_get_alt_if\fR(9F),
 \fBusb_get_cfg\fR(9F), \fBusb_get_string_descr\fR(9F),
 \fBusb_lookup_ep_data\fR(9F), \fBusb_parse_data\fR(9F),
-\fBusb_pipe_open\fR(9F), \fBusb_cfg_descr\fR(9S),
+\fBusb_pipe_xopen\fR(9F), \fBusb_cfg_descr\fR(9S),
 \fBusb_client_dev_data(9S)\fR, \fBusb_ep_descr\fR(9S), \fBusb_if_descr\fR(9S),
 \fBusb_string_descr\fR(9S)
--- a/share/man/man9f/usb_lookup_ep_data.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_lookup_ep_data.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_LOOKUP_EP_DATA 9F "Jan 5, 2004"
+.TH USB_LOOKUP_EP_DATA 9F "Sep 16, 2016"
 .SH NAME
 usb_lookup_ep_data \- Lookup endpoint information
 .SH SYNOPSIS
@@ -19,11 +19,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .ne 2
 .na
 \fB\fIdip\fR\fR
@@ -76,7 +74,7 @@
 .ad
 .RS 13n
 Type of endpoint. This is one of: USB_EP_ATTR_CONTROL, USB_EP_ATTR_ISOCH,
-USB_EP_ATTR_BULK, or USB_EP_ATTR_INTR. Please see \fBusb_pipe_open\fR(9F) for
+USB_EP_ATTR_BULK, or USB_EP_ATTR_INTR. Please see \fBusb_pipe_xopen\fR(9F) for
 more information.
 .RE
 
@@ -91,7 +89,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_lookup_ep_data()\fR function returns endpoint information from the
 tree embedded in client data returned from \fBusb_get_dev_data\fR. It operates
@@ -101,7 +98,6 @@
 then retrieves information on the next matching endpoint it finds. Note that it
 does not make a copy of the data, but points to the tree itself.
 .SH RETURN VALUES
-.sp
 .LP
 On success: the tree node corresponding to the desired endpoint.
 .sp
@@ -110,11 +106,9 @@
 the desired endpoint does not exist in the tree, or no tree is present in
 dev_datap.
 .SH CONTEXT
-.sp
 .LP
 May be called from user, kernel or interrupt context.
 .SH EXAMPLES
-.sp
 .LP
 Retrieve the polling interval for the second interrupt endpoint at interface 0,
 alt 3:
@@ -147,7 +141,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -165,7 +158,6 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBusb_get_dev_data\fR(9F), \fBusb_pipe_open\fR(9F),
 \fBusb_cfg_descr\fR(9S), \fBusb_if_descr\fR(9S), \fBusb_ep_descr\fR(9S)
--- a/share/man/man9f/usb_parse_data.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_parse_data.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_PARSE_DATA 9F "Jan 5, 2004"
+.TH USB_PARSE_DATA 9F "Oct 30, 2016"
 .SH NAME
 usb_parse_data \- Tokenize and align the bytes of raw variable-format data
 .SH SYNOPSIS
@@ -18,11 +18,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .ne 2
 .na
 \fB\fIformat\fR\fR
@@ -73,7 +71,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_parse_data\fR function parses data such as a variable-format class-
 or vendor-specific descriptor. The function also tokenizes and aligns the bytes
@@ -81,7 +78,7 @@
 .sp
 .LP
 While the USBA framework can parse the endpoint, interface, configuration, and
-string descriptors defined by the \fIUSB 2.0\fR specification, the format of
+string descriptors defined by the \fIUSB 3.0\fR specification, the format of
 class- or vendor-specific descriptors cannot be explicitly defined by the
 specification and will be unique for each. The \fIformat\fR argument defines
 how to parse such a descriptor.
@@ -94,18 +91,15 @@
 The \fIstructlen\fR parameter defines the size of the destination data buffer.
 Data is truncated to this size if the destination data buffer is too small.
 .SH RETURN VALUES
-.sp
 .LP
 On success: Returns the size (in bytes) of the parsed data result.
 .sp
 .LP
 On failure: Returns 0. (Same as USB_PARSE_ERROR).
 .SH CONTEXT
-.sp
 .LP
 May be called from user, kernel or interrupt context.
 .SH EXAMPLES
-.sp
 .in +2
 .nf
   /*
@@ -136,7 +130,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -154,7 +147,6 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBusb_get_dev_data\fR(9F),
 \fBusb_get_string_descr\fR(9F), \fBusb_get_cfg\fR(9F)
--- a/share/man/man9f/usb_pipe_bulk_xfer.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_pipe_bulk_xfer.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_PIPE_BULK_XFER 9F "Aug 3, 2006"
+.TH USB_PIPE_BULK_XFER 9F "Sep 16, 2016"
 .SH NAME
 usb_pipe_bulk_xfer \- USB bulk transfer function
 .SH SYNOPSIS
@@ -18,11 +18,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .ne 2
 .na
 \fB\fIpipe_handle\fR\fR
@@ -50,7 +48,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_pipe_bulk_xfer()\fR function requests the USBA framework to perform
 a transfer through a USB bulk pipe. The request is passed to the host
@@ -66,7 +63,6 @@
 mblk for data when a request is allocated via \fBusb_alloc_bulk_req\fR(9F) by
 passing a non-negative value for the \fIlen\fR argument.
 .SH RETURN VALUES
-.sp
 .ne 2
 .na
 \fBUSB_SUCCESS\fR
@@ -179,12 +175,10 @@
 \fBusb_completion_reason\fR(9S) and \fBusb_callback_flags\fR(9S) for more
 information.
 .SH CONTEXT
-.sp
 .LP
 May be called from kernel or user context without regard to arguments. May be
 called from interrupt context only when the USB_FLAGS_SLEEP flag is clear.
 .SH EXAMPLES
-.sp
 .in +2
 .nf
    /* Allocate, initialize and issue a synchronous bulk request. */
@@ -209,7 +203,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -227,12 +220,11 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBusb_alloc_request\fR(9F), \fBusb_get_cfg\fR(9F),
 \fBusb_get_status\fR(9F), \fBusb_pipe_ctrl_xfer\fR(9F),
 \fBusb_pipe_get_state\fR(9F), \fBusb_pipe_intr_xfer\fR(9F),
-\fBusb_pipe_isoc_xfer\fR(9F), \fBusb_pipe_open\fR(9F),
+\fBusb_pipe_isoc_xfer\fR(9F), \fBusb_pipe_xopen\fR(9F),
 \fBusb_pipe_reset\fR(9F), \fBusb_bulk_request\fR(9S),
 \fBusb_callback_flags\fR(9S), \fBusb_completion_reason\fR(9S),
 \fBusb_ctrl_request\fR(9S), \fBusb_intr_request\fR(9S),
--- a/share/man/man9f/usb_pipe_close.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_pipe_close.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -4,7 +4,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_PIPE_CLOSE 9F "Jan 5, 2004"
+.TH USB_PIPE_CLOSE 9F "Sep 16, 2016"
 .SH NAME
 usb_pipe_close \- Close and cleanup a USB device pipe
 .SH SYNOPSIS
@@ -22,11 +22,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .ne 2
 .na
 \fB\fIdip\fR\fR
@@ -74,7 +72,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_pipe_close()\fR function closes the pipe pointed to by
 \fIpipe_handle\fR, releases all related resources and then frees the pipe
@@ -158,7 +155,6 @@
 .RE
 
 .SH RETURN VALUES
-.sp
 .LP
 Status is returned to the caller via the callback handler's rval argument.
 Possible callback hander rval argument values are:
@@ -219,7 +215,6 @@
 on error. (This provides status for calls which otherwise could provide
 status).
 .SH CONTEXT
-.sp
 .LP
 May be called from user or kernel context regardless of arguments. May not be
 called from a callback executing in interrupt     context. Please see
@@ -231,7 +226,6 @@
 callback cannot block. Please see \fBusb_callback_flags\fR(9S) for more
 information on callbacks.
 .SH EXAMPLES
-.sp
 .in +2
 .nf
 /* Synchronous close of pipe. */
@@ -249,7 +243,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -267,8 +260,7 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBusb_get_status\fR(9F), \fBusb_pipe_drain_reqs\fR(9F),
-\fBusb_pipe_get_state\fR(9F), \fBusb_pipe_open\fR(9F),
+\fBusb_pipe_get_state\fR(9F), \fBusb_pipe_xopen\fR(9F),
 \fBusb_pipe_reset\fR(9F), \fBusb_callback_flags\fR(9S)
--- a/share/man/man9f/usb_pipe_ctrl_xfer.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_pipe_ctrl_xfer.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
 .\"  See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with
 .\" the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_PIPE_CTRL_XFER 9F "Sep 15, 2009"
+.TH USB_PIPE_CTRL_XFER 9F "Sep 16, 2016"
 .SH NAME
 usb_pipe_ctrl_xfer, usb_pipe_ctrl_xfer_wait \- USB control pipe transfer
 functions
@@ -26,11 +26,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .LP
 For \fBusb_pipe_ctrl_xfer()\fR:
 .sp
@@ -122,7 +120,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_pipe_ctrl_xfer()\fR function requests the USBA framework to perform
 a transfer through a USB control pipe. The request is passed to the host
@@ -186,7 +183,6 @@
 argument. Control requests passing or receiving no supplemental data need not
 allocate an mblk.
 .SH RETURN VALUES
-.sp
 .LP
 For \fBusb_pipe_ctrl_xfer()\fR:
 .sp
@@ -318,7 +314,6 @@
 \fBusb_callback_flags\fR(9S) and \fBusb_completion_reason\fR(9S) for more
 information.
 .SH CONTEXT
-.sp
 .LP
 The \fBusb_pipe_ctrl_xfer()\fR function may be called from kernel or user
 context without regard to arguments and from the interrupt context only when
@@ -328,7 +323,6 @@
 The \fBusb_pipe_ctrl_xfer_wait()\fR function may be called from kernel or user
 context.
 .SH EXAMPLES
-.sp
 .in +2
 .nf
   /* Allocate, initialize and issue a synchronous control request. */
@@ -436,7 +430,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -454,12 +447,11 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBusb_alloc_request\fR(9F), \fBusb_get_cfg\fR(9F),
 \fBusb_get_status\fR(9F). \fBusb_pipe_bulk_xfer\fR(9F),
 \fBusb_pipe_intr_xfer\fR(9F), \fBusb_pipe_isoc_xfer\fR(9F),
-\fBusb_pipe_open\fR(9F), \fBusb_pipe_reset\fR(9F),
+\fBusb_pipe_xopen\fR(9F), \fBusb_pipe_reset\fR(9F),
 \fBusb_pipe_get_state\fR(9F), \fBusb_bulk_request\fR(9S),
 \fBusb_callback_flags\fR(9S), \fBusb_ctrl_request\fR(9S),
 \fBusb_completion_reason\fR(9S), \fBusb_intr_request\fR(9S),
--- a/share/man/man9f/usb_pipe_get_state.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_pipe_get_state.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_PIPE_GET_STATE 9F "Jan 5, 2004"
+.TH USB_PIPE_GET_STATE 9F "Sep 16, 2016"
 .SH NAME
 usb_pipe_get_state \- Return USB pipe state
 .SH SYNOPSIS
@@ -18,11 +18,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .ne 2
 .na
 \fB\fIpipe_handle\fR\fR
@@ -50,7 +48,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_pipe_get_state()\fR function retrieves the state of the pipe
 referred to by \fIpipe_handle\fR into the location pointed to by
@@ -108,7 +105,6 @@
 .RE
 
 .SH RETURN VALUES
-.sp
 .ne 2
 .na
 \fBUSB_SUCCESS\fR
@@ -136,11 +132,9 @@
 .RE
 
 .SH CONTEXT
-.sp
 .LP
 May be called from user, kernel or interrupt context.
 .SH EXAMPLES
-.sp
 .in +2
 .nf
     usb_pipe_handle_t pipe;
@@ -158,7 +152,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -176,8 +169,7 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBusb_clr_feature\fR(9F), \fBusb_get_cfg\fR(9F).
 \fBusb_get_status\fR(9F), \fBusb_pipe_close\fR(9F),
-\fBusb_pipe_ctrl_xfer\fR(9F), \fBusb_pipe_open\fR(9F). \fBusb_pipe_reset\fR(9F)
+\fBusb_pipe_ctrl_xfer\fR(9F), \fBusb_pipe_xopen\fR(9F). \fBusb_pipe_reset\fR(9F)
--- a/share/man/man9f/usb_pipe_intr_xfer.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_pipe_intr_xfer.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_PIPE_INTR_XFER 9F "Aug 3, 2006"
+.TH USB_PIPE_INTR_XFER 9F "Sep 16, 2016"
 .SH NAME
 usb_pipe_intr_xfer, usb_pipe_stop_intr_polling \- USB interrupt transfer and
 polling functions
@@ -24,11 +24,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .LP
 For \fBusb_pipe_intr_xfer()\fR:
 .sp
@@ -82,7 +80,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_pipe_intr_xfer()\fR function requests the USBA framework to perform
 a transfer through a USB interrupt pipe. The request is passed to the host
@@ -94,7 +91,6 @@
 interrupt-IN, single-transfer interrupt-IN, and (single-transfer)
 interrupt-OUT.
 .SS "Periodic Interrupt-IN Transfers"
-.sp
 .LP
 Periodic or polled interrupt-IN transfers execute on input requests which do
 not have the USB_ATTRS_ONE_XFER attribute set. One request enables repetitive
@@ -126,7 +122,6 @@
 this case. The USB_CR_STOPPED_POLLING flag is always set for callbacks where
 the original request is returned.
 .SS "Single-transfer Interrupt-IN Transfers"
-.sp
 .LP
 Interrupt-IN requests which have the USB_ATTRS_ONE_XFER attribute perform a
 single transfer. Such requests are synchronous when the USB_FLAGS_SLEEP flag is
@@ -135,7 +130,6 @@
 client through the normal or the exception completion callback to signal either
 normal completion or an error condition.
 .SS "Interrupt-OUT Transfers"
-.sp
 .LP
 Interrupt-OUT requests always set up for a single transfer. However, multiple
 requests can be queued and execute in periodic fashion until depleted.
@@ -196,7 +190,6 @@
 allocated via \fBusb_alloc_intr_req\fR(9F) by passing a non-negative value for
 the \fIlen\fR argument.
 .SH RETURN VALUES
-.sp
 .LP
 For \fBusb_pipe_intr_xfer()\fR
 .sp
@@ -320,13 +313,11 @@
 Exception handlers' queued requests which are flushed by these commands before
 execution are returned with completion reason of USB_CR_FLUSHED.
 .SH CONTEXT
-.sp
 .LP
 Both of these functions can be called from kernel or user context without
 regard to arguments, and may be called from interrupt context only when the
 USB_FLAGS_SLEEP flag is clear.
 .SH EXAMPLES
-.sp
 .in +2
 .nf
  /* Start polling on interrupt-IN pipe. */
@@ -389,7 +380,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -407,12 +397,11 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBusb_alloc_request\fR(9F), \fBusb_get_cfg\fR(9F),
 \fBusb_get_status\fR(9F), \fBusb_pipe_bulk_xfer\fR(9F),
 \fBusb_pipe_ctrl_xfer\fR(9F), \fBusb_pipe_get_state\fR(9F),
-\fBusb_pipe_isoc_xfer\fR(9F), \fBusb_pipe_open\fR(9F),
+\fBusb_pipe_isoc_xfer\fR(9F), \fBusb_pipe_xopen\fR(9F),
 \fBusb_pipe_reset\fR(9F), \fBusb_bulk_request\fR(9S),
 \fBusb_callback_flags\fR(9S), \fBusb_completion_reason\fR(9S),
 \fBusb_ctrl_request\fR(9S), \fBusb_ep_descr\fR(9S), \fBusb_intr_request\fR(9S),
--- a/share/man/man9f/usb_pipe_isoc_xfer.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_pipe_isoc_xfer.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_PIPE_ISOC_XFER 9F "Jan 5, 2004"
+.TH USB_PIPE_ISOC_XFER 9F "Sep 16, 2016"
 .SH NAME
 usb_pipe_isoc_xfer, usb_pipe_stop_isoc_polling \- USB isochronous transfer and
 polling functions
@@ -26,11 +26,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .LP
 For \fBusb_pipe_isoc_xfer()\fR:
 .sp
@@ -83,7 +81,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_pipe_isoc_xfer()\fR function requests the USBA framework to perform
 a transfer     through a USB isochronous pipe. The request is passed to the
@@ -115,7 +112,6 @@
 descriptor's isoc_pkt_status field. These errors can be examined when the
 completed request is returned through a normal callback.
 .SS "Isochronous-OUT TRANSFERS"
-.sp
 .LP
 Allocate room for data when allocating isochronous-OUT requests via
 usb_alloc_isoc_req(9F), by passing a positive value for     the \fIlen\fR
@@ -131,7 +127,6 @@
 driver will queue the request to start on a frame which immediately follows the
 last frame of the last queued request.
 .SS "Isochronous-IN TRANSFERS"
-.sp
 .LP
 All isochronous-IN transfers start background polling, and require only a
 single (original) request.  The USBA framework will allocate a new request each
@@ -145,7 +140,6 @@
 return status when polling termination is requested, or for error condition
 notification. There can be only one isochronous-IN request submitted at a time.
 .SS "CALLBACKS"
-.sp
 .LP
 Isochronous transfer normal-completion callbacks cannot block for any reason
 since they are called from interrupt context.  They will have
@@ -226,7 +220,6 @@
 USB_CR_STOPPED_POLLING.
 .RE
 .SH RETURN VALUES
-.sp
 .LP
 For \fBusb_pipe_isoc_xfer()\fR:
 .sp
@@ -374,13 +367,11 @@
 pipe; or the pipe is in an error state. Messages regarding these errors will be
 logged to the console logfile.
 .SH CONTEXT
-.sp
 .LP
 Both of these functions may be called from kernel or user context without
 regard to arguments. May be called from interrupt context only when the
 USB_FLAGS_SLEEP flag is clear.
 .SH EXAMPLES
-.sp
 .in +2
 .nf
 /* Start polling on an isochronous-IN pipe. */
@@ -430,7 +421,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -448,14 +438,13 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBusb_alloc_request\fR(9F),
 \fBusb_get_current_frame_number\fR(9F), \fBusb_get_cfg\fR(9F),
 \fBusb_get_max_pkts_per_isoc_request\fR(9F), \fBusb_get_status\fR(9F),
 \fBusb_pipe_bulk_xfer\fR(9F), \fBusb_pipe_ctrl_xfer\fR(9F),
 \fBusb_pipe_get_state\fR(9F), \fBusb_pipe_intr_xfer\fR(9F),
-\fBusb_pipe_open\fR(9F), \fBusb_pipe_reset\fR(9F), \fBusb_bulk_request\fR(9S),
+\fBusb_pipe_xopen\fR(9F), \fBusb_pipe_reset\fR(9F), \fBusb_bulk_request\fR(9S),
 \fBusb_callback_flags\fR(9S), \fBusb_completion_reason\fR(9S),
 \fBusb_ctrl_request\fR(9S), \fBusb_ep_descr\fR(9S), \fBusb_intr_request\fR(9S),
 \fBusb_isoc_request\fR(9S)
--- a/share/man/man9f/usb_pipe_open.9f	Fri Mar 10 17:02:49 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,373 +0,0 @@
-'\" te
-.\" Copyright (c) 2004, Sun Microsystems, Inc., All Rights Reserved
-.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
-.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
-.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_PIPE_OPEN 9F "Jan 5, 2004"
-.SH NAME
-usb_pipe_open \- Open a USB pipe to a device
-.SH SYNOPSIS
-.LP
-.nf
-#include <sys/usb/usba.h>
-
-
-
-\fBint\fR \fBusb_pipe_open\fR(\fBdev_info_t *\fR\fIdip\fR, \fBusb_ep_descr_t *\fR\fIendpoint\fR,
-     \fBusb_pipe_policy_t *\fR\fIpipe_policy\fR,
-\fBusb_flags_t\fR \fIflags\fR, \fBusb_pipe_handle_t *\fR\fIpipe_handle\fR);
-.fi
-
-.SH INTERFACE LEVEL
-.sp
-.LP
-Solaris DDI specific (Solaris DDI)
-.SH PARAMETERS
-.sp
-.ne 2
-.na
-\fB\fIdip\fR\fR
-.ad
-.RS 15n
-Pointer to the device's \fBdev_info\fR structure.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fIendpoint\fR\fR
-.ad
-.RS 15n
-Pointer to endpoint descriptor.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fIpipe_policy\fR\fR
-.ad
-.RS 15n
-Pointer to \fIpipe_policy\fR. \fIpipe_policy\fR provides hints on pipe usage.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fIflags\fR\fR
-.ad
-.RS 15n
-USB_FLAGS_SLEEP is only flag that is recognized. Wait for memory resources if
-not immediately available.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fIpipe_handle\fR\fR
-.ad
-.RS 15n
-Address to where new pipe handle is returned. (The handle is opaque.)
-.RE
-
-.SH DESCRIPTION
-.sp
-.LP
-A pipe is a logical connection to an endpoint on a USB device. The
-\fBusb_pipe_open()\fR function creates such a logical connection and returns an
-initialized handle which refers to that connection.
-.sp
-.LP
-The \fIUSB 2.0\fR specification defines four endpoint types, each with a
-corresponding type of pipe. Each of the four types of pipes uses its physical
-connection resource differently. They are:
-.sp
-.ne 2
-.na
-\fBControl pipe\fR
-.ad
-.RS 20n
-Used for bursty, non-periodic, reliable, host-initiated request/response
-communication, such as for command/status operations. These are guaranteed to
-get approximately 10% of frame time and will get more if needed and if
-available, but there is no guarantee on transfer promptness. Bidirectional.
-.RE
-
-.sp
-.ne 2
-.na
-\fBBulk pipe\fR
-.ad
-.RS 20n
-Used for large, reliable, non-time-critical data transfers. These get the bus
-on a bandwidth-available basis. Unidirectional. Sample uses include printer
-data.
-.RE
-
-.sp
-.ne 2
-.na
-\fBInterrupt pipe\fR
-.ad
-.RS 20n
-Used for sending or receiving small amounts of reliable data infrequently but
-with bounded service periods, as for interrupt handling. Unidirectional.
-.RE
-
-.sp
-.ne 2
-.na
-\fBIsochronous pipe\fR
-.ad
-.RS 20n
-Used for large, unreliable, time-critical data transfers. Boasts a guaranteed
-constant data rate as long as there is data, but there are no retries of failed
-transfers. Interrupt and isochronous data are together guaranteed 90% of frame
-time as needed. Unidirectional. Sample uses include audio.
-.RE
-
-.sp
-.LP
-The type of endpoint to which a pipe connects (and therefore the pipe type) is
-defined by the \fBbmAttributes\fR field of that pipe's endpoint descriptor.
-(See \fBusb_ep_descr\fR(9S)). Opens to interrupt and isochronous pipes can fail
-if the required bandwidth cannot be guaranteed.
-.sp
-.LP
-The polling interval for periodic (interrupt or isochronous) pipes, carried by
-the endpoint argument's bInterval field, must be within range. Valid ranges
-are:
-.sp
-.LP
-Full speed: range of 1-255 maps to 1-255 ms.
-.sp
-.LP
-Low speed: range of 10-255 maps to 10-255 ms.
-.sp
-.LP
-High speed: range of 1-16 maps to (2**(bInterval-1)) * 125us.
-.sp
-.LP
-Adequate bandwidth during transfers is guaranteed for all periodic pipes which
-are opened successfully. Interrupt and isochronous pipes have guaranteed
-latency times, so bandwidth for them is allocated when they are opened. (Please
-refer to Sections \fI5.7\fR and \fI5.8\fR of the \fIUSB 2.0\fR specification
-which address isochronous and interrupt transfers.) Opens of interrupt and
-isochronous pipes fail if inadequate bandwidth is available to support their
-guaranteed latency time. Because periodic pipe bandwidth is allocated on pipe
-open, open periodic pipes only when needed.
-.sp
-.LP
-The bandwidth required by a device varies based on polling interval, the
-maximum packet size (\fBwMaxPacketSize\fR) and the device speed. Unallocated
-bandwidth remaining for new devices depends on the bandwidth already allocated
-for previously opened periodic pipes.
-.sp
-.LP
-The \fIpipe_policy\fR parameter provides a hint as to pipe usage and must be
-specified. It is a \fBusb_pipe_policy_t\fR which contains the following fields:
-.sp
-.in +2
-.nf
-uchar_t         pp_max_async_reqs:
-                   A hint indicating how many
-                   asynchronous operations requiring
-                   their own kernel thread will be
-                   concurrently in progress, the highest
-                   number of threads ever needed at one
-                   time.  Allow at least one for
-                   synchronous callback handling and as
-                   many as are needed to accommodate the
-                   anticipated parallelism of asynchronous*
-                   calls to the following functions:
-                           usb_pipe_close(9F)
-                           usb_set_cfg(9F)
-                           usb_set_alt_if(9F)
-                           usb_clr_feature(9F)
-                           usb_pipe_reset(9F)
-                           usb_pipe_drain_reqs(9F)
-                           usb_pipe_stop_intr_polling(9F)
-                           usb_pipe_stop_isoc_polling(9F)
-                  Setting to too small a value can
-                  deadlock the pipe.
-
-                  * Asynchronous calls are calls made
-                    without the USB_FLAGS_SLEEP flag being
-                    passed.  Note that a large number of
-                    callbacks becomes an issue mainly when
-                    blocking functions are called from
-                    callback handlers.
-.fi
-.in -2
-
-.sp
-.LP
-The control pipe to the default endpoints (endpoints for both directions with
-addr 0, sometimes called the default control pipe or default pipe) comes
-pre-opened by the hub. A client driver receives the default control pipe handle
-through \fBusb_get_dev_data\fR(9F). A client driver cannot open the default
-control pipe manually. Note that the same control pipe may be shared among
-several drivers when a device has multiple interfaces and each interface is
-operated by its own driver.
-.sp
-.LP
-All explicit pipe opens are exclusive; attempts to open an opened pipe fail.
-.sp
-.LP
-On success, the pipe_handle argument points to an opaque handle of the opened
-pipe. On failure, it is set to NULL.
-.SH RETURN VALUES
-.sp
-.ne 2
-.na
-\fBUSB_SUCCESS\fR
-.ad
-.RS 25n
-Open succeeded.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_NO_RESOURCES\fR
-.ad
-.RS 25n
-Insufficient resources were available.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_NO_BANDWIDTH\fR
-.ad
-.RS 25n
-Insufficient bandwidth available. (isochronous and interrupt pipes).
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_INVALID_CONTEXT\fR
-.ad
-.RS 25n
-Called from interrupt handler with USB_FLAGS_SLEEP set.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_INVALID_ARGS\fR
-.ad
-.RS 25n
-dip and/or pipe_handle is NULL. Pipe_policy is NULL.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_INVALID_PERM\fR
-.ad
-.RS 25n
-Endpoint is NULL, signifying the default control pipe. A client driver cannot
-open the default control pipe.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_NOT_SUPPORTED\fR
-.ad
-.RS 25n
-Isochronous or interrupt endpoint with maximum packet size of zero is not
-supported.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_HC_HARDWARE_ERROR\fR
-.ad
-.RS 25n
-Host controller is in an error state.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_FAILURE\fR
-.ad
-.RS 25n
-Pipe is already open. Host controller not in an operational state. Polling
-interval (\fBep_descr bInterval\fR field) is out of range (intr or isoc pipes).
-.RE
-
-.SH CONTEXT
-.sp
-.LP
-May be called from user or kernel context regardless of arguments. May also be
-called from interrupt context if the \fIUSB_FLAGS_SLEEP\fR option is not set.
-.SH EXAMPLES
-.sp
-.in +2
-.nf
-    usb_ep_data_t *ep_data;
-    usb_pipe_policy_t policy;
-    usb_pipe_handle_t pipe;
-    usb_client_dev_data_t *reg_data;
-    uint8_t interface = 1;
-    uint8_t alternate = 1;
-    uint8_t first_ep_number = 0;
-
-    /* Initialize pipe policy. */
-    bzero(policy, sizeof(usb_pipe_policy_t));
-    policy.pp_max_async_requests = 2;
-
-    /* Get tree of descriptors for device. */
-    if (usb_get_dev_data(
-        dip, USBDRV_VERSION, &reg_data, USB_FLAGS_ALL_DESCR, 0) !=
-        USB_SUCCESS) {
-            ...
-    }
-
-    /* Get first interrupt-IN endpoint. */
-    ep_data = usb_lookup_ep_data(dip, reg_data, interface, alternate,
-        first_ep_number, USB_EP_ATTR_INTR, USB_EP_DIR_IN);
-    if (ep_data == NULL) {
-        ...
-    }
-
-    /* Open the pipe.  Get handle to pipe back in 5th argument. */
-    if (usb_pipe_open(dip, &ep_data.ep_descr
-        &policy, USB_FLAGS_SLEEP, &pipe) != USB_SUCCESS) {
-            ...
-    }
-.fi
-.in -2
-
-.SH ATTRIBUTES
-.sp
-.LP
-See \fBattributes\fR(5) for descriptions of the following attributes:
-.sp
-
-.sp
-.TS
-box;
-c | c
-l | l .
-ATTRIBUTE TYPE	ATTRIBUTE VALUE
-_
-Architecture	PCI-based systems
-_
-Interface stability	Committed
-.TE
-
-.SH SEE ALSO
-.sp
-.LP
-\fBattributes\fR(5), \fBusb_get_alt_if\fR(9F), \fBusb_get_cfg\fR(9F),
-\fBusb_get_status\fR(9F), \fBusb_get_dev_data\fR(9F),
-\fBusb_pipe_bulk_xfer\fR(9F), \fBusb_pipe_ctrl_xfer\fR(9F),
-\fBusb_pipe_close\fR(9F), \fBusb_pipe_get_state\fR(9F),
-\fBusb_pipe_intr_xfer\fR(9F), \fBusb_pipe_isoc_xfer\fR(9F),
-\fBusb_pipe_reset\fR(9F), \fBusb_pipe_set_private\fR(9F),
-\fBusb_ep_descr\fR(9S), \fBusb_callback_flags\fR(9S)
--- a/share/man/man9f/usb_pipe_reset.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_pipe_reset.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_PIPE_RESET 9F "Jan 5, 2004"
+.TH USB_PIPE_RESET 9F "Sep 16, 2016"
 .SH NAME
 usb_pipe_reset \- Abort queued requests from a USB pipe and reset the pipe
 .SH SYNOPSIS
@@ -19,11 +19,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .ne 2
 .na
 \fB\fIdip\fR\fR
@@ -70,7 +68,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 Call \fBusb_pipe_reset()\fR to reset a pipe which is in an error state, or to
 abort a current request and clear the pipe. The \fBusb_pipe_reset()\fR function
@@ -161,7 +158,6 @@
 .RE
 
 .SH RETURN VALUES
-.sp
 .LP
 Status is returned to the caller via the callback handler's rval argument.
 Possible callback hander rval argument values are:
@@ -239,7 +235,6 @@
 on error. This provides status for calls which could not otherwise provide
 status.
 .SH CONTEXT
-.sp
 .LP
 May be called from user or kernel context regardless of arguments. May be
 called from any callback with the USB_FLAGS_SLEEP clear. May not be called from
@@ -251,7 +246,6 @@
 callback cannot block. Please see \fBusb_callback_flags\fR(9S) for more
 information on callbacks.
 .SH EXAMPLES
-.sp
 .in +2
 .nf
 void post_reset_handler(
@@ -270,7 +264,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -288,12 +281,11 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBusb_get_cfg\fR(9F), \fBusb_pipe_bulk_xfer\fR(9F),
 \fBusb_pipe_close\fR(9F), \fBusb_get_status\fR(9F),
 \fBusb_pipe_ctrl_xfer\fR(9F), \fBusb_pipe_drain_reqs\fR(9F),
 \fBusb_pipe_get_state\fR(9F), \fBusb_pipe_intr_xfer\fR(9F),
-\fBusb_pipe_isoc_xfer\fR(9F), \fBusb_pipe_open\fR(9F),
+\fBusb_pipe_isoc_xfer\fR(9F), \fBusb_pipe_xopen\fR(9F),
 \fBusb_pipe_stop_intr_polling\fR(9F), \fBusb_pipe_stop_isoc_polling\fR(9F),
 \fBusb_callback_flags\fR(9S)
--- a/share/man/man9f/usb_pipe_set_private.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_pipe_set_private.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_PIPE_SET_PRIVATE 9F "Jan 5, 2004"
+.TH USB_PIPE_SET_PRIVATE 9F "Sep 16, 2016"
 .SH NAME
 usb_pipe_set_private, usb_pipe_get_private \- USB user-defined pipe data-field
 facility
@@ -21,11 +21,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .LP
 For \fBusb_pipe_set_private()\fR:
 .sp
@@ -59,7 +57,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_set_driver_private()\fR function initializes the user-private data
 field of the pipe referred to by \fIpipe_handle\fR, using \fIdata\fR. The
@@ -72,7 +69,6 @@
 stored via \fBusb_set_driver_private()\fR, from the pipe referred to by
 \fIpipe_handle\fR.
 .SH RETURN VALUES
-.sp
 .LP
 For \fBusb_pipe_set_private()\fR:
 .sp
@@ -115,11 +111,9 @@
 On failure: \fBNULL\fR. Fails if pipe handle is \fBNULL\fR or invalid. Fails if
 pipe handle is to a pipe which is closing or closed.
 .SH CONTEXT
-.sp
 .LP
 May be called from user, kernel or interrupt context.
 .SH EXAMPLES
-.sp
 .in +2
 .nf
     usb_pipe_handle_t pipe;
@@ -137,7 +131,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -155,6 +148,5 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
-\fBattributes\fR(5), \fBusb_pipe_open\fR(9F), \fBusb_alloc_request\fR(9F)
+\fBattributes\fR(5), \fBusb_pipe_xopen\fR(9F), \fBusb_alloc_request\fR(9F)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/man/man9f/usb_pipe_xopen.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,299 @@
+.\" Copyright (c) 2004, Sun Microsystems, Inc., All Rights Reserved
+.\" Copyright 2016 Joyent, Inc.
+.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
+.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
+.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
+.Dd Sep 16, 2016
+.Dt USB_PIPE_XOPEN 9F
+.Os
+.Sh NAME
+.Nm usb_pipe_open ,
+.Nm usb_pipe_xopen
+.Nd Open a USB pipe to a device
+.Sh SYNOPSIS
+.In sys/usb/usba.h
+.Ft int
+.Fo usb_pipe_open
+.Fa "dev_info_t *dip"
+.Fa "usb_ep_descr_t *endpoint"
+.Fa "usb_pipe_policy_t *pipe_policy"
+.Fa "usb_flags_t flags"
+.Fa "usb_pipe_handle_t *pipe_handle"
+.Fc
+.Ft int
+.Fo usb_pipe_xopen
+.Fa "dev_info_t *dip"
+.Fa "usb_ep_xdescr_t *extended_endpoint"
+.Fa "usb_pipe_policy_t *pipe_policy"
+.Fa "usb_flags_t flags"
+.Fa "usb_pipe_handle_t *pipe_handle"
+.Fc
+.Sh INTERFACE LEVEL
+Solaris DDI specific (Solaris DDI)
+.Sh PARAMETERS
+.Bl -tag -width Fa
+.It Fa dip
+Pointer to the device's
+.Sy dev_info
+structure.
+.It Fa endpoint
+Pointer to endpoint descriptor.
+.It Fa extended_endpoint
+Pointer to an extended endpoint descriptor retrieved from calling
+.Xr usb_ep_xdescr_fill 9F .
+.It Fa pipe_policy
+Pointer to
+.Em pipe_policy.
+.Em pipe_policy
+provides hints on pipe usage.
+.It Fa flags
+.Sy USB_FLAGS_SLEEP
+is only flag that is recognized. Wait for memory resources if
+not immediately available.
+.It Fa pipe_handle
+Address to where new pipe handle is returned. (The handle is opaque.)
+.El
+.Sh DESCRIPTION
+A pipe is a logical connection to an endpoint on a USB device. The
+.Fn usb_pipe_xopen
+function creates such a logical connection and returns an
+initialized handle which refers to that connection.
+.Pp
+The
+.Em USB 3.0
+specification defines four endpoint types, each with a
+corresponding type of pipe. Each of the four types of pipes uses its physical
+connection resource differently. They are:
+.Bl -tag -width Sy
+.It Sy Control Pipe
+Used for bursty, non-periodic, reliable, host-initiated request/response
+communication, such as for command/status operations. These are guaranteed to
+get approximately 10% of frame time and will get more if needed and if
+available, but there is no guarantee on transfer promptness. Bidirectional.
+.It Sy Bulk Pipe
+Used for large, reliable, non-time-critical data transfers. These get the bus
+on a bandwidth-available basis. Unidirectional. Sample uses include printer
+data.
+.It Sy Interrupt Pipe
+Used for sending or receiving small amounts of reliable data infrequently but
+with bounded service periods, as for interrupt handling. Unidirectional.
+.It Sy Isochronous Pipe
+Used for large, unreliable, time-critical data transfers. Boasts a guaranteed
+constant data rate as long as there is data, but there are no retries of failed
+transfers. Interrupt and isochronous data are together guaranteed 90% of frame
+time as needed. Unidirectional. Sample uses include audio.
+.El
+.Pp
+The type of endpoint to which a pipe connects (and therefore the pipe type) is
+defined by the
+.Sy bmAttributes
+field of that pipe's endpoint descriptor.
+.Po
+See
+.Xr usb_ep_descr 9S
+.Pc .
+.Pp
+Prior to the
+.Em USB 3.0
+specification, only the
+.Xr usb_ep_descr 9S
+was required to identify all of the attributes of a given pipe. Starting
+with
+.Em USB 3.0
+there are additional endpoint companion descriptors required to open a
+pipe. To support SuperSpeed devices, the new
+.Fn usb_pipe_xopen
+function must be used rather than the older
+.Fn usb_pipe_open
+function. The
+.Xr usb_ep_xdescr 9S
+structure can be automatically filled out and obtained by calling the
+.Xr usb_ep_xdescr_fill 9F
+function.
+.Pp
+Opens to interrupt and isochronous pipes can fail
+if the required bandwidth cannot be guaranteed.
+.Pp
+The polling interval for periodic (interrupt or isochronous) pipes, carried by
+the endpoint argument's bInterval field, must be within range. Valid ranges
+are:
+.Pp
+Full speed: range of 1-255 maps to 1-255 ms.
+.Pp
+Low speed: range of 10-255 maps to 10-255 ms.
+.Pp
+High speed: range of 1-16 maps to (2**(bInterval-1)) * 125us.
+.Pp
+Super speed: range of 1-16 maps to (2**(bInterval-1)) * 125us.
+.Pp
+Adequate bandwidth during transfers is guaranteed for all periodic pipes which
+are opened successfully. Interrupt and isochronous pipes have guaranteed
+latency times, so bandwidth for them is allocated when they are opened.
+.Po
+Please
+refer to Sections
+.Em 4.4.7
+and
+.Em 4.4.8
+of the
+.Em USB 3.1
+specification
+which address isochronous and interrupt transfers.
+.Pc
+Opens of interrupt and isochronous pipes fail if inadequate bandwidth is
+available to support their guaranteed latency time. Because periodic
+pipe bandwidth is allocated on pipe open, open periodic pipes only when
+needed.
+.Pp
+The bandwidth required by a device varies based on polling interval, the
+maximum packet size
+.Pq Sy wMaxPacketSize
+and the device speed. Unallocated
+bandwidth remaining for new devices depends on the bandwidth already allocated
+for previously opened periodic pipes.
+.Pp
+The
+.Em pipe_policy
+parameter provides a hint as to pipe usage and must be
+specified. It is a
+.Em usb_pipe_policy_t
+which contains the following fields:
+.Bd -literal -offset indent
+uchar_t         pp_max_async_reqs:
+.Ed
+.Pp
+The
+.Sy pp_max_async_reqs
+member is a hint indicating how many asynchronous operations requiring
+their own kernel thread will be concurrently in progress, the highest
+number of threads ever needed at one time.  Allow at least one for
+synchronous callback handling and as many as are needed to accommodate
+the anticipated parallelism of asynchronous* calls to the following
+functions:
+.Xr usb_pipe_close 9F ,
+.Xr usb_set_cfg 9F ,
+.Xr usb_set_alt_if 9F ,
+.Xr usb_clr_feature 9F ,
+.Xr usb_pipe_reset 9F ,
+.Xr usb_pipe_drain_reqs 9F ,
+.Xr usb_pipe_stop_intr_polling 9F ,
+and
+.Xr usb_pipe_stop_isoc_polling 9F .
+.Pp
+Setting to too small a value can deadlock the pipe.  Asynchronous calls
+are calls made without the
+.Sy USB_FLAGS_SLEEP
+flag being passed.  Note that
+a large number of callbacks becomes an issue mainly when blocking
+functions are called from callback handlers.
+.Pp
+The control pipe to the default endpoints (endpoints for both directions with
+addr 0, sometimes called the default control pipe or default pipe) comes
+pre-opened by the hub. A client driver receives the default control pipe handle
+through
+.Xr usb_get_dev_data 9F .
+A client driver cannot open the default
+control pipe manually. Note that the same control pipe may be shared among
+several drivers when a device has multiple interfaces and each interface is
+operated by its own driver.
+.Pp
+All explicit pipe opens are exclusive; attempts to open an opened pipe fail.
+.Pp
+On success, the pipe_handle argument points to an opaque handle of the opened
+pipe. On failure, it is set to NULL.
+.Sh CONTEXT
+May be called from user or kernel context regardless of arguments. May also be
+called from interrupt context if the
+.Sy USB_FLAGS_SLEEP
+option is not set.
+.Sh RETURN VALUES
+.Bl -tag -width Sy
+.It Sy USB_SUCCESS
+Open succeeded.
+.It Sy USB_NO_RESOURCES
+Insufficient resources were available.
+.It Sy USB_NO_BANDWIDTH
+Insufficient bandwidth available. (isochronous and interrupt pipes).
+.It Sy USB_INVALID_CONTEXT
+Called from interrupt handler with USB_FLAGS_SLEEP set.
+.It Sy USB_INVALID_ARGS
+dip and/or pipe_handle is NULL. Pipe_policy is NULL.
+.It Sy USB_INVALID_PERM
+Endpoint is NULL, signifying the default control pipe. A client driver cannot
+open the default control pipe.
+.It Sy USB_NOT_SUPPORTED
+Isochronous or interrupt endpoint with maximum packet size of zero is not
+supported.
+.It Sy USB_HC_HARDWARE_ERROR
+Host controller is in an error state.
+.It Sy USB_FAILURE
+Pipe is already open. Host controller not in an operational state. Polling
+interval
+.Pq Sy Bep_descr bInterval No field
+is out of range (intr or isoc pipes).
+.Pp
+The device referred to by
+.Fa dip
+is at least a SuperSpeed device and the older
+.Fn usb_pipe_open
+function was used.
+.El
+.Sh EXAMPLES
+.Bd -literal -offset indent
+usb_ep_data_t *ep_data;
+usb_ep_xdescr_t ep_xdescr;
+usb_pipe_policy_t policy;
+usb_pipe_handle_t pipe;
+usb_client_dev_data_t *reg_data;
+uint8_t interface = 1;
+uint8_t alternate = 1;
+uint8_t first_ep_number = 0;
+
+/* Initialize pipe policy. */
+bzero(policy, sizeof(usb_pipe_policy_t));
+policy.pp_max_async_requests = 2;
+
+/* Get tree of descriptors for device. */
+if (usb_get_dev_data(dip, USBDRV_VERSION, &reg_data,
+    USB_FLAGS_ALL_DESCR, 0) != USB_SUCCESS) {
+        ...
+}
+
+/* Get first interrupt-IN endpoint. */
+ep_data = usb_lookup_ep_data(dip, reg_data, interface, alternate,
+    first_ep_number, USB_EP_ATTR_INTR, USB_EP_DIR_IN);
+if (ep_data == NULL) {
+        ...
+}
+
+/* Translate the ep_data into the filled in usb_ep_xdescr_t */
+if (usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION, dip,
+    ep_data, &ep_xdescr) != USB_SUCCESS) {
+       ...
+}
+
+/* Open the pipe.  Get handle to pipe back in 5th argument. */
+if (usb_pipe_open(dip, &ep_data.ep_descr
+    &policy, USB_FLAGS_SLEEP, &pipe) != USB_SUCCESS) {
+        ...
+}
+.Ed
+.Sh SEE ALSO
+.Xr usb_get_alt_if 9F ,
+.Xr usb_get_cfg 9F ,
+.Xr usb_get_dev_data 9F ,
+.Xr usb_get_status 9F ,
+.Xr usb_pipe_bulk_xfer 9F ,
+.Xr usb_pipe_close 9F ,
+.Xr usb_pipe_ctrl_xfer 9F ,
+.Xr usb_pipe_get_state 9F ,
+.Xr usb_pipe_intr_xfer 9F ,
+.Xr usb_pipe_isoc_xfer 9F ,
+.Xr usb_pipe_reset 9F ,
+.Xr usb_pipe_set_private 9F ,
+.Xr usb_callback_flags 9S ,
+.Xr usb_ep_descr 9S
+.Rs
+.%T Universal Serial Bus 3.1 Specification
+.%U http://www.usb.org
+.Re
--- a/share/man/man9f/usb_reset_device.9f	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9f/usb_reset_device.9f	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_RESET_DEVICE 9F "Aug 21, 2007"
+.TH USB_RESET_DEVICE 9F "Sep 16, 2016"
 .SH NAME
 usb_reset_device \- reset a USB device according to the reset_level.
 .SH SYNOPSIS
@@ -18,11 +18,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH PARAMETERS
-.sp
 .ne 2
 .na
 \fB\fIdip\fR\fR
@@ -44,7 +42,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBusb_reset_device()\fR function provides a client driver to request a
 hardware reset for a \fBUSB\fR device, which may be required in some situations
@@ -104,7 +101,6 @@
 .RE
 
 .SH RETURN VALUES
-.sp
 .LP
 The return values for the \fBusb_reset_device()\fR function are:
 .sp
@@ -237,12 +233,10 @@
 .in -2
 
 .SH CONTEXT
-.sp
 .LP
 The \fBusb_reset_device()\fR function may be called from user or kernel
 context.
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -260,12 +254,10 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBattributes\fR(5), \fBusb_clr_feature\fR(9F), \fBusb_get_cfg\fR(9F),
-\fBusb_pipe_close\fR(9F), \fBusb_pipe_open\fR(9F), \fBusb_pipe_reset\fR(9F),
+\fBusb_pipe_close\fR(9F), \fBusb_pipe_xopen\fR(9F), \fBusb_pipe_reset\fR(9F),
 .SH DIAGNOSTICS
-.sp
 .LP
 The messages described below may appear on the system console as well as being
 logged. All messages are formatted in the following manner:
@@ -318,6 +310,5 @@
 .RE
 
 .SH NOTES
-.sp
 .LP
 Always close all applications before resetting a device.
--- a/share/man/man9s/Makefile	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9s/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -54,18 +54,20 @@
       stroptions.9s \
       tuple.9s \
       uio.9s \
-      usb_bulk_request.9s \
+      usb_bulk_req.9s \
       usb_callback_flags.9s \
       usb_cfg_descr.9s \
       usb_client_dev_data.9s \
       usb_completion_reason.9s \
-      usb_ctrl_request.9s \
+      usb_ctrl_req.9s \
       usb_dev_descr.9s \
       usb_dev_qlf_descr.9s \
       usb_ep_descr.9s \
+      usb_ep_ss_comp_descr.9s \
+      usb_ep_xdescr.9s \
       usb_if_descr.9s \
-      usb_intr_request.9s \
-      usb_isoc_request.9s \
+      usb_intr_req.9s \
+      usb_isoc_req.9s \
       usb_other_speed_cfg_descr.9s \
       usb_request_attributes.9s \
       usb_string_descr.9s
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/man/man9s/usb_bulk_req.9s	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,266 @@
+'\" te
+.\" Copyright (c) 2004, Sun Microsystems, Inc., All Rights Reserved
+.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
+.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
+.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
+.TH USB_BULK_REQUEST 9S "Jan 5, 2004"
+.SH NAME
+usb_bulk_req, usb_bulk_req_t, usb_bulk_request \- USB bulk request structure
+.SH SYNOPSIS
+.LP
+.nf
+#include  <sys/usb/usba.h>
+.fi
+
+.SH INTERFACE LEVEL
+.LP
+Solaris DDI specific (Solaris DDI)
+.SH DESCRIPTION
+.LP
+A bulk request (that is, a request sent through a bulk pipe) is used to
+transfer large amounts of data in reliable but non-time-critical fashion.
+Please refer to Section \fI5.8\fR of the \fIUSB 2.0\fR specification for
+information on bulk transfers. (The \fIUSB 2.0\fR specification is available at
+\fIwww.usb.org\fR.)
+.sp
+.LP
+The fields in the usb_bulk_req_t are used to format a bulk request. Please see
+below for acceptable combinations of flags and attributes.
+.sp
+.LP
+The usb_bulk_req_t fields are:
+.sp
+.in +2
+.nf
+uint_t          bulk_len;       /* number of bytes to xfer      */
+                                /* Please see */
+                                /* usb_pipe_get_max_bulk_xfer_size(9F) */
+                                /* for maximum size */
+mblk_t          *bulk_data;     /* the data for the data phase  */
+                                /* IN or OUT: allocated by client */
+uint_t          bulk_timeout;   /* xfer timeout value in secs   */
+                                /* If set to zero, defaults to 5 sec */
+usb_opaque_t    bulk_client_private; /* Client specific information */
+usb_req_attrs_t bulk_attributes; /* xfer-attributes     */
+
+/* Normal callback function, called upon completion. */
+void            (*bulk_cb)(
+                    usb_pipe_handle_t ph, struct usb_bulk_req *req);
+
+/* Exception callback function, for error handling. */
+void            (*bulk_exc_cb)(
+                    usb_pipe_handle_t ph, struct usb_bulk_req *req);
+
+/* set by USBA/HCD framework on completion */
+usb_cr_t        bulk_completion_reason; /* overall success status */
+                                   /* See usb_completion_reason(9S) */
+usb_cb_flags_t  bulk_cb_flags; /* recovery done by callback hndlr */
+                                   /* See usb_callback_flags(9S) */
+.fi
+.in -2
+
+.sp
+.LP
+Request attributes define special handling for transfers. The following
+attributes are valid for bulk requests:
+.sp
+.ne 2
+.na
+\fBUSB_ATTRS_SHORT_XFER_OK\fR
+.ad
+.RS 27n
+USB framework accepts transfers where less data is received than expected.
+.RE
+
+.sp
+.ne 2
+.na
+\fBUSB_ATTRS_AUTOCLEARING\fR
+.ad
+.RS 27n
+USB framework resets pipe and clears functional stalls automatically on
+exception.
+.RE
+
+.sp
+.ne 2
+.na
+\fBUSB_ATTRS_PIPE_RESET\fR
+.ad
+.RS 27n
+USB framework resets pipe automatically on exception.
+.RE
+
+.sp
+.LP
+Please see \fBusb_request_attributes\fR(9S) for more information.
+.sp
+.LP
+Bulk transfers/requests are subject to the following constraints and caveats:
+.sp
+.LP
+1) The following table indicates combinations of \fBusb_pipe_bulk_xfer()\fR
+flags argument and fields of the usb_bulk_req_t request argument (X = don't
+care).
+.br
+.in +2
+
+.in -2
+.sp
+.in +2
+.nf
+Flags     Type  Attributes       Data   Timeout Semantics
+---------------------------------------------------------------
+ X         X     X                ==NULL X      illegal
+
+ X         X     ONE_XFER         X      X      illegal
+
+ no sleep  IN    !SHORT_XFER_OK   !=NULL 0      See  note (A)
+
+ no sleep  IN    !SHORT_XFER_OK   !=NULL > 0    See  note (B)
+
+ sleep     IN    !SHORT_XFER_OK   !=NULL 0      See  note (C)
+
+ sleep     IN    !SHORT_XFER_OK   !=NULL > 0    See  note (D)
+
+ no sleep  IN    SHORT_XFER_OK    !=NULL 0      See  note (E)
+
+ no sleep  IN    SHORT_XFER_OK    !=NULL > 0    See  note (F)
+
+ sleep     IN    SHORT_XFER_OK    !=NULL 0      See  note (G)
+
+ sleep     IN    SHORT_XFER_OK    !=NULL > 0    See  note (H)
+
+ X         OUT   SHORT_XFER_OK   X       X      illegal
+
+ no sleep  OUT   X               !=NULL  0      See  note (I)
+
+ no sleep  OUT   X               !=NULL  > 0    See  note (J)
+
+ sleep     OUT   X               !=NULL  0      See  note (K)
+
+ sleep     OUT   X               !=NULL  > 0    See  note (L)
+.fi
+.in -2
+
+.sp
+.LP
+Table notes:
+.br
+.in +2
+A). Fill buffer, no timeout, callback when bulk_len is transferred.
+.in -2
+.br
+.in +2
+B). Fill buffer, with timeout; callback when bulk_len is transferred.
+.in -2
+.br
+.in +2
+C). Fill buffer, no timeout, unblock when bulk_len is transferred; no callback.
+.in -2
+.br
+.in +2
+D). Fill buffer, with timeout; unblock when bulk_len is transferred or a
+timeout occurs; no callback.
+.in -2
+.br
+.in +2
+E) Fill buffer, no timeout, callback when bulk_len is transferred or first
+short packet is received.
+.in -2
+.br
+.in +2
+F). Fill buffer, with timeout; callback when bulk_len is transferred or first
+short packet is received.
+.in -2
+.br
+.in +2
+G). Fill buffer, no timeout, unblock when bulk_len is transferred or first
+short packet is received; no callback.
+.in -2
+.br
+.in +2
+H). Fill buffer, with timeout; unblock when bulk_len is transferred, first
+short packet is received, or a timeout occurs; no callback.
+.in -2
+.br
+.in +2
+I). Empty buffer, no timeout; callback when bulk_len is transferred.
+.in -2
+.br
+.in +2
+J) Empty buffer, with timeout; callback when bulk_len is transferred or a
+timeout occurs.
+.in -2
+.br
+.in +2
+K). Empty buffer, no timeout; unblock when bulk_len is transferred; no
+callback.
+.in -2
+.br
+.in +2
+L). Empty buffer, with timeout; unblock when bulk_len is transferred or a
+timeout occurs; no callback.
+.in -2
+.sp
+.LP
+2) bulk_len must be > 0. bulk_data must not be NULL.
+.sp
+.LP
+3) Bulk_residue is set for both READ and WRITE. If it is set to 0, it means
+that all of the data was transferred successfully.  In case of WRITE it
+contains data not written and in case of READ it contains the data NOT read so
+far. A residue can only occur because of timeout or bus/device error. (Note
+that a short transfer for a request where the USB_ATTRS_SHORT_XFER_OK attribute
+is not set is considered a device error.)  An exception callback is made and
+completion_reason will be non-zero.
+.sp
+.LP
+4) Splitting large Bulk xfers: Due to internal constraints, the USBA framework
+can only do a limited size bulk data xfer per request.  A client driver may
+first determine this limitation by calling the USBA interface
+(usb_pipe_get_max_bulk_xfer_size(9F)) and then restrict itself to doing
+transfers in multiples of this fixed size. This forces a client driver to do
+data xfers in a loop for a large request, splitting it into multiple chunks of
+fixed size.
+.sp
+.LP
+The bulk_completion_reason indicates the status of the transfer.  See
+\fBusb_completion_reason\fR(9S) for usb_cr_t definitions.
+.sp
+.LP
+The bulk_cb_flags are set prior to calling the exception callback handler to
+summarize recovery actions taken and errors encountered during recovery. See
+\fBusb_callback_flags\fR(9S) for usb_cb_flags_t definitions.
+.sp
+.LP
+--- Callback handling ---
+.sp
+.LP
+All usb request types share the same callback handling. See
+\fBusb_callback_flags\fR(9S) for details.
+.SH ATTRIBUTES
+.LP
+See attributes(5) for descriptions of the following attributes:
+.sp
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE	ATTRIBUTE VALUE
+_
+Architecture	PCI-based systems
+_
+Interface stability	Committed
+.TE
+
+.SH SEE ALSO
+.LP
+\fBusb_alloc_request\fR(9F), \fBusb_pipe_bulk_xfer\fR(9F),
+\fBusb_pipe_ctrl_xfer\fR(9F), \fBusb_pipe_get_max_bulk_transfer_size\fR(9F),
+\fBusb_pipe_intr_xfer\fR(9F), \fBusb_pipe_isoc_xfer\fR(9F),
+\fBusb_callback_flags\fR(9S), \fBusb_completion_reason\fR(9S),
+\fBusb_ctrl_request\fR(9S), \fBusb_intr_request\fR(9S),
+\fBusb_isoc_request\fR(9S), \fBusb_request_attributes\fR(9S)
--- a/share/man/man9s/usb_bulk_request.9s	Fri Mar 10 17:02:49 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,270 +0,0 @@
-'\" te
-.\" Copyright (c) 2004, Sun Microsystems, Inc., All Rights Reserved
-.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
-.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
-.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_BULK_REQUEST 9S "Jan 5, 2004"
-.SH NAME
-usb_bulk_request \- USB bulk request structure
-.SH SYNOPSIS
-.LP
-.nf
-#include  <sys/usb/usba.h>
-.fi
-
-.SH INTERFACE LEVEL
-.sp
-.LP
-Solaris DDI specific (Solaris DDI)
-.SH DESCRIPTION
-.sp
-.LP
-A bulk request (that is, a request sent through a bulk pipe) is used to
-transfer large amounts of data in reliable but non-time-critical fashion.
-Please refer to Section \fI5.8\fR of the \fIUSB 2.0\fR specification for
-information on bulk transfers. (The \fIUSB 2.0\fR specification is available at
-\fIwww.usb.org\fR.)
-.sp
-.LP
-The fields in the usb_bulk_req_t are used to format a bulk request. Please see
-below for acceptable combinations of flags and     attributes.
-.sp
-.LP
-The usb_bulk_req_t fields are:
-.sp
-.in +2
-.nf
-uint_t          bulk_len;       /* number of bytes to xfer      */
-                                /* Please see */
-                                /* usb_pipe_get_max_bulk_xfer_size(9F) */
-                                /* for maximum size */
-mblk_t          *bulk_data;     /* the data for the data phase  */
-                                /* IN or OUT: allocated by client */
-uint_t          bulk_timeout;   /* xfer timeout value in secs   */
-                                /* If set to zero, defaults to 5 sec */
-usb_opaque_t    bulk_client_private; /* Client specific information */
-usb_req_attrs_t bulk_attributes; /* xfer-attributes     */
-
-/* Normal callback function, called upon completion. */
-void            (*bulk_cb)(
-                    usb_pipe_handle_t ph, struct usb_bulk_req *req);
-
-/* Exception callback function, for error handling. */
-void            (*bulk_exc_cb)(
-                    usb_pipe_handle_t ph, struct usb_bulk_req *req);
-
-/* set by USBA/HCD framework on completion */
-usb_cr_t        bulk_completion_reason; /* overall success status */
-                                   /* See usb_completion_reason(9S) */
-usb_cb_flags_t  bulk_cb_flags; /* recovery done by callback hndlr */
-                                   /* See usb_callback_flags(9S) */
-.fi
-.in -2
-
-.sp
-.LP
-Request attributes define special handling for transfers. The following
-attributes are valid for bulk requests:
-.sp
-.ne 2
-.na
-\fBUSB_ATTRS_SHORT_XFER_OK\fR
-.ad
-.RS 27n
-USB framework accepts transfers where less data is received than expected.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_ATTRS_AUTOCLEARING\fR
-.ad
-.RS 27n
-USB framework resets pipe and clears functional stalls automatically on
-exception.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_ATTRS_PIPE_RESET\fR
-.ad
-.RS 27n
-USB framework resets pipe automatically on exception.
-.RE
-
-.sp
-.LP
-Please see \fBusb_request_attributes\fR(9S) for more information.
-.sp
-.LP
-Bulk transfers/requests are subject to the following constraints and caveats:
-.sp
-.LP
-1) The following table indicates combinations of \fBusb_pipe_bulk_xfer()\fR
-flags argument and fields of the usb_bulk_req_t request argument (X = don't
-care).
-.br
-.in +2
-
-.in -2
-.sp
-.in +2
-.nf
-Flags     Type  Attributes       Data   Timeout Semantics
----------------------------------------------------------------
- X         X     X                ==NULL X      illegal
-
- X         X     ONE_XFER         X      X      illegal
-
- no sleep  IN    !SHORT_XFER_OK   !=NULL 0      See  note (A)
-
- no sleep  IN    !SHORT_XFER_OK   !=NULL > 0    See  note (B)
-
- sleep     IN    !SHORT_XFER_OK   !=NULL 0      See  note (C)
-
- sleep     IN    !SHORT_XFER_OK   !=NULL > 0    See  note (D)
-
- no sleep  IN    SHORT_XFER_OK    !=NULL 0      See  note (E)
-
- no sleep  IN    SHORT_XFER_OK    !=NULL > 0    See  note (F)
-
- sleep     IN    SHORT_XFER_OK    !=NULL 0      See  note (G)
-
- sleep     IN    SHORT_XFER_OK    !=NULL > 0    See  note (H)
-
- X         OUT   SHORT_XFER_OK   X       X      illegal
-
- no sleep  OUT   X               !=NULL  0      See  note (I)
-
- no sleep  OUT   X               !=NULL  > 0    See  note (J)
-
- sleep     OUT   X               !=NULL  0      See  note (K)
-
- sleep     OUT   X               !=NULL  > 0    See  note (L)
-.fi
-.in -2
-
-.sp
-.LP
-Table notes:
-.br
-.in +2
-A). Fill buffer, no timeout, callback when bulk_len is transferred.
-.in -2
-.br
-.in +2
-B). Fill buffer, with timeout; callback when bulk_len is transferred.
-.in -2
-.br
-.in +2
-C). Fill buffer, no timeout, unblock when bulk_len is transferred; no callback.
-.in -2
-.br
-.in +2
-D). Fill buffer, with timeout; unblock when bulk_len is transferred or a
-timeout occurs; no callback.
-.in -2
-.br
-.in +2
-E) Fill buffer, no timeout, callback when bulk_len is transferred or first
-short packet is received.
-.in -2
-.br
-.in +2
-F). Fill buffer, with timeout; callback when bulk_len is transferred or first
-short packet is received.
-.in -2
-.br
-.in +2
-G). Fill buffer, no timeout, unblock when bulk_len is transferred or first
-short packet is received; no callback.
-.in -2
-.br
-.in +2
-H). Fill buffer, with timeout; unblock when bulk_len is transferred, first
-short packet is received, or a timeout occurs; no callback.
-.in -2
-.br
-.in +2
-I). Empty buffer, no timeout; callback when bulk_len is transferred.
-.in -2
-.br
-.in +2
-J) Empty buffer, with timeout; callback when bulk_len is transferred or a
-timeout occurs.
-.in -2
-.br
-.in +2
-K). Empty buffer, no timeout; unblock when bulk_len is transferred; no
-callback.
-.in -2
-.br
-.in +2
-L). Empty buffer, with timeout; unblock when bulk_len is transferred or a
-timeout occurs; no callback.
-.in -2
-.sp
-.LP
-2) bulk_len must be > 0. bulk_data must not be NULL.
-.sp
-.LP
-3) Bulk_residue is set for both READ and WRITE. If it is set to 0, it means
-that all of the data was transferred successfully.  In case of WRITE it
-contains data not written and in case of READ it contains the data NOT read so
-far. A residue can only occur because of timeout or bus/device error. (Note
-that a short transfer for a request where the USB_ATTRS_SHORT_XFER_OK attribute
-is not set is considered a device error.)  An exception callback is made and
-completion_reason will be non-zero.
-.sp
-.LP
-4) Splitting large Bulk xfers: Due to internal constraints, the USBA framework
-can only do a limited size bulk data xfer per request.  A client driver may
-first determine this limitation by calling the USBA interface
-(usb_pipe_get_max_bulk_xfer_size(9F)) and then restrict itself to doing
-transfers in multiples of this fixed size. This forces a client driver to do
-data xfers in a loop for a large request, splitting it into multiple chunks of
-fixed size.
-.sp
-.LP
-The bulk_completion_reason indicates the status of the transfer.  See
-\fBusb_completion_reason\fR(9S) for usb_cr_t definitions.
-.sp
-.LP
-The bulk_cb_flags are set prior to calling the exception callback handler to
-summarize recovery actions taken and errors encountered during recovery. See
-\fBusb_callback_flags\fR(9S) for usb_cb_flags_t definitions.
-.sp
-.LP
---- Callback handling ---
-.sp
-.LP
-All usb request types share the same callback handling. See
-\fBusb_callback_flags\fR(9S) for details.
-.SH ATTRIBUTES
-.sp
-.LP
-See attributes(5) for descriptions of the following attributes:
-.sp
-
-.sp
-.TS
-box;
-c | c
-l | l .
-ATTRIBUTE TYPE	ATTRIBUTE VALUE
-_
-Architecture	PCI-based systems
-_
-Interface stability	Committed
-.TE
-
-.SH SEE ALSO
-.sp
-.LP
-\fBusb_alloc_request\fR(9F), \fBusb_pipe_bulk_xfer\fR(9F),
-\fBusb_pipe_ctrl_xfer\fR(9F), \fBusb_pipe_get_max_bulk_transfer_size\fR(9F),
-\fBusb_pipe_intr_xfer\fR(9F), \fBusb_pipe_isoc_xfer\fR(9F),
-\fBusb_callback_flags\fR(9S), \fBusb_completion_reason\fR(9S),
-\fBusb_ctrl_request\fR(9S), \fBusb_intr_request\fR(9S),
-\fBusb_isoc_request\fR(9S), \fBusb_request_attributes\fR(9S)
--- a/share/man/man9s/usb_client_dev_data.9s	Fri Mar 10 17:02:49 2017 +0200
+++ b/share/man/man9s/usb_client_dev_data.9s	Fri Mar 10 18:57:44 2017 -0500
@@ -3,7 +3,7 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_CLIENT_DEV_DATA 9S "Jan 5, 2004"
+.TH USB_CLIENT_DEV_DATA 9S "Oct 30, 2016"
 .SH NAME
 usb_client_dev_data \- Device configuration information
 .SH SYNOPSIS
@@ -13,11 +13,9 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
 .LP
 Solaris DDI specific (Solaris DDI)
 .SH DESCRIPTION
-.sp
 .LP
 The usb_client_dev_data_t structure carries all device configuration
 information. It is provided to a USB client driver through a call to
@@ -142,7 +140,7 @@
 .LP
 The descriptor tree, returned by dev_cfg, makes a device's parsed standard USB
 descriptors available to the driver. The tree is designed to be easily
-traversed to get any or all standard \fIUSB 2.0\fR descriptors.  (See the "Tree
+traversed to get any or all standard \fIUSB 3.0\fR descriptors.  (See the "Tree
 Structure" section of this manpage below.)  dev_n_cfg returns the number of
 configurations in the tree. Note that this value may differ from the number of
 configurations returned in the device descriptor.
@@ -161,10 +159,9 @@
 this field can differ from the parse_level requested as an argument to
 \fBusb_get_dev_data()\fR.
 .SS "TREE STRUCTURE"
-.sp
 .LP
 The root of the tree is dev_cfg, an array of usb_cfg_data_t configuration
-nodes, each representing one device configuration. The         array index does
+nodes, each representing one device configuration. The array index does
 not correspond to a configuration's value; use the bConfigurationValue field of
 the configuration descriptor within to find out the proper number for a given
 configuration.
@@ -227,7 +224,6 @@
 that all string descriptions returned have a maximum length of USB_MAXSTRINGLEN
 bytes and are in English ASCII.
 .SH EXAMPLES
-.sp
 .LP
 In the following example, a device's configuration data, including the
 following descriptor tree, is retrieved by \fBusb_get_dev_data\fR(9F) into
@@ -278,7 +274,6 @@
 .in -2
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -296,7 +291,6 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBusb_get_alt_if\fR(9F), \fBusb_get_cfg\fR(9F), \fBusb_get_dev_data\fR(9F),
 \fBusb_get_string_descr\fR(9F), \fBusb_lookup_ep_data\fR(9F),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/man/man9s/usb_ctrl_req.9s	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,181 @@
+'\" te
+.\" Copyright (c) 2004, Sun Microsystems, Inc., All Rights Reserved
+.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
+.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
+.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
+.TH USB_CTRL_REQUEST 9S "Jan 5, 2004"
+.SH NAME
+usb_ctrl_req, usb_ctrl_req_t, usb_ctrl_request \- USB control pipe request structure
+.SH SYNOPSIS
+.LP
+.nf
+#include <sys/usb/usba.h>
+.fi
+
+.SH INTERFACE LEVEL
+.LP
+Solaris DDI specific (Solaris DDI)
+.SH DESCRIPTION
+.LP
+A control request is used to send device commands (or requests) and to read
+status. Please refer to Section\fI 5.5\fR of the \fIUSB 2.0\fR specification
+for information on control pipes. For information on formatting requests, see
+Section \fI9.3\fR of the \fIUSB 2.0\fR specification. The USB 2.0 specification
+is available at \fIwww.usb.org\fR.
+.SH STRUCTURE MEMBERS
+.LP
+The fields in the usb_ctrl_req_t are used to format a control request:
+.sp
+.in +2
+.nf
+  uint8_t      ctrl_bmRequestType; /* characteristics of request */
+  uint8_t      ctrl_bRequest;  /* specific request             */
+  uint16_t     ctrl_wValue;    /* varies according to request  */
+  uint16_t     ctrl_wIndex;    /* index or offset              */
+  uint16_t     ctrl_wLength;   /* number of bytes to xfer      */
+  mblk_t       *ctrl_data;     /* data for the data phase  */
+                               /* IN or OUT: allocated by client */
+  uint_t       ctrl_timeout;   /* time until  USBA framework */
+                               /* retires req, in seconds */
+                               /* If set to zero, defaults to 5 sec */
+  usb_opaque_t    ctrl_client_private; /* client private info */
+  usb_req_attrs_t ctrl_attributes; /* attrib. for this req */
+
+  /* Normal callback function, called upon completion. */
+  void       (*ctrl_cb)(
+                 usb_pipe_handle_t ph, struct usb_ctrl_req *req);
+
+  /* Exception callback function, for error handling. */
+  void       (*ctrl_exc_cb)(
+                 usb_pipe_handle_t ph, struct usb_ctrl_req *req);
+  usb_cr_t   ctrl_completion_reason; /* overall success status */
+                                  /* See usb_completion_reason(9S) */
+  usb_cb_flags_t  ctrl_cb_flags; /* recovery done by callback hndlr */
+                                /* See \fBusb_callback_flags(9S)\fR */
+.fi
+.in -2
+
+.sp
+.LP
+Request attributes define special handling for transfers. The following
+attributes are valid for control requests:
+.sp
+.ne 2
+.na
+\fBUSB_ATTRS_SHORT_XFER_OK\fR
+.ad
+.RS 27n
+Accept transfers where less data is received than expected.
+.RE
+
+.sp
+.ne 2
+.na
+\fBUSB_ATTRS_AUTOCLEARING\fR
+.ad
+.RS 27n
+Have USB framework reset pipe and clear functional stalls automatically on
+exception.
+.RE
+
+.sp
+.ne 2
+.na
+\fBUSB_ATTRS_PIPE_RESET\fR
+.ad
+.RS 27n
+Have USB framework reset pipe automatically on exception.
+.RE
+
+.sp
+.LP
+Please see \fBusb_request_attributes\fR(9S) for more information.
+.sp
+.LP
+The following definitions directly pertain to fields in the USB control request
+structure. (See Section \fI 9.3\fR of the \fIUSB 2.0\fR specification.)
+.sp
+.in +2
+.nf
+Direction bitmasks of a control request's ctrl_bmRequestType field
+(USB 2.0 spec, section 9.3.1)
+
+     USB_DEV_REQ_HOST_TO_DEV     | Host to device direction
+     USB_DEV_REQ_DEV_TO_HOST     | Device to host direction
+     USB_DEV_REQ_DIR_MASK        | Bitmask of direction bits
+
+Request type bitmasks of a control request's ctrl_bmRequestType field
+(USB 2.0 spec, section 9.3.1)
+
+     USB_DEV_REQ_TYPE_STANDARD   | USB 2.0 defined command
+                                 | for all USB devices
+     USB_DEV_REQ_TYPE_CLASS      | USB 2.0 defined
+                                 | class-specific command
+     USB_DEV_REQ_TYPE_VENDOR     | Vendor-specific command
+     USB_DEV_REQ_TYPE_MASK       | Bitmask of request type bits
+
+Recipient bitmasks of a control request's ctrl_bmRequestType field
+(USB 2.0 spec, section 9.3.1)
+
+     USB_DEV_REQ_RCPT_DEV        | Request is for device
+     USB_DEV_REQ_RCPT_IF         | Request is for interface
+     USB_DEV_REQ_RCPT_EP         | Request is for endpoint
+     USB_DEV_REQ_RCPT_OTHER      | Req is for other than above
+     USB_DEV_REQ_RCPT_MASK       | Bitmask of request recipient bits
+
+Standard requests (USB 2.0 spec, section 9.4)
+     USB_REQ_GET_STATUS          | Get status of device, endpoint
+                                 |or interface (9.4.5)
+     USB_REQ_CLEAR_FEATURE       | Clear feature specified by
+                                 |wValue field (9.4.1)
+     USB_REQ_SET_FEATURE         | Set feature specified by
+                                 |       wValue field (9.4.9)
+     USB_REQ_SET_ADDRESS         | Set address specified by
+                                 |       wValue field (9.4.6)
+     USB_REQ_GET_DESCR           | Get descr for item/idx in
+                                 |       wValue field (9.4.3)
+     USB_REQ_SET_DESCR           | Set descr for item/idx in
+                                 |       wValue field (9.4.8)
+     USB_REQ_GET_CFG             | Get current device
+                                 |        configuration (9.4.2)
+     USB_REQ_SET_CFG             | Set current device
+                                 |        configuration (9.4.7)
+     USB_REQ_GET_IF              | Get alternate interface
+                                 |       setting (9.4.4)
+     USB_REQ_SET_IF              | Set alternate interface
+                                 |       setting (9.4.10)
+     USB_REQ_SYNC_FRAME          | Set and report an endpoint's
+                                 |       sync frame (9.4.11)
+
+Unicode language ID, used as wIndex for USB_REQ_SET/GET_DESCRIPTOR
+
+     USB_LANG_ID                 | Unicode English Lang ID for
+                                 | parsing str descr
+.fi
+.in -2
+
+.SH ATTRIBUTES
+.LP
+See attributes(5) for descriptions of the following attributes:
+.sp
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE	ATTRIBUTE VALUE
+_
+Architecture	PCI-based systems
+_
+Interface stability	Committed
+.TE
+
+.SH SEE ALSO
+.LP
+\fBusb_alloc_request\fR(9F), \fBusb_pipe_bulk_xfer\fR(9F),
+\fBusb_pipe_ctrl_xfer\fR(9F), \fBusb_pipe_intr_xfer\fR(9F),
+\fBusb_pipe_isoc_xfer\fR(9F), \fBusb_bulk_request\fR(9S),
+\fBusb_callback_flags\fR(9S), \fBusb_completion_reason\fR(9S),
+\fBusb_intr_request\fR(9S), \fBusb_isoc_request\fR(9S),
+\fBusb_request_attributes\fR(9S)
--- a/share/man/man9s/usb_ctrl_request.9s	Fri Mar 10 17:02:49 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +0,0 @@
-'\" te
-.\" Copyright (c) 2004, Sun Microsystems, Inc., All Rights Reserved
-.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
-.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
-.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_CTRL_REQUEST 9S "Jan 5, 2004"
-.SH NAME
-usb_ctrl_request \- USB control pipe request structure
-.SH SYNOPSIS
-.LP
-.nf
-#include <sys/usb/usba.h>
-.fi
-
-.SH INTERFACE LEVEL
-.sp
-.LP
-Solaris DDI specific (Solaris DDI)
-.SH DESCRIPTION
-.sp
-.LP
-A control request is used to send device commands (or requests) and to read
-status. Please refer to Section\fI 5.5\fR of the \fIUSB 2.0\fR specification
-for information on control pipes. For information on formatting requests, see
-Section \fI9.3\fR of the \fIUSB 2.0\fR specification. The USB 2.0 specification
-is available at \fIwww.usb.org\fR.
-.SH STRUCTURE MEMBERS
-.sp
-.LP
-The fields in the usb_ctrl_req_t are used to format a control request:
-.sp
-.in +2
-.nf
-  uint8_t      ctrl_bmRequestType; /* characteristics of request */
-  uint8_t      ctrl_bRequest;  /* specific request             */
-  uint16_t     ctrl_wValue;    /* varies according to request  */
-  uint16_t     ctrl_wIndex;    /* index or offset              */
-  uint16_t     ctrl_wLength;   /* number of bytes to xfer      */
-  mblk_t       *ctrl_data;     /* data for the data phase  */
-                               /* IN or OUT: allocated by client */
-  uint_t       ctrl_timeout;   /* time until  USBA framework */
-                               /* retires req, in seconds */
-                               /* If set to zero, defaults to 5 sec */
-  usb_opaque_t    ctrl_client_private; /* client private info */
-  usb_req_attrs_t ctrl_attributes; /* attrib. for this req */
-
-  /* Normal callback function, called upon completion. */
-  void       (*ctrl_cb)(
-                 usb_pipe_handle_t ph, struct usb_ctrl_req *req);
-
-  /* Exception callback function, for error handling. */
-  void       (*ctrl_exc_cb)(
-                 usb_pipe_handle_t ph, struct usb_ctrl_req *req);
-  usb_cr_t   ctrl_completion_reason; /* overall success status */
-                                  /* See usb_completion_reason(9S) */
-  usb_cb_flags_t  ctrl_cb_flags; /* recovery done by callback hndlr */
-                                /* See \fBusb_callback_flags(9S)\fR */
-.fi
-.in -2
-
-.sp
-.LP
-Request attributes define special handling for transfers. The following
-attributes are valid for control requests:
-.sp
-.ne 2
-.na
-\fBUSB_ATTRS_SHORT_XFER_OK\fR
-.ad
-.RS 27n
-Accept transfers where less data is received than expected.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_ATTRS_AUTOCLEARING\fR
-.ad
-.RS 27n
-Have USB framework reset pipe and clear functional stalls automatically on
-exception.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_ATTRS_PIPE_RESET\fR
-.ad
-.RS 27n
-Have USB framework reset pipe automatically on exception.
-.RE
-
-.sp
-.LP
-Please see \fBusb_request_attributes\fR(9S) for more information.
-.sp
-.LP
-The following definitions directly pertain to fields in the USB control request
-structure. (See Section \fI 9.3\fR of the \fIUSB 2.0\fR specification.)
-.sp
-.in +2
-.nf
-Direction bitmasks of a control request's ctrl_bmRequestType field
-(USB 2.0 spec, section 9.3.1)
-
-     USB_DEV_REQ_HOST_TO_DEV     | Host to device direction
-     USB_DEV_REQ_DEV_TO_HOST     | Device to host direction
-     USB_DEV_REQ_DIR_MASK        | Bitmask of direction bits
-
-Request type bitmasks of a control request's ctrl_bmRequestType field
-(USB 2.0 spec, section 9.3.1)
-
-     USB_DEV_REQ_TYPE_STANDARD   | USB 2.0 defined command
-                                 | for all USB devices
-     USB_DEV_REQ_TYPE_CLASS      | USB 2.0 defined
-                                 | class-specific command
-     USB_DEV_REQ_TYPE_VENDOR     | Vendor-specific command
-     USB_DEV_REQ_TYPE_MASK       | Bitmask of request type bits
-
-Recipient bitmasks of a control request's ctrl_bmRequestType field
-(USB 2.0 spec, section 9.3.1)
-
-     USB_DEV_REQ_RCPT_DEV        | Request is for device
-     USB_DEV_REQ_RCPT_IF         | Request is for interface
-     USB_DEV_REQ_RCPT_EP         | Request is for endpoint
-     USB_DEV_REQ_RCPT_OTHER      | Req is for other than above
-     USB_DEV_REQ_RCPT_MASK       | Bitmask of request recipient bits
-
-Standard requests (USB 2.0 spec, section 9.4)
-     USB_REQ_GET_STATUS          | Get status of device, endpoint
-                                 |or interface (9.4.5)
-     USB_REQ_CLEAR_FEATURE       | Clear feature specified by
-                                 |wValue field (9.4.1)
-     USB_REQ_SET_FEATURE         | Set feature specified by
-                                 |       wValue field (9.4.9)
-     USB_REQ_SET_ADDRESS         | Set address specified by
-                                 |       wValue field (9.4.6)
-     USB_REQ_GET_DESCR           | Get descr for item/idx in
-                                 |       wValue field (9.4.3)
-     USB_REQ_SET_DESCR           | Set descr for item/idx in
-                                 |       wValue field (9.4.8)
-     USB_REQ_GET_CFG             | Get current device
-                                 |        configuration (9.4.2)
-     USB_REQ_SET_CFG             | Set current device
-                                 |        configuration (9.4.7)
-     USB_REQ_GET_IF              | Get alternate interface
-                                 |       setting (9.4.4)
-     USB_REQ_SET_IF              | Set alternate interface
-                                 |       setting (9.4.10)
-     USB_REQ_SYNC_FRAME          | Set and report an endpoint's
-                                 |       sync frame (9.4.11)
-
-Unicode language ID, used as wIndex for USB_REQ_SET/GET_DESCRIPTOR
-
-     USB_LANG_ID                 | Unicode English Lang ID for
-                                 | parsing str descr
-.fi
-.in -2
-
-.SH ATTRIBUTES
-.sp
-.LP
-See attributes(5) for descriptions of the following attributes:
-.sp
-
-.sp
-.TS
-box;
-c | c
-l | l .
-ATTRIBUTE TYPE	ATTRIBUTE VALUE
-_
-Architecture	PCI-based systems
-_
-Interface stability	Committed
-.TE
-
-.SH SEE ALSO
-.sp
-.LP
-\fBusb_alloc_request\fR(9F), \fBusb_pipe_bulk_xfer\fR(9F),
-\fBusb_pipe_ctrl_xfer\fR(9F), \fBusb_pipe_intr_xfer\fR(9F),
-\fBusb_pipe_isoc_xfer\fR(9F), \fBusb_bulk_request\fR(9S),
-\fBusb_callback_flags\fR(9S), \fBusb_completion_reason\fR(9S),
-\fBusb_intr_request\fR(9S), \fBusb_isoc_request\fR(9S),
-\fBusb_request_attributes\fR(9S)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/man/man9s/usb_ep_ss_comp_descr.9s	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,122 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source.  A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2016 Joyent, Inc.
+.\"
+.Dd Aug 10, 2016
+.Dt USB_EP_SS_COMP_DESCR 9S
+.Os
+.Sh NAME
+.Nm usb_ep_ss_comp_descr ,
+.Nm usb_ep_ss_comp_descr_t
+.Nd USB endpoint SuperSpeed Companion Descriptor
+.Sh SYNOPSIS
+.In sys/usb/usba.h
+.Sh INTERFACE LEVEL
+illumos DDI Specific
+.Sh DESCRIPTION
+The
+.Sy usb_ep_ss_comp_descr_t
+structure defines additional endpoint attributes for USB 3.0 and newer
+devices. This structure is considered a
+.Em companion descriptor .
+On its own, it does not uniquely define an endpoint. A standard USB
+descriptor is still required. See
+.Xr usb_ep_descr 9S
+for the definition of the standard descriptor.
+.Pp
+If available, the SuperSpeed companion descriptor can be accessed by
+getting the endpoint data through a call to
+.Xr usb_lookup_ep_data 9F .
+These descriptors are required to open pipes for USB 3.0 and newer
+devices. They can be assembled into the proper format for
+.Xr usb_pipe_xopen 9F
+by calling
+.Xr usb_ep_xdescr_fill 9F .
+.Pp
+This structure is formally defined in section 9.6.7 of the USB 3.1
+specification.
+.Sh STRUCTURE MEMBERS
+.Bd -literal -offset indent
+uint8_t		bLength;
+uint8_t		bDescriptorType;
+uint8_t		bMaxBurst;
+uint8_t		bmAttributes;
+uint16_t	wBytesPerInterval;
+.Ed
+.Pp
+The
+.Sy bLength
+member is always set to the size of this descriptor, which is usually
+six.
+.Pp
+The
+.Sy bDescriptorType
+member should be set to the macro
+.Sy USB_DESCR_TYPE_SS_EP_COMP
+whose value is 0x30.
+.Pp
+The
+.Sy bMaxBurst
+member indicates the maximum number of packets that the endpoint can
+send in one 'burst'. Valid values range from 0 to 15 and the values are
+one less than the number of packets. A value of 0 indicates that 1
+packet can be sent in a burst. A value of 15 indicates that 16 packets
+can be sent in a burst.
+.Pp
+The
+.Sy bmAttributes
+member indicates different attributes of the endpoint. This member is
+reserved and should be zero for
+.Sy control
+and
+.Sy interrupt
+endpoints.
+.Pp
+For a
+.Sy bulk
+endpoint, the
+.Sy bmAttributes
+member is used to indicate the maximum number of streams that the device
+supports. The first five bits (4:0) are used, the remaining 3 bits are
+reserved and should be zero. Values range from 0 to 16.  A value of zero
+indicates that streams are not supported. Otherwise, it indicates that
+the device supports 2 raised to the value number of streams. A value of
+3, indicates 2^3 streams are supported.
+.Pp
+For an
+.Sy isochronous
+endpoint, the
+.Sy bmAttributes
+member is used to indicate the value of the
+.Sy Mult
+property, a value used to calculate the maximum number of packets the
+device and receive in a service interval. The first two bits (1:0) are
+used to determine the mult. The remaining 6 bits (7:2) are reserved and
+should be set to zero.
+.Pp
+The
+.Sy wBytesPerInterval
+member is used to indicate the total number of bytes that can be
+transferred in one service interval. Note, this is only valid for
+.Sy Isochronous
+and
+.Sy Interrupt IN
+(periodic) endpoints.
+.Sh SEE ALSO
+.Xr usb_ep_xdescr_fill 9F ,
+.Xr usb_pipe_xopen 9F ,
+.Xr usb_ep_descr 9S ,
+.Xr usb_ep_xdescr 9S
+.Rs
+.%T Universal Serial Bus 3.1 Specification
+.%U www.usb.org
+.Re
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/man/man9s/usb_ep_xdescr.9s	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,117 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source.  A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2016 Joyent, Inc.
+.\"
+.Dd Sep 16, 2016
+.Dt USB_EP_XDESCR 9S
+.Os
+.Sh NAME
+.Nm usb_ep_xdescr ,
+.Nm usb_ep_xdescr_t
+.Nd extended endpoint descriptor
+.Sh SYNOPSIS
+.In sys/usb/usba.h
+.Sh INTERFACE LEVEL
+illumos DDI Specific
+.Sh DESCRIPTION
+The
+.Sy usb_ep_xdescr_t
+structure is used to describe an endpoint descriptor as well account for
+the continuing evolutions in the USB specification.
+.Pp
+Starting with the
+.Em USB 3.0
+specification,
+.Em USB 3.0
+endpoints have an endpoint SuperSpeed companion descriptor. See
+.Xr usb_ep_ss_comp_descr 9S
+for a description of the descriptor. In the
+.Em USB 3.1
+specification, certain endpoints will have additional companion
+descriptors.
+.Pp
+The
+.Sy usb_ep_xdescr_t
+structure, combined with the
+.Xr usb_ep_xdescr_fill 9F
+and
+.Xr usb_pipe_xopen 9F
+are designed to abstract away the need for USB client device drivers to
+need to be updated in the face of these newer endpoints, whose
+information is required for host controller devices to properly program
+the device.
+.Pp
+After looking up endpoint data, through the
+.Xr usb_lookup_ep_data 9F ,
+device drivers should call the
+.Xr usb_ep_xdescr_fill 9F
+function. After that, the
+.Sy usb_ep_xdescr_t
+structure will be filled in.
+.Sh STRUCTURE MEMBERS
+The
+.Sy usb_ep_xdescr_t
+structure has the following members:
+.Bd -literal -offset indent
+uint_t			uex_version;
+usb_ep_xdescr_flags_t	uex_flags;
+usb_ep_descr_t		uex_ep;
+usb_ep_ss_comp_descr_t	uex_ep_ss;
+.Ed
+.Pp
+The
+.Sy uex_version
+member is used to describe the current version of this structure. This
+member will be set to the value passed in by the device driver to
+.Xr usb_ep_xdescr_fil 9F .
+Device drivers should ignore this field and should not modify the value
+placed there or modify it.
+.Pp
+The
+.Sy uex_flags
+member is an enumeration that defines a number of flags. Each flag
+indicates whether or not a given member is present or valid. Before
+accessing any member other than
+.Sy uex_ep ,
+the device driver should check the flag here, otherwise its contents may
+be undefined. Currently the following flags are defined:
+.Bl -tag -width Sy -offset indent
+.It Sy USB_EP_XFLAGS_SS_COMP
+Indicates that a SuperSpeed endpoint companion descriptor is present and
+has been filled in. The member
+.Sy uex_ep_ss
+is valid.
+.El
+.Pp
+The
+.Sy uex_ep
+member contains a traditional USB endpoint descriptor. Its contents are
+defined in
+.Xr usb_ep_descr 9S .
+There is no flag for this member in
+.Sy uex_flags ,
+it is always valid.
+.Pp
+The
+.Sy uex_ep_ss
+member contains a USB 3.0 SuperSpeed endpoint companion descriptor as
+defined in
+.Xr usb_ep_ss_comp_descr 9S .
+This member is only valid if the
+.Sy USB_EP_XFLAGS_SS_COMP
+flag is specified in
+.Sy uex_flags .
+.Sh SEE ALSO
+.Xr usb_ep_xdescr_fill 9F ,
+.Xr usb_pipe_xopen 9F ,
+.Xr usb_ep_descr 9S ,
+.Xr usb_ep_ss_comp_descr 9S
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/man/man9s/usb_intr_req.9s	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,287 @@
+'\" te
+.\" Copyright (c) 2004, Sun Microsystems, Inc., All Rights Reserved
+.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
+.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
+.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
+.TH USB_INTR_REQUEST 9S "April 9, 2016"
+.SH NAME
+usb_intr_req, usb_intr_req_t, usb_intr_request \- USB interrupt request structure
+.SH SYNOPSIS
+.LP
+.nf
+#include <sys/usb/usba.h>
+.fi
+
+.SH INTERFACE LEVEL
+.LP
+Solaris DDI specific (Solaris DDI)
+.SH DESCRIPTION
+.LP
+An interrupt request (that is, a request sent through an interrupt pipe), is
+used to transfer  small amounts  of data infrequently, but with bounded
+service periods. (Data flows in either direction.) Please refer to Section
+\fI5.7\fR of the \fIUSB 2.0\fR specification for information on interrupt
+transfers. (The \fIUSB 2.0\fR specification is available at \fIwww.usb.org\fR.)
+.sp
+.LP
+The fields in the usb_intr_req_t are used to format an interrupt request.
+Please see below for acceptable combinations of flags and attributes.
+.sp
+.LP
+The usb_intr_req_t fields are:
+.sp
+.in +2
+.nf
+  ushort_t       intr_len;     /* Size of pkt. Must be set */
+                                /* Max size is 8K for low/full speed */
+                                /* Max size is 20K for high speed */
+   mblk_t         *intr_data;   /* Data for the data phase  */
+                                /* IN: zero-len mblk alloc by client */
+                                /* OUT: allocated by client */
+   usb_opaque_t   intr_client_private; /* client specific information  */
+   uint_t         intr_timeout; /* only with ONE TIME POLL, in secs */
+                                /* If set to zero, defaults to 5 sec */
+   usb_req_attrs_t intr_attributes;
+
+   /* Normal callback function, called upon completion. */
+   void           (*intr_cb)(
+                      usb_pipe_handle_t ph, struct usb_intr_req *req);
+
+   /* Exception callback function, for error handling. */
+   void           (*intr_exc_cb)(
+                      usb_pipe_handle_t ph, struct usb_intr_req *req);
+
+   /* set by USBA/HCD on completion */
+   usb_cr_t    intr_completion_reason; /* overall completion status */
+                                      /* See usb_completion_reason(9S) */
+   usb_cb_flags_t intr_cb_flags; /* recovery done by callback hndlr */
+                                     /* See usb_callback_flags(9S) */
+.fi
+.in -2
+
+.sp
+.LP
+Request attributes define special handling for transfers. The following
+attributes are valid for interrupt requests:
+.sp
+.ne 2
+.na
+\fBUSB_ATTRS_SHORT_XFER_OK\fR
+.ad
+.RS 27n
+Accept transfers where less data is received than expected.
+.RE
+
+.sp
+.ne 2
+.na
+\fBUSB_ATTRS_AUTOCLEARING\fR
+.ad
+.RS 27n
+Have USB framework reset pipe and clear functional stalls automatically on
+exception.
+.RE
+
+.sp
+.ne 2
+.na
+\fBUSB_ATTRS_PIPE_RESET\fR
+.ad
+.RS 27n
+Have USB framework reset pipe automatically on exception.
+.RE
+
+.sp
+.ne 2
+.na
+\fBUSB_ATTRS_ONE_XFER\fR
+.ad
+.RS 27n
+Perform a single IN transfer.  Do not start periodic transfers with this
+request.
+.RE
+
+.sp
+.LP
+Please see \fBusb_request_attributes\fR(9S) for more information.
+.sp
+.in +2
+.nf
+Interrupt transfers/requests are subject to the following
+constraints and caveats:
+
+1) The following table indicates combinations of
+   usb_pipe_intr_xfer() flags argument and fields
+   of the usb_intr_req_t request argument (X = don't care):
+
+     "none" as attributes in the table below indicates
+     neither ONE_XFER nor SHORT_XFER_OK
+
+     flags     Type  attributes      data    timeout semantics
+     ----------------------------------------------------------------
+     X         IN    X               !=NULL  X       illegal
+
+     X         IN    !ONE_XFER       X       !=0     illegal
+
+     X         IN    !ONE_XFER       NULL    0       See table note (A)
+
+     no sleep  IN    ONE_XFER        NULL    0       See table note (B)
+
+     no sleep  IN    ONE_XFER        NULL    !=0     See table note (C)
+
+     sleep     IN    ONE_XFER        NULL    0       See table note (D)
+
+     sleep     IN    ONE_XFER        NULL    !=0     See table note (E)
+
+     X         OUT   X               NULL    X       illegal
+
+     X         OUT   ONE_XFER        X       X       illegal
+
+     X         OUT   SHORT_XFER_OK   X       X       illegal
+
+     no sleep  OUT   none            !=NULL  0       See table note (F)
+
+     no sleep  OUT   none            !=NULL  !=0     See table note (G)
+
+     sleep     OUT   none            !=NULL  0       See table note (H)
+
+     sleep     OUT   none            !=NULL  !=0     See table note (I)
+
+     Table notes:
+
+       A) Continuous polling, new data is returned in cloned request
+          structures via continuous callbacks, original request is
+          returned on stop polling.
+
+       B) One time poll, no timeout, callback when data is
+          received.
+
+       C) One time poll, with timeout, callback when data
+          is received.
+
+       D) One time poll, no timeout, one callback, unblock
+          when transfer completes.
+
+       E) One time poll, timeout, one callback, unblock when
+          transfer completes or timeout occurs.
+
+       F) Transfer until data exhausted, no timeout, callback
+          when done.
+
+       G) Transfer until data exhausted, timeout, callback
+          when done.
+
+       H) Transfer until data exhausted, no timeout, unblock
+          when data is received.
+
+       I) Transfer until data exhausted, timeout, unblock
+          when data is received.
+
+
+   2) USB_FLAGS_SLEEP indicates here just to wait for
+      resources, except when ONE_XFER is set, in which case it
+      also waits for completion before returning.
+
+   3) Reads (IN):
+
+       a) The client driver does *not* provide a data buffer.
+          By default, a READ request would mean continuous
+          polling for data IN.  The USBA framework allocates a
+          new data buffer for each poll.  intr_len specifies
+         the amount of 'periodic data' for each poll.
+
+       b) The USBA framework issues a callback to the client
+          at the end of a polling interval when there is data to
+          return.  Each callback returns its data in a new request
+          cloned from the original.  Note that the amount of data
+          read IN is either intr_len or "wMaxPacketSize" in length.
+
+       c) Normally, the HCD keeps polling the interrupt endpoint
+          forever even if there is no data to be read IN.  A
+          client driver may stop this polling by calling
+          usb_pipe_stop_intr_polling(9F).
+
+       d) If a client driver chooses to pass USB_ATTRS_ONE_XFER
+          as 'xfer_attributes' the HCD polls for data until
+          some data is received.  The USBA framework reads in
+          the data, does a callback, and stops polling for any
+          more data.  In this case, the client
+          driver need not explicitly call
+          usb_pipe_stop_intr_polling().
+
+       e) All requests with USB_ATTRS_ONE_XFER require callbacks
+          to be specified.
+
+       f) When continuous polling is stopped, the original
+          request is returned with USB_CR_STOPPED_POLLING.
+
+       g) If the USB_ATTRS_SHORT_XFER_OK attribute is not set
+          and a short transfer is received while polling,
+          an error is assumed and polling is stopped.  In this
+          case or the case of other errors, the error must be cleared
+          and polling restarted by the client driver. Setting the
+          USB_ATTRS_AUTOCLEARING attribute will clear the error
+          but not restart polling.  (NOTE: Polling can be
+          restarted from an exception callback corresponding to
+          an original request.  Please see usb_pipe_intr_xfer(9F)
+          for more information.
+
+   4) Writes (OUT):
+
+       a) A client driver provides the data buffer, and
+          data, needed for intr write.
+
+       b) Unlike read (see previous section), there
+         is no continuous write mode.
+
+       c) The USB_ATTRS_ONE_XFER attribute is illegal.
+          By default USBA keeps writing intr data until
+          the provided data buffer has been
+          written out. The USBA framework does ONE
+          callback to the client driver.
+
+       d) Queueing is supported.
+
+    The intr_completion_reason indicates the status
+    of the transfer.  See usb_completion_reason(9S) for
+    usb_cr_t definitions.
+
+    The intr_cb_flags are set prior to calling the
+    exception callback handler, to summarize recovery actions
+    taken and errors encountered during
+    recovery.  See usb_callback_flags(9S) for
+    usb_cb_flags_t definitions.
+
+    --- Callback handling ---
+
+    All usb request types share the same callback
+    handling. Please see usb_callback_flags(9S) for a
+   description of use and operation.
+.fi
+.in -2
+
+.SH ATTRIBUTES
+.LP
+See attributes(5) for descriptions of the following attributes:
+.sp
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE	ATTRIBUTE VALUE
+_
+Architecture	PCI-based systems
+_
+Interface stability	Committed
+.TE
+
+.SH SEE ALSO
+.LP
+\fBusb_alloc_request\fR(9F), \fBusb_pipe_ctrl_xfer\fR(9F),
+\fBusb_pipe_bulk_xfer\fR(9F), \fBusb_pipe_intr_xfer\fR(9F),
+\fBusb_pipe_isoc_xfer\fR(9F), \fBusb_bulk_request\fR(9S),
+\fBusb_callback_flags\fR(9S), \fBusb_completion_reason\fR(9S),
+\fBusb_ctrl_request\fR(9S), \fBusb_isoc_request\fR(9S),
+\fBusb_request_attributes\fR(9S)
--- a/share/man/man9s/usb_intr_request.9s	Fri Mar 10 17:02:49 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,287 +0,0 @@
-'\" te
-.\" Copyright (c) 2004, Sun Microsystems, Inc., All Rights Reserved
-.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
-.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
-.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_INTR_REQUEST 9S "April 9, 2016"
-.SH NAME
-usb_intr_request \- USB interrupt request structure
-.SH SYNOPSIS
-.LP
-.nf
-#include <sys/usb/usba.h>
-.fi
-
-.SH INTERFACE LEVEL
-.LP
-Solaris DDI specific (Solaris DDI)
-.SH DESCRIPTION
-.LP
-An interrupt request (that is, a request sent through an interrupt pipe), is
-used to transfer  small amounts  of data infrequently, but with bounded
-service periods. (Data flows in either direction.) Please refer to Section
-\fI5.7\fR of the \fIUSB 2.0\fR specification for information on interrupt
-transfers. (The \fIUSB 2.0\fR specification is available at \fIwww.usb.org\fR.)
-.sp
-.LP
-The fields in the usb_intr_req_t are used to format an interrupt request.
-Please see below for acceptable combinations of flags and attributes.
-.sp
-.LP
-The usb_intr_req_t fields are:
-.sp
-.in +2
-.nf
-  ushort_t       intr_len;     /* Size of pkt. Must be set */
-                                /* Max size is 8K for low/full speed */
-                                /* Max size is 20K for high speed */
-   mblk_t         *intr_data;   /* Data for the data phase  */
-                                /* IN: zero-len mblk alloc by client */
-                                /* OUT: allocated by client */
-   usb_opaque_t   intr_client_private; /* client specific information  */
-   uint_t         intr_timeout; /* only with ONE TIME POLL, in secs */
-                                /* If set to zero, defaults to 5 sec */
-   usb_req_attrs_t intr_attributes;
-
-   /* Normal callback function, called upon completion. */
-   void           (*intr_cb)(
-                      usb_pipe_handle_t ph, struct usb_intr_req *req);
-
-   /* Exception callback function, for error handling. */
-   void           (*intr_exc_cb)(
-                      usb_pipe_handle_t ph, struct usb_intr_req *req);
-
-   /* set by USBA/HCD on completion */
-   usb_cr_t    intr_completion_reason; /* overall completion status */
-                                      /* See usb_completion_reason(9S) */
-   usb_cb_flags_t intr_cb_flags; /* recovery done by callback hndlr */
-                                     /* See usb_callback_flags(9S) */
-.fi
-.in -2
-
-.sp
-.LP
-Request attributes define special handling for transfers. The following
-attributes are valid for interrupt requests:
-.sp
-.ne 2
-.na
-\fBUSB_ATTRS_SHORT_XFER_OK\fR
-.ad
-.RS 27n
-Accept transfers where less data is received than expected.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_ATTRS_AUTOCLEARING\fR
-.ad
-.RS 27n
-Have USB framework reset pipe and clear functional stalls automatically on
-exception.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_ATTRS_PIPE_RESET\fR
-.ad
-.RS 27n
-Have USB framework reset pipe automatically on exception.
-.RE
-
-.sp
-.ne 2
-.na
-\fBUSB_ATTRS_ONE_XFER\fR
-.ad
-.RS 27n
-Perform a single IN transfer.  Do not start periodic transfers with this
-request.
-.RE
-
-.sp
-.LP
-Please see \fBusb_request_attributes\fR(9S) for more information.
-.sp
-.in +2
-.nf
-Interrupt transfers/requests are subject to the following
-constraints and caveats:
-
-1) The following table indicates combinations of
-   usb_pipe_intr_xfer() flags argument and fields
-   of the usb_intr_req_t request argument (X = don't care):
-
-     "none" as attributes in the table below indicates
-     neither ONE_XFER nor SHORT_XFER_OK
-
-     flags     Type  attributes      data    timeout semantics
-     ----------------------------------------------------------------
-     X         IN    X               !=NULL  X       illegal
-
-     X         IN    !ONE_XFER       X       !=0     illegal
-
-     X         IN    !ONE_XFER       NULL    0       See table note (A)
-
-     no sleep  IN    ONE_XFER        NULL    0       See table note (B)
-
-     no sleep  IN    ONE_XFER        NULL    !=0     See table note (C)
-
-     sleep     IN    ONE_XFER        NULL    0       See table note (D)
-
-     sleep     IN    ONE_XFER        NULL    !=0     See table note (E)
-
-     X         OUT   X               NULL    X       illegal
-
-     X         OUT   ONE_XFER        X       X       illegal
-
-     X         OUT   SHORT_XFER_OK   X       X       illegal
-
-     no sleep  OUT   none            !=NULL  0       See table note (F)
-
-     no sleep  OUT   none            !=NULL  !=0     See table note (G)
-
-     sleep     OUT   none            !=NULL  0       See table note (H)
-
-     sleep     OUT   none            !=NULL  !=0     See table note (I)
-
-     Table notes:
-
-       A) Continuous polling, new data is returned in cloned request
-          structures via continuous callbacks, original request is
-          returned on stop polling.
-
-       B) One time poll, no timeout, callback when data is
-          received.
-
-       C) One time poll, with timeout, callback when data
-          is received.
-
-       D) One time poll, no timeout, one callback, unblock
-          when transfer completes.
-
-       E) One time poll, timeout, one callback, unblock when
-          transfer completes or timeout occurs.
-
-       F) Transfer until data exhausted, no timeout, callback
-          when done.
-
-       G) Transfer until data exhausted, timeout, callback
-          when done.
-
-       H) Transfer until data exhausted, no timeout, unblock
-          when data is received.
-
-       I) Transfer until data exhausted, timeout, unblock
-          when data is received.
-
-
-   2) USB_FLAGS_SLEEP indicates here just to wait for
-      resources, except when ONE_XFER is set, in which case it
-      also waits for completion before returning.
-
-   3) Reads (IN):
-
-       a) The client driver does *not* provide a data buffer.
-          By default, a READ request would mean continuous
-          polling for data IN.  The USBA framework allocates a
-          new data buffer for each poll.  intr_len specifies
-         the amount of 'periodic data' for each poll.
-
-       b) The USBA framework issues a callback to the client
-          at the end of a polling interval when there is data to
-          return.  Each callback returns its data in a new request
-          cloned from the original.  Note that the amount of data
-          read IN is either intr_len or "wMaxPacketSize" in length.
-
-       c) Normally, the HCD keeps polling the interrupt endpoint
-          forever even if there is no data to be read IN.  A
-          client driver may stop this polling by calling
-          usb_pipe_stop_intr_polling(9F).
-
-       d) If a client driver chooses to pass USB_ATTRS_ONE_XFER
-          as 'xfer_attributes' the HCD polls for data until
-          some data is received.  The USBA framework reads in
-          the data, does a callback, and stops polling for any
-          more data.  In this case, the client
-          driver need not explicitly call
-          usb_pipe_stop_intr_polling().
-
-       e) All requests with USB_ATTRS_ONE_XFER require callbacks
-          to be specified.
-
-       f) When continuous polling is stopped, the original
-          request is returned with USB_CR_STOPPED_POLLING.
-
-       g) If the USB_ATTRS_SHORT_XFER_OK attribute is not set
-          and a short transfer is received while polling,
-          an error is assumed and polling is stopped.  In this
-          case or the case of other errors, the error must be cleared
-          and polling restarted by the client driver. Setting the
-          USB_ATTRS_AUTOCLEARING attribute will clear the error
-          but not restart polling.  (NOTE: Polling can be
-          restarted from an exception callback corresponding to
-          an original request.  Please see usb_pipe_intr_xfer(9F)
-          for more information.
-
-   4) Writes (OUT):
-
-       a) A client driver provides the data buffer, and
-          data, needed for intr write.
-
-       b) Unlike read (see previous section), there
-         is no continuous write mode.
-
-       c) The USB_ATTRS_ONE_XFER attribute is illegal.
-          By default USBA keeps writing intr data until
-          the provided data buffer has been
-          written out. The USBA framework does ONE
-          callback to the client driver.
-
-       d) Queueing is supported.
-
-    The intr_completion_reason indicates the status
-    of the transfer.  See usb_completion_reason(9S) for
-    usb_cr_t definitions.
-
-    The intr_cb_flags are set prior to calling the
-    exception callback handler, to summarize recovery actions
-    taken and errors encountered during
-    recovery.  See usb_callback_flags(9S) for
-    usb_cb_flags_t definitions.
-
-    --- Callback handling ---
-
-    All usb request types share the same callback
-    handling. Please see usb_callback_flags(9S) for a
-   description of use and operation.
-.fi
-.in -2
-
-.SH ATTRIBUTES
-.LP
-See attributes(5) for descriptions of the following attributes:
-.sp
-
-.sp
-.TS
-box;
-c | c
-l | l .
-ATTRIBUTE TYPE	ATTRIBUTE VALUE
-_
-Architecture	PCI-based systems
-_
-Interface stability	Committed
-.TE
-
-.SH SEE ALSO
-.LP
-\fBusb_alloc_request\fR(9F), \fBusb_pipe_ctrl_xfer\fR(9F),
-\fBusb_pipe_bulk_xfer\fR(9F), \fBusb_pipe_intr_xfer\fR(9F),
-\fBusb_pipe_isoc_xfer\fR(9F), \fBusb_bulk_request\fR(9S),
-\fBusb_callback_flags\fR(9S), \fBusb_completion_reason\fR(9S),
-\fBusb_ctrl_request\fR(9S), \fBusb_isoc_request\fR(9S),
-\fBusb_request_attributes\fR(9S)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/man/man9s/usb_isoc_req.9s	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,259 @@
+'\" te
+.\" Copyright (c) 2006, Sun Microsystems, Inc., All Rights Reserved
+.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
+.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
+.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
+.TH USB_ISOC_REQUEST 9S "April 9, 2016"
+.SH NAME
+usb_isoc_req, usb_isoc_req_t, usb_isoc_request \- USB isochronous request structure
+.SH SYNOPSIS
+.LP
+.nf
+#include <sys/usb/usba.h>
+.fi
+
+.SH INTERFACE LEVEL
+.LP
+Solaris DDI specific (Solaris DDI)
+.SH DESCRIPTION
+.LP
+A request sent through an isochronous pipe is used to transfer large amounts of
+packetized data with relative unreliability, but with bounded service periods.
+A packet is guaranteed to be tried within a bounded time period, but is not
+retried upon failure. Isochronous transfers are supported on both USB 1.1 and
+USB 2.0 devices. For further information, see section \fI5.6\fR of the \fIUSB
+2.0\fR specification available at \fIwww.usb.org\fR.
+.sp
+.LP
+This section provides information on acceptable combinations of flags and
+attributes with additional details. The following fields of the usb_isoc_req_t
+are used to format an isochronous request.
+.sp
+.in +2
+.nf
+usb_frame_number_t
+                isoc_frame_no;   /* frame num to start sending req. */
+ushort_t       isoc_pkts_count; /* num USB pkts in this request */
+/*
+ * The sum of all pkt lengths in an isoc request. Recommend to set it to
+ * zero, so the sum of isoc_pkt_length in the isoc_pkt_descr list will be
+ * used automatically and no check will be apply to this element.
+ */
+ushort_t       isoc_pkts_length;
+ushort_t       isoc_error_count;/* num pkts completed w/errs */
+usb_req_attrs_t isoc_attributes;/* request-specific attrs */
+mblk_t          *isoc_data;     /* data to xfer */
+                                 /* IN or OUT: alloc. by client. */
+                                 /* Size=total of all pkt lengths. */
+usb_opaque_t     isoc_client_private; /* for client driver excl use. */
+struct usb_isoc_pkt_descr       /* (see below) */
+                 *isoc_pkt_descr;
+
+/*
+ * Normal callback function, called upon completion.
+ * This function cannot block as it executes in soft interrupt context.
+ */
+void       (*isoc_cb)(
+                usb_pipe_handle_t ph, struct usb_isoc_req *req);
+
+/* Exception callback function, for error handling. */
+void       (*isoc_exc_cb)(
+               usb_pipe_handle_t ph, struct usb_isoc_req *req);
+
+usb_cr_t  isoc_completion_reason; /* overall completion status */
+                                   /* set by USBA framework */
+                                   /* See usb_completion_reason(9S) */
+usb_cb_flags_t  isoc_cb_flags;    /* recovery done by callback hndlr */
+                                   /* set by USBA on exception. */
+                                   /* See usb_callback_flags(9S) */
+.fi
+.in -2
+
+.sp
+.LP
+A \fBusb_isoc_pkt_descr_t\fR describes the status of an isochronous packet
+transferred within a frame or microframe. The following fields of a
+\fBusb_isoc_pkt_descr_t\fR packet descriptor are used within an
+\fBusb_isoc_req_t\fR. The \fBisoc_pkt_length\fR is set by the client driver to
+the amount of data managed by the packet for input or output. The latter two
+fields are set by the \fBUSBA\fR framework to indicate status. Any packets with
+an \fBisoc_completion_reason\fR, other than \fBUSB_CR_OK\fR, are reflected in
+the \fBisoc_error_count\fR of the \fBusb_isoc_req_t\fR.
+.sp
+.in +2
+.nf
+     ushort_t    isoc_pkt_length;        /* number bytes to transfer */
+      ushort_t    isoc_pkt_actual_length; /* actual number transferred */
+      usb_cr_t    isoc_pkt_status;        /* completion status */
+.fi
+.in -2
+
+.sp
+.LP
+If two multi-frame \fBisoc\fR requests that both specify the
+\fBUSB_ATTRS_ISOC_XFER_ASAP\fR attribute are scheduled closely together, the
+first frame of the second request is queued to start after the last frame of
+the first request.
+.sp
+.LP
+No stalls are seen in isochronous transfer exception callbacks. Because
+transfers are not retried upon failure, isochronous transfers continue
+regardless of errors.
+.sp
+.LP
+Request attributes define special handling for transfers. The following
+attributes are valid for isochronous requests:
+.sp
+.ne 2
+.na
+\fB\fBUSB_ATTRS_ISOC_START_FRAME\fR\fR
+.ad
+.RS 30n
+Start transferring at the starting frame number specified in the
+\fBisoc_frame_no\fR field of the request.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBUSB_ATTRS_ISOC_XFER_ASAP\fR\fR
+.ad
+.RS 30n
+Start transferring as soon as possible. The \fBUSBA\fR framework picks an
+immediate frame number to map to the starting frame number.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBUSB_ATTRS_SHORT_XFER_OK\fR\fR
+.ad
+.RS 30n
+Accept transfers where less data is received than expected.
+.RE
+
+.sp
+.LP
+The \fBusb_isoc_req_t\fR contains an array of descriptors that describe
+isochronous packets. One isochronous packet is sent per frame or microframe.
+Because packets that comprise a transfer are sent across consecutive frames or
+microframes, \fBUSB_ATTRS_ONE_XFER\fR is invalid.
+.sp
+.LP
+See \fBusb_request_attributes\fR(9S) for more information.
+.sp
+.LP
+Isochronous transfers/requests are subject to the following constraints and
+caveats:
+.sp
+.in +2
+.nf
+1) The following table indicates combinations of usb_pipe_isoc_xfer
+   flags argument and fields of the usb_isoc_req_t request argument
+   (X = don't care). (Note that attributes considered in this table
+   are ONE_XFER, START_FRAME, XFER_ASAP, and SHORT_XFER, and that
+   some transfer types are characterized by multiple table entries.)
+
+Flags Type     Attributes          Data    Semantics
+---------------------------------------------------------------
+X      X      X                    NULL    illegal
+
+X      X      ONE_XFER             X       illegal
+
+X      X      ISOC_START_FRAME     X       illegal
+              & ISOC_XFER_ASAP
+
+X      X      !ISOC_START_FRAME    X       illegal
+              & !ISOC_XFER_ASAP
+
+X      OUT    SHORT_XFER_OK        X       illegal
+
+X      IN     X                    !=NULL  See table note (A)
+
+X      X      ISOC_START_FRAME     !=NULL  See table note (B)
+
+X      X      ISOC_XFER_ASAP       !=NULL  See table note (C)
+
+Table notes:
+
+    A) continuous polling, new data is returned in
+       cloned request structures via continuous callbacks,
+       original request is returned on stop polling
+
+    B) invalid if the current_frame number is past
+       "isoc_frame_no" or "isoc_frame_no" == 0
+
+    C)"isoc_frame_no" is ignored. The USBA framework
+       determines which frame to insert and start
+       the transfer.
+
+2) USB_FLAGS_SLEEP indicates to wait for resources but
+   not for completion.
+
+3) For polled reads:
+
+  A. The USBA  framework  accepts  a  request  which
+     specifies  the  size and number of packets to fill
+     with data. The packets get filled one  packet  per
+     (1  ms)  frame/(125 us) microframe.  All  requests
+     have an implicit USB_ATTRS_SHORT_XFER_OK attribute
+     set, since transfers  continue in spite of any en-
+     countered. The amount of data read per packet  will
+     match  the  isoc_pkt_length  field  of  the packet
+     descriptor unless a  short  transfer  occurs.  The
+     actual     size     is     returned     in     the
+     isoc_pkt_actual_length   field   of   the   packet
+     descriptor.  When  all packets of the request have
+     been processed, a normal callback is done to  sig-
+     nal the completion of the original request.
+
+  B. When continuous polling is stopped, the original
+     request is returned in an exception callback with a
+     completion reason of USB_CR_STOPPED_POLLING.
+     (NOTE: Polling can be restarted from  an exception
+     callback corresponding to an original request.
+     Please see usb_pipe_isoc_xfer(9F) for more information.
+
+  C. Callbacks must be specified.
+
+  The isoc_completion_reason indicates the status of the transfer. See
+  usb_completion_reason(9s) for usb_cr_t definitions.
+
+  The isoc_cb_flags are set prior to calling the exception
+  callback handler to summarize recovery actions taken and
+  errors encountered during recovery. See usb_callback_flags(9s)
+  for usb_cb_flags_t definitions.
+
+--- Callback handling ---
+All usb request types share the same callback handling. Please see
+usb_callback_flags(9s) for a description of use and operation.
+.fi
+.in -2
+
+.SH ATTRIBUTES
+.LP
+See attributes(5) for descriptions of the following attributes:
+.sp
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE	ATTRIBUTE VALUE
+_
+Architecture	PCI-based systems
+_
+Interface stability	Committed
+.TE
+
+.SH SEE ALSO
+.LP
+\fBattributes\fR(5), \fBusb_alloc_request\fR(9F),
+\fBusb_get_current_frame_number\fR(9F),
+\fBusb_get_max_pkts_per_isoc_request\fR(9F), \fBusb_pipe_bulk_xfer\fR(9F),
+\fBusb_pipe_ctrl_xfer\fR(9F), \fBusb_pipe_intr_xfer\fR(9F),
+\fBusb_pipe_isoc_xfer\fR(9F), \fBusb_bulk_request\fR(9S),
+\fBusb_callback_flags\fR(9S), \fBusb_completion_reason\fR(9S),
+\fBusb_ctrl_request\fR(9S), \fBusb_intr_request\fR(9S),
+\fBusb_request_attributes\fR(9S)
--- a/share/man/man9s/usb_isoc_request.9s	Fri Mar 10 17:02:49 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,259 +0,0 @@
-'\" te
-.\" Copyright (c) 2006, Sun Microsystems, Inc., All Rights Reserved
-.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
-.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
-.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH USB_ISOC_REQUEST 9S "April 9, 2016"
-.SH NAME
-usb_isoc_request \- USB isochronous request structure
-.SH SYNOPSIS
-.LP
-.nf
-#include <sys/usb/usba.h>
-.fi
-
-.SH INTERFACE LEVEL
-.LP
-Solaris DDI specific (Solaris DDI)
-.SH DESCRIPTION
-.LP
-A request sent through an isochronous pipe is used to transfer large amounts of
-packetized data with relative unreliability, but with bounded service periods.
-A packet is guaranteed to be tried within a bounded time period, but is not
-retried upon failure. Isochronous transfers are supported on both USB 1.1 and
-USB 2.0 devices. For further information, see section \fI5.6\fR of the \fIUSB
-2.0\fR specification available at \fIwww.usb.org\fR.
-.sp
-.LP
-This section provides information on acceptable combinations of flags and
-attributes with additional details. The following fields of the usb_isoc_req_t
-are used to format an isochronous request.
-.sp
-.in +2
-.nf
-usb_frame_number_t
-                isoc_frame_no;   /* frame num to start sending req. */
-ushort_t       isoc_pkts_count; /* num USB pkts in this request */
-/*
- * The sum of all pkt lengths in an isoc request. Recommend to set it to
- * zero, so the sum of isoc_pkt_length in the isoc_pkt_descr list will be
- * used automatically and no check will be apply to this element.
- */
-ushort_t       isoc_pkts_length;
-ushort_t       isoc_error_count;/* num pkts completed w/errs */
-usb_req_attrs_t isoc_attributes;/* request-specific attrs */
-mblk_t          *isoc_data;     /* data to xfer */
-                                 /* IN or OUT: alloc. by client. */
-                                 /* Size=total of all pkt lengths. */
-usb_opaque_t     isoc_client_private; /* for client driver excl use. */
-struct usb_isoc_pkt_descr       /* (see below) */
-                 *isoc_pkt_descr;
-
-/*
- * Normal callback function, called upon completion.
- * This function cannot block as it executes in soft interrupt context.
- */
-void       (*isoc_cb)(
-                usb_pipe_handle_t ph, struct usb_isoc_req *req);
-
-/* Exception callback function, for error handling. */
-void       (*isoc_exc_cb)(
-               usb_pipe_handle_t ph, struct usb_isoc_req *req);
-
-usb_cr_t  isoc_completion_reason; /* overall completion status */
-                                   /* set by USBA framework */
-                                   /* See usb_completion_reason(9S) */
-usb_cb_flags_t  isoc_cb_flags;    /* recovery done by callback hndlr */
-                                   /* set by USBA on exception. */
-                                   /* See usb_callback_flags(9S) */
-.fi
-.in -2
-
-.sp
-.LP
-A \fBusb_isoc_pkt_descr_t\fR describes the status of an isochronous packet
-transferred within a frame or microframe. The following fields of a
-\fBusb_isoc_pkt_descr_t\fR packet descriptor are used within an
-\fBusb_isoc_req_t\fR. The \fBisoc_pkt_length\fR is set by the client driver to
-the amount of data managed by the packet for input or output. The latter two
-fields are set by the \fBUSBA\fR framework to indicate status. Any packets with
-an \fBisoc_completion_reason\fR, other than \fBUSB_CR_OK\fR, are reflected in
-the \fBisoc_error_count\fR of the \fBusb_isoc_req_t\fR.
-.sp
-.in +2
-.nf
-     ushort_t    isoc_pkt_length;        /* number bytes to transfer */
-      ushort_t    isoc_pkt_actual_length; /* actual number transferred */
-      usb_cr_t    isoc_pkt_status;        /* completion status */
-.fi
-.in -2
-
-.sp
-.LP
-If two multi-frame \fBisoc\fR requests that both specify the
-\fBUSB_ATTRS_ISOC_XFER_ASAP\fR attribute are scheduled closely together, the
-first frame of the second request is queued to start after the last frame of
-the first request.
-.sp
-.LP
-No stalls are seen in isochronous transfer exception callbacks. Because
-transfers are not retried upon failure, isochronous transfers continue
-regardless of errors.
-.sp
-.LP
-Request attributes define special handling for transfers. The following
-attributes are valid for isochronous requests:
-.sp
-.ne 2
-.na
-\fB\fBUSB_ATTRS_ISOC_START_FRAME\fR\fR
-.ad
-.RS 30n
-Start transferring at the starting frame number specified in the
-\fBisoc_frame_no\fR field of the request.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBUSB_ATTRS_ISOC_XFER_ASAP\fR\fR
-.ad
-.RS 30n
-Start transferring as soon as possible. The \fBUSBA\fR framework picks an
-immediate frame number to map to the starting frame number.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBUSB_ATTRS_SHORT_XFER_OK\fR\fR
-.ad
-.RS 30n
-Accept transfers where less data is received than expected.
-.RE
-
-.sp
-.LP
-The \fBusb_isoc_req_t\fR contains an array of descriptors that describe
-isochronous packets. One isochronous packet is sent per frame or microframe.
-Because packets that comprise a transfer are sent across consecutive frames or
-microframes, \fBUSB_ATTRS_ONE_XFER\fR is invalid.
-.sp
-.LP
-See \fBusb_request_attributes\fR(9S) for more information.
-.sp
-.LP
-Isochronous transfers/requests are subject to the following constraints and
-caveats:
-.sp
-.in +2
-.nf
-1) The following table indicates combinations of usb_pipe_isoc_xfer
-   flags argument and fields of the usb_isoc_req_t request argument
-   (X = don't care). (Note that attributes considered in this table
-   are ONE_XFER, START_FRAME, XFER_ASAP, and SHORT_XFER, and that
-   some transfer types are characterized by multiple table entries.)
-
-Flags Type     Attributes          Data    Semantics
----------------------------------------------------------------
-X      X      X                    NULL    illegal
-
-X      X      ONE_XFER             X       illegal
-
-X      X      ISOC_START_FRAME     X       illegal
-              & ISOC_XFER_ASAP
-
-X      X      !ISOC_START_FRAME    X       illegal
-              & !ISOC_XFER_ASAP
-
-X      OUT    SHORT_XFER_OK        X       illegal
-
-X      IN     X                    !=NULL  See table note (A)
-
-X      X      ISOC_START_FRAME     !=NULL  See table note (B)
-
-X      X      ISOC_XFER_ASAP       !=NULL  See table note (C)
-
-Table notes:
-
-    A) continuous polling, new data is returned in
-       cloned request structures via continuous callbacks,
-       original request is returned on stop polling
-
-    B) invalid if the current_frame number is past
-       "isoc_frame_no" or "isoc_frame_no" == 0
-
-    C)"isoc_frame_no" is ignored. The USBA framework
-       determines which frame to insert and start
-       the transfer.
-
-2) USB_FLAGS_SLEEP indicates to wait for resources but
-   not for completion.
-
-3) For polled reads:
-
-  A. The USBA  framework  accepts  a  request  which
-     specifies  the  size and number of packets to fill
-     with data. The packets get filled one  packet  per
-     (1  ms)  frame/(125 us) microframe.  All  requests
-     have an implicit USB_ATTRS_SHORT_XFER_OK attribute
-     set, since transfers  continue in spite of any en-
-     countered. The amount of data read per packet  will
-     match  the  isoc_pkt_length  field  of  the packet
-     descriptor unless a  short  transfer  occurs.  The
-     actual     size     is     returned     in     the
-     isoc_pkt_actual_length   field   of   the   packet
-     descriptor.  When  all packets of the request have
-     been processed, a normal callback is done to  sig-
-     nal the completion of the original request.
-
-  B. When continuous polling is stopped, the original
-     request is returned in an exception callback with a
-     completion reason of USB_CR_STOPPED_POLLING.
-     (NOTE: Polling can be restarted from  an exception
-     callback corresponding to an original request.
-     Please see usb_pipe_isoc_xfer(9F) for more information.
-
-  C. Callbacks must be specified.
-
-  The isoc_completion_reason indicates the status of the transfer. See
-  usb_completion_reason(9s) for usb_cr_t definitions.
-
-  The isoc_cb_flags are set prior to calling the exception
-  callback handler to summarize recovery actions taken and
-  errors encountered during recovery. See usb_callback_flags(9s)
-  for usb_cb_flags_t definitions.
-
---- Callback handling ---
-All usb request types share the same callback handling. Please see
-usb_callback_flags(9s) for a description of use and operation.
-.fi
-.in -2
-
-.SH ATTRIBUTES
-.LP
-See attributes(5) for descriptions of the following attributes:
-.sp
-
-.sp
-.TS
-box;
-c | c
-l | l .
-ATTRIBUTE TYPE	ATTRIBUTE VALUE
-_
-Architecture	PCI-based systems
-_
-Interface stability	Committed
-.TE
-
-.SH SEE ALSO
-.LP
-\fBattributes\fR(5), \fBusb_alloc_request\fR(9F),
-\fBusb_get_current_frame_number\fR(9F),
-\fBusb_get_max_pkts_per_isoc_request\fR(9F), \fBusb_pipe_bulk_xfer\fR(9F),
-\fBusb_pipe_ctrl_xfer\fR(9F), \fBusb_pipe_intr_xfer\fR(9F),
-\fBusb_pipe_isoc_xfer\fR(9F), \fBusb_bulk_request\fR(9S),
-\fBusb_callback_flags\fR(9S), \fBusb_completion_reason\fR(9S),
-\fBusb_ctrl_request\fR(9S), \fBusb_intr_request\fR(9S),
-\fBusb_request_attributes\fR(9S)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/boot/Makefile.version	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,32 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2016 Toomas Soome <tsoome@me.com>
+#
+
+#
+# The version string is composed according to IPS version string rules:
+#	dotted verstion strings are consisting of numbers and dots,
+#	different strings are separated by comma or dash,
+#	at the end of the dotted version can be timestamp, separated by
+#	the colon.
+#
+# The dotted version part are compared by numbers, timestamps are
+# compared lexicographically, as strings.
+#
+# Here we define two version strings, for loader and boot program.
+# The BOOT_VERSION is used by installboot program to decide if the
+# boot program update is needed, and should be updated according to
+# boot loader code updates.
+
+LOADER_VERSION = 1.1
+BOOT_VERSION = $(LOADER_VERSION)-2017.0.0.1
--- a/usr/src/boot/sys/boot/common/interp_forth.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/boot/sys/boot/common/interp_forth.c	Fri Mar 10 18:57:44 2017 -0500
@@ -32,7 +32,7 @@
 #include "bootstrap.h"
 #include "ficl.h"
 
-extern char bootprog_rev[];
+extern unsigned bootprog_rev;
 
 /* #define BFORTH_DEBUG */
 
@@ -307,8 +307,7 @@
      * version
      */
     env = ficlSystemGetEnvironment(bf_sys);
-    ficlDictionarySetConstant(env, "loader_version",
-	       (bootprog_rev[0] - '0') * 10 + (bootprog_rev[2] - '0'));
+    ficlDictionarySetConstant(env, "loader_version", bootprog_rev);
 
     /* try to load and run init file if present */
     if (rc == NULL)
--- a/usr/src/boot/sys/boot/common/newvers.sh	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/boot/sys/boot/common/newvers.sh	Fri Mar 10 18:57:44 2017 -0500
@@ -1,6 +1,5 @@
 #!/bin/sh -
 #
-# $FreeBSD$
 #	$NetBSD: newvers.sh,v 1.1 1997/07/26 01:50:38 thorpej Exp $
 #
 # Copyright (c) 1984, 1986, 1990, 1993
@@ -36,12 +35,8 @@
 trap "rm -f $tempfile" EXIT INT TERM
 
 LC_ALL=C; export LC_ALL
-u=${USER-root} h=${HOSTNAME-`hostname`} t=`date`
-#r=`head -n 6 $1 | tail -n 1 | awk -F: ' { print $1 } '`
-r=`awk -F: ' /^[0-9]\.[0-9]+:/ { print $1; exit }' $1`
+r="$1"
 
-echo "char bootprog_name[] = \"illumos/${3} ${2}\";" > $tempfile
-echo "char bootprog_rev[] = \"${r}\";" >> $tempfile
-echo "char bootprog_date[] = \"${t}\";" >> $tempfile
-echo "char bootprog_maker[] = \"${u}@${h}\";" >> $tempfile
+echo "char bootprog_info[] = \"illumos/${3} ${2}, Revision ${r}\\\\n\";" > $tempfile
+echo "unsigned bootprog_rev = ${r%%.*}${r##*.};" >> $tempfile
 mv $tempfile vers.c
--- a/usr/src/boot/sys/boot/efi/boot1/Makefile	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/boot/sys/boot/efi/boot1/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -15,6 +15,7 @@
 #
 
 include $(SRC)/Makefile.master
+include $(SRC)/boot/Makefile.version
 
 CC=	$(GCC_ROOT)/bin/gcc
 LD=	$(GNU_ROOT)/bin/gld
@@ -24,10 +25,6 @@
 PROG=		boot1.sym
 MACHINE=$(MACH64)
 
-# need to update this to trigger installboot updates.
-BUILDDATE :sh = TZ=UTC date +%Y%m%dT%H%M%SZ
-BOOT1_VERSION=	1.1:$(BUILDDATE)
-
 # architecture-specific loader code
 SRCS=	boot1.c self_reloc.c start.S ufs_module.c zfs_module.c devopen.c
 OBJS=	boot1.o self_reloc.o start.o ufs_module.o zfs_module.o devopen.o
--- a/usr/src/boot/sys/boot/efi/loader/Makefile	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/boot/sys/boot/efi/loader/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -15,6 +15,7 @@
 #
 
 include $(SRC)/Makefile.master
+include $(SRC)/boot/Makefile.version
 
 CC=		$(GCC_ROOT)/bin/gcc
 LD=		$(GNU_ROOT)/bin/gld
@@ -100,8 +101,8 @@
 
 install: all $(ROOTBOOTFILES)
 
-vers.c:	../../common/newvers.sh ../../efi/loader/version
-	$(SH) ../../common/newvers.sh version ${NEWVERSWHAT}
+vers.c:	../../common/newvers.sh $(SRC)/boot/Makefile.version
+	$(SH) ../../common/newvers.sh ${LOADER_VERSION} ${NEWVERSWHAT}
 
 EFI_TARGET=	pei-x86-64
 
--- a/usr/src/boot/sys/boot/efi/loader/main.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/boot/sys/boot/efi/loader/main.c	Fri Mar 10 18:57:44 2017 -0500
@@ -47,10 +47,7 @@
 
 #include "loader_efi.h"
 
-extern char bootprog_name[];
-extern char bootprog_rev[];
-extern char bootprog_date[];
-extern char bootprog_maker[];
+extern char bootprog_info[];
 
 struct arch_switch archsw;	/* MI/MD interface boundary */
 
@@ -390,9 +387,7 @@
 	printf(" (rev %d.%02d)\n", ST->FirmwareRevision >> 16,
 	    ST->FirmwareRevision & 0xffff);
 
-	printf("\n");
-	printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
-	printf("(%s, %s)\n", bootprog_maker, bootprog_date);
+	printf("\n%s", bootprog_info);
 
 	/*
 	 * Disable the watchdog timer. By default the boot manager sets
--- a/usr/src/boot/sys/boot/efi/loader/version	Fri Mar 10 17:02:49 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-$FreeBSD$
-
-NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE.  The format of this
-file is important.  Make sure the current version number is on line 6.
-
-1.1:	Keep in sync with i386 version.
-0.1:	Initial i386 version. Derived from ia64.
--- a/usr/src/boot/sys/boot/i386/gptzfsboot/Makefile	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/boot/sys/boot/i386/gptzfsboot/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -17,6 +17,7 @@
 #
 
 include $(SRC)/Makefile.master
+include $(SRC)/boot/Makefile.version
 
 AS=	$(GNU_ROOT)/bin/gas
 LD=	$(GNU_ROOT)/bin/gld
@@ -27,10 +28,6 @@
 MAN=		gptzfsboot.8
 FILEMODE=0444
 
-# need to update this to trigger installboot updates.
-BUILDDATE :sh = TZ=UTC date +%Y%m%dT%H%M%SZ
-BOOT2_VERSION=	1.1:$(BUILDDATE)
-
 BOOT_COMCONSOLE_PORT= 0x3f8
 BOOT_COMCONSOLE_SPEED= 9600
 B2SIOFMT=	0x3
@@ -85,7 +82,7 @@
 CLEANFILES=	gptzfsboot $(OBJS)
 
 gptzfsboot: gptldr.bin gptzfsboot.bin ${BTXKERN}
-	$(BTXLD) -E ${ORG2} -f bin -b ${BTXKERN} -V ${BOOT2_VERSION} -l \
+	$(BTXLD) -E ${ORG2} -f bin -b ${BTXKERN} -V ${BOOT_VERSION} -l \
 		gptldr.bin -o $@ gptzfsboot.bin
 
 CLEANFILES +=	gptldr.bin gptldr.out gptldr.o
--- a/usr/src/boot/sys/boot/i386/loader/Makefile	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/boot/sys/boot/i386/loader/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -15,6 +15,7 @@
 #
 
 include $(SRC)/Makefile.master
+include $(SRC)/boot/Makefile.version
 
 CFLAGS= -O2
 CPPFLAGS= -DSTAND -nostdinc -I../../../../include -I../../..
@@ -106,8 +107,8 @@
 
 include ../Makefile.inc
 
-vers.c:	../../common/newvers.sh version
-	$(SH) ../../common/newvers.sh version ${NEWVERSWHAT}
+vers.c:	../../common/newvers.sh $(SRC)/boot/Makefile.version
+	$(SH) ../../common/newvers.sh ${LOADER_VERSION} ${NEWVERSWHAT}
 
 ${LOADER}: ${LOADER}.bin ${BTXLDR} ${BTXKERN}
 	$(BTXLD) -f aout -e ${LOADER_ADDRESS} -o $@ -l ${BTXLDR} \
--- a/usr/src/boot/sys/boot/i386/loader/main.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/boot/sys/boot/i386/loader/main.c	Fri Mar 10 18:57:44 2017 -0500
@@ -25,7 +25,6 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
 
 /*
  * MD bootstrap main() and assorted miscellaneous
@@ -73,7 +72,7 @@
 #endif
 
 /* from vers.c */
-extern	char bootprog_name[], bootprog_rev[], bootprog_date[], bootprog_maker[];
+extern	char bootprog_info[];
 
 /* XXX debugging */
 extern char end[];
@@ -96,7 +95,7 @@
     bzero(&v86, sizeof(v86));
     v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
 
-    /* 
+    /*
      * Initialise the heap as early as possible.  Once this is done, malloc() is usable.
      */
     bios_getmem();
@@ -187,9 +186,7 @@
     /* detect PCI BIOS for future reference */
     biospci_detect();
 
-    printf("\n");
-    printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
-    printf("(%s, %s)\n", bootprog_maker, bootprog_date);
+    printf("\n%s", bootprog_info);
 
     extract_currdev();				/* set $currdev and $loaddev */
     setenv("LINES", "24", 1);			/* optional */
@@ -209,7 +206,7 @@
 }
 
 /*
- * Set the 'current device' by (if possible) recovering the boot device as 
+ * Set the 'current device' by (if possible) recovering the boot device as
  * supplied by the initial bootstrap.
  *
  * XXX should be extended for netbooting.
@@ -281,7 +278,7 @@
 	/*
 	 * If we are booted by an old bootstrap, we have to guess at the BIOS
 	 * unit number.  We will lose if there is more than one disk type
-	 * and we are not booting from the lowest-numbered disk type 
+	 * and we are not booting from the lowest-numbered disk type
 	 * (ie. SCSI when IDE also exists).
 	 */
 	if ((biosdev == 0) && (B_TYPE(initial_bootdev) != 2))	/* biosdev doesn't match major */
--- a/usr/src/boot/sys/boot/i386/loader/version	Fri Mar 10 17:02:49 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-$FreeBSD$
-
-NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE.  The format of this
-file is important.  Make sure the current version number is on line 6.
-
-1.1:	New calling conventions for fopen.
-1.0:	New semantics for finding the kernel, new boot.
-0.8:	Set/getenv & cia, copyin/out.
-0.7:	Supports large KVM
-0.6:	Increased dictionary size -- supports loader.4th
-0.5:	First release version
-0.2:	Initial integration with BTX
-0.1:	Initial i386 version, inspiration and some structure from the
-	NetBSD version.
--- a/usr/src/boot/sys/boot/ofw/common/main.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/boot/sys/boot/ofw/common/main.c	Fri Mar 10 18:57:44 2017 -0500
@@ -26,7 +26,6 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
 
 #include <stand.h>
 #include "openfirm.h"
@@ -36,10 +35,7 @@
 struct arch_switch	archsw;		/* MI/MD interface boundary */
 
 extern char end[];
-extern char bootprog_name[];
-extern char bootprog_rev[];
-extern char bootprog_date[];
-extern char bootprog_maker[];
+extern char bootprog_info[];
 
 u_int32_t	acells, scells;
 
@@ -127,9 +123,7 @@
 		if (devsw[i]->dv_init != NULL)
 			(devsw[i]->dv_init)();
 
-	printf("\n");
-	printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
-	printf("(%s, %s)\n", bootprog_maker, bootprog_date);
+	printf("\n%s", bootprog_info);
 	printf("Memory: %lldKB\n", memsize() / 1024);
 
 	OF_getprop(chosen, "bootpath", bootpath, 64);
--- a/usr/src/boot/sys/boot/sparc64/loader/main.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/boot/sys/boot/sparc64/loader/main.c	Fri Mar 10 18:57:44 2017 -0500
@@ -33,7 +33,6 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
 
 /*
  * FreeBSD/sparc64 kernel loader - machine dependent part
@@ -75,7 +74,7 @@
 #include "libofw.h"
 #include "dev_net.h"
 
-extern char bootprog_name[], bootprog_rev[], bootprog_date[], bootprog_maker[];
+extern char bootprog_info[];
 
 enum {
 	HEAPVA		= 0x800000,
@@ -891,9 +890,7 @@
 	env_setenv("loaddev", EV_VOLATILE, bootpath,
 	    env_noset, env_nounset);
 
-	printf("\n");
-	printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
-	printf("(%s, %s)\n", bootprog_maker, bootprog_date);
+	printf("\n%s", bootprog_info);
 	printf("bootpath=\"%s\"\n", bootpath);
 
 	/* Give control to the machine independent loader code. */
--- a/usr/src/boot/sys/boot/uboot/common/main.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/boot/sys/boot/uboot/common/main.c	Fri Mar 10 18:57:44 2017 -0500
@@ -27,7 +27,6 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
 #include <sys/param.h>
 
 #include <stand.h>
@@ -62,10 +61,7 @@
 };
 
 extern char end[];
-extern char bootprog_name[];
-extern char bootprog_rev[];
-extern char bootprog_date[];
-extern char bootprog_maker[];
+extern char bootprog_info[];
 
 extern unsigned char _etext[];
 extern unsigned char _edata[];
@@ -428,9 +424,7 @@
 	cons_probe();
 	printf("Compatible U-Boot API signature found @%p\n", sig);
 
-	printf("\n");
-	printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
-	printf("(%s, %s)\n", bootprog_maker, bootprog_date);
+	printf("\n%s", bootprog_info);
 	printf("\n");
 
 	dump_sig(sig);
--- a/usr/src/boot/sys/boot/userboot/userboot/main.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/boot/sys/boot/userboot/userboot/main.c	Fri Mar 10 18:57:44 2017 -0500
@@ -51,10 +51,7 @@
 struct loader_callbacks *callbacks;
 void *callbacks_arg;
 
-extern char bootprog_name[];
-extern char bootprog_rev[];
-extern char bootprog_date[];
-extern char bootprog_maker[];
+extern char bootprog_info[];
 static jmp_buf jb;
 
 struct arch_switch archsw;	/* MI/MD interface boundary */
@@ -101,9 +98,7 @@
 	 */
 	cons_probe();
 
-	printf("\n");
-	printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
-	printf("(%s, %s)\n", bootprog_maker, bootprog_date);
+	printf("\n%s", bootprog_info);
 #if 0
 	printf("Memory: %ld k\n", memsize() / 1024);
 #endif
--- a/usr/src/cmd/Makefile	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/cmd/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -422,6 +422,7 @@
 	wracct		\
 	write		\
 	xargs		\
+	xhci		\
 	xstr		\
 	ypcmd		\
 	yppasswd	\
--- a/usr/src/cmd/mdb/common/kmdb/mapfile_skel	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/cmd/mdb/common/kmdb/mapfile_skel	Fri Mar 10 18:57:44 2017 -0500
@@ -58,6 +58,7 @@
 		islower;
 		ispunct;
 		isspace;
+		strlcpy;
 
 		mdb_tgt_aread;
 		mdb_dis_create;
--- a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern	Fri Mar 10 18:57:44 2017 -0500
@@ -102,6 +102,7 @@
 		mdb_get_lbolt			{ FLAGS = EXTERN };
 		mdb_get_pipe			{ FLAGS = EXTERN };
 		mdb_get_soft_state_byaddr	{ FLAGS = EXTERN };
+		mdb_get_soft_state_byname	{ FLAGS = EXTERN };
 		mdb_get_state			{ FLAGS = EXTERN };
 		mdb_get_xdata			{ FLAGS = EXTERN };
 		mdb_gethrtime			{ FLAGS = EXTERN };
--- a/usr/src/cmd/mdb/common/modules/usba/prtusb.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/cmd/mdb/common/modules/usba/prtusb.c	Fri Mar 10 18:57:44 2017 -0500
@@ -21,6 +21,8 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2016 Joyent, Inc.
  */
 
 
@@ -117,6 +119,15 @@
 };
 static uint_t usb_ep_item = 6;
 
+static usb_descr_item_t usb_ep_ss_comp_descr[] = {
+	{1, "bLength"},
+	{1, "bDescriptorType"},
+	{1, "bMaxBurst"},
+	{1, "bmAttributes"},
+	{2, "wBytesPerInterval"}
+};
+static uint_t usb_ep_ss_comp_item = 5;
+
 static usb_descr_item_t usb_qlf_descr[] = {
 	{1, "bLength"},
 	{1, "bDescriptorType"},
@@ -561,8 +572,9 @@
 	/* for the first device, print head */
 	if (DCMD_HDRSPEC(flags)) {
 		count = 1;
-		mdb_printf("%<u>%-8s%-12s%-6s%-16s%-12s%-20s%</u>\n",
-		    "INDEX", "DRIVER", "INST", "NODE", "VID.PID", "PRODUCT");
+		mdb_printf("%<u>%-8s%-12s%-6s%-14s%-5s%-12s%-20s%</u>\n",
+		    "INDEX", "DRIVER", "INST", "NODE", "GEN", "VID.PID",
+		    "PRODUCT");
 	}
 
 	if (mdb_getopts(argc, argv,
@@ -604,16 +616,21 @@
 	if (mdb_readstr(strbuf, STRLEN,
 	    (uintptr_t)usb_dip.devi_node_name) != -1) {
 
-		mdb_printf("%-16s", strbuf);
+		mdb_printf("%-14s", strbuf);
 	} else {
 
-		mdb_printf("%-16s", "No Node Name");
+		mdb_printf("%-14s", "No Node Name");
 	}
 
-	/* vid.pid */
+
 	if (mdb_vread(&dev_desc, sizeof (usb_dev_descr_t),
 	    (uintptr_t)usb_dev.usb_dev_descr) != -1) {
 
+		/* gen (note we read this from the bcd) */
+		mdb_printf("%01x.%01x  ", dev_desc.bcdUSB >> 8,
+		    (dev_desc.bcdUSB & 0xf0) >> 4);
+
+		/* vid.pid */
 		mdb_printf("%04x.%04x   ",
 		    dev_desc.idVendor, dev_desc.idProduct);
 	}
@@ -824,7 +841,7 @@
 	if (strcmp(driver_name, "hubd") == 0) {
 		mdb_arg_t argv[] = {
 		    {MDB_TYPE_STRING, {"hubd_t"}},
-		    {MDB_TYPE_STRING, {"h_hub_descr"}}
+		    {MDB_TYPE_STRING, {"h_ep1_xdescr.uex_ep"}}
 		};
 		mdb_call_dcmd("print", statep, DCMD_ADDRSPEC, 2, argv);
 	}
@@ -1075,6 +1092,16 @@
 			mdb_dec_indent(indent);
 
 			break;
+		case USB_DESCR_TYPE_SS_EP_COMP:
+			indent = 12;
+			mdb_inc_indent(indent);
+			mdb_printf("SuperSpeed Endpoint Companion "
+			    "Descriptor\n");
+			print_descr(paddr, nlen, usb_ep_ss_comp_descr,
+			    usb_ep_ss_comp_item);
+			mdb_dec_indent(indent);
+
+			break;
 		case USB_DESCR_TYPE_DEV_QLF:
 			mdb_printf("Device_Qualifier Descriptor\n");
 			print_descr(paddr, nlen, usb_qlf_descr, usb_qlf_item);
--- a/usr/src/cmd/mdb/common/modules/usba/usb.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/cmd/mdb/common/modules/usba/usb.c	Fri Mar 10 18:57:44 2017 -0500
@@ -21,9 +21,10 @@
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2016 Joyent, Inc.
  */
 
-
 #include <stddef.h>
 #include <sys/mdb_modapi.h>
 #include <mdb/mdb_ks.h>
@@ -33,6 +34,8 @@
 #include <sys/usb/usba/usba_types.h>
 #include <sys/usb/usba/usba_impl.h>
 #include <sys/usb/usba/hcdi_impl.h>
+#include <sys/usb/hubd/hub.h>
+#include <sys/usb/hubd/hubdvar.h>
 #include <sys/file.h>
 #include <sys/sunndi.h>
 #include <unistd.h>
@@ -422,6 +425,72 @@
 	return (WALK_NEXT);
 }
 
+int
+usba_hubd_walk_init(mdb_walk_state_t *wsp)
+{
+	if (wsp->walk_addr != 0) {
+		mdb_warn("hubd only supports global walks.\n");
+		return (WALK_ERR);
+	}
+
+	if (mdb_layered_walk("usba_device", wsp) == -1) {
+		mdb_warn("couldn't walk 'usba_device'");
+		return (WALK_ERR);
+	}
+
+	return (WALK_NEXT);
+}
+
+/*
+ * Getting the hub state is annoying. The root hubs are stored on dev_info_t
+ * while the normal hubs are stored as soft state.
+ */
+int
+usba_hubd_walk_step(mdb_walk_state_t *wsp)
+{
+	usba_device_t ud;
+	hubd_t hubd;
+	struct dev_info dev_info;
+	uintptr_t state_addr;
+
+	if (mdb_vread(&ud, sizeof (ud), wsp->walk_addr) != sizeof (ud)) {
+		mdb_warn("failed to read usba_device_t at %p", wsp->walk_addr);
+		return (WALK_ERR);
+	}
+
+	if (ud.usb_root_hubd != NULL) {
+		if (mdb_vread(&hubd, sizeof (hubd),
+		    (uintptr_t)ud.usb_root_hubd) != sizeof (hubd)) {
+			mdb_warn("failed to read hubd at %p", ud.usb_root_hubd);
+			return (WALK_ERR);
+		}
+		return (wsp->walk_callback((uintptr_t)ud.usb_root_hubd, &hubd,
+		    wsp->walk_cbdata));
+	}
+
+	if (ud.usb_hubdi == NULL)
+		return (WALK_NEXT);
+
+	/*
+	 * For non-root hubs, the hubd_t is stored in the soft state. Figure out
+	 * the instance from the dev_info_t and then get its soft state.
+	 */
+	if (mdb_vread(&dev_info, sizeof (struct dev_info),
+	    (uintptr_t)ud.usb_dip) != sizeof (struct dev_info)) {
+		mdb_warn("failed to read dev_info_t for device %p at %p",
+		    wsp->walk_addr, ud.usb_dip);
+		return (WALK_ERR);
+	}
+
+	if (mdb_get_soft_state_byname("hubd_statep", dev_info.devi_instance,
+	    &state_addr, &hubd, sizeof (hubd)) == -1) {
+		mdb_warn("failed to read hubd soft state for instance %d from "
+		    "usb device %p", dev_info.devi_instance, wsp->walk_addr);
+		return (WALK_ERR);
+	}
+
+	return (wsp->walk_callback(state_addr, &hubd, wsp->walk_cbdata));
+}
 
 /*
  * usba_device dcmd
@@ -790,6 +859,8 @@
 	    usb_pipe_handle_walk_init, usb_pipe_handle_walk_step, NULL, NULL },
 	{ "usba_device", "walk global list of usba_device_t structures",
 	    usba_device_walk_init, usba_list_walk_step, NULL, NULL },
+	{ "hubd", "walk hubd instances", usba_hubd_walk_init,
+	    usba_hubd_walk_step, NULL, NULL },
 	{ NULL }
 };
 
--- a/usr/src/cmd/mdb/intel/modules/Makefile	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/cmd/mdb/intel/modules/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -26,6 +26,7 @@
 	i40e \
 	generic_cpu \
 	amd_opteron \
-	sata
+	sata \
+	xhci
 
 include ../../Makefile.subdirs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/intel/modules/xhci/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,19 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2016 Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+SUBDIRS =		ia32
+$(BUILD64)SUBDIRS += 	$(MACH64)
+include ../../../Makefile.subdirs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/intel/modules/xhci/amd64/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,27 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2016 Joyent, Inc.
+#
+
+
+MODULE = xhci.so
+MDBTGT = kvm
+
+MODSRCS = xhci.c
+
+include ../../../../../Makefile.cmd
+include ../../../../../Makefile.cmd.64
+include ../../../Makefile.amd64
+include ../../../../Makefile.module
+
+CPPFLAGS += -I$(SRC)/uts/common
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/intel/modules/xhci/ia32/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,25 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2016 Joyent, Inc.
+#
+
+MODULE = xhci.so
+MDBTGT = kvm
+
+MODSRCS = xhci.c
+
+include ../../../../../Makefile.cmd
+include ../../../Makefile.ia32
+include ../../../../Makefile.module
+
+CPPFLAGS += -I$(SRC)/uts/common
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/intel/modules/xhci/xhci.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,893 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/mdb_modapi.h>
+#include <sys/usb/hcd/xhci/xhci.h>
+
+#define	XHCI_MDB_TRB_INDENT	4
+
+static const char *xhci_mdb_epctx_eptypes[] = {
+	"Not Valid",
+	"ISOCH OUT",
+	"BULK OUT",
+	"INTR OUT",
+	"CONTROL",
+	"ISOCH IN",
+	"BULK IN",
+	"INTR IN"
+};
+
+static const char *xhci_mdb_epctx_states[] = {
+	"Disabled",
+	"Running",
+	"Halted",
+	"Stopped",
+	"Error",
+	"<Unknown>",
+	"<Unknown>",
+	"<Unknown>"
+};
+
+static const mdb_bitmask_t xhci_mdb_trb_flags[] = {
+	{ "C", XHCI_TRB_CYCLE, XHCI_TRB_CYCLE },
+	{ "ENT", XHCI_TRB_ENT, XHCI_TRB_ENT },
+	{ "ISP", XHCI_TRB_ISP, XHCI_TRB_ISP },
+	{ "NS", XHCI_TRB_NOSNOOP, XHCI_TRB_NOSNOOP },
+	{ "CH", XHCI_TRB_CHAIN, XHCI_TRB_CHAIN },
+	{ "IOC", XHCI_TRB_IOC, XHCI_TRB_IOC },
+	{ "IDT", XHCI_TRB_IDT, XHCI_TRB_IDT },
+	{ "BEI", XHCI_TRB_BEI, XHCI_TRB_BEI },
+	{ NULL, 0, 0 }
+};
+
+typedef struct xhci_mdb_walk_endpoint {
+	xhci_device_t	xmwe_device;
+	uint_t		xmwe_ep;
+} xhci_mdb_walk_endpoint_t;
+
+static const char *
+xhci_mdb_trb_code_to_str(int code)
+{
+	switch (code) {
+	case XHCI_CODE_INVALID:
+		return ("Invalid");
+	case XHCI_CODE_SUCCESS:
+		return ("Success");
+	case XHCI_CODE_DATA_BUF:
+		return ("Data Overrun or Underrun");
+	case XHCI_CODE_BABBLE:
+		return ("Babble");
+	case XHCI_CODE_TXERR:
+		return ("Transaction Error");
+	case XHCI_CODE_TRB:
+		return ("Invalid TRB");
+	case XHCI_CODE_STALL:
+		return ("Stall");
+	case XHCI_CODE_RESOURCE:
+		return ("No Resources Available");
+	case XHCI_CODE_BANDWIDTH:
+		return ("No Bandwidth Available");
+	case XHCI_CODE_NO_SLOTS:
+		return ("No Slots Available");
+	case XHCI_CODE_STREAM_TYPE:
+		return ("Stream Context Type Detected");
+	case XHCI_CODE_SLOT_NOT_ON:
+		return ("Slot disabled");
+	case XHCI_CODE_ENDP_NOT_ON:
+		return ("Endpoint disabled");
+	case XHCI_CODE_SHORT_XFER:
+		return ("Short Transfer");
+	case XHCI_CODE_RING_UNDERRUN:
+		return ("Isoch. Ring Underrun");
+	case XHCI_CODE_RING_OVERRUN:
+		return ("Isoch. Ring Overrun");
+	case XHCI_CODE_VF_RING_FULL:
+		return ("VF Ring Full");
+	case XHCI_CODE_PARAMETER:
+		return ("Invalid Context Parameter");
+	case XHCI_CODE_BW_OVERRUN:
+		return ("Bandwidth Overrun");
+	case XHCI_CODE_CONTEXT_STATE:
+		return ("Illegal Context Transition");
+	case XHCI_CODE_NO_PING_RESP:
+		return ("Failed to Complete Periodic Transfer");
+	case XHCI_CODE_EV_RING_FULL:
+		return ("Event Ring Full");
+	case XHCI_CODE_INCOMPAT_DEV:
+		return ("Incompatible Device");
+	case XHCI_CODE_MISSED_SRV:
+		return ("Missed Isoch. Service Window");
+	case XHCI_CODE_CMD_RING_STOP:
+		return ("Command Ring Stop");
+	case XHCI_CODE_CMD_ABORTED:
+		return ("Command Aborted");
+	case XHCI_CODE_XFER_STOPPED:
+		return ("Transfer Stopped");
+	case XHCI_CODE_XFER_STOPINV:
+		return ("Invalid Transfer Length");
+	case XHCI_CODE_XFER_STOPSHORT:
+		return ("Stopped before End of Transfer Descriptor");
+	case XHCI_CODE_MELAT:
+		return ("Max Exit Latency too large");
+	case XHCI_CODE_RESERVED:
+		return ("Reserved");
+	case XHCI_CODE_ISOC_OVERRUN:
+		return ("Isochronus Overrun");
+	case XHCI_CODE_EVENT_LOST:
+		return ("Event Lost");
+	case XHCI_CODE_UNDEFINED:
+		return ("Undefined Fatal Error");
+	case XHCI_CODE_INVALID_SID:
+		return ("Invalid Stream ID");
+	case XHCI_CODE_SEC_BW:
+		return ("Secondary Bandwith Allocation Failure");
+	case XHCI_CODE_SPLITERR:
+		return ("USB2 Split Transaction Error");
+	default:
+		break;
+	}
+
+	if (code >= 192 && code <= 223)
+		return ("Vendor Defined Error");
+	if (code >= 224 && code <= 255)
+		return ("Vendor Defined Info");
+
+	return ("Reserved");
+}
+
+static const char *
+xhci_mdb_trb_type_to_str(int code)
+{
+	/*
+	 * The macros for the types are all already shifted over based on their
+	 * place in the TRB, so shift there again ourselves.
+	 */
+	switch (code << 10) {
+	case XHCI_TRB_TYPE_NORMAL:
+		return ("Normal");
+	case XHCI_TRB_TYPE_SETUP:
+		return ("Setup");
+	case XHCI_TRB_TYPE_DATA:
+		return ("Data");
+	case XHCI_TRB_TYPE_STATUS:
+		return ("Status");
+	case XHCI_TRB_TYPE_LINK:
+		return ("Link");
+	case XHCI_TRB_TYPE_EVENT:
+		return ("Event");
+	case XHCI_TRB_TYPE_NOOP:
+		return ("No-Op");
+	case XHCI_CMD_ENABLE_SLOT:
+		return ("Enable Slot");
+	case XHCI_CMD_DISABLE_SLOT:
+		return ("Disable Slot");
+	case XHCI_CMD_ADDRESS_DEVICE:
+		return ("Address Device");
+	case XHCI_CMD_CONFIG_EP:
+		return ("Configure Endpoint");
+	case XHCI_CMD_EVAL_CTX:
+		return ("Evaluate Context");
+	case XHCI_CMD_RESET_EP:
+		return ("Reset Endpoint");
+	case XHCI_CMD_STOP_EP:
+		return ("Stop Endpoint");
+	case XHCI_CMD_SET_TR_DEQ:
+		return ("Set Transfer Ring Dequeue Pointer");
+	case XHCI_CMD_RESET_DEV:
+		return ("Reset Device");
+	case XHCI_CMD_FEVENT:
+		return ("Force Event");
+	case XHCI_CMD_NEG_BW:
+		return ("Negotiate Bandwidth");
+	case XHCI_CMD_SET_LT:
+		return ("Set Latency Tolerance");
+	case XHCI_CMD_GET_BW:
+		return ("Get Bandwidth");
+	case XHCI_CMD_FHEADER:
+		return ("Force Header");
+	case XHCI_CMD_NOOP:
+		return ("No-Op Command");
+	case XHCI_EVT_XFER:
+		return ("Transfer Event");
+	case XHCI_EVT_CMD_COMPLETE:
+		return ("Command Completion Event");
+	case XHCI_EVT_PORT_CHANGE:
+		return ("Port Status Change Event");
+	case XHCI_EVT_BW_REQUEST:
+		return ("Bandwidth Request Event");
+	case XHCI_EVT_DOORBELL:
+		return ("Doorbell Event");
+	case XHCI_EVT_HOST_CTRL:
+		return ("Host Controller Event");
+	case XHCI_EVT_DEVICE_NOTIFY:
+		return ("Device Notification Event");
+	case XHCI_EVT_MFINDEX_WRAP:
+		return ("MFINDEX Wrap Event");
+	default:
+		break;
+	}
+
+	if (code >= 43 && code <= 63)
+		return ("Vendor Defiend");
+	return ("Reserved");
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_print_epctx(uintptr_t addr, uint_t flags, int argc,
+    const mdb_arg_t *argv)
+{
+	uint32_t info, info2, txinfo;
+	xhci_endpoint_context_t epctx;
+
+	if (!(flags & DCMD_ADDRSPEC)) {
+		mdb_warn("::xhci_epctx requires an address\n");
+		return (DCMD_USAGE);
+	}
+
+	if (mdb_vread(&epctx, sizeof (epctx), addr) != sizeof (epctx)) {
+		mdb_warn("failed to read xhci_endpoint_context_t at %p", addr);
+		return (DCMD_ERR);
+	}
+
+	info = LE_32(epctx.xec_info);
+	info2 = LE_32(epctx.xec_info2);
+	txinfo = LE_32(epctx.xec_txinfo);
+
+	mdb_printf("Endpoint State: %s (%d)\n",
+	    xhci_mdb_epctx_states[XHCI_EPCTX_STATE(info)],
+	    XHCI_EPCTX_STATE(info));
+
+	mdb_printf("Mult: %d\n", XHCI_EPCTX_GET_MULT(info));
+	mdb_printf("Max Streams: %d\n", XHCI_EPCTX_GET_MAXP_STREAMS(info));
+	mdb_printf("LSA: %d\n", XHCI_EPCTX_GET_LSA(info));
+	mdb_printf("Interval: %d\n", XHCI_EPCTX_GET_IVAL(info));
+	mdb_printf("Max ESIT Hi: %d\n", XHCI_EPCTX_GET_MAX_ESIT_HI(info));
+
+	mdb_printf("CErr: %d\n", XHCI_EPCTX_GET_CERR(info2));
+	mdb_printf("EP Type: %s (%d)\n",
+	    xhci_mdb_epctx_eptypes[XHCI_EPCTX_GET_EPTYPE(info2)],
+	    XHCI_EPCTX_GET_EPTYPE(info2));
+	mdb_printf("Host Initiate Disable: %d\n", XHCI_EPCTX_GET_HID(info2));
+	mdb_printf("Max Burst: %d\n", XHCI_EPCTX_GET_MAXB(info2));
+	mdb_printf("Max Packet Size: %d\n", XHCI_EPCTX_GET_MPS(info2));
+
+	mdb_printf("Ring DCS: %d\n", LE_64(epctx.xec_dequeue) & 0x1);
+	mdb_printf("Ring PA: 0x%lx\n", LE_64(epctx.xec_dequeue) & ~0xf);
+
+	mdb_printf("Average TRB Length: %d\n", XHCI_EPCTX_AVG_TRB_LEN(txinfo));
+	mdb_printf("Max ESIT: %d\n", XHCI_EPCTX_GET_MAX_ESIT_PAYLOAD(txinfo));
+
+	return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_print_slotctx(uintptr_t addr, uint_t flags, int argc,
+    const mdb_arg_t *argv)
+{
+	uint32_t info, info2, tt, state;
+	xhci_slot_context_t sctx;
+
+	if (!(flags & DCMD_ADDRSPEC)) {
+		mdb_warn("::xhci_slotctx requires an address\n");
+		return (DCMD_USAGE);
+	}
+
+	if (mdb_vread(&sctx, sizeof (sctx), addr) != sizeof (sctx)) {
+		mdb_warn("failed to read xhci_slot_context_t at %p", addr);
+		return (DCMD_ERR);
+	}
+
+	info = LE_32(sctx.xsc_info);
+	info2 = LE_32(sctx.xsc_info2);
+	tt = LE_32(sctx.xsc_tt);
+	state = LE_32(sctx.xsc_state);
+
+	mdb_printf("Route: 0x%x\n", XHCI_SCTX_GET_ROUTE(info));
+
+	mdb_printf("Slot Speed: ");
+	switch (XHCI_SCTX_GET_SPEED(info)) {
+	case XHCI_SPEED_FULL:
+		mdb_printf("Full");
+		break;
+	case XHCI_SPEED_LOW:
+		mdb_printf("Low");
+		break;
+	case XHCI_SPEED_HIGH:
+		mdb_printf("High");
+		break;
+	case XHCI_SPEED_SUPER:
+		mdb_printf("Super");
+		break;
+	default:
+		mdb_printf("Unknown");
+		break;
+	}
+	mdb_printf(" (%d)\n", XHCI_SCTX_GET_SPEED(info));
+
+
+	mdb_printf("MTT: %d\n", XHCI_SCTX_GET_MTT(info));
+	mdb_printf("HUB: %d\n", XHCI_SCTX_GET_HUB(info));
+	mdb_printf("DCI: %d\n", XHCI_SCTX_GET_DCI(info));
+
+	mdb_printf("Max Exit Latency: %d\n", XHCI_SCTX_GET_MAX_EL(info2));
+	mdb_printf("Root Hub Port: %d\n", XHCI_SCTX_GET_RHPORT(info2));
+	mdb_printf("Hub Number of Ports: %d\n", XHCI_SCTX_GET_NPORTS(info2));
+
+	mdb_printf("TT Hub Slot id: %d\n", XHCI_SCTX_GET_TT_HUB_SID(tt));
+	mdb_printf("TT Port Number: %d\n", XHCI_SCTX_GET_TT_PORT_NUM(tt));
+	mdb_printf("TT Think Time: %d\n", XHCI_SCTX_GET_TT_THINK_TIME(tt));
+	mdb_printf("IRQ Target: %d\n", XHCI_SCTX_GET_IRQ_TARGET(tt));
+
+	mdb_printf("Device Address: 0x%x\n", XHCI_SCTX_GET_DEV_ADDR(state));
+	mdb_printf("Slot State: ");
+	switch (XHCI_SCTX_GET_SLOT_STATE(state)) {
+	case XHCI_SLOT_DIS_ENAB:
+		mdb_printf("Disabled/Enabled");
+		break;
+	case XHCI_SLOT_DEFAULT:
+		mdb_printf("Default");
+		break;
+	case XHCI_SLOT_ADDRESSED:
+		mdb_printf("Addressed");
+		break;
+	case XHCI_SLOT_CONFIGURED:
+		mdb_printf("Configured");
+		break;
+	default:
+		mdb_printf("Unknown");
+		break;
+	}
+	mdb_printf(" (%d)\n", XHCI_SCTX_GET_SLOT_STATE(state));
+
+	return (DCMD_OK);
+}
+
+static int
+xhci_mdb_print_transfer_event(uint64_t pa, uint32_t status, uint32_t flags)
+{
+	mdb_printf("TRB Address: 0x%lx\n", pa);
+	mdb_printf("Transfer Length (Remain): %d\n", XHCI_TRB_REMAIN(status));
+	mdb_printf("Completion Code: %s (%d)\n",
+	    xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)),
+	    XHCI_TRB_GET_CODE(status));
+
+	mdb_printf("Cycle: %d\n", XHCI_TRB_GET_CYCLE(flags));
+	mdb_printf("Event Data: %d\n", XHCI_TRB_GET_ED(flags));
+	mdb_printf("Endpoint ID: %d\n", XHCI_TRB_GET_EP(flags));
+	mdb_printf("Slot ID: %d\n", XHCI_TRB_GET_SLOT(flags));
+	mdb_dec_indent(XHCI_MDB_TRB_INDENT);
+
+	return (DCMD_OK);
+}
+
+static int
+xhci_mdb_print_command_event(uint64_t pa, uint32_t status, uint32_t flags)
+{
+	mdb_printf("TRB Address: 0x%lx\n", pa);
+	mdb_printf("Command Param: 0x%x\n", XHCI_TRB_REMAIN(status));
+	mdb_printf("Completion Code: %s (%d)\n",
+	    xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)),
+	    XHCI_TRB_GET_CODE(status));
+
+	mdb_printf("Cycle: %d\n", XHCI_TRB_GET_CYCLE(flags));
+	/* Skip VF ID as we don't support VFs */
+	mdb_printf("Slot ID: %d\n", XHCI_TRB_GET_SLOT(flags));
+	mdb_dec_indent(XHCI_MDB_TRB_INDENT);
+
+	return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_print_psc(uint64_t pa, uint32_t status, uint32_t flags)
+{
+	mdb_printf("Port: %d\n", XHCI_TRB_PORTID(pa));
+	mdb_printf("Completion Code: %s (%d)\n",
+	    xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)),
+	    XHCI_TRB_GET_CODE(status));
+	mdb_dec_indent(XHCI_MDB_TRB_INDENT);
+	return (DCMD_OK);
+}
+
+static int
+xhci_mdb_print_normal_trb(uint64_t pa, uint32_t status, uint32_t flags)
+{
+	mdb_printf("TRB Address: 0x%lx\n", pa);
+	mdb_printf("TRB Length: %d bytes\n", XHCI_TRB_LEN(status));
+	mdb_printf("TRB TD Size: %d packets\n", XHCI_TRB_GET_TDREM(status));
+	mdb_printf("TRB Interrupt: %d\n", XHCI_TRB_GET_INTR(status));
+	mdb_printf("TRB Flags: %b (0x%x)\n", flags, xhci_mdb_trb_flags,
+	    XHCI_TRB_GET_FLAGS(flags));
+	mdb_dec_indent(XHCI_MDB_TRB_INDENT);
+
+	return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_print_trb(uintptr_t addr, uint_t flags, int argc,
+    const mdb_arg_t *argv)
+{
+	xhci_trb_t trb;
+	uint64_t pa;
+	uint32_t status, trbflags, type;
+
+	if (!(flags & DCMD_ADDRSPEC)) {
+		mdb_warn("::xhci_trb expects an address\n");
+		return (DCMD_USAGE);
+	}
+
+	if (mdb_vread(&trb, sizeof (trb), addr) != sizeof (trb)) {
+		mdb_warn("failed to read xhci_trb_t at 0x%x", addr);
+		return (DCMD_ERR);
+	}
+
+	pa = LE_64(trb.trb_addr);
+	status = LE_32(trb.trb_status);
+	trbflags = LE_32(trb.trb_flags);
+
+	type = XHCI_TRB_GET_TYPE(trbflags);
+
+	if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST))
+		mdb_printf("\n");
+
+	mdb_set_dot(addr + sizeof (xhci_trb_t));
+	mdb_printf("%s TRB (%d)\n", xhci_mdb_trb_type_to_str(type), type);
+	mdb_inc_indent(XHCI_MDB_TRB_INDENT);
+
+	switch (XHCI_RING_TYPE_SHIFT(type)) {
+	case XHCI_EVT_XFER:
+		return (xhci_mdb_print_transfer_event(pa, status, trbflags));
+	case XHCI_EVT_CMD_COMPLETE:
+		return (xhci_mdb_print_command_event(pa, status, trbflags));
+	case XHCI_EVT_PORT_CHANGE:
+		return (xhci_mdb_print_psc(pa, status, trbflags));
+	case XHCI_TRB_TYPE_NORMAL:
+		return (xhci_mdb_print_normal_trb(pa, status, trbflags));
+	}
+
+	/*
+	 * Just print generic information if we don't have a specific printer
+	 * for that TRB type.
+	 */
+	mdb_printf("TRB Address: 0x%lx\n", pa);
+	mdb_printf("TRB Status: 0x%x\n", status);
+	mdb_printf("TRB Flags: 0x%x\n", trbflags);
+	mdb_dec_indent(XHCI_MDB_TRB_INDENT);
+
+	return (DCMD_OK);
+}
+
+static int
+xhci_mdb_walk_xhci_init(mdb_walk_state_t *wsp)
+{
+	GElf_Sym sym;
+	uintptr_t addr;
+
+	if (wsp->walk_addr != 0) {
+		mdb_warn("::walk xhci only supports global walks\n");
+		return (WALK_ERR);
+	}
+
+	if (mdb_lookup_by_obj("xhci", "xhci_soft_state", &sym) != 0) {
+		mdb_warn("failed to find xhci_soft_state symbol");
+		return (WALK_ERR);
+	}
+
+	if (mdb_vread(&addr, sizeof (addr), sym.st_value) != sizeof (addr)) {
+		mdb_warn("failed to read xhci_soft_state at %p", addr);
+		return (WALK_ERR);
+	}
+
+	wsp->walk_addr = addr;
+	if (mdb_layered_walk("softstate", wsp) != 0) {
+		mdb_warn("failed to walk softstate");
+		return (WALK_ERR);
+	}
+
+	return (WALK_NEXT);
+}
+
+static int
+xhci_mdb_walk_xhci_step(mdb_walk_state_t *wsp)
+{
+	xhci_t xhci;
+
+	if (mdb_vread(&xhci, sizeof (xhci), wsp->walk_addr) != sizeof (xhci)) {
+		mdb_warn("failed to read xhci_t at %p", wsp->walk_addr);
+		return (WALK_ERR);
+	}
+
+	return (wsp->walk_callback(wsp->walk_addr, &xhci, wsp->walk_cbdata));
+}
+
+static int
+xhci_mdb_walk_xhci_device_init(mdb_walk_state_t *wsp)
+{
+	uintptr_t addr;
+
+	if (wsp->walk_addr == 0) {
+		mdb_warn("::walk xhci_device requires an xhci_t\n");
+		return (WALK_ERR);
+	}
+
+	addr = wsp->walk_addr;
+	addr += offsetof(xhci_t, xhci_usba);
+	addr += offsetof(xhci_usba_t, xa_devices);
+	wsp->walk_addr = (uintptr_t)addr;
+	if (mdb_layered_walk("list", wsp) != 0) {
+		mdb_warn("failed to walk list");
+		return (WALK_ERR);
+	}
+
+	return (WALK_NEXT);
+}
+
+static int
+xhci_mdb_walk_xhci_device_step(mdb_walk_state_t *wsp)
+{
+	xhci_device_t xd;
+
+	if (mdb_vread(&xd, sizeof (xd), wsp->walk_addr) != sizeof (xd)) {
+		mdb_warn("failed to read xhci_device_t at %p", wsp->walk_addr);
+		return (WALK_ERR);
+	}
+
+	return (wsp->walk_callback(wsp->walk_addr, &xd, wsp->walk_cbdata));
+}
+
+static int
+xhci_mdb_walk_xhci_endpoint_init(mdb_walk_state_t *wsp)
+{
+	xhci_mdb_walk_endpoint_t *xm;
+	xhci_device_t *xd;
+
+	if (wsp->walk_addr == 0) {
+		mdb_warn("::walk xhci_endpoint requires an xhci_device_t\n");
+		return (WALK_ERR);
+	}
+
+	xm = mdb_alloc(sizeof (xhci_mdb_walk_endpoint_t), UM_SLEEP | UM_GC);
+	xm->xmwe_ep = 0;
+	xd = &xm->xmwe_device;
+	if (mdb_vread(xd, sizeof (*xd), wsp->walk_addr) != sizeof (*xd)) {
+		mdb_warn("failed to read xhci_endpoint_t at %p",
+		    wsp->walk_addr);
+		return (WALK_ERR);
+	}
+	wsp->walk_data = xm;
+
+	return (WALK_NEXT);
+}
+
+static int
+xhci_mdb_walk_xhci_endpoint_step(mdb_walk_state_t *wsp)
+{
+	int ret;
+	uintptr_t addr;
+	xhci_mdb_walk_endpoint_t *xm = wsp->walk_data;
+
+	if (xm->xmwe_ep >= XHCI_NUM_ENDPOINTS)
+		return (WALK_DONE);
+
+	addr = (uintptr_t)xm->xmwe_device.xd_endpoints[xm->xmwe_ep];
+	if (addr != 0) {
+		xhci_endpoint_t xe;
+
+		if (mdb_vread(&xe, sizeof (xe), addr) != sizeof (xe)) {
+			mdb_warn("failed to read xhci_endpoint_t at %p",
+			    xm->xmwe_device.xd_endpoints[xm->xmwe_ep]);
+			return (WALK_ERR);
+		}
+
+		ret = wsp->walk_callback(addr, &xe, wsp->walk_cbdata);
+	} else {
+		ret = WALK_NEXT;
+	}
+	xm->xmwe_ep++;
+
+	return (ret);
+}
+
+typedef struct xhci_mdb_find {
+	int		xmf_slot;
+	int		xmf_ep;
+	uintptr_t	xmf_addr;
+} xhci_mdb_find_t;
+
+static int
+xhci_mdb_find_endpoint_cb(uintptr_t addr, const void *data, void *arg)
+{
+	const xhci_endpoint_t *xep = data;
+	xhci_mdb_find_t *xmf = arg;
+
+	/*
+	 * The endpoints that are presented here are off by one from the actual
+	 * endpoint ID in the xhci_endpoint_t, as we're really displaying the
+	 * index into the device input context.
+	 */
+	if (xep->xep_num + 1 == xmf->xmf_ep) {
+		xmf->xmf_addr = addr;
+		return (WALK_DONE);
+	}
+
+	return (WALK_NEXT);
+}
+
+static int
+xhci_mdb_find_device_cb(uintptr_t addr, const void *data, void *arg)
+{
+	const xhci_device_t *xd = data;
+	xhci_mdb_find_t *xmf = arg;
+
+	if (xd->xd_slot == xmf->xmf_slot) {
+		if (xmf->xmf_ep == -1) {
+			xmf->xmf_addr = addr;
+			return (WALK_DONE);
+		}
+
+		if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_find_endpoint_cb,
+		    xmf, addr) == -1) {
+			mdb_warn("failed to walk xhci_endpoint at %p", addr);
+			return (WALK_ERR);
+		}
+
+		return (WALK_DONE);
+	}
+
+	return (WALK_NEXT);
+}
+
+static int
+xhci_mdb_find(uintptr_t addr, uint_t flags, int argc,
+    const mdb_arg_t *argv)
+{
+	uintptr_t ep, slot;
+	boolean_t ep_set, slot_set;
+	xhci_mdb_find_t xmf;
+
+	if ((flags & DCMD_ADDRSPEC) == 0)
+		return (DCMD_USAGE);
+
+	ep_set = slot_set = B_FALSE;
+	if (mdb_getopts(argc, argv, 'e', MDB_OPT_UINTPTR_SET, &ep_set, &ep,
+	    's', MDB_OPT_UINTPTR_SET, &slot_set, &slot) != argc)
+		return (DCMD_USAGE);
+
+	if (!slot_set) {
+		mdb_warn("-s is required\n");
+		return (DCMD_USAGE);
+	}
+
+	xmf.xmf_slot = (int)slot;
+	if (ep_set)
+		xmf.xmf_ep = (int)ep;
+	else
+		xmf.xmf_ep = -1;
+	xmf.xmf_addr = 0;
+
+	if (mdb_pwalk("xhci`xhci_device", xhci_mdb_find_device_cb,
+	    &xmf, addr) == -1) {
+		mdb_warn("failed to walk xhci_device at %p", addr);
+		return (DCMD_ERR);
+	}
+
+	if (xmf.xmf_addr == 0) {
+		if (ep_set) {
+			mdb_warn("failed to find xhci_endpoint_t for slot %d "
+			    "and endpoint %d\n", slot, ep);
+		} else {
+			mdb_warn("failed to find xhci_device_t for slot %d\n",
+			    slot);
+		}
+		return (DCMD_ERR);
+	}
+
+	mdb_printf("%p\n", xmf.xmf_addr);
+	return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_endpoint_count(uintptr_t addr, const void *ep, void *arg)
+{
+	int *countp = arg;
+
+	*countp += 1;
+	return (WALK_NEXT);
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_print_endpoint_summary(uintptr_t addr, const void *ep, void *arg)
+{
+	const xhci_device_t *xd = arg;
+	const xhci_endpoint_t *xep = ep;
+	const char *type;
+	const char *state;
+	xhci_endpoint_context_t epctx;
+	int eptype;
+
+	if (mdb_vread(&epctx, sizeof (epctx),
+	    (uintptr_t)xd->xd_endout[xep->xep_num]) != sizeof (epctx)) {
+		mdb_warn("failed to read endpoint context at %p",
+		    xd->xd_endout[xep->xep_num]);
+		return (WALK_ERR);
+	}
+
+	eptype = XHCI_EPCTX_GET_EPTYPE(LE_32(epctx.xec_info2));
+	type = xhci_mdb_epctx_eptypes[eptype];
+	state = xhci_mdb_epctx_states[XHCI_EPCTX_STATE(LE_32(epctx.xec_info))];
+
+	mdb_printf("%-4d %-10s %-10s 0x%-04x 0x%-04x\n", xep->xep_num, type,
+	    state, xep->xep_ring.xr_head, xep->xep_ring.xr_tail);
+
+	return (WALK_NEXT);
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_print_device(uintptr_t addr, uint_t flags, int argc,
+    const mdb_arg_t *argv)
+{
+	int count;
+	xhci_device_t xd;
+	usba_device_t ud;
+	char product[256], mfg[256];
+
+	if (!(flags & DCMD_ADDRSPEC)) {
+		return (mdb_eval("::walk xhci`xhci | ::walk xhci`xhci_device | "
+		    "::xhci_device"));
+	}
+
+	if (mdb_vread(&xd, sizeof (xd), addr) != sizeof (xd)) {
+		mdb_warn("failed to read xhci_device_t at 0x%x", addr);
+		return (DCMD_ERR);
+	}
+
+	if (mdb_vread(&ud, sizeof (ud), (uintptr_t)xd.xd_usbdev) !=
+	    sizeof (ud)) {
+		mdb_warn("failed to read usba_device_t at %p\n", xd.xd_usbdev);
+		return (DCMD_ERR);
+	}
+
+	if (ud.usb_mfg_str == NULL || mdb_readstr(mfg, sizeof (mfg),
+	    (uintptr_t)ud.usb_mfg_str) <= 0) {
+		(void) strlcpy(mfg, "Unknown Manufacturer", sizeof (mfg));
+	}
+
+	if (ud.usb_product_str == NULL || mdb_readstr(product, sizeof (product),
+	    (uintptr_t)ud.usb_product_str) <= 0) {
+		(void) strlcpy(product, "Unknown Product", sizeof (product));
+	}
+
+	mdb_printf("%<b>%s - %s%</b>\n", mfg, product);
+
+	count = 0;
+	if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_endpoint_count, &count,
+	    addr) == -1) {
+		mdb_warn("failed to walk xhci_endpoint rooted at 0x%x", addr);
+		return (DCMD_ERR);
+	}
+
+	mdb_printf("Port %02d | Slot %02d | # Endpoints %02d\n", xd.xd_port,
+	    xd.xd_slot, count);
+	mdb_printf("%<u>%-4s %-10s %-10s %-6s %-6s%</u>\n", "EP", "Type",
+	    "State", "Head", "Tail");
+
+	if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_print_endpoint_summary,
+	    &xd, addr) == -1) {
+		mdb_warn("failed to walk xhci_endpoint rooted at 0x%x", addr);
+		return (DCMD_ERR);
+	}
+
+
+	mdb_printf("\n");
+
+	return (DCMD_OK);
+}
+
+static int
+xhci_mdb_find_trb(uintptr_t addr, uint_t flags, int argc,
+    const mdb_arg_t *argv)
+{
+	xhci_ring_t xr;
+	uint64_t base, max, target;
+
+	if (!(flags & DCMD_ADDRSPEC)) {
+		mdb_warn("missing required xhci_ring_t\n");
+		return (DCMD_USAGE);
+	}
+
+	if (argc == 0) {
+		mdb_warn("missing required PA of ring\n");
+		return (DCMD_USAGE);
+	}
+
+	if (argc > 1) {
+		mdb_warn("too many arguments\n");
+		return (DCMD_USAGE);
+	}
+
+	if (mdb_vread(&xr, sizeof (xr), addr) != sizeof (xr)) {
+		mdb_warn("failed to read xhci_ring_t at %p", addr);
+		return (DCMD_USAGE);
+	}
+
+	if (argv[0].a_type == MDB_TYPE_IMMEDIATE) {
+		target = argv[0].a_un.a_val;
+	} else if (argv[0].a_type == MDB_TYPE_STRING) {
+		target = mdb_strtoull(argv[0].a_un.a_str);
+	} else {
+		mdb_warn("argument is an unknown supported type: %d\n",
+		    argv[0].a_type);
+		return (DCMD_USAGE);
+	}
+	target = roundup(target, sizeof (xhci_trb_t));
+
+	base = xr.xr_dma.xdb_cookies[0].dmac_laddress;
+	max = base + xr.xr_ntrb * sizeof (xhci_trb_t);
+
+	if (target < base || target > max) {
+		mdb_warn("target address %p is outside the range of PAs for "
+		    "TRBs in the ring [%p, %p)", target, base, max);
+		return (DCMD_ERR);
+	}
+	target -= base;
+	mdb_printf("0x%" PRIx64 "\n", target + (uintptr_t)xr.xr_trb);
+
+	return (DCMD_OK);
+}
+
+static const mdb_dcmd_t xhci_dcmds[] = {
+	{ "xhci_epctx", ":", "print endpoint context",
+	    xhci_mdb_print_epctx, NULL },
+	{ "xhci_slotctx", ":", "print slot context",
+	    xhci_mdb_print_slotctx, NULL },
+	{ "xhci_trb", ":", "print TRB",
+	    xhci_mdb_print_trb, NULL },
+	{ "xhci_find", ": -s slot [-e endpiont]",
+	    "find given xhci slot or endpoint",
+	    xhci_mdb_find, NULL },
+	{ "xhci_device", ":", "device summary",
+	    xhci_mdb_print_device, NULL },
+	{ "xhci_find_trb", ": pa", "find trb with PA in ring",
+	    xhci_mdb_find_trb, NULL },
+	{ NULL }
+};
+
+static const mdb_walker_t xhci_walkers[] = {
+	{ "xhci", "walk list of xhci_t structures",
+	    xhci_mdb_walk_xhci_init, xhci_mdb_walk_xhci_step, NULL },
+	{ "xhci_device", "walk list of xhci_device_t structures",
+	    xhci_mdb_walk_xhci_device_init, xhci_mdb_walk_xhci_device_step,
+	    NULL },
+	{ "xhci_endpoint", "walk list of xhci_endpoint_t structures",
+	    xhci_mdb_walk_xhci_endpoint_init, xhci_mdb_walk_xhci_endpoint_step,
+	    NULL },
+	{ NULL }
+};
+
+static const mdb_modinfo_t xhci_modinfo = {
+	MDB_API_VERSION, xhci_dcmds, xhci_walkers
+};
+
+const mdb_modinfo_t *
+_mdb_init(void)
+{
+	return (&xhci_modinfo);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/xhci/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,43 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2016 Joyent, Inc.
+#
+
+PROG= xhci_portsc
+
+ROOTLIBXHCI = $(ROOTLIB)/xhci
+ROOTLIBXHCIPROG = $(PROG:%=$(ROOTLIBXHCI)/%)
+
+include ../Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I$(SRC)/uts/common/
+LDLIBS += -ldevinfo
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTLIBXHCIPROG)
+
+clean:
+
+lint:	lint_PROG
+
+$(ROOTLIBXHCI):
+	$(INS.dir)
+
+$(ROOTLIBXHCI)/%: % $(ROOTLIBXHCI)
+	$(INS.file)
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/xhci/xhci_portsc.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,373 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * This is a private utility that combines a number of minor debugging routines
+ * for xhci.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libdevinfo.h>
+#include <sys/usb/hcd/xhci/xhci_ioctl.h>
+#include <sys/usb/hcd/xhci/xhcireg.h>
+
+static char *xp_devpath = NULL;
+static int xp_npaths;
+static const char *xp_path;
+static const char *xp_state = NULL;
+static uint32_t xp_port;
+static boolean_t xp_verbose = B_FALSE;
+static boolean_t xp_clear = B_FALSE;
+static boolean_t xp_list = B_FALSE;
+extern const char *__progname;
+
+static int
+xp_usage(const char *format, ...)
+{
+	if (format != NULL) {
+		va_list alist;
+
+		va_start(alist, format);
+		vwarnx(format, alist);
+		va_end(alist);
+	}
+
+	(void) fprintf(stderr, "usage:  %s [-l] [-v] [-c] [-d path] [-p port] "
+	    "[-s state]\n", __progname);
+	return (2);
+}
+
+static const char *xp_pls_strings[] = {
+	"U0",
+	"U1",
+	"U2",
+	"U3 (suspended)",
+	"Disabled",
+	"RxDetect",
+	"Inactive",
+	"Polling",
+	"Recovery",
+	"Hot Reset",
+	"Compliance Mode",
+	"Test Mode",
+	"Reserved",
+	"Reserved",
+	"Reserved",
+	"Resume",
+	NULL
+};
+
+static void
+xp_dump_verbose(uint32_t portsc)
+{
+	if (portsc & XHCI_PS_CCS)
+		(void) printf("\t\t\tCCS\n");
+	if (portsc & XHCI_PS_PED)
+		(void) printf("\t\t\tPED\n");
+	if (portsc & XHCI_PS_OCA)
+		(void) printf("\t\t\tOCA\n");
+	if (portsc & XHCI_PS_PR)
+		(void) printf("\t\t\tPR\n");
+	if (portsc & XHCI_PS_PP) {
+		(void) printf("\t\t\tPLS: %s (%d)\n",
+		    xp_pls_strings[XHCI_PS_PLS_GET(portsc)],
+		    XHCI_PS_PLS_GET(portsc));
+		(void) printf("\t\t\tPP\n");
+	} else {
+		(void) printf("\t\t\tPLS: undefined (No PP)\n");
+	}
+
+	if (XHCI_PS_SPEED_GET(portsc) != 0) {
+		(void) printf("\t\t\tPort Speed: ");
+		switch (XHCI_PS_SPEED_GET(portsc)) {
+		case 0:
+			(void) printf("Undefined ");
+			break;
+		case XHCI_SPEED_FULL:
+			(void) printf("Full ");
+			break;
+		case XHCI_SPEED_LOW:
+			(void) printf("Low ");
+			break;
+		case XHCI_SPEED_HIGH:
+			(void) printf("High ");
+			break;
+		case XHCI_SPEED_SUPER:
+			(void) printf("Super ");
+			break;
+		default:
+			(void) printf("Unknown ");
+			break;
+		}
+		(void) printf("(%d)\n", XHCI_PS_SPEED_GET(portsc));
+	}
+	if (XHCI_PS_PIC_GET(portsc) != 0)
+		(void) printf("\t\t\tPIC: %d\n", XHCI_PS_PIC_GET(portsc));
+
+	if (portsc & XHCI_PS_LWS)
+		(void) printf("\t\t\tLWS\n");
+	if (portsc & XHCI_PS_CSC)
+		(void) printf("\t\t\tCSC\n");
+	if (portsc & XHCI_PS_PEC)
+		(void) printf("\t\t\tPEC\n");
+	if (portsc & XHCI_PS_WRC)
+		(void) printf("\t\t\tWRC\n");
+	if (portsc & XHCI_PS_OCC)
+		(void) printf("\t\t\tOCC\n");
+	if (portsc & XHCI_PS_PRC)
+		(void) printf("\t\t\tPRC\n");
+	if (portsc & XHCI_PS_PLC)
+		(void) printf("\t\t\tPLC\n");
+	if (portsc & XHCI_PS_CEC)
+		(void) printf("\t\t\tCEC\n");
+	if (portsc & XHCI_PS_CAS)
+		(void) printf("\t\t\tCAS\n");
+	if (portsc & XHCI_PS_WCE)
+		(void) printf("\t\t\tWCE\n");
+	if (portsc & XHCI_PS_WDE)
+		(void) printf("\t\t\tWDE\n");
+	if (portsc & XHCI_PS_WOE)
+		(void) printf("\t\t\tWOE\n");
+	if (portsc & XHCI_PS_DR)
+		(void) printf("\t\t\tDR\n");
+	if (portsc & XHCI_PS_WPR)
+		(void) printf("\t\t\tWPR\n");
+}
+
+static void
+xp_dump(const char *path)
+{
+	int fd, i;
+	xhci_ioctl_portsc_t xhi = { 0 };
+
+	fd = open(path, O_RDWR);
+	if (fd < 0) {
+		err(EXIT_FAILURE, "failed to open %s", path);
+	}
+
+	if (ioctl(fd, XHCI_IOCTL_PORTSC, &xhi) != 0)
+		err(EXIT_FAILURE, "failed to get port status");
+
+	(void) close(fd);
+
+	for (i = 1; i <= xhi.xhi_nports; i++) {
+		if (xp_port != 0 && i != xp_port)
+			continue;
+
+		(void) printf("port %2d:\t0x%08x\n", i, xhi.xhi_portsc[i]);
+		if (xp_verbose == B_TRUE)
+			xp_dump_verbose(xhi.xhi_portsc[i]);
+	}
+}
+
+static void
+xp_set_pls(const char *path, uint32_t port, const char *state)
+{
+	int fd, i;
+	xhci_ioctl_setpls_t xis;
+
+	fd = open(path, O_RDWR);
+	if (fd < 0) {
+		err(EXIT_FAILURE, "failed to open %s", path);
+	}
+
+	xis.xis_port = port;
+	for (i = 0; xp_pls_strings[i] != NULL; i++) {
+		if (strcasecmp(state, xp_pls_strings[i]) == 0)
+			break;
+	}
+
+	if (xp_pls_strings[i] == NULL) {
+		errx(EXIT_FAILURE, "unknown state string: %s\n", state);
+	}
+
+	xis.xis_pls = i;
+	(void) printf("setting port %d with pls %d\n", port, xis.xis_pls);
+
+	if (ioctl(fd, XHCI_IOCTL_SETPLS, &xis) != 0)
+		err(EXIT_FAILURE, "failed to set port status");
+
+	(void) close(fd);
+}
+
+static void
+xp_clear_change(const char *path, uint32_t port)
+{
+	int fd;
+	xhci_ioctl_clear_t xic;
+
+	fd = open(path, O_RDWR);
+	if (fd < 0) {
+		err(EXIT_FAILURE, "failed to open %s", path);
+	}
+
+	xic.xic_port = port;
+	(void) printf("clearing change bits on port %d\n", port);
+	if (ioctl(fd, XHCI_IOCTL_CLEAR, &xic) != 0)
+		err(EXIT_FAILURE, "failed to set port status");
+
+	(void) close(fd);
+}
+
+/* ARGSUSED */
+static int
+xp_devinfo_cb(di_node_t node, void *arg)
+{
+	char *drv;
+	di_minor_t minor;
+	boolean_t *do_print = arg;
+
+	drv = di_driver_name(node);
+	if (drv == NULL)
+		return (DI_WALK_CONTINUE);
+	if (strcmp(drv, "xhci") != 0)
+		return (DI_WALK_CONTINUE);
+
+	/*
+	 * We have an instance of the xhci driver. We need to find the minor
+	 * node for the hubd instance. These are all usually greater than
+	 * HUBD_IS_ROOT_HUB. However, to avoid hardcoding that here, we instead
+	 * rely on the fact that the minor node for the actual device has a
+	 * :hubd as the intance.
+	 */
+	minor = DI_MINOR_NIL;
+	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
+		char *mname, *path;
+
+		mname = di_minor_name(minor);
+		if (mname == NULL)
+			continue;
+		if (strcmp(mname, "hubd") != 0)
+			continue;
+		path = di_devfs_minor_path(minor);
+		if (*do_print == B_TRUE) {
+			(void) printf("/devices%s\n", path);
+			di_devfs_path_free(path);
+		} else {
+			xp_npaths++;
+			if (xp_devpath == NULL)
+				xp_devpath = path;
+			else
+				di_devfs_path_free(path);
+		}
+	}
+
+	return (DI_WALK_PRUNECHILD);
+}
+
+/*
+ * We need to find all minor nodes of instances of the xhci driver whose name is
+ * 'hubd'.
+ */
+static void
+xp_find_devs(boolean_t print)
+{
+	di_node_t root;
+
+	if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
+		err(EXIT_FAILURE, "failed to initialize devices tree");
+	}
+
+	if (di_walk_node(root, DI_WALK_CLDFIRST, &print, xp_devinfo_cb) != 0)
+		err(EXIT_FAILURE, "failed to walk devices tree");
+}
+
+int
+main(int argc, char *argv[])
+{
+	int c;
+	char devpath[PATH_MAX];
+
+	while ((c = getopt(argc, argv, ":d:vlcp:s:")) != -1) {
+		switch (c) {
+		case 'c':
+			xp_clear = B_TRUE;
+			break;
+		case 'd':
+			xp_path = optarg;
+			break;
+		case 'l':
+			xp_list = B_TRUE;
+			break;
+		case 'v':
+			xp_verbose = B_TRUE;
+			break;
+		case 'p':
+			xp_port = atoi(optarg);
+			if (xp_port < 1 || xp_port > XHCI_PORTSC_NPORTS)
+				return (xp_usage("invalid port for -p: %d\n",
+				    optarg));
+			break;
+		case 's':
+			xp_state = optarg;
+			break;
+		case ':':
+			return (xp_usage("-%c requires an operand\n", optopt));
+		case '?':
+			return (xp_usage("unknown option: -%c\n", optopt));
+		default:
+			abort();
+		}
+	}
+
+	if (xp_list == B_TRUE && (xp_path != NULL || xp_clear == B_TRUE ||
+	    xp_port > 0 || xp_state != NULL)) {
+		return (xp_usage("-l cannot be used with other options\n"));
+	}
+
+	if (xp_list == B_TRUE) {
+		xp_find_devs(B_TRUE);
+		return (0);
+	}
+
+	if (xp_path == NULL) {
+		xp_find_devs(B_FALSE);
+		if (xp_npaths == 0) {
+			errx(EXIT_FAILURE, "no xhci devices found");
+		} else if (xp_npaths > 1) {
+			errx(EXIT_FAILURE, "more than one xhci device found, "
+			    "please specify device with -d, use -l to list");
+		}
+		if (snprintf(devpath, sizeof (devpath), "/devices/%s",
+		    xp_devpath) >= sizeof (devpath))
+			errx(EXIT_FAILURE, "xhci path found at %s overflows "
+			    "internal device path");
+		di_devfs_path_free(xp_devpath);
+		xp_devpath = NULL;
+		xp_path = devpath;
+	}
+
+	if (xp_clear == B_TRUE && xp_state != NULL) {
+		return (xp_usage("-c and -s can't be used together\n"));
+	}
+
+	if (xp_state != NULL) {
+		xp_set_pls(xp_path, xp_port, xp_state);
+	} else if (xp_clear == B_TRUE) {
+		xp_clear_change(xp_path, xp_port);
+	} else {
+		xp_dump(xp_path);
+	}
+
+	return (0);
+}
--- a/usr/src/common/pci/pci_strings.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/common/pci/pci_strings.c	Fri Mar 10 18:57:44 2017 -0500
@@ -160,6 +160,7 @@
 	12, 3, 0,	"Universal Serial Bus UHCI compliant",	"usb",
 	12, 3, 0x10,	"Universal Serial Bus OHCI compliant",	"usb",
 	12, 3, 0x20,	"Universal Serial Bus EHCI compliant",	"usb",
+	12, 3, 0x30,	"Universal Serial Bus XHCI compliant",	"usb",
 	12, 3, 0x80,	"Universal Serial Bus generic HCD",	"usb",
 	12, 3, 0xFE,	"Universal Serial Bus device",		"usb",
 	12, 4, 0,	"Fibre Channel",			"fibre",
--- a/usr/src/pkg/manifests/driver-usb.mf	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/pkg/manifests/driver-usb.mf	Fri Mar 10 18:57:44 2017 -0500
@@ -43,6 +43,8 @@
 dir path=kernel/dacf/$(ARCH64) group=sys
 dir path=kernel/drv group=sys
 dir path=kernel/drv/$(ARCH64) group=sys
+dir path=kernel/kmdb group=sys
+dir path=kernel/kmdb/$(ARCH64) group=sys
 dir path=kernel/misc group=sys
 dir path=kernel/misc/$(ARCH64) group=sys
 dir path=kernel/strmod group=sys
@@ -54,6 +56,11 @@
 dir path=lib/svc/method
 dir path=sbin group=sys
 dir path=usr group=sys
+dir path=usr/lib
+dir path=usr/lib/mdb group=sys
+dir path=usr/lib/mdb/kvm group=sys
+dir path=usr/lib/mdb/kvm/$(ARCH64) group=sys
+$(i386_ONLY)dir path=usr/lib/xhci group=sys
 dir path=usr/share
 dir path=usr/share/man
 dir path=usr/share/man/man7m
@@ -72,6 +79,7 @@
 driver name=usb_ia alias=usb,ia
 driver name=usb_mid alias=usb,device
 driver name=usbprn alias=usbif,class7.1 perms="* 0666 root sys"
+$(i386_ONLY)driver name=xhci alias=pciclass,0c0330 perms="* 0644 root sys"
 file path=etc/usb/config_map.conf group=sys preserve=true
 file path=kernel/drv/$(ARCH64)/ehci group=sys
 file path=kernel/drv/$(ARCH64)/hid group=sys
@@ -84,6 +92,7 @@
 file path=kernel/drv/$(ARCH64)/usb_ia group=sys
 file path=kernel/drv/$(ARCH64)/usb_mid group=sys
 file path=kernel/drv/$(ARCH64)/usbprn group=sys
+$(i386_ONLY)file path=kernel/drv/$(ARCH64)/xhci group=sys
 $(i386_ONLY)file path=kernel/drv/ehci group=sys
 file path=kernel/drv/ehci.conf group=sys
 $(i386_ONLY)file path=kernel/drv/hid group=sys
@@ -100,6 +109,10 @@
 $(i386_ONLY)file path=kernel/drv/usb_ia group=sys
 $(i386_ONLY)file path=kernel/drv/usb_mid group=sys
 $(i386_ONLY)file path=kernel/drv/usbprn group=sys
+$(i386_ONLY)file path=kernel/drv/xhci group=sys
+$(i386_ONLY)file path=kernel/drv/xhci.conf group=sys
+$(i386_ONLY)file path=kernel/kmdb/$(ARCH64)/xhci group=sys mode=0555
+$(i386_ONLY)file path=kernel/kmdb/xhci group=sys mode=0555
 file path=kernel/misc/$(ARCH64)/hidparser group=sys mode=0755
 file path=kernel/misc/$(ARCH64)/usba group=sys mode=0755
 file path=kernel/misc/$(ARCH64)/usba10 group=sys mode=0755
@@ -114,6 +127,9 @@
 $(i386_ONLY)file path=kernel/strmod/usbkbm group=sys mode=0755
 $(i386_ONLY)file path=kernel/strmod/usbms group=sys mode=0755
 $(i386_ONLY)file path=kernel/strmod/usbwcm group=sys mode=0755
+$(i386_ONLY)file path=usr/lib/mdb/kvm/$(ARCH64)/xhci.so group=sys mode=0555
+$(i386_ONLY)file path=usr/lib/mdb/kvm/xhci.so group=sys mode=0555
+$(i386_ONLY)file path=usr/lib/xhci/xhci_portsc group=sys mode=0555
 file path=usr/share/man/man7d/ehci.7d
 file path=usr/share/man/man7d/hid.7d
 file path=usr/share/man/man7d/hubd.7d
@@ -133,6 +149,7 @@
 file path=usr/share/man/man9f/usb_client_attach.9f
 file path=usr/share/man/man9f/usb_clr_feature.9f
 file path=usr/share/man/man9f/usb_create_pm_components.9f
+file path=usr/share/man/man9f/usb_ep_xdescr_fill.9f
 file path=usr/share/man/man9f/usb_get_addr.9f
 file path=usr/share/man/man9f/usb_get_alt_if.9f
 file path=usr/share/man/man9f/usb_get_cfg.9f
@@ -152,26 +169,30 @@
 file path=usr/share/man/man9f/usb_pipe_get_state.9f
 file path=usr/share/man/man9f/usb_pipe_intr_xfer.9f
 file path=usr/share/man/man9f/usb_pipe_isoc_xfer.9f
-file path=usr/share/man/man9f/usb_pipe_open.9f
 file path=usr/share/man/man9f/usb_pipe_reset.9f
 file path=usr/share/man/man9f/usb_pipe_set_private.9f
+file path=usr/share/man/man9f/usb_pipe_xopen.9f
 file path=usr/share/man/man9f/usb_register_hotplug_cbs.9f
 file path=usr/share/man/man9f/usb_reset_device.9f
-file path=usr/share/man/man9s/usb_bulk_request.9s
+file path=usr/share/man/man9s/usb_bulk_req.9s
 file path=usr/share/man/man9s/usb_callback_flags.9s
 file path=usr/share/man/man9s/usb_cfg_descr.9s
 file path=usr/share/man/man9s/usb_client_dev_data.9s
 file path=usr/share/man/man9s/usb_completion_reason.9s
-file path=usr/share/man/man9s/usb_ctrl_request.9s
+file path=usr/share/man/man9s/usb_ctrl_req.9s
 file path=usr/share/man/man9s/usb_dev_descr.9s
 file path=usr/share/man/man9s/usb_dev_qlf_descr.9s
 file path=usr/share/man/man9s/usb_ep_descr.9s
+file path=usr/share/man/man9s/usb_ep_ss_comp_descr.9s
+file path=usr/share/man/man9s/usb_ep_xdescr.9s
 file path=usr/share/man/man9s/usb_if_descr.9s
-file path=usr/share/man/man9s/usb_intr_request.9s
-file path=usr/share/man/man9s/usb_isoc_request.9s
+file path=usr/share/man/man9s/usb_intr_req.9s
+file path=usr/share/man/man9s/usb_isoc_req.9s
 file path=usr/share/man/man9s/usb_other_speed_cfg_descr.9s
 file path=usr/share/man/man9s/usb_request_attributes.9s
 file path=usr/share/man/man9s/usb_string_descr.9s
 license cr_Sun license=cr_Sun
 license lic_CDDL license=lic_CDDL
 license license_in_headers license=license_in_headers
+license usr/src/uts/common/sys/usb/hcd/xhci/THIRDPARTYLICENSE \
+    license=usr/src/uts/common/sys/usb/hcd/xhci/THIRDPARTYLICENSE
--- a/usr/src/uts/common/Makefile.files	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/common/Makefile.files	Fri Mar 10 18:57:44 2017 -0500
@@ -1546,6 +1546,10 @@
 
 EHCI_OBJS += ehci.o ehci_hub.o ehci_xfer.o ehci_intr.o ehci_util.o ehci_polled.o ehci_isoch.o ehci_isoch_util.o
 
+XHCI_OBJS += xhci.o xhci_quirks.o xhci_dma.o xhci_context.o xhci_intr.o \
+	     xhci_ring.o xhci_command.o xhci_event.o xhci_usba.o \
+	     xhci_endpoint.o xhci_hub.o
+
 HUBD_OBJS += hubd.o
 
 USB_MID_OBJS += usb_mid.o
--- a/usr/src/uts/common/Makefile.rules	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/common/Makefile.rules	Fri Mar 10 18:57:44 2017 -0500
@@ -989,6 +989,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/usb/hcd/xhci/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/usb/hcd/uhci/%.c
 	$(COMPILE.c) -I../../common -o $@ $<
 	$(CTFCONVERT_O)
--- a/usr/src/uts/common/io/consconfig_dacf.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/common/io/consconfig_dacf.c	Fri Mar 10 18:57:44 2017 -0500
@@ -270,7 +270,8 @@
 };
 
 int
-_init(void) {
+_init(void)
+{
 	return (mod_install(&modlinkage));
 }
 
@@ -958,7 +959,12 @@
 	 * Regardless of platform, ehci needs to initialize first to avoid
 	 * unnecessary connects and disconnects on the companion controller
 	 * when ehci sets up the routing.
+	 *
+	 * The same is generally true of xhci. Many platforms have routing
+	 * between the xhci controller and the ehci controller. To avoid those
+	 * same disconnects, we load xhci before ehci.
 	 */
+	(void) ddi_hold_installed_driver(ddi_name_to_major("xhci"));
 	(void) ddi_hold_installed_driver(ddi_name_to_major("ehci"));
 	(void) ddi_hold_installed_driver(ddi_name_to_major("uhci"));
 	(void) ddi_hold_installed_driver(ddi_name_to_major("ohci"));
--- a/usr/src/uts/common/io/usb/clients/hid/hid.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/common/io/usb/clients/hid/hid.c	Fri Mar 10 18:57:44 2017 -0500
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 
@@ -280,8 +281,7 @@
  */
 /*ARGSUSED*/
 static int
-hid_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
-			void *arg, void **result)
+hid_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 {
 	hid_state_t	*hidp = NULL;
 	int		error = DDI_FAILURE;
@@ -414,7 +414,11 @@
 	}
 
 	mutex_enter(&hidp->hid_mutex);
-	hidp->hid_ep_intr_descr = ep_data->ep_descr;
+	if (usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION, dip, ep_data,
+	    &hidp->hid_ep_intr_xdescr) != USB_SUCCESS) {
+
+		goto fail;
+	}
 
 	/*
 	 * Attempt to find the hid descriptor, it could be after interface
@@ -714,7 +718,7 @@
  *	Gets called at the time of detach.
  */
 static int
-hid_detach(dev_info_t *dip, ddi_detach_cmd_t	cmd)
+hid_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 {
 	int instance = ddi_get_instance(dip);
 	hid_state_t	*hidp;
@@ -844,8 +848,8 @@
 	/* Check if interrupt endpoint exists */
 	if (no_of_ep > 0) {
 		/* Open the interrupt pipe */
-		if (usb_pipe_open(hidp->hid_dip,
-		    &hidp->hid_ep_intr_descr,
+		if (usb_pipe_xopen(hidp->hid_dip,
+		    &hidp->hid_ep_intr_xdescr,
 		    &hidp->hid_intr_pipe_policy, USB_FLAGS_SLEEP,
 		    &hidp->hid_interrupt_pipe) !=
 		    USB_SUCCESS) {
@@ -1783,11 +1787,8 @@
  *	endpoint descriptor
  */
 static size_t
-hid_parse_hid_descr(
-	usb_hid_descr_t		*ret_descr,
-	size_t			ret_buf_len,
-	usb_alt_if_data_t	*altif_data,
-	usb_ep_data_t		*ep_data)
+hid_parse_hid_descr(usb_hid_descr_t *ret_descr,	size_t ret_buf_len,
+    usb_alt_if_data_t *altif_data, usb_ep_data_t *ep_data)
 {
 	usb_cvs_data_t *cvs;
 	int		which_cvs;
@@ -1878,8 +1879,7 @@
  *	it and query the hidparser tree to get the packet size
  */
 static int
-hid_handle_report_descriptor(hid_state_t	*hidp,
-				int		interface)
+hid_handle_report_descriptor(hid_state_t *hidp, int interface)
 {
 	usb_cr_t		completion_reason;
 	usb_cb_flags_t		cb_flags;
@@ -1965,7 +1965,7 @@
  */
 /*ARGSUSED*/
 static void
-hid_set_idle(hid_state_t	*hidp)
+hid_set_idle(hid_state_t *hidp)
 {
 	usb_cr_t	completion_reason;
 	usb_cb_flags_t	cb_flags;
@@ -2509,9 +2509,8 @@
  */
 static int
 hid_send_async_ctrl_request(hid_default_pipe_arg_t *hid_default_pipe_arg,
-			hid_req_t *hid_request,
-			uchar_t request_type, int request_request,
-			ushort_t request_index)
+    hid_req_t *hid_request, uchar_t request_type, int request_request,
+    ushort_t request_index)
 {
 	queue_t		*q = hid_default_pipe_arg->hid_default_pipe_arg_queue;
 	hid_state_t	*hidp = (hid_state_t *)q->q_ptr;
--- a/usr/src/uts/common/io/usb/hcd/ehci/ehci_hub.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/common/io/usb/hcd/ehci/ehci_hub.c	Fri Mar 10 18:57:44 2017 -0500
@@ -1272,7 +1272,7 @@
 	port_status = ehci_get_root_hub_port_status(ehcip, port);
 
 	new_port_status = port_status & PORT_STATUS_MASK;
-	change_status = (port_status >> 16) & PORT_CHANGE_MASK;
+	change_status = (port_status >> 16) & PORT_CHANGE_MASK_2X;
 
 	ehcip->ehci_root_hub.rh_port_status[port] = new_port_status;
 
@@ -1924,7 +1924,7 @@
 		port_status = ehci_get_root_hub_port_status(ehcip, i);
 
 		new_port_status = port_status & PORT_STATUS_MASK;
-		change_status = (port_status >> 16) & PORT_CHANGE_MASK;
+		change_status = (port_status >> 16) & PORT_CHANGE_MASK_2X;
 
 		/*
 		 * If there is change in the port status then set the bit in the
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,2240 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * Extensible Host Controller Interface (xHCI) USB Driver
+ *
+ * The xhci driver is an HCI driver for USB that bridges the gap between client
+ * device drivers and implements the actual way that we talk to devices. The
+ * xhci specification provides access to USB 3.x capable devices, as well as all
+ * prior generations. Like other host controllers, it both provides the way to
+ * talk to devices and also is treated like a hub (often called the root hub).
+ *
+ * This driver is part of the USBA (USB Architecture). It implements the HCDI
+ * (host controller device interface) end of USBA. These entry points are used
+ * by the USBA on behalf of client device drivers to access their devices. The
+ * driver also provides notifications to deal with hot plug events, which are
+ * quite common in USB.
+ *
+ * ----------------
+ * USB Introduction
+ * ----------------
+ *
+ * To properly understand the xhci driver and the design of the USBA HCDI
+ * interfaces it implements, it helps to have a bit of background into how USB
+ * devices are structured and understand how they work at a high-level.
+ *
+ * USB devices, like PCI devices, are broken down into different classes of
+ * device. For example, with USB you have hubs, human-input devices (keyboards,
+ * mice, etc.), mass storage, etc. Every device also has a vendor and device ID.
+ * Many client drivers bind to an entire class of device, for example, the hubd
+ * driver (to hubs) or scsa2usb (USB storage). However, there are other drivers
+ * that bind to explicit IDs such as usbsprl (specific USB to Serial devices).
+ *
+ * USB SPEEDS AND VERSIONS
+ *
+ * USB devices are often referred to in two different ways. One way they're
+ * described is with the USB version that they conform to. In the wild, you're
+ * most likely going to see USB 1.1, 2.0, 2.1, and 3.0. However, you may also
+ * see devices referred to as 'full-', 'low-', 'high-', and 'super-' speed
+ * devices.
+ *
+ * The latter description describes the maximum theoretical speed of a given
+ * device. For example, a super-speed device theoretically caps out around 5
+ * Gbit/s, whereas a low-speed device caps out at 1.5 Mbit/s.
+ *
+ * In general, each speed usually corresponds to a specific USB protocol
+ * generation. For example, all USB 3.0 devices are super-speed devices. All
+ * 'high-speed' devices are USB 2.x devices. Full-speed devices are special in
+ * that they can either be USB 1.x or USB 2.x devices. Low-speed devices are
+ * only a USB 1.x thing, they did not jump the fire line to USB 2.x.
+ *
+ * USB 3.0 devices and ports generally have the wiring for both USB 2.0 and USB
+ * 3.0. When a USB 3.x device is plugged into a USB 2.0 port or hub, then it
+ * will report its version as USB 2.1, to indicate that it is actually a USB 3.x
+ * device.
+ *
+ * USB ENDPOINTS
+ *
+ * A given USB device is made up of endpoints. A request, or transfer, is made
+ * to a specific USB endpoint. These endpoints can provide different services
+ * and have different expectations around the size of the data that'll be used
+ * in a given request and the periodicity of requests. Endpoints themselves are
+ * either used to make one-shot requests, for example, making requests to a mass
+ * storage device for a given sector, or for making periodic requests where you
+ * end up polling on the endpoint, for example, polling on a USB keyboard for
+ * keystrokes.
+ *
+ * Each endpoint encodes two different pieces of information: a direction and a
+ * type. There are two different directions: IN and OUT. These refer to the
+ * general direction that data moves relative to the operating system. For
+ * example, an IN transfer transfers data in to the operating system, from the
+ * device. An OUT transfer transfers data from the operating system, out to the
+ * device.
+ *
+ * There are four different kinds of endpoints:
+ *
+ * 	BULK		These transfers are large transfers of data to or from
+ * 			a device. The most common use for bulk transfers is for
+ * 			mass storage devices. Though they are often also used by
+ * 			network devices and more. Bulk endpoints do not have an
+ * 			explicit time component to them. They are always used
+ * 			for one-shot transfers.
+ *
+ * 	CONTROL		These transfers are used to manipulate devices
+ * 			themselves and are used for USB protocol level
+ * 			operations (whether device-specific, class-specific, or
+ * 			generic across all of USB). Unlike other transfers,
+ * 			control transfers are always bi-directional and use
+ * 			different kinds of transfers.
+ *
+ * 	INTERRUPT	Interrupt transfers are used for small transfers that
+ * 			happen infrequently, but need reasonable latency. A good
+ * 			example of interrupt transfers is to receive input from
+ * 			a USB keyboard. Interrupt-IN transfers are generally
+ * 			polled. Meaning that a client (device driver) opens up
+ * 			an interrupt-IN pipe to poll on it, and receives
+ * 			periodic updates whenever there is information
+ * 			available. However, Interrupt transfers can be used
+ * 			as one-shot transfers both going IN and OUT.
+ *
+ * 	ISOCHRONOUS	These transfers are things that happen once per
+ * 			time-interval at a very regular rate. A good example of
+ * 			these transfers are for audio and video. A device may
+ * 			describe an interval as 10ms at which point it will read
+ * 			or write the next batch of data every 10ms and transform
+ * 			it for the user. There are no one-shot Isochronous-IN
+ * 			transfers. There are one-shot Isochronous-OUT transfers,
+ * 			but these are used by device drivers to always provide
+ * 			the system with sufficient data.
+ *
+ * To find out information about the endpoints, USB devices have a series of
+ * descriptors that cover different aspects of the device. For example, there
+ * are endpoint descriptors which cover the properties of endpoints such as the
+ * maximum packet size or polling interval.
+ *
+ * Descriptors exist at all levels of USB. For example, there are general
+ * descriptors for every device. The USB device descriptor is described in
+ * usb_dev_descr(9S). Host controllers will look at these descriptors to ensure
+ * that they program the device correctly; however, they are more often used by
+ * client device drivers. There are also descriptors that exist at a class
+ * level. For example, the hub class has a class-specific descriptor which
+ * describes properties of the hub. That information is requested for and used
+ * by the hub driver.
+ *
+ * All of the different descriptors are gathered by the system and placed into a
+ * tree which USBA sometimes calls the 'Configuration Cloud'. Client device
+ * drivers gain access to this cloud and then use them to open endpoints, which
+ * are called pipes in USBA (and some revisions of the USB specification).
+ *
+ * Each pipe gives access to a specific endpoint on the device which can be used
+ * to perform transfers of a specific type and direction. For example, a mass
+ * storage device often has three different endpoints, the default control
+ * endpoint (which every device has), a Bulk-IN endpoint, and a Bulk-OUT
+ * endpoint. The device driver ends up with three open pipes. One to the default
+ * control endpoint to configure the device, and then the other two are used to
+ * perform I/O.
+ *
+ * These routines translate more or less directly into calls to a host
+ * controller driver. A request to open a pipe takes an endpoint descriptor that
+ * describes the properties of the pipe, and the host controller driver (this
+ * driver) goes through and does any work necessary to allow the client device
+ * driver to access it. Once the pipe is open, it either makes one-shot
+ * transfers specific to the transfer type or it starts performing a periodic
+ * poll of an endpoint.
+ *
+ * All of these different actions translate into requests to the host
+ * controller. The host controller driver itself is in charge of making sure
+ * that all of the required resources for polling are allocated with a request
+ * and then proceed to give the driver's periodic callbacks.
+ *
+ * HUBS AND HOST CONTROLLERS
+ *
+ * Every device is always plugged into a hub, even if the device is itself a
+ * hub. This continues until we reach what we call the root-hub. The root-hub is
+ * special in that it is not an actual USB hub, but is integrated into the host
+ * controller and is manipulated in its own way. For example, the host
+ * controller is used to turn on and off a given port's power. This may happen
+ * over any interface, though the most common way is through PCI.
+ *
+ * In addition to the normal character device that exists for a host controller
+ * driver, as part of attaching, the host controller binds to an instance of the
+ * hubd driver. While the root-hub is a bit of a fiction, everyone models the
+ * root-hub as the same as any other hub that's plugged in. The hub kernel
+ * module doesn't know that the hub isn't a physical device that's been plugged
+ * in. The host controller driver simulates that view by taking hub requests
+ * that are made and translating them into corresponding requests that are
+ * understood by the host controller, for example, reading and writing to a
+ * memory mapped register.
+ *
+ * The hub driver polls for changes in device state using an Interrupt-IN
+ * request, which is the same as is done for the root-hub. This allows the host
+ * controller driver to not have to know about the implementation of device hot
+ * plug, merely react to requests from a hub, the same as if it were an external
+ * device. When the hub driver detects a change, it will go through the
+ * corresponding state machine and attach or detach the corresponding client
+ * device driver, depending if the device was inserted or removed.
+ *
+ * We detect the changes for the Interrupt-IN primarily based on the port state
+ * change events that are delivered to the event ring. Whenever any event is
+ * fired, we use this to update the hub driver about _all_ ports with
+ * outstanding events. This more closely matches how a hub is supposed to behave
+ * and leaves things less likely for the hub driver to end up without clearing a
+ * flag on a port.
+ *
+ * PACKET SIZES AND BURSTING
+ *
+ * A given USB endpoint has an explicit packet size and a number of packets that
+ * can be sent per time interval. These concepts are abstracted away from client
+ * device drives usually, though they sometimes inform the upper bounds of what
+ * a device can perform.
+ *
+ * The host controller uses this information to transform arbitrary transfer
+ * requests into USB protocol packets. One of the nice things about the host
+ * controllers is that they abstract away all of the signaling and semantics of
+ * the actual USB protocols, allowing for life to be slightly easier in the
+ * operating system.
+ *
+ * That said, if the host controller is not programmed correctly, these can end
+ * up causing transaction errors and other problems in response to the data that
+ * the host controller is trying to send or receive.
+ *
+ * ------------
+ * Organization
+ * ------------
+ *
+ * The driver is made up of the following files. Many of these have their own
+ * theory statements to describe what they do. Here, we touch on each of the
+ * purpose of each of these files.
+ *
+ * xhci_command.c:	This file contains the logic to issue commands to the
+ * 			controller as well as the actual functions that the
+ * 			other parts of the driver use to cause those commands.
+ *
+ * xhci_context.c:	This file manages various data structures used by the
+ * 			controller to manage the controller's and device's
+ * 			context data structures. See more in the xHCI Overview
+ * 			and General Design for more information.
+ *
+ * xhci_dma.c:		This manages the allocation of DMA memory and DMA
+ * 			attributes for controller, whether memory is for a
+ * 			transfer or something else. This file also deals with
+ * 			all the logic of getting data in and out of DMA buffers.
+ *
+ * xhci_endpoint.c:	This manages all of the logic of handling endpoints or
+ * 			pipes. It deals with endpoint configuration, I/O
+ * 			scheduling, timeouts, and callbacks to USBA.
+ *
+ * xhci_event.c:	This manages callbacks from the hardware to the driver.
+ * 			This covers command completion notifications and I/O
+ * 			notifications.
+ *
+ * xhci_hub.c:		This manages the virtual root-hub. It basically
+ * 			implements and translates all of the USB level requests
+ * 			into xhci specific implements. It also contains the
+ * 			functions to register this hub with USBA.
+ *
+ * xhci_intr.c:		This manages the underlying interrupt allocation,
+ * 			interrupt moderation, and interrupt routines.
+ *
+ * xhci_quirks.c:	This manages information about buggy hardware that's
+ * 			been collected and experienced primarily from other
+ * 			systems.
+ *
+ * xhci_ring.c:		This manages the abstraction of a ring in xhci, which is
+ * 			the primary of communication between the driver and the
+ * 			hardware, whether for the controller or a device.
+ *
+ * xhci_usba.c:		This implements all of the HCDI functions required by
+ * 			USBA. This is the main entry point that drivers and the
+ * 			kernel frameworks will reach to start any operation.
+ * 			Many functions here will end up in the command and
+ * 			endpoint code.
+ *
+ * xhci.c:		This provides the main kernel DDI interfaces and
+ * 			performs device initialization.
+ *
+ * xhci.h:		This is the primary header file which defines
+ * 			illumos-specific data structures and constants to manage
+ * 			the system.
+ *
+ * xhcireg.h:		This header file defines all of the register offsets,
+ * 			masks, and related macros. It also contains all of the
+ * 			constants that are used in various structures as defined
+ * 			by the specification, such as command offsets, etc.
+ *
+ * xhci_ioctl.h:	This contains a few private ioctls that are used by a
+ * 			private debugging command. These are private.
+ *
+ * cmd/xhci/xhci_portsc:	This is a private utility that can be useful for
+ * 				debugging xhci state. It is the only consumer of
+ * 				xhci_ioctl.h and the private ioctls.
+ *
+ * ----------------------------------
+ * xHCI Overview and Structure Layout
+ * ----------------------------------
+ *
+ * The design and structure of this driver follows from the way that the xHCI
+ * specification tells us that we have to work with hardware. First we'll give a
+ * rough summary of how that works, though the xHCI 1.1 specification should be
+ * referenced when going through this.
+ *
+ * There are three primary parts of the hardware -- registers, contexts, and
+ * rings. The registers are memory mapped registers that come in four sets,
+ * though all are found within the first BAR. These are used to program and
+ * control the hardware and aspects of the devices. Beyond more traditional
+ * device programming there are two primary sets of registers that are
+ * important:
+ *
+ *   o Port Status and Control Registers (XHCI_PORTSC)
+ *   o Doorbell Array (XHCI_DOORBELL)
+ *
+ * The port status and control registers are used to get and manipulate the
+ * status of a given device. For example, turning on and off the power to it.
+ * The Doorbell Array is used to kick off I/O operations and start the
+ * processing of an I/O ring.
+ *
+ * The contexts are data structures that represent various pieces of information
+ * in the controller. These contexts are generally filled out by the driver and
+ * then acknowledged and consumed by the hardware. There are controller-wide
+ * contexts (mostly managed in xhci_context.c) that are used to point to the
+ * contexts that exist for each device in the system. The primary context is
+ * called the Device Context Base Address Array (DCBAA).
+ *
+ * Each device in the system is allocated a 'slot', which is used to index into
+ * the DCBAA. Slots are assigned based on issuing commands to the controller.
+ * There are a fixed number of slots that determine the maximum number of
+ * devices that can end up being supported in the system. Note this includes all
+ * the devices plugged into the USB device tree, not just devices plugged into
+ * ports on the chassis.
+ *
+ * For each device, there is a context structure that describes properties of
+ * the device. For example, what speed is the device, is it a hub, etc. The
+ * context has slots for the device and for each endpoint on the device. As
+ * endpoints are enabled, their context information which describes things like
+ * the maximum packet size, is filled in and enabled. The mapping between these
+ * contexts look like:
+ *
+ *
+ *      DCBAA
+ *    +--------+                    Device Context
+ *    | Slot 0 |------------------>+--------------+
+ *    +--------+                   | Slot Context |
+ *    |  ...   |                   +--------------+       +----------+
+ *    +--------+   +------+        |  Endpoint 0  |------>| I/O Ring |
+ *    | Slot n |-->| NULL |        | Context (Bi) |       +----------+
+ *    +--------+   +------+        +--------------+
+ *                                 |  Endpoint 1  |
+ *                                 | Context (Out)|
+ *                                 +--------------+
+ *                                 |  Endpoint 1  |
+ *                                 | Context (In) |
+ *                                 +--------------+
+ *                                 |      ...     |
+ *                                 +--------------+
+ *                                 | Endpoint 15  |
+ *                                 | Context (In) |
+ *                                 +--------------+
+ *
+ * These contexts are always owned by the controller, though we can read them
+ * after various operations complete. Commands that toggle device state use a
+ * specific input context, which is a variant of the device context. The only
+ * difference is that it has an input context structure ahead of it to say which
+ * sections of the device context should be evaluated.
+ *
+ * Each active endpoint points us to an I/O ring, which leads us to the third
+ * main data structure that's used by the device: rings. Rings are made up of
+ * transfer request blocks (TRBs), which are joined together to form a given
+ * transfer description (TD) which represents a single I/O request.
+ *
+ * These rings are used to issue I/O to individual endpoints, to issue commands
+ * to the controller, and to receive notification of changes and completions.
+ * Issued commands go on the special ring called the command ring while the
+ * change and completion notifications go on the event ring.  More details are
+ * available in xhci_ring.c. Each of these structures is represented by an
+ * xhci_ring_t.
+ *
+ * Each ring can be made up of one or more disjoint regions of DMA; however, we
+ * only use a single one. This also impacts some additional registers and
+ * structures that exist. The event ring has an indirection table called the
+ * Event Ring Segment Table (ERST). Each entry in the table (a segment)
+ * describes a chunk of the event ring.
+ *
+ * One other thing worth calling out is the scratchpad. The scratchpad is a way
+ * for the controller to be given arbitrary memory by the OS that it can use.
+ * There are two parts to the scratchpad. The first part is an array whose
+ * entries contain pointers to the actual addresses for the pages. The second
+ * part that we allocate are the actual pages themselves.
+ *
+ * -----------------------------
+ * Endpoint State and Management
+ * -----------------------------
+ *
+ * Endpoint management is one of the key parts to the xhci driver as every
+ * endpoint is a pipe that a device driver uses, so they are our primary
+ * currency. Endpoints are enabled and disabled when the client device drivers
+ * open and close a pipe. When an endpoint is enabled, we have to fill in an
+ * endpoint's context structure with information about the endpoint. These
+ * basically tell the controller important properties which it uses to ensure
+ * that there is adequate bandwidth for the device.
+ *
+ * Each endpoint has its own ring as described in the previous section. We place
+ * TRBs (transfer request blocks) onto a given ring to request I/O be performed.
+ * Responses are placed on the event ring, in other words, the rings associated
+ * with an endpoint are purely for producing I/O.
+ *
+ * Endpoints have a defined state machine as described in xHCI 1.1 / 4.8.3.
+ * These states generally correspond with the state of the endpoint to process
+ * I/O and handle timeouts. The driver basically follows a similar state machine
+ * as described there. There are some deviations. For example, what they
+ * describe as 'running' we break into both the Idle and Running states below.
+ * We also have a notion of timed out and quiescing. The following image
+ * summarizes the states and transitions:
+ *
+ *     +------+                                +-----------+
+ *     | Idle |---------*--------------------->|  Running  |<-+
+ *     +------+         . I/O queued on        +-----------+  |
+ *        ^               ring and timeout        |  |  |     |
+ *        |               scheduled.              |  |  |     |
+ *        |                                       |  |  |     |
+ *        +-----*---------------------------------+  |  |     |
+ *        |     . No I/Os remain                     |  |     |
+ *        |                                          |  |     |
+ *        |                +------*------------------+  |     |
+ *        |                |      . Timeout             |     |
+ *        |                |        fires for           |     |
+ *        |                |        I/O                 |     |
+ *        |                v                            v     |
+ *        |          +-----------+                +--------+  |
+ *        |          | Timed Out |                | Halted |  |
+ *        |          +-----------+                +--------+  |
+ *        |             |                           |         |
+ *        |             |   +-----------+           |         |
+ *        |             +-->| Quiescing |<----------+         |
+ *        |                 +-----------+                     |
+ *        |   No TRBs.           |                . TRBs      |
+ *        |   remain .           |                . Remain    |
+ *        +----------*----<------+-------->-------*-----------+
+ *
+ * Normally, a given endpoint will oscillate between having TRBs scheduled and
+ * not. Every time a new I/O is added to the endpoint, we'll ring the doorbell,
+ * making sure that we're processing the ring, presuming that the endpoint isn't
+ * in one of the error states.
+ *
+ * To detect device hangs, we have an active timeout(9F) per active endpoint
+ * that ticks at a one second rate while we still have TRBs outstanding on an
+ * endpoint. Once all outstanding TRBs have been processed, the timeout will
+ * stop itself and there will be no active checking until the endpoint has I/O
+ * scheduled on it again.
+ *
+ * There are two primary ways that things can go wrong on the endpoint. We can
+ * either have a timeout or an event that transitions the endpoint to the Halted
+ * state. In the halted state, we need to issue explicit commands to reset the
+ * endpoint before removing the I/O.
+ *
+ * The way we handle both a timeout and a halted condition is similar, but the
+ * way they are triggered is different. When we detect a halted condition, we
+ * don't immediately clean it up, and wait for the client device driver (or USBA
+ * on its behalf) to issue a pipe reset. When we detect a timeout, we
+ * immediately take action (assuming no other action is ongoing).
+ *
+ * In both cases, we quiesce the device, which takes care of dealing with taking
+ * the endpoint from whatever state it may be in and taking the appropriate
+ * actions based on the state machine in xHCI 1.1 / 4.8.3. The end of quiescing
+ * leaves the device stopped, which allows us to update the ring's pointer and
+ * remove any TRBs that are causing problems.
+ *
+ * As part of all this, we ensure that we can only be quiescing the device from
+ * a given path at a time. Any requests to schedule I/O during this time will
+ * generally fail.
+ *
+ * The following image describes the state machine for the timeout logic. It
+ * ties into the image above.
+ *
+ *         +----------+                            +---------+
+ *         | Disabled |-----*--------------------->| Enabled |<--+
+ *         +----------+     . TRBs scheduled       +---------+   *. 1 sec timer
+ *             ^              and no active          |  |  |     |  fires and
+ *             |              timer.                 |  |  |     |  another
+ *             |                                     |  |  +--+--+  quiesce, in
+ *             |                                     |  |     |     a bad state,
+ *             +------*------------------------------+  |     ^     or decrement
+ *             |      . 1 sec timer                     |     |     I/O timeout
+ *             |        fires and                       |     |
+ *             |        no TRBs or                      |     +--------------+
+ *             |        endpoint shutdown               |                    |
+ *             |                                        *. . timer counter   |
+ *             ^                                        |    reaches zero    |
+ *             |                                        v                    |
+ *             |                                +--------------+             |
+ *             +-------------*---------------<--| Quiesce ring |->---*-------+
+ *                           . No more          | and fail I/O |     . restart
+ *                             I/Os             +--------------+       timer as
+ *                                                                     more I/Os
+ *
+ * As we described above, when there are active TRBs and I/Os, a 1 second
+ * timeout(9F) will be active. Each second, we decrement a counter on the
+ * current, active I/O until either a new I/O takes the head, or the counter
+ * reaches zero. If the counter reaches zero, then we go through, quiesce the
+ * ring, and then clean things up.
+ *
+ * ------------------
+ * Periodic Endpoints
+ * ------------------
+ *
+ * It's worth calling out periodic endpoints explicitly, as they operate
+ * somewhat differently. Periodic endpoints are limited to Interrupt-IN and
+ * Isochronous-IN. The USBA often uses the term polling for these. That's
+ * because the client only needs to make a single API call; however, they'll
+ * receive multiple callbacks until either an error occurs or polling is
+ * requested to be terminated.
+ *
+ * When we have one of these periodic requests, we end up always rescheduling
+ * I/O requests, as well as, having a specific number of pre-existing I/O
+ * requests to cover the periodic needs, in case of latency spikes. Normally,
+ * when replying to a request, we use the request handle that we were given.
+ * However, when we have a periodic request, we're required to duplicate the
+ * handle before giving them data.
+ *
+ * However, the duplication is a bit tricky. For everything that was duplicated,
+ * the framework expects us to submit data. Because of that we, don't duplicate
+ * them until they are needed. This minimizes the likelihood that we have
+ * outstanding requests to deal with when we encounter a fatal polling failure.
+ *
+ * Most of the polling setup logic happens in xhci_usba.c in
+ * xhci_hcdi_periodic_init(). The consumption and duplication is handled in
+ * xhci_endpoint.c.
+ *
+ * ----------------
+ * Structure Layout
+ * ----------------
+ *
+ * The following images relate the core data structures. The primary structure
+ * in the system is the xhci_t. This is the per-controller data structure that
+ * exists for each instance of the driver. From there, each device in the system
+ * is represented by an xhci_device_t and each endpoint is represented by an
+ * xhci_endpoint_t. For each client that opens a given endpoint, there is an
+ * xhci_pipe_t. For each I/O related ring, there is an xhci_ring_t in the
+ * system.
+ *
+ *     +------------------------+
+ *     | Per-Controller         |
+ *     | Structure              |
+ *     | xhci_t                 |
+ *     |                        |
+ *     | uint_t              ---+--> Capability regs offset
+ *     | uint_t              ---+--> Operational regs offset
+ *     | uint_t              ---+--> Runtime regs offset
+ *     | uint_t              ---+--> Doorbell regs offset
+ *     | xhci_state_flags_t  ---+--> Device state flags
+ *     | xhci_quirks_t       ---+--> Device quirk flags
+ *     | xhci_capability_t   ---+--> Controller capability structure
+ *     | xhci_dcbaa_t        ---+----------------------------------+
+ *     | xhci_scratchpad_t   ---+---------+                        |
+ *     | xhci_command_ing_t  ---+------+  |                        v
+ *     | xhci_event_ring_t   ---+----+ |  |              +---------------------+
+ *     | xhci_usba_t         ---+--+ | |  |              | Device Context      |
+ *     +------------------------+  | | |  |              | Base Address        |
+ *                                 | | |  |              | Array Structure     |
+ *                                 | | |  |              | xhci_dcbaa_t        |
+ * +-------------------------------+ | |  |              |                     |
+ * | +-------------------------------+ |  |  DCBAA KVA <-+--        uint64_t * |
+ * | |    +----------------------------+  | DMA Buffer <-+-- xhci_dma_buffer_t |
+ * | |    v                               |              +---------------------+
+ * | | +--------------------------+       +-----------------------+
+ * | | | Event Ring               |                               |
+ * | | | Management               |                               |
+ * | | | xhci_event_ring_t        |                               v
+ * | | |                          |   Event Ring        +----------------------+
+ * | | | xhci_event_segment_t * --|-> Segment VA        |   Scratchpad (Extra  |
+ * | | | xhci_dma_buffer_t      --|-> Segment DMA Buf.  |   Controller Memory) |
+ * | | | xhci_ring_t            --|--+                  |    xhci_scratchpad_t |
+ * | | +--------------------------+  |      Scratchpad  |                      |
+ * | |                               | Base Array KVA <-+-          uint64_t * |
+ * | +------------+                  | Array DMA Buf. <-+-   xhci_dma_buffer_t |
+ * |              v                  | Scratchpad DMA <-+- xhci_dma_buffer_t * |
+ * |   +---------------------------+ | Buffer per page  +----------------------+
+ * |   | Command Ring              | |
+ * |   | xhci_command_ring_t       | +------------------------------+
+ * |   |                           |                                |
+ * |   | xhci_ring_t             --+-> Command Ring --->------------+
+ * |   | list_t                  --+-> Command List                 v
+ * |   | timeout_id_t            --+-> Timeout State     +---------------------+
+ * |   | xhci_command_ring_state_t +-> State Flags       | I/O Ring            |
+ * |   +---------------------------+                     | xhci_ring_t         |
+ * |                                                     |                     |
+ * |                                     Ring DMA Buf. <-+-- xhci_dma_buffer_t |
+ * |                                       Ring Length <-+--            uint_t |
+ * |                                    Ring Entry KVA <-+--      xhci_trb_t * |
+ * |    +---------------------------+        Ring Head <-+--            uint_t |
+ * +--->| USBA State                |        Ring Tail <-+--            uint_t |
+ *      | xhci_usba_t               |       Ring Cycle <-+--            uint_t |
+ *      |                           |                    +---------------------+
+ *      | usba_hcdi_ops_t *        -+-> USBA Ops Vector                       ^
+ *      | usb_dev_dscr_t           -+-> USB Virtual Device Descriptor         |
+ *      | usb_ss_hub_descr_t       -+-> USB Virtual Hub Descriptor            |
+ *      | usba_pipe_handle_data_t * +-> Interrupt polling client              |
+ *      | usb_intr_req_t           -+-> Interrupt polling request             |
+ *      | uint32_t                --+-> Interrupt polling device mask         |
+ *      | list_t                  --+-> Pipe List (Active Users)              |
+ *      | list_t                  --+-------------------+                     |
+ *      +---------------------------+                   |                     ^
+ *                                                      |                     |
+ *                                                      v                     |
+ *     +-------------------------------+             +---------------+        |
+ *     | USB Device                    |------------>| USB Device    |--> ... |
+ *     | xhci_device_t                 |             | xhci_device_t |        |
+ *     |                               |             +---------------+        |
+ *     | usb_port_t                  --+-> USB Port plugged into              |
+ *     | uint8_t                     --+-> Slot Number                        |
+ *     | boolean_t                   --+-> Address Assigned                   |
+ *     | usba_device_t *             --+-> USBA Device State                  |
+ *     | xhci_dma_buffer_t           --+-> Input Context DMA Buffer           |
+ *     | xhci_input_context_t *      --+-> Input Context KVA                  |
+ *     | xhci_slot_contex_t *        --+-> Input Slot Context KVA             |
+ *     | xhci_endpoint_context_t *[] --+-> Input Endpoint Context KVA         |
+ *     | xhci_dma_buffer_t           --+-> Output Context DMA Buffer          |
+ *     | xhci_slot_context_t *       --+-> Output Slot Context KVA            ^
+ *     | xhci_endpoint_context_t *[] --+-> Output Endpoint Context KVA        |
+ *     | xhci_endpoint_t *[]         --+-> Endpoint Tracking ---+             |
+ *     +-------------------------------+                        |             |
+ *                                                              |             |
+ *                                                              v             |
+ *     +------------------------------+            +-----------------+        |
+ *     | Endpoint Data                |----------->| Endpoint Data   |--> ... |
+ *     | xhci_endpoint_t              |            | xhci_endpoint_t |        |
+ *     |                              |            +-----------------+        |
+ *     | int                        --+-> Endpoint Number                     |
+ *     | int                        --+-> Endpoint Type                       |
+ *     | xhci_endpoint_state_t      --+-> Endpoint State                      |
+ *     | timeout_id_t               --+-> Endpoint Timeout State              |
+ *     | usba_pipe_handle_data_t *  --+-> USBA Client Handle                  |
+ *     | xhci_ring_t                --+-> Endpoint I/O Ring  -------->--------+
+ *     | list_t                     --+-> Transfer List --------+
+ *     +------------------------------+                         |
+ *                                                              v
+ *     +-------------------------+                  +--------------------+
+ *     | Transfer Structure      |----------------->| Transfer Structure |-> ...
+ *     | xhci_transfer_t         |                  | xhci_transfer_t    |
+ *     |                         |                  +--------------------+
+ *     | xhci_dma_buffer_t     --+-> I/O DMA Buffer
+ *     | uint_t                --+-> Number of TRBs
+ *     | uint_t                --+-> Short transfer data
+ *     | uint_t                --+-> Timeout seconds remaining
+ *     | usb_cr_t              --+-> USB Transfer return value
+ *     | boolean_t             --+-> Data direction
+ *     | xhci_trb_t *          --+-> Host-order transfer requests for I/O
+ *     | usb_isoc_pkt_descr_t * -+-> Isochronous only response data
+ *     | usb_opaque_t          --+-> USBA Request Handle
+ *     +-------------------------+
+ *
+ * -------------
+ * Lock Ordering
+ * -------------
+ *
+ * There are three different tiers of locks that exist in the driver. First,
+ * there is a lock for each controller: xhci_t`xhci_lock. This protects all the
+ * data for that instance of the controller. If there are multiple instances of
+ * the xHCI controller in the system, each one is independent and protected
+ * separately. The two do not share any data.
+ *
+ * From there, there are two other, specific locks in the system:
+ *
+ *   o xhci_command_ring_t`xcr_lock
+ *   o xhci_device_t`xd_imtx
+ *
+ * There is only one xcr_lock per controller, like the xhci_lock. It protects
+ * the state of the command ring. However, there is on xd_imtx per device.
+ * Recall that each device is scoped to a given controller. This protects the
+ * input slot context for a given device.
+ *
+ * There are a few important rules to keep in mind here that are true
+ * universally throughout the driver:
+ *
+ * 1) Always grab the xhci_t`xhci_lock, before grabbing any of the other locks.
+ * 2) A given xhci_device_t`xd_imtx, must be taken before grabbing the
+ *    xhci_command_ring_t`xcr_lock.
+ * 3) A given thread can only hold one of the given xhci_device_t`xd_imtx locks
+ *    at a given time. In other words, we should never be manipulating the input
+ *    context of two different devices at once.
+ * 4) It is safe to hold the xhci_device_t`xd_imtx while tearing down the
+ *    endpoint timer. Conversely, the endpoint specific logic should never enter
+ *    this lock.
+ *
+ * --------------------
+ * Relationship to EHCI
+ * --------------------
+ *
+ * On some Intel chipsets, a given physical port on the system may be routed to
+ * one of the EHCI or xHCI controllers. This association can be dynamically
+ * changed by writing to platform specific registers as handled by the quirk
+ * logic in xhci_quirk.c.
+ *
+ * As these ports may support USB 3.x speeds, we always route all such ports to
+ * the xHCI controller, when supported. In addition, to minimize disruptions
+ * from devices being enumerated and attached to the EHCI driver and then
+ * disappearing, we generally attempt to load the xHCI controller before the
+ * EHCI controller. This logic is not done in the driver; however, it is done in
+ * other parts of the kernel like in uts/common/io/consconfig_dacf.c in the
+ * function consconfig_load_drivres().
+ *
+ * -----------
+ * Future Work
+ * -----------
+ *
+ * The primary future work in this driver spans two different, but related
+ * areas. The first area is around controller resets and how they tie into FM.
+ * Presently, we do not have a good way to handle controllers coming and going
+ * in the broader USB stack or properly reconfigure the device after a reset.
+ * Secondly, we don't handle the suspend and resume of devices and drivers.
+ */
+
+#include <sys/param.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/cmn_err.h>
+#include <sys/ddifm.h>
+#include <sys/pci.h>
+#include <sys/class.h>
+#include <sys/policy.h>
+
+#include <sys/usb/hcd/xhci/xhci.h>
+#include <sys/usb/hcd/xhci/xhci_ioctl.h>
+
+/*
+ * We want to use the first BAR to access its registers. The regs[] array is
+ * ordered based on the rules for the PCI supplement to IEEE 1275. So regs[1]
+ * will always be the first BAR.
+ */
+#define	XHCI_REG_NUMBER	1
+
+/*
+ * This task queue exists as a global taskq that is used for resetting the
+ * device in the face of FM or runtime errors. Each instance of the device
+ * (xhci_t) happens to have a single taskq_dispatch_ent already allocated so we
+ * know that we should always be able to dispatch such an event.
+ */
+static taskq_t *xhci_taskq;
+
+/*
+ * Global soft state for per-instance data. Note that we must use the soft state
+ * routines and cannot use the ddi_set_driver_private() routines. The USB
+ * framework presumes that it can use the dip's private data.
+ */
+void *xhci_soft_state;
+
+/*
+ * This is the time in us that we wait after a controller resets before we
+ * consider reading any register. There are some controllers that want at least
+ * 1 ms, therefore we default to 10 ms.
+ */
+clock_t xhci_reset_delay = 10000;
+
+void
+xhci_error(xhci_t *xhcip, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	if (xhcip != NULL && xhcip->xhci_dip != NULL) {
+		vdev_err(xhcip->xhci_dip, CE_WARN, fmt, ap);
+	} else {
+		vcmn_err(CE_WARN, fmt, ap);
+	}
+	va_end(ap);
+}
+
+void
+xhci_log(xhci_t *xhcip, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	if (xhcip != NULL && xhcip->xhci_dip != NULL) {
+		vdev_err(xhcip->xhci_dip, CE_NOTE, fmt, ap);
+	} else {
+		vcmn_err(CE_NOTE, fmt, ap);
+	}
+	va_end(ap);
+}
+
+/*
+ * USBA is in charge of creating device nodes for us. USBA explicitly ORs in the
+ * constant HUBD_IS_ROOT_HUB, so we have to undo that when we're looking at
+ * things here. A simple bitwise-and will take care of this. And hey, it could
+ * always be more complex, USBA could clone!
+ */
+static dev_info_t *
+xhci_get_dip(dev_t dev)
+{
+	xhci_t *xhcip;
+	int instance = getminor(dev) & ~HUBD_IS_ROOT_HUB;
+
+	xhcip = ddi_get_soft_state(xhci_soft_state, instance);
+	if (xhcip != NULL)
+		return (xhcip->xhci_dip);
+	return (NULL);
+}
+
+uint8_t
+xhci_get8(xhci_t *xhcip, xhci_reg_type_t rtt, uintptr_t off)
+{
+	uintptr_t addr, roff;
+
+	switch (rtt) {
+	case XHCI_R_CAP:
+		roff = xhcip->xhci_regs_capoff;
+		break;
+	case XHCI_R_OPER:
+		roff = xhcip->xhci_regs_operoff;
+		break;
+	case XHCI_R_RUN:
+		roff = xhcip->xhci_regs_runoff;
+		break;
+	case XHCI_R_DOOR:
+		roff = xhcip->xhci_regs_dooroff;
+		break;
+	default:
+		panic("called %s with bad reg type: %d", __func__, rtt);
+	}
+	ASSERT(roff != PCI_EINVAL32);
+	addr = roff + off + (uintptr_t)xhcip->xhci_regs_base;
+
+	return (ddi_get8(xhcip->xhci_regs_handle, (void *)addr));
+}
+
+uint16_t
+xhci_get16(xhci_t *xhcip, xhci_reg_type_t rtt, uintptr_t off)
+{
+	uintptr_t addr, roff;
+
+	switch (rtt) {
+	case XHCI_R_CAP:
+		roff = xhcip->xhci_regs_capoff;
+		break;
+	case XHCI_R_OPER:
+		roff = xhcip->xhci_regs_operoff;
+		break;
+	case XHCI_R_RUN:
+		roff = xhcip->xhci_regs_runoff;
+		break;
+	case XHCI_R_DOOR:
+		roff = xhcip->xhci_regs_dooroff;
+		break;
+	default:
+		panic("called %s with bad reg type: %d", __func__, rtt);
+	}
+	ASSERT(roff != PCI_EINVAL32);
+	addr = roff + off + (uintptr_t)xhcip->xhci_regs_base;
+
+	return (ddi_get16(xhcip->xhci_regs_handle, (void *)addr));
+}
+
+uint32_t
+xhci_get32(xhci_t *xhcip, xhci_reg_type_t rtt, uintptr_t off)
+{
+	uintptr_t addr, roff;
+
+	switch (rtt) {
+	case XHCI_R_CAP:
+		roff = xhcip->xhci_regs_capoff;
+		break;
+	case XHCI_R_OPER:
+		roff = xhcip->xhci_regs_operoff;
+		break;
+	case XHCI_R_RUN:
+		roff = xhcip->xhci_regs_runoff;
+		break;
+	case XHCI_R_DOOR:
+		roff = xhcip->xhci_regs_dooroff;
+		break;
+	default:
+		panic("called %s with bad reg type: %d", __func__, rtt);
+	}
+	ASSERT(roff != PCI_EINVAL32);
+	addr = roff + off + (uintptr_t)xhcip->xhci_regs_base;
+
+	return (ddi_get32(xhcip->xhci_regs_handle, (void *)addr));
+}
+
+uint64_t
+xhci_get64(xhci_t *xhcip, xhci_reg_type_t rtt, uintptr_t off)
+{
+	uintptr_t addr, roff;
+
+	switch (rtt) {
+	case XHCI_R_CAP:
+		roff = xhcip->xhci_regs_capoff;
+		break;
+	case XHCI_R_OPER:
+		roff = xhcip->xhci_regs_operoff;
+		break;
+	case XHCI_R_RUN:
+		roff = xhcip->xhci_regs_runoff;
+		break;
+	case XHCI_R_DOOR:
+		roff = xhcip->xhci_regs_dooroff;
+		break;
+	default:
+		panic("called %s with bad reg type: %d", __func__, rtt);
+	}
+	ASSERT(roff != PCI_EINVAL32);
+	addr = roff + off + (uintptr_t)xhcip->xhci_regs_base;
+
+	return (ddi_get64(xhcip->xhci_regs_handle, (void *)addr));
+}
+
+void
+xhci_put8(xhci_t *xhcip, xhci_reg_type_t rtt, uintptr_t off, uint8_t val)
+{
+	uintptr_t addr, roff;
+
+	switch (rtt) {
+	case XHCI_R_CAP:
+		roff = xhcip->xhci_regs_capoff;
+		break;
+	case XHCI_R_OPER:
+		roff = xhcip->xhci_regs_operoff;
+		break;
+	case XHCI_R_RUN:
+		roff = xhcip->xhci_regs_runoff;
+		break;
+	case XHCI_R_DOOR:
+		roff = xhcip->xhci_regs_dooroff;
+		break;
+	default:
+		panic("called %s with bad reg type: %d", __func__, rtt);
+	}
+	ASSERT(roff != PCI_EINVAL32);
+	addr = roff + off + (uintptr_t)xhcip->xhci_regs_base;
+
+	ddi_put8(xhcip->xhci_regs_handle, (void *)addr, val);
+}
+
+void
+xhci_put16(xhci_t *xhcip, xhci_reg_type_t rtt, uintptr_t off, uint16_t val)
+{
+	uintptr_t addr, roff;
+
+	switch (rtt) {
+	case XHCI_R_CAP:
+		roff = xhcip->xhci_regs_capoff;
+		break;
+	case XHCI_R_OPER:
+		roff = xhcip->xhci_regs_operoff;
+		break;
+	case XHCI_R_RUN:
+		roff = xhcip->xhci_regs_runoff;
+		break;
+	case XHCI_R_DOOR:
+		roff = xhcip->xhci_regs_dooroff;
+		break;
+	default:
+		panic("called %s with bad reg type: %d", __func__, rtt);
+	}
+	ASSERT(roff != PCI_EINVAL32);
+	addr = roff + off + (uintptr_t)xhcip->xhci_regs_base;
+
+	ddi_put16(xhcip->xhci_regs_handle, (void *)addr, val);
+}
+
+void
+xhci_put32(xhci_t *xhcip, xhci_reg_type_t rtt, uintptr_t off, uint32_t val)
+{
+	uintptr_t addr, roff;
+
+	switch (rtt) {
+	case XHCI_R_CAP:
+		roff = xhcip->xhci_regs_capoff;
+		break;
+	case XHCI_R_OPER:
+		roff = xhcip->xhci_regs_operoff;
+		break;
+	case XHCI_R_RUN:
+		roff = xhcip->xhci_regs_runoff;
+		break;
+	case XHCI_R_DOOR:
+		roff = xhcip->xhci_regs_dooroff;
+		break;
+	default:
+		panic("called %s with bad reg type: %d", __func__, rtt);
+	}
+	ASSERT(roff != PCI_EINVAL32);
+	addr = roff + off + (uintptr_t)xhcip->xhci_regs_base;
+
+	ddi_put32(xhcip->xhci_regs_handle, (void *)addr, val);
+}
+
+void
+xhci_put64(xhci_t *xhcip, xhci_reg_type_t rtt, uintptr_t off, uint64_t val)
+{
+	uintptr_t addr, roff;
+
+	switch (rtt) {
+	case XHCI_R_CAP:
+		roff = xhcip->xhci_regs_capoff;
+		break;
+	case XHCI_R_OPER:
+		roff = xhcip->xhci_regs_operoff;
+		break;
+	case XHCI_R_RUN:
+		roff = xhcip->xhci_regs_runoff;
+		break;
+	case XHCI_R_DOOR:
+		roff = xhcip->xhci_regs_dooroff;
+		break;
+	default:
+		panic("called %s with bad reg type: %d", __func__, rtt);
+	}
+	ASSERT(roff != PCI_EINVAL32);
+	addr = roff + off + (uintptr_t)xhcip->xhci_regs_base;
+
+	ddi_put64(xhcip->xhci_regs_handle, (void *)addr, val);
+}
+
+int
+xhci_check_regs_acc(xhci_t *xhcip)
+{
+	ddi_fm_error_t de;
+
+	/*
+	 * Treat the case where we can't check as fine so we can treat the code
+	 * more simply.
+	 */
+	if (!DDI_FM_ACC_ERR_CAP(xhcip->xhci_fm_caps))
+		return (DDI_FM_OK);
+
+	ddi_fm_acc_err_get(xhcip->xhci_regs_handle, &de, DDI_FME_VERSION);
+	ddi_fm_acc_err_clear(xhcip->xhci_regs_handle, DDI_FME_VERSION);
+	return (de.fme_status);
+}
+
+/*
+ * As a leaf PCIe driver, we just post the ereport and continue on.
+ */
+/* ARGSUSED */
+static int
+xhci_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
+{
+	pci_ereport_post(dip, err, NULL);
+	return (err->fme_status);
+}
+
+static void
+xhci_fm_fini(xhci_t *xhcip)
+{
+	if (xhcip->xhci_fm_caps == 0)
+		return;
+
+	if (DDI_FM_ERRCB_CAP(xhcip->xhci_fm_caps))
+		ddi_fm_handler_unregister(xhcip->xhci_dip);
+
+	if (DDI_FM_EREPORT_CAP(xhcip->xhci_fm_caps) ||
+	    DDI_FM_ERRCB_CAP(xhcip->xhci_fm_caps))
+		pci_ereport_teardown(xhcip->xhci_dip);
+
+	ddi_fm_fini(xhcip->xhci_dip);
+}
+
+static void
+xhci_fm_init(xhci_t *xhcip)
+{
+	ddi_iblock_cookie_t iblk;
+	int def = DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
+	    DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE;
+
+	xhcip->xhci_fm_caps = ddi_prop_get_int(DDI_DEV_T_ANY, xhcip->xhci_dip,
+	    DDI_PROP_DONTPASS, "fm_capable", def);
+
+	if (xhcip->xhci_fm_caps < 0) {
+		xhcip->xhci_fm_caps = 0;
+	} else if (xhcip->xhci_fm_caps & ~def) {
+		xhcip->xhci_fm_caps &= def;
+	}
+
+	if (xhcip->xhci_fm_caps == 0)
+		return;
+
+	ddi_fm_init(xhcip->xhci_dip, &xhcip->xhci_fm_caps, &iblk);
+	if (DDI_FM_EREPORT_CAP(xhcip->xhci_fm_caps) ||
+	    DDI_FM_ERRCB_CAP(xhcip->xhci_fm_caps)) {
+		pci_ereport_setup(xhcip->xhci_dip);
+	}
+
+	if (DDI_FM_ERRCB_CAP(xhcip->xhci_fm_caps)) {
+		ddi_fm_handler_register(xhcip->xhci_dip,
+		    xhci_fm_error_cb, xhcip);
+	}
+}
+
+static int
+xhci_reg_poll(xhci_t *xhcip, xhci_reg_type_t rt, int reg, uint32_t mask,
+    uint32_t targ, uint_t tries, int delay_ms)
+{
+	uint_t i;
+
+	for (i = 0; i < tries; i++) {
+		uint32_t val = xhci_get32(xhcip, rt, reg);
+		if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+			ddi_fm_service_impact(xhcip->xhci_dip,
+			    DDI_SERVICE_LOST);
+			return (EIO);
+		}
+
+		if ((val & mask) == targ)
+			return (0);
+
+		delay(drv_usectohz(delay_ms * 1000));
+	}
+	return (ETIMEDOUT);
+}
+
+static boolean_t
+xhci_regs_map(xhci_t *xhcip)
+{
+	off_t memsize;
+	int ret;
+	ddi_device_acc_attr_t da;
+
+	if (ddi_dev_regsize(xhcip->xhci_dip, XHCI_REG_NUMBER, &memsize) !=
+	    DDI_SUCCESS) {
+		xhci_error(xhcip, "failed to get register set size");
+		return (B_FALSE);
+	}
+
+	bzero(&da, sizeof (ddi_device_acc_attr_t));
+	da.devacc_attr_version = DDI_DEVICE_ATTR_V0;
+	da.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
+	da.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
+	if (DDI_FM_ACC_ERR_CAP(xhcip->xhci_fm_caps)) {
+		da.devacc_attr_access = DDI_FLAGERR_ACC;
+	} else {
+		da.devacc_attr_access = DDI_DEFAULT_ACC;
+	}
+
+	ret = ddi_regs_map_setup(xhcip->xhci_dip, XHCI_REG_NUMBER,
+	    &xhcip->xhci_regs_base, 0, memsize, &da, &xhcip->xhci_regs_handle);
+
+	if (ret != DDI_SUCCESS) {
+		xhci_error(xhcip, "failed to map device registers: %d", ret);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+static boolean_t
+xhci_regs_init(xhci_t *xhcip)
+{
+	/*
+	 * The capabilities always begin at offset zero.
+	 */
+	xhcip->xhci_regs_capoff = 0;
+	xhcip->xhci_regs_operoff = xhci_get8(xhcip, XHCI_R_CAP, XHCI_CAPLENGTH);
+	xhcip->xhci_regs_runoff = xhci_get32(xhcip, XHCI_R_CAP, XHCI_RTSOFF);
+	xhcip->xhci_regs_runoff &= ~0x1f;
+	xhcip->xhci_regs_dooroff = xhci_get32(xhcip, XHCI_R_CAP, XHCI_DBOFF);
+	xhcip->xhci_regs_dooroff &= ~0x3;
+
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to initialize controller register "
+		    "offsets: encountered FM register error");
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+/*
+ * Read various parameters from PCI configuration space and from the Capability
+ * registers that we'll need to register the device. We cache all of the
+ * Capability registers.
+ */
+static boolean_t
+xhci_read_params(xhci_t *xhcip)
+{
+	uint8_t usb;
+	uint16_t vers;
+	uint32_t struc1, struc2, struc3, cap1, cap2, pgsz;
+	uint32_t psize, pbit;
+	xhci_capability_t *xcap;
+	unsigned long ps;
+
+	usb = pci_config_get8(xhcip->xhci_cfg_handle, PCI_XHCI_USBREV);
+	vers = xhci_get16(xhcip, XHCI_R_CAP, XHCI_HCIVERSION);
+	struc1 = xhci_get32(xhcip, XHCI_R_CAP, XHCI_HCSPARAMS1);
+	struc2 = xhci_get32(xhcip, XHCI_R_CAP, XHCI_HCSPARAMS2);
+	struc3 = xhci_get32(xhcip, XHCI_R_CAP, XHCI_HCSPARAMS3);
+	cap1 = xhci_get32(xhcip, XHCI_R_CAP, XHCI_HCCPARAMS1);
+	cap2 = xhci_get32(xhcip, XHCI_R_CAP, XHCI_HCCPARAMS2);
+	pgsz = xhci_get32(xhcip, XHCI_R_OPER, XHCI_PAGESIZE);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to read controller parameters: "
+		    "encountered FM register error");
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (B_FALSE);
+	}
+
+	xcap = &xhcip->xhci_caps;
+	xcap->xcap_usb_vers = usb;
+	xcap->xcap_hci_vers = vers;
+	xcap->xcap_max_slots = XHCI_HCS1_DEVSLOT_MAX(struc1);
+	xcap->xcap_max_intrs = XHCI_HCS1_IRQ_MAX(struc1);
+	xcap->xcap_max_ports = XHCI_HCS1_N_PORTS(struc1);
+	if (xcap->xcap_max_ports > MAX_PORTS) {
+		xhci_error(xhcip, "Root hub has %d ports, but system only "
+		    "supports %d, limiting to %d\n", xcap->xcap_max_ports,
+		    MAX_PORTS, MAX_PORTS);
+		xcap->xcap_max_ports = MAX_PORTS;
+	}
+
+	xcap->xcap_ist_micro = XHCI_HCS2_IST_MICRO(struc2);
+	xcap->xcap_ist = XHCI_HCS2_IST(struc2);
+	xcap->xcap_max_esrt = XHCI_HCS2_ERST_MAX(struc2);
+	xcap->xcap_scratch_restore = XHCI_HCS2_SPR(struc2);
+	xcap->xcap_max_scratch = XHCI_HCS2_SPB_MAX(struc2);
+
+	xcap->xcap_u1_lat = XHCI_HCS3_U1_DEL(struc3);
+	xcap->xcap_u2_lat = XHCI_HCS3_U2_DEL(struc3);
+
+	xcap->xcap_flags = XHCI_HCC1_FLAGS_MASK(cap1);
+	xcap->xcap_max_psa = XHCI_HCC1_PSA_SZ_MAX(cap1);
+	xcap->xcap_xecp_off = XHCI_HCC1_XECP(cap1);
+	xcap->xcap_flags2 = XHCI_HCC2_FLAGS_MASK(cap2);
+
+	/*
+	 * We don't have documentation for what changed from before xHCI 0.96,
+	 * so we just refuse to support versions before 0.96. We also will
+	 * ignore anything with a major version greater than 1.
+	 */
+	if (xcap->xcap_hci_vers < 0x96 || xcap->xcap_hci_vers >= 0x200) {
+		xhci_error(xhcip, "Encountered unsupported xHCI version 0.%2x",
+		    xcap->xcap_hci_vers);
+		return (B_FALSE);
+	}
+
+	/*
+	 * Determine the smallest size page that the controller supports and
+	 * make sure that it matches our pagesize. We basically check here for
+	 * the presence of 4k and 8k pages. The basis of the pagesize is used
+	 * extensively throughout the code and specification. While we could
+	 * support other page sizes here, given that we don't support systems
+	 * with it at this time, it doesn't make much sense.
+	 */
+	ps = PAGESIZE;
+	if (ps == 0x1000) {
+		pbit = XHCI_PAGESIZE_4K;
+		psize = 0x1000;
+	} else if (ps == 0x2000) {
+		pbit = XHCI_PAGESIZE_8K;
+		psize = 0x2000;
+	} else {
+		xhci_error(xhcip, "Encountered host page size that the driver "
+		    "doesn't know how to handle: %lx\n", ps);
+		return (B_FALSE);
+	}
+
+	if (!(pgsz & pbit)) {
+		xhci_error(xhcip, "Encountered controller that didn't support "
+		    "the host page size (%d), supports: %x", psize, pgsz);
+		return (B_FALSE);
+	}
+	xcap->xcap_pagesize = psize;
+
+	return (B_TRUE);
+}
+
+/*
+ * Apply known workarounds and issues. These reports come from other
+ * Operating Systems and have been collected over time.
+ */
+static boolean_t
+xhci_identify(xhci_t *xhcip)
+{
+	xhci_quirks_populate(xhcip);
+
+	if (xhcip->xhci_quirks & XHCI_QUIRK_NO_MSI) {
+		xhcip->xhci_caps.xcap_intr_types = DDI_INTR_TYPE_FIXED;
+	} else {
+		xhcip->xhci_caps.xcap_intr_types = DDI_INTR_TYPE_FIXED |
+		    DDI_INTR_TYPE_MSI | DDI_INTR_TYPE_MSIX;
+	}
+
+	if (xhcip->xhci_quirks & XHCI_QUIRK_32_ONLY) {
+		xhcip->xhci_caps.xcap_flags &= ~XCAP_AC64;
+	}
+
+	return (B_TRUE);
+}
+
+static boolean_t
+xhci_alloc_intr_handle(xhci_t *xhcip, int type)
+{
+	int ret;
+
+	/*
+	 * Normally a well-behaving driver would more carefully request an
+	 * amount of interrupts based on the number available, etc. But since we
+	 * only actually want a single interrupt, we're just going to go ahead
+	 * and ask for a single interrupt.
+	 */
+	ret = ddi_intr_alloc(xhcip->xhci_dip, &xhcip->xhci_intr_hdl, type, 0,
+	    XHCI_NINTR, &xhcip->xhci_intr_num, DDI_INTR_ALLOC_NORMAL);
+	if (ret != DDI_SUCCESS) {
+		xhci_log(xhcip, "!failed to allocate interrupts of type %d: %d",
+		    type, ret);
+		return (B_FALSE);
+	}
+	xhcip->xhci_intr_type = type;
+
+	return (B_TRUE);
+}
+
+static boolean_t
+xhci_alloc_intrs(xhci_t *xhcip)
+{
+	int intr_types, ret;
+
+	if (XHCI_NINTR > xhcip->xhci_caps.xcap_max_intrs) {
+		xhci_error(xhcip, "controller does not support the minimum "
+		    "number of interrupts required (%d), supports %d",
+		    XHCI_NINTR, xhcip->xhci_caps.xcap_max_intrs);
+		return (B_FALSE);
+	}
+
+	if ((ret = ddi_intr_get_supported_types(xhcip->xhci_dip,
+	    &intr_types)) != DDI_SUCCESS) {
+		xhci_error(xhcip, "failed to get supported interrupt types: "
+		    "%d", ret);
+		return (B_FALSE);
+	}
+
+	/*
+	 * Mask off interrupt types we've already ruled out due to quirks or
+	 * other reasons.
+	 */
+	intr_types &= xhcip->xhci_caps.xcap_intr_types;
+	if (intr_types & DDI_INTR_TYPE_MSIX) {
+		if (xhci_alloc_intr_handle(xhcip, DDI_INTR_TYPE_MSIX))
+			return (B_TRUE);
+	}
+
+	if (intr_types & DDI_INTR_TYPE_MSI) {
+		if (xhci_alloc_intr_handle(xhcip, DDI_INTR_TYPE_MSI))
+			return (B_TRUE);
+	}
+
+	if (intr_types & DDI_INTR_TYPE_FIXED) {
+		if (xhci_alloc_intr_handle(xhcip, DDI_INTR_TYPE_FIXED))
+			return (B_TRUE);
+	}
+
+	xhci_error(xhcip, "failed to allocate an interrupt, supported types: "
+	    "0x%x", intr_types);
+	return (B_FALSE);
+}
+
+static boolean_t
+xhci_add_intr_handler(xhci_t *xhcip)
+{
+	int ret;
+
+	if ((ret = ddi_intr_get_pri(xhcip->xhci_intr_hdl,
+	    &xhcip->xhci_intr_pri)) != DDI_SUCCESS) {
+		xhci_error(xhcip, "failed to get interrupt priority: %d", ret);
+		return (B_FALSE);
+	}
+
+	if ((ret = ddi_intr_get_cap(xhcip->xhci_intr_hdl,
+	    &xhcip->xhci_intr_caps)) != DDI_SUCCESS) {
+		xhci_error(xhcip, "failed to get interrupt capabilities: %d",
+		    ret);
+		return (B_FALSE);
+	}
+
+	if ((ret = ddi_intr_add_handler(xhcip->xhci_intr_hdl, xhci_intr, xhcip,
+	    (uintptr_t)0)) != DDI_SUCCESS) {
+		xhci_error(xhcip, "failed to add interrupt handler: %d", ret);
+		return (B_FALSE);
+	}
+	return (B_TRUE);
+}
+
+/*
+ * Find a capability with an identifier whose value is 'id'. The 'init' argument
+ * gives us the offset to start searching at. See xHCI 1.1 / 7 for more
+ * information. This is more or less exactly like PCI capabilities.
+ */
+static boolean_t
+xhci_find_ext_cap(xhci_t *xhcip, uint32_t id, uint32_t init, uint32_t *outp)
+{
+	uint32_t off;
+	uint8_t next = 0;
+
+	/*
+	 * If we have no offset, we're done.
+	 */
+	if (xhcip->xhci_caps.xcap_xecp_off == 0)
+		return (B_FALSE);
+
+	off = xhcip->xhci_caps.xcap_xecp_off << 2;
+	do {
+		uint32_t cap_hdr;
+
+		off += next << 2;
+		cap_hdr = xhci_get32(xhcip, XHCI_R_CAP, off);
+		if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+			xhci_error(xhcip, "failed to read xhci extended "
+			    "capabilities at offset 0x%x: encountered FM "
+			    "register error", off);
+			ddi_fm_service_impact(xhcip->xhci_dip,
+			    DDI_SERVICE_LOST);
+			break;
+		}
+
+		if (cap_hdr == PCI_EINVAL32)
+			break;
+		if (XHCI_XECP_ID(cap_hdr) == id &&
+		    (init == UINT32_MAX || off > init)) {
+			*outp = off;
+			return (B_TRUE);
+		}
+		next = XHCI_XECP_NEXT(cap_hdr);
+		/*
+		 * Watch out for overflow if we somehow end up with a more than
+		 * 2 GiB space.
+		 */
+		if (next << 2 > (INT32_MAX - off))
+			return (B_FALSE);
+	} while (next != 0);
+
+	return (B_FALSE);
+}
+
+/*
+ * For mostly information purposes, we'd like to walk to augment the devinfo
+ * tree with the number of ports that support USB 2 and USB 3. Note though that
+ * these ports may be overlapping. Many ports can support both USB 2 and USB 3
+ * and are wired up to the same physical port, even though they show up as
+ * separate 'ports' in the xhci sense.
+ */
+static boolean_t
+xhci_port_count(xhci_t *xhcip)
+{
+	uint_t nusb2 = 0, nusb3 = 0;
+	uint32_t off = UINT32_MAX;
+
+	while (xhci_find_ext_cap(xhcip, XHCI_ID_PROTOCOLS, off, &off) ==
+	    B_TRUE) {
+		uint32_t rvers, rport;
+
+		/*
+		 * See xHCI 1.1 / 7.2 for the format of this. The first uint32_t
+		 * has version information while the third uint32_t has the port
+		 * count.
+		 */
+		rvers = xhci_get32(xhcip, XHCI_R_CAP, off);
+		rport = xhci_get32(xhcip, XHCI_R_CAP, off + 8);
+		if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+			xhci_error(xhcip, "failed to read xhci port counts: "
+			    "encountered fatal FM register error");
+			ddi_fm_service_impact(xhcip->xhci_dip,
+			    DDI_SERVICE_LOST);
+			return (B_FALSE);
+		}
+
+		rvers = XHCI_XECP_PROT_MAJOR(rvers);
+		rport = XHCI_XECP_PROT_PCOUNT(rport);
+
+		if (rvers == 3) {
+			nusb3 += rport;
+		} else if (rvers <= 2) {
+			nusb2 += rport;
+		} else {
+			xhci_error(xhcip, "encountered port capabilities with "
+			    "unknown major USB version: %d\n", rvers);
+		}
+	}
+
+	(void) ddi_prop_update_int(DDI_DEV_T_NONE, xhcip->xhci_dip,
+	    "usb2-capable-ports", nusb2);
+	(void) ddi_prop_update_int(DDI_DEV_T_NONE, xhcip->xhci_dip,
+	    "usb3-capable-ports", nusb3);
+
+	return (B_TRUE);
+}
+
+/*
+ * Take over control from the BIOS or other firmware, if applicable.
+ */
+static boolean_t
+xhci_controller_takeover(xhci_t *xhcip)
+{
+	int ret;
+	uint32_t val, off;
+
+	/*
+	 * If we can't find the legacy capability, then there's nothing to do.
+	 */
+	if (xhci_find_ext_cap(xhcip, XHCI_ID_USB_LEGACY, UINT32_MAX, &off) ==
+	    B_FALSE)
+		return (B_TRUE);
+	val = xhci_get32(xhcip, XHCI_R_CAP, off);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to read BIOS take over registers: "
+		    "encountered fatal FM register error");
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (B_FALSE);
+	}
+
+	if (val & XHCI_BIOS_OWNED) {
+		val |= XHCI_OS_OWNED;
+		xhci_put32(xhcip, XHCI_R_CAP, off, val);
+		if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+			xhci_error(xhcip, "failed to write BIOS take over "
+			    "registers: encountered fatal FM register error");
+			ddi_fm_service_impact(xhcip->xhci_dip,
+			    DDI_SERVICE_LOST);
+			return (B_FALSE);
+		}
+
+		/*
+		 * Wait up to 5 seconds for things to change. While this number
+		 * isn't specified in the xHCI spec, it seems to be the de facto
+		 * value that various systems are using today. We'll use a 10ms
+		 * interval to check.
+		 */
+		ret = xhci_reg_poll(xhcip, XHCI_R_CAP, off,
+		    XHCI_BIOS_OWNED | XHCI_OS_OWNED, XHCI_OS_OWNED, 500, 10);
+		if (ret == EIO)
+			return (B_FALSE);
+		if (ret == ETIMEDOUT) {
+			xhci_log(xhcip, "!timed out waiting for firmware to "
+			    "hand off, taking over");
+			val &= ~XHCI_BIOS_OWNED;
+			xhci_put32(xhcip, XHCI_R_CAP, off, val);
+			if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+				xhci_error(xhcip, "failed to write forced "
+				    "takeover: encountered fatal FM register "
+				    "error");
+				ddi_fm_service_impact(xhcip->xhci_dip,
+				    DDI_SERVICE_LOST);
+				return (B_FALSE);
+			}
+		}
+	}
+
+	val = xhci_get32(xhcip, XHCI_R_CAP, off + XHCI_XECP_LEGCTLSTS);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to read legacy control registers: "
+		    "encountered fatal FM register error");
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (B_FALSE);
+	}
+	val &= XHCI_XECP_SMI_MASK;
+	val |= XHCI_XECP_CLEAR_SMI;
+	xhci_put32(xhcip, XHCI_R_CAP, off + XHCI_XECP_LEGCTLSTS, val);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to write legacy control registers: "
+		    "encountered fatal FM register error");
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+static int
+xhci_controller_stop(xhci_t *xhcip)
+{
+	uint32_t cmdreg;
+
+	cmdreg = xhci_get32(xhcip, XHCI_R_OPER, XHCI_USBCMD);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to read USB Command register: "
+		    "encountered fatal FM register error");
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (EIO);
+	}
+
+	cmdreg &= ~(XHCI_CMD_RS | XHCI_CMD_INTE);
+	xhci_put32(xhcip, XHCI_R_OPER, XHCI_USBCMD, cmdreg);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to write USB Command register: "
+		    "encountered fatal FM register error");
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (EIO);
+	}
+
+	/*
+	 * Wait up to 50ms for this to occur. The specification says that this
+	 * should stop within 16ms, but we give ourselves a bit more time just
+	 * in case.
+	 */
+	return (xhci_reg_poll(xhcip, XHCI_R_OPER, XHCI_USBSTS, XHCI_STS_HCH,
+	    XHCI_STS_HCH, 50, 10));
+}
+
+static int
+xhci_controller_reset(xhci_t *xhcip)
+{
+	int ret;
+	uint32_t cmdreg;
+
+	cmdreg = xhci_get32(xhcip, XHCI_R_OPER, XHCI_USBCMD);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to read USB Command register for "
+		    "reset: encountered fatal FM register error");
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (EIO);
+	}
+
+	cmdreg |= XHCI_CMD_HCRST;
+	xhci_put32(xhcip, XHCI_R_OPER, XHCI_USBCMD, cmdreg);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to write USB Command register for "
+		    "reset: encountered fatal FM register error");
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (EIO);
+	}
+
+	/*
+	 * Some controllers apparently don't want to be touched for at least 1ms
+	 * after we initiate the reset. Therefore give all controllers this
+	 * moment to breathe.
+	 */
+	delay(drv_usectohz(xhci_reset_delay));
+
+	/*
+	 * To tell that the reset has completed we first verify that the reset
+	 * has finished and that the USBCMD register no longer has the reset bit
+	 * asserted. However, once that's done we have to go verify that CNR
+	 * (Controller Not Ready) is no longer asserted.
+	 */
+	if ((ret = xhci_reg_poll(xhcip, XHCI_R_OPER, XHCI_USBCMD,
+	    XHCI_CMD_HCRST, 0, 500, 10)) != 0)
+		return (ret);
+
+	return (xhci_reg_poll(xhcip, XHCI_R_OPER, XHCI_USBSTS,
+	    XHCI_STS_CNR, 0, 500, 10));
+}
+
+/*
+ * Take care of all the required initialization before we can actually enable
+ * the controller. This means that we need to:
+ *
+ *    o Program the maximum number of slots
+ *    o Program the DCBAAP and allocate the scratchpad
+ *    o Program the Command Ring
+ *    o Initialize the Event Ring
+ *    o Enable interrupts (set imod)
+ */
+static int
+xhci_controller_configure(xhci_t *xhcip)
+{
+	int ret;
+	uint32_t config;
+
+	config = xhci_get32(xhcip, XHCI_R_OPER, XHCI_CONFIG);
+	config &= ~XHCI_CONFIG_SLOTS_MASK;
+	config |= xhcip->xhci_caps.xcap_max_slots;
+	xhci_put32(xhcip, XHCI_R_OPER, XHCI_CONFIG, config);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (EIO);
+	}
+
+	if ((ret = xhci_context_init(xhcip)) != 0) {
+		const char *reason;
+		if (ret == EIO) {
+			reason = "fatal FM I/O error occurred";
+		} else if (ret == ENOMEM) {
+			reason = "unable to allocate DMA memory";
+		} else {
+			reason = "unexpected error occurred";
+		}
+
+		xhci_error(xhcip, "failed to initialize xhci context "
+		    "registers: %s (%d)", reason, ret);
+		return (ret);
+	}
+
+	if ((ret = xhci_command_ring_init(xhcip)) != 0) {
+		xhci_error(xhcip, "failed to initialize commands: %d", ret);
+		return (ret);
+	}
+
+	if ((ret = xhci_event_init(xhcip)) != 0) {
+		xhci_error(xhcip, "failed to initialize events: %d", ret);
+		return (ret);
+	}
+
+	if ((ret = xhci_intr_conf(xhcip)) != 0) {
+		xhci_error(xhcip, "failed to configure interrupts: %d", ret);
+		return (ret);
+	}
+
+	return (0);
+}
+
+static int
+xhci_controller_start(xhci_t *xhcip)
+{
+	uint32_t reg;
+
+	reg = xhci_get32(xhcip, XHCI_R_OPER, XHCI_USBCMD);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to read USB Command register for "
+		    "start: encountered fatal FM register error");
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (EIO);
+	}
+
+	reg |= XHCI_CMD_RS;
+	xhci_put32(xhcip, XHCI_R_OPER, XHCI_USBCMD, reg);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to write USB Command register for "
+		    "start: encountered fatal FM register error");
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (EIO);
+	}
+
+	return (xhci_reg_poll(xhcip, XHCI_R_OPER, XHCI_USBSTS,
+	    XHCI_STS_HCH, 0, 500, 10));
+}
+
+/* ARGSUSED */
+static void
+xhci_reset_task(void *arg)
+{
+	/*
+	 * Longer term, we'd like to properly perform a controller reset.
+	 * However, that requires a bit more assistance from USBA to work
+	 * properly and tear down devices. In the meantime, we panic.
+	 */
+	panic("XHCI runtime reset required");
+}
+
+/*
+ * This function is called when we've detected a fatal FM condition that has
+ * resulted in a loss of service and we need to force a reset of the controller
+ * as a whole. Only one such reset may be ongoing at a time.
+ */
+void
+xhci_fm_runtime_reset(xhci_t *xhcip)
+{
+	boolean_t locked = B_FALSE;
+
+	if (mutex_owned(&xhcip->xhci_lock)) {
+		locked = B_TRUE;
+	} else {
+		mutex_enter(&xhcip->xhci_lock);
+	}
+
+	/*
+	 * If we're already in the error state than a reset is already ongoing
+	 * and there is nothing for us to do here.
+	 */
+	if (xhcip->xhci_state & XHCI_S_ERROR) {
+		goto out;
+	}
+
+	xhcip->xhci_state |= XHCI_S_ERROR;
+	ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+	taskq_dispatch_ent(xhci_taskq, xhci_reset_task, xhcip, 0,
+	    &xhcip->xhci_tqe);
+out:
+	if (!locked) {
+		mutex_exit(&xhcip->xhci_lock);
+	}
+}
+
+static int
+xhci_ioctl_portsc(xhci_t *xhcip, intptr_t arg)
+{
+	int i;
+	xhci_ioctl_portsc_t xhi;
+
+	bzero(&xhi, sizeof (xhci_ioctl_portsc_t));
+	xhi.xhi_nports = xhcip->xhci_caps.xcap_max_ports;
+	for (i = 1; i <= xhcip->xhci_caps.xcap_max_ports; i++) {
+		xhi.xhi_portsc[i] = xhci_get32(xhcip, XHCI_R_OPER,
+		    XHCI_PORTSC(i));
+	}
+
+	if (ddi_copyout(&xhi, (void *)(uintptr_t)arg, sizeof (xhi), 0) != 0)
+		return (EFAULT);
+
+	return (0);
+}
+
+static int
+xhci_ioctl_clear(xhci_t *xhcip, intptr_t arg)
+{
+	uint32_t reg;
+	xhci_ioctl_clear_t xic;
+
+	if (ddi_copyin((const void *)(uintptr_t)arg, &xic, sizeof (xic),
+	    0) != 0)
+		return (EFAULT);
+
+	if (xic.xic_port == 0 || xic.xic_port >
+	    xhcip->xhci_caps.xcap_max_ports)
+		return (EINVAL);
+
+	reg = xhci_get32(xhcip, XHCI_R_OPER, XHCI_PORTSC(xic.xic_port));
+	reg &= ~XHCI_PS_CLEAR;
+	reg |= XHCI_PS_CSC | XHCI_PS_PEC | XHCI_PS_WRC | XHCI_PS_OCC |
+	    XHCI_PS_PRC | XHCI_PS_PLC | XHCI_PS_CEC;
+	xhci_put32(xhcip, XHCI_R_OPER, XHCI_PORTSC(xic.xic_port), reg);
+
+	return (0);
+}
+
+static int
+xhci_ioctl_setpls(xhci_t *xhcip, intptr_t arg)
+{
+	uint32_t reg;
+	xhci_ioctl_setpls_t xis;
+
+	if (ddi_copyin((const void *)(uintptr_t)arg, &xis, sizeof (xis),
+	    0) != 0)
+		return (EFAULT);
+
+	if (xis.xis_port == 0 || xis.xis_port >
+	    xhcip->xhci_caps.xcap_max_ports)
+		return (EINVAL);
+
+	if (xis.xis_pls & ~0xf)
+		return (EINVAL);
+
+	reg = xhci_get32(xhcip, XHCI_R_OPER, XHCI_PORTSC(xis.xis_port));
+	reg &= ~XHCI_PS_CLEAR;
+	reg |= XHCI_PS_PLS_SET(xis.xis_pls);
+	reg |= XHCI_PS_LWS;
+	xhci_put32(xhcip, XHCI_R_OPER, XHCI_PORTSC(xis.xis_port), reg);
+
+	return (0);
+}
+
+static int
+xhci_open(dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+	dev_info_t *dip = xhci_get_dip(*devp);
+
+	return (usba_hubdi_open(dip, devp, flags, otyp, credp));
+}
+
+static int
+xhci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+    int *rvalp)
+{
+	dev_info_t *dip = xhci_get_dip(dev);
+
+	if (cmd == XHCI_IOCTL_PORTSC ||
+	    cmd == XHCI_IOCTL_CLEAR ||
+	    cmd == XHCI_IOCTL_SETPLS) {
+		xhci_t *xhcip = ddi_get_soft_state(xhci_soft_state,
+		    getminor(dev) & ~HUBD_IS_ROOT_HUB);
+
+		if (secpolicy_xhci(credp) != 0 ||
+		    crgetzoneid(credp) != GLOBAL_ZONEID)
+			return (EPERM);
+
+		if (mode & FKIOCTL)
+			return (ENOTSUP);
+
+		if (!(mode & FWRITE))
+			return (EBADF);
+
+		if (cmd == XHCI_IOCTL_PORTSC)
+			return (xhci_ioctl_portsc(xhcip, arg));
+		else if (cmd == XHCI_IOCTL_CLEAR)
+			return (xhci_ioctl_clear(xhcip, arg));
+		else
+			return (xhci_ioctl_setpls(xhcip, arg));
+	}
+
+	return (usba_hubdi_ioctl(dip, dev, cmd, arg, mode, credp, rvalp));
+}
+
+static int
+xhci_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+	dev_info_t *dip = xhci_get_dip(dev);
+
+	return (usba_hubdi_close(dip, dev, flag, otyp, credp));
+}
+
+/*
+ * We try to clean up everything that we can. The only thing that we let stop us
+ * at this time is a failure to remove the root hub, which is realistically the
+ * equivalent of our EBUSY case.
+ */
+static int
+xhci_cleanup(xhci_t *xhcip)
+{
+	int ret, inst;
+
+	if (xhcip->xhci_seq & XHCI_ATTACH_ROOT_HUB) {
+		if ((ret = xhci_root_hub_fini(xhcip)) != 0)
+			return (ret);
+	}
+
+	if (xhcip->xhci_seq & XHCI_ATTACH_USBA) {
+		xhci_hcd_fini(xhcip);
+	}
+
+	if (xhcip->xhci_seq & XHCI_ATTACH_STARTED) {
+		mutex_enter(&xhcip->xhci_lock);
+		while (xhcip->xhci_state & XHCI_S_ERROR)
+			cv_wait(&xhcip->xhci_statecv, &xhcip->xhci_lock);
+		mutex_exit(&xhcip->xhci_lock);
+
+		(void) xhci_controller_stop(xhcip);
+	}
+
+	/*
+	 * Always release the context, command, and event data. They handle the
+	 * fact that they me be in an arbitrary state or unallocated.
+	 */
+	xhci_event_fini(xhcip);
+	xhci_command_ring_fini(xhcip);
+	xhci_context_fini(xhcip);
+
+	if (xhcip->xhci_seq & XHCI_ATTACH_INTR_ENABLE) {
+		(void) xhci_ddi_intr_disable(xhcip);
+	}
+
+	if (xhcip->xhci_seq & XHCI_ATTACH_SYNCH) {
+		cv_destroy(&xhcip->xhci_statecv);
+		mutex_destroy(&xhcip->xhci_lock);
+	}
+
+	if (xhcip->xhci_seq & XHCI_ATTACH_INTR_ADD) {
+		if ((ret = ddi_intr_remove_handler(xhcip->xhci_intr_hdl)) !=
+		    DDI_SUCCESS) {
+			xhci_error(xhcip, "failed to remove interrupt "
+			    "handler: %d", ret);
+		}
+	}
+
+	if (xhcip->xhci_seq & XHCI_ATTACH_INTR_ALLOC) {
+		if ((ret = ddi_intr_free(xhcip->xhci_intr_hdl)) !=
+		    DDI_SUCCESS) {
+			xhci_error(xhcip, "failed to free interrupts: %d", ret);
+		}
+	}
+
+	if (xhcip->xhci_seq & XHCI_ATTACH_REGS_MAP) {
+		ddi_regs_map_free(&xhcip->xhci_regs_handle);
+		xhcip->xhci_regs_handle = NULL;
+	}
+
+	if (xhcip->xhci_seq & XHCI_ATTACH_PCI_CONFIG) {
+		pci_config_teardown(&xhcip->xhci_cfg_handle);
+		xhcip->xhci_cfg_handle = NULL;
+	}
+
+	if (xhcip->xhci_seq & XHCI_ATTACH_FM) {
+		xhci_fm_fini(xhcip);
+		xhcip->xhci_fm_caps = 0;
+	}
+
+	inst = ddi_get_instance(xhcip->xhci_dip);
+	xhcip->xhci_dip = NULL;
+	ddi_soft_state_free(xhci_soft_state, inst);
+
+	return (DDI_SUCCESS);
+}
+
+static int
+xhci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	int ret, inst, route;
+	xhci_t *xhcip;
+
+	if (cmd != DDI_ATTACH)
+		return (DDI_FAILURE);
+
+	inst = ddi_get_instance(dip);
+	if (ddi_soft_state_zalloc(xhci_soft_state, inst) != 0)
+		return (DDI_FAILURE);
+	xhcip = ddi_get_soft_state(xhci_soft_state, ddi_get_instance(dip));
+	xhcip->xhci_dip = dip;
+
+	xhcip->xhci_regs_capoff = PCI_EINVAL32;
+	xhcip->xhci_regs_operoff = PCI_EINVAL32;
+	xhcip->xhci_regs_runoff = PCI_EINVAL32;
+	xhcip->xhci_regs_dooroff = PCI_EINVAL32;
+
+	xhci_fm_init(xhcip);
+	xhcip->xhci_seq |= XHCI_ATTACH_FM;
+
+	if (pci_config_setup(xhcip->xhci_dip, &xhcip->xhci_cfg_handle) !=
+	    DDI_SUCCESS) {
+		goto err;
+	}
+	xhcip->xhci_seq |= XHCI_ATTACH_PCI_CONFIG;
+	xhcip->xhci_vendor_id = pci_config_get16(xhcip->xhci_cfg_handle,
+	    PCI_CONF_VENID);
+	xhcip->xhci_device_id = pci_config_get16(xhcip->xhci_cfg_handle,
+	    PCI_CONF_DEVID);
+
+	if (xhci_regs_map(xhcip) == B_FALSE) {
+		goto err;
+	}
+
+	xhcip->xhci_seq |= XHCI_ATTACH_REGS_MAP;
+
+	if (xhci_regs_init(xhcip) == B_FALSE)
+		goto err;
+
+	if (xhci_read_params(xhcip) == B_FALSE)
+		goto err;
+
+	if (xhci_identify(xhcip) == B_FALSE)
+		goto err;
+
+	if (xhci_alloc_intrs(xhcip) == B_FALSE)
+		goto err;
+	xhcip->xhci_seq |= XHCI_ATTACH_INTR_ALLOC;
+
+	if (xhci_add_intr_handler(xhcip) == B_FALSE)
+		goto err;
+	xhcip->xhci_seq |= XHCI_ATTACH_INTR_ADD;
+
+	mutex_init(&xhcip->xhci_lock, NULL, MUTEX_DRIVER,
+	    (void *)(uintptr_t)xhcip->xhci_intr_pri);
+	cv_init(&xhcip->xhci_statecv, NULL, CV_DRIVER, NULL);
+	xhcip->xhci_seq |= XHCI_ATTACH_SYNCH;
+
+	if (xhci_port_count(xhcip) == B_FALSE)
+		goto err;
+
+	if (xhci_controller_takeover(xhcip) == B_FALSE)
+		goto err;
+
+	/*
+	 * We don't enable interrupts until after we take over the controller
+	 * from the BIOS. We've observed cases where this can cause spurious
+	 * interrupts.
+	 */
+	if (xhci_ddi_intr_enable(xhcip) == B_FALSE)
+		goto err;
+	xhcip->xhci_seq |= XHCI_ATTACH_INTR_ENABLE;
+
+	if ((ret = xhci_controller_stop(xhcip)) != 0) {
+		xhci_error(xhcip, "failed to stop controller: %s",
+		    ret == EIO ? "encountered FM register error" :
+		    "timed out while waiting for controller");
+		goto err;
+	}
+
+	if ((ret = xhci_controller_reset(xhcip)) != 0) {
+		xhci_error(xhcip, "failed to reset controller: %s",
+		    ret == EIO ? "encountered FM register error" :
+		    "timed out while waiting for controller");
+		goto err;
+	}
+
+	if ((ret = xhci_controller_configure(xhcip)) != 0) {
+		xhci_error(xhcip, "failed to configure controller: %d", ret);
+		goto err;
+	}
+
+	/*
+	 * Some systems support having ports routed to both an ehci and xhci
+	 * controller. If we support it and the user hasn't requested otherwise
+	 * via a driver.conf tuning, we reroute it now.
+	 */
+	route = ddi_prop_get_int(DDI_DEV_T_ANY, xhcip->xhci_dip,
+	    DDI_PROP_DONTPASS, "xhci-reroute", XHCI_PROP_REROUTE_DEFAULT);
+	if (route != XHCI_PROP_REROUTE_DISABLE &&
+	    (xhcip->xhci_quirks & XHCI_QUIRK_INTC_EHCI))
+		(void) xhci_reroute_intel(xhcip);
+
+	if ((ret = xhci_controller_start(xhcip)) != 0) {
+		xhci_log(xhcip, "failed to reset controller: %s",
+		    ret == EIO ? "encountered FM register error" :
+		    "timed out while waiting for controller");
+		goto err;
+	}
+	xhcip->xhci_seq |= XHCI_ATTACH_STARTED;
+
+	/*
+	 * Finally, register ourselves with the USB framework itself.
+	 */
+	if ((ret = xhci_hcd_init(xhcip)) != 0) {
+		xhci_error(xhcip, "failed to register hcd with usba");
+		goto err;
+	}
+	xhcip->xhci_seq |= XHCI_ATTACH_USBA;
+
+	if ((ret = xhci_root_hub_init(xhcip)) != 0) {
+		xhci_error(xhcip, "failed to load the root hub driver");
+		goto err;
+	}
+	xhcip->xhci_seq |= XHCI_ATTACH_ROOT_HUB;
+
+	return (DDI_SUCCESS);
+
+err:
+	(void) xhci_cleanup(xhcip);
+	return (DDI_FAILURE);
+}
+
+static int
+xhci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	xhci_t *xhcip;
+
+	if (cmd != DDI_DETACH)
+		return (DDI_FAILURE);
+
+	xhcip = ddi_get_soft_state(xhci_soft_state, ddi_get_instance(dip));
+	if (xhcip == NULL) {
+		dev_err(dip, CE_WARN, "detach called without soft state!");
+		return (DDI_FAILURE);
+	}
+
+	return (xhci_cleanup(xhcip));
+}
+
+/* ARGSUSED */
+static int
+xhci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **outp)
+{
+	dev_t dev;
+	int inst;
+
+	switch (cmd) {
+	case DDI_INFO_DEVT2DEVINFO:
+		dev = (dev_t)arg;
+		*outp = xhci_get_dip(dev);
+		if (*outp == NULL)
+			return (DDI_FAILURE);
+		break;
+	case DDI_INFO_DEVT2INSTANCE:
+		dev = (dev_t)arg;
+		inst = getminor(dev) & ~HUBD_IS_ROOT_HUB;
+		*outp = (void *)(uintptr_t)inst;
+		break;
+	default:
+		return (DDI_FAILURE);
+	}
+
+	return (DDI_SUCCESS);
+}
+
+static struct cb_ops xhci_cb_ops = {
+	xhci_open,		/* cb_open */
+	xhci_close,		/* cb_close */
+	nodev,			/* cb_strategy */
+	nodev,			/* cb_print */
+	nodev,			/* cb_dump */
+	nodev,			/* cb_read */
+	nodev,			/* cb_write */
+	xhci_ioctl,		/* cb_ioctl */
+	nodev,			/* cb_devmap */
+	nodev,			/* cb_mmap */
+	nodev,			/* cb_segmap */
+	nochpoll,		/* cb_chpoll */
+	ddi_prop_op,		/* cb_prop_op */
+	NULL,			/* cb_stream */
+	D_MP | D_HOTPLUG,	/* cb_flag */
+	CB_REV,			/* cb_rev */
+	nodev,			/* cb_aread */
+	nodev			/* cb_awrite */
+};
+
+static struct dev_ops xhci_dev_ops = {
+	DEVO_REV,			/* devo_rev */
+	0,				/* devo_refcnt */
+	xhci_getinfo,			/* devo_getinfo */
+	nulldev,			/* devo_identify */
+	nulldev,			/* devo_probe */
+	xhci_attach,			/* devo_attach */
+	xhci_detach,			/* devo_detach */
+	nodev,				/* devo_reset */
+	&xhci_cb_ops,			/* devo_cb_ops */
+	&usba_hubdi_busops,		/* devo_bus_ops */
+	usba_hubdi_root_hub_power,	/* devo_power */
+	ddi_quiesce_not_supported 	/* devo_quiesce */
+};
+
+static struct modldrv xhci_modldrv = {
+	&mod_driverops,
+	"USB xHCI Driver",
+	&xhci_dev_ops
+};
+
+static struct modlinkage xhci_modlinkage = {
+	MODREV_1,
+	&xhci_modldrv,
+	NULL
+};
+
+int
+_init(void)
+{
+	int ret;
+
+	if ((ret = ddi_soft_state_init(&xhci_soft_state, sizeof (xhci_t),
+	    0)) != 0) {
+		return (ret);
+	}
+
+	xhci_taskq = taskq_create("xhci_taskq", 1, minclsyspri, 0, 0, 0);
+	if (xhci_taskq == NULL) {
+		ddi_soft_state_fini(&xhci_soft_state);
+		return (ENOMEM);
+	}
+
+	if ((ret = mod_install(&xhci_modlinkage)) != 0) {
+		taskq_destroy(xhci_taskq);
+		xhci_taskq = NULL;
+	}
+
+	return (ret);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&xhci_modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+	int ret;
+
+	if ((ret = mod_remove(&xhci_modlinkage)) != 0)
+		return (ret);
+
+	if (xhci_taskq != NULL) {
+		taskq_destroy(xhci_taskq);
+		xhci_taskq = NULL;
+	}
+
+	ddi_soft_state_fini(&xhci_soft_state);
+
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci.conf	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,31 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2016 Joyent, Inc.
+#
+
+#
+# To support hotplug related activity we must always attach even if we
+# don't have any active instances. The force attach directive guarantees
+# that we'll still be around.
+#
+ddi-forceattach=1;
+
+#
+# This indicates that ports which are connected to both the xhci and
+# ehci controllers should be rerouted to the xhci controller. This is
+# the default behavior and required for many ports to operate at USB 3.x
+# speeds. In addition, many systems ACPI tables will do this
+# automatically. Setting this to 0 only causes the xhci driver not to
+# change anything itself.
+#
+xhci-reroute=1;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci_command.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,848 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * -----------------------
+ * Command Ring Management
+ * -----------------------
+ *
+ * The command ring is the primary means by which the xHCI controller is
+ * managed. Events may be placed on the ring, at which point they will be
+ * processed in order. When commands are finished they generate event
+ * completions and we are notified via an interrupt.
+ *
+ * Every command is formatted in a transfer request block (TRB). These TRBs are
+ * queued on the command ring. To start the command ring, a doorbell register is
+ * written to. The current state of the command ring is maintained in the
+ * command ring control register (XHCI_CRCR).
+ *
+ * Every command has a condition variable. When the driver submits a command, it
+ * blocks on the command's CV waiting for a change in the commands status. This
+ * CV will be signaled after the command completes or is aborted, allowing the
+ * caller to treat this as a synchronous, blocking operation.
+ *
+ * The command ring itself consists of three primary states:
+ *
+ * 	XHCI_COMMAND_RING_IDLE		The command ring is not currently
+ * 					processing any events. No timeout events
+ * 					are active.
+ *
+ * 	XHCI_COMMAND_RING_RUNNING	The command ring currently has one or
+ * 					more events enqueued and the hardware
+ * 					has been signalled to process commands.
+ *
+ * 	XHCI_COMMAND_RING_ABORTING	A command has timed out and we are
+ * 					attempting to abort the current command,
+ * 					which will stop the ring.
+ *
+ * 	XHCI_COMMAND_RING_ABORT_DONE	We have successfully received a
+ * 					notification that the abort worked and
+ * 					that the command ring has stopped. This
+ * 					allows us to clean up state and
+ * 					transition back to either idle or
+ * 					running, depending on if we have queued
+ * 					commands.
+ *
+ * The state transition can be summarized as:
+ *
+ *    +------+                        +---------+
+ *    | Idle |--------*-------------->| Running |<----------------------+
+ *    +------+        . Command       +---------+                       |
+ *       ^              TRB queued      |    |                          |
+ *       |              on ring         |    |                          |
+ *       |                              |    * . . Command not          |
+ *       +-------*----------------------+    |     acknowledged         |
+ *       |       . . No more                 |     within timeout       |
+ *       |           commands                |     xhci_command_wait    |
+ *       |           queued                  |                          |
+ *       |                                   v       . abort request    |
+ *       * . No commands              +----------+   . times out        |
+ *       |   queued after             | Aborting |---*--+               |
+ *       |   successful               +----------+      v               |
+ *       |   abort                         |      +----------+          |
+ *       |                       abort . . *      | HW Reset |          |
+ *       |                acknowledged     |      +----------+          |
+ *       |                                 v                            |
+ *       |                           +------------+                     |
+ *       +---------------------------| Abort Done |----*----------------+
+ *                                   +------------+    . Commands queued
+ *                                                       after successful
+ *                                                       abort
+ *
+ * ---------------------------
+ * Timeouts and Command Aborts
+ * ---------------------------
+ *
+ * Commands may time out either due to issues with the host controller or with
+ * the devices connected to it. For example, the ADDRESS DEVICE command may
+ * issue commands to the device. As such, we need to be prepared for commands to
+ * time out.
+ *
+ * To deal with a stalled command, we write to the XHCI_CRCR register to abort
+ * the currently running command. This is discussed in xHCI 1.1 / 4.6.1.2. When
+ * a command is aborted, we should eventually receive a TRB completion for that
+ * command. However, this is no guarantee that an abort will be successful. The
+ * specification recommends waiting about 5 seconds for that to finish. After
+ * which we terminate the device.
+ *
+ * For an abort to be successful, we expect two different notifications. First
+ * we should receive a TRB for the actual command itself indicating that it's
+ * terminated. Next, we should receive a TRB indicating that the command ring
+ * has stopped. Only when we receive this second one, do we consider re-enabling
+ * the command ring.
+ *
+ * -------
+ * Locking
+ * -------
+ *
+ * The command ring's lock, xhci_command_ring_t`xcr_lock, should not be accessed
+ * outside of this file. If a caller needs to take the xhci_t`xhci_lock, it must
+ * be taken before the xcr_lock is taken. It is illegal for to hold
+ * xhci_t`xhci_lock across any command functions. Doing so would lead to
+ * deadlock.
+ */
+
+#include <sys/usb/hcd/xhci/xhci.h>
+#include <sys/sysmacros.h>
+
+/*
+ * Recommended time to wait for an abort in from the Implementation Note
+ * in XHCI 1.1 / 4.6.1.2. The time is kept in microseconds.
+ */
+clock_t xhci_command_abort_wait = 5 * MICROSEC;
+
+/*
+ * Default to waiting for one second for a command to time out. Time stored in
+ * microseconds.
+ */
+clock_t xhci_command_wait = MICROSEC;
+
+/*
+ * Required forwards.
+ */
+static void xhci_command_settimeout(xhci_t *, clock_t);
+
+void
+xhci_command_ring_fini(xhci_t *xhcip)
+{
+	xhci_command_ring_t *xcr = &xhcip->xhci_command;
+
+	/*
+	 * If the ring is not allocated, then nothing else is here.
+	 */
+	if (xcr->xcr_ring.xr_trb == NULL)
+		return;
+	VERIFY(xcr->xcr_timeout == 0);
+	xhci_ring_free(&xcr->xcr_ring);
+	mutex_destroy(&xcr->xcr_lock);
+	cv_destroy(&xcr->xcr_cv);
+	list_destroy(&xcr->xcr_commands);
+}
+
+/*
+ * Initialize or re-initialize the command ring. This will be called whenever we
+ * reset the xHCI commandler, so we may actually have already allocated DMA
+ * memory for the ring.
+ */
+int
+xhci_command_ring_init(xhci_t *xhcip)
+{
+	int ret;
+	uint64_t addr;
+	xhci_command_ring_t *xcr = &xhcip->xhci_command;
+
+	if (xcr->xcr_ring.xr_trb == NULL) {
+		if ((ret = xhci_ring_alloc(xhcip, &xcr->xcr_ring)) != 0)
+			return (ret);
+	}
+
+	if ((ret = xhci_ring_reset(xhcip, &xcr->xcr_ring)) != 0)
+		return (ret);
+
+#ifdef	DEBUG
+	addr = xhci_get64(xhcip, XHCI_R_OPER, XHCI_CRCR);
+	VERIFY0(addr & XHCI_CRCR_CRR);
+#endif
+	addr = LE_64(xhci_dma_pa(&xcr->xcr_ring.xr_dma) | XHCI_CRCR_RCS);
+	xhci_put64(xhcip, XHCI_R_OPER, XHCI_CRCR, addr);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK)
+		return (EIO);
+
+	mutex_init(&xcr->xcr_lock, NULL, MUTEX_DRIVER,
+	    DDI_INTR_PRI(xhcip->xhci_intr_pri));
+	cv_init(&xcr->xcr_cv, NULL, CV_DRIVER, NULL);
+	list_create(&xcr->xcr_commands, sizeof (xhci_command_t),
+	    offsetof(xhci_command_t, xco_link));
+	return (0);
+}
+
+static void
+xhci_command_timeout(void *arg)
+{
+	uint64_t reg;
+	clock_t delay;
+	xhci_t *xhcip = arg;
+	xhci_command_ring_t *xcr = &xhcip->xhci_command;
+	xhci_command_t *xco;
+
+	mutex_enter(&xcr->xcr_lock);
+
+	xco = list_head(&xcr->xcr_commands);
+	if (xco == NULL || xco->xco_state != XHCI_COMMAND_S_QUEUED) {
+		xcr->xcr_timeout = 0;
+		mutex_exit(&xcr->xcr_lock);
+		return;
+	}
+
+	xcr->xcr_state = XHCI_COMMAND_RING_ABORTING;
+	reg = xhci_get64(xhcip, XHCI_R_OPER, XHCI_CRCR);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xcr->xcr_timeout = 0;
+		mutex_exit(&xcr->xcr_lock);
+		xhci_error(xhcip, "encountered fatal FM error reading command "
+		    "ring control register: resetting device");
+		xhci_fm_runtime_reset(xhcip);
+		return;
+	}
+
+	/*
+	 * While all the other bits should be ignored because we're running, if
+	 * for some reason we're not running, then this will make sure that we
+	 * don't screw things up.
+	 */
+	reg |= XHCI_CRCR_CA;
+	xhci_put64(xhcip, XHCI_R_OPER, XHCI_CRCR, reg);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xcr->xcr_timeout = 0;
+		mutex_exit(&xcr->xcr_lock);
+		xhci_error(xhcip, "encountered fatal FM error writing command "
+		    "ring control register: resetting device");
+		xhci_fm_runtime_reset(xhcip);
+		return;
+	}
+
+	delay = drv_usectohz(xhci_command_abort_wait);
+	while (xcr->xcr_state != XHCI_COMMAND_RING_ABORT_DONE) {
+		int ret;
+
+		ret = cv_reltimedwait(&xcr->xcr_cv, &xcr->xcr_lock, delay,
+		    TR_CLOCK_TICK);
+		if (ret == -1) {
+			/* Time out waiting for the abort */
+			xcr->xcr_timeout = 0;
+			mutex_exit(&xcr->xcr_lock);
+			xhci_error(xhcip, "abort command timed out: resetting "
+			    "device");
+			xhci_fm_runtime_reset(xhcip);
+			return;
+		}
+	}
+
+	/*
+	 * Successful abort, transition the ring as needed.
+	 */
+	if (list_is_empty(&xcr->xcr_commands) != 0) {
+		xcr->xcr_state = XHCI_COMMAND_RING_IDLE;
+		xcr->xcr_timeout = 0;
+	} else {
+		xhci_put32(xhcip, XHCI_R_DOOR, XHCI_DOORBELL(0), 0);
+		if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+			xcr->xcr_timeout = 0;
+			mutex_exit(&xcr->xcr_lock);
+			xhci_error(xhcip, "encountered fatal FM error writing "
+			    "command ring control register: resetting device");
+			xhci_fm_runtime_reset(xhcip);
+			return;
+		}
+
+		/*
+		 * Reset our timeout id before we create a new timeout
+		 */
+		xcr->xcr_timeout = 0;
+		xhci_command_settimeout(xhcip, xhci_command_wait);
+		xcr->xcr_state = XHCI_COMMAND_RING_RUNNING;
+	}
+	mutex_exit(&xcr->xcr_lock);
+}
+
+static void
+xhci_command_settimeout(xhci_t *xhcip, clock_t microsecs)
+{
+	clock_t delay;
+	xhci_command_ring_t *xcr = &xhcip->xhci_command;
+
+	ASSERT(MUTEX_HELD(&xcr->xcr_lock));
+	ASSERT(xcr->xcr_timeout == 0);
+
+	delay = drv_usectohz(microsecs);
+	xcr->xcr_timeout = timeout(xhci_command_timeout, xhcip, delay);
+}
+
+void
+xhci_command_init(xhci_command_t *xcp)
+{
+	bzero(xcp, sizeof (xhci_command_t));
+	cv_init(&xcp->xco_cv, NULL, CV_DRIVER, NULL);
+}
+
+void
+xhci_command_fini(xhci_command_t *xcp)
+{
+	cv_destroy(&xcp->xco_cv);
+}
+
+boolean_t
+xhci_command_event_callback(xhci_t *xhcip, xhci_trb_t *trb)
+{
+	int cstat;
+	timeout_id_t to;
+	xhci_command_t *xco, *rem;
+	xhci_command_ring_t *xcr = &xhcip->xhci_command;
+	xhci_ring_t *xrp = &xcr->xcr_ring;
+
+	mutex_enter(&xcr->xcr_lock);
+
+	/*
+	 * If we got an event that indicates that the command ring was stopped,
+	 * then we have successfully finished an abort. While a command ring
+	 * stop can also be done by writing to the XHCI_CRCR register, the
+	 * driver does not do so at this time; however, we guard the state
+	 * transition just in case.
+	 */
+	cstat = XHCI_TRB_GET_CODE(LE_32(trb->trb_status));
+	if (cstat == XHCI_CODE_CMD_RING_STOP) {
+		if (xcr->xcr_state == XHCI_COMMAND_RING_ABORTING)
+			xcr->xcr_state = XHCI_COMMAND_RING_ABORT_DONE;
+		cv_broadcast(&xcr->xcr_cv);
+		mutex_exit(&xcr->xcr_lock);
+		return (B_TRUE);
+	}
+
+	xco = list_head(&xcr->xcr_commands);
+	VERIFY(xco != NULL);
+
+	/*
+	 * The current event should be pointed to by the ring's tail pointer.
+	 * We need to check if this DMA address that we've been given matches
+	 * the address that we'd expect for the tail.
+	 */
+	if (xhci_ring_trb_tail_valid(xrp, LE_64(trb->trb_addr)) == B_FALSE) {
+		mutex_exit(&xcr->xcr_lock);
+		return (B_TRUE);
+	}
+
+	xco->xco_state = XHCI_COMMAND_S_RECEIVED;
+	to = xcr->xcr_timeout;
+	xcr->xcr_timeout = 0;
+	if (xcr->xcr_state != XHCI_COMMAND_RING_ABORTING) {
+		mutex_exit(&xcr->xcr_lock);
+		(void) untimeout(to);
+		mutex_enter(&xcr->xcr_lock);
+	}
+	rem = list_remove_head(&xcr->xcr_commands);
+
+	VERIFY3P(rem, ==, xco);
+
+	xco->xco_res.trb_addr = LE_64(trb->trb_addr);
+	xco->xco_res.trb_status = LE_32(trb->trb_status);
+	xco->xco_res.trb_flags = LE_32(trb->trb_flags);
+	xco->xco_state = XHCI_COMMAND_S_DONE;
+
+	/*
+	 * Advance the ring and wake up anyone who was waiting for a slot.
+	 */
+	if (xhci_ring_trb_consumed(xrp, LE_64(trb->trb_addr)) == B_FALSE) {
+		/*
+		 * Indicate that we need to do a runtime reset to the interrupt
+		 * handler.
+		 */
+		mutex_exit(&xcr->xcr_lock);
+		xhci_error(xhcip, "encountered invalid TRB head while "
+		    "processing command ring: TRB with addr 0x%"PRIx64 " could "
+		    "not be consumed", LE_64(trb->trb_addr));
+		xhci_fm_runtime_reset(xhcip);
+		return (B_FALSE);
+	}
+	cv_broadcast(&xcr->xcr_cv);
+
+	if (xcr->xcr_state < XHCI_COMMAND_RING_ABORTING) {
+		if (list_is_empty(&xcr->xcr_commands) != 0) {
+			xcr->xcr_state = XHCI_COMMAND_RING_IDLE;
+		} else {
+			xhci_command_settimeout(xhcip, xhci_command_wait);
+		}
+	}
+	mutex_exit(&xcr->xcr_lock);
+
+	/*
+	 * Now, let anyone waiting for this command to finish know it's done.
+	 */
+	cv_signal(&xco->xco_cv);
+
+	return (B_TRUE);
+}
+
+static int
+xhci_command_submit(xhci_t *xhcip, xhci_command_t *xco)
+{
+	int ret;
+	xhci_command_ring_t *xcr = &xhcip->xhci_command;
+	xhci_ring_t *xrp = &xcr->xcr_ring;
+
+	mutex_enter(&xcr->xcr_lock);
+
+	while (xhci_ring_trb_space(xrp, 1U) == B_FALSE ||
+	    xcr->xcr_state >= XHCI_COMMAND_RING_ABORTING) {
+		cv_wait(&xcr->xcr_cv, &xcr->xcr_lock);
+	}
+
+	xhci_ring_trb_put(xrp, &xco->xco_req);
+	xco->xco_state = XHCI_COMMAND_S_QUEUED;
+	list_insert_tail(&xcr->xcr_commands, xco);
+
+	/*
+	 * Now, make sure the ring is synched up before we might ring the door
+	 * bell and wake up the processor, if they're not currently doing so.
+	 */
+	XHCI_DMA_SYNC(xrp->xr_dma, DDI_DMA_SYNC_FORDEV);
+	if (xhci_check_dma_handle(xhcip, &xrp->xr_dma) != DDI_FM_OK) {
+		mutex_exit(&xcr->xcr_lock);
+		xhci_error(xhcip, "encountered fatal FM error syncing command "
+		    "ring DMA contents: resetting device");
+		xhci_fm_runtime_reset(xhcip);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	/*
+	 * Always ring the door bell. You never know what state the ring will be
+	 * in, but we do know that we won't be waiting for an abort as we're
+	 * protecting that state currently with the xcr_lock.
+	 */
+	xhci_put32(xhcip, XHCI_R_DOOR, XHCI_DOORBELL(0), 0);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		mutex_exit(&xcr->xcr_lock);
+		xhci_error(xhcip, "encountered fatal FM error ringing command "
+		    "ring doorbell: resetting device");
+		xhci_fm_runtime_reset(xhcip);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	/*
+	 * If the command ring is currently considered idle, make sure to start
+	 * up the timeout.
+	 */
+	if (xcr->xcr_state == XHCI_COMMAND_RING_IDLE) {
+		VERIFY(xcr->xcr_timeout == 0);
+
+		xhci_command_settimeout(xhcip, xhci_command_wait);
+		xcr->xcr_state = XHCI_COMMAND_RING_RUNNING;
+	}
+
+	while (xco->xco_state < XHCI_COMMAND_S_DONE)
+		cv_wait(&xco->xco_cv, &xcr->xcr_lock);
+
+	/*
+	 * When we return USB_SUCCESS, the actual error is returned in the
+	 * command's structure.
+	 */
+	if (xco->xco_state == XHCI_COMMAND_S_DONE)
+		ret = USB_SUCCESS;
+	else
+		ret = USB_HC_HARDWARE_ERROR;
+	mutex_exit(&xcr->xcr_lock);
+
+	return (ret);
+}
+
+int
+xhci_command_enable_slot(xhci_t *xhcip, uint8_t *slotp)
+{
+	int ret;
+	uint8_t slot, code;
+	xhci_command_t co;
+
+	VERIFY(xhcip != NULL);
+	VERIFY(slotp != NULL);
+
+	xhci_command_init(&co);
+
+	/*
+	 * Note, the slot type is supposed to vary depending on the protocol
+	 * type. However, XHCI 1.1/7.2.2.1.4 explicitly says that this will
+	 * always be set to zero for both USB 2 and USB 3, hence why we hardcode
+	 * this to zero and thus only have the command to enable the slot set
+	 * below.
+	 */
+	co.xco_req.trb_flags = LE_32(XHCI_CMD_ENABLE_SLOT) |
+	    XHCI_TRB_SET_STYPE(0);
+	ret = xhci_command_submit(xhcip, &co);
+	if (ret != 0)
+		goto done;
+
+	code = XHCI_TRB_GET_CODE(co.xco_res.trb_status);
+	slot = XHCI_TRB_GET_SLOT(co.xco_res.trb_flags);
+
+	if (code == XHCI_CODE_SUCCESS) {
+		*slotp = slot;
+		ret = USB_SUCCESS;
+	} else if (code == XHCI_CODE_NO_SLOTS) {
+		ret = USB_NO_RESOURCES;
+	} else if (code == XHCI_CODE_CMD_ABORTED) {
+		ret = USB_CR_TIMEOUT;
+	} else {
+		ret = USB_HC_HARDWARE_ERROR;
+		xhci_log(xhcip, "!unexpected error when enabling slot: "
+		    "%d", code);
+	}
+
+done:
+	xhci_command_fini(&co);
+	return (ret);
+}
+
+int
+xhci_command_disable_slot(xhci_t *xhcip, uint8_t slot)
+{
+	int ret, code;
+	xhci_command_t co;
+
+	VERIFY(xhcip != NULL);
+
+	xhci_command_init(&co);
+	co.xco_req.trb_flags = LE_32(XHCI_CMD_DISABLE_SLOT |
+	    XHCI_TRB_SET_SLOT(slot));
+	ret = xhci_command_submit(xhcip, &co);
+	if (ret != 0)
+		goto done;
+
+	code = XHCI_TRB_GET_CODE(co.xco_res.trb_status);
+	if (code == XHCI_CODE_SUCCESS) {
+		ret = USB_SUCCESS;
+	} else if (code == XHCI_CODE_CMD_ABORTED) {
+		ret = USB_CR_TIMEOUT;
+	} else {
+		ret = USB_HC_HARDWARE_ERROR;
+		xhci_log(xhcip, "!unexpected error when disabling slot: "
+		    "%d", code);
+	}
+
+done:
+	xhci_command_fini(&co);
+	return (ret);
+}
+
+int
+xhci_command_set_address(xhci_t *xhcip, xhci_device_t *xd, boolean_t bsr)
+{
+	int ret, code;
+	xhci_command_t co;
+
+	VERIFY(xhcip != NULL);
+	VERIFY(xd != NULL);
+
+	xhci_command_init(&co);
+	co.xco_req.trb_addr = LE_64(xhci_dma_pa(&xd->xd_ictx));
+	co.xco_req.trb_status = 0;
+	co.xco_req.trb_flags = LE_32(XHCI_CMD_ADDRESS_DEVICE |
+	    XHCI_TRB_SET_SLOT(xd->xd_slot));
+	if (bsr == B_TRUE)
+		co.xco_req.trb_flags |= LE_32(XHCI_TRB_BSR);
+
+	ret = xhci_command_submit(xhcip, &co);
+	if (ret != 0)
+		goto done;
+
+	code = XHCI_TRB_GET_CODE(co.xco_res.trb_status);
+	if (code == XHCI_CODE_SUCCESS) {
+		ret = USB_SUCCESS;
+	} else if (code == XHCI_CODE_CMD_ABORTED) {
+		ret = USB_CR_TIMEOUT;
+	} else {
+		ret = USB_HC_HARDWARE_ERROR;
+		xhci_log(xhcip, "!unexpected error when setting address: "
+		    "%d", code);
+	}
+done:
+	xhci_command_fini(&co);
+	return (ret);
+}
+
+int
+xhci_command_configure_endpoint(xhci_t *xhcip, xhci_device_t *xd)
+{
+	int ret, code;
+	xhci_command_t co;
+
+	VERIFY(xhcip != NULL);
+	VERIFY(xd != NULL);
+
+	xhci_command_init(&co);
+	co.xco_req.trb_addr = LE_64(xhci_dma_pa(&xd->xd_ictx));
+	co.xco_req.trb_status = LE_32(0);
+	co.xco_req.trb_flags = LE_32(XHCI_CMD_CONFIG_EP |
+	    XHCI_TRB_SET_SLOT(xd->xd_slot));
+
+	ret = xhci_command_submit(xhcip, &co);
+	if (ret != 0)
+		goto done;
+	code = XHCI_TRB_GET_CODE(co.xco_res.trb_status);
+	switch (code) {
+	case XHCI_CODE_SUCCESS:
+		ret = USB_SUCCESS;
+		break;
+	case XHCI_CODE_CMD_ABORTED:
+		ret = USB_CR_TIMEOUT;
+		break;
+	case XHCI_CODE_SLOT_NOT_ON:
+		xhci_log(xhcip, "!failed to configure endpoints for slot %d, "
+		    "slot not on, likely driver bug!", xd->xd_slot);
+		ret = USB_FAILURE;
+		break;
+	case XHCI_CODE_BANDWIDTH:
+		ret = USB_NO_BANDWIDTH;
+		break;
+	case XHCI_CODE_RESOURCE:
+		ret = USB_NO_RESOURCES;
+		break;
+	default:
+		ret = USB_HC_HARDWARE_ERROR;
+		xhci_log(xhcip, "!unexpected error when configuring enpoints: "
+		    "%d", code);
+		break;
+	}
+done:
+	xhci_command_fini(&co);
+	return (ret);
+}
+
+int
+xhci_command_evaluate_context(xhci_t *xhcip, xhci_device_t *xd)
+{
+	int ret, code;
+	xhci_command_t co;
+
+	VERIFY(xhcip != NULL);
+	VERIFY(xd != NULL);
+
+	xhci_command_init(&co);
+	co.xco_req.trb_addr = LE_64(xhci_dma_pa(&xd->xd_ictx));
+	co.xco_req.trb_status = LE_32(0);
+	co.xco_req.trb_flags = LE_32(XHCI_CMD_EVAL_CTX |
+	    XHCI_TRB_SET_SLOT(xd->xd_slot));
+
+	ret = xhci_command_submit(xhcip, &co);
+	if (ret != 0)
+		goto done;
+	code = XHCI_TRB_GET_CODE(co.xco_res.trb_status);
+	switch (code) {
+	case XHCI_CODE_SUCCESS:
+		ret = USB_SUCCESS;
+		break;
+	case XHCI_CODE_CMD_ABORTED:
+		ret = USB_CR_TIMEOUT;
+		break;
+	case XHCI_CODE_SLOT_NOT_ON:
+		xhci_log(xhcip, "!failed to evaluate endpoints for slot %d, "
+		    "slot not on, likely driver bug!", xd->xd_slot);
+		ret = USB_FAILURE;
+		break;
+	default:
+		ret = USB_HC_HARDWARE_ERROR;
+		xhci_log(xhcip, "!unexpected error when evaluating enpoints: "
+		    "%d", code);
+		break;
+	}
+done:
+	xhci_command_fini(&co);
+	return (ret);
+
+}
+
+int
+xhci_command_reset_endpoint(xhci_t *xhcip, xhci_device_t *xd,
+    xhci_endpoint_t *xep)
+{
+	int ret, code;
+	xhci_command_t co;
+
+	VERIFY(xhcip != NULL);
+	VERIFY(xd != NULL);
+	VERIFY(xep != NULL);
+
+	xhci_command_init(&co);
+
+	co.xco_req.trb_addr = LE_64(0);
+	co.xco_req.trb_status = LE_32(0);
+	co.xco_req.trb_flags = LE_32(XHCI_CMD_RESET_EP |
+	    XHCI_TRB_SET_SLOT(xd->xd_slot) |
+	    XHCI_TRB_SET_EP(xep->xep_num + 1));
+
+	ret = xhci_command_submit(xhcip, &co);
+	if (ret != 0)
+		goto done;
+
+	code = XHCI_TRB_GET_CODE(co.xco_res.trb_status);
+	switch (code) {
+	case XHCI_CODE_SUCCESS:
+		ret = USB_SUCCESS;
+		break;
+	case XHCI_CODE_CMD_ABORTED:
+		ret = USB_CR_TIMEOUT;
+		break;
+	case XHCI_CODE_CONTEXT_STATE:
+	case XHCI_CODE_SLOT_NOT_ON:
+		xhci_log(xhcip, "!xhci reset endpoint command: asked to modify "
+		    "endpoint (%u)/slot (%d) in wrong state: %d", xep->xep_num,
+		    xd->xd_slot, code);
+		if (code == XHCI_CODE_CONTEXT_STATE) {
+			xhci_endpoint_context_t *epctx;
+
+			epctx = xd->xd_endout[xep->xep_num];
+			xhci_log(xhcip, "!endpoint is in state %d",
+			    XHCI_EPCTX_STATE(epctx->xec_info));
+		}
+		ret = USB_INVALID_CONTEXT;
+		break;
+	default:
+		ret = USB_HC_HARDWARE_ERROR;
+		xhci_log(xhcip, "!unexpected error when resetting enpoint: %d",
+		    code);
+		break;
+	}
+
+done:
+	xhci_command_fini(&co);
+	return (ret);
+}
+
+int
+xhci_command_set_tr_dequeue(xhci_t *xhcip, xhci_device_t *xd,
+    xhci_endpoint_t *xep)
+{
+	uint64_t pa;
+	int ret, code;
+	xhci_command_t co;
+	xhci_ring_t *xrp;
+
+	VERIFY(xhcip != NULL);
+	VERIFY(xd != NULL);
+	VERIFY(xep != NULL);
+
+	xhci_command_init(&co);
+
+	xrp = &xep->xep_ring;
+	pa = xhci_dma_pa(&xrp->xr_dma) + sizeof (xhci_trb_t) * xrp->xr_tail;
+	pa |= xrp->xr_cycle;
+	co.xco_req.trb_addr = LE_64(pa);
+	co.xco_req.trb_status = LE_32(0);
+	co.xco_req.trb_flags = LE_32(XHCI_CMD_SET_TR_DEQ |
+	    XHCI_TRB_SET_SLOT(xd->xd_slot) |
+	    XHCI_TRB_SET_EP(xep->xep_num + 1));
+
+	ret = xhci_command_submit(xhcip, &co);
+	if (ret != 0)
+		goto done;
+
+	code = XHCI_TRB_GET_CODE(co.xco_res.trb_status);
+	switch (code) {
+	case XHCI_CODE_SUCCESS:
+		ret = USB_SUCCESS;
+		break;
+	case XHCI_CODE_CMD_ABORTED:
+		ret = USB_CR_TIMEOUT;
+		break;
+	case XHCI_CODE_CONTEXT_STATE:
+	case XHCI_CODE_SLOT_NOT_ON:
+		xhci_log(xhcip, "!xhci set tr dequeue command: asked to modify "
+		    "endpoint (%u)/slot (%d) in wrong state: %d", xep->xep_num,
+		    xd->xd_slot, code);
+		if (code == XHCI_CODE_CONTEXT_STATE) {
+			xhci_endpoint_context_t *epctx;
+
+			epctx = xd->xd_endout[xep->xep_num];
+			xhci_log(xhcip, "!endpoint is in state %d",
+			    XHCI_EPCTX_STATE(epctx->xec_info));
+		}
+		ret = USB_INVALID_CONTEXT;
+		break;
+	default:
+		ret = USB_HC_HARDWARE_ERROR;
+		xhci_log(xhcip, "!unexpected error when resetting enpoint: %d",
+		    code);
+		break;
+	}
+
+done:
+	xhci_command_fini(&co);
+	return (ret);
+
+}
+
+int
+xhci_command_stop_endpoint(xhci_t *xhcip, xhci_device_t *xd,
+    xhci_endpoint_t *xep)
+{
+	int ret, code;
+	xhci_command_t co;
+
+	VERIFY(xhcip != NULL);
+	VERIFY(xd != NULL);
+	VERIFY(xep != NULL);
+
+	xhci_command_init(&co);
+
+	co.xco_req.trb_addr = LE_64(0);
+	co.xco_req.trb_status = LE_32(0);
+	co.xco_req.trb_flags = LE_32(XHCI_CMD_STOP_EP |
+	    XHCI_TRB_SET_SLOT(xd->xd_slot) |
+	    XHCI_TRB_SET_EP(xep->xep_num + 1));
+
+	ret = xhci_command_submit(xhcip, &co);
+	if (ret != 0)
+		goto done;
+
+	code = XHCI_TRB_GET_CODE(co.xco_res.trb_status);
+	switch (code) {
+	case XHCI_CODE_SUCCESS:
+		ret = USB_SUCCESS;
+		break;
+	case XHCI_CODE_CMD_ABORTED:
+		ret = USB_CR_TIMEOUT;
+		break;
+	case XHCI_CODE_CONTEXT_STATE:
+	case XHCI_CODE_SLOT_NOT_ON:
+		xhci_log(xhcip, "!xhci stop endpoint command (%d)/slot "
+		    "(%u) in wrong state: %d", xep->xep_num, xd->xd_slot,
+		    code);
+		if (code == XHCI_CODE_CONTEXT_STATE) {
+			xhci_endpoint_context_t *epctx;
+
+			epctx = xd->xd_endout[xep->xep_num];
+			xhci_log(xhcip, "!endpoint is in state %d",
+			    XHCI_EPCTX_STATE(epctx->xec_info));
+		}
+		ret = USB_INVALID_CONTEXT;
+		break;
+	default:
+		ret = USB_HC_HARDWARE_ERROR;
+		xhci_log(xhcip, "!unexpected error when resetting enpoint: %d",
+		    code);
+		break;
+	}
+
+done:
+	xhci_command_fini(&co);
+	return (ret);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci_context.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,251 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * Device Context Base Address Array (DCBAA) Management and Scratchpad
+ * management. This is also used to manage the device slot contexts in shared
+ * memory.
+ *
+ * Please see the big theory statement in xhci.c for more information.
+ */
+
+#include <sys/usb/hcd/xhci/xhci.h>
+#include <sys/byteorder.h>
+
+static void
+xhci_scratchpad_fini(xhci_t *xhcip)
+{
+	xhci_scratchpad_t *xsp = &xhcip->xhci_scratchpad;
+
+	if (xsp->xsp_scratch_dma != NULL) {
+		int i, npages;
+		npages = xhcip->xhci_caps.xcap_max_scratch;
+		for (i = 0; i < npages; i++) {
+			xhci_dma_free(&xsp->xsp_scratch_dma[i]);
+		}
+		kmem_free(xsp->xsp_scratch_dma,
+		    sizeof (xhci_dma_buffer_t) * npages);
+		xsp->xsp_scratch_dma = NULL;
+	}
+	xhci_dma_free(&xsp->xsp_addr_dma);
+	xsp->xsp_addrs = NULL;
+}
+
+void
+xhci_context_fini(xhci_t *xhcip)
+{
+	xhci_scratchpad_fini(xhcip);
+	xhci_dma_free(&xhcip->xhci_dcbaa.xdc_dma);
+	xhcip->xhci_dcbaa.xdc_base_addrs = NULL;
+}
+
+static int
+xhci_scratchpad_alloc(xhci_t *xhcip)
+{
+	int npages, i;
+	xhci_scratchpad_t *xsp;
+	ddi_device_acc_attr_t acc;
+	ddi_dma_attr_t attr;
+
+	/*
+	 * First allocate the scratchpad table, then the actual pages.
+	 */
+	ASSERT(xhcip->xhci_caps.xcap_max_scratch > 0);
+	npages = xhcip->xhci_caps.xcap_max_scratch;
+	xhci_dma_acc_attr(xhcip, &acc);
+	xhci_dma_dma_attr(xhcip, &attr);
+	xsp = &xhcip->xhci_scratchpad;
+	if (xhci_dma_alloc(xhcip, &xsp->xsp_addr_dma, &attr, &acc,
+	    B_TRUE, sizeof (uint64_t) * npages, B_FALSE) == B_FALSE) {
+		xhci_log(xhcip, "!failed to allocate DMA memory for device "
+		    "context");
+		return (ENOMEM);
+	}
+
+	xsp->xsp_addrs = (void *)xsp->xsp_addr_dma.xdb_va;
+
+	/*
+	 * Note that the scratchpad memory itself can actually be relaxed, which
+	 * is almost better, since we'll never actually access this memory
+	 * ourselves, only use it to tear things down. As such, we also bump up
+	 * the segment boundary restrictions, since we don't really have any for
+	 * this memory.
+	 */
+	xhci_dma_scratchpad_attr(xhcip, &attr);
+	xsp->xsp_scratch_dma = kmem_zalloc(sizeof (xhci_dma_buffer_t) * npages,
+	    KM_SLEEP);
+	for (i = 0; i < npages; i++) {
+		if (xhci_dma_alloc(xhcip, &xsp->xsp_scratch_dma[i], &attr, &acc,
+		    B_TRUE, xhcip->xhci_caps.xcap_pagesize, B_FALSE) ==
+		    B_FALSE) {
+			/*
+			 * It is safe for us to call xhci_scratchpad_fini() in a
+			 * partially constructed state. Because we've zeroed the
+			 * structures in the above allocation, the DMA buffer
+			 * teardown code can handle these zeroed or partially
+			 * initialized structures correctly.
+			 */
+			xhci_scratchpad_fini(xhcip);
+			xhci_log(xhcip, "!failed to allocate DMA memory for "
+			    "device scratchpad");
+			return (ENOMEM);
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * We always allocate the DCBAA based on its maximum possible size, simplifying
+ * the code and at worst wasting only a couple hundred bytes.
+ */
+static int
+xhci_dcbaa_alloc(xhci_t *xhcip)
+{
+	xhci_dcbaa_t *dcb;
+	ddi_device_acc_attr_t acc;
+	ddi_dma_attr_t attr;
+
+	dcb = &xhcip->xhci_dcbaa;
+	xhci_dma_acc_attr(xhcip, &acc);
+	xhci_dma_dma_attr(xhcip, &attr);
+	if (xhci_dma_alloc(xhcip, &dcb->xdc_dma, &attr, &acc,
+	    B_FALSE, sizeof (uint64_t) * XHCI_MAX_SLOTS, B_FALSE) == B_FALSE) {
+		xhci_log(xhcip, "!failed to allocate DMA memory for device "
+		    "context");
+		return (ENOMEM);
+	}
+
+	/*
+	 * This lint gag is safe, because we always have at least a 64-byte
+	 * alignment from the DMA attributes.
+	 */
+	/* LINTED: E_BAD_PTR_CAST_ALIGN */
+	dcb->xdc_base_addrs = (uint64_t *)dcb->xdc_dma.xdb_va;
+	return (0);
+}
+
+/*
+ * We are called to initialize the DCBAA every time that we start the
+ * controller. This happens both the first time we bring it up and after we
+ * reset it from errors. Therefore to initialize the DCBAA we need to do the
+ * following:
+ *
+ *   o Allocate DMA memory (if it doesn't already exist)
+ *   o If scratchpad slots have been requested, allocate and program them if
+ *     necessary
+ *   o Program the DCBAAP register.
+ */
+int
+xhci_context_init(xhci_t *xhcip)
+{
+	int ret;
+	xhci_dcbaa_t *dcb = &xhcip->xhci_dcbaa;
+
+	if (dcb->xdc_base_addrs == NULL) {
+		if ((ret = xhci_dcbaa_alloc(xhcip)) != 0)
+			return (ret);
+	}
+
+	bzero(dcb->xdc_base_addrs, sizeof (uint64_t) * XHCI_MAX_SLOTS);
+	if (xhcip->xhci_caps.xcap_max_scratch != 0) {
+		int i, npages;
+		xhci_scratchpad_t *xsp = &xhcip->xhci_scratchpad;
+
+		if (xsp->xsp_addrs == NULL &&
+		    (ret = xhci_scratchpad_alloc(xhcip)) != 0) {
+			xhci_context_fini(xhcip);
+			return (ret);
+		}
+
+		dcb->xdc_base_addrs[XHCI_DCBAA_SCRATCHPAD_INDEX] =
+		    LE_64(xhci_dma_pa(&xsp->xsp_addr_dma));
+
+		npages = xhcip->xhci_caps.xcap_max_scratch;
+		for (i = 0; i < npages; i++) {
+			xsp->xsp_addrs[i] =
+			    LE_64(xhci_dma_pa(&xsp->xsp_scratch_dma[i]));
+		}
+
+		XHCI_DMA_SYNC(xsp->xsp_addr_dma, DDI_DMA_SYNC_FORDEV);
+		if (xhci_check_dma_handle(xhcip, &xsp->xsp_addr_dma) !=
+		    DDI_FM_OK) {
+			ddi_fm_service_impact(xhcip->xhci_dip,
+			    DDI_SERVICE_LOST);
+			return (EIO);
+		}
+	}
+
+	XHCI_DMA_SYNC(dcb->xdc_dma, DDI_DMA_SYNC_FORDEV);
+	if (xhci_check_dma_handle(xhcip, &dcb->xdc_dma) != DDI_FM_OK) {
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (EIO);
+	}
+
+	xhci_put64(xhcip, XHCI_R_OPER, XHCI_DCBAAP,
+	    LE_64(xhci_dma_pa(&dcb->xdc_dma)));
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		ddi_fm_service_impact(xhcip->xhci_dip,
+		    DDI_SERVICE_LOST);
+		return (EIO);
+	}
+
+	return (0);
+}
+
+/*
+ * Initialize the default output context. It should already have been zeroed, so
+ * all we need to do is insert it into the right place in the device context
+ * array.
+ */
+boolean_t
+xhci_context_slot_output_init(xhci_t *xhcip, xhci_device_t *xd)
+{
+	xhci_dcbaa_t *dcb = &xhcip->xhci_dcbaa;
+	VERIFY(xd->xd_slot > 0 &&
+	    xd->xd_slot <= xhcip->xhci_caps.xcap_max_slots);
+
+	xhcip->xhci_dcbaa.xdc_base_addrs[xd->xd_slot] =
+	    LE_64(xhci_dma_pa(&xd->xd_octx));
+	XHCI_DMA_SYNC(dcb->xdc_dma, DDI_DMA_SYNC_FORDEV);
+	if (xhci_check_dma_handle(xhcip, &dcb->xdc_dma) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to initialize slot output context "
+		    "for device on port %d, slot %d: fatal FM error "
+		    "synchronizing DCBAA slot DMA memory", xd->xd_slot,
+		    xd->xd_port);
+		xhci_fm_runtime_reset(xhcip);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+void
+xhci_context_slot_output_fini(xhci_t *xhcip, xhci_device_t *xd)
+{
+	xhci_dcbaa_t *dcb = &xhcip->xhci_dcbaa;
+	VERIFY(xd->xd_slot > 0 &&
+	    xd->xd_slot <= xhcip->xhci_caps.xcap_max_slots);
+
+	xhcip->xhci_dcbaa.xdc_base_addrs[xd->xd_slot] = 0ULL;
+	XHCI_DMA_SYNC(dcb->xdc_dma, DDI_DMA_SYNC_FORDEV);
+	if (xhci_check_dma_handle(xhcip, &dcb->xdc_dma) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to finalize slot output context "
+		    "for device on port %d, slot %d: fatal FM error "
+		    "synchronizing DCBAA slot DMA memory", xd->xd_slot,
+		    xd->xd_port);
+		xhci_fm_runtime_reset(xhcip);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci_dma.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,535 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * xHCI DMA Management Routines
+ *
+ * Please see the big theory statement in xhci.c for more information.
+ */
+
+#include <sys/usb/hcd/xhci/xhci.h>
+
+int
+xhci_check_dma_handle(xhci_t *xhcip, xhci_dma_buffer_t *xdb)
+{
+	ddi_fm_error_t de;
+
+	if (!DDI_FM_DMA_ERR_CAP(xhcip->xhci_fm_caps))
+		return (0);
+
+	ddi_fm_dma_err_get(xdb->xdb_dma_handle, &de, DDI_FME_VERSION);
+	return (de.fme_status);
+}
+
+void
+xhci_dma_acc_attr(xhci_t *xhcip, ddi_device_acc_attr_t *accp)
+{
+	accp->devacc_attr_version = DDI_DEVICE_ATTR_V0;
+	accp->devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
+	accp->devacc_attr_dataorder = DDI_STRICTORDER_ACC;
+
+	if (DDI_FM_DMA_ERR_CAP(xhcip->xhci_fm_caps)) {
+		accp->devacc_attr_access = DDI_FLAGERR_ACC;
+	} else {
+		accp->devacc_attr_access = DDI_DEFAULT_ACC;
+	}
+}
+
+/*
+ * These are DMA attributes that we assign when making a transfer. The SGL is
+ * variable and based on the caller, which varies based on the type of transfer
+ * we're doing.
+ */
+void
+xhci_dma_transfer_attr(xhci_t *xhcip, ddi_dma_attr_t *attrp, uint_t sgl)
+{
+	VERIFY3U(sgl, >, 0);
+	VERIFY3U(sgl, <=, XHCI_TRANSFER_DMA_SGL);
+	attrp->dma_attr_version = DMA_ATTR_V0;
+
+	/*
+	 * The range of data that we can use is based on what hardware supports.
+	 */
+	attrp->dma_attr_addr_lo = 0x0;
+	if (xhcip->xhci_caps.xcap_flags & XCAP_AC64) {
+		attrp->dma_attr_addr_hi = UINT64_MAX;
+	} else {
+		attrp->dma_attr_addr_hi = UINT32_MAX;
+	}
+
+	/*
+	 * The count max indicates the total amount that will fit into one
+	 * cookie, which is one TRB in our world. In other words 64k.
+	 */
+	attrp->dma_attr_count_max = XHCI_TRB_MAX_TRANSFER;
+
+	/*
+	 * The alignment and segment are related. The alignment describes the
+	 * alignment of the PA. The segment describes a boundary that the DMA
+	 * allocation cannot cross. In other words, for a given chunk of memory
+	 * it cannot cross a 64-byte boundary. However, the physical address
+	 * only needs to be aligned to align bytes.
+	 */
+	attrp->dma_attr_align = XHCI_DMA_ALIGN;
+	attrp->dma_attr_seg = XHCI_TRB_MAX_TRANSFER - 1;
+
+
+	attrp->dma_attr_burstsizes = 0xfff;
+
+	/*
+	 * This is the maximum we can send. Technically this is limited by the
+	 * descriptors and not by hardware, hence why we use a large value for
+	 * the max that'll be less than any memory allocation we ever throw at
+	 * it.
+	 */
+	attrp->dma_attr_minxfer = 0x1;
+	attrp->dma_attr_maxxfer = UINT32_MAX;
+
+	/*
+	 * This is determined by the caller.
+	 */
+	attrp->dma_attr_sgllen = sgl;
+
+	/*
+	 * The granularity describes the addressing granularity. e.g. can things
+	 * ask for chunks in units of this number of bytes. For PCI this should
+	 * always be one.
+	 */
+	attrp->dma_attr_granular = 1;
+
+	if (DDI_FM_DMA_ERR_CAP(xhcip->xhci_fm_caps)) {
+		attrp->dma_attr_flags = DDI_DMA_FLAGERR;
+	} else {
+		attrp->dma_attr_flags = 0;
+	}
+}
+
+/*
+ * This routine tries to create DMA attributes for normal allocations for data
+ * structures and the like. By default we use the same values as the transfer
+ * attributes, but have explicit comments about how they're different.
+ */
+void
+xhci_dma_dma_attr(xhci_t *xhcip, ddi_dma_attr_t *attrp)
+{
+	/*
+	 * Note, we always use a single SGL for these DMA allocations as these
+	 * are used for small data structures.
+	 */
+	xhci_dma_transfer_attr(xhcip, attrp, XHCI_DEF_DMA_SGL);
+
+	/*
+	 * The maximum size of any of these structures is 4k as opposed to the
+	 * 64K max described above. Similarly the boundary requirement is
+	 * reduced to 4k.
+	 */
+	attrp->dma_attr_count_max = xhcip->xhci_caps.xcap_pagesize;
+	attrp->dma_attr_maxxfer = xhcip->xhci_caps.xcap_pagesize;
+	attrp->dma_attr_seg = xhcip->xhci_caps.xcap_pagesize - 1;
+}
+
+/*
+ * Fill in attributes for a scratchpad entry. The scratchpad entries are
+ * somewhat different in so far as they are closest to a normal DMA attribute,
+ * except they have stricter alignments, needing to be page sized.
+ *
+ * In addition, because we never access this memory ourselves, we can just mark
+ * it all as relaxed ordering.
+ */
+void
+xhci_dma_scratchpad_attr(xhci_t *xhcip, ddi_dma_attr_t *attrp)
+{
+	xhci_dma_dma_attr(xhcip, attrp);
+	attrp->dma_attr_align = xhcip->xhci_caps.xcap_pagesize;
+	attrp->dma_attr_flags |= DDI_DMA_RELAXED_ORDERING;
+}
+
+/*
+ * This should be used for the simple case of a single SGL entry, which is the
+ * vast majority of the non-transfer allocations.
+ */
+uint64_t
+xhci_dma_pa(xhci_dma_buffer_t *xdb)
+{
+	ASSERT(xdb->xdb_ncookies == 1);
+	return (xdb->xdb_cookies[0].dmac_laddress);
+}
+
+void
+xhci_dma_free(xhci_dma_buffer_t *xdb)
+{
+	if (xdb->xdb_ncookies != 0) {
+		VERIFY(xdb->xdb_dma_handle != NULL);
+		(void) ddi_dma_unbind_handle(xdb->xdb_dma_handle);
+		xdb->xdb_ncookies = 0;
+		bzero(xdb->xdb_cookies, sizeof (ddi_dma_cookie_t) *
+		    XHCI_TRANSFER_DMA_SGL);
+		xdb->xdb_len = 0;
+	}
+
+	if (xdb->xdb_acc_handle != NULL) {
+		ddi_dma_mem_free(&xdb->xdb_acc_handle);
+		xdb->xdb_acc_handle = NULL;
+		xdb->xdb_va = NULL;
+	}
+
+	if (xdb->xdb_dma_handle != NULL) {
+		ddi_dma_free_handle(&xdb->xdb_dma_handle);
+		xdb->xdb_dma_handle = NULL;
+	}
+
+	ASSERT(xdb->xdb_va == NULL);
+	ASSERT(xdb->xdb_ncookies == 0);
+	ASSERT(xdb->xdb_cookies[0].dmac_laddress == 0);
+	ASSERT(xdb->xdb_len == 0);
+}
+
+boolean_t
+xhci_dma_alloc(xhci_t *xhcip, xhci_dma_buffer_t *xdb,
+    ddi_dma_attr_t *attrp, ddi_device_acc_attr_t *accp, boolean_t zero,
+    size_t size, boolean_t wait)
+{
+	int ret, i;
+	uint_t flags = DDI_DMA_CONSISTENT;
+	size_t len;
+	ddi_dma_cookie_t cookie;
+	uint_t ncookies;
+	int (*memcb)(caddr_t);
+
+	if (wait == B_TRUE) {
+		memcb = DDI_DMA_SLEEP;
+	} else {
+		memcb = DDI_DMA_DONTWAIT;
+	}
+
+	ret = ddi_dma_alloc_handle(xhcip->xhci_dip, attrp, memcb, NULL,
+	    &xdb->xdb_dma_handle);
+	if (ret != 0) {
+		xhci_log(xhcip, "!failed to allocate DMA handle: %d", ret);
+		xdb->xdb_dma_handle = NULL;
+		return (B_FALSE);
+	}
+
+	ret = ddi_dma_mem_alloc(xdb->xdb_dma_handle, size, accp, flags, memcb,
+	    NULL, &xdb->xdb_va, &len, &xdb->xdb_acc_handle);
+	if (ret != DDI_SUCCESS) {
+		xhci_log(xhcip, "!failed to allocate DMA memory: %d", ret);
+		xdb->xdb_va = NULL;
+		xdb->xdb_acc_handle = NULL;
+		xhci_dma_free(xdb);
+		return (B_FALSE);
+	}
+
+	if (zero == B_TRUE)
+		bzero(xdb->xdb_va, len);
+
+	ret = ddi_dma_addr_bind_handle(xdb->xdb_dma_handle, NULL,
+	    xdb->xdb_va, len, DDI_DMA_RDWR | flags, memcb, NULL, &cookie,
+	    &ncookies);
+	if (ret != 0) {
+		xhci_log(xhcip, "!failed to bind DMA memory: %d", ret);
+		xhci_dma_free(xdb);
+		return (B_FALSE);
+	}
+
+	/*
+	 * Note we explicitly store the logical length of this allocation. The
+	 * physical length is available via the cookies.
+	 */
+	xdb->xdb_len = size;
+	xdb->xdb_ncookies = ncookies;
+	xdb->xdb_cookies[0] = cookie;
+	for (i = 1; i < ncookies; i++) {
+		ddi_dma_nextcookie(xdb->xdb_dma_handle, &xdb->xdb_cookies[i]);
+	}
+
+
+	return (B_TRUE);
+}
+
+void
+xhci_transfer_free(xhci_t *xhcip, xhci_transfer_t *xt)
+{
+	if (xt == NULL)
+		return;
+
+	VERIFY(xhcip != NULL);
+	xhci_dma_free(&xt->xt_buffer);
+	if (xt->xt_isoc != NULL) {
+		ASSERT(xt->xt_ntrbs > 0);
+		kmem_free(xt->xt_isoc, sizeof (usb_isoc_pkt_descr_t) *
+		    xt->xt_ntrbs);
+		xt->xt_isoc = NULL;
+	}
+	if (xt->xt_trbs != NULL) {
+		ASSERT(xt->xt_ntrbs > 0);
+		kmem_free(xt->xt_trbs, sizeof (xhci_trb_t) * xt->xt_ntrbs);
+		xt->xt_trbs = NULL;
+	}
+	kmem_free(xt, sizeof (xhci_transfer_t));
+}
+
+xhci_transfer_t *
+xhci_transfer_alloc(xhci_t *xhcip, xhci_endpoint_t *xep, size_t size, int trbs,
+    int usb_flags)
+{
+	int kmflags;
+	boolean_t dmawait;
+	xhci_transfer_t *xt;
+	ddi_device_acc_attr_t acc;
+	ddi_dma_attr_t attr;
+
+	if (usb_flags & USB_FLAGS_SLEEP) {
+		kmflags = KM_SLEEP;
+		dmawait = B_TRUE;
+	} else {
+		kmflags = KM_NOSLEEP;
+		dmawait = B_FALSE;
+	}
+
+	xt = kmem_zalloc(sizeof (xhci_transfer_t), kmflags);
+	if (xt == NULL)
+		return (NULL);
+
+	if (size != 0) {
+		int sgl = XHCI_DEF_DMA_SGL;
+
+		/*
+		 * For BULK transfers, we always increase the number of SGL
+		 * entries that we support to make things easier for the kernel.
+		 * However, for control transfers, we currently opt to keep
+		 * things a bit simpler and use our default of one SGL.  There's
+		 * no good technical reason for this, rather it just keeps
+		 * things a bit easier.
+		 *
+		 * To simplify things, we don't use additional SGL entries for
+		 * ISOC transfers. While this isn't the best, it isn't too far
+		 * off from what ehci and co. have done before. If this becomes
+		 * a technical issue, it's certainly possible to increase the
+		 * SGL entry count.
+		 */
+		if (xep->xep_type == USB_EP_ATTR_BULK)
+			sgl = XHCI_TRANSFER_DMA_SGL;
+
+		xhci_dma_acc_attr(xhcip, &acc);
+		xhci_dma_transfer_attr(xhcip, &attr, sgl);
+		if (xhci_dma_alloc(xhcip, &xt->xt_buffer, &attr, &acc, B_FALSE,
+		    size, dmawait) == B_FALSE) {
+			kmem_free(xt, sizeof (xhci_transfer_t));
+			return (NULL);
+		}
+
+		/*
+		 * ISOC transfers are a bit special and don't need additional
+		 * TRBs for data.
+		 */
+		if (xep->xep_type != USB_EP_ATTR_ISOCH)
+			trbs += xt->xt_buffer.xdb_ncookies;
+	}
+
+	xt->xt_trbs = kmem_zalloc(sizeof (xhci_trb_t) * trbs, kmflags);
+	if (xt->xt_trbs == NULL) {
+		xhci_dma_free(&xt->xt_buffer);
+		kmem_free(xt, sizeof (xhci_transfer_t));
+		return (NULL);
+	}
+
+	/*
+	 * For ISOCH transfers, we need to also allocate the results data.
+	 */
+	if (xep->xep_type == USB_EP_ATTR_ISOCH) {
+		xt->xt_isoc = kmem_zalloc(sizeof (usb_isoc_pkt_descr_t) * trbs,
+		    kmflags);
+		if (xt->xt_isoc == NULL) {
+			kmem_free(xt->xt_trbs, sizeof (xhci_trb_t) * trbs);
+			xhci_dma_free(&xt->xt_buffer);
+			kmem_free(xt, sizeof (xhci_transfer_t));
+			return (NULL);
+		}
+	}
+
+	xt->xt_ntrbs = trbs;
+	xt->xt_cr = USB_CR_OK;
+
+	return (xt);
+}
+
+/*
+ * Abstract the notion of copying out to handle the case of multiple DMA
+ * cookies. If tobuf is true, we are copying to the kernel provided buffer,
+ * otherwise we're copying into the DMA memory.
+ */
+void
+xhci_transfer_copy(xhci_transfer_t *xt, void *buf, size_t len,
+    boolean_t tobuf)
+{
+	void *dmabuf = xt->xt_buffer.xdb_va;
+	if (tobuf == B_TRUE)
+		bcopy(dmabuf, buf, len);
+	else
+		bcopy(buf, dmabuf, len);
+}
+
+int
+xhci_transfer_sync(xhci_t *xhcip, xhci_transfer_t *xt, uint_t type)
+{
+	XHCI_DMA_SYNC(xt->xt_buffer, type);
+	return (xhci_check_dma_handle(xhcip, &xt->xt_buffer));
+}
+
+/*
+ * We're required to try and inform the xHCI controller about the number of data
+ * packets that are required. The algorithm to use is described in xHCI 1.1 /
+ * 4.11.2.4. While it might be tempting to just try and calculate the number of
+ * packets based on simple rounding of the remaining number of bytes, that
+ * misses a critical problem -- DMA boundaries may cause us to need additional
+ * packets that are missed initially. Consider a transfer made up of four
+ * different DMA buffers sized in bytes: 4096, 4096, 256, 256, with a 512 byte
+ * packet size.
+ *
+ * Remain	4608	512	256	0
+ * Bytes	4096	4096	256	256
+ * Naive TD	9	1	1	0
+ * Act TD 	10	2	1	0
+ *
+ * This means that the only safe way forward here is to work backwards and see
+ * how many we need to work up to this point.
+ */
+static int
+xhci_transfer_get_tdsize(xhci_transfer_t *xt, uint_t off, uint_t mps)
+{
+	int i;
+	uint_t npkt = 0;
+
+	/*
+	 * There are always zero packets for the last TRB.
+	 */
+	ASSERT(xt->xt_buffer.xdb_ncookies > 0);
+	for (i = xt->xt_buffer.xdb_ncookies - 1; i > off; i--) {
+		size_t len;
+
+		/*
+		 * The maximum value we can return is 31 packets. So, in that
+		 * case we short-circuit and return.
+		 */
+		if (npkt >= 31)
+			return (31);
+
+		len = roundup(xt->xt_buffer.xdb_cookies[i].dmac_size, mps);
+		npkt += len / mps;
+	}
+
+	return (npkt);
+}
+
+void
+xhci_transfer_trb_fill_data(xhci_endpoint_t *xep, xhci_transfer_t *xt, int off,
+    boolean_t in)
+{
+	uint_t mps, tdsize, flags;
+	int i;
+
+	VERIFY(xt->xt_buffer.xdb_ncookies > 0);
+	VERIFY(xep->xep_pipe != NULL);
+	VERIFY(off + xt->xt_buffer.xdb_ncookies <= xt->xt_ntrbs);
+	mps = xep->xep_pipe->p_ep.wMaxPacketSize;
+
+	for (i = 0; i < xt->xt_buffer.xdb_ncookies; i++) {
+		uint64_t pa, dmasz;
+
+		pa = xt->xt_buffer.xdb_cookies[i].dmac_laddress;
+		dmasz = xt->xt_buffer.xdb_cookies[i].dmac_size;
+
+		tdsize = xhci_transfer_get_tdsize(xt, i, mps);
+
+		flags = XHCI_TRB_TYPE_NORMAL;
+		if (i == 0 && xep->xep_type == USB_EP_ATTR_CONTROL) {
+			flags = XHCI_TRB_TYPE_DATA;
+			if (in == B_TRUE)
+				flags |= XHCI_TRB_DIR_IN;
+		}
+
+		/*
+		 * When reading data in (from the device), we may get shorter
+		 * transfers than the buffer allowed for. To make sure we get
+		 * notified about that and handle that, we need to set the ISP
+		 * flag.
+		 */
+		if (in == B_TRUE) {
+			flags |= XHCI_TRB_ISP;
+			xt->xt_data_tohost = B_TRUE;
+		}
+
+		/*
+		 * When we have more than one cookie, we are technically
+		 * chaining together things according to the controllers view,
+		 * hence why we need to set the chain flag.
+		 */
+		if (xt->xt_buffer.xdb_ncookies > 1 &&
+		    i != (xt->xt_buffer.xdb_ncookies - 1)) {
+			flags |= XHCI_TRB_CHAIN;
+		}
+
+		/*
+		 * If we have a non-control transfer, then we need to make sure
+		 * that we set ourselves up to be interrupted, which we set for
+		 * the last entry.
+		 */
+		if (i + 1 == xt->xt_buffer.xdb_ncookies &&
+		    xep->xep_type != USB_EP_ATTR_CONTROL) {
+			flags |= XHCI_TRB_IOC;
+		}
+
+		xt->xt_trbs[off + i].trb_addr = LE_64(pa);
+		xt->xt_trbs[off + i].trb_status = LE_32(XHCI_TRB_LEN(dmasz) |
+		    XHCI_TRB_TDREM(tdsize) | XHCI_TRB_INTR(0));
+		xt->xt_trbs[off + i].trb_flags = LE_32(flags);
+	}
+}
+
+/*
+ * These are utility functions for isochronus transfers to help calculate the
+ * transfer burst count (TBC) and transfer last burst packet count (TLPBC)
+ * entries for an isochronus entry. See xHCI 1.1 / 4.11.2.3 for how to calculate
+ * them.
+ */
+void
+xhci_transfer_calculate_isoc(xhci_device_t *xd, xhci_endpoint_t *xep,
+    uint_t trb_len, uint_t *tbc, uint_t *tlbpc)
+{
+	uint_t mps, tdpc, burst;
+
+	/*
+	 * Even if we're asked to send no data, that actually requires the
+	 * equivalent of sending one byte of data.
+	 */
+	if (trb_len == 0)
+		trb_len = 1;
+
+	mps = XHCI_EPCTX_GET_MPS(xd->xd_endout[xep->xep_num]->xec_info2);
+	burst = XHCI_EPCTX_GET_MAXB(xd->xd_endout[xep->xep_num]->xec_info2);
+
+	/*
+	 * This is supposed to correspond to the Transfer Descriptor Packet
+	 * Count from xHCI 1.1 / 4.14.1.
+	 */
+	tdpc = howmany(trb_len, mps);
+	*tbc = howmany(tdpc, burst + 1) - 1;
+
+	if ((tdpc % (burst + 1)) == 0)
+		*tlbpc = burst;
+	else
+		*tlbpc = (tdpc % (burst + 1)) - 1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci_endpoint.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,1496 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * xHCI Endpoint Initialization and Management
+ *
+ * Please see the big theory statement in xhci.c for more information.
+ */
+
+#include <sys/usb/hcd/xhci/xhci.h>
+#include <sys/sdt.h>
+
+boolean_t
+xhci_endpoint_is_periodic_in(xhci_endpoint_t *xep)
+{
+	usba_pipe_handle_data_t *ph;
+
+	ASSERT(xep != NULL);
+	ph = xep->xep_pipe;
+	ASSERT(ph != NULL);
+
+	return ((xep->xep_type == USB_EP_ATTR_INTR ||
+	    xep->xep_type == USB_EP_ATTR_ISOCH) &&
+	    (ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN);
+}
+
+/*
+ * Endpoints are a bit weirdly numbered. Endpoint zero is the default control
+ * endpoint, so the direction doesn't matter. For all the others, they're
+ * arranged as ep 1 out, ep 1 in, ep 2 out, ep 2 in. This is based on the layout
+ * of the Device Context Structure in xHCI 1.1 / 6.2.1. Therefore to go from the
+ * endpoint and direction, we know that endpoint n starts at 2n - 1.  e.g.
+ * endpoint 1 starts at entry 1, endpoint 2 at entry 3, etc. Finally, the OUT
+ * direction comes first, followed by the IN direction. So if we're getting the
+ * endpoint for one of those, then we have to deal with that.
+ */
+uint_t
+xhci_endpoint_pipe_to_epid(usba_pipe_handle_data_t *ph)
+{
+	int ep;
+
+	ep = ph->p_ep.bEndpointAddress & USB_EP_NUM_MASK;
+	if (ep == 0)
+		return (ep);
+	ep = ep * 2 - 1;
+	if ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN)
+		ep++;
+
+	VERIFY(ep < XHCI_NUM_ENDPOINTS);
+	return (ep);
+}
+
+/*
+ * The assumption is that someone calling this owns this endpoint / device and
+ * that it's in a state where it's safe to zero out that information.
+ */
+void
+xhci_endpoint_fini(xhci_device_t *xd, int endpoint)
+{
+	xhci_endpoint_t *xep = xd->xd_endpoints[endpoint];
+
+	VERIFY(xep != NULL);
+	xd->xd_endpoints[endpoint] = NULL;
+
+	xhci_ring_free(&xep->xep_ring);
+	cv_destroy(&xep->xep_state_cv);
+	list_destroy(&xep->xep_transfers);
+	kmem_free(xep, sizeof (xhci_endpoint_t));
+}
+
+/*
+ * Set up the default control endpoint input context. This needs to be done
+ * before we address the device. Note, we separate out the default endpoint from
+ * others, as we must set this up before we have a pipe handle.
+ */
+int
+xhci_endpoint_setup_default_context(xhci_t *xhcip, xhci_device_t *xd,
+    xhci_endpoint_t *xep)
+{
+	uint_t mps;
+	xhci_endpoint_context_t *ectx;
+	uint64_t deq;
+
+	ectx = xd->xd_endin[xep->xep_num];
+	VERIFY(ectx != NULL);
+
+	/*
+	 * We may or may not have a device descriptor. This should match the
+	 * same initial sizes that are done in hubd_create_child().
+	 *
+	 * Note, since we don't necessarily have an endpoint descriptor yet to
+	 * base this on we instead use the device's defaults if available. This
+	 * is different from normal endpoints for which there's always a
+	 * specific descriptor.
+	 */
+	switch (xd->xd_usbdev->usb_port_status) {
+	case USBA_LOW_SPEED_DEV:
+		if (xd->xd_usbdev->usb_dev_descr != NULL) {
+			mps = xd->xd_usbdev->usb_dev_descr->bMaxPacketSize0;
+		} else {
+			mps = 8;
+		}
+		break;
+	case USBA_FULL_SPEED_DEV:
+	case USBA_HIGH_SPEED_DEV:
+		if (xd->xd_usbdev->usb_dev_descr != NULL) {
+			mps = xd->xd_usbdev->usb_dev_descr->bMaxPacketSize0;
+		} else {
+			mps = 64;
+		}
+		break;
+	case USBA_SUPER_SPEED_DEV:
+	default:
+		if (xd->xd_usbdev->usb_dev_descr != NULL) {
+			mps = xd->xd_usbdev->usb_dev_descr->bMaxPacketSize0;
+			mps = 1 << mps;
+		} else {
+			mps = 512;
+		}
+		break;
+	}
+
+	bzero(ectx, sizeof (xhci_endpoint_context_t));
+	ectx->xec_info = LE_32(0);
+	ectx->xec_info2 = LE_32(XHCI_EPCTX_SET_CERR(3) |
+	    XHCI_EPCTX_SET_EPTYPE(XHCI_EPCTX_TYPE_CTRL) |
+	    XHCI_EPCTX_SET_MAXB(0) | XHCI_EPCTX_SET_MPS(mps));
+	deq = xhci_dma_pa(&xep->xep_ring.xr_dma) + sizeof (xhci_trb_t) *
+	    xep->xep_ring.xr_tail;
+	ectx->xec_dequeue = LE_64(deq | xep->xep_ring.xr_cycle);
+	ectx->xec_txinfo = LE_32(XHCI_EPCTX_MAX_ESIT_PAYLOAD(0) |
+	    XHCI_EPCTX_AVG_TRB_LEN(XHCI_CONTEXT_DEF_CTRL_ATL));
+
+	XHCI_DMA_SYNC(xd->xd_ictx, DDI_DMA_SYNC_FORDEV);
+	if (xhci_check_dma_handle(xhcip, &xd->xd_ictx) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to initialize default device input "
+		    "context on slot %d and port %d for endpoint %u:  "
+		    "encountered fatal FM error synchronizing input context "
+		    "DMA memory", xd->xd_slot, xd->xd_port, xep->xep_num);
+		xhci_fm_runtime_reset(xhcip);
+		return (EIO);
+	}
+
+	return (0);
+}
+
+/*
+ * Determine if we need to update the maximum packet size of the default
+ * control endpoint. This may happen because we start with the default size
+ * before we have a descriptor and then it may change. For example, with
+ * full-speed devices that may have either an 8 or 64 byte maximum packet size.
+ */
+int
+xhci_endpoint_update_default(xhci_t *xhcip, xhci_device_t *xd,
+    xhci_endpoint_t *xep)
+{
+	int mps, desc, info, ret;
+	ASSERT(xd->xd_usbdev != NULL);
+
+	mps = XHCI_EPCTX_GET_MPS(xd->xd_endout[xep->xep_num]->xec_info2);
+	desc = xd->xd_usbdev->usb_dev_descr->bMaxPacketSize0;
+	if (xd->xd_usbdev->usb_port_status >= USBA_SUPER_SPEED_DEV) {
+		desc = 1 << desc;
+	}
+
+	if (mps == desc)
+		return (USB_SUCCESS);
+
+	/*
+	 * Update only the context for the default control endpoint.
+	 */
+	mutex_enter(&xd->xd_imtx);
+	info = LE_32(xd->xd_endout[xep->xep_num]->xec_info2);
+	info &= ~XHCI_EPCTX_SET_MPS(mps);
+	info |= XHCI_EPCTX_SET_MPS(desc);
+	xd->xd_endin[xep->xep_num]->xec_info2 = LE_32(info);
+	xd->xd_input->xic_drop_flags = LE_32(0);
+	xd->xd_input->xic_add_flags = LE_32(XHCI_INCTX_MASK_DCI(1));
+
+	ret = xhci_command_evaluate_context(xhcip, xd);
+	mutex_exit(&xd->xd_imtx);
+
+	return (ret);
+}
+
+static uint_t
+xhci_endpoint_epdesc_to_type(usb_ep_descr_t *ep)
+{
+	int type = ep->bmAttributes & USB_EP_ATTR_MASK;
+	boolean_t in = (ep->bEndpointAddress & USB_EP_DIR_MASK) ==
+	    USB_EP_DIR_IN;
+
+	switch (type) {
+	case USB_EP_ATTR_CONTROL:
+		return (XHCI_EPCTX_TYPE_CTRL);
+	case USB_EP_ATTR_ISOCH:
+		if (in == B_TRUE)
+			return (XHCI_EPCTX_TYPE_ISOCH_IN);
+		return (XHCI_EPCTX_TYPE_ISOCH_OUT);
+	case USB_EP_ATTR_BULK:
+		if (in == B_TRUE)
+			return (XHCI_EPCTX_TYPE_BULK_IN);
+		return (XHCI_EPCTX_TYPE_BULK_OUT);
+	case USB_EP_ATTR_INTR:
+		if (in == B_TRUE)
+			return (XHCI_EPCTX_TYPE_INTR_IN);
+		return (XHCI_EPCTX_TYPE_INTR_OUT);
+	default:
+		panic("bad USB attribute type: %d", type);
+	}
+
+	/* LINTED: E_FUNC_NO_RET_VAL */
+}
+
+static uint_t
+xhci_endpoint_determine_burst(xhci_device_t *xd, xhci_endpoint_t *xep)
+{
+	switch (xd->xd_usbdev->usb_port_status) {
+	case USBA_LOW_SPEED_DEV:
+	case USBA_FULL_SPEED_DEV:
+		/*
+		 * Per xHCI 1.1 / 6.2.3.4, burst is always zero for these
+		 * devices.
+		 */
+		return (0);
+	case USBA_HIGH_SPEED_DEV:
+		if (xep->xep_type == USB_EP_ATTR_CONTROL ||
+		    xep->xep_type == USB_EP_ATTR_BULK)
+			return (0);
+		return ((xep->xep_pipe->p_xep.uex_ep.wMaxPacketSize &
+		    XHCI_CONTEXT_BURST_MASK) >> XHCI_CONTEXT_BURST_SHIFT);
+	default:
+		/*
+		 * For these USB >= 3.0, this comes from the companion
+		 * descriptor.
+		 */
+		ASSERT(xep->xep_pipe->p_xep.uex_flags & USB_EP_XFLAGS_SS_COMP);
+		return (xep->xep_pipe->p_xep.uex_ep_ss.bMaxBurst);
+	}
+}
+
+/*
+ * Convert a linear mapping of values that are in in the range of 1-255 into a
+ * 2^x value. Because we're supposed to round down for these calculations (see
+ * the note in xHCI 1.1 / 6.2.3.6) we can do this simply with a fls() and
+ * subtracting one.
+ */
+static uint_t
+xhci_endpoint_linear_interval(usb_ep_descr_t *ep)
+{
+	int exp;
+	int ival = ep->bInterval;
+	if (ival < 1)
+		ival = 1;
+	if (ival > 255)
+		ival = 255;
+	exp = ddi_fls(ival) - 1;
+	ASSERT(exp >= 0 && exp <= 7);
+	return (exp);
+}
+
+/*
+ * Convert the set of values that use a 2^(x-1) value for interval into a 2^x
+ * range. Note the valid input range is 1-16, so we clamp values based on this.
+ * See xHCI 1.1 / 6.2.3.6 for more information.
+ */
+static uint_t
+xhci_endpoint_exponential_interval(usb_ep_descr_t *ep)
+{
+	int ival;
+
+	ival = ep->bInterval;
+	if (ival < 1)
+		ival = 1;
+	if (ival > 16)
+		ival = 16;
+	ival--;
+	ASSERT(ival >= 0 && ival <= 15);
+	return (ival);
+}
+
+
+/*
+ * Determining the interval is unfortunately somewhat complicated as there are
+ * many differnet forms that things can take. This is all summarized in a
+ * somewhat helpful table, number 65, in xHCI 1.1 / 6.2.3.6. But here's
+ * basically the six different cases we have to consider:
+ *
+ * Case 1: Non-High Speed Bulk and Control Endpoints
+ * 	Always return 0.
+ *
+ * Case 2: Super Speed and High Speed Isoch and Intr endpoints
+ * 	Convert from a 2^(x-1) range to a 2^x range.
+ *
+ * Case 3: Full Speed Isochronous Endpoints
+ * 	As case 2, but add 3 as its values are in frames and we need to convert
+ * 	to microframes. Adding three to the result is the same as multiplying
+ * 	the initial value by 8.
+ *
+ * Case 4: Full speed and Low Speed Interrupt Endpoints
+ * 	These have a 1-255 ms range that we need to convert to a 2^x * 128 us
+ * 	range. We use the linear conversion and then add 3 to account for the
+ * 	multiplying by 8 conversion from frames to microframes.
+ *
+ * Case 5: High Speed Interrupt and Bulk Output
+ * 	These are a bit of a weird case. The spec and other implementations make
+ * 	it seem that it's similar to case 4, but without the fixed addition as
+ * 	its interpreted differently due to NAKs.
+ *
+ * Case 6: Low Speed Isochronous Endpoints
+ * 	These are not actually defined; however, like other implementations we
+ * 	treat them like case 4.
+ */
+static uint_t
+xhci_endpoint_interval(xhci_device_t *xd, usb_ep_descr_t *ep)
+{
+	int type = ep->bmAttributes & USB_EP_ATTR_MASK;
+	int speed = xd->xd_usbdev->usb_port_status;
+
+	/*
+	 * Handle Cases 1 and 5 first.
+	 */
+	if (type == USB_EP_ATTR_CONTROL || type == USB_EP_ATTR_BULK) {
+		if (speed != USBA_HIGH_SPEED_DEV)
+			return (0);
+		return (xhci_endpoint_linear_interval(ep));
+	}
+
+	/*
+	 * Handle Isoch and Intr cases next.
+	 */
+	switch (speed) {
+	case USBA_LOW_SPEED_DEV:
+		/*
+		 * Interrupt endpoints at low speed are the same as full speed,
+		 * hence the fall through.
+		 */
+		if (type == USB_EP_ATTR_ISOCH) {
+			return (xhci_endpoint_exponential_interval(ep) + 3);
+		}
+		/* FALLTHROUGH */
+	case USBA_FULL_SPEED_DEV:
+		return (xhci_endpoint_linear_interval(ep) + 3);
+	case USBA_HIGH_SPEED_DEV:
+	case USBA_SUPER_SPEED_DEV:
+	default:
+		/*
+		 * Case 2. Treat any newer and faster speeds as Super Speed by
+		 * default as USB 3.1 is effectively treated the same here.
+		 */
+		return (xhci_endpoint_exponential_interval(ep));
+	}
+}
+
+/*
+ * The way to calculate the Maximum ESIT is described in xHCI 1.1 / 4.14.2.
+ * First off, this only applies to Interrupt and Isochronous descriptors. For
+ * Super Speed and newer things, it comes out of a descriptor. Otherwise we
+ * calculate it by doing 'Max Packet Size' * ('Max Burst' + 1).
+ */
+static uint_t
+xhci_endpoint_max_esit(xhci_device_t *xd, xhci_endpoint_t *xep, uint_t mps,
+    uint_t burst)
+{
+	if (xep->xep_type == USB_EP_ATTR_CONTROL ||
+	    xep->xep_type == USB_EP_ATTR_BULK) {
+		return (0);
+	}
+
+	/*
+	 * Note that this will need to be updated for SuperSpeedPlus ISOC
+	 * devices to pull from the secondary companion descriptor they use.
+	 */
+	if (xd->xd_usbdev->usb_port_status >= USBA_SUPER_SPEED_DEV) {
+		usb_ep_xdescr_t *ep_xdesc = &xep->xep_pipe->p_xep;
+		ASSERT(xep->xep_pipe->p_xep.uex_flags & USB_EP_XFLAGS_SS_COMP);
+		return (ep_xdesc->uex_ep_ss.wBytesPerInterval);
+	}
+
+	return (mps * (burst + 1));
+}
+
+/*
+ * We've been asked to calculate and tell the xHCI controller an average TRB
+ * data length. This is talked about in an implementation note in xHCI 1.1 /
+ * 4.14.1.1. So, the reality is that it's hard to actually calculate this, as
+ * we're supposed to take into account all of the TRBs that we use on that ring.
+ *
+ * Surveying other xHCI drivers, they all agree on using the default of 8 for
+ * control endpoints; however, from there things get a little more fluid. For
+ * interrupt and isochronous endpoints, many device use the minimum of the max
+ * packet size and the device's pagesize. For bulk endpoints some folks punt and
+ * don't set anything and others try and set it to the pagesize. The xHCI
+ * implementation note suggests a 3k size here initially. For now, we'll just
+ * guess for bulk endpoints and use our page size as a determining factor for
+ * this and use the BSD style for others. Note Linux here only sets this value
+ * for control devices.
+ */
+static uint_t
+xhci_endpoint_avg_trb(xhci_t *xhcip, usb_ep_descr_t *ep, int mps)
+{
+	int type = ep->bmAttributes & USB_EP_ATTR_MASK;
+
+	switch (type) {
+	case USB_EP_ATTR_ISOCH:
+	case USB_EP_ATTR_INTR:
+		return (MIN(xhcip->xhci_caps.xcap_pagesize, mps));
+	case USB_EP_ATTR_CONTROL:
+		return (XHCI_CONTEXT_DEF_CTRL_ATL);
+	case USB_EP_ATTR_BULK:
+		return (xhcip->xhci_caps.xcap_pagesize);
+	default:
+		panic("bad USB endpoint type: %d", type);
+	}
+
+	/* LINTED: E_FUNC_NO_RET_VAL */
+}
+
+int
+xhci_endpoint_setup_context(xhci_t *xhcip, xhci_device_t *xd,
+    xhci_endpoint_t *xep)
+{
+	uint_t eptype, burst, ival, max_esit, avgtrb, mps, mult, cerr;
+	xhci_endpoint_context_t *ectx;
+	uint64_t deq;
+
+	/*
+	 * For a USB >=3.0 device we should always have its companion descriptor
+	 * provided for us by USBA. If it's not here, complain loudly and fail.
+	 */
+	if (xd->xd_usbdev->usb_port_status >= USBA_SUPER_SPEED_DEV &&
+	    (xep->xep_pipe->p_xep.uex_flags & USB_EP_XFLAGS_SS_COMP) == 0) {
+		const char *prod, *mfg;
+
+		prod = xd->xd_usbdev->usb_product_str;
+		if (prod == NULL)
+			prod = "Unknown Device";
+		mfg = xd->xd_usbdev->usb_mfg_str;
+		if (mfg == NULL)
+			mfg = "Unknown Manufacturer";
+
+		xhci_log(xhcip, "Encountered USB >=3.0 device without endpoint "
+		    "companion descriptor. Ensure driver %s is properly using "
+		    "usb_pipe_xopen() for device %s %s",
+		    ddi_driver_name(xd->xd_usbdev->usb_dip), prod, mfg);
+		return (EINVAL);
+	}
+
+	ectx = xd->xd_endin[xep->xep_num];
+	VERIFY(ectx != NULL);
+	VERIFY(xd->xd_usbdev->usb_dev_descr != NULL);
+	VERIFY(xep->xep_pipe != NULL);
+
+	mps = xep->xep_pipe->p_ep.wMaxPacketSize & XHCI_CONTEXT_MPS_MASK;
+	mult = XHCI_CONTEXT_DEF_MULT;
+	cerr = XHCI_CONTEXT_DEF_CERR;
+
+	switch (xep->xep_type) {
+	case USB_EP_ATTR_ISOCH:
+		/*
+		 * When we have support for USB 3.1 SuperSpeedPlus devices,
+		 * we'll need to make sure that we also check for its secondary
+		 * endpoint companion descriptor here.
+		 */
+		/*
+		 * Super Speed devices nominally have these xHCI super speed
+		 * companion descriptors. We know that we're not properly
+		 * grabbing them right now, so until we do, we should basically
+		 * error about it.
+		 */
+		if (xd->xd_usbdev->usb_port_status >= USBA_SUPER_SPEED_DEV) {
+			ASSERT(xep->xep_pipe->p_xep.uex_flags &
+			    USB_EP_XFLAGS_SS_COMP);
+			mult = xep->xep_pipe->p_xep.uex_ep_ss.bmAttributes &
+			    USB_EP_SS_COMP_ISOC_MULT_MASK;
+		}
+
+		mps &= XHCI_CONTEXT_MPS_MASK;
+		cerr = XHCI_CONTEXT_ISOCH_CERR;
+		break;
+	default:
+		/*
+		 * No explicit changes needed for CONTROL, INTR, and BULK
+		 * endpoints. They've been handled already and don't have any
+		 * differences.
+		 */
+		break;
+	}
+
+	eptype = xhci_endpoint_epdesc_to_type(&xep->xep_pipe->p_xep.uex_ep);
+	burst = xhci_endpoint_determine_burst(xd, xep);
+	ival = xhci_endpoint_interval(xd, &xep->xep_pipe->p_xep.uex_ep);
+	max_esit = xhci_endpoint_max_esit(xd, xep, mps, burst);
+	avgtrb = xhci_endpoint_avg_trb(xhcip, &xep->xep_pipe->p_xep.uex_ep,
+	    mps);
+
+	/*
+	 * The multi field may be reserved as zero if the LEC feature flag is
+	 * set. See the description of mult in xHCI 1.1 / 6.2.3.
+	 */
+	if (xhcip->xhci_caps.xcap_flags2 & XCAP2_LEC)
+		mult = 0;
+
+	bzero(ectx, sizeof (xhci_endpoint_context_t));
+
+	ectx->xec_info = LE_32(XHCI_EPCTX_SET_MULT(mult) |
+	    XHCI_EPCTX_SET_IVAL(ival));
+	if (xhcip->xhci_caps.xcap_flags2 & XCAP2_LEC)
+		ectx->xec_info |= LE_32(XHCI_EPCTX_SET_MAX_ESIT_HI(max_esit));
+
+	ectx->xec_info2 = LE_32(XHCI_EPCTX_SET_CERR(cerr) |
+	    XHCI_EPCTX_SET_EPTYPE(eptype) | XHCI_EPCTX_SET_MAXB(burst) |
+	    XHCI_EPCTX_SET_MPS(mps));
+
+	deq = xhci_dma_pa(&xep->xep_ring.xr_dma) + sizeof (xhci_trb_t) *
+	    xep->xep_ring.xr_tail;
+	ectx->xec_dequeue = LE_64(deq | xep->xep_ring.xr_cycle);
+
+	ectx->xec_txinfo = LE_32(XHCI_EPCTX_MAX_ESIT_PAYLOAD(max_esit) |
+	    XHCI_EPCTX_AVG_TRB_LEN(avgtrb));
+
+	XHCI_DMA_SYNC(xd->xd_ictx, DDI_DMA_SYNC_FORDEV);
+	if (xhci_check_dma_handle(xhcip, &xd->xd_ictx) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to initialize device input "
+		    "context on slot %d and port %d for endpoint %u:  "
+		    "encountered fatal FM error synchronizing input context "
+		    "DMA memory", xd->xd_slot, xd->xd_port, xep->xep_num);
+		xhci_fm_runtime_reset(xhcip);
+		return (EIO);
+	}
+
+	return (0);
+}
+
+/*
+ * Initialize the endpoint and its input context for a given device. This is
+ * called from two different contexts:
+ *
+ *   1. Initializing a device
+ *   2. Opening a USB pipe
+ *
+ * In the second case, we need to worry about locking around the device. We
+ * don't need to worry about the locking in the first case because the USBA
+ * doesn't know about it yet.
+ */
+int
+xhci_endpoint_init(xhci_t *xhcip, xhci_device_t *xd,
+    usba_pipe_handle_data_t *ph)
+{
+	int ret;
+	uint_t epid;
+	xhci_endpoint_t *xep;
+
+	if (ph == NULL) {
+		epid = XHCI_DEFAULT_ENDPOINT;
+	} else {
+		ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+		epid = xhci_endpoint_pipe_to_epid(ph);
+	}
+	VERIFY(xd->xd_endpoints[epid] == NULL);
+
+	xep = kmem_zalloc(sizeof (xhci_endpoint_t), KM_SLEEP);
+	list_create(&xep->xep_transfers, sizeof (xhci_transfer_t),
+	    offsetof(xhci_transfer_t, xt_link));
+	cv_init(&xep->xep_state_cv, NULL, CV_DRIVER, NULL);
+	xep->xep_xd = xd;
+	xep->xep_xhci = xhcip;
+	xep->xep_num = epid;
+	if (ph == NULL) {
+		xep->xep_pipe = NULL;
+		xep->xep_type = USB_EP_ATTR_CONTROL;
+	} else {
+		xep->xep_pipe = ph;
+		xep->xep_type = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
+	}
+
+	if ((ret = xhci_ring_alloc(xhcip, &xep->xep_ring)) != 0) {
+		cv_destroy(&xep->xep_state_cv);
+		list_destroy(&xep->xep_transfers);
+		kmem_free(xep, sizeof (xhci_endpoint_t));
+		return (ret);
+	}
+
+	if ((ret = xhci_ring_reset(xhcip, &xep->xep_ring)) != 0) {
+		xhci_ring_free(&xep->xep_ring);
+		cv_destroy(&xep->xep_state_cv);
+		list_destroy(&xep->xep_transfers);
+		kmem_free(xep, sizeof (xhci_endpoint_t));
+		return (ret);
+	}
+
+	xd->xd_endpoints[epid] = xep;
+	if (ph == NULL) {
+		ret = xhci_endpoint_setup_default_context(xhcip, xd, xep);
+	} else {
+		ret = xhci_endpoint_setup_context(xhcip, xd, xep);
+	}
+	if (ret != 0) {
+		xhci_endpoint_fini(xd, xep->xep_num);
+		return (ret);
+	}
+
+	return (0);
+}
+
+/*
+ * Attempt to quiesce an endpoint. Depending on the state of the endpoint, we
+ * may need to simply stop it. Alternatively, we may need to explicitly reset
+ * the endpoint. Once done, this endpoint should be stopped and can be
+ * manipulated.
+ */
+int
+xhci_endpoint_quiesce(xhci_t *xhcip, xhci_device_t *xd, xhci_endpoint_t *xep)
+{
+	int ret = USB_SUCCESS;
+	xhci_endpoint_context_t *epctx = xd->xd_endout[xep->xep_num];
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+	ASSERT(xep->xep_state & XHCI_ENDPOINT_QUIESCE);
+
+	/*
+	 * First attempt to stop the endpoint, unless it's halted. We don't
+	 * really care what state it is in. Note that because other activity
+	 * could be going on, the state may change on us; however, if it's
+	 * running, it will always transition to a stopped state and none of the
+	 * other valid states will allow transitions without us taking an active
+	 * action.
+	 */
+	if (!(xep->xep_state & XHCI_ENDPOINT_HALTED)) {
+		mutex_exit(&xhcip->xhci_lock);
+		ret = xhci_command_stop_endpoint(xhcip, xd, xep);
+		mutex_enter(&xhcip->xhci_lock);
+
+		if (ret == USB_INVALID_CONTEXT) {
+			XHCI_DMA_SYNC(xd->xd_octx, DDI_DMA_SYNC_FORKERNEL);
+		}
+	}
+
+	/*
+	 * Now, if we had the HALTED flag set or we failed to stop it due to a
+	 * context error and we're in the HALTED state now, reset the end point.
+	 */
+	if ((xep->xep_state & XHCI_ENDPOINT_HALTED) ||
+	    (ret == USB_INVALID_CONTEXT &&
+	    XHCI_EPCTX_STATE(LE_32(epctx->xec_info)) == XHCI_EP_HALTED)) {
+		mutex_exit(&xhcip->xhci_lock);
+		ret = xhci_command_reset_endpoint(xhcip, xd, xep);
+		mutex_enter(&xhcip->xhci_lock);
+	}
+
+	/*
+	 * Ideally, one of the two commands should have worked; however, we
+	 * could have had a context error due to being in the wrong state.
+	 * Verify that we're either in the ERROR or STOPPED state and treat both
+	 * as success. All callers are assumed to be doing this so they can
+	 * change the dequeue pointer.
+	 */
+	if (ret != USB_SUCCESS && ret != USB_INVALID_CONTEXT) {
+		return (ret);
+	}
+
+	if (ret == USB_INVALID_CONTEXT) {
+		XHCI_DMA_SYNC(xd->xd_octx, DDI_DMA_SYNC_FORKERNEL);
+
+		switch (XHCI_EPCTX_STATE(LE_32(epctx->xec_info))) {
+		case XHCI_EP_STOPPED:
+		case XHCI_EP_ERROR:
+			/*
+			 * This is where we wanted to go, so let's just take it.
+			 */
+			ret = USB_SUCCESS;
+			break;
+		case XHCI_EP_DISABLED:
+		case XHCI_EP_RUNNING:
+		case XHCI_EP_HALTED:
+		default:
+			/*
+			 * If we're in any of these, something really weird has
+			 * happened and it's not worth trying to recover at this
+			 * point.
+			 */
+			xhci_error(xhcip, "!asked to stop endpoint %u on slot "
+			    "%d and port %d: ended up in unexpected state %d",
+			    xep->xep_num, xd->xd_slot, xd->xd_port,
+			    XHCI_EPCTX_STATE(LE_32(epctx->xec_info)));
+			return (ret);
+		}
+	}
+
+	/*
+	 * Now that we're successful, we can clear any possible halted state
+	 * tracking that we might have had.
+	 */
+	if (ret == USB_SUCCESS) {
+		xep->xep_state &= ~XHCI_ENDPOINT_HALTED;
+	}
+
+	return (ret);
+}
+
+int
+xhci_endpoint_ring(xhci_t *xhcip, xhci_device_t *xd, xhci_endpoint_t *xep)
+{
+	/*
+	 * The doorbell ID's are offset by one from the endpoint numbers that we
+	 * keep.
+	 */
+	xhci_put32(xhcip, XHCI_R_DOOR, XHCI_DOORBELL(xd->xd_slot),
+	    xep->xep_num + 1);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to ring doorbell for slot %d and "
+		    "endpoint %u: encountered fatal FM register access error",
+		    xd->xd_slot, xep->xep_num);
+		xhci_fm_runtime_reset(xhcip);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	DTRACE_PROBE3(xhci__doorbell__ring, xhci_t *, xhcip, uint32_t,
+	    XHCI_DOORBELL(xd->xd_slot), uint32_t, xep->xep_num + 1);
+
+	return (USB_SUCCESS);
+}
+
+static void
+xhci_endpoint_tick(void *arg)
+{
+	int ret;
+	xhci_transfer_t *xt;
+	xhci_endpoint_t *xep = arg;
+	xhci_device_t *xd = xep->xep_xd;
+	xhci_t *xhcip = xep->xep_xhci;
+
+	mutex_enter(&xhcip->xhci_lock);
+
+	/*
+	 * If we have the teardown flag set, then this is going away, don't try
+	 * to do anything. Also, if somehow a periodic endpoint has something
+	 * scheduled, just quit now and don't bother.
+	 */
+	if (xep->xep_state & (XHCI_ENDPOINT_TEARDOWN |
+	    XHCI_ENDPOINT_PERIODIC)) {
+		xep->xep_timeout = 0;
+		mutex_exit(&xhcip->xhci_lock);
+		return;
+	}
+
+	/*
+	 * If something else has already kicked off, something potentially
+	 * dangerous, just don't bother waiting for it and reschedule.
+	 */
+	if (xep->xep_state & XHCI_ENDPOINT_DONT_SCHEDULE) {
+		xep->xep_timeout = timeout(xhci_endpoint_tick, xep,
+		    drv_usectohz(XHCI_TICK_TIMEOUT_US));
+		mutex_exit(&xhcip->xhci_lock);
+		return;
+	}
+
+	/*
+	 * At this point, we have an endpoint that we need to consider. See if
+	 * there are any transfers on it, if none, we're done. If so, check if
+	 * we have exceeded the timeout. If we have, then we have some work to
+	 * do.
+	 */
+	xt = list_head(&xep->xep_transfers);
+	if (xt == NULL) {
+		xep->xep_timeout = 0;
+		mutex_exit(&xhcip->xhci_lock);
+		return;
+	}
+
+	if (xt->xt_timeout > 0) {
+		xt->xt_timeout--;
+		xep->xep_timeout = timeout(xhci_endpoint_tick, xep,
+		    drv_usectohz(XHCI_TICK_TIMEOUT_US));
+		mutex_exit(&xhcip->xhci_lock);
+		return;
+	}
+
+	/*
+	 * This item has timed out. We need to stop the ring and take action.
+	 */
+	xep->xep_state |= XHCI_ENDPOINT_TIMED_OUT | XHCI_ENDPOINT_QUIESCE;
+	ret = xhci_endpoint_quiesce(xhcip, xd, xep);
+	if (ret != USB_SUCCESS) {
+		/*
+		 * If we fail to quiesce during the timeout, then remove the
+		 * state flags and hopefully we'll be able to the next time
+		 * around or if a reset or polling stop comes in, maybe it can
+		 * deal with it.
+		 */
+		xep->xep_state &= ~(XHCI_ENDPOINT_QUIESCE |
+		    XHCI_ENDPOINT_TIMED_OUT);
+		xep->xep_timeout = timeout(xhci_endpoint_tick, xep,
+		    drv_usectohz(XHCI_TICK_TIMEOUT_US));
+		mutex_exit(&xhcip->xhci_lock);
+		cv_broadcast(&xep->xep_state_cv);
+		xhci_error(xhcip, "failed to successfully quiesce timed out "
+		    "endpoint %u of device on slot %d and port %d: device "
+		    "remains timed out", xep->xep_num, xd->xd_slot,
+		    xd->xd_port);
+		return;
+	}
+
+	xhci_ring_skip_transfer(&xep->xep_ring, xt);
+	(void) list_remove_head(&xep->xep_transfers);
+	mutex_exit(&xhcip->xhci_lock);
+
+	/*
+	 * At this point, we try and set the ring's dequeue pointer. If this
+	 * fails, we're left in an awkward state. We've already adjusted the
+	 * ring and removed the transfer. All we can really do is go through and
+	 * return the transfer and hope that they perhaps attempt to reset the
+	 * ring and that will succeed at this point. Based on everything we've
+	 * done to set things up, it'd be odd if this did fail.
+	 */
+	ret = xhci_command_set_tr_dequeue(xhcip, xd, xep);
+	mutex_enter(&xhcip->xhci_lock);
+	xep->xep_state &= ~XHCI_ENDPOINT_QUIESCE;
+	if (ret == USB_SUCCESS) {
+		xep->xep_state &= ~XHCI_ENDPOINT_TIMED_OUT;
+	} else {
+		xhci_error(xhcip, "failed to successfully set transfer ring "
+		    "dequeue pointer of timed out endpoint %u of "
+		    "device on slot %d and port %d: device remains timed out, "
+		    "please use cfgadm to recover", xep->xep_num, xd->xd_slot,
+		    xd->xd_port);
+	}
+	xep->xep_timeout = timeout(xhci_endpoint_tick, xep,
+	    drv_usectohz(XHCI_TICK_TIMEOUT_US));
+	mutex_exit(&xhcip->xhci_lock);
+	cv_broadcast(&xep->xep_state_cv);
+
+	/*
+	 * Because we never time out periodic related activity, we will always
+	 * have the request on the transfer.
+	 */
+	ASSERT(xt->xt_usba_req != NULL);
+	usba_hcdi_cb(xep->xep_pipe, xt->xt_usba_req, USB_CR_TIMEOUT);
+	xhci_transfer_free(xhcip, xt);
+}
+
+/*
+ * We've been asked to schedule a series of frames onto the specified endpoint.
+ * We need to make sure that there is enough room, at which point we can queue
+ * it and then ring the door bell. Note that we queue in reverse order to make
+ * sure that if the ring moves on, it won't see the correct cycle bit.
+ */
+int
+xhci_endpoint_schedule(xhci_t *xhcip, xhci_device_t *xd, xhci_endpoint_t *xep,
+    xhci_transfer_t *xt, boolean_t ring)
+{
+	int i;
+	xhci_ring_t *rp = &xep->xep_ring;
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+	ASSERT(xt->xt_ntrbs > 0);
+	ASSERT(xt->xt_trbs != NULL);
+
+	if ((xep->xep_state & XHCI_ENDPOINT_DONT_SCHEDULE) != 0)
+		return (USB_FAILURE);
+
+	if (xhci_ring_trb_space(rp, xt->xt_ntrbs) == B_FALSE)
+		return (USB_NO_RESOURCES);
+
+	for (i = xt->xt_ntrbs - 1; i > 0; i--) {
+		xhci_ring_trb_fill(rp, i, &xt->xt_trbs[i], B_TRUE);
+	}
+	xhci_ring_trb_fill(rp, 0U, &xt->xt_trbs[0], B_FALSE);
+
+	XHCI_DMA_SYNC(rp->xr_dma, DDI_DMA_SYNC_FORDEV);
+	xhci_ring_trb_produce(rp, xt->xt_ntrbs);
+	list_insert_tail(&xep->xep_transfers, xt);
+
+	XHCI_DMA_SYNC(rp->xr_dma, DDI_DMA_SYNC_FORDEV);
+	if (xhci_check_dma_handle(xhcip, &rp->xr_dma) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to write out TRB for device on slot "
+		    "%d, port %d, and endpoint %u: encountered fatal FM error "
+		    "synchronizing ring DMA memory", xd->xd_slot, xd->xd_port,
+		    xep->xep_num);
+		xhci_fm_runtime_reset(xhcip);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	if (xep->xep_timeout == 0 &&
+	    !(xep->xep_state & XHCI_ENDPOINT_PERIODIC)) {
+		xep->xep_timeout = timeout(xhci_endpoint_tick, xep,
+		    drv_usectohz(XHCI_TICK_TIMEOUT_US));
+	}
+
+	xt->xt_sched_time = gethrtime();
+
+	if (ring == B_FALSE)
+		return (USB_SUCCESS);
+
+	return (xhci_endpoint_ring(xhcip, xd, xep));
+}
+
+static xhci_transfer_t *
+xhci_endpoint_determine_transfer(xhci_t *xhcip, xhci_endpoint_t *xep,
+    xhci_trb_t *trb, int *offp)
+{
+	xhci_transfer_t *xt;
+
+	ASSERT(xhcip != NULL);
+	ASSERT(offp != NULL);
+	ASSERT(xep != NULL);
+	ASSERT(trb != NULL);
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+
+	if ((xt = list_head(&xep->xep_transfers)) == NULL)
+		return (NULL);
+
+	*offp = xhci_ring_trb_valid_range(&xep->xep_ring, LE_64(trb->trb_addr),
+	    xt->xt_ntrbs);
+	if (*offp == -1)
+		return (NULL);
+	return (xt);
+}
+
+static void
+xhci_endpoint_reschedule_periodic(xhci_t *xhcip, xhci_device_t *xd,
+    xhci_endpoint_t *xep, xhci_transfer_t *xt)
+{
+	int ret;
+	xhci_pipe_t *xp = (xhci_pipe_t *)xep->xep_pipe->p_hcd_private;
+	xhci_periodic_pipe_t *xpp = &xp->xp_periodic;
+
+	ASSERT3U(xpp->xpp_tsize, >, 0);
+
+	xt->xt_short = 0;
+	xt->xt_cr = USB_CR_OK;
+
+	mutex_enter(&xhcip->xhci_lock);
+
+	/*
+	 * If we don't have an active poll, then we shouldn't bother trying to
+	 * reschedule it. This means that we're trying to stop or we ran out of
+	 * memory.
+	 */
+	if (xpp->xpp_poll_state != XHCI_PERIODIC_POLL_ACTIVE) {
+		mutex_exit(&xhcip->xhci_lock);
+		return;
+	}
+
+	if (xep->xep_type == USB_EP_ATTR_ISOCH) {
+		int i;
+		for (i = 0; i < xt->xt_ntrbs; i++) {
+			xt->xt_isoc[i].isoc_pkt_actual_length =
+			    xt->xt_isoc[i].isoc_pkt_length;
+			xt->xt_isoc[i].isoc_pkt_status = USB_CR_OK;
+		}
+	}
+
+	/*
+	 * In general, there should always be space on the ring for this. The
+	 * only reason that rescheduling an existing transfer for a periodic
+	 * endpoint wouldn't work is because we have a hardware error, at which
+	 * point we're going to be going down hard anyways. We log and bump a
+	 * stat here to make this case discoverable in case our assumptions our
+	 * wrong.
+	 */
+	ret = xhci_endpoint_schedule(xhcip, xd, xep, xt, B_TRUE);
+	if (ret != 0) {
+		xhci_log(xhcip, "!failed to reschedule periodic endpoint %u "
+		    "(type %u) on slot %d: %d\n", xep->xep_num, xep->xep_type,
+		    xd->xd_slot, ret);
+	}
+	mutex_exit(&xhcip->xhci_lock);
+}
+
+/*
+ * We're dealing with a message on a control endpoint. This may be a default
+ * endpoint or otherwise. These usually come in groups of 3+ TRBs where you have
+ * a setup stage, data stage (which may have one or more other TRBs) and then a
+ * final status stage.
+ *
+ * We generally set ourselves up such that we get interrupted and notified only
+ * on the status stage and for short transfers in the data stage. If we
+ * encounter a short transfer in the data stage, then we need to go through and
+ * check whether or not the short transfer is allowed. If it is, then there's
+ * nothing to do. We'll update everything and call back the framework once we
+ * get the status stage.
+ */
+static boolean_t
+xhci_endpoint_control_callback(xhci_t *xhcip, xhci_device_t *xd,
+    xhci_endpoint_t *xep, xhci_transfer_t *xt, int off, xhci_trb_t *trb)
+{
+	int code;
+	usb_ctrl_req_t *ucrp;
+	xhci_transfer_t *rem;
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+
+	code = XHCI_TRB_GET_CODE(LE_32(trb->trb_status));
+	ucrp = (usb_ctrl_req_t *)xt->xt_usba_req;
+
+	/*
+	 * Now that we know what this TRB is for, was it for a data/normal stage
+	 * or is it the status stage. We cheat by looking at the last entry. If
+	 * it's a data stage, then we must have gotten a short write. In that
+	 * case, we should go through and check to make sure it's allowed. If
+	 * not, we need to fail the transfer, try to stop the ring, and make
+	 * callbacks. We'll clean up the xhci transfer at this time.
+	 */
+	if (off != xt->xt_ntrbs - 1) {
+		uint_t remain;
+		usb_ctrl_req_t *ucrp = (usb_ctrl_req_t *)xt->xt_usba_req;
+
+		/*
+		 * This is a data stage TRB. The only reason we should have
+		 * gotten something for this is beacuse it was short. Make sure
+		 * it's okay before we continue.
+		 */
+		VERIFY3S(code, ==, XHCI_CODE_SHORT_XFER);
+		if (!(ucrp->ctrl_attributes & USB_ATTRS_SHORT_XFER_OK)) {
+			xt->xt_cr = USB_CR_DATA_UNDERRUN;
+			mutex_exit(&xhcip->xhci_lock);
+			return (B_TRUE);
+		}
+
+		/*
+		 * The value in the resulting trb is how much data remained to
+		 * be transferred. Normalize that against the original buffer
+		 * size.
+		 */
+		remain = XHCI_TRB_REMAIN(LE_32(trb->trb_status));
+		xt->xt_short = xt->xt_buffer.xdb_len - remain;
+		mutex_exit(&xhcip->xhci_lock);
+		return (B_TRUE);
+	}
+
+	/*
+	 * Okay, this is a status stage trb that's in good health. We should
+	 * finally go ahead, sync data and try and finally do the callback. If
+	 * we have short data, then xt->xt_short will be non-zero.
+	 */
+	if (xt->xt_data_tohost == B_TRUE) {
+		size_t len;
+		if (xt->xt_short != 0) {
+			len = xt->xt_short;
+		} else {
+			len = xt->xt_buffer.xdb_len;
+		}
+
+		if (xhci_transfer_sync(xhcip, xt, DDI_DMA_SYNC_FORCPU) !=
+		    DDI_FM_OK) {
+			xhci_error(xhcip, "failed to process control transfer "
+			    "callback for endpoint %u of device on slot %d and "
+			    "port %d: encountered fatal FM error synchronizing "
+			    "DMA memory, resetting device", xep->xep_num,
+			    xd->xd_slot, xd->xd_port);
+			xhci_fm_runtime_reset(xhcip);
+			mutex_exit(&xhcip->xhci_lock);
+			return (B_FALSE);
+		}
+
+		xhci_transfer_copy(xt, ucrp->ctrl_data->b_rptr, len, B_TRUE);
+		ucrp->ctrl_data->b_wptr += len;
+	}
+
+	/*
+	 * Now we're done. We can go ahead and bump the ring. Free the transfer
+	 * outside of the lock and call back into the framework.
+	 */
+	VERIFY(xhci_ring_trb_consumed(&xep->xep_ring, LE_64(trb->trb_addr)));
+	rem = list_remove_head(&xep->xep_transfers);
+	VERIFY3P(rem, ==, xt);
+	mutex_exit(&xhcip->xhci_lock);
+
+	usba_hcdi_cb(xep->xep_pipe, (usb_opaque_t)ucrp, xt->xt_cr);
+	xhci_transfer_free(xhcip, xt);
+
+	return (B_TRUE);
+}
+
+/*
+ * Cons up a new usb request for the periodic data transfer if we can. If there
+ * isn't one available, change the return code to NO_RESOURCES and stop polling
+ * on this endpoint, thus using and consuming the original request.
+ */
+static usb_opaque_t
+xhci_endpoint_dup_periodic(xhci_endpoint_t *xep, xhci_transfer_t *xt,
+    usb_cr_t *cr)
+{
+	usb_opaque_t urp;
+
+	xhci_pipe_t *xp = (xhci_pipe_t *)xep->xep_pipe->p_hcd_private;
+	xhci_periodic_pipe_t *xpp = &xp->xp_periodic;
+
+	/*
+	 * In general, transfers shouldn't have a usb request. However, oneshot
+	 * Interrupt IN ones will, so we use this as a way to shortcut out of
+	 * here.
+	 */
+	if (xt->xt_usba_req != NULL)
+		return (xt->xt_usba_req);
+
+	if (xep->xep_type == USB_EP_ATTR_INTR) {
+		urp = (usb_opaque_t)usba_hcdi_dup_intr_req(xep->xep_pipe->p_dip,
+		    (usb_intr_req_t *)xpp->xpp_usb_req, xpp->xpp_tsize, 0);
+	} else {
+		urp = (usb_opaque_t)usba_hcdi_dup_isoc_req(xep->xep_pipe->p_dip,
+		    (usb_isoc_req_t *)xpp->xpp_usb_req, 0);
+	}
+	if (urp == NULL) {
+		xpp->xpp_poll_state = XHCI_PERIODIC_POLL_NOMEM;
+		urp = xpp->xpp_usb_req;
+		xpp->xpp_usb_req = NULL;
+		*cr = USB_CR_NO_RESOURCES;
+	} else {
+		mutex_enter(&xep->xep_pipe->p_mutex);
+		xep->xep_pipe->p_req_count++;
+		mutex_exit(&xep->xep_pipe->p_mutex);
+	}
+
+	return (urp);
+}
+
+static xhci_device_t *
+xhci_device_lookup_by_slot(xhci_t *xhcip, int slot)
+{
+	xhci_device_t *xd;
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+
+	for (xd = list_head(&xhcip->xhci_usba.xa_devices); xd != NULL;
+	    xd = list_next(&xhcip->xhci_usba.xa_devices, xd)) {
+		if (xd->xd_slot == slot)
+			return (xd);
+	}
+
+	return (NULL);
+}
+
+/*
+ * Handle things which consist solely of normal tranfers, in other words, bulk
+ * and interrupt transfers.
+ */
+static boolean_t
+xhci_endpoint_norm_callback(xhci_t *xhcip, xhci_device_t *xd,
+    xhci_endpoint_t *xep, xhci_transfer_t *xt, int off, xhci_trb_t *trb)
+{
+	int code;
+	usb_cr_t cr;
+	xhci_transfer_t *rem;
+	int attrs;
+	mblk_t *mp;
+	boolean_t periodic = B_FALSE;
+	usb_opaque_t urp;
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+	ASSERT(xep->xep_type == USB_EP_ATTR_BULK ||
+	    xep->xep_type == USB_EP_ATTR_INTR);
+
+	code = XHCI_TRB_GET_CODE(LE_32(trb->trb_status));
+
+	if (code == XHCI_CODE_SHORT_XFER) {
+		int residue;
+		residue = XHCI_TRB_REMAIN(LE_32(trb->trb_status));
+		xt->xt_short = xt->xt_buffer.xdb_len - residue;
+	}
+
+	/*
+	 * If we have an interrupt from something that's not the last entry,
+	 * that must mean we had a short transfer, so there's nothing more for
+	 * us to do at the moment. We won't call back until everything's
+	 * finished for the general transfer.
+	 */
+	if (off < xt->xt_ntrbs - 1) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (B_TRUE);
+	}
+
+	urp = xt->xt_usba_req;
+	if (xep->xep_type == USB_EP_ATTR_BULK) {
+		usb_bulk_req_t *ubrp = (usb_bulk_req_t *)xt->xt_usba_req;
+		attrs = ubrp->bulk_attributes;
+		mp = ubrp->bulk_data;
+	} else {
+		usb_intr_req_t *uirp = (usb_intr_req_t *)xt->xt_usba_req;
+
+		if (uirp == NULL) {
+			periodic = B_TRUE;
+			urp = xhci_endpoint_dup_periodic(xep, xt, &cr);
+			uirp = (usb_intr_req_t *)urp;
+
+			/*
+			 * If we weren't able to duplicate the interrupt, then
+			 * we can't put any data in it.
+			 */
+			if (cr == USB_CR_NO_RESOURCES)
+				goto out;
+		}
+
+		attrs = uirp->intr_attributes;
+		mp = uirp->intr_data;
+	}
+
+	if (xt->xt_data_tohost == B_TRUE) {
+		size_t len;
+		if (xt->xt_short != 0) {
+			if (!(attrs & USB_ATTRS_SHORT_XFER_OK)) {
+				cr = USB_CR_DATA_UNDERRUN;
+				goto out;
+			}
+			len = xt->xt_short;
+		} else {
+			len = xt->xt_buffer.xdb_len;
+		}
+
+		if (xhci_transfer_sync(xhcip, xt, DDI_DMA_SYNC_FORCPU) !=
+		    DDI_FM_OK) {
+			xhci_error(xhcip, "failed to process normal transfer "
+			    "callback for endpoint %u of device on slot %d and "
+			    "port %d: encountered fatal FM error synchronizing "
+			    "DMA memory, resetting device", xep->xep_num,
+			    xd->xd_slot, xd->xd_port);
+			xhci_fm_runtime_reset(xhcip);
+			mutex_exit(&xhcip->xhci_lock);
+			return (B_FALSE);
+		}
+
+		xhci_transfer_copy(xt, mp->b_rptr, len, B_TRUE);
+		mp->b_wptr += len;
+	}
+	cr = USB_CR_OK;
+
+out:
+	VERIFY(xhci_ring_trb_consumed(&xep->xep_ring, LE_64(trb->trb_addr)));
+	rem = list_remove_head(&xep->xep_transfers);
+	VERIFY3P(rem, ==, xt);
+	mutex_exit(&xhcip->xhci_lock);
+
+	usba_hcdi_cb(xep->xep_pipe, urp, cr);
+	if (periodic == B_TRUE) {
+		xhci_endpoint_reschedule_periodic(xhcip, xd, xep, xt);
+	} else {
+		xhci_transfer_free(xhcip, xt);
+	}
+
+	return (B_TRUE);
+}
+
+static boolean_t
+xhci_endpoint_isoch_callback(xhci_t *xhcip, xhci_device_t *xd,
+    xhci_endpoint_t *xep, xhci_transfer_t *xt, int off, xhci_trb_t *trb)
+{
+	int code;
+	usb_cr_t cr;
+	xhci_transfer_t *rem;
+	usb_isoc_pkt_descr_t *desc;
+	usb_isoc_req_t *usrp;
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+	ASSERT3S(xep->xep_type, ==, USB_EP_ATTR_ISOCH);
+
+	code = XHCI_TRB_GET_CODE(LE_32(trb->trb_status));
+
+	/*
+	 * The descriptors that we copy the data from are set up to assume that
+	 * everything was OK and we transferred all the requested data.
+	 */
+	desc = &xt->xt_isoc[off];
+	if (code == XHCI_CODE_SHORT_XFER) {
+		int residue = XHCI_TRB_REMAIN(LE_32(trb->trb_status));
+		desc->isoc_pkt_actual_length -= residue;
+	}
+
+	/*
+	 * We don't perform the callback until the very last TRB is returned
+	 * here. If we have a TRB report on something else, that means that we
+	 * had a short transfer.
+	 */
+	if (off < xt->xt_ntrbs - 1) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (B_TRUE);
+	}
+
+	VERIFY(xhci_ring_trb_consumed(&xep->xep_ring, LE_64(trb->trb_addr)));
+	rem = list_remove_head(&xep->xep_transfers);
+	VERIFY3P(rem, ==, xt);
+	mutex_exit(&xhcip->xhci_lock);
+
+	cr = USB_CR_OK;
+
+	if (xt->xt_data_tohost == B_TRUE) {
+		usb_opaque_t urp;
+		urp = xhci_endpoint_dup_periodic(xep, xt, &cr);
+		usrp = (usb_isoc_req_t *)urp;
+
+		if (cr == USB_CR_OK) {
+			mblk_t *mp;
+			size_t len;
+			if (xhci_transfer_sync(xhcip, xt,
+			    DDI_DMA_SYNC_FORCPU) != DDI_FM_OK) {
+				xhci_error(xhcip, "failed to process "
+				    "isochronous transfer callback for "
+				    "endpoint %u of device on slot %d and port "
+				    "%d: encountered fatal FM error "
+				    "synchronizing DMA memory, resetting "
+				    "device",
+				    xep->xep_num, xd->xd_slot, xd->xd_port);
+				xhci_fm_runtime_reset(xhcip);
+				mutex_exit(&xhcip->xhci_lock);
+				return (B_FALSE);
+			}
+
+			mp = usrp->isoc_data;
+			len = xt->xt_buffer.xdb_len;
+			xhci_transfer_copy(xt, mp->b_rptr, len, B_TRUE);
+			mp->b_wptr += len;
+		}
+	} else {
+		usrp = (usb_isoc_req_t *)xt->xt_usba_req;
+	}
+
+	if (cr == USB_CR_OK) {
+		bcopy(xt->xt_isoc, usrp->isoc_pkt_descr,
+		    sizeof (usb_isoc_pkt_descr_t) * usrp->isoc_pkts_count);
+	}
+
+	usba_hcdi_cb(xep->xep_pipe, (usb_opaque_t)usrp, cr);
+	if (xt->xt_data_tohost == B_TRUE) {
+		xhci_endpoint_reschedule_periodic(xhcip, xd, xep, xt);
+	} else {
+		xhci_transfer_free(xhcip, xt);
+	}
+
+	return (B_TRUE);
+}
+
+boolean_t
+xhci_endpoint_transfer_callback(xhci_t *xhcip, xhci_trb_t *trb)
+{
+	boolean_t ret;
+	int slot, endpoint, code, off;
+	xhci_device_t *xd;
+	xhci_endpoint_t *xep;
+	xhci_transfer_t *xt;
+	boolean_t transfer_done;
+
+	endpoint = XHCI_TRB_GET_EP(LE_32(trb->trb_flags));
+	slot = XHCI_TRB_GET_SLOT(LE_32(trb->trb_flags));
+	code = XHCI_TRB_GET_CODE(LE_32(trb->trb_status));
+
+	mutex_enter(&xhcip->xhci_lock);
+	xd = xhci_device_lookup_by_slot(xhcip, slot);
+	if (xd == NULL) {
+		xhci_error(xhcip, "received transfer trb with code %d for "
+		    "unknown slot %d and endpoint %d: resetting device", code,
+		    slot, endpoint);
+		mutex_exit(&xhcip->xhci_lock);
+		xhci_fm_runtime_reset(xhcip);
+		return (B_FALSE);
+	}
+
+	/*
+	 * Endpoint IDs are indexed based on their Device Context Index, which
+	 * means that we need to subtract one to get the actual ID that we use.
+	 */
+	xep = xd->xd_endpoints[endpoint - 1];
+	if (xep == NULL) {
+		xhci_error(xhcip, "received transfer trb with code %d, slot "
+		    "%d, and unknown endpoint %d: resetting device", code,
+		    slot, endpoint);
+		mutex_exit(&xhcip->xhci_lock);
+		xhci_fm_runtime_reset(xhcip);
+		return (B_FALSE);
+	}
+
+	/*
+	 * This TRB should be part of a transfer. If it's not, then we ignore
+	 * it. We also check whether or not it's for the first transfer. Because
+	 * the rings are serviced in order, it should be.
+	 */
+	if ((xt = xhci_endpoint_determine_transfer(xhcip, xep, trb, &off)) ==
+	    NULL) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (B_TRUE);
+	}
+
+	transfer_done = B_FALSE;
+
+	switch (code) {
+	case XHCI_CODE_SUCCESS:
+	case XHCI_CODE_SHORT_XFER:
+		/* Handled by endpoint logic */
+		break;
+	case XHCI_CODE_XFER_STOPPED:
+	case XHCI_CODE_XFER_STOPINV:
+	case XHCI_CODE_XFER_STOPSHORT:
+		/*
+		 * This causes us to transition the endpoint to a stopped state.
+		 * Each of these indicate a different possible state that we
+		 * have to deal with. Effectively we're going to drop it and
+		 * leave it up to the consumers to figure out what to do. For
+		 * the moment, that's generally okay because stops are only used
+		 * in cases where we're cleaning up outstanding reqs, etc.
+		 */
+		mutex_exit(&xhcip->xhci_lock);
+		return (B_TRUE);
+	case XHCI_CODE_STALL:
+		/*
+		 * This causes us to transition to the halted state;
+		 * however, downstream clients are able to handle this just
+		 * fine.
+		 */
+		xep->xep_state |= XHCI_ENDPOINT_HALTED;
+		xt->xt_cr = USB_CR_STALL;
+		transfer_done = B_TRUE;
+		break;
+	case XHCI_CODE_BABBLE:
+		transfer_done = B_TRUE;
+		xt->xt_cr = USB_CR_DATA_OVERRUN;
+		xep->xep_state |= XHCI_ENDPOINT_HALTED;
+		break;
+	case XHCI_CODE_TXERR:
+	case XHCI_CODE_SPLITERR:
+		transfer_done = B_TRUE;
+		xt->xt_cr = USB_CR_DEV_NOT_RESP;
+		xep->xep_state |= XHCI_ENDPOINT_HALTED;
+		break;
+	default:
+		/*
+		 * Treat these as general unspecified errors that don't cause a
+		 * stop of the ring. Even if it does, a subsequent timeout
+		 * should occur which causes us to end up dropping a pipe reset
+		 * or at least issuing a reset of the device as part of
+		 * quiescing.
+		 */
+		transfer_done = B_TRUE;
+		break;
+	}
+
+	if (transfer_done == B_TRUE) {
+		xhci_transfer_t *alt;
+
+		alt = list_remove_head(&xep->xep_transfers);
+		VERIFY3P(alt, ==, xt);
+		mutex_exit(&xhcip->xhci_lock);
+		if (xt->xt_usba_req == NULL) {
+			usb_opaque_t urp;
+
+			urp = xhci_endpoint_dup_periodic(xep, xt, &xt->xt_cr);
+			usba_hcdi_cb(xep->xep_pipe, urp, xt->xt_cr);
+		} else {
+			usba_hcdi_cb(xep->xep_pipe,
+			    (usb_opaque_t)xt->xt_usba_req, xt->xt_cr);
+			xhci_transfer_free(xhcip, xt);
+		}
+		return (B_TRUE);
+	}
+
+	/*
+	 * Process the transfer callback based on the type of endpoint. Each of
+	 * these callback functions will end up calling back into USBA via
+	 * usba_hcdi_cb() to return transfer information (whether successful or
+	 * not). Because we can't hold any locks across a call to that function,
+	 * all of these callbacks will drop the xhci_t`xhci_lock by the time
+	 * they return. This is why there's no mutex_exit() call before we
+	 * return.
+	 */
+	switch (xep->xep_type) {
+	case USB_EP_ATTR_CONTROL:
+		ret = xhci_endpoint_control_callback(xhcip, xd, xep, xt, off,
+		    trb);
+		break;
+	case USB_EP_ATTR_BULK:
+		ret = xhci_endpoint_norm_callback(xhcip, xd, xep, xt, off, trb);
+		break;
+	case USB_EP_ATTR_INTR:
+		ret = xhci_endpoint_norm_callback(xhcip, xd, xep, xt, off,
+		    trb);
+		break;
+	case USB_EP_ATTR_ISOCH:
+		ret = xhci_endpoint_isoch_callback(xhcip, xd, xep, xt, off,
+		    trb);
+		break;
+	default:
+		panic("bad endpoint type: %u", xep->xep_type);
+	}
+
+	return (ret);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci_event.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,234 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * Event Ring Management
+ *
+ * All activity in xHCI is reported to an event ring, which corresponds directly
+ * with an interrupt. Whether a command is issued or an I/O is issued to a given
+ * device endpoint, it will end up being acknowledged, positively or negatively,
+ * on an event ring.
+ *
+ * Unlike other rings, the OS is a consumer of the event rings, not a producer.
+ * For more information on how the ring is used, see xhci_ring.c. For more
+ * information generally, see xhci.c.
+ *
+ * All of the rings are described in the ERST -- Event Ring Segment Table. As we
+ * only have a single interrupt and a single event ring, we only write a single
+ * entry here.
+ */
+
+#include <sys/usb/hcd/xhci/xhci.h>
+
+
+void
+xhci_event_fini(xhci_t *xhcip)
+{
+	xhci_event_ring_t *xev = &xhcip->xhci_event;
+	xhci_ring_free(&xev->xev_ring);
+	if (xev->xev_segs != NULL)
+		xhci_dma_free(&xev->xev_dma);
+	xev->xev_segs = NULL;
+}
+
+/*
+ * Make sure that if we leave here we either have both the ring and table
+ * addresses initialized or neither.
+ */
+static int
+xhci_event_alloc(xhci_t *xhcip, xhci_event_ring_t *xev)
+{
+	int ret;
+	ddi_dma_attr_t attr;
+	ddi_device_acc_attr_t acc;
+
+	/*
+	 * This is allocating the segment table. It doesn't have any particular
+	 * requirements. Though it could be larger, we can get away with our
+	 * default data structure attributes unless we add a lot more entries.
+	 */
+	xhci_dma_acc_attr(xhcip, &acc);
+	xhci_dma_dma_attr(xhcip, &attr);
+	if (!xhci_dma_alloc(xhcip, &xev->xev_dma, &attr, &acc, B_FALSE,
+	    sizeof (xhci_event_segment_t) * XHCI_EVENT_NSEGS, B_FALSE))
+		return (ENOMEM);
+	if ((ret = xhci_ring_alloc(xhcip, &xev->xev_ring)) != 0) {
+		xhci_dma_free(&xev->xev_dma);
+		return (ret);
+	}
+
+	xev->xev_segs = (void *)xev->xev_dma.xdb_va;
+	return (0);
+}
+
+int
+xhci_event_init(xhci_t *xhcip)
+{
+	int ret;
+	uint32_t reg;
+	xhci_event_ring_t *xev = &xhcip->xhci_event;
+
+	if (xev->xev_segs == NULL) {
+		if ((ret = xhci_event_alloc(xhcip, xev)) != 0)
+			return (ret);
+	}
+
+	if ((ret = xhci_ring_reset(xhcip, &xev->xev_ring)) != 0) {
+		xhci_event_fini(xhcip);
+		return (ret);
+	}
+
+	bzero(xev->xev_segs, sizeof (xhci_event_segment_t) * XHCI_EVENT_NSEGS);
+	xev->xev_segs[0].xes_addr = LE_64(xhci_dma_pa(&xev->xev_ring.xr_dma));
+	xev->xev_segs[0].xes_size = LE_16(xev->xev_ring.xr_ntrb);
+
+	reg = xhci_get32(xhcip, XHCI_R_RUN, XHCI_ERSTSZ(0));
+	reg &= ~XHCI_ERSTS_MASK;
+	reg |= XHCI_ERSTS_SET(XHCI_EVENT_NSEGS);
+	xhci_put32(xhcip, XHCI_R_RUN, XHCI_ERSTSZ(0), reg);
+
+	xhci_put64(xhcip, XHCI_R_RUN, XHCI_ERDP(0),
+	    xhci_dma_pa(&xev->xev_ring.xr_dma));
+	xhci_put64(xhcip, XHCI_R_RUN, XHCI_ERSTBA(0),
+	    xhci_dma_pa(&xev->xev_dma));
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_event_fini(xhcip);
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (EIO);
+	}
+
+	return (0);
+}
+
+static boolean_t
+xhci_event_process_psc(xhci_t *xhcip, xhci_trb_t *trb)
+{
+	uint32_t port;
+
+	if (XHCI_TRB_GET_CODE(LE_32(trb->trb_status)) != XHCI_CODE_SUCCESS) {
+		return (B_TRUE);
+	}
+
+	port = XHCI_TRB_PORTID(LE_64(trb->trb_addr));
+	if (port < 1 || port > xhcip->xhci_caps.xcap_max_ports) {
+		/*
+		 * At some point we may want to send a DDI_FM_DEVICE_INVAL_STATE
+		 * ereport as part of this.
+		 */
+		return (B_FALSE);
+	}
+
+	xhci_root_hub_psc_callback(xhcip);
+	return (B_TRUE);
+}
+
+/*
+ * Process the event ring, note we're in interrupt context while doing this.
+ */
+boolean_t
+xhci_event_process(xhci_t *xhcip)
+{
+	int nevents;
+	uint64_t addr;
+	xhci_ring_t *xrp = &xhcip->xhci_event.xev_ring;
+
+	/*
+	 * While it may be possible for us to transition to an error state at
+	 * any time because we are reasonably not holding the xhci_t's lock
+	 * during the entire interrupt (as it doesn't protect any of the event
+	 * ring's data), we still do an initial test to ensure that we don't go
+	 * too far down the path.
+	 */
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhcip->xhci_state & XHCI_S_ERROR) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (B_FALSE);
+	}
+	mutex_exit(&xhcip->xhci_lock);
+
+	/*
+	 * We've seen a few cases, particularly when dealing with controllers
+	 * where BIOS takeover is involved, that an interrupt gets injected into
+	 * the system before we've actually finished setting things up. If for
+	 * some reason that happens, and we don't actually have a ring yet,
+	 * don't try and do anything.
+	 */
+	if (xhcip->xhci_event.xev_segs == NULL)
+		return (B_TRUE);
+
+	XHCI_DMA_SYNC(xrp->xr_dma, DDI_DMA_SYNC_FORKERNEL);
+	if (xhci_check_dma_handle(xhcip, &xrp->xr_dma) != DDI_FM_OK) {
+		xhci_error(xhcip, "encountered fatal FM error trying to "
+		    "synchronize event ring: resetting device");
+		xhci_fm_runtime_reset(xhcip);
+		return (B_FALSE);
+	}
+
+	/*
+	 * Process at most a full ring worth of events.
+	 */
+	for (nevents = 0; nevents < xrp->xr_ntrb; nevents++) {
+		xhci_trb_t *trb;
+		uint32_t type;
+
+		if ((trb = xhci_ring_event_advance(xrp)) == NULL)
+			break;
+
+		type = LE_32(trb->trb_flags) & XHCI_TRB_TYPE_MASK;
+		switch (type) {
+		case XHCI_EVT_PORT_CHANGE:
+			if (!xhci_event_process_psc(xhcip, trb))
+				return (B_FALSE);
+			break;
+		case XHCI_EVT_CMD_COMPLETE:
+			if (!xhci_command_event_callback(xhcip, trb))
+				return (B_FALSE);
+			break;
+		case XHCI_EVT_DOORBELL:
+			/*
+			 * Because we don't have any VF hardware, this event
+			 * should never happen. If it does, that probably means
+			 * something bad has happened and we should reset the
+			 * device.
+			 */
+			xhci_error(xhcip, "received xHCI VF interrupt even "
+			    "though virtual functions are not supported, "
+			    "resetting device");
+			xhci_fm_runtime_reset(xhcip);
+			return (B_FALSE);
+		case XHCI_EVT_XFER:
+			if (!xhci_endpoint_transfer_callback(xhcip, trb))
+				return (B_FALSE);
+			break;
+		/*
+		 * Ignore other events that come in.
+		 */
+		default:
+			break;
+		}
+	}
+
+	addr = xhci_dma_pa(&xrp->xr_dma) + sizeof (xhci_trb_t) * xrp->xr_tail;
+	addr |= XHCI_ERDP_BUSY;
+	xhci_put64(xhcip, XHCI_R_RUN, XHCI_ERDP(0), addr);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to write to event ring dequeue "
+		    "pointer: encountered fatal FM error, resetting device");
+		xhci_fm_runtime_reset(xhcip);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci_hub.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,881 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * xHCI Root Hub
+ *
+ * Please see the big theory statement in xhci.c for more information.
+ */
+
+#include <sys/usb/hcd/xhci/xhci.h>
+
+/*
+ * The following structure and global define the default configuration that we
+ * 'deliver' to USBA on behalf of our hub. However, it's worth noting that it's
+ * going to take this in as an array of bytes off of the wire, whereas we're
+ * declaring this as a packed C structure to make our life much, much easier.
+ * It is critical that we pay attention to the endianness of anything that is
+ * more than a single byte wide and write the value in the appropriate
+ * endian-aware form.
+ *
+ * Note, we don't use the system structures for these values because they are
+ * not packed, and we must be. Even though we define all members, we still use
+ * C99 structure initialization to make it easier for folks to see what values
+ * are what in a long array of numbers.
+ *
+ * The structure is laid out first with members that make up a usb_cfg_descr_t.
+ * Then it has a usb_if_descr_t. After that, it has a usb_ep_descr_t and finally
+ * a usb_ep_ss_comp_descr_t. Please see the original structure definitions for
+ * the meaning of each member.
+ *
+ * Many of the values used below were derived from the USB 3.1/10.15.1 'Standard
+ * Descriptors for Hub Class'.
+ */
+#pragma pack(1)
+typedef struct xhci_dev_conf {
+	/* usb_cfg_descr_t */
+	uint8_t		xdc_cfg_bLength;
+	uint8_t		xdc_cfg_bDescriptorType;
+	uint16_t	xdc_cfg_wTotalLength;
+	uint8_t		xdc_cfg_bNumInterfaces;
+	uint8_t		xdc_cfg_bConfigurationValue;
+	uint8_t		xdc_cfg_iConfiguration;
+	uint8_t		xdc_cfg_bmAttributes;
+	uint8_t		xdc_cfg_bMaxPower;
+	/* usb_if_descr_t */
+	uint8_t		xdc_if_bLength;
+	uint8_t		xdc_if_bDescriptorType;
+	uint8_t		xdc_if_bInterfaceNumber;
+	uint8_t		xdc_if_bAlternateSetting;
+	uint8_t		xdc_if_bNumEndpoints;
+	uint8_t		xdc_if_bInterfaceClass;
+	uint8_t		xdc_if_bInterfaceSubClass;
+	uint8_t		xdc_if_bInterfaceProtocol;
+	uint8_t		xdc_if_iInterface;
+	/* usb_ep_descr_t */
+	uint8_t		xdc_ep_bLength;
+	uint8_t		xdc_ep_bDescriptorType;
+	uint8_t		xdc_ep_bEndpointAddress;
+	uint8_t		xdc_ep_bmAttributes;
+	uint16_t	xdc_ep_wMaxPacketSize;
+	uint8_t		xdc_ep_bInterval;
+	/* usb_ep_ss_comp_descr_t */
+	uint8_t		xdc_epssc_bLength;
+	uint8_t		xdc_epssc_bDescriptorType;
+	uint8_t		xdc_epssc_bMaxBurst;
+	uint8_t		xdc_epssc_bmAttributes;
+	uint16_t	xdc_epssc_wBytesPerInterval;
+} xhci_dev_conf_t;
+#pragma pack()
+
+#if MAX_PORTS != 31
+#error	"MAX_PORTS has changed, update xdc_ep_wMaxPacketSize"
+#endif
+
+xhci_dev_conf_t xhci_hcdi_conf = {
+	.xdc_cfg_bLength = 0x9,
+	.xdc_cfg_bDescriptorType = USB_DESCR_TYPE_CFG,
+#if defined(_BIG_ENDIAN)
+	.xdc_cfg_wTotalLength = 0x1f00,
+#elif defined(_LITTLE_ENDIAN)
+	.xdc_cfg_wTotalLength = 0x001f,
+#else	/* !_BIG_ENDIAN && !_LITTLE_ENDIAN */
+#error	"Unknown endianness"
+#endif /* _BIG_ENDIAN */
+	.xdc_cfg_bNumInterfaces = 0x1,
+	.xdc_cfg_bConfigurationValue = 0x1,
+	.xdc_cfg_iConfiguration = 0x0,
+	.xdc_cfg_bmAttributes = 0x40,
+	.xdc_cfg_bMaxPower = 0x0,
+
+	.xdc_if_bLength = 0x9,
+	.xdc_if_bDescriptorType = USB_DESCR_TYPE_IF,
+	.xdc_if_bInterfaceNumber = 0x0,
+	.xdc_if_bAlternateSetting = 0x0,
+	.xdc_if_bNumEndpoints = 0x1,
+	.xdc_if_bInterfaceClass = USB_CLASS_HUB,
+	.xdc_if_bInterfaceSubClass = 0x0,
+	.xdc_if_bInterfaceProtocol = 0x0,
+	.xdc_if_iInterface = 0x0,
+
+	.xdc_ep_bLength = 0x7,
+	.xdc_ep_bDescriptorType = USB_DESCR_TYPE_EP,
+	.xdc_ep_bEndpointAddress = USB_EP_DIR_IN | ROOT_HUB_ADDR,
+	.xdc_ep_bmAttributes = USB_EP_ATTR_INTR,
+
+	/*
+	 * We size the endpoint's maximum packet size based on the total number
+	 * of ports that exist. This allows us to ensure that we can always
+	 * deliver a status bit for every port, even if we're not strictly
+	 * playing by the rules and have more than 16 ports. The system defines
+	 * MAX_PORTS to be 31, therefore we set this to four, so we cover it
+	 * all.
+	 */
+#if defined(_BIG_ENDIAN)
+	.xdc_ep_wMaxPacketSize = 0x0400,
+#elif defined(_LITTLE_ENDIAN)
+	.xdc_ep_wMaxPacketSize = 0x0004,
+#else	/* !_BIG_ENDIAN && !_LITTLE_ENDIAN */
+#error	"Unknown endianness"
+#endif /* _BIG_ENDIAN */
+	.xdc_ep_bInterval = 0x8,
+
+	.xdc_epssc_bLength = 0x06,
+	.xdc_epssc_bDescriptorType = USB_DESCR_TYPE_SS_EP_COMP,
+	.xdc_epssc_bMaxBurst = 0,
+	.xdc_epssc_bmAttributes = 0,
+#if defined(_BIG_ENDIAN)
+	.xdc_epssc_wBytesPerInterval = 0x0200
+#elif defined(_LITTLE_ENDIAN)
+	.xdc_epssc_wBytesPerInterval = 0x0002
+#else	/* !_BIG_ENDIAN && !_LITTLE_ENDIAN */
+#error	"Unknown endianness"
+#endif /* _BIG_ENDIAN */
+};
+
+/*
+ * This is a standard device request as defined in USB 3.1 / 9.4.5.
+ */
+static int
+xhci_root_hub_get_device_status(xhci_t *xhcip, usb_ctrl_req_t *ucrp)
+{
+	uint16_t stand;
+	uint32_t psm;
+	uint8_t len;
+	mblk_t *mp;
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+
+	mp = ucrp->ctrl_data;
+
+	/*
+	 * In the case where the request write length doesn't match what we
+	 * expect, we still return that this is 'OK'; however, we don't
+	 * increment the length, allowing the caller to basically see that we
+	 * have no data / failed it. The behavior in this case is defined to be
+	 * undefined and unfortuantely there's no great return value here for
+	 * EINVAL.
+	 */
+	switch (ucrp->ctrl_wValue) {
+	case USB_GET_STATUS_STANDARD:
+		if (ucrp->ctrl_wLength != USB_GET_STATUS_LEN)
+			return (USB_CR_UNSPECIFIED_ERR);
+		len = USB_GET_STATUS_LEN;
+		stand = LE_16(USB_DEV_SLF_PWRD_STATUS);
+		bcopy(&stand, mp->b_wptr, sizeof (stand));
+		break;
+	case USB_GET_STATUS_PTM:
+		if (ucrp->ctrl_wLength != USB_GET_STATUS_PTM_LEN)
+			return (USB_CR_UNSPECIFIED_ERR);
+		/*
+		 * We don't support the root hub, so we always return zero.
+		 */
+		len = USB_GET_STATUS_PTM_LEN;
+		psm = 0;
+		bcopy(&psm, mp->b_wptr, sizeof (psm));
+		break;
+	default:
+		return (USB_CR_NOT_SUPPORTED);
+	}
+
+	mp->b_wptr += len;
+
+	return (USB_CR_OK);
+}
+
+/*
+ * This is a hub class specific device request as defined in USB 3.1 /
+ * 11.24.2.6.
+ */
+static int
+xhci_root_hub_get_status(xhci_t *xhcip, usb_ctrl_req_t *ucrp)
+{
+	const uint32_t status = 0;
+	mblk_t *mp;
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+
+	if (ucrp->ctrl_wLength != HUB_GET_STATUS_LEN)
+		return (USB_CR_UNSPECIFIED_ERR);
+	mp = ucrp->ctrl_data;
+
+	bcopy(&status, mp->b_wptr, sizeof (status));
+	mp->b_wptr += sizeof (status);
+
+	return (USB_CR_OK);
+}
+
+/*
+ * We've been asked to get the root hub's descriptor. According to USB 3.1 /
+ * 10.16.2.3 we return up to a maximum number of bytes based on the actual size
+ * of the request. It's not an error for it to request more or less. e.g. we
+ * only return MIN(req, sizeof (desc)).
+ */
+static void
+xhci_root_hub_get_descriptor(xhci_t *xhcip, usb_ctrl_req_t *ucrp)
+{
+	int len = MIN(sizeof (usb_ss_hub_descr_t), ucrp->ctrl_wLength);
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+
+	/*
+	 * We maintain the root hub's description in a little-endian data format
+	 * regardless of the platform. This means that we don't have to try to
+	 * transform any of the data that we have inside of it when we deliver
+	 * it to USBA.
+	 */
+	bcopy(&xhcip->xhci_usba.xa_hub_descr, ucrp->ctrl_data->b_wptr, len);
+	ucrp->ctrl_data->b_wptr += len;
+}
+
+static int
+xhci_root_hub_handle_port_clear_feature(xhci_t *xhcip, usb_ctrl_req_t *ucrp)
+{
+	int feat = ucrp->ctrl_wValue;
+	int port = XHCI_PS_INDPORT(ucrp->ctrl_wIndex);
+	uint32_t reg;
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+
+	if (port < 1 || port > xhcip->xhci_caps.xcap_max_ports)
+		return (USB_CR_UNSPECIFIED_ERR);
+	if (ucrp->ctrl_wLength != 0)
+		return (USB_CR_UNSPECIFIED_ERR);
+
+	reg = xhci_get32(xhcip, XHCI_R_OPER, XHCI_PORTSC(port));
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to read port status register for "
+		    "port %d: encountered fatal FM error, resetting device",
+		    port);
+		xhci_fm_runtime_reset(xhcip);
+		return (USB_CR_HC_HARDWARE_ERR);
+	}
+
+	/*
+	 * The port status and command register has many bits that we must
+	 * preserve across writes, however, it also has bits that will be
+	 * cleared when a bitwise one is written to them. As some of these
+	 * write-one-to-clear bits may be set, we make sure to mask them off.
+	 */
+	reg &= ~XHCI_PS_CLEAR;
+
+	switch (feat) {
+	case CFS_PORT_ENABLE:
+		reg |= XHCI_PS_PED;
+		break;
+	case CFS_PORT_POWER:
+		reg &= ~XHCI_PS_PP;
+		break;
+	case CFS_C_PORT_CONNECTION:
+		reg |= XHCI_PS_CSC;
+		break;
+	case CFS_C_PORT_RESET:
+		reg |= XHCI_PS_PRC;
+		break;
+	case CFS_C_PORT_OVER_CURRENT:
+		reg |= XHCI_PS_OCC;
+		break;
+	case CFS_C_PORT_SUSPEND:
+	case CFS_C_PORT_LINK_STATE:
+		reg |= XHCI_PS_PLC;
+		break;
+	case CFS_C_PORT_ENABLE:
+		reg |= XHCI_PS_PEC;
+		break;
+	case CFS_C_PORT_CONFIG_ERROR:
+		reg |= XHCI_PS_CEC;
+		break;
+	case CFS_C_BH_PORT_RESET:
+		reg |= XHCI_PS_WRC;
+		break;
+	case CFS_PORT_SUSPEND:
+	default:
+		xhci_log(xhcip, "!asked to clear unsupported root hub "
+		    "feature %d on port %d", feat, port);
+		return (USB_CR_NOT_SUPPORTED);
+	}
+
+	xhci_put32(xhcip, XHCI_R_OPER, XHCI_PORTSC(port), reg);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to write port status register for "
+		    "port %d: encountered fatal FM error, resetting device",
+		    port);
+		xhci_fm_runtime_reset(xhcip);
+		return (USB_CR_HC_HARDWARE_ERR);
+	}
+
+	return (USB_CR_OK);
+}
+
+static int
+xhci_root_hub_handle_port_set_feature(xhci_t *xhcip, usb_ctrl_req_t *ucrp)
+{
+	int feat = ucrp->ctrl_wValue;
+	int port = XHCI_PS_INDPORT(ucrp->ctrl_wIndex);
+	uint32_t val = XHCI_PS_INDVAL(ucrp->ctrl_wIndex);
+	uint32_t reg;
+	uintptr_t index;
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+
+	if (port < 1 || port > xhcip->xhci_caps.xcap_max_ports)
+		return (USB_CR_UNSPECIFIED_ERR);
+	if (ucrp->ctrl_wLength != 0)
+		return (USB_CR_UNSPECIFIED_ERR);
+
+	index = XHCI_PORTSC(port);
+	reg = xhci_get32(xhcip, XHCI_R_OPER, index);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to read port status register for "
+		    "port %d: encountered fatal FM error, resetting device",
+		    port);
+		xhci_fm_runtime_reset(xhcip);
+		return (USB_CR_HC_HARDWARE_ERR);
+	}
+
+	/*
+	 * The port status and command register has many bits that we must
+	 * preserve across writes, however, it also has bits that will be
+	 * cleared when a bitwise one is written to them. As some of these
+	 * write-one-to-clear bits may be set, we make sure to mask them off.
+	 */
+	reg &= ~XHCI_PS_CLEAR;
+
+	switch (feat) {
+	case CFS_PORT_U1_TIMEOUT:
+		index = XHCI_PORTPMSC(port);
+		reg = xhci_get32(xhcip, XHCI_R_OPER, index);
+		if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+			xhci_error(xhcip, "failed to read port power "
+			    "management register for port %d: encountered "
+			    "fatal FM error, resetting device", port);
+			xhci_fm_runtime_reset(xhcip);
+			return (USB_CR_HC_HARDWARE_ERR);
+		}
+		reg &= ~XHCI_PM3_U1TO_SET(0xff);
+		reg |= XHCI_PM3_U1TO_SET(val);
+		break;
+	case CFS_PORT_U2_TIMEOUT:
+		index = XHCI_PORTPMSC(port);
+		reg = xhci_get32(xhcip, XHCI_R_OPER, index);
+		if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+			xhci_error(xhcip, "failed to read port power "
+			    "management register for port %d: encountered "
+			    "fatal FM error, resetting device", port);
+			xhci_fm_runtime_reset(xhcip);
+			return (USB_CR_HC_HARDWARE_ERR);
+		}
+		reg &= ~XHCI_PM3_U1TO_SET(0xff);
+		reg |= XHCI_PM3_U1TO_SET(val);
+		break;
+	case CFS_PORT_LINK_STATE:
+		reg |= XHCI_PS_PLS_SET(val);
+		reg |= XHCI_PS_LWS;
+		break;
+	case CFS_PORT_REMOTE_WAKE_MASK:
+		if (val & CFS_PRWM_CONN_ENABLE)
+			reg |= XHCI_PS_WCE;
+		else
+			reg &= ~XHCI_PS_WCE;
+
+		if (val & CFS_PRWM_DISCONN_ENABLE)
+			reg |= XHCI_PS_WDE;
+		else
+			reg &= ~XHCI_PS_WDE;
+
+		if (val & CFS_PRWM_OC_ENABLE)
+			reg |= XHCI_PS_WOE;
+		else
+			reg &= ~XHCI_PS_WOE;
+		break;
+	case CFS_BH_PORT_RESET:
+		reg |= XHCI_PS_WPR;
+		break;
+	case CFS_PORT_RESET:
+		reg |= XHCI_PS_PR;
+		break;
+	case CFS_PORT_POWER:
+		reg |= XHCI_PS_PP;
+		break;
+	case CFS_PORT_ENABLE:
+		/*
+		 * Enabling happens automatically for both USB 2 and USB 3. So
+		 * there's nothing specific to set here.
+		 */
+		return (USB_CR_OK);
+	case CFS_PORT_SUSPEND:
+	default:
+		xhci_log(xhcip, "!asked to set unsupported root hub "
+		    "feature %d on port %d", feat, port);
+		return (USB_CR_NOT_SUPPORTED);
+	}
+
+	xhci_put32(xhcip, XHCI_R_OPER, index, reg);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to write port status register for "
+		    "port %d: encountered fatal FM error, resetting device",
+		    port);
+		xhci_fm_runtime_reset(xhcip);
+		return (USB_CR_HC_HARDWARE_ERR);
+	}
+
+	return (USB_CR_OK);
+}
+
+/*
+ * We've been asked to get the port's status. While there are multiple forms
+ * that the port status request can take, we only support the primary one. The
+ * enhanced version is only in USB 3.1.
+ *
+ * Note that we don't end up explicitly adding a speed value for the port,
+ * because the only valid values are zero.
+ */
+static int
+xhci_root_hub_handle_port_get_status(xhci_t *xhcip, usb_ctrl_req_t *ucrp)
+{
+	uint32_t reg;
+	uint16_t ps, cs;
+	mblk_t *mp = ucrp->ctrl_data;
+	int port = XHCI_PS_INDPORT(ucrp->ctrl_wIndex);
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+
+	if (port < 1 || port > xhcip->xhci_caps.xcap_max_ports)
+		return (USB_CR_UNSPECIFIED_ERR);
+
+	if (ucrp->ctrl_wValue != PORT_GET_STATUS_PORT)
+		return (USB_CR_NOT_SUPPORTED);
+
+	if (ucrp->ctrl_wLength != PORT_GET_STATUS_PORT_LEN)
+		return (USB_CR_UNSPECIFIED_ERR);
+
+	reg = xhci_get32(xhcip, XHCI_R_OPER, XHCI_PORTSC(port));
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to read port status register for "
+		    "port %d: encountered fatal FM error, resetting device",
+		    port);
+		xhci_fm_runtime_reset(xhcip);
+		return (USB_CR_HC_HARDWARE_ERR);
+	}
+
+	ps = cs = 0;
+	if (reg & XHCI_PS_CCS)
+		ps |= PORT_STATUS_CCS;
+	if (reg & XHCI_PS_PED)
+		ps |= PORT_STATUS_PES;
+	if (reg & XHCI_PS_OCA)
+		ps |= PORT_STATUS_POCI;
+	if (reg & XHCI_PS_PR)
+		ps |= PORT_STATUS_PRS;
+
+	ps |= XHCI_PS_PLS_SET(XHCI_PS_PLS_GET(reg));
+
+	if (reg & XHCI_PS_PP)
+		ps |= PORT_STATUS_PPS;
+
+	/*
+	 * While this isn't a defined part of the status, because we're not a
+	 * true USB 3 hub, this is the only primary way that we can tell USBA
+	 * what the actual speed of the device is. It's a bit dirty, but there's
+	 * not really a great alternative at the moment.
+	 */
+	switch (XHCI_PS_SPEED_GET(reg)) {
+	case XHCI_SPEED_FULL:
+		ps |= USBA_FULL_SPEED_DEV << PORT_STATUS_SPSHIFT_SS;
+		break;
+	case XHCI_SPEED_LOW:
+		ps |= USBA_LOW_SPEED_DEV << PORT_STATUS_SPSHIFT_SS;
+		break;
+	case XHCI_SPEED_HIGH:
+		ps |= USBA_HIGH_SPEED_DEV << PORT_STATUS_SPSHIFT_SS;
+		break;
+	case XHCI_SPEED_SUPER:
+	default:
+		/*
+		 * If we encounter something we don't know, we're going to start
+		 * by assuming it is SuperSpeed, as so far all additions have
+		 * been purely faster than SuperSpeed and have the same external
+		 * behavior.
+		 */
+		ps |= USBA_SUPER_SPEED_DEV << PORT_STATUS_SPSHIFT_SS;
+		break;
+	}
+
+	if (reg & XHCI_PS_CSC)
+		cs |= PORT_CHANGE_CSC;
+	if (reg & XHCI_PS_PEC)
+		cs |= PORT_CHANGE_PESC;
+	if (reg & XHCI_PS_OCC)
+		cs |= PORT_CHANGE_OCIC;
+	if (reg & XHCI_PS_PRC)
+		cs |= PORT_CHANGE_PRSC;
+	if (reg & XHCI_PS_WRC)
+		cs |= PORT_CHANGE_BHPR;
+	if (reg & XHCI_PS_PLC)
+		cs |= PORT_CHANGE_PLSC;
+	if (reg & XHCI_PS_CEC)
+		cs |= PORT_CHANGE_PCE;
+
+	cs = LE_16(cs);
+	ps = LE_16(ps);
+	bcopy(&ps, mp->b_wptr, sizeof (uint16_t));
+	mp->b_wptr += sizeof (uint16_t);
+	bcopy(&cs, mp->b_wptr, sizeof (uint16_t));
+	mp->b_wptr += sizeof (uint16_t);
+
+	return (USB_CR_OK);
+}
+
+/*
+ * USBA has issued a request for the root hub. We need to determine what it's
+ * asking about and then figure out how to handle it and how to respond.
+ */
+int
+xhci_root_hub_ctrl_req(xhci_t *xhcip, usba_pipe_handle_data_t *ph,
+    usb_ctrl_req_t *ucrp)
+{
+	int ret = USB_CR_OK;
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+
+	switch (ucrp->ctrl_bmRequestType) {
+	case HUB_GET_DEVICE_STATUS_TYPE:
+		ret = xhci_root_hub_get_device_status(xhcip, ucrp);
+		break;
+	case HUB_HANDLE_PORT_FEATURE_TYPE:
+		switch (ucrp->ctrl_bRequest) {
+		case USB_REQ_CLEAR_FEATURE:
+			ret = xhci_root_hub_handle_port_clear_feature(xhcip,
+			    ucrp);
+			break;
+		case USB_REQ_SET_FEATURE:
+			ret = xhci_root_hub_handle_port_set_feature(xhcip,
+			    ucrp);
+			break;
+		default:
+			ret = USB_CR_NOT_SUPPORTED;
+			break;
+		}
+		break;
+	case HUB_GET_PORT_STATUS_TYPE:
+		ret = xhci_root_hub_handle_port_get_status(xhcip, ucrp);
+		break;
+	case HUB_CLASS_REQ_TYPE:
+		switch (ucrp->ctrl_bRequest) {
+		case USB_REQ_GET_STATUS:
+			ret = xhci_root_hub_get_status(xhcip, ucrp);
+			break;
+		case USB_REQ_GET_DESCR:
+			xhci_root_hub_get_descriptor(xhcip, ucrp);
+			break;
+		default:
+			xhci_error(xhcip, "Unhandled hub request: 0x%x\n",
+			    ucrp->ctrl_bRequest);
+			ret = USB_CR_NOT_SUPPORTED;
+			break;
+		}
+		break;
+	default:
+		xhci_error(xhcip, "Unhandled hub request type: %x\n",
+		    ucrp->ctrl_bmRequestType);
+		ret = USB_CR_NOT_SUPPORTED;
+		break;
+	}
+
+	mutex_exit(&xhcip->xhci_lock);
+	usba_hcdi_cb(ph, (usb_opaque_t)ucrp, ret);
+	mutex_enter(&xhcip->xhci_lock);
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * This function is invoked whenever the root HUBs interrupt endpoint is opened
+ * or we receive an port change event notification from the hardware on the
+ * event ring from an interrupt.
+ *
+ * If we have a registered interrupt callback requested, then we have to
+ * duplicate the request so we can send it back to usba and then we generate the
+ * actual status message and send it.
+ */
+void
+xhci_root_hub_psc_callback(xhci_t *xhcip)
+{
+	usb_intr_req_t *req, *new;
+	usba_pipe_handle_data_t *ph;
+	mblk_t *mp;
+	uint32_t mask;
+	unsigned i;
+
+	mask = 0;
+	for (i = 0; i <= xhcip->xhci_caps.xcap_max_ports; i++) {
+		uint32_t reg;
+
+		reg = xhci_get32(xhcip, XHCI_R_OPER, XHCI_PORTSC(i));
+		if ((reg & XHCI_HUB_INTR_CHANGE_MASK) != 0)
+			mask |= 1UL << i;
+	}
+
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to read port status registers: "
+		    "encountered fatal FM error, resetting device");
+		xhci_fm_runtime_reset(xhcip);
+		return;
+	}
+	if (mask == 0)
+		return;
+
+	mask = LE_32(mask);
+
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhcip->xhci_usba.xa_intr_cb_req == NULL) {
+		mutex_exit(&xhcip->xhci_lock);
+		return;
+	}
+
+	ASSERT(xhcip->xhci_usba.xa_intr_cb_ph != NULL);
+	req = xhcip->xhci_usba.xa_intr_cb_req;
+	ph = xhcip->xhci_usba.xa_intr_cb_ph;
+
+	new = usba_hcdi_dup_intr_req(ph->p_dip, req, req->intr_len, 0);
+	if (new == NULL) {
+		new = xhcip->xhci_usba.xa_intr_cb_req;
+		xhcip->xhci_usba.xa_intr_cb_req = NULL;
+		mutex_exit(&xhcip->xhci_lock);
+		usba_hcdi_cb(ph, (usb_opaque_t)new, USB_CR_NO_RESOURCES);
+		return;
+	}
+
+	/*
+	 * Why yes, we do have to manually increment this for the given pipe
+	 * before we deliver it. If we don't, it has no way of knowing that
+	 * there's another request inbound and we'll simply blow our assertions
+	 * on requests.
+	 */
+	mutex_enter(&ph->p_mutex);
+	ph->p_req_count++;
+	mutex_exit(&ph->p_mutex);
+
+	mp = new->intr_data;
+	bcopy(&mask, mp->b_wptr, sizeof (mask));
+	mp->b_wptr += sizeof (mask);
+
+	mutex_exit(&xhcip->xhci_lock);
+
+	usba_hcdi_cb(ph, (usb_opaque_t)new, USB_CR_OK);
+}
+
+void
+xhci_root_hub_intr_root_disable(xhci_t *xhcip)
+{
+	usba_pipe_handle_data_t *ph;
+	usb_intr_req_t *uirp;
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+
+	ph = xhcip->xhci_usba.xa_intr_cb_ph;
+	xhcip->xhci_usba.xa_intr_cb_ph = NULL;
+	ASSERT(ph != NULL);
+
+	/*
+	 * If the uirp here is NULL, it's because we ran out of resources at
+	 * some point in xhci_hcdi_psc_callback().
+	 */
+	uirp = xhcip->xhci_usba.xa_intr_cb_req;
+	xhcip->xhci_usba.xa_intr_cb_req = NULL;
+	if (uirp == NULL) {
+		return;
+	}
+
+	mutex_exit(&xhcip->xhci_lock);
+	usba_hcdi_cb(ph, (usb_opaque_t)uirp, USB_CR_STOPPED_POLLING);
+	mutex_enter(&xhcip->xhci_lock);
+
+}
+
+int
+xhci_root_hub_intr_root_enable(xhci_t *xhcip, usba_pipe_handle_data_t *ph,
+    usb_intr_req_t *uirp)
+{
+	ASSERT((ph->p_ep.bEndpointAddress & USB_EP_NUM_MASK) == 1);
+	ASSERT((uirp->intr_attributes & USB_ATTRS_ONE_XFER) == 0);
+
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhcip->xhci_state & XHCI_S_ERROR) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	if (xhcip->xhci_usba.xa_intr_cb_ph != NULL) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_BUSY);
+	}
+
+	xhcip->xhci_usba.xa_intr_cb_ph = ph;
+	xhcip->xhci_usba.xa_intr_cb_req = uirp;
+
+	/*
+	 * USBA is expecting us to act like a hub and therefore whenever we open
+	 * up the interrupt endpoint, we need to generate an event with
+	 * information about all the currently outstanding ports with changes.
+	 */
+	mutex_exit(&xhcip->xhci_lock);
+	xhci_root_hub_psc_callback(xhcip);
+
+	return (USB_SUCCESS);
+}
+
+int
+xhci_root_hub_fini(xhci_t *xhcip)
+{
+	if (usba_hubdi_unbind_root_hub(xhcip->xhci_dip) != USB_SUCCESS)
+		return (DDI_FAILURE);
+
+	return (DDI_SUCCESS);
+}
+
+static int
+xhci_root_hub_fill_hub_desc(xhci_t *xhcip)
+{
+	int i;
+	uint16_t chars;
+	usb_ss_hub_descr_t *hdp = &xhcip->xhci_usba.xa_hub_descr;
+
+	bzero(hdp, sizeof (usb_ss_hub_descr_t));
+
+	hdp->bDescLength = sizeof (usb_ss_hub_descr_t);
+	hdp->bDescriptorType = ROOT_HUB_SS_DESCRIPTOR_TYPE;
+	hdp->bNbrPorts = xhcip->xhci_caps.xcap_max_ports;
+
+	chars = 0;
+	if (xhcip->xhci_caps.xcap_flags & XCAP_PPC)
+		chars |= HUB_CHARS_INDIVIDUAL_PORT_POWER;
+	chars |= HUB_CHARS_INDIV_OVER_CURRENT;
+	hdp->wHubCharacteristics = LE_16(chars);
+	hdp->bPwrOn2PwrGood = XHCI_POWER_GOOD;
+	hdp->bHubContrCurrent = 0;
+
+	/*
+	 * There doesn't appear to be a good way to determine what the impact of
+	 * the root hub on the link should be. However, one way to view it is
+	 * because everything must transfer through here the impact doesn't
+	 * really matter, as everyone is subject to it.
+	 */
+	hdp->bHubHdrDecLat = 0;
+	hdp->wHubDelay = 0;
+
+	for (i = 1; i < xhcip->xhci_caps.xcap_max_ports; i++) {
+		uint32_t reg;
+
+		reg = xhci_get32(xhcip, XHCI_R_OPER, XHCI_PORTSC(i));
+		if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+			xhci_error(xhcip, "encountered fatal FM error while "
+			    "reading port status register %d", i);
+			ddi_fm_service_impact(xhcip->xhci_dip,
+			    DDI_SERVICE_LOST);
+			return (EIO);
+		}
+
+		if (reg & XHCI_PS_DR)
+			hdp->DeviceRemovable[i / 8] |= 1U << (i % 8);
+	}
+
+	return (0);
+}
+
+
+/*
+ * Convert the USB PCI revision which is a uint8_t with 4-bit major and 4-bit
+ * minor into a uint16_t with a 8-bit major and 8-bit minor.
+ */
+static uint16_t
+xhci_root_vers_to_bcd(uint8_t vers)
+{
+	uint8_t major, minor;
+
+	major = (vers & 0xf0) >> 4;
+	minor = (vers & 0x0f);
+	return ((major << 8) | minor);
+}
+
+static void
+xhci_root_hub_fill_dev_desc(xhci_t *xhcip, usb_dev_descr_t *hub)
+{
+	hub->bLength = sizeof (usb_dev_descr_t);
+
+	/*
+	 * The descriptor type is that for a device, which is 0x1.
+	 */
+	hub->bDescriptorType = 0x01;
+	hub->bcdUSB = xhci_root_vers_to_bcd(xhcip->xhci_caps.xcap_usb_vers);
+
+	/*
+	 * As we're trying to pretend we're a hub, we have a fixed device id of
+	 * 0x09. Note, that the device protocol for a super-speed hub
+	 * technically isn't registered as 0x3; however, a vast majority of
+	 * systems out there fake this up to indicate that it's a USB 3.x era
+	 * device. This is presumably due to the suggestions as made in USB 3.1
+	 * / 10.5.1.
+	 */
+	hub->bDeviceClass = USB_CLASS_HUB;
+	hub->bDeviceSubClass = 0x00;
+	hub->bDeviceProtocol = 0x03;
+
+	/*
+	 * The only valid value for a USB 3 device is 09h as indicated in USB
+	 * 3.1 / 9.6.6.
+	 */
+	hub->bMaxPacketSize0 = 9;
+
+	/*
+	 * We have no real identification information, so we set it all to
+	 * zero.
+	 */
+	hub->idVendor = 0x00;
+	hub->idProduct = 0x00;
+	hub->bcdDevice = 0x00;
+	hub->iManufacturer = 0x00;
+	hub->iProduct = 0x00;
+	hub->iSerialNumber = 0x00;
+
+	/*
+	 * To keep our lives simple, we only have a single piece of
+	 * configuration for this device.
+	 */
+	hub->bNumConfigurations = 0x01;
+}
+
+/*
+ * To register a root hub with the framework, we need to fake up a bunch of
+ * information for usba, particularly we need to basically feed it the device
+ * configuration in the form that USB expects. See section 10.15.1 for more
+ * information.
+ */
+int
+xhci_root_hub_init(xhci_t *xhcip)
+{
+	usb_dev_descr_t *hub = &xhcip->xhci_usba.xa_dev_descr;
+	uchar_t *conf = (uchar_t *)&xhci_hcdi_conf;
+
+	xhci_root_hub_fill_dev_desc(xhcip, hub);
+	if (xhci_root_hub_fill_hub_desc(xhcip) != 0)
+		return (DDI_FAILURE);
+
+	if (usba_hubdi_bind_root_hub(xhcip->xhci_dip, conf,
+	    sizeof (xhci_hcdi_conf), hub) != USB_SUCCESS)
+		return (DDI_FAILURE);
+
+	return (DDI_SUCCESS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci_intr.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,193 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * -------------------------
+ * xHCI Interrupt Management
+ * -------------------------
+ *
+ * Interrupts in the xHCI driver are quite straightforward. We only have a
+ * single interrupt, which is always vector zero. Everything is configured to
+ * use this interrupt.
+ *
+ * ------------------
+ * Interrupt Claiming
+ * ------------------
+ *
+ * One of the challenges is knowing when to claim interrupts. Generally
+ * speaking, interrupts for MSI and MSI-X are directed to a specific vector for
+ * a specific device. This allows us to have a bit more confidence on whether
+ * the interrupt is for us. This is contrasted with traditional INTx (pin based)
+ * interrupts in PCI where interrupts are shared between multiple devices.
+ *
+ * xHCI 1.1 / 5.5.2.1 documents the interrupt management register. One of the
+ * quirks here is that when we acknowledge the PCI level MSI or MSI-X, the IP
+ * bit is automatically cleared (see xHCI 1.1 / 4.17.5 for more info). However,
+ * it's not for INTx based systems, thus making things a bit more confusing.
+ * Because of this, we only check the IP bit when we're using INTx interrupts.
+ *
+ * This means that knowing whether or not we can claim something is challenging.
+ * Particularly in the case where we have FM errors. In those cases we opt to
+ * claim rather than not.
+ */
+
+#include <sys/usb/hcd/xhci/xhci.h>
+
+boolean_t
+xhci_ddi_intr_disable(xhci_t *xhcip)
+{
+	int ret;
+
+	if (xhcip->xhci_intr_caps & DDI_INTR_FLAG_BLOCK) {
+		if ((ret = ddi_intr_block_disable(&xhcip->xhci_intr_hdl,
+		    xhcip->xhci_intr_num)) != DDI_SUCCESS) {
+			xhci_error(xhcip, "failed to block-disable interrupts: "
+			    "%d", ret);
+			return (B_FALSE);
+		}
+	} else {
+		if ((ret = ddi_intr_disable(xhcip->xhci_intr_hdl)) !=
+		    DDI_SUCCESS) {
+			xhci_error(xhcip, "failed to disable interrupt: %d",
+			    ret);
+			return (B_FALSE);
+		}
+	}
+
+	return (B_TRUE);
+}
+
+
+boolean_t
+xhci_ddi_intr_enable(xhci_t *xhcip)
+{
+	int ret;
+
+	if (xhcip->xhci_intr_caps & DDI_INTR_FLAG_BLOCK) {
+		if ((ret = ddi_intr_block_enable(&xhcip->xhci_intr_hdl,
+		    xhcip->xhci_intr_num)) != DDI_SUCCESS) {
+			xhci_error(xhcip, "failed to block-enable interrupts: "
+			    "%d", ret);
+			return (B_FALSE);
+		}
+	} else {
+		if ((ret = ddi_intr_enable(xhcip->xhci_intr_hdl)) !=
+		    DDI_SUCCESS) {
+			xhci_error(xhcip, "failed to enable interrupt: %d",
+			    ret);
+			return (B_FALSE);
+		}
+	}
+
+	return (B_TRUE);
+}
+
+/*
+ * Configure the device for interrupts. We need to take care of three things.
+ * Enabling interupt zero, setting interrupt zero's interrupt moderation, and
+ * then enabling interrupts themselves globally.
+ */
+int
+xhci_intr_conf(xhci_t *xhcip)
+{
+	uint32_t reg;
+
+	reg = xhci_get32(xhcip, XHCI_R_RUN, XHCI_IMAN(0));
+	reg |= XHCI_IMAN_INTR_ENA;
+	xhci_put32(xhcip, XHCI_R_RUN, XHCI_IMAN(0), reg);
+
+	xhci_put32(xhcip, XHCI_R_RUN, XHCI_IMOD(0), XHCI_IMOD_DEFAULT);
+
+	reg = xhci_get32(xhcip, XHCI_R_OPER, XHCI_USBCMD);
+	reg |= XHCI_CMD_INTE;
+	xhci_put32(xhcip, XHCI_R_OPER, XHCI_USBCMD, reg);
+
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (EIO);
+	}
+
+	return (0);
+}
+
+uint_t
+xhci_intr(caddr_t arg1, caddr_t arg2)
+{
+	uint32_t iman, status;
+
+	xhci_t *xhcip = (xhci_t *)(void *)arg1;
+	uintptr_t vector = (uintptr_t)arg2;
+
+	ASSERT0(vector);
+
+	/*
+	 * First read the status register.
+	 */
+	status = xhci_get32(xhcip, XHCI_R_OPER, XHCI_USBSTS);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to read USB status register: "
+		    "encountered fatal FM error, resetting device");
+		xhci_fm_runtime_reset(xhcip);
+		return (DDI_INTR_CLAIMED);
+	}
+
+	/*
+	 * Before we read the interrupt management register, check to see if we
+	 * have a fatal bit set. At which point, it's time to reset the world
+	 * anyway.
+	 */
+	if ((status & (XHCI_STS_HSE | XHCI_STS_SRE | XHCI_STS_HCE)) != 0) {
+		xhci_error(xhcip, "found fatal error bit in status register, "
+		    "value: 0x%x: resetting device", status);
+		xhci_fm_runtime_reset(xhcip);
+		return (DDI_INTR_CLAIMED);
+	}
+
+	iman = xhci_get32(xhcip, XHCI_R_RUN, XHCI_IMAN(0));
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to read interrupt register 0: "
+		    "encountered fatal FM error, resetting device");
+		xhci_fm_runtime_reset(xhcip);
+		return (DDI_INTR_CLAIMED);
+	}
+
+	/*
+	 * When using shared interrupts, verify that this interrupt is for us.
+	 * Note that when using MSI and MSI-X, writing to various PCI registers
+	 * can automatically clear this for us.
+	 */
+	if (xhcip->xhci_intr_type == DDI_INTR_TYPE_FIXED &&
+	    (iman & XHCI_IMAN_INTR_PEND) == 0) {
+		return (DDI_INTR_UNCLAIMED);
+	}
+
+	/*
+	 * If we detect some kind of error condition here that's going to result
+	 * in a device reset being dispatched, we purposefully do not clear the
+	 * interrupt and enable it again.
+	 */
+	if (xhci_event_process(xhcip) == B_FALSE) {
+		return (DDI_INTR_CLAIMED);
+	}
+
+	xhci_put32(xhcip, XHCI_R_RUN, XHCI_IMAN(0), iman);
+	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
+		xhci_error(xhcip, "failed to write USB status register: "
+		    "encountered fatal FM error, resetting device");
+		xhci_fm_runtime_reset(xhcip);
+	}
+
+	return (DDI_INTR_CLAIMED);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci_quirks.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,78 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * Collection of known and assembled quirks for devices. These are used while
+ * attaching the controller.
+ *
+ * Please see the big theory statement in xhci.c for more information.
+ */
+
+#include <sys/usb/hcd/xhci/xhci.h>
+
+typedef struct xhci_quirk_table {
+	uint16_t xqt_vendor;
+	uint16_t xqt_device;
+	xhci_quirk_t xqt_quirks;
+} xhci_quirk_table_t;
+
+static xhci_quirk_table_t xhci_quirks[] = {
+	{ 0x1b7e, 0x1000, XHCI_QUIRK_NO_MSI },
+	{ 0x1033, 0x0194, XHCI_QUIRK_32_ONLY },
+	{ 0x1912, 0x0014, XHCI_QUIRK_32_ONLY },
+	{ 0x8086, 0x0f35, XHCI_QUIRK_INTC_EHCI },	/* BayTrail */
+	{ 0x8086, 0x9c31, XHCI_QUIRK_INTC_EHCI },	/* Panther Point */
+	{ 0x8086, 0x1e31, XHCI_QUIRK_INTC_EHCI },	/* Panther Point */
+	{ 0x8086, 0x8c31, XHCI_QUIRK_INTC_EHCI },	/* Lynx Point */
+	{ 0x8086, 0x8cb1, XHCI_QUIRK_INTC_EHCI },	/* Wildcat Point */
+	{ 0x8086, 0x9cb1, XHCI_QUIRK_INTC_EHCI },	/* Wildcat Point-LP */
+	{ 0xffff, 0xffff, 0 }
+};
+
+void
+xhci_quirks_populate(xhci_t *xhcip)
+{
+	xhci_quirk_table_t *xqt;
+
+	for (xqt = &xhci_quirks[0]; xqt->xqt_vendor != 0xffff; xqt++) {
+		if (xqt->xqt_vendor == xhcip->xhci_vendor_id &&
+		    xqt->xqt_device == xhcip->xhci_device_id) {
+			xhcip->xhci_quirks = xqt->xqt_quirks;
+			return;
+		}
+	}
+}
+
+/*
+ * Various Intel Chipsets have shared ports that run under both EHCI and xHCI.
+ * Whenever we reset the controller and its ports, we'll need to toggle these
+ * settings on those platforms. Note that this is generally only needed for
+ * client chipsets and even those have started to drop EHCI.
+ */
+void
+xhci_reroute_intel(xhci_t *xhcip)
+{
+	uint32_t ports;
+
+	ports = pci_config_get32(xhcip->xhci_cfg_handle,
+	    PCI_XHCI_INTEL_USB3PRM);
+	pci_config_put32(xhcip->xhci_cfg_handle, PCI_XHCI_INTEL_USB3_PSSEN,
+	    ports);
+
+	ports = pci_config_get32(xhcip->xhci_cfg_handle,
+	    PCI_XHCI_INTEL_USB2PRM);
+	pci_config_put32(xhcip->xhci_cfg_handle, PCI_XHCI_INTEL_XUSB2PR,
+	    ports);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci_ring.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,444 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * -----------------------------
+ * xHCI Ring Management Routines
+ * -----------------------------
+ *
+ * There are three major different types of rings for xHCI, these are:
+ *
+ * 1) Command Rings
+ * 2) Event Rings
+ * 3) Transfer Rings
+ *
+ * Command and Transfer rings function in similar ways while the event rings are
+ * different. The difference comes in who is the consumer and who is the
+ * producer. In the case of command and transfer rings, the driver is the
+ * producer. For the event ring the driver is the consumer.
+ *
+ * Each ring in xhci has a synthetic head and tail register. Each entry in a
+ * ring has a bit that's often referred to as the 'Cycle bit'. The cycle bit is
+ * toggled as a means of saying that a given entry needs to be consumed.
+ *
+ * When a ring is created, all of the data in it is initialized to zero and the
+ * producer and consumer agree that when the cycle bit is toggled, the ownership
+ * of the entry is transfered from the producer to the consumer.  For example,
+ * the command ring defaults to saying that a cycle bit of one is what indicates
+ * the command is owned by the hardware. So as the driver (the producer) fills
+ * in entries, the driver toggles the cycle bit from 0->1 as part of writing out
+ * the TRB.  When the command ring's doorbell is rung, the hardware (the
+ * consumer) begins processing commands. It will process them until one of two
+ * things happens:
+ *
+ * 1) The hardware encounters an entry with the old cycle bit (0 in this case)
+ *
+ * 2) The hardware hits the last entry in the ring which is a special kind of
+ * entry called a LINK TRB.
+ *
+ * A LINK TRB has two purposes:
+ *
+ * 1) Indicate where processing should be redirected. This can potentially be to
+ * another memory segment; however, this driver always programs LINK TRBs to
+ * point back to the start of the ring.
+ *
+ * 2) Indicate whether or not the cycle bit should be changed. We always
+ * indicate that the cycle bit should be toggled when a LINK TRB is processed.
+ *
+ * In this same example, whereas the driver (the producer) would be setting the
+ * cycle to 1 to indicate that an entry is to be processed, the driver would now
+ * set it to 0. Similarly, the hardware (the consumer) would be looking for a
+ * 0 to determine whether or not it should process the entry.
+ *
+ * Currently, when the driver allocates rings, it always allocates a single page
+ * for the ring. The entire page is dedicated to ring use, which is determined
+ * based on the devices PAGESIZE register. The last entry in a given page is
+ * always configured as a LINK TRB. As each entry in a ring is 16 bytes, this
+ * gives us an average of 255 usable descriptors on x86 and 511 on SPARC, as
+ * PAGESIZE is 4k and 8k respectively.
+ *
+ * The driver is always the producer for all rings except for the event ring,
+ * where it is the consumer.
+ *
+ * ----------------------
+ * Head and Tail Pointers
+ * ----------------------
+ *
+ * Now, while we have the cycle bits for the ring explained, we still need to
+ * keep track of what we consider the head and tail pointers, what the xHCI
+ * specification calls enqueue (head) and dequeue (tail) pointers. Now, in all
+ * the cases here, the actual tracking of the head pointer is basically done by
+ * the cycle bit; however, we maintain an actual offset in the xhci_ring_t
+ * structure. The tail is usually less synthetic; however, it's up for different
+ * folks to maintain it.
+ *
+ * We handle the command and transfer rings the same way. The head pointer
+ * indicates where we should insert the next TRB to transfer. The tail pointer
+ * indicates the last thing that hardware has told us it has processed. If the
+ * head and tail point to the same index, then we know the ring is empty.
+ *
+ * We increment the head pointer whenever we insert an entry. Note that we do
+ * not tell hardware about this in any way, it's just maintained by the cycle
+ * bit. Then, we keep track of what hardware has processed in our tail pointer,
+ * incrementing it only when we have an interrupt that indicates that it's been
+ * processed.
+ *
+ * One oddity here is that we only get notified of this via the event ring. So
+ * when the event ring encounters this information, it needs to go back and
+ * increment our command and transfer ring tails after processing events.
+ *
+ * For the event ring, we handle things differently. We still initialize
+ * everything to zero; however, we start processing things and looking at cycle
+ * bits only when we get an interrupt from hardware. With the event ring, we do
+ * *not* maintain a head pointer (it's still in the structure, but unused).  We
+ * always start processing at the tail pointer and use the cycle bit to indicate
+ * what we should process. Once we're done incrementing things, we go and notify
+ * the hardware of how far we got with this process by updating the tail for the
+ * event ring via a memory mapped register.
+ */
+
+#include <sys/usb/hcd/xhci/xhci.h>
+
+void
+xhci_ring_free(xhci_ring_t *xrp)
+{
+	if (xrp->xr_trb != NULL) {
+		xhci_dma_free(&xrp->xr_dma);
+		xrp->xr_trb = NULL;
+	}
+	xrp->xr_ntrb = 0;
+	xrp->xr_head = 0;
+	xrp->xr_tail = 0;
+	xrp->xr_cycle = 0;
+}
+
+/*
+ * Initialize a ring that hasn't been used and set up its link pointer back to
+ * it.
+ */
+int
+xhci_ring_reset(xhci_t *xhcip, xhci_ring_t *xrp)
+{
+	xhci_trb_t *ltrb;
+
+	ASSERT(xrp->xr_trb != NULL);
+
+	bzero(xrp->xr_trb, sizeof (xhci_trb_t) * xrp->xr_ntrb);
+	xrp->xr_head = 0;
+	xrp->xr_tail = 0;
+	xrp->xr_cycle = 1;
+
+	/*
+	 * Set up the link TRB back to ourselves.
+	 */
+	ltrb = &xrp->xr_trb[xrp->xr_ntrb - 1];
+	ltrb->trb_addr = LE_64(xhci_dma_pa(&xrp->xr_dma));
+	ltrb->trb_flags = LE_32(XHCI_TRB_TYPE_LINK | XHCI_TRB_LINKSEG);
+
+	XHCI_DMA_SYNC(xrp->xr_dma, DDI_DMA_SYNC_FORDEV);
+	if (xhci_check_dma_handle(xhcip, &xrp->xr_dma) != DDI_FM_OK) {
+		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
+		return (EIO);
+	}
+
+	return (0);
+}
+
+int
+xhci_ring_alloc(xhci_t *xhcip, xhci_ring_t *xrp)
+{
+	ddi_dma_attr_t attr;
+	ddi_device_acc_attr_t acc;
+
+	/*
+	 * We use a transfer attribute for the rings as they require 64-byte
+	 * boundaries.
+	 */
+	xhci_dma_acc_attr(xhcip, &acc);
+	xhci_dma_transfer_attr(xhcip, &attr, XHCI_DEF_DMA_SGL);
+	bzero(xrp, sizeof (xhci_ring_t));
+	if (xhci_dma_alloc(xhcip, &xrp->xr_dma, &attr, &acc, B_FALSE,
+	    xhcip->xhci_caps.xcap_pagesize, B_FALSE) == B_FALSE)
+		return (ENOMEM);
+	xrp->xr_trb = (xhci_trb_t *)xrp->xr_dma.xdb_va;
+	xrp->xr_ntrb = xhcip->xhci_caps.xcap_pagesize / sizeof (xhci_trb_t);
+	return (0);
+}
+
+/*
+ * Note, caller should have already synced our DMA memory. This should not be
+ * used for the command ring, as its cycle is maintained by the cycling of the
+ * head. This function is only used for managing the event ring.
+ */
+xhci_trb_t *
+xhci_ring_event_advance(xhci_ring_t *xrp)
+{
+	xhci_trb_t *trb = &xrp->xr_trb[xrp->xr_tail];
+	VERIFY(xrp->xr_tail < xrp->xr_ntrb);
+
+	if (xrp->xr_cycle != (LE_32(trb->trb_flags) & XHCI_TRB_CYCLE))
+		return (NULL);
+
+	/*
+	 * The event ring does not use a link TRB. It instead always uses
+	 * information based on the table to wrap. That means that the last
+	 * entry is in fact going to contain data, so we shouldn't wrap and
+	 * toggle the cycle until after we've processed that, in other words the
+	 * tail equals the total number of entries.
+	 */
+	xrp->xr_tail++;
+	if (xrp->xr_tail == xrp->xr_ntrb) {
+		xrp->xr_cycle ^= 1;
+		xrp->xr_tail = 0;
+	}
+
+	return (trb);
+}
+
+/*
+ * When processing the command ring, we're going to get a single event for each
+ * entry in it. As we've submitted things in order, we need to make sure that
+ * this address matches the DMA address that we'd expect of the current tail.
+ */
+boolean_t
+xhci_ring_trb_tail_valid(xhci_ring_t *xrp, uint64_t dma)
+{
+	uint64_t tail;
+
+	tail = xhci_dma_pa(&xrp->xr_dma) + xrp->xr_tail * sizeof (xhci_trb_t);
+	return (dma == tail);
+}
+
+/*
+ * A variant on the above that checks for a given message within a range of
+ * entries and returns the offset to it from the tail.
+ */
+int
+xhci_ring_trb_valid_range(xhci_ring_t *xrp, uint64_t dma, uint_t range)
+{
+	uint_t i;
+	uint_t tail = xrp->xr_tail;
+	uint64_t taddr;
+
+	VERIFY(range < xrp->xr_ntrb);
+	for (i = 0; i < range; i++) {
+		taddr = xhci_dma_pa(&xrp->xr_dma) + tail * sizeof (xhci_trb_t);
+		if (taddr == dma)
+			return (i);
+
+		tail++;
+		if (tail == xrp->xr_ntrb - 1)
+			tail = 0;
+	}
+
+	return (-1);
+}
+
+/*
+ * Determine whether or not we have enough space for this request in a given
+ * ring for the given request. Note, we have to be a bit careful here and ensure
+ * that we properly handle cases where we cross the link TRB and that we don't
+ * count it.
+ *
+ * To determine if we have enough space for a given number of trbs, we need to
+ * logically advance the head pointer and make sure that we don't cross the tail
+ * pointer. In other words, if after advancement, head == tail, we're in
+ * trouble and don't have enough space.
+ */
+boolean_t
+xhci_ring_trb_space(xhci_ring_t *xrp, uint_t ntrb)
+{
+	uint_t i;
+	uint_t head = xrp->xr_head;
+
+	VERIFY(ntrb > 0);
+	/* We use < to ignore the link TRB */
+	VERIFY(ntrb < xrp->xr_ntrb);
+
+	for (i = 0; i < ntrb; i++) {
+		head++;
+		if (head == xrp->xr_ntrb - 1) {
+			head = 0;
+		}
+
+		if (head == xrp->xr_tail)
+			return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+/*
+ * Fill in a TRB in the ring at offset trboff. If cycle is currently set to
+ * B_TRUE, then we fill in the appropriate cycle bit to tell the system to
+ * advance, otherwise we leave the existing cycle bit untouched so the system
+ * doesn't accidentally advance until we have everything filled in.
+ */
+void
+xhci_ring_trb_fill(xhci_ring_t *xrp, uint_t trboff, xhci_trb_t *host_trb,
+    boolean_t put_cycle)
+{
+	uint_t i;
+	uint32_t flags;
+	uint_t ent = xrp->xr_head;
+	uint8_t cycle = xrp->xr_cycle;
+	xhci_trb_t *trb;
+
+	for (i = 0; i < trboff; i++) {
+		ent++;
+		if (ent == xrp->xr_ntrb - 1) {
+			ent = 0;
+			cycle ^= 1;
+		}
+	}
+
+	/*
+	 * If we're being asked to not update the cycle for it to be valid to be
+	 * produced, we need to xor this once again to get to the inappropriate
+	 * value.
+	 */
+	if (put_cycle == B_FALSE)
+		cycle ^= 1;
+
+	trb = &xrp->xr_trb[ent];
+
+	trb->trb_addr = host_trb->trb_addr;
+	trb->trb_status = host_trb->trb_status;
+	flags = host_trb->trb_flags;
+	if (cycle == 0) {
+		flags &= ~LE_32(XHCI_TRB_CYCLE);
+	} else {
+		flags |= LE_32(XHCI_TRB_CYCLE);
+	}
+
+	trb->trb_flags = flags;
+}
+
+/*
+ * Update our metadata for the ring and verify the cycle bit is correctly set
+ * for the first trb. It is expected that it is incorrectly set.
+ */
+void
+xhci_ring_trb_produce(xhci_ring_t *xrp, uint_t ntrb)
+{
+	uint_t i, ohead;
+	xhci_trb_t *trb;
+
+	VERIFY(ntrb > 0);
+
+	ohead = xrp->xr_head;
+
+	/*
+	 * As part of updating the head, we need to make sure we correctly
+	 * update the cycle bit of the link TRB. So we always do this first
+	 * before we update the old head, to try and get a consistent view of
+	 * the cycle bit.
+	 */
+	for (i = 0; i < ntrb; i++) {
+		xrp->xr_head++;
+		/*
+		 * If we're updating the link TRB, we also need to make sure
+		 * that the Chain bit is set if we're in the middle of a TD
+		 * comprised of multiple TRDs. Thankfully the algorithmn here is
+		 * simple: set it to the value of the previous TRB.
+		 */
+		if (xrp->xr_head == xrp->xr_ntrb - 1) {
+			trb = &xrp->xr_trb[xrp->xr_ntrb - 1];
+			if (xrp->xr_trb[xrp->xr_ntrb - 2].trb_flags &
+			    XHCI_TRB_CHAIN) {
+				trb->trb_flags |= XHCI_TRB_CHAIN;
+			} else {
+				trb->trb_flags &= ~XHCI_TRB_CHAIN;
+
+			}
+			trb->trb_flags ^= LE_32(XHCI_TRB_CYCLE);
+			xrp->xr_cycle ^= 1;
+			xrp->xr_head = 0;
+		}
+	}
+
+	trb = &xrp->xr_trb[ohead];
+	trb->trb_flags ^= LE_32(XHCI_TRB_CYCLE);
+}
+
+/*
+ * This is a convenience wrapper for the single TRB case to make callers less
+ * likely to mess up some of the required semantics.
+ */
+void
+xhci_ring_trb_put(xhci_ring_t *xrp, xhci_trb_t *trb)
+{
+	xhci_ring_trb_fill(xrp, 0U, trb, B_FALSE);
+	xhci_ring_trb_produce(xrp, 1U);
+}
+
+/*
+ * Update the tail pointer for a ring based on the DMA address of a consumed
+ * entry. Note, this entry indicates what we just processed, therefore we should
+ * bump the tail entry to the next one.
+ */
+boolean_t
+xhci_ring_trb_consumed(xhci_ring_t *xrp, uint64_t dma)
+{
+	uint64_t pa = xhci_dma_pa(&xrp->xr_dma);
+	uint64_t high = pa + xrp->xr_ntrb * sizeof (xhci_trb_t);
+
+	if (dma < pa || dma >= high ||
+	    dma % sizeof (xhci_trb_t) != 0)
+		return (B_FALSE);
+
+	dma -= pa;
+	dma /= sizeof (xhci_trb_t);
+
+	VERIFY(dma < xrp->xr_ntrb);
+
+	xrp->xr_tail = dma + 1;
+	if (xrp->xr_tail == xrp->xr_ntrb - 1)
+		xrp->xr_tail = 0;
+
+	return (B_TRUE);
+}
+
+/*
+ * The ring represented here has been reset and we're being asked to basically
+ * skip all outstanding entries. Note, this shouldn't be used for the event
+ * ring. Because the cycle bit is toggled whenever the head moves past the link
+ * trb, the cycle bit is already correct. So in this case, it's really just a
+ * matter of setting the current tail equal to the head, at which point we
+ * consider things empty.
+ */
+void
+xhci_ring_skip(xhci_ring_t *xrp)
+{
+	xrp->xr_tail = xrp->xr_head;
+}
+
+/*
+ * A variant on the normal skip. This basically just tells us to make sure that
+ * that everything this transfer represents has been skipped. Callers need to
+ * make sure that this is actually the first transfer in the ring. Like above,
+ * we don't need to touch the cycle bit.
+ */
+void
+xhci_ring_skip_transfer(xhci_ring_t *xrp, xhci_transfer_t *xt)
+{
+	uint_t i;
+
+	for (i = 0; i < xt->xt_ntrbs; i++) {
+		xrp->xr_tail++;
+		if (xrp->xr_tail == xrp->xr_ntrb - 1)
+			xrp->xr_tail = 0;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/hcd/xhci/xhci_usba.c	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,1989 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * illumos USB framework endpoints and functions for xHCI.
+ *
+ * Please see the big theory statement in xhci.c for more information.
+ */
+
+#include <sys/usb/hcd/xhci/xhci.h>
+#include <sys/sysmacros.h>
+#include <sys/strsun.h>
+#include <sys/strsubr.h>
+
+static xhci_t *
+xhci_hcdi_get_xhcip_from_dev(usba_device_t *ud)
+{
+	dev_info_t *dip = ud->usb_root_hub_dip;
+	xhci_t *xhcip = ddi_get_soft_state(xhci_soft_state,
+	    ddi_get_instance(dip));
+	VERIFY(xhcip != NULL);
+	return (xhcip);
+}
+
+static xhci_t *
+xhci_hcdi_get_xhcip(usba_pipe_handle_data_t *ph)
+{
+	return (xhci_hcdi_get_xhcip_from_dev(ph->p_usba_device));
+}
+
+/*
+ * While the xHCI hardware is capable of supporting power management, we don't
+ * in the driver right now. Note, USBA doesn't seem to end up calling this entry
+ * point.
+ */
+/* ARGSUSED */
+static int
+xhci_hcdi_pm_support(dev_info_t *dip)
+{
+	return (USB_FAILURE);
+}
+
+static int
+xhci_hcdi_pipe_open(usba_pipe_handle_data_t *ph, usb_flags_t usb_flags)
+{
+	xhci_t *xhcip = xhci_hcdi_get_xhcip(ph);
+	xhci_pipe_t *pipe;
+	xhci_endpoint_t *xep;
+	xhci_device_t *xd;
+	int kmflags = usb_flags & USB_FLAGS_SLEEP ? KM_SLEEP : KM_NOSLEEP;
+	int ret;
+	uint_t epid;
+
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhcip->xhci_state & XHCI_S_ERROR) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+	mutex_exit(&xhcip->xhci_lock);
+
+	/*
+	 * If we're here, something must be trying to open an already-opened
+	 * pipe which is bad news.
+	 */
+	if (ph->p_hcd_private != NULL) {
+		return (USB_FAILURE);
+	}
+
+	pipe = kmem_zalloc(sizeof (xhci_pipe_t), kmflags);
+	if (pipe == NULL) {
+		return (USB_NO_RESOURCES);
+	}
+	pipe->xp_opentime = gethrtime();
+	pipe->xp_pipe = ph;
+
+	/*
+	 * If this is the root hub, there's nothing special to do on open. Just
+	 * go ahead and allow it to be opened. All we have to do is add this to
+	 * the list of our tracking structures for open pipes.
+	 */
+	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
+		xep = NULL;
+		goto add;
+	}
+
+	/*
+	 * Now that we're here, we're being asked to open up an endpoint of some
+	 * kind. Because we've already handled the case of the root hub,
+	 * everything should have a device.
+	 */
+	epid = xhci_endpoint_pipe_to_epid(ph);
+	xd = usba_hcdi_get_device_private(ph->p_usba_device);
+	if (xd == NULL) {
+		xhci_error(xhcip, "!encountered endpoint (%d) without device "
+		    "during pipe open", epid);
+		kmem_free(pipe, sizeof (xhci_pipe_t));
+		return (USB_FAILURE);
+	}
+
+	/*
+	 * See if this endpoint exists or not, in general endpoints should not
+	 * exist except for the default control endpoint, which we don't tear
+	 * down until the device itself is cleaned up. Otherwise, a given pipe
+	 * can only be open once.
+	 */
+	mutex_enter(&xhcip->xhci_lock);
+	if (epid == XHCI_DEFAULT_ENDPOINT) {
+		xep = xd->xd_endpoints[epid];
+		VERIFY(xep != NULL);
+		VERIFY(xep->xep_pipe == NULL);
+		xep->xep_pipe = ph;
+		mutex_exit(&xhcip->xhci_lock);
+		ret = xhci_endpoint_update_default(xhcip, xd, xep);
+		if (ret != USB_SUCCESS) {
+			kmem_free(pipe, sizeof (xhci_pipe_t));
+			return (ret);
+		}
+		goto add;
+	}
+
+	if (xd->xd_endpoints[epid] != NULL) {
+		mutex_exit(&xhcip->xhci_lock);
+		kmem_free(pipe, sizeof (xhci_pipe_t));
+		xhci_log(xhcip, "!asked to open endpoint %d on slot %d and "
+		    "port %d, but endpoint already exists", epid, xd->xd_slot,
+		    xd->xd_port);
+		return (USB_FAILURE);
+	}
+
+	/*
+	 * If we're opening an endpoint other than the default control endpoint,
+	 * then the device should have had a USB address assigned by the
+	 * controller. Sanity check that before continuing.
+	 */
+	if (epid != XHCI_DEFAULT_ENDPOINT) {
+		VERIFY(xd->xd_addressed == B_TRUE);
+	}
+
+	/*
+	 * Okay, at this point we need to go create and set up an endpoint.
+	 * Once we're done, we'll try to install it and make sure that it
+	 * doesn't conflict with something else going on.
+	 */
+	ret = xhci_endpoint_init(xhcip, xd, ph);
+	if (ret != 0) {
+		mutex_exit(&xhcip->xhci_lock);
+		kmem_free(pipe, sizeof (xhci_pipe_t));
+		if (ret == EIO) {
+			xhci_error(xhcip, "failed to initialize endpoint %d "
+			    "on device slot %d and port %d: encountered fatal "
+			    "FM error, resetting device", epid, xd->xd_slot,
+			    xd->xd_port);
+			xhci_fm_runtime_reset(xhcip);
+		}
+		return (USB_HC_HARDWARE_ERROR);
+	}
+	xep = xd->xd_endpoints[epid];
+
+	mutex_enter(&xd->xd_imtx);
+	mutex_exit(&xhcip->xhci_lock);
+
+	/*
+	 * Update the slot and input context for this endpoint.
+	 */
+	xd->xd_input->xic_drop_flags = LE_32(0);
+	xd->xd_input->xic_add_flags = LE_32(XHCI_INCTX_MASK_DCI(epid + 1));
+
+	if (epid + 1 > XHCI_SCTX_GET_DCI(LE_32(xd->xd_slotin->xsc_info))) {
+		uint32_t info;
+
+		info = xd->xd_slotin->xsc_info;
+		info &= ~XHCI_SCTX_DCI_MASK;
+		info |= XHCI_SCTX_SET_DCI(epid + 1);
+		xd->xd_slotin->xsc_info = info;
+	}
+
+	XHCI_DMA_SYNC(xd->xd_ictx, DDI_DMA_SYNC_FORDEV);
+	if (xhci_check_dma_handle(xhcip, &xd->xd_ictx) != DDI_FM_OK) {
+		mutex_exit(&xd->xd_imtx);
+		xhci_endpoint_fini(xd, epid);
+		kmem_free(pipe, sizeof (xhci_pipe_t));
+		xhci_error(xhcip, "failed to open pipe on endpoint %d of "
+		    "device with slot %d and port %d: encountered fatal FM "
+		    "error syncing device input context, resetting device",
+		    epid, xd->xd_slot, xd->xd_port);
+		xhci_fm_runtime_reset(xhcip);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	if ((ret = xhci_command_configure_endpoint(xhcip, xd)) != USB_SUCCESS) {
+		mutex_exit(&xd->xd_imtx);
+		xhci_endpoint_fini(xd, epid);
+		kmem_free(pipe, sizeof (xhci_pipe_t));
+		return (ret);
+	}
+
+	mutex_exit(&xd->xd_imtx);
+add:
+	pipe->xp_ep = xep;
+	ph->p_hcd_private = (usb_opaque_t)pipe;
+	mutex_enter(&xhcip->xhci_lock);
+	list_insert_tail(&xhcip->xhci_usba.xa_pipes, pipe);
+	mutex_exit(&xhcip->xhci_lock);
+
+	return (USB_SUCCESS);
+}
+
+static void
+xhci_hcdi_periodic_free(xhci_t *xhcip, xhci_pipe_t *xp)
+{
+	int i;
+	xhci_periodic_pipe_t *xpp = &xp->xp_periodic;
+
+	if (xpp->xpp_tsize == 0)
+		return;
+
+	for (i = 0; i < xpp->xpp_ntransfers; i++) {
+		if (xpp->xpp_transfers[i] == NULL)
+			continue;
+		xhci_transfer_free(xhcip, xpp->xpp_transfers[i]);
+		xpp->xpp_transfers[i] = NULL;
+	}
+
+	xpp->xpp_ntransfers = 0;
+	xpp->xpp_tsize = 0;
+}
+
+/*
+ * Iterate over all transfers and free everything on the pipe. Once done, update
+ * the ring to basically 'consume' everything. For periodic IN endpoints, we
+ * need to handle this somewhat differently and actually close the original
+ * request and not deallocate the related pieces as those exist for the lifetime
+ * of the endpoint and are constantly reused.
+ */
+static void
+xhci_hcdi_pipe_flush(xhci_t *xhcip, xhci_endpoint_t *xep, int intr_code)
+{
+	xhci_transfer_t *xt;
+
+	ASSERT(MUTEX_HELD(&xhcip->xhci_lock));
+
+	while ((xt = list_remove_head(&xep->xep_transfers)) != NULL) {
+		if (xhci_endpoint_is_periodic_in(xep) == B_FALSE) {
+			usba_hcdi_cb(xep->xep_pipe, xt->xt_usba_req,
+			    USB_CR_FLUSHED);
+			xhci_transfer_free(xhcip, xt);
+		}
+	}
+
+	if (xhci_endpoint_is_periodic_in(xep) == B_TRUE) {
+		xhci_pipe_t *xp = (xhci_pipe_t *)xep->xep_pipe->p_hcd_private;
+		xhci_periodic_pipe_t *xpp = &xp->xp_periodic;
+
+		if (xpp->xpp_usb_req != NULL) {
+			usba_hcdi_cb(xep->xep_pipe, xpp->xpp_usb_req,
+			    intr_code);
+			xpp->xpp_usb_req = NULL;
+		}
+	}
+}
+
+/*
+ * We've been asked to terminate some set of regular I/O on an interrupt pipe.
+ * If this is for the root device, e.g. the xhci driver itself, then we remove
+ * our interrupt callback. Otherwise we stop the device for interrupt polling as
+ * follows:
+ *
+ * 1. Issue a stop endpoint command
+ * 2. Check to make sure that the endpoint stopped and reset it if needed.
+ * 3. Any thing that gets resolved can callback in the interim.
+ * 4. Ensure that nothing is scheduled on the ring
+ * 5. Skip the contents of the ring and set the TR dequeue pointer.
+ * 6. Return the original callback with a USB_CR_STOPPED_POLLING, NULL out the
+ *    callback in the process.
+ */
+static int
+xhci_hcdi_pipe_poll_fini(usba_pipe_handle_data_t *ph, boolean_t is_close)
+{
+	int ret;
+	uint_t epid;
+	xhci_t *xhcip = xhci_hcdi_get_xhcip(ph);
+	xhci_device_t *xd;
+	xhci_endpoint_t *xep;
+	xhci_pipe_t *xp;
+	xhci_periodic_pipe_t *xpp;
+	usb_opaque_t urp;
+
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhcip->xhci_state & XHCI_S_ERROR) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
+		xhci_root_hub_intr_root_disable(xhcip);
+		ret = USB_SUCCESS;
+		mutex_exit(&xhcip->xhci_lock);
+		return (ret);
+	}
+
+	xd = usba_hcdi_get_device_private(ph->p_usba_device);
+	epid = xhci_endpoint_pipe_to_epid(ph);
+	if (xd->xd_endpoints[epid] == NULL) {
+		mutex_exit(&xhcip->xhci_lock);
+		xhci_error(xhcip, "asked to stop intr polling on slot %d, "
+		    "port %d, endpoint: %d, but no endpoint structure",
+		    xd->xd_slot, xd->xd_port, epid);
+		return (USB_FAILURE);
+	}
+	xep = xd->xd_endpoints[epid];
+	xp = (xhci_pipe_t *)ph->p_hcd_private;
+	if (xp == NULL) {
+		mutex_exit(&xhcip->xhci_lock);
+		xhci_error(xhcip, "asked to do finish polling on slot %d, "
+		    "port %d, endpoint: %d, but no pipe structure",
+		    xd->xd_slot, xd->xd_port, epid);
+		return (USB_FAILURE);
+	}
+	xpp = &xp->xp_periodic;
+
+	/*
+	 * Ensure that no other resets or time outs are going on right now.
+	 */
+	while ((xep->xep_state & (XHCI_ENDPOINT_SERIALIZE)) != 0) {
+		cv_wait(&xep->xep_state_cv, &xhcip->xhci_lock);
+	}
+
+	if (xpp->xpp_poll_state == XHCI_PERIODIC_POLL_IDLE) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_SUCCESS);
+	}
+
+	if (xpp->xpp_poll_state == XHCI_PERIODIC_POLL_STOPPING) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_FAILURE);
+	}
+
+	xpp->xpp_poll_state = XHCI_PERIODIC_POLL_STOPPING;
+	xep->xep_state |= XHCI_ENDPOINT_QUIESCE;
+	ret = xhci_endpoint_quiesce(xhcip, xd, xep);
+	if (ret != USB_SUCCESS) {
+		xhci_error(xhcip, "!failed to quiesce endpoint on slot %d, "
+		    "port %d, endpoint: %d, failed with %d.",
+		    xd->xd_slot, xd->xd_port, epid, ret);
+		xep->xep_state &= ~XHCI_ENDPOINT_QUIESCE;
+		cv_broadcast(&xep->xep_state_cv);
+		mutex_exit(&xhcip->xhci_lock);
+		return (ret);
+	}
+
+	/*
+	 * Okay, we've stopped this ring time to wrap it all up. Remove all the
+	 * transfers, note they aren't freed like a pipe reset.
+	 */
+	while (list_is_empty(&xep->xep_transfers) == 0)
+		(void) list_remove_head(&xep->xep_transfers);
+	xhci_ring_skip(&xep->xep_ring);
+	mutex_exit(&xhcip->xhci_lock);
+
+	if ((ret = xhci_command_set_tr_dequeue(xhcip, xd, xep)) !=
+	    USB_SUCCESS) {
+		xhci_error(xhcip, "!failed to reset endpoint ring on slot %d, "
+		    "port %d, endpoint: %d, failed with %d.",
+		    xd->xd_slot, xd->xd_port, epid, ret);
+		mutex_enter(&xhcip->xhci_lock);
+		xep->xep_state &= ~XHCI_ENDPOINT_QUIESCE;
+		cv_broadcast(&xep->xep_state_cv);
+		mutex_exit(&xhcip->xhci_lock);
+		return (ret);
+	}
+
+	mutex_enter(&xhcip->xhci_lock);
+	urp = xpp->xpp_usb_req;
+	xpp->xpp_usb_req = NULL;
+	xpp->xpp_poll_state = XHCI_PERIODIC_POLL_IDLE;
+	xep->xep_state &= ~XHCI_ENDPOINT_PERIODIC;
+	mutex_exit(&xhcip->xhci_lock);
+
+	/*
+	 * It's possible that with a persistent pipe, we may not actually have
+	 * anything left to call back on, because we already had.
+	 */
+	if (urp != NULL) {
+		usba_hcdi_cb(ph, urp, is_close == B_TRUE ?
+		    USB_CR_PIPE_CLOSING : USB_CR_STOPPED_POLLING);
+	}
+
+	/*
+	 * Notify anything waiting for us that we're done quiescing this device.
+	 */
+	mutex_enter(&xhcip->xhci_lock);
+	xep->xep_state &= ~XHCI_ENDPOINT_QUIESCE;
+	cv_broadcast(&xep->xep_state_cv);
+	mutex_exit(&xhcip->xhci_lock);
+
+	return (USB_SUCCESS);
+
+}
+
+/*
+ * Tear down everything that we did in open. After this, the consumer of this
+ * USB device is done.
+ */
+/* ARGSUSED */
+static int
+xhci_hcdi_pipe_close(usba_pipe_handle_data_t *ph, usb_flags_t usb_flags)
+{
+	xhci_t *xhcip = xhci_hcdi_get_xhcip(ph);
+	xhci_pipe_t *xp;
+	xhci_device_t *xd;
+	xhci_endpoint_t *xep;
+	uint32_t info;
+	int ret, i;
+	uint_t epid;
+
+	if ((ph->p_ep.bmAttributes & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR &&
+	    xhcip->xhci_usba.xa_intr_cb_ph != NULL) {
+		if ((ret = xhci_hcdi_pipe_poll_fini(ph, B_TRUE)) !=
+		    USB_SUCCESS) {
+			return (ret);
+		}
+	}
+
+	mutex_enter(&xhcip->xhci_lock);
+
+	xp = (xhci_pipe_t *)ph->p_hcd_private;
+	VERIFY(xp != NULL);
+
+	/*
+	 * The default endpoint is special. It is created and destroyed with the
+	 * device. So like with open, closing it is just state tracking. The
+	 * same is true for the root hub.
+	 */
+	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR)
+		goto remove;
+
+	xd = usba_hcdi_get_device_private(ph->p_usba_device);
+	epid = xhci_endpoint_pipe_to_epid(ph);
+	if (xd->xd_endpoints[epid] == NULL) {
+		mutex_exit(&xhcip->xhci_lock);
+		xhci_error(xhcip, "asked to do close pipe on slot %d, "
+		    "port %d, endpoint: %d, but no endpoint structure",
+		    xd->xd_slot, xd->xd_port, epid);
+		return (USB_FAILURE);
+	}
+	xep = xd->xd_endpoints[epid];
+
+	if (xp->xp_ep != NULL && xp->xp_ep->xep_num == XHCI_DEFAULT_ENDPOINT) {
+		xep->xep_pipe = NULL;
+		goto remove;
+	}
+
+	/*
+	 * We need to clean up the endpoint. So the first thing we need to do is
+	 * stop it with a configure endpoint command. Once it's stopped, we can
+	 * free all associated resources.
+	 */
+	mutex_enter(&xd->xd_imtx);
+
+	/*
+	 * Potentially update the slot input context about the current max
+	 * endpoint. While we don't update the slot context with this,
+	 * surrounding code expects it to be updated to be consistent.
+	 */
+	xd->xd_input->xic_drop_flags = LE_32(XHCI_INCTX_MASK_DCI(epid + 1));
+	xd->xd_input->xic_add_flags = LE_32(0);
+	for (i = XHCI_NUM_ENDPOINTS - 1; i >= 0; i--) {
+		if (xd->xd_endpoints[i] != NULL &&
+		    xd->xd_endpoints[i] != xep)
+			break;
+	}
+	info = xd->xd_slotin->xsc_info;
+	info &= ~XHCI_SCTX_DCI_MASK;
+	info |= XHCI_SCTX_SET_DCI(i + 1);
+	xd->xd_slotin->xsc_info = info;
+
+	/*
+	 * Also zero out our context for this endpoint. Note that we don't
+	 * bother with syncing DMA memory here as it's not required to be synced
+	 * for this operation.
+	 */
+	bzero(xd->xd_endin[xep->xep_num], sizeof (xhci_endpoint_context_t));
+
+	/*
+	 * Stop the device and kill our timeout. Note, it is safe to hold the
+	 * device's input mutex across the untimeout, this lock should never be
+	 * referenced by the timeout code.
+	 */
+	xep->xep_state |= XHCI_ENDPOINT_TEARDOWN;
+	mutex_exit(&xhcip->xhci_lock);
+	(void) untimeout(xep->xep_timeout);
+
+	ret = xhci_command_configure_endpoint(xhcip, xd);
+	mutex_exit(&xd->xd_imtx);
+	if (ret != USB_SUCCESS)
+		return (ret);
+	mutex_enter(&xhcip->xhci_lock);
+
+	/*
+	 * Now that we've unconfigured the endpoint. See if we need to flush any
+	 * transfers.
+	 */
+	xhci_hcdi_pipe_flush(xhcip, xep, USB_CR_PIPE_CLOSING);
+	if ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
+		xhci_hcdi_periodic_free(xhcip, xp);
+	}
+
+	xhci_endpoint_fini(xd, epid);
+
+remove:
+	ph->p_hcd_private = NULL;
+	list_remove(&xhcip->xhci_usba.xa_pipes, xp);
+	kmem_free(xp, sizeof (xhci_pipe_t));
+
+	mutex_exit(&xhcip->xhci_lock);
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * We've been asked to reset a pipe aka an endpoint. This endpoint may be in an
+ * arbitrary state, it may be running or it may be halted. In this case, we go
+ * through and check whether or not we know it's been halted or not. If it has
+ * not, then we stop the endpoint.
+ *
+ * Once the endpoint has been stopped, walk all transfers and go ahead and
+ * basically return them as being flushed. Then finally set the dequeue point
+ * for this endpoint.
+ */
+/* ARGSUSED */
+static int
+xhci_hcdi_pipe_reset(usba_pipe_handle_data_t *ph, usb_flags_t usb_flags)
+{
+	xhci_t *xhcip = xhci_hcdi_get_xhcip(ph);
+	xhci_device_t *xd;
+	xhci_endpoint_t *xep;
+	uint_t epid;
+	int ret;
+
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhcip->xhci_state & XHCI_S_ERROR) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_NOT_SUPPORTED);
+	}
+
+	xd = usba_hcdi_get_device_private(ph->p_usba_device);
+	epid = xhci_endpoint_pipe_to_epid(ph);
+	if (xd->xd_endpoints[epid] == NULL) {
+		mutex_exit(&xhcip->xhci_lock);
+		xhci_error(xhcip, "asked to do reset pipe on slot %d, "
+		    "port %d, endpoint: %d, but no endpoint structure",
+		    xd->xd_slot, xd->xd_port, epid);
+		return (USB_FAILURE);
+	}
+
+	xep = xd->xd_endpoints[epid];
+
+	/*
+	 * Ensure that no other resets or time outs are going on right now.
+	 */
+	while ((xep->xep_state & (XHCI_ENDPOINT_SERIALIZE)) != 0) {
+		cv_wait(&xep->xep_state_cv, &xhcip->xhci_lock);
+	}
+
+	xep->xep_state |= XHCI_ENDPOINT_QUIESCE;
+	ret = xhci_endpoint_quiesce(xhcip, xd, xep);
+	if (ret != USB_SUCCESS) {
+		/*
+		 * We failed to quiesce for some reason, remove the flag and let
+		 * someone else give it a shot.
+		 */
+		xhci_error(xhcip, "!failed to quiesce endpoint on slot %d, "
+		    "port %d, endpoint: %d, failed with %d.",
+		    xd->xd_slot, xd->xd_port, epid, ret);
+		xep->xep_state &= ~XHCI_ENDPOINT_QUIESCE;
+		cv_broadcast(&xep->xep_state_cv);
+		mutex_exit(&xhcip->xhci_lock);
+		return (ret);
+	}
+
+	xhci_ring_skip(&xep->xep_ring);
+
+	mutex_exit(&xhcip->xhci_lock);
+	if ((ret = xhci_command_set_tr_dequeue(xhcip, xd, xep)) !=
+	    USB_SUCCESS) {
+		xhci_error(xhcip, "!failed to reset endpoint ring on slot %d, "
+		    "port %d, endpoint: %d, failed setting ring dequeue with "
+		    "%d.", xd->xd_slot, xd->xd_port, epid, ret);
+		mutex_enter(&xhcip->xhci_lock);
+		xep->xep_state &= ~XHCI_ENDPOINT_QUIESCE;
+		cv_broadcast(&xep->xep_state_cv);
+		mutex_exit(&xhcip->xhci_lock);
+		return (ret);
+	}
+
+	mutex_enter(&xhcip->xhci_lock);
+	xhci_hcdi_pipe_flush(xhcip, xep, USB_CR_PIPE_RESET);
+
+	/*
+	 * We need to remove the periodic flag as part of resetting, as if this
+	 * was used for periodic activity, it no longer is and therefore can now
+	 * be used for such purposes.
+	 *
+	 * Notify anything waiting for us that we're done quiescing this device.
+	 */
+	xep->xep_state &= ~(XHCI_ENDPOINT_QUIESCE | XHCI_ENDPOINT_PERIODIC);
+	cv_broadcast(&xep->xep_state_cv);
+	mutex_exit(&xhcip->xhci_lock);
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * We're asked to reset or change the data toggle, which is used in a few cases.
+ * However, there doesn't seem to be a good way to do this in xHCI as the data
+ * toggle isn't exposed. It seems that dropping a reset endpoint would
+ * theoretically do this; however, that can only be used when in the HALTED
+ * state. As such, for now we just return.
+ */
+/* ARGSUSED */
+void
+xhci_hcdi_pipe_reset_data_toggle(usba_pipe_handle_data_t *pipe_handle)
+{
+}
+
+/*
+ * We need to convert the USB request to an 8-byte little endian value. If we
+ * didn't have to think about big endian systems, this would be fine.
+ * Unfortunately, with them, this is a bit confusing. The problem is that if you
+ * think of this as a struct layout, the order that we or things together
+ * represents their byte layout. e.g. ctrl_bRequest is at offset 1 in the SETUP
+ * STAGE trb. However, when it becomes a part of a 64-bit big endian number, if
+ * ends up at byte 7, where as it needs to be at one. Hence why we do a final
+ * LE_64 at the end of this, to convert this into the byte order that it's
+ * expected to be in.
+ */
+static uint64_t
+xhci_hcdi_ctrl_req_to_trb(usb_ctrl_req_t *ucrp)
+{
+	uint64_t ret = ucrp->ctrl_bmRequestType |
+	    (ucrp->ctrl_bRequest << 8) |
+	    ((uint64_t)LE_16(ucrp->ctrl_wValue) << 16) |
+	    ((uint64_t)LE_16(ucrp->ctrl_wIndex) << 32) |
+	    ((uint64_t)LE_16(ucrp->ctrl_wLength) << 48);
+	return (LE_64(ret));
+}
+
+/*
+ * USBA calls us in order to make a specific control type request to a device,
+ * potentially even the root hub. If the request is for the root hub, then we
+ * need to intercept this and cons up the requested data.
+ */
+static int
+xhci_hcdi_pipe_ctrl_xfer(usba_pipe_handle_data_t *ph, usb_ctrl_req_t *ucrp,
+    usb_flags_t usb_flags)
+{
+	int ret, statusdir, trt;
+	uint_t ep;
+	xhci_device_t *xd;
+	xhci_endpoint_t *xep;
+	xhci_transfer_t *xt;
+	boolean_t datain;
+
+	xhci_t *xhcip = xhci_hcdi_get_xhcip(ph);
+
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhcip->xhci_state & XHCI_S_ERROR) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
+		ret = xhci_root_hub_ctrl_req(xhcip, ph, ucrp);
+		mutex_exit(&xhcip->xhci_lock);
+		return (ret);
+	}
+
+	/*
+	 * Determine the device and endpoint.
+	 */
+	xd = usba_hcdi_get_device_private(ph->p_usba_device);
+	ep = xhci_endpoint_pipe_to_epid(ph);
+	if (xd->xd_endpoints[ep] == NULL) {
+		mutex_exit(&xhcip->xhci_lock);
+		xhci_error(xhcip, "asked to do control transfer on slot %d, "
+		    "port %d, endpoint: %d, but no endpoint structure",
+		    xd->xd_slot, xd->xd_port, ep);
+		return (USB_FAILURE);
+	}
+	xep = xd->xd_endpoints[ep];
+
+	/*
+	 * There are several types of requests that we have to handle in special
+	 * ways in xHCI. If we have one of those requests, then we don't
+	 * necessarily go through the normal path. These special cases are all
+	 * documented in xHCI 1.1 / 4.5.4.
+	 *
+	 * Looking at that, you may ask why aren't SET_CONFIGURATION and SET_IF
+	 * special cased here. This action is a little confusing by default. The
+	 * xHCI specification requires that we may need to issue a configure
+	 * endpoint command as part of this. However, the xHCI 1.1 / 4.5.4.2
+	 * states that we don't actually need to if nothing in the endpoint
+	 * configuration context has changed. Because nothing in it should have
+	 * changed as part of this, we don't need to do anything and instead
+	 * just can issue the request normally. We're also assuming in the
+	 * USB_REQ_SET_IF case that if something's changing the interface, the
+	 * non-default endpoint will have yet to be opened.
+	 */
+	if (ucrp->ctrl_bmRequestType == USB_DEV_REQ_HOST_TO_DEV &&
+	    ucrp->ctrl_bRequest == USB_REQ_SET_ADDRESS) {
+		/*
+		 * As we've defined an explicit set-address endpoint, we should
+		 * never call this function. If we get here, always fail.
+		 */
+		mutex_exit(&xhcip->xhci_lock);
+		usba_hcdi_cb(ph, (usb_opaque_t)ucrp, USB_CR_NOT_SUPPORTED);
+		return (USB_SUCCESS);
+	}
+
+	mutex_exit(&xhcip->xhci_lock);
+
+	/*
+	 * Allocate the transfer memory, etc.
+	 */
+	xt = xhci_transfer_alloc(xhcip, xep, ucrp->ctrl_wLength, 2, usb_flags);
+	if (xt == NULL) {
+		return (USB_NO_RESOURCES);
+	}
+	xt->xt_usba_req = (usb_opaque_t)ucrp;
+	xt->xt_timeout = ucrp->ctrl_timeout;
+	if (xt->xt_timeout == 0) {
+		xt->xt_timeout = HCDI_DEFAULT_TIMEOUT;
+	}
+
+	if (ucrp->ctrl_wLength > 0) {
+		if ((ucrp->ctrl_bmRequestType & USB_DEV_REQ_DEV_TO_HOST) != 0) {
+			trt = XHCI_TRB_TRT_IN;
+			datain = B_TRUE;
+			statusdir = 0;
+		} else {
+			trt = XHCI_TRB_TRT_OUT;
+			datain = B_FALSE;
+			statusdir = XHCI_TRB_DIR_IN;
+
+			xhci_transfer_copy(xt, ucrp->ctrl_data->b_rptr,
+			    ucrp->ctrl_wLength, B_FALSE);
+			if (xhci_transfer_sync(xhcip, xt,
+			    DDI_DMA_SYNC_FORDEV) != DDI_FM_OK) {
+				xhci_transfer_free(xhcip, xt);
+				xhci_error(xhcip, "failed to synchronize ctrl "
+				    "transfer DMA memory on endpoint %u of "
+				    "device on slot %d and port %d: resetting "
+				    "device", xep->xep_num, xd->xd_slot,
+				    xd->xd_port);
+				xhci_fm_runtime_reset(xhcip);
+				return (USB_HC_HARDWARE_ERROR);
+			}
+		}
+	} else {
+		trt = 0;
+		datain = B_FALSE;
+		statusdir = XHCI_TRB_DIR_IN;
+	}
+
+	/*
+	 * We always fill in the required setup and status TRBs ourselves;
+	 * however, to minimize our knowledge about how the data has been split
+	 * across multiple DMA cookies in an SGL, we leave that to the transfer
+	 * logic to fill in.
+	 */
+	xt->xt_trbs[0].trb_addr = xhci_hcdi_ctrl_req_to_trb(ucrp);
+	xt->xt_trbs[0].trb_status = LE_32(XHCI_TRB_LEN(8) | XHCI_TRB_INTR(0));
+	xt->xt_trbs[0].trb_flags = LE_32(trt | XHCI_TRB_IDT |
+	    XHCI_TRB_TYPE_SETUP);
+
+	if (ucrp->ctrl_wLength > 0)
+		xhci_transfer_trb_fill_data(xep, xt, 1, datain);
+
+	xt->xt_trbs[xt->xt_ntrbs - 1].trb_addr = 0;
+	xt->xt_trbs[xt->xt_ntrbs - 1].trb_status = LE_32(XHCI_TRB_INTR(0));
+	xt->xt_trbs[xt->xt_ntrbs - 1].trb_flags = LE_32(XHCI_TRB_TYPE_STATUS |
+	    XHCI_TRB_IOC | statusdir);
+
+	mutex_enter(&xhcip->xhci_lock);
+
+	/*
+	 * Schedule the transfer, allocating resources in the process.
+	 */
+	if (xhci_endpoint_schedule(xhcip, xd, xep, xt, B_TRUE) != 0) {
+		xhci_transfer_free(xhcip, xt);
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_NO_RESOURCES);
+	}
+
+	mutex_exit(&xhcip->xhci_lock);
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * This request is trying to get the upper bound on the amount of data we're
+ * willing transfer in one go. Note that this amount can be broken down into
+ * multiple SGL entries, this interface doesn't particularly care about that.
+ */
+/* ARGSUSED */
+static int
+xhci_hcdi_bulk_transfer_size(usba_device_t *ud, size_t *sizep)
+{
+	if (sizep != NULL)
+		*sizep = XHCI_MAX_TRANSFER;
+	return (USB_SUCCESS);
+}
+
+/*
+ * Perform a bulk transfer. This is a pretty straightforward action. We
+ * basically just allocate the appropriate transfer and try to schedule it,
+ * hoping there is enough space.
+ */
+static int
+xhci_hcdi_pipe_bulk_xfer(usba_pipe_handle_data_t *ph, usb_bulk_req_t *ubrp,
+    usb_flags_t usb_flags)
+{
+	uint_t epid;
+	xhci_device_t *xd;
+	xhci_endpoint_t *xep;
+	xhci_transfer_t *xt;
+	boolean_t datain;
+
+	xhci_t *xhcip = xhci_hcdi_get_xhcip(ph);
+
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhcip->xhci_state & XHCI_S_ERROR) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_NOT_SUPPORTED);
+	}
+
+	xd = usba_hcdi_get_device_private(ph->p_usba_device);
+	epid = xhci_endpoint_pipe_to_epid(ph);
+	if (xd->xd_endpoints[epid] == NULL) {
+		mutex_exit(&xhcip->xhci_lock);
+		xhci_error(xhcip, "asked to do control transfer on slot %d, "
+		    "port %d, endpoint: %d, but no endpoint structure",
+		    xd->xd_slot, xd->xd_port, epid);
+		return (USB_FAILURE);
+	}
+	xep = xd->xd_endpoints[epid];
+	mutex_exit(&xhcip->xhci_lock);
+
+	if ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
+		datain = B_TRUE;
+	} else {
+		datain = B_FALSE;
+	}
+
+	xt = xhci_transfer_alloc(xhcip, xep, ubrp->bulk_len, 0, usb_flags);
+	if (xt == NULL) {
+		return (USB_NO_RESOURCES);
+	}
+	xt->xt_usba_req = (usb_opaque_t)ubrp;
+	xt->xt_timeout = ubrp->bulk_timeout;
+	if (xt->xt_timeout == 0) {
+		xt->xt_timeout = HCDI_DEFAULT_TIMEOUT;
+	}
+
+	if (ubrp->bulk_len > 0 && datain == B_FALSE) {
+		xhci_transfer_copy(xt, ubrp->bulk_data->b_rptr, ubrp->bulk_len,
+		    B_FALSE);
+		if (xhci_transfer_sync(xhcip, xt, DDI_DMA_SYNC_FORDEV) !=
+		    DDI_FM_OK) {
+			xhci_transfer_free(xhcip, xt);
+			xhci_error(xhcip, "failed to synchronize bulk "
+			    "transfer DMA memory on endpoint %u of "
+			    "device on slot %d and port %d: resetting "
+			    "device", xep->xep_num, xd->xd_slot,
+			    xd->xd_port);
+			xhci_fm_runtime_reset(xhcip);
+			return (USB_HC_HARDWARE_ERROR);
+		}
+	}
+
+	xhci_transfer_trb_fill_data(xep, xt, 0, datain);
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhci_endpoint_schedule(xhcip, xd, xep, xt, B_TRUE) != 0) {
+		xhci_transfer_free(xhcip, xt);
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_NO_RESOURCES);
+	}
+	mutex_exit(&xhcip->xhci_lock);
+
+	return (USB_SUCCESS);
+}
+
+static void
+xhci_hcdi_isoc_transfer_fill(xhci_device_t *xd, xhci_endpoint_t *xep,
+    xhci_transfer_t *xt, usb_isoc_req_t *usrp)
+{
+	int i;
+	uintptr_t buf;
+
+	buf = xt->xt_buffer.xdb_cookies[0].dmac_laddress;
+	for (i = 0; i < usrp->isoc_pkts_count; i++) {
+		int flags;
+		uint_t tbc, tlbpc;
+
+		ushort_t len = usrp->isoc_pkt_descr[i].isoc_pkt_length;
+		xhci_trb_t *trb = &xt->xt_trbs[i];
+
+		trb->trb_addr = LE_64(buf);
+
+		/*
+		 * Beacuse we know that a single frame can have all of its data
+		 * in a single instance, we know that we don't neeed to do
+		 * anything special here.
+		 */
+		trb->trb_status = LE_32(XHCI_TRB_LEN(len) | XHCI_TRB_TDREM(0) |
+		    XHCI_TRB_INTR(0));
+
+		/*
+		 * Always enable SIA to start the frame ASAP. We also always
+		 * enable an interrupt on a short packet. If this is the last
+		 * trb, then we will set IOC.
+		 */
+		flags = XHCI_TRB_SIA | XHCI_TRB_ISP | XHCI_TRB_SET_FRAME(0);
+		flags |= XHCI_TRB_TYPE_ISOCH;
+
+		if (i + 1 == usrp->isoc_pkts_count)
+			flags |= XHCI_TRB_IOC;
+
+		/*
+		 * Now we need to calculate the TBC and the TLBPC.
+		 */
+		xhci_transfer_calculate_isoc(xd, xep, len, &tbc, &tlbpc);
+		flags |= XHCI_TRB_SET_TBC(tbc);
+		flags |= XHCI_TRB_SET_TLBPC(tlbpc);
+
+		trb->trb_flags = LE_32(flags);
+		buf += len;
+
+		/*
+		 * Go through and copy the required data to our local copy of
+		 * the isoc descriptor. By default, we assume that all data will
+		 * be copied and the status set to OK. This mirrors the fact
+		 * that we won't get a notification unless there's been an
+		 * error or short packet transfer.
+		 */
+		xt->xt_isoc[i].isoc_pkt_length = len;
+		xt->xt_isoc[i].isoc_pkt_actual_length = len;
+		xt->xt_isoc[i].isoc_pkt_status = USB_CR_OK;
+	}
+}
+
+/*
+ * Initialize periodic IN requests (both interrupt and isochronous)
+ */
+static int
+xhci_hcdi_periodic_init(xhci_t *xhcip, usba_pipe_handle_data_t *ph,
+    usb_opaque_t usb_req, size_t len, int usb_flags)
+{
+	int i, ret;
+	uint_t epid;
+	xhci_device_t *xd;
+	xhci_endpoint_t *xep;
+	xhci_pipe_t *xp;
+	xhci_periodic_pipe_t *xpp;
+
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhcip->xhci_state & XHCI_S_ERROR) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	xd = usba_hcdi_get_device_private(ph->p_usba_device);
+	epid = xhci_endpoint_pipe_to_epid(ph);
+	if (xd->xd_endpoints[epid] == NULL) {
+		xhci_error(xhcip, "asked to do periodic transfer on slot %d, "
+		    "port %d, endpoint: %d, but no endpoint structure",
+		    xd->xd_slot, xd->xd_port, epid);
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_FAILURE);
+	}
+	xep = xd->xd_endpoints[epid];
+	xp = (xhci_pipe_t *)ph->p_hcd_private;
+	if (xp == NULL) {
+		xhci_error(xhcip, "asked to do periodic transfer on slot %d, "
+		    "port %d, endpoint: %d, but no pipe structure",
+		    xd->xd_slot, xd->xd_port, epid);
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_FAILURE);
+	}
+	xpp = &xp->xp_periodic;
+
+	/*
+	 * Only allow a single polling request at any given time.
+	 */
+	if (xpp->xpp_usb_req != NULL) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_BUSY);
+	}
+
+	/*
+	 * We keep allocations around in case we restart polling, which most
+	 * devices do (not really caring about a lost event). However, we don't
+	 * support a driver changing that size on us, which it probably won't.
+	 * If we stumble across driver that does, then this will need to become
+	 * a lot more complicated.
+	 */
+	if (xpp->xpp_tsize > 0 && xpp->xpp_tsize < len) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_INVALID_REQUEST);
+	}
+
+	if (xpp->xpp_tsize == 0) {
+		int ntrbs;
+		int ntransfers;
+
+		/*
+		 * What we allocate varies based on whether or not this is an
+		 * isochronous or interrupt IN periodic.
+		 */
+		if (xep->xep_type == USB_EP_ATTR_INTR) {
+			ntrbs = 0;
+			ntransfers = XHCI_INTR_IN_NTRANSFERS;
+		} else {
+			usb_isoc_req_t *usrp;
+			ASSERT(xep->xep_type == USB_EP_ATTR_ISOCH);
+
+			usrp = (usb_isoc_req_t *)usb_req;
+			ntrbs = usrp->isoc_pkts_count;
+			ntransfers = XHCI_ISOC_IN_NTRANSFERS;
+		}
+
+		xpp->xpp_tsize = len;
+		xpp->xpp_ntransfers = ntransfers;
+
+		for (i = 0; i < xpp->xpp_ntransfers; i++) {
+			xhci_transfer_t *xt = xhci_transfer_alloc(xhcip, xep,
+			    len, ntrbs, usb_flags);
+			if (xt == NULL) {
+				xhci_hcdi_periodic_free(xhcip, xp);
+				mutex_exit(&xhcip->xhci_lock);
+				return (USB_NO_RESOURCES);
+			}
+
+			if (xep->xep_type == USB_EP_ATTR_INTR) {
+				xhci_transfer_trb_fill_data(xep, xt, 0, B_TRUE);
+			} else {
+				usb_isoc_req_t *usrp;
+				usrp = (usb_isoc_req_t *)usb_req;
+				xhci_hcdi_isoc_transfer_fill(xd, xep, xt, usrp);
+				xt->xt_data_tohost = B_TRUE;
+			}
+			xpp->xpp_transfers[i] = xt;
+		}
+	}
+
+	/*
+	 * Mark the endpoint as periodic so we don't have timeouts at play.
+	 */
+	xep->xep_state |= XHCI_ENDPOINT_PERIODIC;
+
+	/*
+	 * Now that we've allocated everything, go ahead and schedule them and
+	 * kick off the ring.
+	 */
+	for (i = 0; i < xpp->xpp_ntransfers; i++) {
+		int ret;
+		ret = xhci_endpoint_schedule(xhcip, xd, xep,
+		    xpp->xpp_transfers[i], B_FALSE);
+		if (ret != 0) {
+			(void) xhci_ring_reset(xhcip, &xep->xep_ring);
+			xep->xep_state &= ~XHCI_ENDPOINT_PERIODIC;
+			mutex_exit(&xhcip->xhci_lock);
+			return (ret);
+		}
+	}
+
+	/*
+	 * Don't worry about freeing memory, it'll be done when the endpoint
+	 * closes and the whole system is reset.
+	 */
+	xpp->xpp_usb_req = usb_req;
+	xpp->xpp_poll_state = XHCI_PERIODIC_POLL_ACTIVE;
+
+	ret = xhci_endpoint_ring(xhcip, xd, xep);
+	mutex_exit(&xhcip->xhci_lock);
+	return (ret);
+}
+
+static int
+xhci_hcdi_intr_oneshot(xhci_t *xhcip, usba_pipe_handle_data_t *ph,
+    usb_intr_req_t *uirp, usb_flags_t usb_flags)
+{
+	uint_t epid;
+	xhci_device_t *xd;
+	xhci_endpoint_t *xep;
+	xhci_transfer_t *xt;
+	boolean_t datain;
+	mblk_t *mp = NULL;
+
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhcip->xhci_state & XHCI_S_ERROR) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	xd = usba_hcdi_get_device_private(ph->p_usba_device);
+	epid = xhci_endpoint_pipe_to_epid(ph);
+	if (xd->xd_endpoints[epid] == NULL) {
+		xhci_error(xhcip, "asked to do interrupt transfer on slot %d, "
+		    "port %d, endpoint: %d, but no endpoint structure",
+		    xd->xd_slot, xd->xd_port, epid);
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_FAILURE);
+	}
+	xep = xd->xd_endpoints[epid];
+
+	mutex_exit(&xhcip->xhci_lock);
+
+	if ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
+		datain = B_TRUE;
+	} else {
+		datain = B_FALSE;
+	}
+
+	xt = xhci_transfer_alloc(xhcip, xep, uirp->intr_len, 0, usb_flags);
+	if (xt == NULL) {
+		return (USB_NO_RESOURCES);
+	}
+
+	xt->xt_usba_req = (usb_opaque_t)uirp;
+	xt->xt_timeout = uirp->intr_timeout;
+	if (xt->xt_timeout == 0) {
+		xt->xt_timeout = HCDI_DEFAULT_TIMEOUT;
+	}
+
+	/*
+	 * Unlike other request types, USB Interrupt-IN requests aren't required
+	 * to have allocated the message block for data. If they haven't, we
+	 * take care of that now.
+	 */
+	if (uirp->intr_len > 0 && datain == B_TRUE && uirp->intr_data == NULL) {
+		if (usb_flags & USB_FLAGS_SLEEP) {
+			mp = allocb_wait(uirp->intr_len, BPRI_LO, STR_NOSIG,
+			    NULL);
+		} else {
+			mp = allocb(uirp->intr_len, 0);
+		}
+		if (mp == NULL) {
+			xhci_transfer_free(xhcip, xt);
+			mutex_exit(&xhcip->xhci_lock);
+			return (USB_NO_RESOURCES);
+		}
+		uirp->intr_data = mp;
+	}
+
+	if (uirp->intr_len > 0 && datain == B_FALSE) {
+		xhci_transfer_copy(xt, uirp->intr_data->b_rptr, uirp->intr_len,
+		    B_FALSE);
+		if (xhci_transfer_sync(xhcip, xt, DDI_DMA_SYNC_FORDEV) !=
+		    DDI_FM_OK) {
+			xhci_transfer_free(xhcip, xt);
+			xhci_error(xhcip, "failed to synchronize interrupt "
+			    "transfer DMA memory on endpoint %u of "
+			    "device on slot %d and port %d: resetting "
+			    "device", xep->xep_num, xd->xd_slot,
+			    xd->xd_port);
+			xhci_fm_runtime_reset(xhcip);
+			return (USB_HC_HARDWARE_ERROR);
+		}
+	}
+
+	xhci_transfer_trb_fill_data(xep, xt, 0, datain);
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhci_endpoint_schedule(xhcip, xd, xep, xt, B_TRUE) != 0) {
+		if (mp != NULL) {
+			uirp->intr_data = NULL;
+			freemsg(mp);
+		}
+		xhci_transfer_free(xhcip, xt);
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_NO_RESOURCES);
+	}
+	mutex_exit(&xhcip->xhci_lock);
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * We've been asked to perform an interrupt transfer. When this is an interrupt
+ * IN endpoint, that means that the hcd is being asked to start polling on the
+ * endpoint. When the endpoint is the root hub, it effectively becomes synthetic
+ * polling.
+ *
+ * When we have an interrupt out endpoint, then this is just a single simple
+ * interrupt request that we send out and there isn't much special to do beyond
+ * the normal activity.
+ */
+static int
+xhci_hcdi_pipe_intr_xfer(usba_pipe_handle_data_t *ph, usb_intr_req_t *uirp,
+    usb_flags_t usb_flags)
+{
+	int ret;
+	xhci_t *xhcip = xhci_hcdi_get_xhcip(ph);
+
+	if ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
+		if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
+			ret = xhci_root_hub_intr_root_enable(xhcip, ph, uirp);
+		} else if (uirp->intr_attributes & USB_ATTRS_ONE_XFER) {
+			ret = xhci_hcdi_intr_oneshot(xhcip, ph, uirp,
+			    usb_flags);
+		} else {
+			ret = xhci_hcdi_periodic_init(xhcip, ph,
+			    (usb_opaque_t)uirp, uirp->intr_len, usb_flags);
+		}
+	} else {
+		if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
+			return (USB_NOT_SUPPORTED);
+		}
+		ret = xhci_hcdi_intr_oneshot(xhcip, ph, uirp, usb_flags);
+	}
+
+	return (ret);
+}
+
+/* ARGSUSED */
+static int
+xhci_hcdi_pipe_stop_intr_polling(usba_pipe_handle_data_t *ph,
+    usb_flags_t usb_flags)
+{
+	return (xhci_hcdi_pipe_poll_fini(ph, B_FALSE));
+}
+
+static int
+xhci_hcdi_isoc_periodic(xhci_t *xhcip, usba_pipe_handle_data_t *ph,
+    usb_isoc_req_t *usrp, usb_flags_t usb_flags)
+{
+	int i;
+	size_t count;
+
+	count = 0;
+	for (i = 0; i < usrp->isoc_pkts_count; i++) {
+		count += usrp->isoc_pkt_descr[i].isoc_pkt_length;
+	}
+
+	return (xhci_hcdi_periodic_init(xhcip, ph, (usb_opaque_t)usrp, count,
+	    usb_flags));
+}
+
+/*
+ * This is used to create an isochronous request to send data out to the device.
+ * This is a single one shot request, it is not something that we'll have to
+ * repeat over and over.
+ */
+static int
+xhci_hcdi_isoc_oneshot(xhci_t *xhcip, usba_pipe_handle_data_t *ph,
+    usb_isoc_req_t *usrp, usb_flags_t usb_flags)
+{
+	int i, ret;
+	uint_t epid;
+	size_t count, mblen;
+	xhci_device_t *xd;
+	xhci_endpoint_t *xep;
+	xhci_transfer_t *xt;
+
+	count = 0;
+	for (i = 0; i < usrp->isoc_pkts_count; i++) {
+		count += usrp->isoc_pkt_descr[i].isoc_pkt_length;
+	}
+	mblen = MBLKL(usrp->isoc_data);
+
+	if (count != mblen) {
+		return (USB_INVALID_ARGS);
+	}
+
+	mutex_enter(&xhcip->xhci_lock);
+	if (xhcip->xhci_state & XHCI_S_ERROR) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	xd = usba_hcdi_get_device_private(ph->p_usba_device);
+	epid = xhci_endpoint_pipe_to_epid(ph);
+	if (xd->xd_endpoints[epid] == NULL) {
+		xhci_error(xhcip, "asked to do isochronous transfer on slot "
+		    "%d, port %d, endpoint: %d, but no endpoint structure",
+		    xd->xd_slot, xd->xd_port, epid);
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_FAILURE);
+	}
+	xep = xd->xd_endpoints[epid];
+	mutex_exit(&xhcip->xhci_lock);
+
+	xt = xhci_transfer_alloc(xhcip, xep, mblen, usrp->isoc_pkts_count,
+	    usb_flags);
+	if (xt == NULL) {
+		return (USB_NO_RESOURCES);
+	}
+	xt->xt_usba_req = (usb_opaque_t)usrp;
+
+	/*
+	 * USBA doesn't provide any real way for a timeout to be defined for an
+	 * isochronous event. However, since we technically aren't a periodic
+	 * endpoint, go ahead and always set the default timeout. It's better
+	 * than nothing.
+	 */
+	xt->xt_timeout = HCDI_DEFAULT_TIMEOUT;
+
+	xhci_transfer_copy(xt, usrp->isoc_data->b_rptr, mblen, B_FALSE);
+	if (xhci_transfer_sync(xhcip, xt, DDI_DMA_SYNC_FORDEV) != DDI_FM_OK) {
+		xhci_transfer_free(xhcip, xt);
+		xhci_error(xhcip, "failed to synchronize isochronous "
+		    "transfer DMA memory on endpoint %u of "
+		    "device on slot %d and port %d: resetting "
+		    "device", xep->xep_num, xd->xd_slot,
+		    xd->xd_port);
+		xhci_fm_runtime_reset(xhcip);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	/*
+	 * Fill in the ISOC data. Note, that we always use ASAP scheduling and
+	 * we don't support specifying the frame at this time, for better or
+	 * worse.
+	 */
+	xhci_hcdi_isoc_transfer_fill(xd, xep, xt, usrp);
+
+	mutex_enter(&xhcip->xhci_lock);
+	ret = xhci_endpoint_schedule(xhcip, xd, xep, xt, B_TRUE);
+	mutex_exit(&xhcip->xhci_lock);
+
+	return (ret);
+}
+
+static int
+xhci_hcdi_pipe_isoc_xfer(usba_pipe_handle_data_t *ph, usb_isoc_req_t *usrp,
+    usb_flags_t usb_flags)
+{
+	int ret;
+	xhci_t *xhcip;
+
+	xhcip = xhci_hcdi_get_xhcip(ph);
+
+	/*
+	 * We don't support isochronous transactions on the root hub at all.
+	 * Always fail them if for some reason we end up here.
+	 */
+	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
+		return (USB_NOT_SUPPORTED);
+	}
+
+	/*
+	 * We do not support being asked to set the frame ID at this time. We
+	 * require that everything specify the attribute
+	 * USB_ATTRS_ISOC_XFER_ASAP.
+	 */
+	if (!(usrp->isoc_attributes & USB_ATTRS_ISOC_XFER_ASAP)) {
+		return (USB_NOT_SUPPORTED);
+	}
+
+	if ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
+		/*
+		 * Note, there is no such thing as a non-periodic isochronous
+		 * incoming transfer.
+		 */
+		ret = xhci_hcdi_isoc_periodic(xhcip, ph, usrp, usb_flags);
+	} else {
+		ret = xhci_hcdi_isoc_oneshot(xhcip, ph, usrp, usb_flags);
+	}
+
+	return (ret);
+}
+
+/* ARGSUSED */
+static int
+xhci_hcdi_pipe_stop_isoc_polling(usba_pipe_handle_data_t *ph,
+    usb_flags_t usb_flags)
+{
+	return (xhci_hcdi_pipe_poll_fini(ph, B_FALSE));
+}
+
+/*
+ * This is asking us for the current frame number. The USBA expects this to
+ * actually be a bit of a fiction, as it tries to maintain a frame number well
+ * beyond what the hardware actually contains in its registers. Hardware
+ * basically has a 14-bit counter, whereas we need to have a constant amount of
+ * milliseconds.
+ *
+ * Today, no client drivers actually use this API and everyone specifies the
+ * attribute to say that we should schedule things ASAP. So until we have some
+ * real device that want this functionality, we're going to fail.
+ */
+/* ARGSUSED */
+static int
+xhci_hcdi_get_current_frame_number(usba_device_t *usba_device,
+    usb_frame_number_t *frame_number)
+{
+	return (USB_FAILURE);
+}
+
+/*
+ * See the comments around the XHCI_ISOC_MAX_TRB macro for more information.
+ */
+/* ARGSUSED */
+static int
+xhci_hcdi_get_max_isoc_pkts(usba_device_t *usba_device,
+    uint_t *max_isoc_pkts_per_request)
+{
+	*max_isoc_pkts_per_request = XHCI_ISOC_MAX_TRB;
+	return (USB_SUCCESS);
+}
+
+/*
+ * The next series of routines is used for both the OBP console and general USB
+ * console polled I/O. In general, we opt not to support any of that at this
+ * time in xHCI. As we have the need of that, we can start plumbing that
+ * through.
+ */
+/* ARGSUSED */
+static int
+xhci_hcdi_console_input_init(usba_pipe_handle_data_t *pipe_handle,
+    uchar_t **obp_buf, usb_console_info_impl_t *console_input_info)
+{
+	return (USB_NOT_SUPPORTED);
+}
+
+/* ARGSUSED */
+static int
+xhci_hcdi_console_input_fini(usb_console_info_impl_t *console_input_info)
+{
+	return (USB_NOT_SUPPORTED);
+}
+
+/* ARGSUSED */
+static int
+xhci_hcdi_console_input_enter(usb_console_info_impl_t *console_input_info)
+{
+	return (USB_NOT_SUPPORTED);
+}
+
+/* ARGSUSED */
+static int
+xhci_hcdi_console_read(usb_console_info_impl_t *console_input_info,
+    uint_t *num_characters)
+{
+	return (USB_NOT_SUPPORTED);
+}
+
+/* ARGSUSED */
+static int
+xhci_hcdi_console_input_exit(usb_console_info_impl_t *console_input_info)
+{
+	return (USB_NOT_SUPPORTED);
+}
+
+/* ARGSUSED */
+static int
+xhci_hcdi_console_output_init(usba_pipe_handle_data_t *pipe_handle,
+    usb_console_info_impl_t *console_output_info)
+{
+	return (USB_NOT_SUPPORTED);
+}
+
+/* ARGSUSED */
+static int
+xhci_hcdi_console_output_fini(usb_console_info_impl_t *console_output_info)
+{
+	return (USB_NOT_SUPPORTED);
+}
+
+/* ARGSUSED */
+static int
+xhci_hcdi_console_output_enter(usb_console_info_impl_t *console_output_info)
+{
+	return (USB_NOT_SUPPORTED);
+}
+
+/* ARGSUSED */
+static int
+xhci_hcdi_console_write(usb_console_info_impl_t	*console_output_info,
+    uchar_t *buf, uint_t num_characters, uint_t *num_characters_written)
+{
+	return (USB_NOT_SUPPORTED);
+}
+
+/* ARGSUSED */
+static int
+xhci_hcdi_console_output_exit(usb_console_info_impl_t *console_output_info)
+{
+	return (USB_NOT_SUPPORTED);
+}
+
+/*
+ * VERSION 2 ops and helpers
+ */
+
+static void
+xhci_hcdi_device_free(xhci_device_t *xd)
+{
+	xhci_dma_free(&xd->xd_ictx);
+	xhci_dma_free(&xd->xd_octx);
+	mutex_destroy(&xd->xd_imtx);
+	kmem_free(xd, sizeof (xhci_device_t));
+}
+
+/*
+ * Calculate the device's route string. In USB 3.0 the route string is a 20-bit
+ * number. Each four bits represent a port number attached to a deeper hub.
+ * Particularly it represents the port on that current hub that you need to go
+ * down to reach the next device. Bits 0-3 represent the first *external* hub.
+ * So a device connected to a root hub has a route string of zero. Imagine the
+ * following set of devices:
+ *
+ *               . port 2      . port 5
+ *               .             .
+ *  +----------+ .  +--------+ .  +-------+
+ *  | root hub |-*->| hub 1  |-*->| hub 2 |
+ *  +----------+    +--------+    +-------+
+ *       * . port 12    * . port 8    * . port 1
+ *       v              v             v
+ *   +-------+      +-------+     +-------+
+ *   | dev a |      | dev b |     | dev c |
+ *   +-------+      +-------+     +-------+
+ *
+ * So, based on the above diagram, device a should have a route string of 0,
+ * because it's directly connected to the root port. Device b would simply have
+ * a route string of 8. This is because it travels through one non-root hub, hub
+ * 1, and it does so on port 8. The root ports basically don't matter. Device c
+ * would then have a route string of 0x15, as it's first traversing through hub
+ * 1 on port 2 and then hub 2 on port 5.
+ *
+ * Finally, it's worth mentioning that because it's a four bit field, if for
+ * some reason a device has more than 15 ports, we just treat the value as 15.
+ *
+ * Note, as part of this, we also grab what port on the root hub this whole
+ * chain is on, as we're required to store that information in the slot context.
+ */
+static void
+xhci_hcdi_device_route(usba_device_t *ud, uint32_t *routep, uint32_t *root_port)
+{
+	uint32_t route = 0;
+	usba_device_t *hub = ud->usb_parent_hub;
+	usba_device_t *port_dev = ud;
+
+	ASSERT(hub != NULL);
+
+	/*
+	 * Iterate over every hub, updating the route as we go. When we
+	 * encounter a hub without a parent, then we're at the root hub. At
+	 * which point, the port we want is on port_dev (the child of hub).
+	 */
+	while (hub->usb_parent_hub != NULL) {
+		uint32_t p;
+
+		p = port_dev->usb_port;
+		if (p > 15)
+			p = 15;
+		route <<= 4;
+		route |= p & 0xf;
+		port_dev = hub;
+		hub = hub->usb_parent_hub;
+	}
+
+	ASSERT(port_dev->usb_parent_hub == hub);
+	*root_port = port_dev->usb_port;
+	*routep = XHCI_ROUTE_MASK(route);
+}
+
+/*
+ * If a low or full speed device is behind a high-speed device that is not a
+ * root hub, then we must include the port and slot of that device. USBA already
+ * stores this device in the usb_hs_hub_usba_dev member.
+ */
+static uint32_t
+xhci_hcdi_device_tt(usba_device_t *ud)
+{
+	uint32_t ret;
+	xhci_device_t *xd;
+
+	if (ud->usb_port_status >= USBA_HIGH_SPEED_DEV)
+		return (0);
+
+	if (ud->usb_hs_hub_usba_dev == NULL)
+		return (0);
+
+	ASSERT(ud->usb_hs_hub_usba_dev != NULL);
+	ASSERT(ud->usb_hs_hub_usba_dev->usb_parent_hub != NULL);
+	xd = usba_hcdi_get_device_private(ud->usb_hs_hub_usba_dev);
+	ASSERT(xd != NULL);
+
+	ret = XHCI_SCTX_SET_TT_HUB_SID(xd->xd_slot);
+	ret |= XHCI_SCTX_SET_TT_PORT_NUM(ud->usb_hs_hub_usba_dev->usb_port);
+
+	return (ret);
+}
+
+/*
+ * Initialize a new device. This allocates a device slot from the controller,
+ * which tranfers it to our control.
+ */
+static int
+xhci_hcdi_device_init(usba_device_t *ud, usb_port_t port, void **hcdpp)
+{
+	int ret, i;
+	xhci_device_t *xd;
+	ddi_device_acc_attr_t acc;
+	ddi_dma_attr_t attr;
+	xhci_t *xhcip = xhci_hcdi_get_xhcip_from_dev(ud);
+	size_t isize, osize, incr;
+	uint32_t route, rp, info, info2, tt;
+
+	xd = kmem_zalloc(sizeof (xhci_device_t), KM_SLEEP);
+	xd->xd_port = port;
+	xd->xd_usbdev = ud;
+	mutex_init(&xd->xd_imtx, NULL, MUTEX_DRIVER,
+	    (void *)(uintptr_t)xhcip->xhci_intr_pri);
+
+	/*
+	 * The size of the context structures is based upon the presence of the
+	 * context flag which determines whether we have a 32-byte or 64-byte
+	 * context. Note that the input context always has to account for the
+	 * entire size of the xhci_input_contex_t, which is 32-bytes by default.
+	 */
+	if (xhcip->xhci_caps.xcap_flags & XCAP_CSZ) {
+		incr = 64;
+		osize = XHCI_DEVICE_CONTEXT_64;
+		isize = XHCI_DEVICE_CONTEXT_64 + incr;
+	} else {
+		incr = 32;
+		osize = XHCI_DEVICE_CONTEXT_32;
+		isize = XHCI_DEVICE_CONTEXT_32 + incr;
+	}
+
+	xhci_dma_acc_attr(xhcip, &acc);
+	xhci_dma_dma_attr(xhcip, &attr);
+	if (xhci_dma_alloc(xhcip, &xd->xd_ictx, &attr, &acc, B_TRUE,
+	    isize, B_FALSE) == B_FALSE) {
+		xhci_hcdi_device_free(xd);
+		return (USB_NO_RESOURCES);
+	}
+
+	xd->xd_input = (xhci_input_context_t *)xd->xd_ictx.xdb_va;
+	xd->xd_slotin = (xhci_slot_context_t *)(xd->xd_ictx.xdb_va + incr);
+	for (i = 0; i < XHCI_NUM_ENDPOINTS; i++) {
+		xd->xd_endin[i] =
+		    (xhci_endpoint_context_t *)(xd->xd_ictx.xdb_va +
+		    (i + 2) * incr);
+	}
+
+	if (xhci_dma_alloc(xhcip, &xd->xd_octx, &attr, &acc, B_TRUE,
+	    osize, B_FALSE) == B_FALSE) {
+		xhci_hcdi_device_free(xd);
+		return (USB_NO_RESOURCES);
+	}
+	xd->xd_slotout = (xhci_slot_context_t *)xd->xd_octx.xdb_va;
+	for (i = 0; i < XHCI_NUM_ENDPOINTS; i++) {
+		xd->xd_endout[i] =
+		    (xhci_endpoint_context_t *)(xd->xd_octx.xdb_va +
+		    (i + 1) * incr);
+	}
+
+	ret = xhci_command_enable_slot(xhcip, &xd->xd_slot);
+	if (ret != USB_SUCCESS) {
+		xhci_hcdi_device_free(xd);
+		return (ret);
+	}
+
+	/*
+	 * These are the default slot context and the endpoint zero context that
+	 * we're enabling. See 4.3.3.
+	 */
+	xd->xd_input->xic_add_flags = LE_32(XHCI_INCTX_MASK_DCI(0) |
+	    XHCI_INCTX_MASK_DCI(1));
+
+	/*
+	 * Note, we never need to set the MTT bit as illumos never enables the
+	 * alternate MTT interface.
+	 */
+	xhci_hcdi_device_route(ud, &route, &rp);
+	info = XHCI_SCTX_SET_ROUTE(route) | XHCI_SCTX_SET_DCI(1);
+	switch (ud->usb_port_status) {
+	case USBA_LOW_SPEED_DEV:
+		info |= XHCI_SCTX_SET_SPEED(XHCI_SPEED_LOW);
+		break;
+	case USBA_HIGH_SPEED_DEV:
+		info |= XHCI_SCTX_SET_SPEED(XHCI_SPEED_HIGH);
+		break;
+	case USBA_FULL_SPEED_DEV:
+		info |= XHCI_SCTX_SET_SPEED(XHCI_SPEED_FULL);
+		break;
+	case USBA_SUPER_SPEED_DEV:
+	default:
+		info |= XHCI_SCTX_SET_SPEED(XHCI_SPEED_SUPER);
+		break;
+	}
+	info2 = XHCI_SCTX_SET_RHPORT(rp);
+	tt = XHCI_SCTX_SET_IRQ_TARGET(0);
+	tt |= xhci_hcdi_device_tt(ud);
+
+	xd->xd_slotin->xsc_info = LE_32(info);
+	xd->xd_slotin->xsc_info2 = LE_32(info2);
+	xd->xd_slotin->xsc_tt = LE_32(tt);
+
+	if ((ret = xhci_endpoint_init(xhcip, xd, NULL)) != 0) {
+		(void) xhci_command_disable_slot(xhcip, xd->xd_slot);
+		xhci_hcdi_device_free(xd);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	if (xhci_context_slot_output_init(xhcip, xd) != B_TRUE) {
+		(void) xhci_command_disable_slot(xhcip, xd->xd_slot);
+		xhci_endpoint_fini(xd, 0);
+		xhci_hcdi_device_free(xd);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	if ((ret = xhci_command_set_address(xhcip, xd, B_TRUE)) != 0) {
+		(void) xhci_command_disable_slot(xhcip, xd->xd_slot);
+		xhci_context_slot_output_fini(xhcip, xd);
+		xhci_endpoint_fini(xd, 0);
+		xhci_hcdi_device_free(xd);
+		return (ret);
+	}
+
+	mutex_enter(&xhcip->xhci_lock);
+	list_insert_tail(&xhcip->xhci_usba.xa_devices, xd);
+	mutex_exit(&xhcip->xhci_lock);
+
+	*hcdpp = xd;
+	return (ret);
+}
+
+/*
+ * We're tearing down a device now. That means that the only endpoint context
+ * that's still valid would be endpoint zero.
+ */
+static void
+xhci_hcdi_device_fini(usba_device_t *ud, void *hcdp)
+{
+	int ret;
+	xhci_endpoint_t *xep;
+	xhci_device_t *xd;
+	xhci_t *xhcip;
+
+	/*
+	 * Right now, it's theoretically possible that USBA may try and call
+	 * us here even if we hadn't successfully finished the device_init()
+	 * endpoint. We should probably modify the USBA to make sure that this
+	 * can't happen.
+	 */
+	if (hcdp == NULL)
+		return;
+
+	xd = hcdp;
+	xhcip = xhci_hcdi_get_xhcip_from_dev(ud);
+
+	/*
+	 * Make sure we have no timeout running on the default endpoint still.
+	 */
+	xep = xd->xd_endpoints[XHCI_DEFAULT_ENDPOINT];
+	mutex_enter(&xhcip->xhci_lock);
+	xep->xep_state |= XHCI_ENDPOINT_TEARDOWN;
+	mutex_exit(&xhcip->xhci_lock);
+	(void) untimeout(xep->xep_timeout);
+
+	/*
+	 * Go ahead and disable the slot. There's no reason to do anything
+	 * special about the default endpoint as it will be disabled as a part
+	 * of the slot disabling. However, if this all fails, we'll leave this
+	 * sitting here in a failed state, eating up a device slot. It is
+	 * unlikely this will occur.
+	 */
+	ret = xhci_command_disable_slot(xhcip, xd->xd_slot);
+	if (ret != USB_SUCCESS) {
+		xhci_error(xhcip, "failed to disable slot %d: %d",
+		    xd->xd_slot, ret);
+		return;
+	}
+
+	xhci_context_slot_output_fini(xhcip, xd);
+	xhci_endpoint_fini(xd, XHCI_DEFAULT_ENDPOINT);
+
+	mutex_enter(&xhcip->xhci_lock);
+	list_remove(&xhcip->xhci_usba.xa_devices, xd);
+	mutex_exit(&xhcip->xhci_lock);
+
+	xhci_hcdi_device_free(xd);
+}
+
+/*
+ * Synchronously attempt to set the device address. For xHCI this involves it
+ * deciding what address to use.
+ */
+static int
+xhci_hcdi_device_address(usba_device_t *ud)
+{
+	int ret;
+	xhci_t *xhcip = xhci_hcdi_get_xhcip_from_dev(ud);
+	xhci_device_t *xd = usba_hcdi_get_device_private(ud);
+	xhci_endpoint_t *xep;
+
+	mutex_enter(&xhcip->xhci_lock);
+
+	/*
+	 * This device may already be addressed from the perspective of the xhci
+	 * controller. For example, the device this represents may have been
+	 * unconfigured, which does not actually remove the slot or other
+	 * information, merely tears down all the active use of it and the child
+	 * driver. In such cases, if we're already addressed, just return
+	 * success. The actual USB address is a fiction for USBA anyways.
+	 */
+	if (xd->xd_addressed == B_TRUE) {
+		mutex_exit(&xhcip->xhci_lock);
+		return (USB_SUCCESS);
+	}
+
+	ASSERT(xd->xd_addressed == B_FALSE);
+	xd->xd_addressed = B_TRUE;
+	VERIFY3P(xd->xd_endpoints[XHCI_DEFAULT_ENDPOINT], !=, NULL);
+	xep = xd->xd_endpoints[XHCI_DEFAULT_ENDPOINT];
+	mutex_exit(&xhcip->xhci_lock);
+
+	if ((ret = xhci_endpoint_setup_default_context(xhcip, xd, xep)) != 0) {
+		ASSERT(ret == EIO);
+		return (USB_HC_HARDWARE_ERROR);
+	}
+
+	ret = xhci_command_set_address(xhcip, xd, B_FALSE);
+
+	if (ret != USB_SUCCESS) {
+		mutex_enter(&xhcip->xhci_lock);
+		xd->xd_addressed = B_FALSE;
+		mutex_exit(&xhcip->xhci_lock);
+	}
+
+	return (ret);
+}
+
+/*
+ * This is called relatively early on in a hub's life time. At this point, it's
+ * descriptors have all been pulled and the default control pipe is still open.
+ * What we need to do is go through and update the slot context to indicate that
+ * this is a hub, otherwise, the controller will never let us speak to
+ * downstream ports.
+ */
+static int
+xhci_hcdi_hub_update(usba_device_t *ud, uint8_t nports, uint8_t tt)
+{
+	int ret;
+	xhci_t *xhcip = xhci_hcdi_get_xhcip_from_dev(ud);
+	xhci_device_t *xd = usba_hcdi_get_device_private(ud);
+
+	if (xd == NULL)
+		return (USB_FAILURE);
+
+	if (ud->usb_hubdi == NULL) {
+		return (USB_FAILURE);
+	}
+
+	mutex_enter(&xd->xd_imtx);
+
+	/*
+	 * Note, that usba never sets the interface of a hub to Multi TT. Hence
+	 * why we're never setting the MTT bit in xsc_info.
+	 */
+	xd->xd_slotin->xsc_info |= LE_32(XHCI_SCTX_SET_HUB(1));
+	xd->xd_slotin->xsc_info2 |= LE_32(XHCI_SCTX_SET_NPORTS(nports));
+	if (ud->usb_port_status == USBA_HIGH_SPEED_DEV)
+		xd->xd_slotin->xsc_tt |= LE_32(XHCI_SCTX_SET_TT_THINK_TIME(tt));
+
+	/*
+	 * We're only updating the slot context, no endpoint contexts should be
+	 * touched.
+	 */
+	xd->xd_input->xic_drop_flags = LE_32(0);
+	xd->xd_input->xic_add_flags = LE_32(XHCI_INCTX_MASK_DCI(0));
+
+	ret = xhci_command_evaluate_context(xhcip, xd);
+	mutex_exit(&xd->xd_imtx);
+	return (ret);
+}
+
+void
+xhci_hcd_fini(xhci_t *xhcip)
+{
+	usba_hcdi_unregister(xhcip->xhci_dip);
+	usba_free_hcdi_ops(xhcip->xhci_usba.xa_ops);
+	list_destroy(&xhcip->xhci_usba.xa_pipes);
+	list_destroy(&xhcip->xhci_usba.xa_devices);
+}
+
+int
+xhci_hcd_init(xhci_t *xhcip)
+{
+	usba_hcdi_register_args_t hreg;
+	usba_hcdi_ops_t *ops;
+
+	ops = usba_alloc_hcdi_ops();
+	VERIFY(ops != NULL);
+
+	ops->usba_hcdi_ops_version = HCDI_OPS_VERSION;
+	ops->usba_hcdi_dip = xhcip->xhci_dip;
+
+	ops->usba_hcdi_pm_support = xhci_hcdi_pm_support;
+	ops->usba_hcdi_pipe_open = xhci_hcdi_pipe_open;
+	ops->usba_hcdi_pipe_close = xhci_hcdi_pipe_close;
+	ops->usba_hcdi_pipe_reset = xhci_hcdi_pipe_reset;
+	ops->usba_hcdi_pipe_reset_data_toggle =
+	    xhci_hcdi_pipe_reset_data_toggle;
+	ops->usba_hcdi_pipe_ctrl_xfer = xhci_hcdi_pipe_ctrl_xfer;
+	ops->usba_hcdi_bulk_transfer_size = xhci_hcdi_bulk_transfer_size;
+	ops->usba_hcdi_pipe_bulk_xfer = xhci_hcdi_pipe_bulk_xfer;
+	ops->usba_hcdi_pipe_intr_xfer = xhci_hcdi_pipe_intr_xfer;
+	ops->usba_hcdi_pipe_stop_intr_polling =
+	    xhci_hcdi_pipe_stop_intr_polling;
+	ops->usba_hcdi_pipe_isoc_xfer = xhci_hcdi_pipe_isoc_xfer;
+	ops->usba_hcdi_pipe_stop_isoc_polling =
+	    xhci_hcdi_pipe_stop_isoc_polling;
+	ops->usba_hcdi_get_current_frame_number =
+	    xhci_hcdi_get_current_frame_number;
+	ops->usba_hcdi_get_max_isoc_pkts = xhci_hcdi_get_max_isoc_pkts;
+	ops->usba_hcdi_console_input_init = xhci_hcdi_console_input_init;
+	ops->usba_hcdi_console_input_fini = xhci_hcdi_console_input_fini;
+	ops->usba_hcdi_console_input_enter = xhci_hcdi_console_input_enter;
+	ops->usba_hcdi_console_read = xhci_hcdi_console_read;
+	ops->usba_hcdi_console_input_exit = xhci_hcdi_console_input_exit;
+
+	ops->usba_hcdi_console_output_init = xhci_hcdi_console_output_init;
+	ops->usba_hcdi_console_output_fini = xhci_hcdi_console_output_fini;
+	ops->usba_hcdi_console_output_enter = xhci_hcdi_console_output_enter;
+	ops->usba_hcdi_console_write = xhci_hcdi_console_write;
+	ops->usba_hcdi_console_output_exit = xhci_hcdi_console_output_exit;
+
+	ops->usba_hcdi_device_init = xhci_hcdi_device_init;
+	ops->usba_hcdi_device_fini = xhci_hcdi_device_fini;
+	ops->usba_hcdi_device_address = xhci_hcdi_device_address;
+	ops->usba_hcdi_hub_update = xhci_hcdi_hub_update;
+
+	hreg.usba_hcdi_register_version = HCDI_REGISTER_VERSION;
+	hreg.usba_hcdi_register_dip = xhcip->xhci_dip;
+	hreg.usba_hcdi_register_ops = ops;
+
+	/*
+	 * We're required to give xhci a set of DMA attributes that it may loan
+	 * out to other devices. Therefore we'll be conservative with what we
+	 * end up giving it.
+	 */
+	xhci_dma_dma_attr(xhcip, &xhcip->xhci_usba.xa_dma_attr);
+	hreg.usba_hcdi_register_dma_attr = &xhcip->xhci_usba.xa_dma_attr;
+
+	hreg.usba_hcdi_register_iblock_cookie =
+	    (ddi_iblock_cookie_t)(uintptr_t)xhcip->xhci_intr_pri;
+
+	if (usba_hcdi_register(&hreg, 0) != DDI_SUCCESS) {
+		usba_free_hcdi_ops(ops);
+		return (DDI_FAILURE);
+	}
+
+	xhcip->xhci_usba.xa_ops = ops;
+
+	list_create(&xhcip->xhci_usba.xa_devices, sizeof (xhci_device_t),
+	    offsetof(xhci_device_t, xd_link));
+	list_create(&xhcip->xhci_usba.xa_pipes, sizeof (xhci_pipe_t),
+	    offsetof(xhci_pipe_t, xp_link));
+
+
+	return (DDI_SUCCESS);
+}
--- a/usr/src/uts/common/io/usb/scsa2usb/scsa2usb.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/common/io/usb/scsa2usb/scsa2usb.c	Fri Mar 10 18:57:44 2017 -0500
@@ -21,6 +21,8 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2016 Joyent, Inc.
  * Copyright (c) 2016 by Delphix. All rights reserved.
  */
 
@@ -736,15 +738,36 @@
 	/* figure out the endpoints and copy the descr */
 	if ((ep_data = usb_lookup_ep_data(dip, dev_data, interface, 0, 0,
 	    USB_EP_ATTR_BULK, USB_EP_DIR_OUT)) != NULL) {
-		scsa2usbp->scsa2usb_bulkout_ept = ep_data->ep_descr;
+		if (usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
+		    dip, ep_data, &scsa2usbp->scsa2usb_bulkout_xept) !=
+		    USB_SUCCESS) {
+
+			mutex_exit(&scsa2usbp->scsa2usb_mutex);
+
+			goto fail;
+		}
 	}
 	if ((ep_data = usb_lookup_ep_data(dip, dev_data, interface, 0, 0,
 	    USB_EP_ATTR_BULK, USB_EP_DIR_IN)) != NULL) {
-		scsa2usbp->scsa2usb_bulkin_ept = ep_data->ep_descr;
+		if (usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
+		    dip, ep_data, &scsa2usbp->scsa2usb_bulkin_xept) !=
+		    USB_SUCCESS) {
+
+			mutex_exit(&scsa2usbp->scsa2usb_mutex);
+
+			goto fail;
+		}
 	}
 	if ((ep_data = usb_lookup_ep_data(dip, dev_data, interface, 0, 0,
 	    USB_EP_ATTR_INTR, USB_EP_DIR_IN)) != NULL) {
-		scsa2usbp->scsa2usb_intr_ept = ep_data->ep_descr;
+		if (usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
+		    dip, ep_data, &scsa2usbp->scsa2usb_intr_xept) !=
+		    USB_SUCCESS) {
+
+			mutex_exit(&scsa2usbp->scsa2usb_mutex);
+
+			goto fail;
+		}
 	}
 
 	/*
@@ -2347,7 +2370,7 @@
 /* ARGSUSED */
 static void
 scsa2usb_scsi_tgt_free(dev_info_t *hba_dip, dev_info_t *cdip,
-	scsi_hba_tran_t *tran, struct scsi_device *sd)
+    scsi_hba_tran_t *tran, struct scsi_device *sd)
 {
 	scsa2usb_state_t *scsa2usbp = (scsa2usb_state_t *)
 	    tran->tran_hba_private;
@@ -4515,8 +4538,8 @@
 		mutex_exit(&scsa2usbp->scsa2usb_mutex);
 
 		/* Open the USB bulk-in pipe */
-		if ((rval = usb_pipe_open(scsa2usbp->scsa2usb_dip,
-		    &scsa2usbp->scsa2usb_bulkin_ept, &policy, USB_FLAGS_SLEEP,
+		if ((rval = usb_pipe_xopen(scsa2usbp->scsa2usb_dip,
+		    &scsa2usbp->scsa2usb_bulkin_xept, &policy, USB_FLAGS_SLEEP,
 		    &scsa2usbp->scsa2usb_bulkin_pipe)) != USB_SUCCESS) {
 			mutex_enter(&scsa2usbp->scsa2usb_mutex);
 			USB_DPRINTF_L2(DPRINT_MASK_SCSA,
@@ -4528,8 +4551,8 @@
 		}
 
 		/* Open the bulk-out pipe  using the same policy */
-		if ((rval = usb_pipe_open(scsa2usbp->scsa2usb_dip,
-		    &scsa2usbp->scsa2usb_bulkout_ept, &policy, USB_FLAGS_SLEEP,
+		if ((rval = usb_pipe_xopen(scsa2usbp->scsa2usb_dip,
+		    &scsa2usbp->scsa2usb_bulkout_xept, &policy, USB_FLAGS_SLEEP,
 		    &scsa2usbp->scsa2usb_bulkout_pipe)) != USB_SUCCESS) {
 			usb_pipe_close(scsa2usbp->scsa2usb_dip,
 			    scsa2usbp->scsa2usb_bulkin_pipe,
@@ -4552,8 +4575,8 @@
 		if (SCSA2USB_IS_CBI(scsa2usbp)) {
 			mutex_exit(&scsa2usbp->scsa2usb_mutex);
 
-			if ((rval = usb_pipe_open(scsa2usbp->scsa2usb_dip,
-			    &scsa2usbp->scsa2usb_intr_ept, &policy,
+			if ((rval = usb_pipe_xopen(scsa2usbp->scsa2usb_dip,
+			    &scsa2usbp->scsa2usb_intr_xept, &policy,
 			    USB_FLAGS_SLEEP, &scsa2usbp->scsa2usb_intr_pipe)) !=
 			    USB_SUCCESS) {
 				usb_pipe_close(scsa2usbp->scsa2usb_dip,
--- a/usr/src/uts/common/io/usb/usba/hcdi.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/common/io/usb/usba/hcdi.c	Fri Mar 10 18:57:44 2017 -0500
@@ -21,6 +21,8 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -117,6 +119,19 @@
 	hcdi->hcdi_dip = args->usba_hcdi_register_dip;
 
 	/*
+	 * The hcd driver cannot use private data as we're going to store our
+	 * data there. If it does, fail the registration immediately.
+	 */
+	if (ddi_get_driver_private(hcdi->hcdi_dip) != NULL) {
+		cmn_err(CE_WARN, "failed attempt to register USB hcd, "
+		    "detected private data!");
+		kmem_free(hcdi, sizeof (usba_hcdi_t));
+
+		return (USB_FAILURE);
+	}
+
+
+	/*
 	 * Create a log_handle
 	 */
 	hcdi->hcdi_log_handle = usb_alloc_log_hdl(hcdi->hcdi_dip, NULL,
@@ -467,9 +482,8 @@
  * HCD callback handling
  */
 void
-usba_hcdi_cb(usba_pipe_handle_data_t *ph_data,
-    usb_opaque_t	req,
-    usb_cr_t	completion_reason)
+usba_hcdi_cb(usba_pipe_handle_data_t *ph_data, usb_opaque_t req,
+    usb_cr_t completion_reason)
 {
 
 	usba_device_t		*usba_device = ph_data->p_usba_device;
@@ -953,8 +967,7 @@
  *	hcd_private	- wr_hcd_private field from wrapper
  */
 void
-usba_hcdi_set_req_private(usb_opaque_t req,
-    usb_opaque_t	hcd_private)
+usba_hcdi_set_req_private(usb_opaque_t req, usb_opaque_t hcd_private)
 {
 	usba_req_wrapper_t *wrp = USBA_REQ2WRP(req);
 
@@ -1008,3 +1021,9 @@
 	return (usba_device->usb_ph_list[usb_get_ep_index(ep_addr)].
 	    usba_ph_data);
 }
+
+void *
+usba_hcdi_get_device_private(usba_device_t *usba_device)
+{
+	return (usba_device->usb_hcd_private);
+}
--- a/usr/src/uts/common/io/usb/usba/hubdi.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/common/io/usb/usba/hubdi.c	Fri Mar 10 18:57:44 2017 -0500
@@ -22,6 +22,7 @@
  * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -193,8 +194,7 @@
  *	to calling this	interface
  */
 int
-usba_hubdi_register(dev_info_t	*dip,
-		uint_t		flags)
+usba_hubdi_register(dev_info_t	*dip, uint_t flags)
 {
 	usba_hubdi_t *hubdi = kmem_zalloc(sizeof (usba_hubdi_t), KM_SLEEP);
 	usba_device_t *usba_device = usba_get_usba_device(dip);
@@ -333,9 +333,11 @@
 
 	/*
 	 * The bDeviceProtocol field of root hub device specifies,
-	 * whether root hub is a High or Full speed usb device.
+	 * whether root hub is a Super, High, or Full speed usb device.
 	 */
-	if (root_hub_device_descriptor->bDeviceProtocol) {
+	if (root_hub_device_descriptor->bDeviceProtocol >= 0x3) {
+		usba_device->usb_port_status = USBA_SUPER_SPEED_DEV;
+	} else if (root_hub_device_descriptor->bDeviceProtocol > 0) {
 		usba_device->usb_port_status = USBA_HIGH_SPEED_DEV;
 	} else {
 		usba_device->usb_port_status = USBA_FULL_SPEED_DEV;
@@ -373,10 +375,6 @@
 	return (USB_SUCCESS);
 
 fail:
-	(void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
-
-	usba_rem_root_hub(dip);
-
 	if (ph) {
 		usb_pipe_close(dip, ph,
 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
@@ -392,10 +390,15 @@
 	usba_free_usba_device(usba_device);
 
 	usba_set_usba_device(dip, NULL);
+
 	if (root_hubd) {
 		kmem_free(root_hubd, sizeof (hubd_t));
 	}
 
+	(void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
+
+	usba_rem_root_hub(dip);
+
 	return (USB_FAILURE);
 }
 
@@ -523,6 +526,8 @@
 
 static int hubd_get_hub_descriptor(hubd_t *hubd);
 
+static int hubd_set_hub_depth(hubd_t *hubd);
+
 static int hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status);
 
 static int hubd_reset_port(hubd_t *hubd, usb_port_t port);
@@ -537,7 +542,8 @@
 static int hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port);
 
 static int hubd_determine_port_status(hubd_t *hubd, usb_port_t port,
-	uint16_t *status, uint16_t *change, uint_t ack_flag);
+	uint16_t *status, uint16_t *change, usb_port_status_t *speed,
+	uint_t ack_flag);
 
 static int hubd_enable_all_port_power(hubd_t *hubd);
 static int hubd_disable_all_port_power(hubd_t *hubd);
@@ -602,7 +608,6 @@
 hubd_get_soft_state(dev_info_t *dip)
 {
 	if (dip == NULL) {
-
 		return (NULL);
 	}
 
@@ -677,7 +682,7 @@
 	hubpm = hubd->h_hubpm;
 
 	old_power = 0;
-	for (portno = 1; portno <= hubd->h_hub_descr.bNbrPorts; portno++) {
+	for (portno = 1; portno <= hubd->h_nports; portno++) {
 		old_power += hubpm->hubp_child_pwrstate[portno];
 	}
 
@@ -710,13 +715,13 @@
 	usb_port_t	port;
 
 	mutex_enter(HUBD_MUTEX(hubd));
-	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
+	for (port = 1; port <= hubd->h_nports; port++) {
 		if (hubd->h_children_dips[port] == dip) {
 
 			break;
 		}
 	}
-	ASSERT(port <= hubd->h_hub_descr.bNbrPorts);
+	ASSERT(port <= hubd->h_nports);
 	mutex_exit(HUBD_MUTEX(hubd));
 
 	return (port);
@@ -752,7 +757,7 @@
 	}
 
 	for (port = 1; (total_power == 0) &&
-	    (port <= hubd->h_hub_descr.bNbrPorts); port++) {
+	    (port <= hubd->h_nports); port++) {
 		total_power += hubpm->hubp_child_pwrstate[port];
 	}
 
@@ -819,7 +824,7 @@
 
 		/* either way ack changes on the port */
 		(void) hubd_determine_port_status(hubd, port,
-		    &status, &change, PORT_CHANGE_PSSC);
+		    &status, &change, NULL, PORT_CHANGE_PSSC);
 		retval = USB_SUCCESS;
 
 		break;
@@ -879,7 +884,7 @@
 				delay(drv_usectohz(40000));
 				mutex_enter(HUBD_MUTEX(hubd));
 				(void) hubd_determine_port_status(hubd, port,
-				    &status, &change, PORT_CHANGE_PSSC);
+				    &status, &change, NULL, PORT_CHANGE_PSSC);
 
 				if ((status & PORT_STATUS_PSS) == 0) {
 					/* the port did finally resume */
@@ -983,7 +988,7 @@
 			/* either ways ack changes on the port */
 			mutex_enter(HUBD_MUTEX(hubd));
 			(void) hubd_determine_port_status(hubd, port,
-			    &status, &change, PORT_CHANGE_PSSC);
+			    &status, &change, NULL, PORT_CHANGE_PSSC);
 			if (status & PORT_STATUS_PSS) {
 				/* the port is indeed suspended */
 				retval = USB_SUCCESS;
@@ -1295,7 +1300,6 @@
 static boolean_t
 hubd_config_one(hubd_t *hubd, int port)
 {
-	uint16_t	status, change;
 	dev_info_t	*hdip = hubd->h_dip;
 	dev_info_t	*rh_dip = hubd->h_usba_device->usb_root_hub_dip;
 	boolean_t	online_child = B_FALSE, found = B_FALSE;
@@ -1321,9 +1325,10 @@
 	hubd_pm_busy_component(hubd, hubd->h_dip, 0);
 
 	if (!hubd->h_children_dips[port]) {
+		uint16_t	status, change;
 
 		(void) hubd_determine_port_status(hubd, port,
-		    &status, &change, HUBD_ACK_ALL_CHANGES);
+		    &status, &change, NULL, HUBD_ACK_ALL_CHANGES);
 
 		if (status & PORT_STATUS_CCS) {
 			online_child |=	(hubd_handle_port_connect(hubd,
@@ -1439,7 +1444,7 @@
 
 	/* logically zap children's list */
 	mutex_enter(HUBD_MUTEX(hubd));
-	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
+	for (port = 1; port <= hubd->h_nports; port++) {
 		hubd->h_port_state[port] |= HUBD_CHILD_ZAP;
 	}
 	mutex_exit(HUBD_MUTEX(hubd));
@@ -1462,7 +1467,7 @@
 
 	/* physically zap the children we didn't find */
 	mutex_enter(HUBD_MUTEX(hubd));
-	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
+	for (port = 1; port <= hubd->h_nports; port++) {
 		if (hubd->h_port_state[port] &	HUBD_CHILD_ZAP) {
 			/* zap the dip and usba_device structure as well */
 			hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
@@ -1852,7 +1857,9 @@
 
 		/* generate readable labels for different root hubs */
 		root_hub_drvname = ddi_driver_name(dip);
-		if (strcmp(root_hub_drvname, "ehci") == 0) {
+		if (strcmp(root_hub_drvname, "xhci") == 0) {
+			log_name = "xusb";
+		} else if (strcmp(root_hub_drvname, "ehci") == 0) {
 			log_name = "eusb";
 		} else if (strcmp(root_hub_drvname, "uhci") == 0) {
 			log_name = "uusb";
@@ -1891,10 +1898,10 @@
 	mutex_exit(&child_ud->usb_mutex);
 
 	if ((child_port_status == USBA_FULL_SPEED_DEV) &&
-	    (parent_port_status == USBA_HIGH_SPEED_DEV) &&
+	    (parent_port_status >= USBA_HIGH_SPEED_DEV) &&
 	    (usb_dev_descr->bcdUSB == 0x100)) {
 		USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
-		    "Use of a USB1.0 hub behind a high speed port may "
+		    "Use of a USB1.0 hub behind a higher speed port may "
 		    "cause unexpected failures");
 	}
 
@@ -1925,7 +1932,11 @@
 		goto fail;
 	}
 
-	hubd->h_ep1_descr = ep_data->ep_descr;
+	if (usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION, dip, ep_data,
+	    &hubd->h_ep1_xdescr) != USB_SUCCESS) {
+		goto fail;
+	}
+
 	hubd->h_default_pipe = hubd->h_dev_data->dev_default_ph;
 
 	mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER,
@@ -1983,12 +1994,42 @@
 	mutex_enter(HUBD_MUTEX(hubd));
 	hubd->h_init_state |= HUBD_EVENTS_REGISTERED;
 
-	if ((hubd_get_hub_descriptor(hubd)) != USB_SUCCESS) {
+	if (hubd_get_hub_descriptor(hubd) != USB_SUCCESS) {
 		mutex_exit(HUBD_MUTEX(hubd));
 
 		goto fail;
 	}
 
+	/*
+	 * Now that we have our descriptors, we need to give the host controller
+	 * a chance to properly configure the device if needed (this is required
+	 * for xHCI).
+	 *
+	 * Note, if anyone ever adds support for using the Multi TT mode for USB
+	 * 2 High speed Hubs, the xhci driver will need to be updated and that
+	 * will need to be done before this is called.
+	 */
+	if (hubd->h_usba_device->usb_hcdi_ops->usba_hcdi_hub_update != NULL &&
+	    !usba_is_root_hub(dip)) {
+		int ret;
+		uint8_t chars;
+		usba_device_t *ud = hubd->h_usba_device;
+
+		chars = (hubd->h_hub_chars & HUB_CHARS_TT_THINK_TIME) >>
+		    HUB_CHARS_TT_SHIFT;
+		ret = ud->usb_hcdi_ops->usba_hcdi_hub_update(ud,
+		    hubd->h_nports, chars);
+		if (ret != USB_SUCCESS) {
+			mutex_exit(HUBD_MUTEX(hubd));
+			goto fail;
+		}
+	}
+
+	if (hubd_set_hub_depth(hubd) != USB_SUCCESS) {
+
+		goto fail;
+	}
+
 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip,
 	    (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
 	    "hub-ignore-power-budget") == 1) {
@@ -2022,9 +2063,9 @@
 	hubd_get_ancestry_str(hubd);
 
 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
-	    "#ports=0x%x", hubd->h_hub_descr.bNbrPorts);
-
-	for (i = 1; i <= hubd->h_hub_descr.bNbrPorts; i++) {
+	    "#ports=0x%x", hubd->h_nports);
+
+	for (i = 1; i <= hubd->h_nports; i++) {
 		char ap_name[HUBD_APID_NAMELEN];
 
 		(void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
@@ -2043,7 +2084,7 @@
 		}
 	}
 
-	ports_count = hubd->h_hub_descr.bNbrPorts;
+	ports_count = hubd->h_nports;
 	mutex_exit(HUBD_MUTEX(hubd));
 
 	/* create minor nodes */
@@ -2166,6 +2207,20 @@
 	mutex_enter(HUBD_MUTEX(hubd));
 
 	/*
+	 * If the host controller is in charge of addressing, have it do that
+	 * now and skip everything else.
+	 */
+	if (usba_device->usb_hcdi_ops->usba_hcdi_device_address != NULL) {
+		mutex_exit(HUBD_MUTEX(hubd));
+		rval = usba_device->usb_hcdi_ops->usba_hcdi_device_address(
+		    usba_device);
+		mutex_enter(HUBD_MUTEX(hubd));
+
+		usba_clear_data_toggle(usba_device);
+		return (rval);
+	}
+
+	/*
 	 * As this device has been reset, temporarily
 	 * assign the default address
 	 */
@@ -2331,7 +2386,7 @@
 	if (ddi_driver_major(dip) != hwahc_major) {
 		/* for normal usb hub or root hub */
 		mutex_enter(HUBD_MUTEX(hubd));
-		for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
+		for (port = 1; port <= hubd->h_nports; port++) {
 			dev_info_t *cdip = hubd->h_children_dips[port];
 
 			if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) {
@@ -2575,7 +2630,7 @@
 
 	hubd->h_dev_state = USB_DEV_HUB_STATE_RECOVER;
 
-	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
+	for (port = 1; port <= hubd->h_nports; port++) {
 		USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
 		    "hubd_restore_device_state: port=%d", port);
 
@@ -2588,7 +2643,7 @@
 		if (ch_dip) {
 			/* get port status */
 			(void) hubd_determine_port_status(hubd, port,
-			    &status, &change, PORT_CHANGE_CSC);
+			    &status, &change, NULL, PORT_CHANGE_CSC);
 
 			/* check if it is truly connected */
 			if (status & PORT_STATUS_CCS) {
@@ -2665,7 +2720,7 @@
 		} else if (ehci_root_hub) {
 			/* get port status */
 			(void) hubd_determine_port_status(hubd, port,
-			    &status, &change, PORT_CHANGE_CSC);
+			    &status, &change, NULL, PORT_CHANGE_CSC);
 
 			/* check if it is truly connected */
 			if (status & PORT_STATUS_CCS) {
@@ -2679,7 +2734,7 @@
 				(void) hubd_reset_port(hubd, port);
 
 				(void) hubd_determine_port_status(hubd, port,
-				    &status, &change, PORT_CHANGE_CSC);
+				    &status, &change, NULL, PORT_CHANGE_CSC);
 
 				if (status &
 				    (PORT_STATUS_CCS | PORT_STATUS_HSDA)) {
@@ -2829,7 +2884,7 @@
 
 	if (hubd->h_init_state & HUBD_CHILDREN_CREATED) {
 #ifdef DEBUG
-		for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
+		for (port = 1; port <= hubd->h_nports; port++) {
 			ASSERT(hubd->h_usba_devices[port] == NULL);
 			ASSERT(hubd->h_children_dips[port] == NULL);
 		}
@@ -2946,19 +3001,16 @@
 hubd_determine_port_connection(hubd_t	*hubd)
 {
 	usb_port_t	port;
-	usb_hub_descr_t	*hub_descr;
 	uint16_t	status;
 	uint16_t	change;
 	usb_port_mask_t	port_change = 0;
 
 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
 
-	hub_descr = &hubd->h_hub_descr;
-
-	for (port = 1; port <= hub_descr->bNbrPorts; port++) {
+	for (port = 1; port <= hubd->h_nports; port++) {
 
 		(void) hubd_determine_port_status(hubd, port, &status,
-		    &change, 0);
+		    &change, NULL, 0);
 
 		/* Check if port is in connect status */
 		if (!(status & PORT_STATUS_CCS)) {
@@ -3042,7 +3094,7 @@
 	 * ports go from 1 - n, allocate 1 more entry
 	 */
 	hubd->h_cd_list_length =
-	    (sizeof (dev_info_t **)) * (hubd->h_hub_descr.bNbrPorts + 1);
+	    (sizeof (dev_info_t **)) * (hubd->h_nports + 1);
 
 	hubd->h_children_dips = (dev_info_t **)kmem_zalloc(
 	    hubd->h_cd_list_length, KM_SLEEP);
@@ -3076,7 +3128,7 @@
 	 */
 	port_change = hubd_determine_port_connection(hubd);
 
-	if (port_change) {
+	if (port_change != 0 || hubd->h_port_change != 0) {
 		hubd_pm_busy_component(hubd, hubd->h_dip, 0);
 
 		arg->hubd = hubd;
@@ -3112,11 +3164,10 @@
 static int
 hubd_get_hub_descriptor(hubd_t *hubd)
 {
-	usb_hub_descr_t	*hub_descr = &hubd->h_hub_descr;
 	mblk_t		*data = NULL;
 	usb_cr_t	completion_reason;
 	usb_cb_flags_t	cb_flags;
-	uint16_t	length;
+	uint16_t	length, wValue;
 	int		rval;
 	usb_req_attrs_t attr = 0;
 
@@ -3131,14 +3182,30 @@
 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
 	ASSERT(hubd->h_default_pipe != 0);
 
-	/* get hub descriptor length first by requesting 8 bytes only */
 	mutex_exit(HUBD_MUTEX(hubd));
 
+	/*
+	 * The contents of wValue change depending on whether this is a USB 2 or
+	 * USB 3 device. SuperSpeed Hubs have different descriptors and you
+	 * cannot ask them for the traditional USB 2 descriptor.
+	 */
+	if (hubd->h_usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) {
+		wValue = USB_DESCR_TYPE_SS_HUB << 8 | HUBD_DEFAULT_DESC_INDEX;
+	} else {
+		wValue = USB_DESCR_TYPE_HUB << 8 | HUBD_DEFAULT_DESC_INDEX;
+	}
+
+	/*
+	 * The hub descriptor length varies in various versions of USB. For
+	 * example, in USB 2 it's at least 9 bytes long. To start with, we
+	 * always get the first 8 bytes so we can figure out how long it
+	 * actually is.
+	 */
 	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
 	    hubd->h_default_pipe,
 	    HUB_CLASS_REQ_TYPE,
 	    USB_REQ_GET_DESCR,		/* bRequest */
-	    USB_DESCR_TYPE_SETUP_HUB,	/* wValue */
+	    wValue,			/* wValue */
 	    0,				/* wIndex */
 	    8,				/* wLength */
 	    &data, 0,
@@ -3163,7 +3230,7 @@
 		    hubd->h_default_pipe,
 		    HUB_CLASS_REQ_TYPE,
 		    USB_REQ_GET_DESCR,		/* bRequest */
-		    USB_DESCR_TYPE_SETUP_HUB,	/* wValue */
+		    wValue,			/* wValue */
 		    0,				/* wIndex */
 		    length,			/* wLength */
 		    &data, attr,
@@ -3188,18 +3255,50 @@
 
 	mutex_enter(HUBD_MUTEX(hubd));
 
-	/* parse the hub descriptor */
-	/* only 32 ports are supported at present */
-	ASSERT(*(data->b_rptr + 2) <= 32);
-	if (usb_parse_CV_descr("cccscccccc",
-	    data->b_rptr, MBLKL(data),
-	    (void *)hub_descr, sizeof (usb_hub_descr_t)) == 0) {
-		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
-		    "parsing hub descriptor failed");
-
-		freemsg(data);
-
-		return (USB_FAILURE);
+	/*
+	 * Parse the hub descriptor. Note that the format of the descriptor
+	 * itself depends on the USB version. We handle the different ones and
+	 * transform it into a single uniform view.
+	 */
+
+	ASSERT(*(data->b_rptr + 2) <= (MAX_PORTS + 1));
+	if (hubd->h_usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) {
+		usb_ss_hub_descr_t hub_descr;
+		char *desc = "cccscccs";
+		ASSERT(*(data->b_rptr + 1) == ROOT_HUB_SS_DESCRIPTOR_TYPE);
+
+		/*
+		 * Note many hubs may support less than the 255 devices that the
+		 * USB specification allows for. In those cases, we'll simply
+		 * read less and it should be okay.
+		 */
+		if (usb_parse_CV_descr(desc, data->b_rptr, MBLKL(data),
+		    (void *)&hub_descr, sizeof (hub_descr)) == 0) {
+			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
+			    "parsing hub descriptor failed");
+			freemsg(data);
+			return (USB_FAILURE);
+		}
+
+		hubd->h_nports = hub_descr.bNbrPorts;
+		hubd->h_hub_chars = hub_descr.wHubCharacteristics;
+		hubd->h_power_good = hub_descr.bPwrOn2PwrGood;
+		hubd->h_current = hub_descr.bHubContrCurrent;
+	} else {
+		usb_hub_descr_t hub_descr;
+		if (usb_parse_CV_descr("cccscccccc",
+		    data->b_rptr, MBLKL(data),
+		    (void *)&hub_descr, sizeof (usb_hub_descr_t)) == 0) {
+			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
+			    "parsing hub descriptor failed");
+			freemsg(data);
+			return (USB_FAILURE);
+		}
+
+		hubd->h_nports = hub_descr.bNbrPorts;
+		hubd->h_hub_chars = hub_descr.wHubCharacteristics;
+		hubd->h_power_good = hub_descr.bPwrOn2PwrGood;
+		hubd->h_current = hub_descr.bHubContrCurrent;
 	}
 
 	freemsg(data);
@@ -3207,21 +3306,86 @@
 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
 	    "rval=0x%x bNbrPorts=0x%x wHubChars=0x%x "
 	    "PwrOn2PwrGood=0x%x HubContrCurrent=%dmA", rval,
-	    hub_descr->bNbrPorts, hub_descr->wHubCharacteristics,
-	    hub_descr->bPwrOn2PwrGood, hub_descr->bHubContrCurrent);
-
-	if (hub_descr->bNbrPorts > MAX_PORTS) {
+	    hubd->h_nports, hubd->h_hub_chars,
+	    hubd->h_power_good, hubd->h_current);
+
+	if (hubd->h_nports > MAX_PORTS) {
 		USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
 		    "Hub driver supports max of %d ports on hub. "
 		    "Hence using the first %d port of %d ports available",
-		    MAX_PORTS, MAX_PORTS, hub_descr->bNbrPorts);
-
-		hub_descr->bNbrPorts = MAX_PORTS;
+		    MAX_PORTS, MAX_PORTS, hubd->h_nports);
+
+		hubd->h_nports = MAX_PORTS;
 	}
 
 	return (USB_SUCCESS);
 }
 
+static int
+hubd_set_hub_depth(hubd_t *hubd)
+{
+	int rval;
+	usb_cr_t	completion_reason;
+	usb_cb_flags_t	cb_flags;
+	usba_device_t 	*ud;
+	uint16_t 	depth;
+
+	/*
+	 * We only need to set the hub depth devices for hubs that are at least
+	 * SuperSpeed devices. This didn't exist for USB 2.0 and older hubs.
+	 * There's also no need to call this on the root hub.
+	 */
+	if (hubd->h_usba_device->usb_port_status < USBA_SUPER_SPEED_DEV ||
+	    usba_is_root_hub(hubd->h_dip))
+		return (USB_SUCCESS);
+
+	depth = 0;
+	ud = hubd->h_usba_device;
+	while (ud->usb_parent_hub != NULL) {
+		depth++;
+		ud = ud->usb_parent_hub;
+	}
+	ASSERT(depth > 0);
+
+	if (depth > HUBD_SS_MAX_DEPTH) {
+		const char *mfg, *prod;
+
+		ud = hubd->h_usba_device;
+		prod = ud->usb_product_str;
+		if (prod == NULL)
+			prod = "Unknown Device";
+		mfg = ud->usb_mfg_str;
+		if (mfg == NULL)
+			mfg = "Unknown Manufacturer";
+		cmn_err(CE_WARN, "Unable to attach USB 3.x hub %s %s. A "
+		    "maximum of %d hubs may be cascaded", mfg, prod,
+		    HUBD_SS_MAX_DEPTH);
+		return (USB_FAILURE);
+	}
+
+	/*
+	 * When making the HUB_REQ_SET_HUB_DEPTH request, a hub connected to a
+	 * root port is considered to have a hub depth of zero whereas we
+	 * consider having a hub depth of one above.
+	 */
+	depth--;
+
+	if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
+	    hubd->h_default_pipe,
+	    HUB_SET_HUB_DEPTH_TYPE,
+	    HUB_REQ_SET_HUB_DEPTH,	/* bRequest */
+	    depth,			/* wValue */
+	    0,				/* wIndex */
+	    0,				/* wLength */
+	    NULL, 0,
+	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
+		USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
+		    "get set hub depth failed: cr=%d cb=0x%x",
+		    completion_reason, cb_flags);
+	}
+
+	return (rval);
+}
 
 /*
  * hubd_get_hub_status_words:
@@ -3290,8 +3454,8 @@
 	hubd->h_intr_pipe_state = HUBD_INTR_PIPE_OPENING;
 	mutex_exit(HUBD_MUTEX(hubd));
 
-	if ((rval = usb_pipe_open(hubd->h_dip,
-	    &hubd->h_ep1_descr, &hubd->h_pipe_policy,
+	if ((rval = usb_pipe_xopen(hubd->h_dip,
+	    &hubd->h_ep1_xdescr, &hubd->h_pipe_policy,
 	    0, &hubd->h_ep1_ph)) != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
 		    "open intr pipe failed (%d)", rval);
@@ -3345,7 +3509,7 @@
 		reqp->intr_client_private = (usb_opaque_t)hubd;
 		reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
 		    USB_ATTRS_AUTOCLEARING;
-		reqp->intr_len = hubd->h_ep1_descr.wMaxPacketSize;
+		reqp->intr_len = hubd->h_ep1_xdescr.uex_ep.wMaxPacketSize;
 		reqp->intr_cb = hubd_read_cb;
 		reqp->intr_exc_cb = hubd_exception_cb;
 		mutex_exit(HUBD_MUTEX(hubd));
@@ -3743,7 +3907,7 @@
 
 	ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE);
 
-	nports = hubd->h_hub_descr.bNbrPorts;
+	nports = hubd->h_nports;
 
 	hubd_stop_polling(hubd);
 
@@ -3797,7 +3961,7 @@
 
 			/* ack all changes */
 			(void) hubd_determine_port_status(hubd, port,
-			    &status, &change, HUBD_ACK_ALL_CHANGES);
+			    &status, &change, NULL, HUBD_ACK_ALL_CHANGES);
 
 			USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
 			    "handle port %d:\n\t"
@@ -4089,6 +4253,7 @@
 	long			settling_time;
 	uint16_t		status;
 	uint16_t		change;
+	usb_port_status_t	speed;
 	usb_addr_t		hubd_usb_addr;
 	usba_device_t		*usba_device;
 	usb_port_status_t	port_status = 0;
@@ -4133,7 +4298,7 @@
 
 		if ((rval = hubd_reset_port(hubd, port)) != USB_SUCCESS) {
 			(void) hubd_determine_port_status(hubd,
-			    port, &status, &change, 0);
+			    port, &status, &change, &speed, 0);
 
 			/* continue only if port is still connected */
 			if (status & PORT_STATUS_CCS) {
@@ -4176,7 +4341,7 @@
 		}
 
 		if ((rval = hubd_determine_port_status(hubd, port, &status,
-		    &change, 0)) != USB_SUCCESS) {
+		    &change, &speed, 0)) != USB_SUCCESS) {
 
 			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
 			    "getting status failed (%d)", rval);
@@ -4194,7 +4359,7 @@
 
 			/* ack changes */
 			(void) hubd_determine_port_status(hubd,
-			    port, &status, &change, PORT_CHANGE_OCIC);
+			    port, &status, &change, &speed, PORT_CHANGE_OCIC);
 
 			continue;
 		}
@@ -4213,18 +4378,7 @@
 				break;
 			}
 		} else {
-			/*
-			 * Determine if the device is high or full
-			 * or low speed.
-			 */
-			if (status & PORT_STATUS_LSDA) {
-				port_status = USBA_LOW_SPEED_DEV;
-			} else if (status & PORT_STATUS_HSDA) {
-				port_status = USBA_HIGH_SPEED_DEV;
-			} else {
-				port_status = USBA_FULL_SPEED_DEV;
-			}
-
+			port_status = speed;
 			USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
 			    "creating child port%d, status=0x%x "
 			    "port status=0x%x",
@@ -4354,7 +4508,7 @@
 		 * Host Controller. In this case, USB 2.0 Host Controller
 		 * will transfer the ownership of this port to USB 1.1
 		 * Host Controller. So don't display any error message on
-		 * the console.
+		 * the console. Note, this isn't the case for USB 3.x.
 		 */
 		if ((hubd_usb_addr == ROOT_HUB_ADDR) &&
 		    (hub_port_status == USBA_HIGH_SPEED_DEV) &&
@@ -4383,7 +4537,7 @@
 
 		/* ack all changes because we disabled this port */
 		(void) hubd_determine_port_status(hubd,
-		    port, &status, &change, HUBD_ACK_ALL_CHANGES);
+		    port, &status, &change, NULL, HUBD_ACK_ALL_CHANGES);
 
 	}
 
@@ -4407,7 +4561,6 @@
 	size_t		cfg_length;
 	uchar_t		*usb_cfg;
 	uint8_t		MaxPower;
-	usb_hub_descr_t	*hub_descr;
 	usb_port_t	port;
 
 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
@@ -4571,18 +4724,13 @@
 			 * since all port power status should be the same.
 			 */
 			(void) hubd_determine_port_status(hubd, 1, &status,
-			    &change, 0);
+			    &change, NULL, 0);
 
 			if (status & PORT_STATUS_PPS) {
-
 				return (USB_SUCCESS);
 			}
 
-			hub_descr = &hubd->h_hub_descr;
-
-			for (port = 1; port <= hub_descr->bNbrPorts;
-			    port++) {
-
+			for (port = 1; port <= hubd->h_nports; port++) {
 				(void) hubd_enable_port_power(hubd, port);
 			}
 
@@ -4601,9 +4749,68 @@
 	return (USB_SUCCESS);
 }
 
+/*
+ * Convert a series of USB status requests from USB 2 and USB 3 into a single
+ * uniform type. We separate out the speed into its own value from both USB 2
+ * and USB 3 and from there we transform the status to look like a USB 2 one.
+ */
+static void
+hubd_status_uniform(hubd_t *hubd, usb_port_t port, uint16_t *status,
+    usb_port_status_t *speed)
+{
+	uint16_t os = *status;
+
+	hubd->h_port_raw[port] = os;
+
+	if (hubd->h_usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) {
+		/*
+		 * USB 3 devices are always at super speed when plugged into a
+		 * super speed hub. However, this is only true if we're talking
+		 * about actual hubs. This doesn't hold for the root hub, which
+		 * can support USB 3.x, USB 2.x, and USB 1.x devices operating
+		 * at different speeds. To handle this, the USB 3 HCD driver
+		 * (xhci) uses some of the extra status bits to stash the
+		 * current device's detected speed.
+		 */
+		if (usba_is_root_hub(hubd->h_dip)) {
+			if (speed != NULL) {
+				*speed = (os & PORT_STATUS_SPMASK_SS) >>
+				    PORT_STATUS_SPSHIFT_SS;
+			}
+		} else {
+			if (speed != NULL)
+				*speed = USBA_SUPER_SPEED_DEV;
+		}
+
+		if (os & PORT_STATUS_PPS_SS) {
+			os &= ~PORT_STATUS_PPS_SS;
+			os |= PORT_STATUS_PPS;
+			*status = os;
+		}
+	} else {
+		/*
+		 * For USB 2, the only thing we need to do is transform the
+		 * speed.
+		 */
+		if (speed == NULL)
+			return;
+
+		if (os & PORT_STATUS_HSDA)
+			*speed = USBA_HIGH_SPEED_DEV;
+		else if (os & PORT_STATUS_LSDA)
+			*speed = USBA_LOW_SPEED_DEV;
+		else
+			*speed = USBA_FULL_SPEED_DEV;
+	}
+}
+
 
 /*
- * hubd_reset_port:
+ * Attempt to reset a port. This feels a bit more complicated than it should be
+ * in part due to how HCD, change status notifications, and the hotplug thread
+ * might interact. Basically we try to block port changes by using the
+ * h_port_reset_wait which says we should get signalled rather than kicking off
+ * the hotplug thread. We'll give this a shot for about 100ms at best.
  */
 static int
 hubd_reset_port(hubd_t *hubd, usb_port_t port)
@@ -4615,8 +4822,8 @@
 	mblk_t	*data;
 	uint16_t status;
 	uint16_t change;
-	int	i;
 	clock_t	delta;
+	boolean_t first;
 
 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
 	    "hubd_reset_port: port=%d", port);
@@ -4654,37 +4861,29 @@
 	 * wait for port status change event
 	 */
 	delta = drv_usectohz(hubd_device_delay / 10);
-	for (i = 0; i < hubd_retry_enumerate; i++) {
-		/*
-		 * start polling ep1 for receiving notification on
-		 * reset completion
-		 */
+
+	first = B_TRUE;
+	for (;;) {
+		if (delta < 0) {
+			rval = USB_FAILURE;
+			break;
+		}
+
+		if (first == B_FALSE)
+			hubd->h_port_reset_wait |= port_mask;
+		else
+			first = B_FALSE;
+
 		hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
 
 		/*
-		 * sleep a max of 100ms for reset completion
-		 * notification to be received
+		 * Regardless of the status, we always check to see if the port
+		 * has been reset.
 		 */
-		if (hubd->h_port_reset_wait & port_mask) {
-			rval = cv_reltimedwait(&hubd->h_cv_reset_port,
-			    &hubd->h_mutex, delta, TR_CLOCK_TICK);
-			if ((rval <= 0) &&
-			    (hubd->h_port_reset_wait & port_mask)) {
-				/* we got woken up because of a timeout */
-				USB_DPRINTF_L2(DPRINT_MASK_PORT,
-				    hubd->h_log_handle,
-				    "timeout: reset port=%d failed", port);
-
-				hubd->h_port_reset_wait &=  ~port_mask;
-
-				hubd_stop_polling(hubd);
-
-				return (USB_FAILURE);
-			}
-		}
-
-		USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
-		    "reset completion received");
+		delta = cv_reltimedwait(&hubd->h_cv_reset_port,
+		    &hubd->h_mutex, delta, TR_CLOCK_TICK);
+		if (delta < 0)
+			hubd->h_port_reset_wait &= ~port_mask;
 
 		hubd_stop_polling(hubd);
 
@@ -4720,11 +4919,13 @@
 
 		freemsg(data);
 
+		hubd_status_uniform(hubd, port, &status, NULL);
+
 		/* continue only if port is still connected */
 		if (!(status & PORT_STATUS_CCS)) {
 
 			/* lost connection, set exit condition */
-			i = hubd_retry_enumerate;
+			delta = -1;
 
 			mutex_enter(HUBD_MUTEX(hubd));
 
@@ -4762,16 +4963,38 @@
 				    port, completion_reason, cb_flags, rval);
 			}
 		}
+
+		/*
+		 * In addition to a normal reset, a warm reset may have
+		 * happened. Acknowledge that as well.
+		 */
+		if (change & PORT_CHANGE_BHPR) {
+			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
+			    "clearing feature CFS_C_BH_PORT_RESET");
+
+			if (usb_pipe_sync_ctrl_xfer(hubd->h_dip,
+			    hubd->h_default_pipe,
+			    HUB_HANDLE_PORT_FEATURE_TYPE,
+			    USB_REQ_CLEAR_FEATURE,
+			    CFS_C_BH_PORT_RESET,
+			    port,
+			    0,
+			    NULL, 0,
+			    &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
+				USB_DPRINTF_L2(DPRINT_MASK_PORT,
+				    hubd->h_log_handle,
+				    "clear feature CFS_C_BH_PORT_RESET"
+				    " port%d failed (%d 0x%x %d)",
+				    port, completion_reason, cb_flags, rval);
+			}
+		}
+
+		rval = USB_SUCCESS;
 		mutex_enter(HUBD_MUTEX(hubd));
 
 		break;
 	}
 
-	if (i >= hubd_retry_enumerate) {
-		/* port reset has failed */
-		rval = USB_FAILURE;
-	}
-
 	return (rval);
 }
 
@@ -4891,13 +5114,25 @@
  * hubd_determine_port_status:
  */
 static int
-hubd_determine_port_status(hubd_t *hubd, usb_port_t port,
-		uint16_t *status, uint16_t *change, uint_t ack_flag)
+hubd_determine_port_status(hubd_t *hubd, usb_port_t port, uint16_t *status,
+    uint16_t *change, usb_port_status_t *speed, uint_t ack_flag)
 {
 	int rval;
 	mblk_t	*data = NULL;
 	usb_cr_t completion_reason;
 	usb_cb_flags_t cb_flags;
+	uint16_t st, ch;
+	usb_port_status_t sp;
+
+	if (status == NULL)
+		status = &st;
+	if (change == NULL)
+		change = &ch;
+	if (speed == NULL)
+		speed = &sp;
+
+	*status = *change = 0;
+	*speed = 0;
 
 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
 	    "hubd_determine_port_status: port=%d, state=0x%x ack=0x%x", port,
@@ -4924,7 +5159,6 @@
 			freemsg(data);
 		}
 
-		*status = *change = 0;
 		mutex_enter(HUBD_MUTEX(hubd));
 
 		return (rval);
@@ -4936,7 +5170,6 @@
 		    "port %d: length incorrect %ld",
 		    port, MBLKL(data));
 		freemsg(data);
-		*status = *change = 0;
 
 		return (rval);
 	}
@@ -4944,6 +5177,7 @@
 
 	*status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
 	*change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
+	hubd_status_uniform(hubd, port, status, speed);
 
 	USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
 	    "port%d status=0x%x, change=0x%x", port, *status, *change);
@@ -5028,29 +5262,6 @@
 
 		hubd->h_port_state[port] &= ~(PORT_STATUS_PPS & ack_flag);
 	}
-	if (*status & PORT_STATUS_LSDA) {
-		USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
-		    "port%d low speed", port);
-
-		hubd->h_port_state[port] |= (PORT_STATUS_LSDA & ack_flag);
-	} else {
-		hubd->h_port_state[port] &= ~(PORT_STATUS_LSDA & ack_flag);
-		if (*status & PORT_STATUS_HSDA) {
-			USB_DPRINTF_L3(DPRINT_MASK_PORT,
-			    hubd->h_log_handle, "port%d "
-			    "high speed", port);
-
-			hubd->h_port_state[port] |=
-			    (PORT_STATUS_HSDA & ack_flag);
-		} else {
-			USB_DPRINTF_L3(DPRINT_MASK_PORT,
-			    hubd->h_log_handle, "port%d "
-			    "full speed", port);
-
-			hubd->h_port_state[port] &=
-			    ~(PORT_STATUS_HSDA & ack_flag);
-		}
-	}
 
 	/*
 	 * Acknowledge connection, enable, reset status
@@ -5154,6 +5365,63 @@
 				    port, completion_reason, cb_flags, rval);
 			}
 		}
+		if (*change & PORT_CHANGE_BHPR & ack_flag) {
+			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
+			    "clearing feature CFS_C_BH_PORT_RESET");
+			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
+			    hubd->h_default_pipe,
+			    HUB_HANDLE_PORT_FEATURE_TYPE,
+			    USB_REQ_CLEAR_FEATURE,
+			    CFS_C_BH_PORT_RESET,
+			    port,
+			    0, NULL, 0,
+			    &completion_reason, &cb_flags, 0)) !=
+			    USB_SUCCESS) {
+				USB_DPRINTF_L2(DPRINT_MASK_PORT,
+				    hubd->h_log_handle,
+				    "clear feature CFS_C_BH_PORT_RESET"
+				    " port%d failed (%d 0x%x %d)",
+				    port, completion_reason, cb_flags, rval);
+			}
+		}
+		if (*change & PORT_CHANGE_PLSC & ack_flag) {
+			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
+			    "clearing feature CFS_C_PORT_LINK_STATE");
+			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
+			    hubd->h_default_pipe,
+			    HUB_HANDLE_PORT_FEATURE_TYPE,
+			    USB_REQ_CLEAR_FEATURE,
+			    CFS_C_PORT_LINK_STATE,
+			    port,
+			    0, NULL, 0,
+			    &completion_reason, &cb_flags, 0)) !=
+			    USB_SUCCESS) {
+				USB_DPRINTF_L2(DPRINT_MASK_PORT,
+				    hubd->h_log_handle,
+				    "clear feature CFS_C_PORT_LINK_STATE"
+				    " port%d failed (%d 0x%x %d)",
+				    port, completion_reason, cb_flags, rval);
+			}
+		}
+		if (*change & PORT_CHANGE_PCE & ack_flag) {
+			USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
+			    "clearing feature CFS_C_PORT_CONFIG_ERROR");
+			if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
+			    hubd->h_default_pipe,
+			    HUB_HANDLE_PORT_FEATURE_TYPE,
+			    USB_REQ_CLEAR_FEATURE,
+			    CFS_C_PORT_CONFIG_ERROR,
+			    port,
+			    0, NULL, 0,
+			    &completion_reason, &cb_flags, 0)) !=
+			    USB_SUCCESS) {
+				USB_DPRINTF_L2(DPRINT_MASK_PORT,
+				    hubd->h_log_handle,
+				    "clear feature CFS_C_PORT_CONFIG_ERROR"
+				    " port%d failed (%d 0x%x %d)",
+				    port, completion_reason, cb_flags, rval);
+			}
+		}
 		mutex_enter(HUBD_MUTEX(hubd));
 	}
 
@@ -5182,7 +5450,7 @@
 	(void) hubd_enable_port(hubd, port);
 
 	/* read the port status */
-	(void) hubd_determine_port_status(hubd, port, &status, &change,
+	(void) hubd_determine_port_status(hubd, port, &status, &change, NULL,
 	    PORT_CHANGE_PESC);
 
 	if (status & PORT_STATUS_PES) {
@@ -5211,7 +5479,6 @@
 static int
 hubd_enable_all_port_power(hubd_t *hubd)
 {
-	usb_hub_descr_t	*hub_descr;
 	int		wait;
 	usb_port_t	port;
 	uint_t		retry;
@@ -5223,33 +5490,32 @@
 
 	ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
 
-	hub_descr = &hubd->h_hub_descr;
-
 	/*
 	 * According to section 11.11 of USB, for hubs with no power
 	 * switches, bPwrOn2PwrGood is zero. But we wait for some
 	 * arbitrary time to enable power to become stable.
 	 *
 	 * If an hub supports port power switching, we need to wait
-	 * at least 20ms before accessing corresponding usb port.
+	 * at least 20ms before accessing corresponding usb port. Note,
+	 * this member is stored in the h_power_good member.
 	 */
-	if ((hub_descr->wHubCharacteristics &
-	    HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) {
+	if ((hubd->h_hub_chars & HUB_CHARS_NO_POWER_SWITCHING) ||
+	    (hubd->h_power_good == 0)) {
 		wait = hubd_device_delay / 10;
 	} else {
 		wait = max(HUB_DEFAULT_POPG,
-		    hub_descr->bPwrOn2PwrGood) * 2 * 1000;
+		    hubd->h_power_good) * 2 * 1000;
 	}
 
 	USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
 	    "hubd_enable_all_port_power: popg=%d wait=%d",
-	    hub_descr->bPwrOn2PwrGood, wait);
+	    hubd->h_power_good, wait);
 
 	/*
 	 * Enable power per port. we ignore gang power and power mask
 	 * and always enable all ports one by one.
 	 */
-	for (port = 1; port <= hub_descr->bNbrPorts; port++) {
+	for (port = 1; port <= hubd->h_nports; port++) {
 		/*
 		 * Transition the port from the Powered Off to the
 		 * Disconnected state by supplying power to the port.
@@ -5269,11 +5535,11 @@
 	wait = max(wait, hubd_device_delay / 10);
 
 	/* Check each port power status for a given usb hub */
-	for (port = 1; port <= hub_descr->bNbrPorts; port++) {
+	for (port = 1; port <= hubd->h_nports; port++) {
 
 		/* Get port status */
 		(void) hubd_determine_port_status(hubd, port,
-		    &status, &change, 0);
+		    &status, &change, NULL, 0);
 
 		for (retry = 0; ((!(status & PORT_STATUS_PPS)) &&
 		    (retry < HUBD_PORT_RETRY)); retry++) {
@@ -5290,7 +5556,7 @@
 
 			/* Get port status */
 			(void) hubd_determine_port_status(hubd, port,
-			    &status, &change, 0);
+			    &status, &change, NULL, 0);
 		}
 
 		/* Print warning message if port has no power */
@@ -5364,7 +5630,7 @@
 	/*
 	 * disable power per port, ignore gang power and power mask
 	 */
-	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
+	for (port = 1; port <= hubd->h_nports; port++) {
 		(void) hubd_disable_port_power(hubd, port);
 	}
 
@@ -5422,7 +5688,7 @@
  */
 int
 hubd_select_device_configuration(hubd_t *hubd, usb_port_t port,
-	dev_info_t *child_dip, usba_device_t *child_ud)
+    dev_info_t *child_dip, usba_device_t *child_ud)
 {
 	char		*pathname = NULL;
 	char		*tmp_path = NULL;
@@ -5498,7 +5764,7 @@
  */
 int
 hubd_get_this_config_cloud(hubd_t *hubd, dev_info_t *dip,
-	usba_device_t *child_ud, uint16_t conf_index)
+    usba_device_t *child_ud, uint16_t conf_index)
 {
 	usb_cfg_descr_t	*confdescr;
 	mblk_t		*pdata = NULL;
@@ -5685,7 +5951,7 @@
  */
 int
 hubd_get_all_device_config_cloud(hubd_t *hubd, dev_info_t *dip,
-	usba_device_t *child_ud)
+    usba_device_t *child_ud)
 {
 	int		rval = USB_SUCCESS;
 	int		ncfgs;
@@ -5844,6 +6110,7 @@
 	usb_port_t		parent_usb_port;
 	usba_device_t		*parent_usba_dev;
 	usb_port_status_t	parent_port_status;
+	boolean_t		hcd_called = B_FALSE;
 
 	USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
 	    "hubd_create_child: port=%d", port);
@@ -5881,8 +6148,9 @@
 	parent_port_status = parent_ud->usb_port_status;
 
 	/*
-	 * To support split transactions, update address and port
-	 * of high speed hub to which given device is connected.
+	 * To support split transactions, update address and port of high speed
+	 * hub to which given device is connected.  Note, split transactions
+	 * only exist for high speed devices.
 	 */
 	if (parent_port_status == USBA_HIGH_SPEED_DEV) {
 		parent_usba_dev = parent_ud;
@@ -5901,16 +6169,55 @@
 	child_ud->usb_dev_descr = kmem_alloc(sizeof (usb_dev_descr_t),
 	    KM_SLEEP);
 	bzero(&usb_dev_descr, sizeof (usb_dev_descr_t));
-	usb_dev_descr.bMaxPacketSize0 =
-	    (port_status == USBA_LOW_SPEED_DEV) ? 8 : 64;
+
+	switch (port_status) {
+	case USBA_SUPER_SPEED_DEV:
+		usb_dev_descr.bMaxPacketSize0 = 9;
+		break;
+	case USBA_LOW_SPEED_DEV:
+		usb_dev_descr.bMaxPacketSize0 = 8;
+		break;
+	default:
+		usb_dev_descr.bMaxPacketSize0 = 64;
+		break;
+	}
 	bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
 	    sizeof (usb_dev_descr_t));
 	child_ud->usb_port = port;
+
+	/*
+	 * The parent hub always keeps track of the hub this device is connected
+	 * to; however, the hs_hub_* variables are only keeping track of the
+	 * closest high speed hub. Unfortunately, we need both.
+	 */
+	child_ud->usb_parent_hub = parent_ud;
 	child_ud->usb_hs_hub_usba_dev = parent_usba_dev;
 	child_ud->usb_hs_hub_addr = parent_usb_addr;
 	child_ud->usb_hs_hub_port = parent_usb_port;
 	mutex_exit(&child_ud->usb_mutex);
 
+	/*
+	 * Before we open up the default pipe, give the HCD a chance to do
+	 * something here.
+	 */
+	if (child_ud->usb_hcdi_ops->usba_hcdi_device_init != NULL) {
+		int rval;
+		void *priv = NULL;
+
+		rval = child_ud->usb_hcdi_ops->usba_hcdi_device_init(child_ud,
+		    port, &priv);
+		if (rval != USB_SUCCESS) {
+			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
+			    "HCD usba_hcdi_Device_init failed (%d)", rval);
+			goto fail_cleanup;
+		}
+
+		child_ud->usb_hcd_private = priv;
+		hcd_called = B_TRUE;
+	}
+
+
+
 	/* Open the default pipe */
 	if ((rval = usb_pipe_open(child_dip, NULL, NULL,
 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
@@ -5935,6 +6242,11 @@
 	    &pdata, USB_ATTRS_SHORT_XFER_OK,
 	    &completion_reason, &cb_flags, 0);
 
+	/*
+	 * If this is a full speed device, we cannot assume that its default
+	 * packet size is 64 bytes, it may be 8 bytes.
+	 */
+
 	if ((rval != USB_SUCCESS) &&
 	    (!((completion_reason == USB_CR_DATA_OVERRUN) && pdata))) {
 
@@ -5997,23 +6309,30 @@
 		goto fail_cleanup;
 	}
 
-	/* Set the address of the device */
-	if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
-	    USB_DEV_REQ_HOST_TO_DEV,
-	    USB_REQ_SET_ADDRESS,	/* bRequest */
-	    address,			/* wValue */
-	    0,				/* wIndex */
-	    0,				/* wLength */
-	    NULL, 0,
-	    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
-		char buffer[64];
-		USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
-		    "setting address failed (cr=%s cb_flags=%s rval=%d)",
-		    usb_str_cr(completion_reason),
-		    usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
-		    rval);
-
-		goto fail_cleanup;
+	if (child_ud->usb_hcdi_ops->usba_hcdi_device_address != NULL) {
+		rval = child_ud->usb_hcdi_ops->usba_hcdi_device_address(
+		    child_ud);
+		if (rval != USB_SUCCESS)
+			goto fail_cleanup;
+	} else {
+		/* Set the address of the device */
+		if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
+		    USB_DEV_REQ_HOST_TO_DEV,
+		    USB_REQ_SET_ADDRESS,	/* bRequest */
+		    address,			/* wValue */
+		    0,				/* wIndex */
+		    0,				/* wLength */
+		    NULL, 0,
+		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
+			char buffer[64];
+			USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
+			    "setting address failed (cr=%s cb_flags=%s "
+			    "rval=%d)", usb_str_cr(completion_reason),
+			    usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
+			    rval);
+
+			goto fail_cleanup;
+		}
 	}
 
 	USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
@@ -6195,7 +6514,7 @@
 	/*
 	 * Warn users of a performance hit if connecting a
 	 * High Speed behind a 1.1 hub, which is behind a
-	 * 2.0 port.
+	 * 2.0 port. Don't worry about this for USB 3.x for now.
 	 */
 	if ((parent_port_status != USBA_HIGH_SPEED_DEV) &&
 	    !(usba_is_root_hub(parent_ud->usb_dip)) &&
@@ -6396,6 +6715,14 @@
 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
 	}
 
+	if (child_ud != NULL && hcd_called == B_TRUE &&
+	    child_ud->usb_hcdi_ops->usba_hcdi_device_fini != NULL) {
+		child_ud->usb_hcdi_ops->usba_hcdi_device_fini(child_ud,
+		    child_ud->usb_hcd_private);
+		child_ud->usb_hcd_private = NULL;
+	}
+
+
 	if (child_dip) {
 		int rval = usba_destroy_child_devi(child_dip,
 		    NDI_DEVI_REMOVE);
@@ -6459,6 +6786,7 @@
 			usba_hubdi_incr_power_budget(hubd->h_dip, usba_device);
 		}
 
+
 		rval = usba_destroy_child_devi(child_dip, flag);
 
 		if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) {
@@ -6519,7 +6847,6 @@
 			ASSERT(i_ddi_node_state(dip) < DS_INITIALIZED);
 		}
 #endif
-
 		port = usba_device->usb_port;
 		hubd->h_usba_devices[port] = NULL;
 
@@ -6656,7 +6983,7 @@
 	    "hubd_run_callbacks");
 
 	mutex_enter(HUBD_MUTEX(hubd));
-	for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
+	for (port = 1; port <= hubd->h_nports; port++) {
 		/*
 		 * the childen_dips list may have dips that have been
 		 * already deallocated. we only get a post_detach notification
@@ -6855,7 +7182,7 @@
 		mutex_enter(HUBD_MUTEX(hubd));
 
 		/* close all the open pipes of our children */
-		nports = hubd->h_hub_descr.bNbrPorts;
+		nports = hubd->h_nports;
 		for (port = 1; port <= nports; port++) {
 			usba_dev = hubd->h_usba_devices[port];
 			if (usba_dev != NULL) {
@@ -6992,7 +7319,7 @@
 	case USB_DEV_PWRED_DOWN:
 	case USB_DEV_DISCONNECTED:
 		/* find out if all our children have been quiesced */
-		nports = hubd->h_hub_descr.bNbrPorts;
+		nports = hubd->h_nports;
 		for (port = 1; (no_cpr == 0) && (port <= nports); port++) {
 			usba_dev = hubd->h_usba_devices[port];
 			if (usba_dev != NULL) {
@@ -7305,7 +7632,7 @@
 /* ARGSUSED */
 int
 usba_hubdi_open(dev_info_t *dip, dev_t *devp, int flags, int otyp,
-	cred_t *credp)
+    cred_t *credp)
 {
 	hubd_t *hubd;
 
@@ -7371,7 +7698,7 @@
 /* ARGSUSED */
 int
 usba_hubdi_ioctl(dev_info_t *self, dev_t dev, int cmd, intptr_t arg,
-	int mode, cred_t *credp, int *rvalp)
+    int mode, cred_t *credp, int *rvalp)
 {
 	int			rv = 0;
 	char			*msg;	/* for messages */
@@ -8175,7 +8502,6 @@
 static int
 hubd_toggle_port(hubd_t *hubd, usb_port_t port)
 {
-	usb_hub_descr_t	*hub_descr;
 	int		wait;
 	uint_t		retry;
 	uint16_t	status;
@@ -8197,27 +8523,26 @@
 	delay(drv_usectohz(hubd_device_delay / 10));
 	mutex_enter(HUBD_MUTEX(hubd));
 
-	hub_descr = &hubd->h_hub_descr;
-
 	/*
 	 * According to section 11.11 of USB, for hubs with no power
 	 * switches, bPwrOn2PwrGood is zero. But we wait for some
 	 * arbitrary time to enable power to become stable.
 	 *
 	 * If an hub supports port power swicthing, we need to wait
-	 * at least 20ms before accesing corresonding usb port.
+	 * at least 20ms before accesing corresonding usb port. Note
+	 * this member is stored in the h_power_good member.
 	 */
-	if ((hub_descr->wHubCharacteristics &
-	    HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) {
+	if ((hubd->h_hub_chars & HUB_CHARS_NO_POWER_SWITCHING) ||
+	    (hubd->h_power_good == 0)) {
 		wait = hubd_device_delay / 10;
 	} else {
 		wait = max(HUB_DEFAULT_POPG,
-		    hub_descr->bPwrOn2PwrGood) * 2 * 1000;
+		    hubd->h_power_good) * 2 * 1000;
 	}
 
 	USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
 	    "hubd_toggle_port: popg=%d wait=%d",
-	    hub_descr->bPwrOn2PwrGood, wait);
+	    hubd->h_power_good, wait);
 
 	retry = 0;
 
@@ -8230,7 +8555,7 @@
 
 		/* Get port status */
 		(void) hubd_determine_port_status(hubd, port,
-		    &status, &change, 0);
+		    &status, &change, NULL, 0);
 
 		/* For retry if any, use some extra delay */
 		wait = max(wait, hubd_device_delay / 10);
@@ -8379,9 +8704,7 @@
 		 * and get the power that can be supplied to
 		 * downstream ports
 		 */
-		hubd->h_pwr_left -=
-		    hubd->h_hub_descr.bHubContrCurrent /
-		    USB_CFG_DESCR_PWR_UNIT;
+		hubd->h_pwr_left -= hubd->h_current / USB_CFG_DESCR_PWR_UNIT;
 		if (hubd->h_pwr_left < 0) {
 			USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
 			    "hubd->h_pwr_left is less than bHubContrCurrent, "
@@ -8402,7 +8725,7 @@
  */
 int
 usba_hubdi_check_power_budget(dev_info_t *dip, usba_device_t *child_ud,
-	uint_t config_index)
+    uint_t config_index)
 {
 	int16_t		pwr_left, pwr_limit, pwr_required;
 	size_t		size;
@@ -8780,7 +9103,7 @@
 			mutex_enter(HUBD_MUTEX(hubd));
 		} else {
 			(void) hubd_determine_port_status(hubd, reset_port,
-			    &status, &change, HUBD_ACK_ALL_CHANGES);
+			    &status, &change, NULL, HUBD_ACK_ALL_CHANGES);
 
 			/* Reset the parent hubd port and create new child */
 			if (status & PORT_STATUS_CCS) {
--- a/usr/src/uts/common/io/usb/usba/usba.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/common/io/usb/usba/usba.c	Fri Mar 10 18:57:44 2017 -0500
@@ -230,18 +230,18 @@
 				    usb_get_if_number(rdip));
 			}
 			switch (usba_device->usb_port_status) {
+			case USBA_SUPER_SPEED_DEV:
+				speed = "super speed (USB 3.x)";
+				break;
 			case USBA_HIGH_SPEED_DEV:
 				speed = "hi speed (USB 2.x)";
-
 				break;
 			case USBA_LOW_SPEED_DEV:
 				speed = "low speed (USB 1.x)";
-
 				break;
 			case USBA_FULL_SPEED_DEV:
 			default:
 				speed = "full speed (USB 1.x)";
-
 				break;
 			}
 
@@ -678,6 +678,16 @@
 		}
 	}
 
+	/*
+	 * Give the HCD a chance to clean up this child device before we finish
+	 * tearing things down.
+	 */
+	if (usba_device->usb_hcdi_ops->usba_hcdi_device_fini != NULL) {
+		usba_device->usb_hcdi_ops->usba_hcdi_device_fini(
+		    usba_device, usba_device->usb_hcd_private);
+		usba_device->usb_hcd_private = NULL;
+	}
+
 	mutex_enter(&usba_mutex);
 
 	/* destroy mutex in each usba_ph_impl structure */
@@ -2259,6 +2269,16 @@
 		}
 	}
 
+	if (usba_device->usb_port_status == USBA_SUPER_SPEED_DEV) {
+		rval = ndi_prop_create_boolean(DDI_DEV_T_NONE, child_dip,
+		    "super-speed");
+		if (rval != DDI_PROP_SUCCESS) {
+			USB_DPRINTF_L2(DPRINT_MASK_USBA, usba_log_handle,
+			    "usba_ready_device_node: "
+			    "super speed prop update failed");
+		}
+	}
+
 	USB_DPRINTF_L4(DPRINT_MASK_USBA, usba_log_handle,
 	    "%s%d at port %d: %s, dip=0x%p",
 	    ddi_node_name(ddi_get_parent(child_dip)),
--- a/usr/src/uts/common/io/usb/usba/usba_ugen.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/common/io/usb/usba/usba_ugen.c	Fri Mar 10 18:57:44 2017 -0500
@@ -24,7 +24,7 @@
  */
 
 /*
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -1486,7 +1486,7 @@
  */
 static int
 ugen_epxs_data_init(ugen_state_t *ugenp, usb_ep_data_t *ep_data,
-	uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
+    uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
 {
 	int			ep_index;
 	ugen_ep_t		*epp;
@@ -1520,6 +1520,21 @@
 		epp->ep_lcmd_status	= USB_LC_STAT_NOERROR;
 		epp->ep_pipe_policy.pp_max_async_reqs = 1;
 
+		if (ep_data == NULL) {
+			bzero(&epp->ep_xdescr, sizeof (usb_ep_xdescr_t));
+			epp->ep_xdescr.uex_version =
+			    USB_EP_XDESCR_CURRENT_VERSION;
+			epp->ep_xdescr.uex_ep = *ep_descr;
+		} else {
+			/*
+			 * The only way this could fail is we have a bad
+			 * version, which shouldn't be possible inside of the
+			 * usba module itself.
+			 */
+			(void) usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
+			    ugenp->ug_dip, ep_data, &epp->ep_xdescr);
+		}
+
 		cv_init(&epp->ep_wait_cv, NULL, CV_DRIVER, NULL);
 		epp->ep_ser_cookie	= usb_init_serialization(
 		    ugenp->ug_dip, 0);
@@ -1985,6 +2000,8 @@
 		    usb_get_ep_index(epp->ep_descr.
 		    bEndpointAddress)) {
 			epp->ep_descr = ep_data->ep_descr;
+			(void) usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
+			    ugenp->ug_dip, ep_data, &epp->ep_xdescr);
 
 			break;
 		}
@@ -2085,9 +2102,8 @@
 	} else {
 		mutex_exit(&epp->ep_mutex);
 
-		/* open pipe */
-		rval = usb_pipe_open(ugenp->ug_dip,
-		    &epp->ep_descr, &epp->ep_pipe_policy,
+		rval = usb_pipe_xopen(ugenp->ug_dip,
+		    &epp->ep_xdescr, &epp->ep_pipe_policy,
 		    USB_FLAGS_SLEEP, &epp->ep_ph);
 
 		mutex_enter(&epp->ep_mutex);
--- a/usr/src/uts/common/io/usb/usba/usbai_pipe_mgmt.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/common/io/usb/usba/usbai_pipe_mgmt.c	Fri Mar 10 18:57:44 2017 -0500
@@ -23,6 +23,7 @@
  * Use is subject to license terms.
  *
  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
+ * Copyright 2016 Joyent, Inc.
  */
 
 
@@ -428,9 +429,11 @@
 usba_init_pipe_handle(dev_info_t *dip,
 	usba_device_t		*usba_device,
 	usb_ep_descr_t		*ep,
+	usb_ep_xdescr_t		*ep_xdescr,
 	usb_pipe_policy_t	*pipe_policy,
 	usba_ph_impl_t		*ph_impl)
 {
+	usb_ep_xdescr_t xep;
 	int instance = ddi_get_instance(dip);
 	unsigned int def_instance = instance;
 	static unsigned int anon_instance = 0;
@@ -509,9 +512,25 @@
 		usba_device->usb_shared_taskq_ref_count[iface]++;
 	}
 
+	/*
+	 * In the future, when we may have different versions of the extended
+	 * endpoint descriptor, they should be normalized to the current version
+	 * here such that all of the HCI drivers have a consistent view of the
+	 * world. The extended descriptor may be NULL if we are opening the
+	 * default control endpoint; however, we create a uniform view for the
+	 * HCI drivers.
+	 */
+	if (ep_xdescr == NULL) {
+		bzero(&xep, sizeof (usb_ep_xdescr_t));
+		xep.uex_version = USB_EP_XDESCR_CURRENT_VERSION;
+		xep.uex_ep = *ep;
+		ep_xdescr = &xep;
+	}
+
 	ph_data->p_dip		= dip;
 	ph_data->p_usba_device	= usba_device;
 	ph_data->p_ep		= *ep;
+	ph_data->p_xep		= *ep_xdescr;
 	ph_data->p_ph_impl	= ph_impl;
 	if ((ep->bmAttributes & USB_EP_ATTR_MASK) ==
 	    USB_EP_ATTR_ISOCH) {
@@ -759,13 +778,14 @@
  *	USB_*		 - refer to usbai.h
  */
 int
-usb_pipe_open(
+usb_pipe_xopen(
 	dev_info_t		*dip,
-	usb_ep_descr_t		*ep,
+	usb_ep_xdescr_t		*ep_xdesc,
 	usb_pipe_policy_t	*pipe_policy,
 	usb_flags_t		usb_flags,
 	usb_pipe_handle_t	*pipe_handle)
 {
+	usb_ep_descr_t		*ep;
 	usba_device_t		*usba_device;
 	int			rval;
 	usba_pipe_handle_data_t *ph_data;
@@ -776,8 +796,8 @@
 
 	USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
 	    "usb_pipe_open:\n\t"
-	    "dip=0x%p ep=0x%p pp=0x%p uf=0x%x ph=0x%p",
-	    (void *)dip, (void *)ep, (void *)pipe_policy, usb_flags,
+	    "dip=0x%p ep_xdesc=0x%p pp=0x%p uf=0x%x ph=0x%p",
+	    (void *)dip, (void *)ep_xdesc, (void *)pipe_policy, usb_flags,
 	    (void *)pipe_handle);
 
 	if ((dip == NULL) || (pipe_handle == NULL)) {
@@ -785,13 +805,53 @@
 		return (USB_INVALID_ARGS);
 	}
 
+	if ((ep_xdesc != NULL) &&
+	    ((ep_xdesc->uex_version != USB_EP_XDESCR_CURRENT_VERSION) ||
+	    ((ep_xdesc->uex_flags & ~USB_EP_XFLAGS_SS_COMP) != 0))) {
+
+		return (USB_INVALID_ARGS);
+	}
+
 	if (servicing_interrupt() && (usb_flags & USB_FLAGS_SLEEP)) {
 
 		return (USB_INVALID_CONTEXT);
 	}
 	usba_device = usba_get_usba_device(dip);
 
-	if ((ep != NULL) && (pipe_policy == NULL)) {
+	/*
+	 * Check the device's speed. If we're being asked to open anything other
+	 * than the default endpoint and the device is superspeed or greater and
+	 * we only have a usb_ep_descr_t and not the full endpoint data, then
+	 * this was coming through usb_pipe_open() and we need to fail this
+	 * call.
+	 *
+	 * Some drivers technically cheat and open the default control endpoint
+	 * even though they're not supposed to. ugen appears to be the main
+	 * offender. To deal with this, we check to see if the endpoint
+	 * descriptor bcmps to our default and give them a break, since we don't
+	 * need extended info for default control endpoints.
+	 */
+	if (ep_xdesc != NULL && ep_xdesc->uex_flags == 0 &&
+	    bcmp(&ep_xdesc->uex_ep, &usba_default_ep_descr,
+	    sizeof (usb_ep_descr_t)) != 0 &&
+	    usba_device->usb_port_status >= USBA_SUPER_SPEED_DEV) {
+		const char *dname = ddi_driver_name(dip);
+		const char *prod, *mfg;
+
+		prod = usba_device->usb_product_str;
+		if (prod == NULL)
+			prod = "Unknown Device";
+		mfg = usba_device->usb_mfg_str;
+		if (mfg == NULL)
+			mfg = "Unknown Manufacturer";
+		cmn_err(CE_NOTE, "driver %s attempting to open non-default "
+		    "of a USB 3.0 or newer device through usb_pipe_open(). "
+		    "%s must be updated to use usb_pipe_xopen() to work with "
+		    "USB device %s %s.", dname, dname, mfg, prod);
+		return (USB_FAILURE);
+	}
+
+	if ((ep_xdesc != NULL) && (pipe_policy == NULL)) {
 		USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
 		    "usb_pipe_open: null pipe policy");
 
@@ -799,7 +859,7 @@
 	}
 
 	/* is the device still connected? */
-	if ((ep != NULL) & DEVI_IS_DEVICE_REMOVED(dip)) {
+	if ((ep_xdesc != NULL) & DEVI_IS_DEVICE_REMOVED(dip)) {
 		USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
 		    "usb_pipe_open: device has been removed");
 
@@ -811,7 +871,7 @@
 	 * if a null endpoint pointer was passed, use the default
 	 * endpoint descriptor
 	 */
-	if (ep == NULL) {
+	if (ep_xdesc == NULL) {
 		if ((usb_flags & USBA_FLAGS_PRIVILEGED) == 0) {
 			USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
 			    "usb_pipe_open: not allowed to open def pipe");
@@ -821,6 +881,8 @@
 
 		ep = &usba_default_ep_descr;
 		pipe_policy = &usba_default_ep_pipe_policy;
+	} else {
+		ep = &ep_xdesc->uex_ep;
 	}
 
 	if (usb_flags & USB_FLAGS_SERIALIZED_CB) {
@@ -876,7 +938,7 @@
 	 * allocate and initialize the pipe handle
 	 */
 	if ((rval = usba_init_pipe_handle(dip, usba_device,
-	    ep, pipe_policy, ph_impl)) != USB_SUCCESS) {
+	    ep, ep_xdesc, pipe_policy, ph_impl)) != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
 		    "usb_pipe_open: pipe init failed (%d)", rval);
 
@@ -908,6 +970,29 @@
 	return (rval);
 }
 
+int
+usb_pipe_open(
+	dev_info_t		*dip,
+	usb_ep_descr_t		*ep,
+	usb_pipe_policy_t	*pipe_policy,
+	usb_flags_t		usb_flags,
+	usb_pipe_handle_t	*pipe_handle)
+{
+	usb_ep_xdescr_t xdesc, *xp = NULL;
+
+	/*
+	 * ep may be NULL if trying to open the default control endpoint.
+	 */
+	if (ep != NULL) {
+		bzero(&xdesc, sizeof (usb_ep_xdescr_t));
+		xdesc.uex_version = USB_EP_XDESCR_CURRENT_VERSION;
+		xdesc.uex_ep = *ep;
+		xp = &xdesc;
+	}
+
+	return (usb_pipe_xopen(dip, xp, pipe_policy, usb_flags,
+	    pipe_handle));
+}
 
 /*
  * usb_pipe_close/sync_close:
--- a/usr/src/uts/common/io/usb/usba/usbai_register.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/common/io/usb/usba/usbai_register.c	Fri Mar 10 18:57:44 2017 -0500
@@ -23,6 +23,7 @@
  * Use is subject to license terms.
  *
  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -119,6 +120,7 @@
 static void usba_process_cfg_descr(usba_reg_state_t *);
 static int usba_process_if_descr(usba_reg_state_t *, boolean_t *);
 static int usba_process_ep_descr(usba_reg_state_t *);
+static int usba_process_ss_ep_comp_descr(usba_reg_state_t *);
 static int usba_process_cv_descr(usba_reg_state_t *);
 static int usba_set_parse_values(dev_info_t *dip, usba_device_t *usba_device,
     usba_reg_state_t *state);
@@ -799,6 +801,31 @@
 				}
 
 				break;
+
+			case USB_DESCR_TYPE_SS_EP_COMP:
+
+				/*
+				 * These entries should always follow an
+				 * endpoint description. If an endpoint
+				 * description wasn't the last
+				 * thing that we found, then we shouldn't
+				 * process this descriptor.
+				 */
+				if (state.st_last_processed_descr_type ==
+				    USB_DESCR_TYPE_EP) {
+					if (usba_process_ss_ep_comp_descr(
+					    &state) != USB_SUCCESS) {
+
+						return (USB_FAILURE);
+					}
+
+					state.st_last_processed_descr_type =
+					    USB_DESCR_TYPE_SS_EP_COMP;
+
+					break;
+				}
+				break;
+
 			case USB_DESCR_TYPE_STRING:
 				USB_DPRINTF_L2(DPRINT_MASK_ALL,
 				    usbai_reg_log_handle,
@@ -1046,6 +1073,33 @@
 	return (USB_SUCCESS);
 }
 
+/*
+ * usba_process_ss_ep_comp_descr:
+ * 	This processes a raw endpoint companion descriptor and associates it
+ * 	inside of an existing endpoint's entry.
+ *
+ * Arguments:
+ *	state		- Pointer to this module's state structure.
+ *
+ * Returns:
+ *	USB_SUCCESS:	Descriptor is successfully parsed.
+ *	USB_FAILURE:	Descriptor is inappropriately placed in config cloud.
+ */
+static int
+usba_process_ss_ep_comp_descr(usba_reg_state_t *state)
+{
+	if (state->st_curr_ep == NULL)
+		return (USB_FAILURE);
+
+	(void) usb_parse_data("4cs", state->st_curr_raw_descr,
+	    state->st_curr_raw_descr_len,
+	    &state->st_curr_ep->ep_ss_comp,
+	    sizeof (usb_ep_ss_comp_descr_t));
+	state->st_curr_ep->ep_ss_valid = B_TRUE;
+
+	return (USB_SUCCESS);
+}
+
 
 /*
  * usba_process_cv_descr:
@@ -1798,7 +1852,7 @@
  */
 static void
 usba_dump_ep(uint_t which_ep, usb_ep_data_t *ep, usb_log_handle_t dump_handle,
-		uint_t dump_level, uint_t dump_mask, char *string)
+    uint_t dump_level, uint_t dump_mask, char *string)
 {
 	int which_cv;
 	usb_ep_descr_t *ep_descr = &ep->ep_descr;
@@ -1919,3 +1973,45 @@
 		(void) usb_log(dump_handle, dump_level, dump_mask, buffer);
 	}
 }
+
+/*
+ * usb_ep_xdescr_fill:
+ *
+ * Fills in the extended endpoint descriptor based on data from the
+ * configuration tree.
+ *
+ * Arguments:
+ * 	version		- Should be USB_EP_XDESCR_CURRENT_VERSION
+ * 	dip		- devinfo pointer
+ * 	ep_data		- endpoint data pointer
+ * 	ep_xdesc	- An extended descriptor structure, filled upon
+ *			  successful completion.
+ *
+ * Return values:
+ *	USB_SUCCESS	 - filling data succeeded
+ *	USB_INVALID_ARGS - invalid arguments
+ */
+int
+usb_ep_xdescr_fill(uint_t version, dev_info_t *dip, usb_ep_data_t *ep_data,
+    usb_ep_xdescr_t *ep_xdescr)
+{
+	if (version != USB_EP_XDESCR_VERSION_ONE) {
+
+		return (USB_INVALID_ARGS);
+	}
+
+	if (dip == NULL || ep_data == NULL || ep_xdescr == NULL) {
+
+		return (USB_INVALID_ARGS);
+	}
+
+	bzero(ep_xdescr, sizeof (usb_ep_xdescr_t));
+	ep_xdescr->uex_version = version;
+	ep_xdescr->uex_ep = ep_data->ep_descr;
+	if (ep_data->ep_ss_valid == B_TRUE) {
+		ep_xdescr->uex_flags |= USB_EP_XFLAGS_SS_COMP;
+		ep_xdescr->uex_ep_ss = ep_data->ep_ss_comp;
+	}
+
+	return (USB_SUCCESS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/usb/hcd/xhci/THIRDPARTYLICENSE	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,24 @@
+ Copyright (c) 2014 Martin Pieuchot. All rights reserved.
+ Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
+ Copyright 2016 Joyent, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/usb/hcd/xhci/THIRDPARTYLICENSE.descrip	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,1 @@
+xhci register definitions header file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/usb/hcd/xhci/xhci.h	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,799 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _SYS_USB_XHCI_XHCI_H
+#define	_SYS_USB_XHCI_XHCI_H
+
+/*
+ * Extensible Host Controller Interface (xHCI) USB Driver
+ */
+
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/taskq_impl.h>
+#include <sys/sysmacros.h>
+#include <sys/usb/hcd/xhci/xhcireg.h>
+
+#include <sys/usb/usba.h>
+#include <sys/usb/usba/hcdi.h>
+#include <sys/usb/hubd/hub.h>
+#include <sys/usb/usba/hubdi.h>
+#include <sys/usb/hubd/hubdvar.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The base segment for DMA attributes was determined to be 4k based on xHCI 1.1
+ * / table 54: Data Structure Max Size, Boundary, and Alignment Requirement
+ * Summary.  This indicates that the required alignment for most things is
+ * PAGESIZE, which in our current implementation is required to be 4K. We
+ * provide the ring segment value below for the things which need 64K alignment
+ *
+ * Similarly, in the same table, the maximum required alignment is 64 bytes,
+ * hence we use that for everything.
+ *
+ * Next is the scatter/gather lengths. For most of the data structures, we only
+ * want to have a single SGL entry, e.g. just a simple flat mapping. For many of
+ * our transfers, we use the same logic to simplify the implementation of the
+ * driver. However, for bulk transfers, which are the largest by far, we want to
+ * be able to leverage SGLs to give us more DMA flexibility.
+ *
+ * We can transfer up to 64K in one transfer request block (TRB) which
+ * corresponds to a single SGL entry. Each ring we create is a single page in
+ * size and will support at most 256 TRBs. We've selected to use up to 8 SGLs
+ * for these transfer cases. This allows us to put up to 512 KiB in a given
+ * transfer request and in the worst case, we can have about 30 of them
+ * outstanding. Experimentally, this has proven to be sufficient for most of the
+ * drivers that we support today.
+ */
+#define	XHCI_TRB_MAX_TRANSFER	65536
+#define	XHCI_DMA_ALIGN		64
+#define	XHCI_DEF_DMA_SGL	1
+#define	XHCI_TRANSFER_DMA_SGL	8
+#define	XHCI_MAX_TRANSFER	(XHCI_TRB_MAX_TRANSFER * XHCI_TRANSFER_DMA_SGL)
+#define	XHCI_DMA_STRUCT_SIZE	4096
+
+/*
+ * Properties and values for rerouting ehci ports to xhci.
+ */
+#define	XHCI_PROP_REROUTE_DISABLE	0
+#define	XHCI_PROP_REROUTE_DEFAULT	1
+
+/*
+ * This number is a bit made up. Truthfully, the API here isn't the most useful
+ * for what we need to define as it should really be based on the endpoint that
+ * we're interested in rather than the device as a whole.
+ *
+ * We're basically being asked how many TRBs we're willing to schedule in one
+ * go. There's no great way to come up with this number, so we basically are
+ * making up something such that we use up a good portion of a ring, but not too
+ * much of it.
+ */
+#define	XHCI_ISOC_MAX_TRB	64
+
+#ifdef	DEBUG
+#define	XHCI_DMA_SYNC(dma, flag)	VERIFY0(ddi_dma_sync( \
+					    (dma).xdb_dma_handle, 0, 0, \
+					    (flag)))
+#else
+#define	XHCI_DMA_SYNC(dma, flag)	((void) ddi_dma_sync( \
+					    (dma).xdb_dma_handle, 0, 0, \
+					    (flag)))
+#endif
+
+/*
+ * This defines a time in 2-ms ticks that is required to wait for the controller
+ * to be ready to go. Section 5.4.8 of the XHCI specification in the description
+ * of the PORTSC register indicates that the upper bound is 20 ms. Therefore the
+ * number of ticks is 10.
+ */
+#define	XHCI_POWER_GOOD	10
+
+/*
+ * Definitions to determine the default number of interrupts. Note that we only
+ * bother with a single interrupt at this time, though we've arranged the driver
+ * to make it possible to request more if, for some unlikely reason, it becomes
+ * necessary.
+ */
+#define	XHCI_NINTR	1
+
+/*
+ * Default interrupt modulation value. This enables us to have 4000 interrupts /
+ * second. This is supposed to be the default value of the controller. See xHCI
+ * 1.1 / 4.17.2 for more information.
+ */
+#define	XHCI_IMOD_DEFAULT 	0x000003F8U
+
+/*
+ * Definitions that surround the default values used in various contexts. These
+ * come from various parts of the xHCI specification. In general, see xHCI 1.1 /
+ * 4.8.2. Note that the MPS_MASK is used for ISOCH and INTR endpoints which have
+ * different sizes.
+ *
+ * The burst member is a bit more complicated. By default for USB 2 devices, it
+ * only matters for ISOCH and INTR endpoints and so we use the macros below to
+ * pull it out of the endpoint description's max packet field. For USB 3, it
+ * matters for non-control endpoints. However, it comes out of a companion
+ * description.
+ *
+ * By default the mult member is zero for all cases except for super speed
+ * ISOCH endpoints, where it comes from the companion descriptor.
+ */
+#define	XHCI_CONTEXT_DEF_CERR		3
+#define	XHCI_CONTEXT_ISOCH_CERR		0
+#define	XHCI_CONTEXT_MPS_MASK		0x07ff
+#define	XHCI_CONTEXT_BURST_MASK		0x1800
+#define	XHCI_CONTEXT_BURST_SHIFT	11
+#define	XHCI_CONTEXT_DEF_MULT		0
+#define	XHCI_CONTEXT_DEF_MAX_ESIT	0
+#define	XHCI_CONTEXT_DEF_CTRL_ATL	8
+
+/*
+ * This number represents the number of transfers that we'll set up for a given
+ * interrupt transfer. Note that the idea here is that we'll want to allocate a
+ * certain number of transfers to basically ensure that we'll always be able to
+ * have a transfer available, even if the system is a bit caught up in trying to
+ * process it and for some reason we can't fire the interrupt. As such, we
+ * basically want to have enough available that at the fastest interval (125 us)
+ * that we have enough. So in this case we choose 8, with the assumption that we
+ * should be able to process at least one in a given millisecond. Note that this
+ * is not based in fact and is really just as much a guess and a hope.
+ *
+ * While we could then use less resources for other interrupt transfers that are
+ * slower, starting with uniform resource usage will make things a bit easier.
+ */
+#define	XHCI_INTR_IN_NTRANSFERS	8
+
+/*
+ * This number represents the number of xhci_transfer_t structures that we'll
+ * set up for a given isochronous transfer polling request. A given isochronous
+ * transfer may actually have multiple units of time associated with it. As
+ * such, we basically want to treat this like a case of classic double
+ * buffering. We have one ready to go while the other is being filled up. This
+ * will compensate for additional latency in the system. This is smaller than
+ * the Interrupt IN transfer case above as many callers may ask for multiple
+ * intervals in a single request.
+ */
+#define	XHCI_ISOC_IN_NTRANSFERS	2
+
+#define	XHCI_PERIODIC_IN_NTRANSFERS					\
+	MAX(XHCI_ISOC_IN_NTRANSFERS, XHCI_INTR_IN_NTRANSFERS)
+
+/*
+ * Mask for a route string which is a 20-bit value.
+ */
+#define	XHCI_ROUTE_MASK(x)	((x) & 0xfffff)
+
+/*
+ * This is the default tick that we use for timeouts while endpoints have
+ * outstanding, active, non-periodic transfers. We choose one second as the USBA
+ * specifies timeouts in units of seconds. Note that this is in microseconds, so
+ * it can be fed into drv_usectohz().
+ */
+#define	XHCI_TICK_TIMEOUT_US	(MICROSEC)
+
+/*
+ * Set of bits that we need one of to indicate that this port has something
+ * interesting on it.
+ */
+#define	XHCI_HUB_INTR_CHANGE_MASK	(XHCI_PS_CSC | XHCI_PS_PEC | \
+    XHCI_PS_WRC | XHCI_PS_OCC | XHCI_PS_PRC | XHCI_PS_PLC | XHCI_PS_CEC)
+
+/*
+ * These represent known issues with various xHCI controllers.
+ *
+ * 	XHCI_QUIRK_NO_MSI	MSI support on this controller is known to be
+ * 				broken.
+ *
+ * 	XHCI_QUIRK_32_ONLY	Only use 32-bit DMA addreses with this
+ * 				controller.
+ *
+ * 	XHCI_QUIRK_INTC_EHCI	This is an Intel platform which supports
+ * 				rerouting ports between EHCI and xHCI
+ * 				controllers on the platform.
+ */
+typedef enum xhci_quirk {
+	XHCI_QUIRK_NO_MSI	= 0x01,
+	XHCI_QUIRK_32_ONLY	= 0x02,
+	XHCI_QUIRK_INTC_EHCI	= 0x04
+} xhci_quirk_t;
+
+/*
+ * xHCI capability parameter flags. These are documented in xHCI 1.1 / 5.3.6.
+ */
+typedef enum xhci_cap_flags {
+	XCAP_AC64 	= 0x001,
+	XCAP_BNC	= 0x002,
+	XCAP_CSZ	= 0x004,
+	XCAP_PPC	= 0x008,
+	XCAP_PIND	= 0x010,
+	XCAP_LHRC	= 0x020,
+	XCAP_LTC	= 0x040,
+	XCAP_NSS	= 0x080,
+	XCAP_PAE	= 0x100,
+	XCAP_SPC	= 0x200,
+	XCAP_SEC	= 0x400,
+	XCAP_CFC	= 0x800
+} xchi_cap_flags_t;
+
+/*
+ * Second set of capabilities, these are documented in xHCI 1.1 / 5.3.9.
+ */
+typedef enum xhci_cap2_flags {
+	XCAP2_U3C	= 0x01,
+	XCAP2_CMC	= 0x02,
+	XCAP2_FMC	= 0x04,
+	XCAP2_CTC	= 0x08,
+	XCAP2_LEC	= 0x10,
+	XCAP2_CIC	= 0x20
+} xhci_cap2_flags_t;
+
+/*
+ * These represent and store the various capability registers that we'll need to
+ * use. In addition, we stash a few other versioning related bits here. Note
+ * that we cache more information than we might need so that we have it for
+ * debugging purposes.
+ */
+typedef struct xhci_capability {
+	uint8_t			xcap_usb_vers;
+	uint16_t		xcap_hci_vers;
+	uint32_t		xcap_pagesize;
+	uint8_t			xcap_max_slots;
+	uint16_t		xcap_max_intrs;
+	uint8_t			xcap_max_ports;
+	boolean_t		xcap_ist_micro;
+	uint8_t			xcap_ist;
+	uint16_t		xcap_max_esrt;
+	boolean_t		xcap_scratch_restore;
+	uint16_t		xcap_max_scratch;
+	uint8_t			xcap_u1_lat;
+	uint16_t		xcap_u2_lat;
+	xchi_cap_flags_t	xcap_flags;
+	uint8_t			xcap_max_psa;
+	uint16_t		xcap_xecp_off;
+	xhci_cap2_flags_t	xcap_flags2;
+	int			xcap_intr_types;
+} xhci_capability_t;
+
+/*
+ * This represents a single logical DMA allocation. For the vast majority of
+ * non-transfer cases, it only represents a single DMA buffer and not a
+ * scatter-gather list.
+ */
+typedef struct xhci_dma_buffer {
+	caddr_t			xdb_va;		/* Buffer VA */
+	size_t			xdb_len;	/* Buffer logical len */
+	ddi_acc_handle_t	xdb_acc_handle;	/* Access handle */
+	ddi_dma_handle_t	xdb_dma_handle;	/* DMA handle */
+	int			xdb_ncookies;	/* Number of actual cookies */
+	ddi_dma_cookie_t	xdb_cookies[XHCI_TRANSFER_DMA_SGL];
+} xhci_dma_buffer_t;
+
+/*
+ * This is a single transfer descriptor. It's packed to match the hardware
+ * layout.
+ */
+#pragma pack(1)
+typedef struct xhci_trb {
+	uint64_t	trb_addr;
+	uint32_t	trb_status;
+	uint32_t	trb_flags;
+} xhci_trb_t;
+#pragma pack()
+
+/*
+ * This represents a single transfer that we want to allocate and perform.
+ */
+typedef struct xhci_transfer {
+	list_node_t		xt_link;
+	hrtime_t		xt_sched_time;
+	xhci_dma_buffer_t	xt_buffer;
+	uint_t			xt_ntrbs;
+	uint_t			xt_short;
+	uint_t			xt_timeout;
+	usb_cr_t		xt_cr;
+	boolean_t		xt_data_tohost;
+	xhci_trb_t		*xt_trbs;
+	usb_isoc_pkt_descr_t	*xt_isoc;
+	usb_opaque_t		xt_usba_req;
+} xhci_transfer_t;
+
+/*
+ * This represents a ring in xHCI, upon which event, transfer, and command TRBs
+ * are scheduled.
+ */
+typedef struct xhci_ring {
+	xhci_dma_buffer_t	xr_dma;
+	uint_t			xr_ntrb;
+	xhci_trb_t		*xr_trb;
+	uint_t			xr_head;
+	uint_t			xr_tail;
+	uint8_t			xr_cycle;
+} xhci_ring_t;
+
+/*
+ * This structure is used to represent the xHCI Device Context Base Address
+ * Array. It's defined in section 6.1 of the specification and is required for
+ * the controller to start.
+ *
+ * The maximum number of slots supported is always 256, therefore we size this
+ * structure at its maximum.
+ */
+#define	XHCI_MAX_SLOTS	256
+#define	XHCI_DCBAA_SCRATCHPAD_INDEX	0
+
+typedef struct xhci_dcbaa {
+	uint64_t		*xdc_base_addrs;
+	xhci_dma_buffer_t	xdc_dma;
+} xhci_dcbaa_t;
+
+typedef struct xhci_scratchpad {
+	uint64_t		*xsp_addrs;
+	xhci_dma_buffer_t	xsp_addr_dma;
+	xhci_dma_buffer_t	*xsp_scratch_dma;
+} xhci_scratchpad_t;
+
+/*
+ * Contexts. These structures are inserted into the DCBAA above and are used for
+ * describing the state of the system. Note, that while many of these are
+ * 32-bytes in size, the xHCI specification defines that they'll be extended to
+ * 64-bytes with all the extra bytes as zeros if the CSZ flag is set in the
+ * HCCPARAMS1 register, e.g. we have the flag XCAP_CSZ set.
+ *
+ * The device context covers the slot context and 31 endpoints.
+ */
+#define	XHCI_DEVICE_CONTEXT_32	1024
+#define	XHCI_DEVICE_CONTEXT_64	2048
+#define	XHCI_NUM_ENDPOINTS	31
+#define	XHCI_DEFAULT_ENDPOINT	0
+
+#pragma pack(1)
+typedef struct xhci_slot_context {
+	uint32_t	xsc_info;
+	uint32_t	xsc_info2;
+	uint32_t	xsc_tt;
+	uint32_t	xsc_state;
+	uint32_t	xsc_reserved[4];
+} xhci_slot_context_t;
+
+typedef struct xhci_endpoint_context {
+	uint32_t	xec_info;
+	uint32_t	xec_info2;
+	uint64_t	xec_dequeue;
+	uint32_t	xec_txinfo;
+	uint32_t	xec_reserved[3];
+} xhci_endpoint_context_t;
+
+typedef struct xhci_input_context {
+	uint32_t	xic_drop_flags;
+	uint32_t	xic_add_flags;
+	uint32_t	xic_reserved[6];
+} xhci_input_context_t;
+#pragma pack()
+
+/*
+ * Definitions and structures for maintaining the event ring.
+ */
+#define	XHCI_EVENT_NSEGS	1
+
+#pragma pack(1)
+typedef struct xhci_event_segment {
+	uint64_t	xes_addr;
+	uint16_t	xes_size;
+	uint16_t	xes_rsvd0;
+	uint32_t	xes_rsvd1;
+} xhci_event_segment_t;
+#pragma pack()
+
+typedef struct xhci_event_ring {
+	xhci_event_segment_t	*xev_segs;
+	xhci_dma_buffer_t	xev_dma;
+	xhci_ring_t		xev_ring;
+} xhci_event_ring_t;
+
+typedef enum xhci_command_ring_state {
+	XHCI_COMMAND_RING_IDLE		= 0x00,
+	XHCI_COMMAND_RING_RUNNING	= 0x01,
+	XHCI_COMMAND_RING_ABORTING	= 0x02,
+	XHCI_COMMAND_RING_ABORT_DONE	= 0x03
+} xhci_command_ring_state_t;
+
+typedef struct xhci_command_ring {
+	xhci_ring_t			xcr_ring;
+	kmutex_t			xcr_lock;
+	kcondvar_t			xcr_cv;
+	list_t				xcr_commands;
+	timeout_id_t			xcr_timeout;
+	xhci_command_ring_state_t	xcr_state;
+} xhci_command_ring_t;
+
+/*
+ * Individual command states.
+ *
+ * XHCI_COMMAND_S_INIT		The command has yet to be inserted into the
+ * 				command ring.
+ *
+ * XHCI_COMMAND_S_QUEUED	The command is queued in the command ring.
+ *
+ * XHCI_COMMAND_S_RECEIVED	A command completion for this was received.
+ *
+ * XHCI_COMMAND_S_DONE		The command has been executed. Note that it may
+ * 				have been aborted.
+ *
+ * XHCI_COMMAND_S_RESET		The ring is being reset due to a fatal error and
+ * 				this command has been removed from the ring.
+ * 				This means it has been aborted, but it was not
+ * 				the cause of the abort.
+ *
+ * Note, when adding states, anything after XHCI_COMMAND_S_DONE implies that
+ * upon reaching this state, it is no longer in the ring.
+ */
+typedef enum xhci_command_state {
+	XHCI_COMMAND_S_INIT	= 0x00,
+	XHCI_COMMAND_S_QUEUED	= 0x01,
+	XHCI_COMMAND_S_RECEIVED = 0x02,
+	XHCI_COMMAND_S_DONE	= 0x03,
+	XHCI_COMMAND_S_RESET	= 0x04
+} xhci_command_state_t;
+
+/*
+ * The TRB contents here are always kept in host byte order and are transformed
+ * to little endian when actually scheduled on the ring.
+ */
+typedef struct xhci_command {
+	list_node_t		xco_link;
+	kcondvar_t		xco_cv;
+	xhci_trb_t		xco_req;
+	xhci_trb_t		xco_res;
+	xhci_command_state_t	xco_state;
+} xhci_command_t;
+
+typedef enum xhci_endpoint_state {
+	XHCI_ENDPOINT_PERIODIC		= 0x01,
+	XHCI_ENDPOINT_HALTED		= 0x02,
+	XHCI_ENDPOINT_QUIESCE		= 0x04,
+	XHCI_ENDPOINT_TIMED_OUT		= 0x08,
+	/*
+	 * This is a composite of states that we need to watch for. We don't
+	 * want to allow ourselves to set one of these flags while one of them
+	 * is currently active.
+	 */
+	XHCI_ENDPOINT_SERIALIZE		= 0x0c,
+	/*
+	 * This is a composite of states that we need to make sure that if set,
+	 * we do not schedule activity on the ring.
+	 */
+	XHCI_ENDPOINT_DONT_SCHEDULE	= 0x0e,
+	/*
+	 * This enpdoint is being torn down and should make sure it de-schedules
+	 * itself.
+	 */
+	XHCI_ENDPOINT_TEARDOWN		= 0x10
+} xhci_endpoint_state_t;
+
+/*
+ * Forwards required for the endpoint
+ */
+struct xhci_device;
+struct xhci;
+
+typedef struct xhci_endpoint {
+	struct xhci		*xep_xhci;
+	struct xhci_device	*xep_xd;
+	uint_t			xep_num;
+	uint_t			xep_type;
+	xhci_endpoint_state_t	xep_state;
+	kcondvar_t		xep_state_cv;
+	timeout_id_t		xep_timeout;
+	list_t			xep_transfers;
+	usba_pipe_handle_data_t	*xep_pipe;
+	xhci_ring_t		xep_ring;
+} xhci_endpoint_t;
+
+typedef struct xhci_device {
+	list_node_t		xd_link;
+	usb_port_t		xd_port;
+	uint8_t			xd_slot;
+	boolean_t		xd_addressed;
+	usba_device_t		*xd_usbdev;
+	xhci_dma_buffer_t	xd_ictx;
+	kmutex_t		xd_imtx;	/* Protects input contexts */
+	xhci_input_context_t	*xd_input;
+	xhci_slot_context_t	*xd_slotin;
+	xhci_endpoint_context_t	*xd_endin[XHCI_NUM_ENDPOINTS];
+	xhci_dma_buffer_t	xd_octx;
+	xhci_slot_context_t	*xd_slotout;
+	xhci_endpoint_context_t	*xd_endout[XHCI_NUM_ENDPOINTS];
+	xhci_endpoint_t		*xd_endpoints[XHCI_NUM_ENDPOINTS];
+} xhci_device_t;
+
+typedef enum xhci_periodic_state {
+	XHCI_PERIODIC_POLL_IDLE	= 0x0,
+	XHCI_PERIODIC_POLL_ACTIVE,
+	XHCI_PERIODIC_POLL_NOMEM,
+	XHCI_PERIODIC_POLL_STOPPING
+} xhci_periodic_state_t;
+
+typedef struct xhci_periodic_pipe {
+	xhci_periodic_state_t	xpp_poll_state;
+	usb_opaque_t		xpp_usb_req;
+	size_t			xpp_tsize;
+	uint_t			xpp_ntransfers;
+	xhci_transfer_t		*xpp_transfers[XHCI_PERIODIC_IN_NTRANSFERS];
+} xhci_periodic_pipe_t;
+
+typedef struct xhci_pipe {
+	list_node_t		xp_link;
+	hrtime_t		xp_opentime;
+	usba_pipe_handle_data_t	*xp_pipe;
+	xhci_endpoint_t		*xp_ep;
+	xhci_periodic_pipe_t	xp_periodic;
+} xhci_pipe_t;
+
+typedef struct xhci_usba {
+	usba_hcdi_ops_t		*xa_ops;
+	ddi_dma_attr_t		xa_dma_attr;
+	usb_dev_descr_t		xa_dev_descr;
+	usb_ss_hub_descr_t	xa_hub_descr;
+	usba_pipe_handle_data_t	*xa_intr_cb_ph;
+	usb_intr_req_t		*xa_intr_cb_req;
+	list_t			xa_devices;
+	list_t			xa_pipes;
+} xhci_usba_t;
+
+typedef enum xhci_attach_seq {
+	XHCI_ATTACH_FM		= 0x1 << 0,
+	XHCI_ATTACH_PCI_CONFIG	= 0x1 << 1,
+	XHCI_ATTACH_REGS_MAP	= 0x1 << 2,
+	XHCI_ATTACH_INTR_ALLOC	= 0x1 << 3,
+	XHCI_ATTACH_INTR_ADD	= 0x1 << 4,
+	XHCI_ATTACH_SYNCH	= 0x1 << 5,
+	XHCI_ATTACH_INTR_ENABLE	= 0x1 << 6,
+	XHCI_ATTACH_STARTED	= 0x1 << 7,
+	XHCI_ATTACH_USBA	= 0x1 << 8,
+	XHCI_ATTACH_ROOT_HUB	= 0x1 << 9
+} xhci_attach_seq_t;
+
+typedef enum xhci_state_flags {
+	XHCI_S_ERROR		= 0x1 << 0
+} xhci_state_flags_t;
+
+typedef struct xhci {
+	dev_info_t		*xhci_dip;
+	xhci_attach_seq_t	xhci_seq;
+	int			xhci_fm_caps;
+	ddi_acc_handle_t	xhci_cfg_handle;
+	uint16_t		xhci_vendor_id;
+	uint16_t		xhci_device_id;
+	caddr_t			xhci_regs_base;
+	ddi_acc_handle_t	xhci_regs_handle;
+	uint_t			xhci_regs_capoff;
+	uint_t			xhci_regs_operoff;
+	uint_t			xhci_regs_runoff;
+	uint_t			xhci_regs_dooroff;
+	xhci_capability_t	xhci_caps;
+	xhci_quirk_t		xhci_quirks;
+	ddi_intr_handle_t	xhci_intr_hdl;
+	int			xhci_intr_num;
+	int			xhci_intr_type;
+	uint_t			xhci_intr_pri;
+	int			xhci_intr_caps;
+	xhci_dcbaa_t		xhci_dcbaa;
+	xhci_scratchpad_t	xhci_scratchpad;
+	xhci_command_ring_t	xhci_command;
+	xhci_event_ring_t	xhci_event;
+	taskq_ent_t		xhci_tqe;
+	kmutex_t		xhci_lock;
+	kcondvar_t		xhci_statecv;
+	xhci_state_flags_t	xhci_state;
+	xhci_usba_t		xhci_usba;
+} xhci_t;
+
+/*
+ * The xHCI memory mapped registers come in four different categories. The
+ * offset to them is variable. These represent the given register set that we're
+ * after.
+ */
+typedef enum xhci_reg_type {
+	XHCI_R_CAP,
+	XHCI_R_OPER,
+	XHCI_R_RUN,
+	XHCI_R_DOOR
+} xhci_reg_type_t;
+
+/*
+ * Quirks related functions
+ */
+extern void xhci_quirks_populate(xhci_t *);
+extern void xhci_reroute_intel(xhci_t *);
+
+/*
+ * Interrupt related functions
+ */
+extern uint_t xhci_intr(caddr_t, caddr_t);
+extern boolean_t xhci_ddi_intr_disable(xhci_t *);
+extern boolean_t xhci_ddi_intr_enable(xhci_t *);
+extern int xhci_intr_conf(xhci_t *);
+
+/*
+ * DMA related functions
+ */
+extern int xhci_check_dma_handle(xhci_t *, xhci_dma_buffer_t *);
+extern void xhci_dma_acc_attr(xhci_t *, ddi_device_acc_attr_t *);
+extern void xhci_dma_dma_attr(xhci_t *, ddi_dma_attr_t *);
+extern void xhci_dma_scratchpad_attr(xhci_t *, ddi_dma_attr_t *);
+extern void xhci_dma_transfer_attr(xhci_t *, ddi_dma_attr_t *, uint_t);
+extern void xhci_dma_free(xhci_dma_buffer_t *);
+extern boolean_t xhci_dma_alloc(xhci_t *, xhci_dma_buffer_t *, ddi_dma_attr_t *,
+    ddi_device_acc_attr_t *, boolean_t, size_t, boolean_t);
+extern uint64_t xhci_dma_pa(xhci_dma_buffer_t *);
+
+/*
+ * DMA Transfer Ring functions
+ */
+extern xhci_transfer_t *xhci_transfer_alloc(xhci_t *, xhci_endpoint_t *, size_t,
+    int, int);
+extern void xhci_transfer_free(xhci_t *, xhci_transfer_t *);
+extern void xhci_transfer_copy(xhci_transfer_t *, void *, size_t, boolean_t);
+extern int xhci_transfer_sync(xhci_t *, xhci_transfer_t *, uint_t);
+extern void xhci_transfer_trb_fill_data(xhci_endpoint_t *, xhci_transfer_t *,
+    int, boolean_t);
+extern void xhci_transfer_calculate_isoc(xhci_device_t *, xhci_endpoint_t *,
+    uint_t, uint_t *, uint_t *);
+
+/*
+ * Context (DCBAA, Scratchpad, Slot) functions
+ */
+extern int xhci_context_init(xhci_t *);
+extern void xhci_context_fini(xhci_t *);
+extern boolean_t xhci_context_slot_output_init(xhci_t *, xhci_device_t *);
+extern void xhci_context_slot_output_fini(xhci_t *, xhci_device_t *);
+
+/*
+ * Command Ring Functions
+ */
+extern int xhci_command_ring_init(xhci_t *);
+extern void xhci_command_ring_fini(xhci_t *);
+extern boolean_t xhci_command_event_callback(xhci_t *, xhci_trb_t *trb);
+
+extern void xhci_command_init(xhci_command_t *);
+extern void xhci_command_fini(xhci_command_t *);
+
+extern int xhci_command_enable_slot(xhci_t *, uint8_t *);
+extern int xhci_command_disable_slot(xhci_t *, uint8_t);
+extern int xhci_command_set_address(xhci_t *, xhci_device_t *, boolean_t);
+extern int xhci_command_configure_endpoint(xhci_t *, xhci_device_t *);
+extern int xhci_command_evaluate_context(xhci_t *, xhci_device_t *);
+extern int xhci_command_reset_endpoint(xhci_t *, xhci_device_t *,
+    xhci_endpoint_t *);
+extern int xhci_command_set_tr_dequeue(xhci_t *, xhci_device_t *,
+    xhci_endpoint_t *);
+extern int xhci_command_stop_endpoint(xhci_t *, xhci_device_t *,
+    xhci_endpoint_t *);
+
+/*
+ * Event Ring Functions
+ */
+extern int xhci_event_init(xhci_t *);
+extern void xhci_event_fini(xhci_t *);
+extern boolean_t xhci_event_process(xhci_t *);
+
+/*
+ * General Ring functions
+ */
+extern void xhci_ring_free(xhci_ring_t *);
+extern int xhci_ring_reset(xhci_t *, xhci_ring_t *);
+extern int xhci_ring_alloc(xhci_t *, xhci_ring_t *);
+
+/*
+ * Event Ring (Consumer) oriented functions.
+ */
+extern xhci_trb_t *xhci_ring_event_advance(xhci_ring_t *);
+
+
+/*
+ * Command and Transfer Ring (Producer) oriented functions.
+ */
+extern boolean_t xhci_ring_trb_tail_valid(xhci_ring_t *, uint64_t);
+extern int xhci_ring_trb_valid_range(xhci_ring_t *, uint64_t, uint_t);
+
+extern boolean_t xhci_ring_trb_space(xhci_ring_t *, uint_t);
+extern void xhci_ring_trb_fill(xhci_ring_t *, uint_t, xhci_trb_t *, boolean_t);
+extern void xhci_ring_trb_produce(xhci_ring_t *, uint_t);
+extern boolean_t xhci_ring_trb_consumed(xhci_ring_t *, uint64_t);
+extern void xhci_ring_trb_put(xhci_ring_t *, xhci_trb_t *);
+extern void xhci_ring_skip(xhci_ring_t *);
+extern void xhci_ring_skip_transfer(xhci_ring_t *, xhci_transfer_t *);
+
+/*
+ * MMIO related functions. Note callers are responsible for checking with FM
+ * after accessing registers.
+ */
+extern int xhci_check_regs_acc(xhci_t *);
+
+extern uint8_t xhci_get8(xhci_t *, xhci_reg_type_t, uintptr_t);
+extern uint16_t xhci_get16(xhci_t *, xhci_reg_type_t, uintptr_t);
+extern uint32_t xhci_get32(xhci_t *, xhci_reg_type_t, uintptr_t);
+extern uint64_t xhci_get64(xhci_t *, xhci_reg_type_t, uintptr_t);
+
+extern void xhci_put8(xhci_t *, xhci_reg_type_t, uintptr_t, uint8_t);
+extern void xhci_put16(xhci_t *, xhci_reg_type_t, uintptr_t, uint16_t);
+extern void xhci_put32(xhci_t *, xhci_reg_type_t, uintptr_t, uint32_t);
+extern void xhci_put64(xhci_t *, xhci_reg_type_t, uintptr_t, uint64_t);
+
+/*
+ * Runtime FM related functions
+ */
+extern void xhci_fm_runtime_reset(xhci_t *);
+
+/*
+ * Endpoint related functions
+ */
+extern int xhci_endpoint_init(xhci_t *, xhci_device_t *,
+    usba_pipe_handle_data_t *);
+extern void xhci_endpoint_fini(xhci_device_t *, int);
+extern int xhci_endpoint_update_default(xhci_t *, xhci_device_t *,
+    xhci_endpoint_t *);
+
+extern int xhci_endpoint_setup_default_context(xhci_t *, xhci_device_t *,
+    xhci_endpoint_t *);
+
+extern uint_t xhci_endpoint_pipe_to_epid(usba_pipe_handle_data_t *);
+extern boolean_t xhci_endpoint_is_periodic_in(xhci_endpoint_t *);
+
+extern int xhci_endpoint_quiesce(xhci_t *, xhci_device_t *, xhci_endpoint_t *);
+extern int xhci_endpoint_schedule(xhci_t *, xhci_device_t *, xhci_endpoint_t *,
+    xhci_transfer_t *, boolean_t);
+extern int xhci_endpoint_ring(xhci_t *, xhci_device_t *, xhci_endpoint_t *);
+extern boolean_t xhci_endpoint_transfer_callback(xhci_t *, xhci_trb_t *);
+
+/*
+ * USB Framework related functions
+ */
+extern int xhci_hcd_init(xhci_t *);
+extern void xhci_hcd_fini(xhci_t *);
+
+/*
+ * Root hub related functions
+ */
+extern int xhci_root_hub_init(xhci_t *);
+extern int xhci_root_hub_fini(xhci_t *);
+extern int xhci_root_hub_ctrl_req(xhci_t *, usba_pipe_handle_data_t *,
+    usb_ctrl_req_t *);
+extern void xhci_root_hub_psc_callback(xhci_t *);
+extern int xhci_root_hub_intr_root_enable(xhci_t *, usba_pipe_handle_data_t *,
+    usb_intr_req_t *);
+extern void xhci_root_hub_intr_root_disable(xhci_t *);
+
+/*
+ * Logging functions
+ */
+extern void xhci_log(xhci_t *xhcip, const char *fmt, ...) __KPRINTFLIKE(2);
+extern void xhci_error(xhci_t *xhcip, const char *fmt, ...) __KPRINTFLIKE(2);
+
+/*
+ * Misc. data
+ */
+extern void *xhci_soft_state;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_USB_XHCI_XHCI_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/usb/hcd/xhci/xhci_ioctl.h	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,56 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _SYS_USB_XHCI_XHCI_IOCTL_H
+#define	_SYS_USB_XHCI_XHCI_IOCTL_H
+
+/*
+ * Private ioctls for the xhci driver.
+ */
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define	XHCI_IOCTL	(('x' << 24) | ('h' << 16 | ('i' << 8)))
+
+#define	XHCI_PORTSC_NPORTS	256
+#define	XHCI_IOCTL_PORTSC	(XHCI_IOCTL | 0x01)
+#define	XHCI_IOCTL_SETPLS	(XHCI_IOCTL | 0x02)
+#define	XHCI_IOCTL_CLEAR	(XHCI_IOCTL | 0x03)
+
+typedef struct xhci_ioctl_portsc {
+	uint32_t 	xhi_nports;
+	uint32_t	xhi_pad;
+	uint32_t	xhi_portsc[XHCI_PORTSC_NPORTS];
+} xhci_ioctl_portsc_t;
+
+typedef struct xhci_ioctl_setpls {
+	uint32_t	xis_port;
+	uint32_t	xis_pls;
+} xhci_ioctl_setpls_t;
+
+typedef struct xhci_ioctl_clear {
+	uint32_t	xic_port;
+	uint32_t 	xic_pad;
+} xhci_ioctl_clear_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_USB_XHCI_XHCI_IOCTL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/usb/hcd/xhci/xhcireg.h	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 2014 Martin Pieuchot. All rights reserved.
+ * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _SYS_USB_HCD_XHCI_XHCIREG_H
+#define	_SYS_USB_HCD_XHCI_XHCIREG_H
+
+/*
+ * xHCI Register and Field Definitions
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * xHCI PCI config registers
+ */
+#define	PCI_XHCI_CBMEM		0x10	/* configuration base MEM */
+#define	PCI_XHCI_USBREV		0x60	/* RO USB protocol revision */
+#define	PCI_USB_REV_3_0		0x30	/* USB 3.0 */
+#define	PCI_XHCI_FLADJ		0x61	/* RW frame length adjust */
+
+#define	PCI_XHCI_INTEL_XUSB2PR	0xD0	/* Intel USB2 Port Routing */
+#define	PCI_XHCI_INTEL_USB2PRM	0xD4	/* Intel USB2 Port Routing Mask */
+#define	PCI_XHCI_INTEL_USB3_PSSEN 0xD8	/* Intel USB3 Port SuperSpeed Enable */
+#define	PCI_XHCI_INTEL_USB3PRM	0xDC	/* Intel USB3 Port Routing Mask */
+
+/*
+ * xHCI capability registers
+ */
+#define	XHCI_CAPLENGTH		0x00	/* RO capability */
+#define	XHCI_RESERVED		0x01	/* Reserved */
+#define	XHCI_HCIVERSION		0x02	/* RO Interface version number */
+#define	XHCI_HCIVERSION_0_9	0x0090	/* xHCI version 0.9 */
+#define	XHCI_HCIVERSION_1_0	0x0100	/* xHCI version 1.0 */
+
+/*
+ * Structural Parameters 1 - xHCI 1.1 / 5.3.3
+ */
+#define	XHCI_HCSPARAMS1		0x04
+#define	XHCI_HCS1_DEVSLOT_MAX(x)	((x) & 0xFF)
+#define	XHCI_HCS1_IRQ_MAX(x)	(((x) >> 8) & 0x3FF)
+#define	XHCI_HCS1_N_PORTS(x)	(((x) >> 24) & 0xFF)
+
+/*
+ * Structural Parameters 2 - xHCI 1.1 / 5.3.4
+ */
+#define	XHCI_HCSPARAMS2		0x08
+#define	XHCI_HCS2_IST(x)	((x) & 0x7)
+#define	XHCI_HCS2_IST_MICRO(x)	(!((x) & 0x8))
+#define	XHCI_HCS2_ERST_MAX(x)	(((x) >> 4) & 0xF)
+#define	XHCI_HCS2_SPR(x)	(((x) >> 24) & 0x1)
+#define	XHCI_HCS2_SPB_MAX(x)	((((x) >> 16) & 0x3e0) | (((x) >> 27) & 0x1f))
+
+/*
+ * Structural Parameters 3 - xHCI 1.1 / 5.3.5
+ */
+#define	XHCI_HCSPARAMS3		0x0C
+#define	XHCI_HCS3_U1_DEL(x)	((x) & 0xFF)
+#define	XHCI_HCS3_U2_DEL(x)	(((x) >> 16) & 0xFFFF)
+
+/*
+ * Capability Parameters 1 - xHCI 1.1 / 5.3.6
+ */
+#define	XHCI_HCCPARAMS1		0x10
+#define	XHCI_HCC1_FLAGS_MASK(x)	((x) & 0x7FF)
+#define	XHCI_HCC1_PSA_SZ_MAX(x)	(((x) >> 12) & 0xF)
+#define	XHCI_HCC1_XECP(x)	(((x) >> 16) & 0xFFFF)
+
+/*
+ * Capability Parameters 1 - xHCI 1.1 / 5.3.9
+ */
+#define	XHCI_HCCPARAMS2		0x1C
+#define	XHCI_HCC2_FLAGS_MASK(x)	((x) & 0x3F)
+
+#define	XHCI_DBOFF		0x14	/* RO doorbell offset */
+#define	XHCI_RTSOFF		0x18	/* RO runtime register space offset */
+
+/*
+ * xHCI operational registers.
+ * Offset given by XHCI_CAPLENGTH register
+ */
+#define	XHCI_USBCMD		0x00		/* XHCI command */
+#define	XHCI_CMD_RS		0x00000001	/* RW Run/Stop */
+#define	XHCI_CMD_HCRST		0x00000002	/* RW HC Reset */
+#define	XHCI_CMD_INTE		0x00000004	/* RW Interrupter Enable */
+#define	XHCI_CMD_HSEE		0x00000008	/* RW System Error Enable */
+#define	XHCI_CMD_LHCRST		0x00000080	/* RW Light HC Reset */
+#define	XHCI_CMD_CSS		0x00000100	/* RW Controller Save */
+#define	XHCI_CMD_CRS		0x00000200	/* RW Controller Restore */
+#define	XHCI_CMD_EWE		0x00000400	/* RW Enable Wrap Event */
+#define	XHCI_CMD_EU3S		0x00000800	/* RW Enable U3 MFINDEX Stop */
+
+
+#define	XHCI_USBSTS		0x04		/* XHCI status */
+#define	XHCI_STS_HCH		0x00000001	/* RO - HC Halted */
+#define	XHCI_STS_HSE		0x00000004	/* RW - Host System Error */
+#define	XHCI_STS_EINT		0x00000008	/* RW - Event Interrupt */
+#define	XHCI_STS_PCD		0x00000010	/* RW - Port Change Detect */
+#define	XHCI_STS_SSS		0x00000100	/* RO - Save State Status */
+#define	XHCI_STS_RSS		0x00000200	/* RO - Restore State Status */
+#define	XHCI_STS_SRE		0x00000400	/* RW - Save/Restore Error */
+#define	XHCI_STS_CNR		0x00000800	/* RO - Controller Not Ready */
+#define	XHCI_STS_HCE		0x00001000	/* RO - HC Error */
+
+#define	XHCI_PAGESIZE		0x08		/* XHCI page size mask */
+#define	XHCI_PAGESIZE_4K	0x00000001	/* 4K Page Size */
+#define	XHCI_PAGESIZE_8K	0x00000002	/* 8K Page Size */
+#define	XHCI_PAGESIZE_16K	0x00000004	/* 16K Page Size */
+#define	XHCI_PAGESIZE_32K	0x00000008	/* 32K Page Size */
+#define	XHCI_PAGESIZE_64K	0x00000010	/* 64K Page Size */
+
+#define	XHCI_DNCTRL		0x14	/* XHCI device notification control */
+#define	XHCI_DNCTRL_MASK(n)	(1U << (n))
+
+#define	XHCI_CRCR		0x18		/* XHCI command ring control */
+#define	XHCI_CRCR_RCS		0x00000001	/* RW - consumer cycle state */
+#define	XHCI_CRCR_CS		0x00000002	/* RW - command stop */
+#define	XHCI_CRCR_CA		0x00000004	/* RW - command abort */
+#define	XHCI_CRCR_CRR		0x00000008	/* RW - command ring running */
+#define	XHCI_CRCR_MASK		0x0000000F
+
+/*
+ * Device context base address pointer register.
+ */
+#define	XHCI_DCBAAP		0x30
+
+#define	XHCI_CONFIG		0x38
+#define	XHCI_CONFIG_SLOTS_MASK	0x000000FF
+
+/*
+ * xHCI Port Status Registers and bits. See xHCI 1.1 / 5.4.8.
+ */
+#define	XHCI_PORTSC(n)		(0x3F0 + (0x10 * (n)))	/* XHCI port status */
+#define	XHCI_PS_CCS	0x00000001	/* RO - current connect status */
+#define	XHCI_PS_PED	0x00000002	/* RW - port enabled / disabled */
+#define	XHCI_PS_OCA	0x00000008	/* RO - over current active */
+#define	XHCI_PS_PR	0x00000010	/* RW - port reset */
+#define	XHCI_PS_PLS_GET(x)	(((x) >> 5) & 0xF) /* RW - port link state */
+#define	XHCI_PS_PLS_SET(x)	(((x) & 0xF) << 5) /* RW - port link state */
+#define	XHCI_PS_PP	0x00000200	/* RW - port power */
+#define	XHCI_PS_SPEED_GET(x)	(((x) >> 10) & 0xF) /* RO - port speed */
+#define	XHCI_PS_PIC_GET(x)	(((x) >> 14) & 0x3) /* RW - port indicator */
+#define	XHCI_PS_PIC_SET(x)	(((x) & 0x3) << 14) /* RW - port indicator */
+#define	XHCI_PS_LWS	0x00010000	/* RW - port link state write strobe */
+#define	XHCI_PS_CSC	0x00020000	/* RW - connect status change */
+#define	XHCI_PS_PEC	0x00040000	/* RW - port enable/disable change */
+#define	XHCI_PS_WRC	0x00080000	/* RW - warm port reset change */
+#define	XHCI_PS_OCC	0x00100000	/* RW - over-current change */
+#define	XHCI_PS_PRC	0x00200000	/* RW - port reset change */
+#define	XHCI_PS_PLC	0x00400000	/* RW - port link state change */
+#define	XHCI_PS_CEC	0x00800000	/* RW - config error change */
+#define	XHCI_PS_CAS	0x01000000	/* RO - cold attach status */
+#define	XHCI_PS_WCE	0x02000000	/* RW - wake on connect enable */
+#define	XHCI_PS_WDE	0x04000000	/* RW - wake on disconnect enable */
+#define	XHCI_PS_WOE	0x08000000	/* RW - wake on over-current enable */
+#define	XHCI_PS_DR	0x40000000	/* RO - device removable */
+#define	XHCI_PS_WPR	0x80000000U	/* RW - warm port reset */
+#define	XHCI_PS_CLEAR	0x80FF01FFU	/* command bits */
+#define	XHCI_PS_INDPORT(x)	((x) & 0xFF)
+#define	XHCI_PS_INDVAL(x)	(((x) & 0xFF00) >> 8)
+
+/*
+ * xHCI Port Power Management and Control Register. See xHCI 1.1 / 5.4.9.
+ */
+#define	XHCI_PORTPMSC(n)	(0x3F4 + (0x10 * (n)))
+#define	XHCI_PM3_U1TO_GET(x)	(((x) >> 0) & 0xFF)	/* RW - U1 timeout */
+#define	XHCI_PM3_U1TO_SET(x)	(((x) & 0xFF) << 0)	/* RW - U1 timeout */
+#define	XHCI_PM3_U2TO_GET(x)	(((x) >> 8) & 0xFF)	/* RW - U2 timeout */
+#define	XHCI_PM3_U2TO_SET(x)	(((x) & 0xFF) << 8)	/* RW - U2 timeout */
+#define	XHCI_PM3_FLA		0x00010000	/* RW - Force Link PM Accept */
+#define	XHCI_PM2_L1S_GET(x)	(((x) >> 0) & 0x7)	/* RO - L1 status */
+#define	XHCI_PM2_RWE		0x00000008	/* RW - remote wakup enable */
+/* RW - host initiated resume durations */
+#define	XHCI_PM2_HIRD_GET(x)	(((x) >> 4) & 0xF)
+#define	XHCI_PM2_HIRD_SET(x)	(((x) & 0xF) << 4)
+#define	XHCI_PM2_L1SLOT_GET(x)	(((x) >> 8) & 0xFF) /* RW - L1 device slot */
+#define	XHCI_PM2_L1SLOT_SET(x)	(((x) & 0xFF) << 8) /* RW - L1 device slot */
+#define	XHCI_PM2_HLE		0x00010000	/* RW - hardware LPM enable */
+#define	XHCI_PORTLI(n)		(0x3F8 + (0x10 * (n))) /* RO - port link info */
+#define	XHCI_PLI3_ERR_GET(x)	(((x) >> 0) & 0xFFFF) /* RO - port link errs */
+#define	XHCI_PORTRSV(n)		(0x3FC + (0x10 * (n)))	/* XHCI port reserved */
+
+/*
+ * xHCI runtime registers - xHCI 1.1 / 5.5.
+ * Offset given by XHCI_CAPLENGTH + XHCI_RTSOFF registers.
+ */
+#define	XHCI_MFINDEX		0x0000		/* RO - microframe index */
+#define	XHCI_MFINDEX_GET(x)	((x) & 0x3FFF)
+#define	XHCI_IMAN(n)		(0x0020 + (0x20 * (n)))	/* XHCI interrupt */
+							/* management */
+#define	XHCI_IMAN_INTR_PEND	0x00000001	/* RW - interrupt pending */
+#define	XHCI_IMAN_INTR_ENA	0x00000002	/* RW - interrupt enable */
+
+/*
+ * XHCI Interrupt moderation
+ */
+#define	XHCI_IMOD(n)		(0x0024 + (0x20 * (n)))
+
+/*
+ * XHCI event ring segment table size
+ */
+#define	XHCI_ERSTSZ(n)		(0x0028 + (0x20 * (n)))
+#define	XHCI_ERSTS_MASK		0xffff
+#define	XHCI_ERSTS_SET(x)	((x) & XHCI_ERSTS_MASK)
+
+/*
+ * XHCI event ring segment table BA
+ */
+#define	XHCI_ERSTBA(n)		(0x0030 + (0x20 * (n)))
+
+/*
+ * XHCI event ring dequeue pointer
+ */
+#define	XHCI_ERDP(n)		(0x0038 + (0x20 * (n)))
+#define	XHCI_ERDP_SINDEX(x)	((x) & 0x7)	/* RO - dequeue segment index */
+#define	XHCI_ERDP_BUSY		0x00000008	/* RW - event handler busy */
+
+/*
+ * XHCI doorbell registers - xHCI 1.1 / 5.6.
+ * Offset given by XHCI_CAPLENGTH + XHCI_DBOFF registers
+ */
+#define	XHCI_DOORBELL(n)	(0x0000 + (4 * (n)))
+#define	XHCI_DB_TARGET_GET(x)	((x) & 0xFF)
+#define	XHCI_DB_TARGET_SET(x)	((x) & 0xFF)
+#define	XHCI_DB_SID_GET(x)	(((x) >> 16) & 0xFFFF)
+#define	XHCI_DB_SID_SET(x)	(((x) & 0xFFFF) << 16)
+
+/*
+ * XHCI capability IDs - xHCI 1.1 / 7 - Table 146
+ */
+#define	XHCI_ID_XECP_DONE	0x0000
+#define	XHCI_ID_USB_LEGACY	0x0001
+#define	XHCI_ID_PROTOCOLS	0x0002
+#define	XHCI_ID_POWER_MGMT	0x0003
+#define	XHCI_ID_VIRTUALIZATION	0x0004
+#define	XHCI_ID_MSG_IRQ		0x0005
+#define	XHCI_ID_USB_LOCAL_MEM	0x0006
+#define	XHCI_ID_DEBUG		0x000A
+#define	XHCI_ID_EXT_MSG_IRQ	0x0011
+
+#define	XHCI_XECP_ID(x)		((x) & 0xFF)
+#define	XHCI_XECP_NEXT(x)	(((x) >> 8) & 0xFF)
+
+/*
+ * xHCI USB Legacy Support Capability - xHCI 1.1 / 7.1.
+ */
+#define	XHCI_BIOS_OWNED		(1 << 16)
+#define	XHCI_OS_OWNED		(1 << 24)
+
+/*
+ * These definitions manipulate the generation of SMIs. Note that the contents
+ * of reserved registers are required to be preserved. In addition, Several of
+ * the bits require you to write one to clear.
+ */
+#define	XHCI_XECP_LEGCTLSTS	0x04
+#define	XHCI_XECP_SMI_MASK	(0x7 << 1) + (0xff << 5) + (0x7UL << 17)
+#define	XHCI_XECP_CLEAR_SMI	(0x7UL << 29)
+
+/*
+ * xHCI Supported Protocol Capability. See xHCI 1.1 / 7.2.
+ */
+#define	XHCI_XECP_PROT_MAJOR(x)		((x >> 24) & 0xff)
+#define	XHCI_XECP_PROT_MINOR(x)		((x >> 16) & 0xff)
+#define	XHCI_XECP_PROT_PCOUNT(x)	((x >> 8) & 0xff)
+
+/*
+ * xHCI Slot Context definitions - xHCI 1.1 / 6.2.2.
+ */
+#define	XHCI_SCTX_GET_ROUTE(x)		((x) & 0xfffff)
+#define	XHCI_SCTX_SET_ROUTE(x)		((x) & 0xfffff)
+#define	XHCI_SCTX_GET_SPEED(x)		(((x) >> 20) & 0xf)
+#define	XHCI_SCTX_SET_SPEED(x)		(((x) & 0xf) << 20)
+#define	XHCI_SCTX_GET_MTT(x)		(((x) >> 25) & 0x1)
+#define	XHCI_SCTX_SET_MTT(x)		(((x) & 0x1) << 25)
+#define	XHCI_SCTX_GET_HUB(x)		(((x) >> 26) & 0x1)
+#define	XHCI_SCTX_SET_HUB(x)		(((x) & 0x1) << 26)
+#define	XHCI_SCTX_GET_DCI(x)		(((x) >> 27) & 0x1f)
+#define	XHCI_SCTX_SET_DCI(x)		(((x) & 0x1f) << 27)
+#define	XHCI_SCTX_DCI_MASK		(0x1fUL << 27)
+
+#define	XHCI_SCTX_GET_MAX_EL(x)		((x) & 0xffff)
+#define	XHCI_SCTX_SET_MAX_EL(x)		((x) & 0xffff)
+#define	XHCI_SCTX_GET_RHPORT(x)		(((x) >> 16) & 0xff)
+#define	XHCI_SCTX_SET_RHPORT(x)		(((x) & 0xff) << 16)
+#define	XHCI_SCTX_GET_NPORTS(x)		(((x) >> 24) & 0xff)
+#define	XHCI_SCTX_SET_NPORTS(x)		(((x) & 0xff) << 24)
+
+#define	XHCI_SCTX_GET_TT_HUB_SID(x)	((x) & 0xff)
+#define	XHCI_SCTX_SET_TT_HUB_SID(x)	((x) & 0xff)
+#define	XHCI_SCTX_GET_TT_PORT_NUM(x)	(((x) >> 8) & 0xff)
+#define	XHCI_SCTX_SET_TT_PORT_NUM(x)	(((x) & 0xff) << 8)
+#define	XHCI_SCTX_GET_TT_THINK_TIME(x)	(((x) >> 16) & 0x3)
+#define	XHCI_SCTX_SET_TT_THINK_TIME(x)	(((x) & 0x3) << 16)
+#define	XHCI_SCTX_SET_IRQ_TARGET(x)	(((x) & 0x3ff) << 22)
+#define	XHCI_SCTX_GET_IRQ_TARGET(x)	(((x) >> 22) & 0x3ff)
+
+#define	XHCI_SCTX_GET_DEV_ADDR(x)	((x) & 0xff)
+#define	XHCI_SCTX_GET_SLOT_STATE(x)	(((x) >> 27) & 0x1f)
+
+#define	XHCI_SLOT_DIS_ENAB	0
+#define	XHCI_SLOT_DEFAULT	1
+#define	XHCI_SLOT_ADDRESSED	2
+#define	XHCI_SLOT_CONFIGURED	3
+
+/*
+ * xHCI Slot Context definitions - xHCI 1.1 / 6.2.3.
+ */
+#define	XHCI_EPCTX_STATE(x)		((x) & 0x7)
+#define	XHCI_EP_DISABLED	0x0
+#define	XHCI_EP_RUNNING		0x1
+#define	XHCI_EP_HALTED		0x2
+#define	XHCI_EP_STOPPED		0x3
+#define	XHCI_EP_ERROR		0x4
+#define	XHCI_EPCTX_SET_MULT(x)		(((x) & 0x3) << 8)
+#define	XHCI_EPCTX_GET_MULT(x)		(((x) >> 8) & 0x3)
+#define	XHCI_EPCTX_SET_MAXP_STREAMS(x)	(((x) & 0x1F) << 10)
+#define	XHCI_EPCTX_GET_MAXP_STREAMS(x)	(((x) >> 10) & 0x1F)
+#define	XHCI_EPCTX_SET_LSA(x)		(((x) & 0x1) << 15)
+#define	XHCI_EPCTX_GET_LSA(x)		(((x) >> 15) & 0x1)
+#define	XHCI_EPCTX_SET_IVAL(x)		(((x) & 0xff) << 16)
+#define	XHCI_EPCTX_GET_IVAL(x)		(((x) >> 16) & 0xFF)
+#define	XHCI_EPCTX_GET_MAX_ESIT_HI(x)	((((x) >> 24) & 0xFF) << 16)
+#define	XHCI_EPCTX_SET_MAX_ESIT_HI(x)	((((x) >> 16) & 0xFF) << 24)
+
+#define	XHCI_EPCTX_GET_CERR(x)		(((x) >> 1) & 0x3)
+#define	XHCI_EPCTX_SET_CERR(x)		(((x) & 0x3) << 1)
+#define	XHCI_EPCTX_SET_EPTYPE(x)	(((x) & 0x7) << 3)
+#define	XHCI_EPCTX_GET_EPTYPE(x)	(((x) >> 3) & 0x7)
+#define	XHCI_EPCTX_SET_HID(x)		(((x) & 0x1) << 7)
+#define	XHCI_EPCTX_GET_HID(x)		(((x) >> 7) & 0x1)
+#define	XHCI_EPCTX_SET_MAXB(x)		(((x) & 0xff) << 8)
+#define	XHCI_EPCTX_GET_MAXB(x)		(((x) >> 8) & 0xff)
+#define	XHCI_EPCTX_SET_MPS(x)		(((x) & 0xffff) << 16)
+#define	XHCI_EPCTX_GET_MPS(x)		(((x) >> 16) & 0xffff)
+#define	XHCI_SPEED_FULL		1
+#define	XHCI_SPEED_LOW		2
+#define	XHCI_SPEED_HIGH		3
+#define	XHCI_SPEED_SUPER	4
+
+#define	XHCI_EPCTX_TYPE_ISOCH_OUT	(1)
+#define	XHCI_EPCTX_TYPE_BULK_OUT	(2)
+#define	XHCI_EPCTX_TYPE_INTR_OUT	(3)
+#define	XHCI_EPCTX_TYPE_CTRL		(4)
+#define	XHCI_EPCTX_TYPE_ISOCH_IN	(5)
+#define	XHCI_EPCTX_TYPE_BULK_IN		(6)
+#define	XHCI_EPCTX_TYPE_INTR_IN		(7)
+
+#define	XHCI_EPCTX_AVG_TRB_LEN(x)		((x) & 0xffff)
+#define	XHCI_EPCTX_MAX_ESIT_PAYLOAD(x)		(((x) & 0xffff) << 16)
+#define	XHCI_EPCTX_GET_MAX_ESIT_PAYLOAD(x)	(((x) >> 16) & 0xffff)
+
+#define	XHCI_INCTX_MASK_DCI(n)	(0x1 << (n))
+
+/*
+ * Transfer Request Block definitions.
+ */
+#define	XHCI_TRB_TYPE_MASK	0xfc00
+#define	XHCI_TRB_TYPE(x)	(((x) & XHCI_TRB_TYPE_MASK) >> 10)
+#define	XHCI_TRB_PORTID(x)	(((x) & (0xffUL << 24)) >> 24)	/* Port ID */
+#define	XHCI_TRB_MAXSIZE	(64 * 1024)
+
+#define	XHCI_TRB_GET_CODE(x)	(((x) >> 24) & 0xff) /* Get TRB code */
+#define	XHCI_TRB_TDREM(x)	(((x) & 0x1f) << 17) /* Set TD remaining len. */
+#define	XHCI_TRB_GET_TDREM(x)	(((x) >> 17) & 0x1f) /* Get TD remaining len. */
+#define	XHCI_TRB_REMAIN(x)	((x) & 0xffffff)	/* Remaining length */
+#define	XHCI_TRB_LEN(x)		((x) & 0x1ffff)		/* Transfer length */
+#define	XHCI_TRB_INTR(x)	(((x) & 0x3ff) << 22) /* Set MSI-X target */
+#define	XHCI_TRB_GET_INTR(x)	(((x) >> 22) & 0x3ff) /* Get MSI-X target */
+
+/*
+ * TRB flags that are used between different different TRB types.
+ */
+#define	XHCI_TRB_CYCLE		(1 << 0) 	/* Enqueue point of xfer ring */
+#define	XHCI_TRB_ENT		(1 << 1)	/* Evaluate next TRB */
+#define	XHCI_TRB_LINKSEG	XHCI_TRB_ENT	/* Link to next segment */
+#define	XHCI_TRB_ISP		(1 << 2)	/* Interrupt on short packet */
+#define	XHCI_TRB_NOSNOOP	(1 << 3)	/* PCIe no snoop */
+#define	XHCI_TRB_CHAIN		(1 << 4)	/* Chained with next TRB */
+#define	XHCI_TRB_IOC		(1 << 5)	/* Interrupt On Completion */
+#define	XHCI_TRB_IDT		(1 << 6)	/* Immediate Data */
+#define	XHCI_TRB_GET_TBC(x)	(((x) >> 7) & 0x3)	/* Get/Set Transfer */
+#define	XHCI_TRB_SET_TBC(x)	(((x) & 0x3) << 7)	/* Burst Count */
+#define	XHCI_TRB_BSR		(1 << 9)	/* Block Set Address */
+#define	XHCI_TRB_DCEP		(1 << 9)	/* Deconfigure endpoint */
+#define	XHCI_TRB_TSP		(1 << 9)	/* Transfer State Preserve */
+#define	XHCI_TRB_BEI		(1 << 9)	/* Block Event Interrupt */
+#define	XHCI_TRB_DIR_IN		(1 << 16)
+#define	XHCI_TRB_TRT_OUT	(2 << 16)
+#define	XHCI_TRB_TRT_IN		(3 << 16)
+#define	XHCI_TRB_GET_CYCLE(x)	((x) & 0x1)
+#define	XHCI_TRB_GET_ED(x)	(((x) >> 2) & 0x1)
+#define	XHCI_TRB_GET_FLAGS(x)	((x) & 0x1ff)
+#define	XHCI_TRB_GET_TYPE(x)	(((x) >> 10) & 0x3f)
+#define	XHCI_TRB_GET_EP(x)	(((x) >> 16) & 0x1f)
+#define	XHCI_TRB_SET_EP(x)	(((x) & 0x1f) << 16)
+#define	XHCI_TRB_GET_STYPE(x)	(((x) >> 16) & 0x1f)
+#define	XHCI_TRB_SET_STYPE(x)	(((x) & 0x1f) << 16)
+#define	XHCI_TRB_GET_SLOT(x)	(((x) >> 24) & 0xff)
+#define	XHCI_TRB_SET_SLOT(x)	(((x) & 0xff) << 24)
+
+/*
+ * Isochronous specific fields. See xHCI 1.1 / 6.4.1.3.
+ */
+#define	XHCI_TRB_GET_TLBPC(x)	(((x) >> 16) & 0xf)
+#define	XHCI_TRB_SET_TLBPC(x)	(((x) & 0xf) << 16)
+#define	XHCI_TRB_GET_FRAME(x)	(((x) >> 20) & 0x7ff)
+#define	XHCI_TRB_SET_FRAME(x)	(((x) & 0x7ff) << 20)
+#define	XHCI_TRB_SIA		(1UL << 31)		/* Start Isoch ASAP */
+
+/*
+ * TRB Types. See xHCI 1.1 / 6.4.6.
+ */
+
+/* Transfer Ring Types */
+#define	XHCI_TRB_TYPE_NORMAL	(1 << 10)
+#define	XHCI_TRB_TYPE_SETUP	(2 << 10)
+#define	XHCI_TRB_TYPE_DATA	(3 << 10)
+#define	XHCI_TRB_TYPE_STATUS	(4 << 10)
+#define	XHCI_TRB_TYPE_ISOCH	(5 << 10)
+#define	XHCI_TRB_TYPE_LINK	(6 << 10)
+#define	XHCI_TRB_TYPE_EVENT	(7 << 10)
+#define	XHCI_TRB_TYPE_NOOP	(8 << 10)
+
+/* Command ring Types */
+#define	XHCI_CMD_ENABLE_SLOT	(9 << 10)
+#define	XHCI_CMD_DISABLE_SLOT	(10 << 10)
+#define	XHCI_CMD_ADDRESS_DEVICE	(11 << 10)
+#define	XHCI_CMD_CONFIG_EP	(12 << 10)
+#define	XHCI_CMD_EVAL_CTX	(13 << 10)
+#define	XHCI_CMD_RESET_EP	(14 << 10)
+#define	XHCI_CMD_STOP_EP	(15 << 10)
+#define	XHCI_CMD_SET_TR_DEQ	(16 << 10)
+#define	XHCI_CMD_RESET_DEV	(17 << 10)
+#define	XHCI_CMD_FEVENT		(18 << 10)
+#define	XHCI_CMD_NEG_BW		(19 << 10)
+#define	XHCI_CMD_SET_LT  	(20 << 10)
+#define	XHCI_CMD_GET_BW		(21 << 10)
+#define	XHCI_CMD_FHEADER	(22 << 10)
+#define	XHCI_CMD_NOOP		(23 << 10)
+
+/* Event ring Types */
+#define	XHCI_EVT_XFER		(32 << 10)
+#define	XHCI_EVT_CMD_COMPLETE	(33 << 10)
+#define	XHCI_EVT_PORT_CHANGE	(34 << 10)
+#define	XHCI_EVT_BW_REQUEST	(35 << 10)
+#define	XHCI_EVT_DOORBELL	(36 << 10)
+#define	XHCI_EVT_HOST_CTRL	(37 << 10)
+#define	XHCI_EVT_DEVICE_NOTIFY	(38 << 10)
+#define	XHCI_EVT_MFINDEX_WRAP	(39 << 10)
+
+#define	XHCI_RING_TYPE_SHIFT(x)	((x) << 10)
+
+/*
+ * TRB Completion Codes. See xHCI 1.1 / 6.4.5.
+ */
+#define	XHCI_CODE_INVALID	 0	/* Producer didn't update the code. */
+#define	XHCI_CODE_SUCCESS	 1	/* Badaboum, plaf, plouf, yeepee! */
+#define	XHCI_CODE_DATA_BUF	 2	/* Overrun or underrun */
+#define	XHCI_CODE_BABBLE	 3	/* Device is "babbling" */
+#define	XHCI_CODE_TXERR		 4	/* USB Transaction error */
+#define	XHCI_CODE_TRB		 5	/* Invalid TRB  */
+#define	XHCI_CODE_STALL		 6	/* Stall condition */
+#define	XHCI_CODE_RESOURCE	 7	/* No resource available for the cmd */
+#define	XHCI_CODE_BANDWIDTH	 8	/* Not enough bandwidth  for the cmd */
+#define	XHCI_CODE_NO_SLOTS	 9	/* MaxSlots limit reached */
+#define	XHCI_CODE_STREAM_TYPE	10	/* Stream Context Type value detected */
+#define	XHCI_CODE_SLOT_NOT_ON	11	/* Related device slot is disabled */
+#define	XHCI_CODE_ENDP_NOT_ON	12	/* Related enpoint is disabled */
+#define	XHCI_CODE_SHORT_XFER	13	/* Short packet */
+#define	XHCI_CODE_RING_UNDERRUN	14	/* Empty ring when transmitting isoc */
+#define	XHCI_CODE_RING_OVERRUN	15	/* Empty ring when receiving isoc */
+#define	XHCI_CODE_VF_RING_FULL	16	/* VF's event ring is full */
+#define	XHCI_CODE_PARAMETER	17	/* Context parameter is invalid */
+#define	XHCI_CODE_BW_OVERRUN	18 	/* TD exceeds the bandwidth */
+#define	XHCI_CODE_CONTEXT_STATE	19	/* Transition from illegal ctx state */
+#define	XHCI_CODE_NO_PING_RESP	20	/* Unable to complete periodic xfer */
+#define	XHCI_CODE_EV_RING_FULL	21	/* Unable to post an evt to the ring */
+#define	XHCI_CODE_INCOMPAT_DEV	22	/* Device cannot be accessed */
+#define	XHCI_CODE_MISSED_SRV	23	/* Unable to service isoc EP in ESIT */
+#define	XHCI_CODE_CMD_RING_STOP	24 	/* Command Stop (CS) requested */
+#define	XHCI_CODE_CMD_ABORTED	25 	/* Command Abort (CA) operation */
+#define	XHCI_CODE_XFER_STOPPED	26 	/* xfer terminated by a stop endpoint */
+#define	XHCI_CODE_XFER_STOPINV	27 	/* TRB transfer length invalid */
+#define	XHCI_CODE_XFER_STOPSHORT	28 	/* Stopped before end of TD */
+#define	XHCI_CODE_MELAT		29	/* Max Exit Latency too large */
+#define	XHCI_CODE_RESERVED	30
+#define	XHCI_CODE_ISOC_OVERRUN	31	/* IN data buffer < Max ESIT Payload */
+#define	XHCI_CODE_EVENT_LOST	32 	/* Internal overrun - impl. specific */
+#define	XHCI_CODE_UNDEFINED	33 	/* Fatal error - impl. specific */
+#define	XHCI_CODE_INVALID_SID	34 	/* Invalid stream ID received */
+#define	XHCI_CODE_SEC_BW	35 	/* Cannot alloc secondary BW Domain */
+#define	XHCI_CODE_SPLITERR	36 	/* USB2 split transaction */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_USB_HCD_XHCI_XHCIREG_H */
--- a/usr/src/uts/i86pc/io/consplat.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/i86pc/io/consplat.c	Fri Mar 10 18:57:44 2017 -0500
@@ -53,12 +53,14 @@
 extern int pseudo_isa;
 
 int
-plat_use_polled_debug() {
+plat_use_polled_debug()
+{
 	return (0);
 }
 
 int
-plat_support_serial_kbd_and_ms() {
+plat_support_serial_kbd_and_ms()
+{
 	return (0);
 }
 
@@ -119,6 +121,7 @@
 			boot_console = CONS_TTY;
 			tty_num = cons[3] - 'a';
 		} else if (strcmp(cons, "usb-serial") == 0) {
+			(void) i_ddi_attach_hw_nodes("xhci");
 			(void) i_ddi_attach_hw_nodes("ehci");
 			(void) i_ddi_attach_hw_nodes("uhci");
 			(void) i_ddi_attach_hw_nodes("ohci");
--- a/usr/src/uts/i86pc/io/immu.c	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/i86pc/io/immu.c	Fri Mar 10 18:57:44 2017 -0500
@@ -254,9 +254,13 @@
 	immu_devi_t *immu_devi;
 
 
+	/*
+	 * It's not clear if xHCI really needs these quirks; however, to be on
+	 * the safe side until we know for certain we add it to the list below.
+	 */
 	if (drv == NULL ||
 	    (strcmp(drv, "uhci") != 0 && strcmp(drv, "ohci") != 0 &&
-	    strcmp(drv, "ehci") != 0)) {
+	    strcmp(drv, "ehci") != 0 && strcmp(drv, "xhci") != 0)) {
 		return;
 	}
 
--- a/usr/src/uts/intel/Makefile.intel	Fri Mar 10 17:02:49 2017 +0200
+++ b/usr/src/uts/intel/Makefile.intel	Fri Mar 10 18:57:44 2017 -0500
@@ -428,6 +428,7 @@
 DRV_KMODS	+= usbvc
 DRV_KMODS	+= usbftdi
 DRV_KMODS	+= usbecm
+DRV_KMODS	+= xhci
 
 #
 #	USBGEM modules
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/xhci/Makefile	Fri Mar 10 18:57:44 2017 -0500
@@ -0,0 +1,42 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2016 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+MODULE		= xhci
+OBJECTS		= $(XHCI_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/io/usb/hcd/xhci
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET	= $(BINARY) $(CONFMOD)
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+LDFLAGS		+= -dy -Nmisc/usba
+
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ