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 status_t 169 BulkPipe::QueueBulk(void *data, size_t dataLength, usb_callback_func callback, 170 void *callbackCookie) 171 { 172 if (dataLength > 0 && data == NULL) 173 return B_BAD_VALUE; 174 175 Transfer *transfer = new(std::nothrow) Transfer(this); 176 if (!transfer) 177 return B_NO_MEMORY; 178 179 transfer->SetData((uint8 *)data, dataLength); 180 transfer->SetCallback(callback, callbackCookie); 181 182 status_t result = GetBusManager()->SubmitTransfer(transfer); 183 if (result < B_OK) 184 delete transfer; 185 return result; 186 } 187 188 189 status_t 190 BulkPipe::QueueBulkV(iovec *vector, size_t vectorCount, 191 usb_callback_func callback, void *callbackCookie, bool physical) 192 { 193 if (vectorCount > 0 && vector == NULL) 194 return B_BAD_VALUE; 195 196 Transfer *transfer = new(std::nothrow) Transfer(this); 197 if (!transfer) 198 return B_NO_MEMORY; 199 200 transfer->SetPhysical(physical); 201 transfer->SetVector(vector, vectorCount); 202 transfer->SetCallback(callback, callbackCookie); 203 204 status_t result = GetBusManager()->SubmitTransfer(transfer); 205 if (result < B_OK) 206 delete transfer; 207 return result; 208 } 209 210 211 // 212 // #pragma mark - 213 // 214 215 216 IsochronousPipe::IsochronousPipe(Object *parent) 217 : Pipe(parent), 218 fMaxQueuedPackets(0), 219 fMaxBufferDuration(0), 220 fSampleSize(0) 221 { 222 } 223 224 225 status_t 226 IsochronousPipe::QueueIsochronous(void *data, size_t dataLength, 227 usb_iso_packet_descriptor *packetDesc, uint32 packetCount, 228 uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback, 229 void *callbackCookie) 230 { 231 if ((dataLength > 0 && data == NULL) 232 || (packetCount > 0 && packetDesc == NULL)) 233 return B_BAD_VALUE; 234 235 usb_isochronous_data *isochronousData 236 = new(std::nothrow) usb_isochronous_data; 237 238 if (!isochronousData) 239 return B_NO_MEMORY; 240 241 isochronousData->packet_descriptors = packetDesc; 242 isochronousData->packet_count = packetCount; 243 isochronousData->starting_frame_number = startingFrameNumber; 244 isochronousData->flags = flags; 245 246 Transfer *transfer = new(std::nothrow) Transfer(this); 247 if (!transfer) { 248 delete isochronousData; 249 return B_NO_MEMORY; 250 } 251 252 transfer->SetData((uint8 *)data, dataLength); 253 transfer->SetCallback(callback, callbackCookie); 254 transfer->SetIsochronousData(isochronousData); 255 256 status_t result = GetBusManager()->SubmitTransfer(transfer); 257 if (result < B_OK) 258 delete transfer; 259 return result; 260 } 261 262 263 status_t 264 IsochronousPipe::SetPipePolicy(uint8 maxQueuedPackets, 265 uint16 maxBufferDurationMS, uint16 sampleSize) 266 { 267 if (maxQueuedPackets == fMaxQueuedPackets 268 || maxBufferDurationMS == fMaxBufferDuration 269 || sampleSize == fSampleSize) 270 return B_OK; 271 272 fMaxQueuedPackets = maxQueuedPackets; 273 fMaxBufferDuration = maxBufferDurationMS; 274 fSampleSize = sampleSize; 275 276 GetBusManager()->NotifyPipeChange(this, USB_CHANGE_PIPE_POLICY_CHANGED); 277 return B_OK; 278 } 279 280 281 status_t 282 IsochronousPipe::GetPipePolicy(uint8 *maxQueuedPackets, 283 uint16 *maxBufferDurationMS, uint16 *sampleSize) 284 { 285 if (maxQueuedPackets) 286 *maxQueuedPackets = fMaxQueuedPackets; 287 if (maxBufferDurationMS) 288 *maxBufferDurationMS = fMaxBufferDuration; 289 if (sampleSize) 290 *sampleSize = fSampleSize; 291 return B_OK; 292 } 293 294 295 // 296 // #pragma mark - 297 // 298 299 300 ControlPipe::ControlPipe(Object *parent) 301 : Pipe(parent), 302 fNotifySem(-1) 303 { 304 mutex_init(&fSendRequestLock, "control pipe send request"); 305 } 306 307 308 ControlPipe::~ControlPipe() 309 { 310 if (fNotifySem >= 0) 311 delete_sem(fNotifySem); 312 mutex_lock(&fSendRequestLock); 313 mutex_destroy(&fSendRequestLock); 314 } 315 316 317 status_t 318 ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value, 319 uint16 index, uint16 length, void *data, size_t dataLength, 320 size_t *actualLength) 321 { 322 status_t result = mutex_lock(&fSendRequestLock); 323 if (result != B_OK) 324 return result; 325 326 if (fNotifySem < 0) { 327 fNotifySem = create_sem(0, "usb send request notify"); 328 if (fNotifySem < 0) { 329 mutex_unlock(&fSendRequestLock); 330 return B_NO_MORE_SEMS; 331 } 332 } 333 334 result = QueueRequest(requestType, request, value, index, length, data, 335 dataLength, SendRequestCallback, this); 336 if (result < B_OK) { 337 mutex_unlock(&fSendRequestLock); 338 return result; 339 } 340 341 // The sem will be released unconditionally in the callback after the 342 // result data was filled in. Use a 2 seconds timeout for control transfers. 343 if (acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 2000000) < B_OK) { 344 TRACE_ERROR("timeout waiting for queued request to complete\n"); 345 346 CancelQueuedTransfers(false); 347 348 // After the above cancel returns it is guaranteed that the callback 349 // has been invoked. Therefore we can simply grab that released 350 // semaphore again to clean up. 351 acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 0); 352 353 if (actualLength) 354 *actualLength = 0; 355 356 mutex_unlock(&fSendRequestLock); 357 return B_TIMED_OUT; 358 } 359 360 if (actualLength) 361 *actualLength = fActualLength; 362 363 mutex_unlock(&fSendRequestLock); 364 return fTransferStatus; 365 } 366 367 368 void 369 ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data, 370 size_t actualLength) 371 { 372 ControlPipe *pipe = (ControlPipe *)cookie; 373 pipe->fTransferStatus = status; 374 pipe->fActualLength = actualLength; 375 release_sem(pipe->fNotifySem); 376 } 377 378 379 status_t 380 ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value, 381 uint16 index, uint16 length, void *data, size_t dataLength, 382 usb_callback_func callback, void *callbackCookie) 383 { 384 if (dataLength > 0 && data == NULL) 385 return B_BAD_VALUE; 386 387 usb_request_data *requestData = new(std::nothrow) usb_request_data; 388 if (!requestData) 389 return B_NO_MEMORY; 390 391 requestData->RequestType = requestType; 392 requestData->Request = request; 393 requestData->Value = value; 394 requestData->Index = index; 395 requestData->Length = length; 396 397 Transfer *transfer = new(std::nothrow) Transfer(this); 398 if (!transfer) { 399 delete requestData; 400 return B_NO_MEMORY; 401 } 402 403 transfer->SetRequestData(requestData); 404 transfer->SetData((uint8 *)data, dataLength); 405 transfer->SetCallback(callback, callbackCookie); 406 407 status_t result = GetBusManager()->SubmitTransfer(transfer); 408 if (result < B_OK) 409 delete transfer; 410 return result; 411 } 412