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