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