xref: /haiku/src/add-ons/kernel/bus_managers/usb/Pipe.cpp (revision 372b901dfeada686207d00bbcce456f748bbda12)
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 	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 	// ToDo: keep track of all submited transfers to be able to cancel them
72 	return GetBusManager()->SubmitTransfer(transfer);
73 }
74 
75 
76 status_t
77 Pipe::CancelQueuedTransfers(bool force)
78 {
79 	return GetBusManager()->CancelQueuedTransfers(this, force);
80 }
81 
82 
83 status_t
84 Pipe::SetFeature(uint16 selector)
85 {
86 	TRACE("set feature %u\n", selector);
87 	Device *device = (Device *)Parent();
88 	if (device->InitCheck() != B_OK)
89 		return B_NO_INIT;
90 
91 	return device->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 	Device *device = (Device *)Parent();
108 	if (device->InitCheck() != B_OK)
109 		return B_NO_INIT;
110 
111 	// clearing a stalled condition resets the data toggle
112 	if (selector == USB_FEATURE_ENDPOINT_HALT)
113 		SetDataToggle(false);
114 
115 	TRACE("clear feature %u\n", selector);
116 	return device->DefaultPipe()->SendRequest(
117 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
118 		USB_REQUEST_CLEAR_FEATURE,
119 		selector,
120 		fEndpointAddress | (fDirection == In ? USB_ENDPOINT_ADDR_DIR_IN
121 			: USB_ENDPOINT_ADDR_DIR_OUT),
122 		0,
123 		NULL,
124 		0,
125 		NULL);
126 }
127 
128 
129 status_t
130 Pipe::GetStatus(uint16 *status)
131 {
132 	TRACE("get status\n");
133 	Device *device = (Device *)Parent();
134 	if (device->InitCheck() != B_OK)
135 		return B_NO_INIT;
136 
137 	return device->DefaultPipe()->SendRequest(
138 		USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_IN,
139 		USB_REQUEST_GET_STATUS,
140 		0,
141 		fEndpointAddress | (fDirection == In ? USB_ENDPOINT_ADDR_DIR_IN
142 			: USB_ENDPOINT_ADDR_DIR_OUT),
143 		2,
144 		(void *)status,
145 		2,
146 		NULL);
147 }
148 
149 
150 //
151 // #pragma mark -
152 //
153 
154 
155 InterruptPipe::InterruptPipe(Object *parent)
156 	:	Pipe(parent)
157 {
158 }
159 
160 
161 status_t
162 InterruptPipe::QueueInterrupt(void *data, size_t dataLength,
163 	usb_callback_func callback, void *callbackCookie)
164 {
165 	if (dataLength > 0 && data == NULL)
166 		return B_BAD_VALUE;
167 
168 	Transfer *transfer = new(std::nothrow) Transfer(this);
169 	if (!transfer)
170 		return B_NO_MEMORY;
171 
172 	transfer->SetData((uint8 *)data, dataLength);
173 	transfer->SetCallback(callback, callbackCookie);
174 
175 	status_t result = GetBusManager()->SubmitTransfer(transfer);
176 	if (result < B_OK)
177 		delete transfer;
178 	return result;
179 }
180 
181 
182 //
183 // #pragma mark -
184 //
185 
186 
187 BulkPipe::BulkPipe(Object *parent)
188 	:	Pipe(parent)
189 {
190 }
191 
192 
193 void
194 BulkPipe::InitCommon(int8 deviceAddress, uint8 endpointAddress,
195 	usb_speed speed, pipeDirection direction, size_t maxPacketSize,
196 	uint8 interval, int8 hubAddress, uint8 hubPort)
197 {
198 	// some devices have bogus descriptors
199 	if (speed == USB_SPEED_HIGHSPEED && maxPacketSize != 512)
200 		maxPacketSize = 512;
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, bool physical)
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->SetPhysical(physical);
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 //
251 // #pragma mark -
252 //
253 
254 
255 IsochronousPipe::IsochronousPipe(Object *parent)
256 	:	Pipe(parent),
257 		fMaxQueuedPackets(0),
258 		fMaxBufferDuration(0),
259 		fSampleSize(0)
260 {
261 }
262 
263 
264 status_t
265 IsochronousPipe::QueueIsochronous(void *data, size_t dataLength,
266 	usb_iso_packet_descriptor *packetDesc, uint32 packetCount,
267 	uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback,
268 	void *callbackCookie)
269 {
270 	if ((dataLength > 0 && data == NULL)
271 		|| (packetCount > 0 && packetDesc == NULL))
272 		return B_BAD_VALUE;
273 
274 	usb_isochronous_data *isochronousData
275 		= new(std::nothrow) usb_isochronous_data;
276 
277 	if (!isochronousData)
278 		return B_NO_MEMORY;
279 
280 	isochronousData->packet_descriptors = packetDesc;
281 	isochronousData->packet_count = packetCount;
282 	isochronousData->starting_frame_number = startingFrameNumber;
283 	isochronousData->flags = flags;
284 
285 	Transfer *transfer = new(std::nothrow) Transfer(this);
286 	if (!transfer) {
287 		delete isochronousData;
288 		return B_NO_MEMORY;
289 	}
290 
291 	transfer->SetData((uint8 *)data, dataLength);
292 	transfer->SetCallback(callback, callbackCookie);
293 	transfer->SetIsochronousData(isochronousData);
294 
295 	status_t result = GetBusManager()->SubmitTransfer(transfer);
296 	if (result < B_OK)
297 		delete transfer;
298 	return result;
299 }
300 
301 
302 status_t
303 IsochronousPipe::SetPipePolicy(uint8 maxQueuedPackets,
304 	uint16 maxBufferDurationMS, uint16 sampleSize)
305 {
306 	if (maxQueuedPackets == fMaxQueuedPackets
307 		|| maxBufferDurationMS == fMaxBufferDuration
308 		|| sampleSize == fSampleSize)
309 		return B_OK;
310 
311 	fMaxQueuedPackets = maxQueuedPackets;
312 	fMaxBufferDuration = maxBufferDurationMS;
313 	fSampleSize = sampleSize;
314 
315 	GetBusManager()->NotifyPipeChange(this, USB_CHANGE_PIPE_POLICY_CHANGED);
316 	return B_OK;
317 }
318 
319 
320 status_t
321 IsochronousPipe::GetPipePolicy(uint8 *maxQueuedPackets,
322 	uint16 *maxBufferDurationMS, uint16 *sampleSize)
323 {
324 	if (maxQueuedPackets)
325 		*maxQueuedPackets = fMaxQueuedPackets;
326 	if (maxBufferDurationMS)
327 		*maxBufferDurationMS = fMaxBufferDuration;
328 	if (sampleSize)
329 		*sampleSize = fSampleSize;
330 	return B_OK;
331 }
332 
333 
334 //
335 // #pragma mark -
336 //
337 
338 
339 ControlPipe::ControlPipe(Object *parent)
340 	:	Pipe(parent),
341 		fNotifySem(-1)
342 {
343 	mutex_init(&fSendRequestLock, "control pipe send request");
344 }
345 
346 
347 ControlPipe::~ControlPipe()
348 {
349 	if (fNotifySem >= 0)
350 		delete_sem(fNotifySem);
351 	mutex_lock(&fSendRequestLock);
352 	mutex_destroy(&fSendRequestLock);
353 }
354 
355 
356 void
357 ControlPipe::InitCommon(int8 deviceAddress, uint8 endpointAddress,
358 	usb_speed speed, pipeDirection direction, size_t maxPacketSize,
359 	uint8 interval, int8 hubAddress, uint8 hubPort)
360 {
361 	// The USB 2.0 spec section 5.5.3 gives fixed max packet sizes for the
362 	// different speeds. The USB 3.1 specs defines the max packet size to a
363 	// fixed 512 for control endpoints in 9.6.6. Some devices ignore these
364 	// values and use bogus ones, so we restrict them here.
365 	switch (speed) {
366 		case USB_SPEED_LOWSPEED:
367 			maxPacketSize = 8;
368 			break;
369 		case USB_SPEED_HIGHSPEED:
370 			maxPacketSize = 64;
371 			break;
372 		case USB_SPEED_SUPERSPEED:
373 			maxPacketSize = 512;
374 			break;
375 
376 		default:
377 			break;
378 	}
379 
380 	Pipe::InitCommon(deviceAddress, endpointAddress, speed, direction,
381 		maxPacketSize, interval, hubAddress, hubPort);
382 }
383 
384 
385 status_t
386 ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
387 	uint16 index, uint16 length, void *data, size_t dataLength,
388 	size_t *actualLength)
389 {
390 	status_t result = mutex_lock(&fSendRequestLock);
391 	if (result != B_OK)
392 		return result;
393 
394 	if (fNotifySem < 0) {
395 		fNotifySem = create_sem(0, "usb send request notify");
396 		if (fNotifySem < 0) {
397 			mutex_unlock(&fSendRequestLock);
398 			return B_NO_MORE_SEMS;
399 		}
400 	}
401 
402 	result = QueueRequest(requestType, request, value, index, length, data,
403 		dataLength, SendRequestCallback, this);
404 	if (result < B_OK) {
405 		mutex_unlock(&fSendRequestLock);
406 		return result;
407 	}
408 
409 	// The sem will be released unconditionally in the callback after the
410 	// result data was filled in. Use a 2 seconds timeout for control transfers.
411 	if (acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 2000000) < B_OK) {
412 		TRACE_ERROR("timeout waiting for queued request to complete\n");
413 
414 		CancelQueuedTransfers(false);
415 
416 		// After the above cancel returns it is guaranteed that the callback
417 		// has been invoked. Therefore we can simply grab that released
418 		// semaphore again to clean up.
419 		acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 0);
420 
421 		if (actualLength)
422 			*actualLength = 0;
423 
424 		mutex_unlock(&fSendRequestLock);
425 		return B_TIMED_OUT;
426 	}
427 
428 	if (actualLength)
429 		*actualLength = fActualLength;
430 
431 	mutex_unlock(&fSendRequestLock);
432 	return fTransferStatus;
433 }
434 
435 
436 void
437 ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data,
438 	size_t actualLength)
439 {
440 	ControlPipe *pipe = (ControlPipe *)cookie;
441 	pipe->fTransferStatus = status;
442 	pipe->fActualLength = actualLength;
443 	release_sem(pipe->fNotifySem);
444 }
445 
446 
447 status_t
448 ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value,
449 	uint16 index, uint16 length, void *data, size_t dataLength,
450 	usb_callback_func callback, void *callbackCookie)
451 {
452 	if (dataLength > 0 && data == NULL)
453 		return B_BAD_VALUE;
454 
455 	usb_request_data *requestData = new(std::nothrow) usb_request_data;
456 	if (!requestData)
457 		return B_NO_MEMORY;
458 
459 	requestData->RequestType = requestType;
460 	requestData->Request = request;
461 	requestData->Value = value;
462 	requestData->Index = index;
463 	requestData->Length = length;
464 
465 	Transfer *transfer = new(std::nothrow) Transfer(this);
466 	if (!transfer) {
467 		delete requestData;
468 		return B_NO_MEMORY;
469 	}
470 
471 	transfer->SetRequestData(requestData);
472 	transfer->SetData((uint8 *)data, dataLength);
473 	transfer->SetCallback(callback, callbackCookie);
474 
475 	status_t result = GetBusManager()->SubmitTransfer(transfer);
476 	if (result < B_OK)
477 		delete transfer;
478 	return result;
479 }
480