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