summaryrefslogtreecommitdiffstats
path: root/private/mvdm/vdmredir/vrdlc.h
blob: ee28c5a7989e0a108e07ec8e0f7fc686cae28519 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
/*++

Copyright (c) 1991  Microsoft Corporation
Copyright (c) 1991  Nokia Data Systems

Module Name:

    vrdlc.h

Abstract:

    This module is the only header file of Windows/Nt VDM DLC
    interface module.

    ALL STRUCTURES IN THIS FILE WHICH REFERENCE STRUCTURES IN DOS MEMORY
    ARE BYTE PACKED

Author:

    Antti Saarenheimo (o-anttis) 26-01-1992

Revision History:

--*/

//
// constants
//

#define DOS_DLC_MAX_SAPS                128
#define DOS_DLC_MAX_LINKS               255
#define DOS_DLC_MAX_EVENTS              64

#define LLC_DIR_MODIFY_OPEN_PARMS       0x01
#define LLC_DIR_RESTORE_OPEN_PARMS      0x02
#define LLC_DIR_SET_USER_APPENDAGE      0x2d
#define LLC_DOS_SPECIAL_COMMAND         ((ULONG)(-1))
#define LLC_BREAK                       0x20

#define DOS_DLC_STATUS_NO_INDICATION    0x81

#define LLC_SET_LOCAL_BUSY_BUFFER       0x20

//
// VRDLC_COMMAND_COMPLETION - this value is placed in the CCB_CMD_CMPL field
// of every CCB2 that we issue that is NOT for the VDM. This value is used to
// filter out command completions for commands that are generated by the DOS
// DLC Emulator. This stops us passing command completions through to the
// VDM that are not intended for it!
//

#define VRDLC_COMMAND_COMPLETION        ((ULONG)(-2))

//
// buffer pool sizes
//

#define DOS_DLC_BUFFER_POOL_SIZE   0x00010000   // 64K
#define DOS_DLC_MIN_FREE_THRESHOLD 0x00002000   //  8K

//
// flags for CopyFrame
//

#define CF_CONTIGUOUS               0x00000001  // frame is contiguous
#define CF_BREAK                    0x00000002  // options specified Break
#define CF_PARTIAL                  0x00000004  // receiving partial frame

//
// default values for DOS parameter tables (DD_ = DOS DEFAULT). These replace
// the various parameters which can be specified as 0. They may be different
// to the corresponding defaults applicable to NT DLC, so we fill them in
// specifically
//

//
// defaults for BUFFER.GET:
//

#define DD_BUFFER_GET           1

//
// defaults for DIR.INITIALIZE:
//

#define DD_SRAM_ADDRESS_0       0xd800
#define DD_SRAM_ADDRESS_1       0xd400

//
// defaults for DIR.OPEN.ADAPTER, ADAPTER_PARMS:
//

#define DD_NUMBER_RCV_BUFFERS   8
#define DD_RCV_BUFFER_LENGTH    112
#define DD_DHB_BUFFER_LENGTH    600
#define DD_DATA_HOLD_BUFFERS    1

//
// defaults for DIR.OPEN.ADAPTER, DIRECT_PARMS:
//

#define DD_DIR_BUF_SIZE         160
#define DD_DIR_POOL_BLOCKS      256

//
// defaults for DIR.OPEN.ADAPTER, DLC_PARMS:
//

#define DD_DLC_MAX_SAP          2
#define DD_DLC_MAX_STATIONS     6
#define DD_DLC_MAX_GSAP         0
#define DD_DLC_T1_TICK_ONE      5
#define DD_DLC_T2_TICK_ONE      1
#define DD_DLC_Ti_TICK_ONE      25
#define DD_DLC_T1_TICK_TWO      25
#define DD_DLC_T2_TICK_TWO      10
#define DD_DLC_Ti_TICK_TWO      125

//
// defaults for DLC.OPEN.SAP:
//

#define DD_MAXOUT               2
#define DD_MAXIN                1
#define DD_MAX_RETRY_COUNT      8
#define DD_MAX_I_FIELD          600
#define DD_DLC_BUF_SIZE         160
#define DD_DLC_POOL_LEN         256

//
// macros
//

//
// DOS_PTR_TO_FLAT - given a DOS 16:16 pointer stored implicitly as a DWORD
//

#define DOS_PTR_TO_FLAT(a)  (PVOID)GetVDMAddr(HIWORD(a), LOWORD(a))

//
// NEW_DOS_ADDRESS - generate a new DOS_ADDRESS, given a base DOS_ADDRESS and
// a new pointer which is some number of bytes plus the base DOS_ADDRESS
// converted to a flat pointer. For example, a DOS_ADDRESS of 1234:0000 becomes
// (on x86) a flat pointer of 0x12340. We generate a new pointer 0x12380 and
// want to convert this address back to a DOS_ADDRESS. So we use this macro.
// Offset-wrap and segment update is automatically handled
//

#define NEW_DOS_ADDRESS(b, p)   ((b) + ((DWORD)(p) - (DWORD)DOS_PTR_TO_FLAT(b)))

//
// POOL_INDEX_FROM_SAP - get the index in aBufferPools for a given SAP/adapter
// combination. There are a maximum 127 SAPs per adapter, and 2 adapters which
// are available to DOS
//

#define POOL_INDEX_FROM_SAP(Sap, Adapter)   ((Sap & 0xfe) | Adapter)

//
// POOL_INDEX_FROM_ID - given a station ID (high byte = SAP, low byte = link
// station), get the index to the SAP's buffer pool in aBufferPools
//

#define POOL_INDEX_FROM_ID(Id, Adapter)     POOL_INDEX_FROM_SAP(HIBYTE(Id), Adapter)

//
// GET_POOL_INDEX - the original pool index macro
//

#define GET_POOL_INDEX(Adapter, usStationId)    POOL_INDEX_FROM_ID(usStationId, Adapter)

//
// macros which initialize CCBs and call AcsLan
//

#define DlcFlowControl(Adapter, StationId, Options)\
            LlcCommand(Adapter, LLC_DLC_FLOW_CONTROL, ((DWORD)Options << 16) + StationId)

#define DosDlcFlowControl(Adapter, StationId, Options)\
            LlcCommand(Adapter, LLC_DOS_DLC_FLOW_CONTROL, ((DWORD)Options << 16) + StationId)

