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