aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/net/skfp/smt.c
blob: ffbfb1b79f97360975cd8da31d198591fde55037 (plain) (tree)
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
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
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557



















                                                                                
                         







                                                                        

























































                                                                             
                                                                     






















                                                                                  
                                                 






                                                      
                                                                     




























































                                                                           
                                                                         


































                                                                 
















































































































































































































































































































































































































































































































                                                                                
                                                                        









































































































































































































































































































































































































































































































































































































































































































































































































































































                                                                                  
                                                

































































































                                                                   
                                            




























































































                                                                               
                                        

















































































































































                                                                              
                                            














































































































































                                                                                


                                                           
                                                                




                        

                                       




                         
/******************************************************************************
 *
 *	(C)Copyright 1998,1999 SysKonnect,
 *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
 *
 *	See the file "skfddi.c" for further information.
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	The information in this file is provided "AS IS" without warranty.
 *
 ******************************************************************************/

#include "h/types.h"
#include "h/fddi.h"
#include "h/smc.h"
#include "h/smt_p.h"
#include <linux/bitrev.h>

#define KERNEL
#include "h/smtstate.h"

#ifndef	lint
static const char ID_sccs[] = "@(#)smt.c	2.43 98/11/23 (C) SK " ;
#endif

/*
 * FC in SMbuf
 */
#define m_fc(mb)	((mb)->sm_data[0])

#define SMT_TID_MAGIC	0x1f0a7b3c

#ifdef	DEBUG
static const char *const smt_type_name[] = {
	"SMT_00??", "SMT_INFO", "SMT_02??", "SMT_03??",
	"SMT_04??", "SMT_05??", "SMT_06??", "SMT_07??",
	"SMT_08??", "SMT_09??", "SMT_0A??", "SMT_0B??",
	"SMT_0C??", "SMT_0D??", "SMT_0E??", "SMT_NSA"
} ;

static const char *const smt_class_name[] = {
	"UNKNOWN","NIF","SIF_CONFIG","SIF_OPER","ECF","RAF","RDF",
	"SRF","PMF_GET","PMF_SET","ESF"
} ;
#endif
#define LAST_CLASS	(SMT_PMF_SET)

static const struct fddi_addr SMT_Unknown = {
	{ 0,0,0x1f,0,0,0 }
} ;

/*
 * external variables
 */
extern const struct fddi_addr fddi_broadcast ;

/*
 * external functions
 */
int pcm_status_twisted(struct s_smc *smc);

/*
 * function prototypes
 */
#ifdef	LITTLE_ENDIAN
static int smt_swap_short(u_short s);
#endif
static int mac_index(struct s_smc *smc, int mac);
static int phy_index(struct s_smc *smc, int phy);
static int mac_con_resource_index(struct s_smc *smc, int mac);
static int phy_con_resource_index(struct s_smc *smc, int phy);
static void smt_send_rdf(struct s_smc *smc, SMbuf *rej, int fc, int reason,
			 int local);
static void smt_send_nif(struct s_smc *smc, const struct fddi_addr *dest, 
			 int fc, u_long tid, int type, int local);
static void smt_send_ecf(struct s_smc *smc, struct fddi_addr *dest, int fc,
                         u_long tid, int type, int len);
static void smt_echo_test(struct s_smc *smc, int dna);
static void smt_send_sif_config(struct s_smc *smc, struct fddi_addr *dest,
				u_long tid, int local);
static void smt_send_sif_operation(struct s_smc *smc, struct fddi_addr *dest,
				   u_long tid, int local);
#ifdef LITTLE_ENDIAN
static void smt_string_swap(char *data, const char *format, int len);
#endif
static void smt_add_frame_len(SMbuf *mb, int len);
static void smt_fill_una(struct s_smc *smc, struct smt_p_una *una);
static void smt_fill_sde(struct s_smc *smc, struct smt_p_sde *sde);
static void smt_fill_state(struct s_smc *smc, struct smt_p_state *state);
static void smt_fill_timestamp(struct s_smc *smc, struct smt_p_timestamp *ts);
static void smt_fill_policy(struct s_smc *smc, struct smt_p_policy *policy);
static void smt_fill_latency(struct s_smc *smc, struct smt_p_latency *latency);
static void smt_fill_neighbor(struct s_smc *smc, struct smt_p_neighbor *neighbor);
static int smt_fill_path(struct s_smc *smc, struct smt_p_path *path);
static void smt_fill_mac_status(struct s_smc *smc, struct smt_p_mac_status *st);
static void smt_fill_lem(struct s_smc *smc, struct smt_p_lem *lem, int phy);
static void smt_fill_version(struct s_smc *smc, struct smt_p_version *vers);
static void smt_fill_fsc(struct s_smc *smc, struct smt_p_fsc *fsc);
static void smt_fill_mac_counter(struct s_smc *smc, struct smt_p_mac_counter *mc);
static void smt_fill_mac_fnc(struct s_smc *smc, struct smt_p_mac_fnc *fnc);
static void smt_fill_manufacturer(struct s_smc *smc, 
				  struct smp_p_manufacturer *man);
static void smt_fill_user(struct s_smc *smc, struct smp_p_user *user);
static void smt_fill_setcount(struct s_smc *smc, struct smt_p_setcount *setcount);
static void smt_fill_echo(struct s_smc *smc, struct smt_p_echo *echo, u_long seed,
			  int len);

static void smt_clear_una_dna(struct s_smc *smc);
static void smt_clear_old_una_dna(struct s_smc *smc);
#ifdef	CONCENTRATOR
static int entity_to_index(void);
#endif
static void update_dac(struct s_smc *smc, int report);
static int div_ratio(u_long upper, u_long lower);
#ifdef  USE_CAN_ADDR
static void	hwm_conv_can(struct s_smc *smc, char *data, int len);
#else
#define		hwm_conv_can(smc,data,len)
#endif


static inline int is_my_addr(const struct s_smc *smc, 
			     const struct fddi_addr *addr)
{
	return(*(short *)(&addr->a[0]) ==
		*(short *)(&smc->mib.m[MAC0].fddiMACSMTAddress.a[0])
	  && *(short *)(&addr->a[2]) ==
		*(short *)(&smc->mib.m[MAC0].fddiMACSMTAddress.a[2])
	  && *(short *)(&addr->a[4]) ==
		*(short *)(&smc->mib.m[MAC0].fddiMACSMTAddress.a[4])) ;
}

static inline int is_broadcast(const struct fddi_addr *addr)
{
	return(*(u_short *)(&addr->a[0]) == 0xffff &&
	       *(u_short *)(&addr->a[2]) == 0xffff &&
	       *(u_short *)(&addr->a[4]) == 0xffff ) ;
}

static inline int is_individual(const struct fddi_addr *addr)
{
	return(!(addr->a[0] & GROUP_ADDR)) ;
}

static inline int is_equal(const struct fddi_addr *addr1, 
			   const struct fddi_addr *addr2)
{
	return(*(u_short *)(&addr1->a[0]) == *(u_short *)(&addr2->a[0]) &&
	       *(u_short *)(&addr1->a[2]) == *(u_short *)(&addr2->a[2]) &&
	       *(u_short *)(&addr1->a[4]) == *(u_short *)(&addr2->a[4]) ) ;
}

/*
 * list of mandatory paras in frames
 */
static const u_short plist_nif[] = { SMT_P_UNA,SMT_P_SDE,SMT_P_STATE,0 } ;

/*
 * init SMT agent
 */
void smt_agent_init(struct s_smc *smc)
{
	int		i ;

	/*
	 * get MAC address
	 */
	smc->mib.m[MAC0].fddiMACSMTAddress = smc->hw.fddi_home_addr ;

	/*
	 * get OUI address from driver (bia == built-in-address)
	 */
	smc->mib.fddiSMTStationId.sid_oem[0] = 0 ;
	smc->mib.fddiSMTStationId.sid_oem[1] = 0 ;
	driver_get_bia(smc,&smc->mib.fddiSMTStationId.sid_node) ;
	for (i = 0 ; i < 6 ; i ++) {
		smc->mib.fddiSMTStationId.sid_node.a[i] =
			bitrev8(smc->mib.fddiSMTStationId.sid_node.a[i]);
	}
	smc->mib.fddiSMTManufacturerData[0] =
		smc->mib.fddiSMTStationId.sid_node.a[0] ;
	smc->mib.fddiSMTManufacturerData[1] =
		smc->mib.fddiSMTStationId.sid_node.a[1] ;
	smc->mib.fddiSMTManufacturerData[2] =
		smc->mib.fddiSMTStationId.sid_node.a[2] ;
	smc->sm.smt_tid = 0 ;
	smc->mib.m[MAC0].fddiMACDupAddressTest = DA_NONE ;
	smc->mib.m[MAC0].fddiMACUNDA_Flag = FALSE ;
#ifndef	SLIM_SMT
	smt_clear_una_dna(smc) ;
	smt_clear_old_una_dna(smc) ;
#endif
	for (i = 0 ; i < SMT_MAX_TEST ; i++)
		smc->sm.pend[i] = 0 ;
	smc->sm.please_reconnect = 0 ;
	smc->sm.uniq_ticks = 0 ;
}

/*
 * SMT task
 * forever
 *	delay 30 seconds
 *	send NIF
 *	check tvu & tvd
 * end
 */
void smt_agent_task(struct s_smc *smc)
{
	smt_timer_start(smc,&smc->sm.smt_timer, (u_long)1000000L,
		EV_TOKEN(EVENT_SMT,SM_TIMER)) ;
	DB_SMT("SMT agent task\n",0,0) ;
}

