Exemplo n.º 1
0
/* 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);
}
Exemplo n.º 3
0
/*
 * 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);
}
Exemplo n.º 4
0
/*
 * 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;
	}
}
Exemplo n.º 5
0
/*
 * 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);
}
Exemplo n.º 6
0
/* 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);
	}
}
Exemplo n.º 7
0
/* 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);
}
Exemplo n.º 8
0
/* 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);
}
Exemplo n.º 9
0
/*
 * 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);
}
Exemplo n.º 11
0
/*
 * 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);
}
Exemplo n.º 12
0
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);
}
Exemplo n.º 13
0
/*
 * 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);
}
Exemplo n.º 14
0
/*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);
}