1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * Universal Host Controller Driver (UHCI)
31*7c478bd9Sstevel@tonic-gate  *
32*7c478bd9Sstevel@tonic-gate  * The UHCI driver is a driver which interfaces to the Universal
33*7c478bd9Sstevel@tonic-gate  * Serial Bus Driver (USBA) and the Host Controller (HC). The interface to
34*7c478bd9Sstevel@tonic-gate  * the Host Controller is defined by the Universal Host Controller Interface.
35*7c478bd9Sstevel@tonic-gate  * This file contains the code for HCDI entry points.
36*7c478bd9Sstevel@tonic-gate  */
37*7c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/uhci/uhcid.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/uhci/uhcitgt.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/uhci/uhciutil.h>
40*7c478bd9Sstevel@tonic-gate 
41*7c478bd9Sstevel@tonic-gate /* function prototypes */
42*7c478bd9Sstevel@tonic-gate static int	uhci_pipe_send_isoc_data(uhci_state_t *uhcip,
43*7c478bd9Sstevel@tonic-gate 			usba_pipe_handle_data_t *ph, usb_isoc_req_t *isoc_req,
44*7c478bd9Sstevel@tonic-gate 			usb_flags_t usb_flags);
45*7c478bd9Sstevel@tonic-gate static int	uhci_send_intr_data(uhci_state_t *uhcip,
46*7c478bd9Sstevel@tonic-gate 			usba_pipe_handle_data_t	*pipe_handle,
47*7c478bd9Sstevel@tonic-gate 			usb_intr_req_t		*req,
48*7c478bd9Sstevel@tonic-gate 			usb_flags_t		flags);
49*7c478bd9Sstevel@tonic-gate static int	uhci_start_periodic_pipe_polling(uhci_state_t *uhcip,
50*7c478bd9Sstevel@tonic-gate 			usba_pipe_handle_data_t	*ph,
51*7c478bd9Sstevel@tonic-gate 			usb_opaque_t		reqp,
52*7c478bd9Sstevel@tonic-gate 			usb_flags_t		flags);
53*7c478bd9Sstevel@tonic-gate static int	uhci_stop_periodic_pipe_polling(uhci_state_t *uhcip,
54*7c478bd9Sstevel@tonic-gate 			usba_pipe_handle_data_t	*ph,
55*7c478bd9Sstevel@tonic-gate 			usb_flags_t		flags);
56*7c478bd9Sstevel@tonic-gate static void	uhci_update_intr_td_data_toggle(uhci_state_t *uhcip,
57*7c478bd9Sstevel@tonic-gate 			uhci_pipe_private_t *pp);
58*7c478bd9Sstevel@tonic-gate 
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate /* Maximum bulk transfer size */
61*7c478bd9Sstevel@tonic-gate static int uhci_bulk_transfer_size = UHCI_BULK_MAX_XFER_SIZE;
62*7c478bd9Sstevel@tonic-gate 
63*7c478bd9Sstevel@tonic-gate /*
64*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_pipe_open:
65*7c478bd9Sstevel@tonic-gate  *	Member of HCD Ops structure and called during client specific pipe open
66*7c478bd9Sstevel@tonic-gate  *	Add the pipe to the data structure representing the device and allocate
67*7c478bd9Sstevel@tonic-gate  *	bandwidth for the pipe if it is a interrupt or isochronous endpoint.
68*7c478bd9Sstevel@tonic-gate  */
69*7c478bd9Sstevel@tonic-gate int
70*7c478bd9Sstevel@tonic-gate uhci_hcdi_pipe_open(usba_pipe_handle_data_t *ph, usb_flags_t flags)
71*7c478bd9Sstevel@tonic-gate {
72*7c478bd9Sstevel@tonic-gate 	uint_t			node = 0;
73*7c478bd9Sstevel@tonic-gate 	usb_addr_t		usb_addr;
74*7c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip;
75*7c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp;
76*7c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
77*7c478bd9Sstevel@tonic-gate 
78*7c478bd9Sstevel@tonic-gate 	ASSERT(ph);
79*7c478bd9Sstevel@tonic-gate 
80*7c478bd9Sstevel@tonic-gate 	usb_addr = ph->p_usba_device->usb_addr;
81*7c478bd9Sstevel@tonic-gate 	uhcip = uhci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
82*7c478bd9Sstevel@tonic-gate 
83*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
84*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_pipe_open: addr = 0x%x, ep%d", usb_addr,
85*7c478bd9Sstevel@tonic-gate 	    ph->p_ep.bEndpointAddress & USB_EP_NUM_MASK);
86*7c478bd9Sstevel@tonic-gate 
87*7c478bd9Sstevel@tonic-gate 	sema_p(&uhcip->uhci_ocsem);
88*7c478bd9Sstevel@tonic-gate 
89*7c478bd9Sstevel@tonic-gate 	/*
90*7c478bd9Sstevel@tonic-gate 	 * Return failure immediately for any other pipe open on the root hub
91*7c478bd9Sstevel@tonic-gate 	 * except control or interrupt pipe.
92*7c478bd9Sstevel@tonic-gate 	 */
93*7c478bd9Sstevel@tonic-gate 	if (usb_addr == ROOT_HUB_ADDR) {
94*7c478bd9Sstevel@tonic-gate 		switch (UHCI_XFER_TYPE(&ph->p_ep)) {
95*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
96*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
97*7c478bd9Sstevel@tonic-gate 			    "uhci_hcdi_pipe_open: Root hub control pipe");
98*7c478bd9Sstevel@tonic-gate 			break;
99*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
100*7c478bd9Sstevel@tonic-gate 			ASSERT(UHCI_XFER_DIR(&ph->p_ep) == USB_EP_DIR_IN);
101*7c478bd9Sstevel@tonic-gate 
102*7c478bd9Sstevel@tonic-gate 			mutex_enter(&uhcip->uhci_int_mutex);
103*7c478bd9Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_intr_pipe_handle = ph;
104*7c478bd9Sstevel@tonic-gate 
105*7c478bd9Sstevel@tonic-gate 			/*
106*7c478bd9Sstevel@tonic-gate 			 * Set the state of the root hub interrupt
107*7c478bd9Sstevel@tonic-gate 			 * pipe as IDLE.
108*7c478bd9Sstevel@tonic-gate 			 */
109*7c478bd9Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_pipe_state =
110*7c478bd9Sstevel@tonic-gate 						UHCI_PIPE_STATE_IDLE;
111*7c478bd9Sstevel@tonic-gate 
112*7c478bd9Sstevel@tonic-gate 			ASSERT(uhcip->uhci_root_hub.rh_client_intr_req == NULL);
113*7c478bd9Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_client_intr_req = NULL;
114*7c478bd9Sstevel@tonic-gate 
115*7c478bd9Sstevel@tonic-gate 			ASSERT(uhcip->uhci_root_hub.rh_curr_intr_reqp == NULL);
116*7c478bd9Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_curr_intr_reqp = NULL;
117*7c478bd9Sstevel@tonic-gate 
118*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
119*7c478bd9Sstevel@tonic-gate 			    "uhci_hcdi_pipe_open: Root hub interrupt "
120*7c478bd9Sstevel@tonic-gate 			    "pipe open succeeded");
121*7c478bd9Sstevel@tonic-gate 			mutex_exit(&uhcip->uhci_int_mutex);
122*7c478bd9Sstevel@tonic-gate 			sema_v(&uhcip->uhci_ocsem);
123*7c478bd9Sstevel@tonic-gate 
124*7c478bd9Sstevel@tonic-gate 			return (USB_SUCCESS);
125*7c478bd9Sstevel@tonic-gate 		default:
126*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
127*7c478bd9Sstevel@tonic-gate 			    "uhci_hcdi_pipe_open: Root hub pipe open failed");
128*7c478bd9Sstevel@tonic-gate 			sema_v(&uhcip->uhci_ocsem);
129*7c478bd9Sstevel@tonic-gate 
130*7c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
131*7c478bd9Sstevel@tonic-gate 		}
132*7c478bd9Sstevel@tonic-gate 	}
133*7c478bd9Sstevel@tonic-gate 
134*7c478bd9Sstevel@tonic-gate 	/*
135*7c478bd9Sstevel@tonic-gate 	 * A portion of the bandwidth is reserved for the non-periodic
136*7c478bd9Sstevel@tonic-gate 	 * transfers  i.e control and bulk transfers in each  of one
137*7c478bd9Sstevel@tonic-gate 	 * mill second frame period & usually it will be 10% of frame
138*7c478bd9Sstevel@tonic-gate 	 * period. Hence there is no need to check for the available
139*7c478bd9Sstevel@tonic-gate 	 * bandwidth before adding the control or bulk endpoints.
140*7c478bd9Sstevel@tonic-gate 	 *
141*7c478bd9Sstevel@tonic-gate 	 * There is a need to check for the available bandwidth before
142*7c478bd9Sstevel@tonic-gate 	 * adding the periodic transfers i.e interrupt & isochronous, since
143*7c478bd9Sstevel@tonic-gate 	 * all these periodic transfers are guaranteed transfers. Usually,
144*7c478bd9Sstevel@tonic-gate 	 * 90% of the total frame time is reserved for periodic transfers.
145*7c478bd9Sstevel@tonic-gate 	 */
146*7c478bd9Sstevel@tonic-gate 	if (UHCI_PERIODIC_ENDPOINT(&ph->p_ep)) {
147*7c478bd9Sstevel@tonic-gate 		/* Zero Max Packet size endpoints are not supported */
148*7c478bd9Sstevel@tonic-gate 		if (ph->p_ep.wMaxPacketSize == 0) {
149*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
150*7c478bd9Sstevel@tonic-gate 			    "uhci_hcdi_pipe_open: Zero length packet");
151*7c478bd9Sstevel@tonic-gate 			sema_v(&uhcip->uhci_ocsem);
152*7c478bd9Sstevel@tonic-gate 
153*7c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
154*7c478bd9Sstevel@tonic-gate 		}
155*7c478bd9Sstevel@tonic-gate 
156*7c478bd9Sstevel@tonic-gate 		mutex_enter(&uhcip->uhci_int_mutex);
157*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
158*7c478bd9Sstevel@tonic-gate 
159*7c478bd9Sstevel@tonic-gate 		error = uhci_allocate_bandwidth(uhcip, ph, &node);
160*7c478bd9Sstevel@tonic-gate 		if (error != USB_SUCCESS) {
161*7c478bd9Sstevel@tonic-gate 
162*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
163*7c478bd9Sstevel@tonic-gate 			    "uhci_hcdi_pipe_open: Bandwidth allocation failed");
164*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ph->p_mutex);
165*7c478bd9Sstevel@tonic-gate 			mutex_exit(&uhcip->uhci_int_mutex);
166*7c478bd9Sstevel@tonic-gate 			sema_v(&uhcip->uhci_ocsem);
167*7c478bd9Sstevel@tonic-gate 
168*7c478bd9Sstevel@tonic-gate 			return (error);
169*7c478bd9Sstevel@tonic-gate 		}
170*7c478bd9Sstevel@tonic-gate 
171*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
172*7c478bd9Sstevel@tonic-gate 		mutex_exit(&uhcip->uhci_int_mutex);
173*7c478bd9Sstevel@tonic-gate 	}
174*7c478bd9Sstevel@tonic-gate 
175*7c478bd9Sstevel@tonic-gate 	/* Create the HCD pipe private structure */
176*7c478bd9Sstevel@tonic-gate 	pp = kmem_zalloc(sizeof (uhci_pipe_private_t),
177*7c478bd9Sstevel@tonic-gate 	    (flags & USB_FLAGS_SLEEP) ? KM_SLEEP : KM_NOSLEEP);
178*7c478bd9Sstevel@tonic-gate 	if (pp == NULL) {
179*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
180*7c478bd9Sstevel@tonic-gate 		    "uhci_hcdi_pipe_open: pp allocation failure");
181*7c478bd9Sstevel@tonic-gate 
182*7c478bd9Sstevel@tonic-gate 		if (UHCI_PERIODIC_ENDPOINT(&ph->p_ep)) {
183*7c478bd9Sstevel@tonic-gate 			mutex_enter(&uhcip->uhci_int_mutex);
184*7c478bd9Sstevel@tonic-gate 			uhci_deallocate_bandwidth(uhcip, ph);
185*7c478bd9Sstevel@tonic-gate 			mutex_exit(&uhcip->uhci_int_mutex);
186*7c478bd9Sstevel@tonic-gate 		}
187*7c478bd9Sstevel@tonic-gate 		sema_v(&uhcip->uhci_ocsem);
188*7c478bd9Sstevel@tonic-gate 
189*7c478bd9Sstevel@tonic-gate 		return (USB_NO_RESOURCES);
190*7c478bd9Sstevel@tonic-gate 	}
191*7c478bd9Sstevel@tonic-gate 
192*7c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
193*7c478bd9Sstevel@tonic-gate 	pp->pp_node = node;	/* Store the node in the interrupt lattice */
194*7c478bd9Sstevel@tonic-gate 
195*7c478bd9Sstevel@tonic-gate 	/* Initialize frame number */
196*7c478bd9Sstevel@tonic-gate 	pp->pp_frame_num = INVALID_FRNUM;
197*7c478bd9Sstevel@tonic-gate 
198*7c478bd9Sstevel@tonic-gate 	/* Set the state of pipe as IDLE */
199*7c478bd9Sstevel@tonic-gate 	pp->pp_state = UHCI_PIPE_STATE_IDLE;
200*7c478bd9Sstevel@tonic-gate 
201*7c478bd9Sstevel@tonic-gate 	/* Store a pointer to the pipe handle */
202*7c478bd9Sstevel@tonic-gate 	pp->pp_pipe_handle = ph;
203*7c478bd9Sstevel@tonic-gate 
204*7c478bd9Sstevel@tonic-gate 	/* Store the pointer in the pipe handle */
205*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
206*7c478bd9Sstevel@tonic-gate 	ph->p_hcd_private = (usb_opaque_t)pp;
207*7c478bd9Sstevel@tonic-gate 
208*7c478bd9Sstevel@tonic-gate 	/* Store a copy of the pipe policy */
209*7c478bd9Sstevel@tonic-gate 	bcopy(&ph->p_policy, &pp->pp_policy, sizeof (usb_pipe_policy_t));
210*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
211*7c478bd9Sstevel@tonic-gate 
212*7c478bd9Sstevel@tonic-gate 	/* don't check for ROOT_HUB here anymore */
213*7c478bd9Sstevel@tonic-gate 	if (UHCI_XFER_TYPE(&ph->p_ep) != USB_EP_ATTR_ISOCH) {
214*7c478bd9Sstevel@tonic-gate 		/* Allocate the host controller endpoint descriptor */
215*7c478bd9Sstevel@tonic-gate 		pp->pp_qh = uhci_alloc_queue_head(uhcip);
216*7c478bd9Sstevel@tonic-gate 
217*7c478bd9Sstevel@tonic-gate 		if (pp->pp_qh == NULL) {
218*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
219*7c478bd9Sstevel@tonic-gate 			    "uhci_hcdi_pipe_open: QH allocation failed");
220*7c478bd9Sstevel@tonic-gate 
221*7c478bd9Sstevel@tonic-gate 			mutex_enter(&ph->p_mutex);
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate 			/*
224*7c478bd9Sstevel@tonic-gate 			 * Deallocate the hcd private portion
225*7c478bd9Sstevel@tonic-gate 			 * of the pipe handle.
226*7c478bd9Sstevel@tonic-gate 			 */
227*7c478bd9Sstevel@tonic-gate 			kmem_free(ph->p_hcd_private,
228*7c478bd9Sstevel@tonic-gate 				sizeof (uhci_pipe_private_t));
229*7c478bd9Sstevel@tonic-gate 
230*7c478bd9Sstevel@tonic-gate 			/*
231*7c478bd9Sstevel@tonic-gate 			 * Set the private structure in the
232*7c478bd9Sstevel@tonic-gate 			 * pipe handle equal to NULL.
233*7c478bd9Sstevel@tonic-gate 			 */
234*7c478bd9Sstevel@tonic-gate 			ph->p_hcd_private = NULL;
235*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ph->p_mutex);
236*7c478bd9Sstevel@tonic-gate 
237*7c478bd9Sstevel@tonic-gate 			if (UHCI_PERIODIC_ENDPOINT(&ph->p_ep)) {
238*7c478bd9Sstevel@tonic-gate 				uhci_deallocate_bandwidth(uhcip, ph);
239*7c478bd9Sstevel@tonic-gate 			}
240*7c478bd9Sstevel@tonic-gate 			mutex_exit(&uhcip->uhci_int_mutex);
241*7c478bd9Sstevel@tonic-gate 
242*7c478bd9Sstevel@tonic-gate 			sema_v(&uhcip->uhci_ocsem);
243*7c478bd9Sstevel@tonic-gate 
244*7c478bd9Sstevel@tonic-gate 			return (USB_NO_RESOURCES);
245*7c478bd9Sstevel@tonic-gate 		}
246*7c478bd9Sstevel@tonic-gate 
247*7c478bd9Sstevel@tonic-gate 		/*
248*7c478bd9Sstevel@tonic-gate 		 * Insert the endpoint onto the host controller's
249*7c478bd9Sstevel@tonic-gate 		 * appropriate endpoint list. The host controller
250*7c478bd9Sstevel@tonic-gate 		 * will not schedule this endpoint until there are
251*7c478bd9Sstevel@tonic-gate 		 * any TD's to process.
252*7c478bd9Sstevel@tonic-gate 		 */
253*7c478bd9Sstevel@tonic-gate 		uhci_insert_qh(uhcip, ph);
254*7c478bd9Sstevel@tonic-gate 	}
255*7c478bd9Sstevel@tonic-gate 
256*7c478bd9Sstevel@tonic-gate 	/*
257*7c478bd9Sstevel@tonic-gate 	 * Restore the data toggle from usb device structure.
258*7c478bd9Sstevel@tonic-gate 	 */
259*7c478bd9Sstevel@tonic-gate 	if (UHCI_PERIODIC_ENDPOINT(&ph->p_ep)) {
260*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
261*7c478bd9Sstevel@tonic-gate 
262*7c478bd9Sstevel@tonic-gate 		pp->pp_data_toggle = usba_hcdi_get_data_toggle(
263*7c478bd9Sstevel@tonic-gate 			ph->p_usba_device, ph->p_ep.bEndpointAddress);
264*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
265*7c478bd9Sstevel@tonic-gate 	}
266*7c478bd9Sstevel@tonic-gate 
267*7c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
268*7c478bd9Sstevel@tonic-gate 	sema_v(&uhcip->uhci_ocsem);
269*7c478bd9Sstevel@tonic-gate 
270*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
271*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_pipe_open: ph = 0x%p", ph);
272*7c478bd9Sstevel@tonic-gate 
273*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
274*7c478bd9Sstevel@tonic-gate }
275*7c478bd9Sstevel@tonic-gate 
276*7c478bd9Sstevel@tonic-gate 
277*7c478bd9Sstevel@tonic-gate /*
278*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_pipe_close:
279*7c478bd9Sstevel@tonic-gate  *	Member of HCD Ops structure and called during the client specific pipe
280*7c478bd9Sstevel@tonic-gate  *	close. Remove the pipe to the data structure representing the device
281*7c478bd9Sstevel@tonic-gate  *	deallocate bandwidth for the pipe if it is an intr or isoch endpoint.
282*7c478bd9Sstevel@tonic-gate  */
283*7c478bd9Sstevel@tonic-gate int
284*7c478bd9Sstevel@tonic-gate uhci_hcdi_pipe_close(usba_pipe_handle_data_t *ph, usb_flags_t usb_flags)
285*7c478bd9Sstevel@tonic-gate {
286*7c478bd9Sstevel@tonic-gate 	usb_addr_t		usb_addr;
287*7c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip;
288*7c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
289*7c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp;
290*7c478bd9Sstevel@tonic-gate 
291*7c478bd9Sstevel@tonic-gate 	uhcip = uhci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
292*7c478bd9Sstevel@tonic-gate 	pp = (uhci_pipe_private_t *)ph->p_hcd_private;
293*7c478bd9Sstevel@tonic-gate 	usb_addr = ph->p_usba_device->usb_addr;
294*7c478bd9Sstevel@tonic-gate 
295*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
296*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_pipe_close: addr = 0x%x, ep%d, flags = 0x%x", usb_addr,
297*7c478bd9Sstevel@tonic-gate 	    eptd->bEndpointAddress, usb_flags);
298*7c478bd9Sstevel@tonic-gate 
299*7c478bd9Sstevel@tonic-gate 	sema_p(&uhcip->uhci_ocsem);
300*7c478bd9Sstevel@tonic-gate 
301*7c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate 	/*
304*7c478bd9Sstevel@tonic-gate 	 * Check whether the pipe is a root hub
305*7c478bd9Sstevel@tonic-gate 	 */
306*7c478bd9Sstevel@tonic-gate 	if (usb_addr == ROOT_HUB_ADDR) {
307*7c478bd9Sstevel@tonic-gate 		switch (UHCI_XFER_TYPE(eptd)) {
308*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
309*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
310*7c478bd9Sstevel@tonic-gate 			    "uhci_hcdi_pipe_close: Root hub control pipe "
311*7c478bd9Sstevel@tonic-gate 			    "close succeeded");
312*7c478bd9Sstevel@tonic-gate 
313*7c478bd9Sstevel@tonic-gate 			break;
314*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
315*7c478bd9Sstevel@tonic-gate 			ASSERT((eptd->bEndpointAddress &
316*7c478bd9Sstevel@tonic-gate 				USB_EP_NUM_MASK) == 1);
317*7c478bd9Sstevel@tonic-gate 
318*7c478bd9Sstevel@tonic-gate 			ASSERT(uhcip->uhci_root_hub.rh_pipe_state ==
319*7c478bd9Sstevel@tonic-gate 					UHCI_PIPE_STATE_ACTIVE);
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 			/* Do interrupt pipe cleanup */
322*7c478bd9Sstevel@tonic-gate 			uhci_root_hub_intr_pipe_cleanup(uhcip,
323*7c478bd9Sstevel@tonic-gate 						USB_CR_PIPE_CLOSING);
324*7c478bd9Sstevel@tonic-gate 
325*7c478bd9Sstevel@tonic-gate 			ASSERT(uhcip->uhci_root_hub.rh_pipe_state ==
326*7c478bd9Sstevel@tonic-gate 					UHCI_PIPE_STATE_IDLE);
327*7c478bd9Sstevel@tonic-gate 
328*7c478bd9Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_intr_pipe_handle = NULL;
329*7c478bd9Sstevel@tonic-gate 
330*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
331*7c478bd9Sstevel@tonic-gate 			    "uhci_hcdi_pipe_close: Root hub interrupt "
332*7c478bd9Sstevel@tonic-gate 			    "pipe close succeeded");
333*7c478bd9Sstevel@tonic-gate 
334*7c478bd9Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_pipe_state =
335*7c478bd9Sstevel@tonic-gate 					UHCI_PIPE_STATE_IDLE;
336*7c478bd9Sstevel@tonic-gate 
337*7c478bd9Sstevel@tonic-gate 			mutex_exit(&uhcip->uhci_int_mutex);
338*7c478bd9Sstevel@tonic-gate 			sema_v(&uhcip->uhci_ocsem);
339*7c478bd9Sstevel@tonic-gate 
340*7c478bd9Sstevel@tonic-gate 			return (USB_SUCCESS);
341*7c478bd9Sstevel@tonic-gate 		}
342*7c478bd9Sstevel@tonic-gate 	} else {
343*7c478bd9Sstevel@tonic-gate 		/*
344*7c478bd9Sstevel@tonic-gate 		 * Stop all the transactions if it is not the root hub.
345*7c478bd9Sstevel@tonic-gate 		 */
346*7c478bd9Sstevel@tonic-gate 		if (UHCI_XFER_TYPE(eptd) == USB_EP_ATTR_INTR) {
347*7c478bd9Sstevel@tonic-gate 			/*
348*7c478bd9Sstevel@tonic-gate 			 * Stop polling on the pipe to prevent any subsequently
349*7c478bd9Sstevel@tonic-gate 			 * queued tds (while we're waiting for SOF, below)
350*7c478bd9Sstevel@tonic-gate 			 * from being executed
351*7c478bd9Sstevel@tonic-gate 			 */
352*7c478bd9Sstevel@tonic-gate 			pp->pp_state = UHCI_PIPE_STATE_IDLE;
353*7c478bd9Sstevel@tonic-gate 		}
354*7c478bd9Sstevel@tonic-gate 
355*7c478bd9Sstevel@tonic-gate 		/* Disable all outstanding tds */
356*7c478bd9Sstevel@tonic-gate 		uhci_modify_td_active_bits(uhcip, pp);
357*7c478bd9Sstevel@tonic-gate 
358*7c478bd9Sstevel@tonic-gate 		/* Prevent this queue from being executed */
359*7c478bd9Sstevel@tonic-gate 		if (UHCI_XFER_TYPE(eptd) != USB_EP_ATTR_ISOCH) {
360*7c478bd9Sstevel@tonic-gate 			UHCI_SET_TERMINATE_BIT(pp->pp_qh->element_ptr);
361*7c478bd9Sstevel@tonic-gate 		}
362*7c478bd9Sstevel@tonic-gate 
363*7c478bd9Sstevel@tonic-gate 		/* Wait for the next start of frame */
364*7c478bd9Sstevel@tonic-gate 		(void) uhci_wait_for_sof(uhcip);
365*7c478bd9Sstevel@tonic-gate 
366*7c478bd9Sstevel@tonic-gate 		ASSERT(eptd != NULL);
367*7c478bd9Sstevel@tonic-gate 
368*7c478bd9Sstevel@tonic-gate 		switch (UHCI_XFER_TYPE(eptd)) {
369*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
370*7c478bd9Sstevel@tonic-gate 			uhci_update_intr_td_data_toggle(uhcip, pp);
371*7c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
372*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
373*7c478bd9Sstevel@tonic-gate 			uhci_remove_tds_tws(uhcip, ph);
374*7c478bd9Sstevel@tonic-gate 			break;
375*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_BULK:
376*7c478bd9Sstevel@tonic-gate 			SetQH32(uhcip, pp->pp_qh->element_ptr,
377*7c478bd9Sstevel@tonic-gate 			    TD_PADDR(pp->pp_qh->td_tailp));
378*7c478bd9Sstevel@tonic-gate 			uhci_remove_bulk_tds_tws(uhcip, pp, UHCI_IN_CLOSE);
379*7c478bd9Sstevel@tonic-gate 			uhci_save_data_toggle(pp);
380*7c478bd9Sstevel@tonic-gate 			break;
381*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_ISOCH:
382*7c478bd9Sstevel@tonic-gate 			uhci_remove_isoc_tds_tws(uhcip, pp);
383*7c478bd9Sstevel@tonic-gate 			break;
384*7c478bd9Sstevel@tonic-gate 		default:
385*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
386*7c478bd9Sstevel@tonic-gate 			    "uhci_hcdi_pipe_close: Unknown xfer type");
387*7c478bd9Sstevel@tonic-gate 			break;
388*7c478bd9Sstevel@tonic-gate 		}
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate 		/*
391*7c478bd9Sstevel@tonic-gate 		 * Remove the endoint descriptor from Host Controller's
392*7c478bd9Sstevel@tonic-gate 		 * appropriate endpoint list. Isochronous pipes dont have
393*7c478bd9Sstevel@tonic-gate 		 * any queue heads attached to it.
394*7c478bd9Sstevel@tonic-gate 		 */
395*7c478bd9Sstevel@tonic-gate 		if (UHCI_XFER_TYPE(eptd) != USB_EP_ATTR_ISOCH) {
396*7c478bd9Sstevel@tonic-gate 			uhci_remove_qh(uhcip, pp);
397*7c478bd9Sstevel@tonic-gate 		}
398*7c478bd9Sstevel@tonic-gate 
399*7c478bd9Sstevel@tonic-gate 		/*
400*7c478bd9Sstevel@tonic-gate 		 * Do the callback for the original client
401*7c478bd9Sstevel@tonic-gate 		 * periodic IN request.
402*7c478bd9Sstevel@tonic-gate 		 */
403*7c478bd9Sstevel@tonic-gate 		if (pp->pp_client_periodic_in_reqp) {
404*7c478bd9Sstevel@tonic-gate 			uhci_hcdi_callback(uhcip, pp, ph, NULL,
405*7c478bd9Sstevel@tonic-gate 			    USB_CR_PIPE_CLOSING);
406*7c478bd9Sstevel@tonic-gate 		}
407*7c478bd9Sstevel@tonic-gate 
408*7c478bd9Sstevel@tonic-gate 		/* Deallocate bandwidth */
409*7c478bd9Sstevel@tonic-gate 		if (UHCI_PERIODIC_ENDPOINT(eptd)) {
410*7c478bd9Sstevel@tonic-gate 			mutex_enter(&ph->p_mutex);
411*7c478bd9Sstevel@tonic-gate 			uhci_deallocate_bandwidth(uhcip, ph);
412*7c478bd9Sstevel@tonic-gate 			mutex_exit(&ph->p_mutex);
413*7c478bd9Sstevel@tonic-gate 		}
414*7c478bd9Sstevel@tonic-gate 	}
415*7c478bd9Sstevel@tonic-gate 
416*7c478bd9Sstevel@tonic-gate 	/* Deallocate the hcd private portion of the pipe handle.  */
417*7c478bd9Sstevel@tonic-gate 
418*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ph->p_mutex);
419*7c478bd9Sstevel@tonic-gate 	kmem_free(ph->p_hcd_private, sizeof (uhci_pipe_private_t));
420*7c478bd9Sstevel@tonic-gate 	ph->p_hcd_private = NULL;
421*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ph->p_mutex);
422*7c478bd9Sstevel@tonic-gate 
423*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
424*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_pipe_close: ph = 0x%p", ph);
425*7c478bd9Sstevel@tonic-gate 
426*7c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
427*7c478bd9Sstevel@tonic-gate 	sema_v(&uhcip->uhci_ocsem);
428*7c478bd9Sstevel@tonic-gate 
429*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
430*7c478bd9Sstevel@tonic-gate }
431*7c478bd9Sstevel@tonic-gate 
432*7c478bd9Sstevel@tonic-gate 
433*7c478bd9Sstevel@tonic-gate /*
434*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_pipe_reset:
435*7c478bd9Sstevel@tonic-gate  */
436*7c478bd9Sstevel@tonic-gate int
437*7c478bd9Sstevel@tonic-gate uhci_hcdi_pipe_reset(usba_pipe_handle_data_t *ph, usb_flags_t usb_flags)
438*7c478bd9Sstevel@tonic-gate {
439*7c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip = uhci_obtain_state(
440*7c478bd9Sstevel@tonic-gate 					ph->p_usba_device->usb_root_hub_dip);
441*7c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp = (uhci_pipe_private_t *)ph->p_hcd_private;
442*7c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
443*7c478bd9Sstevel@tonic-gate 
444*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
445*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_pipe_reset: usb_flags = 0x%x", usb_flags);
446*7c478bd9Sstevel@tonic-gate 
447*7c478bd9Sstevel@tonic-gate 	/*
448*7c478bd9Sstevel@tonic-gate 	 * Return failure immediately for any other pipe reset on the root
449*7c478bd9Sstevel@tonic-gate 	 * hub except control or interrupt pipe.
450*7c478bd9Sstevel@tonic-gate 	 */
451*7c478bd9Sstevel@tonic-gate 	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
452*7c478bd9Sstevel@tonic-gate 		switch (UHCI_XFER_TYPE(&ph->p_ep)) {
453*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
454*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
455*7c478bd9Sstevel@tonic-gate 			    "uhci_hcdi_pipe_reset: Pipe reset for root"
456*7c478bd9Sstevel@tonic-gate 			    "hub control pipe successful");
457*7c478bd9Sstevel@tonic-gate 
458*7c478bd9Sstevel@tonic-gate 			break;
459*7c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
460*7c478bd9Sstevel@tonic-gate 			mutex_enter(&uhcip->uhci_int_mutex);
461*7c478bd9Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_pipe_state =
462*7c478bd9Sstevel@tonic-gate 							UHCI_PIPE_STATE_IDLE;
463*7c478bd9Sstevel@tonic-gate 
464*7c478bd9Sstevel@tonic-gate 			/* Do interrupt pipe cleanup */
465*7c478bd9Sstevel@tonic-gate 			uhci_root_hub_intr_pipe_cleanup(uhcip,
466*7c478bd9Sstevel@tonic-gate 						USB_CR_PIPE_RESET);
467*7c478bd9Sstevel@tonic-gate 
468*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
469*7c478bd9Sstevel@tonic-gate 			    "uhci_hcdi_pipe_reset: Pipe reset for "
470*7c478bd9Sstevel@tonic-gate 			    "root hub interrupt pipe successful");
471*7c478bd9Sstevel@tonic-gate 			mutex_exit(&uhcip->uhci_int_mutex);
472*7c478bd9Sstevel@tonic-gate 
473*7c478bd9Sstevel@tonic-gate 			break;
474*7c478bd9Sstevel@tonic-gate 		default:
475*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
476*7c478bd9Sstevel@tonic-gate 			    "uhci_hcdi_pipe_close: Root hub pipe reset failed");
477*7c478bd9Sstevel@tonic-gate 
478*7c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
479*7c478bd9Sstevel@tonic-gate 		}
480*7c478bd9Sstevel@tonic-gate 
481*7c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
482*7c478bd9Sstevel@tonic-gate 	}
483*7c478bd9Sstevel@tonic-gate 
484*7c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
485*7c478bd9Sstevel@tonic-gate 
486*7c478bd9Sstevel@tonic-gate 	/*
487*7c478bd9Sstevel@tonic-gate 	 * Set the active bit in to INACTIVE for all the remaining TD's of
488*7c478bd9Sstevel@tonic-gate 	 * this end point.  Set the active bit for the dummy td. This will
489*7c478bd9Sstevel@tonic-gate 	 * generate an interrupt at the end of the frame.  After receiving
490*7c478bd9Sstevel@tonic-gate 	 * the interrupt, it is safe to to manipulate the lattice.
491*7c478bd9Sstevel@tonic-gate 	 */
492*7c478bd9Sstevel@tonic-gate 	uhci_modify_td_active_bits(uhcip, pp);
493*7c478bd9Sstevel@tonic-gate 
494*7c478bd9Sstevel@tonic-gate 	/* Initialize the element pointer */
495*7c478bd9Sstevel@tonic-gate 	if (UHCI_XFER_TYPE(eptd) != USB_EP_ATTR_ISOCH) {
496*7c478bd9Sstevel@tonic-gate 		UHCI_SET_TERMINATE_BIT(pp->pp_qh->element_ptr);
497*7c478bd9Sstevel@tonic-gate 		SetQH32(uhcip, pp->pp_qh->element_ptr,
498*7c478bd9Sstevel@tonic-gate 		    TD_PADDR(pp->pp_qh->td_tailp));
499*7c478bd9Sstevel@tonic-gate 	}
500*7c478bd9Sstevel@tonic-gate 
501*7c478bd9Sstevel@tonic-gate 	(void) uhci_wait_for_sof(uhcip);
502*7c478bd9Sstevel@tonic-gate 
503*7c478bd9Sstevel@tonic-gate 	/*
504*7c478bd9Sstevel@tonic-gate 	 * Save the data toggle and clear the pipe.
505*7c478bd9Sstevel@tonic-gate 	 */
506*7c478bd9Sstevel@tonic-gate 	switch (UHCI_XFER_TYPE(eptd)) {
507*7c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
508*7c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
509*7c478bd9Sstevel@tonic-gate 		uhci_remove_tds_tws(uhcip, ph);
510*7c478bd9Sstevel@tonic-gate 		break;
511*7c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_BULK:
512*7c478bd9Sstevel@tonic-gate 		SetQH32(uhcip, pp->pp_qh->element_ptr,
513*7c478bd9Sstevel@tonic-gate 		    TD_PADDR(pp->pp_qh->td_tailp));
514*7c478bd9Sstevel@tonic-gate 		uhci_remove_bulk_tds_tws(uhcip, pp, UHCI_IN_RESET);
515*7c478bd9Sstevel@tonic-gate 		break;
516*7c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_ISOCH:
517*7c478bd9Sstevel@tonic-gate 		uhci_remove_isoc_tds_tws(uhcip, pp);
518*7c478bd9Sstevel@tonic-gate 		break;
519*7c478bd9Sstevel@tonic-gate 	default:
520*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
521*7c478bd9Sstevel@tonic-gate 		    "uhci_hcdi_pipe_reset: Unknown xfer type");
522*7c478bd9Sstevel@tonic-gate 		break;
523*7c478bd9Sstevel@tonic-gate 	}
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate 	/*
526*7c478bd9Sstevel@tonic-gate 	 * Do the callback for the original client
527*7c478bd9Sstevel@tonic-gate 	 * periodic IN request.
528*7c478bd9Sstevel@tonic-gate 	 */
529*7c478bd9Sstevel@tonic-gate 	if (pp->pp_client_periodic_in_reqp) {
530*7c478bd9Sstevel@tonic-gate 		uhci_hcdi_callback(uhcip, pp, ph, NULL, USB_CR_PIPE_RESET);
531*7c478bd9Sstevel@tonic-gate 	}
532*7c478bd9Sstevel@tonic-gate 
533*7c478bd9Sstevel@tonic-gate 	/*
534*7c478bd9Sstevel@tonic-gate 	 * Since the endpoint is stripped of Transfer Descriptors (TD),
535*7c478bd9Sstevel@tonic-gate 	 * reset the state of the periodic pipe to IDLE.
536*7c478bd9Sstevel@tonic-gate 	 */
537*7c478bd9Sstevel@tonic-gate 	pp->pp_state = UHCI_PIPE_STATE_IDLE;
538*7c478bd9Sstevel@tonic-gate 
539*7c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
540*7c478bd9Sstevel@tonic-gate 
541*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
542*7c478bd9Sstevel@tonic-gate }
543*7c478bd9Sstevel@tonic-gate 
544*7c478bd9Sstevel@tonic-gate 
545*7c478bd9Sstevel@tonic-gate /*
546*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_pipe_ctrl_xfer:
547*7c478bd9Sstevel@tonic-gate  */
548*7c478bd9Sstevel@tonic-gate int
549*7c478bd9Sstevel@tonic-gate uhci_hcdi_pipe_ctrl_xfer(
550*7c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
551*7c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp,
552*7c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
553*7c478bd9Sstevel@tonic-gate {
554*7c478bd9Sstevel@tonic-gate 	uhci_state_t *uhcip = uhci_obtain_state(
555*7c478bd9Sstevel@tonic-gate 				ph->p_usba_device->usb_root_hub_dip);
556*7c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t *pp = (uhci_pipe_private_t *)ph->p_hcd_private;
557*7c478bd9Sstevel@tonic-gate 	int error = USB_SUCCESS;
558*7c478bd9Sstevel@tonic-gate 
559*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
560*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_pipe_ctrl_xfer: req=0x%p, ph=0x%p, flags=0x%x",
561*7c478bd9Sstevel@tonic-gate 	    ctrl_reqp, ph, flags);
562*7c478bd9Sstevel@tonic-gate 
563*7c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
564*7c478bd9Sstevel@tonic-gate 
565*7c478bd9Sstevel@tonic-gate 	ASSERT(pp->pp_state == UHCI_PIPE_STATE_IDLE);
566*7c478bd9Sstevel@tonic-gate 
567*7c478bd9Sstevel@tonic-gate 	/*
568*7c478bd9Sstevel@tonic-gate 	 * Check and handle root hub control request.
569*7c478bd9Sstevel@tonic-gate 	 */
570*7c478bd9Sstevel@tonic-gate 	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
571*7c478bd9Sstevel@tonic-gate 		error = uhci_handle_root_hub_request(uhcip, ph, ctrl_reqp);
572*7c478bd9Sstevel@tonic-gate 		mutex_exit(&uhcip->uhci_int_mutex);
573*7c478bd9Sstevel@tonic-gate 
574*7c478bd9Sstevel@tonic-gate 		return (error);
575*7c478bd9Sstevel@tonic-gate 	}
576*7c478bd9Sstevel@tonic-gate 
577*7c478bd9Sstevel@tonic-gate 	/* Insert the td's on the endpoint */
578*7c478bd9Sstevel@tonic-gate 	if ((error = uhci_insert_ctrl_td(uhcip, ph, ctrl_reqp, flags)) !=
579*7c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
580*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
581*7c478bd9Sstevel@tonic-gate 		    "uhci_hcdi_pipe_ctrl_xfer: No resources");
582*7c478bd9Sstevel@tonic-gate 	}
583*7c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
584*7c478bd9Sstevel@tonic-gate 
585*7c478bd9Sstevel@tonic-gate 	return (error);
586*7c478bd9Sstevel@tonic-gate }
587*7c478bd9Sstevel@tonic-gate 
588*7c478bd9Sstevel@tonic-gate 
589*7c478bd9Sstevel@tonic-gate /*
590*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_pipe_bulk_xfer:
591*7c478bd9Sstevel@tonic-gate  */
592*7c478bd9Sstevel@tonic-gate int
593*7c478bd9Sstevel@tonic-gate uhci_hcdi_pipe_bulk_xfer(usba_pipe_handle_data_t *pipe_handle,
594*7c478bd9Sstevel@tonic-gate     usb_bulk_req_t *bulk_reqp, usb_flags_t usb_flags)
595*7c478bd9Sstevel@tonic-gate {
596*7c478bd9Sstevel@tonic-gate 	int		error;
597*7c478bd9Sstevel@tonic-gate 	uhci_state_t	*uhcip;
598*7c478bd9Sstevel@tonic-gate 
599*7c478bd9Sstevel@tonic-gate 	uhcip = uhci_obtain_state(pipe_handle->p_usba_device->usb_root_hub_dip);
600*7c478bd9Sstevel@tonic-gate 
601*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
602*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_pipe_bulk_xfer: Flags = 0x%x", usb_flags);
603*7c478bd9Sstevel@tonic-gate 
604*7c478bd9Sstevel@tonic-gate 	/* Check the size of bulk request */
605*7c478bd9Sstevel@tonic-gate 	if (bulk_reqp->bulk_len > UHCI_BULK_MAX_XFER_SIZE) {
606*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
607*7c478bd9Sstevel@tonic-gate 		    "uhci_hcdi_pipe_bulk_xfer: req size 0x%x is more than 0x%x",
608*7c478bd9Sstevel@tonic-gate 		    bulk_reqp->bulk_len, UHCI_BULK_MAX_XFER_SIZE);
609*7c478bd9Sstevel@tonic-gate 
610*7c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
611*7c478bd9Sstevel@tonic-gate 	}
612*7c478bd9Sstevel@tonic-gate 
613*7c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
614*7c478bd9Sstevel@tonic-gate 
615*7c478bd9Sstevel@tonic-gate 	/* Add the TD into the Host Controller's bulk list */
616*7c478bd9Sstevel@tonic-gate 	if ((error = uhci_insert_bulk_td(uhcip, pipe_handle, bulk_reqp,
617*7c478bd9Sstevel@tonic-gate 	    usb_flags)) != USB_SUCCESS) {
618*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
619*7c478bd9Sstevel@tonic-gate 		    "uhci_hcdi_pipe_bulk_xfer: uhci_insert_bulk_td failed");
620*7c478bd9Sstevel@tonic-gate 	}
621*7c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
622*7c478bd9Sstevel@tonic-gate 
623*7c478bd9Sstevel@tonic-gate 	return (error);
624*7c478bd9Sstevel@tonic-gate }
625*7c478bd9Sstevel@tonic-gate 
626*7c478bd9Sstevel@tonic-gate 
627*7c478bd9Sstevel@tonic-gate /*
628*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_bulk_transfer_size:
629*7c478bd9Sstevel@tonic-gate  *	Return maximum bulk transfer size
630*7c478bd9Sstevel@tonic-gate  */
631*7c478bd9Sstevel@tonic-gate int
632*7c478bd9Sstevel@tonic-gate uhci_hcdi_bulk_transfer_size(
633*7c478bd9Sstevel@tonic-gate 	usba_device_t	*usba_device,
634*7c478bd9Sstevel@tonic-gate 	size_t		*size)
635*7c478bd9Sstevel@tonic-gate {
636*7c478bd9Sstevel@tonic-gate 	uhci_state_t *uhcip = uhci_obtain_state(usba_device->usb_root_hub_dip);
637*7c478bd9Sstevel@tonic-gate 
638*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
639*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_bulk_transfer_size:");
640*7c478bd9Sstevel@tonic-gate 
641*7c478bd9Sstevel@tonic-gate 	*size = uhci_bulk_transfer_size;
642*7c478bd9Sstevel@tonic-gate 
643*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
644*7c478bd9Sstevel@tonic-gate }
645*7c478bd9Sstevel@tonic-gate 
646*7c478bd9Sstevel@tonic-gate 
647*7c478bd9Sstevel@tonic-gate /*
648*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_pipe_intr_xfer:
649*7c478bd9Sstevel@tonic-gate  */
650*7c478bd9Sstevel@tonic-gate int
651*7c478bd9Sstevel@tonic-gate uhci_hcdi_pipe_intr_xfer(
652*7c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
653*7c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*req,
654*7c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
655*7c478bd9Sstevel@tonic-gate {
656*7c478bd9Sstevel@tonic-gate 	uhci_state_t	*uhcip = uhci_obtain_state(
657*7c478bd9Sstevel@tonic-gate 					ph->p_usba_device->usb_root_hub_dip);
658*7c478bd9Sstevel@tonic-gate 
659*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
660*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_pipe_intr_xfer: req=0x%p, uf=0x%x", req, flags);
661*7c478bd9Sstevel@tonic-gate 
662*7c478bd9Sstevel@tonic-gate 	if (UHCI_XFER_DIR(&ph->p_ep) == USB_EP_DIR_IN) {
663*7c478bd9Sstevel@tonic-gate 
664*7c478bd9Sstevel@tonic-gate 		return (uhci_start_periodic_pipe_polling(uhcip, ph,
665*7c478bd9Sstevel@tonic-gate 					(usb_opaque_t)req, flags));
666*7c478bd9Sstevel@tonic-gate 	} else {
667*7c478bd9Sstevel@tonic-gate 
668*7c478bd9Sstevel@tonic-gate 		return (uhci_send_intr_data(uhcip, ph, req, flags));
669*7c478bd9Sstevel@tonic-gate 	}
670*7c478bd9Sstevel@tonic-gate }
671*7c478bd9Sstevel@tonic-gate 
672*7c478bd9Sstevel@tonic-gate 
673*7c478bd9Sstevel@tonic-gate /*
674*7c478bd9Sstevel@tonic-gate  * uhci_send_intr_data():
675*7c478bd9Sstevel@tonic-gate  *	send data to interrupt out pipe
676*7c478bd9Sstevel@tonic-gate  */
677*7c478bd9Sstevel@tonic-gate static int
678*7c478bd9Sstevel@tonic-gate uhci_send_intr_data(
679*7c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
680*7c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*pipe_handle,
681*7c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*req,
682*7c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
683*7c478bd9Sstevel@tonic-gate {
684*7c478bd9Sstevel@tonic-gate 	int	rval = USB_SUCCESS;
685*7c478bd9Sstevel@tonic-gate 
686*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
687*7c478bd9Sstevel@tonic-gate 	    "uhci_send_intr_data:");
688*7c478bd9Sstevel@tonic-gate 
689*7c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
690*7c478bd9Sstevel@tonic-gate 
691*7c478bd9Sstevel@tonic-gate 	/* Add the TD into the Host Controller's interrupt list */
692*7c478bd9Sstevel@tonic-gate 	if ((rval = uhci_insert_intr_td(uhcip, pipe_handle, req, flags)) !=
693*7c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
694*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
695*7c478bd9Sstevel@tonic-gate 		    "uhci_send_intr_data: No resources");
696*7c478bd9Sstevel@tonic-gate 	}
697*7c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
698*7c478bd9Sstevel@tonic-gate 
699*7c478bd9Sstevel@tonic-gate 	return (rval);
700*7c478bd9Sstevel@tonic-gate }
701*7c478bd9Sstevel@tonic-gate 
702*7c478bd9Sstevel@tonic-gate 
703*7c478bd9Sstevel@tonic-gate /*
704*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_pipe_stop_intr_polling()
705*7c478bd9Sstevel@tonic-gate  */
706*7c478bd9Sstevel@tonic-gate int
707*7c478bd9Sstevel@tonic-gate uhci_hcdi_pipe_stop_intr_polling(
708*7c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t *pipe_handle,
709*7c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
710*7c478bd9Sstevel@tonic-gate {
711*7c478bd9Sstevel@tonic-gate 	uhci_state_t *uhcip =
712*7c478bd9Sstevel@tonic-gate 		uhci_obtain_state(pipe_handle->p_usba_device->usb_root_hub_dip);
713*7c478bd9Sstevel@tonic-gate 
714*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
715*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_pipe_stop_intr_polling: ph = 0x%p fl = 0x%x",
716*7c478bd9Sstevel@tonic-gate 	    (void *)pipe_handle, flags);
717*7c478bd9Sstevel@tonic-gate 
718*7c478bd9Sstevel@tonic-gate 	return (uhci_stop_periodic_pipe_polling(uhcip, pipe_handle, flags));
719*7c478bd9Sstevel@tonic-gate }
720*7c478bd9Sstevel@tonic-gate 
721*7c478bd9Sstevel@tonic-gate 
722*7c478bd9Sstevel@tonic-gate /*
723*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_get_current_frame_number
724*7c478bd9Sstevel@tonic-gate  *	Returns the current frame number
725*7c478bd9Sstevel@tonic-gate  */
726*7c478bd9Sstevel@tonic-gate usb_frame_number_t
727*7c478bd9Sstevel@tonic-gate uhci_hcdi_get_current_frame_number(usba_device_t *usba_device)
728*7c478bd9Sstevel@tonic-gate {
729*7c478bd9Sstevel@tonic-gate 	uhci_state_t *uhcip = uhci_obtain_state(usba_device->usb_root_hub_dip);
730*7c478bd9Sstevel@tonic-gate 	usb_frame_number_t frame_number;
731*7c478bd9Sstevel@tonic-gate 
732*7c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
733*7c478bd9Sstevel@tonic-gate 	frame_number = uhci_get_sw_frame_number(uhcip);
734*7c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
735*7c478bd9Sstevel@tonic-gate 
736*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
737*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_get_current_frame_number: %llx", frame_number);
738*7c478bd9Sstevel@tonic-gate 
739*7c478bd9Sstevel@tonic-gate 	return (frame_number);
740*7c478bd9Sstevel@tonic-gate }
741*7c478bd9Sstevel@tonic-gate 
742*7c478bd9Sstevel@tonic-gate 
743*7c478bd9Sstevel@tonic-gate /*
744*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_get_max_isoc_pkts
745*7c478bd9Sstevel@tonic-gate  *	Returns the maximum number of isoc packets per USB Isoch request
746*7c478bd9Sstevel@tonic-gate  */
747*7c478bd9Sstevel@tonic-gate uint_t
748*7c478bd9Sstevel@tonic-gate uhci_hcdi_get_max_isoc_pkts(usba_device_t *usba_device)
749*7c478bd9Sstevel@tonic-gate {
750*7c478bd9Sstevel@tonic-gate 	uhci_state_t *uhcip = uhci_obtain_state(usba_device->usb_root_hub_dip);
751*7c478bd9Sstevel@tonic-gate 
752*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
753*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_get_max_isoc_pkts: 0x%x", UHCI_MAX_ISOC_PKTS);
754*7c478bd9Sstevel@tonic-gate 
755*7c478bd9Sstevel@tonic-gate 	return (UHCI_MAX_ISOC_PKTS);
756*7c478bd9Sstevel@tonic-gate }
757*7c478bd9Sstevel@tonic-gate 
758*7c478bd9Sstevel@tonic-gate 
759*7c478bd9Sstevel@tonic-gate /*
760*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_pipe_isoc_xfer:
761*7c478bd9Sstevel@tonic-gate  */
762*7c478bd9Sstevel@tonic-gate int
763*7c478bd9Sstevel@tonic-gate uhci_hcdi_pipe_isoc_xfer(
764*7c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
765*7c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_reqp,
766*7c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
767*7c478bd9Sstevel@tonic-gate {
768*7c478bd9Sstevel@tonic-gate 	uhci_state_t	*uhcip;
769*7c478bd9Sstevel@tonic-gate 
770*7c478bd9Sstevel@tonic-gate 	uhcip = uhci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
771*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
772*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_pipe_isoc_xfer: req=0x%p, uf=0x%x", isoc_reqp, flags);
773*7c478bd9Sstevel@tonic-gate 
774*7c478bd9Sstevel@tonic-gate 	if (UHCI_XFER_DIR(&ph->p_ep) == USB_EP_DIR_IN) {
775*7c478bd9Sstevel@tonic-gate 
776*7c478bd9Sstevel@tonic-gate 		return (uhci_start_periodic_pipe_polling(uhcip, ph,
777*7c478bd9Sstevel@tonic-gate 					(usb_opaque_t)isoc_reqp, flags));
778*7c478bd9Sstevel@tonic-gate 	} else {
779*7c478bd9Sstevel@tonic-gate 
780*7c478bd9Sstevel@tonic-gate 		return (uhci_pipe_send_isoc_data(uhcip, ph, isoc_reqp, flags));
781*7c478bd9Sstevel@tonic-gate 	}
782*7c478bd9Sstevel@tonic-gate }
783*7c478bd9Sstevel@tonic-gate 
784*7c478bd9Sstevel@tonic-gate 
785*7c478bd9Sstevel@tonic-gate /*
786*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_pipe_stop_isoc_polling()
787*7c478bd9Sstevel@tonic-gate  */
788*7c478bd9Sstevel@tonic-gate int
789*7c478bd9Sstevel@tonic-gate uhci_hcdi_pipe_stop_isoc_polling(
790*7c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
791*7c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
792*7c478bd9Sstevel@tonic-gate {
793*7c478bd9Sstevel@tonic-gate 	uhci_state_t *uhcip =
794*7c478bd9Sstevel@tonic-gate 		uhci_obtain_state(ph->p_usba_device->usb_root_hub_dip);
795*7c478bd9Sstevel@tonic-gate 
796*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
797*7c478bd9Sstevel@tonic-gate 	    "uhci_hcdi_pipe_stop_isoc_polling: ph = 0x%p fl = 0x%x",
798*7c478bd9Sstevel@tonic-gate 	    (void *)ph, flags);
799*7c478bd9Sstevel@tonic-gate 
800*7c478bd9Sstevel@tonic-gate 	return (uhci_stop_periodic_pipe_polling(uhcip, ph, flags));
801*7c478bd9Sstevel@tonic-gate }
802*7c478bd9Sstevel@tonic-gate 
803*7c478bd9Sstevel@tonic-gate 
804*7c478bd9Sstevel@tonic-gate /*
805*7c478bd9Sstevel@tonic-gate  * uhci_start_periodic_pipe_polling:
806*7c478bd9Sstevel@tonic-gate  */
807*7c478bd9Sstevel@tonic-gate static int
808*7c478bd9Sstevel@tonic-gate uhci_start_periodic_pipe_polling(
809*7c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
810*7c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
811*7c478bd9Sstevel@tonic-gate 	usb_opaque_t		in_reqp,
812*7c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
813*7c478bd9Sstevel@tonic-gate {
814*7c478bd9Sstevel@tonic-gate 	int			n, num_tds;
815*7c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
816*7c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*intr_reqp = (usb_intr_req_t *)in_reqp;
817*7c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
818*7c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp = (uhci_pipe_private_t *)ph->p_hcd_private;
819*7c478bd9Sstevel@tonic-gate 
820*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
821*7c478bd9Sstevel@tonic-gate 	    "uhci_start_periodic_pipe_polling: flags: 0x%x, ep%d",
822*7c478bd9Sstevel@tonic-gate 	    flags, eptd->bEndpointAddress);
823*7c478bd9Sstevel@tonic-gate 
824*7c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
825*7c478bd9Sstevel@tonic-gate 
826*7c478bd9Sstevel@tonic-gate 	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
827*7c478bd9Sstevel@tonic-gate 		uint_t	pipe_state = uhcip->uhci_root_hub.rh_pipe_state;
828*7c478bd9Sstevel@tonic-gate 
829*7c478bd9Sstevel@tonic-gate 		ASSERT(pipe_state == UHCI_PIPE_STATE_IDLE);
830*7c478bd9Sstevel@tonic-gate 		ASSERT(UHCI_XFER_DIR(eptd) == USB_EP_DIR_IN);
831*7c478bd9Sstevel@tonic-gate 
832*7c478bd9Sstevel@tonic-gate 		/* ONE_XFER not supported */
833*7c478bd9Sstevel@tonic-gate 		ASSERT((intr_reqp->intr_attributes &
834*7c478bd9Sstevel@tonic-gate 		    USB_ATTRS_ONE_XFER) == 0);
835*7c478bd9Sstevel@tonic-gate 		ASSERT(uhcip->uhci_root_hub.rh_client_intr_req == NULL);
836*7c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_client_intr_req = intr_reqp;
837*7c478bd9Sstevel@tonic-gate 
838*7c478bd9Sstevel@tonic-gate 		if ((error = uhci_root_hub_allocate_intr_pipe_resource(
839*7c478bd9Sstevel@tonic-gate 		    uhcip, flags)) != USB_SUCCESS) {
840*7c478bd9Sstevel@tonic-gate 			/* reset the client interrupt request pointer */
841*7c478bd9Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_client_intr_req = NULL;
842*7c478bd9Sstevel@tonic-gate 
843*7c478bd9Sstevel@tonic-gate 			mutex_exit(&uhcip->uhci_int_mutex);
844*7c478bd9Sstevel@tonic-gate 
845*7c478bd9Sstevel@tonic-gate 			return (error);
846*7c478bd9Sstevel@tonic-gate 		}
847*7c478bd9Sstevel@tonic-gate 
848*7c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_pipe_state = USB_PIPE_STATE_ACTIVE;
849*7c478bd9Sstevel@tonic-gate 
850*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
851*7c478bd9Sstevel@tonic-gate 		    "uhci_start_periodic_pipe_polling: "
852*7c478bd9Sstevel@tonic-gate 		    "Start intr polling for root hub successful");
853*7c478bd9Sstevel@tonic-gate 
854*7c478bd9Sstevel@tonic-gate 		/* check if we need to send the reset data up? */
855*7c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_root_hub.rh_status) {
856*7c478bd9Sstevel@tonic-gate 			uhci_root_hub_reset_occurred(uhcip,
857*7c478bd9Sstevel@tonic-gate 			    uhcip->uhci_root_hub.rh_status - 1);
858*7c478bd9Sstevel@tonic-gate 
859*7c478bd9Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_status = 0;
860*7c478bd9Sstevel@tonic-gate 		}
861*7c478bd9Sstevel@tonic-gate 		mutex_exit(&uhcip->uhci_int_mutex);
862*7c478bd9Sstevel@tonic-gate 
863*7c478bd9Sstevel@tonic-gate 		return (error);
864*7c478bd9Sstevel@tonic-gate 	}
865*7c478bd9Sstevel@tonic-gate 
866*7c478bd9Sstevel@tonic-gate 	/* save the original client's periodic IN request */
867*7c478bd9Sstevel@tonic-gate 	pp->pp_client_periodic_in_reqp = in_reqp;
868*7c478bd9Sstevel@tonic-gate 
869*7c478bd9Sstevel@tonic-gate 	ASSERT(pp->pp_state != UHCI_PIPE_STATE_ACTIVE);
870*7c478bd9Sstevel@tonic-gate 	/*
871*7c478bd9Sstevel@tonic-gate 	 *
872*7c478bd9Sstevel@tonic-gate 	 * This pipe is uninitialized. If it is an isoc
873*7c478bd9Sstevel@tonic-gate 	 * receive request, insert four times the same
874*7c478bd9Sstevel@tonic-gate 	 * request so that we do not lose any frames.
875*7c478bd9Sstevel@tonic-gate 	 */
876*7c478bd9Sstevel@tonic-gate 	if (UHCI_XFER_TYPE(eptd) == USB_EP_ATTR_ISOCH) {
877*7c478bd9Sstevel@tonic-gate 		for (n = 0; n < 5; n++) {
878*7c478bd9Sstevel@tonic-gate 			if ((error = uhci_start_isoc_receive_polling(
879*7c478bd9Sstevel@tonic-gate 			    uhcip, ph, NULL, flags)) != USB_SUCCESS) {
880*7c478bd9Sstevel@tonic-gate 
881*7c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(PRINT_MASK_INTR,
882*7c478bd9Sstevel@tonic-gate 				    uhcip->uhci_log_hdl,
883*7c478bd9Sstevel@tonic-gate 				    "uhci_start_periodic_pipe_polling: "
884*7c478bd9Sstevel@tonic-gate 				    "Start isoc polling failed %d", n);
885*7c478bd9Sstevel@tonic-gate 
886*7c478bd9Sstevel@tonic-gate 				pp->pp_client_periodic_in_reqp = NULL;
887*7c478bd9Sstevel@tonic-gate 				mutex_exit(&uhcip->uhci_int_mutex);
888*7c478bd9Sstevel@tonic-gate 
889*7c478bd9Sstevel@tonic-gate 				return (error);
890*7c478bd9Sstevel@tonic-gate 			}
891*7c478bd9Sstevel@tonic-gate 		}
892*7c478bd9Sstevel@tonic-gate 	}
893*7c478bd9Sstevel@tonic-gate 
894*7c478bd9Sstevel@tonic-gate 	if (UHCI_XFER_TYPE(eptd) == USB_EP_ATTR_INTR) {
895*7c478bd9Sstevel@tonic-gate 		if ((pp->pp_node < POLLING_FREQ_7MS) &&
896*7c478bd9Sstevel@tonic-gate 		    (!(intr_reqp->intr_attributes & USB_ATTRS_ONE_XFER))) {
897*7c478bd9Sstevel@tonic-gate 			num_tds = 5;
898*7c478bd9Sstevel@tonic-gate 		} else {
899*7c478bd9Sstevel@tonic-gate 			num_tds = 1;
900*7c478bd9Sstevel@tonic-gate 		}
901*7c478bd9Sstevel@tonic-gate 
902*7c478bd9Sstevel@tonic-gate 		/*
903*7c478bd9Sstevel@tonic-gate 		 * This pipe is uninitialized.
904*7c478bd9Sstevel@tonic-gate 		 * Insert a TD on the interrupt ED.
905*7c478bd9Sstevel@tonic-gate 		 */
906*7c478bd9Sstevel@tonic-gate 		for (n = 0; n < num_tds; n++) {
907*7c478bd9Sstevel@tonic-gate 			if ((error = uhci_insert_intr_td(uhcip, ph, NULL,
908*7c478bd9Sstevel@tonic-gate 			    flags)) != USB_SUCCESS) {
909*7c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(PRINT_MASK_INTR,
910*7c478bd9Sstevel@tonic-gate 				    uhcip->uhci_log_hdl,
911*7c478bd9Sstevel@tonic-gate 				    "uhci_start_periodic_pipe_polling: "
912*7c478bd9Sstevel@tonic-gate 				    "Start polling failed");
913*7c478bd9Sstevel@tonic-gate 
914*7c478bd9Sstevel@tonic-gate 				pp->pp_client_periodic_in_reqp = NULL;
915*7c478bd9Sstevel@tonic-gate 				mutex_exit(&uhcip->uhci_int_mutex);
916*7c478bd9Sstevel@tonic-gate 
917*7c478bd9Sstevel@tonic-gate 				return (error);
918*7c478bd9Sstevel@tonic-gate 			}
919*7c478bd9Sstevel@tonic-gate 		}
920*7c478bd9Sstevel@tonic-gate 	}
921*7c478bd9Sstevel@tonic-gate 
922*7c478bd9Sstevel@tonic-gate 	pp->pp_state = UHCI_PIPE_STATE_ACTIVE;
923*7c478bd9Sstevel@tonic-gate 
924*7c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
925*7c478bd9Sstevel@tonic-gate 
926*7c478bd9Sstevel@tonic-gate 	return (error);
927*7c478bd9Sstevel@tonic-gate }
928*7c478bd9Sstevel@tonic-gate 
929*7c478bd9Sstevel@tonic-gate 
930*7c478bd9Sstevel@tonic-gate /*
931*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_periodic_pipe_stop_polling:
932*7c478bd9Sstevel@tonic-gate  */
933*7c478bd9Sstevel@tonic-gate static int
934*7c478bd9Sstevel@tonic-gate uhci_stop_periodic_pipe_polling(uhci_state_t *uhcip,
935*7c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t  *ph, usb_flags_t flags)
936*7c478bd9Sstevel@tonic-gate {
937*7c478bd9Sstevel@tonic-gate 	uhci_pipe_private_t	*pp = (uhci_pipe_private_t *)ph->p_hcd_private;
938*7c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
939*7c478bd9Sstevel@tonic-gate 
940*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
941*7c478bd9Sstevel@tonic-gate 	    "uhci_stop_periodic_pipe_polling: flags = 0x%x", flags);
942*7c478bd9Sstevel@tonic-gate 
943*7c478bd9Sstevel@tonic-gate 	if (ph->p_usba_device->usb_addr == ROOT_HUB_ADDR) {
944*7c478bd9Sstevel@tonic-gate 		ASSERT(UHCI_XFER_DIR(eptd) == USB_EP_DIR_IN);
945*7c478bd9Sstevel@tonic-gate 
946*7c478bd9Sstevel@tonic-gate 		mutex_enter(&uhcip->uhci_int_mutex);
947*7c478bd9Sstevel@tonic-gate 		if (uhcip->uhci_root_hub.rh_pipe_state ==
948*7c478bd9Sstevel@tonic-gate 		    UHCI_PIPE_STATE_ACTIVE) {
949*7c478bd9Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_pipe_state =
950*7c478bd9Sstevel@tonic-gate 				UHCI_PIPE_STATE_IDLE;
951*7c478bd9Sstevel@tonic-gate 
952*7c478bd9Sstevel@tonic-gate 			/* Do interrupt pipe cleanup */
953*7c478bd9Sstevel@tonic-gate 			uhci_root_hub_intr_pipe_cleanup(uhcip,
954*7c478bd9Sstevel@tonic-gate 						USB_CR_STOPPED_POLLING);
955*7c478bd9Sstevel@tonic-gate 
956*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
957*7c478bd9Sstevel@tonic-gate 			    "uhci_stop_periodic_pipe_polling: Stop intr "
958*7c478bd9Sstevel@tonic-gate 			    "polling for root hub successful");
959*7c478bd9Sstevel@tonic-gate 
960*7c478bd9Sstevel@tonic-gate 		} else {
961*7c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_INTR, uhcip->uhci_log_hdl,
962*7c478bd9Sstevel@tonic-gate 			    "uhci_stop_periodic_pipe_polling: "
963*7c478bd9Sstevel@tonic-gate 			    "Intr polling for root hub is already stopped");
964*7c478bd9Sstevel@tonic-gate 		}
965*7c478bd9Sstevel@tonic-gate 		mutex_exit(&uhcip->uhci_int_mutex);
966*7c478bd9Sstevel@tonic-gate 
967*7c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
968*7c478bd9Sstevel@tonic-gate 	}
969*7c478bd9Sstevel@tonic-gate 
970*7c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
971*7c478bd9Sstevel@tonic-gate 
972*7c478bd9Sstevel@tonic-gate 	if (pp->pp_state != UHCI_PIPE_STATE_ACTIVE) {
973*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_INTR, uhcip->uhci_log_hdl,
974*7c478bd9Sstevel@tonic-gate 		    "uhci_stop_periodic_pipe_polling: Polling already stopped");
975*7c478bd9Sstevel@tonic-gate 		mutex_exit(&uhcip->uhci_int_mutex);
976*7c478bd9Sstevel@tonic-gate 
977*7c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
978*7c478bd9Sstevel@tonic-gate 	}
979*7c478bd9Sstevel@tonic-gate 
980*7c478bd9Sstevel@tonic-gate 	/*
981*7c478bd9Sstevel@tonic-gate 	 * Set the terminate bits in all the tds in the queue and
982*7c478bd9Sstevel@tonic-gate 	 * in the element_ptr.
983*7c478bd9Sstevel@tonic-gate 	 * Do not deallocate the bandwidth or tear down the DMA
984*7c478bd9Sstevel@tonic-gate 	 */
985*7c478bd9Sstevel@tonic-gate 	uhci_modify_td_active_bits(uhcip, pp);
986*7c478bd9Sstevel@tonic-gate 	(void) uhci_wait_for_sof(uhcip);
987*7c478bd9Sstevel@tonic-gate 
988*7c478bd9Sstevel@tonic-gate 	if (UHCI_XFER_TYPE(eptd) == USB_EP_ATTR_ISOCH) {
989*7c478bd9Sstevel@tonic-gate 		uhci_remove_isoc_tds_tws(uhcip, pp);
990*7c478bd9Sstevel@tonic-gate 		pp->pp_state = UHCI_PIPE_STATE_IDLE;
991*7c478bd9Sstevel@tonic-gate 	} else {
992*7c478bd9Sstevel@tonic-gate 		UHCI_SET_TERMINATE_BIT(pp->pp_qh->element_ptr);
993*7c478bd9Sstevel@tonic-gate 		uhci_update_intr_td_data_toggle(uhcip, pp);
994*7c478bd9Sstevel@tonic-gate 		SetQH32(uhcip, pp->pp_qh->element_ptr,
995*7c478bd9Sstevel@tonic-gate 		    TD_PADDR(pp->pp_qh->td_tailp));
996*7c478bd9Sstevel@tonic-gate 		uhci_remove_tds_tws(uhcip, ph);
997*7c478bd9Sstevel@tonic-gate 	}
998*7c478bd9Sstevel@tonic-gate 
999*7c478bd9Sstevel@tonic-gate 	pp->pp_state = UHCI_PIPE_STATE_IDLE;
1000*7c478bd9Sstevel@tonic-gate 
1001*7c478bd9Sstevel@tonic-gate 	if (pp->pp_client_periodic_in_reqp) {
1002*7c478bd9Sstevel@tonic-gate 		uhci_hcdi_callback(uhcip, pp, ph, NULL, USB_CR_STOPPED_POLLING);
1003*7c478bd9Sstevel@tonic-gate 	}
1004*7c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
1005*7c478bd9Sstevel@tonic-gate 
1006*7c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
1007*7c478bd9Sstevel@tonic-gate }
1008*7c478bd9Sstevel@tonic-gate 
1009*7c478bd9Sstevel@tonic-gate 
1010*7c478bd9Sstevel@tonic-gate /*
1011*7c478bd9Sstevel@tonic-gate  * uhci_hcdi_pipe_send_isoc_data:
1012*7c478bd9Sstevel@tonic-gate  *	Handles the isoc write request.
1013*7c478bd9Sstevel@tonic-gate  */
1014*7c478bd9Sstevel@tonic-gate static int
1015*7c478bd9Sstevel@tonic-gate uhci_pipe_send_isoc_data(
1016*7c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
1017*7c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
1018*7c478bd9Sstevel@tonic-gate 	usb_isoc_req_t		*isoc_req,
1019*7c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
1020*7c478bd9Sstevel@tonic-gate {
1021*7c478bd9Sstevel@tonic-gate 	int			error;
1022*7c478bd9Sstevel@tonic-gate 	size_t			max_isoc_xfer_sz, length;
1023*7c478bd9Sstevel@tonic-gate 
1024*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
1025*7c478bd9Sstevel@tonic-gate 	    "uhci_pipe_send_isoc_data: isoc_req = %p flags = %x",
1026*7c478bd9Sstevel@tonic-gate 	    isoc_req, usb_flags);
1027*7c478bd9Sstevel@tonic-gate 
1028*7c478bd9Sstevel@tonic-gate 	ASSERT(isoc_req->isoc_pkts_count < UHCI_MAX_ISOC_PKTS);
1029*7c478bd9Sstevel@tonic-gate 
1030*7c478bd9Sstevel@tonic-gate 	/* Calculate the maximum isochronous transfer size */
1031*7c478bd9Sstevel@tonic-gate 	max_isoc_xfer_sz = UHCI_MAX_ISOC_PKTS * ph->p_ep.wMaxPacketSize;
1032*7c478bd9Sstevel@tonic-gate 
1033*7c478bd9Sstevel@tonic-gate 	/* Check the size of isochronous request */
1034*7c478bd9Sstevel@tonic-gate 	ASSERT(isoc_req->isoc_data != NULL);
1035*7c478bd9Sstevel@tonic-gate 	length = isoc_req->isoc_data->b_wptr - isoc_req->isoc_data->b_rptr;
1036*7c478bd9Sstevel@tonic-gate 
1037*7c478bd9Sstevel@tonic-gate 	if (length > max_isoc_xfer_sz) {
1038*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
1039*7c478bd9Sstevel@tonic-gate 		    "uhci_pipe_send_isoc_data: Maximum isoc request size %lx "
1040*7c478bd9Sstevel@tonic-gate 		    "Given isoc request size %lx", max_isoc_xfer_sz, length);
1041*7c478bd9Sstevel@tonic-gate 
1042*7c478bd9Sstevel@tonic-gate 		return (USB_INVALID_REQUEST);
1043*7c478bd9Sstevel@tonic-gate 	}
1044*7c478bd9Sstevel@tonic-gate 
1045*7c478bd9Sstevel@tonic-gate 
1046*7c478bd9Sstevel@tonic-gate 	/*
1047*7c478bd9Sstevel@tonic-gate 	 * Check whether we can insert these tds?
1048*7c478bd9Sstevel@tonic-gate 	 * At any point of time, we can insert maximum of 1024 isoc td's,
1049*7c478bd9Sstevel@tonic-gate 	 * size of frame list table.
1050*7c478bd9Sstevel@tonic-gate 	 */
1051*7c478bd9Sstevel@tonic-gate 	if (isoc_req->isoc_pkts_count > UHCI_MAX_ISOC_PKTS) {
1052*7c478bd9Sstevel@tonic-gate 
1053*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
1054*7c478bd9Sstevel@tonic-gate 		    "uhci_pipe_send_isoc_data: request too big");
1055*7c478bd9Sstevel@tonic-gate 
1056*7c478bd9Sstevel@tonic-gate 		return (USB_INVALID_REQUEST);
1057*7c478bd9Sstevel@tonic-gate 	}
1058*7c478bd9Sstevel@tonic-gate 
1059*7c478bd9Sstevel@tonic-gate 	/* Add the TD into the Host Controller's isoc list */
1060*7c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
1061*7c478bd9Sstevel@tonic-gate 
1062*7c478bd9Sstevel@tonic-gate 	if ((error = uhci_insert_isoc_td(uhcip, ph, isoc_req,
1063*7c478bd9Sstevel@tonic-gate 	    length, usb_flags)) != USB_SUCCESS) {
1064*7c478bd9Sstevel@tonic-gate 
1065*7c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ISOC, uhcip->uhci_log_hdl,
1066*7c478bd9Sstevel@tonic-gate 		    "uhci_pipe_send_isoc_data: Unable to insert the isoc_req,"
1067*7c478bd9Sstevel@tonic-gate 		    "Error = %d", error);
1068*7c478bd9Sstevel@tonic-gate 	}
1069*7c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
1070*7c478bd9Sstevel@tonic-gate 
1071*7c478bd9Sstevel@tonic-gate 	return (error);
1072*7c478bd9Sstevel@tonic-gate }
1073*7c478bd9Sstevel@tonic-gate 
1074*7c478bd9Sstevel@tonic-gate 
1075*7c478bd9Sstevel@tonic-gate /*
1076*7c478bd9Sstevel@tonic-gate  * uhci_update_intr_td_data_toggle
1077*7c478bd9Sstevel@tonic-gate  *	Update the data toggle and save in the usba_device structure
1078*7c478bd9Sstevel@tonic-gate  */
1079*7c478bd9Sstevel@tonic-gate static void
1080*7c478bd9Sstevel@tonic-gate uhci_update_intr_td_data_toggle(uhci_state_t *uhcip, uhci_pipe_private_t *pp)
1081*7c478bd9Sstevel@tonic-gate {
1082*7c478bd9Sstevel@tonic-gate 	uint32_t	paddr_tail, element_ptr;
1083*7c478bd9Sstevel@tonic-gate 	uhci_td_t	*next_td;
1084*7c478bd9Sstevel@tonic-gate 
1085*7c478bd9Sstevel@tonic-gate 	/* Find the next td that would have been executed */
1086*7c478bd9Sstevel@tonic-gate 	element_ptr = GetQH32(uhcip, pp->pp_qh->element_ptr) &
1087*7c478bd9Sstevel@tonic-gate 						QH_ELEMENT_PTR_MASK;
1088*7c478bd9Sstevel@tonic-gate 	next_td = TD_VADDR(element_ptr);
1089*7c478bd9Sstevel@tonic-gate 	paddr_tail = TD_PADDR(pp->pp_qh->td_tailp);
1090*7c478bd9Sstevel@tonic-gate 
1091*7c478bd9Sstevel@tonic-gate 	/*
1092*7c478bd9Sstevel@tonic-gate 	 * If element_ptr points to the dummy td, then the data toggle in
1093*7c478bd9Sstevel@tonic-gate 	 * pp_data_toggle is correct. Otherwise update the data toggle in
1094*7c478bd9Sstevel@tonic-gate 	 * the pipe private
1095*7c478bd9Sstevel@tonic-gate 	 */
1096*7c478bd9Sstevel@tonic-gate 	if (element_ptr != paddr_tail) {
1097*7c478bd9Sstevel@tonic-gate 		pp->pp_data_toggle = GetTD_dtogg(uhcip, next_td);
1098*7c478bd9Sstevel@tonic-gate 	}
1099*7c478bd9Sstevel@tonic-gate 
1100*7c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
1101*7c478bd9Sstevel@tonic-gate 	    "uhci_update_intr_td_data_toggle: "
1102*7c478bd9Sstevel@tonic-gate 	    "pp %p toggle %x element ptr %x ptail %x",
1103*7c478bd9Sstevel@tonic-gate 	    pp, pp->pp_data_toggle, element_ptr, paddr_tail);
1104*7c478bd9Sstevel@tonic-gate 
1105*7c478bd9Sstevel@tonic-gate 	uhci_save_data_toggle(pp);
1106*7c478bd9Sstevel@tonic-gate }
1107