📄 l1.c
字号:
} crc = crc16_calc( crc, *msg ); msg++; } crc ^= 0xffff; for( index = 0; index < sizeof(crc); index++ ) { char crc_char = (char)(crc & 0x00FF); if( (crc_char == BRL1_ESC_CH) || (crc_char == BRL1_FLAG_CH) ) { *send_ptr++ = BRL1_ESC_CH; pkt_len++; crc_char ^= BRL1_XOR_CH; } *send_ptr++ = crc_char; pkt_len++; crc >>= 8; } *send_ptr++ = BRL1_FLAG_CH; pkt_len++; sc->send_len = pkt_len; sc->sent = 0; { int counter = 0; do { brl1_send_chars( sc ); if ( counter++ > 0xfffff ) { char *str = "Looping waiting for uart to clear (2)\n"; early_l1_serial_out(sc->nasid, str, strlen(str), ALREADY_LOCKED); break; } } while( (sc->sent < sc->send_len) && wait ); } if ( sc->uart == BRL1_LOCALHUB_UART ) unlock_console(sc->nasid); if( sc->sent == sc->send_len ) { /* success! release the send buffer and call the callup */#if !defined(SYNC_CONSOLE_WRITE) brl1_notif_t callup;#endif sc->send_in_use = 0; /* call any upper layer that's asked for notification */#if defined(XX_SYNC_CONSOLE_WRITE) /* * This is probably not a good idea - since the l1_ write func can be called multiple * time within the callup function. */ callup = subch->tx_notify; if( callup && (SUBCH(type_and_subch) == SC_CONS_SYSTEM) ) { L1_collectibles[L1C_SEND_CALLUPS]++; (*callup)(sc->subch[SUBCH(type_and_subch)].irq_frame.bf_irq, sc->subch[SUBCH(type_and_subch)].irq_frame.bf_dev_id, sc->subch[SUBCH(type_and_subch)].irq_frame.bf_regs, sc, SUBCH(type_and_subch)); }#endif /* SYNC_CONSOLE_WRITE */ }#if !defined(SYNC_CONSOLE_WRITE) else if ( !wait ) { /* enable low-water interrupts so buffer will be drained */ uart_enable_xmit_intr(sc); }#endif L1SC_SEND_UNLOCK(sc, pl); return len;}/* brl1_send_cont is intended to be called as an interrupt service * routine. It sends until the UART won't accept any more characters, * or until an error is encountered (in which case we surrender the * send buffer and give up trying to send the packet). Once the * last character in the packet has been sent, this routine releases * the send buffer and calls any previously-registered "low-water" * output routines. */#if !defined(SYNC_CONSOLE_WRITE)intbrl1_send_cont( l1sc_t *sc ){ unsigned long pl = 0; int done = 0; brl1_notif_t callups[BRL1_NUM_SUBCHANS]; brl1_notif_t *callup; brl1_sch_t *subch; int index; /* * I'm not sure how I think this is to be handled - whether the lock is held * over the interrupt - but it seems like it is a bad idea.... */ if ( sc->uart == BRL1_LOCALHUB_UART ) lock_console(sc->nasid); L1SC_SEND_LOCK(sc, pl); brl1_send_chars( sc ); done = (sc->sent == sc->send_len); if( done ) { sc->send_in_use = 0;#if !defined(SYNC_CONSOLE_WRITE) uart_disable_xmit_intr(sc);#endif } if ( sc->uart == BRL1_LOCALHUB_UART ) unlock_console(sc->nasid); /* Release the lock */ L1SC_SEND_UNLOCK(sc, pl); return 0;}#endif /* SYNC_CONSOLE_WRITE *//* internal function -- used by brl1_receive to read a character * from the uart and check whether errors occurred in the process. */static intread_uart( l1sc_t *sc, int *c, int *result ){ *c = sc->getc_f( sc ); /* no character is available */ if( *c == UART_NO_CHAR ) { *result = BRL1_NO_MESSAGE; return 0; } /* some error in UART */ if( *c < 0 ) { *result = BRL1_LINK; return 0; } /* everything's fine */ *result = BRL1_VALID; return 1;}/* * brl1_receive * * This function reads a Bedrock-L1 protocol packet into the l1sc_t * response buffer. * * The operation of this function can be expressed as a finite state * machine: *START STATE INPUT TRANSITION==========================================================BRL1_IDLE (reset or error) flag BRL1_FLAG 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 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 mode ){ int result; /* value to be returned by brl1_receive */ int c; /* most-recently-read character */ int done; /* set done to break out of recv loop */ unsigned long pl = 0, cpl = 0; sc_cq_t *q; /* pointer to queue we're working with */ result = BRL1_NO_MESSAGE; L1SC_RECV_LOCK(sc, cpl); 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 */ 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 ); /* 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, 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 */ atomic_inc(&(subch->packet_arrived)); callup = subch->rx_notify; SUBCH_DATA_UNLOCK( subch, pl ); if( callup && (mode == SERIAL_INTERRUPT_MODE) ) { L1SC_RECV_UNLOCK( sc, cpl ); L1_collectibles[L1C_RECEIVE_CALLUPS]++; (*callup)( sc->subch[SUBCH(LAST_HDR_GET(sc))].irq_frame.bf_irq, sc->subch[SUBCH(LAST_HDR_GET(sc))].irq_frame.bf_dev_id, sc->subch[SUBCH(LAST_HDR_GET(sc))].irq_frame.bf_regs, sc, SUBCH(LAST_HDR_GET(sc)) ); L1SC_RECV_LOCK( sc, cpl ); } 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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -