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