Source code of Windows XP (NT5)
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.

3973 lines
116 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Copyright (c) 1991 Nokia Data Systems
  4. Module Name:
  5. vrdlcpst.c
  6. Abstract:
  7. This module implements the call-back functions for DOS DLC CCBs. The call-
  8. backs are also referred to (in IBM terminology) as appendages or exits.
  9. These are executed asynchronously when a CCB request completes. Other
  10. asynchronous events can be generated such as adapter status change or
  11. network error. These latter types (& other similar events) are dependent
  12. upon the adapter hardware we are using - Token Ring or EtherNet (IBM Ether
  13. Link or PC Network), and are not expected to occur frequently (usually in
  14. error situations or where something bad happens to the net).
  15. We maintain a READ CCB for each supported adapter (2 per VDM). The READ
  16. will capture ALL events - command completions, data reception, status
  17. change, etc. When a READ CCB completes, we put it in a queue of completed
  18. CCBs and interrupt the VDM. The VDM must asynchronously call back to
  19. VrDlcHwInterrupt where it dequeues and processes the completed command at
  20. the head of the queue. The READ CCB has a pointer to the completed CCB or
  21. received data. If the READ is for a completed NT CCB then the CCB will have
  22. a pointer to the original DOS CCB. We have to get the original DOS CCB
  23. address from the NT CCB and complete the command with the relevant
  24. information contained in the READ/completed NT CCBs.
  25. We never expect the queue of pending completions/data receptions to grow
  26. very large, since DOS is single-tasking and unless it has interrupts
  27. disabled for a large part of the time, then the completion queue should be
  28. processed in a timely fashion
  29. Contents:
  30. VrDlcInitialize
  31. VrDlcHwInterrupt
  32. (FindCompletedRead)
  33. (ProcessReceiveFrame)
  34. (QueryEmulatedLocalBusyState)
  35. (SetEmulatedLocalBusyState)
  36. ResetEmulatedLocalBusyState
  37. (ResetEmulatedLocalBusyStateSap)
  38. (ResetEmulatedLocalBusyStateLink)
  39. (DeferReceive)
  40. (DeferAllIFrames)
  41. (RemoveDeferredReceive)
  42. (VrDlcEventHandlerThread)
  43. InitializeEventHandler
  44. InitiateRead
  45. (PutEvent)
  46. (PeekEvent)
  47. (GetEvent)
  48. (FlushEventQueue)
  49. (RemoveDeadReceives)
  50. (ReleaseReceiveResources)
  51. (IssueHardwareInterrupt)
  52. (AcknowledgeHardwareInterrupt)
  53. (CancelHardwareInterrupts)
  54. Author:
  55. Antti Saarenheimo (o-anttis) 26-DEC-1991
  56. Revision History:
  57. 16-Jul-1992 rfirth
  58. Added queue and interrupt serialization; debugging
  59. 19-Nov-1992 rfirth
  60. substantially modified event processing - use a queue per adapter;
  61. consolidated per-adapter data into DOS_ADAPTER structure; re-wrote
  62. local-busy processing
  63. Note: It turns out that a thread can recursively enter a critical
  64. section. The functions marked 'MUST [NOT] BE ENTERED WHILE HOLDING
  65. CRITICAL SECTION <foo>' could be altered
  66. --*/
  67. #include <nt.h>
  68. #include <ntrtl.h> // ASSERT, DbgPrint
  69. #include <nturtl.h>
  70. #include <windows.h>
  71. #include <softpc.h> // x86 virtual machine definitions
  72. #include <vrdlctab.h>
  73. #include <vdmredir.h>
  74. #include "vrdebug.h"
  75. #include <dlcapi.h> // Official DLC API definition
  76. #include <ntdddlc.h> // IOCTL commands
  77. #include <dlcio.h> // Internal IOCTL API interface structures
  78. #include "vrdlc.h"
  79. #include "vrdlcdbg.h"
  80. #define BOOL
  81. #include <insignia.h> // Insignia defines
  82. #include <xt.h> // half_word
  83. #include <ica.h> // ica_hw_interrupt
  84. #include <vrica.h> // call_ica_hw_interrupt
  85. //
  86. // defines
  87. //
  88. #define EVENT_THREAD_STACK 0 // 4096
  89. //
  90. // macros
  91. //
  92. //
  93. // IS_LOCAL_BUSY - determines whether we have a LOCAL BUSY state active for a
  94. // particular link station
  95. //
  96. #define IS_LOCAL_BUSY(adapter, stationId) (QueryEmulatedLocalBusyState((BYTE)adapter, (WORD)stationId) == BUSY)
  97. //
  98. // private types
  99. //
  100. typedef enum {
  101. INDICATE_RECEIVE_FRAME,
  102. INDICATE_LOCAL_BUSY,
  103. INDICATE_COMPLETE_RECEIVE
  104. } INDICATION;
  105. typedef enum {
  106. CURRENT,
  107. DEFERRED
  108. } READ_FRAME_TYPE;
  109. //
  110. // private prototypes
  111. //
  112. PLLC_CCB
  113. FindCompletedRead(
  114. OUT READ_FRAME_TYPE* pFrameType
  115. );
  116. INDICATION
  117. ProcessReceiveFrame(
  118. IN OUT PLLC_CCB* ppCcb,
  119. IN LLC_DOS_CCB UNALIGNED * pDosCcb,
  120. IN LLC_DOS_PARMS UNALIGNED * pDosParms,
  121. OUT LLC_STATUS* Status
  122. );
  123. LOCAL_BUSY_STATE
  124. QueryEmulatedLocalBusyState(
  125. IN BYTE AdapterNumber,
  126. IN WORD StationId
  127. );
  128. VOID
  129. SetEmulatedLocalBusyState(
  130. IN BYTE AdapterNumber,
  131. IN WORD StationId
  132. );
  133. BOOLEAN
  134. ResetEmulatedLocalBusyStateSap(
  135. IN BYTE AdapterNumber,
  136. IN WORD StationId,
  137. IN BYTE DlcCommand
  138. );
  139. BOOLEAN
  140. ResetEmulatedLocalBusyStateLink(
  141. IN BYTE AdapterNumber,
  142. IN WORD StationId,
  143. IN BYTE DlcCommand
  144. );
  145. VOID
  146. DeferReceive(
  147. IN BYTE AdapterNumber,
  148. IN WORD StationId,
  149. IN PLLC_CCB pReadCcb
  150. );
  151. VOID
  152. DeferAllIFrames(
  153. IN BYTE AdapterNumber,
  154. IN WORD StationId
  155. );
  156. PLLC_CCB
  157. RemoveDeferredReceive(
  158. IN BYTE AdapterNumber,
  159. IN WORD StationId,
  160. OUT BOOLEAN* pDeferredFramesLeft
  161. );
  162. DWORD
  163. VrDlcEventHandlerThread(
  164. IN PVOID pParameter
  165. );
  166. VOID
  167. PutEvent(
  168. IN BYTE AdapterNumber,
  169. IN PLLC_CCB pCcb
  170. );
  171. PLLC_CCB
  172. PeekEvent(
  173. IN BYTE AdapterNumber
  174. );
  175. PLLC_CCB
  176. GetEvent(
  177. IN BYTE AdapterNumber
  178. );
  179. VOID
  180. FlushEventQueue(
  181. IN BYTE AdapterNumber
  182. );
  183. VOID
  184. RemoveDeadReceives(
  185. IN PLLC_CCB pCcb
  186. );
  187. VOID
  188. ReleaseReceiveResources(
  189. IN PLLC_CCB pCcb
  190. );
  191. VOID
  192. IssueHardwareInterrupt(
  193. VOID
  194. );
  195. VOID
  196. AcknowledgeHardwareInterrupt(
  197. VOID
  198. );
  199. VOID
  200. CancelHardwareInterrupts(
  201. IN LONG Count
  202. );
  203. //
  204. // external functions
  205. //
  206. extern ACSLAN_STATUS (*lpAcsLan)(IN OUT PLLC_CCB, OUT PLLC_CCB*);
  207. extern
  208. VOID
  209. VrQueueCompletionHandler(
  210. IN VOID (*AsyncDispositionRoutine)(VOID)
  211. );
  212. extern
  213. VOID
  214. VrRaiseInterrupt(
  215. VOID
  216. );
  217. //
  218. // external data
  219. //
  220. extern DOS_ADAPTER Adapters[DOS_DLC_MAX_ADAPTERS];
  221. //
  222. // private data
  223. //
  224. //
  225. // aReadCcbs - for each adapter (max. 2) we have an NT READ CCB which is used
  226. // to get received data which is returned to the DOS program via its RECEIVE
  227. // CCB. Note that these are pointers to CCBs, not the actual CCBs. We also get
  228. // status changes & command completions through the same mechanism. These are
  229. // reflected to the VDM through the various 'appendages'
  230. //
  231. // NB: once initialized, this array is ONLY WRITTEN BY InitiateRead
  232. //
  233. PLLC_CCB aReadCcbs[DOS_DLC_MAX_ADAPTERS];
  234. //
  235. // aReadEvents - for each adapter (max. 2) we have a handle to an EVENT object.
  236. // This is in the unsignalled state until an event completes the READ CCB.
  237. // These are kept in an array because this is how WaitForMultipleObjects
  238. // expects them
  239. //
  240. HANDLE aReadEvents[DOS_DLC_MAX_ADAPTERS];
  241. //
  242. // HardwareIntCritSec is used to protect updates to HardwareIntsQueued.
  243. // HardwareIntsQueued is the number of outstanding hardware interrupt requests
  244. // to the VDM. -1 means none outstanding, 0 is 1, etc.
  245. //
  246. #define NO_INTERRUPTS_PENDING (-1)
  247. static CRITICAL_SECTION HardwareIntCritSec;
  248. static LONG HardwareIntsQueued = NO_INTERRUPTS_PENDING;
  249. //
  250. // hEventThread - handle of asynchronous event thread
  251. //
  252. static HANDLE hEventThread;
  253. //
  254. // routines
  255. //
  256. VOID
  257. VrDlcInitialize(
  258. VOID
  259. )
  260. /*++
  261. Routine Description:
  262. Initializes critical sections that must always be available
  263. Arguments:
  264. None.
  265. Return Value:
  266. None.
  267. --*/
  268. {
  269. IF_DEBUG(DLC) {
  270. DPUT("VrDlcInitialize\n");
  271. }
  272. IF_DEBUG(CRITICAL) {
  273. CRITDUMP(("VrDlcInitialize\n"));
  274. }
  275. //
  276. // initialize the critical section for the event list
  277. //
  278. InitializeCriticalSection(&HardwareIntCritSec);
  279. }
  280. BOOLEAN
  281. VrDlcHwInterrupt(
  282. VOID
  283. )
  284. /*++
  285. Routine Description:
  286. Procedure reads one DOS appendage call from the event queue
  287. and sets the initializes MS-DOS registers for the appendage
  288. call. A NT DLC event may generate several DOS appendage calls.
  289. It is also possible, that no appendage call is needed.
  290. The event queue is incremented only after the last DOS appendage
  291. has been generated
  292. Return Value:
  293. FALSE - the event queue was empty, poll the next interrupt handler
  294. TRUE - an event was handled successfully.
  295. --*/
  296. {
  297. BOOLEAN GetNewEvent = FALSE; // default no extra events generated
  298. PLLC_CCB pReadCcb; // READ CCB at head of EventQueue
  299. PLLC_READ_PARMS pReadParms; // READ parameter table
  300. LLC_DOS_CCB UNALIGNED * pDosCcb;// flat 32-bit pointer to DOS CCB
  301. WORD cLeft;
  302. PLLC_CCB pCcb;
  303. LLC_CCB UNALIGNED * pFlatCcbAddr;
  304. LLC_DOS_PARMS UNALIGNED * pParms;
  305. DWORD iStation;
  306. static DWORD iCurrentTempStatus = 0;
  307. PLLC_BUFFER pNtFrame; // pointer to received NT frame
  308. PLLC_DOS_RECEIVE_PARMS_EX pRcvParms; // special NT rcv params for DOS
  309. LLC_STATUS Status;
  310. INDICATION indication;
  311. WORD buffersLeft;
  312. DOS_ADDRESS dpDosCcb;
  313. DOS_ADDRESS newAddress;
  314. READ_FRAME_TYPE frameType;
  315. BYTE adapterNumber;
  316. BYTE sapNumber;
  317. WORD stationId;
  318. PLLC_CCB cancelledReceive = NULL;
  319. #if DBG
  320. CHAR reasonCode;
  321. DWORD reasonCount;
  322. #endif
  323. IF_DEBUG(DLC_ASYNC) {
  324. DPUT("VrDlcHwInterrupt entered\n");
  325. }
  326. //
  327. // preset the VDM flags to do nothing by default when control is returned
  328. // to the hardware interrupt service routine
  329. //
  330. SET_CALLBACK_NOTHING();
  331. //
  332. // This is called from the hardware interrupt handler in vrnetb.c. If there
  333. // are no events in the queue, then let the NetBIOS h/w interrupt handler
  334. // check if it has any completed NCBs. If there is something in the DLC
  335. // event queue then we will return info to the DOS box telling it that it
  336. // has a completed CCB; if there was something to complete for NetBIOS,
  337. // then it will have to wait until all pending DLC events are completed
  338. //
  339. pReadCcb = FindCompletedRead(&frameType);
  340. if (pReadCcb == NULL) {
  341. IF_DEBUG(DLC_ASYNC) {
  342. DPUT("*** VrDlcHwInterrupt: Error: no completed READs ***\n");
  343. }
  344. IF_DEBUG(CRITICAL) {
  345. CRITDUMP(("*** VrDlcHwInterrupt: Error: no completed READs ***\n"));
  346. }
  347. return FALSE;
  348. }
  349. IF_DEBUG(CRITICAL) {
  350. CRITDUMP(("*** VrDlcHwInterrupt: READ CCB Peeked @ %x ***\n", pReadCcb));
  351. }
  352. //
  353. // get the completion event and dispatch it
  354. //
  355. pReadParms = &pReadCcb->u.pParameterTable->Read;
  356. adapterNumber = pReadCcb->uchAdapterNumber;
  357. switch (pReadParms->uchEvent) {
  358. case LLC_EVENT_COMMAND_COMPLETION:
  359. //
  360. // Event 0x01
  361. //
  362. IF_DEBUG(DLC_ASYNC) {
  363. DPUT("VrDlcHwInterrupt: LLC_EVENT_COMMAND_COMPLETION\n");
  364. }
  365. //
  366. // Complete only the first CCB command in the list.
  367. // The completed receive CCBs are in the 32-bit flat address
  368. // space (we cannot pass them through to DOS because of
  369. // different buffer format between DOS and Nt (or OS/2)).
  370. //
  371. // Every completed receive command (the ones with receive data flag
  372. // are never completed here) uses LLC_DOS_SPECIAL_COMMAND as its
  373. // input flag, the command actual post routine address and the
  374. // original CCB address in DOS have been save to its parameter table.
  375. // The completion flag is zero, if the receive command is not
  376. // completed by a command completion routine.
  377. //
  378. pCcb = pReadParms->Type.Event.pCcbCompletionList;
  379. if (pReadParms->ulNotificationFlag == LLC_DOS_SPECIAL_COMMAND) {
  380. //
  381. // The DOS receive is yet another exception:
  382. // We cannot directly use the DOS receive CCB, because
  383. // the driver completes the command with the received data in
  384. // NT buffers => we have allocated a special receive CCB and
  385. // parameter table from 32-bit address space to execute
  386. // the same receive command. We must now copy the received data
  387. // back to DOS buffer pools and then complete the original DOS
  388. // receive CCB
  389. //
  390. pRcvParms = (PVOID)READ_DWORD(&pCcb->u.pParameterTable);
  391. pDosCcb = DOS_PTR_TO_FLAT((PVOID)READ_DWORD(&pRcvParms->dpOriginalCcbAddress));
  392. //
  393. // Copy the received data only if the receive was successful
  394. //
  395. if (pCcb->uchDlcStatus == STATUS_SUCCESS ||
  396. pCcb->uchDlcStatus == LLC_STATUS_LOST_DATA_INADEQUATE_SPACE) {
  397. //
  398. // We must complete here all receive data commands regardless
  399. // if they have a DOS appendage. Zero value in appendage
  400. // means, that we don't call appendage routine.
  401. // (interrupt vectors don't usually include executable code).
  402. //
  403. if (pRcvParms->dpCompletionFlag != 0) {
  404. setPostRoutine(pRcvParms->dpCompletionFlag);
  405. setES(HIWORD(pRcvParms->dpOriginalCcbAddress));
  406. setBX(LOWORD(pRcvParms->dpOriginalCcbAddress));
  407. setAX((WORD)pCcb->uchDlcStatus);
  408. }
  409. } else {
  410. setPostRoutine(0);
  411. }
  412. pDosCcb->uchDlcStatus = pCcb->uchDlcStatus;
  413. cancelledReceive = pCcb;
  414. } else {
  415. //
  416. // On entry to the command completion appendage, the following
  417. // are set:
  418. //
  419. // ES:BX = CCB address of the completed command.
  420. // CX = adapter number
  421. // AL = return code from CCB_RETCODE
  422. // AH = 0x00
  423. //
  424. pFlatCcbAddr = DOS_PTR_TO_FLAT(pCcb);
  425. setES(HIWORD(pCcb));
  426. setBX(LOWORD(pCcb));
  427. setCX((WORD)pFlatCcbAddr->uchAdapterNumber);
  428. setAX((WORD)pFlatCcbAddr->uchDlcStatus);
  429. setPostRoutine(pReadParms->ulNotificationFlag);
  430. IF_DEBUG(CRITICAL) {
  431. CRITDUMP(("COMMAND_COMPLETION: ANR=%04x:%04x CCB=%04x:%04x Type=%02x Status=%02x Adapter=%04x\n",
  432. HIWORD(pReadParms->ulNotificationFlag),
  433. LOWORD(pReadParms->ulNotificationFlag),
  434. getES(),
  435. getBX(),
  436. pFlatCcbAddr->uchDlcCommand,
  437. getAL(),
  438. getCX()
  439. ));
  440. }
  441. }
  442. break;
  443. case LLC_EVENT_TRANSMIT_COMPLETION:
  444. //
  445. // Event 0x02
  446. //
  447. IF_DEBUG(DLC_ASYNC) {
  448. DPUT("VrDlcHwInterrupt: LLC_EVENT_TRANSMIT_COMPLETION\n");
  449. }
  450. //
  451. // Map first CCB pointer and its parameter table to 32-bit addr space
  452. //
  453. pCcb = pReadParms->Type.Event.pCcbCompletionList;
  454. pFlatCcbAddr = (PLLC_CCB)DOS_PTR_TO_FLAT(pCcb);
  455. pParms = (PLLC_DOS_PARMS)DOS_PTR_TO_FLAT(READ_DWORD(&pFlatCcbAddr->u.pParameterTable));
  456. IF_DEBUG(TX_COMPLETE) {
  457. DPUT3("VrDlcHwInterrupt: pCcb=%x pFlatCcbAddr=%x pParms=%x\n",
  458. pCcb,
  459. pFlatCcbAddr,
  460. pParms
  461. );
  462. }
  463. //
  464. // there may be several completed transmit CCBs chained together
  465. //
  466. if (--pReadParms->Type.Event.usCcbCount) {
  467. //
  468. // RLF 09/24/92
  469. //
  470. // If there are multiple completions linked together then we leave
  471. // this READ CCB at the head of the queue. Decrement the number of
  472. // CCBs left and update the pointer to the next one
  473. //
  474. pReadParms->Type.Event.pCcbCompletionList = (PLLC_CCB)READ_DWORD(&pFlatCcbAddr->pNext);
  475. WRITE_DWORD(&pFlatCcbAddr->pNext, 0);
  476. pReadCcb = NULL;
  477. GetNewEvent = TRUE;
  478. IF_DEBUG(DLC_ASYNC) {
  479. DPUT2("VrDlcHwInterrupt: next Tx completion: %04x:%04x\n",
  480. HIWORD(pReadParms->Type.Event.pCcbCompletionList),
  481. LOWORD(pReadParms->Type.Event.pCcbCompletionList)
  482. );
  483. }
  484. #if DBG
  485. reasonCode = 'T';
  486. reasonCount = pReadParms->Type.Event.usCcbCount;
  487. #endif
  488. }
  489. //
  490. // The second transmit queue must be returned to the buffer pool IF the
  491. // transmit was successful
  492. //
  493. if (pFlatCcbAddr->uchDlcStatus == LLC_STATUS_SUCCESS && READ_DWORD(&pParms->Transmit.pXmitQueue2)) {
  494. IF_DEBUG(DLC_ASYNC) {
  495. DPUT2("VrDlcHwInterrupt: freeing XmitQueue2 @ %04x:%04x\n",
  496. GET_SEGMENT(&pParms->Transmit.pXmitQueue2),
  497. GET_OFFSET(&pParms->Transmit.pXmitQueue2)
  498. );
  499. }
  500. //
  501. // p2-47 IBM LAN Tech Ref:
  502. //
  503. // "Buffers in XMIT_QUEUE_TWO are freed by the adapter support
  504. // software if the transmission is successful (return code is
  505. // zero)."
  506. //
  507. FreeBuffers(GET_POOL_INDEX(pFlatCcbAddr->uchAdapterNumber,
  508. READ_WORD(&pParms->Transmit.usStationId)
  509. ),
  510. (DPLLC_DOS_BUFFER)READ_DWORD(&pParms->Transmit.pXmitQueue2),
  511. &cLeft
  512. );
  513. IF_DEBUG(DLC_ASYNC) {
  514. DPUT1("VrDlcHwInterrupt: after FreeBuffers: %d buffers left\n", cLeft);
  515. // DUMPCCB(pFlatCcbAddr,
  516. // TRUE, // DumpAll
  517. // FALSE, // CcbIsInput
  518. // TRUE, // IsDos
  519. // HIWORD(pCcb), // Segment
  520. // LOWORD(pCcb) // Offset
  521. // );
  522. }
  523. //
  524. // p3-105 IBM LAN Tech Ref:
  525. //
  526. // "Before taking an appendage exit or posting completion, the
  527. // buffers in this queue are returned to the SAP buffer pool
  528. // and this field set to zero upon command completion if the
  529. // return code equals zero (X'00)."
  530. //
  531. WRITE_DWORD(&pParms->Transmit.pXmitQueue2, 0);
  532. }
  533. //
  534. // this is a real asynchronous notification - we must asynchronously
  535. // call a DOS appendage routine - set up registers:
  536. //
  537. // ES:BX = completed (transmit) CCB
  538. // CX = adapter number
  539. // AL = return status
  540. // AH = 0x00
  541. //
  542. setPostRoutine(pReadParms->ulNotificationFlag);
  543. setES(HIWORD(pCcb));
  544. setBX(LOWORD(pCcb));
  545. setCX((WORD)pFlatCcbAddr->uchAdapterNumber);
  546. setAX((WORD)pFlatCcbAddr->uchDlcStatus);
  547. IF_DEBUG(CRITICAL) {
  548. CRITDUMP(("TRANSMIT_COMPLETION: ANR=%04x:%04x CCB=%04x:%04x Type=%02x Status=%02x Adapter=%04x\n",
  549. HIWORD(pReadParms->ulNotificationFlag),
  550. LOWORD(pReadParms->ulNotificationFlag),
  551. getES(),
  552. getBX(),
  553. pFlatCcbAddr->uchDlcCommand,
  554. getAL(),
  555. getCX()
  556. ));
  557. }
  558. break;
  559. case LLC_EVENT_RECEIVE_DATA:
  560. //
  561. // Event 0x04
  562. //
  563. IF_DEBUG(DLC_ASYNC) {
  564. DPUT("VrDlcHwInterrupt: LLC_EVENT_RECEIVE_DATA\n");
  565. //
  566. // dump the NT extended RECEIVE + parms
  567. //
  568. DUMPCCB((PLLC_CCB)(pReadParms->ulNotificationFlag),
  569. TRUE, // DumpAll
  570. FALSE, // CcbIsInput
  571. FALSE, // IsDos
  572. 0, // Segment
  573. 0 // Offset
  574. );
  575. }
  576. /***********************************************************************
  577. picture this, if you will
  578. NT READ CCB
  579. +---------+
  580. | |
  581. | | NT RECEIVE CCB
  582. | |
  583. | | +---------+
  584. | | | |
  585. | | | |
  586. | | | |
  587. | u.pParameterTable-+ | |
  588. +---------+ | | |
  589. | | |
  590. | | |
  591. | | u.pParameterTable-+
  592. | +---------+ |
  593. v ^ |
  594. +---------+ | |
  595. NT READ parms | | | |
  596. | | | v
  597. | | | +---------+ EXTENDED NT
  598. |ulNotificationFlag-+ | | RECEIVE PARMS
  599. | | | |
  600. |pFirstBuffer--+ | |
  601. | | | | |
  602. +---------+ | | |
  603. | | |
  604. | | |
  605. | |dpOrigininalCcbAddress
  606. | +---------+ |
  607. v |
  608. +---------+ |
  609. | | |
  610. first NT receive frame | | v
  611. | | +---------+
  612. | | DOS RECEIVE | |
  613. | | CCB | |
  614. | | | |
  615. | | | |
  616. | | | |
  617. +---------+ | |
  618. | |
  619. +---------- |
  620. | +---------+
  621. |
  622. |
  623. |
  624. v
  625. +---------+
  626. DOS RECEIVE parms | |
  627. | |
  628. | |
  629. | |
  630. | |
  631. | |
  632. | - - - - -> DOS
  633. | | receive
  634. | | data
  635. +---------+ buffers
  636. to avoid the DLC device driver writing to the DOS RECEIVE parameter
  637. table (because the NT RECEIVE parameter table is larger, and the
  638. driver will corrupt DOS memory if we give it a DOS RECEIVE CCB), the
  639. RECEIVE CCB is actually an NT RECEIVE CCB. The RECEIVE parameters are
  640. in an extended table which contains the DOS address of the original
  641. DOS RECEIVE CCB. When completing a DOS RECEIVE, we have to copy the
  642. data from the NT frame to DOS buffers and put the address of the DOS
  643. buffers in the DOS RECEIVE parameter table then call the DOS receive
  644. data appendage
  645. ***********************************************************************/
  646. pDosCcb = (PLLC_DOS_CCB)pReadParms->ulNotificationFlag;
  647. dpDosCcb = ((PLLC_DOS_RECEIVE_PARMS_EX)pDosCcb->u.pParms)->dpOriginalCcbAddress;
  648. pDosCcb = (PLLC_DOS_CCB)DOS_PTR_TO_FLAT(dpDosCcb);
  649. pParms = (PLLC_DOS_PARMS)READ_FAR_POINTER(&pDosCcb->u.pParms);
  650. pNtFrame = pReadParms->Type.Event.pReceivedFrame;
  651. stationId = pNtFrame->Contiguous.usStationId;
  652. sapNumber = SAP_ID(stationId);
  653. IF_DEBUG(RX_DATA) {
  654. DPUT3("VrDlcHwInterrupt: pNtFrame=%x pDosCcb=%x pParms=%x\n",
  655. pNtFrame, pDosCcb, pParms);
  656. }
  657. //
  658. // call ProcessReceiveFrame to find out what to do with this frame. If
  659. // we accepted it then we can free the READ CCB and received NT buffer
  660. // else we have to indicate local busy. N.B. The READ CCB pointed to
  661. // by pReadCcb may well be a different READ CCB when this function
  662. // returns
  663. //
  664. indication = ProcessReceiveFrame(&pReadCcb, pDosCcb, pParms, &Status);
  665. switch (indication) {
  666. case INDICATE_RECEIVE_FRAME:
  667. //
  668. // we have an I-Frame to indicate to the VDM. If we got the I-Frame
  669. // from the deferred queue then generate another h/w interrupt
  670. //
  671. IF_DEBUG(RX_DATA) {
  672. DPUT("INDICATE_RECEIVE_FRAME\n");
  673. }
  674. //
  675. // set up the registers for the data received appendage:
  676. //
  677. // DS:SI = RECEIVE CCB
  678. // ES:BX = received data buffer (Buffer 1)
  679. // CX = adapter number
  680. // AL = return code - STATUS_SUCCESS
  681. // AH = 0x00
  682. //
  683. setDS(HIWORD(dpDosCcb));
  684. setSI(LOWORD(dpDosCcb));
  685. setES(GET_SEGMENT(&pParms->Receive.pFirstBuffer));
  686. setBX(GET_OFFSET(&pParms->Receive.pFirstBuffer));
  687. setAX((WORD)LLC_STATUS_SUCCESS);
  688. setCX((WORD)pReadCcb->uchAdapterNumber);
  689. setPostRoutine(READ_DWORD(&pParms->Receive.ulReceiveExit));
  690. IF_DEBUG(CRITICAL) {
  691. CRITDUMP(("DATA_COMPLETION: ANR=%04x:%04x CCB=%04x:%04x Type=%02x Status=%02x Adapter=%04x\n",
  692. HIWORD(READ_DWORD(&pParms->Receive.ulReceiveExit)),
  693. LOWORD(READ_DWORD(&pParms->Receive.ulReceiveExit)),
  694. getDS(),
  695. getSI(),
  696. pDosCcb->uchDlcCommand,
  697. getAL(),
  698. getCX()
  699. ));
  700. }
  701. IF_DEBUG(ASYNC_EVENT) {
  702. DUMPCCB(POINTER_FROM_WORDS(getDS(), getSI()),
  703. TRUE, // DumpAll
  704. FALSE, // CcbIsInput
  705. TRUE, // IsDos
  706. getDS(),// Segment
  707. getSI() // Offset
  708. );
  709. }
  710. IF_DEBUG(RX_DATA) {
  711. DPUT5("VrDlcHwInterrupt: received data @ %04x:%04x, CCB @ %04x:%04x. status=%02x\n",
  712. getES(),
  713. getBX(),
  714. getDS(),
  715. getSI(),
  716. getAL()
  717. );
  718. }
  719. break;
  720. case INDICATE_LOCAL_BUSY:
  721. //
  722. // we have an I-Frame to receive, but no buffers. We have put NT
  723. // DLC into LOCAL BUSY (Buffer) state for this link, we must tell
  724. // the VDM that we are in LOCAL BUSY state
  725. //
  726. IF_DEBUG(RX_DATA) {
  727. DPUT("INDICATE_LOCAL_BUSY\n");
  728. }
  729. //
  730. // generate a DLC Status Change event & status table
  731. //
  732. iStation = iCurrentTempStatus++ & 7;
  733. RtlZeroMemory((LPBYTE)&lpVdmWindow->aStatusTables[iStation], sizeof(struct _DOS_DLC_STATUS));
  734. WRITE_WORD(&((PDOS_DLC_STATUS)&lpVdmWindow->aStatusTables[iStation])->usStationId, stationId);
  735. WRITE_WORD(&((PDOS_DLC_STATUS)&lpVdmWindow->aStatusTables[iStation])->usDlcStatusCode, LLC_INDICATE_LOCAL_STATION_BUSY);
  736. //
  737. // Set the registers for DLC status change event: ES:BX point to the
  738. // DLC status table containing the additional information; AX contains
  739. // the status code; CX contains the adapter number; SI contains the
  740. // value specified for USER_STAT_VALUE in the DLC.OPEN.SAP
  741. //
  742. newAddress = NEW_DOS_ADDRESS(dpVdmWindow, &lpVdmWindow->aStatusTables[iStation]);
  743. setES(HIWORD(newAddress));
  744. setBX(LOWORD(newAddress));
  745. setAX(LLC_INDICATE_LOCAL_STATION_BUSY);
  746. setCX((WORD)adapterNumber);
  747. setSI(Adapters[adapterNumber].UserStatusValue[sapNumber]);
  748. setPostRoutine(Adapters[adapterNumber].DlcStatusChangeAppendage[sapNumber]);
  749. IF_DEBUG(STATUS_CHANGE) {
  750. DPUT5("VrDlcHwInterrupt: Status Change info: ES:BX=%04x:%04x, AX=%04x, CX=%04x, SI=%04x\n",
  751. getES(),
  752. getBX(),
  753. getAX(),
  754. getCX(),
  755. getSI()
  756. );
  757. DPUT4("VrDlcHwInterrupt: Status Change Exit = %04x:%04x, StationId=%04x, StatusCode=%04x\n",
  758. HIWORD(Adapters[adapterNumber].DlcStatusChangeAppendage[sapNumber]),
  759. LOWORD(Adapters[adapterNumber].DlcStatusChangeAppendage[sapNumber]),
  760. ((PDOS_DLC_STATUS)&lpVdmWindow->aStatusTables[iStation])->usStationId,
  761. ((PDOS_DLC_STATUS)&lpVdmWindow->aStatusTables[iStation])->usDlcStatusCode
  762. );
  763. }
  764. IF_DEBUG(CRITICAL) {
  765. CRITDUMP(("LOCAL_BUSY: ES:BX=%04x:%04x, AX=%04x, CX=%04x, SI=%04x\n"
  766. "Status Change Exit = %04x:%04x, StationId=%04x, StatusCode=%04x\n",
  767. getES(),
  768. getBX(),
  769. getAX(),
  770. getCX(),
  771. getSI(),
  772. HIWORD(Adapters[adapterNumber].DlcStatusChangeAppendage[sapNumber]),
  773. LOWORD(Adapters[adapterNumber].DlcStatusChangeAppendage[sapNumber]),
  774. ((PDOS_DLC_STATUS)&lpVdmWindow->aStatusTables[iStation])->usStationId,
  775. ((PDOS_DLC_STATUS)&lpVdmWindow->aStatusTables[iStation])->usDlcStatusCode
  776. ));
  777. }
  778. break;
  779. case INDICATE_COMPLETE_RECEIVE:
  780. //
  781. // a non I-Frame cannot be completed due to insufficient buffers.
  782. // We will discard the frame, but we have to complete the
  783. // outstanding RECEIVE, which means CANCELLING the outstanding
  784. // NT RECEIVE (assuming that it didn't complete due to real LOCAL
  785. // BUSY state)
  786. //
  787. IF_DEBUG(RX_DATA) {
  788. DPUT("INDICATE_COMPLETE_RECEIVE\n");
  789. }
  790. //
  791. // cancel the NT RECEIVE
  792. //
  793. ReceiveCancel(pReadCcb->uchAdapterNumber,
  794. pReadParms->ulNotificationFlag
  795. );
  796. //
  797. // we must delete any pending receives which reference this RECEIVE CCB
  798. //
  799. cancelledReceive = (PLLC_CCB)pReadParms->ulNotificationFlag;
  800. //
  801. // set up the registers to call the command completion appendage
  802. // for the failed RECEIVE command
  803. //
  804. setES(HIWORD(dpDosCcb));
  805. setBX(LOWORD(dpDosCcb));
  806. setAL((UCHAR)Status);
  807. setPostRoutine(READ_DWORD(&pDosCcb->ulCompletionFlag));
  808. WRITE_BYTE(&pDosCcb->uchDlcStatus, Status);
  809. IF_DEBUG(CRITICAL) {
  810. CRITDUMP(("COMPLETE_RECEIVE: ANR=%04x:%04x CCB=%04x:%04x Type=%02x Status=%02x\n",
  811. HIWORD(READ_DWORD(&pDosCcb->ulCompletionFlag)),
  812. LOWORD(READ_DWORD(&pDosCcb->ulCompletionFlag)),
  813. getES(),
  814. getBX(),
  815. pDosCcb->uchDlcCommand,
  816. getAL()
  817. ));
  818. }
  819. break;
  820. }
  821. //
  822. // if we have a non-NULL READ CCB pointer then we have to free the
  823. // NT buffer and free the CCB
  824. //
  825. if (pReadCcb) {
  826. BufferFree(pReadCcb->uchAdapterNumber,
  827. pReadCcb->u.pParameterTable->Read.Type.Event.pReceivedFrame,
  828. &buffersLeft
  829. );
  830. }
  831. break;
  832. case LLC_EVENT_STATUS_CHANGE:
  833. //
  834. // Event 0x08, 0x10, 0x20 & 0x40?
  835. //
  836. IF_DEBUG(DLC_ASYNC) {
  837. DPUT("VrDlcHwInterrupt: LLC_EVENT_STATUS_CHANGE\n");
  838. }
  839. //
  840. // This heuristics tries to minimize the risk of overwritten
  841. // DLC status indications and minimize the needed DOS memory.
  842. // The first 5 link stations on each adapter has permanent
  843. // status tables => apps requiring permanent status tables
  844. // works fine up to 5 link connections. The first opened
  845. // adapter may use also slots 5 - 10 for permanent status tables,
  846. // if there is only one opened adapter. The last 5 slots
  847. // are always reserved to routing of the status indications.
  848. // This implementation takes 300 bytes DOS memory. The full
  849. // support of all possible 510 link stations would take 10 kB memory.
  850. // It's better save 10 kB DOS memory and have no configuration
  851. // parameters, and accept a very low error probability
  852. //
  853. //
  854. // decrement the station ID by 1 since we always start at station 1
  855. // (0 is the SAP)
  856. //
  857. iStation = (pReadParms->Type.Status.usStationId & 0x00ff) - 1;
  858. if (OpenedAdapters == 1 && iStation < DOS_DLC_STATUS_PERM_SLOTS) {
  859. //
  860. // We have only one adapter, use slots 1 - 10 for permanent tables
  861. //
  862. ; // NOP
  863. } else if (OpenedAdapters == 2 && iStation < (DOS_DLC_STATUS_PERM_SLOTS / 2)) {
  864. //
  865. // We have two adapters, use 5 slots for each adapter
  866. // to have permanent status tables at least for the
  867. // first station ids
  868. //
  869. iStation += (DOS_DLC_STATUS_PERM_SLOTS / 2) * adapterNumber;
  870. } else {
  871. //
  872. // Use the slots reserve for temporary DLC status indications
  873. //
  874. iStation = iCurrentTempStatus;
  875. iCurrentTempStatus = (iCurrentTempStatus + 1) % DOS_DLC_STATUS_TEMP_SLOTS;
  876. }
  877. //
  878. // We must copy the DLC status block to the VDM address space. Note we
  879. // actually use 3 bytes more than the CCB1 status tables, but since we
  880. // are using pointers, it shouldn't matter
  881. //
  882. RtlCopyMemory((LPBYTE)&lpVdmWindow->aStatusTables[iStation],
  883. &pReadParms->Type.Status,
  884. sizeof(struct _DOS_DLC_STATUS)
  885. );
  886. //
  887. // Set the registers for DLC status change event: ES:BX point to the
  888. // DLC status table containing the additional information; AX contains
  889. // the status code; CX contains the adapter number; SI contains the
  890. // value specified for USER_STAT_VALUE in the DLC.OPEN.SAP
  891. //
  892. newAddress = NEW_DOS_ADDRESS(dpVdmWindow, &lpVdmWindow->aStatusTables[iStation]);
  893. setES(HIWORD(newAddress));
  894. setBX(LOWORD(newAddress));
  895. setAX(pReadParms->Type.Status.usDlcStatusCode);
  896. setCX((WORD)pReadCcb->uchAdapterNumber);
  897. setSI(pReadParms->Type.Status.usUserStatusValue);
  898. setPostRoutine(pReadParms->ulNotificationFlag);
  899. IF_DEBUG(CRITICAL) {
  900. CRITDUMP(("STATUS_CHANGE: ANR=%04x:%04x Status Table=%04x:%04x Status=%04x Adapter=%04x\n",
  901. HIWORD(pReadParms->ulNotificationFlag),
  902. LOWORD(pReadParms->ulNotificationFlag),
  903. getES(),
  904. getBX(),
  905. getAX(),
  906. getCX()
  907. ));
  908. }
  909. IF_DEBUG(STATUS_CHANGE) {
  910. DPUT5("VrDlcHwInterrupt: Status Change info: ES:BX=%04x:%04x, AX=%04x, CX=%04x, SI=%04x\n",
  911. getES(),
  912. getBX(),
  913. getAX(),
  914. getCX(),
  915. getSI()
  916. );
  917. DPUT4("VrDlcHwInterrupt: Status Change Exit = %04x:%04x, StationId=%04x, StatusCode=%04x\n",
  918. HIWORD(pReadParms->ulNotificationFlag),
  919. LOWORD(pReadParms->ulNotificationFlag),
  920. ((PDOS_DLC_STATUS)&lpVdmWindow->aStatusTables[iStation])->usStationId,
  921. ((PDOS_DLC_STATUS)&lpVdmWindow->aStatusTables[iStation])->usDlcStatusCode
  922. );
  923. }
  924. break;
  925. default:
  926. //
  927. // This should be impossible!
  928. //
  929. DPUT("VrDlcHwInterrupt: this is an impossible situation!\n");
  930. IF_DEBUG(CRITICAL) {
  931. CRITDUMP(("VrDlcHwInterrupt: this is an impossible situation!\n"));
  932. }
  933. break;
  934. }
  935. //
  936. // if pReadCcb is not NULL, free the READ CCB & parameter table. If it is
  937. // NULL then this means we did not complete processing of the READ CCB -
  938. // it has more than 1 transmit CCB chained to it or it is a received frame
  939. // which is on the deferred queue
  940. //
  941. if (pReadCcb) {
  942. if (frameType == CURRENT) {
  943. //
  944. // RLF 09/24/92
  945. //
  946. // Remove the READ CCB from the head of the queue using GetEvent
  947. // before freeing it. We got the pointer to the completed READ CCB
  948. // from PeekEvent which left the READ at the head of the queue in
  949. // case we needed to access the same completed READ for multiple
  950. // transmit completions
  951. //
  952. #if DBG
  953. //
  954. // ensure that the CCB that we are removing from the head of the queue
  955. // is the same one returned by PeekEvent
  956. //
  957. {
  958. PLLC_CCB CcbAtQueueHead;
  959. CcbAtQueueHead = GetEvent(adapterNumber);
  960. if (pReadCcb != CcbAtQueueHead) {
  961. DbgPrint("VrDlcHwInterrupt: "
  962. "*** ERROR: GetEvent CCB (%x) != PeekEvent CCB (%x) ***\n",
  963. CcbAtQueueHead,
  964. pReadCcb
  965. );
  966. DbgBreakPoint();
  967. }
  968. }
  969. #else
  970. GetEvent(adapterNumber);
  971. #endif
  972. } else {
  973. //
  974. // READ CCB is at head of deferred queue. Remove it. If there are
  975. // any more READ CCBs queued on this deferred queue then we have
  976. // to generate an extra h/w interrupt
  977. //
  978. #if DBG
  979. //
  980. // ensure that the CCB that we are removing from the head of the queue
  981. // is the same one returned by FindCompletedRead
  982. //
  983. {
  984. PLLC_CCB CcbAtQueueHead;
  985. CcbAtQueueHead = RemoveDeferredReceive(adapterNumber, stationId, &GetNewEvent);
  986. if (pReadCcb != CcbAtQueueHead) {
  987. DbgPrint("VrDlcHwInterrupt: "
  988. "*** ERROR: GetEvent CCB (%x) != PeekEvent CCB (%x) ***\n",
  989. CcbAtQueueHead,
  990. pReadCcb
  991. );
  992. DbgBreakPoint();
  993. }
  994. if (GetNewEvent) {
  995. reasonCode = 'R';
  996. reasonCount = Adapters[adapterNumber].LocalBusyInfo[LINK_ID(stationId)].Depth;
  997. }
  998. }
  999. #else
  1000. RemoveDeferredReceive(adapterNumber, stationId, &GetNewEvent);
  1001. #endif
  1002. }
  1003. LocalFree((HLOCAL)pReadCcb);
  1004. IF_DEBUG(DLC_ALLOC) {
  1005. DPUT1("FREE: freed block @ %x\n", pReadCcb);
  1006. }
  1007. }
  1008. //
  1009. // RLF 09/24/92
  1010. //
  1011. // Re-ordered issuing h/w int & removing freeing READ CCB since now that
  1012. // we peek first & remove later, we must be sure that a fully completed
  1013. // READ CCB is removed from the queue before we request a new h/w
  1014. // interrupt. I am not expecting this to be re-entered, but who knows
  1015. // what might happen on a MP machine?
  1016. //
  1017. if (GetNewEvent) {
  1018. //
  1019. // The event generates more than one appendage call. We need a new
  1020. // h/w interrupt for each extra appendage call
  1021. //
  1022. #if DBG
  1023. IF_DEBUG(DLC_ASYNC) {
  1024. DPUT2("*** Calling ica_hw_interrupt from within ISR. Cause = %d %s ***\n",
  1025. reasonCount,
  1026. reasonCode == 'T' ? "multiple transmits" : "deferred I-Frames"
  1027. );
  1028. }
  1029. IF_DEBUG(CRITICAL) {
  1030. CRITDUMP(("*** Calling ica_hw_interrupt from within ISR. Cause = %d %s ***\n",
  1031. reasonCount,
  1032. reasonCode == 'T' ? "multiple transmits" : "deferred I-Frames"
  1033. ));
  1034. }
  1035. #endif
  1036. IssueHardwareInterrupt();
  1037. }
  1038. //
  1039. // if an NT RECEIVE CCB was completed or terminated, remove any pending
  1040. // READ CCBs completed by received data events which reference the terminated
  1041. // RECEIVE
  1042. //
  1043. if (cancelledReceive) {
  1044. RemoveDeadReceives(cancelledReceive);
  1045. LocalFree(cancelledReceive);
  1046. IF_DEBUG(DLC_ALLOC) {
  1047. DPUT1("FREE: freed block @ %x\n", cancelledReceive);
  1048. }
  1049. }
  1050. //
  1051. // acknowledge this hardware interrupt, meaning decrement the interrupt
  1052. // counter and issue a new request if we've got interrupts outstanding
  1053. //
  1054. AcknowledgeHardwareInterrupt();
  1055. //
  1056. // return TRUE to indicate that we have accepted the hw interrupt
  1057. //
  1058. return TRUE;
  1059. }
  1060. PLLC_CCB
  1061. FindCompletedRead(
  1062. OUT READ_FRAME_TYPE* pFrameType
  1063. )
  1064. /*++
  1065. Routine Description:
  1066. Finds the next READ CCB to process - first looks at the event queue of
  1067. current completed CCBs, then at the deferred I-Frame queue.
  1068. This routine attempts to bounce between both adapters if they are both
  1069. active, in order not to only service the most active adapter and ignore
  1070. for a long period of time pending completed CCBs on the less active
  1071. adapter
  1072. NOTE: we only return a deferred I-Frame when the app has issued
  1073. DLC.FLOW.CONTROL against the station
  1074. Arguments:
  1075. pFrameType - returns the type of event found - CURRENT (event READ is at
  1076. head of EventQueue) or DEFERRED (event READ is at head of
  1077. a LOCAL_BUSY_INFO queue)
  1078. Return Value:
  1079. PLLC_CCB
  1080. pointer to completed READ CCB. The CCB is NOT DEQUEUED - the caller
  1081. must do that when the event has been completely disposed of to the VDM
  1082. --*/
  1083. {
  1084. DWORD i;
  1085. PLLC_CCB pCcb = NULL;
  1086. static BYTE ThisAdapter = 0;
  1087. for (i = 0; !Adapters[ThisAdapter].IsOpen && i < DOS_DLC_MAX_ADAPTERS; ++i) {
  1088. ThisAdapter = (ThisAdapter + 1) & (DOS_DLC_MAX_ADAPTERS - 1);
  1089. }
  1090. if (i == DOS_DLC_MAX_ADAPTERS) {
  1091. //
  1092. // no adapters alive?
  1093. //
  1094. IF_DEBUG(DLC_ASYNC) {
  1095. DPUT("*** FindCompletedRead: no open adapters??? ***\n");
  1096. }
  1097. return NULL;
  1098. }
  1099. //
  1100. // RLF 09/24/92
  1101. //
  1102. // Changed GetEvent to PeekEvent: if there are multiple chained completed
  1103. // CCBs (transmit+?) then we have to leave the READ CCB @ the head of the
  1104. // queue until we have generated call-backs for all chained completions.
  1105. // Only when there are no more completions can we remove the READ CCB from
  1106. // the queue (using GetEvent)
  1107. //
  1108. if (pCcb = PeekEvent(ThisAdapter)) {
  1109. *pFrameType = CURRENT;
  1110. } else {
  1111. //
  1112. // see if there are any deferred I-Frames
  1113. //
  1114. IF_DEBUG(CRITSEC) {
  1115. DPUT1("FindCompletedRead: ENTERING Adapters[%d].LocalBusyCritSec\n",
  1116. ThisAdapter
  1117. );
  1118. }
  1119. EnterCriticalSection(&Adapters[ThisAdapter].LocalBusyCritSec);
  1120. //
  1121. // DeferredReceives is a reference count of link stations in emulated
  1122. // local-busy(buffer) state. They can be BUSY or CLEARING. We are only
  1123. // interested in CLEARING: this means that the app has issued
  1124. // DLC.FLOW.CONTROL to us & presumably has returned some buffers via
  1125. // BUFFER.FREE (if not, we just go back to BUSY state)
  1126. //
  1127. if (Adapters[ThisAdapter].DeferredReceives) {
  1128. for (i = Adapters[ThisAdapter].FirstIndex;
  1129. i <= Adapters[ThisAdapter].LastIndex; ++i) {
  1130. if (Adapters[ThisAdapter].LocalBusyInfo[i].State == CLEARING) {
  1131. if (pCcb = Adapters[ThisAdapter].LocalBusyInfo[i].Queue) {
  1132. *pFrameType = DEFERRED;
  1133. break;
  1134. }
  1135. }
  1136. }
  1137. }
  1138. IF_DEBUG(CRITSEC) {
  1139. DPUT1("FindCompletedRead: LEAVING Adapters[%d].LocalBusyCritSec\n",
  1140. ThisAdapter
  1141. );
  1142. }
  1143. LeaveCriticalSection(&Adapters[ThisAdapter].LocalBusyCritSec);
  1144. }
  1145. IF_DEBUG(DLC_ASYNC) {
  1146. DPUT2("FindCompletedRead: returning READ CCB @ %08x type is %s\n",
  1147. pCcb,
  1148. *pFrameType == DEFERRED ? "DEFERRED" : "CURRENT"
  1149. );
  1150. }
  1151. //
  1152. // next time in, try the other adapter first
  1153. //
  1154. ThisAdapter = (ThisAdapter + 1) & (DOS_DLC_MAX_ADAPTERS - 1);
  1155. return pCcb;
  1156. }
  1157. INDICATION
  1158. ProcessReceiveFrame(
  1159. IN OUT PLLC_CCB* ppCcb,
  1160. IN LLC_DOS_CCB UNALIGNED * pDosCcb,
  1161. IN LLC_DOS_PARMS UNALIGNED * pDosParms,
  1162. OUT LLC_STATUS* Status
  1163. )
  1164. /*++
  1165. Routine Description:
  1166. Determines what to do with a received frame. We try to copy the received
  1167. frame to DOS buffers, but if we have insufficient buffers then we must
  1168. queue or discard the frame. We queue the frame only if it is an I-Frame.
  1169. We must first check the queue of deferred received frames or else risk
  1170. getting out-of-sequence received data in the DOS client.
  1171. When this routine is called, *ppCcb points at one of:
  1172. 1. A deferred I-Frame READ CCB
  1173. 2. A current received data READ CCB, I-Frame or other MAC/data
  1174. NOTE: Deferred I-Frames that are for stations whose local-busy(buffer)
  1175. state has been cleared by the app take precedence over the event
  1176. at the head of the EventQueue
  1177. Arguments:
  1178. ppCcb - pointer to new READ CCB. On output, this will be a non-NULL
  1179. value if we are to free the CCB and the received NT frame
  1180. buffer. If we placed the read on the deferred receive queue
  1181. then we return a NULL
  1182. pDosCcb - pointer to DOS CCB
  1183. pDosParms - pointer to DOS RECEIVE parameter table. If we copy - entirely
  1184. or partially - a frame, the DOS buffer chain is linked to
  1185. the DOS RECEIVE parameter table at FIRST_BUFFER
  1186. Status - if the frame was copied, contains the status to return to the
  1187. DOS appendage:
  1188. LLC_STATUS_SUCCESS
  1189. The frame was completely copied to the DOS buffer(s)
  1190. LLC_STATUS_LOST_DATA_NO_BUFFERS
  1191. The frame could not be copied to the DOS buffers and
  1192. has been discarded. There were NO buffers available.
  1193. The frame was NOT an I-Frame
  1194. LLC_STATUS_LOST_DATA_INADEQUATE_SPACE
  1195. The frame has been partially copied to the DOS buffer(s),
  1196. but the rest of it had to be discarded, because not
  1197. enough DOS buffers were available. The frame was NOT an
  1198. I-Frame
  1199. Status is only meaningful when INDICATE_COMPLETE_RECEIVE is
  1200. returned
  1201. Return Value:
  1202. INDICATION
  1203. INDICATE_RECEIVE_FRAME
  1204. We copied the frame to DOS buffers. Free the CCB and NT buffer and
  1205. return the frame to DOS
  1206. INDICATE_LOCAL_BUSY
  1207. We couldn't copy the received I-Frame to DOS buffers because we don't
  1208. have enough of them. We have put the NT link station into LOCAL BUSY
  1209. (Buffer) state. Indicate local busy to the VDM
  1210. INDICATE_COMPLETE_RECEIVE
  1211. We couldn't copy the received non I-Frame to DOS buffers. Complete
  1212. the DOS RECEIVE command with an error
  1213. --*/
  1214. {
  1215. PLLC_CCB pCcb = *ppCcb;
  1216. PLLC_PARMS pParms = pCcb->u.pParameterTable;
  1217. PLLC_BUFFER pFrame = pParms->Read.Type.Event.pReceivedFrame;
  1218. UCHAR adapter = pCcb->uchAdapterNumber;
  1219. WORD stationId = pFrame->Contiguous.usStationId;
  1220. WORD numBufs;
  1221. WORD bufferSize;
  1222. WORD buffersAvailable;
  1223. WORD nLeft;
  1224. DPLLC_DOS_BUFFER dosBuffers;
  1225. INDICATION indication;
  1226. LLC_STATUS status;
  1227. DWORD flags;
  1228. IF_DEBUG(DLC_ASYNC) {
  1229. DPUT2("ProcessReceiveFrame: adapter=%d, stationId=%x\n", adapter, stationId);
  1230. }
  1231. //
  1232. // make sure we don't read or write bogus Adapter structure
  1233. //
  1234. ASSERT(adapter < DOS_DLC_MAX_ADAPTERS);
  1235. //
  1236. // we are NOT chaining receive frames. DOS receive frame processing assumes
  1237. // that we get one frame at a time. Assert that it is so
  1238. //
  1239. ASSERT(pFrame->Contiguous.pNextFrame == NULL);
  1240. //
  1241. // get the contiguous and break flags for CopyFrame based on the RECEIVE options
  1242. //
  1243. flags = ((pFrame->Contiguous.uchOptions & (LLC_CONTIGUOUS_MAC | LLC_CONTIGUOUS_DATA))
  1244. ? CF_CONTIGUOUS : 0)
  1245. | ((pFrame->Contiguous.uchOptions & LLC_BREAK) ? CF_BREAK : 0);
  1246. //
  1247. // calculate the number of buffers required to receive this frame
  1248. //
  1249. numBufs = CalculateBufferRequirement(adapter,
  1250. stationId,
  1251. pFrame,
  1252. pDosParms,
  1253. &bufferSize
  1254. );
  1255. //
  1256. // if the frame is an I-Frame then we have to perform these checks:
  1257. //
  1258. // 1. if there is already a deferred packet for this station ID/adapter
  1259. // then we must try to receive this before the frame in hand
  1260. //
  1261. // 2. if there aren't sufficient buffers for the request then we must
  1262. // queue this frame on the deferred queue (if it isn't there already)
  1263. // and indicate that we have a local busy (buffers) state to the DOS
  1264. // client
  1265. //
  1266. if (pFrame->Contiguous.uchMsgType == LLC_I_FRAME) {
  1267. IF_DEBUG(DLC_ASYNC) {
  1268. DPUT("ProcessReceiveFrame: I-Frame\n");
  1269. }
  1270. //
  1271. // try to allocate the required number of buffers as a chain - returns
  1272. // DOS pointers, ie unusable in flat data space. Note that if we have
  1273. // deferred receives then we may be able to satisfy the request now.
  1274. // We must allocate all the required buffers for an I-Frame, or none
  1275. //
  1276. status = GetBuffers(GET_POOL_INDEX(adapter, stationId),
  1277. numBufs,
  1278. &dosBuffers,
  1279. &nLeft,
  1280. FALSE,
  1281. NULL
  1282. );
  1283. if (status == LLC_STATUS_SUCCESS) {
  1284. //
  1285. // we managed to allocate the required number of DOS buffers. Copy
  1286. // the NT frame to the DOS buffers and set the indication to return
  1287. // the I-Frame and free the READ CCB and NT frame buffer
  1288. //
  1289. status = CopyFrame(pFrame,
  1290. dosBuffers,
  1291. READ_WORD(&pDosParms->Receive.usUserLength),
  1292. bufferSize,
  1293. flags
  1294. );
  1295. ASSERT(status == LLC_STATUS_SUCCESS);
  1296. indication = INDICATE_RECEIVE_FRAME;
  1297. } else {
  1298. //
  1299. // we couldn't get the required number of DOS buffers. We must
  1300. // queue this I-Frame (& any subsequent I-Frames received for this
  1301. // link station) on the deferred queue and indicate the local
  1302. // busy (buffers) state to the DOS client. Set pCcb to NULL to
  1303. // indicate that it mustn't be deallocated by the caller (it has
  1304. // already been deallocated in SetEmulatedLocalBusyState)
  1305. //
  1306. // We set the LOCAL BUSY (Buffer) state as quickly as possible: we
  1307. // don't want more than 1 I-Frame queued per link station if we
  1308. // can help it
  1309. //
  1310. SetEmulatedLocalBusyState(adapter, stationId);
  1311. pCcb = NULL;
  1312. indication = INDICATE_LOCAL_BUSY;
  1313. }
  1314. } else {
  1315. IF_DEBUG(DLC_ASYNC) {
  1316. DPUT("ProcessReceiveFrame: other MAC/DATA Frame\n");
  1317. }
  1318. //
  1319. // the frame is not an I-Frame. If we don't have sufficient buffers to
  1320. // receive it then we can discard it
  1321. //
  1322. status = GetBuffers(GET_POOL_INDEX(adapter, stationId),
  1323. numBufs,
  1324. &dosBuffers,
  1325. &nLeft,
  1326. TRUE,
  1327. &buffersAvailable
  1328. );
  1329. if (status == LLC_STATUS_SUCCESS) {
  1330. //
  1331. // we got some DOS buffers, but maybe not all requirement
  1332. //
  1333. if (buffersAvailable < numBufs) {
  1334. //
  1335. // set the status required to be returned to DOS in the completed
  1336. // RECEIVE CCB and set the CF_PARTIAL flag so that CopyFrame
  1337. // will know to terminate early
  1338. //
  1339. *Status = LLC_STATUS_LOST_DATA_INADEQUATE_SPACE;
  1340. flags |= CF_PARTIAL;
  1341. indication = INDICATE_COMPLETE_RECEIVE;
  1342. } else {
  1343. //
  1344. // we allocated all required DOS buffers for this frame
  1345. //
  1346. indication = INDICATE_RECEIVE_FRAME;
  1347. }
  1348. //
  1349. // copy the whole or partial frame
  1350. //
  1351. status = CopyFrame(pFrame,
  1352. dosBuffers,
  1353. READ_WORD(&pDosParms->Receive.usUserLength),
  1354. bufferSize,
  1355. flags
  1356. );
  1357. IF_DEBUG(DLC_ASYNC) {
  1358. DPUT1("ProcessReceiveFrame: CopyFrame (non-I-Frame) returns %x\n", status);
  1359. }
  1360. } else {
  1361. //
  1362. // no DOS buffers at all
  1363. //
  1364. *Status = LLC_STATUS_LOST_DATA_NO_BUFFERS;
  1365. indication = INDICATE_COMPLETE_RECEIVE;
  1366. }
  1367. }
  1368. //
  1369. // set the FIRST_BUFFER field in the DOS RECEIVE parameter table. This
  1370. // is only meaningful if we're going to complete the RECEIVE, with either
  1371. // success or failure status. Use WRITE_DWORD in case the parameter table
  1372. // is unaligned
  1373. //
  1374. WRITE_DWORD(&pDosParms->Receive.pFirstBuffer, dosBuffers);
  1375. //
  1376. // if we are returning DOS buffers, then return the BUFFERS_LEFT field
  1377. //
  1378. if (dosBuffers) {
  1379. PLLC_DOS_BUFFER pDosBuffer = (PLLC_DOS_BUFFER)DOS_PTR_TO_FLAT(dosBuffers);
  1380. WRITE_WORD(&pDosBuffer->Contiguous.cBuffersLeft, nLeft);
  1381. }
  1382. //
  1383. // set *ppCcb. If this contains non-NULL on return then the caller will
  1384. // deallocate the CCB and free the NT buffer(s)
  1385. //
  1386. *ppCcb = pCcb;
  1387. //
  1388. // return indication of action to take
  1389. //
  1390. return indication;
  1391. }
  1392. LOCAL_BUSY_STATE
  1393. QueryEmulatedLocalBusyState(
  1394. IN BYTE AdapterNumber,
  1395. IN WORD StationId
  1396. )
  1397. /*++
  1398. Routine Description:
  1399. Gets the current local-busy(buffer) state of the requested link station on
  1400. a particular adapter
  1401. Arguments:
  1402. AdapterNumber - which adapter
  1403. StationId - which link station
  1404. Return Value:
  1405. LOCAL_BUSY_STATE
  1406. NOT_BUSY
  1407. AdapterNumber/StationId has no deferred I-Frames
  1408. BUSY
  1409. AdapterNumber/StationId has deferred I-Frames and has not yet
  1410. received DLC.FLOW.CONTROL(local-busy(buffer), reset) from the
  1411. DOS DLC app
  1412. CLEARING
  1413. AdapterNumber/StationId has deferred I-Frames AND has received
  1414. DLC.FLOW.CONTROL(local-busy(buffer), reset) from the DOS DLC app
  1415. and is currently trying to unload I-Frames to DOS DLC app
  1416. --*/
  1417. {
  1418. LOCAL_BUSY_STATE state;
  1419. ASSERT(HIBYTE(StationId) != 0);
  1420. ASSERT(LOBYTE(StationId) != 0);
  1421. IF_DEBUG(CRITSEC) {
  1422. DPUT1("QueryEmulatedLocalBusyState: ENTERING Adapters[%d].LocalBusyCritSec\n",
  1423. AdapterNumber
  1424. );
  1425. }
  1426. EnterCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  1427. state = Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].State;
  1428. if (state == BUSY_BUFFER || state == BUSY_FLOW) {
  1429. state = BUSY;
  1430. }
  1431. IF_DEBUG(CRITSEC) {
  1432. DPUT1("QueryEmulatedLocalBusyState: LEAVING Adapters[%d].LocalBusyCritSec\n",
  1433. AdapterNumber
  1434. );
  1435. }
  1436. LeaveCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  1437. ASSERT(state == NOT_BUSY
  1438. || state == CLEARING
  1439. || state == BUSY
  1440. || state == BUSY_BUFFER
  1441. || state == BUSY_FLOW
  1442. );
  1443. IF_DEBUG(DLC_ASYNC) {
  1444. DPUT1("QueryEmulatedLocalBusyState: returning %s\n",
  1445. state == NOT_BUSY
  1446. ? "NOT_BUSY"
  1447. : state == CLEARING
  1448. ? "CLEARING"
  1449. : state == BUSY
  1450. ? "BUSY"
  1451. : state == BUSY_BUFFER
  1452. ? "BUSY_BUFFER"
  1453. : "BUSY_FLOW"
  1454. );
  1455. }
  1456. return state;
  1457. }
  1458. VOID
  1459. SetEmulatedLocalBusyState(
  1460. IN BYTE AdapterNumber,
  1461. IN WORD StationId
  1462. )
  1463. /*++
  1464. Routine Description:
  1465. Sets the emulated local-busy state to local-busy(buffer). If the state is
  1466. currently NOT_BUSY, sends a DLC.FLOW.CONTROL(local-busy(buffer), set) to
  1467. the DLC driver. In all cases sets the current state to BUSY
  1468. Called during processing of an I-Frame when we run out of DOS buffers into
  1469. which we receive the I-Frame. We can be processing a current I-Frame or a
  1470. deferred I-Frame at the time we run out of buffers: in the first instance
  1471. this routine sets local-busy(buffer) state for the first time; in the
  1472. second instance we regress into local-busy(buffer) state (BUSY) from the
  1473. CLEARING state. This will happen so long as we continue to run out of DOS
  1474. buffers
  1475. Arguments:
  1476. AdapterNumber - which adapter to set emulated local busy state for
  1477. StationId - which link station on AdapterNumber
  1478. Return Value:
  1479. None.
  1480. --*/
  1481. {
  1482. LOCAL_BUSY_STATE state;
  1483. DWORD link = LINK_ID(StationId);
  1484. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  1485. ASSERT(HIBYTE(StationId) != 0); // SAP can't be 0
  1486. ASSERT(LOBYTE(StationId) != 0); // Link Station can't be 0
  1487. IF_DEBUG(CRITSEC) {
  1488. DPUT1("SetEmulatedLocalBusyState: ENTERING Adapters[%d].LocalBusyCritSec\n",
  1489. AdapterNumber
  1490. );
  1491. }
  1492. EnterCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  1493. state = Adapters[AdapterNumber].LocalBusyInfo[link].State;
  1494. ASSERT(state == NOT_BUSY
  1495. || state == CLEARING
  1496. || state == BUSY
  1497. || state == BUSY_BUFFER
  1498. || state == BUSY_FLOW
  1499. );
  1500. //
  1501. // if the state of this link station is currently NOT_BUSY then we have
  1502. // to stop the DLC driver receiving I-Frames for this station. In all
  1503. // cases, put the READ CCB on the end of the deferred queue for this
  1504. // adapter/link station
  1505. //
  1506. Adapters[AdapterNumber].LocalBusyInfo[link].State = BUSY;
  1507. //
  1508. // if the previous state was NOT_BUSY then this is the first time this link
  1509. // station has gone into local-busy(buffer) state. Increment the deferred
  1510. // receive count (number of links in local-busy(buffer) state on this
  1511. // adapter) and send a flow control command to the DLC driver, disabling
  1512. // further I-Frame receives until we clear the backlog
  1513. //
  1514. if (state == NOT_BUSY) {
  1515. IF_DEBUG(DLC_ASYNC) {
  1516. DPUT2("SetEmulatedLocalBusyState: setting %d:%04x to BUSY from NOT_BUSY\n",
  1517. AdapterNumber,
  1518. StationId
  1519. );
  1520. }
  1521. ++Adapters[AdapterNumber].DeferredReceives;
  1522. //
  1523. // update the indexes to reduce search effort when looking for deferred
  1524. // receives
  1525. //
  1526. ASSERT(Adapters[AdapterNumber].FirstIndex <= Adapters[AdapterNumber].LastIndex);
  1527. if (Adapters[AdapterNumber].FirstIndex > link) {
  1528. Adapters[AdapterNumber].FirstIndex = link;
  1529. }
  1530. if (Adapters[AdapterNumber].LastIndex < link
  1531. || Adapters[AdapterNumber].LastIndex == NO_LINKS_BUSY) {
  1532. Adapters[AdapterNumber].LastIndex = link;
  1533. }
  1534. #if DBG
  1535. //ASSERT(DosDlcFlowControl(AdapterNumber, StationId, LLC_SET_LOCAL_BUSY_BUFFER) == LLC_STATUS_SUCCESS);
  1536. ASSERT(DlcFlowControl(AdapterNumber, StationId, LLC_SET_LOCAL_BUSY_USER) == LLC_STATUS_SUCCESS);
  1537. #else
  1538. //DosDlcFlowControl(AdapterNumber, StationId, LLC_SET_LOCAL_BUSY_BUFFER);
  1539. DlcFlowControl(AdapterNumber, StationId, LLC_SET_LOCAL_BUSY_USER);
  1540. #endif
  1541. } else {
  1542. IF_DEBUG(DLC_ASYNC) {
  1543. DPUT3("SetEmulatedLocalBusyState: setting %d:%04x to BUSY from %s\n",
  1544. AdapterNumber,
  1545. StationId,
  1546. state == CLEARING
  1547. ? "CLEARING"
  1548. : state == BUSY_BUFFER
  1549. ? "BUSY_BUFFER"
  1550. : state == BUSY_FLOW
  1551. ? "BUSY_FLOW"
  1552. : "???"
  1553. );
  1554. }
  1555. }
  1556. ASSERT(state != BUSY);
  1557. //
  1558. // add this READ CCB to the end of the deferred receive queue for this
  1559. // adapter/link station and any subsequent READ CCBs which completed with
  1560. // received I-Frames
  1561. //
  1562. DeferAllIFrames(AdapterNumber, StationId);
  1563. IF_DEBUG(DLC_ASYNC) {
  1564. DPUT5("SetEmulatedLocalBusyState(%d, %04x): Ref#=%d, First=%d, Last=%d\n",
  1565. AdapterNumber,
  1566. StationId,
  1567. Adapters[AdapterNumber].DeferredReceives,
  1568. Adapters[AdapterNumber].FirstIndex,
  1569. Adapters[AdapterNumber].LastIndex
  1570. );
  1571. }
  1572. //
  1573. // now reduce the priority of the event handler thread to give the CCB
  1574. // handler thread some time to free buffers & issue DLC.FLOW.CONTROL
  1575. // (mainly for DOS apps which do it in the wrong order)
  1576. //
  1577. SetThreadPriority(hEventThread, THREAD_PRIORITY_LOWEST);
  1578. IF_DEBUG(CRITSEC) {
  1579. DPUT1("SetEmulatedLocalBusyState: LEAVING Adapters[%d].LocalBusyCritSec\n",
  1580. AdapterNumber
  1581. );
  1582. }
  1583. LeaveCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  1584. }
  1585. BOOLEAN
  1586. ResetEmulatedLocalBusyState(
  1587. IN BYTE AdapterNumber,
  1588. IN WORD StationId,
  1589. IN BYTE DlcCommand
  1590. )
  1591. /*++
  1592. Routine Description:
  1593. Clears the local-busy(buffer) state for this adapter/link station. If the
  1594. transition is from BUSY to CLEARING then just changes the state and issues
  1595. a hardware interrupt to the VDM: the reason for this is that the original
  1596. interrupt from the READ which caused us to go into local-busy(buffer) state
  1597. was used to generate a DLC status change event
  1598. Called for a single link station or an entire SAP
  1599. Called in response to receiving DLC.FLOW.CONTROL(local-busy(buffer), reset)
  1600. from DOS app for a SAP or link station
  1601. Arguments:
  1602. AdapterNumber - which adapter to clear the local-busy(buffer) state for
  1603. StationId - which link station on this adapter
  1604. DlcCommand - which DLC command is causing this reset
  1605. Return Value:
  1606. BOOLEAN
  1607. TRUE - state was reset from BUSY to CLEARING
  1608. FALSE - state is NOT_BUSY: invalid request
  1609. --*/
  1610. {
  1611. BOOLEAN reset;
  1612. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  1613. ASSERT(HIBYTE(StationId) != 0); // SAP can't be 0
  1614. //
  1615. // grab the LocalBusyCritSec for the adapter and reset the local-busy(buffer)
  1616. // state for the link station or the entire SAP. If we are resetting for the
  1617. // entire SAP, holding the critical section ensures that new I-Frames won't
  1618. // cause another station to go into emulated local-busy(buffer) state while
  1619. // we are resetting the rest
  1620. //
  1621. IF_DEBUG(CRITSEC) {
  1622. DPUT1("ResetEmulatedLocalBusyState: ENTERING Adapters[%d].LocalBusyCritSec\n",
  1623. AdapterNumber
  1624. );
  1625. }
  1626. EnterCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  1627. if (LOBYTE(StationId) == 0) {
  1628. reset = ResetEmulatedLocalBusyStateSap(AdapterNumber, StationId, DlcCommand);
  1629. } else {
  1630. reset = ResetEmulatedLocalBusyStateLink(AdapterNumber, StationId, DlcCommand);
  1631. }
  1632. IF_DEBUG(CRITSEC) {
  1633. DPUT1("ResetEmulatedLocalBusyState: LEAVING Adapters[%d].LocalBusyCritSec\n",
  1634. AdapterNumber
  1635. );
  1636. }
  1637. LeaveCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  1638. return reset;
  1639. }
  1640. BOOLEAN
  1641. ResetEmulatedLocalBusyStateSap(
  1642. IN BYTE AdapterNumber,
  1643. IN WORD StationId,
  1644. IN BYTE DlcCommand
  1645. )
  1646. /*++
  1647. Routine Description:
  1648. This function is called when the app resets the local-busy(buffer) state
  1649. for an entire SAP
  1650. NB: This function MUST BE CALLED WHILE HOLDING THE LocalBusyCritSec for
  1651. this adapter
  1652. Arguments:
  1653. AdapterNumber - which adapter to
  1654. StationId - SAP:00 - which SAP to reset local-busy(buffer) states for
  1655. DlcCommand - which DLC command is causing this reset
  1656. Return Value:
  1657. BOOLEAN
  1658. TRUE - links reset for this SAP
  1659. FALSE - no links reset for this SAP
  1660. --*/
  1661. {
  1662. DWORD link = Adapters[AdapterNumber].FirstIndex;
  1663. DWORD last = Adapters[AdapterNumber].LastIndex;
  1664. DWORD count = 0;
  1665. LOCAL_BUSY_STATE state;
  1666. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  1667. ASSERT(HIBYTE(StationId) != 0);
  1668. ASSERT(link <= last);
  1669. ASSERT(DlcCommand == LLC_BUFFER_FREE || DlcCommand == LLC_DLC_FLOW_CONTROL);
  1670. IF_DEBUG(DLC_ASYNC) {
  1671. DPUT3("ResetEmulatedLocalBusyStateSap(%d, %04x, %s)\n",
  1672. AdapterNumber,
  1673. StationId,
  1674. DlcCommand == LLC_BUFFER_FREE ? "BUFFER.FREE"
  1675. : DlcCommand == LLC_DLC_FLOW_CONTROL ? "DLC.FLOW.CONTROL"
  1676. : "???"
  1677. );
  1678. }
  1679. //
  1680. // we may have a DLC.FLOW.CONTROL for a SAP which has already been reset
  1681. // by a previous DLC.FLOW.CONTROL
  1682. //
  1683. if (link == NO_LINKS_BUSY) {
  1684. ASSERT(last == NO_LINKS_BUSY);
  1685. IF_DEBUG(DLC_ASYNC) {
  1686. DPUT("ResetEmulatedLocalBusyStateSap: SAP already reset\n");
  1687. }
  1688. return FALSE;
  1689. }
  1690. //
  1691. // since we are holding the LocalBusyCritSec for this adapter, we can use
  1692. // FirstLink and LastLink to try reduce the number of searches for busy
  1693. // stations. No new stations can go busy and change FirstLink or LastLink
  1694. // while we are in this loop
  1695. //
  1696. for (++StationId; link <= last; ++StationId) {
  1697. state = Adapters[AdapterNumber].LocalBusyInfo[link].State;
  1698. ++link;
  1699. if (state == BUSY
  1700. || (state == BUSY_BUFFER && DlcCommand == LLC_DLC_FLOW_CONTROL)
  1701. || (state == BUSY_FLOW && DlcCommand == LLC_BUFFER_FREE)) {
  1702. if (ResetEmulatedLocalBusyStateLink(AdapterNumber, StationId, DlcCommand)) {
  1703. ++count;
  1704. }
  1705. }
  1706. }
  1707. return count != 0;
  1708. }
  1709. BOOLEAN
  1710. ResetEmulatedLocalBusyStateLink(
  1711. IN BYTE AdapterNumber,
  1712. IN WORD StationId,
  1713. IN BYTE DlcCommand
  1714. )
  1715. /*++
  1716. Routine Description:
  1717. This function is called when the app resets the local-busy(buffer) state
  1718. for a single link station
  1719. Clears the local-busy(buffer) state for this adapter/link station. If the
  1720. transition is from BUSY to CLEARING then just changes the state and issues
  1721. a hardware interrupt to the VDM: the reason for this is that the original
  1722. interrupt from the READ which caused us to go into local-busy(buffer) state
  1723. was used to generate a DLC status change event
  1724. NB: This function MUST BE CALLED WHILE HOLDING THE LocalBusyCritSec for
  1725. this adapter
  1726. Arguments:
  1727. AdapterNumber - which adapter to clear the local-busy(buffer) state for
  1728. StationId - which link station on this adapter
  1729. DlcCommand - which DLC command is causing this reset
  1730. Return Value:
  1731. BOOLEAN
  1732. TRUE - state was reset from BUSY to CLEARING
  1733. FALSE - state is NOT_BUSY: invalid request
  1734. --*/
  1735. {
  1736. DWORD link = LINK_ID(StationId);
  1737. LOCAL_BUSY_STATE state;
  1738. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  1739. ASSERT(HIBYTE(StationId) != 0); // SAP can't be 0
  1740. ASSERT(LOBYTE(StationId) != 0); // Link Station can't be 0
  1741. ASSERT(DlcCommand == LLC_BUFFER_FREE || DlcCommand == LLC_DLC_FLOW_CONTROL);
  1742. IF_DEBUG(DLC_ASYNC) {
  1743. DPUT3("ResetEmulatedLocalBusyStateLink(%d, %04x, %s)\n",
  1744. AdapterNumber,
  1745. StationId,
  1746. DlcCommand == LLC_BUFFER_FREE ? "BUFFER.FREE"
  1747. : DlcCommand == LLC_DLC_FLOW_CONTROL ? "DLC.FLOW.CONTROL"
  1748. : "???"
  1749. );
  1750. }
  1751. state = Adapters[AdapterNumber].LocalBusyInfo[link].State;
  1752. ASSERT(state == NOT_BUSY
  1753. || state == CLEARING
  1754. || state == BUSY
  1755. || state == BUSY_BUFFER
  1756. || state == BUSY_FLOW
  1757. );
  1758. if (state == BUSY) {
  1759. //
  1760. // if the state is BUSY then this is the first DLC.FLOW.CONTROL or
  1761. // BUFFER.FREE since we went into local-busy(buffer) state. State
  1762. // transition is to BUSY_FLOW if this is a DLC.FLOW.CONTROL else
  1763. // BUSY_BUFFER
  1764. //
  1765. IF_DEBUG(DLC_ASYNC) {
  1766. DPUT1("ResetEmulatedLocalBusyStateLink: state: BUSY -> %s\n",
  1767. DlcCommand == LLC_BUFFER_FREE ? "BUSY_BUFFER" : "BUSY_FLOW"
  1768. );
  1769. }
  1770. Adapters[AdapterNumber].LocalBusyInfo[link].State = (DlcCommand == LLC_BUFFER_FREE)
  1771. ? BUSY_BUFFER
  1772. : BUSY_FLOW;
  1773. } else if ((state == BUSY_FLOW && DlcCommand == LLC_BUFFER_FREE)
  1774. || (state == BUSY_BUFFER && DlcCommand == LLC_DLC_FLOW_CONTROL)) {
  1775. //
  1776. // state is BUSY_FLOW or BUSY_BUFFER. If this reset is caused by the
  1777. // other half of the state transition requirement then change the state
  1778. //
  1779. IF_DEBUG(DLC_ASYNC) {
  1780. DPUT3("ResetEmulatedLocalBusyStateLink: link %d.%04x changing from %s to CLEARING\n",
  1781. AdapterNumber,
  1782. StationId,
  1783. state == BUSY_FLOW ? "BUSY_FLOW" : "BUSY_BUFFER"
  1784. );
  1785. }
  1786. Adapters[AdapterNumber].LocalBusyInfo[link].State = CLEARING;
  1787. IF_DEBUG(DLC_ASYNC) {
  1788. DPUT("ResetEmulatedLocalBusyStateLink: Interrupting VDM\n");
  1789. }
  1790. IssueHardwareInterrupt();
  1791. //
  1792. // for the benefit of the caller, the state was essentially BUSY
  1793. //
  1794. state = BUSY;
  1795. } else {
  1796. IF_DEBUG(DLC_ASYNC) {
  1797. DPUT3("ResetEmulatedLocalBusyStateLink: NOT resetting state of %d.%04x. state is %s\n",
  1798. AdapterNumber,
  1799. StationId,
  1800. state == CLEARING ? "CLEARING" : "NOT_BUSY"
  1801. );
  1802. }
  1803. }
  1804. return state == BUSY;
  1805. }
  1806. VOID
  1807. DeferReceive(
  1808. IN BYTE AdapterNumber,
  1809. IN WORD StationId,
  1810. IN PLLC_CCB pReadCcb
  1811. )
  1812. /*++
  1813. Routine Description:
  1814. Adds a READ CCB to the end of the deferred receive queue for an adapter/
  1815. station id
  1816. NB: This function MUST BE CALLED WHILE HOLDING THE LocalBusyCritSec for
  1817. this adapter
  1818. Arguments:
  1819. AdapterNumber - which adapter to set emulated local busy state for
  1820. StationId - which link station on AdapterNumber
  1821. pReadCcb - pointer to completed received I-Frame CCB (NT READ CCB)
  1822. Return Value:
  1823. None.
  1824. --*/
  1825. {
  1826. PLLC_CCB* pQueue;
  1827. PLLC_CCB pLlcCcb;
  1828. //
  1829. // if there is nothing in the queue for this adapter number/station ID
  1830. // then place this CCB at the head of the queue, else put the CCB at
  1831. // the end. The CCBs are chained together using their CCB_POINTER fields.
  1832. // Normally, this field is not used for received frame READ CCBs
  1833. //
  1834. ASSERT(pReadCcb->pNext == NULL);
  1835. ASSERT(HIBYTE(StationId) != 0);
  1836. ASSERT(LOBYTE(StationId) != 0);
  1837. #if DBG
  1838. IF_DEBUG(DLC_ASYNC) {
  1839. DPUT4("DeferReceive: deferring I-Frame for %d.%04x. CCB = %08x. Current depth is %d\n",
  1840. AdapterNumber,
  1841. StationId,
  1842. pReadCcb,
  1843. Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].Depth
  1844. );
  1845. }
  1846. #endif
  1847. pQueue = &Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].Queue;
  1848. pLlcCcb = *pQueue;
  1849. if (!pLlcCcb) {
  1850. *pQueue = pReadCcb;
  1851. } else {
  1852. for (; pLlcCcb->pNext; pLlcCcb = pLlcCcb->pNext) {
  1853. ;
  1854. }
  1855. pLlcCcb->pNext = pReadCcb;
  1856. }
  1857. #if DBG
  1858. ++Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].Depth;
  1859. ASSERT(Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].Depth <= MAX_I_FRAME_DEPTH);
  1860. IF_DEBUG(CRITICAL) {
  1861. CRITDUMP(("DeferReceive: %d.%04x CCB=%08x Depth=%d\n",
  1862. AdapterNumber,
  1863. StationId,
  1864. pReadCcb,
  1865. Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].Depth
  1866. ));
  1867. }
  1868. #endif
  1869. }
  1870. VOID
  1871. DeferAllIFrames(
  1872. IN BYTE AdapterNumber,
  1873. IN WORD StationId
  1874. )
  1875. /*++
  1876. Routine Description:
  1877. Removes all pending I-Frames for StationId from the EventQueue for this
  1878. adapter and places them on the deferred queue for this StationId.
  1879. This is done when the StationId goes into local-busy(buffer) state. We do
  1880. this so that any event packets behind the I-Frames can be completed, other
  1881. link stations which aren't blocked can receive their I-Frames and ensures
  1882. that once in local-busy state, all I-Frames are deferred for a link station
  1883. NB: This function MUST BE CALLED WHILE HOLDING THE LocalBusyCritSec for
  1884. this adapter
  1885. NB: we have to gain access to 2 critical sections - the LocalBusyCritSec
  1886. for this station ID & the EventQueueCritSec for this adapter
  1887. Assumption: This function is called in the context of the VDM h/w ISR and
  1888. before AcknowledgeHardwareInterrupt is called for this ISR BOP
  1889. Arguments:
  1890. AdapterNumber - which adapter structure to use
  1891. StationId - which link station to remove I-Frames for
  1892. Return Value:
  1893. None.
  1894. --*/
  1895. {
  1896. PLLC_CCB pCcb;
  1897. PLLC_CCB next;
  1898. PLLC_CCB* last;
  1899. PLLC_READ_PARMS pReadParms;
  1900. PLLC_BUFFER pFrame;
  1901. BOOLEAN remove;
  1902. //
  1903. // deferredFrameCount starts at -1 because it is a count of pending h/w
  1904. // interrupts to cancel. We have to acknowledge the current one which will
  1905. // reduce the count by 1
  1906. //
  1907. LONG deferredFrameCount = -1;
  1908. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  1909. ASSERT(HIBYTE(StationId) != 0);
  1910. ASSERT(LOBYTE(StationId) != 0);
  1911. IF_DEBUG(CRITSEC) {
  1912. DPUT1("DeferAllIFrames: ENTERING Adapters[%d].EventQueueCritSec\n",
  1913. AdapterNumber
  1914. );
  1915. }
  1916. EnterCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  1917. pCcb = Adapters[AdapterNumber].EventQueueHead;
  1918. last = &Adapters[AdapterNumber].EventQueueHead;
  1919. while (pCcb) {
  1920. pReadParms = &pCcb->u.pParameterTable->Read;
  1921. //
  1922. // only remove I-Frames from the EventQueue that are destined for this
  1923. // link station
  1924. //
  1925. remove = FALSE;
  1926. if (pReadParms->uchEvent == LLC_EVENT_RECEIVE_DATA) {
  1927. pFrame = pReadParms->Type.Event.pReceivedFrame;
  1928. if (pFrame->Contiguous.uchMsgType == LLC_I_FRAME
  1929. && pFrame->Contiguous.usStationId == StationId) {
  1930. remove = TRUE;
  1931. }
  1932. }
  1933. if (remove) {
  1934. next = pCcb->pNext;
  1935. *last = next;
  1936. --Adapters[AdapterNumber].QueueElements;
  1937. if (Adapters[AdapterNumber].EventQueueTail == pCcb) {
  1938. if (last == &Adapters[AdapterNumber].EventQueueHead) {
  1939. Adapters[AdapterNumber].EventQueueTail = NULL;
  1940. } else {
  1941. Adapters[AdapterNumber].EventQueueTail = CONTAINING_RECORD(last, LLC_CCB, pNext);
  1942. }
  1943. }
  1944. IF_DEBUG(DLC_ASYNC) {
  1945. DPUT3("DeferAllIFrames: moving CCB %08x for %d.%04x\n",
  1946. pCcb,
  1947. AdapterNumber,
  1948. StationId
  1949. );
  1950. }
  1951. pCcb->pNext = NULL;
  1952. DeferReceive(AdapterNumber, StationId, pCcb);
  1953. ++deferredFrameCount;
  1954. pCcb = next;
  1955. } else {
  1956. IF_DEBUG(DLC_ASYNC) {
  1957. DPUT1("DeferAllIFrames: not removing CCB %08x from EventQueue\n",
  1958. pCcb
  1959. );
  1960. }
  1961. last = (PLLC_CCB*)&pCcb->pNext;
  1962. pCcb = pCcb->pNext;
  1963. }
  1964. }
  1965. if (deferredFrameCount > 0) {
  1966. CancelHardwareInterrupts(deferredFrameCount);
  1967. }
  1968. IF_DEBUG(CRITSEC) {
  1969. DPUT1("DeferAllIFrames: LEAVING Adapters[%d].EventQueueCritSec\n",
  1970. AdapterNumber
  1971. );
  1972. }
  1973. LeaveCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  1974. }
  1975. PLLC_CCB
  1976. RemoveDeferredReceive(
  1977. IN BYTE AdapterNumber,
  1978. IN WORD StationId,
  1979. OUT BOOLEAN* pDeferredFramesLeft
  1980. )
  1981. /*++
  1982. Routine Description:
  1983. Removes a READ CCB from the head of the deferred receive queue for an
  1984. adapter/station id and sets the head to point to the next deferred receive
  1985. in the queue
  1986. NB: This function MUST NOT BE CALLED WHILE HOLDING THE LocalBusyCritSec for
  1987. this adapter (opposite of DeferReceive)
  1988. Arguments:
  1989. AdapterNumber - which adapter to set emulated local busy state for
  1990. StationId - which link station on AdapterNumber
  1991. pDeferredFramesLeft - TRUE if there are more frames on this deferred queue
  1992. Return Value:
  1993. PLLC_CCB
  1994. pointer to CCB from head of queue
  1995. --*/
  1996. {
  1997. PLLC_CCB* pQueue;
  1998. PLLC_CCB pLlcCcb;
  1999. DWORD link = LINK_ID(StationId);
  2000. //
  2001. // if there is nothing in the queue for this adapter number/station ID
  2002. // then place this CCB at the head of the queue, else put the CCB at
  2003. // the end. The CCBs are chained together using their CCB_POINTER fields.
  2004. // Normally, this field is not used for received frame READ CCBs
  2005. //
  2006. ASSERT(HIBYTE(StationId) != 0);
  2007. ASSERT(LOBYTE(StationId) != 0);
  2008. IF_DEBUG(CRITSEC) {
  2009. DPUT1("RemoveDeferredReceive: ENTERING Adapters[%d].LocalBusyCritSec\n",
  2010. AdapterNumber
  2011. );
  2012. }
  2013. EnterCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  2014. pQueue = &Adapters[AdapterNumber].LocalBusyInfo[link].Queue;
  2015. pLlcCcb = *pQueue;
  2016. *pQueue = pLlcCcb->pNext;
  2017. ASSERT(pLlcCcb != NULL);
  2018. IF_DEBUG(DLC_ASYNC) {
  2019. DPUT4("RemovedDeferredReceive: removing I-Frame for %d.%04x. CCB = %08x. Current depth is %d\n",
  2020. AdapterNumber,
  2021. StationId,
  2022. pLlcCcb,
  2023. Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].Depth
  2024. );
  2025. }
  2026. #if DBG
  2027. --Adapters[AdapterNumber].LocalBusyInfo[link].Depth;
  2028. #endif
  2029. //
  2030. // if the deferred queue is now empty, reset the state and issue the real
  2031. // DLC.FLOW.CONTROL(local-busy(buffer), reset) to the DLC driver. Also
  2032. // reduce the reference count of link stations on this adapter in the
  2033. // local-busy(buffer) state and update the first and last link indicies
  2034. //
  2035. if (*pQueue == NULL) {
  2036. IF_DEBUG(DLC_ASYNC) {
  2037. DPUT2("RemoveDeferredReceive: %d.%04x: change state to NOT_BUSY\n",
  2038. AdapterNumber,
  2039. StationId
  2040. );
  2041. }
  2042. Adapters[AdapterNumber].LocalBusyInfo[link].State = NOT_BUSY;
  2043. --Adapters[AdapterNumber].DeferredReceives;
  2044. if (Adapters[AdapterNumber].DeferredReceives) {
  2045. if (link == Adapters[AdapterNumber].FirstIndex) {
  2046. for (link = Adapters[AdapterNumber].FirstIndex + 1;
  2047. link <= Adapters[AdapterNumber].LastIndex;
  2048. ++link) {
  2049. if (Adapters[AdapterNumber].LocalBusyInfo[link].State != NOT_BUSY) {
  2050. Adapters[AdapterNumber].FirstIndex = link;
  2051. break;
  2052. }
  2053. }
  2054. } else if (link == Adapters[AdapterNumber].LastIndex) {
  2055. for (link = Adapters[AdapterNumber].LastIndex - 1;
  2056. link >= Adapters[AdapterNumber].FirstIndex;
  2057. --link
  2058. ) {
  2059. if (Adapters[AdapterNumber].LocalBusyInfo[link].State != NOT_BUSY) {
  2060. Adapters[AdapterNumber].LastIndex = link;
  2061. break;
  2062. }
  2063. }
  2064. }
  2065. } else {
  2066. Adapters[AdapterNumber].FirstIndex = NO_LINKS_BUSY;
  2067. Adapters[AdapterNumber].LastIndex = NO_LINKS_BUSY;
  2068. }
  2069. #if DBG
  2070. //ASSERT(DosDlcFlowControl(AdapterNumber, StationId, LLC_RESET_LOCAL_BUSY_BUFFER) == LLC_STATUS_SUCCESS);
  2071. ASSERT(DlcFlowControl(AdapterNumber, StationId, LLC_RESET_LOCAL_BUSY_USER) == LLC_STATUS_SUCCESS);
  2072. #else
  2073. //DosDlcFlowControl(AdapterNumber, StationId, LLC_RESET_LOCAL_BUSY_BUFFER);
  2074. DlcFlowControl(AdapterNumber, StationId, LLC_RESET_LOCAL_BUSY_USER);
  2075. #endif
  2076. *pDeferredFramesLeft = FALSE;
  2077. //
  2078. // restore async event thread's priority
  2079. //
  2080. SetThreadPriority(hEventThread, THREAD_PRIORITY_ABOVE_NORMAL);
  2081. } else {
  2082. *pDeferredFramesLeft = TRUE;
  2083. }
  2084. IF_DEBUG(DLC_ASYNC) {
  2085. DPUT5("RemoveDeferredReceive(%d, %04x): Ref#=%d, First=%d, Last=%d\n",
  2086. AdapterNumber,
  2087. StationId,
  2088. Adapters[AdapterNumber].DeferredReceives,
  2089. Adapters[AdapterNumber].FirstIndex,
  2090. Adapters[AdapterNumber].LastIndex
  2091. );
  2092. }
  2093. IF_DEBUG(CRITSEC) {
  2094. DPUT1("RemoveDeferredReceive: LEAVING Adapters[%d].LocalBusyCritSec\n",
  2095. AdapterNumber
  2096. );
  2097. }
  2098. LeaveCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  2099. return pLlcCcb;
  2100. }
  2101. DWORD
  2102. VrDlcEventHandlerThread(
  2103. IN PVOID pParameter
  2104. )
  2105. /*++
  2106. Routine Description:
  2107. This is the VDM DLC event handler thread. The thread reads all DLC events
  2108. from both DOS DLC adapters, queues them to the event queue and requests a
  2109. DOS hardware interrupt (the post routine mechanism uses hardware interrupts
  2110. to make an external event in the VDM)
  2111. To make this stuff as fast as possible, we don't allocate or free any memory
  2112. in this loop, but we reuse the old read CCBs and their parameter tables in
  2113. the event queue
  2114. We filter out any completion which will NOT result in an asynchronous event
  2115. in the VDM. This means CCB completions for this emulator (DIR.CLOSE.ADAPTER
  2116. and DIR.CLOSE.DIRECT for example) and received I-Frames for link stations
  2117. which are currently in the BUSY (local-busy(buffer)) state. This avoids us
  2118. making unnecessary interrupts in the VDM and costly (on x86 machines) BOPs
  2119. which achieve no action for the VDM
  2120. Arguments:
  2121. pParameter - not used
  2122. Return Value:
  2123. None, this should loop forever, until the VDM process dies
  2124. --*/
  2125. {
  2126. DWORD status = LLC_STATUS_PENDING;
  2127. DWORD waitIndex;
  2128. PLLC_CCB pReadCcb;
  2129. PLLC_READ_PARMS pReadParms;
  2130. WORD stationId;
  2131. UNREFERENCED_PARAMETER(pParameter);
  2132. IF_DEBUG(DLC_ASYNC) {
  2133. DPUT2("VrDlcEventHandlerThread kicked off: Thread Handle=%x, Id=%d\n",
  2134. GetCurrentThread(),
  2135. GetCurrentThreadId()
  2136. );
  2137. }
  2138. //
  2139. // wait for the READ CCB event for either adapter to become signalled (by
  2140. // DLC driver when READ completes)
  2141. //
  2142. while (TRUE) {
  2143. waitIndex = WaitForMultipleObjects(
  2144. ARRAY_ELEMENTS(aReadEvents), // count of objects
  2145. aReadEvents, // handle array
  2146. FALSE, // do not wait all objects
  2147. INFINITE // wait forever
  2148. );
  2149. //
  2150. // if we get 0xFFFFFFFF back then an error occurred
  2151. //
  2152. if (waitIndex == 0xffffffff) {
  2153. status = GetLastError();
  2154. IF_DEBUG(DLC_ASYNC) {
  2155. DPUT1("VrDlcEventHandlerThread: FATAL: WaitForMultipleObjects returns %d\n", status);
  2156. }
  2157. //
  2158. // this terminates the thread!
  2159. //
  2160. break;
  2161. }
  2162. //
  2163. // if we get a number > number of events-1, then either a timeout
  2164. // occurred or a mutex was abandoned, both of which are highly
  2165. // improbable. Just continue with the loop
  2166. //
  2167. if (waitIndex > LAST_ELEMENT(aReadEvents)) {
  2168. IF_DEBUG(DLC_ASYNC) {
  2169. DPUT1("VrDlcEventHandlerThread: ERROR: WaitForMultipleObjects returns %d?: continuing\n", waitIndex);
  2170. }
  2171. continue;
  2172. }
  2173. //
  2174. // one of the READ CCBs has successfully completed (oh joy!)
  2175. //
  2176. pReadCcb = aReadCcbs[waitIndex];
  2177. //
  2178. // reset the event
  2179. //
  2180. ResetEvent(aReadEvents[waitIndex]);
  2181. IF_DEBUG(DLC_ASYNC) {
  2182. DPUT1("VrDlcEventHandlerThread: Event occurred for adapter %d\n", waitIndex);
  2183. IF_DEBUG(READ_COMPLETE) {
  2184. DUMPCCB(pReadCcb, TRUE, FALSE, FALSE, 0, 0);
  2185. }
  2186. }
  2187. if (pReadCcb->uchDlcStatus == STATUS_SUCCESS) {
  2188. //
  2189. // it gets better!
  2190. //
  2191. pReadParms = &pReadCcb->u.pParameterTable->Read;
  2192. //
  2193. // if the completion flag is VRDLC_COMMAND_COMPLETION then this
  2194. // command originated in this emulator: Do Not hand it back to
  2195. // the VDM. In fact do nothing with it: this is an asynchronous
  2196. // command that we didn't want to wait for (like DIR.CLOSE.ADAPTER
  2197. // or DIR.CLOSE.DIRECT)
  2198. //
  2199. if (pReadParms->ulNotificationFlag == VRDLC_COMMAND_COMPLETION) {
  2200. IF_DEBUG(CRITICAL) {
  2201. CRITDUMP(("*** VrDlcEventHandlerThread: VRDLC_COMMAND_COMPLETION: CCB=%08x COMMAND=%02x ***\n", pReadCcb, pReadCcb->uchDlcCommand));
  2202. }
  2203. } else if (pReadParms->uchEvent == LLC_EVENT_STATUS_CHANGE
  2204. && pReadParms->Type.Status.usDlcStatusCode == LLC_INDICATE_LOCAL_STATION_BUSY
  2205. && !IS_LOCAL_BUSY(waitIndex, pReadParms->Type.Status.usStationId)) {
  2206. //
  2207. // We must separate the buffer busy states of global NT
  2208. // buffer pool and the local buffer pools.
  2209. // This must be a real buffer busy indication, if
  2210. // the SAP has no overflowed receive buffers.
  2211. // How can such a situation arise - if we (ie DOS emulation) aren't
  2212. // holding onto the buffers, then where are they? Sounds like a bug
  2213. // to me (RLF 07/22/92)
  2214. //
  2215. IF_DEBUG(DLC_ASYNC) {
  2216. DPUT("VrDlcEventHandlerThread: *** REAL LOCAL BUSY??? ***\n");
  2217. }
  2218. //
  2219. // We are not queueing buffers because of having
  2220. // no SAP buffers available => this must be a real
  2221. // buffer busy indication. The READ command should
  2222. // automatically extend the buffer pool size (up
  2223. // to the maximum value set in the initialization)
  2224. //
  2225. DlcFlowControl((BYTE)waitIndex, pReadParms->Type.Status.usStationId, LLC_RESET_LOCAL_BUSY_BUFFER);
  2226. } else if (pReadParms->uchEvent == LLC_EVENT_RECEIVE_DATA
  2227. && pReadParms->Type.Event.pReceivedFrame->Contiguous.uchMsgType
  2228. == LLC_I_FRAME) {
  2229. stationId = pReadParms->Type.Event.pReceivedFrame->Contiguous.usStationId;
  2230. ASSERT(HIBYTE(stationId) != 0);
  2231. ASSERT(LOBYTE(stationId) != 0);
  2232. IF_DEBUG(CRITSEC) {
  2233. DPUT1("VrDlcEventHandlerThread: ENTERING Adapters[%d].LocalBusyCritSec\n",
  2234. waitIndex
  2235. );
  2236. }
  2237. EnterCriticalSection(&Adapters[waitIndex].LocalBusyCritSec);
  2238. //
  2239. // if the link station is in emulated local-busy(buffer) state
  2240. // BUSY or CLEARING then queue the I-Frame. This action does NOT
  2241. // generate a h/w interrupt to the VDM. VrDlcHwInterrupt generates
  2242. // additional h/w interrupts when it processes deferred I-Frames
  2243. //
  2244. ASSERT(
  2245. Adapters[waitIndex].LocalBusyInfo[LINK_ID(stationId)].State == NOT_BUSY
  2246. || Adapters[waitIndex].LocalBusyInfo[LINK_ID(stationId)].State == CLEARING
  2247. || Adapters[waitIndex].LocalBusyInfo[LINK_ID(stationId)].State == BUSY
  2248. || Adapters[waitIndex].LocalBusyInfo[LINK_ID(stationId)].State == BUSY_BUFFER
  2249. || Adapters[waitIndex].LocalBusyInfo[LINK_ID(stationId)].State == BUSY_FLOW
  2250. );
  2251. if (Adapters[waitIndex].LocalBusyInfo[LINK_ID(stationId)].State != NOT_BUSY) {
  2252. DeferReceive((BYTE)waitIndex, stationId, pReadCcb);
  2253. //
  2254. // set the READ CCB pointer to NULL: we have to allocate a
  2255. // new READ CCB
  2256. //
  2257. pReadCcb = NULL;
  2258. }
  2259. IF_DEBUG(CRITSEC) {
  2260. DPUT1("VrDlcEventHandlerThread: LEAVING Adapters[%d].LocalBusyCritSec\n",
  2261. waitIndex
  2262. );
  2263. }
  2264. LeaveCriticalSection(&Adapters[waitIndex].LocalBusyCritSec);
  2265. }
  2266. //
  2267. // pReadCcb is NULL if the READ CCB is to be added to the event
  2268. // queue, NULL if we deferred an I-Frame for a link station in
  2269. // local-busy(buffer) state
  2270. //
  2271. if (pReadCcb) {
  2272. //
  2273. // queue the completed READ CCB in the event queue. If it is
  2274. // full (!) then wait. We will already have issued a call to
  2275. // call_ica_hw_interrupt for each of the events in the queue
  2276. // so we wait on the VDM removing events from the queue. This
  2277. // is an irregular situation, that I (RLF) don't expect to
  2278. // arise. If it does, then it probably means there is some
  2279. // horrendous inefficiency somewhere
  2280. //
  2281. PutEvent((BYTE)waitIndex, pReadCcb);
  2282. IF_DEBUG(DLC_ASYNC) {
  2283. DPUT("VrDlcEventHandlerThread: Interrupting VDM\n");
  2284. }
  2285. //
  2286. // poke the VDM so that it knows there is some asynchronous
  2287. // processing to do
  2288. //
  2289. IssueHardwareInterrupt();
  2290. //
  2291. // set pReadCcb to NULL. We have to allocate and submit a new
  2292. // READ CCB
  2293. //
  2294. pReadCcb = NULL;
  2295. }
  2296. } else {
  2297. //
  2298. // The READ function failed, the adapter must be closed. We now
  2299. // wait until the adapter is opened again and the event is set
  2300. // back to the signaled state
  2301. //
  2302. IF_DEBUG(DLC_ASYNC) {
  2303. DPUT1("VrDlcEventHandlerThread: READ failed. Status=%x\n", pReadCcb->uchDlcStatus);
  2304. }
  2305. LocalFree(pReadCcb);
  2306. IF_DEBUG(DLC_ALLOC) {
  2307. DPUT1("FREE: freed block @ %x\n", pReadCcb);
  2308. }
  2309. //
  2310. // wait for a new READ CCB to be created the next time this adapter
  2311. // is opened
  2312. //
  2313. //continue;
  2314. pReadCcb = NULL;
  2315. }
  2316. //
  2317. // if we had a successful completion then get a new CCB. If the CCB was
  2318. // not queued, re-use it
  2319. //
  2320. if (!pReadCcb) {
  2321. pReadCcb = InitiateRead(waitIndex, (LLC_STATUS*)&status);
  2322. if (pReadCcb) {
  2323. status = pReadCcb->uchDlcStatus;
  2324. } else {
  2325. IF_DEBUG(DLC_ASYNC) {
  2326. DPUT("VrDlcEventHandlerThread: Error: InitiateRead returns NULL\n");
  2327. }
  2328. break;
  2329. }
  2330. } else {
  2331. status = lpAcsLan(pReadCcb, NULL);
  2332. if (status != LLC_STATUS_SUCCESS) {
  2333. IF_DEBUG(DLC_ASYNC) {
  2334. DPUT1("VrDlcEventHandlerThread: Error: AcsLan returns %d\n", status);
  2335. }
  2336. break;
  2337. }
  2338. }
  2339. }
  2340. //
  2341. // !!! WE SHOULD NEVER BE HERE !!!
  2342. //
  2343. IF_DEBUG(DLC_ASYNC) {
  2344. DPUT1("VrDlcEventHandlerThread: Fatal: terminating. Status = %x\n", status);
  2345. }
  2346. return 0;
  2347. }
  2348. BOOLEAN
  2349. InitializeEventHandler(
  2350. VOID
  2351. )
  2352. /*++
  2353. Routine Description:
  2354. Initializes static data structures used in event handling
  2355. Arguments:
  2356. None
  2357. Return Value:
  2358. BOOLEAN
  2359. Success - TRUE
  2360. Failure - FALSE
  2361. --*/
  2362. {
  2363. DWORD i;
  2364. DWORD Tid;
  2365. IF_DEBUG(DLC_ASYNC) {
  2366. DPUT("Vr: InitializeEventHandler\n");
  2367. }
  2368. //
  2369. // make sure the read CCBs and event queues are in a known state
  2370. //
  2371. RtlZeroMemory(aReadCcbs, sizeof(aReadCcbs));
  2372. //
  2373. // preset the handle array with invalid handles so we know which ones
  2374. // have been allocated in clean up
  2375. //
  2376. for (i = 0; i < ARRAY_ELEMENTS(aReadEvents); ++i) {
  2377. aReadEvents[i] = INVALID_HANDLE_VALUE;
  2378. }
  2379. //
  2380. // create event handles for all (both) supported adapters. DIR.OPEN.ADAPTER
  2381. // sets the event to signalled which enables the event handler thread to
  2382. // receive events for that adapter. If we get an error creating the handles
  2383. // then clean up before exiting so that we may try this again later
  2384. //
  2385. for (i = 0; i < ARRAY_ELEMENTS(aReadEvents); i++) {
  2386. aReadEvents[i] = CreateEvent(NULL, // security attributes: no inherit
  2387. TRUE, // manual-reset event
  2388. FALSE, // initial state = not signaled
  2389. NULL // unnamed event
  2390. );
  2391. if (aReadEvents[i] == NULL) {
  2392. IF_DEBUG(DLC_ASYNC) {
  2393. DPUT1("Vr: InitializeEventHandler: Error: failed to create read event: %d\n", GetLastError());
  2394. }
  2395. goto cleanUp;
  2396. }
  2397. }
  2398. //
  2399. // create and start the thread which handles RECEIVE events
  2400. //
  2401. hEventThread = CreateThread(NULL, // security attributes
  2402. EVENT_THREAD_STACK, // initial thread stack size
  2403. VrDlcEventHandlerThread,
  2404. NULL, // thread args
  2405. 0, // creation flags
  2406. &Tid
  2407. );
  2408. if (hEventThread) {
  2409. IF_DEBUG(CRITICAL) {
  2410. CRITDUMP(("InitializeEventHandler: Created thread Handle=%x, Tid=%d\n", hEventThread, Tid));
  2411. }
  2412. SetThreadPriority(hEventThread, THREAD_PRIORITY_ABOVE_NORMAL);
  2413. return TRUE;
  2414. } else {
  2415. IF_DEBUG(DLC_ASYNC) {
  2416. DPUT1("Vr: InitializeEventHandler: Error: failed to create thread: %d\n", GetLastError());
  2417. }
  2418. }
  2419. //
  2420. // we come here if for some reason we couldn't create the event handles or
  2421. // the event handler thread
  2422. //
  2423. cleanUp:
  2424. for (i = 0; i < ARRAY_ELEMENTS(aReadEvents); ++i) {
  2425. if (aReadEvents[i] != INVALID_HANDLE_VALUE) {
  2426. CloseHandle(aReadEvents[i]);
  2427. }
  2428. }
  2429. IF_DEBUG(DLC_ASYNC) {
  2430. DPUT("InitializeEventHandler: Error: returning FALSE\n");
  2431. }
  2432. return FALSE;
  2433. }
  2434. PLLC_CCB
  2435. InitiateRead(
  2436. IN DWORD AdapterNumber,
  2437. OUT LLC_STATUS* ErrorStatus
  2438. )
  2439. /*++
  2440. Routine Description:
  2441. Create a READ CCB, initialize it to get all events for all stations, set
  2442. the completion event to the event created for this adapter and submit the
  2443. CCB (via AcsLan). If the submit succeeds, set this CCB as the READ CCB
  2444. for AdapterNumber
  2445. NB: The READ CCB - which will be queued on EventQueue - and its parameter
  2446. table are allocated together, so we only need one call to LocalFree to
  2447. deallocate both
  2448. This routine IS THE ONLY PLACE WHERE aReadCcbs IS WRITTEN once the array
  2449. has been initialized in InitializeEventHandler
  2450. Arguments:
  2451. AdapterNumber - which adapter to initiate this READ for
  2452. ErrorStatus - returned LLC_STATUS describing failure if this function
  2453. returns NULL
  2454. Return Value:
  2455. PLLC_CCB
  2456. pointer to allocated/submitted CCB or NULL.
  2457. If this function succeeds then aReadCcbs[AdapterNumber] points to the
  2458. READ CCB
  2459. If this function fails then *ErrorStatus will contain an LLC_STATUS
  2460. describing why we failed to allocate/submit the CCB. The CCB will be
  2461. deallocated in this case
  2462. --*/
  2463. {
  2464. PLLC_CCB pCcb;
  2465. PLLC_READ_PARMS parms;
  2466. LLC_STATUS status;
  2467. IF_DEBUG(DLC_ASYNC) {
  2468. DPUT1("InitiateRead: AdapterNumber=%d\n", AdapterNumber);
  2469. }
  2470. //
  2471. // Allocate, initialize and issue the next DLC command. Allocate contiguous
  2472. // space for CCB and parameter table
  2473. //
  2474. pCcb = (PLLC_CCB)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
  2475. sizeof(LLC_CCB) + sizeof(LLC_READ_PARMS)
  2476. );
  2477. //
  2478. // put the READ CCB in the array before we have a chance to complete it
  2479. //
  2480. aReadCcbs[AdapterNumber] = pCcb;
  2481. if (pCcb) {
  2482. //
  2483. // initialize required CCB fields
  2484. //
  2485. pCcb->uchAdapterNumber = (UCHAR)AdapterNumber;
  2486. pCcb->uchDlcCommand = LLC_READ;
  2487. parms = (PLLC_READ_PARMS)&pCcb[1];
  2488. pCcb->u.pParameterTable = (PLLC_PARMS)parms;
  2489. pCcb->hCompletionEvent = aReadEvents[AdapterNumber];
  2490. //
  2491. // set the read options to receive ALL events for ALL stations
  2492. //
  2493. parms->uchOptionIndicator = LLC_OPTION_READ_ALL;
  2494. parms->uchEventSet = LLC_READ_ALL_EVENTS;
  2495. //
  2496. // submit the CCB. If it's not ok, free the CCB and NULL the pointer
  2497. // in the list of per-adapter READ CCBs
  2498. //
  2499. status = lpAcsLan(pCcb, NULL);
  2500. if (status != LLC_STATUS_SUCCESS) {
  2501. aReadCcbs[AdapterNumber] = NULL;
  2502. IF_DEBUG(DLC_ASYNC) {
  2503. DPUT1("InitiateRead: AcsLan failed: %x\n", status);
  2504. }
  2505. LocalFree((HLOCAL)pCcb);
  2506. IF_DEBUG(DLC_ALLOC) {
  2507. DPUT1("FREE: freed block @ %x\n", pCcb);
  2508. }
  2509. *ErrorStatus = status;
  2510. pCcb = NULL;
  2511. }
  2512. } else {
  2513. *ErrorStatus = LLC_STATUS_NO_MEMORY;
  2514. }
  2515. #if DBG
  2516. IF_DEBUG(DLC_ASYNC) {
  2517. DPUT2("InitiateRead: returning pCcb=%x%c", pCcb, pCcb ? '\n' : ' ');
  2518. if (!pCcb) {
  2519. DPUT1("*ErrorStatus=%x\n", *ErrorStatus);
  2520. }
  2521. }
  2522. #endif
  2523. return pCcb;
  2524. }
  2525. VOID
  2526. PutEvent(
  2527. IN BYTE AdapterNumber,
  2528. IN PLLC_CCB pCcb
  2529. )
  2530. /*++
  2531. Routine Description:
  2532. Adds a completed event (READ CCB) to the Event Queue. If the event queue
  2533. is full then returns FALSE. Updates the queue tail index. The queue is
  2534. accessed inside a critical section
  2535. Arguments:
  2536. AdapterNumber - which adapter to queue event for
  2537. pCcb - pointer to completed READ CCB to add to the queue
  2538. Return Value:
  2539. None.
  2540. --*/
  2541. {
  2542. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  2543. ASSERT(pCcb->pNext == NULL);
  2544. IF_DEBUG(CRITSEC) {
  2545. DPUT1("PutEvent: ENTERING Adapters[%d].EventQueueCritSec\n",
  2546. AdapterNumber
  2547. );
  2548. }
  2549. EnterCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2550. if (Adapters[AdapterNumber].EventQueueTail == NULL) {
  2551. Adapters[AdapterNumber].EventQueueHead = pCcb;
  2552. } else {
  2553. Adapters[AdapterNumber].EventQueueTail->pNext = pCcb;
  2554. }
  2555. Adapters[AdapterNumber].EventQueueTail = pCcb;
  2556. ++Adapters[AdapterNumber].QueueElements;
  2557. IF_DEBUG(EVENT_QUEUE) {
  2558. DPUT5("PutEvent: Added %x to adapter %d EventQueue. Head=%x Tail=%x Elements=%d\n",
  2559. pCcb,
  2560. AdapterNumber,
  2561. Adapters[AdapterNumber].EventQueueHead,
  2562. Adapters[AdapterNumber].EventQueueTail,
  2563. Adapters[AdapterNumber].QueueElements
  2564. );
  2565. }
  2566. IF_DEBUG(CRITSEC) {
  2567. DPUT1("PutEvent: LEAVING Adapters[%d].EventQueueCritSec\n",
  2568. AdapterNumber
  2569. );
  2570. }
  2571. LeaveCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2572. }
  2573. PLLC_CCB
  2574. PeekEvent(
  2575. IN BYTE AdapterNumber
  2576. )
  2577. /*++
  2578. Routine Description:
  2579. Reads the next completed CCB from the head of the Event Queue. If the
  2580. queue is empty (QueueElements == 0) then returns NULL. The queue is
  2581. accessed inside a critical section
  2582. Arguments:
  2583. None.
  2584. Return Value:
  2585. PLLC_CCB
  2586. Success - pointer to CCB at queue head
  2587. Failure - NULL
  2588. --*/
  2589. {
  2590. PLLC_CCB pCcb;
  2591. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  2592. IF_DEBUG(CRITSEC) {
  2593. DPUT1("PeekEvent: ENTERING Adapters[%d].EventQueueCritSec\n",
  2594. AdapterNumber
  2595. );
  2596. }
  2597. EnterCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2598. if (Adapters[AdapterNumber].QueueElements) {
  2599. pCcb = Adapters[AdapterNumber].EventQueueHead;
  2600. IF_DEBUG(EVENT_QUEUE) {
  2601. DPUT5("PeekEvent: CCB %x from adapter %d queue head. Head=%x Tail=%x Elements=%d\n",
  2602. pCcb,
  2603. AdapterNumber,
  2604. Adapters[AdapterNumber].EventQueueHead,
  2605. Adapters[AdapterNumber].EventQueueTail,
  2606. Adapters[AdapterNumber].QueueElements
  2607. );
  2608. }
  2609. } else {
  2610. pCcb = NULL;
  2611. IF_DEBUG(EVENT_QUEUE) {
  2612. DPUT1("PeekEvent: adapter %d queue is EMPTY!\n", AdapterNumber);
  2613. }
  2614. }
  2615. IF_DEBUG(CRITSEC) {
  2616. DPUT1("PeekEvent: LEAVING Adapters[%d].EventQueueCritSec\n",
  2617. AdapterNumber
  2618. );
  2619. }
  2620. LeaveCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2621. IF_DEBUG(CRITICAL) {
  2622. CRITDUMP(("PeekEvent: returning %x\n", pCcb));
  2623. }
  2624. return pCcb;
  2625. }
  2626. PLLC_CCB
  2627. GetEvent(
  2628. IN BYTE AdapterNumber
  2629. )
  2630. /*++
  2631. Routine Description:
  2632. Gets the next completed CCB from the head of the Event Queue. If the
  2633. queue is empty (QueueElements == 0) then returns NULL. If there is a
  2634. completed event in the queue, removes it and advances the queue head
  2635. to the next element. The queue is accessed inside a critical section
  2636. Arguments:
  2637. AdapterNumber - which adapter's event queue to remove event from
  2638. Return Value:
  2639. PLLC_CCB
  2640. Success - pointer to dequeued CCB
  2641. Failure - NULL
  2642. --*/
  2643. {
  2644. PLLC_CCB pCcb;
  2645. IF_DEBUG(CRITSEC) {
  2646. DPUT1("GetEvent: ENTERING Adapters[%d].EventQueueCritSec\n",
  2647. AdapterNumber
  2648. );
  2649. }
  2650. EnterCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2651. if (Adapters[AdapterNumber].QueueElements) {
  2652. pCcb = Adapters[AdapterNumber].EventQueueHead;
  2653. Adapters[AdapterNumber].EventQueueHead = pCcb->pNext;
  2654. --Adapters[AdapterNumber].QueueElements;
  2655. if (Adapters[AdapterNumber].QueueElements == 0) {
  2656. Adapters[AdapterNumber].EventQueueTail = NULL;
  2657. }
  2658. IF_DEBUG(EVENT_QUEUE) {
  2659. DPUT5("GetEvent: Removed %x from adapter %d EventQueue. Head=%x Tail=%x Elements=%d\n",
  2660. pCcb,
  2661. AdapterNumber,
  2662. Adapters[AdapterNumber].EventQueueHead,
  2663. Adapters[AdapterNumber].EventQueueTail,
  2664. Adapters[AdapterNumber].QueueElements
  2665. );
  2666. }
  2667. } else {
  2668. pCcb = NULL;
  2669. IF_DEBUG(EVENT_QUEUE) {
  2670. DPUT1("GetEvent: queue for adapter %d is EMPTY!\n", AdapterNumber);
  2671. }
  2672. }
  2673. IF_DEBUG(CRITSEC) {
  2674. DPUT1("GetEvent: LEAVING Adapters[%d].EventQueueCritSec\n",
  2675. AdapterNumber
  2676. );
  2677. }
  2678. LeaveCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2679. IF_DEBUG(CRITICAL) {
  2680. CRITDUMP(("GetEvent: returning %x\n", pCcb));
  2681. }
  2682. return pCcb;
  2683. }
  2684. VOID
  2685. FlushEventQueue(
  2686. IN BYTE AdapterNumber
  2687. )
  2688. /*++
  2689. Routine Description:
  2690. Removes all READ CCBs from the event queue.
  2691. Arguments:
  2692. None.
  2693. Return Value:
  2694. None.
  2695. --*/
  2696. {
  2697. PLLC_CCB pCcb;
  2698. IF_DEBUG(CRITSEC) {
  2699. DPUT1("FlushEventQueue: ENTERING Adapters[%d].EventQueueCritSec\n",
  2700. AdapterNumber
  2701. );
  2702. }
  2703. EnterCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2704. #if DBG
  2705. if (!Adapters[AdapterNumber].QueueElements) {
  2706. DPUT("FlushEventQueue: queue is EMPTY!\n");
  2707. }
  2708. #endif
  2709. while (Adapters[AdapterNumber].QueueElements) {
  2710. pCcb = Adapters[AdapterNumber].EventQueueHead;
  2711. --Adapters[AdapterNumber].QueueElements;
  2712. Adapters[AdapterNumber].EventQueueHead = pCcb->pNext;
  2713. IF_DEBUG(EVENT_QUEUE) {
  2714. DPUT5("FlushEventQueue: Removed %x from adapter %d EventQueue. Head=%x Tail=%x Elements=%d\n",
  2715. pCcb,
  2716. AdapterNumber,
  2717. Adapters[AdapterNumber].EventQueueHead,
  2718. Adapters[AdapterNumber].EventQueueTail,
  2719. Adapters[AdapterNumber].QueueElements
  2720. );
  2721. }
  2722. //
  2723. // BUGBUG - received frames?
  2724. //
  2725. LocalFree((HLOCAL)pCcb);
  2726. IF_DEBUG(DLC_ALLOC) {
  2727. DPUT1("FREE: freed block @ %x\n", pCcb);
  2728. }
  2729. Adapters[AdapterNumber].EventQueueTail = NULL;
  2730. }
  2731. IF_DEBUG(CRITSEC) {
  2732. DPUT1("FlushEventQueue: ENTERING Adapters[%d].EventQueueCritSec\n",
  2733. AdapterNumber
  2734. );
  2735. }
  2736. LeaveCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2737. }
  2738. VOID
  2739. RemoveDeadReceives(
  2740. IN PLLC_CCB pCcb
  2741. )
  2742. /*++
  2743. Routine Description:
  2744. The receive command described by pCcb has completed (terminated). We must
  2745. remove any queued reads completed by data receive which refer to this CCB
  2746. Arguments:
  2747. pCcb - pointer to RECEIVE CCB (NT)
  2748. Return Value:
  2749. None.
  2750. --*/
  2751. {
  2752. PLLC_CCB thisCcb;
  2753. PLLC_CCB nextCcb;
  2754. PLLC_CCB lastCcb = NULL;
  2755. PLLC_CCB prevCcb = NULL;
  2756. DWORD i;
  2757. PDOS_ADAPTER pAdapter = &Adapters[pCcb->uchAdapterNumber];
  2758. PLLC_CCB* pQueue;
  2759. //
  2760. // remove any queued receives from the event queue. Note: There should NOT
  2761. // be any. The reason: there can't be any data received after the associated
  2762. // RECEIVE command has been cancelled or terminated. This is the theory,
  2763. // anyway
  2764. //
  2765. EnterCriticalSection(&pAdapter->EventQueueCritSec);
  2766. thisCcb = pAdapter->EventQueueHead;
  2767. for (i = pAdapter->QueueElements; i; --i) {
  2768. nextCcb = thisCcb->pNext;
  2769. if (thisCcb->u.pParameterTable->Read.ulNotificationFlag == (ULONG)pCcb) {
  2770. IF_DEBUG(EVENT_QUEUE) {
  2771. DPUT5("RemoveDeadReceives: Removed %x from adapter %d EventQueue. Head=%x Tail=%x Elements=%d\n",
  2772. thisCcb,
  2773. pCcb->uchAdapterNumber,
  2774. pAdapter->EventQueueHead,
  2775. pAdapter->EventQueueTail,
  2776. pAdapter->QueueElements
  2777. );
  2778. }
  2779. ReleaseReceiveResources(thisCcb);
  2780. if (pAdapter->EventQueueHead == thisCcb) {
  2781. pAdapter->EventQueueHead = nextCcb;
  2782. }
  2783. --pAdapter->QueueElements;
  2784. lastCcb = thisCcb;
  2785. } else {
  2786. prevCcb = thisCcb;
  2787. }
  2788. thisCcb = nextCcb;
  2789. }
  2790. if (pAdapter->EventQueueTail == lastCcb) {
  2791. pAdapter->EventQueueTail = prevCcb;
  2792. }
  2793. LeaveCriticalSection(&pAdapter->EventQueueCritSec);
  2794. //
  2795. // remove any queued deferred receives from the deferred I-Frame queue for
  2796. // this SAP or link station
  2797. //
  2798. EnterCriticalSection(&pAdapter->LocalBusyCritSec);
  2799. if (pAdapter->DeferredReceives) {
  2800. ASSERT(pAdapter->FirstIndex != NO_LINKS_BUSY);
  2801. ASSERT(pAdapter->LastIndex != NO_LINKS_BUSY);
  2802. for (i = pAdapter->FirstIndex; i <= pAdapter->LastIndex; ++i) {
  2803. pQueue = &pAdapter->LocalBusyInfo[i].Queue;
  2804. for (thisCcb = *pQueue; thisCcb; thisCcb = thisCcb->pNext) {
  2805. if (thisCcb->u.pParameterTable->Read.ulNotificationFlag == (ULONG)pCcb) {
  2806. IF_DEBUG(EVENT_QUEUE) {
  2807. DPUT3("RemoveDeadReceives: Removed %x from adapter %d BusyList. Queue=%x\n",
  2808. thisCcb,
  2809. pCcb->uchAdapterNumber,
  2810. pAdapter->LocalBusyInfo[i].Queue
  2811. );
  2812. }
  2813. *pQueue = thisCcb->pNext;
  2814. ReleaseReceiveResources(thisCcb);
  2815. #if DBG
  2816. --pAdapter->LocalBusyInfo[i].Depth;
  2817. #endif
  2818. thisCcb = *pQueue;
  2819. } else {
  2820. pQueue = &thisCcb->pNext;
  2821. }
  2822. }
  2823. if (pAdapter->LocalBusyInfo[i].Queue == NULL) {
  2824. pAdapter->LocalBusyInfo[i].State = NOT_BUSY;
  2825. --pAdapter->DeferredReceives;
  2826. }
  2827. }
  2828. //
  2829. // reset the indicies
  2830. //
  2831. if (pAdapter->DeferredReceives) {
  2832. for (i = pAdapter->FirstIndex; i <= pAdapter->LastIndex; ++i) {
  2833. if (pAdapter->LocalBusyInfo[i].State != NOT_BUSY) {
  2834. pAdapter->FirstIndex = i;
  2835. break;
  2836. }
  2837. }
  2838. for (i = pAdapter->LastIndex; i > pAdapter->FirstIndex; --i) {
  2839. if (pAdapter->LocalBusyInfo[i].State != NOT_BUSY) {
  2840. pAdapter->LastIndex = i;
  2841. break;
  2842. }
  2843. }
  2844. } else {
  2845. pAdapter->FirstIndex = NO_LINKS_BUSY;
  2846. pAdapter->LastIndex = NO_LINKS_BUSY;
  2847. }
  2848. }
  2849. LeaveCriticalSection(&pAdapter->LocalBusyCritSec);
  2850. }
  2851. VOID
  2852. ReleaseReceiveResources(
  2853. IN PLLC_CCB pCcb
  2854. )
  2855. /*++
  2856. Routine Description:
  2857. Releases all resources used by a completed data receive READ CCB
  2858. Arguments:
  2859. pCcb - pointer to completed READ CCB. We have to return all received
  2860. frames to buffer pool, and the READ CCB and parameter table to
  2861. the proceess heap
  2862. Return Value:
  2863. None.
  2864. --*/
  2865. {
  2866. WORD buffersLeft;
  2867. //
  2868. // this is a data receive - return the data buffers to the pool
  2869. //
  2870. ASSERT(pCcb->u.pParameterTable->Read.uchEvent == LLC_EVENT_RECEIVE_DATA);
  2871. BufferFree(pCcb->uchAdapterNumber,
  2872. pCcb->u.pParameterTable->Read.Type.Event.pReceivedFrame,
  2873. &buffersLeft
  2874. );
  2875. //
  2876. // free the READ CCB and parameter table
  2877. //
  2878. LocalFree((HLOCAL)pCcb);
  2879. IF_DEBUG(DLC_ALLOC) {
  2880. DPUT1("FREE: freed block @ %x\n", pCcb);
  2881. }
  2882. }
  2883. VOID
  2884. IssueHardwareInterrupt(
  2885. VOID
  2886. )
  2887. /*++
  2888. Routine Description:
  2889. Issue a simulated hardware interrupt to the VDM. This routine exists because
  2890. we were losing interrupts - seeing more calls to call_ica_hw_interrupt than
  2891. calls to VrDlcHwInterrupt. Hence presumably simulated interrupts were being
  2892. lost. So we now only have 1 un-acknowledged simulated interrupt outstanding
  2893. at any one time. If we already have interrupts outstanding then we just
  2894. increment a counter of pending interrupts. When we dismiss the current
  2895. interrupt using the companion routine AcknowledgeHardwareInterrupt, we can
  2896. generate at that point a queued interrupt
  2897. Arguments:
  2898. None.
  2899. Return Value:
  2900. None.
  2901. --*/
  2902. {
  2903. IF_DEBUG(CRITICAL) {
  2904. CRITDUMP(("*** INT ***\n"));
  2905. }
  2906. IF_DEBUG(DLC_ASYNC) {
  2907. DPUT("*** INT ***\n");
  2908. }
  2909. //
  2910. // increment the hardware interrupt counter under critical section control.
  2911. // The counter starts at -1, so 0 means 1 interrupt outstanding. If we go
  2912. // to >1 then we have interrupts queued and must wait until the current
  2913. // one is dismissed
  2914. //
  2915. IF_DEBUG(CRITSEC) {
  2916. DPUT("IssueHardwareInterrupt: ENTERING HardwareIntCritSec\n");
  2917. }
  2918. EnterCriticalSection(&HardwareIntCritSec);
  2919. ++HardwareIntsQueued;
  2920. if (!HardwareIntsQueued) {
  2921. VrQueueCompletionHandler(VrDlcHwInterrupt);
  2922. //call_ica_hw_interrupt(NETWORK_ICA, NETWORK_LINE, 1);
  2923. VrRaiseInterrupt();
  2924. } else {
  2925. IF_DEBUG(CRITICAL) {
  2926. CRITDUMP(("*** INT Queued (%d) ***\n", HardwareIntsQueued));
  2927. }
  2928. }
  2929. IF_DEBUG(CRITSEC) {
  2930. DPUT("IssueHardwareInterrupt: LEAVING HardwareIntCritSec\n");
  2931. }
  2932. LeaveCriticalSection(&HardwareIntCritSec);
  2933. IF_DEBUG(DLC_ASYNC) {
  2934. DPUT("*** EOF INT ***\n");
  2935. }
  2936. }
  2937. VOID
  2938. AcknowledgeHardwareInterrupt(
  2939. VOID
  2940. )
  2941. /*++
  2942. Routine Description:
  2943. The companion routine to IssueHardwareInterrupt. Here we just decrement the
  2944. interrupt counter. If it is >= 0 then we still have interrupts pending, so
  2945. we issue a new interrupt request. This seems to work - we don't lose
  2946. interrupt requests to the VDM
  2947. Arguments:
  2948. None.
  2949. Return Value:
  2950. None.
  2951. --*/
  2952. {
  2953. #if DBG
  2954. LONG deferredInts;
  2955. #endif
  2956. IF_DEBUG(CRITICAL) {
  2957. CRITDUMP(("*** INT ACK ***\n"));
  2958. }
  2959. IF_DEBUG(DLC_ASYNC) {
  2960. DPUT("*** INT ACK ***\n");
  2961. }
  2962. //
  2963. // decrement the interrupt counter within the critical section. If it goes
  2964. // to -1 then we have no more outstanding hardware interrupt requests. If
  2965. // it is > -1 then issue a new interrupt request
  2966. //
  2967. IF_DEBUG(CRITSEC) {
  2968. DPUT("AcknowledgeHardwareInterrupt: ENTERING HardwareIntCritSec\n");
  2969. }
  2970. EnterCriticalSection(&HardwareIntCritSec);
  2971. --HardwareIntsQueued;
  2972. #if DBG
  2973. deferredInts = HardwareIntsQueued;
  2974. #endif
  2975. //
  2976. // sanity check
  2977. //
  2978. ASSERT(HardwareIntsQueued >= -1);
  2979. if (HardwareIntsQueued >= 0) {
  2980. IF_DEBUG(CRITICAL) {
  2981. CRITDUMP(("*** INT2 ***\n"));
  2982. }
  2983. VrQueueCompletionHandler(VrDlcHwInterrupt);
  2984. //call_ica_hw_interrupt(NETWORK_ICA, NETWORK_LINE, 1);
  2985. VrRaiseInterrupt();
  2986. }
  2987. IF_DEBUG(CRITSEC) {
  2988. DPUT("AcknowledgeHardwareInterrupt: LEAVING HardwareIntCritSec\n");
  2989. }
  2990. LeaveCriticalSection(&HardwareIntCritSec);
  2991. #if DBG
  2992. IF_DEBUG(CRITICAL) {
  2993. CRITDUMP(("*** EOF INT ACK (%d) ***\n", deferredInts));
  2994. }
  2995. IF_DEBUG(DLC_ASYNC) {
  2996. DPUT1("*** EOF INT ACK (%d) ***\n", deferredInts);
  2997. }
  2998. #endif
  2999. }
  3000. VOID
  3001. CancelHardwareInterrupts(
  3002. IN LONG Count
  3003. )
  3004. /*++
  3005. Routine Description:
  3006. Used to decrement the number of pending h/w interrupts. We need to do this
  3007. when removing completed READ CCBs from an event queue for which h/w
  3008. interrupts have been issued
  3009. Arguments:
  3010. Count - number of h/w interrupt requests to cancel. Used to aggregate
  3011. the cancels, saving Count-1 calls to this routine & Enter &
  3012. Leave critical section calls
  3013. Return Value:
  3014. None.
  3015. --*/
  3016. {
  3017. #if DBG
  3018. LONG deferredInts;
  3019. #endif
  3020. IF_DEBUG(CRITICAL) {
  3021. CRITDUMP(("*** CancelHardwareInterrupts(%d) ***\n", Count));
  3022. }
  3023. IF_DEBUG(DLC_ASYNC) {
  3024. DPUT1("*** CancelHardwareInterrupts(%d) ***\n", Count);
  3025. }
  3026. //
  3027. // decrement the interrupt counter within the critical section. If it goes
  3028. // to -1 then we have no more outstanding hardware interrupt requests. If
  3029. // it is > -1 then issue a new interrupt request
  3030. //
  3031. IF_DEBUG(CRITSEC) {
  3032. DPUT("CancelHardwareInterrupts: ENTERING HardwareIntCritSec\n");
  3033. }
  3034. EnterCriticalSection(&HardwareIntCritSec);
  3035. HardwareIntsQueued -= Count;
  3036. #if DBG
  3037. deferredInts = HardwareIntsQueued;
  3038. #endif
  3039. //
  3040. // sanity check
  3041. //
  3042. ASSERT(HardwareIntsQueued >= -1);
  3043. IF_DEBUG(CRITSEC) {
  3044. DPUT("CancelHardwareInterrupts: LEAVING HardwareIntCritSec\n");
  3045. }
  3046. LeaveCriticalSection(&HardwareIntCritSec);
  3047. #if DBG
  3048. IF_DEBUG(CRITICAL) {
  3049. CRITDUMP(("*** EOF CancelHardwareInterrupts (%d) ***\n", deferredInts));
  3050. }
  3051. IF_DEBUG(DLC_ASYNC) {
  3052. DPUT1("*** EOF CancelHardwareInterrupts (%d) ***\n", deferredInts);
  3053. }
  3054. #endif
  3055. }