/* * 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 #ifdef HAVE_SYS_TIME_H # include #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 : ""); #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(__CYGWIN__) 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;cpk_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 */