VPORT.CPP
Upload User: linklycbj
Upload Date: 2009-11-12
Package Size: 447k
Code Size: 32k
Category:

Windows Develop

Development Platform:

WINDOWS

  1. // Vport.cpp -- Implementation of the CPort class
  2. // Copyright (C) 1996 by Walter Oney
  3. // All rights reserved
  4. #include "stdvxd.h"
  5. #pragma hdrstop
  6. #undef CURSEG
  7. #define CURSEG() LCODE // everything in locked code
  8. #pragma VxD_LOCKED_DATA_SEG // ditto data
  9. CPort* CPort::CPortAnchor;
  10. // The following typedef overcomes a bug in C10 that causes an error 2410
  11. // ambiguous member name) with inline-assembler references to class members
  12. typedef class CPort CPORT;
  13. // Use SetNfyFlag and ClrNfyFlag to change m_NfyFlag bits atomically (since
  14. // flags settings are also changed by the interrupt procedure)
  15. #define SetNfyFlag(mask) { 
  16. _asm mov ecx, this 
  17. _asm or [ecx]CPORT.m_NfyFlags, mask 
  18. }
  19. #define ClrNfyFlag(mask) { 
  20. _asm mov ecx,this 
  21. _asm and word ptr [ecx]CPORT.m_NfyFlags,not mask 
  22. }
  23. ///////////////////////////////////////////////////////////////////////////////
  24. ///////////////////////////////////////////////////////////////////////////////
  25. // ctor & dtor
  26. CPort::CPort(char *name, DWORD iobase, DWORD irq, DEVNODE devnode)
  27. { // CPort::CPort
  28. m_signature = 'TROP';
  29. ASSERT(strlen(name) < sizeof(m_name));
  30. strcpy(m_name, name);
  31. m_iobase = iobase;
  32. m_irq = irq;
  33. m_devnode = devnode;
  34. m_contend = NULL;
  35. m_open = FALSE;
  36. m_owner = NULL;
  37. m_hContend = NULL;
  38. m_EvNotify = NULL;
  39. m_RxNotify = NULL;
  40. m_TxNotify = NULL;
  41. m_next = CPortAnchor;
  42. m_prev = NULL;
  43. if (m_next)
  44. m_next->m_prev = this;
  45. CPortAnchor = this;
  46. } // CPort::CPort
  47. ///////////////////////////////////////////////////////////////////////////////
  48. CPort::~CPort()
  49. { // CPort::~CPort
  50. if (m_prev)
  51. m_prev->m_next = m_next;
  52. else
  53. CPortAnchor = m_next;
  54. if (m_next)
  55. m_next->m_prev = m_prev;
  56. } // CPort::~CPort
  57. ///////////////////////////////////////////////////////////////////////////////
  58. // Static member function:
  59. void CPort::DeleteAll()
  60. { // CPort::DeleteAll
  61. while (CPortAnchor)
  62. delete CPortAnchor;
  63. } // CPort::DeleteAll
  64. ///////////////////////////////////////////////////////////////////////////////
  65. // Some missing VCOMM wrappers:
  66. void VCOMM_Map_Ring0DCB_To_Win32(_DCB* r0dcb, PWIN32DCB r3dcb)
  67. {
  68. _asm mov eax, r0dcb
  69. _asm mov edx, r3dcb
  70. VxDCall(VCOMM_Map_Ring0DCB_To_Win32)
  71. }
  72. void VCOMM_Map_Win32DCB_To_Ring0(PWIN32DCB r3dcb, _DCB* r0dcb)
  73. {
  74. _asm mov eax, r3dcb
  75. _asm mov edx, r0dcb
  76. VxDCall(VCOMM_Map_Win32DCB_To_Ring0)
  77. }
  78. ///////////////////////////////////////////////////////////////////////////////
  79. // C-function thunks to interface between VCOMM and appropriate CPort
  80. // member functions:
  81. #define CPORT(hp) ((CPort *) ((DWORD) hp - FIELDOFFSET(CPort, CPort::m_pd)))
  82. #define PF0(f) BOOL f(PortData* hp) {return CPORT(hp)->f();}
  83. #define PF1(f, a1t) BOOL f(PortData* hp, a1t a1) {return CPORT(hp)->f(a1);}
  84. #define PF2(f, a1t, a2t) BOOL f(PortData* hp, a1t a1, a2t a2) {return CPORT(hp)->f(a1, a2);}
  85. #define PF3(f, a1t, a2t, a3t) BOOL f(PortData* hp, a1t a1, a2t a2, a3t a3) {return CPORT(hp)->f(a1, a2, a3);}
  86. #define PF4(f, a1t, a2t, a3t, a4t) BOOL f(PortData* hp, a1t a1, a2t a2, a3t a3, a4t a4) {return CPORT(hp)->f(a1, a2, a3, a4);}
  87. PF2(ClearError, _COMSTAT*, int*)
  88. PF0(Close)
  89. PF2(EnableNotification, PCOMMNOTIFYPROC, DWORD)
  90. PF3(EscapeFunction, DWORD, DWORD, PVOID)
  91. PF2(GetCommConfig, PCOMMCONFIG, PDWORD)
  92. PF1(GetCommState, _DCB*)
  93. PF2(GetEventMask, DWORD, PDWORD)
  94. PF1(GetProperties, _COMMPROP*)
  95. PF1(GetQueueStatus, _COMSTAT*)
  96. PF1(GetError, int*)
  97. PF1(GetModemStatus, PDWORD)
  98. PF1(Purge, DWORD)
  99. PF3(Read, PCHAR, DWORD, PDWORD)
  100. PF2(SetCommConfig, PCOMMCONFIG, DWORD)
  101. PF2(SetCommState, _DCB*, DWORD)
  102. PF2(SetEventMask, DWORD, PDWORD)
  103. PF1(SetModemStatusShadow, PBYTE)
  104. PF3(SetReadCallback, DWORD, PCOMMNOTIFYPROC, DWORD)
  105. PF4(Setup, PCHAR, DWORD, PCHAR, DWORD)
  106. PF3(SetWriteCallback, DWORD, PCOMMNOTIFYPROC, DWORD)
  107. PF1(TransmitChar, CHAR)
  108. PF3(Write, PCHAR, DWORD, PDWORD)
  109. // Port function table for use during OpenComm:
  110. #define PF (BOOL (*)())
  111. PortFunctions functions =
  112. {PF SetCommState, PF GetCommState, PF Setup, PF TransmitChar,
  113. PF Close, PF GetQueueStatus, PF ClearError, PF SetModemStatusShadow,
  114. PF GetProperties, PF EscapeFunction, PF Purge, PF SetEventMask,
  115. PF GetEventMask, PF Write, PF Read, PF EnableNotification,
  116. PF SetReadCallback, PF SetWriteCallback, PF GetModemStatus,
  117. PF GetCommConfig, PF SetCommConfig, PF GetError, NULL};
  118. ///////////////////////////////////////////////////////////////////////////////
  119. // Port registration and creation logic:
  120. // AddPort calls VCOMM to add the port to its list of ports and initializes
  121. // the contention resolution protocol for the port.
  122. BOOL CPort::AddPort(DWORD refdata)
  123. { // CPort::AddPort
  124. if (!VCOMM_Add_Port(refdata, (PFN) CPort::PreOpen, m_name))
  125. return FALSE;
  126. if ((m_contend = (PCONTENTIONPROC) VCOMM_Get_Contention_Handler(m_name)))
  127. m_resource = VCOMM_Map_Name_To_Resource(m_name);
  128. return TRUE;
  129. } // CPort::AddPort
  130. ///////////////////////////////////////////////////////////////////////////////
  131. // PreOpen is a static member function that VCOMM calls to open a port.
  132. // If successful, we return the address of the PortData structure imbedded
  133. // in the CPort structure. VCOMM uses that address as a port handle, and
  134. // the C-language thunks defined immediately above go backward to find the
  135. // address of the CPort object itself.
  136. PortData* CPort::PreOpen(char *name, HVM hVM, int* pError)
  137. { // CPort::PreOpen
  138. CPort* port;
  139. ASSERT(pError);
  140. for (port = CPortAnchor; port; port = port->m_next)
  141. if (strcmp(name, port->m_name) == 0)
  142. { // try to open port
  143. if (port->m_open)
  144. { // already open
  145. *pError = IE_OPEN;
  146. return NULL;
  147. } // already open
  148. if (port->Open(hVM, pError))
  149. { // opened okay
  150. port->m_open = TRUE;
  151. return &port->m_pd;
  152. } // opened okay
  153. port->Release(); // release if already acquired
  154. return NULL; // return with whatever Open stored in pError
  155. } // try to open port
  156. *pError = IE_HARDWARE; // report lack of hardware
  157. ASSERT(FALSE); // should never get here with unknown port
  158. return NULL; // no such port
  159. } // CPort::PreOpen
  160. ///////////////////////////////////////////////////////////////////////////////
  161. // OnPortStolen is a static member function that contention handlers call
  162. // when they are stealing our port.
  163. BOOL CPort::OnPortStolen(CPort* port, BOOL owned)
  164. { // OnPortStolen
  165. if (owned)
  166. port->m_pd.LossByte &= ~1;
  167. else
  168. port->m_pd.LossByte |= 1;
  169. return TRUE; // okay to steal port
  170. } // OnPortStolen
  171. // The compiler isn't willing to let us use the address of OnPortStolen
  172. // as an argument to the contention function, but we know better:
  173. DWORD pPortStolen = (DWORD) CPort::OnPortStolen;
  174. ///////////////////////////////////////////////////////////////////////////////
  175. // ManageTimer is a static member function that manages the timer for all
  176. // the ports controlled by this driver
  177. HTIMEOUT TimeOutHandle; // timeout event handle
  178. void TimeoutProc(); // forward dcl
  179. void OnTimeout(DWORD extra, DWORD refdata);
  180. void CPort::ManageTimer()
  181. { // CPort::ManageTimer
  182. CPort* port;
  183. for (port = CPortAnchor; port; port = port->m_next)
  184. if (!(port->m_MiscFlags & MF_CLRTIMER) && port->m_RxTrigger != 0xFFFFFFFF)
  185. { // need a timeout for at least one port
  186. if (!TimeOutHandle)
  187. TimeOutHandle = Set_Global_Time_Out((VMM_TIMEOUT_HANDLER) TimeoutProc, 100, 0);
  188. return;
  189. } // need a timeout for at least one port
  190. // No port needs a timeout, so cancel the timeout event if one
  191. // is currently scheduled. For some reason, __Cancel_Time_Out isn't
  192. // defined here, so we can't just do a direct VMMCall.
  193. _asm xor esi, esi
  194. _asm xchg esi, TimeOutHandle // note: atomic operation
  195. #ifdef __Cancel_Time_Out // should be defined by VMM_Service in vmm.h!!
  196. #pragma message("__Cancel_Time_Out is finally #defined at line " __LINE__)
  197. VMMCall(Cancel_Time_Out)
  198. #else
  199. HTIMEOUT temp;
  200. _asm mov temp, esi
  201. Cancel_Time_Out(temp);
  202. #endif
  203. } // CPort::ManageTimer
  204. void __declspec(naked) TimeoutProc()
  205. { // TimeoutProc
  206. _asm
  207. {
  208. xor  eax, eax ; clear event variable
  209. xchg eax, TimeOutHandle
  210. test eax, eax ; was it already cancelled?
  211. jz   goback ; if yes, don't do anything else
  212. push edx ; reference data
  213. push ecx ; extra number of milliseconds
  214. call OnTimeout ; go handle timeout
  215. add  esp, 8 ; lose args
  216. goback:
  217. ret ; return to VMM
  218. }
  219. } // TimeoutProc
  220. // Limitation of inline assembler syntax: no way to call a static
  221. // member function
  222. void OnTimeout(DWORD extra, DWORD refdata)
  223. { // OnTimeout
  224. CPort::OnTimeout(extra, refdata);
  225. } // OnTimeout
  226. void CPort::OnTimeout(DWORD extra, DWORD refdata)
  227. { // CPort::OnTimeout
  228. CPort* port;
  229. for (port = CPortAnchor; port; port = port->m_next)
  230. if (!(port->m_MiscFlags & MF_CLRTIMER)     // timer enabled for port
  231. && port->m_RxTrigger != 0xFFFFFFFF // wants receive notifications
  232. && port->m_pd.QInCount // something in queue
  233. && !(port->m_NfyFlags & CN_RECEIVE)) // hasn't already been sent
  234. { // send notification
  235. BYTE notify; // really send notification?
  236. // The idle flag is zero immediately after the mini-driver
  237. // reads a character. Skip the notification the first time
  238. // the timer goes off because the queue may fill up and we'll
  239. // send a notification for the usual reason that we exceed
  240. // its threshold
  241. _asm
  242. { // check idle flag
  243. mov ebx, port
  244. xor [ebx]CPORT.m_NfyFlags, CN_IDLE ; MUST be 0x80
  245. setns al ; 1 if this isn't 1st timer call
  246. mov notify, al
  247. } // check idle flag
  248. if (notify)
  249. port->CallNotifyProc(CN_RECEIVE);
  250. } // send notification
  251. TimeOutHandle = Set_Global_Time_Out((VMM_TIMEOUT_HANDLER) TimeoutProc, 100, 0);
  252. } // CPort::OnTimeout
  253. ///////////////////////////////////////////////////////////////////////////////
  254. ///////////////////////////////////////////////////////////////////////////////
  255. // Overridable base-class functions
  256. BOOL CPort::Acquire(HVM hVM)
  257. { // CPort::Acquire
  258. ASSERT_VALID_CPORT(this);
  259. ASSERT(!m_hContend && !m_owner);
  260. if (m_contend)
  261. { // have contention procedure
  262. if (!m_resource)
  263. return FALSE; // but no resource handle?
  264. m_hContend = (*m_contend)(ACQUIRE_RESOURCE, m_resource,
  265. pPortStolen, this, TRUE); // try to steal if owned
  266. if (m_hContend)
  267. { // we've got it
  268. m_owner = hVM;
  269. return TRUE;
  270. } // we've got it
  271. return FALSE;
  272. } // have contention procedure
  273. m_owner = hVM;
  274. return TRUE;
  275. } // CPort::Acquire
  276. ///////////////////////////////////////////////////////////////////////////////
  277. void CPort::CallNotifyProc(int code)
  278. { // CPort::CallNotifyProc
  279. ASSERT_VALID_CPORT(this);
  280. ASSERT(!(code & 0xFF00));
  281. // VCOMM incorrectly destroys EBX during a notification callback. Therefore,
  282. // use EBX for the "this" reference in the next little bit of code so
  283. // that we'll save and restore it
  284. _asm mov ebx, this
  285. _asm mov al, byte ptr code
  286. _asm or [ebx]CPORT.m_NfyFlags, al ; merge to get sent notifications flag
  287. DWORD events = *m_pEvent;
  288. // Call the ring-0 notification procedure appropriate to the event code.
  289. // Note that VCOMM maintains equivalent client pointers in the PortData
  290. // structure (lpClientEventNotify, etc.). We're not supposed to touch
  291. // those!
  292. switch (code)
  293. { // select which notify proc to call
  294. case CN_EVENT:
  295. ASSERT(m_EvNotify);
  296. (*m_EvNotify)(&m_pd, m_EvData, code, events);
  297. break;
  298. case CN_RECEIVE:
  299. ASSERT(m_RxNotify);
  300. (*m_RxNotify)(&m_pd, m_RxData, code, events);
  301. break;
  302. case CN_TRANSMIT:
  303. ASSERT(m_TxNotify);
  304. (*m_TxNotify)(&m_pd, m_TxData, code, events);
  305. break;
  306. default:
  307. ASSERT(FALSE); // unknown notification code
  308. } // select which notify proc to call
  309. } // CPort::CallNotifyProc
  310. ///////////////////////////////////////////////////////////////////////////////
  311. BOOL CPort::ClearError(_COMSTAT* pComstat, int* pError)
  312. { // CPort::ClearError
  313. ASSERT_VALID_CPORT(this);
  314. if (pComstat)
  315. GetQueueStatus(pComstat); // should not be able to fail
  316. ASSERT(pError);
  317. *pError = (int) m_pd.dwCommError;
  318. m_pd.dwCommError = 0;
  319. m_pd.dwLastError = 0;
  320. return TRUE;
  321. } // CPort::ClearError
  322. ///////////////////////////////////////////////////////////////////////////////
  323. BOOL CPort::Close()
  324. { // CPort::Close
  325. ASSERT_VALID_CPORT(this);
  326. m_pd.dwLastError = 0; // assume no problems
  327. if (!m_open)
  328. return TRUE; // isn't open in the first place
  329. if (!trmcom())
  330. return FALSE; // mini-driver couldn't close
  331. Release(); // release from contention manager
  332. m_open = FALSE;
  333. if (m_MiscFlags & MF_RXQINTERNAL)
  334. { // release Rx buffer
  335. ASSERT(m_pd.QInAddr);
  336. _HeapFree((PVOID) m_pd.QInAddr, 0);
  337. m_pd.QInAddr = NULL;
  338. m_MiscFlags &= ~MF_RXQINTERNAL;
  339. } // release Rx buffer
  340. if (m_MiscFlags & MF_TXQINTERNAL)
  341. { // release Tx buffer
  342. ASSERT(m_pd.QOutAddr);
  343. _HeapFree((PVOID) m_pd.QOutAddr, 0);
  344. m_pd.QOutAddr = NULL;
  345. m_MiscFlags &= ~MF_TXQINTERNAL;
  346. } // release Tx buffer
  347. m_MiscFlags |= MF_CLRTIMER;
  348. ManageTimer();
  349. return TRUE;
  350. } // CPort::Close
  351. ///////////////////////////////////////////////////////////////////////////////
  352. BOOL CPort::EnableNotification(PCOMMNOTIFYPROC pCallback, DWORD refdata)
  353. { // CPort::EnableNotification
  354. ASSERT_VALID_CPORT(this);
  355. ClrNfyFlag(CN_NOTIFY); // prevent any notifications during changeover
  356. m_EvNotify = pCallback;
  357. m_EvData = refdata;
  358. if (pCallback)
  359. { // wants notifications
  360. ASSERT(m_pEvent);
  361. // Prevent possible race with people who discover events and
  362. // try to notify about them so we don't notify twice
  363. _asm pushfd
  364. _asm cli
  365. m_NfyFlags |= CN_NOTIFY;
  366. DWORD pending = m_eventmask & *m_pEvent;
  367. _asm popfd
  368. if (pending)
  369. CallNotifyProc(CN_EVENT);
  370. } // wants notifications
  371. m_pd.dwLastError = 0;
  372. return TRUE;
  373. } // CPort::EnableNotification
  374. ///////////////////////////////////////////////////////////////////////////////
  375. BOOL CPort::EscapeFunction(DWORD lFunc, DWORD InData, PVOID pOutData)
  376. { // CPort::EscapeFunction
  377. ASSERT_VALID_CPORT(this);
  378. m_pd.dwLastError = 0;
  379. switch (lFunc)
  380. { // process escape function
  381. case PEEKCHAR: // lFunc == 200
  382. if (!m_pd.QInCount)
  383. return FALSE; // no pending input character
  384. ASSERT(pOutData);
  385. *(PBYTE) pOutData = ((char *) m_pd.QInAddr)[m_pd.QInGet];
  386. break;
  387. case ENABLETIMERLOGIC: // lFunc == 21
  388. m_MiscFlags &= ~MF_CLRTIMER;
  389. break;
  390. case IGNOREERRORONREADS: // lFunc == 20
  391. m_MiscFlags |= MF_IGNORECOMMERROR;
  392. break;
  393. case CLRTIMERLOGIC: // lFunc == 16
  394. m_MiscFlags |= MF_CLRTIMER;
  395. break;
  396. case SETUPDATETIMEADDR: // lFunc == 19
  397. m_pRxTime = (PDWORD) InData;
  398. break;
  399. default:
  400. if (cextfcn(lFunc, InData, pOutData))
  401. break; // mini-driver handled it
  402. m_pd.dwLastError = (DWORD) IE_EXTINVALID;
  403. return FALSE; // unknown escape
  404. } // process escape function
  405. return TRUE;
  406. } // CPort::EscapeFunction
  407. ///////////////////////////////////////////////////////////////////////////////
  408. BOOL CPort::GetCommConfig(PCOMMCONFIG lpCC, PDWORD lpSize)
  409. { // CPort::GetCommConfig
  410. ASSERT_VALID_CPORT(this);
  411. ASSERT(lpSize);
  412. DWORD size = *lpSize;
  413. *lpSize = sizeof(COMMCONFIG);
  414. if (size < sizeof(COMMCONFIG) || !lpCC)
  415. return TRUE; // this is how SERIAL behaves, but it seems wrong
  416. lpCC->dwProviderOffset = 0;
  417. lpCC->dwProviderSize = 0;
  418. lpCC->dwSize = sizeof(COMMCONFIG);
  419. lpCC->wVersion = 0x0100;
  420. lpCC->dwProviderSubType = GetProviderSubType();
  421. VCOMM_Map_Ring0DCB_To_Win32(&m_dcb, &lpCC->dcb);
  422. return TRUE;
  423. } // CPort::GetCommConfig
  424. ///////////////////////////////////////////////////////////////////////////////
  425. BOOL CPort::GetCommState(_DCB* pDCB)
  426. { // CPort::GetCommState
  427. ASSERT_VALID_CPORT(this);
  428. *pDCB = m_dcb;
  429. m_pd.dwLastError = 0;
  430. return TRUE;
  431. } // CPort::GetCommState
  432. ///////////////////////////////////////////////////////////////////////////////
  433. BOOL CPort::GetEventMask(DWORD mask, PDWORD pEvents)
  434. { // CPort::GetEventMask
  435. ASSERT_VALID_CPORT(this);
  436. ASSERT(pEvents);
  437. _asm pushfd
  438. _asm cli
  439. *pEvents = *m_pEvent; // return all current events
  440. *m_pEvent &= ~mask; // clear selected events
  441. _asm popfd
  442. m_pd.dwLastError = 0;
  443. return TRUE;
  444. } // CPort::GetEventMask
  445. ///////////////////////////////////////////////////////////////////////////////
  446. // Mini-driver should override and call base-class first, then fill
  447. // in device specific parts of the structure
  448. BOOL CPort::GetProperties(_COMMPROP* pCommProp)
  449. { // CPort::GetProperties
  450. ASSERT_VALID_CPORT(this);
  451. memset(pCommProp, 0, sizeof(_COMMPROP));
  452. pCommProp->wPacketLength = sizeof(_COMMPROP);
  453. pCommProp->wPacketVersion = 2;
  454. pCommProp->dwServiceMask = SP_SERIALCOMM;
  455. pCommProp->dwCurrentRxQueue = m_pd.QInSize;
  456. pCommProp->dwCurrentTxQueue = m_pd.QOutSize;
  457. m_pd.dwLastError = 0;
  458. return TRUE;
  459. } // CPort::GetProperties
  460. ///////////////////////////////////////////////////////////////////////////////
  461. // Mini-driver should override, fill in BitMask, then call base-class fcn
  462. BOOL CPort::GetQueueStatus(_COMSTAT* pComstat)
  463. { // CPort::GetQueueStatus
  464. ASSERT_VALID_CPORT(this);
  465. m_pd.dwLastError = 0;
  466. pComstat->cbInque = m_pd.QInCount;
  467. pComstat->cbOutque = m_pd.QOutCount;
  468. return TRUE;
  469. } // CPort::GetQueueStatus
  470. ///////////////////////////////////////////////////////////////////////////////
  471. BOOL CPort::GetError(int* pError)
  472. { // CPort::GetError
  473. ASSERT_VALID_CPORT(this);
  474. ASSERT(pError);
  475. *pError = m_pd.dwLastError;
  476. return TRUE;
  477. } // CPort::GetError
  478. ///////////////////////////////////////////////////////////////////////////////
  479. // Mini-driver should replace the base class fcn
  480. BOOL CPort::GetModemStatus(PDWORD pModemStatus)
  481. { // CPort::GetModemStatus
  482. ASSERT_VALID_CPORT(this);
  483. ASSERT(pModemStatus);
  484. *pModemStatus = *m_pMsrShadow & MS_Modem_Status;
  485. return TRUE;
  486. } // CPort::GetModemStatus
  487. ///////////////////////////////////////////////////////////////////////////////
  488. BOOL CPort::Open(HVM hVM, int* pError)
  489. { // CPort::Open
  490. ASSERT_VALID_CPORT(this);
  491. ASSERT(!m_open);
  492. memset(&m_pd, 0, sizeof(m_pd)); // do this before VCD can muck with LossByte
  493. memset(&m_dcb, 0, sizeof(m_dcb));
  494. if (!Acquire(hVM))
  495. { // physical port is in use
  496. *pError = IE_OPEN;
  497. return FALSE;
  498. } // physical port is in use
  499. m_pd.PDLength = sizeof(m_pd);
  500. m_pd.PDVersion = 0x010A; // version 1.1 of PortData structure
  501. m_pd.PDfunctions = &functions;
  502. m_pd.PDNumFunctions = sizeof(functions) / sizeof(BOOL (*)());
  503. m_pd.dwCommError = 0;
  504. m_dcb.XonLim = 0xFFFFFFFF;
  505. m_dcb.XoffLim = 0xFFFFFFFF;
  506. m_dcb.BitMask = fBinary;
  507. m_dcb.XonChar = 0x11; // Ctrl+Q
  508. m_dcb.XoffChar = 0x13; // Ctrl+S
  509. m_pRxTime = &m_pd.dwLastReceiveTime;
  510. m_pEvent = &m_pd.dwDetectedEvents;
  511. m_eventmask = 0;
  512. m_pMsrShadow = &m_pd.bMSRShadow;
  513. m_RxTrigger = 0xFFFFFFFF;
  514. m_TxTrigger = 0;
  515. m_NfyFlags = 0;
  516. m_MiscFlags = 0;
  517. return inicom(pError);
  518. } // CPort::Open
  519. ///////////////////////////////////////////////////////////////////////////////
  520. BOOL CPort::Purge(DWORD qType)
  521. { // CPort::Purge
  522. ASSERT_VALID_CPORT(this);
  523. switch (qType)
  524. { // purge requested queue
  525. case 0: // Tx queue
  526. { // flush Tx queue
  527. DWORD count = m_pd.QOutCount;
  528. _asm pushfd
  529. _asm cli
  530. m_pd.QOutCount = 0;
  531. m_pd.QOutGet = 0;
  532. m_pd.QOutPut = 0;
  533. _asm popfd
  534. if (count && m_TxTrigger)
  535. CallNotifyProc(CN_TRANSMIT); // we're below Tx trigger now
  536. Flush(qType);
  537. break;
  538. } // flush Tx queue
  539. case 1: // Rx queue
  540. _asm pushfd
  541. _asm cli
  542. m_pd.QInCount = 0;
  543. m_pd.QInGet = 0;
  544. m_pd.QInPut = 0;
  545. _asm popfd
  546. Flush(qType);
  547. ClrNfyFlag(CN_RECEIVE);
  548. break;
  549. default:
  550. ASSERT(FALSE); // incorrect queue type
  551. break;
  552. } // purge requested queue
  553. m_pd.dwLastError = 0;
  554. return TRUE;
  555. } // CPort::Purge
  556. ///////////////////////////////////////////////////////////////////////////////
  557. // Mini-driver should override, call base-class fcn, and then check for
  558. // needing to X-on the transmitter, etc.
  559. BOOL CPort::Read(PCHAR buf, DWORD cbRequest, PDWORD pRxCount)
  560. { // CPort::Read
  561. ASSERT_VALID_CPORT(this);
  562. ASSERT(pRxCount);
  563. if (!(m_MiscFlags & MF_IGNORECOMMERROR) && m_pd.dwCommError)
  564. { // pending errors, so stop
  565. m_pd.dwLastError = m_pd.dwCommError;
  566. return FALSE;
  567. } // pending errors, so stop
  568. m_pd.dwLastError = 0;
  569. DWORD numread = m_pd.QInCount; // we'll copy this many bytes
  570. if (!numread)
  571. { // quick out if queue empty
  572. *pRxCount = 0;
  573. return TRUE; // it's success because there's no error
  574. } // quick out if queue empty
  575. if (numread > cbRequest)
  576. numread = cbRequest; // (unless that's more than caller wants)
  577. DWORD get = m_pd.QInGet; 
  578. DWORD ncopy = m_pd.QInSize - get; // # avail until end of buffer
  579. if (ncopy > numread)
  580. ncopy = numread; // won't need 2d copy
  581. memcpy(buf, (PCHAR) m_pd.QInAddr + get, ncopy);
  582. if (ncopy == numread)
  583. get += ncopy; // we'll advance QInGet to here
  584. else
  585. { // wraparound to start of buffer
  586. buf += ncopy;
  587. ncopy = numread - ncopy;
  588. memcpy(buf, (PCHAR) m_pd.QInAddr, ncopy); // copy from start of buffer
  589. get = ncopy;
  590. } // wraparound to start of buffer
  591. // Changing the QInCount and altering the notify flag mask
  592. // needs to be protected against simultaneous access by
  593. // our interrupt handler. Note that interrupt handler can only
  594. // increase QInCount and never touches QInGet.
  595. ASSERT(get <= m_pd.QInSize);
  596. ASSERT(m_pd.QInCount >= numread);
  597. m_pd.QInGet = get;
  598. _asm pushfd
  599. _asm cli
  600. m_pd.QInCount -= numread;
  601. m_NfyFlags &= ~CN_RECEIVE; // allow notifications again
  602. _asm popfd
  603. *pRxCount = numread;
  604. return TRUE;
  605. } // CPort::Read
  606. ///////////////////////////////////////////////////////////////////////////////
  607. void CPort::Release()
  608. { // CPort::Release
  609. ASSERT_VALID_CPORT(this);
  610. m_owner = NULL;
  611. if (m_hContend)
  612. { // release port
  613. ASSERT(m_contend);
  614. (*m_contend)(RELEASE_RESOURCE, m_hContend, pPortStolen);
  615. m_hContend = NULL;
  616. } // release port
  617. } // CPort::Release
  618. ///////////////////////////////////////////////////////////////////////////////
  619. BOOL CPort::SetCommConfig(PCOMMCONFIG lpCC, DWORD dwSize)
  620. { // CPort::SetCommConfig
  621. ASSERT_VALID_CPORT(this);
  622. if (dwSize < sizeof(COMMCONFIG))
  623. { // too small
  624. m_pd.dwLastError = (DWORD) IE_INVALIDPARAM;
  625. return FALSE;
  626. } // too small
  627. _DCB r0dcb;
  628. memset(&r0dcb, 0, sizeof(r0dcb)); // some fields won't get initialized!
  629. VCOMM_Map_Win32DCB_To_Ring0(&lpCC->dcb, &r0dcb);
  630. return SetCommState(&r0dcb, 0xFFFFFFFF);
  631. } // CPort::SetCommConfig
  632. ///////////////////////////////////////////////////////////////////////////////
  633. BOOL CPort::SetCommState(_DCB* pDCB, DWORD ActionMask)
  634. { // CPort::SetCommState
  635. ASSERT_VALID_CPORT(this);
  636. if ((m_pd.LossByte & 1) && !StealPort())
  637. { // port stolen
  638. m_pd.dwLastError = (DWORD) IE_DEFAULT;
  639. return FALSE;
  640. } // port stolen
  641. if (!CheckState(pDCB, ActionMask))
  642. return FALSE; // error in mini-port specific parts
  643. BeginSetState(); // prepare to change state of port
  644. DWORD ChangedMask = 0; // assume nothing changed yet
  645. #define ss(m) if (ActionMask & f##m) { 
  646. if (m_dcb.m != pDCB->m) ChangedMask |= f##m; 
  647. m_dcb.m = pDCB->m;}
  648. ss(BaudRate)
  649. ss(BitMask)
  650. ss(XonLim)
  651. ss(XoffLim)
  652. ss(ByteSize)
  653. ss(Parity)
  654. ss(StopBits)
  655. ss(XonChar)
  656. ss(XoffChar)
  657. ss(ErrorChar)
  658. ss(EofChar)
  659. ss(EvtChar1)
  660. ss(EvtChar2)
  661. ss(RlsTimeout)
  662. ss(CtsTimeout)
  663. ss(DsrTimeout)
  664. ss(TxDelay)
  665. EndSetState(ChangedMask); // install new parameters
  666. m_MiscFlags |= MF_STATESETONCE;
  667. m_pd.dwLastError = 0;
  668. return TRUE;
  669. } // CPort::SetCommState
  670. ///////////////////////////////////////////////////////////////////////////////
  671. BOOL CPort::SetEventMask(DWORD mask, PDWORD pEvents)
  672. { // CPort::SetEventMask
  673. ASSERT_VALID_CPORT(this);
  674. m_eventmask = mask;
  675. // Calling with NULL event word ptr allows caller to change the mask.
  676. if (pEvents)
  677. m_pEvent = pEvents;
  678. m_pd.dwLastError = 0;
  679. return TRUE;
  680. } // CPort::SetEventMask
  681. ///////////////////////////////////////////////////////////////////////////////
  682. BOOL CPort::SetModemStatusShadow(PBYTE pShadow)
  683. { // CPort::SetModemStatusShadow
  684. ASSERT_VALID_CPORT(this);
  685. ASSERT(pShadow);
  686. m_pMsrShadow = pShadow;
  687. m_pd.dwLastError = 0;
  688. return TRUE;
  689. } // CPort::SetModemStatusShadow
  690. ///////////////////////////////////////////////////////////////////////////////
  691. BOOL CPort::SetReadCallback(DWORD RxTrigger, PCOMMNOTIFYPROC pCallback, DWORD refdata)
  692. { // CPort::SetReadCallback
  693. ASSERT_VALID_CPORT(this);
  694. if (RxTrigger != 0xFFFFFFFF && RxTrigger > m_pd.QInSize)
  695. RxTrigger = m_pd.QInSize; // make it sensible
  696. if (!pCallback)
  697. RxTrigger = 0xFFFFFFFF; // reset if no callback fcn
  698. // Prevent race while changing over to new notification parameters
  699. _asm pushfd
  700. _asm cli
  701. m_RxNotify = pCallback;
  702. m_RxData = refdata;
  703. m_RxTrigger = RxTrigger;
  704. _asm popfd
  705. if (!m_pd.QInCount)
  706. *m_pRxTime = 0;
  707. ManageTimer();
  708. return TRUE;
  709. } // CPort::SetReadCallback
  710. ///////////////////////////////////////////////////////////////////////////////
  711. // This implementation of Setup mimics what SERIAL.VXD does. This works
  712. // correctly if called zero or more times for external buffers followed
  713. // by zero or more times for internal buffers. It leaks memory if called
  714. // to assign an external buffer if an internal buffer has already been
  715. // assigned.
  716. BOOL CPort::Setup(PCHAR RxQueue, DWORD RxLength, PCHAR TxQueue, DWORD TxLength)
  717. { // CPort::Setup
  718. ASSERT_VALID_CPORT(this);
  719. m_pd.dwLastError = 0; // no error
  720. m_pd.QInCount = 0;
  721. m_pd.QInGet = 0;
  722. m_pd.QInPut = 0;
  723. m_pd.QOutCount = 0;
  724. m_pd.QOutGet = 0;
  725. m_pd.QOutPut = 0;
  726. if (!RxQueue)
  727. { // need internal buffer
  728. ASSERT(RxLength);
  729. if (m_MiscFlags & MF_RXQINTERNAL)
  730. { // reallocate existing buffer
  731. RxQueue = (PCHAR) _HeapReAllocate((PVOID) m_pd.QInAddr, RxLength, 0);
  732. if (!RxQueue)
  733. return FALSE;
  734. } // reallocate existing buffer
  735. else
  736. { // allocate buffer 1st time
  737. RxQueue = (PCHAR) _HeapAllocate(RxLength, 0);
  738. if (!RxQueue)
  739. return FALSE; // means no change made
  740. m_MiscFlags |= MF_RXQINTERNAL;
  741. } // allocate buffer 1st time
  742. } // need internal buffer
  743. else
  744. { // using external buffer
  745. ASSERT(!(m_MiscFlags & MF_RXQINTERNAL)); // SERIAL doesn't test this case (BUG?)
  746. } // using external buffer
  747. m_pd.QInAddr = (DWORD) RxQueue;
  748. m_pd.QInSize = RxLength;
  749. if (!TxQueue)
  750. if (TxLength)
  751. { // need internal buffer
  752. if (m_MiscFlags & MF_TXQINTERNAL)
  753. { // reallocate existing buffer
  754. TxQueue = (PCHAR) _HeapReAllocate((PVOID) m_pd.QOutAddr, TxLength, 0);
  755. if (!TxQueue)
  756. return FALSE;
  757. } // reallocate existing buffer
  758. else
  759. { // allocate buffer 1st time
  760. TxQueue = (PCHAR) _HeapAllocate(TxLength, 0);
  761. if (!TxQueue)
  762. { // can't allocate
  763. if (m_MiscFlags & MF_RXQINTERNAL)
  764. { // release internal buffer
  765. _HeapFree((PVOID) m_pd.QInAddr, 0);
  766. m_MiscFlags &= ~MF_RXQINTERNAL;
  767. } // release internal buffer
  768. return FALSE;
  769. } // can't allocate
  770. m_MiscFlags |= MF_TXQINTERNAL;
  771. } // allocate buffer 1st time
  772. } // need internal buffer
  773. else
  774. { // no Tx buffer
  775. ASSERT(!(m_MiscFlags & MF_TXQINTERNAL));
  776. } // no Tx buffer
  777. m_pd.QOutAddr = (DWORD) TxQueue;
  778. m_pd.QOutSize = TxLength;
  779. if (TxQueue)
  780. m_MiscFlags |= MF_TXQSET;
  781. return TRUE;
  782. } // CPort::Setup
  783. ///////////////////////////////////////////////////////////////////////////////
  784. BOOL CPort::SetWriteCallback(DWORD TxTrigger, PCOMMNOTIFYPROC pCallback, DWORD refdata)
  785. { // CPort::SetWriteCallback
  786. ASSERT_VALID_CPORT(this);
  787. if (TxTrigger == 0xFFFFFFFF)
  788. TxTrigger = 0; // allow -1 to mean "no trigger"
  789. if ((m_MiscFlags & MF_TXQSET) && TxTrigger > m_pd.QOutSize)
  790. TxTrigger = m_pd.QOutSize;
  791. if (!pCallback)
  792. TxTrigger = 0;
  793. // Prevent race while changing over to new notification parameters
  794. _asm pushfd
  795. _asm cli
  796. m_TxNotify = pCallback;
  797. m_TxData = refdata;
  798. m_TxTrigger = TxTrigger;
  799. if (m_pd.QOutCount < TxTrigger)
  800. m_NfyFlags |= CN_TRANSMIT; // no notification till we exceed trigger
  801. _asm popfd
  802. return TRUE;
  803. } // CPort::SetWriteCallback
  804. ///////////////////////////////////////////////////////////////////////////////
  805. BOOL CPort::StealPort()
  806. { // CPort::StealPort
  807. ASSERT_VALID_CPORT(this);
  808. if (!(m_pd.LossByte & 1))
  809. return TRUE; // we never lost it
  810. ASSERT(m_contend && m_hContend);
  811. if ((*m_contend)(STEAL_RESOURCE, m_hContend, pPortStolen))
  812. { // stole it back
  813. m_pd.LossByte &= ~1;
  814. return TRUE;
  815. } // stole it back
  816. return FALSE;
  817. } // CPort::StealPort
  818. ///////////////////////////////////////////////////////////////////////////////
  819. // Mini-driver should completely replace base-class fcn
  820. BOOL CPort::TransmitChar(CHAR ch)
  821. { // CPort::TransmitChar
  822. ASSERT_VALID_CPORT(this);
  823. return FALSE;
  824. } // CPort::TransmitChar
  825. ///////////////////////////////////////////////////////////////////////////////
  826. BOOL CPort::Write(PCHAR buf, DWORD cbRequest, PDWORD pTxCount)
  827. { // CPort::Write
  828. ASSERT_VALID_CPORT(this);
  829. ASSERT(pTxCount);
  830. DWORD nwritten;
  831. m_pd.dwLastError = 0;
  832. if (m_MiscFlags & MF_TXQSET)
  833. { // using an output buffer
  834. nwritten = m_pd.QOutSize - m_pd.QOutCount; // maximum we can transfer
  835. if (nwritten > cbRequest)
  836. nwritten = cbRequest; // don't send more than we've been given
  837. DWORD put = m_pd.QOutPut; // where we can start copying to
  838. DWORD ncopy = m_pd.QOutSize - put; // most we can copy now
  839. if (ncopy > nwritten)
  840. ncopy = nwritten; // but not more than we've got room for
  841. memcpy((PCHAR) m_pd.QOutAddr + put, buf, ncopy);
  842. if (ncopy == nwritten)
  843. put += ncopy; // only need 1 copy
  844. else
  845. { // wraparound to start of buffer
  846. buf += ncopy;
  847. ncopy = nwritten - ncopy; // amount left to do
  848. memcpy((PCHAR) m_pd.QOutAddr, buf, ncopy);
  849. put = ncopy;
  850. } // wraparound to start of buffer
  851. // Protect update of buffering parameters against interference
  852. // by our interrupt procedure
  853. m_pd.QOutPut = put;
  854. _asm pushfd
  855. _asm cli
  856. if ((m_pd.QOutCount += nwritten) >= m_TxTrigger)
  857. m_NfyFlags &= ~CN_TRANSMIT; // start checking trigger now
  858. _asm popfd
  859. } // using an output buffer
  860. else
  861. { // no output buffer setup
  862. // We're not using an output buffer, so redirect the buffering
  863. // parameters to the caller's buffer. This needs to be protected
  864. // against access by the interrupt routine until we're done.
  865. _asm pushfd
  866. _asm cli
  867. m_pd.QOutAddr = (DWORD) buf;
  868. m_pd.QOutSize = cbRequest;
  869. m_pd.QOutCount = cbRequest;
  870. m_pd.QOutPut = 0;
  871. m_pd.QOutGet = 0;
  872. if (cbRequest >= m_TxTrigger)
  873. m_NfyFlags &= ~CN_TRANSMIT; // start checking trigger now
  874. _asm popfd
  875. nwritten = cbRequest; // pretend all of it written
  876. } // no output buffer setup
  877. KickTx(); // try to restart output
  878. if (nwritten < cbRequest)
  879. m_pd.dwCommError |= CE_TXFULL; // buffer became full
  880. *pTxCount = nwritten;
  881. return TRUE;
  882. } // CPort::Write
  883. ///////////////////////////////////////////////////////////////////////////////
  884. // Defaults for mini-port callouts:
  885. BOOL CPort::cextfcn(DWORD lFunc, DWORD InData, PVOID pOutData){return FALSE;} // part of EscapeFunction
  886. BOOL CPort::inicom(int* pError){return TRUE;} // part of Open
  887. BOOL CPort::trmcom(){return TRUE;} // part of Close
  888. void CPort::BeginSetState(){}
  889. void CPort::EndSetState(DWORD ChangedMask){}
  890. void CPort::Flush(DWORD qType){} // part of Purge
  891. BOOL CPort::CheckState(_DCB* pDCB, DWORD ActionMask){return TRUE;}
  892. DWORD CPort::GetProviderSubType(){return 0;} // for filling in COMMCONFIG
  893. void CPort::KickTx(){}