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