xref: /freebsd/sys/dev/usb/usb_hub_acpi.c (revision c4e127e24dc9f1322ebe7ade0991de7022010bf1)
1 /* $FreeBSD$ */
2 /*-
3  * SPDX-License-Identifier: BSD-2-Clause-NetBSD
4  *
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.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
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.
18  *
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
29  * SUCH DAMAGE.
30  */
31 
32 /*
33  * USB spec: http://www.usb.org/developers/docs/usbspec.zip
34  */
35 
36 #ifdef USB_GLOBAL_INCLUDE_FILE
37 #include USB_GLOBAL_INCLUDE_FILE
38 #else
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>
46 #include <sys/bus.h>
47 #include <sys/module.h>
48 #include <sys/lock.h>
49 #include <sys/mutex.h>
50 #include <sys/condvar.h>
51 #include <sys/sysctl.h>
52 #include <sys/sx.h>
53 #include <sys/unistd.h>
54 #include <sys/callout.h>
55 #include <sys/malloc.h>
56 #include <sys/priv.h>
57 
58 #include <dev/usb/usb.h>
59 #include <dev/usb/usbdi.h>
60 #include <dev/usb/usbdi_util.h>
61 
62 #define	USB_DEBUG_VAR uhub_debug
63 
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>
74 
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>
82 
83 static UINT32 acpi_uhub_find_rh_cb(ACPI_HANDLE ah, UINT32 nl, void *ctx, void **status);
84 static ACPI_STATUS acpi_uhub_find_rh(device_t dev, ACPI_HANDLE * ah);
85 static ACPI_STATUS
86 acpi_usb_hub_port_probe_cb(ACPI_HANDLE ah, UINT32 lv, void *ctx, void **rv);
87 static ACPI_STATUS acpi_usb_hub_port_probe(device_t dev, ACPI_HANDLE ah);
88 static int acpi_uhub_root_probe(device_t dev);
89 static int acpi_uhub_probe(device_t dev);
90 static int acpi_uhub_root_attach(device_t dev);
91 static int acpi_uhub_attach(device_t dev);
92 static int acpi_uhub_detach(device_t dev);
93 static int
94 acpi_uhub_read_ivar(device_t dev, device_t child, int idx,
95     uintptr_t *res);
96 static int
97 acpi_uhub_child_location_string(device_t parent, device_t child,
98     char *buf, size_t buflen);
99 static int acpi_uhub_parse_upc(device_t dev, unsigned int port, ACPI_HANDLE ah);
100 
101 struct acpi_uhub_softc {
102 	struct uhub_softc usc;
103 	uint8_t	nports;
104 	ACPI_HANDLE *porthandle;
105 };
106 
107 UINT32
108 acpi_uhub_find_rh_cb(ACPI_HANDLE ah, UINT32 nl, void *ctx, void **status){
109 	ACPI_DEVICE_INFO *devinfo;
110 	UINT32 ret = AE_OK;
111 
112 	*status = NULL;
113 	devinfo = NULL;
114 
115 	ret = AcpiGetObjectInfo(ah, &devinfo);
116 
117 	if (ACPI_FAILURE(ret)) {
118 		return ret;
119 	}
120 	if ((devinfo->Valid & ACPI_VALID_ADR) &&
121 	    (devinfo->Address == 0)) {
122 		ret = AE_CTRL_TERMINATE;
123 		*status = ah;
124 	}
125 	AcpiOsFree(devinfo);
126 
127 	return ret;
128 }
129 
130 static int
131 acpi_uhub_parse_upc(device_t dev, unsigned int port, ACPI_HANDLE ah)
132 {
133 	ACPI_BUFFER buf;
134 
135 	buf.Pointer = NULL;
136 	buf.Length = ACPI_ALLOCATE_BUFFER;
137 	if (AcpiEvaluateObject(ah, "_UPC", NULL, &buf) == AE_OK) {
138 		UINT64 porttypenum, conn;
139 		const char *connectable;
140 		const char *typelist[] = {"TypeA", "MiniAB", "Express",
141 			"USB3-A", "USB3-B", "USB-MicroB",
142 			"USB3-MicroAB", "USB3-PowerB",
143 			"TypeC-USB2", "TypeC-Switch",
144 		"TypeC-nonSwitch"};
145 		const char *porttype;
146 		const int last = sizeof(typelist) / sizeof(typelist[0]);
147 		ACPI_OBJECT *obj = buf.Pointer;
148 
149 		acpi_PkgInt(obj, 0, &conn);
150 		acpi_PkgInt(obj, 1, &porttypenum);
151 		connectable = conn ? "" : "non";
152 		if (porttypenum == 0xff)
153 			porttype = "Proprietary";
154 		else if (porttypenum < last) {
155 			porttype = typelist[porttypenum];
156 		} else {
157 			porttype = "Unknown";
158 		}
159 		if (usb_debug)
160 			device_printf(dev, "Port %u %sconnectable %s\n",
161 			    port, connectable, porttype);
162 	}
163 	AcpiOsFree(buf.Pointer);
164 
165 	return 0;
166 }
167 
168 static int
169 acpi_uhub_parse_pld(device_t dev, unsigned int port, ACPI_HANDLE ah)
170 {
171 	ACPI_BUFFER buf;
172 
173 	buf.Pointer = NULL;
174 	buf.Length = ACPI_ALLOCATE_BUFFER;
175 	if (AcpiEvaluateObject(ah, "_PLD", NULL, &buf) == AE_OK) {
176 		ACPI_OBJECT *obj;
177 		unsigned char *resbuf;
178 		int len;
179 
180 		obj = buf.Pointer;
181 
182 		if (obj->Type == ACPI_TYPE_PACKAGE
183 		    && obj->Package.Elements[0].Type == ACPI_TYPE_BUFFER) {
184 			ACPI_OBJECT *obj1;
185 
186 			obj1 = &obj->Package.Elements[0];
187 			len = obj1->Buffer.Length;
188 			resbuf = obj1->Buffer.Pointer;
189 		} else if (obj->Type == ACPI_TYPE_BUFFER) {
190 			len = obj->Buffer.Length;
191 			resbuf = obj->Buffer.Pointer;
192 		} else {
193 			goto skip;
194 		}
195 		if (usb_debug) {
196 			device_printf(dev, "Revision:%d\n",
197 			    resbuf[0] & 0x7f);
198 			if ((resbuf[0] & 0x80) == 0) {
199 				device_printf(dev,
200 				    "Color:#%02x%02x%02x\n",
201 				    resbuf[1], resbuf[2],
202 				    resbuf[3]);
203 			}
204 			device_printf(dev, "Width %d mm Height %d mm\n",
205 			    resbuf[4] | (resbuf[5] << 8),
206 			    resbuf[6] | (resbuf[7] << 8));
207 			if (resbuf[8] & 1) {
208 				device_printf(dev, "Visible\n");
209 			}
210 			if (resbuf[8] & 2) {
211 				device_printf(dev, "Dock\n");
212 			}
213 			if (resbuf[8] & 4) {
214 				device_printf(dev, "Lid\n");
215 			}
216 			device_printf(dev, "PanelPosition: %d\n",
217 			    (resbuf[8] >> 3) & 7);
218 			device_printf(dev, "VertPosition: %d\n",
219 			    (resbuf[8] >> 6) & 3);
220 			device_printf(dev, "HorizPosition: %d\n",
221 			    (resbuf[9]) & 3);
222 			device_printf(dev, "Shape: %d\n",
223 			    (resbuf[9] >> 2) & 0xf);
224 			device_printf(dev, "80: %02x, %02x, %02x\n",
225 			    resbuf[9], resbuf[10], resbuf[11]);
226 			device_printf(dev, "96: %02x, %02x, %02x, %02x\n",
227 			    resbuf[12], resbuf[13],
228 			    resbuf[14], resbuf[15]);
229 
230 			if ((resbuf[0] & 0x7f) >= 2) {
231 				device_printf(dev, "VOFF%d mm HOFF %dmm",
232 				    resbuf[16] | (resbuf[17] << 8),
233 				    resbuf[18] | (resbuf[19] << 8));
234 			}
235 		}
236 	skip:
237 		AcpiOsFree(buf.Pointer);
238 
239 	}
240 
241 
242 	return 0;
243 }
244 
245 ACPI_STATUS
246 acpi_uhub_find_rh(device_t dev, ACPI_HANDLE * ah)
247 {
248 	device_t grand;
249 	ACPI_HANDLE gah;
250 
251 	*ah = NULL;
252 	grand = device_get_parent(device_get_parent(dev));
253 	if ((gah = acpi_get_handle(grand)) == NULL) {
254 		return AE_ERROR;
255 	}
256 	return AcpiWalkNamespace(ACPI_TYPE_DEVICE, gah, 1,
257 	    acpi_uhub_find_rh_cb, NULL, dev, ah);
258 }
259 
260 ACPI_STATUS
261 acpi_usb_hub_port_probe_cb(ACPI_HANDLE ah, UINT32 lv, void *ctx, void **rv)
262 {
263 	ACPI_DEVICE_INFO *devinfo;
264 	device_t dev = ctx;
265 	struct acpi_uhub_softc *sc = device_get_softc(dev);
266 
267 	if (usb_debug)
268 		device_printf(dev, "%s\n", acpi_name(ah));
269 
270 	AcpiGetObjectInfo(ah, &devinfo);
271 	if ((devinfo->Valid & ACPI_VALID_ADR) &&
272 	    (devinfo->Address > 0) &&
273 	    (devinfo->Address <= (uint64_t)sc->nports)) {
274 		sc->porthandle[devinfo->Address - 1] = ah;
275 		acpi_uhub_parse_upc(dev, devinfo->Address, ah);
276 		acpi_uhub_parse_pld(dev, devinfo->Address, ah);
277 	} else {
278 		device_printf(dev, "Skiping invalid devobj %s\n",
279 		    acpi_name(ah));
280 	}
281 	AcpiOsFree(devinfo);
282 	return AE_OK;
283 }
284 
285 ACPI_STATUS
286 acpi_usb_hub_port_probe(device_t dev, ACPI_HANDLE ah)
287 {
288 	return AcpiWalkNamespace(ACPI_TYPE_DEVICE,
289 	    ah, 1,
290 	    acpi_usb_hub_port_probe_cb,
291 	    NULL, dev, NULL);
292 }
293 int
294 acpi_uhub_root_probe(device_t dev)
295 {
296 	ACPI_HANDLE ah;
297 	ACPI_STATUS status;
298 
299 	if(acpi_disabled("usb")) {
300 		return ENXIO;
301 	}
302 	status = acpi_uhub_find_rh(dev, &ah);
303 	if (ACPI_SUCCESS(status)
304 	    && ah != NULL
305 	    && (uhub_probe(dev) <= 0)) {
306 		/* success prior than non - acpi hub */
307 		return (BUS_PROBE_DEFAULT + 1);
308 	}
309 	return ENXIO;
310 }
311 
312 int
313 acpi_uhub_probe(device_t dev)
314 {
315 	ACPI_HANDLE ah = acpi_get_handle(dev);
316 
317 	if (!acpi_disabled("usb") && ah && (uhub_probe(dev) <= 0)) {
318 		/*success prior than non - acpi hub*/
319 		    return (BUS_PROBE_DEFAULT + 1);
320 	}
321 	return (ENXIO);
322 }
323 int
324 acpi_uhub_root_attach(device_t dev)
325 {
326 	ACPI_HANDLE devhandle;
327 	struct usb_hub *uh;
328 	struct acpi_uhub_softc *sc = device_get_softc(dev);
329 	int ret;
330 
331 	if ((ret = uhub_attach(dev)) != 0) {
332 		return (ret);
333 	}
334 	uh = sc->usc.sc_udev->hub;
335 
336 	if (ACPI_FAILURE(acpi_uhub_find_rh(dev, &devhandle)) ||
337 	    (devhandle == NULL)) {
338 		return ENXIO;
339 	}
340 
341 	sc->nports = uh->nports;
342 	sc->porthandle = malloc(sizeof(ACPI_HANDLE) * uh->nports,
343 	    M_USBDEV, M_WAITOK | M_ZERO);
344 	acpi_usb_hub_port_probe(dev, devhandle);
345 
346 	return 0;
347 }
348 
349 int
350 acpi_uhub_attach(device_t dev)
351 {
352 	struct usb_hub *uh;
353 	struct acpi_uhub_softc *sc = device_get_softc(dev);
354 	ACPI_HANDLE devhandle;
355 	int ret;
356 
357 	if ((ret = uhub_attach(dev)) != 0) {
358 		return (ret);
359 	}
360 	uh = sc->usc.sc_udev->hub;
361 	devhandle = acpi_get_handle(dev);
362 
363 	if (devhandle == NULL) {
364 		return ENXIO;
365 	}
366 
367 	sc->nports = uh->nports;
368 	sc->porthandle = malloc(sizeof(ACPI_HANDLE) * uh->nports,
369 	    M_USBDEV, M_WAITOK | M_ZERO);
370 	acpi_usb_hub_port_probe(dev, acpi_get_handle(dev));
371 	return 0;
372 }
373 
374 int
375 acpi_uhub_read_ivar(device_t dev, device_t child, int idx,
376     uintptr_t *res)
377 {
378 	struct hub_result hres;
379 	struct acpi_uhub_softc *sc = device_get_softc(dev);
380 	ACPI_HANDLE ah;
381 
382 	mtx_lock(&Giant);
383 	uhub_find_iface_index(sc->usc.sc_udev->hub, child, &hres);
384 	mtx_unlock(&Giant);
385 	if ((idx == ACPI_IVAR_HANDLE) &&
386 	    (hres.portno > 0) &&
387 	    (hres.portno <= sc->nports) &&
388 	    (ah = sc->porthandle[hres.portno - 1])) {
389 		*res = (uintptr_t)ah;
390 		return (0);
391 	}
392 	return (ENXIO);
393 }
394 static int
395 acpi_uhub_child_location_string(device_t parent, device_t child,
396     char *buf, size_t buflen)
397 {
398 
399 	ACPI_HANDLE ah;
400 
401 	uhub_child_location_string(parent, child, buf, buflen);
402 	ah = acpi_get_handle(child);
403 	if (ah) {
404 		strlcat(buf, " handle=", buflen);
405 		strlcat(buf, acpi_name(ah), buflen);
406 	}
407 	return (0);
408 }
409 
410 int
411 acpi_uhub_detach(device_t dev)
412 {
413 	struct acpi_uhub_softc *sc = device_get_softc(dev);
414 
415 	free(sc->porthandle, M_USBDEV);
416 	return uhub_detach(dev);
417 }
418 
419 static device_method_t acpi_uhub_methods[] = {
420 	DEVMETHOD(device_probe, acpi_uhub_probe),
421 	DEVMETHOD(device_attach, acpi_uhub_attach),
422 	DEVMETHOD(device_detach, acpi_uhub_detach),
423 	DEVMETHOD(bus_child_location_str, acpi_uhub_child_location_string),
424 	DEVMETHOD(bus_read_ivar, acpi_uhub_read_ivar),
425 	DEVMETHOD_END
426 
427 };
428 
429 static device_method_t acpi_uhub_root_methods[] = {
430 	DEVMETHOD(device_probe, acpi_uhub_root_probe),
431 	DEVMETHOD(device_attach, acpi_uhub_root_attach),
432 	DEVMETHOD(device_detach, acpi_uhub_detach),
433 	DEVMETHOD(bus_read_ivar, acpi_uhub_read_ivar),
434 	DEVMETHOD(bus_child_location_str, acpi_uhub_child_location_string),
435 	DEVMETHOD_END
436 };
437 
438 static devclass_t uhub_devclass;
439 extern driver_t uhub_driver;
440 static kobj_class_t uhub_baseclasses[] = {&uhub_driver, NULL};
441 static driver_t acpi_uhub_driver = {
442 	.name = "uhub",
443 	.methods = acpi_uhub_methods,
444 	.size = sizeof(struct acpi_uhub_softc),
445 	.baseclasses = uhub_baseclasses,
446 };
447 static driver_t acpi_uhub_root_driver = {
448 	.name = "uhub",
449 	.methods = acpi_uhub_root_methods,
450 	.size = sizeof(struct acpi_uhub_softc),
451 	.baseclasses = uhub_baseclasses,
452 };
453 
454 DRIVER_MODULE(acpi_uhub, uhub, acpi_uhub_driver, uhub_devclass, 0, 0);
455 MODULE_DEPEND(acpi_uhub, acpi, 1, 1, 1);
456 DRIVER_MODULE(acpi_uhub, usbus, acpi_uhub_root_driver, uhub_devclass, 0, 0);
457