#ifndef SMT_REAL_TOKEN_CT
void smt_emulate_token_ct(struct s_smc *smc, int mac_index)
{
	u_long	count;
	u_long	time;


	time = smt_get_time();
	count =	((time - smc->sm.last_tok_time[mac_index]) *
					100)/TICKS_PER_SECOND;

	/*
	 * Only when ring is up we will have a token count. The
	 * flag is unfortunatly a single instance value. This
	 * doesn't matter now, because we currently have only
	 * one MAC instance.
	 */
	if (smc->hw.mac_ring_is_up){
		smc->mib.m[mac_index].fddiMACToken_Ct += count;
	}

	/* Remember current time */
	smc->sm.last_tok_time[mac_index] = time;

}
#endif

/*ARGSUSED1*/
void smt_event(struct s_smc *smc, int event)
{
	u_long		time ;
#ifndef SMT_REAL_TOKEN_CT
	int		i ;
#endif


	if (smc->sm.please_reconnect) {
		smc->sm.please_reconnect -- ;
		if (smc->sm.please_reconnect == 0) {
			/* Counted down */
			queue_event(smc,EVENT_ECM,EC_CONNECT) ;
		}
	}

	if (event == SM_FAST)
		return ;

	/*
	 * timer for periodic cleanup in driver
	 * reset and start the watchdog (FM2)
	 * ESS timer
	 * SBA timer
	 */
	smt_timer_poll(smc) ;
	smt_start_watchdog(smc) ;
#ifndef	SLIM_SMT
#ifndef BOOT
#ifdef	ESS
	ess_timer_poll(smc) ;
#endif
#endif
#ifdef	SBA
	sba_timer_poll(smc) ;
#endif

	smt_srf_event(smc,0,0,0) ;

#endif	/* no SLIM_SMT */

	time = smt_get_time() ;

	if (time - smc->sm.smt_last_lem >= TICKS_PER_SECOND*8) {
		/*
		 * Use 8 sec. for the time intervall, it simplifies the
		 * LER estimation.
		 */
		struct fddi_mib_m	*mib ;
		u_long			upper ;
		u_long			lower ;
		int			cond ;
		int			port;
		struct s_phy		*phy ;
		/*
		 * calculate LEM bit error rate
		 */
		sm_lem_evaluate(smc) ;
		smc->sm.smt_last_lem = time ;

		/*
		 * check conditions
		 */
#ifndef	SLIM_SMT
		mac_update_counter(smc) ;
		mib = smc->mib.m ;
		upper =
		(mib->fddiMACLost_Ct - mib->fddiMACOld_Lost_Ct) +
		(mib->fddiMACError_Ct - mib->fddiMACOld_Error_Ct) ;
		lower =
		(mib->fddiMACFrame_Ct - mib->fddiMACOld_Frame_Ct) +
		(mib->fddiMACLost_Ct - mib->fddiMACOld_Lost_Ct) ;
		mib->fddiMACFrameErrorRatio = div_ratio(upper,lower) ;

		cond =
			((!mib->fddiMACFrameErrorThreshold &&
			mib->fddiMACError_Ct != mib->fddiMACOld_Error_Ct) ||
			(mib->fddiMACFrameErrorRatio >
			mib->fddiMACFrameErrorThreshold)) ;

		if (cond != mib->fddiMACFrameErrorFlag)
			smt_srf_event(smc,SMT_COND_MAC_FRAME_ERROR,
				INDEX_MAC,cond) ;

		upper =
		(mib->fddiMACNotCopied_Ct - mib->fddiMACOld_NotCopied_Ct) ;
		lower =
		upper +
		(mib->fddiMACCopied_Ct - mib->fddiMACOld_Copied_Ct) ;
		mib->fddiMACNotCopiedRatio = div_ratio(upper,lower) ;

		cond =
			((!mib->fddiMACNotCopiedThreshold &&
			mib->fddiMACNotCopied_Ct !=
				mib->fddiMACOld_NotCopied_Ct)||
			(mib->fddiMACNotCopiedRatio >
			mib->fddiMACNotCopiedThreshold)) ;

		if (cond != mib->fddiMACNotCopiedFlag)
			smt_srf_event(smc,SMT_COND_MAC_NOT_COPIED,
				INDEX_MAC,cond) ;

		/*
		 * set old values
		 */
		mib->fddiMACOld_Frame_Ct = mib->fddiMACFrame_Ct ;
		mib->fddiMACOld_Copied_Ct = mib->fddiMACCopied_Ct ;
		mib->fddiMACOld_Error_Ct = mib->fddiMACError_Ct ;
		mib->fddiMACOld_Lost_Ct = mib->fddiMACLost_Ct ;
		mib->fddiMACOld_NotCopied_Ct = mib->fddiMACNotCopied_Ct ;

		/*
		 * Check port EBError Condition
		 */
		for (port = 0; port < NUMPHYS; port ++) {
			phy = &smc->y[port] ;

			if (!phy->mib->fddiPORTHardwarePresent) {
				continue;
			}

			cond = (phy->mib->fddiPORTEBError_Ct -
				phy->mib->fddiPORTOldEBError_Ct > 5) ;

			/* If ratio is more than 5 in 8 seconds
			 * Set the condition.
			 */
			smt_srf_event(smc,SMT_COND_PORT_EB_ERROR,
				(int) (INDEX_PORT+ phy->np) ,cond) ;

			/*
			 * set old values
			 */
			phy->mib->fddiPORTOldEBError_Ct =
				phy->mib->fddiPORTEBError_Ct ;
		}

#endif	/* no SLIM_SMT */
	}

#ifndef	SLIM_SMT

	if (time - smc->sm.smt_last_notify >= (u_long)
		(smc->mib.fddiSMTTT_Notify * TICKS_PER_SECOND) ) {
		/*
		 * we can either send an announcement or a request
		 * a request will trigger a reply so that we can update
		 * our dna
		 * note: same tid must be used until reply is received
		 */
		if (!smc->sm.pend[SMT_TID_NIF])
			smc->sm.pend[SMT_TID_NIF] = smt_get_tid(smc) ;
		smt_send_nif(smc,&fddi_broadcast, FC_SMT_NSA,
			smc->sm.pend[SMT_TID_NIF], SMT_REQUEST,0) ;
		smc->sm.smt_last_notify = time ;
	}

	/*
	 * check timer
	 */
	if (smc->sm.smt_tvu &&
	    time - smc->sm.smt_tvu > 228*TICKS_PER_SECOND) {
		DB_SMT("SMT : UNA expired\n",0,0) ;
		smc->sm.smt_tvu = 0 ;

		if (!is_equal(&smc->mib.m[MAC0].fddiMACUpstreamNbr,
			&SMT_Unknown)){
			/* Do not update unknown address */
			smc->mib.m[MAC0].fddiMACOldUpstreamNbr=
				smc->mib.m[MAC0].fddiMACUpstreamNbr ;
		}
		smc->mib.m[MAC0].fddiMACUpstreamNbr = SMT_Unknown ;
		smc->mib.m[MAC0].fddiMACUNDA_Flag = FALSE ;
		/*
		 * Make sure the fddiMACUNDA_Flag = FALSE is
		 * included in the SRF so we don't generate
		 * a separate SRF for the deassertion of this
		 * condition
		 */
		update_dac(smc,0) ;
		smt_srf_event(smc, SMT_EVENT_MAC_NEIGHBOR_CHANGE,
			INDEX_MAC,0) ;
	}
	if (smc->sm.smt_tvd &&
	    time - smc->sm.smt_tvd > 228*TICKS_PER_SECOND) {
		DB_SMT("SMT : DNA expired\n",0,0) ;
		smc->sm.smt_tvd = 0 ;
		if (!is_equal(&smc->mib.m[MAC0].fddiMACDownstreamNbr,
			&SMT_Unknown)){
			/* Do not update unknown address */
			smc->mib.m[MAC0].fddiMACOldDownstreamNbr=
				smc->mib.m[MAC0].fddiMACDownstreamNbr ;
		}
		smc->mib.m[MAC0].fddiMACDownstreamNbr = SMT_Unknown ;
		smt_srf_event(smc, SMT_EVENT_MAC_NEIGHBOR_CHANGE,
			INDEX_MAC,0) ;
	}

#endif	/* no SLIM_SMT */

#ifndef SMT_REAL_TOKEN_CT
	/*
	 * Token counter emulation section. If hardware supports the token
	 * count, the token counter will be updated in mac_update_counter.
	 */
	for (i = MAC0; i < NUMMACS; i++ ){
		if (time - smc->sm.last_tok_time[i] > 2*TICKS_PER_SECOND ){
			smt_emulate_token_ct( smc, i );
		}
	}
#endif

	smt_timer_start(smc,&smc->sm.smt_timer, (u_long)1000000L,
		EV_TOKEN(EVENT_SMT,SM_TIMER)) ;
}

static int div_ratio(u_long upper, u_long lower)
{
	if ((upper<<16L) < upper)
		upper = 0xffff0000L ;
	else
		upper <<= 16L ;
	if (!lower)
		return(0) ;
	return((int)(upper/lower)) ;
}

#ifndef	SLIM_SMT

/*
 * receive packet handler
 */
