//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//

/*++

THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:

    Camdriver.cpp   Camera Interface Driver

Abstract:

   Streams interface driver (MDD)

Functions:

Notes:

--*/

#include <precomp.h>

CEDEVICE_POWER_STATE    g_PowerState[COUNT_CAMERA_IP];

DBGPARAM dpCurSettings =                                \
{                                                       \
    TEXT(__MODULE__),                                   \
    {                                                   \
        TEXT("Errors"),                 /* 0  */        \
        TEXT("Warnings"),               /* 1  */        \
        TEXT("Performance"),            /* 2  */        \
        TEXT("Temporary tests"),        /* 3  */        \
        TEXT("Enter,Exit"),             /* 4  */        \
        TEXT("Initialize"),             /* 5  */        \
        TEXT("Open"),                   /* 6  */        \
        TEXT("Close"),                  /* 7  */        \
        TEXT("Io Control"),             /* 8  */        \
        TEXT("Camera Interface"),       /* 9  */        \
        TEXT("Post Interface")          /* 10 */        \
    },                                                  \
    (CAMDRV_ZONES)                               \
};

BOOL
WINAPI
DllEntry(
   HINSTANCE   hDllHandle, 
   DWORD    dwReason, 
   VOID   * lpReserved
   ) 
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        DEBUGREGISTER(hDllHandle);
        DBGMSG(CIF_FUNC && CIF_USR1, (_T("[CAM] DllEntry() : Process Attach\r\n")));
    }
    else if (dwReason == DLL_PROCESS_DETACH)
    {
        DBGMSG(CIF_FUNC && CIF_USR1, (_T("[CAM] DllEntry() : Process Detach\r\n")));
    }
    DBGMSG(CIF_FUNC && CIF_USR1, (_T("[CAM] DllEntry() : Exception:0x%x\r\n"), dwReason));    

    return TRUE;
}

BOOL
CAM_PowerUp(DWORD pContext)
{
    DBGMSG(CIF_FUNC && CIF_USR1, (_T("\r\n[CAM] CAM_PowerUp(0x%08x)\r\n"), pContext));

    return TRUE;
}

BOOL
CAM_PowerDown(DWORD pContext)
{
    DBGMSG(CIF_FUNC && CIF_USR1, (_T("\r\n[CAM] CAM_PowerDown(0x%08x)\r\n"), pContext));

    return TRUE;
}

BOOL CAM_Deinit(PHW_INDEP_INFO pContext)
{
    DBGMSG(CIF_FUNC && CIF_USR1, (_T("\r\n[CAM] ++CAM_Deinit(0x%08x)\r\n"), pContext));

    if(pContext != NULL)
        CusCam_Deinitialize(pContext->uCamID);

    LocalFree(pContext);    

    DBGMSG(CIF_FUNC && CIF_USR1, (_T("[CAM] --CAM_Deinit()\r\n")));

    return TRUE;
}

