/* * find the firmware module's "_start", "_end" symbols * and get the size of firmware. */ static int wusbdf_mod_loadsym(wusb_df_state_t *dfp, ddi_modhandle_t modp, char *mod, char *sym, char **start, size_t *len) { char start_sym[256]; char end_sym[256]; char *p, *end; int rv; size_t n; (void) snprintf(start_sym, sizeof (start_sym), "%s_start", sym); (void) snprintf(end_sym, sizeof (end_sym), "%s_end", sym); p = (char *)ddi_modsym(modp, start_sym, &rv); if (p == NULL || rv != 0) { USB_DPRINTF_L2(PRINT_MASK_ATTA, dfp->wusb_df_log_hdl, "mod %s: symbol %s not found\n", mod, start_sym); return (-1); } end = (char *)ddi_modsym(modp, end_sym, &rv); if (end == NULL || rv != 0) { USB_DPRINTF_L2(PRINT_MASK_ATTA, dfp->wusb_df_log_hdl, "mod %s: symbol %s not found\n", mod, end_sym); return (-1); } n = end - p; *start = p; *len = n; return (0); }
/* * 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; }
/* Download firmware into device */ static int keyspan_download_firmware(keyspan_pre_state_t *kbp) { usbser_keyspan_fw_record_t *record = NULL; /* If the firmware module exists, then download it to device. */ if (&keyspan_usa49wlc_fw) { record = keyspan_usa49wlc_fw(); } if (!record) { USB_DPRINTF_L1(DPRINT_ATTACH, kbp->kb_lh, "No firmware available for Keyspan usa49wlc" " usb-to-serial adapter. Refer to usbsksp(7D)" " for details."); return (USB_FAILURE); } /* Set bit 1 before downloading firmware. */ if (keyspan_set_reg(&kbp->kb_def_pipe, 1) != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_ATTACH, kbp->kb_lh, "keyspan_pre_attach: Set register failed."); return (USB_FAILURE); } /* Write until the last record of the firmware */ while (record->address != 0xffff) { if (keyspan_write_memory(&kbp->kb_def_pipe, record->address, (uchar_t *)record->data, record->data_len, KEYSPAN_REQ_SET) != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_ATTACH, kbp->kb_lh, "keyspan_pre_attach: download firmware failed."); return (USB_FAILURE); } record++; } /* * Set bit 0, device will be enumerated again after a while, * and then go to keyspan_attach() */ if (keyspan_set_reg(&kbp->kb_def_pipe, 0) != USB_SUCCESS) { return (USB_FAILURE); } return (USB_SUCCESS); }
/* * 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"); }
/* * start receiving data */ static int uftdi_rx_start(uftdi_state_t *uf) { usb_bulk_req_t *br; int rval; USB_DPRINTF_L4(DPRINT_OUT_PIPE, uf->uf_lh, "uftdi_rx_start"); ASSERT(mutex_owned(&uf->uf_lock)); uf->uf_bulkin_state = UFTDI_PIPE_BUSY; mutex_exit(&uf->uf_lock); br = usb_alloc_bulk_req(uf->uf_dip, uf->uf_xfer_sz, USB_FLAGS_SLEEP); br->bulk_len = uf->uf_xfer_sz; br->bulk_timeout = UFTDI_BULKIN_TIMEOUT; br->bulk_cb = uftdi_bulkin_cb; br->bulk_exc_cb = uftdi_bulkin_cb; br->bulk_client_private = (usb_opaque_t)uf; br->bulk_attributes = USB_ATTRS_AUTOCLEARING | USB_ATTRS_SHORT_XFER_OK; rval = usb_pipe_bulk_xfer(uf->uf_bulkin_ph, br, 0); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_IN_PIPE, uf->uf_lh, "uftdi_rx_start: xfer failed %d", rval); usb_free_bulk_req(br); } mutex_enter(&uf->uf_lock); if (rval != USB_SUCCESS) uf->uf_bulkin_state = UFTDI_PIPE_IDLE; return (rval); }
static int uftdi_pwrlvl3(uftdi_state_t *uf) { int rval; USB_DPRINTF_L4(DPRINT_PM, uf->uf_lh, "uftdi_pwrlvl3"); switch (uf->uf_dev_state) { case USB_DEV_PWRED_DOWN: /* Issue USB D0 command to the device here */ rval = usb_set_device_pwrlvl0(uf->uf_dip); ASSERT(rval == USB_SUCCESS); uf->uf_dev_state = USB_DEV_ONLINE; uf->uf_pm->pm_cur_power = USB_DEV_OS_FULL_PWR; /*FALLTHROUGH*/ case USB_DEV_ONLINE: /* we are already in full power */ /*FALLTHROUGH*/ case USB_DEV_DISCONNECTED: case USB_DEV_SUSPENDED: return (USB_SUCCESS); default: USB_DPRINTF_L2(DPRINT_PM, uf->uf_lh, "uftdi_pwrlvl3: illegal device state"); return (USB_FAILURE); } }
/* * Functions to handle power transition for OS levels 0 -> 3 * The same level as OS state, different from USB state */ static int uftdi_pwrlvl0(uftdi_state_t *uf) { int rval; USB_DPRINTF_L4(DPRINT_PM, uf->uf_lh, "uftdi_pwrlvl0"); switch (uf->uf_dev_state) { case USB_DEV_ONLINE: /* issue USB D3 command to the device */ rval = usb_set_device_pwrlvl3(uf->uf_dip); ASSERT(rval == USB_SUCCESS); uf->uf_dev_state = USB_DEV_PWRED_DOWN; uf->uf_pm->pm_cur_power = USB_DEV_OS_PWR_OFF; /*FALLTHROUGH*/ case USB_DEV_DISCONNECTED: case USB_DEV_SUSPENDED: /* allow a disconnect/cpr'ed device to go to lower power */ return (USB_SUCCESS); case USB_DEV_PWRED_DOWN: default: USB_DPRINTF_L2(DPRINT_PM, uf->uf_lh, "uftdi_pwrlvl0: illegal device state"); return (USB_FAILURE); } }
/* * ehci_wait_for_transfers_completion: * * Wait for processing all completed transfers and to send results * to upstream. */ static void ehci_wait_for_isoc_completion( ehci_state_t *ehcip, ehci_pipe_private_t *pp) { clock_t xfer_cmpl_time_wait; ASSERT(mutex_owned(&ehcip->ehci_int_mutex)); if (pp->pp_itw_head == NULL) { return; } /* Get the number of clock ticks to wait */ xfer_cmpl_time_wait = drv_usectohz(EHCI_XFER_CMPL_TIMEWAIT * 1000000); (void) cv_timedwait(&pp->pp_xfer_cmpl_cv, &ehcip->ehci_int_mutex, ddi_get_lbolt() + xfer_cmpl_time_wait); if (pp->pp_itw_head) { USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl, "ehci_wait_for_isoc_completion: " "No transfers completion confirmation received"); } }
static int uftdi_send_data(uftdi_state_t *uf, mblk_t *data) { usb_bulk_req_t *br; int len = MBLKL(data); int rval; USB_DPRINTF_L4(DPRINT_OUT_PIPE, uf->uf_lh, "uftdi_send_data: %d 0x%x 0x%x 0x%x", len, data->b_rptr[0], (len > 1) ? data->b_rptr[1] : 0, (len > 2) ? data->b_rptr[2] : 0); ASSERT(!mutex_owned(&uf->uf_lock)); br = usb_alloc_bulk_req(uf->uf_dip, 0, USB_FLAGS_SLEEP); br->bulk_data = data; br->bulk_len = len; br->bulk_timeout = UFTDI_BULKOUT_TIMEOUT; br->bulk_cb = uftdi_bulkout_cb; br->bulk_exc_cb = uftdi_bulkout_cb; br->bulk_client_private = (usb_opaque_t)uf; br->bulk_attributes = USB_ATTRS_AUTOCLEARING; rval = usb_pipe_bulk_xfer(uf->uf_bulkout_ph, br, 0); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_OUT_PIPE, uf->uf_lh, "uftdi_send_data: xfer failed %d", rval); br->bulk_data = NULL; usb_free_bulk_req(br); } return (rval); }
/* * 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"); }
/* * ehci_hcdi_pipe_bulk_xfer: */ int ehci_hcdi_pipe_bulk_xfer( usba_pipe_handle_data_t *ph, usb_bulk_req_t *bulk_reqp, usb_flags_t usb_flags) { ehci_state_t *ehcip = ehci_obtain_state( ph->p_usba_device->usb_root_hub_dip); ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private; int rval, error = USB_SUCCESS; ehci_trans_wrapper_t *tw; USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl, "ehci_hcdi_pipe_bulk_xfer: ph = 0x%p reqp = 0x%p flags = %x", (void *)ph, bulk_reqp, usb_flags); mutex_enter(&ehcip->ehci_int_mutex); rval = ehci_state_is_operational(ehcip); if (rval != USB_SUCCESS) { mutex_exit(&ehcip->ehci_int_mutex); return (rval); } /* * Check whether pipe is in halted state. */ if (pp->pp_state == EHCI_PIPE_STATE_ERROR) { USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl, "ehci_hcdi_pipe_bulk_xfer:" "Pipe is in error state, need pipe reset to continue"); mutex_exit(&ehcip->ehci_int_mutex); return (USB_FAILURE); } /* Allocate a transfer wrapper */ if ((tw = ehci_allocate_bulk_resources(ehcip, pp, bulk_reqp, usb_flags)) == NULL) { error = USB_NO_RESOURCES; } else { /* Add the QTD into the Host Controller's bulk list */ ehci_insert_bulk_req(ehcip, ph, bulk_reqp, tw, usb_flags); } mutex_exit(&ehcip->ehci_int_mutex); return (error); }
/* * download firmware or command by control pipe */ static int wusb_df_send_data(wusb_df_state_t *wusbp, unsigned int address, const unsigned char *buffer, unsigned int size) { int error = DDI_FAILURE; usb_ctrl_setup_t setup; usb_cb_flags_t cb_flags; usb_cr_t cr; mblk_t *data = NULL; /* data for USBA */ uint16_t data_len; /* # of bytes want to write */ uint_t cnt; /* # of xfered bytes */ setup.bmRequestType = USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_HOST_TO_DEV | USB_DEV_REQ_RCPT_DEV; setup.bRequest = 0xf0; setup.attrs = 0; for (cnt = 0; cnt < size; cnt += data_len) { data_len = min(size - cnt, 512); /* reuse previous mblk if possible */ if ((data = reallocb(data, data_len, 0)) == NULL) { return (USB_FAILURE); } bcopy(buffer + cnt, data->b_rptr, data_len); data->b_wptr += data_len; setup.wValue = (address + cnt) & 0xffff; setup.wIndex = ((address + cnt) >> 16) & 0xffff; setup.wLength = data_len; error = usb_pipe_ctrl_xfer_wait( wusbp->wusb_df_reg->dev_default_ph, &setup, &data, &cr, &cb_flags, 0); if (error != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusbp->wusb_df_log_hdl, "wusb_df_send_data: " "send failed rval=%d, cr=%d, cb=0x%x\n", error, cr, cb_flags); break; } } if (data) { freemsg(data); } return (error); }
/* * 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); }
/* * Firmware download. Program device special registers and then call * wusb_df_fw_download() to download the true data. */ static int wusb_df_firmware_download(wusb_df_state_t *wusbp) { int error = USB_FAILURE; unsigned char buf[4]; (void) memset(buf, 0, 4); /* program the device register to make it ready to accept fw */ error = wusb_df_send_data(wusbp, 0x800000c0, buf, 4); if (error != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusbp->wusb_df_log_hdl, "Fail init"); return (error); } error = wusb_df_fw_download(wusbp); if (error != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusbp->wusb_df_log_hdl, "Fail to download firmware"); return (error); } buf[0] = 0x48; buf[1] = 0x56; buf[2] = 0x2c; buf[3] = 0x00; error = wusb_df_send_data(wusbp, 0x80008060, buf, 4); if (error != USB_SUCCESS) { return (error); } (void) memset(buf, 0, 4); buf[0] = 0x18; /* firmware download finished, program the device to lock fw */ error = wusb_df_send_data(wusbp, 0x800000c0, buf, 4); return (error); }
/* * usba_handle_device_remote_wakeup: * internal function to enable/disable remote wakeup in the device * or interface */ static int usba_handle_device_remote_wakeup(dev_info_t *dip, int cmd) { int rval; uint8_t bmRequest = USB_DEV_REQ_HOST_TO_DEV; uchar_t bRequest; uint16_t wIndex = 0; usb_cr_t completion_reason = 0; usb_cb_flags_t cb_flags; usb_pipe_handle_t ph; USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle, "usba_handle_device_remote_wakeup: dip = 0x%p", (void *)dip); USBA_CHECK_CONTEXT(); /* get the default pipe */ ph = usba_get_dflt_pipe_handle(dip); /* do we own the device? */ if (usb_owns_device(dip)) { bmRequest |= USB_DEV_REQ_RCPT_DEV; } else { bmRequest |= USB_DEV_REQ_RCPT_IF; wIndex = usba_get_ifno(dip); } bRequest = ((cmd == USB_REMOTE_WAKEUP_ENABLE) ? USB_REQ_SET_FEATURE : USB_REQ_CLEAR_FEATURE); if ((rval = usb_pipe_sync_ctrl_xfer(dip, ph, bmRequest, /* bmRequest */ bRequest, /* bRequest */ USB_DEV_REMOTE_WAKEUP, /* wValue */ wIndex, /* wIndex */ 0, /* wLength */ NULL, 0, &completion_reason, &cb_flags, USB_FLAGS_SLEEP)) != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle, "Set/ClearFeature (RemoteWakep) failed: " "rval = %d, cmd = %d, cr = 0x%x cb = 0x%x", rval, cmd, completion_reason, cb_flags); } return (rval); }
/* * pipe operations */ static int uftdi_open_pipes(uftdi_state_t *uf) { int ifc, alt; usb_pipe_policy_t policy; usb_ep_data_t *in_data, *out_data; /* get ep data */ ifc = uf->uf_dev_data->dev_curr_if; alt = 0; in_data = usb_lookup_ep_data(uf->uf_dip, uf->uf_dev_data, ifc, alt, 0, USB_EP_ATTR_BULK, USB_EP_DIR_IN); out_data = usb_lookup_ep_data(uf->uf_dip, uf->uf_dev_data, ifc, alt, 0, USB_EP_ATTR_BULK, USB_EP_DIR_OUT); if (in_data == NULL || out_data == NULL) { USB_DPRINTF_L2(DPRINT_ATTACH, uf->uf_lh, "uftdi_open_pipes: can't get ep data"); return (USB_FAILURE); } /* open pipes */ policy.pp_max_async_reqs = 2; if (usb_pipe_open(uf->uf_dip, &in_data->ep_descr, &policy, USB_FLAGS_SLEEP, &uf->uf_bulkin_ph) != USB_SUCCESS) return (USB_FAILURE); if (usb_pipe_open(uf->uf_dip, &out_data->ep_descr, &policy, USB_FLAGS_SLEEP, &uf->uf_bulkout_ph) != USB_SUCCESS) { usb_pipe_close(uf->uf_dip, uf->uf_bulkin_ph, USB_FLAGS_SLEEP, NULL, NULL); return (USB_FAILURE); } mutex_enter(&uf->uf_lock); uf->uf_bulkin_state = UFTDI_PIPE_IDLE; uf->uf_bulkout_state = UFTDI_PIPE_IDLE; mutex_exit(&uf->uf_lock); return (USB_SUCCESS); }
/* * scsa2usb_bulk_only_handle_error: * handle transport errors and start recovery */ static void scsa2usb_bulk_only_handle_error(scsa2usb_state_t *scsa2usbp, usb_bulk_req_t *req) { struct scsi_pkt *pkt = scsa2usbp->scsa2usb_cur_pkt; USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "scsa2usb_bulk_only_handle_error: req = 0x%p, cr = 0x%x", (void *)req, (req ? req->bulk_completion_reason : 0)); if (req) { SCSA2USB_SET_PKT_DO_COMP_STATE(scsa2usbp); /* invoke reset recovery */ switch (req->bulk_completion_reason) { case USB_CR_STALL: if (pkt) { pkt->pkt_reason = CMD_TRAN_ERR; } break; case USB_CR_TIMEOUT: if (pkt) { pkt->pkt_reason = CMD_TIMEOUT; pkt->pkt_statistics |= STAT_TIMEOUT; } break; case USB_CR_DEV_NOT_RESP: if (pkt) { pkt->pkt_reason = CMD_DEV_GONE; /* scsi_poll relies on this */ pkt->pkt_state = STATE_GOT_BUS; } break; default: if (pkt) { pkt->pkt_reason = CMD_TRAN_ERR; } } scsa2usb_bulk_only_reset_recovery(scsa2usbp); } SCSA2USB_FREE_BULK_REQ(req); }
/* * mark device busy and raise power */ static int uftdi_pm_set_busy(uftdi_state_t *uf) { uftdi_pm_t *pm = uf->uf_pm; dev_info_t *dip = uf->uf_dip; int rval; USB_DPRINTF_L4(DPRINT_PM, uf->uf_lh, "uftdi_pm_set_busy"); if (!pm) return (USB_SUCCESS); mutex_enter(&uf->uf_lock); /* if already marked busy, just increment the counter */ if (pm->pm_busy_cnt++ > 0) { mutex_exit(&uf->uf_lock); return (USB_SUCCESS); } rval = pm_busy_component(dip, 0); ASSERT(rval == DDI_SUCCESS); if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) { mutex_exit(&uf->uf_lock); return (USB_SUCCESS); } /* need to raise power */ pm->pm_raise_power = B_TRUE; mutex_exit(&uf->uf_lock); rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); if (rval != DDI_SUCCESS) { USB_DPRINTF_L2(DPRINT_PM, uf->uf_lh, "raising power failed"); } mutex_enter(&uf->uf_lock); pm->pm_raise_power = B_FALSE; mutex_exit(&uf->uf_lock); return (USB_SUCCESS); }
/* * restore device state after CPR resume or reconnect */ static int uftdi_restore_device_state(uftdi_state_t *uf) { int state; mutex_enter(&uf->uf_lock); state = uf->uf_dev_state; mutex_exit(&uf->uf_lock); if (state != USB_DEV_DISCONNECTED && state != USB_DEV_SUSPENDED) return (state); if (usb_check_same_device(uf->uf_dip, uf->uf_lh, USB_LOG_L0, DPRINT_MASK_ALL, USB_CHK_ALL, NULL) != USB_SUCCESS) { mutex_enter(&uf->uf_lock); state = uf->uf_dev_state = USB_DEV_DISCONNECTED; mutex_exit(&uf->uf_lock); return (state); } if (state == USB_DEV_DISCONNECTED) { USB_DPRINTF_L0(DPRINT_HOTPLUG, uf->uf_lh, "Device has been reconnected but data may have been lost"); } if (uftdi_reconnect_pipes(uf) != USB_SUCCESS) return (state); /* * init device state */ mutex_enter(&uf->uf_lock); state = uf->uf_dev_state = USB_DEV_ONLINE; mutex_exit(&uf->uf_lock); if ((uftdi_restore_port_state(uf) != USB_SUCCESS)) { USB_DPRINTF_L2(DPRINT_HOTPLUG, uf->uf_lh, "uftdi_restore_device_state: failed"); } return (state); }
/* * restore ports state after CPR resume or reconnect */ static int uftdi_restore_port_state(uftdi_state_t *uf) { int rval; mutex_enter(&uf->uf_lock); if (uf->uf_port_state != UFTDI_PORT_OPEN) { mutex_exit(&uf->uf_lock); return (USB_SUCCESS); } mutex_exit(&uf->uf_lock); /* open hardware serial port, restoring old settings */ if ((rval = uftdi_open_hw_port(uf, 1)) != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_HOTPLUG, uf->uf_lh, "uftdi_restore_port_state: failed"); } return (rval); }
/* * 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); }
/* * initialize hardware serial port */ static int uftdi_open_hw_port(uftdi_state_t *uf, int dorestore) { int rval; /* * Perform a full reset on the device */ rval = uftdi_cmd_vendor_write0(uf, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, uf->uf_hwport); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_DEF_PIPE, uf->uf_lh, "uftdi_open_hw_port: failed to reset!"); return (rval); } if (dorestore) { /* * Restore settings from our soft copy of HW registers */ (void) uftdi_setregs(uf, NULL); } else { /* * 9600 baud, 2 stop bits, no parity, 8-bit, h/w flow control */ static ds_port_param_entry_t ents[] = { #if defined(__lock_lint) /* * (Sigh - wlcc doesn't understand this newer * form of structure member initialization.) */ { 0 } #else { DS_PARAM_BAUD, .val.ui = B9600 }, { DS_PARAM_STOPB, .val.ui = CSTOPB }, { DS_PARAM_PARITY, .val.ui = 0 },
/* * scsa2usb_handle_csw_result: * Handle status results */ static int scsa2usb_handle_csw_result(scsa2usb_state_t *scsa2usbp, mblk_t *data) { int rval = USB_SUCCESS; int residue; char *msg = "CSW FAILED"; uint_t signature, tag, status; usb_bulk_csw_t csw; struct scsi_pkt *pkt = scsa2usbp->scsa2usb_cur_pkt; scsa2usb_cmd_t *cmd = PKT2CMD(pkt); ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex)); /* * This shouldn't happen. It implies the device's * firmware is bad and has returned NULL CSW. * return failure back. */ if (data == NULL) { USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "scsa2usb_handle_csw_result: data == NULL"); return (USB_FAILURE); } /* check if we got back CSW_LEN or not */ if (MBLKL(data) != CSW_LEN) { USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "scsa2usb_handle_csw_result: no enough data (%ld)", (long)(MBLKL(data))); return (USB_FAILURE); } /* Read into csw */ bcopy(data->b_rptr, &csw, CSW_LEN); status = csw.csw_bCSWStatus; signature = SCSA2USB_MK_32BIT(csw.csw_dCSWSignature3, csw.csw_dCSWSignature2, csw.csw_dCSWSignature1, csw.csw_dCSWSignature0); residue = SCSA2USB_MK_32BIT(csw.csw_dCSWDataResidue3, csw.csw_dCSWDataResidue2, csw.csw_dCSWDataResidue1, csw.csw_dCSWDataResidue0); tag = SCSA2USB_MK_32BIT(csw.csw_dCSWTag3, csw.csw_dCSWTag2, csw.csw_dCSWTag1, csw.csw_dCSWTag0); USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "CSW: Signature = 0x%x Status = 0%x Tag = 0x%x Residue = 0x%x", signature, status, tag, residue); /* Check for abnormal errors */ if ((signature != CSW_SIGNATURE) || (tag != cmd->cmd_tag) || (status > CSW_STATUS_PHASE_ERROR)) { USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "CSW_ERR: Status = 0x%x, Tag = 0x%x xfercount = 0x%lx", status, cmd->cmd_tag, cmd->cmd_total_xfercount); return (USB_FAILURE); } switch (status) { case CSW_STATUS_GOOD: /* * Fail the command if the device misbehaves and * gives a good status but doesn't transfer any data. * Otherwise we'll get into an infinite retry loop. * * We test only against cmd_total_xfercount here and * assume that this will not happen on a command that * transfers a large amount of data and therefore may * be split into separate transfers. For a large data * transfer it is assumed that the device will return * an error status if the transfer does not occur. * this isn't quite correct because a subsequent request * sense may not give a valid sense key. */ if (!cmd->cmd_done && residue && (residue == cmd->cmd_total_xfercount)) { *(pkt->pkt_scbp) = STATUS_CHECK; cmd->cmd_xfercount = 0; cmd->cmd_done = 1; } else { msg = "CSW GOOD"; } break; case CSW_STATUS_FAILED: *(pkt->pkt_scbp) = STATUS_CHECK; /* Set check condition */ cmd->cmd_done = 1; break; case CSW_STATUS_PHASE_ERROR: USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "scsa2usb_handle_csw_result: Phase Error"); /* invoke reset recovery */ scsa2usb_bulk_only_handle_error(scsa2usbp, NULL); return (USB_FAILURE); default: /* shouldn't happen anymore */ USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "scsa2usb_handle_csw_result: Invalid CSW"); /* invoke reset recovery */ scsa2usb_bulk_only_handle_error(scsa2usbp, NULL); return (USB_SUCCESS); } /* end of switch */ /* Set resid */ if (residue || cmd->cmd_resid_xfercount) { USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "total=0x%lx cmd_xfercount=0x%lx residue=0x%x " "cmd_offset=0x%lx", cmd->cmd_total_xfercount, cmd->cmd_xfercount, residue, cmd->cmd_offset); /* * we need to adjust using the residue and * assume worst case. Some devices lie about * residue. some report a residue greater than * the residue we have calculated. * first adjust back the total_xfercount */ cmd->cmd_total_xfercount += cmd->cmd_xfercount - cmd->cmd_resid_xfercount; /* * we need to adjust cmd_offset as well, or the data * buffer for subsequent transfer may exceed the buffer * boundary */ cmd->cmd_offset -= cmd->cmd_xfercount - cmd->cmd_resid_xfercount; /* * now take the min of the reported residue by * the device and the requested xfer count * (just in case the device reported a residue greater * than our request count). * then take the max of this residue and the residue * that the HCD reported and subtract this from * the request count. This is the actual number * of valid bytes transferred during the last transfer * which we now subtract from the total_xfercount */ if ((!(scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_USE_CSW_RESIDUE)) || (residue < 0) || (residue > cmd->cmd_total_xfercount)) { /* some devices lie about the resid, ignore */ cmd->cmd_total_xfercount -= cmd->cmd_xfercount - cmd->cmd_resid_xfercount; cmd->cmd_offset += cmd->cmd_xfercount - cmd->cmd_resid_xfercount; } else { cmd->cmd_total_xfercount -= cmd->cmd_xfercount - max(min(residue, cmd->cmd_xfercount), cmd->cmd_resid_xfercount); cmd->cmd_offset += cmd->cmd_xfercount - max(min(residue, cmd->cmd_xfercount), cmd->cmd_resid_xfercount); /* * if HCD does not report residue while the device * reports a residue equivalent to the xfercount, * it is very likely the device lies about the * residue. we need to stop the command, or we'll * get into an infinite retry loop. */ if ((cmd->cmd_resid_xfercount == 0) && (residue == cmd->cmd_xfercount)) { cmd->cmd_xfercount = 0; cmd->cmd_done = 1; } } pkt->pkt_resid = cmd->cmd_total_xfercount; } USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "scsa2usb_handle_csw_result: %s, resid: 0x%lx", msg, pkt->pkt_resid); /* we are done and ready to callback */ SCSA2USB_SET_PKT_DO_COMP_STATE(scsa2usbp); return (rval); }
/* * wusb_df_attach: * Attach or resume. * * For attach, initialize state and device, including: * state variables, locks, device node * device registration with system * power management, hotplugging * For resume, restore device and state */ static int wusb_df_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int instance = ddi_get_instance(dip); char *devinst; int devinstlen; wusb_df_state_t *wusb_dfp = NULL; usb_ep_data_t *ep_datap; int status; switch (cmd) { case DDI_ATTACH: break; case DDI_RESUME: wusb_df_cpr_resume(dip); /* * Always return success to work around enumeration failures. * This works around an issue where devices which are present * before a suspend and absent upon resume could cause a system * panic on resume. */ return (DDI_SUCCESS); default: return (DDI_FAILURE); } if (ddi_soft_state_zalloc(wusb_df_statep, instance) == DDI_SUCCESS) { wusb_dfp = ddi_get_soft_state(wusb_df_statep, instance); } if (wusb_dfp == NULL) { return (DDI_FAILURE); } wusb_dfp->wusb_df_dip = dip; devinst = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP); devinstlen = snprintf(devinst, USB_MAXSTRINGLEN, "%s%d: ", ddi_driver_name(dip), instance); wusb_dfp->wusb_df_devinst = kmem_zalloc(devinstlen + 1, KM_SLEEP); (void) strncpy(wusb_dfp->wusb_df_devinst, devinst, devinstlen); kmem_free(devinst, USB_MAXSTRINGLEN); wusb_dfp->wusb_df_log_hdl = usb_alloc_log_hdl(dip, "wusb_df", &wusb_df_errlevel, &wusb_df_errmask, &wusb_df_instance_debug, 0); USB_DPRINTF_L4(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "Attach: enter for attach"); if ((status = usb_client_attach(dip, USBDRV_VERSION, 0)) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "attach: usb_client_attach failed, error code:%d", status); goto fail; } if ((status = usb_get_dev_data(dip, &wusb_dfp->wusb_df_reg, USB_PARSE_LVL_ALL, 0)) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "attach: usb_get_dev_data failed, error code:%d", status); goto fail; } /* * Get the descriptor for an intr pipe at alt 0 of current interface. * This will be used later to open the pipe. */ if ((ep_datap = usb_lookup_ep_data(dip, wusb_dfp->wusb_df_reg, wusb_dfp->wusb_df_reg->dev_curr_if, 0, 0, USB_EP_ATTR_INTR, USB_EP_DIR_IN)) == NULL) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "attach: Error getting intr endpoint descriptor"); goto fail; } wusb_dfp->wusb_df_intr_ep_descr = ep_datap->ep_descr; usb_free_descr_tree(dip, wusb_dfp->wusb_df_reg); mutex_init(&wusb_dfp->wusb_df_mutex, NULL, MUTEX_DRIVER, wusb_dfp->wusb_df_reg->dev_iblock_cookie); cv_init(&wusb_dfp->wusb_df_serial_cv, NULL, CV_DRIVER, NULL); wusb_dfp->wusb_df_serial_inuse = B_FALSE; wusb_dfp->wusb_df_locks_initialized = B_TRUE; /* create minor node */ if (ddi_create_minor_node(dip, name, S_IFCHR, instance, "wusb_df", 0) != DDI_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "attach: Error creating minor node"); goto fail; } /* Put online before PM init as can get power managed afterward. */ wusb_dfp->wusb_df_dev_state = USB_DEV_ONLINE; /* initialize power management */ wusb_df_init_power_mgmt(wusb_dfp); if (usb_register_hotplug_cbs(dip, wusb_df_disconnect_callback, wusb_df_reconnect_callback) != USB_SUCCESS) { goto fail; } /* Report device */ ddi_report_dev(dip); (void) wusb_df_firmware_download(wusb_dfp); if (usb_reset_device(dip, USB_RESET_LVL_REATTACH) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "reset device failed"); return (USB_FAILURE); } return (DDI_SUCCESS); fail: if (wusb_dfp) { (void) wusb_df_cleanup(dip, wusb_dfp); } return (DDI_FAILURE); }
/* * scsa2usb_bulk_only_get_max_lun: * this function returns the number of LUNs supported by the device */ int scsa2usb_bulk_only_get_max_lun(scsa2usb_state_t *scsa2usbp) { int luns = 1, rval; mblk_t *data = NULL; usb_cr_t completion_reason; usb_cb_flags_t cb_flags; USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "scsa2usb_bulk_only_get_max_lun:"); ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex)); mutex_exit(&scsa2usbp->scsa2usb_mutex); rval = usb_pipe_sync_ctrl_xfer(scsa2usbp->scsa2usb_dip, scsa2usbp->scsa2usb_default_pipe, BULK_ONLY_GET_MAXLUN_BMREQ, /* bmRequestType */ BULK_ONLY_GET_MAXLUN_REQ, /* bRequest */ 0, /* wValue */ scsa2usbp->scsa2usb_intfc_num, /* wIndex */ 1, /* wLength */ &data, 0, &completion_reason, &cb_flags, 0); mutex_enter(&scsa2usbp->scsa2usb_mutex); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "get max lun failed, rval=%d cr=%d cb=0x%x data=0x%p", rval, completion_reason, cb_flags, (void *)data); } else { /* * This check ensures that we have valid data returned back. * Otherwise we assume that device supports only one LUN. */ if (MBLKL(data) != 1) { USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "device reported incorrect luns (adjusting to 1)"); } else { /* * Set scsa2usb_n_luns to value returned by the device * plus 1. (See Section 3.2) */ luns = *data->b_rptr + 1; /* * In case a device returns incorrect LUNs * which are more than 15 or negative or 0; * we assume 1. */ if ((luns >= SCSA2USB_MAX_LUNS) || (luns <= 0)) { USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "device reported %d luns " "(adjusting to 1)", luns); luns = 1; } } } SCSA2USB_FREE_MSG(data); /* Free data */ return (luns); }
/* write firmware segments into device through control endpoint */ static int wusb_df_fw_download(wusb_df_state_t *wusb_dfp) { int error = USB_SUCCESS; size_t size = 0, record_cnt = 0; unsigned char *pdata, *data_end; unsigned char *firmware_image; fw_dsc_t *pdsc = NULL, *rcd_head = NULL, *tmpr = NULL; unsigned int remaining_size; int rv = 0; ddi_modhandle_t modp; char *firm_start; USB_DPRINTF_L3(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "Download firmware: %s", wusb_fwmod); /* allow user specify firmware in .conf? */ /* see elfwrap(1) for how to turn firmware into loadable module */ modp = ddi_modopen(wusb_fwmod, KRTLD_MODE_FIRST, &rv); if (modp == NULL) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "module %s not found", wusb_fwmod); error = USB_FAILURE; goto checkstatus; } rv = wusbdf_mod_loadsym(wusb_dfp, modp, wusb_fwmod, wusb_fwsym1, &firm_start, &size); if (rv != 0) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "module(%s) loadsym error", wusb_fwmod); error = USB_FAILURE; goto checkstatus; } if (size >= MAX_DAT_FILE_SIZE) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "file size too big"); error = USB_FAILURE; goto checkstatus; } else { firmware_image = (unsigned char *)kmem_alloc(size, KM_SLEEP); if (!firmware_image) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "malloc failed"); error = USB_FAILURE; goto checkstatus; } (void) memcpy(firmware_image, firm_start, size); } USB_DPRINTF_L3(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "file size = %d", (int)size); /* * close the module, return if 1) fail to close or 2) encounter error * when getting above symbol */ checkstatus: if (modp != NULL) rv = ddi_modclose(modp); if ((rv != 0) || (error != USB_SUCCESS)) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "modclose(%s) error", wusb_fwmod); return (USB_FAILURE); } /* * BIN firmware has this format: * address(4B) + length(4B) + data(Length Bytes) ... repeat */ pdata = firmware_image; data_end = firmware_image + size; while (pdata < data_end) { error = USB_FAILURE; pdsc = (fw_dsc_t *)kmem_zalloc(sizeof (fw_dsc_t), KM_SLEEP); /* hdr_offset = pdata - firmware_image; */ remaining_size = data_end - pdata; if ((pdata + 8) > data_end) { kmem_free(pdsc, sizeof (fw_dsc_t)); free_fw_dscs(rcd_head); break; } pdsc->next = NULL; pdsc->addr = char2int32(pdata); pdsc->size = 4 * char2int32(pdata + 4); pdsc->data = pdata + 8; if (pdsc->size > remaining_size) { kmem_free(pdsc, sizeof (fw_dsc_t)); free_fw_dscs(rcd_head); break; } USB_DPRINTF_L3(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "address = 0x%x, length = 0x%x, " "first 4 byte is : 0x%02x 0x%02x 0x%02x 0x%02x", pdsc->addr, (int)pdsc->size, pdsc->data[0], pdsc->data[1], pdsc->data[2], pdsc->data[3]); pdata += 8 + pdsc->size; if (rcd_head == NULL) { rcd_head = pdsc; } else { tmpr->next = pdsc; } tmpr = pdsc; /* tmp record */ record_cnt ++; error = USB_SUCCESS; } USB_DPRINTF_L3(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "Start download firmware ..."); for (pdsc = rcd_head; pdsc != NULL; pdsc = pdsc->next) { error = wusb_df_send_data(wusb_dfp, pdsc->addr, pdsc->data, pdsc->size); if (error != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "Download failure!"); break; } delay(drv_usectohz(1000)); } USB_DPRINTF_L2(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "Download firmware end."); free_fw_dscs(rcd_head); kmem_free(firmware_image, size); return (error); }
/* * ehci_intr: * * EHCI (EHCI) interrupt handling routine. */ uint_t ehci_intr(caddr_t arg1, caddr_t arg2) { uint_t intr; ehci_state_t *ehcip = (ehci_state_t *)arg1; USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "ehci_intr: Interrupt occurred, arg1 0x%p arg2 0x%p", arg1, arg2); /* Get the ehci global mutex */ mutex_enter(&ehcip->ehci_int_mutex); /* * Now process the actual ehci interrupt events that caused * invocation of this ehci interrupt handler. */ intr = (Get_OpReg(ehci_status) & Get_OpReg(ehci_interrupt)); /* Update kstat values */ ehci_do_intrs_stats(ehcip, intr); /* * We could have gotten a spurious interrupts. If so, do not * claim it. This is quite possible on some architectures * where more than one PCI slots share the IRQs. If so, the * associated driver's interrupt routine may get called even * if the interrupt is not meant for them. * * By unclaiming the interrupt, the other driver gets chance * to service its interrupt. */ if (!intr) { mutex_exit(&ehcip->ehci_int_mutex); return (DDI_INTR_UNCLAIMED); } /* Acknowledge the interrupt */ Set_OpReg(ehci_status, intr); if (ehcip->ehci_hc_soft_state == EHCI_CTLR_ERROR_STATE) { mutex_exit(&ehcip->ehci_int_mutex); return (DDI_INTR_CLAIMED); } USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "Interrupt status 0x%x", intr); /* * If necessary broadcast that an interrupt has occured. This * is only necessary during controller init. */ if (ehcip->ehci_flags & EHCI_CV_INTR) { ehcip->ehci_flags &= ~EHCI_CV_INTR; cv_broadcast(&ehcip->ehci_async_schedule_advance_cv); } /* Check for Frame List Rollover */ if (intr & EHCI_INTR_FRAME_LIST_ROLLOVER) { USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "ehci_intr: Frame List Rollover"); ehci_handle_frame_list_rollover(ehcip); /* VIA VT6202 looses EHCI_INTR_USB interrupts, workaround. */ if ((ehcip->ehci_vendor_id == PCI_VENDOR_VIA) && (ehci_vt62x2_workaround & EHCI_VIA_LOST_INTERRUPTS)) { ehcip->ehci_missed_intr_sts |= EHCI_INTR_USB; } } /* Check for Advance on Asynchronous Schedule */ if (intr & EHCI_INTR_ASYNC_ADVANCE) { USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "ehci_intr: Asynchronous Schedule Advance Notification"); /* Disable async list advance interrupt */ Set_OpReg(ehci_interrupt, (Get_OpReg(ehci_interrupt) & ~EHCI_INTR_ASYNC_ADVANCE)); /* * Call cv_broadcast on every this interrupt to wakeup * all the threads that are waiting the async list advance * event. */ cv_broadcast(&ehcip->ehci_async_schedule_advance_cv); } /* Always process completed itds */ ehci_traverse_active_isoc_list(ehcip); /* * Check for any USB transaction completion notification. Also * process any missed USB transaction completion interrupts. */ if ((intr & EHCI_INTR_USB) || (intr & EHCI_INTR_USB_ERROR) || (ehcip->ehci_missed_intr_sts & EHCI_INTR_USB) || (ehcip->ehci_missed_intr_sts & EHCI_INTR_USB_ERROR)) { USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "ehci_intr: USB Transaction Completion Notification"); /* Clear missed interrupts */ if (ehcip->ehci_missed_intr_sts) { ehcip->ehci_missed_intr_sts = 0; } /* Process completed qtds */ ehci_traverse_active_qtd_list(ehcip); } /* Process endpoint reclamation list */ if (ehcip->ehci_reclaim_list) { ehci_handle_endpoint_reclaimation(ehcip); } /* Check for Host System Error */ if (intr & EHCI_INTR_HOST_SYSTEM_ERROR) { USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "ehci_intr: Unrecoverable error"); ehci_handle_ue(ehcip); } /* * Read interrupt status register to make sure that any PIO * store to clear the ISR has made it on the PCI bus before * returning from its interrupt handler. */ (void) Get_OpReg(ehci_status); /* Release the ehci global mutex */ mutex_exit(&ehcip->ehci_int_mutex); USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "Interrupt handling completed"); return (DDI_INTR_CLAIMED); }
/* * ehci_hcdi_pipe_open: * * Member of HCD Ops structure and called during client specific pipe open * Add the pipe to the data structure representing the device and allocate * bandwidth for the pipe if it is a interrupt or isochronous endpoint. */ int ehci_hcdi_pipe_open( usba_pipe_handle_data_t *ph, usb_flags_t flags) { ehci_state_t *ehcip = ehci_obtain_state( ph->p_usba_device->usb_root_hub_dip); usb_ep_descr_t *epdt = &ph->p_ep; int rval, error = USB_SUCCESS; int kmflag = (flags & USB_FLAGS_SLEEP) ? KM_SLEEP : KM_NOSLEEP; uchar_t smask = 0; uchar_t cmask = 0; uint_t pnode = 0; ehci_pipe_private_t *pp; USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl, "ehci_hcdi_pipe_open: addr = 0x%x, ep%d", ph->p_usba_device->usb_addr, epdt->bEndpointAddress & USB_EP_NUM_MASK); mutex_enter(&ehcip->ehci_int_mutex); rval = ehci_state_is_operational(ehcip); mutex_exit(&ehcip->ehci_int_mutex); if (rval != USB_SUCCESS) { return (rval); } /* * Check and handle root hub pipe open. */ if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) { mutex_enter(&ehcip->ehci_int_mutex); error = ehci_handle_root_hub_pipe_open(ph, flags); mutex_exit(&ehcip->ehci_int_mutex); return (error); } /* * Opening of other pipes excluding root hub pipe are * handled below. Check whether pipe is already opened. */ if (ph->p_hcd_private) { USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl, "ehci_hcdi_pipe_open: Pipe is already opened"); return (USB_FAILURE); } /* * A portion of the bandwidth is reserved for the non-periodic * transfers, i.e control and bulk transfers in each of one * millisecond frame period & usually it will be 20% of frame * period. Hence there is no need to check for the available * bandwidth before adding the control or bulk endpoints. * * There is a need to check for the available bandwidth before * adding the periodic transfers, i.e interrupt & isochronous, * since all these periodic transfers are guaranteed transfers. * Usually 80% of the total frame time is reserved for periodic * transfers. */ if (EHCI_PERIODIC_ENDPOINT(epdt)) { mutex_enter(&ehcip->ehci_int_mutex); mutex_enter(&ph->p_mutex); error = ehci_allocate_bandwidth(ehcip, ph, &pnode, &smask, &cmask); if (error != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl, "ehci_hcdi_pipe_open: Bandwidth allocation failed"); mutex_exit(&ph->p_mutex); mutex_exit(&ehcip->ehci_int_mutex); return (error); } mutex_exit(&ph->p_mutex); mutex_exit(&ehcip->ehci_int_mutex); } /* Create the HCD pipe private structure */ pp = kmem_zalloc(sizeof (ehci_pipe_private_t), kmflag); /* * Return failure if ehci pipe private * structure allocation fails. */ if (pp == NULL) { mutex_enter(&ehcip->ehci_int_mutex); /* Deallocate bandwidth */ if (EHCI_PERIODIC_ENDPOINT(epdt)) { mutex_enter(&ph->p_mutex); ehci_deallocate_bandwidth(ehcip, ph, pnode, smask, cmask); mutex_exit(&ph->p_mutex); } mutex_exit(&ehcip->ehci_int_mutex); return (USB_NO_RESOURCES); } mutex_enter(&ehcip->ehci_int_mutex); /* Save periodic nodes */ pp->pp_pnode = pnode; /* Save start and complete split mask values */ pp->pp_smask = smask; pp->pp_cmask = cmask; /* Create prototype for xfer completion condition variable */ cv_init(&pp->pp_xfer_cmpl_cv, NULL, CV_DRIVER, NULL); /* Set the state of pipe as idle */ pp->pp_state = EHCI_PIPE_STATE_IDLE; /* Store a pointer to the pipe handle */ pp->pp_pipe_handle = ph; mutex_enter(&ph->p_mutex); /* Store the pointer in the pipe handle */ ph->p_hcd_private = (usb_opaque_t)pp; /* Store a copy of the pipe policy */ bcopy(&ph->p_policy, &pp->pp_policy, sizeof (usb_pipe_policy_t)); mutex_exit(&ph->p_mutex); /* Allocate the host controller endpoint descriptor */ pp->pp_qh = ehci_alloc_qh(ehcip, ph, NULL); /* Initialize the halting flag */ pp->pp_halt_state = EHCI_HALT_STATE_FREE; /* Create prototype for halt completion condition variable */ cv_init(&pp->pp_halt_cmpl_cv, NULL, CV_DRIVER, NULL); /* Isoch does not use QH, so ignore this */ if ((pp->pp_qh == NULL) && !(EHCI_ISOC_ENDPOINT(epdt))) { ASSERT(pp->pp_qh == NULL); USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl, "ehci_hcdi_pipe_open: QH allocation failed"); mutex_enter(&ph->p_mutex); /* Deallocate bandwidth */ if (EHCI_PERIODIC_ENDPOINT(epdt)) { ehci_deallocate_bandwidth(ehcip, ph, pnode, smask, cmask); } /* Destroy the xfer completion condition variable */ cv_destroy(&pp->pp_xfer_cmpl_cv); /* * Deallocate the hcd private portion * of the pipe handle. */ kmem_free(ph->p_hcd_private, sizeof (ehci_pipe_private_t)); /* * Set the private structure in the * pipe handle equal to NULL. */ ph->p_hcd_private = NULL; mutex_exit(&ph->p_mutex); mutex_exit(&ehcip->ehci_int_mutex); return (USB_NO_RESOURCES); } /* * Isoch does not use QH so no need to * restore data toggle or insert QH */ if (!(EHCI_ISOC_ENDPOINT(epdt))) { /* Restore the data toggle information */ ehci_restore_data_toggle(ehcip, ph); } /* * Insert the endpoint onto the host controller's * appropriate endpoint list. The host controller * will not schedule this endpoint and will not have * any QTD's to process. It will also update the pipe count. */ ehci_insert_qh(ehcip, ph); USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl, "ehci_hcdi_pipe_open: ph = 0x%p", (void *)ph); ehcip->ehci_open_pipe_count++; mutex_exit(&ehcip->ehci_int_mutex); return (USB_SUCCESS); }
/* * ehci_allocate_isoc_resources: * * Calculates the number of tds necessary for a isoch transfer, and * allocates all the necessary resources. * * Returns NULL if there is insufficient resources otherwise ITW. */ ehci_isoc_xwrapper_t * ehci_allocate_isoc_resources( ehci_state_t *ehcip, usba_pipe_handle_data_t *ph, usb_isoc_req_t *isoc_reqp, usb_flags_t usb_flags) { ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private; int pipe_dir; uint_t max_ep_pkt_size, max_isoc_xfer_size; usb_isoc_pkt_descr_t *isoc_pkt_descr; size_t isoc_pkt_count, isoc_pkt_length; size_t itw_xfer_size; ehci_isoc_xwrapper_t *itw; USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl, "ehci_allocate_isoc_resources: flags = 0x%x", usb_flags); ASSERT(mutex_owned(&ehcip->ehci_int_mutex)); /* * Check whether pipe is in halted state. */ if (pp->pp_state == EHCI_PIPE_STATE_ERROR) { USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl, "ehci_allocate_isoc_resources:" "Pipe is in error state, need pipe reset to continue"); return (NULL); } /* Calculate the maximum isochronous transfer size we allow */ max_ep_pkt_size = ph->p_ep.wMaxPacketSize; max_isoc_xfer_size = EHCI_MAX_ISOC_PKTS_PER_XFER * max_ep_pkt_size; /* Get the packet descriptor and number of packets to send */ if (isoc_reqp) { isoc_pkt_descr = isoc_reqp->isoc_pkt_descr; isoc_pkt_count = isoc_reqp->isoc_pkts_count; isoc_pkt_length = isoc_reqp->isoc_pkts_length; } else { isoc_pkt_descr = ((usb_isoc_req_t *) pp->pp_client_periodic_in_reqp)->isoc_pkt_descr; isoc_pkt_count = ((usb_isoc_req_t *) pp->pp_client_periodic_in_reqp)->isoc_pkts_count; isoc_pkt_length = ((usb_isoc_req_t *) pp->pp_client_periodic_in_reqp)->isoc_pkts_length; } /* Calculate the size of the transfer. */ pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK; if (pipe_dir == USB_EP_DIR_IN) { itw_xfer_size = isoc_pkt_count * isoc_pkt_length; isoc_pkt_descr += isoc_pkt_count; } else { ASSERT(isoc_reqp != NULL); itw_xfer_size = isoc_reqp->isoc_data->b_wptr - isoc_reqp->isoc_data->b_rptr; } /* Check the size of isochronous request */ if (itw_xfer_size > max_isoc_xfer_size) { USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl, "ehci_allocate_isoc_resources: Maximum isoc request" "size 0x%x Given isoc request size 0x%x", max_isoc_xfer_size, itw_xfer_size); return (NULL); } USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl, "ehci_allocate_isoc_resources: length = 0x%x", itw_xfer_size); /* Allocate the itw for this request */ if ((itw = ehci_allocate_itw_resources(ehcip, pp, itw_xfer_size, usb_flags, isoc_pkt_count)) == NULL) { return (NULL); } itw->itw_handle_callback_value = NULL; if (pipe_dir == USB_EP_DIR_IN) { if (ehci_allocate_isoc_in_resource(ehcip, pp, itw, usb_flags) != USB_SUCCESS) { ehci_deallocate_itw(ehcip, pp, itw); return (NULL); } } else { if (itw->itw_length) { ASSERT(isoc_reqp->isoc_data != NULL); /* Copy the data into the buffer */ bcopy(isoc_reqp->isoc_data->b_rptr, itw->itw_buf, itw->itw_length); Sync_IO_Buffer_for_device(itw->itw_dmahandle, itw->itw_length); } itw->itw_curr_xfer_reqp = isoc_reqp; } return (itw); }
/* * scsa2usb_bulk_only_reset_recovery: * Reset the USB device step-wise in case of errors. * NOTE that the order of reset is very important. */ static void scsa2usb_bulk_only_reset_recovery(scsa2usb_state_t *scsa2usbp) { int rval; usb_cr_t completion_reason; usb_cb_flags_t cb_flags; USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "scsa2usb_bulk_only_reset_recovery: scsa2usbp = 0x%p", (void *)scsa2usbp); ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex)); if (!(SCSA2USB_DEVICE_ACCESS_OK(scsa2usbp))) { return; } /* * assume that the reset will be successful. if it isn't, retrying * from target driver won't help much */ if (scsa2usbp->scsa2usb_cur_pkt) { scsa2usbp->scsa2usb_cur_pkt->pkt_statistics |= STAT_DEV_RESET; } /* set the reset condition */ scsa2usbp->scsa2usb_pipe_state = SCSA2USB_PIPE_DEV_RESET; /* Send a sync DEVICE-RESET request to the device */ mutex_exit(&scsa2usbp->scsa2usb_mutex); rval = usb_pipe_sync_ctrl_xfer(scsa2usbp->scsa2usb_dip, scsa2usbp->scsa2usb_default_pipe, USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF, (uint8_t)BULK_ONLY_RESET, /* bRequest */ 0, /* wValue */ scsa2usbp->scsa2usb_intfc_num, /* wIndex */ 0, /* wLength */ NULL, 0, &completion_reason, &cb_flags, 0); mutex_enter(&scsa2usbp->scsa2usb_mutex); USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "\tbulk-only device-reset rval: %d", rval); if (rval != USB_SUCCESS) { goto exc_exit; } /* reset and clear STALL on bulk-in pipe */ rval = scsa2usb_clear_ept_stall(scsa2usbp, scsa2usbp->scsa2usb_bulkin_ept.bEndpointAddress, scsa2usbp->scsa2usb_bulkin_pipe, "bulk-in"); USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "\tbulk-in pipe clear stall: %d", rval); if (rval != USB_SUCCESS) { goto exc_exit; } /* reset and clear STALL on bulk-out pipe */ rval = scsa2usb_clear_ept_stall(scsa2usbp, scsa2usbp->scsa2usb_bulkout_ept.bEndpointAddress, scsa2usbp->scsa2usb_bulkout_pipe, "bulk-out"); USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "\tbulk-out pipe clear stall: %d", rval); exc_exit: /* clear the reset condition */ scsa2usbp->scsa2usb_pipe_state &= ~SCSA2USB_PIPE_DEV_RESET; }