void smt_received_pack(struct s_smc *smc, SMbuf *mb, int fs)
/* int fs;  frame status */
{
	struct smt_header	*sm ;
	int			local ;

	int			illegal = 0 ;

	switch (m_fc(mb)) {
	case FC_SMT_INFO :
	case FC_SMT_LAN_LOC :
	case FC_SMT_LOC :
	case FC_SMT_NSA :
		break ;
	default :
		smt_free_mbuf(smc,mb) ;
		return ;
	}

	smc->mib.m[MAC0].fddiMACSMTCopied_Ct++ ;
	sm = smtod(mb,struct smt_header *) ;
	local = ((fs & L_INDICATOR) != 0) ;
	hwm_conv_can(smc,(char *)sm,12) ;

	/* check destination address */
	if (is_individual(&sm->smt_dest) && !is_my_addr(smc,&sm->smt_dest)) {
		smt_free_mbuf(smc,mb) ;
		return ;
	}
#if	0		/* for DUP recognition, do NOT filter them */
	/* ignore loop back packets */
	if (is_my_addr(smc,&sm->smt_source) && !local) {
		smt_free_mbuf(smc,mb) ;
		return ;
	}
#endif

	smt_swap_para(sm,(int) mb->sm_len,1) ;
	DB_SMT("SMT : received packet [%s] at 0x%x\n",
		smt_type_name[m_fc(mb) & 0xf],sm) ;
	DB_SMT("SMT : version %d, class %s\n",sm->smt_version,
		smt_class_name[(sm->smt_class>LAST_CLASS)?0 : sm->smt_class]) ;

#ifdef	SBA
	/*
	 * check if NSA frame
	 */
	if (m_fc(mb) == FC_SMT_NSA && sm->smt_class == SMT_NIF &&
		(sm->smt_type == SMT_ANNOUNCE || sm->smt_type == SMT_REQUEST)) {
			smc->sba.sm = sm ;
			sba(smc,NIF) ;
	}
#endif

	/*
	 * ignore any packet with NSA and A-indicator set
	 */
	if ( (fs & A_INDICATOR) && m_fc(mb) == FC_SMT_NSA) {
		DB_SMT("SMT : ignoring NSA with A-indicator set from %s\n",
			addr_to_string(&sm->smt_source),0) ;
		smt_free_mbuf(smc,mb) ;
		return ;
	}

	/*
	 * ignore frames with illegal length
	 */
	if (((sm->smt_class == SMT_ECF) && (sm->smt_len > SMT_MAX_ECHO_LEN)) ||
	    ((sm->smt_class != SMT_ECF) && (sm->smt_len > SMT_MAX_INFO_LEN))) {
		smt_free_mbuf(smc,mb) ;
		return ;
	}

	/*
	 * check SMT version
	 */
	switch (sm->smt_class) {
	case SMT_NIF :
	case SMT_SIF_CONFIG :
	case SMT_SIF_OPER :
	case SMT_ECF :
		if (sm->smt_version != SMT_VID)
			illegal = 1;
		break ;
	default :
		if (sm->smt_version != SMT_VID_2)
			illegal = 1;
		break ;
	}
	if (illegal) {
		DB_SMT("SMT : version = %d, dest = %s\n",
			sm->smt_version,addr_to_string(&sm->smt_source)) ;
		smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_VERSION,local) ;
		smt_free_mbuf(smc,mb) ;
		return ;
	}
	if ((sm->smt_len > mb->sm_len - sizeof(struct smt_header)) ||
	    ((sm->smt_len & 3) && (sm->smt_class != SMT_ECF))) {
		DB_SMT("SMT: info length error, len = %d\n",sm->smt_len,0) ;
		smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_LENGTH,local) ;
		smt_free_mbuf(smc,mb) ;
		return ;
	}
	switch (sm->smt_class) {
	case SMT_NIF :
		if (smt_check_para(smc,sm,plist_nif)) {
			DB_SMT("SMT: NIF with para problem, ignoring\n",0,0) ;
			break ;
		} ;
		switch (sm->smt_type) {
		case SMT_ANNOUNCE :
		case SMT_REQUEST :
			if (!(fs & C_INDICATOR) && m_fc(mb) == FC_SMT_NSA
				&& is_broadcast(&sm->smt_dest)) {
				struct smt_p_state	*st ;

				/* set my UNA */
				if (!is_equal(
					&smc->mib.m[MAC0].fddiMACUpstreamNbr,
					&sm->smt_source)) {
					DB_SMT("SMT : updated my UNA = %s\n",
					addr_to_string(&sm->smt_source),0) ;
					if (!is_equal(&smc->mib.m[MAC0].
					    fddiMACUpstreamNbr,&SMT_Unknown)){
					 /* Do not update unknown address */
					 smc->mib.m[MAC0].fddiMACOldUpstreamNbr=
					 smc->mib.m[MAC0].fddiMACUpstreamNbr ;
					}

					smc->mib.m[MAC0].fddiMACUpstreamNbr =
						sm->smt_source ;
					smt_srf_event(smc,
						SMT_EVENT_MAC_NEIGHBOR_CHANGE,
						INDEX_MAC,0) ;
					smt_echo_test(smc,0) ;
				}
				smc->sm.smt_tvu = smt_get_time() ;
				st = (struct smt_p_state *)
					sm_to_para(smc,sm,SMT_P_STATE) ;
				if (st) {
					smc->mib.m[MAC0].fddiMACUNDA_Flag =
					(st->st_dupl_addr & SMT_ST_MY_DUPA) ?
					TRUE : FALSE ;
					update_dac(smc,1) ;
				}
			}
			if ((sm->smt_type == SMT_REQUEST) &&
			    is_individual(&sm->smt_source) &&
			    ((!(fs & A_INDICATOR) && m_fc(mb) == FC_SMT_NSA) ||
			     (m_fc(mb) != FC_SMT_NSA))) {
				DB_SMT("SMT : replying to NIF request %s\n",
					addr_to_string(&sm->smt_source),0) ;
				smt_send_nif(smc,&sm->smt_source,
					FC_SMT_INFO,
					sm->smt_tid,
					SMT_REPLY,local) ;
			}
			break ;
		case SMT_REPLY :
			DB_SMT("SMT : received NIF response from %s\n",
				addr_to_string(&sm->smt_source),0) ;
			if (fs & A_INDICATOR) {
				smc->sm.pend[SMT_TID_NIF] = 0 ;
				DB_SMT("SMT : duplicate address\n",0,0) ;
				smc->mib.m[MAC0].fddiMACDupAddressTest =
					DA_FAILED ;
				smc->r.dup_addr_test = DA_FAILED ;
				queue_event(smc,EVENT_RMT,RM_DUP_ADDR) ;
				smc->mib.m[MAC0].fddiMACDA_Flag = TRUE ;
				update_dac(smc,1) ;
				break ;
			}
			if (sm->smt_tid == smc->sm.pend[SMT_TID_NIF]) {
				smc->sm.pend[SMT_TID_NIF] = 0 ;
				/* set my DNA */
				if (!is_equal(
					&smc->mib.m[MAC0].fddiMACDownstreamNbr,
					&sm->smt_source)) {
					DB_SMT("SMT : updated my DNA\n",0,0) ;
					if (!is_equal(&smc->mib.m[MAC0].
					 fddiMACDownstreamNbr, &SMT_Unknown)){
					 /* Do not update unknown address */
				smc->mib.m[MAC0].fddiMACOldDownstreamNbr =
					 smc->mib.m[MAC0].fddiMACDownstreamNbr ;
					}

					smc->mib.m[MAC0].fddiMACDownstreamNbr =
						sm->smt_source ;
					smt_srf_event(smc,
						SMT_EVENT_MAC_NEIGHBOR_CHANGE,
						INDEX_MAC,0) ;
					smt_echo_test(smc,1) ;
				}
				smc->mib.m[MAC0].fddiMACDA_Flag = FALSE ;
				update_dac(smc,1) ;
				smc->sm.smt_tvd = smt_get_time() ;
				smc->mib.m[MAC0].fddiMACDupAddressTest =
					DA_PASSED ;
				if (smc->r.dup_addr_test != DA_PASSED) {
					smc->r.dup_addr_test = DA_PASSED ;
					queue_event(smc,EVENT_RMT,RM_DUP_ADDR) ;
				}
			}
			else if (sm->smt_tid ==
				smc->sm.pend[SMT_TID_NIF_TEST]) {
				DB_SMT("SMT : NIF test TID ok\n",0,0) ;
			}
			else {
				DB_SMT("SMT : expected TID %lx, got %lx\n",
				smc->sm.pend[SMT_TID_NIF],sm->smt_tid) ;
			}
			break ;
		default :
			illegal = 2 ;
			break ;
		}
		break ;
	case SMT_SIF_CONFIG :	/* station information */
		if (sm->smt_type != SMT_REQUEST)
			break ;
		DB_SMT("SMT : replying to SIF Config request from %s\n",
			addr_to_string(&sm->smt_source),0) ;
		smt_send_sif_config(smc,&sm->smt_source,sm->smt_tid,local) ;
		break ;
	case SMT_SIF_OPER :	/* station information */
		if (sm->smt_type != SMT_REQUEST)
			break ;
		DB_SMT("SMT : replying to SIF Operation request from %s\n",
			addr_to_string(&sm->smt_source),0) ;
		smt_send_sif_operation(smc,&sm->smt_source,sm->smt_tid,local) ;
		break ;
	case SMT_ECF :		/* echo frame */
		switch (sm->smt_type) {
		case SMT_REPLY :
			smc->mib.priv.fddiPRIVECF_Reply_Rx++ ;
			DB_SMT("SMT: received ECF reply from %s\n",
				addr_to_string(&sm->smt_source),0) ;
			if (sm_to_para(smc,sm,SMT_P_ECHODATA) == NULL) {
				DB_SMT("SMT: ECHODATA missing\n",0,0) ;
				break ;
			}
			if (sm->smt_tid == smc->sm.pend[SMT_TID_ECF]) {
				DB_SMT("SMT : ECF test TID ok\n",0,0) ;
			}
			else if (sm->smt_tid == smc->sm.pend[SMT_TID_ECF_UNA]) {
				DB_SMT("SMT : ECF test UNA ok\n",0,0) ;
			}
			else if (sm->smt_tid == smc->sm.pend[SMT_TID_ECF_DNA]) {
				DB_SMT("SMT : ECF test DNA ok\n",0,0) ;
			}
			else {
				DB_SMT("SMT : expected TID %lx, got %lx\n",
					smc->sm.pend[SMT_TID_ECF],
					sm->smt_tid) ;
			}
			break ;
		case SMT_REQUEST :
			smc->mib.priv.fddiPRIVECF_Req_Rx++ ;
			{
			if (sm->smt_len && !sm_to_para(smc,sm,SMT_P_ECHODATA)) {
			DB_SMT("SMT: ECF with para problem,sending RDF\n",0,0) ;
				smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_LENGTH,
					local) ;
				break ;
			}
			DB_SMT("SMT - sending ECF reply to %s\n",
				addr_to_string(&sm->smt_source),0) ;

			/* set destination addr.  & reply */
			sm->smt_dest = sm->smt_source ;
			sm->smt_type = SMT_REPLY ;
			dump_smt(smc,sm,"ECF REPLY") ;
			smc->mib.priv.fddiPRIVECF_Reply_Tx++ ;
			smt_send_frame(smc,mb,FC_SMT_INFO,local) ;
			return ;		/* DON'T free mbuf */
			}
		default :
			illegal = 1 ;
			break ;
		}
		break ;
