6817700 EHCI/OHCI need support polled I/O for usbserial console
authorzhigang lu - Sun Microsystems - Beijing China <Zhigang.Lu@Sun.COM>
Thu, 19 Mar 2009 14:02:20 +0800
changeset 9095 ef4ae1685182
parent 9094 77f003128c4d
child 9096 32c8cbba4262
6817700 EHCI/OHCI need support polled I/O for usbserial console
usr/src/uts/common/io/usb/hcd/ehci/ehci_polled.c
usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c
usr/src/uts/common/io/usb/hcd/openhci/ohci.c
usr/src/uts/common/io/usb/hcd/openhci/ohci_polled.c
usr/src/uts/common/io/warlock/ehci.wlcmd
usr/src/uts/common/io/warlock/ohci.wlcmd
usr/src/uts/common/sys/usb/hcd/ehci/ehci_polled.h
usr/src/uts/common/sys/usb/hcd/ehci/ehcid.h
usr/src/uts/common/sys/usb/hcd/openhci/ohci_polled.h
usr/src/uts/common/sys/usb/hcd/openhci/ohcid.h
--- a/usr/src/uts/common/io/usb/hcd/ehci/ehci_polled.c	Thu Mar 19 10:21:31 2009 +0800
+++ b/usr/src/uts/common/io/usb/hcd/ehci/ehci_polled.c	Thu Mar 19 14:02:20 2009 +0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -42,6 +42,10 @@
 #include <sys/usb/hcd/ehci/ehci_util.h>
 #include <sys/usb/hcd/ehci/ehci_polled.h>
 
+#ifndef __sparc
+extern void invalidate_cache();
+#endif
+
 /*
  * Internal Function Prototypes
  */
@@ -71,9 +75,11 @@
 static int	ehci_polled_handle_normal_qtd(
 				ehci_polled_t		*ehci_polledp,
 				ehci_qtd_t		*qtd);
-static void	ehci_polled_insert_qtd(
+static void	ehci_polled_insert_intr_qtd(
 				ehci_polled_t		*ehci_polledp,
 				ehci_qtd_t		*qtd);
+static void	ehci_polled_insert_bulk_qtd(
+				ehci_polled_t		*ehci_polledp);
 static void	ehci_polled_fill_in_qtd(
 				ehci_state_t		*ehcip,
 				ehci_qtd_t		*qtd,
@@ -99,6 +105,16 @@
 static void	ehci_polled_finish_interrupt(
 				ehci_state_t		*ehcip,
 				uint_t			intr);
+static int	ehci_polled_create_tw(
+				ehci_polled_t		*ehci_polledp,
+				usba_pipe_handle_data_t	*ph,
+				usb_flags_t		usb_flags);
+static void	ehci_polled_insert_async_qh(
+				ehci_state_t		*ehcip,
+				ehci_pipe_private_t	*pp);
+static void	ehci_polled_remove_async_qh(
+				ehci_state_t		*ehcip,
+				ehci_pipe_private_t	*pp);
 
 /*
  * POLLED entry points
@@ -109,7 +125,7 @@
 /*
  * ehci_hcdi_polled_input_init:
  *
- * This is the initialization routine for handling the USB keyboard
+ * This is the initialization routine for handling the USB input device
  * in POLLED mode.  This routine is not called from POLLED mode, so
  * it is OK to acquire mutexes.
  */
@@ -224,11 +240,25 @@
 ehci_hcdi_polled_input_enter(usb_console_info_impl_t *info)
 {
 	ehci_polled_t		*ehci_polledp;
+	ehci_state_t		*ehcip;
+	usba_pipe_handle_data_t	*ph;
+	ehci_pipe_private_t	*pp;
+	int			pipe_attr;
 
+#ifndef lint
+	_NOTE(NO_COMPETING_THREADS_NOW);
+#endif
 	ehci_polledp = (ehci_polled_t *)info->uci_private;
+	ehcip = ehci_polledp->ehci_polled_ehcip;
+	ph = ehci_polledp->ehci_polled_input_pipe_handle;
+	pp = (ehci_pipe_private_t *)ph->p_hcd_private;
+
+	pipe_attr = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
+#ifndef lint
+	_NOTE(COMPETING_THREADS_NOW);
+#endif
 
 	ehci_polledp->ehci_polled_entry++;
-
 	/*
 	 * If the controller is already switched over, just return
 	 */
@@ -237,7 +267,33 @@
 		return (USB_SUCCESS);
 	}
 
-	ehci_polled_save_state(ehci_polledp);
+	switch (pipe_attr) {
+	case USB_EP_ATTR_INTR:
+		ehci_polled_save_state(ehci_polledp);
+		break;
+	case USB_EP_ATTR_BULK:
+#ifndef lint
+	_NOTE(NO_COMPETING_THREADS_NOW);
+#endif
+		Set_OpReg(ehci_command, (Get_OpReg(ehci_command) &
+		    ~(EHCI_CMD_PERIODIC_SCHED_ENABLE |
+		    EHCI_CMD_ASYNC_SCHED_ENABLE)));
+		/* Wait for few milliseconds */
+		drv_usecwait(EHCI_POLLED_TIMEWAIT);
+
+		ehci_polled_insert_async_qh(ehcip, pp);
+
+		Set_OpReg(ehci_command,
+		    (Get_OpReg(ehci_command) | EHCI_CMD_ASYNC_SCHED_ENABLE));
+#ifndef lint
+	_NOTE(COMPETING_THREADS_NOW);
+#endif
+		/* Wait for few milliseconds */
+		drv_usecwait(EHCI_POLLED_TIMEWAIT);
+		break;
+	default:
+		return (USB_FAILURE);
+	}
 
 	ehci_polledp->ehci_polled_flags |= POLLED_INPUT_MODE_INUSE;
 
@@ -255,8 +311,23 @@
 ehci_hcdi_polled_input_exit(usb_console_info_impl_t *info)
 {
 	ehci_polled_t		*ehci_polledp;
+	ehci_state_t		*ehcip;
+	ehci_pipe_private_t	*pp;
+	int			pipe_attr;
 
+#ifndef lint
+	_NOTE(NO_COMPETING_THREADS_NOW);
+#endif
 	ehci_polledp = (ehci_polled_t *)info->uci_private;
+	ehcip = ehci_polledp->ehci_polled_ehcip;
+	pp = (ehci_pipe_private_t *)ehci_polledp->
+	    ehci_polled_input_pipe_handle->p_hcd_private;
+
+	pipe_attr = ehci_polledp->ehci_polled_input_pipe_handle->
+	    p_ep.bmAttributes & USB_EP_ATTR_MASK;
+#ifndef lint
+	_NOTE(COMPETING_THREADS_NOW);
+#endif
 
 	ehci_polledp->ehci_polled_entry--;
 
@@ -270,7 +341,35 @@
 
 	ehci_polledp->ehci_polled_flags &= ~POLLED_INPUT_MODE_INUSE;
 
-	ehci_polled_restore_state(ehci_polledp);
+	switch (pipe_attr & USB_EP_ATTR_MASK) {
+	case USB_EP_ATTR_INTR:
+		ehci_polled_restore_state(ehci_polledp);
+		break;
+	case USB_EP_ATTR_BULK:
+#ifndef lint
+	_NOTE(NO_COMPETING_THREADS_NOW);
+#endif
+		Set_OpReg(ehci_command, (Get_OpReg(ehci_command) &
+		    ~(EHCI_CMD_PERIODIC_SCHED_ENABLE |
+		    EHCI_CMD_ASYNC_SCHED_ENABLE)));
+		/* Wait for few milliseconds */
+		drv_usecwait(EHCI_POLLED_TIMEWAIT);
+
+		ehci_polled_remove_async_qh(ehcip, pp);
+
+		Set_OpReg(ehci_command,
+		    (Get_OpReg(ehci_command) | EHCI_CMD_ASYNC_SCHED_ENABLE |
+		    EHCI_CMD_ASYNC_SCHED_ENABLE));
+#ifndef lint
+	_NOTE(COMPETING_THREADS_NOW);
+#endif
+		/* Wait for few milliseconds */
+		drv_usecwait(EHCI_POLLED_TIMEWAIT);
+		break;
+	default:
+		return (USB_FAILURE);
+
+	}
 
 	return (USB_SUCCESS);
 }
@@ -289,6 +388,7 @@
 	ehci_state_t		*ehcip;
 	ehci_polled_t		*ehci_polledp;
 	uint_t			intr;
+	int			pipe_attr;
 
 	ehci_polledp = (ehci_polled_t *)info->uci_private;
 
@@ -300,6 +400,13 @@
 
 	*num_characters = 0;
 
+	pipe_attr = ehci_polledp->ehci_polled_input_pipe_handle->
+	    p_ep.bmAttributes & USB_EP_ATTR_MASK;
+
+	if (pipe_attr == USB_EP_ATTR_BULK) {
+		ehci_polled_insert_bulk_qtd(ehci_polledp);
+	}
+
 	intr = ((Get_OpReg(ehci_status) & Get_OpReg(ehci_interrupt)) &
 	    (EHCI_INTR_FRAME_LIST_ROLLOVER |
 	    EHCI_INTR_USB | EHCI_INTR_USB_ERROR));
@@ -315,22 +422,240 @@
 		    EHCI_INTR_FRAME_LIST_ROLLOVER);
 	}
 