#define InitializeCcb(pCcb, AdapterNumber, Command, pParameter) \
            RtlZeroMemory((pCcb), sizeof(*(pCcb)));\
            RtlZeroMemory((pParameter), sizeof(*(pParameter)));\
            (pCcb)->uchAdapterNumber = (UCHAR)AdapterNumber;\
            (pCcb)->uchDlcCommand = (UCHAR)Command;\
            (pCcb)->u.pParameterTable = (PLLC_PARMS)(pParameter)

#define InitializeCcb2(pCcb, AdapterNumber, Command) \
            RtlZeroMemory((pCcb), sizeof(*(pCcb)));\
            (pCcb)->uchAdapterNumber = (UCHAR)AdapterNumber;\
            (pCcb)->uchDlcCommand = (UCHAR)Command;

#define ReceiveCancel(AdapterNumber, pCcb) \
            LlcCommand(AdapterNumber, LLC_RECEIVE_CANCEL, (DWORD)pCcb)

//
// DLC_ERROR_STATUS - after calling AcsLan, if an error was returned by AcsLan
// then return that, else get the return code out of the CCB and return that
//

#define DLC_ERROR_STATUS(AcslanStatus, uchDlcStatus) \
            (DWORD)((AcslanStatus == 0) ? (DWORD)uchDlcStatus : (DWORD)AcslanStatus)

//
// VRDLC_ALLOC - standard allocation strategy in VDM REDIR DLC functions
//

#define VRDLC_ALLOC(Bytes)  LocalAlloc(LMEM_FIXED, Bytes)

//
// VRDLC_FREE - companion to VRDLC_ALLOC - standard allocation free strategy
//

#define VRDLC_FREE(Pointer) LocalFree((HLOCAL)Pointer)

//
// SAP_ID - get the SAP from a station ID word. Used as array index 0..127
// (corresponding to SAP 0..254 step 2)
//

#define SAP_ID(stationId)   (HIBYTE(stationId) >> 1)

//
// LINK_ID - get the link station ID from a station ID word. Used as array index
// 0..254 (corresponding to link station 1..255)
//

#define LINK_ID(stationId)  (LOBYTE(stationId) - 1)
//
// types
//

union _LLC_DOS_PARMS;
typedef union _LLC_DOS_PARMS LLC_DOS_PARMS, *PLLC_DOS_PARMS;
typedef DWORD DOS_ADDRESS;
typedef DOS_ADDRESS DPLLC_DOS_BUFFER;

//
// LLC_DOS_BUFFER - this is a union of all the DOS DLC data buffers. There are
// basically 3 kinds: Buffer 1, the first buffer in a chain which contains net
// address info, this can be in contigous or non-contiguous form, and Buffer 2
// format which is the 2nd and subsequent buffers in a chain. DLC uses the
// buffers for received data. Transmit data (passed from the app to DLC) can
// use a buffer (or chain of buffers) from the pool or can source its own
// buffer. The latter is preferred since taking buffers which DLC would use
// for receiving data can leave DLC in the local busy state (ie no receive
// buffers)
//

#include <packon.h>

typedef union _LLC_DOS_BUFFER {

    //
    // pNext is just a pointer so we can follow the chain
    //

    union _LLC_DOS_BUFFER * pNext;

    //
    // NextDosBuffer is the Buffer 2 structure defined in the IBM Lan Tech.
    // Ref. pg 2-45
    //

    struct _NextDosBuffer {
        union _LLC_DOS_BUFFER * pNextBuffer;// next frame segment
        WORD        cbFrame;                // length of the whole rcvd frame
        WORD        cbBuffer;               // length of this segment
        WORD        offUserData;            // offset of data from descr header
        WORD        cbUserData;             // length of the data
    } Next;

    //
    // NotContiguous is the Not contiguous MAC/Data Buffer 1 structure defined
    // in IBM Lan Tech. Ref. pg 2-42
    //

    struct _DosDlcNotContiguousFirstBuffer {
        union _LLC_DOS_BUFFER * pNextBuffer;  // next frame segment
        WORD            cbFrame;        // length of entire frame
        WORD            cbBuffer;       // length of this buffer
        WORD            offUserData;    // user data in this struct
        WORD            cbUserData;     // length of user data
        WORD            usStationId;    // ssnn station id
        UCHAR           uchOptions;     // option byte from RECEIVE param tbl
        UCHAR           uchMsgType;     // the message type
        WORD            cBuffersLeft;   // number of basic buffer units left
        UCHAR           uchRcvFS;       // the received frame status
        UCHAR           uchAdapterNumber;  // current adapter number
        UCHAR           cbLanHeader;    // length of the LAN header
        UCHAR           cbDlcHeader;    // length of the DLC header
        UCHAR           auchLanHeader[32];// LAN header of the received frame
        UCHAR           auchDlcHeader[4]; // DLC header of the received frame
    } NotContiguous;

    //
    // Contiguous is the Contiguous MAC/Data Buffer 1 structure defined
    // in IBM Lan Tech. Ref. pg 2-43
    //

    struct _DosDlcContiguousFirstBuffer {
        union _LLC_DOS_BUFFER * pNextBuffer;  // next frame segment
        WORD            cbFrame;        // length of entire frame
        WORD            cbBuffer;       // length of this buffer
        WORD            offUserData;    // user data in this struct
        WORD            cbUserData;     // length of user data
        WORD            usStationId;    // ssnn station id
        UCHAR           uchOptions;     // option byte from RECEIVE param tbl
        UCHAR           uchMsgType;     // the message type
        WORD            cBuffersLeft;   // number of basic buffer units left
        UCHAR           uchRcvFS;       // the received frame status
        UCHAR           uchAdapterNumber;
    } Contiguous;
} LLC_DOS_BUFFER, *PLLC_DOS_BUFFER;

#include <packoff.h>

//
// DOS_DLC_BUFFER_POOL - there is one of these per each SAP per adapter (max.
// 127 SAPs per adapter * max. 2 adapters = 256), kept in an array. This
// structure maintains basic information about the DOS buffer pool - its
// starting address (dpBuffer) in DOS 16:16 format, the size of an individual
// buffer in the pool (BufferSize) and the number of buffers in the pool
// (BufferCount). A buffer must be an integral multiple of 16 bytes, a minimum
// length of 80 bytes and not exceeding 64K-16 (0xfff0 = 65520)
//

