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