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