1999-04-16 03:35:26 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 1995 Advanced RISC Machines Limited. All rights reserved.
|
|
|
|
*
|
|
|
|
* This software may be freely used, copied, modified, and distributed
|
|
|
|
* provided that the above copyright notice is preserved in all copies of the
|
|
|
|
* software.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* -*-C-*-
|
|
|
|
*
|
|
|
|
* $Revision$
|
|
|
|
* $Date$
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* hostchan.c - Semi Synchronous Host side channel interface for Angel.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_TIME_H
|
|
|
|
# include <sys/time.h>
|
|
|
|
#else
|
|
|
|
# include "winsock.h"
|
|
|
|
# include "time.h"
|
|
|
|
#endif
|
|
|
|
#include "hsys.h"
|
|
|
|
#include "host.h"
|
|
|
|
#include "logging.h"
|
|
|
|
#include "chandefs.h"
|
|
|
|
#include "chanpriv.h"
|
|
|
|
#include "devclnt.h"
|
|
|
|
#include "buffers.h"
|
|
|
|
#include "drivers.h"
|
|
|
|
#include "adperr.h"
|
|
|
|
#include "devsw.h"
|
|
|
|
#include "hostchan.h"
|
|
|
|
|
|
|
|
#ifndef UNUSED
|
|
|
|
#define UNUSED(x) (x = x) /* Silence compiler warnings for unused arguments */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define HEARTRATE 5000000
|
|
|
|
|
|
|
|
/*
|
|
|
|
* list of available drivers, declared in drivers.c
|
|
|
|
*/
|
|
|
|
extern DeviceDescr *devices[];
|
|
|
|
|
|
|
|
static DeviceDescr *deviceToUse = NULL;
|
|
|
|
|
|
|
|
static struct Channel {
|
|
|
|
ChannelCallback callback;
|
|
|
|
void *callback_state;
|
|
|
|
} channels[CI_NUM_CHANNELS];
|
|
|
|
|
|
|
|
static unsigned char HomeSeq;
|
|
|
|
static unsigned char OppoSeq;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handler for DC_APPL packets
|
|
|
|
*/
|
|
|
|
static DC_Appl_Handler dc_appl_handler = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* slots for registered asynchronous processing callback procedures
|
|
|
|
*/
|
|
|
|
#define MAX_ASYNC_CALLBACKS 8
|
|
|
|
static unsigned int num_async_callbacks = 0;
|
|
|
|
static Adp_Async_Callback async_callbacks[MAX_ASYNC_CALLBACKS];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* writeQueueRoot is the queue of write requests pending acknowledgement
|
|
|
|
* writeQueueSend is the queue of pending write requests which will
|
|
|
|
* be a subset of the list writeQueueRoot
|
|
|
|
*/
|
|
|
|
static Packet *writeQueueRoot = NULL;
|
|
|
|
static Packet *writeQueueSend = NULL;
|
|
|
|
static Packet *resend_pkt = NULL;
|
|
|
|
static int resending = FALSE;
|
|
|
|
|
|
|
|
/* heartbeat_enabled is a flag used to indicate whether the heartbeat is
|
|
|
|
* currently turned on, heartbeat_enabled will be false in situations
|
|
|
|
* where even though a heartbeat is being used it is problematical or
|
|
|
|
* dis-advantageous to have it turned on, for instance during the
|
|
|
|
* initial stages of boot up
|
|
|
|
*/
|
|
|
|
unsigned int heartbeat_enabled = FALSE;
|
|
|
|
/* heartbeat_configured is set up by the device driver to indicate whether
|
|
|
|
* the heartbeat is being used during this debug session. In contrast to
|
|
|
|
* heartbeat_enabled it must not be changed during a session. The logic for
|
|
|
|
* deciding whether to send a heartbeat is: Is heartbeat_configured for this
|
|
|
|
* session? if and only if it is then if heartbeat[is currently]_enabled and
|
|
|
|
* we are due to send a pulse then send it
|
|
|
|
*/
|
|
|
|
unsigned int heartbeat_configured = TRUE;
|
|
|
|
|
|
|
|
void Adp_initSeq( void ) {
|
|
|
|
Packet *tmp_pkt = writeQueueSend;
|
|
|
|
|
|
|
|
HomeSeq = 0;
|
|
|
|
OppoSeq = 0;
|
|
|
|
if ( writeQueueSend != NULL) {
|
|
|
|
while (writeQueueSend->pk_next !=NULL) {
|
|
|
|
tmp_pkt = writeQueueSend;
|
|
|
|
writeQueueSend = tmp_pkt->pk_next;
|
|
|
|
DevSW_FreePacket(tmp_pkt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tmp_pkt = writeQueueRoot;
|
|
|
|
if ( writeQueueRoot == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (writeQueueRoot->pk_next !=NULL) {
|
|
|
|
tmp_pkt = writeQueueRoot;
|
|
|
|
writeQueueRoot = tmp_pkt->pk_next;
|
|
|
|
DevSW_FreePacket(tmp_pkt);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Function: DummyCallback
|
|
|
|
* Purpose: Default callback routine to handle unexpected input
|
|
|
|
* on a channel
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* Input: packet The received packet
|
|
|
|
*
|
|
|
|
* state Contains nothing of significance
|
|
|
|
*
|
|
|
|
* Returns: Nothing
|
|
|
|
*/
|
|
|
|
static void DummyCallback(Packet *packet, void *state)
|
|
|
|
{
|
|
|
|
ChannelID chan;
|
|
|
|
const char fmt[] = "Unexpected read on channel %u, length %d\n";
|
|
|
|
char fmtbuf[sizeof(fmt) + 24];
|
|
|
|
|
|
|
|
UNUSED(state);
|
|
|
|
|
|
|
|
chan = *(packet->pk_buffer);
|
|
|
|
sprintf(fmtbuf, fmt, chan, packet->pk_length);
|
|
|
|
printf(fmtbuf);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* junk this packet
|
|
|
|
*/
|
|
|
|
DevSW_FreePacket(packet);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Function: BlockingCallback
|
|
|
|
* Purpose: Callback routine used to implement a blocking read call
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* Input: packet The received packet.
|
|
|
|
*
|
|
|
|
* Output: state Address of higher level's pointer to the received
|
|
|
|
* packet.
|
|
|
|
*
|
|
|
|
* Returns: Nothing
|
|
|
|
*/
|
|
|
|
static void BlockingCallback(Packet *packet, void *state)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Pass the packet back to the caller which requested a packet
|
|
|
|
* from this channel. This also flags the completion of the I/O
|
|
|
|
* request to the blocking read call.
|
|
|
|
*/
|
|
|
|
*((Packet **)state) = packet;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Function: FireCallback
|
|
|
|
* Purpose: Pass received packet along to the callback routine for
|
|
|
|
* the appropriate channel
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* Input: packet The received packet.
|
|
|
|
*
|
|
|
|
* Returns: Nothing
|
|
|
|
*
|
|
|
|
* Post-conditions: The Target-to-Host sequence number for the channel
|
|
|
|
* will have been incremented.
|
|
|
|
*/
|
|
|
|
static void FireCallback(Packet *packet)
|
|
|
|
{
|
|
|
|
ChannelID chan;
|
|
|
|
struct Channel *ch;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* is this a sensible channel number?
|
|
|
|
*/
|
|
|
|
chan = *(packet->pk_buffer);
|
|
|
|
if (invalidChannelID(chan))
|
|
|
|
{
|
|
|
|
printf("ERROR: invalid ChannelID received from target\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* free the packet's resources, 'cause no-one else will
|
|
|
|
*/
|
|
|
|
DevSW_FreePacket(packet);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* looks OK - increment sequence number, and pass packet to callback
|
|
|
|
*/
|
|
|
|
ch = channels + chan;
|
|
|
|
(ch->callback)(packet, ch->callback_state);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These are the externally visible functions. They are documented
|
|
|
|
* in hostchan.h
|
|
|
|
*/
|
|
|
|
void Adp_addToQueue(Packet **head, Packet *newpkt)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* this is a bit of a hack
|
|
|
|
*/
|
|
|
|
Packet *pk;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make sure that the hack we are about to use will work as expected
|
|
|
|
*/
|
|
|
|
ASSERT(&(((Packet *)0)->pk_next) == 0, "bad struct Packet layout");
|
|
|
|
|
1999-11-02 05:44:47 +01:00
|
|
|
#if defined(DEBUG) && 0
|
1999-04-16 03:35:26 +02:00
|
|
|
printf("Adp_addToQueue(%p, %p)\n", head, newpkt);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* here's the hack - it relies upon the next
|
|
|
|
* pointer being at the start of Packet.
|
|
|
|
*/
|
|
|
|
pk = (Packet *)(head);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* skip to the end of the queue
|
|
|
|
*/
|
|
|
|
while (pk->pk_next != NULL)
|
|
|
|
pk = pk->pk_next;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* now add the new element
|
|
|
|
*/
|
|
|
|
newpkt->pk_next = NULL;
|
|
|
|
pk->pk_next = newpkt;
|
|
|
|
}
|
|
|
|
|
|
|
|
Packet *Adp_removeFromQueue(Packet **head)
|
|
|
|
{
|
|
|
|
struct Packet *pk;
|
|
|
|
|
|
|
|
pk = *head;
|
|
|
|
|
|
|
|
if (pk != NULL)
|
|
|
|
*head = pk->pk_next;
|
|
|
|
|
|
|
|
return pk;
|
|
|
|
}
|
|
|
|
|
1999-11-02 05:44:47 +01:00
|
|
|
void Adp_SetLogEnable(int logEnableFlag)
|
|
|
|
{
|
|
|
|
DevSW_SetLogEnable(logEnableFlag);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Adp_SetLogfile(const char *filename)
|
|
|
|
{
|
|
|
|
DevSW_SetLogfile(filename);
|
|
|
|
}
|
|
|
|
|
1999-04-16 03:35:26 +02:00
|
|
|
AdpErrs Adp_OpenDevice(const char *name, const char *arg,
|
|
|
|
unsigned int heartbeat_on)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
AdpErrs retc;
|
|
|
|
ChannelID chan;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("Adp_OpenDevice(%s, %s)\n", name, arg ? arg : "<NULL>");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
heartbeat_configured = heartbeat_on;
|
|
|
|
if (deviceToUse != NULL)
|
|
|
|
return adp_device_already_open;
|
|
|
|
|
|
|
|
for (i = 0; (deviceToUse = devices[i]) != NULL; ++i)
|
|
|
|
if (DevSW_Match(deviceToUse, name, arg) == adp_ok)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (deviceToUse == NULL)
|
|
|
|
return adp_device_not_found;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we seem to have found a suitable device driver, so try to open it
|
|
|
|
*/
|
|
|
|
if ((retc = DevSW_Open(deviceToUse, name, arg, DC_DBUG)) != adp_ok)
|
|
|
|
{
|
|
|
|
/* we don't have a device to use */
|
|
|
|
deviceToUse = NULL;
|
|
|
|
return retc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* there is no explicit open on channels any more, so
|
|
|
|
* initialise state for all channels.
|
|
|
|
*/
|
|
|
|
for (chan = 0; chan < CI_NUM_CHANNELS; ++chan)
|
|
|
|
{
|
|
|
|
struct Channel *ch = channels + chan;
|
|
|
|
|
|
|
|
ch->callback = DummyCallback;
|
|
|
|
ch->callback_state = NULL;
|
|
|
|
OppoSeq = 0;
|
|
|
|
HomeSeq = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return adp_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
AdpErrs Adp_CloseDevice(void)
|
|
|
|
{
|
|
|
|
AdpErrs retc;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("Adp_CloseDevice\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (deviceToUse == NULL)
|
|
|
|
return adp_device_not_open;
|
|
|
|
|
|
|
|
heartbeat_enabled = FALSE;
|
|
|
|
|
|
|
|
retc = DevSW_Close(deviceToUse, DC_DBUG);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we have to clear deviceToUse, even when the lower layers
|
|
|
|
* faulted the close, otherwise the condition will never clear
|
|
|
|
*/
|
|
|
|
if (retc != adp_ok)
|
|
|
|
WARN("DevSW_Close faulted the call");
|
|
|
|
|
|
|
|
deviceToUse = NULL;
|
|
|
|
return retc;
|
|
|
|
}
|
|
|
|
|
|
|
|
AdpErrs Adp_Ioctl(int opcode, void *args)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("Adp_Ioctl\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (deviceToUse == NULL)
|
|
|
|
return adp_device_not_open;
|
|
|
|
|
|
|
|
return DevSW_Ioctl(deviceToUse, opcode, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
AdpErrs Adp_ChannelRegisterRead(const ChannelID chan,
|
|
|
|
const ChannelCallback cbfunc,
|
|
|
|
void *cbstate)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("Adp_ChannelRegisterRead(%d, %p, %x)\n", chan, cbfunc, cbstate);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (deviceToUse == NULL)
|
|
|
|
return adp_device_not_open;
|
|
|
|
|
|
|
|
if (invalidChannelID(chan))
|
|
|
|
return adp_bad_channel_id;
|
|
|
|
|
|
|
|
if (cbfunc == NULL)
|
|
|
|
{
|
|
|
|
channels[chan].callback = DummyCallback;
|
|
|
|
channels[chan].callback_state = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
channels[chan].callback = cbfunc;
|
|
|
|
channels[chan].callback_state = cbstate;
|
|
|
|
}
|
|
|
|
|
|
|
|
return adp_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
AdpErrs Adp_ChannelRead(const ChannelID chan, Packet **packet)
|
|
|
|
{
|
|
|
|
struct Channel *ch;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("Adp_ChannelRead(%d, %x)\n", chan, *packet);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (deviceToUse == NULL)
|
|
|
|
return adp_device_not_open;
|
|
|
|
|
|
|
|
if (invalidChannelID(chan))
|
|
|
|
return adp_bad_channel_id;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if a callback has already been registered for this
|
|
|
|
* channel, then we do not allow this blocking read.
|
|
|
|
*/
|
|
|
|
ch = channels + chan;
|
|
|
|
if (ch->callback != DummyCallback)
|
|
|
|
return adp_callback_already_registered;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, use our own callback to wait for a packet to arrive
|
|
|
|
* on this channel
|
|
|
|
*/
|
|
|
|
ch->callback = BlockingCallback;
|
|
|
|
ch->callback_state = packet;
|
|
|
|
*packet = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* keep polling until a packet appears for this channel
|
|
|
|
*/
|
|
|
|
while (((volatile Packet *)(*packet)) == NULL)
|
|
|
|
/*
|
|
|
|
* this call will block until a packet is read on any channel
|
|
|
|
*/
|
|
|
|
Adp_AsynchronousProcessing(async_block_on_read);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, the packet has arrived: clear the callback
|
|
|
|
*/
|
|
|
|
ch->callback = DummyCallback;
|
|
|
|
ch->callback_state = NULL;
|
|
|
|
|
|
|
|
return adp_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
static AdpErrs ChannelWrite(
|
|
|
|
const ChannelID chan, Packet *packet, AsyncMode mode)
|
|
|
|
{
|
|
|
|
struct Channel *ch;
|
|
|
|
unsigned char *cptr;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "Adp_ChannelWrite(%d, %x)\n", chan, packet );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (deviceToUse == NULL)
|
|
|
|
return adp_device_not_open;
|
|
|
|
|
|
|
|
if (invalidChannelID(chan))
|
|
|
|
return adp_bad_channel_id;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fill in the channels header at the start of this buffer
|
|
|
|
*/
|
|
|
|
ch = channels + chan;
|
|
|
|
cptr = packet->pk_buffer;
|
|
|
|
*cptr++ = chan;
|
|
|
|
*cptr = 0;
|
|
|
|
packet->pk_length += CHAN_HEADER_SIZE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, add this packet to the write queue, and try to flush it out
|
|
|
|
*/
|
|
|
|
|
|
|
|
Adp_addToQueue(&writeQueueSend, packet);
|
|
|
|
Adp_AsynchronousProcessing(mode);
|
|
|
|
|
|
|
|
return adp_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
AdpErrs Adp_ChannelWrite(const ChannelID chan, Packet *packet) {
|
|
|
|
return ChannelWrite(chan, packet, async_block_on_write);
|
|
|
|
}
|
|
|
|
|
|
|
|
AdpErrs Adp_ChannelWriteAsync(const ChannelID chan, Packet *packet) {
|
|
|
|
return ChannelWrite(chan, packet, async_block_on_nothing);
|
|
|
|
}
|
|
|
|
|
|
|
|
static AdpErrs send_resend_msg(DeviceID devid) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send a resend message, usually in response to a bad packet or
|
|
|
|
* a resend request */
|
|
|
|
Packet * packet;
|
|
|
|
packet = DevSW_AllocatePacket(CF_DATA_BYTE_POS);
|
|
|
|
packet->pk_buffer[CF_CHANNEL_BYTE_POS] = CI_PRIVATE;
|
|
|
|
packet->pk_buffer[CF_HOME_SEQ_BYTE_POS] = HomeSeq;
|
|
|
|
packet->pk_buffer[CF_OPPO_SEQ_BYTE_POS] = OppoSeq;
|
|
|
|
packet->pk_buffer[CF_FLAGS_BYTE_POS] = CF_RELIABLE | CF_RESEND;
|
|
|
|
packet->pk_length = CF_DATA_BYTE_POS;
|
|
|
|
return DevSW_Write(deviceToUse, packet, devid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static AdpErrs check_seq(unsigned char msg_home, unsigned char msg_oppo) {
|
|
|
|
Packet *tmp_pkt;
|
|
|
|
|
|
|
|
UNUSED(msg_oppo);
|
|
|
|
/*
|
|
|
|
* check if we have got an ack for anything and if so remove it from the
|
|
|
|
* queue
|
|
|
|
*/
|
|
|
|
if (msg_home == (unsigned char)(OppoSeq+1)) {
|
|
|
|
/*
|
|
|
|
* arrived in sequence can increment our opposing seq number and remove
|
|
|
|
* the relevant packet from our queue
|
|
|
|
* check that the packet we're going to remove really is the right one
|
|
|
|
*/
|
|
|
|
tmp_pkt = writeQueueRoot;
|
|
|
|
while ((tmp_pkt->pk_next != NULL) &&
|
|
|
|
(tmp_pkt->pk_next->pk_buffer[CF_HOME_SEQ_BYTE_POS]
|
|
|
|
!= OppoSeq)){
|
|
|
|
tmp_pkt = tmp_pkt->pk_next;
|
|
|
|
}
|
|
|
|
OppoSeq++;
|
|
|
|
if (tmp_pkt->pk_next == NULL) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("trying to remove a non existant packet\n");
|
|
|
|
#endif
|
|
|
|
return adp_bad_packet;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Packet *tmp = tmp_pkt->pk_next;
|
|
|
|
#ifdef RET_DEBUG
|
|
|
|
printf("removing a packet from the root queue\n");
|
|
|
|
#endif
|
|
|
|
tmp_pkt->pk_next = tmp_pkt->pk_next->pk_next;
|
|
|
|
/* remove the appropriate packet */
|
|
|
|
DevSW_FreePacket(tmp);
|
|
|
|
return adp_ok;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (msg_home < (unsigned char) (OppoSeq+1)){
|
|
|
|
/* already received this message */
|
|
|
|
#ifdef RET_DEBUG
|
|
|
|
printf("sequence numbers low\n");
|
|
|
|
#endif
|
|
|
|
return adp_seq_low;
|
|
|
|
}
|
|
|
|
else { /* we've missed something */
|
|
|
|
#ifdef RET_DEBUG
|
|
|
|
printf("sequence numbers high\n");
|
|
|
|
#endif
|
|
|
|
return adp_seq_high;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long tv_diff(const struct timeval *time_now,
|
|
|
|
const struct timeval *time_was)
|
|
|
|
{
|
|
|
|
return ( ((time_now->tv_sec * 1000000) + time_now->tv_usec)
|
|
|
|
- ((time_was->tv_sec * 1000000) + time_was->tv_usec) );
|
|
|
|
}
|
|
|
|
|
|
|
|
#if !defined(__unix) && !defined(__CYGWIN32__)
|
|
|
|
static void gettimeofday( struct timeval *time_now, void *dummy )
|
|
|
|
{
|
|
|
|
time_t t = clock();
|
|
|
|
UNUSED(dummy);
|
|
|
|
time_now->tv_sec = t/CLOCKS_PER_SEC;
|
|
|
|
time_now->tv_usec = (t%CLOCKS_PER_SEC)*(1000000/CLOCKS_PER_SEC);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static AdpErrs pacemaker(void)
|
|
|
|
{
|
|
|
|
Packet *packet;
|
|
|
|
|
|
|
|
packet = DevSW_AllocatePacket(CF_DATA_BYTE_POS);
|
|
|
|
if (packet == NULL) {
|
|
|
|
printf("ERROR: could not allocate a packet in pacemaker()\n");
|
|
|
|
return adp_malloc_failure;
|
|
|
|
}
|
|
|
|
packet->pk_buffer[CF_CHANNEL_BYTE_POS] = CI_PRIVATE;
|
|
|
|
packet->pk_buffer[CF_HOME_SEQ_BYTE_POS] = HomeSeq;
|
|
|
|
packet->pk_buffer[CF_OPPO_SEQ_BYTE_POS] = OppoSeq;
|
|
|
|
packet->pk_buffer[CF_FLAGS_BYTE_POS] = CF_RELIABLE | CF_HEARTBEAT;
|
|
|
|
packet->pk_length = CF_DATA_BYTE_POS;
|
|
|
|
return DevSW_Write(deviceToUse, packet, DC_DBUG);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef FAKE_BAD_LINE_RX
|
|
|
|
static AdpErrs fake_bad_line_rx( const Packet *const packet, AdpErrs adp_err )
|
|
|
|
{
|
|
|
|
static unsigned int bl_num = 0;
|
|
|
|
|
|
|
|
if ( (packet != NULL)
|
|
|
|
&& (bl_num++ >= 20 )
|
|
|
|
&& ((bl_num % FAKE_BAD_LINE_RX) == 0))
|
|
|
|
{
|
|
|
|
printf("DEBUG: faking a bad packet\n");
|
|
|
|
return adp_bad_packet;
|
|
|
|
}
|
|
|
|
return adp_err;
|
|
|
|
}
|
|
|
|
#endif /* def FAKE_BAD_LINE_RX */
|
|
|
|
|
|
|
|
#ifdef FAKE_BAD_LINE_TX
|
|
|
|
static unsigned char tmp_ch;
|
|
|
|
|
|
|
|
static void fake_bad_line_tx( void )
|
|
|
|
{
|
|
|
|
static unsigned int bl_num = 0;
|
|
|
|
|
|
|
|
/* give the thing a chance to boot then try corrupting stuff */
|
|
|
|
if ( (bl_num++ >= 20) && ((bl_num % FAKE_BAD_LINE_TX) == 0))
|
|
|
|
{
|
|
|
|
printf("DEBUG: faking a bad packet for tx\n");
|
|
|
|
tmp_ch = writeQueueSend->pk_buffer[CF_FLAGS_BYTE_POS];
|
|
|
|
writeQueueSend->pk_buffer[CF_FLAGS_BYTE_POS] = 77;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unfake_bad_line_tx( void )
|
|
|
|
{
|
|
|
|
static unsigned int bl_num = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* must reset the packet so that its not corrupted when we
|
|
|
|
* resend it
|
|
|
|
*/
|
|
|
|
if ( (bl_num >= 20) && ((bl_num % FAKE_BAD_LINE_TX) != 0))
|
|
|
|
{
|
|
|
|
writeQueueSend->pk_buffer[CF_FLAGS_BYTE_POS] = tmp_ch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* def FAKE_BAD_LINE_TX */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE: we are assuming that a resolution of microseconds will
|
|
|
|
* be good enough for the purporses of the heartbeat. If this proves
|
|
|
|
* not to be the case then we may need a rethink, possibly using
|
|
|
|
* [get,set]itimer
|
|
|
|
*/
|
|
|
|
static struct timeval time_now;
|
|
|
|
static struct timeval time_lastalive;
|
|
|
|
|
|
|
|
static void async_process_dbug_read( const AsyncMode mode,
|
|
|
|
bool *const finished )
|
|
|
|
{
|
|
|
|
Packet *packet;
|
|
|
|
unsigned int msg_home, msg_oppo;
|
|
|
|
AdpErrs adp_err;
|
|
|
|
|
|
|
|
adp_err = DevSW_Read(deviceToUse, DC_DBUG, &packet,
|
|
|
|
mode == async_block_on_read );
|
|
|
|
|
|
|
|
#ifdef FAKE_BAD_LINE_RX
|
|
|
|
adp_err = fake_bad_line_rx( packet, adp_err );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (adp_err == adp_bad_packet) {
|
|
|
|
/* We got a bad packet, ask for a resend, send a resend message */
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("received a bad packet\n");
|
|
|
|
#endif
|
|
|
|
send_resend_msg(DC_DBUG);
|
|
|
|
}
|
|
|
|
else if (packet != NULL)
|
|
|
|
{
|
|
|
|
/* update the heartbeat clock */
|
|
|
|
gettimeofday(&time_lastalive, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we got a live one here - were we waiting for it?
|
|
|
|
*/
|
|
|
|
if (mode == async_block_on_read)
|
|
|
|
/* not any more */
|
|
|
|
*finished = TRUE;
|
|
|
|
#ifdef RETRANS
|
|
|
|
|
|
|
|
if (packet->pk_length < CF_DATA_BYTE_POS) {
|
|
|
|
/* we've got a packet with no header information! */
|
|
|
|
printf("ERROR: packet with no transport header\n");
|
|
|
|
send_resend_msg(DC_DBUG);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
#ifdef RET_DEBUG
|
|
|
|
unsigned int c;
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
* TODO: Check to see if its acknowledgeing anything, remove
|
|
|
|
* those packets it is from the queue. If its a retrans add the
|
|
|
|
* packets to the queue
|
|
|
|
*/
|
|
|
|
msg_home = packet->pk_buffer[CF_HOME_SEQ_BYTE_POS];
|
|
|
|
msg_oppo = packet->pk_buffer[CF_OPPO_SEQ_BYTE_POS];
|
|
|
|
#ifdef RET_DEBUG
|
|
|
|
printf("msg seq numbers are hseq 0x%x oseq 0x%x\n",
|
|
|
|
msg_home, msg_oppo);
|
|
|
|
for (c=0;c<packet->pk_length;c++)
|
|
|
|
printf("%02.2x", packet->pk_buffer[c]);
|
|
|
|
printf("\n");
|
|
|
|
#endif
|
|
|
|
/* now was it a resend request? */
|
|
|
|
if ((packet->pk_buffer[CF_FLAGS_BYTE_POS])
|
|
|
|
& CF_RESEND) {
|
|
|
|
/* we've been asked for a resend so we had better resend */
|
|
|
|
/*
|
|
|
|
* I don't think we can use a resend as acknowledgement for
|
|
|
|
* anything so lets not do this for the moment
|
|
|
|
* check_seq(msg_home, msg_oppo);
|
|
|
|
*/
|
|
|
|
#ifdef RET_DEBUG
|
|
|
|
printf("received a resend request\n");
|
|
|
|
#endif
|
|
|
|
if (HomeSeq != msg_oppo) {
|
|
|
|
int found = FALSE;
|
|
|
|
/* need to resend from msg_oppo +1 upwards */
|
|
|
|
DevSW_FreePacket(packet);
|
|
|
|
resending = TRUE;
|
|
|
|
/* find the correct packet to resend from */
|
|
|
|
packet = writeQueueRoot;
|
|
|
|
while (((packet->pk_next) != NULL) && !found) {
|
|
|
|
if ((packet->pk_buffer[CF_OPPO_SEQ_BYTE_POS])
|
|
|
|
!= msg_oppo+1) {
|
|
|
|
resend_pkt = packet;
|
|
|
|
found = TRUE;
|
|
|
|
}
|
|
|
|
packet = packet->pk_next;
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
panic("trying to resend non-existent packets\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (OppoSeq != msg_home) {
|
|
|
|
/*
|
|
|
|
* send a resend request telling the target where we think
|
|
|
|
* the world is at
|
|
|
|
*/
|
|
|
|
DevSW_FreePacket(packet);
|
|
|
|
send_resend_msg(DC_DBUG);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* not a resend request, lets check the sequence numbers */
|
|
|
|
|
|
|
|
if ((packet->pk_buffer[CF_CHANNEL_BYTE_POS] != CI_HBOOT) &&
|
|
|
|
(packet->pk_buffer[CF_CHANNEL_BYTE_POS] != CI_TBOOT)) {
|
|
|
|
adp_err = check_seq(msg_home, msg_oppo);
|
|
|
|
if (adp_err == adp_seq_low) {
|
|
|
|
/* we have already received this packet so discard */
|
|
|
|
DevSW_FreePacket(packet);
|
|
|
|
}
|
|
|
|
else if (adp_err == adp_seq_high) {
|
|
|
|
/*
|
|
|
|
* we must have missed a packet somewhere, discard this
|
|
|
|
* packet and tell the target where we are
|
|
|
|
*/
|
|
|
|
DevSW_FreePacket(packet);
|
|
|
|
send_resend_msg(DC_DBUG);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/*
|
|
|
|
* now pass the packet to whoever is waiting for it
|
|
|
|
*/
|
|
|
|
FireCallback(packet);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
FireCallback(packet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
/*
|
|
|
|
* now pass the packet to whoever is waiting for it
|
|
|
|
*/
|
|
|
|
FireCallback(packet);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void async_process_appl_read(void)
|
|
|
|
{
|
|
|
|
Packet *packet;
|
|
|
|
AdpErrs adp_err;
|
|
|
|
|
|
|
|
/* see if there is anything for the DC_APPL channel */
|
|
|
|
adp_err = DevSW_Read(deviceToUse, DC_APPL, &packet, FALSE);
|
|
|
|
|
|
|
|
if (adp_err == adp_ok && packet != NULL)
|
|
|
|
{
|
|
|
|
/* got an application packet on a shared device */
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("GOT DC_APPL PACKET: len %d\nData: ", packet->pk_length);
|
|
|
|
{
|
|
|
|
unsigned int c;
|
|
|
|
for ( c = 0; c < packet->pk_length; ++c )
|
|
|
|
printf( "%02X ", packet->pk_buffer[c] );
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (dc_appl_handler != NULL)
|
|
|
|
{
|
|
|
|
dc_appl_handler( deviceToUse, packet );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* for now, just free it!! */
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("no handler - dropping DC_APPL packet\n");
|
|
|
|
#endif
|
|
|
|
DevSW_FreePacket( packet );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void async_process_write( const AsyncMode mode,
|
|
|
|
bool *const finished )
|
|
|
|
{
|
|
|
|
Packet *packet;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
static unsigned int num_written = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE: here we rely in the fact that any packet in the writeQueueSend
|
|
|
|
* section of the queue will need its sequence number setting up while
|
|
|
|
* and packet in the writeQueueRoot section will have its sequence
|
|
|
|
* numbers set up from when it was first sent so we can easily look
|
|
|
|
* up the packet numbers when(if) we want to resend the packet.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (writeQueueSend!=NULL)
|
|
|
|
printf("written 0x%x\n",num_written += writeQueueSend->pk_length);
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
* give the switcher a chance to complete any partial writes
|
|
|
|
*/
|
|
|
|
if (DevSW_FlushPendingWrite(deviceToUse) == adp_write_busy)
|
|
|
|
{
|
|
|
|
/* no point trying a new write */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* now see whether there is anything to write
|
|
|
|
*/
|
|
|
|
packet = NULL;
|
|
|
|
if (resending) {
|
|
|
|
packet = resend_pkt;
|
|
|
|
#ifdef RET_DEBUG
|
|
|
|
printf("resending hseq 0x%x oseq 0x%x\n",
|
|
|
|
packet->pk_buffer[CF_HOME_SEQ_BYTE_POS],
|
|
|
|
packet->pk_buffer[CF_OPPO_SEQ_BYTE_POS]);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else if (writeQueueSend != NULL) {
|
|
|
|
#ifdef RETRANS
|
|
|
|
/* set up the sequence number on the packet */
|
|
|
|
packet = writeQueueSend;
|
|
|
|
HomeSeq++;
|
|
|
|
(writeQueueSend->pk_buffer[CF_OPPO_SEQ_BYTE_POS])
|
|
|
|
= OppoSeq;
|
|
|
|
(writeQueueSend->pk_buffer[CF_HOME_SEQ_BYTE_POS])
|
|
|
|
= HomeSeq;
|
|
|
|
(writeQueueSend->pk_buffer[CF_FLAGS_BYTE_POS])
|
|
|
|
= CF_RELIABLE;
|
|
|
|
# ifdef RET_DEBUG
|
|
|
|
printf("sending packet with hseq 0x%x oseq 0x%x\n",
|
|
|
|
writeQueueSend->pk_buffer[CF_HOME_SEQ_BYTE_POS],
|
|
|
|
writeQueueSend->pk_buffer[CF_OPPO_SEQ_BYTE_POS]);
|
|
|
|
# endif
|
|
|
|
#endif /* RETRANS */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packet != NULL) {
|
|
|
|
AdpErrs dev_err;
|
|
|
|
|
|
|
|
#ifdef FAKE_BAD_LINE_TX
|
|
|
|
fake_bad_line_tx();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dev_err = DevSW_Write(deviceToUse, packet, DC_DBUG);
|
|
|
|
if (dev_err == adp_ok) {
|
|
|
|
#ifdef RETRANS
|
|
|
|
if (resending) {
|
|
|
|
/* check to see if we've recovered yet */
|
|
|
|
if ((packet->pk_next) == NULL){
|
|
|
|
# ifdef RET_DEBUG
|
|
|
|
printf("we have recovered\n");
|
|
|
|
# endif
|
|
|
|
resending = FALSE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
resend_pkt = resend_pkt->pk_next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* move the packet we just sent from the send queue to the root
|
|
|
|
*/
|
|
|
|
Packet *tmp_pkt, *tmp;
|
|
|
|
|
|
|
|
# ifdef FAKE_BAD_LINE_TX
|
|
|
|
unfake_bad_line_tx();
|
|
|
|
# endif
|
|
|
|
|
|
|
|
tmp_pkt = writeQueueSend;
|
|
|
|
writeQueueSend = writeQueueSend->pk_next;
|
|
|
|
tmp_pkt->pk_next = NULL;
|
|
|
|
if (writeQueueRoot == NULL)
|
|
|
|
writeQueueRoot = tmp_pkt;
|
|
|
|
else {
|
|
|
|
tmp = writeQueueRoot;
|
|
|
|
while (tmp->pk_next != NULL) {
|
|
|
|
tmp = tmp->pk_next;
|
|
|
|
}
|
|
|
|
tmp->pk_next = tmp_pkt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else /* not RETRANS */
|
|
|
|
/*
|
|
|
|
* switcher has taken the write, so remove it from the
|
|
|
|
* queue, and free its resources
|
|
|
|
*/
|
|
|
|
DevSW_FreePacket(Adp_removeFromQueue(&writeQueueSend));
|
|
|
|
#endif /* if RETRANS ... else ... */
|
|
|
|
|
|
|
|
if (mode == async_block_on_write)
|
|
|
|
*finished = DevSW_WriteFinished(deviceToUse);
|
|
|
|
|
|
|
|
} /* endif write ok */
|
|
|
|
}
|
|
|
|
else /* packet == NULL */
|
|
|
|
{
|
|
|
|
if (mode == async_block_on_write)
|
|
|
|
*finished = DevSW_WriteFinished(deviceToUse);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void async_process_heartbeat( void )
|
|
|
|
{
|
|
|
|
/* check to see whether we need to send a heartbeat */
|
|
|
|
gettimeofday(&time_now, NULL);
|
|
|
|
|
|
|
|
if (tv_diff(&time_now, &time_lastalive) >= HEARTRATE)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* if we've not booted then don't do send a heartrate the link
|
|
|
|
* must be reliable enough for us to boot without any clever stuff,
|
|
|
|
* if we can't do this then theres little chance of the link staying
|
|
|
|
* together even with the resends etc
|
|
|
|
*/
|
|
|
|
if (heartbeat_enabled) {
|
|
|
|
gettimeofday(&time_lastalive, NULL);
|
|
|
|
pacemaker();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void async_process_callbacks( void )
|
|
|
|
{
|
|
|
|
/* call any registered asynchronous callbacks */
|
|
|
|
unsigned int i;
|
|
|
|
for ( i = 0; i < num_async_callbacks; ++i )
|
|
|
|
async_callbacks[i]( deviceToUse, &time_now );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Adp_AsynchronousProcessing(const AsyncMode mode)
|
|
|
|
{
|
|
|
|
bool finished = FALSE;
|
|
|
|
#ifdef DEBUG
|
|
|
|
unsigned int wc = 0, dc = 0, ac = 0, hc = 0;
|
|
|
|
# define INC_COUNT(x) ((x)++)
|
|
|
|
#else
|
|
|
|
# define INC_COUNT(x)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ((time_lastalive.tv_sec == 0) && (time_lastalive.tv_usec == 0)) {
|
|
|
|
/* first time through, needs initing */
|
|
|
|
gettimeofday(&time_lastalive, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* main loop */
|
|
|
|
do
|
|
|
|
{
|
|
|
|
async_process_write( mode, &finished );
|
|
|
|
INC_COUNT(wc);
|
|
|
|
|
|
|
|
if ( ! finished && mode != async_block_on_write )
|
|
|
|
{
|
|
|
|
async_process_dbug_read( mode, &finished );
|
|
|
|
INC_COUNT(dc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! finished && mode != async_block_on_write )
|
|
|
|
{
|
|
|
|
async_process_appl_read();
|
|
|
|
INC_COUNT(ac);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! finished )
|
|
|
|
{
|
|
|
|
if (heartbeat_configured)
|
|
|
|
async_process_heartbeat();
|
|
|
|
async_process_callbacks();
|
|
|
|
INC_COUNT(hc);
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (!finished && mode != async_block_on_nothing);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if ( mode != async_block_on_nothing )
|
|
|
|
printf( "Async: %s - w %d, d %d, a %d, h %d\n",
|
|
|
|
mode == async_block_on_write ? "blk_write" : "blk_read",
|
|
|
|
wc, dc, ac, hc );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* install a handler for DC_APPL packets (can be NULL), returning old one.
|
|
|
|
*/
|
|
|
|
DC_Appl_Handler Adp_Install_DC_Appl_Handler(const DC_Appl_Handler handler)
|
|
|
|
{
|
|
|
|
DC_Appl_Handler old_handler = dc_appl_handler;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "Installing DC_APPL handler %x (old %x)\n", handler, old_handler );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dc_appl_handler = handler;
|
|
|
|
return old_handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add an asynchronous processing callback to the list
|
|
|
|
* TRUE == okay, FALSE == no more async processing slots
|
|
|
|
*/
|
|
|
|
bool Adp_Install_Async_Callback( const Adp_Async_Callback callback_proc )
|
|
|
|
{
|
|
|
|
if ( num_async_callbacks < MAX_ASYNC_CALLBACKS && callback_proc != NULL )
|
|
|
|
{
|
|
|
|
async_callbacks[num_async_callbacks] = callback_proc;
|
|
|
|
++num_async_callbacks;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* delay for a given period (in microseconds)
|
|
|
|
*/
|
|
|
|
void Adp_delay(unsigned int period)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("delaying for %d microseconds\n", period);
|
|
|
|
#endif
|
|
|
|
tv.tv_sec = (period / 1000000);
|
|
|
|
tv.tv_usec = (period % 1000000);
|
|
|
|
|
|
|
|
(void)select(0, NULL, NULL, NULL, &tv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* EOF hostchan.c */
|