Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 16 Apr 2018 16:19:31 +0000 (UTC)
From:      Edward Tomasz Napierala <trasz@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r332598 - stable/11/sys/dev/usb
Message-ID:  <201804161619.w3GGJVhg093869@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: trasz
Date: Mon Apr 16 16:19:31 2018
New Revision: 332598
URL: https://svnweb.freebsd.org/changeset/base/332598

Log:
  MFC r328589:
  
  Make the handler routine for the hw.usb.template sysctl trigger the USB
  host to reprobe the bus by switching the USB pull up resistors off and
  back on.  In other words - when FreeBSD is configured as a USB device,
  changing the sysctl will be immediately noticed by the machine it's
  connected to.
  
  Relnotes:	yes
  Sponsored by:	The FreeBSD Foundation

Modified:
  stable/11/sys/dev/usb/usb_device.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/dev/usb/usb_device.c
==============================================================================
--- stable/11/sys/dev/usb/usb_device.c	Mon Apr 16 16:16:24 2018	(r332597)
+++ stable/11/sys/dev/usb/usb_device.c	Mon Apr 16 16:19:31 2018	(r332598)
@@ -85,6 +85,7 @@
 
 /* function prototypes  */
 
+static int	sysctl_hw_usb_template(SYSCTL_HANDLER_ARGS);
 static void	usb_init_endpoint(struct usb_device *, uint8_t,
 		    struct usb_endpoint_descriptor *,
 		    struct usb_endpoint_ss_comp_descriptor *,
@@ -118,8 +119,137 @@ int	usb_template = USB_TEMPLATE;
 int	usb_template;
 #endif
 
-SYSCTL_INT(_hw_usb, OID_AUTO, template, CTLFLAG_RWTUN,
-    &usb_template, 0, "Selected USB device side template");
+SYSCTL_PROC(_hw_usb, OID_AUTO, template,
+    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
+    NULL, 0, sysctl_hw_usb_template,
+    "I", "Selected USB device side template");
+
+/*------------------------------------------------------------------------*
+ *	usb_trigger_reprobe_on_off
+ *
+ * This function sets the pull up resistors for all ports currently
+ * operating in device mode either on (when on_not_off is 1), or off
+ * (when it's 0).
+ *------------------------------------------------------------------------*/
+static void
+usb_trigger_reprobe_on_off(int on_not_off)
+{
+	struct usb_port_status ps;
+	struct usb_bus *bus;
+	struct usb_device *udev;
+	usb_error_t err;
+	int do_unlock, max;
+
+	max = devclass_get_maxunit(usb_devclass_ptr);
+	while (max >= 0) {
+		mtx_lock(&usb_ref_lock);
+		bus = devclass_get_softc(usb_devclass_ptr, max);
+		max--;
+
+		if (bus == NULL || bus->devices == NULL ||
+		    bus->devices[USB_ROOT_HUB_ADDR] == NULL) {
+			mtx_unlock(&usb_ref_lock);
+			continue;
+		}
+
+		udev = bus->devices[USB_ROOT_HUB_ADDR];
+
+		if (udev->refcount == USB_DEV_REF_MAX) {
+			mtx_unlock(&usb_ref_lock);
+			continue;
+		}
+
+		udev->refcount++;
+		mtx_unlock(&usb_ref_lock);
+
+		do_unlock = usbd_enum_lock(udev);
+		if (do_unlock > 1) {
+			do_unlock = 0;
+			goto next;
+		}
+
+		err = usbd_req_get_port_status(udev, NULL, &ps, 1);
+		if (err != 0) {
+			DPRINTF("usbd_req_get_port_status() "
+			    "failed: %s\n", usbd_errstr(err));
+			goto next;
+		}
+
+		if ((UGETW(ps.wPortStatus) & UPS_PORT_MODE_DEVICE) == 0)
+			goto next;
+
+		if (on_not_off) {
+			err = usbd_req_set_port_feature(udev, NULL, 1,
+			    UHF_PORT_POWER);
+			if (err != 0) {
+				DPRINTF("usbd_req_set_port_feature() "
+				    "failed: %s\n", usbd_errstr(err));
+			}
+		} else {
+			err = usbd_req_clear_port_feature(udev, NULL, 1,
+			    UHF_PORT_POWER);
+			if (err != 0) {
+				DPRINTF("usbd_req_clear_port_feature() "
+				    "failed: %s\n", usbd_errstr(err));
+			}
+		}
+
+next:
+		mtx_lock(&usb_ref_lock);
+		if (do_unlock)
+			usbd_enum_unlock(udev);
+		if (--(udev->refcount) == 0)
+			cv_broadcast(&udev->ref_cv);
+		mtx_unlock(&usb_ref_lock);
+	}
+}
+
+/*------------------------------------------------------------------------*
+ *	usb_trigger_reprobe_all
+ *
+ * This function toggles the pull up resistors for all ports currently
+ * operating in device mode, causing the host machine to reenumerate them.
+ *------------------------------------------------------------------------*/
+static void
+usb_trigger_reprobe_all(void)
+{
+
+	/*
+	 * Set the pull up resistors off for all ports in device mode.
+	 */
+	usb_trigger_reprobe_on_off(0);
+
+	/*
+	 * According to the DWC OTG spec this must be at least 3ms.
+	 */
+	usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_POWER_DOWN_TIME));
+
+	/*
+	 * Set the pull up resistors back on.
+	 */
+	usb_trigger_reprobe_on_off(1);
+}
+
+static int
+sysctl_hw_usb_template(SYSCTL_HANDLER_ARGS)
+{
+	int error, val;
+
+	val = usb_template;
+	error = sysctl_handle_int(oidp, &val, 0, req);
+	if (error != 0 || req->newptr == NULL || usb_template == val)
+		return (error);
+
+	usb_template = val;
+
+	if (usb_template < 0) {
+		usb_trigger_reprobe_on_off(0);
+	} else {
+		usb_trigger_reprobe_all();
+	}
+
+	return (0);
+}
 
 /* English is default language */
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201804161619.w3GGJVhg093869>