xref: /haiku/src/add-ons/kernel/bus_managers/usb/Pipe.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
1 /*
2  * Copyright 2004-2020, 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 
13 Pipe::Pipe(Object *parent)
14 	:	Object(parent),
15 		fDataToggle(false),
16 		fControllerCookie(NULL)
17 {
18 	// all other init is to be done in InitCommon()
19 }
20 
21 
22 Pipe::~Pipe()
23 {
24 	PutUSBID();
25 
26 	Pipe::CancelQueuedTransfers(true);
27 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_DESTROYED);
28 }
29 
30 
31 void
32 Pipe::InitCommon(int8 deviceAddress, uint8 endpointAddress, usb_speed speed,
33 	pipeDirection direction, size_t maxPacketSize, uint8 interval,
34 	int8 hubAddress, uint8 hubPort)
35 {
36 	fDeviceAddress = deviceAddress;
37 	fEndpointAddress = endpointAddress;
38 	fSpeed = speed;
39 	fDirection = direction;
40 	fMaxPacketSize = maxPacketSize;
41 	fInterval = interval;
42 	fHubAddress = hubAddress;
43 	fHubPort = hubPort;
44 
45 	fMaxBurst = 0;
46 	fBytesPerInterval = 0;
47 
48 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_CREATED);
49 }
50 
51 
52 void
53 Pipe::InitSuperSpeed(uint8 maxBurst, uint16 bytesPerInterval)
54 {
55 	fMaxBurst = maxBurst;
56 	fBytesPerInterval = bytesPerInterval;
57 }
58 
59 
60 void
61 Pipe::SetHubInfo(int8 address, uint8 port)
62 {
63 	fHubAddress = address;
64 	fHubPort = port;
65 }
66 
67 
68 status_t
69 Pipe::SubmitTransfer(Transfer *transfer)
70 {
71 	if (USBID() == UINT32_MAX)
72 		return B_NO_INIT;
73 
74 	// ToDo: keep track of all submited transfers to be able to cancel them
75 	return GetBusManager()->SubmitTransfer(transfer);
76 }
77 
78 
79 status_t
80 Pipe::CancelQueuedTransfers(bool force)
81 {
82 	return GetBusManager()->CancelQueuedTransfers(this, force);
83 }
84 
85 
86 status_t
87 Pipe::SetFeature(uint16 selector)
88 {
89 	TRACE("set feature %u\n", selector);
90 	return ((Device *)Parent())->DefaultPipe()->SendRequest(
91 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
92 		USB_REQUEST_SET_FEATURE,
93 		selector,
94 		fEndpointAddress | (fDirection == In ? USB_ENDPOINT_ADDR_DIR_IN
95 			: USB_ENDPOINT_ADDR_DIR_OUT),
96 		0,
97 		NULL,
98 		0,
99 		NULL);
100 }
101 
102 
103 status_t
104 Pipe::ClearFeature(uint16 selector)
105 {
106 	// clearing a stalled condition resets the data toggle
107 	if (selector == USB_FEATURE_ENDPOINT_HALT)
108 		SetDataToggle(false);
109 
110 	TRACE("clear feature %u\n", selector);
111 	return ((Device *)Parent())->DefaultPipe()->SendRequest(
112 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
113 		USB_REQUEST_CLEAR_FEATURE,
114 		selector,
115 		fEndpointAddress | (fDirection == In ? USB_ENDPOINT_ADDR_DIR_IN
116 			: USB_ENDPOINT_ADDR_DIR_OUT),
117 		0,
118 		NULL,
119 		0,
120 		NULL);
121 }
122 
123 
124 status_t
125 Pipe::GetStatus(uint16 *status)
126 {
127 	TRACE("get status\n");
128 	return ((Device *)Parent())->DefaultPipe()->SendRequest(
129 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_IN,
130 		USB_REQUEST_GET_STATUS,
131 		0,
132 		fEndpointAddress | (fDirection == In ? USB_ENDPOINT_ADDR_DIR_IN
133 			: USB_ENDPOINT_ADDR_DIR_OUT),
134 		2,
135 		(void *)status,
136 		2,
137 		NULL);
138 }
139 
140 
141 //
142 // #pragma mark -
143 //
144 
145 
146 InterruptPipe::InterruptPipe(Object *parent)
147 	:	Pipe(parent)
148 {
149 }
150 
151 
152 status_t
153 InterruptPipe::QueueInterrupt(void *data, size_t dataLength,
154 	usb_callback_func callback, void *callbackCookie)
155 {
156 	if (dataLength > 0 && data == NULL)
157 		return B_BAD_VALUE;
158 
159 	Transfer *transfer = new(std::nothrow) Transfer(this);
160 	if (!transfer)
161 		return B_NO_MEMORY;
162 
163 	transfer->SetData((uint8 *)data, dataLength);
164 	transfer->SetCallback(callback, callbackCookie);
165 
166 	status_t result = GetBusManager()->SubmitTransfer(transfer);
167 	if (result < B_OK)
168 		delete transfer;
169 	return result;
170 }
171 
172 
173 //
174 // #pragma mark -
175 //
176 
177 
178 BulkPipe::BulkPipe(Object *parent)
179 	:	Pipe(parent)
180 {
181 }
182 
183 
184 void
185 BulkPipe::InitCommon(int8 deviceAddress, uint8 endpointAddress,
186 	usb_speed speed, pipeDirection direction, size_t maxPacketSize,
187 	uint8 interval, int8 hubAddress, uint8 hubPort)
188 {
189 	// some devices have bogus descriptors
190 	if (speed == USB_SPEED_HIGHSPEED && maxPacketSize != 512)
191 		maxPacketSize = 512;
192 
193 	Pipe::InitCommon(deviceAddress, endpointAddress, speed, direction,
194 		maxPacketSize, interval, hubAddress, hubPort);
195 }
196 
197 
198 status_t
199 BulkPipe::QueueBulk(void *data, size_t dataLength, usb_callback_func callback,
200 	void *callbackCookie)
201 {
202 	if (dataLength > 0 && data == NULL)
203 		return B_BAD_VALUE;
204 
205 	Transfer *transfer = new(std::nothrow) Transfer(this);
206 	if (!transfer)
207 		return B_NO_MEMORY;
208 
209 	transfer->SetData((uint8 *)data, dataLength);
210 	transfer->SetCallback(callback, callbackCookie);
211 
212 	status_t result = GetBusManager()->SubmitTransfer(transfer);
213 	if (result < B_OK)
214 		delete transfer;
215 	return result;
216 }
217 
218 
219 status_t
220 BulkPipe::QueueBulkV(iovec *vector, size_t vectorCount,
221 	usb_callback_func callback, void *callbackCookie, bool physical)
222 {
223 	if (vectorCount > 0 && vector == NULL)
224 		return B_BAD_VALUE;
225 
226 	Transfer *transfer = new(std::nothrow) Transfer(this);
227 	if (!transfer)
228 		return B_NO_MEMORY;
229 
230 	transfer->SetPhysical(physical);
231 	transfer->SetVector(vector, vectorCount);
232 	transfer->SetCallback(callback, callbackCookie);
233 
234 	status_t result = GetBusManager()->SubmitTransfer(transfer);
235 	if (result < B_OK)
236 		delete transfer;
237 	return result;
238 }
239 
240 
241 //
242 // #pragma mark -
243 //
244 
245 
246 IsochronousPipe::IsochronousPipe(Object *parent)
247 	:	Pipe(parent),
248 		fMaxQueuedPackets(0),
249 		fMaxBufferDuration(0),
250 		fSampleSize(0)
251 {
252 }
253 
254 
255 status_t
256 IsochronousPipe::QueueIsochronous(void *data, size_t dataLength,
257 	usb_iso_packet_descriptor *packetDesc, uint32 packetCount,
258 	uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback,
259 	void *callbackCookie)
260 {
261 	if ((dataLength > 0 && data == NULL)
262 		|| (packetCount > 0 && packetDesc == NULL))
263 		return B_BAD_VALUE;
264 
265 	usb_isochronous_data *isochronousData
266 		= new(std::nothrow) usb_isochronous_data;
267 
268 	if (!isochronousData)
269 		return B_NO_MEMORY;
270 
271 	isochronousData->packet_descriptors = packetDesc;
272 	isochronousData->packet_count = packetCount;
273 	isochronousData->starting_frame_number = startingFrameNumber;
274 	isochronousData->flags = flags;
275 
276 	Transfer *transfer = new(std::nothrow) Transfer(this);
277 	if (!transfer) {
278 		delete isochronousData;
279 		return B_NO_MEMORY;
280 	}
281 
282 	transfer->SetData((uint8 *)data, dataLength);
283 	transfer->SetCallback(callback, callbackCookie);
284 	transfer->SetIsochronousData(isochronousData);
285 
286 	status_t result = GetBusManager()->SubmitTransfer(transfer);
287 	if (result < B_OK)
288 		delete transfer;
289 	return result;
290 }
291 
292 
293 status_t
294 IsochronousPipe::SetPipePolicy(uint8 maxQueuedPackets,
295 	uint16 maxBufferDurationMS, uint16 sampleSize)
296 {
297 	if (maxQueuedPackets == fMaxQueuedPackets
298 		|| maxBufferDurationMS == fMaxBufferDuration
299 		|| sampleSize == fSampleSize)
300 		return B_OK;
301 
302 	fMaxQueuedPackets = maxQueuedPackets;
303 	fMaxBufferDuration = maxBufferDurationMS;
304 	fSampleSize = sampleSize;
305 
306 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_PIPE_POLICY_CHANGED);
307 	return B_OK;
308 }
309 
310 
311 status_t
312 IsochronousPipe::GetPipePolicy(uint8 *maxQueuedPackets,
313 	uint16 *maxBufferDurationMS, uint16 *sampleSize)
314 {
315 	if (maxQueuedPackets)
316 		*maxQueuedPackets = fMaxQueuedPackets;
317 	if (maxBufferDurationMS)
318 		*maxBufferDurationMS = fMaxBufferDuration;
319 	if (sampleSize)
320 		*sampleSize = fSampleSize;
321 	return B_OK;
322 }
323 
324 
325 //
326 // #pragma mark -
327 //
328 
329 
330 ControlPipe::ControlPipe(Object *parent)
331 	:	Pipe(parent),
332 		fNotifySem(-1)
333 {
334 	mutex_init(&fSendRequestLock, "control pipe send request");
335 }
336 
337 
338 ControlPipe::~ControlPipe()
339 {
340 	// We do this here in case a submitted request is still running.
341 	PutUSBID(false);
342 	ControlPipe::CancelQueuedTransfers(true);
343 	WaitForUnbusy();
344 
345 	if (fNotifySem >= 0)
346 		delete_sem(fNotifySem);
347 	mutex_lock(&fSendRequestLock);
348 	mutex_destroy(&fSendRequestLock);
349 }
350 
351 
352 void
353 ControlPipe::InitCommon(int8 deviceAddress, uint8 endpointAddress,
354 	usb_speed speed, pipeDirection direction, size_t maxPacketSize,
355 	uint8 interval, int8 hubAddress, uint8 hubPort)
356 {
357 	// The USB 2.0 spec section 5.5.3 gives fixed max packet sizes for the
358 	// different speeds. The USB 3.1 specs defines the max packet size to a
359 	// fixed 512 for control endpoints in 9.6.6. Some devices ignore these
360 	// values and use bogus ones, so we restrict them here.
361 	switch (speed) {
362 		case USB_SPEED_LOWSPEED:
363 			maxPacketSize = 8;
364 			break;
365 		case USB_SPEED_HIGHSPEED:
366 			maxPacketSize = 64;
367 			break;
368 		case USB_SPEED_SUPERSPEED:
369 			maxPacketSize = 512;
370 			break;
371 
372 		default:
373 			break;
374 	}
375 
376 	Pipe::InitCommon(deviceAddress, endpointAddress, speed, direction,
377 		maxPacketSize, interval, hubAddress, hubPort);
378 }
379 
380 
381 status_t
382 ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
383 	uint16 index, uint16 length, void *data, size_t dataLength,
384 	size_t *actualLength)
385 {
386 	status_t result = mutex_lock(&fSendRequestLock);
387 	if (result != B_OK)
388 		return result;
389 
390 	if (fNotifySem < 0) {
391 		fNotifySem = create_sem(0, "usb send request notify");
392 		if (fNotifySem < 0) {
393 			mutex_unlock(&fSendRequestLock);
394 			return B_NO_MORE_SEMS;
395 		}
396 	}
397 
398 	result = QueueRequest(requestType, request, value, index, length, data,
399 		dataLength, SendRequestCallback, this);
400 	if (result < B_OK) {
401 		mutex_unlock(&fSendRequestLock);
402 		return result;
403 	}
404 
405 	// The sem will be released unconditionally in the callback after the
406 	// result data was filled in. Use a 2 seconds timeout for control transfers.
407 	if (acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 2000000) < B_OK) {
408 		TRACE_ERROR("timeout waiting for queued request to complete\n");
409 
410 		CancelQueuedTransfers(false);
411 
412 		// After the above cancel returns it is guaranteed that the callback
413 		// has been invoked. Therefore we can simply grab that released
414 		// semaphore again to clean up.
415 		acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 0);
416 
417 		if (actualLength)
418 			*actualLength = 0;
419 
420 		mutex_unlock(&fSendRequestLock);
421 		return B_TIMED_OUT;
422 	}
423 
424 	if (actualLength)
425 		*actualLength = fActualLength;
426 
427 	mutex_unlock(&fSendRequestLock);
428 	return fTransferStatus;
429 }
430 
431 
432 void
433 ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data,
434 	size_t actualLength)
435 {
436 	ControlPipe *pipe = (ControlPipe *)cookie;
437 	pipe->fTransferStatus = status;
438 	pipe->fActualLength = actualLength;
439 	release_sem(pipe->fNotifySem);
440 }
441 
442 
443 status_t
444 ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value,
445 	uint16 index, uint16 length, void *data, size_t dataLength,
446 	usb_callback_func callback, void *callbackCookie)
447 {
448 	if (dataLength > 0 && data == NULL)
449 		return B_BAD_VALUE;
450 
451 	if (USBID() == UINT32_MAX)
452 		return B_NO_INIT;
453 
454 	usb_request_data *requestData = new(std::nothrow) usb_request_data;
455 	if (!requestData)
456 		return B_NO_MEMORY;
457 
458 	requestData->RequestType = requestType;
459 	requestData->Request = request;
460 	requestData->Value = value;
461 	requestData->Index = index;
462 	requestData->Length = length;
463 
464 	Transfer *transfer = new(std::nothrow) Transfer(this);
465 	if (!transfer) {
466 		delete requestData;
467 		return B_NO_MEMORY;
468 	}
469 
470 	transfer->SetRequestData(requestData);
471 	transfer->SetData((uint8 *)data, dataLength);
472 	transfer->SetCallback(callback, callbackCookie);
473 
474 	status_t result = GetBusManager()->SubmitTransfer(transfer);
475 	if (result < B_OK)
476 		delete transfer;
477 	return result;
478 }
479 
480 
481 status_t
482 ControlPipe::CancelQueuedTransfers(bool force)
483 {
484 	if (force && fNotifySem >= 0) {
485 		// There is likely a transfer currently running; we need to cancel it
486 		// manually, as callbacks are not invoked when force-cancelling.
487 		fTransferStatus = B_CANCELED;
488 		fActualLength = 0;
489 		release_sem_etc(fNotifySem, 1, B_RELEASE_IF_WAITING_ONLY);
490 	}
491 
492 	return Pipe::CancelQueuedTransfers(force);
493 }
494