📄 l1.c
字号:
/* type mismatch */ va_end( al ); DUMP_RESP; return -1; } bufptr = va_arg( al, char* ); argno++; strcpy( (char *)bufptr, (char *)&(resp[index]) ); /* include terminating null */ index += (strlen( &(resp[index]) ) + 1); break; default: if( (l1_fld_t & L1_ARG_UNKNOWN) == L1_ARG_UNKNOWN ) { int *arglen; arglen = va_arg( al, int* ); argno++; bufptr = va_arg( al, void* ); argno++; *arglen = ((resp[index++] & ~L1_ARG_UNKNOWN) & 0xff); BCOPY( &(resp[index]), bufptr, *arglen ); index += (*arglen); } else { /* unhandled type */ va_end( al ); DUMP_RESP; return -1; } } } va_end( al ); if( (l1_fldno != 0) || (argno != resp_nargs) ) { /* wrong number of arguments */ DUMP_RESP; return -1; } return 0;}/* sc_send takes as arguments a system controller struct, a * buffer which contains a Bedrock<->L1 "request" message, * the message length, and the subchannel (presumably obtained * from an earlier invocation of sc_open) over which the * message is to be sent. The final argument ("wait") indicates * whether the send is to be performed synchronously or not. * * sc_send returns either zero or an error value. Synchronous sends * (wait != 0) will not return until the data has actually been sent * to the UART. Synchronous sends generally receive privileged * treatment. The intent is that they be used sparingly, for such * purposes as kernel printf's (the "ducons" routines). Run-of-the-mill * console output and L1 requests should NOT use a non-zero value * for wait. */intsc_send( l1sc_t *sc, int ch, char *msg, int len, int wait ){ char type_and_subch; int result; if( (ch < 0) || ( ch >= BRL1_NUM_SUBCHANS) ) { return SC_BADSUBCH; } /* Verify that this is an open subchannel */ if( sc->subch[ch].use == BRL1_SUBCH_FREE ) { return SC_NOPEN; } type_and_subch = (BRL1_REQUEST | ((u_char)ch)); result = brl1_send( sc, msg, len, type_and_subch, wait ); /* If we sent as much as we asked to, return "ok". */ if( result == len ) return( SC_SUCCESS ); /* Or, if we sent less, than either the UART is busy or * we're trying to send too large a packet anyway. */ else if( result >= 0 && result < len ) return( SC_BUSY ); /* Or, if something else went wrong (result < 0), then * return that error value. */ else return( result );}/* subch_pull_msg pulls a message off the receive queue for subch * and places it the buffer pointed to by msg. This routine should only * be called when the caller already knows a message is available on the * receive queue (and, in the kernel, only when the subchannel data lock * is held by the caller). */static voidsubch_pull_msg( brl1_sch_t *subch, char *msg, int *len ){ sc_cq_t *q; /* receive queue */ int before_wrap, /* packet may be split into two different */ after_wrap; /* pieces to acommodate queue wraparound */ /* pull message off the receive queue */ q = subch->iqp; cq_rem( q, *len ); /* remove length byte and store */ cq_discard( q ); /* remove type/subch byte and discard */ if ( *len > 0 ) (*len)--; /* don't count type/subch byte in length returned */ if( (q->opos + (*len)) > BRL1_QSIZE ) { before_wrap = BRL1_QSIZE - q->opos; after_wrap = (*len) - before_wrap; } else { before_wrap = (*len); after_wrap = 0; } BCOPY( q->buf + q->opos, msg, before_wrap ); if( after_wrap ) { BCOPY( q->buf, msg + before_wrap, after_wrap ); q->opos = after_wrap; } else { q->opos = ((q->opos + before_wrap) & (BRL1_QSIZE - 1)); } atomic_dec(&(subch->packet_arrived));}/* sc_recv_poll can be called as a blocking or non-blocking function; * it attempts to pull a message off of the subchannel specified * in the argument list (ch). * * The "block" argument, if non-zero, is interpreted as a timeout * delay (to avoid permanent waiting). */intsc_recv_poll( l1sc_t *sc, int ch, char *msg, int *len, uint64_t block ){ int is_msg = 0; brl1_sch_t *subch = &(sc->subch[ch]); rtc_time_t exp_time = rtc_time() + block; /* sanity check-- make sure this is an open subchannel */ if( subch->use == BRL1_SUBCH_FREE ) return( SC_NOPEN ); do { /* kick the next lower layer and see if it pulls anything in */ brl1_receive( sc ); is_msg = atomic_read(&subch->packet_arrived); } while( block && !is_msg && (rtc_time() < exp_time) ); if( !is_msg ) { /* no message and we didn't care to wait for one */ return( SC_NMSG ); } SUBCH_DATA_LOCK( subch ); subch_pull_msg( subch, msg, len ); SUBCH_DATA_UNLOCK( subch ); return( SC_SUCCESS );} /* Like sc_recv_poll, sc_recv_intr can be called in either a blocking * or non-blocking mode. Rather than polling until an appointed timeout, * however, sc_recv_intr sleeps on a syncrhonization variable until a * signal from the lower layer tells us that a packet has arrived. * * sc_recv_intr can't be used with remote (router) L1s. */intsc_recv_intr( l1sc_t *sc, int ch, char *msg, int *len, uint64_t block ){ int is_msg = 0; brl1_sch_t *subch = &(sc->subch[ch]); do { SUBCH_DATA_LOCK(subch); is_msg = atomic_read(&subch->packet_arrived); if( !is_msg && block ) { /* wake me when you've got something */ subch->rx_notify = sc_data_ready; sv_wait( &(subch->arrive_sv), 0, 0); if( subch->use == BRL1_SUBCH_FREE ) { /* oops-- somebody closed our subchannel while we were * sleeping! */ /* no need to unlock since the channel's closed anyhow */ return( SC_NOPEN ); } } } while( !is_msg && block ); if( !is_msg ) { /* no message and we didn't care to wait for one */ SUBCH_DATA_UNLOCK( subch ); return( SC_NMSG ); } subch_pull_msg( subch, msg, len ); SUBCH_DATA_UNLOCK( subch ); return( SC_SUCCESS );}/* sc_command implements a (blocking) combination of sc_send and sc_recv. * It is intended to be the SN1 equivalent of SN0's "elsc_command", which * issued a system controller command and then waited for a response from * the system controller before returning. * * cmd points to the outgoing command; resp points to the buffer in * which the response is to be stored. Both buffers are assumed to * be the same length; if there is any doubt as to whether the * response buffer is long enough to hold the L1's response, then * make it BRL1_QSIZE bytes-- no Bedrock<->L1 message can be any * bigger. * * Be careful using the same buffer for both cmd and resp; it could get * hairy if there were ever an L1 command reqeuest that spanned multiple * packets. (On the other hand, that would require some additional * rewriting of the L1 command interface anyway.) */#define __RETRIES 50#define __WAIT_SEND ( sc->uart != BRL1_LOCALUART )#define __WAIT_RECV 10000000intsc_command( l1sc_t *sc, int ch, char *cmd, char *resp, int *len ){#ifndef CONFIG_SERIAL_SGI_L1_PROTOCOL return SC_NMSG;#else int result; int retries; if ( IS_RUNNING_ON_SIMULATOR() ) return SC_NMSG; retries = __RETRIES; while( (result = sc_send( sc, ch, cmd, *len, __WAIT_SEND )) < 0 ) { if( result == SC_BUSY ) { retries--; if( retries <= 0 ) return result; uart_delay(500); } else { return result; } } /* block on sc_recv_* */#ifdef LATER if( sc->uart == BRL1_LOCALUART ) { return( sc_recv_intr( sc, ch, resp, len, __WAIT_RECV ) ); } else#endif /* LATER */ { return( sc_recv_poll( sc, ch, resp, len, __WAIT_RECV ) ); }#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */}/* sc_command_kern is a knuckle-dragging, no-patience version of sc_command * used in situations where the kernel has a command that shouldn't be * delayed until the send buffer clears. sc_command should be used instead * under most circumstances. */intsc_command_kern( l1sc_t *sc, int ch, char *cmd, char *resp, int *len ){#ifndef CONFIG_SERIAL_SGI_L1_PROTOCOL return SC_NMSG;#else int result; if ( IS_RUNNING_ON_SIMULATOR() ) return SC_NMSG; if( (result = sc_send( sc, ch, cmd, *len, 1 )) < 0 ) { return result; } return( sc_recv_poll( sc, ch, resp, len, __WAIT_RECV ) );#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */}/* sc_poll checks the queue corresponding to the given * subchannel to see if there's anything available. If * not, it kicks the brl1 layer and then checks again. * * Returns 1 if input is available on the given queue, * 0 otherwise. */intsc_poll( l1sc_t *sc, int ch ){ brl1_sch_t *subch = &(sc->subch[ch]); if( atomic_read(&subch->packet_arrived) ) return 1; brl1_receive( sc ); if( atomic_read(&subch->packet_arrived) ) return 1; return 0;}/* for now, sc_init just calls brl1_init */voidsc_init( l1sc_t *sc, nasid_t nasid, net_vec_t uart ){ if ( !IS_RUNNING_ON_SIMULATOR() ) brl1_init( sc, nasid, uart );}/* sc_dispatch_env_event handles events sent from the system control * network's environmental monitor tasks. */#ifdef LINUX_KERNEL_THREADSstatic voidsc_dispatch_env_event( uint code, int argc, char *args, int maxlen ){ int j, i = 0; uint32_t ESPcode; switch( code ) { /* for now, all codes do the same thing: grab two arguments * and print a cmn_err_tag message */ default: /* check number of arguments */ if( argc != 2 ) { L1_DBG_PRF(( "sc_dispatch_env_event: " "expected 2 arguments, got %d\n", argc )); return; } /* get ESP code (integer argument) */ if( args[i++] != L1_ARG_INT ) { L1_DBG_PRF(( "sc_dispatch_env_event: " "expected integer argument\n" )); return; } /* WARNING: highly endian */ COPY_BUFFER_TO_INT(args, i, ESPcode); /* verify string argument */ if( args[i++] != L1_ARG_ASCII ) { L1_DBG_PRF(( "sc_dispatch_env_event: " "expected an ASCII string\n" )); return; } for( j = i; j < maxlen; j++ ) { if( args[j] == '\0' ) break; /* found string termination */ } if( j == maxlen ) { j--; L1_DBG_PRF(( "sc_dispatch_env_event: " "message too long-- truncating\n" )); } /* strip out trailing cr/lf */ for( ; j > 1 && ((args[j-1] == 0xd) || (args[j-1] == 0xa)); j-- ); args[j] = '\0'; /* strip out leading cr/lf */ for( ; i < j && ((args[i] == 0xd) || (args[i] == 0xa)); i++ ); }}#endif /* LINUX_KERNEL_THREADS *//* sc_event waits for events to arrive from the system controller, and * prints appropriate messages to the syslog. */#ifdef LINUX_KERNEL_THREADSstatic voidsc_event( l1sc_t *sc, int ch ){ char event[BRL1_QSIZE]; int i; int result; int event_len; uint32_t ev_src; uint32_t ev_code; int ev_argc; while(1) { bzero( event, BRL1_QSIZE ); /* * wait for an event */ result = sc_recv_intr( sc, ch, event, &event_len, 1 ); if( result != SC_SUCCESS ) { PRINT_WARNING("Error receiving sysctl event on nasid %d\n", sc->nasid ); } else { /* * an event arrived; break it down into useful pieces */#if defined(L1_DEBUG) && 0 int ix; printf( "Event packet received:\n" ); for (ix = 0; ix < 64; ix++) { printf( "%x%x ", ((event[ix] >> 4) & ((uint64_t)0xf)), (event[ix] & ((uint64_t)0xf)) ); if( (ix % 16) == 0xf ) printf( "\n" ); }#endif /* L1_DEBUG */ i = 0; /* get event source */ COPY_BUFFER_TO_INT(event, i, ev_src); COPY_BUFFER_TO_INT(event, i, ev_code); /* get arg count */ ev_argc = (event[i++] & 0xffUL); /* dispatch events by task */ switch( (ev_src & L1_ADDR_TASK_MASK) >> L1_ADDR_TASK_SHFT ) { case L1_ADDR_TASK_ENV: /* environmental monitor event */ sc_dispatch_env_event( ev_code, ev_argc, &(event[i]), BRL1_QSIZE - i ); break; default: /* unhandled task type */ L1_DBG_PRF(( "Unhandled event type received from system " "controllers: source task %x\n", (ev_src & L1_ADDR_TASK_MASK) >> L1_ADDR_TASK_SHFT )); } } } }#endif /* LINUX_KERNEL_THREADS *//* sc_listen sets up a service thread to listen for incoming events. */voidsc_listen( l1sc_t *sc ){ int result; brl1_sch_t *subch; char msg[BRL1_QSIZE]; int len; /* length of message being sent */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -