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)
21	:	Device(parent, hubAddress, hubPort, desc, deviceAddress, speed,
22			isRootHub),
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
80	// Wait some time before powering up the ports
81	if (!isRootHub)
82		snooze(USB_DELAY_HUB_POWER_UP);
83
84	// Enable port power on all ports
85	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
86		status = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
87			USB_REQUEST_SET_FEATURE, PORT_POWER, i + 1, 0, NULL, 0, NULL);
88
89		if (status < B_OK)
90			TRACE_ERROR("power up failed on port %" B_PRId32 "\n", i);
91	}
92
93	// Wait for power to stabilize
94	snooze(fHubDescriptor.power_on_to_power_good * 2000);
95
96	fInitOK = true;
97	TRACE("initialised ok\n");
98}
99
100
101Hub::~Hub()
102{
103	delete fInterruptPipe;
104}
105
106
107status_t
108Hub::Changed(change_item **changeList, bool added)
109{
110	status_t result = Device::Changed(changeList, added);
111	if (added || result < B_OK)
112		return result;
113
114	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
115		if (fChildren[i] == NULL)
116			continue;
117
118		fChildren[i]->Changed(changeList, false);
119		fChildren[i] = NULL;
120	}
121
122	return B_OK;
123}
124
125
126status_t
127Hub::UpdatePortStatus(uint8 index)
128{
129	// get the current port status
130	size_t actualLength = 0;
131	status_t result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_IN,
132		USB_REQUEST_GET_STATUS, 0, index + 1, sizeof(usb_port_status),
133		(void *)&fPortStatus[index], sizeof(usb_port_status), &actualLength);
134
135	if (result < B_OK || actualLength < sizeof(usb_port_status)) {
136		TRACE_ERROR("error updating port status\n");
137		return B_ERROR;
138	}
139
140	return B_OK;
141}
142
143
144status_t
145Hub::ResetPort(uint8 index)
146{
147	status_t result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
148		USB_REQUEST_SET_FEATURE, PORT_RESET, index + 1, 0, NULL, 0, NULL);
149
150	if (result < B_OK)
151		return result;
152
153	for (int32 i = 0; i < 10; i++) {
154		snooze(USB_DELAY_PORT_RESET);
155
156		result = UpdatePortStatus(index);
157		if (result < B_OK)
158			return result;
159
160		if (fPortStatus[index].change & PORT_STATUS_RESET) {
161			// reset is done
162			break;
163		}
164	}
165
166	if ((fPortStatus[index].change & PORT_STATUS_RESET) == 0) {
167		TRACE_ERROR("port %d won't reset\n", index);
168		return B_ERROR;
169	}
170
171	// clear the reset change
172	result = DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
173		USB_REQUEST_CLEAR_FEATURE, C_PORT_RESET, index + 1, 0, NULL, 0, NULL);
174	if (result < B_OK)
175		return result;
176
177	// wait for reset recovery
178	snooze(USB_DELAY_PORT_RESET_RECOVERY);
179	TRACE("port %d was reset successfully\n", index);
180	return B_OK;
181}
182
183
184status_t
185Hub::DisablePort(uint8 index)
186{
187	return DefaultPipe()->SendRequest(USB_REQTYPE_CLASS
188		| USB_REQTYPE_OTHER_OUT, USB_REQUEST_CLEAR_FEATURE, PORT_ENABLE,
189		index + 1, 0, NULL, 0, NULL);
190}
191
192
193
194void
195Hub::Explore(change_item **changeList)
196{
197	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
198		status_t result = UpdatePortStatus(i);
199		if (result < B_OK)
200			continue;
201
202#ifdef TRACE_USB
203		if (fPortStatus[i].change) {
204			TRACE("port %" B_PRId32 ": status: 0x%04x; change: 0x%04x\n", i,
205				fPortStatus[i].status, fPortStatus[i].change);
206			TRACE("device at port %" B_PRId32 ": %p (%" B_PRId32 ")\n", i,
207				fChildren[i], fChildren[i] != NULL
208					? fChildren[i]->USBID() : 0);
209		}
210#endif
211
212		if (fPortStatus[i].change & PORT_STATUS_CONNECTION) {
213			// clear status change
214			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
215				USB_REQUEST_CLEAR_FEATURE, C_PORT_CONNECTION, i + 1,
216				0, NULL, 0, NULL);
217
218			if (fPortStatus[i].status & PORT_STATUS_CONNECTION) {
219				// new device attached!
220				TRACE_ALWAYS("port %" B_PRId32 ": new device connected\n", i);
221
222				int32 retry = 2;
223				while (retry--) {
224					// wait some time for the device to power up
225					snooze(USB_DELAY_DEVICE_POWER_UP);
226
227					// reset the port, this will also enable it
228					result = ResetPort(i);
229					if (result < B_OK) {
230						TRACE_ERROR("resetting port %" B_PRId32 " failed\n",
231							i);
232						break;
233					}
234
235					result = UpdatePortStatus(i);
236					if (result < B_OK)
237						break;
238
239					if ((fPortStatus[i].status & PORT_STATUS_CONNECTION) == 0) {
240						// device has vanished after reset, ignore
241						TRACE("device disappeared on reset\n");
242						break;
243					}
244
245					if (fChildren[i] != NULL) {
246						TRACE_ERROR("new device on a port that is already in "
247							"use\n");
248						fChildren[i]->Changed(changeList, false);
249						fChildren[i] = NULL;
250					}
251
252					usb_speed speed = USB_SPEED_FULLSPEED;
253					if (fDeviceDescriptor.usb_version == 0x300)
254						speed = USB_SPEED_SUPER;
255					else if (fPortStatus[i].status & PORT_STATUS_LOW_SPEED)
256						speed = USB_SPEED_LOWSPEED;
257					else if (fPortStatus[i].status & PORT_STATUS_HIGH_SPEED)
258						speed = USB_SPEED_HIGHSPEED;
259
260					// either let the device inherit our addresses (if we are
261					// already potentially using a transaction translator) or
262					// set ourselfs as the hub when we might become the
263					// transaction translator for the device.
264					int8 hubAddress = HubAddress();
265					uint8 hubPort = HubPort();
266					if (Speed() == USB_SPEED_HIGHSPEED || fDeviceDescriptor.usb_version == 0x300) {
267						hubAddress = DeviceAddress();
268						hubPort = i + 1;
269					}
270
271					Device *newDevice = GetBusManager()->AllocateDevice(this,
272						hubAddress, hubPort, speed);
273
274					if (newDevice) {
275						newDevice->Changed(changeList, true);
276						fChildren[i] = newDevice;
277						break;
278					} else {
279						// the device failed to setup correctly, disable the
280						// port so that the device doesn't get in the way of
281						// future addressing.
282						DisablePort(i);
283					}
284				}
285			} else {
286				// Device removed...
287				TRACE_ALWAYS("port %" B_PRId32 ": device removed\n", i);
288				if (fChildren[i] != NULL) {
289					TRACE("removing device %p\n", fChildren[i]);
290					fChildren[i]->Changed(changeList, false);
291					fChildren[i] = NULL;
292				}
293			}
294		}
295
296		// other port changes we do not really handle, report and clear them
297		if (fPortStatus[i].change & PORT_STATUS_ENABLE) {
298			TRACE_ALWAYS("port %" B_PRId32 " %sabled\n", i,
299				(fPortStatus[i].status & PORT_STATUS_ENABLE) ? "en" : "dis");
300			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
301				USB_REQUEST_CLEAR_FEATURE, C_PORT_ENABLE, i + 1,
302				0, NULL, 0, NULL);
303		}
304
305		if (fPortStatus[i].change & PORT_STATUS_SUSPEND) {
306			TRACE_ALWAYS("port %" B_PRId32 " is %ssuspended\n", i,
307				(fPortStatus[i].status & PORT_STATUS_SUSPEND) ? "" : "not ");
308			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
309				USB_REQUEST_CLEAR_FEATURE, C_PORT_SUSPEND, i + 1,
310				0, NULL, 0, NULL);
311		}
312
313		if (fPortStatus[i].change & PORT_STATUS_OVER_CURRENT) {
314			TRACE_ALWAYS("port %" B_PRId32 " is %sin an over current state\n",
315				i, (fPortStatus[i].status & PORT_STATUS_OVER_CURRENT) ? "" : "not ");
316			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
317				USB_REQUEST_CLEAR_FEATURE, C_PORT_OVER_CURRENT, i + 1,
318				0, NULL, 0, NULL);
319		}
320
321		if (fPortStatus[i].change & PORT_STATUS_RESET) {
322			TRACE_ALWAYS("port %" B_PRId32 "was reset\n", i);
323			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
324				USB_REQUEST_CLEAR_FEATURE, C_PORT_RESET, i + 1,
325				0, NULL, 0, NULL);
326		}
327	}
328
329	// explore down the tree if we have hubs connected
330	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
331		if (!fChildren[i] || (fChildren[i]->Type() & USB_OBJECT_HUB) == 0)
332			continue;
333
334		((Hub *)fChildren[i])->Explore(changeList);
335	}
336}
337
338
339void
340Hub::InterruptCallback(void *cookie, status_t status, void *data,
341	size_t actualLength)
342{
343	TRACE_STATIC((Hub *)cookie, "interrupt callback!\n");
344}
345
346
347status_t
348Hub::GetDescriptor(uint8 descriptorType, uint8 index, uint16 languageID,
349	void *data, size_t dataLength, size_t *actualLength)
350{
351	return DefaultPipe()->SendRequest(
352		USB_REQTYPE_DEVICE_IN | USB_REQTYPE_CLASS,			// type
353		USB_REQUEST_GET_DESCRIPTOR,							// request
354		(descriptorType << 8) | index,						// value
355		languageID,											// index
356		dataLength,											// length
357		data,												// buffer
358		dataLength,											// buffer length
359		actualLength);										// actual length
360}
361
362
363status_t
364Hub::ReportDevice(usb_support_descriptor *supportDescriptors,
365	uint32 supportDescriptorCount, const usb_notify_hooks *hooks,
366	usb_driver_cookie **cookies, bool added, bool recursive)
367{
368	status_t result = B_UNSUPPORTED;
369
370	if (added) {
371		// Report hub before children when adding devices
372		TRACE("reporting hub before children\n");
373		result = Device::ReportDevice(supportDescriptors,
374			supportDescriptorCount, hooks, cookies, added, recursive);
375	}
376
377	for (int32 i = 0; recursive && i < fHubDescriptor.num_ports; i++) {
378		if (!fChildren[i])
379			continue;
380
381		if (fChildren[i]->ReportDevice(supportDescriptors,
382				supportDescriptorCount, hooks, cookies, added, true) == B_OK)
383			result = B_OK;
384	}
385
386	if (!added) {
387		// Report hub after children when removing devices
388		TRACE("reporting hub after children\n");
389		if (Device::ReportDevice(supportDescriptors, supportDescriptorCount,
390				hooks, cookies, added, recursive) == B_OK)
391			result = B_OK;
392	}
393
394	return result;
395}
396
397
398status_t
399Hub::BuildDeviceName(char *string, uint32 *index, size_t bufferSize,
400	Device *device)
401{
402	status_t result = Device::BuildDeviceName(string, index, bufferSize, device);
403	if (result < B_OK) {
404		// recursion to parent failed, we're at the root(hub)
405		if (*index < bufferSize) {
406			int32 managerIndex = GetStack()->IndexOfBusManager(GetBusManager());
407			size_t totalBytes = snprintf(string + *index, bufferSize - *index,
408				"%" B_PRId32, managerIndex);
409			*index += std::min(totalBytes, bufferSize - *index - 1);
410		}
411	}
412
413	if (!device) {
414		// no device was specified - report the hub
415		if (*index < bufferSize) {
416			size_t totalBytes = snprintf(string + *index, bufferSize - *index,
417				"/hub");
418			*index += std::min(totalBytes, bufferSize - *index - 1);
419		}
420	} else {
421		// find out where the requested device sitts
422		for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
423			if (fChildren[i] == device) {
424				if (*index < bufferSize) {
425					size_t totalBytes = snprintf(string + *index,
426						bufferSize - *index, "/%" B_PRId32, i);
427					*index += std::min(totalBytes, bufferSize - *index - 1);
428				}
429				break;
430			}
431		}
432	}
433
434	return B_OK;
435}
436