// ****************************************************************
//
//      @doc EXTERNAL
//      @func           HANDLE | CAM_INIT | Camera device initialization.
//
//      @parm           ULONG  | Identifier | Port identifier.  The device loader
//                              passes in the registry key that contains information
//                              about the active device.
//
//      @remark         This routine is called at device load time in order
//                              to perform any initialization.   Typically the init
//                              routine does as little as possible, postponing memory
//                              allocation and device power-on to Open time.
//
//       @rdesc         Returns a pointer to the camera head which is passed into
//                              the CAM_OPEN and CAM_DEINIT entry points as a device handle.
//
HANDLE
CAM_Init(
        ULONG   Identifier
        )
{
    PVOID           pHWHead     = NULL;
    PHW_INDEP_INFO  pCAMHead = NULL;
    DWORD           dwDevIndex;
    DWORD           dwMemBase;
    DWORD           dwIrq;
    HKEY            hKey;
    ULONG           kreserved = 0, kvaluetype;
    ULONG           datasize = sizeof(ULONG);

    /*
     *  INTERNAL: this routine initializes the hardware abstraction interface
     *  via HWInit(). It allocates a data structure representing this
     *  instantiation of the device. It also creates an event and initializes
     *  a critical section for receiving as well as registering the logical
     *  interrupt dwIntID with NK via InterruptInitialize. This call
     *  requires that the hardware dependent portion export apis that return
     *  the physical address of the receive buffer and the size of that buffer.
     *  Finally, it creates a buffer to act as an intermediate
     *  buffer when receiving.
     */
    DBGMSG(CIF_FUNC && CIF_USR1, (_T("\r\n[CAM] ++CAM_Init(0x%x)\r\n"), Identifier));

    // Prepare HW independent context

	RETAILMSG(1, (TEXT("CAM_Init\r\n")));

    pCAMHead  =  (PHW_INDEP_INFO)LocalAlloc(LPTR, sizeof(HW_INDEP_INFO));

    // Check that LocalAlloc did stuff ok too.
    if ( !pCAMHead ) {
        ERRMSG((TEXT("Error allocating memory for pSerialHead, CAM_Init failed\n\r")));
        return(NULL);
    }
    memset(pCAMHead,0,sizeof(HW_INDEP_INFO));

    // Initially, open list is empty.
    InitializeListHead( &pCAMHead->OpenList );
    InitializeCriticalSection(&(pCAMHead->OpenCS));

    /* Want to use the Identifier to do RegOpenKey and RegQueryValue (?)
     * to get the index to be passed to GetHWObj.
     * The HWObj will also have a flag denoting whether to start the
     * listening thread or provide the callback.
     */
    DBGMSG(CIF_FUNC && CIF_USR1,(TEXT("Try to open %s\r\n"), (LPCTSTR)Identifier));
    hKey = OpenDeviceKey((LPCTSTR)Identifier);
    if ( !hKey ) {
        ERRMSG((TEXT("Failed to open devkeypath, CAM_Init failed\r\n")));
        CAM_Deinit(pCAMHead);
        return(NULL);
    }

    datasize = sizeof(DWORD);

    if ( RegQueryValueEx(hKey, PC_REG_DEVINDEX_VAL_NAME, NULL, &kvaluetype,
                         (LPBYTE)&dwDevIndex, &datasize) ) {
        ERRMSG((TEXT("Failed to get %s value, %s failed\r\n"), PC_REG_DEVINDEX_VAL_NAME, _T(__FUNCTION__)));
        RegCloseKey (hKey);
        CAM_Deinit(pCAMHead);
        return(NULL);
    }

    datasize = sizeof(DWORD);
    if ( RegQueryValueEx(hKey, PC_REG_PRIORITY_VAL_NAME, NULL, &kvaluetype,
                         (LPBYTE)&pCAMHead->Priority256, &datasize) ) {
        pCAMHead->Priority256 = DEFAULT_CE_THREAD_PRIORITY;
        DBGMSG(CIF_FUNC && CIF_USR1,
                  (TEXT("Failed to get %s value, defaulting to %d\r\n"), PC_REG_PRIORITY_VAL_NAME, pCAMHead->Priority256));
    }

    if ( RegQueryValueEx(hKey, PC_REG_MEMBASE_VAL_NAME, NULL, &kvaluetype,
                         (LPBYTE)&dwMemBase, &datasize) ) {
        DBGMSG(CIF_FUNC && CIF_USR1,
                  (TEXT("Failed to get %s value, %s failed\r\n"), PC_REG_MEMBASE_VAL_NAME, _T(__FUNCTION__)));
        RegCloseKey(hKey);
        CAM_Deinit(pCAMHead);
        return(NULL);
    }

    if ( RegQueryValueEx(hKey, PC_REG_PHYIRQ_VAL_NAME, NULL, &kvaluetype,
                         (LPBYTE)&dwIrq, &datasize) ) {
        DBGMSG(CIF_FUNC && CIF_USR1,
                  (TEXT("Failed to get %s value, %s failed\r\n"), PC_REG_PHYIRQ_VAL_NAME, _T(__FUNCTION__)));
        RegCloseKey(hKey);
        CAM_Deinit(pCAMHead);
        return(NULL);
    }
    

    RegCloseKey (hKey);

    DBGMSG(CIF_FUNC && CIF_USR1,
              (TEXT("DevIndex 0x%X, MemBase:0x%x, PhyIrq:0x%x\r\n"), dwDevIndex, dwMemBase, dwIrq));
              
    // HW intialization, Allocate our control structure.
    // Initialize hardware dependent data.    
    pCAMHead->pCtxCamera = CusCam_Initialize((UINT32)dwDevIndex, dwMemBase, dwIrq);
    if ( !pCAMHead->pCtxCamera ) {
        ERRMSG((_T("[CAM:ERR] --%s() : Failed\r\n"), _T(__FUNCTION__)));
        CAM_Deinit(pCAMHead);
        return(NULL);
    }

#ifdef USE_FUNCTBL_BIND_SCHEME
    DBGMSG(CIF_FUNC && CIF_USR1, (TEXT("About to call HWInit(%s,0x%X)\r\n"),
                          Identifier, pCAMHead));
    pHWHead = pCAMHead->pCtxCamera->pCameraIF->HWInit(Identifier, pCAMHead, pCAMHead->pCtxCamera);
    pCAMHead->pHWHead = pHWHead;

    /* Check that HWInit did stuff ok.  From here on out, call Deinit function
     * when things fail.
     */
    if ( !pHWHead ) {
        ERRMSG((TEXT("Hardware doesn't init correctly, %s failed\r\n"), _T(__FUNCTION__)));
        CAM_Deinit(pCAMHead);
        return(NULL);
    }
    DBGMSG(CIF_FUNC && CIF_USR1,
              (TEXT("Back from hardware init\r\n")));
#endif    

    DBGMSG(CIF_FUNC && CIF_USR1, (_T("[CAM] --%s()\r\n"), _T(__FUNCTION__)));

    return(pCAMHead);
}

