1/*
2 * Copyright (c) 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23#include <mach/message.h>
24#include <mach/mach_error.h>
25#include <CoreFoundation/CoreFoundation.h>
26#include <IOKit/IOKitLib.h>
27#include <string.h>
28#include <ctype.h>
29#include <stdlib.h>
30#include <stdio.h>
31#include <unistd.h>
32#include <syslog.h>	// Debug messages
33#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
34
35#include <IOKit/IOMessage.h>
36
37#include <IOKit/firewire/IOFireWireLib.h>
38#include <IOKit/avc/IOFireWireAVCConsts.h>
39#include "DVLib.h"
40
41#include "DVFamily.h"
42
43#define kNTSCCompressedBufferSize	120000
44#define	kPALCompressedBufferSize	144000
45
46#define kMaxNotifications 64
47
48typedef struct device_info_struct {
49    DVDevice *fDevice;
50    UInt32 fNumOutputFrames;
51    UInt32 fOpens;
52    UInt32 frameSize;
53    UInt8* bufMem[kDVMaxFrames];
54    DVFrameVars *fReadSharedVars;	// Structure shared with isoc program thread
55    DVFrameVars *fWriteSharedVars;	// Structure shared with isoc program thread
56    DVGlobalOutPtr fWrite;
57    DVGlobalInPtr fRead;
58    UInt8		fOutputMode;		// AVC output signal mode - NTSC/SDL etc.
59} device_info;
60
61// notification stuff
62typedef struct DVNotificationEntryStruct {
63	UInt32		wantedEvents;
64	DVNotifyProc	notifyProc;
65	void		*userRefCon;
66        DVDeviceID	device;
67} DVNotificationEntry, *DVNotificationEntryPtr;
68
69
70static DVThread *sThread;
71static UInt32 fNumDevices, fNumAlive;
72static device_info devices[kDVMaxDevicesActive];
73static DVNotificationEntry sNotifications[kMaxNotifications];
74static int inited = 0;
75
76static void postEvent( DVEventHeaderPtr event )
77{
78    DVNotificationEntryPtr note;
79    int i;
80    for(i=0; i<kMaxNotifications; i++) {
81        note = &sNotifications[i];
82        if(note->notifyProc != NULL && (note->wantedEvents & event->theEvent) &&
83            (note->device == kEveryDVDeviceID || note->device == event->deviceID)) {
84            event->notifID = (DVNotificationID)(i+1);
85            note->notifyProc((DVEventRecordPtr)event, note->userRefCon);
86        }
87    }
88}
89
90static void deviceMessage(void * refcon, UInt32 messageType, void *messageArgument)
91{
92    DVDeviceRefNum refNum = (DVDeviceRefNum)refcon;
93
94	device_info *dev = &devices[refNum];
95    //syslog(LOG_INFO,"Got message: refcon %d, type 0x%x arg %p\n",
96    //    refcon, messageType, messageArgument);
97
98    switch(messageType) {
99        case kIOMessageServiceIsTerminated:
100            //syslog(LOG_INFO, "Terminating device %d\n", refNum);
101            break;
102
103        case kIOFWMessageServiceIsRequestingClose:
104            if(dev->fOpens > 0) {
105                //syslog(LOG_INFO, "Force closing device %d\n", refNum);
106                if(dev->fWrite)
107                    DVDisableWrite(refNum);
108                if(dev->fRead)
109                    DVDisableRead(refNum);
110                dev->fOpens = 1;
111                DVCloseDriver(refNum);
112            }
113            break;
114
115        case kIOMessageServiceWasClosed:
116            //syslog(LOG_INFO, "device %d closing\n", refNum);
117            break;
118        default:
119            break;
120    }
121
122}
123
124static void deviceArrived(void *refcon, DVDevice *device, UInt32 index, UInt32 refound)
125{
126    //syslog(LOG_INFO,"deviceArrived(0x%x, 0x%x)\n", refcon, index);
127    fNumAlive++;
128    devices[index].fDevice = device;
129    if(!refound)
130        fNumDevices++;
131    //syslog(LOG_INFO, "Found a device, GUID: 0x%x%08x name: %s\n",
132    //        (UInt32)(GUID>>32), (UInt32)(GUID & 0xffffffff), devices[device].fName);
133
134    {
135        // post a DV event to let the curious know...
136        DVConnectionEvent theEvent;
137        theEvent.eventHeader.deviceID	= (DVDeviceID) index;
138        theEvent.eventHeader.theEvent 	= kDVDeviceAdded;
139        postEvent( &theEvent.eventHeader );
140    }
141}
142
143static SInt32 init(void)
144{
145    fNumDevices = 0;
146    fNumAlive = 0;
147
148   // sThread = DVCreateThread(IOServiceMatching( kDVKernelDriverName ), deviceArrived, (void *)12345, deviceRemoved, (void *)12346);
149    sThread = DVCreateThread(deviceArrived, (void *)12345, nil, nil, deviceMessage);
150    DVRunThread(sThread);
151
152    //syslog(LOG_INFO, "workloop is: %p\n", workLoop);
153
154    inited = 1;
155    //syslog(LOG_INFO, "Initted\n");
156
157    return noErr;
158}
159
160///////////////////////////////////////////////////////////////////////
161// Notifications
162//
163///////////////////////////////////////////////////////////////////////
164OSErr DVNewNotification( DVDeviceRefNum refNum, DVNotifyProc notifyProc,
165                                    void *userData, DVNotificationID *pNotifyID )
166{
167    int i;
168    for(i=0; i<kMaxNotifications; i++) {
169        if(sNotifications[i].notifyProc == NULL) {
170            sNotifications[i].wantedEvents = 0;
171            sNotifications[i].notifyProc = notifyProc;
172            sNotifications[i].userRefCon = userData;
173            sNotifications[i].device = (DVDeviceID)refNum;
174            *pNotifyID = (DVNotificationID)(i+1);
175            return noErr;
176        }
177    }
178    return kDVNoNotificationsErr;
179}
180
181OSErr DVNotifyMeWhen( DVDeviceRefNum refNum, DVNotificationID notifyID, UInt32 events)
182{
183    int id = ((int)notifyID)-1;
184    if(sNotifications[id].notifyProc != NULL) {
185        sNotifications[id].wantedEvents = events;
186        return noErr;
187    }
188    else
189        return paramErr;
190}
191
192OSErr DVCancelNotification( DVDeviceRefNum refNum, DVNotificationID notifyID )
193{
194    int id = ((int)notifyID)-1;
195    if(sNotifications[id].notifyProc != NULL) {
196        sNotifications[id].wantedEvents = 0;
197        return noErr;
198    }
199    else
200        return paramErr;
201}
202
203OSErr DVDisposeNotification( DVDeviceRefNum refNum, DVNotificationID notifyID )
204{
205    int id = ((int)notifyID)-1;
206    if(sNotifications[id].notifyProc != NULL) {
207        sNotifications[id].wantedEvents = 0;
208        sNotifications[id].notifyProc = NULL;
209        sNotifications[id].userRefCon = NULL;
210        return noErr;
211    }
212    else
213        return paramErr;
214}
215
216
217///////////////////////////////////////////////////////////////////////
218// AVC
219///////////////////////////////////////////////////////////////////////
220
221OSErr DVDoAVCTransaction( DVDeviceRefNum refNum, AVCTransactionParamsPtr pParams )
222{
223    IOReturn err;
224    device_info *dev = &devices[refNum];
225    //syslog(LOG_INFO, "DVDoAVCTransaction, open %d, interface %p\n", dev->fOpens, dev->fDevice.fAVCInterface);
226
227    if(!dev->fDevice->fSupportsFCP) {
228        return( -4162 ); // timeoutErr
229    }
230
231	err = (*dev->fDevice->fAVCInterface)->AVCCommand(dev->fDevice->fAVCInterface,
232                                    pParams->commandBufferPtr, pParams->commandLength,
233                                    pParams->responseBufferPtr, &pParams->responseBufferSize);
234    //syslog(LOG_INFO, "DVDoAVCTransaction returns %d: %x %x %x %x\n",
235    //    pParams->responseBufferSize, pParams->responseBufferPtr[0], pParams->responseBufferPtr[1],
236    //                                    pParams->responseBufferPtr[2], pParams->responseBufferPtr[3]);
237
238    //if(err)
239    //    syslog(LOG_INFO, "DVDoAVCTransaction(), err 0x%x\n", err);
240    return err;
241}
242
243///////////////////////////////////////////////////////////////////////
244// device management
245///////////////////////////////////////////////////////////////////////
246
247UInt32 DVCountDevices( void )
248{
249    if(!inited)
250        init();
251    // Return total number of devices, not just the number currently connected.
252    //syslog(LOG_INFO, "DVCountDevices() = %d\n", fNumDevices);
253    return fNumDevices;
254}
255
256OSErr DVGetIndDevice( DVDeviceID * pDVDevice, UInt32 index )
257{
258    *pDVDevice = index;
259    return noErr;
260}
261
262OSErr DVSetDeviceName( DVDeviceID deviceID, char * str )
263{
264//printf("DVSetDeviceName(0x%x, %s)\n", deviceID, str);
265    return noErr; // FIXME
266}
267
268OSErr DVGetDeviceName( DVDeviceID deviceID, char * str )
269{
270    strcpy(str, devices[deviceID].fDevice->fName);
271    return noErr;
272}
273
274OSErr DVUnregisterClientApp( DVClientID dvClientID )
275{
276//printf("DVUnregisterClientApp(0x%x)\n", dvClientID);
277    return noErr; // FIXME
278}
279
280OSErr DVRegisterClientApp( DVClientID *pDVClientID, UInt32 clientContextData )
281{
282//printf("DVRegisterClientApp(0x%x, 0x%x)\n", pDVClientID, clientContextData);
283    *pDVClientID = (DVClientID)1;
284    return noErr; // FIXME
285}
286
287OSStatus DVGetNextClientEvent( DVClientID dvClientID )
288{
289//printf("DVGetNextClientEvent(%d)\n", dvClientID);
290    return noErr; // FIXME
291}
292
293OSErr DVOpenDriver( DVDeviceID deviceID, DVDeviceRefNum *pRefNum )
294{
295    IOReturn err = kIOReturnSuccess;
296    device_info *dev = &devices[deviceID];
297//syslog(LOG_INFO, "DVOpenDriver(0x%x, 0x%x)\n", deviceID, pRefNum);
298    *pRefNum = deviceID;
299
300    if(dev->fOpens > 0) {
301        dev->fOpens++;
302        return noErr;
303        //return kAlreadyEnabledErr;
304    }
305
306    do {
307        if(dev->fDevice->fObject == 0) {
308            err = kDVDisconnectedErr;
309            break;
310        }
311        //err = DVDeviceOpen(sThread, dev->fDevice);
312        //if(err != kIOReturnSuccess) break;
313
314        dev->fNumOutputFrames = 5;
315        dev->fOpens++;
316    } while (0);
317
318    if(err != kIOReturnSuccess) {
319        syslog(LOG_INFO, "error opening DV device: %x", err);
320        dev->fOpens = 1;
321        DVCloseDriver(deviceID);
322    }
323    return err; // FIXME
324}
325
326OSErr DVCloseDriver( DVDeviceRefNum refNum )
327{
328    device_info *dev = &devices[refNum];
329//syslog(LOG_INFO, "DVCloseDriver(0x%x), opens = %d\n", refNum, devices[refNum].fOpens);
330    if(dev->fOpens > 0) {
331        dev->fOpens--;
332        if(dev->fOpens == 0) {
333        }
334    }
335    return noErr;
336}
337
338OSErr DVGetDeviceStandard(DVDeviceRefNum refNum, UInt32 * pStandard )
339{
340    AVCCTSFrameStruct		avcFrame;
341    AVCTransactionParams	transactionParams;
342    UInt8					responseBuffer[ 16 ];
343    OSStatus				theErr = noErr;
344    UInt32					currentSignal, AVCStatus;
345    device_info *			dev = &devices[refNum];
346
347//syslog(LOG_INFO, "DVGetDeviceStandard(0x%x)\n", refNum);
348    if(!devices[refNum].fDevice->fSupportsFCP) {
349            *pStandard = kNTSCStandard;
350            devices[refNum].frameSize = kNTSCCompressedBufferSize;
351            devices[refNum].fOutputMode = kAVCSignalModeSD525_60;
352            return( theErr );
353    }
354
355    // fill up the avc frame
356    avcFrame.cmdType_respCode  = kAVCStatusInquiryCommand;
357    avcFrame.headerAddress     = 0x20;                        // for now
358    avcFrame.opcode            = kAVCOutputSignalModeOpcode;
359    avcFrame.operand[ 0 ]      = kAVCSignalModeDummyOperand;
360
361    // fill up the transaction parameter block
362    transactionParams.commandBufferPtr      = (Ptr) &avcFrame;
363    transactionParams.commandLength         = 4;
364    transactionParams.responseBufferPtr     = (Ptr) responseBuffer;
365    transactionParams.responseBufferSize    = 4;
366    transactionParams.responseHandler       = nil;
367
368	theErr = (*dev->fDevice->fAVCInterface)->AVCCommand(dev->fDevice->fAVCInterface,
369                                    transactionParams.commandBufferPtr, transactionParams.commandLength,
370                                    transactionParams.responseBufferPtr, &transactionParams.responseBufferSize);
371    if(theErr) {
372        //syslog(LOG_INFO, "DVGetDeviceStandard(), err 0x%x\n", theErr);
373        if(theErr == kIOReturnTimeout) {
374            *pStandard = kNTSCStandard;
375            return noErr;
376        }
377        return theErr;
378    }
379    currentSignal = ((responseBuffer[ 2 ] << 8) | responseBuffer[ 3 ]);
380    AVCStatus = responseBuffer[ 0 ];
381
382    *pStandard = kUnknownStandard;
383    switch (currentSignal & 0x000000ff)
384    {
385        case kAVCSignalModeSD525_60:
386        case kAVCSignalModeSDL525_60:
387        case kAVCSignalModeHD1125_60:
388            devices[refNum].frameSize = kNTSCCompressedBufferSize;
389            devices[refNum].fOutputMode = kAVCSignalModeSD525_60;
390
391            *pStandard = kNTSCStandard;
392            return( theErr );
393
394        case kAVCSignalModeSD625_50:
395        case kAVCSignalModeSDL625_50:
396        case kAVCSignalModeHD1250_50:
397            devices[refNum].frameSize = kPALCompressedBufferSize;
398            devices[refNum].fOutputMode = kAVCSignalModeSD625_50;
399
400            *pStandard = kPALStandard;
401            return( theErr );
402
403        default:
404            syslog(LOG_INFO, "DVGetDeviceStandard(), err 0x%x\n", kUnknownStandardErr);
405            return( kUnknownStandardErr ); // how should I handle this?
406    }
407}
408
409///////////////////////////////////////////////////////////////////////
410// readin'
411///////////////////////////////////////////////////////////////////////
412
413OSErr DVIsEnabled( DVDeviceRefNum refNum, Boolean *isEnabled)
414{
415    //syslog(LOG_INFO, "DVIsEnabled, returning %d\n", devices[refNum].fRead);
416    *isEnabled = devices[refNum].fRead != 0;
417    return noErr; // FIXME
418}
419
420OSErr DVEnableRead( DVDeviceRefNum refNum )
421{
422    OSErr err;
423    device_info *dev = &devices[refNum];
424    //syslog(LOG_INFO, "DVEnableRead entry\n");
425    if(dev->fRead) {
426        //syslog(LOG_INFO, "DVEnableRead, already enabled!\n");
427        return noErr;
428    }
429    if(dev->fWrite) {
430        //syslog(LOG_INFO, "DVEnableRead, already writing!\n");
431        return kAlreadyEnabledErr;
432    }
433    do {
434        err = DVDeviceOpen(sThread, dev->fDevice);
435        if(err != kIOReturnSuccess) break;
436        dev->fRead = DVAllocRead(dev->fDevice, sThread);
437        err = DVReadAllocFrames(dev->fRead, dev->fNumOutputFrames,
438            &dev->fReadSharedVars, dev->bufMem);
439        if(err != kIOReturnSuccess) break;
440        err = DVReadStart(dev->fRead);
441    } while (0);
442    if(err) {
443        syslog(LOG_INFO, "DVEnableRead(), err 0x%x\n", err);
444        DVDisableRead(refNum);
445    }
446    return err;
447}
448
449OSErr DVDisableRead( DVDeviceRefNum refNum )
450{
451    device_info *dev = &devices[refNum];
452    if(dev->fRead) {
453        //syslog(LOG_INFO, "DVDisableRead\n");
454        DVReadStop(dev->fRead);
455        DVReadFreeFrames(dev->fRead);
456        DVReadFree(dev->fRead);
457        dev->fRead = NULL;
458        DVDeviceClose(dev->fDevice);
459    }
460    return noErr;
461}
462
463OSErr DVReadFrame( DVDeviceRefNum refNum, Ptr *ppReadBuffer, UInt32 * pSize )
464{
465    device_info *dev = &devices[refNum];
466    int index=0, i;
467
468    // wait for writer
469     for(i=dev->fReadSharedVars->fReader; i<dev->fReadSharedVars->fWriter; i++) {
470        index = i % dev->fNumOutputFrames;
471        if(dev->fReadSharedVars->fFrameStatus[index] == kReady)
472            break;
473    }
474    if (i >= dev->fReadSharedVars->fWriter)
475        return -1;
476    // copy frame
477    *ppReadBuffer = dev->bufMem[index];
478    *pSize = dev->fReadSharedVars->fFrameSize[index];
479    dev->fReadSharedVars->fFrameStatus[index] = kReading;
480    //syslog(LOG_INFO,"DVReadFrame returning frame %d @ %x\n", index, *ppReadBuffer);
481    dev->fReadSharedVars->fReader = i;
482    return noErr; // FIXME
483}
484
485OSErr DVReleaseFrame( DVDeviceRefNum refNum, Ptr pReadBuffer )
486{
487    int i;
488    device_info *dev = &devices[refNum];
489    for(i=0; i<dev->fNumOutputFrames; i++) {
490        if(pReadBuffer == dev->bufMem[i])
491            break;
492    }
493    //syslog(LOG_INFO, "released frame %d=%p, fReader now %d\n",
494    //        i, pReadBuffer, dev->fReadSharedVars->fReader);
495    dev->fReadSharedVars->fFrameStatus[i] = kEmpty;
496    return noErr; // FIXME
497}
498
499///////////////////////////////////////////////////////////////////////
500// writin'
501///////////////////////////////////////////////////////////////////////
502
503OSErr DVEnableWrite( DVDeviceRefNum refNum )
504{
505    IOReturn err;
506    device_info *dev = &devices[refNum];
507        //syslog(LOG_INFO, "DVEnableWrite entry\n");
508
509    if(dev->fWrite) {
510        //syslog(LOG_INFO, "DVEnableWrite, already enabled!\n");
511        return noErr;
512    }
513    if(dev->fRead) {
514        //syslog(LOG_INFO, "DVEnableWrite, already reading!\n");
515        return kAlreadyEnabledErr;
516    }
517
518    do {
519        err = DVDeviceOpen(sThread, dev->fDevice);
520        if(err != kIOReturnSuccess) break;
521        dev->fWrite = DVAllocWrite(dev->fDevice, sThread);
522        err = DVWriteSetSignalMode(dev->fWrite, dev->fOutputMode);
523        if(err)
524            break;
525        err = DVWriteAllocFrames(dev->fWrite, dev->fNumOutputFrames,
526            &dev->fWriteSharedVars, dev->bufMem);
527        if(err)
528            break;
529
530        err = DVWriteStart(dev->fWrite);
531    } while (0);
532
533    if(err) {
534        syslog(LOG_INFO, "DVEnableWrite(), err 0x%x\n", err);
535        DVDisableWrite(refNum);
536    }
537    return err;
538}
539
540OSErr DVDisableWrite( DVDeviceRefNum refNum )
541{
542    //OSErr err;
543    device_info *dev = &devices[refNum];
544    //syslog(LOG_INFO, "DVDisableWrite\n");
545    if(dev->fWrite) {
546        DVWriteStop(dev->fWrite);
547        DVWriteFreeFrames(dev->fWrite);
548        DVWriteFree(dev->fWrite);
549        dev->fWrite = NULL;
550        DVDeviceClose(dev->fDevice);
551    }
552
553    return noErr;
554}
555
556OSErr DVGetEmptyFrame( DVDeviceRefNum refNum, Ptr *ppEmptyFrameBuffer, UInt32 * pSize )
557{
558    // check for error
559    if(!devices[refNum].fWrite) {
560        syslog(LOG_INFO, "DVGetEmptyFrame, not writing!!\n");
561        return kNotEnabledErr;
562    }
563
564    if (devices[refNum].fWriteSharedVars->fStatus == 2)
565    {
566        syslog(LOG_INFO, "DVGetEmptyFrame, status %d\n", devices[refNum].fWriteSharedVars->fStatus);
567        return -2;
568    }
569
570    // wait for reader
571    // if (*devices[refNum].fWriter + 2 >= *devices[refNum].fReader + devices[refNum].fNumOutputFrames) return -1;
572    if (devices[refNum].fWriteSharedVars->fWriter + 1 >=
573        devices[refNum].fWriteSharedVars->fReader + devices[refNum].fNumOutputFrames)
574    {
575        //syslog(LOG_INFO, "DVGetEmptyFrame, no frame available: %d-%d\n",
576        //    devices[refNum].fWriteSharedVars->fWriter, devices[refNum].fWriteSharedVars->fReader);
577        return -1;
578    }
579    // copy frame
580    *ppEmptyFrameBuffer = devices[refNum].bufMem[devices[refNum].fWriteSharedVars->fWriter % devices[refNum].fNumOutputFrames];
581    *pSize = devices[refNum].frameSize;
582
583    return noErr; // FIXME
584}
585
586OSErr DVWriteFrame( DVDeviceRefNum refNum, Ptr pWriteBuffer )
587{
588    device_info *dev = &devices[refNum];
589    if(!dev->fWrite) {
590        syslog(LOG_INFO, "DVWriteFrame, not writing!!\n");
591        return kNotEnabledErr;
592    }
593    dev->fWriteSharedVars->fWriter += 1;
594
595    return noErr; // FIXME
596}
597
598OSErr DVSetWriteSignalMode( DVDeviceRefNum refNum, UInt8 mode)
599{
600    devices[refNum].fOutputMode = mode;
601
602    return noErr;
603}
604
605
606UInt32 getNumDroppedFrames(DVDeviceRefNum refNum)
607{
608    return devices[refNum].fWriteSharedVars->fDroppedFrames;
609}
610