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