#ifndef	BOOT
	case SMT_RAF :		/* resource allocation */
#ifdef	ESS
		DB_ESSN(2,"ESS: RAF frame received\n",0,0) ;
		fs = ess_raf_received_pack(smc,mb,sm,fs) ;
#endif

#ifdef	SBA
		DB_SBAN(2,"SBA: RAF frame received\n",0,0) ;
		sba_raf_received_pack(smc,sm,fs) ;
#endif
		break ;
	case SMT_RDF :		/* request denied */
		smc->mib.priv.fddiPRIVRDF_Rx++ ;
		break ;
	case SMT_ESF :		/* extended service - not supported */
		if (sm->smt_type == SMT_REQUEST) {
			DB_SMT("SMT - received ESF, sending RDF\n",0,0) ;
			smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_CLASS,local) ;
		}
		break ;
	case SMT_PMF_GET :
	case SMT_PMF_SET :
		if (sm->smt_type != SMT_REQUEST)
			break ;
		/* update statistics */
		if (sm->smt_class == SMT_PMF_GET)
			smc->mib.priv.fddiPRIVPMF_Get_Rx++ ;
		else
			smc->mib.priv.fddiPRIVPMF_Set_Rx++ ;
		/*
		 * ignore PMF SET with I/G set
		 */
		if ((sm->smt_class == SMT_PMF_SET) &&
			!is_individual(&sm->smt_dest)) {
			DB_SMT("SMT: ignoring PMF-SET with I/G set\n",0,0) ;
			break ;
		}
		smt_pmf_received_pack(smc,mb, local) ;
		break ;
	case SMT_SRF :
		dump_smt(smc,sm,"SRF received") ;
		break ;
	default :
		if (sm->smt_type != SMT_REQUEST)
			break ;
		/*
		 * For frames with unknown class:
		 * we need to send a RDF frame according to 8.1.3.1.1,
		 * only if it is a REQUEST.
		 */
		DB_SMT("SMT : class = %d, send RDF to %s\n",
			sm->smt_class, addr_to_string(&sm->smt_source)) ;

		smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_CLASS,local) ;
		break ;
#endif
	}
	if (illegal) {
		DB_SMT("SMT: discarding invalid frame, reason = %d\n",
			illegal,0) ;
	}
	smt_free_mbuf(smc,mb) ;
}

static void update_dac(struct s_smc *smc, int report)
{
	int	cond ;

	cond = ( smc->mib.m[MAC0].fddiMACUNDA_Flag |
		smc->mib.m[MAC0].fddiMACDA_Flag) != 0 ;
	if (report && (cond != smc->mib.m[MAC0].fddiMACDuplicateAddressCond))
		smt_srf_event(smc, SMT_COND_MAC_DUP_ADDR,INDEX_MAC,cond) ;
	else
		smc->mib.m[MAC0].fddiMACDuplicateAddressCond = cond ;
}

/*
 * send SMT frame
 *	set source address
 *	set station ID
 *	send frame
 */
void smt_send_frame(struct s_smc *smc, SMbuf *mb, int fc, int local)
/* SMbuf *mb;	buffer to send */
/* int fc;	FC value */
{
	struct smt_header	*sm ;

	if (!smc->r.sm_ma_avail && !local) {
		smt_free_mbuf(smc,mb) ;
		return ;
	}
	sm = smtod(mb,struct smt_header *) ;
	sm->smt_source = smc->mib.m[MAC0].fddiMACSMTAddress ;
	sm->smt_sid = smc->mib.fddiSMTStationId ;

	smt_swap_para(sm,(int) mb->sm_len,0) ;		/* swap para & header */
	hwm_conv_can(smc,(char *)sm,12) ;		/* convert SA and DA */
	smc->mib.m[MAC0].fddiMACSMTTransmit_Ct++ ;
	smt_send_mbuf(smc,mb,local ? FC_SMT_LOC : fc) ;
}

/*
 * generate and send RDF
 */
static void smt_send_rdf(struct s_smc *smc, SMbuf *rej, int fc, int reason,
			 int local)
/* SMbuf *rej;	mbuf of offending frame */
/* int fc;	FC of denied frame */
/* int reason;	reason code */
{
	SMbuf	*mb ;
	struct smt_header	*sm ;	/* header of offending frame */
	struct smt_rdf	*rdf ;
	int		len ;
	int		frame_len ;

	sm = smtod(rej,struct smt_header *) ;
	if (sm->smt_type != SMT_REQUEST)
		return ;

	DB_SMT("SMT: sending RDF to %s,reason = 0x%x\n",
		addr_to_string(&sm->smt_source),reason) ;


	/*
	 * note: get framelength from MAC length, NOT from SMT header
	 * smt header length is included in sm_len
	 */
	frame_len = rej->sm_len ;

	if (!(mb=smt_build_frame(smc,SMT_RDF,SMT_REPLY,sizeof(struct smt_rdf))))
		return ;
	rdf = smtod(mb,struct smt_rdf *) ;
	rdf->smt.smt_tid = sm->smt_tid ;		/* use TID from sm */
	rdf->smt.smt_dest = sm->smt_source ;		/* set dest = source */

	/* set P12 */
	rdf->reason.para.p_type = SMT_P_REASON ;
	rdf->reason.para.p_len = sizeof(struct smt_p_reason) - PARA_LEN ;
	rdf->reason.rdf_reason = reason ;

	/* set P14 */
	rdf->version.para.p_type = SMT_P_VERSION ;
	rdf->version.para.p_len = sizeof(struct smt_p_version) - PARA_LEN ;
	rdf->version.v_pad = 0 ;
	rdf->version.v_n = 1 ;
	rdf->version.v_index = 1 ;
	rdf->version.v_version[0] = SMT_VID_2 ;
	rdf->version.v_pad2 = 0 ;

	/* set P13 */
	if ((unsigned) frame_len <= SMT_MAX_INFO_LEN - sizeof(*rdf) +
		2*sizeof(struct smt_header))
		len = frame_len ;
	else
		len = SMT_MAX_INFO_LEN - sizeof(*rdf) +
			2*sizeof(struct smt_header) ;
	/* make length multiple of 4 */
	len &= ~3 ;
	rdf->refused.para.p_type = SMT_P_REFUSED ;
	/* length of para is smt_frame + ref_fc */
	rdf->refused.para.p_len = len + 4 ;
	rdf->refused.ref_fc = fc ;

	/* swap it back */
	smt_swap_para(sm,frame_len,0) ;

	memcpy((char *) &rdf->refused.ref_header,(char *) sm,len) ;

	len -= sizeof(struct smt_header) ;
	mb->sm_len += len ;
	rdf->smt.smt_len += len ;

	dump_smt(smc,(struct smt_header *)rdf,"RDF") ;
	smc->mib.priv.fddiPRIVRDF_Tx++ ;
	smt_send_frame(smc,mb,FC_SMT_INFO,local) ;
}

/*
 * generate and send NIF
 */
static void smt_send_nif(struct s_smc *smc, const struct fddi_addr *dest, 
			 int fc, u_long tid, int type, int local)
/* struct fddi_addr *dest;	dest address */
/* int fc;			frame control */
/* u_long tid;			transaction id */
/* int type;			frame type */
{
	struct smt_nif	*nif ;
	SMbuf		*mb ;

	if (!(mb = smt_build_frame(smc,SMT_NIF,type,sizeof(struct smt_nif))))
		return ;
	nif = smtod(mb, struct smt_nif *) ;
	smt_fill_una(smc,&nif->una) ;	/* set UNA */
	smt_fill_sde(smc,&nif->sde) ;	/* set station descriptor */
	smt_fill_state(smc,&nif->state) ;	/* set state information */
#ifdef	SMT6_10
	smt_fill_fsc(smc,&nif->fsc) ;	/* set frame status cap. */
#endif
	nif->smt.smt_dest = *dest ;	/* destination address */
	nif->smt.smt_tid = tid ;	/* transaction ID */
	dump_smt(smc,(struct smt_header *)nif,"NIF") ;
	smt_send_frame(smc,mb,fc,local) ;
}

#ifdef	DEBUG
/*
 * send NIF request (test purpose)
 */
static void smt_send_nif_request(struct s_smc *smc, struct fddi_addr *dest)
{
	smc->sm.pend[SMT_TID_NIF_TEST] = smt_get_tid(smc) ;
	smt_send_nif(smc,dest, FC_SMT_INFO, smc->sm.pend[SMT_TID_NIF_TEST],
		SMT_REQUEST,0) ;
}

/*
 * send ECF request (test purpose)
 */