+	/* Process any QTD's on the active interrupt qtd list */
+	*num_characters =
+	    ehci_polled_process_active_intr_qtd_list(ehci_polledp);
+
+#ifndef lint
+	_NOTE(COMPETING_THREADS_NOW);
+#endif
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * ehci_hcdi_polled_output_init:
+ *
+ * This is the initialization routine for handling the USB serial output
+ * in POLLED mode.  This routine is not called from POLLED mode, so
+ * it is OK to acquire mutexes.
+ */
+int
+ehci_hcdi_polled_output_init(
+	usba_pipe_handle_data_t	*ph,
+	usb_console_info_impl_t	*console_output_info)
+{
+	ehci_polled_t		*ehci_polledp;
+	ehci_state_t		*ehcip;
+	ehci_pipe_private_t	*pp;
+	int			ret;
+
+	ehcip = ehci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
+
+	/*
+	 * Grab the ehci_int_mutex so that things don't change on us
+	 * if an interrupt comes in.
+	 */
+	mutex_enter(&ehcip->ehci_int_mutex);
+
+	ret = ehci_polled_init(ph, ehcip, console_output_info);
+
+	if (ret != USB_SUCCESS) {
+
+		/* Allow interrupts to continue */
+		mutex_exit(&ehcip->ehci_int_mutex);
+
+		return (ret);
+	}
+
+	ehci_polledp = (ehci_polled_t *)console_output_info->uci_private;
+	/*
+	 * Mark the structure so that if we are using it, we don't free
+	 * the structures if one of them is unplugged.
+	 */
+	ehci_polledp->ehci_polled_flags |= POLLED_OUTPUT_MODE;
+
+	/*
+	 * Insert the Endpoint Descriptor to appropriate endpoint list.
+	 */
+	pp = (ehci_pipe_private_t *)ehci_polledp->
+	    ehci_polled_input_pipe_handle->p_hcd_private;
+	ehci_polled_insert_async_qh(ehcip, pp);
+
+	/*
+	 * This is a software workaround to fix schizo hardware bug.
+	 * Existence of "no-prom-cdma-sync"  property means consistent
+	 * dma sync should not be done while in prom or polled mode.
+	 */
+	if (ddi_prop_exists(DDI_DEV_T_ANY, ehcip->ehci_dip,
+	    DDI_PROP_NOTPROM, "no-prom-cdma-sync")) {
+		ehci_polledp->ehci_polled_no_sync_flag = B_TRUE;
+	}
+
+	/* Allow interrupts to continue */
+	mutex_exit(&ehcip->ehci_int_mutex);
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * ehci_hcdi_polled_output_fini:
+ */
+int
+ehci_hcdi_polled_output_fini(usb_console_info_impl_t *info)
+{
+	ehci_polled_t		*ehci_polledp;
+	ehci_state_t		*ehcip;
+	ehci_pipe_private_t	*pp;
+	int			ret;
+
+	ehci_polledp = (ehci_polled_t *)info->uci_private;
+
+	ehcip = ehci_polledp->ehci_polled_ehcip;
+
+	mutex_enter(&ehcip->ehci_int_mutex);
+
+	/* Remove the Endpoint Descriptor. */
+	pp = (ehci_pipe_private_t *)ehci_polledp->
+	    ehci_polled_input_pipe_handle->p_hcd_private;
+	ehci_polled_remove_async_qh(ehcip, pp);
+
+	/*
+	 * Reset the POLLED_INPUT_MODE flag so that we can tell if
+	 * this structure is in use in the ehci_polled_fini routine.
+	 */
+	ehci_polledp->ehci_polled_flags &= ~POLLED_OUTPUT_MODE;
+
+	ret = ehci_polled_fini(ehci_polledp);
+
+	info->uci_private = NULL;
+
+	mutex_exit(&ehcip->ehci_int_mutex);
+
+	return (ret);
+}
+
+
+/*
+ * ehci_hcdi_polled_output_enter:
+ *
+ * everything is done in input enter
+ */
+/*ARGSUSED*/
+int
+ehci_hcdi_polled_output_enter(usb_console_info_impl_t *info)
+{
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * ehci_hcdi_polled_output_exit:
+ *
+ * everything is done in input exit
+ */
+/*ARGSUSED*/
+int
+ehci_hcdi_polled_output_exit(usb_console_info_impl_t *info)
+{
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * ehci_hcdi_polled_write:
+ *	Put a key character.
+ */
+int
+ehci_hcdi_polled_write(usb_console_info_impl_t *info, uchar_t *buf,
+    uint_t num_characters, uint_t *num_characters_written)
+{
+	ehci_state_t		*ehcip;
+	ehci_polled_t		*ehci_polledp;
+	ehci_trans_wrapper_t	*tw;
+	ehci_pipe_private_t	*pp;
+	usba_pipe_handle_data_t	*ph;
+	int			intr;
+
+#ifndef lint
+	_NOTE(NO_COMPETING_THREADS_NOW);
+#endif
+	ehci_polledp = (ehci_polled_t *)info->uci_private;
+	ehcip = ehci_polledp->ehci_polled_ehcip;
+	ph = ehci_polledp->ehci_polled_input_pipe_handle;
+	pp = (ehci_pipe_private_t *)ph->p_hcd_private;
+
+	/* Disable all list processing */
+	Set_OpReg(ehci_command, Get_OpReg(ehci_command) &
+	    ~(EHCI_CMD_ASYNC_SCHED_ENABLE |
+	    EHCI_CMD_PERIODIC_SCHED_ENABLE));
+
+	/* Wait for few milliseconds */
+	drv_usecwait(EHCI_POLLED_TIMEWAIT);
+
+	tw = pp->pp_tw_head;
+	ASSERT(tw != NULL);
+
+	/* copy transmit buffer */
+	if (num_characters > POLLED_RAW_BUF_SIZE) {
+		cmn_err(CE_NOTE, "polled write size %d bigger than %d",
+		    num_characters, POLLED_RAW_BUF_SIZE);
+		num_characters = POLLED_RAW_BUF_SIZE;
+	}
+	tw->tw_length = num_characters;
+	ddi_rep_put8(tw->tw_accesshandle,
+	    buf, (uint8_t *)tw->tw_buf,
+	    tw->tw_length, DDI_DEV_AUTOINCR);
+	Sync_IO_Buffer_for_device(tw->tw_dmahandle, tw->tw_length);
+
+	ehci_polled_insert_bulk_qtd(ehci_polledp);
+
+	/* Enable async list processing */
+	Set_OpReg(ehci_command, (Get_OpReg(ehci_command) |
+	    EHCI_CMD_ASYNC_SCHED_ENABLE));
+
+	/* Wait for few milliseconds */
+	drv_usecwait(EHCI_POLLED_TIMEWAIT);
+
+	while (!((Get_OpReg(ehci_status)) & (EHCI_INTR_USB
+	    |EHCI_INTR_FRAME_LIST_ROLLOVER | EHCI_INTR_USB_ERROR))) {
+#ifndef __sparc
+		invalidate_cache();
+#else
+		;
+#endif
+	}
+
+	intr = (Get_OpReg(ehci_status)) &
+	    (EHCI_INTR_FRAME_LIST_ROLLOVER |
+	    EHCI_INTR_USB | EHCI_INTR_USB_ERROR);
+
+	/*
+	 * Check whether any frame list rollover interrupt is pending
+	 * and if it is pending, process this interrupt.
+	 */
+	if (intr & EHCI_INTR_FRAME_LIST_ROLLOVER) {
+
+		ehci_handle_frame_list_rollover(ehcip);
+		ehci_polled_finish_interrupt(ehcip,
+		    EHCI_INTR_FRAME_LIST_ROLLOVER);
+	}
+
 	/* Check for any USB transaction completion notification */
 	if (intr & (EHCI_INTR_USB | EHCI_INTR_USB_ERROR)) {
-		ehcip->ehci_polled_read_count ++;
-		/* Process any QTD's on the active interrupt qtd list */
-		*num_characters =
-		    ehci_polled_process_active_intr_qtd_list(ehci_polledp);
+
+		(void) ehci_polled_process_active_intr_qtd_list(ehci_polledp);
 
-		if (ehcip->ehci_polled_read_count ==
-		    ehcip->ehci_polled_enter_count) {
-			/* Acknowledge the frame list rollover interrupt */
-			ehci_polled_finish_interrupt(ehcip,
-			    intr & (EHCI_INTR_USB | EHCI_INTR_USB_ERROR));
-			ehcip->ehci_polled_read_count = 0;
-		}
+		/* Acknowledge the USB and USB error interrupt */
+		ehci_polled_finish_interrupt(ehcip,
+		    intr & (EHCI_INTR_USB | EHCI_INTR_USB_ERROR));
+
 	}
 
+	*num_characters_written = num_characters;
+
 #ifndef lint
 	_NOTE(COMPETING_THREADS_NOW);
 #endif
@@ -363,6 +688,7 @@
 	ehci_polled_t		*ehci_polledp;
 	ehci_pipe_private_t	*pp;
 	ehci_qtd_t		*qtd;
+	int			pipe_attr;
 
 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
 
@@ -465,8 +791,8 @@
 	}
 
 	/*
-	 * Allocate the interrupt endpoint. This QH will be inserted in
-	 * to the lattice chain for the  keyboard device. This endpoint
+	 * Allocate the endpoint. This QH will be inserted in
+	 * to the lattice chain for the device. This endpoint
 	 * will have the QTDs hanging off of it for the processing.
 	 */
 	ehci_polledp->ehci_polled_qh = ehci_alloc_qh(
@@ -486,33 +812,50 @@
 	/* Insert the endpoint onto the pipe handle */
 	pp->pp_qh = ehci_polledp->ehci_polled_qh;
 
-	/*
-	 * Set soft interrupt handler flag in the normal mode usb
-	 * pipe handle.
-	 */
-	mutex_enter(&ph->p_mutex);
-	ph->p_spec_flag |= USBA_PH_FLAG_USE_SOFT_INTR;
-	mutex_exit(&ph->p_mutex);
+	pipe_attr = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
+
+	switch (pipe_attr) {
+	case USB_EP_ATTR_INTR:
+		/*
+		 * Set soft interrupt handler flag in the normal mode usb
+		 * pipe handle.
+		 */
+		mutex_enter(&ph->p_mutex);
+		ph->p_spec_flag |= USBA_PH_FLAG_USE_SOFT_INTR;
+		mutex_exit(&ph->p_mutex);
 
-	/*
-	 * Insert a Interrupt polling request onto the endpoint.
-	 *
-	 * There will now be two QTDs on the QH, one is the dummy QTD that
-	 * was allocated above in the  ehci_alloc_qh and this new one.
-	 */
-	if ((ehci_start_periodic_pipe_polling(ehcip,
-	    ehci_polledp->ehci_polled_input_pipe_handle,
-	    NULL, USB_FLAGS_SLEEP)) != USB_SUCCESS) {
+		/*
+		 * Insert a Interrupt polling request onto the endpoint.
+		 *
+		 * There will now be two QTDs on the QH, one is the dummy QTD
+		 * that was allocated above in the  ehci_alloc_qh and this
+		 * new one.
+		 */
+		if ((ehci_start_periodic_pipe_polling(ehcip,
+		    ehci_polledp->ehci_polled_input_pipe_handle,
+		    NULL, USB_FLAGS_SLEEP)) != USB_SUCCESS) {
 
-		return (USB_NO_RESOURCES);
-	}
+			return (USB_NO_RESOURCES);
+		}
+		/* Get the given new interrupt qtd */
+		qtd = (ehci_qtd_t *)(ehci_qtd_iommu_to_cpu(ehcip,
+		    (Get_QH(pp->pp_qh->qh_next_qtd) & EHCI_QH_NEXT_QTD_PTR)));
 
-	/* Get the given new interrupt qtd */
-	qtd = (ehci_qtd_t *)(ehci_qtd_iommu_to_cpu(ehcip,
-	    (Get_QH(pp->pp_qh->qh_next_qtd) & EHCI_QH_NEXT_QTD_PTR)));
+		/* Insert this qtd into active interrupt QTD list */
+		ehci_polled_insert_qtd_into_active_intr_qtd_list(ehci_polledp,
+		    qtd);
+		break;
+	case USB_EP_ATTR_BULK:
+		if ((ehci_polled_create_tw(ehci_polledp,
+		    ehci_polledp->ehci_polled_input_pipe_handle,
+		    USB_FLAGS_SLEEP)) != USB_SUCCESS) {
 
-	/* Insert this qtd into active interrupt QTD list */
-	ehci_polled_insert_qtd_into_active_intr_qtd_list(ehci_polledp, qtd);
+			return (USB_NO_RESOURCES);
+		}
+		break;
+	default:
+		return (USB_FAILURE);
+	}
 
 	return (USB_SUCCESS);
 }
@@ -1017,6 +1360,7 @@
 	ehci_trans_wrapper_t	*tw;
 	ehci_pipe_private_t	*pp;
 	usb_cr_t		error;
+	int			pipe_attr, pipe_dir;
 
 	/* Sync QH and QTD pool */
 	if (ehci_polledp->ehci_polled_no_sync_flag == B_FALSE) {
@@ -1026,6 +1370,10 @@
 	/* Create done qtd list */
 	qtd = ehci_polled_create_done_qtd_list(ehci_polledp);
 
+	pipe_attr = ehci_polledp->ehci_polled_input_pipe_handle->
+	    p_ep.bmAttributes & USB_EP_ATTR_MASK;
+	pipe_dir = ehci_polledp->ehci_polled_input_pipe_handle->
+	    p_ep.bEndpointAddress & USB_EP_DIR_MASK;
 	/*
 	 * Traverse the list of transfer descriptors.  We can't destroy
 	 * the qtd_next pointers of these QTDs because we are using it
@@ -1041,7 +1389,7 @@
 		tw = (ehci_trans_wrapper_t *)EHCI_LOOKUP_ID(
 		    (uint32_t)Get_QTD(qtd->qtd_trans_wrapper));
 
-		/* Get ohci pipe from transfer wrapper */
+		/* Get ehci pipe from transfer wrapper */
 		pp = tw->tw_pipe_private;
 
 		/* Look at the status */
@@ -1055,19 +1403,38 @@
 		 * clear the halt condition in the Endpoint  Descriptor
 		 * (QH) associated with this Transfer  Descriptor (QTD).
 		 */
-		if (error == USB_CR_OK) {
-			num_characters +=
-			    ehci_polled_handle_normal_qtd(ehci_polledp, qtd);
-		} else {
+		if (error != USB_CR_OK) {
 			/* Clear the halt bit */
 			Set_QH(pp->pp_qh->qh_status,
 			    Get_QH(pp->pp_qh->qh_status) &
 			    ~(EHCI_QH_STS_XACT_STATUS));
+		} else if (pipe_dir == USB_EP_DIR_IN) {
+
+			num_characters +=
+			    ehci_polled_handle_normal_qtd(ehci_polledp,
+			    qtd);
 		}
 
 		/* Insert this qtd back into QH's qtd list */
-		ehci_polled_insert_qtd(ehci_polledp, qtd);
-
+		switch (pipe_attr) {
+		case USB_EP_ATTR_INTR:
+			ehci_polled_insert_intr_qtd(ehci_polledp, qtd);
+			break;
+		case USB_EP_ATTR_BULK:
+			if (tw->tw_qtd_free_list != NULL) {
+				uint32_t	td_addr;
+				td_addr = ehci_qtd_cpu_to_iommu(ehcip,
+				    tw->tw_qtd_free_list);
+				Set_QTD(qtd->qtd_tw_next_qtd, td_addr);
+				Set_QTD(qtd->qtd_state, EHCI_QTD_DUMMY);
+				tw->tw_qtd_free_list = qtd;
+			} else {
+				tw->tw_qtd_free_list = qtd;
+				Set_QTD(qtd->qtd_tw_next_qtd, NULL);
+				Set_QTD(qtd->qtd_state, EHCI_QTD_DUMMY);
+			}
+			break;
+		}
 		qtd = next_qtd;
 	}
 
@@ -1126,12 +1493,12 @@
 
 
 /*
- * ehci_polled_insert_qtd:
+ * ehci_polled_insert_intr_qtd:
  *
  * Insert a Transfer Descriptor (QTD) on an Endpoint Descriptor (QH).
  */
 static void
-ehci_polled_insert_qtd(
+ehci_polled_insert_intr_qtd(
 	ehci_polled_t		*ehci_polledp,
 	ehci_qtd_t		*qtd)
 {
@@ -1209,6 +1576,61 @@
 }
 
 
+static void
+ehci_polled_insert_bulk_qtd(
+	ehci_polled_t	*ehci_polledp)
+{
+	ehci_state_t		*ehcip;
+	ehci_pipe_private_t	*pp;
+	ehci_trans_wrapper_t	*tw;
+	ehci_qh_t		*qh;
+	ehci_qtd_t		*new_dummy_qtd;
+	ehci_qtd_t		*curr_dummy_qtd, *next_dummy_qtd;
+	uint_t			qtd_control;
+
+	ehcip = ehci_polledp->ehci_polled_ehcip;
+	pp = (ehci_pipe_private_t *)ehci_polledp->
+	    ehci_polled_input_pipe_handle->p_hcd_private;
+	tw = pp->pp_tw_head;
+	qh = ehci_polledp->ehci_polled_qh;
+	new_dummy_qtd = tw->tw_qtd_free_list;
+
+	if (new_dummy_qtd == NULL) {
+		return;
+	}
+
+	tw->tw_qtd_free_list = ehci_qtd_iommu_to_cpu(ehcip,
+	    Get_QTD(new_dummy_qtd->qtd_tw_next_qtd));
+	Set_QTD(new_dummy_qtd->qtd_tw_next_qtd, NULL);
+
+	/* Get the current and next dummy QTDs */
+	curr_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
+	    Get_QH(qh->qh_dummy_qtd));
+	next_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
+	    Get_QTD(curr_dummy_qtd->qtd_next_qtd));
+
+	/* Update QH's dummy qtd field */
+	Set_QH(qh->qh_dummy_qtd, ehci_qtd_cpu_to_iommu(ehcip, next_dummy_qtd));
+
+	/* Update next dummy's next qtd pointer */
+	Set_QTD(next_dummy_qtd->qtd_next_qtd,
+	    ehci_qtd_cpu_to_iommu(ehcip, new_dummy_qtd));
+
+	qtd_control = (tw->tw_direction | EHCI_QTD_CTRL_INTR_ON_COMPLETE);
+
+	/*
+	 * Fill in the current dummy qtd and
+	 * add the new dummy to the end.
+	 */
+	ehci_polled_fill_in_qtd(ehcip, curr_dummy_qtd, qtd_control,
+	    0, tw->tw_length, tw);
+
+	/* Insert this qtd into active interrupt QTD list */
+	ehci_polled_insert_qtd_into_active_intr_qtd_list(
+	    ehci_polledp, curr_dummy_qtd);
+}
+
+
 /*
  * ehci_polled_fill_in_qtd:
  *
@@ -1570,3 +1992,274 @@
 	 */
 	(void) Get_OpReg(ehci_status);
 }
+
+
+static int
+ehci_polled_create_tw(
+	ehci_polled_t	*ehci_polledp,
+	usba_pipe_handle_data_t	*ph,
+	usb_flags_t	usb_flags)
+{
+	uint_t			ccount;
+	size_t			real_length;
+	ehci_trans_wrapper_t	*tw;
+	ddi_device_acc_attr_t	dev_attr;
+	int			result, pipe_dir, qtd_count;
+	ehci_state_t		*ehcip;
+	ehci_pipe_private_t	*pp;
+	ddi_dma_attr_t		dma_attr;
+
+	ehcip = ehci_polledp->ehci_polled_ehcip;
+	pp = (ehci_pipe_private_t *)ph->p_hcd_private;
+
+	/* Get the required qtd counts */
+	qtd_count = (POLLED_RAW_BUF_SIZE - 1) / EHCI_MAX_QTD_XFER_SIZE + 1;
+
+	if ((tw = kmem_zalloc(sizeof (ehci_trans_wrapper_t),
+	    KM_NOSLEEP)) == NULL) {
+		return (USB_FAILURE);
+	}
+
+	/* allow sg lists for transfer wrapper dma memory */
+	bcopy(&ehcip->ehci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
+	dma_attr.dma_attr_sgllen = EHCI_DMA_ATTR_TW_SGLLEN;
+	dma_attr.dma_attr_align = EHCI_DMA_ATTR_ALIGNMENT;
+
+	/* Allocate the DMA handle */
+	if ((result = ddi_dma_alloc_handle(ehcip->ehci_dip,
+	    &dma_attr, DDI_DMA_DONTWAIT, 0, &tw->tw_dmahandle)) !=
+	    DDI_SUCCESS) {
+		kmem_free(tw, sizeof (ehci_trans_wrapper_t));
+
+		return (USB_FAILURE);
+	}
+
+	dev_attr.devacc_attr_version		= DDI_DEVICE_ATTR_V0;
+	dev_attr.devacc_attr_endian_flags	= DDI_STRUCTURE_LE_ACC;
+	dev_attr.devacc_attr_dataorder		= DDI_STRICTORDER_ACC;
+
+	/* Allocate the memory */
+	if ((result = ddi_dma_mem_alloc(tw->tw_dmahandle, POLLED_RAW_BUF_SIZE,
+	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
+	    &tw->tw_buf, &real_length, &tw->tw_accesshandle)) !=
+	    DDI_SUCCESS) {
+		ddi_dma_free_handle(&tw->tw_dmahandle);
+		kmem_free(tw, sizeof (ehci_trans_wrapper_t));
+
+		return (USB_FAILURE);
+	}
+
+	/* Bind the handle */
+	if ((result = ddi_dma_addr_bind_handle(tw->tw_dmahandle, NULL,
+	    tw->tw_buf, real_length, DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
+	    DDI_DMA_DONTWAIT, NULL, &tw->tw_cookie, &ccount)) !=
+	    DDI_DMA_MAPPED) {
+		ddi_dma_mem_free(&tw->tw_accesshandle);
+		ddi_dma_free_handle(&tw->tw_dmahandle);
+		kmem_free(tw, sizeof (ehci_trans_wrapper_t));
+
+		return (USB_FAILURE);
+	}
+
+	/* The cookie count should be 1 */
+	if (ccount != 1) {
+		result = ddi_dma_unbind_handle(tw->tw_dmahandle);
+		ASSERT(result == DDI_SUCCESS);
+
+		ddi_dma_mem_free(&tw->tw_accesshandle);
+		ddi_dma_free_handle(&tw->tw_dmahandle);
+		kmem_free(tw, sizeof (ehci_trans_wrapper_t));
+
+		return (USB_FAILURE);
+	}
+
+	if (ehci_allocate_tds_for_tw(ehcip, pp, tw, qtd_count) == USB_SUCCESS) {
+		tw->tw_num_qtds = qtd_count;
+	} else {
+		ehci_deallocate_tw(ehcip, pp, tw);
+		return (USB_FAILURE);
+	}
+	tw->tw_cookie_idx = 0;
+	tw->tw_dma_offs = 0;
+
+	/*
+	 * Only allow one wrapper to be added at a time. Insert the
+	 * new transaction wrapper into the list for this pipe.
+	 */
+	if (pp->pp_tw_head == NULL) {
+		pp->pp_tw_head = tw;
+		pp->pp_tw_tail = tw;
+	} else {
+		pp->pp_tw_tail->tw_next = tw;
+		pp->pp_tw_tail = tw;
+	}
+
+	/* Store the transfer length */
+	tw->tw_length = POLLED_RAW_BUF_SIZE;
+
+	/* Store a back pointer to the pipe private structure */
+	tw->tw_pipe_private = pp;
+
+	/* Store the transfer type - synchronous or asynchronous */
+	tw->tw_flags = usb_flags;
+
+	/* Get and Store 32bit ID */
+	tw->tw_id = EHCI_GET_ID((void *)tw);
+
+	pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
+	tw->tw_direction = (pipe_dir == USB_EP_DIR_OUT)?
+	    EHCI_QTD_CTRL_OUT_PID : EHCI_QTD_CTRL_IN_PID;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
+	    "ehci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
+	    (void *)tw, tw->tw_ncookies);
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * ehci_polled_insert_async_qh:
+ *
+ * Insert a bulk endpoint into the Host Controller's (HC)
+ * Asynchronous schedule endpoint list.
+ */
+static void
+ehci_polled_insert_async_qh(
+	ehci_state_t		*ehcip,
+	ehci_pipe_private_t	*pp)
+{
+	ehci_qh_t		*qh = pp->pp_qh;
+	ehci_qh_t		*async_head_qh;
+	ehci_qh_t		*next_qh;
+	uintptr_t		qh_addr;
+
+	/* Make sure this QH is not already in the list */
+	ASSERT((Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR) == NULL);
+
+	qh_addr = ehci_qh_cpu_to_iommu(ehcip, qh);
+
+	/* Obtain a ptr to the head of the Async schedule list */
+	async_head_qh = ehcip->ehci_head_of_async_sched_list;
+
+	if (async_head_qh == NULL) {
+		/* Set this QH to be the "head" of the circular list */
+		Set_QH(qh->qh_ctrl,
+		    (Get_QH(qh->qh_ctrl) | EHCI_QH_CTRL_RECLAIM_HEAD));
+
+		/* Set new QH's link and previous pointer to itself */
+		Set_QH(qh->qh_link_ptr, qh_addr | EHCI_QH_LINK_REF_QH);
+		Set_QH(qh->qh_prev, qh_addr);
+
+		ehcip->ehci_head_of_async_sched_list = qh;
+
+		/* Set the head ptr to the new endpoint */
+		Set_OpReg(ehci_async_list_addr, qh_addr);
+
+		/*
+		 * For some reason this register might get nulled out by
+		 * the Uli M1575 South Bridge. To workaround the hardware
+		 * problem, check the value after write and retry if the
+		 * last write fails.
+		 *
+		 * If the ASYNCLISTADDR remains "stuck" after
+		 * EHCI_MAX_RETRY retries, then the M1575 is broken
+		 * and is stuck in an inconsistent state and is about
+		 * to crash the machine with a trn_oor panic when it
+		 * does a DMA read from 0x0.  It is better to panic
+		 * now rather than wait for the trn_oor crash; this
+		 * way Customer Service will have a clean signature
+		 * that indicts the M1575 chip rather than a
+		 * mysterious and hard-to-diagnose trn_oor panic.
+		 */
+		if ((ehcip->ehci_vendor_id == PCI_VENDOR_ULi_M1575) &&
+		    (ehcip->ehci_device_id == PCI_DEVICE_ULi_M1575) &&
+		    (qh_addr != Get_OpReg(ehci_async_list_addr))) {
+			int retry = 0;
+
+			Set_OpRegRetry(ehci_async_list_addr, qh_addr, retry);
+			if (retry >= EHCI_MAX_RETRY)
+				cmn_err(CE_PANIC, "ehci_insert_async_qh:"
+				    " ASYNCLISTADDR write failed.");
+
+			USB_DPRINTF_L2(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
+			    "ehci_insert_async_qh: ASYNCLISTADDR "
+			    "write failed, retry=%d", retry);
+		}
+	} else {
+		ASSERT(Get_QH(async_head_qh->qh_ctrl) &
+		    EHCI_QH_CTRL_RECLAIM_HEAD);
+
+		/* Ensure this QH's "H" bit is not set */
+		Set_QH(qh->qh_ctrl,
+		    (Get_QH(qh->qh_ctrl) & ~EHCI_QH_CTRL_RECLAIM_HEAD));
+
+		next_qh = ehci_qh_iommu_to_cpu(ehcip,
+		    Get_QH(async_head_qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
+
+		/* Set new QH's link and previous pointers */
+		Set_QH(qh->qh_link_ptr,
+		    Get_QH(async_head_qh->qh_link_ptr) | EHCI_QH_LINK_REF_QH);
+		Set_QH(qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, async_head_qh));
+
+		/* Set next QH's prev pointer */
+		Set_QH(next_qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, qh));
+
+		/* Set QH Head's link pointer points to new QH */
+		Set_QH(async_head_qh->qh_link_ptr,
+		    qh_addr | EHCI_QH_LINK_REF_QH);
+	}
+}
+
+
+/*
+ * ehci_remove_async_qh:
+ *
+ * Remove a control/bulk endpoint into the Host Controller's (HC)
+ * Asynchronous schedule endpoint list.
+ */
+static void
+ehci_polled_remove_async_qh(
+	ehci_state_t		*ehcip,
+	ehci_pipe_private_t	*pp)
+{
+	ehci_qh_t		*qh = pp->pp_qh; /* qh to be removed */
+	ehci_qh_t		*prev_qh, *next_qh;
+
+	prev_qh = ehci_qh_iommu_to_cpu(ehcip,
+	    Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR);
+	next_qh = ehci_qh_iommu_to_cpu(ehcip,
+	    Get_QH(qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
+
+	/* Make sure this QH is in the list */
+	ASSERT(prev_qh != NULL);
+
+	/*
+	 * If next QH and current QH are the same, then this is the last
+	 * QH on the Asynchronous Schedule list.
+	 */
+	if (qh == next_qh) {
+		ASSERT(Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_RECLAIM_HEAD);
+		/*
+		 * Null our pointer to the async sched list, but do not
+		 * touch the host controller's list_addr.
+		 */
+		ehcip->ehci_head_of_async_sched_list = NULL;
+	} else {
+		/* If this QH is the HEAD then find another one to replace it */
+		if (ehcip->ehci_head_of_async_sched_list == qh) {
+
+			ASSERT(Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_RECLAIM_HEAD);
+			ehcip->ehci_head_of_async_sched_list = next_qh;
+			Set_QH(next_qh->qh_ctrl,
+			    Get_QH(next_qh->qh_ctrl) |
+			    EHCI_QH_CTRL_RECLAIM_HEAD);
+		}
+		Set_QH(prev_qh->qh_link_ptr, Get_QH(qh->qh_link_ptr));
+		Set_QH(next_qh->qh_prev, Get_QH(qh->qh_prev));
+	}
+
+	/* qh_prev to indicate it is no longer in the circular list */
+	Set_QH(qh->qh_prev, NULL);
+
+}
--- a/usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c	Thu Mar 19 10:21:31 2009 +0800
+++ b/usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c	Thu Mar 19 14:02:20 2009 +0800
@@ -1744,6 +1744,17 @@
 	    ehci_hcdi_polled_input_exit;
 	usba_hcdi_ops->usba_hcdi_console_input_fini =
 	    ehci_hcdi_polled_input_fini;
+
+	usba_hcdi_ops->usba_hcdi_console_output_init =
+	    ehci_hcdi_polled_output_init;
+	usba_hcdi_ops->usba_hcdi_console_output_enter =
+	    ehci_hcdi_polled_output_enter;
+	usba_hcdi_ops->usba_hcdi_console_write =
+	    ehci_hcdi_polled_write;
+	usba_hcdi_ops->usba_hcdi_console_output_exit =
+	    ehci_hcdi_polled_output_exit;
+	usba_hcdi_ops->usba_hcdi_console_output_fini =
+	    ehci_hcdi_polled_output_fini;
 	return (usba_hcdi_ops);
 }
 
--- a/usr/src/uts/common/io/usb/hcd/openhci/ohci.c	Thu Mar 19 10:21:31 2009 +0800
+++ b/usr/src/uts/common/io/usb/hcd/openhci/ohci.c	Thu Mar 19 14:02:20 2009 +0800
@@ -325,7 +325,7 @@
 				ushort_t		pkt_count,
 				size_t			td_count,
 				uint_t			usb_flags);
-static int	ohci_allocate_tds_for_tw(
+int	ohci_allocate_tds_for_tw(
 				ohci_state_t		*ohcip,
 				ohci_trans_wrapper_t	*tw,
 				size_t			td_count);
@@ -2063,6 +2063,16 @@
 	usba_hcdi_ops->usba_hcdi_console_input_fini =
 	    ohci_hcdi_polled_input_fini;
 
+	usba_hcdi_ops->usba_hcdi_console_output_init =
+	    ohci_hcdi_polled_output_init;
+	usba_hcdi_ops->usba_hcdi_console_output_enter =
+	    ohci_hcdi_polled_output_enter;
+	usba_hcdi_ops->usba_hcdi_console_write = ohci_hcdi_polled_write;
+	usba_hcdi_ops->usba_hcdi_console_output_exit =
+	    ohci_hcdi_polled_output_exit;
+	usba_hcdi_ops->usba_hcdi_console_output_fini =
+	    ohci_hcdi_polled_output_fini;
+
 	return (usba_hcdi_ops);
 }
 
@@ -6752,7 +6762,7 @@
  * Returns USB_NO_RESOURCES if it was not able to allocate all the requested TD
  * otherwise USB_SUCCESS.
  */
-static int
+int
 ohci_allocate_tds_for_tw(
 	ohci_state_t		*ohcip,
 	ohci_trans_wrapper_t	*tw,
--- a/usr/src/uts/common/io/usb/hcd/openhci/ohci_polled.c	Thu Mar 19 10:21:31 2009 +0800
+++ b/usr/src/uts/common/io/usb/hcd/openhci/ohci_polled.c	Thu Mar 19 14:02:20 2009 +0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -92,6 +92,19 @@
 static void	ohci_polled_finish_interrupt(
 				ohci_state_t		*ohcip,
 				uint_t			intr);
+static void	ohci_polled_insert_bulk_td(
+				ohci_polled_t		*ohci_polledp);
+static int 	ohci_polled_create_tw(
+				ohci_state_t		*ohcip,
+				usba_pipe_handle_data_t	*ph,
+				usb_flags_t		usb_flags);
+static int	ohci_polled_insert_hc_td(
+				ohci_state_t		*ohcip,
+				uint_t			hctd_ctrl,
+				uint32_t		hctd_dma_offs,
+				size_t			hctd_length,
+				ohci_pipe_private_t	*pp,
+				ohci_trans_wrapper_t	*tw);
 /*
  * POLLED entry points
  *
@@ -101,7 +114,7 @@
 /*
  * ohci_hcdi_polled_input_init:
  *
- * This is the initialization routine for handling the USB keyboard
+ * This is the initialization routine for handling the USB input device
  * in POLLED mode.  This routine is not called from POLLED mode, so
  * it is OK to acquire mutexes.
  */
@@ -113,7 +126,7 @@
 {
 	ohci_polled_t		*ohci_polledp;
 	ohci_state_t		*ohcip;
-	int			ret;
+	int			pipe_attr, ret;
 
 	ohcip = ohci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
 
@@ -134,7 +147,6 @@
 	}
 
 	ohci_polledp = (ohci_polled_t *)console_input_info->uci_private;
-
 	/*
 	 * Mark the structure so that if we are using it, we don't free
 	 * the structures if one of them is unplugged.
@@ -153,6 +165,13 @@
 
 	*polled_buf = ohci_polledp->ohci_polled_buf;
 
+	/* Insert bulkin td into endpoint's tds list */
+	pipe_attr = ohci_polledp->ohci_polled_input_pipe_handle->
+	    p_ep.bmAttributes & USB_EP_ATTR_MASK;
+
+	if (pipe_attr == USB_EP_ATTR_BULK) {
+		ohci_polled_insert_bulk_td(ohci_polledp);
+	}
 	/*
 	 * This is a software workaround to fix schizo hardware bug.
 	 * Existence of "no-prom-cdma-sync"  property means consistent
@@ -290,33 +309,30 @@
 	 * Check whether any Frame Number Overflow interrupt is pending
 	 * and if it is pending, process this interrupt.
 	 */
-
 	if (intr & HCR_INTR_FNO) {
 		ohci_handle_frame_number_overflow(ohcip);
 
 		/* Acknowledge the FNO interrupt */
 		ohci_polled_finish_interrupt(ohcip, HCR_INTR_FNO);
 	}
+
+	/* Check to see if there are any TD's for this input device */
+	if (ohci_polled_check_done_list(ohci_polledp) == USB_SUCCESS) {
+
+		/* Process any TD's on the input done list */
+		*num_characters =
+		    ohci_polled_process_input_list(ohci_polledp);
+	}
+
+	/*
+	 * To make sure after we get the done list from DoneHead,
+	 * every input device get his own TD's in the
+	 * ohci_polled_done_list and then clear the interrupt status.
+	 */
 	if (intr & HCR_INTR_WDH) {
 
-		/* Check to see if there are any TD's for this input device */
-		if (ohci_polled_check_done_list(ohci_polledp) == USB_SUCCESS) {
-
-			/* Process any TD's on the input done list */
-			*num_characters =
-			    ohci_polled_process_input_list(ohci_polledp);
-		}
-
-		/*
-		 * To make sure after we get the done list from DoneHead,
-		 * every input device get his own TD's in the
-		 * ohci_polled_done_list and then clear the interrupt status.
-		 */
-		if (ohcip->ohci_polled_done_list == NULL) {
-
-			/* Acknowledge the WDH interrupt */
-			ohci_polled_finish_interrupt(ohcip, HCR_INTR_WDH);
-		}
+		/* Acknowledge the WDH interrupt */
+		ohci_polled_finish_interrupt(ohcip, HCR_INTR_WDH);
 	}
 #ifndef lint
 	_NOTE(COMPETING_THREADS_NOW);
@@ -327,6 +343,232 @@
 
 
 /*
+ * ohci_hcdi_polled_output_init:
+ *
+ * This is the initialization routine for handling the USB serial output
+ * in POLLED mode.  This routine is not called from POLLED mode, so
+ * it is OK to acquire mutexes.
+ */
+int
+ohci_hcdi_polled_output_init(
+	usba_pipe_handle_data_t	*ph,
+	usb_console_info_impl_t	*console_output_info)
+{
+	ohci_polled_t		*ohci_polledp;
+	ohci_state_t		*ohcip;
+	int			ret;
+
+	ohcip = ohci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
+
+	/*
+	 * Grab the ohci_int_mutex so that things don't change on us
+	 * if an interrupt comes in.
+	 */
+	mutex_enter(&ohcip->ohci_int_mutex);
+
+	ret = ohci_polled_init(ph, ohcip, console_output_info);
+
+	if (ret != USB_SUCCESS) {
+
+		/* Allow interrupts to continue */
+		mutex_exit(&ohcip->ohci_int_mutex);
+
+		return (ret);
+	}
+
+	ohci_polledp = (ohci_polled_t *)console_output_info->uci_private;
+	/*
+	 * Mark the structure so that if we are using it, we don't free
+	 * the structures if one of them is unplugged.
+	 */
+	ohci_polledp->ohci_polled_flags |= POLLED_OUTPUT_MODE;
+
+	/*
+	 * This is a software workaround to fix schizo hardware bug.
+	 * Existence of "no-prom-cdma-sync"  property means consistent
+	 * dma sync should not be done while in prom or polled mode.
+	 */
+	if (ddi_prop_exists(DDI_DEV_T_ANY, ohcip->ohci_dip,
+	    DDI_PROP_NOTPROM, "no-prom-cdma-sync")) {
+		ohci_polledp->ohci_polled_no_sync_flag = B_TRUE;
+	}
+
+	/* Allow interrupts to continue */
+	mutex_exit(&ohcip->ohci_int_mutex);
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * ohci_hcdi_polled_output_fini:
+ */
+int
+ohci_hcdi_polled_output_fini(usb_console_info_impl_t *info)
+{
+	ohci_polled_t		*ohci_polledp;
+	ohci_state_t		*ohcip;
+	int			ret;
+
+	ohci_polledp = (ohci_polled_t *)info->uci_private;
+
+	ohcip = ohci_polledp->ohci_polled_ohcip;
+
+	mutex_enter(&ohcip->ohci_int_mutex);
+
+	/*
+	 * Reset the POLLED_INPUT_MODE flag so that we can tell if
+	 * this structure is in use in the ohci_polled_fini routine.
+	 */
+	ohci_polledp->ohci_polled_flags &= ~POLLED_OUTPUT_MODE;
+
+	ret = ohci_polled_fini(ohci_polledp);
+
+	info->uci_private = NULL;
+
+	mutex_exit(&ohcip->ohci_int_mutex);
+
+	return (ret);
+}
+
+
+/*
+ * ohci_hcdi_polled_output_enter:
+ *
+ * everything is done in input enter
+ */
+/*ARGSUSED*/
+int
+ohci_hcdi_polled_output_enter(usb_console_info_impl_t *info)
+{
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * ohci_hcdi_polled_output_exit:
+ *
+ * everything is done in input exit
+ */
+/*ARGSUSED*/
+int
+ohci_hcdi_polled_output_exit(usb_console_info_impl_t *info)
+{
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * ohci_hcdi_polled_write:
+ *	Put a key character -- rewrite this!
+ */
+int
+ohci_hcdi_polled_write(usb_console_info_impl_t *info, uchar_t *buf,
+    uint_t num_characters, uint_t *num_characters_written)
+{
+	ohci_state_t		*ohcip;
+	ohci_polled_t		*ohci_polledp;
+	ohci_trans_wrapper_t	*tw;
+	ohci_pipe_private_t	*pp;
+	usba_pipe_handle_data_t	*ph;
+	uint32_t		ctrl;
+	uint_t			intr, bulk_pkg_size;
+	int			i;
+
+#ifndef lint
+	_NOTE(NO_COMPETING_THREADS_NOW);
+#endif
+
+	ohci_polledp = (ohci_polled_t *)info->uci_private;
+	ohcip = ohci_polledp->ohci_polled_ohcip;
+
+	/* Disable periodic list processing */
+	Set_OpReg(hcr_control,
+	    (Get_OpReg(hcr_control) & (~HCR_CONTROL_PLE)));
+
+	/* Add the endpoint to the lattice */
+	for (i = ohcip->ohci_polled_enter_count; i < NUM_INTR_ED_LISTS;
+	    i = i + MIN_LOW_SPEED_POLL_INTERVAL) {
+		Set_HCCA(ohcip->ohci_hccap->HccaIntTble[i],
+		    ohci_ed_cpu_to_iommu(ohcip,
+		    ohci_polledp->ohci_polled_ed));
+	}
+
+	ph = ohci_polledp->ohci_polled_input_pipe_handle;
+	pp = (ohci_pipe_private_t *)ph->p_hcd_private;
+	tw = pp->pp_tw_head;
+
+	ASSERT(tw != NULL);
+	if (tw->tw_hctd_free_list == NULL) {
+#ifndef lint
+	_NOTE(COMPETING_THREADS_NOW);
+#endif
+		return (USB_SUCCESS);
+	}
+
+	/* Copy transmit buffer */
+	if (num_characters > POLLED_RAW_BUF_SIZE) {
+		cmn_err(CE_NOTE, "polled write size %d bigger than %d",
+		    num_characters, POLLED_RAW_BUF_SIZE);
+		num_characters = POLLED_RAW_BUF_SIZE;
+	}
+	tw->tw_length = num_characters;
+
+	ddi_rep_put8(tw->tw_accesshandle,
+	    buf, (uint8_t *)tw->tw_buf,
+	    tw->tw_length, DDI_DEV_AUTOINCR);
+	Sync_IO_Buffer_for_device(tw->tw_dmahandle, tw->tw_length);
+
+	/* Insert td into endpoint's tds list */
+	ctrl = tw->tw_direction | HC_TD_DT_0|HC_TD_1I | HC_TD_R;
+	bulk_pkg_size = min(tw->tw_length, OHCI_MAX_TD_XFER_SIZE);
+
+	(void) ohci_polled_insert_hc_td(ohcip, ctrl, 0, bulk_pkg_size, pp, tw);
+
+	/* Enable periodic list processing */
+	Set_OpReg(hcr_control,
+	    (Get_OpReg(hcr_control) | HCR_CONTROL_PLE));
+
+	/* Wait for bulk out tds transfer completion */
+	for (;;) {
+		intr = Get_OpReg(hcr_intr_status);
+
+		if (intr & HCR_INTR_FNO) {
+			ohci_handle_frame_number_overflow(ohcip);
+			ohci_polled_finish_interrupt(ohcip, HCR_INTR_FNO);
+		}
+
+		if (intr & HCR_INTR_WDH) {
+			if (ohci_polled_check_done_list(ohci_polledp) ==
+			    USB_SUCCESS) {
+				*num_characters_written =
+				    ohci_polled_process_input_list(
+				    ohci_polledp);
+				break;
+			}
+		}
+
+		Set_OpReg(hcr_intr_status, intr);
+		(void) Get_OpReg(hcr_intr_status);
+	}
+
+	/* Remove the endpoint from the lattice */
+	for (i = ohcip->ohci_polled_enter_count; i < NUM_INTR_ED_LISTS;
+	    i = i + MIN_LOW_SPEED_POLL_INTERVAL) {
+		Set_HCCA(ohcip->ohci_hccap->HccaIntTble[i],
+		    ohci_ed_cpu_to_iommu(ohcip,
+		    ohci_polledp->ohci_polled_dummy_ed));
+	}
+
+	Set_OpReg(hcr_intr_status, intr);
+	(void) Get_OpReg(hcr_intr_status);
+#ifndef lint
+	_NOTE(COMPETING_THREADS_NOW);
+#endif
+	return (USB_SUCCESS);
+}
+
+
+/*
  * Internal Functions
  */
 
@@ -349,6 +591,7 @@
 {
 	ohci_polled_t		*ohci_polledp;
 	ohci_pipe_private_t	*pp;
+	int			pipe_attr;
 
 	ASSERT(mutex_owned(&ohcip->ohci_int_mutex));
 
@@ -450,8 +693,8 @@
 	}
 
 	/*
-	 * Allocate the interrupt endpoint. This ED will be inserted in
-	 * to the lattice chain for the  keyboard device. This endpoint
+	 * Allocate the endpoint. This ED will be inserted in
+	 * to the lattice chain for the device. This endpoint
 	 * will have the TDs hanging off of it for the processing.
 	 */
 	ohci_polledp->ohci_polled_ed = ohci_alloc_hc_ed(ohcip,
@@ -468,27 +711,41 @@
 	/* Insert the endpoint onto the pipe handle */
 	pp->pp_ept = ohci_polledp->ohci_polled_ed;
 
-	/*
-	 * Set soft interrupt handler flag in the normal mode usb
-	 * pipe handle.
-	 */
-	mutex_enter(&ph->p_mutex);
-	ph->p_spec_flag |= USBA_PH_FLAG_USE_SOFT_INTR;
-	mutex_exit(&ph->p_mutex);
+	pipe_attr = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
+
+	switch (pipe_attr) {
+	case USB_EP_ATTR_INTR:
+		/*
+		 * Set soft interrupt handler flag in the normal mode usb
+		 * pipe handle.
+		 */
+		mutex_enter(&ph->p_mutex);
+		ph->p_spec_flag |= USBA_PH_FLAG_USE_SOFT_INTR;
+		mutex_exit(&ph->p_mutex);
 
-	/*
-	 * Insert a Interrupt polling request onto the endpoint.
-	 *
-	 * There will now be two TDs on the ED, one is the dummy TD that
-	 * was allocated above in the  ohci_alloc_hc_ed and this new one.
-	 */
-	if ((ohci_start_periodic_pipe_polling(ohcip,
-	    ohci_polledp->ohci_polled_input_pipe_handle,
-	    NULL, USB_FLAGS_SLEEP)) != USB_SUCCESS) {
-
-		return (USB_NO_RESOURCES);
+		/*
+		 * Insert a Interrupt polling request onto the endpoint.
+		 *
+		 * There will now be two TDs on the ED, one is the dummy TD
+		 * that was allocated above in the ohci_alloc_hc_ed and
+		 * this new one.
+		 */
+		if ((ohci_start_periodic_pipe_polling(ohcip,
+		    ohci_polledp->ohci_polled_input_pipe_handle,
+		    NULL, USB_FLAGS_SLEEP)) != USB_SUCCESS) {
+			return (USB_NO_RESOURCES);
+		}
+		break;
+	case USB_EP_ATTR_BULK:
+		if ((ohci_polled_create_tw(ohcip,
+		    ohci_polledp->ohci_polled_input_pipe_handle,
+		    USB_FLAGS_SLEEP)) != USB_SUCCESS) {
+			return (USB_NO_RESOURCES);
+		}
+		break;
+	default:
+		return (USB_FAILURE);
 	}
-
 	return (USB_SUCCESS);
 }
 
@@ -1214,7 +1471,6 @@
 			Set_OpReg(hcr_intr_enable, mask | HCR_INTR_MIE);
 		}
 	}
-
 #ifndef lint
 	_NOTE(COMPETING_THREADS_NOW);
 #endif
@@ -1247,8 +1503,6 @@
 /*
  * Polled read routines
  */
-
-
 /*
  * ohci_polled_check_done_list:
  *
@@ -1277,6 +1531,7 @@
 	if (done_head == NULL) {
 		if (ohcip->ohci_polled_done_list) {
 			done_head = ohcip->ohci_polled_done_list;
+			ohcip->ohci_polled_done_list = NULL;
 		} else {
 
 			return (USB_FAILURE);
@@ -1284,7 +1539,6 @@
 	} else {
 		/* Reset the done head to NULL */
 		Set_HCCA(ohcip->ohci_hccap->HccaDoneHead, NULL);
-		ohcip->ohci_polled_done_list = NULL;
 	}
 
 	/* Sync ED and TD pool */
@@ -1309,6 +1563,7 @@
 	return (USB_SUCCESS);
 }
 
+
 /*
  * ohci_polled_pickup_done_list:
  *
@@ -1320,7 +1575,6 @@
 	ohci_td_t	*done_head)
 {
 	ohci_state_t	*ohcip = ohci_polledp->ohci_polled_ohcip;
-	ohci_td_t	*reserve_head = NULL, *reserve_tail = NULL;
 	ohci_td_t	*create_head = NULL, *current_td, *td;
 	ohci_trans_wrapper_t	*tw;
 	ohci_pipe_private_t	*pp;
@@ -1344,8 +1598,8 @@
 		pp = tw->tw_pipe_private;
 
 		/*
-		 * Figure  out	which  done list to put this TD on and put it
-		 * there.   If	the  pipe handle  of the TD matches the pipe
+		 * Figure  out  which  done list to put this TD on and put it
+		 * there.   If  the  pipe handle  of the TD matches the pipe
 		 * handle  we  are  using for the input device, then this must
 		 * be an input TD, reverse the order and link to the list for
 		 * this input device. Else put the TD to the reserve done list
@@ -1362,29 +1616,25 @@
 				create_head = current_td;
 			}
 		} else {
-			if (reserve_head == NULL) {
-				reserve_head = reserve_tail = current_td;
+			if (ohcip->ohci_polled_done_list == NULL) {
+				ohcip->ohci_polled_done_list = (ohci_td_t *)
+				    (uintptr_t)ohci_td_cpu_to_iommu(ohcip,
+				    current_td);
 			} else {
-				Set_TD(reserve_tail->hctd_next_td,
-				    ohci_td_cpu_to_iommu(ohcip, current_td));
-				reserve_tail = current_td;
+				Set_TD(current_td->hctd_next_td,
+				    ohcip->ohci_polled_done_list);
+				ohcip->ohci_polled_done_list = (ohci_td_t *)
+				    (uintptr_t)ohci_td_cpu_to_iommu(ohcip,
+				    current_td);
 			}
 		}
 		current_td = td;
 	}
 
-	/* Check if there is other TDs left for other input devices */
-	if (reserve_head) {
-		ohcip->ohci_polled_done_list = (ohci_td_t *)(uintptr_t)
-		    ohci_td_cpu_to_iommu(ohcip, reserve_head);
-
-	} else {
-		ohcip->ohci_polled_done_list = NULL;
-	}
-
 	return (create_head);
 }
 
+
 /*
  * ohci_polled_create_input_list:
  *
@@ -1457,6 +1707,7 @@
 	uint_t			num_characters;
 	ohci_trans_wrapper_t	*tw;
 	ohci_pipe_private_t	*pp;
+	int 			pipe_dir;
 
 	/*
 	 * Get the first TD on the input done head.
@@ -1498,13 +1749,52 @@
 			/* Clear the halt bit */
 			Set_ED(pp->pp_ept->hced_headp,
 			    (Get_ED(pp->pp_ept->hced_headp) & ~HC_EPT_Halt));
-		} else {
-			num_characters +=
-			    ohci_polled_handle_normal_td(ohci_polledp, td);
 		}
 
-		/* Insert this interrupt TD back onto the ED's TD list */
-		ohci_polled_insert_td(ohcip, td);
+		/* Obtain the transfer wrapper from the TD */
+		tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
+		    (uint32_t)Get_TD(td->hctd_trans_wrapper));
+
+		/* Get the pipe direction for this transfer wrapper */
+		pipe_dir = tw->tw_pipe_private->pp_pipe_handle->
+		    p_ep.bEndpointAddress & USB_EP_DIR_MASK;
+
+		switch (pipe_dir) {
+		case USB_EP_DIR_IN:
+			num_characters +=
+			    ohci_polled_handle_normal_td(ohci_polledp,
+			    td);
+
+			/*
+			 * Insert this TD back
+			 * onto the ED's TD list
+			 */
+			ohci_polled_insert_td(ohcip, td);
+			break;
+		case USB_EP_DIR_OUT:
+			ASSERT((ohci_td_t *)tw->tw_hctd_head == td);
+
+			tw->tw_hctd_head = (ohci_td_t *)
+			    ohci_td_iommu_to_cpu(ohcip,
+			    Get_TD(td->hctd_tw_next_td));
+			Set_TD(td->hctd_state, HC_TD_DUMMY);
+
+			if (tw->tw_hctd_head == NULL) {
+				tw->tw_hctd_tail = NULL;
+			}
+
+			if (tw->tw_hctd_free_list != NULL) {
+				uint32_t	td_addr;
+				td_addr = ohci_td_cpu_to_iommu(ohcip,
+				    tw->tw_hctd_free_list);
+				Set_TD(td->hctd_tw_next_td, td_addr);
+				tw->tw_hctd_free_list = td;
+			} else {
+				tw->tw_hctd_free_list = td;
+				Set_TD(td->hctd_tw_next_td, NULL);
+			}
+			break;
+		}
 
 		td = next_td;
 	}
