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>