예제 #1
0
CNXT_SMC_STATUS cnxt_smc_get_state ( CNXT_SMC_HANDLE Handle,
                                     CNXT_SMC_STATE  *pState )
{
   CNXT_SMC_INST *pInst = (CNXT_SMC_INST*)Handle;
   u_int32 uUnit;

   IS_DRIVER_INITED(SMC,pDriverInst->bInit);
   IS_VALID_HANDLE(SMC, pInst, &(pDriverInst->pUnitInst[pInst->uUnitNumber].pFirstInst));

   /*
    * Put Driver Specific code here.
    */
   uUnit = pInst->uUnitNumber;

   /* check if a card is in the slot */
   if ( CNXT_GET ( SMC_ICC_STAT_REG_ADDR ( BANK_FROM_SLOT_ID ( uUnit ) ),
                   SMC_ICC_STAT_CARDPRESENT_MASK ) == 0 )
   {
      *pState = CNXT_SMC_EMPTY;
   }
   else
   {
      if ( Card[uUnit].State == SMC_ATR_PARSED )
      {
         *pState = CNXT_SMC_READY;
      }
      else
      {
         *pState = CNXT_SMC_NOT_READY;
      }
   }

   return CNXT_SMC_OK;
}  /* end cnxt_smc_query_state */
예제 #2
0
CNXT_SMC_STATUS cnxt_smc_get_atr ( CNXT_SMC_HANDLE Handle,
                                   void            *pAtr,
                                   u_int32         *puBufLength )
{
   CNXT_SMC_INST *pInst = (CNXT_SMC_INST*)Handle;
   u_int32 uUnit;

   IS_DRIVER_INITED(SMC,pDriverInst->bInit);
   IS_VALID_HANDLE(SMC, pInst, &(pDriverInst->pUnitInst[pInst->uUnitNumber].pFirstInst));
   IS_NOT_NULL_POINTER(SMC, puBufLength);

   /*
    * Put Driver Specific code here.
    */
   uUnit = pInst->uUnitNumber;

   /* check state of the card */
   if ( Card[uUnit].State < SMC_ATR_RECEIVED )
   {
      return CNXT_SMC_NOT_AVAILABLE;
   }

   if ( pAtr == NULL )
   {
      /* get size of ATR only */
      *puBufLength = Card[uUnit].uAtrLength;
      return CNXT_SMC_OK;
   }

   if ( *puBufLength < Card[uUnit].uAtrLength )
   {
      /* input buffer is too small */
      *puBufLength = Card[uUnit].uAtrLength;
      return CNXT_SMC_BAD_PARAMETER;
   }

   /* copy ATR */
   memcpy ( pAtr, Card[uUnit].uAtr, Card[uUnit].uAtrLength );
   *puBufLength = Card[uUnit].uAtrLength;

   return CNXT_SMC_OK;
}  /* end cnxt_smc_get_atr */
예제 #3
0
CNXT_SMC_STATUS cnxt_smc_close ( CNXT_SMC_HANDLE Handle )
{
   CNXT_SMC_INST *pInst = (CNXT_SMC_INST*)Handle;
   CNXT_SMC_UNIT_INST *pUnitInst;

   IS_DRIVER_INITED(SMC,pDriverInst->bInit);
   IS_VALID_HANDLE(SMC, pInst, &(pDriverInst->pUnitInst[pInst->uUnitNumber].pFirstInst));

   if (sem_get(pDriverInst->DriverSem, KAL_WAIT_FOREVER) != RC_OK)
   {
      return CNXT_SMC_INTERNAL_ERROR;
   }

   /*
    * Put Driver Specific code here.
    */

   pUnitInst = &(pDriverInst->pUnitInst[pInst->uUnitNumber]);

   /* release the resource of the instance */
   if (REMOVE_HANDLE(&(pUnitInst->pFirstInst), pInst) == FALSE)
   {
      sem_put(pDriverInst->DriverSem); /* must be last line of routine! */
      return CNXT_SMC_INTERNAL_ERROR;
   }

   DESTROY_HANDLE(&(pDriverInst->pInstList), pInst);

   /* set the unit to be shared if no instance opened for the unit */
   if ( pUnitInst->pFirstInst == NULL )
   {
      pUnitInst->bExclusive = FALSE;
   }

   sem_put(pDriverInst->DriverSem); /* must be last line of routine! */
   return CNXT_SMC_OK;
} /* end cnxt_smc_close() */
예제 #4
0
CNXT_SMC_STATUS cnxt_smc_powerdown_card ( CNXT_SMC_HANDLE Handle,
                                          bool            bAsync,
                                          void            *Tag )
{
   CNXT_SMC_INST *pInst = (CNXT_SMC_INST*)Handle;
   SMC_RW_JOB_DESCRIPTOR *pRwJob;
   u_int32 uUnit;
   bool bKs;

   IS_DRIVER_INITED(SMC,pDriverInst->bInit);
   IS_VALID_HANDLE(SMC, pInst, &(pDriverInst->pUnitInst[pInst->uUnitNumber].pFirstInst));

   /*
    * Put Driver Specific code here.
    */

   uUnit = pInst->uUnitNumber;

   /* check if there is resetting/power down pending */
   bKs = critical_section_begin ();
   if ( Card[uUnit].ResetJob.pInst )
   {
      critical_section_end ( bKs );
      return CNXT_SMC_NOT_AVAILABLE;
   }
   if ( Card[uUnit].PowerdownJob.pInst )
   {
      critical_section_end ( bKs );
      return CNXT_SMC_OK;
   }
   Card[uUnit].PowerdownJob.pInst = pInst;
   critical_section_end ( bKs );

   /* clear all pending RW jobs */
   bKs = critical_section_begin ();
   while ( Card[uUnit].pRwJob )
   {
      pRwJob = Card[uUnit].pRwJob;
      Card[uUnit].pRwJob = pRwJob->pNext;

      smc_term_rw_job ( pRwJob, CNXT_SMC_TIMEOUT );
   }
   critical_section_end ( bKs );

   if ( Card[uUnit].State == SMC_NOT_INSERT )
   {
      Card[uUnit].PowerdownJob.pInst = NULL;
      return CNXT_SMC_NOT_AVAILABLE;
   }

   /* save bAsync & Tag */
   Card[uUnit].PowerdownJob.bAsync = bAsync;
   Card[uUnit].PowerdownJob.Tag    = Tag;

   /* check if the card is up */
   if ( CNXT_GET ( SMC_ICC_STAT_REG_ADDR ( BANK_FROM_SLOT_ID ( uUnit ) ),
                   SMC_ICC_STAT_CARDPOWER_MASK ) == 0 )
   {
      /* card is down, clear power down job */
      Card[uUnit].PowerdownJob.pInst = NULL;
   }
   else
   {
      /* initiate deactivate */
      *(LPREG)( SMC_ICC_CTRL_REG_ADDR ( BANK_FROM_SLOT_ID ( uUnit ) ) ) 
                                          = SMC_ICC_CTRL_DEACTIVATECARD_MASK;
   }

   if ( Card[uUnit].PowerdownJob.bAsync == FALSE )
   {
      if ( sem_get ( Card[uUnit].PowerdownJob.SyncSem,
                     Card[uUnit].Config.uTimeout ) != RC_OK )
      {
         return CNXT_SMC_TIMEOUT;
      }
   }
      

   return CNXT_SMC_OK;
}  /* end cnxt_smc_reset_card */
예제 #5
0
CNXT_SMC_STATUS cnxt_smc_reset_card ( CNXT_SMC_HANDLE Handle,
                                      bool            bAsync,
                                      void            *Tag )
{
   CNXT_SMC_INST *pInst = (CNXT_SMC_INST*)Handle;
   SMC_RW_JOB_DESCRIPTOR *pRwJob;
   u_int32 uUnit, uBank;
   bool bKs;

   IS_DRIVER_INITED(SMC,pDriverInst->bInit);
   IS_VALID_HANDLE(SMC, pInst, &(pDriverInst->pUnitInst[pInst->uUnitNumber].pFirstInst));

   /*
    * Put Driver Specific code here.
    */

   uUnit = pInst->uUnitNumber;

   /* check if there is another resetting pending */
   bKs = critical_section_begin ();
   if ( Card[uUnit].ResetJob.pInst ) 
   {
      critical_section_end ( bKs );
      return CNXT_SMC_OK;
   }
   Card[uUnit].ResetJob.pInst = pInst;
   critical_section_end ( bKs );

   /* clear other pending transactions */
   bKs = critical_section_begin ();
   if ( Card[uUnit].PowerdownJob.pInst ) 
        
   {
      if ( ( Card[uUnit].PowerdownJob.bAsync ) && 
           ( (Card[uUnit].PowerdownJob.pInst)->pNotifyFn ) )
      {
         (Card[uUnit].PowerdownJob.pInst)->pNotifyFn ( 
                               (CNXT_SMC_HANDLE)Card[uUnit].PowerdownJob.pInst,
                               (Card[uUnit].PowerdownJob.pInst)->pUserData,
                               CNXT_SMC_EVENT_CARD_POWER_DOWN_TIMEOUT,
                               NULL,
                               Card[uUnit].PowerdownJob.Tag );
      }
      Card[uUnit].PowerdownJob.pInst = NULL;
   }
   critical_section_end ( bKs );
                                                   
   /* clear all pending RW jobs */
   while ( Card[uUnit].pRwJob )
   {
      bKs = critical_section_begin ();
      pRwJob = Card[uUnit].pRwJob;
      Card[uUnit].pRwJob = pRwJob->pNext;
      critical_section_end ( bKs );

      smc_term_rw_job ( pRwJob, CNXT_SMC_TIMEOUT );
   }


   /* save bAsync & Tag */
   Card[uUnit].ResetJob.bAsync = bAsync;
   Card[uUnit].ResetJob.Tag    = Tag;

   uBank = BANK_FROM_SLOT_ID ( uUnit );

   /* initialize card descriptor */
   smc_descriptor_init ( &Card[uUnit], uUnit );

   /* check if there is card in the slot */
   if ( Card[uUnit].State == SMC_NOT_INSERT )
   {
      Card[uUnit].ResetJob.pInst = NULL;
      return CNXT_SMC_NOT_AVAILABLE;
   }

   /* initialize card HW */
   smc_hw_init ( &Card[uUnit], uUnit );

   /* set receive mode to receive ATR */
   CNXT_SET_VAL ( SMC_TERM_CTRL_REG_ADDR ( uBank ), 
                  SMC_TERM_CTRL_MODE_MASK, 
                  SMC_MODE_RX );

   /* Enable Rx Interrupt */
   CNXT_SET_VAL ( SMC_INT_MASK_REG_ADDR ( uBank ), 
                  SMC_INT_RXREAD_MASK, 
                  SMC_ENABLE );

   /* initiate reset */
   if (CNXT_GET (SMC_ICC_STAT_REG_ADDR (uBank), SMC_ICC_STAT_CARDPOWER_MASK))
   {
      /* do a warm reset */
      *(LPREG)(SMC_ICC_CTRL_REG_ADDR (uBank)) = SMC_ICC_CTRL_WARMRESET_MASK;
   }
   else
   {
      /* do a cold reset */
      *(LPREG)(SMC_ICC_CTRL_REG_ADDR (uBank)) = ( SMC_ICC_CTRL_ACTIVATECARD_MASK |
                                                  SMC_ICC_CTRL_DEACTIVATECARD_MASK );
   }

   if ( Card[uUnit].ResetJob.bAsync == FALSE )
   {
      if ( sem_get ( Card[uUnit].ResetJob.SyncSem,
                     Card[uUnit].Config.uTimeout ) != RC_OK )
      {
         return CNXT_SMC_TIMEOUT;
      }
   }
   
   return CNXT_SMC_OK;
}  /* end cnxt_smc_reset_card */
예제 #6
0
CNXT_SMC_STATUS cnxt_smc_read_write ( CNXT_SMC_HANDLE Handle,
                                      bool            bAsync,
                                      void            *pOutBuf,
                                      u_int32         uOutLength,
                                      void            *pInBuf,
                                      u_int32         *pInLength,
                                      void            *Tag )
{
   CNXT_SMC_INST *pInst = (CNXT_SMC_INST*)Handle;
   u_int32 uUnit;
   SMC_RW_JOB_DESCRIPTOR *pRwJob, *pJobNode;
   bool bKs;
   CNXT_SMC_STATUS RetCode = CNXT_SMC_OK;

   IS_DRIVER_INITED(SMC,pDriverInst->bInit);
   IS_VALID_HANDLE(SMC, pInst, &(pDriverInst->pUnitInst[pInst->uUnitNumber].pFirstInst));

   if ( ( ( pOutBuf == NULL ) || ( uOutLength == 0 ) ) && 
        ( ( pInBuf == NULL ) || ( pInLength == NULL ) || ( *pInLength == 0 ) ) )
   {
      return CNXT_SMC_BAD_PARAMETER;
   }

   uUnit = pInst->uUnitNumber;

   /* check the state of the card */
   if ( Card[uUnit].State != SMC_ATR_PARSED )
   {
      return CNXT_SMC_NOT_AVAILABLE;
   }

   /* set up Rw job */
   bKs = critical_section_begin ();
   if ( ( pRwJob = cnxt_smc_new_rw_job () ) == NULL )
   {
      critical_section_end(bKs);
      return CNXT_SMC_RESOURCE_ERROR;
   }
   critical_section_end(bKs);
  
#ifdef TFCAS
	CNXT_SET_VAL ( SMC_CHTIME_REG_ADDR ( uUnit ), SMC_TIME_CYCLES_MASK, 9600 );
	CNXT_SET_VAL ( SMC_BLKTIME_REG_ADDR ( uUnit ), SMC_TIME_CYCLES_MASK, 9600 );
#endif

   pRwJob->pInst        = pInst;
   pRwJob->bAsync       = bAsync;
   pRwJob->pTxPtr       = pOutBuf;
   pRwJob->uBytesToTx   = uOutLength;
   pRwJob->pRxPtr       = pInBuf;
   pRwJob->pBytesRecved = pInLength;
   pRwJob->pRxBufEnd    = (u_int8*)pInBuf + *pInLength;
   pRwJob->Tag          = Tag;
   pRwJob->pNext        = NULL;
   *(pRwJob->pBytesRecved) = 0;

   /* add the job to the execution list */
   bKs = critical_section_begin ();
   if ( Card[uUnit].pRwJob )
   {
      pJobNode = Card[uUnit].pRwJob;
      while ( pJobNode->pNext )
      {
         pJobNode = pJobNode->pNext;
      }
      pJobNode->pNext = pRwJob;
   }
   else
   {
      Card[uUnit].pRwJob = pRwJob;
      smc_start_rw_job ( pRwJob );
   }
   critical_section_end ( bKs );

   if ( ( bAsync == FALSE ) && pRwJob->SyncSem )
   {
      if ( sem_get ( pRwJob->SyncSem, KAL_WAIT_FOREVER ) == RC_OK )
      {
         /* rw job finished */
         RetCode = pRwJob->Status;
         bKs = critical_section_begin ();
         cnxt_smc_free_rw_job ( pRwJob );
         critical_section_end ( bKs );
      }
      else
      {
         RetCode = CNXT_SMC_INTERNAL_ERROR;
      }
   }

#ifdef TFCAS
	CNXT_SET_VAL ( SMC_CHTIME_REG_ADDR ( uUnit ), SMC_TIME_CYCLES_MASK, 9600 );
	CNXT_SET_VAL ( SMC_BLKTIME_REG_ADDR ( uUnit ), SMC_TIME_CYCLES_MASK, 9600 );
#endif

   return RetCode;
}
예제 #7
0
CNXT_SMC_STATUS cnxt_smc_get_config ( CNXT_SMC_HANDLE      Handle,
                                      CNXT_SMC_CONFIG_ITEM Item,
                                      u_int32              *puValue )
{
   CNXT_SMC_INST *pInst = (CNXT_SMC_INST*)Handle;
   u_int32 uUnit;

   IS_DRIVER_INITED(SMC,pDriverInst->bInit);
   IS_VALID_HANDLE(SMC, pInst, &(pDriverInst->pUnitInst[pInst->uUnitNumber].pFirstInst));

   /*
    * Put Driver Specific code here.
    */
   uUnit = pInst->uUnitNumber;

   /* check the state of the card */
   if ( Card[uUnit].State != SMC_ATR_PARSED )
   {
      return CNXT_SMC_NOT_AVAILABLE;
   }

   switch ( Item )
   {
      case CNXT_SMC_CONFIG_CONVENTION:
         *puValue = (u_int32)Card[uUnit].Config.uConvention;
         break;

      case CNXT_SMC_CONFIG_PROTOCOL:
         *puValue = (u_int32)Card[uUnit].Config.uProtocol;
         break;
            
      case CNXT_SMC_CONFIG_FI:
         *puValue = (u_int32)Card[uUnit].Config.uFI;
         break;

      case CNXT_SMC_CONFIG_DI:
         *puValue = (u_int32)Card[uUnit].Config.uDI;
         break;

      case CNXT_SMC_CONFIG_PI1:
         *puValue = (u_int32)Card[uUnit].Config.uPI1;
         break;

      case CNXT_SMC_CONFIG_PI2:
         *puValue = (u_int32)Card[uUnit].Config.uPI2;
         break;

      case CNXT_SMC_CONFIG_II:
         *puValue = (u_int32)Card[uUnit].Config.uII;
         break;

      case CNXT_SMC_CONFIG_N:
         *puValue = (u_int32)Card[uUnit].Config.uN;
         break;
            
      case CNXT_SMC_CONFIG_HISTORICAL:
         *puValue = (u_int32)Card[uUnit].Config.pHistorical;
         break;

      case CNXT_SMC_CONFIG_HISTORICAL_LEN:
         *puValue = (u_int32)Card[uUnit].Config.uHistoricalLength;
         break;

      case CNXT_SMC_CONFIG_TIMEOUT:
         *puValue = (u_int32)Card[uUnit].Config.uTimeout;
         break;

      case CNXT_SMC_CONFIG_RETRY:
         *puValue = (u_int32)Card[uUnit].Config.uRetry;
         break;

      default:
         return CNXT_SMC_BAD_PARAMETER;
   }


   return CNXT_SMC_OK;
}  /* end cnxt_smc_get_config */
예제 #8
0
CNXT_SMC_STATUS cnxt_smc_set_config ( CNXT_SMC_HANDLE      Handle,
                                      CNXT_SMC_CONFIG_ITEM Item,
                                      u_int32              uValue )
{
   CNXT_SMC_INST *pInst = (CNXT_SMC_INST*)Handle;
   u_int32 uUnit, uBank;

   IS_DRIVER_INITED(SMC,pDriverInst->bInit);
   IS_VALID_HANDLE(SMC, pInst, &(pDriverInst->pUnitInst[pInst->uUnitNumber].pFirstInst));

   /*
    * Put Driver Specific code here.
    */
   uUnit = pInst->uUnitNumber;

   /* check the state of the card */
   if ( Card[uUnit].State != SMC_ATR_PARSED )
   {
      return CNXT_SMC_NOT_AVAILABLE;
   }

   uBank = BANK_FROM_SLOT_ID ( uUnit );

   switch ( Item )
   {
      case CNXT_SMC_CONFIG_CONVENTION:
         if ( (CNXT_SMC_CONVENTION)uValue == CNXT_SMC_CONV_DIRECT )
         {
            CNXT_SET_VAL ( SMC_CONV_REG_ADDR ( uBank ), 
                           SMC_CONV_SENSE_MASK, 
                           SMC_SENSE_DIRECT );
            CNXT_SET_VAL ( SMC_CONV_REG_ADDR ( uBank ), 
                           SMC_CONV_ORDER_MASK, 
                           SMC_ORDER_DIRECT );
         }
         else if ( (CNXT_SMC_CONVENTION)uValue == CNXT_SMC_CONV_INVERSE )
         {
            CNXT_SET_VAL ( SMC_CONV_REG_ADDR ( uBank ),
                           SMC_CONV_SENSE_MASK,
                           SMC_SENSE_INVERSE );
            CNXT_SET_VAL ( SMC_CONV_REG_ADDR ( uBank ),
                           SMC_CONV_ORDER_MASK,
                           SMC_ORDER_INVERSE );
         }
         else
         {
            return CNXT_SMC_BAD_PARAMETER;
         }
         Card[uUnit].Config.uConvention = (u_int8)uValue;
         break;

      case CNXT_SMC_CONFIG_PROTOCOL:
         /* Protocol is a software/driver state. No change required to the hw */
         if ( ( (CNXT_SMC_PROTOCOL)uValue == CNXT_SMC_PROTOCOL_T0 ) || 
              ( (CNXT_SMC_PROTOCOL)uValue == CNXT_SMC_PROTOCOL_T1 ) )
         {
            Card[uUnit].Config.uProtocol = (u_int8)uValue;
         }
         else
         {
            return CNXT_SMC_BAD_PARAMETER;
         }
         break;
            
      case CNXT_SMC_CONFIG_FI:
         if ( ( uValue > 0 ) && ( uValue <= 15 ) )
         {
            /* set the new F */
            smc_set_fi ( uBank, uValue );
            Card[uUnit].Config.uFI = (u_int8)uValue;
         }
         else
         {
            return CNXT_SMC_BAD_PARAMETER;
         }
         break;

      case CNXT_SMC_CONFIG_DI:
         if ( ( uValue > 0 ) && ( uValue <= 15 ) )
         {
            /* set the new D */
            smc_set_di ( uBank, uValue );
            Card[uUnit].Config.uDI = (u_int8)uValue;
         }
         else
         {
            return CNXT_SMC_BAD_PARAMETER;
         }
         break;

      case CNXT_SMC_CONFIG_PI1:
      case CNXT_SMC_CONFIG_PI2:
      case CNXT_SMC_CONFIG_II:
         /* these config parameters cannot be set */
         return CNXT_SMC_BAD_PARAMETER;

      case CNXT_SMC_CONFIG_N:
         if ( ( uValue > 0 ) && ( uValue <= 255 ) )
         {
            /* set new N */
            CNXT_SET_VAL ( SMC_CHGUARD_REG_ADDR ( uBank ), SMC_GUARD_CYCLES_MASK, uValue );
            Card[uUnit].Config.uN = (u_int8)uValue;
         }
         else
         {
            return CNXT_SMC_BAD_PARAMETER;
         }
         break;
            
      case CNXT_SMC_CONFIG_TIMEOUT:
         Card[uUnit].Config.uTimeout = (u_int16)uValue;
         break;

      case CNXT_SMC_CONFIG_RETRY:
         Card[uUnit].Config.uRetry = (u_int8)uValue;
         break;

      default:
         return CNXT_SMC_BAD_PARAMETER;
   }


   return CNXT_SMC_OK;
}  /* end cnxt_smc_set_config */
예제 #9
0
파일: vddsup.c 프로젝트: hackbunny/reactos
VOID WINAPI ThirdPartyVDDBop(LPWORD Stack)
{
    /* Get the Function Number and skip it */
    BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
    setIP(getIP() + 1);

    switch (FuncNum)
    {
        /* RegisterModule */
        case 0:
        {
            BOOL Success = TRUE;
            WORD RetVal  = 0;
            WORD Entry   = 0;
            LPCSTR DllName = NULL,
                   InitRoutineName     = NULL,
                   DispatchRoutineName = NULL;
            HMODULE hDll = NULL;
            VDD_PROC InitRoutine     = NULL,
                     DispatchRoutine = NULL;

            DPRINT("RegisterModule() called\n");

            /* Clear the Carry Flag (no error happened so far) */
            setCF(0);

            /* Retrieve the next free entry in the table (used later on) */
            Entry = GetNextFreeVDDEntry();
            if (Entry >= MAX_VDD_MODULES)
            {
                DPRINT1("Failed to create a new VDD module entry\n");
                Success = FALSE;
                RetVal = 4;
                goto Quit;
            }

            /* Retrieve the VDD name in DS:SI */
            DllName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI());

            /* Retrieve the initialization routine API name in ES:DI (optional --> ES=DI=0) */
            if (TO_LINEAR(getES(), getDI()) != 0)
                InitRoutineName = (LPCSTR)SEG_OFF_TO_PTR(getES(), getDI());

            /* Retrieve the dispatch routine API name in DS:BX */
            DispatchRoutineName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getBX());

            DPRINT1("DllName = '%s' - InitRoutineName = '%s' - DispatchRoutineName = '%s'\n",
                    (DllName ? DllName : "n/a"),
                    (InitRoutineName ? InitRoutineName : "n/a"),
                    (DispatchRoutineName ? DispatchRoutineName : "n/a"));

            /* Load the VDD DLL */
            hDll = LoadLibraryA(DllName);
            if (hDll == NULL)
            {
                DWORD LastError = GetLastError();
                Success = FALSE;

                if (LastError == ERROR_NOT_ENOUGH_MEMORY)
                {
                    DPRINT1("Not enough memory to load DLL '%s'\n", DllName);
                    RetVal = 4;
                    goto Quit;
                }
                else
                {
                    DPRINT1("Failed to load DLL '%s'; last error = %d\n", DllName, LastError);
                    RetVal = 1;
                    goto Quit;
                }
            }

            /* Load the initialization routine if needed */
            if (InitRoutineName)
            {
                InitRoutine = (VDD_PROC)GetProcAddress(hDll, InitRoutineName);
                if (InitRoutine == NULL)
                {
                    DPRINT1("Failed to load the initialization routine '%s'\n", InitRoutineName);
                    Success = FALSE;
                    RetVal = 3;
                    goto Quit;
                }
            }

            /* Load the dispatch routine */
            DispatchRoutine = (VDD_PROC)GetProcAddress(hDll, DispatchRoutineName);
            if (DispatchRoutine == NULL)
            {
                DPRINT1("Failed to load the dispatch routine '%s'\n", DispatchRoutineName);
                Success = FALSE;
                RetVal = 2;
                goto Quit;
            }

            /* If we arrived there, that means everything is OK */

            /* Register the VDD DLL */
            VDDList[Entry].hDll = hDll;
            VDDList[Entry].DispatchRoutine = DispatchRoutine;

            /* Call the initialization routine if needed */
            if (InitRoutine) InitRoutine();

            /* We succeeded. RetVal will contain a valid VDD DLL handle */
            Success = TRUE;
            RetVal  = ENTRY_TO_HANDLE(Entry); // Convert the entry to a valid handle

Quit:
            if (!Success)
            {
                /* Unload the VDD DLL */
                if (hDll) FreeLibrary(hDll);

                /* Set the Carry Flag to indicate that an error happened */
                setCF(1);
            }
            // else
            // {
                // /* Clear the Carry Flag (success) */
                // setCF(0);
            // }
            setAX(RetVal);
            break;
        }

        /* UnRegisterModule */
        case 1:
        {
            WORD Handle = getAX();
            WORD Entry  = HANDLE_TO_ENTRY(Handle); // Convert the handle to a valid entry

            DPRINT("UnRegisterModule() called\n");

            /* Sanity checks */
            if (!IS_VALID_HANDLE(Handle) || VDDList[Entry].hDll == NULL)
            {
                DPRINT1("Invalid VDD DLL Handle: %d\n", Entry);
                /* Stop the VDM */
                EmulatorTerminate();
                return;
            }

            /* Unregister the VDD DLL */
            FreeLibrary(VDDList[Entry].hDll);
            VDDList[Entry].hDll = NULL;
            VDDList[Entry].DispatchRoutine = NULL;
            break;
        }

        /* DispatchCall */
        case 2:
        {
            WORD Handle = getAX();
            WORD Entry  = HANDLE_TO_ENTRY(Handle); // Convert the handle to a valid entry

            DPRINT("DispatchCall() called\n");

            /* Sanity checks */
            if (!IS_VALID_HANDLE(Handle)    ||
                VDDList[Entry].hDll == NULL ||
                VDDList[Entry].DispatchRoutine == NULL)
            {
                DPRINT1("Invalid VDD DLL Handle: %d\n", Entry);
                /* Stop the VDM */
                EmulatorTerminate();
                return;
            }

            /* Call the dispatch routine */
            VDDList[Entry].DispatchRoutine();
            break;
        }

        default:
        {
            DPRINT1("Unknown 3rd-party VDD BOP Function: 0x%02X\n", FuncNum);
            setCF(1);
            break;
        }
    }
}