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