📄 l1.c
字号:
int ch, /* subchannel for this message */ char *msg, /* message buffer */ int msg_len, /* size of message buffer */ l1addr_t addr_task, /* target system controller task */ short req_code, /* 16-bit request code */ int req_nargs, /* # of arguments (varargs) passed */ ... ) /* any additional parameters */{ uint32_t buf32; /* 32-bit buffer used to bounce things around */ void *bufptr; /* used to hold command argument addresses */ va_list al; /* variable argument list */ int index; /* current index into msg buffer */ int argno; /* current position in varargs list */ int l1_argno; /* running total of arguments to l1 */ int l1_arg_t; /* argument type/length */ int l1_argno_byte; /* offset of argument count byte */ index = argno = 0; /* set up destination address */ if( (msg_len -= sizeof( buf32 )) < 0 ) return -1; L1_ADDRESS_TO_TASK( &buf32, sc->subch[ch].target, addr_task ); COPY_INT_TO_BUFFER(msg, index, buf32); /* copy request code */ if( (msg_len -= 2) < 0 ) return( -1 ); msg[index++] = ((req_code >> 8) & 0xff); msg[index++] = (req_code & 0xff); if( !req_nargs ) { return index; } /* reserve a byte for the argument count */ if( (msg_len -= 1) < 0 ) return( -1 ); l1_argno_byte = index++; l1_argno = 0; /* copy additional arguments */ va_start( al, req_nargs ); while( argno < req_nargs ) { l1_argno++; l1_arg_t = va_arg( al, int ); argno++; switch( l1_arg_t ) { case L1_ARG_INT: if( (msg_len -= (sizeof( buf32 ) + 1)) < 0 ) return( -1 ); msg[index++] = L1_ARG_INT; buf32 = (unsigned)va_arg( al, int ); argno++; COPY_INT_TO_BUFFER(msg, index, buf32); break; case L1_ARG_ASCII: bufptr = va_arg( al, char* ); argno++; if( (msg_len -= (strlen( bufptr ) + 2)) < 0 ) return( -1 ); msg[index++] = L1_ARG_ASCII; strcpy( (char *)&(msg[index]), (char *)bufptr ); index += (strlen( bufptr ) + 1); /* include terminating null */ break; case L1_ARG_UNKNOWN: { int arglen; arglen = va_arg( al, int ); argno++; bufptr = va_arg( al, void* ); argno++; if( (msg_len -= (arglen + 1)) < 0 ) return( -1 ); msg[index++] = L1_ARG_UNKNOWN | arglen; BCOPY( bufptr, &(msg[index]), arglen ); index += arglen; break; } default: /* unhandled argument type */ return -1; } } va_end( al ); msg[l1_argno_byte] = l1_argno; return index;}/* sc_interpret_resp verifies an L1 response to a bedrock request, and * breaks the response data up into the constituent parts. If the * response message indicates error, or if a mismatch is found in the * expected number and type of arguments, an error is returned. The * arguments to this function work very much like the arguments to * sc_construct_msg, above, except that L1_ARG_INTs must be followed * by a _pointer_ to an integer that can be filled in by this function. */intsc_interpret_resp( char *resp, /* buffer received from L1 */ int resp_nargs, /* number of _varargs_ passed in */ ... ){ uint32_t buf32; /* 32-bit buffer used to bounce things around */ void *bufptr; /* used to hold response field addresses */ va_list al; /* variable argument list */ int index; /* current index into response buffer */ int argno; /* current position in varargs list */ int l1_fldno; /* number of resp fields received from l1 */ int l1_fld_t; /* field type/length */ index = argno = 0;#if defined(L1_DEBUG)#define DUMP_RESP \ { \ int ix; \ char outbuf[512]; \ sprintf( outbuf, "sc_interpret_resp error line %d: ", __LINE__ ); \ for( ix = 0; ix < 16; ix++ ) { \ sprintf( &outbuf[strlen(outbuf)], "%x ", resp[ix] ); \ } \ printk( "%s\n", outbuf ); \ }#else#define DUMP_RESP#endif /* L1_DEBUG */ /* check response code */ COPY_BUFFER_TO_INT(resp, index, buf32); if( buf32 != L1_RESP_OK ) { DUMP_RESP; return buf32; } /* get number of response fields */ l1_fldno = resp[index++]; va_start( al, resp_nargs ); /* copy out response fields */ while( argno < resp_nargs ) { l1_fldno--; l1_fld_t = va_arg( al, int ); argno++; switch( l1_fld_t ) { case L1_ARG_INT: if( resp[index++] != L1_ARG_INT ) { /* type mismatch */ va_end( al ); DUMP_RESP; return -1; } bufptr = va_arg( al, int* ); argno++; COPY_BUFFER_TO_BUFFER(resp, index, bufptr); break; case L1_ARG_ASCII: if( resp[index++] != L1_ARG_ASCII ) { /* 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)); } atomicAddInt( &(subch->packet_arrived), -1 );}/* 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 pl; /* lock cookie */ 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 = 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, pl ); subch_pull_msg( subch, msg, len ); SUBCH_DATA_UNLOCK( subch, pl ); 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 pl; /* lock cookie */ int is_msg = 0; brl1_sch_t *subch = &(sc->subch[ch]); do { SUBCH_DATA_LOCK(subch, pl); is_msg = 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, &(subch->data_lock), pl ); 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, pl ); return( SC_NMSG ); } subch_pull_msg( subch, msg, len ); SUBCH_DATA_UNLOCK( subch, pl ); 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 notyet if( sc->uart == BRL1_LOCALUART ) { return( sc_recv_intr( sc, ch, resp, len, __WAIT_RECV ) ); } else#endif { 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[
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -