summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/dev/usb/usb_device.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-08-20 15:53:03 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-09-21 10:29:39 +0200
commit18fa92c2dcc6c52e0bf27d214d80f0c25a89b47d (patch)
treea3020ac5b1f366f2f0920941b589808e435dbcee /freebsd/sys/dev/usb/usb_device.c
parentUpdate to FreeBSD head 2017-12-01 (diff)
downloadrtems-libbsd-18fa92c2dcc6c52e0bf27d214d80f0c25a89b47d.tar.bz2
Update to FreeBSD head 2018-02-01
Git mirror commit d079ae0442af8fa3cfd6d7ede190d04e64a2c0d4. Update #3472.
Diffstat (limited to 'freebsd/sys/dev/usb/usb_device.c')
-rw-r--r--freebsd/sys/dev/usb/usb_device.c134
1 files changed, 132 insertions, 2 deletions
diff --git a/freebsd/sys/dev/usb/usb_device.c b/freebsd/sys/dev/usb/usb_device.c
index 861f6029..5f1cf409 100644
--- a/freebsd/sys/dev/usb/usb_device.c
+++ b/freebsd/sys/dev/usb/usb_device.c
@@ -89,6 +89,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 *,
@@ -123,8 +124,137 @@ int usb_template;
#endif
#ifndef __rtems__
-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);
+}
#endif /* __rtems__ */
/* English is default language */