/* ARGSUSED */ static void ehci_sendup_itd_message( ehci_state_t *ehcip, ehci_pipe_private_t *pp, ehci_isoc_xwrapper_t *itw, ehci_itd_t *td, usb_cr_t error) { usb_isoc_req_t *isoc_reqp = itw->itw_curr_xfer_reqp; usba_pipe_handle_data_t *ph = pp->pp_pipe_handle; size_t length; uchar_t *buf; mblk_t *mp; ASSERT(mutex_owned(&ehcip->ehci_int_mutex)); USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "ehci_sendup_itd_message:"); ASSERT(itw != NULL); length = itw->itw_length; /* Copy the data into the mblk_t */ buf = (uchar_t *)itw->itw_buf; USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "ehci_sendup_itd_message: length %d error %d", length, error); /* Get the message block */ mp = isoc_reqp->isoc_data; ASSERT(mp != NULL); if (length) { /* Sync IO buffer */ Sync_IO_Buffer(itw->itw_dmahandle, length); /* Copy the data into the message */ bcopy(buf, mp->b_rptr, length); /* Increment the write pointer */ mp->b_wptr = mp->b_wptr + length; } else { USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "ehci_sendup_itd_message: Zero length packet"); } ehci_hcdi_isoc_callback(ph, itw, error); }
/* * scsa2usb_handle_status_start: * Receive status data */ static int scsa2usb_handle_status_start(scsa2usb_state_t *scsa2usbp, usb_bulk_req_t *req) { int rval; USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "scsa2usb_handle_status_start: req = 0x%p", (void *)req); ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex)); /* setup up for receiving CSW */ #ifdef SCSA2USB_BULK_ONLY_TEST req->bulk_attributes = 0; #else req->bulk_attributes = USB_ATTRS_SHORT_XFER_OK; #endif /* SCSA2USB_BULK_ONLY_TEST */ req->bulk_len = CSW_LEN; SCSA2USB_FREE_MSG(req->bulk_data); req->bulk_data = allocb_wait(req->bulk_len, BPRI_LO, STR_NOSIG, NULL); /* Issue the request */ mutex_exit(&scsa2usbp->scsa2usb_mutex); ASSERT(req->bulk_timeout); rval = usb_pipe_bulk_xfer(scsa2usbp->scsa2usb_bulkin_pipe, req, USB_FLAGS_SLEEP); mutex_enter(&scsa2usbp->scsa2usb_mutex); USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle, "scsa2usb_handle_status_start: END rval = 0x%x", rval); if (rval != USB_SUCCESS) { if (scsa2usbp->scsa2usb_pkt_state == SCSA2USB_PKT_PROCESS_CSW) { scsa2usb_bulk_only_reset_recovery(scsa2usbp); return (rval); } if (req->bulk_completion_reason == USB_CR_STALL) { (void) scsa2usb_clear_ept_stall(scsa2usbp, scsa2usbp->scsa2usb_bulkin_ept.bEndpointAddress, scsa2usbp->scsa2usb_bulkin_pipe, "bulk-in"); } } return (rval); }
/* * wusb_df_cleanup: * clean up the driver state for detach */ static int wusb_df_cleanup(dev_info_t *dip, wusb_df_state_t *wusb_dfp) { USB_DPRINTF_L3(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "Cleanup: enter"); if (wusb_dfp->wusb_df_locks_initialized) { /* This must be done 1st to prevent more events from coming. */ usb_unregister_hotplug_cbs(dip); /* * At this point, no new activity can be initiated. The driver * has disabled hotplug callbacks. The Solaris framework has * disabled new opens on a device being detached, and does not * allow detaching an open device. * * The following ensures that all driver activity has drained. */ mutex_enter(&wusb_dfp->wusb_df_mutex); (void) wusb_df_serialize_access(wusb_dfp, WUSB_DF_SER_NOSIG); wusb_df_release_access(wusb_dfp); mutex_exit(&wusb_dfp->wusb_df_mutex); /* All device activity has died down. */ wusb_df_destroy_power_mgmt(wusb_dfp); /* start dismantling */ ddi_remove_minor_node(dip, NULL); cv_destroy(&wusb_dfp->wusb_df_serial_cv); mutex_destroy(&wusb_dfp->wusb_df_mutex); } usb_client_detach(dip, wusb_dfp->wusb_df_reg); usb_free_log_hdl(wusb_dfp->wusb_df_log_hdl); if (wusb_dfp->wusb_df_devinst != NULL) { kmem_free(wusb_dfp->wusb_df_devinst, strlen(wusb_dfp->wusb_df_devinst) + 1); } ddi_soft_state_free(wusb_df_statep, ddi_get_instance(dip)); ddi_prop_remove_all(dip); return (USB_SUCCESS); }
/* * ehci_traverse_active_isoc_list: */ void ehci_traverse_active_isoc_list( ehci_state_t *ehcip) { ehci_isoc_xwrapper_t *curr_itw; ehci_itd_t *curr_itd, *next_itd; uint_t state; ehci_pipe_private_t *pp; USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "ehci_traverse_active_isoc_list:"); ASSERT(mutex_owned(&ehcip->ehci_int_mutex)); /* Sync ITD pool */ Sync_ITD_Pool(ehcip); /* Traverse the list of done itds */ curr_itd = ehci_create_done_itd_list(ehcip); while (curr_itd) { /* Save the next_itd */ next_itd = ehci_itd_iommu_to_cpu(ehcip, Get_ITD(curr_itd->itd_next_active_itd)); /* Get the transfer wrapper and the pp */ curr_itw = (ehci_isoc_xwrapper_t *)EHCI_LOOKUP_ID( (uint32_t)Get_ITD(curr_itd->itd_trans_wrapper)); pp = curr_itw->itw_pipe_private; USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "ehci_traverse_active_isoc_list:\t" "pp = 0x%p itw = 0x%p itd = 0x%p", pp, curr_itw, curr_itd); ehci_print_itd(ehcip, curr_itd); /* Get the ITD state */ state = Get_ITD(curr_itd->itd_state); /* Only process the ITDs marked as active. */ if (state == EHCI_ITD_ACTIVE) { ehci_parse_isoc_error(ehcip, curr_itw, curr_itd); ehci_handle_isoc(ehcip, curr_itw, curr_itd); } else { ASSERT(state == EHCI_ITD_RECLAIM); ehci_reclaim_isoc(ehcip, curr_itw, curr_itd, pp); } /* * Deallocate the transfer wrapper if there are no more * ITD's for the transfer wrapper. ehci_deallocate_itw() * will not deallocate the tw for a periodic in endpoint * since it will always have a ITD attached to it. */ ehci_deallocate_itw(ehcip, pp, curr_itw); /* Check any ISOC is waiting for transfers completion event */ if (pp->pp_itw_head == NULL) { USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl, "ehci_traverse_active_isoc_list: " "Sent transfers completion event pp = 0x%p", pp); cv_signal(&pp->pp_xfer_cmpl_cv); } curr_itd = next_itd; } }
/* * ehci_start_isoc_polling: * * Insert the number of periodic requests corresponding to polling * interval as calculated during pipe open. */ int ehci_start_isoc_polling( ehci_state_t *ehcip, usba_pipe_handle_data_t *ph, usb_flags_t flags) { ehci_pipe_private_t *pp = (ehci_pipe_private_t *)ph->p_hcd_private; ehci_isoc_xwrapper_t *itw_list, *itw; int i, total_itws; int error = USB_SUCCESS; /* Allocate all the necessary resources for the IN transfer */ itw_list = NULL; total_itws = pp->pp_max_periodic_req_cnt - pp->pp_cur_periodic_req_cnt; for (i = 0; i < total_itws; i += 1) { itw = ehci_allocate_isoc_resources(ehcip, ph, NULL, flags); if (itw == NULL) { error = USB_NO_RESOURCES; /* There are not enough resources deallocate the ITWs */ itw = itw_list; while (itw != NULL) { itw_list = itw->itw_next; ehci_deallocate_isoc_in_resource( ehcip, pp, itw); ehci_deallocate_itw(ehcip, pp, itw); itw = itw_list; } return (error); } else { if (itw_list == NULL) { itw_list = itw; } } } i = 0; while (pp->pp_cur_periodic_req_cnt < pp->pp_max_periodic_req_cnt) { USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl, "ehci_start_isoc_polling: max = %d curr = %d itw = %p:", pp->pp_max_periodic_req_cnt, pp->pp_cur_periodic_req_cnt, itw_list); itw = itw_list; itw_list = itw->itw_next; error = ehci_insert_isoc_req(ehcip, pp, itw, flags); if (error == USB_SUCCESS) { pp->pp_cur_periodic_req_cnt++; } else { /* * Deallocate the remaining tw * The current tw should have already been deallocated */ itw = itw_list; while (itw != NULL) { itw_list = itw->itw_next; ehci_deallocate_isoc_in_resource( ehcip, pp, itw); ehci_deallocate_itw(ehcip, pp, itw); itw = itw_list; } /* * If this is the first req return an error. * Otherwise return success. */ if (i != 0) { error = USB_SUCCESS; } break; } i++; } return (error); }
/* ARGSUSED */ static void ehci_handle_sitd( ehci_state_t *ehcip, ehci_pipe_private_t *pp, ehci_isoc_xwrapper_t *itw, ehci_itd_t *itd, void *tw_handle_callback_value) { usba_pipe_handle_data_t *ph = pp->pp_pipe_handle; usb_isoc_req_t *curr_isoc_reqp = (usb_isoc_req_t *)itw->itw_curr_xfer_reqp; int error = USB_SUCCESS; USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "ehci_handle_sitd: pp=0x%p itw=0x%p itd=0x%p " "isoc_reqp=0%p data=0x%p", pp, itw, itd, curr_isoc_reqp, curr_isoc_reqp->isoc_data); /* * Decrement the ITDs counter and check whether all the isoc * data has been send or received. If ITDs counter reaches * zero then inform client driver about completion current * isoc request. Otherwise wait for completion of other isoc * ITDs or transactions on this pipe. */ if (--itw->itw_num_itds != 0) { /* Deallocate this transfer descriptor */ ehci_deallocate_itd(ehcip, itw, itd); return; } /* * If this is a isoc in pipe, return the data to the client. * For a isoc out pipe, there is no need to do anything. */ if (itw->itw_direction == USB_EP_DIR_OUT) { USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl, "ehci_handle_sitd: Isoc out pipe, isoc_reqp=0x%p," "data=0x%p", curr_isoc_reqp, curr_isoc_reqp->isoc_data); /* Do the callback */ ehci_hcdi_isoc_callback(ph, itw, USB_CR_OK); /* Deallocate this transfer descriptor */ ehci_deallocate_itd(ehcip, itw, itd); return; } /* Decrement number of IN isochronous request count */ pp->pp_cur_periodic_req_cnt--; /* Call ehci_sendup_itd_message to send message to upstream */ ehci_sendup_itd_message(ehcip, pp, itw, itd, USB_CR_OK); /* Deallocate this transfer descriptor */ ehci_deallocate_itd(ehcip, itw, itd); /* * If isochronous pipe state is still active, insert next isochronous * request into the Host Controller's isochronous list. */ if (pp->pp_state != EHCI_PIPE_STATE_ACTIVE) { return; } if ((error = ehci_allocate_isoc_in_resource(ehcip, pp, itw, 0)) == USB_SUCCESS) { curr_isoc_reqp = (usb_isoc_req_t *)itw->itw_curr_xfer_reqp; ASSERT(curr_isoc_reqp != NULL); itw->itw_num_itds = ehci_calc_num_itds(itw, curr_isoc_reqp->isoc_pkts_count); if (ehci_allocate_itds_for_itw(ehcip, itw, itw->itw_num_itds) != USB_SUCCESS) { ehci_deallocate_isoc_in_resource(ehcip, pp, itw); itw->itw_num_itds = 0; error = USB_FAILURE; } } if ((error != USB_SUCCESS) || (ehci_insert_isoc_req(ehcip, pp, itw, 0) != USB_SUCCESS)) { /* * Set pipe state to stop polling and error to no * resource. Don't insert any more isoch polling * requests. */ pp->pp_state = EHCI_PIPE_STATE_STOP_POLLING; pp->pp_error = USB_CR_NO_RESOURCES; } else { /* Increment number of IN isochronous request count */ pp->pp_cur_periodic_req_cnt++; ASSERT(pp->pp_cur_periodic_req_cnt == pp->pp_max_periodic_req_cnt); } }
/* ARGSUSED */ static int wusb_df_power(dev_info_t *dip, int comp, int level) { wusb_df_state_t *wusb_dfp; wusb_df_power_t *pm; int rval = USB_SUCCESS; wusb_dfp = ddi_get_soft_state(wusb_df_statep, ddi_get_instance(dip)); USB_DPRINTF_L3(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_power: enter: level = %d", level); mutex_enter(&wusb_dfp->wusb_df_mutex); (void) wusb_df_serialize_access(wusb_dfp, WUSB_DF_SER_NOSIG); /* * If we are disconnected/suspended, return success. Note that if we * return failure, bringing down the system will hang when * PM tries to power up all devices */ if ((wusb_dfp->wusb_df_dev_state == USB_DEV_DISCONNECTED) || (wusb_dfp->wusb_df_dev_state == USB_DEV_SUSPENDED)) { USB_DPRINTF_L3(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_power: disconnected/suspended " "dev_state=%d", wusb_dfp->wusb_df_dev_state); rval = USB_SUCCESS; goto done; } if (wusb_dfp->wusb_df_pm == NULL) { goto done; } pm = wusb_dfp->wusb_df_pm; /* Check if we are transitioning to a legal power level */ if (USB_DEV_PWRSTATE_OK(pm->wusb_df_pwr_states, level)) { USB_DPRINTF_L3(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_power: illegal power level = %d " "pwr_states: %x", level, pm->wusb_df_pwr_states); goto done; } switch (level) { case USB_DEV_OS_PWR_OFF : /* fail attempt to go to low power if busy */ if (pm->wusb_df_pm_busy) { goto done; } if (wusb_dfp->wusb_df_dev_state == USB_DEV_ONLINE) { wusb_dfp->wusb_df_dev_state = USB_DEV_PWRED_DOWN; wusb_dfp->wusb_df_pm->wusb_df_current_power = USB_DEV_OS_PWR_OFF; } else { rval = USB_SUCCESS; } break; case USB_DEV_OS_FULL_PWR : /* * PM framework tries to put us in full power during system * shutdown. */ wusb_dfp->wusb_df_dev_state = USB_DEV_ONLINE; wusb_dfp->wusb_df_pm->wusb_df_current_power = USB_DEV_OS_FULL_PWR; break; /* Levels 1 and 2 are not supported by this driver to keep it simple. */ default: USB_DPRINTF_L3(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_power: power level %d not supported", level); break; } done: wusb_df_release_access(wusb_dfp); mutex_exit(&wusb_dfp->wusb_df_mutex); return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE); }
/* 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); }
/* * 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); }
/* * usb_create_pm_components: * map descriptor into pm properties */ int usb_create_pm_components(dev_info_t *dip, uint_t *pwr_states) { uchar_t *usb_cfg; /* buf for config descriptor */ usb_cfg_descr_t cfg_descr; size_t cfg_length; usba_cfg_pwr_descr_t confpwr_descr; usba_if_pwr_descr_t ifpwr_descr; uint8_t cfg_attrib; int i, lvl, rval; int n_prop = 0; uint8_t *ptr; char *drvname; char str[USBA_POWER_STR_SIZE]; char *pm_comp[USBA_N_PMCOMP]; USBA_CHECK_CONTEXT(); if (usb_is_pm_enabled(dip) != USB_SUCCESS) { return (USB_FAILURE); } /* Obtain the raw configuration descriptor */ usb_cfg = usb_get_raw_cfg_data(dip, &cfg_length); /* get configuration descriptor, must succceed */ rval = usb_parse_cfg_descr(usb_cfg, cfg_length, &cfg_descr, USB_CFG_DESCR_SIZE); ASSERT(rval == USB_CFG_DESCR_SIZE); cfg_attrib = cfg_descr.bmAttributes; *pwr_states = 0; /* * Now start creating the pm-components strings */ drvname = (char *)ddi_driver_name(dip); (void) snprintf(str, USBA_POWER_STR_SIZE, "NAME= %s%d Power", drvname, ddi_get_instance(dip)); pm_comp[n_prop] = kmem_zalloc(strlen(str) + 1, KM_SLEEP); (void) strcpy(pm_comp[n_prop++], str); /* * if the device is bus powered we look at the bBusPowerSavingDx * fields else we look at bSelfPowerSavingDx fields. * OS and USB power states are numerically reversed, * * Here is the mapping :- * OS State USB State * 0 D3 (minimal or no power) * 1 D2 * 2 D1 * 3 D0 (Full power) * * if we own the whole device, we look at the config pwr descr * else at the interface pwr descr. */ if (usb_owns_device(dip)) { /* Parse the configuration power descriptor */ rval = usba_parse_cfg_pwr_descr(usb_cfg, cfg_length, &confpwr_descr, USBA_CFG_PWR_DESCR_SIZE); if (rval != USBA_CFG_PWR_DESCR_SIZE) { USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle, "usb_create_pm_components: " "usb_parse_cfg_pwr_descr returns length of %d, " "expecting %d", rval, USBA_CFG_PWR_DESCR_SIZE); return (USB_FAILURE); } if (cfg_attrib & USB_CFG_ATTR_SELFPWR) { ptr = &confpwr_descr.bSelfPowerSavingD3; } else { ptr = &confpwr_descr.bBusPowerSavingD3; } } 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); if (rval != USBA_IF_PWR_DESCR_SIZE) { USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle, "usb_create_pm_components: " "usb_parse_if_pwr_descr " "returns length of %d, " "expecting %d", rval, USBA_CFG_PWR_DESCR_SIZE); return (USB_FAILURE); } if (cfg_attrib & USB_CFG_ATTR_SELFPWR) { ptr = &ifpwr_descr.bSelfPowerSavingD3; } else { ptr = &ifpwr_descr.bBusPowerSavingD3; } } /* walk thru levels and create prop level=name strings */ for (lvl = USB_DEV_OS_PWR_0; lvl <= USB_DEV_OS_PWR_3; lvl++) { if (*ptr || (lvl == USB_DEV_OS_PWR_3)) { (void) snprintf(str, USBA_POWER_STR_SIZE, "%d=USB D%d State", lvl, USB_DEV_OS_PWR2USB_PWR(lvl)); pm_comp[n_prop] = kmem_zalloc(strlen(str) + 1, KM_SLEEP); (void) strcpy(pm_comp[n_prop++], str); *pwr_states |= USB_DEV_PWRMASK(lvl); } ptr -= 2; /* skip to the next power state */ } USB_DPRINTF_L3(DPRINT_MASK_USBAI, usbai_log_handle, "usb_create_pm_components: pwr_states: %x", *pwr_states); /* now create the actual components */ rval = ddi_prop_update_string_array(DDI_DEV_T_NONE, dip, "pm-components", pm_comp, n_prop); if (rval == DDI_PROP_SUCCESS) { rval = USB_SUCCESS; } else { rval = USB_FAILURE; } /* display & delete properties */ USB_DPRINTF_L3(DPRINT_MASK_USBAI, usbai_log_handle, "usb_create_pm_components: The properties are:"); for (i = 0; i < n_prop; i++) { USB_DPRINTF_L3(DPRINT_MASK_USBAI, usbai_log_handle, "\t%s", pm_comp[i]); kmem_free(pm_comp[i], strlen(pm_comp[i]) + 1); } return (rval); }
static int uftdi_param2regs(uftdi_state_t *uf, ds_port_params_t *tp, uftdi_regs_t *ur) { ds_port_param_entry_t *pe; int i; ur->ur_data = 0; ur->ur_flowval = 0; ur->ur_flowidx = FTDI_SIO_DISABLE_FLOW_CTRL << 8; for (i = 0, pe = tp->tp_entries; i < tp->tp_cnt; i++, pe++) { switch (pe->param) { case DS_PARAM_BAUD: switch (pe->val.ui) { case B300: ur->ur_baud = ftdi_8u232am_b300; break; case B600: ur->ur_baud = ftdi_8u232am_b600; break; case B1200: ur->ur_baud = ftdi_8u232am_b1200; break; case B2400: ur->ur_baud = ftdi_8u232am_b2400; break; case B4800: ur->ur_baud = ftdi_8u232am_b4800; break; case B9600: ur->ur_baud = ftdi_8u232am_b9600; break; case B19200: ur->ur_baud = ftdi_8u232am_b19200; break; case B38400: ur->ur_baud = ftdi_8u232am_b38400; break; case B57600: ur->ur_baud = ftdi_8u232am_b57600; break; case B115200: ur->ur_baud = ftdi_8u232am_b115200; break; case B230400: ur->ur_baud = ftdi_8u232am_b230400; break; case B460800: ur->ur_baud = ftdi_8u232am_b460800; break; case B921600: ur->ur_baud = ftdi_8u232am_b921600; break; default: USB_DPRINTF_L3(DPRINT_CTLOP, uf->uf_lh, "uftdi_param2regs: bad baud %d", pe->val.ui); return (USB_FAILURE); } break; case DS_PARAM_PARITY: if (pe->val.ui & PARENB) { if (pe->val.ui & PARODD) ur->ur_data |= FTDI_SIO_SET_DATA_PARITY_ODD; else ur->ur_data |= FTDI_SIO_SET_DATA_PARITY_EVEN; } else { /* LINTED [E_EXPR_NULL_EFFECT] */ ur->ur_data |= FTDI_SIO_SET_DATA_PARITY_NONE; } break; case DS_PARAM_STOPB: if (pe->val.ui & CSTOPB) ur->ur_data |= FTDI_SIO_SET_DATA_STOP_BITS_2; else { /* LINTED [E_EXPR_NULL_EFFECT] */ ur->ur_data |= FTDI_SIO_SET_DATA_STOP_BITS_1; } break; case DS_PARAM_CHARSZ: switch (pe->val.ui) { case CS5: ur->ur_data |= FTDI_SIO_SET_DATA_BITS(5); break; case CS6: ur->ur_data |= FTDI_SIO_SET_DATA_BITS(6); break; case CS7: ur->ur_data |= FTDI_SIO_SET_DATA_BITS(7); break; case CS8: default: ur->ur_data |= FTDI_SIO_SET_DATA_BITS(8); break; } break; case DS_PARAM_XON_XOFF: /* Software flow control */ if ((pe->val.ui & IXON) || (pe->val.ui & IXOFF)) { uint8_t xonc = pe->val.uc[0]; uint8_t xoffc = pe->val.uc[1]; ur->ur_flowval = (xoffc << 8) | xonc; ur->ur_flowidx = FTDI_SIO_XON_XOFF_HS << 8; } break; case DS_PARAM_FLOW_CTL: /* Hardware flow control */ if (pe->val.ui & (RTSXOFF | CTSXON)) { ur->ur_flowval = 0; ur->ur_flowidx = FTDI_SIO_RTS_CTS_HS << 8; } if (pe->val.ui & DTRXOFF) { ur->ur_flowval = 0; ur->ur_flowidx = FTDI_SIO_DTR_DSR_HS << 8; } break; default: USB_DPRINTF_L2(DPRINT_CTLOP, uf->uf_lh, "uftdi_param2regs: bad param %d", pe->param); break; } } return (USB_SUCCESS); }
/* * ds_attach */ static int uftdi_attach(ds_attach_info_t *aip) { uftdi_state_t *uf; usb_dev_descr_t *dd; int recognized; uf = kmem_zalloc(sizeof (*uf), KM_SLEEP); uf->uf_dip = aip->ai_dip; uf->uf_usb_events = aip->ai_usb_events; *aip->ai_hdl = (ds_hdl_t)uf; /* only one port */ *aip->ai_port_cnt = 1; if (usb_client_attach(uf->uf_dip, USBDRV_VERSION, 0) != USB_SUCCESS) { uftdi_cleanup(uf, 1); return (USB_FAILURE); } if (usb_get_dev_data(uf->uf_dip, &uf->uf_dev_data, USB_PARSE_LVL_IF, 0) != USB_SUCCESS) { uftdi_cleanup(uf, 2); return (USB_FAILURE); } uf->uf_hwport = FTDI_PIT_SIOA + uf->uf_dev_data->dev_curr_if; mutex_init(&uf->uf_lock, NULL, MUTEX_DRIVER, uf->uf_dev_data->dev_iblock_cookie); cv_init(&uf->uf_tx_cv, NULL, CV_DRIVER, NULL); uf->uf_lh = usb_alloc_log_hdl(uf->uf_dip, "uftdi", &uftdi_errlevel, &uftdi_errmask, &uftdi_instance_debug, 0); /* * This device and its clones has numerous physical instantiations. */ recognized = B_TRUE; dd = uf->uf_dev_data->dev_descr; switch (dd->idVendor) { case USB_VENDOR_FTDI: switch (dd->idProduct) { case USB_PRODUCT_FTDI_SERIAL_8U232AM: case USB_PRODUCT_FTDI_SEMC_DSS20: case USB_PRODUCT_FTDI_CFA_631: case USB_PRODUCT_FTDI_CFA_632: case USB_PRODUCT_FTDI_CFA_633: case USB_PRODUCT_FTDI_CFA_634: case USB_PRODUCT_FTDI_CFA_635: case USB_PRODUCT_FTDI_USBSERIAL: case USB_PRODUCT_FTDI_MX2_3: case USB_PRODUCT_FTDI_MX4_5: case USB_PRODUCT_FTDI_LK202: case USB_PRODUCT_FTDI_LK204: case USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M: case USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S: case USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U: case USB_PRODUCT_FTDI_EISCOU: case USB_PRODUCT_FTDI_UOPTBR: case USB_PRODUCT_FTDI_EMCU2D: case USB_PRODUCT_FTDI_PCMSFU: case USB_PRODUCT_FTDI_EMCU2H: break; default: recognized = B_FALSE; break; } break; case USB_VENDOR_SIIG2: switch (dd->idProduct) { case USB_PRODUCT_SIIG2_US2308: break; default: recognized = B_FALSE; break; } break; case USB_VENDOR_INTREPIDCS: switch (dd->idProduct) { case USB_PRODUCT_INTREPIDCS_VALUECAN: case USB_PRODUCT_INTREPIDCS_NEOVI: break; default: recognized = B_FALSE; break; } break; case USB_VENDOR_BBELECTRONICS: switch (dd->idProduct) { case USB_PRODUCT_BBELECTRONICS_USOTL4: break; default: recognized = B_FALSE; break; } break; case USB_VENDOR_MELCO: switch (dd->idProduct) { case USB_PRODUCT_MELCO_PCOPRS1: break; default: recognized = B_FALSE; break; } break; case USB_VENDOR_MARVELL: switch (dd->idProduct) { case USB_PRODUCT_MARVELL_SHEEVAPLUG_JTAG: break; default: recognized = B_FALSE; break; } break; default: recognized = B_FALSE; break; } /* * Set 'uftdi_attach_unrecognized' to non-zero to * experiment with newer devices .. */ if (!recognized && !uftdi_attach_unrecognized) { uftdi_cleanup(uf, 3); return (USB_FAILURE); } USB_DPRINTF_L3(DPRINT_ATTACH, uf->uf_lh, "uftdi: matched vendor 0x%x product 0x%x port %d", dd->idVendor, dd->idProduct, uf->uf_hwport); uf->uf_def_ph = uf->uf_dev_data->dev_default_ph; mutex_enter(&uf->uf_lock); uf->uf_dev_state = USB_DEV_ONLINE; uf->uf_port_state = UFTDI_PORT_CLOSED; mutex_exit(&uf->uf_lock); if (uftdi_create_pm_components(uf) != USB_SUCCESS) { uftdi_cleanup(uf, 3); return (USB_FAILURE); } if (usb_register_event_cbs(uf->uf_dip, uf->uf_usb_events, 0) != USB_SUCCESS) { uftdi_cleanup(uf, 4); return (USB_FAILURE); } if (usb_pipe_get_max_bulk_transfer_size(uf->uf_dip, &uf->uf_xfer_sz) != USB_SUCCESS) { uftdi_cleanup(uf, 5); return (USB_FAILURE); } /* * TODO: modern ftdi devices have deeper (and asymmetric) * fifos than this minimal 64 bytes .. but how to tell * -safely- ? */ #define FTDI_MAX_XFERSIZE 64 if (uf->uf_xfer_sz > FTDI_MAX_XFERSIZE) uf->uf_xfer_sz = FTDI_MAX_XFERSIZE; if (uftdi_dev_attach(uf) != USB_SUCCESS) { uftdi_cleanup(uf, 5); return (USB_FAILURE); } return (USB_SUCCESS); }
/*ARGSUSED*/ static void uftdi_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) { uftdi_state_t *uf = (uftdi_state_t *)req->bulk_client_private; mblk_t *data; int data_len; data = req->bulk_data; data_len = data ? MBLKL(data) : 0; /* * The first two bytes of data are status register bytes * that arrive with every packet from the device. Process * them here before handing the rest of the data on. * * When active, the device will send us these bytes at least * every 40 milliseconds, even if there's no received data. */ if (req->bulk_completion_reason == USB_CR_OK && data_len >= 2) { uint8_t msr = FTDI_GET_MSR(data->b_rptr); uint8_t lsr = FTDI_GET_LSR(data->b_rptr); int new_rx_err; data->b_rptr += 2; mutex_enter(&uf->uf_lock); if (uf->uf_msr != msr) { /* * modem status register changed */ USB_DPRINTF_L3(DPRINT_IN_PIPE, uf->uf_lh, "uftdi_bulkin_cb: new msr: 0x%02x -> 0x%02x", uf->uf_msr, msr); uf->uf_msr = msr; if (uf->uf_port_state == UFTDI_PORT_OPEN && uf->uf_cb.cb_status) { mutex_exit(&uf->uf_lock); uf->uf_cb.cb_status(uf->uf_cb.cb_arg); mutex_enter(&uf->uf_lock); } } if ((uf->uf_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK)) { /* * line status register *receive* bits changed * * (The THRE and TEMT (transmit) status bits are * masked out above.) */ USB_DPRINTF_L3(DPRINT_IN_PIPE, uf->uf_lh, "uftdi_bulkin_cb: new lsr: 0x%02x -> 0x%02x", uf->uf_lsr, lsr); new_rx_err = B_TRUE; } else new_rx_err = B_FALSE; uf->uf_lsr = lsr; /* THRE and TEMT captured here */ if ((lsr & FTDI_LSR_MASK) != 0 && (MBLKL(data) > 0 || new_rx_err) && uf->uf_port_state == UFTDI_PORT_OPEN) { /* * The current line status register value indicates * that there's been some sort of unusual condition * on the receive side. We either received a break, * or got some badly formed characters from the * serial port - framing errors, overrun, parity etc. * So there's either some new data to post, or a * new error (break) to post, or both. * * Invoke uftdi_rxerr_put() to place the inbound * characters as M_BREAK messages on the receive * mblk chain, decorated with error flag(s) for * upper-level modules (e.g. ldterm) to process. */ mutex_exit(&uf->uf_lock); uftdi_rxerr_put(&uf->uf_rx_mp, data, lsr); ASSERT(MBLKL(data) == 0); /* * Since we've converted all the received * characters into M_BREAK messages, we * invoke the rx callback to shove the mblks * up the STREAM. */ if (uf->uf_cb.cb_rx) uf->uf_cb.cb_rx(uf->uf_cb.cb_arg); mutex_enter(&uf->uf_lock); } mutex_exit(&uf->uf_lock); data_len = MBLKL(data); } USB_DPRINTF_L4(DPRINT_IN_PIPE, uf->uf_lh, "uftdi_bulkin_cb: " "cr=%d len=%d", req->bulk_completion_reason, data_len); /* save data and notify GSD */ if (data_len > 0 && uf->uf_port_state == UFTDI_PORT_OPEN && req->bulk_completion_reason == USB_CR_OK) { req->bulk_data = NULL; uftdi_put_tail(&uf->uf_rx_mp, data); if (uf->uf_cb.cb_rx) uf->uf_cb.cb_rx(uf->uf_cb.cb_arg); } usb_free_bulk_req(req); /* receive more */ mutex_enter(&uf->uf_lock); uf->uf_bulkin_state = UFTDI_PIPE_IDLE; if (uf->uf_port_state == UFTDI_PORT_OPEN && uf->uf_dev_state == USB_DEV_ONLINE) { if (uftdi_rx_start(uf) != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_IN_PIPE, uf->uf_lh, "uftdi_bulkin_cb: restart rx fail"); } } mutex_exit(&uf->uf_lock); }