typedef struct _DOS_DLC_BUFFER_POOL {
    DOS_ADDRESS dpBuffer;
    WORD BufferSize;
    WORD BufferCount;
    WORD MaximumBufferCount;
} DOS_DLC_BUFFER_POOL, *PDOS_DLC_BUFFER_POOL;

//
// DOS DLC CCB aka CCB1 - see definition in IBM Lan Tech. Ref. pg 2-6
//

#include <packon.h>

typedef struct _LLC_DOS_CCB {
    UCHAR   uchAdapterNumber;       // Adapter 0 or 1
    UCHAR   uchDlcCommand;          // DLC command
    UCHAR   uchDlcStatus;           // DLC command completion code
    UCHAR   uchReserved1;           // reserved for DLC DLL
    struct _LLC_DOS_CCB *pNext;     // queued another CCB
    DWORD   ulCompletionFlag;       // used in command completion
    union {
        PLLC_DOS_PARMS pParms;      // pointer to the parameter table
        struct {
            WORD    usStationId;    // Station id
            WORD    usParameter;    // optional parameter
        } dlc;
        struct {
            WORD    usParameter0;   // first optional parameter
            WORD    usParameter1;   // second optional parameter
        } dir;
        UCHAR   auchBuffer[4];      // group/functional address
        DWORD   ulParameter;
    } u;
} LLC_DOS_CCB, *PLLC_DOS_CCB;

//
// additional parameter tables not defined in (sdk\inc\) DLCAPI.H (or where
// CCB1 parameter tables different from those defined in DLCAPI.H)
//

//
// LLC_DOS_DIR_INITIALIZE_PARMS - CCB1 DIR.INITIALIZE parameter table
//

typedef struct {
    WORD    BringUps;
    WORD    SharedRamAddress;
    WORD    Reserved;
    DWORD   AdapterCheckExit;
    DWORD   NetworkStatusExit;
    DWORD   PcErrorExit;
} LLC_DOS_DIR_INITIALIZE_PARMS, *PLLC_DOS_DIR_INITIALIZE_PARMS;

//
// ADAPTER_PARMS, DIRECT_PARMS, DLC_PARMS and NCB_PARMS - these are the
// parameter tables which are passed in to DIR.OPEN.ADAPTER
//

//
// ADAPTER_PARMS - parameters returned from the adapter support s/w
//

typedef struct _ADAPTER_PARMS {
    WORD    OpenErrorCode;          // error detected opening adapter
    WORD    OpenOptions;            // options for Token Ring only:

    //
    // OpenOptions Bit Meanings
    //
    //  This has been paraphrased from the IBM Lan Tech. Ref. p3-22. I don't
    //  understand most of it, but I think I made it easier to read than the
    //  IBM technicalese. Note: ONLY MEANINGFUL TO TOKEN RING ADAPTER
    //
    // Bit 15: Wrap Interface
    //          The adapter doesn't attach to the network; instead, all
    //          transmitted data is reflected back as received data
    //
    // Bit 14: Disable Hard Error
    //          Stops network status change involving "Hard Error" and
    //          "Transmit Beacon" bits from generating interrupt
    //
    // Bit 13: Disable Soft Errors
    //          Stops network status change involving "Soft Error" bit
    //          generating interrupt
    //
    // Bit 12: Pass Adapter MAC Frames
    //          Unsupported MAC frames are passed to the direct station.
    //          If OFF, these frames are ignored
    //
    // Bit 11: Pass Attention MAC Frames
    //          Passes attention MAC frames which are not the same as the last
    //          received Attention MAC Frame to the direct station. If OFF,
    //          these frames are not passed to the direct station (ie App)
    //
    // Bit 10: Reserved
    //          Should be zero, but not checked by adapter
    //
    // Bit  9: Pass Parameter Table
    //          If the adapter is already open, returns options specified
    //
    // Bit  8: Contender
    //          If ON, this adapter will participate in monitor contention
    //          (claim token), should the need arise. If OFF, and it is
    //          another adapter decides it is necessary to claim the token,
    //          this adapter will not participate
    //
    //          If this adapter decides it is necessary to determine a new
    //          active monitor, this adapter will initiate monitor contention
    //          processing IRRESPECTIVE OF THE VALUE OF THIS BIT
    //
    // Bit  7: Pass Beacon MAC Frames
    //          Pass to direct station first Beacon MAC frame and all subsequent
    //          Beacon MAC frames having a change in source address or beacon type
    //
    // Bit  6: Reserved
    //          Should be zero, but not checked by adapter
    //
    // Bit  5: Remote Program Load
    //          Only implemented on 16/4 adapters. Prevents adapter becoming
    //          a monitor during open process. If ON, will cause this adapter
    //          to fail the open if there are no other active adapters on the
    //          ring when it tries to insert itself
    //
    // Bit  4: Token Release
    //          Only implemented on 16/4 adapters and only available when
    //          operating at 16 Mbps. OFF: use early token release (default).
    //          ON: selects no early token release for adapter a 16 Mbps
    //
    // Bit  3: Reserved  \
    // Bit  2: Reserved   >  Should be 0, but are not checked by adapter
    // Bit  1: Reserved  /
    // Bit  0: Reserved /
    //

    BYTE    NodeAddress[6];         // this adapter's address
    DWORD   GroupAddress;           // group address to set
    DWORD   FunctionalAddress;      // functional address to set
    WORD    NumberReceiveBuffers;   // number of receive buffers
    WORD    ReceiveBufferLength;    // size of receive buffer
    WORD    DataHoldBufferLength;   // size of transmit data hold buffer
    BYTE    NumberDataHoldBuffers;  // returned: only by Token Ring
    BYTE    Reserved;
    WORD    OpenLock;               // Protection code to control closing adapter
                                    // This is NOT RETURNED when OpenOptions.9
                                    // is set (Pass parameter table)
    DWORD   ProductId;              // 18-byte product ID
                                    // This is NOT RETURNED when OpenOptions.9
                                    // is set (Pass parameter table)
    //
    // according to table 3-9 in IBM LAN Tech. Ref. (p3-25) the ProductId field
    // should point at an 18-byte buffer formatted like so:
    //
    //  Byte 0      0x01 indicates workstation
    //  Byte 1      0x10
    //  Byte 2-5    last 4 digits from workstation serial number in EBCDIC
    //  Byte 6-17   0x00
    //
} ADAPTER_PARMS, *PADAPTER_PARMS;

