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