aboutsummaryrefslogtreecommitdiff
path: root/drivers/char/ip2/i2ellis.c
blob: 29db44de399ff37111660a6a506875d89df1b827 (plain)
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
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
/*******************************************************************************
*
*   (c) 1998 by Computone Corporation
*
********************************************************************************
*
*
*   PACKAGE:     Linux tty Device Driver for IntelliPort family of multiport
*                serial I/O controllers.
*
*   DESCRIPTION: Low-level interface code for the device driver
*                (This is included source code, not a separate compilation
*                module.)
*
*******************************************************************************/
//---------------------------------------------
// Function declarations private to this module
//---------------------------------------------
// Functions called only indirectly through i2eBordStr entries.

static int iiWriteBuf16(i2eBordStrPtr, unsigned char *, int);
static int iiWriteBuf8(i2eBordStrPtr, unsigned char *, int);
static int iiReadBuf16(i2eBordStrPtr, unsigned char *, int);
static int iiReadBuf8(i2eBordStrPtr, unsigned char *, int);

static unsigned short iiReadWord16(i2eBordStrPtr);
static unsigned short iiReadWord8(i2eBordStrPtr);
static void iiWriteWord16(i2eBordStrPtr, unsigned short);
static void iiWriteWord8(i2eBordStrPtr, unsigned short);

static int iiWaitForTxEmptyII(i2eBordStrPtr, int);
static int iiWaitForTxEmptyIIEX(i2eBordStrPtr, int);
static int iiTxMailEmptyII(i2eBordStrPtr);
static int iiTxMailEmptyIIEX(i2eBordStrPtr);
static int iiTrySendMailII(i2eBordStrPtr, unsigned char);
static int iiTrySendMailIIEX(i2eBordStrPtr, unsigned char);

static unsigned short iiGetMailII(i2eBordStrPtr);
static unsigned short iiGetMailIIEX(i2eBordStrPtr);

static void iiEnableMailIrqII(i2eBordStrPtr);
static void iiEnableMailIrqIIEX(i2eBordStrPtr);
static void iiWriteMaskII(i2eBordStrPtr, unsigned char);
static void iiWriteMaskIIEX(i2eBordStrPtr, unsigned char);

static void ii2Nop(void);

//***************
//* Static Data *
//***************

static int ii2Safe;         // Safe I/O address for delay routine

static int iiDelayed;	// Set when the iiResetDelay function is
							// called. Cleared when ANY board is reset.
static DEFINE_RWLOCK(Dl_spinlock);

//********
//* Code *
//********

//=======================================================
// Initialization Routines
//
// iiSetAddress
// iiReset
// iiResetDelay
// iiInitialize
//=======================================================

//******************************************************************************
// Function:   iiSetAddress(pB, address, delay)
// Parameters: pB      - pointer to the board structure
//             address - the purported I/O address of the board
//             delay   - pointer to the 1-ms delay function to use
//                       in this and any future operations to this board
//
// Returns:    True if everything appears copacetic.
//             False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// This routine (roughly) checks for address validity, sets the i2eValid OK and
// sets the state to II_STATE_COLD which means that we haven't even sent a reset
// yet.
//
//******************************************************************************
static int
iiSetAddress( i2eBordStrPtr pB, int address, delayFunc_t delay )
{
	// Should any failure occur before init is finished...
	pB->i2eValid = I2E_INCOMPLETE;

	// Cannot check upper limit except extremely: Might be microchannel
	// Address must be on an 8-byte boundary

	if ((unsigned int)address <= 0x100
		|| (unsigned int)address >= 0xfff8
		|| (address & 0x7)
		)
	{
		I2_COMPLETE(pB, I2EE_BADADDR);
	}

	// Initialize accelerators
	pB->i2eBase    = address;
	pB->i2eData    = address + FIFO_DATA;
	pB->i2eStatus  = address + FIFO_STATUS;
	pB->i2ePointer = address + FIFO_PTR;
	pB->i2eXMail   = address + FIFO_MAIL;
	pB->i2eXMask   = address + FIFO_MASK;

	// Initialize i/o address for ii2DelayIO
	ii2Safe = address + FIFO_NOP;

	// Initialize the delay routine
	pB->i2eDelay = ((delay != (delayFunc_t)NULL) ? delay : (delayFunc_t)ii2Nop);

	pB->i2eValid = I2E_MAGIC;
	pB->i2eState = II_STATE_COLD;

	I2_COMPLETE(pB, I2EE_GOOD);
}

//******************************************************************************
// Function:   iiReset(pB)
// Parameters: pB - pointer to the board structure
//
// Returns:    True if everything appears copacetic.
//             False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Attempts to reset the board (see also i2hw.h). Normally, we would use this to
// reset a board immediately after iiSetAddress(), but it is valid to reset a
// board from any state, say, in order to change or re-load loadware. (Under
// such circumstances, no reason to re-run iiSetAddress(), which is why it is a
// separate routine and not included in this routine.
//
//******************************************************************************
static int
iiReset(i2eBordStrPtr pB)
{
	// Magic number should be set, else even the address is suspect
	if (pB->i2eValid != I2E_MAGIC)
	{
		I2_COMPLETE(pB, I2EE_BADMAGIC);
	}

	outb(0, pB->i2eBase + FIFO_RESET);  /* Any data will do */
	iiDelay(pB, 50);                    // Pause between resets
	outb(0, pB->i2eBase + FIFO_RESET);  /* Second reset */

	// We must wait before even attempting to read anything from the FIFO: the
	// board's P.O.S.T may actually attempt to read and write its end of the
	// FIFO in order to check flags, loop back (where supported), etc. On
	// completion of this testing it would reset the FIFO, and on completion
	// of all // P.O.S.T., write the message. We must not mistake data which
	// might have been sent for testing as part of the reset message. To
	// better utilize time, say, when resetting several boards, we allow the
	// delay to be performed externally; in this way the caller can reset 
	// several boards, delay a single time, then call the initialization
	// routine for all.

	pB->i2eState = II_STATE_RESET;

	iiDelayed = 0;	// i.e., the delay routine hasn't been called since the most
					// recent reset.

	// Ensure anything which would have been of use to standard loadware is
	// blanked out, since board has now forgotten everything!.

	pB->i2eUsingIrq = I2_IRQ_UNDEFINED; /* to not use an interrupt so far */
	pB->i2eWaitingForEmptyFifo = 0;
	pB->i2eOutMailWaiting = 0;
	pB->i2eChannelPtr = NULL;
	pB->i2eChannelCnt = 0;

	pB->i2eLeadoffWord[0] = 0;
	pB->i2eFifoInInts = 0;
	pB->i2eFifoOutInts = 0;
	pB->i2eFatalTrap = NULL;
	pB->i2eFatal = 0;

	I2_COMPLETE(pB, I2EE_GOOD);
}