//
// DIRECT_PARMS - input parameters defining Direct Station for adapter
//

typedef struct _DIRECT_PARMS {

    //
    // the direct buffer size is min. 80 bytes, and must be integral multiple
    // of 16-bytes. If 0, default of 160 is used
    //

    WORD    DirectBufferSize;       // size of buffers in direct buffer pool


    //
    // direct pool blocks - number of 16-byte blocks in direct station buffer
    // pool. If 0, default of 256 is used (= 4096 byte buffer pool)
    //

    WORD    DirectPoolBlocks;       // size of buffer in 16-byte blocks


    //
    // direct buffer pool - segment address in workstation memory where direct
    // station buffer pool is created. Spec. doesn't say what happens if there
    // is a non-zero (or any, for that matter) offset. If 0, the application
    // must build the direct station buffer pool, in which case DirectBufferSize
    // must indicate the size of each buffer
    //

    DWORD   DirectBufferPool;       // start address of direct buffer pool


    //
    // adapter check exit - vectors to this address when the adapter detects
    // an internal error. If 0, the value specified in DIR.INITIALIZE is used
    //

    DWORD   AdapterCheckExit;       // I/O appendage exit: adapter check


    //
    // network status exit - vectors to this address when the network status
    // changes (whatever that means). If 0, the value specified by
    // DIR.INITIALIZE is used
    //

    DWORD   NetworkStatusExit;      // I/O appendage exit: network status change


    //
    // PC error exit - vectors to this address when the adapter s/w detects an
    // error in the workstation (!). If 0, the value specified by DIR.INITIALIZE
    // is used
    //

    DWORD   PcErrorExit;            // I/O appendage exit: error in workstation


    //
    // adapter work area - segment of area of w/s memory which is to be used
    // by the adapter. Ignored if  AdapterWorkAreaRequested is 0
    //

    DWORD   AdapterWorkArea;        // TR: adapter work are


    //
    // adapter work area length (requested) - the size of the workspace area,
    // the segment of which is specified in AdapterWorkArea. Size is calculated
    // thus: Number of SAPs x 36 + Number of stations (links) x 6 + 48
    //

    WORD    AdapterWorkAreaRequested; // TR: work area length requested


    //
    // adapter work area length (actual) - this value is returned by the
    // adapter. It is the amount of the work area used by the adapter (in bytes).
    // If this is greater than AdapterWorkAreaRequested then an error is returned
    // (0x12)

    WORD    AdapterWorkAreaActual;  // TR: actual work area length taken

} DIRECT_PARMS, *PDIRECT_PARMS;

//
// DLC_PARMS - returned values defining DLC limits
//

typedef struct _DLC_PARMS {

    //
    // maximum number of concurrently opened SAPs: limited by available
    // adapter memory and/or workspace memory. Maximum is 127 (126 if NetBIOS
    // is specified). If 0, the default 2 is used
    //

    BYTE    MaxSaps;                // maximum number of SAPs


    //
    // maximum number of concurrently opened link stations: limited by
    // available adapter and/or work area memory in workstation. Maximum is 255
    // for Token Ring, Ethernet or PC Network. If 0, the default of 6 is used
    //

    BYTE    MaxStations;            // maximum number of link stations


    //
    // maximum number of group SAPs concurrently opened. If 0, no group SAPs
    // can be opened. Maximum value is 126 for Token Ring, 125 for PC Network
    // and Ethernet
    //

    BYTE    MaxGroupSaps;           // maximum number of group SAPs


    //
    // maximum number of SAPs assigned to a group. Maximum is 127 for Token
    // Ring, 126 for PC Network and Ethernet
    //

    BYTE    MaxGroupMembers;        // maximum members per group SAP


    //
    // Timers. There are 3 timers: T1 is the Response Timer; T2 is the Inactivity
    // Timer; and Ti is the Receiver Acknowledgement Timer.
    //
    // Timers are set to a multiple of 40ms. They count down and interrupt the
    // adapter when they reach 0. Timer values can be between 1 and 10. If it
    // is between 1 and 5, the short timer tick (TICK_ONE) is used and is
    // referred to as group 1. If the number is between 6 and 10, the long timer
    // tick (TICK_TWO) is used and is referred to as group 2. The timer value is
    // the number (6 to 10) minus 5 multiplied by the long tick value.
    //

    //
    // Tick1 - number of 40 ms ticks for short DLC timer. Defaults (if 0):
    //  T1 5 (200ms-400ms)
    //  T2 1 (40ms-80ms)
    //  Ti 25 (1s-2s)
    //

    BYTE    T1Tick1;                // Timer 1 short timer
    BYTE    T2Tick1;                // Timer 2 short timer
    BYTE    TiTick1;                // Timer i short timer


    //
    // Tick2 - number of 40 ms ticks for long DLC timer. Default (if 0):
    //  T1 25 (1s-2s)
    //  T2 10 (400ms-800ms)
    //  Ti 125 (5s-10s)
    //

    BYTE    T1Tick2;                // Timer 1 long timer
    BYTE    T2Tick2;                // Timer 2 long timer
    BYTE    TiTick2;                // Timer i long timer
} DLC_PARMS, *PDLC_PARMS;

//
// NCB_PARMS - we are not interested in running DOS NETBIOS over DOS DLC (are we?)
//

typedef struct _NCB_PARMS {
    BYTE    Reserved1[4];       // adapter work area
    BYTE    TimerT1;
    BYTE    TimerT2;
    BYTE    TimerTi;
    BYTE    MaxOut;
    BYTE    MaxIn;
    BYTE    MaxOutIncr;
    BYTE    MaxRetry;
    BYTE    Reserved2[4];
    BYTE    NcbAccessPri;
    BYTE    MaxStations;
    BYTE    Reserved3[19];
    BYTE    MaxNames;
    BYTE    MaxNcbs;
    BYTE    MaxSessions;
    BYTE    Reserved4[2];
    BYTE    Options;
    WORD    PoolLength;
    DWORD   PoolAddress;
    BYTE    TxTimeout;
    BYTE    TxCount;
} NCB_PARMS, *PNCB_PARMS;

//
// LLC_DOS_DIR_OPEN_ADAPTER_PARMS is the CCB1 DIR.OPEN.ADAPTER parameter table
//