HANDLE
CAM_Open(
        HANDLE  pHead,          // @parm Handle returned by CAM_Init.
        DWORD   AccessCode,     // @parm access code.
        DWORD   ShareMode       // @parm share mode - Not used in this driver.
        )
{
    PHW_INDEP_INFO  pCAMHead = (PHW_INDEP_INFO)pHead;
    PHW_OPEN_INFO   pOpenHead;
    PCTX_CAMERA     pCtxCamera;

    DBGMSG(CIF_FUNC && CIF_USR1,(_T("\r\n[CAM] %s(0x%08x, 0x%08x, 0x%08x)\r\n"), _T(__FUNCTION__), pHead, AccessCode, ShareMode));

	RETAILMSG(1, (TEXT("CAM_Open\r\n")));
    // Return NULL if CameraInit failed.
    if ( !pCAMHead ) {
        ERRMSG((TEXT("Open attempted on uninited device!\r\n")));
        SetLastError(ERROR_INVALID_HANDLE);
        return(NULL);
    }
    else
    {
        pCtxCamera = pCAMHead->pCtxCamera;
    }

    if (AccessCode & DEVACCESS_BUSNAMESPACE ) {
        AccessCode &=~(GENERIC_READ |GENERIC_WRITE|GENERIC_EXECUTE|GENERIC_ALL);
    }

    // Return NULL if opening with access & someone else already has
    /*if ( (AccessCode & (GENERIC_READ | GENERIC_WRITE)) &&
         pCAMHead->pAccessOwner ) {
        DEBUGMSG (CAM_ZONE_OPEN|CAM_ZONE_ERROR,
                  (TEXT("Open requested access %x, handle x%X already has x%X!\r\n"),
                   AccessCode, pCAMHead->pAccessOwner,
                   pCAMHead->pAccessOwner->AccessCode));
        SetLastError(ERROR_INVALID_ACCESS);
        return(NULL);
    }*/

    // OK, lets allocate an open structure
    pOpenHead    =  (PHW_OPEN_INFO)LocalAlloc(LPTR, sizeof(HW_OPEN_INFO));
    if ( !pOpenHead ) {
        ERRMSG((TEXT("Error allocating memory for pOpenHead, CAM_Open failed\n\r")));
        return(NULL);
    }

    // Init the structure
    pOpenHead->pCamHead = pCAMHead;  // pointer back to our parent
    pOpenHead->StructUsers = 0;
    pOpenHead->AccessCode = AccessCode;
    pOpenHead->ShareMode = ShareMode;
    pOpenHead->PostCtxt.hPostCmdDone = CreateEvent(NULL, TRUE, FALSE, NULL);

    // if we have access permissions, note it in pCAMhead
    /*if ( AccessCode & (GENERIC_READ | GENERIC_WRITE) ) {
        DEBUGMSG(CAM_ZONE_INIT|CAM_ZONE_OPEN,
                 (TEXT("CAM_Open: Access permission handle granted x%X\n\r"),
                  pOpenHead));
        pCAMHead->pAccessOwner = pOpenHead;
    }*/

    // add this open entry to list of open entries.
    // Note that we hold the open CS for the duration of the routine since
    // all of our state info is in flux during this time.  In particular,
    // without the CS is would be possible for an open & close to be going on
    // simultaneously and have bad things happen like spinning a new event
    // thread before the old one was gone, etc.
    EnterCriticalSection(&(pCAMHead->OpenCS));
    InsertHeadList(&pCAMHead->OpenList,
                   &pOpenHead->llist);

    // We do special for Power Manger and Device Manager.
/*    if((AccessCode == 0x0) && (ShareMode == (FILE_SHARE_READ|FILE_SHARE_WRITE)))
    {
        // PM calls
        return 0x100;
    }
*/  
    if (pOpenHead->AccessCode &  DEVACCESS_BUSNAMESPACE ) {
        // OK, We do not need initialize pSerailHead and start any thread. return the handle now.
        LeaveCriticalSection(&(pCAMHead->OpenCS));
        DBGMSG(CIF_FUNC && CIF_USR1, (TEXT("[CAM] CAM_Open handle 0x%x, 0x%x, Ref 0x%x\r\n"),
                                        pOpenHead, pOpenHead->pCamHead, pCAMHead->OpenCnt));
        return(pOpenHead);

    }

    // If port not yet opened, we need to do some init
    if ( ! pCAMHead->OpenCnt ) {
        DBGMSG(CIF_FUNC && CIF_USR1,
                 (TEXT("CAM_Open: First open : Do Init x%X\n\r"),
                  pOpenHead));

        // Create Open Context
        // We always grant open for all access
        // Camera Initialization with camera module will be processed by IOCTL(IOCTL_CAM_INIT)
        // If some Open process is needed, insert it here, and if that process fail then jump to OpenFail
    }
    ++(pCAMHead->OpenCnt);

    pOpenHead->PostCtxt.dwContextNum = pCAMHead->OpenCnt;

    // OK, we are finally back in a stable state.  Release the CS.
    LeaveCriticalSection(&(pCAMHead->OpenCS));

    DBGMSG(CIF_FUNC && CIF_USR1, (TEXT("-CAM_Open handle x%X, x%X, Ref x%X\r\n"),
                                        pOpenHead, pOpenHead->pCamHead, pCAMHead->OpenCnt));

    return(pOpenHead);
/*
// All failcase alreay treated above
OpenFail :
    DEBUGMSG (CAM_ZONE_OPEN|CAM_ZONE_TEMP, (TEXT("-CAM_Open handle x%X, x%X, Ref x%X\r\n"),
                                        NULL, pOpenHead->pCamHead, pCAMHead->OpenCnt));

    SetLastError(ERROR_OPEN_FAILED);

    // If this was the handle with access permission, remove pointer
    if ( pOpenHead == pCAMHead->pAccessOwner )
        pCAMHead->pAccessOwner = NULL;

    // Remove the Open entry from the linked list
    RemoveEntryList(&pOpenHead->llist);

    // OK, everything is stable so release the critical section
    LeaveCriticalSection(&(pCAMHead->OpenCS));

    LocalFree( pOpenHead );

    RETAILMSG(CAM_ZONE_ERROR,(_T("[CAM:ERR] --%s() : Failed\r\n"), _T(__FUNCTION__)));

    return(NULL);
*/
}

