xref: /haiku/src/add-ons/kernel/bus_managers/usb/Pipe.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
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 	Transfer *transfer = new(std::nothrow) Transfer(this);
306 	if (!transfer) {
307 		delete isochronousData;
308 		return B_NO_MEMORY;
309 	}
310 
311 	transfer->SetData((uint8 *)data, dataLength);
312 	transfer->SetCallback(callback, callbackCookie);
313 	transfer->SetIsochronousData(isochronousData);
314 
315 	status_t result = GetBusManager()->SubmitTransfer(transfer);
316 	if (result < B_OK)
317 		delete transfer;
318 	return result;
319 }
320 
321 
322 status_t
323 IsochronousPipe::SetPipePolicy(uint8 maxQueuedPackets,
324 	uint16 maxBufferDurationMS, uint16 sampleSize)
325 {
326 	if (maxQueuedPackets == fMaxQueuedPackets
327 		|| maxBufferDurationMS == fMaxBufferDuration
328 		|| sampleSize == fSampleSize)
329 		return B_OK;
330 
331 	fMaxQueuedPackets = maxQueuedPackets;
332 	fMaxBufferDuration = maxBufferDurationMS;
333 	fSampleSize = sampleSize;
334 
335 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_PIPE_POLICY_CHANGED);
336 	return B_OK;
337 }
338 
339 
340 status_t
341 IsochronousPipe::GetPipePolicy(uint8 *maxQueuedPackets,
342 	uint16 *maxBufferDurationMS, uint16 *sampleSize)
343 {
344 	if (maxQueuedPackets)
345 		*maxQueuedPackets = fMaxQueuedPackets;
346 	if (maxBufferDurationMS)
347 		*maxBufferDurationMS = fMaxBufferDuration;
348 	if (sampleSize)
349 		*sampleSize = fSampleSize;
350 	return B_OK;
351 }
352 
353 
354 //
355 // #pragma mark -
356 //
357 
358 
359 ControlPipe::ControlPipe(Object *parent)
360 	:	Pipe(parent),
361 		fNotifySem(-1)
362 {
363 	mutex_init(&fSendRequestLock, "control pipe send request");
364 }
365 
366 
367 ControlPipe::~ControlPipe()
368 {
369 	// We do this here in case a submitted request is still running.
370 	PutUSBID(false);
371 	ControlPipe::CancelQueuedTransfers(true);
372 	WaitForUnbusy();
373 
374 	if (fNotifySem >= 0)
375 		delete_sem(fNotifySem);
376 	mutex_lock(&fSendRequestLock);
377 	mutex_destroy(&fSendRequestLock);
378 }
379 
380 
381 void
382 ControlPipe::InitCommon(int8 deviceAddress, uint8 endpointAddress,
383 	usb_speed speed, pipeDirection direction, size_t maxPacketSize,
384 	uint8 interval, int8 hubAddress, uint8 hubPort)
385 {
386 	// The USB 2.0 spec section 5.5.3 gives fixed max packet sizes for the
387 	// different speeds. The USB 3.1 specs defines some fixed max packet sizes,
388 	// including for control endpoints in 9.6.6. Some devices ignore these
389 	// values and use bogus ones, so we restrict them here.
390 	switch (speed) {
391 		case USB_SPEED_LOWSPEED:
392 			maxPacketSize = 8;
393 			break;
394 		case USB_SPEED_HIGHSPEED:
395 			maxPacketSize = 64;
396 			break;
397 		case USB_SPEED_SUPERSPEED:
398 			maxPacketSize = 512;
399 			break;
400 
401 		default:
402 			break;
403 	}
404 
405 	Pipe::InitCommon(deviceAddress, endpointAddress, speed, direction,
406 		maxPacketSize, interval, hubAddress, hubPort);
407 }
408 
409 
410 status_t
411 ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
412 	uint16 index, uint16 length, void *data, size_t dataLength,
413 	size_t *actualLength)
414 {
415 	status_t result = mutex_lock(&fSendRequestLock);
416 	if (result != B_OK)
417 		return result;
418 
419 	if (fNotifySem < 0) {
420 		fNotifySem = create_sem(0, "usb send request notify");
421 		if (fNotifySem < 0) {
422 			mutex_unlock(&fSendRequestLock);
423 			return B_NO_MORE_SEMS;
424 		}
425 	}
426 
427 	result = QueueRequest(requestType, request, value, index, length, data,
428 		dataLength, SendRequestCallback, this);
429 	if (result < B_OK) {
430 		mutex_unlock(&fSendRequestLock);
431 		return result;
432 	}
433 
434 	// The sem will be released unconditionally in the callback after the
435 	// result data was filled in. Use a 2 seconds timeout for control transfers.
436 	if (acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 2000000) < B_OK) {
437 		TRACE_ERROR("timeout waiting for queued request to complete\n");
438 
439 		CancelQueuedTransfers(false);
440 
441 		// After the above cancel returns it is guaranteed that the callback
442 		// has been invoked. Therefore we can simply grab that released
443 		// semaphore again to clean up.
444 		acquire_sem(fNotifySem);
445 
446 		if (actualLength)
447 			*actualLength = 0;
448 
449 		mutex_unlock(&fSendRequestLock);
450 		return B_TIMED_OUT;
451 	}
452 
453 	if (actualLength)
454 		*actualLength = fActualLength;
455 
456 	mutex_unlock(&fSendRequestLock);
457 	return fTransferStatus;
458 }
459 
460 
461 void
462 ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data,
463 	size_t actualLength)
464 {
465 	ControlPipe *pipe = (ControlPipe *)cookie;
466 	pipe->fTransferStatus = status;
467 	pipe->fActualLength = actualLength;
468 	release_sem(pipe->fNotifySem);
469 }
470 
471 
472 status_t
473 ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value,
474 	uint16 index, uint16 length, void *data, size_t dataLength,
475 	usb_callback_func callback, void *callbackCookie)
476 {
477 	if (dataLength > 0 && data == NULL)
478 		return B_BAD_VALUE;
479 
480 	if (USBID() == UINT32_MAX)
481 		return B_NO_INIT;
482 
483 	usb_request_data *requestData = new(std::nothrow) usb_request_data;
484 	if (!requestData)
485 		return B_NO_MEMORY;
486 
487 	requestData->RequestType = requestType;
488 	requestData->Request = request;
489 	requestData->Value = value;
490 	requestData->Index = index;
491 	requestData->Length = length;
492 
493 	Transfer *transfer = new(std::nothrow) Transfer(this);
494 	if (!transfer) {
495 		delete requestData;
496 		return B_NO_MEMORY;
497 	}
498 
499 	transfer->SetRequestData(requestData);
500 	transfer->SetData((uint8 *)data, dataLength);
501 	transfer->SetCallback(callback, callbackCookie);
502 
503 	status_t result = GetBusManager()->SubmitTransfer(transfer);
504 	if (result < B_OK)
505 		delete transfer;
506 	return result;
507 }
508 
509 
510 status_t
511 ControlPipe::CancelQueuedTransfers(bool force)
512 {
513 	if (force && fNotifySem >= 0) {
514 		// There is likely a transfer currently running; we need to cancel it
515 		// manually, as callbacks are not invoked when force-cancelling.
516 		fTransferStatus = B_CANCELED;
517 		fActualLength = 0;
518 		release_sem_etc(fNotifySem, 1, B_RELEASE_IF_WAITING_ONLY);
519 	}
520 
521 	return Pipe::CancelQueuedTransfers(force);
522 }
523