diff options
author | lg150142 <none@none> | 2007-08-09 20:21:18 -0700 |
---|---|---|
committer | lg150142 <none@none> | 2007-08-09 20:21:18 -0700 |
commit | ffcd51f34e6cd303b9745909c4632da63426be17 (patch) | |
tree | b7c4c290af3f3a5169aea9e56b569787bd40acc1 /usr/src | |
parent | 2e14588420ccfbaa5be20605ed2be8b9802d1d49 (diff) | |
download | illumos-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')
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); + /* * *************************************************************************** |