/******************************************************************************* ** ** OS9 - Specific Ethernet Code for RPC ** ** This code is part of the RPC system from CERN DD/OC and collaborators. ** ** ** FUNCTION: ** ** This is the machine-dependent part of the code to allow thsystem to ** communicate over ethernet. This version is for the OS9 operating system. ** ** HISTORY: ** ** 1988 Ethernet driver running over q0driv by H.v.d.Schmitt, CERN/EP ** 1988 Adaption to RPC: Johannes Raab CERN/EP (OPAL) ** Sep 89 Maintenaince and update Tim Berners-Lee CERN/DD ** ** RESTRICTIONS: ** ** o The error codes are not explanatory. ** o The trace on error is not complete. ** o Only one ethernet socket may be open at a time per process (it seems?) ** The routines rpc_eth_open and rpc_eth_close are process-wide largely. */ /*******************************************************\ * * * ************* * * **** *************** **** * * ******** ***** ***** ***** * * ********** **** **** **** * * *********** ***** ***** **** * * ***** **** *************** **** * * **** *** *************** *** * * **** *** **** *** * * **** *** **** ********* *** * * ***** **** **** ********* ** * * *********** **** *** *** *** * * ********** **** ********* ********* * * ******* **** ********* ******* * * **** *** *** * * **** *** *** * * * * * \*******************************************************/ #include #include #include "osk_specific.h" #define TS /* get TS interface visibility */ #define TS_INTERNALS /* get TS internals visibility */ #include "rpcrts.h" /* RPC structures including TS layer */ #include "rpc_code.h" /* Coding macros */ #define NBUFF 2 /* number of buffers to queue a read into */ #define MAX_TRY 10 /* number of allowed retries for write */ #define MAX_WAIT 50 /* time to wait for EvCreation */ #define INI_VAL 0 /* EvCreate parameters */ #define WAIT_INC -1 #define SIG_INC 1 #define SLEEP_INC 1 /* time increment while waiting for event */ #define RP_SIZE 1500 /* Size of a reply packet */ #define max(A,B) ((A) > (B) ? (A) : (B)) #define min(A,B) ((A) < (B) ? (A) : (B)) typedef unsigned long ulong; typedef unsigned char uchar; struct net_request { uchar dest[6],source[6],protocol[2]; uchar rq_data[RP_SIZE-14]; } ; struct net_reply { uchar dest[6],source[6],protocol[2]; uchar rp_data[RP_SIZE-14]; } ; /* Module-global data: */ struct net_request request,requack; struct net_reply reply[NBUFF],replack; int len[NBUFF]; int seq[NBUFF], gseq, lseq; int epath; /* Path to network device */ int evtid; /* Event id */ int inidone; char evname[12]; int eth_usage; /* Count of current usage of ethernet */ union zsam { courier_word twobytes; /* to get the protocol right */ short sword; } zsamma; /* General Ethernet Initialisation ** ------------------------------- ** */ EInit( proto, paddress) short proto; /* protocol */ rpc_ethernet_address paddress; { int i, code; short temp; /* Initialise node address */ /* The lance driver will write our address in the packet */ /* Open the network path */ strcpy(evname,"RPC_"); evname[4] = ( (temp=(proto >> 12)) < 10 ? '0'+temp : '7'+temp); evname[5] = ( (temp=((proto >> 8) & 017)) < 10 ? '0'+temp : '7'+temp); evname[6] = ( (temp=((proto >> 4) & 017)) < 10 ? '0'+temp : '7'+temp); evname[7] = ( (temp=(proto & 017)) < 10 ? '0'+temp : '7'+temp); evname[8] = '_'; i = getpid(); evname[9] = ( i/10 +'0'); evname[10] = ( i%10 + '0'); evname[11] = '\0'; epath = open("/elan",S_IREAD | S_IWRITE); if( epath==-1 ) { CTRACE(tfp, "RPC/TS: Ethernet open() failed\n"); return -1; /* Error: OPEN failed. */ } /* Link/Create event : if we fail to create it then delete it */ evtid = _ev_creat(INI_VAL, WAIT_INC, SIG_INC, evname); if ( evtid == -1 ) { evtid = _ev_link( evname); do _ev_unlink(evtid); while (_ev_delete(evname) == -1); evtid = _ev_creat(INI_VAL, WAIT_INC, SIG_INC, evname); if( evtid == -1 ) { CTRACE(tfp, "RPC/TS/ETH: Some one is bogarting the event %d\n",errno); return -2; } } if ((evtid = _ev_link(evname)) == -1 ) { CTRACE(tfp,"RPC/TS/ETH/Open: Can't link to event \n"); return -2; } gseq = lseq = 0; /* Issue initial read rq's for all buffers */ inidone = 1; for( i=0; i 0) { sleep_time = (unsigned) SLEEP_INC; do { tsleep( sleep_time ); if ((n = _ev_read(evtid)) > 0) goto GET_IT_NOW; sleep_time += sleep_time; /* use a backoff like TIM's */ } while ( (timer -= sleep_time) > 0); CTRACE(tfp,"RPC/TS/ETH: ev ret code %d time waited %d\n", n,timer); return -1; } GET_IT_NOW: ev_wait(evtid); lseq += 1; for( i=0; i=0; try--) { done = eth_write(epath,&request,size); if( !done ) { tsleep( (unsigned) 50); if (rpc_trace) printf(" write timeout, stat =%d\n", done); } } if( !done ) return -1; if (rpc_trace) { printf("RPC/TS/ETH/SEND: Size %d \n", size); i = min(40,size); for (kount = 0; kount < i; kount++) printf("%2x",*(inbuf+kount)); printf("\n"); } return 0; } /* Assembler Routines ** ================== ** ** These routines perform the actual operating system interface ** necessary to to call the driver in nonstandard ways. */ _strass( a,b,leng ) char *a,*b; int leng; { #asm movem.l d2/a0-a1,-(a7) move.l 8(a5),d2 ble.s _sas1 subq.l #1,d2 movea.l d0,a0 movea.l d1,a1 _sas2 move.b (a1)+,(a0)+ dbra d2,_sas2 _sas1 movem.l (a7)+,d2/a0-a1 #endasm } /* Assembler routine to call driver to Write a packet */ PRIVATE eth_write( pth,buf,leng ) int pth,leng; unsigned char *buf; { #asm movem.l d1-d2/a0,-(a7) move.l 8(a5),d2 movea.l d1,a0 move.l #1001,d1 os9 I$GetStt bcc.s wr_ok clr.l d0 bra.s wr_ex wr_ok moveq.l #1,d0 wr_ex movem.l (a7)+,d1-d2/a0 #endasm } /* Assembler routine to call driver to perform a multiple read */ eth_mul_rd( pth,buf,pro,code,lln,maxl,seq,gseq ) int pth,code,*lln,maxl,*seq,*gseq; unsigned short pro; unsigned char *buf; { #asm movem.l d1-d4/a0-a3,-(a7) clr.l d2 move.w 10(a5),d2 move.l 12(a5),d3 movea.l 16(a5),a1 move.l 20(a5),d4 movea.l 24(a5),a2 movea.l 28(a5),a3 movea.l d1,a0 move.l #1005,d1 os9 I$GetStt bcc.s mu_ok clr.l d0 bra.s mu_ex mu_ok moveq.l #1,d0 mu_ex movem.l (a7)+,d1-d4/a0-a3 #endasm } /* Assembler routine to call driver to Read a packet */ eth_mul_rd_from_src( pth,buf,pro,code,lln,maxl,addr,seq,gseq ) int pth,code,*lln,maxl,*seq,*gseq,*addr; unsigned short pro; unsigned char *buf; { #asm movem.l d1-d4/a0-a3,-(a7) clr.l d2 move.w 10(a5),d2 move.l 12(a5),d3 movea.l 16(a5),a1 move.l 20(a5),d4 movea.l 24(a5),a2 movea.l 28(a5),a3 movea.l 32(a5),a4 movea.l d1,a0 move.l #1011,d1 os9 I$GetStt bcc.s mus_ok clr.l d0 bra.s mus_ex mus_ok moveq.l #1,d0 mus_ex movem.l (a7)+,d1-d4/a0-a3 #endasm } /* Assembler routine to call driver to return our ethernet address */ getmyid( pth, myaddr ) int pth; uchar *myaddr; { #asm movem.l d1/a0,-(a7) movea.l d1,a0 move.l #1010,d1 os9 I$GetStt movem.l (a7)+,d1/a0 #endasm } /* Assembler routine to wait for an event */ ev_wait( evtid ) int evtid; { #asm movem.l d1-d3,-(a7) moveq.l #Ev$Wait,d1 moveq.l #1,d2 move.l #2000000000,d3 os9 F$Event movem.l (a7)+,d1-d3 #endasm } /* INTERFACE ROUTINES CONFORMING TO RPC REQUIREMENTS */ /* 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() { 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(psocket) socket_type psocket; { register socket_type soc = psocket; int eth_status; eth_usage++; if (eth_usage == 1) { rpc_os9_init(); /* Connect intercept handler */ #ifdef AST eth_rxq = NULL; #endif } zsamma.twobytes = soc->mdp.soc_ether.soc_type_filter; eth_status = EInit( zsamma.sword, soc->mdp.soc_ether.soc_addr_filter, rpc_trace); if (eth_status<3) return RPC_S_TS_INTERNAL_ERROR; /* @@@@ */ else return RPC_S_NORMAL; } /* Return the Address of this socket rpc_eth_my_address() ** --------------------------------- ** ** On entry, ** socket is a valid socket pointer, open over ethernet. ** buf points to a suitable buffer for the result. ** ** On exit, ** buf[] contains the address as 6 bytes in binary. */ PUBLIC rpc_status rpc_eth_my_address(buf) rpc_byte * buf; { getmyid(epath, buf); return RPC_S_NORMAL; } /* ** Procedure: DeInitialise the ethernet i/o for one channel ** --------- --------------------------------------------- ** ** This procedure in fact checks whether the very last close 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 */ PUBLIC rpc_eth_close(soc) socket_type soc; { eth_usage = eth_usage - 1; if (eth_usage == 0) { /* close path, and get rid of the events in the system */ if (_ev_unlink(evtid) != -1 ) { do _ev_unlink(evtid); while (_ev_delete(evname) == -1); } _ev_delete(evname); CTRACE(tfp, "RPC/TS/Eth: Closing neatly \n"); close(epath); } return RPC_S_NORMAL; } /* 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(mes) rpc_message_pointer mes; { int bufsize = (int)mes->m_index; register socket_type soc = mes->m_socket; zsamma.twobytes = soc->mdp.soc_ether.soc_type_filter; if (EWrite( zsamma.sword, /* protocol */ mes->header.ether.dest, /* Data address */ bufsize+14, /* Size inc. Eth Hdr */ soc->mdp.soc_ether.soc_addr_filter)) return RPC_S_TS_INTERNAL_ERROR; return RPC_S_NORMAL; } /* Wait for ethernet message rpc_eth_receive() ** ------------------------- ** ** 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. */ PUBLIC rpc_status rpc_eth_receive(ppmessage, timeout) rpc_message_pointer *ppmessage; int timeout; { register rpc_message *mes = *ppmessage; register socket_type soc = mes->m_socket; int istat; short ticker = (short) timeout; zsamma.twobytes = soc->mdp.soc_ether.soc_type_filter; istat = ERead ( zsamma.sword, /* Reqd prot.type */ mes->header.ether.dest, /* buffer adr */ &mes->m_index, /* returned buf length */ ticker, /* timeout in milliseconds */ soc->mdp.soc_ether.soc_addr_filter); if (istat != 0) { if (istat == -1) return RPC_S_TIMEOUT; else return RPC_S_TS_INTERNAL_ERROR; } mes->m_index = mes->m_index - 14; return (RPC_S_NORMAL); } #ifdef AST /* Set up handler for next message to arrive rpc_eth_when_receive() ** ----------------------------------------- ** ** (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 eth_when_receive(pmessage) rpc_message_pointer pmessage; { register struct socket_struct *soc = pmessage->m_socket; if (!lance_enabled) { bbopen(); lance_enabled = true; } soc->soc_astadr = &(eth_ast_service); soc->soc_astprm = pmessage; queue_add_tail(ð_rxq, soc); return RPC_S_NORMAL; } #endif /* AST */