DVIsochComponent.MacOS9 [plain text]
/*
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):
<DV86> 3/16/00 SF [2438610] Put 1-second delay before checking camera
configuration.
<DV85> 2/14/00 CK [2432944] A little more to kill this bug <sf,cp>
<DV84> 2/10/00 SF [2432944] Add retries to bandwidth/channel allocations.
<DV83> 12/7/99 jkl Changed isocMedia to isochMedia.
<DV82> 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.
<DV81> 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.
<DV80> 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.
<DV79> 9/28/99 KW In IDHRead and IDHWrite, do not tear down read/write
notification if an error occurs.
<DV78> 9/21/99 jkl Backed out this read/write postEvent deferred stuff. A
stack-based variable was killing it and it needs more analysis.
<DV77> 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.
<OX43> 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.
<OX30> 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.
<OX11> 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 <DeviceControl.h>
#include <DeviceControlPriv.h>
#include <DriverServices.h>
#include <Gestalt.h>
#include <Resources.h>
#include <Sound.h>
#include <TextUtils.h>
#include <DeviceControl.h>
#include <IsochronousDataHandler.h>
#include <IsochronousDataHandlerPriv.h>
#include <Folders.h>
#include <fp.h>
#include <stddef.h>
#include <stdio.h>
#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; i<kMaxNotifications; ++i) // purge all notifications for this client
{
if( gGlobals->clientNotification[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; i<kMaxDevicesActive; ++i) // free queue elements
{
DeviceDescription *deviceDescriptionPtr = &gGlobals->deviceDescription[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; i<kMaxDevicesActive; ++i) // free queue elements
{
DeviceDescription *deviceDescriptionPtr = &gGlobals->deviceDescription[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; i<kMaxDevicesActive; ++i)
{
PBQueueDelete( gGlobals->deviceDescription[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; i<kMaxDevicesActive; ++i)
{
PBQueueDelete( gGlobals->deviceDescription[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; i<kMaxDevicesActive; ++i) // initialize device decription structs
{
int j;
// create free and used queue for each device
result = PBQueueCreate( &gGlobals->deviceDescription[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; j<kMaxInstances; ++j)
gGlobals->deviceDescription[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; i<kMaxInstances; ++i) // remove instance from device list
{
if( deviceDescriptionPtr->instancePtr[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; devIndex<kMaxDevicesActive; ++devIndex)
{
if( gGlobals->deviceDescription[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; i<kMaxInstances; ++i)
if( deviceDescriptionPtr->instancePtr[i] == ih)
break;
if( i >= kMaxInstances) // client not registered?
{
for( i=0; i<kMaxInstances; ++i)
{
if( deviceDescriptionPtr->instancePtr[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; i<nDVDevices; ++i)
{
QTAtom deviceAtomNew, nameAtomNew, deviceIDAtom;
QTAtom deviceAtomOld, nameAtomOld;
DeviceDescription *deviceDescriptionPtr;
IDHDeviceID deviceID;
UInt8 newName[256];
SInt32 actualSize;
// get the client supplied atoms
deviceAtomNew = QTFindChildByIndex( *deviceList, kParentAtomIsContainer, kIDHDeviceAtomType, i + 1, nil);
FailIf( deviceAtomNew == nil, Exit);
nameAtomNew = QTFindChildByIndex( *deviceList, deviceAtomNew, kIDHNameAtomType, 1, nil);
FailIf( nameAtomNew == nil, Exit);
deviceIDAtom = QTFindChildByIndex( *deviceList, deviceAtomNew, kIDHDeviceIDType, 1, nil);
FailIf( deviceIDAtom == nil, Exit);
QTLockContainer( *deviceList);
QTCopyAtomDataToPtr( *deviceList, deviceIDAtom, true, sizeof( IDHDeviceID), &deviceID, nil);
QTUnlockContainer( *deviceList);
// find the local copy of this device container
result = findDeviceDescriptionforDevice( ih, deviceID, &deviceDescriptionPtr);
FailWithVal( result != noErr, Exit, result);
deviceAtomOld = QTFindChildByIndex( deviceDescriptionPtr->deviceContainer, 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; j<kBuffersPerDevice; ++j) // add buffers to this devices queues
{
qep = (QueueElement *) NewPtrSysClear( sizeof( QueueElement));
FailWithAction( qep == nil, result = MemError(), Exit);
qep->buffer = 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; i<kMaxDevicesActive; ++i)
{
if( gGlobals->deviceDescription[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; i<kMaxDevicesActive; ++i)
{
if( gGlobals->deviceDescription[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; i<kMaxInstances; ++i)
{
IsochComponentInstancePtr ih = deviceDescriptionPtr->instancePtr[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; i<kMaxInstances; ++i)
{
IsochComponentInstancePtr ih = deviceDescriptionPtr->instancePtr[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; i<kMaxDevicesActive; ++i)
{
if( gGlobals->deviceDescription[i].deviceID == event->eventHeader.deviceID)
{
deviceDescriptionPtr = &gGlobals->deviceDescription[i];
break;
}
}
FailIf( deviceDescriptionPtr == nil, Exit); // couldn't find device in list
for( i=0; i<kMaxInstances; ++i) // process each client for this device
{
IsochComponentInstancePtr ih = deviceDescriptionPtr->instancePtr[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;i<n;i++)
{
// Yet another attempt ...
// Mute all the audio samples
for (i=0;i<n;i++)
{
for (j=0;j<9;j++)
{
tPtr = (UInt8 *)pImageBuffer + (i * 12000) + ((j * 16 + 6) * 80) + 8;
for (k=0;k<72;k++)
*tPtr++ = 0x0;
}
}
}
}
}
}
}
// Find next send packet start command.
pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
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;
}