//******************************************************************************
// Function:   iiResetDelay(pB)
// Parameters: pB - pointer to the board structure
//
// Returns:    True if everything appears copacetic.
//             False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Using the delay defined in board structure, waits two seconds (for board to
// reset).
//
//******************************************************************************
static int
iiResetDelay(i2eBordStrPtr pB)
{
	if (pB->i2eValid != I2E_MAGIC) {
		I2_COMPLETE(pB, I2EE_BADMAGIC);
	}
	if (pB->i2eState != II_STATE_RESET) {
		I2_COMPLETE(pB, I2EE_BADSTATE);
	}
	iiDelay(pB,2000);       /* Now we wait for two seconds. */
	iiDelayed = 1;          /* Delay has been called: ok to initialize */
	I2_COMPLETE(pB, I2EE_GOOD);
}

//******************************************************************************
// Function:   iiInitialize(pB)
// Parameters: pB - pointer to the board structure
//
// Returns:    True if everything appears copacetic.
//             False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Attempts to read the Power-on reset message. Initializes any remaining fields
// in the pB structure.
//
// This should be called as the third step of a process beginning with
// iiReset(), then iiResetDelay(). This routine checks to see that the structure
// is "valid" and in the reset state, also confirms that the delay routine has
// been called since the latest reset (to any board! overly strong!).
//
//******************************************************************************
static int
iiInitialize(i2eBordStrPtr pB)
{
	int itemp;
	unsigned char c;
	unsigned short utemp;
	unsigned int ilimit;

	if (pB->i2eValid != I2E_MAGIC)
	{
		I2_COMPLETE(pB, I2EE_BADMAGIC);
	}

	if (pB->i2eState != II_STATE_RESET || !iiDelayed)
	{
		I2_COMPLETE(pB, I2EE_BADSTATE);
	}

	// In case there is a failure short of our completely reading the power-up
	// message.
	pB->i2eValid = I2E_INCOMPLETE;


	// Now attempt to read the message.

	for (itemp = 0; itemp < sizeof(porStr); itemp++)
	{
		// We expect the entire message is ready.
		if (!I2_HAS_INPUT(pB)) {
			pB->i2ePomSize = itemp;
			I2_COMPLETE(pB, I2EE_PORM_SHORT);
		}

		pB->i2ePom.c[itemp] = c = inb(pB->i2eData);

		// We check the magic numbers as soon as they are supposed to be read
		// (rather than after) to minimize effect of reading something we
		// already suspect can't be "us".
		if (  (itemp == POR_1_INDEX && c != POR_MAGIC_1) ||
				(itemp == POR_2_INDEX && c != POR_MAGIC_2))
		{
			pB->i2ePomSize = itemp+1;
			I2_COMPLETE(pB, I2EE_BADMAGIC);
		}
	}

	pB->i2ePomSize = itemp;

	// Ensure that this was all the data...
	if (I2_HAS_INPUT(pB))
		I2_COMPLETE(pB, I2EE_PORM_LONG);

	// For now, we'll fail to initialize if P.O.S.T reports bad chip mapper:
	// Implying we will not be able to download any code either:  That's ok: the
	// condition is pretty explicit.
	if (pB->i2ePom.e.porDiag1 & POR_BAD_MAPPER)
	{
		I2_COMPLETE(pB, I2EE_POSTERR);
	}

	// Determine anything which must be done differently depending on the family
	// of boards!
	switch (pB->i2ePom.e.porID & POR_ID_FAMILY)
	{
	case POR_ID_FII:  // IntelliPort-II

		pB->i2eFifoStyle   = FIFO_II;
		pB->i2eFifoSize    = 512;     // 512 bytes, always
		pB->i2eDataWidth16 = false;

		pB->i2eMaxIrq = 15;	// Because board cannot tell us it is in an 8-bit
							// slot, we do allow it to be done (documentation!)

		pB->i2eGoodMap[1] =
		pB->i2eGoodMap[2] =
		pB->i2eGoodMap[3] =
		pB->i2eChannelMap[1] =
		pB->i2eChannelMap[2] =
		pB->i2eChannelMap[3] = 0;

		switch (pB->i2ePom.e.porID & POR_ID_SIZE)
		{
		case POR_ID_II_4:
			pB->i2eGoodMap[0] =
			pB->i2eChannelMap[0] = 0x0f;  // four-port

			// Since porPorts1 is based on the Hardware ID register, the numbers
			// should always be consistent for IntelliPort-II.  Ditto below...
			if (pB->i2ePom.e.porPorts1 != 4)
			{
				I2_COMPLETE(pB, I2EE_INCONSIST);
			}
			break;

		case POR_ID_II_8:
		case POR_ID_II_8R:
			pB->i2eGoodMap[0] =
			pB->i2eChannelMap[0] = 0xff;  // Eight port
			if (pB->i2ePom.e.porPorts1 != 8)
			{
				I2_COMPLETE(pB, I2EE_INCONSIST);
			}
			break;

		case POR_ID_II_6:
			pB->i2eGoodMap[0] =
			pB->i2eChannelMap[0] = 0x3f;  // Six Port
			if (pB->i2ePom.e.porPorts1 != 6)
			{
				I2_COMPLETE(pB, I2EE_INCONSIST);
			}
			break;
		}

		// Fix up the "good channel list based on any errors reported.
		if (pB->i2ePom.e.porDiag1 & POR_BAD_UART1)
		{
			pB->i2eGoodMap[0] &= ~0x0f;
		}

		if (pB->i2ePom.e.porDiag1 & POR_BAD_UART2)
		{
			pB->i2eGoodMap[0] &= ~0xf0;
		}

		break;   // POR_ID_FII case

	case POR_ID_FIIEX:   // IntelliPort-IIEX

		pB->i2eFifoStyle = FIFO_IIEX;

		itemp = pB->i2ePom.e.porFifoSize;

		// Implicit assumption that fifo would not grow beyond 32k, 
		// nor would ever be less than 256.

		if (itemp < 8 || itemp > 15)
		{
			I2_COMPLETE(pB, I2EE_INCONSIST);
		}
		pB->i2eFifoSize = (1 << itemp);

		// These are based on what P.O.S.T thinks should be there, based on
		// box ID registers
		ilimit = pB->i2ePom.e.porNumBoxes;
		if (ilimit > ABS_MAX_BOXES)
		{
			ilimit = ABS_MAX_BOXES;
		}

		// For as many boxes as EXIST, gives the type of box.
		// Added 8/6/93: check for the ISA-4 (asic) which looks like an
		// expandable but for whom "8 or 16?" is not the right question.

		utemp = pB->i2ePom.e.porFlags;
		if (utemp & POR_CEX4)
		{
			pB->i2eChannelMap[0] = 0x000f;
		} else {
			utemp &= POR_BOXES;
			for (itemp = 0; itemp < ilimit; itemp++)
			{
				pB->i2eChannelMap[itemp] = 
					((utemp & POR_BOX_16) ? 0xffff : 0x00ff);
				utemp >>= 1;
			}
		}

		// These are based on what P.O.S.T actually found.

		utemp = (pB->i2ePom.e.porPorts2 << 8) + pB->i2ePom.e.porPorts1;

		for (itemp = 0; itemp < ilimit; itemp++)
		{
			pB->i2eGoodMap[itemp] = 0;
			if (utemp & 1) pB->i2eGoodMap[itemp] |= 0x000f;
			if (utemp & 2) pB->i2eGoodMap[itemp] |= 0x00f0;
			if (utemp & 4) pB->i2eGoodMap[itemp] |= 0x0f00;
			if (utemp & 8) pB->i2eGoodMap[itemp] |= 0xf000;
			utemp >>= 4;
		}

		// Now determine whether we should transfer in 8 or 16-bit mode.
		switch (pB->i2ePom.e.porBus & (POR_BUS_SLOT16 | POR_BUS_DIP16) )
		{
		case POR_BUS_SLOT16 | POR_BUS_DIP16:
			pB->i2eDataWidth16 = true;
			pB->i2eMaxIrq = 15;
			break;

		case POR_BUS_SLOT16:
			pB->i2eDataWidth16 = false;
			pB->i2eMaxIrq = 15;
			break;

		case 0:
		case POR_BUS_DIP16:     // In an 8-bit slot, DIP switch don't care.
		default:
			pB->i2eDataWidth16 = false;
			pB->i2eMaxIrq = 7;
			break;
		}
		break;   // POR_ID_FIIEX case

	default:    // Unknown type of board
		I2_COMPLETE(pB, I2EE_BAD_FAMILY);
		break;
	}  // End the switch based on family

	// Temporarily, claim there is no room in the outbound fifo. 
	// We will maintain this whenever we check for an empty outbound FIFO.
	pB->i2eFifoRemains = 0;

	// Now, based on the bus type, should we expect to be able to re-configure
	// interrupts (say, for testing purposes).
	switch (pB->i2ePom.e.porBus & POR_BUS_TYPE)
	{
	case POR_BUS_T_ISA:
	case POR_BUS_T_UNK:  // If the type of bus is undeclared, assume ok.
	case POR_BUS_T_MCA:
	case POR_BUS_T_EISA:
		break;
	default:
		I2_COMPLETE(pB, I2EE_BADBUS);
	}

	if (pB->i2eDataWidth16)
	{
		pB->i2eWriteBuf  = iiWriteBuf16;
		pB->i2eReadBuf   = iiReadBuf16;
		pB->i2eWriteWord = iiWriteWord16;
		pB->i2eReadWord  = iiReadWord16;
	} else {
		pB->i2eWriteBuf  = iiWriteBuf8;
		pB->i2eReadBuf   = iiReadBuf8;
		pB->i2eWriteWord = iiWriteWord8;
		pB->i2eReadWord  = iiReadWord8;
	}

	switch(pB->i2eFifoStyle)
	{
	case FIFO_II:
		pB->i2eWaitForTxEmpty = iiWaitForTxEmptyII;
		pB->i2eTxMailEmpty    = iiTxMailEmptyII;
		pB->i2eTrySendMail    = iiTrySendMailII;
		pB->i2eGetMail        = iiGetMailII;
		pB->i2eEnableMailIrq  = iiEnableMailIrqII;
		pB->i2eWriteMask      = iiWriteMaskII;

		break;

	case FIFO_IIEX:
		pB->i2eWaitForTxEmpty = iiWaitForTxEmptyIIEX;
		pB->i2eTxMailEmpty    = iiTxMailEmptyIIEX;
		pB->i2eTrySendMail    = iiTrySendMailIIEX;
		pB->i2eGetMail        = iiGetMailIIEX;
		pB->i2eEnableMailIrq  = iiEnableMailIrqIIEX;
		pB->i2eWriteMask      = iiWriteMaskIIEX;

		break;

	default:
		I2_COMPLETE(pB, I2EE_INCONSIST);
	}

	// Initialize state information.
	pB->i2eState = II_STATE_READY;   // Ready to load loadware.

	// Some Final cleanup:
	// For some boards, the bootstrap firmware may perform some sort of test
	// resulting in a stray character pending in the incoming mailbox. If one is
	// there, it should be read and discarded, especially since for the standard
	// firmware, it's the mailbox that interrupts the host.

	pB->i2eStartMail = iiGetMail(pB);

	// Throw it away and clear the mailbox structure element
	pB->i2eStartMail = NO_MAIL_HERE;

	// Everything is ok now, return with good status/

	pB->i2eValid = I2E_MAGIC;
	I2_COMPLETE(pB, I2EE_GOOD);
}

