3 * SPDX-License-Identifier: BSD-2-Clause-NetBSD
5 * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.
6 * Copyright (c) 1998 Lennart Augustsson. All rights reserved.
7 * Copyright (c) 2008-2010 Hans Petter Selasky. All rights reserved.
8 * Copyright (c) 2019 Takanori Watanabe.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * USB spec: http://www.usb.org/developers/docs/usbspec.zip
36 #ifdef USB_GLOBAL_INCLUDE_FILE
37 #include USB_GLOBAL_INCLUDE_FILE
39 #include <sys/stdint.h>
40 #include <sys/stddef.h>
41 #include <sys/param.h>
42 #include <sys/queue.h>
43 #include <sys/types.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
47 #include <sys/module.h>
49 #include <sys/mutex.h>
50 #include <sys/condvar.h>
51 #include <sys/sysctl.h>
53 #include <sys/unistd.h>
54 #include <sys/callout.h>
55 #include <sys/malloc.h>
58 #include <dev/usb/usb.h>
59 #include <dev/usb/usbdi.h>
60 #include <dev/usb/usbdi_util.h>
62 #define USB_DEBUG_VAR uhub_debug
64 #include <dev/usb/usb_core.h>
65 #include <dev/usb/usb_process.h>
66 #include <dev/usb/usb_device.h>
67 #include <dev/usb/usb_request.h>
68 #include <dev/usb/usb_debug.h>
69 #include <dev/usb/usb_hub.h>
70 #include <dev/usb/usb_util.h>
71 #include <dev/usb/usb_busdma.h>
72 #include <dev/usb/usb_transfer.h>
73 #include <dev/usb/usb_dynamic.h>
75 #include <dev/usb/usb_controller.h>
76 #include <dev/usb/usb_bus.h>
77 #endif /* USB_GLOBAL_INCLUDE_FILE */
78 #include <dev/usb/usb_hub_private.h>
79 #include <contrib/dev/acpica/include/acpi.h>
80 #include <contrib/dev/acpica/include/accommon.h>
81 #include <dev/acpica/acpivar.h>
84 #define ACPI_PLD_SIZE 20
85 struct acpi_uhub_port {
87 #define ACPI_UPC_CONNECTABLE 0x80000000
88 #define ACPI_UPC_PORTTYPE(x) ((x)&0xff)
90 uint8_t pld[ACPI_PLD_SIZE];
93 struct acpi_uhub_softc {
94 struct uhub_softc usc;
97 struct acpi_uhub_port *port;
101 acpi_uhub_find_rh_cb(ACPI_HANDLE ah, UINT32 nl, void *ctx, void **status)
103 ACPI_DEVICE_INFO *devinfo;
109 ret = AcpiGetObjectInfo(ah, &devinfo);
110 if (ACPI_SUCCESS(ret)) {
111 if ((devinfo->Valid & ACPI_VALID_ADR) &&
112 (devinfo->Address == 0)) {
113 ret = AE_CTRL_TERMINATE;
122 acpi_uhub_upc_type(uint8_t type)
124 const char *typelist[] = {"TypeA", "MiniAB", "Express",
125 "USB3-A", "USB3-B", "USB-MicroB",
126 "USB3-MicroAB", "USB3-PowerB",
127 "TypeC-USB2", "TypeC-Switch",
129 const int last = sizeof(typelist) / sizeof(typelist[0]);
132 return "Proprietary";
135 return (type < last) ? typelist[type] : "Unknown";
139 acpi_uhub_parse_upc(device_t dev, unsigned int p, ACPI_HANDLE ah, struct sysctl_oid_list *poid)
142 struct acpi_uhub_softc *sc = device_get_softc(dev);
143 struct acpi_uhub_port *port = &sc->port[p - 1];
146 buf.Length = ACPI_ALLOCATE_BUFFER;
148 if (AcpiEvaluateObject(ah, "_UPC", NULL, &buf) == AE_OK) {
149 ACPI_OBJECT *obj = buf.Pointer;
150 UINT64 porttypenum, conn;
151 uint8_t *connectable;
153 acpi_PkgInt(obj, 0, &conn);
154 acpi_PkgInt(obj, 1, &porttypenum);
155 connectable = conn ? "" : "non";
157 port->upc = porttypenum;
158 port->upc |= (conn) ? (ACPI_UPC_CONNECTABLE) : 0;
161 device_printf(dev, "Port %u %sconnectable %s\n",
163 acpi_uhub_upc_type(porttypenum));
166 device_get_sysctl_ctx(dev),
169 CTLFLAG_RD | CTLFLAG_MPSAFE,
170 SYSCTL_NULL_U32_PTR, port->upc,
171 "UPC value. MSB is visible flag");
173 AcpiOsFree(buf.Pointer);
178 acpi_uhub_port_sysctl(SYSCTL_HANDLER_ARGS)
180 struct acpi_uhub_port *port = oidp->oid_arg1;
184 sbuf_new_for_sysctl(&sb, NULL, 256, req);
185 sbuf_printf(&sb, "Handle %s\n", acpi_name(port->handle));
186 if (port->upc == 0xffffffff) {
187 sbuf_printf(&sb, "\tNo information\n");
190 sbuf_printf(&sb, "\t");
191 if (port->upc & ACPI_UPC_CONNECTABLE) {
192 sbuf_printf(&sb, "Connectable ");
194 sbuf_printf(&sb, "%s port\n", acpi_uhub_upc_type(port->upc & 0xff));
196 if ((port->pld[0] & 0x80) == 0) {
198 "\tColor:#%02x%02x%02x\n",
199 port->pld[1], port->pld[2],
202 sbuf_printf(&sb, "\tWidth %d mm Height %d mm\n",
203 port->pld[4] | (port->pld[5] << 8),
204 port->pld[6] | (port->pld[7] << 8));
205 if (port->pld[8] & 1) {
206 sbuf_printf(&sb, "\tVisible\n");
208 if (port->pld[8] & 2) {
209 sbuf_printf(&sb, "\tDock\n");
211 if (port->pld[8] & 4) {
212 sbuf_printf(&sb, "\tLid\n");
214 int panelpos = (port->pld[8] >> 3) & 7;
215 const char *panposstr[] = {"Top", "Bottom", "Left",
216 "Right", "Front", "Back",
217 "Unknown", "Invalid"};
218 const char *shapestr[] = {
219 "Round", "Oval", "Square", "VRect", "HRect",
220 "VTrape", "HTrape", "Unknown", "Chamferd",
221 "Rsvd", "Rsvd", "Rsvd", "Rsvd",
222 "Rsvd", "Rsvd", "Rsvd", "Rsvd"};
224 sbuf_printf(&sb, "\tPanelPosition: %s\n", panposstr[panelpos]);
226 const char *posstr[] = {"Upper", "Center",
229 sbuf_printf(&sb, "\tVertPosition: %s\n",
230 posstr[(port->pld[8] >> 6) & 3]);
231 sbuf_printf(&sb, "\tHorizPosition: %s\n",
232 posstr[(port->pld[9]) & 3]);
234 sbuf_printf(&sb, "\tShape: %s\n",
235 shapestr[(port->pld[9] >> 2) & 0xf]);
236 sbuf_printf(&sb, "\tGroup Orientation %s\n",
237 ((port->pld[9] >> 6) & 1) ? "Vertical" :
239 sbuf_printf(&sb, "\tGroupToken %x\n",
241 | (port->pld[10] << 1)) & 0xff);
242 sbuf_printf(&sb, "\tGroupPosition %x\n",
243 ((port->pld[10] >> 7)
244 | (port->pld[11] << 1)) & 0xff);
245 sbuf_printf(&sb, "\t%s %s %s\n",
246 (port->pld[11] & 0x80) ?
248 (port->pld[12] & 1) ? "Eject" : "",
249 (port->pld[12] & 2) ? "OSPM" : ""
252 if ((port->pld[0] & 0x7f) >= 2) {
253 sbuf_printf(&sb, "\tVOFF%d mm HOFF %dmm",
254 port->pld[16] | (port->pld[17] << 8),
255 port->pld[18] | (port->pld[19] << 8));
259 error = sbuf_finish(&sb);
265 acpi_uhub_parse_pld(device_t dev, unsigned int p, ACPI_HANDLE ah, struct sysctl_oid_list *tree)
268 struct acpi_uhub_softc *sc = device_get_softc(dev);
269 struct acpi_uhub_port *port = &sc->port[p - 1];
272 buf.Length = ACPI_ALLOCATE_BUFFER;
274 if (AcpiEvaluateObject(ah, "_PLD", NULL, &buf) == AE_OK) {
276 unsigned char *resbuf;
281 if (obj->Type == ACPI_TYPE_PACKAGE
282 && obj->Package.Elements[0].Type == ACPI_TYPE_BUFFER) {
285 obj1 = &obj->Package.Elements[0];
286 len = obj1->Buffer.Length;
287 resbuf = obj1->Buffer.Pointer;
288 } else if (obj->Type == ACPI_TYPE_BUFFER) {
289 len = obj->Buffer.Length;
290 resbuf = obj->Buffer.Pointer;
294 len = (len < ACPI_PLD_SIZE) ? len : ACPI_PLD_SIZE;
295 memcpy(port->pld, resbuf, len);
297 device_get_sysctl_ctx(dev), tree, OID_AUTO,
298 "pldraw", CTLFLAG_RD | CTLFLAG_MPSAFE,
299 port->pld, len, "A", "Raw PLD value");
302 device_printf(dev, "Revision:%d\n",
304 if ((resbuf[0] & 0x80) == 0) {
306 "Color:#%02x%02x%02x\n",
307 resbuf[1], resbuf[2],
310 device_printf(dev, "Width %d mm Height %d mm\n",
311 resbuf[4] | (resbuf[5] << 8),
312 resbuf[6] | (resbuf[7] << 8));
314 device_printf(dev, "Visible\n");
317 device_printf(dev, "Dock\n");
320 device_printf(dev, "Lid\n");
322 device_printf(dev, "PanelPosition: %d\n",
323 (resbuf[8] >> 3) & 7);
324 device_printf(dev, "VertPosition: %d\n",
325 (resbuf[8] >> 6) & 3);
326 device_printf(dev, "HorizPosition: %d\n",
328 device_printf(dev, "Shape: %d\n",
329 (resbuf[9] >> 2) & 0xf);
330 device_printf(dev, "80: %02x, %02x, %02x\n",
331 resbuf[9], resbuf[10], resbuf[11]);
332 device_printf(dev, "96: %02x, %02x, %02x, %02x\n",
333 resbuf[12], resbuf[13],
334 resbuf[14], resbuf[15]);
336 if ((resbuf[0] & 0x7f) >= 2) {
337 device_printf(dev, "VOFF%d mm HOFF %dmm",
338 resbuf[16] | (resbuf[17] << 8),
339 resbuf[18] | (resbuf[19] << 8));
343 AcpiOsFree(buf.Pointer);
349 acpi_uhub_find_rh(device_t dev, ACPI_HANDLE *ah)
355 grand = device_get_parent(device_get_parent(dev));
357 if ((gah = acpi_get_handle(grand)) == NULL)
360 return (AcpiWalkNamespace(ACPI_TYPE_DEVICE, gah, 1,
361 acpi_uhub_find_rh_cb, NULL, dev, ah));
365 acpi_usb_hub_port_probe_cb(ACPI_HANDLE ah, UINT32 lv, void *ctx, void **rv)
367 ACPI_DEVICE_INFO *devinfo;
369 struct acpi_uhub_softc *sc = device_get_softc(dev);
372 ret = AcpiGetObjectInfo(ah, &devinfo);
373 if (ACPI_SUCCESS(ret)) {
374 if ((devinfo->Valid & ACPI_VALID_ADR) &&
375 (devinfo->Address > 0) &&
376 (devinfo->Address <= (uint64_t)sc->nports)) {
377 char buf[] = "portXXX";
378 struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
379 struct sysctl_oid *oid;
380 struct sysctl_oid_list *tree;
382 snprintf(buf, sizeof(buf), "port%ju",
383 (uintmax_t)devinfo->Address);
384 oid = SYSCTL_ADD_NODE(ctx,
385 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
386 OID_AUTO, buf, CTLFLAG_RD | CTLFLAG_MPSAFE,
388 tree = SYSCTL_CHILDREN(oid);
389 sc->port[devinfo->Address - 1].handle = ah;
390 sc->port[devinfo->Address - 1].upc = 0xffffffff;
391 acpi_uhub_parse_upc(dev, devinfo->Address, ah, tree);
392 acpi_uhub_parse_pld(dev, devinfo->Address, ah, tree);
393 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), tree,
395 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
396 &sc->port[devinfo->Address - 1], 0,
397 acpi_uhub_port_sysctl, "A", "Port information");
405 acpi_usb_hub_port_probe(device_t dev, ACPI_HANDLE ah)
407 return (AcpiWalkNamespace(ACPI_TYPE_DEVICE,
409 acpi_usb_hub_port_probe_cb,
414 acpi_uhub_root_probe(device_t dev)
419 if (acpi_disabled("usb"))
422 status = acpi_uhub_find_rh(dev, &ah);
423 if (ACPI_SUCCESS(status) && ah != NULL &&
424 uhub_probe(dev) <= 0) {
425 /* success prior than non-ACPI USB HUB */
426 return (BUS_PROBE_DEFAULT + 1);
432 acpi_uhub_probe(device_t dev)
436 if (acpi_disabled("usb"))
439 ah = acpi_get_handle(dev);
443 if (uhub_probe(dev) <= 0) {
444 /* success prior than non-ACPI USB HUB */
445 return (BUS_PROBE_DEFAULT + 1);
450 acpi_uhub_attach_common(device_t dev)
453 struct acpi_uhub_softc *sc = device_get_softc(dev);
457 uh = sc->usc.sc_udev->hub;
458 sc->nports = uh->nports;
459 sc->port = malloc(sizeof(struct acpi_uhub_port) * uh->nports,
460 M_USBDEV, M_WAITOK | M_ZERO);
461 status = acpi_usb_hub_port_probe(dev, sc->ah);
463 if (ACPI_SUCCESS(status)){
471 acpi_uhub_detach(device_t dev)
473 struct acpi_uhub_softc *sc = device_get_softc(dev);
475 free(sc->port, M_USBDEV);
477 return (uhub_detach(dev));
481 acpi_uhub_root_attach(device_t dev)
484 struct acpi_uhub_softc *sc = device_get_softc(dev);
486 if (ACPI_FAILURE(acpi_uhub_find_rh(dev, &sc->ah)) ||
490 if ((ret = uhub_attach(dev)) != 0) {
494 if ((ret = acpi_uhub_attach_common(dev)) != 0) {
495 acpi_uhub_detach(dev);
501 acpi_uhub_attach(device_t dev)
504 struct acpi_uhub_softc *sc = device_get_softc(dev);
506 sc->ah = acpi_get_handle(dev);
508 if (sc->ah == NULL) {
511 if ((ret = uhub_attach(dev)) != 0) {
515 if ((ret = acpi_uhub_attach_common(dev)) != 0) {
516 acpi_uhub_detach(dev);
523 acpi_uhub_read_ivar(device_t dev, device_t child, int idx, uintptr_t *res)
525 struct hub_result hres;
526 struct acpi_uhub_softc *sc = device_get_softc(dev);
530 uhub_find_iface_index(sc->usc.sc_udev->hub, child, &hres);
533 if ((idx == ACPI_IVAR_HANDLE) &&
535 (hres.portno <= sc->nports) &&
536 (ah = sc->port[hres.portno - 1].handle)) {
537 *res = (uintptr_t)ah;
544 acpi_uhub_child_location_string(device_t parent, device_t child,
545 char *buf, size_t buflen)
549 uhub_child_location_string(parent, child, buf, buflen);
551 ah = acpi_get_handle(child);
553 strlcat(buf, " handle=", buflen);
554 strlcat(buf, acpi_name(ah), buflen);
559 static device_method_t acpi_uhub_methods[] = {
560 DEVMETHOD(device_probe, acpi_uhub_probe),
561 DEVMETHOD(device_attach, acpi_uhub_attach),
562 DEVMETHOD(device_detach, acpi_uhub_detach),
563 DEVMETHOD(bus_child_location_str, acpi_uhub_child_location_string),
564 DEVMETHOD(bus_read_ivar, acpi_uhub_read_ivar),
569 static device_method_t acpi_uhub_root_methods[] = {
570 DEVMETHOD(device_probe, acpi_uhub_root_probe),
571 DEVMETHOD(device_attach, acpi_uhub_root_attach),
572 DEVMETHOD(device_detach, acpi_uhub_detach),
573 DEVMETHOD(bus_read_ivar, acpi_uhub_read_ivar),
574 DEVMETHOD(bus_child_location_str, acpi_uhub_child_location_string),
578 static devclass_t uhub_devclass;
579 extern driver_t uhub_driver;
580 static kobj_class_t uhub_baseclasses[] = {&uhub_driver, NULL};
582 static driver_t acpi_uhub_driver = {
584 .methods = acpi_uhub_methods,
585 .size = sizeof(struct acpi_uhub_softc),
586 .baseclasses = uhub_baseclasses,
589 static driver_t acpi_uhub_root_driver = {
591 .methods = acpi_uhub_root_methods,
592 .size = sizeof(struct acpi_uhub_softc),
593 .baseclasses = uhub_baseclasses,
596 DRIVER_MODULE(uacpi, uhub, acpi_uhub_driver, uhub_devclass, 0, 0);
597 DRIVER_MODULE(uacpi, usbus, acpi_uhub_root_driver, uhub_devclass, 0, 0);
599 MODULE_DEPEND(uacpi, acpi, 1, 1, 1);
600 MODULE_DEPEND(uacpi, usb, 1, 1, 1);
602 MODULE_VERSION(uacpi, 1);