diff options
Diffstat (limited to 'gdb/rdi-share/hostchan.c')
-rw-r--r-- | gdb/rdi-share/hostchan.c | 1057 |
1 files changed, 1057 insertions, 0 deletions
diff --git a/gdb/rdi-share/hostchan.c b/gdb/rdi-share/hostchan.c new file mode 100644 index 0000000..8e41da4 --- /dev/null +++ b/gdb/rdi-share/hostchan.c @@ -0,0 +1,1057 @@ +/* + * 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"); + +#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 */ |