Пример #1
0
/*
 * 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);
}
Пример #2
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;
}
Пример #3
0
/* 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);
}
Пример #4
0
/*
 * 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");

}
Пример #5
0
/*
 * 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);
}
Пример #6
0
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);
	}
}
Пример #7
0
/*
 * 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);
	}
}
Пример #8
0
/*
 * 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");
	}
}
Пример #9
0
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);
}
Пример #10
0
/*
 * 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");

}
Пример #11
0
/*
 * 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);
}
Пример #12
0
/*
 * 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);
}
Пример #13
0
/*
 * 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);
}
Пример #14
0
/*
 * 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);
}
Пример #15
0
/*
 * 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);
}
Пример #16
0
/*
 * 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);
}
Пример #18
0
/*
 * 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);
}
Пример #19
0
/*
 * 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);
}
Пример #20
0
/*
 * 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);
}
Пример #21
0
/*
 * 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);
}
Пример #22
0
/*
 * 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);
}
Пример #24
0
/*
 * 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);
}
Пример #26
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);
}
Пример #27
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);
}
Пример #28
0
/*
 * 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);
}
Пример #29
0
/*
 * 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;
}