//******************************************************************************
// Function:   ii2DelayTimer(mseconds)
// Parameters: mseconds - number of milliseconds to delay
//
// Returns:    Nothing
//
// Description:
//
// This routine delays for approximately mseconds milliseconds and is intended
// to be called indirectly through i2Delay field in i2eBordStr. It uses the
// Linux timer_list mechanism.
//
// The Linux timers use a unit called "jiffies" which are 10mS in the Intel
// architecture. This function rounds the delay period up to the next "jiffy".
// In the Alpha architecture the "jiffy" is 1mS, but this driver is not intended
// for Alpha platforms at this time.
//
//******************************************************************************
static void
ii2DelayTimer(unsigned int mseconds)
{
	msleep_interruptible(mseconds);
}

#if 0
//static void ii2DelayIO(unsigned int);
//******************************************************************************
// !!! Not Used, this is DOS crap, some of you young folks may be interested in
//     in how things were done in the stone age of caculating machines       !!!
// Function:   ii2DelayIO(mseconds)
// Parameters: mseconds - number of milliseconds to delay
//
// Returns:    Nothing
//
// Description:
//
// This routine delays for approximately mseconds milliseconds and is intended
// to be called indirectly through i2Delay field in i2eBordStr. It is intended
// for use where a clock-based function is impossible: for example, DOS drivers.
//
// This function uses the IN instruction to place bounds on the timing and
// assumes that ii2Safe has been set. This is because I/O instructions are not
// subject to caching and will therefore take a certain minimum time. To ensure
// the delay is at least long enough on fast machines, it is based on some
// fastest-case calculations.  On slower machines this may cause VERY long
// delays. (3 x fastest case). In the fastest case, everything is cached except
// the I/O instruction itself.
//
// Timing calculations:
// The fastest bus speed for I/O operations is likely to be 10 MHz. The I/O
// operation in question is a byte operation to an odd address. For 8-bit
// operations, the architecture generally enforces two wait states. At 10 MHz, a
// single cycle time is 100nS. A read operation at two wait states takes 6
// cycles for a total time of 600nS. Therefore approximately 1666 iterations
// would be required to generate a single millisecond delay. The worst
// (reasonable) case would be an 8MHz system with no cacheing. In this case, the
// I/O instruction would take 125nS x 6 cyles = 750 nS. More importantly, code
// fetch of other instructions in the loop would take time (zero wait states,
// however) and would be hard to estimate. This is minimized by using in-line
// assembler for the in inner loop of IN instructions. This consists of just a
// few bytes. So we'll guess about four code fetches per loop. Each code fetch
// should take four cycles, so we have 125nS * 8 = 1000nS. Worst case then is
// that what should have taken 1 mS takes instead 1666 * (1750) = 2.9 mS.
//
// So much for theoretical timings: results using 1666 value on some actual
// machines:
// IBM      286      6MHz     3.15 mS
// Zenith   386      33MHz    2.45 mS
// (brandX) 386      33MHz    1.90 mS  (has cache)
// (brandY) 486      33MHz    2.35 mS
// NCR      486      ??       1.65 mS (microchannel)
//
// For most machines, it is probably safe to scale this number back (remember,
// for robust operation use an actual timed delay if possible), so we are using
// a value of 1190. This yields 1.17 mS for the fastest machine in our sample,
// 1.75 mS for typical 386 machines, and 2.25 mS the absolute slowest machine.
//
// 1/29/93:
// The above timings are too slow. Actual cycle times might be faster. ISA cycle
// times could approach 500 nS, and ...
// The IBM model 77 being microchannel has no wait states for 8-bit reads and
// seems to be accessing the I/O at 440 nS per access (from start of one to
// start of next). This would imply we need 1000/.440 = 2272 iterations to
// guarantee we are fast enough. In actual testing, we see that 2 * 1190 are in
// fact enough. For diagnostics, we keep the level at 1190, but developers note
// this needs tuning.
//
// Safe assumption:  2270 i/o reads = 1 millisecond
//
//******************************************************************************


