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. Reviewed by: hselasky@ MFC after: 2 weeks Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
2145d321ac
commit
f66c3cfc58
@ -87,6 +87,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 *,
|
||||
@ -120,8 +121,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 */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user