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