/*
 * Copyright (C) 2002 Andreas Thiede ( a.thiede@berlin.de )
 * 
 * based on:
 *
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 *
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright (c) 2000 Apple Computer, Inc.  All rights reserved.
 *
 *  DRI: Josh de Cesare
 *
 */

#include <IOKit/IOLib.h>
#include <IOKit/IOCommandGate.h>
#include <vm/vm_kern.h>

#include "Bt8xx.h"
#include "Bt8xxRegs.h"
#include "Bt8xxImpl.h"
#include "Bt8xxUserClient.h"

#include <IOKit/IOLib.h>

#define IOErr IOLog

//---------------------------------------------------------------------------//

#define super IOService

OSDefineMetaClassAndStructors( Bt8xxUserClient, IOUserClient );

Bt8xxUserClient *Bt8xxUserClient::withTask(task_t owningTask)
{
  IOLog("Bt8xxUserClient::withTask\n");

  Bt8xxUserClient *userClient;
  
  userClient = new Bt8xxUserClient;;
  if(userClient && !userClient->init()) {
    userClient->release();
    userClient = 0;
  }

  userClient->task = owningTask;
  
  IOLog("Bt8xxUserClient::withTask done\n");
  
  return userClient;
}

#define SetExternalMethod(index, obj, methodName, type, countIn, countOut) \
externals[(index)].object = (obj); \
externals[(index)].func   = (IOMethod)&Bt8xxUserClient::##methodName; \
externals[(index)].flags  = (type); \
externals[(index)].count0 = (countIn); \
externals[(index)].count1 = (countOut);

bool Bt8xxUserClient::start(IOService *provider)
{
  IOLog("Bt8xxUserClient::start\n");
  
  if( provider == 0 ) {

    IOLog("Bt8xxUserClient::start provider == 0 !!!!!!! \n");
    return false;
  }
  
  // Setup the external methods.

  SetExternalMethod(kBt8xGetDeviceID,   this, _extGetDeviceID,      kIOUCScalarIScalarO, 0, 1 );
  
  SetExternalMethod(kBt8xxSetup,        this, _extSetup,            kIOUCScalarIScalarO, 2, 0);
 
  SetExternalMethod(kBt8xxStart,        this, _extStart,            kIOUCScalarIScalarO, 0, 0 );

  SetExternalMethod(kBt8xxStop,         this, _extStop,             kIOUCScalarIScalarO, 0, 0 );
  
  SetExternalMethod(kBt8xxSetAudioMask, this, _extSetAudioMask,     kIOUCScalarIScalarO, 1, 0 );
    
  SetExternalMethod(kBt8xxReadReg8,     this, _extReadReg8,         kIOUCScalarIScalarO, 1, 1 );
  
  SetExternalMethod(kBt8xxWriteReg8,    this, _extWriteReg8,        kIOUCScalarIScalarO, 2, 0 );
  
  SetExternalMethod(kBt8xxReadReg16,    this, _extReadReg16,        kIOUCScalarIScalarO, 1, 1 );
  
  SetExternalMethod(kBt8xxWriteReg16,   this, _extWriteReg16,       kIOUCScalarIScalarO, 2, 0 );

  SetExternalMethod(kBt8xxReadReg32,    this, _extReadReg32,        kIOUCScalarIScalarO, 1, 1 );
  
  SetExternalMethod(kBt8xxWriteReg32,   this, _extWriteReg32,       kIOUCScalarIScalarO, 2, 0 );

  SetExternalMethod(kBt8xxGetVideoBase, this, _extGetVideoPhysAddr, kIOUCScalarIScalarO, 0, 1 );

  SetExternalMethod(kBt8xxGetAudioBase, this, _extGetAudioPhysAddr, kIOUCScalarIScalarO, 0, 1 );

  SetExternalMethod(kBt8xxWait4VBI,     this, _extWait4VBI,         kIOUCScalarIScalarO, 0, 0 );

  SetExternalMethod(kBt8xxWait4Video,   this, _extWait4Video,       kIOUCScalarIScalarO, 0, 0 );

  SetExternalMethod(kBt8xxReadAudio,   this, _extReadAudio,         kIOUCScalarIScalarO, 2, 0 );

  SetExternalMethod(kBt8xxAllocPages,  this, _extAllocPages,        kIOUCScalarIStructO, 
                                                                    1,
                                                                    sizeof(MemoryDescriptor) );

  SetExternalMethod(kBt8xxFreePages,   this, _extFreePages,         kIOUCStructIStructO, 
                                                                    sizeof(MemoryDescriptor),
                                                                    0 );

  SetExternalMethod(kBt8xxIrqWait,     this, _extIrqWait,           kIOUCScalarIScalarO, 0, 0 );

  owner = (Bt8xx *)provider;

  if (!super::start(provider)) {
    IOErr("Bt8xxUserClient::start failed\n");
    return false;
  }
  
  IOLog("Bt8xxUserClient::start done\n");

  return true;
}