static int ii2DelValue = 1190;  // See timing calculations below
						// 1666 for fastest theoretical machine
						// 1190 safe for most fast 386 machines
						// 1000 for fastest machine tested here
						//  540 (sic) for AT286/6Mhz
static void
ii2DelayIO(unsigned int mseconds)
{
	if (!ii2Safe) 
		return;   /* Do nothing if this variable uninitialized */

	while(mseconds--) {
		int i = ii2DelValue;
		while ( i-- ) {
			inb(ii2Safe);
		}
	}
}
#endif 

//******************************************************************************
// Function:   ii2Nop()
// Parameters: None
//
// Returns:    Nothing
//
// Description:
//
// iiInitialize will set i2eDelay to this if the delay parameter is NULL. This
// saves checking for a NULL pointer at every call.
//******************************************************************************
static void
ii2Nop(void)
{
	return;	// no mystery here
}

//=======================================================
// Routines which are available in 8/16-bit versions, or
// in different fifo styles. These are ALL called
// indirectly through the board structure.
//=======================================================

//******************************************************************************
// Function:   iiWriteBuf16(pB, address, count)
// Parameters: pB      - pointer to board structure
//             address - address of data to write
//             count   - number of data bytes to write
//
// Returns:    True if everything appears copacetic.
//             False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Writes 'count' bytes from 'address' to the data fifo specified by the board
// structure pointer pB. Should count happen to be odd, an extra pad byte is
// sent (identity unknown...). Uses 16-bit (word) operations. Is called
// indirectly through pB->i2eWriteBuf.
//
//******************************************************************************
static int
iiWriteBuf16(i2eBordStrPtr pB, unsigned char *address, int count)
{
	// Rudimentary sanity checking here.
	if (pB->i2eValid != I2E_MAGIC)
		I2_COMPLETE(pB, I2EE_INVALID);

	I2_OUTSW(pB->i2eData, address, count);

	I2_COMPLETE(pB, I2EE_GOOD);
}

