xref: /illumos-gate/usr/src/uts/common/io/usb/hcd/uhci/uhcihub.c (revision 112116d842e816e29d26a8fe28ed25d201063169)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Universal Serial BUS  Host Controller Driver (UHCI)
30  *
31  * The UHCI driver is a driver which interfaces to the Universal
32  * Serial Bus Architecture (USBA) and the Host Controller (HC). The interface to
33  * the Host Controller is defined by the Universal Host Controller Interface.
34  * This file contains the code for root hub related functions.
35  */
36 #include <sys/usb/hcd/uhci/uhcid.h>
37 #include <sys/usb/hcd/uhci/uhci.h>
38 #include <sys/usb/hcd/uhci/uhcihub.h>
39 
40 /*
41  *  Function Prototypes
42  */
43 static int	uhci_handle_set_clear_port_feature(
44 			uhci_state_t		*uhcip,
45 			uchar_t			bRequest,
46 			uint16_t		wValue,
47 			usb_port_t		port);
48 static	void	uhci_handle_port_power(
49 			uhci_state_t		*uhcip,
50 			usb_port_t		port,
51 			uint_t			on);
52 static	void	uhci_handle_port_suspend(
53 			uhci_state_t		*uhcip,
54 			usb_port_t		port,
55 			uint_t			on);
56 static	void	uhci_handle_port_enable_disable(
57 			uhci_state_t		*uhcip,
58 			usb_port_t		port,
59 			uint_t			on);
60 static	void	uhci_handle_port_reset(
61 			uhci_state_t		*uhcip,
62 			usb_port_t		port);
63 static	void	uhci_handle_complete_port_reset(
64 			uhci_state_t		*uhcip,
65 			usb_port_t		port);
66 static	void	uhci_handle_clear_port_connection(
67 			uhci_state_t		*uhcip,
68 			usb_port_t		port);
69 static	void	uhci_handle_get_port_status(
70 			uhci_state_t		*uhcip,
71 			usb_ctrl_req_t		*req,
72 			usb_port_t		port);
73 static	void	uhci_handle_get_hub_descriptor(
74 			uhci_state_t		*uhcip,
75 			usb_ctrl_req_t		*req);
76 static void	uhci_handle_get_hub_status(
77 			uhci_state_t		*uhcip,
78 			usb_ctrl_req_t		*req);
79 static void	uhci_handle_get_device_status(
80 			uhci_state_t		*uhcip,
81 			usb_ctrl_req_t		*req);
82 static uint_t	uhci_get_port_status(
83 			uhci_state_t		*uhcip,
84 			usb_port_t		port);
85 static	void	uhci_rh_hcdi_callback(
86 			uhci_state_t		*uhcip,
87 			usba_pipe_handle_data_t	*ph,
88 			usb_opaque_t		req,
89 			usb_cr_t		cr);
90 
91 /*
92  * root hub device descriptor
93  */
94 static usb_dev_descr_t uhci_rh_dev_descr = {
95 	0x12,	/* Length */
96 	1,	/* Type */
97 	0x110,	/* BCD - v1.1 */
98 	9,	/* Class */
99 	0,	/* Sub class */
100 	0,	/* Protocol */
101 	8,	/* Max pkt size */
102 	0,	/* Vendor */
103 	0,	/* Product id */
104 	0,	/* Device release */
105 	0,	/* Manufacturer */
106 	0,	/* Product */
107 	0,	/* Sn */
108 	1	/* No of configs */
109 };
110 
111 /*
112  * root hub config descriptor
113  */
114 static uchar_t uhci_rh_config_descr[] = {
115 	/* config descriptor */
116 	0x09,		/* bLength */
117 	0x02,		/* bDescriptorType, Configuration */
118 	0x19, 0x00,	/* wTotalLength */
119 	0x01,		/* bNumInterfaces */
120 	0x01,		/* bConfigurationValue */
121 	0x00,		/* iConfiguration */
122 	0x40,		/* bmAttributes */
123 	0x00,		/* MaxPower */
124 
125 	/* interface descriptor */
126 	0x09,		/* bLength */
127 	0x04,		/* bDescriptorType, Interface */
128 	0x00,		/* bInterfaceNumber */
129 	0x00,		/* bAlternateSetting */
130 	0x01,		/* bNumEndpoints */
131 	0x09,		/* bInterfaceClass */
132 	0x01,		/* bInterfaceSubClass */
133 	0x00,		/* bInterfaceProtocol */
134 	0x00,		/* iInterface */
135 
136 	/* endpoint descriptor */
137 	0x07,		/* bLength */
138 	0x05,		/* bDescriptorType, Endpoint */
139 	0x81,		/* bEndpointAddress */
140 	0x03,		/* bmAttributes */
141 	0x01, 0x00,	/* wMaxPacketSize, 1 +	(OHCI_MAX_RH_PORTS / 8) */
142 	0x20		/* bInterval */
143 };
144 
145 
146 /*
147  * uhci_init_root_hub:
148  *	Initialize the root hub
149  */
150 int
151 uhci_init_root_hub(uhci_state_t *uhcip)
152 {
153 	int		i, length;
154 	usb_hub_descr_t	*root_hub_descr = &uhcip->uhci_root_hub.rh_descr;
155 
156 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
157 	    "uhci_init_root_hub:");
158 
159 	uhcip->uhci_root_hub.rh_num_ports = MAX_RH_PORTS;
160 
161 	/*
162 	 * Build the hub descriptor
163 	 */
164 	root_hub_descr->bDescriptorType = ROOT_HUB_DESCRIPTOR_TYPE;
165 	root_hub_descr->bNbrPorts	= MAX_RH_PORTS;
166 
167 	length = root_hub_descr->bNbrPorts / 8;
168 	if (length) {
169 		root_hub_descr->bDescLength = 7 + (2 * (length + 1));
170 	} else {
171 		root_hub_descr->bDescLength = ROOT_HUB_DESCRIPTOR_LENGTH;
172 	}
173 
174 	/* Determine the Power Switching Mode */
175 	root_hub_descr->bPwrOn2PwrGood = 10; /* arbitrary number */
176 	root_hub_descr->wHubCharacteristics =
177 	    HUB_CHARS_NO_POWER_SWITCHING|HUB_CHARS_NO_OVER_CURRENT;
178 
179 	/* Indicate if the device is removable */
180 	root_hub_descr->DeviceRemovable = 0x0;
181 
182 	/* Fill in the port power control mask */
183 	root_hub_descr->PortPwrCtrlMask = 0xff;
184 
185 	for (i = 0; i < uhcip->uhci_root_hub.rh_num_ports; i++) {
186 		uhcip->uhci_root_hub.rh_port_state[i]  = DISCONNECTED;
187 		uhcip->uhci_root_hub.rh_port_status[i] = 0;
188 		uhcip->uhci_root_hub.rh_port_changes[i] = 0;
189 	}
190 
191 	/* Finally load the root hub driver */
192 	return (usba_hubdi_bind_root_hub(uhcip->uhci_dip, uhci_rh_config_descr,
193 	    sizeof (uhci_rh_config_descr), &uhci_rh_dev_descr));
194 }
195 
196 
197 /*
198  * uhci_handle_root_hub_request:
199  *	Intercept a root hub request.
200  *	Handle the  root hub request through the registers
201  */
202 int
203 uhci_handle_root_hub_request(
204 	uhci_state_t		*uhcip,
205 	usba_pipe_handle_data_t  *pipe_handle,
206 	usb_ctrl_req_t		*req)
207 {
208 	int		error = USB_SUCCESS;
209 	uint16_t	port = req->ctrl_wIndex - 1;
210 	usb_cr_t	completion_reason;
211 
212 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
213 	    "uhci_handle_root_hub_request: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%p",
214 	    req->ctrl_bmRequestType, req->ctrl_bRequest, req->ctrl_wValue,
215 	    req->ctrl_wIndex, req->ctrl_wLength, (void *)req->ctrl_data);
216 
217 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
218 
219 	switch (req->ctrl_bmRequestType) {
220 	case HUB_GET_DEVICE_STATUS_TYPE:
221 		uhci_handle_get_device_status(uhcip, req);
222 
223 		break;
224 	case HUB_HANDLE_PORT_FEATURE_TYPE:
225 		error = uhci_handle_set_clear_port_feature(uhcip,
226 		    req->ctrl_bRequest, req->ctrl_wValue, port);
227 
228 		break;
229 	case HUB_GET_PORT_STATUS_TYPE:
230 		uhci_handle_get_port_status(uhcip, req, port);
231 
232 		break;
233 	case HUB_CLASS_REQ_TYPE:
234 		switch (req->ctrl_bRequest) {
235 		case USB_REQ_GET_DESCR:
236 			uhci_handle_get_hub_descriptor(uhcip, req);
237 
238 			break;
239 		case USB_REQ_GET_STATUS:
240 			uhci_handle_get_hub_status(uhcip, req);
241 
242 			break;
243 		default:
244 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
245 			    "uhci_handle_root_hub_request: Unsupported "
246 			    "request 0x%x", req->ctrl_bmRequestType);
247 
248 			break;
249 		}
250 
251 		break;
252 	default:
253 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
254 		    "uhci_handle_root_hub_request: Unsupported request 0x%x",
255 		    req->ctrl_bmRequestType);
256 
257 		break;
258 	}
259 
260 	completion_reason = (error != USB_SUCCESS) ?
261 	    USB_CR_NOT_SUPPORTED : USB_CR_OK;
262 
263 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
264 	    "uhci_handle_root_hub_request: error = %d", error);
265 
266 	uhci_rh_hcdi_callback(uhcip, pipe_handle, (usb_opaque_t)req,
267 	    completion_reason);
268 
269 	return (USB_SUCCESS);
270 }
271 
272 
273 /*
274  * uhci_handle_set_clear_port_feature:
275  */
276 static int
277 uhci_handle_set_clear_port_feature(
278 	uhci_state_t		*uhcip,
279 	uchar_t			bRequest,
280 	uint16_t		wValue,
281 	usb_port_t		port)
282 {
283 	int    error = USB_SUCCESS;
284 
285 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
286 	    "uhci_handle_set_clear_port_feature: 0x%x 0x%x 0x%x",
287 	    bRequest, wValue, port);
288 
289 	switch (bRequest) {
290 	case USB_REQ_SET_FEATURE:
291 		switch (wValue) {
292 		case CFS_PORT_ENABLE:
293 			uhci_handle_port_enable_disable(uhcip,
294 			    port, UHCI_ENABLE_PORT);
295 			break;
296 		case CFS_PORT_SUSPEND:
297 			uhci_handle_port_suspend(uhcip, port, 1);
298 
299 			break;
300 		case CFS_PORT_RESET:
301 			uhci_handle_port_reset(uhcip, port);
302 
303 			break;
304 		case CFS_PORT_POWER:
305 			uhci_handle_port_power(uhcip, port,
306 			    UHCI_ENABLE_PORT_PWR);
307 			break;
308 
309 		default:
310 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
311 			    "uhci_handle_set_clear_port_feature: "
312 			    "Unsupported request 0x%x 0x%x", bRequest, wValue);
313 			error = USB_FAILURE;
314 
315 			break;
316 		}
317 
318 		break;
319 	case USB_REQ_CLEAR_FEATURE:
320 		switch (wValue) {
321 		case CFS_PORT_ENABLE:
322 			uhci_handle_port_enable_disable(uhcip,
323 			    port, UHCI_DISABLE_PORT);
324 
325 			break;
326 		case CFS_C_PORT_ENABLE:
327 			uhci_handle_port_enable_disable(uhcip,
328 			    port, UHCI_CLEAR_ENDIS_BIT);
329 
330 			break;
331 		case CFS_PORT_SUSPEND:
332 			uhci_handle_port_suspend(uhcip, port, 0);
333 
334 			break;
335 		case CFS_C_PORT_RESET:
336 			uhci_handle_complete_port_reset(uhcip, port);
337 
338 			break;
339 		case CFS_PORT_POWER:
340 			uhci_handle_port_power(uhcip, port,
341 			    UHCI_DISABLE_PORT_PWR);
342 
343 			break;
344 		case CFS_C_PORT_CONNECTION:
345 			uhci_handle_clear_port_connection(uhcip, port);
346 
347 			break;
348 		default:
349 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
350 			    "uhci_handle_set_clear_port_feature: "
351 			    "Unsupported request 0x%x 0x%x", bRequest, wValue);
352 			error = USB_FAILURE;
353 
354 			break;
355 		}
356 
357 		break;
358 	default:
359 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
360 		    "uhci_handle_set_clear_port_feature: "
361 		    "Unsupported request 0x%x 0x%x", bRequest, wValue);
362 		error = USB_FAILURE;
363 	}
364 
365 
366 	return (error);
367 }
368 
369 
370 /*
371  * uhci_handle_port_suspend:
372  */
373 static void
374 uhci_handle_port_suspend(
375 	uhci_state_t		*uhcip,
376 	usb_port_t		port,
377 	uint_t			on)
378 {
379 	uint_t	port_status = Get_OpReg16(PORTSC[port]);
380 
381 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
382 	    "uhci_handle_port_suspend: port=%d on=%d",
383 	    port, on);
384 
385 	if (on) {
386 		/* See if the port suspend is already on */
387 		if (!(port_status & HCR_PORT_SUSPEND)) {
388 			/* suspend the port */
389 			Set_OpReg16(PORTSC[port],
390 			    (port_status | HCR_PORT_SUSPEND));
391 		}
392 	} else {
393 		/* See if the port suspend is already off */
394 		if ((port_status & HCR_PORT_SUSPEND)) {
395 			/* resume the port */
396 			Set_OpReg16(PORTSC[port],
397 			    (port_status & ~HCR_PORT_SUSPEND));
398 		}
399 	}
400 }
401 
402 
403 /*
404  * uhci_handle_port_power:
405  *	Turn on a root hub port.  NOTE: Driver does not have any control
406  *	over the power status.
407  */
408 /* ARGSUSED */
409 static void
410 uhci_handle_port_power(
411 	uhci_state_t		*uhcip,
412 	usb_port_t		port,
413 	uint_t			on)
414 {
415 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
416 	    "uhci_handle_port_power: nothing to do");
417 }
418 
419 
420 /*
421  * uhci_handle_port_enable_disable:
422  *	Handle port enable request.
423  */
424 static void
425 uhci_handle_port_enable_disable(
426 	uhci_state_t		*uhcip,
427 	usb_port_t		port,
428 	uint_t			action)
429 {
430 	uint_t	port_status = Get_OpReg16(PORTSC[port]);
431 
432 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
433 	    "uhci_handle_port_enable: port = 0x%x, status = 0x%x",
434 	    port, port_status);
435 
436 	if (action == UHCI_ENABLE_PORT) {
437 		/* See if the port enable is already on */
438 		if (!(port_status & HCR_PORT_ENABLE)) {
439 			/* Enable the port */
440 			Set_OpReg16(PORTSC[port],
441 			    (port_status | HCR_PORT_ENABLE));
442 		}
443 	} else if (action == UHCI_DISABLE_PORT) {
444 		/* See if the port enable is already off */
445 		if ((port_status & HCR_PORT_ENABLE)) {
446 			/* Disable the port */
447 			Set_OpReg16(PORTSC[port],
448 			    (port_status & ~HCR_PORT_ENABLE));
449 		}
450 	} else {
451 		/* Clear the Enable/Disable change bit */
452 		Set_OpReg16(PORTSC[port], (port_status | HCR_PORT_ENDIS_CHG));
453 
454 		/* Update software port_changes register */
455 		uhcip->uhci_root_hub.rh_port_changes[port] &= ~PORT_CHANGE_PESC;
456 	}
457 }
458 
459 
460 /*
461  * uhci_root_hub_reset_occurred:
462  *	Inform the upper layer that reset has occured on the port.
463  *	This is required because the upper layer is expecting an
464  *	event immediately after doing a reset. In case of OHCI
465  *	the HC gets an interrupt for the change in the root hub
466  *	status, but in case of UHCI we don't. So, we send an
467  *	event to the upper layer as soon as we complete the reset
468  *	as long as the root hub pipe is polling.
469  */
470 void
471 uhci_root_hub_reset_occurred(
472 	uhci_state_t	*uhcip,
473 	uint16_t	port)
474 {
475 	usb_intr_req_t	*intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp;
476 
477 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
478 	    "uhci_root_hub_reset_occurred: intr_reqp = 0x%p data = 0x%p",
479 	    (void *)intr_reqp, (void *)intr_reqp->intr_data);
480 
481 	*intr_reqp->intr_data->b_wptr++ = (1 << (port+1));
482 
483 	uhci_rh_hcdi_callback(uhcip, uhcip->uhci_root_hub.rh_intr_pipe_handle,
484 	    (usb_opaque_t)intr_reqp, USB_CR_OK);
485 }
486 
487 
488 /*
489  * uhci_handle_port_reset:
490  *	Perform a port reset.
491  */
492 static void
493 uhci_handle_port_reset(
494 	uhci_state_t		*uhcip,
495 	usb_port_t		port)
496 {
497 	uint_t	port_status = Get_OpReg16(PORTSC[port]);
498 
499 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
500 	    "uhci_handle_port_reset: port = 0x%x, status = 0x%x",
501 	    port, port_status);
502 
503 	if (!(port_status & HCR_PORT_CCS)) {
504 		USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
505 		    "port_status & HCR_PORT_CCS == 0: "
506 		    "port = 0x%x, status = 0x%x", port, port_status);
507 	}
508 
509 	Set_OpReg16(PORTSC[port], (port_status| HCR_PORT_RESET));
510 
511 	drv_usecwait(UHCI_RESET_DELAY);
512 
513 	Set_OpReg16(PORTSC[port], (port_status & ~HCR_PORT_RESET));
514 
515 	drv_usecwait(UHCI_RESET_DELAY/100);
516 
517 	Set_OpReg16(PORTSC[port], (port_status| HCR_PORT_ENABLE));
518 
519 	/*
520 	 * The next function is only called if the interrupt pipe
521 	 * is polling and the USBA is ready to receive the
522 	 * data. If not, we could panic.
523 	 */
524 	if (uhcip->uhci_root_hub.rh_pipe_state != UHCI_PIPE_STATE_ACTIVE) {
525 		/* make a note that we need to send status back */
526 		uhcip->uhci_root_hub.rh_status = port + 1;
527 	} else {
528 		uhci_root_hub_reset_occurred(uhcip, port);
529 	}
530 }
531 
532 
533 /*
534  * uhci_handle_complete_port_reset:
535  *	Perform a port reset change.
536  */
537 static void
538 uhci_handle_complete_port_reset(
539 	uhci_state_t		*uhcip,
540 	usb_port_t		port)
541 {
542 	uint_t port_status = Get_OpReg16(PORTSC[port]);
543 
544 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
545 	    "uhci_handle_complete_port_reset: port = 0x%x status = 0x%x",
546 	    port, port_status);
547 
548 	if (!(port_status & HCR_PORT_CCS)) {
549 		USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
550 		    "port_status & HCR_PORT_CCS == 0: "
551 		    "port = 0x%x, status = 0x%x", port, port_status);
552 	}
553 
554 	Set_OpReg16(PORTSC[port], (port_status & (~ HCR_PORT_RESET)));
555 
556 	/* Update software port_changes register */
557 	uhcip->uhci_root_hub.rh_port_changes[port] &= ~PORT_CHANGE_PRSC;
558 }
559 
560 
561 /*
562  * uhci_handle_clear_port_connection:
563  *	Perform a clear port connection.
564  */
565 static void
566 uhci_handle_clear_port_connection(
567 	uhci_state_t		*uhcip,
568 	usb_port_t		port)
569 {
570 	uint_t port_status = Get_OpReg16(PORTSC[port]);
571 
572 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
573 	    "uhci_handle_clear_port_connection: port = 0x%x status = 0x%x",
574 	    port, port_status);
575 
576 	/* Clear CSC bit */
577 	Set_OpReg16(PORTSC[port], port_status | HCR_PORT_CSC);
578 
579 	/* Update software port_changes register */
580 	uhcip->uhci_root_hub.rh_port_changes[port] &= ~PORT_CHANGE_CSC;
581 }
582 
583 
584 /*
585  * uhci_handle_get_port_status:
586  *	Handle a get port status request.
587  */
588 static void
589 uhci_handle_get_port_status(
590 	uhci_state_t		*uhcip,
591 	usb_ctrl_req_t		*req,
592 	usb_port_t		port)
593 {
594 	uint_t		new_port_status;
595 	uint_t		old_port_status =
596 	    uhcip->uhci_root_hub.rh_port_status[port];
597 	uint_t		old_port_changes =
598 	    uhcip->uhci_root_hub.rh_port_changes[port];
599 	uint_t		change_status;
600 	usb_ctrl_req_t	*ctrl_reqp = (usb_ctrl_req_t *)req;
601 	uint16_t	wLength = req->ctrl_wLength;
602 
603 	ASSERT(wLength == 4);
604 	ASSERT(ctrl_reqp->ctrl_data != NULL);
605 
606 	/* Read the current port status and return it */
607 	new_port_status = uhci_get_port_status(uhcip, port);
608 	change_status	= (old_port_status ^ new_port_status) & 0xff;
609 	change_status	|= old_port_changes;
610 
611 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
612 	    "uhci_handle_get_port_status:\n\t"
613 	    "port%d: old status = 0x%x	new status = 0x%x change = 0x%x",
614 	    port, old_port_status, new_port_status, change_status);
615 
616 	*ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)new_port_status;
617 	*ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)(new_port_status >> 8);
618 	*ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)change_status;
619 	*ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)(change_status >> 8);
620 
621 	/* Update the status */
622 	uhcip->uhci_root_hub.rh_port_status[port] = new_port_status;
623 	uhcip->uhci_root_hub.rh_port_changes[port] = change_status;
624 }
625 
626 
627 /*
628  * uhci_handle_get_hub_descriptor:
629  */
630 static void
631 uhci_handle_get_hub_descriptor(
632 	uhci_state_t		*uhcip,
633 	usb_ctrl_req_t		*req)
634 {
635 	uchar_t		raw_descr[ROOT_HUB_DESCRIPTOR_LENGTH];
636 	usb_hub_descr_t	*root_hub_descr = &uhcip->uhci_root_hub.rh_descr;
637 
638 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
639 	    "uhci_handle_get_hub_descriptor: wLength = 0x%x",
640 	    req->ctrl_wLength);
641 
642 	ASSERT(req->ctrl_wLength != 0);
643 	ASSERT(req->ctrl_data != NULL);
644 
645 	bzero(&raw_descr, ROOT_HUB_DESCRIPTOR_LENGTH);
646 
647 	raw_descr[0] = root_hub_descr->bDescLength;
648 	raw_descr[1] = root_hub_descr->bDescriptorType;
649 	raw_descr[2] = root_hub_descr->bNbrPorts;
650 	raw_descr[3] = root_hub_descr->wHubCharacteristics & 0x00ff;
651 	raw_descr[4] = (root_hub_descr->wHubCharacteristics & 0xff00) >> 8;
652 	raw_descr[5] = root_hub_descr->bPwrOn2PwrGood;
653 	raw_descr[6] = root_hub_descr->bHubContrCurrent;
654 	raw_descr[7] = root_hub_descr->DeviceRemovable;
655 	raw_descr[8] = root_hub_descr->PortPwrCtrlMask;
656 
657 	bcopy(raw_descr, req->ctrl_data->b_wptr, req->ctrl_wLength);
658 	req->ctrl_data->b_wptr += req->ctrl_wLength;
659 }
660 
661 
662 /*
663  * uhci_handle_get_hub_status:
664  */
665 static void
666 uhci_handle_get_hub_status(
667 	uhci_state_t		*uhcip,
668 	usb_ctrl_req_t		*req)
669 {
670 
671 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
672 	    "uhci_handle_get_hub_status: wLength = 0x%x",
673 	    req->ctrl_wLength);
674 	ASSERT(req->ctrl_wLength != 0);
675 	ASSERT(req->ctrl_data != NULL);
676 
677 	/*
678 	 * A good status is always sent because there is no way that
679 	 * the driver can get to know about the status change of the
680 	 * over current or power failure of the root hub from the HC.
681 	 */
682 	bzero(req->ctrl_data->b_wptr, req->ctrl_wLength);
683 	req->ctrl_data->b_wptr += req->ctrl_wLength;
684 }
685 
686 
687 /*
688  * uhci_handle_get_device_status:
689  */
690 static void
691 uhci_handle_get_device_status(
692 	uhci_state_t		*uhcip,
693 	usb_ctrl_req_t		*req)
694 {
695 	uint16_t	dev_status;
696 
697 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
698 	    "uhci_handle_get_device_status: wLength = 0x%x",
699 	    req->ctrl_wLength);
700 
701 	ASSERT(req->ctrl_wLength != 0);
702 	ASSERT(req->ctrl_data != NULL);
703 
704 	/*
705 	 * UHCI doesn't have device status information.
706 	 * Simply return what is desired for the request.
707 	 */
708 	dev_status = USB_DEV_SLF_PWRD_STATUS;
709 
710 	*req->ctrl_data->b_wptr++ = (uchar_t)dev_status;
711 	*req->ctrl_data->b_wptr++ = (uchar_t)(dev_status >> 8);
712 }
713 
714 
715 /*
716  * uhci_handle_root_hub_status_change:
717  *	This function is called every 32 seconds from the time out handler.
718  *	It checks for the status change of the root hub and its ports.
719  */
720 void
721 uhci_handle_root_hub_status_change(void *arg)
722 {
723 	usb_port_t	port;
724 	uint_t		old_port_status;
725 	uint_t		new_port_status;
726 	ushort_t	port_status;
727 	uint_t		change_status;
728 	uchar_t		all_ports_status = 0;
729 	uhci_state_t	*uhcip = (uhci_state_t *)arg;
730 	usb_intr_req_t	*curr_intr_reqp;
731 
732 	mutex_enter(&uhcip->uhci_int_mutex);
733 
734 	/* reset the timeout id */
735 	uhcip->uhci_timeout_id = 0;
736 
737 	/* Get the current interrupt request pointer */
738 	curr_intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp;
739 
740 	/* Check each port */
741 	for (port = 0; port < uhcip->uhci_root_hub.rh_num_ports; port++) {
742 		new_port_status = uhci_get_port_status(uhcip, port);
743 		old_port_status = uhcip->uhci_root_hub.rh_port_status[port];
744 
745 		change_status = (old_port_status ^ new_port_status) & 0xff;
746 		change_status |= uhcip->uhci_root_hub.rh_port_changes[port];
747 
748 		/* See if a device was attached/detached */
749 		if (change_status & PORT_STATUS_CCS) {
750 			all_ports_status |= 1 << (port + 1);
751 		}
752 
753 		port_status = Get_OpReg16(PORTSC[port]);
754 		Set_OpReg16(PORTSC[port], port_status | HCR_PORT_ENDIS_CHG);
755 
756 		uhcip->uhci_root_hub.rh_port_status[port] = new_port_status;
757 		uhcip->uhci_root_hub.rh_port_changes[port] = change_status;
758 
759 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
760 		    "port %d old status 0x%x new status 0x%x change 0x%x\n\t"
761 		    "all_ports_status = 0x%x", port, old_port_status,
762 		    new_port_status, change_status, all_ports_status);
763 	}
764 
765 	if (uhcip->uhci_root_hub.rh_intr_pipe_handle &&
766 	    all_ports_status && curr_intr_reqp &&
767 	    (uhcip->uhci_root_hub.rh_pipe_state == UHCI_PIPE_STATE_ACTIVE)) {
768 
769 		ASSERT(curr_intr_reqp->intr_data != NULL);
770 
771 		*curr_intr_reqp->intr_data->b_wptr++ = all_ports_status;
772 
773 		uhci_rh_hcdi_callback(uhcip,
774 		    uhcip->uhci_root_hub.rh_intr_pipe_handle,
775 		    (usb_opaque_t)curr_intr_reqp, USB_CR_OK);
776 	}
777 
778 	if (uhcip->uhci_root_hub.rh_pipe_state == UHCI_PIPE_STATE_ACTIVE) {
779 		/*
780 		 * If needed, allocate new interrupt request. Also
781 		 * start the timer for the root hub interrupt polling.
782 		 */
783 		if (uhci_root_hub_allocate_intr_pipe_resource(uhcip, 0) !=
784 		    USB_SUCCESS) {
785 
786 			/* Do interrupt pipe cleanup */
787 			uhci_root_hub_intr_pipe_cleanup(uhcip,
788 			    USB_CR_NO_RESOURCES);
789 		}
790 	}
791 
792 	mutex_exit(&uhcip->uhci_int_mutex);
793 }
794 
795 
796 static uint_t
797 uhci_get_port_status(
798 	uhci_state_t	*uhcip,
799 	usb_port_t	port)
800 {
801 	uint_t		new_port_status = PORT_STATUS_PPS;
802 	ushort_t	port_status = Get_OpReg16(PORTSC[port]);
803 
804 	if (port_status & HCR_PORT_CCS) {
805 		new_port_status |= PORT_STATUS_CCS;
806 	}
807 
808 	if (port_status & HCR_PORT_LSDA) {
809 		new_port_status |= PORT_STATUS_LSDA;
810 	}
811 
812 	if (port_status & HCR_PORT_ENABLE) {
813 		new_port_status |= PORT_STATUS_PES;
814 	}
815 
816 	if (port_status & HCR_PORT_SUSPEND) {
817 		new_port_status |= PORT_STATUS_PSS;
818 	}
819 
820 	if (port_status & HCR_PORT_RESET) {
821 		new_port_status |= PORT_STATUS_PRS;
822 	}
823 
824 	return (new_port_status);
825 }
826 
827 
828 /*
829  * uhci_root_hub_allocate_intr_pipe_resource:
830  *	Allocate interrupt requests and initialize them.
831  */
832 int
833 uhci_root_hub_allocate_intr_pipe_resource(
834 	uhci_state_t	*uhcip,
835 	usb_flags_t	flags)
836 {
837 	usb_intr_req_t		*curr_intr_reqp;
838 	usba_pipe_handle_data_t	*ph;
839 
840 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
841 	    "uhci_root_hub_allocate_intr_pipe_resource:");
842 
843 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
844 
845 	/* Get the interrupt pipe handle */
846 	ph = uhcip->uhci_root_hub.rh_intr_pipe_handle;
847 
848 	/* Get the current interrupt request pointer */
849 	curr_intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp;
850 
851 	/*
852 	 * If current interrupt request pointer is null,
853 	 * allocate new interrupt request.
854 	 */
855 	if (curr_intr_reqp == NULL) {
856 		ASSERT(uhcip->uhci_root_hub.rh_client_intr_req);
857 
858 		if ((curr_intr_reqp = usba_hcdi_dup_intr_req(ph->p_dip,
859 		    uhcip->uhci_root_hub.rh_client_intr_req,
860 		    uhcip->uhci_root_hub.rh_client_intr_req->intr_len,
861 		    flags)) == NULL) {
862 			USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
863 			    "uhci_root_hub_allocate_intr_pipe_resource:"
864 			    "Interrupt request structure allocation failed");
865 
866 			return (USB_NO_RESOURCES);
867 		}
868 
869 		uhcip->uhci_root_hub.rh_curr_intr_reqp = curr_intr_reqp;
870 
871 		mutex_enter(&ph->p_mutex);
872 		ph->p_req_count++;
873 		mutex_exit(&ph->p_mutex);
874 	}
875 
876 	if (uhcip->uhci_timeout_id == 0) {
877 		uhcip->uhci_timeout_id = timeout(
878 		    uhci_handle_root_hub_status_change,
879 		    (void *)uhcip, UHCI_32_MS);
880 		uhcip->uhci_root_hub.rh_pipe_state =
881 		    UHCI_PIPE_STATE_ACTIVE;
882 	}
883 
884 	return (USB_SUCCESS);
885 }
886 
887 
888 /*
889  * uhci_root_hub_intr_pipe_cleanup:
890  *	Deallocate all interrupt requests and do callback
891  *	the original client interrupt request.
892  */
893 void
894 uhci_root_hub_intr_pipe_cleanup(uhci_state_t *uhcip, usb_cr_t cr)
895 {
896 	usb_intr_req_t		*curr_intr_reqp;
897 	usb_opaque_t		client_intr_reqp;
898 	usba_pipe_handle_data_t	*ph;
899 	timeout_id_t		timer_id;
900 
901 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
902 	    "uhci_root_hub_intr_pipe_cleanup:");
903 
904 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
905 
906 	/* Get the interrupt pipe handle */
907 	ph = uhcip->uhci_root_hub.rh_intr_pipe_handle;
908 
909 	/* Get the interrupt timerid */
910 	timer_id = uhcip->uhci_timeout_id;
911 
912 	/* Stop the root hub interrupt timer */
913 	if (timer_id) {
914 
915 		/* Reset the timer id to zero */
916 		uhcip->uhci_timeout_id = 0;
917 		uhcip->uhci_root_hub.rh_pipe_state =
918 		    UHCI_PIPE_STATE_IDLE;
919 
920 		mutex_exit(&uhcip->uhci_int_mutex);
921 		(void) untimeout(timer_id);
922 		mutex_enter(&uhcip->uhci_int_mutex);
923 	}
924 
925 	/* Reset the current interrupt request pointer */
926 	curr_intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp;
927 
928 	/* Deallocate uncompleted interrupt request */
929 	if (curr_intr_reqp) {
930 		uhcip->uhci_root_hub.rh_curr_intr_reqp = NULL;
931 		usb_free_intr_req(curr_intr_reqp);
932 
933 		mutex_enter(&ph->p_mutex);
934 		ph->p_req_count--;
935 		mutex_exit(&ph->p_mutex);
936 	}
937 
938 	client_intr_reqp = (usb_opaque_t)
939 	    uhcip->uhci_root_hub.rh_client_intr_req;
940 
941 	/* Callback for original client interrupt request */
942 	if (client_intr_reqp) {
943 		uhcip->uhci_root_hub.rh_client_intr_req = NULL;
944 		uhci_rh_hcdi_callback(uhcip, ph,
945 		    (usb_opaque_t)client_intr_reqp, cr);
946 	}
947 }
948 
949 
950 /*
951  * uhci_rh_hcdi_callback:
952  *	Convenience wrapper around usba_hcdi_cb() for the root hub.
953  */
954 static void
955 uhci_rh_hcdi_callback(
956 	uhci_state_t		*uhcip,
957 	usba_pipe_handle_data_t	*ph,
958 	usb_opaque_t		req,
959 	usb_cr_t		cr)
960 {
961 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
962 	    "uhci_rh_hcdi_callback: ph=0x%p cr=0x%x req=0x%p",
963 	    (void *)ph, cr, (void *)req);
964 
965 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
966 
967 	switch (UHCI_XFER_TYPE(&ph->p_ep)) {
968 	case USB_EP_ATTR_CONTROL:
969 
970 		break;
971 	case USB_EP_ATTR_INTR:
972 		if ((usb_intr_req_t *)req ==
973 		    uhcip->uhci_root_hub.rh_curr_intr_reqp) {
974 			uhcip->uhci_root_hub.rh_curr_intr_reqp = NULL;
975 
976 			break;
977 		} else if ((usb_intr_req_t *)req ==
978 		    uhcip->uhci_root_hub.rh_client_intr_req) {
979 			uhcip->uhci_root_hub.rh_client_intr_req = NULL;
980 
981 			break;
982 		}
983 		/*FALLTHRU*/
984 	default:
985 		ASSERT(req);
986 		break;
987 	}
988 
989 	mutex_exit(&uhcip->uhci_int_mutex);
990 	usba_hcdi_cb(ph, req, cr);
991 	mutex_enter(&uhcip->uhci_int_mutex);
992 }
993