IOReturn Bt8xxUserClient::clientClose(void)
{
  IOLog("Bt8xxUserClient::clientClose\n");

  owner->close();
  
  detach(owner);

  IOLog("Bt8xxUserClient::clientClose done\n");
  
  return kIOReturnSuccess;
}

IOService *Bt8xxUserClient::getService(void)
{
  return owner;
}

IOReturn Bt8xxUserClient::clientMemoryForType( UInt32 type, IOOptionBits * options, IOMemoryDescriptor ** memory )
{
    IOLog("Bt8xxUserClient::clientMemoryForType type(%lx)\n", type );

    if( memory != 0 ) {
    
        * memory = 0;
   
        // _nub points to driver instance, initialized in start()
    
        switch( type ) {

        case kBt8xxVideoBuffer:
    
            * memory = IOMemoryDescriptor::withAddress( (void*)owner->instance()->mVideoVirtAddr, 
                                                        (IOByteCount)owner->instance()->mVideoBufSize,
                                                        kIODirectionOut );
            break;
        case kBt8xxVBIBuffer:
    
            * memory = IOMemoryDescriptor::withAddress( (void*)owner->instance()->mVBIVirtAddr, 
                                                        (IOByteCount)owner->instance()->mVBIBufSize,
                                                        kIODirectionOut );
            break;
        case kBt8xxAudioBuffer:

            * memory = IOMemoryDescriptor::withAddress( (void*)owner->instance()->mAudioVirtAddr, 
                                                        (IOByteCount)owner->instance()->mAudioBufSize,
                                                            kIODirectionOut );
            
            break;
#if 0        
        case kBt8xxRiscAudioCode:

            * memory = IOMemoryDescriptor::withAddress( (void*)owner->instance()->mAudioCmdPage, 
                                                        PAGE_SIZE,kIODirectionOut );
        
            break;
            
        case kBt8xxRiscVideoCode:

            * memory = IOMemoryDescriptor::withAddress( (void*)owner->instance()->mAudioCmdPage, 
                                                        PAGE_SIZE,kIODirectionOut );
        
            break;
#endif        
        }

        return( *memory != 0 ) ? kIOReturnSuccess : kIOReturnError;

    }
    return kIOReturnError;
}

IOExternalMethod *Bt8xxUserClient::getTargetAndMethodForIndex(IOService **targetP, UInt32 index)
{
  IOExternalMethod *method = NULL;
  
  if (index < kBt8xxCount) {
    method = externals + index;
    *targetP = (IOService *)method->object;
  }

  return method;
}

IOReturn Bt8xxUserClient::_extNone(void)
{
  //IOLog("Bt8xxUserClient::_extNone not implied\n");

  return kIOReturnSuccess;
}

