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
53304303fSsl  * Common Development and Distribution License (the "License").
63304303fSsl  * 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  */
21dfbb3a42SRaymond Chen 
227c478bd9Sstevel@tonic-gate /*
23dfbb3a42SRaymond Chen  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * EHCI Host Controller Driver (EHCI)
287c478bd9Sstevel@tonic-gate  *
297c478bd9Sstevel@tonic-gate  * The EHCI driver is a software driver which interfaces to the Universal
307c478bd9Sstevel@tonic-gate  * Serial Bus layer (USBA) and the Host Controller (HC). The interface to
317c478bd9Sstevel@tonic-gate  * the Host Controller is defined by the EHCI Host Controller Interface.
327c478bd9Sstevel@tonic-gate  *
337c478bd9Sstevel@tonic-gate  * This module contains the main EHCI driver code which handles all USB
347c478bd9Sstevel@tonic-gate  * transfers, bandwidth allocations and other general functionalities.
357c478bd9Sstevel@tonic-gate  */
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehcid.h>
387c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_intr.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 
427c478bd9Sstevel@tonic-gate /* Adjustable variables for the size of the pools */
437c478bd9Sstevel@tonic-gate extern int ehci_qh_pool_size;
447c478bd9Sstevel@tonic-gate extern int ehci_qtd_pool_size;
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate /* Endpoint Descriptor (QH) related functions */
487c478bd9Sstevel@tonic-gate ehci_qh_t	*ehci_alloc_qh(
497c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
507c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
517c478bd9Sstevel@tonic-gate 				uint_t			flag);
527c478bd9Sstevel@tonic-gate static void	ehci_unpack_endpoint(
537c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
547c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
557c478bd9Sstevel@tonic-gate 				ehci_qh_t		*qh);
567c478bd9Sstevel@tonic-gate void		ehci_insert_qh(
577c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
587c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
597c478bd9Sstevel@tonic-gate static void	ehci_insert_async_qh(
607c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
617c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
627c478bd9Sstevel@tonic-gate static void	ehci_insert_intr_qh(
637c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
647c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
657c478bd9Sstevel@tonic-gate static void	ehci_modify_qh_status_bit(
667c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
677c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
687c478bd9Sstevel@tonic-gate 				halt_bit_t		action);
697c478bd9Sstevel@tonic-gate static void	ehci_halt_hs_qh(
707c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
717c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
727c478bd9Sstevel@tonic-gate 				ehci_qh_t		*qh);
737c478bd9Sstevel@tonic-gate static void	ehci_halt_fls_ctrl_and_bulk_qh(
747c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
757c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
767c478bd9Sstevel@tonic-gate 				ehci_qh_t		*qh);
777c478bd9Sstevel@tonic-gate static void	ehci_clear_tt_buffer(
787c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
797c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
807c478bd9Sstevel@tonic-gate 				ehci_qh_t		*qh);
817c478bd9Sstevel@tonic-gate static void	ehci_halt_fls_intr_qh(
827c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
837c478bd9Sstevel@tonic-gate 				ehci_qh_t		*qh);
847c478bd9Sstevel@tonic-gate void		ehci_remove_qh(
857c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
867c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
877c478bd9Sstevel@tonic-gate 				boolean_t		reclaim);
887c478bd9Sstevel@tonic-gate static void	ehci_remove_async_qh(
897c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
907c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
917c478bd9Sstevel@tonic-gate 				boolean_t		reclaim);
927c478bd9Sstevel@tonic-gate static void	ehci_remove_intr_qh(
937c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
947c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
957c478bd9Sstevel@tonic-gate 				boolean_t		reclaim);
967c478bd9Sstevel@tonic-gate static void	ehci_insert_qh_on_reclaim_list(
977c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
987c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
997c478bd9Sstevel@tonic-gate void		ehci_deallocate_qh(
1007c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1017c478bd9Sstevel@tonic-gate 				ehci_qh_t		*old_qh);
1027c478bd9Sstevel@tonic-gate uint32_t	ehci_qh_cpu_to_iommu(
1037c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1047c478bd9Sstevel@tonic-gate 				ehci_qh_t		*addr);
1057c478bd9Sstevel@tonic-gate ehci_qh_t	*ehci_qh_iommu_to_cpu(
1067c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1077c478bd9Sstevel@tonic-gate 				uintptr_t		addr);
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate /* Transfer Descriptor (QTD) related functions */
1107c478bd9Sstevel@tonic-gate static int	ehci_initialize_dummy(
1117c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1127c478bd9Sstevel@tonic-gate 				ehci_qh_t		*qh);
1137c478bd9Sstevel@tonic-gate ehci_trans_wrapper_t *ehci_allocate_ctrl_resources(
1147c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1157c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
1167c478bd9Sstevel@tonic-gate 				usb_ctrl_req_t		*ctrl_reqp,
1177c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1187c478bd9Sstevel@tonic-gate void		ehci_insert_ctrl_req(
1197c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1207c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1217c478bd9Sstevel@tonic-gate 				usb_ctrl_req_t		*ctrl_reqp,
1227c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
1237c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1247c478bd9Sstevel@tonic-gate ehci_trans_wrapper_t *ehci_allocate_bulk_resources(
1257c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1267c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
1277c478bd9Sstevel@tonic-gate 				usb_bulk_req_t		*bulk_reqp,
1287c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1297c478bd9Sstevel@tonic-gate void		ehci_insert_bulk_req(
1307c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1317c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1327c478bd9Sstevel@tonic-gate 				usb_bulk_req_t		*bulk_reqp,
1337c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
1347c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1357c478bd9Sstevel@tonic-gate int		ehci_start_periodic_pipe_polling(
1367c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1377c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1387c478bd9Sstevel@tonic-gate 				usb_opaque_t		periodic_in_reqp,
1397c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1407c478bd9Sstevel@tonic-gate static int	ehci_start_pipe_polling(
1417c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1427c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1437c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1447c478bd9Sstevel@tonic-gate static int	ehci_start_intr_polling(
1457c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1467c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1477c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1487c478bd9Sstevel@tonic-gate static void	ehci_set_periodic_pipe_polling(
1497c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1507c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
1517c478bd9Sstevel@tonic-gate ehci_trans_wrapper_t *ehci_allocate_intr_resources(
1527c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1537c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1547c478bd9Sstevel@tonic-gate 				usb_intr_req_t		*intr_reqp,
1557c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags);
1567c478bd9Sstevel@tonic-gate void		ehci_insert_intr_req(
1577c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1587c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
1597c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
1607c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1617c478bd9Sstevel@tonic-gate int		ehci_stop_periodic_pipe_polling(
1627c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1637c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1647c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1657c478bd9Sstevel@tonic-gate int		ehci_insert_qtd(
1667c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1677c478bd9Sstevel@tonic-gate 				uint32_t		qtd_ctrl,
1683304303fSsl 				size_t			qtd_dma_offs,
1697c478bd9Sstevel@tonic-gate 				size_t			qtd_length,
1707c478bd9Sstevel@tonic-gate 				uint32_t		qtd_ctrl_phase,
1717c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
1727c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
1737c478bd9Sstevel@tonic-gate static ehci_qtd_t *ehci_allocate_qtd_from_pool(
1747c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip);
1757c478bd9Sstevel@tonic-gate static void	ehci_fill_in_qtd(
1767c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1777c478bd9Sstevel@tonic-gate 				ehci_qtd_t		*qtd,
1787c478bd9Sstevel@tonic-gate 				uint32_t		qtd_ctrl,
1793304303fSsl 				size_t			qtd_dma_offs,
1807c478bd9Sstevel@tonic-gate 				size_t			qtd_length,
1817c478bd9Sstevel@tonic-gate 				uint32_t		qtd_ctrl_phase,
1827c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
1837c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
1847c478bd9Sstevel@tonic-gate static void	ehci_insert_qtd_on_tw(
1857c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1867c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
1877c478bd9Sstevel@tonic-gate 				ehci_qtd_t		*qtd);
1887c478bd9Sstevel@tonic-gate static void	ehci_insert_qtd_into_active_qtd_list(
1897c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1907c478bd9Sstevel@tonic-gate 				ehci_qtd_t		*curr_qtd);
1917c478bd9Sstevel@tonic-gate void		ehci_remove_qtd_from_active_qtd_list(
1927c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1937c478bd9Sstevel@tonic-gate 				ehci_qtd_t		*curr_qtd);
1947c478bd9Sstevel@tonic-gate static void	ehci_traverse_qtds(
1957c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1967c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
1977c478bd9Sstevel@tonic-gate void		ehci_deallocate_qtd(
1987c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1997c478bd9Sstevel@tonic-gate 				ehci_qtd_t		*old_qtd);
2007c478bd9Sstevel@tonic-gate uint32_t	ehci_qtd_cpu_to_iommu(
2017c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2027c478bd9Sstevel@tonic-gate 				ehci_qtd_t		*addr);
2037c478bd9Sstevel@tonic-gate ehci_qtd_t	*ehci_qtd_iommu_to_cpu(
2047c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2057c478bd9Sstevel@tonic-gate 				uintptr_t		addr);
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate /* Transfer Wrapper (TW) functions */
2087c478bd9Sstevel@tonic-gate static ehci_trans_wrapper_t  *ehci_create_transfer_wrapper(
2097c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2107c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2117c478bd9Sstevel@tonic-gate 				size_t			length,
2127c478bd9Sstevel@tonic-gate 				uint_t			usb_flags);
2137c478bd9Sstevel@tonic-gate int		ehci_allocate_tds_for_tw(
2147c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2157c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2167c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
2177c478bd9Sstevel@tonic-gate 				size_t			qtd_count);
2187c478bd9Sstevel@tonic-gate static ehci_trans_wrapper_t  *ehci_allocate_tw_resources(
2197c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2207c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2217c478bd9Sstevel@tonic-gate 				size_t			length,
2227c478bd9Sstevel@tonic-gate 				usb_flags_t		usb_flags,
2237c478bd9Sstevel@tonic-gate 				size_t			td_count);
2247c478bd9Sstevel@tonic-gate static void	ehci_free_tw_td_resources(
2257c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2267c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
2277c478bd9Sstevel@tonic-gate static void	ehci_start_xfer_timer(
2287c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2297c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2307c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
2317c478bd9Sstevel@tonic-gate void		ehci_stop_xfer_timer(
2327c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2337c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
2347c478bd9Sstevel@tonic-gate 				uint_t			flag);
2357c478bd9Sstevel@tonic-gate static void	ehci_xfer_timeout_handler(void		*arg);
2367c478bd9Sstevel@tonic-gate static void	ehci_remove_tw_from_timeout_list(
2377c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2387c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
2397c478bd9Sstevel@tonic-gate static void	ehci_start_timer(ehci_state_t		*ehcip,
2407c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
2417c478bd9Sstevel@tonic-gate void		ehci_deallocate_tw(
2427c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2437c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2447c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
2457c478bd9Sstevel@tonic-gate void		ehci_free_dma_resources(
2467c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2477c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
2487c478bd9Sstevel@tonic-gate static void	ehci_free_tw(
2497c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2507c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2517c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate /* Miscellaneous functions */
2547c478bd9Sstevel@tonic-gate int		ehci_allocate_intr_in_resource(
2557c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2567c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2577c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
2587c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
2597c478bd9Sstevel@tonic-gate void		ehci_pipe_cleanup(
2607c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2617c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
2627c478bd9Sstevel@tonic-gate static void	ehci_wait_for_transfers_completion(
2637c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2647c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
2657c478bd9Sstevel@tonic-gate void		ehci_check_for_transfers_completion(
2667c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2677c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
2687c478bd9Sstevel@tonic-gate static void	ehci_save_data_toggle(
2697c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2707c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
2717c478bd9Sstevel@tonic-gate void		ehci_restore_data_toggle(
2727c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2737c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph);
2747c478bd9Sstevel@tonic-gate void		ehci_handle_outstanding_requests(
2757c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2767c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp);
2777c478bd9Sstevel@tonic-gate void		ehci_deallocate_intr_in_resource(
2787c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2797c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2807c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw);
2817c478bd9Sstevel@tonic-gate void		ehci_do_client_periodic_in_req_callback(
2827c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
2837c478bd9Sstevel@tonic-gate 				ehci_pipe_private_t	*pp,
2847c478bd9Sstevel@tonic-gate 				usb_cr_t		completion_reason);
2857c478bd9Sstevel@tonic-gate void		ehci_hcdi_callback(
2867c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
2877c478bd9Sstevel@tonic-gate 				ehci_trans_wrapper_t	*tw,
2887c478bd9Sstevel@tonic-gate 				usb_cr_t		completion_reason);
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate /*
2927c478bd9Sstevel@tonic-gate  * Endpoint Descriptor (QH) manipulations functions
2937c478bd9Sstevel@tonic-gate  */
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate /*
2967c478bd9Sstevel@tonic-gate  * ehci_alloc_qh:
2977c478bd9Sstevel@tonic-gate  *
2987c478bd9Sstevel@tonic-gate  * Allocate an endpoint descriptor (QH)
2997c478bd9Sstevel@tonic-gate  *
3007c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
3017c478bd9Sstevel@tonic-gate  */
3027c478bd9Sstevel@tonic-gate ehci_qh_t *
ehci_alloc_qh(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,uint_t flag)3037c478bd9Sstevel@tonic-gate ehci_alloc_qh(
3047c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
3057c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
3067c478bd9Sstevel@tonic-gate 	uint_t			flag)
3077c478bd9Sstevel@tonic-gate {
3087c478bd9Sstevel@tonic-gate 	int			i, state;
3097c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
3127c478bd9Sstevel@tonic-gate 	    "ehci_alloc_qh: ph = 0x%p flag = 0x%x", (void *)ph, flag);
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	/*
3177c478bd9Sstevel@tonic-gate 	 * If this is for a ISOC endpoint return null.
3187c478bd9Sstevel@tonic-gate 	 * Isochronous uses ITD put directly onto the PFL.
3197c478bd9Sstevel@tonic-gate 	 */
3207c478bd9Sstevel@tonic-gate 	if (ph) {
3217c478bd9Sstevel@tonic-gate 		if (EHCI_ISOC_ENDPOINT((&ph->p_ep))) {
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 			return (NULL);
3247c478bd9Sstevel@tonic-gate 		}
3257c478bd9Sstevel@tonic-gate 	}
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	/*
3287c478bd9Sstevel@tonic-gate 	 * The first 63 endpoints in the Endpoint Descriptor (QH)
3297c478bd9Sstevel@tonic-gate 	 * buffer pool are reserved for building interrupt lattice
3307c478bd9Sstevel@tonic-gate 	 * tree. Search for a blank endpoint descriptor in the QH
3317c478bd9Sstevel@tonic-gate 	 * buffer pool.
3327c478bd9Sstevel@tonic-gate 	 */
3337c478bd9Sstevel@tonic-gate 	for (i = EHCI_NUM_STATIC_NODES; i < ehci_qh_pool_size; i ++) {
3347c478bd9Sstevel@tonic-gate 		state = Get_QH(ehcip->ehci_qh_pool_addr[i].qh_state);
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 		if (state == EHCI_QH_FREE) {
3377c478bd9Sstevel@tonic-gate 			break;
3387c478bd9Sstevel@tonic-gate 		}
3397c478bd9Sstevel@tonic-gate 	}
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
3427c478bd9Sstevel@tonic-gate 	    "ehci_alloc_qh: Allocated %d", i);
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	if (i == ehci_qh_pool_size) {
3457c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALLOC,  ehcip->ehci_log_hdl,
3467c478bd9Sstevel@tonic-gate 		    "ehci_alloc_qh: QH exhausted");
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate 		return (NULL);
3497c478bd9Sstevel@tonic-gate 	} else {
3507c478bd9Sstevel@tonic-gate 		qh = &ehcip->ehci_qh_pool_addr[i];
3516a9de478Ssl 		bzero((void *)qh, sizeof (ehci_qh_t));
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
3547c478bd9Sstevel@tonic-gate 		    "ehci_alloc_qh: Allocated address 0x%p", (void *)qh);
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 		/* Check polled mode flag */
3577c478bd9Sstevel@tonic-gate 		if (flag == EHCI_POLLED_MODE_FLAG) {
3587c478bd9Sstevel@tonic-gate 			Set_QH(qh->qh_link_ptr, EHCI_QH_LINK_PTR_VALID);
3597c478bd9Sstevel@tonic-gate 			Set_QH(qh->qh_ctrl, EHCI_QH_CTRL_ED_INACTIVATE);
3607c478bd9Sstevel@tonic-gate 		}
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 		/* Unpack the endpoint descriptor into a control field */
3637c478bd9Sstevel@tonic-gate 		if (ph) {
3647c478bd9Sstevel@tonic-gate 			if ((ehci_initialize_dummy(ehcip,
3657c478bd9Sstevel@tonic-gate 			    qh)) == USB_NO_RESOURCES) {
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 				Set_QH(qh->qh_state, EHCI_QH_FREE);
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 				return (NULL);
3707c478bd9Sstevel@tonic-gate 			}
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 			ehci_unpack_endpoint(ehcip, ph, qh);
3737c478bd9Sstevel@tonic-gate 
3746aef9e11SToomas Soome 			Set_QH(qh->qh_curr_qtd, 0);
3757c478bd9Sstevel@tonic-gate 			Set_QH(qh->qh_alt_next_qtd,
3767c478bd9Sstevel@tonic-gate 			    EHCI_QH_ALT_NEXT_QTD_PTR_VALID);
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 			/* Change QH's state Active */
3797c478bd9Sstevel@tonic-gate 			Set_QH(qh->qh_state, EHCI_QH_ACTIVE);
3807c478bd9Sstevel@tonic-gate 		} else {
3817c478bd9Sstevel@tonic-gate 			Set_QH(qh->qh_status, EHCI_QH_STS_HALTED);
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 			/* Change QH's state Static */
3847c478bd9Sstevel@tonic-gate 			Set_QH(qh->qh_state, EHCI_QH_STATIC);
3857c478bd9Sstevel@tonic-gate 		}
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 		ehci_print_qh(ehcip, qh);
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 		return (qh);
3907c478bd9Sstevel@tonic-gate 	}
3917c478bd9Sstevel@tonic-gate }
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate /*
3957c478bd9Sstevel@tonic-gate  * ehci_unpack_endpoint:
3967c478bd9Sstevel@tonic-gate  *
3977c478bd9Sstevel@tonic-gate  * Unpack the information in the pipe handle and create the first byte
3987c478bd9Sstevel@tonic-gate  * of the Host Controller's (HC) Endpoint Descriptor (QH).
3997c478bd9Sstevel@tonic-gate  */
4007c478bd9Sstevel@tonic-gate static void
ehci_unpack_endpoint(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,ehci_qh_t * qh)4017c478bd9Sstevel@tonic-gate ehci_unpack_endpoint(
4027c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
4037c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
4047c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh)
4057c478bd9Sstevel@tonic-gate {
4067c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*endpoint = &ph->p_ep;
4077c478bd9Sstevel@tonic-gate 	uint_t			maxpacketsize, addr, xactions;
4087c478bd9Sstevel@tonic-gate 	uint_t			ctrl = 0, status = 0, split_ctrl = 0;
4097c478bd9Sstevel@tonic-gate 	usb_port_status_t	usb_port_status;
4107c478bd9Sstevel@tonic-gate 	usba_device_t		*usba_device = ph->p_usba_device;
4117c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
4147c478bd9Sstevel@tonic-gate 	    "ehci_unpack_endpoint:");
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
4177c478bd9Sstevel@tonic-gate 	ctrl = usba_device->usb_addr;
4187c478bd9Sstevel@tonic-gate 	usb_port_status = usba_device->usb_port_status;
4197c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 	addr = endpoint->bEndpointAddress;
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	/* Assign the endpoint's address */
4247c478bd9Sstevel@tonic-gate 	ctrl |= ((addr & USB_EP_NUM_MASK) << EHCI_QH_CTRL_ED_NUMBER_SHIFT);
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 	/* Assign the speed */
4277c478bd9Sstevel@tonic-gate 	switch (usb_port_status) {
4287c478bd9Sstevel@tonic-gate 	case USBA_LOW_SPEED_DEV:
4297c478bd9Sstevel@tonic-gate 		ctrl |= EHCI_QH_CTRL_ED_LOW_SPEED;
4307c478bd9Sstevel@tonic-gate 		break;
4317c478bd9Sstevel@tonic-gate 	case USBA_FULL_SPEED_DEV:
4327c478bd9Sstevel@tonic-gate 		ctrl |= EHCI_QH_CTRL_ED_FULL_SPEED;
4337c478bd9Sstevel@tonic-gate 		break;
4347c478bd9Sstevel@tonic-gate 	case USBA_HIGH_SPEED_DEV:
4357c478bd9Sstevel@tonic-gate 		ctrl |= EHCI_QH_CTRL_ED_HIGH_SPEED;
4367c478bd9Sstevel@tonic-gate 		break;
4377c478bd9Sstevel@tonic-gate 	}
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 	switch (endpoint->bmAttributes & USB_EP_ATTR_MASK) {
4407c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
4417c478bd9Sstevel@tonic-gate 		/* Assign data toggle information */
4427c478bd9Sstevel@tonic-gate 		ctrl |= EHCI_QH_CTRL_DATA_TOGGLE;
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 		if (usb_port_status != USBA_HIGH_SPEED_DEV) {
4457c478bd9Sstevel@tonic-gate 			ctrl |= EHCI_QH_CTRL_CONTROL_ED_FLAG;
4467c478bd9Sstevel@tonic-gate 		}
4477c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
4487c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
4497c478bd9Sstevel@tonic-gate 		/* Maximum nak counter */
4507c478bd9Sstevel@tonic-gate 		ctrl |= EHCI_QH_CTRL_MAX_NC;
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 		if (usb_port_status == USBA_HIGH_SPEED_DEV) {
4537c478bd9Sstevel@tonic-gate 			/*
4547c478bd9Sstevel@tonic-gate 			 * Perform ping before executing control
4557c478bd9Sstevel@tonic-gate 			 * and bulk transactions.
4567c478bd9Sstevel@tonic-gate 			 */
4577c478bd9Sstevel@tonic-gate 			status = EHCI_QH_STS_DO_PING;
4587c478bd9Sstevel@tonic-gate 		}
4597c478bd9Sstevel@tonic-gate 		break;
4607c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
4617c478bd9Sstevel@tonic-gate 		/* Set start split mask */
4627c478bd9Sstevel@tonic-gate 		split_ctrl = (pp->pp_smask & EHCI_QH_SPLIT_CTRL_INTR_MASK);
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 		/*
4657c478bd9Sstevel@tonic-gate 		 * Set complete split mask for low/full speed
4667c478bd9Sstevel@tonic-gate 		 * usb devices.
4677c478bd9Sstevel@tonic-gate 		 */
4687c478bd9Sstevel@tonic-gate 		if (usb_port_status != USBA_HIGH_SPEED_DEV) {
4697c478bd9Sstevel@tonic-gate 			split_ctrl |= ((pp->pp_cmask <<
4707c478bd9Sstevel@tonic-gate 			    EHCI_QH_SPLIT_CTRL_COMP_SHIFT) &
4717c478bd9Sstevel@tonic-gate 			    EHCI_QH_SPLIT_CTRL_COMP_MASK);
4727c478bd9Sstevel@tonic-gate 		}
4737c478bd9Sstevel@tonic-gate 		break;
4747c478bd9Sstevel@tonic-gate 	}
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	/* Get the max transactions per microframe */
4777c478bd9Sstevel@tonic-gate 	xactions = (endpoint->wMaxPacketSize &
4787c478bd9Sstevel@tonic-gate 	    USB_EP_MAX_XACTS_MASK) >>  USB_EP_MAX_XACTS_SHIFT;
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 	switch (xactions) {
4817c478bd9Sstevel@tonic-gate 	case 0:
4827c478bd9Sstevel@tonic-gate 		split_ctrl |= EHCI_QH_SPLIT_CTRL_1_XACTS;
4837c478bd9Sstevel@tonic-gate 		break;
4847c478bd9Sstevel@tonic-gate 	case 1:
4857c478bd9Sstevel@tonic-gate 		split_ctrl |= EHCI_QH_SPLIT_CTRL_2_XACTS;
4867c478bd9Sstevel@tonic-gate 		break;
4877c478bd9Sstevel@tonic-gate 	case 2:
4887c478bd9Sstevel@tonic-gate 		split_ctrl |= EHCI_QH_SPLIT_CTRL_3_XACTS;
4897c478bd9Sstevel@tonic-gate 		break;
4907c478bd9Sstevel@tonic-gate 	default:
4917c478bd9Sstevel@tonic-gate 		split_ctrl |= EHCI_QH_SPLIT_CTRL_1_XACTS;
4927c478bd9Sstevel@tonic-gate 		break;
4937c478bd9Sstevel@tonic-gate 	}
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 	/*
4967c478bd9Sstevel@tonic-gate 	 * For low/full speed devices, program high speed hub
4977c478bd9Sstevel@tonic-gate 	 * address and port number.
4987c478bd9Sstevel@tonic-gate 	 */
4997c478bd9Sstevel@tonic-gate 	if (usb_port_status != USBA_HIGH_SPEED_DEV) {
5007c478bd9Sstevel@tonic-gate 		mutex_enter(&usba_device->usb_mutex);
5017c478bd9Sstevel@tonic-gate 		split_ctrl |= ((usba_device->usb_hs_hub_addr
5027c478bd9Sstevel@tonic-gate 		    << EHCI_QH_SPLIT_CTRL_HUB_ADDR_SHIFT) &
5037c478bd9Sstevel@tonic-gate 		    EHCI_QH_SPLIT_CTRL_HUB_ADDR);
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 		split_ctrl |= ((usba_device->usb_hs_hub_port
5067c478bd9Sstevel@tonic-gate 		    << EHCI_QH_SPLIT_CTRL_HUB_PORT_SHIFT) &
5077c478bd9Sstevel@tonic-gate 		    EHCI_QH_SPLIT_CTRL_HUB_PORT);
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 		mutex_exit(&usba_device->usb_mutex);
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 		/* Set start split transaction state */
5127c478bd9Sstevel@tonic-gate 		status = EHCI_QH_STS_DO_START_SPLIT;
5137c478bd9Sstevel@tonic-gate 	}
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate 	/* Assign endpoint's maxpacketsize */
5167c478bd9Sstevel@tonic-gate 	maxpacketsize = endpoint->wMaxPacketSize & USB_EP_MAX_PKTSZ_MASK;
5177c478bd9Sstevel@tonic-gate 	maxpacketsize = maxpacketsize << EHCI_QH_CTRL_MAXPKTSZ_SHIFT;
5187c478bd9Sstevel@tonic-gate 	ctrl |= (maxpacketsize & EHCI_QH_CTRL_MAXPKTSZ);
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_ctrl, ctrl);
5217c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_split_ctrl, split_ctrl);
5227c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_status, status);
5237c478bd9Sstevel@tonic-gate }
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate /*
5277c478bd9Sstevel@tonic-gate  * ehci_insert_qh:
5287c478bd9Sstevel@tonic-gate  *
5297c478bd9Sstevel@tonic-gate  * Add the Endpoint Descriptor (QH) into the Host Controller's
5307c478bd9Sstevel@tonic-gate  * (HC) appropriate endpoint list.
5317c478bd9Sstevel@tonic-gate  */
5327c478bd9Sstevel@tonic-gate void
ehci_insert_qh(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)5337c478bd9Sstevel@tonic-gate ehci_insert_qh(
5347c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
5357c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
5367c478bd9Sstevel@tonic-gate {
5377c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
540112116d8Sfb 	    "ehci_insert_qh: qh=0x%p", (void *)pp->pp_qh);
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	switch (ph->p_ep.bmAttributes & USB_EP_ATTR_MASK) {
5457c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
5467c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
5477c478bd9Sstevel@tonic-gate 		ehci_insert_async_qh(ehcip, pp);
5487c478bd9Sstevel@tonic-gate 		ehcip->ehci_open_async_count++;
5497c478bd9Sstevel@tonic-gate 		break;
5507c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
5517c478bd9Sstevel@tonic-gate 		ehci_insert_intr_qh(ehcip, pp);
5527c478bd9Sstevel@tonic-gate 		ehcip->ehci_open_periodic_count++;
5537c478bd9Sstevel@tonic-gate 		break;
5547c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
5557c478bd9Sstevel@tonic-gate 		/* ISOCH does not use QH, don't do anything but update count */
5567c478bd9Sstevel@tonic-gate 		ehcip->ehci_open_periodic_count++;
5577c478bd9Sstevel@tonic-gate 		break;
5587c478bd9Sstevel@tonic-gate 	}
5597c478bd9Sstevel@tonic-gate }
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate /*
5637c478bd9Sstevel@tonic-gate  * ehci_insert_async_qh:
5647c478bd9Sstevel@tonic-gate  *
5657c478bd9Sstevel@tonic-gate  * Insert a control/bulk endpoint into the Host Controller's (HC)
5667c478bd9Sstevel@tonic-gate  * Asynchronous schedule endpoint list.
5677c478bd9Sstevel@tonic-gate  */
5687c478bd9Sstevel@tonic-gate static void
ehci_insert_async_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp)5697c478bd9Sstevel@tonic-gate ehci_insert_async_qh(
5707c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
5717c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
5727c478bd9Sstevel@tonic-gate {
5737c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh = pp->pp_qh;
5747c478bd9Sstevel@tonic-gate 	ehci_qh_t		*async_head_qh;
5757c478bd9Sstevel@tonic-gate 	ehci_qh_t		*next_qh;
5767c478bd9Sstevel@tonic-gate 	uintptr_t		qh_addr;
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
5797c478bd9Sstevel@tonic-gate 	    "ehci_insert_async_qh:");
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 	/* Make sure this QH is not already in the list */
584*0a1044f1SToomas Soome 	ASSERT((Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR) == 0);
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 	qh_addr = ehci_qh_cpu_to_iommu(ehcip, qh);
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	/* Obtain a ptr to the head of the Async schedule list */
5897c478bd9Sstevel@tonic-gate 	async_head_qh = ehcip->ehci_head_of_async_sched_list;
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 	if (async_head_qh == NULL) {
5927c478bd9Sstevel@tonic-gate 		/* Set this QH to be the "head" of the circular list */
5937c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_ctrl,
5947c478bd9Sstevel@tonic-gate 		    (Get_QH(qh->qh_ctrl) | EHCI_QH_CTRL_RECLAIM_HEAD));
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 		/* Set new QH's link and previous pointer to itself */
5977c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_link_ptr, qh_addr | EHCI_QH_LINK_REF_QH);
5987c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_prev, qh_addr);
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 		ehcip->ehci_head_of_async_sched_list = qh;
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 		/* Set the head ptr to the new endpoint */
6037c478bd9Sstevel@tonic-gate 		Set_OpReg(ehci_async_list_addr, qh_addr);
604921cd50dSgk 
605921cd50dSgk 		/*
606921cd50dSgk 		 * For some reason this register might get nulled out by
607921cd50dSgk 		 * the Uli M1575 South Bridge. To workaround the hardware
608921cd50dSgk 		 * problem, check the value after write and retry if the
609921cd50dSgk 		 * last write fails.
610921cd50dSgk 		 *
611921cd50dSgk 		 * If the ASYNCLISTADDR remains "stuck" after
612921cd50dSgk 		 * EHCI_MAX_RETRY retries, then the M1575 is broken
613921cd50dSgk 		 * and is stuck in an inconsistent state and is about
614921cd50dSgk 		 * to crash the machine with a trn_oor panic when it
615921cd50dSgk 		 * does a DMA read from 0x0.  It is better to panic
616921cd50dSgk 		 * now rather than wait for the trn_oor crash; this
617921cd50dSgk 		 * way Customer Service will have a clean signature
618921cd50dSgk 		 * that indicts the M1575 chip rather than a
619921cd50dSgk 		 * mysterious and hard-to-diagnose trn_oor panic.
620921cd50dSgk 		 */
621921cd50dSgk 		if ((ehcip->ehci_vendor_id == PCI_VENDOR_ULi_M1575) &&
622921cd50dSgk 		    (ehcip->ehci_device_id == PCI_DEVICE_ULi_M1575) &&
623921cd50dSgk 		    (qh_addr != Get_OpReg(ehci_async_list_addr))) {
624921cd50dSgk 			int retry = 0;
625921cd50dSgk 
626921cd50dSgk 			Set_OpRegRetry(ehci_async_list_addr, qh_addr, retry);
627921cd50dSgk 			if (retry >= EHCI_MAX_RETRY)
628921cd50dSgk 				cmn_err(CE_PANIC, "ehci_insert_async_qh:"
629921cd50dSgk 				    " ASYNCLISTADDR write failed.");
630921cd50dSgk 
631921cd50dSgk 			USB_DPRINTF_L2(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
632921cd50dSgk 			    "ehci_insert_async_qh: ASYNCLISTADDR "
6336a9de478Ssl 			    "write failed, retry=%d", retry);
634921cd50dSgk 		}
6357c478bd9Sstevel@tonic-gate 	} else {
6367c478bd9Sstevel@tonic-gate 		ASSERT(Get_QH(async_head_qh->qh_ctrl) &
6377c478bd9Sstevel@tonic-gate 		    EHCI_QH_CTRL_RECLAIM_HEAD);
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 		/* Ensure this QH's "H" bit is not set */
6407c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_ctrl,
6417c478bd9Sstevel@tonic-gate 		    (Get_QH(qh->qh_ctrl) & ~EHCI_QH_CTRL_RECLAIM_HEAD));
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 		next_qh = ehci_qh_iommu_to_cpu(ehcip,
6447c478bd9Sstevel@tonic-gate 		    Get_QH(async_head_qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 		/* Set new QH's link and previous pointers */
6477c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_link_ptr,
6487c478bd9Sstevel@tonic-gate 		    Get_QH(async_head_qh->qh_link_ptr) | EHCI_QH_LINK_REF_QH);
6497c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, async_head_qh));
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 		/* Set next QH's prev pointer */
6527c478bd9Sstevel@tonic-gate 		Set_QH(next_qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, qh));
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 		/* Set QH Head's link pointer points to new QH */
6557c478bd9Sstevel@tonic-gate 		Set_QH(async_head_qh->qh_link_ptr,
6567c478bd9Sstevel@tonic-gate 		    qh_addr | EHCI_QH_LINK_REF_QH);
6577c478bd9Sstevel@tonic-gate 	}
6587c478bd9Sstevel@tonic-gate }
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate /*
6627c478bd9Sstevel@tonic-gate  * ehci_insert_intr_qh:
6637c478bd9Sstevel@tonic-gate  *
6647c478bd9Sstevel@tonic-gate  * Insert a interrupt endpoint into the Host Controller's (HC) interrupt
6657c478bd9Sstevel@tonic-gate  * lattice tree.
6667c478bd9Sstevel@tonic-gate  */
6677c478bd9Sstevel@tonic-gate static void
ehci_insert_intr_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp)6687c478bd9Sstevel@tonic-gate ehci_insert_intr_qh(
6697c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
6707c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
6717c478bd9Sstevel@tonic-gate {
6727c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh = pp->pp_qh;
6737c478bd9Sstevel@tonic-gate 	ehci_qh_t		*next_lattice_qh, *lattice_qh;
6747c478bd9Sstevel@tonic-gate 	uint_t			hnode;
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
6777c478bd9Sstevel@tonic-gate 	    "ehci_insert_intr_qh:");
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 	/* Make sure this QH is not already in the list */
682*0a1044f1SToomas Soome 	ASSERT((Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR) == 0);
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	/*
6857c478bd9Sstevel@tonic-gate 	 * The appropriate high speed node was found
6867c478bd9Sstevel@tonic-gate 	 * during the opening of the pipe.
6877c478bd9Sstevel@tonic-gate 	 */
6887c478bd9Sstevel@tonic-gate 	hnode = pp->pp_pnode;
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	/* Find the lattice endpoint */
6917c478bd9Sstevel@tonic-gate 	lattice_qh = &ehcip->ehci_qh_pool_addr[hnode];
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 	/* Find the next lattice endpoint */
6947c478bd9Sstevel@tonic-gate 	next_lattice_qh = ehci_qh_iommu_to_cpu(
6957c478bd9Sstevel@tonic-gate 	    ehcip, (Get_QH(lattice_qh->qh_link_ptr) & EHCI_QH_LINK_PTR));
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 	/* Update the previous pointer */
6987c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_prev, ehci_qh_cpu_to_iommu(ehcip, lattice_qh));
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 	/* Check next_lattice_qh value */
7017c478bd9Sstevel@tonic-gate 	if (next_lattice_qh) {
7027c478bd9Sstevel@tonic-gate 		/* Update this qh to point to the next one in the lattice */
7037c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_link_ptr, Get_QH(lattice_qh->qh_link_ptr));
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 		/* Update the previous pointer of qh->qh_link_ptr */
7067c478bd9Sstevel@tonic-gate 		if (Get_QH(next_lattice_qh->qh_state) != EHCI_QH_STATIC) {
7077c478bd9Sstevel@tonic-gate 			Set_QH(next_lattice_qh->qh_prev,
7087c478bd9Sstevel@tonic-gate 			    ehci_qh_cpu_to_iommu(ehcip, qh));
7097c478bd9Sstevel@tonic-gate 		}
7107c478bd9Sstevel@tonic-gate 	} else {
7117c478bd9Sstevel@tonic-gate 		/* Update qh's link pointer to terminate periodic list */
7127c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_link_ptr,
7137c478bd9Sstevel@tonic-gate 		    (Get_QH(lattice_qh->qh_link_ptr) | EHCI_QH_LINK_PTR_VALID));
7147c478bd9Sstevel@tonic-gate 	}
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	/* Insert this endpoint into the lattice */
7177c478bd9Sstevel@tonic-gate 	Set_QH(lattice_qh->qh_link_ptr,
7187c478bd9Sstevel@tonic-gate 	    (ehci_qh_cpu_to_iommu(ehcip, qh) | EHCI_QH_LINK_REF_QH));
7197c478bd9Sstevel@tonic-gate }
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate /*
7237c478bd9Sstevel@tonic-gate  * ehci_modify_qh_status_bit:
7247c478bd9Sstevel@tonic-gate  *
7257c478bd9Sstevel@tonic-gate  * Modify the halt bit on the Host Controller (HC) Endpoint Descriptor (QH).
7267c478bd9Sstevel@tonic-gate  *
7277c478bd9Sstevel@tonic-gate  * If several threads try to halt the same pipe, they will need to wait on
7287c478bd9Sstevel@tonic-gate  * a condition variable.  Only one thread is allowed to halt or unhalt the
7297c478bd9Sstevel@tonic-gate  * pipe at a time.
7307c478bd9Sstevel@tonic-gate  *
7317c478bd9Sstevel@tonic-gate  * Usually after a halt pipe, an unhalt pipe will follow soon after.  There
7327c478bd9Sstevel@tonic-gate  * is an assumption that an Unhalt pipe will never occur without a halt pipe.
7337c478bd9Sstevel@tonic-gate  */
7347c478bd9Sstevel@tonic-gate static void
ehci_modify_qh_status_bit(ehci_state_t * ehcip,ehci_pipe_private_t * pp,halt_bit_t action)7357c478bd9Sstevel@tonic-gate ehci_modify_qh_status_bit(
7367c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
7377c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
7387c478bd9Sstevel@tonic-gate 	halt_bit_t		action)
7397c478bd9Sstevel@tonic-gate {
7407c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh = pp->pp_qh;
7417c478bd9Sstevel@tonic-gate 	uint_t			smask, eps, split_intr_qh;
7427c478bd9Sstevel@tonic-gate 	uint_t			status;
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
7457c478bd9Sstevel@tonic-gate 	    "ehci_modify_qh_status_bit: action=0x%x qh=0x%p",
746112116d8Sfb 	    action, (void *)qh);
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 	ehci_print_qh(ehcip, qh);
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	/*
7537c478bd9Sstevel@tonic-gate 	 * If this pipe is in the middle of halting don't allow another
7547c478bd9Sstevel@tonic-gate 	 * thread to come in and modify the same pipe.
7557c478bd9Sstevel@tonic-gate 	 */
7567c478bd9Sstevel@tonic-gate 	while (pp->pp_halt_state & EHCI_HALT_STATE_HALTING) {
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 		cv_wait(&pp->pp_halt_cmpl_cv,
7596a9de478Ssl 		    &ehcip->ehci_int_mutex);
7607c478bd9Sstevel@tonic-gate 	}
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	/* Sync the QH QTD pool to get up to date information */
7637c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	if (action == CLEAR_HALT) {
7677c478bd9Sstevel@tonic-gate 		/*
7687c478bd9Sstevel@tonic-gate 		 * If the halt bit is to be cleared, just clear it.
7697c478bd9Sstevel@tonic-gate 		 * there shouldn't be any race condition problems.
7707c478bd9Sstevel@tonic-gate 		 * If the host controller reads the bit before the
7717c478bd9Sstevel@tonic-gate 		 * driver has a chance to set the bit, the bit will
7727c478bd9Sstevel@tonic-gate 		 * be reread on the next frame.
7737c478bd9Sstevel@tonic-gate 		 */
7747c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_ctrl,
7757c478bd9Sstevel@tonic-gate 		    (Get_QH(qh->qh_ctrl) & ~EHCI_QH_CTRL_ED_INACTIVATE));
7767c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_status,
7777c478bd9Sstevel@tonic-gate 		    Get_QH(qh->qh_status) & ~(EHCI_QH_STS_XACT_STATUS));
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 		goto success;
7807c478bd9Sstevel@tonic-gate 	}
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate 	/* Halt the the QH, but first check to see if it is already halted */
7837c478bd9Sstevel@tonic-gate 	status = Get_QH(qh->qh_status);
7847c478bd9Sstevel@tonic-gate 	if (!(status & EHCI_QH_STS_HALTED)) {
7857c478bd9Sstevel@tonic-gate 		/* Indicate that this pipe is in the middle of halting. */
7867c478bd9Sstevel@tonic-gate 		pp->pp_halt_state |= EHCI_HALT_STATE_HALTING;
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 		/*
7897c478bd9Sstevel@tonic-gate 		 * Find out if this is an full/low speed interrupt endpoint.
7907c478bd9Sstevel@tonic-gate 		 * A non-zero Cmask indicates that this QH is an interrupt
7917c478bd9Sstevel@tonic-gate 		 * endpoint.  Check the endpoint speed to see if it is either
7927c478bd9Sstevel@tonic-gate 		 * FULL or LOW .
7937c478bd9Sstevel@tonic-gate 		 */
7947c478bd9Sstevel@tonic-gate 		smask = Get_QH(qh->qh_split_ctrl) &
7957c478bd9Sstevel@tonic-gate 		    EHCI_QH_SPLIT_CTRL_INTR_MASK;
7967c478bd9Sstevel@tonic-gate 		eps = Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_ED_SPEED;
7977c478bd9Sstevel@tonic-gate 		split_intr_qh = ((smask != 0) &&
7987c478bd9Sstevel@tonic-gate 		    (eps != EHCI_QH_CTRL_ED_HIGH_SPEED));
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 		if (eps == EHCI_QH_CTRL_ED_HIGH_SPEED) {
8017c478bd9Sstevel@tonic-gate 			ehci_halt_hs_qh(ehcip, pp, qh);
8027c478bd9Sstevel@tonic-gate 		} else {
8037c478bd9Sstevel@tonic-gate 			if (split_intr_qh) {
8047c478bd9Sstevel@tonic-gate 				ehci_halt_fls_intr_qh(ehcip, qh);
8057c478bd9Sstevel@tonic-gate 			} else {
8067c478bd9Sstevel@tonic-gate 				ehci_halt_fls_ctrl_and_bulk_qh(ehcip, pp, qh);
8077c478bd9Sstevel@tonic-gate 			}
8087c478bd9Sstevel@tonic-gate 		}
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate 		/* Indicate that this pipe is not in the middle of halting. */
8117c478bd9Sstevel@tonic-gate 		pp->pp_halt_state &= ~EHCI_HALT_STATE_HALTING;
8127c478bd9Sstevel@tonic-gate 	}
8137c478bd9Sstevel@tonic-gate 
8147c478bd9Sstevel@tonic-gate 	/* Sync the QH QTD pool again to get the most up to date information */
8157c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 	ehci_print_qh(ehcip, qh);
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate 	status = Get_QH(qh->qh_status);
8207c478bd9Sstevel@tonic-gate 	if (!(status & EHCI_QH_STS_HALTED)) {
8217c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
822112116d8Sfb 		    "ehci_modify_qh_status_bit: Failed to halt qh=0x%p",
823112116d8Sfb 		    (void *)qh);
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate 		ehci_print_qh(ehcip, qh);
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 		/* Set host controller soft state to error */
8287c478bd9Sstevel@tonic-gate 		ehcip->ehci_hc_soft_state = EHCI_CTLR_ERROR_STATE;
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 		ASSERT(status & EHCI_QH_STS_HALTED);
8317c478bd9Sstevel@tonic-gate 	}
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate success:
8347c478bd9Sstevel@tonic-gate 	/* Wake up threads waiting for this pipe to be halted. */
8357c478bd9Sstevel@tonic-gate 	cv_signal(&pp->pp_halt_cmpl_cv);
8367c478bd9Sstevel@tonic-gate }
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate /*
8407c478bd9Sstevel@tonic-gate  * ehci_halt_hs_qh:
8417c478bd9Sstevel@tonic-gate  *
8427c478bd9Sstevel@tonic-gate  * Halts all types of HIGH SPEED QHs.
8437c478bd9Sstevel@tonic-gate  */
8447c478bd9Sstevel@tonic-gate static void
ehci_halt_hs_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_qh_t * qh)8457c478bd9Sstevel@tonic-gate ehci_halt_hs_qh(
8467c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
8477c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
8487c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh)
8497c478bd9Sstevel@tonic-gate {
8507c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
8537c478bd9Sstevel@tonic-gate 	    "ehci_halt_hs_qh:");
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	/* Remove this qh from the HCD's view, but do not reclaim it */
8567c478bd9Sstevel@tonic-gate 	ehci_remove_qh(ehcip, pp, B_FALSE);
8577484d118SRaymond Chen 	ehci_toggle_scheduler_on_pipe(ehcip);
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 	/*
8607c478bd9Sstevel@tonic-gate 	 * Wait for atleast one SOF, just in case the HCD is in the
8617c478bd9Sstevel@tonic-gate 	 * middle accessing this QH.
8627c478bd9Sstevel@tonic-gate 	 */
8637c478bd9Sstevel@tonic-gate 	(void) ehci_wait_for_sof(ehcip);
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 	/* Sync the QH QTD pool to get up to date information */
8667c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	/* Modify the status bit and halt this QH. */
8697c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_status,
8707c478bd9Sstevel@tonic-gate 	    ((Get_QH(qh->qh_status) &
8716a9de478Ssl 	    ~(EHCI_QH_STS_ACTIVE)) | EHCI_QH_STS_HALTED));
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	/* Insert this QH back into the HCD's view */
8747c478bd9Sstevel@tonic-gate 	ehci_insert_qh(ehcip, ph);
8757484d118SRaymond Chen 	ehci_toggle_scheduler_on_pipe(ehcip);
8767c478bd9Sstevel@tonic-gate }
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 
8797c478bd9Sstevel@tonic-gate /*
8807c478bd9Sstevel@tonic-gate  * ehci_halt_fls_ctrl_and_bulk_qh:
8817c478bd9Sstevel@tonic-gate  *
8827c478bd9Sstevel@tonic-gate  * Halts FULL/LOW Ctrl and Bulk QHs only.
8837c478bd9Sstevel@tonic-gate  */
8847c478bd9Sstevel@tonic-gate static void
ehci_halt_fls_ctrl_and_bulk_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_qh_t * qh)8857c478bd9Sstevel@tonic-gate ehci_halt_fls_ctrl_and_bulk_qh(
8867c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
8877c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
8887c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh)
8897c478bd9Sstevel@tonic-gate {
8907c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
8917c478bd9Sstevel@tonic-gate 	uint_t			status, split_status, bytes_left;
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
8957c478bd9Sstevel@tonic-gate 	    "ehci_halt_fls_ctrl_and_bulk_qh:");
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate 	/* Remove this qh from the HCD's view, but do not reclaim it */
8987c478bd9Sstevel@tonic-gate 	ehci_remove_qh(ehcip, pp, B_FALSE);
8997484d118SRaymond Chen 	ehci_toggle_scheduler_on_pipe(ehcip);
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 	/*
9027c478bd9Sstevel@tonic-gate 	 * Wait for atleast one SOF, just in case the HCD is in the
9037c478bd9Sstevel@tonic-gate 	 * middle accessing this QH.
9047c478bd9Sstevel@tonic-gate 	 */
9057c478bd9Sstevel@tonic-gate 	(void) ehci_wait_for_sof(ehcip);
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate 	/* Sync the QH QTD pool to get up to date information */
9087c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
9097c478bd9Sstevel@tonic-gate 
9107c478bd9Sstevel@tonic-gate 	/* Modify the status bit and halt this QH. */
9117c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_status,
9127c478bd9Sstevel@tonic-gate 	    ((Get_QH(qh->qh_status) &
9136a9de478Ssl 	    ~(EHCI_QH_STS_ACTIVE)) | EHCI_QH_STS_HALTED));
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	/* Check to see if the QH was in the middle of a transaction */
9167c478bd9Sstevel@tonic-gate 	status = Get_QH(qh->qh_status);
9177c478bd9Sstevel@tonic-gate 	split_status = status & EHCI_QH_STS_SPLIT_XSTATE;
9187c478bd9Sstevel@tonic-gate 	bytes_left = status & EHCI_QH_STS_BYTES_TO_XFER;
9197c478bd9Sstevel@tonic-gate 	if ((split_status == EHCI_QH_STS_DO_COMPLETE_SPLIT) &&
9207c478bd9Sstevel@tonic-gate 	    (bytes_left != 0)) {
9217c478bd9Sstevel@tonic-gate 		/* send ClearTTBuffer to this device's parent 2.0 hub */
9227c478bd9Sstevel@tonic-gate 		ehci_clear_tt_buffer(ehcip, ph, qh);
9237c478bd9Sstevel@tonic-gate 	}
9247c478bd9Sstevel@tonic-gate 
9257c478bd9Sstevel@tonic-gate 	/* Insert this QH back into the HCD's view */
9267c478bd9Sstevel@tonic-gate 	ehci_insert_qh(ehcip, ph);
9277484d118SRaymond Chen 	ehci_toggle_scheduler_on_pipe(ehcip);
9287c478bd9Sstevel@tonic-gate }
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate /*
9327c478bd9Sstevel@tonic-gate  * ehci_clear_tt_buffer
9337c478bd9Sstevel@tonic-gate  *
9347c478bd9Sstevel@tonic-gate  * This function will sent a Clear_TT_Buffer request to the pipe's
9357c478bd9Sstevel@tonic-gate  * parent 2.0 hub.
9367c478bd9Sstevel@tonic-gate  */
9377c478bd9Sstevel@tonic-gate static void
ehci_clear_tt_buffer(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,ehci_qh_t * qh)9387c478bd9Sstevel@tonic-gate ehci_clear_tt_buffer(
9397c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
9407c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
9417c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh)
9427c478bd9Sstevel@tonic-gate {
9437c478bd9Sstevel@tonic-gate 	usba_device_t		*usba_device;
9447c478bd9Sstevel@tonic-gate 	usba_device_t		*hub_usba_device;
9457c478bd9Sstevel@tonic-gate 	usb_pipe_handle_t	hub_def_ph;
9467c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd;
9477c478bd9Sstevel@tonic-gate 	uchar_t			attributes;
9487c478bd9Sstevel@tonic-gate 	uint16_t		wValue;
9497c478bd9Sstevel@tonic-gate 	usb_ctrl_setup_t	setup;
9507c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason;
9517c478bd9Sstevel@tonic-gate 	usb_cb_flags_t		cb_flags;
9527c478bd9Sstevel@tonic-gate 	int			retry;
9537c478bd9Sstevel@tonic-gate 
9547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
9557c478bd9Sstevel@tonic-gate 	    "ehci_clear_tt_buffer: ");
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 	/* Get some information about the current pipe */
9587c478bd9Sstevel@tonic-gate 	usba_device = ph->p_usba_device;
9597c478bd9Sstevel@tonic-gate 	eptd = &ph->p_ep;
9607c478bd9Sstevel@tonic-gate 	attributes = eptd->bmAttributes & USB_EP_ATTR_MASK;
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 	/*
9637c478bd9Sstevel@tonic-gate 	 * Create the wIndex for this request (usb spec 11.24.2.3)
9647c478bd9Sstevel@tonic-gate 	 * 3..0		Endpoint Number
9657c478bd9Sstevel@tonic-gate 	 * 10..4	Device Address
9667c478bd9Sstevel@tonic-gate 	 * 12..11	Endpoint Type
9677c478bd9Sstevel@tonic-gate 	 * 14..13	Reserved (must be 0)
9687c478bd9Sstevel@tonic-gate 	 * 15		Direction 1 = IN, 0 = OUT
9697c478bd9Sstevel@tonic-gate 	 */
9707c478bd9Sstevel@tonic-gate 	wValue = 0;
9717c478bd9Sstevel@tonic-gate 	if ((eptd->bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
9727c478bd9Sstevel@tonic-gate 		wValue |= 0x8000;
9737c478bd9Sstevel@tonic-gate 	}
9747c478bd9Sstevel@tonic-gate 	wValue |= attributes << 11;
9757c478bd9Sstevel@tonic-gate 	wValue |= (Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_DEVICE_ADDRESS) << 4;
9767c478bd9Sstevel@tonic-gate 	wValue |= (Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_ED_HIGH_SPEED) >>
9777c478bd9Sstevel@tonic-gate 	    EHCI_QH_CTRL_ED_NUMBER_SHIFT;
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
9807c478bd9Sstevel@tonic-gate 
9817c478bd9Sstevel@tonic-gate 	/* Manually fill in the request. */
9827c478bd9Sstevel@tonic-gate 	setup.bmRequestType = EHCI_CLEAR_TT_BUFFER_REQTYPE;
9837c478bd9Sstevel@tonic-gate 	setup.bRequest = EHCI_CLEAR_TT_BUFFER_BREQ;
9847c478bd9Sstevel@tonic-gate 	setup.wValue = wValue;
9857c478bd9Sstevel@tonic-gate 	setup.wIndex = 1;
9867c478bd9Sstevel@tonic-gate 	setup.wLength = 0;
9877c478bd9Sstevel@tonic-gate 	setup.attrs = USB_ATTRS_NONE;
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 	/* Get the usba_device of the parent 2.0 hub. */
9907c478bd9Sstevel@tonic-gate 	mutex_enter(&usba_device->usb_mutex);
9917c478bd9Sstevel@tonic-gate 	hub_usba_device = usba_device->usb_hs_hub_usba_dev;
9927c478bd9Sstevel@tonic-gate 	mutex_exit(&usba_device->usb_mutex);
9937c478bd9Sstevel@tonic-gate 
9947c478bd9Sstevel@tonic-gate 	/* Get the default ctrl pipe for the parent 2.0 hub */
9957c478bd9Sstevel@tonic-gate 	mutex_enter(&hub_usba_device->usb_mutex);
9967c478bd9Sstevel@tonic-gate 	hub_def_ph = (usb_pipe_handle_t)&hub_usba_device->usb_ph_list[0];
9977c478bd9Sstevel@tonic-gate 	mutex_exit(&hub_usba_device->usb_mutex);
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 	for (retry = 0; retry < 3; retry++) {
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 		/* sync send the request to the default pipe */
10027c478bd9Sstevel@tonic-gate 		if (usb_pipe_ctrl_xfer_wait(
10037c478bd9Sstevel@tonic-gate 		    hub_def_ph,
10047c478bd9Sstevel@tonic-gate 		    &setup,
10057c478bd9Sstevel@tonic-gate 		    NULL,
10067c478bd9Sstevel@tonic-gate 		    &completion_reason, &cb_flags, 0) == USB_SUCCESS) {
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate 			break;
10097c478bd9Sstevel@tonic-gate 		}
10107c478bd9Sstevel@tonic-gate 
10117c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
10127c478bd9Sstevel@tonic-gate 		    "ehci_clear_tt_buffer: Failed to clear tt buffer,"
10137c478bd9Sstevel@tonic-gate 		    "retry = %d, cr = %d, cb_flags = 0x%x\n",
10147c478bd9Sstevel@tonic-gate 		    retry, completion_reason, cb_flags);
10157c478bd9Sstevel@tonic-gate 	}
10167c478bd9Sstevel@tonic-gate 
10177c478bd9Sstevel@tonic-gate 	if (retry >= 3) {
10187c478bd9Sstevel@tonic-gate 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
10197c478bd9Sstevel@tonic-gate 		dev_info_t *dip = hub_usba_device->usb_dip;
10207c478bd9Sstevel@tonic-gate 
10217c478bd9Sstevel@tonic-gate 		/*
10227c478bd9Sstevel@tonic-gate 		 * Ask the user to hotplug the 2.0 hub, to make sure that
10237c478bd9Sstevel@tonic-gate 		 * all the buffer is in sync since this command has failed.
10247c478bd9Sstevel@tonic-gate 		 */
10257c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
10267c478bd9Sstevel@tonic-gate 		    "Error recovery failure: Please hotplug the 2.0 hub at"
10277c478bd9Sstevel@tonic-gate 		    "%s", ddi_pathname(dip, path));
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 		kmem_free(path, MAXPATHLEN);
10307c478bd9Sstevel@tonic-gate 	}
10317c478bd9Sstevel@tonic-gate 
10327c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
10337c478bd9Sstevel@tonic-gate }
10347c478bd9Sstevel@tonic-gate 
10357c478bd9Sstevel@tonic-gate /*
10367c478bd9Sstevel@tonic-gate  * ehci_halt_fls_intr_qh:
10377c478bd9Sstevel@tonic-gate  *
10387c478bd9Sstevel@tonic-gate  * Halts FULL/LOW speed Intr QHs.
10397c478bd9Sstevel@tonic-gate  */
10407c478bd9Sstevel@tonic-gate static void
ehci_halt_fls_intr_qh(ehci_state_t * ehcip,ehci_qh_t * qh)10417c478bd9Sstevel@tonic-gate ehci_halt_fls_intr_qh(
10427c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
10437c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh)
10447c478bd9Sstevel@tonic-gate {
10457c478bd9Sstevel@tonic-gate 	usb_frame_number_t	starting_frame;
10467c478bd9Sstevel@tonic-gate 	usb_frame_number_t	frames_past;
10477c478bd9Sstevel@tonic-gate 	uint_t			status, i;
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
10507c478bd9Sstevel@tonic-gate 	    "ehci_halt_fls_intr_qh:");
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 	/*
10537c478bd9Sstevel@tonic-gate 	 * Ask the HC to deactivate the QH in a
10547c478bd9Sstevel@tonic-gate 	 * full/low periodic QH.
10557c478bd9Sstevel@tonic-gate 	 */
10567c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_ctrl,
10577c478bd9Sstevel@tonic-gate 	    (Get_QH(qh->qh_ctrl) | EHCI_QH_CTRL_ED_INACTIVATE));
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 	starting_frame = ehci_get_current_frame_number(ehcip);
10607c478bd9Sstevel@tonic-gate 
10617c478bd9Sstevel@tonic-gate 	/*
10627c478bd9Sstevel@tonic-gate 	 * Wait at least EHCI_NUM_INTR_QH_LISTS+2 frame or until
10637c478bd9Sstevel@tonic-gate 	 * the QH has been halted.
10647c478bd9Sstevel@tonic-gate 	 */
10657c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
10667c478bd9Sstevel@tonic-gate 	frames_past = 0;
10677c478bd9Sstevel@tonic-gate 	status = Get_QH(qh->qh_status) & EHCI_QTD_CTRL_ACTIVE_XACT;
10687c478bd9Sstevel@tonic-gate 
10697c478bd9Sstevel@tonic-gate 	while ((frames_past <= (EHCI_NUM_INTR_QH_LISTS + 2)) &&
10707c478bd9Sstevel@tonic-gate 	    (status != 0)) {
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 		(void) ehci_wait_for_sof(ehcip);
10737c478bd9Sstevel@tonic-gate 
10747c478bd9Sstevel@tonic-gate 		Sync_QH_QTD_Pool(ehcip);
10757c478bd9Sstevel@tonic-gate 		status = Get_QH(qh->qh_status) & EHCI_QTD_CTRL_ACTIVE_XACT;
10767c478bd9Sstevel@tonic-gate 		frames_past = ehci_get_current_frame_number(ehcip) -
10777c478bd9Sstevel@tonic-gate 		    starting_frame;
10787c478bd9Sstevel@tonic-gate 	}
10797c478bd9Sstevel@tonic-gate 
10807c478bd9Sstevel@tonic-gate 	/* Modify the status bit and halt this QH. */
10817c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
10827c478bd9Sstevel@tonic-gate 
10837c478bd9Sstevel@tonic-gate 	status = Get_QH(qh->qh_status);
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate 	for (i = 0; i < EHCI_NUM_INTR_QH_LISTS; i++) {
10867c478bd9Sstevel@tonic-gate 		Set_QH(qh->qh_status,
10876a9de478Ssl 		    ((Get_QH(qh->qh_status) &
10886a9de478Ssl 		    ~(EHCI_QH_STS_ACTIVE)) | EHCI_QH_STS_HALTED));
10897c478bd9Sstevel@tonic-gate 
10907c478bd9Sstevel@tonic-gate 		Sync_QH_QTD_Pool(ehcip);
10917c478bd9Sstevel@tonic-gate 
10927c478bd9Sstevel@tonic-gate 		(void) ehci_wait_for_sof(ehcip);
10937c478bd9Sstevel@tonic-gate 		Sync_QH_QTD_Pool(ehcip);
10947c478bd9Sstevel@tonic-gate 
10957c478bd9Sstevel@tonic-gate 		if (Get_QH(qh->qh_status) & EHCI_QH_STS_HALTED) {
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate 			break;
10987c478bd9Sstevel@tonic-gate 		}
10997c478bd9Sstevel@tonic-gate 	}
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate 	Sync_QH_QTD_Pool(ehcip);
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
1104112116d8Sfb 	    "ehci_halt_fls_intr_qh: qh=0x%p frames past=%llu,"
1105112116d8Sfb 	    " status=0x%x, 0x%x", (void *)qh,
1106112116d8Sfb 	    (unsigned long long)(ehci_get_current_frame_number(ehcip) -
1107112116d8Sfb 	    starting_frame), status, Get_QH(qh->qh_status));
11087c478bd9Sstevel@tonic-gate }
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate /*
11127c478bd9Sstevel@tonic-gate  * ehci_remove_qh:
11137c478bd9Sstevel@tonic-gate  *
11147c478bd9Sstevel@tonic-gate  * Remove the Endpoint Descriptor (QH) from the Host Controller's appropriate
11157c478bd9Sstevel@tonic-gate  * endpoint list.
11167c478bd9Sstevel@tonic-gate  */
11177c478bd9Sstevel@tonic-gate void
ehci_remove_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,boolean_t reclaim)11187c478bd9Sstevel@tonic-gate ehci_remove_qh(
11197c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
11207c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
11217c478bd9Sstevel@tonic-gate 	boolean_t		reclaim)
11227c478bd9Sstevel@tonic-gate {
11237c478bd9Sstevel@tonic-gate 	uchar_t			attributes;
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
11267c478bd9Sstevel@tonic-gate 
11277c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
1128112116d8Sfb 	    "ehci_remove_qh: qh=0x%p", (void *)pp->pp_qh);
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate 	attributes = pp->pp_pipe_handle->p_ep.bmAttributes & USB_EP_ATTR_MASK;
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate 	switch (attributes) {
11337c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
11347c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
11357c478bd9Sstevel@tonic-gate 		ehci_remove_async_qh(ehcip, pp, reclaim);
11367c478bd9Sstevel@tonic-gate 		ehcip->ehci_open_async_count--;
11377c478bd9Sstevel@tonic-gate 		break;
11387c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
11397c478bd9Sstevel@tonic-gate 		ehci_remove_intr_qh(ehcip, pp, reclaim);
11407c478bd9Sstevel@tonic-gate 		ehcip->ehci_open_periodic_count--;
11417c478bd9Sstevel@tonic-gate 		break;
11427c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
11437c478bd9Sstevel@tonic-gate 		/* ISOCH does not use QH, don't do anything but update count */
11447c478bd9Sstevel@tonic-gate 		ehcip->ehci_open_periodic_count--;
11457c478bd9Sstevel@tonic-gate 		break;
11467c478bd9Sstevel@tonic-gate 	}
11477c478bd9Sstevel@tonic-gate }
11487c478bd9Sstevel@tonic-gate 
11497c478bd9Sstevel@tonic-gate 
11507c478bd9Sstevel@tonic-gate /*
11517c478bd9Sstevel@tonic-gate  * ehci_remove_async_qh:
11527c478bd9Sstevel@tonic-gate  *
11537c478bd9Sstevel@tonic-gate  * Remove a control/bulk endpoint into the Host Controller's (HC)
11547c478bd9Sstevel@tonic-gate  * Asynchronous schedule endpoint list.
11557c478bd9Sstevel@tonic-gate  */
11567c478bd9Sstevel@tonic-gate static void
ehci_remove_async_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,boolean_t reclaim)11577c478bd9Sstevel@tonic-gate ehci_remove_async_qh(
11587c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
11597c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
11607c478bd9Sstevel@tonic-gate 	boolean_t		reclaim)
11617c478bd9Sstevel@tonic-gate {
11627c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh = pp->pp_qh; /* qh to be removed */
11637c478bd9Sstevel@tonic-gate 	ehci_qh_t		*prev_qh, *next_qh;
11647c478bd9Sstevel@tonic-gate 
11657c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
11667c478bd9Sstevel@tonic-gate 	    "ehci_remove_async_qh:");
11677c478bd9Sstevel@tonic-gate 
11687c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
11697c478bd9Sstevel@tonic-gate 
11707c478bd9Sstevel@tonic-gate 	prev_qh = ehci_qh_iommu_to_cpu(ehcip,
11717c478bd9Sstevel@tonic-gate 	    Get_QH(qh->qh_prev) & EHCI_QH_LINK_PTR);
11727c478bd9Sstevel@tonic-gate 	next_qh = ehci_qh_iommu_to_cpu(ehcip,
11737c478bd9Sstevel@tonic-gate 	    Get_QH(qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate 	/* Make sure this QH is in the list */
11767c478bd9Sstevel@tonic-gate 	ASSERT(prev_qh != NULL);
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 	/*
11797c478bd9Sstevel@tonic-gate 	 * If next QH and current QH are the same, then this is the last
11807c478bd9Sstevel@tonic-gate 	 * QH on the Asynchronous Schedule list.
11817c478bd9Sstevel@tonic-gate 	 */
11827c478bd9Sstevel@tonic-gate 	if (qh == next_qh) {
11837c478bd9Sstevel@tonic-gate 		ASSERT(Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_RECLAIM_HEAD);
11847c478bd9Sstevel@tonic-gate 		/*
11857c478bd9Sstevel@tonic-gate 		 * Null our pointer to the async sched list, but do not
11867c478bd9Sstevel@tonic-gate 		 * touch the host controller's list_addr.
11877c478bd9Sstevel@tonic-gate 		 */
11887c478bd9Sstevel@tonic-gate 		ehcip->ehci_head_of_async_sched_list = NULL;
11897c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_open_async_count == 1);
11907c478bd9Sstevel@tonic-gate 	} else {
11917c478bd9Sstevel@tonic-gate 		/* If this QH is the HEAD then find another one to replace it */
11927c478bd9Sstevel@tonic-gate 		if (ehcip->ehci_head_of_async_sched_list == qh) {
11937c478bd9Sstevel@tonic-gate 
11947c478bd9Sstevel@tonic-gate 			ASSERT(Get_QH(qh->qh_ctrl) & EHCI_QH_CTRL_RECLAIM_HEAD);
11957c478bd9Sstevel@tonic-gate 			ehcip->ehci_head_of_async_sched_list = next_qh;
11967c478bd9Sstevel@tonic-gate 			Set_QH(next_qh->qh_ctrl,
11977c478bd9Sstevel@tonic-gate 			    Get_QH(next_qh->qh_ctrl) |
11987c478bd9Sstevel@tonic-gate 			    EHCI_QH_CTRL_RECLAIM_HEAD);
11997c478bd9Sstevel@tonic-gate 		}
12007c478bd9Sstevel@tonic-gate 		Set_QH(prev_qh->qh_link_ptr, Get_QH(qh->qh_link_ptr));
12017c478bd9Sstevel@tonic-gate 		Set_QH(next_qh->qh_prev, Get_QH(qh->qh_prev));
12027c478bd9Sstevel@tonic-gate 	}
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 	/* qh_prev to indicate it is no longer in the circular list */
12056aef9e11SToomas Soome 	Set_QH(qh->qh_prev, 0);
12067c478bd9Sstevel@tonic-gate 
12077c478bd9Sstevel@tonic-gate 	if (reclaim) {
12087c478bd9Sstevel@tonic-gate 		ehci_insert_qh_on_reclaim_list(ehcip, pp);
12097c478bd9Sstevel@tonic-gate 	}
12107c478bd9Sstevel@tonic-gate }
12117c478bd9Sstevel@tonic-gate 
12127c478bd9Sstevel@tonic-gate 
12137c478bd9Sstevel@tonic-gate /*
12147c478bd9Sstevel@tonic-gate  * ehci_remove_intr_qh:
12157c478bd9Sstevel@tonic-gate  *
12167c478bd9Sstevel@tonic-gate  * Set up an interrupt endpoint to be removed from the Host Controller's (HC)
12177c478bd9Sstevel@tonic-gate  * interrupt lattice tree. The Endpoint Descriptor (QH) will be freed in the
12187c478bd9Sstevel@tonic-gate  * interrupt handler.
12197c478bd9Sstevel@tonic-gate  */
12207c478bd9Sstevel@tonic-gate static void
ehci_remove_intr_qh(ehci_state_t * ehcip,ehci_pipe_private_t * pp,boolean_t reclaim)12217c478bd9Sstevel@tonic-gate ehci_remove_intr_qh(
12227c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
12237c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
12247c478bd9Sstevel@tonic-gate 	boolean_t		reclaim)
12257c478bd9Sstevel@tonic-gate {
12267c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh = pp->pp_qh; /* qh to be removed */
12277c478bd9Sstevel@tonic-gate 	ehci_qh_t		*prev_qh, *next_qh;
12287c478bd9Sstevel@tonic-gate 
12297c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
12307c478bd9Sstevel@tonic-gate 	    "ehci_remove_intr_qh:");
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
12337c478bd9Sstevel@tonic-gate 
12347c478bd9Sstevel@tonic-gate 	prev_qh = ehci_qh_iommu_to_cpu(ehcip, Get_QH(qh->qh_prev));
12357c478bd9Sstevel@tonic-gate 	next_qh = ehci_qh_iommu_to_cpu(ehcip,
12367c478bd9Sstevel@tonic-gate 	    Get_QH(qh->qh_link_ptr) & EHCI_QH_LINK_PTR);
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate 	/* Make sure this QH is in the list */
12397c478bd9Sstevel@tonic-gate 	ASSERT(prev_qh != NULL);
12407c478bd9Sstevel@tonic-gate 
12417c478bd9Sstevel@tonic-gate 	if (next_qh) {
12427c478bd9Sstevel@tonic-gate 		/* Update previous qh's link pointer */
12437c478bd9Sstevel@tonic-gate 		Set_QH(prev_qh->qh_link_ptr, Get_QH(qh->qh_link_ptr));
12447c478bd9Sstevel@tonic-gate 
12457c478bd9Sstevel@tonic-gate 		if (Get_QH(next_qh->qh_state) != EHCI_QH_STATIC) {
12467c478bd9Sstevel@tonic-gate 			/* Set the previous pointer of the next one */
12477c478bd9Sstevel@tonic-gate 			Set_QH(next_qh->qh_prev, Get_QH(qh->qh_prev));
12487c478bd9Sstevel@tonic-gate 		}
12497c478bd9Sstevel@tonic-gate 	} else {
12507c478bd9Sstevel@tonic-gate 		/* Update previous qh's link pointer */
12517c478bd9Sstevel@tonic-gate 		Set_QH(prev_qh->qh_link_ptr,
12527c478bd9Sstevel@tonic-gate 		    (Get_QH(qh->qh_link_ptr) | EHCI_QH_LINK_PTR_VALID));
12537c478bd9Sstevel@tonic-gate 	}
12547c478bd9Sstevel@tonic-gate 
12557c478bd9Sstevel@tonic-gate 	/* qh_prev to indicate it is no longer in the circular list */
12566aef9e11SToomas Soome 	Set_QH(qh->qh_prev, 0);
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 	if (reclaim) {
12597c478bd9Sstevel@tonic-gate 		ehci_insert_qh_on_reclaim_list(ehcip, pp);
12607c478bd9Sstevel@tonic-gate 	}
12617c478bd9Sstevel@tonic-gate }
12627c478bd9Sstevel@tonic-gate 
12637c478bd9Sstevel@tonic-gate 
12647c478bd9Sstevel@tonic-gate /*
12657c478bd9Sstevel@tonic-gate  * ehci_insert_qh_on_reclaim_list:
12667c478bd9Sstevel@tonic-gate  *
12677c478bd9Sstevel@tonic-gate  * Insert Endpoint onto the reclaim list
12687c478bd9Sstevel@tonic-gate  */
12697c478bd9Sstevel@tonic-gate static void
ehci_insert_qh_on_reclaim_list(ehci_state_t * ehcip,ehci_pipe_private_t * pp)12707c478bd9Sstevel@tonic-gate ehci_insert_qh_on_reclaim_list(
12717c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
12727c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
12737c478bd9Sstevel@tonic-gate {
12747c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh = pp->pp_qh; /* qh to be removed */
12757c478bd9Sstevel@tonic-gate 	ehci_qh_t		*next_qh, *prev_qh;
12767c478bd9Sstevel@tonic-gate 	usb_frame_number_t	frame_number;
12777c478bd9Sstevel@tonic-gate 
12787c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate 	/*
12817c478bd9Sstevel@tonic-gate 	 * Read current usb frame number and add appropriate number of
12827c478bd9Sstevel@tonic-gate 	 * usb frames needs to wait before reclaiming current endpoint.
12837c478bd9Sstevel@tonic-gate 	 */
12847c478bd9Sstevel@tonic-gate 	frame_number =
12857c478bd9Sstevel@tonic-gate 	    ehci_get_current_frame_number(ehcip) + MAX_SOF_WAIT_COUNT;
12867c478bd9Sstevel@tonic-gate 
12877c478bd9Sstevel@tonic-gate 	/* Store 32-bit ID */
12887c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_reclaim_frame,
12897c478bd9Sstevel@tonic-gate 	    ((uint32_t)(EHCI_GET_ID((void *)(uintptr_t)frame_number))));
12907c478bd9Sstevel@tonic-gate 
12917c478bd9Sstevel@tonic-gate 	/* Insert the endpoint onto the reclamation list */
12927c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_reclaim_list) {
12937c478bd9Sstevel@tonic-gate 		next_qh = ehcip->ehci_reclaim_list;
12947c478bd9Sstevel@tonic-gate 
12957c478bd9Sstevel@tonic-gate 		while (next_qh) {
12967c478bd9Sstevel@tonic-gate 			prev_qh = next_qh;
12977c478bd9Sstevel@tonic-gate 			next_qh = ehci_qh_iommu_to_cpu(ehcip,
12987c478bd9Sstevel@tonic-gate 			    Get_QH(next_qh->qh_reclaim_next));
12997c478bd9Sstevel@tonic-gate 		}
13007c478bd9Sstevel@tonic-gate 
13017c478bd9Sstevel@tonic-gate 		Set_QH(prev_qh->qh_reclaim_next,
13027c478bd9Sstevel@tonic-gate 		    ehci_qh_cpu_to_iommu(ehcip, qh));
13037c478bd9Sstevel@tonic-gate 	} else {
13047c478bd9Sstevel@tonic-gate 		ehcip->ehci_reclaim_list = qh;
13057c478bd9Sstevel@tonic-gate 	}
13067c478bd9Sstevel@tonic-gate 
1307*0a1044f1SToomas Soome 	ASSERT(Get_QH(qh->qh_reclaim_next) == 0);
13087c478bd9Sstevel@tonic-gate }
13097c478bd9Sstevel@tonic-gate 
13107c478bd9Sstevel@tonic-gate 
13117c478bd9Sstevel@tonic-gate /*
13127c478bd9Sstevel@tonic-gate  * ehci_deallocate_qh:
13137c478bd9Sstevel@tonic-gate  *
13147c478bd9Sstevel@tonic-gate  * Deallocate a Host Controller's (HC) Endpoint Descriptor (QH).
13157c478bd9Sstevel@tonic-gate  *
13167c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
13177c478bd9Sstevel@tonic-gate  */
13187c478bd9Sstevel@tonic-gate void
ehci_deallocate_qh(ehci_state_t * ehcip,ehci_qh_t * old_qh)13197c478bd9Sstevel@tonic-gate ehci_deallocate_qh(
13207c478bd9Sstevel@tonic-gate 	ehci_state_t	*ehcip,
13217c478bd9Sstevel@tonic-gate 	ehci_qh_t	*old_qh)
13227c478bd9Sstevel@tonic-gate {
13237c478bd9Sstevel@tonic-gate 	ehci_qtd_t	*first_dummy_qtd, *second_dummy_qtd;
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
13267c478bd9Sstevel@tonic-gate 	    "ehci_deallocate_qh:");
13277c478bd9Sstevel@tonic-gate 
13287c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
13297c478bd9Sstevel@tonic-gate 
13307c478bd9Sstevel@tonic-gate 	first_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
13317c478bd9Sstevel@tonic-gate 	    (Get_QH(old_qh->qh_next_qtd) & EHCI_QH_NEXT_QTD_PTR));
13327c478bd9Sstevel@tonic-gate 
13337c478bd9Sstevel@tonic-gate 	if (first_dummy_qtd) {
13347c478bd9Sstevel@tonic-gate 		ASSERT(Get_QTD(first_dummy_qtd->qtd_state) == EHCI_QTD_DUMMY);
13357c478bd9Sstevel@tonic-gate 
13367c478bd9Sstevel@tonic-gate 		second_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
13377c478bd9Sstevel@tonic-gate 		    Get_QTD(first_dummy_qtd->qtd_next_qtd));
13387c478bd9Sstevel@tonic-gate 
13397c478bd9Sstevel@tonic-gate 		if (second_dummy_qtd) {
13407c478bd9Sstevel@tonic-gate 			ASSERT(Get_QTD(second_dummy_qtd->qtd_state) ==
13417c478bd9Sstevel@tonic-gate 			    EHCI_QTD_DUMMY);
13427c478bd9Sstevel@tonic-gate 
13437c478bd9Sstevel@tonic-gate 			ehci_deallocate_qtd(ehcip, second_dummy_qtd);
13447c478bd9Sstevel@tonic-gate 		}
13457c478bd9Sstevel@tonic-gate 
13467c478bd9Sstevel@tonic-gate 		ehci_deallocate_qtd(ehcip, first_dummy_qtd);
13477c478bd9Sstevel@tonic-gate 	}
13487c478bd9Sstevel@tonic-gate 
13497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
13507c478bd9Sstevel@tonic-gate 	    "ehci_deallocate_qh: Deallocated 0x%p", (void *)old_qh);
13517c478bd9Sstevel@tonic-gate 
13527c478bd9Sstevel@tonic-gate 	Set_QH(old_qh->qh_state, EHCI_QH_FREE);
13537c478bd9Sstevel@tonic-gate }
13547c478bd9Sstevel@tonic-gate 
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate /*
13577c478bd9Sstevel@tonic-gate  * ehci_qh_cpu_to_iommu:
13587c478bd9Sstevel@tonic-gate  *
13597c478bd9Sstevel@tonic-gate  * This function converts for the given Endpoint Descriptor (QH) CPU address
13607c478bd9Sstevel@tonic-gate  * to IO address.
13617c478bd9Sstevel@tonic-gate  *
13627c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
13637c478bd9Sstevel@tonic-gate  */
13647c478bd9Sstevel@tonic-gate uint32_t
ehci_qh_cpu_to_iommu(ehci_state_t * ehcip,ehci_qh_t * addr)13657c478bd9Sstevel@tonic-gate ehci_qh_cpu_to_iommu(
13667c478bd9Sstevel@tonic-gate 	ehci_state_t	*ehcip,
13677c478bd9Sstevel@tonic-gate 	ehci_qh_t	*addr)
13687c478bd9Sstevel@tonic-gate {
13697c478bd9Sstevel@tonic-gate 	uint32_t	qh;
13707c478bd9Sstevel@tonic-gate 
13717c478bd9Sstevel@tonic-gate 	qh = (uint32_t)ehcip->ehci_qh_pool_cookie.dmac_address +
13727c478bd9Sstevel@tonic-gate 	    (uint32_t)((uintptr_t)addr - (uintptr_t)(ehcip->ehci_qh_pool_addr));
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate 	ASSERT(qh >= ehcip->ehci_qh_pool_cookie.dmac_address);
13757c478bd9Sstevel@tonic-gate 	ASSERT(qh <= ehcip->ehci_qh_pool_cookie.dmac_address +
13767c478bd9Sstevel@tonic-gate 	    sizeof (ehci_qh_t) * ehci_qh_pool_size);
13777c478bd9Sstevel@tonic-gate 
13787c478bd9Sstevel@tonic-gate 	return (qh);
13797c478bd9Sstevel@tonic-gate }
13807c478bd9Sstevel@tonic-gate 
13817c478bd9Sstevel@tonic-gate 
13827c478bd9Sstevel@tonic-gate /*
13837c478bd9Sstevel@tonic-gate  * ehci_qh_iommu_to_cpu:
13847c478bd9Sstevel@tonic-gate  *
13857c478bd9Sstevel@tonic-gate  * This function converts for the given Endpoint Descriptor (QH) IO address
13867c478bd9Sstevel@tonic-gate  * to CPU address.
13877c478bd9Sstevel@tonic-gate  */
13887c478bd9Sstevel@tonic-gate ehci_qh_t *
ehci_qh_iommu_to_cpu(ehci_state_t * ehcip,uintptr_t addr)13897c478bd9Sstevel@tonic-gate ehci_qh_iommu_to_cpu(
13907c478bd9Sstevel@tonic-gate 	ehci_state_t	*ehcip,
13917c478bd9Sstevel@tonic-gate 	uintptr_t	addr)
13927c478bd9Sstevel@tonic-gate {
13937c478bd9Sstevel@tonic-gate 	ehci_qh_t	*qh;
13947c478bd9Sstevel@tonic-gate 
13956aef9e11SToomas Soome 	if (addr == 0)
13967c478bd9Sstevel@tonic-gate 		return (NULL);
13977c478bd9Sstevel@tonic-gate 
13987c478bd9Sstevel@tonic-gate 	qh = (ehci_qh_t *)((uintptr_t)
13997c478bd9Sstevel@tonic-gate 	    (addr - ehcip->ehci_qh_pool_cookie.dmac_address) +
14007c478bd9Sstevel@tonic-gate 	    (uintptr_t)ehcip->ehci_qh_pool_addr);
14017c478bd9Sstevel@tonic-gate 
14027c478bd9Sstevel@tonic-gate 	ASSERT(qh >= ehcip->ehci_qh_pool_addr);
14037c478bd9Sstevel@tonic-gate 	ASSERT((uintptr_t)qh <= (uintptr_t)ehcip->ehci_qh_pool_addr +
14047c478bd9Sstevel@tonic-gate 	    (uintptr_t)(sizeof (ehci_qh_t) * ehci_qh_pool_size));
14057c478bd9Sstevel@tonic-gate 
14067c478bd9Sstevel@tonic-gate 	return (qh);
14077c478bd9Sstevel@tonic-gate }
14087c478bd9Sstevel@tonic-gate 
14097c478bd9Sstevel@tonic-gate 
14107c478bd9Sstevel@tonic-gate /*
14117c478bd9Sstevel@tonic-gate  * Transfer Descriptor manipulations functions
14127c478bd9Sstevel@tonic-gate  */
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate /*
14157c478bd9Sstevel@tonic-gate  * ehci_initialize_dummy:
14167c478bd9Sstevel@tonic-gate  *
14177c478bd9Sstevel@tonic-gate  * An Endpoint Descriptor (QH) has a  dummy Transfer Descriptor (QTD) on the
14187c478bd9Sstevel@tonic-gate  * end of its QTD list. Initially, both the head and tail pointers of the QH
14197c478bd9Sstevel@tonic-gate  * point to the dummy QTD.
14207c478bd9Sstevel@tonic-gate  */
14217c478bd9Sstevel@tonic-gate static int
ehci_initialize_dummy(ehci_state_t * ehcip,ehci_qh_t * qh)14227c478bd9Sstevel@tonic-gate ehci_initialize_dummy(
14237c478bd9Sstevel@tonic-gate 	ehci_state_t	*ehcip,
14247c478bd9Sstevel@tonic-gate 	ehci_qh_t	*qh)
14257c478bd9Sstevel@tonic-gate {
14267c478bd9Sstevel@tonic-gate 	ehci_qtd_t	*first_dummy_qtd, *second_dummy_qtd;
14277c478bd9Sstevel@tonic-gate 
14287c478bd9Sstevel@tonic-gate 	/* Allocate first dummy QTD */
14297c478bd9Sstevel@tonic-gate 	first_dummy_qtd = ehci_allocate_qtd_from_pool(ehcip);
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate 	if (first_dummy_qtd == NULL) {
14327c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
14337c478bd9Sstevel@tonic-gate 	}
14347c478bd9Sstevel@tonic-gate 
14357c478bd9Sstevel@tonic-gate 	/* Allocate second dummy QTD */
14367c478bd9Sstevel@tonic-gate 	second_dummy_qtd = ehci_allocate_qtd_from_pool(ehcip);
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 	if (second_dummy_qtd == NULL) {
14397c478bd9Sstevel@tonic-gate 		/* Deallocate first dummy QTD */
14407c478bd9Sstevel@tonic-gate 		ehci_deallocate_qtd(ehcip, first_dummy_qtd);
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
14437c478bd9Sstevel@tonic-gate 	}
14447c478bd9Sstevel@tonic-gate 
14457c478bd9Sstevel@tonic-gate 	/* Next QTD pointer of an QH point to this new dummy QTD */
14467c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_next_qtd, ehci_qtd_cpu_to_iommu(ehcip,
14477c478bd9Sstevel@tonic-gate 	    first_dummy_qtd) & EHCI_QH_NEXT_QTD_PTR);
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate 	/* Set qh's dummy qtd field */
14507c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_dummy_qtd, ehci_qtd_cpu_to_iommu(ehcip, first_dummy_qtd));
14517c478bd9Sstevel@tonic-gate 
14527c478bd9Sstevel@tonic-gate 	/* Set first_dummy's next qtd pointer */
14537c478bd9Sstevel@tonic-gate 	Set_QTD(first_dummy_qtd->qtd_next_qtd,
14547c478bd9Sstevel@tonic-gate 	    ehci_qtd_cpu_to_iommu(ehcip, second_dummy_qtd));
14557c478bd9Sstevel@tonic-gate 
14567c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
14577c478bd9Sstevel@tonic-gate }
14587c478bd9Sstevel@tonic-gate 
14597c478bd9Sstevel@tonic-gate /*
14607c478bd9Sstevel@tonic-gate  * ehci_allocate_ctrl_resources:
14617c478bd9Sstevel@tonic-gate  *
14627c478bd9Sstevel@tonic-gate  * Calculates the number of tds necessary for a ctrl transfer, and allocates
14637c478bd9Sstevel@tonic-gate  * all the resources necessary.
14647c478bd9Sstevel@tonic-gate  *
14657c478bd9Sstevel@tonic-gate  * Returns NULL if there is insufficient resources otherwise TW.
14667c478bd9Sstevel@tonic-gate  */
14677c478bd9Sstevel@tonic-gate ehci_trans_wrapper_t *
ehci_allocate_ctrl_resources(ehci_state_t * ehcip,ehci_pipe_private_t * pp,usb_ctrl_req_t * ctrl_reqp,usb_flags_t usb_flags)14687c478bd9Sstevel@tonic-gate ehci_allocate_ctrl_resources(
14697c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
14707c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
14717c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp,
14727c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
14737c478bd9Sstevel@tonic-gate {
14747c478bd9Sstevel@tonic-gate 	size_t			qtd_count = 2;
14753304303fSsl 	size_t			ctrl_buf_size;
14767c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw;
14777c478bd9Sstevel@tonic-gate 
14787c478bd9Sstevel@tonic-gate 	/* Add one more td for data phase */
14797c478bd9Sstevel@tonic-gate 	if (ctrl_reqp->ctrl_wLength) {
14807c478bd9Sstevel@tonic-gate 		qtd_count += 1;
14817c478bd9Sstevel@tonic-gate 	}
14827c478bd9Sstevel@tonic-gate 
14833304303fSsl 	/*
14843304303fSsl 	 * If we have a control data phase, the data buffer starts
14853304303fSsl 	 * on the next 4K page boundary. So the TW buffer is allocated
14863304303fSsl 	 * to be larger than required. The buffer in the range of
14873304303fSsl 	 * [SETUP_SIZE, EHCI_MAX_QTD_BUF_SIZE) is just for padding
14883304303fSsl 	 * and not to be transferred.
14893304303fSsl 	 */
14903304303fSsl 	if (ctrl_reqp->ctrl_wLength) {
14913304303fSsl 		ctrl_buf_size = EHCI_MAX_QTD_BUF_SIZE +
14923304303fSsl 		    ctrl_reqp->ctrl_wLength;
14933304303fSsl 	} else {
14943304303fSsl 		ctrl_buf_size = SETUP_SIZE;
14953304303fSsl 	}
14963304303fSsl 
14973304303fSsl 	tw = ehci_allocate_tw_resources(ehcip, pp, ctrl_buf_size,
14987c478bd9Sstevel@tonic-gate 	    usb_flags, qtd_count);
14997c478bd9Sstevel@tonic-gate 
15007c478bd9Sstevel@tonic-gate 	return (tw);
15017c478bd9Sstevel@tonic-gate }
15027c478bd9Sstevel@tonic-gate 
15037c478bd9Sstevel@tonic-gate /*
15047c478bd9Sstevel@tonic-gate  * ehci_insert_ctrl_req:
15057c478bd9Sstevel@tonic-gate  *
15067c478bd9Sstevel@tonic-gate  * Create a Transfer Descriptor (QTD) and a data buffer for a control endpoint.
15077c478bd9Sstevel@tonic-gate  */
15087c478bd9Sstevel@tonic-gate /* ARGSUSED */
15097c478bd9Sstevel@tonic-gate void
ehci_insert_ctrl_req(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_ctrl_req_t * ctrl_reqp,ehci_trans_wrapper_t * tw,usb_flags_t usb_flags)15107c478bd9Sstevel@tonic-gate ehci_insert_ctrl_req(
15117c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
15127c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
15137c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp,
15147c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw,
15157c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
15167c478bd9Sstevel@tonic-gate {
15177c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
15187c478bd9Sstevel@tonic-gate 	uchar_t			bmRequestType = ctrl_reqp->ctrl_bmRequestType;
15197c478bd9Sstevel@tonic-gate 	uchar_t			bRequest = ctrl_reqp->ctrl_bRequest;
15207c478bd9Sstevel@tonic-gate 	uint16_t		wValue = ctrl_reqp->ctrl_wValue;
15217c478bd9Sstevel@tonic-gate 	uint16_t		wIndex = ctrl_reqp->ctrl_wIndex;
15227c478bd9Sstevel@tonic-gate 	uint16_t		wLength = ctrl_reqp->ctrl_wLength;
15237c478bd9Sstevel@tonic-gate 	mblk_t			*data = ctrl_reqp->ctrl_data;
15247c478bd9Sstevel@tonic-gate 	uint32_t		ctrl = 0;
15257c478bd9Sstevel@tonic-gate 	uint8_t			setup_packet[8];
15267c478bd9Sstevel@tonic-gate 
15277c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
15287c478bd9Sstevel@tonic-gate 	    "ehci_insert_ctrl_req:");
15297c478bd9Sstevel@tonic-gate 
15307c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
15317c478bd9Sstevel@tonic-gate 
15327c478bd9Sstevel@tonic-gate 	/*
15337c478bd9Sstevel@tonic-gate 	 * Save current control request pointer and timeout values
15347c478bd9Sstevel@tonic-gate 	 * in transfer wrapper.
15357c478bd9Sstevel@tonic-gate 	 */
15367c478bd9Sstevel@tonic-gate 	tw->tw_curr_xfer_reqp = (usb_opaque_t)ctrl_reqp;
15377c478bd9Sstevel@tonic-gate 	tw->tw_timeout = ctrl_reqp->ctrl_timeout ?
15387c478bd9Sstevel@tonic-gate 	    ctrl_reqp->ctrl_timeout : EHCI_DEFAULT_XFER_TIMEOUT;
15397c478bd9Sstevel@tonic-gate 
15407c478bd9Sstevel@tonic-gate 	/*
15417c478bd9Sstevel@tonic-gate 	 * Initialize the callback and any callback data for when
15427c478bd9Sstevel@tonic-gate 	 * the qtd completes.
15437c478bd9Sstevel@tonic-gate 	 */
15447c478bd9Sstevel@tonic-gate 	tw->tw_handle_qtd = ehci_handle_ctrl_qtd;
15457c478bd9Sstevel@tonic-gate 	tw->tw_handle_callback_value = NULL;
15467c478bd9Sstevel@tonic-gate 
15477c478bd9Sstevel@tonic-gate 	/*
15487c478bd9Sstevel@tonic-gate 	 * swap the setup bytes where necessary since we specified
15497c478bd9Sstevel@tonic-gate 	 * NEVERSWAP
15507c478bd9Sstevel@tonic-gate 	 */
15517c478bd9Sstevel@tonic-gate 	setup_packet[0] = bmRequestType;
15527c478bd9Sstevel@tonic-gate 	setup_packet[1] = bRequest;
1553d29f5a71Szhigang lu - Sun Microsystems - Beijing China 	setup_packet[2] = (uint8_t)wValue;
15547c478bd9Sstevel@tonic-gate 	setup_packet[3] = wValue >> 8;
1555d29f5a71Szhigang lu - Sun Microsystems - Beijing China 	setup_packet[4] = (uint8_t)wIndex;
15567c478bd9Sstevel@tonic-gate 	setup_packet[5] = wIndex >> 8;
1557d29f5a71Szhigang lu - Sun Microsystems - Beijing China 	setup_packet[6] = (uint8_t)wLength;
15587c478bd9Sstevel@tonic-gate 	setup_packet[7] = wLength >> 8;
15597c478bd9Sstevel@tonic-gate 
15607c478bd9Sstevel@tonic-gate 	bcopy(setup_packet, tw->tw_buf, SETUP_SIZE);
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate 	Sync_IO_Buffer_for_device(tw->tw_dmahandle, SETUP_SIZE);
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate 	ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_0 | EHCI_QTD_CTRL_SETUP_PID);
15657c478bd9Sstevel@tonic-gate 
15667c478bd9Sstevel@tonic-gate 	/*
15677c478bd9Sstevel@tonic-gate 	 * The QTD's are placed on the QH one at a time.
15687c478bd9Sstevel@tonic-gate 	 * Once this QTD is placed on the done list, the
15697c478bd9Sstevel@tonic-gate 	 * data or status phase QTD will be enqueued.
15707c478bd9Sstevel@tonic-gate 	 */
15713304303fSsl 	(void) ehci_insert_qtd(ehcip, ctrl, 0, SETUP_SIZE,
15727c478bd9Sstevel@tonic-gate 	    EHCI_CTRL_SETUP_PHASE, pp, tw);
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
15757c478bd9Sstevel@tonic-gate 	    "ehci_insert_ctrl_req: pp 0x%p", (void *)pp);
15767c478bd9Sstevel@tonic-gate 
15777c478bd9Sstevel@tonic-gate 	/*
15787c478bd9Sstevel@tonic-gate 	 * If this control transfer has a data phase, record the
15797c478bd9Sstevel@tonic-gate 	 * direction. If the data phase is an OUT transaction,
15807c478bd9Sstevel@tonic-gate 	 * copy the data into the buffer of the transfer wrapper.
15817c478bd9Sstevel@tonic-gate 	 */
15827c478bd9Sstevel@tonic-gate 	if (wLength != 0) {
15837c478bd9Sstevel@tonic-gate 		/* There is a data stage.  Find the direction */
15847c478bd9Sstevel@tonic-gate 		if (bmRequestType & USB_DEV_REQ_DEV_TO_HOST) {
15857c478bd9Sstevel@tonic-gate 			tw->tw_direction = EHCI_QTD_CTRL_IN_PID;
15867c478bd9Sstevel@tonic-gate 		} else {
15877c478bd9Sstevel@tonic-gate 			tw->tw_direction = EHCI_QTD_CTRL_OUT_PID;
15887c478bd9Sstevel@tonic-gate 
15897c478bd9Sstevel@tonic-gate 			/* Copy the data into the message */
15903304303fSsl 			bcopy(data->b_rptr, tw->tw_buf + EHCI_MAX_QTD_BUF_SIZE,
15916a9de478Ssl 			    wLength);
15927c478bd9Sstevel@tonic-gate 
15937c478bd9Sstevel@tonic-gate 			Sync_IO_Buffer_for_device(tw->tw_dmahandle,
15946a9de478Ssl 			    wLength + EHCI_MAX_QTD_BUF_SIZE);
15957c478bd9Sstevel@tonic-gate 		}
15967c478bd9Sstevel@tonic-gate 
15977c478bd9Sstevel@tonic-gate 		ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_1 | tw->tw_direction);
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate 		/*
16007c478bd9Sstevel@tonic-gate 		 * Create the QTD.  If this is an OUT transaction,
16017c478bd9Sstevel@tonic-gate 		 * the data is already in the buffer of the TW.
16023304303fSsl 		 * The transfer should start from EHCI_MAX_QTD_BUF_SIZE
16033304303fSsl 		 * which is 4K aligned, though the ctrl phase only
16043304303fSsl 		 * transfers a length of SETUP_SIZE. The padding data
16053304303fSsl 		 * in the TW buffer are discarded.
16067c478bd9Sstevel@tonic-gate 		 */
16073304303fSsl 		(void) ehci_insert_qtd(ehcip, ctrl, EHCI_MAX_QTD_BUF_SIZE,
16083304303fSsl 		    tw->tw_length - EHCI_MAX_QTD_BUF_SIZE,
16093304303fSsl 		    EHCI_CTRL_DATA_PHASE, pp, tw);
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate 		/*
16127c478bd9Sstevel@tonic-gate 		 * The direction of the STATUS QTD depends  on
16137c478bd9Sstevel@tonic-gate 		 * the direction of the transfer.
16147c478bd9Sstevel@tonic-gate 		 */
16157c478bd9Sstevel@tonic-gate 		if (tw->tw_direction == EHCI_QTD_CTRL_IN_PID) {
16167c478bd9Sstevel@tonic-gate 			ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_1|
16177c478bd9Sstevel@tonic-gate 			    EHCI_QTD_CTRL_OUT_PID |
16187c478bd9Sstevel@tonic-gate 			    EHCI_QTD_CTRL_INTR_ON_COMPLETE);
16197c478bd9Sstevel@tonic-gate 		} else {
16207c478bd9Sstevel@tonic-gate 			ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_1|
16217c478bd9Sstevel@tonic-gate 			    EHCI_QTD_CTRL_IN_PID |
16227c478bd9Sstevel@tonic-gate 			    EHCI_QTD_CTRL_INTR_ON_COMPLETE);
16237c478bd9Sstevel@tonic-gate 		}
16247c478bd9Sstevel@tonic-gate 	} else {
16257c478bd9Sstevel@tonic-gate 		/*
16267c478bd9Sstevel@tonic-gate 		 * There is no data stage,  then initiate
16277c478bd9Sstevel@tonic-gate 		 * status phase from the host.
16287c478bd9Sstevel@tonic-gate 		 */
16297c478bd9Sstevel@tonic-gate 		ctrl = (EHCI_QTD_CTRL_DATA_TOGGLE_1 |
16307c478bd9Sstevel@tonic-gate 		    EHCI_QTD_CTRL_IN_PID |
16317c478bd9Sstevel@tonic-gate 		    EHCI_QTD_CTRL_INTR_ON_COMPLETE);
16327c478bd9Sstevel@tonic-gate 	}
16337c478bd9Sstevel@tonic-gate 
16347c478bd9Sstevel@tonic-gate 
16353304303fSsl 	(void) ehci_insert_qtd(ehcip, ctrl, 0, 0,
16367c478bd9Sstevel@tonic-gate 	    EHCI_CTRL_STATUS_PHASE, pp,  tw);
16377c478bd9Sstevel@tonic-gate 
16387c478bd9Sstevel@tonic-gate 	/* Start the timer for this control transfer */
16397c478bd9Sstevel@tonic-gate 	ehci_start_xfer_timer(ehcip, pp, tw);
16407c478bd9Sstevel@tonic-gate }
16417c478bd9Sstevel@tonic-gate 
16427c478bd9Sstevel@tonic-gate 
16437c478bd9Sstevel@tonic-gate /*
16447c478bd9Sstevel@tonic-gate  * ehci_allocate_bulk_resources:
16457c478bd9Sstevel@tonic-gate  *
16467c478bd9Sstevel@tonic-gate  * Calculates the number of tds necessary for a ctrl transfer, and allocates
16477c478bd9Sstevel@tonic-gate  * all the resources necessary.
16487c478bd9Sstevel@tonic-gate  *
16497c478bd9Sstevel@tonic-gate  * Returns NULL if there is insufficient resources otherwise TW.
16507c478bd9Sstevel@tonic-gate  */
16517c478bd9Sstevel@tonic-gate ehci_trans_wrapper_t *
ehci_allocate_bulk_resources(ehci_state_t * ehcip,ehci_pipe_private_t * pp,usb_bulk_req_t * bulk_reqp,usb_flags_t usb_flags)16527c478bd9Sstevel@tonic-gate ehci_allocate_bulk_resources(
16537c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
16547c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
16557c478bd9Sstevel@tonic-gate 	usb_bulk_req_t		*bulk_reqp,
16567c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
16577c478bd9Sstevel@tonic-gate {
16587c478bd9Sstevel@tonic-gate 	size_t			qtd_count = 0;
16597c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw;
16607c478bd9Sstevel@tonic-gate 
16617c478bd9Sstevel@tonic-gate 	/* Check the size of bulk request */
16627c478bd9Sstevel@tonic-gate 	if (bulk_reqp->bulk_len > EHCI_MAX_BULK_XFER_SIZE) {
16637c478bd9Sstevel@tonic-gate 
16647c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
16657c478bd9Sstevel@tonic-gate 		    "ehci_allocate_bulk_resources: Bulk request size 0x%x is "
16667c478bd9Sstevel@tonic-gate 		    "more than 0x%x", bulk_reqp->bulk_len,
16677c478bd9Sstevel@tonic-gate 		    EHCI_MAX_BULK_XFER_SIZE);
16687c478bd9Sstevel@tonic-gate 
16697c478bd9Sstevel@tonic-gate 		return (NULL);
16707c478bd9Sstevel@tonic-gate 	}
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 	/* Get the required bulk packet size */
16737c478bd9Sstevel@tonic-gate 	qtd_count = bulk_reqp->bulk_len / EHCI_MAX_QTD_XFER_SIZE;
1674688b07c5Sgc 	if (bulk_reqp->bulk_len % EHCI_MAX_QTD_XFER_SIZE ||
16756a9de478Ssl 	    bulk_reqp->bulk_len == 0) {
16767c478bd9Sstevel@tonic-gate 		qtd_count += 1;
16777c478bd9Sstevel@tonic-gate 	}
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 	tw = ehci_allocate_tw_resources(ehcip, pp, bulk_reqp->bulk_len,
16807c478bd9Sstevel@tonic-gate 	    usb_flags, qtd_count);
16817c478bd9Sstevel@tonic-gate 
16827c478bd9Sstevel@tonic-gate 	return (tw);
16837c478bd9Sstevel@tonic-gate }
16847c478bd9Sstevel@tonic-gate 
16857c478bd9Sstevel@tonic-gate /*
16867c478bd9Sstevel@tonic-gate  * ehci_insert_bulk_req:
16877c478bd9Sstevel@tonic-gate  *
16887c478bd9Sstevel@tonic-gate  * Create a Transfer Descriptor (QTD) and a data buffer for a bulk
16897c478bd9Sstevel@tonic-gate  * endpoint.
16907c478bd9Sstevel@tonic-gate  */
16917c478bd9Sstevel@tonic-gate /* ARGSUSED */
16927c478bd9Sstevel@tonic-gate void
ehci_insert_bulk_req(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_bulk_req_t * bulk_reqp,ehci_trans_wrapper_t * tw,usb_flags_t flags)16937c478bd9Sstevel@tonic-gate ehci_insert_bulk_req(
16947c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
16957c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
16967c478bd9Sstevel@tonic-gate 	usb_bulk_req_t		*bulk_reqp,
16977c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw,
16987c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
16997c478bd9Sstevel@tonic-gate {
17007c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
17017c478bd9Sstevel@tonic-gate 	uint_t			bulk_pkt_size, count;
17027c478bd9Sstevel@tonic-gate 	size_t			residue = 0, len = 0;
17037c478bd9Sstevel@tonic-gate 	uint32_t		ctrl = 0;
17047c478bd9Sstevel@tonic-gate 	int			pipe_dir;
17057c478bd9Sstevel@tonic-gate 
17067c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
17077c478bd9Sstevel@tonic-gate 	    "ehci_insert_bulk_req: bulk_reqp = 0x%p flags = 0x%x",
1708112116d8Sfb 	    (void *)bulk_reqp, flags);
17097c478bd9Sstevel@tonic-gate 
17107c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
17117c478bd9Sstevel@tonic-gate 
17127c478bd9Sstevel@tonic-gate 	/* Get the bulk pipe direction */
17137c478bd9Sstevel@tonic-gate 	pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
17147c478bd9Sstevel@tonic-gate 
17157c478bd9Sstevel@tonic-gate 	/* Get the required bulk packet size */
17167c478bd9Sstevel@tonic-gate 	bulk_pkt_size = min(bulk_reqp->bulk_len, EHCI_MAX_QTD_XFER_SIZE);
17177c478bd9Sstevel@tonic-gate 
1718688b07c5Sgc 	if (bulk_pkt_size) {
1719688b07c5Sgc 		residue = tw->tw_length % bulk_pkt_size;
1720688b07c5Sgc 	}
17217c478bd9Sstevel@tonic-gate 
17227c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
17237c478bd9Sstevel@tonic-gate 	    "ehci_insert_bulk_req: bulk_pkt_size = %d", bulk_pkt_size);
17247c478bd9Sstevel@tonic-gate 
17257c478bd9Sstevel@tonic-gate 	/*
17267c478bd9Sstevel@tonic-gate 	 * Save current bulk request pointer and timeout values
17277c478bd9Sstevel@tonic-gate 	 * in transfer wrapper.
17287c478bd9Sstevel@tonic-gate 	 */
17297c478bd9Sstevel@tonic-gate 	tw->tw_curr_xfer_reqp = (usb_opaque_t)bulk_reqp;
17307c478bd9Sstevel@tonic-gate 	tw->tw_timeout = bulk_reqp->bulk_timeout;
17317c478bd9Sstevel@tonic-gate 
17327c478bd9Sstevel@tonic-gate 	/*
17337c478bd9Sstevel@tonic-gate 	 * Initialize the callback and any callback
17347c478bd9Sstevel@tonic-gate 	 * data required when the qtd completes.
17357c478bd9Sstevel@tonic-gate 	 */
17367c478bd9Sstevel@tonic-gate 	tw->tw_handle_qtd = ehci_handle_bulk_qtd;
17377c478bd9Sstevel@tonic-gate 	tw->tw_handle_callback_value = NULL;
17387c478bd9Sstevel@tonic-gate 
17397c478bd9Sstevel@tonic-gate 	tw->tw_direction = (pipe_dir == USB_EP_DIR_OUT) ?
17407c478bd9Sstevel@tonic-gate 	    EHCI_QTD_CTRL_OUT_PID : EHCI_QTD_CTRL_IN_PID;
17417c478bd9Sstevel@tonic-gate 
17427c478bd9Sstevel@tonic-gate 	if (tw->tw_direction == EHCI_QTD_CTRL_OUT_PID) {
17437c478bd9Sstevel@tonic-gate 
1744688b07c5Sgc 		if (bulk_reqp->bulk_len) {
1745688b07c5Sgc 			ASSERT(bulk_reqp->bulk_data != NULL);
17467c478bd9Sstevel@tonic-gate 
1747688b07c5Sgc 			bcopy(bulk_reqp->bulk_data->b_rptr, tw->tw_buf,
17486a9de478Ssl 			    bulk_reqp->bulk_len);
17497c478bd9Sstevel@tonic-gate 
1750688b07c5Sgc 			Sync_IO_Buffer_for_device(tw->tw_dmahandle,
17516a9de478Ssl 			    bulk_reqp->bulk_len);
1752688b07c5Sgc 		}
17537c478bd9Sstevel@tonic-gate 	}
17547c478bd9Sstevel@tonic-gate 
17557c478bd9Sstevel@tonic-gate 	ctrl = tw->tw_direction;
17567c478bd9Sstevel@tonic-gate 
17577c478bd9Sstevel@tonic-gate 	/* Insert all the bulk QTDs */
17587c478bd9Sstevel@tonic-gate 	for (count = 0; count < tw->tw_num_qtds; count++) {
17597c478bd9Sstevel@tonic-gate 
17607c478bd9Sstevel@tonic-gate 		/* Check for last qtd */
17617c478bd9Sstevel@tonic-gate 		if (count == (tw->tw_num_qtds - 1)) {
17627c478bd9Sstevel@tonic-gate 
17637c478bd9Sstevel@tonic-gate 			ctrl |= EHCI_QTD_CTRL_INTR_ON_COMPLETE;
17647c478bd9Sstevel@tonic-gate 
17657c478bd9Sstevel@tonic-gate 			/* Check for inserting residue data */
17667c478bd9Sstevel@tonic-gate 			if (residue) {
1767d29f5a71Szhigang lu - Sun Microsystems - Beijing China 				bulk_pkt_size = (uint_t)residue;
17687c478bd9Sstevel@tonic-gate 			}
17697c478bd9Sstevel@tonic-gate 		}
17707c478bd9Sstevel@tonic-gate 
17717c478bd9Sstevel@tonic-gate 		/* Insert the QTD onto the endpoint */
17723304303fSsl 		(void) ehci_insert_qtd(ehcip, ctrl, len, bulk_pkt_size,
17733304303fSsl 		    0, pp, tw);
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 		len = len + bulk_pkt_size;
17767c478bd9Sstevel@tonic-gate 	}
17777c478bd9Sstevel@tonic-gate 
17787c478bd9Sstevel@tonic-gate 	/* Start the timer for this bulk transfer */
17797c478bd9Sstevel@tonic-gate 	ehci_start_xfer_timer(ehcip, pp, tw);
17807c478bd9Sstevel@tonic-gate }
17817c478bd9Sstevel@tonic-gate 
17827c478bd9Sstevel@tonic-gate 
17837c478bd9Sstevel@tonic-gate /*
17847c478bd9Sstevel@tonic-gate  * ehci_start_periodic_pipe_polling:
17857c478bd9Sstevel@tonic-gate  *
17867c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
17877c478bd9Sstevel@tonic-gate  */
17887c478bd9Sstevel@tonic-gate int
ehci_start_periodic_pipe_polling(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_opaque_t periodic_in_reqp,usb_flags_t flags)17897c478bd9Sstevel@tonic-gate ehci_start_periodic_pipe_polling(
17907c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
17917c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
17927c478bd9Sstevel@tonic-gate 	usb_opaque_t		periodic_in_reqp,
17937c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
17947c478bd9Sstevel@tonic-gate {
17957c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
17967c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
17977c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
17987c478bd9Sstevel@tonic-gate 
17997c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
18007c478bd9Sstevel@tonic-gate 	    "ehci_start_periodic_pipe_polling: ep%d",
18017c478bd9Sstevel@tonic-gate 	    ph->p_ep.bEndpointAddress & USB_EP_NUM_MASK);
18027c478bd9Sstevel@tonic-gate 
18037c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
18047c478bd9Sstevel@tonic-gate 
18057c478bd9Sstevel@tonic-gate 	/*
18067c478bd9Sstevel@tonic-gate 	 * Check and handle start polling on root hub interrupt pipe.
18077c478bd9Sstevel@tonic-gate 	 */
18087c478bd9Sstevel@tonic-gate 	if ((ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) &&
18097c478bd9Sstevel@tonic-gate 	    ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
18107c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_INTR)) {
18117c478bd9Sstevel@tonic-gate 
18127c478bd9Sstevel@tonic-gate 		error = ehci_handle_root_hub_pipe_start_intr_polling(ph,
18137c478bd9Sstevel@tonic-gate 		    (usb_intr_req_t *)periodic_in_reqp, flags);
18147c478bd9Sstevel@tonic-gate 
18157c478bd9Sstevel@tonic-gate 		return (error);
18167c478bd9Sstevel@tonic-gate 	}
18177c478bd9Sstevel@tonic-gate 
18187c478bd9Sstevel@tonic-gate 	switch (pp->pp_state) {
18197c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_IDLE:
18207c478bd9Sstevel@tonic-gate 		/* Save the Original client's Periodic IN request */
18217c478bd9Sstevel@tonic-gate 		pp->pp_client_periodic_in_reqp = periodic_in_reqp;
18227c478bd9Sstevel@tonic-gate 
18237c478bd9Sstevel@tonic-gate 		/*
18247c478bd9Sstevel@tonic-gate 		 * This pipe is uninitialized or if a valid QTD is
18257c478bd9Sstevel@tonic-gate 		 * not found then insert a QTD on the interrupt IN
18267c478bd9Sstevel@tonic-gate 		 * endpoint.
18277c478bd9Sstevel@tonic-gate 		 */
18287c478bd9Sstevel@tonic-gate 		error = ehci_start_pipe_polling(ehcip, ph, flags);
18297c478bd9Sstevel@tonic-gate 
18307c478bd9Sstevel@tonic-gate 		if (error != USB_SUCCESS) {
18317c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_INTR,
18327c478bd9Sstevel@tonic-gate 			    ehcip->ehci_log_hdl,
18337c478bd9Sstevel@tonic-gate 			    "ehci_start_periodic_pipe_polling: "
18347c478bd9Sstevel@tonic-gate 			    "Start polling failed");
18357c478bd9Sstevel@tonic-gate 
18367c478bd9Sstevel@tonic-gate 			pp->pp_client_periodic_in_reqp = NULL;
18377c478bd9Sstevel@tonic-gate 
18387c478bd9Sstevel@tonic-gate 			return (error);
18397c478bd9Sstevel@tonic-gate 		}
18407c478bd9Sstevel@tonic-gate 
18417c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
1842112116d8Sfb 		    "ehci_start_periodic_pipe_polling: PP = 0x%p", (void *)pp);
18437c478bd9Sstevel@tonic-gate 
18447c478bd9Sstevel@tonic-gate #ifdef DEBUG
18457c478bd9Sstevel@tonic-gate 		switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
18467c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
18477c478bd9Sstevel@tonic-gate 			ASSERT((pp->pp_tw_head != NULL) &&
18487c478bd9Sstevel@tonic-gate 			    (pp->pp_tw_tail != NULL));
18497c478bd9Sstevel@tonic-gate 			break;
18507c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_ISOCH:
18517c478bd9Sstevel@tonic-gate 			ASSERT((pp->pp_itw_head != NULL) &&
18527c478bd9Sstevel@tonic-gate 			    (pp->pp_itw_tail != NULL));
18537c478bd9Sstevel@tonic-gate 			break;
18547c478bd9Sstevel@tonic-gate 		}
18557c478bd9Sstevel@tonic-gate #endif
18567c478bd9Sstevel@tonic-gate 
18577c478bd9Sstevel@tonic-gate 		break;
18587c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_ACTIVE:
18597c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR,
18607c478bd9Sstevel@tonic-gate 		    ehcip->ehci_log_hdl,
18617c478bd9Sstevel@tonic-gate 		    "ehci_start_periodic_pipe_polling: "
18627c478bd9Sstevel@tonic-gate 		    "Polling is already in progress");
18637c478bd9Sstevel@tonic-gate 
18647c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
18657c478bd9Sstevel@tonic-gate 		break;
18667c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_ERROR:
18677c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR,
18687c478bd9Sstevel@tonic-gate 		    ehcip->ehci_log_hdl,
18697c478bd9Sstevel@tonic-gate 		    "ehci_start_periodic_pipe_polling: "
18707c478bd9Sstevel@tonic-gate 		    "Pipe is halted and perform reset"
18717c478bd9Sstevel@tonic-gate 		    "before restart polling");
18727c478bd9Sstevel@tonic-gate 
18737c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
18747c478bd9Sstevel@tonic-gate 		break;
18757c478bd9Sstevel@tonic-gate 	default:
18767c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR,
18777c478bd9Sstevel@tonic-gate 		    ehcip->ehci_log_hdl,
18787c478bd9Sstevel@tonic-gate 		    "ehci_start_periodic_pipe_polling: "
18797c478bd9Sstevel@tonic-gate 		    "Undefined state");
18807c478bd9Sstevel@tonic-gate 
18817c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
18827c478bd9Sstevel@tonic-gate 		break;
18837c478bd9Sstevel@tonic-gate 	}
18847c478bd9Sstevel@tonic-gate 
18857c478bd9Sstevel@tonic-gate 	return (error);
18867c478bd9Sstevel@tonic-gate }
18877c478bd9Sstevel@tonic-gate 
18887c478bd9Sstevel@tonic-gate 
18897c478bd9Sstevel@tonic-gate /*
18907c478bd9Sstevel@tonic-gate  * ehci_start_pipe_polling:
18917c478bd9Sstevel@tonic-gate  *
18927c478bd9Sstevel@tonic-gate  * Insert the number of periodic requests corresponding to polling
18937c478bd9Sstevel@tonic-gate  * interval as calculated during pipe open.
18947c478bd9Sstevel@tonic-gate  */
18957c478bd9Sstevel@tonic-gate static int
ehci_start_pipe_polling(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_flags_t flags)18967c478bd9Sstevel@tonic-gate ehci_start_pipe_polling(
18977c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
18987c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
18997c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
19007c478bd9Sstevel@tonic-gate {
19017c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
19027c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
19037c478bd9Sstevel@tonic-gate 	int			error = USB_FAILURE;
19047c478bd9Sstevel@tonic-gate 
19057c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
19067c478bd9Sstevel@tonic-gate 	    "ehci_start_pipe_polling:");
19077c478bd9Sstevel@tonic-gate 
19087c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate 	/*
19117c478bd9Sstevel@tonic-gate 	 * For the start polling, pp_max_periodic_req_cnt will be zero
19127c478bd9Sstevel@tonic-gate 	 * and for the restart polling request, it will be non zero.
19137c478bd9Sstevel@tonic-gate 	 *
19147c478bd9Sstevel@tonic-gate 	 * In case of start polling request, find out number of requests
19157c478bd9Sstevel@tonic-gate 	 * required for the Interrupt IN endpoints corresponding to the
19167c478bd9Sstevel@tonic-gate 	 * endpoint polling interval. For Isochronous IN endpoints, it is
19177c478bd9Sstevel@tonic-gate 	 * always fixed since its polling interval will be one ms.
19187c478bd9Sstevel@tonic-gate 	 */
19197c478bd9Sstevel@tonic-gate 	if (pp->pp_max_periodic_req_cnt == 0) {
19207c478bd9Sstevel@tonic-gate 
19217c478bd9Sstevel@tonic-gate 		ehci_set_periodic_pipe_polling(ehcip, ph);
19227c478bd9Sstevel@tonic-gate 	}
19237c478bd9Sstevel@tonic-gate 
19247c478bd9Sstevel@tonic-gate 	ASSERT(pp->pp_max_periodic_req_cnt != 0);
19257c478bd9Sstevel@tonic-gate 
19267c478bd9Sstevel@tonic-gate 	switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
19277c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
19287c478bd9Sstevel@tonic-gate 		error = ehci_start_intr_polling(ehcip, ph, flags);
19297c478bd9Sstevel@tonic-gate 		break;
19307c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
19317c478bd9Sstevel@tonic-gate 		error = ehci_start_isoc_polling(ehcip, ph, flags);
19327c478bd9Sstevel@tonic-gate 		break;
19337c478bd9Sstevel@tonic-gate 	}
19347c478bd9Sstevel@tonic-gate 
19357c478bd9Sstevel@tonic-gate 	return (error);
19367c478bd9Sstevel@tonic-gate }
19377c478bd9Sstevel@tonic-gate 
19387c478bd9Sstevel@tonic-gate static int
ehci_start_intr_polling(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_flags_t flags)19397c478bd9Sstevel@tonic-gate ehci_start_intr_polling(
19407c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
19417c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
19427c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
19437c478bd9Sstevel@tonic-gate {
19447c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
19457c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw_list, *tw;
19467c478bd9Sstevel@tonic-gate 	int			i, total_tws;
19477c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
19487c478bd9Sstevel@tonic-gate 
19497c478bd9Sstevel@tonic-gate 	/* Allocate all the necessary resources for the IN transfer */
19507c478bd9Sstevel@tonic-gate 	tw_list = NULL;
19517c478bd9Sstevel@tonic-gate 	total_tws = pp->pp_max_periodic_req_cnt - pp->pp_cur_periodic_req_cnt;
19527c478bd9Sstevel@tonic-gate 	for (i = 0; i < total_tws; i += 1) {
19537c478bd9Sstevel@tonic-gate 		tw = ehci_allocate_intr_resources(ehcip, ph, NULL, flags);
19547c478bd9Sstevel@tonic-gate 		if (tw == NULL) {
19557c478bd9Sstevel@tonic-gate 			error = USB_NO_RESOURCES;
19567c478bd9Sstevel@tonic-gate 			/* There are not enough resources, deallocate the TWs */
19577c478bd9Sstevel@tonic-gate 			tw = tw_list;
19587c478bd9Sstevel@tonic-gate 			while (tw != NULL) {
19597c478bd9Sstevel@tonic-gate 				tw_list = tw->tw_next;
19607c478bd9Sstevel@tonic-gate 				ehci_deallocate_intr_in_resource(
19616a9de478Ssl 				    ehcip, pp, tw);
19627c478bd9Sstevel@tonic-gate 				ehci_deallocate_tw(ehcip, pp, tw);
19637c478bd9Sstevel@tonic-gate 				tw = tw_list;
19647c478bd9Sstevel@tonic-gate 			}
19657c478bd9Sstevel@tonic-gate 
19667c478bd9Sstevel@tonic-gate 			return (error);
19677c478bd9Sstevel@tonic-gate 		} else {
19687c478bd9Sstevel@tonic-gate 			if (tw_list == NULL) {
19697c478bd9Sstevel@tonic-gate 				tw_list = tw;
19707c478bd9Sstevel@tonic-gate 			}
19717c478bd9Sstevel@tonic-gate 		}
19727c478bd9Sstevel@tonic-gate 	}
19737c478bd9Sstevel@tonic-gate 
19747c478bd9Sstevel@tonic-gate 	while (pp->pp_cur_periodic_req_cnt < pp->pp_max_periodic_req_cnt) {
19757c478bd9Sstevel@tonic-gate 
19767c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
19777c478bd9Sstevel@tonic-gate 		    "ehci_start_pipe_polling: max = %d curr = %d tw = %p:",
19787c478bd9Sstevel@tonic-gate 		    pp->pp_max_periodic_req_cnt, pp->pp_cur_periodic_req_cnt,
1979112116d8Sfb 		    (void *)tw_list);
19807c478bd9Sstevel@tonic-gate 
19817c478bd9Sstevel@tonic-gate 		tw = tw_list;
19827c478bd9Sstevel@tonic-gate 		tw_list = tw->tw_next;
19837c478bd9Sstevel@tonic-gate 
19847c478bd9Sstevel@tonic-gate 		ehci_insert_intr_req(ehcip, pp, tw, flags);
19857c478bd9Sstevel@tonic-gate 
19867c478bd9Sstevel@tonic-gate 		pp->pp_cur_periodic_req_cnt++;
19877c478bd9Sstevel@tonic-gate 	}
19887c478bd9Sstevel@tonic-gate 
19897c478bd9Sstevel@tonic-gate 	return (error);
19907c478bd9Sstevel@tonic-gate }
19917c478bd9Sstevel@tonic-gate 
19927c478bd9Sstevel@tonic-gate 
19937c478bd9Sstevel@tonic-gate /*
19947c478bd9Sstevel@tonic-gate  * ehci_set_periodic_pipe_polling:
19957c478bd9Sstevel@tonic-gate  *
19967c478bd9Sstevel@tonic-gate  * Calculate the number of periodic requests needed corresponding to the
19977c478bd9Sstevel@tonic-gate  * interrupt IN endpoints polling interval. Table below gives the number
19987c478bd9Sstevel@tonic-gate  * of periodic requests needed for the interrupt IN endpoints  according
19997c478bd9Sstevel@tonic-gate  * to endpoint polling interval.
20007c478bd9Sstevel@tonic-gate  *
20017c478bd9Sstevel@tonic-gate  * Polling interval		Number of periodic requests
20027c478bd9Sstevel@tonic-gate  *
20037c478bd9Sstevel@tonic-gate  * 1ms				4
20047c478bd9Sstevel@tonic-gate  * 2ms				2
20057c478bd9Sstevel@tonic-gate  * 4ms to 32ms			1
20067c478bd9Sstevel@tonic-gate  */
20077c478bd9Sstevel@tonic-gate static void
ehci_set_periodic_pipe_polling(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)20087c478bd9Sstevel@tonic-gate ehci_set_periodic_pipe_polling(
20097c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
20107c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
20117c478bd9Sstevel@tonic-gate {
20127c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
20137c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*endpoint = &ph->p_ep;
20147c478bd9Sstevel@tonic-gate 	uchar_t			ep_attr = endpoint->bmAttributes;
20157c478bd9Sstevel@tonic-gate 	uint_t			interval;
20167c478bd9Sstevel@tonic-gate 
20177c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
20187c478bd9Sstevel@tonic-gate 	    "ehci_set_periodic_pipe_polling:");
20197c478bd9Sstevel@tonic-gate 
20207c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
20217c478bd9Sstevel@tonic-gate 
20227c478bd9Sstevel@tonic-gate 	pp->pp_cur_periodic_req_cnt = 0;
20237c478bd9Sstevel@tonic-gate 
20247c478bd9Sstevel@tonic-gate 	/*
20257c478bd9Sstevel@tonic-gate 	 * Check usb flag whether USB_FLAGS_ONE_TIME_POLL flag is
20267c478bd9Sstevel@tonic-gate 	 * set and if so, set pp->pp_max_periodic_req_cnt to one.
20277c478bd9Sstevel@tonic-gate 	 */
20287c478bd9Sstevel@tonic-gate 	if (((ep_attr & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR) &&
20297c478bd9Sstevel@tonic-gate 	    (pp->pp_client_periodic_in_reqp)) {
20307c478bd9Sstevel@tonic-gate 		usb_intr_req_t *intr_reqp = (usb_intr_req_t *)
20316a9de478Ssl 		    pp->pp_client_periodic_in_reqp;
20327c478bd9Sstevel@tonic-gate 
20337c478bd9Sstevel@tonic-gate 		if (intr_reqp->intr_attributes &
20347c478bd9Sstevel@tonic-gate 		    USB_ATTRS_ONE_XFER) {
20357c478bd9Sstevel@tonic-gate 
20367c478bd9Sstevel@tonic-gate 			pp->pp_max_periodic_req_cnt = EHCI_INTR_XMS_REQS;
20377c478bd9Sstevel@tonic-gate 
20387c478bd9Sstevel@tonic-gate 			return;
20397c478bd9Sstevel@tonic-gate 		}
20407c478bd9Sstevel@tonic-gate 	}
20417c478bd9Sstevel@tonic-gate 
20427c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_usba_device->usb_mutex);
20437c478bd9Sstevel@tonic-gate 
20447c478bd9Sstevel@tonic-gate 	/*
20457c478bd9Sstevel@tonic-gate 	 * The ehci_adjust_polling_interval function will not fail
20467c478bd9Sstevel@tonic-gate 	 * at this instance since bandwidth allocation is already
20477c478bd9Sstevel@tonic-gate 	 * done. Here we are getting only the periodic interval.
20487c478bd9Sstevel@tonic-gate 	 */
20497c478bd9Sstevel@tonic-gate 	interval = ehci_adjust_polling_interval(ehcip, endpoint,
20506a9de478Ssl 	    ph->p_usba_device->usb_port_status);
20517c478bd9Sstevel@tonic-gate 
20527c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_usba_device->usb_mutex);
20537c478bd9Sstevel@tonic-gate 
20547c478bd9Sstevel@tonic-gate 	switch (interval) {
20557c478bd9Sstevel@tonic-gate 	case EHCI_INTR_1MS_POLL:
20567c478bd9Sstevel@tonic-gate 		pp->pp_max_periodic_req_cnt = EHCI_INTR_1MS_REQS;
20577c478bd9Sstevel@tonic-gate 		break;
20587c478bd9Sstevel@tonic-gate 	case EHCI_INTR_2MS_POLL:
20597c478bd9Sstevel@tonic-gate 		pp->pp_max_periodic_req_cnt = EHCI_INTR_2MS_REQS;
20607c478bd9Sstevel@tonic-gate 		break;
20617c478bd9Sstevel@tonic-gate 	default:
20627c478bd9Sstevel@tonic-gate 		pp->pp_max_periodic_req_cnt = EHCI_INTR_XMS_REQS;
20637c478bd9Sstevel@tonic-gate 		break;
20647c478bd9Sstevel@tonic-gate 	}
20657c478bd9Sstevel@tonic-gate 
20667c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
20677c478bd9Sstevel@tonic-gate 	    "ehci_set_periodic_pipe_polling: Max periodic requests = %d",
20687c478bd9Sstevel@tonic-gate 	    pp->pp_max_periodic_req_cnt);
20697c478bd9Sstevel@tonic-gate }
20707c478bd9Sstevel@tonic-gate 
20717c478bd9Sstevel@tonic-gate /*
20727c478bd9Sstevel@tonic-gate  * ehci_allocate_intr_resources:
20737c478bd9Sstevel@tonic-gate  *
20747c478bd9Sstevel@tonic-gate  * Calculates the number of tds necessary for a intr transfer, and allocates
20757c478bd9Sstevel@tonic-gate  * all the necessary resources.
20767c478bd9Sstevel@tonic-gate  *
20777c478bd9Sstevel@tonic-gate  * Returns NULL if there is insufficient resources otherwise TW.
20787c478bd9Sstevel@tonic-gate  */
20797c478bd9Sstevel@tonic-gate ehci_trans_wrapper_t *
ehci_allocate_intr_resources(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_intr_req_t * intr_reqp,usb_flags_t flags)20807c478bd9Sstevel@tonic-gate ehci_allocate_intr_resources(
20817c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
20827c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
20837c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*intr_reqp,
20847c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
20857c478bd9Sstevel@tonic-gate {
20867c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
20877c478bd9Sstevel@tonic-gate 	int			pipe_dir;
20887c478bd9Sstevel@tonic-gate 	size_t			qtd_count = 1;
20897c478bd9Sstevel@tonic-gate 	size_t			tw_length;
20907c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw;
20917c478bd9Sstevel@tonic-gate 
20927c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
20937c478bd9Sstevel@tonic-gate 	    "ehci_allocate_intr_resources:");
20947c478bd9Sstevel@tonic-gate 
20957c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
20967c478bd9Sstevel@tonic-gate 
20977c478bd9Sstevel@tonic-gate 	pipe_dir = ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK;
20987c478bd9Sstevel@tonic-gate 
20997c478bd9Sstevel@tonic-gate 	/* Get the length of interrupt transfer & alloc data */
21007c478bd9Sstevel@tonic-gate 	if (intr_reqp) {
21017c478bd9Sstevel@tonic-gate 		tw_length = intr_reqp->intr_len;
21027c478bd9Sstevel@tonic-gate 	} else {
21037c478bd9Sstevel@tonic-gate 		ASSERT(pipe_dir == USB_EP_DIR_IN);
21047c478bd9Sstevel@tonic-gate 		tw_length = (pp->pp_client_periodic_in_reqp) ?
21057c478bd9Sstevel@tonic-gate 		    (((usb_intr_req_t *)pp->
21067c478bd9Sstevel@tonic-gate 		    pp_client_periodic_in_reqp)->intr_len) :
21077c478bd9Sstevel@tonic-gate 		    ph->p_ep.wMaxPacketSize;
21087c478bd9Sstevel@tonic-gate 	}
21097c478bd9Sstevel@tonic-gate 
21107c478bd9Sstevel@tonic-gate 	/* Check the size of interrupt request */
21117c478bd9Sstevel@tonic-gate 	if (tw_length > EHCI_MAX_QTD_XFER_SIZE) {
21127c478bd9Sstevel@tonic-gate 
21137c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
21147c478bd9Sstevel@tonic-gate 		    "ehci_allocate_intr_resources: Intr request size 0x%lx is "
21157c478bd9Sstevel@tonic-gate 		    "more than 0x%x", tw_length, EHCI_MAX_QTD_XFER_SIZE);
21167c478bd9Sstevel@tonic-gate 
21177c478bd9Sstevel@tonic-gate 		return (NULL);
21187c478bd9Sstevel@tonic-gate 	}
21197c478bd9Sstevel@tonic-gate 
21207c478bd9Sstevel@tonic-gate 	if ((tw = ehci_allocate_tw_resources(ehcip, pp, tw_length, flags,
21217c478bd9Sstevel@tonic-gate 	    qtd_count)) == NULL) {
21227c478bd9Sstevel@tonic-gate 
21237c478bd9Sstevel@tonic-gate 		return (NULL);
21247c478bd9Sstevel@tonic-gate 	}
21257c478bd9Sstevel@tonic-gate 
21267c478bd9Sstevel@tonic-gate 	if (pipe_dir == USB_EP_DIR_IN) {
21277c478bd9Sstevel@tonic-gate 		if (ehci_allocate_intr_in_resource(ehcip, pp, tw, flags) !=
21287c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
21297c478bd9Sstevel@tonic-gate 			ehci_deallocate_tw(ehcip, pp, tw);
21307c478bd9Sstevel@tonic-gate 		}
21317c478bd9Sstevel@tonic-gate 		tw->tw_direction = EHCI_QTD_CTRL_IN_PID;
21327c478bd9Sstevel@tonic-gate 	} else {
2133688b07c5Sgc 		if (tw_length) {
2134688b07c5Sgc 			ASSERT(intr_reqp->intr_data != NULL);
21357c478bd9Sstevel@tonic-gate 
2136688b07c5Sgc 			/* Copy the data into the buffer */
2137688b07c5Sgc 			bcopy(intr_reqp->intr_data->b_rptr, tw->tw_buf,
2138688b07c5Sgc 			    intr_reqp->intr_len);
21397c478bd9Sstevel@tonic-gate 
2140688b07c5Sgc 			Sync_IO_Buffer_for_device(tw->tw_dmahandle,
2141688b07c5Sgc 			    intr_reqp->intr_len);
2142688b07c5Sgc 		}
21437c478bd9Sstevel@tonic-gate 
21447c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp = (usb_opaque_t)intr_reqp;
21457c478bd9Sstevel@tonic-gate 		tw->tw_direction = EHCI_QTD_CTRL_OUT_PID;
21467c478bd9Sstevel@tonic-gate 	}
21477c478bd9Sstevel@tonic-gate 
21487c478bd9Sstevel@tonic-gate 	if (intr_reqp) {
21497c478bd9Sstevel@tonic-gate 		tw->tw_timeout = intr_reqp->intr_timeout;
21507c478bd9Sstevel@tonic-gate 	}
21517c478bd9Sstevel@tonic-gate 
21527c478bd9Sstevel@tonic-gate 	/*
21537c478bd9Sstevel@tonic-gate 	 * Initialize the callback and any callback
21547c478bd9Sstevel@tonic-gate 	 * data required when the qtd completes.
21557c478bd9Sstevel@tonic-gate 	 */
21567c478bd9Sstevel@tonic-gate 	tw->tw_handle_qtd = ehci_handle_intr_qtd;
21577c478bd9Sstevel@tonic-gate 	tw->tw_handle_callback_value = NULL;
21587c478bd9Sstevel@tonic-gate 
21597c478bd9Sstevel@tonic-gate 	return (tw);
21607c478bd9Sstevel@tonic-gate }
21617c478bd9Sstevel@tonic-gate 
21627c478bd9Sstevel@tonic-gate 
21637c478bd9Sstevel@tonic-gate /*
21647c478bd9Sstevel@tonic-gate  * ehci_insert_intr_req:
21657c478bd9Sstevel@tonic-gate  *
21667c478bd9Sstevel@tonic-gate  * Insert an Interrupt request into the Host Controller's periodic list.
21677c478bd9Sstevel@tonic-gate  */
21687c478bd9Sstevel@tonic-gate /* ARGSUSED */
21697c478bd9Sstevel@tonic-gate void
ehci_insert_intr_req(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw,usb_flags_t flags)21707c478bd9Sstevel@tonic-gate ehci_insert_intr_req(
21717c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
21727c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
21737c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw,
21747c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
21757c478bd9Sstevel@tonic-gate {
21767c478bd9Sstevel@tonic-gate 	uint_t			ctrl = 0;
21777c478bd9Sstevel@tonic-gate 
21787c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
21797c478bd9Sstevel@tonic-gate 
21807c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_curr_xfer_reqp != NULL);
21817c478bd9Sstevel@tonic-gate 
21827c478bd9Sstevel@tonic-gate 	ctrl = (tw->tw_direction | EHCI_QTD_CTRL_INTR_ON_COMPLETE);
21837c478bd9Sstevel@tonic-gate 
21847c478bd9Sstevel@tonic-gate 	/* Insert another interrupt QTD */
21853304303fSsl 	(void) ehci_insert_qtd(ehcip, ctrl, 0, tw->tw_length, 0, pp, tw);
21867c478bd9Sstevel@tonic-gate 
21877c478bd9Sstevel@tonic-gate 	/* Start the timer for this Interrupt transfer */
21887c478bd9Sstevel@tonic-gate 	ehci_start_xfer_timer(ehcip, pp, tw);
21897c478bd9Sstevel@tonic-gate }
21907c478bd9Sstevel@tonic-gate 
21917c478bd9Sstevel@tonic-gate 
21927c478bd9Sstevel@tonic-gate /*
21937c478bd9Sstevel@tonic-gate  * ehci_stop_periodic_pipe_polling:
21947c478bd9Sstevel@tonic-gate  */
21957c478bd9Sstevel@tonic-gate /* ARGSUSED */
21967c478bd9Sstevel@tonic-gate int
ehci_stop_periodic_pipe_polling(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph,usb_flags_t flags)21977c478bd9Sstevel@tonic-gate ehci_stop_periodic_pipe_polling(
21987c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
21997c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
22007c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
22017c478bd9Sstevel@tonic-gate {
22027c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
22037c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
22047c478bd9Sstevel@tonic-gate 
22057c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
22067c478bd9Sstevel@tonic-gate 	    "ehci_stop_periodic_pipe_polling: Flags = 0x%x", flags);
22077c478bd9Sstevel@tonic-gate 
22087c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
22097c478bd9Sstevel@tonic-gate 
22107c478bd9Sstevel@tonic-gate 	/*
22117c478bd9Sstevel@tonic-gate 	 * Check and handle stop polling on root hub interrupt pipe.
22127c478bd9Sstevel@tonic-gate 	 */
22137c478bd9Sstevel@tonic-gate 	if ((ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) &&
22147c478bd9Sstevel@tonic-gate 	    ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
22157c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_INTR)) {
22167c478bd9Sstevel@tonic-gate 
22177c478bd9Sstevel@tonic-gate 		ehci_handle_root_hub_pipe_stop_intr_polling(ph, flags);
22187c478bd9Sstevel@tonic-gate 
22197c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
22207c478bd9Sstevel@tonic-gate 	}
22217c478bd9Sstevel@tonic-gate 
22227c478bd9Sstevel@tonic-gate 	if (pp->pp_state != EHCI_PIPE_STATE_ACTIVE) {
22237c478bd9Sstevel@tonic-gate 
22247c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
22257c478bd9Sstevel@tonic-gate 		    "ehci_stop_periodic_pipe_polling: "
22267c478bd9Sstevel@tonic-gate 		    "Polling already stopped");
22277c478bd9Sstevel@tonic-gate 
22287c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
22297c478bd9Sstevel@tonic-gate 	}
22307c478bd9Sstevel@tonic-gate 
22317c478bd9Sstevel@tonic-gate 	/* Set pipe state to pipe stop polling */
22327c478bd9Sstevel@tonic-gate 	pp->pp_state = EHCI_PIPE_STATE_STOP_POLLING;
22337c478bd9Sstevel@tonic-gate 
22347c478bd9Sstevel@tonic-gate 	ehci_pipe_cleanup(ehcip, ph);
22357c478bd9Sstevel@tonic-gate 
22367c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
22377c478bd9Sstevel@tonic-gate }
22387c478bd9Sstevel@tonic-gate 
22397c478bd9Sstevel@tonic-gate 
22407c478bd9Sstevel@tonic-gate /*
22417c478bd9Sstevel@tonic-gate  * ehci_insert_qtd:
22427c478bd9Sstevel@tonic-gate  *
22437c478bd9Sstevel@tonic-gate  * Insert a Transfer Descriptor (QTD) on an Endpoint Descriptor (QH).
22447c478bd9Sstevel@tonic-gate  * Always returns USB_SUCCESS for now.	Once Isoch has been implemented,
22457c478bd9Sstevel@tonic-gate  * it may return USB_FAILURE.
22467c478bd9Sstevel@tonic-gate  */
22477c478bd9Sstevel@tonic-gate int
ehci_insert_qtd(ehci_state_t * ehcip,uint32_t qtd_ctrl,size_t qtd_dma_offs,size_t qtd_length,uint32_t qtd_ctrl_phase,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw)22487c478bd9Sstevel@tonic-gate ehci_insert_qtd(
22497c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
22507c478bd9Sstevel@tonic-gate 	uint32_t		qtd_ctrl,
22513304303fSsl 	size_t			qtd_dma_offs,
22527c478bd9Sstevel@tonic-gate 	size_t			qtd_length,
22537c478bd9Sstevel@tonic-gate 	uint32_t		qtd_ctrl_phase,
22547c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
22557c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw)
22567c478bd9Sstevel@tonic-gate {
22577c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*curr_dummy_qtd, *next_dummy_qtd;
22587c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*new_dummy_qtd;
22597c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh = pp->pp_qh;
22607c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
22617c478bd9Sstevel@tonic-gate 
22627c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
22637c478bd9Sstevel@tonic-gate 
22647c478bd9Sstevel@tonic-gate 	/* Allocate new dummy QTD */
22657c478bd9Sstevel@tonic-gate 	new_dummy_qtd = tw->tw_qtd_free_list;
22667c478bd9Sstevel@tonic-gate 
22677c478bd9Sstevel@tonic-gate 	ASSERT(new_dummy_qtd != NULL);
22687c478bd9Sstevel@tonic-gate 	tw->tw_qtd_free_list = ehci_qtd_iommu_to_cpu(ehcip,
22697c478bd9Sstevel@tonic-gate 	    Get_QTD(new_dummy_qtd->qtd_tw_next_qtd));
22706aef9e11SToomas Soome 	Set_QTD(new_dummy_qtd->qtd_tw_next_qtd, 0);
22717c478bd9Sstevel@tonic-gate 
22727c478bd9Sstevel@tonic-gate 	/* Get the current and next dummy QTDs */
22737c478bd9Sstevel@tonic-gate 	curr_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
22747c478bd9Sstevel@tonic-gate 	    Get_QH(qh->qh_dummy_qtd));
22757c478bd9Sstevel@tonic-gate 	next_dummy_qtd = ehci_qtd_iommu_to_cpu(ehcip,
22767c478bd9Sstevel@tonic-gate 	    Get_QTD(curr_dummy_qtd->qtd_next_qtd));
22777c478bd9Sstevel@tonic-gate 
22787c478bd9Sstevel@tonic-gate 	/* Update QH's dummy qtd field */
22797c478bd9Sstevel@tonic-gate 	Set_QH(qh->qh_dummy_qtd, ehci_qtd_cpu_to_iommu(ehcip, next_dummy_qtd));
22807c478bd9Sstevel@tonic-gate 
22817c478bd9Sstevel@tonic-gate 	/* Update next dummy's next qtd pointer */
22827c478bd9Sstevel@tonic-gate 	Set_QTD(next_dummy_qtd->qtd_next_qtd,
22837c478bd9Sstevel@tonic-gate 	    ehci_qtd_cpu_to_iommu(ehcip, new_dummy_qtd));
22847c478bd9Sstevel@tonic-gate 
22857c478bd9Sstevel@tonic-gate 	/*
22867c478bd9Sstevel@tonic-gate 	 * Fill in the current dummy qtd and
22877c478bd9Sstevel@tonic-gate 	 * add the new dummy to the end.
22887c478bd9Sstevel@tonic-gate 	 */
22897c478bd9Sstevel@tonic-gate 	ehci_fill_in_qtd(ehcip, curr_dummy_qtd, qtd_ctrl,
22903304303fSsl 	    qtd_dma_offs, qtd_length, qtd_ctrl_phase, pp, tw);
22917c478bd9Sstevel@tonic-gate 
22927c478bd9Sstevel@tonic-gate 	/* Insert this qtd onto the tw */
22937c478bd9Sstevel@tonic-gate 	ehci_insert_qtd_on_tw(ehcip, tw, curr_dummy_qtd);
22947c478bd9Sstevel@tonic-gate 
22957c478bd9Sstevel@tonic-gate 	/*
22967c478bd9Sstevel@tonic-gate 	 * Insert this qtd onto active qtd list.
22977c478bd9Sstevel@tonic-gate 	 * Don't insert polled mode qtd here.
22987c478bd9Sstevel@tonic-gate 	 */
22997c478bd9Sstevel@tonic-gate 	if (pp->pp_flag != EHCI_POLLED_MODE_FLAG) {
23007c478bd9Sstevel@tonic-gate 		/* Insert this qtd onto active qtd list */
23017c478bd9Sstevel@tonic-gate 		ehci_insert_qtd_into_active_qtd_list(ehcip, curr_dummy_qtd);
23027c478bd9Sstevel@tonic-gate 	}
23037c478bd9Sstevel@tonic-gate 
23047c478bd9Sstevel@tonic-gate 	/* Print qh and qtd */
23057c478bd9Sstevel@tonic-gate 	ehci_print_qh(ehcip, qh);
23067c478bd9Sstevel@tonic-gate 	ehci_print_qtd(ehcip, curr_dummy_qtd);
23077c478bd9Sstevel@tonic-gate 
23087c478bd9Sstevel@tonic-gate 	return (error);
23097c478bd9Sstevel@tonic-gate }
23107c478bd9Sstevel@tonic-gate 
23117c478bd9Sstevel@tonic-gate 
23127c478bd9Sstevel@tonic-gate /*
23137c478bd9Sstevel@tonic-gate  * ehci_allocate_qtd_from_pool:
23147c478bd9Sstevel@tonic-gate  *
23157c478bd9Sstevel@tonic-gate  * Allocate a Transfer Descriptor (QTD) from the QTD buffer pool.
23167c478bd9Sstevel@tonic-gate  */
23177c478bd9Sstevel@tonic-gate static ehci_qtd_t *
ehci_allocate_qtd_from_pool(ehci_state_t * ehcip)23187c478bd9Sstevel@tonic-gate ehci_allocate_qtd_from_pool(ehci_state_t	*ehcip)
23197c478bd9Sstevel@tonic-gate {
23207c478bd9Sstevel@tonic-gate 	int		i, ctrl;
23217c478bd9Sstevel@tonic-gate 	ehci_qtd_t	*qtd;
23227c478bd9Sstevel@tonic-gate 
23237c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
23247c478bd9Sstevel@tonic-gate 
23257c478bd9Sstevel@tonic-gate 	/*
23267c478bd9Sstevel@tonic-gate 	 * Search for a blank Transfer Descriptor (QTD)
23277c478bd9Sstevel@tonic-gate 	 * in the QTD buffer pool.
23287c478bd9Sstevel@tonic-gate 	 */
23297c478bd9Sstevel@tonic-gate 	for (i = 0; i < ehci_qtd_pool_size; i ++) {
23307c478bd9Sstevel@tonic-gate 		ctrl = Get_QTD(ehcip->ehci_qtd_pool_addr[i].qtd_state);
23317c478bd9Sstevel@tonic-gate 		if (ctrl == EHCI_QTD_FREE) {
23327c478bd9Sstevel@tonic-gate 			break;
23337c478bd9Sstevel@tonic-gate 		}
23347c478bd9Sstevel@tonic-gate 	}
23357c478bd9Sstevel@tonic-gate 
23367c478bd9Sstevel@tonic-gate 	if (i >= ehci_qtd_pool_size) {
23377c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
23387c478bd9Sstevel@tonic-gate 		    "ehci_allocate_qtd_from_pool: QTD exhausted");
23397c478bd9Sstevel@tonic-gate 
23407c478bd9Sstevel@tonic-gate 		return (NULL);
23417c478bd9Sstevel@tonic-gate 	}
23427c478bd9Sstevel@tonic-gate 
23437c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
23447c478bd9Sstevel@tonic-gate 	    "ehci_allocate_qtd_from_pool: Allocated %d", i);
23457c478bd9Sstevel@tonic-gate 
23467c478bd9Sstevel@tonic-gate 	/* Create a new dummy for the end of the QTD list */
23477c478bd9Sstevel@tonic-gate 	qtd = &ehcip->ehci_qtd_pool_addr[i];
23487c478bd9Sstevel@tonic-gate 
23497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
23507c478bd9Sstevel@tonic-gate 	    "ehci_allocate_qtd_from_pool: qtd 0x%p", (void *)qtd);
23517c478bd9Sstevel@tonic-gate 
23527c478bd9Sstevel@tonic-gate 	/* Mark the newly allocated QTD as a dummy */
23537c478bd9Sstevel@tonic-gate 	Set_QTD(qtd->qtd_state, EHCI_QTD_DUMMY);
23547c478bd9Sstevel@tonic-gate 
23557c478bd9Sstevel@tonic-gate 	/* Mark the status of this new QTD to halted state */
23567c478bd9Sstevel@tonic-gate 	Set_QTD(qtd->qtd_ctrl, EHCI_QTD_CTRL_HALTED_XACT);
23577c478bd9Sstevel@tonic-gate 
23587c478bd9Sstevel@tonic-gate 	/* Disable dummy QTD's next and alternate next pointers */
23597c478bd9Sstevel@tonic-gate 	Set_QTD(qtd->qtd_next_qtd, EHCI_QTD_NEXT_QTD_PTR_VALID);
23607c478bd9Sstevel@tonic-gate 	Set_QTD(qtd->qtd_alt_next_qtd, EHCI_QTD_ALT_NEXT_QTD_PTR_VALID);
23617c478bd9Sstevel@tonic-gate 
23627c478bd9Sstevel@tonic-gate 	return (qtd);
23637c478bd9Sstevel@tonic-gate }
23647c478bd9Sstevel@tonic-gate 
23657c478bd9Sstevel@tonic-gate 
23667c478bd9Sstevel@tonic-gate /*
23677c478bd9Sstevel@tonic-gate  * ehci_fill_in_qtd:
23687c478bd9Sstevel@tonic-gate  *
23697c478bd9Sstevel@tonic-gate  * Fill in the fields of a Transfer Descriptor (QTD).
23703304303fSsl  * The "Buffer Pointer" fields of a QTD are retrieved from the TW
23713304303fSsl  * it is associated with.
23723304303fSsl  *
23733304303fSsl  * Note:
23743304303fSsl  * qtd_dma_offs - the starting offset into the TW buffer, where the QTD
2375112116d8Sfb  *		  should transfer from. It should be 4K aligned. And when
2376112116d8Sfb  *		  a TW has more than one QTDs, the QTDs must be filled in
2377112116d8Sfb  *		  increasing order.
23783304303fSsl  * qtd_length - the total bytes to transfer.
23797c478bd9Sstevel@tonic-gate  */
23807c478bd9Sstevel@tonic-gate /*ARGSUSED*/
23817c478bd9Sstevel@tonic-gate static void
ehci_fill_in_qtd(ehci_state_t * ehcip,ehci_qtd_t * qtd,uint32_t qtd_ctrl,size_t qtd_dma_offs,size_t qtd_length,uint32_t qtd_ctrl_phase,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw)23827c478bd9Sstevel@tonic-gate ehci_fill_in_qtd(
23837c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
23847c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*qtd,
23857c478bd9Sstevel@tonic-gate 	uint32_t		qtd_ctrl,
23863304303fSsl 	size_t			qtd_dma_offs,
23877c478bd9Sstevel@tonic-gate 	size_t			qtd_length,
23887c478bd9Sstevel@tonic-gate 	uint32_t		qtd_ctrl_phase,
23897c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
23907c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw)
23917c478bd9Sstevel@tonic-gate {
23923304303fSsl 	uint32_t		buf_addr;
23937c478bd9Sstevel@tonic-gate 	size_t			buf_len = qtd_length;
23947c478bd9Sstevel@tonic-gate 	uint32_t		ctrl = qtd_ctrl;
23957c478bd9Sstevel@tonic-gate 	uint_t			i = 0;
23963304303fSsl 	int			rem_len;
23977c478bd9Sstevel@tonic-gate 
23987c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
23993304303fSsl 	    "ehci_fill_in_qtd: qtd 0x%p ctrl 0x%x bufoffs 0x%lx "
2400112116d8Sfb 	    "len 0x%lx", (void *)qtd, qtd_ctrl, qtd_dma_offs, qtd_length);
24017c478bd9Sstevel@tonic-gate 
24027c478bd9Sstevel@tonic-gate 	/* Assert that the qtd to be filled in is a dummy */
24037c478bd9Sstevel@tonic-gate 	ASSERT(Get_QTD(qtd->qtd_state) == EHCI_QTD_DUMMY);
24047c478bd9Sstevel@tonic-gate 
24057c478bd9Sstevel@tonic-gate 	/* Change QTD's state Active */
24067c478bd9Sstevel@tonic-gate 	Set_QTD(qtd->qtd_state, EHCI_QTD_ACTIVE);
24077c478bd9Sstevel@tonic-gate 
24087c478bd9Sstevel@tonic-gate 	/* Set the total length data transfer */
24097c478bd9Sstevel@tonic-gate 	ctrl |= (((qtd_length << EHCI_QTD_CTRL_BYTES_TO_XFER_SHIFT)
24107c478bd9Sstevel@tonic-gate 	    & EHCI_QTD_CTRL_BYTES_TO_XFER) | EHCI_QTD_CTRL_MAX_ERR_COUNTS);
24117c478bd9Sstevel@tonic-gate 
24127c478bd9Sstevel@tonic-gate 	/*
24133304303fSsl 	 * QTDs must be filled in increasing DMA offset order.
24143304303fSsl 	 * tw_dma_offs is initialized to be 0 at TW creation and
24153304303fSsl 	 * is only increased in this function.
24163304303fSsl 	 */
24173304303fSsl 	ASSERT(buf_len == 0 || qtd_dma_offs >= tw->tw_dma_offs);
24183304303fSsl 
24193304303fSsl 	/*
24203304303fSsl 	 * Save the starting dma buffer offset used and
24217c478bd9Sstevel@tonic-gate 	 * length of data that will be transfered in
24227c478bd9Sstevel@tonic-gate 	 * the current QTD.
24237c478bd9Sstevel@tonic-gate 	 */
24243304303fSsl 	Set_QTD(qtd->qtd_xfer_offs, qtd_dma_offs);
24257c478bd9Sstevel@tonic-gate 	Set_QTD(qtd->qtd_xfer_len, buf_len);
24267c478bd9Sstevel@tonic-gate 
24277c478bd9Sstevel@tonic-gate 	while (buf_len) {
24283304303fSsl 		/*
24293304303fSsl 		 * Advance to the next DMA cookie until finding the cookie
24303304303fSsl 		 * that qtd_dma_offs falls in.
24313304303fSsl 		 * It is very likely this loop will never repeat more than
24323304303fSsl 		 * once. It is here just to accommodate the case qtd_dma_offs
24333304303fSsl 		 * is increased by multiple cookies during two consecutive
24343304303fSsl 		 * calls into this function. In that case, the interim DMA
24353304303fSsl 		 * buffer is allowed to be skipped.
24363304303fSsl 		 */
24373304303fSsl 		while ((tw->tw_dma_offs + tw->tw_cookie.dmac_size) <=
24383304303fSsl 		    qtd_dma_offs) {
24393304303fSsl 			/*
24403304303fSsl 			 * tw_dma_offs always points to the starting offset
24413304303fSsl 			 * of a cookie
24423304303fSsl 			 */
24433304303fSsl 			tw->tw_dma_offs += tw->tw_cookie.dmac_size;
24443304303fSsl 			ddi_dma_nextcookie(tw->tw_dmahandle, &tw->tw_cookie);
24453304303fSsl 			tw->tw_cookie_idx++;
24463304303fSsl 			ASSERT(tw->tw_cookie_idx < tw->tw_ncookies);
24473304303fSsl 		}
24483304303fSsl 
24493304303fSsl 		/*
24503304303fSsl 		 * Counting the remained buffer length to be filled in
24513304303fSsl 		 * the QTD for current DMA cookie
24523304303fSsl 		 */
24533304303fSsl 		rem_len = (tw->tw_dma_offs + tw->tw_cookie.dmac_size) -
24543304303fSsl 		    qtd_dma_offs;
24553304303fSsl 
24567c478bd9Sstevel@tonic-gate 		/* Update the beginning of the buffer */
24573304303fSsl 		buf_addr = (qtd_dma_offs - tw->tw_dma_offs) +
24583304303fSsl 		    tw->tw_cookie.dmac_address;
24593304303fSsl 		ASSERT((buf_addr % EHCI_4K_ALIGN) == 0);
24607c478bd9Sstevel@tonic-gate 		Set_QTD(qtd->qtd_buf[i], buf_addr);
24617c478bd9Sstevel@tonic-gate 
24623304303fSsl 		USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
2463112116d8Sfb 		    "ehci_fill_in_qtd: dmac_addr 0x%x dmac_size "
24643304303fSsl 		    "0x%lx idx %d", buf_addr, tw->tw_cookie.dmac_size,
24653304303fSsl 		    tw->tw_cookie_idx);
24663304303fSsl 
24677c478bd9Sstevel@tonic-gate 		if (buf_len <= EHCI_MAX_QTD_BUF_SIZE) {
24683304303fSsl 			ASSERT(buf_len <= rem_len);
24697c478bd9Sstevel@tonic-gate 			break;
24707c478bd9Sstevel@tonic-gate 		} else {
24713304303fSsl 			ASSERT(rem_len >= EHCI_MAX_QTD_BUF_SIZE);
24727c478bd9Sstevel@tonic-gate 			buf_len -= EHCI_MAX_QTD_BUF_SIZE;
24733304303fSsl 			qtd_dma_offs += EHCI_MAX_QTD_BUF_SIZE;
24747c478bd9Sstevel@tonic-gate 		}
24757c478bd9Sstevel@tonic-gate 
24767c478bd9Sstevel@tonic-gate 		i++;
24777c478bd9Sstevel@tonic-gate 	}
24787c478bd9Sstevel@tonic-gate 
24797c478bd9Sstevel@tonic-gate 	/*
24807c478bd9Sstevel@tonic-gate 	 * Setup the alternate next qTD pointer if appropriate.  The alternate
24817c478bd9Sstevel@tonic-gate 	 * qtd is currently pointing to a QTD that is not yet linked, but will
24827c478bd9Sstevel@tonic-gate 	 * be in the very near future.	If a short_xfer occurs in this
24837c478bd9Sstevel@tonic-gate 	 * situation , the HC will automatically skip this QH.	Eventually
24847c478bd9Sstevel@tonic-gate 	 * everything will be placed and the alternate_qtd will be valid QTD.
24857c478bd9Sstevel@tonic-gate 	 * For more information on alternate qtds look at section 3.5.2 in the
24867c478bd9Sstevel@tonic-gate 	 * EHCI spec.
24877c478bd9Sstevel@tonic-gate 	 */
24887c478bd9Sstevel@tonic-gate 	if (tw->tw_alt_qtd != NULL) {
24897c478bd9Sstevel@tonic-gate 		Set_QTD(qtd->qtd_alt_next_qtd,
24907c478bd9Sstevel@tonic-gate 		    (ehci_qtd_cpu_to_iommu(ehcip, tw->tw_alt_qtd) &
24917c478bd9Sstevel@tonic-gate 		    EHCI_QTD_ALT_NEXT_QTD_PTR));
24927c478bd9Sstevel@tonic-gate 	}
24937c478bd9Sstevel@tonic-gate 
24947c478bd9Sstevel@tonic-gate 	/*
24957c478bd9Sstevel@tonic-gate 	 * For control, bulk and interrupt QTD, now
24967c478bd9Sstevel@tonic-gate 	 * enable current QTD by setting active bit.
24977c478bd9Sstevel@tonic-gate 	 */
24987c478bd9Sstevel@tonic-gate 	Set_QTD(qtd->qtd_ctrl, (ctrl | EHCI_QTD_CTRL_ACTIVE_XACT));
24997c478bd9Sstevel@tonic-gate 
25007c478bd9Sstevel@tonic-gate 	/*
25017c478bd9Sstevel@tonic-gate 	 * For Control Xfer, qtd_ctrl_phase is a valid filed.
25027c478bd9Sstevel@tonic-gate 	 */
25037c478bd9Sstevel@tonic-gate 	if (qtd_ctrl_phase) {
25047c478bd9Sstevel@tonic-gate 		Set_QTD(qtd->qtd_ctrl_phase, qtd_ctrl_phase);
25057c478bd9Sstevel@tonic-gate 	}
25067c478bd9Sstevel@tonic-gate 
25077c478bd9Sstevel@tonic-gate 	/* Set the transfer wrapper */
25087c478bd9Sstevel@tonic-gate 	ASSERT(tw != NULL);
2509*0a1044f1SToomas Soome 	ASSERT(tw->tw_id != 0);
25107c478bd9Sstevel@tonic-gate 
25117c478bd9Sstevel@tonic-gate 	Set_QTD(qtd->qtd_trans_wrapper, (uint32_t)tw->tw_id);
25127c478bd9Sstevel@tonic-gate }
25137c478bd9Sstevel@tonic-gate 
25147c478bd9Sstevel@tonic-gate 
25157c478bd9Sstevel@tonic-gate /*
25167c478bd9Sstevel@tonic-gate  * ehci_insert_qtd_on_tw:
25177c478bd9Sstevel@tonic-gate  *
25187c478bd9Sstevel@tonic-gate  * The transfer wrapper keeps a list of all Transfer Descriptors (QTD) that
25197c478bd9Sstevel@tonic-gate  * are allocated for this transfer. Insert a QTD  onto this list. The  list
25207c478bd9Sstevel@tonic-gate  * of QTD's does not include the dummy QTD that is at the end of the list of
25217c478bd9Sstevel@tonic-gate  * QTD's for the endpoint.
25227c478bd9Sstevel@tonic-gate  */
25237c478bd9Sstevel@tonic-gate static void
ehci_insert_qtd_on_tw(ehci_state_t * ehcip,ehci_trans_wrapper_t * tw,ehci_qtd_t * qtd)25247c478bd9Sstevel@tonic-gate ehci_insert_qtd_on_tw(
25257c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
25267c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw,
25277c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*qtd)
25287c478bd9Sstevel@tonic-gate {
25297c478bd9Sstevel@tonic-gate 	/*
25307c478bd9Sstevel@tonic-gate 	 * Set the next pointer to NULL because
25317c478bd9Sstevel@tonic-gate 	 * this is the last QTD on list.
25327c478bd9Sstevel@tonic-gate 	 */
25336aef9e11SToomas Soome 	Set_QTD(qtd->qtd_tw_next_qtd, 0);
25347c478bd9Sstevel@tonic-gate 
25357c478bd9Sstevel@tonic-gate 	if (tw->tw_qtd_head == NULL) {
25367c478bd9Sstevel@tonic-gate 		ASSERT(tw->tw_qtd_tail == NULL);
25377c478bd9Sstevel@tonic-gate 		tw->tw_qtd_head = qtd;
25387c478bd9Sstevel@tonic-gate 		tw->tw_qtd_tail = qtd;
25397c478bd9Sstevel@tonic-gate 	} else {
25407c478bd9Sstevel@tonic-gate 		ehci_qtd_t *dummy = (ehci_qtd_t *)tw->tw_qtd_tail;
25417c478bd9Sstevel@tonic-gate 
25427c478bd9Sstevel@tonic-gate 		ASSERT(dummy != NULL);
25437c478bd9Sstevel@tonic-gate 		ASSERT(dummy != qtd);
25447c478bd9Sstevel@tonic-gate 		ASSERT(Get_QTD(qtd->qtd_state) != EHCI_QTD_DUMMY);
25457c478bd9Sstevel@tonic-gate 
25467c478bd9Sstevel@tonic-gate 		/* Add the qtd to the end of the list */
25477c478bd9Sstevel@tonic-gate 		Set_QTD(dummy->qtd_tw_next_qtd,
25487c478bd9Sstevel@tonic-gate 		    ehci_qtd_cpu_to_iommu(ehcip, qtd));
25497c478bd9Sstevel@tonic-gate 
25507c478bd9Sstevel@tonic-gate 		tw->tw_qtd_tail = qtd;
25517c478bd9Sstevel@tonic-gate 
2552*0a1044f1SToomas Soome 		ASSERT(Get_QTD(qtd->qtd_tw_next_qtd) == 0);
25537c478bd9Sstevel@tonic-gate 	}
25547c478bd9Sstevel@tonic-gate }
25557c478bd9Sstevel@tonic-gate 
25567c478bd9Sstevel@tonic-gate 
25577c478bd9Sstevel@tonic-gate /*
25587c478bd9Sstevel@tonic-gate  * ehci_insert_qtd_into_active_qtd_list:
25597c478bd9Sstevel@tonic-gate  *
25607c478bd9Sstevel@tonic-gate  * Insert current QTD into active QTD list.
25617c478bd9Sstevel@tonic-gate  */
25627c478bd9Sstevel@tonic-gate static void
ehci_insert_qtd_into_active_qtd_list(ehci_state_t * ehcip,ehci_qtd_t * qtd)25637c478bd9Sstevel@tonic-gate ehci_insert_qtd_into_active_qtd_list(
25647c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
25657c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*qtd)
25667c478bd9Sstevel@tonic-gate {
25677c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*curr_qtd, *next_qtd;
25687c478bd9Sstevel@tonic-gate 
25697c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
25707c478bd9Sstevel@tonic-gate 
25717c478bd9Sstevel@tonic-gate 	curr_qtd = ehcip->ehci_active_qtd_list;
25727c478bd9Sstevel@tonic-gate 
25737c478bd9Sstevel@tonic-gate 	/* Insert this QTD into QTD Active List */
25747c478bd9Sstevel@tonic-gate 	if (curr_qtd) {
25757c478bd9Sstevel@tonic-gate 		next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
25767c478bd9Sstevel@tonic-gate 		    Get_QTD(curr_qtd->qtd_active_qtd_next));
25777c478bd9Sstevel@tonic-gate 
25787c478bd9Sstevel@tonic-gate 		while (next_qtd) {
25797c478bd9Sstevel@tonic-gate 			curr_qtd = next_qtd;
25807c478bd9Sstevel@tonic-gate 			next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
25817c478bd9Sstevel@tonic-gate 			    Get_QTD(curr_qtd->qtd_active_qtd_next));
25827c478bd9Sstevel@tonic-gate 		}
25837c478bd9Sstevel@tonic-gate 
25847c478bd9Sstevel@tonic-gate 		Set_QTD(qtd->qtd_active_qtd_prev,
25857c478bd9Sstevel@tonic-gate 		    ehci_qtd_cpu_to_iommu(ehcip, curr_qtd));
25867c478bd9Sstevel@tonic-gate 
25877c478bd9Sstevel@tonic-gate 		Set_QTD(curr_qtd->qtd_active_qtd_next,
25887c478bd9Sstevel@tonic-gate 		    ehci_qtd_cpu_to_iommu(ehcip, qtd));
25897c478bd9Sstevel@tonic-gate 	} else {
25907c478bd9Sstevel@tonic-gate 		ehcip->ehci_active_qtd_list = qtd;
25916aef9e11SToomas Soome 		Set_QTD(qtd->qtd_active_qtd_next, 0);
25926aef9e11SToomas Soome 		Set_QTD(qtd->qtd_active_qtd_prev, 0);
25937c478bd9Sstevel@tonic-gate 	}
25947c478bd9Sstevel@tonic-gate }
25957c478bd9Sstevel@tonic-gate 
25967c478bd9Sstevel@tonic-gate 
25977c478bd9Sstevel@tonic-gate /*
25987c478bd9Sstevel@tonic-gate  * ehci_remove_qtd_from_active_qtd_list:
25997c478bd9Sstevel@tonic-gate  *
26007c478bd9Sstevel@tonic-gate  * Remove current QTD from the active QTD list.
26017c478bd9Sstevel@tonic-gate  *
26027c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
26037c478bd9Sstevel@tonic-gate  */
26047c478bd9Sstevel@tonic-gate void
ehci_remove_qtd_from_active_qtd_list(ehci_state_t * ehcip,ehci_qtd_t * qtd)26057c478bd9Sstevel@tonic-gate ehci_remove_qtd_from_active_qtd_list(
26067c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
26077c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*qtd)
26087c478bd9Sstevel@tonic-gate {
26097c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*curr_qtd, *prev_qtd, *next_qtd;
26107c478bd9Sstevel@tonic-gate 
26117c478bd9Sstevel@tonic-gate 	ASSERT(qtd != NULL);
26127c478bd9Sstevel@tonic-gate 
26137c478bd9Sstevel@tonic-gate 	curr_qtd = ehcip->ehci_active_qtd_list;
26147c478bd9Sstevel@tonic-gate 
26157c478bd9Sstevel@tonic-gate 	while ((curr_qtd) && (curr_qtd != qtd)) {
26167c478bd9Sstevel@tonic-gate 		curr_qtd = ehci_qtd_iommu_to_cpu(ehcip,
26177c478bd9Sstevel@tonic-gate 		    Get_QTD(curr_qtd->qtd_active_qtd_next));
26187c478bd9Sstevel@tonic-gate 	}
26197c478bd9Sstevel@tonic-gate 
26207c478bd9Sstevel@tonic-gate 	if ((curr_qtd) && (curr_qtd == qtd)) {
26217c478bd9Sstevel@tonic-gate 		prev_qtd = ehci_qtd_iommu_to_cpu(ehcip,
26227c478bd9Sstevel@tonic-gate 		    Get_QTD(curr_qtd->qtd_active_qtd_prev));
26237c478bd9Sstevel@tonic-gate 		next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
26247c478bd9Sstevel@tonic-gate 		    Get_QTD(curr_qtd->qtd_active_qtd_next));
26257c478bd9Sstevel@tonic-gate 
26267c478bd9Sstevel@tonic-gate 		if (prev_qtd) {
26277c478bd9Sstevel@tonic-gate 			Set_QTD(prev_qtd->qtd_active_qtd_next,
26287c478bd9Sstevel@tonic-gate 			    Get_QTD(curr_qtd->qtd_active_qtd_next));
26297c478bd9Sstevel@tonic-gate 		} else {
26307c478bd9Sstevel@tonic-gate 			ehcip->ehci_active_qtd_list = next_qtd;
26317c478bd9Sstevel@tonic-gate 		}
26327c478bd9Sstevel@tonic-gate 
26337c478bd9Sstevel@tonic-gate 		if (next_qtd) {
26347c478bd9Sstevel@tonic-gate 			Set_QTD(next_qtd->qtd_active_qtd_prev,
26357c478bd9Sstevel@tonic-gate 			    Get_QTD(curr_qtd->qtd_active_qtd_prev));
26367c478bd9Sstevel@tonic-gate 		}
26377c478bd9Sstevel@tonic-gate 	} else {
26387c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
26397c478bd9Sstevel@tonic-gate 		    "ehci_remove_qtd_from_active_qtd_list: "
26406a9de478Ssl 		    "Unable to find QTD in active_qtd_list");
26417c478bd9Sstevel@tonic-gate 	}
26427c478bd9Sstevel@tonic-gate }
26437c478bd9Sstevel@tonic-gate 
26447c478bd9Sstevel@tonic-gate 
26457c478bd9Sstevel@tonic-gate /*
26467c478bd9Sstevel@tonic-gate  * ehci_traverse_qtds:
26477c478bd9Sstevel@tonic-gate  *
26487c478bd9Sstevel@tonic-gate  * Traverse the list of QTDs for given pipe using transfer wrapper.  Since
26497c478bd9Sstevel@tonic-gate  * the endpoint is marked as Halted, the Host Controller (HC) is no longer
26507c478bd9Sstevel@tonic-gate  * accessing these QTDs. Remove all the QTDs that are attached to endpoint.
26517c478bd9Sstevel@tonic-gate  */
26527c478bd9Sstevel@tonic-gate static void
ehci_traverse_qtds(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)26537c478bd9Sstevel@tonic-gate ehci_traverse_qtds(
26547c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
26557c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
26567c478bd9Sstevel@tonic-gate {
26577c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
26587c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*next_tw;
26597c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*qtd;
26607c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*next_qtd;
26617c478bd9Sstevel@tonic-gate 
26627c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
26637c478bd9Sstevel@tonic-gate 
26647c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
26657c478bd9Sstevel@tonic-gate 	    "ehci_traverse_qtds:");
26667c478bd9Sstevel@tonic-gate 
26677c478bd9Sstevel@tonic-gate 	/* Process the transfer wrappers for this pipe */
26687c478bd9Sstevel@tonic-gate 	next_tw = pp->pp_tw_head;
26697c478bd9Sstevel@tonic-gate 
26707c478bd9Sstevel@tonic-gate 	while (next_tw) {
26717c478bd9Sstevel@tonic-gate 		/* Stop the the transfer timer */
26727c478bd9Sstevel@tonic-gate 		ehci_stop_xfer_timer(ehcip, next_tw, EHCI_REMOVE_XFER_ALWAYS);
26737c478bd9Sstevel@tonic-gate 
26747c478bd9Sstevel@tonic-gate 		qtd = (ehci_qtd_t *)next_tw->tw_qtd_head;
26757c478bd9Sstevel@tonic-gate 
26767c478bd9Sstevel@tonic-gate 		/* Walk through each QTD for this transfer wrapper */
26777c478bd9Sstevel@tonic-gate 		while (qtd) {
26787c478bd9Sstevel@tonic-gate 			/* Remove this QTD from active QTD list */
26797c478bd9Sstevel@tonic-gate 			ehci_remove_qtd_from_active_qtd_list(ehcip, qtd);
26807c478bd9Sstevel@tonic-gate 
26817c478bd9Sstevel@tonic-gate 			next_qtd = ehci_qtd_iommu_to_cpu(ehcip,
26827c478bd9Sstevel@tonic-gate 			    Get_QTD(qtd->qtd_tw_next_qtd));
26837c478bd9Sstevel@tonic-gate 
26847c478bd9Sstevel@tonic-gate 			/* Deallocate this QTD */
26857c478bd9Sstevel@tonic-gate 			ehci_deallocate_qtd(ehcip, qtd);
26867c478bd9Sstevel@tonic-gate 
26877c478bd9Sstevel@tonic-gate 			qtd = next_qtd;
26887c478bd9Sstevel@tonic-gate 		}
26897c478bd9Sstevel@tonic-gate 
26907c478bd9Sstevel@tonic-gate 		next_tw = next_tw->tw_next;
26917c478bd9Sstevel@tonic-gate 	}
26927c478bd9Sstevel@tonic-gate 
26937c478bd9Sstevel@tonic-gate 	/* Clear current qtd pointer */
26947c478bd9Sstevel@tonic-gate 	Set_QH(pp->pp_qh->qh_curr_qtd, (uint32_t)0x00000000);
26957c478bd9Sstevel@tonic-gate 
26967c478bd9Sstevel@tonic-gate 	/* Update the next qtd pointer in the QH */
26977c478bd9Sstevel@tonic-gate 	Set_QH(pp->pp_qh->qh_next_qtd, Get_QH(pp->pp_qh->qh_dummy_qtd));
26987c478bd9Sstevel@tonic-gate }
26997c478bd9Sstevel@tonic-gate 
27007c478bd9Sstevel@tonic-gate 
27017c478bd9Sstevel@tonic-gate /*
27027c478bd9Sstevel@tonic-gate  * ehci_deallocate_qtd:
27037c478bd9Sstevel@tonic-gate  *
27047c478bd9Sstevel@tonic-gate  * Deallocate a Host Controller's (HC) Transfer Descriptor (QTD).
27057c478bd9Sstevel@tonic-gate  *
27067c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
27077c478bd9Sstevel@tonic-gate  */
27087c478bd9Sstevel@tonic-gate void
ehci_deallocate_qtd(ehci_state_t * ehcip,ehci_qtd_t * old_qtd)27097c478bd9Sstevel@tonic-gate ehci_deallocate_qtd(
27107c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
27117c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*old_qtd)
27127c478bd9Sstevel@tonic-gate {
27137c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw = NULL;
27147c478bd9Sstevel@tonic-gate 
27157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
27167c478bd9Sstevel@tonic-gate 	    "ehci_deallocate_qtd: old_qtd = 0x%p", (void *)old_qtd);
27177c478bd9Sstevel@tonic-gate 
27187c478bd9Sstevel@tonic-gate 	/*
27197c478bd9Sstevel@tonic-gate 	 * Obtain the transaction wrapper and tw will be
27207c478bd9Sstevel@tonic-gate 	 * NULL for the dummy QTDs.
27217c478bd9Sstevel@tonic-gate 	 */
27227c478bd9Sstevel@tonic-gate 	if (Get_QTD(old_qtd->qtd_state) != EHCI_QTD_DUMMY) {
27237c478bd9Sstevel@tonic-gate 		tw = (ehci_trans_wrapper_t *)
27246a9de478Ssl 		    EHCI_LOOKUP_ID((uint32_t)
27256a9de478Ssl 		    Get_QTD(old_qtd->qtd_trans_wrapper));
27267c478bd9Sstevel@tonic-gate 
27277c478bd9Sstevel@tonic-gate 		ASSERT(tw != NULL);
27287c478bd9Sstevel@tonic-gate 	}
27297c478bd9Sstevel@tonic-gate 
27307c478bd9Sstevel@tonic-gate 	/*
27317c478bd9Sstevel@tonic-gate 	 * If QTD's transfer wrapper is NULL, don't access its TW.
27327c478bd9Sstevel@tonic-gate 	 * Just free the QTD.
27337c478bd9Sstevel@tonic-gate 	 */
27347c478bd9Sstevel@tonic-gate 	if (tw) {
27357c478bd9Sstevel@tonic-gate 		ehci_qtd_t	*qtd, *next_qtd;
27367c478bd9Sstevel@tonic-gate 
27377c478bd9Sstevel@tonic-gate 		qtd = tw->tw_qtd_head;
27387c478bd9Sstevel@tonic-gate 
27397c478bd9Sstevel@tonic-gate 		if (old_qtd != qtd) {
27407c478bd9Sstevel@tonic-gate 			next_qtd = ehci_qtd_iommu_to_cpu(
27416a9de478Ssl 			    ehcip, Get_QTD(qtd->qtd_tw_next_qtd));
27427c478bd9Sstevel@tonic-gate 
27437c478bd9Sstevel@tonic-gate 			while (next_qtd != old_qtd) {
27447c478bd9Sstevel@tonic-gate 				qtd = next_qtd;
27457c478bd9Sstevel@tonic-gate 				next_qtd = ehci_qtd_iommu_to_cpu(
27467c478bd9Sstevel@tonic-gate 				    ehcip, Get_QTD(qtd->qtd_tw_next_qtd));
27477c478bd9Sstevel@tonic-gate 			}
27487c478bd9Sstevel@tonic-gate 
27497c478bd9Sstevel@tonic-gate 			Set_QTD(qtd->qtd_tw_next_qtd, old_qtd->qtd_tw_next_qtd);
27507c478bd9Sstevel@tonic-gate 
27516aef9e11SToomas Soome 			if (qtd->qtd_tw_next_qtd == 0) {
27527c478bd9Sstevel@tonic-gate 				tw->tw_qtd_tail = qtd;
27537c478bd9Sstevel@tonic-gate 			}
27547c478bd9Sstevel@tonic-gate 		} else {
27557c478bd9Sstevel@tonic-gate 			tw->tw_qtd_head = ehci_qtd_iommu_to_cpu(
27567c478bd9Sstevel@tonic-gate 			    ehcip, Get_QTD(old_qtd->qtd_tw_next_qtd));
27577c478bd9Sstevel@tonic-gate 
27587c478bd9Sstevel@tonic-gate 			if (tw->tw_qtd_head == NULL) {
27597c478bd9Sstevel@tonic-gate 				tw->tw_qtd_tail = NULL;
27607c478bd9Sstevel@tonic-gate 			}
27617c478bd9Sstevel@tonic-gate 		}
27627c478bd9Sstevel@tonic-gate 	}
27637c478bd9Sstevel@tonic-gate 
27647c478bd9Sstevel@tonic-gate 	bzero((void *)old_qtd, sizeof (ehci_qtd_t));
27657c478bd9Sstevel@tonic-gate 	Set_QTD(old_qtd->qtd_state, EHCI_QTD_FREE);
27667c478bd9Sstevel@tonic-gate 
27677c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
27687c478bd9Sstevel@tonic-gate 	    "Dealloc_qtd: qtd 0x%p", (void *)old_qtd);
27697c478bd9Sstevel@tonic-gate }
27707c478bd9Sstevel@tonic-gate 
27717c478bd9Sstevel@tonic-gate 
27727c478bd9Sstevel@tonic-gate /*
27737c478bd9Sstevel@tonic-gate  * ehci_qtd_cpu_to_iommu:
27747c478bd9Sstevel@tonic-gate  *
27757c478bd9Sstevel@tonic-gate  * This function converts for the given Transfer Descriptor (QTD) CPU address
27767c478bd9Sstevel@tonic-gate  * to IO address.
27777c478bd9Sstevel@tonic-gate  *
27787c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
27797c478bd9Sstevel@tonic-gate  */
27807c478bd9Sstevel@tonic-gate uint32_t
ehci_qtd_cpu_to_iommu(ehci_state_t * ehcip,ehci_qtd_t * addr)27817c478bd9Sstevel@tonic-gate ehci_qtd_cpu_to_iommu(
27827c478bd9Sstevel@tonic-gate 	ehci_state_t	*ehcip,
27837c478bd9Sstevel@tonic-gate 	ehci_qtd_t	*addr)
27847c478bd9Sstevel@tonic-gate {
27857c478bd9Sstevel@tonic-gate 	uint32_t	td;
27867c478bd9Sstevel@tonic-gate 
27877c478bd9Sstevel@tonic-gate 	td  = (uint32_t)ehcip->ehci_qtd_pool_cookie.dmac_address +
27887c478bd9Sstevel@tonic-gate 	    (uint32_t)((uintptr_t)addr -
27897c478bd9Sstevel@tonic-gate 	    (uintptr_t)(ehcip->ehci_qtd_pool_addr));
27907c478bd9Sstevel@tonic-gate 
27917c478bd9Sstevel@tonic-gate 	ASSERT((ehcip->ehci_qtd_pool_cookie.dmac_address +
27927c478bd9Sstevel@tonic-gate 	    (uint32_t) (sizeof (ehci_qtd_t) *
27937c478bd9Sstevel@tonic-gate 	    (addr - ehcip->ehci_qtd_pool_addr))) ==
27947c478bd9Sstevel@tonic-gate 	    (ehcip->ehci_qtd_pool_cookie.dmac_address +
27957c478bd9Sstevel@tonic-gate 	    (uint32_t)((uintptr_t)addr - (uintptr_t)
27967c478bd9Sstevel@tonic-gate 	    (ehcip->ehci_qtd_pool_addr))));
27977c478bd9Sstevel@tonic-gate 
27987c478bd9Sstevel@tonic-gate 	ASSERT(td >= ehcip->ehci_qtd_pool_cookie.dmac_address);
27997c478bd9Sstevel@tonic-gate 	ASSERT(td <= ehcip->ehci_qtd_pool_cookie.dmac_address +
28007c478bd9Sstevel@tonic-gate 	    sizeof (ehci_qtd_t) * ehci_qtd_pool_size);
28017c478bd9Sstevel@tonic-gate 
28027c478bd9Sstevel@tonic-gate 	return (td);
28037c478bd9Sstevel@tonic-gate }
28047c478bd9Sstevel@tonic-gate 
28057c478bd9Sstevel@tonic-gate 
28067c478bd9Sstevel@tonic-gate /*
28077c478bd9Sstevel@tonic-gate  * ehci_qtd_iommu_to_cpu:
28087c478bd9Sstevel@tonic-gate  *
28097c478bd9Sstevel@tonic-gate  * This function converts for the given Transfer Descriptor (QTD) IO address
28107c478bd9Sstevel@tonic-gate  * to CPU address.
28117c478bd9Sstevel@tonic-gate  *
28127c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
28137c478bd9Sstevel@tonic-gate  */
28147c478bd9Sstevel@tonic-gate ehci_qtd_t *
ehci_qtd_iommu_to_cpu(ehci_state_t * ehcip,uintptr_t addr)28157c478bd9Sstevel@tonic-gate ehci_qtd_iommu_to_cpu(
28167c478bd9Sstevel@tonic-gate 	ehci_state_t	*ehcip,
28177c478bd9Sstevel@tonic-gate 	uintptr_t	addr)
28187c478bd9Sstevel@tonic-gate {
28197c478bd9Sstevel@tonic-gate 	ehci_qtd_t	*qtd;
28207c478bd9Sstevel@tonic-gate 
28216aef9e11SToomas Soome 	if (addr == 0)
28227c478bd9Sstevel@tonic-gate 		return (NULL);
28237c478bd9Sstevel@tonic-gate 
28247c478bd9Sstevel@tonic-gate 	qtd = (ehci_qtd_t *)((uintptr_t)
28257c478bd9Sstevel@tonic-gate 	    (addr - ehcip->ehci_qtd_pool_cookie.dmac_address) +
28267c478bd9Sstevel@tonic-gate 	    (uintptr_t)ehcip->ehci_qtd_pool_addr);
28277c478bd9Sstevel@tonic-gate 
28287c478bd9Sstevel@tonic-gate 	ASSERT(qtd >= ehcip->ehci_qtd_pool_addr);
28297c478bd9Sstevel@tonic-gate 	ASSERT((uintptr_t)qtd <= (uintptr_t)ehcip->ehci_qtd_pool_addr +
28307c478bd9Sstevel@tonic-gate 	    (uintptr_t)(sizeof (ehci_qtd_t) * ehci_qtd_pool_size));
28317c478bd9Sstevel@tonic-gate 
28327c478bd9Sstevel@tonic-gate 	return (qtd);
28337c478bd9Sstevel@tonic-gate }
28347c478bd9Sstevel@tonic-gate 
28357c478bd9Sstevel@tonic-gate /*
28367c478bd9Sstevel@tonic-gate  * ehci_allocate_tds_for_tw_resources:
28377c478bd9Sstevel@tonic-gate  *
28387c478bd9Sstevel@tonic-gate  * Allocate n Transfer Descriptors (TD) from the TD buffer pool and places it
28397c478bd9Sstevel@tonic-gate  * into the TW.  Also chooses the correct alternate qtd when required.	It is
28407c478bd9Sstevel@tonic-gate  * used for hardware short transfer support.  For more information on
28417c478bd9Sstevel@tonic-gate  * alternate qtds look at section 3.5.2 in the EHCI spec.
28427c478bd9Sstevel@tonic-gate  * Here is how each alternate qtd's are used:
28437c478bd9Sstevel@tonic-gate  *
28447c478bd9Sstevel@tonic-gate  * Bulk: used fully.
28457c478bd9Sstevel@tonic-gate  * Intr: xfers only require 1 QTD, so alternate qtds are never used.
28467c478bd9Sstevel@tonic-gate  * Ctrl: Should not use alternate QTD
28477c478bd9Sstevel@tonic-gate  * Isoch: Doesn't support short_xfer nor does it use QTD
28487c478bd9Sstevel@tonic-gate  *
28497c478bd9Sstevel@tonic-gate  * Returns USB_NO_RESOURCES if it was not able to allocate all the requested TD
28507c478bd9Sstevel@tonic-gate  * otherwise USB_SUCCESS.
28517c478bd9Sstevel@tonic-gate  */
28527c478bd9Sstevel@tonic-gate int
ehci_allocate_tds_for_tw(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw,size_t qtd_count)28537c478bd9Sstevel@tonic-gate ehci_allocate_tds_for_tw(
28547c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
28557c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
28567c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw,
28577c478bd9Sstevel@tonic-gate 	size_t			qtd_count)
28587c478bd9Sstevel@tonic-gate {
28597c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &pp->pp_pipe_handle->p_ep;
28607c478bd9Sstevel@tonic-gate 	uchar_t			attributes;
28617c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*qtd;
28627c478bd9Sstevel@tonic-gate 	uint32_t		qtd_addr;
28637c478bd9Sstevel@tonic-gate 	int			i;
28647c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
28657c478bd9Sstevel@tonic-gate 
28667c478bd9Sstevel@tonic-gate 	attributes = eptd->bmAttributes & USB_EP_ATTR_MASK;
28677c478bd9Sstevel@tonic-gate 
28687c478bd9Sstevel@tonic-gate 	for (i = 0; i < qtd_count; i += 1) {
28697c478bd9Sstevel@tonic-gate 		qtd = ehci_allocate_qtd_from_pool(ehcip);
28707c478bd9Sstevel@tonic-gate 		if (qtd == NULL) {
28717c478bd9Sstevel@tonic-gate 			error = USB_NO_RESOURCES;
28727c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
28737c478bd9Sstevel@tonic-gate 			    "ehci_allocate_qtds_for_tw: "
28747c478bd9Sstevel@tonic-gate 			    "Unable to allocate %lu QTDs",
28757c478bd9Sstevel@tonic-gate 			    qtd_count);
28767c478bd9Sstevel@tonic-gate 			break;
28777c478bd9Sstevel@tonic-gate 		}
28787c478bd9Sstevel@tonic-gate 		if (i > 0) {
28797c478bd9Sstevel@tonic-gate 			qtd_addr = ehci_qtd_cpu_to_iommu(ehcip,
28807c478bd9Sstevel@tonic-gate 			    tw->tw_qtd_free_list);
28817c478bd9Sstevel@tonic-gate 			Set_QTD(qtd->qtd_tw_next_qtd, qtd_addr);
28827c478bd9Sstevel@tonic-gate 		}
28837c478bd9Sstevel@tonic-gate 		tw->tw_qtd_free_list = qtd;
28847c478bd9Sstevel@tonic-gate 
28857c478bd9Sstevel@tonic-gate 		/*
28867c478bd9Sstevel@tonic-gate 		 * Save the second one as a pointer to the new dummy 1.
28877c478bd9Sstevel@tonic-gate 		 * It is used later for the alt_qtd_ptr.  Xfers with only
28887c478bd9Sstevel@tonic-gate 		 * one qtd do not need alt_qtd_ptr.
28897c478bd9Sstevel@tonic-gate 		 * The tds's are allocated and put into a stack, that is
28907c478bd9Sstevel@tonic-gate 		 * why the second qtd allocated will turn out to be the
28917c478bd9Sstevel@tonic-gate 		 * new dummy 1.
28927c478bd9Sstevel@tonic-gate 		 */
28937c478bd9Sstevel@tonic-gate 		if ((i == 1) && (attributes == USB_EP_ATTR_BULK)) {
28947c478bd9Sstevel@tonic-gate 			tw->tw_alt_qtd = qtd;
28957c478bd9Sstevel@tonic-gate 		}
28967c478bd9Sstevel@tonic-gate 	}
28977c478bd9Sstevel@tonic-gate 
28987c478bd9Sstevel@tonic-gate 	return (error);
28997c478bd9Sstevel@tonic-gate }
29007c478bd9Sstevel@tonic-gate 
29017c478bd9Sstevel@tonic-gate /*
29027c478bd9Sstevel@tonic-gate  * ehci_allocate_tw_resources:
29037c478bd9Sstevel@tonic-gate  *
29047c478bd9Sstevel@tonic-gate  * Allocate a Transaction Wrapper (TW) and n Transfer Descriptors (QTD)
29057c478bd9Sstevel@tonic-gate  * from the QTD buffer pool and places it into the TW.	It does an all
29067c478bd9Sstevel@tonic-gate  * or nothing transaction.
29077c478bd9Sstevel@tonic-gate  *
29087c478bd9Sstevel@tonic-gate  * Returns NULL if there is insufficient resources otherwise TW.
29097c478bd9Sstevel@tonic-gate  */
29107c478bd9Sstevel@tonic-gate static ehci_trans_wrapper_t *
ehci_allocate_tw_resources(ehci_state_t * ehcip,ehci_pipe_private_t * pp,size_t tw_length,usb_flags_t usb_flags,size_t qtd_count)29117c478bd9Sstevel@tonic-gate ehci_allocate_tw_resources(
29127c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
29137c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
29147c478bd9Sstevel@tonic-gate 	size_t			tw_length,
29157c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags,
29167c478bd9Sstevel@tonic-gate 	size_t			qtd_count)
29177c478bd9Sstevel@tonic-gate {
29187c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw;
29197c478bd9Sstevel@tonic-gate 
29207c478bd9Sstevel@tonic-gate 	tw = ehci_create_transfer_wrapper(ehcip, pp, tw_length, usb_flags);
29217c478bd9Sstevel@tonic-gate 
29227c478bd9Sstevel@tonic-gate 	if (tw == NULL) {
29237c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
29247c478bd9Sstevel@tonic-gate 		    "ehci_allocate_tw_resources: Unable to allocate TW");
29257c478bd9Sstevel@tonic-gate 	} else {
29267c478bd9Sstevel@tonic-gate 		if (ehci_allocate_tds_for_tw(ehcip, pp, tw, qtd_count) ==
29277c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
2928d29f5a71Szhigang lu - Sun Microsystems - Beijing China 			tw->tw_num_qtds = (uint_t)qtd_count;
29297c478bd9Sstevel@tonic-gate 		} else {
29307c478bd9Sstevel@tonic-gate 			ehci_deallocate_tw(ehcip, pp, tw);
29317c478bd9Sstevel@tonic-gate 			tw = NULL;
29327c478bd9Sstevel@tonic-gate 		}
29337c478bd9Sstevel@tonic-gate 	}
29347c478bd9Sstevel@tonic-gate 
29357c478bd9Sstevel@tonic-gate 	return (tw);
29367c478bd9Sstevel@tonic-gate }
29377c478bd9Sstevel@tonic-gate 
29387c478bd9Sstevel@tonic-gate 
29397c478bd9Sstevel@tonic-gate /*
29407c478bd9Sstevel@tonic-gate  * ehci_free_tw_td_resources:
29417c478bd9Sstevel@tonic-gate  *
29427c478bd9Sstevel@tonic-gate  * Free all allocated resources for Transaction Wrapper (TW).
29437c478bd9Sstevel@tonic-gate  * Does not free the TW itself.
29447c478bd9Sstevel@tonic-gate  *
29457c478bd9Sstevel@tonic-gate  * Returns NULL if there is insufficient resources otherwise TW.
29467c478bd9Sstevel@tonic-gate  */
29477c478bd9Sstevel@tonic-gate static void
ehci_free_tw_td_resources(ehci_state_t * ehcip,ehci_trans_wrapper_t * tw)29487c478bd9Sstevel@tonic-gate ehci_free_tw_td_resources(
29497c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
29507c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw)
29517c478bd9Sstevel@tonic-gate {
29527c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*qtd = NULL;
29537c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*temp_qtd = NULL;
29547c478bd9Sstevel@tonic-gate 
29557c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
2956112116d8Sfb 	    "ehci_free_tw_td_resources: tw = 0x%p", (void *)tw);
29577c478bd9Sstevel@tonic-gate 
29587c478bd9Sstevel@tonic-gate 	qtd = tw->tw_qtd_free_list;
29597c478bd9Sstevel@tonic-gate 	while (qtd != NULL) {
29607c478bd9Sstevel@tonic-gate 		/* Save the pointer to the next qtd before destroying it */
29617c478bd9Sstevel@tonic-gate 		temp_qtd = ehci_qtd_iommu_to_cpu(ehcip,
29627c478bd9Sstevel@tonic-gate 		    Get_QTD(qtd->qtd_tw_next_qtd));
29637c478bd9Sstevel@tonic-gate 		ehci_deallocate_qtd(ehcip, qtd);
29647c478bd9Sstevel@tonic-gate 		qtd = temp_qtd;
29657c478bd9Sstevel@tonic-gate 	}
29667c478bd9Sstevel@tonic-gate 	tw->tw_qtd_free_list = NULL;
29677c478bd9Sstevel@tonic-gate }
29687c478bd9Sstevel@tonic-gate 
29697c478bd9Sstevel@tonic-gate /*
29707c478bd9Sstevel@tonic-gate  * Transfer Wrapper functions
29717c478bd9Sstevel@tonic-gate  *
29727c478bd9Sstevel@tonic-gate  * ehci_create_transfer_wrapper:
29737c478bd9Sstevel@tonic-gate  *
29747c478bd9Sstevel@tonic-gate  * Create a Transaction Wrapper (TW) and this involves the allocating of DMA
29757c478bd9Sstevel@tonic-gate  * resources.
29767c478bd9Sstevel@tonic-gate  */
29777c478bd9Sstevel@tonic-gate static ehci_trans_wrapper_t *
ehci_create_transfer_wrapper(ehci_state_t * ehcip,ehci_pipe_private_t * pp,size_t length,uint_t usb_flags)29787c478bd9Sstevel@tonic-gate ehci_create_transfer_wrapper(
29797c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
29807c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
29817c478bd9Sstevel@tonic-gate 	size_t			length,
29827c478bd9Sstevel@tonic-gate 	uint_t			usb_flags)
29837c478bd9Sstevel@tonic-gate {
29847c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t	dev_attr;
29853304303fSsl 	ddi_dma_attr_t		dma_attr;
29867c478bd9Sstevel@tonic-gate 	int			result;
29877c478bd9Sstevel@tonic-gate 	size_t			real_length;
29887c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw;
29893304303fSsl 	int			kmem_flag;
29903304303fSsl 	int			(*dmamem_wait)(caddr_t);
2991dfbb3a42SRaymond Chen 	usb_ep_descr_t		*eptd = &pp->pp_pipe_handle->p_ep;
29927c478bd9Sstevel@tonic-gate 
29937c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
29947c478bd9Sstevel@tonic-gate 	    "ehci_create_transfer_wrapper: length = 0x%lx flags = 0x%x",
29957c478bd9Sstevel@tonic-gate 	    length, usb_flags);
29967c478bd9Sstevel@tonic-gate 
29977c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
29987c478bd9Sstevel@tonic-gate 
2999f9190914Szhigang lu - Sun Microsystems - Beijing China 	/* SLEEP flag should not be used while holding mutex */
3000f9190914Szhigang lu - Sun Microsystems - Beijing China 	kmem_flag = KM_NOSLEEP;
3001f9190914Szhigang lu - Sun Microsystems - Beijing China 	dmamem_wait = DDI_DMA_DONTWAIT;
30023304303fSsl 
30037c478bd9Sstevel@tonic-gate 	/* Allocate space for the transfer wrapper */
30043304303fSsl 	tw = kmem_zalloc(sizeof (ehci_trans_wrapper_t), kmem_flag);
30057c478bd9Sstevel@tonic-gate 
30067c478bd9Sstevel@tonic-gate 	if (tw == NULL) {
30077c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS,  ehcip->ehci_log_hdl,
30087c478bd9Sstevel@tonic-gate 		    "ehci_create_transfer_wrapper: kmem_zalloc failed");
30097c478bd9Sstevel@tonic-gate 
30107c478bd9Sstevel@tonic-gate 		return (NULL);
30117c478bd9Sstevel@tonic-gate 	}
30127c478bd9Sstevel@tonic-gate 
3013688b07c5Sgc 	/* zero-length packet doesn't need to allocate dma memory */
3014688b07c5Sgc 	if (length == 0) {
3015688b07c5Sgc 
3016688b07c5Sgc 		goto dmadone;
3017688b07c5Sgc 	}
3018688b07c5Sgc 
30193304303fSsl 	/* allow sg lists for transfer wrapper dma memory */
30203304303fSsl 	bcopy(&ehcip->ehci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
30213304303fSsl 	dma_attr.dma_attr_sgllen = EHCI_DMA_ATTR_TW_SGLLEN;
30223304303fSsl 	dma_attr.dma_attr_align = EHCI_DMA_ATTR_ALIGNMENT;
30237c478bd9Sstevel@tonic-gate 
30247c478bd9Sstevel@tonic-gate 	/* Allocate the DMA handle */
30257c478bd9Sstevel@tonic-gate 	result = ddi_dma_alloc_handle(ehcip->ehci_dip,
30263304303fSsl 	    &dma_attr, dmamem_wait, 0, &tw->tw_dmahandle);
30277c478bd9Sstevel@tonic-gate 
30287c478bd9Sstevel@tonic-gate 	if (result != DDI_SUCCESS) {
30297c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
30307c478bd9Sstevel@tonic-gate 		    "ehci_create_transfer_wrapper: Alloc handle failed");
30317c478bd9Sstevel@tonic-gate 
30327c478bd9Sstevel@tonic-gate 		kmem_free(tw, sizeof (ehci_trans_wrapper_t));
30337c478bd9Sstevel@tonic-gate 
30347c478bd9Sstevel@tonic-gate 		return (NULL);
30357c478bd9Sstevel@tonic-gate 	}
30367c478bd9Sstevel@tonic-gate 
30377c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
30387c478bd9Sstevel@tonic-gate 
30397c478bd9Sstevel@tonic-gate 	/* no need for swapping the raw data */
30407c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_endian_flags  = DDI_NEVERSWAP_ACC;
30417c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
30427c478bd9Sstevel@tonic-gate 
30437c478bd9Sstevel@tonic-gate 	/* Allocate the memory */
30447c478bd9Sstevel@tonic-gate 	result = ddi_dma_mem_alloc(tw->tw_dmahandle, length,
30453304303fSsl 	    &dev_attr, DDI_DMA_CONSISTENT, dmamem_wait, NULL,
30467c478bd9Sstevel@tonic-gate 	    (caddr_t *)&tw->tw_buf, &real_length, &tw->tw_accesshandle);
30477c478bd9Sstevel@tonic-gate 
30487c478bd9Sstevel@tonic-gate 	if (result != DDI_SUCCESS) {
30497c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
30507c478bd9Sstevel@tonic-gate 		    "ehci_create_transfer_wrapper: dma_mem_alloc fail");
30517c478bd9Sstevel@tonic-gate 
30527c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&tw->tw_dmahandle);
30537c478bd9Sstevel@tonic-gate 		kmem_free(tw, sizeof (ehci_trans_wrapper_t));
30547c478bd9Sstevel@tonic-gate 
30557c478bd9Sstevel@tonic-gate 		return (NULL);
30567c478bd9Sstevel@tonic-gate 	}
30577c478bd9Sstevel@tonic-gate 
30587c478bd9Sstevel@tonic-gate 	ASSERT(real_length >= length);
30597c478bd9Sstevel@tonic-gate 
30607c478bd9Sstevel@tonic-gate 	/* Bind the handle */
30617c478bd9Sstevel@tonic-gate 	result = ddi_dma_addr_bind_handle(tw->tw_dmahandle, NULL,
30627c478bd9Sstevel@tonic-gate 	    (caddr_t)tw->tw_buf, real_length, DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
30633304303fSsl 	    dmamem_wait, NULL, &tw->tw_cookie, &tw->tw_ncookies);
30647c478bd9Sstevel@tonic-gate 
30653304303fSsl 	if (result != DDI_DMA_MAPPED) {
30667c478bd9Sstevel@tonic-gate 		ehci_decode_ddi_dma_addr_bind_handle_result(ehcip, result);
30677c478bd9Sstevel@tonic-gate 
30687c478bd9Sstevel@tonic-gate 		ddi_dma_mem_free(&tw->tw_accesshandle);
30697c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&tw->tw_dmahandle);
30707c478bd9Sstevel@tonic-gate 		kmem_free(tw, sizeof (ehci_trans_wrapper_t));
30717c478bd9Sstevel@tonic-gate 
30727c478bd9Sstevel@tonic-gate 		return (NULL);
30737c478bd9Sstevel@tonic-gate 	}
30747c478bd9Sstevel@tonic-gate 
30753304303fSsl 	tw->tw_cookie_idx = 0;
30763304303fSsl 	tw->tw_dma_offs = 0;
30773304303fSsl 
3078688b07c5Sgc dmadone:
30797c478bd9Sstevel@tonic-gate 	/*
30807c478bd9Sstevel@tonic-gate 	 * Only allow one wrapper to be added at a time. Insert the
30817c478bd9Sstevel@tonic-gate 	 * new transaction wrapper into the list for this pipe.
30827c478bd9Sstevel@tonic-gate 	 */
30837c478bd9Sstevel@tonic-gate 	if (pp->pp_tw_head == NULL) {
30847c478bd9Sstevel@tonic-gate 		pp->pp_tw_head = tw;
30857c478bd9Sstevel@tonic-gate 		pp->pp_tw_tail = tw;
30867c478bd9Sstevel@tonic-gate 	} else {
30877c478bd9Sstevel@tonic-gate 		pp->pp_tw_tail->tw_next = tw;
30887c478bd9Sstevel@tonic-gate 		pp->pp_tw_tail = tw;
30897c478bd9Sstevel@tonic-gate 	}
30907c478bd9Sstevel@tonic-gate 
30917c478bd9Sstevel@tonic-gate 	/* Store the transfer length */
30927c478bd9Sstevel@tonic-gate 	tw->tw_length = length;
30937c478bd9Sstevel@tonic-gate 
30947c478bd9Sstevel@tonic-gate 	/* Store a back pointer to the pipe private structure */
30957c478bd9Sstevel@tonic-gate 	tw->tw_pipe_private = pp;
30967c478bd9Sstevel@tonic-gate 
30977c478bd9Sstevel@tonic-gate 	/* Store the transfer type - synchronous or asynchronous */
30987c478bd9Sstevel@tonic-gate 	tw->tw_flags = usb_flags;
30997c478bd9Sstevel@tonic-gate 
31007c478bd9Sstevel@tonic-gate 	/* Get and Store 32bit ID */
31017c478bd9Sstevel@tonic-gate 	tw->tw_id = EHCI_GET_ID((void *)tw);
31027c478bd9Sstevel@tonic-gate 
3103*0a1044f1SToomas Soome 	ASSERT(tw->tw_id != 0);
31047c478bd9Sstevel@tonic-gate 
3105dfbb3a42SRaymond Chen 	/* isoc ep will not come here */
3106dfbb3a42SRaymond Chen 	if (EHCI_INTR_ENDPOINT(eptd)) {
3107dfbb3a42SRaymond Chen 		ehcip->ehci_periodic_req_count++;
3108dfbb3a42SRaymond Chen 	} else {
3109dfbb3a42SRaymond Chen 		ehcip->ehci_async_req_count++;
3110dfbb3a42SRaymond Chen 	}
3111dfbb3a42SRaymond Chen 	ehci_toggle_scheduler(ehcip);
3112dfbb3a42SRaymond Chen 
31137c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
31143304303fSsl 	    "ehci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
3115112116d8Sfb 	    (void *)tw, tw->tw_ncookies);
31167c478bd9Sstevel@tonic-gate 
31177c478bd9Sstevel@tonic-gate 	return (tw);
31187c478bd9Sstevel@tonic-gate }
31197c478bd9Sstevel@tonic-gate 
31207c478bd9Sstevel@tonic-gate 
31217c478bd9Sstevel@tonic-gate /*
31227c478bd9Sstevel@tonic-gate  * ehci_start_xfer_timer:
31237c478bd9Sstevel@tonic-gate  *
31247c478bd9Sstevel@tonic-gate  * Start the timer for the control, bulk and for one time interrupt
31257c478bd9Sstevel@tonic-gate  * transfers.
31267c478bd9Sstevel@tonic-gate  */
31277c478bd9Sstevel@tonic-gate /* ARGSUSED */
31287c478bd9Sstevel@tonic-gate static void
ehci_start_xfer_timer(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw)31297c478bd9Sstevel@tonic-gate ehci_start_xfer_timer(
31307c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
31317c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
31327c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw)
31337c478bd9Sstevel@tonic-gate {
31347c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS,  ehcip->ehci_log_hdl,
3135112116d8Sfb 	    "ehci_start_xfer_timer: tw = 0x%p", (void *)tw);
31367c478bd9Sstevel@tonic-gate 
31377c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
31387c478bd9Sstevel@tonic-gate 
31397c478bd9Sstevel@tonic-gate 	/*
31407c478bd9Sstevel@tonic-gate 	 * The timeout handling is done only for control, bulk and for
31417c478bd9Sstevel@tonic-gate 	 * one time Interrupt transfers.
31427c478bd9Sstevel@tonic-gate 	 *
31437c478bd9Sstevel@tonic-gate 	 * NOTE: If timeout is zero; Assume infinite timeout and don't
31447c478bd9Sstevel@tonic-gate 	 * insert this transfer on the timeout list.
31457c478bd9Sstevel@tonic-gate 	 */
31467c478bd9Sstevel@tonic-gate 	if (tw->tw_timeout) {
31477c478bd9Sstevel@tonic-gate 		/*
31487c478bd9Sstevel@tonic-gate 		 * Add this transfer wrapper to the head of the pipe's
31497c478bd9Sstevel@tonic-gate 		 * tw timeout list.
31507c478bd9Sstevel@tonic-gate 		 */
31517c478bd9Sstevel@tonic-gate 		if (pp->pp_timeout_list) {
31527c478bd9Sstevel@tonic-gate 			tw->tw_timeout_next = pp->pp_timeout_list;
31537c478bd9Sstevel@tonic-gate 		}
31547c478bd9Sstevel@tonic-gate 
31557c478bd9Sstevel@tonic-gate 		pp->pp_timeout_list = tw;
31567c478bd9Sstevel@tonic-gate 		ehci_start_timer(ehcip, pp);
31577c478bd9Sstevel@tonic-gate 	}
31587c478bd9Sstevel@tonic-gate }
31597c478bd9Sstevel@tonic-gate 
31607c478bd9Sstevel@tonic-gate 
31617c478bd9Sstevel@tonic-gate /*
31627c478bd9Sstevel@tonic-gate  * ehci_stop_xfer_timer:
31637c478bd9Sstevel@tonic-gate  *
31647c478bd9Sstevel@tonic-gate  * Start the timer for the control, bulk and for one time interrupt
31657c478bd9Sstevel@tonic-gate  * transfers.
31667c478bd9Sstevel@tonic-gate  */
31677c478bd9Sstevel@tonic-gate void
ehci_stop_xfer_timer(ehci_state_t * ehcip,ehci_trans_wrapper_t * tw,uint_t flag)31687c478bd9Sstevel@tonic-gate ehci_stop_xfer_timer(
31697c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
31707c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw,
31717c478bd9Sstevel@tonic-gate 	uint_t			flag)
31727c478bd9Sstevel@tonic-gate {
31737c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp;
31747c478bd9Sstevel@tonic-gate 	timeout_id_t		timer_id;
31757c478bd9Sstevel@tonic-gate 
31767c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS,  ehcip->ehci_log_hdl,
3177112116d8Sfb 	    "ehci_stop_xfer_timer: tw = 0x%p", (void *)tw);
31787c478bd9Sstevel@tonic-gate 
31797c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
31807c478bd9Sstevel@tonic-gate 
31817c478bd9Sstevel@tonic-gate 	/* Obtain the pipe private structure */
31827c478bd9Sstevel@tonic-gate 	pp = tw->tw_pipe_private;
31837c478bd9Sstevel@tonic-gate 
31847c478bd9Sstevel@tonic-gate 	/* check if the timeout tw list is empty */
31857c478bd9Sstevel@tonic-gate 	if (pp->pp_timeout_list == NULL) {
31867c478bd9Sstevel@tonic-gate 
31877c478bd9Sstevel@tonic-gate 		return;
31887c478bd9Sstevel@tonic-gate 	}
31897c478bd9Sstevel@tonic-gate 
31907c478bd9Sstevel@tonic-gate 	switch (flag) {
31917c478bd9Sstevel@tonic-gate 	case EHCI_REMOVE_XFER_IFLAST:
31927c478bd9Sstevel@tonic-gate 		if (tw->tw_qtd_head != tw->tw_qtd_tail) {
31937c478bd9Sstevel@tonic-gate 			break;
31947c478bd9Sstevel@tonic-gate 		}
31957c478bd9Sstevel@tonic-gate 
31967c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
31977c478bd9Sstevel@tonic-gate 	case EHCI_REMOVE_XFER_ALWAYS:
31987c478bd9Sstevel@tonic-gate 		ehci_remove_tw_from_timeout_list(ehcip, tw);
31997c478bd9Sstevel@tonic-gate 
32007c478bd9Sstevel@tonic-gate 		if ((pp->pp_timeout_list == NULL) &&
32017c478bd9Sstevel@tonic-gate 		    (pp->pp_timer_id)) {
32027c478bd9Sstevel@tonic-gate 
32037c478bd9Sstevel@tonic-gate 			timer_id = pp->pp_timer_id;
32047c478bd9Sstevel@tonic-gate 
32057c478bd9Sstevel@tonic-gate 			/* Reset the timer id to zero */
32067c478bd9Sstevel@tonic-gate 			pp->pp_timer_id = 0;
32077c478bd9Sstevel@tonic-gate 
32087c478bd9Sstevel@tonic-gate 			mutex_exit(&ehcip->ehci_int_mutex);
32097c478bd9Sstevel@tonic-gate 
32107c478bd9Sstevel@tonic-gate 			(void) untimeout(timer_id);
32117c478bd9Sstevel@tonic-gate 
32127c478bd9Sstevel@tonic-gate 			mutex_enter(&ehcip->ehci_int_mutex);
32137c478bd9Sstevel@tonic-gate 		}
32147c478bd9Sstevel@tonic-gate 		break;
32157c478bd9Sstevel@tonic-gate 	default:
32167c478bd9Sstevel@tonic-gate 		break;
32177c478bd9Sstevel@tonic-gate 	}
32187c478bd9Sstevel@tonic-gate }
32197c478bd9Sstevel@tonic-gate 
32207c478bd9Sstevel@tonic-gate 
32217c478bd9Sstevel@tonic-gate /*
32227c478bd9Sstevel@tonic-gate  * ehci_xfer_timeout_handler:
32237c478bd9Sstevel@tonic-gate  *
32247c478bd9Sstevel@tonic-gate  * Control or bulk transfer timeout handler.
32257c478bd9Sstevel@tonic-gate  */
32267c478bd9Sstevel@tonic-gate static void
ehci_xfer_timeout_handler(void * arg)32277c478bd9Sstevel@tonic-gate ehci_xfer_timeout_handler(void *arg)
32287c478bd9Sstevel@tonic-gate {
32297c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = (usba_pipe_handle_data_t *)arg;
32307c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
32316a9de478Ssl 	    ph->p_usba_device->usb_root_hub_dip);
32327c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
32337c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw, *next;
32347c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*expire_xfer_list = NULL;
32357c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*qtd;
32367c478bd9Sstevel@tonic-gate 
32377c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS,  ehcip->ehci_log_hdl,
3238112116d8Sfb 	    "ehci_xfer_timeout_handler: ehcip = 0x%p, ph = 0x%p",
3239112116d8Sfb 	    (void *)ehcip, (void *)ph);
32407c478bd9Sstevel@tonic-gate 
32417c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
32427c478bd9Sstevel@tonic-gate 
32437c478bd9Sstevel@tonic-gate 	/*
32447c478bd9Sstevel@tonic-gate 	 * Check whether still timeout handler is valid.
32457c478bd9Sstevel@tonic-gate 	 */
32467c478bd9Sstevel@tonic-gate 	if (pp->pp_timer_id != 0) {
32477c478bd9Sstevel@tonic-gate 
32487c478bd9Sstevel@tonic-gate 		/* Reset the timer id to zero */
32497c478bd9Sstevel@tonic-gate 		pp->pp_timer_id = 0;
32507c478bd9Sstevel@tonic-gate 	} else {
32517c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
32527c478bd9Sstevel@tonic-gate 
32537c478bd9Sstevel@tonic-gate 		return;
32547c478bd9Sstevel@tonic-gate 	}
32557c478bd9Sstevel@tonic-gate 
32567c478bd9Sstevel@tonic-gate 	/* Get the transfer timeout list head */
32577c478bd9Sstevel@tonic-gate 	tw = pp->pp_timeout_list;
32587c478bd9Sstevel@tonic-gate 
32597c478bd9Sstevel@tonic-gate 	while (tw) {
32607c478bd9Sstevel@tonic-gate 
32617c478bd9Sstevel@tonic-gate 		/* Get the transfer on the timeout list */
32627c478bd9Sstevel@tonic-gate 		next = tw->tw_timeout_next;
32637c478bd9Sstevel@tonic-gate 
32647c478bd9Sstevel@tonic-gate 		tw->tw_timeout--;
32657c478bd9Sstevel@tonic-gate 
32667c478bd9Sstevel@tonic-gate 		if (tw->tw_timeout <= 0) {
32677c478bd9Sstevel@tonic-gate 
32687c478bd9Sstevel@tonic-gate 			/* remove the tw from the timeout list */
32697c478bd9Sstevel@tonic-gate 			ehci_remove_tw_from_timeout_list(ehcip, tw);
32707c478bd9Sstevel@tonic-gate 
32717c478bd9Sstevel@tonic-gate 			/* remove QTDs from active QTD list */
32727c478bd9Sstevel@tonic-gate 			qtd = tw->tw_qtd_head;
32737c478bd9Sstevel@tonic-gate 			while (qtd) {
32747c478bd9Sstevel@tonic-gate 				ehci_remove_qtd_from_active_qtd_list(
32756a9de478Ssl 				    ehcip, qtd);
32767c478bd9Sstevel@tonic-gate 
32777c478bd9Sstevel@tonic-gate 				/* Get the next QTD from the wrapper */
32787c478bd9Sstevel@tonic-gate 				qtd = ehci_qtd_iommu_to_cpu(ehcip,
32797c478bd9Sstevel@tonic-gate 				    Get_QTD(qtd->qtd_tw_next_qtd));
32807c478bd9Sstevel@tonic-gate 			}
32817c478bd9Sstevel@tonic-gate 
32827c478bd9Sstevel@tonic-gate 			/*
32837c478bd9Sstevel@tonic-gate 			 * Preserve the order to the requests
32847c478bd9Sstevel@tonic-gate 			 * started time sequence.
32857c478bd9Sstevel@tonic-gate 			 */
32867c478bd9Sstevel@tonic-gate 			tw->tw_timeout_next = expire_xfer_list;
32877c478bd9Sstevel@tonic-gate 			expire_xfer_list = tw;
32887c478bd9Sstevel@tonic-gate 		}
32897c478bd9Sstevel@tonic-gate 
32907c478bd9Sstevel@tonic-gate 		tw = next;
32917c478bd9Sstevel@tonic-gate 	}
32927c478bd9Sstevel@tonic-gate 
32937c478bd9Sstevel@tonic-gate 	/*
32947c478bd9Sstevel@tonic-gate 	 * The timer should be started before the callbacks.
32957c478bd9Sstevel@tonic-gate 	 * There is always a chance that ehci interrupts come
32967c478bd9Sstevel@tonic-gate 	 * in when we release the mutex while calling the tw back.
32977c478bd9Sstevel@tonic-gate 	 * To keep an accurate timeout it should be restarted
32987c478bd9Sstevel@tonic-gate 	 * as soon as possible.
32997c478bd9Sstevel@tonic-gate 	 */
33007c478bd9Sstevel@tonic-gate 	ehci_start_timer(ehcip, pp);
33017c478bd9Sstevel@tonic-gate 
33027c478bd9Sstevel@tonic-gate 	/* Get the expired transfer timeout list head */
33037c478bd9Sstevel@tonic-gate 	tw = expire_xfer_list;
33047c478bd9Sstevel@tonic-gate 
33057c478bd9Sstevel@tonic-gate 	while (tw) {
33067c478bd9Sstevel@tonic-gate 
33077c478bd9Sstevel@tonic-gate 		/* Get the next tw on the expired transfer timeout list */
33087c478bd9Sstevel@tonic-gate 		next = tw->tw_timeout_next;
33097c478bd9Sstevel@tonic-gate 
33107c478bd9Sstevel@tonic-gate 		/*
33117c478bd9Sstevel@tonic-gate 		 * The error handle routine will release the mutex when
33127c478bd9Sstevel@tonic-gate 		 * calling back to USBA. But this will not cause any race.
33137c478bd9Sstevel@tonic-gate 		 * We do the callback and are relying on ehci_pipe_cleanup()
33147c478bd9Sstevel@tonic-gate 		 * to halt the queue head and clean up since we should not
33157c478bd9Sstevel@tonic-gate 		 * block in timeout context.
33167c478bd9Sstevel@tonic-gate 		 */
33177c478bd9Sstevel@tonic-gate 		ehci_handle_error(ehcip, tw->tw_qtd_head, USB_CR_TIMEOUT);
33187c478bd9Sstevel@tonic-gate 
33197c478bd9Sstevel@tonic-gate 		tw = next;
33207c478bd9Sstevel@tonic-gate 	}
33217c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
33227c478bd9Sstevel@tonic-gate }
33237c478bd9Sstevel@tonic-gate 
33247c478bd9Sstevel@tonic-gate 
33257c478bd9Sstevel@tonic-gate /*
33267c478bd9Sstevel@tonic-gate  * ehci_remove_tw_from_timeout_list:
33277c478bd9Sstevel@tonic-gate  *
33287c478bd9Sstevel@tonic-gate  * Remove Control or bulk transfer from the timeout list.
33297c478bd9Sstevel@tonic-gate  */
33307c478bd9Sstevel@tonic-gate static void
ehci_remove_tw_from_timeout_list(ehci_state_t * ehcip,ehci_trans_wrapper_t * tw)33317c478bd9Sstevel@tonic-gate ehci_remove_tw_from_timeout_list(
33327c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
33337c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw)
33347c478bd9Sstevel@tonic-gate {
33357c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp;
33367c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*prev, *next;
33377c478bd9Sstevel@tonic-gate 
33387c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS,  ehcip->ehci_log_hdl,
3339112116d8Sfb 	    "ehci_remove_tw_from_timeout_list: tw = 0x%p", (void *)tw);
33407c478bd9Sstevel@tonic-gate 
33417c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
33427c478bd9Sstevel@tonic-gate 
33437c478bd9Sstevel@tonic-gate 	/* Obtain the pipe private structure */
33447c478bd9Sstevel@tonic-gate 	pp = tw->tw_pipe_private;
33457c478bd9Sstevel@tonic-gate 
33467c478bd9Sstevel@tonic-gate 	if (pp->pp_timeout_list) {
33477c478bd9Sstevel@tonic-gate 		if (pp->pp_timeout_list == tw) {
33487c478bd9Sstevel@tonic-gate 			pp->pp_timeout_list = tw->tw_timeout_next;
33497c478bd9Sstevel@tonic-gate 
33507c478bd9Sstevel@tonic-gate 			tw->tw_timeout_next = NULL;
33517c478bd9Sstevel@tonic-gate 		} else {
33527c478bd9Sstevel@tonic-gate 			prev = pp->pp_timeout_list;
33537c478bd9Sstevel@tonic-gate 			next = prev->tw_timeout_next;
33547c478bd9Sstevel@tonic-gate 
33557c478bd9Sstevel@tonic-gate 			while (next && (next != tw)) {
33567c478bd9Sstevel@tonic-gate 				prev = next;
33577c478bd9Sstevel@tonic-gate 				next = next->tw_timeout_next;
33587c478bd9Sstevel@tonic-gate 			}
33597c478bd9Sstevel@tonic-gate 
33607c478bd9Sstevel@tonic-gate 			if (next == tw) {
33617c478bd9Sstevel@tonic-gate 				prev->tw_timeout_next =
33626a9de478Ssl 				    next->tw_timeout_next;
33637c478bd9Sstevel@tonic-gate 				tw->tw_timeout_next = NULL;
33647c478bd9Sstevel@tonic-gate 			}
33657c478bd9Sstevel@tonic-gate 		}
33667c478bd9Sstevel@tonic-gate 	}
33677c478bd9Sstevel@tonic-gate }
33687c478bd9Sstevel@tonic-gate 
33697c478bd9Sstevel@tonic-gate 
33707c478bd9Sstevel@tonic-gate /*
33717c478bd9Sstevel@tonic-gate  * ehci_start_timer:
33727c478bd9Sstevel@tonic-gate  *
33737c478bd9Sstevel@tonic-gate  * Start the pipe's timer
33747c478bd9Sstevel@tonic-gate  */
33757c478bd9Sstevel@tonic-gate static void
ehci_start_timer(ehci_state_t * ehcip,ehci_pipe_private_t * pp)33767c478bd9Sstevel@tonic-gate ehci_start_timer(
33777c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
33787c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
33797c478bd9Sstevel@tonic-gate {
33807c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS,  ehcip->ehci_log_hdl,
3381112116d8Sfb 	    "ehci_start_timer: ehcip = 0x%p, pp = 0x%p",
3382112116d8Sfb 	    (void *)ehcip, (void *)pp);
33837c478bd9Sstevel@tonic-gate 
33847c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
33857c478bd9Sstevel@tonic-gate 
33867c478bd9Sstevel@tonic-gate 	/*
33877c478bd9Sstevel@tonic-gate 	 * Start the pipe's timer only if currently timer is not
33887c478bd9Sstevel@tonic-gate 	 * running and if there are any transfers on the timeout
33897c478bd9Sstevel@tonic-gate 	 * list. This timer will be per pipe.
33907c478bd9Sstevel@tonic-gate 	 */
33917c478bd9Sstevel@tonic-gate 	if ((!pp->pp_timer_id) && (pp->pp_timeout_list)) {
33927c478bd9Sstevel@tonic-gate 		pp->pp_timer_id = timeout(ehci_xfer_timeout_handler,
33937c478bd9Sstevel@tonic-gate 		    (void *)(pp->pp_pipe_handle), drv_usectohz(1000000));
33947c478bd9Sstevel@tonic-gate 	}
33957c478bd9Sstevel@tonic-gate }
33967c478bd9Sstevel@tonic-gate 
33977c478bd9Sstevel@tonic-gate /*
33987c478bd9Sstevel@tonic-gate  * ehci_deallocate_tw:
33997c478bd9Sstevel@tonic-gate  *
34007c478bd9Sstevel@tonic-gate  * Deallocate of a Transaction Wrapper (TW) and this involves the freeing of
34017c478bd9Sstevel@tonic-gate  * of DMA resources.
34027c478bd9Sstevel@tonic-gate  */
34037c478bd9Sstevel@tonic-gate void
ehci_deallocate_tw(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw)34047c478bd9Sstevel@tonic-gate ehci_deallocate_tw(
34057c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
34067c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
34077c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw)
34087c478bd9Sstevel@tonic-gate {
34097c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*prev, *next;
34107c478bd9Sstevel@tonic-gate 
34117c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
3412112116d8Sfb 	    "ehci_deallocate_tw: tw = 0x%p", (void *)tw);
34137c478bd9Sstevel@tonic-gate 
34147c478bd9Sstevel@tonic-gate 	/*
34157c478bd9Sstevel@tonic-gate 	 * If the transfer wrapper has no Host Controller (HC)
34167c478bd9Sstevel@tonic-gate 	 * Transfer Descriptors (QTD) associated with it,  then
34177c478bd9Sstevel@tonic-gate 	 * remove the transfer wrapper.
34187c478bd9Sstevel@tonic-gate 	 */
34197c478bd9Sstevel@tonic-gate 	if (tw->tw_qtd_head) {
34207c478bd9Sstevel@tonic-gate 		ASSERT(tw->tw_qtd_tail != NULL);
34217c478bd9Sstevel@tonic-gate 
34227c478bd9Sstevel@tonic-gate 		return;
34237c478bd9Sstevel@tonic-gate 	}
34247c478bd9Sstevel@tonic-gate 
34257c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_qtd_tail == NULL);
34267c478bd9Sstevel@tonic-gate 
34277c478bd9Sstevel@tonic-gate 	/* Make sure we return all the unused qtd's to the pool as well */
34287c478bd9Sstevel@tonic-gate 	ehci_free_tw_td_resources(ehcip, tw);
34297c478bd9Sstevel@tonic-gate 
34307c478bd9Sstevel@tonic-gate 	/*
34317c478bd9Sstevel@tonic-gate 	 * If pp->pp_tw_head and pp->pp_tw_tail are pointing to
34327c478bd9Sstevel@tonic-gate 	 * given TW then set the head and  tail  equal to NULL.
34337c478bd9Sstevel@tonic-gate 	 * Otherwise search for this TW in the linked TW's list
34347c478bd9Sstevel@tonic-gate 	 * and then remove this TW from the list.
34357c478bd9Sstevel@tonic-gate 	 */
34367c478bd9Sstevel@tonic-gate 	if (pp->pp_tw_head == tw) {
34377c478bd9Sstevel@tonic-gate 		if (pp->pp_tw_tail == tw) {
34387c478bd9Sstevel@tonic-gate 			pp->pp_tw_head = NULL;
34397c478bd9Sstevel@tonic-gate 			pp->pp_tw_tail = NULL;
34407c478bd9Sstevel@tonic-gate 		} else {
34417c478bd9Sstevel@tonic-gate 			pp->pp_tw_head = tw->tw_next;
34427c478bd9Sstevel@tonic-gate 		}
34437c478bd9Sstevel@tonic-gate 	} else {
34447c478bd9Sstevel@tonic-gate 		prev = pp->pp_tw_head;
34457c478bd9Sstevel@tonic-gate 		next = prev->tw_next;
34467c478bd9Sstevel@tonic-gate 
34477c478bd9Sstevel@tonic-gate 		while (next && (next != tw)) {
34487c478bd9Sstevel@tonic-gate 			prev = next;
34497c478bd9Sstevel@tonic-gate 			next = next->tw_next;
34507c478bd9Sstevel@tonic-gate 		}
34517c478bd9Sstevel@tonic-gate 
34527c478bd9Sstevel@tonic-gate 		if (next == tw) {
34537c478bd9Sstevel@tonic-gate 			prev->tw_next = next->tw_next;
34547c478bd9Sstevel@tonic-gate 
34557c478bd9Sstevel@tonic-gate 			if (pp->pp_tw_tail == tw) {
34567c478bd9Sstevel@tonic-gate 				pp->pp_tw_tail = prev;
34577c478bd9Sstevel@tonic-gate 			}
34587c478bd9Sstevel@tonic-gate 		}
34597c478bd9Sstevel@tonic-gate 	}
34607c478bd9Sstevel@tonic-gate 
34617c478bd9Sstevel@tonic-gate 	/*
34627c478bd9Sstevel@tonic-gate 	 * Make sure that, this TW has been removed
34637c478bd9Sstevel@tonic-gate 	 * from the timeout list.
34647c478bd9Sstevel@tonic-gate 	 */
34657c478bd9Sstevel@tonic-gate 	ehci_remove_tw_from_timeout_list(ehcip, tw);
34667c478bd9Sstevel@tonic-gate 
34677c478bd9Sstevel@tonic-gate 	/* Deallocate this TW */
34687c478bd9Sstevel@tonic-gate 	ehci_free_tw(ehcip, pp, tw);
34697c478bd9Sstevel@tonic-gate }
34707c478bd9Sstevel@tonic-gate 
34717c478bd9Sstevel@tonic-gate 
34727c478bd9Sstevel@tonic-gate /*
34737c478bd9Sstevel@tonic-gate  * ehci_free_dma_resources:
34747c478bd9Sstevel@tonic-gate  *
34757c478bd9Sstevel@tonic-gate  * Free dma resources of a Transfer Wrapper (TW) and also free the TW.
34767c478bd9Sstevel@tonic-gate  *
34777c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
34787c478bd9Sstevel@tonic-gate  */
34797c478bd9Sstevel@tonic-gate void
ehci_free_dma_resources(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)34807c478bd9Sstevel@tonic-gate ehci_free_dma_resources(
34817c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
34827c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
34837c478bd9Sstevel@tonic-gate {
34847c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
34857c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*head_tw = pp->pp_tw_head;
34867c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*next_tw, *tw;
34877c478bd9Sstevel@tonic-gate 
34887c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
34897c478bd9Sstevel@tonic-gate 	    "ehci_free_dma_resources: ph = 0x%p", (void *)ph);
34907c478bd9Sstevel@tonic-gate 
34917c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
34927c478bd9Sstevel@tonic-gate 
34937c478bd9Sstevel@tonic-gate 	/* Process the Transfer Wrappers */
34947c478bd9Sstevel@tonic-gate 	next_tw = head_tw;
34957c478bd9Sstevel@tonic-gate 	while (next_tw) {
34967c478bd9Sstevel@tonic-gate 		tw = next_tw;
34977c478bd9Sstevel@tonic-gate 		next_tw = tw->tw_next;
34987c478bd9Sstevel@tonic-gate 
34997c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
35007c478bd9Sstevel@tonic-gate 		    "ehci_free_dma_resources: Free TW = 0x%p", (void *)tw);
35017c478bd9Sstevel@tonic-gate 
35027c478bd9Sstevel@tonic-gate 		ehci_free_tw(ehcip, pp, tw);
35037c478bd9Sstevel@tonic-gate 	}
35047c478bd9Sstevel@tonic-gate 
35057c478bd9Sstevel@tonic-gate 	/* Adjust the head and tail pointers */
35067c478bd9Sstevel@tonic-gate 	pp->pp_tw_head = NULL;
35077c478bd9Sstevel@tonic-gate 	pp->pp_tw_tail = NULL;
35087c478bd9Sstevel@tonic-gate }
35097c478bd9Sstevel@tonic-gate 
35107c478bd9Sstevel@tonic-gate 
35117c478bd9Sstevel@tonic-gate /*
35127c478bd9Sstevel@tonic-gate  * ehci_free_tw:
35137c478bd9Sstevel@tonic-gate  *
35147c478bd9Sstevel@tonic-gate  * Free the Transfer Wrapper (TW).
35157c478bd9Sstevel@tonic-gate  */
35167c478bd9Sstevel@tonic-gate /*ARGSUSED*/
35177c478bd9Sstevel@tonic-gate static void
ehci_free_tw(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw)35187c478bd9Sstevel@tonic-gate ehci_free_tw(
35197c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
35207c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
35217c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw)
35227c478bd9Sstevel@tonic-gate {
35237c478bd9Sstevel@tonic-gate 	int	rval;
3524dfbb3a42SRaymond Chen 	usb_ep_descr_t	*eptd = &pp->pp_pipe_handle->p_ep;
35257c478bd9Sstevel@tonic-gate 
35267c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
3527112116d8Sfb 	    "ehci_free_tw: tw = 0x%p", (void *)tw);
35287c478bd9Sstevel@tonic-gate 
35297c478bd9Sstevel@tonic-gate 	ASSERT(tw != NULL);
3530*0a1044f1SToomas Soome 	ASSERT(tw->tw_id != 0);
35317c478bd9Sstevel@tonic-gate 
35327c478bd9Sstevel@tonic-gate 	/* Free 32bit ID */
35337c478bd9Sstevel@tonic-gate 	EHCI_FREE_ID((uint32_t)tw->tw_id);
35347c478bd9Sstevel@tonic-gate 
3535688b07c5Sgc 	if (tw->tw_dmahandle != NULL) {
3536688b07c5Sgc 		rval = ddi_dma_unbind_handle(tw->tw_dmahandle);
3537688b07c5Sgc 		ASSERT(rval == DDI_SUCCESS);
35387c478bd9Sstevel@tonic-gate 
3539688b07c5Sgc 		ddi_dma_mem_free(&tw->tw_accesshandle);
3540688b07c5Sgc 		ddi_dma_free_handle(&tw->tw_dmahandle);
3541688b07c5Sgc 	}
35427c478bd9Sstevel@tonic-gate 
3543dfbb3a42SRaymond Chen 	/* interrupt ep will come to this point */
3544dfbb3a42SRaymond Chen 	if (EHCI_INTR_ENDPOINT(eptd)) {
3545dfbb3a42SRaymond Chen 		ehcip->ehci_periodic_req_count--;
3546dfbb3a42SRaymond Chen 	} else {
3547dfbb3a42SRaymond Chen 		ehcip->ehci_async_req_count--;
3548dfbb3a42SRaymond Chen 	}
3549dfbb3a42SRaymond Chen 	ehci_toggle_scheduler(ehcip);
3550dfbb3a42SRaymond Chen 
35517c478bd9Sstevel@tonic-gate 	/* Free transfer wrapper */
35527c478bd9Sstevel@tonic-gate 	kmem_free(tw, sizeof (ehci_trans_wrapper_t));
35537c478bd9Sstevel@tonic-gate }
35547c478bd9Sstevel@tonic-gate 
35557c478bd9Sstevel@tonic-gate 
35567c478bd9Sstevel@tonic-gate /*
35577c478bd9Sstevel@tonic-gate  * Miscellaneous functions
35587c478bd9Sstevel@tonic-gate  */
35597c478bd9Sstevel@tonic-gate 
35607c478bd9Sstevel@tonic-gate /*
35617c478bd9Sstevel@tonic-gate  * ehci_allocate_intr_in_resource
35627c478bd9Sstevel@tonic-gate  *
35637c478bd9Sstevel@tonic-gate  * Allocate interrupt request structure for the interrupt IN transfer.
35647c478bd9Sstevel@tonic-gate  */
35657c478bd9Sstevel@tonic-gate /*ARGSUSED*/
35667c478bd9Sstevel@tonic-gate int
ehci_allocate_intr_in_resource(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw,usb_flags_t flags)35677c478bd9Sstevel@tonic-gate ehci_allocate_intr_in_resource(
35687c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
35697c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
35707c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw,
35717c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
35727c478bd9Sstevel@tonic-gate {
35737c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
35747c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
35757c478bd9Sstevel@tonic-gate 	usb_opaque_t		client_periodic_in_reqp;
35767c478bd9Sstevel@tonic-gate 	size_t			length = 0;
35777c478bd9Sstevel@tonic-gate 
35787c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
35797c478bd9Sstevel@tonic-gate 	    "ehci_allocate_intr_in_resource:"
3580112116d8Sfb 	    "pp = 0x%p tw = 0x%p flags = 0x%x", (void *)pp, (void *)tw, flags);
35817c478bd9Sstevel@tonic-gate 
35827c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
35837c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_curr_xfer_reqp == NULL);
35847c478bd9Sstevel@tonic-gate 
35857c478bd9Sstevel@tonic-gate 	/* Get the client periodic in request pointer */
35867c478bd9Sstevel@tonic-gate 	client_periodic_in_reqp = pp->pp_client_periodic_in_reqp;
35877c478bd9Sstevel@tonic-gate 
35887c478bd9Sstevel@tonic-gate 	/*
35897c478bd9Sstevel@tonic-gate 	 * If it a periodic IN request and periodic request is NULL,
35907c478bd9Sstevel@tonic-gate 	 * allocate corresponding usb periodic IN request for the
35917c478bd9Sstevel@tonic-gate 	 * current periodic polling request and copy the information
35927c478bd9Sstevel@tonic-gate 	 * from the saved periodic request structure.
35937c478bd9Sstevel@tonic-gate 	 */
35947c478bd9Sstevel@tonic-gate 	if (client_periodic_in_reqp) {
35957c478bd9Sstevel@tonic-gate 
35967c478bd9Sstevel@tonic-gate 		/* Get the interrupt transfer length */
35977c478bd9Sstevel@tonic-gate 		length = ((usb_intr_req_t *)
35987c478bd9Sstevel@tonic-gate 		    client_periodic_in_reqp)->intr_len;
35997c478bd9Sstevel@tonic-gate 
36007c478bd9Sstevel@tonic-gate 		curr_intr_reqp = usba_hcdi_dup_intr_req(ph->p_dip,
36017c478bd9Sstevel@tonic-gate 		    (usb_intr_req_t *)client_periodic_in_reqp, length, flags);
36027c478bd9Sstevel@tonic-gate 	} else {
36037c478bd9Sstevel@tonic-gate 		curr_intr_reqp = usb_alloc_intr_req(ph->p_dip, length, flags);
36047c478bd9Sstevel@tonic-gate 	}
36057c478bd9Sstevel@tonic-gate 
36067c478bd9Sstevel@tonic-gate 	if (curr_intr_reqp == NULL) {
36077c478bd9Sstevel@tonic-gate 
36087c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
36097c478bd9Sstevel@tonic-gate 		    "ehci_allocate_intr_in_resource: Interrupt"
36107c478bd9Sstevel@tonic-gate 		    "request structure allocation failed");
36117c478bd9Sstevel@tonic-gate 
36127c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
36137c478bd9Sstevel@tonic-gate 	}
36147c478bd9Sstevel@tonic-gate 
36157c478bd9Sstevel@tonic-gate 	/* For polled mode */
36167c478bd9Sstevel@tonic-gate 	if (client_periodic_in_reqp == NULL) {
36177c478bd9Sstevel@tonic-gate 		curr_intr_reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK;
36187c478bd9Sstevel@tonic-gate 		curr_intr_reqp->intr_len = ph->p_ep.wMaxPacketSize;
36197c478bd9Sstevel@tonic-gate 	} else {
36207c478bd9Sstevel@tonic-gate 		/* Check and save the timeout value */
36217c478bd9Sstevel@tonic-gate 		tw->tw_timeout = (curr_intr_reqp->intr_attributes &
36227c478bd9Sstevel@tonic-gate 		    USB_ATTRS_ONE_XFER) ? curr_intr_reqp->intr_timeout: 0;
36237c478bd9Sstevel@tonic-gate 	}
36247c478bd9Sstevel@tonic-gate 
36257c478bd9Sstevel@tonic-gate 	tw->tw_curr_xfer_reqp = (usb_opaque_t)curr_intr_reqp;
36267c478bd9Sstevel@tonic-gate 	tw->tw_length = curr_intr_reqp->intr_len;
36277c478bd9Sstevel@tonic-gate 
36287c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
36297c478bd9Sstevel@tonic-gate 	ph->p_req_count++;
36307c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
36317c478bd9Sstevel@tonic-gate 
36327c478bd9Sstevel@tonic-gate 	pp->pp_state = EHCI_PIPE_STATE_ACTIVE;
36337c478bd9Sstevel@tonic-gate 
36347c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
36357c478bd9Sstevel@tonic-gate }
36367c478bd9Sstevel@tonic-gate 
36377c478bd9Sstevel@tonic-gate /*
36387c478bd9Sstevel@tonic-gate  * ehci_pipe_cleanup
36397c478bd9Sstevel@tonic-gate  *
36407c478bd9Sstevel@tonic-gate  * Cleanup ehci pipe.
36417c478bd9Sstevel@tonic-gate  */
36427c478bd9Sstevel@tonic-gate void
ehci_pipe_cleanup(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)36437c478bd9Sstevel@tonic-gate ehci_pipe_cleanup(
36447c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
36457c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
36467c478bd9Sstevel@tonic-gate {
36477c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
36487c478bd9Sstevel@tonic-gate 	uint_t			pipe_state = pp->pp_state;
36497c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason;
36507c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
36517c478bd9Sstevel@tonic-gate 
36527c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
3653112116d8Sfb 	    "ehci_pipe_cleanup: ph = 0x%p", (void *)ph);
36547c478bd9Sstevel@tonic-gate 
36557c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
36567c478bd9Sstevel@tonic-gate 
36577c478bd9Sstevel@tonic-gate 	if (EHCI_ISOC_ENDPOINT(eptd)) {
36587c478bd9Sstevel@tonic-gate 		ehci_isoc_pipe_cleanup(ehcip, ph);
36597c478bd9Sstevel@tonic-gate 
36607c478bd9Sstevel@tonic-gate 		return;
36617c478bd9Sstevel@tonic-gate 	}
36627c478bd9Sstevel@tonic-gate 
36637c478bd9Sstevel@tonic-gate 	ASSERT(!servicing_interrupt());
36647c478bd9Sstevel@tonic-gate 
36657c478bd9Sstevel@tonic-gate 	/*
36667c478bd9Sstevel@tonic-gate 	 * Set the QH's status to Halt condition.
36677c478bd9Sstevel@tonic-gate 	 * If another thread is halting this function will automatically
36687c478bd9Sstevel@tonic-gate 	 * wait. If a pipe close happens at this time
36697c478bd9Sstevel@tonic-gate 	 * we will be in lots of trouble.
36707c478bd9Sstevel@tonic-gate 	 * If we are in an interrupt thread, don't halt, because it may
36717c478bd9Sstevel@tonic-gate 	 * do a wait_for_sof.
36727c478bd9Sstevel@tonic-gate 	 */
36737c478bd9Sstevel@tonic-gate 	ehci_modify_qh_status_bit(ehcip, pp, SET_HALT);
36747c478bd9Sstevel@tonic-gate 
36757c478bd9Sstevel@tonic-gate 	/*
36767c478bd9Sstevel@tonic-gate 	 * Wait for processing all completed transfers and
36777c478bd9Sstevel@tonic-gate 	 * to send results to upstream.
36787c478bd9Sstevel@tonic-gate 	 */
36797c478bd9Sstevel@tonic-gate 	ehci_wait_for_transfers_completion(ehcip, pp);
36807c478bd9Sstevel@tonic-gate 
36817c478bd9Sstevel@tonic-gate 	/* Save the data toggle information */
36827c478bd9Sstevel@tonic-gate 	ehci_save_data_toggle(ehcip, ph);
36837c478bd9Sstevel@tonic-gate 
36847c478bd9Sstevel@tonic-gate 	/*
36857c478bd9Sstevel@tonic-gate 	 * Traverse the list of QTDs for this pipe using transfer
36867c478bd9Sstevel@tonic-gate 	 * wrapper. Process these QTDs depending on their status.
36877c478bd9Sstevel@tonic-gate 	 * And stop the timer of this pipe.
36887c478bd9Sstevel@tonic-gate 	 */
36897c478bd9Sstevel@tonic-gate 	ehci_traverse_qtds(ehcip, ph);
36907c478bd9Sstevel@tonic-gate 
36917c478bd9Sstevel@tonic-gate 	/* Make sure the timer is not running */
36927c478bd9Sstevel@tonic-gate 	ASSERT(pp->pp_timer_id == 0);
36937c478bd9Sstevel@tonic-gate 
36947c478bd9Sstevel@tonic-gate 	/* Do callbacks for all unfinished requests */
36957c478bd9Sstevel@tonic-gate 	ehci_handle_outstanding_requests(ehcip, pp);
36967c478bd9Sstevel@tonic-gate 
36977c478bd9Sstevel@tonic-gate 	/* Free DMA resources */
36987c478bd9Sstevel@tonic-gate 	ehci_free_dma_resources(ehcip, ph);
36997c478bd9Sstevel@tonic-gate 
37007c478bd9Sstevel@tonic-gate 	switch (pipe_state) {
37017c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_CLOSE:
37027c478bd9Sstevel@tonic-gate 		completion_reason = USB_CR_PIPE_CLOSING;
37037c478bd9Sstevel@tonic-gate 		break;
37047c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_RESET:
37057c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_STOP_POLLING:
37067c478bd9Sstevel@tonic-gate 		/* Set completion reason */
37077c478bd9Sstevel@tonic-gate 		completion_reason = (pipe_state ==
37087c478bd9Sstevel@tonic-gate 		    EHCI_PIPE_STATE_RESET) ?
37097c478bd9Sstevel@tonic-gate 		    USB_CR_PIPE_RESET: USB_CR_STOPPED_POLLING;
37107c478bd9Sstevel@tonic-gate 
37117c478bd9Sstevel@tonic-gate 		/* Restore the data toggle information */
37127c478bd9Sstevel@tonic-gate 		ehci_restore_data_toggle(ehcip, ph);
37137c478bd9Sstevel@tonic-gate 
37147c478bd9Sstevel@tonic-gate 		/*
37157c478bd9Sstevel@tonic-gate 		 * Clear the halt bit to restart all the
37167c478bd9Sstevel@tonic-gate 		 * transactions on this pipe.
37177c478bd9Sstevel@tonic-gate 		 */
37187c478bd9Sstevel@tonic-gate 		ehci_modify_qh_status_bit(ehcip, pp, CLEAR_HALT);
37197c478bd9Sstevel@tonic-gate 
37207c478bd9Sstevel@tonic-gate 		/* Set pipe state to idle */
37217c478bd9Sstevel@tonic-gate 		pp->pp_state = EHCI_PIPE_STATE_IDLE;
37227c478bd9Sstevel@tonic-gate 
37237c478bd9Sstevel@tonic-gate 		break;
37247c478bd9Sstevel@tonic-gate 	}
37257c478bd9Sstevel@tonic-gate 
37267c478bd9Sstevel@tonic-gate 	/*
37277c478bd9Sstevel@tonic-gate 	 * Do the callback for the original client
37287c478bd9Sstevel@tonic-gate 	 * periodic IN request.
37297c478bd9Sstevel@tonic-gate 	 */
37307c478bd9Sstevel@tonic-gate 	if ((EHCI_PERIODIC_ENDPOINT(eptd)) &&
37317c478bd9Sstevel@tonic-gate 	    ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) ==
37327c478bd9Sstevel@tonic-gate 	    USB_EP_DIR_IN)) {
37337c478bd9Sstevel@tonic-gate 
37347c478bd9Sstevel@tonic-gate 		ehci_do_client_periodic_in_req_callback(
37357c478bd9Sstevel@tonic-gate 		    ehcip, pp, completion_reason);
37367c478bd9Sstevel@tonic-gate 	}
37377c478bd9Sstevel@tonic-gate }
37387c478bd9Sstevel@tonic-gate 
37397c478bd9Sstevel@tonic-gate 
37407c478bd9Sstevel@tonic-gate /*
37417c478bd9Sstevel@tonic-gate  * ehci_wait_for_transfers_completion:
37427c478bd9Sstevel@tonic-gate  *
37437c478bd9Sstevel@tonic-gate  * Wait for processing all completed transfers and to send results
37447c478bd9Sstevel@tonic-gate  * to upstream.
37457c478bd9Sstevel@tonic-gate  */
37467c478bd9Sstevel@tonic-gate static void
ehci_wait_for_transfers_completion(ehci_state_t * ehcip,ehci_pipe_private_t * pp)37477c478bd9Sstevel@tonic-gate ehci_wait_for_transfers_completion(
37487c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
37497c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
37507c478bd9Sstevel@tonic-gate {
37517c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*next_tw = pp->pp_tw_head;
37527c478bd9Sstevel@tonic-gate 	ehci_qtd_t		*qtd;
37537c478bd9Sstevel@tonic-gate 
37547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS,
37557c478bd9Sstevel@tonic-gate 	    ehcip->ehci_log_hdl,
3756112116d8Sfb 	    "ehci_wait_for_transfers_completion: pp = 0x%p", (void *)pp);
37577c478bd9Sstevel@tonic-gate 
37587c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
37597c478bd9Sstevel@tonic-gate 
37607c478bd9Sstevel@tonic-gate 	if ((ehci_state_is_operational(ehcip)) != USB_SUCCESS) {
37617c478bd9Sstevel@tonic-gate 
37627c478bd9Sstevel@tonic-gate 		return;
37637c478bd9Sstevel@tonic-gate 	}
37647c478bd9Sstevel@tonic-gate 
37657c478bd9Sstevel@tonic-gate 	pp->pp_count_done_qtds = 0;
37667c478bd9Sstevel@tonic-gate 
37677c478bd9Sstevel@tonic-gate 	/* Process the transfer wrappers for this pipe */
37687c478bd9Sstevel@tonic-gate 	while (next_tw) {
37697c478bd9Sstevel@tonic-gate 		qtd = (ehci_qtd_t *)next_tw->tw_qtd_head;
37707c478bd9Sstevel@tonic-gate 
37717c478bd9Sstevel@tonic-gate 		/*
37727c478bd9Sstevel@tonic-gate 		 * Walk through each QTD for this transfer wrapper.
37737c478bd9Sstevel@tonic-gate 		 * If a QTD still exists, then it is either on done
37747c478bd9Sstevel@tonic-gate 		 * list or on the QH's list.
37757c478bd9Sstevel@tonic-gate 		 */
37767c478bd9Sstevel@tonic-gate 		while (qtd) {
37777c478bd9Sstevel@tonic-gate 			if (!(Get_QTD(qtd->qtd_ctrl) &
37787c478bd9Sstevel@tonic-gate 			    EHCI_QTD_CTRL_ACTIVE_XACT)) {
37797c478bd9Sstevel@tonic-gate 				pp->pp_count_done_qtds++;
37807c478bd9Sstevel@tonic-gate 			}
37817c478bd9Sstevel@tonic-gate 
37827c478bd9Sstevel@tonic-gate 			qtd = ehci_qtd_iommu_to_cpu(ehcip,
37837c478bd9Sstevel@tonic-gate 			    Get_QTD(qtd->qtd_tw_next_qtd));
37847c478bd9Sstevel@tonic-gate 		}
37857c478bd9Sstevel@tonic-gate 
37867c478bd9Sstevel@tonic-gate 		next_tw = next_tw->tw_next;
37877c478bd9Sstevel@tonic-gate 	}
37887c478bd9Sstevel@tonic-gate 
37897c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
37907c478bd9Sstevel@tonic-gate 	    "ehci_wait_for_transfers_completion: count_done_qtds = 0x%x",
37917c478bd9Sstevel@tonic-gate 	    pp->pp_count_done_qtds);
37927c478bd9Sstevel@tonic-gate 
37937c478bd9Sstevel@tonic-gate 	if (!pp->pp_count_done_qtds) {
37947c478bd9Sstevel@tonic-gate 
37957c478bd9Sstevel@tonic-gate 		return;
37967c478bd9Sstevel@tonic-gate 	}
37977c478bd9Sstevel@tonic-gate 
3798d3d50737SRafael Vanoni 	(void) cv_reltimedwait(&pp->pp_xfer_cmpl_cv, &ehcip->ehci_int_mutex,
3799d3d50737SRafael Vanoni 	    drv_usectohz(EHCI_XFER_CMPL_TIMEWAIT * 1000000), TR_CLOCK_TICK);
38007c478bd9Sstevel@tonic-gate 
38017c478bd9Sstevel@tonic-gate 	if (pp->pp_count_done_qtds) {
38027c478bd9Sstevel@tonic-gate 
38037c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
38047c478bd9Sstevel@tonic-gate 		    "ehci_wait_for_transfers_completion:"
38057c478bd9Sstevel@tonic-gate 		    "No transfers completion confirmation received");
38067c478bd9Sstevel@tonic-gate 	}
38077c478bd9Sstevel@tonic-gate }
38087c478bd9Sstevel@tonic-gate 
38097c478bd9Sstevel@tonic-gate /*
38107c478bd9Sstevel@tonic-gate  * ehci_check_for_transfers_completion:
38117c478bd9Sstevel@tonic-gate  *
38127c478bd9Sstevel@tonic-gate  * Check whether anybody is waiting for transfers completion event. If so, send
38137c478bd9Sstevel@tonic-gate  * this event and also stop initiating any new transfers on this pipe.
38147c478bd9Sstevel@tonic-gate  */
38157c478bd9Sstevel@tonic-gate void
ehci_check_for_transfers_completion(ehci_state_t * ehcip,ehci_pipe_private_t * pp)38167c478bd9Sstevel@tonic-gate ehci_check_for_transfers_completion(
38177c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
38187c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
38197c478bd9Sstevel@tonic-gate {
38207c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS,
38217c478bd9Sstevel@tonic-gate 	    ehcip->ehci_log_hdl,
3822112116d8Sfb 	    "ehci_check_for_transfers_completion: pp = 0x%p", (void *)pp);
38237c478bd9Sstevel@tonic-gate 
38247c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
38257c478bd9Sstevel@tonic-gate 
38267c478bd9Sstevel@tonic-gate 	if ((pp->pp_state == EHCI_PIPE_STATE_STOP_POLLING) &&
38277c478bd9Sstevel@tonic-gate 	    (pp->pp_error == USB_CR_NO_RESOURCES) &&
38287c478bd9Sstevel@tonic-gate 	    (pp->pp_cur_periodic_req_cnt == 0)) {
38297c478bd9Sstevel@tonic-gate 
38307c478bd9Sstevel@tonic-gate 		/* Reset pipe error to zero */
38317c478bd9Sstevel@tonic-gate 		pp->pp_error = 0;
38327c478bd9Sstevel@tonic-gate 
38337c478bd9Sstevel@tonic-gate 		/* Do callback for original request */
38347c478bd9Sstevel@tonic-gate 		ehci_do_client_periodic_in_req_callback(
38357c478bd9Sstevel@tonic-gate 		    ehcip, pp, USB_CR_NO_RESOURCES);
38367c478bd9Sstevel@tonic-gate 	}
38377c478bd9Sstevel@tonic-gate 
38387c478bd9Sstevel@tonic-gate 	if (pp->pp_count_done_qtds) {
38397c478bd9Sstevel@tonic-gate 
38407c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
38417c478bd9Sstevel@tonic-gate 		    "ehci_check_for_transfers_completion:"
38427c478bd9Sstevel@tonic-gate 		    "count_done_qtds = 0x%x", pp->pp_count_done_qtds);
38437c478bd9Sstevel@tonic-gate 
38447c478bd9Sstevel@tonic-gate 		/* Decrement the done qtd count */
38457c478bd9Sstevel@tonic-gate 		pp->pp_count_done_qtds--;
38467c478bd9Sstevel@tonic-gate 
38477c478bd9Sstevel@tonic-gate 		if (!pp->pp_count_done_qtds) {
38487c478bd9Sstevel@tonic-gate 
38497c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
38507c478bd9Sstevel@tonic-gate 			    "ehci_check_for_transfers_completion:"
3851112116d8Sfb 			    "Sent transfers completion event pp = 0x%p",
3852112116d8Sfb 			    (void *)pp);
38537c478bd9Sstevel@tonic-gate 
38547c478bd9Sstevel@tonic-gate 			/* Send the transfer completion signal */
38557c478bd9Sstevel@tonic-gate 			cv_signal(&pp->pp_xfer_cmpl_cv);
38567c478bd9Sstevel@tonic-gate 		}
38577c478bd9Sstevel@tonic-gate 	}
38587c478bd9Sstevel@tonic-gate }
38597c478bd9Sstevel@tonic-gate 
38607c478bd9Sstevel@tonic-gate 
38617c478bd9Sstevel@tonic-gate /*
38627c478bd9Sstevel@tonic-gate  * ehci_save_data_toggle:
38637c478bd9Sstevel@tonic-gate  *
38647c478bd9Sstevel@tonic-gate  * Save the data toggle information.
38657c478bd9Sstevel@tonic-gate  */
38667c478bd9Sstevel@tonic-gate static void
ehci_save_data_toggle(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)38677c478bd9Sstevel@tonic-gate ehci_save_data_toggle(
38687c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
38697c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
38707c478bd9Sstevel@tonic-gate {
38717c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
38727c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
38737c478bd9Sstevel@tonic-gate 	uint_t			data_toggle;
38747c478bd9Sstevel@tonic-gate 	usb_cr_t		error = pp->pp_error;
38757c478bd9Sstevel@tonic-gate 	ehci_qh_t		*qh = pp->pp_qh;
38767c478bd9Sstevel@tonic-gate 
38777c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS,
38787c478bd9Sstevel@tonic-gate 	    ehcip->ehci_log_hdl,
3879112116d8Sfb 	    "ehci_save_data_toggle: ph = 0x%p", (void *)ph);
38807c478bd9Sstevel@tonic-gate 
38817c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
38827c478bd9Sstevel@tonic-gate 
38837c478bd9Sstevel@tonic-gate 	/* Reset the pipe error value */
38847c478bd9Sstevel@tonic-gate 	pp->pp_error = USB_CR_OK;
38857c478bd9Sstevel@tonic-gate 
38867c478bd9Sstevel@tonic-gate 	/* Return immediately if it is a control pipe */
38877c478bd9Sstevel@tonic-gate 	if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
38887c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_CONTROL) {
38897c478bd9Sstevel@tonic-gate 
38907c478bd9Sstevel@tonic-gate 		return;
38917c478bd9Sstevel@tonic-gate 	}
38927c478bd9Sstevel@tonic-gate 
38937c478bd9Sstevel@tonic-gate 	/* Get the data toggle information from the endpoint (QH) */
38947c478bd9Sstevel@tonic-gate 	data_toggle = (Get_QH(qh->qh_status) &
38957c478bd9Sstevel@tonic-gate 	    EHCI_QH_STS_DATA_TOGGLE)? DATA1:DATA0;
38967c478bd9Sstevel@tonic-gate 
38977c478bd9Sstevel@tonic-gate 	/*
38987c478bd9Sstevel@tonic-gate 	 * If error is STALL, then, set
38997c478bd9Sstevel@tonic-gate 	 * data toggle to zero.
39007c478bd9Sstevel@tonic-gate 	 */
39017c478bd9Sstevel@tonic-gate 	if (error == USB_CR_STALL) {
39027c478bd9Sstevel@tonic-gate 		data_toggle = DATA0;
39037c478bd9Sstevel@tonic-gate 	}
39047c478bd9Sstevel@tonic-gate 
39057c478bd9Sstevel@tonic-gate 	/*
39067c478bd9Sstevel@tonic-gate 	 * Save the data toggle information
39077c478bd9Sstevel@tonic-gate 	 * in the usb device structure.
39087c478bd9Sstevel@tonic-gate 	 */
39097c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
39107c478bd9Sstevel@tonic-gate 	usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
39117c478bd9Sstevel@tonic-gate 	    data_toggle);
39127c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
39137c478bd9Sstevel@tonic-gate }
39147c478bd9Sstevel@tonic-gate 
39157c478bd9Sstevel@tonic-gate 
39167c478bd9Sstevel@tonic-gate /*
39177c478bd9Sstevel@tonic-gate  * ehci_restore_data_toggle:
39187c478bd9Sstevel@tonic-gate  *
39197c478bd9Sstevel@tonic-gate  * Restore the data toggle information.
39207c478bd9Sstevel@tonic-gate  */
39217c478bd9Sstevel@tonic-gate void
ehci_restore_data_toggle(ehci_state_t * ehcip,usba_pipe_handle_data_t * ph)39227c478bd9Sstevel@tonic-gate ehci_restore_data_toggle(
39237c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
39247c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
39257c478bd9Sstevel@tonic-gate {
39267c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
39277c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
39287c478bd9Sstevel@tonic-gate 	uint_t			data_toggle = 0;
39297c478bd9Sstevel@tonic-gate 
39307c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS,
39317c478bd9Sstevel@tonic-gate 	    ehcip->ehci_log_hdl,
3932112116d8Sfb 	    "ehci_restore_data_toggle: ph = 0x%p", (void *)ph);
39337c478bd9Sstevel@tonic-gate 
39347c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
39357c478bd9Sstevel@tonic-gate 
39367c478bd9Sstevel@tonic-gate 	/* Return immediately if it is a control pipe */
39377c478bd9Sstevel@tonic-gate 	if ((eptd->bmAttributes & USB_EP_ATTR_MASK) ==
39387c478bd9Sstevel@tonic-gate 	    USB_EP_ATTR_CONTROL) {
39397c478bd9Sstevel@tonic-gate 
39407c478bd9Sstevel@tonic-gate 		return;
39417c478bd9Sstevel@tonic-gate 	}
39427c478bd9Sstevel@tonic-gate 
39437c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
39447c478bd9Sstevel@tonic-gate 
39457c478bd9Sstevel@tonic-gate 	data_toggle = usba_hcdi_get_data_toggle(ph->p_usba_device,
39467c478bd9Sstevel@tonic-gate 	    ph->p_ep.bEndpointAddress);
39477c478bd9Sstevel@tonic-gate 	usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
39487c478bd9Sstevel@tonic-gate 	    0);
39497c478bd9Sstevel@tonic-gate 
39507c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
39517c478bd9Sstevel@tonic-gate 
39527c478bd9Sstevel@tonic-gate 	/*
39537c478bd9Sstevel@tonic-gate 	 * Restore the data toggle bit depending on the
39547c478bd9Sstevel@tonic-gate 	 * previous data toggle information.
39557c478bd9Sstevel@tonic-gate 	 */
39567c478bd9Sstevel@tonic-gate 	if (data_toggle) {
39577c478bd9Sstevel@tonic-gate 		Set_QH(pp->pp_qh->qh_status,
39587c478bd9Sstevel@tonic-gate 		    Get_QH(pp->pp_qh->qh_status) | EHCI_QH_STS_DATA_TOGGLE);
39597c478bd9Sstevel@tonic-gate 	} else {
39607c478bd9Sstevel@tonic-gate 		Set_QH(pp->pp_qh->qh_status,
39617c478bd9Sstevel@tonic-gate 		    Get_QH(pp->pp_qh->qh_status) & (~EHCI_QH_STS_DATA_TOGGLE));
39627c478bd9Sstevel@tonic-gate 	}
39637c478bd9Sstevel@tonic-gate }
39647c478bd9Sstevel@tonic-gate 
39657c478bd9Sstevel@tonic-gate 
39667c478bd9Sstevel@tonic-gate /*
39677c478bd9Sstevel@tonic-gate  * ehci_handle_outstanding_requests
39687c478bd9Sstevel@tonic-gate  *
39697c478bd9Sstevel@tonic-gate  * Deallocate interrupt request structure for the interrupt IN transfer.
39707c478bd9Sstevel@tonic-gate  * Do the callbacks for all unfinished requests.
39717c478bd9Sstevel@tonic-gate  *
39727c478bd9Sstevel@tonic-gate  * NOTE: This function is also called from POLLED MODE.
39737c478bd9Sstevel@tonic-gate  */
39747c478bd9Sstevel@tonic-gate void
ehci_handle_outstanding_requests(ehci_state_t * ehcip,ehci_pipe_private_t * pp)39757c478bd9Sstevel@tonic-gate ehci_handle_outstanding_requests(
39767c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
39777c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp)
39787c478bd9Sstevel@tonic-gate {
39797c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
39807c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
39817c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*curr_tw;
39827c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*next_tw;
39837c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_xfer_reqp;
39847c478bd9Sstevel@tonic-gate 
39857c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS,
39867c478bd9Sstevel@tonic-gate 	    ehcip->ehci_log_hdl,
3987112116d8Sfb 	    "ehci_handle_outstanding_requests: pp = 0x%p", (void *)pp);
39887c478bd9Sstevel@tonic-gate 
39897c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
39907c478bd9Sstevel@tonic-gate 
39917c478bd9Sstevel@tonic-gate 	/* Deallocate all pre-allocated interrupt requests */
39927c478bd9Sstevel@tonic-gate 	next_tw = pp->pp_tw_head;
39937c478bd9Sstevel@tonic-gate 
39947c478bd9Sstevel@tonic-gate 	while (next_tw) {
39957c478bd9Sstevel@tonic-gate 		curr_tw = next_tw;
39967c478bd9Sstevel@tonic-gate 		next_tw = curr_tw->tw_next;
39977c478bd9Sstevel@tonic-gate 
39987c478bd9Sstevel@tonic-gate 		curr_xfer_reqp = curr_tw->tw_curr_xfer_reqp;
39997c478bd9Sstevel@tonic-gate 
40007c478bd9Sstevel@tonic-gate 		/* Deallocate current interrupt request */
40017c478bd9Sstevel@tonic-gate 		if (curr_xfer_reqp) {
40027c478bd9Sstevel@tonic-gate 
40037c478bd9Sstevel@tonic-gate 			if ((EHCI_PERIODIC_ENDPOINT(eptd)) &&
40047c478bd9Sstevel@tonic-gate 			    (curr_tw->tw_direction == EHCI_QTD_CTRL_IN_PID)) {
40057c478bd9Sstevel@tonic-gate 
40067c478bd9Sstevel@tonic-gate 				/* Decrement periodic in request count */
40077c478bd9Sstevel@tonic-gate 				pp->pp_cur_periodic_req_cnt--;
40087c478bd9Sstevel@tonic-gate 
40097c478bd9Sstevel@tonic-gate 				ehci_deallocate_intr_in_resource(
40107c478bd9Sstevel@tonic-gate 				    ehcip, pp, curr_tw);
40117c478bd9Sstevel@tonic-gate 			} else {
40127c478bd9Sstevel@tonic-gate 				ehci_hcdi_callback(ph, curr_tw, USB_CR_FLUSHED);
40137c478bd9Sstevel@tonic-gate 			}
40147c478bd9Sstevel@tonic-gate 		}
40157c478bd9Sstevel@tonic-gate 	}
40167c478bd9Sstevel@tonic-gate }
40177c478bd9Sstevel@tonic-gate 
40187c478bd9Sstevel@tonic-gate 
40197c478bd9Sstevel@tonic-gate /*
40207c478bd9Sstevel@tonic-gate  * ehci_deallocate_intr_in_resource
40217c478bd9Sstevel@tonic-gate  *
40227c478bd9Sstevel@tonic-gate  * Deallocate interrupt request structure for the interrupt IN transfer.
40237c478bd9Sstevel@tonic-gate  */
40247c478bd9Sstevel@tonic-gate void
ehci_deallocate_intr_in_resource(ehci_state_t * ehcip,ehci_pipe_private_t * pp,ehci_trans_wrapper_t * tw)40257c478bd9Sstevel@tonic-gate ehci_deallocate_intr_in_resource(
40267c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
40277c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
40287c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw)
40297c478bd9Sstevel@tonic-gate {
40307c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
40317c478bd9Sstevel@tonic-gate 	uchar_t			ep_attr = ph->p_ep.bmAttributes;
40327c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_xfer_reqp;
40337c478bd9Sstevel@tonic-gate 
40347c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS,
40357c478bd9Sstevel@tonic-gate 	    ehcip->ehci_log_hdl,
40367c478bd9Sstevel@tonic-gate 	    "ehci_deallocate_intr_in_resource: "
4037112116d8Sfb 	    "pp = 0x%p tw = 0x%p", (void *)pp, (void *)tw);
40387c478bd9Sstevel@tonic-gate 
40397c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
40407c478bd9Sstevel@tonic-gate 	ASSERT((ep_attr & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTR);
40417c478bd9Sstevel@tonic-gate 
40427c478bd9Sstevel@tonic-gate 	curr_xfer_reqp = tw->tw_curr_xfer_reqp;
40437c478bd9Sstevel@tonic-gate 
40447c478bd9Sstevel@tonic-gate 	/* Check the current periodic in request pointer */
40457c478bd9Sstevel@tonic-gate 	if (curr_xfer_reqp) {
40467c478bd9Sstevel@tonic-gate 
40477c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp = NULL;
40487c478bd9Sstevel@tonic-gate 
40497c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
40507c478bd9Sstevel@tonic-gate 		ph->p_req_count--;
40517c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
40527c478bd9Sstevel@tonic-gate 
40537c478bd9Sstevel@tonic-gate 		/* Free pre-allocated interrupt requests */
40547c478bd9Sstevel@tonic-gate 		usb_free_intr_req((usb_intr_req_t *)curr_xfer_reqp);
40557c478bd9Sstevel@tonic-gate 
40567c478bd9Sstevel@tonic-gate 		/* Set periodic in pipe state to idle */
40577c478bd9Sstevel@tonic-gate 		pp->pp_state = EHCI_PIPE_STATE_IDLE;
40587c478bd9Sstevel@tonic-gate 	}
40597c478bd9Sstevel@tonic-gate }
40607c478bd9Sstevel@tonic-gate 
40617c478bd9Sstevel@tonic-gate 
40627c478bd9Sstevel@tonic-gate /*
40637c478bd9Sstevel@tonic-gate  * ehci_do_client_periodic_in_req_callback
40647c478bd9Sstevel@tonic-gate  *
40657c478bd9Sstevel@tonic-gate  * Do callback for the original client periodic IN request.
40667c478bd9Sstevel@tonic-gate  */
40677c478bd9Sstevel@tonic-gate void
ehci_do_client_periodic_in_req_callback(ehci_state_t * ehcip,ehci_pipe_private_t * pp,usb_cr_t completion_reason)40687c478bd9Sstevel@tonic-gate ehci_do_client_periodic_in_req_callback(
40697c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
40707c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp,
40717c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason)
40727c478bd9Sstevel@tonic-gate {
40737c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
40747c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
40757c478bd9Sstevel@tonic-gate 
40767c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS,
40777c478bd9Sstevel@tonic-gate 	    ehcip->ehci_log_hdl,
40787c478bd9Sstevel@tonic-gate 	    "ehci_do_client_periodic_in_req_callback: "
4079112116d8Sfb 	    "pp = 0x%p cc = 0x%x", (void *)pp, completion_reason);
40807c478bd9Sstevel@tonic-gate 
40817c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
40827c478bd9Sstevel@tonic-gate 
40837c478bd9Sstevel@tonic-gate 	/*
40847c478bd9Sstevel@tonic-gate 	 * Check for Interrupt/Isochronous IN, whether we need to do
40857c478bd9Sstevel@tonic-gate 	 * callback for the original client's periodic IN request.
40867c478bd9Sstevel@tonic-gate 	 */
40877c478bd9Sstevel@tonic-gate 	if (pp->pp_client_periodic_in_reqp) {
40887c478bd9Sstevel@tonic-gate 		ASSERT(pp->pp_cur_periodic_req_cnt == 0);
40897c478bd9Sstevel@tonic-gate 		if (EHCI_ISOC_ENDPOINT(eptd)) {
40907c478bd9Sstevel@tonic-gate 			ehci_hcdi_isoc_callback(ph, NULL, completion_reason);
40917c478bd9Sstevel@tonic-gate 		} else {
40927c478bd9Sstevel@tonic-gate 			ehci_hcdi_callback(ph, NULL, completion_reason);
40937c478bd9Sstevel@tonic-gate 		}
40947c478bd9Sstevel@tonic-gate 	}
40957c478bd9Sstevel@tonic-gate }
40967c478bd9Sstevel@tonic-gate 
40977c478bd9Sstevel@tonic-gate 
40987c478bd9Sstevel@tonic-gate /*
40997c478bd9Sstevel@tonic-gate  * ehci_hcdi_callback()
41007c478bd9Sstevel@tonic-gate  *
41017c478bd9Sstevel@tonic-gate  * Convenience wrapper around usba_hcdi_cb() other than root hub.
41027c478bd9Sstevel@tonic-gate  */
41037c478bd9Sstevel@tonic-gate void
ehci_hcdi_callback(usba_pipe_handle_data_t * ph,ehci_trans_wrapper_t * tw,usb_cr_t completion_reason)41047c478bd9Sstevel@tonic-gate ehci_hcdi_callback(
41057c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
41067c478bd9Sstevel@tonic-gate 	ehci_trans_wrapper_t	*tw,
41077c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason)
41087c478bd9Sstevel@tonic-gate {
41097c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
41106a9de478Ssl 	    ph->p_usba_device->usb_root_hub_dip);
41117c478bd9Sstevel@tonic-gate 	ehci_pipe_private_t	*pp = (ehci_pipe_private_t *)ph->p_hcd_private;
41127c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_xfer_reqp;
41137c478bd9Sstevel@tonic-gate 	uint_t			pipe_state = 0;
41147c478bd9Sstevel@tonic-gate 
41157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, ehcip->ehci_log_hdl,
41167c478bd9Sstevel@tonic-gate 	    "ehci_hcdi_callback: ph = 0x%p, tw = 0x%p, cr = 0x%x",
4117112116d8Sfb 	    (void *)ph, (void *)tw, completion_reason);
41187c478bd9Sstevel@tonic-gate 
41197c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
41207c478bd9Sstevel@tonic-gate 
41217c478bd9Sstevel@tonic-gate 	/* Set the pipe state as per completion reason */
41227c478bd9Sstevel@tonic-gate 	switch (completion_reason) {
41237c478bd9Sstevel@tonic-gate 	case USB_CR_OK:
41247c478bd9Sstevel@tonic-gate 		pipe_state = pp->pp_state;
41257c478bd9Sstevel@tonic-gate 		break;
41267c478bd9Sstevel@tonic-gate 	case USB_CR_NO_RESOURCES:
41277c478bd9Sstevel@tonic-gate 	case USB_CR_NOT_SUPPORTED:
41287c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
41297c478bd9Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
41307c478bd9Sstevel@tonic-gate 		pipe_state = EHCI_PIPE_STATE_IDLE;
41317c478bd9Sstevel@tonic-gate 		break;
41327c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_CLOSING:
41337c478bd9Sstevel@tonic-gate 		break;
41347c478bd9Sstevel@tonic-gate 	default:
41357c478bd9Sstevel@tonic-gate 		/* Set the pipe state to error */
41367c478bd9Sstevel@tonic-gate 		pipe_state = EHCI_PIPE_STATE_ERROR;
41377c478bd9Sstevel@tonic-gate 		pp->pp_error = completion_reason;
41387c478bd9Sstevel@tonic-gate 		break;
41397c478bd9Sstevel@tonic-gate 
41407c478bd9Sstevel@tonic-gate 	}
41417c478bd9Sstevel@tonic-gate 
41427c478bd9Sstevel@tonic-gate 	pp->pp_state = pipe_state;
41437c478bd9Sstevel@tonic-gate 
41447c478bd9Sstevel@tonic-gate 	if (tw && tw->tw_curr_xfer_reqp) {
41457c478bd9Sstevel@tonic-gate 		curr_xfer_reqp = tw->tw_curr_xfer_reqp;
41467c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp = NULL;
41477c478bd9Sstevel@tonic-gate 	} else {
41487c478bd9Sstevel@tonic-gate 		ASSERT(pp->pp_client_periodic_in_reqp != NULL);
41497c478bd9Sstevel@tonic-gate 
41507c478bd9Sstevel@tonic-gate 		curr_xfer_reqp = pp->pp_client_periodic_in_reqp;
41517c478bd9Sstevel@tonic-gate 		pp->pp_client_periodic_in_reqp = NULL;
41527c478bd9Sstevel@tonic-gate 	}
41537c478bd9Sstevel@tonic-gate 
41547c478bd9Sstevel@tonic-gate 	ASSERT(curr_xfer_reqp != NULL);
41557c478bd9Sstevel@tonic-gate 
41567c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
41577c478bd9Sstevel@tonic-gate 
41587c478bd9Sstevel@tonic-gate 	usba_hcdi_cb(ph, curr_xfer_reqp, completion_reason);
41597c478bd9Sstevel@tonic-gate 
41607c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
41617c478bd9Sstevel@tonic-gate }
4162