// ****************************************************************
//
//      @doc EXTERNAL
//
//      @func BOOL      | CAM_Close | close the camera device.
//
//      @parm DWORD | pHead             | Context pointer returned from CAM_Open
//
//      @rdesc TRUE if success; FALSE if failure
//
//      @remark This routine is called by the device manager to close the device.
//
//
//
BOOL
CAM_Close(PHW_OPEN_INFO pOpenHead)
{
    PHW_INDEP_INFO  pCAMHead = pOpenHead->pCamHead;
    PCTX_CAMERA     pCtxCamera;
    BOOL            RetCode = TRUE;

    DBGMSG(CIF_FUNC && CIF_USR1, (_T("\r\n[CAM] CAM_Close(0x%08x)\r\n"), pOpenHead));

    if ( !pCAMHead ) {
        ERRMSG((TEXT("!!CAM_Close: pCAMHead == NULL!!\r\n")));
        SetLastError(ERROR_INVALID_HANDLE);
        return(FALSE);
    }
    pCtxCamera = (PCTX_CAMERA)pCAMHead->pCtxCamera;

    // Use the OpenCS to make sure we don't collide with an in-progress open.
    EnterCriticalSection(&(pCAMHead->OpenCS));
    // We do special for Power Manger and Device Manager.
    if (pOpenHead->AccessCode & DEVACCESS_BUSNAMESPACE) {
        // Remove the entry from the linked list
        RemoveEntryList(&pOpenHead->llist);
        // Free all data allocated in open : NONE
        LocalFree( pOpenHead );
    }
    else
    if ( pCAMHead->OpenCnt ) {
        --(pCAMHead->OpenCnt);

        DBGMSG(CIF_FUNC && CIF_USR1, (TEXT("CAM_Close: (%d handles) \r\n"), pCAMHead->OpenCnt));

        // In multi open case, do we need to restore state later on or something???? : NONE


        
        // If we are closing the last open handle, then close PDD also
        if ( !pCAMHead->OpenCnt ) {
            DBGMSG(CIF_FUNC && CIF_USR1, (TEXT("About to call HWClose\r\n")));
            //if ( pCtxCamera )            // check this code
            //    CusCam_CameraDeinitialize(pCtxCamera->uCamID);
            DBGMSG(CIF_FUNC && CIF_USR1, (TEXT("Returned from HWClose\r\n")));
        }

        // If this was the handle with access permission, remove pointer
        if ( pOpenHead == pCAMHead->pAccessOwner ) {
            DBGMSG(CIF_FUNC && CIF_USR1,
                     (TEXT("CAM_Close: Closed access owner handle\n\r"),
                      pOpenHead));

            pCAMHead->pAccessOwner = NULL;
        }

        // Remove the entry from the linked list
        RemoveEntryList(&pOpenHead->llist);

        CloseHandle(pOpenHead->PostCtxt.hPostCmdDone);

        // Free all data allocated in open : NONE in MDD
        LocalFree( pOpenHead );
    } else {
        ERRMSG((TEXT("!!Close of non-open serial port\r\n")));
        SetLastError(ERROR_INVALID_HANDLE);
        RetCode = FALSE;
    }

    // OK, other inits/opens can go ahead.
    LeaveCriticalSection(&(pCAMHead->OpenCS));

    DBGMSG(CIF_FUNC && CIF_USR1, (TEXT("-CAM_Close\r\n")));
    return(RetCode);
}