//******************************************************************************
// Function:   iiWriteBuf8(pB, address, count)
// Parameters: pB      - pointer to board structure
//             address - address of data to write
//             count   - number of data bytes to write
//
// Returns:    True if everything appears copacetic.
//             False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Writes 'count' bytes from 'address' to the data fifo specified by the board
// structure pointer pB. Should count happen to be odd, an extra pad byte is
// sent (identity unknown...). This is to be consistent with the 16-bit version.
// Uses 8-bit (byte) operations. Is called indirectly through pB->i2eWriteBuf.
//
//******************************************************************************
static int
iiWriteBuf8(i2eBordStrPtr pB, unsigned char *address, int count)
{
	/* Rudimentary sanity checking here */
	if (pB->i2eValid != I2E_MAGIC)
		I2_COMPLETE(pB, I2EE_INVALID);

	I2_OUTSB(pB->i2eData, address, count);

	I2_COMPLETE(pB, I2EE_GOOD);
}

//******************************************************************************
// Function:   iiReadBuf16(pB, address, count)
// Parameters: pB      - pointer to board structure
//             address - address to put data read
//             count   - number of data bytes to read
//
// Returns:    True if everything appears copacetic.
//             False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Reads 'count' bytes into 'address' from the data fifo specified by the board
// structure pointer pB. Should count happen to be odd, an extra pad byte is
// received (identity unknown...). Uses 16-bit (word) operations. Is called
// indirectly through pB->i2eReadBuf.
//
//******************************************************************************
static int
iiReadBuf16(i2eBordStrPtr pB, unsigned char *address, int count)
{
	// Rudimentary sanity checking here.
	if (pB->i2eValid != I2E_MAGIC)
		I2_COMPLETE(pB, I2EE_INVALID);

	I2_INSW(pB->i2eData, address, count);

	I2_COMPLETE(pB, I2EE_GOOD);
}

//******************************************************************************
// Function:   iiReadBuf8(pB, address, count)
// Parameters: pB      - pointer to board structure
//             address - address to put data read
//             count   - number of data bytes to read
//
// Returns:    True if everything appears copacetic.
//             False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Reads 'count' bytes into 'address' from the data fifo specified by the board
// structure pointer pB. Should count happen to be odd, an extra pad byte is
// received (identity unknown...). This to match the 16-bit behaviour. Uses
// 8-bit (byte) operations. Is called indirectly through pB->i2eReadBuf.
//
//******************************************************************************
static int
iiReadBuf8(i2eBordStrPtr pB, unsigned char *address, int count)
{
	// Rudimentary sanity checking here.
	if (pB->i2eValid != I2E_MAGIC)
		I2_COMPLETE(pB, I2EE_INVALID);

	I2_INSB(pB->i2eData, address, count);

	I2_COMPLETE(pB, I2EE_GOOD);
}

//******************************************************************************
// Function:   iiReadWord16(pB)
// Parameters: pB      - pointer to board structure
//
// Returns:    True if everything appears copacetic.
//             False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Returns the word read from the data fifo specified by the board-structure
// pointer pB. Uses a 16-bit operation. Is called indirectly through
// pB->i2eReadWord.
//
//******************************************************************************
static unsigned short
iiReadWord16(i2eBordStrPtr pB)
{
	return inw(pB->i2eData);
}

//******************************************************************************
// Function:   iiReadWord8(pB)
// Parameters: pB      - pointer to board structure
//
// Returns:    True if everything appears copacetic.
//             False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Returns the word read from the data fifo specified by the board-structure
// pointer pB. Uses two 8-bit operations. Bytes are assumed to be LSB first. Is
// called indirectly through pB->i2eReadWord.
//
//******************************************************************************
static unsigned short
iiReadWord8(i2eBordStrPtr pB)
{
	unsigned short urs;

	urs = inb(pB->i2eData);

	return (inb(pB->i2eData) << 8) | urs;
}

//******************************************************************************
// Function:   iiWriteWord16(pB, value)
// Parameters: pB    - pointer to board structure
//             value - data to write
//
// Returns:    True if everything appears copacetic.
//             False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Writes the word 'value' to the data fifo specified by the board-structure
// pointer pB. Uses 16-bit operation. Is called indirectly through
// pB->i2eWriteWord.
//
//******************************************************************************
static void
iiWriteWord16(i2eBordStrPtr pB, unsigned short value)
{
	outw((int)value, pB->i2eData);
}

