xref: /haiku/src/add-ons/kernel/bus_managers/usb/Hub.cpp (revision 3c6e2dd68577c34d93e17f19711f6245bf6d0915)
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_p.h"
12 
13 #include <stdio.h>
14 
15 #include <algorithm>
16 
17 
18 Hub::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 %ld\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 
101 Hub::~Hub()
102 {
103 	delete fInterruptPipe;
104 }
105 
106 
107 status_t
108 Hub::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 
126 status_t
127 Hub::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, 4, (void *)&fPortStatus[index],
133 		4, &actualLength);
134 
135 	if (result < B_OK || actualLength < 4) {
136 		TRACE_ERROR("error updating port status\n");
137 		return B_ERROR;
138 	}
139 
140 	return B_OK;
141 }
142 
143 
144 status_t
145 Hub::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 
184 status_t
185 Hub::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 
194 void
195 Hub::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 %ld: status: 0x%04x; change: 0x%04x\n", i,
205 				fPortStatus[i].status, fPortStatus[i].change);
206 			TRACE("device at port %ld: %p (%ld)\n", i, fChildren[i],
207 				fChildren[i] != NULL ? fChildren[i]->USBID() : 0);
208 		}
209 #endif
210 
211 		if (fPortStatus[i].change & PORT_STATUS_CONNECTION) {
212 			// clear status change
213 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
214 				USB_REQUEST_CLEAR_FEATURE, C_PORT_CONNECTION, i + 1,
215 				0, NULL, 0, NULL);
216 
217 			if (fPortStatus[i].status & PORT_STATUS_CONNECTION) {
218 				// new device attached!
219 				TRACE_ALWAYS("port %ld: new device connected\n", i);
220 
221 				// wait some time for the device to power up
222 				snooze(USB_DELAY_DEVICE_POWER_UP);
223 
224 				// reset the port, this will also enable it
225 				result = ResetPort(i);
226 				if (result < B_OK) {
227 					TRACE_ERROR("resetting port %ld failed\n", i);
228 					continue;
229 				}
230 
231 				result = UpdatePortStatus(i);
232 				if (result < B_OK)
233 					continue;
234 
235 				if ((fPortStatus[i].status & PORT_STATUS_CONNECTION) == 0) {
236 					// device has vanished after reset, ignore
237 					TRACE("device disappeared on reset\n");
238 					continue;
239 				}
240 
241 				if (fChildren[i] != NULL) {
242 					TRACE_ERROR("new device on a port that is already in use\n");
243 					fChildren[i]->Changed(changeList, false);
244 					fChildren[i] = NULL;
245 				}
246 
247 				usb_speed speed = USB_SPEED_FULLSPEED;
248 				if (fPortStatus[i].status & PORT_STATUS_LOW_SPEED)
249 					speed = USB_SPEED_LOWSPEED;
250 				if (fPortStatus[i].status & PORT_STATUS_HIGH_SPEED)
251 					speed = USB_SPEED_HIGHSPEED;
252 
253 				// either let the device inherit our addresses (if we are
254 				// already potentially using a transaction translator) or set
255 				// ourselfs as the hub when we might become the transaction
256 				// translator for the device.
257 				int8 hubAddress = HubAddress();
258 				uint8 hubPort = HubPort();
259 				if (Speed() == USB_SPEED_HIGHSPEED) {
260 					hubAddress = DeviceAddress();
261 					hubPort = i + 1;
262 				}
263 
264 				Device *newDevice = GetBusManager()->AllocateDevice(this,
265 					hubAddress, hubPort, speed);
266 
267 				if (newDevice) {
268 					newDevice->Changed(changeList, true);
269 					fChildren[i] = newDevice;
270 				} else {
271 					// the device failed to setup correctly, disable the port
272 					// so that the device doesn't get in the way of future
273 					// addressing.
274 					DisablePort(i);
275 				}
276 			} else {
277 				// Device removed...
278 				TRACE_ALWAYS("port %ld: device removed\n", i);
279 				if (fChildren[i] != NULL) {
280 					TRACE("removing device %p\n", fChildren[i]);
281 					fChildren[i]->Changed(changeList, false);
282 					fChildren[i] = NULL;
283 				}
284 			}
285 		}
286 
287 		// other port changes we do not really handle, report and clear them
288 		if (fPortStatus[i].change & PORT_STATUS_ENABLE) {
289 			TRACE_ALWAYS("port %ld %sabled\n", i, (fPortStatus[i].status & PORT_STATUS_ENABLE) ? "en" : "dis");
290 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
291 				USB_REQUEST_CLEAR_FEATURE, C_PORT_ENABLE, i + 1,
292 				0, NULL, 0, NULL);
293 		}
294 
295 		if (fPortStatus[i].change & PORT_STATUS_SUSPEND) {
296 			TRACE_ALWAYS("port %ld is %ssuspended\n", i, (fPortStatus[i].status & PORT_STATUS_SUSPEND) ? "" : "not ");
297 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
298 				USB_REQUEST_CLEAR_FEATURE, C_PORT_SUSPEND, i + 1,
299 				0, NULL, 0, NULL);
300 		}
301 
302 		if (fPortStatus[i].change & PORT_STATUS_OVER_CURRENT) {
303 			TRACE_ALWAYS("port %ld is %sin an over current state\n", i, (fPortStatus[i].status & PORT_STATUS_OVER_CURRENT) ? "" : "not ");
304 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
305 				USB_REQUEST_CLEAR_FEATURE, C_PORT_OVER_CURRENT, i + 1,
306 				0, NULL, 0, NULL);
307 		}
308 
309 		if (fPortStatus[i].change & PORT_STATUS_RESET) {
310 			TRACE_ALWAYS("port %ld was reset\n", i);
311 			DefaultPipe()->SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT,
312 				USB_REQUEST_CLEAR_FEATURE, C_PORT_RESET, i + 1,
313 				0, NULL, 0, NULL);
314 		}
315 	}
316 
317 	// explore down the tree if we have hubs connected
318 	for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
319 		if (!fChildren[i] || (fChildren[i]->Type() & USB_OBJECT_HUB) == 0)
320 			continue;
321 
322 		((Hub *)fChildren[i])->Explore(changeList);
323 	}
324 }
325 
326 
327 void
328 Hub::InterruptCallback(void *cookie, status_t status, void *data,
329 	size_t actualLength)
330 {
331 	TRACE_STATIC((Hub *)cookie, "interrupt callback!\n");
332 }
333 
334 
335 status_t
336 Hub::GetDescriptor(uint8 descriptorType, uint8 index, uint16 languageID,
337 	void *data, size_t dataLength, size_t *actualLength)
338 {
339 	return DefaultPipe()->SendRequest(
340 		USB_REQTYPE_DEVICE_IN | USB_REQTYPE_CLASS,			// type
341 		USB_REQUEST_GET_DESCRIPTOR,							// request
342 		(descriptorType << 8) | index,						// value
343 		languageID,											// index
344 		dataLength,											// length
345 		data,												// buffer
346 		dataLength,											// buffer length
347 		actualLength);										// actual length
348 }
349 
350 
351 status_t
352 Hub::ReportDevice(usb_support_descriptor *supportDescriptors,
353 	uint32 supportDescriptorCount, const usb_notify_hooks *hooks,
354 	usb_driver_cookie **cookies, bool added, bool recursive)
355 {
356 	status_t result = B_UNSUPPORTED;
357 
358 	if (added) {
359 		// Report hub before children when adding devices
360 		TRACE("reporting hub before children\n");
361 		result = Device::ReportDevice(supportDescriptors,
362 			supportDescriptorCount, hooks, cookies, added, recursive);
363 	}
364 
365 	for (int32 i = 0; recursive && i < fHubDescriptor.num_ports; i++) {
366 		if (!fChildren[i])
367 			continue;
368 
369 		if (fChildren[i]->ReportDevice(supportDescriptors,
370 				supportDescriptorCount, hooks, cookies, added, true) == B_OK)
371 			result = B_OK;
372 	}
373 
374 	if (!added) {
375 		// Report hub after children when removing devices
376 		TRACE("reporting hub after children\n");
377 		if (Device::ReportDevice(supportDescriptors, supportDescriptorCount,
378 				hooks, cookies, added, recursive) == B_OK)
379 			result = B_OK;
380 	}
381 
382 	return result;
383 }
384 
385 
386 status_t
387 Hub::BuildDeviceName(char *string, uint32 *index, size_t bufferSize,
388 	Device *device)
389 {
390 	status_t result = Device::BuildDeviceName(string, index, bufferSize, device);
391 	if (result < B_OK) {
392 		// recursion to parent failed, we're at the root(hub)
393 		if (*index < bufferSize) {
394 			int32 managerIndex = GetStack()->IndexOfBusManager(GetBusManager());
395 			size_t totalBytes = snprintf(string + *index, bufferSize - *index,
396 				"%ld", managerIndex);
397 			*index += std::min(totalBytes, bufferSize - *index - 1);
398 		}
399 	}
400 
401 	if (!device) {
402 		// no device was specified - report the hub
403 		if (*index < bufferSize) {
404 			size_t totalBytes = snprintf(string + *index, bufferSize - *index,
405 				"/hub");
406 			*index += std::min(totalBytes, bufferSize - *index - 1);
407 		}
408 	} else {
409 		// find out where the requested device sitts
410 		for (int32 i = 0; i < fHubDescriptor.num_ports; i++) {
411 			if (fChildren[i] == device) {
412 				if (*index < bufferSize) {
413 					size_t totalBytes = snprintf(string + *index,
414 						bufferSize - *index, "/%ld", i);
415 					*index += std::min(totalBytes, bufferSize - *index - 1);
416 				}
417 				break;
418 			}
419 		}
420 	}
421 
422 	return B_OK;
423 }
424