📄 l1.c
字号:
/* uart interrupts were blocked at bedrock when the the interrupt * was initially answered; reenable them now */ intr_unblock_bit( sc->intr_cpu, UART_INTR ); ep = ep; /* placate the compiler */}#endif/* brl1_intr is called directly from the uart interrupt; after it runs, the * interrupt "daemon" xthread is signalled to continue. */#ifdef IRIXvoidbrl1_intr( struct eframe_s *ep ){ /* Disable the UART interrupt, giving the xthread time to respond. * When the daemon (xthread) finishes doing its thing, it will * unblock the interrupt. */ intr_block_bit( get_elsc()->intr_cpu, UART_INTR ); ep = ep; /* placate the compiler */}/* set up uart interrupt handling for this node's uart */voidbrl1_connect_intr( l1sc_t *sc ){ cpuid_t last_cpu; sc->intr_cpu = nodepda->node_first_cpu; if( intr_connect_level(sc->intr_cpu, UART_INTR, INTPEND0_MAXMASK, (intr_func_t)brl1_intrd, 0, (intr_func_t)brl1_intr) ) cmn_err(CE_PANIC, "brl1_connect_intr: Can't connect UART interrupt."); uart_enable_recv_intr( sc );}#endif /* IRIX */#ifdef SABLE/* this function is called periodically to generate fake interrupts * and allow brl1_intrd to send/receive characters */voidhubuart_service( void ){ l1sc_t *sc = get_elsc(); /* note that we'll lose error state by reading the lsr_reg. * This is probably ok in the frictionless domain of sable. */ int lsr_reg; nasid_t nasid = sc->nasid; lsr_reg = READ_L1_UART_REG( nasid, REG_LSR ); if( lsr_reg & (LSR_RCA | LSR_XSRE) ) { REMOTE_HUB_PI_SEND_INTR(0, 0, UART_INTR); }}#endif /* SABLE *//********************************************************************* * The following function allows the kernel to "go around" the * uninitialized l1sc structure to allow console output during * early system startup. *//* These are functions to use from serial_in/out when in protocol * mode to send and receive uart control regs. */voidbrl1_send_control(int offset, int value){ nasid_t nasid = get_nasid(); WRITE_L1_UART_REG(nasid, offset, value); }intbrl1_get_control(int offset){ nasid_t nasid = get_nasid(); return(READ_L1_UART_REG(nasid, offset)); }#define PUTCHAR(ch) \ { \ while( !(READ_L1_UART_REG( nasid, REG_LSR ) & LSR_XHRE) ); \ WRITE_L1_UART_REG( nasid, REG_DAT, (ch) ); \ }intbrl1_send_console_packet( char *str, int len ){ int sent = len; char crc_char; unsigned short crc = INIT_CRC; nasid_t nasid = get_nasid(); PUTCHAR( BRL1_FLAG_CH ); PUTCHAR( BRL1_EVENT | SC_CONS_SYSTEM ); crc = crc16_calc( crc, (BRL1_EVENT | SC_CONS_SYSTEM) ); while( len ) { if( (*str == BRL1_FLAG_CH) || (*str == BRL1_ESC_CH) ) { PUTCHAR( BRL1_ESC_CH ); PUTCHAR( (*str) ^ BRL1_XOR_CH ); } else { PUTCHAR( *str ); } crc = crc16_calc( crc, *str ); str++; len--; } crc ^= 0xffff; crc_char = crc & 0xff; if( (crc_char == BRL1_ESC_CH) || (crc_char == BRL1_FLAG_CH) ) { crc_char ^= BRL1_XOR_CH; PUTCHAR( BRL1_ESC_CH ); } PUTCHAR( crc_char ); crc_char = (crc >> 8) & 0xff; if( (crc_char == BRL1_ESC_CH) || (crc_char == BRL1_FLAG_CH) ) { crc_char ^= BRL1_XOR_CH; PUTCHAR( BRL1_ESC_CH ); } PUTCHAR( crc_char ); PUTCHAR( BRL1_FLAG_CH ); return sent - len;}/********************************************************************* * l1_cons functions * * These allow the L1 to act as the system console. They're intended * to abstract away most of the br/l1 internal details from the * _L1_cons_* functions (in the prom-- see "l1_console.c") and * l1_* functions (in the kernel-- see "sio_l1.c") that they support. * */intl1_cons_poll( l1sc_t *sc ){ /* in case this gets called before the l1sc_t structure for the module_t * struct for this node is initialized (i.e., if we're called with a * zero l1sc_t pointer)... */ if( !sc ) { return 0; } if( sc->subch[SC_CONS_SYSTEM].packet_arrived ) { return 1; } brl1_receive( sc ); if( sc->subch[SC_CONS_SYSTEM].packet_arrived ) { return 1; } return 0;}/* pull a character off of the system console queue (if one is available) */intl1_cons_getc( l1sc_t *sc ){ int c;#ifdef SPINLOCKS_WORK int pl;#endif brl1_sch_t *subch = &(sc->subch[SC_CONS_SYSTEM]); sc_cq_t *q = subch->iqp; if( !l1_cons_poll( sc ) ) { return 0; } SUBCH_DATA_LOCK( subch, pl ); if( cq_empty( q ) ) { subch->packet_arrived = 0; SUBCH_DATA_UNLOCK( subch, pl ); return 0; } cq_rem( q, c ); if( cq_empty( q ) ) subch->packet_arrived = 0; SUBCH_DATA_UNLOCK( subch, pl ); return c;}/* initialize the system console subchannel */voidl1_cons_init( l1sc_t *sc ){#ifdef SPINLOCKS_WORK int pl;#endif brl1_sch_t *subch = &(sc->subch[SC_CONS_SYSTEM]); SUBCH_DATA_LOCK( subch, pl ); subch->packet_arrived = 0; cq_init( subch->iqp ); SUBCH_DATA_UNLOCK( subch, pl );}/* * Write a message to the L1 on the system console subchannel. * * Danger: don't use a non-zero value for the wait parameter unless you're * someone important (like a kernel error message). */intl1_cons_write( l1sc_t *sc, char *msg, int len, int wait ){ return( brl1_send( sc, msg, len, (SC_CONS_SYSTEM | BRL1_EVENT), wait ) );}/* * Read as many characters from the system console receive queue as are * available there (up to avail bytes). */intl1_cons_read( l1sc_t *sc, char *buf, int avail ){ int pl; int before_wrap, after_wrap; brl1_sch_t *subch = &(sc->subch[SC_CONS_SYSTEM]); sc_cq_t *q = subch->iqp; if( !(subch->packet_arrived) ) return 0; SUBCH_DATA_LOCK( subch, pl ); if( q->opos > q->ipos ) { before_wrap = BRL1_QSIZE - q->opos; if( before_wrap >= avail ) { before_wrap = avail; after_wrap = 0; } else { avail -= before_wrap; after_wrap = q->ipos; if( after_wrap > avail ) after_wrap = avail; } } else { before_wrap = q->ipos - q->opos; if( before_wrap > avail ) before_wrap = avail; after_wrap = 0; } BCOPY( q->buf + q->opos, buf, before_wrap ); if( after_wrap ) BCOPY( q->buf, buf + before_wrap, after_wrap ); q->opos = ((q->opos + before_wrap + after_wrap) % BRL1_QSIZE); subch->packet_arrived = 0; SUBCH_DATA_UNLOCK( subch, pl ); return( before_wrap + after_wrap );} /* * Install a callback function for the system console subchannel * to allow an upper layer to be notified when the send buffer * has been emptied. */voidl1_cons_tx_notif( l1sc_t *sc, brl1_notif_t func ){ subch_set_tx_notify( sc, SC_CONS_SYSTEM, func );}/* * Install a callback function for the system console subchannel * to allow an upper layer to be notified when a packet has been * received. */voidl1_cons_rx_notif( l1sc_t *sc, brl1_notif_t func ){ subch_set_rx_notify( sc, SC_CONS_SYSTEM, func );}/********************************************************************* * The following functions and definitions implement the "message"- * style interface to the L1 system controller. * * Note that throughout this file, "sc" generally stands for "system * controller", while "subchannels" tend to be represented by * variables with names like subch or ch. * */#ifdef L1_DEBUG#define L1_DBG_PRF(x) printf x#else#define L1_DBG_PRF(x)#endif/* sc_data_ready is called to signal threads that are blocked on * l1 input. */voidsc_data_ready( l1sc_t *sc, int ch ){ brl1_sch_t *subch = &(sc->subch[ch]); sv_signal( &(subch->arrive_sv) );}/* sc_open reserves a subchannel to send a request to the L1 (the * L1's response will arrive on the same channel). The number * returned by sc_open is the system controller subchannel * acquired. */intsc_open( l1sc_t *sc, uint target ){ /* The kernel version implements a locking scheme to arbitrate * subchannel assignment. */ int ch; int pl; brl1_sch_t *subch; SUBCH_LOCK( sc, pl ); /* Look for a free subchannel. Subchannels 0-15 are reserved * for other purposes. */ for( subch = &(sc->subch[BRL1_CMD_SUBCH]), ch = BRL1_CMD_SUBCH; ch < BRL1_NUM_SUBCHANS; subch++, ch++ ) { if( subch->use == BRL1_SUBCH_FREE ) break; } if( ch == BRL1_NUM_SUBCHANS ) { /* there were no subchannels available! */ SUBCH_UNLOCK( sc, pl ); return SC_NSUBCH; } subch->use = BRL1_SUBCH_RSVD; SUBCH_UNLOCK( sc, pl ); subch->packet_arrived = 0; subch->target = target; sv_init( &(subch->arrive_sv), SV_FIFO, NULL ); spinlock_init( &(subch->data_lock), NULL ); subch->tx_notify = NULL; subch->rx_notify = sc_data_ready; subch->iqp = kmem_zalloc_node( sizeof(sc_cq_t), KM_NOSLEEP, NASID_TO_COMPACT_NODEID(sc->nasid) ); ASSERT( subch->iqp ); cq_init( subch->iqp ); return ch;}/* sc_close frees a Bedrock<->L1 subchannel. */intsc_close( l1sc_t *sc, int ch ){ brl1_sch_t *subch; int pl; SUBCH_LOCK( sc, pl ); subch = &(sc->subch[ch]); if( subch->use != BRL1_SUBCH_RSVD ) { /* we're trying to close a subchannel that's not open */ return SC_NOPEN; } subch->packet_arrived = 0; subch->use = BRL1_SUBCH_FREE; sv_broadcast( &(subch->arrive_sv) ); sv_destroy( &(subch->arrive_sv) ); spinlock_destroy( &(subch->data_lock) ); ASSERT( subch->iqp && (subch->iqp != &sc->garbage_q) ); kmem_free( subch->iqp, sizeof(sc_cq_t) ); subch->iqp = &sc->garbage_q; SUBCH_UNLOCK( sc, pl ); return SC_SUCCESS;}/* sc_construct_msg builds a bedrock-to-L1 request in the supplied * buffer. Returns the length of the message. The * safest course when passing a buffer to be filled in is to use * BRL1_QSIZE as the buffer size. * * Command arguments are passed as type/argument pairs, i.e., to * pass the number 5 as an argument to an L1 command, call * sc_construct_msg as follows: * * char msg[BRL1_QSIZE]; * msg_len = sc_construct_msg( msg, * BRL1_QSIZE, * target_component, * L1_ADDR_TASK_BOGUSTASK, * L1_BOGUSTASK_REQ_BOGUSREQ, * 2, * L1_ARG_INT, 5 ); * * To pass an additional ASCII argument, you'd do the following: * * char *str; * ... str points to a null-terminated ascii string ... * msg_len = sc_construct_msg( msg, * BRL1_QSIZE, * target_component, * L1_ADDR_TASK_BOGUSTASK, * L1_BOGUSTASK_REQ_BOGUSREQ, * 4, * L1_ARG_INT, 5, * L1_ARG_ASCII, str ); * * Finally, arbitrary data of unknown type is passed using the argtype * code L1_ARG_UNKNOWN, a data length, and a buffer pointer, e.g. * * msg_len = sc_construct_msg( msg, * BRL1_QSIZE, * target_component, * L1_ADDR_TASK_BOGUSTASK, * L1_BOGUSTASK_REQ_BOGUSREQ, * 3, * L1_ARG_UNKNOWN, 32, bufptr ); * * ...passes 32 bytes of data starting at bufptr. Note that no string or * "unknown"-type argument should be long enough to overflow the message * buffer. * * To construct a message for an L1 command that requires no arguments, * you'd use the following: * * msg_len = sc_construct_msg( msg, * BRL1_QSIZE, * target_component, * L1_ADDR_TASK_BOGUSTASK, * L1_BOGUSTASK_REQ_BOGUSREQ, * 0 ); * * The final 0 means "no varargs". Notice that this parameter is used to hold * the number of additional arguments to sc_construct_msg, _not_ the actual * number of arguments used by the L1 command (so 2 per L1_ARG_[INT,ASCII] * type argument, and 3 per L1_ARG_UNKOWN type argument). A call to construct * an L1 command which required three integer arguments and two arguments of * some arbitrary (unknown) type would pass 12 as the value for this parameter. * * ENDIANNESS WARNING: The following code does a lot of copying back-and-forth * between byte arrays and four-byte big-endian integers. Depending on the * system controller connection and endianness of future architectures, some * rewriting might be necessary. */intsc_construct_msg( l1sc_t *sc, /* system controller struct */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -