1/* 2 * Copyright (c) 1998-2001 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#include <IOKit/avc/IOFireWireAVCCommand.h> 23#include <IOKit/avc/IOFireWireAVCConsts.h> 24 25#include <IOKit/firewire/IOFireWireNub.h> 26#include <IOKit/firewire/IOFireWireController.h> 27#include <IOKit/IOSyncer.h> 28 29#if FIRELOG 30#import <IOKit/firewire/FireLog.h> 31#define FIRELOG_MSG(x) FireLog x 32#else 33#define FIRELOG_MSG(x) do {} while (0) 34#endif 35 36#define kInterimTimeout 10000000 37 38class IOFireWireAVCCommandInGen : public IOFireWireAVCCommand 39{ 40 OSDeclareDefaultStructors(IOFireWireAVCCommandInGen) 41 42protected: 43 UInt32 fDummy; 44 45 virtual IOReturn complete(IOReturn status); 46 47public: 48 virtual bool init(IOFireWireNub *device, UInt32 generation, const UInt8 * command, UInt32 cmdLen, 49 UInt8 * response, UInt32 * responseLen); 50 virtual IOReturn reinit(IOFireWireNub *device, UInt32 generation, const UInt8 * command, UInt32 cmdLen, 51 UInt8 * response, UInt32 * responseLen); 52 53private: 54 OSMetaClassDeclareReservedUnused(IOFireWireAVCCommandInGen, 0); 55 OSMetaClassDeclareReservedUnused(IOFireWireAVCCommandInGen, 1); 56 OSMetaClassDeclareReservedUnused(IOFireWireAVCCommandInGen, 2); 57 OSMetaClassDeclareReservedUnused(IOFireWireAVCCommandInGen, 3); 58}; 59 60 61OSDefineMetaClassAndStructors(IOFireWireAVCCommand, IOFWCommand) 62//OSMetaClassDefineReservedUnused(IOFireWireAVCCommand, 0); 63OSMetaClassDefineReservedUnused(IOFireWireAVCCommand, 1); 64OSMetaClassDefineReservedUnused(IOFireWireAVCCommand, 2); 65OSMetaClassDefineReservedUnused(IOFireWireAVCCommand, 3); 66 67/* 68 Possible states for an AVCCommand: 69 70 Not submitted: fTimeout = 0, fWriteNodeID = kFWBadNodeID, fStatus != Busy(). 71 Write submitted: fTimeout = 0, fWriteNodeID = kFWBadNodeID, fStatus = kIOReturnBusy 72 Write complete: fTimeout = 250000, fWriteNodeID = device node, fStatus = kIOReturnBusy 73 Interim received: fTimeout = kInterimTimeout, fWriteNodeID = device node, fStatus = kIOReturnBusy 74 Complete: fStatus != Busy() 75*/ 76 77void IOFireWireAVCCommand::writeDone(void *refcon, IOReturn status, IOFireWireNub *device, IOFWCommand *fwCmd) 78{ 79 IOFireWireAVCCommand *me = (IOFireWireAVCCommand *)refcon; 80 81 FIRELOG_MSG(("IOFireWireAVCCommand::writeDone (this=0x%08X)\n",me)); 82 83 if(status == kIOReturnSuccess) { 84 // Store current node and generation 85 if(device) 86 device->getNodeIDGeneration(me->fWriteGen, me->fWriteNodeID); 87 88 // Start timer for command response 89 me->fTimeout = 250000; 90 me->updateTimer(); 91 } 92 else { 93 IOLog("Write for %p done, status %x\n", refcon, status); 94 me->complete(status); 95 } 96} 97 98UInt32 IOFireWireAVCCommand::handleResponseWithSimpleMatching(UInt16 nodeID, UInt32 len, const void *buf) 99{ 100 bypassRobustCommandResponseMatching = true; 101 return handleResponse(nodeID,len,buf); 102} 103 104 105UInt32 IOFireWireAVCCommand::handleResponse(UInt16 nodeID, UInt32 len, const void *buf) 106{ 107 FIRELOG_MSG(("IOFireWireAVCCommand::handleResponse (this=0x%08X)\n",this)); 108 109 const UInt8 *p; 110 UInt32 i; 111 UInt32 res = kFWResponseAddressError; 112 UInt8 commandAddress; 113 UInt8 commandOpcode; 114 115 // copy the status bytes from fPseudoSpace if this is for us 116 // Don't need to check generation because the command is cancelled when a bus reset happens. 117 // fTimeout is only non-zero if the write was successful. 118 if(fTimeout && nodeID == fWriteNodeID) { 119 120 // Add additional validation, to make sure this AVCResponse packet 121 // seems sensible for matching on our outstanding AVCCommand. 122 // Check the unit/subunit address, and the opcode. Note, If subunit is tape, 123 // and AVCCommand opcode is "Transport State", the AVCResponse packet has the 124 // opcode overwritten with either the Play, Wind, Record, or LoadMedium opcode. 125 if (fCommand) 126 { 127 p = (UInt8*) fCommand; 128 commandAddress = p[kAVCAddress]; 129 commandOpcode = p[kAVCOpcode]; 130 } 131 else 132 { 133 UInt8 commandBytes[4]; 134 IOByteCount cmdByteCount = fMem->readBytes(0,commandBytes,4); 135 if (cmdByteCount != 4) 136 return kFWResponseAddressError; 137 commandAddress = commandBytes[kAVCAddress]; 138 commandOpcode = commandBytes[kAVCOpcode]; 139 } 140 141 p = (const UInt8 *)buf; 142 143 // Unless bypass flag is set, do the robust command/response matching 144 if (!bypassRobustCommandResponseMatching) 145 { 146 //IOLog("Doing robust response matching for IOFireWireAVCCommand\n"); 147 148 if (p[kAVCAddress] != commandAddress) 149 return kFWResponseAddressError; 150 151 if (((commandAddress & 0xF8) == 0x20) && (commandOpcode == 0xD0)) 152 { 153 if ( (p[kAVCOpcode] != 0xD0) && (p[kAVCOpcode] != 0xC1) && (p[kAVCOpcode] != 0xC2) && (p[kAVCOpcode] != 0xC3) && (p[kAVCOpcode] != 0xC4)) 154 return kFWResponseAddressError; 155 } 156 else 157 { 158 if (p[kAVCOpcode] != commandOpcode) 159 return kFWResponseAddressError; 160 } 161 } 162 else 163 { 164 //IOLog("Bypassing robust response matching for IOFireWireAVCCommand\n"); 165 } 166 167 if(p[kAVCCommandResponse] == kAVCInterimStatus) { 168 fTimeout = kInterimTimeout; // We could wait for ever after the Interim, 10 seconds seems long enough 169 updateTimer(); 170 } 171 else { 172 if(len > *fResponseLen) 173 len = *fResponseLen; 174 for (i = 0 ; i < len ; i++) 175 fResponse[i] = *p++; 176 *fResponseLen = len; 177 // Make sure we don't accept another response, we're done! 178 fTimeout = 0; 179 complete(kIOReturnSuccess); 180 } 181 res = kFWResponseComplete; 182 } 183 else { 184 //IOLog("%p: ------ Write not for me ----------\n", this); 185 //IOLog("nodeID: %d-%d\n", nodeID, fWriteNodeID); 186 //IOLog("Data: %x len %d\n", (unsigned int) *(const UInt32 *)buf, (int)len); 187 } 188 return res; 189} 190 191void IOFireWireAVCCommand::free() 192{ 193 FIRELOG_MSG(("IOFireWireAVCCommand::free (this=0x%08X)\n",this)); 194 195 if( fIOFireWireAVCCommandExpansion ) 196 fIOFireWireAVCCommandExpansion->fStarted = false; 197 198 if( Busy() ) 199 { 200 cancel(kIOReturnAborted); 201 } 202 203 if( fWriteCmd ) 204 { 205 if( fWriteCmd->Busy() ) 206 { 207 fWriteCmd->cancel(kIOReturnAborted); 208 } 209 fWriteCmd->release(); 210 fWriteCmd = NULL; 211 } 212 213 if( fMem ) 214 { 215 fMem->release(); 216 fMem = NULL; 217 } 218 219 IOFree ( fIOFireWireAVCCommandExpansion, sizeof(ExpansionData) ); 220 fIOFireWireAVCCommandExpansion = NULL; 221 222 IOFWCommand::free(); 223} 224 225IOReturn IOFireWireAVCCommand::complete(IOReturn state) 226{ 227 FIRELOG_MSG(("IOFireWireAVCCommand::complete (this=0x%08X)\n",this)); 228 229 if(state == kIOFireWireBusReset && fWriteNodeID == kFWBadNodeID) 230 state = kIOReturnOffline; // Write was 'retry on bus reset', so device must have gone. 231 232 state = IOFWCommand::complete(state); 233 // If write was sent successfully but response wasn't received, or 234 // Command was written, bus reset happened before response was sent. 235 // - try again 236 237 if( (state == kIOReturnTimeout && fTimeout != 0 && fCurRetries--) || 238 (state == kIOFireWireBusReset)) { 239 FWAddress addr; 240 241 // setup quad write 242 addr.addressHi = kCSRRegisterSpaceBaseAddressHi; 243 addr.addressLo = kFCPCommandAddress; 244 if(fMem) 245 ((IOFWWriteCommand *)fWriteCmd)->reinit(addr, fMem, writeDone, this); 246 else 247 ((IOFWWriteQuadCommand *)fWriteCmd)->reinit(addr,(UInt32 *)fCommand, fCmdLen/4, writeDone, this); 248 fTimeout = 0; 249 return fStatus = startExecution(); 250 } 251 252 if( fSync ) 253 { 254 if( fSyncWakeup && (not fIOFireWireAVCCommandExpansion->fSyncWakeupSignaled) ) 255 { 256 fIOFireWireAVCCommandExpansion->fSyncWakeupSignaled = true; 257 fSyncWakeup->signal(state); 258 } 259 } 260 //else if(fComplete) 261 //(*fComplete)(fRefCon, state, fControl, this); 262 return state; 263} 264 265IOReturn IOFireWireAVCCommand::submit(bool queue) 266{ 267 fIOFireWireAVCCommandExpansion->fSyncWakeupSignaled = false; 268 269 return IOFWCommand::submit(queue); 270} 271 272IOReturn IOFireWireAVCCommand::execute() 273{ 274 FIRELOG_MSG(("IOFireWireAVCCommand::execute (this=0x%08X)\n",this)); 275 276 if( fIOFireWireAVCCommandExpansion->fStarted == false ) 277 { 278 return kIOReturnOffline; 279 } 280 281 fStatus = kIOReturnBusy; 282 fWriteCmd->submit(); 283 return fStatus; 284} 285 286IOReturn IOFireWireAVCCommand::resetInterimTimeout() 287{ 288 FIRELOG_MSG(("IOFireWireAVCCommand::resetInterimTimeout (this=0x%08X)\n",this)); 289 290 // Reinitialize the timeout if we're waiting for the final response after an interim. 291 // Must check internal state with FireWire command gate closed. 292 fControl->closeGate(); 293 if(fTimeout == kInterimTimeout && fStatus == kIOReturnBusy) 294 updateTimer(); 295 fControl->openGate(); 296 297 return kIOReturnSuccess; 298} 299 300bool IOFireWireAVCCommand::init(IOFireWireNub *device, const UInt8 * command, UInt32 cmdLen, 301 UInt8 * response, UInt32 * responseLen) 302{ 303 FIRELOG_MSG(("IOFireWireAVCCommand::init (this=0x%08X)\n",this)); 304 305 FWAddress addr; 306 307 // setup quad write 308 addr.addressHi = kCSRRegisterSpaceBaseAddressHi; 309 addr.addressLo = kFCPCommandAddress; 310 311 IOFWCommand::initWithController(device->getController()); 312 // Start out without timeout, update when command is accepted by device 313 fTimeout = 0; 314 fSync = true; 315 fCancelOnReset = true; 316 fResponse = response; 317 fResponseLen = responseLen; 318 fWriteNodeID = kFWBadNodeID; 319 fMaxRetries = 4; 320 fCurRetries = fMaxRetries; 321 fCmdLen = cmdLen; 322 bypassRobustCommandResponseMatching = false; 323 324 // create/clear expansion data 325 fIOFireWireAVCCommandExpansion = (ExpansionData*) IOMalloc( sizeof(ExpansionData) ); 326 if( fIOFireWireAVCCommandExpansion == NULL ) 327 return false; 328 else 329 bzero( fIOFireWireAVCCommandExpansion, sizeof(ExpansionData) ); 330 331 // create command 332 if(cmdLen == 4 || cmdLen == 8) { 333 fMem = NULL; 334 fCommand = command; 335 fWriteCmd = device->createWriteQuadCommand(addr,(UInt32 *)command, cmdLen/4, writeDone, this); 336 if(!fWriteCmd) { 337 return false; 338 } 339 } 340 else { 341 fCommand = NULL; 342 fMem = IOMemoryDescriptor::withAddress((void *)command, cmdLen, 343 kIODirectionOutIn); 344 if(!fMem) { 345 return false; 346 } 347 348 IOReturn err = fMem->prepare(); 349 if( err != kIOReturnSuccess ) 350 { 351 fMem->release(); 352 return false; 353 } 354 355 fWriteCmd = device->createWriteCommand(addr, fMem, writeDone, this); 356 if(!fWriteCmd) { 357 return false; 358 } 359 } 360 361 fIOFireWireAVCCommandExpansion->fStarted = true; 362 fIOFireWireAVCCommandExpansion->fSyncWakeupSignaled = false; 363 364 return true; 365} 366 367IOReturn IOFireWireAVCCommand::reinit(IOFireWireNub *device, const UInt8 * command, UInt32 cmdLen, 368 UInt8 * response, UInt32 * responseLen) 369{ 370 FIRELOG_MSG(("IOFireWireAVCCommand::reinit (this=0x%08X)\n",this)); 371 372 if(Busy()) 373 return fStatus; 374 375 if(fMem) { 376 fMem->release(); 377 fMem = NULL; 378 } 379 380 if(fWriteCmd) { 381 fWriteCmd->release(); 382 fWriteCmd = NULL; 383 } 384 385 if(init(device, command, cmdLen, response, responseLen)) 386 return kIOReturnSuccess; 387 else 388 return kIOReturnNoMemory; 389} 390 391IOFireWireAVCCommand * 392IOFireWireAVCCommand::withNub(IOFireWireNub *device, const UInt8 * command, UInt32 cmdLen, 393 UInt8 * response, UInt32 * responseLen) 394{ 395 IOFireWireAVCCommand *me = new IOFireWireAVCCommand; 396 397 FIRELOG_MSG(("IOFireWireAVCCommand::withNub (this=0x%08X)\n",me)); 398 399 if(me) { 400 if(!me->init(device, command, cmdLen, response, responseLen)) { 401 me->release(); 402 me = NULL; 403 } 404 } 405 return me; 406} 407 408IOFireWireAVCCommand * 409IOFireWireAVCCommand::withNub(IOFireWireNub *device, UInt32 generation, 410 const UInt8 * command, UInt32 cmdLen, UInt8 * response, UInt32 * responseLen) 411{ 412 413 IOFireWireAVCCommandInGen *me = new IOFireWireAVCCommandInGen; 414 415 FIRELOG_MSG(("IOFireWireAVCCommand::withNub (this=0x%08X)\n",me)); 416 417 418 if(me) { 419 if(!me->init(device, generation, command, cmdLen, response, responseLen)) { 420 me->release(); 421 me = NULL; 422 } 423 } 424 return me; 425} 426 427/* --------------- IOFireWireAVCCommandInGen -------------------- */ 428OSDefineMetaClassAndStructors(IOFireWireAVCCommandInGen, IOFireWireAVCCommand) 429OSMetaClassDefineReservedUnused(IOFireWireAVCCommandInGen, 0); 430OSMetaClassDefineReservedUnused(IOFireWireAVCCommandInGen, 1); 431OSMetaClassDefineReservedUnused(IOFireWireAVCCommandInGen, 2); 432OSMetaClassDefineReservedUnused(IOFireWireAVCCommandInGen, 3); 433 434 435IOReturn IOFireWireAVCCommandInGen::complete(IOReturn state) 436{ 437 FIRELOG_MSG(("IOFireWireAVCCommandInGen::complete (this=0x%08X)\n",this)); 438 439 state = IOFWCommand::complete(state); 440 441 // If write was sent successfully but response wasn't received - try again 442 // Don't retry on bus reset! 443 if(state == kIOReturnTimeout && fTimeout != 0 && fCurRetries--) { 444 FWAddress addr; 445 446 // setup quad write 447 addr.nodeID = fWriteNodeID; 448 addr.addressHi = kCSRRegisterSpaceBaseAddressHi; 449 addr.addressLo = kFCPCommandAddress; 450 if(fMem) 451 ((IOFWWriteCommand *)fWriteCmd)->reinit(fWriteGen, addr, fMem, writeDone, this); 452 else 453 ((IOFWWriteQuadCommand *)fWriteCmd)->reinit(fWriteGen, addr, 454 (UInt32 *)fCommand, fCmdLen/4, writeDone, this); 455 fTimeout = 0; 456 return fStatus = startExecution(); 457 } 458 if(fSync) 459 fSyncWakeup->signal(state); 460 //else if(fComplete) 461 //(*fComplete)(fRefCon, state, fControl, this); 462 return state; 463} 464 465bool IOFireWireAVCCommandInGen::init(IOFireWireNub *device, UInt32 generation, 466 const UInt8 * command, UInt32 cmdLen, UInt8 * response, UInt32 * responseLen) 467{ 468 FIRELOG_MSG(("IOFireWireAVCCommandInGen::init (this=0x%08X)\n",this)); 469 470 FWAddress addr; 471 UInt32 dummyGen; 472 IOFireWireController *control; 473 // Get nodeID and check generation 474 device->getNodeIDGeneration(dummyGen, fWriteNodeID); 475 fWriteGen = generation; 476 477 // setup quad write 478 addr.nodeID = fWriteNodeID; 479 addr.addressHi = kCSRRegisterSpaceBaseAddressHi; 480 addr.addressLo = kFCPCommandAddress; 481 482 control = device->getController(); 483 IOFWCommand::initWithController(control); 484 // Start out without timeout, update when command is accepted by device 485 fTimeout = 0; 486 fSync = true; 487 fCancelOnReset = true; 488 fResponse = response; 489 fResponseLen = responseLen; 490 fMaxRetries = 4; 491 fCurRetries = fMaxRetries; 492 fCmdLen = cmdLen; 493 494 // create/clear expansion data 495 fIOFireWireAVCCommandExpansion = (ExpansionData*) IOMalloc( sizeof(ExpansionData) ); 496 if( fIOFireWireAVCCommandExpansion == NULL ) 497 return false; 498 else 499 bzero( fIOFireWireAVCCommandExpansion, sizeof(ExpansionData) ); 500 501 // create command 502 if(cmdLen == 4 || cmdLen == 8) { 503 fMem = NULL; 504 fCommand = command; 505 fWriteCmd = new IOFWWriteQuadCommand; 506 if(!fWriteCmd) { 507 return false; 508 } 509 ((IOFWWriteQuadCommand *)fWriteCmd)->initAll(control, generation, addr, 510 (UInt32 *)command, cmdLen/4, writeDone, this); 511 } 512 else { 513 fCommand = NULL; 514 fMem = IOMemoryDescriptor::withAddress((void *)command, cmdLen, 515 kIODirectionOutIn); 516 if(!fMem) { 517 return false; 518 } 519 520 IOReturn err = fMem->prepare(); 521 if( err != kIOReturnSuccess ) 522 { 523 fMem->release(); 524 return false; 525 } 526 527 fWriteCmd = new IOFWWriteCommand; 528 if(!fWriteCmd) { 529 return false; 530 } 531 ((IOFWWriteCommand *)fWriteCmd)->initAll(control, generation, addr, fMem, writeDone, this); 532 } 533 534 fIOFireWireAVCCommandExpansion->fStarted = true; 535 fIOFireWireAVCCommandExpansion->fSyncWakeupSignaled = false; 536 537 return true; 538} 539 540IOReturn IOFireWireAVCCommandInGen::reinit(IOFireWireNub *device, UInt32 generation, 541 const UInt8 * command, UInt32 cmdLen, UInt8 * response, UInt32 * responseLen) 542{ 543 FIRELOG_MSG(("IOFireWireAVCCommandInGen::reinit (this=0x%08X)\n",this)); 544 545 if(Busy()) 546 return fStatus; 547 if(fMem) { 548 fMem->release(); 549 fMem = NULL; 550 } 551 if(fWriteCmd) { 552 fWriteCmd->release(); 553 fWriteCmd = NULL; 554 } 555 if(init(device, generation, command, cmdLen, response, responseLen)) 556 return kIOReturnSuccess; 557 else 558 return kIOReturnNoMemory; 559} 560 561