1/* 2 * Copyright (c) 1998-2007 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22 23#include "IOFWUserPHYPacketListener.h" 24 25#import <IOKit/firewire/IOFireWireController.h> 26#import <IOKit/firewire/IOFireWireNub.h> 27#import <IOKit/firewire/IOFWPHYPacketListener.h> 28 29//#import <IOKit/firewire/FireLog.h> 30 31// ============================================================ 32// IOFWUserPHYPacketListener methods 33// ============================================================ 34 35OSDefineMetaClassAndStructors( IOFWUserPHYPacketListener, IOFWPHYPacketListener ); 36 37// withUserClient 38// 39// 40 41IOFWUserPHYPacketListener * 42IOFWUserPHYPacketListener::withUserClient( IOFireWireUserClient * inUserClient, UInt32 queue_count ) 43{ 44 IOFWUserPHYPacketListener* result = NULL; 45 46 result = OSTypeAlloc( IOFWUserPHYPacketListener ); 47 if( result && !result->initWithUserClient( inUserClient, queue_count ) ) 48 { 49 result->release(); 50 result = NULL; 51 } 52 53 return result; 54} 55 56// init 57// 58// 59 60bool 61IOFWUserPHYPacketListener::initWithUserClient( IOFireWireUserClient * inUserClient, UInt32 queue_count ) 62{ 63 bool success = true; 64 65 IOFireWireController * control = inUserClient->getOwner()->getController(); 66 if( !IOFWPHYPacketListener::initWithController( control ) ) 67 success = false; 68 69 if( success ) 70 { 71 fUserClient = inUserClient; 72 73 fMaxQueueCount = queue_count; 74 75 // enforce a minimum queue size 76 if( fMaxQueueCount < 2 ) 77 { 78 fMaxQueueCount = 2; 79 } 80 81 fAllocatedQueueCount = 0; 82 83 fElementWaitingCompletion = NULL; 84 85 // get a lock for the packet queue 86 fLock = IOLockAlloc(); 87 if( fLock == NULL ) 88 { 89 success = false; 90 } 91 } 92 93 return success; 94} 95 96// free 97// 98// 99 100void 101IOFWUserPHYPacketListener::free( void ) 102{ 103 destroyAllElements(); 104 105 if( fLock ) 106 { 107 IOLockFree( fLock ); 108 fLock = NULL; 109 } 110 111 IOFWPHYPacketListener::free(); 112} 113 114// exporterCleanup 115// 116// 117 118void 119IOFWUserPHYPacketListener::exporterCleanup( const OSObject * self ) 120{ 121 IOFWUserPHYPacketListener * me = (IOFWUserPHYPacketListener*)self; 122 123 DebugLog("IOFWUserPHYPacketListener::exporterCleanup\n"); 124 125 me->deactivate(); 126} 127 128#pragma mark - 129///////////////////////////////////////////////////////////////////////////////// 130 131// setPacketCallback 132// 133// 134 135IOReturn 136IOFWUserPHYPacketListener::setPacketCallback( OSAsyncReference64 async_ref, 137 mach_vm_address_t callback, 138 io_user_reference_t refCon ) 139{ 140// FireLog( "IOFWUserPHYPacketListener::setPacketCallback - asyncref = 0x%016llx callback = 0x%016llx, refcon = 0x%016llx\n", async_ref[0], callback, refCon ); 141 142 if( callback ) 143 { 144 IOFireWireUserClient::setAsyncReference64( fCallbackAsyncRef, (mach_port_t)async_ref[0], callback, refCon ); 145 } 146 else 147 { 148 fCallbackAsyncRef[0] = 0; 149 } 150 151 return kIOReturnSuccess; 152} 153 154// setSkippedCallback 155// 156// 157 158IOReturn 159IOFWUserPHYPacketListener::setSkippedCallback( OSAsyncReference64 async_ref, 160 mach_vm_address_t callback, 161 io_user_reference_t refCon ) 162{ 163// FireLog( "IOFWUserPHYPacketListener::setSkippedCallback - asyncref = 0x%016llx callback = 0x%016llx, refcon = 0x%016llx\n", async_ref[0], callback, refCon ); 164 165 if( callback ) 166 { 167 IOFireWireUserClient::setAsyncReference64( fSkippedAsyncRef, (mach_port_t)async_ref[0], callback, refCon ); 168 } 169 else 170 { 171 fSkippedAsyncRef[0] = 0; 172 } 173 174 return kIOReturnSuccess; 175} 176 177// processPHYPacket 178// 179// on workloop 180 181void IOFWUserPHYPacketListener::processPHYPacket( UInt32 data1, UInt32 data2 ) 182{ 183 IOLockLock( fLock ); 184 185 PHYRxElement * element = NULL; 186 187// FireLog( "IOFWUserPHYPacketListener::processPHYPacket - 0x%08lx %08lx\n", data1, data2 ); 188 189 // try to get a data element 190 191 // allocate an element, but tell the allocator to reserve an element for skipped packets 192 element = allocateDataElement(); 193 if( element ) 194 { 195 // we've got a data element 196 element->type = kTypeData; 197 element->data1 = data1; 198 element->data2 = data2; 199 200 // tell user space if it's not already busy 201 if( fElementWaitingCompletion == NULL ) 202 { 203 // FireLog( "IOFWUserPHYPacketListener::processPHYPacket - data element - send notification\n" ); 204 205 sendPacketNotification( element ); 206 } 207 else 208 { 209 // FireLog( "IOFWUserPHYPacketListener::processPHYPacket - data element - queue - fElementWaitingCompletion = 0x%08lx\n", fElementWaitingCompletion ); 210 211 // else queue it up 212 addElementToPendingList( element ); 213 } 214 } 215 else 216 { 217 // if we couldn't get a data element we're skipping 218 219 // if we're skipping, but user space isn't working on anything 220 // then we're in trouble 221 FWKLOGASSERT( fElementWaitingCompletion != NULL ); 222 223 // reuse the tail if it's a skip packet 224 if( fPendingListTail && fPendingListTail->type == kTypeSkipped ) 225 { 226 element = fPendingListTail; 227 228 // and it hasn't been sent to the user yet 229 // this should be true because the queue size is always at least 2 and the 230 // commands must be processed by user space sequentially 231 FWKLOGASSERT( fPendingListTail != fElementWaitingCompletion ); 232 233 // bump the count 234 UInt32 count = element->getSkippedCount(); 235 count++; 236 element->setSkippedCount( count ); 237 } 238 else 239 { 240 // else get an element 241 element = allocateElement(); 242 if( element ) 243 { 244 element->type = kTypeSkipped; 245 246 // set our skipped count 247 element->setSkippedCount( 1 ); 248 249 // FireLog( "IOFWUserPHYPacketListener::processPHYPacket - skip element - queue\n" ); 250 251 // queue it up 252 addElementToPendingList( element ); 253 } 254 else 255 { 256 // just drop it I guess 257 IOLog( "FireWire - UserPHYPacketListener out of elements\n" ); 258 } 259 } 260 } 261 262 IOLockUnlock( fLock ); 263} 264 265// clientCommandIsComplete 266// 267// on user thread 268 269void 270IOFWUserPHYPacketListener::clientCommandIsComplete( FWClientCommandID commandID ) 271{ 272 IOLockLock( fLock ); 273 274// FireLog( "IOFWUserPHYPacketListener::clientCommandIsComplete - commandID = 0x%08lx\n", commandID ); 275 276 // verify we're completing the right command 277 if( fElementWaitingCompletion == commandID ) 278 { 279 // we're done with the outstanding element 280 deallocateElement( fElementWaitingCompletion ); 281 282 fElementWaitingCompletion = NULL; 283 284 // FireLog( "IOFWUserPHYPacketListener::clientCommandIsComplete - element = 0x%08lx fElementWaitingCompletion = 0x%08lx\n", commandID, fElementWaitingCompletion ); 285 286 // if we've got another one on the pending list, send it off 287 PHYRxElement * element = fPendingListHead; 288 if( element ) 289 { 290 removeElementFromPendingList( element ); 291 sendPacketNotification( element ); 292 } 293 } 294 295 IOLockUnlock( fLock ); 296} 297 298// sendPacketNotification 299// 300// lock is held 301 302void 303IOFWUserPHYPacketListener::sendPacketNotification( IOFWUserPHYPacketListener::PHYRxElement * element ) 304{ 305 if( fElementWaitingCompletion == NULL ) 306 { 307 if( element->type == kTypeData ) 308 { 309 fElementWaitingCompletion = element; 310 311 io_user_reference_t args[3]; 312 args[0] = (io_user_reference_t)element; // commandID 313 args[1] = element->data1; // data1 314 args[2] = element->data2; // data2 315 316 // FireLog( "IOFWUserPHYPacketListener::sendPacketNotification - kTypeData fElementWaitingCompletion = 0x%08lx\n", fElementWaitingCompletion ); 317 318 // FireLog( "IOFWUserPHYPacketListener::sendPacketNotification - fCallbackAsyncRef[0] = 0x%016llx\n", fCallbackAsyncRef[0] ); 319 320 IOFireWireUserClient::sendAsyncResult64( fCallbackAsyncRef, kIOReturnSuccess, args, 3 ); 321 } 322 else if( element->type == kTypeSkipped ) 323 { 324 fElementWaitingCompletion = element; 325 326 io_user_reference_t args[3]; 327 args[0] = (io_user_reference_t)element; // commandID 328 args[1] = element->getSkippedCount(); // count 329 args[2] = 0; //zzz for some reason I need to send an arg count of 3 for my data to make it to user space 330 //zzz sounds like a kernel bug. pad it for now 331 332 // FireLog( "IOFWUserPHYPacketListener::sendPacketNotification - kTypeSkipped count = %d, fElementWaitingCompletion = 0x%08lx\n", element->getSkippedCount(), (UInt32)fElementWaitingCompletion ); 333 334 // FireLog( "IOFWUserPHYPacketListener::sendPacketNotification - fSkippedAsyncRef[0] = 0x%016llx\n", fSkippedAsyncRef[0] ); 335 336 IOFireWireUserClient::sendAsyncResult64( fSkippedAsyncRef, kIOReturnSuccess, args, 3 ); 337 } 338 } 339} 340 341#pragma mark - 342 343// allocateElement 344// 345// 346 347IOFWUserPHYPacketListener::PHYRxElement * IOFWUserPHYPacketListener::allocateElement( void ) 348{ 349 // 350 // allocate 351 // 352 353 PHYRxElement * element = fFreeListHead; 354 355 if( element == NULL ) 356 { 357 // create elements on demand 358 // make skipped elements up to the threshold 359 if(fAllocatedQueueCount < fMaxQueueCount ) 360 { 361 element = new PHYRxElement; 362 if( element != NULL ) 363 { 364 element->next = NULL; 365 element->prev = NULL; 366 element->type = kTypeNone; 367 element->state = kFreeState; 368 element->data1 = 0; 369 element->data2 = 0; 370 371 // 372 // link it in 373 // 374 375 fFreeListHead = element; 376 fFreeListTail = element; 377 378 fAllocatedQueueCount++; 379 } 380 } 381 } 382 383 if( element != NULL ) 384 { 385 fFreeListHead = element->next; 386 if( fFreeListHead ) 387 { 388 fFreeListHead->prev = NULL; 389 } 390 else 391 { 392 FWKLOGASSERT( fFreeListTail == element ); 393 394 fFreeListTail = NULL; 395 } 396 397 FWKLOGASSERT( element->prev == NULL ); 398 FWKLOGASSERT( element->state == kFreeState ); 399 400 element->next = NULL; 401 element->prev = NULL; 402 element->state = kFreeState; 403 404 DebugLog( "IOFWUserPHYPacketListener::allocateElement - element = %p\n", element ); 405 } 406 407 return element; 408} 409 410// allocateElement 411// 412// 413 414IOFWUserPHYPacketListener::PHYRxElement * IOFWUserPHYPacketListener::allocateDataElement( void ) 415{ 416 // 417 // allocate 418 // 419 420 PHYRxElement * element = fFreeListHead; 421 422 if( element == NULL ) 423 { 424 // create elements on demand 425 // make data elements only if we can make one more for skipped packets 426 427 if( fAllocatedQueueCount < (fMaxQueueCount - 1) ) 428 { 429 element = new PHYRxElement; 430 if( element != NULL ) 431 { 432 element->next = NULL; 433 element->prev = NULL; 434 element->type = kTypeNone; 435 element->state = kFreeState; 436 element->data1 = 0; 437 element->data2 = 0; 438 439 // 440 // link it in 441 // 442 443 fFreeListHead = element; 444 fFreeListTail = element; 445 446 fAllocatedQueueCount++; 447 } 448 } 449 } 450 451 if( element != NULL ) 452 { 453 // we cannot allocate the last element if it is a data element 454 if( (element != fFreeListTail) || // if it's not the tail 455 (fAllocatedQueueCount < (fMaxQueueCount - 1)) ) // or we can allocate more 456 { 457 fFreeListHead = element->next; 458 if( fFreeListHead ) 459 { 460 fFreeListHead->prev = NULL; 461 } 462 else 463 { 464 FWKLOGASSERT( fFreeListTail == element ); 465 466 fFreeListTail = NULL; 467 } 468 469 FWKLOGASSERT( element->prev == NULL ); 470 FWKLOGASSERT( element->state == kFreeState ); 471 472 element->next = NULL; 473 element->prev = NULL; 474 element->state = kFreeState; 475 476 DebugLog( "IOFWUserPHYPacketListener::allocateDataElement - element = %p\n", element ); 477 } 478 else 479 { 480 element = NULL; 481 } 482 } 483 484 return element; 485} 486 487 488// deallocateElement 489// 490// 491 492void IOFWUserPHYPacketListener::deallocateElement( PHYRxElement * element ) 493{ 494 DebugLog( "IOFWUserPHYPacketListener::deallocateElement - element = %p\n", element ); 495 496 element->next = NULL; 497 element->prev = fFreeListTail; 498 element->state = kFreeState; 499 500 if( fFreeListTail ) 501 { 502 fFreeListTail->next = element; 503 } 504 else 505 { 506 FWKLOGASSERT( fFreeListHead == NULL ) 507 508 fFreeListHead = element; 509 } 510 511 fFreeListTail = element; 512 513 FWKLOGASSERT( fFreeListHead != NULL ); 514 FWKLOGASSERT( fFreeListTail != NULL ); 515} 516 517#pragma mark - 518 519// destroyAllElements 520// 521// 522 523void IOFWUserPHYPacketListener::destroyAllElements( void ) 524{ 525 DebugLog(( "IOFWUserPHYPacketListener::destroyAllElements\n" )); 526 527 // 528 // return all elements to the free pool 529 // 530 531 { 532 PHYRxElement * element = fPendingListHead; 533 534 while( element ) 535 { 536 PHYRxElement * next_element = element->next; 537 538 removeElementFromPendingList( element ); 539 540 deallocateElement( element ); 541 542 element = next_element; 543 } 544 545 FWKLOGASSERT( fPendingListHead == NULL ); 546 fPendingListHead = NULL; // should already be NULL 547 548 FWKLOGASSERT( fPendingListTail == NULL ); 549 fPendingListTail = NULL; // should already be NULL 550 } 551 552 // 553 // delete all elements in free pool 554 // 555 556 { 557 PHYRxElement * element = fFreeListHead; 558 while( element ) 559 { 560 PHYRxElement * next_element = element->next; 561 562 delete element; 563 564 element = next_element; 565 } 566 567 fFreeListHead = 0; 568 fFreeListTail = 0; 569 } 570} 571 572#pragma mark - 573 574// addElementToPendingList 575// 576// 577 578void IOFWUserPHYPacketListener::addElementToPendingList( PHYRxElement * element ) 579{ 580 DebugLog( "IOFWUserPHYPacketListener::addElementToPendingList - element = %p\n", element ); 581 582 // pending should only be entered from the free state 583 584 FWKLOGASSERT( element != NULL ); 585 FWKLOGASSERT( element->next == NULL ); 586 FWKLOGASSERT( element->state == kFreeState ); 587 588 element->next = NULL; 589 element->prev = fPendingListTail; 590 element->state = kPendingState; 591 592 if( fPendingListTail ) 593 { 594 fPendingListTail->next = element; 595 } 596 else 597 { 598 FWKLOGASSERT( fPendingListHead == NULL ); 599 600 fPendingListHead = element; 601 } 602 603 fPendingListTail = element; 604 605 FWKLOGASSERT( fPendingListHead != NULL ); 606 FWKLOGASSERT( fPendingListTail != NULL ); 607 608} 609 610// removeElementFromPendingList 611// 612// 613 614void IOFWUserPHYPacketListener::removeElementFromPendingList( PHYRxElement * element ) 615{ 616 DebugLog( "IOFWUserPHYPacketListener::removeElementFromPendingList - element = %p\n", element ); 617 618 // element on the pending list should not be in the free state 619 620 FWKLOGASSERT( element->state != kFreeState ); 621 622 // remove from pending list 623 624 // 625 // handle head / next ptr 626 // 627 628 if( fPendingListHead == element ) 629 { 630 FWKLOGASSERT( element->prev == NULL ); 631 632 fPendingListHead = element->next; 633 if( fPendingListHead != NULL ) 634 { 635 fPendingListHead->prev = NULL; 636 } 637 } 638 else 639 { 640 FWPANICASSERT( element->prev != NULL ); 641 642 element->prev->next = element->next; 643 } 644 645 // 646 // handle tail / previous ptr 647 // 648 649 if( fPendingListTail == element ) 650 { 651 FWKLOGASSERT( element->next == NULL ); 652 653 fPendingListTail = element->prev; 654 if( fPendingListTail != NULL ) 655 { 656 fPendingListTail->prev = NULL; 657 } 658 } 659 else 660 { 661 FWPANICASSERT( element->next != NULL ); 662 663 element->next->prev = element->prev; 664 } 665 666 // 667 // reset link ptrs 668 // 669 670 element->next = NULL; 671 element->prev = NULL; 672} 673