aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/net/skfp/smt.c
blob: 08d94329c12ff7502580c21ccad49a48a6a84148 (plain) (tree)
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



















                                                                                
                         
                         







                                                                        


























                                                                  




















                                                                             
                                                                     






















                                                                                  
                                                 






                                                      
                                                                     

















                                                                       
                                                     
                                                     
                                                   



                                                             
                                          




                                                          
                                                                          
                                                                          
                                                                        


























                                                                          
                                                                         


































                                                                 












                                                               
                                                              












































































































































































































































                                                                            

                                   


















































































































                                                                                
                 































































































































                                                                                
                                                                        






















































































































































































































































































































































































































                                                                                
                         


                                      
                          



















                                                                        
                  





























































































































                                                                             
                           






                                                         
                                                  






































































































                                                                                 
                   









































































































































































                                                                                  
                                                















                                                                
                                 
















































































                                                                   
                                            







                                                                               
                                  


                     
                 



























                                                                               
                                     
































                                                              





                                              


                                                           
         
                              
                      



             
                                        



                                                         
                         
         
                                             










                                                
                           
     
                                               








                                                
                       









                                                             
                                                                         





                                           
                         

                            
                         
         
                                             








                                                             
                                                                               


                                           
                                         
                            
                                         
                         
                                         
                         
                                         
                            
                         
         
                   






                                                    
                                         
            
                                                         





                                    
                                             


















































                                                                    
                                            














































































































                                                                                
                                 



















                                              
                                 



                                                        
                         
         
                 


  


                                                           
                                                                




                        

                                       




                         
/******************************************************************************
 *
 *	(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>
#include <linux/kernel.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 }
} ;

/*
 * 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 unfortunately 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 ;
	const u_char *map ;
	u_short	in ;
	u_short	out ;

	/*
	 * MIB para 101b (fddiSMTConnectionPolicy) coding
	 * is different from 0005 coding
	 */
	static const 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
char *addr_to_string(struct fddi_addr *addr)
{
	int	i ;
	static char	string[6*3] = "****" ;

	for (i = 0 ; i < 6 ; i++) {
		string[i * 3] = hex_asc_hi(addr->a[i]);
		string[i * 3 + 1] = hex_asc_lo(addr->a[i]);
		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 */