ParallelPort.cpp
上传用户:kathy7909
上传日期:2007-03-27
资源大小:15k
文件大小:30k
源码类别:

并口编程

开发平台:

Visual C++

  1. /*
  2. Module : PARALLEL.CPP
  3. Purpose: Implementation for an MFC class to encapsulate parallel ports
  4. Created: PJN / 28-12-1999
  5. History: PJN / 06-03-2000 Code now throws an exception if no Win32 parallel ports are available
  6.          PJN / 04-04-2000 Code handles the case where it is run on NT / Windows 2000 by throwing
  7.                           an exception                 
  8.          PJN / 19-07-2000 1. Now works on NT / Windows 2000 thanks to DriverLINX Port I/O Driver
  9.                           2. Made all typedefs and enums local to the appropiate class rather
  10.                           than polluting the global namespace
  11. Copyright (c) 1999 - 2000 by PJ Naughter.  
  12. All rights reserved.
  13. */
  14. /////////////////////////////////  Includes  //////////////////////////////////
  15. #include "stdafx.h"
  16. #include "ParallelPort.h"
  17. #include <winerror.h>
  18. #include <conio.h>
  19. #include <afxpriv.h>
  20. ///////////////////////////////// Defines / Statics ///////////////////////////
  21. #ifdef _DEBUG
  22. #define new DEBUG_NEW
  23. #undef THIS_FILE
  24. static char THIS_FILE[] = __FILE__;
  25. #endif
  26. int CParallelPort::sm_nRefCount = 0;
  27. BOOL CParallelPort::sm_bRunningOnNT = FALSE;
  28. HINSTANCE CParallelPort::sm_hDLINX = NULL;
  29. CParallelPort::LPDLPORTREADPORTUCHAR CParallelPort::sm_lpfnDlPortReadUchar = NULL;
  30. CParallelPort::LPDLPORTWRITEPORTUCHAR CParallelPort::sm_lpfnDlPortWriteUchar = NULL;
  31. CArray<CParallelPortSettings, CParallelPortSettings&> CParallelPort::sm_Ports;
  32. //////////////////////////////// Implementation ///////////////////////////////
  33. ////////// Exception handling code
  34. void AfxThrowParallelException(DWORD dwError /* = 0 */)
  35. {
  36. if (dwError == 0)
  37. dwError = ::GetLastError();
  38. CParallelException* pException = new CParallelException(dwError);
  39. TRACE(_T("Warning: throwing CParallelException for error %dn"), dwError);
  40. THROW(pException);
  41. }
  42. BOOL CParallelException::GetErrorMessage(LPTSTR pstrError, UINT nMaxError, PUINT pnHelpContext)
  43. {
  44. ASSERT(pstrError != NULL && AfxIsValidString(pstrError, nMaxError));
  45. if (pnHelpContext != NULL)
  46. *pnHelpContext = 0;
  47. LPTSTR lpBuffer;
  48. BOOL bRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  49.                       NULL,  m_dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
  50.                       (LPTSTR) &lpBuffer, 0, NULL);
  51. if (bRet == FALSE)
  52. *pstrError = '';
  53. else
  54. {
  55. lstrcpyn(pstrError, lpBuffer, nMaxError);
  56. bRet = TRUE;
  57. LocalFree(lpBuffer);
  58. }
  59. return bRet;
  60. }
  61. CString CParallelException::GetErrorMessage()
  62. {
  63.   CString rVal;
  64.   LPTSTR pstrError = rVal.GetBuffer(4096);
  65.   GetErrorMessage(pstrError, 4096, NULL);
  66.   rVal.ReleaseBuffer();
  67.   return rVal;
  68. }
  69. CParallelException::CParallelException(DWORD dwError)
  70. {
  71. m_dwError = dwError;
  72. }
  73. CParallelException::~CParallelException()
  74. {
  75. }
  76. IMPLEMENT_DYNAMIC(CParallelException, CException)
  77. #ifdef _DEBUG
  78. void CParallelException::Dump(CDumpContext& dc) const
  79. {
  80. CObject::Dump(dc);
  81. dc << "m_dwError = " << m_dwError;
  82. }
  83. #endif
  84. ////////// Settings class
  85. CParallelPortSettings::CParallelPortSettings()
  86. {
  87.   m_nBaseAddress = 0;
  88.   m_Type         = ParallelTypeUndefined;
  89.   m_ECPMode      = ECPModeUndefined;
  90. }
  91. CParallelPortSettings::CParallelPortSettings(const CParallelPortSettings& state)
  92. {
  93.   *this = state;
  94. }
  95. CParallelPortSettings& CParallelPortSettings::operator=(const CParallelPortSettings& state)
  96. {
  97.   m_nBaseAddress = state.m_nBaseAddress;
  98.   m_Type         = state.m_Type;   
  99.   m_ECPMode      = state.m_ECPMode;
  100.   return *this;
  101. }
  102. ////////// The actual parallel port class
  103. CParallelPort::CParallelPort()
  104. {
  105.   m_hPort = INVALID_HANDLE_VALUE;
  106.   m_nPortIndex = -1;
  107.   m_nBaseAddress = 0;
  108.   m_dwTimeout = 1000;  //Default timeout is 1 second
  109.   //Test for presence of parallel ports at the standard addresses of 0x3BC, 0x378 and 0x278
  110.   if (sm_nRefCount == 0)
  111.   {
  112.     ++sm_nRefCount;
  113.     //Initialize the DriverLINX driver if on NT / Windows 2000
  114.     sm_bRunningOnNT = RunningOnNT();
  115.     if (sm_bRunningOnNT)
  116.     {
  117.       if (!InitializeDriverLINX())
  118.       {
  119.         TRACE(_T("Running on Windows NT / Windows 2000 and the DriverLINX PORTIO driver is not installed, aborting !!n"));
  120.         AfxThrowParallelException();
  121.       }
  122.     }
  123.     //Open LPT1 - LPT3 as a precaution against other processes trying to write 
  124.     //to the port while we are detecting them
  125.     HANDLE hPort1 = CreateFile(_T("\\.\LPT1"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
  126.     HANDLE hPort2 = CreateFile(_T("\\.\LPT2"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
  127.     HANDLE hPort3 = CreateFile(_T("\\.\LPT3"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
  128.     //Must have at least one port available in Win32 to continue to attempt
  129.     //detection of the ports
  130.     int nWin32Ports = 0;
  131.     if (hPort1 != INVALID_HANDLE_VALUE)
  132.       ++nWin32Ports;
  133.     if (hPort2 != INVALID_HANDLE_VALUE)
  134.       ++nWin32Ports;
  135.     if (hPort3 != INVALID_HANDLE_VALUE)
  136.       ++nWin32Ports;
  137.     if (nWin32Ports == 0)
  138.     {
  139.       TRACE(_T("No parallel ports are available to Win32, aborting !!n"));
  140.       AfxThrowParallelException(ERROR_DEV_NOT_EXIST);
  141.     }
  142.     //Try to detect the details of the 3 standard ports
  143.     CParallelPortSettings settings;
  144.     if (GetPort(0x3BC, settings))
  145.       sm_Ports.Add(settings);
  146.     if (sm_Ports.GetSize() < nWin32Ports) 
  147.       if (GetPort(0x378, settings))
  148.         sm_Ports.Add(settings);
  149.     if (sm_Ports.GetSize() < nWin32Ports) 
  150.       if (GetPort(0x278, settings))
  151.         sm_Ports.Add(settings);
  152.     if (sm_Ports.GetSize() == 0)
  153.       TRACE(_T("Could not detect any parallel ports on this machinen"));
  154.     //Don't forget to close the 3 SDK handles we had open
  155.     CloseHandle(hPort3);
  156.     CloseHandle(hPort2);
  157.     CloseHandle(hPort1);
  158.   }
  159. }
  160. CParallelPort::~CParallelPort()
  161. {
  162.   //decrement the reference count and 
  163.   //free the DriverLINX pointers if necessary
  164.   --sm_nRefCount;
  165.   if (sm_nRefCount == 0)
  166.     DeInitializeDriverLINX();
  167.   Close();
  168. }
  169. IMPLEMENT_DYNAMIC(CParallelPort, CObject)
  170. #ifdef _DEBUG
  171. void CParallelPort::Dump(CDumpContext& dc) const
  172. {
  173. CObject::Dump(dc);
  174. dc << _T("m_hPort = ") << m_hPort << _T("n");
  175. }
  176. #endif
  177. void CParallelPort::Open(int nPort)
  178. {
  179.   //Call Close just in case we already have the port open
  180.   Close();
  181.   m_nPortIndex = nPort - 1;
  182.   if (m_nPortIndex < sm_Ports.GetSize())
  183.   {
  184.     //Cache the base address of the port for performance reasons
  185.     m_nBaseAddress = sm_Ports.ElementAt(m_nPortIndex).m_nBaseAddress;
  186.     //Call CreateFile to open up the parallel port. This prevents other apps 
  187.     //causing problems when we do the Port IO directly.
  188.     CString sPort;
  189.     sPort.Format(_T("\\.\LPT%d"), nPort);
  190.     m_hPort = CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
  191.     if (m_hPort == INVALID_HANDLE_VALUE)
  192.     {
  193.       TRACE(_T("Failed to open parallel port, LPT%dn"), nPort);
  194.       Close();
  195.       AfxThrowParallelException();
  196.     }
  197.   }
  198.   else
  199.   {
  200.     TRACE(_T("Could not find the parallel port, LPT%dn"), nPort);
  201.     Close();
  202.     AfxThrowParallelException(ERROR_FILE_NOT_FOUND);
  203.   }
  204. }
  205. BOOL CParallelPort::IsOpen() const
  206. {
  207.   return (m_hPort != INVALID_HANDLE_VALUE);
  208. }
  209. void CParallelPort::Close()
  210. {
  211.   if (IsOpen())
  212.   {
  213.     BOOL bSuccess = CloseHandle(m_hPort);
  214.     m_hPort = INVALID_HANDLE_VALUE;
  215.     m_nPortIndex = -1;
  216.     m_nBaseAddress = 0;
  217.     if (!bSuccess)
  218.       TRACE(_T("Failed to close the parallel port, GetLastError:%dn"), GetLastError());
  219.   }
  220. }
  221. void CParallelPort::SetECPMode(CParallelPortSettings::ECPPortMode mode)
  222. {
  223.   ASSERT(IsOpen()); //Port must be open 
  224.   CParallelPortSettings& settings = sm_Ports.ElementAt(m_nPortIndex);
  225.   ASSERT(settings.m_Type == CParallelPortSettings::ParallelTypeECP); //Must be an ECP port
  226.   ASSERT(mode != CParallelPortSettings::ECPModeUndefined);
  227.   unsigned short nEcrAddress = (unsigned short)(m_nBaseAddress + 0x402);
  228.   //Read the ECR & clear bits 5, 6 & 7
  229.   int nEcrData = _inp(nEcrAddress) & 0x1F;
  230.   
  231.   //Write the selected value to bits 5, 6 & 7
  232.   switch (mode)
  233.   {
  234.     case CParallelPortSettings::ECPModeSPP:            nEcrData |= (0 << 5); break;
  235.     case CParallelPortSettings::ECPModePS2:            nEcrData |= (1 << 5); break;
  236.     case CParallelPortSettings::ECPModeFastCentronics: nEcrData |= (2 << 5); break;
  237.     case CParallelPortSettings::ECPModeECP:            nEcrData |= (3 << 5); break;
  238.     case CParallelPortSettings::ECPModeEPP:            nEcrData |= (4 << 5); break;
  239.     case CParallelPortSettings::ECPModeTest:           nEcrData |= (6 << 5); break;
  240.     case CParallelPortSettings::ECPModeConfiguration:  nEcrData |= (7 << 5); break;
  241.     default: ASSERT(FALSE);                           break;
  242.   }
  243.   _outp(nEcrAddress, nEcrData);
  244.   //Update the value in our cached array
  245.   settings.m_ECPMode = mode;
  246. }
  247. CParallelPortSettings::ECPPortMode CParallelPort::GetECPMode()
  248. {
  249.   ASSERT(IsOpen()); //Port must be open 
  250.   CParallelPortSettings& settings = sm_Ports.ElementAt(m_nPortIndex);
  251.   ASSERT(settings.m_Type == CParallelPortSettings::ParallelTypeECP); //Must be an ECP port
  252.   CParallelPortSettings::ECPPortMode t = ReadECPMode(settings.m_nBaseAddress);
  253.   ASSERT(t == settings.m_ECPMode);
  254.   return settings.m_ECPMode;
  255. }
  256. CParallelPortSettings::ECPPortMode CParallelPort::ReadECPMode(unsigned short nBaseAddress)
  257. {
  258.   CParallelPortSettings::ECPPortMode mode = CParallelPortSettings::ECPModeUndefined;
  259.   int nEcrData = _inp((unsigned short)(nBaseAddress+0x402));
  260.   nEcrData = (nEcrData & 0xE0) >> 5;
  261.   switch (nEcrData)
  262.   { 
  263.     case 0: mode = CParallelPortSettings::ECPModeSPP;            break;
  264.     case 1: mode = CParallelPortSettings::ECPModePS2;            break;
  265.     case 2: mode = CParallelPortSettings::ECPModeFastCentronics; break;
  266.     case 3: mode = CParallelPortSettings::ECPModeECP;            break;
  267.     case 4: mode = CParallelPortSettings::ECPModeEPP;            break;
  268.     case 6: mode = CParallelPortSettings::ECPModeTest;           break;
  269.     case 7: mode = CParallelPortSettings::ECPModeConfiguration;  break;
  270.     default: break;
  271.   }
  272.   return mode;
  273. }
  274. BOOL CParallelPort::GetPort(unsigned short nBaseAddress, CParallelPortSettings& settings)
  275. {
  276.   BOOL bSuccess = FALSE;
  277.   //First try to detect an ECP port
  278.   if (GetECPPort(nBaseAddress))
  279.   {
  280.     settings.m_nBaseAddress = nBaseAddress;
  281.     settings.m_Type = CParallelPortSettings::ParallelTypeECP;
  282.     settings.m_ECPMode = ReadECPMode(nBaseAddress);
  283.     bSuccess = TRUE;
  284.   }
  285.   else
  286.   {
  287.     //If its not an ECP, look for an EPP.
  288.     //If the baseaddress is 3BCh, skip the EPP test.
  289.     //EPPs aren't allowed at 3BCh due to possible conflicts
  290.     //with video memory
  291.     BOOL bFoundEPP = FALSE;
  292.     if (nBaseAddress != 0x3BC)
  293.     {
  294.       bFoundEPP = GetEPPPort(nBaseAddress);
  295.       if (bFoundEPP)
  296.       {
  297.         settings.m_nBaseAddress = nBaseAddress;
  298.         settings.m_Type = CParallelPortSettings::ParallelTypeEPP;
  299.         settings.m_ECPMode = CParallelPortSettings::ECPModeUndefined;
  300.         bSuccess = TRUE;
  301.       }
  302.     }
  303.     if (!bFoundEPP)
  304.     {
  305.       //If its not an EPP, look for an SPP.
  306.       if (GetSPPPort(nBaseAddress))
  307.       {
  308.         //Test for a PS/2 port only if the SPP exists.
  309.         //because if the port doesn't exist, it will 
  310.         //pass the PS/2 test!
  311.         if (GetPS2Port(nBaseAddress))
  312.           settings.m_Type = CParallelPortSettings::ParallelTypePS2;
  313.         else
  314.           settings.m_Type = CParallelPortSettings::ParallelTypeSPP;
  315.         settings.m_nBaseAddress = nBaseAddress;
  316.         settings.m_ECPMode = CParallelPortSettings::ECPModeUndefined;
  317.         bSuccess = TRUE;
  318.       }
  319.     }
  320.   }
  321.   if (!bSuccess)
  322.   {
  323.     settings.m_nBaseAddress = 0;
  324.     settings.m_Type = CParallelPortSettings::ParallelTypeUndefined;
  325.     settings.m_ECPMode = CParallelPortSettings::ECPModeUndefined;
  326.   }  
  327.   return bSuccess;
  328. }
  329. BOOL CParallelPort::GetECPPort(unsigned short nBaseAddress)
  330. {
  331.   //If the ECP is idle and the FIFO empty,
  332.   //in the ECP's Ecp (at base address+402h),
  333.   //bit 1 (Fifo full)=0, and bit 0 (Fifo empty)=1.
  334.   //The first test is to see if these bits differ from the
  335.   //corresponding bits in the control port (at base address+2).
  336.   //If so a further test is to write 34h to the Ecr, 
  337.   //then read it back. Bit 1 is read/write and bit 0 is read-only.
  338.   //If the value read is 35h, the port is an ECP.
  339.   BOOL bSuccess = FALSE;
  340.  
  341.   unsigned short nEcrAddress = (unsigned short)(nBaseAddress+0x402);
  342.   int nEcrData = _inp(nEcrAddress);
  343.   
  344.   //Read bits 0 and 1 and control port bit 1
  345.   int nEcrBit0 = nEcrData & 0x1;
  346.   int nEcrBit1 = (nEcrData & 0x2) >> 1;
  347.   int nControlBit1 = (ReadControl(nBaseAddress) & 0x2) >> 1;
  348.   if (nEcrBit0 == 1 && nEcrBit1 == 0)
  349.   {
  350.     //Compare control bit 1 to ECR bit 1
  351.     //Toggle the control bit if necessary
  352.     //to be sure the two registers are different.
  353.     if (nControlBit1 == 0)
  354.     {
  355.       WriteControl(nBaseAddress, 0xF);
  356.       nControlBit1 = (ReadControl(nBaseAddress) & 0x2) >> 1;
  357.     }
  358.     if (nEcrBit1 != nControlBit1)
  359.     {
  360.       int nOriginalEcrData = nEcrData;
  361.       _outp(nEcrAddress, 0x34);
  362.       if (_inp(nEcrAddress) == 0x35)
  363.         bSuccess = TRUE;
  364.       //Restore the ECR to its original value
  365.       _outp(nEcrAddress, nOriginalEcrData);
  366.     }
  367.   }
  368.   return bSuccess;
  369. }
  370. BOOL CParallelPort::GetEPPPort(unsigned short nBaseAddress)
  371. {
  372.   //Write to an EPP register, then read it back.
  373.   //If the read matches the write, it's probably an EPP
  374.   BOOL bSuccess = FALSE;
  375.   //Use nEppAddressPort for testing, SPP's, ECP's and PS/2 ports
  376.   //do not have this register
  377.   unsigned short nEppAddressPort = (unsigned short) (nBaseAddress+3);
  378.   _outp(nEppAddressPort, 0x55);
  379.   //Clear the timeout bit after each EPP operation
  380.   int nTimeoutBit = GetEPPTimeoutBit(nBaseAddress);
  381.   int nRead = _inp(nEppAddressPort);
  382.   nTimeoutBit = GetEPPTimeoutBit(nBaseAddress);
  383.   if (nRead == 0x55)
  384.   {
  385.     _outp(nEppAddressPort, 0xAA);
  386.     nTimeoutBit = GetEPPTimeoutBit(nBaseAddress);
  387.     nRead = _inp(nEppAddressPort);
  388.     nTimeoutBit = GetEPPTimeoutBit(nBaseAddress);
  389.     if (nRead == 0xAA)
  390.       bSuccess = TRUE;
  391.   }  
  392.   return bSuccess;
  393. }
  394. int CParallelPort::GetEPPTimeoutBit(unsigned short nBaseAddress)
  395. {
  396.   //Reads and clears the EPP timeout bit (Statis port bit 0)
  397.   //Should be done after each EPP operation.
  398.   //The method for clearing the bit varies, so try 3 ways
  399.   //1. Write 1 to status port bit 0
  400.   //2. Write 0 to status port bit 1
  401.   //3. Read the status port again
  402.   int nReturn = (ReadStatus(nBaseAddress) & 0x1);
  403.   unsigned short nStatusPortAddress = (unsigned short)(nBaseAddress+1);
  404.   _outp(nStatusPortAddress, 1);
  405.   _outp(nStatusPortAddress, 0);
  406.   nReturn = (ReadStatus(nBaseAddress) & 0x1);
  407.   return nReturn;
  408. }
  409. BOOL CParallelPort::GetPS2Port(unsigned short nBaseAddress)
  410. {
  411.   //First try to tristate (disable) the data outputs by
  412.   //setting bit 5 of the control port. Then write 2 values
  413.   //to the data port and read each one back. If the values
  414.   //match, the data outputs are not disabled and the port
  415.   //is not bidirectional. If the values don't match, the data 
  416.   //outputs are disabled and the port is didirectional
  417.   BOOL bSuccess = FALSE;
  418.   //Set control port bit 5
  419.   WriteControl(nBaseAddress, 0x2F);
  420.   //Write the first byte and read it back
  421.   WriteData(nBaseAddress, 0x55);
  422.   //If it doesn't match, the port is bidirectional
  423.   if (ReadData(nBaseAddress) == 0x55)
  424.   {      
  425.     WriteData(nBaseAddress, 0xAA);
  426.     //If it doesn't match, the port is bidirectional
  427.     if (ReadData(nBaseAddress) != 0xAA)
  428.       bSuccess = TRUE;
  429.   }
  430.   else
  431.     bSuccess = TRUE;
  432.   //Reset control port bit 5
  433.   WriteControl(nBaseAddress, 0xF);
  434.   return bSuccess;
  435. }
  436. BOOL CParallelPort::GetSPPPort(unsigned short nBaseAddress)
  437. {
  438.   //Write two bytes and read them back. If the
  439.   //reads matches the writes, the port exists
  440.   BOOL bSuccess = FALSE;
  441.   
  442.   //Be sure that control port bit 5 = 0 (Data outputs enabled)
  443.   WriteControl(nBaseAddress, 0xF);
  444.   //Perform the first write  
  445.   WriteData(nBaseAddress, 0x55);
  446.   if (ReadData(nBaseAddress) == 0x55)
  447.   {
  448.     WriteData(nBaseAddress, 0xAA);
  449.     bSuccess = (ReadData(nBaseAddress) == 0xAA);
  450.   }
  451.   return bSuccess;
  452. }
  453. void CParallelPort::WriteControl(unsigned short nBaseAddress, int nData)
  454. {
  455.   //The control port is at offset nBaseAddress + 2.
  456.   //Bits 0, 1 & 3 need to be inverted  
  457.   _outp((unsigned short)(nBaseAddress+2), nData ^ 0xB);
  458. }
  459. int CParallelPort::ReadControl(unsigned short nBaseAddress)
  460. {
  461.   //The control port is at offset nBaseAddress + 2.
  462.   //Bits 0, 1 & 3 need to be inverted  
  463.   return (_inp((unsigned short)(nBaseAddress+2)) ^ 0xB);
  464. }
  465. void CParallelPort::WriteData(unsigned short nBaseAddress, int nData)
  466. {
  467.   //The data port is at offset nBaseAddress.
  468.   _outp(nBaseAddress, nData);
  469. }
  470. int CParallelPort::ReadData(unsigned short nBaseAddress)
  471. {
  472.   //The data port is at offset nBaseAddress.
  473.   return _inp(nBaseAddress);
  474. }
  475. int CParallelPort::ReadStatus(unsigned short nBaseAddress)
  476. {
  477.   //The status port is at offset nBaseAddress + 1.
  478.   //Bit 7 need to be inverted  
  479.   return (_inp((unsigned short)(nBaseAddress+1)) ^ 0x80);
  480. }
  481. void CParallelPort::WriteControl(int nData)
  482. {
  483.   ASSERT(IsOpen()); //Port must be open
  484.   WriteControl(m_nBaseAddress, nData);  
  485. }
  486. int CParallelPort::ReadControl()
  487. {
  488.   ASSERT(IsOpen()); //Port must be open
  489.   return ReadControl(m_nBaseAddress);  
  490. }
  491. void CParallelPort::WriteData(int nData)
  492. {
  493.   ASSERT(IsOpen()); //Port must be open
  494.   WriteData(m_nBaseAddress, nData);  
  495. }
  496. int CParallelPort::ReadData()
  497. {
  498.   ASSERT(IsOpen()); //Port must be open
  499.   return ReadData(m_nBaseAddress);  
  500. }
  501. int CParallelPort::ReadStatus()
  502. {
  503.   ASSERT(IsOpen()); //Port must be open
  504.   return ReadStatus(m_nBaseAddress);  
  505. }
  506. BOOL CParallelPort::ReadByteUsingNibbleMode(BYTE& byData)
  507. {
  508.   ASSERT(IsOpen()); //Port must be open
  509.   //Read a byte of data at the status port, in 2 nibbles
  510.   //When S6 = 0, set D3 to 0
  511.   int S6;
  512.   DWORD dwStartTicks = GetTickCount();
  513.   do
  514.   {
  515.     S6 = (ReadStatus(m_nBaseAddress) & 0x40) >> 6;
  516.     //Check the timeout has not elapsed
  517.     if ((GetTickCount() - dwStartTicks) > m_dwTimeout)
  518.       return FALSE;
  519.   }
  520.   while (S6 == 1);
  521.   WriteData(m_nBaseAddress, 0);
  522.   //When the peripheral responds by setting S6=1, Set D3=1
  523.   //nLowNibble then holds 4 bits of data
  524.   int nLowNibble;
  525.   do
  526.   {
  527.     nLowNibble = ReadStatus(m_nBaseAddress);
  528.     S6 = (nLowNibble & 0x40) >> 6;
  529.     //Check the timeout has not elapsed
  530.     if ((GetTickCount() - dwStartTicks) > m_dwTimeout)
  531.       return FALSE;
  532.   }
  533.   while (S6 == 0);
  534.   WriteData(m_nBaseAddress, 8);
  535.   //When S6 = 0, set D3 to 0
  536.   do
  537.   {
  538.     S6 = (ReadStatus(m_nBaseAddress) & 0x40) >> 6;
  539.     //Check the timeout has not elapsed
  540.     if ((GetTickCount() - dwStartTicks) > m_dwTimeout)
  541.       return FALSE;
  542.   }
  543.   while (S6 == 1);
  544.   WriteData(m_nBaseAddress, 0);
  545.   //When the peripheral responds by setting S6=1, Set D3=1
  546.   //nHighNibble then holds 4 bits of data
  547.   int nHighNibble;
  548.   do
  549.   {
  550.     nHighNibble = ReadStatus(m_nBaseAddress);
  551.     S6 = (nHighNibble & 0x40) >> 6;
  552.     //Check the timeout has not elapsed
  553.     if ((GetTickCount() - dwStartTicks) > m_dwTimeout)
  554.       return FALSE;
  555.   }
  556.   while (S6 == 0);
  557.   WriteData(m_nBaseAddress, 8);
  558.   //Recombine the two nibbles back into the byte
  559.   byData = (BYTE) (((nLowNibble & 0x8) >> 3) +
  560.                    ((nLowNibble & 0x10) >> 3) +
  561.                    ((nLowNibble & 0x20) >> 3) +
  562.                    ((nLowNibble & 0x80) >> 4) +
  563.                    ((nHighNibble & 0x8) << 1) +
  564.                    ((nHighNibble & 0x10) << 1) +
  565.                    ((nHighNibble & 0x20) << 1) +
  566.                    ((nHighNibble & 0x80))); 
  567.   return TRUE;
  568. }
  569. BOOL CParallelPort::WriteByteUsingNibbleMode(BYTE byData)
  570. {
  571.   ASSERT(IsOpen()); //Port must be open
  572.   //Write a byte to the data port, in 2 nibbles.
  573.   //The remote system reads the data at its status port.
  574.   //The data bits are D0, D1, D2 and D4.
  575.   //D3 is the strobe
  576.   int nLowNibble = byData & 0x07;
  577.   nLowNibble |= (byData & 0x08) << 1;
  578.   int nHighNibble = (byData & 0x70) >> 4;
  579.   nHighNibble |= (byData & 0x80) >> 3;
  580.   //When S6=1 (not busy), write the low nibble and set D3=0.
  581.   int S6;
  582.   DWORD dwStartTicks = GetTickCount();  
  583.   do
  584.   {
  585.     S6 = (ReadStatus(m_nBaseAddress) & 0x40) >> 6;
  586.     //Check the timeout has not elapsed
  587.     if ((GetTickCount() - dwStartTicks) > m_dwTimeout)
  588.       return FALSE;
  589.   }
  590.   while (S6 == 0);
  591.   WriteData(m_nBaseAddress, nLowNibble);
  592.   //When the peripheral responds by setting S6=0, Set D3=1.
  593.   do
  594.   {
  595.     S6 = (ReadStatus(m_nBaseAddress) & 0x40) >> 6;
  596.     //Check the timeout has not elapsed
  597.     if ((GetTickCount() - dwStartTicks) > m_dwTimeout)
  598.       return FALSE;
  599.   }
  600.   while (S6 == 1);
  601.   WriteData(m_nBaseAddress, nLowNibble | 0x8);
  602.   //When S6=1, write the high nibble and set D3=0
  603.   do
  604.   {
  605.     S6 = (ReadStatus(m_nBaseAddress) & 0x40) >> 6;
  606.     //Check the timeout has not elapsed
  607.     if ((GetTickCount() - dwStartTicks) > m_dwTimeout)
  608.       return FALSE;
  609.   }
  610.   while (S6 == 0);
  611.   WriteData(m_nBaseAddress, nHighNibble);
  612.   //When the peripheral responds by setting S6=0, Set D3=1.
  613.   do
  614.   {
  615.     S6 = (ReadStatus(m_nBaseAddress) & 0x40) >> 6;
  616.     //Check the timeout has not elapsed
  617.     if ((GetTickCount() - dwStartTicks) > m_dwTimeout)
  618.       return FALSE;
  619.   }
  620.   while (S6 == 1);
  621.   WriteData(m_nBaseAddress, nHighNibble | 0x8);
  622.   return TRUE;
  623. }
  624. BOOL CParallelPort::ReadUsingNibbleMode(void* lpBuf, DWORD dwBytes)
  625. {
  626.   //Validate the parameters
  627.   ASSERT(lpBuf);
  628.   //Read each byte into lpBuf  
  629.   BYTE* lpByteBuf = (BYTE*) lpBuf;
  630.   for (DWORD i=0; i<dwBytes; i++)
  631.   {
  632.     if (!ReadByteUsingNibbleMode(lpByteBuf[i]))
  633.       return FALSE;
  634.   }
  635.   return TRUE;
  636. }
  637. BOOL CParallelPort::WriteUsingNibbleMode(const void* lpBuf, DWORD dwBytes)
  638. {
  639.   //Validate the parameters
  640.   ASSERT(lpBuf);
  641.   //Write each byte from lpBuf  
  642.   const BYTE* lpByteBuf = (const BYTE*) lpBuf;
  643.   for (DWORD i=0; i<dwBytes; i++)
  644.   {
  645.     if (!WriteByteUsingNibbleMode(lpByteBuf[i]))
  646.       return FALSE;
  647.   }
  648.   return TRUE;
  649. }
  650. int CParallelPort::_inp(unsigned short port)
  651. {
  652.   if (sm_bRunningOnNT)
  653.   {
  654.     ASSERT(sm_lpfnDlPortReadUchar);
  655.     return sm_lpfnDlPortReadUchar(port);
  656.   }
  657.   else
  658.     return ::_inp(port);
  659. }
  660. int CParallelPort::_outp(unsigned short port, int databyte)
  661. {
  662.   if (sm_bRunningOnNT)
  663.   {
  664.     ASSERT(sm_lpfnDlPortWriteUchar);
  665.     sm_lpfnDlPortWriteUchar(port, (UCHAR) databyte);
  666.     return databyte;
  667.   }
  668.   else
  669.     return ::_outp(port, databyte);
  670. }
  671. BOOL CParallelPort::InitializeDriverLINX()
  672. {
  673.   //Load up the DriverLINX dll and get the function pointers we are interested in
  674.   ASSERT(sm_hDLINX == NULL);
  675.   BOOL bSuccess = FALSE;
  676.   sm_hDLINX = LoadLibrary(_T("DLPORTIO.DLL"));
  677.   if (sm_hDLINX)
  678.   {
  679.     //Get the function pointers
  680.     sm_lpfnDlPortReadUchar = (LPDLPORTREADPORTUCHAR) GetProcAddress(sm_hDLINX, "DlPortReadPortUchar");
  681.     sm_lpfnDlPortWriteUchar = (LPDLPORTWRITEPORTUCHAR) GetProcAddress(sm_hDLINX, "DlPortWritePortUchar");
  682.     //If any of the functions are not installed then fail the load
  683.     if (sm_lpfnDlPortReadUchar == NULL ||
  684.         sm_lpfnDlPortWriteUchar == NULL)
  685.     {
  686.       TRACE(_T("Failed to get one of the function pointer`s in the DriverLINX dlln"));
  687.       DeInitializeDriverLINX();
  688.     }
  689.     else
  690.       bSuccess = TRUE;
  691.   }
  692.   else
  693.     TRACE(_T("Could not find the load the DriverLINX dll, please ensure DriverLINX driver is installedn"));
  694.   return bSuccess;
  695. }
  696. void CParallelPort::DeInitializeDriverLINX()
  697. {
  698.   if (sm_hDLINX)
  699.   {
  700.     //Unload the dll and reset the function pointers to NULL    
  701.     FreeLibrary(sm_hDLINX);
  702.     sm_hDLINX = NULL;
  703.     sm_lpfnDlPortReadUchar = NULL;
  704.     sm_lpfnDlPortWriteUchar = NULL;
  705.   }
  706. }
  707. BOOL CParallelPort::PortPresent(int nPort)
  708. {
  709.   BOOL bSuccess = FALSE;
  710.   //Try to open the port 
  711.   CString sPort;
  712.   sPort.Format(_T("\\.\LPT%d"), nPort);
  713.   HANDLE hPort = ::CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
  714.   if (hPort != INVALID_HANDLE_VALUE)
  715.   {
  716.     bSuccess = TRUE;
  717.     //Close the port, now that we don't need it anymore
  718.     ::CloseHandle(hPort);
  719.   }
  720.   else
  721.   {
  722.     //Determine if the port exists based on the error code.
  723.     DWORD dwError = GetLastError();
  724.     //Check to see if the error was because some other app had the port open or a general failure
  725.     if (dwError == ERROR_ACCESS_DENIED || dwError == ERROR_GEN_FAILURE)
  726.       bSuccess = TRUE;
  727.   }
  728.   return bSuccess;
  729. }
  730. BOOL CParallelPort::RunningOnNT()
  731. {
  732.   OSVERSIONINFO osvi;
  733.   memset(&osvi, 0, sizeof(OSVERSIONINFO));
  734.   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  735.   VERIFY(GetVersionEx(&osvi));
  736.   return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT);
  737. }
  738. CParallelPortSettings CParallelPort::GetSettings() const
  739. {
  740.   ASSERT(IsOpen()); //Port must be open 
  741.   return sm_Ports.GetAt(m_nPortIndex);
  742. }
  743. ////////// The file transfer class
  744. CParallelPortFileTransfer::CParallelPortFileTransfer(CParallelPort* pPort)
  745. {
  746.   m_pPort = NULL;
  747.   ASSERT(pPort != NULL);
  748.   ASSERT(pPort->IsOpen());
  749.   //Ensure the Port is set to SPP mode if it is an ECP port
  750.   CParallelPortSettings settings = pPort->GetSettings();
  751.   if (settings.m_Type == CParallelPortSettings::ParallelTypeECP)
  752.     pPort->SetECPMode(CParallelPortSettings::ECPModeSPP);
  753.   //Initialize D3 (strobe) to 1
  754.   pPort->WriteData(0x8);
  755.   //Wait for the opposite end to set D3=1 (not busy)
  756.   int S6;
  757.   DWORD dwStartTicks = GetTickCount();
  758.   do
  759.   {
  760.     S6 = (pPort->ReadStatus() & 0x40) >> 6;
  761.     //Check the timeout has not elapsed
  762.     if ((GetTickCount() - dwStartTicks) > pPort->GetTimeout())
  763.     {
  764.       TRACE(_T("Could not setup file transfer, other end busyn"));
  765.       AfxThrowParallelException(ERROR_TIMEOUT);
  766.     }
  767.   }
  768.   while (S6 == 0);
  769.   //Store away the port pointer for use in other class methods
  770.   m_pPort = pPort;
  771. }
  772. CParallelPortFileTransfer::~CParallelPortFileTransfer()
  773. {
  774.   m_pPort = NULL;
  775. }
  776. void CParallelPortFileTransfer::SendFile(const CString& sLocalFile)
  777. {
  778.   ASSERT(m_pPort); //Initialize failed
  779. //For correct operation of the T2A macro, see MFC Tech Note 59
  780. USES_CONVERSION;
  781.   //First try to open the file to send
  782.   CFile file;
  783.   if (file.Open(sLocalFile, CFile::modeRead | CFile::shareDenyWrite))
  784.   {
  785.     //first send the filename (excluding the path) (size if _MAX_PATH)
  786.     TCHAR pszPath[_MAX_PATH];
  787.     TCHAR pszFname[_MAX_FNAME];
  788.     TCHAR pszExt[_MAX_EXT];
  789.     _tsplitpath(sLocalFile, NULL, NULL, pszFname, pszExt);
  790.     _tmakepath(pszPath, NULL, NULL, pszFname, pszExt);
  791.     //Now need to convert the filename to ASCII so that a UNICODE
  792.     //client can take to an ASCII server
  793.     char pszAsciiPath[_MAX_PATH];
  794.     char* pszTemp = T2A(pszPath);
  795.     ZeroMemory(pszAsciiPath, _MAX_PATH);
  796.     strcpy(pszAsciiPath, pszTemp);
  797.     //Do the actual send of the filename
  798.     if (!m_pPort->WriteUsingNibbleMode(pszAsciiPath, _MAX_PATH))
  799.     {
  800.       TRACE(_T("Failed to send the filenamen"));
  801.       AfxThrowParallelException(ERROR_TIMEOUT);
  802.     }
  803.     try
  804.     {
  805.       //Then send the size of the file (as 4 bytes).
  806.       DWORD dwLength = file.GetLength();
  807.       if (!m_pPort->WriteUsingNibbleMode(&dwLength, sizeof(DWORD)))
  808.       {
  809.         TRACE(_T("Failed to send the file lengthn"));
  810.         AfxThrowParallelException(ERROR_TIMEOUT);
  811.       }
  812.       //Then the actual file contents ifself (4k at a time)
  813.       BYTE byData[4096];
  814.       int nBytesRead;
  815.       do
  816.       {
  817.         nBytesRead = file.Read(byData, 4096);
  818.         if (!m_pPort->WriteUsingNibbleMode(byData, nBytesRead))
  819.         {
  820.           TRACE(_T("Failed to send the file contentsn"));
  821.           AfxThrowParallelException(ERROR_TIMEOUT);
  822.         }
  823.       }
  824.       while (nBytesRead);
  825.     }
  826.     catch(CFileException* pEx)
  827.     {
  828.       TRACE(_T("An error occurred reading from the file to sendn"));
  829.       delete pEx;
  830.       AfxThrowParallelException();
  831.     }
  832.   }
  833.   else
  834.   {
  835.     TRACE(_T("Could not open the file to send, %sn"), sLocalFile);
  836.     AfxThrowParallelException();
  837.   }
  838. }
  839. void CParallelPortFileTransfer::ReceiveFile(const CString& sLocalFile)
  840. {
  841. //For correct operation of the T2A macro, see MFC Tech Note 59
  842. USES_CONVERSION;
  843.   //First try to open the file to send
  844.   CFile file;
  845.   if (file.Open(sLocalFile, CFile::modeCreate | CFile::modeWrite | CFile::shareDenyWrite))
  846.   {
  847.     //Receive the filename
  848.     char pszAsciiPath[_MAX_PATH];
  849.     if (!m_pPort->ReadUsingNibbleMode(pszAsciiPath, _MAX_PATH))
  850.     {
  851.       TRACE(_T("Failed to receive the filenamen"));
  852.       //Delete the local file
  853.       file.Close();
  854.       DeleteFile(sLocalFile);
  855.       //Throw the exception
  856.       AfxThrowParallelException(ERROR_TIMEOUT);
  857.     }
  858.     try
  859.     {
  860.       //Receive the size of the file (as 4 bytes).
  861.       DWORD dwLength;
  862.       if (!m_pPort->ReadUsingNibbleMode(&dwLength, sizeof(DWORD)))
  863.       {
  864.         TRACE(_T("Failed to receive the file lengthn"));
  865.   
  866.         //Delete the local file
  867.         file.Close();
  868.         DeleteFile(sLocalFile);
  869.         //Throw the exception
  870.         AfxThrowParallelException(ERROR_TIMEOUT);
  871.       }
  872.       //Then the actual file contents ifself (4k at a time)
  873.       BYTE byData[4096];
  874.       DWORD dwBytesLeft = dwLength;
  875.       do
  876.       {
  877.         DWORD dwBytesToRead;
  878.         if (dwBytesLeft > 4096)
  879.           dwBytesToRead = 4096;
  880.         else
  881.           dwBytesToRead = dwBytesLeft;
  882.           
  883.         if (!m_pPort->ReadUsingNibbleMode(byData, dwBytesToRead))
  884.         {
  885.           TRACE(_T("Failed to receive the file contentsn"));
  886.           //Delete the local file
  887.           file.Close();
  888.           DeleteFile(sLocalFile);
  889.           //Throw the exception
  890.           AfxThrowParallelException(ERROR_TIMEOUT);
  891.         }
  892.         file.Write(byData, dwBytesToRead);
  893.         dwBytesLeft -= dwBytesToRead;
  894.       }
  895.       while (dwBytesLeft);
  896.     }
  897.     catch(CFileException* pEx)
  898.     {
  899.       TRACE(_T("An error occurred writing the file which was being receivedn"));
  900.       delete pEx;
  901.       AfxThrowParallelException();
  902.     }
  903.   }
  904.   else
  905.   {
  906.     TRACE(_T("Could not open the file to receive into, %sn"), sLocalFile);
  907.     AfxThrowParallelException();
  908.   }
  909. }