static void smt_send_ecf_request(struct s_smc *smc, struct fddi_addr *dest,
				 int len)
{
	smc->sm.pend[SMT_TID_ECF] = smt_get_tid(smc) ;
	smt_send_ecf(smc,dest, FC_SMT_INFO, smc->sm.pend[SMT_TID_ECF],
		SMT_REQUEST,len) ;
}
#endif

/*
 * echo test
 */
static void smt_echo_test(struct s_smc *smc, int dna)
{
	u_long	tid ;

	smc->sm.pend[dna ? SMT_TID_ECF_DNA : SMT_TID_ECF_UNA] =
		tid = smt_get_tid(smc) ;
	smt_send_ecf(smc, dna ?
		&smc->mib.m[MAC0].fddiMACDownstreamNbr :
		&smc->mib.m[MAC0].fddiMACUpstreamNbr,
		FC_SMT_INFO,tid, SMT_REQUEST, (SMT_TEST_ECHO_LEN & ~3)-8) ;
}

/*
 * generate and send ECF
 */
static void smt_send_ecf(struct s_smc *smc, struct fddi_addr *dest, int fc,
			 u_long tid, int type, int len)
/* struct fddi_addr *dest;	dest address */
/* int fc;			frame control */
/* u_long tid;			transaction id */
/* int type;			frame type */
/* int len;			frame length */
{
	struct smt_ecf	*ecf ;
	SMbuf		*mb ;

	if (!(mb = smt_build_frame(smc,SMT_ECF,type,SMT_ECF_LEN + len)))
		return ;
	ecf = smtod(mb, struct smt_ecf *) ;

	smt_fill_echo(smc,&ecf->ec_echo,tid,len) ;	/* set ECHO */
	ecf->smt.smt_dest = *dest ;	/* destination address */
	ecf->smt.smt_tid = tid ;	/* transaction ID */
	smc->mib.priv.fddiPRIVECF_Req_Tx++ ;
	smt_send_frame(smc,mb,fc,0) ;
}

/*
 * generate and send SIF config response
 */

static void smt_send_sif_config(struct s_smc *smc, struct fddi_addr *dest,
				u_long tid, int local)
/* struct fddi_addr *dest;	dest address */
/* u_long tid;			transaction id */
{
	struct smt_sif_config	*sif ;
	SMbuf			*mb ;
	int			len ;
	if (!(mb = smt_build_frame(smc,SMT_SIF_CONFIG,SMT_REPLY,
		SIZEOF_SMT_SIF_CONFIG)))
		return ;

	sif = smtod(mb, struct smt_sif_config *) ;
	smt_fill_timestamp(smc,&sif->ts) ;	/* set time stamp */
	smt_fill_sde(smc,&sif->sde) ;		/* set station descriptor */
	smt_fill_version(smc,&sif->version) ;	/* set version information */
	smt_fill_state(smc,&sif->state) ;	/* set state information */
	smt_fill_policy(smc,&sif->policy) ;	/* set station policy */
	smt_fill_latency(smc,&sif->latency);	/* set station latency */
	smt_fill_neighbor(smc,&sif->neighbor);	/* set station neighbor */
	smt_fill_setcount(smc,&sif->setcount) ;	/* set count */
	len = smt_fill_path(smc,&sif->path);	/* set station path descriptor*/
	sif->smt.smt_dest = *dest ;		/* destination address */
	sif->smt.smt_tid = tid ;		/* transaction ID */
	smt_add_frame_len(mb,len) ;		/* adjust length fields */
	dump_smt(smc,(struct smt_header *)sif,"SIF Configuration Reply") ;
	smt_send_frame(smc,mb,FC_SMT_INFO,local) ;
}

/*
 * generate and send SIF operation response
 */

static void smt_send_sif_operation(struct s_smc *smc, struct fddi_addr *dest,
				   u_long tid, int local)
/* struct fddi_addr *dest;	dest address */
/* u_long tid;			transaction id */
{
	struct smt_sif_operation *sif ;
	SMbuf			*mb ;
	int			ports ;
	int			i ;

	ports = NUMPHYS ;
#ifndef	CONCENTRATOR
	if (smc->s.sas == SMT_SAS)
		ports = 1 ;
#endif

	if (!(mb = smt_build_frame(smc,SMT_SIF_OPER,SMT_REPLY,
		SIZEOF_SMT_SIF_OPERATION+ports*sizeof(struct smt_p_lem))))
		return ;
	sif = smtod(mb, struct smt_sif_operation *) ;
	smt_fill_timestamp(smc,&sif->ts) ;	/* set time stamp */
	smt_fill_mac_status(smc,&sif->status) ; /* set mac status */
	smt_fill_mac_counter(smc,&sif->mc) ; /* set mac counter field */
	smt_fill_mac_fnc(smc,&sif->fnc) ; /* set frame not copied counter */
	smt_fill_manufacturer(smc,&sif->man) ; /* set manufacturer field */
	smt_fill_user(smc,&sif->user) ;		/* set user field */
	smt_fill_setcount(smc,&sif->setcount) ;	/* set count */
	/*
	 * set link error mon information
	 */
	if (ports == 1) {
		smt_fill_lem(smc,sif->lem,PS) ;
	}
	else {
		for (i = 0 ; i < ports ; i++) {
			smt_fill_lem(smc,&sif->lem[i],i) ;
		}
	}

	sif->smt.smt_dest = *dest ;	/* destination address */
	sif->smt.smt_tid = tid ;	/* transaction ID */
	dump_smt(smc,(struct smt_header *)sif,"SIF Operation Reply") ;
	smt_send_frame(smc,mb,FC_SMT_INFO,local) ;
}

/*
 * get and initialize SMT frame
 */
SMbuf *smt_build_frame(struct s_smc *smc, int class, int type,
				  int length)
{
	SMbuf			*mb ;
	struct smt_header	*smt ;

#if	0
	if (!smc->r.sm_ma_avail) {
		return(0) ;
	}
#endif
	if (!(mb = smt_get_mbuf(smc)))
		return(mb) ;

	mb->sm_len = length ;
	smt = smtod(mb, struct smt_header *) ;
	smt->smt_dest = fddi_broadcast ; /* set dest = broadcast */
	smt->smt_class = class ;
	smt->smt_type = type ;
	switch (class) {
	case SMT_NIF :
	case SMT_SIF_CONFIG :
	case SMT_SIF_OPER :
	case SMT_ECF :
		smt->smt_version = SMT_VID ;
		break ;
	default :
		smt->smt_version = SMT_VID_2 ;
		break ;
	}
	smt->smt_tid = smt_get_tid(smc) ;	/* set transaction ID */
	smt->smt_pad = 0 ;
	smt->smt_len = length - sizeof(struct smt_header) ;
	return(mb) ;
}

static void smt_add_frame_len(SMbuf *mb, int len)
{
	struct smt_header	*smt ;

	smt = smtod(mb, struct smt_header *) ;
	smt->smt_len += len ;
	mb->sm_len += len ;
}



/*
 * fill values in UNA parameter
 */
static void smt_fill_una(struct s_smc *smc, struct smt_p_una *una)
{
	SMTSETPARA(una,SMT_P_UNA) ;
	una->una_pad = 0 ;
	una->una_node = smc->mib.m[MAC0].fddiMACUpstreamNbr ;
}

/*
 * fill values in SDE parameter
 */
static void smt_fill_sde(struct s_smc *smc, struct smt_p_sde *sde)
{
	SMTSETPARA(sde,SMT_P_SDE) ;
	sde->sde_non_master = smc->mib.fddiSMTNonMaster_Ct ;
	sde->sde_master = smc->mib.fddiSMTMaster_Ct ;
	sde->sde_mac_count = NUMMACS ;		/* only 1 MAC */
#ifdef	CONCENTRATOR
	sde->sde_type = SMT_SDE_CONCENTRATOR ;
#else
	sde->sde_type = SMT_SDE_STATION ;
#endif
}

/*
 * fill in values in station state parameter
 */
static void smt_fill_state(struct s_smc *smc, struct smt_p_state *state)
{
	int	top ;
	int	twist ;

	SMTSETPARA(state,SMT_P_STATE) ;
	state->st_pad = 0 ;

	/* determine topology */
	top = 0 ;
	if (smc->mib.fddiSMTPeerWrapFlag) {
		top |= SMT_ST_WRAPPED ;		/* state wrapped */
	}
#ifdef	CONCENTRATOR
	if (cfm_status_unattached(smc)) {
		top |= SMT_ST_UNATTACHED ;	/* unattached concentrator */
	}
#endif
	if ((twist = pcm_status_twisted(smc)) & 1) {
		top |= SMT_ST_TWISTED_A ;	/* twisted cable */
	}
	if (twist & 2) {
		top |= SMT_ST_TWISTED_B ;	/* twisted cable */
	}
#ifdef	OPT_SRF
	top |= SMT_ST_SRF ;
#endif
	if (pcm_rooted_station(smc))
		top |= SMT_ST_ROOTED_S ;
	if (smc->mib.a[0].fddiPATHSbaPayload != 0)
		top |= SMT_ST_SYNC_SERVICE ;
	state->st_topology = top ;
	state->st_dupl_addr =
		((smc->mib.m[MAC0].fddiMACDA_Flag ? SMT_ST_MY_DUPA : 0 ) |
		 (smc->mib.m[MAC0].fddiMACUNDA_Flag ? SMT_ST_UNA_DUPA : 0)) ;
}

/*
 * fill values in timestamp parameter
 */
static void smt_fill_timestamp(struct s_smc *smc, struct smt_p_timestamp *ts)
{

	SMTSETPARA(ts,SMT_P_TIMESTAMP) ;
	smt_set_timestamp(smc,ts->ts_time) ;
}

