xref: /haiku/src/add-ons/kernel/bus_managers/usb/Pipe.cpp (revision 909af08f4328301fbdef1ffb41f566c3b5bec0c7)
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 	// See comments in ControlPipe::InitCommon.
190 	switch (speed) {
191 		case USB_SPEED_HIGHSPEED:
192 			maxPacketSize = 512;
193 			break;
194 		case USB_SPEED_SUPERSPEED:
195 		case USB_SPEED_SUPERSPEEDPLUS:
196 			maxPacketSize = 1024;
197 			break;
198 
199 		default:
200 			break;
201 	}
202 
203 	Pipe::InitCommon(deviceAddress, endpointAddress, speed, direction,
204 		maxPacketSize, interval, hubAddress, hubPort);
205 }
206 
207 
208 status_t
209 BulkPipe::QueueBulk(void *data, size_t dataLength, usb_callback_func callback,
210 	void *callbackCookie)
211 {
212 	if (dataLength > 0 && data == NULL)
213 		return B_BAD_VALUE;
214 
215 	Transfer *transfer = new(std::nothrow) Transfer(this);
216 	if (!transfer)
217 		return B_NO_MEMORY;
218 
219 	transfer->SetData((uint8 *)data, dataLength);
220 	transfer->SetCallback(callback, callbackCookie);
221 
222 	status_t result = GetBusManager()->SubmitTransfer(transfer);
223 	if (result < B_OK)
224 		delete transfer;
225 	return result;
226 }
227 
228 
229 status_t
230 BulkPipe::QueueBulkV(iovec *vector, size_t vectorCount,
231 	usb_callback_func callback, void *callbackCookie)
232 {
233 	if (vectorCount > 0 && vector == NULL)
234 		return B_BAD_VALUE;
235 
236 	Transfer *transfer = new(std::nothrow) Transfer(this);
237 	if (!transfer)
238 		return B_NO_MEMORY;
239 
240 	transfer->SetVector(vector, vectorCount);
241 	transfer->SetCallback(callback, callbackCookie);
242 
243 	status_t result = GetBusManager()->SubmitTransfer(transfer);
244 	if (result < B_OK)
245 		delete transfer;
246 	return result;
247 }
248 
249 
250 status_t
251 BulkPipe::QueueBulkV(physical_entry *vector, size_t vectorCount,
252 	usb_callback_func callback, void *callbackCookie)
253 {
254 	if (vectorCount > 0 && vector == NULL)
255 		return B_BAD_VALUE;
256 
257 	Transfer *transfer = new(std::nothrow) Transfer(this);
258 	if (!transfer)
259 		return B_NO_MEMORY;
260 
261 	transfer->SetVector(vector, vectorCount);
262 	transfer->SetCallback(callback, callbackCookie);
263 
264 	status_t result = GetBusManager()->SubmitTransfer(transfer);
265 	if (result < B_OK)
266 		delete transfer;
267 	return result;
268 }
269 
270 
271 //
272 // #pragma mark -
273 //
274 
275 
276 IsochronousPipe::IsochronousPipe(Object *parent)
277 	:	Pipe(parent),
278 		fMaxQueuedPackets(0),
279 		fMaxBufferDuration(0),
280 		fSampleSize(0)
281 {
282 }
283 
284 
285 status_t
286 IsochronousPipe::QueueIsochronous(void *data, size_t dataLength,
287 	usb_iso_packet_descriptor *packetDesc, uint32 packetCount,
288 	uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback,
289 	void *callbackCookie)
290 {
291 	if ((dataLength > 0 && data == NULL)
292 		|| (packetCount > 0 && packetDesc == NULL))
293 		return B_BAD_VALUE;
294 
295 	usb_isochronous_data *isochronousData
296 		= new(std::nothrow) usb_isochronous_data;
297 
298 	if (!isochronousData)
299 		return B_NO_MEMORY;
300 
301 	isochronousData->packet_descriptors = packetDesc;
302 	isochronousData->packet_count = packetCount;
303 	isochronousData->starting_frame_number = startingFrameNumber;
304 	isochronousData->flags = flags;
305 
306 	for (uint32 i = 0; i < isochronousData->packet_count; i++) {
307 		isochronousData->packet_descriptors[i].actual_length = 0;
308 		isochronousData->packet_descriptors[i].status = B_NO_INIT;
309 	}
310 
311 	Transfer *transfer = new(std::nothrow) Transfer(this);
312 	if (!transfer) {
313 		delete isochronousData;
314 		return B_NO_MEMORY;
315 	}
316 
317 	transfer->SetData((uint8 *)data, dataLength);
318 	transfer->SetCallback(callback, callbackCookie);
319 	transfer->SetIsochronousData(isochronousData);
320 
321 	status_t result = GetBusManager()->SubmitTransfer(transfer);
322 	if (result < B_OK)
323 		delete transfer;
324 	return result;
325 }
326 
327 
328 status_t
329 IsochronousPipe::SetPipePolicy(uint8 maxQueuedPackets,
330 	uint16 maxBufferDurationMS, uint16 sampleSize)
331 {
332 	if (maxQueuedPackets == fMaxQueuedPackets
333 		|| maxBufferDurationMS == fMaxBufferDuration
334 		|| sampleSize == fSampleSize)
335 		return B_OK;
336 
337 	fMaxQueuedPackets = maxQueuedPackets;
338 	fMaxBufferDuration = maxBufferDurationMS;
339 	fSampleSize = sampleSize;
340 
341 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_PIPE_POLICY_CHANGED);
342 	return B_OK;
343 }
344 
345 
346 status_t
347 IsochronousPipe::GetPipePolicy(uint8 *maxQueuedPackets,
348 	uint16 *maxBufferDurationMS, uint16 *sampleSize)
349 {
350 	if (maxQueuedPackets)
351 		*maxQueuedPackets = fMaxQueuedPackets;
352 	if (maxBufferDurationMS)
353 		*maxBufferDurationMS = fMaxBufferDuration;
354 	if (sampleSize)
355 		*sampleSize = fSampleSize;
356 	return B_OK;
357 }
358 
359 
360 //
361 // #pragma mark -
362 //
363 
364 
365 ControlPipe::ControlPipe(Object *parent)
366 	:	Pipe(parent),
367 		fNotifySem(-1)
368 {
369 	mutex_init(&fSendRequestLock, "control pipe send request");
370 }
371 
372 
373 ControlPipe::~ControlPipe()
374 {
375 	// We do this here in case a submitted request is still running.
376 	PutUSBID(false);
377 	ControlPipe::CancelQueuedTransfers(true);
378 	WaitForUnbusy();
379 
380 	if (fNotifySem >= 0)
381 		delete_sem(fNotifySem);
382 	mutex_lock(&fSendRequestLock);
383 	mutex_destroy(&fSendRequestLock);
384 }
385 
386 
387 void
388 ControlPipe::InitCommon(int8 deviceAddress, uint8 endpointAddress,
389 	usb_speed speed, pipeDirection direction, size_t maxPacketSize,
390 	uint8 interval, int8 hubAddress, uint8 hubPort)
391 {
392 	// The USB 2.0 spec section 5.5.3 gives fixed max packet sizes for the
393 	// different speeds. The USB 3.1 specs defines some fixed max packet sizes,
394 	// including for control endpoints in 9.6.6. Some devices ignore these
395 	// values and use bogus ones, so we restrict them here.
396 	switch (speed) {
397 		case USB_SPEED_LOWSPEED:
398 			maxPacketSize = 8;
399 			break;
400 		case USB_SPEED_HIGHSPEED:
401 			maxPacketSize = 64;
402 			break;
403 		case USB_SPEED_SUPERSPEED:
404 		case USB_SPEED_SUPERSPEEDPLUS:
405 			maxPacketSize = 512;
406 			break;
407 
408 		default:
409 			break;
410 	}
411 
412 	Pipe::InitCommon(deviceAddress, endpointAddress, speed, direction,
413 		maxPacketSize, interval, hubAddress, hubPort);
414 }
415 
416 
417 status_t
418 ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
419 	uint16 index, uint16 length, void *data, size_t dataLength,
420 	size_t *actualLength)
421 {
422 	status_t result = mutex_lock(&fSendRequestLock);
423 	if (result != B_OK)
424 		return result;
425 
426 	if (fNotifySem < 0) {
427 		fNotifySem = create_sem(0, "usb send request notify");
428 		if (fNotifySem < 0) {
429 			mutex_unlock(&fSendRequestLock);
430 			return B_NO_MORE_SEMS;
431 		}
432 	}
433 
434 	result = QueueRequest(requestType, request, value, index, length, data,
435 		dataLength, SendRequestCallback, this);
436 	if (result < B_OK) {
437 		mutex_unlock(&fSendRequestLock);
438 		return result;
439 	}
440 
441 	// The sem will be released unconditionally in the callback after the
442 	// result data was filled in. Use a 2 seconds timeout for control transfers.
443 	if (acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 2000000) < B_OK) {
444 		TRACE_ERROR("timeout waiting for queued request to complete\n");
445 
446 		CancelQueuedTransfers(false);
447 
448 		// After the above cancel returns it is guaranteed that the callback
449 		// has been invoked. Therefore we can simply grab that released
450 		// semaphore again to clean up.
451 		acquire_sem(fNotifySem);
452 
453 		if (actualLength)
454 			*actualLength = 0;
455 
456 		mutex_unlock(&fSendRequestLock);
457 		return B_TIMED_OUT;
458 	}
459 
460 	if (actualLength)
461 		*actualLength = fActualLength;
462 
463 	mutex_unlock(&fSendRequestLock);
464 	return fTransferStatus;
465 }
466 
467 
468 void
469 ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data,
470 	size_t actualLength)
471 {
472 	ControlPipe *pipe = (ControlPipe *)cookie;
473 	pipe->fTransferStatus = status;
474 	pipe->fActualLength = actualLength;
475 	release_sem(pipe->fNotifySem);
476 }
477 
478 
479 status_t
480 ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value,
481 	uint16 index, uint16 length, void *data, size_t dataLength,
482 	usb_callback_func callback, void *callbackCookie)
483 {
484 	if (dataLength > 0 && data == NULL)
485 		return B_BAD_VALUE;
486 
487 	if (USBID() == UINT32_MAX)
488 		return B_NO_INIT;
489 
490 	usb_request_data *requestData = new(std::nothrow) usb_request_data;
491 	if (!requestData)
492 		return B_NO_MEMORY;
493 
494 	requestData->RequestType = requestType;
495 	requestData->Request = request;
496 	requestData->Value = value;
497 	requestData->Index = index;
498 	requestData->Length = length;
499 
500 	Transfer *transfer = new(std::nothrow) Transfer(this);
501 	if (!transfer) {
502 		delete requestData;
503 		return B_NO_MEMORY;
504 	}
505 
506 	transfer->SetRequestData(requestData);
507 	transfer->SetData((uint8 *)data, dataLength);
508 	transfer->SetCallback(callback, callbackCookie);
509 
510 	status_t result = GetBusManager()->SubmitTransfer(transfer);
511 	if (result < B_OK)
512 		delete transfer;
513 	return result;
514 }
515 
516 
517 status_t
518 ControlPipe::CancelQueuedTransfers(bool force)
519 {
520 	if (force && fNotifySem >= 0) {
521 		// There is likely a transfer currently running; we need to cancel it
522 		// manually, as callbacks are not invoked when force-cancelling.
523 		fTransferStatus = B_CANCELED;
524 		fActualLength = 0;
525 		release_sem_etc(fNotifySem, 1, B_RELEASE_IF_WAITING_ONLY);
526 	}
527 
528 	return Pipe::CancelQueuedTransfers(force);
529 }
530