// ****************************************************************
//
//      @func BOOL | CAM_IOControl | Device IO control routine
//      @parm DWORD | dwOpenData | value returned from CAM_Open call
//      @parm DWORD | dwCode | io control code to be performed
//      @parm PBYTE | pBufIn | input data to the device
//      @parm DWORD | dwLenIn | number of bytes being passed in
//      @parm PBYTE | pBufOut | output data from the device
//      @parm DWORD | dwLenOut |maximum number of bytes to receive from device
//      @parm PDWORD | pdwActualOut | actual number of bytes received from device
//
//      @rdesc          Returns TRUE for success, FALSE for failure
//
//      @remark         Routine exported by a device driver.  "CAM" is the string
//                              passed in as lpszType in RegisterDevice

BOOL
CAM_IOControl(PHW_OPEN_INFO pOpenHead,
              DWORD dwCode, PBYTE pBufIn,
              DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
              PDWORD pdwActualOut)
{
    BOOL            RetVal           = TRUE;        // Initialize to success
    PHW_INDEP_INFO  pCAMHead; //= pOpenHead->pCamHead;
//    PLIST_ENTRY     pEntry;
    PCTX_CAMERA     pCtxCamera = NULL;
//    PVOID           pHWHead  = NULL;

    DBGMSG(CIF_FUNC && CIF_USR1, (_T("\r\n[CAM] CAM_IOControl(0x%08x, 0x%08x)\r\n"), pOpenHead, dwCode));

//	RETAILMSG(1, (TEXT("CAM_IOControl dwCode = %x\r\n"),dwCode));

    if (pOpenHead==NULL) {
        SetLastError (ERROR_INVALID_HANDLE);
        return(FALSE);
    }
    pCAMHead = pOpenHead->pCamHead;
    if ( pCAMHead ) {
        pCtxCamera = (PCTX_CAMERA)pCAMHead->pCtxCamera;
//        pHWHead  = pCAMHead->pHWHead; // we do not use FunctionTable Binding
    } else {
        SetLastError (ERROR_INVALID_HANDLE);
        return(FALSE);
    }
/*
    if ((pOpenHead->AccessCode & DEVACCESS_BUSNAMESPACE)!=0) { // Special IOCTL
        switch (dwCode) {
        case IOCTL_POWER_CAPABILITIES:
        case IOCTL_POWER_SET:
        case IOCTL_POWER_GET:
        case IOCTL_POWER_QUERY:
        case IOCTL_REGISTER_POWER_RELATIONSHIP:
            // Power is Handle by PDD.
            // Pass IOCTL through to PDD if hook is provided
//            if ( (pFuncTbl->HWIoctl == NULL) ||
//                 (pFuncTbl->HWIoctl(pHWHead,dwCode,pBufIn,dwLenIn,pBufOut,dwLenOut, pdwActualOut) == FALSE)) {
                SetLastError (ERROR_INVALID_PARAMETER);
                RetVal = FALSE;
                DBGMSG(CIF_FUNC && CIF_USR1, (TEXT("[CAM] POWER Invalid ioctl 0x%X\r\n"), dwCode));
//            }
            break;
        default:
            SetLastError (ERROR_INVALID_HANDLE);
            RetVal = FALSE;
            break;
        }
        return RetVal;
    }
*/
    DBGMSG(CIF_FUNC && CIF_USR1,
              (TEXT("+CAM_IOControl(0x%X, %d, 0x%X, %d, 0x%X, %d, 0x%X)\r\n"),
               pOpenHead, dwCode, pBufIn, dwLenIn, pBufOut,
               dwLenOut, pdwActualOut));

    if ( !pCAMHead->OpenCnt ) {
        ERRMSG((TEXT(" CAM_IOControl - device was closed\r\n")));
        SetLastError (ERROR_INVALID_HANDLE);
        return(FALSE);
    }

    // Make sure the caller has access permissions
    // NOTE : Pay attention here.  I hate to make this check repeatedly
    // below, so I'll optimize it here.  But as you add new ioctl's be
    // sure to account for them in this if check.
    if ( !( (dwCode == IOCTL_CAM_INIT) ||
//            (dwCode == IOCTL_CAM_INITBUFFER) ||
            (dwCode == IOCTL_CAM_DEINIT) ||
            (dwCode == IOCTL_CAM_RESET) ||
            (dwCode == IOCTL_CAM_PREVIEW_START) ||
            (dwCode == IOCTL_CAM_PREVIEW_GETFRAME) ||
            (dwCode == IOCTL_CAM_PREVIEW_STOP) ||
            (dwCode == IOCTL_CAM_STILLCUT) ||
            (dwCode == IOCTL_CAM_JPEGSTILLCUT) ||
            (dwCode == IOCTL_CAM_STILL_STOP) ||
            (dwCode == IOCTL_CAM_VIDEO_START) ||
            (dwCode == IOCTL_CAM_VIDEO_GETFRAME) ||
            (dwCode == IOCTL_CAM_VIDEO_STOP) ||
            (dwCode == IOCTL_CAM_SET_PREVIEWSOURCE) ||
            (dwCode == IOCTL_CAM_SET_STILLSOURCE) ||
            (dwCode == IOCTL_CAM_SET_VIDEOSOURCE) ||
            (dwCode == IOCTL_CAM_SET_PREVIEWSIZE) ||
            (dwCode == IOCTL_CAM_SET_STILLSIZE) ||
            (dwCode == IOCTL_CAM_SET_VIDEOSIZE) ||
            (dwCode == IOCTL_CAM_SET_PROPERTY) ||
            (dwCode == IOCTL_CAM_GET_ERROR) ||
            (dwCode == IOCTL_CAM_SETCALLBACK) ||
            (dwCode == IOCTL_CAM_INIT_SENSOR) ||
            (dwCode == IOCTL_CAM_GETCURRENTFRAMENUM) ||
            (dwCode == IOCTL_CAM_PREPAREBUFFER) ||
            (dwCode == IOCTL_CAM_RELEASEBUFFER) ||
            (dwCode == IOCTL_CAM_SET_OPERATIONMODE) ||
            (dwCode == IOCTL_CAM_ZOOM) ||
            (dwCode == IOCTL_CAM_CLOCK_ONOFF) ||
            (dwCode == IOCTL_CAM_RSC_REQUEST) ||
            (dwCode == IOCTL_CAM_RSC_RELEASE) ||
            (dwCode == IOCTL_CAM_SET_PCLK) ||
            (dwCode == IOCTL_CAM_DEINIT_SENSOR) ||            
            (dwCode == IOCTL_CAM_POST_SET_PROCESSING_PARAM) ||
            (dwCode == IOCTL_CAM_POST_SET_SOURCE_BUFFER) ||
            (dwCode == IOCTL_CAM_POST_SET_NEXT_SOURCE_BUFFER) ||
            (dwCode == IOCTL_CAM_POST_SET_DESTINATION_BUFFER) ||
            (dwCode == IOCTL_CAM_POST_SET_DESTINATION_1ST_BUFFER) ||
            (dwCode == IOCTL_CAM_POST_SET_DESTINATION_2ND_BUFFER) ||
            (dwCode == IOCTL_CAM_POST_SET_DESTINATION_3RD_BUFFER) ||
            (dwCode == IOCTL_CAM_POST_SET_DESTINATION_4TH_BUFFER) ||
            (dwCode == IOCTL_CAM_POST_SET_PROCESSING_START) ||
            (dwCode == IOCTL_CAM_POST_SET_PROCESSING_STOP) ||
            (dwCode == IOCTL_CAM_POST_WAIT_PROCESSING_DONE) ||
            (dwCode == IOCTL_CAM_POST_GET_PROCESSING_STATUS) ||
            (dwCode == IOCTL_CAM_POST_SUSPEND) ||
            (dwCode == IOCTL_CAM_POST_RESUME) ||
            (dwCode == IOCTL_POWER_CAPABILITIES) ||
            (dwCode == IOCTL_POWER_QUERY) ||
            (dwCode == IOCTL_POWER_SET)) ) {

        // If not one of the above operations, then read or write
        // access permissions are required.
        if ( !(pOpenHead->AccessCode & (GENERIC_READ | GENERIC_WRITE) ) ) {
            ERRMSG((TEXT("CAM_Ioctl: Ioctl %x access permission failure x%X\n\r"),
                      dwCode, pOpenHead->AccessCode));
            SetLastError (ERROR_INVALID_ACCESS);
            return(FALSE);
        }
    }    

    switch (dwCode)
    {

//-----------------------------------------------------------------------------------------
        case IOCTL_POWER_CAPABILITIES: 
        {
            PPOWER_CAPABILITIES ppc;
            DBGMSG(CIF_FUNC && CIF_USR1, (_T("[CAM] CAM_IOControl(): IOCTL_POWER_CAPABILITIES\r\n")));
            
            if ( !pdwActualOut || !pBufOut || (dwLenOut < sizeof(POWER_CAPABILITIES)) ) {
                SetLastError (ERROR_INVALID_PARAMETER);
                return FALSE;
            }

            ppc = (PPOWER_CAPABILITIES)pBufOut;
            
            memset(ppc, 0, sizeof(POWER_CAPABILITIES));

            ppc->DeviceDx = DX_MASK(D0) | DX_MASK(D4);

            // Report our power consumption in uAmps rather than mWatts. 
            ppc->Flags = POWER_CAP_PREFIX_MICRO | POWER_CAP_UNIT_AMPS;
            
            // 25 m = 25000 uA
            // TODO: find out a more accurate value
            ppc->Power[D0] = 25000;
            
            *pdwActualOut = sizeof(POWER_CAPABILITIES);
            
        } break;

        case IOCTL_POWER_SET: 
        {
            DBGMSG(CIF_FUNC && CIF_USR1, (_T("[CAM] CAM_IOControl(): IOCTL_POWER_SET CamID = %d\r\n"),pCtxCamera->uCamID));

            CEDEVICE_POWER_STATE NewDx;

            if ( !pdwActualOut || !pBufOut || (dwLenOut < sizeof(CEDEVICE_POWER_STATE)) ) {
                SetLastError (ERROR_INVALID_PARAMETER);
                return FALSE;
            }
            
            NewDx = *(PCEDEVICE_POWER_STATE)pBufOut;

            if ( VALID_DX(NewDx) ) {
                
                DBGMSG(CIF_FUNC && CIF_USR1, (_T("[CAM] CAM_IOControl(): IOCTL_POWER_SET %d , CamID = %d\r\n"), NewDx, pCtxCamera->uCamID));
                
                switch ( NewDx ) {
                case D0:
                    if (g_PowerState[pCtxCamera->uCamID] != D0) {
                        // CamPowerUp
                        // CamPostPowerOn
                        CAM_post_power_on(pCtxCamera->uCamID);
                        CusCam_PowerUp(pCtxCamera->uCamID);
                        g_PowerState[pCtxCamera->uCamID] = D0;
                        
                    }
                    break;

                default:
                    if (g_PowerState[pCtxCamera->uCamID] != (_CEDEVICE_POWER_STATE)D4) {
                        // CamPowerDown
                        // CamPostPowerDown
                        CAM_post_power_off(pCtxCamera->uCamID);
                        CusCam_PowerDown(pCtxCamera->uCamID);
                        g_PowerState[pCtxCamera->uCamID] = (_CEDEVICE_POWER_STATE)D4;
                    }
                    break;
                }

                // return our state
                *(PCEDEVICE_POWER_STATE)pBufOut = g_PowerState[pCtxCamera->uCamID];

                *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
            }
            else {
                SetLastError (ERROR_INVALID_PARAMETER);
                return FALSE;
            }
        }
        break;

        case IOCTL_POWER_GET: 
            DBGMSG(CIF_FUNC && CIF_USR1, (_T("[CAM] CAM_IOControl(): IOCTL_POWER_GET\r\n")));

            if ( !pdwActualOut || !pBufOut || (dwLenOut < sizeof(CEDEVICE_POWER_STATE)) ) {
                SetLastError (ERROR_INVALID_PARAMETER);
                return FALSE;
            }

            *(PCEDEVICE_POWER_STATE)pBufOut = g_PowerState[pCtxCamera->uCamID];

            DBGMSG(CIF_FUNC && CIF_USR1,(_T("[CAM] CAM_IOControl(): IOCTL_POWER_GET D%d\r\n"), g_PowerState[pCtxCamera->uCamID]));

            *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
            break;

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


//-----------------------------------------------------------------------------------------
        // Camera resource control
        case IOCTL_CAM_RSC_REQUEST:
        case IOCTL_CAM_RSC_RELEASE:
            DBGMSG(CIF_FUNC && CIF_USR1, (_T("[CAM] CAM_IOControl(): IOCTL_CAM_RSC_REQUEST && IOCTL_CAM_RSC_RELEASE\r\n")));

            RetVal = CAM_Resource_API_Proc(pOpenHead, dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, pdwActualOut); 
            break;


//-----------------------------------------------------------------------------------------
        // Camera Control Interface
        case IOCTL_CAM_INIT:
        case IOCTL_CAM_DEINIT:
        case IOCTL_CAM_RESET:
        case IOCTL_CAM_PREVIEW_START:
        case IOCTL_CAM_PREVIEW_GETFRAME:
        case IOCTL_CAM_PREVIEW_STOP:
        case IOCTL_CAM_STILLCUT:
        case IOCTL_CAM_JPEGSTILLCUT:
        case IOCTL_CAM_STILL_STOP:
        case IOCTL_CAM_VIDEO_START:
        case IOCTL_CAM_VIDEO_GETFRAME:
        case IOCTL_CAM_VIDEO_STOP:
        case IOCTL_CAM_SET_PREVIEWSOURCE:
        case IOCTL_CAM_SET_STILLSOURCE:
        case IOCTL_CAM_SET_VIDEOSOURCE:
        case IOCTL_CAM_SET_PREVIEWSIZE:
        case IOCTL_CAM_SET_STILLSIZE:
        case IOCTL_CAM_SET_VIDEOSIZE:
        case IOCTL_CAM_SET_PROPERTY:
        case IOCTL_CAM_GET_ERROR:
        case IOCTL_CAM_SETCALLBACK:
        case IOCTL_CAM_INIT_SENSOR:
        case IOCTL_CAM_GETCURRENTFRAMENUM:
        case IOCTL_CAM_PREPAREBUFFER:
        case IOCTL_CAM_RELEASEBUFFER:
        case IOCTL_CAM_SET_OPERATIONMODE:
        case IOCTL_CAM_SET_REGISTER:
        case IOCTL_CAM_CAPTURECONTROL:
        case IOCTL_CAM_ZOOM:
        case IOCTL_CAM_CLOCK_ONOFF:
        case IOCTL_CAM_SET_PCLK:
        case IOCTL_CAM_SET_DMA_PARAMETER:
        case IOCTL_CAM_DEINIT_SENSOR:        
        case IOCTL_CAM_IMAGE_EFFECT_ON:
        case IOCTL_CAM_IMAGE_EFFECT_OFF:
            RetVal = CAM_Camera_API_Proc(pOpenHead, dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, pdwActualOut); 
            break;
            
//-------------------------------------
        // Post Processor Interface
        case IOCTL_CAM_POST_SET_PROCESSING_PARAM:
        case IOCTL_CAM_POST_SET_SOURCE_BUFFER:
        case IOCTL_CAM_POST_SET_NEXT_SOURCE_BUFFER:
        case IOCTL_CAM_POST_SET_DESTINATION_BUFFER:
        case IOCTL_CAM_POST_SET_DESTINATION_1ST_BUFFER:
        case IOCTL_CAM_POST_SET_DESTINATION_2ND_BUFFER:
        case IOCTL_CAM_POST_SET_DESTINATION_3RD_BUFFER:
        case IOCTL_CAM_POST_SET_DESTINATION_4TH_BUFFER:
        case IOCTL_CAM_POST_SET_PROCESSING_START:
        case IOCTL_CAM_POST_SET_PROCESSING_STOP:
        case IOCTL_CAM_POST_WAIT_PROCESSING_DONE:
        case IOCTL_CAM_POST_GET_PROCESSING_STATUS:
        case IOCTL_CAM_POST_SUSPEND:
        case IOCTL_CAM_POST_RESUME:
            RetVal = CAM_Post_API_Proc(pOpenHead, ((dwCode>>2)&0xFFF), pBufIn, dwLenIn, pBufOut, dwLenOut, pdwActualOut);
            break;
            
        default : 
            ERRMSG((_T("[CAM:ERR] CAM_IOControl(): Ioctl code = 0x%x\r\n"), dwCode));
            RetVal = FALSE;

    }

    return RetVal;    // Failure
}

