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
Pipe(Object * parent)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
~Pipe()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
InitCommon(int8 deviceAddress,uint8 endpointAddress,usb_speed speed,pipeDirection direction,size_t maxPacketSize,uint8 interval,int8 hubAddress,uint8 hubPort)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
InitSuperSpeed(uint8 maxBurst,uint16 bytesPerInterval)54 Pipe::InitSuperSpeed(uint8 maxBurst, uint16 bytesPerInterval)
55 {
56 fMaxBurst = maxBurst;
57 fBytesPerInterval = bytesPerInterval;
58 }
59
60
61 void
SetHubInfo(int8 address,uint8 port)62 Pipe::SetHubInfo(int8 address, uint8 port)
63 {
64 fHubAddress = address;
65 fHubPort = port;
66 }
67
68
69 status_t
SubmitTransfer(Transfer * transfer)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
CancelQueuedTransfers(bool force)81 Pipe::CancelQueuedTransfers(bool force)
82 {
83 return GetBusManager()->CancelQueuedTransfers(this, force);
84 }
85
86
87 status_t
SetFeature(uint16 selector)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
ClearFeature(uint16 selector)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
GetStatus(uint16 * status)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
InterruptPipe(Object * parent)147 InterruptPipe::InterruptPipe(Object *parent)
148 : Pipe(parent)
149 {
150 }
151
152
153 status_t
QueueInterrupt(void * data,size_t dataLength,usb_callback_func callback,void * callbackCookie)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
BulkPipe(Object * parent)179 BulkPipe::BulkPipe(Object *parent)
180 : Pipe(parent)
181 {
182 }
183
184
185 void
InitCommon(int8 deviceAddress,uint8 endpointAddress,usb_speed speed,pipeDirection direction,size_t maxPacketSize,uint8 interval,int8 hubAddress,uint8 hubPort)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
QueueBulk(void * data,size_t dataLength,usb_callback_func callback,void * callbackCookie)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
QueueBulkV(iovec * vector,size_t vectorCount,usb_callback_func callback,void * callbackCookie)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
QueueBulkV(physical_entry * vector,size_t vectorCount,usb_callback_func callback,void * callbackCookie)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
IsochronousPipe(Object * parent)277 IsochronousPipe::IsochronousPipe(Object *parent)
278 : Pipe(parent),
279 fMaxQueuedPackets(0),
280 fMaxBufferDuration(0),
281 fSampleSize(0)
282 {
283 }
284
285
286 status_t
QueueIsochronous(void * data,size_t dataLength,usb_iso_packet_descriptor * packetDesc,uint32 packetCount,uint32 * startingFrameNumber,uint32 flags,usb_callback_func callback,void * callbackCookie)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
SetPipePolicy(uint8 maxQueuedPackets,uint16 maxBufferDurationMS,uint16 sampleSize)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
GetPipePolicy(uint8 * maxQueuedPackets,uint16 * maxBufferDurationMS,uint16 * sampleSize)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
ControlPipe(Object * parent)366 ControlPipe::ControlPipe(Object *parent)
367 : Pipe(parent),
368 fNotifySem(-1)
369 {
370 mutex_init(&fSendRequestLock, "control pipe send request");
371 }
372
373
~ControlPipe()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
InitCommon(int8 deviceAddress,uint8 endpointAddress,usb_speed speed,pipeDirection direction,size_t maxPacketSize,uint8 interval,int8 hubAddress,uint8 hubPort)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
SendRequest(uint8 requestType,uint8 request,uint16 value,uint16 index,uint16 length,void * data,size_t dataLength,size_t * actualLength)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
SendRequestCallback(void * cookie,status_t status,void * data,size_t actualLength)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
QueueRequest(uint8 requestType,uint8 request,uint16 value,uint16 index,uint16 length,void * data,size_t dataLength,usb_callback_func callback,void * callbackCookie)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
CancelQueuedTransfers(bool force)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