//******************************************************************************
// Function:   iiWriteWord8(pB, value)
// Parameters: pB    - pointer to board structure
//             value - data to write
//
// Returns:    True if everything appears copacetic.
//             False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Writes the word 'value' to the data fifo specified by the board-structure
// pointer pB. Uses two 8-bit operations (writes LSB first). Is called
// indirectly through pB->i2eWriteWord.
//
//******************************************************************************
static void
iiWriteWord8(i2eBordStrPtr pB, unsigned short value)
{
	outb((char)value, pB->i2eData);
	outb((char)(value >> 8), pB->i2eData);
}

//******************************************************************************
// Function:   iiWaitForTxEmptyII(pB, mSdelay)
// Parameters: pB      - pointer to board structure
//             mSdelay - period to wait before returning
//
// Returns:    True if the FIFO is empty.
//             False if it not empty in the required time: the pB->i2eError
//             field has the error.
//
// Description:
//
// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if
// not empty by the required time, returns false and error in pB->i2eError,
// otherwise returns true.
//
// mSdelay == 0 is taken to mean must be empty on the first test.
//
// This version operates on IntelliPort-II - style FIFO's
//
// Note this routine is organized so that if status is ok there is no delay at
// all called either before or after the test.  Is called indirectly through
// pB->i2eWaitForTxEmpty.
//
//******************************************************************************
static int
iiWaitForTxEmptyII(i2eBordStrPtr pB, int mSdelay)
{
	unsigned long	flags;
	int itemp;

	for (;;)
	{
		// This routine hinges on being able to see the "other" status register
		// (as seen by the local processor).  His incoming fifo is our outgoing
		// FIFO.
		//
		// By the nature of this routine, you would be using this as part of a
		// larger atomic context: i.e., you would use this routine to ensure the
		// fifo empty, then act on this information. Between these two halves, 
		// you will generally not want to service interrupts or in any way 
		// disrupt the assumptions implicit in the larger context.
		//
		// Even worse, however, this routine "shifts" the status register to 
		// point to the local status register which is not the usual situation.
		// Therefore for extra safety, we force the critical section to be
		// completely atomic, and pick up after ourselves before allowing any
		// interrupts of any kind.


		write_lock_irqsave(&Dl_spinlock, flags);
		outb(SEL_COMMAND, pB->i2ePointer);
		outb(SEL_CMD_SH, pB->i2ePointer);

		itemp = inb(pB->i2eStatus);

		outb(SEL_COMMAND, pB->i2ePointer);
		outb(SEL_CMD_UNSH, pB->i2ePointer);

		if (itemp & ST_IN_EMPTY)
		{
			I2_UPDATE_FIFO_ROOM(pB);
			write_unlock_irqrestore(&Dl_spinlock, flags);
			I2_COMPLETE(pB, I2EE_GOOD);
		}

		write_unlock_irqrestore(&Dl_spinlock, flags);

		if (mSdelay-- == 0)
			break;

		iiDelay(pB, 1);      /* 1 mS granularity on checking condition */
	}
	I2_COMPLETE(pB, I2EE_TXE_TIME);
}

//******************************************************************************
// Function:   iiWaitForTxEmptyIIEX(pB, mSdelay)
// Parameters: pB      - pointer to board structure
//             mSdelay - period to wait before returning
//
// Returns:    True if the FIFO is empty.
//             False if it not empty in the required time: the pB->i2eError
//             field has the error.
//
// Description:
//
// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if
// not empty by the required time, returns false and error in pB->i2eError,
// otherwise returns true.
//
// mSdelay == 0 is taken to mean must be empty on the first test.
//
// This version operates on IntelliPort-IIEX - style FIFO's
//
// Note this routine is organized so that if status is ok there is no delay at
// all called either before or after the test.  Is called indirectly through
// pB->i2eWaitForTxEmpty.
//
//******************************************************************************
static int
iiWaitForTxEmptyIIEX(i2eBordStrPtr pB, int mSdelay)
{
	unsigned long	flags;

	for (;;)
	{
		// By the nature of this routine, you would be using this as part of a
		// larger atomic context: i.e., you would use this routine to ensure the
		// fifo empty, then act on this information. Between these two halves,
		// you will generally not want to service interrupts or in any way
		// disrupt the assumptions implicit in the larger context.

		write_lock_irqsave(&Dl_spinlock, flags);

		if (inb(pB->i2eStatus) & STE_OUT_MT) {
			I2_UPDATE_FIFO_ROOM(pB);
			write_unlock_irqrestore(&Dl_spinlock, flags);
			I2_COMPLETE(pB, I2EE_GOOD);
		}
		write_unlock_irqrestore(&Dl_spinlock, flags);

		if (mSdelay-- == 0)
			break;

		iiDelay(pB, 1);      // 1 mS granularity on checking condition
	}
	I2_COMPLETE(pB, I2EE_TXE_TIME);
}

//******************************************************************************
// Function:   iiTxMailEmptyII(pB)
// Parameters: pB      - pointer to board structure
//
// Returns:    True if the transmit mailbox is empty.
//             False if it not empty.
//
// Description:
//
// Returns true or false according to whether the transmit mailbox is empty (and
// therefore able to accept more mail)
//
// This version operates on IntelliPort-II - style FIFO's
//
//******************************************************************************
static int
iiTxMailEmptyII(i2eBordStrPtr pB)
{
	int port = pB->i2ePointer;
	outb(SEL_OUTMAIL, port);
	return inb(port) == 0;
}

//******************************************************************************
// Function:   iiTxMailEmptyIIEX(pB)
// Parameters: pB      - pointer to board structure
//
// Returns:    True if the transmit mailbox is empty.
//             False if it not empty.
//
// Description:
//
// Returns true or false according to whether the transmit mailbox is empty (and
// therefore able to accept more mail)
//
// This version operates on IntelliPort-IIEX - style FIFO's
//
//******************************************************************************
static int
iiTxMailEmptyIIEX(i2eBordStrPtr pB)
{
	return !(inb(pB->i2eStatus) & STE_OUT_MAIL);
}