typedef struct _LLC_DOS_DIR_OPEN_ADAPTER_PARMS {
    PADAPTER_PARMS  pAdapterParms;
    PDIRECT_PARMS   pDirectParms;
    PDLC_PARMS      pDlcParms;
    PNCB_PARMS      pNcbParms;
} LLC_DOS_DIR_OPEN_ADAPTER_PARMS, *PLLC_DOS_DIR_OPEN_ADAPTER_PARMS;

//
// LLC_DOS_RECEIVE_PARMS is the CCB1 RECEIVE parameter table
//

typedef struct _LLC_DOS_RECEIVE_PARMS {
    WORD        usStationId;    // SAP, link station or direct id
    WORD        usUserLength;   // length of user data in buffer header
    DWORD       ulReceiveExit;  // the received data handler
    PLLC_BUFFER pFirstBuffer;   // first buffer in the pool
    UCHAR       uchOptions;     // defines how the frame is received
} LLC_DOS_RECEIVE_PARMS,  *PLLC_DOS_RECEIVE_PARMS;

//
// LLC_DOS_RECEIVE_PARMS_EX is an extended version of the LLC_DOS_RECEIVE_PARMS
// parameter table. We keep extra information - the DOS address of the original
// CCB and the original RECEIVE_DATA completion exit routine
//

typedef struct _LLC_DOS_RECEIVE_PARMS_EX {
    WORD        usStationId;            // SAP, link station or direct id
    WORD        usUserLength;           // length of user data in buffer header
    DWORD       ulReceiveExit;          // the received data handler
    PLLC_BUFFER pFirstBuffer;           // first buffer in the pool
    UCHAR       uchOptions;             // defines how the frame is received
    UCHAR       auchReserved1[3];       //
    UCHAR       uchRcvReadOption;       // defines if rcv frames are chained
    UCHAR       auchReserved2[3];       // align dpOriginalCcbAddress on DWORD
    DOS_ADDRESS dpOriginalCcbAddress;   // dos address of orginal ccb
    DOS_ADDRESS dpCompletionFlag;       // orginal completion flag
} LLC_DOS_RECEIVE_PARMS_EX,  *PLLC_DOS_RECEIVE_PARMS_EX;

//
// LLC_DOS_RECEIVE_MODIFY_PARMS is the parameter table for RECEIVE.MODIFY which
// we don't seem to support in NT native DLC
//

typedef struct {
    WORD    StationId;                  // SAP & link station Id
    WORD    UserLength;                 // length of user area in buffer
    DWORD   ReceivedDataExit;           // address of routine to call with data
    DWORD   FirstBuffer;                // pointer to first buffer from pool
    DWORD   Subroutine;                 // address of routine to call to get app buffer
} LLC_DOS_RECEIVE_MODIFY_PARMS, *PLLC_DOS_RECEIVE_MODIFY_PARMS;

//
// LLC_DOS_TRANSMIT_PARMS this structure is identical to LLC_TRANSMIT_PARMS
// except that there is no XMIT_READ_OPTION byte on the end, and the types of
// the fields are different, although the sizes are the same: eg. DOS_ADDRESS
// instead of PVOID or PLLC_XMIT_BUFFER
//

typedef struct _LLC_DOS_TRANSMIT_PARMS {
    WORD        usStationId;            // SAP and link station ID
    BYTE        uchTransmitFs;          // returned: Frame Status
    BYTE        uchRemoteSap;           // remote SAP we're talking to
    DOS_ADDRESS pXmitQueue1;            // address of 1st buffer queue. Not pooled
    DOS_ADDRESS pXmitQueue2;            // address of 2nd buffer queue. Pooled
    WORD        cbBuffer1;              // length of data in pBuffer1
    WORD        cbBuffer2;              // length of data in pBuffer2
    DOS_ADDRESS pBuffer1;               // address of 1st data buffer
    DOS_ADDRESS pBuffer2;               // address of 2nd data buffer
} LLC_DOS_TRANSMIT_PARMS, *PLLC_DOS_TRANSMIT_PARMS;

typedef struct _LLC_MODIFY_OPEN_PARMS {
    WORD        usBufferSize;           // block size of dlc buffers (>=80)
    WORD        cPoolBlocks;            // number of 16 bytes blocks in buffer
    DOS_ADDRESS dpPoolAddress;
    DOS_ADDRESS dpAdapterCheckExit;
    DOS_ADDRESS dpNetworkStatusExit;
    DOS_ADDRESS dpPcErrorExit;
    WORD        usOpenOption;
} LLC_MODIFY_OPEN_PARMS, *PLLC_MODIFY_OPEN_PARMS;

typedef struct _DOS_DLC_DIRECT_PARMS {
    WORD        usBufferSize;           // block size of dlc buffers (>=80)
    WORD        cPoolBlocks;            // number of 16 bytes blocks in buffer
    DOS_ADDRESS dpPoolAddress;          //
    DOS_ADDRESS dpAdapterCheckExit;
    DOS_ADDRESS dpNetworkStatusExit;
    DOS_ADDRESS dpPcErrorExit;
    DWORD       ulReserved1;
    WORD        usReserved2;
    WORD        usReserved3;
} DOS_DLC_DIRECT_PARMS, *PDOS_DLC_DIRECT_PARMS;

typedef struct _DOS_DLC_OPEN_SAP_PARMS {
    WORD        usStationId;            // SAP or link station id
    WORD        usUserStatValue;        // reserved for user
    UCHAR       uchT1;                  // response timer
    UCHAR       uchT2;                  // aknowledgment timer
    UCHAR       uchTi;                  // inactivity timer
    UCHAR       uchMaxOut;              // max tramists without ack
    UCHAR       uchMaxIn;               // max receives without ack
    UCHAR       uchMaxOutIncr;          // dynamic window increment value
    UCHAR       uchMaxRetryCnt;         // N2 value (retries)
    UCHAR       uchMaxMembers;          // maximum members for group SAP
    WORD        usMaxI_Field;           // maximum length of the Info field
    UCHAR       uchSapValue;            // SAP value to be assigned
    UCHAR       uchOptionsPriority;     // SAP options and access priority
    UCHAR       uchcStationCount;       // maximum number of link stations in sap
    UCHAR       uchReserved2[2];        //
    UCHAR       cGroupCount;            // number of group SAPs of this SAP
    PUCHAR      pGroupList;             // offset to the group list
    DWORD       DlcStatusFlags;         // User notify flag for DLC status changes
    WORD        usBufferSize;           // size of individual buffer in bytes
    WORD        cPoolBlocks;            // number of 16-byte blocks in pool
    DOS_ADDRESS dpPoolAddress;          // address of Buffer Pool (may be 0)
} DOS_DLC_OPEN_SAP_PARMS,  *PDOS_DLC_OPEN_SAP_PARMS;

