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

3991 lines
121 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 = 0;
  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. //
  979. // PYS 03/18/02
  980. //
  981. // There are two cases where pReadCcb is NULL. Either
  982. // LLC_EVENT_RECEIVE_DATA which calls ProcessReceiveFrame
  983. // in which case we do have a valid stationId or
  984. // LLC_EVENT_TRANSMIT_COMPLETE, in which case stationId is
  985. // uninitialize as prefix will warn on the two calls down
  986. // here with stationId. Since RemoveDeferredReceive asserts
  987. // on 0 stationId, I added it as a sentinel above and will
  988. // not call it when it occurs. I don't know how to generate
  989. // this, so I leave a breakpoint in debug to catch this.
  990. //
  991. //
  992. #if DBG
  993. {
  994. // ensure that the CCB that we are removing from the head of the queue
  995. // is the same one returned by FindCompletedRead
  996. //
  997. PLLC_CCB CcbAtQueueHead;
  998. CcbAtQueueHead = RemoveDeferredReceive(adapterNumber, stationId, &GetNewEvent);
  999. if (pReadCcb != CcbAtQueueHead) {
  1000. DbgPrint("VrDlcHwInterrupt: "
  1001. "*** ERROR: GetEvent CCB (%x) != PeekEvent CCB (%x) ***\n",
  1002. CcbAtQueueHead,
  1003. pReadCcb
  1004. );
  1005. DbgBreakPoint();
  1006. }
  1007. if (GetNewEvent) {
  1008. reasonCode = 'R';
  1009. reasonCount = Adapters[adapterNumber].LocalBusyInfo[LINK_ID(stationId)].Depth;
  1010. }
  1011. }
  1012. #else
  1013. RemoveDeferredReceive(adapterNumber, stationId, &GetNewEvent);
  1014. #endif
  1015. }
  1016. LocalFree((HLOCAL)pReadCcb);
  1017. IF_DEBUG(DLC_ALLOC) {
  1018. DPUT1("FREE: freed block @ %x\n", pReadCcb);
  1019. }
  1020. }
  1021. //
  1022. // RLF 09/24/92
  1023. //
  1024. // Re-ordered issuing h/w int & removing freeing READ CCB since now that
  1025. // we peek first & remove later, we must be sure that a fully completed
  1026. // READ CCB is removed from the queue before we request a new h/w
  1027. // interrupt. I am not expecting this to be re-entered, but who knows
  1028. // what might happen on a MP machine?
  1029. //
  1030. if (GetNewEvent) {
  1031. //
  1032. // The event generates more than one appendage call. We need a new
  1033. // h/w interrupt for each extra appendage call
  1034. //
  1035. #if DBG
  1036. IF_DEBUG(DLC_ASYNC) {
  1037. DPUT2("*** Calling ica_hw_interrupt from within ISR. Cause = %d %s ***\n",
  1038. reasonCount,
  1039. reasonCode == 'T' ? "multiple transmits" : "deferred I-Frames"
  1040. );
  1041. }
  1042. IF_DEBUG(CRITICAL) {
  1043. CRITDUMP(("*** Calling ica_hw_interrupt from within ISR. Cause = %d %s ***\n",
  1044. reasonCount,
  1045. reasonCode == 'T' ? "multiple transmits" : "deferred I-Frames"
  1046. ));
  1047. }
  1048. #endif
  1049. IssueHardwareInterrupt();
  1050. }
  1051. //
  1052. // if an NT RECEIVE CCB was completed or terminated, remove any pending
  1053. // READ CCBs completed by received data events which reference the terminated
  1054. // RECEIVE
  1055. //
  1056. if (cancelledReceive) {
  1057. RemoveDeadReceives(cancelledReceive);
  1058. LocalFree(cancelledReceive);
  1059. IF_DEBUG(DLC_ALLOC) {
  1060. DPUT1("FREE: freed block @ %x\n", cancelledReceive);
  1061. }
  1062. }
  1063. //
  1064. // acknowledge this hardware interrupt, meaning decrement the interrupt
  1065. // counter and issue a new request if we've got interrupts outstanding
  1066. //
  1067. AcknowledgeHardwareInterrupt();
  1068. //
  1069. // return TRUE to indicate that we have accepted the hw interrupt
  1070. //
  1071. return TRUE;
  1072. }
  1073. PLLC_CCB
  1074. FindCompletedRead(
  1075. OUT READ_FRAME_TYPE* pFrameType
  1076. )
  1077. /*++
  1078. Routine Description:
  1079. Finds the next READ CCB to process - first looks at the event queue of
  1080. current completed CCBs, then at the deferred I-Frame queue.
  1081. This routine attempts to bounce between both adapters if they are both
  1082. active, in order not to only service the most active adapter and ignore
  1083. for a long period of time pending completed CCBs on the less active
  1084. adapter
  1085. NOTE: we only return a deferred I-Frame when the app has issued
  1086. DLC.FLOW.CONTROL against the station
  1087. Arguments:
  1088. pFrameType - returns the type of event found - CURRENT (event READ is at
  1089. head of EventQueue) or DEFERRED (event READ is at head of
  1090. a LOCAL_BUSY_INFO queue)
  1091. Return Value:
  1092. PLLC_CCB
  1093. pointer to completed READ CCB. The CCB is NOT DEQUEUED - the caller
  1094. must do that when the event has been completely disposed of to the VDM
  1095. --*/
  1096. {
  1097. DWORD i;
  1098. PLLC_CCB pCcb = NULL;
  1099. static BYTE ThisAdapter = 0;
  1100. for (i = 0; !Adapters[ThisAdapter].IsOpen && i < DOS_DLC_MAX_ADAPTERS; ++i) {
  1101. ThisAdapter = (ThisAdapter + 1) & (DOS_DLC_MAX_ADAPTERS - 1);
  1102. }
  1103. if (i == DOS_DLC_MAX_ADAPTERS) {
  1104. //
  1105. // no adapters alive?
  1106. //
  1107. IF_DEBUG(DLC_ASYNC) {
  1108. DPUT("*** FindCompletedRead: no open adapters??? ***\n");
  1109. }
  1110. return NULL;
  1111. }
  1112. //
  1113. // RLF 09/24/92
  1114. //
  1115. // Changed GetEvent to PeekEvent: if there are multiple chained completed
  1116. // CCBs (transmit+?) then we have to leave the READ CCB @ the head of the
  1117. // queue until we have generated call-backs for all chained completions.
  1118. // Only when there are no more completions can we remove the READ CCB from
  1119. // the queue (using GetEvent)
  1120. //
  1121. if (pCcb = PeekEvent(ThisAdapter)) {
  1122. *pFrameType = CURRENT;
  1123. } else {
  1124. //
  1125. // see if there are any deferred I-Frames
  1126. //
  1127. IF_DEBUG(CRITSEC) {
  1128. DPUT1("FindCompletedRead: ENTERING Adapters[%d].LocalBusyCritSec\n",
  1129. ThisAdapter
  1130. );
  1131. }
  1132. EnterCriticalSection(&Adapters[ThisAdapter].LocalBusyCritSec);
  1133. //
  1134. // DeferredReceives is a reference count of link stations in emulated
  1135. // local-busy(buffer) state. They can be BUSY or CLEARING. We are only
  1136. // interested in CLEARING: this means that the app has issued
  1137. // DLC.FLOW.CONTROL to us & presumably has returned some buffers via
  1138. // BUFFER.FREE (if not, we just go back to BUSY state)
  1139. //
  1140. if (Adapters[ThisAdapter].DeferredReceives) {
  1141. for (i = Adapters[ThisAdapter].FirstIndex;
  1142. i <= Adapters[ThisAdapter].LastIndex; ++i) {
  1143. if (Adapters[ThisAdapter].LocalBusyInfo[i].State == CLEARING) {
  1144. if (pCcb = Adapters[ThisAdapter].LocalBusyInfo[i].Queue) {
  1145. *pFrameType = DEFERRED;
  1146. break;
  1147. }
  1148. }
  1149. }
  1150. }
  1151. IF_DEBUG(CRITSEC) {
  1152. DPUT1("FindCompletedRead: LEAVING Adapters[%d].LocalBusyCritSec\n",
  1153. ThisAdapter
  1154. );
  1155. }
  1156. LeaveCriticalSection(&Adapters[ThisAdapter].LocalBusyCritSec);
  1157. }
  1158. IF_DEBUG(DLC_ASYNC) {
  1159. DPUT2("FindCompletedRead: returning READ CCB @ %08x type is %s\n",
  1160. pCcb,
  1161. *pFrameType == DEFERRED ? "DEFERRED" : "CURRENT"
  1162. );
  1163. }
  1164. //
  1165. // next time in, try the other adapter first
  1166. //
  1167. ThisAdapter = (ThisAdapter + 1) & (DOS_DLC_MAX_ADAPTERS - 1);
  1168. return pCcb;
  1169. }
  1170. INDICATION
  1171. ProcessReceiveFrame(
  1172. IN OUT PLLC_CCB* ppCcb,
  1173. IN LLC_DOS_CCB UNALIGNED * pDosCcb,
  1174. IN LLC_DOS_PARMS UNALIGNED * pDosParms,
  1175. OUT LLC_STATUS* Status
  1176. )
  1177. /*++
  1178. Routine Description:
  1179. Determines what to do with a received frame. We try to copy the received
  1180. frame to DOS buffers, but if we have insufficient buffers then we must
  1181. queue or discard the frame. We queue the frame only if it is an I-Frame.
  1182. We must first check the queue of deferred received frames or else risk
  1183. getting out-of-sequence received data in the DOS client.
  1184. When this routine is called, *ppCcb points at one of:
  1185. 1. A deferred I-Frame READ CCB
  1186. 2. A current received data READ CCB, I-Frame or other MAC/data
  1187. NOTE: Deferred I-Frames that are for stations whose local-busy(buffer)
  1188. state has been cleared by the app take precedence over the event
  1189. at the head of the EventQueue
  1190. Arguments:
  1191. ppCcb - pointer to new READ CCB. On output, this will be a non-NULL
  1192. value if we are to free the CCB and the received NT frame
  1193. buffer. If we placed the read on the deferred receive queue
  1194. then we return a NULL
  1195. pDosCcb - pointer to DOS CCB
  1196. pDosParms - pointer to DOS RECEIVE parameter table. If we copy - entirely
  1197. or partially - a frame, the DOS buffer chain is linked to
  1198. the DOS RECEIVE parameter table at FIRST_BUFFER
  1199. Status - if the frame was copied, contains the status to return to the
  1200. DOS appendage:
  1201. LLC_STATUS_SUCCESS
  1202. The frame was completely copied to the DOS buffer(s)
  1203. LLC_STATUS_LOST_DATA_NO_BUFFERS
  1204. The frame could not be copied to the DOS buffers and
  1205. has been discarded. There were NO buffers available.
  1206. The frame was NOT an I-Frame
  1207. LLC_STATUS_LOST_DATA_INADEQUATE_SPACE
  1208. The frame has been partially copied to the DOS buffer(s),
  1209. but the rest of it had to be discarded, because not
  1210. enough DOS buffers were available. The frame was NOT an
  1211. I-Frame
  1212. Status is only meaningful when INDICATE_COMPLETE_RECEIVE is
  1213. returned
  1214. Return Value:
  1215. INDICATION
  1216. INDICATE_RECEIVE_FRAME
  1217. We copied the frame to DOS buffers. Free the CCB and NT buffer and
  1218. return the frame to DOS
  1219. INDICATE_LOCAL_BUSY
  1220. We couldn't copy the received I-Frame to DOS buffers because we don't
  1221. have enough of them. We have put the NT link station into LOCAL BUSY
  1222. (Buffer) state. Indicate local busy to the VDM
  1223. INDICATE_COMPLETE_RECEIVE
  1224. We couldn't copy the received non I-Frame to DOS buffers. Complete
  1225. the DOS RECEIVE command with an error
  1226. --*/
  1227. {
  1228. PLLC_CCB pCcb = *ppCcb;
  1229. PLLC_PARMS pParms = pCcb->u.pParameterTable;
  1230. PLLC_BUFFER pFrame = pParms->Read.Type.Event.pReceivedFrame;
  1231. UCHAR adapter = pCcb->uchAdapterNumber;
  1232. WORD stationId = pFrame->Contiguous.usStationId;
  1233. WORD numBufs;
  1234. WORD bufferSize;
  1235. WORD buffersAvailable;
  1236. WORD nLeft;
  1237. DPLLC_DOS_BUFFER dosBuffers;
  1238. INDICATION indication;
  1239. LLC_STATUS status;
  1240. DWORD flags;
  1241. IF_DEBUG(DLC_ASYNC) {
  1242. DPUT2("ProcessReceiveFrame: adapter=%d, stationId=%x\n", adapter, stationId);
  1243. }
  1244. //
  1245. // make sure we don't read or write bogus Adapter structure
  1246. //
  1247. ASSERT(adapter < DOS_DLC_MAX_ADAPTERS);
  1248. //
  1249. // we are NOT chaining receive frames. DOS receive frame processing assumes
  1250. // that we get one frame at a time. Assert that it is so
  1251. //
  1252. ASSERT(pFrame->Contiguous.pNextFrame == NULL);
  1253. //
  1254. // get the contiguous and break flags for CopyFrame based on the RECEIVE options
  1255. //
  1256. flags = ((pFrame->Contiguous.uchOptions & (LLC_CONTIGUOUS_MAC | LLC_CONTIGUOUS_DATA))
  1257. ? CF_CONTIGUOUS : 0)
  1258. | ((pFrame->Contiguous.uchOptions & LLC_BREAK) ? CF_BREAK : 0);
  1259. //
  1260. // calculate the number of buffers required to receive this frame
  1261. //
  1262. numBufs = CalculateBufferRequirement(adapter,
  1263. stationId,
  1264. pFrame,
  1265. pDosParms,
  1266. &bufferSize
  1267. );
  1268. //
  1269. // if the frame is an I-Frame then we have to perform these checks:
  1270. //
  1271. // 1. if there is already a deferred packet for this station ID/adapter
  1272. // then we must try to receive this before the frame in hand
  1273. //
  1274. // 2. if there aren't sufficient buffers for the request then we must
  1275. // queue this frame on the deferred queue (if it isn't there already)
  1276. // and indicate that we have a local busy (buffers) state to the DOS
  1277. // client
  1278. //
  1279. if (pFrame->Contiguous.uchMsgType == LLC_I_FRAME) {
  1280. IF_DEBUG(DLC_ASYNC) {
  1281. DPUT("ProcessReceiveFrame: I-Frame\n");
  1282. }
  1283. //
  1284. // try to allocate the required number of buffers as a chain - returns
  1285. // DOS pointers, ie unusable in flat data space. Note that if we have
  1286. // deferred receives then we may be able to satisfy the request now.
  1287. // We must allocate all the required buffers for an I-Frame, or none
  1288. //
  1289. status = GetBuffers(GET_POOL_INDEX(adapter, stationId),
  1290. numBufs,
  1291. &dosBuffers,
  1292. &nLeft,
  1293. FALSE,
  1294. NULL
  1295. );
  1296. if (status == LLC_STATUS_SUCCESS) {
  1297. //
  1298. // we managed to allocate the required number of DOS buffers. Copy
  1299. // the NT frame to the DOS buffers and set the indication to return
  1300. // the I-Frame and free the READ CCB and NT frame buffer
  1301. //
  1302. status = CopyFrame(pFrame,
  1303. dosBuffers,
  1304. READ_WORD(&pDosParms->Receive.usUserLength),
  1305. bufferSize,
  1306. flags
  1307. );
  1308. ASSERT(status == LLC_STATUS_SUCCESS);
  1309. indication = INDICATE_RECEIVE_FRAME;
  1310. } else {
  1311. //
  1312. // we couldn't get the required number of DOS buffers. We must
  1313. // queue this I-Frame (& any subsequent I-Frames received for this
  1314. // link station) on the deferred queue and indicate the local
  1315. // busy (buffers) state to the DOS client. Set pCcb to NULL to
  1316. // indicate that it mustn't be deallocated by the caller (it has
  1317. // already been deallocated in SetEmulatedLocalBusyState)
  1318. //
  1319. // We set the LOCAL BUSY (Buffer) state as quickly as possible: we
  1320. // don't want more than 1 I-Frame queued per link station if we
  1321. // can help it
  1322. //
  1323. SetEmulatedLocalBusyState(adapter, stationId);
  1324. pCcb = NULL;
  1325. indication = INDICATE_LOCAL_BUSY;
  1326. }
  1327. } else {
  1328. IF_DEBUG(DLC_ASYNC) {
  1329. DPUT("ProcessReceiveFrame: other MAC/DATA Frame\n");
  1330. }
  1331. //
  1332. // the frame is not an I-Frame. If we don't have sufficient buffers to
  1333. // receive it then we can discard it
  1334. //
  1335. status = GetBuffers(GET_POOL_INDEX(adapter, stationId),
  1336. numBufs,
  1337. &dosBuffers,
  1338. &nLeft,
  1339. TRUE,
  1340. &buffersAvailable
  1341. );
  1342. if (status == LLC_STATUS_SUCCESS) {
  1343. //
  1344. // we got some DOS buffers, but maybe not all requirement
  1345. //
  1346. if (buffersAvailable < numBufs) {
  1347. //
  1348. // set the status required to be returned to DOS in the completed
  1349. // RECEIVE CCB and set the CF_PARTIAL flag so that CopyFrame
  1350. // will know to terminate early
  1351. //
  1352. *Status = LLC_STATUS_LOST_DATA_INADEQUATE_SPACE;
  1353. flags |= CF_PARTIAL;
  1354. indication = INDICATE_COMPLETE_RECEIVE;
  1355. } else {
  1356. //
  1357. // we allocated all required DOS buffers for this frame
  1358. //
  1359. indication = INDICATE_RECEIVE_FRAME;
  1360. }
  1361. //
  1362. // copy the whole or partial frame
  1363. //
  1364. status = CopyFrame(pFrame,
  1365. dosBuffers,
  1366. READ_WORD(&pDosParms->Receive.usUserLength),
  1367. bufferSize,
  1368. flags
  1369. );
  1370. IF_DEBUG(DLC_ASYNC) {
  1371. DPUT1("ProcessReceiveFrame: CopyFrame (non-I-Frame) returns %x\n", status);
  1372. }
  1373. } else {
  1374. //
  1375. // no DOS buffers at all
  1376. //
  1377. *Status = LLC_STATUS_LOST_DATA_NO_BUFFERS;
  1378. indication = INDICATE_COMPLETE_RECEIVE;
  1379. }
  1380. }
  1381. //
  1382. // set the FIRST_BUFFER field in the DOS RECEIVE parameter table. This
  1383. // is only meaningful if we're going to complete the RECEIVE, with either
  1384. // success or failure status. Use WRITE_DWORD in case the parameter table
  1385. // is unaligned
  1386. //
  1387. WRITE_DWORD(&pDosParms->Receive.pFirstBuffer, dosBuffers);
  1388. //
  1389. // if we are returning DOS buffers, then return the BUFFERS_LEFT field
  1390. //
  1391. if (dosBuffers) {
  1392. PLLC_DOS_BUFFER pDosBuffer = (PLLC_DOS_BUFFER)DOS_PTR_TO_FLAT(dosBuffers);
  1393. WRITE_WORD(&pDosBuffer->Contiguous.cBuffersLeft, nLeft);
  1394. }
  1395. //
  1396. // set *ppCcb. If this contains non-NULL on return then the caller will
  1397. // deallocate the CCB and free the NT buffer(s)
  1398. //
  1399. *ppCcb = pCcb;
  1400. //
  1401. // return indication of action to take
  1402. //
  1403. return indication;
  1404. }
  1405. LOCAL_BUSY_STATE
  1406. QueryEmulatedLocalBusyState(
  1407. IN BYTE AdapterNumber,
  1408. IN WORD StationId
  1409. )
  1410. /*++
  1411. Routine Description:
  1412. Gets the current local-busy(buffer) state of the requested link station on
  1413. a particular adapter
  1414. Arguments:
  1415. AdapterNumber - which adapter
  1416. StationId - which link station
  1417. Return Value:
  1418. LOCAL_BUSY_STATE
  1419. NOT_BUSY
  1420. AdapterNumber/StationId has no deferred I-Frames
  1421. BUSY
  1422. AdapterNumber/StationId has deferred I-Frames and has not yet
  1423. received DLC.FLOW.CONTROL(local-busy(buffer), reset) from the
  1424. DOS DLC app
  1425. CLEARING
  1426. AdapterNumber/StationId has deferred I-Frames AND has received
  1427. DLC.FLOW.CONTROL(local-busy(buffer), reset) from the DOS DLC app
  1428. and is currently trying to unload I-Frames to DOS DLC app
  1429. --*/
  1430. {
  1431. LOCAL_BUSY_STATE state;
  1432. ASSERT(HIBYTE(StationId) != 0);
  1433. ASSERT(LOBYTE(StationId) != 0);
  1434. IF_DEBUG(CRITSEC) {
  1435. DPUT1("QueryEmulatedLocalBusyState: ENTERING Adapters[%d].LocalBusyCritSec\n",
  1436. AdapterNumber
  1437. );
  1438. }
  1439. EnterCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  1440. state = Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].State;
  1441. if (state == BUSY_BUFFER || state == BUSY_FLOW) {
  1442. state = BUSY;
  1443. }
  1444. IF_DEBUG(CRITSEC) {
  1445. DPUT1("QueryEmulatedLocalBusyState: LEAVING Adapters[%d].LocalBusyCritSec\n",
  1446. AdapterNumber
  1447. );
  1448. }
  1449. LeaveCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  1450. ASSERT(state == NOT_BUSY
  1451. || state == CLEARING
  1452. || state == BUSY
  1453. || state == BUSY_BUFFER
  1454. || state == BUSY_FLOW
  1455. );
  1456. IF_DEBUG(DLC_ASYNC) {
  1457. DPUT1("QueryEmulatedLocalBusyState: returning %s\n",
  1458. state == NOT_BUSY
  1459. ? "NOT_BUSY"
  1460. : state == CLEARING
  1461. ? "CLEARING"
  1462. : state == BUSY
  1463. ? "BUSY"
  1464. : state == BUSY_BUFFER
  1465. ? "BUSY_BUFFER"
  1466. : "BUSY_FLOW"
  1467. );
  1468. }
  1469. return state;
  1470. }
  1471. VOID
  1472. SetEmulatedLocalBusyState(
  1473. IN BYTE AdapterNumber,
  1474. IN WORD StationId
  1475. )
  1476. /*++
  1477. Routine Description:
  1478. Sets the emulated local-busy state to local-busy(buffer). If the state is
  1479. currently NOT_BUSY, sends a DLC.FLOW.CONTROL(local-busy(buffer), set) to
  1480. the DLC driver. In all cases sets the current state to BUSY
  1481. Called during processing of an I-Frame when we run out of DOS buffers into
  1482. which we receive the I-Frame. We can be processing a current I-Frame or a
  1483. deferred I-Frame at the time we run out of buffers: in the first instance
  1484. this routine sets local-busy(buffer) state for the first time; in the
  1485. second instance we regress into local-busy(buffer) state (BUSY) from the
  1486. CLEARING state. This will happen so long as we continue to run out of DOS
  1487. buffers
  1488. Arguments:
  1489. AdapterNumber - which adapter to set emulated local busy state for
  1490. StationId - which link station on AdapterNumber
  1491. Return Value:
  1492. None.
  1493. --*/
  1494. {
  1495. LOCAL_BUSY_STATE state;
  1496. DWORD link = LINK_ID(StationId);
  1497. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  1498. ASSERT(HIBYTE(StationId) != 0); // SAP can't be 0
  1499. ASSERT(LOBYTE(StationId) != 0); // Link Station can't be 0
  1500. IF_DEBUG(CRITSEC) {
  1501. DPUT1("SetEmulatedLocalBusyState: ENTERING Adapters[%d].LocalBusyCritSec\n",
  1502. AdapterNumber
  1503. );
  1504. }
  1505. EnterCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  1506. state = Adapters[AdapterNumber].LocalBusyInfo[link].State;
  1507. ASSERT(state == NOT_BUSY
  1508. || state == CLEARING
  1509. || state == BUSY
  1510. || state == BUSY_BUFFER
  1511. || state == BUSY_FLOW
  1512. );
  1513. //
  1514. // if the state of this link station is currently NOT_BUSY then we have
  1515. // to stop the DLC driver receiving I-Frames for this station. In all
  1516. // cases, put the READ CCB on the end of the deferred queue for this
  1517. // adapter/link station
  1518. //
  1519. Adapters[AdapterNumber].LocalBusyInfo[link].State = BUSY;
  1520. //
  1521. // if the previous state was NOT_BUSY then this is the first time this link
  1522. // station has gone into local-busy(buffer) state. Increment the deferred
  1523. // receive count (number of links in local-busy(buffer) state on this
  1524. // adapter) and send a flow control command to the DLC driver, disabling
  1525. // further I-Frame receives until we clear the backlog
  1526. //
  1527. if (state == NOT_BUSY) {
  1528. IF_DEBUG(DLC_ASYNC) {
  1529. DPUT2("SetEmulatedLocalBusyState: setting %d:%04x to BUSY from NOT_BUSY\n",
  1530. AdapterNumber,
  1531. StationId
  1532. );
  1533. }
  1534. ++Adapters[AdapterNumber].DeferredReceives;
  1535. //
  1536. // update the indexes to reduce search effort when looking for deferred
  1537. // receives
  1538. //
  1539. ASSERT(Adapters[AdapterNumber].FirstIndex <= Adapters[AdapterNumber].LastIndex);
  1540. if (Adapters[AdapterNumber].FirstIndex > link) {
  1541. Adapters[AdapterNumber].FirstIndex = link;
  1542. }
  1543. if (Adapters[AdapterNumber].LastIndex < link
  1544. || Adapters[AdapterNumber].LastIndex == NO_LINKS_BUSY) {
  1545. Adapters[AdapterNumber].LastIndex = link;
  1546. }
  1547. #if DBG
  1548. //ASSERT(DosDlcFlowControl(AdapterNumber, StationId, LLC_SET_LOCAL_BUSY_BUFFER) == LLC_STATUS_SUCCESS);
  1549. ASSERT(DlcFlowControl(AdapterNumber, StationId, LLC_SET_LOCAL_BUSY_USER) == LLC_STATUS_SUCCESS);
  1550. #else
  1551. //DosDlcFlowControl(AdapterNumber, StationId, LLC_SET_LOCAL_BUSY_BUFFER);
  1552. DlcFlowControl(AdapterNumber, StationId, LLC_SET_LOCAL_BUSY_USER);
  1553. #endif
  1554. } else {
  1555. IF_DEBUG(DLC_ASYNC) {
  1556. DPUT3("SetEmulatedLocalBusyState: setting %d:%04x to BUSY from %s\n",
  1557. AdapterNumber,
  1558. StationId,
  1559. state == CLEARING
  1560. ? "CLEARING"
  1561. : state == BUSY_BUFFER
  1562. ? "BUSY_BUFFER"
  1563. : state == BUSY_FLOW
  1564. ? "BUSY_FLOW"
  1565. : "???"
  1566. );
  1567. }
  1568. }
  1569. ASSERT(state != BUSY);
  1570. //
  1571. // add this READ CCB to the end of the deferred receive queue for this
  1572. // adapter/link station and any subsequent READ CCBs which completed with
  1573. // received I-Frames
  1574. //
  1575. DeferAllIFrames(AdapterNumber, StationId);
  1576. IF_DEBUG(DLC_ASYNC) {
  1577. DPUT5("SetEmulatedLocalBusyState(%d, %04x): Ref#=%d, First=%d, Last=%d\n",
  1578. AdapterNumber,
  1579. StationId,
  1580. Adapters[AdapterNumber].DeferredReceives,
  1581. Adapters[AdapterNumber].FirstIndex,
  1582. Adapters[AdapterNumber].LastIndex
  1583. );
  1584. }
  1585. //
  1586. // now reduce the priority of the event handler thread to give the CCB
  1587. // handler thread some time to free buffers & issue DLC.FLOW.CONTROL
  1588. // (mainly for DOS apps which do it in the wrong order)
  1589. //
  1590. SetThreadPriority(hEventThread, THREAD_PRIORITY_LOWEST);
  1591. IF_DEBUG(CRITSEC) {
  1592. DPUT1("SetEmulatedLocalBusyState: LEAVING Adapters[%d].LocalBusyCritSec\n",
  1593. AdapterNumber
  1594. );
  1595. }
  1596. LeaveCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  1597. }
  1598. BOOLEAN
  1599. ResetEmulatedLocalBusyState(
  1600. IN BYTE AdapterNumber,
  1601. IN WORD StationId,
  1602. IN BYTE DlcCommand
  1603. )
  1604. /*++
  1605. Routine Description:
  1606. Clears the local-busy(buffer) state for this adapter/link station. If the
  1607. transition is from BUSY to CLEARING then just changes the state and issues
  1608. a hardware interrupt to the VDM: the reason for this is that the original
  1609. interrupt from the READ which caused us to go into local-busy(buffer) state
  1610. was used to generate a DLC status change event
  1611. Called for a single link station or an entire SAP
  1612. Called in response to receiving DLC.FLOW.CONTROL(local-busy(buffer), reset)
  1613. from DOS app for a SAP or link station
  1614. Arguments:
  1615. AdapterNumber - which adapter to clear the local-busy(buffer) state for
  1616. StationId - which link station on this adapter
  1617. DlcCommand - which DLC command is causing this reset
  1618. Return Value:
  1619. BOOLEAN
  1620. TRUE - state was reset from BUSY to CLEARING
  1621. FALSE - state is NOT_BUSY: invalid request
  1622. --*/
  1623. {
  1624. BOOLEAN reset;
  1625. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  1626. ASSERT(HIBYTE(StationId) != 0); // SAP can't be 0
  1627. //
  1628. // grab the LocalBusyCritSec for the adapter and reset the local-busy(buffer)
  1629. // state for the link station or the entire SAP. If we are resetting for the
  1630. // entire SAP, holding the critical section ensures that new I-Frames won't
  1631. // cause another station to go into emulated local-busy(buffer) state while
  1632. // we are resetting the rest
  1633. //
  1634. IF_DEBUG(CRITSEC) {
  1635. DPUT1("ResetEmulatedLocalBusyState: ENTERING Adapters[%d].LocalBusyCritSec\n",
  1636. AdapterNumber
  1637. );
  1638. }
  1639. EnterCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  1640. if (LOBYTE(StationId) == 0) {
  1641. reset = ResetEmulatedLocalBusyStateSap(AdapterNumber, StationId, DlcCommand);
  1642. } else {
  1643. reset = ResetEmulatedLocalBusyStateLink(AdapterNumber, StationId, DlcCommand);
  1644. }
  1645. IF_DEBUG(CRITSEC) {
  1646. DPUT1("ResetEmulatedLocalBusyState: LEAVING Adapters[%d].LocalBusyCritSec\n",
  1647. AdapterNumber
  1648. );
  1649. }
  1650. LeaveCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  1651. return reset;
  1652. }
  1653. BOOLEAN
  1654. ResetEmulatedLocalBusyStateSap(
  1655. IN BYTE AdapterNumber,
  1656. IN WORD StationId,
  1657. IN BYTE DlcCommand
  1658. )
  1659. /*++
  1660. Routine Description:
  1661. This function is called when the app resets the local-busy(buffer) state
  1662. for an entire SAP
  1663. NB: This function MUST BE CALLED WHILE HOLDING THE LocalBusyCritSec for
  1664. this adapter
  1665. Arguments:
  1666. AdapterNumber - which adapter to
  1667. StationId - SAP:00 - which SAP to reset local-busy(buffer) states for
  1668. DlcCommand - which DLC command is causing this reset
  1669. Return Value:
  1670. BOOLEAN
  1671. TRUE - links reset for this SAP
  1672. FALSE - no links reset for this SAP
  1673. --*/
  1674. {
  1675. DWORD link = Adapters[AdapterNumber].FirstIndex;
  1676. DWORD last = Adapters[AdapterNumber].LastIndex;
  1677. DWORD count = 0;
  1678. LOCAL_BUSY_STATE state;
  1679. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  1680. ASSERT(HIBYTE(StationId) != 0);
  1681. ASSERT(link <= last);
  1682. ASSERT(DlcCommand == LLC_BUFFER_FREE || DlcCommand == LLC_DLC_FLOW_CONTROL);
  1683. IF_DEBUG(DLC_ASYNC) {
  1684. DPUT3("ResetEmulatedLocalBusyStateSap(%d, %04x, %s)\n",
  1685. AdapterNumber,
  1686. StationId,
  1687. DlcCommand == LLC_BUFFER_FREE ? "BUFFER.FREE"
  1688. : DlcCommand == LLC_DLC_FLOW_CONTROL ? "DLC.FLOW.CONTROL"
  1689. : "???"
  1690. );
  1691. }
  1692. //
  1693. // we may have a DLC.FLOW.CONTROL for a SAP which has already been reset
  1694. // by a previous DLC.FLOW.CONTROL
  1695. //
  1696. if (link == NO_LINKS_BUSY) {
  1697. ASSERT(last == NO_LINKS_BUSY);
  1698. IF_DEBUG(DLC_ASYNC) {
  1699. DPUT("ResetEmulatedLocalBusyStateSap: SAP already reset\n");
  1700. }
  1701. return FALSE;
  1702. }
  1703. //
  1704. // since we are holding the LocalBusyCritSec for this adapter, we can use
  1705. // FirstLink and LastLink to try reduce the number of searches for busy
  1706. // stations. No new stations can go busy and change FirstLink or LastLink
  1707. // while we are in this loop
  1708. //
  1709. for (++StationId; link <= last; ++StationId) {
  1710. state = Adapters[AdapterNumber].LocalBusyInfo[link].State;
  1711. ++link;
  1712. if (state == BUSY
  1713. || (state == BUSY_BUFFER && DlcCommand == LLC_DLC_FLOW_CONTROL)
  1714. || (state == BUSY_FLOW && DlcCommand == LLC_BUFFER_FREE)) {
  1715. if (ResetEmulatedLocalBusyStateLink(AdapterNumber, StationId, DlcCommand)) {
  1716. ++count;
  1717. }
  1718. }
  1719. }
  1720. return count != 0;
  1721. }
  1722. BOOLEAN
  1723. ResetEmulatedLocalBusyStateLink(
  1724. IN BYTE AdapterNumber,
  1725. IN WORD StationId,
  1726. IN BYTE DlcCommand
  1727. )
  1728. /*++
  1729. Routine Description:
  1730. This function is called when the app resets the local-busy(buffer) state
  1731. for a single link station
  1732. Clears the local-busy(buffer) state for this adapter/link station. If the
  1733. transition is from BUSY to CLEARING then just changes the state and issues
  1734. a hardware interrupt to the VDM: the reason for this is that the original
  1735. interrupt from the READ which caused us to go into local-busy(buffer) state
  1736. was used to generate a DLC status change event
  1737. NB: This function MUST BE CALLED WHILE HOLDING THE LocalBusyCritSec for
  1738. this adapter
  1739. Arguments:
  1740. AdapterNumber - which adapter to clear the local-busy(buffer) state for
  1741. StationId - which link station on this adapter
  1742. DlcCommand - which DLC command is causing this reset
  1743. Return Value:
  1744. BOOLEAN
  1745. TRUE - state was reset from BUSY to CLEARING
  1746. FALSE - state is NOT_BUSY: invalid request
  1747. --*/
  1748. {
  1749. DWORD link = LINK_ID(StationId);
  1750. LOCAL_BUSY_STATE state;
  1751. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  1752. ASSERT(HIBYTE(StationId) != 0); // SAP can't be 0
  1753. ASSERT(LOBYTE(StationId) != 0); // Link Station can't be 0
  1754. ASSERT(DlcCommand == LLC_BUFFER_FREE || DlcCommand == LLC_DLC_FLOW_CONTROL);
  1755. IF_DEBUG(DLC_ASYNC) {
  1756. DPUT3("ResetEmulatedLocalBusyStateLink(%d, %04x, %s)\n",
  1757. AdapterNumber,
  1758. StationId,
  1759. DlcCommand == LLC_BUFFER_FREE ? "BUFFER.FREE"
  1760. : DlcCommand == LLC_DLC_FLOW_CONTROL ? "DLC.FLOW.CONTROL"
  1761. : "???"
  1762. );
  1763. }
  1764. state = Adapters[AdapterNumber].LocalBusyInfo[link].State;
  1765. ASSERT(state == NOT_BUSY
  1766. || state == CLEARING
  1767. || state == BUSY
  1768. || state == BUSY_BUFFER
  1769. || state == BUSY_FLOW
  1770. );
  1771. if (state == BUSY) {
  1772. //
  1773. // if the state is BUSY then this is the first DLC.FLOW.CONTROL or
  1774. // BUFFER.FREE since we went into local-busy(buffer) state. State
  1775. // transition is to BUSY_FLOW if this is a DLC.FLOW.CONTROL else
  1776. // BUSY_BUFFER
  1777. //
  1778. IF_DEBUG(DLC_ASYNC) {
  1779. DPUT1("ResetEmulatedLocalBusyStateLink: state: BUSY -> %s\n",
  1780. DlcCommand == LLC_BUFFER_FREE ? "BUSY_BUFFER" : "BUSY_FLOW"
  1781. );
  1782. }
  1783. Adapters[AdapterNumber].LocalBusyInfo[link].State = (DlcCommand == LLC_BUFFER_FREE)
  1784. ? BUSY_BUFFER
  1785. : BUSY_FLOW;
  1786. } else if ((state == BUSY_FLOW && DlcCommand == LLC_BUFFER_FREE)
  1787. || (state == BUSY_BUFFER && DlcCommand == LLC_DLC_FLOW_CONTROL)) {
  1788. //
  1789. // state is BUSY_FLOW or BUSY_BUFFER. If this reset is caused by the
  1790. // other half of the state transition requirement then change the state
  1791. //
  1792. IF_DEBUG(DLC_ASYNC) {
  1793. DPUT3("ResetEmulatedLocalBusyStateLink: link %d.%04x changing from %s to CLEARING\n",
  1794. AdapterNumber,
  1795. StationId,
  1796. state == BUSY_FLOW ? "BUSY_FLOW" : "BUSY_BUFFER"
  1797. );
  1798. }
  1799. Adapters[AdapterNumber].LocalBusyInfo[link].State = CLEARING;
  1800. IF_DEBUG(DLC_ASYNC) {
  1801. DPUT("ResetEmulatedLocalBusyStateLink: Interrupting VDM\n");
  1802. }
  1803. IssueHardwareInterrupt();
  1804. //
  1805. // for the benefit of the caller, the state was essentially BUSY
  1806. //
  1807. state = BUSY;
  1808. } else {
  1809. IF_DEBUG(DLC_ASYNC) {
  1810. DPUT3("ResetEmulatedLocalBusyStateLink: NOT resetting state of %d.%04x. state is %s\n",
  1811. AdapterNumber,
  1812. StationId,
  1813. state == CLEARING ? "CLEARING" : "NOT_BUSY"
  1814. );
  1815. }
  1816. }
  1817. return state == BUSY;
  1818. }
  1819. VOID
  1820. DeferReceive(
  1821. IN BYTE AdapterNumber,
  1822. IN WORD StationId,
  1823. IN PLLC_CCB pReadCcb
  1824. )
  1825. /*++
  1826. Routine Description:
  1827. Adds a READ CCB to the end of the deferred receive queue for an adapter/
  1828. station id
  1829. NB: This function MUST BE CALLED WHILE HOLDING THE LocalBusyCritSec for
  1830. this adapter
  1831. Arguments:
  1832. AdapterNumber - which adapter to set emulated local busy state for
  1833. StationId - which link station on AdapterNumber
  1834. pReadCcb - pointer to completed received I-Frame CCB (NT READ CCB)
  1835. Return Value:
  1836. None.
  1837. --*/
  1838. {
  1839. PLLC_CCB* pQueue;
  1840. PLLC_CCB pLlcCcb;
  1841. //
  1842. // if there is nothing in the queue for this adapter number/station ID
  1843. // then place this CCB at the head of the queue, else put the CCB at
  1844. // the end. The CCBs are chained together using their CCB_POINTER fields.
  1845. // Normally, this field is not used for received frame READ CCBs
  1846. //
  1847. ASSERT(pReadCcb->pNext == NULL);
  1848. ASSERT(HIBYTE(StationId) != 0);
  1849. ASSERT(LOBYTE(StationId) != 0);
  1850. #if DBG
  1851. IF_DEBUG(DLC_ASYNC) {
  1852. DPUT4("DeferReceive: deferring I-Frame for %d.%04x. CCB = %08x. Current depth is %d\n",
  1853. AdapterNumber,
  1854. StationId,
  1855. pReadCcb,
  1856. Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].Depth
  1857. );
  1858. }
  1859. #endif
  1860. pQueue = &Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].Queue;
  1861. pLlcCcb = *pQueue;
  1862. if (!pLlcCcb) {
  1863. *pQueue = pReadCcb;
  1864. } else {
  1865. for (; pLlcCcb->pNext; pLlcCcb = pLlcCcb->pNext) {
  1866. ;
  1867. }
  1868. pLlcCcb->pNext = pReadCcb;
  1869. }
  1870. #if DBG
  1871. ++Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].Depth;
  1872. ASSERT(Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].Depth <= MAX_I_FRAME_DEPTH);
  1873. IF_DEBUG(CRITICAL) {
  1874. CRITDUMP(("DeferReceive: %d.%04x CCB=%08x Depth=%d\n",
  1875. AdapterNumber,
  1876. StationId,
  1877. pReadCcb,
  1878. Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].Depth
  1879. ));
  1880. }
  1881. #endif
  1882. }
  1883. VOID
  1884. DeferAllIFrames(
  1885. IN BYTE AdapterNumber,
  1886. IN WORD StationId
  1887. )
  1888. /*++
  1889. Routine Description:
  1890. Removes all pending I-Frames for StationId from the EventQueue for this
  1891. adapter and places them on the deferred queue for this StationId.
  1892. This is done when the StationId goes into local-busy(buffer) state. We do
  1893. this so that any event packets behind the I-Frames can be completed, other
  1894. link stations which aren't blocked can receive their I-Frames and ensures
  1895. that once in local-busy state, all I-Frames are deferred for a link station
  1896. NB: This function MUST BE CALLED WHILE HOLDING THE LocalBusyCritSec for
  1897. this adapter
  1898. NB: we have to gain access to 2 critical sections - the LocalBusyCritSec
  1899. for this station ID & the EventQueueCritSec for this adapter
  1900. Assumption: This function is called in the context of the VDM h/w ISR and
  1901. before AcknowledgeHardwareInterrupt is called for this ISR BOP
  1902. Arguments:
  1903. AdapterNumber - which adapter structure to use
  1904. StationId - which link station to remove I-Frames for
  1905. Return Value:
  1906. None.
  1907. --*/
  1908. {
  1909. PLLC_CCB pCcb;
  1910. PLLC_CCB next;
  1911. PLLC_CCB* last;
  1912. PLLC_READ_PARMS pReadParms;
  1913. PLLC_BUFFER pFrame;
  1914. BOOLEAN remove;
  1915. //
  1916. // deferredFrameCount starts at -1 because it is a count of pending h/w
  1917. // interrupts to cancel. We have to acknowledge the current one which will
  1918. // reduce the count by 1
  1919. //
  1920. LONG deferredFrameCount = -1;
  1921. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  1922. ASSERT(HIBYTE(StationId) != 0);
  1923. ASSERT(LOBYTE(StationId) != 0);
  1924. IF_DEBUG(CRITSEC) {
  1925. DPUT1("DeferAllIFrames: ENTERING Adapters[%d].EventQueueCritSec\n",
  1926. AdapterNumber
  1927. );
  1928. }
  1929. EnterCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  1930. pCcb = Adapters[AdapterNumber].EventQueueHead;
  1931. last = &Adapters[AdapterNumber].EventQueueHead;
  1932. while (pCcb) {
  1933. pReadParms = &pCcb->u.pParameterTable->Read;
  1934. //
  1935. // only remove I-Frames from the EventQueue that are destined for this
  1936. // link station
  1937. //
  1938. remove = FALSE;
  1939. if (pReadParms->uchEvent == LLC_EVENT_RECEIVE_DATA) {
  1940. pFrame = pReadParms->Type.Event.pReceivedFrame;
  1941. if (pFrame->Contiguous.uchMsgType == LLC_I_FRAME
  1942. && pFrame->Contiguous.usStationId == StationId) {
  1943. remove = TRUE;
  1944. }
  1945. }
  1946. if (remove) {
  1947. next = pCcb->pNext;
  1948. *last = next;
  1949. --Adapters[AdapterNumber].QueueElements;
  1950. if (Adapters[AdapterNumber].EventQueueTail == pCcb) {
  1951. if (last == &Adapters[AdapterNumber].EventQueueHead) {
  1952. Adapters[AdapterNumber].EventQueueTail = NULL;
  1953. } else {
  1954. Adapters[AdapterNumber].EventQueueTail = CONTAINING_RECORD(last, LLC_CCB, pNext);
  1955. }
  1956. }
  1957. IF_DEBUG(DLC_ASYNC) {
  1958. DPUT3("DeferAllIFrames: moving CCB %08x for %d.%04x\n",
  1959. pCcb,
  1960. AdapterNumber,
  1961. StationId
  1962. );
  1963. }
  1964. pCcb->pNext = NULL;
  1965. DeferReceive(AdapterNumber, StationId, pCcb);
  1966. ++deferredFrameCount;
  1967. pCcb = next;
  1968. } else {
  1969. IF_DEBUG(DLC_ASYNC) {
  1970. DPUT1("DeferAllIFrames: not removing CCB %08x from EventQueue\n",
  1971. pCcb
  1972. );
  1973. }
  1974. last = (PLLC_CCB*)&pCcb->pNext;
  1975. pCcb = pCcb->pNext;
  1976. }
  1977. }
  1978. if (deferredFrameCount > 0) {
  1979. CancelHardwareInterrupts(deferredFrameCount);
  1980. }
  1981. IF_DEBUG(CRITSEC) {
  1982. DPUT1("DeferAllIFrames: LEAVING Adapters[%d].EventQueueCritSec\n",
  1983. AdapterNumber
  1984. );
  1985. }
  1986. LeaveCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  1987. }
  1988. PLLC_CCB
  1989. RemoveDeferredReceive(
  1990. IN BYTE AdapterNumber,
  1991. IN WORD StationId,
  1992. OUT BOOLEAN* pDeferredFramesLeft
  1993. )
  1994. /*++
  1995. Routine Description:
  1996. Removes a READ CCB from the head of the deferred receive queue for an
  1997. adapter/station id and sets the head to point to the next deferred receive
  1998. in the queue
  1999. NB: This function MUST NOT BE CALLED WHILE HOLDING THE LocalBusyCritSec for
  2000. this adapter (opposite of DeferReceive)
  2001. Arguments:
  2002. AdapterNumber - which adapter to set emulated local busy state for
  2003. StationId - which link station on AdapterNumber
  2004. pDeferredFramesLeft - TRUE if there are more frames on this deferred queue
  2005. Return Value:
  2006. PLLC_CCB
  2007. pointer to CCB from head of queue
  2008. --*/
  2009. {
  2010. PLLC_CCB* pQueue;
  2011. PLLC_CCB pLlcCcb;
  2012. DWORD link;
  2013. //
  2014. // if there is nothing in the queue for this adapter number/station ID
  2015. // then place this CCB at the head of the queue, else put the CCB at
  2016. // the end. The CCBs are chained together using their CCB_POINTER fields.
  2017. // Normally, this field is not used for received frame READ CCBs
  2018. //
  2019. if (StationId == 0)
  2020. {
  2021. #if DBG
  2022. DbgBreakPoint();
  2023. #endif
  2024. return(NULL);
  2025. }
  2026. link = LINK_ID(StationId);
  2027. IF_DEBUG(CRITSEC) {
  2028. DPUT1("RemoveDeferredReceive: ENTERING Adapters[%d].LocalBusyCritSec\n",
  2029. AdapterNumber
  2030. );
  2031. }
  2032. EnterCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  2033. pQueue = &Adapters[AdapterNumber].LocalBusyInfo[link].Queue;
  2034. pLlcCcb = *pQueue;
  2035. *pQueue = pLlcCcb->pNext;
  2036. ASSERT(pLlcCcb != NULL);
  2037. IF_DEBUG(DLC_ASYNC) {
  2038. DPUT4("RemoveDeferredReceive: removing I-Frame for %d.%04x. CCB = %08x. Current depth is %d\n",
  2039. AdapterNumber,
  2040. StationId,
  2041. pLlcCcb,
  2042. Adapters[AdapterNumber].LocalBusyInfo[LINK_ID(StationId)].Depth
  2043. );
  2044. }
  2045. #if DBG
  2046. --Adapters[AdapterNumber].LocalBusyInfo[link].Depth;
  2047. #endif
  2048. //
  2049. // if the deferred queue is now empty, reset the state and issue the real
  2050. // DLC.FLOW.CONTROL(local-busy(buffer), reset) to the DLC driver. Also
  2051. // reduce the reference count of link stations on this adapter in the
  2052. // local-busy(buffer) state and update the first and last link indicies
  2053. //
  2054. if (*pQueue == NULL) {
  2055. IF_DEBUG(DLC_ASYNC) {
  2056. DPUT2("RemoveDeferredReceive: %d.%04x: change state to NOT_BUSY\n",
  2057. AdapterNumber,
  2058. StationId
  2059. );
  2060. }
  2061. Adapters[AdapterNumber].LocalBusyInfo[link].State = NOT_BUSY;
  2062. --Adapters[AdapterNumber].DeferredReceives;
  2063. if (Adapters[AdapterNumber].DeferredReceives) {
  2064. if (link == Adapters[AdapterNumber].FirstIndex) {
  2065. for (link = Adapters[AdapterNumber].FirstIndex + 1;
  2066. link <= Adapters[AdapterNumber].LastIndex;
  2067. ++link) {
  2068. if (Adapters[AdapterNumber].LocalBusyInfo[link].State != NOT_BUSY) {
  2069. Adapters[AdapterNumber].FirstIndex = link;
  2070. break;
  2071. }
  2072. }
  2073. } else if (link == Adapters[AdapterNumber].LastIndex) {
  2074. for (link = Adapters[AdapterNumber].LastIndex - 1;
  2075. link >= Adapters[AdapterNumber].FirstIndex;
  2076. --link
  2077. ) {
  2078. if (Adapters[AdapterNumber].LocalBusyInfo[link].State != NOT_BUSY) {
  2079. Adapters[AdapterNumber].LastIndex = link;
  2080. break;
  2081. }
  2082. }
  2083. }
  2084. } else {
  2085. Adapters[AdapterNumber].FirstIndex = NO_LINKS_BUSY;
  2086. Adapters[AdapterNumber].LastIndex = NO_LINKS_BUSY;
  2087. }
  2088. #if DBG
  2089. //ASSERT(DosDlcFlowControl(AdapterNumber, StationId, LLC_RESET_LOCAL_BUSY_BUFFER) == LLC_STATUS_SUCCESS);
  2090. ASSERT(DlcFlowControl(AdapterNumber, StationId, LLC_RESET_LOCAL_BUSY_USER) == LLC_STATUS_SUCCESS);
  2091. #else
  2092. //DosDlcFlowControl(AdapterNumber, StationId, LLC_RESET_LOCAL_BUSY_BUFFER);
  2093. DlcFlowControl(AdapterNumber, StationId, LLC_RESET_LOCAL_BUSY_USER);
  2094. #endif
  2095. *pDeferredFramesLeft = FALSE;
  2096. //
  2097. // restore async event thread's priority
  2098. //
  2099. SetThreadPriority(hEventThread, THREAD_PRIORITY_ABOVE_NORMAL);
  2100. } else {
  2101. *pDeferredFramesLeft = TRUE;
  2102. }
  2103. IF_DEBUG(DLC_ASYNC) {
  2104. DPUT5("RemoveDeferredReceive(%d, %04x): Ref#=%d, First=%d, Last=%d\n",
  2105. AdapterNumber,
  2106. StationId,
  2107. Adapters[AdapterNumber].DeferredReceives,
  2108. Adapters[AdapterNumber].FirstIndex,
  2109. Adapters[AdapterNumber].LastIndex
  2110. );
  2111. }
  2112. IF_DEBUG(CRITSEC) {
  2113. DPUT1("RemoveDeferredReceive: LEAVING Adapters[%d].LocalBusyCritSec\n",
  2114. AdapterNumber
  2115. );
  2116. }
  2117. LeaveCriticalSection(&Adapters[AdapterNumber].LocalBusyCritSec);
  2118. return pLlcCcb;
  2119. }
  2120. DWORD
  2121. VrDlcEventHandlerThread(
  2122. IN PVOID pParameter
  2123. )
  2124. /*++
  2125. Routine Description:
  2126. This is the VDM DLC event handler thread. The thread reads all DLC events
  2127. from both DOS DLC adapters, queues them to the event queue and requests a
  2128. DOS hardware interrupt (the post routine mechanism uses hardware interrupts
  2129. to make an external event in the VDM)
  2130. To make this stuff as fast as possible, we don't allocate or free any memory
  2131. in this loop, but we reuse the old read CCBs and their parameter tables in
  2132. the event queue
  2133. We filter out any completion which will NOT result in an asynchronous event
  2134. in the VDM. This means CCB completions for this emulator (DIR.CLOSE.ADAPTER
  2135. and DIR.CLOSE.DIRECT for example) and received I-Frames for link stations
  2136. which are currently in the BUSY (local-busy(buffer)) state. This avoids us
  2137. making unnecessary interrupts in the VDM and costly (on x86 machines) BOPs
  2138. which achieve no action for the VDM
  2139. Arguments:
  2140. pParameter - not used
  2141. Return Value:
  2142. None, this should loop forever, until the VDM process dies
  2143. --*/
  2144. {
  2145. DWORD status = LLC_STATUS_PENDING;
  2146. DWORD waitIndex;
  2147. PLLC_CCB pReadCcb;
  2148. PLLC_READ_PARMS pReadParms;
  2149. WORD stationId;
  2150. UNREFERENCED_PARAMETER(pParameter);
  2151. IF_DEBUG(DLC_ASYNC) {
  2152. DPUT2("VrDlcEventHandlerThread kicked off: Thread Handle=%x, Id=%d\n",
  2153. GetCurrentThread(),
  2154. GetCurrentThreadId()
  2155. );
  2156. }
  2157. //
  2158. // wait for the READ CCB event for either adapter to become signalled (by
  2159. // DLC driver when READ completes)
  2160. //
  2161. while (TRUE) {
  2162. waitIndex = WaitForMultipleObjects(
  2163. ARRAY_ELEMENTS(aReadEvents), // count of objects
  2164. aReadEvents, // handle array
  2165. FALSE, // do not wait all objects
  2166. INFINITE // wait forever
  2167. );
  2168. //
  2169. // if we get 0xFFFFFFFF back then an error occurred
  2170. //
  2171. if (waitIndex == 0xffffffff) {
  2172. status = GetLastError();
  2173. IF_DEBUG(DLC_ASYNC) {
  2174. DPUT1("VrDlcEventHandlerThread: FATAL: WaitForMultipleObjects returns %d\n", status);
  2175. }
  2176. //
  2177. // this terminates the thread!
  2178. //
  2179. break;
  2180. }
  2181. //
  2182. // if we get a number > number of events-1, then either a timeout
  2183. // occurred or a mutex was abandoned, both of which are highly
  2184. // improbable. Just continue with the loop
  2185. //
  2186. if (waitIndex > LAST_ELEMENT(aReadEvents)) {
  2187. IF_DEBUG(DLC_ASYNC) {
  2188. DPUT1("VrDlcEventHandlerThread: ERROR: WaitForMultipleObjects returns %d?: continuing\n", waitIndex);
  2189. }
  2190. continue;
  2191. }
  2192. //
  2193. // one of the READ CCBs has successfully completed (oh joy!)
  2194. //
  2195. pReadCcb = aReadCcbs[waitIndex];
  2196. //
  2197. // reset the event
  2198. //
  2199. ResetEvent(aReadEvents[waitIndex]);
  2200. IF_DEBUG(DLC_ASYNC) {
  2201. DPUT1("VrDlcEventHandlerThread: Event occurred for adapter %d\n", waitIndex);
  2202. IF_DEBUG(READ_COMPLETE) {
  2203. DUMPCCB(pReadCcb, TRUE, FALSE, FALSE, 0, 0);
  2204. }
  2205. }
  2206. if (pReadCcb->uchDlcStatus == STATUS_SUCCESS) {
  2207. //
  2208. // it gets better!
  2209. //
  2210. pReadParms = &pReadCcb->u.pParameterTable->Read;
  2211. //
  2212. // if the completion flag is VRDLC_COMMAND_COMPLETION then this
  2213. // command originated in this emulator: Do Not hand it back to
  2214. // the VDM. In fact do nothing with it: this is an asynchronous
  2215. // command that we didn't want to wait for (like DIR.CLOSE.ADAPTER
  2216. // or DIR.CLOSE.DIRECT)
  2217. //
  2218. if (pReadParms->ulNotificationFlag == VRDLC_COMMAND_COMPLETION) {
  2219. IF_DEBUG(CRITICAL) {
  2220. CRITDUMP(("*** VrDlcEventHandlerThread: VRDLC_COMMAND_COMPLETION: CCB=%08x COMMAND=%02x ***\n", pReadCcb, pReadCcb->uchDlcCommand));
  2221. }
  2222. } else if (pReadParms->uchEvent == LLC_EVENT_STATUS_CHANGE
  2223. && pReadParms->Type.Status.usDlcStatusCode == LLC_INDICATE_LOCAL_STATION_BUSY
  2224. && !IS_LOCAL_BUSY(waitIndex, pReadParms->Type.Status.usStationId)) {
  2225. //
  2226. // We must separate the buffer busy states of global NT
  2227. // buffer pool and the local buffer pools.
  2228. // This must be a real buffer busy indication, if
  2229. // the SAP has no overflowed receive buffers.
  2230. // How can such a situation arise - if we (ie DOS emulation) aren't
  2231. // holding onto the buffers, then where are they? Sounds like a bug
  2232. // to me (RLF 07/22/92)
  2233. //
  2234. IF_DEBUG(DLC_ASYNC) {
  2235. DPUT("VrDlcEventHandlerThread: *** REAL LOCAL BUSY??? ***\n");
  2236. }
  2237. //
  2238. // We are not queueing buffers because of having
  2239. // no SAP buffers available => this must be a real
  2240. // buffer busy indication. The READ command should
  2241. // automatically extend the buffer pool size (up
  2242. // to the maximum value set in the initialization)
  2243. //
  2244. DlcFlowControl((BYTE)waitIndex, pReadParms->Type.Status.usStationId, LLC_RESET_LOCAL_BUSY_BUFFER);
  2245. } else if (pReadParms->uchEvent == LLC_EVENT_RECEIVE_DATA
  2246. && pReadParms->Type.Event.pReceivedFrame->Contiguous.uchMsgType
  2247. == LLC_I_FRAME) {
  2248. stationId = pReadParms->Type.Event.pReceivedFrame->Contiguous.usStationId;
  2249. ASSERT(HIBYTE(stationId) != 0);
  2250. ASSERT(LOBYTE(stationId) != 0);
  2251. IF_DEBUG(CRITSEC) {
  2252. DPUT1("VrDlcEventHandlerThread: ENTERING Adapters[%d].LocalBusyCritSec\n",
  2253. waitIndex
  2254. );
  2255. }
  2256. EnterCriticalSection(&Adapters[waitIndex].LocalBusyCritSec);
  2257. //
  2258. // if the link station is in emulated local-busy(buffer) state
  2259. // BUSY or CLEARING then queue the I-Frame. This action does NOT
  2260. // generate a h/w interrupt to the VDM. VrDlcHwInterrupt generates
  2261. // additional h/w interrupts when it processes deferred I-Frames
  2262. //
  2263. ASSERT(
  2264. Adapters[waitIndex].LocalBusyInfo[LINK_ID(stationId)].State == NOT_BUSY
  2265. || Adapters[waitIndex].LocalBusyInfo[LINK_ID(stationId)].State == CLEARING
  2266. || Adapters[waitIndex].LocalBusyInfo[LINK_ID(stationId)].State == BUSY
  2267. || Adapters[waitIndex].LocalBusyInfo[LINK_ID(stationId)].State == BUSY_BUFFER
  2268. || Adapters[waitIndex].LocalBusyInfo[LINK_ID(stationId)].State == BUSY_FLOW
  2269. );
  2270. if (Adapters[waitIndex].LocalBusyInfo[LINK_ID(stationId)].State != NOT_BUSY) {
  2271. DeferReceive((BYTE)waitIndex, stationId, pReadCcb);
  2272. //
  2273. // set the READ CCB pointer to NULL: we have to allocate a
  2274. // new READ CCB
  2275. //
  2276. pReadCcb = NULL;
  2277. }
  2278. IF_DEBUG(CRITSEC) {
  2279. DPUT1("VrDlcEventHandlerThread: LEAVING Adapters[%d].LocalBusyCritSec\n",
  2280. waitIndex
  2281. );
  2282. }
  2283. LeaveCriticalSection(&Adapters[waitIndex].LocalBusyCritSec);
  2284. }
  2285. //
  2286. // pReadCcb is NULL if the READ CCB is to be added to the event
  2287. // queue, NULL if we deferred an I-Frame for a link station in
  2288. // local-busy(buffer) state
  2289. //
  2290. if (pReadCcb) {
  2291. //
  2292. // queue the completed READ CCB in the event queue. If it is
  2293. // full (!) then wait. We will already have issued a call to
  2294. // call_ica_hw_interrupt for each of the events in the queue
  2295. // so we wait on the VDM removing events from the queue. This
  2296. // is an irregular situation, that I (RLF) don't expect to
  2297. // arise. If it does, then it probably means there is some
  2298. // horrendous inefficiency somewhere
  2299. //
  2300. PutEvent((BYTE)waitIndex, pReadCcb);
  2301. IF_DEBUG(DLC_ASYNC) {
  2302. DPUT("VrDlcEventHandlerThread: Interrupting VDM\n");
  2303. }
  2304. //
  2305. // poke the VDM so that it knows there is some asynchronous
  2306. // processing to do
  2307. //
  2308. IssueHardwareInterrupt();
  2309. //
  2310. // set pReadCcb to NULL. We have to allocate and submit a new
  2311. // READ CCB
  2312. //
  2313. pReadCcb = NULL;
  2314. }
  2315. } else {
  2316. //
  2317. // The READ function failed, the adapter must be closed. We now
  2318. // wait until the adapter is opened again and the event is set
  2319. // back to the signaled state
  2320. //
  2321. IF_DEBUG(DLC_ASYNC) {
  2322. DPUT1("VrDlcEventHandlerThread: READ failed. Status=%x\n", pReadCcb->uchDlcStatus);
  2323. }
  2324. LocalFree(pReadCcb);
  2325. IF_DEBUG(DLC_ALLOC) {
  2326. DPUT1("FREE: freed block @ %x\n", pReadCcb);
  2327. }
  2328. //
  2329. // wait for a new READ CCB to be created the next time this adapter
  2330. // is opened
  2331. //
  2332. //continue;
  2333. pReadCcb = NULL;
  2334. }
  2335. //
  2336. // if we had a successful completion then get a new CCB. If the CCB was
  2337. // not queued, re-use it
  2338. //
  2339. if (!pReadCcb) {
  2340. pReadCcb = InitiateRead(waitIndex, (LLC_STATUS*)&status);
  2341. if (pReadCcb) {
  2342. status = pReadCcb->uchDlcStatus;
  2343. } else {
  2344. IF_DEBUG(DLC_ASYNC) {
  2345. DPUT("VrDlcEventHandlerThread: Error: InitiateRead returns NULL\n");
  2346. }
  2347. break;
  2348. }
  2349. } else {
  2350. status = lpAcsLan(pReadCcb, NULL);
  2351. if (status != LLC_STATUS_SUCCESS) {
  2352. IF_DEBUG(DLC_ASYNC) {
  2353. DPUT1("VrDlcEventHandlerThread: Error: AcsLan returns %d\n", status);
  2354. }
  2355. break;
  2356. }
  2357. }
  2358. }
  2359. //
  2360. // !!! WE SHOULD NEVER BE HERE !!!
  2361. //
  2362. IF_DEBUG(DLC_ASYNC) {
  2363. DPUT1("VrDlcEventHandlerThread: Fatal: terminating. Status = %x\n", status);
  2364. }
  2365. return 0;
  2366. }
  2367. BOOLEAN
  2368. InitializeEventHandler(
  2369. VOID
  2370. )
  2371. /*++
  2372. Routine Description:
  2373. Initializes static data structures used in event handling
  2374. Arguments:
  2375. None
  2376. Return Value:
  2377. BOOLEAN
  2378. Success - TRUE
  2379. Failure - FALSE
  2380. --*/
  2381. {
  2382. DWORD i;
  2383. DWORD Tid;
  2384. IF_DEBUG(DLC_ASYNC) {
  2385. DPUT("Vr: InitializeEventHandler\n");
  2386. }
  2387. //
  2388. // make sure the read CCBs and event queues are in a known state
  2389. //
  2390. RtlZeroMemory(aReadCcbs, sizeof(aReadCcbs));
  2391. //
  2392. // preset the handle array with invalid handles so we know which ones
  2393. // have been allocated in clean up
  2394. //
  2395. for (i = 0; i < ARRAY_ELEMENTS(aReadEvents); ++i) {
  2396. aReadEvents[i] = INVALID_HANDLE_VALUE;
  2397. }
  2398. //
  2399. // create event handles for all (both) supported adapters. DIR.OPEN.ADAPTER
  2400. // sets the event to signalled which enables the event handler thread to
  2401. // receive events for that adapter. If we get an error creating the handles
  2402. // then clean up before exiting so that we may try this again later
  2403. //
  2404. for (i = 0; i < ARRAY_ELEMENTS(aReadEvents); i++) {
  2405. aReadEvents[i] = CreateEvent(NULL, // security attributes: no inherit
  2406. TRUE, // manual-reset event
  2407. FALSE, // initial state = not signaled
  2408. NULL // unnamed event
  2409. );
  2410. if (aReadEvents[i] == NULL) {
  2411. IF_DEBUG(DLC_ASYNC) {
  2412. DPUT1("Vr: InitializeEventHandler: Error: failed to create read event: %d\n", GetLastError());
  2413. }
  2414. goto cleanUp;
  2415. }
  2416. }
  2417. //
  2418. // create and start the thread which handles RECEIVE events
  2419. //
  2420. hEventThread = CreateThread(NULL, // security attributes
  2421. EVENT_THREAD_STACK, // initial thread stack size
  2422. VrDlcEventHandlerThread,
  2423. NULL, // thread args
  2424. 0, // creation flags
  2425. &Tid
  2426. );
  2427. if (hEventThread) {
  2428. IF_DEBUG(CRITICAL) {
  2429. CRITDUMP(("InitializeEventHandler: Created thread Handle=%x, Tid=%d\n", hEventThread, Tid));
  2430. }
  2431. SetThreadPriority(hEventThread, THREAD_PRIORITY_ABOVE_NORMAL);
  2432. return TRUE;
  2433. } else {
  2434. IF_DEBUG(DLC_ASYNC) {
  2435. DPUT1("Vr: InitializeEventHandler: Error: failed to create thread: %d\n", GetLastError());
  2436. }
  2437. }
  2438. //
  2439. // we come here if for some reason we couldn't create the event handles or
  2440. // the event handler thread
  2441. //
  2442. cleanUp:
  2443. for (i = 0; i < ARRAY_ELEMENTS(aReadEvents); ++i) {
  2444. if (aReadEvents[i] != INVALID_HANDLE_VALUE) {
  2445. CloseHandle(aReadEvents[i]);
  2446. }
  2447. }
  2448. IF_DEBUG(DLC_ASYNC) {
  2449. DPUT("InitializeEventHandler: Error: returning FALSE\n");
  2450. }
  2451. return FALSE;
  2452. }
  2453. PLLC_CCB
  2454. InitiateRead(
  2455. IN DWORD AdapterNumber,
  2456. OUT LLC_STATUS* ErrorStatus
  2457. )
  2458. /*++
  2459. Routine Description:
  2460. Create a READ CCB, initialize it to get all events for all stations, set
  2461. the completion event to the event created for this adapter and submit the
  2462. CCB (via AcsLan). If the submit succeeds, set this CCB as the READ CCB
  2463. for AdapterNumber
  2464. NB: The READ CCB - which will be queued on EventQueue - and its parameter
  2465. table are allocated together, so we only need one call to LocalFree to
  2466. deallocate both
  2467. This routine IS THE ONLY PLACE WHERE aReadCcbs IS WRITTEN once the array
  2468. has been initialized in InitializeEventHandler
  2469. Arguments:
  2470. AdapterNumber - which adapter to initiate this READ for
  2471. ErrorStatus - returned LLC_STATUS describing failure if this function
  2472. returns NULL
  2473. Return Value:
  2474. PLLC_CCB
  2475. pointer to allocated/submitted CCB or NULL.
  2476. If this function succeeds then aReadCcbs[AdapterNumber] points to the
  2477. READ CCB
  2478. If this function fails then *ErrorStatus will contain an LLC_STATUS
  2479. describing why we failed to allocate/submit the CCB. The CCB will be
  2480. deallocated in this case
  2481. --*/
  2482. {
  2483. PLLC_CCB pCcb;
  2484. PLLC_READ_PARMS parms;
  2485. LLC_STATUS status;
  2486. IF_DEBUG(DLC_ASYNC) {
  2487. DPUT1("InitiateRead: AdapterNumber=%d\n", AdapterNumber);
  2488. }
  2489. //
  2490. // Allocate, initialize and issue the next DLC command. Allocate contiguous
  2491. // space for CCB and parameter table
  2492. //
  2493. pCcb = (PLLC_CCB)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
  2494. sizeof(LLC_CCB) + sizeof(LLC_READ_PARMS)
  2495. );
  2496. //
  2497. // put the READ CCB in the array before we have a chance to complete it
  2498. //
  2499. aReadCcbs[AdapterNumber] = pCcb;
  2500. if (pCcb) {
  2501. //
  2502. // initialize required CCB fields
  2503. //
  2504. pCcb->uchAdapterNumber = (UCHAR)AdapterNumber;
  2505. pCcb->uchDlcCommand = LLC_READ;
  2506. parms = (PLLC_READ_PARMS)&pCcb[1];
  2507. pCcb->u.pParameterTable = (PLLC_PARMS)parms;
  2508. pCcb->hCompletionEvent = aReadEvents[AdapterNumber];
  2509. //
  2510. // set the read options to receive ALL events for ALL stations
  2511. //
  2512. parms->uchOptionIndicator = LLC_OPTION_READ_ALL;
  2513. parms->uchEventSet = LLC_READ_ALL_EVENTS;
  2514. //
  2515. // submit the CCB. If it's not ok, free the CCB and NULL the pointer
  2516. // in the list of per-adapter READ CCBs
  2517. //
  2518. status = lpAcsLan(pCcb, NULL);
  2519. if (status != LLC_STATUS_SUCCESS) {
  2520. aReadCcbs[AdapterNumber] = NULL;
  2521. IF_DEBUG(DLC_ASYNC) {
  2522. DPUT1("InitiateRead: AcsLan failed: %x\n", status);
  2523. }
  2524. LocalFree((HLOCAL)pCcb);
  2525. IF_DEBUG(DLC_ALLOC) {
  2526. DPUT1("FREE: freed block @ %x\n", pCcb);
  2527. }
  2528. *ErrorStatus = status;
  2529. pCcb = NULL;
  2530. }
  2531. } else {
  2532. *ErrorStatus = LLC_STATUS_NO_MEMORY;
  2533. }
  2534. #if DBG
  2535. IF_DEBUG(DLC_ASYNC) {
  2536. DPUT2("InitiateRead: returning pCcb=%x%c", pCcb, pCcb ? '\n' : ' ');
  2537. if (!pCcb) {
  2538. DPUT1("*ErrorStatus=%x\n", *ErrorStatus);
  2539. }
  2540. }
  2541. #endif
  2542. return pCcb;
  2543. }
  2544. VOID
  2545. PutEvent(
  2546. IN BYTE AdapterNumber,
  2547. IN PLLC_CCB pCcb
  2548. )
  2549. /*++
  2550. Routine Description:
  2551. Adds a completed event (READ CCB) to the Event Queue. If the event queue
  2552. is full then returns FALSE. Updates the queue tail index. The queue is
  2553. accessed inside a critical section
  2554. Arguments:
  2555. AdapterNumber - which adapter to queue event for
  2556. pCcb - pointer to completed READ CCB to add to the queue
  2557. Return Value:
  2558. None.
  2559. --*/
  2560. {
  2561. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  2562. ASSERT(pCcb->pNext == NULL);
  2563. IF_DEBUG(CRITSEC) {
  2564. DPUT1("PutEvent: ENTERING Adapters[%d].EventQueueCritSec\n",
  2565. AdapterNumber
  2566. );
  2567. }
  2568. EnterCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2569. if (Adapters[AdapterNumber].EventQueueTail == NULL) {
  2570. Adapters[AdapterNumber].EventQueueHead = pCcb;
  2571. } else {
  2572. Adapters[AdapterNumber].EventQueueTail->pNext = pCcb;
  2573. }
  2574. Adapters[AdapterNumber].EventQueueTail = pCcb;
  2575. ++Adapters[AdapterNumber].QueueElements;
  2576. IF_DEBUG(EVENT_QUEUE) {
  2577. DPUT5("PutEvent: Added %x to adapter %d EventQueue. Head=%x Tail=%x Elements=%d\n",
  2578. pCcb,
  2579. AdapterNumber,
  2580. Adapters[AdapterNumber].EventQueueHead,
  2581. Adapters[AdapterNumber].EventQueueTail,
  2582. Adapters[AdapterNumber].QueueElements
  2583. );
  2584. }
  2585. IF_DEBUG(CRITSEC) {
  2586. DPUT1("PutEvent: LEAVING Adapters[%d].EventQueueCritSec\n",
  2587. AdapterNumber
  2588. );
  2589. }
  2590. LeaveCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2591. }
  2592. PLLC_CCB
  2593. PeekEvent(
  2594. IN BYTE AdapterNumber
  2595. )
  2596. /*++
  2597. Routine Description:
  2598. Reads the next completed CCB from the head of the Event Queue. If the
  2599. queue is empty (QueueElements == 0) then returns NULL. The queue is
  2600. accessed inside a critical section
  2601. Arguments:
  2602. None.
  2603. Return Value:
  2604. PLLC_CCB
  2605. Success - pointer to CCB at queue head
  2606. Failure - NULL
  2607. --*/
  2608. {
  2609. PLLC_CCB pCcb;
  2610. ASSERT(AdapterNumber < DOS_DLC_MAX_ADAPTERS);
  2611. IF_DEBUG(CRITSEC) {
  2612. DPUT1("PeekEvent: ENTERING Adapters[%d].EventQueueCritSec\n",
  2613. AdapterNumber
  2614. );
  2615. }
  2616. EnterCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2617. if (Adapters[AdapterNumber].QueueElements) {
  2618. pCcb = Adapters[AdapterNumber].EventQueueHead;
  2619. IF_DEBUG(EVENT_QUEUE) {
  2620. DPUT5("PeekEvent: CCB %x from adapter %d queue head. Head=%x Tail=%x Elements=%d\n",
  2621. pCcb,
  2622. AdapterNumber,
  2623. Adapters[AdapterNumber].EventQueueHead,
  2624. Adapters[AdapterNumber].EventQueueTail,
  2625. Adapters[AdapterNumber].QueueElements
  2626. );
  2627. }
  2628. } else {
  2629. pCcb = NULL;
  2630. IF_DEBUG(EVENT_QUEUE) {
  2631. DPUT1("PeekEvent: adapter %d queue is EMPTY!\n", AdapterNumber);
  2632. }
  2633. }
  2634. IF_DEBUG(CRITSEC) {
  2635. DPUT1("PeekEvent: LEAVING Adapters[%d].EventQueueCritSec\n",
  2636. AdapterNumber
  2637. );
  2638. }
  2639. LeaveCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2640. IF_DEBUG(CRITICAL) {
  2641. CRITDUMP(("PeekEvent: returning %x\n", pCcb));
  2642. }
  2643. return pCcb;
  2644. }
  2645. PLLC_CCB
  2646. GetEvent(
  2647. IN BYTE AdapterNumber
  2648. )
  2649. /*++
  2650. Routine Description:
  2651. Gets the next completed CCB from the head of the Event Queue. If the
  2652. queue is empty (QueueElements == 0) then returns NULL. If there is a
  2653. completed event in the queue, removes it and advances the queue head
  2654. to the next element. The queue is accessed inside a critical section
  2655. Arguments:
  2656. AdapterNumber - which adapter's event queue to remove event from
  2657. Return Value:
  2658. PLLC_CCB
  2659. Success - pointer to dequeued CCB
  2660. Failure - NULL
  2661. --*/
  2662. {
  2663. PLLC_CCB pCcb;
  2664. IF_DEBUG(CRITSEC) {
  2665. DPUT1("GetEvent: ENTERING Adapters[%d].EventQueueCritSec\n",
  2666. AdapterNumber
  2667. );
  2668. }
  2669. EnterCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2670. if (Adapters[AdapterNumber].QueueElements) {
  2671. pCcb = Adapters[AdapterNumber].EventQueueHead;
  2672. Adapters[AdapterNumber].EventQueueHead = pCcb->pNext;
  2673. --Adapters[AdapterNumber].QueueElements;
  2674. if (Adapters[AdapterNumber].QueueElements == 0) {
  2675. Adapters[AdapterNumber].EventQueueTail = NULL;
  2676. }
  2677. IF_DEBUG(EVENT_QUEUE) {
  2678. DPUT5("GetEvent: Removed %x from adapter %d EventQueue. Head=%x Tail=%x Elements=%d\n",
  2679. pCcb,
  2680. AdapterNumber,
  2681. Adapters[AdapterNumber].EventQueueHead,
  2682. Adapters[AdapterNumber].EventQueueTail,
  2683. Adapters[AdapterNumber].QueueElements
  2684. );
  2685. }
  2686. } else {
  2687. pCcb = NULL;
  2688. IF_DEBUG(EVENT_QUEUE) {
  2689. DPUT1("GetEvent: queue for adapter %d is EMPTY!\n", AdapterNumber);
  2690. }
  2691. }
  2692. IF_DEBUG(CRITSEC) {
  2693. DPUT1("GetEvent: LEAVING Adapters[%d].EventQueueCritSec\n",
  2694. AdapterNumber
  2695. );
  2696. }
  2697. LeaveCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2698. IF_DEBUG(CRITICAL) {
  2699. CRITDUMP(("GetEvent: returning %x\n", pCcb));
  2700. }
  2701. return pCcb;
  2702. }
  2703. VOID
  2704. FlushEventQueue(
  2705. IN BYTE AdapterNumber
  2706. )
  2707. /*++
  2708. Routine Description:
  2709. Removes all READ CCBs from the event queue.
  2710. Arguments:
  2711. None.
  2712. Return Value:
  2713. None.
  2714. --*/
  2715. {
  2716. PLLC_CCB pCcb;
  2717. IF_DEBUG(CRITSEC) {
  2718. DPUT1("FlushEventQueue: ENTERING Adapters[%d].EventQueueCritSec\n",
  2719. AdapterNumber
  2720. );
  2721. }
  2722. EnterCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2723. #if DBG
  2724. if (!Adapters[AdapterNumber].QueueElements) {
  2725. DPUT("FlushEventQueue: queue is EMPTY!\n");
  2726. }
  2727. #endif
  2728. while (Adapters[AdapterNumber].QueueElements) {
  2729. pCcb = Adapters[AdapterNumber].EventQueueHead;
  2730. --Adapters[AdapterNumber].QueueElements;
  2731. Adapters[AdapterNumber].EventQueueHead = pCcb->pNext;
  2732. IF_DEBUG(EVENT_QUEUE) {
  2733. DPUT5("FlushEventQueue: Removed %x from adapter %d EventQueue. Head=%x Tail=%x Elements=%d\n",
  2734. pCcb,
  2735. AdapterNumber,
  2736. Adapters[AdapterNumber].EventQueueHead,
  2737. Adapters[AdapterNumber].EventQueueTail,
  2738. Adapters[AdapterNumber].QueueElements
  2739. );
  2740. }
  2741. //
  2742. // BUGBUG - received frames?
  2743. //
  2744. LocalFree((HLOCAL)pCcb);
  2745. IF_DEBUG(DLC_ALLOC) {
  2746. DPUT1("FREE: freed block @ %x\n", pCcb);
  2747. }
  2748. Adapters[AdapterNumber].EventQueueTail = NULL;
  2749. }
  2750. IF_DEBUG(CRITSEC) {
  2751. DPUT1("FlushEventQueue: ENTERING Adapters[%d].EventQueueCritSec\n",
  2752. AdapterNumber
  2753. );
  2754. }
  2755. LeaveCriticalSection(&Adapters[AdapterNumber].EventQueueCritSec);
  2756. }
  2757. VOID
  2758. RemoveDeadReceives(
  2759. IN PLLC_CCB pCcb
  2760. )
  2761. /*++
  2762. Routine Description:
  2763. The receive command described by pCcb has completed (terminated). We must
  2764. remove any queued reads completed by data receive which refer to this CCB
  2765. Arguments:
  2766. pCcb - pointer to RECEIVE CCB (NT)
  2767. Return Value:
  2768. None.
  2769. --*/
  2770. {
  2771. PLLC_CCB thisCcb;
  2772. PLLC_CCB nextCcb;
  2773. PLLC_CCB lastCcb = NULL;
  2774. PLLC_CCB prevCcb = NULL;
  2775. DWORD i;
  2776. PDOS_ADAPTER pAdapter = &Adapters[pCcb->uchAdapterNumber];
  2777. PLLC_CCB* pQueue;
  2778. //
  2779. // remove any queued receives from the event queue. Note: There should NOT
  2780. // be any. The reason: there can't be any data received after the associated
  2781. // RECEIVE command has been cancelled or terminated. This is the theory,
  2782. // anyway
  2783. //
  2784. EnterCriticalSection(&pAdapter->EventQueueCritSec);
  2785. thisCcb = pAdapter->EventQueueHead;
  2786. for (i = pAdapter->QueueElements; i; --i) {
  2787. nextCcb = thisCcb->pNext;
  2788. if (thisCcb->u.pParameterTable->Read.ulNotificationFlag == (ULONG)pCcb) {
  2789. IF_DEBUG(EVENT_QUEUE) {
  2790. DPUT5("RemoveDeadReceives: Removed %x from adapter %d EventQueue. Head=%x Tail=%x Elements=%d\n",
  2791. thisCcb,
  2792. pCcb->uchAdapterNumber,
  2793. pAdapter->EventQueueHead,
  2794. pAdapter->EventQueueTail,
  2795. pAdapter->QueueElements
  2796. );
  2797. }
  2798. ReleaseReceiveResources(thisCcb);
  2799. if (pAdapter->EventQueueHead == thisCcb) {
  2800. pAdapter->EventQueueHead = nextCcb;
  2801. }
  2802. --pAdapter->QueueElements;
  2803. lastCcb = thisCcb;
  2804. } else {
  2805. prevCcb = thisCcb;
  2806. }
  2807. thisCcb = nextCcb;
  2808. }
  2809. if (pAdapter->EventQueueTail == lastCcb) {
  2810. pAdapter->EventQueueTail = prevCcb;
  2811. }
  2812. LeaveCriticalSection(&pAdapter->EventQueueCritSec);
  2813. //
  2814. // remove any queued deferred receives from the deferred I-Frame queue for
  2815. // this SAP or link station
  2816. //
  2817. EnterCriticalSection(&pAdapter->LocalBusyCritSec);
  2818. if (pAdapter->DeferredReceives) {
  2819. ASSERT(pAdapter->FirstIndex != NO_LINKS_BUSY);
  2820. ASSERT(pAdapter->LastIndex != NO_LINKS_BUSY);
  2821. for (i = pAdapter->FirstIndex; i <= pAdapter->LastIndex; ++i) {
  2822. pQueue = &pAdapter->LocalBusyInfo[i].Queue;
  2823. for (thisCcb = *pQueue; thisCcb; thisCcb = thisCcb->pNext) {
  2824. if (thisCcb->u.pParameterTable->Read.ulNotificationFlag == (ULONG)pCcb) {
  2825. IF_DEBUG(EVENT_QUEUE) {
  2826. DPUT3("RemoveDeadReceives: Removed %x from adapter %d BusyList. Queue=%x\n",
  2827. thisCcb,
  2828. pCcb->uchAdapterNumber,
  2829. pAdapter->LocalBusyInfo[i].Queue
  2830. );
  2831. }
  2832. *pQueue = thisCcb->pNext;
  2833. ReleaseReceiveResources(thisCcb);
  2834. #if DBG
  2835. --pAdapter->LocalBusyInfo[i].Depth;
  2836. #endif
  2837. thisCcb = *pQueue;
  2838. } else {
  2839. pQueue = &thisCcb->pNext;
  2840. }
  2841. }
  2842. if (pAdapter->LocalBusyInfo[i].Queue == NULL) {
  2843. pAdapter->LocalBusyInfo[i].State = NOT_BUSY;
  2844. --pAdapter->DeferredReceives;
  2845. }
  2846. }
  2847. //
  2848. // reset the indicies
  2849. //
  2850. if (pAdapter->DeferredReceives) {
  2851. for (i = pAdapter->FirstIndex; i <= pAdapter->LastIndex; ++i) {
  2852. if (pAdapter->LocalBusyInfo[i].State != NOT_BUSY) {
  2853. pAdapter->FirstIndex = i;
  2854. break;
  2855. }
  2856. }
  2857. for (i = pAdapter->LastIndex; i > pAdapter->FirstIndex; --i) {
  2858. if (pAdapter->LocalBusyInfo[i].State != NOT_BUSY) {
  2859. pAdapter->LastIndex = i;
  2860. break;
  2861. }
  2862. }
  2863. } else {
  2864. pAdapter->FirstIndex = NO_LINKS_BUSY;
  2865. pAdapter->LastIndex = NO_LINKS_BUSY;
  2866. }
  2867. }
  2868. LeaveCriticalSection(&pAdapter->LocalBusyCritSec);
  2869. }
  2870. VOID
  2871. ReleaseReceiveResources(
  2872. IN PLLC_CCB pCcb
  2873. )
  2874. /*++
  2875. Routine Description:
  2876. Releases all resources used by a completed data receive READ CCB
  2877. Arguments:
  2878. pCcb - pointer to completed READ CCB. We have to return all received
  2879. frames to buffer pool, and the READ CCB and parameter table to
  2880. the proceess heap
  2881. Return Value:
  2882. None.
  2883. --*/
  2884. {
  2885. WORD buffersLeft;
  2886. //
  2887. // this is a data receive - return the data buffers to the pool
  2888. //
  2889. ASSERT(pCcb->u.pParameterTable->Read.uchEvent == LLC_EVENT_RECEIVE_DATA);
  2890. BufferFree(pCcb->uchAdapterNumber,
  2891. pCcb->u.pParameterTable->Read.Type.Event.pReceivedFrame,
  2892. &buffersLeft
  2893. );
  2894. //
  2895. // free the READ CCB and parameter table
  2896. //
  2897. LocalFree((HLOCAL)pCcb);
  2898. IF_DEBUG(DLC_ALLOC) {
  2899. DPUT1("FREE: freed block @ %x\n", pCcb);
  2900. }
  2901. }
  2902. VOID
  2903. IssueHardwareInterrupt(
  2904. VOID
  2905. )
  2906. /*++
  2907. Routine Description:
  2908. Issue a simulated hardware interrupt to the VDM. This routine exists because
  2909. we were losing interrupts - seeing more calls to call_ica_hw_interrupt than
  2910. calls to VrDlcHwInterrupt. Hence presumably simulated interrupts were being
  2911. lost. So we now only have 1 un-acknowledged simulated interrupt outstanding
  2912. at any one time. If we already have interrupts outstanding then we just
  2913. increment a counter of pending interrupts. When we dismiss the current
  2914. interrupt using the companion routine AcknowledgeHardwareInterrupt, we can
  2915. generate at that point a queued interrupt
  2916. Arguments:
  2917. None.
  2918. Return Value:
  2919. None.
  2920. --*/
  2921. {
  2922. IF_DEBUG(CRITICAL) {
  2923. CRITDUMP(("*** INT ***\n"));
  2924. }
  2925. IF_DEBUG(DLC_ASYNC) {
  2926. DPUT("*** INT ***\n");
  2927. }
  2928. //
  2929. // increment the hardware interrupt counter under critical section control.
  2930. // The counter starts at -1, so 0 means 1 interrupt outstanding. If we go
  2931. // to >1 then we have interrupts queued and must wait until the current
  2932. // one is dismissed
  2933. //
  2934. IF_DEBUG(CRITSEC) {
  2935. DPUT("IssueHardwareInterrupt: ENTERING HardwareIntCritSec\n");
  2936. }
  2937. EnterCriticalSection(&HardwareIntCritSec);
  2938. ++HardwareIntsQueued;
  2939. if (!HardwareIntsQueued) {
  2940. VrQueueCompletionHandler(VrDlcHwInterrupt);
  2941. //call_ica_hw_interrupt(NETWORK_ICA, NETWORK_LINE, 1);
  2942. VrRaiseInterrupt();
  2943. } else {
  2944. IF_DEBUG(CRITICAL) {
  2945. CRITDUMP(("*** INT Queued (%d) ***\n", HardwareIntsQueued));
  2946. }
  2947. }
  2948. IF_DEBUG(CRITSEC) {
  2949. DPUT("IssueHardwareInterrupt: LEAVING HardwareIntCritSec\n");
  2950. }
  2951. LeaveCriticalSection(&HardwareIntCritSec);
  2952. IF_DEBUG(DLC_ASYNC) {
  2953. DPUT("*** EOF INT ***\n");
  2954. }
  2955. }
  2956. VOID
  2957. AcknowledgeHardwareInterrupt(
  2958. VOID
  2959. )
  2960. /*++
  2961. Routine Description:
  2962. The companion routine to IssueHardwareInterrupt. Here we just decrement the
  2963. interrupt counter. If it is >= 0 then we still have interrupts pending, so
  2964. we issue a new interrupt request. This seems to work - we don't lose
  2965. interrupt requests to the VDM
  2966. Arguments:
  2967. None.
  2968. Return Value:
  2969. None.
  2970. --*/
  2971. {
  2972. #if DBG
  2973. LONG deferredInts;
  2974. #endif
  2975. IF_DEBUG(CRITICAL) {
  2976. CRITDUMP(("*** INT ACK ***\n"));
  2977. }
  2978. IF_DEBUG(DLC_ASYNC) {
  2979. DPUT("*** INT ACK ***\n");
  2980. }
  2981. //
  2982. // decrement the interrupt counter within the critical section. If it goes
  2983. // to -1 then we have no more outstanding hardware interrupt requests. If
  2984. // it is > -1 then issue a new interrupt request
  2985. //
  2986. IF_DEBUG(CRITSEC) {
  2987. DPUT("AcknowledgeHardwareInterrupt: ENTERING HardwareIntCritSec\n");
  2988. }
  2989. EnterCriticalSection(&HardwareIntCritSec);
  2990. --HardwareIntsQueued;
  2991. #if DBG
  2992. deferredInts = HardwareIntsQueued;
  2993. #endif
  2994. //
  2995. // sanity check
  2996. //
  2997. ASSERT(HardwareIntsQueued >= -1);
  2998. if (HardwareIntsQueued >= 0) {
  2999. IF_DEBUG(CRITICAL) {
  3000. CRITDUMP(("*** INT2 ***\n"));
  3001. }
  3002. VrQueueCompletionHandler(VrDlcHwInterrupt);
  3003. //call_ica_hw_interrupt(NETWORK_ICA, NETWORK_LINE, 1);
  3004. VrRaiseInterrupt();
  3005. }
  3006. IF_DEBUG(CRITSEC) {
  3007. DPUT("AcknowledgeHardwareInterrupt: LEAVING HardwareIntCritSec\n");
  3008. }
  3009. LeaveCriticalSection(&HardwareIntCritSec);
  3010. #if DBG
  3011. IF_DEBUG(CRITICAL) {
  3012. CRITDUMP(("*** EOF INT ACK (%d) ***\n", deferredInts));
  3013. }
  3014. IF_DEBUG(DLC_ASYNC) {
  3015. DPUT1("*** EOF INT ACK (%d) ***\n", deferredInts);
  3016. }
  3017. #endif
  3018. }
  3019. VOID
  3020. CancelHardwareInterrupts(
  3021. IN LONG Count
  3022. )
  3023. /*++
  3024. Routine Description:
  3025. Used to decrement the number of pending h/w interrupts. We need to do this
  3026. when removing completed READ CCBs from an event queue for which h/w
  3027. interrupts have been issued
  3028. Arguments:
  3029. Count - number of h/w interrupt requests to cancel. Used to aggregate
  3030. the cancels, saving Count-1 calls to this routine & Enter &
  3031. Leave critical section calls
  3032. Return Value:
  3033. None.
  3034. --*/
  3035. {
  3036. #if DBG
  3037. LONG deferredInts;
  3038. #endif
  3039. IF_DEBUG(CRITICAL) {
  3040. CRITDUMP(("*** CancelHardwareInterrupts(%d) ***\n", Count));
  3041. }
  3042. IF_DEBUG(DLC_ASYNC) {
  3043. DPUT1("*** CancelHardwareInterrupts(%d) ***\n", Count);
  3044. }
  3045. //
  3046. // decrement the interrupt counter within the critical section. If it goes
  3047. // to -1 then we have no more outstanding hardware interrupt requests. If
  3048. // it is > -1 then issue a new interrupt request
  3049. //
  3050. IF_DEBUG(CRITSEC) {
  3051. DPUT("CancelHardwareInterrupts: ENTERING HardwareIntCritSec\n");
  3052. }
  3053. EnterCriticalSection(&HardwareIntCritSec);
  3054. HardwareIntsQueued -= Count;
  3055. #if DBG
  3056. deferredInts = HardwareIntsQueued;
  3057. #endif
  3058. //
  3059. // sanity check
  3060. //
  3061. ASSERT(HardwareIntsQueued >= -1);
  3062. IF_DEBUG(CRITSEC) {
  3063. DPUT("CancelHardwareInterrupts: LEAVING HardwareIntCritSec\n");
  3064. }
  3065. LeaveCriticalSection(&HardwareIntCritSec);
  3066. #if DBG
  3067. IF_DEBUG(CRITICAL) {
  3068. CRITDUMP(("*** EOF CancelHardwareInterrupts (%d) ***\n", deferredInts));
  3069. }
  3070. IF_DEBUG(DLC_ASYNC) {
  3071. DPUT1("*** EOF CancelHardwareInterrupts (%d) ***\n", deferredInts);
  3072. }
  3073. #endif
  3074. }