//******************************************************************************
// Function:   iiTrySendMailII(pB,mail)
// Parameters: pB   - pointer to board structure
//             mail - value to write to mailbox
//
// Returns:    True if the transmit mailbox is empty, and mail is sent.
//             False if it not empty.
//
// Description:
//
// If outgoing mailbox is empty, sends mail and returns true. If outgoing
// mailbox is not empty, returns false.
//
// This version operates on IntelliPort-II - style FIFO's
//
//******************************************************************************
static int
iiTrySendMailII(i2eBordStrPtr pB, unsigned char mail)
{
	int port = pB->i2ePointer;

	outb(SEL_OUTMAIL, port);
	if (inb(port) == 0) {
		outb(SEL_OUTMAIL, port);
		outb(mail, port);
		return 1;
	}
	return 0;
}

//******************************************************************************
// Function:   iiTrySendMailIIEX(pB,mail)
// Parameters: pB   - pointer to board structure
//             mail - value to write to mailbox
//
// Returns:    True if the transmit mailbox is empty, and mail is sent.
//             False if it not empty.
//
// Description:
//
// If outgoing mailbox is empty, sends mail and returns true. If outgoing
// mailbox is not empty, returns false.
//
// This version operates on IntelliPort-IIEX - style FIFO's
//
//******************************************************************************
static int
iiTrySendMailIIEX(i2eBordStrPtr pB, unsigned char mail)
{
	if (inb(pB->i2eStatus) & STE_OUT_MAIL)
		return 0;
	outb(mail, pB->i2eXMail);
	return 1;
}

//******************************************************************************
// Function:   iiGetMailII(pB,mail)
// Parameters: pB   - pointer to board structure
//
// Returns:    Mailbox data or NO_MAIL_HERE.
//
// Description:
//
// If no mail available, returns NO_MAIL_HERE otherwise returns the data from
// the mailbox, which is guaranteed != NO_MAIL_HERE.
//
// This version operates on IntelliPort-II - style FIFO's
//
//******************************************************************************
static unsigned short
iiGetMailII(i2eBordStrPtr pB)
{
	if (I2_HAS_MAIL(pB)) {
		outb(SEL_INMAIL, pB->i2ePointer);
		return inb(pB->i2ePointer);
	} else {
		return NO_MAIL_HERE;
	}
}

//******************************************************************************
// Function:   iiGetMailIIEX(pB,mail)
// Parameters: pB   - pointer to board structure
//
// Returns:    Mailbox data or NO_MAIL_HERE.
//
// Description:
//
// If no mail available, returns NO_MAIL_HERE otherwise returns the data from
// the mailbox, which is guaranteed != NO_MAIL_HERE.
//
// This version operates on IntelliPort-IIEX - style FIFO's
//
//******************************************************************************
static unsigned short
iiGetMailIIEX(i2eBordStrPtr pB)
{
	if (I2_HAS_MAIL(pB))
		return inb(pB->i2eXMail);
	else
		return NO_MAIL_HERE;
}

//******************************************************************************
// Function:   iiEnableMailIrqII(pB)
// Parameters: pB - pointer to board structure
//
// Returns:    Nothing
//
// Description:
//
// Enables board to interrupt host (only) by writing to host's in-bound mailbox.
//
// This version operates on IntelliPort-II - style FIFO's
//
//******************************************************************************
static void
iiEnableMailIrqII(i2eBordStrPtr pB)
{
	outb(SEL_MASK, pB->i2ePointer);
	outb(ST_IN_MAIL, pB->i2ePointer);
}

//******************************************************************************
// Function:   iiEnableMailIrqIIEX(pB)
// Parameters: pB - pointer to board structure
//
// Returns:    Nothing
//
// Description:
//
// Enables board to interrupt host (only) by writing to host's in-bound mailbox.
//
// This version operates on IntelliPort-IIEX - style FIFO's
//
//******************************************************************************
static void
iiEnableMailIrqIIEX(i2eBordStrPtr pB)
{
	outb(MX_IN_MAIL, pB->i2eXMask);
}

//******************************************************************************
// Function:   iiWriteMaskII(pB)
// Parameters: pB - pointer to board structure
//
// Returns:    Nothing
//
// Description:
//
// Writes arbitrary value to the mask register.
//
// This version operates on IntelliPort-II - style FIFO's
//
//******************************************************************************
static void
iiWriteMaskII(i2eBordStrPtr pB, unsigned char value)
{
	outb(SEL_MASK, pB->i2ePointer);
	outb(value, pB->i2ePointer);
}

//******************************************************************************
// Function:   iiWriteMaskIIEX(pB)
// Parameters: pB - pointer to board structure
//
// Returns:    Nothing
//
// Description:
//
// Writes arbitrary value to the mask register.
//
// This version operates on IntelliPort-IIEX - style FIFO's
//
//******************************************************************************
static void
iiWriteMaskIIEX(i2eBordStrPtr pB, unsigned char value)
{
	outb(value, pB->i2eXMask);
}

