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
502acac7eSsl  * Common Development and Distribution License (the "License").
602acac7eSsl  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22269552cdSguoqing zhu - Sun Microsystems - Beijing China  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * Universal Host Controller Driver (UHCI)
297c478bd9Sstevel@tonic-gate  *
307c478bd9Sstevel@tonic-gate  * The UHCI driver is a driver which interfaces to the Universal
317c478bd9Sstevel@tonic-gate  * Serial Bus Driver (USBA) and the Host Controller (HC). The interface to
327c478bd9Sstevel@tonic-gate  * the Host Controller is defined by the UHCI.
337c478bd9Sstevel@tonic-gate  * This file contains misc functions.
347c478bd9Sstevel@tonic-gate  */
357c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/uhci/uhcid.h>
367c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/uhci/uhciutil.h>
377c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/uhci/uhcipolled.h>
387c478bd9Sstevel@tonic-gate 
3902acac7eSsl #include <sys/disp.h>
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate /* Globals */
427c478bd9Sstevel@tonic-gate extern uint_t	uhci_td_pool_size;			/* Num TDs */
437c478bd9Sstevel@tonic-gate extern uint_t	uhci_qh_pool_size;			/* Num QHs */
447c478bd9Sstevel@tonic-gate extern ushort_t	uhci_tree_bottom_nodes[];
457c478bd9Sstevel@tonic-gate extern void	*uhci_statep;
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate /* function prototypes */
487c478bd9Sstevel@tonic-gate static void	uhci_build_interrupt_lattice(uhci_state_t *uhcip);
497c478bd9Sstevel@tonic-gate static int	uhci_init_frame_lst_table(dev_info_t *dip, uhci_state_t *uhcip);
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate static uint_t	uhci_lattice_height(uint_t bandwidth);
527c478bd9Sstevel@tonic-gate static uint_t	uhci_lattice_parent(uint_t node);
537c478bd9Sstevel@tonic-gate static uint_t	uhci_leftmost_leaf(uint_t node, uint_t height);
547c478bd9Sstevel@tonic-gate static uint_t	uhci_compute_total_bandwidth(usb_ep_descr_t *endpoint,
557c478bd9Sstevel@tonic-gate 		    usb_port_status_t port_status);
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate static int	uhci_bandwidth_adjust(uhci_state_t *uhcip,
587c478bd9Sstevel@tonic-gate 		    usb_ep_descr_t *endpoint, usb_port_status_t port_status);
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate static uhci_td_t *uhci_allocate_td_from_pool(uhci_state_t *uhcip);
617c478bd9Sstevel@tonic-gate static void	uhci_fill_in_td(uhci_state_t *uhcip,
627c478bd9Sstevel@tonic-gate 		    uhci_td_t *td, uhci_td_t *current_dummy,
6302acac7eSsl 		    uint32_t buffer_offset, size_t length,
647c478bd9Sstevel@tonic-gate 		    uhci_pipe_private_t	*pp, uchar_t PID,
6502acac7eSsl 		    usb_req_attrs_t attrs, uhci_trans_wrapper_t *tw);
6602acac7eSsl static uint32_t	uhci_get_tw_paddr_by_offs(uhci_state_t *uhcip,
6702acac7eSsl 		    uint32_t buffer_offset, size_t length,
6802acac7eSsl 		    uhci_trans_wrapper_t *tw);
697c478bd9Sstevel@tonic-gate static uhci_trans_wrapper_t *uhci_create_transfer_wrapper(
707c478bd9Sstevel@tonic-gate 		    uhci_state_t *uhcip, uhci_pipe_private_t *pp,
717c478bd9Sstevel@tonic-gate 		    size_t length, usb_flags_t usb_flags);
7202acac7eSsl static uhci_trans_wrapper_t *uhci_create_isoc_transfer_wrapper(
7302acac7eSsl 		    uhci_state_t *uhcip, uhci_pipe_private_t *pp,
7402acac7eSsl 		    usb_isoc_req_t *req, size_t length,
7502acac7eSsl 		    usb_flags_t usb_flags);
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate static int	uhci_create_setup_pkt(uhci_state_t *uhcip,
787c478bd9Sstevel@tonic-gate 		    uhci_pipe_private_t	*pp, uhci_trans_wrapper_t *tw);
797c478bd9Sstevel@tonic-gate static void	uhci_insert_ctrl_qh(uhci_state_t *uhcip,
807c478bd9Sstevel@tonic-gate 		    uhci_pipe_private_t *pp);
817c478bd9Sstevel@tonic-gate static void	uhci_remove_ctrl_qh(uhci_state_t *uhcip,
827c478bd9Sstevel@tonic-gate 		    uhci_pipe_private_t *pp);
837c478bd9Sstevel@tonic-gate static void	uhci_insert_intr_qh(uhci_state_t *uhcip,
847c478bd9Sstevel@tonic-gate 		    uhci_pipe_private_t *pp);
857c478bd9Sstevel@tonic-gate static void	uhci_remove_intr_qh(uhci_state_t *uhcip,
867c478bd9Sstevel@tonic-gate 		    uhci_pipe_private_t *pp);
877c478bd9Sstevel@tonic-gate static void	uhci_remove_bulk_qh(uhci_state_t *uhcip,
887c478bd9Sstevel@tonic-gate 		    uhci_pipe_private_t *pp);
897c478bd9Sstevel@tonic-gate static void	uhci_insert_bulk_qh(uhci_state_t *uhcip,
907c478bd9Sstevel@tonic-gate 		    uhci_pipe_private_t *pp);
917c478bd9Sstevel@tonic-gate static void	uhci_handle_bulk_td_errors(uhci_state_t *uhcip, uhci_td_t *td);
927c478bd9Sstevel@tonic-gate static int	uhci_alloc_memory_for_tds(uhci_state_t *uhcip, uint_t num_tds,
937c478bd9Sstevel@tonic-gate 		    uhci_bulk_isoc_xfer_t *info);
9402acac7eSsl static int	uhci_alloc_bulk_isoc_tds(uhci_state_t *uhcip, uint_t num_tds,
9502acac7eSsl 		    uhci_bulk_isoc_xfer_t *info);
9602acac7eSsl static void	uhci_get_isoc_td_by_index(uhci_state_t *uhcip,
9702acac7eSsl 		    uhci_bulk_isoc_xfer_t *info, uint_t index,
9802acac7eSsl 		    uhci_td_t **tdpp, uhci_bulk_isoc_td_pool_t **td_pool_pp);
9902acac7eSsl static void	uhci_get_bulk_td_by_paddr(uhci_state_t *uhcip,
10002acac7eSsl 		    uhci_bulk_isoc_xfer_t *info, uint32_t paddr,
10102acac7eSsl 		    uhci_bulk_isoc_td_pool_t **td_pool_pp);
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate static	int	uhci_handle_isoc_receive(uhci_state_t *uhcip,
1047c478bd9Sstevel@tonic-gate 		uhci_pipe_private_t *pp, uhci_trans_wrapper_t *tw);
1057c478bd9Sstevel@tonic-gate static void	uhci_delete_isoc_td(uhci_state_t *uhcip,
1067c478bd9Sstevel@tonic-gate 		    uhci_td_t *td);
1077c478bd9Sstevel@tonic-gate #ifdef DEBUG
1087c478bd9Sstevel@tonic-gate static void	uhci_print_td(uhci_state_t *uhcip, uhci_td_t *td);
1097c478bd9Sstevel@tonic-gate static void	uhci_print_qh(uhci_state_t *uhcip, queue_head_t *qh);
1107c478bd9Sstevel@tonic-gate #endif
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate /*
1147c478bd9Sstevel@tonic-gate  * uhci_build_interrupt_lattice:
1157c478bd9Sstevel@tonic-gate  *
1167c478bd9Sstevel@tonic-gate  * Construct the interrupt lattice tree using static Queue Head pointers.
1177c478bd9Sstevel@tonic-gate  * This interrupt lattice tree will have total of 63 queue heads and the
1187c478bd9Sstevel@tonic-gate  * Host Controller (HC) processes queue heads every frame.
1197c478bd9Sstevel@tonic-gate  */
1207c478bd9Sstevel@tonic-gate static void
uhci_build_interrupt_lattice(uhci_state_t * uhcip)1217c478bd9Sstevel@tonic-gate uhci_build_interrupt_lattice(uhci_state_t *uhcip)
1227c478bd9Sstevel@tonic-gate {
1237c478bd9Sstevel@tonic-gate 	int			half_list = NUM_INTR_QH_LISTS / 2;
1247c478bd9Sstevel@tonic-gate 	uint16_t		i, j, k;
1257c478bd9Sstevel@tonic-gate 	uhci_td_t		*sof_td, *isoc_td;
1267c478bd9Sstevel@tonic-gate 	uintptr_t		addr;
1277c478bd9Sstevel@tonic-gate 	queue_head_t		*list_array = uhcip->uhci_qh_pool_addr;
1287c478bd9Sstevel@tonic-gate 	queue_head_t		*tmp_qh;
1297c478bd9Sstevel@tonic-gate 	frame_lst_table_t	*frame_lst_tablep =
130fffe0b30Sqz 	    uhcip->uhci_frame_lst_tablep;
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
1337c478bd9Sstevel@tonic-gate 	    "uhci_build_interrupt_lattice:");
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	/*
1367c478bd9Sstevel@tonic-gate 	 * Reserve the first 63 queue head structures in the pool as static
1377c478bd9Sstevel@tonic-gate 	 * queue heads & these are required for constructing interrupt
1387c478bd9Sstevel@tonic-gate 	 * lattice tree.
1397c478bd9Sstevel@tonic-gate 	 */
1407c478bd9Sstevel@tonic-gate 	for (i = 0; i < NUM_INTR_QH_LISTS; i++) {
1417c478bd9Sstevel@tonic-gate 		SetQH32(uhcip, list_array[i].link_ptr, HC_END_OF_LIST);
1427c478bd9Sstevel@tonic-gate 		SetQH32(uhcip, list_array[i].element_ptr, HC_END_OF_LIST);
1437c478bd9Sstevel@tonic-gate 		list_array[i].qh_flag		= QUEUE_HEAD_FLAG_STATIC;
1447c478bd9Sstevel@tonic-gate 		list_array[i].node		= i;
1457c478bd9Sstevel@tonic-gate 	}
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate 	/* Build the interrupt lattice tree */
1487c478bd9Sstevel@tonic-gate 	for (i = 0; i < half_list - 1; i++) {
1497c478bd9Sstevel@tonic-gate 		/*
1507c478bd9Sstevel@tonic-gate 		 * The next  pointer in the host controller  queue head
1517c478bd9Sstevel@tonic-gate 		 * descriptor must contain an iommu address. Calculate
1527c478bd9Sstevel@tonic-gate 		 * the offset into the cpu address and add this to the
1537c478bd9Sstevel@tonic-gate 		 * starting iommu address.
1547c478bd9Sstevel@tonic-gate 		 */
1557c478bd9Sstevel@tonic-gate 		addr = QH_PADDR(&list_array[i]) | HC_QUEUE_HEAD;
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 		SetQH32(uhcip, list_array[2*i + 1].link_ptr, addr);
1587c478bd9Sstevel@tonic-gate 		SetQH32(uhcip, list_array[2*i + 2].link_ptr, addr);
1597c478bd9Sstevel@tonic-gate 	}
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	/*
1627c478bd9Sstevel@tonic-gate 	 * Initialize the interrupt list in the Frame list Table
1637c478bd9Sstevel@tonic-gate 	 * so that it points to the bottom of the tree.
1647c478bd9Sstevel@tonic-gate 	 */
1657c478bd9Sstevel@tonic-gate 	for (i = 0, j = 0; i < pow_2(TREE_HEIGHT); i++) {
1667c478bd9Sstevel@tonic-gate 		addr = QH_PADDR(&list_array[half_list + i - 1]);
1677c478bd9Sstevel@tonic-gate 		for (k = 0; k <  pow_2(VIRTUAL_TREE_HEIGHT); k++) {
1687c478bd9Sstevel@tonic-gate 			SetFL32(uhcip,
1697c478bd9Sstevel@tonic-gate 			    frame_lst_tablep[uhci_tree_bottom_nodes[j++]],
1707c478bd9Sstevel@tonic-gate 			    addr | HC_QUEUE_HEAD);
1717c478bd9Sstevel@tonic-gate 		}
1727c478bd9Sstevel@tonic-gate 	}
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	/*
1757c478bd9Sstevel@tonic-gate 	 *  Create a controller and bulk Queue heads
1767c478bd9Sstevel@tonic-gate 	 */
1777c478bd9Sstevel@tonic-gate 	uhcip->uhci_ctrl_xfers_q_head = uhci_alloc_queue_head(uhcip);
1787c478bd9Sstevel@tonic-gate 	tmp_qh = uhcip->uhci_ctrl_xfers_q_tail = uhcip->uhci_ctrl_xfers_q_head;
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, list_array[0].link_ptr,
181fffe0b30Sqz 	    (QH_PADDR(tmp_qh) | HC_QUEUE_HEAD));
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 	uhcip->uhci_bulk_xfers_q_head = uhci_alloc_queue_head(uhcip);
1847c478bd9Sstevel@tonic-gate 	uhcip->uhci_bulk_xfers_q_tail = uhcip->uhci_bulk_xfers_q_head;
1857c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, tmp_qh->link_ptr,
1867c478bd9Sstevel@tonic-gate 	    (QH_PADDR(uhcip->uhci_bulk_xfers_q_head)|HC_QUEUE_HEAD));
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, uhcip->uhci_bulk_xfers_q_head->link_ptr, HC_END_OF_LIST);
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 	/*
1917c478bd9Sstevel@tonic-gate 	 * Add a dummy TD to the static queue head 0. THis is used
1927c478bd9Sstevel@tonic-gate 	 * to generate an at the end of frame.
1937c478bd9Sstevel@tonic-gate 	 */
1947c478bd9Sstevel@tonic-gate 	sof_td = uhci_allocate_td_from_pool(uhcip);
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, list_array[0].element_ptr,
1977c478bd9Sstevel@tonic-gate 	    TD_PADDR(sof_td) | HC_TD_HEAD);
1987c478bd9Sstevel@tonic-gate 	SetTD32(uhcip, sof_td->link_ptr, HC_END_OF_LIST);
1997c478bd9Sstevel@tonic-gate 	uhcip->uhci_sof_td = sof_td;
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	/*
2027c478bd9Sstevel@tonic-gate 	 * Add a dummy td that is used to generate an interrupt for
2037c478bd9Sstevel@tonic-gate 	 * every 1024 frames.
2047c478bd9Sstevel@tonic-gate 	 */
2057c478bd9Sstevel@tonic-gate 	isoc_td = uhci_allocate_td_from_pool(uhcip);
2067c478bd9Sstevel@tonic-gate 	SetTD32(uhcip, isoc_td->link_ptr, HC_END_OF_LIST);
2077c478bd9Sstevel@tonic-gate 	uhcip->uhci_isoc_td = isoc_td;
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 	uhcip->uhci_isoc_qh = uhci_alloc_queue_head(uhcip);
2107c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, uhcip->uhci_isoc_qh->link_ptr,
2117c478bd9Sstevel@tonic-gate 	    GetFL32(uhcip, uhcip->uhci_frame_lst_tablep[MAX_FRAME_NUM]));
2127c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, uhcip->uhci_isoc_qh->element_ptr, TD_PADDR(isoc_td));
2137c478bd9Sstevel@tonic-gate 	SetFL32(uhcip, uhcip->uhci_frame_lst_tablep[MAX_FRAME_NUM],
214fffe0b30Sqz 	    QH_PADDR(uhcip->uhci_isoc_qh) | HC_QUEUE_HEAD);
2157c478bd9Sstevel@tonic-gate }
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate /*
2197c478bd9Sstevel@tonic-gate  * uhci_allocate_pools:
2207c478bd9Sstevel@tonic-gate  *	Allocate the system memory for the Queue Heads Descriptor and
2217c478bd9Sstevel@tonic-gate  *	for the Transfer Descriptor (TD) pools. Both QH and TD structures
2227c478bd9Sstevel@tonic-gate  *	must be aligned to a 16 byte boundary.
2237c478bd9Sstevel@tonic-gate  */
2247c478bd9Sstevel@tonic-gate int
uhci_allocate_pools(uhci_state_t * uhcip)2257c478bd9Sstevel@tonic-gate uhci_allocate_pools(uhci_state_t *uhcip)
2267c478bd9Sstevel@tonic-gate {
2277c478bd9Sstevel@tonic-gate 	dev_info_t		*dip = uhcip->uhci_dip;
2287c478bd9Sstevel@tonic-gate 	size_t			real_length;
2297c478bd9Sstevel@tonic-gate 	int			i, result;
2307c478bd9Sstevel@tonic-gate 	uint_t			ccount;
2317c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t	dev_attr;
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
2347c478bd9Sstevel@tonic-gate 	    "uhci_allocate_pools:");
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 	/* The host controller will be little endian */
2377c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_version		= DDI_DEVICE_ATTR_V0;
2387c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_endian_flags	= DDI_STRUCTURE_LE_ACC;
2397c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_dataorder		= DDI_STRICTORDER_ACC;
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 	/* Allocate the TD pool DMA handle */
2427c478bd9Sstevel@tonic-gate 	if (ddi_dma_alloc_handle(dip, &uhcip->uhci_dma_attr, DDI_DMA_SLEEP, 0,
2437c478bd9Sstevel@tonic-gate 	    &uhcip->uhci_td_pool_dma_handle) != DDI_SUCCESS) {
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
2467c478bd9Sstevel@tonic-gate 	}
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	/* Allocate the memory for the TD pool */
2497c478bd9Sstevel@tonic-gate 	if (ddi_dma_mem_alloc(uhcip->uhci_td_pool_dma_handle,
2507c478bd9Sstevel@tonic-gate 	    uhci_td_pool_size * sizeof (uhci_td_t),
2517c478bd9Sstevel@tonic-gate 	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
2527c478bd9Sstevel@tonic-gate 	    (caddr_t *)&uhcip->uhci_td_pool_addr, &real_length,
2537c478bd9Sstevel@tonic-gate 	    &uhcip->uhci_td_pool_mem_handle)) {
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
2567c478bd9Sstevel@tonic-gate 	}
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	/* Map the TD pool into the I/O address space */
2597c478bd9Sstevel@tonic-gate 	result = ddi_dma_addr_bind_handle(uhcip->uhci_td_pool_dma_handle,
2607c478bd9Sstevel@tonic-gate 	    NULL, (caddr_t)uhcip->uhci_td_pool_addr, real_length,
2617c478bd9Sstevel@tonic-gate 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
2627c478bd9Sstevel@tonic-gate 	    NULL, &uhcip->uhci_td_pool_cookie, &ccount);
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	bzero((void *)uhcip->uhci_td_pool_addr,
2657c478bd9Sstevel@tonic-gate 	    uhci_td_pool_size * sizeof (uhci_td_t));
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 	/* Process the result */
2687c478bd9Sstevel@tonic-gate 	if (result == DDI_DMA_MAPPED) {
2697c478bd9Sstevel@tonic-gate 		/* The cookie count should be 1 */
2707c478bd9Sstevel@tonic-gate 		if (ccount != 1) {
2717c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
2727c478bd9Sstevel@tonic-gate 			    "uhci_allocate_pools: More than 1 cookie");
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
2757c478bd9Sstevel@tonic-gate 		}
2767c478bd9Sstevel@tonic-gate 	} else {
2777c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
2787c478bd9Sstevel@tonic-gate 		    "uhci_allocate_pools: Result = %d", result);
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 		uhci_decode_ddi_dma_addr_bind_handle_result(uhcip, result);
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
2837c478bd9Sstevel@tonic-gate 	}
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_addr_bind_flag |= UHCI_TD_POOL_BOUND;
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 	/* Initialize the TD pool */
2887c478bd9Sstevel@tonic-gate 	for (i = 0; i < uhci_td_pool_size; i++) {
2897c478bd9Sstevel@tonic-gate 		uhcip->uhci_td_pool_addr[i].flag = TD_FLAG_FREE;
2907c478bd9Sstevel@tonic-gate 	}
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	/* Allocate the TD pool DMA handle */
2937c478bd9Sstevel@tonic-gate 	if (ddi_dma_alloc_handle(dip, &uhcip->uhci_dma_attr, DDI_DMA_SLEEP,
2947c478bd9Sstevel@tonic-gate 	    0, &uhcip->uhci_qh_pool_dma_handle) != DDI_SUCCESS) {
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
2977c478bd9Sstevel@tonic-gate 	}
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 	/* Allocate the memory for the QH pool */
3007c478bd9Sstevel@tonic-gate 	if (ddi_dma_mem_alloc(uhcip->uhci_qh_pool_dma_handle,
3017c478bd9Sstevel@tonic-gate 	    uhci_qh_pool_size * sizeof (queue_head_t),
3027c478bd9Sstevel@tonic-gate 	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
3037c478bd9Sstevel@tonic-gate 	    (caddr_t *)&uhcip->uhci_qh_pool_addr, &real_length,
3047c478bd9Sstevel@tonic-gate 	    &uhcip->uhci_qh_pool_mem_handle) != DDI_SUCCESS) {
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3077c478bd9Sstevel@tonic-gate 	}
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 	result = ddi_dma_addr_bind_handle(uhcip->uhci_qh_pool_dma_handle,
3107c478bd9Sstevel@tonic-gate 	    NULL, (caddr_t)uhcip->uhci_qh_pool_addr, real_length,
3117c478bd9Sstevel@tonic-gate 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
3127c478bd9Sstevel@tonic-gate 	    &uhcip->uhci_qh_pool_cookie, &ccount);
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 	/* Process the result */
3157c478bd9Sstevel@tonic-gate 	if (result == DDI_DMA_MAPPED) {
3167c478bd9Sstevel@tonic-gate 		/* The cookie count should be 1 */
3177c478bd9Sstevel@tonic-gate 		if (ccount != 1) {
3187c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
3197c478bd9Sstevel@tonic-gate 			    "uhci_allocate_pools: More than 1 cookie");
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
3227c478bd9Sstevel@tonic-gate 		}
3237c478bd9Sstevel@tonic-gate 	} else {
3247c478bd9Sstevel@tonic-gate 		uhci_decode_ddi_dma_addr_bind_handle_result(uhcip, result);
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3277c478bd9Sstevel@tonic-gate 	}
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_addr_bind_flag |= UHCI_QH_POOL_BOUND;
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 	bzero((void *)uhcip->uhci_qh_pool_addr,
3327c478bd9Sstevel@tonic-gate 	    uhci_qh_pool_size * sizeof (queue_head_t));
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	/* Initialize the QH pool */
3357c478bd9Sstevel@tonic-gate 	for (i = 0; i < uhci_qh_pool_size; i ++) {
3367c478bd9Sstevel@tonic-gate 		uhcip->uhci_qh_pool_addr[i].qh_flag = QUEUE_HEAD_FLAG_FREE;
3377c478bd9Sstevel@tonic-gate 	}
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
3407c478bd9Sstevel@tonic-gate 	    "uhci_allocate_pools: Completed");
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
3437c478bd9Sstevel@tonic-gate }
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate /*
3477c478bd9Sstevel@tonic-gate  * uhci_free_pools:
3487c478bd9Sstevel@tonic-gate  *	Cleanup on attach failure or detach
3497c478bd9Sstevel@tonic-gate  */
3507c478bd9Sstevel@tonic-gate void
uhci_free_pools(uhci_state_t * uhcip)3517c478bd9Sstevel@tonic-gate uhci_free_pools(uhci_state_t *uhcip)
3527c478bd9Sstevel@tonic-gate {
3537c478bd9Sstevel@tonic-gate 	int			i, flag, rval;
3547c478bd9Sstevel@tonic-gate 	uhci_td_t		*td;
3557c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw;
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
358fffe0b30Sqz 	    "uhci_free_pools:");
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_td_pool_addr && uhcip->uhci_td_pool_mem_handle) {
3617c478bd9Sstevel@tonic-gate 		for (i = 0; i < uhci_td_pool_size; i ++) {
3627c478bd9Sstevel@tonic-gate 			td = &uhcip->uhci_td_pool_addr[i];
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 			flag = uhcip->uhci_td_pool_addr[i].flag;
3657c478bd9Sstevel@tonic-gate 			if ((flag != TD_FLAG_FREE) &&
3667c478bd9Sstevel@tonic-gate 			    (flag != TD_FLAG_DUMMY) && (td->tw != NULL)) {
3677c478bd9Sstevel@tonic-gate 				tw = td->tw;
3687c478bd9Sstevel@tonic-gate 				uhci_free_tw(uhcip, tw);
3697c478bd9Sstevel@tonic-gate 			}
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 		}
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_dma_addr_bind_flag & UHCI_TD_POOL_BOUND) {
3747c478bd9Sstevel@tonic-gate 			rval = ddi_dma_unbind_handle(
375fffe0b30Sqz 			    uhcip->uhci_td_pool_dma_handle);
3767c478bd9Sstevel@tonic-gate 			ASSERT(rval == DDI_SUCCESS);
3777c478bd9Sstevel@tonic-gate 		}
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 		ddi_dma_mem_free(&uhcip->uhci_td_pool_mem_handle);
3807c478bd9Sstevel@tonic-gate 	}
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 	/* Free the TD pool */
3837c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_td_pool_dma_handle) {
3847c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&uhcip->uhci_td_pool_dma_handle);
3857c478bd9Sstevel@tonic-gate 	}
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_qh_pool_addr && uhcip->uhci_qh_pool_mem_handle) {
3887c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_dma_addr_bind_flag & UHCI_QH_POOL_BOUND) {
3897c478bd9Sstevel@tonic-gate 			rval = ddi_dma_unbind_handle(
390fffe0b30Sqz 			    uhcip->uhci_qh_pool_dma_handle);
3917c478bd9Sstevel@tonic-gate 			ASSERT(rval == DDI_SUCCESS);
3927c478bd9Sstevel@tonic-gate 		}
3937c478bd9Sstevel@tonic-gate 		ddi_dma_mem_free(&uhcip->uhci_qh_pool_mem_handle);
3947c478bd9Sstevel@tonic-gate 	}
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 	/* Free the QH pool */
3977c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_qh_pool_dma_handle) {
3987c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&uhcip->uhci_qh_pool_dma_handle);
3997c478bd9Sstevel@tonic-gate 	}
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 	/* Free the Frame list Table area */
4027c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_frame_lst_tablep && uhcip->uhci_flt_mem_handle) {
4037c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_dma_addr_bind_flag & UHCI_FLA_POOL_BOUND) {
4047c478bd9Sstevel@tonic-gate 			rval = ddi_dma_unbind_handle(
405fffe0b30Sqz 			    uhcip->uhci_flt_dma_handle);
4067c478bd9Sstevel@tonic-gate 			ASSERT(rval == DDI_SUCCESS);
4077c478bd9Sstevel@tonic-gate 		}
4087c478bd9Sstevel@tonic-gate 		ddi_dma_mem_free(&uhcip->uhci_flt_mem_handle);
4097c478bd9Sstevel@tonic-gate 	}
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_flt_dma_handle) {
4127c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&uhcip->uhci_flt_dma_handle);
4137c478bd9Sstevel@tonic-gate 	}
4147c478bd9Sstevel@tonic-gate }
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate /*
4187c478bd9Sstevel@tonic-gate  * uhci_decode_ddi_dma_addr_bind_handle_result:
4197c478bd9Sstevel@tonic-gate  *	Process the return values of ddi_dma_addr_bind_handle()
4207c478bd9Sstevel@tonic-gate  */
4217c478bd9Sstevel@tonic-gate void
uhci_decode_ddi_dma_addr_bind_handle_result(uhci_state_t * uhcip,int result)4227c478bd9Sstevel@tonic-gate uhci_decode_ddi_dma_addr_bind_handle_result(uhci_state_t *uhcip, int result)
4237c478bd9Sstevel@tonic-gate {
4247c478bd9Sstevel@tonic-gate 	char *msg;
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
4277c478bd9Sstevel@tonic-gate 	    "uhci_decode_ddi_dma_addr_bind_handle_result:");
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	switch (result) {
4307c478bd9Sstevel@tonic-gate 	case DDI_DMA_PARTIAL_MAP:
4317c478bd9Sstevel@tonic-gate 		msg = "Partial transfers not allowed";
4327c478bd9Sstevel@tonic-gate 		break;
4337c478bd9Sstevel@tonic-gate 	case DDI_DMA_INUSE:
4347c478bd9Sstevel@tonic-gate 		msg = "Handle is in use";
4357c478bd9Sstevel@tonic-gate 		break;
4367c478bd9Sstevel@tonic-gate 	case DDI_DMA_NORESOURCES:
4377c478bd9Sstevel@tonic-gate 		msg = "No resources";
4387c478bd9Sstevel@tonic-gate 		break;
4397c478bd9Sstevel@tonic-gate 	case DDI_DMA_NOMAPPING:
4407c478bd9Sstevel@tonic-gate 		msg = "No mapping";
4417c478bd9Sstevel@tonic-gate 		break;
4427c478bd9Sstevel@tonic-gate 	case DDI_DMA_TOOBIG:
4437c478bd9Sstevel@tonic-gate 		msg = "Object is too big";
4447c478bd9Sstevel@tonic-gate 		break;
4457c478bd9Sstevel@tonic-gate 	default:
4467c478bd9Sstevel@tonic-gate 		msg = "Unknown dma error";
4477c478bd9Sstevel@tonic-gate 	}
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uhcip->uhci_log_hdl, "%s", msg);
4507c478bd9Sstevel@tonic-gate }
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate /*
4547c478bd9Sstevel@tonic-gate  * uhci_init_ctlr:
4557c478bd9Sstevel@tonic-gate  *	Initialize the Host Controller (HC).
4567c478bd9Sstevel@tonic-gate  */
4577c478bd9Sstevel@tonic-gate int
uhci_init_ctlr(uhci_state_t * uhcip)4587c478bd9Sstevel@tonic-gate uhci_init_ctlr(uhci_state_t *uhcip)
4597c478bd9Sstevel@tonic-gate {
4607c478bd9Sstevel@tonic-gate 	dev_info_t *dip = uhcip->uhci_dip;
4617c478bd9Sstevel@tonic-gate 	uint_t	cmd_reg;
4627c478bd9Sstevel@tonic-gate 	uint_t	frame_base_addr;
4637c478bd9Sstevel@tonic-gate 
464932cf989Ssl 	mutex_enter(&uhcip->uhci_int_mutex);
465932cf989Ssl 
4667c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl, "uhci_init_ctlr:");
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	/*
4697c478bd9Sstevel@tonic-gate 	 * When USB legacy mode is enabled, the BIOS manages the USB keyboard
4707c478bd9Sstevel@tonic-gate 	 * attached to the UHCI controller. It has been observed that some
4717c478bd9Sstevel@tonic-gate 	 * times the BIOS does not clear the interrupts in the legacy mode
4727c478bd9Sstevel@tonic-gate 	 * register in the PCI configuration space. So, disable the SMI intrs
4737c478bd9Sstevel@tonic-gate 	 * and route the intrs to PIRQD here.
4747c478bd9Sstevel@tonic-gate 	 */
4757c478bd9Sstevel@tonic-gate 	pci_config_put16(uhcip->uhci_config_handle,
4767c478bd9Sstevel@tonic-gate 	    LEGACYMODE_REG_OFFSET, LEGACYMODE_REG_INIT_VALUE);
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate 	/*
4797c478bd9Sstevel@tonic-gate 	 * Disable all the interrupts.
4807c478bd9Sstevel@tonic-gate 	 */
4817c478bd9Sstevel@tonic-gate 	Set_OpReg16(USBINTR, DISABLE_ALL_INTRS);
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 	cmd_reg = Get_OpReg16(USBCMD);
4847c478bd9Sstevel@tonic-gate 	cmd_reg &= (~USBCMD_REG_HC_RUN);
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate 	/* Stop the controller */
4877c478bd9Sstevel@tonic-gate 	Set_OpReg16(USBCMD, cmd_reg);
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 	/* Reset the host controller */
4907c478bd9Sstevel@tonic-gate 	Set_OpReg16(USBCMD, USBCMD_REG_GBL_RESET);
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 	/* Wait 10ms for reset to complete */
4937c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
4947c478bd9Sstevel@tonic-gate 	delay(drv_usectohz(UHCI_RESET_DELAY));
4957c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 	Set_OpReg16(USBCMD, 0);
4987c478bd9Sstevel@tonic-gate 
4997c478bd9Sstevel@tonic-gate 	/* Set the frame number to zero */
5007c478bd9Sstevel@tonic-gate 	Set_OpReg16(FRNUM, 0);
5017c478bd9Sstevel@tonic-gate 
502fffe0b30Sqz 	if (uhcip->uhci_hc_soft_state == UHCI_CTLR_INIT_STATE) {
503fffe0b30Sqz 		/* Initialize the Frame list base address area */
504fffe0b30Sqz 		if (uhci_init_frame_lst_table(dip, uhcip) != USB_SUCCESS) {
505fffe0b30Sqz 			mutex_exit(&uhcip->uhci_int_mutex);
5067c478bd9Sstevel@tonic-gate 
507fffe0b30Sqz 			return (USB_FAILURE);
508fffe0b30Sqz 		}
5097c478bd9Sstevel@tonic-gate 	}
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 	/* Save the contents of the Frame Interval Registers */
5127c478bd9Sstevel@tonic-gate 	uhcip->uhci_frame_interval = Get_OpReg8(SOFMOD);
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 	frame_base_addr = uhcip->uhci_flt_cookie.dmac_address;
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	/* Set the Frame list base address */
5177c478bd9Sstevel@tonic-gate 	Set_OpReg32(FRBASEADD, frame_base_addr);
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 	/*
5207c478bd9Sstevel@tonic-gate 	 * Begin sending SOFs
5217c478bd9Sstevel@tonic-gate 	 * Set the Host Controller Functional State to Operational
5227c478bd9Sstevel@tonic-gate 	 */
5237c478bd9Sstevel@tonic-gate 	cmd_reg = Get_OpReg16(USBCMD);
5247c478bd9Sstevel@tonic-gate 	cmd_reg |= (USBCMD_REG_HC_RUN | USBCMD_REG_MAXPKT_64 |
525fffe0b30Sqz 	    USBCMD_REG_CONFIG_FLAG);
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	Set_OpReg16(USBCMD, cmd_reg);
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 	/*
5307c478bd9Sstevel@tonic-gate 	 * Verify the Command and interrupt enable registers,
5317c478bd9Sstevel@tonic-gate 	 * a sanity check whether actually initialized or not
5327c478bd9Sstevel@tonic-gate 	 */
5337c478bd9Sstevel@tonic-gate 	cmd_reg = Get_OpReg16(USBCMD);
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate 	if (!(cmd_reg & (USBCMD_REG_HC_RUN | USBCMD_REG_MAXPKT_64 |
5367c478bd9Sstevel@tonic-gate 	    USBCMD_REG_CONFIG_FLAG))) {
537d291d9f2Sfrits 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
5387c478bd9Sstevel@tonic-gate 		    "uhci_init_ctlr: Controller initialization failed");
539fffe0b30Sqz 		mutex_exit(&uhcip->uhci_int_mutex);
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
5427c478bd9Sstevel@tonic-gate 	}
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	/*
5457c478bd9Sstevel@tonic-gate 	 * Set the ioc bit of the isoc intr td. This enables
5467c478bd9Sstevel@tonic-gate 	 * the generation of an interrupt for every 1024 frames.
5477c478bd9Sstevel@tonic-gate 	 */
5487c478bd9Sstevel@tonic-gate 	SetTD_ioc(uhcip, uhcip->uhci_isoc_td, 1);
5497c478bd9Sstevel@tonic-gate 
550fffe0b30Sqz 	/* Set host controller soft state to operational */
551fffe0b30Sqz 	uhcip->uhci_hc_soft_state = UHCI_CTLR_OPERATIONAL_STATE;
552fffe0b30Sqz 	mutex_exit(&uhcip->uhci_int_mutex);
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
5557c478bd9Sstevel@tonic-gate 	    "uhci_init_ctlr: Completed");
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
5587c478bd9Sstevel@tonic-gate }
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate /*
5627c478bd9Sstevel@tonic-gate  * uhci_uninit_ctlr:
5637c478bd9Sstevel@tonic-gate  *	uninitialize the Host Controller (HC).
5647c478bd9Sstevel@tonic-gate  */
5657c478bd9Sstevel@tonic-gate void
uhci_uninit_ctlr(uhci_state_t * uhcip)5667c478bd9Sstevel@tonic-gate uhci_uninit_ctlr(uhci_state_t *uhcip)
5677c478bd9Sstevel@tonic-gate {
5687c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_regs_handle) {
5697c478bd9Sstevel@tonic-gate 		/* Disable all the interrupts. */
5707c478bd9Sstevel@tonic-gate 		Set_OpReg16(USBINTR, DISABLE_ALL_INTRS);
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 		/* Complete the current transaction and then halt. */
5737c478bd9Sstevel@tonic-gate 		Set_OpReg16(USBCMD, 0);
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 		/* Wait for sometime */
5767c478bd9Sstevel@tonic-gate 		mutex_exit(&uhcip->uhci_int_mutex);
5777c478bd9Sstevel@tonic-gate 		delay(drv_usectohz(UHCI_TIMEWAIT));
5787c478bd9Sstevel@tonic-gate 		mutex_enter(&uhcip->uhci_int_mutex);
5797c478bd9Sstevel@tonic-gate 	}
5807c478bd9Sstevel@tonic-gate }
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate /*
5847c478bd9Sstevel@tonic-gate  * uhci_map_regs:
5857c478bd9Sstevel@tonic-gate  *	The Host Controller (HC) contains a set of on-chip operational
5867c478bd9Sstevel@tonic-gate  *	registers and which should be mapped into a non-cacheable
5877c478bd9Sstevel@tonic-gate  *	portion of the system addressable space.
5887c478bd9Sstevel@tonic-gate  */
5897c478bd9Sstevel@tonic-gate int
uhci_map_regs(uhci_state_t * uhcip)5907c478bd9Sstevel@tonic-gate uhci_map_regs(uhci_state_t *uhcip)
5917c478bd9Sstevel@tonic-gate {
5927c478bd9Sstevel@tonic-gate 	dev_info_t		*dip = uhcip->uhci_dip;
5937c478bd9Sstevel@tonic-gate 	int			index;
5947c478bd9Sstevel@tonic-gate 	uint32_t		regs_prop_len;
5957c478bd9Sstevel@tonic-gate 	int32_t			*regs_list;
5967c478bd9Sstevel@tonic-gate 	uint16_t		command_reg;
5977c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t	attr;
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl, "uhci_map_regs:");
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	/* The host controller will be little endian */
6027c478bd9Sstevel@tonic-gate 	attr.devacc_attr_version	= DDI_DEVICE_ATTR_V0;
6037c478bd9Sstevel@tonic-gate 	attr.devacc_attr_endian_flags	= DDI_STRUCTURE_LE_ACC;
6047c478bd9Sstevel@tonic-gate 	attr.devacc_attr_dataorder	= DDI_STRICTORDER_ACC;
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, uhcip->uhci_dip,
6077c478bd9Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "reg", &regs_list, &regs_prop_len) !=
6087c478bd9Sstevel@tonic-gate 	    DDI_PROP_SUCCESS) {
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
6117c478bd9Sstevel@tonic-gate 	}
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 	for (index = 0; index * 5 < regs_prop_len; index++) {
6147c478bd9Sstevel@tonic-gate 		if (regs_list[index * 5] & UHCI_PROP_MASK) {
6157c478bd9Sstevel@tonic-gate 			break;
6167c478bd9Sstevel@tonic-gate 		}
6177c478bd9Sstevel@tonic-gate 	}
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	/*
6207c478bd9Sstevel@tonic-gate 	 * Deallocate the memory allocated by the ddi_prop_lookup_int_array
6217c478bd9Sstevel@tonic-gate 	 */
6227c478bd9Sstevel@tonic-gate 	ddi_prop_free(regs_list);
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	if (index * 5 >= regs_prop_len) {
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
6277c478bd9Sstevel@tonic-gate 	}
6287c478bd9Sstevel@tonic-gate 
6297c478bd9Sstevel@tonic-gate 	/* Map in operational registers */
6307c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(dip, index, (caddr_t *)&uhcip->uhci_regsp,
6317c478bd9Sstevel@tonic-gate 	    0, sizeof (hc_regs_t), &attr, &uhcip->uhci_regs_handle) !=
6327c478bd9Sstevel@tonic-gate 	    DDI_SUCCESS) {
633d291d9f2Sfrits 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
6347c478bd9Sstevel@tonic-gate 		    "ddi_regs_map_setup: failed");
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
6377c478bd9Sstevel@tonic-gate 	}
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 	if (pci_config_setup(dip, &uhcip->uhci_config_handle) != DDI_SUCCESS) {
6407c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
6417c478bd9Sstevel@tonic-gate 		    "uhci_map_regs: Config error");
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
6447c478bd9Sstevel@tonic-gate 	}
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 	/* Make sure Memory Access Enable and Master Enable are set */
6477c478bd9Sstevel@tonic-gate 	command_reg = pci_config_get16(uhcip->uhci_config_handle,
6487c478bd9Sstevel@tonic-gate 	    PCI_CONF_COMM);
6497c478bd9Sstevel@tonic-gate 	if (!(command_reg & (PCI_COMM_MAE | PCI_COMM_ME))) {
6507c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
6517c478bd9Sstevel@tonic-gate 		    "uhci_map_regs: No MAE/ME");
6527c478bd9Sstevel@tonic-gate 	}
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 	command_reg |= PCI_COMM_MAE | PCI_COMM_ME;
6557c478bd9Sstevel@tonic-gate 	pci_config_put16(uhcip->uhci_config_handle, PCI_CONF_COMM, command_reg);
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	/*
6587c478bd9Sstevel@tonic-gate 	 * Check whether I/O base address is configured and enabled.
6597c478bd9Sstevel@tonic-gate 	 */
6607c478bd9Sstevel@tonic-gate 	if (!(command_reg & PCI_COMM_IO)) {
6617c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
6627c478bd9Sstevel@tonic-gate 		    "I/O Base address access disabled");
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
6657c478bd9Sstevel@tonic-gate 	}
6667c478bd9Sstevel@tonic-gate 	/*
6677c478bd9Sstevel@tonic-gate 	 * Get the IO base address of the controller
6687c478bd9Sstevel@tonic-gate 	 */
6697c478bd9Sstevel@tonic-gate 	uhcip->uhci_iobase = (pci_config_get16(uhcip->uhci_config_handle,
6707c478bd9Sstevel@tonic-gate 	    PCI_CONF_IOBASE) & PCI_CONF_IOBASE_MASK);
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
6737c478bd9Sstevel@tonic-gate 	    "uhci_map_regs: Completed");
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
6767c478bd9Sstevel@tonic-gate }
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate void
uhci_unmap_regs(uhci_state_t * uhcip)6807c478bd9Sstevel@tonic-gate uhci_unmap_regs(uhci_state_t *uhcip)
6817c478bd9Sstevel@tonic-gate {
6827c478bd9Sstevel@tonic-gate 	/* Unmap the UHCI registers */
6837c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_regs_handle) {
6847c478bd9Sstevel@tonic-gate 		/* Reset the host controller */
6857c478bd9Sstevel@tonic-gate 		Set_OpReg16(USBCMD, USBCMD_REG_GBL_RESET);
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&uhcip->uhci_regs_handle);
6887c478bd9Sstevel@tonic-gate 	}
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_config_handle) {
6917c478bd9Sstevel@tonic-gate 		pci_config_teardown(&uhcip->uhci_config_handle);
6927c478bd9Sstevel@tonic-gate 	}
6937c478bd9Sstevel@tonic-gate }
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate /*
6977c478bd9Sstevel@tonic-gate  * uhci_set_dma_attributes:
6987c478bd9Sstevel@tonic-gate  *	Set the limits in the DMA attributes structure. Most of the values used
6997c478bd9Sstevel@tonic-gate  *	in the	DMA limit structres are the default values as specified by  the
7007c478bd9Sstevel@tonic-gate  *	Writing PCI device drivers document.
7017c478bd9Sstevel@tonic-gate  */
7027c478bd9Sstevel@tonic-gate void
uhci_set_dma_attributes(uhci_state_t * uhcip)7037c478bd9Sstevel@tonic-gate uhci_set_dma_attributes(uhci_state_t *uhcip)
7047c478bd9Sstevel@tonic-gate {
7057c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
7067c478bd9Sstevel@tonic-gate 	    "uhci_set_dma_attributes:");
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 	/* Initialize the DMA attributes */
7097c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_version = DMA_ATTR_V0;
7107c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_addr_lo = 0x00000000ull;
7117c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_addr_hi = 0xfffffff0ull;
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 	/* 32 bit addressing */
7147c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_count_max = 0xffffffull;
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	/*
7177c478bd9Sstevel@tonic-gate 	 * Setting the dam_att_align to 512, some times fails the
7187c478bd9Sstevel@tonic-gate 	 * binding handle. I dont know why ? But setting to 16 will
7197c478bd9Sstevel@tonic-gate 	 * be right for our case (16 byte alignment required per
7207c478bd9Sstevel@tonic-gate 	 * UHCI spec for TD descriptors).
7217c478bd9Sstevel@tonic-gate 	 */
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate 	/* 16 byte alignment */
7247c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_align = 0x10;
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 	/*
7277c478bd9Sstevel@tonic-gate 	 * Since PCI  specification is byte alignment, the
7287c478bd9Sstevel@tonic-gate 	 * burstsize field should be set to 1 for PCI devices.
7297c478bd9Sstevel@tonic-gate 	 */
7307c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_burstsizes = 0x1;
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_minxfer	= 0x1;
7337c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_maxxfer	= 0xffffffull;
7347c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_seg	= 0xffffffffull;
7357c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_sgllen	= 1;
7367c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_granular	= 1;
7377c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_flags	= 0;
7387c478bd9Sstevel@tonic-gate }
7397c478bd9Sstevel@tonic-gate 
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate uint_t
pow_2(uint_t x)7427c478bd9Sstevel@tonic-gate pow_2(uint_t x)
7437c478bd9Sstevel@tonic-gate {
7447c478bd9Sstevel@tonic-gate 	return ((x == 0) ? 1 : (1 << x));
7457c478bd9Sstevel@tonic-gate }
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate uint_t
log_2(uint_t x)7497c478bd9Sstevel@tonic-gate log_2(uint_t x)
7507c478bd9Sstevel@tonic-gate {
7517c478bd9Sstevel@tonic-gate 	int ret_val = 0;
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate 	while (x != 1) {
7547c478bd9Sstevel@tonic-gate 		ret_val++;
7557c478bd9Sstevel@tonic-gate 		x = x >> 1;
7567c478bd9Sstevel@tonic-gate 	}
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 	return (ret_val);
7597c478bd9Sstevel@tonic-gate }
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate /*
7637c478bd9Sstevel@tonic-gate  * uhci_obtain_state:
7647c478bd9Sstevel@tonic-gate  */
7657c478bd9Sstevel@tonic-gate uhci_state_t *
uhci_obtain_state(dev_info_t * dip)7667c478bd9Sstevel@tonic-gate uhci_obtain_state(dev_info_t *dip)
7677c478bd9Sstevel@tonic-gate {
7687c478bd9Sstevel@tonic-gate 	int instance = ddi_get_instance(dip);
7697c478bd9Sstevel@tonic-gate 	uhci_state_t *state = ddi_get_soft_state(uhci_statep, instance);
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	ASSERT(state != NULL);
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 	return (state);
7747c478bd9Sstevel@tonic-gate }
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate /*
7787c478bd9Sstevel@tonic-gate  * uhci_alloc_hcdi_ops:
7797c478bd9Sstevel@tonic-gate  *	The HCDI interfaces or entry points are the software interfaces used by
7807c478bd9Sstevel@tonic-gate  *	the Universal Serial Bus Driver  (USBA) to  access the services of the
7817c478bd9Sstevel@tonic-gate  *	Host Controller Driver (HCD).  During HCD initialization, inform  USBA
7827c478bd9Sstevel@tonic-gate  *	about all available HCDI interfaces or entry points.
7837c478bd9Sstevel@tonic-gate  */
7847c478bd9Sstevel@tonic-gate usba_hcdi_ops_t *
uhci_alloc_hcdi_ops(uhci_state_t * uhcip)7857c478bd9Sstevel@tonic-gate uhci_alloc_hcdi_ops(uhci_state_t *uhcip)
7867c478bd9Sstevel@tonic-gate {
7877c478bd9Sstevel@tonic-gate 	usba_hcdi_ops_t	*hcdi_ops;
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
7907c478bd9Sstevel@tonic-gate 	    "uhci_alloc_hcdi_ops:");
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate 	hcdi_ops = usba_alloc_hcdi_ops();
7937c478bd9Sstevel@tonic-gate 
79428cdc3d7Sszhou 	hcdi_ops->usba_hcdi_ops_version = HCDI_OPS_VERSION_1;
79528cdc3d7Sszhou 
7967c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_pipe_open = uhci_hcdi_pipe_open;
7977c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_pipe_close	= uhci_hcdi_pipe_close;
7987c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_pipe_reset = uhci_hcdi_pipe_reset;
799269552cdSguoqing zhu - Sun Microsystems - Beijing China 	hcdi_ops->usba_hcdi_pipe_reset_data_toggle =
800269552cdSguoqing zhu - Sun Microsystems - Beijing China 	    uhci_hcdi_pipe_reset_data_toggle;
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_pipe_ctrl_xfer = uhci_hcdi_pipe_ctrl_xfer;
8037c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_pipe_bulk_xfer = uhci_hcdi_pipe_bulk_xfer;
8047c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_pipe_intr_xfer = uhci_hcdi_pipe_intr_xfer;
8057c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_pipe_isoc_xfer = uhci_hcdi_pipe_isoc_xfer;
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_bulk_transfer_size = uhci_hcdi_bulk_transfer_size;
8087c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_pipe_stop_intr_polling =
809fffe0b30Sqz 	    uhci_hcdi_pipe_stop_intr_polling;
8107c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_pipe_stop_isoc_polling =
811fffe0b30Sqz 	    uhci_hcdi_pipe_stop_isoc_polling;
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_get_current_frame_number =
814fffe0b30Sqz 	    uhci_hcdi_get_current_frame_number;
8157c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_get_max_isoc_pkts = uhci_hcdi_get_max_isoc_pkts;
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_console_input_init = uhci_hcdi_polled_input_init;
8187c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_console_input_enter = uhci_hcdi_polled_input_enter;
8197c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_console_read = uhci_hcdi_polled_read;
8207c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_console_input_exit = uhci_hcdi_polled_input_exit;
8217c478bd9Sstevel@tonic-gate 	hcdi_ops->usba_hcdi_console_input_fini = uhci_hcdi_polled_input_fini;
8227c478bd9Sstevel@tonic-gate 
82328cdc3d7Sszhou 	hcdi_ops->usba_hcdi_console_output_init = uhci_hcdi_polled_output_init;
82428cdc3d7Sszhou 	hcdi_ops->usba_hcdi_console_output_enter =
82528cdc3d7Sszhou 	    uhci_hcdi_polled_output_enter;
82628cdc3d7Sszhou 	hcdi_ops->usba_hcdi_console_write = uhci_hcdi_polled_write;
82728cdc3d7Sszhou 	hcdi_ops->usba_hcdi_console_output_exit = uhci_hcdi_polled_output_exit;
82828cdc3d7Sszhou 	hcdi_ops->usba_hcdi_console_output_fini = uhci_hcdi_polled_output_fini;
82928cdc3d7Sszhou 
8307c478bd9Sstevel@tonic-gate 	return (hcdi_ops);
8317c478bd9Sstevel@tonic-gate }
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate /*
8357c478bd9Sstevel@tonic-gate  * uhci_init_frame_lst_table :
8367c478bd9Sstevel@tonic-gate  *	Allocate the system memory and initialize Host Controller
8377c478bd9Sstevel@tonic-gate  *	Frame list table area The starting of the Frame list Table
8387c478bd9Sstevel@tonic-gate  *	area must be 4096 byte aligned.
8397c478bd9Sstevel@tonic-gate  */
8407c478bd9Sstevel@tonic-gate static int
uhci_init_frame_lst_table(dev_info_t * dip,uhci_state_t * uhcip)8417c478bd9Sstevel@tonic-gate uhci_init_frame_lst_table(dev_info_t *dip, uhci_state_t *uhcip)
8427c478bd9Sstevel@tonic-gate {
8437c478bd9Sstevel@tonic-gate 	int			result;
8447c478bd9Sstevel@tonic-gate 	uint_t			ccount;
8457c478bd9Sstevel@tonic-gate 	size_t			real_length;
8467c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t	dev_attr;
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
8497c478bd9Sstevel@tonic-gate 
8507c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
8517c478bd9Sstevel@tonic-gate 	    "uhci_init_frame_lst_table:");
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 	/* The host controller will be little endian */
8547c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_version		= DDI_DEVICE_ATTR_V0;
8557c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_endian_flags	= DDI_STRUCTURE_LE_ACC;
8567c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_dataorder		= DDI_STRICTORDER_ACC;
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	/* 4K alignment required */
8597c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_align = 0x1000;
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate 	/* Create space for the HCCA block */
8627c478bd9Sstevel@tonic-gate 	if (ddi_dma_alloc_handle(dip, &uhcip->uhci_dma_attr, DDI_DMA_SLEEP,
8637c478bd9Sstevel@tonic-gate 	    0, &uhcip->uhci_flt_dma_handle) != DDI_SUCCESS) {
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
8667c478bd9Sstevel@tonic-gate 	}
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	/* Reset to default 16 bytes */
8697c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_attr.dma_attr_align = 0x10;
8707c478bd9Sstevel@tonic-gate 
8717c478bd9Sstevel@tonic-gate 	if (ddi_dma_mem_alloc(uhcip->uhci_flt_dma_handle,
8727c478bd9Sstevel@tonic-gate 	    SIZE_OF_FRAME_LST_TABLE, &dev_attr, DDI_DMA_CONSISTENT,
8737c478bd9Sstevel@tonic-gate 	    DDI_DMA_SLEEP, 0, (caddr_t *)&uhcip->uhci_frame_lst_tablep,
8747c478bd9Sstevel@tonic-gate 	    &real_length, &uhcip->uhci_flt_mem_handle)) {
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
8777c478bd9Sstevel@tonic-gate 	}
8787c478bd9Sstevel@tonic-gate 
8797c478bd9Sstevel@tonic-gate 	/* Map the whole Frame list base area into the I/O address space */
8807c478bd9Sstevel@tonic-gate 	result = ddi_dma_addr_bind_handle(uhcip->uhci_flt_dma_handle,
8817c478bd9Sstevel@tonic-gate 	    NULL, (caddr_t)uhcip->uhci_frame_lst_tablep, real_length,
8827c478bd9Sstevel@tonic-gate 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
8837c478bd9Sstevel@tonic-gate 	    &uhcip->uhci_flt_cookie, &ccount);
8847c478bd9Sstevel@tonic-gate 
8857c478bd9Sstevel@tonic-gate 	if (result == DDI_DMA_MAPPED) {
8867c478bd9Sstevel@tonic-gate 		/* The cookie count should be 1 */
8877c478bd9Sstevel@tonic-gate 		if (ccount != 1) {
8887c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
8897c478bd9Sstevel@tonic-gate 			    "uhci_init_frame_list_table: More than 1 cookie");
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
8927c478bd9Sstevel@tonic-gate 		}
8937c478bd9Sstevel@tonic-gate 	} else {
8947c478bd9Sstevel@tonic-gate 		uhci_decode_ddi_dma_addr_bind_handle_result(uhcip, result);
8957c478bd9Sstevel@tonic-gate 
8967c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
8977c478bd9Sstevel@tonic-gate 	}
8987c478bd9Sstevel@tonic-gate 
8997c478bd9Sstevel@tonic-gate 	uhcip->uhci_dma_addr_bind_flag |= UHCI_FLA_POOL_BOUND;
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 	bzero((void *)uhcip->uhci_frame_lst_tablep, real_length);
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 	/* Initialize the interrupt lists */
9047c478bd9Sstevel@tonic-gate 	uhci_build_interrupt_lattice(uhcip);
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
9077c478bd9Sstevel@tonic-gate }
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 
9107c478bd9Sstevel@tonic-gate /*
9117c478bd9Sstevel@tonic-gate  * uhci_alloc_queue_head:
9127c478bd9Sstevel@tonic-gate  *	Allocate a queue head
9137c478bd9Sstevel@tonic-gate  */
9147c478bd9Sstevel@tonic-gate queue_head_t *
uhci_alloc_queue_head(uhci_state_t * uhcip)9157c478bd9Sstevel@tonic-gate uhci_alloc_queue_head(uhci_state_t *uhcip)
9167c478bd9Sstevel@tonic-gate {
9177c478bd9Sstevel@tonic-gate 	int		index;
9187c478bd9Sstevel@tonic-gate 	uhci_td_t	*dummy_td;
9197c478bd9Sstevel@tonic-gate 	queue_head_t	*queue_head;
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
9227c478bd9Sstevel@tonic-gate 	    "uhci_alloc_queue_head");
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 	/* Allocate a dummy td first. */
9277c478bd9Sstevel@tonic-gate 	if ((dummy_td = uhci_allocate_td_from_pool(uhcip)) == NULL) {
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALLOC,  uhcip->uhci_log_hdl,
930fffe0b30Sqz 		    "uhci_alloc_queue_head: allocate td from pool failed");
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 		return (NULL);
9337c478bd9Sstevel@tonic-gate 	}
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	/*
9367c478bd9Sstevel@tonic-gate 	 * The first 63 queue heads in the Queue Head (QH)
9377c478bd9Sstevel@tonic-gate 	 * buffer pool are reserved for building interrupt lattice
9387c478bd9Sstevel@tonic-gate 	 * tree. Search for a blank Queue head in the QH buffer pool.
9397c478bd9Sstevel@tonic-gate 	 */
9407c478bd9Sstevel@tonic-gate 	for (index = NUM_STATIC_NODES; index < uhci_qh_pool_size; index++) {
9417c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_qh_pool_addr[index].qh_flag ==
9427c478bd9Sstevel@tonic-gate 		    QUEUE_HEAD_FLAG_FREE) {
9437c478bd9Sstevel@tonic-gate 			break;
9447c478bd9Sstevel@tonic-gate 		}
9457c478bd9Sstevel@tonic-gate 	}
9467c478bd9Sstevel@tonic-gate 
9477c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
9487c478bd9Sstevel@tonic-gate 	    "uhci_alloc_queue_head: Allocated %d", index);
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 	if (index == uhci_qh_pool_size) {
951d291d9f2Sfrits 		USB_DPRINTF_L2(PRINT_MASK_ALLOC,  uhcip->uhci_log_hdl,
9527c478bd9Sstevel@tonic-gate 		    "uhci_alloc_queue_head: All QH exhausted");
9537c478bd9Sstevel@tonic-gate 
9547c478bd9Sstevel@tonic-gate 		/* Free the dummy td allocated for this qh. */
9557c478bd9Sstevel@tonic-gate 		dummy_td->flag = TD_FLAG_FREE;
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 		return (NULL);
9587c478bd9Sstevel@tonic-gate 	}
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate 	queue_head = &uhcip->uhci_qh_pool_addr[index];
9617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
962112116d8Sfb 	    "uhci_alloc_queue_head: Allocated address 0x%p",
963112116d8Sfb 	    (void *)queue_head);
9647c478bd9Sstevel@tonic-gate 
9657c478bd9Sstevel@tonic-gate 	bzero((void *)queue_head, sizeof (queue_head_t));
9667c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, queue_head->link_ptr, HC_END_OF_LIST);
9677c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, queue_head->element_ptr, HC_END_OF_LIST);
9687c478bd9Sstevel@tonic-gate 	queue_head->prev_qh	= NULL;
9697c478bd9Sstevel@tonic-gate 	queue_head->qh_flag	= QUEUE_HEAD_FLAG_BUSY;
9707c478bd9Sstevel@tonic-gate 
9717c478bd9Sstevel@tonic-gate 	bzero((char *)dummy_td, sizeof (uhci_td_t));
9727c478bd9Sstevel@tonic-gate 	queue_head->td_tailp	= dummy_td;
9737c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, queue_head->element_ptr, TD_PADDR(dummy_td));
9747c478bd9Sstevel@tonic-gate 
9757c478bd9Sstevel@tonic-gate 	return (queue_head);
9767c478bd9Sstevel@tonic-gate }
9777c478bd9Sstevel@tonic-gate 
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate /*
9807c478bd9Sstevel@tonic-gate  * uhci_allocate_bandwidth:
9817c478bd9Sstevel@tonic-gate  *	Figure out whether or not this interval may be supported. Return
9827c478bd9Sstevel@tonic-gate  *	the index into the  lattice if it can be supported.  Return
9837c478bd9Sstevel@tonic-gate  *	allocation failure if it can not be supported.
9847c478bd9Sstevel@tonic-gate  */
9857c478bd9Sstevel@tonic-gate int
uhci_allocate_bandwidth(uhci_state_t * uhcip,usba_pipe_handle_data_t * pipe_handle,uint_t * node)9867c478bd9Sstevel@tonic-gate uhci_allocate_bandwidth(
9877c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
9887c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*pipe_handle,
9897c478bd9Sstevel@tonic-gate 	uint_t			*node)
9907c478bd9Sstevel@tonic-gate {
9917c478bd9Sstevel@tonic-gate 	int		bandwidth;	/* Requested bandwidth */
9927c478bd9Sstevel@tonic-gate 	uint_t		min, min_index;
9937c478bd9Sstevel@tonic-gate 	uint_t		i;
9947c478bd9Sstevel@tonic-gate 	uint_t		height;		/* Bandwidth's height in the tree */
9957c478bd9Sstevel@tonic-gate 	uint_t		leftmost;
9967c478bd9Sstevel@tonic-gate 	uint_t		length;
9977c478bd9Sstevel@tonic-gate 	uint32_t	paddr;
9987c478bd9Sstevel@tonic-gate 	queue_head_t	*tmp_qh;
9997c478bd9Sstevel@tonic-gate 	usb_ep_descr_t	*endpoint = &pipe_handle->p_ep;
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate 	/*
10047c478bd9Sstevel@tonic-gate 	 * Calculate the length in bytes of a transaction on this
10057c478bd9Sstevel@tonic-gate 	 * periodic endpoint.
10067c478bd9Sstevel@tonic-gate 	 */
10077c478bd9Sstevel@tonic-gate 	mutex_enter(&pipe_handle->p_usba_device->usb_mutex);
10087c478bd9Sstevel@tonic-gate 
10097c478bd9Sstevel@tonic-gate 	length = uhci_compute_total_bandwidth(endpoint,
1010fffe0b30Sqz 	    pipe_handle->p_usba_device->usb_port_status);
10117c478bd9Sstevel@tonic-gate 	mutex_exit(&pipe_handle->p_usba_device->usb_mutex);
10127c478bd9Sstevel@tonic-gate 
10137c478bd9Sstevel@tonic-gate 	/*
10147c478bd9Sstevel@tonic-gate 	 * If the length in bytes plus the allocated bandwidth exceeds
10157c478bd9Sstevel@tonic-gate 	 * the maximum, return bandwidth allocation failure.
10167c478bd9Sstevel@tonic-gate 	 */
10177c478bd9Sstevel@tonic-gate 	if ((length + uhcip->uhci_bandwidth_intr_min +
1018fffe0b30Sqz 	    uhcip->uhci_bandwidth_isoch_sum) > (MAX_PERIODIC_BANDWIDTH)) {
10197c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
10207c478bd9Sstevel@tonic-gate 		    "uhci_allocate_bandwidth: "
10217c478bd9Sstevel@tonic-gate 		    "Reached maximum bandwidth value and cannot allocate "
10227c478bd9Sstevel@tonic-gate 		    "bandwidth for a given Interrupt/Isoch endpoint");
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 		return (USB_NO_BANDWIDTH);
10257c478bd9Sstevel@tonic-gate 	}
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate 	/*
10287c478bd9Sstevel@tonic-gate 	 * ISOC xfers are not supported at this point type
10297c478bd9Sstevel@tonic-gate 	 */
10307c478bd9Sstevel@tonic-gate 	if (UHCI_XFER_TYPE(endpoint) == USB_EP_ATTR_ISOCH) {
10317c478bd9Sstevel@tonic-gate 		uhcip->uhci_bandwidth_isoch_sum += length;
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
10347c478bd9Sstevel@tonic-gate 	}
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate 	/*
10377c478bd9Sstevel@tonic-gate 	 * This is an interrupt endpoint.
10387c478bd9Sstevel@tonic-gate 	 * Adjust bandwidth to be a power of 2
10397c478bd9Sstevel@tonic-gate 	 */
10407c478bd9Sstevel@tonic-gate 	mutex_enter(&pipe_handle->p_usba_device->usb_mutex);
10417c478bd9Sstevel@tonic-gate 	bandwidth = uhci_bandwidth_adjust(uhcip, endpoint,
1042fffe0b30Sqz 	    pipe_handle->p_usba_device->usb_port_status);
10437c478bd9Sstevel@tonic-gate 	mutex_exit(&pipe_handle->p_usba_device->usb_mutex);
10447c478bd9Sstevel@tonic-gate 
10457c478bd9Sstevel@tonic-gate 	/*
10467c478bd9Sstevel@tonic-gate 	 * If this bandwidth can't be supported,
10477c478bd9Sstevel@tonic-gate 	 * return allocation failure.
10487c478bd9Sstevel@tonic-gate 	 */
10497c478bd9Sstevel@tonic-gate 	if (bandwidth == USB_FAILURE) {
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
10527c478bd9Sstevel@tonic-gate 	}
10537c478bd9Sstevel@tonic-gate 
10547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_BW, uhcip->uhci_log_hdl,
10557c478bd9Sstevel@tonic-gate 	    "The new bandwidth is %d", bandwidth);
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate 	/* Find the leaf with the smallest allocated bandwidth */
10587c478bd9Sstevel@tonic-gate 	min_index = 0;
10597c478bd9Sstevel@tonic-gate 	min = uhcip->uhci_bandwidth[0];
10607c478bd9Sstevel@tonic-gate 
10617c478bd9Sstevel@tonic-gate 	for (i = 1; i < NUM_FRAME_LST_ENTRIES; i++) {
10627c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_bandwidth[i] < min) {
10637c478bd9Sstevel@tonic-gate 			min_index = i;
10647c478bd9Sstevel@tonic-gate 			min = uhcip->uhci_bandwidth[i];
10657c478bd9Sstevel@tonic-gate 		}
10667c478bd9Sstevel@tonic-gate 	}
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_BW, uhcip->uhci_log_hdl,
10697c478bd9Sstevel@tonic-gate 	    "The leaf with minimal bandwidth %d, "
10707c478bd9Sstevel@tonic-gate 	    "The smallest bandwidth %d", min_index, min);
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 	/*
10737c478bd9Sstevel@tonic-gate 	 * Find the index into the lattice given the
10747c478bd9Sstevel@tonic-gate 	 * leaf with the smallest allocated bandwidth.
10757c478bd9Sstevel@tonic-gate 	 */
10767c478bd9Sstevel@tonic-gate 	height = uhci_lattice_height(bandwidth);
10777c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_BW, uhcip->uhci_log_hdl,
10787c478bd9Sstevel@tonic-gate 	    "The height is %d", height);
10797c478bd9Sstevel@tonic-gate 
10807c478bd9Sstevel@tonic-gate 	*node = uhci_tree_bottom_nodes[min_index];
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 	/* check if there are isocs TDs scheduled for this frame */
10837c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_isoc_q_tailp[*node]) {
10847c478bd9Sstevel@tonic-gate 		paddr = (uhcip->uhci_isoc_q_tailp[*node]->link_ptr &
1085fffe0b30Sqz 		    FRAME_LST_PTR_MASK);
10867c478bd9Sstevel@tonic-gate 	} else {
10877c478bd9Sstevel@tonic-gate 		paddr = (uhcip->uhci_frame_lst_tablep[*node] &
1088fffe0b30Sqz 		    FRAME_LST_PTR_MASK);
10897c478bd9Sstevel@tonic-gate 	}
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 	tmp_qh = QH_VADDR(paddr);
10927c478bd9Sstevel@tonic-gate 	*node = tmp_qh->node;
10937c478bd9Sstevel@tonic-gate 	for (i = 0; i < height; i++) {
10947c478bd9Sstevel@tonic-gate 		*node = uhci_lattice_parent(*node);
10957c478bd9Sstevel@tonic-gate 	}
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_BW, uhcip->uhci_log_hdl,
10987c478bd9Sstevel@tonic-gate 	    "The real node is %d", *node);
10997c478bd9Sstevel@tonic-gate 
11007c478bd9Sstevel@tonic-gate 	/*
11017c478bd9Sstevel@tonic-gate 	 * Find the leftmost leaf in the subtree specified by the node.
11027c478bd9Sstevel@tonic-gate 	 */
11037c478bd9Sstevel@tonic-gate 	leftmost = uhci_leftmost_leaf(*node, height);
11047c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_BW, uhcip->uhci_log_hdl,
11057c478bd9Sstevel@tonic-gate 	    "Leftmost %d", leftmost);
11067c478bd9Sstevel@tonic-gate 
11077c478bd9Sstevel@tonic-gate 	for (i = leftmost; i < leftmost +
11087c478bd9Sstevel@tonic-gate 	    (NUM_FRAME_LST_ENTRIES/bandwidth); i ++) {
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate 		if ((length + uhcip->uhci_bandwidth_isoch_sum +
1111fffe0b30Sqz 		    uhcip->uhci_bandwidth[i]) > MAX_PERIODIC_BANDWIDTH) {
11127c478bd9Sstevel@tonic-gate 
11137c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
11147c478bd9Sstevel@tonic-gate 			    "uhci_allocate_bandwidth: "
11157c478bd9Sstevel@tonic-gate 			    "Reached maximum bandwidth value and cannot "
11167c478bd9Sstevel@tonic-gate 			    "allocate bandwidth for Interrupt endpoint");
11177c478bd9Sstevel@tonic-gate 
11187c478bd9Sstevel@tonic-gate 			return (USB_NO_BANDWIDTH);
11197c478bd9Sstevel@tonic-gate 		}
11207c478bd9Sstevel@tonic-gate 	}
11217c478bd9Sstevel@tonic-gate 
11227c478bd9Sstevel@tonic-gate 	/*
11237c478bd9Sstevel@tonic-gate 	 * All the leaves for this node must be updated with the bandwidth.
11247c478bd9Sstevel@tonic-gate 	 */
11257c478bd9Sstevel@tonic-gate 	for (i = leftmost; i < leftmost +
11267c478bd9Sstevel@tonic-gate 	    (NUM_FRAME_LST_ENTRIES/bandwidth); i ++) {
11277c478bd9Sstevel@tonic-gate 		uhcip->uhci_bandwidth[i] += length;
11287c478bd9Sstevel@tonic-gate 	}
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate 	/* Find the leaf with the smallest allocated bandwidth */
11317c478bd9Sstevel@tonic-gate 	min_index = 0;
11327c478bd9Sstevel@tonic-gate 	min = uhcip->uhci_bandwidth[0];
11337c478bd9Sstevel@tonic-gate 
11347c478bd9Sstevel@tonic-gate 	for (i = 1; i < NUM_FRAME_LST_ENTRIES; i++) {
11357c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_bandwidth[i] < min) {
11367c478bd9Sstevel@tonic-gate 			min_index = i;
11377c478bd9Sstevel@tonic-gate 			min = uhcip->uhci_bandwidth[i];
11387c478bd9Sstevel@tonic-gate 		}
11397c478bd9Sstevel@tonic-gate 	}
11407c478bd9Sstevel@tonic-gate 
11417c478bd9Sstevel@tonic-gate 	/* Save the minimum for later use */
11427c478bd9Sstevel@tonic-gate 	uhcip->uhci_bandwidth_intr_min = min;
11437c478bd9Sstevel@tonic-gate 
11447c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
11457c478bd9Sstevel@tonic-gate }
11467c478bd9Sstevel@tonic-gate 
11477c478bd9Sstevel@tonic-gate 
11487c478bd9Sstevel@tonic-gate /*
11497c478bd9Sstevel@tonic-gate  * uhci_deallocate_bandwidth:
11507c478bd9Sstevel@tonic-gate  *	Deallocate bandwidth for the given node in the lattice
11517c478bd9Sstevel@tonic-gate  *	and the length of transfer.
11527c478bd9Sstevel@tonic-gate  */
11537c478bd9Sstevel@tonic-gate void
uhci_deallocate_bandwidth(uhci_state_t * uhcip,usba_pipe_handle_data_t * pipe_handle)11547c478bd9Sstevel@tonic-gate uhci_deallocate_bandwidth(uhci_state_t *uhcip,
11557c478bd9Sstevel@tonic-gate     usba_pipe_handle_data_t *pipe_handle)
11567c478bd9Sstevel@tonic-gate {
11577c478bd9Sstevel@tonic-gate 	uint_t		bandwidth;
11587c478bd9Sstevel@tonic-gate 	uint_t		height;
11597c478bd9Sstevel@tonic-gate 	uint_t		leftmost;
11607c478bd9Sstevel@tonic-gate 	uint_t		i;
11617c478bd9Sstevel@tonic-gate 	uint_t		min;
11627c478bd9Sstevel@tonic-gate 	usb_ep_descr_t	*endpoint = &pipe_handle->p_ep;
11637c478bd9Sstevel@tonic-gate 	uint_t		node, length;
11647c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t *pp =
1165fffe0b30Sqz 	    (uhci_pipe_private_t *)pipe_handle->p_hcd_private;
11667c478bd9Sstevel@tonic-gate 
11677c478bd9Sstevel@tonic-gate 	/* This routine is protected by the uhci_int_mutex */
11687c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
11697c478bd9Sstevel@tonic-gate 
11707c478bd9Sstevel@tonic-gate 	/* Obtain the length */
11717c478bd9Sstevel@tonic-gate 	mutex_enter(&pipe_handle->p_usba_device->usb_mutex);
11727c478bd9Sstevel@tonic-gate 	length = uhci_compute_total_bandwidth(endpoint,
1173fffe0b30Sqz 	    pipe_handle->p_usba_device->usb_port_status);
11747c478bd9Sstevel@tonic-gate 	mutex_exit(&pipe_handle->p_usba_device->usb_mutex);
11757c478bd9Sstevel@tonic-gate 
11767c478bd9Sstevel@tonic-gate 	/*
11777c478bd9Sstevel@tonic-gate 	 * If this is an isochronous endpoint, just delete endpoint's
11787c478bd9Sstevel@tonic-gate 	 * bandwidth from the total allocated isochronous bandwidth.
11797c478bd9Sstevel@tonic-gate 	 */
11807c478bd9Sstevel@tonic-gate 	if (UHCI_XFER_TYPE(endpoint) == USB_EP_ATTR_ISOCH) {
11817c478bd9Sstevel@tonic-gate 		uhcip->uhci_bandwidth_isoch_sum -= length;
11827c478bd9Sstevel@tonic-gate 
11837c478bd9Sstevel@tonic-gate 		return;
11847c478bd9Sstevel@tonic-gate 	}
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 	/* Obtain the node */
11877c478bd9Sstevel@tonic-gate 	node = pp->pp_node;
11887c478bd9Sstevel@tonic-gate 
11897c478bd9Sstevel@tonic-gate 	/* Adjust bandwidth to be a power of 2 */
11907c478bd9Sstevel@tonic-gate 	mutex_enter(&pipe_handle->p_usba_device->usb_mutex);
11917c478bd9Sstevel@tonic-gate 	bandwidth = uhci_bandwidth_adjust(uhcip, endpoint,
1192fffe0b30Sqz 	    pipe_handle->p_usba_device->usb_port_status);
11937c478bd9Sstevel@tonic-gate 	mutex_exit(&pipe_handle->p_usba_device->usb_mutex);
11947c478bd9Sstevel@tonic-gate 
11957c478bd9Sstevel@tonic-gate 	/* Find the height in the tree */
11967c478bd9Sstevel@tonic-gate 	height = uhci_lattice_height(bandwidth);
11977c478bd9Sstevel@tonic-gate 
11987c478bd9Sstevel@tonic-gate 	/*
11997c478bd9Sstevel@tonic-gate 	 * Find the leftmost leaf in the subtree specified by the node
12007c478bd9Sstevel@tonic-gate 	 */
12017c478bd9Sstevel@tonic-gate 	leftmost = uhci_leftmost_leaf(node, height);
12027c478bd9Sstevel@tonic-gate 
12037c478bd9Sstevel@tonic-gate 	/* Delete the bandwith from the appropriate lists */
12047c478bd9Sstevel@tonic-gate 	for (i = leftmost; i < leftmost + (NUM_FRAME_LST_ENTRIES/bandwidth);
12057c478bd9Sstevel@tonic-gate 	    i ++) {
12067c478bd9Sstevel@tonic-gate 		uhcip->uhci_bandwidth[i] -= length;
12077c478bd9Sstevel@tonic-gate 	}
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate 	min = uhcip->uhci_bandwidth[0];
12107c478bd9Sstevel@tonic-gate 
12117c478bd9Sstevel@tonic-gate 	/* Recompute the minimum */
12127c478bd9Sstevel@tonic-gate 	for (i = 1; i < NUM_FRAME_LST_ENTRIES; i++) {
12137c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_bandwidth[i] < min) {
12147c478bd9Sstevel@tonic-gate 			min = uhcip->uhci_bandwidth[i];
12157c478bd9Sstevel@tonic-gate 		}
12167c478bd9Sstevel@tonic-gate 	}
12177c478bd9Sstevel@tonic-gate 
12187c478bd9Sstevel@tonic-gate 	/* Save the minimum for later use */
12197c478bd9Sstevel@tonic-gate 	uhcip->uhci_bandwidth_intr_min = min;
12207c478bd9Sstevel@tonic-gate }
12217c478bd9Sstevel@tonic-gate 
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate /*
12247c478bd9Sstevel@tonic-gate  * uhci_compute_total_bandwidth:
12257c478bd9Sstevel@tonic-gate  *
12267c478bd9Sstevel@tonic-gate  * Given a periodic endpoint (interrupt or isochronous) determine the total
12277c478bd9Sstevel@tonic-gate  * bandwidth for one transaction. The UHCI host controller traverses the
12287c478bd9Sstevel@tonic-gate  * endpoint descriptor lists on a first-come-first-serve basis. When the HC
12297c478bd9Sstevel@tonic-gate  * services an endpoint, only a single transaction attempt is made. The  HC
12307c478bd9Sstevel@tonic-gate  * moves to the next Endpoint Descriptor after the first transaction attempt
12317c478bd9Sstevel@tonic-gate  * rather than finishing the entire Transfer Descriptor. Therefore, when  a
12327c478bd9Sstevel@tonic-gate  * Transfer Descriptor is inserted into the lattice, we will only count the
12337c478bd9Sstevel@tonic-gate  * number of bytes for one transaction.
12347c478bd9Sstevel@tonic-gate  *
12357c478bd9Sstevel@tonic-gate  * The following are the formulas used for calculating bandwidth in terms
12367c478bd9Sstevel@tonic-gate  * bytes and it is for the single USB full speed and low speed	transaction
12377c478bd9Sstevel@tonic-gate  * respectively. The protocol overheads will be different for each of  type
12387c478bd9Sstevel@tonic-gate  * of USB transfer and all these formulas & protocol overheads are  derived
12397c478bd9Sstevel@tonic-gate  * from the 5.9.3 section of USB Specification & with the help of Bandwidth
12407c478bd9Sstevel@tonic-gate  * Analysis white paper which is posted on the USB  developer forum.
12417c478bd9Sstevel@tonic-gate  *
12427c478bd9Sstevel@tonic-gate  * Full-Speed:
12437c478bd9Sstevel@tonic-gate  *	  Protocol overhead  + ((MaxPacketSize * 7)/6 )  + Host_Delay
12447c478bd9Sstevel@tonic-gate  *
12457c478bd9Sstevel@tonic-gate  * Low-Speed:
12467c478bd9Sstevel@tonic-gate  *		Protocol overhead  + Hub LS overhead +
12477c478bd9Sstevel@tonic-gate  *		  (Low-Speed clock * ((MaxPacketSize * 7)/6 )) + Host_Delay
12487c478bd9Sstevel@tonic-gate  */
12497c478bd9Sstevel@tonic-gate static uint_t
uhci_compute_total_bandwidth(usb_ep_descr_t * endpoint,usb_port_status_t port_status)12507c478bd9Sstevel@tonic-gate uhci_compute_total_bandwidth(usb_ep_descr_t *endpoint,
12517c478bd9Sstevel@tonic-gate 		usb_port_status_t port_status)
12527c478bd9Sstevel@tonic-gate {
12537c478bd9Sstevel@tonic-gate 	uint_t		bandwidth;
12547c478bd9Sstevel@tonic-gate 	ushort_t	MaxPacketSize = endpoint->wMaxPacketSize;
12557c478bd9Sstevel@tonic-gate 
12567c478bd9Sstevel@tonic-gate 	/* Add Host Controller specific delay to required bandwidth */
12577c478bd9Sstevel@tonic-gate 	bandwidth = HOST_CONTROLLER_DELAY;
12587c478bd9Sstevel@tonic-gate 
12597c478bd9Sstevel@tonic-gate 	/* Add bit-stuffing overhead */
12607c478bd9Sstevel@tonic-gate 	MaxPacketSize = (ushort_t)((MaxPacketSize * 7) / 6);
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate 	/* Low Speed interrupt transaction */
12637c478bd9Sstevel@tonic-gate 	if (port_status == USBA_LOW_SPEED_DEV) {
12647c478bd9Sstevel@tonic-gate 		/* Low Speed interrupt transaction */
12657c478bd9Sstevel@tonic-gate 		bandwidth += (LOW_SPEED_PROTO_OVERHEAD +
1266fffe0b30Sqz 		    HUB_LOW_SPEED_PROTO_OVERHEAD +
1267fffe0b30Sqz 		    (LOW_SPEED_CLOCK * MaxPacketSize));
12687c478bd9Sstevel@tonic-gate 	} else {
12697c478bd9Sstevel@tonic-gate 		/* Full Speed transaction */
12707c478bd9Sstevel@tonic-gate 		bandwidth += MaxPacketSize;
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate 		if (UHCI_XFER_TYPE(endpoint) == USB_EP_ATTR_INTR) {
12737c478bd9Sstevel@tonic-gate 			/* Full Speed interrupt transaction */
12747c478bd9Sstevel@tonic-gate 			bandwidth += FS_NON_ISOC_PROTO_OVERHEAD;
12757c478bd9Sstevel@tonic-gate 		} else {
12767c478bd9Sstevel@tonic-gate 			/* Isochronus and input transaction */
12777c478bd9Sstevel@tonic-gate 			if (UHCI_XFER_DIR(endpoint) == USB_EP_DIR_IN) {
12787c478bd9Sstevel@tonic-gate 				bandwidth += FS_ISOC_INPUT_PROTO_OVERHEAD;
12797c478bd9Sstevel@tonic-gate 			} else {
12807c478bd9Sstevel@tonic-gate 				/* Isochronus and output transaction */
12817c478bd9Sstevel@tonic-gate 				bandwidth += FS_ISOC_OUTPUT_PROTO_OVERHEAD;
12827c478bd9Sstevel@tonic-gate 			}
12837c478bd9Sstevel@tonic-gate 		}
12847c478bd9Sstevel@tonic-gate 	}
12857c478bd9Sstevel@tonic-gate 
12867c478bd9Sstevel@tonic-gate 	return (bandwidth);
12877c478bd9Sstevel@tonic-gate }
12887c478bd9Sstevel@tonic-gate 
12897c478bd9Sstevel@tonic-gate 
12907c478bd9Sstevel@tonic-gate /*
12917c478bd9Sstevel@tonic-gate  * uhci_bandwidth_adjust:
12927c478bd9Sstevel@tonic-gate  */
12937c478bd9Sstevel@tonic-gate static int
uhci_bandwidth_adjust(uhci_state_t * uhcip,usb_ep_descr_t * endpoint,usb_port_status_t port_status)12947c478bd9Sstevel@tonic-gate uhci_bandwidth_adjust(
12957c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
12967c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*endpoint,
12977c478bd9Sstevel@tonic-gate 	usb_port_status_t	port_status)
12987c478bd9Sstevel@tonic-gate {
12997c478bd9Sstevel@tonic-gate 	int	i = 0;
13007c478bd9Sstevel@tonic-gate 	uint_t	interval;
13017c478bd9Sstevel@tonic-gate 
13027c478bd9Sstevel@tonic-gate 	/*
13037c478bd9Sstevel@tonic-gate 	 * Get the polling interval from the endpoint descriptor
13047c478bd9Sstevel@tonic-gate 	 */
13057c478bd9Sstevel@tonic-gate 	interval = endpoint->bInterval;
13067c478bd9Sstevel@tonic-gate 
13077c478bd9Sstevel@tonic-gate 	/*
13087c478bd9Sstevel@tonic-gate 	 * The bInterval value in the endpoint descriptor can range
13097c478bd9Sstevel@tonic-gate 	 * from 1 to 255ms. The interrupt lattice has 32 leaf nodes,
13107c478bd9Sstevel@tonic-gate 	 * and the host controller cycles through these nodes every
13117c478bd9Sstevel@tonic-gate 	 * 32ms. The longest polling  interval that the  controller
13127c478bd9Sstevel@tonic-gate 	 * supports is 32ms.
13137c478bd9Sstevel@tonic-gate 	 */
13147c478bd9Sstevel@tonic-gate 
13157c478bd9Sstevel@tonic-gate 	/*
13167c478bd9Sstevel@tonic-gate 	 * Return an error if the polling interval is less than 1ms
13177c478bd9Sstevel@tonic-gate 	 * and greater than 255ms
13187c478bd9Sstevel@tonic-gate 	 */
13197c478bd9Sstevel@tonic-gate 	if ((interval < MIN_POLL_INTERVAL) || (interval > MAX_POLL_INTERVAL)) {
13207c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
13217c478bd9Sstevel@tonic-gate 		    "uhci_bandwidth_adjust: Endpoint's poll interval must be "
13227c478bd9Sstevel@tonic-gate 		    "between %d and %d ms", MIN_POLL_INTERVAL,
13237c478bd9Sstevel@tonic-gate 		    MAX_POLL_INTERVAL);
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
13267c478bd9Sstevel@tonic-gate 	}
13277c478bd9Sstevel@tonic-gate 
13287c478bd9Sstevel@tonic-gate 	/*
13297c478bd9Sstevel@tonic-gate 	 * According USB Specifications, a  full-speed endpoint can
13307c478bd9Sstevel@tonic-gate 	 * specify a desired polling interval 1ms to 255ms and a low
13317c478bd9Sstevel@tonic-gate 	 * speed  endpoints are limited to  specifying only 10ms to
13327c478bd9Sstevel@tonic-gate 	 * 255ms. But some old keyboards & mice uses polling interval
13337c478bd9Sstevel@tonic-gate 	 * of 8ms. For compatibility  purpose, we are using polling
13347c478bd9Sstevel@tonic-gate 	 * interval between 8ms & 255ms for low speed endpoints.
13357c478bd9Sstevel@tonic-gate 	 */
13367c478bd9Sstevel@tonic-gate 	if ((port_status == USBA_LOW_SPEED_DEV) &&
13377c478bd9Sstevel@tonic-gate 	    (interval < MIN_LOW_SPEED_POLL_INTERVAL)) {
1338d291d9f2Sfrits 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
13397c478bd9Sstevel@tonic-gate 		    "uhci_bandwidth_adjust: Low speed endpoint's poll interval "
13407c478bd9Sstevel@tonic-gate 		    "must be >= %d ms, adjusted",
13417c478bd9Sstevel@tonic-gate 		    MIN_LOW_SPEED_POLL_INTERVAL);
13427c478bd9Sstevel@tonic-gate 
13437c478bd9Sstevel@tonic-gate 		interval = MIN_LOW_SPEED_POLL_INTERVAL;
13447c478bd9Sstevel@tonic-gate 	}
13457c478bd9Sstevel@tonic-gate 
13467c478bd9Sstevel@tonic-gate 	/*
13477c478bd9Sstevel@tonic-gate 	 * If polling interval is greater than 32ms,
13487c478bd9Sstevel@tonic-gate 	 * adjust polling interval equal to 32ms.
13497c478bd9Sstevel@tonic-gate 	 */
13507c478bd9Sstevel@tonic-gate 	if (interval > 32) {
13517c478bd9Sstevel@tonic-gate 		interval = 32;
13527c478bd9Sstevel@tonic-gate 	}
13537c478bd9Sstevel@tonic-gate 
13547c478bd9Sstevel@tonic-gate 	/*
13557c478bd9Sstevel@tonic-gate 	 * Find the nearest power of 2 that's less
13567c478bd9Sstevel@tonic-gate 	 * than interval.
13577c478bd9Sstevel@tonic-gate 	 */
13587c478bd9Sstevel@tonic-gate 	while ((pow_2(i)) <= interval) {
13597c478bd9Sstevel@tonic-gate 		i++;
13607c478bd9Sstevel@tonic-gate 	}
13617c478bd9Sstevel@tonic-gate 
13627c478bd9Sstevel@tonic-gate 	return (pow_2((i - 1)));
13637c478bd9Sstevel@tonic-gate }
13647c478bd9Sstevel@tonic-gate 
13657c478bd9Sstevel@tonic-gate 
13667c478bd9Sstevel@tonic-gate /*
13677c478bd9Sstevel@tonic-gate  * uhci_lattice_height:
13687c478bd9Sstevel@tonic-gate  *	Given the requested bandwidth, find the height in the tree at
13697c478bd9Sstevel@tonic-gate  *	which the nodes for this bandwidth fall.  The height is measured
13707c478bd9Sstevel@tonic-gate  *	as the number of nodes from the leaf to the level specified by
13717c478bd9Sstevel@tonic-gate  *	bandwidth The root of the tree is at height TREE_HEIGHT.
13727c478bd9Sstevel@tonic-gate  */
13737c478bd9Sstevel@tonic-gate static uint_t
uhci_lattice_height(uint_t bandwidth)13747c478bd9Sstevel@tonic-gate uhci_lattice_height(uint_t bandwidth)
13757c478bd9Sstevel@tonic-gate {
13767c478bd9Sstevel@tonic-gate 	return (TREE_HEIGHT - (log_2(bandwidth)));
13777c478bd9Sstevel@tonic-gate }
13787c478bd9Sstevel@tonic-gate 
13797c478bd9Sstevel@tonic-gate 
13807c478bd9Sstevel@tonic-gate static uint_t
uhci_lattice_parent(uint_t node)13817c478bd9Sstevel@tonic-gate uhci_lattice_parent(uint_t node)
13827c478bd9Sstevel@tonic-gate {
13837c478bd9Sstevel@tonic-gate 	return (((node % 2) == 0) ? ((node/2) - 1) : (node/2));
13847c478bd9Sstevel@tonic-gate }
13857c478bd9Sstevel@tonic-gate 
13867c478bd9Sstevel@tonic-gate 
13877c478bd9Sstevel@tonic-gate /*
13887c478bd9Sstevel@tonic-gate  * uhci_leftmost_leaf:
13897c478bd9Sstevel@tonic-gate  *	Find the leftmost leaf in the subtree specified by the node.
13907c478bd9Sstevel@tonic-gate  *	Height refers to number of nodes from the bottom of the tree
13917c478bd9Sstevel@tonic-gate  *	to the node,  including the node.
13927c478bd9Sstevel@tonic-gate  */
13937c478bd9Sstevel@tonic-gate static uint_t
uhci_leftmost_leaf(uint_t node,uint_t height)13947c478bd9Sstevel@tonic-gate uhci_leftmost_leaf(uint_t node, uint_t height)
13957c478bd9Sstevel@tonic-gate {
13967c478bd9Sstevel@tonic-gate 	node = pow_2(height + VIRTUAL_TREE_HEIGHT) * (node+1) -
1397fffe0b30Sqz 	    NUM_FRAME_LST_ENTRIES;
13987c478bd9Sstevel@tonic-gate 	return (node);
13997c478bd9Sstevel@tonic-gate }
14007c478bd9Sstevel@tonic-gate 
14017c478bd9Sstevel@tonic-gate 
14027c478bd9Sstevel@tonic-gate /*
14037c478bd9Sstevel@tonic-gate  * uhci_insert_qh:
14047c478bd9Sstevel@tonic-gate  *	Add the Queue Head (QH) into the Host Controller's (HC)
14057c478bd9Sstevel@tonic-gate  *	appropriate queue head list.
14067c478bd9Sstevel@tonic-gate  */
14077c478bd9Sstevel@tonic-gate void
uhci_insert_qh(uhci_state_t * uhcip,usba_pipe_handle_data_t * ph)14087c478bd9Sstevel@tonic-gate uhci_insert_qh(uhci_state_t *uhcip, usba_pipe_handle_data_t *ph)
14097c478bd9Sstevel@tonic-gate {
14107c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t *pp = (uhci_pipe_private_t *)ph->p_hcd_private;
14117c478bd9Sstevel@tonic-gate 
14127c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
14137c478bd9Sstevel@tonic-gate 	    "uhci_insert_qh:");
14147c478bd9Sstevel@tonic-gate 
14157c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
14167c478bd9Sstevel@tonic-gate 
14177c478bd9Sstevel@tonic-gate 	switch (UHCI_XFER_TYPE(&ph->p_ep)) {
14187c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
14197c478bd9Sstevel@tonic-gate 		uhci_insert_ctrl_qh(uhcip, pp);
14207c478bd9Sstevel@tonic-gate 		break;
14217c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
14227c478bd9Sstevel@tonic-gate 		uhci_insert_bulk_qh(uhcip, pp);
14237c478bd9Sstevel@tonic-gate 		break;
14247c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
14257c478bd9Sstevel@tonic-gate 		uhci_insert_intr_qh(uhcip, pp);
14267c478bd9Sstevel@tonic-gate 		break;
14277c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
14287c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
1429fffe0b30Sqz 			    "uhci_insert_qh: Illegal request");
14307c478bd9Sstevel@tonic-gate 		break;
14317c478bd9Sstevel@tonic-gate 	}
14327c478bd9Sstevel@tonic-gate }
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate 
14357c478bd9Sstevel@tonic-gate /*
14367c478bd9Sstevel@tonic-gate  * uhci_insert_ctrl_qh:
14377c478bd9Sstevel@tonic-gate  *	Insert a control QH into the Host Controller's (HC) control QH list.
14387c478bd9Sstevel@tonic-gate  */
14397c478bd9Sstevel@tonic-gate static void
uhci_insert_ctrl_qh(uhci_state_t * uhcip,uhci_pipe_private_t * pp)14407c478bd9Sstevel@tonic-gate uhci_insert_ctrl_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
14417c478bd9Sstevel@tonic-gate {
14427c478bd9Sstevel@tonic-gate 	queue_head_t *qh = pp->pp_qh;
14437c478bd9Sstevel@tonic-gate 
14447c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
14457c478bd9Sstevel@tonic-gate 	    "uhci_insert_ctrl_qh:");
14467c478bd9Sstevel@tonic-gate 
14477c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_ctrl_xfers_q_head == uhcip->uhci_ctrl_xfers_q_tail) {
14507c478bd9Sstevel@tonic-gate 		uhcip->uhci_ctrl_xfers_q_head->prev_qh	= UHCI_INVALID_PTR;
14517c478bd9Sstevel@tonic-gate 	}
14527c478bd9Sstevel@tonic-gate 
14537c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, qh->link_ptr,
14547c478bd9Sstevel@tonic-gate 	    GetQH32(uhcip, uhcip->uhci_ctrl_xfers_q_tail->link_ptr));
14557c478bd9Sstevel@tonic-gate 	qh->prev_qh = uhcip->uhci_ctrl_xfers_q_tail;
14567c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, uhcip->uhci_ctrl_xfers_q_tail->link_ptr,
1457fffe0b30Sqz 	    QH_PADDR(qh) | HC_QUEUE_HEAD);
14587c478bd9Sstevel@tonic-gate 	uhcip->uhci_ctrl_xfers_q_tail = qh;
14597c478bd9Sstevel@tonic-gate 
14607c478bd9Sstevel@tonic-gate }
14617c478bd9Sstevel@tonic-gate 
14627c478bd9Sstevel@tonic-gate 
14637c478bd9Sstevel@tonic-gate /*
14647c478bd9Sstevel@tonic-gate  * uhci_insert_bulk_qh:
14657c478bd9Sstevel@tonic-gate  *	Insert a bulk QH into the Host Controller's (HC) bulk QH list.
14667c478bd9Sstevel@tonic-gate  */
14677c478bd9Sstevel@tonic-gate static void
uhci_insert_bulk_qh(uhci_state_t * uhcip,uhci_pipe_private_t * pp)14687c478bd9Sstevel@tonic-gate uhci_insert_bulk_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
14697c478bd9Sstevel@tonic-gate {
14707c478bd9Sstevel@tonic-gate 	queue_head_t *qh = pp->pp_qh;
14717c478bd9Sstevel@tonic-gate 
14727c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
14737c478bd9Sstevel@tonic-gate 	    "uhci_insert_bulk_qh:");
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
14767c478bd9Sstevel@tonic-gate 
14777c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_bulk_xfers_q_head == uhcip->uhci_bulk_xfers_q_tail) {
14787c478bd9Sstevel@tonic-gate 		uhcip->uhci_bulk_xfers_q_head->prev_qh = UHCI_INVALID_PTR;
14797c478bd9Sstevel@tonic-gate 	} else if (uhcip->uhci_bulk_xfers_q_head->link_ptr ==
1480fffe0b30Sqz 	    uhcip->uhci_bulk_xfers_q_tail->link_ptr) {
14817c478bd9Sstevel@tonic-gate 
14827c478bd9Sstevel@tonic-gate 		/* If there is already a loop, we should keep the loop. */
14837c478bd9Sstevel@tonic-gate 		qh->link_ptr = uhcip->uhci_bulk_xfers_q_tail->link_ptr;
14847c478bd9Sstevel@tonic-gate 	}
14857c478bd9Sstevel@tonic-gate 
14867c478bd9Sstevel@tonic-gate 	qh->prev_qh = uhcip->uhci_bulk_xfers_q_tail;
14877c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, uhcip->uhci_bulk_xfers_q_tail->link_ptr,
1488fffe0b30Sqz 	    QH_PADDR(qh) | HC_QUEUE_HEAD);
14897c478bd9Sstevel@tonic-gate 	uhcip->uhci_bulk_xfers_q_tail = qh;
14907c478bd9Sstevel@tonic-gate }
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate 
14937c478bd9Sstevel@tonic-gate /*
14947c478bd9Sstevel@tonic-gate  * uhci_insert_intr_qh:
14957c478bd9Sstevel@tonic-gate  *	Insert a periodic Queue head i.e Interrupt queue head into the
14967c478bd9Sstevel@tonic-gate  *	Host Controller's (HC) interrupt lattice tree.
14977c478bd9Sstevel@tonic-gate  */
14987c478bd9Sstevel@tonic-gate static void
uhci_insert_intr_qh(uhci_state_t * uhcip,uhci_pipe_private_t * pp)14997c478bd9Sstevel@tonic-gate uhci_insert_intr_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
15007c478bd9Sstevel@tonic-gate {
15017c478bd9Sstevel@tonic-gate 	uint_t		node = pp->pp_node;	/* The appropriate node was */
15027c478bd9Sstevel@tonic-gate 						/* found during the opening */
15037c478bd9Sstevel@tonic-gate 						/* of the pipe.  */
15047c478bd9Sstevel@tonic-gate 	queue_head_t	*qh = pp->pp_qh;
15057c478bd9Sstevel@tonic-gate 	queue_head_t	*next_lattice_qh, *lattice_qh;
15067c478bd9Sstevel@tonic-gate 
15077c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
15087c478bd9Sstevel@tonic-gate 	    "uhci_insert_intr_qh:");
15097c478bd9Sstevel@tonic-gate 
15107c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
15117c478bd9Sstevel@tonic-gate 
15127c478bd9Sstevel@tonic-gate 	/* Find the lattice queue head */
15137c478bd9Sstevel@tonic-gate 	lattice_qh = &uhcip->uhci_qh_pool_addr[node];
15147c478bd9Sstevel@tonic-gate 	next_lattice_qh =
15157c478bd9Sstevel@tonic-gate 	    QH_VADDR(GetQH32(uhcip, lattice_qh->link_ptr) & QH_LINK_PTR_MASK);
15167c478bd9Sstevel@tonic-gate 
15177c478bd9Sstevel@tonic-gate 	next_lattice_qh->prev_qh = qh;
15187c478bd9Sstevel@tonic-gate 	qh->link_ptr	= lattice_qh->link_ptr;
15197c478bd9Sstevel@tonic-gate 	qh->prev_qh	= lattice_qh;
15207c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, lattice_qh->link_ptr, QH_PADDR(qh) | HC_QUEUE_HEAD);
15217c478bd9Sstevel@tonic-gate 	pp->pp_data_toggle = 0;
15227c478bd9Sstevel@tonic-gate }
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate 
15257c478bd9Sstevel@tonic-gate /*
15267c478bd9Sstevel@tonic-gate  * uhci_insert_intr_td:
15277c478bd9Sstevel@tonic-gate  *	Create a TD and a data buffer for an interrupt endpoint.
15287c478bd9Sstevel@tonic-gate  */
15297c478bd9Sstevel@tonic-gate int
uhci_insert_intr_td(uhci_state_t * uhcip,usba_pipe_handle_data_t * ph,usb_intr_req_t * req,usb_flags_t flags)15307c478bd9Sstevel@tonic-gate uhci_insert_intr_td(
15317c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
15327c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
15337c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*req,
15347c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
15357c478bd9Sstevel@tonic-gate {
15367c478bd9Sstevel@tonic-gate 	int			error, pipe_dir;
15377c478bd9Sstevel@tonic-gate 	uint_t			length, mps;
153802acac7eSsl 	uint32_t		buf_offs;
15397c478bd9Sstevel@tonic-gate 	uhci_td_t		*tmp_td;
15407c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*intr_reqp;
15417c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp = (uhci_pipe_private_t *)ph->p_hcd_private;
15427c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw;
15437c478bd9Sstevel@tonic-gate 
15447c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
1545112116d8Sfb 	    "uhci_insert_intr_td: req: 0x%p", (void *)req);
15467c478bd9Sstevel@tonic-gate 
15477c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
15487c478bd9Sstevel@tonic-gate 
15497c478bd9Sstevel@tonic-gate 	/* Get the interrupt pipe direction */
15507c478bd9Sstevel@tonic-gate 	pipe_dir = UHCI_XFER_DIR(&ph->p_ep);
15517c478bd9Sstevel@tonic-gate 
15527c478bd9Sstevel@tonic-gate 	/* Get the current interrupt request pointer */
15537c478bd9Sstevel@tonic-gate 	if (req) {
15547c478bd9Sstevel@tonic-gate 		length = req->intr_len;
15557c478bd9Sstevel@tonic-gate 	} else {
15567c478bd9Sstevel@tonic-gate 		ASSERT(pipe_dir == USB_EP_DIR_IN);
15576d9a41ffSqz 		length = (pp->pp_client_periodic_in_reqp) ?
15586d9a41ffSqz 		    (((usb_intr_req_t *)pp->
15596d9a41ffSqz 		    pp_client_periodic_in_reqp)->intr_len) :
15606d9a41ffSqz 		    ph->p_ep.wMaxPacketSize;
15617c478bd9Sstevel@tonic-gate 	}
15627c478bd9Sstevel@tonic-gate 
15636d9a41ffSqz 	/* Check the size of interrupt request */
15646d9a41ffSqz 	if (length > UHCI_MAX_TD_XFER_SIZE) {
15657c478bd9Sstevel@tonic-gate 
15666d9a41ffSqz 		/* the length shouldn't exceed 8K */
15677c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
1568112116d8Sfb 		    "uhci_insert_intr_td: Intr request size 0x%x is "
15697c478bd9Sstevel@tonic-gate 		    "more than 0x%x", length, UHCI_MAX_TD_XFER_SIZE);
15707c478bd9Sstevel@tonic-gate 
1571fffe0b30Sqz 		return (USB_INVALID_REQUEST);
15727c478bd9Sstevel@tonic-gate 	}
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
1575112116d8Sfb 	    "uhci_insert_intr_td: length: 0x%x", length);
15767c478bd9Sstevel@tonic-gate 
15777c478bd9Sstevel@tonic-gate 	/* Allocate a transaction wrapper */
15787c478bd9Sstevel@tonic-gate 	if ((tw = uhci_create_transfer_wrapper(uhcip, pp, length, flags)) ==
15797c478bd9Sstevel@tonic-gate 	    NULL) {
15807c478bd9Sstevel@tonic-gate 
15817c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
15827c478bd9Sstevel@tonic-gate 		    "uhci_insert_intr_td: TW allocation failed");
15837c478bd9Sstevel@tonic-gate 
15847c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
15857c478bd9Sstevel@tonic-gate 	}
15867c478bd9Sstevel@tonic-gate 
15877c478bd9Sstevel@tonic-gate 	/*
15887c478bd9Sstevel@tonic-gate 	 * Initialize the callback and any callback
15897c478bd9Sstevel@tonic-gate 	 * data for when the td completes.
15907c478bd9Sstevel@tonic-gate 	 */
15917c478bd9Sstevel@tonic-gate 	tw->tw_handle_td = uhci_handle_intr_td;
15927c478bd9Sstevel@tonic-gate 	tw->tw_handle_callback_value = NULL;
15937c478bd9Sstevel@tonic-gate 	tw->tw_direction = (pipe_dir == USB_EP_DIR_OUT) ?
1594fffe0b30Sqz 	    PID_OUT : PID_IN;
15957c478bd9Sstevel@tonic-gate 	tw->tw_curr_xfer_reqp = (usb_opaque_t)req;
15967c478bd9Sstevel@tonic-gate 
15977c478bd9Sstevel@tonic-gate 	/*
15987c478bd9Sstevel@tonic-gate 	 * If it is an Interrupt IN request and interrupt request is NULL,
15997c478bd9Sstevel@tonic-gate 	 * allocate the usb interrupt request structure for the current
16007c478bd9Sstevel@tonic-gate 	 * interrupt polling request.
16017c478bd9Sstevel@tonic-gate 	 */
16027c478bd9Sstevel@tonic-gate 	if (tw->tw_direction == PID_IN) {
16037c478bd9Sstevel@tonic-gate 		if ((error = uhci_allocate_periodic_in_resource(uhcip,
16047c478bd9Sstevel@tonic-gate 		    pp, tw, flags)) != USB_SUCCESS) {
16057c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
16067c478bd9Sstevel@tonic-gate 			    "uhci_insert_intr_td: Interrupt request structure "
16077c478bd9Sstevel@tonic-gate 			    "allocation failed");
16087c478bd9Sstevel@tonic-gate 
160902acac7eSsl 			/* free the transfer wrapper */
161002acac7eSsl 			uhci_deallocate_tw(uhcip, pp, tw);
161102acac7eSsl 
16127c478bd9Sstevel@tonic-gate 			return (error);
16137c478bd9Sstevel@tonic-gate 		}
16147c478bd9Sstevel@tonic-gate 	}
16157c478bd9Sstevel@tonic-gate 
16167c478bd9Sstevel@tonic-gate 	intr_reqp = (usb_intr_req_t *)tw->tw_curr_xfer_reqp;
16177c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_curr_xfer_reqp != NULL);
16187c478bd9Sstevel@tonic-gate 
16197c478bd9Sstevel@tonic-gate 	tw->tw_timeout_cnt = (intr_reqp->intr_attributes & USB_ATTRS_ONE_XFER) ?
16207c478bd9Sstevel@tonic-gate 	    intr_reqp->intr_timeout : 0;
16217c478bd9Sstevel@tonic-gate 
16227c478bd9Sstevel@tonic-gate 	/* DATA IN */
16237c478bd9Sstevel@tonic-gate 	if (tw->tw_direction == PID_IN) {
16247c478bd9Sstevel@tonic-gate 		/* Insert the td onto the queue head */
162502acac7eSsl 		error = uhci_insert_hc_td(uhcip, 0,
1626fffe0b30Sqz 		    length, pp, tw, PID_IN, intr_reqp->intr_attributes);
16277c478bd9Sstevel@tonic-gate 
16287c478bd9Sstevel@tonic-gate 		if (error != USB_SUCCESS) {
16297c478bd9Sstevel@tonic-gate 
16307c478bd9Sstevel@tonic-gate 			uhci_deallocate_periodic_in_resource(uhcip, pp, tw);
16317c478bd9Sstevel@tonic-gate 			/* free the transfer wrapper */
16327c478bd9Sstevel@tonic-gate 			uhci_deallocate_tw(uhcip, pp, tw);
16337c478bd9Sstevel@tonic-gate 
16347c478bd9Sstevel@tonic-gate 			return (USB_NO_RESOURCES);
16357c478bd9Sstevel@tonic-gate 		}
16367c478bd9Sstevel@tonic-gate 		tw->tw_bytes_xfered = 0;
16377c478bd9Sstevel@tonic-gate 
16387c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
16397c478bd9Sstevel@tonic-gate 	}
16407c478bd9Sstevel@tonic-gate 
1641688b07c5Sgc 	if (req->intr_len) {
1642688b07c5Sgc 		/* DATA OUT */
1643688b07c5Sgc 		ASSERT(req->intr_data != NULL);
16447c478bd9Sstevel@tonic-gate 
1645688b07c5Sgc 		/* Copy the data into the message */
1646688b07c5Sgc 		ddi_rep_put8(tw->tw_accesshandle, req->intr_data->b_rptr,
1647fffe0b30Sqz 		    (uint8_t *)tw->tw_buf, req->intr_len, DDI_DEV_AUTOINCR);
1648688b07c5Sgc 	}
16497c478bd9Sstevel@tonic-gate 
16507c478bd9Sstevel@tonic-gate 	/* set tw->tw_claim flag, so that nobody else works on this tw. */
16517c478bd9Sstevel@tonic-gate 	tw->tw_claim = UHCI_INTR_HDLR_CLAIMED;
16527c478bd9Sstevel@tonic-gate 
16537c478bd9Sstevel@tonic-gate 	mps = ph->p_ep.wMaxPacketSize;
165402acac7eSsl 	buf_offs = 0;
16557c478bd9Sstevel@tonic-gate 
16567c478bd9Sstevel@tonic-gate 	/* Insert tds onto the queue head */
16577c478bd9Sstevel@tonic-gate 	while (length > 0) {
16587c478bd9Sstevel@tonic-gate 
165902acac7eSsl 		error = uhci_insert_hc_td(uhcip, buf_offs,
1660fffe0b30Sqz 		    (length > mps) ? mps : length,
1661fffe0b30Sqz 		    pp, tw, PID_OUT,
1662fffe0b30Sqz 		    intr_reqp->intr_attributes);
16637c478bd9Sstevel@tonic-gate 
16647c478bd9Sstevel@tonic-gate 		if (error != USB_SUCCESS) {
16657c478bd9Sstevel@tonic-gate 			/* no resource. */
16667c478bd9Sstevel@tonic-gate 			break;
16677c478bd9Sstevel@tonic-gate 		}
16687c478bd9Sstevel@tonic-gate 
16697c478bd9Sstevel@tonic-gate 		if (length <= mps) {
16707c478bd9Sstevel@tonic-gate 			/* inserted all data. */
16717c478bd9Sstevel@tonic-gate 			length = 0;
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate 		} else {
16747c478bd9Sstevel@tonic-gate 
167502acac7eSsl 			buf_offs += mps;
16767c478bd9Sstevel@tonic-gate 			length -= mps;
16777c478bd9Sstevel@tonic-gate 		}
16787c478bd9Sstevel@tonic-gate 	}
16797c478bd9Sstevel@tonic-gate 
16807c478bd9Sstevel@tonic-gate 	if (error != USB_SUCCESS) {
16817c478bd9Sstevel@tonic-gate 
16827c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
16837c478bd9Sstevel@tonic-gate 		    "uhci_insert_intr_td: allocate td failed, free resource");
16847c478bd9Sstevel@tonic-gate 
16857c478bd9Sstevel@tonic-gate 		/* remove all the tds */
16867c478bd9Sstevel@tonic-gate 		while (tw->tw_hctd_head != NULL) {
16877c478bd9Sstevel@tonic-gate 			uhci_delete_td(uhcip, tw->tw_hctd_head);
16887c478bd9Sstevel@tonic-gate 		}
16897c478bd9Sstevel@tonic-gate 
16907c478bd9Sstevel@tonic-gate 		tw->tw_claim = UHCI_NOT_CLAIMED;
16917c478bd9Sstevel@tonic-gate 		uhci_deallocate_tw(uhcip, pp, tw);
16927c478bd9Sstevel@tonic-gate 
16937c478bd9Sstevel@tonic-gate 		return (error);
16947c478bd9Sstevel@tonic-gate 	}
16957c478bd9Sstevel@tonic-gate 
16967c478bd9Sstevel@tonic-gate 	/* allow HC to xfer the tds of this tw */
16977c478bd9Sstevel@tonic-gate 	tmp_td = tw->tw_hctd_head;
16987c478bd9Sstevel@tonic-gate 	while (tmp_td != NULL) {
16997c478bd9Sstevel@tonic-gate 
17007c478bd9Sstevel@tonic-gate 		SetTD_status(uhcip, tmp_td, UHCI_TD_ACTIVE);
17017c478bd9Sstevel@tonic-gate 		tmp_td = tmp_td->tw_td_next;
17027c478bd9Sstevel@tonic-gate 	}
17037c478bd9Sstevel@tonic-gate 
17047c478bd9Sstevel@tonic-gate 	tw->tw_bytes_xfered = 0;
17057c478bd9Sstevel@tonic-gate 	tw->tw_claim = UHCI_NOT_CLAIMED;
17067c478bd9Sstevel@tonic-gate 
17077c478bd9Sstevel@tonic-gate 	return (error);
17087c478bd9Sstevel@tonic-gate }
17097c478bd9Sstevel@tonic-gate 
17107c478bd9Sstevel@tonic-gate 
17117c478bd9Sstevel@tonic-gate /*
17127c478bd9Sstevel@tonic-gate  * uhci_create_transfer_wrapper:
171302acac7eSsl  *	Create a Transaction Wrapper (TW) for non-isoc transfer types.
17147c478bd9Sstevel@tonic-gate  *	This involves the allocating of DMA resources.
171502acac7eSsl  *
171602acac7eSsl  *	For non-isoc transfers, one DMA handle and one DMA buffer are
171702acac7eSsl  *	allocated per transfer. The DMA buffer may contain multiple
171802acac7eSsl  *	DMA cookies and the cookies should meet certain alignment
171902acac7eSsl  *	requirement to be able to fit in the multiple TDs. The alignment
172002acac7eSsl  *	needs to ensure:
172102acac7eSsl  *	1. the size of a cookie be larger than max TD length (0x500)
172202acac7eSsl  *	2. the size of a cookie be a multiple of wMaxPacketSize of the
172302acac7eSsl  *	ctrl/bulk pipes
172402acac7eSsl  *
172502acac7eSsl  *	wMaxPacketSize for ctrl and bulk pipes may be 8, 16, 32 or 64 bytes.
172602acac7eSsl  *	So the alignment should be a multiple of 64. wMaxPacketSize for intr
172702acac7eSsl  *	pipes is a little different since it only specifies the max to be
172802acac7eSsl  *	64 bytes, but as long as an intr transfer is limited to max TD length,
172902acac7eSsl  *	any alignment can work if the cookie size is larger than max TD length.
173002acac7eSsl  *
173102acac7eSsl  *	Considering the above conditions, 2K alignment is used. 4K alignment
173202acac7eSsl  *	should also be fine.
17337c478bd9Sstevel@tonic-gate  */
17347c478bd9Sstevel@tonic-gate static uhci_trans_wrapper_t *
uhci_create_transfer_wrapper(uhci_state_t * uhcip,uhci_pipe_private_t * pp,size_t length,usb_flags_t usb_flags)17357c478bd9Sstevel@tonic-gate uhci_create_transfer_wrapper(
17367c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
17377c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp,
17387c478bd9Sstevel@tonic-gate 	size_t			length,
17397c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
17407c478bd9Sstevel@tonic-gate {
17417c478bd9Sstevel@tonic-gate 	size_t			real_length;
17427c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw;
17437c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t	dev_attr;
174402acac7eSsl 	ddi_dma_attr_t		dma_attr;
174502acac7eSsl 	int			kmem_flag;
174602acac7eSsl 	int			(*dmamem_wait)(caddr_t);
174702acac7eSsl 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
17487c478bd9Sstevel@tonic-gate 
17497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
17507c478bd9Sstevel@tonic-gate 	    "uhci_create_transfer_wrapper: length = 0x%lx flags = 0x%x",
17517c478bd9Sstevel@tonic-gate 	    length, usb_flags);
17527c478bd9Sstevel@tonic-gate 
17537c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
17547c478bd9Sstevel@tonic-gate 
175502acac7eSsl 	/* isochronous pipe should not call into this function */
175602acac7eSsl 	if (UHCI_XFER_TYPE(&ph->p_ep) == USB_EP_ATTR_ISOCH) {
175702acac7eSsl 
175802acac7eSsl 		return (NULL);
175902acac7eSsl 	}
176002acac7eSsl 
1761f9190914Szhigang lu - Sun Microsystems - Beijing China 	/* SLEEP flag should not be used while holding mutex */
1762f9190914Szhigang lu - Sun Microsystems - Beijing China 	kmem_flag = KM_NOSLEEP;
1763f9190914Szhigang lu - Sun Microsystems - Beijing China 	dmamem_wait = DDI_DMA_DONTWAIT;
176402acac7eSsl 
17657c478bd9Sstevel@tonic-gate 	/* Allocate space for the transfer wrapper */
176602acac7eSsl 	if ((tw = kmem_zalloc(sizeof (uhci_trans_wrapper_t), kmem_flag)) ==
17677c478bd9Sstevel@tonic-gate 	    NULL) {
17687c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS,  uhcip->uhci_log_hdl,
17697c478bd9Sstevel@tonic-gate 		    "uhci_create_transfer_wrapper: kmem_alloc failed");
17707c478bd9Sstevel@tonic-gate 
17717c478bd9Sstevel@tonic-gate 		return (NULL);
17727c478bd9Sstevel@tonic-gate 	}
17737c478bd9Sstevel@tonic-gate 
1774688b07c5Sgc 	/* zero-length packet doesn't need to allocate dma memory */
1775688b07c5Sgc 	if (length == 0) {
1776688b07c5Sgc 
1777688b07c5Sgc 		goto dmadone;
1778688b07c5Sgc 	}
1779688b07c5Sgc 
178002acac7eSsl 	/* allow sg lists for transfer wrapper dma memory */
178102acac7eSsl 	bcopy(&uhcip->uhci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
178202acac7eSsl 	dma_attr.dma_attr_sgllen = UHCI_DMA_ATTR_SGLLEN;
178302acac7eSsl 	dma_attr.dma_attr_align = UHCI_DMA_ATTR_ALIGN;
178402acac7eSsl 
17857c478bd9Sstevel@tonic-gate 	/* Store the transfer length */
17867c478bd9Sstevel@tonic-gate 	tw->tw_length = length;
17877c478bd9Sstevel@tonic-gate 
17887c478bd9Sstevel@tonic-gate 	/* Allocate the DMA handle */
178902acac7eSsl 	if (ddi_dma_alloc_handle(uhcip->uhci_dip, &dma_attr, dmamem_wait,
179002acac7eSsl 	    0, &tw->tw_dmahandle) != DDI_SUCCESS) {
17917c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
17927c478bd9Sstevel@tonic-gate 		    "uhci_create_transfer_wrapper: Alloc handle failed");
17937c478bd9Sstevel@tonic-gate 		kmem_free(tw, sizeof (uhci_trans_wrapper_t));
17947c478bd9Sstevel@tonic-gate 
17957c478bd9Sstevel@tonic-gate 		return (NULL);
17967c478bd9Sstevel@tonic-gate 	}
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_version		= DDI_DEVICE_ATTR_V0;
17997c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_endian_flags	= DDI_STRUCTURE_LE_ACC;
18007c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_dataorder		= DDI_STRICTORDER_ACC;
18017c478bd9Sstevel@tonic-gate 
18027c478bd9Sstevel@tonic-gate 	/* Allocate the memory */
180302acac7eSsl 	if (ddi_dma_mem_alloc(tw->tw_dmahandle, tw->tw_length, &dev_attr,
180402acac7eSsl 	    DDI_DMA_CONSISTENT, dmamem_wait, NULL, (caddr_t *)&tw->tw_buf,
180502acac7eSsl 	    &real_length, &tw->tw_accesshandle) != DDI_SUCCESS) {
18067c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
18077c478bd9Sstevel@tonic-gate 		    "uhci_create_transfer_wrapper: dma_mem_alloc fail");
18087c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&tw->tw_dmahandle);
18097c478bd9Sstevel@tonic-gate 		kmem_free(tw, sizeof (uhci_trans_wrapper_t));
18107c478bd9Sstevel@tonic-gate 
18117c478bd9Sstevel@tonic-gate 		return (NULL);
18127c478bd9Sstevel@tonic-gate 	}
18137c478bd9Sstevel@tonic-gate 
18147c478bd9Sstevel@tonic-gate 	ASSERT(real_length >= length);
18157c478bd9Sstevel@tonic-gate 
18167c478bd9Sstevel@tonic-gate 	/* Bind the handle */
181702acac7eSsl 	if (ddi_dma_addr_bind_handle(tw->tw_dmahandle, NULL,
18187c478bd9Sstevel@tonic-gate 	    (caddr_t)tw->tw_buf, real_length, DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
181902acac7eSsl 	    dmamem_wait, NULL, &tw->tw_cookie, &tw->tw_ncookies) !=
18207c478bd9Sstevel@tonic-gate 	    DDI_DMA_MAPPED) {
18217c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
18227c478bd9Sstevel@tonic-gate 		    "uhci_create_transfer_wrapper: Bind handle failed");
18237c478bd9Sstevel@tonic-gate 		ddi_dma_mem_free(&tw->tw_accesshandle);
18247c478bd9Sstevel@tonic-gate 		ddi_dma_free_handle(&tw->tw_dmahandle);
18257c478bd9Sstevel@tonic-gate 		kmem_free(tw, sizeof (uhci_trans_wrapper_t));
18267c478bd9Sstevel@tonic-gate 
18277c478bd9Sstevel@tonic-gate 		return (NULL);
18287c478bd9Sstevel@tonic-gate 	}
18297c478bd9Sstevel@tonic-gate 
183002acac7eSsl 	tw->tw_cookie_idx = 0;
183102acac7eSsl 	tw->tw_dma_offs = 0;
18327c478bd9Sstevel@tonic-gate 
1833688b07c5Sgc dmadone:
18347c478bd9Sstevel@tonic-gate 	/*
18357c478bd9Sstevel@tonic-gate 	 * Only allow one wrapper to be added at a time. Insert the
18367c478bd9Sstevel@tonic-gate 	 * new transaction wrapper into the list for this pipe.
18377c478bd9Sstevel@tonic-gate 	 */
18387c478bd9Sstevel@tonic-gate 	if (pp->pp_tw_head == NULL) {
18397c478bd9Sstevel@tonic-gate 		pp->pp_tw_head = tw;
18407c478bd9Sstevel@tonic-gate 		pp->pp_tw_tail = tw;
18417c478bd9Sstevel@tonic-gate 	} else {
18427c478bd9Sstevel@tonic-gate 		pp->pp_tw_tail->tw_next = tw;
18437c478bd9Sstevel@tonic-gate 		pp->pp_tw_tail = tw;
18447c478bd9Sstevel@tonic-gate 		ASSERT(tw->tw_next == NULL);
18457c478bd9Sstevel@tonic-gate 	}
18467c478bd9Sstevel@tonic-gate 
18477c478bd9Sstevel@tonic-gate 	/* Store a back pointer to the pipe private structure */
18487c478bd9Sstevel@tonic-gate 	tw->tw_pipe_private = pp;
18497c478bd9Sstevel@tonic-gate 
18507c478bd9Sstevel@tonic-gate 	/* Store the transfer type - synchronous or asynchronous */
18517c478bd9Sstevel@tonic-gate 	tw->tw_flags = usb_flags;
18527c478bd9Sstevel@tonic-gate 
185302acac7eSsl 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
185402acac7eSsl 	    "uhci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
1855112116d8Sfb 	    (void *)tw, tw->tw_ncookies);
185602acac7eSsl 
18577c478bd9Sstevel@tonic-gate 	return (tw);
18587c478bd9Sstevel@tonic-gate }
18597c478bd9Sstevel@tonic-gate 
18607c478bd9Sstevel@tonic-gate 
18617c478bd9Sstevel@tonic-gate /*
18627c478bd9Sstevel@tonic-gate  * uhci_insert_hc_td:
18637c478bd9Sstevel@tonic-gate  *	Insert a Transfer Descriptor (TD) on an QH.
18647c478bd9Sstevel@tonic-gate  */
18657c478bd9Sstevel@tonic-gate int
uhci_insert_hc_td(uhci_state_t * uhcip,uint32_t buffer_offset,size_t hcgtd_length,uhci_pipe_private_t * pp,uhci_trans_wrapper_t * tw,uchar_t PID,usb_req_attrs_t attrs)18667c478bd9Sstevel@tonic-gate uhci_insert_hc_td(
18677c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
186802acac7eSsl 	uint32_t		buffer_offset,
18697c478bd9Sstevel@tonic-gate 	size_t			hcgtd_length,
18707c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp,
18717c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw,
18727c478bd9Sstevel@tonic-gate 	uchar_t			PID,
18737c478bd9Sstevel@tonic-gate 	usb_req_attrs_t		attrs)
18747c478bd9Sstevel@tonic-gate {
18757c478bd9Sstevel@tonic-gate 	uhci_td_t	*td, *current_dummy;
18767c478bd9Sstevel@tonic-gate 	queue_head_t	*qh = pp->pp_qh;
18777c478bd9Sstevel@tonic-gate 
18787c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
18797c478bd9Sstevel@tonic-gate 
18807c478bd9Sstevel@tonic-gate 	if ((td = uhci_allocate_td_from_pool(uhcip)) == NULL) {
18817c478bd9Sstevel@tonic-gate 
18827c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
18837c478bd9Sstevel@tonic-gate 	}
18847c478bd9Sstevel@tonic-gate 
18857c478bd9Sstevel@tonic-gate 	current_dummy = qh->td_tailp;
18867c478bd9Sstevel@tonic-gate 
18877c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
1888112116d8Sfb 	    "uhci_insert_hc_td: td %p, attrs = 0x%x", (void *)td, attrs);
18897c478bd9Sstevel@tonic-gate 
18907c478bd9Sstevel@tonic-gate 	/*
18917c478bd9Sstevel@tonic-gate 	 * Fill in the current dummy td and
18927c478bd9Sstevel@tonic-gate 	 * add the new dummy to the end.
18937c478bd9Sstevel@tonic-gate 	 */
189402acac7eSsl 	uhci_fill_in_td(uhcip, td, current_dummy, buffer_offset,
189502acac7eSsl 	    hcgtd_length, pp, PID, attrs, tw);
18967c478bd9Sstevel@tonic-gate 
18977c478bd9Sstevel@tonic-gate 	/*
18987c478bd9Sstevel@tonic-gate 	 * Allow HC hardware xfer the td, except interrupt out td.
18997c478bd9Sstevel@tonic-gate 	 */
19007c478bd9Sstevel@tonic-gate 	if ((tw->tw_handle_td != uhci_handle_intr_td) || (PID != PID_OUT)) {
19017c478bd9Sstevel@tonic-gate 
19027c478bd9Sstevel@tonic-gate 		SetTD_status(uhcip, current_dummy, UHCI_TD_ACTIVE);
19037c478bd9Sstevel@tonic-gate 	}
19047c478bd9Sstevel@tonic-gate 
19057c478bd9Sstevel@tonic-gate 	/* Insert this td onto the tw */
19067c478bd9Sstevel@tonic-gate 
19077c478bd9Sstevel@tonic-gate 	if (tw->tw_hctd_head == NULL) {
19087c478bd9Sstevel@tonic-gate 		ASSERT(tw->tw_hctd_tail == NULL);
19097c478bd9Sstevel@tonic-gate 		tw->tw_hctd_head = current_dummy;
19107c478bd9Sstevel@tonic-gate 		tw->tw_hctd_tail = current_dummy;
19117c478bd9Sstevel@tonic-gate 	} else {
19127c478bd9Sstevel@tonic-gate 		/* Add the td to the end of the list */
19137c478bd9Sstevel@tonic-gate 		tw->tw_hctd_tail->tw_td_next = current_dummy;
19147c478bd9Sstevel@tonic-gate 		tw->tw_hctd_tail = current_dummy;
19157c478bd9Sstevel@tonic-gate 	}
19167c478bd9Sstevel@tonic-gate 
19177c478bd9Sstevel@tonic-gate 	/*
19187c478bd9Sstevel@tonic-gate 	 * Insert the TD on to the QH. When this occurs,
19197c478bd9Sstevel@tonic-gate 	 * the Host Controller will see the newly filled in TD
19207c478bd9Sstevel@tonic-gate 	 */
19217c478bd9Sstevel@tonic-gate 	current_dummy->outst_td_next	 = NULL;
19227c478bd9Sstevel@tonic-gate 	current_dummy->outst_td_prev	 = uhcip->uhci_outst_tds_tail;
19237c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_outst_tds_head == NULL) {
19247c478bd9Sstevel@tonic-gate 		uhcip->uhci_outst_tds_head = current_dummy;
19257c478bd9Sstevel@tonic-gate 	} else {
19267c478bd9Sstevel@tonic-gate 		uhcip->uhci_outst_tds_tail->outst_td_next = current_dummy;
19277c478bd9Sstevel@tonic-gate 	}
19287c478bd9Sstevel@tonic-gate 	uhcip->uhci_outst_tds_tail = current_dummy;
19297c478bd9Sstevel@tonic-gate 	current_dummy->tw = tw;
19307c478bd9Sstevel@tonic-gate 
19317c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
19327c478bd9Sstevel@tonic-gate }
19337c478bd9Sstevel@tonic-gate 
19347c478bd9Sstevel@tonic-gate 
19357c478bd9Sstevel@tonic-gate /*
19367c478bd9Sstevel@tonic-gate  * uhci_fill_in_td:
19377c478bd9Sstevel@tonic-gate  *	Fill in the fields of a Transfer Descriptor (TD).
19387c478bd9Sstevel@tonic-gate  */
19397c478bd9Sstevel@tonic-gate static void
uhci_fill_in_td(uhci_state_t * uhcip,uhci_td_t * td,uhci_td_t * current_dummy,uint32_t buffer_offset,size_t length,uhci_pipe_private_t * pp,uchar_t PID,usb_req_attrs_t attrs,uhci_trans_wrapper_t * tw)19407c478bd9Sstevel@tonic-gate uhci_fill_in_td(
19417c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
19427c478bd9Sstevel@tonic-gate 	uhci_td_t		*td,
19437c478bd9Sstevel@tonic-gate 	uhci_td_t		*current_dummy,
194402acac7eSsl 	uint32_t		buffer_offset,
19457c478bd9Sstevel@tonic-gate 	size_t			length,
19467c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp,
19477c478bd9Sstevel@tonic-gate 	uchar_t			PID,
194802acac7eSsl 	usb_req_attrs_t		attrs,
194902acac7eSsl 	uhci_trans_wrapper_t	*tw)
19507c478bd9Sstevel@tonic-gate {
19517c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
195202acac7eSsl 	uint32_t		buf_addr;
19537c478bd9Sstevel@tonic-gate 
19547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
195502acac7eSsl 	    "uhci_fill_in_td: td 0x%p buf_offs 0x%x len 0x%lx "
1956112116d8Sfb 	    "attrs 0x%x", (void *)td, buffer_offset, length, attrs);
19577c478bd9Sstevel@tonic-gate 
19587c478bd9Sstevel@tonic-gate 	/*
19597c478bd9Sstevel@tonic-gate 	 * If this is an isochronous TD, just return
19607c478bd9Sstevel@tonic-gate 	 */
19617c478bd9Sstevel@tonic-gate 	if (UHCI_XFER_TYPE(&ph->p_ep) == USB_EP_ATTR_ISOCH) {
19627c478bd9Sstevel@tonic-gate 
19637c478bd9Sstevel@tonic-gate 		return;
19647c478bd9Sstevel@tonic-gate 	}
19657c478bd9Sstevel@tonic-gate 
196602acac7eSsl 	/* The maximum transfer length of UHCI cannot exceed 0x500 bytes */
196702acac7eSsl 	ASSERT(length <= UHCI_MAX_TD_XFER_SIZE);
196802acac7eSsl 
19697c478bd9Sstevel@tonic-gate 	bzero((char *)td, sizeof (uhci_td_t));	/* Clear the TD */
19707c478bd9Sstevel@tonic-gate 	SetTD32(uhcip, current_dummy->link_ptr, TD_PADDR(td));
19717c478bd9Sstevel@tonic-gate 
19727c478bd9Sstevel@tonic-gate 	if (attrs & USB_ATTRS_SHORT_XFER_OK) {
19737c478bd9Sstevel@tonic-gate 		SetTD_spd(uhcip, current_dummy, 1);
19747c478bd9Sstevel@tonic-gate 	}
19757c478bd9Sstevel@tonic-gate 
19767c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_usba_device->usb_mutex);
19777c478bd9Sstevel@tonic-gate 	if (ph->p_usba_device->usb_port_status == USBA_LOW_SPEED_DEV) {
19787c478bd9Sstevel@tonic-gate 		SetTD_ls(uhcip, current_dummy, LOW_SPEED_DEVICE);
19797c478bd9Sstevel@tonic-gate 	}
19807c478bd9Sstevel@tonic-gate 
19817c478bd9Sstevel@tonic-gate 	SetTD_c_err(uhcip, current_dummy, UHCI_MAX_ERR_COUNT);
1982688b07c5Sgc 	SetTD_mlen(uhcip, current_dummy,
1983fffe0b30Sqz 	    (length == 0) ? ZERO_LENGTH : (length - 1));
19847c478bd9Sstevel@tonic-gate 	SetTD_dtogg(uhcip, current_dummy, pp->pp_data_toggle);
19857c478bd9Sstevel@tonic-gate 
19867c478bd9Sstevel@tonic-gate 	/* Adjust the data toggle bit */
19877c478bd9Sstevel@tonic-gate 	ADJ_DATA_TOGGLE(pp);
19887c478bd9Sstevel@tonic-gate 
19897c478bd9Sstevel@tonic-gate 	SetTD_devaddr(uhcip, current_dummy,  ph->p_usba_device->usb_addr);
19907c478bd9Sstevel@tonic-gate 	SetTD_endpt(uhcip, current_dummy,
1991fffe0b30Sqz 	    ph->p_ep.bEndpointAddress & END_POINT_ADDRESS_MASK);
19927c478bd9Sstevel@tonic-gate 	SetTD_PID(uhcip, current_dummy, PID);
19937c478bd9Sstevel@tonic-gate 	SetTD_ioc(uhcip, current_dummy, INTERRUPT_ON_COMPLETION);
19947c478bd9Sstevel@tonic-gate 
199502acac7eSsl 	buf_addr = uhci_get_tw_paddr_by_offs(uhcip, buffer_offset, length, tw);
199602acac7eSsl 	SetTD32(uhcip, current_dummy->buffer_address, buf_addr);
199702acac7eSsl 
19987c478bd9Sstevel@tonic-gate 	td->qh_td_prev			= current_dummy;
19997c478bd9Sstevel@tonic-gate 	current_dummy->qh_td_prev	= NULL;
20007c478bd9Sstevel@tonic-gate 	pp->pp_qh->td_tailp		= td;
20017c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_usba_device->usb_mutex);
20027c478bd9Sstevel@tonic-gate }
20037c478bd9Sstevel@tonic-gate 
200402acac7eSsl /*
200502acac7eSsl  * uhci_get_tw_paddr_by_offs:
200602acac7eSsl  *	Walk through the DMA cookies of a TW buffer to retrieve
200702acac7eSsl  *	the device address used for a TD.
200802acac7eSsl  *
200902acac7eSsl  * buffer_offset - the starting offset into the TW buffer, where the
2010fffe0b30Sqz  *		   TD should transfer from. When a TW has more than
2011fffe0b30Sqz  *		   one TD, the TDs must be filled in increasing order.
201202acac7eSsl  */
201302acac7eSsl static uint32_t
uhci_get_tw_paddr_by_offs(uhci_state_t * uhcip,uint32_t buffer_offset,size_t length,uhci_trans_wrapper_t * tw)201402acac7eSsl uhci_get_tw_paddr_by_offs(
201502acac7eSsl 	uhci_state_t		*uhcip,
201602acac7eSsl 	uint32_t		buffer_offset,
201702acac7eSsl 	size_t			length,
201802acac7eSsl 	uhci_trans_wrapper_t	*tw)
201902acac7eSsl {
202002acac7eSsl 	uint32_t		buf_addr;
202102acac7eSsl 	int			rem_len;
202202acac7eSsl 
202302acac7eSsl 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
202402acac7eSsl 	    "uhci_get_tw_paddr_by_offs: buf_offs 0x%x len 0x%lx",
202502acac7eSsl 	    buffer_offset, length);
202602acac7eSsl 
202702acac7eSsl 	/*
202802acac7eSsl 	 * TDs must be filled in increasing DMA offset order.
202902acac7eSsl 	 * tw_dma_offs is initialized to be 0 at TW creation and
203002acac7eSsl 	 * is only increased in this function.
203102acac7eSsl 	 */
203202acac7eSsl 	ASSERT(length == 0 || buffer_offset >= tw->tw_dma_offs);
203302acac7eSsl 
203402acac7eSsl 	if (length == 0) {
203502acac7eSsl 		buf_addr = 0;
203602acac7eSsl 
203702acac7eSsl 		return (buf_addr);
203802acac7eSsl 	}
203902acac7eSsl 
204002acac7eSsl 	/*
204102acac7eSsl 	 * Advance to the next DMA cookie until finding the cookie
204202acac7eSsl 	 * that buffer_offset falls in.
204302acac7eSsl 	 * It is very likely this loop will never repeat more than
204402acac7eSsl 	 * once. It is here just to accommodate the case buffer_offset
204502acac7eSsl 	 * is increased by multiple cookies during two consecutive
204602acac7eSsl 	 * calls into this function. In that case, the interim DMA
204702acac7eSsl 	 * buffer is allowed to be skipped.
204802acac7eSsl 	 */
204902acac7eSsl 	while ((tw->tw_dma_offs + tw->tw_cookie.dmac_size) <=
205002acac7eSsl 	    buffer_offset) {
205102acac7eSsl 		/*
205202acac7eSsl 		 * tw_dma_offs always points to the starting offset
205302acac7eSsl 		 * of a cookie
205402acac7eSsl 		 */
205502acac7eSsl 		tw->tw_dma_offs += tw->tw_cookie.dmac_size;
205602acac7eSsl 		ddi_dma_nextcookie(tw->tw_dmahandle, &tw->tw_cookie);
205702acac7eSsl 		tw->tw_cookie_idx++;
205802acac7eSsl 		ASSERT(tw->tw_cookie_idx < tw->tw_ncookies);
205902acac7eSsl 	}
206002acac7eSsl 
206102acac7eSsl 	/*
206202acac7eSsl 	 * Counting the remained buffer length to be filled in
206302acac7eSsl 	 * the TDs for current DMA cookie
206402acac7eSsl 	 */
206502acac7eSsl 	rem_len = (tw->tw_dma_offs + tw->tw_cookie.dmac_size) -
206602acac7eSsl 	    buffer_offset;
206702acac7eSsl 
206802acac7eSsl 	/* Calculate the beginning address of the buffer */
206902acac7eSsl 	ASSERT(length <= rem_len);
207002acac7eSsl 	buf_addr = (buffer_offset - tw->tw_dma_offs) +
207102acac7eSsl 	    tw->tw_cookie.dmac_address;
207202acac7eSsl 
207302acac7eSsl 	USB_DPRINTF_L3(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
2074112116d8Sfb 	    "uhci_get_tw_paddr_by_offs: dmac_addr 0x%x dmac_size "
207502acac7eSsl 	    "0x%lx idx %d", buf_addr, tw->tw_cookie.dmac_size,
207602acac7eSsl 	    tw->tw_cookie_idx);
207702acac7eSsl 
207802acac7eSsl 	return (buf_addr);
207902acac7eSsl }
208002acac7eSsl 
20817c478bd9Sstevel@tonic-gate 
20827c478bd9Sstevel@tonic-gate /*
20837c478bd9Sstevel@tonic-gate  * uhci_modify_td_active_bits:
20847c478bd9Sstevel@tonic-gate  *	Sets active bit in all the tds of QH to INACTIVE so that
20857c478bd9Sstevel@tonic-gate  *	the HC stops processing the TD's related to the QH.
20867c478bd9Sstevel@tonic-gate  */
20877c478bd9Sstevel@tonic-gate void
uhci_modify_td_active_bits(uhci_state_t * uhcip,uhci_pipe_private_t * pp)20887c478bd9Sstevel@tonic-gate uhci_modify_td_active_bits(
20897c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
20907c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp)
20917c478bd9Sstevel@tonic-gate {
20927c478bd9Sstevel@tonic-gate 	uhci_td_t		*td_head;
20937c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*ept = &pp->pp_pipe_handle->p_ep;
20947c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw_head = pp->pp_tw_head;
20957c478bd9Sstevel@tonic-gate 
20967c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
20977c478bd9Sstevel@tonic-gate 	    "uhci_modify_td_active_bits: tw head %p", (void *)tw_head);
20987c478bd9Sstevel@tonic-gate 
20997c478bd9Sstevel@tonic-gate 	while (tw_head != NULL) {
21007c478bd9Sstevel@tonic-gate 		tw_head->tw_claim = UHCI_MODIFY_TD_BITS_CLAIMED;
21017c478bd9Sstevel@tonic-gate 		td_head = tw_head->tw_hctd_head;
21027c478bd9Sstevel@tonic-gate 
21037c478bd9Sstevel@tonic-gate 		while (td_head) {
21047c478bd9Sstevel@tonic-gate 			if (UHCI_XFER_TYPE(ept) == USB_EP_ATTR_ISOCH) {
21057c478bd9Sstevel@tonic-gate 				SetTD_status(uhcip, td_head,
21067c478bd9Sstevel@tonic-gate 				    GetTD_status(uhcip, td_head) & TD_INACTIVE);
21077c478bd9Sstevel@tonic-gate 			} else {
21087c478bd9Sstevel@tonic-gate 				SetTD32(uhcip, td_head->link_ptr,
21097c478bd9Sstevel@tonic-gate 				    GetTD32(uhcip, td_head->link_ptr) |
21107c478bd9Sstevel@tonic-gate 				    HC_END_OF_LIST);
21117c478bd9Sstevel@tonic-gate 			}
21127c478bd9Sstevel@tonic-gate 
21137c478bd9Sstevel@tonic-gate 			td_head = td_head->tw_td_next;
21147c478bd9Sstevel@tonic-gate 		}
21157c478bd9Sstevel@tonic-gate 		tw_head = tw_head->tw_next;
21167c478bd9Sstevel@tonic-gate 	}
21177c478bd9Sstevel@tonic-gate }
21187c478bd9Sstevel@tonic-gate 
21197c478bd9Sstevel@tonic-gate 
21207c478bd9Sstevel@tonic-gate /*
21217c478bd9Sstevel@tonic-gate  * uhci_insert_ctrl_td:
21227c478bd9Sstevel@tonic-gate  *	Create a TD and a data buffer for a control Queue Head.
21237c478bd9Sstevel@tonic-gate  */
21247c478bd9Sstevel@tonic-gate int
uhci_insert_ctrl_td(uhci_state_t * uhcip,usba_pipe_handle_data_t * ph,usb_ctrl_req_t * ctrl_reqp,usb_flags_t flags)21257c478bd9Sstevel@tonic-gate uhci_insert_ctrl_td(
21267c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
21277c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t  *ph,
21287c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp,
21297c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
21307c478bd9Sstevel@tonic-gate {
21317c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t  *pp = (uhci_pipe_private_t *)ph->p_hcd_private;
21327c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t *tw;
213302acac7eSsl 	size_t	ctrl_buf_size;
21347c478bd9Sstevel@tonic-gate 
21357c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
21367c478bd9Sstevel@tonic-gate 	    "uhci_insert_ctrl_td: timeout: 0x%x", ctrl_reqp->ctrl_timeout);
21377c478bd9Sstevel@tonic-gate 
21387c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
21397c478bd9Sstevel@tonic-gate 
214002acac7eSsl 	/*
214102acac7eSsl 	 * If we have a control data phase, make the data buffer start
214202acac7eSsl 	 * on the next 64-byte boundary so as to ensure the DMA cookie
214302acac7eSsl 	 * can fit in the multiple TDs. The buffer in the range of
214402acac7eSsl 	 * [SETUP_SIZE, UHCI_CTRL_EPT_MAX_SIZE) is just for padding
214502acac7eSsl 	 * and not to be transferred.
214602acac7eSsl 	 */
214702acac7eSsl 	if (ctrl_reqp->ctrl_wLength) {
214802acac7eSsl 		ctrl_buf_size = UHCI_CTRL_EPT_MAX_SIZE +
214902acac7eSsl 		    ctrl_reqp->ctrl_wLength;
215002acac7eSsl 	} else {
215102acac7eSsl 		ctrl_buf_size = SETUP_SIZE;
215202acac7eSsl 	}
215302acac7eSsl 
21547c478bd9Sstevel@tonic-gate 	/* Allocate a transaction wrapper */
21557c478bd9Sstevel@tonic-gate 	if ((tw = uhci_create_transfer_wrapper(uhcip, pp,
215602acac7eSsl 	    ctrl_buf_size, flags)) == NULL) {
21577c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
21587c478bd9Sstevel@tonic-gate 		    "uhci_insert_ctrl_td: TW allocation failed");
21597c478bd9Sstevel@tonic-gate 
21607c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
21617c478bd9Sstevel@tonic-gate 	}
21627c478bd9Sstevel@tonic-gate 
21637c478bd9Sstevel@tonic-gate 	pp->pp_data_toggle = 0;
21647c478bd9Sstevel@tonic-gate 
21657c478bd9Sstevel@tonic-gate 	tw->tw_curr_xfer_reqp = (usb_opaque_t)ctrl_reqp;
21667c478bd9Sstevel@tonic-gate 	tw->tw_bytes_xfered = 0;
21677c478bd9Sstevel@tonic-gate 	tw->tw_bytes_pending = ctrl_reqp->ctrl_wLength;
21687c478bd9Sstevel@tonic-gate 	tw->tw_timeout_cnt = max(UHCI_CTRL_TIMEOUT, ctrl_reqp->ctrl_timeout);
21697c478bd9Sstevel@tonic-gate 
21707c478bd9Sstevel@tonic-gate 	/*
21717c478bd9Sstevel@tonic-gate 	 * Initialize the callback and any callback
21727c478bd9Sstevel@tonic-gate 	 * data for when the td completes.
21737c478bd9Sstevel@tonic-gate 	 */
21747c478bd9Sstevel@tonic-gate 	tw->tw_handle_td = uhci_handle_ctrl_td;
21757c478bd9Sstevel@tonic-gate 	tw->tw_handle_callback_value = NULL;
21767c478bd9Sstevel@tonic-gate 
21777c478bd9Sstevel@tonic-gate 	if ((uhci_create_setup_pkt(uhcip, pp, tw)) != USB_SUCCESS) {
21787c478bd9Sstevel@tonic-gate 		tw->tw_ctrl_state = 0;
21797c478bd9Sstevel@tonic-gate 
21807c478bd9Sstevel@tonic-gate 		/* free the transfer wrapper */
21817c478bd9Sstevel@tonic-gate 		uhci_deallocate_tw(uhcip, pp, tw);
21827c478bd9Sstevel@tonic-gate 
21837c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
21847c478bd9Sstevel@tonic-gate 	}
21857c478bd9Sstevel@tonic-gate 
21867c478bd9Sstevel@tonic-gate 	tw->tw_ctrl_state = SETUP;
21877c478bd9Sstevel@tonic-gate 
21887c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
21897c478bd9Sstevel@tonic-gate }
21907c478bd9Sstevel@tonic-gate 
21917c478bd9Sstevel@tonic-gate 
21927c478bd9Sstevel@tonic-gate /*
21937c478bd9Sstevel@tonic-gate  * uhci_create_setup_pkt:
21947c478bd9Sstevel@tonic-gate  *	create a setup packet to initiate a control transfer.
21957c478bd9Sstevel@tonic-gate  *
21967c478bd9Sstevel@tonic-gate  *	OHCI driver has seen the case where devices fail if there is
21977c478bd9Sstevel@tonic-gate  *	more than one control transfer to the device within a frame.
21987c478bd9Sstevel@tonic-gate  *	So, the UHCI ensures that only one TD will be put on the control
21997c478bd9Sstevel@tonic-gate  *	pipe to one device (to be consistent with OHCI driver).
22007c478bd9Sstevel@tonic-gate  */
22017c478bd9Sstevel@tonic-gate static int
uhci_create_setup_pkt(uhci_state_t * uhcip,uhci_pipe_private_t * pp,uhci_trans_wrapper_t * tw)22027c478bd9Sstevel@tonic-gate uhci_create_setup_pkt(
22037c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
22047c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp,
22057c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw)
22067c478bd9Sstevel@tonic-gate {
22077c478bd9Sstevel@tonic-gate 	int		sdata;
22087c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t	*req = (usb_ctrl_req_t *)tw->tw_curr_xfer_reqp;
22097c478bd9Sstevel@tonic-gate 
22107c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
22117c478bd9Sstevel@tonic-gate 	    "uhci_create_setup_pkt: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%p",
22127c478bd9Sstevel@tonic-gate 	    req->ctrl_bmRequestType, req->ctrl_bRequest, req->ctrl_wValue,
22137c478bd9Sstevel@tonic-gate 	    req->ctrl_wIndex, req->ctrl_wLength, (void *)req->ctrl_data);
22147c478bd9Sstevel@tonic-gate 
22157c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
22167c478bd9Sstevel@tonic-gate 	ASSERT(tw != NULL);
22177c478bd9Sstevel@tonic-gate 
22187c478bd9Sstevel@tonic-gate 	/* Create the first four bytes of the setup packet */
22197c478bd9Sstevel@tonic-gate 	sdata = (req->ctrl_bmRequestType | (req->ctrl_bRequest << 8) |
2220fffe0b30Sqz 	    (req->ctrl_wValue << 16));
22217c478bd9Sstevel@tonic-gate 	ddi_put32(tw->tw_accesshandle, (uint_t *)tw->tw_buf, sdata);
22227c478bd9Sstevel@tonic-gate 
22237c478bd9Sstevel@tonic-gate 	/* Create the second four bytes */
22247c478bd9Sstevel@tonic-gate 	sdata = (uint32_t)(req->ctrl_wIndex | (req->ctrl_wLength << 16));
22257c478bd9Sstevel@tonic-gate 	ddi_put32(tw->tw_accesshandle,
22267c478bd9Sstevel@tonic-gate 	    (uint_t *)(tw->tw_buf + sizeof (uint_t)), sdata);
22277c478bd9Sstevel@tonic-gate 
22287c478bd9Sstevel@tonic-gate 	/*
22297c478bd9Sstevel@tonic-gate 	 * The TD's are placed on the QH one at a time.
22307c478bd9Sstevel@tonic-gate 	 * Once this TD is placed on the done list, the
22317c478bd9Sstevel@tonic-gate 	 * data or status phase TD will be enqueued.
22327c478bd9Sstevel@tonic-gate 	 */
223302acac7eSsl 	if ((uhci_insert_hc_td(uhcip, 0, SETUP_SIZE,
22347c478bd9Sstevel@tonic-gate 	    pp, tw, PID_SETUP, req->ctrl_attributes)) != USB_SUCCESS) {
22357c478bd9Sstevel@tonic-gate 
22367c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
22377c478bd9Sstevel@tonic-gate 	}
22387c478bd9Sstevel@tonic-gate 
22397c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
2240112116d8Sfb 	    "Create_setup: pp = 0x%p, attrs = 0x%x", (void *)pp,
2241112116d8Sfb 	    req->ctrl_attributes);
22427c478bd9Sstevel@tonic-gate 
22437c478bd9Sstevel@tonic-gate 	/*
22447c478bd9Sstevel@tonic-gate 	 * If this control transfer has a data phase, record the
22457c478bd9Sstevel@tonic-gate 	 * direction. If the data phase is an OUT transaction ,
22467c478bd9Sstevel@tonic-gate 	 * copy the data into the buffer of the transfer wrapper.
22477c478bd9Sstevel@tonic-gate 	 */
22487c478bd9Sstevel@tonic-gate 	if (req->ctrl_wLength != 0) {
22497c478bd9Sstevel@tonic-gate 		/* There is a data stage.  Find the direction */
22507c478bd9Sstevel@tonic-gate 		if (req->ctrl_bmRequestType & USB_DEV_REQ_DEV_TO_HOST) {
22517c478bd9Sstevel@tonic-gate 			tw->tw_direction = PID_IN;
22527c478bd9Sstevel@tonic-gate 		} else {
22537c478bd9Sstevel@tonic-gate 			tw->tw_direction = PID_OUT;
22547c478bd9Sstevel@tonic-gate 
22557c478bd9Sstevel@tonic-gate 			/* Copy the data into the buffer */
22567c478bd9Sstevel@tonic-gate 			ddi_rep_put8(tw->tw_accesshandle,
22577c478bd9Sstevel@tonic-gate 			    req->ctrl_data->b_rptr,
225802acac7eSsl 			    (uint8_t *)(tw->tw_buf + UHCI_CTRL_EPT_MAX_SIZE),
22597c478bd9Sstevel@tonic-gate 			    req->ctrl_wLength,
22607c478bd9Sstevel@tonic-gate 			    DDI_DEV_AUTOINCR);
22617c478bd9Sstevel@tonic-gate 		}
22627c478bd9Sstevel@tonic-gate 	}
22637c478bd9Sstevel@tonic-gate 
22647c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
22657c478bd9Sstevel@tonic-gate }
22667c478bd9Sstevel@tonic-gate 
22677c478bd9Sstevel@tonic-gate 
22687c478bd9Sstevel@tonic-gate /*
22697c478bd9Sstevel@tonic-gate  * uhci_create_stats:
22707c478bd9Sstevel@tonic-gate  *	Allocate and initialize the uhci kstat structures
22717c478bd9Sstevel@tonic-gate  */
22727c478bd9Sstevel@tonic-gate void
uhci_create_stats(uhci_state_t * uhcip)22737c478bd9Sstevel@tonic-gate uhci_create_stats(uhci_state_t *uhcip)
22747c478bd9Sstevel@tonic-gate {
22757c478bd9Sstevel@tonic-gate 	int			i;
22767c478bd9Sstevel@tonic-gate 	char			kstatname[KSTAT_STRLEN];
22777c478bd9Sstevel@tonic-gate 	char			*usbtypes[USB_N_COUNT_KSTATS] =
2278fffe0b30Sqz 	    {"ctrl", "isoch", "bulk", "intr"};
22797c478bd9Sstevel@tonic-gate 	uint_t			instance = uhcip->uhci_instance;
22807c478bd9Sstevel@tonic-gate 	const char		*dname = ddi_driver_name(uhcip->uhci_dip);
22817c478bd9Sstevel@tonic-gate 	uhci_intrs_stats_t	*isp;
22827c478bd9Sstevel@tonic-gate 
22837c478bd9Sstevel@tonic-gate 	if (UHCI_INTRS_STATS(uhcip) == NULL) {
22847c478bd9Sstevel@tonic-gate 		(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,intrs",
22857c478bd9Sstevel@tonic-gate 		    dname, instance);
22867c478bd9Sstevel@tonic-gate 		UHCI_INTRS_STATS(uhcip) = kstat_create("usba", instance,
22877c478bd9Sstevel@tonic-gate 		    kstatname, "usb_interrupts", KSTAT_TYPE_NAMED,
22887c478bd9Sstevel@tonic-gate 		    sizeof (uhci_intrs_stats_t) / sizeof (kstat_named_t),
22897c478bd9Sstevel@tonic-gate 		    KSTAT_FLAG_PERSISTENT);
22907c478bd9Sstevel@tonic-gate 
22917c478bd9Sstevel@tonic-gate 		if (UHCI_INTRS_STATS(uhcip) != NULL) {
22927c478bd9Sstevel@tonic-gate 			isp = UHCI_INTRS_STATS_DATA(uhcip);
22937c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->uhci_intrs_hc_halted,
22947c478bd9Sstevel@tonic-gate 			    "HC Halted", KSTAT_DATA_UINT64);
22957c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->uhci_intrs_hc_process_err,
22967c478bd9Sstevel@tonic-gate 			    "HC Process Errors", KSTAT_DATA_UINT64);
22977c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->uhci_intrs_host_sys_err,
22987c478bd9Sstevel@tonic-gate 			    "Host Sys Errors", KSTAT_DATA_UINT64);
22997c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->uhci_intrs_resume_detected,
23007c478bd9Sstevel@tonic-gate 			    "Resume Detected", KSTAT_DATA_UINT64);
23017c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->uhci_intrs_usb_err_intr,
23027c478bd9Sstevel@tonic-gate 			    "USB Error", KSTAT_DATA_UINT64);
23037c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->uhci_intrs_usb_intr,
23047c478bd9Sstevel@tonic-gate 			    "USB Interrupts", KSTAT_DATA_UINT64);
23057c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->uhci_intrs_total,
23067c478bd9Sstevel@tonic-gate 			    "Total Interrupts", KSTAT_DATA_UINT64);
23077c478bd9Sstevel@tonic-gate 			kstat_named_init(&isp->uhci_intrs_not_claimed,
23087c478bd9Sstevel@tonic-gate 			    "Not Claimed", KSTAT_DATA_UINT64);
23097c478bd9Sstevel@tonic-gate 
23107c478bd9Sstevel@tonic-gate 			UHCI_INTRS_STATS(uhcip)->ks_private = uhcip;
23117c478bd9Sstevel@tonic-gate 			UHCI_INTRS_STATS(uhcip)->ks_update = nulldev;
23127c478bd9Sstevel@tonic-gate 			kstat_install(UHCI_INTRS_STATS(uhcip));
23137c478bd9Sstevel@tonic-gate 		}
23147c478bd9Sstevel@tonic-gate 	}
23157c478bd9Sstevel@tonic-gate 
23167c478bd9Sstevel@tonic-gate 	if (UHCI_TOTAL_STATS(uhcip) == NULL) {
23177c478bd9Sstevel@tonic-gate 		(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,total",
23187c478bd9Sstevel@tonic-gate 		    dname, instance);
23197c478bd9Sstevel@tonic-gate 		UHCI_TOTAL_STATS(uhcip) = kstat_create("usba", instance,
23207c478bd9Sstevel@tonic-gate 		    kstatname, "usb_byte_count", KSTAT_TYPE_IO, 1,
23217c478bd9Sstevel@tonic-gate 		    KSTAT_FLAG_PERSISTENT);
23227c478bd9Sstevel@tonic-gate 
23237c478bd9Sstevel@tonic-gate 		if (UHCI_TOTAL_STATS(uhcip) != NULL) {
23247c478bd9Sstevel@tonic-gate 			kstat_install(UHCI_TOTAL_STATS(uhcip));
23257c478bd9Sstevel@tonic-gate 		}
23267c478bd9Sstevel@tonic-gate 	}
23277c478bd9Sstevel@tonic-gate 
23287c478bd9Sstevel@tonic-gate 	for (i = 0; i < USB_N_COUNT_KSTATS; i++) {
23297c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_count_stats[i] == NULL) {
23307c478bd9Sstevel@tonic-gate 			(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,%s",
23317c478bd9Sstevel@tonic-gate 			    dname, instance, usbtypes[i]);
23327c478bd9Sstevel@tonic-gate 			uhcip->uhci_count_stats[i] = kstat_create("usba",
23337c478bd9Sstevel@tonic-gate 			    instance, kstatname, "usb_byte_count",
23347c478bd9Sstevel@tonic-gate 			    KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT);
23357c478bd9Sstevel@tonic-gate 
23367c478bd9Sstevel@tonic-gate 			if (uhcip->uhci_count_stats[i] != NULL) {
23377c478bd9Sstevel@tonic-gate 				kstat_install(uhcip->uhci_count_stats[i]);
23387c478bd9Sstevel@tonic-gate 			}
23397c478bd9Sstevel@tonic-gate 		}
23407c478bd9Sstevel@tonic-gate 	}
23417c478bd9Sstevel@tonic-gate }
23427c478bd9Sstevel@tonic-gate 
23437c478bd9Sstevel@tonic-gate 
23447c478bd9Sstevel@tonic-gate /*
23457c478bd9Sstevel@tonic-gate  * uhci_destroy_stats:
23467c478bd9Sstevel@tonic-gate  *	Clean up uhci kstat structures
23477c478bd9Sstevel@tonic-gate  */
23487c478bd9Sstevel@tonic-gate void
uhci_destroy_stats(uhci_state_t * uhcip)23497c478bd9Sstevel@tonic-gate uhci_destroy_stats(uhci_state_t *uhcip)
23507c478bd9Sstevel@tonic-gate {
23517c478bd9Sstevel@tonic-gate 	int i;
23527c478bd9Sstevel@tonic-gate 
23537c478bd9Sstevel@tonic-gate 	if (UHCI_INTRS_STATS(uhcip)) {
23547c478bd9Sstevel@tonic-gate 		kstat_delete(UHCI_INTRS_STATS(uhcip));
23557c478bd9Sstevel@tonic-gate 		UHCI_INTRS_STATS(uhcip) = NULL;
23567c478bd9Sstevel@tonic-gate 	}
23577c478bd9Sstevel@tonic-gate 
23587c478bd9Sstevel@tonic-gate 	if (UHCI_TOTAL_STATS(uhcip)) {
23597c478bd9Sstevel@tonic-gate 		kstat_delete(UHCI_TOTAL_STATS(uhcip));
23607c478bd9Sstevel@tonic-gate 		UHCI_TOTAL_STATS(uhcip) = NULL;
23617c478bd9Sstevel@tonic-gate 	}
23627c478bd9Sstevel@tonic-gate 
23637c478bd9Sstevel@tonic-gate 	for (i = 0; i < USB_N_COUNT_KSTATS; i++) {
23647c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_count_stats[i]) {
23657c478bd9Sstevel@tonic-gate 			kstat_delete(uhcip->uhci_count_stats[i]);
23667c478bd9Sstevel@tonic-gate 			uhcip->uhci_count_stats[i] = NULL;
23677c478bd9Sstevel@tonic-gate 		}
23687c478bd9Sstevel@tonic-gate 	}
23697c478bd9Sstevel@tonic-gate }
23707c478bd9Sstevel@tonic-gate 
23717c478bd9Sstevel@tonic-gate 
23727c478bd9Sstevel@tonic-gate void
uhci_do_intrs_stats(uhci_state_t * uhcip,int val)23737c478bd9Sstevel@tonic-gate uhci_do_intrs_stats(uhci_state_t *uhcip, int val)
23747c478bd9Sstevel@tonic-gate {
23757c478bd9Sstevel@tonic-gate 	if (UHCI_INTRS_STATS(uhcip) == NULL) {
23767c478bd9Sstevel@tonic-gate 
23777c478bd9Sstevel@tonic-gate 		return;
23787c478bd9Sstevel@tonic-gate 	}
23797c478bd9Sstevel@tonic-gate 
23807c478bd9Sstevel@tonic-gate 	UHCI_INTRS_STATS_DATA(uhcip)->uhci_intrs_total.value.ui64++;
23817c478bd9Sstevel@tonic-gate 	switch (val) {
23827c478bd9Sstevel@tonic-gate 	case USBSTS_REG_HC_HALTED:
23837c478bd9Sstevel@tonic-gate 		UHCI_INTRS_STATS_DATA(uhcip)->uhci_intrs_hc_halted.value.ui64++;
23847c478bd9Sstevel@tonic-gate 		break;
23857c478bd9Sstevel@tonic-gate 	case USBSTS_REG_HC_PROCESS_ERR:
23867c478bd9Sstevel@tonic-gate 		UHCI_INTRS_STATS_DATA(uhcip)->
2387fffe0b30Sqz 		    uhci_intrs_hc_process_err.value.ui64++;
23887c478bd9Sstevel@tonic-gate 		break;
23897c478bd9Sstevel@tonic-gate 	case USBSTS_REG_HOST_SYS_ERR:
23907c478bd9Sstevel@tonic-gate 		UHCI_INTRS_STATS_DATA(uhcip)->
2391fffe0b30Sqz 		    uhci_intrs_host_sys_err.value.ui64++;
23927c478bd9Sstevel@tonic-gate 		break;
23937c478bd9Sstevel@tonic-gate 	case USBSTS_REG_RESUME_DETECT:
23947c478bd9Sstevel@tonic-gate 		UHCI_INTRS_STATS_DATA(uhcip)->
2395fffe0b30Sqz 		    uhci_intrs_resume_detected.value.ui64++;
23967c478bd9Sstevel@tonic-gate 		break;
23977c478bd9Sstevel@tonic-gate 	case USBSTS_REG_USB_ERR_INTR:
23987c478bd9Sstevel@tonic-gate 		UHCI_INTRS_STATS_DATA(uhcip)->
2399fffe0b30Sqz 		    uhci_intrs_usb_err_intr.value.ui64++;
24007c478bd9Sstevel@tonic-gate 		break;
24017c478bd9Sstevel@tonic-gate 	case USBSTS_REG_USB_INTR:
24027c478bd9Sstevel@tonic-gate 		UHCI_INTRS_STATS_DATA(uhcip)->uhci_intrs_usb_intr.value.ui64++;
24037c478bd9Sstevel@tonic-gate 		break;
24047c478bd9Sstevel@tonic-gate 	default:
24057c478bd9Sstevel@tonic-gate 		UHCI_INTRS_STATS_DATA(uhcip)->
2406fffe0b30Sqz 		    uhci_intrs_not_claimed.value.ui64++;
24077c478bd9Sstevel@tonic-gate 		break;
24087c478bd9Sstevel@tonic-gate 	}
24097c478bd9Sstevel@tonic-gate }
24107c478bd9Sstevel@tonic-gate 
24117c478bd9Sstevel@tonic-gate 
24127c478bd9Sstevel@tonic-gate void
uhci_do_byte_stats(uhci_state_t * uhcip,size_t len,uint8_t attr,uint8_t addr)24137c478bd9Sstevel@tonic-gate uhci_do_byte_stats(uhci_state_t *uhcip, size_t len, uint8_t attr, uint8_t addr)
24147c478bd9Sstevel@tonic-gate {
24157c478bd9Sstevel@tonic-gate 	uint8_t type = attr & USB_EP_ATTR_MASK;
24167c478bd9Sstevel@tonic-gate 	uint8_t dir = addr & USB_EP_DIR_MASK;
24177c478bd9Sstevel@tonic-gate 
24187c478bd9Sstevel@tonic-gate 	switch (dir) {
24197c478bd9Sstevel@tonic-gate 	case USB_EP_DIR_IN:
24207c478bd9Sstevel@tonic-gate 		UHCI_TOTAL_STATS_DATA(uhcip)->reads++;
24217c478bd9Sstevel@tonic-gate 		UHCI_TOTAL_STATS_DATA(uhcip)->nread += len;
24227c478bd9Sstevel@tonic-gate 		switch (type) {
24237c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
24247c478bd9Sstevel@tonic-gate 			UHCI_CTRL_STATS(uhcip)->reads++;
24257c478bd9Sstevel@tonic-gate 			UHCI_CTRL_STATS(uhcip)->nread += len;
24267c478bd9Sstevel@tonic-gate 			break;
24277c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
24287c478bd9Sstevel@tonic-gate 			UHCI_BULK_STATS(uhcip)->reads++;
24297c478bd9Sstevel@tonic-gate 			UHCI_BULK_STATS(uhcip)->nread += len;
24307c478bd9Sstevel@tonic-gate 			break;
24317c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
24327c478bd9Sstevel@tonic-gate 			UHCI_INTR_STATS(uhcip)->reads++;
24337c478bd9Sstevel@tonic-gate 			UHCI_INTR_STATS(uhcip)->nread += len;
24347c478bd9Sstevel@tonic-gate 			break;
24357c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_ISOCH:
24367c478bd9Sstevel@tonic-gate 			UHCI_ISOC_STATS(uhcip)->reads++;
24377c478bd9Sstevel@tonic-gate 			UHCI_ISOC_STATS(uhcip)->nread += len;
24387c478bd9Sstevel@tonic-gate 			break;
24397c478bd9Sstevel@tonic-gate 		}
24407c478bd9Sstevel@tonic-gate 		break;
24417c478bd9Sstevel@tonic-gate 	case USB_EP_DIR_OUT:
24427c478bd9Sstevel@tonic-gate 		UHCI_TOTAL_STATS_DATA(uhcip)->writes++;
24437c478bd9Sstevel@tonic-gate 		UHCI_TOTAL_STATS_DATA(uhcip)->nwritten += len;
24447c478bd9Sstevel@tonic-gate 		switch (type) {
24457c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
24467c478bd9Sstevel@tonic-gate 			UHCI_CTRL_STATS(uhcip)->writes++;
24477c478bd9Sstevel@tonic-gate 			UHCI_CTRL_STATS(uhcip)->nwritten += len;
24487c478bd9Sstevel@tonic-gate 			break;
24497c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
24507c478bd9Sstevel@tonic-gate 			UHCI_BULK_STATS(uhcip)->writes++;
24517c478bd9Sstevel@tonic-gate 			UHCI_BULK_STATS(uhcip)->nwritten += len;
24527c478bd9Sstevel@tonic-gate 			break;
24537c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
24547c478bd9Sstevel@tonic-gate 			UHCI_INTR_STATS(uhcip)->writes++;
24557c478bd9Sstevel@tonic-gate 			UHCI_INTR_STATS(uhcip)->nwritten += len;
24567c478bd9Sstevel@tonic-gate 			break;
24577c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_ISOCH:
24587c478bd9Sstevel@tonic-gate 			UHCI_ISOC_STATS(uhcip)->writes++;
24597c478bd9Sstevel@tonic-gate 			UHCI_ISOC_STATS(uhcip)->nwritten += len;
24607c478bd9Sstevel@tonic-gate 			break;
24617c478bd9Sstevel@tonic-gate 		}
24627c478bd9Sstevel@tonic-gate 		break;
24637c478bd9Sstevel@tonic-gate 	}
24647c478bd9Sstevel@tonic-gate }
24657c478bd9Sstevel@tonic-gate 
24667c478bd9Sstevel@tonic-gate 
24677c478bd9Sstevel@tonic-gate /*
24687c478bd9Sstevel@tonic-gate  * uhci_free_tw:
24697c478bd9Sstevel@tonic-gate  *	Free the Transfer Wrapper (TW).
24707c478bd9Sstevel@tonic-gate  */
24717c478bd9Sstevel@tonic-gate void
uhci_free_tw(uhci_state_t * uhcip,uhci_trans_wrapper_t * tw)24727c478bd9Sstevel@tonic-gate uhci_free_tw(uhci_state_t *uhcip, uhci_trans_wrapper_t *tw)
24737c478bd9Sstevel@tonic-gate {
247402acac7eSsl 	int rval, i;
24757c478bd9Sstevel@tonic-gate 
24767c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl, "uhci_free_tw:");
24777c478bd9Sstevel@tonic-gate 
24787c478bd9Sstevel@tonic-gate 	ASSERT(tw != NULL);
24797c478bd9Sstevel@tonic-gate 
248002acac7eSsl 	if (tw->tw_isoc_strtlen > 0) {
248102acac7eSsl 		ASSERT(tw->tw_isoc_bufs != NULL);
248202acac7eSsl 		for (i = 0; i < tw->tw_ncookies; i++) {
248302acac7eSsl 			rval = ddi_dma_unbind_handle(
248402acac7eSsl 			    tw->tw_isoc_bufs[i].dma_handle);
248502acac7eSsl 			ASSERT(rval == USB_SUCCESS);
248602acac7eSsl 			ddi_dma_mem_free(&tw->tw_isoc_bufs[i].mem_handle);
248702acac7eSsl 			ddi_dma_free_handle(&tw->tw_isoc_bufs[i].dma_handle);
248802acac7eSsl 		}
248902acac7eSsl 		kmem_free(tw->tw_isoc_bufs, tw->tw_isoc_strtlen);
249002acac7eSsl 	} else if (tw->tw_dmahandle != NULL) {
249102acac7eSsl 		rval = ddi_dma_unbind_handle(tw->tw_dmahandle);
249202acac7eSsl 		ASSERT(rval == DDI_SUCCESS);
249302acac7eSsl 
249402acac7eSsl 		ddi_dma_mem_free(&tw->tw_accesshandle);
249502acac7eSsl 		ddi_dma_free_handle(&tw->tw_dmahandle);
249602acac7eSsl 	}
24977c478bd9Sstevel@tonic-gate 
24987c478bd9Sstevel@tonic-gate 	kmem_free(tw, sizeof (uhci_trans_wrapper_t));
24997c478bd9Sstevel@tonic-gate }
25007c478bd9Sstevel@tonic-gate 
25017c478bd9Sstevel@tonic-gate 
25027c478bd9Sstevel@tonic-gate /*
25037c478bd9Sstevel@tonic-gate  * uhci_deallocate_tw:
25047c478bd9Sstevel@tonic-gate  *	Deallocate of a Transaction Wrapper (TW) and this involves
25057c478bd9Sstevel@tonic-gate  *	the freeing of DMA resources.
25067c478bd9Sstevel@tonic-gate  */
25077c478bd9Sstevel@tonic-gate void
uhci_deallocate_tw(uhci_state_t * uhcip,uhci_pipe_private_t * pp,uhci_trans_wrapper_t * tw)25087c478bd9Sstevel@tonic-gate uhci_deallocate_tw(uhci_state_t *uhcip,
25097c478bd9Sstevel@tonic-gate     uhci_pipe_private_t *pp, uhci_trans_wrapper_t *tw)
25107c478bd9Sstevel@tonic-gate {
25117c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*head;
25127c478bd9Sstevel@tonic-gate 
25137c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
25147c478bd9Sstevel@tonic-gate 	    "uhci_deallocate_tw:");
25157c478bd9Sstevel@tonic-gate 
25167c478bd9Sstevel@tonic-gate 	/*
25177c478bd9Sstevel@tonic-gate 	 * If the transfer wrapper has no Host Controller (HC)
25187c478bd9Sstevel@tonic-gate 	 * Transfer Descriptors (TD) associated with it,  then
25197c478bd9Sstevel@tonic-gate 	 * remove the transfer wrapper. The transfers are done
25207c478bd9Sstevel@tonic-gate 	 * in FIFO order, so this should be the first transfer
25217c478bd9Sstevel@tonic-gate 	 * wrapper on the list.
25227c478bd9Sstevel@tonic-gate 	 */
25237c478bd9Sstevel@tonic-gate 	if (tw->tw_hctd_head != NULL) {
25247c478bd9Sstevel@tonic-gate 		ASSERT(tw->tw_hctd_tail != NULL);
25257c478bd9Sstevel@tonic-gate 
25267c478bd9Sstevel@tonic-gate 		return;
25277c478bd9Sstevel@tonic-gate 	}
25287c478bd9Sstevel@tonic-gate 
25297c478bd9Sstevel@tonic-gate 	ASSERT(tw->tw_hctd_tail == NULL);
25307c478bd9Sstevel@tonic-gate 	ASSERT(pp->pp_tw_head != NULL);
25317c478bd9Sstevel@tonic-gate 
25327c478bd9Sstevel@tonic-gate 	/*
25337c478bd9Sstevel@tonic-gate 	 * If pp->pp_tw_head is NULL, set the tail also to NULL.
25347c478bd9Sstevel@tonic-gate 	 */
25357c478bd9Sstevel@tonic-gate 	head = pp->pp_tw_head;
25367c478bd9Sstevel@tonic-gate 
25377c478bd9Sstevel@tonic-gate 	if (head == tw) {
25387c478bd9Sstevel@tonic-gate 		pp->pp_tw_head = head->tw_next;
25397c478bd9Sstevel@tonic-gate 		if (pp->pp_tw_head == NULL) {
25407c478bd9Sstevel@tonic-gate 			pp->pp_tw_tail = NULL;
25417c478bd9Sstevel@tonic-gate 		}
25427c478bd9Sstevel@tonic-gate 	} else {
25437c478bd9Sstevel@tonic-gate 		while (head->tw_next != tw)
25447c478bd9Sstevel@tonic-gate 			head = head->tw_next;
25457c478bd9Sstevel@tonic-gate 		head->tw_next = tw->tw_next;
25467c478bd9Sstevel@tonic-gate 		if (tw->tw_next == NULL) {
25477c478bd9Sstevel@tonic-gate 			pp->pp_tw_tail = head;
25487c478bd9Sstevel@tonic-gate 		}
25497c478bd9Sstevel@tonic-gate 	}
25507c478bd9Sstevel@tonic-gate 	uhci_free_tw(uhcip, tw);
25517c478bd9Sstevel@tonic-gate }
25527c478bd9Sstevel@tonic-gate 
25537c478bd9Sstevel@tonic-gate 
25547c478bd9Sstevel@tonic-gate void
uhci_delete_td(uhci_state_t * uhcip,uhci_td_t * td)25557c478bd9Sstevel@tonic-gate uhci_delete_td(uhci_state_t *uhcip, uhci_td_t *td)
25567c478bd9Sstevel@tonic-gate {
25577c478bd9Sstevel@tonic-gate 	uhci_td_t		*tmp_td;
25587c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw = td->tw;
25597c478bd9Sstevel@tonic-gate 
25607c478bd9Sstevel@tonic-gate 	if ((td->outst_td_next == NULL) && (td->outst_td_prev == NULL)) {
25617c478bd9Sstevel@tonic-gate 		uhcip->uhci_outst_tds_head = NULL;
25627c478bd9Sstevel@tonic-gate 		uhcip->uhci_outst_tds_tail = NULL;
25637c478bd9Sstevel@tonic-gate 	} else if (td->outst_td_next == NULL) {
25647c478bd9Sstevel@tonic-gate 		td->outst_td_prev->outst_td_next = NULL;
25657c478bd9Sstevel@tonic-gate 		uhcip->uhci_outst_tds_tail = td->outst_td_prev;
25667c478bd9Sstevel@tonic-gate 	} else if (td->outst_td_prev == NULL) {
25677c478bd9Sstevel@tonic-gate 		td->outst_td_next->outst_td_prev = NULL;
25687c478bd9Sstevel@tonic-gate 		uhcip->uhci_outst_tds_head = td->outst_td_next;
25697c478bd9Sstevel@tonic-gate 	} else {
25707c478bd9Sstevel@tonic-gate 		td->outst_td_prev->outst_td_next = td->outst_td_next;
25717c478bd9Sstevel@tonic-gate 		td->outst_td_next->outst_td_prev = td->outst_td_prev;
25727c478bd9Sstevel@tonic-gate 	}
25737c478bd9Sstevel@tonic-gate 
25747c478bd9Sstevel@tonic-gate 	tmp_td = tw->tw_hctd_head;
25757c478bd9Sstevel@tonic-gate 
25767c478bd9Sstevel@tonic-gate 	if (tmp_td != td) {
25777c478bd9Sstevel@tonic-gate 		while (tmp_td->tw_td_next != td) {
25787c478bd9Sstevel@tonic-gate 			tmp_td = tmp_td->tw_td_next;
25797c478bd9Sstevel@tonic-gate 		}
25807c478bd9Sstevel@tonic-gate 		ASSERT(tmp_td);
25817c478bd9Sstevel@tonic-gate 		tmp_td->tw_td_next = td->tw_td_next;
25827c478bd9Sstevel@tonic-gate 		if (td->tw_td_next == NULL) {
25837c478bd9Sstevel@tonic-gate 			tw->tw_hctd_tail = tmp_td;
25847c478bd9Sstevel@tonic-gate 		}
25857c478bd9Sstevel@tonic-gate 	} else {
25867c478bd9Sstevel@tonic-gate 		tw->tw_hctd_head = tw->tw_hctd_head->tw_td_next;
25877c478bd9Sstevel@tonic-gate 		if (tw->tw_hctd_head == NULL) {
25887c478bd9Sstevel@tonic-gate 			tw->tw_hctd_tail = NULL;
25897c478bd9Sstevel@tonic-gate 		}
25907c478bd9Sstevel@tonic-gate 	}
25917c478bd9Sstevel@tonic-gate 
25927c478bd9Sstevel@tonic-gate 	td->flag  = TD_FLAG_FREE;
25937c478bd9Sstevel@tonic-gate }
25947c478bd9Sstevel@tonic-gate 
25957c478bd9Sstevel@tonic-gate 
25967c478bd9Sstevel@tonic-gate void
uhci_remove_tds_tws(uhci_state_t * uhcip,usba_pipe_handle_data_t * ph)25977c478bd9Sstevel@tonic-gate uhci_remove_tds_tws(
25987c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
25997c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph)
26007c478bd9Sstevel@tonic-gate {
26017c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_reqp;
26027c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp = (uhci_pipe_private_t *)ph->p_hcd_private;
26037c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*ept = &pp->pp_pipe_handle->p_ep;
26047c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw_tmp;
26057c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw_head = pp->pp_tw_head;
26067c478bd9Sstevel@tonic-gate 
26077c478bd9Sstevel@tonic-gate 	while (tw_head != NULL) {
26087c478bd9Sstevel@tonic-gate 		tw_tmp = tw_head;
26097c478bd9Sstevel@tonic-gate 		tw_head = tw_head->tw_next;
26107c478bd9Sstevel@tonic-gate 
26117c478bd9Sstevel@tonic-gate 		curr_reqp = tw_tmp->tw_curr_xfer_reqp;
26127c478bd9Sstevel@tonic-gate 		if (curr_reqp) {
26137c478bd9Sstevel@tonic-gate 			/* do this for control/bulk/intr */
26147c478bd9Sstevel@tonic-gate 			if ((tw_tmp->tw_direction == PID_IN) &&
26157c478bd9Sstevel@tonic-gate 			    (UHCI_XFER_TYPE(ept) == USB_EP_ATTR_INTR)) {
26167c478bd9Sstevel@tonic-gate 				uhci_deallocate_periodic_in_resource(uhcip,
26177c478bd9Sstevel@tonic-gate 				    pp, tw_tmp);
26187c478bd9Sstevel@tonic-gate 			} else {
26197c478bd9Sstevel@tonic-gate 				uhci_hcdi_callback(uhcip, pp,
26207c478bd9Sstevel@tonic-gate 				    pp->pp_pipe_handle, tw_tmp, USB_CR_FLUSHED);
26217c478bd9Sstevel@tonic-gate 			}
26227c478bd9Sstevel@tonic-gate 		} /* end of curr_reqp */
26237c478bd9Sstevel@tonic-gate 
26247c478bd9Sstevel@tonic-gate 		if (tw_tmp->tw_claim != UHCI_MODIFY_TD_BITS_CLAIMED) {
26257c478bd9Sstevel@tonic-gate 			continue;
26267c478bd9Sstevel@tonic-gate 		}
26277c478bd9Sstevel@tonic-gate 
26287c478bd9Sstevel@tonic-gate 		while (tw_tmp->tw_hctd_head != NULL) {
26297c478bd9Sstevel@tonic-gate 			uhci_delete_td(uhcip, tw_tmp->tw_hctd_head);
26307c478bd9Sstevel@tonic-gate 		}
26317c478bd9Sstevel@tonic-gate 
26327c478bd9Sstevel@tonic-gate 		uhci_deallocate_tw(uhcip, pp, tw_tmp);
26337c478bd9Sstevel@tonic-gate 	}
26347c478bd9Sstevel@tonic-gate }
26357c478bd9Sstevel@tonic-gate 
26367c478bd9Sstevel@tonic-gate 
26377c478bd9Sstevel@tonic-gate /*
26387c478bd9Sstevel@tonic-gate  * uhci_remove_qh:
26397c478bd9Sstevel@tonic-gate  *	Remove the Queue Head from the Host Controller's
26407c478bd9Sstevel@tonic-gate  *	appropriate QH list.
26417c478bd9Sstevel@tonic-gate  */
26427c478bd9Sstevel@tonic-gate void
uhci_remove_qh(uhci_state_t * uhcip,uhci_pipe_private_t * pp)26437c478bd9Sstevel@tonic-gate uhci_remove_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
26447c478bd9Sstevel@tonic-gate {
26457c478bd9Sstevel@tonic-gate 	uhci_td_t	*dummy_td;
26467c478bd9Sstevel@tonic-gate 
26477c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
26487c478bd9Sstevel@tonic-gate 
26497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
26507c478bd9Sstevel@tonic-gate 	    "uhci_remove_qh:");
26517c478bd9Sstevel@tonic-gate 
26527c478bd9Sstevel@tonic-gate 	dummy_td = pp->pp_qh->td_tailp;
26537c478bd9Sstevel@tonic-gate 	dummy_td->flag = TD_FLAG_FREE;
26547c478bd9Sstevel@tonic-gate 
26557c478bd9Sstevel@tonic-gate 	switch (UHCI_XFER_TYPE(&pp->pp_pipe_handle->p_ep)) {
26567c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
26577c478bd9Sstevel@tonic-gate 		uhci_remove_ctrl_qh(uhcip, pp);
26587c478bd9Sstevel@tonic-gate 		break;
26597c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
26607c478bd9Sstevel@tonic-gate 		uhci_remove_bulk_qh(uhcip, pp);
26617c478bd9Sstevel@tonic-gate 		break;
26627c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
26637c478bd9Sstevel@tonic-gate 		uhci_remove_intr_qh(uhcip, pp);
26647c478bd9Sstevel@tonic-gate 		break;
26657c478bd9Sstevel@tonic-gate 	}
26667c478bd9Sstevel@tonic-gate }
26677c478bd9Sstevel@tonic-gate 
26687c478bd9Sstevel@tonic-gate 
26697c478bd9Sstevel@tonic-gate static void
uhci_remove_intr_qh(uhci_state_t * uhcip,uhci_pipe_private_t * pp)26707c478bd9Sstevel@tonic-gate uhci_remove_intr_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
26717c478bd9Sstevel@tonic-gate {
26727c478bd9Sstevel@tonic-gate 	queue_head_t   *qh = pp->pp_qh;
26737c478bd9Sstevel@tonic-gate 	queue_head_t   *next_lattice_qh =
2674fffe0b30Sqz 	    QH_VADDR(GetQH32(uhcip, qh->link_ptr) & QH_LINK_PTR_MASK);
26757c478bd9Sstevel@tonic-gate 
26767c478bd9Sstevel@tonic-gate 	qh->prev_qh->link_ptr	 = qh->link_ptr;
26777c478bd9Sstevel@tonic-gate 	next_lattice_qh->prev_qh = qh->prev_qh;
26787c478bd9Sstevel@tonic-gate 	qh->qh_flag = QUEUE_HEAD_FLAG_FREE;
26797c478bd9Sstevel@tonic-gate 
26807c478bd9Sstevel@tonic-gate }
26817c478bd9Sstevel@tonic-gate 
26827c478bd9Sstevel@tonic-gate /*
26837c478bd9Sstevel@tonic-gate  * uhci_remove_bulk_qh:
26847c478bd9Sstevel@tonic-gate  *	Remove a bulk QH from the Host Controller's QH list. There may be a
26857c478bd9Sstevel@tonic-gate  *	loop for bulk QHs, we must care about this while removing a bulk QH.
26867c478bd9Sstevel@tonic-gate  */
26877c478bd9Sstevel@tonic-gate static void
uhci_remove_bulk_qh(uhci_state_t * uhcip,uhci_pipe_private_t * pp)26887c478bd9Sstevel@tonic-gate uhci_remove_bulk_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
26897c478bd9Sstevel@tonic-gate {
26907c478bd9Sstevel@tonic-gate 	queue_head_t   *qh = pp->pp_qh;
26917c478bd9Sstevel@tonic-gate 	queue_head_t   *next_lattice_qh;
26927c478bd9Sstevel@tonic-gate 	uint32_t	paddr;
26937c478bd9Sstevel@tonic-gate 
26947c478bd9Sstevel@tonic-gate 	paddr = (GetQH32(uhcip, qh->link_ptr) & QH_LINK_PTR_MASK);
26957c478bd9Sstevel@tonic-gate 	next_lattice_qh = (qh == uhcip->uhci_bulk_xfers_q_tail) ?
26967c478bd9Sstevel@tonic-gate 	    0 : QH_VADDR(paddr);
26977c478bd9Sstevel@tonic-gate 
26987c478bd9Sstevel@tonic-gate 	if ((qh == uhcip->uhci_bulk_xfers_q_tail) &&
26997c478bd9Sstevel@tonic-gate 	    (qh->prev_qh == uhcip->uhci_bulk_xfers_q_head)) {
27007c478bd9Sstevel@tonic-gate 		SetQH32(uhcip, qh->prev_qh->link_ptr, HC_END_OF_LIST);
27017c478bd9Sstevel@tonic-gate 	} else {
27027c478bd9Sstevel@tonic-gate 		qh->prev_qh->link_ptr = qh->link_ptr;
27037c478bd9Sstevel@tonic-gate 	}
27047c478bd9Sstevel@tonic-gate 
27057c478bd9Sstevel@tonic-gate 	if (next_lattice_qh == NULL) {
27067c478bd9Sstevel@tonic-gate 		uhcip->uhci_bulk_xfers_q_tail = qh->prev_qh;
27077c478bd9Sstevel@tonic-gate 	} else {
27087c478bd9Sstevel@tonic-gate 		next_lattice_qh->prev_qh = qh->prev_qh;
27097c478bd9Sstevel@tonic-gate 	}
27107c478bd9Sstevel@tonic-gate 
27117c478bd9Sstevel@tonic-gate 	qh->qh_flag = QUEUE_HEAD_FLAG_FREE;
27127c478bd9Sstevel@tonic-gate 
27137c478bd9Sstevel@tonic-gate }
27147c478bd9Sstevel@tonic-gate 
27157c478bd9Sstevel@tonic-gate 
27167c478bd9Sstevel@tonic-gate static void
uhci_remove_ctrl_qh(uhci_state_t * uhcip,uhci_pipe_private_t * pp)27177c478bd9Sstevel@tonic-gate uhci_remove_ctrl_qh(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
27187c478bd9Sstevel@tonic-gate {
27197c478bd9Sstevel@tonic-gate 	queue_head_t   *qh = pp->pp_qh;
27207c478bd9Sstevel@tonic-gate 	queue_head_t   *next_lattice_qh =
27217c478bd9Sstevel@tonic-gate 	    QH_VADDR(GetQH32(uhcip, qh->link_ptr) & QH_LINK_PTR_MASK);
27227c478bd9Sstevel@tonic-gate 
27237c478bd9Sstevel@tonic-gate 	qh->prev_qh->link_ptr = qh->link_ptr;
27247c478bd9Sstevel@tonic-gate 	if (next_lattice_qh->prev_qh != NULL) {
27257c478bd9Sstevel@tonic-gate 		next_lattice_qh->prev_qh = qh->prev_qh;
27267c478bd9Sstevel@tonic-gate 	} else {
27277c478bd9Sstevel@tonic-gate 		uhcip->uhci_ctrl_xfers_q_tail = qh->prev_qh;
27287c478bd9Sstevel@tonic-gate 	}
27297c478bd9Sstevel@tonic-gate 
27307c478bd9Sstevel@tonic-gate 	qh->qh_flag = QUEUE_HEAD_FLAG_FREE;
27317c478bd9Sstevel@tonic-gate }
27327c478bd9Sstevel@tonic-gate 
27337c478bd9Sstevel@tonic-gate 
27347c478bd9Sstevel@tonic-gate /*
27357c478bd9Sstevel@tonic-gate  * uhci_allocate_td_from_pool:
27367c478bd9Sstevel@tonic-gate  *	Allocate a Transfer Descriptor (TD) from the TD buffer pool.
27377c478bd9Sstevel@tonic-gate  */
27387c478bd9Sstevel@tonic-gate static uhci_td_t *
uhci_allocate_td_from_pool(uhci_state_t * uhcip)27397c478bd9Sstevel@tonic-gate uhci_allocate_td_from_pool(uhci_state_t *uhcip)
27407c478bd9Sstevel@tonic-gate {
27417c478bd9Sstevel@tonic-gate 	int		index;
27427c478bd9Sstevel@tonic-gate 	uhci_td_t	*td;
27437c478bd9Sstevel@tonic-gate 
27447c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
27457c478bd9Sstevel@tonic-gate 
27467c478bd9Sstevel@tonic-gate 	/*
27477c478bd9Sstevel@tonic-gate 	 * Search for a blank Transfer Descriptor (TD)
27487c478bd9Sstevel@tonic-gate 	 * in the TD buffer pool.
27497c478bd9Sstevel@tonic-gate 	 */
27507c478bd9Sstevel@tonic-gate 	for (index = 0; index < uhci_td_pool_size; index ++) {
27517c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_td_pool_addr[index].flag == TD_FLAG_FREE) {
27527c478bd9Sstevel@tonic-gate 			break;
27537c478bd9Sstevel@tonic-gate 		}
27547c478bd9Sstevel@tonic-gate 	}
27557c478bd9Sstevel@tonic-gate 
27567c478bd9Sstevel@tonic-gate 	if (index == uhci_td_pool_size) {
27577c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
27587c478bd9Sstevel@tonic-gate 		    "uhci_allocate_td_from_pool: TD exhausted");
27597c478bd9Sstevel@tonic-gate 
27607c478bd9Sstevel@tonic-gate 		return (NULL);
27617c478bd9Sstevel@tonic-gate 	}
27627c478bd9Sstevel@tonic-gate 
27637c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALLOC, uhcip->uhci_log_hdl,
27647c478bd9Sstevel@tonic-gate 	    "uhci_allocate_td_from_pool: Allocated %d", index);
27657c478bd9Sstevel@tonic-gate 
27667c478bd9Sstevel@tonic-gate 	/* Create a new dummy for the end of the TD list */
27677c478bd9Sstevel@tonic-gate 	td = &uhcip->uhci_td_pool_addr[index];
27687c478bd9Sstevel@tonic-gate 
27697c478bd9Sstevel@tonic-gate 	/* Mark the newly allocated TD as a dummy */
27707c478bd9Sstevel@tonic-gate 	td->flag =  TD_FLAG_DUMMY;
27717c478bd9Sstevel@tonic-gate 	td->qh_td_prev	=  NULL;
27727c478bd9Sstevel@tonic-gate 
27737c478bd9Sstevel@tonic-gate 	return (td);
27747c478bd9Sstevel@tonic-gate }
27757c478bd9Sstevel@tonic-gate 
27767c478bd9Sstevel@tonic-gate 
27777c478bd9Sstevel@tonic-gate /*
27787c478bd9Sstevel@tonic-gate  * uhci_insert_bulk_td:
27797c478bd9Sstevel@tonic-gate  */
27807c478bd9Sstevel@tonic-gate int
uhci_insert_bulk_td(uhci_state_t * uhcip,usba_pipe_handle_data_t * ph,usb_bulk_req_t * req,usb_flags_t flags)27817c478bd9Sstevel@tonic-gate uhci_insert_bulk_td(
27827c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
27837c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
27847c478bd9Sstevel@tonic-gate 	usb_bulk_req_t		*req,
27857c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
27867c478bd9Sstevel@tonic-gate {
27877c478bd9Sstevel@tonic-gate 	size_t			length;
27887c478bd9Sstevel@tonic-gate 	uint_t			mps;	/* MaxPacketSize */
278902acac7eSsl 	uint_t			num_bulk_tds, i, j;
279002acac7eSsl 	uint32_t		buf_offs;
27917c478bd9Sstevel@tonic-gate 	uhci_td_t		*bulk_td_ptr;
279202acac7eSsl 	uhci_td_t		*current_dummy, *tmp_td;
27937c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp = (uhci_pipe_private_t *)ph->p_hcd_private;
27947c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw;
27957c478bd9Sstevel@tonic-gate 	uhci_bulk_isoc_xfer_t	*bulk_xfer_info;
279602acac7eSsl 	uhci_bulk_isoc_td_pool_t *td_pool_ptr;
27977c478bd9Sstevel@tonic-gate 
27987c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
2799112116d8Sfb 	    "uhci_insert_bulk_td: req: 0x%p, flags = 0x%x", (void *)req, flags);
28007c478bd9Sstevel@tonic-gate 
28017c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
28027c478bd9Sstevel@tonic-gate 
28037c478bd9Sstevel@tonic-gate 	/*
28047c478bd9Sstevel@tonic-gate 	 * Create transfer wrapper
28057c478bd9Sstevel@tonic-gate 	 */
28067c478bd9Sstevel@tonic-gate 	if ((tw = uhci_create_transfer_wrapper(uhcip, pp, req->bulk_len,
28077c478bd9Sstevel@tonic-gate 	    flags)) == NULL) {
28087c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
28097c478bd9Sstevel@tonic-gate 		    "uhci_insert_bulk_td: TW allocation failed");
28107c478bd9Sstevel@tonic-gate 
28117c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
28127c478bd9Sstevel@tonic-gate 	}
28137c478bd9Sstevel@tonic-gate 
28147c478bd9Sstevel@tonic-gate 	tw->tw_bytes_xfered		= 0;
28157c478bd9Sstevel@tonic-gate 	tw->tw_bytes_pending		= req->bulk_len;
28167c478bd9Sstevel@tonic-gate 	tw->tw_handle_td		= uhci_handle_bulk_td;
28177c478bd9Sstevel@tonic-gate 	tw->tw_handle_callback_value	= (usb_opaque_t)req->bulk_data;
28187c478bd9Sstevel@tonic-gate 	tw->tw_timeout_cnt		= req->bulk_timeout;
28197c478bd9Sstevel@tonic-gate 	tw->tw_data			= req->bulk_data;
28207c478bd9Sstevel@tonic-gate 	tw->tw_curr_xfer_reqp		= (usb_opaque_t)req;
28217c478bd9Sstevel@tonic-gate 
28227c478bd9Sstevel@tonic-gate 	/* Get the bulk pipe direction */
28237c478bd9Sstevel@tonic-gate 	tw->tw_direction = (UHCI_XFER_DIR(&ph->p_ep) == USB_EP_DIR_OUT) ?
2824fffe0b30Sqz 	    PID_OUT : PID_IN;
28257c478bd9Sstevel@tonic-gate 
28267c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
28277c478bd9Sstevel@tonic-gate 	    "uhci_insert_bulk_td: direction: 0x%x", tw->tw_direction);
28287c478bd9Sstevel@tonic-gate 
28297c478bd9Sstevel@tonic-gate 	/* If the DATA OUT, copy the data into transfer buffer. */
28307c478bd9Sstevel@tonic-gate 	if (tw->tw_direction == PID_OUT) {
2831688b07c5Sgc 		if (req->bulk_len) {
2832688b07c5Sgc 			ASSERT(req->bulk_data != NULL);
28337c478bd9Sstevel@tonic-gate 
2834688b07c5Sgc 			/* Copy the data into the message */
2835688b07c5Sgc 			ddi_rep_put8(tw->tw_accesshandle,
2836fffe0b30Sqz 			    req->bulk_data->b_rptr,
2837fffe0b30Sqz 			    (uint8_t *)tw->tw_buf,
2838fffe0b30Sqz 			    req->bulk_len, DDI_DEV_AUTOINCR);
2839688b07c5Sgc 		}
28407c478bd9Sstevel@tonic-gate 	}
28417c478bd9Sstevel@tonic-gate 
28427c478bd9Sstevel@tonic-gate 	/* Get the max packet size.  */
28437c478bd9Sstevel@tonic-gate 	length = mps = pp->pp_pipe_handle->p_ep.wMaxPacketSize;
28447c478bd9Sstevel@tonic-gate 
28457c478bd9Sstevel@tonic-gate 	/*
28467c478bd9Sstevel@tonic-gate 	 * Calculate number of TD's to insert in the current frame interval.
28477c478bd9Sstevel@tonic-gate 	 * Max number TD's allowed (driver implementation) is 128
28487c478bd9Sstevel@tonic-gate 	 * in one frame interval. Once all the TD's are completed
28497c478bd9Sstevel@tonic-gate 	 * then the remaining TD's will be inserted into the lattice
28507c478bd9Sstevel@tonic-gate 	 * in the uhci_handle_bulk_td().
28517c478bd9Sstevel@tonic-gate 	 */
28527c478bd9Sstevel@tonic-gate 	if ((tw->tw_bytes_pending / mps) >= MAX_NUM_BULK_TDS_PER_XFER) {
28537c478bd9Sstevel@tonic-gate 		num_bulk_tds = MAX_NUM_BULK_TDS_PER_XFER;
28547c478bd9Sstevel@tonic-gate 	} else {
28557c478bd9Sstevel@tonic-gate 		num_bulk_tds = (tw->tw_bytes_pending / mps);
28567c478bd9Sstevel@tonic-gate 
2857688b07c5Sgc 		if (tw->tw_bytes_pending % mps || tw->tw_bytes_pending == 0) {
28587c478bd9Sstevel@tonic-gate 			num_bulk_tds++;
28597c478bd9Sstevel@tonic-gate 			length = (tw->tw_bytes_pending % mps);
28607c478bd9Sstevel@tonic-gate 		}
28617c478bd9Sstevel@tonic-gate 	}
28627c478bd9Sstevel@tonic-gate 
28637c478bd9Sstevel@tonic-gate 	/*
28647c478bd9Sstevel@tonic-gate 	 * Allocate memory for the bulk xfer information structure
28657c478bd9Sstevel@tonic-gate 	 */
28667c478bd9Sstevel@tonic-gate 	if ((bulk_xfer_info = kmem_zalloc(
28677c478bd9Sstevel@tonic-gate 	    sizeof (uhci_bulk_isoc_xfer_t), KM_NOSLEEP)) == NULL) {
28687c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
28697c478bd9Sstevel@tonic-gate 		    "uhci_insert_bulk_td: kmem_zalloc failed");
28707c478bd9Sstevel@tonic-gate 
28717c478bd9Sstevel@tonic-gate 		/* Free the transfer wrapper */
28727c478bd9Sstevel@tonic-gate 		uhci_deallocate_tw(uhcip, pp, tw);
28737c478bd9Sstevel@tonic-gate 
28747c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
28757c478bd9Sstevel@tonic-gate 	}
28767c478bd9Sstevel@tonic-gate 
28777c478bd9Sstevel@tonic-gate 	/* Allocate memory for the bulk TD's */
287802acac7eSsl 	if (uhci_alloc_bulk_isoc_tds(uhcip, num_bulk_tds, bulk_xfer_info) !=
28797c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
28807c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
288102acac7eSsl 		    "uhci_insert_bulk_td: alloc_bulk_isoc_tds failed");
28827c478bd9Sstevel@tonic-gate 
28837c478bd9Sstevel@tonic-gate 		kmem_free(bulk_xfer_info, sizeof (uhci_bulk_isoc_xfer_t));
28847c478bd9Sstevel@tonic-gate 
28857c478bd9Sstevel@tonic-gate 		/* Free the transfer wrapper */
28867c478bd9Sstevel@tonic-gate 		uhci_deallocate_tw(uhcip, pp, tw);
28877c478bd9Sstevel@tonic-gate 
28887c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
28897c478bd9Sstevel@tonic-gate 	}
28907c478bd9Sstevel@tonic-gate 
289102acac7eSsl 	td_pool_ptr = &bulk_xfer_info->td_pools[0];
289202acac7eSsl 	bulk_td_ptr = (uhci_td_t *)td_pool_ptr->pool_addr;
28937c478bd9Sstevel@tonic-gate 	bulk_td_ptr[0].qh_td_prev = NULL;
28947c478bd9Sstevel@tonic-gate 	current_dummy = pp->pp_qh->td_tailp;
289502acac7eSsl 	buf_offs = 0;
28967c478bd9Sstevel@tonic-gate 	pp->pp_qh->bulk_xfer_info = bulk_xfer_info;
28977c478bd9Sstevel@tonic-gate 
28987c478bd9Sstevel@tonic-gate 	/* Fill up all the bulk TD's */
289902acac7eSsl 	for (i = 0; i < bulk_xfer_info->num_pools; i++) {
290002acac7eSsl 		for (j = 0; j < (td_pool_ptr->num_tds - 1); j++) {
290102acac7eSsl 			uhci_fill_in_bulk_isoc_td(uhcip, &bulk_td_ptr[j],
290202acac7eSsl 			    &bulk_td_ptr[j+1], BULKTD_PADDR(td_pool_ptr,
290302acac7eSsl 			    &bulk_td_ptr[j+1]), ph, buf_offs, mps, tw);
290402acac7eSsl 			buf_offs += mps;
290502acac7eSsl 		}
290602acac7eSsl 
290702acac7eSsl 		/* fill in the last TD */
290802acac7eSsl 		if (i == (bulk_xfer_info->num_pools - 1)) {
290902acac7eSsl 			uhci_fill_in_bulk_isoc_td(uhcip, &bulk_td_ptr[j],
291002acac7eSsl 			    current_dummy, TD_PADDR(current_dummy),
291102acac7eSsl 			    ph, buf_offs, length, tw);
291202acac7eSsl 		} else {
291302acac7eSsl 			/* fill in the TD at the tail of a pool */
291402acac7eSsl 			tmp_td = &bulk_td_ptr[j];
291502acac7eSsl 			td_pool_ptr = &bulk_xfer_info->td_pools[i + 1];
291602acac7eSsl 			bulk_td_ptr = (uhci_td_t *)td_pool_ptr->pool_addr;
291702acac7eSsl 			uhci_fill_in_bulk_isoc_td(uhcip, tmp_td,
291802acac7eSsl 			    &bulk_td_ptr[0], BULKTD_PADDR(td_pool_ptr,
291902acac7eSsl 			    &bulk_td_ptr[0]), ph, buf_offs, mps, tw);
292002acac7eSsl 			buf_offs += mps;
292102acac7eSsl 		}
29227c478bd9Sstevel@tonic-gate 	}
29237c478bd9Sstevel@tonic-gate 
2924d29f5a71Szhigang lu - Sun Microsystems - Beijing China 	bulk_xfer_info->num_tds	= (ushort_t)num_bulk_tds;
29257c478bd9Sstevel@tonic-gate 
29267c478bd9Sstevel@tonic-gate 	/*
29277c478bd9Sstevel@tonic-gate 	 * Point the end of the lattice tree to the start of the bulk xfers
29287c478bd9Sstevel@tonic-gate 	 * queue head. This allows the HC to execute the same Queue Head/TD
29297c478bd9Sstevel@tonic-gate 	 * in the same frame. There are some bulk devices, which NAKs after
29307c478bd9Sstevel@tonic-gate 	 * completing each TD. As a result, the performance on such devices
29317c478bd9Sstevel@tonic-gate 	 * is very bad.  This loop will  provide a chance to execute NAk'ed
29327c478bd9Sstevel@tonic-gate 	 * bulk TDs again in the same frame.
29337c478bd9Sstevel@tonic-gate 	 */
29347c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_pending_bulk_cmds++ == 0) {
29357c478bd9Sstevel@tonic-gate 		uhcip->uhci_bulk_xfers_q_tail->link_ptr =
2936fffe0b30Sqz 		    uhcip->uhci_bulk_xfers_q_head->link_ptr;
29377c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
29387c478bd9Sstevel@tonic-gate 		    "uhci_insert_bulk_td: count = %d no tds  %d",
29397c478bd9Sstevel@tonic-gate 		    uhcip->uhci_pending_bulk_cmds, num_bulk_tds);
29407c478bd9Sstevel@tonic-gate 	}
29417c478bd9Sstevel@tonic-gate 
29427c478bd9Sstevel@tonic-gate 	/* Insert on the bulk queue head for the execution by HC */
29437c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, pp->pp_qh->element_ptr,
294402acac7eSsl 	    bulk_xfer_info->td_pools[0].cookie.dmac_address);
29457c478bd9Sstevel@tonic-gate 
29467c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
29477c478bd9Sstevel@tonic-gate }
29487c478bd9Sstevel@tonic-gate 
29497c478bd9Sstevel@tonic-gate 
29507c478bd9Sstevel@tonic-gate /*
29517c478bd9Sstevel@tonic-gate  * uhci_fill_in_bulk_isoc_td
295202acac7eSsl  *     Fills the bulk/isoc TD
295302acac7eSsl  *
295402acac7eSsl  * offset - different meanings for bulk and isoc TDs:
2955fffe0b30Sqz  *	    starting offset into the TW buffer for a bulk TD
2956fffe0b30Sqz  *	    and the index into the isoc packet list for an isoc TD
29577c478bd9Sstevel@tonic-gate  */
29587c478bd9Sstevel@tonic-gate void
uhci_fill_in_bulk_isoc_td(uhci_state_t * uhcip,uhci_td_t * current_td,uhci_td_t * next_td,uint32_t next_td_paddr,usba_pipe_handle_data_t * ph,uint_t offset,uint_t length,uhci_trans_wrapper_t * tw)29597c478bd9Sstevel@tonic-gate uhci_fill_in_bulk_isoc_td(uhci_state_t *uhcip, uhci_td_t *current_td,
29607c478bd9Sstevel@tonic-gate 	uhci_td_t		*next_td,
29617c478bd9Sstevel@tonic-gate 	uint32_t		next_td_paddr,
29627c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
296302acac7eSsl 	uint_t			offset,
29647c478bd9Sstevel@tonic-gate 	uint_t			length,
29657c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw)
29667c478bd9Sstevel@tonic-gate {
29677c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp = (uhci_pipe_private_t *)ph->p_hcd_private;
29687c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*ept = &pp->pp_pipe_handle->p_ep;
296902acac7eSsl 	uint32_t		buf_addr;
29707c478bd9Sstevel@tonic-gate 
29717c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
297202acac7eSsl 	    "uhci_fill_in_bulk_isoc_td: tw 0x%p offs 0x%x length 0x%x",
2973112116d8Sfb 	    (void *)tw, offset, length);
29747c478bd9Sstevel@tonic-gate 
29757c478bd9Sstevel@tonic-gate 	bzero((char *)current_td, sizeof (uhci_td_t));
29767c478bd9Sstevel@tonic-gate 	SetTD32(uhcip, current_td->link_ptr, next_td_paddr | HC_DEPTH_FIRST);
29777c478bd9Sstevel@tonic-gate 
29787c478bd9Sstevel@tonic-gate 	switch (UHCI_XFER_TYPE(ept)) {
29797c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
29807c478bd9Sstevel@tonic-gate 		if (((usb_isoc_req_t *)tw->tw_curr_xfer_reqp)->isoc_attributes
29817c478bd9Sstevel@tonic-gate 		    & USB_ATTRS_SHORT_XFER_OK) {
29827c478bd9Sstevel@tonic-gate 			SetTD_spd(uhcip, current_td, 1);
29837c478bd9Sstevel@tonic-gate 		}
29847c478bd9Sstevel@tonic-gate 		break;
29857c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
29867c478bd9Sstevel@tonic-gate 		if (((usb_bulk_req_t *)tw->tw_curr_xfer_reqp)->bulk_attributes
29877c478bd9Sstevel@tonic-gate 		    & USB_ATTRS_SHORT_XFER_OK) {
29887c478bd9Sstevel@tonic-gate 			SetTD_spd(uhcip, current_td, 1);
29897c478bd9Sstevel@tonic-gate 		}
29907c478bd9Sstevel@tonic-gate 		break;
29917c478bd9Sstevel@tonic-gate 	}
29927c478bd9Sstevel@tonic-gate 
29937c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_usba_device->usb_mutex);
29947c478bd9Sstevel@tonic-gate 
29957c478bd9Sstevel@tonic-gate 	SetTD_c_err(uhcip, current_td, UHCI_MAX_ERR_COUNT);
29967c478bd9Sstevel@tonic-gate 	SetTD_status(uhcip, current_td, UHCI_TD_ACTIVE);
29977c478bd9Sstevel@tonic-gate 	SetTD_ioc(uhcip, current_td, INTERRUPT_ON_COMPLETION);
2998688b07c5Sgc 	SetTD_mlen(uhcip, current_td,
2999fffe0b30Sqz 	    (length == 0) ? ZERO_LENGTH : (length - 1));
30007c478bd9Sstevel@tonic-gate 	SetTD_dtogg(uhcip, current_td, pp->pp_data_toggle);
30017c478bd9Sstevel@tonic-gate 	SetTD_devaddr(uhcip, current_td, ph->p_usba_device->usb_addr);
30027c478bd9Sstevel@tonic-gate 	SetTD_endpt(uhcip, current_td, ph->p_ep.bEndpointAddress &
3003fffe0b30Sqz 	    END_POINT_ADDRESS_MASK);
30047c478bd9Sstevel@tonic-gate 	SetTD_PID(uhcip, current_td, tw->tw_direction);
300502acac7eSsl 
300602acac7eSsl 	/* Get the right buffer address for the current TD */
300702acac7eSsl 	switch (UHCI_XFER_TYPE(ept)) {
300802acac7eSsl 	case USB_EP_ATTR_ISOCH:
300902acac7eSsl 		buf_addr = tw->tw_isoc_bufs[offset].cookie.dmac_address;
301002acac7eSsl 		break;
301102acac7eSsl 	case USB_EP_ATTR_BULK:
301202acac7eSsl 		buf_addr = uhci_get_tw_paddr_by_offs(uhcip, offset,
301302acac7eSsl 		    length, tw);
301402acac7eSsl 		break;
301502acac7eSsl 	}
301602acac7eSsl 	SetTD32(uhcip, current_td->buffer_address, buf_addr);
30177c478bd9Sstevel@tonic-gate 
30187c478bd9Sstevel@tonic-gate 	/*
30197c478bd9Sstevel@tonic-gate 	 * Adjust the data toggle.
30207c478bd9Sstevel@tonic-gate 	 * The data toggle bit must always be 0 for isoc transfers.
30217c478bd9Sstevel@tonic-gate 	 * And set the "iso" bit in the TD for isoc transfers.
30227c478bd9Sstevel@tonic-gate 	 */
30237c478bd9Sstevel@tonic-gate 	if (UHCI_XFER_TYPE(ept) == USB_EP_ATTR_ISOCH) {
30247c478bd9Sstevel@tonic-gate 		pp->pp_data_toggle = 0;
30257c478bd9Sstevel@tonic-gate 		SetTD_iso(uhcip, current_td, 1);
30267c478bd9Sstevel@tonic-gate 	} else {
30277c478bd9Sstevel@tonic-gate 		ADJ_DATA_TOGGLE(pp);
30287c478bd9Sstevel@tonic-gate 		next_td->qh_td_prev = current_td;
30297c478bd9Sstevel@tonic-gate 		pp->pp_qh->td_tailp = next_td;
30307c478bd9Sstevel@tonic-gate 	}
30317c478bd9Sstevel@tonic-gate 
30327c478bd9Sstevel@tonic-gate 	current_td->outst_td_next = NULL;
30337c478bd9Sstevel@tonic-gate 	current_td->outst_td_prev = uhcip->uhci_outst_tds_tail;
30347c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_outst_tds_head == NULL) {
30357c478bd9Sstevel@tonic-gate 		uhcip->uhci_outst_tds_head = current_td;
30367c478bd9Sstevel@tonic-gate 	} else {
30377c478bd9Sstevel@tonic-gate 		uhcip->uhci_outst_tds_tail->outst_td_next = current_td;
30387c478bd9Sstevel@tonic-gate 	}
30397c478bd9Sstevel@tonic-gate 	uhcip->uhci_outst_tds_tail = current_td;
30407c478bd9Sstevel@tonic-gate 	current_td->tw = tw;
30417c478bd9Sstevel@tonic-gate 
30427c478bd9Sstevel@tonic-gate 	if (tw->tw_hctd_head == NULL) {
30437c478bd9Sstevel@tonic-gate 		ASSERT(tw->tw_hctd_tail == NULL);
30447c478bd9Sstevel@tonic-gate 		tw->tw_hctd_head = current_td;
30457c478bd9Sstevel@tonic-gate 		tw->tw_hctd_tail = current_td;
30467c478bd9Sstevel@tonic-gate 	} else {
30477c478bd9Sstevel@tonic-gate 		/* Add the td to the end of the list */
30487c478bd9Sstevel@tonic-gate 		tw->tw_hctd_tail->tw_td_next = current_td;
30497c478bd9Sstevel@tonic-gate 		tw->tw_hctd_tail = current_td;
30507c478bd9Sstevel@tonic-gate 	}
30517c478bd9Sstevel@tonic-gate 
30527c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_usba_device->usb_mutex);
30537c478bd9Sstevel@tonic-gate }
30547c478bd9Sstevel@tonic-gate 
30557c478bd9Sstevel@tonic-gate 
305602acac7eSsl /*
305702acac7eSsl  * uhci_alloc_bulk_isoc_tds:
305802acac7eSsl  *	- Allocates the isoc/bulk TD pools. It will allocate one whole
305902acac7eSsl  *	  pool to store all the TDs if the system allows. Only when the
306002acac7eSsl  *	  first allocation fails, it tries to allocate several small
306102acac7eSsl  *	  pools with each pool limited in physical page size.
306202acac7eSsl  */
306302acac7eSsl static int
uhci_alloc_bulk_isoc_tds(uhci_state_t * uhcip,uint_t num_tds,uhci_bulk_isoc_xfer_t * info)306402acac7eSsl uhci_alloc_bulk_isoc_tds(
306502acac7eSsl 	uhci_state_t		*uhcip,
306602acac7eSsl 	uint_t			num_tds,
306702acac7eSsl 	uhci_bulk_isoc_xfer_t	*info)
306802acac7eSsl {
306902acac7eSsl 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
307002acac7eSsl 	    "uhci_alloc_bulk_isoc_tds: num_tds: 0x%x info: 0x%p",
3071112116d8Sfb 	    num_tds, (void *)info);
307202acac7eSsl 
307302acac7eSsl 	info->num_pools = 1;
307402acac7eSsl 	/* allocate as a whole pool at the first time */
307502acac7eSsl 	if (uhci_alloc_memory_for_tds(uhcip, num_tds, info) !=
307602acac7eSsl 	    USB_SUCCESS) {
307702acac7eSsl 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
307802acac7eSsl 		    "alloc_memory_for_tds failed: num_tds %d num_pools %d",
307902acac7eSsl 		    num_tds, info->num_pools);
308002acac7eSsl 
308102acac7eSsl 		/* reduce the td number per pool and alloc again */
308202acac7eSsl 		info->num_pools = num_tds / UHCI_MAX_TD_NUM_PER_POOL;
308302acac7eSsl 		if (num_tds % UHCI_MAX_TD_NUM_PER_POOL) {
308402acac7eSsl 			info->num_pools++;
308502acac7eSsl 		}
308602acac7eSsl 
308702acac7eSsl 		if (uhci_alloc_memory_for_tds(uhcip, num_tds, info) !=
308802acac7eSsl 		    USB_SUCCESS) {
308902acac7eSsl 			USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
309002acac7eSsl 			    "alloc_memory_for_tds failed: num_tds %d "
309102acac7eSsl 			    "num_pools %d", num_tds, info->num_pools);
309202acac7eSsl 
309302acac7eSsl 			return (USB_NO_RESOURCES);
309402acac7eSsl 		}
309502acac7eSsl 	}
309602acac7eSsl 
309702acac7eSsl 	return (USB_SUCCESS);
309802acac7eSsl }
309902acac7eSsl 
310002acac7eSsl 
31017c478bd9Sstevel@tonic-gate /*
31027c478bd9Sstevel@tonic-gate  * uhci_alloc_memory_for_tds:
31037c478bd9Sstevel@tonic-gate  *	- Allocates memory for the isoc/bulk td pools.
31047c478bd9Sstevel@tonic-gate  */
31057c478bd9Sstevel@tonic-gate static int
uhci_alloc_memory_for_tds(uhci_state_t * uhcip,uint_t num_tds,uhci_bulk_isoc_xfer_t * info)31067c478bd9Sstevel@tonic-gate uhci_alloc_memory_for_tds(
31077c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
31087c478bd9Sstevel@tonic-gate 	uint_t			num_tds,
31097c478bd9Sstevel@tonic-gate 	uhci_bulk_isoc_xfer_t	*info)
31107c478bd9Sstevel@tonic-gate {
311102acac7eSsl 	int			result, i, j, err;
31127c478bd9Sstevel@tonic-gate 	size_t			real_length;
311302acac7eSsl 	uint_t			ccount, num;
31147c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t	dev_attr;
311502acac7eSsl 	uhci_bulk_isoc_td_pool_t *td_pool_ptr1, *td_pool_ptr2;
31167c478bd9Sstevel@tonic-gate 
31177c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
311802acac7eSsl 	    "uhci_alloc_memory_for_tds: num_tds: 0x%x info: 0x%p "
3119112116d8Sfb 	    "num_pools: %u", num_tds, (void *)info, info->num_pools);
31207c478bd9Sstevel@tonic-gate 
31217c478bd9Sstevel@tonic-gate 	/* The host controller will be little endian */
31227c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_version		= DDI_DEVICE_ATTR_V0;
31237c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_endian_flags	= DDI_STRUCTURE_LE_ACC;
31247c478bd9Sstevel@tonic-gate 	dev_attr.devacc_attr_dataorder		= DDI_STRICTORDER_ACC;
31257c478bd9Sstevel@tonic-gate 
312602acac7eSsl 	/* Allocate the TD pool structures */
312702acac7eSsl 	if ((info->td_pools = kmem_zalloc(
312802acac7eSsl 	    (sizeof (uhci_bulk_isoc_td_pool_t) * info->num_pools),
312902acac7eSsl 	    KM_SLEEP)) == NULL) {
313002acac7eSsl 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
313102acac7eSsl 		    "uhci_alloc_memory_for_tds: alloc td_pools failed");
31327c478bd9Sstevel@tonic-gate 
31337c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
31347c478bd9Sstevel@tonic-gate 	}
31357c478bd9Sstevel@tonic-gate 
313602acac7eSsl 	for (i = 0; i < info->num_pools; i++) {
313702acac7eSsl 		if (info->num_pools == 1) {
313802acac7eSsl 			num = num_tds;
313902acac7eSsl 		} else if (i < (info->num_pools - 1)) {
314002acac7eSsl 			num = UHCI_MAX_TD_NUM_PER_POOL;
314102acac7eSsl 		} else {
314202acac7eSsl 			num = (num_tds % UHCI_MAX_TD_NUM_PER_POOL);
314302acac7eSsl 		}
31447c478bd9Sstevel@tonic-gate 
314502acac7eSsl 		td_pool_ptr1 = &info->td_pools[i];
31467c478bd9Sstevel@tonic-gate 
314702acac7eSsl 		/* Allocate the bulk TD pool DMA handle */
314802acac7eSsl 		if (ddi_dma_alloc_handle(uhcip->uhci_dip,
314902acac7eSsl 		    &uhcip->uhci_dma_attr, DDI_DMA_SLEEP, 0,
315002acac7eSsl 		    &td_pool_ptr1->dma_handle) != DDI_SUCCESS) {
31517c478bd9Sstevel@tonic-gate 
315202acac7eSsl 			for (j = 0; j < i; j++) {
315302acac7eSsl 				td_pool_ptr2 = &info->td_pools[j];
315402acac7eSsl 				result = ddi_dma_unbind_handle(
315502acac7eSsl 				    td_pool_ptr2->dma_handle);
315602acac7eSsl 				ASSERT(result == DDI_SUCCESS);
315702acac7eSsl 				ddi_dma_mem_free(&td_pool_ptr2->mem_handle);
315802acac7eSsl 				ddi_dma_free_handle(&td_pool_ptr2->dma_handle);
315902acac7eSsl 			}
316002acac7eSsl 
316102acac7eSsl 			kmem_free(info->td_pools,
316202acac7eSsl 			    (sizeof (uhci_bulk_isoc_td_pool_t) *
316302acac7eSsl 			    info->num_pools));
316402acac7eSsl 
316502acac7eSsl 			return (USB_FAILURE);
316602acac7eSsl 		}
316702acac7eSsl 
316802acac7eSsl 		/* Allocate the memory for the bulk TD pool */
316902acac7eSsl 		if (ddi_dma_mem_alloc(td_pool_ptr1->dma_handle,
317002acac7eSsl 		    num * sizeof (uhci_td_t), &dev_attr,
317102acac7eSsl 		    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
317202acac7eSsl 		    &td_pool_ptr1->pool_addr, &real_length,
317302acac7eSsl 		    &td_pool_ptr1->mem_handle) != DDI_SUCCESS) {
317402acac7eSsl 
317502acac7eSsl 			ddi_dma_free_handle(&td_pool_ptr1->dma_handle);
317602acac7eSsl 
317702acac7eSsl 			for (j = 0; j < i; j++) {
317802acac7eSsl 				td_pool_ptr2 = &info->td_pools[j];
317902acac7eSsl 				result = ddi_dma_unbind_handle(
318002acac7eSsl 				    td_pool_ptr2->dma_handle);
318102acac7eSsl 				ASSERT(result == DDI_SUCCESS);
318202acac7eSsl 				ddi_dma_mem_free(&td_pool_ptr2->mem_handle);
318302acac7eSsl 				ddi_dma_free_handle(&td_pool_ptr2->dma_handle);
318402acac7eSsl 			}
318502acac7eSsl 
318602acac7eSsl 			kmem_free(info->td_pools,
318702acac7eSsl 			    (sizeof (uhci_bulk_isoc_td_pool_t) *
318802acac7eSsl 			    info->num_pools));
318902acac7eSsl 
319002acac7eSsl 			return (USB_FAILURE);
319102acac7eSsl 		}
319202acac7eSsl 
319302acac7eSsl 		/* Map the bulk TD pool into the I/O address space */
319402acac7eSsl 		result = ddi_dma_addr_bind_handle(td_pool_ptr1->dma_handle,
319502acac7eSsl 		    NULL, (caddr_t)td_pool_ptr1->pool_addr, real_length,
319602acac7eSsl 		    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
319702acac7eSsl 		    &td_pool_ptr1->cookie, &ccount);
319802acac7eSsl 
319902acac7eSsl 		/* Process the result */
320002acac7eSsl 		err = USB_SUCCESS;
320102acac7eSsl 
320202acac7eSsl 		if (result != DDI_DMA_MAPPED) {
32037c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
320402acac7eSsl 			    "uhci_allocate_memory_for_tds: Result = %d",
320502acac7eSsl 			    result);
320602acac7eSsl 			uhci_decode_ddi_dma_addr_bind_handle_result(uhcip,
320702acac7eSsl 			    result);
320802acac7eSsl 
320902acac7eSsl 			err = USB_FAILURE;
321002acac7eSsl 		}
321102acac7eSsl 
321202acac7eSsl 		if ((result == DDI_DMA_MAPPED) && (ccount != 1)) {
321302acac7eSsl 			/* The cookie count should be 1 */
321402acac7eSsl 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
321502acac7eSsl 			    uhcip->uhci_log_hdl,
321602acac7eSsl 			    "uhci_allocate_memory_for_tds: "
321702acac7eSsl 			    "More than 1 cookie");
321802acac7eSsl 
321902acac7eSsl 			result = ddi_dma_unbind_handle(
322002acac7eSsl 			    td_pool_ptr1->dma_handle);
322102acac7eSsl 			ASSERT(result == DDI_SUCCESS);
322202acac7eSsl 
322302acac7eSsl 			err = USB_FAILURE;
322402acac7eSsl 		}
322502acac7eSsl 
322602acac7eSsl 		if (err == USB_FAILURE) {
322702acac7eSsl 
322802acac7eSsl 			ddi_dma_mem_free(&td_pool_ptr1->mem_handle);
322902acac7eSsl 			ddi_dma_free_handle(&td_pool_ptr1->dma_handle);
323002acac7eSsl 
323102acac7eSsl 			for (j = 0; j < i; j++) {
323202acac7eSsl 				td_pool_ptr2 = &info->td_pools[j];
323302acac7eSsl 				result = ddi_dma_unbind_handle(
323402acac7eSsl 				    td_pool_ptr2->dma_handle);
323502acac7eSsl 				ASSERT(result == DDI_SUCCESS);
323602acac7eSsl 				ddi_dma_mem_free(&td_pool_ptr2->mem_handle);
323702acac7eSsl 				ddi_dma_free_handle(&td_pool_ptr2->dma_handle);
323802acac7eSsl 			}
323902acac7eSsl 
324002acac7eSsl 			kmem_free(info->td_pools,
324102acac7eSsl 			    (sizeof (uhci_bulk_isoc_td_pool_t) *
324202acac7eSsl 			    info->num_pools));
32437c478bd9Sstevel@tonic-gate 
32447c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
32457c478bd9Sstevel@tonic-gate 		}
32467c478bd9Sstevel@tonic-gate 
324702acac7eSsl 		bzero((void *)td_pool_ptr1->pool_addr,
324802acac7eSsl 		    num * sizeof (uhci_td_t));
3249d29f5a71Szhigang lu - Sun Microsystems - Beijing China 		td_pool_ptr1->num_tds = (ushort_t)num;
32507c478bd9Sstevel@tonic-gate 	}
32517c478bd9Sstevel@tonic-gate 
32527c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
32537c478bd9Sstevel@tonic-gate }
32547c478bd9Sstevel@tonic-gate 
32557c478bd9Sstevel@tonic-gate 
32567c478bd9Sstevel@tonic-gate /*
32577c478bd9Sstevel@tonic-gate  * uhci_handle_bulk_td:
32587c478bd9Sstevel@tonic-gate  *
32597c478bd9Sstevel@tonic-gate  *	Handles the completed bulk transfer descriptors
32607c478bd9Sstevel@tonic-gate  */
32617c478bd9Sstevel@tonic-gate void
uhci_handle_bulk_td(uhci_state_t * uhcip,uhci_td_t * td)32627c478bd9Sstevel@tonic-gate uhci_handle_bulk_td(uhci_state_t *uhcip, uhci_td_t *td)
32637c478bd9Sstevel@tonic-gate {
326402acac7eSsl 	uint_t			num_bulk_tds, index, td_count, j;
32657c478bd9Sstevel@tonic-gate 	usb_cr_t		error;
32667c478bd9Sstevel@tonic-gate 	uint_t			length, bytes_xfered;
32677c478bd9Sstevel@tonic-gate 	ushort_t		MaxPacketSize;
326802acac7eSsl 	uint32_t		buf_offs, paddr;
32697c478bd9Sstevel@tonic-gate 	uhci_td_t		*bulk_td_ptr, *current_dummy, *td_head;
327002acac7eSsl 	uhci_td_t		*tmp_td;
32717c478bd9Sstevel@tonic-gate 	queue_head_t		*qh, *next_qh;
32727c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw = td->tw;
32737c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp = tw->tw_pipe_private;
32747c478bd9Sstevel@tonic-gate 	uhci_bulk_isoc_xfer_t	*bulk_xfer_info;
327502acac7eSsl 	uhci_bulk_isoc_td_pool_t *td_pool_ptr;
32767c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
32777c478bd9Sstevel@tonic-gate 
32787c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
3279112116d8Sfb 	    "uhci_handle_bulk_td: td = 0x%p tw = 0x%p", (void *)td, (void *)tw);
32807c478bd9Sstevel@tonic-gate 
32817c478bd9Sstevel@tonic-gate 	/*
32827c478bd9Sstevel@tonic-gate 	 * Update the tw_bytes_pending, and tw_bytes_xfered
32837c478bd9Sstevel@tonic-gate 	 */
32847c478bd9Sstevel@tonic-gate 	bytes_xfered = ZERO_LENGTH;
32857c478bd9Sstevel@tonic-gate 
32867c478bd9Sstevel@tonic-gate 	/*
32877c478bd9Sstevel@tonic-gate 	 * Check whether there are any errors occurred in the xfer.
32887c478bd9Sstevel@tonic-gate 	 * If so, update the data_toggle for the queue head and
32897c478bd9Sstevel@tonic-gate 	 * return error to the upper layer.
32907c478bd9Sstevel@tonic-gate 	 */
32917c478bd9Sstevel@tonic-gate 	if (GetTD_status(uhcip, td) & TD_STATUS_MASK) {
32927c478bd9Sstevel@tonic-gate 		uhci_handle_bulk_td_errors(uhcip, td);
32937c478bd9Sstevel@tonic-gate 
32947c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
32957c478bd9Sstevel@tonic-gate 		    "uhci_handle_bulk_td: error; data toggle: 0x%x",
32967c478bd9Sstevel@tonic-gate 		    pp->pp_data_toggle);
32977c478bd9Sstevel@tonic-gate 
32987c478bd9Sstevel@tonic-gate 		return;
32997c478bd9Sstevel@tonic-gate 	}
33007c478bd9Sstevel@tonic-gate 
33017c478bd9Sstevel@tonic-gate 	/*
33027c478bd9Sstevel@tonic-gate 	 * Update the tw_bytes_pending, and tw_bytes_xfered
33037c478bd9Sstevel@tonic-gate 	 */
33047c478bd9Sstevel@tonic-gate 	bytes_xfered = GetTD_alen(uhcip, td);
33057c478bd9Sstevel@tonic-gate 	if (bytes_xfered != ZERO_LENGTH) {
33067c478bd9Sstevel@tonic-gate 		tw->tw_bytes_pending -= (bytes_xfered + 1);
33077c478bd9Sstevel@tonic-gate 		tw->tw_bytes_xfered  += (bytes_xfered + 1);
33087c478bd9Sstevel@tonic-gate 	}
33097c478bd9Sstevel@tonic-gate 
33107c478bd9Sstevel@tonic-gate 	/*
33117c478bd9Sstevel@tonic-gate 	 * Get Bulk pipe information and pipe handle
33127c478bd9Sstevel@tonic-gate 	 */
33137c478bd9Sstevel@tonic-gate 	bulk_xfer_info	= pp->pp_qh->bulk_xfer_info;
33147c478bd9Sstevel@tonic-gate 	ph = tw->tw_pipe_private->pp_pipe_handle;
33157c478bd9Sstevel@tonic-gate 
33167c478bd9Sstevel@tonic-gate 	/*
33177c478bd9Sstevel@tonic-gate 	 * Check whether data underrun occurred.
33187c478bd9Sstevel@tonic-gate 	 * If so, complete the transfer
33197c478bd9Sstevel@tonic-gate 	 * Update the data toggle bit
33207c478bd9Sstevel@tonic-gate 	 */
33217c478bd9Sstevel@tonic-gate 	if (bytes_xfered != GetTD_mlen(uhcip, td)) {
33227c478bd9Sstevel@tonic-gate 		bulk_xfer_info->num_tds = 1;
33237c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
33247c478bd9Sstevel@tonic-gate 		    "uhci_handle_bulk_td: Data underrun occured");
33257c478bd9Sstevel@tonic-gate 
33267c478bd9Sstevel@tonic-gate 		pp->pp_data_toggle = GetTD_dtogg(uhcip, td) == 0 ? 1 : 0;
33277c478bd9Sstevel@tonic-gate 	}
33287c478bd9Sstevel@tonic-gate 
33297c478bd9Sstevel@tonic-gate 	/*
33307c478bd9Sstevel@tonic-gate 	 * If the TD's in the current frame are completed, then check
33317c478bd9Sstevel@tonic-gate 	 * whether we have any more bytes to xfer. If so, insert TD's.
33327c478bd9Sstevel@tonic-gate 	 * If no more bytes needs to be transferred, then do callback to the
33337c478bd9Sstevel@tonic-gate 	 * upper layer.
33347c478bd9Sstevel@tonic-gate 	 * If the TD's in the current frame are not completed, then
33357c478bd9Sstevel@tonic-gate 	 * just delete the TD from the linked lists.
33367c478bd9Sstevel@tonic-gate 	 */
33377c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
33387c478bd9Sstevel@tonic-gate 	    "uhci_handle_bulk_td: completed TD data toggle: 0x%x",
33397c478bd9Sstevel@tonic-gate 	    GetTD_dtogg(uhcip, td));
33407c478bd9Sstevel@tonic-gate 
33417c478bd9Sstevel@tonic-gate 	if (--bulk_xfer_info->num_tds == 0) {
33427c478bd9Sstevel@tonic-gate 		uhci_delete_td(uhcip, td);
33437c478bd9Sstevel@tonic-gate 
33447c478bd9Sstevel@tonic-gate 		if ((tw->tw_bytes_pending) &&
33457c478bd9Sstevel@tonic-gate 		    (GetTD_mlen(uhcip, td) - GetTD_alen(uhcip, td) == 0)) {
33467c478bd9Sstevel@tonic-gate 
33477c478bd9Sstevel@tonic-gate 			MaxPacketSize = pp->pp_pipe_handle->p_ep.wMaxPacketSize;
33487c478bd9Sstevel@tonic-gate 			length = MaxPacketSize;
33497c478bd9Sstevel@tonic-gate 
33507c478bd9Sstevel@tonic-gate 			qh = pp->pp_qh;
33517c478bd9Sstevel@tonic-gate 			paddr = GetQH32(uhcip, qh->link_ptr) & QH_LINK_PTR_MASK;
33527c478bd9Sstevel@tonic-gate 			if (GetQH32(uhcip, qh->link_ptr) !=
33537c478bd9Sstevel@tonic-gate 			    GetQH32(uhcip,
3354fffe0b30Sqz 			    uhcip->uhci_bulk_xfers_q_head->link_ptr)) {
33557c478bd9Sstevel@tonic-gate 				next_qh = QH_VADDR(paddr);
33567c478bd9Sstevel@tonic-gate 				SetQH32(uhcip, qh->prev_qh->link_ptr,
33577c478bd9Sstevel@tonic-gate 				    paddr|(0x2));
33587c478bd9Sstevel@tonic-gate 				next_qh->prev_qh = qh->prev_qh;
33597c478bd9Sstevel@tonic-gate 				SetQH32(uhcip, qh->link_ptr,
33607c478bd9Sstevel@tonic-gate 				    GetQH32(uhcip,
33617c478bd9Sstevel@tonic-gate 				    uhcip->uhci_bulk_xfers_q_head->link_ptr));
33627c478bd9Sstevel@tonic-gate 				qh->prev_qh = uhcip->uhci_bulk_xfers_q_tail;
33637c478bd9Sstevel@tonic-gate 				SetQH32(uhcip,
33647c478bd9Sstevel@tonic-gate 				    uhcip->uhci_bulk_xfers_q_tail->link_ptr,
33657c478bd9Sstevel@tonic-gate 				    QH_PADDR(qh) | 0x2);
33667c478bd9Sstevel@tonic-gate 				uhcip->uhci_bulk_xfers_q_tail = qh;
33677c478bd9Sstevel@tonic-gate 			}
33687c478bd9Sstevel@tonic-gate 
33697c478bd9Sstevel@tonic-gate 			if ((tw->tw_bytes_pending / MaxPacketSize) >=
33707c478bd9Sstevel@tonic-gate 			    MAX_NUM_BULK_TDS_PER_XFER) {
33717c478bd9Sstevel@tonic-gate 				num_bulk_tds = MAX_NUM_BULK_TDS_PER_XFER;
33727c478bd9Sstevel@tonic-gate 			} else {
33737c478bd9Sstevel@tonic-gate 				num_bulk_tds =
3374fffe0b30Sqz 				    (tw->tw_bytes_pending / MaxPacketSize);
33757c478bd9Sstevel@tonic-gate 				if (tw->tw_bytes_pending % MaxPacketSize) {
33767c478bd9Sstevel@tonic-gate 					num_bulk_tds++;
33777c478bd9Sstevel@tonic-gate 					length = (tw->tw_bytes_pending %
3378fffe0b30Sqz 					    MaxPacketSize);
33797c478bd9Sstevel@tonic-gate 				}
33807c478bd9Sstevel@tonic-gate 			}
33817c478bd9Sstevel@tonic-gate 
33827c478bd9Sstevel@tonic-gate 			current_dummy = pp->pp_qh->td_tailp;
338302acac7eSsl 			td_pool_ptr = &bulk_xfer_info->td_pools[0];
338402acac7eSsl 			bulk_td_ptr = (uhci_td_t *)td_pool_ptr->pool_addr;
338502acac7eSsl 			buf_offs = tw->tw_bytes_xfered;
338602acac7eSsl 			td_count = num_bulk_tds;
338702acac7eSsl 			index = 0;
338802acac7eSsl 
338902acac7eSsl 			/* reuse the TDs to transfer more data */
339002acac7eSsl 			while (td_count > 0) {
339102acac7eSsl 				for (j = 0;
339202acac7eSsl 				    (j < (td_pool_ptr->num_tds - 1)) &&
339302acac7eSsl 				    (td_count > 1); j++, td_count--) {
339402acac7eSsl 					uhci_fill_in_bulk_isoc_td(uhcip,
339502acac7eSsl 					    &bulk_td_ptr[j], &bulk_td_ptr[j+1],
339602acac7eSsl 					    BULKTD_PADDR(td_pool_ptr,
339702acac7eSsl 					    &bulk_td_ptr[j+1]), ph, buf_offs,
339802acac7eSsl 					    MaxPacketSize, tw);
339902acac7eSsl 					buf_offs += MaxPacketSize;
340002acac7eSsl 				}
34017c478bd9Sstevel@tonic-gate 
340202acac7eSsl 				if (td_count == 1) {
340302acac7eSsl 					uhci_fill_in_bulk_isoc_td(uhcip,
340402acac7eSsl 					    &bulk_td_ptr[j], current_dummy,
340502acac7eSsl 					    TD_PADDR(current_dummy), ph,
340602acac7eSsl 					    buf_offs, length, tw);
340702acac7eSsl 
340802acac7eSsl 					break;
340902acac7eSsl 				} else {
341002acac7eSsl 					tmp_td = &bulk_td_ptr[j];
341102acac7eSsl 					ASSERT(index <
341202acac7eSsl 					    (bulk_xfer_info->num_pools - 1));
341302acac7eSsl 					td_pool_ptr = &bulk_xfer_info->
341402acac7eSsl 					    td_pools[index + 1];
341502acac7eSsl 					bulk_td_ptr = (uhci_td_t *)
341602acac7eSsl 					    td_pool_ptr->pool_addr;
341702acac7eSsl 					uhci_fill_in_bulk_isoc_td(uhcip,
341802acac7eSsl 					    tmp_td, &bulk_td_ptr[0],
341902acac7eSsl 					    BULKTD_PADDR(td_pool_ptr,
342002acac7eSsl 					    &bulk_td_ptr[0]), ph, buf_offs,
342102acac7eSsl 					    MaxPacketSize, tw);
342202acac7eSsl 					buf_offs += MaxPacketSize;
342302acac7eSsl 					td_count--;
342402acac7eSsl 					index++;
342502acac7eSsl 				}
342602acac7eSsl 			}
34277c478bd9Sstevel@tonic-gate 
34287c478bd9Sstevel@tonic-gate 			pp->pp_qh->bulk_xfer_info = bulk_xfer_info;
3429d29f5a71Szhigang lu - Sun Microsystems - Beijing China 			bulk_xfer_info->num_tds	= (ushort_t)num_bulk_tds;
34307c478bd9Sstevel@tonic-gate 			SetQH32(uhcip, pp->pp_qh->element_ptr,
343102acac7eSsl 			    bulk_xfer_info->td_pools[0].cookie.dmac_address);
34327c478bd9Sstevel@tonic-gate 		} else {
34337c478bd9Sstevel@tonic-gate 			usba_pipe_handle_data_t *usb_pp = pp->pp_pipe_handle;
34347c478bd9Sstevel@tonic-gate 
34357c478bd9Sstevel@tonic-gate 			pp->pp_qh->bulk_xfer_info = NULL;
34367c478bd9Sstevel@tonic-gate 
34377c478bd9Sstevel@tonic-gate 			if (tw->tw_bytes_pending) {
34387c478bd9Sstevel@tonic-gate 				/* Update the element pointer */
34397c478bd9Sstevel@tonic-gate 				SetQH32(uhcip, pp->pp_qh->element_ptr,
3440fffe0b30Sqz 				    TD_PADDR(pp->pp_qh->td_tailp));
34417c478bd9Sstevel@tonic-gate 
34427c478bd9Sstevel@tonic-gate 				/* Remove all the tds */
34437c478bd9Sstevel@tonic-gate 				td_head = tw->tw_hctd_head;
34447c478bd9Sstevel@tonic-gate 				while (td_head != NULL) {
34457c478bd9Sstevel@tonic-gate 					uhci_delete_td(uhcip, td_head);
34467c478bd9Sstevel@tonic-gate 					td_head = tw->tw_hctd_head;
34477c478bd9Sstevel@tonic-gate 				}
34487c478bd9Sstevel@tonic-gate 			}
34497c478bd9Sstevel@tonic-gate 
34507c478bd9Sstevel@tonic-gate 			if (tw->tw_direction == PID_IN) {
34517c478bd9Sstevel@tonic-gate 				usb_req_attrs_t	attrs = ((usb_bulk_req_t *)
3452fffe0b30Sqz 				    tw->tw_curr_xfer_reqp)->bulk_attributes;
34537c478bd9Sstevel@tonic-gate 
34547c478bd9Sstevel@tonic-gate 				error = USB_CR_OK;
34557c478bd9Sstevel@tonic-gate 
34567c478bd9Sstevel@tonic-gate 				/* Data run occurred */
34577c478bd9Sstevel@tonic-gate 				if (tw->tw_bytes_pending &&
34587c478bd9Sstevel@tonic-gate 				    (!(attrs & USB_ATTRS_SHORT_XFER_OK))) {
34597c478bd9Sstevel@tonic-gate 					error = USB_CR_DATA_UNDERRUN;
34607c478bd9Sstevel@tonic-gate 				}
34617c478bd9Sstevel@tonic-gate 
34627c478bd9Sstevel@tonic-gate 				uhci_sendup_td_message(uhcip, error, tw);
34637c478bd9Sstevel@tonic-gate 			} else {
34647c478bd9Sstevel@tonic-gate 				uhci_do_byte_stats(uhcip, tw->tw_length,
34657c478bd9Sstevel@tonic-gate 				    usb_pp->p_ep.bmAttributes,
34667c478bd9Sstevel@tonic-gate 				    usb_pp->p_ep.bEndpointAddress);
34677c478bd9Sstevel@tonic-gate 
34687c478bd9Sstevel@tonic-gate 				/* Data underrun occurred */
34697c478bd9Sstevel@tonic-gate 				if (tw->tw_bytes_pending) {
34707c478bd9Sstevel@tonic-gate 
34717c478bd9Sstevel@tonic-gate 					tw->tw_data->b_rptr +=
3472fffe0b30Sqz 					    tw->tw_bytes_xfered;
34737c478bd9Sstevel@tonic-gate 
34747c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(PRINT_MASK_ATTA,
34757c478bd9Sstevel@tonic-gate 					    uhcip->uhci_log_hdl,
34767c478bd9Sstevel@tonic-gate 					    "uhci_handle_bulk_td: "
34777c478bd9Sstevel@tonic-gate 					    "data underrun occurred");
34787c478bd9Sstevel@tonic-gate 
34797c478bd9Sstevel@tonic-gate 					uhci_hcdi_callback(uhcip, pp,
34807c478bd9Sstevel@tonic-gate 					    tw->tw_pipe_private->pp_pipe_handle,
34817c478bd9Sstevel@tonic-gate 					    tw, USB_CR_DATA_UNDERRUN);
34827c478bd9Sstevel@tonic-gate 				} else {
34837c478bd9Sstevel@tonic-gate 					uhci_hcdi_callback(uhcip, pp,
34847c478bd9Sstevel@tonic-gate 					    tw->tw_pipe_private->pp_pipe_handle,
34857c478bd9Sstevel@tonic-gate 					    tw, USB_CR_OK);
34867c478bd9Sstevel@tonic-gate 				}
34877c478bd9Sstevel@tonic-gate 			} /* direction */
34887c478bd9Sstevel@tonic-gate 
34897c478bd9Sstevel@tonic-gate 			/* Deallocate DMA memory */
34907c478bd9Sstevel@tonic-gate 			uhci_deallocate_tw(uhcip, pp, tw);
349102acac7eSsl 			for (j = 0; j < bulk_xfer_info->num_pools; j++) {
349202acac7eSsl 				td_pool_ptr = &bulk_xfer_info->td_pools[j];
349302acac7eSsl 				(void) ddi_dma_unbind_handle(
349402acac7eSsl 				    td_pool_ptr->dma_handle);
349502acac7eSsl 				ddi_dma_mem_free(&td_pool_ptr->mem_handle);
349602acac7eSsl 				ddi_dma_free_handle(&td_pool_ptr->dma_handle);
349702acac7eSsl 			}
349802acac7eSsl 			kmem_free(bulk_xfer_info->td_pools,
349902acac7eSsl 			    (sizeof (uhci_bulk_isoc_td_pool_t) *
350002acac7eSsl 			    bulk_xfer_info->num_pools));
35017c478bd9Sstevel@tonic-gate 			kmem_free(bulk_xfer_info,
35027c478bd9Sstevel@tonic-gate 			    sizeof (uhci_bulk_isoc_xfer_t));
35037c478bd9Sstevel@tonic-gate 
35047c478bd9Sstevel@tonic-gate 			/*
35057c478bd9Sstevel@tonic-gate 			 * When there are no pending bulk commands, point the
35067c478bd9Sstevel@tonic-gate 			 * end of the lattice tree to NULL. This will make sure
35077c478bd9Sstevel@tonic-gate 			 * that the HC control does not loop anymore and PCI
35087c478bd9Sstevel@tonic-gate 			 * bus is not affected.
35097c478bd9Sstevel@tonic-gate 			 */
35107c478bd9Sstevel@tonic-gate 			if (--uhcip->uhci_pending_bulk_cmds == 0) {
35117c478bd9Sstevel@tonic-gate 				uhcip->uhci_bulk_xfers_q_tail->link_ptr =
35127c478bd9Sstevel@tonic-gate 				    HC_END_OF_LIST;
35137c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L3(PRINT_MASK_ATTA,
35147c478bd9Sstevel@tonic-gate 				    uhcip->uhci_log_hdl,
35157c478bd9Sstevel@tonic-gate 				    "uhci_handle_bulk_td: count = %d",
35167c478bd9Sstevel@tonic-gate 				    uhcip->uhci_pending_bulk_cmds);
35177c478bd9Sstevel@tonic-gate 			}
35187c478bd9Sstevel@tonic-gate 		}
35197c478bd9Sstevel@tonic-gate 	} else {
35207c478bd9Sstevel@tonic-gate 		uhci_delete_td(uhcip, td);
35217c478bd9Sstevel@tonic-gate 	}
35227c478bd9Sstevel@tonic-gate }
35237c478bd9Sstevel@tonic-gate 
35247c478bd9Sstevel@tonic-gate 
35257c478bd9Sstevel@tonic-gate void
uhci_handle_bulk_td_errors(uhci_state_t * uhcip,uhci_td_t * td)35267c478bd9Sstevel@tonic-gate uhci_handle_bulk_td_errors(uhci_state_t *uhcip, uhci_td_t *td)
35277c478bd9Sstevel@tonic-gate {
35287c478bd9Sstevel@tonic-gate 	usb_cr_t		usb_err;
352902acac7eSsl 	uint32_t		paddr_tail, element_ptr, paddr;
35307c478bd9Sstevel@tonic-gate 	uhci_td_t		*next_td;
35317c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp;
35327c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw = td->tw;
35337c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
353402acac7eSsl 	uhci_bulk_isoc_td_pool_t *td_pool_ptr = NULL;
35357c478bd9Sstevel@tonic-gate 
35367c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
35377c478bd9Sstevel@tonic-gate 	    "uhci_handle_bulk_td_errors: td = %p", (void *)td);
35387c478bd9Sstevel@tonic-gate 
35397c478bd9Sstevel@tonic-gate #ifdef	DEBUG
35407c478bd9Sstevel@tonic-gate 	uhci_print_td(uhcip, td);
35417c478bd9Sstevel@tonic-gate #endif
35427c478bd9Sstevel@tonic-gate 
35437c478bd9Sstevel@tonic-gate 	tw = td->tw;
35447c478bd9Sstevel@tonic-gate 	ph = tw->tw_pipe_private->pp_pipe_handle;
35457c478bd9Sstevel@tonic-gate 	pp = (uhci_pipe_private_t *)ph->p_hcd_private;
35467c478bd9Sstevel@tonic-gate 
35477c478bd9Sstevel@tonic-gate 	/*
35487c478bd9Sstevel@tonic-gate 	 * Find the type of error occurred and return the error
35497c478bd9Sstevel@tonic-gate 	 * to the upper layer. And adjust the data toggle.
35507c478bd9Sstevel@tonic-gate 	 */
35517c478bd9Sstevel@tonic-gate 	element_ptr = GetQH32(uhcip, pp->pp_qh->element_ptr) &
35527c478bd9Sstevel@tonic-gate 	    QH_ELEMENT_PTR_MASK;
35537c478bd9Sstevel@tonic-gate 	paddr_tail = TD_PADDR(pp->pp_qh->td_tailp);
35547c478bd9Sstevel@tonic-gate 
35557c478bd9Sstevel@tonic-gate 	/*
35567c478bd9Sstevel@tonic-gate 	 * If a timeout occurs before a transfer has completed,
35577c478bd9Sstevel@tonic-gate 	 * the timeout handler sets the CRC/Timeout bit and clears the Active
35587c478bd9Sstevel@tonic-gate 	 * bit in the link_ptr for each td in the transfer.
35597c478bd9Sstevel@tonic-gate 	 * It then waits (at least) 1 ms so that any tds the controller might
35607c478bd9Sstevel@tonic-gate 	 * have been executing will have completed.
35617c478bd9Sstevel@tonic-gate 	 * So at this point element_ptr will point to either:
35627c478bd9Sstevel@tonic-gate 	 * 1) the next td for the transfer (which has not been executed,
35637c478bd9Sstevel@tonic-gate 	 * and has the CRC/Timeout status bit set and Active bit cleared),
35647c478bd9Sstevel@tonic-gate 	 * 2) the dummy td for this qh.
35657c478bd9Sstevel@tonic-gate 	 * So if the element_ptr does not point to the dummy td, we know
35667c478bd9Sstevel@tonic-gate 	 * it points to the next td that would have been executed.
35677c478bd9Sstevel@tonic-gate 	 * That td has the data toggle we want to save.
35687c478bd9Sstevel@tonic-gate 	 * All outstanding tds have been marked as CRC/Timeout,
35697c478bd9Sstevel@tonic-gate 	 * so it doesn't matter which td we pass to uhci_parse_td_error
35707c478bd9Sstevel@tonic-gate 	 * for the error status.
35717c478bd9Sstevel@tonic-gate 	 */
35727c478bd9Sstevel@tonic-gate 	if (element_ptr != paddr_tail) {
357302acac7eSsl 		paddr = (element_ptr & QH_ELEMENT_PTR_MASK);
357402acac7eSsl 		uhci_get_bulk_td_by_paddr(uhcip, pp->pp_qh->bulk_xfer_info,
357502acac7eSsl 		    paddr, &td_pool_ptr);
357602acac7eSsl 		next_td = BULKTD_VADDR(td_pool_ptr, paddr);
35777c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
35787c478bd9Sstevel@tonic-gate 		    "uhci_handle_bulk_td_errors: next td = %p",
35797c478bd9Sstevel@tonic-gate 		    (void *)next_td);
35807c478bd9Sstevel@tonic-gate 
35817c478bd9Sstevel@tonic-gate 		usb_err = uhci_parse_td_error(uhcip, pp, next_td);
35827c478bd9Sstevel@tonic-gate 	} else {
35837c478bd9Sstevel@tonic-gate 		usb_err = uhci_parse_td_error(uhcip, pp, td);
35847c478bd9Sstevel@tonic-gate 	}
35857c478bd9Sstevel@tonic-gate 
35867c478bd9Sstevel@tonic-gate 	/*
35877c478bd9Sstevel@tonic-gate 	 * Update the link pointer.
35887c478bd9Sstevel@tonic-gate 	 */
35897c478bd9Sstevel@tonic-gate 	SetQH32(uhcip, pp->pp_qh->element_ptr, TD_PADDR(pp->pp_qh->td_tailp));
35907c478bd9Sstevel@tonic-gate 
35917c478bd9Sstevel@tonic-gate 	/*
35927c478bd9Sstevel@tonic-gate 	 * Send up number of bytes transferred before the error condition.
35937c478bd9Sstevel@tonic-gate 	 */
35947c478bd9Sstevel@tonic-gate 	if ((tw->tw_direction == PID_OUT) && tw->tw_data) {
35957c478bd9Sstevel@tonic-gate 		tw->tw_data->b_rptr += tw->tw_bytes_xfered;
35967c478bd9Sstevel@tonic-gate 	}
35977c478bd9Sstevel@tonic-gate 
35987c478bd9Sstevel@tonic-gate 	uhci_remove_bulk_tds_tws(uhcip, tw->tw_pipe_private, UHCI_IN_ERROR);
35997c478bd9Sstevel@tonic-gate 
36007c478bd9Sstevel@tonic-gate 	/*
36017c478bd9Sstevel@tonic-gate 	 * When there  are no pending bulk commands, point the end of the
36027c478bd9Sstevel@tonic-gate 	 * lattice tree to NULL. This will make sure that the  HC control
36037c478bd9Sstevel@tonic-gate 	 * does not loop anymore and PCI bus is not affected.
36047c478bd9Sstevel@tonic-gate 	 */
36057c478bd9Sstevel@tonic-gate 	if (--uhcip->uhci_pending_bulk_cmds == 0) {
36067c478bd9Sstevel@tonic-gate 		uhcip->uhci_bulk_xfers_q_tail->link_ptr = HC_END_OF_LIST;
36077c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
36087c478bd9Sstevel@tonic-gate 		    "uhci_handle_bulk_td_errors: count = %d",
36097c478bd9Sstevel@tonic-gate 		    uhcip->uhci_pending_bulk_cmds);
36107c478bd9Sstevel@tonic-gate 	}
36117c478bd9Sstevel@tonic-gate 
36127c478bd9Sstevel@tonic-gate 	uhci_hcdi_callback(uhcip, pp, ph, tw, usb_err);
36137c478bd9Sstevel@tonic-gate 	uhci_deallocate_tw(uhcip, pp, tw);
36147c478bd9Sstevel@tonic-gate }
36157c478bd9Sstevel@tonic-gate 
36167c478bd9Sstevel@tonic-gate 
361702acac7eSsl /*
361802acac7eSsl  * uhci_get_bulk_td_by_paddr:
361902acac7eSsl  *	Obtain the address of the TD pool the physical address falls in.
362002acac7eSsl  *
362102acac7eSsl  * td_pool_pp - pointer to the address of the TD pool containing the paddr
362202acac7eSsl  */
362302acac7eSsl /* ARGSUSED */
362402acac7eSsl static void
uhci_get_bulk_td_by_paddr(uhci_state_t * uhcip,uhci_bulk_isoc_xfer_t * info,uint32_t paddr,uhci_bulk_isoc_td_pool_t ** td_pool_pp)362502acac7eSsl uhci_get_bulk_td_by_paddr(
362602acac7eSsl 	uhci_state_t			*uhcip,
362702acac7eSsl 	uhci_bulk_isoc_xfer_t		*info,
362802acac7eSsl 	uint32_t			paddr,
362902acac7eSsl 	uhci_bulk_isoc_td_pool_t	**td_pool_pp)
363002acac7eSsl {
363102acac7eSsl 	uint_t				i = 0;
363202acac7eSsl 
363302acac7eSsl 	while (i < info->num_pools) {
363402acac7eSsl 		*td_pool_pp = &info->td_pools[i];
363502acac7eSsl 		if (((*td_pool_pp)->cookie.dmac_address <= paddr) &&
363602acac7eSsl 		    (((*td_pool_pp)->cookie.dmac_address +
363702acac7eSsl 		    (*td_pool_pp)->cookie.dmac_size) > paddr)) {
363802acac7eSsl 
363902acac7eSsl 			break;
364002acac7eSsl 		}
364102acac7eSsl 		i++;
364202acac7eSsl 	}
364302acac7eSsl 
364402acac7eSsl 	ASSERT(i < info->num_pools);
364502acac7eSsl }
364602acac7eSsl 
364702acac7eSsl 
36487c478bd9Sstevel@tonic-gate void
uhci_remove_bulk_tds_tws(uhci_state_t * uhcip,uhci_pipe_private_t * pp,int what)36497c478bd9Sstevel@tonic-gate uhci_remove_bulk_tds_tws(
36507c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
36517c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp,
36527c478bd9Sstevel@tonic-gate 	int			what)
36537c478bd9Sstevel@tonic-gate {
365402acac7eSsl 	uint_t			rval, i;
36557c478bd9Sstevel@tonic-gate 	uhci_td_t		*head;
36567c478bd9Sstevel@tonic-gate 	uhci_td_t		*head_next;
36577c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_reqp;
36587c478bd9Sstevel@tonic-gate 	uhci_bulk_isoc_xfer_t	*info;
365902acac7eSsl 	uhci_bulk_isoc_td_pool_t *td_pool_ptr;
36607c478bd9Sstevel@tonic-gate 
36617c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
36627c478bd9Sstevel@tonic-gate 
36637c478bd9Sstevel@tonic-gate 	if ((info = pp->pp_qh->bulk_xfer_info) == NULL) {
36647c478bd9Sstevel@tonic-gate 
36657c478bd9Sstevel@tonic-gate 		return;
36667c478bd9Sstevel@tonic-gate 	}
36677c478bd9Sstevel@tonic-gate 
36687c478bd9Sstevel@tonic-gate 	head = uhcip->uhci_outst_tds_head;
36697c478bd9Sstevel@tonic-gate 
36707c478bd9Sstevel@tonic-gate 	while (head) {
36717c478bd9Sstevel@tonic-gate 		uhci_trans_wrapper_t *tw_tmp = head->tw;
36727c478bd9Sstevel@tonic-gate 		head_next = head->outst_td_next;
36737c478bd9Sstevel@tonic-gate 
36747c478bd9Sstevel@tonic-gate 		if (pp->pp_qh == tw_tmp->tw_pipe_private->pp_qh) {
36757c478bd9Sstevel@tonic-gate 			curr_reqp = tw_tmp->tw_curr_xfer_reqp;
36767c478bd9Sstevel@tonic-gate 			if (curr_reqp &&
36777c478bd9Sstevel@tonic-gate 			    ((what == UHCI_IN_CLOSE) ||
36787c478bd9Sstevel@tonic-gate 			    (what == UHCI_IN_RESET))) {
36797c478bd9Sstevel@tonic-gate 				uhci_hcdi_callback(uhcip, pp,
36807c478bd9Sstevel@tonic-gate 				    pp->pp_pipe_handle,
36817c478bd9Sstevel@tonic-gate 				    tw_tmp, USB_CR_FLUSHED);
36827c478bd9Sstevel@tonic-gate 			} /* end of curr_reqp */
36837c478bd9Sstevel@tonic-gate 
36847c478bd9Sstevel@tonic-gate 			uhci_delete_td(uhcip, head);
36857c478bd9Sstevel@tonic-gate 
36867c478bd9Sstevel@tonic-gate 			if (what == UHCI_IN_CLOSE || what == UHCI_IN_RESET) {
36877c478bd9Sstevel@tonic-gate 				ASSERT(info->num_tds > 0);
36887c478bd9Sstevel@tonic-gate 				if (--info->num_tds == 0) {
36897c478bd9Sstevel@tonic-gate 					uhci_deallocate_tw(uhcip, pp, tw_tmp);
36907c478bd9Sstevel@tonic-gate 
36917c478bd9Sstevel@tonic-gate 					/*
36927c478bd9Sstevel@tonic-gate 					 * This will make sure that the HC
36937c478bd9Sstevel@tonic-gate 					 * does not loop anymore when there
36947c478bd9Sstevel@tonic-gate 					 * are no pending bulk commands.
36957c478bd9Sstevel@tonic-gate 					 */
36967c478bd9Sstevel@tonic-gate 					if (--uhcip->uhci_pending_bulk_cmds
36977c478bd9Sstevel@tonic-gate 					    == 0) {
36987c478bd9Sstevel@tonic-gate 						uhcip->uhci_bulk_xfers_q_tail->
36997c478bd9Sstevel@tonic-gate 						    link_ptr = HC_END_OF_LIST;
37007c478bd9Sstevel@tonic-gate 						USB_DPRINTF_L3(PRINT_MASK_ATTA,
37017c478bd9Sstevel@tonic-gate 						    uhcip->uhci_log_hdl,
37027c478bd9Sstevel@tonic-gate 						    "uhci_remove_bulk_tds_tws:"
37037c478bd9Sstevel@tonic-gate 						    " count = %d",
37047c478bd9Sstevel@tonic-gate 						    uhcip->
37057c478bd9Sstevel@tonic-gate 						    uhci_pending_bulk_cmds);
37067c478bd9Sstevel@tonic-gate 					}
37077c478bd9Sstevel@tonic-gate 				}
37087c478bd9Sstevel@tonic-gate 			}
37097c478bd9Sstevel@tonic-gate 		}
37107c478bd9Sstevel@tonic-gate 
37117c478bd9Sstevel@tonic-gate 		head = head_next;
37127c478bd9Sstevel@tonic-gate 	}
37137c478bd9Sstevel@tonic-gate 
37147c478bd9Sstevel@tonic-gate 	if (what == UHCI_IN_CLOSE || what == UHCI_IN_RESET) {
37157c478bd9Sstevel@tonic-gate 		ASSERT(info->num_tds == 0);
37167c478bd9Sstevel@tonic-gate 	}
37177c478bd9Sstevel@tonic-gate 
371802acac7eSsl 	for (i = 0; i < info->num_pools; i++) {
371902acac7eSsl 		td_pool_ptr = &info->td_pools[i];
372002acac7eSsl 		rval = ddi_dma_unbind_handle(td_pool_ptr->dma_handle);
372102acac7eSsl 		ASSERT(rval == DDI_SUCCESS);
372202acac7eSsl 		ddi_dma_mem_free(&td_pool_ptr->mem_handle);
372302acac7eSsl 		ddi_dma_free_handle(&td_pool_ptr->dma_handle);
372402acac7eSsl 	}
372502acac7eSsl 	kmem_free(info->td_pools, (sizeof (uhci_bulk_isoc_td_pool_t) *
372602acac7eSsl 	    info->num_pools));
37277c478bd9Sstevel@tonic-gate 	kmem_free(info, sizeof (uhci_bulk_isoc_xfer_t));
37287c478bd9Sstevel@tonic-gate 	pp->pp_qh->bulk_xfer_info = NULL;
37297c478bd9Sstevel@tonic-gate }
37307c478bd9Sstevel@tonic-gate 
37317c478bd9Sstevel@tonic-gate 
37327c478bd9Sstevel@tonic-gate /*
37337c478bd9Sstevel@tonic-gate  * uhci_save_data_toggle ()
37347c478bd9Sstevel@tonic-gate  *	Save the data toggle in the usba_device structure
37357c478bd9Sstevel@tonic-gate  */
37367c478bd9Sstevel@tonic-gate void
uhci_save_data_toggle(uhci_pipe_private_t * pp)37377c478bd9Sstevel@tonic-gate uhci_save_data_toggle(uhci_pipe_private_t *pp)
37387c478bd9Sstevel@tonic-gate {
37397c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
37407c478bd9Sstevel@tonic-gate 
37417c478bd9Sstevel@tonic-gate 	/* Save the data toggle in the usb devices structure. */
37427c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
37437c478bd9Sstevel@tonic-gate 	usba_hcdi_set_data_toggle(ph->p_usba_device, ph->p_ep.bEndpointAddress,
37447c478bd9Sstevel@tonic-gate 	    pp->pp_data_toggle);
37457c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
37467c478bd9Sstevel@tonic-gate }
37477c478bd9Sstevel@tonic-gate 
374802acac7eSsl /*
374902acac7eSsl  * uhci_create_isoc_transfer_wrapper:
375002acac7eSsl  *	Create a Transaction Wrapper (TW) for isoc transfer.
375102acac7eSsl  *	This involves the allocating of DMA resources.
375202acac7eSsl  *
375302acac7eSsl  *	For isoc transfers, one isoc transfer includes multiple packets
375402acac7eSsl  *	and each packet may have a different length. So each packet is
375502acac7eSsl  *	transfered by one TD. We only know the individual packet length
375602acac7eSsl  *	won't exceed 1023 bytes, but we don't know exactly the lengths.
375702acac7eSsl  *	It is hard to make one physically discontiguous DMA buffer which
375802acac7eSsl  *	can fit in all the TDs like what can be done to the ctrl/bulk/
375902acac7eSsl  *	intr transfers. It is also undesirable to make one physically
376002acac7eSsl  *	contiguous DMA buffer for all the packets, since this may easily
376102acac7eSsl  *	fail when the system is in low memory. So an individual DMA
376202acac7eSsl  *	buffer is allocated for an individual isoc packet and each DMA
376302acac7eSsl  *	buffer is physically contiguous. An extra structure is allocated
376402acac7eSsl  *	to save the multiple DMA handles.
376502acac7eSsl  */
376602acac7eSsl static uhci_trans_wrapper_t *
uhci_create_isoc_transfer_wrapper(uhci_state_t * uhcip,uhci_pipe_private_t * pp,usb_isoc_req_t * req,size_t length,usb_flags_t usb_flags)376702acac7eSsl uhci_create_isoc_transfer_wrapper(
376802acac7eSsl 	uhci_state_t		*uhcip,
376902acac7eSsl 	uhci_pipe_private_t	*pp,
377002acac7eSsl 	usb_isoc_req_t		*req,
377102acac7eSsl 	size_t			length,
377202acac7eSsl 	usb_flags_t		usb_flags)
377302acac7eSsl {
377402acac7eSsl 	int			result;
377502acac7eSsl 	size_t			real_length, strtlen, xfer_size;
377602acac7eSsl 	uhci_trans_wrapper_t	*tw;
377702acac7eSsl 	ddi_device_acc_attr_t	dev_attr;
377802acac7eSsl 	ddi_dma_attr_t		dma_attr;
377902acac7eSsl 	int			kmem_flag;
378002acac7eSsl 	int			(*dmamem_wait)(caddr_t);
378102acac7eSsl 	uint_t			i, j, ccount;
378202acac7eSsl 	usb_isoc_req_t		*tmp_req = req;
378302acac7eSsl 
378402acac7eSsl 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
378502acac7eSsl 
378602acac7eSsl 	if (UHCI_XFER_TYPE(&pp->pp_pipe_handle->p_ep) != USB_EP_ATTR_ISOCH) {
378702acac7eSsl 
378802acac7eSsl 		return (NULL);
378902acac7eSsl 	}
379002acac7eSsl 
379102acac7eSsl 	if ((req == NULL) && (UHCI_XFER_DIR(&pp->pp_pipe_handle->p_ep) ==
379202acac7eSsl 	    USB_EP_DIR_IN)) {
379302acac7eSsl 		tmp_req = (usb_isoc_req_t *)pp->pp_client_periodic_in_reqp;
379402acac7eSsl 	}
379502acac7eSsl 
379602acac7eSsl 	if (tmp_req == NULL) {
379702acac7eSsl 
379802acac7eSsl 		return (NULL);
379902acac7eSsl 	}
380002acac7eSsl 
380102acac7eSsl 
380202acac7eSsl 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
380302acac7eSsl 	    "uhci_create_isoc_transfer_wrapper: length = 0x%lx flags = 0x%x",
380402acac7eSsl 	    length, usb_flags);
380502acac7eSsl 
380602acac7eSsl 	/* SLEEP flag should not be used in interrupt context */
380702acac7eSsl 	if (servicing_interrupt()) {
380802acac7eSsl 		kmem_flag = KM_NOSLEEP;
380902acac7eSsl 		dmamem_wait = DDI_DMA_DONTWAIT;
381002acac7eSsl 	} else {
381102acac7eSsl 		kmem_flag = KM_SLEEP;
381202acac7eSsl 		dmamem_wait = DDI_DMA_SLEEP;
381302acac7eSsl 	}
381402acac7eSsl 
381502acac7eSsl 	/* Allocate space for the transfer wrapper */
381602acac7eSsl 	if ((tw = kmem_zalloc(sizeof (uhci_trans_wrapper_t), kmem_flag)) ==
381702acac7eSsl 	    NULL) {
381802acac7eSsl 		USB_DPRINTF_L2(PRINT_MASK_LISTS,  uhcip->uhci_log_hdl,
381902acac7eSsl 		    "uhci_create_isoc_transfer_wrapper: kmem_alloc failed");
382002acac7eSsl 
382102acac7eSsl 		return (NULL);
382202acac7eSsl 	}
382302acac7eSsl 
382402acac7eSsl 	/* Allocate space for the isoc buffer handles */
382502acac7eSsl 	strtlen = sizeof (uhci_isoc_buf_t) * tmp_req->isoc_pkts_count;
382602acac7eSsl 	if ((tw->tw_isoc_bufs = kmem_zalloc(strtlen, kmem_flag)) == NULL) {
382702acac7eSsl 		USB_DPRINTF_L2(PRINT_MASK_LISTS,  uhcip->uhci_log_hdl,
382802acac7eSsl 		    "uhci_create_isoc_transfer_wrapper: kmem_alloc "
382902acac7eSsl 		    "isoc buffer failed");
383002acac7eSsl 		kmem_free(tw, sizeof (uhci_trans_wrapper_t));
383102acac7eSsl 
383202acac7eSsl 		return (NULL);
383302acac7eSsl 	}
383402acac7eSsl 
383502acac7eSsl 	bcopy(&uhcip->uhci_dma_attr, &dma_attr, sizeof (ddi_dma_attr_t));
383602acac7eSsl 	dma_attr.dma_attr_sgllen = 1;
383702acac7eSsl 
383802acac7eSsl 	dev_attr.devacc_attr_version		= DDI_DEVICE_ATTR_V0;
383902acac7eSsl 	dev_attr.devacc_attr_endian_flags	= DDI_STRUCTURE_LE_ACC;
384002acac7eSsl 	dev_attr.devacc_attr_dataorder		= DDI_STRICTORDER_ACC;
384102acac7eSsl 
384202acac7eSsl 	/* Store the transfer length */
384302acac7eSsl 	tw->tw_length = length;
384402acac7eSsl 
384502acac7eSsl 	for (i = 0; i < tmp_req->isoc_pkts_count; i++) {
3846d29f5a71Szhigang lu - Sun Microsystems - Beijing China 		tw->tw_isoc_bufs[i].index = (ushort_t)i;
384702acac7eSsl 
384802acac7eSsl 		/* Allocate the DMA handle */
384902acac7eSsl 		if ((result = ddi_dma_alloc_handle(uhcip->uhci_dip, &dma_attr,
385002acac7eSsl 		    dmamem_wait, 0, &tw->tw_isoc_bufs[i].dma_handle)) !=
385102acac7eSsl 		    DDI_SUCCESS) {
385202acac7eSsl 			USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
385302acac7eSsl 			    "uhci_create_isoc_transfer_wrapper: "
385402acac7eSsl 			    "Alloc handle %d failed", i);
385502acac7eSsl 
385602acac7eSsl 			for (j = 0; j < i; j++) {
385702acac7eSsl 				result = ddi_dma_unbind_handle(
385802acac7eSsl 				    tw->tw_isoc_bufs[j].dma_handle);
385902acac7eSsl 				ASSERT(result == USB_SUCCESS);
386002acac7eSsl 				ddi_dma_mem_free(&tw->tw_isoc_bufs[j].
386102acac7eSsl 				    mem_handle);
386202acac7eSsl 				ddi_dma_free_handle(&tw->tw_isoc_bufs[j].
386302acac7eSsl 				    dma_handle);
386402acac7eSsl 			}
386502acac7eSsl 			kmem_free(tw->tw_isoc_bufs, strtlen);
386602acac7eSsl 			kmem_free(tw, sizeof (uhci_trans_wrapper_t));
386702acac7eSsl 
386802acac7eSsl 			return (NULL);
386902acac7eSsl 		}
387002acac7eSsl 
387102acac7eSsl 		/* Allocate the memory */
387202acac7eSsl 		xfer_size = tmp_req->isoc_pkt_descr[i].isoc_pkt_length;
387302acac7eSsl 		if ((result = ddi_dma_mem_alloc(tw->tw_isoc_bufs[i].dma_handle,
387402acac7eSsl 		    xfer_size, &dev_attr, DDI_DMA_CONSISTENT, dmamem_wait,
387502acac7eSsl 		    NULL, (caddr_t *)&tw->tw_isoc_bufs[i].buf_addr,
387602acac7eSsl 		    &real_length, &tw->tw_isoc_bufs[i].mem_handle)) !=
387702acac7eSsl 		    DDI_SUCCESS) {
387802acac7eSsl 			USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
387902acac7eSsl 			    "uhci_create_isoc_transfer_wrapper: "
388002acac7eSsl 			    "dma_mem_alloc %d fail", i);
388102acac7eSsl 			ddi_dma_free_handle(&tw->tw_isoc_bufs[i].dma_handle);
388202acac7eSsl 
388302acac7eSsl 			for (j = 0; j < i; j++) {
388402acac7eSsl 				result = ddi_dma_unbind_handle(
388502acac7eSsl 				    tw->tw_isoc_bufs[j].dma_handle);
388602acac7eSsl 				ASSERT(result == USB_SUCCESS);
388702acac7eSsl 				ddi_dma_mem_free(&tw->tw_isoc_bufs[j].
388802acac7eSsl 				    mem_handle);
388902acac7eSsl 				ddi_dma_free_handle(&tw->tw_isoc_bufs[j].
389002acac7eSsl 				    dma_handle);
389102acac7eSsl 			}
389202acac7eSsl 			kmem_free(tw->tw_isoc_bufs, strtlen);
389302acac7eSsl 			kmem_free(tw, sizeof (uhci_trans_wrapper_t));
389402acac7eSsl 
389502acac7eSsl 			return (NULL);
389602acac7eSsl 		}
389702acac7eSsl 
389802acac7eSsl 		ASSERT(real_length >= xfer_size);
389902acac7eSsl 
390002acac7eSsl 		/* Bind the handle */
390102acac7eSsl 		result = ddi_dma_addr_bind_handle(
390202acac7eSsl 		    tw->tw_isoc_bufs[i].dma_handle, NULL,
390302acac7eSsl 		    (caddr_t)tw->tw_isoc_bufs[i].buf_addr, real_length,
390402acac7eSsl 		    DDI_DMA_RDWR|DDI_DMA_CONSISTENT, dmamem_wait, NULL,
390502acac7eSsl 		    &tw->tw_isoc_bufs[i].cookie, &ccount);
390602acac7eSsl 
390702acac7eSsl 		if ((result == DDI_DMA_MAPPED) && (ccount == 1)) {
390802acac7eSsl 			tw->tw_isoc_bufs[i].length = xfer_size;
390902acac7eSsl 
391002acac7eSsl 			continue;
391102acac7eSsl 		} else {
391202acac7eSsl 			USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
391302acac7eSsl 			    "uhci_create_isoc_transfer_wrapper: "
391402acac7eSsl 			    "Bind handle %d failed", i);
391502acac7eSsl 			if (result == DDI_DMA_MAPPED) {
391602acac7eSsl 				result = ddi_dma_unbind_handle(
391702acac7eSsl 				    tw->tw_isoc_bufs[i].dma_handle);
391802acac7eSsl 				ASSERT(result == USB_SUCCESS);
391902acac7eSsl 			}
392002acac7eSsl 			ddi_dma_mem_free(&tw->tw_isoc_bufs[i].mem_handle);
392102acac7eSsl 			ddi_dma_free_handle(&tw->tw_isoc_bufs[i].dma_handle);
392202acac7eSsl 
392302acac7eSsl 			for (j = 0; j < i; j++) {
392402acac7eSsl 				result = ddi_dma_unbind_handle(
392502acac7eSsl 				    tw->tw_isoc_bufs[j].dma_handle);
392602acac7eSsl 				ASSERT(result == USB_SUCCESS);
392702acac7eSsl 				ddi_dma_mem_free(&tw->tw_isoc_bufs[j].
392802acac7eSsl 				    mem_handle);
392902acac7eSsl 				ddi_dma_free_handle(&tw->tw_isoc_bufs[j].
393002acac7eSsl 				    dma_handle);
393102acac7eSsl 			}
393202acac7eSsl 			kmem_free(tw->tw_isoc_bufs, strtlen);
393302acac7eSsl 			kmem_free(tw, sizeof (uhci_trans_wrapper_t));
393402acac7eSsl 
393502acac7eSsl 			return (NULL);
393602acac7eSsl 		}
393702acac7eSsl 	}
393802acac7eSsl 
393902acac7eSsl 	tw->tw_ncookies = tmp_req->isoc_pkts_count;
394002acac7eSsl 	tw->tw_isoc_strtlen = strtlen;
394102acac7eSsl 
394202acac7eSsl 	/*
394302acac7eSsl 	 * Only allow one wrapper to be added at a time. Insert the
394402acac7eSsl 	 * new transaction wrapper into the list for this pipe.
394502acac7eSsl 	 */
394602acac7eSsl 	if (pp->pp_tw_head == NULL) {
394702acac7eSsl 		pp->pp_tw_head = tw;
394802acac7eSsl 		pp->pp_tw_tail = tw;
394902acac7eSsl 	} else {
395002acac7eSsl 		pp->pp_tw_tail->tw_next = tw;
395102acac7eSsl 		pp->pp_tw_tail = tw;
395202acac7eSsl 		ASSERT(tw->tw_next == NULL);
395302acac7eSsl 	}
395402acac7eSsl 
395502acac7eSsl 	/* Store a back pointer to the pipe private structure */
395602acac7eSsl 	tw->tw_pipe_private = pp;
395702acac7eSsl 
395802acac7eSsl 	/* Store the transfer type - synchronous or asynchronous */
395902acac7eSsl 	tw->tw_flags = usb_flags;
396002acac7eSsl 
396102acac7eSsl 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
396202acac7eSsl 	    "uhci_create_isoc_transfer_wrapper: tw = 0x%p, ncookies = %u",
3963112116d8Sfb 	    (void *)tw, tw->tw_ncookies);
396402acac7eSsl 
396502acac7eSsl 	return (tw);
396602acac7eSsl }
39677c478bd9Sstevel@tonic-gate 
39687c478bd9Sstevel@tonic-gate /*
39697c478bd9Sstevel@tonic-gate  * uhci_insert_isoc_td:
39707c478bd9Sstevel@tonic-gate  *	- Create transfer wrapper
39717c478bd9Sstevel@tonic-gate  *	- Allocate memory for the isoc td's
39727c478bd9Sstevel@tonic-gate  *	- Fill up all the TD's and submit to the HC
39737c478bd9Sstevel@tonic-gate  *	- Update all the linked lists
39747c478bd9Sstevel@tonic-gate  */
39757c478bd9Sstevel@tonic-gate int
uhci_insert_isoc_td(uhci_state_t * uhcip,usba_pipe_handle_data_t * ph,usb_isoc_req_t * isoc_req,size_t length,usb_flags_t flags)39767c478bd9Sstevel@tonic-gate uhci_insert_isoc_td(
39777c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
39787c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
39797c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_req,
39807c478bd9Sstevel@tonic-gate 	size_t			length,
39817c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
39827c478bd9Sstevel@tonic-gate {
39837c478bd9Sstevel@tonic-gate 	int			rval = USB_SUCCESS;
39847c478bd9Sstevel@tonic-gate 	int			error;
39857c478bd9Sstevel@tonic-gate 	uint_t			ddic;
398602acac7eSsl 	uint32_t		i, j, index;
39877c478bd9Sstevel@tonic-gate 	uint32_t		bytes_to_xfer;
39887c478bd9Sstevel@tonic-gate 	uint32_t		expired_frames = 0;
39897c478bd9Sstevel@tonic-gate 	usb_frame_number_t	start_frame, end_frame, current_frame;
39907c478bd9Sstevel@tonic-gate 	uhci_td_t		*td_ptr;
39917c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp = (uhci_pipe_private_t *)ph->p_hcd_private;
39927c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw;
39937c478bd9Sstevel@tonic-gate 	uhci_bulk_isoc_xfer_t	*isoc_xfer_info;
399402acac7eSsl 	uhci_bulk_isoc_td_pool_t *td_pool_ptr;
39957c478bd9Sstevel@tonic-gate 
39967c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
399702acac7eSsl 	    "uhci_insert_isoc_td: ph = 0x%p isoc req = %p length = %lu",
3998112116d8Sfb 	    (void *)ph, (void *)isoc_req, length);
39997c478bd9Sstevel@tonic-gate 
40007c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
40017c478bd9Sstevel@tonic-gate 
40027c478bd9Sstevel@tonic-gate 	/* Allocate a transfer wrapper */
400302acac7eSsl 	if ((tw = uhci_create_isoc_transfer_wrapper(uhcip, pp, isoc_req,
400402acac7eSsl 	    length, flags)) == NULL) {
40057c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
40067c478bd9Sstevel@tonic-gate 		    "uhci_insert_isoc_td: TW allocation failed");
40077c478bd9Sstevel@tonic-gate 
40087c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
40097c478bd9Sstevel@tonic-gate 	}
40107c478bd9Sstevel@tonic-gate 
40117c478bd9Sstevel@tonic-gate 	/* Save current isochronous request pointer */
40127c478bd9Sstevel@tonic-gate 	tw->tw_curr_xfer_reqp = (usb_opaque_t)isoc_req;
40137c478bd9Sstevel@tonic-gate 
40147c478bd9Sstevel@tonic-gate 	/*
40157c478bd9Sstevel@tonic-gate 	 * Initialize the transfer wrapper. These values are useful
40167c478bd9Sstevel@tonic-gate 	 * for sending back the reply.
40177c478bd9Sstevel@tonic-gate 	 */
40187c478bd9Sstevel@tonic-gate 	tw->tw_handle_td		= uhci_handle_isoc_td;
40197c478bd9Sstevel@tonic-gate 	tw->tw_handle_callback_value	= NULL;
40207c478bd9Sstevel@tonic-gate 	tw->tw_direction = (UHCI_XFER_DIR(&ph->p_ep) == USB_EP_DIR_OUT) ?
4021fffe0b30Sqz 	    PID_OUT : PID_IN;
40227c478bd9Sstevel@tonic-gate 
40237c478bd9Sstevel@tonic-gate 	/*
40247c478bd9Sstevel@tonic-gate 	 * If the transfer isoc send, then copy the data from the request
40257c478bd9Sstevel@tonic-gate 	 * to the transfer wrapper.
40267c478bd9Sstevel@tonic-gate 	 */
40277c478bd9Sstevel@tonic-gate 	if ((tw->tw_direction == PID_OUT) && length) {
402802acac7eSsl 		uchar_t *p;
402902acac7eSsl 
40307c478bd9Sstevel@tonic-gate 		ASSERT(isoc_req->isoc_data != NULL);
403102acac7eSsl 		p = isoc_req->isoc_data->b_rptr;
40327c478bd9Sstevel@tonic-gate 
40337c478bd9Sstevel@tonic-gate 		/* Copy the data into the message */
403402acac7eSsl 		for (i = 0; i < isoc_req->isoc_pkts_count; i++) {
403502acac7eSsl 			ddi_rep_put8(tw->tw_isoc_bufs[i].mem_handle,
403602acac7eSsl 			    p, (uint8_t *)tw->tw_isoc_bufs[i].buf_addr,
403702acac7eSsl 			    isoc_req->isoc_pkt_descr[i].isoc_pkt_length,
403802acac7eSsl 			    DDI_DEV_AUTOINCR);
403902acac7eSsl 			p += isoc_req->isoc_pkt_descr[i].isoc_pkt_length;
404002acac7eSsl 		}
40417c478bd9Sstevel@tonic-gate 	}
40427c478bd9Sstevel@tonic-gate 
40437c478bd9Sstevel@tonic-gate 	if (tw->tw_direction == PID_IN) {
40447c478bd9Sstevel@tonic-gate 		if ((rval = uhci_allocate_periodic_in_resource(uhcip, pp, tw,
40457c478bd9Sstevel@tonic-gate 		    flags)) != USB_SUCCESS) {
40467c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
40477c478bd9Sstevel@tonic-gate 			    "uhci_insert_isoc_td: isoc_req_t alloc failed");
404802acac7eSsl 			uhci_deallocate_tw(uhcip, pp, tw);
40497c478bd9Sstevel@tonic-gate 
40507c478bd9Sstevel@tonic-gate 			return (rval);
40517c478bd9Sstevel@tonic-gate 		}
40527c478bd9Sstevel@tonic-gate 
40537c478bd9Sstevel@tonic-gate 		isoc_req = (usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
40547c478bd9Sstevel@tonic-gate 	}
40557c478bd9Sstevel@tonic-gate 
40567c478bd9Sstevel@tonic-gate 	tw->tw_isoc_req	= (usb_isoc_req_t *)tw->tw_curr_xfer_reqp;
40577c478bd9Sstevel@tonic-gate 
40587c478bd9Sstevel@tonic-gate 	/* Get the pointer to the isoc_xfer_info structure */
40597c478bd9Sstevel@tonic-gate 	isoc_xfer_info = (uhci_bulk_isoc_xfer_t *)&tw->tw_xfer_info;
40607c478bd9Sstevel@tonic-gate 	isoc_xfer_info->num_tds = isoc_req->isoc_pkts_count;
40617c478bd9Sstevel@tonic-gate 
40627c478bd9Sstevel@tonic-gate 	/*
40637c478bd9Sstevel@tonic-gate 	 * Allocate memory for isoc tds
40647c478bd9Sstevel@tonic-gate 	 */
406502acac7eSsl 	if ((rval = uhci_alloc_bulk_isoc_tds(uhcip, isoc_req->isoc_pkts_count,
40667c478bd9Sstevel@tonic-gate 	    isoc_xfer_info)) != USB_SUCCESS) {
40677c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
406802acac7eSsl 		    "uhci_alloc_bulk_isoc_td: Memory allocation failure");
406902acac7eSsl 
407002acac7eSsl 		if (tw->tw_direction == PID_IN) {
407102acac7eSsl 			uhci_deallocate_periodic_in_resource(uhcip, pp, tw);
407202acac7eSsl 		}
40737c478bd9Sstevel@tonic-gate 		uhci_deallocate_tw(uhcip, pp, tw);
40747c478bd9Sstevel@tonic-gate 
40757c478bd9Sstevel@tonic-gate 		return (rval);
40767c478bd9Sstevel@tonic-gate 	}
40777c478bd9Sstevel@tonic-gate 
40787c478bd9Sstevel@tonic-gate 	/*
40797c478bd9Sstevel@tonic-gate 	 * Get the isoc td pool address, buffer address and
40807c478bd9Sstevel@tonic-gate 	 * max packet size that the device supports.
40817c478bd9Sstevel@tonic-gate 	 */
408202acac7eSsl 	td_pool_ptr = &isoc_xfer_info->td_pools[0];
408302acac7eSsl 	td_ptr = (uhci_td_t *)td_pool_ptr->pool_addr;
408402acac7eSsl 	index = 0;
40857c478bd9Sstevel@tonic-gate 
40867c478bd9Sstevel@tonic-gate 	/*
40877c478bd9Sstevel@tonic-gate 	 * Fill up the isoc tds
40887c478bd9Sstevel@tonic-gate 	 */
40897c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
40907c478bd9Sstevel@tonic-gate 	    "uhci_insert_isoc_td : isoc pkts %d", isoc_req->isoc_pkts_count);
40917c478bd9Sstevel@tonic-gate 
409202acac7eSsl 	for (i = 0; i < isoc_xfer_info->num_pools; i++) {
409302acac7eSsl 		for (j = 0; j < td_pool_ptr->num_tds; j++) {
409402acac7eSsl 			bytes_to_xfer =
409502acac7eSsl 			    isoc_req->isoc_pkt_descr[index].isoc_pkt_length;
409602acac7eSsl 
409702acac7eSsl 			uhci_fill_in_bulk_isoc_td(uhcip, &td_ptr[j],
409802acac7eSsl 			    (uhci_td_t *)NULL, HC_END_OF_LIST, ph, index,
409902acac7eSsl 			    bytes_to_xfer, tw);
4100d29f5a71Szhigang lu - Sun Microsystems - Beijing China 			td_ptr[j].isoc_pkt_index = (ushort_t)index;
410102acac7eSsl 			index++;
410202acac7eSsl 		}
41037c478bd9Sstevel@tonic-gate 
410402acac7eSsl 		if (i < (isoc_xfer_info->num_pools - 1)) {
410502acac7eSsl 			td_pool_ptr = &isoc_xfer_info->td_pools[i + 1];
410602acac7eSsl 			td_ptr = (uhci_td_t *)td_pool_ptr->pool_addr;
410702acac7eSsl 		}
41087c478bd9Sstevel@tonic-gate 	}
41097c478bd9Sstevel@tonic-gate 
41107c478bd9Sstevel@tonic-gate 	/*
41117c478bd9Sstevel@tonic-gate 	 * Get the starting frame number.
41127c478bd9Sstevel@tonic-gate 	 * The client drivers sets the flag USB_ATTRS_ISOC_XFER_ASAP to inform
41137c478bd9Sstevel@tonic-gate 	 * the HCD to care of starting frame number.
41147c478bd9Sstevel@tonic-gate 	 *
41157c478bd9Sstevel@tonic-gate 	 * Following code is very time critical. So, perform atomic execution.
41167c478bd9Sstevel@tonic-gate 	 */
41177c478bd9Sstevel@tonic-gate 	ddic = ddi_enter_critical();
41187c478bd9Sstevel@tonic-gate 	current_frame = uhci_get_sw_frame_number(uhcip);
41197c478bd9Sstevel@tonic-gate 
41207c478bd9Sstevel@tonic-gate 	if (isoc_req->isoc_attributes & USB_ATTRS_ISOC_START_FRAME) {
41217c478bd9Sstevel@tonic-gate 		start_frame = isoc_req->isoc_frame_no;
41227c478bd9Sstevel@tonic-gate 		end_frame = start_frame + isoc_req->isoc_pkts_count;
41237c478bd9Sstevel@tonic-gate 
41247c478bd9Sstevel@tonic-gate 		/* Check available frames */
41257c478bd9Sstevel@tonic-gate 		if ((end_frame - current_frame) < UHCI_MAX_ISOC_FRAMES) {
41267c478bd9Sstevel@tonic-gate 			if (current_frame > start_frame) {
41277c478bd9Sstevel@tonic-gate 				if ((current_frame + FRNUM_OFFSET) <
41287c478bd9Sstevel@tonic-gate 				    end_frame) {
41297c478bd9Sstevel@tonic-gate 					expired_frames = current_frame +
4130fffe0b30Sqz 					    FRNUM_OFFSET - start_frame;
41317c478bd9Sstevel@tonic-gate 					start_frame = current_frame +
4132fffe0b30Sqz 					    FRNUM_OFFSET;
41337c478bd9Sstevel@tonic-gate 				} else {
41347c478bd9Sstevel@tonic-gate 					rval = USB_INVALID_START_FRAME;
41357c478bd9Sstevel@tonic-gate 				}
41367c478bd9Sstevel@tonic-gate 			}
41377c478bd9Sstevel@tonic-gate 		} else {
41387c478bd9Sstevel@tonic-gate 			rval = USB_INVALID_START_FRAME;
41397c478bd9Sstevel@tonic-gate 		}
41407c478bd9Sstevel@tonic-gate 
41417c478bd9Sstevel@tonic-gate 	} else if (isoc_req->isoc_attributes & USB_ATTRS_ISOC_XFER_ASAP) {
41427c478bd9Sstevel@tonic-gate 		start_frame = pp->pp_frame_num;
41437c478bd9Sstevel@tonic-gate 
41447c478bd9Sstevel@tonic-gate 		if (start_frame == INVALID_FRNUM) {
41457c478bd9Sstevel@tonic-gate 			start_frame = current_frame + FRNUM_OFFSET;
41467c478bd9Sstevel@tonic-gate 		} else if (current_frame > start_frame) {
41477c478bd9Sstevel@tonic-gate 			start_frame = current_frame + FRNUM_OFFSET;
41487c478bd9Sstevel@tonic-gate 		}
41497c478bd9Sstevel@tonic-gate 
41507c478bd9Sstevel@tonic-gate 		end_frame = start_frame + isoc_req->isoc_pkts_count;
41517c478bd9Sstevel@tonic-gate 		isoc_req->isoc_frame_no = start_frame;
41527c478bd9Sstevel@tonic-gate 
41537c478bd9Sstevel@tonic-gate 	}
41547c478bd9Sstevel@tonic-gate 
41557c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
41567c478bd9Sstevel@tonic-gate 
41577c478bd9Sstevel@tonic-gate 		/* Exit the critical */
41587c478bd9Sstevel@tonic-gate 		ddi_exit_critical(ddic);
41597c478bd9Sstevel@tonic-gate 
41607c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
41617c478bd9Sstevel@tonic-gate 		    "uhci_insert_isoc_td: Invalid starting frame number");
41627c478bd9Sstevel@tonic-gate 
416302acac7eSsl 		if (tw->tw_direction == PID_IN) {
416402acac7eSsl 			uhci_deallocate_periodic_in_resource(uhcip, pp, tw);
416502acac7eSsl 		}
416602acac7eSsl 
41677c478bd9Sstevel@tonic-gate 		while (tw->tw_hctd_head) {
41687c478bd9Sstevel@tonic-gate 			uhci_delete_td(uhcip, tw->tw_hctd_head);
41697c478bd9Sstevel@tonic-gate 		}
41707c478bd9Sstevel@tonic-gate 
417102acac7eSsl 		for (i = 0; i < isoc_xfer_info->num_pools; i++) {
417202acac7eSsl 			td_pool_ptr = &isoc_xfer_info->td_pools[i];
417302acac7eSsl 			error = ddi_dma_unbind_handle(td_pool_ptr->dma_handle);
417402acac7eSsl 			ASSERT(error == DDI_SUCCESS);
417502acac7eSsl 			ddi_dma_mem_free(&td_pool_ptr->mem_handle);
417602acac7eSsl 			ddi_dma_free_handle(&td_pool_ptr->dma_handle);
417702acac7eSsl 		}
417802acac7eSsl 		kmem_free(isoc_xfer_info->td_pools,
417902acac7eSsl 		    (sizeof (uhci_bulk_isoc_td_pool_t) *
418002acac7eSsl 		    isoc_xfer_info->num_pools));
41817c478bd9Sstevel@tonic-gate 
41827c478bd9Sstevel@tonic-gate 		uhci_deallocate_tw(uhcip, pp, tw);
41837c478bd9Sstevel@tonic-gate 
41847c478bd9Sstevel@tonic-gate 		return (rval);
41857c478bd9Sstevel@tonic-gate 	}
41867c478bd9Sstevel@tonic-gate 
41877c478bd9Sstevel@tonic-gate 	for (i = 0; i < expired_frames; i++) {
41887c478bd9Sstevel@tonic-gate 		isoc_req->isoc_pkt_descr[i].isoc_pkt_status =
4189fffe0b30Sqz 		    USB_CR_NOT_ACCESSED;
41907c478bd9Sstevel@tonic-gate 		isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length =
4191fffe0b30Sqz 		    isoc_req->isoc_pkt_descr[i].isoc_pkt_length;
419202acac7eSsl 		uhci_get_isoc_td_by_index(uhcip, isoc_xfer_info, i,
419302acac7eSsl 		    &td_ptr, &td_pool_ptr);
419402acac7eSsl 		uhci_delete_td(uhcip, td_ptr);
41957c478bd9Sstevel@tonic-gate 		--isoc_xfer_info->num_tds;
41967c478bd9Sstevel@tonic-gate 	}
41977c478bd9Sstevel@tonic-gate 
41987c478bd9Sstevel@tonic-gate 	/*
41997c478bd9Sstevel@tonic-gate 	 * Add the TD's to the HC list
42007c478bd9Sstevel@tonic-gate 	 */
42017c478bd9Sstevel@tonic-gate 	start_frame = (start_frame & 0x3ff);
42027c478bd9Sstevel@tonic-gate 	for (; i < isoc_req->isoc_pkts_count; i++) {
420302acac7eSsl 		uhci_get_isoc_td_by_index(uhcip, isoc_xfer_info, i,
420402acac7eSsl 		    &td_ptr, &td_pool_ptr);
42057c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_isoc_q_tailp[start_frame]) {
420602acac7eSsl 			td_ptr->isoc_prev =
4207fffe0b30Sqz 			    uhcip->uhci_isoc_q_tailp[start_frame];
420802acac7eSsl 			td_ptr->isoc_next = NULL;
420902acac7eSsl 			td_ptr->link_ptr =
42107c478bd9Sstevel@tonic-gate 			    uhcip->uhci_isoc_q_tailp[start_frame]->link_ptr;
42117c478bd9Sstevel@tonic-gate 			uhcip->uhci_isoc_q_tailp[start_frame]->isoc_next =
4212fffe0b30Sqz 			    td_ptr;
42137c478bd9Sstevel@tonic-gate 			SetTD32(uhcip,
42147c478bd9Sstevel@tonic-gate 			    uhcip->uhci_isoc_q_tailp[start_frame]->link_ptr,
421502acac7eSsl 			    ISOCTD_PADDR(td_pool_ptr, td_ptr));
421602acac7eSsl 			uhcip->uhci_isoc_q_tailp[start_frame] = td_ptr;
42177c478bd9Sstevel@tonic-gate 		} else {
421802acac7eSsl 			uhcip->uhci_isoc_q_tailp[start_frame] = td_ptr;
421902acac7eSsl 			td_ptr->isoc_next = NULL;
422002acac7eSsl 			td_ptr->isoc_prev = NULL;
422102acac7eSsl 			SetTD32(uhcip, td_ptr->link_ptr,
42227c478bd9Sstevel@tonic-gate 			    GetFL32(uhcip,
4223fffe0b30Sqz 			    uhcip->uhci_frame_lst_tablep[start_frame]));
42247c478bd9Sstevel@tonic-gate 			SetFL32(uhcip,
42257c478bd9Sstevel@tonic-gate 			    uhcip->uhci_frame_lst_tablep[start_frame],
422602acac7eSsl 			    ISOCTD_PADDR(td_pool_ptr, td_ptr));
42277c478bd9Sstevel@tonic-gate 		}
4228d29f5a71Szhigang lu - Sun Microsystems - Beijing China 		td_ptr->starting_frame = (uint_t)start_frame;
42297c478bd9Sstevel@tonic-gate 
42307c478bd9Sstevel@tonic-gate 		if (++start_frame == NUM_FRAME_LST_ENTRIES)
42317c478bd9Sstevel@tonic-gate 			start_frame = 0;
42327c478bd9Sstevel@tonic-gate 	}
42337c478bd9Sstevel@tonic-gate 
42347c478bd9Sstevel@tonic-gate 	ddi_exit_critical(ddic);
42357c478bd9Sstevel@tonic-gate 	pp->pp_frame_num = end_frame;
42367c478bd9Sstevel@tonic-gate 
42377c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
42387c478bd9Sstevel@tonic-gate 	    "uhci_insert_isoc_td: current frame number 0x%llx, pipe frame num"
4239112116d8Sfb 	    " 0x%llx", (unsigned long long)current_frame,
4240112116d8Sfb 	    (unsigned long long)(pp->pp_frame_num));
42417c478bd9Sstevel@tonic-gate 
42427c478bd9Sstevel@tonic-gate 	return (rval);
42437c478bd9Sstevel@tonic-gate }
42447c478bd9Sstevel@tonic-gate 
42457c478bd9Sstevel@tonic-gate 
424602acac7eSsl /*
424702acac7eSsl  * uhci_get_isoc_td_by_index:
424802acac7eSsl  *	Obtain the addresses of the TD pool and the TD at the index.
424902acac7eSsl  *
425002acac7eSsl  * tdpp - pointer to the address of the TD at the isoc packet index
425102acac7eSsl  * td_pool_pp - pointer to the address of the TD pool containing
4252fffe0b30Sqz  *		the specified TD
425302acac7eSsl  */
425402acac7eSsl /* ARGSUSED */
425502acac7eSsl static void
uhci_get_isoc_td_by_index(uhci_state_t * uhcip,uhci_bulk_isoc_xfer_t * info,uint_t index,uhci_td_t ** tdpp,uhci_bulk_isoc_td_pool_t ** td_pool_pp)425602acac7eSsl uhci_get_isoc_td_by_index(
425702acac7eSsl 	uhci_state_t			*uhcip,
425802acac7eSsl 	uhci_bulk_isoc_xfer_t		*info,
425902acac7eSsl 	uint_t				index,
426002acac7eSsl 	uhci_td_t			**tdpp,
426102acac7eSsl 	uhci_bulk_isoc_td_pool_t	**td_pool_pp)
426202acac7eSsl {
426302acac7eSsl 	uint_t			i = 0, j = 0;
426402acac7eSsl 	uhci_td_t		*td_ptr;
426502acac7eSsl 
426602acac7eSsl 	while (j < info->num_pools) {
426702acac7eSsl 		if ((i + info->td_pools[j].num_tds) <= index) {
426802acac7eSsl 			i += info->td_pools[j].num_tds;
426902acac7eSsl 			j++;
427002acac7eSsl 		} else {
427102acac7eSsl 			i = index - i;
427202acac7eSsl 
427302acac7eSsl 			break;
427402acac7eSsl 		}
427502acac7eSsl 	}
427602acac7eSsl 
427702acac7eSsl 	ASSERT(j < info->num_pools);
427802acac7eSsl 	*td_pool_pp = &info->td_pools[j];
427902acac7eSsl 	td_ptr = (uhci_td_t *)((*td_pool_pp)->pool_addr);
428002acac7eSsl 	*tdpp = &td_ptr[i];
428102acac7eSsl }
428202acac7eSsl 
428302acac7eSsl 
42847c478bd9Sstevel@tonic-gate /*
42857c478bd9Sstevel@tonic-gate  * uhci_handle_isoc_td:
42867c478bd9Sstevel@tonic-gate  *	Handles the completed isoc tds
42877c478bd9Sstevel@tonic-gate  */
42887c478bd9Sstevel@tonic-gate void
uhci_handle_isoc_td(uhci_state_t * uhcip,uhci_td_t * td)42897c478bd9Sstevel@tonic-gate uhci_handle_isoc_td(uhci_state_t *uhcip, uhci_td_t *td)
42907c478bd9Sstevel@tonic-gate {
429102acac7eSsl 	uint_t			rval, i;
42927c478bd9Sstevel@tonic-gate 	uint32_t		pkt_index = td->isoc_pkt_index;
42937c478bd9Sstevel@tonic-gate 	usb_cr_t		cr;
42947c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw = td->tw;
42957c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_req = (usb_isoc_req_t *)tw->tw_isoc_req;
42967c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp = tw->tw_pipe_private;
42977c478bd9Sstevel@tonic-gate 	uhci_bulk_isoc_xfer_t	*isoc_xfer_info = &tw->tw_xfer_info;
42987c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*usb_pp;
429902acac7eSsl 	uhci_bulk_isoc_td_pool_t *td_pool_ptr;
43007c478bd9Sstevel@tonic-gate 
43017c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
43027c478bd9Sstevel@tonic-gate 	    "uhci_handle_isoc_td: td = 0x%p, pp = 0x%p, tw = 0x%p, req = 0x%p, "
4303112116d8Sfb 	    "index = %x", (void *)td, (void *)pp, (void *)tw, (void *)isoc_req,
4304112116d8Sfb 	    pkt_index);
43057c478bd9Sstevel@tonic-gate 
43067c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
43077c478bd9Sstevel@tonic-gate 
43087c478bd9Sstevel@tonic-gate 	usb_pp = pp->pp_pipe_handle;
43097c478bd9Sstevel@tonic-gate 
43107c478bd9Sstevel@tonic-gate 	/*
43117c478bd9Sstevel@tonic-gate 	 * Check whether there are any errors occurred. If so, update error
43127c478bd9Sstevel@tonic-gate 	 * count and return it to the upper.But never return a non zero
43137c478bd9Sstevel@tonic-gate 	 * completion reason.
43147c478bd9Sstevel@tonic-gate 	 */
43157c478bd9Sstevel@tonic-gate 	cr = USB_CR_OK;
43167c478bd9Sstevel@tonic-gate 	if (GetTD_status(uhcip, td) & TD_STATUS_MASK) {
43177c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
43187c478bd9Sstevel@tonic-gate 		    "uhci_handle_isoc_td: Error Occurred: TD Status = %x",
43197c478bd9Sstevel@tonic-gate 		    GetTD_status(uhcip, td));
43207c478bd9Sstevel@tonic-gate 		isoc_req->isoc_error_count++;
43217c478bd9Sstevel@tonic-gate 	}
43227c478bd9Sstevel@tonic-gate 
43237c478bd9Sstevel@tonic-gate 	if (isoc_req != NULL) {
43247c478bd9Sstevel@tonic-gate 		isoc_req->isoc_pkt_descr[pkt_index].isoc_pkt_status = cr;
43257c478bd9Sstevel@tonic-gate 		isoc_req->isoc_pkt_descr[pkt_index].isoc_pkt_actual_length =
4326fffe0b30Sqz 		    (GetTD_alen(uhcip, td) == ZERO_LENGTH) ? 0 :
4327fffe0b30Sqz 		    GetTD_alen(uhcip, td) + 1;
43287c478bd9Sstevel@tonic-gate 	}
43297c478bd9Sstevel@tonic-gate 
43307c478bd9Sstevel@tonic-gate 	uhci_delete_isoc_td(uhcip, td);
43317c478bd9Sstevel@tonic-gate 
43327c478bd9Sstevel@tonic-gate 	if (--isoc_xfer_info->num_tds != 0) {
43337c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
43347c478bd9Sstevel@tonic-gate 		    "uhci_handle_isoc_td: Number of TDs %d",
43357c478bd9Sstevel@tonic-gate 		    isoc_xfer_info->num_tds);
43367c478bd9Sstevel@tonic-gate 
43377c478bd9Sstevel@tonic-gate 		return;
43387c478bd9Sstevel@tonic-gate 	}
43397c478bd9Sstevel@tonic-gate 
43407c478bd9Sstevel@tonic-gate 	tw->tw_claim = UHCI_INTR_HDLR_CLAIMED;
43417c478bd9Sstevel@tonic-gate 	if (tw->tw_direction == PID_IN) {
43427c478bd9Sstevel@tonic-gate 		uhci_sendup_td_message(uhcip, cr, tw);
43437c478bd9Sstevel@tonic-gate 
43447c478bd9Sstevel@tonic-gate 		if ((uhci_handle_isoc_receive(uhcip, pp, tw)) != USB_SUCCESS) {
43457c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
43467c478bd9Sstevel@tonic-gate 			    "uhci_handle_isoc_td: Drop message");
43477c478bd9Sstevel@tonic-gate 		}
43487c478bd9Sstevel@tonic-gate 
43497c478bd9Sstevel@tonic-gate 	} else {
43507c478bd9Sstevel@tonic-gate 		/* update kstats only for OUT. sendup_td_msg() does it for IN */
43517c478bd9Sstevel@tonic-gate 		uhci_do_byte_stats(uhcip, tw->tw_length,
43527c478bd9Sstevel@tonic-gate 		    usb_pp->p_ep.bmAttributes, usb_pp->p_ep.bEndpointAddress);
43537c478bd9Sstevel@tonic-gate 
43547c478bd9Sstevel@tonic-gate 		uhci_hcdi_callback(uhcip, pp, usb_pp, tw, USB_CR_OK);
43557c478bd9Sstevel@tonic-gate 	}
43567c478bd9Sstevel@tonic-gate 
435702acac7eSsl 	for (i = 0; i < isoc_xfer_info->num_pools; i++) {
435802acac7eSsl 		td_pool_ptr = &isoc_xfer_info->td_pools[i];
435902acac7eSsl 		rval = ddi_dma_unbind_handle(td_pool_ptr->dma_handle);
436002acac7eSsl 		ASSERT(rval == DDI_SUCCESS);
436102acac7eSsl 		ddi_dma_mem_free(&td_pool_ptr->mem_handle);
436202acac7eSsl 		ddi_dma_free_handle(&td_pool_ptr->dma_handle);
436302acac7eSsl 	}
436402acac7eSsl 	kmem_free(isoc_xfer_info->td_pools,
436502acac7eSsl 	    (sizeof (uhci_bulk_isoc_td_pool_t) *
436602acac7eSsl 	    isoc_xfer_info->num_pools));
43677c478bd9Sstevel@tonic-gate 	uhci_deallocate_tw(uhcip, pp, tw);
43687c478bd9Sstevel@tonic-gate }
43697c478bd9Sstevel@tonic-gate 
43707c478bd9Sstevel@tonic-gate 
43717c478bd9Sstevel@tonic-gate /*
43727c478bd9Sstevel@tonic-gate  * uhci_handle_isoc_receive:
43737c478bd9Sstevel@tonic-gate  *	- Sends the isoc data to the client
43747c478bd9Sstevel@tonic-gate  *	- Inserts another isoc receive request
43757c478bd9Sstevel@tonic-gate  */
43767c478bd9Sstevel@tonic-gate static int
uhci_handle_isoc_receive(uhci_state_t * uhcip,uhci_pipe_private_t * pp,uhci_trans_wrapper_t * tw)43777c478bd9Sstevel@tonic-gate uhci_handle_isoc_receive(
43787c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
43797c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp,
43807c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw)
43817c478bd9Sstevel@tonic-gate {
43827c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
4383112116d8Sfb 	    "uhci_handle_isoc_receive: tw = 0x%p", (void *)tw);
43847c478bd9Sstevel@tonic-gate 
43857c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
43867c478bd9Sstevel@tonic-gate 
43877c478bd9Sstevel@tonic-gate 	/*
43887c478bd9Sstevel@tonic-gate 	 * -- check for pipe state being polling before
43897c478bd9Sstevel@tonic-gate 	 * inserting a new request. Check when is TD
43907c478bd9Sstevel@tonic-gate 	 * de-allocation being done? (so we can reuse the same TD)
43917c478bd9Sstevel@tonic-gate 	 */
43927c478bd9Sstevel@tonic-gate 	if (uhci_start_isoc_receive_polling(uhcip,
43937c478bd9Sstevel@tonic-gate 	    pp->pp_pipe_handle, (usb_isoc_req_t *)tw->tw_curr_xfer_reqp,
43947c478bd9Sstevel@tonic-gate 	    0) != USB_SUCCESS) {
43957c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
43967c478bd9Sstevel@tonic-gate 		    "uhci_handle_isoc_receive: receive polling failed");
43977c478bd9Sstevel@tonic-gate 
43987c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
43997c478bd9Sstevel@tonic-gate 	}
44007c478bd9Sstevel@tonic-gate 
44017c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
44027c478bd9Sstevel@tonic-gate }
44037c478bd9Sstevel@tonic-gate 
44047c478bd9Sstevel@tonic-gate 
44057c478bd9Sstevel@tonic-gate /*
44067c478bd9Sstevel@tonic-gate  * uhci_delete_isoc_td:
44077c478bd9Sstevel@tonic-gate  *	- Delete from the outstanding command queue
44087c478bd9Sstevel@tonic-gate  *	- Delete from the tw queue
44097c478bd9Sstevel@tonic-gate  *	- Delete from the isoc queue
44107c478bd9Sstevel@tonic-gate  *	- Delete from the HOST CONTROLLER list
44117c478bd9Sstevel@tonic-gate  */
44127c478bd9Sstevel@tonic-gate static void
uhci_delete_isoc_td(uhci_state_t * uhcip,uhci_td_t * td)44137c478bd9Sstevel@tonic-gate uhci_delete_isoc_td(uhci_state_t *uhcip, uhci_td_t *td)
44147c478bd9Sstevel@tonic-gate {
44157c478bd9Sstevel@tonic-gate 	uint32_t	starting_frame = td->starting_frame;
44167c478bd9Sstevel@tonic-gate 
44177c478bd9Sstevel@tonic-gate 	if ((td->isoc_next == NULL) && (td->isoc_prev == NULL)) {
44187c478bd9Sstevel@tonic-gate 		SetFL32(uhcip, uhcip->uhci_frame_lst_tablep[starting_frame],
4419fffe0b30Sqz 		    GetTD32(uhcip, td->link_ptr));
44207c478bd9Sstevel@tonic-gate 		uhcip->uhci_isoc_q_tailp[starting_frame] = 0;
44217c478bd9Sstevel@tonic-gate 	} else if (td->isoc_next == NULL) {
44227c478bd9Sstevel@tonic-gate 		td->isoc_prev->link_ptr = td->link_ptr;
44237c478bd9Sstevel@tonic-gate 		td->isoc_prev->isoc_next = NULL;
44247c478bd9Sstevel@tonic-gate 		uhcip->uhci_isoc_q_tailp[starting_frame] = td->isoc_prev;
44257c478bd9Sstevel@tonic-gate 	} else if (td->isoc_prev == NULL) {
44267c478bd9Sstevel@tonic-gate 		td->isoc_next->isoc_prev = NULL;
44277c478bd9Sstevel@tonic-gate 		SetFL32(uhcip, uhcip->uhci_frame_lst_tablep[starting_frame],
44287c478bd9Sstevel@tonic-gate 		    GetTD32(uhcip, td->link_ptr));
44297c478bd9Sstevel@tonic-gate 	} else {
44307c478bd9Sstevel@tonic-gate 		td->isoc_prev->isoc_next = td->isoc_next;
44317c478bd9Sstevel@tonic-gate 		td->isoc_next->isoc_prev = td->isoc_prev;
44327c478bd9Sstevel@tonic-gate 		td->isoc_prev->link_ptr = td->link_ptr;
44337c478bd9Sstevel@tonic-gate 	}
44347c478bd9Sstevel@tonic-gate 
44357c478bd9Sstevel@tonic-gate 	uhci_delete_td(uhcip, td);
44367c478bd9Sstevel@tonic-gate }
44377c478bd9Sstevel@tonic-gate 
44387c478bd9Sstevel@tonic-gate 
44397c478bd9Sstevel@tonic-gate /*
44407c478bd9Sstevel@tonic-gate  * uhci_send_isoc_receive
44417c478bd9Sstevel@tonic-gate  *	- Allocates usb_isoc_request
44427c478bd9Sstevel@tonic-gate  *	- Updates the isoc request
44437c478bd9Sstevel@tonic-gate  *	- Inserts the isoc td's into the HC processing list.
44447c478bd9Sstevel@tonic-gate  */
44457c478bd9Sstevel@tonic-gate int
uhci_start_isoc_receive_polling(uhci_state_t * uhcip,usba_pipe_handle_data_t * ph,usb_isoc_req_t * isoc_req,usb_flags_t usb_flags)44467c478bd9Sstevel@tonic-gate uhci_start_isoc_receive_polling(
44477c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
44487c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
44497c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_req,
44507c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
44517c478bd9Sstevel@tonic-gate {
44527c478bd9Sstevel@tonic-gate 	int			ii, error;
4453b3001defSlg 	size_t			max_isoc_xfer_size, length, isoc_pkts_length;
44547c478bd9Sstevel@tonic-gate 	ushort_t		isoc_pkt_count;
44557c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp = (uhci_pipe_private_t *)ph->p_hcd_private;
44567c478bd9Sstevel@tonic-gate 	usb_isoc_pkt_descr_t	*isoc_pkt_descr;
44577c478bd9Sstevel@tonic-gate 
44587c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
44597c478bd9Sstevel@tonic-gate 	    "uhci_start_isoc_receive_polling: usb_flags = %x", usb_flags);
44607c478bd9Sstevel@tonic-gate 
44617c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
44627c478bd9Sstevel@tonic-gate 
44637c478bd9Sstevel@tonic-gate 	max_isoc_xfer_size = ph->p_ep.wMaxPacketSize * UHCI_MAX_ISOC_PKTS;
44647c478bd9Sstevel@tonic-gate 
44657c478bd9Sstevel@tonic-gate 	if (isoc_req) {
44667c478bd9Sstevel@tonic-gate 		isoc_pkt_descr = isoc_req->isoc_pkt_descr;
44677c478bd9Sstevel@tonic-gate 		isoc_pkt_count = isoc_req->isoc_pkts_count;
4468b3001defSlg 		isoc_pkts_length = isoc_req->isoc_pkts_length;
44697c478bd9Sstevel@tonic-gate 	} else {
44707c478bd9Sstevel@tonic-gate 		isoc_pkt_descr = ((usb_isoc_req_t *)
4471fffe0b30Sqz 		    pp->pp_client_periodic_in_reqp)->isoc_pkt_descr;
44727c478bd9Sstevel@tonic-gate 		isoc_pkt_count = ((usb_isoc_req_t *)
4473fffe0b30Sqz 		    pp->pp_client_periodic_in_reqp)->isoc_pkts_count;
4474b3001defSlg 		isoc_pkts_length = ((usb_isoc_req_t *)
4475fffe0b30Sqz 		    pp->pp_client_periodic_in_reqp)->isoc_pkts_length;
44767c478bd9Sstevel@tonic-gate 	}
44777c478bd9Sstevel@tonic-gate 
44787c478bd9Sstevel@tonic-gate 	for (ii = 0, length = 0; ii < isoc_pkt_count; ii++) {
44797c478bd9Sstevel@tonic-gate 		length += isoc_pkt_descr->isoc_pkt_length;
44807c478bd9Sstevel@tonic-gate 		isoc_pkt_descr++;
44817c478bd9Sstevel@tonic-gate 	}
44827c478bd9Sstevel@tonic-gate 
4483b3001defSlg 	if ((isoc_pkts_length) && (isoc_pkts_length != length)) {
4484b3001defSlg 
4485b3001defSlg 		USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
4486112116d8Sfb 		    "uhci_start_isoc_receive_polling: isoc_pkts_length 0x%lx "
4487112116d8Sfb 		    "is not equal to the sum of all pkt lengths 0x%lx in "
4488b3001defSlg 		    "an isoc request", isoc_pkts_length, length);
4489b3001defSlg 
4490b3001defSlg 		return (USB_FAILURE);
4491b3001defSlg 	}
4492b3001defSlg 
44937c478bd9Sstevel@tonic-gate 	/* Check the size of isochronous request */
44947c478bd9Sstevel@tonic-gate 	if (length > max_isoc_xfer_size) {
44957c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
44967c478bd9Sstevel@tonic-gate 		    "uhci_start_isoc_receive_polling: "
44977c478bd9Sstevel@tonic-gate 		    "Max isoc request size = %lx, Given isoc req size = %lx",
44987c478bd9Sstevel@tonic-gate 		    max_isoc_xfer_size, length);
44997c478bd9Sstevel@tonic-gate 
45007c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
45017c478bd9Sstevel@tonic-gate 	}
45027c478bd9Sstevel@tonic-gate 
45037c478bd9Sstevel@tonic-gate 	/* Add the TD into the Host Controller's isoc list */
450402acac7eSsl 	error = uhci_insert_isoc_td(uhcip, ph, isoc_req, length, usb_flags);
45057c478bd9Sstevel@tonic-gate 
45067c478bd9Sstevel@tonic-gate 	return (error);
45077c478bd9Sstevel@tonic-gate }
45087c478bd9Sstevel@tonic-gate 
45097c478bd9Sstevel@tonic-gate 
45107c478bd9Sstevel@tonic-gate /*
45117c478bd9Sstevel@tonic-gate  * uhci_remove_isoc_tds_tws
45127c478bd9Sstevel@tonic-gate  *	This routine scans the pipe and removes all the td's
45137c478bd9Sstevel@tonic-gate  *	and transfer wrappers and deallocates the memory
45147c478bd9Sstevel@tonic-gate  *	associated with those td's and tw's.
45157c478bd9Sstevel@tonic-gate  */
45167c478bd9Sstevel@tonic-gate void
uhci_remove_isoc_tds_tws(uhci_state_t * uhcip,uhci_pipe_private_t * pp)45177c478bd9Sstevel@tonic-gate uhci_remove_isoc_tds_tws(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
45187c478bd9Sstevel@tonic-gate {
451902acac7eSsl 	uint_t			rval, i;
45207c478bd9Sstevel@tonic-gate 	uhci_td_t		*tmp_td, *td_head;
45217c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_req;
45227c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tmp_tw, *tw_head;
452302acac7eSsl 	uhci_bulk_isoc_xfer_t	*isoc_xfer_info;
452402acac7eSsl 	uhci_bulk_isoc_td_pool_t *td_pool_ptr;
45257c478bd9Sstevel@tonic-gate 
45267c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
45277c478bd9Sstevel@tonic-gate 	    "uhci_remove_isoc_tds_tws: pp = %p", (void *)pp);
45287c478bd9Sstevel@tonic-gate 
45297c478bd9Sstevel@tonic-gate 	tw_head = pp->pp_tw_head;
45307c478bd9Sstevel@tonic-gate 	while (tw_head) {
45317c478bd9Sstevel@tonic-gate 		tmp_tw = tw_head;
45327c478bd9Sstevel@tonic-gate 		tw_head = tw_head->tw_next;
45337c478bd9Sstevel@tonic-gate 		td_head = tmp_tw->tw_hctd_head;
45347c478bd9Sstevel@tonic-gate 		if (tmp_tw->tw_direction == PID_IN) {
45357c478bd9Sstevel@tonic-gate 			uhci_deallocate_periodic_in_resource(uhcip, pp,
4536fffe0b30Sqz 			    tmp_tw);
45377c478bd9Sstevel@tonic-gate 		} else if (tmp_tw->tw_direction == PID_OUT) {
45387c478bd9Sstevel@tonic-gate 			uhci_hcdi_callback(uhcip, pp, pp->pp_pipe_handle,
45397c478bd9Sstevel@tonic-gate 			    tmp_tw, USB_CR_FLUSHED);
45407c478bd9Sstevel@tonic-gate 		}
45417c478bd9Sstevel@tonic-gate 
45427c478bd9Sstevel@tonic-gate 		while (td_head) {
45437c478bd9Sstevel@tonic-gate 			tmp_td = td_head;
45447c478bd9Sstevel@tonic-gate 			td_head = td_head->tw_td_next;
45457c478bd9Sstevel@tonic-gate 			uhci_delete_isoc_td(uhcip, tmp_td);
45467c478bd9Sstevel@tonic-gate 		}
45477c478bd9Sstevel@tonic-gate 
45487c478bd9Sstevel@tonic-gate 		isoc_req = (usb_isoc_req_t *)tmp_tw->tw_isoc_req;
45497c478bd9Sstevel@tonic-gate 		if (isoc_req) {
45507c478bd9Sstevel@tonic-gate 			usb_free_isoc_req(isoc_req);
45517c478bd9Sstevel@tonic-gate 		}
45527c478bd9Sstevel@tonic-gate 
45537c478bd9Sstevel@tonic-gate 		ASSERT(tmp_tw->tw_hctd_head == NULL);
45547c478bd9Sstevel@tonic-gate 
455502acac7eSsl 		if (tmp_tw->tw_xfer_info.td_pools) {
455602acac7eSsl 			isoc_xfer_info =
455702acac7eSsl 			    (uhci_bulk_isoc_xfer_t *)&tmp_tw->tw_xfer_info;
455802acac7eSsl 			for (i = 0; i < isoc_xfer_info->num_pools; i++) {
455902acac7eSsl 				td_pool_ptr = &isoc_xfer_info->td_pools[i];
456002acac7eSsl 				rval = ddi_dma_unbind_handle(
456102acac7eSsl 				    td_pool_ptr->dma_handle);
456202acac7eSsl 				ASSERT(rval == DDI_SUCCESS);
456302acac7eSsl 				ddi_dma_mem_free(&td_pool_ptr->mem_handle);
456402acac7eSsl 				ddi_dma_free_handle(&td_pool_ptr->dma_handle);
456502acac7eSsl 			}
456602acac7eSsl 			kmem_free(isoc_xfer_info->td_pools,
456702acac7eSsl 			    (sizeof (uhci_bulk_isoc_td_pool_t) *
456802acac7eSsl 			    isoc_xfer_info->num_pools));
45697c478bd9Sstevel@tonic-gate 		}
45707c478bd9Sstevel@tonic-gate 
45717c478bd9Sstevel@tonic-gate 		uhci_deallocate_tw(uhcip, pp, tmp_tw);
45727c478bd9Sstevel@tonic-gate 	}
45737c478bd9Sstevel@tonic-gate }
45747c478bd9Sstevel@tonic-gate 
45757c478bd9Sstevel@tonic-gate 
45767c478bd9Sstevel@tonic-gate /*
45777c478bd9Sstevel@tonic-gate  * uhci_isoc_update_sw_frame_number()
45787c478bd9Sstevel@tonic-gate  *	to avoid code duplication, call uhci_get_sw_frame_number()
45797c478bd9Sstevel@tonic-gate  */
45807c478bd9Sstevel@tonic-gate void
uhci_isoc_update_sw_frame_number(uhci_state_t * uhcip)45817c478bd9Sstevel@tonic-gate uhci_isoc_update_sw_frame_number(uhci_state_t *uhcip)
45827c478bd9Sstevel@tonic-gate {
45837c478bd9Sstevel@tonic-gate 	(void) uhci_get_sw_frame_number(uhcip);
45847c478bd9Sstevel@tonic-gate }
45857c478bd9Sstevel@tonic-gate 
45867c478bd9Sstevel@tonic-gate 
45877c478bd9Sstevel@tonic-gate /*
45887c478bd9Sstevel@tonic-gate  * uhci_get_sw_frame_number:
45897c478bd9Sstevel@tonic-gate  *	Hold the uhci_int_mutex before calling this routine.
45907c478bd9Sstevel@tonic-gate  */
45917c478bd9Sstevel@tonic-gate uint64_t
uhci_get_sw_frame_number(uhci_state_t * uhcip)45927c478bd9Sstevel@tonic-gate uhci_get_sw_frame_number(uhci_state_t *uhcip)
45937c478bd9Sstevel@tonic-gate {
45947c478bd9Sstevel@tonic-gate 	uint64_t sw_frnum, hw_frnum, current_frnum;
45957c478bd9Sstevel@tonic-gate 
45967c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
45977c478bd9Sstevel@tonic-gate 
45987c478bd9Sstevel@tonic-gate 	sw_frnum = uhcip->uhci_sw_frnum;
45997c478bd9Sstevel@tonic-gate 	hw_frnum = Get_OpReg16(FRNUM);
46007c478bd9Sstevel@tonic-gate 
46017c478bd9Sstevel@tonic-gate 	/*
46027c478bd9Sstevel@tonic-gate 	 * Check bit 10 in the software counter and hardware frame counter.
46037c478bd9Sstevel@tonic-gate 	 * If both are same, then don't increment the software frame counter
46047c478bd9Sstevel@tonic-gate 	 * (Bit 10 of hw frame counter toggle for every 1024 frames)
46057c478bd9Sstevel@tonic-gate 	 * The lower 11 bits of software counter contains the hardware frame
46067c478bd9Sstevel@tonic-gate 	 * counter value. The MSB (bit 10) of software counter is incremented
46077c478bd9Sstevel@tonic-gate 	 * for every 1024 frames either here or in get frame number routine.
46087c478bd9Sstevel@tonic-gate 	 */
46097c478bd9Sstevel@tonic-gate 	if ((sw_frnum & UHCI_BIT_10_MASK) == (hw_frnum & UHCI_BIT_10_MASK)) {
46107c478bd9Sstevel@tonic-gate 		/* The MSB of hw counter did not toggle */
46117c478bd9Sstevel@tonic-gate 		current_frnum = ((sw_frnum & (SW_FRNUM_MASK)) | hw_frnum);
46127c478bd9Sstevel@tonic-gate 	} else {
46137c478bd9Sstevel@tonic-gate 		/*
46147c478bd9Sstevel@tonic-gate 		 * The hw counter wrapped around. And the interrupt handler
46157c478bd9Sstevel@tonic-gate 		 * did not get a chance to update the sw frame counter.
46167c478bd9Sstevel@tonic-gate 		 * So, update the sw frame counter and return correct frame no.
46177c478bd9Sstevel@tonic-gate 		 */
46187c478bd9Sstevel@tonic-gate 		sw_frnum >>= UHCI_SIZE_OF_HW_FRNUM - 1;
46197c478bd9Sstevel@tonic-gate 		current_frnum =
46207c478bd9Sstevel@tonic-gate 		    ((++sw_frnum << (UHCI_SIZE_OF_HW_FRNUM - 1)) | hw_frnum);
46217c478bd9Sstevel@tonic-gate 	}
46227c478bd9Sstevel@tonic-gate 	uhcip->uhci_sw_frnum = current_frnum;
46237c478bd9Sstevel@tonic-gate 
46247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
4625112116d8Sfb 	    "uhci_get_sw_frame_number: sw=%lld hd=%lld",
4626112116d8Sfb 	    (unsigned long long)(uhcip->uhci_sw_frnum),
4627112116d8Sfb 	    (unsigned long long)hw_frnum);
46287c478bd9Sstevel@tonic-gate 
46297c478bd9Sstevel@tonic-gate 	return (current_frnum);
46307c478bd9Sstevel@tonic-gate }
46317c478bd9Sstevel@tonic-gate 
46327c478bd9Sstevel@tonic-gate 
46337c478bd9Sstevel@tonic-gate /*
46347c478bd9Sstevel@tonic-gate  * uhci_cmd_timeout_hdlr:
46357c478bd9Sstevel@tonic-gate  *	This routine will get called for every second. It checks for
46367c478bd9Sstevel@tonic-gate  *	timed out control commands/bulk commands. Timeout any commands
46377c478bd9Sstevel@tonic-gate  *	that exceeds the time out period specified by the pipe policy.
46387c478bd9Sstevel@tonic-gate  */
46397c478bd9Sstevel@tonic-gate void
uhci_cmd_timeout_hdlr(void * arg)46407c478bd9Sstevel@tonic-gate uhci_cmd_timeout_hdlr(void *arg)
46417c478bd9Sstevel@tonic-gate {
46427c478bd9Sstevel@tonic-gate 	uint_t			flag = B_FALSE;
46437c478bd9Sstevel@tonic-gate 	uhci_td_t		*head, *tmp_td;
46447c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip = (uhci_state_t *)arg;
46457c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp;
46467c478bd9Sstevel@tonic-gate 
46477c478bd9Sstevel@tonic-gate 	/*
46487c478bd9Sstevel@tonic-gate 	 * Check whether any of the control xfers are timed out.
46497c478bd9Sstevel@tonic-gate 	 * If so, complete those commands with time out as reason.
46507c478bd9Sstevel@tonic-gate 	 */
46517c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
46527c478bd9Sstevel@tonic-gate 	head = uhcip->uhci_outst_tds_head;
46537c478bd9Sstevel@tonic-gate 
46547c478bd9Sstevel@tonic-gate 	while (head) {
46557c478bd9Sstevel@tonic-gate 		/*
46567c478bd9Sstevel@tonic-gate 		 * If timeout out is zero, then dont timeout command.
46577c478bd9Sstevel@tonic-gate 		 */
46587c478bd9Sstevel@tonic-gate 		if (head->tw->tw_timeout_cnt == 0)  {
46597c478bd9Sstevel@tonic-gate 			head = head->outst_td_next;
46607c478bd9Sstevel@tonic-gate 			continue;
46617c478bd9Sstevel@tonic-gate 		}
46627c478bd9Sstevel@tonic-gate 
46637c478bd9Sstevel@tonic-gate 		if (!(head->tw->tw_flags & TW_TIMEOUT_FLAG)) {
46647c478bd9Sstevel@tonic-gate 			head->tw->tw_flags |= TW_TIMEOUT_FLAG;
46657c478bd9Sstevel@tonic-gate 			--head->tw->tw_timeout_cnt;
46667c478bd9Sstevel@tonic-gate 		}
46677c478bd9Sstevel@tonic-gate 
46687c478bd9Sstevel@tonic-gate 		/* only do it for bulk and control TDs */
46697c478bd9Sstevel@tonic-gate 		if ((head->tw->tw_timeout_cnt == 0) &&
46707c478bd9Sstevel@tonic-gate 		    (head->tw->tw_handle_td != uhci_handle_isoc_td)) {
46717c478bd9Sstevel@tonic-gate 
46727c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_ATTA, uhcip->uhci_log_hdl,
46737c478bd9Sstevel@tonic-gate 			    "Command timed out: td = %p", (void *)head);
46747c478bd9Sstevel@tonic-gate 
46757c478bd9Sstevel@tonic-gate 			head->tw->tw_claim = UHCI_TIMEOUT_HDLR_CLAIMED;
46767c478bd9Sstevel@tonic-gate 
46777c478bd9Sstevel@tonic-gate 			/*
46787c478bd9Sstevel@tonic-gate 			 * Check finally whether the command completed
46797c478bd9Sstevel@tonic-gate 			 */
46807c478bd9Sstevel@tonic-gate 			if (GetTD_status(uhcip, head) & UHCI_TD_ACTIVE) {
46817c478bd9Sstevel@tonic-gate 				SetTD32(uhcip, head->link_ptr,
46827c478bd9Sstevel@tonic-gate 				    GetTD32(uhcip, head->link_ptr) |
46837c478bd9Sstevel@tonic-gate 				    HC_END_OF_LIST);
46847c478bd9Sstevel@tonic-gate 				pp = head->tw->tw_pipe_private;
46857c478bd9Sstevel@tonic-gate 				SetQH32(uhcip, pp->pp_qh->element_ptr,
46867c478bd9Sstevel@tonic-gate 				    GetQH32(uhcip, pp->pp_qh->element_ptr) |
46877c478bd9Sstevel@tonic-gate 				    HC_END_OF_LIST);
46887c478bd9Sstevel@tonic-gate 			}
46897c478bd9Sstevel@tonic-gate 
46907c478bd9Sstevel@tonic-gate 			flag = B_TRUE;
46917c478bd9Sstevel@tonic-gate 		}
46927c478bd9Sstevel@tonic-gate 
46937c478bd9Sstevel@tonic-gate 		head = head->outst_td_next;
46947c478bd9Sstevel@tonic-gate 	}
46957c478bd9Sstevel@tonic-gate 
46967c478bd9Sstevel@tonic-gate 	if (flag) {
46977c478bd9Sstevel@tonic-gate 		(void) uhci_wait_for_sof(uhcip);
46987c478bd9Sstevel@tonic-gate 	}
46997c478bd9Sstevel@tonic-gate 
47007c478bd9Sstevel@tonic-gate 	head = uhcip->uhci_outst_tds_head;
47017c478bd9Sstevel@tonic-gate 	while (head) {
47027c478bd9Sstevel@tonic-gate 		if (head->tw->tw_flags & TW_TIMEOUT_FLAG) {
47037c478bd9Sstevel@tonic-gate 			head->tw->tw_flags &= ~TW_TIMEOUT_FLAG;
47047c478bd9Sstevel@tonic-gate 		}
47057c478bd9Sstevel@tonic-gate 		if (head->tw->tw_claim == UHCI_TIMEOUT_HDLR_CLAIMED) {
47067c478bd9Sstevel@tonic-gate 			head->tw->tw_claim = UHCI_NOT_CLAIMED;
47077c478bd9Sstevel@tonic-gate 			tmp_td = head->tw->tw_hctd_head;
47087c478bd9Sstevel@tonic-gate 			while (tmp_td) {
47097c478bd9Sstevel@tonic-gate 				SetTD_status(uhcip, tmp_td,
4710fffe0b30Sqz 				    UHCI_TD_CRC_TIMEOUT);
47117c478bd9Sstevel@tonic-gate 				tmp_td = tmp_td->tw_td_next;
47127c478bd9Sstevel@tonic-gate 			}
47137c478bd9Sstevel@tonic-gate 		}
47147c478bd9Sstevel@tonic-gate 		head = head->outst_td_next;
47157c478bd9Sstevel@tonic-gate 	}
47167c478bd9Sstevel@tonic-gate 
47177c478bd9Sstevel@tonic-gate 	/*
47187c478bd9Sstevel@tonic-gate 	 * Process the td which was completed before shifting from normal
47197c478bd9Sstevel@tonic-gate 	 * mode to polled mode
47207c478bd9Sstevel@tonic-gate 	 */
47217c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_polled_flag == UHCI_POLLED_FLAG_TRUE) {
47227c478bd9Sstevel@tonic-gate 		uhci_process_submitted_td_queue(uhcip);
47237c478bd9Sstevel@tonic-gate 		uhcip->uhci_polled_flag = UHCI_POLLED_FLAG_FALSE;
47247c478bd9Sstevel@tonic-gate 	} else if (flag) {
47257c478bd9Sstevel@tonic-gate 		/* Process the completed/timed out commands */
47267c478bd9Sstevel@tonic-gate 		uhci_process_submitted_td_queue(uhcip);
47277c478bd9Sstevel@tonic-gate 	}
47287c478bd9Sstevel@tonic-gate 
47297c478bd9Sstevel@tonic-gate 	/* Re-register the control/bulk/intr commands' timeout handler */
47307c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_cmd_timeout_id) {
47317c478bd9Sstevel@tonic-gate 		uhcip->uhci_cmd_timeout_id = timeout(uhci_cmd_timeout_hdlr,
47327c478bd9Sstevel@tonic-gate 		    (void *)uhcip, UHCI_ONE_SECOND);
47337c478bd9Sstevel@tonic-gate 	}
47347c478bd9Sstevel@tonic-gate 
47357c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
47367c478bd9Sstevel@tonic-gate }
47377c478bd9Sstevel@tonic-gate 
47387c478bd9Sstevel@tonic-gate 
47397c478bd9Sstevel@tonic-gate /*
47407c478bd9Sstevel@tonic-gate  * uhci_wait_for_sof:
47417c478bd9Sstevel@tonic-gate  *	Wait for the start of the next frame (implying any changes made in the
47427c478bd9Sstevel@tonic-gate  *	lattice have now taken effect).
47437c478bd9Sstevel@tonic-gate  *	To be sure this is the case, we wait for the completion of the current
47447c478bd9Sstevel@tonic-gate  *	frame (which might have already been pending), then another complete
47457c478bd9Sstevel@tonic-gate  *	frame to ensure everything has taken effect.
47467c478bd9Sstevel@tonic-gate  */
47477c478bd9Sstevel@tonic-gate int
uhci_wait_for_sof(uhci_state_t * uhcip)47487c478bd9Sstevel@tonic-gate uhci_wait_for_sof(uhci_state_t *uhcip)
47497c478bd9Sstevel@tonic-gate {
4750fffe0b30Sqz 	int	n, error;
47517c478bd9Sstevel@tonic-gate 	ushort_t    cmd_reg;
47527c478bd9Sstevel@tonic-gate 	usb_frame_number_t	before_frame_number, after_frame_number;
4753*d3d50737SRafael Vanoni 	clock_t	rval;
47547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
4755112116d8Sfb 	    "uhci_wait_for_sof: uhcip = %p", (void *)uhcip);
47567c478bd9Sstevel@tonic-gate 
47577c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
4758fffe0b30Sqz 
4759fffe0b30Sqz 	error = uhci_state_is_operational(uhcip);
4760fffe0b30Sqz 
4761fffe0b30Sqz 	if (error != USB_SUCCESS) {
4762fffe0b30Sqz 
4763fffe0b30Sqz 		return (error);
4764fffe0b30Sqz 	}
4765fffe0b30Sqz 
47667c478bd9Sstevel@tonic-gate 	before_frame_number =  uhci_get_sw_frame_number(uhcip);
47677c478bd9Sstevel@tonic-gate 	for (n = 0; n < MAX_SOF_WAIT_COUNT; n++) {
47687c478bd9Sstevel@tonic-gate 		SetTD_ioc(uhcip, uhcip->uhci_sof_td, 1);
47697c478bd9Sstevel@tonic-gate 		uhcip->uhci_cv_signal = B_TRUE;
47707c478bd9Sstevel@tonic-gate 
4771*d3d50737SRafael Vanoni 		rval = cv_reltimedwait(&uhcip->uhci_cv_SOF,
4772*d3d50737SRafael Vanoni 		    &uhcip->uhci_int_mutex, UHCI_ONE_SECOND, TR_CLOCK_TICK);
47737c478bd9Sstevel@tonic-gate 
47747c478bd9Sstevel@tonic-gate 		after_frame_number = uhci_get_sw_frame_number(uhcip);
47757c478bd9Sstevel@tonic-gate 		if ((rval == -1) &&
47767c478bd9Sstevel@tonic-gate 		    (after_frame_number <= before_frame_number)) {
47777c478bd9Sstevel@tonic-gate 			cmd_reg = Get_OpReg16(USBCMD);
47787c478bd9Sstevel@tonic-gate 			Set_OpReg16(USBCMD, (cmd_reg | USBCMD_REG_HC_RUN));
47797c478bd9Sstevel@tonic-gate 			Set_OpReg16(USBINTR, ENABLE_ALL_INTRS);
47807c478bd9Sstevel@tonic-gate 			after_frame_number = uhci_get_sw_frame_number(uhcip);
47817c478bd9Sstevel@tonic-gate 		}
47827c478bd9Sstevel@tonic-gate 		before_frame_number = after_frame_number;
47837c478bd9Sstevel@tonic-gate 	}
47847c478bd9Sstevel@tonic-gate 
47857c478bd9Sstevel@tonic-gate 	SetTD_ioc(uhcip, uhcip->uhci_sof_td, 0);
47867c478bd9Sstevel@tonic-gate 
47877c478bd9Sstevel@tonic-gate 	return (uhcip->uhci_cv_signal ? USB_FAILURE : USB_SUCCESS);
47887c478bd9Sstevel@tonic-gate 
47897c478bd9Sstevel@tonic-gate }
47907c478bd9Sstevel@tonic-gate 
47917c478bd9Sstevel@tonic-gate /*
47927c478bd9Sstevel@tonic-gate  * uhci_allocate_periodic_in_resource:
47937c478bd9Sstevel@tonic-gate  *	Allocate interrupt/isochronous request structure for the
47947c478bd9Sstevel@tonic-gate  *	interrupt/isochronous IN transfer.
47957c478bd9Sstevel@tonic-gate  */
47967c478bd9Sstevel@tonic-gate int
uhci_allocate_periodic_in_resource(uhci_state_t * uhcip,uhci_pipe_private_t * pp,uhci_trans_wrapper_t * tw,usb_flags_t flags)47977c478bd9Sstevel@tonic-gate uhci_allocate_periodic_in_resource(
47987c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
47997c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp,
48007c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw,
48017c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
48027c478bd9Sstevel@tonic-gate {
48037c478bd9Sstevel@tonic-gate 	size_t			length = 0;
48047c478bd9Sstevel@tonic-gate 	usb_opaque_t		client_periodic_in_reqp;
48057c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*cur_intr_req;
48067c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*curr_isoc_reqp;
48077c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
48087c478bd9Sstevel@tonic-gate 
48097c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
48107c478bd9Sstevel@tonic-gate 	    "uhci_allocate_periodic_in_resource:\n\t"
4811112116d8Sfb 	    "ph = 0x%p, pp = 0x%p, tw = 0x%p, flags = 0x%x",
4812112116d8Sfb 	    (void *)ph, (void *)pp, (void *)tw, flags);
48137c478bd9Sstevel@tonic-gate 
48147c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
48157c478bd9Sstevel@tonic-gate 
48167c478bd9Sstevel@tonic-gate 	/* Check the current periodic in request pointer */
48177c478bd9Sstevel@tonic-gate 	if (tw->tw_curr_xfer_reqp) {
48187c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
48197c478bd9Sstevel@tonic-gate 		    "uhci_allocate_periodic_in_resource: Interrupt "
48207c478bd9Sstevel@tonic-gate 		    "request structure already exists: "
48217c478bd9Sstevel@tonic-gate 		    "allocation failed");
48227c478bd9Sstevel@tonic-gate 
48237c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
48247c478bd9Sstevel@tonic-gate 	}
48257c478bd9Sstevel@tonic-gate 
48267c478bd9Sstevel@tonic-gate 	/* Get the client periodic in request pointer */
48277c478bd9Sstevel@tonic-gate 	client_periodic_in_reqp = pp->pp_client_periodic_in_reqp;
48287c478bd9Sstevel@tonic-gate 
48297c478bd9Sstevel@tonic-gate 	/*
48307c478bd9Sstevel@tonic-gate 	 * If it a periodic IN request and periodic request is NULL,
48317c478bd9Sstevel@tonic-gate 	 * allocate corresponding usb periodic IN request for the
48327c478bd9Sstevel@tonic-gate 	 * current periodic polling request and copy the information
48337c478bd9Sstevel@tonic-gate 	 * from the saved periodic request structure.
48347c478bd9Sstevel@tonic-gate 	 */
48357c478bd9Sstevel@tonic-gate 	if (UHCI_XFER_TYPE(&ph->p_ep) == USB_EP_ATTR_INTR) {
48367c478bd9Sstevel@tonic-gate 		/* Get the interrupt transfer length */
48377c478bd9Sstevel@tonic-gate 		length = ((usb_intr_req_t *)client_periodic_in_reqp)->
4838fffe0b30Sqz 		    intr_len;
48397c478bd9Sstevel@tonic-gate 
48407c478bd9Sstevel@tonic-gate 		cur_intr_req = usba_hcdi_dup_intr_req(ph->p_dip,
48417c478bd9Sstevel@tonic-gate 		    (usb_intr_req_t *)client_periodic_in_reqp, length, flags);
48427c478bd9Sstevel@tonic-gate 		if (cur_intr_req == NULL) {
48437c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
48447c478bd9Sstevel@tonic-gate 			    "uhci_allocate_periodic_in_resource: Interrupt "
48457c478bd9Sstevel@tonic-gate 			    "request structure allocation failed");
48467c478bd9Sstevel@tonic-gate 
48477c478bd9Sstevel@tonic-gate 			return (USB_NO_RESOURCES);
48487c478bd9Sstevel@tonic-gate 		}
48497c478bd9Sstevel@tonic-gate 
48507c478bd9Sstevel@tonic-gate 		/* Check and save the timeout value */
48517c478bd9Sstevel@tonic-gate 		tw->tw_timeout_cnt = (cur_intr_req->intr_attributes &
48527c478bd9Sstevel@tonic-gate 		    USB_ATTRS_ONE_XFER) ? cur_intr_req->intr_timeout: 0;
48537c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp = (usb_opaque_t)cur_intr_req;
48547c478bd9Sstevel@tonic-gate 		tw->tw_length = cur_intr_req->intr_len;
48557c478bd9Sstevel@tonic-gate 	} else {
48567c478bd9Sstevel@tonic-gate 		ASSERT(client_periodic_in_reqp != NULL);
48577c478bd9Sstevel@tonic-gate 
48587c478bd9Sstevel@tonic-gate 		if ((curr_isoc_reqp = usba_hcdi_dup_isoc_req(ph->p_dip,
48597c478bd9Sstevel@tonic-gate 		    (usb_isoc_req_t *)client_periodic_in_reqp, flags)) ==
48607c478bd9Sstevel@tonic-gate 		    NULL) {
48617c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
48627c478bd9Sstevel@tonic-gate 			    "uhci_allocate_periodic_in_resource: Isochronous "
48637c478bd9Sstevel@tonic-gate 			    "request structure allocation failed");
48647c478bd9Sstevel@tonic-gate 
48657c478bd9Sstevel@tonic-gate 			return (USB_NO_RESOURCES);
48667c478bd9Sstevel@tonic-gate 		}
48677c478bd9Sstevel@tonic-gate 
48687c478bd9Sstevel@tonic-gate 		/*
48697c478bd9Sstevel@tonic-gate 		 * Save the client's isochronous request pointer and
48707c478bd9Sstevel@tonic-gate 		 * length of isochronous transfer in transfer wrapper.
48717c478bd9Sstevel@tonic-gate 		 * The dup'ed request is saved in pp_client_periodic_in_reqp
48727c478bd9Sstevel@tonic-gate 		 */
48737c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp =
4874fffe0b30Sqz 		    (usb_opaque_t)pp->pp_client_periodic_in_reqp;
48757c478bd9Sstevel@tonic-gate 		pp->pp_client_periodic_in_reqp = (usb_opaque_t)curr_isoc_reqp;
48767c478bd9Sstevel@tonic-gate 	}
48777c478bd9Sstevel@tonic-gate 
48787c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
48797c478bd9Sstevel@tonic-gate 	ph->p_req_count++;
48807c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
48817c478bd9Sstevel@tonic-gate 
48827c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
48837c478bd9Sstevel@tonic-gate }
48847c478bd9Sstevel@tonic-gate 
48857c478bd9Sstevel@tonic-gate 
48867c478bd9Sstevel@tonic-gate /*
48877c478bd9Sstevel@tonic-gate  * uhci_deallocate_periodic_in_resource:
48887c478bd9Sstevel@tonic-gate  *	Deallocate interrupt/isochronous request structure for the
48897c478bd9Sstevel@tonic-gate  *	interrupt/isochronous IN transfer.
48907c478bd9Sstevel@tonic-gate  */
48917c478bd9Sstevel@tonic-gate void
uhci_deallocate_periodic_in_resource(uhci_state_t * uhcip,uhci_pipe_private_t * pp,uhci_trans_wrapper_t * tw)48927c478bd9Sstevel@tonic-gate uhci_deallocate_periodic_in_resource(
48937c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
48947c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp,
48957c478bd9Sstevel@tonic-gate 	uhci_trans_wrapper_t	*tw)
48967c478bd9Sstevel@tonic-gate {
48977c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_xfer_reqp;
48987c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph = pp->pp_pipe_handle;
48997c478bd9Sstevel@tonic-gate 
49007c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
49017c478bd9Sstevel@tonic-gate 	    "uhci_deallocate_periodic_in_resource: "
4902112116d8Sfb 	    "pp = 0x%p tw = 0x%p", (void *)pp, (void *)tw);
49037c478bd9Sstevel@tonic-gate 
49047c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
49057c478bd9Sstevel@tonic-gate 
49067c478bd9Sstevel@tonic-gate 	curr_xfer_reqp = tw->tw_curr_xfer_reqp;
49077c478bd9Sstevel@tonic-gate 	if (curr_xfer_reqp) {
49087c478bd9Sstevel@tonic-gate 		/*
49097c478bd9Sstevel@tonic-gate 		 * Reset periodic in request usb isoch
49107c478bd9Sstevel@tonic-gate 		 * packet request pointers to null.
49117c478bd9Sstevel@tonic-gate 		 */
49127c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp = NULL;
49137c478bd9Sstevel@tonic-gate 		tw->tw_isoc_req = NULL;
49147c478bd9Sstevel@tonic-gate 
49157c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
49167c478bd9Sstevel@tonic-gate 		ph->p_req_count--;
49177c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
49187c478bd9Sstevel@tonic-gate 
49197c478bd9Sstevel@tonic-gate 		/*
49207c478bd9Sstevel@tonic-gate 		 * Free pre-allocated interrupt or isochronous requests.
49217c478bd9Sstevel@tonic-gate 		 */
49227c478bd9Sstevel@tonic-gate 		switch (UHCI_XFER_TYPE(&ph->p_ep)) {
49237c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
49247c478bd9Sstevel@tonic-gate 			usb_free_intr_req((usb_intr_req_t *)curr_xfer_reqp);
49257c478bd9Sstevel@tonic-gate 			break;
49267c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_ISOCH:
49277c478bd9Sstevel@tonic-gate 			usb_free_isoc_req((usb_isoc_req_t *)curr_xfer_reqp);
49287c478bd9Sstevel@tonic-gate 			break;
49297c478bd9Sstevel@tonic-gate 		}
49307c478bd9Sstevel@tonic-gate 	}
49317c478bd9Sstevel@tonic-gate }
49327c478bd9Sstevel@tonic-gate 
49337c478bd9Sstevel@tonic-gate 
49347c478bd9Sstevel@tonic-gate /*
49357c478bd9Sstevel@tonic-gate  * uhci_hcdi_callback()
49367c478bd9Sstevel@tonic-gate  *	convenience wrapper around usba_hcdi_callback()
49377c478bd9Sstevel@tonic-gate  */
49387c478bd9Sstevel@tonic-gate void
uhci_hcdi_callback(uhci_state_t * uhcip,uhci_pipe_private_t * pp,usba_pipe_handle_data_t * ph,uhci_trans_wrapper_t * tw,usb_cr_t cr)49397c478bd9Sstevel@tonic-gate uhci_hcdi_callback(uhci_state_t *uhcip, uhci_pipe_private_t *pp,
49407c478bd9Sstevel@tonic-gate     usba_pipe_handle_data_t *ph, uhci_trans_wrapper_t *tw, usb_cr_t cr)
49417c478bd9Sstevel@tonic-gate {
49427c478bd9Sstevel@tonic-gate 	usb_opaque_t	curr_xfer_reqp;
49437c478bd9Sstevel@tonic-gate 
49447c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
4945112116d8Sfb 	    "uhci_hcdi_callback: ph = 0x%p, tw = 0x%p, cr = 0x%x",
4946112116d8Sfb 	    (void *)ph, (void *)tw, cr);
49477c478bd9Sstevel@tonic-gate 
49487c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
49497c478bd9Sstevel@tonic-gate 
49507c478bd9Sstevel@tonic-gate 	if (tw && tw->tw_curr_xfer_reqp) {
49517c478bd9Sstevel@tonic-gate 		curr_xfer_reqp = tw->tw_curr_xfer_reqp;
49527c478bd9Sstevel@tonic-gate 		tw->tw_curr_xfer_reqp = NULL;
49537c478bd9Sstevel@tonic-gate 		tw->tw_isoc_req = NULL;
49547c478bd9Sstevel@tonic-gate 	} else {
49557c478bd9Sstevel@tonic-gate 		ASSERT(pp->pp_client_periodic_in_reqp != NULL);
49567c478bd9Sstevel@tonic-gate 
49577c478bd9Sstevel@tonic-gate 		curr_xfer_reqp = pp->pp_client_periodic_in_reqp;
49587c478bd9Sstevel@tonic-gate 		pp->pp_client_periodic_in_reqp = NULL;
49597c478bd9Sstevel@tonic-gate 	}
49607c478bd9Sstevel@tonic-gate 
49617c478bd9Sstevel@tonic-gate 	ASSERT(curr_xfer_reqp != NULL);
49627c478bd9Sstevel@tonic-gate 
49637c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
49647c478bd9Sstevel@tonic-gate 	usba_hcdi_cb(ph, curr_xfer_reqp, cr);
49657c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
49667c478bd9Sstevel@tonic-gate }
49677c478bd9Sstevel@tonic-gate 
49687c478bd9Sstevel@tonic-gate 
4969fffe0b30Sqz /*
4970fffe0b30Sqz  * uhci_state_is_operational:
4971fffe0b30Sqz  *
4972fffe0b30Sqz  * Check the Host controller state and return proper values.
4973fffe0b30Sqz  */
4974fffe0b30Sqz int
uhci_state_is_operational(uhci_state_t * uhcip)4975fffe0b30Sqz uhci_state_is_operational(uhci_state_t	*uhcip)
4976fffe0b30Sqz {
4977fffe0b30Sqz 	int	val;
4978fffe0b30Sqz 
4979fffe0b30Sqz 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
4980fffe0b30Sqz 
4981fffe0b30Sqz 	switch (uhcip->uhci_hc_soft_state) {
4982fffe0b30Sqz 	case UHCI_CTLR_INIT_STATE:
4983fffe0b30Sqz 	case UHCI_CTLR_SUSPEND_STATE:
4984fffe0b30Sqz 		val = USB_FAILURE;
4985fffe0b30Sqz 		break;
4986fffe0b30Sqz 	case UHCI_CTLR_OPERATIONAL_STATE:
4987fffe0b30Sqz 		val = USB_SUCCESS;
4988fffe0b30Sqz 		break;
4989fffe0b30Sqz 	case UHCI_CTLR_ERROR_STATE:
4990fffe0b30Sqz 		val = USB_HC_HARDWARE_ERROR;
4991fffe0b30Sqz 		break;
4992fffe0b30Sqz 	default:
4993fffe0b30Sqz 		val = USB_FAILURE;
4994fffe0b30Sqz 		break;
4995fffe0b30Sqz 	}
4996fffe0b30Sqz 
4997fffe0b30Sqz 	return (val);
4998fffe0b30Sqz }
4999fffe0b30Sqz 
5000fffe0b30Sqz 
50017c478bd9Sstevel@tonic-gate #ifdef DEBUG
50027c478bd9Sstevel@tonic-gate static void
uhci_print_td(uhci_state_t * uhcip,uhci_td_t * td)50037c478bd9Sstevel@tonic-gate uhci_print_td(uhci_state_t *uhcip, uhci_td_t *td)
50047c478bd9Sstevel@tonic-gate {
50057c478bd9Sstevel@tonic-gate 	uint_t	*ptr = (uint_t *)td;
50067c478bd9Sstevel@tonic-gate 
50077c478bd9Sstevel@tonic-gate #ifndef lint
50087c478bd9Sstevel@tonic-gate 	_NOTE(NO_COMPETING_THREADS_NOW);
50097c478bd9Sstevel@tonic-gate #endif
50107c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_DUMPING, uhcip->uhci_log_hdl,
50117c478bd9Sstevel@tonic-gate 	    "\tDWORD 1 0x%x\t DWORD 2 0x%x", ptr[0], ptr[1]);
50127c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_DUMPING, uhcip->uhci_log_hdl,
50137c478bd9Sstevel@tonic-gate 	    "\tDWORD 3 0x%x\t DWORD 4 0x%x", ptr[2], ptr[3]);
50147c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_DUMPING, uhcip->uhci_log_hdl,
50157c478bd9Sstevel@tonic-gate 	    "\tBytes xfered    = %d", td->tw->tw_bytes_xfered);
50167c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_DUMPING, uhcip->uhci_log_hdl,
50177c478bd9Sstevel@tonic-gate 	    "\tBytes Pending   = %d", td->tw->tw_bytes_pending);
50187c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_DUMPING, uhcip->uhci_log_hdl,
50197c478bd9Sstevel@tonic-gate 	    "Queue Head Details:");
50207c478bd9Sstevel@tonic-gate 	uhci_print_qh(uhcip, td->tw->tw_pipe_private->pp_qh);
50217c478bd9Sstevel@tonic-gate 
50227c478bd9Sstevel@tonic-gate #ifndef lint
50237c478bd9Sstevel@tonic-gate 	_NOTE(COMPETING_THREADS_NOW);
50247c478bd9Sstevel@tonic-gate #endif
50257c478bd9Sstevel@tonic-gate }
50267c478bd9Sstevel@tonic-gate 
50277c478bd9Sstevel@tonic-gate 
50287c478bd9Sstevel@tonic-gate static void
uhci_print_qh(uhci_state_t * uhcip,queue_head_t * qh)50297c478bd9Sstevel@tonic-gate uhci_print_qh(uhci_state_t *uhcip, queue_head_t *qh)
50307c478bd9Sstevel@tonic-gate {
50317c478bd9Sstevel@tonic-gate 	uint_t	*ptr = (uint_t *)qh;
50327c478bd9Sstevel@tonic-gate 
50337c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_DUMPING, uhcip->uhci_log_hdl,
50347c478bd9Sstevel@tonic-gate 	    "\tLink Ptr = %x Element Ptr = %x", ptr[0], ptr[1]);
50357c478bd9Sstevel@tonic-gate }
50367c478bd9Sstevel@tonic-gate #endif
5037