void smt_set_timestamp(struct s_smc *smc, u_char *p)
{
	u_long	time ;
	u_long	utime ;

	/*
	 * timestamp is 64 bits long ; resolution is 80 nS
	 * our clock resolution is 10mS
	 * 10mS/80ns = 125000 ~ 2^17 = 131072
	 */
	utime = smt_get_time() ;
	time = utime * 100 ;
	time /= TICKS_PER_SECOND ;
	p[0] = 0 ;
	p[1] = (u_char)((time>>(8+8+8+8-1)) & 1) ;
	p[2] = (u_char)(time>>(8+8+8-1)) ;
	p[3] = (u_char)(time>>(8+8-1)) ;
	p[4] = (u_char)(time>>(8-1)) ;
	p[5] = (u_char)(time<<1) ;
	p[6] = (u_char)(smc->sm.uniq_ticks>>8) ;
	p[7] = (u_char)smc->sm.uniq_ticks ;
	/*
	 * make sure we don't wrap: restart whenever the upper digits change
	 */
	if (utime != smc->sm.uniq_time) {
		smc->sm.uniq_ticks = 0 ;
	}
	smc->sm.uniq_ticks++ ;
	smc->sm.uniq_time = utime ;
}

/*
 * fill values in station policy parameter
 */
static void smt_fill_policy(struct s_smc *smc, struct smt_p_policy *policy)
{
	int	i ;
	u_char	*map ;
	u_short	in ;
	u_short	out ;

	/*
	 * MIB para 101b (fddiSMTConnectionPolicy) coding
	 * is different from 0005 coding
	 */
	static u_char	ansi_weirdness[16] = {
		0,7,5,3,8,1,6,4,9,10,2,11,12,13,14,15
	} ;
	SMTSETPARA(policy,SMT_P_POLICY) ;

	out = 0 ;
	in = smc->mib.fddiSMTConnectionPolicy ;
	for (i = 0, map = ansi_weirdness ; i < 16 ; i++) {
		if (in & 1)
			out |= (1<<*map) ;
		in >>= 1 ;
		map++ ;
	}
	policy->pl_config = smc->mib.fddiSMTConfigPolicy ;
	policy->pl_connect = out ;
}

/*
 * fill values in latency equivalent parameter
 */
static void smt_fill_latency(struct s_smc *smc, struct smt_p_latency *latency)
{
	SMTSETPARA(latency,SMT_P_LATENCY) ;

	latency->lt_phyout_idx1 = phy_index(smc,0) ;
	latency->lt_latency1 = 10 ;	/* in octets (byte clock) */
	/*
	 * note: latency has two phy entries by definition
	 * for a SAS, the 2nd one is null
	 */
	if (smc->s.sas == SMT_DAS) {
		latency->lt_phyout_idx2 = phy_index(smc,1) ;
		latency->lt_latency2 = 10 ;	/* in octets (byte clock) */
	}
	else {
		latency->lt_phyout_idx2 = 0 ;
		latency->lt_latency2 = 0 ;
	}
}

/*
 * fill values in MAC neighbors parameter
 */
static void smt_fill_neighbor(struct s_smc *smc, struct smt_p_neighbor *neighbor)
{
	SMTSETPARA(neighbor,SMT_P_NEIGHBORS) ;

	neighbor->nb_mib_index = INDEX_MAC ;
	neighbor->nb_mac_index = mac_index(smc,1) ;
	neighbor->nb_una = smc->mib.m[MAC0].fddiMACUpstreamNbr ;
	neighbor->nb_dna = smc->mib.m[MAC0].fddiMACDownstreamNbr ;
}

/*
 * fill values in path descriptor
 */
#ifdef	CONCENTRATOR
#define ALLPHYS	NUMPHYS
#else
#define ALLPHYS	((smc->s.sas == SMT_SAS) ? 1 : 2)
#endif

static int smt_fill_path(struct s_smc *smc, struct smt_p_path *path)
{
	SK_LOC_DECL(int,type) ;
	SK_LOC_DECL(int,state) ;
	SK_LOC_DECL(int,remote) ;
	SK_LOC_DECL(int,mac) ;
	int	len ;
	int	p ;
	int	physp ;
	struct smt_phy_rec	*phy ;
	struct smt_mac_rec	*pd_mac ;

	len =	PARA_LEN +
		sizeof(struct smt_mac_rec) * NUMMACS +
		sizeof(struct smt_phy_rec) * ALLPHYS ;
	path->para.p_type = SMT_P_PATH ;
	path->para.p_len = len - PARA_LEN ;

	/* PHYs */
	for (p = 0,phy = path->pd_phy ; p < ALLPHYS ; p++, phy++) {
		physp = p ;
#ifndef	CONCENTRATOR
		if (smc->s.sas == SMT_SAS)
			physp = PS ;
#endif
		pcm_status_state(smc,physp,&type,&state,&remote,&mac) ;
#ifdef	LITTLE_ENDIAN
		phy->phy_mib_index = smt_swap_short((u_short)p+INDEX_PORT) ;
#else
		phy->phy_mib_index = p+INDEX_PORT ;
#endif
		phy->phy_type = type ;
		phy->phy_connect_state = state ;
		phy->phy_remote_type = remote ;
		phy->phy_remote_mac = mac ;
		phy->phy_resource_idx = phy_con_resource_index(smc,p) ;
	}

	/* MAC */
	pd_mac = (struct smt_mac_rec *) phy ;
	pd_mac->mac_addr = smc->mib.m[MAC0].fddiMACSMTAddress ;
	pd_mac->mac_resource_idx = mac_con_resource_index(smc,1) ;
	return(len) ;
}

/*
 * fill values in mac status
 */
static void smt_fill_mac_status(struct s_smc *smc, struct smt_p_mac_status *st)
{
	SMTSETPARA(st,SMT_P_MAC_STATUS) ;

	st->st_mib_index = INDEX_MAC ;
	st->st_mac_index = mac_index(smc,1) ;

	mac_update_counter(smc) ;
	/*
	 * timer values are represented in SMT as 2's complement numbers
	 * units :	internal :  2's complement BCLK
	 */
	st->st_t_req = smc->mib.m[MAC0].fddiMACT_Req ;
	st->st_t_neg = smc->mib.m[MAC0].fddiMACT_Neg ;
	st->st_t_max = smc->mib.m[MAC0].fddiMACT_Max ;
	st->st_tvx_value = smc->mib.m[MAC0].fddiMACTvxValue ;
	st->st_t_min = smc->mib.m[MAC0].fddiMACT_Min ;

	st->st_sba = smc->mib.a[PATH0].fddiPATHSbaPayload ;
	st->st_frame_ct = smc->mib.m[MAC0].fddiMACFrame_Ct ;
	st->st_error_ct = smc->mib.m[MAC0].fddiMACError_Ct ;
	st->st_lost_ct = smc->mib.m[MAC0].fddiMACLost_Ct ;
}

/*
 * fill values in LEM status
 */
static void smt_fill_lem(struct s_smc *smc, struct smt_p_lem *lem, int phy)
{
	struct fddi_mib_p	*mib ;

	mib = smc->y[phy].mib ;

	SMTSETPARA(lem,SMT_P_LEM) ;
	lem->lem_mib_index = phy+INDEX_PORT ;
	lem->lem_phy_index = phy_index(smc,phy) ;
	lem->lem_pad2 = 0 ;
	lem->lem_cutoff = mib->fddiPORTLer_Cutoff ;
	lem->lem_alarm = mib->fddiPORTLer_Alarm ;
	/* long term bit error rate */
	lem->lem_estimate = mib->fddiPORTLer_Estimate ;
	/* # of rejected connections */
	lem->lem_reject_ct = mib->fddiPORTLem_Reject_Ct ;
	lem->lem_ct = mib->fddiPORTLem_Ct ;	/* total number of errors */
}

/*
 * fill version parameter
 */
static void smt_fill_version(struct s_smc *smc, struct smt_p_version *vers)
{
	SK_UNUSED(smc) ;
	SMTSETPARA(vers,SMT_P_VERSION) ;
	vers->v_pad = 0 ;
	vers->v_n = 1 ;				/* one version is enough .. */
	vers->v_index = 1 ;
	vers->v_version[0] = SMT_VID_2 ;
	vers->v_pad2 = 0 ;
}

#ifdef	SMT6_10
/*
 * fill frame status capabilities
 */
/*
 * note: this para 200B is NOT in swap table, because it's also set in
 * PMF add_para
 */
static void smt_fill_fsc(struct s_smc *smc, struct smt_p_fsc *fsc)
{
	SK_UNUSED(smc) ;
	SMTSETPARA(fsc,SMT_P_FSC) ;
	fsc->fsc_pad0 = 0 ;
	fsc->fsc_mac_index = INDEX_MAC ;	/* this is MIB ; MIB is NOT
						 * mac_index ()i !
						 */
	fsc->fsc_pad1 = 0 ;
	fsc->fsc_value = FSC_TYPE0 ;		/* "normal" node */
#ifdef	LITTLE_ENDIAN
	fsc->fsc_mac_index = smt_swap_short(INDEX_MAC) ;
	fsc->fsc_value = smt_swap_short(FSC_TYPE0) ;
#endif
}
#endif

/*
 * fill mac counter field
 */
static void smt_fill_mac_counter(struct s_smc *smc, struct smt_p_mac_counter *mc)
{
	SMTSETPARA(mc,SMT_P_MAC_COUNTER) ;
	mc->mc_mib_index = INDEX_MAC ;
	mc->mc_index = mac_index(smc,1) ;
	mc->mc_receive_ct = smc->mib.m[MAC0].fddiMACCopied_Ct ;
	mc->mc_transmit_ct =  smc->mib.m[MAC0].fddiMACTransmit_Ct ;
}

/*
 * fill mac frame not copied counter
 */
