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