Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3893 lines
112 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Copyright (c) 1991 Nokia Data Systems
  4. Module Name:
  5. vrdlc5c.c
  6. Abstract:
  7. This module handles DLC INT 5Ch calls from a VDM
  8. Contents:
  9. VrDlc5cHandler
  10. (ValidateDosAddress)
  11. (AutoOpenAdapter)
  12. (ProcessImmediateCommand)
  13. (MapDosCommandsToNt)
  14. CompleteCcbProcessing
  15. (InitializeAdapterSupport)
  16. (SaveExceptions)
  17. (RestoreExceptions)
  18. (CopyDosBuffersToDescriptorArray)
  19. (BufferCreate)
  20. (SetExceptionFlags)
  21. LlcCommand
  22. (OpenAdapter)
  23. (CloseAdapter)
  24. (OpenDirectStation)
  25. (CloseDirectStation)
  26. BufferFree
  27. (VrDlcInit)
  28. VrVdmWindowInit
  29. (GetAdapterType)
  30. (LoadDlcDll)
  31. TerminateDlcEmulation
  32. InitializeDlcWorkerThread
  33. VrDlcWorkerThread
  34. DlcCallWorker
  35. Author:
  36. Antti Saarenheimo (o-anttis) 26-DEC-1991
  37. Revision History:
  38. 16-Jul-1992 Richard L Firth (rfirth)
  39. Rewrote large parts - separated functions into categories (complete
  40. in DLL, complete in driver, complete asynchronously); allocate NT
  41. CCBs for commands which complete asynchronously; fixed asynchronous
  42. processing; added extra debugging; condensed various per-adapter data
  43. structures into Adapters data structure; made processing closer to IBM
  44. LAN Tech. Ref. specification
  45. --*/
  46. #include <nt.h>
  47. #include <ntrtl.h> // ASSERT, DbgPrint
  48. #include <nturtl.h>
  49. #include <windows.h>
  50. #include <softpc.h> // x86 virtual machine definitions
  51. #include <vrdlctab.h>
  52. #include <vdmredir.h>
  53. #include <smbgtpt.h>
  54. #include <dlcapi.h> // Official DLC API definition
  55. #include <ntdddlc.h> // IOCTL commands
  56. #include <dlcio.h> // Internal IOCTL API interface structures
  57. #include <vrdefld.h> // VDM_LOAD_INFO
  58. #include "vrdlc.h"
  59. #include "vrdebug.h"
  60. #include "vrdlcdbg.h"
  61. //
  62. // defines
  63. //
  64. //
  65. // for each DLC command, a flags byte in DlcFunctionalCharacteristics uses these
  66. // bits to indicate properties of the command processing
  67. //
  68. #define POINTERS_IN_TABLE 0x01 // pointers in parameter table
  69. #define OUTPUT_PARMS 0x02 // parameters returned from DLC
  70. #define SECONDARY_TABLE 0x04 // parameter table has pointers to secondary table(s)
  71. #define IMMEDIATE_COMMAND 0x20 // command executes without call to DLC DLL
  72. #define SYNCHRONOUS_COMMAND 0x40 // command executes in workstation
  73. #define UNSUPPORTED_COMMAND 0x80 // command is not supported in DOS DLC
  74. //
  75. // macros
  76. //
  77. //
  78. // IS_IMMEDIATE_COMMAND - the following commands are those which complete
  79. // 'immediately' - i.e. without having to submit the CCB to AcsLan or NtAcsLan.
  80. // Immediate commands may read and write the parameter table though
  81. //
  82. #define IS_IMMEDIATE_COMMAND(c) (((c) == LLC_BUFFER_FREE) || \
  83. ((c) == LLC_BUFFER_GET) || \
  84. ((c) == LLC_DIR_INTERRUPT) || \
  85. ((c) == LLC_DIR_MODIFY_OPEN_PARMS) || \
  86. ((c) == LLC_DIR_RESTORE_OPEN_PARMS) || \
  87. ((c) == LLC_DIR_SET_USER_APPENDAGE) \
  88. )
  89. //
  90. // private prototypes
  91. //
  92. LLC_STATUS
  93. ValidateDosAddress(
  94. IN DOS_ADDRESS Address,
  95. IN WORD Size,
  96. IN LLC_STATUS ErrorCode
  97. );
  98. LLC_STATUS
  99. AutoOpenAdapter(
  100. IN UCHAR AdapterNumber
  101. );
  102. LLC_STATUS
  103. ProcessImmediateCommand(
  104. IN UCHAR AdapterNumber,
  105. IN UCHAR Command,
  106. IN LLC_DOS_PARMS UNALIGNED * pParms
  107. );
  108. LLC_STATUS
  109. MapDosCommandsToNt(
  110. IN PLLC_CCB pDosCcb,
  111. IN DOS_ADDRESS dpOriginalCcbAddress,
  112. OUT LLC_DOS_CCB UNALIGNED * pOutputCcb
  113. );
  114. LLC_STATUS
  115. InitializeAdapterSupport(
  116. IN UCHAR AdapterNumber,
  117. IN DOS_DLC_DIRECT_PARMS UNALIGNED * pDirectParms OPTIONAL
  118. );
  119. VOID
  120. SaveExceptions(
  121. IN UCHAR AdapterNumber,
  122. IN LPDWORD pulExceptionFlags
  123. );
  124. LPDWORD
  125. RestoreExceptions(
  126. IN UCHAR AdapterNumber
  127. );
  128. LLC_STATUS
  129. CopyDosBuffersToDescriptorArray(
  130. IN OUT PLLC_TRANSMIT_DESCRIPTOR pDescriptors,
  131. IN PLLC_XMIT_BUFFER pDlcBufferQueue,
  132. IN OUT LPDWORD pIndex
  133. );
  134. LLC_STATUS
  135. BufferCreate(
  136. IN UCHAR AdapterNumber,
  137. IN PVOID pVirtualMemoryBuffer,
  138. IN DWORD ulVirtualMemorySize,
  139. IN DWORD ulMinFreeSizeThreshold,
  140. OUT HANDLE* phBufferPoolHandle
  141. );
  142. LLC_STATUS
  143. SetExceptionFlags(
  144. IN UCHAR AdapterNumber,
  145. IN DWORD ulAdapterCheckFlag,
  146. IN DWORD ulNetworkStatusFlag,
  147. IN DWORD ulPcErrorFlag,
  148. IN DWORD ulSystemActionFlag
  149. );
  150. LLC_STATUS
  151. OpenAdapter(
  152. IN UCHAR AdapterNumber
  153. );
  154. VOID
  155. CloseAdapter(
  156. IN UCHAR AdapterNumber
  157. );
  158. LLC_STATUS
  159. OpenDirectStation(
  160. IN UCHAR AdapterNumber
  161. );
  162. VOID
  163. CloseDirectStation(
  164. IN UCHAR AdapterNumber
  165. );
  166. LLC_STATUS
  167. VrDlcInit(
  168. VOID
  169. );
  170. ADAPTER_TYPE
  171. GetAdapterType(
  172. IN UCHAR AdapterNumber
  173. );
  174. BOOLEAN
  175. LoadDlcDll(
  176. VOID
  177. );
  178. BOOLEAN
  179. InitializeDlcWorkerThread(
  180. VOID
  181. );
  182. VOID
  183. VrDlcWorkerThread(
  184. IN LPVOID Parameters
  185. );
  186. LLC_STATUS
  187. DlcCallWorker(
  188. PLLC_CCB pInputCcb,
  189. PLLC_CCB pOriginalCcb,
  190. PLLC_CCB pOutputCcb
  191. );
  192. //
  193. // public data
  194. //
  195. //
  196. // lpVdmWindow is the flat 32-bit address of the VDM_REDIR_DOS_WINDOW structure
  197. // in DOS memory (in the redir TSR)
  198. //
  199. LPVDM_REDIR_DOS_WINDOW lpVdmWindow = 0;
  200. //
  201. // dpVdmWindow is the DOS address (ssssoooo, s=segment, o=offset) of the
  202. // VDM_REDIR_DOS_WINDOW structure in DOS memory (in the redir TSR)
  203. //
  204. DOS_ADDRESS dpVdmWindow = 0;
  205. DWORD OpenedAdapters = 0;
  206. //
  207. // Adapters - for each adapter supported by DOS emulation (maximum 2 adapters -
  208. // primary & secondary) there is a structure which maintains adapter specific
  209. // information - like whether the adapter has been opened, etc.
  210. //
  211. DOS_ADAPTER Adapters[DOS_DLC_MAX_ADAPTERS];
  212. //
  213. // all functions in DLCAPI.DLL are now called indirected through function pointer
  214. // create these typedefs to avoid compiler warnings
  215. //
  216. typedef ACSLAN_STATUS (*ACSLAN_FUNC_PTR)(IN OUT PLLC_CCB, OUT PLLC_CCB*);
  217. ACSLAN_FUNC_PTR lpAcsLan;
  218. typedef LLC_STATUS (*DLC_CALL_DRIVER_FUNC_PTR)(IN UINT, IN UINT, IN PVOID, IN UINT, OUT PVOID, IN UINT);
  219. DLC_CALL_DRIVER_FUNC_PTR lpDlcCallDriver;
  220. typedef LLC_STATUS (*NTACSLAN_FUNC_PTR)(IN PLLC_CCB, IN PVOID, OUT PLLC_CCB, IN HANDLE OPTIONAL);
  221. NTACSLAN_FUNC_PTR lpNtAcsLan;
  222. //
  223. // private data
  224. //
  225. static LLC_EXTENDED_ADAPTER_PARMS DefaultExtendedParms = {
  226. NULL, // hBufferPool
  227. NULL, // pSecurityDescriptor
  228. LLC_ETHERNET_TYPE_DEFAULT // LlcEthernetType
  229. };
  230. //
  231. // DlcFunctionCharacteristics - for each DOS DLC command, tells us the size of
  232. // the parameter table to copy and whether there are pointers in the parameter
  233. // table. Segmented 16-bit pointers in the parameter table must be converted to
  234. // flat 32-bit pointers. The Flags byte tells us - at a glance - the following:
  235. //
  236. // - if this command is supported a) in DOS DLC, b) in our implementation
  237. // - if this command has parameters
  238. // - if there are (DOS) pointers in the parameter table
  239. // - if this command receives output parameters (ie written to parameter table)
  240. // - if the parameter table has secondary parameter tables (DIR.OPEN.ADAPTER)
  241. // - if this command is synchronous (ie does not return 0xFF)
  242. //
  243. struct {
  244. BYTE ParamSize; // no parameter tables >255 bytes long
  245. BYTE Flags;
  246. } DlcFunctionCharacteristics[] = {
  247. {0, IMMEDIATE_COMMAND}, // 0x00, DIR.INTERRUPT
  248. {
  249. sizeof(LLC_DIR_MODIFY_OPEN_PARMS),
  250. IMMEDIATE_COMMAND
  251. }, // 0x01, DIR.MODIFY.OPEN.PARMS
  252. {0, IMMEDIATE_COMMAND}, // 0x02, DIR.RESTORE.OPEN.PARMS
  253. {
  254. sizeof(LLC_DIR_OPEN_ADAPTER_PARMS),
  255. SYNCHRONOUS_COMMAND
  256. | SECONDARY_TABLE
  257. | OUTPUT_PARMS
  258. | POINTERS_IN_TABLE
  259. }, // 0x03, DIR.OPEN.ADAPTER
  260. {0, 0x00}, // 0x04, DIR.CLOSE.ADAPTER
  261. {0, UNSUPPORTED_COMMAND}, // 0x05, ?
  262. {0, SYNCHRONOUS_COMMAND}, // 0x06, DIR.SET.GROUP.ADDRESS
  263. {0, SYNCHRONOUS_COMMAND}, // 0x07, DIR.SET.FUNCTIONAL.ADDRESS
  264. {0, SYNCHRONOUS_COMMAND}, // 0x08, READ.LOG
  265. {0, UNSUPPORTED_COMMAND}, // 0x09, NT: TRANSMIT.FRAME
  266. {
  267. sizeof(LLC_TRANSMIT_PARMS),
  268. POINTERS_IN_TABLE
  269. }, // 0x0a, TRANSMIT.DIR.FRAME
  270. {
  271. sizeof(LLC_TRANSMIT_PARMS),
  272. POINTERS_IN_TABLE
  273. }, // 0x0b, TRANSMIT.I.FRAME
  274. {0, UNSUPPORTED_COMMAND}, // 0x0c, ?
  275. {sizeof(LLC_TRANSMIT_PARMS), POINTERS_IN_TABLE},// 0x0d, TRANSMIT.UI.FRAME
  276. {sizeof(LLC_TRANSMIT_PARMS), POINTERS_IN_TABLE},// 0x0e, TRANSMIT.XID.CMD
  277. {sizeof(LLC_TRANSMIT_PARMS), POINTERS_IN_TABLE},// 0x0f, TRANSMIT.XID.RESP.FINAL
  278. {sizeof(LLC_TRANSMIT_PARMS), POINTERS_IN_TABLE},// 0x10, TRANSMIT.XID.RESP.NOT.FINAL
  279. {sizeof(LLC_TRANSMIT_PARMS), POINTERS_IN_TABLE},// 0x11, TRANSMIT.TEST.CMD
  280. {0, UNSUPPORTED_COMMAND}, // 0x12, ?
  281. {0, UNSUPPORTED_COMMAND}, // 0x13, ?
  282. {0, 0x00}, // 0x14, DLC.RESET
  283. {
  284. sizeof(LLC_DLC_OPEN_SAP_PARMS),
  285. SYNCHRONOUS_COMMAND
  286. | OUTPUT_PARMS
  287. | POINTERS_IN_TABLE
  288. }, // 0x15, DLC.OPEN.SAP
  289. {0, 0x00}, // 0x16, DLC.CLOSE.SAP
  290. {0, SYNCHRONOUS_COMMAND}, // 0x17, DLC_REALLOCATE
  291. {0, UNSUPPORTED_COMMAND}, // 0x18, ?
  292. {
  293. sizeof(LLC_DLC_OPEN_STATION_PARMS),
  294. SYNCHRONOUS_COMMAND
  295. | OUTPUT_PARMS
  296. | POINTERS_IN_TABLE
  297. }, // 0x19, DLC.OPEN.STATION
  298. {0, 0x00}, // 0x1a, DLC.CLOSE.STATION
  299. {
  300. sizeof(LLC_DLC_CONNECT_PARMS),
  301. POINTERS_IN_TABLE
  302. }, // 0x1b, DLC.CONNECT.STATION
  303. {
  304. sizeof(LLC_DLC_MODIFY_PARMS),
  305. SYNCHRONOUS_COMMAND
  306. | POINTERS_IN_TABLE
  307. }, // 0x1c, DLC.MODIFY
  308. {0, SYNCHRONOUS_COMMAND}, // 0x1d, DLC.FLOW.CONTROL
  309. {
  310. sizeof(LLC_DLC_STATISTICS_PARMS),
  311. SYNCHRONOUS_COMMAND
  312. | OUTPUT_PARMS
  313. | POINTERS_IN_TABLE
  314. }, // 0x1e, DLC.STATISTICS
  315. {0, UNSUPPORTED_COMMAND}, // 0x1f, ?
  316. {
  317. sizeof(LLC_DOS_DIR_INITIALIZE_PARMS),
  318. SYNCHRONOUS_COMMAND
  319. | OUTPUT_PARMS
  320. }, // 0x20, DIR.INITIALIZE
  321. {
  322. sizeof(DOS_DIR_STATUS_PARMS) - 2,
  323. SYNCHRONOUS_COMMAND
  324. | OUTPUT_PARMS
  325. | POINTERS_IN_TABLE
  326. }, // 0x21, DIR.STATUS
  327. {0, 0x00}, // 0x22, DIR.TIMER.SET
  328. {0, SYNCHRONOUS_COMMAND}, // 0x23, DIR.TIMER.CANCEL
  329. {0, UNSUPPORTED_COMMAND}, // 0x24, PDT.TRACE.ON / DLC_TRACE_INITIALIZE
  330. {0, UNSUPPORTED_COMMAND}, // 0x25, PDT.TRACE.OFF
  331. {
  332. sizeof(LLC_BUFFER_GET_PARMS),
  333. IMMEDIATE_COMMAND
  334. | OUTPUT_PARMS
  335. }, // 0x26, BUFFER.GET
  336. {
  337. sizeof(LLC_BUFFER_FREE_PARMS),
  338. IMMEDIATE_COMMAND
  339. | POINTERS_IN_TABLE
  340. }, // 0x27, BUFFER.FREE
  341. {sizeof(LLC_DOS_RECEIVE_PARMS), OUTPUT_PARMS}, // 0x28, RECEIVE
  342. {0, SYNCHRONOUS_COMMAND}, // 0x29, RECEIVE.CANCEL
  343. {
  344. sizeof(LLC_DOS_RECEIVE_MODIFY_PARMS),
  345. SYNCHRONOUS_COMMAND
  346. | OUTPUT_PARMS
  347. }, // 0x2a, RECEIVE.MODIFY
  348. {0, UNSUPPORTED_COMMAND}, // 0x2b, DIR.DEFINE.MIF.ENVIRONMENT
  349. {0, SYNCHRONOUS_COMMAND}, // 0x2c, DLC.TIMER.CANCEL.GROUP
  350. {
  351. sizeof(LLC_DIR_SET_EFLAG_PARMS),
  352. IMMEDIATE_COMMAND
  353. } // 0x2d, DIR.SET.USER.APPENDAGE
  354. };
  355. //
  356. // routines
  357. //
  358. VOID
  359. VrDlc5cHandler(
  360. VOID
  361. )
  362. /*++
  363. Routine Description:
  364. Receives control from the INT 5Ch BOP provided by the DOS redir TSR. The
  365. DLC calls can be subdivided into the following categories:
  366. * complete within this translation layer
  367. * complete synchronously in a call to AcsLan
  368. * complete asynchronously after calling AcsLan
  369. The latter type complete when a READ (which we submit when the adapter is
  370. opened) completes. Control transfers to an ISR in the DOS redir TSR via
  371. the EventHandlerThread (in vrdlcpst.c)
  372. The calls can be further subdivided:
  373. * calls which return parameters in the parameter table
  374. * calls which do not return parameters in the parameter table
  375. For the former type of call, we have to copy the parameter table from
  376. DOS memory and copy the returned parameters back to DOS memory
  377. With the exception of a few DLC commands, we assume that the parameter
  378. tables are exactly the same size between DOS and NT, even if the don't
  379. contain exactly the same information
  380. Arguments:
  381. None.
  382. Return Value:
  383. None, LLC_STATUS is return in AL register.
  384. --*/
  385. {
  386. LLC_CCB ccb; // should be NT CCB for NtAcsLan
  387. LLC_PARMS parms;
  388. LLC_DOS_CCB UNALIGNED * pOutputCcb;
  389. LLC_DOS_PARMS UNALIGNED * pDosParms;
  390. DOS_ADDRESS dpOriginalCcbAddress;
  391. BOOLEAN parmsCopied;
  392. WORD paramSize;
  393. LLC_STATUS status;
  394. UCHAR command;
  395. UCHAR adapter;
  396. BYTE functionFlags;
  397. static BOOLEAN IsDlcDllLoaded = FALSE;
  398. IF_DEBUG(DLC) {
  399. DPUT("VrDlc5cHandler entered\n");
  400. }
  401. //
  402. // DLCAPI.DLL is now dynamically loaded
  403. //
  404. if (!IsDlcDllLoaded) {
  405. if (!LoadDlcDll()) {
  406. setAL(LLC_STATUS_COMMAND_CANCELLED_FAILURE);
  407. return;
  408. } else {
  409. IsDlcDllLoaded = TRUE;
  410. }
  411. }
  412. //
  413. // dpOriginalCcbAddress is the segmented 16-bit address stored as a DWORD
  414. // eg. a CCB1 address of 1234:abcd gets stored as 0x1234abcd. This will
  415. // be used in asynchronous command completion to get back the address of
  416. // the original DOS CCB
  417. //
  418. dpOriginalCcbAddress = (DOS_ADDRESS)MAKE_DWORD(getES(), getBX());
  419. //
  420. // pOutputCcb is the flat 32-bit address of the DOS CCB. We can use this
  421. // to read and write byte fields only (unaligned)
  422. //
  423. pOutputCcb = POINTER_FROM_WORDS(getES(), getBX());
  424. pOutputCcb->uchDlcStatus = (UCHAR)LLC_STATUS_PENDING;
  425. //
  426. // zero the CCB_POINTER (pNext) field. CCB1 cannot have chained CCBs on
  427. // input: this is just for returning (cancelled) pending CCBs. If we don't
  428. // zero it & the app leaves garbage there, then NtAcsLan can think it is
  429. // a pointer to a chain of CCBs (CCB2 can be chained), which is bogus
  430. //
  431. WRITE_DWORD(&pOutputCcb->pNext, 0);
  432. IF_DEBUG(CRITICAL) {
  433. CRITDUMP(("INPUT CCB @%04x:%04x command=%02x\n", getES(), getBX(), pOutputCcb->uchDlcCommand));
  434. }
  435. IF_DEBUG(DOS_CCB_IN) {
  436. //
  437. // dump the input CCB1 - gives us an opportunity to check out what
  438. // the DOS app is sending us, even if its garbage
  439. //
  440. DUMPCCB(pOutputCcb,
  441. TRUE, // DumpAll
  442. TRUE, // CcbIsInput
  443. TRUE, // IsDos
  444. HIWORD(dpOriginalCcbAddress), // segment
  445. LOWORD(dpOriginalCcbAddress) // offset
  446. );
  447. }
  448. //
  449. // first check that the adapter is 0 or 1 - DOS only supports 2 adapters -
  450. // and check that the request code in the CCB is not off the end of our
  451. // table. Unsupported requests will be filtered out in the BIG switch
  452. // statement below
  453. //
  454. adapter = pOutputCcb->uchAdapterNumber;
  455. command = pOutputCcb->uchDlcCommand;
  456. if (adapter >= DOS_DLC_MAX_ADAPTERS) {
  457. //
  458. // adapter is not 0 or 1 - return 0x1D
  459. //
  460. status = LLC_STATUS_INVALID_ADAPTER;
  461. pOutputCcb->uchDlcStatus = (UCHAR)status;
  462. } else if (command > LAST_ELEMENT(DlcFunctionCharacteristics)) {
  463. //
  464. // command is off end of supported list - return 0x01
  465. //
  466. status = LLC_STATUS_INVALID_COMMAND;
  467. pOutputCcb->uchDlcStatus = (UCHAR)status;
  468. } else {
  469. //
  470. // the command is in range. Get the parameter table size and flags from
  471. // the function characteristics array
  472. //
  473. functionFlags = DlcFunctionCharacteristics[command].Flags;
  474. paramSize = DlcFunctionCharacteristics[command].ParamSize;
  475. //
  476. // if we don't support this command, return an error
  477. //
  478. status = LLC_STATUS_SUCCESS;
  479. if (functionFlags & UNSUPPORTED_COMMAND) {
  480. status = LLC_STATUS_INVALID_COMMAND;
  481. pOutputCcb->uchDlcStatus = LLC_STATUS_INVALID_COMMAND;
  482. } else {
  483. //
  484. // command is supported. If it has a parameter table, check that
  485. // the address is in range for 0x1B error check
  486. //
  487. if (paramSize) {
  488. status = ValidateDosAddress((DOS_ADDRESS)(pOutputCcb->u.pParms),
  489. paramSize,
  490. LLC_STATUS_INVALID_PARAMETER_TABLE
  491. );
  492. }
  493. //
  494. // we allow the adapter to be opened as a consequence of another
  495. // request since DOS apps could assume that the adapter has already
  496. // been opened (by NetBIOS). If the command is DIR.OPEN.ADAPTER or
  497. // DIR.CLOSE.ADAPTER then let it go through
  498. //
  499. if (status == LLC_STATUS_SUCCESS
  500. && !Adapters[adapter].IsOpen
  501. && !(command == LLC_DIR_OPEN_ADAPTER || command == LLC_DIR_CLOSE_ADAPTER)) {
  502. status = AutoOpenAdapter(adapter);
  503. } else {
  504. status = LLC_STATUS_SUCCESS;
  505. }
  506. }
  507. //
  508. // if we have a valid command, an ok-looking parameter table pointer
  509. // and an open adapter (or a command which will open or close it) then
  510. // process the command
  511. //
  512. if (status == LLC_STATUS_SUCCESS) {
  513. //
  514. // get a 32-bit pointer to the DOS parameter table. This may be
  515. // an unaligned address!
  516. //
  517. pDosParms = READ_FAR_POINTER(&pOutputCcb->u.pParms);
  518. //
  519. // the CCB commands are subdivided into those which do not need the
  520. // CCB to be mapped from DOS memory to NT memory and which complete
  521. // 'immediately' in this DLL, and those which must be mapped from
  522. // DOS to NT and which may complete synchronously or asynchronously
  523. //
  524. if (functionFlags & IMMEDIATE_COMMAND) {
  525. IF_DEBUG(DLC) {
  526. DPUT("VrDlc5cHandler: request is IMMEDIATE command\n");
  527. }
  528. status = ProcessImmediateCommand(adapter, command, pDosParms);
  529. //
  530. // the following is safe - it is a single byte write
  531. //
  532. pOutputCcb->uchDlcStatus = (char)status;
  533. //
  534. // the 'immediate' case is now complete, and control can be
  535. // returned to the VDM
  536. //
  537. } else {
  538. //
  539. // the CCB is not one which can be completed immediately. We
  540. // have to copy (and align) the DOS CCB (and the parameter
  541. // table, if there is one) into 32-bit address space.
  542. // Note that since we are going to call AcsLan or NtAcsLan
  543. // with this CCB then we supply the correct CCB format - 2,
  544. // not 1 as it was previously. However, handing in a CCB1
  545. // didn't *seem* to cause any problems (yet)
  546. //
  547. RtlCopyMemory(&ccb, pOutputCcb, sizeof(*pOutputCcb));
  548. //
  549. // zero the unused fields
  550. //
  551. ccb.hCompletionEvent = 0;
  552. ccb.uchReserved2 = 0;
  553. ccb.uchReadFlag = 0;
  554. ccb.usReserved3 = 0;
  555. parmsCopied = FALSE;
  556. if (paramSize) {
  557. //
  558. // if the parameter table contains (segmented) pointers
  559. // (which we need to convert to flat-32 bit pointers) OR
  560. // the parameter table is not DWORD aligned, copy the whole
  561. // parameter table to 32-bit memory . If we need to modify
  562. // pointers, do it in the specific case in the switch
  563. // statement in MapDosCommandsToNt
  564. //
  565. // Note: DIR.OPEN.ADAPTER is a special case because its
  566. // parameter table just points to 4 other parameter tables.
  567. // We take care of this in MapDosCommandsToNt and
  568. // CompleteCcbProcessing
  569. //
  570. if ((functionFlags & POINTERS_IN_TABLE)) {
  571. RtlCopyMemory(&parms, pDosParms, paramSize);
  572. ccb.u.pParameterTable = &parms;
  573. parmsCopied = TRUE;
  574. } else {
  575. //
  576. // didn't need to copy parameter table - leave it in
  577. // DOS memory. It is safe to read & write this table
  578. //
  579. ccb.u.pParameterTable = (PLLC_PARMS)pDosParms;
  580. }
  581. }
  582. //
  583. // submit the synchronous/asynchronous CCB for processing
  584. //
  585. status = MapDosCommandsToNt(&ccb, dpOriginalCcbAddress, pOutputCcb);
  586. if (status == STATUS_PENDING) {
  587. status = LLC_STATUS_PENDING;
  588. }
  589. IF_DEBUG(CRITICAL) {
  590. CRITDUMP(("CCB submitted: returns %02x\n", status));
  591. }
  592. IF_DEBUG(DLC) {
  593. DPUT2("VrDlc5cHandler: MapDosCommandsToNt returns %#x (%d)\n", status, status);
  594. }
  595. //
  596. // if status is not LLC_STATUS_PENDING then the CCB completed
  597. // synchronously. We can complete the processing here
  598. //
  599. if (status != LLC_STATUS_PENDING) {
  600. if ((functionFlags & OUTPUT_PARMS) && parmsCopied) {
  601. //
  602. // if there are no pointers in the parameter table then
  603. // we can simply copy the 32-bit parameters to 16-bit
  604. // memory. If there are pointers, then they will be in
  605. // an incorrect format for DOS. We must update these
  606. // parameter tables individually
  607. //
  608. if (!(functionFlags & POINTERS_IN_TABLE)) {
  609. RtlCopyMemory(pDosParms, &parms, paramSize);
  610. } else {
  611. CompleteCcbProcessing(status, pOutputCcb, &parms);
  612. }
  613. }
  614. //
  615. // set the CCB status. It will be marked as PENDING if
  616. // LLC_STATUS_PENDING returned from MapDosCommandsToNt
  617. //
  618. pOutputCcb->uchDlcStatus = (UCHAR)status;
  619. }
  620. }
  621. } else {
  622. pOutputCcb->uchDlcStatus = (UCHAR)status;
  623. }
  624. }
  625. //
  626. // return the DLC status in AL
  627. //
  628. setAL((UCHAR)status);
  629. #if DBG
  630. IF_DEBUG(DOS_CCB_OUT) {
  631. DPUT2("VrDlc5cHandler: returning AL=%02x CCB.RETCODE=%02x\n",
  632. status,
  633. pOutputCcb->uchDlcStatus
  634. );
  635. //
  636. // dump the CCB being returned to the DOS app
  637. //
  638. DumpCcb(pOutputCcb,
  639. TRUE, // DumpAll
  640. FALSE, // CcbIsInput
  641. TRUE, // IsDos
  642. HIWORD(dpOriginalCcbAddress), // segment
  643. LOWORD(dpOriginalCcbAddress) // offset
  644. );
  645. }
  646. //
  647. // make sure (in debug version) that the error code being returned is valid
  648. // for this particular command. Doesn't necessarily mean the return code is
  649. // semantically correct, just that it belongs to the set of allowed DLC
  650. // return codes for the DLC command being processed
  651. //
  652. IF_DEBUG(DLC) {
  653. if (!IsCcbErrorCodeAllowable(pOutputCcb->uchDlcCommand, pOutputCcb->uchDlcStatus)) {
  654. DPUT2("Returning bad error code: Command=%02x, Retcode=%02x\n",
  655. pOutputCcb->uchDlcCommand,
  656. pOutputCcb->uchDlcStatus
  657. );
  658. DEBUG_BREAK();
  659. }
  660. }
  661. #endif
  662. }
  663. LLC_STATUS
  664. ValidateDosAddress(
  665. IN DOS_ADDRESS Address,
  666. IN WORD Size,
  667. IN LLC_STATUS ErrorCode
  668. )
  669. /*++
  670. Routine Description:
  671. IBM DLC performs some checking of pointers - if the address points into
  672. the IVT or is close enough to the end of a segment that the address would
  673. wrap then we return an error
  674. This is a useless test, but we do it for compatibility (just in case an
  675. app tests for the specific error code). There are a million other addresses
  676. in DOS memory that need to be checked. The tests in this routine will only
  677. protect against scribbling over the interrupt vectors, but would allow e.g.
  678. scribbling over DOS's code segment
  679. Arguments:
  680. Address - DOS address to check (ssssoooo, s=segment, o=offset)
  681. Size - word size of structure at Address
  682. ErrorCode - which error code to return. This function called to validate
  683. A) the parameter table pointer, in which case the error code
  684. to return is LLC_STATUS_INVALID_PARAMETER_TABLE (0x1B) or
  685. B) pointers within the parameter table, in which case the
  686. error to return is LLC_STATUS_INVALID_POINTER_IN_CCB (0x1C)
  687. Return Value:
  688. LLC_STATUS
  689. --*/
  690. {
  691. //
  692. // convert segment:offset into 20-bit real-mode linear address
  693. //
  694. DWORD linearAddress = HIWORD(Address) * 16 + LOWORD(Address);
  695. //
  696. // the Interrupt Vector Table (IVT) in real-mode occupies addresses 0
  697. // through 400h
  698. //
  699. if ((linearAddress < 0x400L) || (((DWORD)LOWORD(Address) + Size) < (DWORD)LOWORD(Address))) {
  700. return ErrorCode;
  701. }
  702. return LLC_STATUS_SUCCESS;
  703. }
  704. LLC_STATUS
  705. AutoOpenAdapter(
  706. IN UCHAR AdapterNumber
  707. )
  708. /*++
  709. Routine Description:
  710. Opens the adapter as a consequence of a request other than DIR.OPEN.ADAPTER
  711. Arguments:
  712. AdapterNumber - which adapter to open
  713. Return Value:
  714. LLC_STATUS
  715. Success - LLC_STATUS_SUCCESS
  716. Failure -
  717. --*/
  718. {
  719. LLC_STATUS status;
  720. //
  721. // Any DLC command except DIR.OPEN.ADAPTER or DIR.INITIALIZE automatically
  722. // opens the adapter. There are three reasons to do this:
  723. //
  724. // 1. DIR.STATUS command can be issued before DIR.OPEN.ADAPTER in DOS.
  725. // In Windows/Nt this is not possible. Therefore, DIR.STATUS should
  726. // open the adapter
  727. //
  728. // 2. An application may assume that the adapter is always opened
  729. // by NetBIOS and that it can't open the adapter itself if it
  730. // has already been opened
  731. //
  732. // 3. A DOS DLC application may initialize (= hw reset) a closed
  733. // adapter before the open and that takes 5 - 10 seconds.
  734. //
  735. IF_DEBUG(DLC) {
  736. DPUT1("AutoOpenAdapter: automatically opening adapter %d\n", AdapterNumber);
  737. }
  738. status = OpenAdapter(AdapterNumber);
  739. if (status == LLC_STATUS_SUCCESS) {
  740. //
  741. // initialize the buffer pool for the direct station on this
  742. // adapter. If this succeeds, open the direct station. If that
  743. // succeeds, preset the ADAPTER_PARMS and DLC_PARMS structures
  744. // in the DOS_ADAPTER with default values
  745. //
  746. status = InitializeAdapterSupport(AdapterNumber, NULL);
  747. if (status == LLC_STATUS_SUCCESS) {
  748. status = OpenDirectStation(AdapterNumber);
  749. if (status == LLC_STATUS_SUCCESS) {
  750. }
  751. }
  752. if (status != LLC_STATUS_SUCCESS) {
  753. IF_DEBUG(DLC) {
  754. DPUT("AutoOpenAdapter: InitializeAdapterSupport failed\n");
  755. }
  756. }
  757. } else {
  758. IF_DEBUG(DLC) {
  759. DPUT("AutoOpenAdapter: auto open adapter failed\n");
  760. }
  761. }
  762. return status;
  763. }
  764. LLC_STATUS
  765. ProcessImmediateCommand(
  766. IN UCHAR AdapterNumber,
  767. IN UCHAR Command,
  768. IN LLC_DOS_PARMS UNALIGNED * pParms
  769. )
  770. /*++
  771. Routine Description:
  772. Processes CCB requests which complete 'immediately'. An immediate completion
  773. is one where the CCB does not have to be submitted to the DLC driver. There
  774. may be other calls to the driver as a consequence of the immediate command,
  775. but the CCB itself is not submitted. Immediate command completion requires
  776. the parameter table only. We may return parameters into the DOS parameter
  777. table
  778. Arguments:
  779. AdapterNumber - which adapter to process command for
  780. Command - command to process
  781. pParms - pointer to parameter table (in DOS memory)
  782. Return Value:
  783. LLC_STATUS
  784. Completion status of the 'immediate' command
  785. --*/
  786. {
  787. LLC_STATUS status;
  788. WORD cBuffersLeft;
  789. WORD stationId;
  790. DPLLC_DOS_BUFFER buffer;
  791. switch (Command) {
  792. case LLC_BUFFER_FREE:
  793. IF_DEBUG(DLC) {
  794. DPUT("LLC_BUFFER_FREE\n");
  795. }
  796. //
  797. // if the FIRST_BUFFER field is 0:0 then this request returns success
  798. //
  799. buffer = (DPLLC_DOS_BUFFER)READ_DWORD(&pParms->BufferFree.pFirstBuffer);
  800. if (!buffer) {
  801. status = LLC_STATUS_SUCCESS;
  802. break;
  803. }
  804. //
  805. // Windows/Nt doesn't need station id for buffer pool operation =>
  806. // thus the field is reserved
  807. //
  808. stationId = READ_WORD(&pParms->BufferFree.usReserved1);
  809. status = FreeBuffers(GET_POOL_INDEX(AdapterNumber, stationId),
  810. buffer,
  811. &cBuffersLeft
  812. );
  813. IF_DEBUG(CRITICAL) {
  814. CRITDUMP(("LLC_BUFFER_FREE: %d\n", status));
  815. }
  816. if (status == LLC_STATUS_SUCCESS) {
  817. //
  818. // write the number of buffers left to the parameter table using
  819. // WRITE_WORD macro, since the table may not be aligned on a WORD
  820. // boundary
  821. //
  822. WRITE_WORD(&pParms->BufferFree.cBuffersLeft, cBuffersLeft);
  823. //
  824. // p3-4 of the IBM LAN Tech. Ref. states that the FIRST_BUFFER
  825. // field will be set to zero when the request is completed
  826. //
  827. WRITE_DWORD(&pParms->BufferFree.pFirstBuffer, 0);
  828. //
  829. // note that a successful BUFFER.FREE has been executed for this
  830. // adapter
  831. //
  832. Adapters[AdapterNumber].BufferFree = TRUE;
  833. //
  834. // perform half of the local-busy reset processing. This only has
  835. // an effect if the link is in emulated local-busy(buffer) state.
  836. // This is required because we need 2 events to get us out of local
  837. // busy buffer state - a BUFFER.FREE and a DLC.FLOW.CONTROL command
  838. // Apps don't always issue these in the correct sequence
  839. //
  840. ResetEmulatedLocalBusyState(AdapterNumber, stationId, LLC_BUFFER_FREE);
  841. //
  842. // this here because Extra! for Windows gets its state machine in a
  843. // knot if we go buffer busy too quickly after a flow control
  844. //
  845. if (AllBuffersInPool(GET_POOL_INDEX(AdapterNumber, stationId))) {
  846. ResetEmulatedLocalBusyState(AdapterNumber, stationId, LLC_DLC_FLOW_CONTROL);
  847. }
  848. }
  849. break;
  850. case LLC_BUFFER_GET:
  851. IF_DEBUG(DLC) {
  852. DPUT("LLC_BUFFER_GET\n");
  853. }
  854. status = GetBuffers(
  855. GET_POOL_INDEX(AdapterNumber, READ_WORD(&pParms->BufferGet.usReserved1)),
  856. READ_WORD(&pParms->BufferGet.cBuffersToGet),
  857. &buffer,
  858. &cBuffersLeft,
  859. FALSE,
  860. NULL
  861. );
  862. //
  863. // if GetBuffers fails, buffer is returned as 0
  864. //
  865. WRITE_WORD(&pParms->BufferGet.cBuffersLeft, cBuffersLeft);
  866. WRITE_DWORD(&pParms->BufferGet.pFirstBuffer, buffer);
  867. break;
  868. case LLC_DIR_INTERRUPT:
  869. IF_DEBUG(DLC) {
  870. DPUT("LLC_DIR_INTERRUPT\n");
  871. }
  872. //
  873. // We may consider, that the adapter is always initialized!
  874. // I hope, that the apps doesn't use an appendage routine
  875. // in DIR_INTERRUPT, because it would be very difficult to
  876. // call from here.
  877. //
  878. status = LLC_STATUS_SUCCESS;
  879. break;
  880. case LLC_DIR_MODIFY_OPEN_PARMS:
  881. IF_DEBUG(DLC) {
  882. DPUT("LLC_DIR_MODIFY_OPEN_PARMS\n");
  883. }
  884. //
  885. // this command is rejected if a BUFFER.FREE has been issued for this
  886. // adapter or there is a RECEIVE active at the direct interface
  887. //
  888. if (Adapters[AdapterNumber].BufferFree || Adapters[AdapterNumber].DirectReceive) {
  889. //
  890. // BUGBUG - this can't be correct error code. Check what is
  891. // returned by IBM DOS DLC stack
  892. //
  893. status = LLC_STATUS_INVALID_COMMAND;
  894. } else if (Adapters[AdapterNumber].WaitingRestore) {
  895. //
  896. // BUGBUG - this can't be correct error code. Check what is
  897. // returned by IBM DOS DLC stack
  898. //
  899. status = LLC_STATUS_INVALID_COMMAND;
  900. } else {
  901. //
  902. // Create a buffer pool, if there are no buffers in
  903. // the current one, set the exception flags (or dos appendage
  904. // routines), if the operation was success
  905. //
  906. status = CreateBufferPool(
  907. (DWORD)GET_POOL_INDEX(AdapterNumber, 0),
  908. (DOS_ADDRESS)READ_DWORD(&pParms->DirModifyOpenParms.dpPoolAddress),
  909. READ_WORD(&pParms->DirModifyOpenParms.cPoolBlocks),
  910. READ_WORD(&pParms->DirModifyOpenParms.usBufferSize)
  911. );
  912. if (status == LLC_STATUS_SUCCESS) {
  913. //
  914. // SaveExceptions uses RtlCopyMemory, so its okay to pass in a possibly
  915. // unaligned pointer to the exception handlers
  916. //
  917. SaveExceptions(AdapterNumber,
  918. (LPDWORD)&pParms->DirModifyOpenParms.dpAdapterCheckExit
  919. );
  920. //
  921. // set the exception handlers as the exception flags in the
  922. // DLC driver
  923. //
  924. status = SetExceptionFlags(
  925. AdapterNumber,
  926. READ_DWORD(&pParms->DirModifyOpenParms.dpAdapterCheckExit),
  927. READ_DWORD(&pParms->DirModifyOpenParms.dpNetworkStatusExit),
  928. READ_DWORD(&pParms->DirModifyOpenParms.dpPcErrorExit),
  929. 0
  930. );
  931. }
  932. //
  933. // mark this adapter as waiting for a DIR.RESTORE.OPEN.PARMS before
  934. // we can process the next DIR.MODIFY.OPEN.PARMS
  935. //
  936. if (status == LLC_STATUS_SUCCESS) {
  937. Adapters[AdapterNumber].WaitingRestore = TRUE;
  938. }
  939. }
  940. break;
  941. case LLC_DIR_RESTORE_OPEN_PARMS:
  942. IF_DEBUG(DLC) {
  943. DPUT("LLC_DIR_RESTORE_OPEN_PARMS\n");
  944. }
  945. //
  946. // if a DIR.MODIFY.OPEN.PARMS has not been successfully processed for
  947. // this adapter then return an error
  948. //
  949. if (!Adapters[AdapterNumber].WaitingRestore) {
  950. status = LLC_STATUS_INVALID_OPTION;
  951. } else {
  952. //
  953. // Delete the current buffer pool and restore the previous exception
  954. // handlers
  955. //
  956. DeleteBufferPool(GET_POOL_INDEX(AdapterNumber, 0));
  957. pParms = (PLLC_DOS_PARMS)RestoreExceptions(AdapterNumber);
  958. status = SetExceptionFlags(
  959. AdapterNumber,
  960. READ_DWORD(&pParms->DirSetExceptionFlags.ulAdapterCheckFlag),
  961. READ_DWORD(&pParms->DirSetExceptionFlags.ulNetworkStatusFlag),
  962. READ_DWORD(&pParms->DirSetExceptionFlags.ulPcErrorFlag),
  963. 0
  964. );
  965. //
  966. // if the restore succeeded, mark this adapter as able to accept
  967. // the next DIR.MODIFY.OPEN.PARMS request
  968. //
  969. if (status == LLC_STATUS_SUCCESS) {
  970. Adapters[AdapterNumber].WaitingRestore = FALSE;
  971. }
  972. }
  973. break;
  974. case LLC_DIR_SET_USER_APPENDAGE:
  975. IF_DEBUG(DLC) {
  976. DPUT("LLC_DIR_SET_USER_APPENDAGE\n");
  977. }
  978. if (pParms == NULL) {
  979. pParms = (PLLC_DOS_PARMS)RestoreExceptions(AdapterNumber);
  980. } else {
  981. SaveExceptions(AdapterNumber, (LPDWORD)&pParms->DirSetUserAppendage);
  982. }
  983. status = SetExceptionFlags(
  984. AdapterNumber,
  985. READ_DWORD(&pParms->DirSetUserAppendage.dpAdapterCheckExit),
  986. READ_DWORD(&pParms->DirSetUserAppendage.dpNetworkStatusExit),
  987. READ_DWORD(&pParms->DirSetUserAppendage.dpPcErrorExit),
  988. 0
  989. );
  990. break;
  991. #if DBG
  992. default:
  993. DPUT1("ProcessImmediateCommand: Error: Command is NOT immediate (%x)\n", Command);
  994. DbgBreakPoint();
  995. #endif
  996. }
  997. return status;
  998. }
  999. LLC_STATUS
  1000. MapDosCommandsToNt(
  1001. IN PLLC_CCB pDosCcb,
  1002. IN DOS_ADDRESS dpOriginalCcbAddress,
  1003. OUT LLC_DOS_CCB UNALIGNED * pOutputCcb
  1004. )
  1005. /*++
  1006. Routine Description:
  1007. This function handles DOS CCBs which must be submitted to the DLC driver.
  1008. The CCBs may complete synchronously - i.e. the DLC driver returns a
  1009. success or failure indication, or they complete asyncronously - i.e. the
  1010. DLC driver returns a pending status.
  1011. This function processes CCBs which may have parameter tables. The parameter
  1012. tables will have been mapped to 32-bit memory unless they are already aligned
  1013. on a DWORD boundary
  1014. Architecture
  1015. ------------
  1016. There is a major difference in the adapter initialization between DOS and
  1017. NT (or OS/2) DLC applications. A DOS DLC application could assume that
  1018. the adapter is always open (opened by NetBIOS). It might directly check
  1019. the type of adapter with DIR.STATUS command, open SAP stations and setup
  1020. a link session to a host. Usually a DOS app uses DIR.INTERRUPT to check if
  1021. the adapter is already initialized. There are also commands to redefine
  1022. new parameters for the direct interface and restore the old ones when the
  1023. application completes. Only one application may be simultaneously using
  1024. the direct station or a SAP.
  1025. In Windows/NT each DLC application is a process having its own separate
  1026. virtual image of the DLC interface. They must first make a logical open for
  1027. the adapter to access DLC services and close the adapter when they are
  1028. terminating. Process exit automatically closes any open DLC objects.
  1029. A Windows/NT MVDM process does not allocate any DLC resources until it
  1030. issues the first DLC command, which opens the adapter and initializes its
  1031. buffer pool.
  1032. DLC Commands Different In Windows/NT And DOS
  1033. --------------------------------------------
  1034. BUFFER.FREE, BUFFER.GET
  1035. - separate buffer pools ...
  1036. DIR.DEFINE.MIF.ENVIRONMENT
  1037. - not supported, a special api for
  1038. IBM Netbios running on DLC.
  1039. DIR.INITIALIZE
  1040. - We will always return OK status from DIR.INITIALIZE: the app should not
  1041. call this very often. We don't need to care about the exception handlers
  1042. set in DIR.INITIALIZE, because they are never used. DOS DLC and OS/2 DLC
  1043. states can be mapped thus:
  1044. DOS DLC OS/2 DLC
  1045. ----------------------------
  1046. uninitialized Closed
  1047. Initialized Closed
  1048. Open Open
  1049. DIR.OPEN.ADAPTER defines new exception handlers, thus the values
  1050. set by DIR.INITIALIZE are valid only when the adapter is closed.
  1051. Therefore, nothing untoward can happen if we just ignore them
  1052. DIR.INTERRUPT
  1053. - This command opens the adapter, if it has not yet been opened
  1054. and returns the successful status.
  1055. DIR.MODIFY.OPEN.PARMS
  1056. - Defines buffers pool for the direct station, if it was not defined
  1057. in DIR.OPEN.ADAPTER, and defines DLC exception handlers.
  1058. DIR.OPEN.ADAPTER
  1059. - Can be executed only immediately after DIR.CLOSE.ADAPTER
  1060. and DIR.INITIALIZE. We must support the special DOS Direct Open Parms.
  1061. DIR.OPEN.DIRECT, DIR.CLOSE.DIRECT
  1062. - Not supported for DOS
  1063. DIR.SET.USER.APPENDAGE == DIR.SET.EXCEPTION.FLAGS (- system action flag)
  1064. - This is just one of those many functions to set the exception handlers
  1065. for DOS DLC (you may set them when adapter is opened, you may set
  1066. them when adapter is closed, you may restore the old values and
  1067. set the new values if the buffer pool was uninitialized or if they
  1068. were restored ... (I become crazy)
  1069. DIR.STATUS
  1070. - (We must fill MicroCodeLevel with a sensible string and set
  1071. AdapterConfigAddress to point a some constant code in DOS
  1072. DLC handler) Not yet implemented!!!
  1073. - DOS DLC stub code should hook the timer tick interrupt and
  1074. update the timer counter.
  1075. - We must also set the correct adapter data rate in AdapterConfig
  1076. (this should be done by the DLC driver!).
  1077. - We must convert the Nt (and OS/2) adapter type to a DOS type
  1078. (ethernet have a different value in IBM DOS and OS/2 DLC
  1079. implementations)
  1080. PDT.TRACE.ON, PDT.TRACE.OFF
  1081. - Not Supported
  1082. RECEIVE.MODIFY
  1083. - This function is not supported in the first implementation,
  1084. Richard may have to do this later, if a DOS DLC application
  1085. uses the function.
  1086. RECEIVE
  1087. - DOS uses shorter RECEIVE parameter table, than NT (or OS/2).
  1088. Thus we cannot directly use DOS CCBs. We also need the pointer
  1089. of the original receive CCB and the DOS receive appendage is called.
  1090. On the other hand, the only original CCB can be linked to the
  1091. other CCBs (using the original DOS pointer).
  1092. Solution:
  1093. The Input CCB and its parameter table are always allocated from the
  1094. stack. Output CCB is the original DOS CCB mapped to 32-bit address space.
  1095. The receive flag in the input CCB parameter table is the output CCB
  1096. pointer. The actual receive data appendage can be read from
  1097. the output CCB. DOS DLC driver completes and links the receive CCB
  1098. correctly, because we use the original receive CCB as an output buffer
  1099. and DOS CCB address.
  1100. This method would not work if we had to receive any parameters
  1101. to the parameter table (actually we could get it to work by
  1102. calling directly DLC driver with a correct parameter table address
  1103. in the CCB structure of the input parameter block. The actual
  1104. input parameters would be modified (receive data appendage)).
  1105. Adapter Exception Handlers
  1106. --------------------------
  1107. The exception handler setting and resetting is very confusing in DOS DLC,
  1108. but the main idea seems to be this: a temporary DOS DLC application must
  1109. restore the exception handlers set by a TSR, because otherwise the next
  1110. network exception would call the random memory. On the other hand, a newer
  1111. TSR may always overwrite the previous exception handlers (because it never
  1112. restores the old values). We may assume, that any DOS DLC application
  1113. process resets its exception handlers and restores the original addresses.
  1114. Solution: we have two tables, both initially reset: any set operation
  1115. copies first table 1 to table 2 and saves the new values back to table 1.
  1116. A restore operation copies table 2 back to table 1 and sets its values to DLC.
  1117. We don't make any difference between set/reset by DIR.SET.USER.APPENDAGE or
  1118. doing the same operation with DIR.MODIFY.OPEN.PARMS and DIR.RESTORE.OPEN.PARMS.
  1119. We don't try to save the buffer pool definitions, because it is unnecessary.
  1120. DLC Status
  1121. ----------
  1122. A DOS DLC status indication returns a pointer to a link specific DLC
  1123. status table. DOS DLC application may keep this pointer and use it
  1124. later for example to find the remote node address and SAP (not very likely).
  1125. On the other hand, the link may expect the status table to be stable
  1126. until another DOS task (eg. from timer tick) has responded to it.
  1127. If we used only one status table for all links, a new overwrite the old
  1128. status, because it has been handled by DOS.
  1129. For example, losing a local buffer busy indication would hang up the link
  1130. station permamently. Thus we cannot use just one link status table.
  1131. Allocating 20 bytes for each 2 * 255 link station would take 10 kB
  1132. DOS memory. Limiting the number of available link stations would
  1133. be also too painful to implement and manage by installation program.
  1134. Thus we will implement a compromise:
  1135. 1. We allocate 5 permanent DLC status tables for the first link stations
  1136. on both adapters.
  1137. 2. All other link stations (on both adapters) share the last
  1138. 5 status tables
  1139. => We need to allocate only 300 bytes DOS memory for the DLC status tables.
  1140. This will not work, if a DOS application assumes having permanent pointers
  1141. to status tables and uses more than 5 DLC sessions for an adapter or
  1142. if an application has again over 5 link stations on an adapter and
  1143. it gets very rapidily more than 5 DLC status indications (it may lose
  1144. the first DLC indications).
  1145. Actually this should work quite well, because DLC applications should
  1146. save (by copying) the DLC status in the DLC status appendage routine,
  1147. because a new DLC status indication for the same adapter could overwrite
  1148. the previous status.
  1149. Arguments:
  1150. pDosCcb - aligned DOS DLC Command control block (NT CCB)
  1151. dpOriginalCcbAddress- the original
  1152. pOutputCcb - the original DLC Command control block
  1153. Return Value:
  1154. LLC_STATUS
  1155. --*/
  1156. {
  1157. UCHAR adapter = pDosCcb->uchAdapterNumber;
  1158. UCHAR command = pDosCcb->uchDlcCommand;
  1159. DWORD InputBufferSize;
  1160. UCHAR FrameType;
  1161. DWORD cElement;
  1162. DWORD i;
  1163. PLLC_CCB pNewCcb;
  1164. LLC_STATUS Status;
  1165. NT_DLC_PARMS NtDlcParms;
  1166. LLC_DOS_PARMS UNALIGNED * pParms = (PLLC_DOS_PARMS)pDosCcb->u.pParameterTable;
  1167. PDOS_DLC_DIRECT_PARMS pDirectParms;
  1168. PLLC_PARMS pNtParms;
  1169. //
  1170. // adapterOpenParms and dlcParms are used to take the place of the DOS
  1171. // ADAPTER_PARMS and DLC_PARMS structures in DIR.OPEN.ADAPTER
  1172. //
  1173. LLC_ADAPTER_OPEN_PARMS adapterParms;
  1174. LLC_DLC_PARMS dlcParms;
  1175. DWORD groupAddress;
  1176. DWORD functionalAddress;
  1177. IF_DEBUG(DLC) {
  1178. DPUT("MapDosCommandsToNt\n");
  1179. }
  1180. //
  1181. // check that the command code in the CCB is a valid CCB1 command - this
  1182. // will error if its one of the disallowed OS/2 (CCB2) commands or an
  1183. // unsupported DOS (CCB1) command (eg PDT.TRACE.ON)
  1184. //
  1185. CHECK_CCB_COMMAND(pDosCcb);
  1186. //
  1187. // preset the CCB to PENDING
  1188. //
  1189. pOutputCcb->uchDlcStatus = (UCHAR)LLC_STATUS_PENDING;
  1190. //
  1191. // This large switch statement will convert individual DOS format parameter
  1192. // tables to NT format. Functions that can be handled entirely in VdmRedir
  1193. // return early, else we need to make a call into DlcApi (AcsLan or NtAcsLan)
  1194. //
  1195. // We must convert all 16:16 DOS pointers to flat 32-bit pointers.
  1196. // We must copy all changed data structures (that includes pointers)
  1197. // to stack, because we don't want to change them back, when
  1198. // the command completes. All transmit commands are changed to
  1199. // the new generic transmit.
  1200. //
  1201. switch (command) {
  1202. default:
  1203. IF_DEBUG(DLC) {
  1204. DPUT("*** Shouldn't be here - this command should be caught already ***\n");
  1205. }
  1206. return LLC_STATUS_INVALID_COMMAND;
  1207. //
  1208. // *** everything below here has been sanctioned ***
  1209. //
  1210. case LLC_DIR_CLOSE_ADAPTER:
  1211. IF_DEBUG(DLC) {
  1212. DPUT("LLC_DIR_CLOSE_ADAPTER\n");
  1213. }
  1214. //
  1215. // no parameter table
  1216. //
  1217. break;
  1218. case LLC_DIR_INITIALIZE:
  1219. IF_DEBUG(DLC) {
  1220. DPUT("LLC_DIR_INITIALIZE\n");
  1221. }
  1222. break;
  1223. case LLC_DIR_OPEN_ADAPTER:
  1224. IF_DEBUG(DLC) {
  1225. DPUT("LLC_DIR_OPEN_ADAPTER\n");
  1226. }
  1227. //
  1228. // copy the adapter parms and DLC parms to 32-bit memory. If there is no
  1229. // adapter parms or direct parms pointer then return an error
  1230. //
  1231. if (!(pParms->DirOpenAdapter.pAdapterParms && pParms->DirOpenAdapter.pExtendedParms)) {
  1232. return LLC_STATUS_PARAMETER_MISSING;
  1233. }
  1234. //
  1235. // copy the DOS ADAPTER_PARMS table to an NT ADAPTER_PARMS table. The
  1236. // NT table is larger, so zero the uncopied part
  1237. //
  1238. RtlCopyMemory(&adapterParms,
  1239. DOS_PTR_TO_FLAT(pParms->DirOpenAdapter.pAdapterParms),
  1240. sizeof(ADAPTER_PARMS)
  1241. );
  1242. RtlZeroMemory(&adapterParms.usReserved3[0],
  1243. sizeof(LLC_ADAPTER_OPEN_PARMS) - (DWORD)&((PLLC_ADAPTER_OPEN_PARMS)0)->usReserved3
  1244. );
  1245. pParms->DirOpenAdapter.pAdapterParms = &adapterParms;
  1246. //
  1247. // make a note of the group and functional addresses. We have to set
  1248. // these later if they're not 0x00000000. We have to save these, because
  1249. // the addresses in the table will get blasted when DIR.OPEN.ADAPTER
  1250. // (in DLCAPI.DLL/DLC.SYS) writes the current (0) values to the table
  1251. //
  1252. groupAddress = *(UNALIGNED DWORD *)adapterParms.auchGroupAddress;
  1253. functionalAddress = *(UNALIGNED DWORD *)adapterParms.auchFunctionalAddress;
  1254. //
  1255. // the DLC_PARMS table doesn't HAVE to be supplied - if we just want to
  1256. // use the direct station, we don't need these parameters. However, if
  1257. // they were supplied, copy them to 32-bit memory. The tables are the
  1258. // same size in DOS and NT
  1259. //
  1260. if (pParms->DirOpenAdapter.pDlcParms) {
  1261. RtlCopyMemory(&dlcParms,
  1262. DOS_PTR_TO_FLAT(pParms->DirOpenAdapter.pDlcParms),
  1263. sizeof(dlcParms)
  1264. );
  1265. pParms->DirOpenAdapter.pDlcParms = &dlcParms;
  1266. }
  1267. //
  1268. // set the default values for ADAPTER_PARMS. Not sure about these
  1269. //
  1270. // usReserved1 == NUMBER_RCV_BUFFERS
  1271. // usReserved2 == RCV_BUFFER_LEN
  1272. // usMaxFrameSize == DHB_BUFFER_LENGTH
  1273. // usReserved3[0] == DATA_HOLD_BUFFERS
  1274. //
  1275. if (pParms->DirOpenAdapter.pAdapterParms->usReserved1 < 2) {
  1276. pParms->DirOpenAdapter.pAdapterParms->usReserved1 = DD_NUMBER_RCV_BUFFERS;
  1277. }
  1278. if (pParms->DirOpenAdapter.pAdapterParms->usReserved2 == 0) {
  1279. pParms->DirOpenAdapter.pAdapterParms->usReserved2 = DD_RCV_BUFFER_LENGTH;
  1280. }
  1281. if (pParms->DirOpenAdapter.pAdapterParms->usMaxFrameSize == 0) {
  1282. pParms->DirOpenAdapter.pAdapterParms->usReserved1 = DD_DHB_BUFFER_LENGTH;
  1283. }
  1284. if (*(PBYTE)&pParms->DirOpenAdapter.pAdapterParms->usReserved3 == 0) {
  1285. pParms->DirOpenAdapter.pAdapterParms->usReserved1 = DD_DATA_HOLD_BUFFERS;
  1286. }
  1287. //
  1288. // if we have DLC_PARMS then set the defaults, else reset the flag
  1289. //
  1290. if (pParms->DirOpenAdapter.pDlcParms) {
  1291. if (pParms->DirOpenAdapter.pDlcParms->uchDlcMaxSaps == 0) {
  1292. pParms->DirOpenAdapter.pDlcParms->uchDlcMaxSaps = DD_DLC_MAX_SAP;
  1293. }
  1294. if (pParms->DirOpenAdapter.pDlcParms->uchDlcMaxStations == 0) {
  1295. pParms->DirOpenAdapter.pDlcParms->uchDlcMaxStations = DD_DLC_MAX_STATIONS;
  1296. }
  1297. if (pParms->DirOpenAdapter.pDlcParms->uchDlcMaxGroupSaps == 0) {
  1298. pParms->DirOpenAdapter.pDlcParms->uchDlcMaxGroupSaps = DD_DLC_MAX_GSAP;
  1299. }
  1300. if (pParms->DirOpenAdapter.pDlcParms->uchT1_TickOne == 0) {
  1301. pParms->DirOpenAdapter.pDlcParms->uchT1_TickOne = DD_DLC_T1_TICK_ONE;
  1302. }
  1303. if (pParms->DirOpenAdapter.pDlcParms->uchT2_TickOne == 0) {
  1304. pParms->DirOpenAdapter.pDlcParms->uchT2_TickOne = DD_DLC_T2_TICK_ONE;
  1305. }
  1306. if (pParms->DirOpenAdapter.pDlcParms->uchTi_TickOne == 0) {
  1307. pParms->DirOpenAdapter.pDlcParms->uchTi_TickOne = DD_DLC_Ti_TICK_ONE;
  1308. }
  1309. if (pParms->DirOpenAdapter.pDlcParms->uchT1_TickTwo == 0) {
  1310. pParms->DirOpenAdapter.pDlcParms->uchT1_TickTwo = DD_DLC_T1_TICK_TWO;
  1311. }
  1312. if (pParms->DirOpenAdapter.pDlcParms->uchT2_TickTwo == 0) {
  1313. pParms->DirOpenAdapter.pDlcParms->uchT2_TickTwo = DD_DLC_T2_TICK_TWO;
  1314. }
  1315. if (pParms->DirOpenAdapter.pDlcParms->uchTi_TickTwo == 0) {
  1316. pParms->DirOpenAdapter.pDlcParms->uchTi_TickTwo = DD_DLC_Ti_TICK_TWO;
  1317. }
  1318. Adapters[adapter].DlcSpecified = TRUE;
  1319. } else {
  1320. Adapters[adapter].DlcSpecified = FALSE;
  1321. }
  1322. //
  1323. // replace DIRECT_PARMS with the EXTENDED_ADAPTER_PARMS
  1324. //
  1325. pDirectParms = (PDOS_DLC_DIRECT_PARMS)
  1326. READ_FAR_POINTER(&pParms->DirOpenAdapter.pExtendedParms);
  1327. pParms->DirOpenAdapter.pExtendedParms = &DefaultExtendedParms;
  1328. break;
  1329. case LLC_DIR_READ_LOG:
  1330. IF_DEBUG(DLC) {
  1331. DPUT("LLC_DIR_READ_LOG\n");
  1332. }
  1333. pParms->DirReadLog.pLogBuffer = DOS_PTR_TO_FLAT(pParms->DirReadLog.pLogBuffer);
  1334. break;
  1335. case LLC_DIR_SET_FUNCTIONAL_ADDRESS:
  1336. IF_DEBUG(DLC) {
  1337. DPUT("LLC_DIR_SET_FUNCTIONAL_ADDRESS\n");
  1338. }
  1339. //
  1340. // no parameter table
  1341. //
  1342. break;
  1343. case LLC_DIR_SET_GROUP_ADDRESS:
  1344. IF_DEBUG(DLC) {
  1345. DPUT("LLC_DIR_SET_GROUP_ADDRESS\n");
  1346. }
  1347. //
  1348. // no parameter table
  1349. //
  1350. break;
  1351. case LLC_DIR_STATUS:
  1352. IF_DEBUG(DLC) {
  1353. DPUT("LLC_DIR_STATUS\n");
  1354. }
  1355. break;
  1356. case LLC_DIR_TIMER_CANCEL:
  1357. IF_DEBUG(DLC) {
  1358. DPUT("LLC_DIR_TIMER_CANCEL\n");
  1359. }
  1360. //
  1361. // no parameter table
  1362. //
  1363. break;
  1364. case LLC_DIR_TIMER_CANCEL_GROUP:
  1365. IF_DEBUG(DLC) {
  1366. DPUT("LLC_DIR_TIMER_CANCEL_GROUP\n");
  1367. }
  1368. //
  1369. // no parameter table
  1370. //
  1371. break;
  1372. case LLC_DIR_TIMER_SET:
  1373. IF_DEBUG(DLC) {
  1374. DPUT("LLC_DIR_TIMER_SET\n");
  1375. }
  1376. //
  1377. // no parameter table
  1378. //
  1379. //
  1380. // Debug code is too slow - commands keep timing out. Multiply the
  1381. // timer value by 2 to give us a chance!
  1382. //
  1383. IF_DEBUG(DOUBLE_TICKS) {
  1384. pDosCcb->u.dir.usParameter0 *= 2;
  1385. }
  1386. break;
  1387. case LLC_DLC_CLOSE_SAP:
  1388. IF_DEBUG(DLC) {
  1389. DPUT("LLC_DLC_CLOSE_SAP\n");
  1390. }
  1391. //
  1392. // no parameter table
  1393. //
  1394. break;
  1395. case LLC_DLC_CLOSE_STATION:
  1396. IF_DEBUG(DLC) {
  1397. DPUT("LLC_DLC_CLOSE_STATION\n");
  1398. }
  1399. //
  1400. // no parameter table
  1401. //
  1402. break;
  1403. case LLC_DLC_CONNECT_STATION:
  1404. IF_DEBUG(DLC) {
  1405. DPUT("LLC_DLC_CONNECT_STATION\n");
  1406. }
  1407. pParms->DlcConnectStation.pRoutingInfo = DOS_PTR_TO_FLAT(pParms->DlcConnectStation.pRoutingInfo);
  1408. break;
  1409. case LLC_DLC_FLOW_CONTROL:
  1410. IF_DEBUG(DLC) {
  1411. DPUT("LLC_DLC_FLOW_CONTROL\n");
  1412. }
  1413. //
  1414. // if we are resetting the local-busy(buffer) state then we clear the
  1415. // emulated state. When all deferred I-Frames are indicated to the app
  1416. // the real local-busy(buffer) state will be reset in the driver
  1417. //
  1418. // If we don't think the indicated link station is in emulated local
  1419. // busy(buffer) state then let the driver handle the request. If we
  1420. // reset the emulated state then return success
  1421. //
  1422. if ((LOBYTE(pDosCcb->u.dlc.usParameter) & LLC_RESET_LOCAL_BUSY_BUFFER) == LLC_RESET_LOCAL_BUSY_BUFFER) {
  1423. if (ResetEmulatedLocalBusyState(adapter, pDosCcb->u.dlc.usStationId, LLC_DLC_FLOW_CONTROL)) {
  1424. return LLC_STATUS_SUCCESS;
  1425. } else {
  1426. IF_DEBUG(DLC) {
  1427. DPUT2("MapDosCommandsToNt: ERROR: Adapter %d StationId %04x not in local-busy(buffer) state\n",
  1428. adapter,
  1429. pDosCcb->u.dlc.usStationId
  1430. );
  1431. }
  1432. return LLC_STATUS_SUCCESS;
  1433. }
  1434. }
  1435. //
  1436. // let AcsLan/driver handle any other type of flow control request
  1437. //
  1438. break;
  1439. case LLC_DLC_MODIFY:
  1440. IF_DEBUG(DLC) {
  1441. DPUT("LLC_DLC_MODIFY\n");
  1442. }
  1443. pParms->DlcModify.pGroupList = DOS_PTR_TO_FLAT(pParms->DlcModify.pGroupList);
  1444. break;
  1445. case LLC_DLC_OPEN_SAP:
  1446. IF_DEBUG(DLC) {
  1447. DPUT("LLC_DLC_OPEN_SAP\n");
  1448. }
  1449. //
  1450. // convert segmented group list pointer to flat-32
  1451. //
  1452. pParms->DlcOpenSap.pGroupList = DOS_PTR_TO_FLAT(pParms->DlcOpenSap.pGroupList);
  1453. //
  1454. // Initialize the DOS DLC buffer pool for the SAP station. If this fails
  1455. // return error immediately else call the NT driver to create the SAP
  1456. // proper. If that fails, then the buffer pool created here will be
  1457. // destroyed
  1458. //
  1459. Status = CreateBufferPool(
  1460. POOL_INDEX_FROM_SAP(pParms->DosDlcOpenSap.uchSapValue, adapter),
  1461. pParms->DosDlcOpenSap.dpPoolAddress,
  1462. pParms->DosDlcOpenSap.cPoolBlocks,
  1463. pParms->DosDlcOpenSap.usBufferSize
  1464. );
  1465. if (Status != LLC_STATUS_SUCCESS) {
  1466. IF_DEBUG(DLC) {
  1467. DPUT1("MapDosCommandsToNt: Couldn't create buffer pool for DLC.OPEN.SAP (%x)\n", Status);
  1468. }
  1469. return Status;
  1470. }
  1471. //
  1472. // trim the timer parameters to the range expected by the DLC driver
  1473. //
  1474. if (pParms->DlcOpenSap.uchT1 > 10) {
  1475. pParms->DlcOpenSap.uchT1 = 10;
  1476. }
  1477. if (pParms->DlcOpenSap.uchT2 > 10) {
  1478. pParms->DlcOpenSap.uchT2 = 10;
  1479. }
  1480. if (pParms->DlcOpenSap.uchTi > 10) {
  1481. pParms->DlcOpenSap.uchTi = 10;
  1482. }
  1483. break;
  1484. case LLC_DLC_OPEN_STATION:
  1485. IF_DEBUG(DLC) {
  1486. DPUT("LLC_DLC_OPEN_STATION\n");
  1487. }
  1488. pParms->DlcOpenStation.pRemoteNodeAddress = DOS_PTR_TO_FLAT(pParms->DlcOpenStation.pRemoteNodeAddress);
  1489. break;
  1490. case LLC_DLC_REALLOCATE_STATIONS:
  1491. IF_DEBUG(DLC) {
  1492. DPUT("LLC_DLC_REALLOCATE_STATIONS\n");
  1493. }
  1494. break;
  1495. case LLC_DLC_RESET:
  1496. IF_DEBUG(DLC) {
  1497. DPUT("LLC_DLC_RESET\n");
  1498. }
  1499. //
  1500. // no parameter table
  1501. //
  1502. break;
  1503. case LLC_DLC_STATISTICS:
  1504. IF_DEBUG(DLC) {
  1505. DPUT("LLC_DLC_STATISTICS\n");
  1506. }
  1507. pParms->DlcStatistics.pLogBuf = DOS_PTR_TO_FLAT(pParms->DlcStatistics.pLogBuf);
  1508. break;
  1509. //
  1510. // RECEIVE processing
  1511. //
  1512. case LLC_RECEIVE:
  1513. IF_DEBUG(DLC) {
  1514. DPUT("LLC_RECEIVE\n");
  1515. }
  1516. //
  1517. // we have to replace the DOS RECEIVE with an NT RECEIVE for 2 reasons:
  1518. // (i) NT assumes an NT RECEIVE parameter table which is longer than
  1519. // the DOS RECEIVE parameter table: if we send the DOS pointers through
  1520. // NT may write RECEIVE parameter information where we don't want it;
  1521. // (ii) NT will complete the RECEIVE with NT buffers which we need to
  1522. // convert to DOS buffers anyway in the event completion processing
  1523. //
  1524. // NOTE: we no longer chain receive frames on the SAP since this doesn't
  1525. // really improve performance because we generate the same number of VDM
  1526. // interrupts if we don't chain frames, and it just serves to complicate
  1527. // completion event processing
  1528. //
  1529. pNewCcb = (PLLC_CCB)LocalAlloc(LMEM_FIXED,
  1530. sizeof(LLC_CCB)
  1531. + sizeof(LLC_DOS_RECEIVE_PARMS_EX)
  1532. );
  1533. if (pNewCcb == NULL) {
  1534. return LLC_STATUS_NO_MEMORY;
  1535. } else {
  1536. IF_DEBUG(DLC) {
  1537. DPUT1("VrDlc5cHandler: allocated Extended RECEIVE+parms @ %08x\n", pNewCcb);
  1538. }
  1539. }
  1540. RtlCopyMemory(pNewCcb, pDosCcb, sizeof(LLC_DOS_CCB));
  1541. RtlCopyMemory((PVOID)(pNewCcb + 1), (PVOID)pParms, sizeof(LLC_DOS_RECEIVE_PARMS));
  1542. pNewCcb->hCompletionEvent = NULL;
  1543. pNewCcb->uchReserved2 = 0;
  1544. pNewCcb->uchReadFlag = 0;
  1545. pNewCcb->usReserved3 = 0;
  1546. pDosCcb = pNewCcb;
  1547. pNtParms = (PLLC_PARMS)(pNewCcb + 1);
  1548. pDosCcb->u.pParameterTable = pNtParms;
  1549. ((PLLC_DOS_RECEIVE_PARMS_EX)pNtParms)->dpOriginalCcbAddress = dpOriginalCcbAddress;
  1550. ((PLLC_DOS_RECEIVE_PARMS_EX)pNtParms)->dpCompletionFlag = 0;
  1551. dpOriginalCcbAddress = (DOS_ADDRESS)pOutputCcb = (DOS_ADDRESS)pDosCcb;
  1552. //
  1553. // point the notification flag at the extended RECEIVE CCB. This is how
  1554. // we get back to the extended RECEIVE CCB when a READ completes with a
  1555. // receive event. From this CCB pointer, we can find our way to the
  1556. // extended parameter table and from there the original DOS CCB address
  1557. // and from there the original DOS RECEIVE parameter table
  1558. //
  1559. pNtParms->Receive.ulReceiveFlag = (DWORD)dpOriginalCcbAddress;
  1560. //
  1561. // uchRcvReadOption of 0x00 means DO NOT chain received frames. DOS DLC
  1562. // cannot handle more than 1 frame at a time
  1563. //
  1564. pNtParms->Receive.uchRcvReadOption = 0;
  1565. //
  1566. // indicate, using LLC_DOS_SPECIAL_COMMAND as the completion flags, that
  1567. // this RECEIVE CCB & parameter table were allocated on behalf of the
  1568. // VDM, in this emulator, and should be freed when the command completes.
  1569. // This also indicates that the parameter table is the extended receive
  1570. // parameter table and as such contains the address of the original DOS
  1571. // CCB which we must complete with the same information which completes
  1572. // the NT RECEIVE we are about to submit
  1573. //
  1574. pNewCcb->ulCompletionFlag = LLC_DOS_SPECIAL_COMMAND;
  1575. #if DBG
  1576. //
  1577. // clear out the first-buffer field to stop the debug code displaying
  1578. // a ton of garbage if the field is left uninitialized
  1579. //
  1580. WRITE_DWORD(&pOutputCcb->u.pParms->DosReceive.pFirstBuffer, 0);
  1581. pNtParms->Receive.pFirstBuffer = NULL;
  1582. #endif
  1583. break;
  1584. case LLC_RECEIVE_CANCEL:
  1585. IF_DEBUG(DLC) {
  1586. DPUT("LLC_RECEIVE_CANCEL\n");
  1587. }
  1588. break;
  1589. case LLC_RECEIVE_MODIFY:
  1590. IF_DEBUG(DLC) {
  1591. DPUT("LLC_RECEIVE_MODIFY\n");
  1592. }
  1593. break;
  1594. //
  1595. // TRANSMIT processing. All transmit commands (7 flavours) are collapsed
  1596. // into the new TRANSMIT command
  1597. //
  1598. case LLC_TRANSMIT_DIR_FRAME:
  1599. IF_DEBUG(DLC) {
  1600. DPUT("LLC_TRANSMIT_DIR_FRAME\n");
  1601. }
  1602. FrameType = LLC_DIRECT_TRANSMIT;
  1603. goto TransmitHandling;
  1604. case LLC_TRANSMIT_I_FRAME:
  1605. IF_DEBUG(DLC) {
  1606. DPUT("LLC_TRANSMIT_I_FRAME\n");
  1607. }
  1608. FrameType = LLC_I_FRAME;
  1609. goto TransmitHandling;
  1610. case LLC_TRANSMIT_TEST_CMD:
  1611. IF_DEBUG(DLC) {
  1612. DPUT("LLC_TRANSMIT_TEST_CMD\n");
  1613. }
  1614. FrameType = LLC_TEST_COMMAND_POLL;
  1615. goto TransmitHandling;
  1616. case LLC_TRANSMIT_UI_FRAME:
  1617. IF_DEBUG(DLC) {
  1618. DPUT("LLC_TRANSMIT_UI_FRAME\n");
  1619. }
  1620. FrameType = LLC_UI_FRAME;
  1621. goto TransmitHandling;
  1622. case LLC_TRANSMIT_XID_CMD:
  1623. IF_DEBUG(DLC) {
  1624. DPUT("LLC_TRANSMIT_XID_CMD\n");
  1625. }
  1626. FrameType = LLC_XID_COMMAND_POLL;
  1627. goto TransmitHandling;
  1628. case LLC_TRANSMIT_XID_RESP_FINAL:
  1629. IF_DEBUG(DLC) {
  1630. DPUT("LLC_TRANSMIT_XID_RESP_FINAL\n");
  1631. }
  1632. FrameType = LLC_XID_RESPONSE_FINAL;
  1633. goto TransmitHandling;
  1634. case LLC_TRANSMIT_XID_RESP_NOT_FINAL:
  1635. IF_DEBUG(DLC) {
  1636. DPUT("LLC_TRANSMIT_XID_RESP_NOT_FINAL\n");
  1637. }
  1638. FrameType = LLC_XID_RESPONSE_NOT_FINAL;
  1639. TransmitHandling:
  1640. //
  1641. // Copy the DOS CCB to the input buffer, save the original DOS address
  1642. // of the CCB and fix the parameter table address (to a flat address)
  1643. // Copy the link list headers to the descriptor array and build NT CCB
  1644. //
  1645. WRITE_DWORD(&pOutputCcb->pNext, dpOriginalCcbAddress);
  1646. RtlCopyMemory((PCHAR)&NtDlcParms.Async.Ccb, (PCHAR)pOutputCcb, sizeof(NT_DLC_CCB));
  1647. NtDlcParms.Async.Ccb.u.pParameterTable = DOS_PTR_TO_FLAT(NtDlcParms.Async.Ccb.u.pParameterTable);
  1648. NtDlcParms.Async.Parms.Transmit.StationId = pParms->Transmit.usStationId;
  1649. NtDlcParms.Async.Parms.Transmit.RemoteSap = pParms->Transmit.uchRemoteSap;
  1650. NtDlcParms.Async.Parms.Transmit.XmitReadOption = LLC_CHAIN_XMIT_COMMANDS_ON_SAP;
  1651. NtDlcParms.Async.Parms.Transmit.FrameType = FrameType;
  1652. cElement = 0;
  1653. if (pParms->Transmit.pXmitQueue1) {
  1654. Status = CopyDosBuffersToDescriptorArray(
  1655. NtDlcParms.Async.Parms.Transmit.XmitBuffer,
  1656. (PLLC_XMIT_BUFFER)pParms->Transmit.pXmitQueue1,
  1657. &cElement
  1658. );
  1659. if (Status != LLC_STATUS_SUCCESS) {
  1660. return Status;
  1661. }
  1662. }
  1663. if (pParms->Transmit.pXmitQueue2) {
  1664. Status = CopyDosBuffersToDescriptorArray(
  1665. NtDlcParms.Async.Parms.Transmit.XmitBuffer,
  1666. (PLLC_XMIT_BUFFER)pParms->Transmit.pXmitQueue2,
  1667. &cElement
  1668. );
  1669. if (Status != LLC_STATUS_SUCCESS) {
  1670. return Status;
  1671. }
  1672. }
  1673. if (pParms->Transmit.cbBuffer1) {
  1674. if (cElement == MAX_TRANSMIT_SEGMENTS) {
  1675. return LLC_STATUS_TRANSMIT_ERROR;
  1676. }
  1677. NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].pBuffer = DOS_PTR_TO_FLAT(pParms->Transmit.pBuffer1);
  1678. NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].cbBuffer = pParms->Transmit.cbBuffer1;
  1679. NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].boolFreeBuffer = FALSE;
  1680. NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].eSegmentType = LLC_NEXT_DATA_SEGMENT;
  1681. cElement++;
  1682. }
  1683. if (pParms->Transmit.cbBuffer2) {
  1684. if (cElement == MAX_TRANSMIT_SEGMENTS) {
  1685. return LLC_STATUS_TRANSMIT_ERROR;
  1686. }
  1687. NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].cbBuffer = pParms->Transmit.cbBuffer2;
  1688. NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].pBuffer = DOS_PTR_TO_FLAT(pParms->Transmit.pBuffer2);
  1689. NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].boolFreeBuffer = FALSE;
  1690. NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].eSegmentType = LLC_NEXT_DATA_SEGMENT;
  1691. cElement++;
  1692. }
  1693. NtDlcParms.Async.Parms.Transmit.XmitBuffer[0].eSegmentType = LLC_FIRST_DATA_SEGMENT;
  1694. NtDlcParms.Async.Parms.Transmit.XmitBufferCount = cElement;
  1695. InputBufferSize = sizeof(LLC_TRANSMIT_DESCRIPTOR) * cElement
  1696. + sizeof(NT_DLC_TRANSMIT_PARMS)
  1697. + sizeof(NT_DLC_CCB)
  1698. - sizeof(LLC_TRANSMIT_DESCRIPTOR);
  1699. //
  1700. // We don't need return FrameCopied status, when sending
  1701. // I-frames. We save an extra memory locking, when we use
  1702. // TRANSMIT2 with the I- frames.
  1703. //
  1704. return lpDlcCallDriver((DWORD)adapter,
  1705. //(FrameType == LLC_I_FRAME)
  1706. // ? IOCTL_DLC_TRANSMIT2
  1707. // : IOCTL_DLC_TRANSMIT,
  1708. IOCTL_DLC_TRANSMIT,
  1709. &NtDlcParms,
  1710. InputBufferSize,
  1711. pOutputCcb,
  1712. sizeof(NT_DLC_CCB_OUTPUT)
  1713. );
  1714. }
  1715. //
  1716. // Call the common DLC API entry point for DOS and Windows/Nt
  1717. //
  1718. Status = DlcCallWorker((PLLC_CCB)pDosCcb, // aligned input CCB pointer
  1719. (PLLC_CCB)dpOriginalCcbAddress,
  1720. (PLLC_CCB)pOutputCcb // possibly unaligned output CCB pointer
  1721. );
  1722. IF_DEBUG(DLC) {
  1723. DPUT2("MapDosCommandsToNt: NtAcsLan returns %#x (%d)\n", Status, Status);
  1724. }
  1725. switch (pDosCcb->uchDlcCommand) {
  1726. case LLC_DIR_CLOSE_ADAPTER:
  1727. case LLC_DIR_INITIALIZE:
  1728. OpenedAdapters--;
  1729. //
  1730. // NtAcsLan converts DIR.INITIALIZE to DIR.CLOSE.ADAPTER. The former
  1731. // completes "in the workstation", whereas the latter completes
  1732. // asynchronously. Interpret LLC_STATUS_PENDING as LLC_STATUS_SUCCESS
  1733. // in this case, otherwise we may not fully uninitialize the adapter
  1734. //
  1735. if (Status == LLC_STATUS_SUCCESS || Status == LLC_STATUS_PENDING) {
  1736. //
  1737. // We may free all virtual memory in NT buffer pool
  1738. //
  1739. Adapters[adapter].IsOpen = FALSE;
  1740. LocalFree(Adapters[adapter].BufferPool);
  1741. IF_DEBUG(DLC_ALLOC) {
  1742. DPUT1("FREE: freed block @ %x\n", Adapters[adapter].BufferPool);
  1743. }
  1744. //
  1745. // Delete all DOS buffer pools
  1746. //
  1747. for (i = 0; i <= 0xfe00; i += 0x0200) {
  1748. DeleteBufferPool(GET_POOL_INDEX(adapter, i));
  1749. }
  1750. //
  1751. // closing the adapter also closed the direct station
  1752. //
  1753. Adapters[adapter].DirectStationOpen = FALSE;
  1754. //
  1755. // clear the stored ADAPTER_PARMS and DLC_PARMS
  1756. //
  1757. RtlZeroMemory(&Adapters[adapter].AdapterParms, sizeof(ADAPTER_PARMS));
  1758. RtlZeroMemory(&Adapters[adapter].DlcParms, sizeof(DLC_PARMS));
  1759. if (pDosCcb->uchDlcCommand == LLC_DIR_INITIALIZE) {
  1760. Status = LLC_STATUS_SUCCESS;
  1761. }
  1762. }
  1763. break;
  1764. case LLC_DIR_OPEN_ADAPTER:
  1765. if (Status != LLC_STATUS_SUCCESS) {
  1766. break;
  1767. }
  1768. //
  1769. // Initialize the adapter support software
  1770. //
  1771. Status = InitializeAdapterSupport(adapter, pDirectParms);
  1772. //
  1773. // if we allocated the direct station buffer ok then perform the
  1774. // rest of the open request - open the direct station, add any
  1775. // group or functional addresses specified and set the ADAPTER_PARMS
  1776. // and DLC_PARMS default values in the DOS_ADAPTER structure
  1777. //
  1778. if (Status == LLC_STATUS_SUCCESS) {
  1779. //
  1780. // open the direct station
  1781. //
  1782. Status = OpenDirectStation(adapter);
  1783. if (Status == LLC_STATUS_SUCCESS) {
  1784. //
  1785. // add the group address
  1786. //
  1787. if (groupAddress) {
  1788. Status = LlcCommand(adapter,
  1789. LLC_DIR_SET_GROUP_ADDRESS,
  1790. groupAddress
  1791. );
  1792. } else IF_DEBUG(DLC) {
  1793. DPUT1("Error: couldn't set group address: %02x\n", Status);
  1794. }
  1795. if (Status == LLC_STATUS_SUCCESS) {
  1796. //
  1797. // add the functional address
  1798. //
  1799. if (functionalAddress) {
  1800. Status = LlcCommand(adapter,
  1801. LLC_DIR_SET_FUNCTIONAL_ADDRESS,
  1802. functionalAddress
  1803. );
  1804. }
  1805. } else IF_DEBUG(DLC) {
  1806. DPUT1("Error: couldn't set functional address: %02x\n", Status);
  1807. }
  1808. } else IF_DEBUG(DLC) {
  1809. DPUT1("Error: could open Direct Station: %02x\n", Status);
  1810. }
  1811. }
  1812. //
  1813. // copy the returned default information to the adapter structure if
  1814. // we successfully managed to open the direct station and add the
  1815. // group and functional addresses (if specified)
  1816. //
  1817. if (Status == LLC_STATUS_SUCCESS) {
  1818. RtlCopyMemory(&Adapters[adapter].AdapterParms,
  1819. pParms->DirOpenAdapter.pAdapterParms,
  1820. sizeof(ADAPTER_PARMS)
  1821. );
  1822. if (pParms->DirOpenAdapter.pDlcParms) {
  1823. RtlCopyMemory(&Adapters[adapter].DlcParms,
  1824. pParms->DirOpenAdapter.pDlcParms,
  1825. sizeof(DLC_PARMS)
  1826. );
  1827. }
  1828. } else {
  1829. //
  1830. // yoiks! something failed - close the direct station (if its
  1831. // open, close the adapter and fail the request)
  1832. //
  1833. if (Adapters[adapter].DirectStationOpen) {
  1834. CloseDirectStation(adapter);
  1835. }
  1836. CloseAdapter(adapter);
  1837. }
  1838. break;
  1839. case LLC_DLC_CLOSE_SAP:
  1840. case LLC_DLC_CLOSE_STATION:
  1841. //
  1842. // Delete the buffer pools of the closed or closing station.
  1843. // It does not matter, if the close operation is still pending,
  1844. // because the pending operations should always succeed
  1845. //
  1846. if (Status == LLC_STATUS_SUCCESS || Status == LLC_STATUS_PENDING) {
  1847. DeleteBufferPool(GET_POOL_INDEX(adapter, pDosCcb->u.dlc.usStationId));
  1848. //
  1849. // DLC.SYS returns a pointer to the NT RECEIVE CCB for this SAP.
  1850. // Change the pointer to the DOS RECEIVE CCB
  1851. //
  1852. if (Status == LLC_STATUS_SUCCESS || !pDosCcb->ulCompletionFlag) {
  1853. PLLC_CCB pNtReceive;
  1854. PLLC_DOS_RECEIVE_PARMS_EX pNtReceiveParms;
  1855. pNtReceive = (PLLC_CCB)READ_DWORD(&pOutputCcb->pNext);
  1856. if (pNtReceive) {
  1857. pNtReceiveParms = (PLLC_DOS_RECEIVE_PARMS_EX)(pNtReceive->u.pParameterTable);
  1858. WRITE_FAR_POINTER(&pOutputCcb->pNext, pNtReceiveParms->dpOriginalCcbAddress);
  1859. //
  1860. // free the NT RECEIVE CCB we allocated (see LLC_RECEIVE above)
  1861. //
  1862. ASSERT(pNtReceive->ulCompletionFlag == LLC_DOS_SPECIAL_COMMAND);
  1863. LocalFree((HLOCAL)pNtReceive);
  1864. IF_DEBUG(DLC) {
  1865. DPUT1("VrDlc5cHandler: freed Extended RECEIVE+parms @ %08x\n", pNtReceive);
  1866. }
  1867. }
  1868. }
  1869. }
  1870. break;
  1871. case LLC_DLC_OPEN_SAP:
  1872. //
  1873. // delete the buffer pool, if the open SAP command failed
  1874. //
  1875. if (Status != LLC_STATUS_SUCCESS) {
  1876. DeleteBufferPool(GET_POOL_INDEX(adapter, pParms->DlcOpenSap.usStationId));
  1877. } else {
  1878. //
  1879. // record the DLC status change appendage for this SAP
  1880. //
  1881. Adapters[ adapter ].DlcStatusChangeAppendage
  1882. [ SAP_ID(pParms->DlcOpenSap.usStationId) ]
  1883. = pParms->DlcOpenSap.DlcStatusFlags;
  1884. //
  1885. // and user value
  1886. //
  1887. Adapters[ adapter ].UserStatusValue
  1888. [ SAP_ID(pParms->DlcOpenSap.usStationId) ]
  1889. = pParms->DlcOpenSap.usUserStatValue;
  1890. }
  1891. break;
  1892. case LLC_DLC_RESET:
  1893. //
  1894. // Delete the reset sap buffer pool,
  1895. // or all sap buffer pools. We don't need to care about
  1896. // the possible error codes, because this can fail only
  1897. // if the given sap station does not exist any more =>
  1898. // it does not matter, if we reset it again.
  1899. //
  1900. if (pDosCcb->u.dlc.usStationId != 0) {
  1901. DeleteBufferPool(GET_POOL_INDEX(adapter, pDosCcb->u.dlc.usStationId));
  1902. } else {
  1903. int sapNumber;
  1904. //
  1905. // Close all SAP stations (0x0200 - 0xfe00). SAP number goes up in
  1906. // increments of 2 since bit 0 is ignored (ie SAP 2 == SAP 3 etc)
  1907. //
  1908. for (sapNumber = 2; sapNumber <= 0xfe; sapNumber += 2) {
  1909. DeleteBufferPool(POOL_INDEX_FROM_SAP(sapNumber, adapter));
  1910. }
  1911. }
  1912. break;
  1913. }
  1914. return Status;
  1915. }
  1916. VOID
  1917. CompleteCcbProcessing(
  1918. IN LLC_STATUS Status,
  1919. IN LLC_DOS_CCB UNALIGNED * pCcb,
  1920. IN PLLC_PARMS pNtParms
  1921. )
  1922. /*++
  1923. Routine Description:
  1924. Performs any CCB completion processing. Processing can be called either
  1925. when the CCB completes synchronously, or asynchronously. Processing is
  1926. typically to fill in parts of the DOS CCB or parameter table
  1927. Arguments:
  1928. Status - of the request
  1929. pCcb - pointer to DOS CCB to complete (flat 32-bit pointer to DOS memory)
  1930. pNtParms- pointer to NT parameter table
  1931. Return Value:
  1932. None.
  1933. --*/
  1934. {
  1935. LLC_DOS_PARMS UNALIGNED * pDosParms = READ_FAR_POINTER(&pCcb->u.pParms);
  1936. BYTE adapter = pCcb->uchAdapterNumber;
  1937. IF_DEBUG(DLC) {
  1938. DPUT("CompleteCcbProcessing\n");
  1939. }
  1940. switch (pCcb->uchDlcCommand) {
  1941. case LLC_DIR_OPEN_ADAPTER:
  1942. //
  1943. // this command is unique in that it has a parameter table which points
  1944. // to up to 4 other parameter tables. The following values are output:
  1945. //
  1946. // ADAPTER_PARMS
  1947. // OPEN_ERROR_CODE
  1948. // NODE_ADDRESS
  1949. //
  1950. // DIRECT_PARMS
  1951. // WORK_LEN_ACT
  1952. //
  1953. // DLC_PARMS
  1954. // None for CCB1
  1955. //
  1956. // NCB_PARMS
  1957. // Not accessed
  1958. //
  1959. //
  1960. // we only copy the info if the command succeeded (we may have garbage
  1961. // table pointers otherwise). It is also OK to copy the information if
  1962. // the adapter is already open and the caller requested that we pass
  1963. // the default information back
  1964. //
  1965. if (Status == LLC_STATUS_SUCCESS || Status == LLC_STATUS_ADAPTER_OPEN) {
  1966. PLLC_DOS_DIR_OPEN_ADAPTER_PARMS pOpenAdapterParms = (PLLC_DOS_DIR_OPEN_ADAPTER_PARMS)pDosParms;
  1967. PADAPTER_PARMS pAdapterParms = READ_FAR_POINTER(&pOpenAdapterParms->pAdapterParms);
  1968. PDIRECT_PARMS pDirectParms = READ_FAR_POINTER(&pOpenAdapterParms->pDirectParms);
  1969. PDLC_PARMS pDlcParms = READ_FAR_POINTER(&pOpenAdapterParms->pDlcParms);
  1970. //
  1971. // if we got an error and the caller didn't request the original
  1972. // open parameters, then skip out
  1973. //
  1974. if (Status == LLC_STATUS_ADAPTER_OPEN && !(pAdapterParms->OpenOptions & 0x200)) {
  1975. break;
  1976. }
  1977. WRITE_WORD(&pAdapterParms->OpenErrorCode, pNtParms->DirOpenAdapter.pAdapterParms->usOpenErrorCode);
  1978. RtlCopyMemory(&pAdapterParms->NodeAddress,
  1979. pNtParms->DirOpenAdapter.pAdapterParms->auchNodeAddress,
  1980. sizeof(pAdapterParms->NodeAddress)
  1981. );
  1982. //
  1983. // direct parms are not returned from NT DLC, so we just copy the
  1984. // requested work area size to the actual
  1985. //
  1986. WRITE_WORD(&pDirectParms->AdapterWorkAreaActual,
  1987. READ_WORD(&pDirectParms->AdapterWorkAreaRequested)
  1988. );
  1989. //
  1990. // copy the entire DLC_PARMS structure from the DOS_ADAPTER structure
  1991. //
  1992. if (pDlcParms) {
  1993. RtlCopyMemory(pDlcParms, &Adapters[adapter].DlcParms, sizeof(*pDlcParms));
  1994. }
  1995. }
  1996. break;
  1997. case LLC_DIR_STATUS:
  1998. //
  1999. // copy the common areas from the 32-bit parameter table to 16-bit table
  2000. // This copies up to the adapter parameters address
  2001. //
  2002. RtlCopyMemory(pDosParms, pNtParms, (DWORD)&((PDOS_DIR_STATUS_PARMS)0)->dpAdapterParmsAddr);
  2003. //
  2004. // fill in the other fields as best we can
  2005. //
  2006. RtlZeroMemory(pDosParms->DosDirStatus.auchMicroCodeLevel,
  2007. sizeof(pDosParms->DosDirStatus.auchMicroCodeLevel)
  2008. );
  2009. WRITE_DWORD(&pDosParms->DosDirStatus.dpAdapterMacAddr, 0);
  2010. WRITE_DWORD(&pDosParms->DosDirStatus.dpTimerTick, 0);
  2011. WRITE_WORD(&pDosParms->DosDirStatus.usLastNetworkStatus,
  2012. Adapters[adapter].LastNetworkStatusChange
  2013. );
  2014. //
  2015. // If the app has requested we return the extended parameter table, then
  2016. // fill it in with reasonable values if we can. There is one table per
  2017. // adapter, statically allocated in the real-mode redir TSR
  2018. //
  2019. if (pDosParms->DosDirStatus.uchAdapterConfig & 0x20) {
  2020. //
  2021. // Ethernet type uses different bit in DOS and Nt (or OS/2)
  2022. //
  2023. lpVdmWindow->aExtendedStatus[adapter].cbSize = sizeof(EXTENDED_STATUS_PARMS);
  2024. //
  2025. // if adapter type as reported by NtAcsLan is Ethernet (0x100), set
  2026. // adapter type in extended status table to Ethernet (0x10), else
  2027. // record whatever NtAcsLan gave us
  2028. //
  2029. if (pNtParms->DirStatus.usAdapterType & 0x100) {
  2030. WRITE_WORD(&lpVdmWindow->aExtendedStatus[adapter].wAdapterType,
  2031. 0x0010
  2032. );
  2033. lpVdmWindow->aExtendedStatus[adapter].cbPageFrameSize = 0;
  2034. WRITE_WORD(&lpVdmWindow->aExtendedStatus[adapter].wCurrentFrameSize,
  2035. 0
  2036. );
  2037. WRITE_WORD(&lpVdmWindow->aExtendedStatus[adapter].wMaxFrameSize,
  2038. 0
  2039. );
  2040. } else {
  2041. WRITE_WORD(&lpVdmWindow->aExtendedStatus[adapter].wAdapterType,
  2042. pNtParms->DirStatus.usAdapterType
  2043. );
  2044. //
  2045. // set the TR page frame size (KBytes)
  2046. //
  2047. lpVdmWindow->aExtendedStatus[adapter].cbPageFrameSize = 16;
  2048. //
  2049. // set the current and maximum DHB sizes for TR cards
  2050. //
  2051. WRITE_WORD(&lpVdmWindow->aExtendedStatus[adapter].wCurrentFrameSize,
  2052. (WORD)pNtParms->DirStatus.ulMaxFrameLength
  2053. );
  2054. WRITE_WORD(&lpVdmWindow->aExtendedStatus[adapter].wMaxFrameSize,
  2055. (WORD)pNtParms->DirStatus.ulMaxFrameLength
  2056. );
  2057. }
  2058. //
  2059. // record the address of the extended parameter table in the
  2060. // DIR.STATUS parameter table
  2061. //
  2062. WRITE_DWORD(&pDosParms->DosDirStatus.dpExtendedParms,
  2063. NEW_DOS_ADDRESS(dpVdmWindow, &lpVdmWindow->aExtendedStatus[adapter])
  2064. );
  2065. } else {
  2066. //
  2067. // no extended parameters requested
  2068. //
  2069. WRITE_DWORD(&pDosParms->DosDirStatus.dpExtendedParms, 0);
  2070. }
  2071. //
  2072. // return the tick counter. We don't currently update the tick counter
  2073. //
  2074. WRITE_DWORD(&pDosParms->DosDirStatus.dpTimerTick,
  2075. NEW_DOS_ADDRESS(dpVdmWindow, &lpVdmWindow->dwDlcTimerTick)
  2076. );
  2077. //
  2078. // always return a pointer to the extended adapter parameter table we
  2079. // now keep in DOS memory. We currently always zero this table. It
  2080. // would normally be maintained by the adapter (MAC) software
  2081. //
  2082. WRITE_DWORD(&pDosParms->DosDirStatus.dpAdapterParmsAddr,
  2083. NEW_DOS_ADDRESS(dpVdmWindow, &lpVdmWindow->AdapterStatusParms[adapter])
  2084. );
  2085. RtlZeroMemory(&lpVdmWindow->AdapterStatusParms[adapter],
  2086. sizeof(lpVdmWindow->AdapterStatusParms[adapter])
  2087. );
  2088. break;
  2089. case LLC_DLC_OPEN_SAP:
  2090. //
  2091. // STATION_ID is only output value
  2092. //
  2093. WRITE_WORD(&pDosParms->DlcOpenSap.usStationId, pNtParms->DlcOpenSap.usStationId);
  2094. break;
  2095. case LLC_DLC_OPEN_STATION:
  2096. //
  2097. // LINK_STATION_ID is only output value
  2098. //
  2099. WRITE_WORD(&pDosParms->DlcOpenStation.usLinkStationId, pNtParms->DlcOpenStation.usLinkStationId);
  2100. break;
  2101. case LLC_DLC_STATISTICS:
  2102. break;
  2103. }
  2104. }
  2105. LLC_STATUS
  2106. InitializeAdapterSupport(
  2107. IN UCHAR AdapterNumber,
  2108. IN DOS_DLC_DIRECT_PARMS UNALIGNED * pDirectParms OPTIONAL
  2109. )
  2110. /*++
  2111. Routine Description:
  2112. The function initializes the buffer pool for the new adapter opened by
  2113. DOS DLC
  2114. Arguments:
  2115. AdapterNumber - which adapter to initialize the buffer pool for
  2116. pDirectParms - Direct station parameter table, not used in NT, but
  2117. optional in DOS
  2118. Return Value:
  2119. LLC_STATUS
  2120. LLC_NO_RESOURCES
  2121. --*/
  2122. {
  2123. LLC_STATUS Status;
  2124. HANDLE hBufferPool;
  2125. IF_DEBUG(DLC) {
  2126. DPUT("InitializeAdapterSupport\n");
  2127. }
  2128. //
  2129. // Check if the global DLL initialization has already been done. This is not
  2130. // done in global DLL init because there is no reason to start an extra
  2131. // thread if DLC is not used. If this succeeds then the asynchronous event
  2132. // handler thread will be waiting on a list of 2 events - one for each
  2133. // adapter. We need to submit a READ CCB for this adapter
  2134. //
  2135. Status = VrDlcInit();
  2136. if (Status != LLC_STATUS_SUCCESS) {
  2137. return Status;
  2138. } else if (InitiateRead(AdapterNumber, &Status) == NULL) {
  2139. return Status;
  2140. }
  2141. OpenedAdapters++;
  2142. //
  2143. // mark the adapter as being opened and get the media type/class
  2144. //
  2145. Adapters[AdapterNumber].IsOpen = TRUE;
  2146. Adapters[AdapterNumber].AdapterType = GetAdapterType(AdapterNumber);
  2147. //
  2148. // Create the DLC buffer pool for the new adapter. DLC driver will
  2149. // deallocate the buffer pool in the DIR.CLOSE.ADAPTER or when
  2150. // the MVDM process makes a process exit
  2151. //
  2152. Adapters[AdapterNumber].BufferPool = (PVOID)LocalAlloc(LMEM_FIXED, DOS_DLC_BUFFER_POOL_SIZE);
  2153. if (Adapters[AdapterNumber].BufferPool == NULL) {
  2154. Status = LLC_STATUS_NO_MEMORY;
  2155. goto ErrorHandler;
  2156. }
  2157. Status = BufferCreate(AdapterNumber,
  2158. Adapters[AdapterNumber].BufferPool,
  2159. DOS_DLC_BUFFER_POOL_SIZE,
  2160. DOS_DLC_MIN_FREE_THRESHOLD,
  2161. &hBufferPool
  2162. );
  2163. if (Status != LLC_STATUS_SUCCESS) {
  2164. goto ErrorHandler;
  2165. }
  2166. if (ARGUMENT_PRESENT(pDirectParms)) {
  2167. //
  2168. // create a buffer pool for the direct station (SAP 0). This allows
  2169. // us to receive MAC and non-MAC frames sent to the direct station
  2170. // without having to purposefully allocate a buffer
  2171. //
  2172. Status = CreateBufferPool(GET_POOL_INDEX(AdapterNumber, 0),
  2173. pDirectParms->dpPoolAddress,
  2174. pDirectParms->cPoolBlocks,
  2175. pDirectParms->usBufferSize
  2176. );
  2177. if (Status != LLC_STATUS_SUCCESS) {
  2178. goto ErrorHandler;
  2179. }
  2180. SaveExceptions(AdapterNumber,
  2181. (LPDWORD)&pDirectParms->dpAdapterCheckExit
  2182. );
  2183. Status = SetExceptionFlags(AdapterNumber,
  2184. (DWORD)pDirectParms->dpAdapterCheckExit,
  2185. (DWORD)pDirectParms->dpNetworkStatusExit,
  2186. (DWORD)pDirectParms->dpPcErrorExit,
  2187. 0
  2188. );
  2189. if (Status != LLC_STATUS_SUCCESS) {
  2190. goto ErrorHandler;
  2191. }
  2192. }
  2193. IF_DEBUG(DLC) {
  2194. DPUT("InitializeAdapterSupport: returning success\n");
  2195. }
  2196. return LLC_STATUS_SUCCESS;
  2197. ErrorHandler:
  2198. //
  2199. // The open failed. We must close the adapter, but we don't care about the
  2200. // result. This must succeed
  2201. //
  2202. if (Adapters[AdapterNumber].BufferPool != NULL) {
  2203. LocalFree(Adapters[AdapterNumber].BufferPool);
  2204. IF_DEBUG(DLC_ALLOC) {
  2205. DPUT1("FREE: freed block @ %x\n", Adapters[AdapterNumber].BufferPool);
  2206. }
  2207. }
  2208. CloseAdapter(AdapterNumber);
  2209. Adapters[AdapterNumber].IsOpen = FALSE;
  2210. //
  2211. // this is probably not the right error code to return under these
  2212. // circumstances, but we'll keep it until something better comes along
  2213. //
  2214. IF_DEBUG(DLC) {
  2215. DPUT("InitializeAdapterSupport: returning FAILURE\n");
  2216. }
  2217. return LLC_STATUS_ADAPTER_NOT_INITIALIZED;
  2218. }
  2219. VOID
  2220. SaveExceptions(
  2221. IN UCHAR AdapterNumber,
  2222. IN LPDWORD pulExceptionFlags
  2223. )
  2224. /*++
  2225. Routine Description:
  2226. Procedure saves the current exception handlers
  2227. and copies new current values on the old ones.
  2228. Arguments:
  2229. IN UCHAR AdapterNumber - current adapter
  2230. IN LPDWORD pulExceptionFlags - 3 dos exception handlers in the arrays
  2231. Return Value:
  2232. None
  2233. --*/
  2234. {
  2235. IF_DEBUG(DLC) {
  2236. DPUT("SaveExceptions\n");
  2237. }
  2238. RtlCopyMemory(Adapters[AdapterNumber].PreviousExceptionHandlers,
  2239. Adapters[AdapterNumber].CurrentExceptionHandlers,
  2240. sizeof(Adapters[AdapterNumber].PreviousExceptionHandlers)
  2241. );
  2242. RtlCopyMemory(Adapters[AdapterNumber].CurrentExceptionHandlers,
  2243. pulExceptionFlags,
  2244. sizeof(Adapters[AdapterNumber].CurrentExceptionHandlers)
  2245. );
  2246. }
  2247. LPDWORD
  2248. RestoreExceptions(
  2249. IN UCHAR AdapterNumber
  2250. )
  2251. /*++
  2252. Routine Description:
  2253. Procedure restores the previous exception handlers
  2254. and returns the their address.
  2255. Arguments:
  2256. IN UCHAR AdapterNumber - current adapter
  2257. Return Value:
  2258. None
  2259. --*/
  2260. {
  2261. IF_DEBUG(DLC) {
  2262. DPUT("RestoreExceptions\n");
  2263. }
  2264. RtlCopyMemory(Adapters[AdapterNumber].CurrentExceptionHandlers,
  2265. Adapters[AdapterNumber].PreviousExceptionHandlers,
  2266. sizeof(Adapters[AdapterNumber].CurrentExceptionHandlers)
  2267. );
  2268. return Adapters[AdapterNumber].CurrentExceptionHandlers;
  2269. }
  2270. LLC_STATUS
  2271. CopyDosBuffersToDescriptorArray(
  2272. IN OUT PLLC_TRANSMIT_DESCRIPTOR pDescriptors,
  2273. IN PLLC_XMIT_BUFFER pDlcBufferQueue,
  2274. IN OUT LPDWORD pIndex
  2275. )
  2276. /*++
  2277. Routine Description:
  2278. The routine copies DOS transmit buffers to a Nt Transmit
  2279. descriptor array. All DOS pointers must be mapped to the flat
  2280. 32-bit address space. Any data in the parameter table may be
  2281. unaligned.
  2282. Arguments:
  2283. pDescriptors - current descriptor array
  2284. pDlcBufferQueue - DOS transmit buffer queue
  2285. pIndex - current index in the descriptor array
  2286. Return Value:
  2287. LLC_STATUS
  2288. --*/
  2289. {
  2290. PLLC_XMIT_BUFFER pBuffer;
  2291. DWORD Index = *pIndex;
  2292. DWORD i = 0;
  2293. DWORD DlcStatus = LLC_STATUS_SUCCESS;
  2294. WORD cbBuffer;
  2295. IF_DEBUG(DLC) {
  2296. DPUT("CopyDosBuffersToDescriptorArray\n");
  2297. }
  2298. while (pDlcBufferQueue) {
  2299. pBuffer = (PLLC_XMIT_BUFFER)DOS_PTR_TO_FLAT(pDlcBufferQueue);
  2300. //
  2301. // Check the overflow of the internal xmit buffer in stack and
  2302. // the loop counter, that prevents the forever loop of zero length
  2303. // transmit buffer (the buffer chain might be circular)
  2304. //
  2305. if (Index >= MAX_TRANSMIT_SEGMENTS || i > 60000) {
  2306. DlcStatus = LLC_STATUS_TRANSMIT_ERROR;
  2307. break;
  2308. }
  2309. if ((cbBuffer = READ_WORD(&pBuffer->cbBuffer)) != 0) {
  2310. pDescriptors[Index].pBuffer = (PUCHAR)(pBuffer->auchData)
  2311. + READ_WORD(&pBuffer->cbUserData);
  2312. pDescriptors[Index].cbBuffer = cbBuffer;
  2313. pDescriptors[Index].eSegmentType = LLC_NEXT_DATA_SEGMENT;
  2314. pDescriptors[Index].boolFreeBuffer = FALSE;
  2315. Index++;
  2316. }
  2317. i++;
  2318. pDlcBufferQueue = (PLLC_XMIT_BUFFER)READ_DWORD(&pBuffer->pNext);
  2319. }
  2320. *pIndex = Index;
  2321. return DlcStatus;
  2322. }
  2323. LLC_STATUS
  2324. BufferCreate(
  2325. IN UCHAR AdapterNumber,
  2326. IN PVOID pVirtualMemoryBuffer,
  2327. IN DWORD ulVirtualMemorySize,
  2328. IN DWORD ulMinFreeSizeThreshold,
  2329. OUT HANDLE* phBufferPoolHandle
  2330. )
  2331. /*++
  2332. Routine Description:
  2333. Function creates a Windows/Nt DLC buffer pool.
  2334. THIS COMMAND COMPLETES SYNCHRONOUSLY
  2335. Arguments:
  2336. AdapterNumber -
  2337. pVirtualMemoryBuffer - pointer to a virtual memory
  2338. ulVirtualMemorySize - size of all available buffer pool space
  2339. ulMinFreeSizeThreshold - locks more pages when this is exceeded
  2340. phBufferPoolHandle -
  2341. Return Value:
  2342. LLC_STATUS
  2343. --*/
  2344. {
  2345. LLC_CCB ccb;
  2346. LLC_BUFFER_CREATE_PARMS BufferCreate;
  2347. LLC_STATUS status;
  2348. IF_DEBUG(DLC) {
  2349. DPUT("BufferCreate\n");
  2350. }
  2351. InitializeCcb(&ccb, AdapterNumber, LLC_BUFFER_CREATE, &BufferCreate);
  2352. BufferCreate.pBuffer = pVirtualMemoryBuffer;
  2353. BufferCreate.cbBufferSize = ulVirtualMemorySize;
  2354. BufferCreate.cbMinimumSizeThreshold = ulMinFreeSizeThreshold;
  2355. status = lpAcsLan(&ccb, NULL);
  2356. *phBufferPoolHandle = BufferCreate.hBufferPool;
  2357. IF_DEBUG(DLC) {
  2358. DPUT2("BufferCreate: returning %#x (%d)\n", status, status);
  2359. }
  2360. return DLC_ERROR_STATUS(status, ccb.uchDlcStatus);
  2361. }
  2362. LLC_STATUS
  2363. SetExceptionFlags(
  2364. IN UCHAR AdapterNumber,
  2365. IN DWORD ulAdapterCheckFlag,
  2366. IN DWORD ulNetworkStatusFlag,
  2367. IN DWORD ulPcErrorFlag,
  2368. IN DWORD ulSystemActionFlag
  2369. )
  2370. /*++
  2371. Routine Description:
  2372. Sets the new appendage addresses
  2373. THIS COMMAND COMPLETES SYNCHRONOUSLY
  2374. Arguments:
  2375. AdapterNumber -
  2376. ulAdapterCheckFlag -
  2377. ulNetworkStatusFlag -
  2378. ulPcErrorFlag -
  2379. ulSystemActionFlag -
  2380. Return Value:
  2381. LLC_STATUS
  2382. --*/
  2383. {
  2384. LLC_CCB ccb;
  2385. LLC_STATUS status;
  2386. LLC_DIR_SET_EFLAG_PARMS DirSetFlags;
  2387. IF_DEBUG(DLC) {
  2388. DPUT("SetExceptionFlags\n");
  2389. }
  2390. InitializeCcb(&ccb, AdapterNumber, LLC_DIR_SET_EXCEPTION_FLAGS, &DirSetFlags);
  2391. DirSetFlags.ulAdapterCheckFlag = ulAdapterCheckFlag;
  2392. DirSetFlags.ulNetworkStatusFlag = ulNetworkStatusFlag;
  2393. DirSetFlags.ulPcErrorFlag = ulPcErrorFlag;
  2394. DirSetFlags.ulSystemActionFlag = ulSystemActionFlag;
  2395. status = lpAcsLan(&ccb, NULL);
  2396. return DLC_ERROR_STATUS(status, ccb.uchDlcStatus);
  2397. }
  2398. LLC_STATUS
  2399. LlcCommand(
  2400. IN UCHAR AdapterNumber,
  2401. IN UCHAR Command,
  2402. IN DWORD Parameter
  2403. )
  2404. /*++
  2405. Routine Description:
  2406. Calls the ACSLAN DLL to perform a DLC request which takes no parameter
  2407. table, but which takes parameters in byte, word or dword form in the CCB
  2408. COMMANDS USING THIS ROUTINE MUST COMPLETE SYNCHRONOUSLY
  2409. Arguments:
  2410. AdapterNumber - which adapter to perform command for
  2411. Command - which DLC command to perform. Currently, commands are:
  2412. DIR.SET.GROUP.ADDRESS
  2413. DIR.SET.FUNCTIONAL.ADDRESS
  2414. DLC.FLOW.CONTROL
  2415. RECEIVE.CANCEL
  2416. Parameter - the associated command
  2417. Return Value:
  2418. DWORD
  2419. --*/
  2420. {
  2421. LLC_CCB ccb;
  2422. LLC_STATUS status;
  2423. IF_DEBUG(DLC) {
  2424. DPUT3("LlcCommand(%d, %02x, %08x)\n", AdapterNumber, Command, Parameter);
  2425. }
  2426. InitializeCcb2(&ccb, AdapterNumber, Command);
  2427. ccb.u.ulParameter = Parameter;
  2428. status = lpAcsLan(&ccb, NULL);
  2429. return DLC_ERROR_STATUS(status, ccb.uchDlcStatus);
  2430. }
  2431. LLC_STATUS
  2432. OpenAdapter(
  2433. IN UCHAR AdapterNumber
  2434. )
  2435. /*++
  2436. Routine Description:
  2437. Opens a DLC adapter context for a Windows/Nt VDM
  2438. THIS COMMAND COMPLETES SYNCHRONOUSLY
  2439. Arguments:
  2440. AdapterNumber - which adapter to open
  2441. Return Value:
  2442. LLC_STATUS
  2443. --*/
  2444. {
  2445. LLC_CCB Ccb;
  2446. LLC_DIR_OPEN_ADAPTER_PARMS DirOpenAdapter;
  2447. LLC_ADAPTER_OPEN_PARMS AdapterParms;
  2448. LLC_EXTENDED_ADAPTER_PARMS ExtendedParms;
  2449. LLC_DLC_PARMS DlcParms;
  2450. LLC_STATUS status;
  2451. IF_DEBUG(DLC) {
  2452. DPUT1("OpenAdapter(AdapterNumber=%d)\n", AdapterNumber);
  2453. }
  2454. InitializeCcb(&Ccb, AdapterNumber, LLC_DIR_OPEN_ADAPTER, &DirOpenAdapter);
  2455. DirOpenAdapter.pAdapterParms = &AdapterParms;
  2456. DirOpenAdapter.pExtendedParms = &ExtendedParms;
  2457. DirOpenAdapter.pDlcParms = &DlcParms;
  2458. ExtendedParms.hBufferPool = NULL;
  2459. ExtendedParms.pSecurityDescriptor = NULL;
  2460. ExtendedParms.LlcEthernetType = LLC_ETHERNET_TYPE_DEFAULT;
  2461. RtlZeroMemory(&AdapterParms, sizeof(AdapterParms));
  2462. RtlZeroMemory(&DlcParms, sizeof(DlcParms));
  2463. status = lpAcsLan(&Ccb, NULL);
  2464. if (status == LLC_STATUS_SUCCESS) {
  2465. //
  2466. // get the adapter media type/class
  2467. //
  2468. Adapters[AdapterNumber].AdapterType = GetAdapterType(AdapterNumber);
  2469. //
  2470. // mark the adapter structure as open
  2471. //
  2472. Adapters[AdapterNumber].IsOpen = TRUE;
  2473. //
  2474. // fill in the DOS ADAPTER_PARMS and DLC_PARMS structures with any
  2475. // returned values
  2476. //
  2477. RtlCopyMemory(&Adapters[AdapterNumber].AdapterParms,
  2478. &AdapterParms,
  2479. sizeof(ADAPTER_PARMS)
  2480. );
  2481. RtlCopyMemory(&Adapters[AdapterNumber].DlcParms,
  2482. &DlcParms,
  2483. sizeof(DLC_PARMS)
  2484. );
  2485. Adapters[AdapterNumber].DlcSpecified = TRUE;
  2486. }
  2487. IF_DEBUG(DLC) {
  2488. DPUT2("OpenAdapter: returning %d (%x)\n", status, status);
  2489. }
  2490. return DLC_ERROR_STATUS(status, Ccb.uchDlcStatus);
  2491. }
  2492. VOID
  2493. CloseAdapter(
  2494. IN UCHAR AdapterNumber
  2495. )
  2496. /*++
  2497. Routine Description:
  2498. Closes this adapter. Uses a CCB in the DOS_ADAPTER structure specifically
  2499. for this purpose
  2500. THIS COMMAND COMPLETES ** ASYNCHRONOUSLY **
  2501. Arguments:
  2502. AdapterNumber - adapter to close
  2503. Return Value:
  2504. None.
  2505. --*/
  2506. {
  2507. InitializeCcb2(&Adapters[AdapterNumber].AdapterCloseCcb, AdapterNumber, LLC_DIR_CLOSE_ADAPTER);
  2508. Adapters[AdapterNumber].AdapterCloseCcb.ulCompletionFlag = VRDLC_COMMAND_COMPLETION;
  2509. #if DBG
  2510. ASSERT(lpAcsLan(&Adapters[AdapterNumber].AdapterCloseCcb, NULL) == LLC_STATUS_SUCCESS);
  2511. #else
  2512. lpAcsLan(&Adapters[AdapterNumber].AdapterCloseCcb, NULL);
  2513. #endif
  2514. //
  2515. // mark the adapter structure as being closed
  2516. //
  2517. Adapters[AdapterNumber].IsOpen = FALSE;
  2518. }
  2519. LLC_STATUS
  2520. OpenDirectStation(
  2521. IN UCHAR AdapterNumber
  2522. )
  2523. /*++
  2524. Routine Description:
  2525. Opens the direct station for this adapter
  2526. THIS COMMAND COMPLETES SYNCHRONOUSLY
  2527. Arguments:
  2528. AdapterNumber - which adapter to open direct station for
  2529. Return Value:
  2530. LLC_STATUS
  2531. --*/
  2532. {
  2533. LLC_CCB ccb;
  2534. LLC_DIR_OPEN_DIRECT_PARMS DirOpenDirect;
  2535. LLC_STATUS status;
  2536. IF_DEBUG(DLC) {
  2537. DPUT1("OpenDirectStation(%d)\n", AdapterNumber);
  2538. }
  2539. InitializeCcb(&ccb, AdapterNumber, LLC_DIR_OPEN_DIRECT, &DirOpenDirect);
  2540. DirOpenDirect.usOpenOptions = 0;
  2541. DirOpenDirect.usEthernetType = 0;
  2542. status = lpAcsLan(&ccb, NULL);
  2543. if (status == LLC_STATUS_SUCCESS) {
  2544. //
  2545. // mark this DOS_ADAPTER as having the direct station open
  2546. //
  2547. Adapters[AdapterNumber].DirectStationOpen = TRUE;
  2548. }
  2549. status = DLC_ERROR_STATUS(status, ccb.uchDlcStatus);
  2550. IF_DEBUG(DLC) {
  2551. DPUT2("OpenDirectStation: returning %d (%x)\n", status, status);
  2552. }
  2553. return status;
  2554. }
  2555. VOID
  2556. CloseDirectStation(
  2557. IN UCHAR AdapterNumber
  2558. )
  2559. /*++
  2560. Routine Description:
  2561. Closes the direct station for this adapter. Uses a CCB in the DOS_ADAPTER
  2562. structure specifically for this purpose
  2563. THIS COMMAND COMPLETES ** ASYNCHRONOUSLY **
  2564. Arguments:
  2565. AdapterNumber - adapter to close the direct station for
  2566. Return Value:
  2567. None.
  2568. --*/
  2569. {
  2570. InitializeCcb2(&Adapters[AdapterNumber].DirectCloseCcb, AdapterNumber, LLC_DIR_CLOSE_DIRECT);
  2571. Adapters[AdapterNumber].DirectCloseCcb.ulCompletionFlag = VRDLC_COMMAND_COMPLETION;
  2572. #if DBG
  2573. ASSERT(lpAcsLan(&Adapters[AdapterNumber].DirectCloseCcb, NULL) == LLC_STATUS_SUCCESS);
  2574. #else
  2575. lpAcsLan(&Adapters[AdapterNumber].DirectCloseCcb, NULL);
  2576. #endif
  2577. //
  2578. // mark the adapter structure as no longer having the direct station open
  2579. //
  2580. Adapters[AdapterNumber].DirectStationOpen = FALSE;
  2581. }
  2582. LLC_STATUS
  2583. BufferFree(
  2584. IN UCHAR AdapterNumber,
  2585. IN PVOID pFirstBuffer,
  2586. OUT LPWORD pusBuffersLeft
  2587. )
  2588. /*++
  2589. Routine Description:
  2590. Frees a SAP buffer pool in the NT DLC driver
  2591. THIS COMMAND COMPLETES SYNCHRONOUSLY
  2592. Arguments:
  2593. AdapterNumber -
  2594. pFirstBuffer -
  2595. pusBuffersLeft -
  2596. Return Value:
  2597. LLC_STATUS
  2598. --*/
  2599. {
  2600. LLC_CCB ccb;
  2601. LLC_BUFFER_FREE_PARMS parms;
  2602. LLC_STATUS status;
  2603. IF_DEBUG(DLC) {
  2604. DPUT1("BufferFree(%x)\n", pFirstBuffer);
  2605. }
  2606. InitializeCcb(&ccb, AdapterNumber, LLC_BUFFER_FREE, &parms);
  2607. parms.pFirstBuffer = pFirstBuffer;
  2608. status = lpAcsLan(&ccb, NULL);
  2609. *pusBuffersLeft = parms.cBuffersLeft;
  2610. return DLC_ERROR_STATUS(status, ccb.uchDlcStatus);
  2611. }
  2612. LLC_STATUS
  2613. VrDlcInit(
  2614. VOID
  2615. )
  2616. /*++
  2617. Routine Description:
  2618. perform one-shot initialization:
  2619. * clear Adapters structures
  2620. * initialize array of buffer pool structures and initialize the buffer
  2621. pool critical section (InitializeBufferPools in vrdlcbuf.c)
  2622. * create all events and threads for asynchronous command completion
  2623. processing (InitializeEventHandler in vrdlcpst.c)
  2624. * initialize critical sections for each adapter's local-busy(buffer)
  2625. state information
  2626. * set the DLC initialized flag
  2627. Arguments:
  2628. None.
  2629. Return Value:
  2630. LLC_STATUS
  2631. Success - LLC_STATUS_SUCCESS
  2632. DLC support already initialized or initialization completed
  2633. successfully
  2634. Failure - LLC_STATUS_NO_MEMORY
  2635. failed to create the asynchronous event thread or an event
  2636. object
  2637. --*/
  2638. {
  2639. static BOOLEAN VrDlcInitialized = FALSE;
  2640. LLC_STATUS Status = LLC_STATUS_SUCCESS;
  2641. if (!VrDlcInitialized) {
  2642. //
  2643. // ensure that the DOS_ADAPTER structures begin life in a known state
  2644. //
  2645. RtlZeroMemory(Adapters, sizeof(Adapters));
  2646. //
  2647. // clear out the buffer pool structures and initialize the buffer
  2648. // pool critical section
  2649. //
  2650. InitializeBufferPools();
  2651. //
  2652. // crreate the event handler thread and the worker thread
  2653. //
  2654. if (!(InitializeEventHandler() && InitializeDlcWorkerThread())) {
  2655. Status = LLC_STATUS_NO_MEMORY;
  2656. } else {
  2657. //
  2658. // initialize each adapter's local-busy state critical section
  2659. // and set the first & last indicies to -1, meaning no index
  2660. //
  2661. int i;
  2662. for (i = 0; i < ARRAY_ELEMENTS(Adapters); ++i) {
  2663. InitializeCriticalSection(&Adapters[i].EventQueueCritSec);
  2664. InitializeCriticalSection(&Adapters[i].LocalBusyCritSec);
  2665. Adapters[i].FirstIndex = Adapters[i].LastIndex = NO_LINKS_BUSY;
  2666. }
  2667. VrDlcInitialized = TRUE;
  2668. }
  2669. }
  2670. return Status;
  2671. }
  2672. VOID
  2673. VrVdmWindowInit(
  2674. VOID
  2675. )
  2676. /*++
  2677. Routine Description:
  2678. This routine saves the address of a VDM memory window, that is used
  2679. in the communication betwen VDM TSR and its virtual device driver.
  2680. This is called from a DOS TSR module.
  2681. Arguments:
  2682. ES:BX in the VDM context are set to point to a memory window in TSR.
  2683. Return Value:
  2684. None
  2685. --*/
  2686. {
  2687. IF_DEBUG(DLC) {
  2688. DPUT("VrVdmWindowInit\n");
  2689. }
  2690. //
  2691. // Initialize the VDM memory window addresses
  2692. //
  2693. dpVdmWindow = MAKE_DWORD(getES(), getBX());
  2694. lpVdmWindow = (LPVDM_REDIR_DOS_WINDOW)DOS_PTR_TO_FLAT(dpVdmWindow);
  2695. IF_DEBUG(DLC) {
  2696. DPUT2("VrVdmWindowsInit: dpVdmWindow=%08x lpVdmWindow=%08x\n", dpVdmWindow, lpVdmWindow);
  2697. }
  2698. //
  2699. // have to return success to VDM redir TSR
  2700. //
  2701. setCF(0);
  2702. }
  2703. ADAPTER_TYPE
  2704. GetAdapterType(
  2705. IN UCHAR AdapterNumber
  2706. )
  2707. /*++
  2708. Routine Description:
  2709. Determines what type of adapter AdapterNumber designates
  2710. THE DIR.STATUS COMMAND COMPLETES SYNCHRONOUSLY
  2711. Arguments:
  2712. AdapterNumber - number of adapter to get type of
  2713. Return Value:
  2714. ADAPTER_TYPE
  2715. TokenRing, Ethernet, PcNetwork, or UnknownAdapter
  2716. --*/
  2717. {
  2718. LLC_CCB ccb;
  2719. LLC_DIR_STATUS_PARMS parms;
  2720. LLC_STATUS status;
  2721. IF_DEBUG(DLC) {
  2722. DPUT("GetAdapterType\n");
  2723. }
  2724. InitializeCcb(&ccb, AdapterNumber, LLC_DIR_STATUS, &parms);
  2725. status = lpAcsLan(&ccb, NULL);
  2726. if (status == LLC_STATUS_SUCCESS) {
  2727. switch (parms.usAdapterType) {
  2728. case 0x0001: // Token Ring Network PC Adapter
  2729. case 0x0002: // Token Ring Network PC Adapter II
  2730. case 0x0004: // Token Ring Network Adapter/A
  2731. case 0x0008: // Token Ring Network PC Adapter II
  2732. case 0x0020: // Token Ring Network 16/4 Adapter
  2733. case 0x0040: // Token Ring Network 16/4 Adapter/A
  2734. case 0x0080: // Token Ring Network Adapter/A
  2735. return TokenRing;
  2736. case 0x0100: //Ethernet Adapter
  2737. return Ethernet;
  2738. case 0x4000: // PC Network Adapter
  2739. case 0x8000: // PC Network Adapter/A
  2740. return PcNetwork;
  2741. }
  2742. }
  2743. return UnknownAdapter;
  2744. }
  2745. BOOLEAN
  2746. LoadDlcDll(
  2747. VOID
  2748. )
  2749. /*++
  2750. Routine Description:
  2751. Dynamically loads DLCAPI.DLL & fixes-up entry points
  2752. Arguments:
  2753. None.
  2754. Return Value:
  2755. BOOLEAN
  2756. TRUE if success else FALSE
  2757. --*/
  2758. {
  2759. HANDLE hLibrary;
  2760. LPWORD lpVdmPointer;
  2761. if ((hLibrary = LoadLibrary("DLCAPI")) == NULL) {
  2762. IF_DEBUG(DLC) {
  2763. DPUT1("LoadDlcDll: Error: cannot load DLCAPI.DLL: %d\n", GetLastError());
  2764. }
  2765. return FALSE;
  2766. }
  2767. if ((lpAcsLan = (ACSLAN_FUNC_PTR)GetProcAddress(hLibrary, "AcsLan")) == NULL) {
  2768. IF_DEBUG(DLC) {
  2769. DPUT1("LoadDlcDll: Error: cannot GetProcAddress(AcsLan): %d\n", GetLastError());
  2770. }
  2771. return FALSE;
  2772. }
  2773. if ((lpDlcCallDriver = (DLC_CALL_DRIVER_FUNC_PTR)GetProcAddress(hLibrary, "DlcCallDriver")) == NULL) {
  2774. IF_DEBUG(DLC) {
  2775. DPUT1("LoadDlcDll: Error: cannot GetProcAddress(DlcCallDriver): %d\n", GetLastError());
  2776. }
  2777. return FALSE;
  2778. }
  2779. if ((lpNtAcsLan = (NTACSLAN_FUNC_PTR)GetProcAddress(hLibrary, "NtAcsLan")) == NULL) {
  2780. IF_DEBUG(DLC) {
  2781. DPUT1("LoadDlcDll: Error: cannot GetProcAddress(NtAcsLan): %d\n", GetLastError());
  2782. }
  2783. return FALSE;
  2784. }
  2785. IF_DEBUG(DLC) {
  2786. DPUT("LoadDlcDll: DLCAPI.DLL loaded Ok\n");
  2787. }
  2788. //
  2789. // Initialize the VDM memory window addresses from our well-known address
  2790. // in the VDM Redir. Do this here because we no longer initialize 32-bit
  2791. // support at the point where we load the 16-bit REDIR
  2792. //
  2793. lpVdmPointer = POINTER_FROM_WORDS(getCS(), (DWORD)&((VDM_LOAD_INFO*)0)->DlcWindowAddr);
  2794. dpVdmWindow = MAKE_DWORD(GET_SEGMENT(lpVdmPointer), GET_OFFSET(lpVdmPointer));
  2795. lpVdmWindow = (LPVDM_REDIR_DOS_WINDOW)DOS_PTR_TO_FLAT(dpVdmWindow);
  2796. IF_DEBUG(DLC) {
  2797. DPUT4("LoadDlcDll: lpVdmPointer=%x dpVdmWindow = %04x:%04x lpVdmWindow=%x\n",
  2798. lpVdmPointer,
  2799. HIWORD(dpVdmWindow),
  2800. LOWORD(dpVdmWindow),
  2801. lpVdmWindow
  2802. );
  2803. }
  2804. return TRUE;
  2805. }
  2806. VOID
  2807. TerminateDlcEmulation(
  2808. VOID
  2809. )
  2810. /*++
  2811. Routine Description:
  2812. Closes any open adapters. Any pending commands are terminated
  2813. Arguments:
  2814. None.
  2815. Return Value:
  2816. None.
  2817. --*/
  2818. {
  2819. DWORD i;
  2820. IF_DEBUG(DLC) {
  2821. DPUT("TerminateDlcEmulation\n");
  2822. }
  2823. IF_DEBUG(CRITICAL) {
  2824. DPUT("TerminateDlcEmulation\n");
  2825. }
  2826. for (i = 0; i < ARRAY_ELEMENTS(Adapters); ++i) {
  2827. if (Adapters[i].IsOpen) {
  2828. CloseAdapter((BYTE)i);
  2829. }
  2830. }
  2831. }
  2832. HANDLE DlcWorkerEvent;
  2833. HANDLE DlcWorkerCompletionEvent;
  2834. HANDLE DlcWorkerThreadHandle;
  2835. struct {
  2836. PLLC_CCB Input;
  2837. PLLC_CCB Original;
  2838. PLLC_CCB Output;
  2839. LLC_STATUS Status;
  2840. } DlcWorkerThreadParms;
  2841. BOOLEAN
  2842. InitializeDlcWorkerThread(
  2843. VOID
  2844. )
  2845. /*++
  2846. Routine Description:
  2847. Creates events which control VrDlcWorkerThread and starts the worker thread
  2848. Arguments:
  2849. None.
  2850. Return Value:
  2851. BOOLEAN
  2852. TRUE - worker thread was successfully created
  2853. FALSE - couldn't start worker thread for some reason
  2854. --*/
  2855. {
  2856. DWORD threadId;
  2857. //
  2858. // create 2 auto-reset events
  2859. //
  2860. DlcWorkerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  2861. if (DlcWorkerEvent == NULL) {
  2862. return FALSE;
  2863. }
  2864. DlcWorkerCompletionEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  2865. if (DlcWorkerCompletionEvent == NULL) {
  2866. CloseHandle(DlcWorkerEvent);
  2867. return FALSE;
  2868. }
  2869. //
  2870. // kick off the one-and-only worker thread
  2871. //
  2872. DlcWorkerThreadHandle = CreateThread(NULL,
  2873. 0,
  2874. (LPTHREAD_START_ROUTINE)VrDlcWorkerThread,
  2875. NULL,
  2876. 0,
  2877. &threadId
  2878. );
  2879. if (DlcWorkerThreadHandle == NULL) {
  2880. CloseHandle(DlcWorkerEvent);
  2881. CloseHandle(DlcWorkerCompletionEvent);
  2882. return FALSE;
  2883. }
  2884. return TRUE;
  2885. }
  2886. VOID
  2887. VrDlcWorkerThread(
  2888. IN LPVOID Parameters
  2889. )
  2890. /*++
  2891. Routine Description:
  2892. Submits requests to NtAcsLan on behalf of DOS thread. This exists because of
  2893. a problem with 16-bit Windows apps that use DLC (like Extra!). Eg:
  2894. 1. start Extra! session. Extra submits RECEIVE command
  2895. 2. connect to mainframe
  2896. 3. start second Extra! session
  2897. 4. connect second instance to mainframe
  2898. 5. kill first Extra! session
  2899. On a DOS machine, the RECEIVE is submitted for the entire process, so when
  2900. the first Extra! session is killed, the RECEIVE is still active.
  2901. However, on NT, each session is represented by a separate thread in NTVDM.
  2902. So when the first session is killed, any outstanding IRPs are cancelled,
  2903. including the RECEIVE. The second instance of Extra! doesn't know that the
  2904. RECEIVE has been cancelled, and never receives any more data
  2905. Arguments:
  2906. Parameters - unused pointer to parameter block
  2907. Return Value:
  2908. None.
  2909. --*/
  2910. {
  2911. DWORD object;
  2912. UNREFERENCED_PARAMETER(Parameters);
  2913. while (TRUE) {
  2914. object = WaitForSingleObject(DlcWorkerEvent, INFINITE);
  2915. if (object == WAIT_OBJECT_0) {
  2916. DlcWorkerThreadParms.Status = lpNtAcsLan(DlcWorkerThreadParms.Input,
  2917. DlcWorkerThreadParms.Original,
  2918. DlcWorkerThreadParms.Output,
  2919. NULL
  2920. );
  2921. SetEvent(DlcWorkerCompletionEvent);
  2922. }
  2923. }
  2924. }
  2925. LLC_STATUS
  2926. DlcCallWorker(
  2927. PLLC_CCB pInputCcb,
  2928. PLLC_CCB pOriginalCcb,
  2929. PLLC_CCB pOutputCcb
  2930. )
  2931. /*++
  2932. Routine Description:
  2933. Queues (depth is one) a request to the DLC worker thread and waits for the
  2934. worker thread to complete the request
  2935. Arguments:
  2936. pInputCcb - pointer to input CCB. Mapped to 32-bit aligned memory
  2937. pOriginalCcb - address of original CCB. Can be non-aligned DOS address
  2938. pOutputCcb - pointer to output CCB. Can be non-aligned DOS address
  2939. Return Value:
  2940. LLC_STATUS
  2941. --*/
  2942. {
  2943. DlcWorkerThreadParms.Input = pInputCcb;
  2944. DlcWorkerThreadParms.Original = pOriginalCcb;
  2945. DlcWorkerThreadParms.Output = pOutputCcb;
  2946. SetEvent(DlcWorkerEvent);
  2947. WaitForSingleObject(DlcWorkerCompletionEvent, INFINITE);
  2948. return DlcWorkerThreadParms.Status;
  2949. }