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
13Pipe::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
22Pipe::~Pipe()
23{
24	PutUSBID();
25
26	Pipe::CancelQueuedTransfers(true);
27	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_DESTROYED);
28}
29
30
31void
32Pipe::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
52void
53Pipe::InitSuperSpeed(uint8 maxBurst, uint16 bytesPerInterval)
54{
55	fMaxBurst = maxBurst;
56	fBytesPerInterval = bytesPerInterval;
57}
58
59
60void
61Pipe::SetHubInfo(int8 address, uint8 port)
62{
63	fHubAddress = address;
64	fHubPort = port;
65}
66
67
68status_t
69Pipe::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
79status_t
80Pipe::CancelQueuedTransfers(bool force)
81{
82	return GetBusManager()->CancelQueuedTransfers(this, force);
83}
84
85
86status_t
87Pipe::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
103status_t
104Pipe::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
124status_t
125Pipe::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
146InterruptPipe::InterruptPipe(Object *parent)
147	:	Pipe(parent)
148{
149}
150
151
152status_t
153InterruptPipe::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
178BulkPipe::BulkPipe(Object *parent)
179	:	Pipe(parent)
180{
181}
182
183
184void
185BulkPipe::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
207status_t
208BulkPipe::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
228status_t
229BulkPipe::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
249status_t
250BulkPipe::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
275IsochronousPipe::IsochronousPipe(Object *parent)
276	:	Pipe(parent),
277		fMaxQueuedPackets(0),
278		fMaxBufferDuration(0),
279		fSampleSize(0)
280{
281}
282
283
284status_t
285IsochronousPipe::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
327status_t
328IsochronousPipe::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
345status_t
346IsochronousPipe::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
364ControlPipe::ControlPipe(Object *parent)
365	:	Pipe(parent),
366		fNotifySem(-1)
367{
368	mutex_init(&fSendRequestLock, "control pipe send request");
369}
370
371
372ControlPipe::~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
386void
387ControlPipe::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
415status_t
416ControlPipe::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
466void
467ControlPipe::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
477status_t
478ControlPipe::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
515status_t
516ControlPipe::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