17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5b3001defSlg  * Common Development and Distribution License (the "License").
6b3001defSlg  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22112116d8Sfb  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * EHCI Host Controller Driver (EHCI)
297c478bd9Sstevel@tonic-gate  *
307c478bd9Sstevel@tonic-gate  * The EHCI driver is a software driver which interfaces to the Universal
317c478bd9Sstevel@tonic-gate  * Serial Bus layer (USBA) and the Host Controller (HC). The interface to
327c478bd9Sstevel@tonic-gate  * the Host Controller is defined by the EHCI Host Controller Interface.
337c478bd9Sstevel@tonic-gate  *
347c478bd9Sstevel@tonic-gate  * This module contains the EHCI driver isochronous code, which handles all
357c478bd9Sstevel@tonic-gate  * Checking of status of USB transfers, error recovery and callbacks.
367c478bd9Sstevel@tonic-gate  */
377c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehcid.h>
387c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_xfer.h>
397c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_util.h>
407c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_isoch.h>
417c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_isoch_util.h>
42*d29f5a71Szhigang lu - Sun Microsystems - Beijing China #include <sys/strsun.h>
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate /*
457c478bd9Sstevel@tonic-gate  * Isochronous initialization functions
467c478bd9Sstevel@tonic-gate  */
477c478bd9Sstevel@tonic-gate int ehci_isoc_init(
487c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip);
497c478bd9Sstevel@tonic-gate void ehci_isoc_cleanup(
507c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip);
517c478bd9Sstevel@tonic-gate void ehci_isoc_pipe_cleanup(
527c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
537c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t *ph);
547c478bd9Sstevel@tonic-gate static void ehci_wait_for_isoc_completion(
557c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
567c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp);
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate /*
597c478bd9Sstevel@tonic-gate  * Isochronous request functions
607c478bd9Sstevel@tonic-gate  */
617c478bd9Sstevel@tonic-gate ehci_isoc_xwrapper_t *ehci_allocate_isoc_resources(
627c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
637c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t *ph,
647c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_reqp,
657c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags);
667c478bd9Sstevel@tonic-gate int ehci_insert_isoc_req(
677c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
687c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
697c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
707c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags);
71b3001defSlg static int ehci_insert_itd_req(
72b3001defSlg 	ehci_state_t		*ehcip,
73b3001defSlg 	ehci_pipe_private_t	*pp,
74b3001defSlg 	ehci_isoc_xwrapper_t	*itw,
75b3001defSlg 	usb_flags_t		usb_flags);
767c478bd9Sstevel@tonic-gate static int ehci_insert_sitd_req(
777c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
787c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
797c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
807c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags);
817c478bd9Sstevel@tonic-gate static void ehci_remove_isoc_itds(
827c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
837c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp);
847c478bd9Sstevel@tonic-gate static void ehci_mark_reclaim_isoc(
857c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
867c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp);
877c478bd9Sstevel@tonic-gate static void ehci_reclaim_isoc(
887c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
897c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
907c478bd9Sstevel@tonic-gate 	ehci_itd_t		*itd,
917c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp);
927c478bd9Sstevel@tonic-gate int	ehci_start_isoc_polling(
937c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
947c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
957c478bd9Sstevel@tonic-gate 	usb_flags_t		flags);
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate /*
987c478bd9Sstevel@tonic-gate  * Isochronronous handling functions.
997c478bd9Sstevel@tonic-gate  */
1007c478bd9Sstevel@tonic-gate void ehci_traverse_active_isoc_list(
1017c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip);
1027c478bd9Sstevel@tonic-gate static void ehci_handle_isoc(
1037c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
1047c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
1057c478bd9Sstevel@tonic-gate 	ehci_itd_t		*itd);
1067c478bd9Sstevel@tonic-gate static void ehci_handle_itd(
1077c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
1087c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
1097c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
1107c478bd9Sstevel@tonic-gate 	ehci_itd_t		*itd,
1117c478bd9Sstevel@tonic-gate 	void			*tw_handle_callback_value);
1127c478bd9Sstevel@tonic-gate static void ehci_sendup_itd_message(
1137c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
1147c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
1157c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
1167c478bd9Sstevel@tonic-gate 	ehci_itd_t		*td,
1177c478bd9Sstevel@tonic-gate 	usb_cr_t		error);
1187c478bd9Sstevel@tonic-gate void ehci_hcdi_isoc_callback(
1197c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
1207c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
1217c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason);
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate /*
1257c478bd9Sstevel@tonic-gate  * Isochronous initialization functions
1267c478bd9Sstevel@tonic-gate  */
1277c478bd9Sstevel@tonic-gate /*
1287c478bd9Sstevel@tonic-gate  * Initialize all the needed resources needed by isochronous pipes.
1297c478bd9Sstevel@tonic-gate  */
1307c478bd9Sstevel@tonic-gate int
1317c478bd9Sstevel@tonic-gate ehci_isoc_init(
1327c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip)
1337c478bd9Sstevel@tonic-gate {
1347c478bd9Sstevel@tonic-gate 	return (ehci_allocate_isoc_pools(ehcip));
1357c478bd9Sstevel@tonic-gate }
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate /*
1397c478bd9Sstevel@tonic-gate  * Cleanup isochronous resources.
1407c478bd9Sstevel@tonic-gate  */
1417c478bd9Sstevel@tonic-gate void
1427c478bd9Sstevel@tonic-gate ehci_isoc_cleanup(
1437c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip)
1447c478bd9Sstevel@tonic-gate {
1457c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw;
1467c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp;
1477c478bd9Sstevel@tonic-gate 	ehci_itd_t		*itd;
1487c478bd9Sstevel@tonic-gate 	int			i, ctrl, rval;
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 	/* Free all the buffers */
1517c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_itd_pool_addr && ehcip->ehci_itd_pool_mem_handle) {
1527c478bd9Sstevel@tonic-gate 		for (i = 0; i < ehci_get_itd_pool_size(); i ++) {
1537c478bd9Sstevel@tonic-gate 			itd = &ehcip->ehci_itd_pool_addr[i];
1547c478bd9Sstevel@tonic-gate 			ctrl = Get_ITD(ehcip->
1557c478bd9Sstevel@tonic-gate 			    ehci_itd_pool_addr[i].itd_state);
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 			if ((ctrl != EHCI_ITD_FREE) &&
1587c478bd9Sstevel@tonic-gate 			    (ctrl != EHCI_ITD_DUMMY) &&
1597c478bd9Sstevel@tonic-gate 			    (itd->itd_trans_wrapper)) {
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 				mutex_enter(&ehcip->ehci_int_mutex);
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 				itw = (ehci_isoc_xwrapper_t *)
1640a05e705Slc 				    EHCI_LOOKUP_ID((uint32_t)
1650a05e705Slc 				    Get_ITD(itd->itd_trans_wrapper));
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 				/* Obtain the pipe private structure */
1687c478bd9Sstevel@tonic-gate 				pp = itw->itw_pipe_private;
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 				ehci_deallocate_itd(ehcip, itw, itd);
1717c478bd9Sstevel@tonic-gate 				ehci_deallocate_itw(ehcip, pp, itw);
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 				mutex_exit(&ehcip->ehci_int_mutex);
1747c478bd9Sstevel@tonic-gate 			}
1757c478bd9Sstevel@tonic-gate 		}
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 		/*
1787c478bd9Sstevel@tonic-gate 		 * If EHCI_ITD_POOL_BOUND flag is set, then unbind
1797c478bd9Sstevel@tonic-gate 		 * the handle for ITD pools.
1807c478bd9Sstevel@tonic-gate 		 */
1817c478bd9Sstevel@tonic-gate 		if ((ehcip->ehci_dma_addr_bind_flag &
1827c478bd9Sstevel@tonic-gate 		    EHCI_ITD_POOL_BOUND) == EHCI_ITD_POOL_BOUND) {
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 			rval = ddi_dma_unbind_handle(
1857c478bd9Sstevel@tonic-gate 			    ehcip->ehci_itd_pool_dma_handle);
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 			ASSERT(rval == DDI_SUCCESS);
1887c478bd9Sstevel@tonic-gate 		}
1897c478bd9Sstevel@tonic-gate 		ddi_dma_mem_free(&ehcip->ehci_itd_pool_mem_handle);
1907c478bd9Sstevel@tonic-gate 	}
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	/* Free the ITD pool */
1937c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_itd_pool_dma_handle) {
1947c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&ehcip->ehci_itd_pool_dma_handle);
1957c478bd9Sstevel@tonic-gate 	}
1967c478bd9Sstevel@tonic-gate }
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate /*
2007c478bd9Sstevel@tonic-gate  * ehci_isoc_pipe_cleanup
2017c478bd9Sstevel@tonic-gate  *
2027c478bd9Sstevel@tonic-gate  * Cleanup ehci isoc pipes.
2037c478bd9Sstevel@tonic-gate  */
2047c478bd9Sstevel@tonic-gate void ehci_isoc_pipe_cleanup(
2057c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
2067c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t *ph) {
2077c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
2087c478bd9Sstevel@tonic-gate 	uint_t			pipe_state = pp->pp_state;
2097c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason;
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
2127c478bd9Sstevel@tonic-gate 	    "ehci_isoc_pipe_cleanup: ph = 0x%p", (void *)ph);
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	/* Stop all further processing */
2177c478bd9Sstevel@tonic-gate 	ehci_mark_reclaim_isoc(ehcip, pp);
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 	/*
2207c478bd9Sstevel@tonic-gate 	 * Wait for processing all completed transfers
2217c478bd9Sstevel@tonic-gate 	 * and send result upstream/
2227c478bd9Sstevel@tonic-gate 	 */
2237c478bd9Sstevel@tonic-gate 	ehci_wait_for_isoc_completion(ehcip, pp);
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	/* Go ahead and remove all remaining itds if there are any */
2267c478bd9Sstevel@tonic-gate 	ehci_remove_isoc_itds(ehcip, pp);
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 	switch (pipe_state) {
2297c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_CLOSE:
2307c478bd9Sstevel@tonic-gate 		completion_reason = USB_CR_PIPE_CLOSING;
2317c478bd9Sstevel@tonic-gate 		break;
2327c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_RESET:
2337c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_STOP_POLLING:
2347c478bd9Sstevel@tonic-gate 		/* Set completion reason */
2357c478bd9Sstevel@tonic-gate 		completion_reason = (pipe_state ==
2367c478bd9Sstevel@tonic-gate 		    EHCI_PIPE_STATE_RESET) ?
2377c478bd9Sstevel@tonic-gate 		    USB_CR_PIPE_RESET: USB_CR_STOPPED_POLLING;
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 		/* Set pipe state to idle */
2407c478bd9Sstevel@tonic-gate 		pp->pp_state = EHCI_PIPE_STATE_IDLE;
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 		break;
2437c478bd9Sstevel@tonic-gate 	}
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	/*
2467c478bd9Sstevel@tonic-gate 	 * Do the callback for the original client
2477c478bd9Sstevel@tonic-gate 	 * periodic IN request.
2487c478bd9Sstevel@tonic-gate 	 */
2497c478bd9Sstevel@tonic-gate 	if ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) ==
2507c478bd9Sstevel@tonic-gate 	    USB_EP_DIR_IN) {
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 		ehci_do_client_periodic_in_req_callback(
2537c478bd9Sstevel@tonic-gate 		    ehcip, pp, completion_reason);
2547c478bd9Sstevel@tonic-gate 	}
2557c478bd9Sstevel@tonic-gate }
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate /*
2597c478bd9Sstevel@tonic-gate  * ehci_wait_for_transfers_completion:
2607c478bd9Sstevel@tonic-gate  *
2617c478bd9Sstevel@tonic-gate  * Wait for processing all completed transfers and to send results
2627c478bd9Sstevel@tonic-gate  * to upstream.
2637c478bd9Sstevel@tonic-gate  */
2647c478bd9Sstevel@tonic-gate static void
2657c478bd9Sstevel@tonic-gate ehci_wait_for_isoc_completion(
2667c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
2677c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
2687c478bd9Sstevel@tonic-gate {
2697c478bd9Sstevel@tonic-gate 	clock_t			xfer_cmpl_time_wait;
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 	if (pp->pp_itw_head == NULL) {
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 		return;
2767c478bd9Sstevel@tonic-gate 	}
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 	/* Get the number of clock ticks to wait */
2797c478bd9Sstevel@tonic-gate 	xfer_cmpl_time_wait = drv_usectohz(EHCI_XFER_CMPL_TIMEWAIT * 1000000);
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 	(void) cv_timedwait(&pp->pp_xfer_cmpl_cv,
2827c478bd9Sstevel@tonic-gate 	    &ehcip->ehci_int_mutex,
2837c478bd9Sstevel@tonic-gate 	    ddi_get_lbolt() + xfer_cmpl_time_wait);
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	if (pp->pp_itw_head) {
2867c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
2877c478bd9Sstevel@tonic-gate 		    "ehci_wait_for_isoc_completion: "
2887c478bd9Sstevel@tonic-gate 		    "No transfers completion confirmation received");
2897c478bd9Sstevel@tonic-gate 	}
2907c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate /*
2947c478bd9Sstevel@tonic-gate  *  Isochronous request functions
2957c478bd9Sstevel@tonic-gate  */
2967c478bd9Sstevel@tonic-gate /*
2977c478bd9Sstevel@tonic-gate  * ehci_allocate_isoc_resources:
2987c478bd9Sstevel@tonic-gate  *
2997c478bd9Sstevel@tonic-gate  * Calculates the number of tds necessary for a isoch transfer, and
3007c478bd9Sstevel@tonic-gate  * allocates all the necessary resources.
3017c478bd9Sstevel@tonic-gate  *
3027c478bd9Sstevel@tonic-gate  * Returns NULL if there is insufficient resources otherwise ITW.
3037c478bd9Sstevel@tonic-gate  */
3047c478bd9Sstevel@tonic-gate ehci_isoc_xwrapper_t *
3057c478bd9Sstevel@tonic-gate ehci_allocate_isoc_resources(
3067c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
3077c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t *ph,
3087c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_reqp,
3097c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
3107c478bd9Sstevel@tonic-gate {
3117c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
312b3001defSlg 	int			pipe_dir, i;
3137c478bd9Sstevel@tonic-gate 	uint_t			max_ep_pkt_size, max_isoc_xfer_size;
3147c478bd9Sstevel@tonic-gate 	usb_isoc_pkt_descr_t	*isoc_pkt_descr;
315b3001defSlg 	size_t			isoc_pkt_count, isoc_pkts_length;
3160a05e705Slc 	size_t			itw_xfer_size = 0;
3177c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw;
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
3207c478bd9Sstevel@tonic-gate 	    "ehci_allocate_isoc_resources: flags = 0x%x", usb_flags);
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 	/*
3257c478bd9Sstevel@tonic-gate 	 * Check whether pipe is in halted state.
3267c478bd9Sstevel@tonic-gate 	 */
3277c478bd9Sstevel@tonic-gate 	if (pp->pp_state == EHCI_PIPE_STATE_ERROR) {
3287c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
3297c478bd9Sstevel@tonic-gate 		    "ehci_allocate_isoc_resources:"
3307c478bd9Sstevel@tonic-gate 		    "Pipe is in error state, need pipe reset to continue");
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 		return (NULL);
3337c478bd9Sstevel@tonic-gate 	}
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	/* Calculate the maximum isochronous transfer size we allow */
3360a05e705Slc 	max_ep_pkt_size = (ph->p_ep.wMaxPacketSize &
3370a05e705Slc 	    EHCI_ITD_CTRL_MAX_PACKET_MASK) *
3380a05e705Slc 	    CalculateITDMultiField(ph->p_ep.wMaxPacketSize);
3390a05e705Slc 
3407c478bd9Sstevel@tonic-gate 	max_isoc_xfer_size = EHCI_MAX_ISOC_PKTS_PER_XFER * max_ep_pkt_size;
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 	/* Get the packet descriptor and number of packets to send */
3437c478bd9Sstevel@tonic-gate 	if (isoc_reqp) {
3447c478bd9Sstevel@tonic-gate 		isoc_pkt_descr = isoc_reqp->isoc_pkt_descr;
3457c478bd9Sstevel@tonic-gate 		isoc_pkt_count = isoc_reqp->isoc_pkts_count;
346b3001defSlg 		isoc_pkts_length = isoc_reqp->isoc_pkts_length;
3477c478bd9Sstevel@tonic-gate 	} else {
3487c478bd9Sstevel@tonic-gate 		isoc_pkt_descr = ((usb_isoc_req_t *)
3497c478bd9Sstevel@tonic-gate 		    pp->pp_client_periodic_in_reqp)->isoc_pkt_descr;
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 		isoc_pkt_count = ((usb_isoc_req_t *)
3527c478bd9Sstevel@tonic-gate 		    pp->pp_client_periodic_in_reqp)->isoc_pkts_count;
353b3001defSlg 
354b3001defSlg 		isoc_pkts_length = ((usb_isoc_req_t *)
3557c478bd9Sstevel@tonic-gate 		    pp->pp_client_periodic_in_reqp)->isoc_pkts_length;
3567c478bd9Sstevel@tonic-gate 	}
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	/* Calculate the size of the transfer. */
3597c478bd9Sstevel@tonic-gate 	pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
3607c478bd9Sstevel@tonic-gate 	if (pipe_dir == USB_EP_DIR_IN) {
361b3001defSlg 		for (i = 0; i < isoc_pkt_count; i++) {
3620a05e705Slc 			/*
3630a05e705Slc 			 * isoc_pkt_length is used as Transaction Length and
3640a05e705Slc 			 * according to EHCI spec Table 3-3, the maximum value
3650a05e705Slc 			 * allowed is 3072
3660a05e705Slc 			 */
3670a05e705Slc 			if (isoc_pkt_descr->isoc_pkt_length > 3072) {
3680a05e705Slc 
3690a05e705Slc 				return (NULL);
3700a05e705Slc 			}
3710a05e705Slc 
372b3001defSlg 			itw_xfer_size += isoc_pkt_descr->isoc_pkt_length;
3730a05e705Slc 
374b3001defSlg 			isoc_pkt_descr++;
375b3001defSlg 		}
376b3001defSlg 
377b3001defSlg 		if ((isoc_pkts_length) &&
3780a05e705Slc 		    (isoc_pkts_length != itw_xfer_size)) {
379b3001defSlg 
380b3001defSlg 			USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
381b3001defSlg 			    "ehci_allocate_isoc_resources: "
382112116d8Sfb 			    "isoc_pkts_length 0x%lx is not equal to the sum of "
383112116d8Sfb 			    "all pkt lengths 0x%lx in an isoc request",
384b3001defSlg 			    isoc_pkts_length, itw_xfer_size);
385b3001defSlg 
386b3001defSlg 			return (NULL);
387b3001defSlg 		}
388b3001defSlg 
3897c478bd9Sstevel@tonic-gate 	} else {
3907c478bd9Sstevel@tonic-gate 		ASSERT(isoc_reqp != NULL);
391*d29f5a71Szhigang lu - Sun Microsystems - Beijing China 		itw_xfer_size = MBLKL(isoc_reqp->isoc_data);
3927c478bd9Sstevel@tonic-gate 	}
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 	/* Check the size of isochronous request */
3957c478bd9Sstevel@tonic-gate 	if (itw_xfer_size > max_isoc_xfer_size) {
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
398b3001defSlg 		    "ehci_allocate_isoc_resources: Maximum isoc request "
399112116d8Sfb 		    "size 0x%x Given isoc request size 0x%lx",
4007c478bd9Sstevel@tonic-gate 		    max_isoc_xfer_size, itw_xfer_size);
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 		return (NULL);
4037c478bd9Sstevel@tonic-gate 	}
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
406112116d8Sfb 	    "ehci_allocate_isoc_resources: length = 0x%lx", itw_xfer_size);
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 	/* Allocate the itw for this request */
4097c478bd9Sstevel@tonic-gate 	if ((itw = ehci_allocate_itw_resources(ehcip, pp, itw_xfer_size,
4107c478bd9Sstevel@tonic-gate 	    usb_flags, isoc_pkt_count)) == NULL) {
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 		return (NULL);
4137c478bd9Sstevel@tonic-gate 	}
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate 	itw->itw_handle_callback_value = NULL;
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 	if (pipe_dir == USB_EP_DIR_IN) {
4187c478bd9Sstevel@tonic-gate 		if (ehci_allocate_isoc_in_resource(ehcip, pp, itw, usb_flags) !=
4197c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 			ehci_deallocate_itw(ehcip, pp, itw);
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 			return (NULL);
4247c478bd9Sstevel@tonic-gate 		}
4257c478bd9Sstevel@tonic-gate 	} else {
4267c478bd9Sstevel@tonic-gate 		if (itw->itw_length) {
4277c478bd9Sstevel@tonic-gate 			ASSERT(isoc_reqp->isoc_data != NULL);
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 			/* Copy the data into the buffer */
4307c478bd9Sstevel@tonic-gate 			bcopy(isoc_reqp->isoc_data->b_rptr,
4317c478bd9Sstevel@tonic-gate 			    itw->itw_buf, itw->itw_length);
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 			Sync_IO_Buffer_for_device(itw->itw_dmahandle,
4347c478bd9Sstevel@tonic-gate 			    itw->itw_length);
4357c478bd9Sstevel@tonic-gate 		}
4367c478bd9Sstevel@tonic-gate 		itw->itw_curr_xfer_reqp = isoc_reqp;
4377c478bd9Sstevel@tonic-gate 	}
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 	return (itw);
4407c478bd9Sstevel@tonic-gate }
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate /*
4447c478bd9Sstevel@tonic-gate  * ehci_insert_isoc_req:
4457c478bd9Sstevel@tonic-gate  *
4467c478bd9Sstevel@tonic-gate  * Insert an isochronous request into the Host Controller's
4477c478bd9Sstevel@tonic-gate  * isochronous list.
4487c478bd9Sstevel@tonic-gate  */
4497c478bd9Sstevel@tonic-gate int
4507c478bd9Sstevel@tonic-gate ehci_insert_isoc_req(
4517c478bd9Sstevel@tonic-gate 	ehci_state_t			*ehcip,
4527c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t		*pp,
4537c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t		*itw,
4547c478bd9Sstevel@tonic-gate 	usb_flags_t			usb_flags)
4557c478bd9Sstevel@tonic-gate {
4567c478bd9Sstevel@tonic-gate 	int			error;
457b3001defSlg 	ehci_itd_t		*new_itd, *temp_itd;
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
4607c478bd9Sstevel@tonic-gate 	    "ehci_insert_isoc_req: flags = 0x%x port status = 0x%x",
4617c478bd9Sstevel@tonic-gate 	    usb_flags, itw->itw_port_status);
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	ASSERT(itw->itw_curr_xfer_reqp != NULL);
4667c478bd9Sstevel@tonic-gate 	ASSERT(itw->itw_curr_xfer_reqp->isoc_pkt_descr != NULL);
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	/*
4697c478bd9Sstevel@tonic-gate 	 * Save address of first usb isochronous packet descriptor.
4707c478bd9Sstevel@tonic-gate 	 */
4717c478bd9Sstevel@tonic-gate 	itw->itw_curr_isoc_pktp = itw->itw_curr_xfer_reqp->isoc_pkt_descr;
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	if (itw->itw_port_status == USBA_HIGH_SPEED_DEV) {
474b3001defSlg 		error = ehci_insert_itd_req(ehcip, pp, itw, usb_flags);
4757c478bd9Sstevel@tonic-gate 	} else {
4767c478bd9Sstevel@tonic-gate 		error = ehci_insert_sitd_req(ehcip, pp, itw, usb_flags);
4777c478bd9Sstevel@tonic-gate 	}
4787c478bd9Sstevel@tonic-gate 
479b3001defSlg 	/* Either all the isocs will be added or none of them will */
480b3001defSlg 	error = ehci_insert_isoc_to_pfl(ehcip, pp, itw);
481b3001defSlg 
482b3001defSlg 	if (error != USB_SUCCESS) {
483b3001defSlg 		/*
484b3001defSlg 		 * Deallocate all the ITDs, otherwise they will be
485b3001defSlg 		 * lost forever.
486b3001defSlg 		 */
487b3001defSlg 		new_itd = itw->itw_itd_head;
488b3001defSlg 		while (new_itd) {
489b3001defSlg 			temp_itd = ehci_itd_iommu_to_cpu(ehcip,
490b3001defSlg 			    Get_ITD(new_itd->itd_itw_next_itd));
491b3001defSlg 			ehci_deallocate_itd(ehcip, itw, new_itd);
492b3001defSlg 			new_itd = temp_itd;
493b3001defSlg 		}
494b3001defSlg 		if ((itw->itw_direction == USB_EP_DIR_IN)) {
495b3001defSlg 			ehci_deallocate_isoc_in_resource(ehcip, pp, itw);
496b3001defSlg 
497b3001defSlg 			if (pp->pp_cur_periodic_req_cnt) {
498b3001defSlg 				/*
499b3001defSlg 				 * Set pipe state to stop polling and
500b3001defSlg 				 * error to no resource. Don't insert
501b3001defSlg 				 * any more isoch polling requests.
502b3001defSlg 				 */
503b3001defSlg 				pp->pp_state =
504b3001defSlg 				    EHCI_PIPE_STATE_STOP_POLLING;
505b3001defSlg 				pp->pp_error = error;
506b3001defSlg 			} else {
507b3001defSlg 				/* Set periodic in pipe state to idle */
508b3001defSlg 				pp->pp_state = EHCI_PIPE_STATE_IDLE;
509b3001defSlg 			}
510b3001defSlg 
511b3001defSlg 			return (error);
512b3001defSlg 		}
513b3001defSlg 
514b3001defSlg 		/* Save how many packets and data actually went */
515b3001defSlg 		itw->itw_num_itds = 0;
516b3001defSlg 		itw->itw_length  = 0;
517b3001defSlg 	}
518b3001defSlg 
519b3001defSlg 	/*
520b3001defSlg 	 * Reset back to the address of first usb isochronous
521b3001defSlg 	 * packet descriptor.
522b3001defSlg 	 */
523b3001defSlg 	itw->itw_curr_isoc_pktp = itw->itw_curr_xfer_reqp->isoc_pkt_descr;
524b3001defSlg 
525b3001defSlg 	/* Reset the CONTINUE flag */
526b3001defSlg 	pp->pp_flag &= ~EHCI_ISOC_XFER_CONTINUE;
527b3001defSlg 
5287c478bd9Sstevel@tonic-gate 	return (error);
5297c478bd9Sstevel@tonic-gate }
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 
532b3001defSlg /*
533b3001defSlg  * ehci_insert_itd_req:
534b3001defSlg  *
535b3001defSlg  * Insert an ITD request into the Host Controller's isochronous list.
536b3001defSlg  */
537b3001defSlg /* ARGSUSED */
538b3001defSlg static int
539b3001defSlg ehci_insert_itd_req(
540b3001defSlg 	ehci_state_t		*ehcip,
541b3001defSlg 	ehci_pipe_private_t	*pp,
542b3001defSlg 	ehci_isoc_xwrapper_t	*itw,
543b3001defSlg 	usb_flags_t		usb_flags)
544b3001defSlg {
545b3001defSlg 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
546b3001defSlg 	usb_isoc_req_t		*curr_isoc_reqp;
547b3001defSlg 	usb_isoc_pkt_descr_t	*curr_isoc_pkt_descr;
548b3001defSlg 	size_t			curr_isoc_xfer_offset;
549b3001defSlg 	size_t			isoc_pkt_length;
550b3001defSlg 	uint_t			count, xactcount;
551b3001defSlg 	uint32_t		xact_status;
552b3001defSlg 	uint32_t		page, pageselected;
553b3001defSlg 	uint32_t		buf[EHCI_ITD_BUFFER_LIST_SIZE];
554b3001defSlg 	uint16_t		index = 0;
555b3001defSlg 	uint16_t		multi = 0;
556b3001defSlg 	ehci_itd_t		*new_itd;
557b3001defSlg 
558b3001defSlg 	/*
559b3001defSlg 	 * Get the current isochronous request and packet
560b3001defSlg 	 * descriptor pointers.
561b3001defSlg 	 */
562b3001defSlg 	curr_isoc_reqp = (usb_isoc_req_t *)itw->itw_curr_xfer_reqp;
563b3001defSlg 
564b3001defSlg 	page = itw->itw_cookie.dmac_address;
565b3001defSlg 	ASSERT((page % EHCI_4K_ALIGN) == 0);
566b3001defSlg 
567b3001defSlg 	USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
568112116d8Sfb 	    "ehci_insert_itd_req: itw_curr_xfer_reqp = 0x%p page = 0x%x,"
569112116d8Sfb 	    " pagesize = 0x%lx", (void *)itw->itw_curr_xfer_reqp, page,
5700a05e705Slc 	    itw->itw_cookie.dmac_size);
571b3001defSlg 
572b3001defSlg 	/* Insert all the isochronous TDs */
573b3001defSlg 	count = 0;
574b3001defSlg 	curr_isoc_xfer_offset = 0;
575b3001defSlg 
576b3001defSlg 	while (count < curr_isoc_reqp->isoc_pkts_count) {
577b3001defSlg 
578b3001defSlg 		/* Grab a new itd */
579b3001defSlg 		new_itd = itw->itw_itd_free_list;
580b3001defSlg 
581b3001defSlg 		ASSERT(new_itd != NULL);
582b3001defSlg 
583b3001defSlg 		itw->itw_itd_free_list = ehci_itd_iommu_to_cpu(ehcip,
584b3001defSlg 		    Get_ITD(new_itd->itd_link_ptr));
585b3001defSlg 		Set_ITD(new_itd->itd_link_ptr, NULL);
586b3001defSlg 
587b3001defSlg 		bzero(buf, EHCI_ITD_BUFFER_LIST_SIZE * sizeof (uint32_t));
588b3001defSlg 
589b3001defSlg 		multi = CalculateITDMultiField(ph->p_ep.wMaxPacketSize);
590b3001defSlg 
591b3001defSlg 		if (multi > EHCI_ITD_CTRL_MULTI_MASK) {
592b3001defSlg 			USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
593b3001defSlg 			    "ehci_insert_itd_req: Wrong multi value.");
594b3001defSlg 
595b3001defSlg 			return (USB_FAILURE);
596b3001defSlg 		}
597b3001defSlg 
598b3001defSlg 		/* Fill 8 transaction for every iTD */
599b3001defSlg 		for (xactcount = 0, pageselected = 0;
600b3001defSlg 		    xactcount < EHCI_ITD_CTRL_LIST_SIZE; xactcount++) {
601b3001defSlg 
602b3001defSlg 			curr_isoc_pkt_descr = itw->itw_curr_isoc_pktp;
603b3001defSlg 
6040a05e705Slc 			isoc_pkt_length =
6050a05e705Slc 			    curr_isoc_pkt_descr->isoc_pkt_length;
606b3001defSlg 
607b3001defSlg 			curr_isoc_pkt_descr->isoc_pkt_actual_length
608*d29f5a71Szhigang lu - Sun Microsystems - Beijing China 			    = (ushort_t)isoc_pkt_length;
609b3001defSlg 
610b3001defSlg 			xact_status = 0;
611b3001defSlg 
612b3001defSlg 			if (pageselected < EHCI_ITD_BUFFER_LIST_SIZE) {
613b3001defSlg 
614b3001defSlg 				buf[pageselected] |= page;
615b3001defSlg 			} else {
616b3001defSlg 				USB_DPRINTF_L2(PRINT_MASK_INTR,
617b3001defSlg 				    ehcip->ehci_log_hdl,
618b3001defSlg 				    "ehci_insert_itd_req: "
619b3001defSlg 				    "Error in buffer pointer.");
620b3001defSlg 
621b3001defSlg 				return (USB_FAILURE);
622b3001defSlg 			}
623b3001defSlg 
624*d29f5a71Szhigang lu - Sun Microsystems - Beijing China 			xact_status = (uint32_t)curr_isoc_xfer_offset;
625b3001defSlg 			xact_status |= (pageselected << 12);
6260a05e705Slc 			xact_status |= isoc_pkt_length << 16;
627b3001defSlg 			xact_status |= EHCI_ITD_XFER_ACTIVE;
628b3001defSlg 
629b3001defSlg 			/* Set IOC on the last TD. */
630b3001defSlg 			if (count == (curr_isoc_reqp->isoc_pkts_count - 1)) {
631b3001defSlg 				xact_status |= EHCI_ITD_XFER_IOC_ON;
632b3001defSlg 			}
633b3001defSlg 
634b3001defSlg 			USB_DPRINTF_L3(PRINT_MASK_INTR,
635b3001defSlg 			    ehcip->ehci_log_hdl,
636b3001defSlg 			    "ehci_insert_itd_req: count = 0x%x multi = %d"
637b3001defSlg 			    "status = 0x%x page = 0x%x index = %d "
638112116d8Sfb 			    "pageselected = %d isoc_pkt_length = 0x%lx",
639b3001defSlg 			    xactcount, multi, xact_status, page,
640b3001defSlg 			    index, pageselected, isoc_pkt_length);
641b3001defSlg 
642b3001defSlg 			/* Fill in the new itd */
643b3001defSlg 			Set_ITD_BODY(new_itd, xactcount, xact_status);
644b3001defSlg 
645b3001defSlg 			itw->itw_curr_isoc_pktp++;
646b3001defSlg 			Set_ITD_INDEX(new_itd, xactcount, index++);
647b3001defSlg 
6480a05e705Slc 			curr_isoc_xfer_offset += isoc_pkt_length;
649b3001defSlg 
650b3001defSlg 			if (curr_isoc_xfer_offset >= EHCI_4K_ALIGN) {
651b3001defSlg 				pageselected ++;
652b3001defSlg 				page += EHCI_4K_ALIGN;
653b3001defSlg 				curr_isoc_xfer_offset -= EHCI_4K_ALIGN;
654b3001defSlg 			}
655b3001defSlg 
656b3001defSlg 			count ++;
657b3001defSlg 			if (count >= curr_isoc_reqp->isoc_pkts_count) {
658b3001defSlg 
6590a05e705Slc 				break;
660b3001defSlg 			}
661b3001defSlg 		}
662b3001defSlg 
663b3001defSlg 		buf[0] |= (itw->itw_endpoint_num << 8);
664b3001defSlg 		buf[0] |= itw->itw_device_addr;
6650a05e705Slc 		buf[1] |= ph->p_ep.wMaxPacketSize &
6660a05e705Slc 		    EHCI_ITD_CTRL_MAX_PACKET_MASK;
6670a05e705Slc 
668b3001defSlg 		if (itw->itw_direction == USB_EP_DIR_IN) {
669b3001defSlg 			buf[1] |= EHCI_ITD_CTRL_DIR_IN;
670b3001defSlg 		}
671b3001defSlg 		buf[2] |= multi;
672b3001defSlg 
673b3001defSlg 		Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER0, buf[0]);
674b3001defSlg 		Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER1, buf[1]);
675b3001defSlg 		Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER2, buf[2]);
676b3001defSlg 		Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER3, buf[3]);
677b3001defSlg 		Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER4, buf[4]);
678b3001defSlg 		Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER5, buf[5]);
679b3001defSlg 		Set_ITD_BODY(new_itd, EHCI_ITD_BUFFER6, buf[6]);
680b3001defSlg 
681b3001defSlg 		Set_ITD(new_itd->itd_state, EHCI_ITD_ACTIVE);
682b3001defSlg 		ehci_print_itd(ehcip, new_itd);
683b3001defSlg 
684b3001defSlg 		/*
685b3001defSlg 		 * Add this itd to the itw before we add it in the PFL
686b3001defSlg 		 * If adding it to the PFL fails, we will have to cleanup.
687b3001defSlg 		 */
688b3001defSlg 		ehci_insert_itd_on_itw(ehcip, itw, new_itd);
689b3001defSlg 
690b3001defSlg 	}
691b3001defSlg 
692b3001defSlg 	return (USB_SUCCESS);
693b3001defSlg }
694b3001defSlg 
695b3001defSlg 
6967c478bd9Sstevel@tonic-gate /*
6977c478bd9Sstevel@tonic-gate  * ehci_insert_sitd_req:
6987c478bd9Sstevel@tonic-gate  *
6997c478bd9Sstevel@tonic-gate  * Insert an SITD request into the Host Controller's isochronous list.
7007c478bd9Sstevel@tonic-gate  */
7017c478bd9Sstevel@tonic-gate /* ARGSUSED */
7027c478bd9Sstevel@tonic-gate static int
7037c478bd9Sstevel@tonic-gate ehci_insert_sitd_req(
7047c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
7057c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
7067c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
7077c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
7087c478bd9Sstevel@tonic-gate {
7097c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
7107c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*curr_isoc_reqp;
7117c478bd9Sstevel@tonic-gate 	usb_isoc_pkt_descr_t	*curr_isoc_pkt_descr;
7127c478bd9Sstevel@tonic-gate 	size_t			curr_isoc_xfer_offset;
7137c478bd9Sstevel@tonic-gate 	size_t			isoc_pkt_length;
714b3001defSlg 	uint_t			count;
7157c478bd9Sstevel@tonic-gate 	uint32_t		ctrl, uframe_sched, xfer_state;
7167c478bd9Sstevel@tonic-gate 	uint32_t		page0, page1, prev_sitd;
7177c478bd9Sstevel@tonic-gate 	uint32_t		ssplit_count;
718b3001defSlg 	ehci_itd_t		*new_sitd;
7197c478bd9Sstevel@tonic-gate 
7207c478bd9Sstevel@tonic-gate 	/*
7217c478bd9Sstevel@tonic-gate 	 * Get the current isochronous request and packet
7227c478bd9Sstevel@tonic-gate 	 * descriptor pointers.
7237c478bd9Sstevel@tonic-gate 	 */
7247c478bd9Sstevel@tonic-gate 	curr_isoc_reqp = (usb_isoc_req_t *)itw->itw_curr_xfer_reqp;
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 	/* Set the ctrl field */
7277c478bd9Sstevel@tonic-gate 	ctrl = 0;
7287c478bd9Sstevel@tonic-gate 	if (itw->itw_direction == USB_EP_DIR_IN) {
7297c478bd9Sstevel@tonic-gate 		ctrl |= EHCI_SITD_CTRL_DIR_IN;
7307c478bd9Sstevel@tonic-gate 	} else {
7317c478bd9Sstevel@tonic-gate 		ctrl |= EHCI_SITD_CTRL_DIR_OUT;
7327c478bd9Sstevel@tonic-gate 	}
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 	ctrl |= (itw->itw_hub_port << EHCI_SITD_CTRL_PORT_SHIFT) &
7357c478bd9Sstevel@tonic-gate 	    EHCI_SITD_CTRL_PORT_MASK;
7367c478bd9Sstevel@tonic-gate 	ctrl |= (itw->itw_hub_addr << EHCI_SITD_CTRL_HUB_SHIFT) &
7377c478bd9Sstevel@tonic-gate 	    EHCI_SITD_CTRL_HUB_MASK;
7387c478bd9Sstevel@tonic-gate 	ctrl |= (itw->itw_endpoint_num << EHCI_SITD_CTRL_END_PT_SHIFT) &
7397c478bd9Sstevel@tonic-gate 	    EHCI_SITD_CTRL_END_PT_MASK;
7407c478bd9Sstevel@tonic-gate 	ctrl |= (itw->itw_device_addr << EHCI_SITD_CTRL_DEVICE_SHIFT) &
7417c478bd9Sstevel@tonic-gate 	    EHCI_SITD_CTRL_DEVICE_MASK;
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate 	/* Set the micro frame schedule */
7447c478bd9Sstevel@tonic-gate 	uframe_sched = 0;
7457c478bd9Sstevel@tonic-gate 	uframe_sched |= (pp->pp_smask << EHCI_SITD_UFRAME_SMASK_SHIFT) &
7467c478bd9Sstevel@tonic-gate 	    EHCI_SITD_UFRAME_SMASK_MASK;
7477c478bd9Sstevel@tonic-gate 	uframe_sched |= (pp->pp_cmask << EHCI_SITD_UFRAME_CMASK_SHIFT) &
7487c478bd9Sstevel@tonic-gate 	    EHCI_SITD_UFRAME_CMASK_MASK;
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 	/* Set the default page information */
7517c478bd9Sstevel@tonic-gate 	page0 = itw->itw_cookie.dmac_address;
7527c478bd9Sstevel@tonic-gate 	page1 = 0;
7537c478bd9Sstevel@tonic-gate 
7547c478bd9Sstevel@tonic-gate 	prev_sitd = EHCI_ITD_LINK_PTR_INVALID;
7557c478bd9Sstevel@tonic-gate 
7567c478bd9Sstevel@tonic-gate 	/*
7577c478bd9Sstevel@tonic-gate 	 * Save the number of isochronous TDs needs
7587c478bd9Sstevel@tonic-gate 	 * to be insert to complete current isochronous request.
7597c478bd9Sstevel@tonic-gate 	 */
7607c478bd9Sstevel@tonic-gate 	itw->itw_num_itds = curr_isoc_reqp->isoc_pkts_count;
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	/* Insert all the isochronous TDs */
7637c478bd9Sstevel@tonic-gate 	for (count = 0, curr_isoc_xfer_offset = 0;
7640a05e705Slc 	    count < itw->itw_num_itds; count++) {
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 		curr_isoc_pkt_descr = itw->itw_curr_isoc_pktp;
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 		isoc_pkt_length = curr_isoc_pkt_descr->isoc_pkt_length;
769*d29f5a71Szhigang lu - Sun Microsystems - Beijing China 		curr_isoc_pkt_descr->isoc_pkt_actual_length =
770*d29f5a71Szhigang lu - Sun Microsystems - Beijing China 		    (ushort_t)isoc_pkt_length;
7717c478bd9Sstevel@tonic-gate 
7727c478bd9Sstevel@tonic-gate 		/* Set the transfer state information */
7737c478bd9Sstevel@tonic-gate 		xfer_state = 0;
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 		if (itw->itw_direction == USB_EP_DIR_IN) {
7767c478bd9Sstevel@tonic-gate 			/* Set the size to the max packet size */
7777c478bd9Sstevel@tonic-gate 			xfer_state |= (ph->p_ep.wMaxPacketSize <<
7787c478bd9Sstevel@tonic-gate 			    EHCI_SITD_XFER_TOTAL_SHIFT) &
7797c478bd9Sstevel@tonic-gate 			    EHCI_SITD_XFER_TOTAL_MASK;
7807c478bd9Sstevel@tonic-gate 		} else {
7817c478bd9Sstevel@tonic-gate 			/* Set the size to the packet length */
7827c478bd9Sstevel@tonic-gate 			xfer_state |= (isoc_pkt_length <<
7837c478bd9Sstevel@tonic-gate 			    EHCI_SITD_XFER_TOTAL_SHIFT) &
7847c478bd9Sstevel@tonic-gate 			    EHCI_SITD_XFER_TOTAL_MASK;
7857c478bd9Sstevel@tonic-gate 		}
7867c478bd9Sstevel@tonic-gate 		xfer_state |=  EHCI_SITD_XFER_ACTIVE;
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 		/* Set IOC on the last TD. */
7897c478bd9Sstevel@tonic-gate 		if (count == (itw->itw_num_itds - 1)) {
7907c478bd9Sstevel@tonic-gate 			xfer_state |= EHCI_SITD_XFER_IOC_ON;
7917c478bd9Sstevel@tonic-gate 		}
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate 		ssplit_count = isoc_pkt_length / MAX_UFRAME_SITD_XFER;
7947c478bd9Sstevel@tonic-gate 		if (isoc_pkt_length % MAX_UFRAME_SITD_XFER) {
7957c478bd9Sstevel@tonic-gate 			ssplit_count++;
7967c478bd9Sstevel@tonic-gate 		}
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 		page1 = (ssplit_count & EHCI_SITD_XFER_TCOUNT_MASK) <<
7997c478bd9Sstevel@tonic-gate 		    EHCI_SITD_XFER_TCOUNT_SHIFT;
8007c478bd9Sstevel@tonic-gate 		if (ssplit_count > 1) {
8017c478bd9Sstevel@tonic-gate 			page1 |= EHCI_SITD_XFER_TP_BEGIN;
8027c478bd9Sstevel@tonic-gate 		} else {
8037c478bd9Sstevel@tonic-gate 			page1 |= EHCI_SITD_XFER_TP_ALL;
8047c478bd9Sstevel@tonic-gate 		}
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate 		/* Grab a new sitd */
8077c478bd9Sstevel@tonic-gate 		new_sitd = itw->itw_itd_free_list;
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate 		ASSERT(new_sitd != NULL);
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 		itw->itw_itd_free_list = ehci_itd_iommu_to_cpu(ehcip,
8127c478bd9Sstevel@tonic-gate 		    Get_ITD(new_sitd->itd_link_ptr));
8137c478bd9Sstevel@tonic-gate 		Set_ITD(new_sitd->itd_link_ptr, NULL);
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate 		/* Fill in the new sitd */
8167c478bd9Sstevel@tonic-gate 		Set_ITD_BODY(new_sitd, EHCI_SITD_CTRL, ctrl);
8177c478bd9Sstevel@tonic-gate 		Set_ITD_BODY(new_sitd, EHCI_SITD_UFRAME_SCHED, uframe_sched);
8187c478bd9Sstevel@tonic-gate 		Set_ITD_BODY(new_sitd, EHCI_SITD_XFER_STATE, xfer_state);
8197c478bd9Sstevel@tonic-gate 		Set_ITD_BODY(new_sitd, EHCI_SITD_BUFFER0,
8207c478bd9Sstevel@tonic-gate 		    page0 + curr_isoc_xfer_offset);
8217c478bd9Sstevel@tonic-gate 		Set_ITD_BODY(new_sitd, EHCI_SITD_BUFFER1, page1);
8227c478bd9Sstevel@tonic-gate 		Set_ITD_BODY(new_sitd, EHCI_SITD_PREV_SITD, prev_sitd);
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 		Set_ITD(new_sitd->itd_state, EHCI_ITD_ACTIVE);
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 		/*
8277c478bd9Sstevel@tonic-gate 		 * Add this itd to the itw before we add it in the PFL
8287c478bd9Sstevel@tonic-gate 		 * If adding it to the PFL fails, we will have to cleanup.
8297c478bd9Sstevel@tonic-gate 		 */
8307c478bd9Sstevel@tonic-gate 		ehci_insert_itd_on_itw(ehcip, itw, new_sitd);
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 		itw->itw_curr_isoc_pktp++;
8337c478bd9Sstevel@tonic-gate 		curr_isoc_xfer_offset += isoc_pkt_length;
8347c478bd9Sstevel@tonic-gate 	}
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
8377c478bd9Sstevel@tonic-gate }
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate 
8407c478bd9Sstevel@tonic-gate /*
8417c478bd9Sstevel@tonic-gate  * ehci_remove_isoc_itds:
8427c478bd9Sstevel@tonic-gate  *
8437c478bd9Sstevel@tonic-gate  * Remove all itds from the PFL.
8447c478bd9Sstevel@tonic-gate  */
8457c478bd9Sstevel@tonic-gate static void
8467c478bd9Sstevel@tonic-gate ehci_remove_isoc_itds(
8477c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
8487c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
8497c478bd9Sstevel@tonic-gate {
8507c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*curr_itw, *next_itw;
8517c478bd9Sstevel@tonic-gate 	ehci_itd_t		*curr_itd, *next_itd;
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
854112116d8Sfb 	    "ehci_remove_isoc_itds: pp = 0x%p", (void *)pp);
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	curr_itw = pp->pp_itw_head;
8597c478bd9Sstevel@tonic-gate 	while (curr_itw) {
8607c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
8617c478bd9Sstevel@tonic-gate 		    "ehci_remove_isoc_itds: itw = 0x%p num itds = %d",
862112116d8Sfb 		    (void *)curr_itw, curr_itw->itw_num_itds);
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 		next_itw = curr_itw->itw_next;
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 		curr_itd = curr_itw->itw_itd_head;
8677c478bd9Sstevel@tonic-gate 		while (curr_itd) {
8687c478bd9Sstevel@tonic-gate 			next_itd = ehci_itd_iommu_to_cpu(ehcip,
8697c478bd9Sstevel@tonic-gate 			    Get_ITD(curr_itd->itd_itw_next_itd));
8707c478bd9Sstevel@tonic-gate 
8717c478bd9Sstevel@tonic-gate 			ehci_reclaim_isoc(ehcip, curr_itw, curr_itd, pp);
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 			curr_itd = next_itd;
8747c478bd9Sstevel@tonic-gate 		}
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 		ehci_deallocate_itw(ehcip, pp, curr_itw);
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 		curr_itw = next_itw;
8797c478bd9Sstevel@tonic-gate 	}
8807c478bd9Sstevel@tonic-gate }
8817c478bd9Sstevel@tonic-gate 
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate /*
8847c478bd9Sstevel@tonic-gate  * ehci_mark_reclaim_isoc:
8857c478bd9Sstevel@tonic-gate  *
8867c478bd9Sstevel@tonic-gate  * Set active ITDs to RECLAIM.
8877c478bd9Sstevel@tonic-gate  * Return number of ITD that need to be processed.
8887c478bd9Sstevel@tonic-gate  */
8897c478bd9Sstevel@tonic-gate static void
8907c478bd9Sstevel@tonic-gate ehci_mark_reclaim_isoc(
8917c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
8927c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
8937c478bd9Sstevel@tonic-gate {
8947c478bd9Sstevel@tonic-gate 	usb_frame_number_t	current_frame_number;
8957c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*curr_itw, *next_itw;
8967c478bd9Sstevel@tonic-gate 	ehci_itd_t		*curr_itd, *next_itd;
8977c478bd9Sstevel@tonic-gate 	uint_t			ctrl;
8987c478bd9Sstevel@tonic-gate 	uint_t			isActive;
899b3001defSlg 	int			i;
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
902112116d8Sfb 	    "ehci_mark_reclaim_isoc: pp = 0x%p", (void *)pp);
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate 	if (pp->pp_itw_head == NULL) {
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 		return;
9077c478bd9Sstevel@tonic-gate 	}
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 	/* Get the current frame number. */
9107c478bd9Sstevel@tonic-gate 	current_frame_number = ehci_get_current_frame_number(ehcip);
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 	/* Traverse the list of transfer descriptors */
9137c478bd9Sstevel@tonic-gate 	curr_itw = pp->pp_itw_head;
9147c478bd9Sstevel@tonic-gate 	while (curr_itw) {
9157c478bd9Sstevel@tonic-gate 		next_itw = curr_itw->itw_next;
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
9187c478bd9Sstevel@tonic-gate 		    "ehci_mark_reclaim_isoc: itw = 0x%p num itds = %d",
919112116d8Sfb 		    (void *)curr_itw, curr_itw->itw_num_itds);
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 		curr_itd = curr_itw->itw_itd_head;
9227c478bd9Sstevel@tonic-gate 		while (curr_itd) {
9237c478bd9Sstevel@tonic-gate 			next_itd = ehci_itd_iommu_to_cpu(ehcip,
9247c478bd9Sstevel@tonic-gate 			    Get_ITD(curr_itd->itd_itw_next_itd));
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 			if (curr_itw->itw_port_status == USBA_HIGH_SPEED_DEV) {
9277c478bd9Sstevel@tonic-gate 
928b3001defSlg 				for (i = 0; i < EHCI_ITD_CTRL_LIST_SIZE; i++) {
929b3001defSlg 					ctrl = Get_ITD_BODY(curr_itd,
930b3001defSlg 					    EHCI_ITD_CTRL0 + i);
931b3001defSlg 					isActive = ctrl & EHCI_ITD_XFER_ACTIVE;
932b3001defSlg 					/* If still active, deactivate it */
933b3001defSlg 					if (isActive) {
934b3001defSlg 						ctrl &= ~EHCI_ITD_XFER_ACTIVE;
935b3001defSlg 						Set_ITD_BODY(curr_itd,
936b3001defSlg 						    EHCI_ITD_CTRL0 + i,
937b3001defSlg 						    ctrl);
938b3001defSlg 						break;
939b3001defSlg 					}
940b3001defSlg 				}
9417c478bd9Sstevel@tonic-gate 			} else {
9427c478bd9Sstevel@tonic-gate 				ctrl = Get_ITD_BODY(curr_itd,
9437c478bd9Sstevel@tonic-gate 				    EHCI_SITD_XFER_STATE);
9447c478bd9Sstevel@tonic-gate 				isActive = ctrl & EHCI_SITD_XFER_ACTIVE;
945b3001defSlg 				/* If it is still active deactivate it */
9467c478bd9Sstevel@tonic-gate 				if (isActive) {
9477c478bd9Sstevel@tonic-gate 					ctrl &= ~EHCI_SITD_XFER_ACTIVE;
9487c478bd9Sstevel@tonic-gate 					Set_ITD_BODY(curr_itd,
9497c478bd9Sstevel@tonic-gate 					    EHCI_SITD_XFER_STATE,
9507c478bd9Sstevel@tonic-gate 					    ctrl);
9517c478bd9Sstevel@tonic-gate 				}
9527c478bd9Sstevel@tonic-gate 			}
9537c478bd9Sstevel@tonic-gate 
9547c478bd9Sstevel@tonic-gate 			/*
9557c478bd9Sstevel@tonic-gate 			 * If the itd was active put it on the reclaim status,
9567c478bd9Sstevel@tonic-gate 			 * so the interrupt handler will know not to process it.
9577c478bd9Sstevel@tonic-gate 			 * Otherwise leave it alone and let the interrupt
9587c478bd9Sstevel@tonic-gate 			 * handler process it normally.
9597c478bd9Sstevel@tonic-gate 			 */
9607c478bd9Sstevel@tonic-gate 			if (isActive) {
9617c478bd9Sstevel@tonic-gate 				Set_ITD(curr_itd->itd_state, EHCI_ITD_RECLAIM);
9627c478bd9Sstevel@tonic-gate 				Set_ITD_FRAME(curr_itd->itd_reclaim_number,
9637c478bd9Sstevel@tonic-gate 				    current_frame_number);
9647c478bd9Sstevel@tonic-gate 				ehci_remove_isoc_from_pfl(ehcip, curr_itd);
9657c478bd9Sstevel@tonic-gate 			}
9667c478bd9Sstevel@tonic-gate 			curr_itd = next_itd;
9677c478bd9Sstevel@tonic-gate 		}
9687c478bd9Sstevel@tonic-gate 		curr_itw = next_itw;
9697c478bd9Sstevel@tonic-gate 	}
9707c478bd9Sstevel@tonic-gate }
9717c478bd9Sstevel@tonic-gate 
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate /*
9747c478bd9Sstevel@tonic-gate  * ehci_reclaim_isoc:
9757c478bd9Sstevel@tonic-gate  *
9767c478bd9Sstevel@tonic-gate  * "Reclaim" itds that were marked as RECLAIM.
9777c478bd9Sstevel@tonic-gate  */
9787c478bd9Sstevel@tonic-gate static void
9797c478bd9Sstevel@tonic-gate ehci_reclaim_isoc(
9807c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
9817c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
9827c478bd9Sstevel@tonic-gate 	ehci_itd_t		*itd,
9837c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
9847c478bd9Sstevel@tonic-gate {
9857c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
986112116d8Sfb 	    "ehci_reclaim_isoc: itd = 0x%p", (void *)itd);
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	/*
9897c478bd9Sstevel@tonic-gate 	 * These are itds that were marked "RECLAIM"
9907c478bd9Sstevel@tonic-gate 	 * by the pipe cleanup.
9917c478bd9Sstevel@tonic-gate 	 *
9927c478bd9Sstevel@tonic-gate 	 * Decrement the num_itds and the periodic in
9937c478bd9Sstevel@tonic-gate 	 * request count if necessary.
9947c478bd9Sstevel@tonic-gate 	 */
9957c478bd9Sstevel@tonic-gate 	if ((--itw->itw_num_itds == 0) && (itw->itw_curr_xfer_reqp)) {
9967c478bd9Sstevel@tonic-gate 		if (itw->itw_direction == USB_EP_DIR_IN) {
9977c478bd9Sstevel@tonic-gate 
9987c478bd9Sstevel@tonic-gate 			pp->pp_cur_periodic_req_cnt--;
9997c478bd9Sstevel@tonic-gate 
10007c478bd9Sstevel@tonic-gate 			ehci_deallocate_isoc_in_resource(ehcip, pp, itw);
10017c478bd9Sstevel@tonic-gate 		} else {
10027c478bd9Sstevel@tonic-gate 			ehci_hcdi_isoc_callback(pp->pp_pipe_handle, itw,
10037c478bd9Sstevel@tonic-gate 			    USB_CR_FLUSHED);
10047c478bd9Sstevel@tonic-gate 		}
10057c478bd9Sstevel@tonic-gate 	}
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate 	/* Deallocate this transfer descriptor */
10087c478bd9Sstevel@tonic-gate 	ehci_deallocate_itd(ehcip, itw, itd);
10097c478bd9Sstevel@tonic-gate }
10107c478bd9Sstevel@tonic-gate 
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate /*
10137c478bd9Sstevel@tonic-gate  * ehci_start_isoc_polling:
10147c478bd9Sstevel@tonic-gate  *
10157c478bd9Sstevel@tonic-gate  * Insert the number of periodic requests corresponding to polling
10167c478bd9Sstevel@tonic-gate  * interval as calculated during pipe open.
10177c478bd9Sstevel@tonic-gate  */
10187c478bd9Sstevel@tonic-gate int
10197c478bd9Sstevel@tonic-gate ehci_start_isoc_polling(
10207c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
10217c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
10227c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
10237c478bd9Sstevel@tonic-gate {
10247c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
10257c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw_list, *itw;
10267c478bd9Sstevel@tonic-gate 	int			i, total_itws;
10277c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
10287c478bd9Sstevel@tonic-gate 
1029b3001defSlg 	USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
10300a05e705Slc 	    "ehci_start_isoc_polling:");
1031b3001defSlg 
10327c478bd9Sstevel@tonic-gate 	/* Allocate all the necessary resources for the IN transfer */
10337c478bd9Sstevel@tonic-gate 	itw_list = NULL;
10347c478bd9Sstevel@tonic-gate 	total_itws = pp->pp_max_periodic_req_cnt - pp->pp_cur_periodic_req_cnt;
10357c478bd9Sstevel@tonic-gate 	for (i = 0; i < total_itws; i += 1) {
10367c478bd9Sstevel@tonic-gate 		itw = ehci_allocate_isoc_resources(ehcip, ph, NULL, flags);
10377c478bd9Sstevel@tonic-gate 		if (itw == NULL) {
10387c478bd9Sstevel@tonic-gate 			error = USB_NO_RESOURCES;
10397c478bd9Sstevel@tonic-gate 			/* There are not enough resources deallocate the ITWs */
10407c478bd9Sstevel@tonic-gate 			itw = itw_list;
10417c478bd9Sstevel@tonic-gate 			while (itw != NULL) {
10427c478bd9Sstevel@tonic-gate 				itw_list = itw->itw_next;
10437c478bd9Sstevel@tonic-gate 				ehci_deallocate_isoc_in_resource(
10440a05e705Slc 				    ehcip, pp, itw);
10457c478bd9Sstevel@tonic-gate 				ehci_deallocate_itw(ehcip, pp, itw);
10467c478bd9Sstevel@tonic-gate 				itw = itw_list;
10477c478bd9Sstevel@tonic-gate 			}
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 			return (error);
10507c478bd9Sstevel@tonic-gate 		} else {
10517c478bd9Sstevel@tonic-gate 			if (itw_list == NULL) {
10527c478bd9Sstevel@tonic-gate 				itw_list = itw;
10537c478bd9Sstevel@tonic-gate 			}
10547c478bd9Sstevel@tonic-gate 		}
10557c478bd9Sstevel@tonic-gate 	}
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate 	i = 0;
10587c478bd9Sstevel@tonic-gate 	while (pp->pp_cur_periodic_req_cnt < pp->pp_max_periodic_req_cnt) {
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
10617c478bd9Sstevel@tonic-gate 		    "ehci_start_isoc_polling: max = %d curr = %d itw = %p:",
10627c478bd9Sstevel@tonic-gate 		    pp->pp_max_periodic_req_cnt, pp->pp_cur_periodic_req_cnt,
1063112116d8Sfb 		    (void *)itw_list);
10647c478bd9Sstevel@tonic-gate 
10657c478bd9Sstevel@tonic-gate 		itw = itw_list;
10667c478bd9Sstevel@tonic-gate 		itw_list = itw->itw_next;
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate 		error = ehci_insert_isoc_req(ehcip, pp, itw, flags);
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 		if (error == USB_SUCCESS) {
10717c478bd9Sstevel@tonic-gate 			pp->pp_cur_periodic_req_cnt++;
10727c478bd9Sstevel@tonic-gate 		} else {
10737c478bd9Sstevel@tonic-gate 			/*
10747c478bd9Sstevel@tonic-gate 			 * Deallocate the remaining tw
10757c478bd9Sstevel@tonic-gate 			 * The current tw should have already been deallocated
10767c478bd9Sstevel@tonic-gate 			 */
10777c478bd9Sstevel@tonic-gate 			itw = itw_list;
10787c478bd9Sstevel@tonic-gate 			while (itw != NULL) {
10797c478bd9Sstevel@tonic-gate 				itw_list = itw->itw_next;
10807c478bd9Sstevel@tonic-gate 				ehci_deallocate_isoc_in_resource(
10810a05e705Slc 				    ehcip, pp, itw);
10827c478bd9Sstevel@tonic-gate 				ehci_deallocate_itw(ehcip, pp, itw);
10837c478bd9Sstevel@tonic-gate 				itw = itw_list;
10847c478bd9Sstevel@tonic-gate 			}
10857c478bd9Sstevel@tonic-gate 			/*
10867c478bd9Sstevel@tonic-gate 			 * If this is the first req return an error.
10877c478bd9Sstevel@tonic-gate 			 * Otherwise return success.
10887c478bd9Sstevel@tonic-gate 			 */
10897c478bd9Sstevel@tonic-gate 			if (i != 0) {
10907c478bd9Sstevel@tonic-gate 				error = USB_SUCCESS;
10917c478bd9Sstevel@tonic-gate 			}
10927c478bd9Sstevel@tonic-gate 
10937c478bd9Sstevel@tonic-gate 			break;
10947c478bd9Sstevel@tonic-gate 		}
10957c478bd9Sstevel@tonic-gate 		i++;
10967c478bd9Sstevel@tonic-gate 	}
10977c478bd9Sstevel@tonic-gate 
10987c478bd9Sstevel@tonic-gate 	return (error);
10997c478bd9Sstevel@tonic-gate }
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate 
11027c478bd9Sstevel@tonic-gate /*
11037c478bd9Sstevel@tonic-gate  * Isochronronous handling functions.
11047c478bd9Sstevel@tonic-gate  */
11057c478bd9Sstevel@tonic-gate /*
11067c478bd9Sstevel@tonic-gate  * ehci_traverse_active_isoc_list:
11077c478bd9Sstevel@tonic-gate  */
11087c478bd9Sstevel@tonic-gate void
11097c478bd9Sstevel@tonic-gate ehci_traverse_active_isoc_list(
11107c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip)
11117c478bd9Sstevel@tonic-gate {
11127c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*curr_itw;
11137c478bd9Sstevel@tonic-gate 	ehci_itd_t		*curr_itd, *next_itd;
11147c478bd9Sstevel@tonic-gate 	uint_t			state;
11157c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp;
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
11187c478bd9Sstevel@tonic-gate 	    "ehci_traverse_active_isoc_list:");
11197c478bd9Sstevel@tonic-gate 
11207c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
11217c478bd9Sstevel@tonic-gate 
11227c478bd9Sstevel@tonic-gate 	/* Sync ITD pool */
11237c478bd9Sstevel@tonic-gate 	Sync_ITD_Pool(ehcip);
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 	/* Traverse the list of done itds */
11267c478bd9Sstevel@tonic-gate 	curr_itd = ehci_create_done_itd_list(ehcip);
1127b3001defSlg 	USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
1128112116d8Sfb 	    "ehci_traverse_active_isoc_list: current itd = 0x%p",
1129112116d8Sfb 	    (void *)curr_itd);
11307c478bd9Sstevel@tonic-gate 
11317c478bd9Sstevel@tonic-gate 	while (curr_itd) {
11327c478bd9Sstevel@tonic-gate 		/* Save the next_itd */
11337c478bd9Sstevel@tonic-gate 		next_itd = ehci_itd_iommu_to_cpu(ehcip,
11347c478bd9Sstevel@tonic-gate 		    Get_ITD(curr_itd->itd_next_active_itd));
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate 		/* Get the transfer wrapper and the pp */
11377c478bd9Sstevel@tonic-gate 		curr_itw = (ehci_isoc_xwrapper_t *)EHCI_LOOKUP_ID(
11380a05e705Slc 		    (uint32_t)Get_ITD(curr_itd->itd_trans_wrapper));
11397c478bd9Sstevel@tonic-gate 		pp = curr_itw->itw_pipe_private;
11407c478bd9Sstevel@tonic-gate 
1141b3001defSlg 		if (curr_itw->itw_port_status == USBA_HIGH_SPEED_DEV) {
1142b3001defSlg 			ehci_print_itd(ehcip, curr_itd);
1143b3001defSlg 		} else {
1144b3001defSlg 			ehci_print_sitd(ehcip, curr_itd);
1145b3001defSlg 		}
11467c478bd9Sstevel@tonic-gate 
11477c478bd9Sstevel@tonic-gate 		/* Get the ITD state */
11487c478bd9Sstevel@tonic-gate 		state = Get_ITD(curr_itd->itd_state);
11497c478bd9Sstevel@tonic-gate 
11507c478bd9Sstevel@tonic-gate 		/* Only process the ITDs marked as active. */
11517c478bd9Sstevel@tonic-gate 		if (state == EHCI_ITD_ACTIVE) {
11527c478bd9Sstevel@tonic-gate 			ehci_parse_isoc_error(ehcip, curr_itw, curr_itd);
11537c478bd9Sstevel@tonic-gate 			ehci_handle_isoc(ehcip, curr_itw, curr_itd);
11547c478bd9Sstevel@tonic-gate 		} else {
11557c478bd9Sstevel@tonic-gate 			ASSERT(state == EHCI_ITD_RECLAIM);
11567c478bd9Sstevel@tonic-gate 			ehci_reclaim_isoc(ehcip, curr_itw, curr_itd, pp);
11577c478bd9Sstevel@tonic-gate 		}
11587c478bd9Sstevel@tonic-gate 
11597c478bd9Sstevel@tonic-gate 		/*
11607c478bd9Sstevel@tonic-gate 		 * Deallocate the transfer wrapper if there are no more
11617c478bd9Sstevel@tonic-gate 		 * ITD's for the transfer wrapper.  ehci_deallocate_itw()
11627c478bd9Sstevel@tonic-gate 		 * will  not deallocate the tw for a periodic in endpoint
11637c478bd9Sstevel@tonic-gate 		 * since it will always have a ITD attached to it.
11647c478bd9Sstevel@tonic-gate 		 */
11657c478bd9Sstevel@tonic-gate 		ehci_deallocate_itw(ehcip, pp, curr_itw);
11667c478bd9Sstevel@tonic-gate 
11677c478bd9Sstevel@tonic-gate 		/* Check any ISOC is waiting for transfers completion event */
11687c478bd9Sstevel@tonic-gate 		if (pp->pp_itw_head == NULL) {
11697c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
11707c478bd9Sstevel@tonic-gate 			    "ehci_traverse_active_isoc_list: "
1171112116d8Sfb 			    "Sent transfers completion event pp = 0x%p",
1172112116d8Sfb 			    (void *)pp);
11737c478bd9Sstevel@tonic-gate 			cv_signal(&pp->pp_xfer_cmpl_cv);
11747c478bd9Sstevel@tonic-gate 		}
11757c478bd9Sstevel@tonic-gate 
11767c478bd9Sstevel@tonic-gate 		curr_itd = next_itd;
1177b3001defSlg 
1178b3001defSlg 		USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
1179b3001defSlg 		    "ehci_traverse_active_isoc_list: state = 0x%x "
1180b3001defSlg 		    "pp = 0x%p itw = 0x%p itd = 0x%p next_itd = 0x%p",
1181112116d8Sfb 		    state, (void *)pp, (void *)curr_itw, (void *)curr_itd,
1182112116d8Sfb 		    (void *)next_itd);
11837c478bd9Sstevel@tonic-gate 	}
11847c478bd9Sstevel@tonic-gate }
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 
11877c478bd9Sstevel@tonic-gate static void
11887c478bd9Sstevel@tonic-gate ehci_handle_isoc(
11897c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
11907c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
11917c478bd9Sstevel@tonic-gate 	ehci_itd_t		*itd)
11927c478bd9Sstevel@tonic-gate {
11937c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp;	/* Pipe private field */
11947c478bd9Sstevel@tonic-gate 
11957c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
11967c478bd9Sstevel@tonic-gate 
11977c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
11987c478bd9Sstevel@tonic-gate 	    "ehci_handle_isoc:");
11997c478bd9Sstevel@tonic-gate 
12007c478bd9Sstevel@tonic-gate 	/* Obtain the pipe private structure */
12017c478bd9Sstevel@tonic-gate 	pp = itw->itw_pipe_private;
12027c478bd9Sstevel@tonic-gate 
1203b3001defSlg 	ehci_handle_itd(ehcip, pp, itw, itd, itw->itw_handle_callback_value);
12047c478bd9Sstevel@tonic-gate }
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 
12077c478bd9Sstevel@tonic-gate /*
12087c478bd9Sstevel@tonic-gate  * ehci_handle_itd:
12097c478bd9Sstevel@tonic-gate  *
1210b3001defSlg  * Handle an (split) isochronous transfer descriptor.
12117c478bd9Sstevel@tonic-gate  * This function will deallocate the itd from the list as well.
12127c478bd9Sstevel@tonic-gate  */
12137c478bd9Sstevel@tonic-gate /* ARGSUSED */
12147c478bd9Sstevel@tonic-gate static void
1215b3001defSlg ehci_handle_itd(
12167c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
12177c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
12187c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
12197c478bd9Sstevel@tonic-gate 	ehci_itd_t		*itd,
12207c478bd9Sstevel@tonic-gate 	void			*tw_handle_callback_value)
12217c478bd9Sstevel@tonic-gate {
12227c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
12237c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*curr_isoc_reqp =
12240a05e705Slc 	    (usb_isoc_req_t *)itw->itw_curr_xfer_reqp;
12257c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
1226b3001defSlg 	int			i, index;
12277c478bd9Sstevel@tonic-gate 
12287c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
1229b3001defSlg 	    "ehci_handle_itd: pp=0x%p itw=0x%p itd=0x%p "
1230112116d8Sfb 	    "isoc_reqp=0%p data=0x%p", (void *)pp, (void *)itw, (void *)itd,
1231112116d8Sfb 	    (void *)curr_isoc_reqp, (void *)curr_isoc_reqp->isoc_data);
12327c478bd9Sstevel@tonic-gate 
1233b3001defSlg 	if (itw->itw_port_status == USBA_HIGH_SPEED_DEV &&
1234b3001defSlg 	    curr_isoc_reqp != NULL) {
1235b3001defSlg 
12360a05e705Slc 		for (i = 0; i < EHCI_ITD_CTRL_LIST_SIZE; i++) {
1237b3001defSlg 
12380a05e705Slc 			index = Get_ITD_INDEX(itd, i);
12390a05e705Slc 			if (index == EHCI_ITD_UNUSED_INDEX) {
1240b3001defSlg 
12410a05e705Slc 				continue;
12420a05e705Slc 			}
12430a05e705Slc 			curr_isoc_reqp->
12440a05e705Slc 			    isoc_pkt_descr[index].isoc_pkt_actual_length =
12450a05e705Slc 			    (Get_ITD_BODY(itd, i) & EHCI_ITD_XFER_LENGTH) >> 16;
1246b3001defSlg 		}
1247b3001defSlg 	}
1248b3001defSlg 
12497c478bd9Sstevel@tonic-gate 	/*
12507c478bd9Sstevel@tonic-gate 	 * Decrement the ITDs counter and check whether all the isoc
12517c478bd9Sstevel@tonic-gate 	 * data has been send or received. If ITDs counter reaches
12527c478bd9Sstevel@tonic-gate 	 * zero then inform client driver about completion current
12537c478bd9Sstevel@tonic-gate 	 * isoc request. Otherwise wait for completion of other isoc
12547c478bd9Sstevel@tonic-gate 	 * ITDs or transactions on this pipe.
12557c478bd9Sstevel@tonic-gate 	 */
12567c478bd9Sstevel@tonic-gate 	if (--itw->itw_num_itds != 0) {
12577c478bd9Sstevel@tonic-gate 		/* Deallocate this transfer descriptor */
12587c478bd9Sstevel@tonic-gate 		ehci_deallocate_itd(ehcip, itw, itd);
12597c478bd9Sstevel@tonic-gate 
12607c478bd9Sstevel@tonic-gate 		return;
12617c478bd9Sstevel@tonic-gate 	}
12627c478bd9Sstevel@tonic-gate 
12637c478bd9Sstevel@tonic-gate 	/*
12647c478bd9Sstevel@tonic-gate 	 * If this is a isoc in pipe, return the data to the client.
12657c478bd9Sstevel@tonic-gate 	 * For a isoc out pipe, there is no need to do anything.
12667c478bd9Sstevel@tonic-gate 	 */
12677c478bd9Sstevel@tonic-gate 	if (itw->itw_direction == USB_EP_DIR_OUT) {
12687c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
1269112116d8Sfb 		    "ehci_handle_itd: Isoc out pipe, isoc_reqp=0x%p, data=0x%p",
1270112116d8Sfb 		    (void *)curr_isoc_reqp, (void *)curr_isoc_reqp->isoc_data);
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate 		/* Do the callback */
12737c478bd9Sstevel@tonic-gate 		ehci_hcdi_isoc_callback(ph, itw, USB_CR_OK);
12747c478bd9Sstevel@tonic-gate 
12757c478bd9Sstevel@tonic-gate 		/* Deallocate this transfer descriptor */
12767c478bd9Sstevel@tonic-gate 		ehci_deallocate_itd(ehcip, itw, itd);
12777c478bd9Sstevel@tonic-gate 
12787c478bd9Sstevel@tonic-gate 		return;
12797c478bd9Sstevel@tonic-gate 	}
12807c478bd9Sstevel@tonic-gate 
12817c478bd9Sstevel@tonic-gate 	/* Decrement number of IN isochronous request count */
12827c478bd9Sstevel@tonic-gate 	pp->pp_cur_periodic_req_cnt--;
12837c478bd9Sstevel@tonic-gate 
1284b3001defSlg 	USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
1285b3001defSlg 	    "ehci_handle_itd: pp_cur_periodic_req_cnt = 0x%x ",
1286b3001defSlg 	    pp->pp_cur_periodic_req_cnt);
1287b3001defSlg 
12887c478bd9Sstevel@tonic-gate 	/* Call ehci_sendup_itd_message to send message to upstream */
12897c478bd9Sstevel@tonic-gate 	ehci_sendup_itd_message(ehcip, pp, itw, itd, USB_CR_OK);
12907c478bd9Sstevel@tonic-gate 
12917c478bd9Sstevel@tonic-gate 	/* Deallocate this transfer descriptor */
12927c478bd9Sstevel@tonic-gate 	ehci_deallocate_itd(ehcip, itw, itd);
12937c478bd9Sstevel@tonic-gate 
12947c478bd9Sstevel@tonic-gate 	/*
12957c478bd9Sstevel@tonic-gate 	 * If isochronous pipe state is still active, insert next isochronous
12967c478bd9Sstevel@tonic-gate 	 * request into the Host Controller's isochronous list.
12977c478bd9Sstevel@tonic-gate 	 */
12987c478bd9Sstevel@tonic-gate 	if (pp->pp_state != EHCI_PIPE_STATE_ACTIVE) {
12997c478bd9Sstevel@tonic-gate 
13007c478bd9Sstevel@tonic-gate 		return;
13017c478bd9Sstevel@tonic-gate 	}
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate 	if ((error = ehci_allocate_isoc_in_resource(ehcip, pp, itw, 0)) ==
13047c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
13057c478bd9Sstevel@tonic-gate 		curr_isoc_reqp = (usb_isoc_req_t *)itw->itw_curr_xfer_reqp;
13067c478bd9Sstevel@tonic-gate 
13077c478bd9Sstevel@tonic-gate 		ASSERT(curr_isoc_reqp != NULL);
13087c478bd9Sstevel@tonic-gate 
13097c478bd9Sstevel@tonic-gate 		itw->itw_num_itds = ehci_calc_num_itds(itw,
13107c478bd9Sstevel@tonic-gate 		    curr_isoc_reqp->isoc_pkts_count);
13117c478bd9Sstevel@tonic-gate 
13127c478bd9Sstevel@tonic-gate 		if (ehci_allocate_itds_for_itw(ehcip, itw, itw->itw_num_itds) !=
13137c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
13147c478bd9Sstevel@tonic-gate 			ehci_deallocate_isoc_in_resource(ehcip, pp, itw);
13157c478bd9Sstevel@tonic-gate 			itw->itw_num_itds = 0;
13167c478bd9Sstevel@tonic-gate 			error = USB_FAILURE;
13177c478bd9Sstevel@tonic-gate 		}
13187c478bd9Sstevel@tonic-gate 	}
13197c478bd9Sstevel@tonic-gate 
13207c478bd9Sstevel@tonic-gate 	if ((error != USB_SUCCESS) ||
13217c478bd9Sstevel@tonic-gate 	    (ehci_insert_isoc_req(ehcip, pp, itw, 0) != USB_SUCCESS)) {
13227c478bd9Sstevel@tonic-gate 		/*
13237c478bd9Sstevel@tonic-gate 		 * Set pipe state to stop polling and error to no
13247c478bd9Sstevel@tonic-gate 		 * resource. Don't insert any more isoch polling
13257c478bd9Sstevel@tonic-gate 		 * requests.
13267c478bd9Sstevel@tonic-gate 		 */
13277c478bd9Sstevel@tonic-gate 		pp->pp_state = EHCI_PIPE_STATE_STOP_POLLING;
13287c478bd9Sstevel@tonic-gate 		pp->pp_error = USB_CR_NO_RESOURCES;
13297c478bd9Sstevel@tonic-gate 
13307c478bd9Sstevel@tonic-gate 	} else {
13317c478bd9Sstevel@tonic-gate 		/* Increment number of IN isochronous request count */
13327c478bd9Sstevel@tonic-gate 		pp->pp_cur_periodic_req_cnt++;
13337c478bd9Sstevel@tonic-gate 
13347c478bd9Sstevel@tonic-gate 		ASSERT(pp->pp_cur_periodic_req_cnt ==
13357c478bd9Sstevel@tonic-gate 		    pp->pp_max_periodic_req_cnt);
13367c478bd9Sstevel@tonic-gate 	}
13377c478bd9Sstevel@tonic-gate }
13387c478bd9Sstevel@tonic-gate 
13397c478bd9Sstevel@tonic-gate 
13407c478bd9Sstevel@tonic-gate /*
13417c478bd9Sstevel@tonic-gate  * ehci_sendup_qtd_message:
13427c478bd9Sstevel@tonic-gate  *	copy data, if necessary and do callback
13437c478bd9Sstevel@tonic-gate  */
13447c478bd9Sstevel@tonic-gate /* ARGSUSED */
13457c478bd9Sstevel@tonic-gate static void
13467c478bd9Sstevel@tonic-gate ehci_sendup_itd_message(
13477c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
13487c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
13497c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
13507c478bd9Sstevel@tonic-gate 	ehci_itd_t		*td,
13517c478bd9Sstevel@tonic-gate 	usb_cr_t		error)
13527c478bd9Sstevel@tonic-gate {
13537c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_reqp = itw->itw_curr_xfer_reqp;
13547c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
13557c478bd9Sstevel@tonic-gate 	size_t			length;
13567c478bd9Sstevel@tonic-gate 	uchar_t			*buf;
13577c478bd9Sstevel@tonic-gate 	mblk_t			*mp;
13587c478bd9Sstevel@tonic-gate 
13597c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
13607c478bd9Sstevel@tonic-gate 
13617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
13627c478bd9Sstevel@tonic-gate 	    "ehci_sendup_itd_message:");
13637c478bd9Sstevel@tonic-gate 
13647c478bd9Sstevel@tonic-gate 	ASSERT(itw != NULL);
13657c478bd9Sstevel@tonic-gate 
13667c478bd9Sstevel@tonic-gate 	length = itw->itw_length;
13677c478bd9Sstevel@tonic-gate 
13687c478bd9Sstevel@tonic-gate 	/* Copy the data into the mblk_t */
13697c478bd9Sstevel@tonic-gate 	buf = (uchar_t *)itw->itw_buf;
13707c478bd9Sstevel@tonic-gate 
1371b3001defSlg 	USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
1372112116d8Sfb 	    "ehci_sendup_itd_message: length %ld error %d", length, error);
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate 	/* Get the message block */
13757c478bd9Sstevel@tonic-gate 	mp = isoc_reqp->isoc_data;
13767c478bd9Sstevel@tonic-gate 
13777c478bd9Sstevel@tonic-gate 	ASSERT(mp != NULL);
13787c478bd9Sstevel@tonic-gate 
13797c478bd9Sstevel@tonic-gate 	if (length) {
13807c478bd9Sstevel@tonic-gate 		/* Sync IO buffer */
13817c478bd9Sstevel@tonic-gate 		Sync_IO_Buffer(itw->itw_dmahandle, length);
13827c478bd9Sstevel@tonic-gate 
13837c478bd9Sstevel@tonic-gate 		/* Copy the data into the message */
1384b3001defSlg 		ddi_rep_get8(itw->itw_accesshandle,
1385b3001defSlg 		    mp->b_rptr, buf, length, DDI_DEV_AUTOINCR);
13867c478bd9Sstevel@tonic-gate 
13877c478bd9Sstevel@tonic-gate 		/* Increment the write pointer */
13887c478bd9Sstevel@tonic-gate 		mp->b_wptr = mp->b_wptr + length;
13897c478bd9Sstevel@tonic-gate 	} else {
13907c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
13917c478bd9Sstevel@tonic-gate 		    "ehci_sendup_itd_message: Zero length packet");
13927c478bd9Sstevel@tonic-gate 	}
13937c478bd9Sstevel@tonic-gate 
13947c478bd9Sstevel@tonic-gate 	ehci_hcdi_isoc_callback(ph, itw, error);
13957c478bd9Sstevel@tonic-gate }
13967c478bd9Sstevel@tonic-gate 
13977c478bd9Sstevel@tonic-gate 
13987c478bd9Sstevel@tonic-gate /*
13997c478bd9Sstevel@tonic-gate  * ehci_hcdi_isoc_callback:
14007c478bd9Sstevel@tonic-gate  *
14017c478bd9Sstevel@tonic-gate  * Convenience wrapper around usba_hcdi_cb() other than root hub.
14027c478bd9Sstevel@tonic-gate  */
14037c478bd9Sstevel@tonic-gate void
14047c478bd9Sstevel@tonic-gate ehci_hcdi_isoc_callback(
14057c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
14067c478bd9Sstevel@tonic-gate 	ehci_isoc_xwrapper_t	*itw,
14077c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason)
14087c478bd9Sstevel@tonic-gate {
14097c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
14100a05e705Slc 	    ph->p_usba_device->usb_root_hub_dip);
14117c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
14127c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_xfer_reqp;
14137c478bd9Sstevel@tonic-gate 	uint_t			pipe_state = 0;
14147c478bd9Sstevel@tonic-gate 
14157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
14167c478bd9Sstevel@tonic-gate 	    "ehci_hcdi_isoc_callback: ph = 0x%p, itw = 0x%p, cr = 0x%x",
1417112116d8Sfb 	    (void *)ph, (void *)itw, completion_reason);
14187c478bd9Sstevel@tonic-gate 
14197c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
14207c478bd9Sstevel@tonic-gate 
14217c478bd9Sstevel@tonic-gate 	/* Set the pipe state as per completion reason */
14227c478bd9Sstevel@tonic-gate 	switch (completion_reason) {
14237c478bd9Sstevel@tonic-gate 	case USB_CR_OK:
14247c478bd9Sstevel@tonic-gate 		pipe_state = pp->pp_state;
14257c478bd9Sstevel@tonic-gate 		break;
14267c478bd9Sstevel@tonic-gate 	case USB_CR_NO_RESOURCES:
14277c478bd9Sstevel@tonic-gate 	case USB_CR_NOT_SUPPORTED:
14287c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
14297c478bd9Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
14307c478bd9Sstevel@tonic-gate 		pipe_state = EHCI_PIPE_STATE_IDLE;
14317c478bd9Sstevel@tonic-gate 		break;
14327c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_CLOSING:
14337c478bd9Sstevel@tonic-gate 		break;
14347c478bd9Sstevel@tonic-gate 	}
14357c478bd9Sstevel@tonic-gate 
14367c478bd9Sstevel@tonic-gate 	pp->pp_state = pipe_state;
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 	if (itw && itw->itw_curr_xfer_reqp) {
14397c478bd9Sstevel@tonic-gate 		curr_xfer_reqp = (usb_opaque_t)itw->itw_curr_xfer_reqp;
14407c478bd9Sstevel@tonic-gate 		itw->itw_curr_xfer_reqp = NULL;
14417c478bd9Sstevel@tonic-gate 	} else {
14427c478bd9Sstevel@tonic-gate 		ASSERT(pp->pp_client_periodic_in_reqp != NULL);
14437c478bd9Sstevel@tonic-gate 
14447c478bd9Sstevel@tonic-gate 		curr_xfer_reqp = pp->pp_client_periodic_in_reqp;
14457c478bd9Sstevel@tonic-gate 		pp->pp_client_periodic_in_reqp = NULL;
14467c478bd9Sstevel@tonic-gate 	}
14477c478bd9Sstevel@tonic-gate 
14487c478bd9Sstevel@tonic-gate 	ASSERT(curr_xfer_reqp != NULL);
14497c478bd9Sstevel@tonic-gate 
14507c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
14517c478bd9Sstevel@tonic-gate 
14527c478bd9Sstevel@tonic-gate 	usba_hcdi_cb(ph, curr_xfer_reqp, completion_reason);
14537c478bd9Sstevel@tonic-gate 
14547c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
14557c478bd9Sstevel@tonic-gate }
1456