summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorlg150142 <none@none>2007-08-09 20:21:18 -0700
committerlg150142 <none@none>2007-08-09 20:21:18 -0700
commitffcd51f34e6cd303b9745909c4632da63426be17 (patch)
treeb7c4c290af3f3a5169aea9e56b569787bd40acc1 /usr/src
parent2e14588420ccfbaa5be20605ed2be8b9802d1d49 (diff)
downloadillumos-joyent-ffcd51f34e6cd303b9745909c4632da63426be17.tar.gz
PSARC 2007/406 USB device reset
4904724 client drivers should be able to request a port reset
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/io/usb/usba/hubdi.c504
-rw-r--r--usr/src/uts/common/io/usb/usba/usbai.c41
-rw-r--r--usr/src/uts/common/io/warlock/hid_with_usba.wlcmd2
-rw-r--r--usr/src/uts/common/io/warlock/scsa2usb_with_usba.wlcmd2
-rw-r--r--usr/src/uts/common/io/warlock/ugen_with_usba.wlcmd2
-rw-r--r--usr/src/uts/common/io/warlock/usb_ac_with_usba.wlcmd2
-rw-r--r--usr/src/uts/common/io/warlock/usb_as_with_usba.wlcmd2
-rw-r--r--usr/src/uts/common/io/warlock/usb_ia_with_usba.wlcmd4
-rw-r--r--usr/src/uts/common/io/warlock/usb_mid_with_usba.wlcmd4
-rw-r--r--usr/src/uts/common/io/warlock/usbprn_with_usba.wlcmd2
-rw-r--r--usr/src/uts/common/io/warlock/usbsacm_with_usba.wlcmd2
-rw-r--r--usr/src/uts/common/io/warlock/usbser_edge_with_usba.wlcmd2
-rw-r--r--usr/src/uts/common/io/warlock/usbser_keyspan_with_usba.wlcmd2
-rw-r--r--usr/src/uts/common/io/warlock/usbskel_with_usba.wlcmd2
-rw-r--r--usr/src/uts/common/io/warlock/usbsprl_with_usba.wlcmd2
-rw-r--r--usr/src/uts/common/io/warlock/usbvc_with_usba.wlcmd2
-rw-r--r--usr/src/uts/common/sys/usb/hubd/hubdvar.h16
-rw-r--r--usr/src/uts/common/sys/usb/usba/hubdi.h8
-rw-r--r--usr/src/uts/common/sys/usb/usbai.h69
19 files changed, 641 insertions, 29 deletions
diff --git a/usr/src/uts/common/io/usb/usba/hubdi.c b/usr/src/uts/common/io/usb/usba/hubdi.c
index 9b4722fd3e..100b3cf831 100644
--- a/usr/src/uts/common/io/usb/usba/hubdi.c
+++ b/usr/src/uts/common/io/usb/usba/hubdi.c
@@ -500,6 +500,7 @@ static void hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req);
static void hubd_exception_cb(usb_pipe_handle_t pipe,
usb_intr_req_t *req);
static void hubd_hotplug_thread(void *arg);
+static void hubd_reset_thread(void *arg);
static int hubd_create_child(dev_info_t *dip,
hubd_t *hubd,
usba_device_t *usba_device,
@@ -554,6 +555,7 @@ static int hubd_post_resume_event_cb(dev_info_t *dip);
static int hubd_cpr_suspend(hubd_t *hubd);
static void hubd_cpr_resume(dev_info_t *dip);
static int hubd_restore_state_cb(dev_info_t *dip);
+static int hubd_check_same_device(hubd_t *hubd, usb_port_t port);
static int hubd_init_power_budget(hubd_t *hubd);
@@ -1357,7 +1359,7 @@ hubd_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
/* physically zap the children we didn't find */
mutex_enter(HUBD_MUTEX(hubd));
for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
- if (hubd->h_port_state[port] & HUBD_CHILD_ZAP) {
+ if (hubd->h_port_state[port] & HUBD_CHILD_ZAP) {
/* zap the dip and usba_device structure as well */
hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
hubd->h_children_dips[port] = NULL;
@@ -1824,6 +1826,7 @@ usba_hubdi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER,
hubd->h_dev_data->dev_iblock_cookie);
cv_init(&hubd->h_cv_reset_port, NULL, CV_DRIVER, NULL);
+ cv_init(&hubd->h_cv_hotplug_dev, NULL, CV_DRIVER, NULL);
hubd->h_init_state |= HUBD_LOCKS_DONE;
@@ -2727,6 +2730,7 @@ hubd_cleanup(dev_info_t *dip, hubd_t *hubd)
if (hubd->h_init_state & HUBD_LOCKS_DONE) {
mutex_destroy(HUBD_MUTEX(hubd));
cv_destroy(&hubd->h_cv_reset_port);
+ cv_destroy(&hubd->h_cv_hotplug_dev);
}
ndi_devi_exit(dip, circ);
@@ -3831,8 +3835,20 @@ hubd_hotplug_thread(void *arg)
* start polling can immediately kick off read callback
* we need to set the h_hotplug_thread to 0 so that
* the callback is not dropped
+ *
+ * if there is device during reset, still stop polling to avoid the
+ * read callback interrupting the reset, the polling will be started
+ * in hubd_reset_thread.
*/
- hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
+ for (port = 1; port <= MAX_PORTS; port++) {
+ if (hubd->h_reset_port[port]) {
+
+ break;
+ }
+ }
+ if (port > MAX_PORTS) {
+ hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
+ }
/*
* Earlier we would set the h_hotplug_thread = 0 before
@@ -3848,6 +3864,8 @@ hubd_hotplug_thread(void *arg)
/* mark this device as idle */
(void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
+ cv_broadcast(&hubd->h_cv_hotplug_dev);
+
USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
"hubd_hotplug_thread: exit");
@@ -4035,12 +4053,35 @@ hubd_handle_port_connect(hubd_t *hubd, usb_port_t port)
if (rval == USB_SUCCESS) {
/*
+ * if the port is resetting, check if
+ * device's descriptors have changed.
+ */
+ if ((hubd->h_reset_port[port]) &&
+ (hubd_check_same_device(hubd,
+ port) != USB_SUCCESS)) {
+ retry = hubd_retry_enumerate;
+
+ break;
+ }
+
+ /*
* set the default config for
* this device
*/
hubd_setdevconfig(hubd, port);
/*
+ * if we are doing Default reset, do
+ * not post reconnect event since we
+ * don't know where reset function is
+ * called.
+ */
+ if (hubd->h_reset_port[port]) {
+
+ return (USB_SUCCESS);
+ }
+
+ /*
* indicate to the child that
* it is online again
*/
@@ -6233,7 +6274,7 @@ hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, boolean_t retry)
if (hubd->h_children_dips[port] == child_dip) {
usba_device_t *ud =
hubd->h_usba_devices[port];
- hubd->h_children_dips[port] = NULL;
+ hubd->h_children_dips[port] = NULL;
if (ud) {
mutex_exit(HUBD_MUTEX(hubd));
@@ -7220,6 +7261,17 @@ usba_hubdi_ioctl(dev_info_t *self, dev_t dev, int cmd, intptr_t arg,
return (EIO);
}
+ if (hubd->h_reset_port[port]) {
+ USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle,
+ "This port is resetting, just return");
+ mutex_exit(HUBD_MUTEX(hubd));
+ if (dcp) {
+ ndi_dc_freehdl(dcp);
+ }
+
+ return (EIO);
+ }
+
hubd_pm_busy_component(hubd, hubd->h_dip, 0);
mutex_exit(HUBD_MUTEX(hubd));
@@ -8339,3 +8391,449 @@ usba_hubdi_decr_power_budget(dev_info_t *dip, usba_device_t *child_ud)
child_ud->usb_pwr_from_hub = pwr_value;
mutex_exit(&child_ud->usb_mutex);
}
+
+/*
+ * hubd_wait_for_hotplug_exit:
+ * Waiting for the exit of the running hotplug thread or ioctl thread.
+ */
+static int
+hubd_wait_for_hotplug_exit(hubd_t *hubd)
+{
+ clock_t until = ddi_get_lbolt() + drv_usectohz(1000000);
+ int rval;
+
+ ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
+
+ if (hubd->h_hotplug_thread) {
+ USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
+ "waiting for hubd hotplug thread exit");
+ rval = cv_timedwait(&hubd->h_cv_hotplug_dev,
+ &hubd->h_mutex, until);
+
+ if ((rval <= 0) && (hubd->h_hotplug_thread)) {
+
+ return (USB_FAILURE);
+ }
+ }
+
+ return (USB_SUCCESS);
+}
+
+/*
+ * hubd_reset_thread:
+ * handles the "USB_RESET_LVL_REATTACH" reset of usb device.
+ *
+ * - delete the child (force detaching the device and its children)
+ * - reset the corresponding parent hub port
+ * - create the child (force re-attaching the device and its children)
+ */
+static void
+hubd_reset_thread(void *arg)
+{
+ hubd_reset_arg_t *hd_arg = (hubd_reset_arg_t *)arg;
+ hubd_t *hubd = hd_arg->hubd;
+ uint16_t reset_port = hd_arg->reset_port;
+ uint16_t status, change;
+ hub_power_t *hubpm;
+ dev_info_t *hdip = hubd->h_dip;
+ dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
+ dev_info_t *child_dip;
+ boolean_t online_child = B_FALSE;
+ int prh_circ, rh_circ, circ, devinst;
+ char *devname;
+
+ USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
+ "hubd_reset_thread: started, hubd_reset_port = 0x%x", reset_port);
+
+ kmem_free(arg, sizeof (hubd_reset_arg_t));
+
+ mutex_enter(HUBD_MUTEX(hubd));
+
+ child_dip = hubd->h_children_dips[reset_port];
+ ASSERT(child_dip != NULL);
+
+ devname = (char *)ddi_driver_name(child_dip);
+ devinst = ddi_get_instance(child_dip);
+
+ /* if our bus power entry point is active, quit the reset */
+ if (hubd->h_bus_pwr) {
+ USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
+ "%s%d is under bus power management, cannot be reset. "
+ "Please disconnect and reconnect this device.",
+ devname, devinst);
+
+ goto Fail;
+ }
+
+ if (hubd_wait_for_hotplug_exit(hubd) == USB_FAILURE) {
+ /* we got woken up because of a timeout */
+ USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
+ hubd->h_log_handle, "Time out when resetting the device"
+ " %s%d. Please disconnect and reconnect this device.",
+ devname, devinst);
+
+ goto Fail;
+ }
+
+ hubd->h_hotplug_thread++;
+
+ /* is this the root hub? */
+ if ((hdip == rh_dip) &&
+ (hubd->h_dev_state == USB_DEV_PWRED_DOWN)) {
+ hubpm = hubd->h_hubpm;
+
+ /* mark the root hub as full power */
+ hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
+ hubpm->hubp_time_at_full_power = ddi_get_time();
+ mutex_exit(HUBD_MUTEX(hubd));
+
+ USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
+ "hubd_reset_thread: call pm_power_has_changed");
+
+ (void) pm_power_has_changed(hdip, 0,
+ USB_DEV_OS_FULL_PWR);
+
+ mutex_enter(HUBD_MUTEX(hubd));
+ hubd->h_dev_state = USB_DEV_ONLINE;
+ }
+
+ mutex_exit(HUBD_MUTEX(hubd));
+
+ /*
+ * this ensures one reset activity per system at a time.
+ * we enter the parent PCI node to have this serialization.
+ * this also excludes ioctls and deathrow thread
+ */
+ ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
+ ndi_devi_enter(rh_dip, &rh_circ);
+
+ /* exclude other threads */
+ ndi_devi_enter(hdip, &circ);
+ mutex_enter(HUBD_MUTEX(hubd));
+
+ /*
+ * We need to make sure that the child is still online for a hotplug
+ * thread could have inserted which detached the child.
+ */
+ if (hubd->h_children_dips[reset_port]) {
+ mutex_exit(HUBD_MUTEX(hubd));
+ /* First disconnect the device */
+ hubd_post_event(hubd, reset_port, USBA_EVENT_TAG_HOT_REMOVAL);
+ mutex_enter(HUBD_MUTEX(hubd));
+
+ /* Then force detaching the device */
+ if (hubd_delete_child(hubd, reset_port, NDI_DEVI_REMOVE,
+ B_FALSE) != USB_SUCCESS) {
+ USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
+ "%s%d cannot be reset due to other applications "
+ "are using it, please first close these "
+ "applications, then disconnect and reconnect"
+ "the device.", devname, devinst);
+
+ mutex_exit(HUBD_MUTEX(hubd));
+ /* post a re-connect event */
+ hubd_post_event(hubd, reset_port,
+ USBA_EVENT_TAG_HOT_INSERTION);
+ mutex_enter(HUBD_MUTEX(hubd));
+ } else {
+ (void) hubd_determine_port_status(hubd, reset_port,
+ &status, &change, HUBD_ACK_ALL_CHANGES);
+
+ /* Reset the parent hubd port and create new child */
+ if (status & PORT_STATUS_CCS) {
+ online_child |= (hubd_handle_port_connect(hubd,
+ reset_port) == USB_SUCCESS);
+ }
+ }
+ }
+
+ /* release locks so we can do a devfs_clean */
+ mutex_exit(HUBD_MUTEX(hubd));
+
+ /* delete cached dv_node's but drop locks first */
+ ndi_devi_exit(hdip, circ);
+ ndi_devi_exit(rh_dip, rh_circ);
+ ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
+
+ (void) devfs_clean(rh_dip, NULL, 0);
+
+ /* now check if any children need onlining */
+ if (online_child) {
+ USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
+ "hubd_reset_thread: onlining children");
+
+ (void) ndi_devi_online(hubd->h_dip, 0);
+ }
+
+ mutex_enter(HUBD_MUTEX(hubd));
+
+ /* allow hotplug thread now */
+ hubd->h_hotplug_thread--;
+Fail:
+ hubd_start_polling(hubd, 0);
+
+ /* mark this device as idle */
+ (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
+
+ USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
+ "hubd_reset_thread: exit, %d", hubd->h_hotplug_thread);
+
+ hubd->h_reset_port[reset_port] = B_FALSE;
+
+ mutex_exit(HUBD_MUTEX(hubd));
+
+ ndi_rele_devi(hdip);
+}
+
+/*
+ * hubd_check_same_device:
+ * - open the default pipe of the device.
+ * - compare the old and new descriptors of the device.
+ * - close the default pipe.
+ */
+static int
+hubd_check_same_device(hubd_t *hubd, usb_port_t port)
+{
+ dev_info_t *dip = hubd->h_children_dips[port];
+ usb_pipe_handle_t ph;
+ int rval = USB_FAILURE;
+
+ ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
+
+ mutex_exit(HUBD_MUTEX(hubd));
+ /* Open the default pipe to operate the device */
+ if (usb_pipe_open(dip, NULL, NULL,
+ USB_FLAGS_SLEEP| USBA_FLAGS_PRIVILEGED,
+ &ph) == USB_SUCCESS) {
+ /*
+ * Check that if the device's descriptors are different
+ * from the values saved before the port reset.
+ */
+ rval = usb_check_same_device(dip,
+ hubd->h_log_handle, USB_LOG_L0,
+ DPRINT_MASK_ALL, USB_CHK_ALL, NULL);
+
+ usb_pipe_close(dip, ph, USB_FLAGS_SLEEP |
+ USBA_FLAGS_PRIVILEGED, NULL, NULL);
+ }
+ mutex_enter(HUBD_MUTEX(hubd));
+
+ return (rval);
+}
+
+/*
+ * usba_hubdi_reset_device
+ * Called by usb_reset_device to handle usb device reset.
+ */
+int
+usba_hubdi_reset_device(dev_info_t *dip, usb_dev_reset_lvl_t reset_level)
+{
+ hubd_t *hubd;
+ usb_port_t port = 0;
+ dev_info_t *hdip;
+ usb_pipe_state_t prev_pipe_state = 0;
+ usba_device_t *usba_device;
+ hubd_reset_arg_t *arg;
+ int i, ph_open_cnt;
+ int rval = USB_FAILURE;
+
+ if ((!dip) || usba_is_root_hub(dip)) {
+
+ return (USB_INVALID_ARGS);
+ }
+
+ if (!usb_owns_device(dip)) {
+
+ return (USB_INVALID_PERM);
+ }
+
+ if ((reset_level != USB_RESET_LVL_REATTACH) &&
+ (reset_level != USB_RESET_LVL_DEFAULT)) {
+
+ return (USB_INVALID_ARGS);
+ }
+
+ if ((hdip = ddi_get_parent(dip)) == NULL) {
+
+ return (USB_INVALID_ARGS);
+ }
+
+ if ((hubd = hubd_get_soft_state(hdip)) == NULL) {
+
+ return (USB_INVALID_ARGS);
+ }
+
+ mutex_enter(HUBD_MUTEX(hubd));
+
+ /* make sure the hub is connected before trying any kinds of reset. */
+ if ((hubd->h_dev_state == USB_DEV_DISCONNECTED) ||
+ (hubd->h_dev_state == USB_DEV_SUSPENDED)) {
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
+ "usb_reset_device: the state %d of the hub/roothub "
+ "associated to the device 0x%x is incorrect",
+ hubd->h_dev_state, dip);
+ mutex_exit(HUBD_MUTEX(hubd));
+
+ return (USB_INVALID_ARGS);
+ }
+
+ mutex_exit(HUBD_MUTEX(hubd));
+
+ port = hubd_child_dip2port(hubd, dip);
+
+ mutex_enter(HUBD_MUTEX(hubd));
+
+ if (hubd->h_reset_port[port]) {
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
+ "usb_reset_device: the corresponding port is resetting");
+ mutex_exit(HUBD_MUTEX(hubd));
+
+ return (USB_SUCCESS);
+ }
+
+ /*
+ * For Default reset, client drivers should first close all the pipes
+ * except default pipe before calling the function, also should not
+ * call the function during interrupt context.
+ */
+ if (reset_level == USB_RESET_LVL_DEFAULT) {
+ usba_device = hubd->h_usba_devices[port];
+ mutex_exit(HUBD_MUTEX(hubd));
+
+ if (servicing_interrupt()) {
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
+ "usb_reset_device: during interrput context, quit");
+
+ return (USB_INVALID_CONTEXT);
+ }
+ /* Check if all the pipes have been closed */
+ for (ph_open_cnt = 0, i = 1; i < USBA_N_ENDPOINTS; i++) {
+ if (usba_device->usb_ph_list[i].usba_ph_data) {
+ ph_open_cnt++;
+ break;
+ }
+ }
+ if (ph_open_cnt) {
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
+ "usb_reset_device: %d pipes are still open",
+ ph_open_cnt);
+
+ return (USB_BUSY);
+ }
+ mutex_enter(HUBD_MUTEX(hubd));
+ }
+
+ /* Don't perform reset while the device is detaching */
+ if (hubd->h_port_state[port] & HUBD_CHILD_DETACHING) {
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
+ "usb_reset_device: the device is detaching, "
+ "cannot be reset");
+ mutex_exit(HUBD_MUTEX(hubd));
+
+ return (USB_FAILURE);
+ }
+
+ hubd->h_reset_port[port] = B_TRUE;
+ hdip = hubd->h_dip;
+ mutex_exit(HUBD_MUTEX(hubd));
+
+ /* Don't allow hub detached during the reset */
+ ndi_hold_devi(hdip);
+
+ mutex_enter(HUBD_MUTEX(hubd));
+ hubd_pm_busy_component(hubd, hdip, 0);
+ mutex_exit(HUBD_MUTEX(hubd));
+ /* go full power */
+ (void) pm_raise_power(hdip, 0, USB_DEV_OS_FULL_PWR);
+ mutex_enter(HUBD_MUTEX(hubd));
+
+ hubd->h_hotplug_thread++;
+
+ /* stop polling if it was active */
+ if (hubd->h_ep1_ph) {
+ mutex_exit(HUBD_MUTEX(hubd));
+ (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state,
+ USB_FLAGS_SLEEP);
+ mutex_enter(HUBD_MUTEX(hubd));
+
+ if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) {
+ hubd_stop_polling(hubd);
+ }
+ }
+
+ switch (reset_level) {
+ case USB_RESET_LVL_REATTACH:
+ mutex_exit(HUBD_MUTEX(hubd));
+ arg = (hubd_reset_arg_t *)kmem_zalloc(
+ sizeof (hubd_reset_arg_t), KM_SLEEP);
+ arg->hubd = hubd;
+ arg->reset_port = port;
+ mutex_enter(HUBD_MUTEX(hubd));
+
+ if ((rval = usb_async_req(hdip, hubd_reset_thread,
+ (void *)arg, 0)) == USB_SUCCESS) {
+ hubd->h_hotplug_thread--;
+ mutex_exit(HUBD_MUTEX(hubd));
+
+ return (USB_SUCCESS);
+ } else {
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
+ "Cannot create reset thread, the device %s%d failed"
+ " to reset", ddi_driver_name(dip),
+ ddi_get_instance(dip));
+
+ kmem_free(arg, sizeof (hubd_reset_arg_t));
+ }
+
+ break;
+ case USB_RESET_LVL_DEFAULT:
+ /*
+ * Reset hub port and then recover device's address, set back
+ * device's configuration, hubd_handle_port_connect() will
+ * handle errors happened during this process.
+ */
+ if ((rval = hubd_handle_port_connect(hubd, port))
+ == USB_SUCCESS) {
+ mutex_exit(HUBD_MUTEX(hubd));
+ /* re-open the default pipe */
+ rval = usba_persistent_pipe_open(usba_device);
+ mutex_enter(HUBD_MUTEX(hubd));
+ if (rval != USB_SUCCESS) {
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA,
+ hubd->h_log_handle, "failed to reopen "
+ "default pipe after reset, disable hub"
+ "port for %s%d", ddi_driver_name(dip),
+ ddi_get_instance(dip));
+ /*
+ * Disable port to set out a hotplug thread
+ * which will handle errors.
+ */
+ (void) hubd_disable_port(hubd, port);
+ }
+ }
+
+ break;
+ default:
+
+ break;
+ }
+
+ /* allow hotplug thread now */
+ hubd->h_hotplug_thread--;
+
+ if ((hubd->h_dev_state == USB_DEV_ONLINE) && hubd->h_ep1_ph &&
+ (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) {
+ hubd_start_polling(hubd, 0);
+ }
+
+ hubd_pm_idle_component(hubd, hdip, 0);
+
+ /* Clear reset mark for the port. */
+ hubd->h_reset_port[port] = B_FALSE;
+
+ mutex_exit(HUBD_MUTEX(hubd));
+
+ ndi_rele_devi(hdip);
+
+ return (rval);
+}
diff --git a/usr/src/uts/common/io/usb/usba/usbai.c b/usr/src/uts/common/io/usb/usba/usbai.c
index b9b45e4c7e..5d27d50377 100644
--- a/usr/src/uts/common/io/usb/usba/usbai.c
+++ b/usr/src/uts/common/io/usb/usba/usbai.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,7 +18,7 @@
*
* CDDL HEADER END
*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -89,7 +88,7 @@ void
usba_usbai_initialization()
{
usbai_log_handle = usb_alloc_log_hdl(NULL, "usbai", &usbai_errlevel,
- &usbai_errmask, NULL, 0);
+ &usbai_errmask, NULL, 0);
USB_DPRINTF_L4(DPRINT_MASK_USBAI, usbai_log_handle,
"usba_usbai_initialization");
@@ -185,7 +184,7 @@ usba_clear_dprint_buf()
usba_buf_sptr = usba_debug_buf;
usba_buf_eptr = usba_debug_buf + usba_debug_buf_size;
bzero(usba_debug_buf, usba_debug_buf_size +
- USBA_DEBUG_SIZE_EXTRA_ALLOC);
+ USBA_DEBUG_SIZE_EXTRA_ALLOC);
}
}
@@ -313,7 +312,7 @@ usb_vprintf(dev_info_t *dip, int level, char *label, char *fmt, va_list ap)
bcopy(usba_print_buf, usba_buf_sptr, left);
bcopy((caddr_t)usba_print_buf + left,
- usba_debug_buf, len - left);
+ usba_debug_buf, len - left);
usba_buf_sptr = usba_debug_buf + len - left;
} else {
bcopy(usba_print_buf, usba_buf_sptr, len);
@@ -711,7 +710,7 @@ usb_is_pm_enabled(dev_info_t *dip)
hcdi = usba_hcdi_get_hcdi(root_hub_dip);
if (hcdi && hcdi->hcdi_ops->usba_hcdi_pm_support) {
rval = hcdi->hcdi_ops->
- usba_hcdi_pm_support(root_hub_dip);
+ usba_hcdi_pm_support(root_hub_dip);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_USBA,
usbai_log_handle,
@@ -914,11 +913,11 @@ usb_create_pm_components(dev_info_t *dip, uint_t *pwr_states)
} else {
/* Parse the interface power descriptor */
rval = usba_parse_if_pwr_descr(usb_cfg,
- cfg_length,
- usba_get_ifno(dip), /* interface index */
- 0, /* XXXX alt interface index */
- &ifpwr_descr,
- USBA_IF_PWR_DESCR_SIZE);
+ cfg_length,
+ usba_get_ifno(dip), /* interface index */
+ 0, /* XXXX alt interface index */
+ &ifpwr_descr,
+ USBA_IF_PWR_DESCR_SIZE);
if (rval != USBA_IF_PWR_DESCR_SIZE) {
USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle,
@@ -944,7 +943,7 @@ usb_create_pm_components(dev_info_t *dip, uint_t *pwr_states)
"%d=USB D%d State",
lvl, USB_DEV_OS_PWR2USB_PWR(lvl));
pm_comp[n_prop] = kmem_zalloc(strlen(str) + 1,
- KM_SLEEP);
+ KM_SLEEP);
(void) strcpy(pm_comp[n_prop++], str);
*pwr_states |= USB_DEV_PWRMASK(lvl);
@@ -958,7 +957,7 @@ usb_create_pm_components(dev_info_t *dip, uint_t *pwr_states)
/* now create the actual components */
rval = ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
- "pm-components", pm_comp, n_prop);
+ "pm-components", pm_comp, n_prop);
if (rval == DDI_PROP_SUCCESS) {
rval = USB_SUCCESS;
} else {
@@ -1115,7 +1114,7 @@ usb_register_hotplug_cbs(dev_info_t *dip,
mutex_enter(&usba_device->usb_mutex);
usba_device->usb_client_flags[usba_get_ifno(dip)] |=
- USBA_CLIENT_FLAG_EV_CBS;
+ USBA_CLIENT_FLAG_EV_CBS;
usba_device->usb_client_ev_cb_list->dip = dip;
mutex_exit(&usba_device->usb_mutex);
@@ -1230,7 +1229,7 @@ usb_register_event_cbs(dev_info_t *dip, usb_event_t *usb_evdata,
mutex_enter(&usba_device->usb_mutex);
usba_device->usb_client_flags[usba_get_ifno(dip)] |=
- USBA_CLIENT_FLAG_EV_CBS;
+ USBA_CLIENT_FLAG_EV_CBS;
usba_device->usb_client_ev_cb_list->dip = dip;
usba_device->usb_client_ev_cb_list->ev_data = usb_evdata;
mutex_exit(&usba_device->usb_mutex);
@@ -1280,6 +1279,12 @@ usb_unregister_event_cbs(dev_info_t *dip, usb_event_t *usb_evdata)
mutex_enter(&usba_device->usb_mutex);
usba_device->usb_client_flags[usba_get_ifno(dip)] &=
- ~USBA_CLIENT_FLAG_EV_CBS;
+ ~USBA_CLIENT_FLAG_EV_CBS;
mutex_exit(&usba_device->usb_mutex);
}
+
+int
+usb_reset_device(dev_info_t *dip, usb_dev_reset_lvl_t reset_level)
+{
+ return (usba_hubdi_reset_device(dip, reset_level));
+}
diff --git a/usr/src/uts/common/io/warlock/hid_with_usba.wlcmd b/usr/src/uts/common/io/warlock/hid_with_usba.wlcmd
index 8da05d8bb8..f4068f43b5 100644
--- a/usr/src/uts/common/io/warlock/hid_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/hid_with_usba.wlcmd
@@ -85,6 +85,7 @@ root usb_get_ep_data
root usb_register_hotplug_cbs
root usb_get_current_cfgidx
root usb_register_client
+root usb_reset_device
root usba_common_power
root usba_common_register_events
@@ -108,6 +109,7 @@ root hubd_disconnect_event_cb
root hubd_reconnect_event_cb
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_cpr_post_user_callb
root ohci_intr
diff --git a/usr/src/uts/common/io/warlock/scsa2usb_with_usba.wlcmd b/usr/src/uts/common/io/warlock/scsa2usb_with_usba.wlcmd
index 65c1da43aa..115d9934dc 100644
--- a/usr/src/uts/common/io/warlock/scsa2usb_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/scsa2usb_with_usba.wlcmd
@@ -100,6 +100,7 @@ root usb_register_hotplug_cbs
root usb_get_current_cfgidx
root usb_register_client
root usb_ugen_power
+root usb_reset_device
root usba_common_power
root usba_common_register_events
@@ -115,6 +116,7 @@ root hcdi_shared_cb_thread
root hubd_cpr_post_user_callb
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_disconnect_event_cb
root hubd_post_resume_event_cb
root hubd_pre_suspend_event_cb
diff --git a/usr/src/uts/common/io/warlock/ugen_with_usba.wlcmd b/usr/src/uts/common/io/warlock/ugen_with_usba.wlcmd
index 0299703185..b307a5c21d 100644
--- a/usr/src/uts/common/io/warlock/ugen_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/ugen_with_usba.wlcmd
@@ -74,6 +74,7 @@ root usb_get_ep_data
root usb_register_hotplug_cbs
root usb_is_pm_enabled
root usb_register_client
+root usb_reset_device
root usba_common_power
root usba_common_register_events
@@ -99,6 +100,7 @@ root hcdi_cb_thread
root hcdi_shared_cb_thread
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_cpr_post_user_callb
root hubd_restore_state_cb
root hubd_disconnect_event_cb
diff --git a/usr/src/uts/common/io/warlock/usb_ac_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usb_ac_with_usba.wlcmd
index 8db69adedc..e5441a38e2 100644
--- a/usr/src/uts/common/io/warlock/usb_ac_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/usb_ac_with_usba.wlcmd
@@ -89,6 +89,7 @@ root usb_clr_feature
root usb_register_hotplug_cbs
root usb_get_current_cfgidx
root usb_register_client
+root usb_reset_device
root hcdi_autoclearing
root hcdi_cb_thread
@@ -139,6 +140,7 @@ root hubd_post_resume_event_cb
root hubd_pre_suspend_event_cb
root hubd_reconnect_event_cb
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_cpr_post_user_callb
root ohci_intr
diff --git a/usr/src/uts/common/io/warlock/usb_as_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usb_as_with_usba.wlcmd
index 407a00ab3c..264bd51ec1 100644
--- a/usr/src/uts/common/io/warlock/usb_as_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/usb_as_with_usba.wlcmd
@@ -89,6 +89,7 @@ root usb_get_ep_data
root usb_register_hotplug_cbs
root usb_get_current_cfgidx
root usb_register_client
+root usb_reset_device
root usb_ugen_attach
root usb_ugen_close
@@ -147,6 +148,7 @@ root hubd_post_resume_event_cb
root hubd_pre_suspend_event_cb
root hubd_reconnect_event_cb
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_cpr_post_user_callb
root hubd_bus_power
diff --git a/usr/src/uts/common/io/warlock/usb_ia_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usb_ia_with_usba.wlcmd
index 6fbe635964..400af40f87 100644
--- a/usr/src/uts/common/io/warlock/usb_ia_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/usb_ia_with_usba.wlcmd
@@ -1,5 +1,5 @@
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# CDDL HEADER START
@@ -44,6 +44,7 @@ root usb_console_input_fini
root usb_console_input_init
root usb_console_read
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_cpr_post_user_callb
root usb_get_dev_descr
root usb_get_if_number
@@ -114,6 +115,7 @@ root usb_clr_feature
root usb_get_ep_data
root usb_register_hotplug_cbs
root usb_register_client
+root usb_reset_device
root usb_ugen_power
root usb_ugen_attach
root usb_ugen_close
diff --git a/usr/src/uts/common/io/warlock/usb_mid_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usb_mid_with_usba.wlcmd
index 81b5c8fbd7..c8248fc9de 100644
--- a/usr/src/uts/common/io/warlock/usb_mid_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/usb_mid_with_usba.wlcmd
@@ -1,5 +1,5 @@
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# CDDL HEADER START
@@ -44,6 +44,7 @@ root usb_console_input_fini
root usb_console_input_init
root usb_console_read
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_cpr_post_user_callb
root usb_get_dev_descr
root usb_get_if_number
@@ -114,6 +115,7 @@ root usb_clr_feature
root usb_get_ep_data
root usb_register_hotplug_cbs
root usb_register_client
+root usb_reset_device
root usb_ugen_power
root hubd_root_hub_cleanup_thread
diff --git a/usr/src/uts/common/io/warlock/usbprn_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usbprn_with_usba.wlcmd
index c3c411ace5..d3c8386557 100644
--- a/usr/src/uts/common/io/warlock/usbprn_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/usbprn_with_usba.wlcmd
@@ -87,6 +87,7 @@ root usb_rval2errno
root usb_register_hotplug_cbs
root usb_get_current_cfgidx
root usb_register_client
+root usb_reset_device
root usb_ugen_power
root hcdi_autoclearing
@@ -100,6 +101,7 @@ root hubd_post_resume_event_cb
root hubd_pre_suspend_event_cb
root hubd_reconnect_event_cb
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_cpr_post_user_callb
root hubd_root_hub_cleanup_thread
root hubd_bus_power
diff --git a/usr/src/uts/common/io/warlock/usbsacm_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usbsacm_with_usba.wlcmd
index 9aa0caae8d..124850dc53 100644
--- a/usr/src/uts/common/io/warlock/usbsacm_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/usbsacm_with_usba.wlcmd
@@ -98,6 +98,7 @@ root usb_rval2errno
root usb_register_hotplug_cbs
root usb_get_current_cfgidx
root usb_register_client
+root usb_reset_device
root usb_ugen_attach
root usb_ugen_close
@@ -121,6 +122,7 @@ root hubd_post_resume_event_cb
root hubd_pre_suspend_event_cb
root hubd_reconnect_event_cb
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_cpr_post_user_callb
root hubd_root_hub_cleanup_thread
root hubd_bus_power
diff --git a/usr/src/uts/common/io/warlock/usbser_edge_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usbser_edge_with_usba.wlcmd
index 102b36f949..50e2f88102 100644
--- a/usr/src/uts/common/io/warlock/usbser_edge_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/usbser_edge_with_usba.wlcmd
@@ -78,6 +78,7 @@ root usb_rval2errno
root usb_register_hotplug_cbs
root usb_get_current_cfgidx
root usb_register_client
+root usb_reset_device
root usb_ugen_attach
root usb_ugen_close
@@ -102,6 +103,7 @@ root hubd_post_resume_event_cb
root hubd_pre_suspend_event_cb
root hubd_reconnect_event_cb
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_cpr_post_user_callb
root hubd_root_hub_cleanup_thread
root hubd_bus_power
diff --git a/usr/src/uts/common/io/warlock/usbser_keyspan_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usbser_keyspan_with_usba.wlcmd
index a98cfe1bcb..d9bfab4102 100644
--- a/usr/src/uts/common/io/warlock/usbser_keyspan_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/usbser_keyspan_with_usba.wlcmd
@@ -99,6 +99,7 @@ root usb_rval2errno
root usb_register_hotplug_cbs
root usb_get_current_cfgidx
root usb_register_client
+root usb_reset_device
root usb_ugen_attach
root usb_ugen_close
@@ -123,6 +124,7 @@ root hubd_post_resume_event_cb
root hubd_pre_suspend_event_cb
root hubd_reconnect_event_cb
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_cpr_post_user_callb
root hubd_root_hub_cleanup_thread
root hubd_bus_power
diff --git a/usr/src/uts/common/io/warlock/usbskel_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usbskel_with_usba.wlcmd
index 26f10ed9fa..22f4007431 100644
--- a/usr/src/uts/common/io/warlock/usbskel_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/usbskel_with_usba.wlcmd
@@ -83,6 +83,7 @@ root usb_serialize_access
root usb_is_pm_enabled
root usb_get_current_cfgidx
root usb_register_client
+root usb_reset_device
root usb_ugen_attach
root usb_ugen_close
@@ -120,6 +121,7 @@ root hcdi_shared_cb_thread
root hubd_bus_power
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_restore_state_cb
root hubd_disconnect_event_cb
root hubd_post_resume_event_cb
diff --git a/usr/src/uts/common/io/warlock/usbsprl_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usbsprl_with_usba.wlcmd
index b62ad6e15c..d2cf713966 100644
--- a/usr/src/uts/common/io/warlock/usbsprl_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/usbsprl_with_usba.wlcmd
@@ -93,6 +93,7 @@ root usb_rval2errno
root usb_register_hotplug_cbs
root usb_get_current_cfgidx
root usb_register_client
+root usb_reset_device
root usb_ugen_attach
root usb_ugen_close
@@ -116,6 +117,7 @@ root hubd_post_resume_event_cb
root hubd_pre_suspend_event_cb
root hubd_reconnect_event_cb
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_cpr_post_user_callb
root hubd_root_hub_cleanup_thread
root hubd_bus_power
diff --git a/usr/src/uts/common/io/warlock/usbvc_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usbvc_with_usba.wlcmd
index f00cc5f6eb..ba95bb221f 100644
--- a/usr/src/uts/common/io/warlock/usbvc_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/usbvc_with_usba.wlcmd
@@ -88,6 +88,7 @@ root usb_rval2errno
root usb_register_hotplug_cbs
root usb_get_current_cfgidx
root usb_register_client
+root usb_reset_device
root usb_ugen_power
root usb_ugen_attach
root usb_ugen_close
@@ -112,6 +113,7 @@ root hubd_post_resume_event_cb
root hubd_pre_suspend_event_cb
root hubd_reconnect_event_cb
root hubd_hotplug_thread
+root hubd_reset_thread
root hubd_cpr_post_user_callb
root hubd_root_hub_cleanup_thread
root hubd_bus_power
diff --git a/usr/src/uts/common/sys/usb/hubd/hubdvar.h b/usr/src/uts/common/sys/usb/hubd/hubdvar.h
index 1a962ed253..ec48997495 100644
--- a/usr/src/uts/common/sys/usb/hubd/hubdvar.h
+++ b/usr/src/uts/common/sys/usb/hubd/hubdvar.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -197,10 +197,14 @@ typedef struct hubd {
/* track transitions of child on each port */
uint16_t h_port_state[MAX_PORTS + 1];
+ /* track reset state of each port */
+ boolean_t h_reset_port[MAX_PORTS + 1];
+
/* track event registration of children */
uint8_t h_child_events[MAX_PORTS + 1];
kcondvar_t h_cv_reset_port;
+ kcondvar_t h_cv_hotplug_dev;
uint_t h_intr_completion_reason;
usb_log_handle_t h_log_handle; /* for logging msgs */
@@ -273,7 +277,17 @@ typedef struct hubd_hotplug_arg {
boolean_t hotplug_during_attach;
} hubd_hotplug_arg_t;
+/*
+ * hubd reset thread argument data structure
+ */
+typedef struct hubd_reset_arg {
+ hubd_t *hubd;
+ /* The port needs to be reset */
+ uint16_t reset_port;
+} hubd_reset_arg_t;
+
_NOTE(SCHEME_PROTECTS_DATA("unshared", hubd_hotplug_arg))
+_NOTE(SCHEME_PROTECTS_DATA("unshared", hubd_reset_arg))
#define HUBD_UNIT(dev) (getminor((dev)))
#define HUBD_MUTEX(hubd) (&((hubd)->h_mutex))
diff --git a/usr/src/uts/common/sys/usb/usba/hubdi.h b/usr/src/uts/common/sys/usb/usba/hubdi.h
index eaa07d413f..74be69d668 100644
--- a/usr/src/uts/common/sys/usb/usba/hubdi.h
+++ b/usr/src/uts/common/sys/usb/usba/hubdi.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -62,6 +61,7 @@ int usba_hubdi_bind_root_hub(dev_info_t *, uchar_t *, size_t,
usb_dev_descr_t *);
int usba_hubdi_unbind_root_hub(dev_info_t *);
+int usba_hubdi_reset_device(dev_info_t *, usb_dev_reset_lvl_t);
/* power budget control routines */
void usba_hubdi_incr_power_budget(dev_info_t *, usba_device_t *);
void usba_hubdi_decr_power_budget(dev_info_t *, usba_device_t *);
diff --git a/usr/src/uts/common/sys/usb/usbai.h b/usr/src/uts/common/sys/usb/usbai.h
index 1bea82b9d1..dde5f9f1e5 100644
--- a/usr/src/uts/common/sys/usb/usbai.h
+++ b/usr/src/uts/common/sys/usb/usbai.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -2335,6 +2335,73 @@ int usb_register_hotplug_cbs(
void usb_unregister_hotplug_cbs(dev_info_t *dip);
+/*
+ * Reset_level determines the extent to which the device is reset,
+ * It has the following values:
+ *
+ * USB_RESET_LVL_REATTACH - The device is reset, the original driver is
+ * detached and a new driver attaching process
+ * is started according to the updated
+ * compatible name. This reset level applies to
+ * the firmware download with the descriptors
+ * changing, or other situations in which the
+ * device needs to be reenumerated.
+ *
+ * USB_RESET_LVL_DEFAULT - Default reset level. The device is reset, all
+ * error status is cleared, the device state
+ * machines and registers are also cleared and
+ * need to be reinitialized in the driver. The
+ * current driver remains attached. This reset
+ * level applies to hardware error recovery, or
+ * firmware download without descriptors
+ * changing.
+ */
+typedef enum {
+ USB_RESET_LVL_REATTACH = 0,
+ USB_RESET_LVL_DEFAULT = 1
+} usb_dev_reset_lvl_t;
+
+/*
+ * usb_reset_device:
+ *
+ * Client drivers call this function to request hardware reset for themselves,
+ * which may be required in some situations such as:
+ *
+ * 1) Some USB devices need the driver to upload firmware into devices' RAM
+ * and initiate a hardware reset in order to activate the new firmware.
+ * 2) Hardware reset may help drivers to recover devices from an error state
+ * caused by physical or firmware defects.
+ *
+ * Arguments:
+ * dip - pointer to devinfo of the client
+ * reset_level - see above
+ *
+ * Return values:
+ * USB_SUCCESS - With USB_RESET_LVL_DEFAULT: the device was reset
+ * successfully.
+ * - With USB_RESET_LVL_REATTACH: reenumeration was
+ * started successfully or a previous reset is still
+ * in progress.
+ * USB_FAILURE - The state of the device's parent hub is invalid
+ * (disconnected or suspended).
+ * - Called when the driver being detached.
+ * - The device failed to be reset with
+ * USB_RESET_LVL_DEFAULT specified.
+ * - Reenumeration failed to start up with
+ * - USB_RESET_LVL_REATTACH specified.
+ * USB_INVALID_ARGS - Invalid arguments.
+ * USB_INVALID_PERM - The driver of the dip doesn't own entire device.
+ * USB_BUSY - One or more pipes other than the default control
+ * pipe are open on the device with
+ * USB_RESET_LVL_DEFAULT specified.
+ * USB_INVALID_CONTEXT - Called from interrupt context with
+ * USB_RESET_LVL_DEFAULT specified.
+ */
+
+int usb_reset_device(
+ dev_info_t *dip,
+ usb_dev_reset_lvl_t reset_level);
+
/*
* ***************************************************************************