typedef struct _DOS_DIR_STATUS_PARMS {
    UCHAR       auchPermanentAddress[6];// permanent encoded address
    UCHAR       auchNodeAddress[6];     // adapter's network address
    UCHAR       auchGroupAddress[4];    // adapter's group address
    UCHAR       auchFunctAddr[4];       // adapter's functional address
    UCHAR       uchMaxSap;              // maximum allowable SAP
    UCHAR       uchOpenSaps;            // number of currently open saps
    UCHAR       uchMaxStations;         // max number of stations (always 253)
    UCHAR       uchOpenStation;         // number of open stations (only up to 253)
    UCHAR       uchAvailStations;       // number of available stations (always 253)
    UCHAR       uchAdapterConfig;       // adapter configuration flags
    UCHAR       auchMicroCodeLevel[10]; // microcode level
    DOS_ADDRESS dpAdapterParmsAddr;     // shared RAM address of adapter parms
    DOS_ADDRESS dpAdapterMacAddr;       // shared RAM address of adapter MAC buffer
    DOS_ADDRESS dpTimerTick;            // address of DLC timer tick counter
    USHORT      usLastNetworkStatus;    // most recent network status issued
    DOS_ADDRESS dpExtendedParms;        // address of extended status table
} DOS_DIR_STATUS_PARMS, *PDOS_DIR_STATUS_PARMS;

typedef struct {
    DOS_ADDRESS dpAdapterCheckExit;     // adapter check appendage
    DOS_ADDRESS dpNetworkStatusExit;    // network status change appendage
    DOS_ADDRESS dpPcErrorExit;          // workstation error appendage
} LLC_DIR_SET_USER_APPENDAGE_PARMS, *PLLC_DIR_SET_USER_APPENDAGE_PARMS;

#include <packoff.h>

union _LLC_DOS_PARMS {
    LLC_BUFFER_FREE_PARMS       BufferFree;
    LLC_BUFFER_GET_PARMS        BufferGet;
    LLC_DLC_CONNECT_PARMS       DlcConnectStation;
    LLC_DLC_MODIFY_PARMS        DlcModify;
    LLC_DLC_OPEN_SAP_PARMS      DlcOpenSap;
    LLC_DLC_OPEN_STATION_PARMS  DlcOpenStation;
    LLC_DLC_REALLOCATE_PARMS    DlcReallocate;
    LLC_DLC_SET_THRESHOLD_PARMS DlcSetThreshold;
    LLC_DLC_STATISTICS_PARMS    DlcStatistics;
    LLC_DIR_INITIALIZE_PARMS    DirInitialize;
    LLC_MODIFY_OPEN_PARMS       DirModifyOpenParms;
    LLC_DIR_OPEN_ADAPTER_PARMS  DirOpenAdapter;
    LLC_DIR_OPEN_DIRECT_PARMS   DirOpenDirect;
    LLC_DIR_READ_LOG_PARMS      DirReadLog;
    LLC_DIR_SET_EFLAG_PARMS     DirSetExceptionFlags;
    LLC_DIR_SET_USER_APPENDAGE_PARMS    DirSetUserAppendage;
    LLC_DIR_STATUS_PARMS        DirStatus;
    DOS_DIR_STATUS_PARMS        DosDirStatus;
    LLC_READ_PARMS              Read;
    LLC_DOS_RECEIVE_PARMS_EX    Receive;
    LLC_DOS_RECEIVE_PARMS       DosReceive;
    LLC_TRANSMIT_PARMS          Transmit;
    LLC_TRANSMIT2_COMMAND       Transmit2;
    LLC_TRACE_INITIALIZE_PARMS  TraceInitialize;
    DOS_DLC_OPEN_SAP_PARMS      DosDlcOpenSap;
};

//
// ADAPTER_TYPE - what type of network adapter we have - Token Ring, Ethernet
// or (less likely) PC network
//

typedef enum {
    TokenRing,
    Ethernet,
    PcNetwork,
    UnknownAdapter
} ADAPTER_TYPE;

//
// LOCAL_BUSY_STATE - a link station can be in 1 of 3 emulated local-busy
// (buffer) states:
//
//  NOT_BUSY
//      the station doesn't have any backed-up I-frames pending
//
//  BUSY
//      the station is in emulated local-busy(buffer) state and a
//      DLC.FLOW.CONTROL(local-busy(buffer), set) has been sent to
//      the DLC device driver
//
//  BUSY_BUFFER
//      to get out of BUSY state into CLEARING, we need at least one buffer and
//      a DLC.FLOW.CONTROL from the app. Because apps can issue DLC.FLOW.CONTROL
//      and BUFFER.FREE in the wrong order, we need an AND of these 2 commands
//      to get going again. So we have this intermediate state where we are
//      awaiting either command to restart I-Frame reception
//
//  BUSY_FLOW
//      Together with BUSY_BUFFER, used to create a hysteresis whereby we can't
//      reach CLEARING from BUSY without getting both a DLC.FLOW.CONTROL and
//      BUFFER.FREE
//
//  CLEARING
//      the VDM app has cleared the emulated local-busy state, but
//      the DLC device driver is still in local-busy (buffer) state.
//      When the last deferred I-Frame is indicated to the VDM app,
//      the NT device driver local-busy(buffer) state will be reset
//      and normal service will resume
//
//
// State transitions:
//
//  NOT_BUSY -> BUSY
//      occurs when we discover there aren't enough DOS buffers to
//      receive an I-Frame. This transition is distinguished by the
//      following actions:
//
//          1. DLC.FLOW.CONTROL(local-busy(buffer), set) is indicated to
//             the DLC device driver
//          2. the received I-Frame is dequeued from the event queue for
//             this adapter and queued on the LocalBusyInfo.Queue
//          3. a local-busy(buffer) DLC status change event is indicated to
//             the DOS DLC app
//
//  BUSY -> BUSY_FLOW/BUSY_BUFFER
//      occurs when either a DLC.FLOW.CONTROL or BUFFER.FREE (resp) is issued
//
//  BUSY_FLOW/BUSY_BUFFER -> CLEARING
//      occurs when the DOS DLC app indicates we can continue receiving.
//      This is done when the other (DLC.FLOW.CONTROL or BUFFER.FREE) required
//      command is issued
//      This transition is distinguished by the following actions:
//
//          1. DOS DLC app issues DLC.FLOW.CONTROL(local-busy(buffer), reset)
//          2. DOS DLC app *may* issue BUFFER.FREE to return receive
//             buffers to the SAP pool
//
//  CLEARING -> NOT_BUSY
//      occurs when we indicate the last deferred receive to the DOS DLC
//      app. At this point we can do the following:
//
//          1. issue DLC.FLOW.CONTROL(local-busy(buffer), reset) to the
//             device driver
//
//  CLEARING -> BUSY
//      occurs when, during indicating deferred received I-Frames to the DOS
//      DLC app, we once again run out of buffers. Again, we indicate a DLC
//      status change of local-busy(buffer) to the DOS DLC app, but WE DO NOT
//      indicate local-busy(buffer) to the DLC device driver (it is still in
//      local-busy(buffer) state)
//