IOReturn Bt8xxUserClient::_extGetDeviceID( UInt32 * id )
{
  //IOLog("Bt8xxUserClient::_extGetDeviceID\n");

  //GateGuard guard( owner->getWorkLoop() );
  
  IOReturn ret = KERN_SUCCESS;
  
  *id = owner->instance()->mDeviceID;

  //IOLog("Bt8xxUserClient::_extSetup done\n");

  return ret;
    
}

IOReturn Bt8xxUserClient::_extSetup( UInt32 capFmt, UInt32 colFmt )
{
  //IOLog("Bt8xxUserClient::_extSetup\n");

  //GateGuard guard( owner->getWorkLoop() );
  
  IOReturn ret = owner->instance()->setup( (eCaptureFormat) capFmt, colFmt );

  //IOLog("Bt8xxUserClient::_extSetup done\n");

  return ret;
}


IOReturn Bt8xxUserClient::_extStart(void)
{
  //IOLog("Bt8xxUserClient::_extStart\n");

  //GateGuard guard( owner->getWorkLoop() );

  IOReturn ret = owner->instance()->clientStart();

  //IOLog("Bt8xxUserClient::_extStart\n");

  return ret;
}

IOReturn Bt8xxUserClient::_extStop(void)
{
  //IOLog("Bt8xxUserClient::_extStop\n");

  //GateGuard guard( owner->getWorkLoop() );

  IOReturn ret = owner->instance()->clientStop();

  //IOLog("Bt8xxUserClient::_extStop done\n");

  return ret;
}

IOReturn Bt8xxUserClient::_extGetVideoPhysAddr(UInt32 *source)
{
  //IOLog("Bt8xxUserClient::_extGetVideoPhysAddr\n");

  //GateGuard guard( owner->getWorkLoop() );

  *source = owner->instance()->mVideoCmdPagePhys;

  //IOLog("Bt8xxUserClient::_extGetVideoPhysAddr done\n");
  
  return kIOReturnSuccess;
}

IOReturn Bt8xxUserClient::_extGetAudioPhysAddr(UInt32 *source)
{
  //IOLog("Bt8xxUserClient::_extGetAudioPhysAddr\n");

  //GateGuard guard( owner->getWorkLoop() );

  *source = owner->instance()->mAudioCmdPagePhys;

  //IOLog("Bt8xxUserClient::_extGetAudioPhysAddr done\n");
  
  return kIOReturnSuccess;
}

IOReturn Bt8xxUserClient::_extSetAudioMask(SInt32 mask )
{
  //IOLog("Bt8xxUserClient::_extSetSource\n");

  //GateGuard guard( owner->getWorkLoop() );

  owner->instance()->mAudioMask = mask;

  //IOLog("Bt8xxUserClient::_extSetSource done\n");

  return kIOReturnSuccess;
}

IOReturn Bt8xxUserClient::_extReadReg8(UInt32 reg, UInt32 *data)
{
  //IOLog("Bt8xxUserClient::_extReadReg8\n");

  //GateGuard guard( owner->getWorkLoop() );

  *data = owner->instance()->readVideoReg8(reg);

  //IOLog("Bt8xxUserClient::_extReadReg8 done\n");
  
  return kIOReturnSuccess;
}

IOReturn Bt8xxUserClient::_extWriteReg8(UInt32 reg, UInt32 data)
{
  //IOLog("Bt8xxUserClient::_extWriteReg8\n");

  //GateGuard guard( owner->getWorkLoop() );

  owner->instance()->writeVideoReg8(reg, data);

  //IOLog("Bt8xxUserClient::_extWriteReg8\n");
  
  return kIOReturnSuccess;
}

IOReturn Bt8xxUserClient::_extReadReg16(UInt32 reg, UInt32 *data)
{
  //IOLog("Bt8xxUserClient::_extReadReg16\n");

  //GateGuard guard( owner->getWorkLoop() );

  *data = owner->instance()->readVideoReg16(reg);

  //IOLog("Bt8xxUserClient::_extReadReg16 done\n");
  
  return kIOReturnSuccess;
}

