int eth_trace = 0; #define ETH_DBUG 0 #define ETH_TRACE if(eth_trace) printf #if APELLEgWM #include "apelle.h" int db_eth = -1; #endif #define ANR_DBUG 0 #define ANR_INLINE 1 /* Use inline assembler instead of extern module */ /* ** RAWETH.C --- 14-Nov-1989 - Specific ETHERNET Code for the IBM-PC */ #include #include #include #define TS /* We want TS layer visibility */ #include "syspec.h" #include "rpcrts.h" #include "rpc_code.h" #if ANR_DBUG #define ethspy(a, b) *(unsigned *)MK_FP(0xB800, a) = b #else #define ethspy(a, b) #endif /* ============= TS / AST specific information ============== */ #define PROTECT asm cli #define UNPROTECT asm sti #define NO_AST_FLAG (rpc_pointer)0 #define SET_AST_FLAG (rpc_pointer)1 #define NIL_SOCKET_PTR (socket_type)0 #define NIL_MESSAGE_PTR (rpc_message_pointer)0 volatile socket_type eth_rxq; /* Queue of sockets waiting for ETH packets */ int eth_usage; /* Usage count for ETH */ extern void eth_ast_service(); /* ============= BICC specific information ========================== */ #define INT 0x5B #define ACB_LEN 54 /* minus 1 */ #define TCB_LEN 68 /* minus 1 */ #define LG_STACK 4096 #define M_LDATA 1500 /* Maximum packet length Blue Book MAC */ /* Command codes */ #define CD_M_ACTIVATE_SAP 0xfa #define CD_M_DEACTIVATE_SAP 0Xfb #define CD_L_DATA_SEND 0xf4 #define CD_STATUS 0xb3 #define CF_M_ACTIVATE_SAP 0x12 #define CF_M_DEACTIVATE_SAP 0X14 #define ID_M_DATA 0X0a /* Error codes returned by the Multi Protocol Support routines */ #define MPS_COMMAND_SUCCESS 0 #define MPS_ILLEGAL_BUF_LEN 1 #define MPS_ILLEGAL_COMMAND 3 #define MPS_CALL_TIMEOUT 5 #define MPS_MORE_DATA_COMING 6 #define MPS_VCID_INVALID 8 #define MPS_NO_RESOURCE_AVAIL 9 #define MPS_VIRT_CIRCUIT_DOWN 10 #define MPS_COMMAND_CANCELLED 11 #define MPS_VIRT_CIRCUIT_ABORT 24 #define MPS_INTERFACE_BUSY 33 #define MPS_TOO_MANY_COMMANDS 34 #define MPS_NO_CMND_TO_CANCEL 36 #define MPS_LSAP_ERROR 66 #define MPS_BAD_PROTOCOL_TYPE 67 #define MPS_ALL_MAC_GRABBED 68 #define MPS_TYPE_FIELD_CLASH 69 #define MPS_NO_CAN_CORRESPOND 70 #define MPS_FACILITY 98 /* Facility code for MPS routines */ #define MPS_SEVERITY 2 /* Severity level = ERROR */ #define EA_LENGTH 6 #define EA_OFFSET 9 #define EA_STRLEN 12 typedef char BOOLEEN; typedef unsigned char BYTE; typedef unsigned int WORD; typedef char m_data[M_LDATA]; /*---------------Structure used for the TCB (see BICC spec)-------------*/ typedef struct { BYTE command; BYTE cid; BYTE vcid; WORD len; char far *baddr; BOOLEEN expedited; BOOLEEN cancelable; char laddr[16]; char raddr[16]; int (far *async)(); char lnet[4]; char rnet[4]; BYTE rto; BYTE sto; char res2[8]; char cmdext; WORD status; } TCB; /*---------------Structure used for the ACB (see BICC spec)-------------*/ typedef struct { BYTE cid; BYTE vcid; BYTE err; BYTE cmd; WORD len; char raddr[16]; char rnet[4]; char laddr[16]; char lnet[4]; WORD status; BYTE eventcode; char far *baddr; BOOLEEN expedited; } ACB; TCB mytcb; TCB * ptcb = &mytcb; rpc_status eth_status; courier_word typef_start = {0x50,0x00}; courier_word typef_end = {0x51,0xFF}; courier_word typef = {0x50,0x50}; rpc_ethernet_address my_eth_adr; rpc_ethernet_address remote_eth_adr; void rpc_eth_receive_service(); /*************************************************/ /* Asynchronous Notification Routine - C Part */ /* (Internal Procedure) */ /*************************************************/ PUBLIC void anr(ACB * ptacb) { int i, sentbyme; char * ptr = ptacb -> baddr; #if ANR_DBUG printf(" ANR error %x\n", ptacb -> err); printf(" ANR command %x\n", ptacb -> cmd); printf(" ANR eventcode %x\n", ptacb -> eventcode); #endif if ((ptacb -> err == 0) && (ptacb -> cmd == CD_M_ACTIVATE_SAP) && (ptacb -> eventcode == ID_M_DATA)) { /* Don't catch my OWN messages */ sentbyme = 1; for (i = 0; i < EA_LENGTH; i++) if (ptacb -> raddr[i + EA_OFFSET] != my_eth_adr[i]) sentbyme = 0; } if (!sentbyme) { #if ANR_DBUG printf("---> %d bytes received\n", ptacb -> len); for (i=0; i len; i++) printf("%02x ", (*ptr++)&0xff); printf("\n"); #endif rpc_eth_receive_service(ptacb); } } #if ANR_INLINE /********************************************************************/ /* Asynchronous Notification Routine - Assembler Part */ /* (Internal Procedure) */ /********************************************************************/ #define STACK_SIZE 1024 int save[2]; char stack[STACK_SIZE]; PRIVATE int far asyncf(void) { /* Restore the data segment value */ asm mov ax, DGROUP asm mov ds, ax /* Save the stack values (SS:SP) */ asm mov [save], sp asm mov ax, ss asm mov [save + 2], ax /* Give new stack values */ asm cli asm lea ax, [stack + STACK_SIZE] asm and ax, 0xFFFE asm mov sp, ax asm mov ax, DGROUP asm mov ss, ax asm sti /* Push as parameter the ACB pointer and call ANR routine */ asm push es asm push bx asm call _anr /* Restore the former stack values */ asm mov sp, [save] asm mov ax, [save + 2] asm mov ss, ax } #else extern int far asyncf(); #endif /*****************************************************************/ /* Call the Resident Communication System */ /* (Internal Function) */ /*****************************************************************/ PRIVATE BYTE mps_interface(TCB * tcb) { union REGS inreg, outreg; struct SREGS segreg; tcb -> async = asyncf; segread(&segreg); segreg.es = segreg.ds; inreg.x.bx = (unsigned int) tcb; int86x(INT, &inreg, &outreg, &segreg); return(outreg.h.al); } /**************************************************************/ /* Fills a TCB with 00x */ /* (Internal Function) */ /**************************************************************/ PRIVATE void tcb_init(char * tcb) { memset(tcb, 0, TCB_LEN); } /**************************************************************/ /* Get the BICC Ethernet address */ /* (Internal Function) */ /**************************************************************/ PUBLIC BYTE get_local_eaddr(rpc_ethernet_address eaddr) { int i; BYTE retval; long addr; BYTE user_buffer[80]; tcb_init((char *)ptcb); ptcb -> command = CD_STATUS; ptcb -> len = 16; ptcb -> baddr = (char far *) user_buffer; retval = mps_interface(ptcb); for (i = 0; i < EA_LENGTH; i++) eaddr[i] = user_buffer[i + EA_OFFSET]; return retval; } /**************************************************************/ /* Reserves a typefield interval to listen to */ /* (External Function) */ /**************************************************************/ PRIVATE BYTE bic_m_activate_sap(courier_word typefield_low, courier_word typefield_high, BYTE tcb_id) { unsigned long tpf, tpf1, tpf2; tcb_init((char *)ptcb); ptcb -> command = CD_M_ACTIVATE_SAP; ptcb -> cid = tcb_id; ptcb -> len = 0; tpf1 = (typefield_low.h << 8) + typefield_low.l; tpf2 = (typefield_high.h << 8) + typefield_high.l; tpf = tpf1 + (tpf2 << 16); ptcb -> baddr = (char far *) tpf; return mps_interface(ptcb); } /**************************************************************/ /* Reserves a typefield interval to listen to */ /* (External Function) */ /**************************************************************/ PRIVATE BYTE bic_m_deactivate_sap(courier_word typefield_low, courier_word typefield_high, BYTE tcb_id) { BYTE stat; unsigned long tpf, tpf1, tpf2; tcb_init((char *)ptcb); ptcb -> command = CD_M_DEACTIVATE_SAP; ptcb -> cid = tcb_id; ptcb -> len = 0; tpf1 = (typefield_low.h << 8) + typefield_low.l; tpf2 = (typefield_high.h << 8) + typefield_high.l; tpf = tpf1 + (tpf2 << 16); ptcb -> baddr = (char far *) tpf; stat = mps_interface(ptcb); CTRACE(tfp, "DEACTIVATE returned %x \n", (int)stat); return stat; } PRIVATE BYTE bic_m_data_send(rpc_ethernet_address dest, rpc_ethernet_address source, unsigned short typefield, char *data_ptr, int data_len) { int i; tcb_init((char *)ptcb); ptcb -> command = CD_L_DATA_SEND; ptcb -> cid = 33; ptcb -> len = data_len; ptcb -> baddr = (char far *) data_ptr; for (i = 0; i < EA_LENGTH; i++) { ptcb -> laddr[i + EA_OFFSET] = source[i]; ptcb -> raddr[i + EA_OFFSET] = dest[i]; } ptcb -> status = typefield; return mps_interface(ptcb); } #if ETH_DBUG /* ================== RPC_TS_DEBUGGING_UTILITIES ========================== */ /* ** ** Print out socket descriptor ** --------------------------- ** ** */ PRIVATE dump_socket(socket_type soc) { if (rpc_trace) { if (soc == NIL_SOCKET_PTR) { UTRACE(tfp,"Dumping NIL SOCKET !!!\n"); return; } UTRACE(tfp," Socket at %lx:\n", (long int) soc); UTRACE(tfp," next @ %lx\n", (long int)soc->soc_next); UTRACE(tfp," astadr = %lx\n", (long int)soc->soc_astadr); UTRACE(tfp," astprm = %lx\n", (long int)soc->soc_astprm); UTRACE(tfp," last call sent @ %lx\n", (long int)soc->soc_last_call_sent); UTRACE(tfp," last reply sent @ %lx\n", (long int)soc->soc_last_reply_sent); UTRACE(tfp," medium = "); switch(soc->soc_medium) { case invalid_medium: UTRACE(tfp, "INVALID (ZERO)\n"); break; case v24: UTRACE(tfp, "V24\n"); break; case ether: UTRACE(tfp, "ETHERNET\n"); UTRACE(tfp, " Address filter: %2x_%2x_%2x_%2x_%2x_%2x %2x_%2x\n", soc->mdp.soc_ether.soc_addr_filter[0], soc->mdp.soc_ether.soc_addr_filter[1], soc->mdp.soc_ether.soc_addr_filter[2], soc->mdp.soc_ether.soc_addr_filter[3], soc->mdp.soc_ether.soc_addr_filter[4], soc->mdp.soc_ether.soc_addr_filter[5], soc->mdp.soc_ether.soc_type_filter.h, soc->mdp.soc_ether.soc_type_filter.l); UTRACE(tfp, " Address mask: %2x_%2x_%2x_%2x_%2x_%2x %4x\n", soc->mdp.soc_ether.soc_addr_mask[0], soc->mdp.soc_ether.soc_addr_mask[1], soc->mdp.soc_ether.soc_addr_mask[2], soc->mdp.soc_ether.soc_addr_mask[3], soc->mdp.soc_ether.soc_addr_mask[4], soc->mdp.soc_ether.soc_addr_mask[5], soc->mdp.soc_ether.soc_type_mask); UTRACE(tfp, " Header of last packet received on this socket:\n"); UTRACE(tfp, " To: %2x_%2x_%2x_%2x_%2x_%2x\n", soc->mdp.soc_ether.soc_rx_dest_addr[0], soc->mdp.soc_ether.soc_rx_dest_addr[1], soc->mdp.soc_ether.soc_rx_dest_addr[2], soc->mdp.soc_ether.soc_rx_dest_addr[3], soc->mdp.soc_ether.soc_rx_dest_addr[4], soc->mdp.soc_ether.soc_rx_dest_addr[5]); UTRACE(tfp, " From: %2x_%2x_%2x_%2x_%2x_%2x\n", soc->mdp.soc_ether.soc_rx_source_addr[0], soc->mdp.soc_ether.soc_rx_source_addr[1], soc->mdp.soc_ether.soc_rx_source_addr[2], soc->mdp.soc_ether.soc_rx_source_addr[3], soc->mdp.soc_ether.soc_rx_source_addr[4], soc->mdp.soc_ether.soc_rx_source_addr[5]); UTRACE(tfp, " Ptype: %2x_%2x\n", soc->mdp.soc_ether.soc_rx_protocol_type.h, soc->mdp.soc_ether.soc_rx_protocol_type.l); break; decnet: UTRACE(tfp, "DECNET\n"); break; ieee: UTRACE(tfp, "IEEE\n"); break; x25: UTRACE(tfp, "X25\n"); break; drb: UTRACE(tfp, "DRB/CHI/HVIOR link\n"); break; udp: UTRACE(tfp, "UDP/IP\n"); break; tcp: UTRACE(tfp, "TCP/IP\n"); break; default: UTRACE(tfp,"ILLEGAL: hex %x\n", (int)soc->soc_medium); } /*switch */ UTRACE(tfp,"\n"); } /* end if trace */ } /* dump socket */ /* Dump RPC Message Internal bits dump_message() ** ------------------------------ ** */ void dump_message(rpc_message_pointer mes) { int i; if (rpc_trace) { UTRACE(tfp, " Message at %lx:\n", (long int)mes); UTRACE(tfp, " Next at %lx", (long int)mes->m_next); UTRACE(tfp, " Status is %lx\n", (long int)mes->m_status); UTRACE(tfp, " Socket at %lx", (long int)mes->m_socket); UTRACE(tfp, " Index is %lx\n", (long int)mes->m_index); UTRACE(tfp, " Protocol header:\n"); UTRACE(tfp, " "); for(i=0; i<16; i++) UTRACE(tfp, "%2x ", (int)(((unsigned char*)(&mes->header))[i])); UTRACE(tfp, "\n"); UTRACE(tfp, "\n"); } } /* ================ RPC_TS_RAW_ETHERNET_UTILITIES ========================== */ #endif PUBLIC void sys_setast(int flag) { if (flag == 0) PROTECT; else UNPROTECT; } extern long BiosTicks(); PRIVATE void rpc_wait(rpc_pointer * event, int timeout) { long int time_on_entry; int current_time; ETH_TRACE("RPC_WAIT called with %x\n", timeout); if (timeout != 0) { if (timeout > 0) { /* Get current time in ticks from BIOS */ time_on_entry = BiosTicks(); do { /* Wait for a maximum of 'timeout' 10th of ms */ /* There are 18.2 ticks per second */ current_time = (int)(((BiosTicks() - time_on_entry) * 100L) / 18L); } while ((*event == NO_AST_FLAG) && (current_time < timeout)); /* Time out! */ } else /* if timeout < 0 wait for EVER */ { do { } while (*event == NO_AST_FLAG); } } } PRIVATE void queue_add_head(volatile socket_type * queue, socket_type item) { PROTECT; /* Critical code start */ item->soc_next = *queue; *queue = item; UNPROTECT; /* Critical code end */ } PRIVATE void queue_add_tail(volatile socket_type * queue, socket_type item) { socket_type scan; PROTECT; /* Critical code start */ item->soc_next = NIL_SOCKET_PTR; if (*queue == NIL_SOCKET_PTR) *queue = item; else { for (scan = *queue; scan->soc_next; scan = scan->soc_next) ; scan->soc_next = item; } UNPROTECT; /* Critical code end */ } PRIVATE void queue_remove(volatile socket_type * queue, socket_type item) { socket_type scan; PROTECT; /* Critical code start */ if (*queue == item) *queue = item->soc_next; else { scan = *queue; while (scan && (scan->soc_next != item)) scan = scan->soc_next; if (scan) scan->soc_next = item->soc_next; } UNPROTECT; /* Critical code end */ } /* Convert BICC Status to 32-bit RPC Status ======================================== */ PRIVATE rpc_status bicc_stat(BYTE bicc_completion) { rpc_status retval; if (bicc_completion == MPS_COMMAND_SUCCESS) retval = RPC_S_NORMAL; else retval = (MPS_SEVERITY + (8 * bicc_completion) + 65536 * (2048 + MPS_FACILITY)); return (retval); } /* ================ RPC_TS_RAW_ETHERNET_INTERFACE ========================= */ /* Procedure: Initialise the ETHERNET I/O ** --------- --------------------------- ** ** This routine is called once only for this process, to initialise ** any process-wide things. ** ** On exit, ** return gives the return status */ PUBLIC rpc_status rpc_eth_init(void) { #if APELLEgWM /* Open ETHERNET debug window */ if ((db_eth = WCreateWindow("ETH debug", W_TEXT, 15, 60, BLACKonIWHITE)) < 0) CltFatal("Error opening ETH Debug Window %d ",db_eth); if (eth_trace) WMapRaised(db_eth); #endif eth_usage = 0; return RPC_S_NORMAL; } /* Procedure: Initialise the ETHERNET I/O for one channel ** --------- ------------------------------------------- ** ** On entry, ** psocket points to a socket descriptor which has been set up ** with the correct ethernet addreess, type fields and masks. ** On exit, ** return gives the return status */ PUBLIC rpc_status rpc_eth_open(socket_type psocket) { rpc_status eth_status; int i; ETH_TRACE("RPC_ETH_OPEN called\n"); eth_status = RPC_S_NORMAL; eth_usage++; if (eth_usage == 1) { eth_rxq = NIL_SOCKET_PTR; /* Clear all pending ASTs */ #if ZAPPED eth_status = (rpc_status) get_local_eaddr(my_eth_adr); #endif eth_status = (rpc_status) bic_m_activate_sap(typef_start, typef_end, 1); if (eth_status == 0) eth_status = RPC_S_NORMAL; } /* In order not to miss any packets, we immediately allocate a ** receive buffer. This may be filled at any time by the ANR. */ rpc_new(&psocket->soc_astprm, RPC_BUFFER_SIZE); /* Get a rx buffer */ psocket->soc_astprm->m_socket = psocket; /* Link msg back to socket */ psocket->soc_astadr = NO_AST_FLAG; /* mark buffer as empty */ queue_add_tail(ð_rxq, psocket); /* Make receive active */ return eth_status; } /* ** Procedure: DeInitialise the ethernet i/o for one channel ** --------- --------------------------------------------- ** ** This procedure in fact checks whether the very last open is being done, ** in which case it performs any global deinitialisation which may be required. ** ** On entry, ** psocket points to a socket descriptor. ** On exit, ** return gives the return status */ /* NOTE: If an asynchronous receive is in progress, then the buffer passed to the RPC system will be disposed of. This is because the buffer may have been swapped many times with the one the user originally provided, so he has no way of knowing which one to dispose of This may not be safe if a call arrives just as the cancellation occurs !!! */ PUBLIC rpc_eth_close(socket_type psocket) { rpc_status eth_status; socket_type scan; BYTE active; ETH_TRACE( "RPC_ETH_CLOSE called\n"); /* Deactivate reception of packets */ queue_remove(ð_rxq, psocket); /* Dispose of any buffer waiting for an async mess */ if (psocket->soc_astprm != NIL_MESSAGE_PTR) rpc_dispose(psocket->soc_astprm); /* Dispose of any record of last call made if any */ if (psocket->soc_last_call_sent != NIL_MESSAGE_PTR) { rpc_dispose(psocket->soc_last_call_sent); psocket->soc_last_call_sent = NIL_MESSAGE_PTR; } /* Dispose of last reply to EACH address */ while (psocket->soc_last_reply_sent != NIL_MESSAGE_PTR) { register rpc_message_pointer temp; PROTECT; temp = psocket->soc_last_reply_sent; psocket->soc_last_reply_sent = psocket->soc_last_reply_sent->m_next; rpc_dispose(temp); UNPROTECT; } eth_status = RPC_S_NORMAL; eth_usage--; if (eth_usage == 0) { eth_status = bicc_stat( bic_m_deactivate_sap(typef_start, typef_end, 1) ); } return eth_status; } /* Procedure: Send ethernet packet ** --------- -------------------- ** ** On entry, ** socket points to an initialised socket. ** ** buffer points to a packet. The destination field and protocol type ** in the packet header are correct. ** m_socket is valid ** m_index is the number of bytes (excluding the header) ** The source address is not yet filled in. ** bufsize gives the number of bytes to be transmitted, EXCLUDING the ** ethernet header. ** On exit, ** return Gives the status: if good, the packet has been sent. ** */ PUBLIC rpc_status rpc_eth_send(rpc_message_pointer pmessage) { int bufsize = (int)pmessage->m_index; rpc_status eth_status; unsigned short typefield; int i; typefield = (unsigned short) ((pmessage->header.ether.ptype.h << 8) + pmessage->header.ether.ptype.l); ETH_TRACE( "RPC_ETH_SEND called\n"); eth_status = bicc_stat( bic_m_data_send( pmessage->header.ether.dest, my_eth_adr, typefield, pmessage -> body.rpc_b, bufsize) ); return eth_status; } /* ** Procedure: Wait for ethernet message ** ** This procedure receives one ethernet message on the given socket. ** The packets are filtered by source address and protocol type according ** to the way the socket has been set up (wildcard or not). ** ** On entry, ** ** ppmessage points to a pointer to a suitable message buffer with ** the following fields set up: ** ** m_socket Pointer to socket descriptor ** ** timeout may be -1 for infinite timeout, or 0 for poll. ** else units of microseconds. ** ** On return, ** ** return returns error code or normal or timeout. ** *pmessage may have been changed, ie the message swapped for another. ** **pmessage has the header and data fields both filled in. ** m_index gives the number of data bytes in the message. ** m_socket pointer to socket descriptor */ PUBLIC rpc_status rpc_eth_receive(rpc_message_pointer * ppmessage, int timeout) { register rpc_message *mes = *ppmessage; register socket_type soc = mes->m_socket; rpc_status eth_status; ETH_TRACE("RPC_RECEIVE called with %x\n", timeout); /* Wait for Soc_Astadr <> 0 or timeout */ rpc_wait(&soc->soc_astadr, timeout); /* Now we have to be careful that the timeout might occur at the same time ** as the message arrives. */ if (soc->soc_astadr) { rpc_message_pointer temp; PROTECT; temp = soc->soc_astprm; /* Swap buffers */ soc->soc_astprm = *ppmessage; /* to get data */ soc->soc_astadr = NO_AST_FLAG; /* Mark as empty */ UNPROTECT; *ppmessage = temp; return RPC_S_NORMAL; } else { return RPC_S_TIMEOUT; } /*NOTREACHED*/ } /*************************************************************************** ** ** Procedure: Set up handler for next message to arrive ** (Only if ASTs supported) ** ** On entry, ** pmessage points to an available message buffer, fields as follows: ** m_index maximum length to be read ** m_status (undefined) ** m_next (undefined) ** m_socket points to valid socket descriptor, includes fields ** soc_user_ast address of AST completion routine */ PUBLIC rpc_status rpc_eth_when_receive(rpc_message_pointer pmessage) { ETH_TRACE("ETH_WHEN_RECEIVE called\n"); return RPC_S_NOT_IMPLEMENTED; /* yet */ } /* Incoming Packet Handler ======================= This routine is called from the asynchronous notification routine (ANR) */ PRIVATE void rpc_eth_receive_service(ACB * ptacb) { socket_type scan; int i; ethspy((80*23 + 70)*2, 0x6901); for (scan = eth_rxq; scan != NIL_SOCKET_PTR; scan = scan->soc_next) { BOOLEAN match = TRUE; int i; for (i = 0; i < EA_LENGTH; i++) { if ((scan->mdp.soc_ether.soc_addr_mask[i] != 0) && (scan->mdp.soc_ether.soc_addr_filter[i] != ptacb->raddr[i + EA_OFFSET])) match = FALSE; /* Wrong source address */ } if (!match) break; /* wrong source address */ /* Protocol types match ? */ if( (scan->mdp.soc_ether.soc_type_mask == 0) /* Wildcard */ || ((scan->mdp.soc_ether.soc_type_filter.l == ((char)(ptacb->status & 0xff))) && (scan->mdp.soc_ether.soc_type_filter.h == ((char)(ptacb->status >> 8)))) ) /* if */ { /* Copy packet data (a pity we have to) */ #if ANR_DBUG printf("Copying Data ... AST\n"); { char * ptr = ptacb -> baddr; int i; printf("-----> %d bytes received\n", ptacb -> len); for (i=0; i len; i++) printf("%02x ", (*ptr++)&0xff); printf("\n"); } #endif memcpy(scan->soc_astprm->body.rpc_b, ptacb->baddr, ptacb->len); for (i = 0; i < EA_LENGTH; i++) { /* Copy packet header */ scan->soc_astprm->header.ether.source[i] = ptacb->raddr[i + EA_OFFSET]; scan->soc_astprm->header.ether.dest[i] = ptacb->laddr[i + EA_OFFSET]; } scan->soc_astprm->header.ether.ptype.l = ptacb->status & 0xff; scan->soc_astprm->header.ether.ptype.h = ptacb->status << 8; scan->soc_astprm->m_index = (rpc_long)ptacb->len; /* Inform the waiting task that the buffer is valid */ if (scan->soc_astadr == NO_AST_FLAG) { scan->soc_astadr = SET_AST_FLAG; ethspy((80*23 + 73)*2, 0x6943); } else if (scan->soc_astadr == SET_AST_FLAG) ethspy((80*23 + 74)*2, 0x694C); else { ethspy((80*23 + 75)*2, 0x6941); (*(scan->soc_astadr))(scan->soc_astprm); /* call AST */ } } /* if */ } /* for */ ethspy((80*23 + 79)*2, 0x6952); } /* rpc_eth_receive_service */