typedef enum {
    NOT_BUSY = 0,
    CLEARING,
    BUSY,
    BUSY_BUFFER,
    BUSY_FLOW
} LOCAL_BUSY_STATE;

//
// LOCAL_BUSY_INFO - this structure maintains a local-busy(buffer) state
// indicator and a pointer to a queue of deferred received I-Frames per link
// station per adapter
//

typedef struct {

    //
    // State maintains the tri-state of the link station w.r.t. received I-Frames
    //
    //  NOT_BUSY
    //      nothing queued on Queue, get next completed event from event Q
    //
    //  BUSY
    //      local-busy(buffer) state has been set in DLC driver,
    //      awaiting buffers & flow control command from DOS DLC app
    //
    //  CLEARING
    //      DOS DLC app has submitted DLC.FLOW.CONTROL(local_busy(buffer), reset)
    //      command, we are now trying to indicate deferred received I-Frames to
    //      DOS DLC app, pending enough DOS receive buffers
    //

    LOCAL_BUSY_STATE State;

    //
    // Queue - when in BUSY and CLEARING states, maintains a linked list of
    // completed NT READ CCBs containing received I-Frames
    //

    PLLC_CCB Queue;

#if DBG

    //
    // track queue depth for each link station in debug version
    //

    DWORD Depth;
#endif

} LOCAL_BUSY_INFO;

//
// MAX_I_FRAME_DEPTH - we don't expect the queue of deferred I-Frames to grow
// beyond this small number. The deferred I-Frame queue is a buffer between
// running out of receive buffers & restarting I-Frame reception
//

#define MAX_I_FRAME_DEPTH   64  // !

//
// DOS_ADAPTER - there is one of these for each DOS adapter (i.e. 2 max.). The
// structure contains information about the virtual state of each DOS adapter.
// We record such information as the parameters used to open the adapter, the
// exit addresses and the direct station information
//

typedef struct {

    //
    // AdapterType - tells us what type (class?) of adapter we are using. We
    // mainly use this to differentiate the types of values we return based
    // on whether this is a Token Ring adapter. We get the information from
    // the NT DIR.STATUS command
    //

    ADAPTER_TYPE AdapterType;

    //
    // IsOpen will be TRUE when this adapter has been successfully opened
    //

    BOOLEAN IsOpen;

    //
    // DirectStationOpen is TRUE when the direct station has been initialized
    // for this adapter. This is required because the direct station is opened
    // separately from the adapter in NT, but DOS expects both to be opened
    // simultaneously. Hence, we only issue a DIR.OPEN.DIRECT when the DOS app
    // issues a request for the direct station
    //

    BOOLEAN DirectStationOpen;

    //
    // if DirectReceive is TRUE then there is a receive outstanding on the
    // direct station for this adapter
    //

    BOOLEAN DirectReceive;

    //
    // if WaitingRestore is TRUE then we must get a DIR.RESTORE.OPEN.PARMS
    // before we can accept the next DIR.MODIFY.OPEN.PARMS
    //

    BOOLEAN WaitingRestore;

    //
    // BufferFree is TRUE when a BUFFER_FREE has been successfully issued for
    // any station ID belonging to this adapter
    //

    BOOLEAN BufferFree;

    //
    // BufferPool is the buffer pool for this adapter's direct station
    //

    PVOID BufferPool;

    //
    // CurrentExceptionHandlers and PreviousExceptionHandlers are the addresses
    // of exception 'exit' routines in DOS memory which are called asynchronously
    // if one of the special exceptions occurs. These are mapped to exception
    // flags in NT DLC and are presented as such in the READ CCB (ulCompletionFlag)
    //
    // exception handlers are always presented in the following order:
    //
    //  Adapter Check Exit
    //  Network Status Exit
    //  PC Error Exit
    //

    DWORD CurrentExceptionHandlers[3];
    DWORD PreviousExceptionHandlers[3];

    //
    // DlcStatusChangeAppendage - this appendage pointer is supplied in DLC.OPEN.SAP
    // - one for each SAP. We need to keep them here because the emulator can
    // generate its own DLC status change call-backs (local-busy(buffer))
    //

    DWORD DlcStatusChangeAppendage[DOS_DLC_MAX_SAPS];

    //
    // LastNetworkStatusChange is the last network status change we recorded.
    // This is reported in DIR.STATUS
    //

    WORD LastNetworkStatusChange;

    //
    // UserStatusValue - we have to record the USER_STAT_VALUE from each
    // successful DLC.OPEN.SAP. This is returned to the DLC status change
    // appendage. We need this info for when we generate our own status change
    // event (ie when we detect emulated local busy (buffer) state)
    //

    WORD UserStatusValue[DOS_DLC_MAX_SAPS];

    //
    // AdapterParms are the actual adapter parameters that this adapter was
    // opened with - either specified in the DIR.OPEN.ADAPTER from the DOS
    // app, or those which we use internally when we automatically open the
    // adapter
    //

    ADAPTER_PARMS AdapterParms;

    //
    // DlcSpecified will be TRUE if the DLC_PARMS table was given when the
    // adapter was opened (either by DIR.OPEN.ADAPTER from DOS app, or by
    // DIR.OPEN.ADAPTER from automatic open)
    //

    BOOLEAN DlcSpecified;

    //
    // DlcParms - the DLC parameters specified in the open
    //

    DLC_PARMS DlcParms;

    //
    // AdapterCloseCcb - used in asynchronous adapter close when close is
    // initiated by emulator
    //

    LLC_CCB AdapterCloseCcb;

    //
    // DirectCloseCcb - used in asynchronous direct station close when close is
    // initiated by emulator
    //

    LLC_CCB DirectCloseCcb;

    //
    // ReadCcb - pointer to current READ CCB for this adapter
    //

    PLLC_CCB ReadCcb;

    //
    // EventQueueCritSec - must hold this while accessing EventQueue
    //

    CRITICAL_SECTION EventQueueCritSec;

    //
    // EventQueue - linked list of pending completed READ CCBs. These are linked
    // by pNext field which is not normally used by READ CCB. The event queue is
    // a serialized list of asynchronous events which have occurred for this
    // adapter. Events are command completions, transmit completions, received
    // data frames and status changes
    //

    PLLC_CCB EventQueueHead;    // pointer to READ CCB at head of queue
    PLLC_CCB EventQueueTail;    //    "     "   "   "  "  end  "    "
    DWORD QueueElements;        // count of elements currently on EventQueue

    //
    // LocalBusyCritSec - must hold this while accessing DeferredReceives or
    // LocalBusyInfo array
    //

    CRITICAL_SECTION LocalBusyCritSec;

    //
    // DeferredReceives - reference count of number of link stations for this
    // adapter which are in local busy (buffer) state. Accessed while holding
    // LocalBusyCritSec. Serves as a boolean: check for !0 to discover if there
    // are deferred receives before checking all of LocalBusyInfo
    //

    DWORD DeferredReceives;

    //
    // FirstIndex and LastIndex - the start & stop points for searches through
    // LocalBusyInfo. These are used in an attempt to improve searching, since
    // for the vast majority of time, very few of the 255 possible slots in
    // LocalBusyInfo will be used
    //
    // NOTE: these are array indicies, NOT link station ids (index = id - 1)
    //

    DWORD FirstIndex;
    DWORD LastIndex;

    //
    // LocalBusyInfo - when a link station is in emulated local busy (buffer)
    // state, we dequeue any completed received I-Frames from the event queue
    // and link them onto the LocalBusyInfo list. For each adapter, there are
    // 255 lists - one per link station (there are 255 possible link stations
    // per adapter). The deferred receives are a list of completed NT READ CCBs
    // linked by CCB.pNext field. The lists serve as a buffer between realizing
    // we are out of buffers and the DLC device driver receiving the
    // DLC.FLOW.CONTROL(set, buffer) command. The lists are not expected to
    // grow very long
    //
    // This array combines the state of each link station for this adapter w.r.t.
    // local-busy(buffer) and maintains the list of deferred I-Frames.
    //
    // The array is accessed both by the main VDM thread and the EventHandlerThread
    // and so must only be accessed when holding LocalBusyCritSec
    //
    // Since there are 255 possible link stations per adapter and since the
    // Direct Station doesn't support link stations, link station 01 uses slot
    // 0, etc.
    //

    LOCAL_BUSY_INFO LocalBusyInfo[DOS_DLC_MAX_LINKS];

} DOS_ADAPTER, *PDOS_ADAPTER;

