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 */
|
/* function prototypes */
|
||||||
|
|
||||||
|
static int sysctl_hw_usb_template(SYSCTL_HANDLER_ARGS);
|
||||||
static void usb_init_endpoint(struct usb_device *, uint8_t,
|
static void usb_init_endpoint(struct usb_device *, uint8_t,
|
||||||
struct usb_endpoint_descriptor *,
|
struct usb_endpoint_descriptor *,
|
||||||
struct usb_endpoint_ss_comp_descriptor *,
|
struct usb_endpoint_ss_comp_descriptor *,
|
||||||
@ -120,8 +121,137 @@ int usb_template = USB_TEMPLATE;
|
|||||||
int usb_template;
|
int usb_template;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SYSCTL_INT(_hw_usb, OID_AUTO, template, CTLFLAG_RWTUN,
|
SYSCTL_PROC(_hw_usb, OID_AUTO, template,
|
||||||
&usb_template, 0, "Selected USB device side 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 */
|
/* English is default language */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user