static void smt_fill_mac_fnc(struct s_smc *smc, struct smt_p_mac_fnc *fnc)
{
	SMTSETPARA(fnc,SMT_P_MAC_FNC) ;
	fnc->nc_mib_index = INDEX_MAC ;
	fnc->nc_index = mac_index(smc,1) ;
	fnc->nc_counter = smc->mib.m[MAC0].fddiMACNotCopied_Ct ;
}


/*
 * fill manufacturer field
 */
static void smt_fill_manufacturer(struct s_smc *smc, 
				  struct smp_p_manufacturer *man)
{
	SMTSETPARA(man,SMT_P_MANUFACTURER) ;
	memcpy((char *) man->mf_data,
		(char *) smc->mib.fddiSMTManufacturerData,
		sizeof(man->mf_data)) ;
}

/*
 * fill user field
 */
static void smt_fill_user(struct s_smc *smc, struct smp_p_user *user)
{
	SMTSETPARA(user,SMT_P_USER) ;
	memcpy((char *) user->us_data,
		(char *) smc->mib.fddiSMTUserData,
		sizeof(user->us_data)) ;
}

/*
 * fill set count
 */
static void smt_fill_setcount(struct s_smc *smc, struct smt_p_setcount *setcount)
{
	SK_UNUSED(smc) ;
	SMTSETPARA(setcount,SMT_P_SETCOUNT) ;
	setcount->count = smc->mib.fddiSMTSetCount.count ;
	memcpy((char *)setcount->timestamp,
		(char *)smc->mib.fddiSMTSetCount.timestamp,8) ;
}

/*
 * fill echo data
 */
static void smt_fill_echo(struct s_smc *smc, struct smt_p_echo *echo, u_long seed,
			  int len)
{
	u_char	*p ;

	SK_UNUSED(smc) ;
	SMTSETPARA(echo,SMT_P_ECHODATA) ;
	echo->para.p_len = len ;
	for (p = echo->ec_data ; len ; len--) {
		*p++ = (u_char) seed ;
		seed += 13 ;
	}
}

/*
 * clear DNA and UNA
 * called from CFM if configuration changes
 */
static void smt_clear_una_dna(struct s_smc *smc)
{
	smc->mib.m[MAC0].fddiMACUpstreamNbr = SMT_Unknown ;
	smc->mib.m[MAC0].fddiMACDownstreamNbr = SMT_Unknown ;
}

static void smt_clear_old_una_dna(struct s_smc *smc)
{
	smc->mib.m[MAC0].fddiMACOldUpstreamNbr = SMT_Unknown ;
	smc->mib.m[MAC0].fddiMACOldDownstreamNbr = SMT_Unknown ;
}

u_long smt_get_tid(struct s_smc *smc)
{
	u_long	tid ;
	while ((tid = ++(smc->sm.smt_tid) ^ SMT_TID_MAGIC) == 0)
		;
	return(tid & 0x3fffffffL) ;
}


/*
 * table of parameter lengths
 */
static const struct smt_pdef {
	int	ptype ;
	int	plen ;
	const char	*pswap ;
} smt_pdef[] = {
	{ SMT_P_UNA,	sizeof(struct smt_p_una) ,
		SWAP_SMT_P_UNA					} ,
	{ SMT_P_SDE,	sizeof(struct smt_p_sde) ,
		SWAP_SMT_P_SDE					} ,
	{ SMT_P_STATE,	sizeof(struct smt_p_state) ,
		SWAP_SMT_P_STATE				} ,
	{ SMT_P_TIMESTAMP,sizeof(struct smt_p_timestamp) ,
		SWAP_SMT_P_TIMESTAMP				} ,
	{ SMT_P_POLICY,	sizeof(struct smt_p_policy) ,
		SWAP_SMT_P_POLICY				} ,
	{ SMT_P_LATENCY,	sizeof(struct smt_p_latency) ,
		SWAP_SMT_P_LATENCY				} ,
	{ SMT_P_NEIGHBORS,sizeof(struct smt_p_neighbor) ,
		SWAP_SMT_P_NEIGHBORS				} ,
	{ SMT_P_PATH,	sizeof(struct smt_p_path) ,
		SWAP_SMT_P_PATH					} ,
	{ SMT_P_MAC_STATUS,sizeof(struct smt_p_mac_status) ,
		SWAP_SMT_P_MAC_STATUS				} ,
	{ SMT_P_LEM,	sizeof(struct smt_p_lem) ,
		SWAP_SMT_P_LEM					} ,
	{ SMT_P_MAC_COUNTER,sizeof(struct smt_p_mac_counter) ,
		SWAP_SMT_P_MAC_COUNTER				} ,
	{ SMT_P_MAC_FNC,sizeof(struct smt_p_mac_fnc) ,
		SWAP_SMT_P_MAC_FNC				} ,
	{ SMT_P_PRIORITY,sizeof(struct smt_p_priority) ,
		SWAP_SMT_P_PRIORITY				} ,
	{ SMT_P_EB,sizeof(struct smt_p_eb) ,
		SWAP_SMT_P_EB					} ,
	{ SMT_P_MANUFACTURER,sizeof(struct smp_p_manufacturer) ,
		SWAP_SMT_P_MANUFACTURER				} ,
	{ SMT_P_REASON,	sizeof(struct smt_p_reason) ,
		SWAP_SMT_P_REASON				} ,
	{ SMT_P_REFUSED, sizeof(struct smt_p_refused) ,
		SWAP_SMT_P_REFUSED				} ,
	{ SMT_P_VERSION, sizeof(struct smt_p_version) ,
		SWAP_SMT_P_VERSION				} ,
#ifdef ESS
	{ SMT_P0015, sizeof(struct smt_p_0015) , SWAP_SMT_P0015 } ,
	{ SMT_P0016, sizeof(struct smt_p_0016) , SWAP_SMT_P0016 } ,
	{ SMT_P0017, sizeof(struct smt_p_0017) , SWAP_SMT_P0017 } ,
	{ SMT_P0018, sizeof(struct smt_p_0018) , SWAP_SMT_P0018 } ,
	{ SMT_P0019, sizeof(struct smt_p_0019) , SWAP_SMT_P0019 } ,
	{ SMT_P001A, sizeof(struct smt_p_001a) , SWAP_SMT_P001A } ,
	{ SMT_P001B, sizeof(struct smt_p_001b) , SWAP_SMT_P001B } ,
	{ SMT_P001C, sizeof(struct smt_p_001c) , SWAP_SMT_P001C } ,
	{ SMT_P001D, sizeof(struct smt_p_001d) , SWAP_SMT_P001D } ,
#endif
#if	0
	{ SMT_P_FSC,	sizeof(struct smt_p_fsc) ,
		SWAP_SMT_P_FSC					} ,
#endif

	{ SMT_P_SETCOUNT,0,	SWAP_SMT_P_SETCOUNT		} ,
	{ SMT_P1048,	0,	SWAP_SMT_P1048			} ,
	{ SMT_P208C,	0,	SWAP_SMT_P208C			} ,
	{ SMT_P208D,	0,	SWAP_SMT_P208D			} ,
	{ SMT_P208E,	0,	SWAP_SMT_P208E			} ,
	{ SMT_P208F,	0,	SWAP_SMT_P208F			} ,
	{ SMT_P2090,	0,	SWAP_SMT_P2090			} ,
#ifdef	ESS
	{ SMT_P320B, sizeof(struct smt_p_320b) , SWAP_SMT_P320B } ,
	{ SMT_P320F, sizeof(struct smt_p_320f) , SWAP_SMT_P320F } ,
	{ SMT_P3210, sizeof(struct smt_p_3210) , SWAP_SMT_P3210 } ,
#endif
	{ SMT_P4050,	0,	SWAP_SMT_P4050			} ,
	{ SMT_P4051,	0,	SWAP_SMT_P4051			} ,
	{ SMT_P4052,	0,	SWAP_SMT_P4052			} ,
	{ SMT_P4053,	0,	SWAP_SMT_P4053			} ,
} ;

#define N_SMT_PLEN	ARRAY_SIZE(smt_pdef)

int smt_check_para(struct s_smc *smc, struct smt_header	*sm,
		   const u_short list[])
{
	const u_short		*p = list ;
	while (*p) {
		if (!sm_to_para(smc,sm,(int) *p)) {
			DB_SMT("SMT: smt_check_para - missing para %x\n",*p,0);
			return(-1) ;
		}
		p++ ;
	}
	return(0) ;
}

void *sm_to_para(struct s_smc *smc, struct smt_header *sm, int para)
{
	char	*p ;
	int	len ;
	int	plen ;
	void	*found = NULL;

	SK_UNUSED(smc) ;

	len = sm->smt_len ;
	p = (char *)(sm+1) ;		/* pointer to info */
	while (len > 0 ) {
		if (((struct smt_para *)p)->p_type == para)
			found = (void *) p ;
		plen = ((struct smt_para *)p)->p_len + PARA_LEN ;
		p += plen ;
		len -= plen ;
		if (len < 0) {
			DB_SMT("SMT : sm_to_para - length error %d\n",plen,0) ;
			return NULL;
		}
		if ((plen & 3) && (para != SMT_P_ECHODATA)) {
			DB_SMT("SMT : sm_to_para - odd length %d\n",plen,0) ;
			return NULL;
		}
		if (found)
			return(found) ;
	}
	return NULL;
}

#if	0
/*
 * send ANTC data test frame
 */
void fddi_send_antc(struct s_smc *smc, struct fddi_addr *dest)
{
	SK_UNUSED(smc) ;
	SK_UNUSED(dest) ;
#if	0
	SMbuf			*mb ;
	struct smt_header	*smt ;
	int			i ;
	char			*p ;

	mb = smt_get_mbuf() ;
	mb->sm_len = 3000+12 ;
	p = smtod(mb, char *) + 12 ;
	for (i = 0 ; i < 3000 ; i++)
		*p++ = 1 << (i&7) ;

	smt = smtod(mb, struct smt_header *) ;
	smt->smt_dest = *dest ;
	smt->smt_source = smc->mib.m[MAC0].fddiMACSMTAddress ;
	smt_send_mbuf(smc,mb,FC_ASYNC_LLC) ;
#endif
}
#endif

