Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1075 lines
37 KiB

  1. #include "precomp.h"
  2. /* tprtintf.cpp
  3. *
  4. * Copyright (c) 1996 by Microsoft Corporation
  5. *
  6. * Abstract:
  7. * This is the implementation file for the Win32 PSTN transport stack.
  8. * This file contains all of the public functions needed to use
  9. * the PSTN stack. Transprt.cpp really performs three functions:
  10. *
  11. * 1. Since this is a Win32 DLL, it provides a C to C++ conversion
  12. * layer. The functions called by the user are not processed
  13. * in this file, they are passed on down to the C++ object that
  14. * handles it (g_pController). If we supplied a C++
  15. * interface to the user, it would force the user to use C++ and
  16. * it would force them to use a C++ compiler that used the same
  17. * name-mangling algorithm as our compiler(s).
  18. *
  19. * 2. It holds the code that acts as the administrator for the PSTN
  20. * thread. This is a fairly straightforward piece of code that
  21. * is explained later in this comment block.
  22. *
  23. * 3. This file provides thread synchronization to the underlying
  24. * code by protecting it with a CRITICAL_SECTION. This prevents
  25. * multiple threads from executing the code at the same time.
  26. *
  27. *
  28. *
  29. * CREATING A SECONDARY "PSTN" THREAD
  30. *
  31. * Since this is a Win32 DLL, we create a seperate thread to maintain the
  32. * PSTN DLL. During initialization of the DLL, we create the secondary or
  33. * PSTN thread which runs in response to timeouts and system events. It
  34. * uses the WaitForMultipleObjects() system call to wait for events that
  35. * need to be processed.
  36. *
  37. * This DLL is event driven. When a new com port is opened and
  38. * initialized, it uses Win32 event objects to signal significant events.
  39. * Three events that can occur are read events, write events, and control
  40. * events. An example of a control event is a change in the carrier
  41. * detect signal.
  42. *
  43. * When the ComPort object initializes an event object, it registers the
  44. * object with the PSTN thread, by placing it in the PSTN Event_List. It
  45. * not only registers the event, but also all of the data needed to
  46. * identify the event. The following structure must be filled out by the
  47. * ComPort for each event that it registers.
  48. * struct
  49. * {
  50. * HANDLE event; // event object
  51. * WIN32Event event_type; // READ, WRITE, CONTROL
  52. * BOOL delete_event; // FLAG, delete it?
  53. * PhysicalHandle physical_handle; // Handle associated with
  54. * // ComPort
  55. * IObject * physical_layer; // this pointer of comport
  56. * } EventObject;
  57. *
  58. * When an event occurs, the PSTN thread makes a call directly to the
  59. * ComPort object to notify it. If the event that occured was a WRITE
  60. * event, we also issue a PollTransmitter() function call to the
  61. * g_pController object so that it can transmit more data.
  62. *
  63. * If a READ event occurs, we call the ComPort object directly to notify
  64. * it that the event occurred. We then issue a PollReceiver() function
  65. * call to the g_pController object so that we can process the
  66. * received data. We then issue a PollTransmitter() to the
  67. * g_pController object. Logically, this doesn't make much sense,
  68. * but if you study the Q922 code, you'll see that we sometimes don't
  69. * transmit data until we receive notification that previously transmitted
  70. * data was successfully transmitted.
  71. *
  72. * If a CONTROL event occurs, we call the ComPort object directly to
  73. * notify it that the event occurred. We then issue a PollReceiver()
  74. * function call to the g_pController object so that the CONTROL
  75. * event will be processed.
  76. *
  77. *
  78. *
  79. * X.214 INTERFACE
  80. *
  81. * You will notice that many of the entry points to this DLL were taken
  82. * from the X.214 service definition. These entry points are consistent
  83. * between all DataBeam Transport DLLs. This gives the user application
  84. * a consistent interface.
  85. *
  86. *
  87. * PHYSICAL API
  88. *
  89. * Some of the entry points to this DLL are specific to the creation
  90. * and use of LOGICAL connections, and some of the entry points are
  91. * specific to the creation and deletion of PHYSICAL connections. The
  92. * out-of-band physical API is new as of version 2.0 and provides the
  93. * user with an optional way of making PHYSICAL connections. In earlier
  94. * versions of the Transport stacks, there was no Physical API and PHYSICAL
  95. * connections were made in-band using the TConnectRequest() and
  96. * TDisconnectRequest() calls. This form of call control is still
  97. * supported in this version.
  98. *
  99. * Although the user can use out-of-band (PHYSICAL call control) and
  100. * in-band call control, the DLL must be put in one mode or the other.
  101. * The DLL can NOT function in both modes simultaneously. The user
  102. * determines the DLL's mode of operation by the calls it makes to it.
  103. * If the user makes the TPhysicalInitialize() function call to the DLL
  104. * before it issues the TInitialize() function call, the DLL is in the
  105. * out-of-band call control mode. If the user only makes the TInitialize()
  106. * function call, by default the DLL will be in the in-band call control
  107. * mode.
  108. *
  109. * If MCS will be using this DLL, it makes the TInitialize() function call.
  110. * Therefore, if the user wants to use the out-of-band call control mode,
  111. * it should call TPhysicalInitialize() before it initializes MCS.
  112. *
  113. *
  114. * Static Variables:
  115. * Static_Instance_Handle - Instance handle passed to the DLL when it
  116. * is started. The instance handle is used
  117. * for Win32 system calls.
  118. * g_pController - This is a pointer to the controller that
  119. * maintains the T123 stacks.
  120. * m_cUsers - This is incremented each time someone
  121. * calls the TInitialize function. It is
  122. * decremented each time someone calls the
  123. * TCleanup function. When this value
  124. * reaches 0, we destroy the transport
  125. * controller.
  126. * g_hWorkerThread - This global variable keeps the handle of
  127. * the thread we spawn on initialization.
  128. * g_dwWorkerThreadID - Thread identifier
  129. * g_hWorkerThreadEvent - This is an event object that is used to
  130. * synchronize actions between the 2 threads.
  131. * g_hThreadTerminateEvent - This event is used to signal the PSTN
  132. * thread that it needs to terminate.
  133. * ComPort_List - This is a list of the ComPort objects
  134. * serviced by this DLL.
  135. * class.
  136. * Number_Of_Diagnostic_Users - This variable keeps up with the number of
  137. * people using who called T120DiagnosticCreate.
  138. * We won't delete the Diagnostic class until
  139. * an equal number of people call
  140. * T120DiagnosticDestroy.
  141. *
  142. */
  143. /*
  144. ** External Interfaces
  145. */
  146. #include "tprtintf.h"
  147. #include "comport.h"
  148. #define TPRTINTF_MAXIMUM_WAIT_OBJECTS (1 + 16)
  149. /*
  150. ** Global variables used within the C to C++ converter.
  151. */
  152. HINSTANCE g_hDllInst = NULL;
  153. CTransportInterface *g_pTransportInterface = NULL;
  154. CRITICAL_SECTION g_csPSTN;
  155. Timer *g_pSystemTimer = NULL;
  156. TransportController *g_pController = NULL;
  157. HANDLE g_hWorkerThread = NULL;
  158. DWORD g_dwWorkerThreadID = 0;
  159. HANDLE g_hWorkerThreadEvent= NULL;
  160. HANDLE g_hThreadTerminateEvent = NULL;
  161. DictionaryClass *g_pComPortList2 = NULL;
  162. SListClass *g_pPSTNEventList = NULL;
  163. TransportCallback g_pfnT120Notify = NULL;
  164. void *g_pUserData = NULL;
  165. BOOL g_fEventListChanged = FALSE;
  166. /*
  167. ** The following defines the MCATPSTN User window class name.
  168. ** This name must be unique, system wide.
  169. */
  170. #define MCATPSTN_USER_WINDOW_CLASS_NAME "NMPSTN USER Message Window"
  171. /*
  172. /*
  173. ** Timer duration. We will poll the DLL every X milliseconds. During
  174. ** this time, we can do any maintenance that is necessary
  175. */
  176. #define MCATPSTN_TIMER_DURATION 250
  177. /*
  178. ** These are prototypes
  179. */
  180. DWORD T123_WorkerThreadProc(DWORD *);
  181. /*
  182. * BOOL WINAPI DllMain (
  183. * HINSTANCE instance_handle,
  184. * DWORD reason,
  185. * LPVOID)
  186. *
  187. * Functional Description:
  188. * This routine is the DLL startup routine. It is analagous to the
  189. * constructor of a class. It is called by Windows when the DLL is
  190. * loaded and when other significant events occur.
  191. */
  192. #ifdef _DEBUG
  193. #define INIT_DBG_ZONE_DATA
  194. #include "fsdiag2.h"
  195. #endif /* _DEBUG */
  196. BOOL WINAPI DllMain(HINSTANCE hDllInst, DWORD reason, LPVOID)
  197. {
  198. switch (reason)
  199. {
  200. case DLL_PROCESS_ATTACH:
  201. g_hDllInst = hDllInst;
  202. ::DisableThreadLibraryCalls(hDllInst);
  203. #ifdef _DEBUG
  204. MLZ_DbgInit((PSTR *) &c_apszDbgZones[0],
  205. (sizeof(c_apszDbgZones) / sizeof(c_apszDbgZones[0])) - 1);
  206. #endif
  207. DBG_INIT_MEMORY_TRACKING(hDllInst);
  208. ::InitializeCriticalSection(&g_csPSTN);
  209. break;
  210. case DLL_PROCESS_DETACH:
  211. ::DeleteCriticalSection(&g_csPSTN);
  212. DBG_CHECK_MEMORY_TRACKING(hDllInst);
  213. #ifdef _DEBUG
  214. MLZ_DbgDeInit();
  215. #endif
  216. break;
  217. }
  218. return TRUE;
  219. }
  220. TransportError WINAPI T123_CreateTransportInterface(ILegacyTransport **pp)
  221. {
  222. TransportError rc;
  223. if (NULL != pp)
  224. {
  225. *pp = NULL;
  226. if (NULL == g_pTransportInterface)
  227. {
  228. rc = TRANSPORT_MEMORY_FAILURE;
  229. DBG_SAVE_FILE_LINE
  230. g_pTransportInterface = new CTransportInterface(&rc);
  231. if (NULL != g_pTransportInterface && TRANSPORT_NO_ERROR == rc)
  232. {
  233. *pp = (ILegacyTransport *) g_pTransportInterface;
  234. return TRANSPORT_NO_ERROR;
  235. }
  236. delete g_pTransportInterface;
  237. g_pTransportInterface = NULL;
  238. return rc;
  239. }
  240. ASSERT(0);
  241. return TRANSPORT_ALREADY_INITIALIZED;
  242. }
  243. return TRANSPORT_INVALID_PARAMETER;
  244. }
  245. CTransportInterface::CTransportInterface(TransportError *rc)
  246. :
  247. m_cUsers(0)
  248. {
  249. g_hWorkerThread = NULL;
  250. g_dwWorkerThreadID = 0;
  251. g_pfnT120Notify = NULL;
  252. g_pUserData = NULL;
  253. g_fEventListChanged = FALSE;
  254. DBG_SAVE_FILE_LINE
  255. g_pSystemTimer = new Timer;
  256. ASSERT(NULL != g_pSystemTimer);
  257. DBG_SAVE_FILE_LINE
  258. g_hWorkerThreadEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
  259. ASSERT(NULL != g_hWorkerThreadEvent);
  260. DBG_SAVE_FILE_LINE
  261. g_hThreadTerminateEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
  262. ASSERT(NULL != g_hThreadTerminateEvent);
  263. DBG_SAVE_FILE_LINE
  264. g_pComPortList2 = new DictionaryClass(TRANSPORT_HASHING_BUCKETS);
  265. ASSERT(NULL != g_pComPortList2);
  266. DBG_SAVE_FILE_LINE
  267. g_pPSTNEventList = new SListClass;
  268. ASSERT(NULL != g_pPSTNEventList);
  269. *rc = (g_pSystemTimer &&
  270. g_hWorkerThreadEvent &&
  271. g_hThreadTerminateEvent &&
  272. g_pComPortList2 &&
  273. g_pPSTNEventList)
  274. ?
  275. TRANSPORT_NO_ERROR :
  276. TRANSPORT_INITIALIZATION_FAILED;
  277. ASSERT(TRANSPORT_NO_ERROR == *rc);
  278. }
  279. CTransportInterface::~CTransportInterface(void)
  280. {
  281. if (m_cUsers > 0)
  282. {
  283. // TCleanup() was not properly called enough times in the process.
  284. // Force final cleanup.
  285. m_cUsers = 1;
  286. TCleanup();
  287. }
  288. delete g_pSystemTimer;
  289. g_pSystemTimer = NULL;
  290. delete g_pComPortList2;
  291. g_pComPortList2 = NULL;
  292. delete g_pPSTNEventList;
  293. g_pPSTNEventList = NULL;
  294. g_pfnT120Notify = NULL;
  295. g_pUserData = NULL;
  296. if (NULL != g_hWorkerThreadEvent)
  297. {
  298. ::CloseHandle(g_hWorkerThreadEvent);
  299. g_hWorkerThreadEvent = NULL;
  300. }
  301. if (NULL != g_hThreadTerminateEvent)
  302. {
  303. ::CloseHandle(g_hThreadTerminateEvent);
  304. g_hThreadTerminateEvent = NULL;
  305. }
  306. if (NULL != g_hWorkerThread)
  307. {
  308. ::CloseHandle(g_hWorkerThread);
  309. g_hWorkerThread = NULL;
  310. }
  311. }
  312. void CTransportInterface::ReleaseInterface(void)
  313. {
  314. delete this;
  315. }
  316. /*
  317. * TransportError APIENTRY TInitialize (
  318. * TransportCallback transport_callback,
  319. * PVoid user_defined)
  320. *
  321. * Functional Description:
  322. * This function must be called by the user of the Transport
  323. * stack. The PSTN thread is started.
  324. *
  325. * Caveats:
  326. * If this function is successful, the user must also call the
  327. * TCleanup() function when he is finished with the DLL.
  328. */
  329. TransportError CTransportInterface::TInitialize
  330. (
  331. TransportCallback transport_callback,
  332. void *user_defined
  333. )
  334. {
  335. TransportError rc = TRANSPORT_NO_ERROR;
  336. if (! m_cUsers)
  337. {
  338. g_pfnT120Notify = transport_callback;
  339. g_pUserData = user_defined;
  340. /*
  341. ** At this point, we need to create another thread that will
  342. ** maintain this DLL.
  343. */
  344. g_hWorkerThread = ::CreateThread(
  345. NULL, 0, (LPTHREAD_START_ROUTINE) T123_WorkerThreadProc,
  346. NULL, 0, &g_dwWorkerThreadID);
  347. if (NULL != g_hWorkerThread)
  348. {
  349. if (::SetThreadPriority(g_hWorkerThread, THREAD_PRIORITY_ABOVE_NORMAL) == FALSE)
  350. {
  351. WARNING_OUT(("SetThreadPriority ERROR: = %d", ::GetLastError()));
  352. }
  353. /*
  354. ** Wait for the secondary thread to notify us that initialization is
  355. ** complete
  356. */
  357. ::WaitForSingleObject(g_hWorkerThreadEvent, 30000); // 30 seconds
  358. /*
  359. ** If g_pController == NULL, initialization FAILED. We use
  360. ** this variable to signal us regarding the status of the secondary
  361. ** thread.
  362. */
  363. if (NULL == g_pController)
  364. {
  365. ERROR_OUT(("TInitialize: Unable to initialize transport"));
  366. rc = TRANSPORT_INITIALIZATION_FAILED;
  367. }
  368. }
  369. else
  370. {
  371. rc = TRANSPORT_INITIALIZATION_FAILED;
  372. }
  373. }
  374. if (TRANSPORT_NO_ERROR == rc)
  375. {
  376. m_cUsers++;
  377. }
  378. return rc;
  379. }
  380. /*
  381. * TransportError TCleanup (void)
  382. *
  383. * Functional Description:
  384. * This function is called to release all system resources that
  385. * were used during the life of the DLL.
  386. *
  387. * Caveats:
  388. * This function should only be called if the user had previously
  389. * called the TInitialize() function.
  390. */
  391. TransportError CTransportInterface::TCleanup(void)
  392. {
  393. TransportError rc = TRANSPORT_NOT_INITIALIZED;
  394. if (m_cUsers > 0)
  395. {
  396. if (--m_cUsers == 0)
  397. {
  398. /*
  399. ** Set the g_hThreadTerminateEvent to notify the PSTN thread
  400. ** that it should terminate operations.
  401. */
  402. ::SetEvent(g_hThreadTerminateEvent);
  403. /*
  404. ** Wait for the PSTN thread to notify us that cleanup is
  405. ** complete
  406. */
  407. if (g_hWorkerThreadEvent != NULL)
  408. {
  409. // WaitForSingleObject (g_hWorkerThreadEvent, INFINITE);
  410. ::WaitForSingleObject(g_hWorkerThreadEvent, 30000); // 30 seconds
  411. g_hWorkerThreadEvent = NULL;
  412. //
  413. // Relinquish the remainder of our time slice, to allow trasnsport thread to exit.
  414. //
  415. Sleep(0);
  416. }
  417. }
  418. rc = TRANSPORT_NO_ERROR;
  419. }
  420. return rc;
  421. }
  422. TransportError CTransportInterface::TCreateTransportStack
  423. (
  424. BOOL fCaller,
  425. HANDLE hCommLink,
  426. HANDLE hevtClose,
  427. PLUGXPRT_PARAMETERS *pParams
  428. )
  429. {
  430. TransportError rc = TRANSPORT_NOT_INITIALIZED;
  431. if (m_cUsers > 0)
  432. {
  433. ::EnterCriticalSection(&g_csPSTN);
  434. if (NULL != g_pController)
  435. {
  436. rc = g_pController->CreateTransportStack(fCaller, hCommLink, hevtClose, pParams);
  437. }
  438. ::LeaveCriticalSection(&g_csPSTN);
  439. }
  440. return rc;
  441. }
  442. TransportError CTransportInterface::TCloseTransportStack
  443. (
  444. HANDLE hCommLink
  445. )
  446. {
  447. TransportError rc = TRANSPORT_NOT_INITIALIZED;
  448. if (m_cUsers > 0)
  449. {
  450. ::EnterCriticalSection(&g_csPSTN);
  451. if (NULL != g_pController)
  452. {
  453. rc = g_pController->CloseTransportStack(hCommLink);
  454. }
  455. ::LeaveCriticalSection(&g_csPSTN);
  456. }
  457. return rc;
  458. }
  459. /*
  460. * TransportError APIENTRY TConnectRequest (
  461. * TransportAddress transport_address,
  462. * TransportPriority transport_priority,
  463. * LogicalHandle *logical_handle)
  464. *
  465. * Functional Description:
  466. * This function starts the process of building a transport connection.
  467. * It uses the transport_address and attempts to make a connection to it.
  468. *
  469. * If the DLL is set up for in-band call control, the DLL will choose a
  470. * modem and attempt a connection. After the physical connection is up,
  471. * it will create a logical connection that the user application can use.
  472. *
  473. * If the DLL is set up for out-of-band call control, the transport address
  474. * is actually an ascii string that represents a DLL generated
  475. * physicalhandle. This call can only be made in response to a successful
  476. * TPhysicalConnectRequest() function. The TPhysicalConnectRequest() call
  477. * returns a PhysicalHandle that the user must convert to ascii and pass
  478. * back to the DLL via this call. Although this is very ugly, we did this
  479. * for a reason. We added the out-of-band call control API to the
  480. * transports but we did not want to change GCC or MCS to do it. GCC and
  481. * MCS already expected an ascii string as the transport address. In the
  482. * future, look for this to change in MCS and GCC.
  483. */
  484. TransportError CTransportInterface::TConnectRequest
  485. (
  486. LEGACY_HANDLE *logical_handle,
  487. HANDLE hCommLink
  488. )
  489. {
  490. TransportError rc = TRANSPORT_NOT_INITIALIZED;
  491. if (m_cUsers > 0)
  492. {
  493. ::EnterCriticalSection(&g_csPSTN);
  494. if (NULL != g_pController)
  495. {
  496. rc = g_pController->ConnectRequest(logical_handle, hCommLink);
  497. }
  498. ::LeaveCriticalSection(&g_csPSTN);
  499. }
  500. return rc;
  501. }
  502. /*
  503. * TransportError APIENTRY TDisconnectRequest (
  504. * LogicalHandle logical_handle,
  505. * BOOL trash_packets)
  506. *
  507. * Functional Description:
  508. * This function breaks a logical connection. This is also an X.214
  509. * primitive. Since the DLL may have packets queued up for transmission
  510. * for this logical connection, the trash_packets parameter determines
  511. * whether the DLL should trash those packets before disconnecting the
  512. * logical connection.
  513. */
  514. TransportError CTransportInterface::TDisconnectRequest
  515. (
  516. LEGACY_HANDLE logical_handle,
  517. BOOL trash_packets
  518. )
  519. {
  520. TransportError rc = TRANSPORT_NOT_INITIALIZED;
  521. if (m_cUsers > 0)
  522. {
  523. ::EnterCriticalSection(&g_csPSTN);
  524. if (NULL != g_pController)
  525. {
  526. rc = g_pController->DisconnectRequest(logical_handle, trash_packets);
  527. }
  528. ::LeaveCriticalSection(&g_csPSTN);
  529. }
  530. return rc;
  531. }
  532. /*
  533. * TransportError APIENTRY TDataRequest (
  534. * LogicalHandle logical_handle,
  535. * LPBYTE user_data,
  536. * ULONG user_data_length)
  537. *
  538. * Functional Description:
  539. * This function takes the data passed in and attempts to send it
  540. * to the remote site.
  541. */
  542. TransportError CTransportInterface::TDataRequest
  543. (
  544. LEGACY_HANDLE logical_handle,
  545. LPBYTE user_data,
  546. ULONG user_data_length
  547. )
  548. {
  549. TransportError rc = TRANSPORT_NOT_INITIALIZED;
  550. if (m_cUsers > 0)
  551. {
  552. ::EnterCriticalSection(&g_csPSTN);
  553. if (NULL != g_pController)
  554. {
  555. rc = g_pController->DataRequest(logical_handle, user_data, user_data_length);
  556. /*
  557. ** If the packet was accepted, we need to issue a
  558. ** PollTransmitter() to flush the packet out. Otherwise,
  559. ** we will have a latency problem.
  560. */
  561. if (TRANSPORT_NO_ERROR == rc)
  562. {
  563. ComPort *comport;
  564. PhysicalHandle physical_handle = g_pController->GetPhysicalHandle(logical_handle);
  565. if ( ( 0 != physical_handle ) &&
  566. g_pComPortList2->find((DWORD_PTR) physical_handle, (PDWORD_PTR) &comport))
  567. {
  568. if (! comport->IsWriteActive())
  569. {
  570. g_pController->PollTransmitter(physical_handle);
  571. }
  572. }
  573. }
  574. }
  575. ::LeaveCriticalSection(&g_csPSTN);
  576. }
  577. return rc;
  578. }
  579. /*
  580. * TransportError APIENTRY TReceiveBufferAvailable (void)
  581. *
  582. * Functional Description:
  583. * This function informs the transport to enable packet transfer from the
  584. * Transport to the user application. This function makes a call to
  585. * 'Enable' the receiver and then it issues a PollReceiver() to actually
  586. * allow any pending packets to be sent on up to the user.
  587. */
  588. TransportError CTransportInterface::TReceiveBufferAvailable(void)
  589. {
  590. TransportError rc = TRANSPORT_NOT_INITIALIZED;
  591. if (m_cUsers > 0)
  592. {
  593. ::EnterCriticalSection(&g_csPSTN);
  594. if (NULL != g_pController)
  595. {
  596. g_pController->EnableReceiver();
  597. g_pController->PollReceiver();
  598. /*
  599. ** We are doing a PollTransmitter() here to allow
  600. ** the Q922 object to exit the 'receiver not ready' mode.
  601. ** If MCS had been rejecting packets, and now it is accepting
  602. ** packets, we need to let the remote site know that it
  603. ** can re-start its transmitter.
  604. */
  605. g_pController->PollTransmitter();
  606. }
  607. ::LeaveCriticalSection(&g_csPSTN);
  608. rc = TRANSPORT_NO_ERROR;
  609. }
  610. return rc;
  611. }
  612. /*
  613. * TransportError APIENTRY TPurgeRequest (
  614. * LogicalHandle logical_handle)
  615. *
  616. * Functional Description:
  617. * This function purges all of the outbound packets for the given
  618. * logical connection.
  619. */
  620. TransportError CTransportInterface::TPurgeRequest
  621. (
  622. LEGACY_HANDLE logical_handle
  623. )
  624. {
  625. TransportError rc = TRANSPORT_NOT_INITIALIZED;
  626. if (m_cUsers > 0)
  627. {
  628. ::EnterCriticalSection(&g_csPSTN);
  629. if (NULL != g_pController)
  630. {
  631. rc = g_pController->PurgeRequest(logical_handle);
  632. }
  633. ::LeaveCriticalSection(&g_csPSTN);
  634. }
  635. return rc;
  636. }
  637. TransportError CTransportInterface::TEnableReceiver(void)
  638. {
  639. TransportError rc = TRANSPORT_NOT_INITIALIZED;
  640. if (m_cUsers > 0)
  641. {
  642. ::EnterCriticalSection(&g_csPSTN);
  643. if (NULL != g_pController)
  644. {
  645. g_pController->EnableReceiver();
  646. }
  647. ::LeaveCriticalSection(&g_csPSTN);
  648. }
  649. return TRANSPORT_NO_ERROR;
  650. }
  651. /*
  652. * DWORD T123_WorkerThreadProc (LPDWORD)
  653. *
  654. * Functional Description:
  655. * This function is the PSTN thread. It maintains the DLL.
  656. */
  657. DWORD T123_WorkerThreadProc(DWORD *)
  658. {
  659. PEventObject event_struct;
  660. BOOL fContinue = TRUE;
  661. HANDLE aEvents[TPRTINTF_MAXIMUM_WAIT_OBJECTS];
  662. ULONG cEvents;
  663. ULONG last_time_polled;
  664. ULONG current_time;
  665. g_fEventListChanged = FALSE;
  666. /*
  667. ** Create the one and only instance of the Transport Controller
  668. ** to be in charge of this DLL. Once it is created, all other
  669. ** primitives are routed to it.
  670. */
  671. DBG_SAVE_FILE_LINE
  672. g_pController = new TransportController();
  673. if (g_pController == NULL)
  674. {
  675. ERROR_OUT(("PSTN_WorkerThreadProc: cannot allocate TransportController"));
  676. ::SetEvent(g_hWorkerThreadEvent);
  677. return TRANSPORT_INITIALIZATION_FAILED;
  678. }
  679. /*
  680. ** The DLL needs to be polled every X milliseconds. We do this
  681. ** by checking the system clock and if X milliseconds have elapsed
  682. ** since the last DLL poll, we do it again. This line of code is
  683. ** actually initializing the timer.
  684. */
  685. last_time_polled = ::GetTickCount();
  686. /*
  687. ** Notify the primary thread that we are up
  688. */
  689. ::SetEvent(g_hWorkerThreadEvent);
  690. // THREAD LOOP
  691. /*
  692. ** This while loop is the heart of the PSTN thread. We use the
  693. ** WaitForMultipleObjects() function to wake up our thread when a
  694. ** significant event occurs OR when a timeout occurs.
  695. **
  696. ** There are four different types of event objects that can occur
  697. ** that will wake up the thread.
  698. **
  699. ** 1. The primary thread sets the g_hThreadTerminateEvent object.
  700. ** 2. Read event from the ComPort object. If a read of a comm port
  701. ** completes or times out, the event object is set
  702. ** 3. Write event from the ComPort object. If a write on a comm
  703. ** port completes or times out.
  704. ** 4. Control event from the ComPort object. If the carrier detect
  705. ** signal changes.
  706. **
  707. ** Also, the thread will continue running if the MCATPSTN_TIMER_DURATION
  708. ** expires
  709. **
  710. **
  711. ** A ComPort object can add event objects to our PSTN Event_List at any
  712. ** time. When they are added to the Event_List, we add them to the
  713. ** local aEvents and the WaitForMultipleObjects() function waits
  714. ** for them.
  715. **
  716. ** This approach works very well except that the WaitForMultipleObjects()
  717. ** function can only handle TPRTINTF_MAXIMUM_WAIT_OBJECTS at a time. Currently,
  718. ** this #define is set to 64 by the Windows 95 operating system. Since
  719. ** each ComPort uses 3 event objects, this limits us to a maximum of
  720. ** 21 active ports.
  721. */
  722. aEvents[0] = g_hThreadTerminateEvent;
  723. cEvents = 1;
  724. while (fContinue)
  725. {
  726. /*
  727. ** Wait for an event to occur or for the timeout to expire
  728. */
  729. DWORD dwRet = ::WaitForMultipleObjects(cEvents, aEvents, FALSE, MCATPSTN_TIMER_DURATION);
  730. /*
  731. ** We MUST enter this critical section of code and lock out the
  732. ** primary thread from enterring it. Both threads executing this
  733. ** code could be disasterous
  734. */
  735. ::EnterCriticalSection(&g_csPSTN);
  736. if (dwRet == WAIT_FAILED)
  737. {
  738. fContinue = FALSE;
  739. break;
  740. }
  741. else
  742. if (dwRet == WAIT_TIMEOUT)
  743. {
  744. if (cEvents > 1)
  745. {
  746. last_time_polled = ::GetTickCount();
  747. g_pController->PollReceiver();
  748. g_pController->PollTransmitter();
  749. g_pSystemTimer->ProcessTimerEvents();
  750. }
  751. }
  752. else
  753. {
  754. /*
  755. ** Determine which object has signalled us
  756. */
  757. ULONG dwIdx = dwRet - WAIT_OBJECT_0;
  758. if (dwIdx > cEvents)
  759. {
  760. ERROR_OUT(("ThreadFunc: Invalid object signalled = %d", dwIdx));
  761. }
  762. else
  763. {
  764. /*
  765. ** If the object that signalled us is index 0, it is the
  766. ** terminate thread object.
  767. */
  768. if (! dwIdx)
  769. {
  770. TRACE_OUT(("ThreadFunc: Terminating thread"));
  771. fContinue = FALSE;
  772. }
  773. else
  774. {
  775. dwIdx--;
  776. event_struct = (PEventObject) g_pPSTNEventList->read((USHORT) dwIdx);
  777. if (NULL != event_struct)
  778. {
  779. /*
  780. ** Check the delete_event, if it is set, we need to
  781. ** terminate the event.
  782. **
  783. ** By design, other objects throughout the DLL create
  784. ** the events, and this function deletes them. If the
  785. ** creating function deleted them, we could be waiting
  786. ** on an event that gets deleted. That has the potential
  787. ** of being messy.
  788. */
  789. if (event_struct->delete_event)
  790. {
  791. /*
  792. ** Remove the ComPort from our list
  793. */
  794. ComPort *comport = NULL;
  795. if (g_pComPortList2->find((DWORD_PTR) event_struct->hCommLink, (PDWORD_PTR) &comport))
  796. {
  797. g_pComPortList2->remove((DWORD_PTR) event_struct->hCommLink);
  798. comport->Release();
  799. }
  800. /*
  801. ** Close the event
  802. */
  803. ::CloseHandle(event_struct->event);
  804. /*
  805. ** Remove the event from our list
  806. */
  807. g_pPSTNEventList->remove((DWORD_PTR) event_struct);
  808. delete event_struct;
  809. g_fEventListChanged = TRUE;
  810. }
  811. else
  812. {
  813. ComPort *comport = event_struct->comport;
  814. /*
  815. ** Switch on the event_type to determine the
  816. ** operation that needs to take place
  817. */
  818. switch (event_struct->event_type)
  819. {
  820. case READ_EVENT:
  821. comport->ProcessReadEvent();
  822. /*
  823. ** If a READ event occurred, we need to issue
  824. ** a PollReceiver() and then issue a
  825. ** PollTransmitter(). We issue the
  826. ** PollTransmitter() because the PollReceiver()
  827. ** may have freed up space for us to send out
  828. ** data.
  829. */
  830. g_pController->PollReceiver(event_struct->hCommLink);
  831. g_pController->PollTransmitter(event_struct->hCommLink);
  832. break;
  833. case WRITE_EVENT:
  834. /*
  835. ** If a WRITE_EVENT occurs, this means that
  836. ** space has become available to transmit more
  837. ** data.
  838. */
  839. comport->ProcessWriteEvent();
  840. g_pController->PollTransmitter(event_struct->hCommLink);
  841. break;
  842. case CONTROL_EVENT:
  843. /*
  844. ** The CONTROL events are particular to the
  845. ** ComPort object. A change in the CD signal
  846. ** could be a CONTROL event.
  847. */
  848. g_pController->PollReceiver(event_struct->hCommLink);
  849. break;
  850. default:
  851. ERROR_OUT(("ThreadFunc: Illegal event type = %d", event_struct->event_type));
  852. break;
  853. } // switch
  854. }
  855. } // if event_struct
  856. }
  857. }
  858. /*
  859. ** Check to see if enough time has elapsed since the last time we
  860. ** polled the DLL to do it again.
  861. */
  862. if (cEvents > 1)
  863. {
  864. current_time = ::GetTickCount();
  865. if ((current_time - last_time_polled) >= MCATPSTN_TIMER_DURATION)
  866. {
  867. last_time_polled = current_time;
  868. g_pController->PollReceiver();
  869. g_pController->PollTransmitter();
  870. g_pSystemTimer->ProcessTimerEvents();
  871. }
  872. }
  873. }
  874. /*
  875. ** This Event_List_Changed will only be set to TRUE if we need to
  876. ** update our event list
  877. */
  878. if (g_fEventListChanged)
  879. {
  880. aEvents[0] = g_hThreadTerminateEvent;
  881. cEvents = 1;
  882. if (g_pPSTNEventList->entries() > (TPRTINTF_MAXIMUM_WAIT_OBJECTS - 1))
  883. {
  884. ERROR_OUT(("ThreadFunc: ERROR: Number of event objects = %d",
  885. g_pPSTNEventList->entries() + 1));
  886. }
  887. /*
  888. ** Go thru the Event_List and create a new aEvents.
  889. */
  890. g_pPSTNEventList->reset();
  891. while (g_pPSTNEventList->iterate((PDWORD_PTR) &event_struct))
  892. {
  893. aEvents[cEvents] = event_struct -> event;
  894. cEvents++;
  895. /*
  896. ** Add the physical_handle to the ComPort_List
  897. */
  898. if (event_struct->event_type == WRITE_EVENT)
  899. {
  900. if (! g_pComPortList2->find((DWORD_PTR) event_struct->hCommLink))
  901. {
  902. g_pComPortList2->insert((DWORD_PTR) event_struct->hCommLink,
  903. (DWORD_PTR) event_struct->comport);
  904. }
  905. }
  906. }
  907. g_fEventListChanged = FALSE;
  908. }
  909. ::LeaveCriticalSection(&g_csPSTN);
  910. } // while
  911. // CLEANUP THE THREAD RESOURCES
  912. delete g_pController;
  913. g_pController = NULL;
  914. /*
  915. ** Delete all of the event objects
  916. */
  917. g_pPSTNEventList->reset();
  918. while (g_pPSTNEventList->iterate((PDWORD_PTR) &event_struct))
  919. {
  920. ::CloseHandle(event_struct->event);
  921. delete event_struct;
  922. }
  923. /*
  924. ** Notify the primary thread that we are up
  925. */
  926. ::SetEvent(g_hWorkerThreadEvent);
  927. return (0);
  928. }
  929. ULONG NotifyT120(ULONG msg, void *param)
  930. {
  931. if (NULL != g_pfnT120Notify)
  932. {
  933. return (*g_pfnT120Notify) (msg, param, g_pUserData);
  934. }
  935. return 0;
  936. }
  937. IObject::~IObject(void) { }
  938.