ParallelPort.cpp
上传用户:angelica
上传日期:2010-03-17
资源大小:17k
文件大小:32k
源码类别:

并口编程

开发平台:

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