#ifdef	DEBUG
#define hextoasc(x)	"0123456789abcdef"[x]

char *addr_to_string(struct fddi_addr *addr)
{
	int	i ;
	static char	string[6*3] = "****" ;

	for (i = 0 ; i < 6 ; i++) {
		string[i*3] = hextoasc((addr->a[i]>>4)&0xf) ;
		string[i*3+1] = hextoasc((addr->a[i])&0xf) ;
		string[i*3+2] = ':' ;
	}
	string[5*3+2] = 0 ;
	return(string) ;
}
#endif

#ifdef	AM29K
int smt_ifconfig(int argc, char *argv[])
{
	if (argc >= 2 && !strcmp(argv[0],"opt_bypass") &&
	    !strcmp(argv[1],"yes")) {
		smc->mib.fddiSMTBypassPresent = 1 ;
		return(0) ;
	}
	return(amdfddi_config(0,argc,argv)) ;
}
#endif

/*
 * return static mac index
 */
static int mac_index(struct s_smc *smc, int mac)
{
	SK_UNUSED(mac) ;
#ifdef	CONCENTRATOR
	SK_UNUSED(smc) ;
	return(NUMPHYS+1) ;
#else
	return((smc->s.sas == SMT_SAS) ? 2 : 3) ;
#endif
}

/*
 * return static phy index
 */
static int phy_index(struct s_smc *smc, int phy)
{
	SK_UNUSED(smc) ;
	return(phy+1);
}

/*
 * return dynamic mac connection resource index
 */
static int mac_con_resource_index(struct s_smc *smc, int mac)
{
#ifdef	CONCENTRATOR
	SK_UNUSED(smc) ;
	SK_UNUSED(mac) ;
	return(entity_to_index(smc,cem_get_downstream(smc,ENTITY_MAC))) ;
#else
	SK_UNUSED(mac) ;
	switch (smc->mib.fddiSMTCF_State) {
	case SC9_C_WRAP_A :
	case SC5_THRU_B :
	case SC11_C_WRAP_S :
		return(1) ;
	case SC10_C_WRAP_B :
	case SC4_THRU_A :
		return(2) ;
	}
	return(smc->s.sas == SMT_SAS ? 2 : 3) ;
#endif
}

/*
 * return dynamic phy connection resource index
 */
static int phy_con_resource_index(struct s_smc *smc, int phy)
{
#ifdef	CONCENTRATOR
	return(entity_to_index(smc,cem_get_downstream(smc,ENTITY_PHY(phy)))) ;
#else
	switch (smc->mib.fddiSMTCF_State) {
	case SC9_C_WRAP_A :
		return(phy == PA ? 3 : 2) ;
	case SC10_C_WRAP_B :
		return(phy == PA ? 1 : 3) ;
	case SC4_THRU_A :
		return(phy == PA ? 3 : 1) ;
	case SC5_THRU_B :
		return(phy == PA ? 2 : 3) ;
	case SC11_C_WRAP_S :
		return(2) ;
	}
	return(phy) ;
#endif
}

#ifdef	CONCENTRATOR
static int entity_to_index(struct s_smc *smc, int e)
{
	if (e == ENTITY_MAC)
		return(mac_index(smc,1)) ;
	else
		return(phy_index(smc,e - ENTITY_PHY(0))) ;
}
#endif

#ifdef	LITTLE_ENDIAN
static int smt_swap_short(u_short s)
{
	return(((s>>8)&0xff)|((s&0xff)<<8)) ;
}

void smt_swap_para(struct smt_header *sm, int len, int direction)
/* int direction;	0 encode 1 decode */
{
	struct smt_para	*pa ;
	const  struct smt_pdef	*pd ;
	char	*p ;
	int	plen ;
	int	type ;
	int	i ;

/*	printf("smt_swap_para sm %x len %d dir %d\n",
		sm,len,direction) ;
 */
	smt_string_swap((char *)sm,SWAP_SMTHEADER,len) ;

	/* swap args */
	len -= sizeof(struct smt_header) ;

	p = (char *) (sm + 1) ;
	while (len > 0) {
		pa = (struct smt_para *) p ;
		plen = pa->p_len ;
		type = pa->p_type ;
		pa->p_type = smt_swap_short(pa->p_type) ;
		pa->p_len = smt_swap_short(pa->p_len) ;
		if (direction) {
			plen = pa->p_len ;
			type = pa->p_type ;
		}
		/*
		 * note: paras can have 0 length !
		 */
		if (plen < 0)
			break ;
		plen += PARA_LEN ;
		for (i = N_SMT_PLEN, pd = smt_pdef; i ; i--,pd++) {
			if (pd->ptype == type)
				break ;
		}
		if (i && pd->pswap) {
			smt_string_swap(p+PARA_LEN,pd->pswap,len) ;
		}
		len -= plen ;
		p += plen ;
	}
}

static void smt_string_swap(char *data, const char *format, int len)
{
	const char	*open_paren = NULL ;
	int	x ;

	while (len > 0  && *format) {
		switch (*format) {
		case '[' :
			open_paren = format ;
			break ;
		case ']' :
			format = open_paren ;
			break ;
		case '1' :
		case '2' :
		case '3' :
		case '4' :
		case '5' :
		case '6' :
		case '7' :
		case '8' :
		case '9' :
			data  += *format - '0' ;
			len   -= *format - '0' ;
			break ;
		case 'c':
			data++ ;
			len-- ;
			break ;
		case 's' :
			x = data[0] ;
			data[0] = data[1] ;
			data[1] = x ;
			data += 2 ;
			len -= 2 ;
			break ;
		case 'l' :
			x = data[0] ;
			data[0] = data[3] ;
			data[3] = x ;
			x = data[1] ;
			data[1] = data[2] ;
			data[2] = x ;
			data += 4 ;
			len -= 4 ;
			break ;
		}
		format++ ;
	}
}
#else
void smt_swap_para(struct smt_header *sm, int len, int direction)
/* int direction;	0 encode 1 decode */
{
	SK_UNUSED(sm) ;
	SK_UNUSED(len) ;
	SK_UNUSED(direction) ;
}
#endif

/*
 * PMF actions
 */
int smt_action(struct s_smc *smc, int class, int code, int index)
{
	int	event ;
	int	port ;
	DB_SMT("SMT: action %d code %d\n",class,code) ;
	switch(class) {
	case SMT_STATION_ACTION :
		switch(code) {
		case SMT_STATION_ACTION_CONNECT :
			smc->mib.fddiSMTRemoteDisconnectFlag = FALSE ;
			queue_event(smc,EVENT_ECM,EC_CONNECT) ;
			break ;
		case SMT_STATION_ACTION_DISCONNECT :
			queue_event(smc,EVENT_ECM,EC_DISCONNECT) ;
			smc->mib.fddiSMTRemoteDisconnectFlag = TRUE ;
			RS_SET(smc,RS_DISCONNECT) ;
			AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
				FDDI_SMT_EVENT, (u_long) FDDI_REMOTE_DISCONNECT,
				smt_get_event_word(smc));
			break ;
		case SMT_STATION_ACTION_PATHTEST :
			AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
				FDDI_SMT_EVENT, (u_long) FDDI_PATH_TEST,
				smt_get_event_word(smc));
			break ;
		case SMT_STATION_ACTION_SELFTEST :
			AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
				FDDI_SMT_EVENT, (u_long) FDDI_REMOTE_SELF_TEST,
				smt_get_event_word(smc));
			break ;
		case SMT_STATION_ACTION_DISABLE_A :
			if (smc->y[PA].pc_mode == PM_PEER) {
				RS_SET(smc,RS_EVENT) ;
				queue_event(smc,EVENT_PCM+PA,PC_DISABLE) ;
			}
			break ;
		case SMT_STATION_ACTION_DISABLE_B :
			if (smc->y[PB].pc_mode == PM_PEER) {
				RS_SET(smc,RS_EVENT) ;
				queue_event(smc,EVENT_PCM+PB,PC_DISABLE) ;
			}
			break ;
		case SMT_STATION_ACTION_DISABLE_M :
			for (port = 0 ; port <  NUMPHYS ; port++) {
				if (smc->mib.p[port].fddiPORTMy_Type != TM)
					continue ;
				RS_SET(smc,RS_EVENT) ;
				queue_event(smc,EVENT_PCM+port,PC_DISABLE) ;
			}
			break ;
		default :
			return(1) ;
		}
		break ;
	case SMT_PORT_ACTION :
		switch(code) {
		case SMT_PORT_ACTION_ENABLE :
			event = PC_ENABLE ;
			break ;
		case SMT_PORT_ACTION_DISABLE :
			event = PC_DISABLE ;
			break ;
		case SMT_PORT_ACTION_MAINT :
			event = PC_MAINT ;
			break ;
		case SMT_PORT_ACTION_START :
			event = PC_START ;
			break ;
		case SMT_PORT_ACTION_STOP :
			event = PC_STOP ;
			break ;
		default :
			return(1) ;
		}
		queue_event(smc,EVENT_PCM+index,event) ;
		break ;
	default :
		return(1) ;
	}
	return(0) ;
}

/*
 * canonical conversion of <len> bytes beginning form *data
 */
#ifdef  USE_CAN_ADDR
static void hwm_conv_can(struct s_smc *smc, char *data, int len)
{
	int i ;

	SK_UNUSED(smc) ;

	for (i = len; i ; i--, data++)
		*data = bitrev8(*data);
}
#endif

#endif	/* no SLIM_SMT */