NetBSD/gnu/dist/gdb/rdi-share/hostchan.c

1056 lines
28 KiB
C

/*
* 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-*-
*
*
*
* hostchan.c - Semi Synchronous Host side channel interface for Angel.
*/
#include <stdio.h>
#if defined(__unix) || defined(__CYGWIN32__)
# 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");
#if DEBUG && 0
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;
}
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 */