@@ -1535,7 +1825,6 @@
 	buf = (uchar_t *)tw->tw_buf;
 
 	length = tw->tw_length;
-
 	/*
 	 * If "CurrentBufferPointer" of Transfer Descriptor (TD) is
 	 * not equal to zero, then we  received less data  from the
@@ -1554,7 +1843,7 @@
 		Sync_IO_Buffer(tw->tw_dmahandle, length);
 	}
 
-	/* Copy the data into the message */
+		/* Copy the data into the message */
 	ddi_rep_get8(tw->tw_accesshandle,
 	    (uint8_t *)ohci_polledp->ohci_polled_buf,
 	    (uint8_t *)buf, length, DDI_DEV_AUTOINCR);
@@ -1579,6 +1868,8 @@
 	ohci_trans_wrapper_t	*tw;
 	ohci_td_t		*cpu_current_dummy;
 	usb_intr_req_t		*intr_req;
+	usba_pipe_handle_data_t	*ph;
+	int			pipe_attr;
 
 	/* Obtain the transfer wrapper from the TD */
 	tw = (ohci_trans_wrapper_t *)OHCI_LOOKUP_ID(
@@ -1611,16 +1902,27 @@
 	Set_TD(td->hctd_state, HC_TD_DUMMY);
 
 	pp = tw->tw_pipe_private;
+	ph = pp->pp_pipe_handle;
 
-	/* Obtain the endpoint and interrupt request */
+	/* Obtain the endpoint and the request */
 	ept = pp->pp_ept;
 
-	intr_req = (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
+	/* Get the pipe attribute */
+	pipe_attr = ph->p_ep.bmAttributes & USB_EP_ATTR_MASK;
+
+	switch (pipe_attr) {
+	case USB_EP_ATTR_INTR:
+		intr_req = (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
 
-	if (intr_req->intr_attributes & USB_ATTRS_SHORT_XFER_OK) {
-		td_control = HC_TD_IN|HC_TD_1I|HC_TD_R;
-	} else {
-		td_control = HC_TD_IN|HC_TD_1I;
+		if (intr_req->intr_attributes & USB_ATTRS_SHORT_XFER_OK) {
+			td_control = HC_TD_IN|HC_TD_1I|HC_TD_R;
+		} else {
+			td_control = HC_TD_IN|HC_TD_1I;
+		}
+		break;
+	case USB_EP_ATTR_BULK:
+		td_control = tw->tw_direction|HC_TD_DT_0|HC_TD_1I|HC_TD_R;
+		break;
 	}
 
 	/* Get the current dummy */
@@ -1767,3 +2069,210 @@
 	 */
 	(void) Get_OpReg(hcr_intr_status);
 }
+
+
+/*
+ * ohci_polled_buikin_start:
+ * 	Insert bulkin td into endpoint's td list.
+ */
+static void
+ohci_polled_insert_bulk_td(
+	ohci_polled_t	*ohci_polledp)
+{
+	ohci_state_t		*ohcip;
+	ohci_trans_wrapper_t	*tw;
+	ohci_pipe_private_t	*pp;
+	usba_pipe_handle_data_t	*ph;
+	uint32_t		ctrl;
+	uint_t			bulk_pkg_size;
+
+	ohcip = ohci_polledp->ohci_polled_ohcip;
+	ph = ohci_polledp->ohci_polled_input_pipe_handle;
+	pp = (ohci_pipe_private_t *)ph->p_hcd_private;
+
+	tw = pp->pp_tw_head;
+	ASSERT(tw != NULL);
+
+	ctrl = tw->tw_direction | HC_TD_DT_0 | HC_TD_1I | HC_TD_R;
+	bulk_pkg_size = min(POLLED_RAW_BUF_SIZE, OHCI_MAX_TD_XFER_SIZE);
+
+	(void) ohci_polled_insert_hc_td(ohcip, ctrl, 0, bulk_pkg_size, pp, tw);
+}
+
+
+/*
+ * ohci_polled_create_tw:
+ *	Create the transfer wrapper used in polled mode.
+ */
+static int
+ohci_polled_create_tw(
+	ohci_state_t	*ohcip,
+	usba_pipe_handle_data_t	*ph,
+	usb_flags_t	usb_flags)
+{
+	uint_t			ccount;
+	ohci_trans_wrapper_t	*tw;
+	ddi_device_acc_attr_t	dev_attr;
+	ddi_dma_attr_t		dma_attr;
+	ohci_pipe_private_t	*pp;
+	int			result, pipe_dir, td_count;
+	size_t			real_length;
+
+	pp = (ohci_pipe_private_t *)ph->p_hcd_private;
+	td_count = (POLLED_RAW_BUF_SIZE - 1) / OHCI_MAX_TD_XFER_SIZE + 1;
+
+	if ((tw = kmem_zalloc(sizeof (ohci_trans_wrapper_t),
+	    KM_NOSLEEP)) == NULL) {
+		return (USB_FAILURE);
+	}
+
+	/* allow sg lists for transfer wrapper dma memory */
+	bcopy(&ohcip->ohci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
+	dma_attr.dma_attr_sgllen = OHCI_DMA_ATTR_TW_SGLLEN;
+	dma_attr.dma_attr_align = OHCI_DMA_ATTR_ALIGNMENT;
+
+	/* Allocate the DMA handle */
+	if ((result = ddi_dma_alloc_handle(ohcip->ohci_dip,
+	    &dma_attr, DDI_DMA_DONTWAIT, 0, &tw->tw_dmahandle)) !=
+	    DDI_SUCCESS) {
+		kmem_free(tw, sizeof (ohci_trans_wrapper_t));
+
+		return (USB_FAILURE);
+	}
+
+	dev_attr.devacc_attr_version		= DDI_DEVICE_ATTR_V0;
+	dev_attr.devacc_attr_endian_flags	= DDI_STRUCTURE_LE_ACC;
+	dev_attr.devacc_attr_dataorder		= DDI_STRICTORDER_ACC;
+
+	/* Allocate the memory */
+	if ((result = ddi_dma_mem_alloc(tw->tw_dmahandle, POLLED_RAW_BUF_SIZE,
+	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
+	    &tw->tw_buf, &real_length, &tw->tw_accesshandle)) !=
+	    DDI_SUCCESS) {
+		ddi_dma_free_handle(&tw->tw_dmahandle);
+		kmem_free(tw, sizeof (ohci_trans_wrapper_t));
+
+		return (USB_FAILURE);
+	}
+
+	/* Bind the handle */
+	if ((result = ddi_dma_addr_bind_handle(tw->tw_dmahandle, NULL,
+	    tw->tw_buf, real_length, DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
+	    DDI_DMA_DONTWAIT, NULL, &tw->tw_cookie, &ccount)) !=
+	    DDI_DMA_MAPPED) {
+		ddi_dma_mem_free(&tw->tw_accesshandle);
+		ddi_dma_free_handle(&tw->tw_dmahandle);
+		kmem_free(tw, sizeof (ohci_trans_wrapper_t));
+
+		return (USB_FAILURE);
+	}
+
+	/* The cookie count should be 1 */
+	if (ccount != 1) {
+		result = ddi_dma_unbind_handle(tw->tw_dmahandle);
+		ASSERT(result == DDI_SUCCESS);
+
+		ddi_dma_mem_free(&tw->tw_accesshandle);
+		ddi_dma_free_handle(&tw->tw_dmahandle);
+		kmem_free(tw, sizeof (ohci_trans_wrapper_t));
+
+		return (USB_FAILURE);
+	}
+
+	if (ohci_allocate_tds_for_tw(ohcip, tw, td_count) == USB_SUCCESS) {
+		tw->tw_num_tds = td_count;
+	} else {
+		ohci_deallocate_tw_resources(ohcip, pp, tw);
+		return (USB_FAILURE);
+	}
+	tw->tw_cookie_idx = 0;
+	tw->tw_dma_offs = 0;
+
+	/*
+	 * Only allow one wrapper to be added at a time. Insert the
+	 * new transaction wrapper into the list for this pipe.
+	 */
+	if (pp->pp_tw_head == NULL) {
+		pp->pp_tw_head = tw;
+		pp->pp_tw_tail = tw;
+	} else {
+		pp->pp_tw_tail->tw_next = tw;
+		pp->pp_tw_tail = tw;
+	}
+
+	/* Store the transfer length */
+	tw->tw_length = POLLED_RAW_BUF_SIZE;
+
+	/* Store a back pointer to the pipe private structure */
+	tw->tw_pipe_private = pp;
+
+	/* Store the transfer type - synchronous or asynchronous */
+	tw->tw_flags = usb_flags;
+
+	/* Get and Store 32bit ID */
+	tw->tw_id = OHCI_GET_ID((void *)tw);
+
+	ASSERT(tw->tw_id != NULL);
+
+	pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
+	tw->tw_direction = (pipe_dir == USB_EP_DIR_IN) ? HC_TD_IN : HC_TD_OUT;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ohcip->ohci_log_hdl,
+	    "ohci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
+	    (void *)tw, tw->tw_ncookies);
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * ohci_polled_insert_hc_td:
+ *
+ * Insert a Transfer Descriptor (TD) on an Endpoint Descriptor (ED).
+ */
+int
+ohci_polled_insert_hc_td(
+	ohci_state_t		*ohcip,
+	uint_t			hctd_ctrl,
+	uint32_t		hctd_dma_offs,
+	size_t			hctd_length,
+	ohci_pipe_private_t	*pp,
+	ohci_trans_wrapper_t	*tw)
+{
+	ohci_td_t		*new_dummy;
+	ohci_td_t		*cpu_current_dummy;
+	ohci_ed_t		*ept = pp->pp_ept;
+
+	/* Retrieve preallocated td from the TW */
+	new_dummy = tw->tw_hctd_free_list;
+
+	ASSERT(new_dummy != NULL);
+
+	tw->tw_hctd_free_list = ohci_td_iommu_to_cpu(ohcip,
+	    Get_TD(new_dummy->hctd_tw_next_td));
+	Set_TD(new_dummy->hctd_tw_next_td, NULL);
+
+	/* Fill in the current dummy */
+	cpu_current_dummy = (ohci_td_t *)
+	    (ohci_td_iommu_to_cpu(ohcip, Get_ED(ept->hced_tailp)));
+
+	/*
+	 * Fill in the current dummy td and
+	 * add the new dummy to the end.
+	 */
+	ohci_polled_fill_in_td(ohcip, cpu_current_dummy, new_dummy,
+	    hctd_ctrl, hctd_dma_offs, hctd_length, tw);
+
+	/*
+	 * add the new dummy to the ED's list. When
+	 * this occurs, the Host Controller will see
+	 * the newly filled in dummy TD.
+	 */
+	Set_ED(ept->hced_tailp,
+	    (ohci_td_cpu_to_iommu(ohcip, new_dummy)));
+
+	/* Insert this td onto the tw */
+	ohci_polled_insert_td_on_tw(ohcip, tw, cpu_current_dummy);
+
+	return (USB_SUCCESS);
+}
--- a/usr/src/uts/common/io/warlock/ehci.wlcmd	Thu Mar 19 10:21:31 2009 +0800
+++ b/usr/src/uts/common/io/warlock/ehci.wlcmd	Thu Mar 19 14:02:20 2009 +0800
@@ -68,6 +68,11 @@
 root   ehci_hcdi_polled_input_enter
 root   ehci_hcdi_polled_input_exit
 root   ehci_hcdi_polled_read
+root   ehci_hcdi_polled_output_init
+root   ehci_hcdi_polled_output_fini
+root   ehci_hcdi_polled_output_enter
+root   ehci_hcdi_polled_output_exit
+root   ehci_hcdi_polled_write
 
 ### currently unused functions
  
--- a/usr/src/uts/common/io/warlock/ohci.wlcmd	Thu Mar 19 10:21:31 2009 +0800
+++ b/usr/src/uts/common/io/warlock/ohci.wlcmd	Thu Mar 19 14:02:20 2009 +0800
@@ -66,6 +66,11 @@
 root   ohci_hcdi_polled_input_enter
 root   ohci_hcdi_polled_input_exit
 root   ohci_hcdi_polled_read
+root   ohci_hcdi_polled_output_init
+root   ohci_hcdi_polled_output_fini
+root   ohci_hcdi_polled_output_enter
+root   ohci_hcdi_polled_output_exit
+root   ohci_hcdi_polled_write
  
 ### currently unused functions
  
--- a/usr/src/uts/common/sys/usb/hcd/ehci/ehci_polled.h	Thu Mar 19 10:21:31 2009 +0800
+++ b/usr/src/uts/common/sys/usb/hcd/ehci/ehci_polled.h	Thu Mar 19 14:02:20 2009 +0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -50,6 +50,7 @@
  * in use.
  */
 #define	POLLED_INPUT_MODE		0x01
+#define	POLLED_OUTPUT_MODE		0x10
 
 /*
  * These two flags are used to determine if this structure is already in
@@ -57,6 +58,7 @@
  * restore it once.  These flags are used for the ehci_polled_flags below.
  */
 #define	POLLED_INPUT_MODE_INUSE		0x04
+#define	POLLED_OUTPUT_MODE_INUSE	0x40
 #define	MAX_NUM_FOR_KEYBOARD		0x8
 /*
  * State structure for the POLLED switch off
--- a/usr/src/uts/common/sys/usb/hcd/ehci/ehcid.h	Thu Mar 19 10:21:31 2009 +0800
+++ b/usr/src/uts/common/sys/usb/hcd/ehci/ehcid.h	Thu Mar 19 14:02:20 2009 +0800
@@ -1151,7 +1151,20 @@
 				usb_console_info_impl_t	*info);
 int		ehci_hcdi_polled_input_fini(
 				usb_console_info_impl_t	*info);
-
+int		ehci_hcdi_polled_output_init(
+				usba_pipe_handle_data_t	*ph,
+				usb_console_info_impl_t	*console_output_info);
+int		ehci_hcdi_polled_output_enter(
+				usb_console_info_impl_t	*info);
+int		ehci_hcdi_polled_write(
+				usb_console_info_impl_t *info,
+				uchar_t *buf,
+				uint_t num_characters,
+				uint_t *num_characters_written);
+int		ehci_hcdi_polled_output_exit(
+				usb_console_info_impl_t	*info);
+int		ehci_hcdi_polled_output_fini(
+				usb_console_info_impl_t *info);
 /*
  * EHCI Root Hub entry points function prototypes.
  */
--- a/usr/src/uts/common/sys/usb/hcd/openhci/ohci_polled.h	Thu Mar 19 10:21:31 2009 +0800
+++ b/usr/src/uts/common/sys/usb/hcd/openhci/ohci_polled.h	Thu Mar 19 14:02:20 2009 +0800
@@ -19,14 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_SYS_USB_OHCI_POLLED_H
 #define	_SYS_USB_OHCI_POLLED_H
 
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -51,6 +50,7 @@
  * in use.
  */
 #define	POLLED_INPUT_MODE		0x01
+#define	POLLED_OUTPUT_MODE		0x10
 
 /*
  * These two flags are used to determine if this structure is already in
@@ -58,6 +58,7 @@
  * restore it once.  These flags are used for the ohci_polled_flags below.
  */
 #define	POLLED_INPUT_MODE_INUSE		0x04
+#define	POLLED_OUTPUT_MODE_INUSE	0x40
 
 /*
  * For ohci bandwidth of low speed interrupt devices limits,
--- a/usr/src/uts/common/sys/usb/hcd/openhci/ohcid.h	Thu Mar 19 10:21:31 2009 +0800
+++ b/usr/src/uts/common/sys/usb/hcd/openhci/ohcid.h	Thu Mar 19 14:02:20 2009 +0800
@@ -19,14 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _SYS_USB_OHCID_H
 #define	_SYS_USB_OHCID_H
 
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -807,6 +806,9 @@
 #define	Sync_IO_Buffer(dma_handle, length) \
 				(void) ddi_dma_sync(dma_handle, \
 				0, length, DDI_DMA_SYNC_FORCPU);
+#define	Sync_IO_Buffer_for_device(dma_handle, length) \
+				(void) ddi_dma_sync(dma_handle, \
+				0, length, DDI_DMA_SYNC_FORDEV);
 
 /*
  * Macros to speed handling of 32bit IDs
@@ -897,6 +899,21 @@
 int		ohci_hcdi_polled_input_fini(
 				usb_console_info_impl_t	*info);
 
+int		ohci_hcdi_polled_output_init(
+				usba_pipe_handle_data_t	*ph,
+				usb_console_info_impl_t	*console_output_info);
+int		ohci_hcdi_polled_output_enter(
+				usb_console_info_impl_t	*info);
+int		ohci_hcdi_polled_write(
+				usb_console_info_impl_t *info,
+				uchar_t *buf,
+				uint_t num_characters,
+				uint_t *num_characters_written);
+int		ohci_hcdi_polled_output_exit(
+				usb_console_info_impl_t	*info);
+int		ohci_hcdi_polled_output_fini(
+				usb_console_info_impl_t *info);
+
 /* Root hub related functions */
 int		ohci_init_root_hub(
 				ohci_state_t		*ohcip);
@@ -986,6 +1003,10 @@
 void		ohci_handle_outstanding_requests(
 				ohci_state_t		*ohcip,
 				ohci_pipe_private_t	*pp);
+int	ohci_allocate_tds_for_tw(
+				ohci_state_t		*ohcip,
+				ohci_trans_wrapper_t	*tw,
+				size_t			td_count);
 
 #ifdef __cplusplus
 }