IOReturn Bt8xxUserClient::_extWriteReg16(UInt32 reg, UInt32 data)
{
  //IOLog("Bt8xxUserClient::_extWriteReg16\n");

  owner->instance()->writeVideoReg16(reg, data);

  //IOLog("Bt8xxUserClient::_extWriteReg16\n");
  
  return kIOReturnSuccess;
}

IOReturn Bt8xxUserClient::_extReadReg32(UInt32 reg, UInt32 *data)
{
  //IOLog("Bt8xxUserClient::_extReadReg32\n");

  //GateGuard guard( owner->getWorkLoop() );

  *data = owner->instance()->readVideoReg32(reg);

  //IOLog("Bt8xxUserClient::_extReadReg32 done\n");
  
  return kIOReturnSuccess;
}

IOReturn Bt8xxUserClient::_extWriteReg32(UInt32 reg, UInt32 data)
{
  //IOLog("Bt8xxUserClient::_extWriteReg32\n");

  //GateGuard guard( owner->getWorkLoop() );

  owner->instance()->writeVideoReg32( reg, data );

  //IOLog("Bt8xxUserClient::_extWriteReg32\n");
  
  return kIOReturnSuccess;
}

IOReturn Bt8xxUserClient::_extWait4Video()
{
  //IOLog("Bt8xxUserClient::_extWait4Video\n");

  ////GateGuard guard( owner->getWorkLoop() );

  IOReturn r = owner->instance()->wait4Video();

  //IOLog("Bt8xxUserClient::_extWaitVideo done\n");
  
  return r;
}

IOReturn Bt8xxUserClient::_extWait4VBI()
{
  //IOLog("Bt8xxUserClient::_extWait4VBI\n");

  ////GateGuard guard( owner->getWorkLoop() );

  IOReturn r = owner->instance()->wait4VBI();
  //IOLog("Bt8xxUserClient::_extWaitVBI done\n");
  
  return r;
}


IOReturn Bt8xxUserClient::_extReadAudio( UInt32 * buffer, UInt32 swcount )
{
  //IOLog("Bt8xxUserClient::_extWait4Audio\n");

  IOMemoryDescriptor * userAddr = IOMemoryDescriptor::withAddress( (vm_address_t) buffer, 
                                                                   (IOByteCount ) swcount, 
                                                                   kIODirectionOut, task );

  userAddr->prepare( kIODirectionOutIn );
  
  IOByteCount iobc = swcount;

  void * mappedBuffer = userAddr->getVirtualSegment( 0,  &iobc);
    
  IOReturn r = owner->instance()->readAudio( (char*) mappedBuffer, swcount );

  userAddr->complete( kIODirectionOutIn );
  
  //userAddr->unmap();
  
  userAddr->release();
  
  //IOLog("Bt8xxUserClient::_extWait4Audio done\n");
  
  return r;
}

IOReturn Bt8xxUserClient::_extAllocPages( UInt32 nPages, MemoryDescriptor * md )
{
    IOLog("Bt8xxUserClient::_extAllocPages %p %ld\n", md, nPages );

    IOReturn ret = kIOReturnError; //owner->instance()->allocPages( nPages, md, task );

    IOLog("Bt8xxUserClient::_extAllocPages done\n" );
    
    return ret;

}

IOReturn Bt8xxUserClient::_extFreePages( MemoryDescriptor * md )
{
    IOLog("Bt8xxUserClient::_extFreePages %p %ld\n", md, md->mLength );

    IOReturn ret = kIOReturnError; //owner->instance()->freePages( md );

    IOLog("Bt8xxUserClient::_extFreePages done\n" );

    return ret;
}

IOReturn Bt8xxUserClient::_extIrqWait()
{
    IOLog("Bt8xxUserClient::_extIrqWait\n" );

    IOReturn ret = KERN_SUCCESS; //owner->instance()->irqWait();

    IOLog("Bt8xxUserClient::_extIrqWait done\n" );

    return ret;
}
