📄 l1.c
字号:
other BRL1_IDLE@BRL1_FLAG (saw a flag (0x7e)) flag BRL1_FLAG escape BRL1_IDLE@ header byte BRL1_HDR other BRL1_IDLE@BRL1_HDR (saw a type/subch byte)(see below) BRL1_BODY BRL1_HDRBRL1_BODY (reading packet body) flag BRL1_FLAG escape BRL1_ESC other BRL1_BODYBRL1_ESC (saw an escape (0x7d)) flag BRL1_FLAG@ escape BRL1_IDLE@ other BRL1_BODY=========================================================="@" denotes an error transition. * The BRL1_HDR state is a transient state which doesn't read input, * but just provides a way in to code which decides to whom an * incoming packet should be directed. * * brl1_receive can be used to poll for input from the L1, or as * an interrupt service routine. It reads as much data as is * ready from the junk bus UART and places into the appropriate * input queues according to subchannel. The header byte is * stripped from console-type data, but is retained for message- * type data (L1 responses). A length byte will also be * prepended to message-type packets. * * This routine is non-blocking; if the caller needs to block * for input, it must call brl1_receive in a loop. * * brl1_receive returns when there is no more input, the queue * for the current incoming message is full, or there is an * error (parity error, bad header, bad CRC, etc.). */#define STATE_SET(l,s) ((l)->brl1_state = (s))#define STATE_GET(l) ((l)->brl1_state)#define LAST_HDR_SET(l,h) ((l)->brl1_last_hdr = (h))#define LAST_HDR_GET(l) ((l)->brl1_last_hdr)#define SEQSTAMP_INCR(l)#define SEQSTAMP_GET(l)#define VALID_HDR(c) \ ( SUBCH((c)) <= SC_CONS_SYSTEM \ ? PKT_TYPE((c)) == BRL1_REQUEST \ : ( PKT_TYPE((c)) == BRL1_RESPONSE || \ PKT_TYPE((c)) == BRL1_EVENT ) )#define IS_TTY_PKT(l) \ ( SUBCH(LAST_HDR_GET(l)) <= SC_CONS_SYSTEM ? 1 : 0 )intbrl1_receive( l1sc_t *sc ){ int result; /* value to be returned by brl1_receive */ int c; /* most-recently-read character */ int pl; /* priority level for UART receive lock */ int done; /* set done to break out of recv loop */ sc_cq_t *q; /* pointer to queue we're working with */ result = BRL1_NO_MESSAGE; L1SC_RECV_LOCK( sc, pl ); L1_CONS_HW_LOCK( sc ); done = 0; while( !done ) { switch( STATE_GET(sc) ) { case BRL1_IDLE: /* Initial or error state. Waiting for a flag character * to resynchronize with the L1. */ if( !read_uart( sc, &c, &result ) ) { /* error reading uart */ done = 1; continue; } if( c == BRL1_FLAG_CH ) { /* saw a flag character */ STATE_SET( sc, BRL1_FLAG ); continue; } break; case BRL1_FLAG: /* One or more flag characters have been read; look for * the beginning of a packet (header byte). */ if( !read_uart( sc, &c, &result ) ) { /* error reading uart */ if( c != UART_NO_CHAR ) STATE_SET( sc, BRL1_IDLE ); done = 1; continue; } if( c == BRL1_FLAG_CH ) { /* multiple flags are OK */ continue; } if( !VALID_HDR( c ) ) { /* if c isn't a flag it should have been * a valid header, so we have an error */ result = BRL1_PROTOCOL; STATE_SET( sc, BRL1_IDLE ); done = 1; continue; } /* we have a valid header byte */ LAST_HDR_SET( sc, c ); STATE_SET( sc, BRL1_HDR ); break; case BRL1_HDR: /* A header byte has been read. Do some bookkeeping. */ q = sc->subch[ SUBCH( LAST_HDR_GET(sc) ) ].iqp; ASSERT(q); if( !IS_TTY_PKT(sc) ) { /* if this is an event or command response rather * than console I/O, we need to reserve a couple * of extra spaces in the queue for the header * byte and a length byte; if we can't, stay in * the BRL1_HDR state. */ if( cq_room( q ) < 2 ) { result = BRL1_FULL_Q; done = 1; continue; } cq_tent_add( q, 0 ); /* reserve length byte */ cq_tent_add( q, LAST_HDR_GET( sc ) ); /* record header byte */ } STATE_SET( sc, BRL1_BODY ); break; case BRL1_BODY: /* A header byte has been read. We are now attempting * to receive the packet body. */ q = sc->subch[ SUBCH( LAST_HDR_GET(sc) ) ].iqp; ASSERT(q); /* if the queue we want to write into is full, don't read from * the uart (this provides backpressure to the L1 side) */ if( cq_tent_full( q ) ) { result = BRL1_FULL_Q; done = 1; continue; } if( !read_uart( sc, &c, &result ) ) { /* error reading uart */ if( c != UART_NO_CHAR ) STATE_SET( sc, BRL1_IDLE ); done = 1; continue; } if( c == BRL1_ESC_CH ) { /* prepare to unescape the next character */ STATE_SET( sc, BRL1_ESC ); continue; } if( c == BRL1_FLAG_CH ) { /* flag signifies the end of a packet */ unsigned short crc; /* holds the crc as we calculate it */ int i; /* index variable */ brl1_sch_t *subch; /* subchannel for received packet */ int sch_pl; /* cookie for subchannel lock */ brl1_notif_t callup; /* "data ready" callup */ /* whatever else may happen, we've seen a flag and we're * starting a new packet */ STATE_SET( sc, BRL1_FLAG ); SEQSTAMP_INCR(sc); /* bump the packet sequence counter */ /* if the packet body has less than 2 characters, * it can't be a well-formed packet. Discard it. */ if( cq_tent_len( q ) < /* 2 + possible length byte */ (2 + (IS_TTY_PKT(sc) ? 0 : 1)) ) { result = BRL1_PROTOCOL; cq_discard_tent( q ); STATE_SET( sc, BRL1_FLAG ); done = 1; continue; } /* check CRC */ /* accumulate CRC, starting with the header byte and * ending with the transmitted CRC. This should * result in a known good value. */ crc = crc16_calc( INIT_CRC, LAST_HDR_GET(sc) ); for( i = (q->ipos + (IS_TTY_PKT(sc) ? 0 : 2)) % BRL1_QSIZE; i != q->tent_next; i = (i + 1) % BRL1_QSIZE ) { crc = crc16_calc( crc, q->buf[i] ); } /* verify the caclulated crc against the "good" crc value; * if we fail, discard the bad packet and return an error. */ if( crc != (unsigned short)GOOD_CRC ) { result = BRL1_CRC; cq_discard_tent( q ); STATE_SET( sc, BRL1_FLAG ); done = 1; continue; } /* so the crc check was ok. Now we discard the CRC * from the end of the received bytes. */ q->tent_next += (BRL1_QSIZE - 2); q->tent_next %= BRL1_QSIZE; /* get the subchannel and lock it */ subch = &(sc->subch[SUBCH( LAST_HDR_GET(sc) )]); SUBCH_DATA_LOCK( subch, sch_pl ); /* if this isn't a console packet, we need to record * a length byte */ if( !IS_TTY_PKT(sc) ) { q->buf[q->ipos] = cq_tent_len( q ) - 1; } /* record packet for posterity */ cq_commit_tent( q ); result = BRL1_VALID; /* notify subchannel owner that there's something * on the queue for them */ atomicAddInt( &(subch->packet_arrived), 1); callup = subch->rx_notify; SUBCH_DATA_UNLOCK( subch, sch_pl ); if( callup ) { L1_CONS_HW_UNLOCK( sc ); L1SC_RECV_UNLOCK( sc, pl ); (*callup)( sc, SUBCH(LAST_HDR_GET(sc)) ); L1SC_RECV_LOCK( sc, pl ); L1_CONS_HW_LOCK( sc ); } continue; /* go back for more! */ } /* none of the special cases applied; we've got a normal * body character */ cq_tent_add( q, c ); break; case BRL1_ESC: /* saw an escape character. The next character will need * to be unescaped. */ q = sc->subch[ SUBCH( LAST_HDR_GET(sc) ) ].iqp; ASSERT(q); /* if the queue we want to write into is full, don't read from * the uart (this provides backpressure to the L1 side) */ if( cq_tent_full( q ) ) { result = BRL1_FULL_Q; done = 1; continue; } if( !read_uart( sc, &c, &result ) ) { /* error reading uart */ if( c != UART_NO_CHAR ) { cq_discard_tent( q ); STATE_SET( sc, BRL1_IDLE ); } done = 1; continue; } if( c == BRL1_FLAG_CH ) { /* flag after escape is an error */ STATE_SET( sc, BRL1_FLAG ); cq_discard_tent( q ); result = BRL1_PROTOCOL; done = 1; continue; } if( c == BRL1_ESC_CH ) { /* two consecutive escapes is an error */ STATE_SET( sc, BRL1_IDLE ); cq_discard_tent( q ); result = BRL1_PROTOCOL; done = 1; continue; } /* otherwise, we've got a character that needs * to be unescaped */ cq_tent_add( q, (c ^ BRL1_XOR_CH) ); STATE_SET( sc, BRL1_BODY ); break; } /* end of switch( STATE_GET(sc) ) */ } /* end of while(!done) */ L1_CONS_HW_UNLOCK( sc ); L1SC_RECV_UNLOCK(sc, pl); return result;} /* brl1_init initializes the Bedrock/L1 protocol layer. This includes * zeroing out the send and receive state information. */voidbrl1_init( l1sc_t *sc, nasid_t nasid, net_vec_t uart ){ int i; brl1_sch_t *subch; bzero( sc, sizeof( *sc ) ); sc->nasid = nasid; sc->uart = uart; sc->getc_f = (uart == BRL1_LOCALUART ? uart_getc : rtr_uart_getc); sc->putc_f = (uart == BRL1_LOCALUART ? uart_putc : rtr_uart_putc); sc->sol = 1; subch = sc->subch; /* initialize L1 subchannels */ /* assign processor TTY channels */ for( i = 0; i < CPUS_PER_NODE; i++, subch++ ) { subch->use = BRL1_SUBCH_RSVD; subch->packet_arrived = 0; spinlock_init( &(subch->data_lock), NULL ); sv_init( &(subch->arrive_sv), SV_FIFO, NULL ); subch->tx_notify = NULL; /* (for now, drop elscuart packets in the kernel) */ subch->rx_notify = brl1_discard_packet; subch->iqp = &sc->garbage_q; } /* assign system TTY channel (first free subchannel after each * processor's individual TTY channel has been assigned) */ subch->use = BRL1_SUBCH_RSVD; subch->packet_arrived = 0; spinlock_init( &(subch->data_lock), NULL ); sv_init( &(subch->arrive_sv), SV_FIFO, NULL ); subch->tx_notify = NULL; if( sc->uart == BRL1_LOCALUART ) { subch->iqp = kmem_zalloc_node( sizeof(sc_cq_t), KM_NOSLEEP, NASID_TO_COMPACT_NODEID(nasid) ); ASSERT( subch->iqp ); cq_init( subch->iqp ); subch->rx_notify = NULL; } else { /* we shouldn't be getting console input from remote UARTs */ subch->iqp = &sc->garbage_q; subch->rx_notify = brl1_discard_packet; } subch++; i++; /* "reserved" subchannels (0x05-0x0F); for now, throw away * incoming packets */ for( ; i < 0x10; i++, subch++ ) { subch->use = BRL1_SUBCH_FREE; subch->packet_arrived = 0; subch->tx_notify = NULL; subch->rx_notify = brl1_discard_packet; subch->iqp = &sc->garbage_q; } /* remaining subchannels are free */ for( ; i < BRL1_NUM_SUBCHANS; i++, subch++ ) { subch->use = BRL1_SUBCH_FREE; subch->packet_arrived = 0; subch->tx_notify = NULL; subch->rx_notify = brl1_discard_packet; subch->iqp = &sc->garbage_q; } /* initialize synchronization structures */ spinlock_init( &(sc->send_lock), NULL ); spinlock_init( &(sc->recv_lock), NULL ); spinlock_init( &(sc->subch_lock), NULL ); if( sc->uart == BRL1_LOCALUART ) { uart_init( sc, UART_BAUD_RATE ); } else { rtr_uart_init( sc, UART_BAUD_RATE ); } /* Set up remaining fields using L1 command functions-- elsc_module_get * to read the module id, elsc_debug_get to see whether or not we're * in verbose mode. */ { extern int elsc_module_get(l1sc_t *); sc->modid = elsc_module_get( sc ); sc->modid = (sc->modid < 0 ? INVALID_MODULE : sc->modid); sc->verbose = 1; }}/********************************************************************* * These are interrupt-related functions used in the kernel to service * the L1. *//* * brl1_intrd is the function which is called in a loop by the * xthread that services L1 interrupts. */#ifdef IRIXvoidbrl1_intrd( struct eframe_s *ep ){ u_char isr_reg; l1sc_t *sc = get_elsc(); isr_reg = READ_L1_UART_REG(sc->nasid, REG_ISR); while( isr_reg & (ISR_RxRDY | ISR_TxRDY) ) { if( isr_reg & ISR_RxRDY ) { brl1_receive(sc); } if( (isr_reg & ISR_TxRDY) || (sc->send_in_use && UART_PUTC_READY(sc->nasid)) ) { brl1_send_cont(sc); } isr_reg = READ_L1_UART_REG(sc->nasid, REG_ISR); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -