/******************************************************************************
*
* (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_ti