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 return ((Device *)Parent())->DefaultPipe()->SendRequest( 73 USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT, 74 USB_REQUEST_SET_FEATURE, 75 selector, 76 fEndpointAddress, 77 0, 78 NULL, 79 0, 80 NULL); 81 } 82 83 84 status_t 85 Pipe::ClearFeature(uint16 selector) 86 { 87 // clearing a stalled condition resets the data toggle 88 if (selector == USB_FEATURE_ENDPOINT_HALT) 89 SetDataToggle(false); 90 91 return ((Device *)Parent())->DefaultPipe()->SendRequest( 92 USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT, 93 USB_REQUEST_CLEAR_FEATURE, 94 selector, 95 fEndpointAddress, 96 0, 97 NULL, 98 0, 99 NULL); 100 } 101 102 103 status_t 104 Pipe::GetStatus(uint16 *status) 105 { 106 return ((Device *)Parent())->DefaultPipe()->SendRequest( 107 USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_IN, 108 USB_REQUEST_GET_STATUS, 109 0, 110 fEndpointAddress, 111 2, 112 (void *)status, 113 2, 114 NULL); 115 } 116 117 118 // 119 // #pragma mark - 120 // 121 122 123 InterruptPipe::InterruptPipe(Object *parent) 124 : Pipe(parent) 125 { 126 } 127 128 129 status_t 130 InterruptPipe::QueueInterrupt(void *data, size_t dataLength, 131 usb_callback_func callback, void *callbackCookie) 132 { 133 if (dataLength > 0 && data == NULL) 134 return B_BAD_VALUE; 135 136 Transfer *transfer = new(std::nothrow) Transfer(this); 137 if (!transfer) 138 return B_NO_MEMORY; 139 140 transfer->SetData((uint8 *)data, dataLength); 141 transfer->SetCallback(callback, callbackCookie); 142 143 status_t result = GetBusManager()->SubmitTransfer(transfer); 144 if (result < B_OK) 145 delete transfer; 146 return result; 147 } 148 149 150 // 151 // #pragma mark - 152 // 153 154 155 BulkPipe::BulkPipe(Object *parent) 156 : Pipe(parent) 157 { 158 } 159 160 161 status_t 162 BulkPipe::QueueBulk(void *data, size_t dataLength, usb_callback_func callback, 163 void *callbackCookie) 164 { 165 if (dataLength > 0 && data == NULL) 166 return B_BAD_VALUE; 167 168 Transfer *transfer = new(std::nothrow) Transfer(this); 169 if (!transfer) 170 return B_NO_MEMORY; 171 172 transfer->SetData((uint8 *)data, dataLength); 173 transfer->SetCallback(callback, callbackCookie); 174 175 status_t result = GetBusManager()->SubmitTransfer(transfer); 176 if (result < B_OK) 177 delete transfer; 178 return result; 179 } 180 181 182 status_t 183 BulkPipe::QueueBulkV(iovec *vector, size_t vectorCount, 184 usb_callback_func callback, void *callbackCookie) 185 { 186 if (vectorCount > 0 && vector == 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->SetVector(vector, vectorCount); 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 // 204 // #pragma mark - 205 // 206 207 208 IsochronousPipe::IsochronousPipe(Object *parent) 209 : Pipe(parent), 210 fMaxQueuedPackets(0), 211 fMaxBufferDuration(0), 212 fSampleSize(0) 213 { 214 } 215 216 217 status_t 218 IsochronousPipe::QueueIsochronous(void *data, size_t dataLength, 219 usb_iso_packet_descriptor *packetDesc, uint32 packetCount, 220 uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback, 221 void *callbackCookie) 222 { 223 if ((dataLength > 0 && data == NULL) 224 || (packetCount > 0 && packetDesc == NULL)) 225 return B_BAD_VALUE; 226 227 usb_isochronous_data *isochronousData 228 = new(std::nothrow) usb_isochronous_data; 229 230 if (!isochronousData) 231 return B_NO_MEMORY; 232 233 isochronousData->packet_descriptors = packetDesc; 234 isochronousData->packet_count = packetCount; 235 isochronousData->starting_frame_number = startingFrameNumber; 236 isochronousData->flags = flags; 237 238 Transfer *transfer = new(std::nothrow) Transfer(this); 239 if (!transfer) { 240 delete isochronousData; 241 return B_NO_MEMORY; 242 } 243 244 transfer->SetData((uint8 *)data, dataLength); 245 transfer->SetCallback(callback, callbackCookie); 246 transfer->SetIsochronousData(isochronousData); 247 248 status_t result = GetBusManager()->SubmitTransfer(transfer); 249 if (result < B_OK) 250 delete transfer; 251 return result; 252 } 253 254 255 status_t 256 IsochronousPipe::SetPipePolicy(uint8 maxQueuedPackets, 257 uint16 maxBufferDurationMS, uint16 sampleSize) 258 { 259 if (maxQueuedPackets == fMaxQueuedPackets 260 || maxBufferDurationMS == fMaxBufferDuration 261 || sampleSize == fSampleSize) 262 return B_OK; 263 264 fMaxQueuedPackets = maxQueuedPackets; 265 fMaxBufferDuration = maxBufferDurationMS; 266 fSampleSize = sampleSize; 267 268 GetBusManager()->NotifyPipeChange(this, USB_CHANGE_PIPE_POLICY_CHANGED); 269 return B_OK; 270 } 271 272 273 status_t 274 IsochronousPipe::GetPipePolicy(uint8 *maxQueuedPackets, 275 uint16 *maxBufferDurationMS, uint16 *sampleSize) 276 { 277 if (maxQueuedPackets) 278 *maxQueuedPackets = fMaxQueuedPackets; 279 if (maxBufferDurationMS) 280 *maxBufferDurationMS = fMaxBufferDuration; 281 if (sampleSize) 282 *sampleSize = fSampleSize; 283 return B_OK; 284 } 285 286 287 // 288 // #pragma mark - 289 // 290 291 292 typedef struct transfer_result_data_s { 293 sem_id notify_sem; 294 status_t status; 295 size_t actual_length; 296 } transfer_result_data; 297 298 299 ControlPipe::ControlPipe(Object *parent) 300 : Pipe(parent) 301 { 302 } 303 304 305 status_t 306 ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value, 307 uint16 index, uint16 length, void *data, size_t dataLength, 308 size_t *actualLength) 309 { 310 transfer_result_data *transferResult 311 = new(std::nothrow) transfer_result_data; 312 313 transferResult->notify_sem = create_sem(0, "usb send request notify"); 314 if (transferResult->notify_sem < B_OK) 315 return B_NO_MORE_SEMS; 316 317 status_t result = QueueRequest(requestType, request, value, index, length, 318 data, dataLength, SendRequestCallback, transferResult); 319 if (result < B_OK) { 320 delete_sem(transferResult->notify_sem); 321 return result; 322 } 323 324 // the sem will be released in the callback after the result data was 325 // filled into the provided struct. use a 1 second timeout to avoid 326 // hanging applications. 327 if (acquire_sem_etc(transferResult->notify_sem, 1, B_RELATIVE_TIMEOUT, 1000000) < B_OK) { 328 TRACE_ERROR(("USB ControlPipe: timeout waiting for queued request to complete\n")); 329 330 delete_sem(transferResult->notify_sem); 331 if (actualLength) 332 *actualLength = 0; 333 334 CancelQueuedTransfers(false); 335 return B_TIMED_OUT; 336 } 337 338 delete_sem(transferResult->notify_sem); 339 if (actualLength) 340 *actualLength = transferResult->actual_length; 341 342 result = transferResult->status; 343 delete transferResult; 344 return result; 345 } 346 347 348 void 349 ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data, 350 size_t actualLength) 351 { 352 transfer_result_data *transferResult = (transfer_result_data *)cookie; 353 transferResult->status = status; 354 transferResult->actual_length = actualLength; 355 if (release_sem(transferResult->notify_sem) < B_OK) { 356 // the request has timed out already - cleanup after us 357 delete transferResult; 358 } 359 } 360 361 362 status_t 363 ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value, 364 uint16 index, uint16 length, void *data, size_t dataLength, 365 usb_callback_func callback, void *callbackCookie) 366 { 367 if (dataLength > 0 && data == NULL) 368 return B_BAD_VALUE; 369 370 usb_request_data *requestData = new(std::nothrow) usb_request_data; 371 if (!requestData) 372 return B_NO_MEMORY; 373 374 requestData->RequestType = requestType; 375 requestData->Request = request; 376 requestData->Value = value; 377 requestData->Index = index; 378 requestData->Length = length; 379 380 Transfer *transfer = new(std::nothrow) Transfer(this); 381 if (!transfer) { 382 delete requestData; 383 return B_NO_MEMORY; 384 } 385 386 transfer->SetRequestData(requestData); 387 transfer->SetData((uint8 *)data, dataLength); 388 transfer->SetCallback(callback, callbackCookie); 389 390 status_t result = GetBusManager()->SubmitTransfer(transfer); 391 if (result < B_OK) 392 delete transfer; 393 return result; 394 } 395