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