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 13Pipe::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 22Pipe::~Pipe() 23{ 24 CancelQueuedTransfers(true); 25 GetBusManager()->NotifyPipeChange(this, USB_CHANGE_DESTROYED); 26} 27 28 29void 30Pipe::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 47void 48Pipe::SetHubInfo(int8 address, uint8 port) 49{ 50 fHubAddress = address; 51 fHubPort = port; 52} 53 54 55status_t 56Pipe::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 63status_t 64Pipe::CancelQueuedTransfers(bool force) 65{ 66 return GetBusManager()->CancelQueuedTransfers(this, force); 67} 68 69 70status_t 71Pipe::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 87status_t 88Pipe::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 108status_t 109Pipe::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 130InterruptPipe::InterruptPipe(Object *parent) 131 : Pipe(parent) 132{ 133} 134 135 136status_t 137InterruptPipe::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 162BulkPipe::BulkPipe(Object *parent) 163 : Pipe(parent) 164{ 165} 166 167 168status_t 169BulkPipe::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 189status_t 190BulkPipe::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 216IsochronousPipe::IsochronousPipe(Object *parent) 217 : Pipe(parent), 218 fMaxQueuedPackets(0), 219 fMaxBufferDuration(0), 220 fSampleSize(0) 221{ 222} 223 224 225status_t 226IsochronousPipe::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 263status_t 264IsochronousPipe::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 281status_t 282IsochronousPipe::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 300ControlPipe::ControlPipe(Object *parent) 301 : Pipe(parent), 302 fNotifySem(-1) 303{ 304 mutex_init(&fSendRequestLock, "control pipe send request"); 305} 306 307 308ControlPipe::~ControlPipe() 309{ 310 if (fNotifySem >= 0) 311 delete_sem(fNotifySem); 312 mutex_lock(&fSendRequestLock); 313 mutex_destroy(&fSendRequestLock); 314} 315 316 317status_t 318ControlPipe::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 368void 369ControlPipe::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 379status_t 380ControlPipe::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