Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1546 lines
47 KiB

/*--------------------------------------------------------------------------
| port.c - common port code
Change History:
4-27-98 - adjust for scanrate addition.
3-23-98 - add in broadcast for boxes if not found(had put in V1.12) but
the changes did not make it into source-safe. kpb.
3-20-98 - Change scheme to track remote tx-buffer level, all changes
ifdef'ed by NEW_Q in port.h, turned off for now. - kpb.
3-16-98 - VS recovery fix, reset flags in port_resync_all() to force update.
If RAS lost box, it would continue to see active connections, then on
recovery, DSR,CD,CTS input signals would not be updated immediately.
11-05-97 - Add Backup Server feature. DCS
6-17-97 - start using index field assigned to box to id rx-messages.
6-17-97 - change link-integrity check code.
|--------------------------------------------------------------------------*/
#include "precomp.h"
int check_ack_code(PortMan *pm, BYTE *pkt);
int send_code(PortMan *pm);
int send_go(PortMan *pm);
int port_handle_outpkt(PortMan *pm, BYTE **buf, int *tx_used, int *port_set);
BYTE *port_setup_outpkt(PortMan *pm, int *tx_used);
void port_query_reply(PortMan *pm, BYTE *pkt);
void port_load_pkt(PortMan *pm, BYTE *pkt);
ULONG port_event_proc(PVOID context, int message_id, ULONG message_data);
int port_resync_all(PortMan *pm);
int port_connect_reply(Hdlc *hd);
int port_connect_ask(Hdlc *hd);
int port_packet(PortMan *pm, BYTE *buf);
#define DISABLE()
#define ENABLE()
#define TraceErr3(s, p1, p2, p3) GTrace3(D_Error, sz_modid, s, p1, p2, p3)
#define TraceErr2(s, p1, p2) GTrace2(D_Error, sz_modid, s, p1, p2)
#define TraceErr1(s, p1) GTrace1(D_Error, sz_modid, s, p1)
#define Trace2(s, p1, p2) GTrace2(D_Port, sz_modid, s, p1, p2)
#define Trace1(s, p1) GTrace1(D_Port, sz_modid, s, p1)
#define TraceStr(s) GTrace(D_Port, sz_modid, s)
#define TraceErr(s) GTrace(D_Error, sz_modid_err, s)
#define TraceAssert(l,s)
static char *sz_modid = {"Port"};
static char *sz_modid_err = {"Error,Port"};
// following for trace or dump messages, make public for other mods as well.
char *port_state_str[] = {"Init", "InitOwn", "SendCode", "Connect", "Active", "."};
#ifdef NEW_Q
/*--------------------------------------------------------------------------
PortGetTxCntRemote -
|--------------------------------------------------------------------------*/
WORD PortGetTxCntRemote(SerPort *p)
{
WORD Get, Put;
Get = p->nGetRemote;
Put = p->nPutRemote;
if (Put >= Get)
return (Put - Get);
else
return (Put + (~Get) + 1);
}
#endif
/*--------------------------------------------------------------------------
PortFlushTx -
|--------------------------------------------------------------------------*/
void PortFlushTx(SerPort *p)
{
if (!q_empty(&p->QOut)) // flush local side
{
q_put_flush(&p->QOut);
}
pFlushOutput(p); // flush remote
}
/*--------------------------------------------------------------------------
PortFlushRx -
|--------------------------------------------------------------------------*/
void PortFlushRx(SerPort *p)
{
if (!q_empty(&p->QIn)) // flush local side
{
#ifdef NEW_Q
p->nGetLocal += q_count(&p->QIn);
#endif
q_get_flush(&p->QIn);
p->Status |= S_UPDATE_ROOM;
}
pFlushInput(p); // flush remote
}
/*--------------------------------------------------------------------------
port_resync_all - total-resync, this routine is called to reset port
users, to inform them of a re-sync operation and adjust operation
accordingly.
If our case, since remote q is critical to maintain between both sides,
we clear out all buffer data and start with all empty buffers.
|--------------------------------------------------------------------------*/
int port_resync_all(PortMan *pm)
{
int i;
SerPort *Port;
TraceStr( "ReSync");
for (i=0; i<pm->num_ports; i++)
{
Port = pm->sp[i];
if (Port != NULL)
{
Port->QOut.QSize = OUT_BUF_SIZE;
Port->QOut.QGet = 0;
Port->QOut.QPut = 0;
#ifdef NEW_Q
Port->nPutRemote = 0;
Port->nGetRemote = 0;
Port->nGetLocal = 0;
#else
Port->QInRemote.QSize= OUT_BUF_SIZE; // for now assume same sizes
Port->QInRemote.QGet = 0;
Port->QInRemote.QPut = 0;
#endif
//Port->LanIndex = i;
Port->QIn.QSize = IN_BUF_SIZE;
Port->QIn.QGet = 0;
Port->QIn.QPut = 0;
Port->change_flags |= (CHG_BAUDRATE | CHG_SP_CHARS);
Port->old_baudrate = 0; // force baud rate update
Port->old_control_settings = ~Port->control_settings; // force update
Port->old_mcr_value = ~Port->mcr_value; // force update
// reset this
memset(&Port->last_sp_chars, 0, sizeof(Port->last_sp_chars));
Port->msr_value = 0; // back to initial state.
}
}
return 0;
}
/*--------------------------------------------------------------------------
Callback routine that hdlc(l2) protocol calls on events.
We are upper layer(3).
|--------------------------------------------------------------------------*/
ULONG port_event_proc(PVOID context, int message_id, ULONG message_data)
{
TraceStr("L3Event");
switch(message_id)
{
case EV_L2_CHECK_LINK: // hdlc wants us to check link
TraceStr("Chk Link");
// request that the portman do a link message check
((PortMan *) context)->Status |= S_CHECK_LINK;
break;
case EV_L2_ADMIN_REPLY: // got a query-id reply ADMIN packet
TraceStr("ID PKT");
port_query_reply((PortMan *) context, (BYTE *) message_data);
break;
case EV_L2_BOOT_REPLY: // got a boot loader ADMIN packet
TraceStr("LOAD PKT");
port_load_pkt((PortMan *) context, (BYTE *) message_data);
break;
case EV_L2_RESYNC:
// this happens on RK_CONNECT reply
port_resync_all((PortMan *) context);
break;
case EV_L2_RELOAD:
// this only happens when alive timer times out,
// (hdlc-level detects a bad connection),
// so lets assume box needs to be brought up from ground zero.
port_resync_all((PortMan *) context);
((PortMan *) context)->state = ST_INIT;
((PortMan *) context)->load_timer = 0;
++((PortMan *) context)->reload_errors;
TraceErr("Reload device");
break;
case EV_L2_RX_PACKET:
port_packet((PortMan *) context, ((BYTE *) message_data) );
break;
}
return 0;
}
/*--------------------------------------------------------------------------
port_set_new_mac_addr -
|--------------------------------------------------------------------------*/
int port_set_new_mac_addr(PortMan *pm, BYTE *box_addr)
{
// Hdlc *hd;
// int i;
// copy over the new mac-address
memcpy(pm->hd->dest_addr, box_addr, 6);
// force a complete update of the box
pm->reload_errors = 0;
pm->state = 0;
pm->Status |= S_NEED_CODE_UPDATE;
pm->Status |= S_SERVER; // yes we are server(not box)
port_resync_all(pm);
return 0;
}
/*--------------------------------------------------------------------------
portman_init - init the Box(PortMAn) struct, and the associated hdlc hd object.
At this point the Nic object is already open.
|--------------------------------------------------------------------------*/
int portman_init(Hdlc *hd,
PortMan *pm,
int num_ports,
int unique_id,
int backup_server,
int backup_timer,
BYTE *box_addr)
{
int i, stat;
MyKdPrint(D_Init, ("portman_init\n"))
TraceStr( "PortInit");
stat = 0;
// allocate serial-port structures.
for (i=0; i<num_ports; i++)
{
if (pm->sp[i] == NULL)
{
pm->sp[i] = (SerPort *)our_locked_alloc(sizeof(SerPort), "Dsp");
port_init(pm->sp[i]); // let port create and init some stuff
}
}
pm->num_ports = num_ports;
pm->backup_server = backup_server;
pm->backup_timer = backup_timer;
pm->unique_id = unique_id;
pm->load_timer = 0;
// default to the first nic card slot, port state handling and nic
// packet reception handling dynamically figures this out.
// we should probably set it to null, but I'm afraid of this right now
#ifdef BREAK_NIC_STUFF
pm->nic =NULL;
#else
pm->nic = &Driver.nics[0];
#endif
pm->nic_index = 0;
pm->hd = hd;
pm->reload_errors = 0;
pm->state = 0;
pm->state_timer = 0;
pm->Status |= S_NEED_CODE_UPDATE;
pm->Status |= S_SERVER; // yes we are server(not box)
pm->hd = hd;
stat = hdlc_open(pm->hd, box_addr);
hd->context = pm; // put our handle here, so hdlc sends this along
// with any upper_layer messages
if (stat)
{
if (Driver.VerboseLog)
Eprintf("Hdlc open fail:%d",stat);
TraceStr("Err-Hdlc Open!");
return 3;
}
// set HDLC's callback RX-proc to point to our routine
hd->upper_layer_proc = port_event_proc;
port_resync_all(pm);
return 0;
}
/*--------------------------------------------------------------------------
portman_close - close down the port manager.
|--------------------------------------------------------------------------*/
int portman_close(PortMan *pm)
{
int i;
pm->state = 0;
// deallocate any Port things
for (i=0; i<pm->num_ports; i++)
{
if (pm->sp[i] != NULL)
{
port_close(pm->sp[i]);
our_free(pm->sp[i], "Dsp");
pm->sp[i] = NULL;
}
}
return 0;
}
/*--------------------------------------------------------------------------
port_init - init a SerPort thing.
|--------------------------------------------------------------------------*/
int port_init(SerPort *sp)
{
TraceStr("SPort_Init");
if (sp->QOut.QBase == NULL)
{
sp->QOut.QBase = our_locked_alloc(OUT_BUF_SIZE+2,"pmQO");
if (sp->QOut.QBase == NULL)
{
return 1;
}
}
if (sp->QIn.QBase == NULL)
{
sp->QIn.QBase = our_locked_alloc(IN_BUF_SIZE+2, "pmQI");
if (sp->QIn.QBase == NULL)
{
return 2;
}
}
sp->Status |= S_OPENED;
sp->mcr_value = 0;
//sp->mcr_value = MCR_RTS_SET_ON | MCR_DTR_SET_ON;
sp->old_mcr_value = sp->mcr_value;
sp->sp_chars.tx_xon = 0x11;
sp->sp_chars.tx_xoff = 0x13;
sp->sp_chars.rx_xon = 0x11;
sp->sp_chars.rx_xoff = 0x13;
sp->last_sp_chars = sp->sp_chars; // copy struct to old
sp->change_flags = 0;
}
/*--------------------------------------------------------------------------
port_close -
|--------------------------------------------------------------------------*/
int port_close(SerPort *sp)
{
int i;
if (sp == NULL)
return 0;
if (sp->QIn.QBase != NULL)
{
our_free(sp->QIn.QBase,"pmQI");
sp->QIn.QBase = NULL;
}
if (sp->QOut.QBase != NULL)
{
our_free(sp->QOut.QBase,"pmQO");
sp->QOut.QBase = NULL;
}
return 0;
}
/*--------------------------------------------------------------------------
port_packet - got an incoming packet, do something with it.
|--------------------------------------------------------------------------*/
int port_packet(PortMan *pm, BYTE *buf)
{
SerPort *Port;
int port_num;
int done, len;
int QInRoom;
TraceStr( "GotPkt");
Port = pm->sp[0]; // default to point to first port
//----- process all the sub-packets in the lan-packet, process
// until we hit a zero header field, or some header we don't know
// about(default: case).
done = 0;
if (*(buf) == 0)
{
// bugbug: this is a problem, we get a bunch of these during
// normal operation, for now just show in debug version, as
// they are a nuciance in peer error tracing.
#if DBG
TraceErr("Empty pkt!");
#endif
}
while (!done)
{
switch(*buf++)
{
case RK_CONNECT_CHECK: // check link
TraceStr( "Rk_Conn_Chk_reply");
// do nothing on the server, on box send back a iframe reply
break;
case RK_CONNECT_REPLY: // reply from our request to bring up connection
TraceStr( "Rk_reply");
if (pm->Status & S_SERVER)
{
if (pm->state == ST_CONNECT) // should happen at this time
{
pm->state = ST_ACTIVE; // fire up a connection
}
else // got it when not expecting it.
{
TraceStr("Err-Recover!");
// client recovering, need resyc.
port_resync_all(pm);
}
}
break;
case RK_CONNECT_ASK: // packet from server
TraceStr( "Rk_Ask");
// should not see this on server
break;
case RK_PORT_SET: // set the port num to work with
TraceStr( "Rk_Port");
if (*buf >= pm->num_ports)
{
TraceErr( "PortI!");
port_num = *buf++;
break;
}
port_num = *buf++;
Port = pm->sp[port_num];
break;
#ifdef COMMENT_OUT // not on server
case RK_BAUD_SET: // set the baud rate
Port->baudrate = *((DWORD *)(buf)); // Remotes QIn.QGet value
buf += 4;
//sSetBaudRate(ChP, Port->baudrate, 1);
break;
case RK_CONTROL_SET: // set the baud rate
w1 = *((WORD *)(buf)); // control settings
buf += 2;
control_set(port_num, w1);
break;
case RK_MCR_SET: // modem control reg pkt
w1 = *((WORD *)(buf)); // control settings
buf += 2;
mcr_set(port_num, w1);
break;
#endif
case RK_MSR_SET: // modem status reg pkt
Port->msr_value = *((WORD *)(buf));
Trace1("Rk_MSR:%xH", Port->msr_value);
buf += 2;
break;
case RK_ACTION_ACK: // modem status reg pkt
// NT does not use this one, novell driver does to
// help dump all data in transit during a flush.
//Port->action_resp = *((WORD *)(buf));
Trace1("Rk_Act_Ack:%xH", *((WORD *)(buf)));
buf += 2;
break;
case RK_ESR_SET: // error status reg pkt
Port->esr_reg = *((WORD *)(buf));
Trace1("Rk_ESR:%xH", Port->esr_reg);
buf += 2;
break;
case RK_QIN_STATUS: // qin status report
TraceStr( "Rk_QStat");
#ifdef NEW_Q
Port->nGetRemote = *((WORD *)(buf)); // track remote output buffer space
#else
Port->QInRemote.QGet = *((short *)(buf)); // Remotes QIn.QGet value
#endif
buf += 2;
break;
case RK_DATA_BLK: // data block to put in buffer queue
TraceStr( "Rk_Data");
#ifdef NEW_Q
//old(does not belong here!):Port->Status |= S_UPDATE_ROOM;
#else
Port->Status |= S_UPDATE_ROOM;
#endif
len = *((WORD *)(buf));
buf += 2;
QInRoom = q_room(&Port->QIn);
TraceAssert((QInRoom < Port->QIn.QSize), "Err1B!!!");
TraceAssert((QInRoom >= 0), "Err1B!!!");
if (len > QInRoom) // Overflow
{
TraceErr("Err-Port Overflow!");
len = 0;
}
q_put(&Port->QIn, buf, len);
buf += len;
break;
default:
done = 1;
if (*(buf-1) != 0)
{
TraceErr("Bad Sub pkt hdr!");
GTrace1(D_Error,sz_modid," HDR:%xH",*(buf-1));
}
break;
} // case per sub-packet
} // while not done with sub-packets
return 0;
}
/*--------------------------------------------------------------------------
port_poll - check to see if we need to send any packets over. If we have
new data in or need to send status packets over.
|--------------------------------------------------------------------------*/
int port_poll(PortMan *pm)
{
#define MAX_TX_SPACE 1460
int i, tx_used;
SerPort *Port;
unsigned char *buf;
int ToMove, ThisMove;
int QOutCount;
int QLanRoom;
#ifdef NEW_Q
WORD tmp_word;
#endif
int port_set; // flag it as not setup.
// this logic is in isr.c service routine now
// if (pm->state != ST_ACTIVE)
// {
// port_state_handler(pm);
// return 0;
// }
tx_used = MAX_TX_SPACE+1000; // indicate no pkt allocated
#if DBG
if (pm == NULL)
{
MyKdPrint(D_Error, ("!!!!!pm null\n"))
return 0;
}
#endif
// handle box things, send out a query to check-connection if
// hdlc saw inactivity.
if (pm->Status & S_CHECK_LINK)
{
if (tx_used > (MAX_TX_SPACE-50)) // if our tx-pkt is near full or null
{
buf = port_setup_outpkt(pm, &tx_used);
if (buf == NULL)
return 0; // no more output packet space available, so all done
buf[tx_used++] = RK_CONNECT_CHECK;
// at this point we queued our iframe to query other side(to ensure
// link-integrity.
pm->Status &= ~S_CHECK_LINK; // reset our request to send this
TraceStr("Check sent");
}
}
for (i=0; i<pm->num_ports; i++)
{
Port = pm->sp[pm->last_round_robin];
//----- see if flag set to tell other side how much room in Port Rx buf
if (Port->Status & S_UPDATE_ROOM)
{
TraceStr("Update needed");
if (tx_used > (MAX_TX_SPACE-50)) // if our tx-pkt is near full or null
{
buf = port_setup_outpkt(pm, &tx_used);
if (buf == NULL)
return 0; // no more output packet space available, so all done
port_set = 0xff; // flag it as not setup.
}
if (port_set != pm->last_round_robin) // our port index not setup
{
buf[tx_used++] = RK_PORT_SET;
buf[tx_used++] = (BYTE) pm->last_round_robin;
port_set = pm->last_round_robin;
}
// take away status reminder flag
Port->Status &= ~S_UPDATE_ROOM;
// form the sub-packet in our output packet buffer
buf[tx_used++] = RK_QIN_STATUS;
// report our actual QGet index to other side.
#ifdef NEW_Q
*((WORD *)(&buf[tx_used])) = Port->nGetLocal;
#else
*((short *)(&buf[tx_used])) = Port->QIn.QGet;
#endif
tx_used += 2;
}
//----- do action items
if (Port->action_reg != 0)
{
if (port_handle_outpkt(pm, &buf, &tx_used, &port_set) != 0) // no pkt space avail
return 0; // no more output packet space available, so all done
TraceStr("act pkt");
buf[tx_used++] = RK_ACTION_SET;
*((WORD *)(&buf[tx_used])) = Port->action_reg;
Port->action_reg = 0; // its a one-shot deal, so we reset this now
tx_used += 2;
}
//----- do updates for control settings, mcr, etc
if (Port->old_control_settings != Port->control_settings)
{
if (port_handle_outpkt(pm, &buf, &tx_used, &port_set) != 0) // no pkt space avail
return 0; // no more output packet space available, so all done
Port->old_control_settings = Port->control_settings;
TraceStr("ctr chg");
buf[tx_used++] = RK_CONTROL_SET;
*((WORD *)(&buf[tx_used])) = Port->control_settings;
tx_used += 2;
}
//----- do updates for mcr
if (Port->old_mcr_value != Port->mcr_value)
{
if (port_handle_outpkt(pm, &buf, &tx_used, &port_set) != 0) // no pkt space avail
return 0; // no more output packet space available, so all done
TraceStr("mcr chg");
Port->old_mcr_value = Port->mcr_value;
buf[tx_used++] = RK_MCR_SET;
*((WORD *)(&buf[tx_used])) = Port->mcr_value;
tx_used += 2;
}
//----- do updates for special chars, etc
if (Port->change_flags)
{
if (Port->change_flags & CHG_BAUDRATE)
{
//----- do updates for baud rate settings
if (Port->old_baudrate != Port->baudrate)
{
if (port_handle_outpkt(pm, &buf, &tx_used, &port_set) != 0) // no pkt space avail
return 0; // no more output packet space available, so all done
Port->old_baudrate = Port->baudrate;
Trace1("baud:%lu", Port->baudrate);
buf[tx_used++] = RK_BAUD_SET;
*((DWORD *)(&buf[tx_used])) = Port->baudrate;
tx_used += 4;
}
}
if (Port->change_flags & CHG_SP_CHARS)
{
if (memcmp(&Port->last_sp_chars, &Port->sp_chars, 6) != 0) // compare structs for chg
{
Port->last_sp_chars = Port->sp_chars; // remember last set values
if (port_handle_outpkt(pm, &buf, &tx_used, &port_set) != 0) // no pkt space avail
return 0; // no more output packet space available, so all done
TraceStr("sp_chars");
buf[tx_used++] = RK_SPECIAL_CHAR_SET;
Trace1(" rx_xon:%x", Port->sp_chars.rx_xon);
Trace1(" rx_xoff:%x", Port->sp_chars.rx_xoff);
Trace1(" tx_xon:%x", Port->sp_chars.tx_xon);
Trace1(" tx_xoff:%x", Port->sp_chars.tx_xoff);
Trace1(" error:%x", Port->sp_chars.error);
Trace1(" event:%x", Port->sp_chars.event);
buf[tx_used++] = Port->sp_chars.rx_xon;
buf[tx_used++] = Port->sp_chars.rx_xoff;
buf[tx_used++] = Port->sp_chars.tx_xon;
buf[tx_used++] = Port->sp_chars.tx_xoff;
buf[tx_used++] = Port->sp_chars.error;
buf[tx_used++] = Port->sp_chars.event;
}
}
Port->change_flags = 0; // reset all
}
//----- send any outgoing data if other side has room.
QOutCount = q_count(&Port->QOut);
#ifdef NEW_Q
// calculate our remote tx-buffer space based on WORD modulo arithmetic
tmp_word = PortGetTxCntRemote(Port);
// right now this var is equal to how much tx-data is in remote buffer.
if (tmp_word < REMOTE_IN_BUF_SIZE)
QLanRoom = REMOTE_IN_BUF_SIZE - tmp_word;
else QLanRoom = 0;
// now it is how much room we have in the remote tx-buffer.
#else
QLanRoom = q_room(&Port->QInRemote); // other sides port queue room
#endif
if ((QOutCount > 0) && (QLanRoom > 50)) // have data, other side has room
{
TraceStr("Data to Send");
if (QOutCount > QLanRoom) // more data than room
ToMove = QLanRoom; // limit
else ToMove = QOutCount;
do
{
if (tx_used > (MAX_TX_SPACE-50)) // if our tx-pkt is near full or null
{
buf = port_setup_outpkt(pm, &tx_used); // allocate a new one
if (buf == NULL)
return 0; // no more output packet space available, so all done
port_set = 0xff; // flag it as not setup.
}
if (port_set != pm->last_round_robin) // our port index not setup
{
buf[tx_used++] = RK_PORT_SET;
buf[tx_used++] = (BYTE) pm->last_round_robin;
port_set = pm->last_round_robin;
}
// make sure we have emough room for data, limit if we don't
if (ToMove > ((MAX_TX_SPACE-1) - tx_used) )
{
ThisMove = (MAX_TX_SPACE-1) - tx_used;
ToMove -= ThisMove;
}
else
{
ThisMove = ToMove;
ToMove = 0;
}
buf[tx_used++] = RK_DATA_BLK; // set header sub-type
*((WORD *)(&buf[tx_used])) = ThisMove; // set header data size
tx_used += 2;
q_get(&Port->QOut, &buf[tx_used], ThisMove);
tx_used += ThisMove;
// keep our own copy of remote qin indexes
#ifdef NEW_Q
// bump our tx-buffer count based on WORD modulo arithmetic
Port->nPutRemote += ((WORD)ThisMove);
#else
q_putted(&Port->QInRemote, ((short)ThisMove));
#endif
} while (ToMove > 0); // keep using packets if more to send
} // if data sent
++pm->last_round_robin;
if (pm->last_round_robin >= pm->num_ports)
pm->last_round_robin = 0;
}
if (tx_used < (MAX_TX_SPACE+1000)) // then we allocated a packet prior
{ // and need to send it
if (hdlc_send_outpkt(pm->hd, tx_used, pm->hd->dest_addr)) // send it out!
{
TraceErr("Err-hdlc_send1");
}
}
//TraceStr( "EndPoll");
return 0;
}
/*--------------------------------------------------------------------------
port_state_handler - handle states other than normal data flowage.
Called at scanrate(1-20ms) times per second from service routine.
|--------------------------------------------------------------------------*/
void port_state_handler(PortMan *pm)
{
int inic;
if (pm->old_state != pm->state)
{
pm->old_state = pm->state;
pm->state_timer = 0;
}
pm->timer_base += ((WORD) Driver.Tick100usBase); // 100us base units(typical:100)
if (pm->timer_base < 98) // less than 9.8ms
{
// we want to run roughly 100 ticks per second
return;
}
pm->timer_base = 0;
switch(pm->state)
{
case ST_INIT:
// if we are server, then wait for query back.
//pm->state_timer = 0;
//break;
if (pm->Status & S_SERVER)
{
if (pm->state_timer == 600) // 6 seconds
{
pm->ownership_timer = 0;
TraceStr( "Send Query");
// find box out on network, use ADMIN pkt
// do the query on all nic-segments
for (inic=0; inic<VS1000_MAX_NICS; inic++)
{
if (Driver.nics[inic].Open) // if nic-card open for use
{
// send a passive query(don't try to assume ownership
if (admin_send_query_id(&Driver.nics[inic], pm->hd->dest_addr,
0, 0) != 0)
{
TraceErr( "Err1E");
}
}
}
}
else if (pm->state_timer == 1800) // 18 seconds
{
// try a broadcast to cut through switches.
TraceStr( "Send Br.Query");
// find box out on network, use ADMIN pkt
// do the query on all nic-segments
for (inic=0; inic<VS1000_MAX_NICS; inic++)
{
if (Driver.nics[inic].Open) // if nic-card open for use
{
// send a passive query(don't try to assume ownership
if (admin_send_query_id(&Driver.nics[inic], broadcast_addr,
0, 0) != 0)
{
TraceErr( "Err1E");
}
}
}
}
else if (pm->state_timer > 2400) // 24 sec, give up start over
pm->state_timer = 0;
}
break;
case ST_GET_OWNERSHIP:
// if we are server, then wait for query back.
if (pm->Status & S_SERVER)
{
// Increment when in ST_GET_OWNERSHIP state for backup server.
++pm->load_timer;
if (pm->state_timer == 10) // 100ms
{
TraceStr( "Send Query Owner");
// find box out on network, use ADMIN pkt
// do the query on all nic-segments
for (inic=0; inic<VS1000_MAX_NICS; inic++)
{
if (Driver.nics[inic].Open) // if nic-card open for use
{
// BUGFIX(8-26-98), this was only sending it out on
// the nic card assigned to pm.
//)if (admin_send_query_id(pm->nic, pm->hd->dest_addr,
if (admin_send_query_id(&Driver.nics[inic], pm->hd->dest_addr,
1, (BYTE) pm->unique_id) != 0)
{
TraceErr( "Err1G");
}
}
}
}
else if (pm->state_timer > 600) // 6 seconds
{
// SAFE GUARD ADDED DUE to SCREWED UP OWNERSHIP STATE MACHINE
// kpb, 8-25-98, make sure we don't spend forever in this state.
pm->ownership_timer += 6;
if (pm->ownership_timer > (60 * 15)) // 15 minutes
{
pm->state = ST_INIT;
pm->load_timer = 0;
}
pm->state_timer = 0;
}
// 8-26-98
// NOTICE, we are not reseting state to INIT after a while,
// this is a problem!
}
break;
case ST_SENDCODE: // download main driver code to box
if (pm->state_timer == 0)
{
++pm->total_loads;
pm->code_cnt = 0; // start upload
send_code(pm);
}
else if (pm->state_timer == 1000) // 10 seconds since init
{
TraceErr("Upload Retry");
++pm->total_loads;
pm->code_cnt = 0; // start upload
send_code(pm);
}
else if (pm->state_timer == 2000) // 20 seconds since init
{
TraceErr("Upload Retry");
++pm->total_loads;
pm->code_cnt = 0; // start upload
send_code(pm);
}
else if (pm->state_timer == 3000) // fail it out, start over with init
{
TraceErr("Upload Fail");
pm->state = ST_INIT;
pm->load_timer = 0;
}
else if (pm->code_state == 1) // signal port poll code to send next chunk
{
TraceStr("Upload, next chk.");
if (pm->code_cnt < Driver.MicroCodeSize)
{
if (send_code(pm) == 0) // success
pm->code_state = 0;
}
else // all done
{
TraceStr("Code Upload Done.");
if (send_go(pm) == 0)
{
++pm->good_loads;
pm->code_cnt = 0;
pm->state = ST_GET_OWNERSHIP;
}
}
}
break;
case ST_CONNECT:
if (pm->state_timer == 0)
port_connect_ask(pm->hd);
else if (pm->state_timer == 1000) // 10 seconds
port_connect_ask(pm->hd);
else if (pm->state_timer == 2000) // 20 seconds
{
pm->state = ST_INIT; // fall back
pm->load_timer = 0;
}
break;
default:
TraceErr("Err-PState!");
pm->state = ST_INIT;
pm->load_timer = 0;
break;
}
++pm->state_timer;
}
/*--------------------------------------------------------------------------
port_handle_outpkt - check if we have at least 50 bytes in outpkt, if
not get a new one. If no new one avail, return non-zero.
|--------------------------------------------------------------------------*/
int port_handle_outpkt(PortMan *pm, BYTE **buf, int *tx_used, int *port_set)
{
if (*tx_used > (MAX_TX_SPACE-50)) // if our tx-pkt is near full or null
{
*buf = port_setup_outpkt(pm, tx_used);
if (*buf == NULL)
return 1; // no more output packet space available, so all done
*port_set = 0xff;
}
if (*port_set != pm->last_round_robin)
{
// since we have a new pkt, we need to
(*buf)[(*tx_used)++] = RK_PORT_SET;
(*buf)[(*tx_used)++] = (BYTE) pm->last_round_robin;
*port_set = pm->last_round_robin;
}
return 0; // current pkt has plenty of room(at least 50 bytes)
}
/*--------------------------------------------------------------------------
port_setup_outpkt - setup an outgoing packet if one is available, if previously
filled one out then we ship it off out the nic card.
|--------------------------------------------------------------------------*/
BYTE *port_setup_outpkt(PortMan *pm, int *tx_used)
{
BYTE *buf;
if (*tx_used != (MAX_TX_SPACE+1000)) // then we allocated a packet prior
{ // and need to send it
if (hdlc_send_outpkt(pm->hd, *tx_used, pm->hd->dest_addr)) // send it out!
{
TraceErr("send err");
}
}
if (hdlc_get_outpkt(pm->hd, &buf) == 0) // no error, got a output packet
{
TraceStr("NPkt2");
*tx_used = 0; // have a new empty output packet allocated
return buf; // all done
}
else
{
TraceStr("NPktDone2");
*tx_used = MAX_TX_SPACE+1000; // indicate no pkt allocated
return NULL; // all done
}
}
/*--------------------------------------------------------------------------
port_load_pkt - got a admin boot load packet: ACK back from code download pkt.
|--------------------------------------------------------------------------*/
void port_load_pkt(PortMan *pm, BYTE *pkt)
{
if (pm->state != ST_SENDCODE) // not expected at this time, lets reset it.
{
TraceErr("BootLoad not at SENDCODE!");
Tprintf("state=%d", pm->state);
// other details????
pm->state = ST_INIT;
pm->load_timer = 0;
//pm->hd->state = ST_HDLC_INIT;
return;
}
if (Driver.MicroCodeSize == 0)
{
TraceErr("Bad MC");
return;
}
if (check_ack_code(pm,pkt) != 0)
{
TraceErr("Bad Ack");
return;
}
TraceStr("Good Ack!");
// send more data
if (pm->code_cnt < Driver.MicroCodeSize)
pm->code_cnt += 1000;
pm->code_state = 1; // signal port poll code to send next chunk
}
#if NEW_QUERY_HANDLER
/*--------------------------------------------------------------------------
port_query_reply - got a ADMIN query reply back, server sends out
query-id request on init and when setup is entered, box sends back
id(which tells us if code is loaded.) A query reply is ignored in
states other that ST_INIT and ST_GET_OWNERSHIP
|--------------------------------------------------------------------------*/
void port_query_reply(PortMan *pm, BYTE *pkt)
{
int unit_available = 0;
int unit_needs_code = 0;
int unit_needs_reset = 0;
if (!mac_match(pkt, pm->hd->dest_addr))
{
TraceErr("Reply MAC bad!");
return;
}
// ignore if not ST_INIT or ST_GET_OWNERSHIP
if ((pm->state != ST_INIT) && (pm->state != ST_GET_OWNERSHIP))
{
return;
}
if (pkt[7] >= VS1000_MAX_NICS) // if invalid nic-index
{
TraceErr("Nic Index Reply!");
return;
}
// when we get the query packet, we stash the nic-card index
// into part of the receive buffer that is unused(pkt[7]).
// see if this matches what our port-manager nic_index is,
// if not, then we switched nic cards and need to update some
// things.
if (pm->nic_index != (int)(pkt[7])) // changed nic cards
{
TraceErr("Nic Changed!");
pm->nic_index = (int)(pkt[7]); // set nic_index
pm->nic = &Driver.nics[pm->nic_index]; // changed nic cards
pm->hd->nic = pm->nic; // update the hdlc nic ptr
}
#define Q_DRIVER_RUNNING 1
#define Q_NOT_OWNER 2
#define Q_ABANDONED 4
// we are NOT owner(2H), and main app-driver running(1H), be careful
if ((pkt[6] & Q_DRIVER_RUNNING) && (pkt[6] & Q_NOT_OWNER))
{
// if not owner timeout, (4H=ABANDONED) then leave alone!
// some other server is actively using it.
if ((pkt[6] & Q_ABANDONED) == 0)
{
Trace1("ReplyID, Not Ours. [%x]", pkt[6]);
pm->load_timer = 0;
pm->state = ST_INIT;
pm->load_timer = 0;
return;
}
// else its abandoned, so we can take ownership.
unit_available = 1;
unit_needs_reset = 1;
}
else
{
// we are owner or main-driver not running yet
unit_available = 1;
}
if ((pkt[6] & Q_DRIVER_RUNNING) == 0)
{
unit_needs_code = 1;
}
if (pm->Status & S_NEED_CODE_UPDATE)
{
unit_needs_reset = 1;
unit_needs_code = 1;
}
// ok to take ownership(no owner)
TraceStr("ReplyID, Unit Available");
if (pm->state == ST_INIT)
{
if ((pm->backup_server == 0) ||
(pm->load_timer >= (pm->backup_timer*6000)) )
{
if (pm->backup_server == 0)
{ TraceStr("Pri. make owner"); }
else
{ TraceStr("2nd. make owner"); }
pm->state = ST_GET_OWNERSHIP;
// this will cause the state machine to issue a query trying to
// obtain ownership
unit_needs_reset = 1;
}
else
{
if (pm->load_timer >= (pm->backup_timer*6000))
{
TraceStr("2nd, make owner");
pm->state = ST_GET_OWNERSHIP;
// this will cause the state machine to issue a query trying to
// obtain ownership
}
}
}
else if (pm->state == ST_GET_OWNERSHIP)
{
TraceStr("ReplyID in GET_OWNERSHIP");
// Is this the primary server or has the backup timer expired?
if ((pm->backup_server == 0) && (pm->load_timer >= (pm->backup_timer*6000))
{
// we are NOT owner(2H), and main app-driver running(1H), be careful
if ((pkt[6] & 3) == 3)
{
if (pkt[6] & 4) // Owner has timed out - force reload
{
// force a reset of box on driver-load(this bit is set in
// port_init) so we load up some fresh microcode.
admin_send_reset(pm->nic, pm->hd->dest_addr);
TraceStr("Abandoned, ReSet");
}
}
else if ((pkt[6] & 1) == 0) // code is not downloaded, so download it.
{
// Make sure that we are the owner?
if (pkt[6] & 2) // 2h=not owner bit
{
TraceStr("GET_OWNERSHIP: No App - Not Owner!");
pm->state = ST_INIT;
pm->load_timer = 0;
return;
}
TraceStr("GET_OWNERSHIP: Download");
pm->Status &= ~S_NEED_CODE_UPDATE;
pm->state = ST_SENDCODE;
}
else // code is downloaded - we are the owner
{
if (pm->Status & S_NEED_CODE_UPDATE)
{
// force a reset of box on driver-load(this bit is set in
// port_init) and set S_NEED_CODE_UPDATE so we load up some
// fresh microcode.
admin_send_reset(pm->nic, pm->hd->dest_addr);
TraceStr("ReplyID, ReLoad");
pm->Status &= ~S_NEED_CODE_UPDATE;
}
else
{
TraceStr("ReplyID, GoToConnect");
port_resync_all(pm);
//pm->state = ST_ACTIVE;
pm->state = ST_CONNECT;
}
}
}
}
}
#else
/*--------------------------------------------------------------------------
port_query_reply - got a ADMIN query reply back, server sends out
query-id request on init and when setup is entered, box sends back
id(which tells us if code is loaded.) A query reply is ignored in
states other that ST_INIT and ST_GET_OWNERSHIP
|--------------------------------------------------------------------------*/
void port_query_reply(PortMan *pm, BYTE *pkt)
{
if (!mac_match(pkt, pm->hd->dest_addr))
{
TraceErr("Reply MAC bad!");
return;
}
// ignore if not ST_INIT or ST_GET_OWNERSHIP
if ((pm->state != ST_INIT) && (pm->state != ST_GET_OWNERSHIP))
{
return;
}
if (pkt[7] >= VS1000_MAX_NICS) // if invalid nic-index
{
TraceErr("Nic Index Reply!");
return;
}
// when we get the query packet, we stash the nic-card index
// into part of the receive buffer that is unused(pkt[7]).
// see if this matches what our port-manager nic_index is,
// if not, then we switched nic cards and need to update some
// things.
if (pm->nic_index != (int)(pkt[7])) // changed nic cards
{
TraceErr("Nic Changed!");
pm->nic_index = (int)(pkt[7]); // set nic_index
pm->nic = &Driver.nics[pm->nic_index]; // changed nic cards
pm->hd->nic = pm->nic; // update the hdlc nic ptr
}
// we are NOT owner(2H), and main app-driver running(1H), be careful
if ((pkt[6] & 3) == 3)
{
// if not owner timeout, (4H=ABANDONED) then leave alone!
// some other server is actively using it.
if ((pkt[6] & 4) == 0)
{
Trace1("ReplyID, Not Ours. [%x]", pkt[6]);
pm->load_timer = 0;
pm->state = ST_INIT;
return;
}
}
if (pm->state == ST_INIT)
{
// ok to take ownership(no owner)
pm->state = ST_GET_OWNERSHIP;
if(pm->backup_server == 0)
{
Trace1("ReplyID, Primary Server - Unit Available [%x]", pkt[6]);
}
else
{
Trace1("ReplyID, Backup Server - Unit Available [%x]", pkt[6]);
}
}
else if (pm->state == ST_GET_OWNERSHIP)
{
Trace1("ReplyID, GET_OWNERSHIP [%x]", pkt[6]);
// Is this the primary server or has the backup timer expired?
if((pm->backup_server == 0) ||
(pm->load_timer >= (pm->backup_timer*6000)))
{
// we are NOT owner(2H), and main app-driver running(1H), be careful
if ((pkt[6] & 3) == 3)
{
if (pkt[6] & 4) // Owner has timed out - force reload
{
// force a reset of box on driver-load(this bit is set in
// port_init) so we load up some fresh microcode.
admin_send_reset(pm->nic, pm->hd->dest_addr);
TraceStr("ReplyID, ReLoad");
}
else
{
TraceStr("GET_OWNERSHIP: App Running - Not Owner!");
pm->state = ST_INIT;
pm->load_timer = 0;
return;
}
}
else if ((pkt[6] & 1) == 0) // code is not downloaded, so download it.
{
// Make sure that we are the owner?
if (pkt[6] & 2) // 2h=not owner bit
{
TraceStr("GET_OWNERSHIP: No App - Not Owner!");
pm->state = ST_INIT;
pm->load_timer = 0;
return;
}
TraceStr("GET_OWNERSHIP: Download");
pm->Status &= ~S_NEED_CODE_UPDATE;
pm->state = ST_SENDCODE;
}
else // code is downloaded - we are the owner
{
if (pm->Status & S_NEED_CODE_UPDATE)
{
// force a reset of box on driver-load(this bit is set in
// port_init) and set S_NEED_CODE_UPDATE so we load up some
// fresh microcode.
admin_send_reset(pm->nic, pm->hd->dest_addr);
TraceStr("ReplyID, ReLoad");
pm->Status &= ~S_NEED_CODE_UPDATE;
}
else
{
TraceStr("ReplyID, GoToConnect");
port_resync_all(pm);
//pm->state = ST_ACTIVE;
pm->state = ST_CONNECT;
}
}
}
}
}
#endif
/*--------------------------------------------------------------------------
| port_connect_reply - Reply to server connection request, we return our
MAC address, and do a re-sync operation.
|--------------------------------------------------------------------------*/
int port_connect_reply(Hdlc *hd)
{
BYTE rkt_header[8];
TraceStr( "Connect Reply");
rkt_header[0] = RK_CONNECT_REPLY;
memcpy(&rkt_header[1], hd->nic->address,6);
hdlc_send_control(hd, rkt_header, 7,
NULL, 0, // ptr to data to send
hd->dest_addr); // MAC address to send to
hdlc_resync(hd);
return 0;
}
/*--------------------------------------------------------------------------
| port_connect_ask - Ask box to initiate a connection. We send out our
MAC address, and do a resync.
|--------------------------------------------------------------------------*/
int port_connect_ask(Hdlc *hd)
{
BYTE rkt_header[8];
TraceStr( "Connect Ask");
rkt_header[0] = RK_CONNECT_ASK;
memcpy(&rkt_header[1], hd->nic->address,6);
hdlc_send_control(hd, rkt_header, 7,
NULL, 0, // ptr to data to send
hd->dest_addr); // MAC address to send to
hdlc_resync(hd);
return 0;
}
/*------------------------------------------------------------------
PortSetBaudRate - Set the desired baud rate. Return non-zero on error.
|-------------------------------------------------------------------*/
int PortSetBaudRate(SerPort *p,
ULONG desired_baud,
USHORT SetHardware,
DWORD clock_freq,
DWORD clk_prescaler)
{
ULONG diff;
ULONG act_baud;
ULONG percent_error;
ULONG div;
ULONG base_clock_rate;
base_clock_rate = ((clock_freq/16) / ((clk_prescaler & 0xf)+1));
// calculate the divisor for our hardware register.
// this is really just div = clk/desired_baud -1. but we do some
// work to minimize round-off error.
if (desired_baud <= 0) desired_baud = 1; // guard against div 0
div = ((base_clock_rate+(desired_baud>>1)) / desired_baud) - 1;
if (div > 8191) // overflow hardware divide register
div = 8191;
// this is really just (clk) / (div+1) but we do some
// work to minimize round-off error.
act_baud = (base_clock_rate+((div+1)>>1)) / (div+1);
if (desired_baud > act_baud)
diff = desired_baud - act_baud;
else
diff = act_baud - desired_baud;
percent_error = (diff * 100) / desired_baud;
if (percent_error > 5)
return (int) percent_error;
if (SetHardware)
{
p->change_flags |= CHG_BAUDRATE;
//---- OLD p->out_flags |= SC_BAUDRATE_CHANGE; // tells what needs changing to remote
p->baudrate = desired_baud;
}
return 0;
}
/*---------------------------------------------------------------------------
| check_ack_code - upload code, given ack packet, check for good status.
|---------------------------------------------------------------------------*/
int check_ack_code(PortMan *pm, BYTE *pkt)
{
int stat;
int snd;
TraceStr("CodeChk");
stat = eth_device_reply(UPLOAD_COMMAND,
0x00010000L + pm->code_cnt,
&snd,
NULL,
pkt);
return stat;
}
/*---------------------------------------------------------------------------
| send_go - send boot loader command to start execution of uploaded driver
at 1000:0 in memory.
|---------------------------------------------------------------------------*/
int send_go(PortMan *pm)
{
int stat;
BYTE *buf;
WORD io[4];
BYTE *tx_base;
TraceStr("GoSend");
hdlc_get_ctl_outpkt(pm->hd, &buf);
if (buf == NULL)
return 1;
tx_base = buf - 20; // backup to start of pkt
io[0] = 0x1000; // segment to go at
io[1] = 0; // offset to go at
// send more code, loading at 10000H location in mem.
// first just transfer data to an outgoing packet buffer
stat = ioctl_device(IOCTL_COMMAND,
(BYTE *) io,
buf,
12, // 12 = go command
4); // num bytes of data
// setup header
tx_base[14] = ASYNC_PRODUCT_HEADER_ID; // comtrol packet type = driver management, any product.
tx_base[15] = 0; // conc. index field
tx_base[16] = 1; // admin
*((WORD *)&tx_base[17]) = 40;
tx_base[19] = 1; // ADMIN packet type, 1=boot-loader, 3=id-reply
// send it.
stat = hdlc_send_raw(pm->hd, 60, NULL);
return 0;
}
/*---------------------------------------------------------------------------
| send_code - upload code.
|---------------------------------------------------------------------------*/
int send_code(PortMan *pm)
{
int stat;
BYTE *buf;
BYTE *tx_base;
long snd;
TraceStr("CodeSend");
// send more data
if (pm->code_cnt < Driver.MicroCodeSize)
{
if ((Driver.MicroCodeSize - pm->code_cnt) > 1000)
snd = 1000;
else
snd = Driver.MicroCodeSize - pm->code_cnt;
hdlc_get_ctl_outpkt(pm->hd, &buf);
if (buf == NULL)
{
TraceErr("CodeSend Err1A");
return 1;
}
tx_base = buf - 20; // backup to start of pkt
// send more code, loading at 10000H location in mem.
// first just transfer data to an outgoing packet buffer
stat = ioctl_device(UPLOAD_COMMAND,
&Driver.MicroCodeImage[pm->code_cnt],
buf,
0x00010000L + pm->code_cnt, // offset into memory
snd);
// setup header
tx_base[14] = ASYNC_PRODUCT_HEADER_ID; // comtrol packet type = driver management, any product.
tx_base[15] = 0; // conc. index field
tx_base[16] = 1; // admin
*((WORD *)&tx_base[17]) = snd+20;
tx_base[19] = 1; // ADMIN packet type, 1=boot-loader, 3=id-reply
// send it.
stat = hdlc_send_raw(pm->hd, snd+40, NULL);
}
return 0;
}