//******************************************************************************
// Function:   iiDownloadBlock(pB, pSource, isStandard)
// Parameters: pB         - pointer to board structure
//             pSource    - loadware block to download
//             isStandard - True if "standard" loadware, else false.
//
// Returns:    Success or Failure
//
// Description:
//
// Downloads a single block (at pSource)to the board referenced by pB. Caller
// sets isStandard to true/false according to whether the "standard" loadware is
// what's being loaded. The normal process, then, is to perform an iiInitialize
// to the board, then perform some number of iiDownloadBlocks using the returned
// state to determine when download is complete.
//
// Possible return values: (see I2ELLIS.H)
// II_DOWN_BADVALID
// II_DOWN_BADFILE
// II_DOWN_CONTINUING
// II_DOWN_GOOD
// II_DOWN_BAD
// II_DOWN_BADSTATE
// II_DOWN_TIMEOUT
//
// Uses the i2eState and i2eToLoad fields (initialized at iiInitialize) to
// determine whether this is the first block, whether to check for magic
// numbers, how many blocks there are to go...
//
//******************************************************************************
static int
iiDownloadBlock ( i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard)
{
	int itemp;
	int loadedFirst;

	if (pB->i2eValid != I2E_MAGIC) return II_DOWN_BADVALID;

	switch(pB->i2eState)
	{
	case II_STATE_READY:

		// Loading the first block after reset. Must check the magic number of the
		// loadfile, store the number of blocks we expect to load.
		if (pSource->e.loadMagic != MAGIC_LOADFILE)
		{
			return II_DOWN_BADFILE;
		}

		// Next we store the total number of blocks to load, including this one.
		pB->i2eToLoad = 1 + pSource->e.loadBlocksMore;

		// Set the state, store the version numbers. ('Cause this may have come
		// from a file - we might want to report these versions and revisions in
		// case of an error!
		pB->i2eState = II_STATE_LOADING;
		pB->i2eLVersion = pSource->e.loadVersion;
		pB->i2eLRevision = pSource->e.loadRevision;
		pB->i2eLSub = pSource->e.loadSubRevision;

		// The time and date of compilation is also available but don't bother
		// storing it for normal purposes.
		loadedFirst = 1;
		break;

	case II_STATE_LOADING:
		loadedFirst = 0;
		break;

	default:
		return II_DOWN_BADSTATE;
	}

	// Now we must be in the II_STATE_LOADING state, and we assume i2eToLoad
	// must be positive still, because otherwise we would have cleaned up last
	// time and set the state to II_STATE_LOADED.
	if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) {
		return II_DOWN_TIMEOUT;
	}

	if (!iiWriteBuf(pB, pSource->c, LOADWARE_BLOCK_SIZE)) {
		return II_DOWN_BADVALID;
	}

	// If we just loaded the first block, wait for the fifo to empty an extra
	// long time to allow for any special startup code in the firmware, like
	// sending status messages to the LCD's.

	if (loadedFirst) {
		if (!iiWaitForTxEmpty(pB, MAX_DLOAD_START_TIME)) {
			return II_DOWN_TIMEOUT;
		}
	}

	// Determine whether this was our last block!
	if (--(pB->i2eToLoad)) {
		return II_DOWN_CONTINUING;    // more to come...
	}

	// It WAS our last block: Clean up operations...
	// ...Wait for last buffer to drain from the board...
	if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) {
		return II_DOWN_TIMEOUT;
	}
	// If there were only a single block written, this would come back
	// immediately and be harmless, though not strictly necessary.
	itemp = MAX_DLOAD_ACK_TIME/10;
	while (--itemp) {
		if (I2_HAS_INPUT(pB)) {
			switch (inb(pB->i2eData)) {
			case LOADWARE_OK:
				pB->i2eState =
					isStandard ? II_STATE_STDLOADED :II_STATE_LOADED;

				// Some revisions of the bootstrap firmware (e.g. ISA-8 1.0.2)
				// will, // if there is a debug port attached, require some
				// time to send information to the debug port now. It will do
				// this before // executing any of the code we just downloaded.
				// It may take up to 700 milliseconds.
				if (pB->i2ePom.e.porDiag2 & POR_DEBUG_PORT) {
					iiDelay(pB, 700);
				}

				return II_DOWN_GOOD;

			case LOADWARE_BAD:
			default:
				return II_DOWN_BAD;
			}
		}

		iiDelay(pB, 10);      // 10 mS granularity on checking condition
	}

	// Drop-through --> timed out waiting for firmware confirmation

	pB->i2eState = II_STATE_BADLOAD;
	return II_DOWN_TIMEOUT;
}

//******************************************************************************
// Function:   iiDownloadAll(pB, pSource, isStandard, size)
// Parameters: pB         - pointer to board structure
//             pSource    - loadware block to download
//             isStandard - True if "standard" loadware, else false.
//             size       - size of data to download (in bytes)
//
// Returns:    Success or Failure
//
// Description:
//
// Given a pointer to a board structure, a pointer to the beginning of some
// loadware, whether it is considered the "standard loadware", and the size of
// the array in bytes loads the entire array to the board as loadware.
//
// Assumes the board has been freshly reset and the power-up reset message read.
// (i.e., in II_STATE_READY). Complains if state is bad, or if there seems to be
// too much or too little data to load, or if iiDownloadBlock complains.
//******************************************************************************
static int
iiDownloadAll(i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard, int size)
{
	int status;

	// We know (from context) board should be ready for the first block of
	// download.  Complain if not.
	if (pB->i2eState != II_STATE_READY) return II_DOWN_BADSTATE;

	while (size > 0) {
		size -= LOADWARE_BLOCK_SIZE;	// How much data should there be left to
										// load after the following operation ?

		// Note we just bump pSource by "one", because its size is actually that
		// of an entire block, same as LOADWARE_BLOCK_SIZE.
		status = iiDownloadBlock(pB, pSource++, isStandard);

		switch(status)
		{
		case II_DOWN_GOOD:
			return ( (size > 0) ? II_DOWN_OVER : II_DOWN_GOOD);

		case II_DOWN_CONTINUING:
			break;

		default:
			return status;
		}
	}

	// We shouldn't drop out: it means "while" caught us with nothing left to
	// download, yet the previous DownloadBlock did not return complete. Ergo,
	// not enough data to match the size byte in the header.
	return II_DOWN_UNDER;
}