/* File: DVIsochComponent.c Contains: DV version of the Isoch component Version: 1.0 Copyright: й 1999-2000 by Apple Computer, Inc., all rights reserved. File Ownership: DRI: Richard Sepulveda Other Contact: Jay Lloyd, Kevin Williams Technology: DV Writers: (CK) Casey King (SF) Sean Findley (KW) Kevin Williams (jkl) Jay Lloyd (GDW) George D. Wilson Jr. (SW) Sean Williams (RS) Richard Sepulveda Change History (most recent first): 3/16/00 SF [2438610] Put 1-second delay before checking camera configuration. 2/14/00 CK [2432944] A little more to kill this bug 2/10/00 SF [2432944] Add retries to bandwidth/channel allocations. 12/7/99 jkl Changed isocMedia to isochMedia. 11/1/99 jkl Added useCMP atom for turning on and off CMP. Moved CMP from device specific to global. Moved the check for useCMP in the resource to global allocate time. Changed CMPWrite to update the expectedValue after each attempt to try to make it succeed. Some time the Canon camera arbitrarily fails on this. This needs to be redone so the entire operation can make sure the values are correct. 10/25/99 KW If an error occurs during one of the calls to FSL during cmpResetStage1..9, set the reset stage to kNoResetsProcessing. This ensures that the next bus reset will be processed. 10/22/99 KW In updateDeviceList, do not zero out localFWReferenceID when a device is removed. Zeroing out the localFWReferenceID causes the cmp bus reset routines to stop propogating. In doAVCTransaction deallocated the command object in the event of an error. 9/28/99 KW In IDHRead and IDHWrite, do not tear down read/write notification if an error occurs. 9/21/99 jkl Backed out this read/write postEvent deferred stuff. A stack-based variable was killing it and it needs more analysis. 9/20/99 jkl Really made the previous change. The DVFamilyLib postEvent was commented as meaning to defer the rest of the read/write complete notification to another deferred task, but was calling CallSecondaryInterruptHandler2 to do it. Since the postEvents for the read/write complete are already at deferred task, this call basically did nothing. Now QueueSecondaryInterruptHandler is used which definitely postpones the completion of postEvent to another deferred task. This is now much more of a risky change. <76> 9/17/99 jkl Changed isoch read/write complete notification callProcs to be called in another SIH. <75> 8/29/99 RS Fixed problem with unfriendly clients who wouldn't cancel their notifications before closing the isoch component. This could cause a crash if we callback and the client isn't there anymore. <74> 8/28/99 KW Dispose of all clients signed up for notifications when last instance is closed. Fixes crashes for hot plugging. <73> 8/17/99 jkl Changed CMP check to look for file type of INIT instead of gdfx. <72> 8/16/99 RS Added isoch version atom to device list. <71> 8/16/99 jkl Changed useCMP function to be off by default and look for a 'ucmp' resource id -20756 before using CMP. FireWire 2.2 or greater check is still there. <70> 8/16/99 jkl Replicated a default DV name atom property in old name property location. Moved the bus reset notification proc with FireWire to only be active during the isoch read and write. This makes sure an instance is always open with the call proc address valid. <69> 8/12/99 jkl Bracketed the name QTCopyAtomToPtr with lock/unlock container calls. <68> 8/10/99 jkl Added camera name updating routine. <67> 8/9/99 RS Added IDHUpdateDeviceList() to API. The client can now change r/w atoms in the internal device list. This currently includes device name. Completed the bus reset generated IDHDeviceChanged notification. The client can now be notified at interrupt time whether a device state has been changed. <66> 8/9/99 jkl Added handleBusReset call proc for interrupt level bus reset notification. <65> 8/9/99 jkl Added some camera name stuff. Moved the name atom to be a direct child of the device atom. Default name is DV, if a name has been entered for the device then use it, else if the vendor is known then name is "vendor name DV". <64> 8/6/99 jkl More work to get rid of DVFamily.h. Added defines for getDeviceStandard AVC transaction. Also changed default camera name to DV and left off standard. <63> 8/6/99 jkl Changed kNTSCStandard to be ntscIn from QTComponents.h. <62> 8/3/99 RS Properly handling device disconnect by propogating errors to client(s). <61> 7/29/99 jkl Set input channel when camera is listener during bus reset handling. Some cameras do not follow the spec. Made sure to set bus reset processing state to noProcessing after bus reset processing has been interrupted. <60> 7/28/99 jkl Added all of the bus reset stages for CMP. Still not robust enough to handle errors during bus reset. Added Gestalt check for FireWire 2.2 or greater. CMP does not run if this check fails, allowing HaveBlue to run on FireWire 2.1. <59> 7/28/99 jkl Changed FWRegisterDriver to use the device description instead of driver refNum for client specific data. This lets the bus reset handling get the device description. Nobody was using the refNum. Turned on bus reset management. <58> 7/28/99 jkl Moved isochPortCommandObject and asynchCommandObject allocation to updateDeviceList, device added routine. Set max payload for asynch command object to 512. Changed all of the cmp routines to use the already allocated asynch command object. Changed local pcrBuffer modifications to always use FWRead and FWCompareAndSwap. Reduced overhead ID from 14 to 12. <57> 7/23/99 jkl Turned off CMP bus reset handling. <56> 7/22/99 jkl CMP, added correct speed, bandwidth, and overheadID calculations and configuration to manage reconnections after bus reset. <55> 7/20/99 jkl Fixed CMP bug where device not broadcasting could look connected. Turned off broadcast bit to disconnect plug before changing channel on a device only broadcasting. Sony is not going to like this but it solves compatibility problems and strictly follows the spec. <54> 7/20/99 jkl Turned CMP on. Can create a file in Preferences folder called "no cmp" to disable CMP. <53> 7/18/99 jkl Fixed command object allocation in irmAllocateBandwidth. Fixed initial overhead ID value for output plug control register. <52> 7/16/99 jkl Added latest CMP code. <51> 7/16/99 KW Moved FireWire callbacks to here. Register all devices with FireWire that have ever been seen when the first isoch component is opened. Likewise, Unregister all devices when the last isoch component is closed. Ensure that the device notification ID for a device is zeroed out after the notification has been disposed of. <50> 7/14/99 jkl Fixed dclProgramID mistake. <49> 7/14/99 jkl Made sure isochPortCommandObjects are allocated safely and not prematurely deallocated. Made sure disable read and write are safe if extraneous calls are made to them. <48> 7/13/99 jkl Reverted to previous version. Added fix for device control getTimeCode failing after hot plug. <47> 7/12/99 jkl Added a bunch of CMP stuff. Almost ready to turn on. <46> 7/12/99 RS Changed component instance storage from system heap to application heap to work around misbehaved apps that don't CloseComponent. <45> 7/10/99 RS Added notification for device added/removed so that we can update the fwClientID in the device control component if the camera is removed then re-added. <44> 7/8/99 RS Fixed problem where the device control instance was being prematurely released before all of the clients were finished. 7/7/99 SW Just removed some unused variables to stop compiler warnings. <42> 7/7/99 jkl Cleared driverRefNum and fwClientID fields on device removed, and updated them on device added for an already known device. Also updated localFWReferenceID in case the same camera is plugged into a different FireWire interface on the machine. <41> 7/5/99 jkl Added SetMaxPayload for client. <40> 7/2/99 RS Replaced event recording with logger to increase debug capability. <39> 7/1/99 RS Passing actual device description pointer instead of client instance to isoch callbacks since the client can change deviceID behind our back. <38> 6/30/99 RS Fixed error codes returned in certain functions. Removed openDeviceConnection() function because it is no long needed. Fixed bug with device name atom. Fixed bug where we were disabling read/writes in Close() if device was already possibly disabled. <37> 6/28/99 RS Moved IDHGetClientID from public to private call. Added kIDHErrDeviceTimeout errors for sync read/writes timeouts. Created public structs for IDHDimension and IDHResolution. Added deviceID to atom tree. <36> 6/28/99 RS Moved open device control from OpenDevice to SetConfig so that client doesn't have to Open a device for read/write in order to device control. Added device enable/disable notification. Returning error on getConfig if no previous setConfig. <35> 6/27/99 jkl Added default to NTSC in getDeviceStandard for devices that may not have any device control capability. <34> 6/26/99 jkl Integrated Richard's, Kevin's, and my changes to get read and write working. Wired clock back up. Too much other stuff to mention. Compare revisions if you really want to know. <33> 6/24/99 RS Added the DVCWriteFrame() and DVGetNextEmptyFrame() equivalent. We think all of the plumbing is in. So let the fun debug begin. <32> 6/24/99 KW Not ready for prime time but things compile. Errors exist. <31> 6/24/99 jkl Yanked all of the DV driver code into here. Some fun. 6/22/99 SW Add support for private calls to the IDH DV component instead of just using morphed GetDeviceStatus() calls. <29> 6/21/99 RS Added timeouts to synchronous reads and writes. The timeout has initially been set to 1/15 second (2 frame times). <28> 6/20/99 RS Moved device atom to device description structure to correctly handle device added/removed issues. Added PAL audio format config. Initialize device atom seed to 1. More cleanup. <27> 6/19/99 RS Removed getting connectionID in IDHGetDeviceStatus (not needed). <26> 6/19/99 RS Added deviceAtom and device standard to device description structure. Allocating correct buffer size according to camera format (ntsc or pal). Fixed bug where deviceID could change with last connection going to zero (BAD). Alot of general cleanup. <25> 6/18/99 RS Added kIDHUniqueIDType to device atom tree. Added some auxillary functions to help traverse atom list. <24> 6/18/99 GDW Made device control component handling global verses per instance. <23> 6/18/99 GDW Made some changes for additional device control component. <22> 6/17/99 RS Made some changes to open and close device to handle device access a little more gracefully. <21> 6/17/99 RS Added FWClientID to device descriptor for device control. Fixed bug when groing from write to read mode on a camera. Buffers weren't being allocated. <20> 6/17/99 jkl Added fwClientID to DVGetLocalFWReferenceID call to make the fwClientID part of the device info so IDH can execute getDeviceStandard by itself. <19> 6/16/99 RS Added Firewire Node ID to device structure. Added AVC transaction code to isoch component to work around DVDriver hang problem. Check for sync I/O problem when called at non-task time. <18> 6/17/99 GDW Added more device control calls. <17> 6/16/99 RS Do parameter checking on all public IDH functions. Turn on exclusive access checking. Fix bug with I/O direction atom size. <16> 6/16/99 GDW Add calls to device control component. <15> 6/15/99 RS Updating device list everytime a device is added or removed. Locking down atom container when we are copying data from atom to pointer. <14> 6/15/99 RS Added synchronous I/O and passing a 'nil' buffer into IDHWrite. <13> 6/15/99 RS Changed IODirection on DV configs from input to InputAndOutput. <12> 6/15/99 RS Merged DVFamilyLib into Isoch component. Added seed for device list validity check from client. Create component globals in register and free in unregister. Check seed when client does a SetConfig. 6/11/99 SW MasterInterfaces integration <10> 6/10/99 RS Added two new IDH functions ReleaseBuffer and CancelIO. Added notification version of Reads and Writes. MAJOR CHANGES. <9> 6/8/99 RS Fixed bug storing IDHDeviceStatus in atom list. <8> 6/7/99 RS Keep track of open for reads/writes per device. <7> 6/7/99 RS Saving open permissions in component instance. Replaced Fail macro with new macro that displays function return code (better for debug). Disable device on close based on permissions flags. Returning bufferlist on read/write completion callback. Fixed bug in IDHWrite where we weren't doing a DVCWriteFrame. <6> 6/4/99 SW Adjust for slight changes in IDH.i <5> 6/1/99 RS Added private functions to find device atom given any of its siblings and a function to get device status given a device ID. Added device standard and firewire node ID to status struct. Save open permissions in device status. <4> 5/24/99 RS Changed param type in SetDeviceConfig. Allow user to get device status on any device (not just configured device). Changed IDHIsochReadData and IDHIsochWriteData to IDHReadData and IDHWrite. <3> 5/24/99 RS We are now returning a copy of device descrip atom container. <2> 5/20/99 RS Added queueing of isoch reads and write. Changed error return code names. */ /* еее TO DO еее camera tracking if a camera goes offline, do we return an error from getdevicestatus or set the active flag super find atom turn on permanent locks by client review atom list (i.e. isoch container atom) */ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "DVVers.h" #include "FireWire.h" #include "GenericDriverFamily.h" #include "LoggerAndDebugMacros.h" #include "OxcartDebug.h" #include "DVDCLOut.h" #include "DVDCLIn.h" #define kDVDeviceInfo 'ddin' // DV device info #define kBuffersPerDevice 5 // number of buffers per device for reads and writes #define kMaxDevicesActive 64 // max devices on firewire bus #define kMaxInstances 10 // max clients attached to a device #define kMaxNotifications 100 // 100 notifcaionts can be supported #define kNTSCCompressedBufferSize 120000 #define kPALCompressedBufferSize 144000 #define kIDHSeedAtomType 'seed' // seed for device atom validity #define kIDHDevicePresentType 'pres' // is device present? #define kServiceTypeDVServices 'avc ' #define kTimeoutDuration (1000 / 15) * durationMillisecond // 1/15 second (2 frame times) #define kMaxRetries 8 #if PRAGMA_ALIGN_SUPPORTED #pragma options align=mac68k #endif #define kNoResetsProcessing 0 #define kResetProcessing 1 #define kResetInterrupted 2 #define kResetFailed 3 #define koPCRaddrHi 0x0000FFFF #define koPCRaddrLo 0xF0000904 #define kiPCRaddrHi 0x0000FFFF #define kiPCRaddrLo 0xF0000984 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // globals ComponentInstance gCurInstance = 0; typedef struct IsochComponentInstance IsochComponentInstance, *IsochComponentInstancePtr; typedef struct IsochComponentGlobals IsochComponentGlobals, *IsochComponentGlobalsPtr, **IsochComponentGlobalsHandle; struct IDHDVCompleteEvent { IDHEventHeader eventHeader; Ptr frameBuffer; UInt32 bufferSize; UInt32 fwCycleTime; }; typedef struct IDHDVCompleteEvent IDHDVCompleteEvent; // internal frame queue structure typedef struct QueueElement { struct QElem *qLink; // standard queue vars short qType; short locks; // number of clients owning buffer Ptr buffer; // the data long size; // size of the data } QueueElement, *QueueElementPtr; // describes a device on the firewire bus struct DeviceDescription { IsochComponentGlobals *componentGlobals; // isoch component globals QHdrPtr freeQueue, // holds free buffers usedQueue; // holds buffers being used by client IDHDeviceID deviceID; // persistent unique ID for each device FWReferenceID fwClientID; // volatile FW Client ID for FSL, unique for all active devices FWReferenceID localFWReferenceID; // reference to local node, may change if more than one FW interface is available RegEntryID regEntryID; // used to register driver with FireWire CSRNodeUniqueID uniqueID; // peristent 64-bit EUID for 1394 IDHNotificationID readIsochNotificationID; // read notification ID IDHNotificationID writeIsochNotificationID; // write notification ID IsochComponentInstance *instancePtr[kMaxInstances]; // clients that are attached to device ComponentInstance deviceControlInstance; // device control component instance IDHNotificationID deviceControlNotificationID; // tell me when device is added/removed so that i can adjust fwClientID Component clock; // FireWire clock component, client's get their own instance, not sure why UInt32 standard; // device standard QTAtomContainer deviceContainer; // contains device database IsochPortID isochPortID; // contains FireWire isoch port ID for active stream IsochChannelID outputIsochChannelID; // FireWire channel ID for isoch transmit IsochChannelID inputIsochChannelID; // FireWire channel ID for isoch receive DVGlobalOutPtr pGlobalDVOutData; // global data for output DCL DVGlobalInPtr pGlobalDVInData; // global data for input DCL DCLProgramID dclProgramID; // ID of DCL program FWCommandObjectID isochPortCommandObjectID; // FireWire command object used in init/release isoch port routines FWCommandObjectID startIsochPortCommandObjectID; // FireWire command object used in start isoch port routines FWCommandObjectID stopIsochPortCommandObjectID; // FireWire command object used in stop isoch port routines FWCommandObjectID asynchCommandObjectID; // FireWire command object used in CMP FWAddressSpaceID pcrID; // address space ID of plug control register UInt32 pcrBuffer; // pcr buffer space, use FWRead and FWLock to access UInt32 pcrIndex; // pcr number that was allocated UInt32 resetStatus; // used in bus reset processing to keep up with reset stage UInt32 buffer[2]; // buffer for pcr reads and locks UInt32 expectedPCR; // value to check for successful update of PCR UInt32 cmpBandwidth; // allocated isoch bandwidth for a stream, used in re-establishing connection after bus reset UInt32 cmpOverheadID; // overheadID for a stream, used in re-establishing connection after bus reset UInt32 cmpChannel; // channel for a stream, used in re-establishing connection after bus reset DriverRefNum refNum; // volatile driver refNum, unique for all active devices SInt16 deviceControlCount; // number of clients using device control SInt16 nQueueElements; // holds number of buffers avail SInt16 readLocks; // num clients that have device open for reads SInt16 writeLocks; // num clients that have device open for writes SInt16 exclusiveAccess; // does client have exclusive access? Boolean complete; // I/O complete flag for synchronous operation Boolean active; // device is currently online }; typedef struct DeviceDescription DeviceDescription, *DeviceDescriptionPtr; struct ClientNotification { ComponentInstance ihc; IDHDeviceID deviceID; IDHNotificationProc notificationProc; IDHEvent events; void *userData; }; typedef struct ClientNotification ClientNotification; // component globals struct IsochComponentGlobals { IsochComponentInstancePtr lastInstance; // last instance opened UInt32 useCMP; // CMP off by default, turn on by adding 'ucmp' resource DeviceDescription deviceDescription[kMaxDevicesActive]; // description of each device ClientNotification clientNotification[kMaxNotifications]; // arbirtary # of notifications UInt32 nDevices; // number of devices in the list UInt32 seed; // keep track of device lists IsochComponentInstance *localInstance; // our local instance Boolean unRegistered; // was component unregistered? }; // per component instance variables struct IsochComponentInstance { Handle instanceHandle; // instance storage handle ComponentInstance self; // this component IsochComponentGlobalsPtr gGlobals; // pointer to component globals QTAtomSpec currentConfig; // current device config set by client IDHDeviceID deviceID; // persistent unique ID for each device, seed value long permissions; // open permissions QHdrPtr readQueue; // queue of read requests QHdrPtr writeQueue; // queue of write requests Boolean timeout; // timeout for synchronous reads Boolean hasDeviceControl; // does this client have device control? }; // convenience stuff for getDeviceStandard typedef struct AVCCTSFrameStruct { UInt8 cmdType_respCode; // cmd type/response code UInt8 headerAddress; UInt8 opcode; UInt8 operand[5]; } AVCCTSFrameStruct, *AVCCTSFrameStructPtr; enum { kAVCStatusInquiryCommand = 0x01, kAVCOutputSignalModeOpcode = 0x78, kAVCInputSignalModeOpcode = 0x79, kAVCSignalModeSD525_60 = 0x00, kAVCSignalModeSDL525_60 = 0x04, kAVCSignalModeHD1125_60 = 0x08, kAVCSignalModeSD625_50 = 0x80, kAVCSignalModeSDL625_50 = 0x84, kAVCSignalModeHD1250_50 = 0x88, kAVCSignalModeDummyOperand = 0xff }; /************************************************************************************/ // Begin Dispatch Stuff // Used by Type's .k.h to create prototypes for our routines #define IDH_BASENAME() IDH #define IDH_GLOBALS() ComponentInstance #define IDHDV_BASENAME() IDHDV #define IDHDV_GLOBALS() ComponentInstance #include "IsochronousDataHandler.k.h" #include "IsochronousDataHandlerPriv.k.h" // Used by Component Dispatch Helper to call our routines #define CALLCOMPONENT_BASENAME() IDH #define CALLCOMPONENT_GLOBALS() IsochComponentInstancePtr storage // Other defines for Component Dispatch Helper #define COMPONENT_DISPATCH_FILE "IDHDispatch.h" /* describes what to dispatch */ #define COMPONENT_UPP_SELECT_ROOT() IDH /* root for Type's UPP_PREFIX and SELECT_PREFIX */ #define COMPONENT_SUBTYPE_UPP_SELECT_ROOT() IDHDV #include "Components.k.h" /* std .k.h */ #include "QuickTimeComponents.k.h" /* Type & SubType .k.h */ #include "ComponentDispatchHelper.c" /* make our dispatcher and cando */ // End Dispatch Stuff /************************************************************************************/ // local prototypes OSErr addBuffersToQueue( DeviceDescription *deviceDescriptionPtr); OSErr checkSeed( IsochComponentGlobalsPtr gGlobals, QTAtomSpec *configID); OSErr closeDeviceControl( ComponentInstance ih, DeviceDescriptionPtr deviceDescriptionPtr); OSStatus deviceControlCallback( IDHGenericEvent *event, void *userData); OSErr doAVCTransaction( FWReferenceID ih, DVCTransactionParams* inTransaction); OSErr findAtom( QTAtomSpec *atomSpec, OSType theType, QTAtom *theAtom); OSErr findDeviceDescriptionforDevice( IsochComponentInstancePtr ih, UInt32 deviceID, DeviceDescription **deviceDescription); OSErr getDeviceID( QTAtomSpec *configID, UInt32 *deviceID); OSErr getDeviceStandard( FWReferenceID ih, UInt32 * pStandard ); OSStatus localCompletionRoutine(IDHGenericEvent *event, void *userData); OSStatus myTimerHandler( void *p1, void *p2); OSErr postEvent(IsochComponentGlobals* gGlobals, IDHDeviceID deviceID, IDHEvent event, void* privateData); OSStatus postEventSIH(void* p1, void* p2); OSStatus readNotifyProc( IDHGenericEvent *event, void *userData); OSErr setupAVCTransaction( FWReferenceID ih, DVCTransactionParams *pParams); OSErr setupVideoAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard); OSErr setup32kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard); OSErr setup44kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard); OSErr setup48kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard); OSStatus writeNotifyProc( IDHGenericEvent *event, void *userData); OSStatus cameraNameLookup(DeviceDescriptionPtr pDeviceDescription, UInt8 *name); OSStatus updateCameraName(CSRNodeUniqueID *guid, UInt8 *name); OSErr updateDeviceList(IsochComponentInstancePtr ih, GDFDeviceEventData *pGDFData, IDHDeviceID *deviceID, Boolean doAdd); OSStatus handleDeviceAdded(ComponentInstance ihc, GDFDeviceEventData *pGDFData); OSStatus handleDeviceRemoved(ComponentInstance ihc, GDFDeviceEventData *pGDFData); OSStatus initIsochPort( FWClientInitIsochPortParams *pInitIsochPortParams, UInt32* outCommandAcceptance); OSStatus releaseIsochPort(FWClientReleaseIsochPortParams *pReleaseIsochPortParams, UInt32* outCommandAcceptance); OSStatus stopIsochPort( FWClientIsochPortControlParams *pIsochPortControlParams, UInt32* outCommandAcceptance); OSStatus startIsochPort(FWClientIsochPortControlParams *pIsochPortControlParams, UInt32* outCommandAcceptance); void isochPortCommandCompletionProc(FWCommandObjectID fwCommandObjectID, OSStatus commandStatus, UInt32 completionProcData); OSStatus doLocalTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams); OSStatus doRemoteTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams); OSStatus trialLocalTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams); OSStatus trialRemoteTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams); OSStatus doLocalListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams); OSStatus doRemoteListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams); OSStatus trialLocalListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams); OSStatus trialRemoteListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams); OSErr cmpNewPointToPointConnection(UInt32 pcrOffset, FWClientID clientID, UInt32 channel, UInt32 speed, UInt32 overhead, DeviceDescriptionPtr pDeviceDescription); OSErr cmpDisposePointToPointConnection(UInt32 pcrOffset, FWClientID clientID, DeviceDescriptionPtr pDeviceDescription); OSStatus irmReleaseChannel(FWClientID fwClientID, UInt32 channel, DeviceDescriptionPtr pDeviceDescription); OSStatus irmReleaseBandwidth(FWClientID fwClientID, UInt32 fwBandwidthUnits, DeviceDescriptionPtr pDeviceDescription); OSStatus irmAllocateChannel(FWClientID fwClientID, UInt32 *channel, DeviceDescriptionPtr pDeviceDescription); OSStatus irmAllocateBandwidth(FWClientID fwClientID, UInt32 fwBandwidthUnits, DeviceDescriptionPtr pDeviceDescription); UInt32 irmCalculateBandwidthUnits(UInt32 pcrValue); OSStatus cmpHandleLocalLock(FWClientAsynchRequestParamsPtr pFWClientAsynchRequestParams, UInt32 *pCommandAcceptance); OSErr cmpRead(UInt32 pcrOffset, FWClientID clientID, UInt32 *pcrValue, DeviceDescriptionPtr pDeviceDescription); OSErr cmpWrite(UInt32 pcrOffset, FWClientID clientID, UInt32 expectedPCRValue, UInt32 newPCRValue, DeviceDescriptionPtr pDeviceDescription); OSStatus cmpHandleBusReset(FWClientInterfaceParamsPtr pParams, UInt32 *pCommandAcceptance); OSErr cmpResetRead(UInt32 offsetLo, FWClientID clientID, FWCommandCompletionProcPtr completionProc, DeviceDescriptionPtr deviceDescriptionPtr); OSErr cmpResetWrite(UInt32 offsetLo, FWClientID clientID, FWCommandCompletionProcPtr completionProc, DeviceDescriptionPtr deviceDescriptionPtr); void cmpResetStageOne(DeviceDescriptionPtr pDeviceDescription); void cmpResetStageTwo(FWCommandObjectID theObjectID, OSStatus status, UInt32 data); void cmpResetStageThree(FWCommandObjectID theObjectID, OSStatus status, UInt32 data); void cmpResetStageFour(FWCommandObjectID theObjectID, OSStatus status, UInt32 data); void cmpResetStageFive(FWCommandObjectID theObjectID, OSStatus status, UInt32 data); void cmpResetStageSix(FWCommandObjectID theObjectID, OSStatus status, UInt32 data); void cmpResetStageSeven(FWCommandObjectID theObjectID, OSStatus status, UInt32 data); void cmpResetStageEight(FWCommandObjectID theObjectID, OSStatus status, UInt32 data); void cmpResetStageNine(FWCommandObjectID theObjectID, OSStatus status, UInt32 data); void cmpResetComplete(FWCommandObjectID theObjectID, OSStatus status, UInt32 data); OSErr enableRead(IsochComponentInstancePtr ih); OSErr enableWrite(IsochComponentInstancePtr ih); OSErr disableRead(IsochComponentInstancePtr ih); OSErr disableWrite(IsochComponentInstancePtr ih); OSErr dclInitOutput(DeviceDescriptionPtr pDeviceDescription, IsochComponentGlobals* isochComponentGlobals); DVLocalOutPtr dclAllocatePlayBufferGroup(DVGlobalOutPtr pGlobalData); DCLCommandPtr dclAllocateCommand(DCLCommandPoolPtr pDCLCommandPool, UInt32 dclSize); DCLCommandBlockPtr dclAllocateCommandBlock(DCLCommandPoolPtr pDCLCommandPool); DCLCommandPoolPtr dclAllocateCommandPool(void); void dclDeallocateCommandPool( DCLCommandPoolPtr pDCLCommandPool ); void dclDeallocatePlayBufferGroup( DVLocalOutPtr pLocalData ); void dclDeallocateCommandBlock(DCLCommandBlockPtr pDCLCommandBlock); void handleDVOutput(DCLCommandPtr pDCLCommandPtr); void handleDVOutputUnderrun(DCLCommandPtr pDCLCommandPtr); void updateDVOutputBuffers(DCLCommandPtr pDCLCommandPtr); void handleDVOutputUnderrunCompletion(FWCommandObjectID fwCommandObjectID, OSStatus commandStatus, UInt32 completionProcData); OSErr createDVPlayBufferGroupUpdateList( DVLocalOutPtr pLocalData); OSErr disposeDCLOutput( DVGlobalOutPtr pOutputData ); UInt32 addFWCycleTimeToFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 ); UInt32 subtractFWCycleTimeFromFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 ); UInt32 convertFractionalSecondsToFWCycleTime( UInt32 secondsNumerator, UInt32 secondsDenominator ); OSErr getNextFullOutputFrame( DVIODataPtr pOData, UInt32** ppFrame ); OSErr getNextEmptyOutputFrame( DVIODataPtr pOData, UInt32** ppFrame); OSErr queueNextFullOutputFrame( DVIODataPtr pOData, UInt32* pFrame ); OSErr getNextEmptyInputFrame( DVIODataPtr pIData, UInt32** ppFrame ); OSErr getNextFullInputFrame( DVIODataPtr pIData, UInt32** ppFrame ); OSErr releaseFullInputFrame( DVIODataPtr pIData, UInt32* pFrame ); Boolean isDVFinished( DVIODataPtr pIOData ); OSErr getCurrentDCLFrame( DVIODataPtr pIOData, UInt32** ppFrame ); OSErr initDVFrameOutput(UInt32 standard, DVIODataPtr* ppIOData); OSErr initDVFrameInput(UInt32 standard, DVIODataPtr* ppIOData); OSErr releaseDVFrameIO( DVIODataPtr pIOData ); void deleteDVFrameAndElement( DVFrameQElemPtr pQElem ); OSErr dclInitInput(DeviceDescriptionPtr pDeviceDescription, IsochComponentGlobals* isochComponentGlobals); void storeDVPackets( DCLCommandPtr pDCLCommandPtr ); OSErr disposeDCLInput( DVGlobalInPtr pInputData ); OSErr writeFrame( IsochComponentInstancePtr ih, char *tmpBuffPtr); OSErr getEmptyOutputFrame( IsochComponentInstancePtr ih, Ptr *tmpBuffPtr, UInt32 *size); OSStatus registerDevice(RegEntryID* inRegEntryID, FWDriverID* outDriverID, DeviceDescriptionPtr pDeviceDescription); Boolean useCMP(void); OSStatus handleBusReset(FWClientInterfaceParamsPtr pParams, UInt32 *pCommandAcceptance); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Component Routines #pragma mark - #pragma mark ееееееееее Standard Component Calls ееееееееее //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Reverses anything done in the open call. // See this mark for more information on variables handling: GlobalsAndPrefs. static pascal ComponentResult IDHClose( IsochComponentInstancePtr iGlobals, ComponentInstance self ) { IsochComponentGlobalsHandle globHdl = 0L; // Handle to our globals OSErr result = noErr; IsochComponentGlobalsPtr gGlobals; UInt32 i; RecordEventLogger( 'isoc', 'clos', 0, (unsigned long) iGlobals); FailMessage(self == 0); globHdl = (IsochComponentGlobalsHandle) GetComponentRefcon((Component) self); // Try and get our refCon if ( (globHdl == nil) || ((UInt32) globHdl == 0xFFFFFFFF) || (iGlobals == nil)) // If its all F's this is a close for register { // otherwise it is a real close return(noErr); } gGlobals = *globHdl; if( iGlobals->deviceID && iGlobals->hasDeviceControl) // Close device control { DeviceDescription *deviceDescriptionPtr; result = findDeviceDescriptionforDevice( iGlobals, iGlobals->deviceID, &deviceDescriptionPtr); FailWithVal( result != noErr, Exit, result); result = closeDeviceControl( (ComponentInstance) iGlobals, deviceDescriptionPtr); FailMessage( result != noErr); iGlobals->hasDeviceControl = false; } if( iGlobals->permissions) IDHCloseDevice( (ComponentInstance) iGlobals); // close device in case the client forgot to for( i=0; iclientNotification[i].ihc == (ComponentInstance) iGlobals) { RecordEventLogger( 'isoc', 'clos', 'not-', (unsigned long) iGlobals); gGlobals->clientNotification[i].deviceID = 0; gGlobals->clientNotification[i].ihc = 0; } } PBQueueDelete( iGlobals->readQueue); // delete queues PBQueueDelete( iGlobals->writeQueue); if (CountComponentInstances((Component)self) <= 1) // Is this the last close? { int i; QueueElement *qep; RecordEventLogger( 'isoc', 'clos', 0, 'last'); for( i=0; ideviceDescription[i]; // teardown read just in case the client forgot if( deviceDescriptionPtr->deviceID && deviceDescriptionPtr->readIsochNotificationID) { result = IDHCancelNotification( (ComponentInstance)gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID); FailMessage( result != noErr); result = IDHDisposeNotification( (ComponentInstance)gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID); FailMessage( result != noErr); deviceDescriptionPtr->readIsochNotificationID = nil; if( deviceDescriptionPtr->readLocks > 0) { result = disableRead((IsochComponentInstancePtr) iGlobals); FailMessage( result != noErr); deviceDescriptionPtr->readLocks = 0; } } // teardown write just in case the client forgot if( deviceDescriptionPtr->deviceID && deviceDescriptionPtr->writeIsochNotificationID) { result = IDHCancelNotification( (ComponentInstance)gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID); FailMessage( result != noErr); result = IDHDisposeNotification( (ComponentInstance)gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID); FailMessage( result != noErr); deviceDescriptionPtr->writeIsochNotificationID = nil; if( deviceDescriptionPtr->writeLocks > 0) { result = disableWrite((IsochComponentInstancePtr) iGlobals); FailMessage( result != noErr); deviceDescriptionPtr->writeLocks = 0; } } // free all of the queue buffers while( PBDequeueFirst( gGlobals->deviceDescription[i].freeQueue, (QElemPtr *) &qep) == noErr) { if( qep) { if( qep->buffer) DisposePtr( qep->buffer); DisposePtr( (char *) qep); } } while( PBDequeueFirst( gGlobals->deviceDescription[i].usedQueue, (QElemPtr *) &qep) == noErr) { if( qep) { if( qep->buffer) DisposePtr( qep->buffer); DisposePtr( (char *) qep); } } gGlobals->deviceDescription[i].readLocks = gGlobals->deviceDescription[i].writeLocks = 0; } // Unregister all devices that have ever been seen. for (i=0; i < gGlobals->nDevices; ++i) { result = FWUnregisterDriver(gGlobals->deviceDescription[i].fwClientID); FailMessage(noErr != result); } // make sure all notifications are cancelled for (i = 0; i < kMaxNotifications; ++i) { if( gGlobals->clientNotification[i].deviceID != 0 && gGlobals->clientNotification[i].ihc != (ComponentInstance) gGlobals->localInstance) { gGlobals->clientNotification[i].deviceID = 0; log_debugstr( "All notifications weren't cancelled in last close !!!"); } } if( gGlobals->unRegistered) { for( i=0; ideviceDescription[i]; result = closeDeviceControl( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr); FailMessage( result != noErr); if( deviceDescriptionPtr->deviceContainer) // free device atoms { result = QTRemoveChildren( deviceDescriptionPtr->deviceContainer, kParentAtomIsContainer); FailMessage( result != noErr); result = QTDisposeAtomContainer( deviceDescriptionPtr->deviceContainer); FailMessage( result != noErr); deviceDescriptionPtr->deviceContainer = nil; } } for( i=0; ideviceDescription[i].freeQueue); // delete device queues PBQueueDelete( gGlobals->deviceDescription[i].usedQueue); } if( gGlobals->localInstance) DisposePtr( (Ptr) gGlobals->localInstance); HUnlock((Handle)globHdl); DisposeHandle((Handle)globHdl); SetComponentRefcon((Component) self, 0xFFFFFFFF); // component is now unused gGlobals = nil; } } if( iGlobals != nil && iGlobals->instanceHandle) // dispose instance handle DisposeHandle( iGlobals->instanceHandle); Exit: FailMessage( result != noErr); return (result); } pascal ComponentResult IDHVersion( IsochComponentInstance *storage) { #pragma unused (storage) RecordEventLogger( 'isoc', 'vers', 0, 0); return 0x10000; } pascal ComponentResult IDHTarget(struct IsochComponentInstance *storage, ComponentInstance parentComponent) { #pragma unused (storage, parentComponent) RecordEventLogger( 'isoc', 'targ', 0, 0); return(noErr); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Opens the component instance. // See this mark for more information on variables handling: GlobalsAndPrefs. static pascal ComponentResult IDHOpen( struct IsochComponentInstance *unused1, ComponentInstance self ) { #pragma unused (unused1) IsochComponentInstancePtr instancePtr = nil; // Pointer to instance globals Handle instanceHandle = nil; // Handle to instance globals ComponentResult result = noErr; // Result return by this routine IsochComponentGlobalsHandle refConHdl = nil; IsochComponentGlobalsPtr gGlobals = nil; // Check the ComponentRefcon to see if this 'Open' request is part of the // registering process (Open, Register, Close) or a true 'Open'. // By convention in FWD, the ComponentRefcon is used to convey Registration // information and to point to a structure of items that are shared by component // instances as shown below: // // NULL // The component has NOT been successfully registered. // // 0xFFFFFFFF // The component has been successfully registered, but NO shared data // has been allocated or initialized. // // Other Value // Any other value means that the component has been successfully // registered. Additionally, it points to the data that is shared // between ComponentInstances. //еее RecordEventLogger( 'isoc', 'open', 0, 0); SetComponentInstanceStorage(self, (Handle) 0); // save instance ptr in component refConHdl = (IsochComponentGlobalsHandle) GetComponentRefcon((Component) self); if ( refConHdl == nil ) // Is this a register call? { RecordEventLogger( 'isoc', 'open', 'reg ', 0); gCurInstance = self; // Save for register return(noErr); // Just return lt register happen } //====================================================================== // Start allocating instance storage ReserveMem( sizeof(IsochComponentInstance) ); instanceHandle = NewHandleClear( sizeof(IsochComponentInstance) ); // allocate instance storage FailWithAction( instanceHandle == nil, result = MemError(), BadExit); HLock((Handle) instanceHandle); instancePtr = (IsochComponentInstancePtr) *instanceHandle; instancePtr->instanceHandle = instanceHandle; instancePtr->self = self; SetComponentInstanceStorage(self, (Handle) instancePtr); // save instance ptr in component RecordEventLogger( 'isoc', 'open', 0, (unsigned long) instancePtr); Exit: instancePtr->gGlobals = *refConHdl; gGlobals = *refConHdl; gGlobals->lastInstance = instancePtr; // allocate queues for client read and write requests result = PBQueueCreate( &instancePtr->readQueue); FailWithVal( result != noErr, BadExit, result); result = PBQueueInit( instancePtr->readQueue); FailWithVal( result != noErr, BadExit, result); result = PBQueueCreate( &instancePtr->writeQueue); FailWithVal( result != noErr, BadExit, result); result = PBQueueInit( instancePtr->writeQueue); FailWithVal( result != noErr, BadExit, result); FailMessage( result != noErr); if (1 == CountComponentInstances((Component)self)) // Is this the first Open? { // Register all devices that are currently online. UInt32 devIndex; OSStatus status; RecordEventLogger('isoc', '1st ', 'open', gGlobals->nDevices); for (devIndex = 0; devIndex < gGlobals->nDevices; ++devIndex) { status = registerDevice( &gGlobals->deviceDescription[devIndex].regEntryID, &gGlobals->deviceDescription[devIndex].fwClientID, &gGlobals->deviceDescription[devIndex]); FailMessage(noErr != status); } } return result; BadExit: // chunk component stuff if last instance if (CountComponentInstances((Component)self) == 1 && gGlobals != nil) { int i; for( i=0; ideviceDescription[i].freeQueue); // release queues PBQueueDelete( gGlobals->deviceDescription[i].usedQueue); // release queues } DisposeHandle( (Handle) refConHdl); refConHdl = nil; gGlobals = nil; SetComponentRefcon((Component) self, (SInt32) 0xFFFFFFFF); } if( instanceHandle != nil) { DisposeHandle( instanceHandle); instanceHandle = nil; } FailMessage( result != noErr); return result; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Registers the component. Returns non-zero result if component should not // be registered. This routine is called by the IDHOpen routine // after the instance globals are allocated, but before the component globals // are instantiated. This routine should not call any other functions that // require component globals or preferences. // See this mark for more information on variables handling: GlobalsAndPrefs. static pascal ComponentResult IDHRegister( IsochComponentInstancePtr globals ) { #pragma unused( globals) ComponentResult result = false; // Assume success int i; IsochComponentGlobalsHandle refConHdl; IsochComponentGlobals *gGlobals; IsochComponentInstance *localInstance; RecordEventLogger( 'isoc', 'reg ', 0, 0); //========================================================================= // Allocate globals for all component instances ReserveMem(sizeof(IsochComponentGlobals)); refConHdl = (IsochComponentGlobalsHandle) NewHandleSysClear(sizeof(IsochComponentGlobals)); // Try and allocate refCon data FailWithAction( refConHdl == nil, result = MemError(), Exit); HLock( (Handle) refConHdl); gGlobals = *refConHdl; SetComponentRefcon((Component) gCurInstance, (long) refConHdl); for( i=0; ideviceDescription[i].freeQueue); FailWithVal( result != noErr, Exit, result); result = PBQueueInit( gGlobals->deviceDescription[i].freeQueue); FailWithVal( result != noErr, Exit, result); result = PBQueueCreate( &gGlobals->deviceDescription[i].usedQueue); FailWithVal( result != noErr, Exit, result); result = PBQueueInit( gGlobals->deviceDescription[i].usedQueue); FailWithVal( result != noErr, Exit, result); // initialize other variables gGlobals->deviceDescription[i].nQueueElements = 0; gGlobals->deviceDescription[i].deviceID = nil; gGlobals->deviceDescription[i].readLocks = 0; gGlobals->deviceDescription[i].writeLocks = 0; gGlobals->deviceDescription[i].exclusiveAccess = 0; gGlobals->deviceDescription[i].readIsochNotificationID = nil; gGlobals->deviceDescription[i].writeIsochNotificationID = nil; for( j=0; jdeviceDescription[i].instancePtr[j] = nil; } // see if CMP should be turned on gGlobals->useCMP = useCMP(); localInstance = (IsochComponentInstance *) NewPtrSysClear( sizeof( IsochComponentInstance)); FailWithAction( localInstance == NULL, result = MemError(), Exit); localInstance->self = (ComponentInstance) localInstance; localInstance->gGlobals = gGlobals; // pointer to component globals gGlobals->localInstance = localInstance; gGlobals->seed = 1; gGlobals->nDevices = 0; Exit: return result; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // The component is being unregistered. Mark the globals to be disposed. // See this mark for more information on variables handling: GlobalsAndPrefs. static pascal ComponentResult IDHUnregister( IsochComponentInstancePtr iGlobals ) { IsochComponentGlobalsPtr gGlobals; RecordEventLogger( 'isoc', 'ureg', 0, 0); FailIf(iGlobals == nil, Exit); gGlobals = iGlobals->gGlobals; FailIf(gGlobals == nil, Exit); gGlobals->unRegistered = true; Exit: return noErr; } #pragma mark - #pragma mark ееееееееее IDH Component Calls ееееееееее pascal ComponentResult IDHCancelNotification( ComponentInstance ihc, IDHNotificationID notificationID) { OSErr result = noErr; ClientNotification* clientNotification = (ClientNotification*)notificationID; RecordEventLogger( 'isoc', 'canc', 'el ', 'noti'); FailWithAction( clientNotification == nil, result = paramErr, Exit); clientNotification->events = 0; Exit: return result; } pascal ComponentResult IDHCancelPendingIO( ComponentInstance ihc, IDHParameterBlock *pb) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; OSErr result = noErr; QHdrPtr queue = nil; RecordEventLogger( 'isoc', 'canc', 'el ', 'pend'); FailWithAction( pb == nil, result = paramErr, Exit); if( ih->permissions & kIDHOpenForReadTransactions) queue = ih->readQueue; if( ih->permissions & kIDHOpenForWriteTransactions) queue = ih->writeQueue; if( queue) { result = PBDequeue( (QElem *) pb, queue); // FailWithVal( result != noErr, Exit, result); if( result != noErr) goto Exit; } Exit: return result; } pascal ComponentResult IDHCloseDevice( ComponentInstance ihc) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; OSErr result = noErr; IsochComponentGlobalsPtr gGlobals = ih->gGlobals; DeviceDescription *deviceDescriptionPtr = nil; short i; IDHParameterBlock *IORequest; RecordEventLogger( 'isoc', 'clos', ' dev', 0); FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit); if( ih->permissions == 0) // client has no open devices? goto Exit; // flush IO queues while( PBDequeueFirst( ih->readQueue, (QElemPtr *) &IORequest) == noErr) ; while( PBDequeueFirst( ih->writeQueue, (QElemPtr *) &IORequest) == noErr) ; result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr); // find the device FailWithVal( result != noErr, Exit, result); for( i=0; iinstancePtr[i] == ih) { deviceDescriptionPtr->instancePtr[i] = nil; } } FailWithAction( deviceDescriptionPtr->deviceID == nil, result = kIDHErrDeviceNotOpened, Exit); if( ih->permissions & kIDHOpenForReadTransactions) // tear down read { if( --deviceDescriptionPtr->readLocks <= 0) // is device completely freed by clients? { if( deviceDescriptionPtr->readIsochNotificationID) { result = IDHCancelNotification( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID); FailMessage( result != noErr); result = IDHDisposeNotification( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID); FailMessage( result != noErr); deviceDescriptionPtr->readIsochNotificationID = nil; } result = disableRead(ih); FailMessage( result != noErr); deviceDescriptionPtr->exclusiveAccess = false; } } if( ih->permissions & kIDHOpenForWriteTransactions) // tear down write { if( --deviceDescriptionPtr->writeLocks <= 0) // is device completely freed by clients? { if( deviceDescriptionPtr->writeIsochNotificationID) { result = IDHCancelNotification( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID); FailMessage( result != noErr); result = IDHDisposeNotification( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID); FailMessage( result != noErr); deviceDescriptionPtr->writeIsochNotificationID = nil; } result = disableWrite(ih); FailMessage( result != noErr); deviceDescriptionPtr->exclusiveAccess = false; } } result = postEvent( ih->gGlobals, ih->deviceID, (ih->permissions & kIDHOpenForReadTransactions)?kIDHEventReadDisabled:kIDHEventWriteDisabled, nil); FailWithVal( result != noErr, Exit, result); Exit: ih->permissions = 0; // make sure device is closed return result; } pascal ComponentResult IDHDisposeNotification( ComponentInstance ihc, IDHNotificationID notificationID) { OSErr result = noErr; ClientNotification* clientNotification = (ClientNotification*)notificationID; RecordEventLogger( 'isoc', 'disp', 'ose ', 'noti'); FailWithAction( clientNotification == nil, result = paramErr, Exit); clientNotification->deviceID = 0; Exit: return result; } pascal ComponentResult IDHGetDeviceClock( ComponentInstance ihc, Component *clock ) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; DeviceDescription *deviceDescriptionPtr; OSErr result = noErr; RecordEventLogger( 'isoc', 'get ', 'clok', 0); FailWithAction( clock == nil, result = paramErr, Exit); FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit); result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr); FailWithVal( result != noErr, Exit, result); // FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit); *clock = deviceDescriptionPtr->clock; Exit: return result; } //еее USER BETTER KEEP HIS DEVICE LIST AROUND IF THIS IS TO MEAN ANYTHING pascal ComponentResult IDHGetDeviceConfiguration( ComponentInstance ihc, QTAtomSpec *configID ) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; OSErr result = noErr; RecordEventLogger( 'isoc', 'get ', 'dev ', 'conf'); FailWithAction( configID == nil, result = paramErr, Exit); FailWithAction( ih->currentConfig.container == nil || ih->currentConfig.atom == nil, result = kIDHErrDeviceNotConfigured, Exit); *configID = ih->currentConfig; Exit: return result; } pascal ComponentResult IDHGetDeviceControl(ComponentInstance ihc, ComponentInstance *deviceControl) { ComponentResult result = noErr; IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; DeviceDescription *deviceDescriptionPtr; RecordEventLogger( 'isoc', 'get ', 'dev ', 'ctrl'); if ( deviceControl == nil ) return(paramErr); FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit); result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr); FailWithVal( result != noErr, Exit, result); FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit); *deviceControl = deviceDescriptionPtr->deviceControlInstance; FailMessageVal( *deviceControl == nil, (long) ih); Exit: return(result); } //еее USER MUST FREE ATOM LIST WHEN FINISHED pascal ComponentResult IDHGetDeviceList( ComponentInstance ihc, QTAtomContainer *deviceList ) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; IsochComponentGlobalsPtr gGlobals = ih->gGlobals; OSErr result = noErr; QTAtomContainer container = nil; short devIndex; UInt32 version; RecordEventLogger( 'isoc', 'get ', 'dev ', 'list'); FailWithAction( deviceList == nil, result = paramErr, Exit); // create device atom list now result = QTNewAtomContainer( &container); FailWithVal( result != noErr, Exit, result); // save seed result = QTInsertChild( container, kParentAtomIsContainer, kIDHSeedAtomType, 0, 0, sizeof( gGlobals->seed), &gGlobals->seed, nil); FailWithVal( result != noErr, Exit, result); version = (DVVersion << 24) | (DVRevision << 16) | DVBuildNumber; // save isoch version result = QTInsertChild( container, kParentAtomIsContainer, kIDHIsochVersionAtomType, 0, 0, sizeof( UInt32), &version, nil); FailWithVal( result != noErr, Exit, result); // save useCMP value result = QTInsertChild( container, kParentAtomIsContainer, kIDHUseCMPAtomType, 0, 0, sizeof( UInt32), &gGlobals->useCMP, nil); FailWithVal( result != noErr, Exit, result); // save each active device for( devIndex=0; devIndexdeviceDescription[devIndex].deviceID != 0 && gGlobals->deviceDescription[devIndex].active) { result = QTInsertChildren( container, kParentAtomIsContainer, gGlobals->deviceDescription[devIndex].deviceContainer); FailWithVal( result != noErr, Exit, result); } } *deviceList = container; Exit: if( result != noErr && container) { QTRemoveChildren( container, kParentAtomIsContainer); QTDisposeAtomContainer( container); container = nil; } return result; } pascal ComponentResult IDHGetDeviceStatus( ComponentInstance ihc, const QTAtomSpec *devSpec, IDHDeviceStatus *status ) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; IsochComponentGlobalsPtr gGlobals = ih->gGlobals; OSErr result = noErr; IDHDeviceID deviceID = nil; QTAtom deviceInfoAtom, deviceAtom; QTAtomSpec volatileAtomSpec; long size; DeviceDescription *deviceDescriptionPtr; RecordEventLogger( 'isoc', 'get ', 'stat', 0); FailWithAction( devSpec == nil, result = paramErr, Exit); FailWithAction( status == nil, result = paramErr, Exit); volatileAtomSpec = *devSpec; result = checkSeed( ih->gGlobals, &volatileAtomSpec); if( result != noErr) goto Exit; result = getDeviceID( &volatileAtomSpec, &deviceID); FailWithVal( result != noErr, Exit, result); result = findDeviceDescriptionforDevice( ih, deviceID, &deviceDescriptionPtr); // find description for this device FailWithVal( result != noErr, Exit, result); deviceAtom = QTFindChildByIndex( deviceDescriptionPtr->deviceContainer, kParentAtomIsContainer, kIDHDeviceAtomType, 1, nil); FailWithAction( deviceAtom == nil, result = kIDHErrDeviceList, Exit); // find device status for this device deviceInfoAtom = QTFindChildByIndex( deviceDescriptionPtr->deviceContainer, deviceAtom, kDVDeviceInfo, 1, nil); FailWithAction( deviceInfoAtom == nil, result = kIDHErrDeviceList, Exit); QTLockContainer( deviceDescriptionPtr->deviceContainer); // get the value of the mediaType atom QTCopyAtomDataToPtr( deviceDescriptionPtr->deviceContainer, deviceInfoAtom, true, sizeof( IDHDeviceStatus), status, &size); QTUnlockContainer( deviceDescriptionPtr->deviceContainer); status->version = 100; status->physicallyConnected = true; status->readEnabled = deviceDescriptionPtr->readLocks; status->writeEnabled = deviceDescriptionPtr->writeLocks; status->exclusiveAccess = deviceDescriptionPtr->exclusiveAccess; status->currentBandwidth = 0; status->currentChannel = 0; //еее need to make this work with camera tracking status->deviceActive = deviceDescriptionPtr->active; status->inputStandard = deviceDescriptionPtr->standard; // JKL *** what to to with this? does this mean deviceID, cameraFWClientID, or localNodeFWClientID // Think this is for clock to set the localFWReferenceID status->localNodeID = (PsuedoID) deviceDescriptionPtr->localFWReferenceID; result = QTSetAtomData( deviceDescriptionPtr->deviceContainer, deviceInfoAtom, sizeof( IDHDeviceStatus), status); FailWithVal( result != noErr, Exit, result); Exit: return result; } pascal ComponentResult IDHNewNotification( ComponentInstance ihc, IDHDeviceID deviceID, IDHNotificationProc notificationProc, void *userData, IDHNotificationID *notificationID) { IsochComponentGlobalsPtr gGlobals = ((IsochComponentInstancePtr)ihc)->gGlobals; UInt32 i; Boolean addedClient = false; OSErr result = noErr; RecordEventLogger( 'isoc', 'new ', 'noti', 'fy '); FailWithAction( notificationProc == nil, result = paramErr, Exit); FailWithAction( notificationID == nil, result = paramErr, Exit); i = 0; while (i < kMaxNotifications) { if (0 == gGlobals->clientNotification[i].deviceID) { gGlobals->clientNotification[i].ihc = ihc; gGlobals->clientNotification[i].deviceID = deviceID; gGlobals->clientNotification[i].notificationProc = notificationProc; gGlobals->clientNotification[i].events = 0; gGlobals->clientNotification[i].userData = userData; *notificationID = (UInt32)&gGlobals->clientNotification[i]; addedClient = true; break; } ++i; } if (!addedClient) // List is full. Unable to add addtional clients result = paramErr; Exit: return result; } pascal ComponentResult IDHNotifyMeWhen( ComponentInstance ihc, IDHNotificationID notificationID, IDHEvent events ) { OSErr result = noErr; ClientNotification* clientNotification = (ClientNotification*)notificationID; RecordEventLogger( 'isoc', 'noti', 'when', events); FailWithAction( clientNotification == nil, result = paramErr, Exit); clientNotification->events = events; Exit: return result; } pascal ComponentResult IDHOpenDevice( ComponentInstance ihc, UInt32 permissions) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; IsochComponentGlobalsPtr gGlobals = ih->gGlobals; OSErr result = noErr; short i; DeviceDescription *deviceDescriptionPtr = nil; Component devcComp = 0; ComponentInstance devc = 0; RecordEventLogger( 'isoc', 'open', ' dev', 0); FailWithAction( permissions == 0, result = paramErr, Exit); FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit); result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr); FailWithVal( result != noErr, Exit, result); FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit); // trying to reopen for read or write? if( ih->permissions & kIDHOpenForReadTransactions && permissions & kIDHOpenForReadTransactions) goto Exit; // don't do anything if( ih->permissions & kIDHOpenForWriteTransactions && permissions & kIDHOpenForWriteTransactions) goto Exit; // don't do anything // check for switching directions before opening FailWithAction( ih->permissions & kIDHOpenForReadTransactions && permissions & kIDHOpenForWriteTransactions, result = kIDHErrDeviceInUse, Exit); //еее FailWithAction( ih->permissions & kIDHOpenForWriteTransactions && permissions & kIDHOpenForReadTransactions, result = kIDHErrDeviceInUse, Exit); //еее // make sure device wasn't already opened for exclusive access FailWithAction( deviceDescriptionPtr->exclusiveAccess == true, result = kIDHErrDeviceInUse, Exit); if( permissions & kIDHOpenForReadTransactions) // add buffers to queue for read devices { result = addBuffersToQueue( deviceDescriptionPtr); FailWithVal( result != noErr, Exit, result); } // if user is opening for read, make sure device isn't already opened for writes if( permissions & kIDHOpenForReadTransactions) { FailWithAction( deviceDescriptionPtr->writeLocks, result = kIDHErrDeviceInUse, Exit); if( deviceDescriptionPtr->readLocks == 0) { result = enableRead(ih); FailWithVal( result != noErr, Exit, result); } ++deviceDescriptionPtr->readLocks; // keep track of read count } // if user is opening for write, make sure device isn't already opened if( permissions & kIDHOpenForWriteTransactions) { FailWithAction( deviceDescriptionPtr->readLocks || deviceDescriptionPtr->writeLocks, result = kIDHErrDeviceInUse, Exit); result = enableWrite(ih); FailWithVal( result != noErr, Exit, result); deviceDescriptionPtr->writeLocks = 1; // keep track of write count } if( permissions & kIDHOpenWithExclusiveAccess) deviceDescriptionPtr->exclusiveAccess = true; ih->permissions = permissions; // save the permissions // see if this client has already been registered for this device for( i=0; iinstancePtr[i] == ih) break; if( i >= kMaxInstances) // client not registered? { for( i=0; iinstancePtr[i] == nil) // find an empty slot, and add client { deviceDescriptionPtr->instancePtr[i] = ih; break; } } FailWithAction( i >= kMaxInstances, result = kIDHErrInvalidIndex, Exit); // no slots available } result = postEvent( ih->gGlobals, ih->deviceID, (permissions & kIDHOpenForReadTransactions)?kIDHEventReadEnabled:kIDHEventWriteEnabled, nil); FailWithVal( result != noErr, Exit, result); Exit: return result; } // can be called synchronously at task level only pascal ComponentResult IDHRead( ComponentInstance ihc, IDHParameterBlock *pb) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; OSErr result = noErr; DeviceDescription *deviceDescriptionPtr; Boolean synchronous = false; IsochComponentGlobalsPtr gGlobals = ih->gGlobals; RecordEventLogger( 'isoc', 'read', (unsigned long) ih->readQueue, (unsigned long) pb); FailWithAction( pb == nil, result = paramErr, Exit); // can't do sync reads at non-task level FailWithAction( pb->completionProc == nil && CurrentExecutionLevel() != kTaskLevel, result = paramErr, Exit); FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit); result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr); FailWithVal( result != noErr, Exit, result); FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit); if( deviceDescriptionPtr->readIsochNotificationID == nil) { result = IDHNewNotification( (ComponentInstance) gGlobals->localInstance, ih->deviceID, readNotifyProc, (void *) ih->gGlobals, &deviceDescriptionPtr->readIsochNotificationID); FailWithVal( result != noErr, Exit, result); } if ( deviceDescriptionPtr->readIsochNotificationID ) { result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID, kIDHPrivateEventReadComplete); FailWithVal( result != noErr, Exit, result); } if( pb->completionProc == nil) // synchronous read { synchronous = true; pb->completionProc = localCompletionRoutine; pb->refCon = deviceDescriptionPtr; deviceDescriptionPtr->complete = false; } PBEnqueueLast( (QElemPtr) pb, ih->readQueue); // queue the read if( synchronous) // synchronous read { AbsoluteTime expirationTime; TimerID theTimer; ih->timeout = false; // expiration is current time + 1/15 second (2 frame times) expirationTime = AddDurationToAbsolute( kTimeoutDuration, UpTime()); result = SetInterruptTimer( &expirationTime, myTimerHandler, ih, &theTimer); FailWithVal( result != noErr, Exit, result); while( deviceDescriptionPtr->complete == false && ih->timeout == false) // loop until complete or timeout ; expirationTime = DurationToAbsolute( 0); CancelTimer( theTimer, &expirationTime); pb->completionProc = nil; FailWithAction( ih->timeout == true, result = kIDHErrDeviceTimeout, Exit); //еее correct error code } Exit: return result; } pascal ComponentResult IDHReleaseBuffer( ComponentInstance ihc, IDHParameterBlock *pb) { OSErr result = noErr; IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; DeviceDescription *deviceDescriptionPtr; QueueElement *qep; RecordEventLogger( 'isoc', 'rele', 'ase ', 'buff'); FailWithAction( pb == nil, result = paramErr, Exit); FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit); result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr); FailWithVal( result != noErr, Exit, result); qep = (QueueElement *) deviceDescriptionPtr->usedQueue->qHead; while( qep) // find this buffer in the list of used buffers { if( qep->buffer == pb->buffer) // is this our buffer? { if( --qep->locks <= 0) // are all of the locks gone? { qep->locks = 0; // remove from used queue PBDequeue( (QElemPtr) qep, deviceDescriptionPtr->usedQueue); // enqueue this list in the free queue PBEnqueueLast( (QElemPtr) qep, deviceDescriptionPtr->freeQueue); } break; } // check next element in the used queue qep = (QueueElement *) qep->qLink; } Exit: return result; } pascal ComponentResult IDHSetDeviceConfiguration( ComponentInstance ihc, const QTAtomSpec *configID) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; OSErr result = noErr; IsochComponentGlobalsPtr gGlobals = ih->gGlobals; QTAtomSpec volatileAtomSpec; IDHDeviceID previousDeviceID; DeviceDescription *deviceDescriptionPtr; RecordEventLogger( 'isoc', 'set ', 'dev ', 'conf'); FailWithAction( configID == nil, result = paramErr, Exit); FailWithAction( configID->container == nil, result = paramErr, Exit); // if the client is setting to the same config, then we are ok if( configID->container == ih->currentConfig.container && configID->atom == ih->currentConfig.atom) goto Exit; // device already in use, please close device first FailWithAction( ih->permissions != 0, result = kIDHErrDeviceInUse, Exit); previousDeviceID = ih->deviceID; volatileAtomSpec = *configID; result = getDeviceID( &volatileAtomSpec, &ih->deviceID); FailWithVal( result != noErr, Exit, result); result = checkSeed( ih->gGlobals, &volatileAtomSpec); FailWithVal( result != noErr, Exit, result); ih->currentConfig = *configID; result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr); FailWithVal( result != noErr, Exit, result); FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit); // if this client already has device control, free the previous connection if( previousDeviceID && ih->hasDeviceControl) { DeviceDescription *deviceDescriptionPtr2; result = findDeviceDescriptionforDevice( ih, previousDeviceID, &deviceDescriptionPtr2); FailWithVal( result != noErr, Exit, result); result = closeDeviceControl( ihc, deviceDescriptionPtr2); FailMessage( result != noErr); ih->hasDeviceControl = false; } // ======================================================================= // All should be good here from a device stand point, now open the device // control component. If the devc is not nil either we didn't release it // from close or the same client is calling open again. if ( deviceDescriptionPtr->deviceControlInstance == nil) { ComponentDescription devcDesc; Component devcComp; ComponentInstance devc; devcDesc.componentType = kDeviceControlComponentType; devcDesc.componentSubType = kDeviceControlSubtypeFWDV; devcDesc.componentManufacturer = 0; devcDesc.componentFlags = 0L; devcDesc.componentFlagsMask = 0L; devcComp = FindNextComponent( devcComp, &devcDesc); FailMessage( devcComp == nil); if ( devcComp ) { result = OpenAComponent(devcComp, &devc); FailWithVal( result != noErr, Exit, result); result = DeviceControlSetDeviceConnectionID(devc, (DeviceConnectionID) deviceDescriptionPtr->fwClientID); //еее deviceID); FailWithVal( result != noErr, Exit, result); result = DeviceControlEnableAVCTransactions(devc); FailWithVal( result != noErr, Exit, result); deviceDescriptionPtr->deviceControlInstance = devc; // now sign up for callback in case device is added or removed and we need to update connectionID if( deviceDescriptionPtr->deviceControlNotificationID == nil) { result = IDHNewNotification( (ComponentInstance) gGlobals->localInstance, ih->deviceID, deviceControlCallback, deviceDescriptionPtr, &deviceDescriptionPtr->deviceControlNotificationID); FailWithVal( result != noErr, Exit, result); } if ( deviceDescriptionPtr->deviceControlNotificationID ) { result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->deviceControlNotificationID, kIDHEventDeviceAdded | kIDHEventDeviceRemoved); FailWithVal( result != noErr, Exit, result); } } } if( ih->hasDeviceControl == false && deviceDescriptionPtr->deviceControlInstance != nil) { ih->hasDeviceControl = true; ++deviceDescriptionPtr->deviceControlCount; } Exit: return result; } pascal ComponentResult IDHUpdateDeviceList( ComponentInstance ihc, QTAtomContainer *deviceList ) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; IsochComponentGlobalsPtr gGlobals = ih->gGlobals; OSErr result = noErr; QTAtomContainer container = nil; short nDVDevices, i; QTAtomSpec atomSpec; RecordEventLogger( 'isoc', 'updt', 'dev ', 'list'); FailWithAction( deviceList == nil, result = paramErr, Exit); atomSpec.container = *deviceList; result = checkSeed( gGlobals, &atomSpec); // make sure the container is current if( result != noErr) goto Exit; // check for useCMP value changing if (result == noErr) { QTAtom useCMPAtom; UInt32 useCMPValue; useCMPAtom = QTFindChildByIndex(*deviceList, kParentAtomIsContainer, kIDHUseCMPAtomType, 1, nil); if (useCMPAtom) { QTLockContainer(*deviceList); result = QTCopyAtomDataToPtr(*deviceList, useCMPAtom, true, 4, &useCMPValue, nil); QTUnlockContainer(*deviceList); if (result == noErr) gGlobals->useCMP = useCMPValue; } } // move all volatile atoms here nDVDevices = QTCountChildrenOfType( *deviceList, kParentAtomIsContainer, kIDHDeviceAtomType); for( i=0; ideviceContainer, kParentAtomIsContainer, kIDHDeviceAtomType, 1, nil); FailIf( deviceAtomOld == nil, Exit); nameAtomOld = QTFindChildByIndex( deviceDescriptionPtr->deviceContainer, deviceAtomOld, kIDHNameAtomType, 1, nil); FailIf( nameAtomOld == nil, Exit); // get new name QTLockContainer( *deviceList); result = QTCopyAtomDataToPtr(*deviceList, nameAtomNew, true, 256, newName, &actualSize); QTUnlockContainer( *deviceList); FailWithVal( result != noErr, Exit, result); // update prefs file with new name // JKL, don't want to do this for now since FCP might be using this routine to toggle CMP // updateCameraName(&deviceDescriptionPtr->uniqueID, newName); // copy the new data into the current atom result = QTReplaceAtom( deviceDescriptionPtr->deviceContainer, nameAtomOld, *deviceList, nameAtomNew); FailWithVal( result != noErr, Exit, result); } Exit: return result; } // can be called synchronously at task level only pascal ComponentResult IDHWrite( ComponentInstance ihc, IDHParameterBlock *pb) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; OSErr result = noErr; DeviceDescription *deviceDescriptionPtr; Boolean synchronous = false; IsochComponentGlobalsPtr gGlobals = ih->gGlobals; RecordEventLogger( 'isoc', 'writ', 0, 0); FailWithAction( pb == nil, result = paramErr, Exit); // can't do sync writes at non-task level FailWithAction( pb->completionProc == nil && CurrentExecutionLevel() != kTaskLevel, result = paramErr, Exit); // check for illegal condition FailWithAction( pb->completionProc == nil && pb->buffer == nil, result = paramErr, Exit); FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit); result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr); FailWithVal( result != noErr, Exit, result); FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit); if( deviceDescriptionPtr->writeIsochNotificationID == nil) { result = IDHNewNotification( (ComponentInstance) gGlobals->localInstance, ih->deviceID, writeNotifyProc, (void *) ih->gGlobals, &deviceDescriptionPtr->writeIsochNotificationID ); FailWithVal( result != noErr, Exit, result); } if ( deviceDescriptionPtr->writeIsochNotificationID ) { result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID, kIDHPrivateEventWriteComplete); FailWithVal( result != noErr, Exit, result); } if( pb->completionProc == nil) // synchronous write { synchronous = true; pb->completionProc = localCompletionRoutine; pb->refCon = deviceDescriptionPtr; deviceDescriptionPtr->complete = false; } PBEnqueueLast( (QElemPtr) pb, ih->writeQueue); if( synchronous) // synchronous write { AbsoluteTime expirationTime; TimerID theTimer; ih->timeout = false; // expiration is current time + 1/15 second (2 frame times) expirationTime = AddDurationToAbsolute( kTimeoutDuration, UpTime()); result = SetInterruptTimer( &expirationTime, myTimerHandler, ih, &theTimer); FailWithVal( result != noErr, Exit, result); while( deviceDescriptionPtr->complete == false && ih->timeout == false) // loop until complete or timeout ; expirationTime = DurationToAbsolute( 0); CancelTimer( theTimer, &expirationTime); pb->completionProc = nil; FailWithAction( ih->timeout == true, result = kIDHErrDeviceTimeout, Exit); //еее correct error code } Exit: return result; } #pragma mark - #pragma mark ееееееееее IDHDV Private Component Calls ееееееееее pascal ComponentResult IDHDVPrivateCall( ComponentInstance ihc, IDHDVPrivateSelector selector, void *privateData) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; OSErr result = noErr; switch (selector) { case kIDHDVPrivateSelectorDeviceAdded: RecordEventLogger( 'isoc', 'get ', 'priv', 'dadd'); result = handleDeviceAdded(ihc, (GDFDeviceEventData*) privateData); break; case kIDHDVPrivateSelectorDeviceRemoved: RecordEventLogger( 'isoc', 'get ', 'priv', 'remv'); result = handleDeviceRemoved(ihc, (GDFDeviceEventData*) privateData); break; } return result; } #pragma mark - #pragma mark ееееееееее IDH Internal Calls ееееееееее OSErr addBuffersToQueue( DeviceDescription *deviceDescriptionPtr) { QElemPtr qelem; OSErr result = noErr; QueueElement *qep; short j; if( countQueueElements( deviceDescriptionPtr->freeQueue) > 0 || countQueueElements( deviceDescriptionPtr->usedQueue) > 0) // are there already buffers? { while( PBDequeueFirst( deviceDescriptionPtr->usedQueue, &qelem) == noErr) // move buffers from used to free queue { ((QueueElement *)qelem)->locks = 0; PBEnqueueLast( qelem, deviceDescriptionPtr->freeQueue); } } else { for( j=0; jbuffer = NewPtrSys( (deviceDescriptionPtr->standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize); FailWithAction( qep->buffer == nil, result = MemError(), Exit); qep->locks = 0; // initialize to no client locks PBEnqueueLast( (QElemPtr) qep, deviceDescriptionPtr->freeQueue); } deviceDescriptionPtr->nQueueElements = kBuffersPerDevice; // save number of buffers } Exit: return result; BadExit: // dispose buffers if error while( PBDequeueFirst( deviceDescriptionPtr->freeQueue, (QElemPtr *) &qep) == noErr) { if( qep) { if( qep->buffer) DisposePtr( qep->buffer); DisposePtr( (char *) qep); } } while( PBDequeueFirst( deviceDescriptionPtr->usedQueue, (QElemPtr *) &qep) == noErr) { if( qep) { if( qep->buffer) DisposePtr( qep->buffer); DisposePtr( (char *) qep); } } deviceDescriptionPtr->nQueueElements = 0; return result; } OSErr closeDeviceControl( ComponentInstance ihc, DeviceDescriptionPtr deviceDescriptionPtr) { OSErr result = noErr; IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; IsochComponentGlobalsPtr gGlobals = ih->gGlobals; if( deviceDescriptionPtr->deviceControlInstance) { if( --deviceDescriptionPtr->deviceControlCount <= 0) { deviceDescriptionPtr->deviceControlCount = 0; result = IDHCancelNotification( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->deviceControlNotificationID); FailMessage( result != noErr); result = IDHDisposeNotification((ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->deviceControlNotificationID); FailMessage( result != noErr); deviceDescriptionPtr->deviceControlNotificationID = 0; result = DeviceControlDisableAVCTransactions(deviceDescriptionPtr->deviceControlInstance); result = DeviceControlSetDeviceConnectionID(deviceDescriptionPtr->deviceControlInstance, 0); CloseComponent(deviceDescriptionPtr->deviceControlInstance); deviceDescriptionPtr->deviceControlInstance = nil; } } Exit: return result; } OSErr checkSeed( IsochComponentGlobalsPtr gGlobals, QTAtomSpec *configID) { QTAtom seedAtom; OSErr result = noErr; UInt32 seed; // look for device in device container seedAtom = QTFindChildByIndex( configID->container, kParentAtomIsContainer, kIDHSeedAtomType, 1, nil); FailWithAction( seedAtom == nil, result = kIDHErrDeviceList, Exit); QTLockContainer( configID->container); // get the value of the devicePresent atom QTCopyAtomDataToPtr( configID->container, seedAtom, true, sizeof( seed), &seed, nil); QTUnlockContainer( configID->container); // seed has expired? if( seed != gGlobals->seed) { result = kIDHErrDeviceList; goto Exit; } Exit: return result; } int countQueueElements( QHdrPtr queue) { QElem *qptr; int nElements = 0; qptr = queue->qHead; while( qptr) { ++nElements; qptr = qptr->qLink; } return nElements; } // called when a device is added/removed and updates device control clientID OSStatus deviceControlCallback( IDHGenericEvent *event, void *userData) { OSErr result = noErr; DeviceDescription *deviceDescriptionPtr = (DeviceDescriptionPtr) userData; IsochComponentGlobalsPtr gGlobals = deviceDescriptionPtr->componentGlobals; RecordEventLogger( 'isoc', 'devc', 'calb', event->eventHeader.event); switch( event->eventHeader.event) { case kIDHEventDeviceAdded: result = DeviceControlDisableAVCTransactions(deviceDescriptionPtr->deviceControlInstance); FailMessageVal( result != noErr, result); result = DeviceControlSetDeviceConnectionID(deviceDescriptionPtr->deviceControlInstance, (DeviceConnectionID) deviceDescriptionPtr->fwClientID); FailWithVal( result != noErr, Exit, result); result = DeviceControlEnableAVCTransactions(deviceDescriptionPtr->deviceControlInstance); FailWithVal( result != noErr, Exit, result); break; case kIDHEventDeviceRemoved: result = DeviceControlDisableAVCTransactions(deviceDescriptionPtr->deviceControlInstance); FailWithVal( result != noErr, Exit, result); break; default: FailMessage( (event->eventHeader.event & (kIDHEventDeviceRemoved | kIDHEventDeviceAdded)) == nil); break; } result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->deviceControlNotificationID, kIDHEventDeviceAdded | kIDHEventDeviceRemoved); FailWithVal( result != noErr, Exit, result); Exit: return result; } OSErr doAVCTransaction( FWReferenceID refID, DVCTransactionParams* inTransaction) { FWCommandObjectID commandObjectID; OSErr result = noErr; FCPResponseHandler *responseHandler = 0; UInt32 fwCommandFlags = kFWCommandSyncFlag; // allocate command object for sending FCP commands synchronously. result = FWAllocateFCPCommandObject(&commandObjectID); FailWithVal( result != noErr, Exit1, result); // Set up FCP command params to tell camera to do something. result = FWSetFWCommandParams( commandObjectID, // objectID refID, // ref ID fwCommandFlags, // cmd flags nil, // completion proc 0); // completion data FailWithVal( result != noErr, Exit, result); result = FWSetFCPCommandParams( commandObjectID, // objectID inTransaction->commandBufferPtr, // cmd buffer inTransaction->commandLength, // cmd length inTransaction->responseBufferPtr, // response buffer inTransaction->responseBufferSize, // response size 100 * durationMillisecond, // timeout 8, // max retries 0, // transfer flags responseHandler ); // response handler FailWithVal( result != noErr, Exit, result); // send the FCP command result = FWSendFCPCommand(commandObjectID); FailWithVal( result != noErr, Exit, result); Exit: FWDeallocateFWCommandObject(commandObjectID); Exit1: return result; } OSErr findDeviceDescriptionforDevice( IsochComponentInstancePtr ih, UInt32 deviceID, DeviceDescription **deviceDescription) { OSErr result = noErr; IsochComponentGlobalsPtr gGlobals = ih->gGlobals; short i; *deviceDescription = nil; for( i=0; ideviceDescription[i].deviceID == deviceID) { *deviceDescription = &gGlobals->deviceDescription[i]; break; } } if( *deviceDescription == nil) result = kIDHErrInvalidDeviceID; Exit: return result; } //еее do a super find atom someday OSErr findAtom( QTAtomSpec *atomSpec, OSType theType, QTAtom *theAtom) { OSErr result = noErr; OSType type; QTAtom atom; atom = atomSpec->atom; result = QTGetAtomTypeAndID( atomSpec->container, atom, (long *) &type, nil); FailWithVal( result != noErr, Exit, result); while( type != kIDHDeviceAtomType && type != theType) { atom = QTGetAtomParent( atomSpec->container, atom); // should be isoch atom FailWithAction( atom == nil || atom == -1, result = kIDHErrDeviceList, Exit); result = QTGetAtomTypeAndID( atomSpec->container, atom, (long *) &type, nil); FailWithVal( result != noErr, Exit, result); } if( theType == type) { *theAtom = atom; result = noErr; } else { *theAtom = nil; result = kIDHErrDeviceList; } Exit: return result; } OSErr getDeviceID( QTAtomSpec *configID, UInt32 *deviceID) { OSErr result = noErr; QTAtom deviceAtom; *deviceID = nil; result = findAtom( configID, kIDHDeviceAtomType, &deviceAtom); FailWithVal( result != noErr, Exit, result); result = QTGetAtomTypeAndID( configID->container, deviceAtom, nil, (long *) deviceID); FailWithVal( result != noErr, Exit, result); Exit: return result; } OSErr getDeviceStandard( FWReferenceID clientID, UInt32 * pStandard ) { AVCCTSFrameStruct avcFrame; DVCTransactionParams transactionParams; UInt8 responseBuffer[ 16 ]; OSErr theErr = noErr; UInt32 currentSignal, AVCStatus; *pStandard = ntscIn; // fill up the avc frame avcFrame.cmdType_respCode = kAVCStatusInquiryCommand; avcFrame.headerAddress = 0x20; // for now avcFrame.opcode = kAVCOutputSignalModeOpcode; avcFrame.operand[ 0 ] = kAVCSignalModeDummyOperand; // fill up the transaction parameter block transactionParams.commandBufferPtr = (Ptr) &avcFrame; transactionParams.commandLength = 4; transactionParams.responseBufferPtr = (Ptr) responseBuffer; transactionParams.responseBufferSize = 16; transactionParams.responseHandler = nil; // some cameras need some time before they can respond to this command DelayFor ( 1 * durationSecond ); theErr = setupAVCTransaction( clientID, &transactionParams ); FailWithVal( theErr != noErr, Exit, theErr); currentSignal = ((responseBuffer[ 2 ] << 8) | responseBuffer[ 3 ]); AVCStatus = responseBuffer[ 0 ]; switch (currentSignal & 0x000000ff) { case kAVCSignalModeSD525_60: case kAVCSignalModeSDL525_60: case kAVCSignalModeHD1125_60: *pStandard = ntscIn; break; case kAVCSignalModeSD625_50: case kAVCSignalModeSDL625_50: case kAVCSignalModeHD1250_50: *pStandard = palIn; break; } Exit: // JKL, because of devices that do not have device control, it is probably not safe // to return an error here. Just default to NTSC. Need a better solution for this: // user input? examining the stream? If this is just for setting the buffer size // might be better to waste 100k in the NTSC case and just use PAL buffer size. if (theErr != noErr) { *pStandard = ntscIn; theErr = noErr; } return theErr; } OSStatus localCompletionRoutine( IDHGenericEvent *event, void *userData) { #pragma unused (event) DeviceDescription *deviceDescriptionPtr = (DeviceDescription *) userData; deviceDescriptionPtr->complete = true; // tell the read/write operation that operation is complete return noErr; } OSStatus myTimerHandler( void *p1, void *p2) { #pragma unused( p2) IsochComponentInstancePtr ih = (IsochComponentInstancePtr) p1; ih->timeout = true; return noErr; } OSErr postEvent(IsochComponentGlobals* gGlobals, IDHDeviceID deviceID, IDHEvent event, void* privateData) { UInt32 i; IDHDeviceConnectionEvent connectionEvent; OSErr error = noErr; // We now have two broad classifications of events - ones that need to be // reported ASAP, which are stream related: // // kIDHPrivateEventReadComplete // kIDHPrivateEventWriteComplete // // and ones that are device management related, whose notifications will // probably generate massive amounts of task-level only Toolbox calls: // // kIDHEventDeviceAdded // kIDHEventDeviceRemoved // kIDHEventReadEnabled // kIDHEventReadDisabled // kIDHEventWriteEnabled // kIDHEventWriteDisabled // // kIDHPrivateEventReadComplete and kIDHPrivateEventWriteComplete are posted to a secondary // interrupt handler. All other events are handled immediately. RecordEventLogger( 'isoc', 'post', deviceID, event); for (i = 0; i < kMaxNotifications ; ++i) { ClientNotification* clientNotification = &gGlobals->clientNotification[i]; IDHDeviceID clientDeviceID = clientNotification->deviceID; IDHEvent wantedEvents = clientNotification->events; if (event & wantedEvents) { // Send notification if the device matches the client's desired device // or client specified all devices. if ((kIDHDeviceIDEveryDevice == clientDeviceID) || (deviceID == clientDeviceID) ) { // we currently only support a one-shot notification, like clock callbacks clientNotification->events = 0; switch(event) { case kIDHPrivateEventReadComplete: case kIDHPrivateEventWriteComplete: (*clientNotification->notificationProc) ((IDHGenericEvent*)privateData, clientNotification->userData); break; case kIDHEventDeviceChanged: case kIDHEventDeviceAdded: case kIDHEventDeviceRemoved: case kIDHEventReadEnabled: case kIDHEventReadDisabled: case kIDHEventWriteEnabled: case kIDHEventWriteDisabled: connectionEvent.eventHeader.event = event; connectionEvent.eventHeader.deviceID = deviceID; connectionEvent.eventHeader.notificationID = (UInt32)clientNotification; (*clientNotification->notificationProc) ((IDHGenericEvent*)&connectionEvent, clientNotification->userData); break; default: RecordEventLogger( 'isoc', 'post', '????', event); break; } } } } return error; } OSStatus postEventSIH(void* p1, void* p2) { ClientNotification *clientNotification = (ClientNotification *) p1; IDHGenericEvent *privateData = (IDHGenericEvent *) p2; OSStatus status = noErr; // call the routine (*clientNotification->notificationProc) (privateData, clientNotification->userData); return status; } OSStatus readNotifyProc( IDHGenericEvent *event, void *userData) { OSErr result = noErr; IsochComponentGlobalsPtr gGlobals = userData; IDHDVCompleteEvent* readCompletePtr = (IDHDVCompleteEvent*) event; DeviceDescription *deviceDescriptionPtr = nil; short i; QueueElementPtr qep; RecordEventLogger( 'isoc', 'read', 'comp', ((char *) readCompletePtr->frameBuffer)[640] & 0xf); FailIf( event->eventHeader.event != kIDHPrivateEventReadComplete, Exit); // find the device description that belongs to this read callback deviceDescriptionPtr = nil; for( i=0; ideviceDescription[i].deviceID == event->eventHeader.deviceID) { deviceDescriptionPtr = &gGlobals->deviceDescription[i]; break; } } FailIf( deviceDescriptionPtr == nil, Exit); // couldn't find device in tree // find a buffer to copy the data into if( (result = PBDequeueFirst( deviceDescriptionPtr->freeQueue, (QElemPtr *) &qep)) != noErr) if( (result = PBDequeueFirst( deviceDescriptionPtr->usedQueue, (QElemPtr *) &qep)) != noErr) //еее changes when lock { FailWithVal( result != noErr, Exit, result); // no queue elems available } qep->locks = 0; // reset lock // make sure we have enuf room FailIf( readCompletePtr->bufferSize > kPALCompressedBufferSize, Exit); // (deviceDescriptionPtr->standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize, Exit); // copy the data and save the size BlockMoveData( readCompletePtr->frameBuffer, qep->buffer, readCompletePtr->bufferSize); qep->size = readCompletePtr->bufferSize; // find all of instances that need to be serviced for this device for( i=0; iinstancePtr[i]; IDHParameterBlock *readRequest; if( deviceDescriptionPtr->instancePtr[i] == nil) // no instance? continue; // get a read request if( PBDequeueFirst( ih->readQueue, (QElemPtr *) &readRequest) != noErr) continue; readRequest->actualCount = 0; readRequest->result = noErr; // does the user want an internal buffer or copy to user buffer if( readRequest->buffer == nil) { RecordEventLogger( 'isoc', 'redc', 'read', 'nil '); readRequest->buffer = readCompletePtr->frameBuffer; // return internal buffer readRequest->actualCount = readCompletePtr->bufferSize; ++qep->locks; // add a lock to this buffer } else { RecordEventLogger( 'isoc', 'redc', 'read', 'buff'); if( readCompletePtr->bufferSize <= readRequest->requestedCount) // do we have enuf room? { // copy the buffer into client buffer BlockMoveData( readCompletePtr->frameBuffer, readRequest->buffer, readCompletePtr->bufferSize); readRequest->actualCount = readCompletePtr->bufferSize; } } // call the client callback result = (*readRequest->completionProc)( (IDHGenericEvent *) readRequest, readRequest->refCon); } // end of processing instances if( qep->locks > 0) // if this buffer has locks, move it to the usedQueue else requeue on free queue PBEnqueueLast( (QElemPtr) qep, deviceDescriptionPtr->usedQueue); else PBEnqueueLast( (QElemPtr) qep, deviceDescriptionPtr->freeQueue); // reset read complete notification result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID, kIDHPrivateEventReadComplete); Exit: return result; } OSErr setupAVCTransaction( FWReferenceID refID, DVCTransactionParams *pParams ) { DVCTransactionParams transactionParams; OSErr error = noErr; // fill out the internal transaction block transactionParams.commandBufferPtr = pParams->commandBufferPtr; transactionParams.commandLength = pParams->commandLength; transactionParams.responseBufferPtr = pParams->responseBufferPtr; transactionParams.responseBufferSize = pParams->responseBufferSize; transactionParams.responseHandler = nil; //еее pParams->responseHandler; error = doAVCTransaction( refID, &transactionParams ); FailWithVal( error != noErr, Exit, error); Exit: return( error ); } OSErr setupVideoAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard) // add DV NTSC config { OSErr result = noErr; QTAtom configAtom; OSType type; long size; float interval; long direction; IDHDimension dimension; IDHResolution resolution; Fixed refresh; OSType pixel; OSType decoType; Component decoComponent; ComponentDescription compDescrip; // create a vide NTSC mode result = QTInsertChild( container, isocAtom, kIDHIsochModeAtomType, 0, 0, 0, nil, &configAtom); FailWithVal( result != noErr, Exit, result); type = kIDHVideoMediaAtomType; result = QTInsertChild( container, configAtom, kIDHIsochMediaType, 0, 0, sizeof( type), &type, nil); FailWithVal( result != noErr, Exit, result); // for compatibility with Hemingway releases prior to our d13 release result = QTInsertChild( container, configAtom, kIDHNameAtomType, 0, 0, 3, "\pDV", nil); FailWithVal( result != noErr, Exit, result); type = (standard == ntscIn)?'DVC ':'DVCP'; result = QTInsertChild( container, configAtom, kIDHDataTypeAtomType, 0, 0, sizeof( type), &type, nil); FailWithVal( result != noErr, Exit, result); size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize; result = QTInsertChild( container, configAtom, kIDHDataSizeAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( result != noErr, Exit, result); size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize; result = QTInsertChild( container, configAtom, kIDHDataBufferSizeAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( result != noErr, Exit, result); interval = 29.97; result = QTInsertChild( container, configAtom, kIDHDataIntervalAtomType, 0, 0, sizeof( interval), &interval, nil); FailWithVal( result != noErr, Exit, result); direction = kIDHDataTypeIsInputAndOutput; result = QTInsertChild( container, configAtom, kIDHDataIODirectionAtomType, 0, 0, sizeof( direction), &direction, nil); FailWithVal( result != noErr, Exit, result); dimension.x = 720; dimension.y = (standard == ntscIn)?480:576; result = QTInsertChild( container, configAtom, kIDHVideoDimensionsAtomType, 0, 0, sizeof( dimension), &dimension, nil); FailWithVal( result != noErr, Exit, result); resolution.x = 72 << 16; resolution.y = 72 << 16; result = QTInsertChild( container, configAtom, kIDHVideoResolutionAtomType, 0, 0, sizeof( resolution), &resolution, nil); FailWithVal( result != noErr, Exit, result); refresh = (29 << 16) + 97; //еее result = QTInsertChild( container, configAtom, kIDHVideoRefreshRateAtomType, 0, 0, sizeof( refresh), &refresh, nil); FailWithVal( result != noErr, Exit, result); pixel = 'dv '; //еее result = QTInsertChild( container, configAtom, kIDHVideoPixelTypeAtomType, 0, 0, sizeof( pixel), &pixel, nil); FailWithVal( result != noErr, Exit, result); //еее kIDHVideoDecompressorsAtomType = FOUR_CHAR_CODE('deco'), decoType = (standard == ntscIn)?'dvc ':'dvcp'; result = QTInsertChild( container, configAtom, kIDHVideoDecompressorTypeAtomType, 0, 0, sizeof( decoType), &decoType, nil); FailWithVal( result != noErr, Exit, result); compDescrip.componentType = 'imdc'; compDescrip.componentSubType = decoType; compDescrip.componentManufacturer = 0; compDescrip.componentFlags = 0; compDescrip.componentFlagsMask = 0; decoComponent = FindNextComponent( nil, &compDescrip); result = QTInsertChild( container, configAtom, kIDHVideoDecompressorComponentAtomType, 0, 0, sizeof( decoComponent), &decoComponent, nil); FailWithVal( result != noErr, Exit, result); Exit: return result; } OSErr setup48kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard) { OSErr err; QTAtom configAtom; StringPtr name; OSType type; long size; Fixed rate; float interval; long direction; // create a vide NTSC mode err = QTInsertChild( container, isocAtom, kIDHIsochModeAtomType, 0, 0, 0, nil, &configAtom); FailWithVal( err != noErr, Exit, err); type = kIDHSoundMediaAtomType; err = QTInsertChild( container, configAtom, kIDHIsochMediaType, 0, 0, sizeof( type), &type, nil); FailWithVal( err != noErr, Exit, err); name = "\pDV-48khz"; err = QTInsertChild( container, configAtom, kIDHNameAtomType, 0, 0, name[0], name, nil); FailWithVal( err != noErr, Exit, err); type = 'DV48'; err = QTInsertChild( container, configAtom, kIDHDataTypeAtomType, 0, 0, sizeof( type), &type, nil); FailWithVal( err != noErr, Exit, err); size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize; err = QTInsertChild( container, configAtom, kIDHDataSizeAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( err != noErr, Exit, err); size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize; err = QTInsertChild( container, configAtom, kIDHDataBufferSizeAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( err != noErr, Exit, err); interval = 29.97; err = QTInsertChild( container, configAtom, kIDHDataIntervalAtomType, 0, 0, sizeof( interval), &interval, nil); FailWithVal( err != noErr, Exit, err); direction = kIDHDataTypeIsInputAndOutput; err = QTInsertChild( container, configAtom, kIDHDataIODirectionAtomType, 0, 0, sizeof( direction), &direction, nil); FailWithVal( err != noErr, Exit, err); size = 2; err = QTInsertChild( container, configAtom, kIDHSoundChannelCountAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( err != noErr, Exit, err); size = 2; err = QTInsertChild( container, configAtom, kIDHSoundSampleSizeAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( err != noErr, Exit, err); rate = rate44khz; err = QTInsertChild( container, configAtom, kIDHSoundSampleRateAtomType, 0, 0, sizeof( rate), &rate, nil); FailWithVal( err != noErr, Exit, err); Exit: return err; } // sound 1 config OSErr setup32kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard) { OSErr err; QTAtom configAtom; StringPtr name; OSType type; long size; Fixed rate; float interval; long direction; // create a vide NTSC mode err = QTInsertChild( container, isocAtom, kIDHIsochModeAtomType, 0, 0, 0, nil, &configAtom); FailWithVal( err != noErr, Exit, err); type = kIDHSoundMediaAtomType; err = QTInsertChild( container, configAtom, kIDHIsochMediaType, 0, 0, sizeof( type), &type, nil); FailWithVal( err != noErr, Exit, err); name = "\pDV-32khz"; err = QTInsertChild( container, configAtom, kIDHNameAtomType, 0, 0, name[0], name, nil); FailWithVal( err != noErr, Exit, err); type = 'DV32'; err = QTInsertChild( container, configAtom, kIDHDataTypeAtomType, 0, 0, sizeof( type), &type, nil); FailWithVal( err != noErr, Exit, err); size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize; err = QTInsertChild( container, configAtom, kIDHDataSizeAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( err != noErr, Exit, err); size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize; err = QTInsertChild( container, configAtom, kIDHDataBufferSizeAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( err != noErr, Exit, err); interval = 29.97; err = QTInsertChild( container, configAtom, kIDHDataIntervalAtomType, 0, 0, sizeof( interval), &interval, nil); FailWithVal( err != noErr, Exit, err); direction = kIDHDataTypeIsInputAndOutput; err = QTInsertChild( container, configAtom, kIDHDataIODirectionAtomType, 0, 0, sizeof( direction), &direction, nil); FailWithVal( err != noErr, Exit, err); size = 4; err = QTInsertChild( container, configAtom, kIDHSoundChannelCountAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( err != noErr, Exit, err); size = 2; err = QTInsertChild( container, configAtom, kIDHSoundSampleSizeAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( err != noErr, Exit, err); rate = 32000 << 16; err = QTInsertChild( container, configAtom, kIDHSoundSampleRateAtomType, 0, 0, sizeof( rate), &rate, nil); FailWithVal( err != noErr, Exit, err); Exit: return err; } // sound 2 config OSErr setup44kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard) { OSErr err; QTAtom configAtom; StringPtr name; OSType type; long size; Fixed rate; float interval; long direction; // create a vide NTSC mode err = QTInsertChild( container, isocAtom, kIDHIsochModeAtomType, 0, 0, 0, nil, &configAtom); FailWithVal( err != noErr, Exit, err); type = kIDHSoundMediaAtomType; err = QTInsertChild( container, configAtom, kIDHIsochMediaType, 0, 0, sizeof( type), &type, nil); FailWithVal( err != noErr, Exit, err); name = "\pDV-44khz"; err = QTInsertChild( container, configAtom, kIDHNameAtomType, 0, 0, name[0], name, nil); FailWithVal( err != noErr, Exit, err); type = 'DV44'; err = QTInsertChild( container, configAtom, kIDHDataTypeAtomType, 0, 0, sizeof( type), &type, nil); FailWithVal( err != noErr, Exit, err); size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize; err = QTInsertChild( container, configAtom, kIDHDataSizeAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( err != noErr, Exit, err); size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize; err = QTInsertChild( container, configAtom, kIDHDataBufferSizeAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( err != noErr, Exit, err); interval = 29.97; err = QTInsertChild( container, configAtom, kIDHDataIntervalAtomType, 0, 0, sizeof( interval), &interval, nil); FailWithVal( err != noErr, Exit, err); direction = kIDHDataTypeIsInputAndOutput; err = QTInsertChild( container, configAtom, kIDHDataIODirectionAtomType, 0, 0, sizeof( direction), &direction, nil); FailWithVal( err != noErr, Exit, err); size = 4; err = QTInsertChild( container, configAtom, kIDHSoundChannelCountAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( err != noErr, Exit, err); size = 2; err = QTInsertChild( container, configAtom, kIDHSoundSampleSizeAtomType, 0, 0, sizeof( size), &size, nil); FailWithVal( err != noErr, Exit, err); rate = 44100 << 16; err = QTInsertChild( container, configAtom, kIDHSoundSampleRateAtomType, 0, 0, sizeof( rate), &rate, nil); FailWithVal( err != noErr, Exit, err); Exit: return err; } // sound 3 config OSErr updateDeviceList(IsochComponentInstancePtr ih, GDFDeviceEventData *pGDFData, IDHDeviceID *deviceID, Boolean doAdd) { OSErr result = noErr; QTAtom deviceAtom, isocAtom; IsochComponentGlobalsPtr gGlobals = ih->gGlobals; Boolean listChanged = false, foundDevice = false; DeviceDescription *deviceDescriptionPtr; IDHDeviceStatus deviceStatus; UInt32 standard; UInt32 devIndex; FWReferenceID localFWReferenceID; ComponentDescription clkDesc; RecordEventLogger( 'isoc', 'updt', pGDFData->driverRefNum, 0); ++gGlobals->seed; if (doAdd) { // The refNum of a driver is NOT a constant IF the driver is allowed to close // once it has been opened. Our expert never allows the driver to be closed. // So use that refNum to search for the device in. // The same provision holds true for the RegEntryID so we could do that. // look for existing device for( devIndex=0; devIndex < gGlobals->nDevices; ++devIndex) { if (pGDFData->driverRefNum == gGlobals->deviceDescription[devIndex].refNum) { FWDriverID clientID = gGlobals->deviceDescription[devIndex].fwClientID; gGlobals->deviceDescription[devIndex].active = true; foundDevice = true; // max packet size for s100, set again in case clientID changes FWSetMaxPayloadSize(clientID, 512); // Driver/RegEntry ID would change if device wasplugged into a different // interface/card. Can fix in future by looking at UniqueID in "new" // devices and see if they match any of the old devices. // if there are more than one FW interfaces on the machine, its conceivable // that the same device could get plugged into a different interface // update the local FW reference just in case result = FWGetLocalFWReferenceIDFromFWReferenceID(clientID, &localFWReferenceID); FailWithVal( result != noErr, Exit, result); gGlobals->deviceDescription[devIndex].localFWReferenceID = localFWReferenceID; *deviceID = gGlobals->deviceDescription[devIndex].deviceID; } } if (!foundDevice) { FWDriverID clientID; CSRNodeUniqueID csrNodeUniqueID; Str255 cameraName; deviceDescriptionPtr = &gGlobals->deviceDescription[gGlobals->nDevices]; result = registerDevice( &pGDFData->deviceRegEntryID, &clientID, deviceDescriptionPtr); FailIf(noErr != result, Exit); RecordEventLogger( 'isoc', 'updt', 'add ', 'reg '); result = FWGetUniqueID(clientID, &csrNodeUniqueID); FailIf( result != noErr, Exit); // add its description gGlobals->nDevices++; result = FWGetLocalFWReferenceIDFromFWReferenceID(clientID, &localFWReferenceID); FailWithVal( result != noErr, Exit, result); *deviceID = gGlobals->nDevices; deviceDescriptionPtr->componentGlobals = gGlobals; deviceDescriptionPtr->regEntryID = pGDFData->deviceRegEntryID; deviceDescriptionPtr->refNum = pGDFData->driverRefNum; deviceDescriptionPtr->deviceID = *deviceID; deviceDescriptionPtr->fwClientID = clientID; deviceDescriptionPtr->localFWReferenceID = localFWReferenceID; deviceDescriptionPtr->uniqueID.hi = csrNodeUniqueID.hi; deviceDescriptionPtr->uniqueID.lo = csrNodeUniqueID.lo; deviceDescriptionPtr->deviceControlNotificationID = 0; // max packet size for s100 FWSetMaxPayloadSize(clientID, 512); // add the device standard (PAL, NTSC) result = getDeviceStandard(clientID, &standard); FailWithVal( result != noErr, Exit, result); deviceDescriptionPtr->standard = standard; deviceDescriptionPtr->isochPortID = 0; deviceDescriptionPtr->outputIsochChannelID = 0; deviceDescriptionPtr->inputIsochChannelID = 0; deviceDescriptionPtr->pGlobalDVOutData = nil; deviceDescriptionPtr->pGlobalDVInData = nil; deviceDescriptionPtr->dclProgramID = 0; deviceDescriptionPtr->active = true; // allocate command objects that may be used at non-task level // jkl, do these need to be deallocated somewhere? result = FWAllocateIsochPortCommandObject(&deviceDescriptionPtr->isochPortCommandObjectID); FailWithVal( result != noErr, Exit, result); result = FWAllocateIsochPortCommandObject(&deviceDescriptionPtr->stopIsochPortCommandObjectID); FailWithVal( result != noErr, Exit, result); result = FWAllocateIsochPortCommandObject(&deviceDescriptionPtr->startIsochPortCommandObjectID); FailWithVal( result != noErr, Exit, result); result = FWAllocateAsynchCommandObject(&deviceDescriptionPtr->asynchCommandObjectID); FailWithVal( result != noErr, Exit, result); FWSetAsynchCommandMaxPayloadSize(deviceDescriptionPtr->asynchCommandObjectID, 512); // find clock component // wouldn't it be better for us to open an instance on OpenDevice, set FWClockPrivLocalReference, etc. clkDesc.componentType = clockComponentType; clkDesc.componentSubType = 'fwcy'; clkDesc.componentManufacturer = 'appl'; clkDesc.componentFlags = 0L; clkDesc.componentFlagsMask = 0L; deviceDescriptionPtr->clock = 0; deviceDescriptionPtr->clock = FindNextComponent( deviceDescriptionPtr->clock, &clkDesc); // Look for FireWire clock component FailMessage( deviceDescriptionPtr->clock == nil); // create device description atom structure result = QTNewAtomContainer( &deviceDescriptionPtr->deviceContainer); FailWithVal( result != noErr, Exit, result); // add a device atom result = QTInsertChild( deviceDescriptionPtr->deviceContainer, kParentAtomIsContainer, kIDHDeviceAtomType, (long) *deviceID, 0, 0, nil, &deviceAtom); FailWithVal( result != noErr, Exit, result); // add the unique 64 bit FireWire GUID id to device atom result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kIDHUniqueIDType, 0, 0, sizeof(CSRNodeUniqueID), &csrNodeUniqueID, nil); FailWithVal( result != noErr, Exit, result); result = cameraNameLookup(deviceDescriptionPtr, cameraName); FailWithVal( result != noErr, Exit, result); result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kIDHNameAtomType, 0, 0, cameraName[0] + 1, cameraName, nil); // add the IDH unique id to device atom result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kIDHDeviceIDType, 0, 0, sizeof(deviceDescriptionPtr->deviceID), &deviceDescriptionPtr->deviceID, nil); FailWithVal( result != noErr, Exit, result); // create a device status structure and add it to the device atom deviceStatus.version = 1; deviceStatus.physicallyConnected = true; deviceStatus.readEnabled = false; deviceStatus.writeEnabled = false; deviceStatus.exclusiveAccess = false; deviceStatus.currentBandwidth = 1; deviceStatus.currentChannel = 0; deviceStatus.inputStandard = 0; deviceStatus.deviceActive = false; result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kDVDeviceInfo, 0, 0, sizeof( IDHDeviceStatus), &deviceStatus, nil); FailWithVal( result != noErr, Exit, result); // add isoch descriptions to structure result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kIDHIsochServiceAtomType, 0, 0, 0, nil, &isocAtom); FailWithVal( result != noErr, Exit, result); // add the configs to the isoc atom result = setupVideoAtoms( deviceDescriptionPtr->deviceContainer, isocAtom, standard); FailWithVal( result != noErr, Exit, result); if( standard == ntscIn) { result = setup48kAudioAtoms( deviceDescriptionPtr->deviceContainer, isocAtom, standard); FailWithVal( result != noErr, Exit, result); result = setup32kAudioAtoms( deviceDescriptionPtr->deviceContainer, isocAtom, standard); FailWithVal( result != noErr, Exit, result); } else // PAL audio { result = setup44kAudioAtoms( deviceDescriptionPtr->deviceContainer, isocAtom, standard); FailWithVal( result != noErr, Exit, result); } } } else // process device disabled { RecordEventLogger( 'isoc', 'updt', 'disa', 0); for( devIndex=0; devIndex < gGlobals->nDevices; ++devIndex) // find disabled device in list { if (gGlobals->deviceDescription[devIndex].refNum == pGDFData->driverRefNum) // do we match? { DeviceDescription *deviceDescriptionPtr = &(gGlobals->deviceDescription[devIndex]); int i; gGlobals->deviceDescription[devIndex].active = false; *deviceID = gGlobals->deviceDescription[devIndex].deviceID; RecordEventLogger( 'isoc', 'updt', 'disa', 'foun'); // find all of instances that have pending I/O for this device for( i=0; iinstancePtr[i]; IDHParameterBlock *writeRequest, *readRequest; if( deviceDescriptionPtr->instancePtr[i] == nil) // no instance? continue; // get a read request while( PBDequeueFirst( ih->readQueue, (QElemPtr *) &readRequest) == noErr) { RecordEventLogger( 'isoc', 'updt', 'disa', 'disr'); readRequest->result = kIDHErrDeviceDisconnected; // call the client callback result = (*readRequest->completionProc)( (IDHGenericEvent *) readRequest, readRequest->refCon); } // get a read request while( PBDequeueFirst( ih->writeQueue, (QElemPtr *) &writeRequest) == noErr) { RecordEventLogger( 'isoc', 'updt', 'disa', 'disw'); writeRequest->result = kIDHErrDeviceDisconnected; // call the client callback result = (*writeRequest->completionProc)( (IDHGenericEvent *) writeRequest, writeRequest->refCon); } } // end of processing instances break; } } } Exit: return result; } OSStatus writeNotifyProc( IDHGenericEvent *event, void *userData) { OSErr result = noErr; IsochComponentGlobalsPtr gGlobals = userData; DeviceDescription *deviceDescriptionPtr; short i; RecordEventLogger( 'isoc', 'writ', 'comp', 'lete'); FailIf( event->eventHeader.event != kIDHPrivateEventWriteComplete, Exit); // make sure this is a write deviceDescriptionPtr = nil; // find device causing notification in list for( i=0; ideviceDescription[i].deviceID == event->eventHeader.deviceID) { deviceDescriptionPtr = &gGlobals->deviceDescription[i]; break; } } FailIf( deviceDescriptionPtr == nil, Exit); // couldn't find device in list for( i=0; iinstancePtr[i]; IDHParameterBlock *writeRequest; Ptr tmpBuffPtr; unsigned long frameSize; if( deviceDescriptionPtr->instancePtr[i] == nil) // no instance? Skip to next. continue; if( PBDequeueFirst( ih->writeQueue, (QElemPtr *) &writeRequest) != noErr) // get a write request continue; // Get a buffer for output result = getEmptyOutputFrame( ih, &tmpBuffPtr, &frameSize); FailWithVal( result != noErr, Exit, result); writeRequest->actualCount = 0; writeRequest->result = noErr; FailMessage( frameSize < writeRequest->requestedCount) // make sure buffer is big enough if( frameSize < writeRequest->requestedCount) result = kIDHErrDeviceWriteError; if( result == noErr) // valid frame avail? { if( writeRequest->buffer == nil) // user wants buffer { writeRequest->buffer = tmpBuffPtr; // pass the buffer back to the client // call clients callback so that he can copy his data into buffer result = (*writeRequest->completionProc)( (IDHGenericEvent *) writeRequest, writeRequest->refCon); FailWithVal( result != noErr, Exit, result); // write the frame result = writeFrame( ih, tmpBuffPtr); FailWithVal( result != noErr, Exit, result); } else // client supplied frame { // move the client data into the buffer BlockMoveData( writeRequest->buffer, tmpBuffPtr, writeRequest->requestedCount); writeRequest->actualCount = writeRequest->requestedCount; // write the frame result = writeFrame( ih, tmpBuffPtr); FailWithVal( result != noErr, Exit, result); // save the result and call clients callback writeRequest->result = result; result = (*writeRequest->completionProc)( (IDHGenericEvent *) writeRequest, writeRequest->refCon); FailMessage( result != noErr); } } // valid frame avail? } // next client // reset callback result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID, kIDHPrivateEventWriteComplete); Exit: return result; } OSStatus cameraNameLookup(DeviceDescriptionPtr pDeviceDescription, UInt8 *name) { Handle h; FSSpec fileSpec; Str255 cameraName = "\pDV"; SInt32 dirID; UInt32 *pGuid; UInt32 guidCount; UInt32 i, index; OSStatus result = noErr; SInt16 refNum; Boolean foundName = false; BlockMoveData(cameraName, name, cameraName[0] + 1); // first look for a "DV Names" preferences file // find the preferences folder result = FindFolder(kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder, &refNum, &dirID); if (result != noErr) return noErr; result = FSMakeFSSpec(refNum, dirID, "\pDV Names", &fileSpec); if (result == noErr) { // prefs file exsits, open it refNum = FSpOpenResFile(&fileSpec, fsRdPerm); result = ResError(); } if (result == noErr) { // read guid resource and look for matching guid h = Get1Resource('guid', -20775); // jkl, get resource assignment and constant result = ResError(); if (h && (result == noErr)) { HLock(h); // first long is number of guid's stored in resource pGuid = (UInt32 *) *h; guidCount = *pGuid; pGuid++; for (i = 0, index = 1; i < (guidCount * 2); i+=2, index++) { if ((pGuid[i] == pDeviceDescription->uniqueID.hi) && (pGuid[i+1] == pDeviceDescription->uniqueID.lo)) { // found device, get its name GetIndString(cameraName, -20775, index); if (cameraName[0]) { BlockMoveData(cameraName, name, cameraName[0] + 1); foundName = true; break; } } } HUnlock(h); ReleaseResource(h); } CloseResFile(refNum); } if (!foundName) { // look in local vendor ID resource stored in QT FW DV Support to at least determine manufacturer name result = FindFolder(kOnSystemDisk, kExtensionFolderType, kDontCreateFolder, &refNum, &dirID); if (result != noErr) return noErr; } // jkl, this should be changed to search by type and creator, not name result = FSMakeFSSpec(refNum, dirID, "\pQuickTime FireWire DV Support", &fileSpec); if (result == noErr) { // prefs file exsits, open it refNum = FSpOpenResFile(&fileSpec, fsRdPerm); result = ResError(); if (result != noErr) return noErr; } if (result == noErr) { // read vendor id resource and look for matching guid h = Get1Resource('vnid', -20775); // jkl, get resource assignment and constant result = ResError(); if (h && (result == noErr)) { HLock(h); // first long is number of vendor id's stored in resource guidCount = **((UInt32 **) h); pGuid = *((UInt32 **) h); pGuid++; for (i = 0, index = 1; i < guidCount; i++, index++) { if (pGuid[i] == (pDeviceDescription->uniqueID.hi >> 8)) { // found device, get its name GetIndString(cameraName, -20775, index); if (cameraName[0]) { BlockMoveData(cameraName, name, cameraName[0] + 1); BlockMoveData(" DV", name + name[0] + 1, 3); name[0] += 3; break; } } } HUnlock(h); ReleaseResource(h); } CloseResFile(refNum); } // don't fail because can't find a name return noErr; } struct UniqueIDAndName { CSRNodeUniqueID guid; Str255 name; }; typedef struct UniqueIDAndName UniqueIDAndName, *UniqueIDAndNamePtr; // structural representation of preferences file data struct CameraNamePrefs { UInt32 idNameCount; UniqueIDAndName idName[]; }; typedef struct CameraNamePrefs CameraNamePrefs, *CameraNamePrefsPtr; OSStatus updateCameraName(CSRNodeUniqueID *guid, UInt8 *name) { UniqueIDAndNamePtr pGuidName; FSSpec fileSpec; SInt32 dirID; SInt32 requestedCount; UInt32 recordCount; UInt32 i; OSStatus result = noErr; SInt16 refNum, vRefNum; Boolean foundGuid = false; Boolean fileOpened = false; // look for a "DV Names" preferences file, jkl - name needs to be localized // find the preferences folder result = FindFolder(kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder, &vRefNum, &dirID); if (result != noErr) return noErr; result = FSMakeFSSpec(vRefNum, dirID, "\pDV Names", &fileSpec); if (result == fnfErr) result = FSpCreate(&fileSpec, 'dvcf', 'pref', smSystemScript); if (result == noErr) result = FSpOpenDF(&fileSpec, fsRdWrPerm, &refNum); if (result == noErr) { fileOpened = true; result = SetFPos(refNum, fsFromStart, 0); } if (result == noErr) { requestedCount = 4; result = FSRead(refNum, &requestedCount, &recordCount); } if ((result == noErr) && (requestedCount == 4)) { requestedCount = recordCount * sizeof(UniqueIDAndName); // allocate memory to read all of the data pGuidName = (UniqueIDAndNamePtr) NewPtr(requestedCount); if (pGuidName == nil) result = memFullErr; if (result == noErr) result = FSRead(refNum, &requestedCount, pGuidName); if (result == noErr) { for (i = 0; i < recordCount; i++) { if ((pGuidName[i].guid.hi == guid->hi) && (pGuidName[i].guid.lo == guid->lo)) { BlockMoveData(name, pGuidName[i].name, name[0] + 1); foundGuid = true; break; } } } } else { // prefs file is empty result = noErr; recordCount = 1; requestedCount = sizeof(UniqueIDAndName); // allocate memory for new record pGuidName = (UniqueIDAndNamePtr) NewPtr(requestedCount); if (pGuidName == nil) result = memFullErr; if (result == noErr) { BlockMoveData(name, pGuidName[0].name, name[0] + 1); pGuidName[0].guid = *guid; foundGuid = true; } } if ((result == noErr) && !foundGuid) { // allocate space for new record requestedCount = GetPtrSize((Ptr) pGuidName); requestedCount += sizeof(UniqueIDAndName); SetPtrSize((Ptr) pGuidName, requestedCount); result = MemError(); if (result == noErr) { BlockMoveData(name, pGuidName[recordCount].name, name[0] + 1); pGuidName[recordCount].guid = *guid; } recordCount++; } if (result == noErr) { // write data back to disk result = SetFPos(refNum, fsFromStart, 0); requestedCount = 4; if (result == noErr) result = FSWrite(refNum, &requestedCount, &recordCount); requestedCount = GetPtrSize((Ptr) pGuidName); if (result == noErr) result = FSWrite(refNum, &requestedCount, pGuidName); } if (fileOpened) FSClose(refNum); FlushVol(nil, vRefNum); return result; } #pragma mark - #pragma mark ееееееееее Firewire Calls ееееееееее OSStatus handleDeviceAdded(ComponentInstance ihc, GDFDeviceEventData *pGDFData) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; IDHDeviceID deviceID; OSStatus status = noErr; RecordEventLogger( 'isoc', 'hana', 0, 0); //еее // update component info status = updateDeviceList(ih, pGDFData, &deviceID, true /* activate */); // Post deviceAddedEvent to anybody that wants one postEvent(ih->gGlobals, deviceID, kIDHEventDeviceAdded, nil); return status; } OSStatus handleDeviceRemoved(ComponentInstance ihc, GDFDeviceEventData *pGDFData) { IsochComponentInstancePtr ih = (IsochComponentInstancePtr) ihc; IDHDeviceID deviceID; OSStatus status = noErr; RecordEventLogger( 'isoc', 'hanr', 0, 0); // update component info status = updateDeviceList(ih, pGDFData, &deviceID, false /* deactivate */); // Post deviceAddedEvent to anybody that wants one postEvent(ih->gGlobals, deviceID, kIDHEventDeviceRemoved, nil); return status; } OSStatus initIsochPort(FWClientInitIsochPortParams *pInitIsochPortParams, UInt32* outCommandAcceptance) { DeviceDescriptionPtr pDeviceDescription; OSErr error = noErr; Boolean portIsTalker, trial; Boolean portIsLocal = false; pDeviceDescription = (DeviceDescriptionPtr) pInitIsochPortParams->fwClientIsochPortParams.refCon; // Get port information portIsTalker = pInitIsochPortParams->fwClientIsochPortParams.portIsTalker; trial = pInitIsochPortParams->trial; // if input and call is not for talker, then call is for local port if ((pDeviceDescription->inputIsochChannelID == pInitIsochPortParams->fwClientIsochPortParams.isochChannelID) && !portIsTalker) portIsLocal = true; // JKL, really could be an else here // if output and call is for talker, then call is for local port if ((pDeviceDescription->outputIsochChannelID == pInitIsochPortParams->fwClientIsochPortParams.isochChannelID) && portIsTalker) portIsLocal = true; if (portIsTalker) { if (!trial) { if (portIsLocal) error = doLocalTalkerPort(pDeviceDescription, pInitIsochPortParams); else error = doRemoteTalkerPort(pDeviceDescription, pInitIsochPortParams); } else { if (portIsLocal) error = trialLocalTalkerPort(pDeviceDescription, pInitIsochPortParams); else error = trialRemoteTalkerPort(pDeviceDescription, pInitIsochPortParams); } } else { if (!trial) { if (portIsLocal) error = doLocalListenerPort(pDeviceDescription, pInitIsochPortParams); else error = doRemoteListenerPort(pDeviceDescription, pInitIsochPortParams); } else { if (portIsLocal) error = trialLocalListenerPort(pDeviceDescription, pInitIsochPortParams); else error = trialRemoteListenerPort(pDeviceDescription, pInitIsochPortParams); } } FailMessageVal( error != noErr, error); FWClientCommandIsComplete( pInitIsochPortParams->fwClientInterfaceParams.fwClientCommandID, error); *outCommandAcceptance = kFWClientCommandAcceptNoMore; return error; } OSStatus doLocalTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams) { OSStatus error = noErr; error = FWAllocateIsochPortID(&pDeviceDescription->isochPortID, pDeviceDescription->dclProgramID, pInitIsochPortParams->channelNum, pInitIsochPortParams->speed, true); FailMessageVal( error != noErr, error); // Send an allocate isoch port command to allocate port for talking if (error == noErr) { FWSetFWCommandParams(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->fwClientID, kFWCommandSyncFlag, nil, 0); FWSetIsochPortCommandIsochPortID(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->isochPortID); error = FWAllocateLocalIsochronousPort(pDeviceDescription->isochPortCommandObjectID); FailMessageVal( error != noErr, error); } if (pDeviceDescription->componentGlobals->useCMP) { if (error == noErr) error = cmpNewPointToPointConnection(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID, pInitIsochPortParams->channelNum, pInitIsochPortParams->speed, pDeviceDescription->cmpOverheadID, pDeviceDescription); } return error; } OSStatus doRemoteTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams) { OSStatus error = noErr; if (pDeviceDescription->componentGlobals->useCMP) { // set camera's output plug if (error == noErr) error = cmpNewPointToPointConnection(0x04, pDeviceDescription->fwClientID, pInitIsochPortParams->channelNum, pInitIsochPortParams->speed, pDeviceDescription->cmpOverheadID, pDeviceDescription); } return error; } OSStatus trialLocalTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams) { UInt32 localOutputPCRValue, pcrValue; UInt32 fwBandwidthUnits; UInt32 channelNum; OSStatus error = noErr; if (pDeviceDescription->componentGlobals->useCMP) { error = cmpRead(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID, &pcrValue, pDeviceDescription); if (error == noErr) { // initialize mac output plug, turns on connected bit, 448 overhead unlike cameras so two streams fit in started 0x1333 bandwidth, 122 quadlet payload // jkl, need to computer overhead, ask Eric pDeviceDescription->cmpOverheadID = 0x0c; localOutputPCRValue = 0x8000007a | (pDeviceDescription->cmpOverheadID << 10); error = cmpWrite(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID, pcrValue, localOutputPCRValue, pDeviceDescription); } if (error == noErr) { // reserve bandwidth // jkl, problem with this is final speed may be faster than s100, allocated bandwidth will be too much and not enough bandwidth will get deallocated // could either stay at default s100 or fix up the bandwidth in the commit phase fwBandwidthUnits = irmCalculateBandwidthUnits(localOutputPCRValue); error = irmAllocateBandwidth(pDeviceDescription->fwClientID, fwBandwidthUnits, pDeviceDescription); } // reserve channel if (error == noErr) { pDeviceDescription->cmpBandwidth = fwBandwidthUnits; channelNum = 0; // reserve channel // jkl, what if further init stuff fails - like target device in camera mode, how do we know to release resources error = irmAllocateChannel(pDeviceDescription->fwClientID, &channelNum, pDeviceDescription); if (error == noErr) { pDeviceDescription->cmpChannel = channelNum; if (channelNum < 32) { pInitIsochPortParams->supportedChannelNumHi = ((UInt32) 0x80000000) >> channelNum; pInitIsochPortParams->supportedChannelNumLo = 0; } else { channelNum -= 32; pInitIsochPortParams->supportedChannelNumHi = 0; pInitIsochPortParams->supportedChannelNumLo = ((UInt32) 0x80000000) >> channelNum; } } } // set speed if (error == noErr) { UInt32 localOutputMasterPlugValue; error = cmpRead(0x00, pDeviceDescription->localFWReferenceID, &localOutputMasterPlugValue, pDeviceDescription); if (error == noErr) pInitIsochPortParams->speed = (localOutputMasterPlugValue >> 30) & 3; else { pInitIsochPortParams->speed = kFWSpeed100MBit; // don't fail for this error = noErr; } } } else { // no CMP just use channel 63, should make sure bandwidth is allocated pInitIsochPortParams->supportedChannelNumHi = 0; pInitIsochPortParams->supportedChannelNumLo = 1; // set speed pInitIsochPortParams->speed = kFWSpeed100MBit; } return error; } OSStatus trialRemoteTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams) { UInt32 fwBandwidthUnits; UInt32 channelNum; UInt32 pcrValue; OSStatus error = noErr; if (pDeviceDescription->componentGlobals->useCMP) { // determine current state of device error = cmpRead(0x04, pDeviceDescription->fwClientID, &pcrValue, pDeviceDescription); if (error == noErr) { // save channelNum channelNum = (pcrValue >> 16) & 0x3f; // if there is a point to point connection or if camera is broadcasting on a channel other than 63, // use existing channel and bandwidth if ((pcrValue & 0x3f000000) || (((pcrValue & 0xC0000000) == 0xC0000000) && (channelNum != 63))) { if (channelNum < 32) { pInitIsochPortParams->supportedChannelNumHi = ((UInt32) 0x80000000) >> channelNum; pInitIsochPortParams->supportedChannelNumLo = 0; } else { channelNum -= 32; pInitIsochPortParams->supportedChannelNumHi = 0; pInitIsochPortParams->supportedChannelNumLo = ((UInt32) 0x80000000) >> channelNum; } pDeviceDescription->cmpChannel = channelNum; pDeviceDescription->cmpBandwidth = irmCalculateBandwidthUnits(pcrValue); pDeviceDescription->cmpOverheadID = (pcrValue >> 10) & 0x0f; } else { if ((pcrValue & 0xC0000000) == 0xC0000000) { // device is broadcasting on 63, clear broadcast, release channel and bandwidth error = cmpWrite(0x04, pDeviceDescription->fwClientID, pcrValue, pcrValue & 0xbfffffff, pDeviceDescription); if (error == noErr) error = irmReleaseChannel(pDeviceDescription->fwClientID, 63, pDeviceDescription); if (error == noErr) { fwBandwidthUnits = irmCalculateBandwidthUnits(pcrValue); error = irmReleaseBandwidth(pDeviceDescription->fwClientID, fwBandwidthUnits, pDeviceDescription); } } // allocate bandwidth // jkl, bandwidth could be wrong if speed is negotiated faster than s100, too much is allocated and not enough gets deallocated // could just hard wire to s100 or re-check bandwith in commit phase pDeviceDescription->cmpOverheadID = 0x0c; fwBandwidthUnits = irmCalculateBandwidthUnits((pcrValue & 0x03ff) | (pDeviceDescription->cmpOverheadID << 10)); error = irmAllocateBandwidth(pDeviceDescription->fwClientID, fwBandwidthUnits, pDeviceDescription); if (error == noErr) { pDeviceDescription->cmpBandwidth = fwBandwidthUnits; // allocate channel channelNum = 0; error = irmAllocateChannel(pDeviceDescription->fwClientID, &channelNum, pDeviceDescription); if (error == noErr) { pDeviceDescription->cmpChannel = channelNum; if (channelNum < 32) { pInitIsochPortParams->supportedChannelNumHi = ((UInt32) 0x80000000) >> channelNum; pInitIsochPortParams->supportedChannelNumLo = 0; } else { channelNum -= 32; pInitIsochPortParams->supportedChannelNumHi = 0; pInitIsochPortParams->supportedChannelNumLo = ((UInt32) 0x80000000) >> channelNum; } } } } } // set speed if (error == noErr) { UInt32 remoteOutputMasterPlugValue; error = cmpRead(0x00, pDeviceDescription->fwClientID, &remoteOutputMasterPlugValue, pDeviceDescription); if (error == noErr) pInitIsochPortParams->speed = (remoteOutputMasterPlugValue >> 30) & 3; else { pInitIsochPortParams->speed = kFWSpeed100MBit; // don't fail for this error = noErr; } } } else { // just use channel 63 pInitIsochPortParams->supportedChannelNumHi = 0; pInitIsochPortParams->supportedChannelNumLo = 1; // set speed pInitIsochPortParams->speed = kFWSpeed100MBit; } return error; } OSStatus doLocalListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams) { OSStatus error = noErr; error = FWAllocateIsochPortID(&pDeviceDescription->isochPortID, pDeviceDescription->dclProgramID, pInitIsochPortParams->channelNum, pInitIsochPortParams->speed, false); FailMessageVal( error != noErr, error); // Send an allocate isoch port command to allocate port for listening if (error == noErr) { FWSetFWCommandParams(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->fwClientID, kFWCommandSyncFlag, nil, 0); FWSetIsochPortCommandIsochPortID(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->isochPortID); error = FWAllocateLocalIsochronousPort(pDeviceDescription->isochPortCommandObjectID); FailMessageVal( error != noErr, error); } if (pDeviceDescription->componentGlobals->useCMP) { // set mac's input plug if (error == noErr) error = cmpNewPointToPointConnection(0x80 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID, pInitIsochPortParams->channelNum, 0, 0, pDeviceDescription); } return error; } OSStatus doRemoteListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams) { OSStatus error = noErr; if (pDeviceDescription->componentGlobals->useCMP) { if (error == noErr) { // set camera's input plug error = cmpNewPointToPointConnection(0x84, pDeviceDescription->fwClientID, pInitIsochPortParams->channelNum, 0, 0, pDeviceDescription); } } return error; } OSStatus trialLocalListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams) { UInt32 pcrValue; OSStatus error = noErr; if (pDeviceDescription->componentGlobals->useCMP) { // initialize mac plug, on-line, read value should be 0 error = cmpRead(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID, &pcrValue, pDeviceDescription); if (error == noErr) error = cmpWrite(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID, pcrValue, 0x80000000, pDeviceDescription); if (error == noErr) error = cmpRead(0x80, pDeviceDescription->localFWReferenceID, &pcrValue, pDeviceDescription); if (error == noErr) pInitIsochPortParams->speed = (pcrValue >> 30) & 3; else { pInitIsochPortParams->speed = kFWSpeed100MBit; // don't fail for this error = noErr; } } else pInitIsochPortParams->speed = kFWSpeed100MBit; pInitIsochPortParams->supportedChannelNumHi = 0xffffffff; pInitIsochPortParams->supportedChannelNumLo = 0xffffffff; return error; } OSStatus trialRemoteListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams) { UInt32 remoteInputMasterPlugValue; OSStatus error = noErr; if (pDeviceDescription->componentGlobals->useCMP) { error = cmpRead(0x80, pDeviceDescription->fwClientID, &remoteInputMasterPlugValue, pDeviceDescription); if (error == noErr) pInitIsochPortParams->speed = (remoteInputMasterPlugValue >> 30) & 3; else { pInitIsochPortParams->speed = kFWSpeed100MBit; // don't fail for this error = noErr; } } else pInitIsochPortParams->speed = kFWSpeed100MBit; pInitIsochPortParams->supportedChannelNumHi = 0xffffffff; pInitIsochPortParams->supportedChannelNumLo = 0xffffffff; return error; } OSStatus releaseIsochPort(FWClientReleaseIsochPortParams *pReleaseIsochPortParams, UInt32* outCommandAcceptance) { DeviceDescriptionPtr pDeviceDescription; FWReferenceID fwClientID; Boolean portIsTalker; Boolean portIsLocal = false; OSErr error = noErr; pDeviceDescription = (DeviceDescriptionPtr) pReleaseIsochPortParams->fwClientIsochPortParams.refCon; // Get port information fwClientID = pReleaseIsochPortParams->fwClientInterfaceParams.fwReferenceID; portIsTalker = pReleaseIsochPortParams->fwClientIsochPortParams.portIsTalker; // if input and call is not for talker, then call is for local port if ((pDeviceDescription->inputIsochChannelID == pReleaseIsochPortParams->fwClientIsochPortParams.isochChannelID) && !portIsTalker) portIsLocal = true; // JKL, really could be an else here // if output and call is for talker, then call is for local port if ((pDeviceDescription->outputIsochChannelID == pReleaseIsochPortParams->fwClientIsochPortParams.isochChannelID) && portIsTalker) portIsLocal = true; // if called for local port, release port from FSL if (portIsLocal) { FWSetFWCommandParams(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->fwClientID, kFWCommandSyncFlag, nil, 0); FWSetIsochPortCommandIsochPortID(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->isochPortID); FWReleaseLocalIsochronousPort(pDeviceDescription->isochPortCommandObjectID); FWDeallocateIsochPortID(pDeviceDescription->isochPortID); pDeviceDescription->isochPortID = 0; } if (pDeviceDescription->componentGlobals->useCMP) { if (portIsTalker) { if (portIsLocal) error = cmpDisposePointToPointConnection(pDeviceDescription->pcrIndex * 4, pDeviceDescription->localFWReferenceID, pDeviceDescription); else error = cmpDisposePointToPointConnection(0x04, fwClientID, pDeviceDescription); } else { if (portIsLocal) error = cmpDisposePointToPointConnection(0x80 + (pDeviceDescription->pcrIndex * 4), pDeviceDescription->localFWReferenceID, pDeviceDescription); else error = cmpDisposePointToPointConnection(0x84, fwClientID, pDeviceDescription); } } FWClientCommandIsComplete( pReleaseIsochPortParams->fwClientInterfaceParams.fwClientCommandID, error); *outCommandAcceptance = kFWClientCommandAcceptNoMore; return error; } OSStatus startIsochPort(FWClientIsochPortControlParams *pIsochPortControlParams, UInt32* outCommandAcceptance) { DeviceDescriptionPtr pDeviceDescription; FWClientCommandID fwClientCommandID; FWReferenceID fwClientID; Boolean completionIsPending = false; Boolean portIsTalker; Boolean portIsLocal = false; OSErr error = noErr; RecordEventLogger( 'isoc', 'strt', 'port', 'bgn '); pDeviceDescription = (DeviceDescriptionPtr) pIsochPortControlParams->fwClientIsochPortParams.refCon; // Get port information fwClientID = pIsochPortControlParams->fwClientInterfaceParams.fwReferenceID; portIsTalker = pIsochPortControlParams->fwClientIsochPortParams.portIsTalker; // if input and call is not for talker, then call is for local port if ((pDeviceDescription->inputIsochChannelID == pIsochPortControlParams->fwClientIsochPortParams.isochChannelID) && !portIsTalker) portIsLocal = true; // JKL, really could be an else here // if output and call is for talker, then call is for local port if ((pDeviceDescription->outputIsochChannelID == pIsochPortControlParams->fwClientIsochPortParams.isochChannelID) && portIsTalker) portIsLocal = true; // if called for local port, start port if (portIsLocal) { fwClientCommandID = pIsochPortControlParams->fwClientInterfaceParams.fwClientCommandID; FWSetFWCommandParams(pDeviceDescription->startIsochPortCommandObjectID, pDeviceDescription->fwClientID, 0, isochPortCommandCompletionProc, (UInt32) fwClientCommandID); FWSetIsochPortCommandIsochPortID(pDeviceDescription->startIsochPortCommandObjectID, pDeviceDescription->isochPortID); error = FWStartLocalIsochronousPort(pDeviceDescription->startIsochPortCommandObjectID); FailMessageVal( error != noErr, error); completionIsPending = true; //zzz what if above call returns an error? } if ((error == noErr) && completionIsPending) error = kIDHErrCompletionPending; if (error != kIDHErrCompletionPending) { FWClientCommandIsComplete( pIsochPortControlParams->fwClientInterfaceParams.fwClientCommandID, error); } else { error = noErr; } *outCommandAcceptance = kFWClientCommandAcceptNoMore; return error; } OSStatus stopIsochPort(FWClientIsochPortControlParams *pIsochPortControlParams, UInt32* outCommandAcceptance) { DeviceDescriptionPtr pDeviceDescription; FWClientCommandID fwClientCommandID; FWReferenceID fwClientID; Boolean completionIsPending = false; Boolean portIsTalker; Boolean portIsLocal = false; OSErr error = noErr; pDeviceDescription = (DeviceDescriptionPtr) pIsochPortControlParams->fwClientIsochPortParams.refCon; // Get port information fwClientID = pIsochPortControlParams->fwClientInterfaceParams.fwReferenceID; portIsTalker = pIsochPortControlParams->fwClientIsochPortParams.portIsTalker; // if input and call is not for talker, then call is for local port if ((pDeviceDescription->inputIsochChannelID == pIsochPortControlParams->fwClientIsochPortParams.isochChannelID) && !portIsTalker) portIsLocal = true; // JKL, really could be an else here // if output and call is for talker, then call is for local port if ((pDeviceDescription->outputIsochChannelID == pIsochPortControlParams->fwClientIsochPortParams.isochChannelID) && portIsTalker) portIsLocal = true; // if called for local port, stop port if (portIsLocal) { fwClientCommandID = pIsochPortControlParams->fwClientInterfaceParams.fwClientCommandID; FWSetFWCommandParams(pDeviceDescription->stopIsochPortCommandObjectID, pDeviceDescription->fwClientID, 0, isochPortCommandCompletionProc, (UInt32) fwClientCommandID); FWSetIsochPortCommandIsochPortID(pDeviceDescription->stopIsochPortCommandObjectID, pDeviceDescription->isochPortID); error = FWStopLocalIsochronousPort(pDeviceDescription->stopIsochPortCommandObjectID); FailMessageVal( error != noErr, error); completionIsPending = true; //zzz what if above call returns an error? } if ((error == noErr) && completionIsPending) error = kIDHErrCompletionPending; if (error != kIDHErrCompletionPending) { FWClientCommandIsComplete( pIsochPortControlParams->fwClientInterfaceParams.fwClientCommandID, error); } else { error = noErr; } *outCommandAcceptance = kFWClientCommandAcceptNoMore; return error; } void isochPortCommandCompletionProc(FWCommandObjectID fwCommandObjectID, OSStatus commandStatus, UInt32 completionProcData) { FWClientCommandIsComplete((FWClientCommandID) completionProcData, commandStatus); } //////////////////////////////////////////////////////////////////////////////// // // cmpDisposePointToPointConnection // // Tear down a point to point connection through the client's plug control register. // // *** Must be task level only OSErr cmpDisposePointToPointConnection ( UInt32 pcrOffset, // offset of register address from fffff0000900 FWClientID clientID, DeviceDescriptionPtr pDeviceDescription) { UInt32 pcrValue, expectedPCRValue; OSErr err = noErr; if (pcrOffset > 0x00fc) err = addressRangeErr; // get existing plug control register value if (err == noErr) err = cmpRead(pcrOffset, clientID, &pcrValue, pDeviceDescription); if (err == noErr) { expectedPCRValue = pcrValue; // decrement point to point counter // jkl, only if it is already set, need to fix this if (pcrValue & 0x3f000000) pcrValue -= 0x01000000; // if point to point goes to zero, also turn off broadcast // jkl, Sony suggests to also make sure camera is stopped if (!(pcrValue & 0x3f000000)) pcrValue &= 0xbfffffff; err = cmpWrite(pcrOffset, clientID, expectedPCRValue, pcrValue, pDeviceDescription); // release isochronous resources if counter goes to zero and device was talking if (err == noErr) { if (!(pcrValue & 0x3f000000) && (pcrOffset < 0x080)) { irmReleaseChannel(clientID, (pcrValue & 0x003f0000) >> 16, pDeviceDescription); irmReleaseBandwidth(clientID, irmCalculateBandwidthUnits(pcrValue), pDeviceDescription); } } } return err; } //////////////////////////////////////////////////////////////////////////////// // // cmpNewPointToPointConnection // // Create a point to point connection through the client's and the local // plug control register. // // *** Must be task level only OSErr cmpNewPointToPointConnection ( UInt32 pcrOffset, // offset of register address from fffff0000900 FWClientID clientID, UInt32 channel, UInt32 speed, UInt32 overhead, DeviceDescriptionPtr pDeviceDescription) { UInt32 pcrValue, expectedPCRValue; OSErr err = noErr; if (pcrOffset > 0x00fc) err = addressRangeErr; // get existing plug control register value if (err == noErr) err = cmpRead(pcrOffset, clientID, &pcrValue, pDeviceDescription); if (err == noErr) { expectedPCRValue = pcrValue; // increment point to point counter, will this ever overflow?, not turning off broadcast pcrValue += 0x01000000; if (pcrOffset < 0x80) {// setting up output plug // set speed pcrValue |= (speed << 14); // set overhead pcrValue |= (overhead << 10); // if not already broadcasting or if point-to-point counter was previously not 0, set channel if (((pcrValue & 0x3f000000) == 0x01000000) && !(pcrValue & 0x40000000)) { // set channel number pcrValue &= 0xffc0ffff; pcrValue |= (channel << 16); } } else { // for input plug just set channel if no point-to-point exists if ((pcrValue & 0x3f000000) == 0x01000000) { // set channel number pcrValue &= 0xffc0ffff; pcrValue |= (channel << 16); } } err = cmpWrite(pcrOffset, clientID, expectedPCRValue, pcrValue, pDeviceDescription); } return err; } //////////////////////////////////////////////////////////////////////////////// // // cmpRead // // Read the plug control register specified by the offset. // // *** Must be task level only OSErr cmpRead ( UInt32 pcrOffset, // offset of register address from fffff0000900 FWClientID clientID, UInt32 *pcrValue, DeviceDescriptionPtr deviceDescriptionPtr) { FWCommandObjectID fwCommandObjectID = nil; UInt32 retryCount; OSErr err = noErr; if (pcrOffset > 0x00fc) err = addressRangeErr; fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID; // set command params for synchronous read if (err == noErr) err = FWSetFWCommandParams(fwCommandObjectID, clientID, kFWCommandSyncFlag, nil, 0); // set more command params for synchronous read if (err == noErr) err = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000900 + pcrOffset, (Ptr) pcrValue, 4); if (err == noErr) { retryCount = 10; do { retryCount--; err = FWRead(fwCommandObjectID); if (err == noErr) return err; // don't slam the camera too hard DelayForHardware(DurationToAbsolute(1 * durationMillisecond)); } // probably should retry only for specific error like retryExceeded or timeOut while (err && retryCount); err = retryExceededErr; } return err; } //////////////////////////////////////////////////////////////////////////////// // // cmpWrite // // Write a plug control register through a compare and swap lock transaction. // The plug control register is specified by the offset. After the transaction // The expectedPCRValue is set to the value of the PCR prior before the request. // If this value equals the original passed in value the command completed. // // *** Must be task level only OSErr cmpWrite ( UInt32 pcrOffset, // offset of register address from fffff0000900 FWClientID clientID, UInt32 expectedPCRValue, UInt32 newPCRValue, DeviceDescriptionPtr deviceDescriptionPtr) { FWCommandObjectID fwCommandObjectID = nil; UInt32 compareAndSwapBuffer[2]; UInt32 retryCount; OSErr err = noErr; if (pcrOffset > 0x00fc) err = addressRangeErr; fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID; // set command params for synchronous lock if (err == noErr) err = FWSetFWCommandParams(fwCommandObjectID, clientID, kFWCommandSyncFlag, nil, 0); // set more command params for synchronous lock if (err == noErr) { compareAndSwapBuffer[0] = expectedPCRValue; compareAndSwapBuffer[1] = newPCRValue; err = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000900 + pcrOffset, (Ptr) compareAndSwapBuffer, 8); } retryCount = 10; do { retryCount--; // JKL, fix this, arguments need to be fixed up after each try err = FWCompareAndSwap(fwCommandObjectID); if (err == noErr) { if (compareAndSwapBuffer[0] == expectedPCRValue) return err; else { // force it to try again, although probably lost cause at this point // jkl, need to start back at the beginning of the write command to redo values err = retryExceededErr; // jkl ***, hack, reset expectedPCRValue to what was returned even though this no longer may be a valid starting argument expectedPCRValue = compareAndSwapBuffer[0]; } } // don't slam the camera too hard DelayForHardware(DurationToAbsolute(1 * durationMillisecond)); } // probably should retry only for specific error like retryExceeded or timeOut while (err && retryCount); return err; } //////////////////////////////////////////////////////////////////////////////// // // cmpHandleLocalLock // // This is a notification routine that is called once the response to a lock // transaction to one of the PCR addresses has been sent. One way to handle // this is to look at the result of the transaction, is it consistent with // the current configuration. Does the configuration change to match the PCR // request or is the PCR updated with the current configuration overriding // the lock request. This may also be called for locks initiated by the driver // to a local PCR. // // There is also a LockRequest notification that could be setup to look at the // request. I don't know if we have the ability to override the lock request // and basically deny it before FSL responds. Something to talk over with Eric // and Collin. // OSStatus cmpHandleLocalLock( FWClientAsynchRequestParamsPtr pFWClientAsynchRequestParams, UInt32 *pCommandAcceptance) { DeviceDescriptionPtr pDeviceDescription; OSStatus status = noErr; pDeviceDescription = (DeviceDescriptionPtr) pFWClientAsynchRequestParams->pAddressSpecificData; // now what? // Complete FireWire client command. FWClientCommandIsComplete(pFWClientAsynchRequestParams->fwClientInterfaceParams.fwClientCommandID, status); *pCommandAcceptance = kFWClientCommandAcceptNoMore; return status; } // *** Must be task level only OSStatus irmReleaseChannel( FWClientID fwClientID, UInt32 channel, DeviceDescriptionPtr deviceDescriptionPtr) { FWReferenceID fwIsochResourceManagerID; FWCommandObjectID fwCommandObjectID = nil; UInt32 asynchCommandArgs[2]; UInt32 clippedChannel, saveMaxRT; OSStatus status = noErr; status = FWGetFWIsochResourceManagerID(fwClientID, &fwIsochResourceManagerID); if ((status == noErr) && (fwIsochResourceManagerID != (FWReferenceID) kInvalidFWIsochResourceManagerID)) { fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID; FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, kFWCommandSyncFlag, nil, 0); FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT ); FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries ); if (channel < 32) FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000224, (Ptr) &asynchCommandArgs[0], 4); else FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000228, (Ptr) &asynchCommandArgs[0], 4); // read channel allocated register status = FWRead(fwCommandObjectID); if (status == noErr) { // check if channel is reserved clippedChannel = (channel & 0x0000001f); if (!(asynchCommandArgs[0] & (0x80000000 >> clippedChannel))) { // channel is allocated, deallocate it asynchCommandArgs[1] = asynchCommandArgs[0] | (0x80000000 >> clippedChannel); // use existing settings except for length FWSetAsynchCommandLength(fwCommandObjectID, 8); status = FWCompareAndSwap(fwCommandObjectID); } } FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT ); } return status; } // *** Must be task level only OSStatus irmReleaseBandwidth( FWClientID fwClientID, UInt32 fwBandwidthUnits, DeviceDescriptionPtr deviceDescriptionPtr) { FWReferenceID fwIsochResourceManagerID; FWCommandObjectID fwCommandObjectID = nil; UInt32 asynchCommandArgs[2], saveMaxRT; OSStatus status = noErr; status = FWGetFWIsochResourceManagerID(fwClientID, &fwIsochResourceManagerID); if ((status == noErr) && (fwIsochResourceManagerID != (FWReferenceID) kInvalidFWIsochResourceManagerID)) { // read IRM bandwidth register fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID; FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, kFWCommandSyncFlag, nil, 0); FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000220, (Ptr) &asynchCommandArgs[0], 8); FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT ); FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries ); // Set up arguments for add. // First parameter is the threshold, second is amount to add. // JKL *** Sony uses 0x1388 for starting available bandwidth which is nice since most of the // camcorders are allocating more bandwidth for one channel than necessary and not enough is // left for an additional channel // p1394 says IRM max bandwidth value should be 0x1333, 4915 // asynchCommandArgs[0] = 0x1388; asynchCommandArgs[0] = 0x1333; asynchCommandArgs[1] = fwBandwidthUnits; // Add to bandwidth allocation. status = FWClippedAdd(fwCommandObjectID); FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT ); } return status; } // *** Must be task level only OSStatus irmAllocateChannel( FWClientID fwClientID, UInt32 *channel, DeviceDescriptionPtr deviceDescriptionPtr) { FWReferenceID fwIsochResourceManagerID; FWCommandObjectID fwCommandObjectID = nil; UInt32 asynchCommandArgs[2], channelArgs, saveMaxRT; OSStatus status = noErr; status = FWGetFWIsochResourceManagerID(fwClientID, &fwIsochResourceManagerID); // JKL *** this is not right since noErr could get returned but not have an IRM if ((status == noErr) && (fwIsochResourceManagerID != (FWReferenceID) kInvalidFWIsochResourceManagerID)) { // read IRM channel register fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID; FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, kFWCommandSyncFlag, nil, 0); // populate both command args with the channel registers FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000224, (Ptr) &asynchCommandArgs[0], 4); FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT ); FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries ); // read channel hi allocated register status = FWRead(fwCommandObjectID); if (status == noErr) { FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000228, (Ptr) &asynchCommandArgs[1], 4); // read channel lo allocated register status = FWRead(fwCommandObjectID); } if (status == noErr) { // get next available channel if (*channel < 32) channelArgs = asynchCommandArgs[0]; else channelArgs = asynchCommandArgs[1]; while (! (channelArgs & (0x80000000 >> (*channel & 0x0000001f)))) { *channel = *channel + 1; if (*channel > 31) channelArgs = asynchCommandArgs[1]; if (*channel > 63) break; } // hopefully a channel got allocated if (*channel < 32) { // set channel hi address FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000224, (Ptr) &asynchCommandArgs[0], 8); } else if (*channel < 64) { // set channel lo address FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000228, (Ptr) &asynchCommandArgs[0], 8); } else status = noChannelsAvailableErr; if (status == noErr) { // clear allocated channel bit asynchCommandArgs[1] = asynchCommandArgs[0] & ~(0x80000000 >> (*channel & 0x01f)); // do the command status = FWCompareAndSwap(fwCommandObjectID); } } FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT ); } return status; } // *** Must be task level only OSStatus irmAllocateBandwidth( FWClientID fwClientID, UInt32 fwBandwidthUnits, DeviceDescriptionPtr deviceDescriptionPtr) { FWReferenceID fwIsochResourceManagerID; FWCommandObjectID fwCommandObjectID = nil; UInt32 asynchCommandArgs[2], saveMaxRT; OSStatus status = noErr; status = FWGetFWIsochResourceManagerID(fwClientID, &fwIsochResourceManagerID); // JKL *** this is not right since noErr could get returned but not have an IRM if ((status == noErr) && (fwIsochResourceManagerID != (FWReferenceID) kInvalidFWIsochResourceManagerID)) { fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID; FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, kFWCommandSyncFlag, nil, 0); // subtract bandwidth with a min at 0 asynchCommandArgs[0] = 0; asynchCommandArgs[1] = fwBandwidthUnits; FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000220, (Ptr) &asynchCommandArgs[0], 8); FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT ); FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries ); // Subtract from bandwidth allocation. status = FWThresholdSubtract(fwCommandObjectID); // Check if bandwidth was allocated. if (status == noErr) { // Bandwidth was allocated if old bandwidth value is greater // or equal to what we asked for. if (asynchCommandArgs[0] < asynchCommandArgs[1]) status = insufficientBandwidthErr; } FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT ); } return status; } UInt32 irmCalculateBandwidthUnits( UInt32 pcrValue) { UInt32 pcrDataRate, pcrOverheadID, pcrPayload, fwBandwidthUnits; // set bandwidth units according to 61883 formula and PCR value // JKL *** all cameras do not set these values correctly, some set the // overhead too high and don't leave enough bandwidth for two streams // computer data rate multiplier pcrDataRate = (pcrValue & 0x0000c000) >> 14; switch (pcrDataRate) { case 0: pcrDataRate = 16; break; case 1: pcrDataRate = 8; break; case 2: pcrDataRate = 4; break; } pcrPayload = pcrValue & 0x03ff; pcrOverheadID = (pcrValue & 0x03c00) >> 10; // 0 is max for overhead if (!pcrOverheadID) pcrOverheadID = 16; fwBandwidthUnits = (pcrOverheadID * 32) + ((pcrPayload + 3) * pcrDataRate); return fwBandwidthUnits; } OSStatus cmpHandleBusReset(FWClientInterfaceParamsPtr pParams, UInt32 *pCommandAcceptance) { DeviceDescriptionPtr pDeviceDescription; OSErr error = noErr; pDeviceDescription = (DeviceDescriptionPtr) pParams->fwClientSpecificData; RecordEventLogger( 'cmp ', 'busR', pDeviceDescription->resetStatus, error); if (pDeviceDescription->resetStatus == kResetInterrupted) pDeviceDescription->resetStatus = kNoResetsProcessing; cmpResetStageOne(pDeviceDescription); FWClientCommandIsComplete(pParams->fwClientCommandID, error); *pCommandAcceptance = kFWClientCommandAcceptNoMore; return error; } // // clear local PCR, FSL can do this for us // void cmpResetStageOne(DeviceDescriptionPtr pDeviceDescription) { OSErr error = noErr; RecordEventLogger( 'cmp ', 'res1', pDeviceDescription->resetStatus, error); if (pDeviceDescription->resetStatus == kNoResetsProcessing) { pDeviceDescription->resetStatus = kResetProcessing; if (pDeviceDescription->inputIsochChannelID) { error = cmpResetRead(0xf0000980 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID, cmpResetStageTwo, pDeviceDescription); } else { error = cmpResetRead(0xf0000900 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID, cmpResetStageTwo, pDeviceDescription); } if (noErr != error) pDeviceDescription->resetStatus = kNoResetsProcessing; } else pDeviceDescription->resetStatus = kResetInterrupted; } // // complete clearing local PCR // void cmpResetStageTwo(FWCommandObjectID theObjectID, OSStatus status, UInt32 data) { DeviceDescriptionPtr pDeviceDescription = (DeviceDescriptionPtr) data; OSErr error = noErr; RecordEventLogger( 'cmp ', 'res2', pDeviceDescription->resetStatus, status); if (pDeviceDescription->resetStatus == kResetProcessing) { if (status == noErr) { pDeviceDescription->buffer[0]; if (pDeviceDescription->inputIsochChannelID) { pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] & 0x803f0000; error = cmpResetWrite(0xf0000980 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID, cmpResetStageThree, pDeviceDescription); } else { pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] & 0x803fc3ff; error = cmpResetWrite(0xf0000900 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID, cmpResetStageThree, pDeviceDescription); } } if ((noErr != status) || (noErr != error)) pDeviceDescription->resetStatus = kNoResetsProcessing; } else { // another bus reset came in, start over pDeviceDescription->resetStatus = kNoResetsProcessing; cmpResetStageOne(pDeviceDescription); } } // // re-allocate channel, read IRM channel // void cmpResetStageThree(FWCommandObjectID theObjectID, OSStatus status, UInt32 data) { DeviceDescriptionPtr pDeviceDescription = (DeviceDescriptionPtr) data; FWReferenceID fwIsochResourceManagerID; OSErr error = noErr; RecordEventLogger( 'cmp ', 'res3', pDeviceDescription->resetStatus, status); if (pDeviceDescription->resetStatus == kResetProcessing) { if (status == noErr) { error = FWGetFWIsochResourceManagerID(pDeviceDescription->fwClientID, &fwIsochResourceManagerID); if (error == noErr) { if (pDeviceDescription->cmpChannel < 32) error = cmpResetRead(0xf0000224, fwIsochResourceManagerID, cmpResetStageFour, pDeviceDescription); else error = cmpResetRead(0xf0000228, fwIsochResourceManagerID, cmpResetStageFour, pDeviceDescription); } } if ((noErr != status) || (noErr != error)) pDeviceDescription->resetStatus = kNoResetsProcessing; } else { // another bus reset came in, start over pDeviceDescription->resetStatus = kNoResetsProcessing; cmpResetStageOne(pDeviceDescription); } } // // complete re-allocating channel // jkl, this might already be allocated void cmpResetStageFour(FWCommandObjectID theObjectID, OSStatus status, UInt32 data) { DeviceDescriptionPtr pDeviceDescription = (DeviceDescriptionPtr) data; FWReferenceID fwIsochResourceManagerID; OSErr error = noErr; RecordEventLogger( 'cmp ', 'res4', pDeviceDescription->resetStatus, status); if (pDeviceDescription->resetStatus == kResetProcessing) { if (status == noErr) { error = FWGetFWIsochResourceManagerID(pDeviceDescription->fwClientID, &fwIsochResourceManagerID); if (error == noErr) { if (pDeviceDescription->cmpChannel < 32) { pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] & ~(0x80000000 >> pDeviceDescription->cmpChannel); error = cmpResetWrite(0xf0000224, fwIsochResourceManagerID, cmpResetStageFive, pDeviceDescription); } else { pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] & ~(0x80000000 >> (pDeviceDescription->cmpChannel - 32)); error = cmpResetWrite(0xf0000228, fwIsochResourceManagerID, cmpResetStageFive, pDeviceDescription); } } } if ((noErr != status) || (noErr != error)) pDeviceDescription->resetStatus = kNoResetsProcessing; } else { // another bus reset came in, start over pDeviceDescription->resetStatus = kNoResetsProcessing; cmpResetStageOne(pDeviceDescription); } } // // re-allocate bandwidth // jkl, this might already be allocated void cmpResetStageFive(FWCommandObjectID theObjectID, OSStatus status, UInt32 data) { FWCommandObjectID fwCommandObjectID; DeviceDescriptionPtr pDeviceDescription = (DeviceDescriptionPtr) data; FWReferenceID fwIsochResourceManagerID; OSErr error = noErr; UInt32 saveMaxRT; RecordEventLogger( 'cmp ', 'res5', pDeviceDescription->resetStatus, status); if (pDeviceDescription->resetStatus == kResetProcessing) { if (status == noErr) { error = FWGetFWIsochResourceManagerID(pDeviceDescription->fwClientID, &fwIsochResourceManagerID); if (error == noErr) { fwCommandObjectID = pDeviceDescription->asynchCommandObjectID; // set command params for synchronous read error = FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, 0, cmpResetStageSix, (UInt32) pDeviceDescription); // set more command params for synchronous read if (error == noErr) error = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000220, (Ptr) &(pDeviceDescription->buffer[0]), 8); FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT ); FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries ); if (error == noErr) { pDeviceDescription->buffer[0] = 0; pDeviceDescription->buffer[1] = pDeviceDescription->cmpBandwidth; error = FWThresholdSubtract(fwCommandObjectID); } FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT ); } } if ((noErr != status) || (noErr != error)) pDeviceDescription->resetStatus = kNoResetsProcessing; } else { // another bus reset came in, start over pDeviceDescription->resetStatus = kNoResetsProcessing; cmpResetStageOne(pDeviceDescription); } } // // set oPCR, read it first // void cmpResetStageSix(FWCommandObjectID theObjectID, OSStatus status, UInt32 data) { DeviceDescriptionPtr pDeviceDescription = (DeviceDescriptionPtr) data; OSErr error = noErr; RecordEventLogger( 'cmp ', 'res6', pDeviceDescription->resetStatus, status); if (pDeviceDescription->resetStatus == kResetProcessing) { if (status == noErr) { if (pDeviceDescription->inputIsochChannelID) { error = cmpResetRead(0xf0000904, pDeviceDescription->fwClientID, cmpResetStageSeven, pDeviceDescription); } else { error = cmpResetRead(0xf0000900 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID, cmpResetStageSeven, pDeviceDescription); } } if ((noErr != status) || (noErr != error)) pDeviceDescription->resetStatus = kNoResetsProcessing; } else { // another bus reset came in, start over pDeviceDescription->resetStatus = kNoResetsProcessing; cmpResetStageOne(pDeviceDescription); } } // // complete oPCR // void cmpResetStageSeven(FWCommandObjectID theObjectID, OSStatus status, UInt32 data) { DeviceDescriptionPtr pDeviceDescription = (DeviceDescriptionPtr) data; OSErr error = noErr; RecordEventLogger( 'cmp ', 'res7', pDeviceDescription->resetStatus, status); if (pDeviceDescription->resetStatus == kResetProcessing) { if (status == noErr) { if (pDeviceDescription->inputIsochChannelID) { pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] + 0x01000000; pDeviceDescription->buffer[1] |= (pDeviceDescription->cmpOverheadID << 10); error = cmpResetWrite(0xf0000904, pDeviceDescription->fwClientID, cmpResetStageEight, pDeviceDescription); } else { pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] + 0x01000000; pDeviceDescription->buffer[1] |= (pDeviceDescription->cmpOverheadID << 10); error = cmpResetWrite(0xf0000900 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID, cmpResetStageEight, pDeviceDescription); } } if ((noErr != status) || (noErr != error)) pDeviceDescription->resetStatus = kNoResetsProcessing; } else { // another bus reset came in, start over pDeviceDescription->resetStatus = kNoResetsProcessing; cmpResetStageOne(pDeviceDescription); } } // // set iPCR, read it first // void cmpResetStageEight(FWCommandObjectID theObjectID, OSStatus status, UInt32 data) { DeviceDescriptionPtr pDeviceDescription = (DeviceDescriptionPtr) data; OSErr error = noErr; RecordEventLogger( 'cmp ', 'res8', pDeviceDescription->resetStatus, status); if (pDeviceDescription->resetStatus == kResetProcessing) { if (status == noErr) { if (pDeviceDescription->outputIsochChannelID) { error = cmpResetRead(0xf0000984, pDeviceDescription->fwClientID, cmpResetStageNine, pDeviceDescription); } else { error = cmpResetRead(0xf0000980 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID, cmpResetStageNine, pDeviceDescription); } } if ((noErr != status) || (noErr != error)) pDeviceDescription->resetStatus = kNoResetsProcessing; } else { // another bus reset came in, start over pDeviceDescription->resetStatus = kNoResetsProcessing; cmpResetStageOne(pDeviceDescription); } } // // complete iPCR // void cmpResetStageNine(FWCommandObjectID theObjectID, OSStatus status, UInt32 data) { DeviceDescriptionPtr pDeviceDescription = (DeviceDescriptionPtr) data; OSErr error = noErr; RecordEventLogger( 'cmp ', 'res9', pDeviceDescription->resetStatus, status); if (pDeviceDescription->resetStatus == kResetProcessing) { if (status == noErr) { if (pDeviceDescription->outputIsochChannelID) { pDeviceDescription->buffer[1] = (pDeviceDescription->buffer[0] + 0x01000000) & 0xffc0ffff; pDeviceDescription->buffer[1] |= (pDeviceDescription->cmpChannel << 16); error = cmpResetWrite(0xf0000984, pDeviceDescription->fwClientID, cmpResetComplete, pDeviceDescription); } else { pDeviceDescription->buffer[1] = (pDeviceDescription->buffer[0] + 0x01000000) & 0xffc0ffff; pDeviceDescription->buffer[1] |= (pDeviceDescription->cmpChannel << 16); error = cmpResetWrite(0xf0000980 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID, cmpResetComplete, pDeviceDescription); } } if ((noErr != status) || (noErr != error)) pDeviceDescription->resetStatus = kNoResetsProcessing; } else { // another bus reset came in, start over pDeviceDescription->resetStatus = kNoResetsProcessing; cmpResetStageOne(pDeviceDescription); } } void cmpResetComplete(FWCommandObjectID theObjectID, OSStatus status, UInt32 data) { DeviceDescriptionPtr pDeviceDescription = (DeviceDescriptionPtr) data; OSErr error = noErr; RecordEventLogger( 'cmp ', 'res ', 'comp', status); pDeviceDescription->resetStatus = kNoResetsProcessing; } // // Do an asynch read // OSErr cmpResetRead ( UInt32 offsetLo, FWClientID clientID, FWCommandCompletionProcPtr completionProc, DeviceDescriptionPtr deviceDescriptionPtr) { FWCommandObjectID fwCommandObjectID; OSErr error = noErr; UInt32 saveMaxRT; fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID; // set command params for synchronous read error = FWSetFWCommandParams(fwCommandObjectID, clientID, 0, completionProc, (UInt32) deviceDescriptionPtr); // set more command params for synchronous read if (error == noErr) error = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, offsetLo, (Ptr) &(deviceDescriptionPtr->buffer[0]), 4); FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT ); FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries ); if (error == noErr) error = FWRead(fwCommandObjectID); FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT ); return error; } // // Do an asynch write, really a lock // OSErr cmpResetWrite ( UInt32 offsetLo, FWClientID clientID, FWCommandCompletionProcPtr completionProc, DeviceDescriptionPtr deviceDescriptionPtr) { FWCommandObjectID fwCommandObjectID; OSErr error = noErr; UInt32 saveMaxRT; fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID; // set command params for synchronous read error = FWSetFWCommandParams(fwCommandObjectID, clientID, 0, completionProc, (UInt32) deviceDescriptionPtr); // set more command params for synchronous read if (error == noErr) error = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, offsetLo, (Ptr) &(deviceDescriptionPtr->buffer[0]), 8); FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT ); FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries ); if (error == noErr) error = FWCompareAndSwap(fwCommandObjectID); FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT ); return error; } Boolean useCMP() { Handle h; Str63 fileName; CInfoPBRec catInfoPB; FSSpec fileSpec; SInt32 extfDirID; SInt16 extfIndex = 1; SInt16 extfVRefNum, fRef; SInt32 gestaltVers; OSErr err; Boolean useCMP = false; // default is to not use it // use cmp only if FireWire 2.2 or greater err = Gestalt(gestaltFireWireVersion, &gestaltVers); if (err == noErr) { if (gestaltVers >= 0x0220) useCMP = true; } // if FireWire 2.2 or greater check if useGestalt resource is available and its value matches if (useCMP) { // get a shortcut to the extensions folder err = FindFolder(kOnSystemDisk, kExtensionFolderType, kDontCreateFolder, &extfVRefNum, &extfDirID); if (err == noErr) { // iterate through files in extensions folder while (err == noErr) { fileName[ 0 ] = '\0'; fileName[ 1 ] = '\0'; catInfoPB.hFileInfo.ioNamePtr = &fileName[ 0 ]; catInfoPB.hFileInfo.ioVRefNum = extfVRefNum; catInfoPB.hFileInfo.ioFDirIndex = extfIndex++; catInfoPB.hFileInfo.ioDirID = extfDirID; err = PBGetCatInfoSync(&catInfoPB); if (err == noErr) { // find the file matching these attributes if ((catInfoPB.hFileInfo.ioFlAttrib & 0x10) != 0 || (catInfoPB.hFileInfo.ioFlFndrInfo.fdType != 'INIT') || (catInfoPB.hFileInfo.ioFlFndrInfo.fdCreator != 'dvfw')) continue; // found our file, make an FSSpec err = FSMakeFSSpec(extfVRefNum, extfDirID, catInfoPB.hFileInfo.ioNamePtr, &fileSpec); break; } } } if (err == noErr) { fRef = FSpOpenResFile(&fileSpec, fsRdPerm); err = ResError(); if (err == noErr) { h = Get1Resource('ucmp', -20756); err = ResError(); if ((err == noErr) && (h != nil)) useCMP = true; else useCMP = false; CloseResFile(fRef); } } // turn off useCMP in case of any other error if (err != noErr) useCMP = false; } return useCMP; } OSErr enableRead(IsochComponentInstancePtr ih) { FWCommandObjectID isochChannelCommandObjectID = nil; DeviceDescriptionPtr pDeviceDescription; SInt32 time; OSErr error = noErr; error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription); FailMessageVal( error != noErr, error); if (error == noErr) { error = dclInitInput(pDeviceDescription, ih->gGlobals); FailMessageVal( error != noErr, error); } if (error == noErr) { // JKL, how do we know the speed before doing CMP, IRM error = FWAllocateIsochronousChannelID(&pDeviceDescription->inputIsochChannelID, false, 30000000, kFWSpeed100MBit); FailMessageVal( error != noErr, error); if (error != noErr) disposeDCLInput(pDeviceDescription->pGlobalDVInData); } // add a talking client, camera if (error == noErr) { error = FWAddIsochronousChannelClient(pDeviceDescription->inputIsochChannelID, pDeviceDescription->fwClientID, (UInt32) pDeviceDescription, true); FailMessageVal( error != noErr, error); if (error != noErr) { FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID); pDeviceDescription->inputIsochChannelID = nil; disposeDCLInput(pDeviceDescription->pGlobalDVInData); } } // add a listening client, mac if (error == noErr) { error = FWAddIsochronousChannelClient(pDeviceDescription->inputIsochChannelID, pDeviceDescription->fwClientID, (UInt32) pDeviceDescription, false); FailMessageVal( error != noErr, error); if( error != noErr ) { FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID); pDeviceDescription->inputIsochChannelID = nil; disposeDCLInput(pDeviceDescription->pGlobalDVInData); } } // Initialize the isochronous channel if (error == noErr) { if (pDeviceDescription->componentGlobals->useCMP) { pDeviceDescription->resetStatus = kNoResetsProcessing; // allocate PCR space from FSL // JKL, for now should get pcr #1, what if we don't error = FWAllocatePCRAddressSpace(&pDeviceDescription->pcrID, pDeviceDescription->fwClientID, (Ptr) &pDeviceDescription->pcrBuffer, kFWAddressReadEnable | kFWAddressLockEnable | kFWAddressLockRequestNotify | kFWAddressLockCompleteNotify, (Ptr) pDeviceDescription, true /* inputPCR */, 1, &pDeviceDescription->pcrIndex); FailMessageVal( error != noErr, error); // JKL, not using lock call backs, could be used to respond to device trying to modify local PCR's // if (error == noErr) // error = FWSetFWClientLockCompleteProc(pDeviceDescription->fwClientID, pcrHandleLocalLock); // // if (error == noErr) // error = FWSetFWClientLockRequestProc(pDeviceDescription->fwClientID, pcrHandleLocalLock); // add bus reset proc if (error == noErr) error = FWSetFWClientBusManagementNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) cmpHandleBusReset); FailMessageVal( error != noErr, error); } // register for bus reset notification from FireWire for DV deviceChanged notification // unregister at release if (error == noErr) { error = FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, handleBusReset); FailMessageVal( error != noErr, error); } if (error == noErr) { error = FWAllocateIsochChannelCommandObject(&isochChannelCommandObjectID); FailMessageVal( error != noErr, error); } if (error == noErr) { FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0); FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->inputIsochChannelID); error = FWInitializeIsochronousChannel(isochChannelCommandObjectID); FailMessageVal( error != noErr, error); } if (error != noErr) { // release channel even though init failed, part of init (listener or talker) may have succeeded FWReleaseIsochronousChannel(isochChannelCommandObjectID); FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID); pDeviceDescription->inputIsochChannelID = nil; disposeDCLInput(pDeviceDescription->pGlobalDVInData); } else { // Start the isochronous channel error = FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0); FailMessageVal( error != noErr, error); error = FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->inputIsochChannelID); FailMessageVal( error != noErr, error); // JKL, revisit this. // delay because some (many) cameras send bad data immediately after you change the plug control registers // (at least that's my guess, It could be a bug somewhere in FSL... who knows... // we reprogram the PCRs with the above FWInitializeIsochronousChannel call // the delay was arrived at by good ole' trial and error time = TickCount(); while( TickCount() < (time + 7) ) { // Yes...really do nothing ; } error = FWStartIsochronousChannel(isochChannelCommandObjectID); FailMessageVal( error != noErr, error); if (error != noErr) { FWReleaseIsochronousChannel(isochChannelCommandObjectID); FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID); pDeviceDescription->inputIsochChannelID = nil; disposeDCLInput(pDeviceDescription->pGlobalDVInData); } } } if (isochChannelCommandObjectID) FWDeallocateFWCommandObject(isochChannelCommandObjectID); // JKL, readEnabledNotification? if (error) FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, nil); FailMessageVal( error != noErr, error); return error; } OSErr disableRead(IsochComponentInstancePtr ih) { FWCommandObjectID isochChannelCommandObjectID = nil; DeviceDescriptionPtr pDeviceDescription; OSErr error = noErr; error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription); if (error == noErr) { if (pDeviceDescription->inputIsochChannelID) { if (error == noErr) error = FWAllocateIsochChannelCommandObject(&isochChannelCommandObjectID); if (error == noErr) { // stop channel FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0); FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->inputIsochChannelID); FWStopIsochronousChannel(isochChannelCommandObjectID); // release channel FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0); FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->inputIsochChannelID); FWReleaseIsochronousChannel(isochChannelCommandObjectID); FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID); pDeviceDescription->inputIsochChannelID = 0; if (pDeviceDescription->componentGlobals->useCMP) { // JKL, should clear pcr before deallocating? FWDeallocateAddressSpace(pDeviceDescription->pcrID); pDeviceDescription->pcrID = 0; pDeviceDescription->pcrIndex = 0; FWSetFWClientBusManagementNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) nil); // JKL, not using lock call backs or bus management yet // FWSetFWClientLockCompleteProc(pDeviceDescription->fwClientID, (FWClientLockProcPtr) nil); // FWSetFWClientLockRequestProc(pDeviceDescription->fwClientID, (FWClientLockProcPtr) nil); } } // unregister bus reset notification FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) nil); disposeDCLInput(pDeviceDescription->pGlobalDVInData); } } return error; } OSErr enableWrite(IsochComponentInstancePtr ih) { FWCommandObjectID isochChannelCommandObjectID = nil; DeviceDescriptionPtr pDeviceDescription; SInt32 time; OSErr error = noErr; error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription); if (error == noErr) error = dclInitOutput(pDeviceDescription, ih->gGlobals); if (error == noErr) { // JKL, how do we know the speed before doing CMP, IRM error = FWAllocateIsochronousChannelID(&pDeviceDescription->outputIsochChannelID, false, 30000000, kFWSpeed100MBit); if (error != noErr) disposeDCLOutput(pDeviceDescription->pGlobalDVOutData); } // add a talking client, mac if (error == noErr) { error = FWAddIsochronousChannelClient(pDeviceDescription->outputIsochChannelID, pDeviceDescription->fwClientID, (UInt32) pDeviceDescription, true); if (error != noErr) { FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID); pDeviceDescription->outputIsochChannelID = nil; disposeDCLOutput(pDeviceDescription->pGlobalDVOutData); } } // add a listening client, camera if (error == noErr) { error = FWAddIsochronousChannelClient(pDeviceDescription->outputIsochChannelID, pDeviceDescription->fwClientID, (UInt32) pDeviceDescription, false); if (error != noErr) { FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID); pDeviceDescription->outputIsochChannelID = nil; disposeDCLOutput(pDeviceDescription->pGlobalDVOutData); } } // Initialize the isochronous channel if (error == noErr) { if (pDeviceDescription->componentGlobals->useCMP) { pDeviceDescription->resetStatus = kNoResetsProcessing; // allocate PCR space from FSL // JKL, for now should get pcr #1, what if we don't error = FWAllocatePCRAddressSpace(&pDeviceDescription->pcrID, pDeviceDescription->fwClientID, (Ptr) &pDeviceDescription->pcrBuffer, kFWAddressReadEnable | kFWAddressLockEnable | kFWAddressLockRequestNotify | kFWAddressLockCompleteNotify, (Ptr) pDeviceDescription, false /* outputPCR */, 1, &pDeviceDescription->pcrIndex); FailMessageVal( error != noErr, error); // JKL, not using lock call backs, could be used to respond to a device trying to modify local PCR's // if (error == noErr) // error = FWSetFWClientLockCompleteProc(pDeviceDescription->fwClientID, cmpHandleLocalLock); // // if (error == noErr) // error = FWSetFWClientLockRequestProc(pDeviceDescription->fwClientID, cmpHandleLocalLock); // add bus reset proc if (error == noErr) error = FWSetFWClientBusManagementNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) cmpHandleBusReset); FailMessageVal( error != noErr, error); } // register for bus reset notification from FireWire for DV deviceChanged notification // unregister at release if (error == noErr) { error = FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, handleBusReset); FailMessageVal( error != noErr, error); } if (error == noErr) { error = FWAllocateIsochChannelCommandObject(&isochChannelCommandObjectID); FailMessageVal( error != noErr, error); } if (error == noErr) { FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0); FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID); error = FWInitializeIsochronousChannel(isochChannelCommandObjectID); } if (error != noErr) { // release channel even though init failed, part of init (listener or talker) may have succeeded FWReleaseIsochronousChannel(isochChannelCommandObjectID); FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID); pDeviceDescription->outputIsochChannelID = nil; disposeDCLOutput(pDeviceDescription->pGlobalDVOutData); } else { // Start the isochronous channel error = FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0); error = FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID); // delay because some (many) cameras send bad data immediately after you change the plug control registers // (at least that's my guess, It could be a bug somewhere in FSL... who knows... // we reprogram the PCRs with the above FWInitializeIsochronousChannel call // the delay was arrived at by good ole' trial and error error = FWStartIsochronousChannel(isochChannelCommandObjectID); if (error == noErr) { time = TickCount(); while( TickCount() < (time + 60) ) { // Yes...really do nothing ; } } else { FWReleaseIsochronousChannel(isochChannelCommandObjectID); FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID); pDeviceDescription->outputIsochChannelID = nil; disposeDCLOutput(pDeviceDescription->pGlobalDVOutData); } } } if (error) FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, nil); if (isochChannelCommandObjectID) FWDeallocateFWCommandObject(isochChannelCommandObjectID); // JKL, writeEnabledNotification return error; } OSErr disableWrite(IsochComponentInstancePtr ih) { FWCommandObjectID isochChannelCommandObjectID = nil; DeviceDescriptionPtr pDeviceDescription; OSErr error = noErr; error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription); if (error == noErr) { if (pDeviceDescription->outputIsochChannelID) { if (error == noErr) error = FWAllocateIsochChannelCommandObject(&isochChannelCommandObjectID); if (error == noErr) { // JKL, need top delay stopping transmit until all queued frames are sent // originally had a isFinished call and then delayed 2 * ticks * howeverManyFramesAreQueued // stop channel FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0); FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID); FWStopIsochronousChannel(isochChannelCommandObjectID); // release channel FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0); FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID); FWReleaseIsochronousChannel(isochChannelCommandObjectID); FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID); pDeviceDescription->outputIsochChannelID = 0; if (pDeviceDescription->componentGlobals->useCMP) { // jkl, should clear pcr before deallocating FWDeallocateAddressSpace(pDeviceDescription->pcrID); pDeviceDescription->pcrID = 0; pDeviceDescription->pcrIndex = 0; FWSetFWClientBusManagementNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) nil); // JKL, not using lock call backs or bus management yet // FWSetFWClientLockCompleteProc(pDeviceDescription->fwClientID, (FWClientLockProcPtr) nil); // FWSetFWClientLockRequestProc(pDeviceDescription->fwClientID, (FWClientLockProcPtr) nil); } } // unregister bus reset notification FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) nil); disposeDCLOutput(pDeviceDescription->pGlobalDVOutData); } } return error; } OSErr dclInitOutput(DeviceDescriptionPtr pDeviceDescription, IsochComponentGlobals* isochComponentGlobals) { float A, B, C, d, n; UInt32 numEmptyPacketsPerPlayBufferGroup; UInt32 transmitBuffersSize; DCLCommandPoolPtr pDCLCommandPool; DCLCommandPtr pDCLCommand; DCLCommandPtr pFirstBufferGroupDCLCommand; DVGlobalOutPtr pGlobalData; DCLLabelPtr pUnderrunDCLLabel, pLoopDCLLabel, pBufferGroupDCLLabel, pDCLLabel; DCLTransferPacketPtr pDCLTransferPacket; DCLCallProcPtr pDCLCallProc; DCLSetTagSyncBitsPtr pDCLSetTagSyncBits; DCLJumpPtr pDCLJump, pBufferGroupDCLJump; DCLLabelPtr pBufferGroupSkipEmptyPacketDCLLabel; DCLUpdateDCLListPtr pDCLUpdateDCLList; DCLTimeStampPtr pDCLTimeStamp; DVLocalOutPtr pPlayBufferGroupData; UInt32 *pTransmitBuffer; UInt32 bufferGroupNum, dataPacketNum, numPackets; UInt32 emptyPacketNumerator; UInt32 playFrameRateNumerator, playFrameRateDenominator; UInt32 numDataPacketsPerPage; UInt32 pageSize; OSErr error = noErr; // allocate space for global data pGlobalData = PoolAllocateResident(sizeof(DVGlobalOut), true); if (pGlobalData == nil) error = memFullErr; if (error == noErr) { pDeviceDescription->pGlobalDVOutData = pGlobalData; pGlobalData->isochComponentGlobals = isochComponentGlobals; pGlobalData->deviceID = pDeviceDescription->deviceID; pGlobalData->pBufferGroupDataList = nil; pGlobalData->pDCLCommandPool = nil; pGlobalData->pDVFrameOutputData = nil; pGlobalData->pTransmitBuffers = nil; pGlobalData->isNTSC = (pDeviceDescription->standard == ntscIn); pGlobalData->localFWReferenceID = pDeviceDescription->localFWReferenceID; error = initDVFrameOutput(pDeviceDescription->standard, &pGlobalData->pDVFrameOutputData); } if (error == noErr) { error = FWCreateDCLProgram(&pGlobalData->DCLProgramID); pDeviceDescription->dclProgramID = pGlobalData->DCLProgramID; } if (error == noErr) { error = FWAllocateIsochChannelCommandObject(&pGlobalData->isochChannelCommandObjectID); } if (error == noErr) { if( pGlobalData->isNTSC ) { pGlobalData->playFramePeriodNumerator = kNTSCPlayFramePeriodNumerator; pGlobalData->playFramePeriodDenominator = kNTSCPlayFramePeriodDenominator; playFrameRateNumerator = kNTSCFrameRateNumerator; playFrameRateDenominator = kNTSCFrameRateDenominator; pGlobalData->numDataPacketsPerFrame = kNTSCNumDataPacketsPerDVFrame; } else { pGlobalData->playFramePeriodNumerator = kPALPlayFramePeriodNumerator; pGlobalData->playFramePeriodDenominator = kPALPlayFramePeriodDenominator; playFrameRateNumerator = kPALFrameRateNumerator; playFrameRateDenominator = kPALFrameRateDenominator; pGlobalData->numDataPacketsPerFrame = kPALNumDataPacketsPerDVFrame; } // Compute nominal frame period cycle time. pGlobalData->nominalFrameCycleTime = convertFractionalSecondsToFWCycleTime (pGlobalData->playFramePeriodNumerator, pGlobalData->playFramePeriodDenominator); // Compute the number of data packets per empty packet. // If the frame rate is expressed as n/d, the number of data packets per buffer group // expressed as A, and the number of data packets per frame as C, then the number of // empty packets per buffer group B should be // // B = int (8000*d/n*A/C - A + 1) // // in order to ensure that the frame rate may be maintained by periodically reducing // the number of empty packets in a buffer group by 1. // A = (float) kNumDataPacketsPerPlayBufferGroup; C = (float) pGlobalData->numDataPacketsPerFrame; n = (float) playFrameRateNumerator; d = (float) playFrameRateDenominator; B = 8000.0*d/n*A/C - A + 1; numEmptyPacketsPerPlayBufferGroup = (UInt32) B; ///////////////////////////////////////// // Allocate transmit buffers. // // Compute size. // to prevent packets crossing page boundaries // allocate buffer memory based on pages, // there is enough extra room in each page for the empty packets pageSize = GetLogicalPageSize(); numDataPacketsPerPage = pageSize / (kDVPacketTransferSize + kDVPacketAlignSlop); transmitBuffersSize = kNumDataPacketsPerPlayBufferGroup / numDataPacketsPerPage * pageSize; transmitBuffersSize *= kNumPlayBufferGroups; // add an extra page to account for first page not being page aligned transmitBuffersSize += pageSize; // Allocate. pGlobalData->pTransmitBuffers = PoolAllocateResident(transmitBuffersSize, false); if (pGlobalData->pTransmitBuffers == nil) error = memFullErr; else pTransmitBuffer = (UInt32 *) pGlobalData->pTransmitBuffers; } if (error == noErr) { ///////////////////////////////////////// // Start Up DCL Allocation Engine // Allocate DCL command pool. pDCLCommandPool = dclAllocateCommandPool(); if (pDCLCommandPool == nil) error = memFullErr; else pGlobalData->pDCLCommandPool = pDCLCommandPool; } if (error == noErr) { //////////////////////////////////// // Create DCL Program // // Initialize total packet count. pGlobalData->totalPackets = 0; // Create label for start of loop. pLoopDCLLabel = (DCLLabelPtr) dclAllocateCommand(pDCLCommandPool, sizeof (DCLLabel)); //zzz check error pDCLCommand = (DCLCommandPtr) pLoopDCLLabel; pGlobalData->pDCLList = pDCLCommand; pLoopDCLLabel->opcode = kDCLLabelOp; // Set isoch packet tag bits to the way DV likes 'em pDCLSetTagSyncBits = (DCLSetTagSyncBitsPtr) dclAllocateCommand(pDCLCommandPool, sizeof (DCLSetTagSyncBits)); //zzz check error pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLSetTagSyncBits; pDCLCommand = (DCLCommandPtr) pDCLSetTagSyncBits; pDCLSetTagSyncBits->opcode = kDCLSetTagSyncBitsOp; pDCLSetTagSyncBits->tagBits = 1; pDCLSetTagSyncBits->syncBits = 0; for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++) { // Allocate a buffer group data record. pPlayBufferGroupData = dclAllocatePlayBufferGroup(pGlobalData); if (pPlayBufferGroupData == nil) { error = memFullErr; goto bail; } // Initialize for loop. dataPacketNum = 0; numPackets = 0; emptyPacketNumerator = 0; pFirstBufferGroupDCLCommand = nil; pBufferGroupSkipEmptyPacketDCLLabel = nil; while (dataPacketNum < kNumDataPacketsPerPlayBufferGroup) { // Send a packet: CIP header + payload. pDCLTransferPacket = (DCLTransferPacketPtr) dclAllocateCommand(pDCLCommandPool, sizeof (DCLTransferPacket)); if( pDCLTransferPacket == nil ) { error = memFullErr; goto bail; } pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket; pDCLCommand = (DCLCommandPtr) pDCLTransferPacket; pDCLTransferPacket->opcode = kDCLSendPacketStartOp; pDCLTransferPacket->size = kDVPacketTransferSize; // check for buffer crossing page if (((UInt32) (pTransmitBuffer + kDVPacketAlignOffset) & 0x0fff) < (kDVPacketAlignOffset * 4)) { // if it does, increment buffer pointer // and lop off page rollover to start at next page pTransmitBuffer += kDVPacketAlignOffset; pTransmitBuffer = (UInt32 *) ((UInt32) pTransmitBuffer & 0xfffff000); } pDCLTransferPacket->buffer = (Ptr) pTransmitBuffer; // increment by 496 bytes to maintain cache alignment pTransmitBuffer += kDVPacketAlignOffset; // Save first data packet DCL command. if (pFirstBufferGroupDCLCommand == nil) pFirstBufferGroupDCLCommand = (DCLCommandPtr) pDCLCommand; dataPacketNum++; numPackets++; emptyPacketNumerator += numEmptyPacketsPerPlayBufferGroup; if (emptyPacketNumerator >= kNumDataPacketsPerPlayBufferGroup) { // Add skip jump if this is the first empty packet in the buffer group. if (pBufferGroupSkipEmptyPacketDCLLabel == nil) { pDCLJump = (DCLJumpPtr) dclAllocateCommand(pDCLCommandPool, sizeof (DCLJump)); if( pDCLJump == nil ) { error = memFullErr; goto bail; } pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLJump = pDCLJump; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLJump; pDCLCommand = (DCLCommandPtr) pDCLJump; pDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag; pDCLLabel = (DCLLabelPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLLabel)); if( pDCLLabel == nil ) { error = memFullErr; goto bail; } pPlayBufferGroupData->pBufferGroupDontSkipEmptyPacketDCLLabel = pDCLLabel; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLLabel; pDCLCommand = (DCLCommandPtr) pDCLLabel; pDCLLabel->opcode = kDCLLabelOp; pDCLJump->pJumpDCLLabel = pDCLLabel; } // Send a packet. // Just CIP header. pDCLTransferPacket = (DCLTransferPacketPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLTransferPacket)); if( pDCLTransferPacket == nil ) { error = memFullErr; goto bail; } pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket; pDCLCommand = (DCLCommandPtr) pDCLTransferPacket; pDCLTransferPacket->opcode = kDCLSendPacketStartOp; pDCLTransferPacket->buffer = (Ptr) pTransmitBuffer; pDCLTransferPacket->size = kDVPacketCIPSize; // increment 16 bytes to maintain alignment pTransmitBuffer += kDVEmptyPacketAlignOffset; numPackets++; emptyPacketNumerator -= kNumDataPacketsPerPlayBufferGroup; // Add skip jump label if this is the first empty packet in the // buffer group. if (pBufferGroupSkipEmptyPacketDCLLabel == nil) { // Add skip label. pDCLLabel = (DCLLabelPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLLabel)); if( pDCLLabel == nil ) { error = memFullErr; goto bail; } pBufferGroupSkipEmptyPacketDCLLabel = pDCLLabel; pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLLabel = pBufferGroupSkipEmptyPacketDCLLabel; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLLabel; pDCLCommand = (DCLCommandPtr) pDCLLabel; pDCLLabel->opcode = kDCLLabelOp; } } } // Save number of packets in this buffer group, DCL update list size, and last // DCL command. pPlayBufferGroupData->numPackets = numPackets; pPlayBufferGroupData->pFirstBufferGroupDCLCommand = pFirstBufferGroupDCLCommand; pPlayBufferGroupData->pLastBufferGroupDCLCommand = (DCLCommandPtr) pDCLCommand; // Create buffer group update list. createDVPlayBufferGroupUpdateList( pPlayBufferGroupData ); // Update total packet count. pGlobalData->totalPackets += numPackets; // Create end of buffer group jump. pBufferGroupDCLJump = (DCLJumpPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLJump)); if( pBufferGroupDCLJump == nil ) { error = memFullErr; goto bail; } pPlayBufferGroupData->pEndOfBufferGroupDCLJump = pBufferGroupDCLJump; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pBufferGroupDCLJump; pDCLCommand = (DCLCommandPtr) pBufferGroupDCLJump; pBufferGroupDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag; // Create label for end of buffer group. pBufferGroupDCLLabel = (DCLLabelPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLLabel)); if( pBufferGroupDCLLabel == nil ) { error = memFullErr; goto bail; } pPlayBufferGroupData->pEndOfBufferGroupDCLLabel = pBufferGroupDCLLabel; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pBufferGroupDCLLabel; pDCLCommand = (DCLCommandPtr) pBufferGroupDCLLabel; pBufferGroupDCLLabel->opcode = kDCLLabelOp; // Set end of buffer group jump to jump to end of buffer group. pBufferGroupDCLJump->pJumpDCLLabel = pBufferGroupDCLLabel; // Get time stamp at end of buffer group. pDCLTimeStamp = (DCLTimeStampPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLTimeStamp)); if( pDCLTimeStamp == nil ) { error = memFullErr; goto bail; } pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTimeStamp; pDCLCommand = (DCLCommandPtr) pDCLTimeStamp; pDCLTimeStamp->opcode = kDCLTimeStampOp; pPlayBufferGroupData->pBufferGroupDCLTimeStamp = pDCLTimeStamp; pPlayBufferGroupData->timeStampUpdateDCLList = (DCLCommandPtr) pDCLTimeStamp; // Create update DCL list to update time stamp. pDCLUpdateDCLList = (DCLUpdateDCLListPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLUpdateDCLList)); if( pDCLUpdateDCLList == nil ) { error = memFullErr; goto bail; } pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList; pDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList; pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp; pDCLUpdateDCLList->dclCommandList = &(pPlayBufferGroupData->timeStampUpdateDCLList); pDCLUpdateDCLList->numDCLCommands = 1; // Call a proc at end of buffer group. pDCLCallProc = (DCLCallProcPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLCallProc)); if( pDCLCallProc == nil ) { error = memFullErr; goto bail; } pPlayBufferGroupData->pBufferGroupDCLCallProc = pDCLCallProc; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLCallProc; pDCLCommand = (DCLCommandPtr) pDCLCallProc; pDCLCallProc->opcode = kDCLCallProcOp; pDCLCallProc->proc = handleDVOutput; pDCLCallProc->procData = (UInt32) pPlayBufferGroupData; // Create update DCL list to update buffers. pDCLUpdateDCLList = (DCLUpdateDCLListPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLUpdateDCLList)); if( pDCLUpdateDCLList == nil ) { error = memFullErr; goto bail; } pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList; pDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList; pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp; pDCLUpdateDCLList->dclCommandList = pPlayBufferGroupData->bufferGroupUpdateDCLList; pDCLUpdateDCLList->numDCLCommands = pPlayBufferGroupData->updateListSize; } // Loop to first buffer group. pDCLJump = (DCLJumpPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLJump)); if( pDCLJump == nil ) { error = memFullErr; goto bail; } pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLJump; pDCLCommand = (DCLCommandPtr) pDCLJump; pDCLJump->opcode = kDCLJumpOp; pDCLJump->pJumpDCLLabel = pLoopDCLLabel; // Create label for underrun. pUnderrunDCLLabel = (DCLLabelPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLLabel)); if( pUnderrunDCLLabel == nil ) { error = memFullErr; goto bail; } pGlobalData->pUnderrunDCLLabel = pUnderrunDCLLabel; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pUnderrunDCLLabel; pDCLCommand = (DCLCommandPtr) pUnderrunDCLLabel; pUnderrunDCLLabel->opcode = kDCLLabelOp; // Set last buffer group's jump DCL to jump to underrun. pBufferGroupDCLJump->pJumpDCLLabel = pUnderrunDCLLabel; // Call underrun proc. // This is the last command. pDCLCallProc = (DCLCallProcPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLCallProc)); if( pDCLCallProc == nil ) { error = memFullErr; goto bail; } pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLCallProc; pDCLCallProc->pNextDCLCommand = nil; pDCLCallProc->opcode = kDCLCallProcOp; pDCLCallProc->proc = handleDVOutputUnderrun; pDCLCallProc->procData = (UInt32) pDeviceDescription; // Initialize number of active play packets. pGlobalData->activePackets = pGlobalData->totalPackets; } // Set up all of the buffer groups. if (error == noErr) { pGlobalData->nextSYT = kPlaySYTDelay; pGlobalData->nextDBC = 0; pGlobalData->nextDataPacketNum = 0; pGlobalData->pImageBuffer = nil; pPlayBufferGroupData = pGlobalData->pBufferGroupDataList; for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++) { updateDVOutputBuffers( (DCLCommandPtr) pPlayBufferGroupData->pBufferGroupDCLCallProc ); pPlayBufferGroupData = pPlayBufferGroupData->pNextLocalData; } } if (error == noErr) error = FWSetDCLProgramStart(pGlobalData->DCLProgramID, pGlobalData->pDCLList); // jkl, do we need to do this? // if (error == noErr) // error = FWSetDCLProgramStartEvent(pGlobalData->DCLProgramID, kFWDCLCycleEvent, 0, BitRange (12, 15)); bail: // if we had a problem, lets clean it up if (error != noErr) { disposeDCLOutput(pDeviceDescription->pGlobalDVOutData); pGlobalData = nil; } return error; } //////////////////////////////////////////////////////////////////////////////// // // DVHandleOutput // // This routine handles isochronous sending of DV data. // void handleDVOutput(DCLCommandPtr pDCLCommandPtr) { DCLCallProcPtr pDCLCallProc; DCLTimeStampPtr pDCLTimeStamp; DVLocalOutPtr pLocalData; DVGlobalOutPtr pGlobalData; DVLocalOutPtr pPrevLocalData; UInt32 nominalFrameCycleTime; UInt32 fractionalFrameCycleCount, fractionalFrameCycleOffset; SInt32 timeDrift; UInt32 cycleDrift; UInt32 projectedTimeStamp, projectedSYT; // Recast pDCLCommandPtr. pDCLCallProc = (DCLCallProcPtr) pDCLCommandPtr; // Get data for buffer group and driver data. pLocalData = (DVLocalOutPtr) pDCLCallProc->procData; pPrevLocalData = pLocalData->pPrevLocalData; pGlobalData = pLocalData->pGlobalData; nominalFrameCycleTime = pGlobalData->nominalFrameCycleTime; // Undo skipping empty packet if we're currently skipping a packet. if (pLocalData->skippingEmptyPacket) { FWModifyDCLJump( pGlobalData->DCLProgramID, pLocalData->pBufferGroupSkipEmptyPacketDCLJump, pLocalData->pBufferGroupDontSkipEmptyPacketDCLLabel); pGlobalData->activePackets++; pLocalData->skippingEmptyPacket = false; } // Compute time drift. // Compute the projected time stamp value for the first packet of the current // buffer group the next time this proc is called for the current buffer group. // Start at time stamp of first packet in next buffer group to be sent. pDCLTimeStamp = pLocalData->pBufferGroupDCLTimeStamp; projectedTimeStamp = pDCLTimeStamp->timeStamp; projectedTimeStamp = addFWCycleTimeToFWCycleTime(projectedTimeStamp, 1 << 12); // Add the total number of cycles for all active buffer group packets. projectedTimeStamp = addFWCycleTimeToFWCycleTime(projectedTimeStamp, pGlobalData->activePackets << 12); // Subtract the number of cycles for all packets in the current buffer group. projectedTimeStamp = subtractFWCycleTimeFromFWCycleTime(projectedTimeStamp, pLocalData->numPackets << 12); // Compute the projected SYT value for the first packet of the current buffer group // the next time this proc is called for the current buffer group. // Start with the SYT value to use for the first packet of the next frame. projectedSYT = pGlobalData->nextSYT; // Subtract the SYT offset between frames. projectedSYT = subtractFWCycleTimeFromFWCycleTime(projectedSYT, nominalFrameCycleTime); // Add the fraction of the SYT offset between the start of the frame and the // first data packet for the current buffer group. fractionalFrameCycleOffset = ((nominalFrameCycleTime & 0x0FFF) * pGlobalData->nextDataPacketNum) / pGlobalData->numDataPacketsPerFrame; fractionalFrameCycleCount = ((nominalFrameCycleTime & 0x01FFF000) * pGlobalData->nextDataPacketNum) / pGlobalData->numDataPacketsPerFrame; fractionalFrameCycleCount = (fractionalFrameCycleCount & 0x01FFF000) + (((fractionalFrameCycleCount & 0x0FFF) * 3072) / 4096); projectedSYT = addFWCycleTimeToFWCycleTime (projectedSYT, fractionalFrameCycleOffset); projectedSYT = addFWCycleTimeToFWCycleTime (projectedSYT, fractionalFrameCycleCount); // The time drift is the difference between the projected time stamp and SYT. // We must convert the time drift to cycles. cycleDrift = addFWCycleTimeToFWCycleTime(projectedTimeStamp, kPlaySYTDelay << 12); cycleDrift = subtractFWCycleTimeFromFWCycleTime(cycleDrift, projectedSYT); timeDrift = (cycleDrift >> 12) & 0x0F; // Skip an empty packet if we're drifting. // Only consider positive drifting. if ((timeDrift > 0) && (timeDrift < 8)) { FWModifyDCLJump (pGlobalData->DCLProgramID, pLocalData->pBufferGroupSkipEmptyPacketDCLJump, pLocalData->pBufferGroupSkipEmptyPacketDCLLabel); pGlobalData->activePackets--; pLocalData->skippingEmptyPacket = true; } updateDVOutputBuffers( pDCLCommandPtr ); // Update DCL jumps to call underrun proc after this buffer group. //zzz check errors. FWModifyDCLJump (pGlobalData->DCLProgramID, pLocalData->pEndOfBufferGroupDCLJump, pGlobalData->pUnderrunDCLLabel); FWModifyDCLJump (pGlobalData->DCLProgramID, pPrevLocalData->pEndOfBufferGroupDCLJump, pPrevLocalData->pEndOfBufferGroupDCLLabel); } //////////////////////////////////////////////////////////////////////////////// // // DVUpdateOutputBuffers // // This routine updates the buffers for sending DV data. // void updateDVOutputBuffers(DCLCommandPtr pDCLCommandPtr) { DCLCommandPtr pCurrentDCLCommand; DCLCallProcPtr pDCLCallProc; DCLTransferPacketPtr pDCLTransferPacket; DVLocalOutPtr pLocalData, pPrevLocalData; DVGlobalOutPtr pGlobalData; UInt32 localNodeID; UInt32 generation; UInt32 nominalFrameCycleTime; UInt32 syt; UInt32 *pBuffer, *pImageBuffer, *pLastImageBuffer; UInt32 packetNum, dataPacketNum, numPackets; UInt32 dbc; // Recast pDCLCommandPtr. pDCLCallProc = (DCLCallProcPtr) pDCLCommandPtr; // Get data for buffer group. pLocalData = (DVLocalOutPtr) pDCLCallProc->procData; // Get driver data and first DCL command. pGlobalData = pLocalData->pGlobalData; pCurrentDCLCommand = pLocalData->pFirstBufferGroupDCLCommand; nominalFrameCycleTime = pGlobalData->nominalFrameCycleTime; // Get data for previous buffer group. pPrevLocalData = pLocalData->pPrevLocalData; syt = pGlobalData->nextSYT; dbc = pGlobalData->nextDBC; dataPacketNum = pGlobalData->nextDataPacketNum; // Get local node ID. FWGetNodeID( pGlobalData->localFWReferenceID, &localNodeID, &generation); // Get first send packet command for this buffer group. while (pCurrentDCLCommand->opcode != kDCLSendPacketStartOp) pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand; pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentDCLCommand; // Update the packet buffers. numPackets = pLocalData->numPackets; // Get the next frame to output if( pGlobalData->pImageBuffer == nil ) getNextFullOutputFrame( pGlobalData->pDVFrameOutputData, &(pGlobalData->pImageBuffer) ); pImageBuffer = ( pGlobalData->pImageBuffer + (kDVPacketQuadSize * dataPacketNum) ); for( packetNum = 0; packetNum < numPackets; packetNum++) { // Set up packet header. pBuffer = (UInt32 *) pDCLTransferPacket->buffer; pBuffer[0] = 0x00780000 | (localNodeID << 24) | (dbc & 0xFF); if( pGlobalData->isNTSC ) pBuffer[1] = 0x8000FFFF; else pBuffer[1] = 0x8080FFFF; // set PAL bit // if not an empty packet if (pDCLTransferPacket->size == 488) { // Set SYT field if this is the first data packet in the frame. if (dataPacketNum == 0) { if( pGlobalData->isNTSC ) pBuffer[1] = 0x80000000 | (syt & 0xFFFF); else pBuffer[1] = 0x80800000 | (syt & 0xFFFF); // PAL bit syt = addFWCycleTimeToFWCycleTime(syt, pGlobalData->nominalFrameCycleTime); } // Copy data into packet. BlockMoveData(pImageBuffer, pDCLTransferPacket->buffer + kDVPacketCIPSize, kDVPacketDataSize); pImageBuffer += 120; dbc++; dataPacketNum++; // check if frame is done if (dataPacketNum == pGlobalData->numDataPacketsPerFrame ) { IDHDVCompleteEvent event; event.eventHeader.deviceID = pGlobalData->deviceID; event.eventHeader.event = kIDHPrivateEventWriteComplete; event.frameBuffer = (Ptr) pImageBuffer; event.fwCycleTime = syt; postEvent( pGlobalData->isochComponentGlobals, pGlobalData->deviceID, kIDHPrivateEventWriteComplete, (void*)&event); dataPacketNum = 0; pLastImageBuffer = pGlobalData->pImageBuffer; getNextFullOutputFrame( pGlobalData->pDVFrameOutputData, &(pGlobalData->pImageBuffer) ); pImageBuffer = pGlobalData->pImageBuffer; // Mute the audio on repeating frames, based on repeating frame sequences if (pImageBuffer == pLastImageBuffer) { UInt32 i,j,k,n; UInt8 *tPtr; { // Get DSF flag in byte 3 of header (Blue Book p. 113) tPtr = (UInt8 *)pImageBuffer; if ((tPtr[3] &= 0x80) == 0) n=10; // ntsc else n=12; // pal // Go thru the frame (10 times for ntsc, 12 for pal) for (i=0;ipNextDCLCommand; while (pCurrentDCLCommand != nil) { if (pCurrentDCLCommand->opcode != kDCLSendPacketStartOp) pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand; else break; } pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentDCLCommand; } pGlobalData->nextSYT = syt; pGlobalData->nextDBC = dbc; pGlobalData->nextDataPacketNum = dataPacketNum; // Call to update DCL's if we need to. We have to do this on underrun because the update // DCL command won't be run to reflect the changes we've made here. if (pLocalData->needsUpdate) { FWUpdateDCLList (pGlobalData->DCLProgramID, pLocalData->bufferGroupUpdateDCLList, pLocalData->updateListSize); pLocalData->needsUpdate = false; } } //////////////////////////////////////////////////////////////////////////////// // // DVHandleOutputUnderrun // // This routine handles underruns for sending DV data. // void handleDVOutputUnderrun(DCLCommandPtr pDCLCommandPtr) { DCLCallProcPtr pDCLCallProc; DeviceDescriptionPtr pDeviceDescription; FWCommandObjectID isochChannelCommandObjectID; // Recast pDCLCommandPtr. pDCLCallProc = (DCLCallProcPtr) pDCLCommandPtr; // Get driver data. pDeviceDescription = (DeviceDescriptionPtr) pDCLCallProc->procData; // Set up params for stopping isochronous channel. isochChannelCommandObjectID = pDeviceDescription->pGlobalDVOutData->isochChannelCommandObjectID; FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, 0, handleDVOutputUnderrunCompletion, (UInt32) pDCLCallProc); FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID); // Stop the isochronous channel. FWStopIsochronousChannel(isochChannelCommandObjectID); } //////////////////////////////////////////////////////////////////////////////// // // DVHandleOuputUnderrunCompletion // // This routine handles underruns for sending DV data. // void handleDVOutputUnderrunCompletion ( FWCommandObjectID fwCommandObjectID, OSStatus commandStatus, UInt32 completionProcData) { DCLCallProcPtr pDCLCallProc; DeviceDescriptionPtr pDeviceDescription; DVLocalOutPtr pDVPlayBufferGroupData; FWCommandObjectID isochChannelCommandObjectID; UInt32 bufferGroupNum; // Get DCL call proc data. pDCLCallProc = (DCLCallProcPtr) completionProcData; // Get driver data. pDeviceDescription = (DeviceDescriptionPtr) pDCLCallProc->procData; // Reset next SYT, dbc, and data packet num. pDeviceDescription->pGlobalDVOutData->nextSYT = kPlaySYTDelay; pDeviceDescription->pGlobalDVOutData->nextDBC = 0; pDeviceDescription->pGlobalDVOutData->nextDataPacketNum = 0; pDeviceDescription->pGlobalDVOutData->pImageBuffer = nil; // Reset up all of the buffer groups. pDVPlayBufferGroupData = pDeviceDescription->pGlobalDVOutData->pBufferGroupDataList; for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++) { // Update buffer group buffers. // jkl, no update needed, Lynx fwim and OHCI fwim don't do anything for this anyway // pDVPlayBufferGroupData->needsUpdate = true; updateDVOutputBuffers((DCLCommandPtr) pDVPlayBufferGroupData->pBufferGroupDCLCallProc); // Update buffer group jump DCL. if (bufferGroupNum < (kNumPlayBufferGroups - 1)) { FWModifyDCLJump (pDeviceDescription->pGlobalDVOutData->DCLProgramID, pDVPlayBufferGroupData->pEndOfBufferGroupDCLJump, pDVPlayBufferGroupData->pEndOfBufferGroupDCLLabel); } else { FWModifyDCLJump (pDeviceDescription->pGlobalDVOutData->DCLProgramID, pDVPlayBufferGroupData->pEndOfBufferGroupDCLJump, pDeviceDescription->pGlobalDVOutData->pUnderrunDCLLabel); } pDVPlayBufferGroupData = pDVPlayBufferGroupData->pNextLocalData; } // Set up params for starting isochronous channel. isochChannelCommandObjectID = pDeviceDescription->pGlobalDVOutData->isochChannelCommandObjectID; FWSetFWCommandParams (isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, 0, nil, 0); FWSetIsochChannelCommandIsochChannelID (isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID); // Restart the isochronous channel. FWStartIsochronousChannel (isochChannelCommandObjectID); } /////////////////////////////////////////////////////////////////////// // // DVDisposeDCLOutput // /////////////////////////////////////////////////////////////////////// OSErr disposeDCLOutput( DVGlobalOutPtr pOutputData ) { DVLocalOutPtr pLocalData, pNextLocalData; UInt32 bufferGroupNum; OSErr error = noErr; if( pOutputData != nil ) { // Deallocate play buffer group data records. // and update lists associated with them pLocalData = pOutputData->pBufferGroupDataList; for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++) { if( pLocalData != nil ) { pNextLocalData = pLocalData->pNextLocalData; dclDeallocatePlayBufferGroup (pLocalData); pLocalData = pNextLocalData; } } if( pOutputData->pDCLCommandPool != nil ) dclDeallocateCommandPool( pOutputData->pDCLCommandPool ); if( pOutputData->pDVFrameOutputData != nil ) releaseDVFrameIO( pOutputData->pDVFrameOutputData ); FWDisposeDCLProgram( pOutputData->DCLProgramID ); if( pOutputData->pTransmitBuffers != nil ) PoolDeallocate ( pOutputData->pTransmitBuffers ); if( pOutputData->isochChannelCommandObjectID != nil ) FWDeallocateFWCommandObject ( pOutputData->isochChannelCommandObjectID ); PoolDeallocate ( pOutputData ); } return( error ); } ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// // Utilitiy Routines ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////// // Buffer Allocation Routines // For Output /////////////////// //////////////////////////////////////////////////////////////////////////////// // // DVCAllocatePlayBufferGroup // // This routine allocates a buffer group for playing. // DVLocalOutPtr dclAllocatePlayBufferGroup(DVGlobalOutPtr pGlobalData) { DVLocalOutPtr pLocalData = nil, pPrevLocalData, pNextLocalData; OSErr error = noErr; // Allocate buffer group data record. pLocalData = (DVLocalOutPtr) PoolAllocateResident(sizeof (DVLocalOut), true); if (pLocalData != nil) pLocalData->pGlobalData = pGlobalData; else error = memFullErr; // Insert buffer group data record into list. if (error == noErr) { pNextLocalData = pGlobalData->pBufferGroupDataList; if (pNextLocalData != nil) { pPrevLocalData = pNextLocalData->pPrevLocalData; pNextLocalData->pPrevLocalData = pLocalData; pLocalData->pNextLocalData = pNextLocalData; } else { pPrevLocalData = pLocalData; pGlobalData->pBufferGroupDataList = pLocalData; } pPrevLocalData->pNextLocalData = pLocalData; pLocalData->pPrevLocalData = pPrevLocalData; } return pLocalData; } //////////////////////////////////////////////////////////////////////////////// // // DVDeallocatePlayBufferGroup // // This routine deallocates a buffer group for playing. // void dclDeallocatePlayBufferGroup( DVLocalOutPtr pLocalData ) { if (pLocalData != nil) { if (pLocalData->bufferGroupUpdateDCLList != nil) PoolDeallocate ((Ptr) pLocalData->bufferGroupUpdateDCLList); PoolDeallocate ( pLocalData ); } } //////////////////////////////////////////////////////////////////////////////// // // DVCreatePlayBufferGroupUpdateList // // This routine creates the update list for a play buffer group. // OSErr createDVPlayBufferGroupUpdateList( DVLocalOutPtr pLocalData) { DCLCommandPtr pDCLCommand, pLastDCLCommand; DCLCommandPtr *updateDCLList, *pUpdateDCLListEntry; UInt32 opcode; UInt32 updateListSize; OSErr error = noErr; // Loop through all DCL commands in buffer group and count all send packet DCL // commands. pDCLCommand = pLocalData->pFirstBufferGroupDCLCommand; pLastDCLCommand = pLocalData->pLastBufferGroupDCLCommand; updateListSize = 0; while (pDCLCommand != pLastDCLCommand) { opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask; if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp)) updateListSize++; pDCLCommand = pDCLCommand->pNextDCLCommand; } opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask; if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp)) updateListSize++; // Allocate update list. updateDCLList = (DCLCommandPtr *) PoolAllocateResident (updateListSize * sizeof (DCLCommandPtr), false); if (updateDCLList == nil) error = memFullErr; // Loop through all DCL commands in buffer group and add all send packet DCL // commands to update list. if (error == noErr) { pDCLCommand = pLocalData->pFirstBufferGroupDCLCommand; pLastDCLCommand = pLocalData->pLastBufferGroupDCLCommand; pUpdateDCLListEntry = updateDCLList; while (pDCLCommand != pLastDCLCommand) { opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask; if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp)) *pUpdateDCLListEntry++ = pDCLCommand; pDCLCommand = pDCLCommand->pNextDCLCommand; } opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask; if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp)) *pUpdateDCLListEntry++ = pDCLCommand; } // Save update list. if (error == noErr) { pLocalData->bufferGroupUpdateDCLList = updateDCLList; pLocalData->updateListSize = updateListSize; } else { pLocalData->bufferGroupUpdateDCLList = nil; pLocalData->updateListSize = 0; } return ( error ); } //////////////////////////////////////////////////////////////////////////////////// // DCL Allocation Routines // // We run our own allocation engine because of the sheer number of DCLs. The command // pool holds larger blocks out of which we allocate the DCL commands, this takes // some work out of the hands of the memory manager. // /////////////////// //////////////////////////////////////////////////////////////////////////////// // // AllocateDCLCommand // // This routine allocates a DCL command. // DCLCommandPtr dclAllocateCommand(DCLCommandPoolPtr pDCLCommandPool, UInt32 dclSize) { DCLCommandBlockPtr pDCLCommandBlock; DCLCommandPtr pDCLCommand; // Get last DCL command block in pool. pDCLCommandBlock = pDCLCommandPool->pLastDCLCommandBlock; // Check if we have enough room in command block. Allocate a new block if there // is not enough room. if (pDCLCommandBlock != nil) { if( dclSize > (kDCLCommandPoolBlockSize - pDCLCommandBlock->blockLevel) ) pDCLCommandBlock = dclAllocateCommandBlock( pDCLCommandPool ); } else { pDCLCommandBlock = dclAllocateCommandBlock ( pDCLCommandPool ); } // Allocate a DCL from the command block. if (pDCLCommandBlock != nil) { pDCLCommand = (DCLCommandPtr) (pDCLCommandBlock->block + pDCLCommandBlock->blockLevel); pDCLCommandBlock->blockLevel += dclSize; } else { pDCLCommand = nil; } return pDCLCommand; } //////////////////////////////////////////////////////////////////////////////// // // AllocateDCLCommandBlock // // This routine allocates a DCL command block. // DCLCommandBlockPtr dclAllocateCommandBlock( DCLCommandPoolPtr pDCLCommandPool ) { DCLCommandBlockPtr pDCLCommandBlock = nil; Ptr block; OSStatus error = noErr; // Allocate command block record. pDCLCommandBlock = (DCLCommandBlockPtr) PoolAllocateResident( sizeof(DCLCommandBlock), true ); if( pDCLCommandBlock == nil ) error = memFullErr; // Allocate command block block. if( error == noErr ) { block = PoolAllocateResident( kDCLCommandPoolBlockSize, false ); if (block != nil) pDCLCommandBlock->block = block; else error = memFullErr; } // Insert command block into pool. if( error == noErr ) { if( pDCLCommandPool->pFirstDCLCommandBlock != nil ) pDCLCommandPool->pLastDCLCommandBlock->pNextDCLCommandBlock = pDCLCommandBlock; else pDCLCommandPool->pFirstDCLCommandBlock = pDCLCommandBlock; pDCLCommandPool->pLastDCLCommandBlock = pDCLCommandBlock; pDCLCommandBlock->pNextDCLCommandBlock = nil; } // Clean up on error. if( ( error != noErr ) && ( pDCLCommandBlock != nil ) ) { dclDeallocateCommandBlock( pDCLCommandBlock ); pDCLCommandBlock = nil; } // Return results. return( pDCLCommandBlock ); } //////////////////////////////////////////////////////////////////////////////// // // DeallocateDCLCommandBlock // // This routine deallocates a DCL command block. // void dclDeallocateCommandBlock( DCLCommandBlockPtr pDCLCommandBlock ) { if( pDCLCommandBlock != nil ) { // Deallocate command block block. if( pDCLCommandBlock->block != nil ) PoolDeallocate( pDCLCommandBlock->block ); // Deallocate command block record. PoolDeallocate( (Ptr) pDCLCommandBlock ); //zzz if there were any errors...? } } //////////////////////////////////////////////////////////////////////////////// // // AllocateDCLCommandPool // // This routine allocates a DCL command pool. // DCLCommandPoolPtr dclAllocateCommandPool( void ) { DCLCommandPoolPtr pDCLCommandPool; // Allocate DCL command pool record. pDCLCommandPool = (DCLCommandPoolPtr) PoolAllocateResident( sizeof( DCLCommandPool ), true ); return (pDCLCommandPool); } //////////////////////////////////////////////////////////////////////////////// // // DeallocateDCLCommandPool // // This routine deallocates a DCL command pool. // void dclDeallocateCommandPool( DCLCommandPoolPtr pDCLCommandPool ) { DCLCommandBlockPtr pDCLCommandBlock, pNextDCLCommandBlock; if( pDCLCommandPool != nil ) { // Deallocate all command blocks. pDCLCommandBlock = pDCLCommandPool->pFirstDCLCommandBlock; while( pDCLCommandBlock != nil ) { pNextDCLCommandBlock = pDCLCommandBlock->pNextDCLCommandBlock; dclDeallocateCommandBlock( pDCLCommandBlock ); pDCLCommandBlock = pNextDCLCommandBlock; } // Deallocate command pool record. PoolDeallocate ((Ptr) pDCLCommandPool); //zzz if there was an error...? } } //////////////////////////////////////////////////////////////////////////////// // // AddFWCycleTimeToFWCycleTime // // This routine adds the two given cycle times. // UInt32 addFWCycleTimeToFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 ) { UInt32 secondCount, cycleCount, cycleOffset; UInt32 cycleTime; // Add cycle offsets. cycleOffset = (cycleTime1 & 0x0FFF) + (cycleTime2 & 0x0FFF); // Add cycle counts. cycleCount = (cycleTime1 & 0x01FFF000) + (cycleTime2 & 0x01FFF000); // Add any carry over from cycle offset to cycle count. if (cycleOffset > 3071) { cycleCount += 0x1000; cycleOffset -= 3072; } // Add secondCounts. secondCount = (cycleTime1 & 0xFE000000) + (cycleTime2 & 0xFE000000); // Add any carry over from cycle count to secondCount. if (cycleCount > (7999 << 12)) { secondCount += 0x02000000; cycleCount -= (8000 << 12); } // Put everything together into cycle time. cycleTime = secondCount | cycleCount | cycleOffset; return (cycleTime); } //////////////////////////////////////////////////////////////////////////////// // // SubtractFWCycleTimeFromFWCycleTime // // This routine subtracts the two given cycle times. // UInt32 subtractFWCycleTimeFromFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 ) { SInt32 secondCount, cycleCount, cycleOffset; UInt32 cycleTime; // Subtract cycle offsets. cycleOffset = (cycleTime1 & 0x0FFF) - (cycleTime2 & 0x0FFF); // Subtract cycle counts. cycleCount = (cycleTime1 & 0x01FFF000) - (cycleTime2 & 0x01FFF000); // Subtract any borrow over from cycle offset to cycle count. if (cycleOffset < 0) { cycleCount -= 0x1000; cycleOffset += 3072; } // Subtract secondCounts. secondCount = (cycleTime1 & 0xFE000000) - (cycleTime2 & 0xFE000000); // Subtract any borrow over from cycle count to secondCount. if (cycleCount < 0) { secondCount -= 0x02000000; cycleCount += (8000 << 12); } // Put everything together into cycle time. cycleTime = secondCount | cycleCount | cycleOffset; return (cycleTime); } //////////////////////////////////////////////////////////////////////////////// // // ConvertFractionalSecondsToFWCycleTime // // This routine converts a time represented in fractional seconds to a time // represented in cycle timer format. // UInt32 convertFractionalSecondsToFWCycleTime( UInt32 secondsNumerator, UInt32 secondsDenominator ) { float fSecondCount; float fCycleCount; float fCycleOffset; UInt32 iSecondsCount; UInt32 iCycleCount; UInt32 iCycleOffset; UInt32 secondsCycleTime; // Convert fractional seconds into floating point and compute seconds count. fSecondCount = ((float) secondsNumerator) / ((float) secondsDenominator); iSecondsCount = (UInt32) fSecondCount; // Subtract whole seconds out of fSecondCount and convert to cycle count. fCycleCount = (fSecondCount - ((float) iSecondsCount)) * 8000.0; iCycleCount = (UInt32) fCycleCount; // Subtract whole cycles out of fCycleCount and convert to cycle offset. fCycleOffset = (fCycleCount - ((float) iCycleCount)) * 3072.0; iCycleOffset = (UInt32) fCycleOffset; // Convert to cycle timer format. secondsCycleTime = (iSecondsCount << 25) | (iCycleCount << 12) | iCycleOffset; return (secondsCycleTime); } ////////////////////////////////////////////////////////////////////// // DVGetNextFullOutputFrame // // Returns next frame full of data for output // OSErr getNextFullOutputFrame( DVIODataPtr pOData, UInt32** ppFrame ) { OSErr error = noErr; QElemPtr pQElem; // dequeue an element from the output list error = PBDequeueFirst( pOData->pFullQ, &pQElem); // if we got a frame if( error == noErr ) { // put old frame on empty queue error = PBEnqueueLast( pOData->pCurFrameElem, pOData->pEmptyQ ); //zzz what if we can't enqueue // save new frame element pOData->pCurFrameElem = pQElem; // return the actual frame *ppFrame = ((DVFrameQElemPtr)pQElem)->pFrame; } else // return old frame *ppFrame = ((DVFrameQElemPtr)pOData->pCurFrameElem)->pFrame; return( error ); } /////////////////////////////////////////////////////////////////////// // // DVGetNextEmptyOutputFrame // /////////////////////////////////////////////////////////////////////// OSErr getNextEmptyOutputFrame( DVIODataPtr pOData, UInt32** ppFrame) { OSErr error = noErr; DVFrameQElemPtr pQElem; // dequeue an element from the empty list error = PBDequeueFirst( pOData->pEmptyQ, (QElemPtr *) &pQElem ); // if we got an element if( error == noErr ) { // put the frame on the busy queue error = PBEnqueueLast( (QElemPtr) pQElem, pOData->pBusyQ ); //zzz and if we get an error // return the frame *ppFrame = pQElem->pFrame; } else *ppFrame = nil; return( error ); } /////////////////////////////////////////////////////////////////////// // // DVQueueNextFullOutputFrame // /////////////////////////////////////////////////////////////////////// OSErr queueNextFullOutputFrame( DVIODataPtr pOData, UInt32* pFrame ) { OSStatus error = noErr; QElemPtr pCurElem; // find frame in busy queue pCurElem = pOData->pBusyQ->qHead; while( pCurElem != nil) { // is it the one we want? if( ((DVFrameQElemPtr)pCurElem)->pFrame == pFrame ) break; else // next pCurElem = pCurElem->qLink; } // if we found it if( pCurElem != nil ) { // get the element from the busy queue error = PBDequeue( pCurElem, pOData->pBusyQ ); // and put it on the full queue if( error == noErr ) error = PBEnqueueLast( pCurElem, pOData->pFullQ ); } else error = kDVFrameNotFoundErr; return( error ); } ////////////////////////////////////////////////////////////////////// // DVGetNextEmptyInputFrame // // Returns next empty frame for input // OSErr getNextEmptyInputFrame( DVIODataPtr pIData, UInt32** ppFrame ) { OSErr error = noErr; QElemPtr pQElem; // dequeue an element from the output list error = PBDequeueFirst( pIData->pEmptyQ, &pQElem); if( error != noErr ) { error = PBDequeueFirst( pIData->pFullQ, &pQElem); } // if we got a frame if( error == noErr ) { // put old frame on full queue error = PBEnqueueLast( pIData->pCurFrameElem, pIData->pFullQ ); //zzz what if we can't enqueue // save new frame element pIData->pCurFrameElem = pQElem; // return the actual frame *ppFrame = ((DVFrameQElemPtr)pQElem)->pFrame; } else // return old frame *ppFrame = ((DVFrameQElemPtr)pIData->pCurFrameElem)->pFrame; return( error ); } ////////////////////////////////////////////////////////////////////// // // DVGetNextFullInputFrame // // Returns next full frame of data for input // OSErr getNextFullInputFrame( DVIODataPtr pIData, UInt32** ppFrame ) { OSErr error = noErr; DVFrameQElemPtr pQElem; // dequeue an element from the full list error = PBDequeueFirst( pIData->pFullQ, (QElemPtr *) &pQElem ); // if we got an element if( error == noErr ) { // put the frame on the busy queue error = PBEnqueueLast( (QElemPtr) pQElem, pIData->pBusyQ ); //zzz and if we get an error // return the frame *ppFrame = pQElem->pFrame; } else *ppFrame = nil; return( error ); } /////////////////////////////////////////////////////////////////////// // // DVReleaseFullInputFrame // /////////////////////////////////////////////////////////////////////// OSErr releaseFullInputFrame( DVIODataPtr pIData, UInt32* pFrame ) { OSStatus error = noErr; QElemPtr pCurElem; // find frame in busy queue pCurElem = pIData->pBusyQ->qHead; while( pCurElem != nil) { // is it the one we want? if( ((DVFrameQElemPtr)pCurElem)->pFrame == pFrame ) break; else // next pCurElem = pCurElem->qLink; } // if we found it if( pCurElem != nil ) { // get the element from the busy queue error = PBDequeue( pCurElem, pIData->pBusyQ ); // and put it on the empty queue if( error == noErr ) error = PBEnqueueLast( pCurElem, pIData->pEmptyQ ); } else error = kDVFrameNotFoundErr; return( error ); } ////////////////////////////////////////////////////////////////////// // // DVGetCurrentDCLFrame // Boolean isDVFinished( DVIODataPtr pIOData ) { Boolean result = false; if( pIOData != nil ) { if( pIOData->pFullQ->qHead == nil ) result = true; else result = false; } else result = false; return( result ); } ////////////////////////////////////////////////////////////////////// // // DVGetCurrentDCLFrame // OSErr getCurrentDCLFrame( DVIODataPtr pIOData, UInt32** ppFrame ) { OSErr error = noErr; if( pIOData != nil ) { // get the current frame *ppFrame = ((DVFrameQElemPtr)pIOData->pCurFrameElem)->pFrame; } else { error = kDVFrameNotFoundErr; *ppFrame = nil; } return( error ); } /////////////////////////////////////////////////////////////////////// // // DVInitFrameIO // OSErr initDVFrameInput(UInt32 standard, DVIODataPtr* ppIOData) { OSErr error = noErr; DVIODataPtr pIOData; DVFrameQElemPtr pQElem[kNumInputDVFrames]; UInt32 i; UInt32 frameSize; // create output data struct pIOData = (DVIODataPtr) PoolAllocateResident( sizeof( DVIOData ), true ); if( pIOData == nil ) { error = memFullErr; goto bail; } // create busy queue error = PBQueueCreate( &(pIOData->pBusyQ) ); if( error != noErr ) goto bail; error = PBQueueInit( pIOData->pBusyQ ); if( error != noErr ) goto bail; // create empty queue error = PBQueueCreate( &(pIOData->pEmptyQ) ); if( error != noErr ) goto bail; error = PBQueueInit( pIOData->pEmptyQ ); if( error != noErr ) goto bail; // create full queue error = PBQueueCreate( &(pIOData->pFullQ) ); if( error != noErr ) goto bail; error = PBQueueInit( pIOData->pFullQ ); if( error != noErr ) goto bail; for(i = 0; i < kNumInputDVFrames; i++) { // create queue elements pQElem[i] = (DVFrameQElemPtr) PoolAllocateResident( sizeof(DVFrameQElem), true ); if ( pQElem[i] == nil ) { error = memFullErr; goto bail; } // create frames if (standard == ntscIn) frameSize = kNTSCCompressedBufferSize; else frameSize = kPALCompressedBufferSize; pQElem[i]->pFrame = (UInt32 *) PoolAllocateResident( frameSize, true ); if ( pQElem[i]->pFrame == nil ) { error = memFullErr; goto bail; } } // set current frame to start pIOData->pCurFrameElem = (QElemPtr) pQElem[0]; // insert the rest of the queue elements into empty queue for(i = 1; i < kNumInputDVFrames; i++ ) { error = PBEnqueueLast( (QElemPtr) pQElem[i], pIOData->pEmptyQ ); } bail: if( error != noErr ) { releaseDVFrameIO( pIOData ); pIOData = nil; } *ppIOData = pIOData; return( error ); } /////////////////////////////////////////////////////////////////////// // // initDVFrameOutput // OSErr initDVFrameOutput(UInt32 standard, DVIODataPtr* ppIOData) { OSErr error = noErr; DVIODataPtr pIOData; DVFrameQElemPtr pQElem[kNumOutputDVFrames]; UInt32 i; UInt32 frameSize; // create output data struct pIOData = (DVIODataPtr) PoolAllocateResident(sizeof(DVIOData), true); if( pIOData == nil ) { error = memFullErr; goto bail; } // create busy queue error = PBQueueCreate( &(pIOData->pBusyQ) ); if( error != noErr ) goto bail; error = PBQueueInit( pIOData->pBusyQ ); if( error != noErr ) goto bail; // create empty queue error = PBQueueCreate( &(pIOData->pEmptyQ) ); if( error != noErr ) goto bail; error = PBQueueInit( pIOData->pEmptyQ ); if( error != noErr ) goto bail; // create full queue error = PBQueueCreate( &(pIOData->pFullQ) ); if( error != noErr ) goto bail; error = PBQueueInit( pIOData->pFullQ ); if( error != noErr ) goto bail; for(i = 0; i < kNumOutputDVFrames; i++) { // create queue elements pQElem[i] = (DVFrameQElemPtr) PoolAllocateResident( sizeof(DVFrameQElem), true ); if ( pQElem[i] == nil ) { error = memFullErr; goto bail; } // create frames if (standard == ntscIn) frameSize = kNTSCCompressedBufferSize; else frameSize = kPALCompressedBufferSize; pQElem[i]->pFrame = (UInt32 *) PoolAllocateResident( frameSize, true ); if ( pQElem[i]->pFrame == nil ) { error = memFullErr; goto bail; } } // set current frame to start pIOData->pCurFrameElem = (QElemPtr) pQElem[0]; // insert the rest of the queue elements into empty queue for(i = 1; i < kNumOutputDVFrames; i++ ) { error = PBEnqueueLast( (QElemPtr) pQElem[i], pIOData->pEmptyQ ); } bail: if( error != noErr ) { releaseDVFrameIO( pIOData ); pIOData = nil; } *ppIOData = pIOData; return( error ); } /////////////////////////////////////////////////////////////////////// // // DVDeleteFrameAndElement // /////////////////////////////////////////////////////////////////////// static void deleteDVFrameAndElement( DVFrameQElemPtr pQElem ) { // deallocate frame // checks just to be safe if( pQElem != nil ) { if( pQElem->pFrame != nil ) PoolDeallocate( pQElem->pFrame ); // deallocate queue element PoolDeallocate( pQElem ); //zzz never check errors } } /////////////////////////////////////////////////////////////////////// // // DVReleaseFrameIO // /////////////////////////////////////////////////////////////////////// OSErr releaseDVFrameIO( DVIODataPtr pIOData ) { OSErr error = noErr; QElemPtr pQElem; if( pIOData != nil ) { if( pIOData->pBusyQ != nil ) { // dequeue and delete all elements in the busy queue do { error = PBDequeueFirst( pIOData->pBusyQ, (QElemPtr *) &pQElem ); if( error == noErr ) deleteDVFrameAndElement( (DVFrameQElemPtr) pQElem ); } while( error == noErr ); error = PBQueueDelete( pIOData->pBusyQ ); } if( pIOData->pEmptyQ != nil ) { // dequeue and delete all elements in the empty queue do { error = PBDequeueFirst( pIOData->pEmptyQ, (QElemPtr *) &pQElem ); if( error == noErr ) deleteDVFrameAndElement( (DVFrameQElemPtr) pQElem ); } while( error == noErr ); error = PBQueueDelete( pIOData->pEmptyQ ); } if( pIOData->pFullQ != nil ) { // dequeue and delete all elements in the full queue do { error = PBDequeueFirst( pIOData->pFullQ, (QElemPtr *) &pQElem ); if( error == noErr ) deleteDVFrameAndElement( (DVFrameQElemPtr) pQElem ); } while( error == noErr ); error = PBQueueDelete( pIOData->pFullQ ); } // delete current frame and element if( pIOData->pCurFrameElem != nil ) deleteDVFrameAndElement( (DVFrameQElemPtr) pIOData->pCurFrameElem ); // dump the main guy PoolDeallocate( pIOData ); } return( error ); } /////////////////////////////////////////////////////////////////////// // // DVInitDCLInput // /////////////////////////////////////////////////////////////////////// OSErr dclInitInput(DeviceDescriptionPtr pDeviceDescription, IsochComponentGlobals* isochComponentGlobals ) { Ptr pReceiveBuffer; Ptr pDCLCommand; DVGlobalInPtr pGlobalData; DCLLabelPtr pStartDCLLabel; DVLocalInPtr pLocalData; DCLCommandPtr *updateDCLList, *startUpdateDCLList; DCLUpdateDCLListPtr pDCLUpdateDCLList; DCLTransferPacketPtr pDCLTransferPacket; DCLCallProcPtr pDCLCallProc; DCLJumpPtr pDCLInputLoop; UInt32 packetNum; UInt32 bufferNum; UInt32 updateListSize; OSErr error = noErr; // Create the global data structure pGlobalData = PoolAllocateResident( sizeof(DVGlobalIn), true ); if( pGlobalData == nil ) error = memFullErr; else pDeviceDescription->pGlobalDVInData = pGlobalData; pGlobalData->isochComponentGlobals = isochComponentGlobals; pGlobalData->deviceID = pDeviceDescription->deviceID; pGlobalData->pDCLList = nil; pGlobalData->ppUpdateDCLList = nil; pGlobalData->pDVFrameInputData = nil; // Create input buffer. Add slop for page alignment pGlobalData->pReceiveBuffers = PoolAllocateResident(kInputBufferSize + 4096, false); if( pGlobalData->pReceiveBuffers == nil ) { error = memFullErr; goto bail; } pReceiveBuffer = pGlobalData->pReceiveBuffers; // Align buffer, should be aligned already pReceiveBuffer = (Ptr) ((((UInt32) pReceiveBuffer) + 4096) & 0xfffff000); #ifdef dbg_in_init ASSERTVAR( error, "Error initializing input DCL data structures. Error: %d" ); #endif // init frame IO error = initDVFrameInput(pDeviceDescription->standard, &pGlobalData->pDVFrameInputData); if( error != noErr ) goto bail; error = getCurrentDCLFrame( pGlobalData->pDVFrameInputData, (UInt32 **) &(pGlobalData->pImageBuffer) ); if( error != noErr ) goto bail; #ifdef dbg_in_init ASSERTVAR( error, "Error initializing input DCL frame I/O system. Error: %d" ); #endif // Fill out global structure pGlobalData->isNTSC = (pDeviceDescription->standard == ntscIn); pGlobalData->packetCount = 0; error = FWCreateDCLProgram( &(pGlobalData->DCLProgramID) ); pDeviceDescription->dclProgramID = pGlobalData->DCLProgramID; if( error != noErr ) goto bail; // create list of DCLs for channel // make sure we zero it so deallocation can work even if not fully initialized pGlobalData->pDCLList = (DCLCommandPtr) PoolAllocateResident( kDCLReadProgramSize, true ); if ( pGlobalData->pDCLList == nil ) { error = memFullErr; goto bail; } // create list of update DCLs for channel // make sure we zero it so deallocation can work even if not fully initialized pGlobalData->ppUpdateDCLList = (DCLCommandPtr *) PoolAllocateResident( kNumInputDCLs * sizeof (DCLCommandPtr), true ); if ( pGlobalData->ppUpdateDCLList == nil ) { error = memFullErr; goto bail; } #ifdef dbg_in_init DEBUGVAR( "Initialized input DCL commands. Error: %d", error ); #endif // Get pointer to start of DCL commands and update list. pDCLCommand = (Ptr) pGlobalData->pDCLList; updateDCLList = pGlobalData->ppUpdateDCLList; // Create label for start of loop. pStartDCLLabel = (DCLLabelPtr) pDCLCommand; pDCLCommand += sizeof( DCLLabel ); pStartDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pStartDCLLabel->opcode = kDCLLabelOp; // Create input buffer lists of 100 packets each. for (bufferNum = 0; bufferNum < kNumInputBuffers; bufferNum++) { // Create the DCL input record record and fill it in. pLocalData = (DVLocalInPtr) PoolAllocateResident( sizeof(DVLocalIn), true ); if( pLocalData == nil ) { error = memFullErr; goto bail; } pLocalData->pFirstCmd = (DCLCommandPtr) pDCLCommand; pLocalData->pGlobalData = pGlobalData; startUpdateDCLList = updateDCLList; updateListSize = 0; // Create transfer DCL for each packet. for (packetNum = 0; packetNum < kNumPacketsPerInputBuffer; packetNum++) { // Receive one packet up to kReceiveDVPacketSize bytes. pDCLTransferPacket = (DCLTransferPacketPtr) pDCLCommand; pDCLCommand += sizeof (DCLTransferPacket); pDCLTransferPacket->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pDCLTransferPacket->opcode = kDCLReceivePacketStartOp; pDCLTransferPacket->buffer = pReceiveBuffer; pDCLTransferPacket->size = kReceiveDVPacketSize; *updateDCLList++ = (DCLCommandPtr) pDCLTransferPacket; updateListSize++; pReceiveBuffer += kAlignedDVPacketSize; } // Create update DCL list. pDCLUpdateDCLList = (DCLUpdateDCLListPtr) pDCLCommand; pDCLCommand += sizeof (DCLUpdateDCLList); pDCLUpdateDCLList->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp; pDCLUpdateDCLList->dclCommandList = startUpdateDCLList; pDCLUpdateDCLList->numDCLCommands = updateListSize; // Call the DVStorePackets proc. pDCLCallProc = (DCLCallProcPtr) pDCLCommand; pDCLCommand += sizeof (DCLCallProc); pDCLCallProc->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pDCLCallProc->opcode = kDCLCallProcOp; pDCLCallProc->proc = storeDVPackets; pDCLCallProc->procData = (UInt32) pLocalData; } #ifdef dbg_in_init DEBUGVAR( "Created input DCL program. Error: %d", error ); #endif // Loop to start of ping pong. pDCLInputLoop = (DCLJumpPtr) pDCLCommand; pDCLInputLoop->pNextDCLCommand = nil; pDCLInputLoop->opcode = kDCLJumpOp; pDCLInputLoop->pJumpDCLLabel = pStartDCLLabel; // Set start of DCL program. error = FWSetDCLProgramStart (pGlobalData->DCLProgramID, pGlobalData->pDCLList); bail: if( error != noErr ) { disposeDCLInput( pGlobalData ); pGlobalData = nil; } return( error ); } /////////////////////////////////////////////////////////////////////// // // DVStorePackets( // /////////////////////////////////////////////////////////////////////// void storeDVPackets( DCLCommandPtr pDCLCommandPtr ) { DCLCallProcPtr pDCLCallProc; DVLocalInPtr pLocalData; DVGlobalInPtr pGlobalData; DCLCommandPtr pCurrentCmd; DCLTransferPacketPtr pDCLTransferPacket; Ptr pPacketBuffer; UInt32 packetHeader, packetSize, packetNum, packetPerFrame; Boolean vSyncDetected; UInt8 currentSequenceCount; // Recast DCL command. pDCLCallProc = (DCLCallProcPtr) pDCLCommandPtr; // Get input data. pLocalData = (DVLocalInPtr) pDCLCallProc->procData; pGlobalData = pLocalData->pGlobalData; // Get info from ping pong data. pCurrentCmd = pLocalData->pFirstCmd; // How many packets we talkin'? packetPerFrame = pGlobalData->isNTSC ? kNumPacketsPerNTSCFrame : kNumPacketsPerPALFrame; for ( packetNum = 0; packetNum < kNumPacketsPerInputBuffer; packetNum++ ) { // compute size of transfer pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentCmd; pPacketBuffer = pDCLTransferPacket->buffer; packetHeader = *((UInt32*) pPacketBuffer); pPacketBuffer += 4; // 4 byte 1394 header packetSize = (packetHeader & kFWIsochDataLength) >> kFWIsochDataLengthPhase; // detect vSync if( packetSize > 8 ) vSyncDetected = ((*(short *)(pPacketBuffer + 8) & 0xE0F8 ) == 0x0000 ); else vSyncDetected = false; // get current data block sequence counter value and increment saved value // saved value will be decremented at the end for null packets which do not // increment data block count currentSequenceCount = pPacketBuffer[3]; pGlobalData->lastSequenceCount++; // skip over CIP header pPacketBuffer += 8; // 8 bytes packetSize -= 8; // RecordEventLogger( 'isoc', 'cnt ', pGlobalData->lastSequenceCount, currentSequenceCount); if( vSyncDetected ) { if( pGlobalData->packetCount == packetPerFrame ) // if we got our frameSync at the right time { IDHDVCompleteEvent event; // post a DV event to let the curious know... event.eventHeader.deviceID = pGlobalData->deviceID; event.eventHeader.event = kIDHPrivateEventReadComplete; event.frameBuffer = (Ptr) pGlobalData->pImageBuffer; event.bufferSize = pGlobalData->packetCount * kDVPayloadPacketSize; event.fwCycleTime = (packetHeader & kFWIsochSy) >> kFWIsochSyPhase; postEvent( pGlobalData->isochComponentGlobals, pGlobalData->deviceID, kIDHPrivateEventReadComplete, (void*)&event); getNextEmptyInputFrame( pGlobalData->pDVFrameInputData, (UInt32 **) &(pGlobalData->pImageBuffer) ); // get a new frame } pGlobalData->packetCount = 0; // start a new frame if ((packetSize == kDVPayloadPacketSize) && (currentSequenceCount == pGlobalData->lastSequenceCount)) { // store the packet BlockMoveData ( pPacketBuffer, (Ptr)((UInt32) pGlobalData->pImageBuffer + (pGlobalData->packetCount * kDVPayloadPacketSize)), packetSize ); pGlobalData->packetCount++; } } else { if (currentSequenceCount == pGlobalData->lastSequenceCount) { if ((pGlobalData->packetCount < packetPerFrame) && (packetSize == kDVPayloadPacketSize)) { // store the packet BlockMoveData ( pPacketBuffer, (Ptr)((UInt32) pGlobalData->pImageBuffer + (pGlobalData->packetCount * kDVPayloadPacketSize)), packetSize ); pGlobalData->packetCount++; } else if ((pGlobalData->packetCount >= packetPerFrame) && (packetSize > 0)) { // too many packets between vSync detection, start new frame pGlobalData->packetCount = 0; RecordEventLogger( 'isoc', 'stor', 0, '2man'); } } else { // packet out of sequence, start new frame pGlobalData->packetCount = 0; RecordEventLogger( '****', 'seq!', currentSequenceCount, pGlobalData->lastSequenceCount); } } // if null packet, decrement saved count since DBC is not incremented // otherwise set last count to current count to resynch counts if bad sequence if (packetSize == 0) // already deceremented CIP header from packet size pGlobalData->lastSequenceCount--; else if( packetSize == kDVPayloadPacketSize) pGlobalData->lastSequenceCount = currentSequenceCount; else { pGlobalData->lastSequenceCount--; RecordEventLogger( '****', 'wird', pGlobalData->lastSequenceCount, packetSize); // DebugStr( "\pWeird packet"); } // update for next packet pCurrentCmd = pCurrentCmd->pNextDCLCommand; } } /////////////////////////////////////////////////////////////////////// // // DVDisposeDCLInput // /////////////////////////////////////////////////////////////////////// OSErr disposeDCLInput( DVGlobalInPtr pInputData ) { Ptr pDCLCommand; DCLCallProcPtr pDCLPingPongProc; short bufferNum; OSErr error = noErr; if( pInputData != nil ) { // deallocate receive buffer if (pInputData->pReceiveBuffers != nil) PoolDeallocate(pInputData->pReceiveBuffers); // traverse through the DCL program to get to where // the buffers are and deallocate them if( pInputData->pDCLList != nil ) { pDCLCommand = (Ptr) pInputData->pDCLList + sizeof (DCLLabel); // for all call procs for (bufferNum = 0; bufferNum < kNumInputBuffers; bufferNum++) { // find the call proc pDCLCommand += sizeof (DCLTransferPacket) * kNumPacketsPerInputBuffer + sizeof (DCLUpdateDCLList); // dealloc the call proc pDCLPingPongProc = (DCLCallProcPtr) pDCLCommand; pDCLCommand += sizeof (DCLCallProc); if( pDCLPingPongProc->procData != nil ) PoolDeallocate( (LogicalAddress) pDCLPingPongProc->procData ); } PoolDeallocate( pInputData->pDCLList ); } // release update list if( pInputData->ppUpdateDCLList != nil ) error = PoolDeallocate( pInputData->ppUpdateDCLList ); // release frame IO if( pInputData->pDVFrameInputData != nil ) error = releaseDVFrameIO( pInputData->pDVFrameInputData ); // dispose the program error = FWDisposeDCLProgram( pInputData->DCLProgramID ); // and the whole bloody structure PoolDeallocate( pInputData ); } return( error ); } OSErr writeFrame(IsochComponentInstancePtr ih, char *tmpBuffPtr) { DeviceDescriptionPtr pDeviceDescription; OSErr error = noErr; error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription); if (error == noErr) error = queueNextFullOutputFrame(pDeviceDescription->pGlobalDVOutData->pDVFrameOutputData, (UInt32 *) tmpBuffPtr); FailWithVal( error != noErr, Exit, error); Exit: return error; } OSErr getEmptyOutputFrame(IsochComponentInstancePtr ih, Ptr *tmpBuffPtr, UInt32 *size) { DeviceDescriptionPtr pDeviceDescription; OSErr error = noErr; error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription); FailWithVal( error != noErr, Exit, error); *size = (pDeviceDescription->standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize; error = getNextEmptyOutputFrame(pDeviceDescription->pGlobalDVOutData->pDVFrameOutputData, (UInt32 **) tmpBuffPtr); FailWithVal( error != noErr, Exit, error); Exit: return error; } //==================================================================================== // // registerDevice() // Register the device and install the FireWire port callback routines and bus reset proc. // //==================================================================================== OSStatus registerDevice( RegEntryID* inRegEntryID, FWDriverID* outDriverID, DeviceDescriptionPtr pDeviceDescription) { CSRROMEntryID csrROMEntryID; OSStatus err = noErr; err = FWRegisterDriver( inRegEntryID, outDriverID, &csrROMEntryID, (UInt32) pDeviceDescription); if (err) goto Exit; err = FWSetFWClientInitIsochPortProc(*outDriverID, initIsochPort); if (err) goto Exit; err = FWSetFWClientStartIsochPortProc(*outDriverID, startIsochPort); if (err) goto Exit; err = FWSetFWClientStopIsochPortProc(*outDriverID, stopIsochPort); if (err) goto Exit; err = FWSetFWClientReleaseIsochPortProc(*outDriverID, releaseIsochPort); Exit: return err; } // handle any reset notifications for the device OSStatus handleBusReset(FWClientInterfaceParamsPtr pParams, UInt32 *pCommandAcceptance) { DeviceDescriptionPtr pDeviceDescription; OSErr error = noErr; pDeviceDescription = (DeviceDescriptionPtr) pParams->fwClientSpecificData; RecordEventLogger( 'bus ', 'res ', (UInt32) pDeviceDescription->fwClientID, 0); FWClientCommandIsComplete(pParams->fwClientCommandID, error); *pCommandAcceptance = kFWClientCommandAcceptNoMore; error = postEvent( pDeviceDescription->componentGlobals, pDeviceDescription->deviceID, kIDHEventDeviceChanged, nil); return error; }