1/*
2 * Copyright 2003-2006, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Michael Lotz <mmlr@mlotz.ch>
7 *		Niels S. Reedijk
8 */
9
10
11#include "usb_private.h"
12
13#include <stdio.h>
14
15#include <algorithm>
16
17
18Hub::Hub(Object *parent, int8 hubAddress, uint8 hubPort,
19	usb_device_descriptor &desc, int8 deviceAddress, usb_speed speed,
20	bool isRootHub, void *controllerCookie)
21	:	Device(parent, hubAddress, hubPort, desc, deviceAddress, speed,
22			isRootHub, controllerCookie),
23		fInterruptPipe(NULL)
24{
25	TRACE("creating hub\n");
26
27	memset(&fHubDescriptor, 0, sizeof(fHubDescriptor));
28	for (int32 i = 0; i < USB_MAX_PORT_COUNT; i++)
29		fChildren[i] = NULL;
30
31	if (!fInitOK) {
32		TRACE_ERROR("device failed to initialize\n");
33		return;
34	}
35
36	// Set to false again for the hub init.
37	fInitOK = false;
38
39	if (fDeviceDescriptor.device_class != 9) {
40		TRACE_ERROR("wrong class! bailing out\n");
41		return;
42	}
43
44	TRACE("getting hub descriptor...\n");
45	size_t actualLength;
46	status_t status = GetDescriptor(USB_DESCRIPTOR_HUB, 0, 0,
47		(void *)&fHubDescriptor, sizeof(usb_hub_descriptor), &actualLength);
48
49	// we need at least 8 bytes
50	if (status < B_OK || actualLength < 8) {
51		TRACE_ERROR("error getting hub descriptor\n");
52		return;
53	}
54
55	TRACE("hub descriptor (%ld bytes):\n", actualLength);
56	TRACE("\tlength:..............%d\n", fHubDescriptor.length);
57	TRACE("\tdescriptor_type:.....0x%02x\n", fHubDescriptor.descriptor_type);
58	TRACE("\tnum_ports:...........%d\n", fHubDescriptor.num_ports);
59	TRACE("\tcharacteristics:.....0x%04x\n", fHubDescriptor.characteristics);
60	TRACE("\tpower_on_to_power_g:.%d\n", fHubDescriptor.power_on_to_power_good);
61	TRACE("\tdevice_removeable:...0x%02x\n", fHubDescriptor.device_removeable);
62	TRACE("\tpower_control_mask:..0x%02x\n", fHubDescriptor.power_control_mask);
63
64	if (fHubDescriptor.num_ports > USB_MAX_PORT_COUNT) {
65		TRACE_ALWAYS("hub supports more ports than we do (%d vs. %d)\n",
66			fHubDescriptor.num_ports, USB_MAX_PORT_COUNT);
67		fHubDescriptor.num_ports = USB_MAX_PORT_COUNT;
68	}
69
70	usb_interface_list *list = Configuration()->interface;
71	Object *object = GetStack()->GetObject(list->active->endpoint[0].handle);
72	if (object && (object->Type() & USB_OBJECT_INTERRUPT_PIPE) != 0) {
73		fInterruptPipe = (InterruptPipe *)object;
74		fInterruptPipe->QueueInterrupt(fInterruptStatus,
75			sizeof(fInterruptStatus), InterruptCallback, this);
76	} else {
77		TRACE_ALWAYS("no interrupt pipe found\n");
78	}
79	object->SetBusy(false);
80
81	// Wait some time before powering up the ports
82	if (!isRootHub)
83		snooze(USB_DELAY_HUB_POWER_UP);
84
85	// Enable port power on all ports
86	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
87		status = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
88			USB_REQUEST_SET_FEATURE, PORT_POWER, i + 1, 0, NULL, 0, NULL);
89
90		if (status < B_OK)
91			TRACE_ERROR("power up failed on port %" B_PRId32 "\n", i);
92	}
93
94	// Wait for power to stabilize
95	snooze(fHubDescriptor.power_on_to_power_good * 2000);
96
97	fInitOK = true;
98	TRACE("initialised ok\n");
99}
100
101
102Hub::~Hub()
103{
104	delete fInterruptPipe;
105}
106
107
108status_t
109Hub::Changed(change_item **changeList, bool added)
110{
111	status_t result = Device::Changed(changeList, added);
112	if (added || result < B_OK)
113		return result;
114
115	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
116		if (fChildren[i] == NULL)
117			continue;
118
119		fChildren[i]->Changed(changeList, false);
120		fChildren[i] = NULL;
121	}
122
123	return B_OK;
124}
125
126
127status_t
128Hub::UpdatePortStatus(uint8 index)
129{
130	// get the current port status
131	size_t actualLength = 0;
132	status_t result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_IN,
133		USB_REQUEST_GET_STATUS, 0, index + 1, sizeof(usb_port_status),
134		(void *)&fPortStatus[index], sizeof(usb_port_status), &actualLength);
135
136	if (result < B_OK || actualLength < sizeof(usb_port_status)) {
137		TRACE_ERROR("error updating port status\n");
138		return B_ERROR;
139	}
140
141	return B_OK;
142}
143
144
145status_t
146Hub::ResetPort(uint8 index)
147{
148	status_t result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
149		USB_REQUEST_SET_FEATURE, PORT_RESET, index + 1, 0, NULL, 0, NULL);
150
151	if (result < B_OK)
152		return result;
153
154	for (int32 i = 0; i < 10; i++) {
155		snooze(USB_DELAY_PORT_RESET);
156
157		result = UpdatePortStatus(index);
158		if (result < B_OK)
159			return result;
160
161		if ((fPortStatus[index].change & PORT_STATUS_RESET) != 0
162			|| (fPortStatus[index].status & PORT_STATUS_RESET) == 0) {
163			// reset is done
164			break;
165		}
166	}
167
168	if ((fPortStatus[index].change & PORT_STATUS_RESET) == 0
169			&& (fPortStatus[index].status & PORT_STATUS_RESET) != 0) {
170		TRACE_ERROR("port %d won't reset (%#x, %#x)\n", index,
171			fPortStatus[index].change, fPortStatus[index].status);
172		return B_ERROR;
173	}
174
175	// clear the reset change
176	result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
177		USB_REQUEST_CLEAR_FEATURE, C_PORT_RESET, index + 1, 0, NULL, 0, NULL);
178	if (result < B_OK)
179		return result;
180
181	// wait for reset recovery
182	snooze(USB_DELAY_PORT_RESET_RECOVERY);
183	TRACE("port %d was reset successfully\n", index);
184	return B_OK;
185}
186
187
188status_t
189Hub::DisablePort(uint8 index)
190{
191	return DefaultPipe()->SendRequest(USB_REQTYPE_CLASS
192		| USB_REQTYPE_OTHER_OUT, USB_REQUEST_CLEAR_FEATURE, PORT_ENABLE,
193		index + 1, 0, NULL, 0, NULL);
194}
195
196
197
198void
199Hub::Explore(change_item **changeList)
200{
201	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
202		status_t result = UpdatePortStatus(i);
203		if (result < B_OK)
204			continue;
205
206#ifdef TRACE_USB
207		if (fPortStatus[i].change) {
208			TRACE("port %" B_PRId32 ": status: 0x%04x; change: 0x%04x\n", i,
209				fPortStatus[i].status, fPortStatus[i].change);
210			TRACE("device at port %" B_PRId32 ": %p (%" B_PRId32 ")\n", i,
211				fChildren[i], fChildren[i] != NULL
212					? fChildren[i]->USBID() : 0);
213		}
214#endif
215
216		if ((fPortStatus[i].change & PORT_STATUS_CONNECTION)
217				|| ((fPortStatus[i].status & PORT_STATUS_CONNECTION)
218					&& fChildren[i] == NULL)) {
219			// clear status change
220			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
221				USB_REQUEST_CLEAR_FEATURE, C_PORT_CONNECTION, i + 1,
222				0, NULL, 0, NULL);
223
224			if (fPortStatus[i].status & PORT_STATUS_CONNECTION) {
225				// new device attached!
226				TRACE_ALWAYS("port %" B_PRId32 ": new device connected\n", i);
227
228				int32 retry = 2;
229				while (retry--) {
230					// wait for stable device power
231					result = _DebouncePort(i);
232					if (result != B_OK) {
233						TRACE_ERROR("debouncing port %" B_PRId32
234							" failed: %s\n", i, strerror(result));
235						break;
236					}
237
238					// reset the port, this will also enable it
239					result = ResetPort(i);
240					if (result < B_OK) {
241						TRACE_ERROR("resetting port %" B_PRId32 " failed\n",
242							i);
243						break;
244					}
245
246					result = UpdatePortStatus(i);
247					if (result < B_OK)
248						break;
249
250					if ((fPortStatus[i].status & PORT_STATUS_CONNECTION) == 0) {
251						// device has vanished after reset, ignore
252						TRACE("device disappeared on reset\n");
253						break;
254					}
255
256					if (fChildren[i] != NULL) {
257						TRACE_ERROR("new device on a port that is already in "
258							"use\n");
259						fChildren[i]->Changed(changeList, false);
260						fChildren[i] = NULL;
261					}
262
263					// Determine the device speed.
264					usb_speed speed;
265
266					// PORT_STATUS_LOW_SPEED and PORT_STATUS_SS_POWER are the
267					// same, but PORT_STATUS_POWER will not be set for SS
268					// devices, hence this somewhat convoluted logic.
269					if ((fPortStatus[i].status & PORT_STATUS_POWER) != 0) {
270						if ((fPortStatus[i].status & PORT_STATUS_HIGH_SPEED) != 0)
271							speed = USB_SPEED_HIGHSPEED;
272						else if ((fPortStatus[i].status & PORT_STATUS_LOW_SPEED) != 0)
273							speed = USB_SPEED_LOWSPEED;
274						else
275							speed = USB_SPEED_FULLSPEED;
276					} else {
277						// This must be a SuperSpeed device, which will
278						// simply inherit our speed.
279						speed = Speed();
280					}
281					if (speed > Speed())
282						speed = Speed();
283
284					// either let the device inherit our addresses (if we are
285					// already potentially using a transaction translator) or
286					// set ourselves as the hub when we might become the
287					// transaction translator for the device.
288					int8 hubAddress = HubAddress();
289					uint8 hubPort = HubPort();
290					if (Speed() == USB_SPEED_HIGHSPEED || Speed() == USB_SPEED_SUPERSPEED) {
291						hubAddress = DeviceAddress();
292						hubPort = i + 1;
293					}
294
295					Device *newDevice = GetBusManager()->AllocateDevice(this,
296						hubAddress, hubPort, speed);
297
298					if (newDevice) {
299						newDevice->Changed(changeList, true);
300						fChildren[i] = newDevice;
301						break;
302					} else {
303						// the device failed to setup correctly, disable the
304						// port so that the device doesn't get in the way of
305						// future addressing.
306						DisablePort(i);
307					}
308				}
309			} else {
310				// Device removed...
311				TRACE_ALWAYS("port %" B_PRId32 ": device removed\n", i);
312				if (fChildren[i] != NULL) {
313					TRACE("removing device %p\n", fChildren[i]);
314					fChildren[i]->Changed(changeList, false);
315					fChildren[i] = NULL;
316				}
317			}
318		}
319
320		// other port changes we do not really handle, report and clear them
321		if (fPortStatus[i].change & PORT_STATUS_ENABLE) {
322			TRACE_ALWAYS("port %" B_PRId32 " %sabled\n", i,
323				(fPortStatus[i].status & PORT_STATUS_ENABLE) ? "en" : "dis");
324			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
325				USB_REQUEST_CLEAR_FEATURE, C_PORT_ENABLE, i + 1,
326				0, NULL, 0, NULL);
327		}
328
329		if (fPortStatus[i].change & PORT_STATUS_SUSPEND) {
330			TRACE_ALWAYS("port %" B_PRId32 " is %ssuspended\n", i,
331				(fPortStatus[i].status & PORT_STATUS_SUSPEND) ? "" : "not ");
332			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
333				USB_REQUEST_CLEAR_FEATURE, C_PORT_SUSPEND, i + 1,
334				0, NULL, 0, NULL);
335		}
336
337		if (fPortStatus[i].change & PORT_STATUS_OVER_CURRENT) {
338			TRACE_ALWAYS("port %" B_PRId32 " is %sin an over current state\n",
339				i, (fPortStatus[i].status & PORT_STATUS_OVER_CURRENT) ? "" : "not ");
340			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
341				USB_REQUEST_CLEAR_FEATURE, C_PORT_OVER_CURRENT, i + 1,
342				0, NULL, 0, NULL);
343		}
344
345		if (fPortStatus[i].change & PORT_STATUS_RESET) {
346			TRACE_ALWAYS("port %" B_PRId32 " was reset\n", i);
347			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
348				USB_REQUEST_CLEAR_FEATURE, C_PORT_RESET, i + 1,
349				0, NULL, 0, NULL);
350		}
351
352		if (fPortStatus[i].change & PORT_CHANGE_LINK_STATE) {
353			TRACE_ALWAYS("port %" B_PRId32 " link state changed\n", i);
354			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
355				USB_REQUEST_CLEAR_FEATURE, C_PORT_LINK_STATE, i + 1,
356				0, NULL, 0, NULL);
357		}
358
359		if (fPortStatus[i].change & PORT_CHANGE_BH_PORT_RESET) {
360			TRACE_ALWAYS("port %" B_PRId32 " was warm reset\n", i);
361			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
362				USB_REQUEST_CLEAR_FEATURE, C_PORT_BH_PORT_RESET, i + 1,
363				0, NULL, 0, NULL);
364		}
365
366	}
367
368	// explore down the tree if we have hubs connected
369	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
370		if (!fChildren[i] || (fChildren[i]->Type() & USB_OBJECT_HUB) == 0)
371			continue;
372
373		((Hub *)fChildren[i])->Explore(changeList);
374	}
375}
376
377
378void
379Hub::InterruptCallback(void *cookie, status_t status, void *data,
380	size_t actualLength)
381{
382	TRACE_STATIC((Hub *)cookie, "interrupt callback!\n");
383}
384
385
386status_t
387Hub::GetDescriptor(uint8 descriptorType, uint8 index, uint16 languageID,
388	void *data, size_t dataLength, size_t *actualLength)
389{
390	return DefaultPipe()->SendRequest(
391		USB_REQTYPE_DEVICE_IN | USB_REQTYPE_CLASS,			// type
392		USB_REQUEST_GET_DESCRIPTOR,							// request
393		(descriptorType << 8) | index,						// value
394		languageID,											// index
395		dataLength,											// length
396		data,												// buffer
397		dataLength,											// buffer length
398		actualLength);										// actual length
399}
400
401
402status_t
403Hub::ReportDevice(usb_support_descriptor *supportDescriptors,
404	uint32 supportDescriptorCount, const usb_notify_hooks *hooks,
405	usb_driver_cookie **cookies, bool added, bool recursive)
406{
407	status_t result = B_UNSUPPORTED;
408
409	if (added) {
410		// Report hub before children when adding devices
411		TRACE("reporting hub before children\n");
412		result = Device::ReportDevice(supportDescriptors,
413			supportDescriptorCount, hooks, cookies, added, recursive);
414	}
415
416	for (int32 i = 0; recursive && i < fHubDescriptor.num_ports; i++) {
417		if (!fChildren[i])
418			continue;
419
420		if (fChildren[i]->ReportDevice(supportDescriptors,
421				supportDescriptorCount, hooks, cookies, added, true) == B_OK)
422			result = B_OK;
423	}
424
425	if (!added) {
426		// Report hub after children when removing devices
427		TRACE("reporting hub after children\n");
428		if (Device::ReportDevice(supportDescriptors, supportDescriptorCount,
429				hooks, cookies, added, recursive) == B_OK)
430			result = B_OK;
431	}
432
433	return result;
434}
435
436
437status_t
438Hub::BuildDeviceName(char *string, uint32 *index, size_t bufferSize,
439	Device *device)
440{
441	status_t result = Device::BuildDeviceName(string, index, bufferSize, device);
442	if (result < B_OK) {
443		// recursion to parent failed, we're at the root(hub)
444		if (*index < bufferSize) {
445			int32 managerIndex = GetStack()->IndexOfBusManager(GetBusManager());
446			size_t totalBytes = snprintf(string + *index, bufferSize - *index,
447				"%" B_PRId32, managerIndex);
448			*index += std::min(totalBytes, (size_t)(bufferSize - *index - 1));
449		}
450	}
451
452	if (!device) {
453		// no device was specified - report the hub
454		if (*index < bufferSize) {
455			size_t totalBytes = snprintf(string + *index, bufferSize - *index,
456				"/hub");
457			*index += std::min(totalBytes, (size_t)(bufferSize - *index - 1));
458		}
459	} else {
460		// find out where the requested device sitts
461		for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
462			if (fChildren[i] == device) {
463				if (*index < bufferSize) {
464					size_t totalBytes = snprintf(string + *index,
465						bufferSize - *index, "/%" B_PRId32, i);
466					*index += std::min(totalBytes,
467						(size_t)(bufferSize - *index - 1));
468				}
469				break;
470			}
471		}
472	}
473
474	return B_OK;
475}
476
477
478status_t
479Hub::_DebouncePort(uint8 index)
480{
481	uint32 timeout = 0;
482	uint32 stableTime = 0;
483	while (timeout < USB_DEBOUNCE_TIMEOUT) {
484		snooze(USB_DEBOUNCE_CHECK_INTERVAL);
485		timeout += USB_DEBOUNCE_CHECK_INTERVAL;
486
487		status_t result = UpdatePortStatus(index);
488		if (result != B_OK)
489			return result;
490
491		if ((fPortStatus[index].change & PORT_STATUS_CONNECTION) == 0) {
492			stableTime += USB_DEBOUNCE_CHECK_INTERVAL;
493			if (stableTime >= USB_DEBOUNCE_STABLE_TIME)
494				return B_OK;
495			continue;
496		}
497
498		// clear the connection change and reset stable time
499		result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS
500			| USB_REQTYPE_OTHER_OUT, USB_REQUEST_CLEAR_FEATURE,
501			C_PORT_CONNECTION, index + 1, 0, NULL, 0, NULL);
502		if (result != B_OK)
503			return result;
504
505		TRACE("got connection change during debounce, resetting stable time\n");
506		stableTime = 0;
507	}
508
509	return B_TIMED_OUT;
510}
511