#define NO_LINKS_BUSY   ((DWORD)0x7fffffff)

//
// DOS DLC prototypes and externals
//

extern PLLC_BUFFER aOverflowedData[];
extern DWORD OpenedAdapters;
extern DOS_ADDRESS dpVdmWindow;

//
// vrdlc5c.c
//

VOID
VrDlc5cHandler(
    VOID
    );

VOID
CompleteCcbProcessing(
    IN LLC_STATUS Status,
    IN LLC_DOS_CCB UNALIGNED * pCcb,
    IN PLLC_PARMS pNtParms
    );

LLC_STATUS
LlcCommand(
    IN  UCHAR   AdapterNumber,
    IN  UCHAR   Command,
    IN  DWORD   Parameter
    );

LLC_STATUS
BufferFree(
    IN  UCHAR   AdapterNumber,
    IN  PVOID   pFirstBuffer,
    OUT LPWORD  pusBuffersLeft
    );

VOID
VrVdmWindowInit(
    VOID
    );

VOID
TerminateDlcEmulation(
    VOID
    );

//
// vrdlcbuf.c
//

VOID
InitializeBufferPools(
    VOID
    );

LLC_STATUS
CreateBufferPool(
    IN  DWORD   PoolIndex,
    IN  DOS_ADDRESS dpBuffer,
    IN  WORD    BufferCount,
    IN  WORD    BufferSize
    );

VOID
DeleteBufferPool(
    IN  DWORD   PoolIndex
    );

LLC_STATUS
GetBuffers(
    IN  DWORD   PoolIndex,
    IN  WORD    BuffersToGet,
    IN  DPLLC_DOS_BUFFER *pdpBuffer,
    OUT LPWORD  pusBuffersLeft,
    IN  BOOLEAN PartialList,
    OUT PWORD   BuffersGot OPTIONAL
    );

LLC_STATUS
FreeBuffers(
    IN  DWORD   PoolIndex,
    IN  DPLLC_DOS_BUFFER dpBuffer,
    OUT LPWORD  pusBuffersLeft
    );

WORD
CalculateBufferRequirement(
    IN UCHAR Adapter,
    IN WORD StationId,
    IN PLLC_BUFFER pFrame,
    IN LLC_DOS_PARMS UNALIGNED * pDosParms,
    OUT PWORD BufferSize
    );

LLC_STATUS
CopyFrame(
    IN PLLC_BUFFER pFrame,
    IN DPLLC_DOS_BUFFER DosBuffers,
    IN WORD UserLength,
    IN WORD BufferSize,
    IN DWORD Flags
    );

BOOLEAN
AllBuffersInPool(
    IN DWORD PoolIndex
    );

//
// vrdlcpst.c
//

VOID
VrDlcInitialize(
    VOID
    );

BOOLEAN
VrDlcHwInterrupt(
    VOID
    );

BOOLEAN
ResetEmulatedLocalBusyState(
    IN BYTE AdapterNumber,
    IN WORD StationId,
    IN BYTE DlcCommand
    );

BOOLEAN
InitializeEventHandler(
    VOID
    );

PLLC_CCB
InitiateRead(
    IN DWORD AdapterNumber,
    OUT LLC_STATUS* ErrorStatus
    );