/* * destroy PM components */ static void uftdi_destroy_pm_components(uftdi_state_t *uf) { uftdi_pm_t *pm = uf->uf_pm; dev_info_t *dip = uf->uf_dip; int rval; if (!pm) return; if (uf->uf_dev_state != USB_DEV_DISCONNECTED) { if (pm->pm_wakeup_enabled) { rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); if (rval != DDI_SUCCESS) { USB_DPRINTF_L2(DPRINT_PM, uf->uf_lh, "uftdi_destroy_pm_components: " "raising power failed, rval=%d", rval); } rval = usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_DISABLE); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_PM, uf->uf_lh, "uftdi_destroy_pm_components: disable " "remote wakeup failed, rval=%d", rval); } } (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF); } kmem_free(pm, sizeof (*pm)); uf->uf_pm = NULL; }
int usba10_usb_handle_remote_wakeup( dev_info_t *dip, int cmd) { return (usb_handle_remote_wakeup(dip, cmd)); }
/* * wusb_df_destroy_power_mgmt: * Shut down and destroy power management and remote wakeup functionality. */ static void wusb_df_destroy_power_mgmt(wusb_df_state_t *wusb_dfp) { USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "destroy_power_mgmt enter"); ASSERT(!mutex_owned(&wusb_dfp->wusb_df_mutex)); mutex_enter(&wusb_dfp->wusb_df_mutex); if (!wusb_dfp->wusb_df_pm) { mutex_exit(&wusb_dfp->wusb_df_mutex); return; } mutex_exit(&wusb_dfp->wusb_df_mutex); (void) wusb_df_pm_busy_component(wusb_dfp); mutex_enter(&wusb_dfp->wusb_df_mutex); if (wusb_dfp->wusb_df_dev_state != USB_DEV_DISCONNECTED) { if (wusb_dfp->wusb_df_pm->wusb_df_wakeup_enabled) { mutex_exit(&wusb_dfp->wusb_df_mutex); (void) pm_raise_power(wusb_dfp->wusb_df_dip, 0, USB_DEV_OS_FULL_PWR); if (usb_handle_remote_wakeup(wusb_dfp->wusb_df_dip, USB_REMOTE_WAKEUP_DISABLE) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_destroy_power_mgmt: " "Error disabling rmt wakeup"); } mutex_enter(&wusb_dfp->wusb_df_mutex); } } mutex_exit(&wusb_dfp->wusb_df_mutex); /* * Since remote wakeup is disabled now, * no one can raise power * and get to device once power is lowered here. */ (void) pm_lower_power(wusb_dfp->wusb_df_dip, 0, USB_DEV_OS_PWR_OFF); wusb_df_pm_idle_component(wusb_dfp); mutex_enter(&wusb_dfp->wusb_df_mutex); kmem_free(wusb_dfp->wusb_df_pm, sizeof (wusb_df_power_t)); wusb_dfp->wusb_df_pm = NULL; mutex_exit(&wusb_dfp->wusb_df_mutex); }
/* * wusb_df_restore_device_state: * Called during hotplug-reconnect and resume. * reenable power management * Verify the device is the same as before the disconnect/suspend. * Restore device state * Thaw any IO which was frozen. * Quiesce device. (Other routines will activate if thawed IO.) * Set device online. * Leave device disconnected if there are problems. */ static void wusb_df_restore_device_state(dev_info_t *dip, wusb_df_state_t *wusb_dfp) { USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_restore_device_state: enter"); ASSERT(mutex_owned(&wusb_dfp->wusb_df_mutex)); ASSERT((wusb_dfp->wusb_df_dev_state == USB_DEV_DISCONNECTED) || (wusb_dfp->wusb_df_dev_state == USB_DEV_SUSPENDED)); mutex_exit(&wusb_dfp->wusb_df_mutex); /* Check if we are talking to the same device */ if (usb_check_same_device(dip, wusb_dfp->wusb_df_log_hdl, USB_LOG_L0, PRINT_MASK_ALL, USB_CHK_ALL, NULL) != USB_SUCCESS) { /* change the device state from suspended to disconnected */ mutex_enter(&wusb_dfp->wusb_df_mutex); wusb_dfp->wusb_df_dev_state = USB_DEV_SUSPENDED; USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_restore_device_state: check same device failed"); return; } mutex_enter(&wusb_dfp->wusb_df_mutex); wusb_dfp->wusb_df_dev_state = USB_DEV_ONLINE; if (wusb_dfp->wusb_df_pm && wusb_dfp->wusb_df_pm->wusb_df_wakeup_enabled) { /* Failure here means device disappeared again. */ mutex_exit(&wusb_dfp->wusb_df_mutex); if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "device may or may not be accessible. " "Please verify reconnection"); } mutex_enter(&wusb_dfp->wusb_df_mutex); } USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_restore_device_state: end"); }
/* * wusb_df_init_power_mgmt: * Initialize power management and remote wakeup functionality. * No mutex is necessary in this function as it's called only by attach. */ static void wusb_df_init_power_mgmt(wusb_df_state_t *wusb_dfp) { wusb_df_power_t *wusb_dfpm; uint_t pwr_states; USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "init_power_mgmt enter"); /* * If remote wakeup is not available you may not want to do * power management. */ /* Allocate the state structure */ wusb_dfpm = kmem_zalloc(sizeof (wusb_df_power_t), KM_SLEEP); wusb_dfp->wusb_df_pm = wusb_dfpm; wusb_dfpm->wusb_df_state = wusb_dfp; wusb_dfpm->wusb_df_pm_capabilities = 0; wusb_dfpm->wusb_df_current_power = USB_DEV_OS_FULL_PWR; if (usb_create_pm_components(wusb_dfp->wusb_df_dip, &pwr_states) == USB_SUCCESS) { USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_init_power_mgmt: created PM components"); wusb_dfpm->wusb_df_pwr_states = (uint8_t)pwr_states; (void) pm_raise_power(wusb_dfp->wusb_df_dip, 0, USB_DEV_OS_FULL_PWR); if (usb_handle_remote_wakeup(wusb_dfp->wusb_df_dip, USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) { wusb_dfpm->wusb_df_wakeup_enabled = 1; } else { USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_init_power_mgmt:" "fail to enable remote wakeup"); } } else { USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_init_power_mgmt: create_pm_compts failed"); } USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_init_power_mgmt: end"); }
/* * create PM components */ static int uftdi_create_pm_components(uftdi_state_t *uf) { dev_info_t *dip = uf->uf_dip; uftdi_pm_t *pm; uint_t pwr_states; if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_PM, uf->uf_lh, "uftdi_create_pm_components: failed"); return (USB_SUCCESS); } pm = uf->uf_pm = kmem_zalloc(sizeof (*pm), KM_SLEEP); pm->pm_pwr_states = (uint8_t)pwr_states; pm->pm_cur_power = USB_DEV_OS_FULL_PWR; pm->pm_wakeup_enabled = usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS; (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); return (USB_SUCCESS); }
/*------------------------------------------------------------------------* * usb_handle_request * * Internal state sequence: * * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR * * Returns: * 0: Ready to start hardware * Else: Stall current transfer, if any *------------------------------------------------------------------------*/ static usb_error_t usb_handle_request(struct usb_xfer *xfer) { struct usb_device_request req; struct usb_device *udev; const void *src_zcopy; /* zero-copy source pointer */ const void *src_mcopy; /* non zero-copy source pointer */ uint16_t off; /* data offset */ uint16_t rem; /* data remainder */ uint16_t max_len; /* max fragment length */ uint16_t wValue; uint16_t wIndex; uint8_t state; uint8_t is_complete = 1; usb_error_t err; union { uWord wStatus; uint8_t buf[2]; } temp; /* * Filter the USB transfer state into * something which we understand: */ switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: state = USB_HR_NOT_COMPLETE; if (!xfer->flags_int.control_act) { /* nothing to do */ goto tr_stalled; } break; case USB_ST_TRANSFERRED: if (!xfer->flags_int.control_act) { state = USB_HR_COMPLETE_OK; } else { state = USB_HR_NOT_COMPLETE; } break; default: state = USB_HR_COMPLETE_ERR; break; } /* reset frame stuff */ usbd_xfer_set_frame_len(xfer, 0, 0); usbd_xfer_set_frame_offset(xfer, 0, 0); usbd_xfer_set_frame_offset(xfer, sizeof(req), 1); /* get the current request, if any */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); if (xfer->flags_int.control_rem == 0xFFFF) { /* first time - not initialised */ rem = UGETW(req.wLength); off = 0; } else { /* not first time - initialised */ rem = xfer->flags_int.control_rem; off = UGETW(req.wLength) - rem; } /* set some defaults */ max_len = 0; src_zcopy = NULL; src_mcopy = NULL; udev = xfer->xroot->udev; /* get some request fields decoded */ wValue = UGETW(req.wValue); wIndex = UGETW(req.wIndex); DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x " "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType, req.bRequest, wValue, wIndex, off, rem, state); /* demultiplex the control request */ switch (req.bmRequestType) { case UT_READ_DEVICE: if (state != USB_HR_NOT_COMPLETE) { break; } switch (req.bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_descriptor; case UR_GET_CONFIG: goto tr_handle_get_config; case UR_GET_STATUS: goto tr_handle_get_status; default: goto tr_stalled; } break; case UT_WRITE_DEVICE: switch (req.bRequest) { case UR_SET_ADDRESS: goto tr_handle_set_address; case UR_SET_CONFIG: goto tr_handle_set_config; case UR_CLEAR_FEATURE: switch (wValue) { case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_clear_wakeup; default: goto tr_stalled; } break; case UR_SET_FEATURE: switch (wValue) { case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_set_wakeup; default: goto tr_stalled; } break; default: goto tr_stalled; } break; case UT_WRITE_ENDPOINT: switch (req.bRequest) { case UR_CLEAR_FEATURE: switch (wValue) { case UF_ENDPOINT_HALT: goto tr_handle_clear_halt; default: goto tr_stalled; } break; case UR_SET_FEATURE: switch (wValue) { case UF_ENDPOINT_HALT: goto tr_handle_set_halt; default: goto tr_stalled; } break; default: goto tr_stalled; } break; case UT_READ_ENDPOINT: switch (req.bRequest) { case UR_GET_STATUS: goto tr_handle_get_ep_status; default: goto tr_stalled; } break; default: /* we use "USB_ADD_BYTES" to de-const the src_zcopy */ err = usb_handle_iface_request(xfer, USB_ADD_BYTES(&src_zcopy, 0), &max_len, req, off, state); if (err == 0) { is_complete = 0; goto tr_valid; } else if (err == USB_ERR_SHORT_XFER) { goto tr_valid; } /* * Reset zero-copy pointer and max length * variable in case they were unintentionally * set: */ src_zcopy = NULL; max_len = 0; /* * Check if we have a vendor specific * descriptor: */ goto tr_handle_get_descriptor; } goto tr_valid; tr_handle_get_descriptor: err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len); if (err) goto tr_stalled; if (src_zcopy == NULL) goto tr_stalled; goto tr_valid; tr_handle_get_config: temp.buf[0] = udev->curr_config_no; src_mcopy = temp.buf; max_len = 1; goto tr_valid; tr_handle_get_status: wValue = 0; USB_BUS_LOCK(udev->bus); if (udev->flags.remote_wakeup) { wValue |= UDS_REMOTE_WAKEUP; } if (udev->flags.self_powered) { wValue |= UDS_SELF_POWERED; } USB_BUS_UNLOCK(udev->bus); USETW(temp.wStatus, wValue); src_mcopy = temp.wStatus; max_len = sizeof(temp.wStatus); goto tr_valid; tr_handle_set_address: if (state == USB_HR_NOT_COMPLETE) { if (wValue >= 0x80) { /* invalid value */ goto tr_stalled; } else if (udev->curr_config_no != 0) { /* we are configured ! */ goto tr_stalled; } } else if (state != USB_HR_NOT_COMPLETE) { udev->address = (wValue & 0x7F); goto tr_bad_context; } goto tr_valid; tr_handle_set_config: if (state == USB_HR_NOT_COMPLETE) { if (usb_handle_set_config(xfer, req.wValue[0])) { goto tr_stalled; } } goto tr_valid; tr_handle_clear_halt: if (state == USB_HR_NOT_COMPLETE) { if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) { goto tr_stalled; } } goto tr_valid; tr_handle_clear_wakeup: if (state == USB_HR_NOT_COMPLETE) { if (usb_handle_remote_wakeup(xfer, 0)) { goto tr_stalled; } } goto tr_valid; tr_handle_set_halt: if (state == USB_HR_NOT_COMPLETE) { if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) { goto tr_stalled; } } goto tr_valid; tr_handle_set_wakeup: if (state == USB_HR_NOT_COMPLETE) { if (usb_handle_remote_wakeup(xfer, 1)) { goto tr_stalled; } } goto tr_valid; tr_handle_get_ep_status: if (state == USB_HR_NOT_COMPLETE) { temp.wStatus[0] = usb_handle_get_stall(udev, req.wIndex[0]); temp.wStatus[1] = 0; src_mcopy = temp.wStatus; max_len = sizeof(temp.wStatus); } goto tr_valid; tr_valid: if (state != USB_HR_NOT_COMPLETE) { goto tr_stalled; } /* subtract offset from length */ max_len -= off; /* Compute the real maximum data length */ if (max_len > xfer->max_data_length) { max_len = usbd_xfer_max_len(xfer); } if (max_len > rem) { max_len = rem; } /* * If the remainder is greater than the maximum data length, * we need to truncate the value for the sake of the * comparison below: */ if (rem > xfer->max_data_length) { rem = usbd_xfer_max_len(xfer); } if ((rem != max_len) && (is_complete != 0)) { /* * If we don't transfer the data we can transfer, then * the transfer is short ! */ xfer->flags.force_short_xfer = 1; xfer->nframes = 2; } else { /* * Default case */ xfer->flags.force_short_xfer = 0; xfer->nframes = max_len ? 2 : 1; } if (max_len > 0) { if (src_mcopy) { src_mcopy = USB_ADD_BYTES(src_mcopy, off); usbd_copy_in(xfer->frbuffers + 1, 0, src_mcopy, max_len); usbd_xfer_set_frame_len(xfer, 1, max_len); } else { usbd_xfer_set_frame_data(xfer, 1, USB_ADD_BYTES(src_zcopy, off), max_len); } } else { /* the end is reached, send status */ xfer->flags.manual_status = 0; usbd_xfer_set_frame_len(xfer, 1, 0); } DPRINTF("success\n"); return (0); /* success */ tr_stalled: DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ? "complete" : "stalled"); return (USB_ERR_STALLED); tr_bad_context: DPRINTF("bad context\n"); return (USB_ERR_BAD_CONTEXT); }