diff options
Diffstat (limited to 'chips/tca100_if.c')
-rw-r--r-- | chips/tca100_if.c | 1377 |
1 files changed, 1377 insertions, 0 deletions
diff --git a/chips/tca100_if.c b/chips/tca100_if.c new file mode 100644 index 0000000..5eeec10 --- /dev/null +++ b/chips/tca100_if.c @@ -0,0 +1,1377 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/*** TCA 100 ATM NETWORK INTERFACE ***/ + +#ifndef STUB +#include <chips/tca100_if.h> +# else +#include "tca100_if.h" +#endif + +#define SMALL_WINDOW_SIZE (BOM_DATA_SIZE + EOM_DATA_SIZE) +#define INITIAL_WINDOW_SIZE BOM_DATA_SIZE +#define CONTINUATION_WINDOW_SIZE (71 * COM_DATA_SIZE) +#define FINAL_WINDOW_SIZE (70 * COM_DATA_SIZE + EOM_DATA_SIZE) +#define MAX_LONG_RX 2 +#define MAX_LONG_TX 5 +#define BASE_TIME_OUT 5 +#define DELAYED_TIME_OUT 15 +#define MAX_RETRY 3 +#define POLL_LIMIT 100000 +#define POLL_IDLE_TIME 1 +#define POLL_CELL_TIME 8 + +#define TCA_SYNCH 0xfc00 +#define TCA_ACK (NW_SUCCESS << 10) +#define TCA_NAK (NW_FAILURE << 10) +#define TCA_OVR (NW_OVERRUN << 10) +#define TCA_SEQ (NW_INCONSISTENCY << 10) + +int tca100_verbose = 0; + +int tick[MAX_DEV]; + +nw_control_s nw_tx_control[MAX_DEV][MAX_LONG_TX]; +nw_control_s nw_rx_control[MAX_DEV][MAX_LONG_RX]; + +int long_tx_count[MAX_DEV], long_rx_count[MAX_DEV]; + +nw_tx_header_t delayed_tx_first[MAX_DEV], delayed_tx_last[MAX_DEV]; +nw_rx_header_t delayed_rx_first[MAX_DEV], delayed_rx_last[MAX_DEV]; + +nw_tcb tct[MAX_EP]; + +u_int MTU[] = {9244, 65528, 65532, 65528}; +u_int MTU_URGENT[] = {32, 28, 32, 28}; + +nw_dev_entry_s tca100_entry_table = { + tca100_initialize, tca100_status, spans_timer_sweep, tca100_timer_sweep, + tca100_poll, tca100_send, tca100_rpc, spans_input, spans_open, spans_accept, + spans_close, spans_add, spans_drop}; + +typedef enum { + ATM_HEADER, + SAR_HEADER, + SAR_TRAILER, + CS_HEADER, + CS_TRAILER, + FRAME_ERROR, + DELIVERY_ERROR, + SYNCH_ERROR, + SEQ_ERROR, + OVERRUN_ERROR, + RX_RETRANSMISSION, + TX_RETRANSMISSION +} tca_error; + +int tca_ec[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +nw_result tca100_initialize(int dev) { + nw_result rc; + int i; + + rc = spans_initialize(dev); + if (rc = NW_SUCCESS) { + tick[dev] = 0; + for (i = 0; i < MAX_LONG_TX; i++) + nw_tx_control[dev][i].ep = 0; + long_tx_count[dev] = 0; + delayed_tx_first[dev] = delayed_tx_last[dev] = NULL; + for (i = 0; i < MAX_LONG_RX; i++) + nw_rx_control[dev][i].ep = 0; + long_rx_count[dev] = 0; + delayed_rx_first[dev] = delayed_rx_last[dev] = NULL; + for (i = 0; i < MAX_EP; i++) { + tct[i].rx_sar_header = 0; + tct[i].rx_control = NULL; + tct[i].tx_queued_count = 0; + tct[i].tx_control = NULL; + } + rc = NW_SUCCESS; + } + return rc; +} + +nw_result tca100_status(int dev) { + nw_result rc; + atm_device_t atmp; + u_int status; + + atmp = (atm_device_t) devct[dev].addr; + status = atmp->sreg; + if (status & RX_NO_CARRIER) { + atmp->creg_set = CR_RX_RESET; + atmp->creg_clr = ~CR_RX_RESET; + atmp->creg_set = CR_RX_ENABLE; + atmp->sreg = 0; + rc = NW_NO_CARRIER; + } else if (status & RX_CELL_LOST) { + atmp->sreg = RX_COUNT_INTR; + rc = NW_OVERRUN; + } else { + rc = NW_SUCCESS; + } + return rc; +} + + +void tca100_synch_send(int dev, nw_tcb_t tcb, u_int reply) { + vol_u_int *tx_fifo = &((atm_device_t) devct[dev].addr)->txfifo[0]; + +#ifdef TRACING + printf("Synch sent %x\n", reply); +#endif + + tx_fifo[0] = tcb->tx_atm_header; + tx_fifo[1] = SSM; + tx_fifo[2] = HTONL(8); + tx_fifo[3] = HTONL(NW_SYNCHRONIZATION); + tx_fifo[4] = htonl(reply); + tx_fifo[5] = HTONL(8); + tx_fifo[6] = 0; + tx_fifo[7] = 0; + tx_fifo[8] = 0; + tx_fifo[9] = 0; + tx_fifo[10] = 0; + tx_fifo[11] = 0; + tx_fifo[12] = 0; + tx_fifo[13] = SYNCH_SEGMENT_TRAILER; +} + +#define broken_cell_mend(length) { \ + missing = length; \ + while (missing != 0) { \ + if (missing > block_count) { \ + limit = 0; \ + missing -= block_count; \ + } else { \ + limit = block_count - missing; \ + missing = 0; \ + } \ + while (block_count > limit) { \ + t1 = block[0]; \ + block++; \ + tx_fifo[1] = t1; \ + block_count -= 4; \ + } \ + if (block_count == 0) { \ + ecb->tx_current = tx_header = ecb->tx_current->next; \ + if (tx_header != NULL) { \ + block_count = tx_header->block_length; \ + block = (vol_u_int *) tx_header->block; \ + } \ + } \ + } \ +} + + +nw_result tca100_window_send(int dev, nw_ecb_t ecb, nw_tcb_t tcb, + boolean_t initial) { + nw_result rc; + register vol_u_int *tx_fifo = &((atm_device_t) devct[dev].addr)->txfifo[0]; + register vol_u_int *block; + register u_int block_count, msg_count; + register int com_count; + int eom_count; + register u_int atm_header, sar_header, sar_trailer; + u_int cs_header, end_count; + int limit, missing; + register u_int t1, t2; + nw_tx_header_t tx_header; + nw_options options; + + atm_header = tcb->tx_atm_header; + if (initial) { + sar_header = tcb->tx_sar_header & MID; + tx_header = ecb->tx_initial; + block = (vol_u_int *) tx_header->block; + block_count = tx_header->block_length; + options = tx_header->options; + msg_count = tx_header->msg_length; + if (ecb->protocol == NW_LINE) + msg_count += 4; + if (options == NW_URGENT) + msg_count += 4; + cs_header = ecb->protocol | (sar_header & 0xff) << 8 | + (msg_count & 0xff00) << 8 | msg_count << 24; + tcb->tx_cs_header = cs_header; + + if (msg_count <= SSM_DATA_SIZE) { /*Single segment message*/ + tx_fifo[0] = atm_header; + sar_trailer = (msg_count + 8) << 26; + tx_fifo[1] = SSM | sar_header; /*Sequence number 0 is implicit*/ + end_count = msg_count + 4; + tx_fifo[1] = cs_header; + if (options == NW_URGENT) { + tx_fifo[1] = HTONL(NW_URGENT); + msg_count -= 4; + } + if (ecb->protocol == NW_LINE) { + tx_fifo[1] = tx_header->peer.local_ep >> 8 | + (tx_header->peer.local_ep & 0x00ff) << 8 | + (tx_header->peer.remote_ep & 0xff00) << 8 | + tx_header->peer.remote_ep << 24; + msg_count -= 4; + } + if (ecb->protocol == NW_SEQ_PACKET) { + tcb->tx_synch = 0; + } else { + tcb->tx_synch = -1; + } + goto EOM_payload; + + } else { /*Beginning of message*/ + tx_fifo[0] = atm_header; + sar_trailer = FULL_SEGMENT_TRAILER; + tx_fifo[1] = BOM | sar_header; /*Sequence number 0 is implicit*/ + tx_fifo[2] = cs_header; + if (block_count < BOM_DATA_SIZE) { + if (ecb->protocol == NW_LINE) { + t1 = tx_header->peer.local_ep >> 8 | + (tx_header->peer.local_ep & 0x00ff) << 8 | + (tx_header->peer.remote_ep & 0xff00) << 8 | + tx_header->peer.remote_ep << 24; + missing = BOM_DATA_SIZE - 4; + tx_fifo[3] = t1; + } else { + missing = BOM_DATA_SIZE; + } + broken_cell_mend(missing); + } else { + if (ecb->protocol == NW_LINE) { + t1 = tx_header->peer.local_ep >> 8 | + (tx_header->peer.local_ep & 0x00ff) << 8 | + (tx_header->peer.remote_ep & 0xff00) << 8 | + tx_header->peer.remote_ep << 24; + } else { + t1 = block[0]; + block_count -= 4; + block++; + } + t2 = block[0]; + tx_fifo[3] = t1; + tx_fifo[4] = t2; + t1 = block[1]; + t2 = block[2]; + tx_fifo[5] = t1; + tx_fifo[6] = t2; + t1 = block[3]; + t2 = block[4]; + tx_fifo[7] = t1; + tx_fifo[8] = t2; + t1 = block[5]; + t2 = block[6]; + tx_fifo[9] = t1; + tx_fifo[10] = t2; + t1 = block[7]; + t2 = block[8]; + tx_fifo[11] = t1; + tx_fifo[12] = t2; + block_count -= (BOM_DATA_SIZE - 4); + block += 9; + } + if (ecb->protocol == NW_RAW) { + msg_count -= BOM_DATA_SIZE; + com_count = msg_count / COM_DATA_SIZE; + msg_count = msg_count % COM_DATA_SIZE; + eom_count = 1; + tcb->tx_synch = -1; + } else if (msg_count > SMALL_WINDOW_SIZE) { + com_count = eom_count = 0; + tcb->tx_synch = msg_count; + msg_count -= BOM_DATA_SIZE; + } else { + com_count = 0; + eom_count = 1; + if (ecb->protocol == NW_SEQ_PACKET) { + tcb->tx_synch = 0; + } else { + tcb->tx_synch = -1; + } + msg_count -= BOM_DATA_SIZE; + } + tx_fifo[13] = sar_trailer; + sar_header += SEQ_INC; + } + + } else { + sar_header = tcb->tx_sar_header; + sar_trailer = FULL_SEGMENT_TRAILER; + block = (vol_u_int *) tcb->tx_p; + block_count = tcb->tx_block_count; + msg_count = tcb->tx_msg_count; + if (msg_count > FINAL_WINDOW_SIZE) { + com_count = (CONTINUATION_WINDOW_SIZE / COM_DATA_SIZE); + eom_count = 0; + tcb->tx_synch = msg_count; + msg_count -= CONTINUATION_WINDOW_SIZE; + } else { + com_count = msg_count / COM_DATA_SIZE; + msg_count = msg_count % COM_DATA_SIZE; + eom_count = 1; + if (ecb->protocol == NW_SEQ_PACKET) { + tcb->tx_synch = 0; + } else { + tcb->tx_synch = -1; + } + } + } + + while (com_count-- > 0) { /*Continuation of message*/ + tx_fifo[0] = atm_header; + tx_fifo[1] = sar_header; /*COM is 0 and is implicit*/ + if (block_count >= COM_DATA_SIZE) { + t1 = block[0]; + t2 = block[1]; + tx_fifo[2] = t1; + tx_fifo[3] = t2; + t1 = block[2]; + t2 = block[3]; + tx_fifo[4] = t1; + tx_fifo[5] = t2; + t1 = block[4]; + t2 = block[5]; + tx_fifo[6] = t1; + tx_fifo[7] = t2; + t1 = block[6]; + t2 = block[7]; + tx_fifo[8] = t1; + tx_fifo[9] = t2; + t1 = block[8]; + t2 = block[9]; + tx_fifo[10] = t1; + tx_fifo[11] = t2; + t1 = block[10]; + block_count -= COM_DATA_SIZE; + tx_fifo[12] = t1; + tx_fifo[13] = sar_trailer; + block += 11; + sar_header = (sar_header + SEQ_INC) & (SEQ_NO | MID); + } else { + broken_cell_mend(COM_DATA_SIZE); + tx_fifo[13] = sar_trailer; + sar_header = (sar_header + SEQ_INC) & (SEQ_NO | MID); + } + } + + if (eom_count != 0) { /*End of message*/ + tx_fifo[0] = atm_header; + tx_fifo[1] = EOM | sar_header; + end_count = msg_count; + sar_trailer = (msg_count + 4) << 26; + + EOM_payload: + if (block_count >= msg_count) { + if (msg_count & 0x4) { + t1 = block[0]; + tx_fifo[1] = t1; + } + block = (vol_u_int *) ((char *) block + msg_count); + switch (msg_count >> 3) { + case 5: + t1 = block[-10]; + t2 = block[-9]; + tx_fifo[1] = t1; + tx_fifo[1] = t2; + case 4: + t1 = block[-8]; + t2 = block[-7]; + tx_fifo[1] = t1; + tx_fifo[1] = t2; + case 3: + t1 = block[-6]; + t2 = block[-5]; + tx_fifo[1] = t1; + tx_fifo[1] = t2; + case 2: + t1 = block[-4]; + t2 = block[-3]; + tx_fifo[1] = t1; + tx_fifo[1] = t2; + case 1: + t1 = block[-2]; + t2 = block[-1]; + tx_fifo[1] = t1; + tx_fifo[1] = t2; + } + msg_count = 0; + } else { + broken_cell_mend(msg_count); + msg_count = 0; + } + + EOM_cs_trailer: + tx_fifo[1] = tcb->tx_cs_header; + switch (end_count) { + case 0: tx_fifo[1] = 0; + case 4: tx_fifo[1] = 0; + case 8: tx_fifo[1] = 0; + case 12: tx_fifo[1] = 0; + case 16: tx_fifo[1] = 0; + case 20: tx_fifo[1] = 0; + case 24: tx_fifo[1] = 0; + case 28: tx_fifo[1] = 0; + case 32: tx_fifo[1] = 0; + case 36: tx_fifo[1] = 0; + } + tx_fifo[13] = sar_trailer; + } + + if (tcb->tx_synch == -1) { + +#ifdef TRACING + printf("Final window sent\n"); +#endif + + sar_header = (sar_header + MID_INC) & MID; + if (sar_header == 0) + sar_header = 1; + tcb->tx_sar_header = sar_header; + rc = NW_SUCCESS; + } else { + +#ifdef TRACING + printf("Window synch at %x\n", msg_count); +#endif + + tcb->tx_sar_header = sar_header; + tcb->tx_p = (u_int *) block; + tcb->tx_block_count = block_count; + tcb->tx_msg_count = msg_count; + rc = NW_SYNCH; + } + return rc; +} + +nw_result tca100_send(nw_ep ep, nw_tx_header_t header, nw_options options) { + nw_result rc; + int i, dev; + nw_ecb_t ecb; + nw_tcb_t tcb; + nw_control_t control; + nw_tx_header_t tx_header, tx_previous; + + dev = NW_DEVICE(header->peer.rem_addr_1); + ecb = &ect[ep]; + tcb = &tct[ep]; + if ((options == NW_URGENT && header->msg_length > + MTU_URGENT[ecb->protocol]) || header->msg_length > MTU[ecb->protocol]) { + rc = NW_BAD_LENGTH; + } else if (tcb->tx_queued_count != 0 || + (ecb->protocol != NW_RAW && + long_tx_count[dev] >= MAX_LONG_TX && + (header->msg_length > SMALL_WINDOW_SIZE || + ecb->protocol == NW_SEQ_PACKET))) { + if (options == NW_URGENT && tcb->tx_queued_count != 0) { + tx_header = delayed_tx_first[dev]; + tx_previous = NULL; + while (tx_header != NULL && tx_header->sender != ep) { + tx_previous = tx_header; + tx_header = tx_header->next; + } + if (tx_previous == NULL) + delayed_tx_first[dev] = header; + else + tx_previous->next = header; + while (header->next != NULL) + header = header->next; + header->next = tx_header; + } else { + if (delayed_tx_first[dev] == NULL) + delayed_tx_first[dev] = header; + else + delayed_tx_last[dev]->next = header; + delayed_tx_last[dev] = header; + } + tcb->tx_queued_count++; + rc = NW_QUEUED; + +#ifdef TRACING + printf("Send enqueued ep %d\n", ep); +#endif + + } else { + + +#ifdef TRACING + printf("Send ep %d\n", ep); +#endif + + ecb->tx_initial = ecb->tx_current = header; + rc = tca100_window_send(dev, ecb, tcb, TRUE); + if (rc == NW_SUCCESS) { + while (header != NULL) { + if (header->buffer != NULL) + nc_buffer_deallocate(ep, header->buffer); + header = header->next; + } + nc_tx_header_deallocate(ecb->tx_initial); + ecb->tx_initial = ecb->tx_current = NULL; + } else { + control = &nw_tx_control[dev][0]; + while (control->ep != 0) + control++; + control->ep = ep; + control->time_out = tick[dev] + BASE_TIME_OUT; + control->retry = 0; + tcb->reply = TCA_SYNCH; + tcb->tx_control = control; + tcb->tx_queued_count++; + if (long_tx_count[dev] + long_rx_count[dev] == 0) + nc_fast_timer_set(dev); + long_tx_count[dev]++; + } + } + return rc; +} + + +nw_result tx_slot_free(int dev, nw_control_t control) { + nw_result rc; + nw_tcb_t tcb; + nw_ecb_t ecb; + nw_tx_header_t tx_header; + nw_ep ep; + + tcb = &tct[control->ep]; + tcb->tx_control = NULL; + tcb->tx_queued_count--; + do { + tx_header = delayed_tx_first[dev]; + if (tx_header == NULL) { + control->ep = 0; + long_tx_count[dev]--; + rc = NW_FAILURE; + } else { + ep = tx_header->sender; + +#ifdef TRACING + printf("Send dequeued ep %d\n", ep); +#endif + + ecb = &ect[ep]; + tcb = &tct[ep]; + ecb->tx_initial = ecb->tx_current = tx_header; + while (tx_header->next != NULL && + tx_header->next->msg_length == 0) { + tx_header = tx_header->next; + } + delayed_tx_first[dev] = tx_header->next; + if (tx_header->next == NULL) + delayed_tx_last[dev] = NULL; + tx_header->next = NULL; + rc = tca100_window_send(dev, ecb, tcb, TRUE); + if (rc == NW_SYNCH) { + control->ep = ep; + tcb->tx_control = control; + tcb->reply = TCA_SYNCH; + control->time_out = tick[dev] + BASE_TIME_OUT; + control->retry = 0; + } + } + } while (rc == NW_SUCCESS); + return rc; +} + +nw_result rx_slot_free(int dev, nw_control_t control) { + nw_result rc; + nw_rx_header_t rx_header; + nw_ep ep; + nw_tcb_t tcb; + + if (control == NULL) { + rc = NW_SUCCESS; + } else { + tct[control->ep].rx_control = NULL; + while ((rx_header = delayed_rx_first[dev]) != NULL && + tick[dev] >= rx_header->time_stamp) { + delayed_rx_first[dev] = rx_header->next; + nc_buffer_deallocate(rx_header->buffer->peer.local_ep, + rx_header->buffer); + ep = rx_header->receiver; + tcb = &tct[ep]; + tcb->rx_sar_header = SSM | (tcb->rx_sar_header & MID); + nc_rx_header_deallocate(rx_header); + } + if (rx_header == NULL) { + delayed_rx_last[dev] = NULL; + control->ep = 0; + long_rx_count[dev]--; + rc = NW_FAILURE; + } else { + delayed_rx_first[dev] = rx_header->next; + if (rx_header->next == NULL) + delayed_rx_last[dev] = NULL; + ep = rx_header->receiver; + tcb = &tct[ep]; + tca100_synch_send(dev, tcb, rx_header->reply); + control->ep = ep; + control->time_out = tick[dev] + BASE_TIME_OUT; + tcb->rx_control = control; + nc_rx_header_deallocate(rx_header); + } + } +} + + +int tca100_poll(int dev) { + vol_u_int *status = &((atm_device_t) devct[dev].addr)->sreg; + vol_u_int *ctl_set = &((atm_device_t) devct[dev].addr)->creg_set; + vol_u_int *rx_counter = &((atm_device_t) devct[dev].addr)->rxcount; + register vol_u_int *rx_fifo = &((atm_device_t) devct[dev].addr)->rxfifo[0]; + register u_int rx_cell_count; + register u_int predicted_atm_header = 0; + register u_int predicted_sar_header; + u_int atm_header, sar_header, predicted_sar_trailer, + cs_header, end_count, cs_pad, rx_cell_total, reply, + block_length, initial_offset; + register vol_u_int *msg; + register int msg_count; + register int next_synch; + register u_int t1, t2; + nw_ecb_t ecb, tx_ecb; + nw_tcb_t new_tcb, tx_tcb; + nw_tcb dummy_tcb_s; + nw_tcb_t tcb = &dummy_tcb_s; + nw_control_t control; + nw_buffer_t buffer; + nw_protocol protocol; + nw_ep lep, rep; + nw_delivery delivery_type = NW_RECEIVE; + nw_rx_header_t rx_header; + nw_tx_header_t tx_header; + int i; + u_int tx_seqno, rx_seqno, tx_count, rx_count; + + rx_cell_total = 0; + while ((rx_cell_count = *rx_counter & RX_COUNTER_MASK) != 0) { + rx_cell_total += rx_cell_count; + while (rx_cell_count-- > 0) { + atm_header = rx_fifo[0]; /*Check ATM header and SAR header*/ + sar_header = (rx_fifo[1] & SAR_HEADER_MASK); + if (atm_header != predicted_atm_header) { + /*Must be cell from a different connection*/ + if (atm_header & ~(ATM_VPVC_MASK | ATM_HEADER_RSV_BITS)) { + atm_header_error: + tca_ec[ATM_HEADER]++; + if (tca100_verbose) + printf("ATM header error %x\n", atm_header); + discard_cell: + *((char *) rx_fifo) = 0; + delivery_type = NW_RECEIVE; + continue; + } else { + t1 = (atm_header & ATM_VPVC_MASK) >> ATM_VPVC_SHIFT; + new_tcb = &tct[t1]; + ecb = &ect[t1]; + + /*Switch cached connection*/ + if (new_tcb->rx_sar_header == 0) + goto atm_header_error; + tcb->rx_sar_header = predicted_sar_header; + tcb->rx_p = (u_int *) msg; + tcb->rx_count = msg_count; + tcb->rx_next_synch = next_synch; + predicted_atm_header = atm_header; + tcb = new_tcb; + predicted_sar_header = tcb->rx_sar_header; + msg = tcb->rx_p; + msg_count = tcb->rx_count; + next_synch = tcb->rx_next_synch; + } + } + + if (sar_header != predicted_sar_header) { + if ((sar_header ^ predicted_sar_header) == EOM && + ((predicted_sar_header & BOM) || msg_count <= EOM_DATA_SIZE)) { + /*Difference on end of message bit only*/ + predicted_sar_header = sar_header; + } else if (sar_header == SSM) { /*MID 0*/ + cs_header = rx_fifo[2]; + t1 = rx_fifo[3]; + if (cs_header == HTONL(8) && t1 == HTONL(NW_SYNCHRONIZATION)) { + reply = rx_fifo[4]; /*Synch cell*/ + if (rx_fifo[5] != cs_header) + goto cs_header_error; + cs_pad = rx_fifo[6]; + t1 = rx_fifo[7]; + t2 = rx_fifo[8]; + cs_pad |= t1; + cs_pad |= t2; + t1 = rx_fifo[9]; + t2 = rx_fifo[10]; + cs_pad |= t1; + cs_pad |= t2; + t1 = rx_fifo[11]; + t2 = rx_fifo[12]; + cs_pad |= t1; + cs_pad |= t2; + t1 = rx_fifo[13]; + if (cs_pad) + goto cs_trailer_error; + if ((t1 & SAR_TRAILER_MASK) != SYNCH_SEGMENT_TRAILER) + goto sar_trailer_error; + if (tcb->tx_control == NULL) { + tca_ec[SYNCH_ERROR]++; + if (tca100_verbose) + printf("Synch error %x\n", ntohl(reply)); + } else { + tcb->reply = ntohl(reply); + +#ifdef TRACING + printf("Received synch ep %d %x\n", ecb->id, tcb->reply); +#endif + + } + continue; + } else if (t1 == HTONL(NW_URGENT)) { /*Urgent cell*/ + delivery_type = NW_RECEIVE_URGENT; + goto cs_header_check; + } else { /*Bad segment*/ + goto sar_header_error; + } + } else if (!(sar_header & ATM_HEADER_CRC_SYNDROME) && + (sar_header & BOM) && (sar_header & SEQ_NO) == 0) { + if ((sar_header & MID) == (predicted_sar_header & MID)) { + /*Retransmission*/ + if (tcb->rx_control != NULL) { + tcb->rx_control->ep = 0; + long_rx_count[dev]--; + } + nc_buffer_deallocate(tcb->rx_buffer->peer.local_ep, + tcb->rx_buffer); + predicted_sar_header = sar_header; + tca_ec[RX_RETRANSMISSION]++; + if (tca100_verbose) + printf("Receiving retransmission ep %d sar %x\n", + ecb->id, sar_header); + } else if (predicted_sar_header & BOM) { + /*Sequence number error*/ + if (tca100_verbose) + printf("Sequence error ep %d pred %x real %x\n", ecb->id, + predicted_sar_header, sar_header); + if (ecb->protocol == NW_SEQ_PACKET) { + reply = 0xffff0000 | TCA_SEQ | (predicted_sar_header & MID); + tca100_synch_send(dev, tcb, reply); + tca_ec[SEQ_ERROR]++; + goto discard_cell; + } else { + predicted_sar_header = sar_header; + } + } else { + goto sar_header_error; /*Badly out of synch*/ + } + } else { /*Cell loss*/ + + sar_header_error: + if (!(predicted_sar_header & BOM)) { + rx_slot_free(dev, tcb->rx_control); + nc_buffer_deallocate(tcb->rx_buffer->peer.local_ep, + tcb->rx_buffer); + predicted_sar_header = SSM | (predicted_sar_header & MID); + } + tca_ec[SAR_HEADER]++; + if (tca100_verbose) + printf("SAR header error ep %d pred %x real %x\n", ecb->id, + predicted_sar_header, sar_header); + goto discard_cell; + } + } + + if ((predicted_sar_header & SEG_TYPE) == COM) { + /*Continuation of message*/ + if (msg_count <= next_synch) { + if (msg_count == next_synch && + msg_count >= CONTINUATION_WINDOW_SIZE) { + reply = (msg_count << 16) | TCA_ACK | (predicted_sar_header & MID); + tca100_synch_send(dev, tcb, reply); + if (msg_count > (CONTINUATION_WINDOW_SIZE + FINAL_WINDOW_SIZE)) { + next_synch = msg_count - CONTINUATION_WINDOW_SIZE; + } else if (ecb->protocol == NW_SEQ_PACKET) { + next_synch = 0; + } else { + next_synch = -1; + } + tcb->rx_control->time_out = tick[dev] + BASE_TIME_OUT; + } else { + rx_slot_free(dev, tcb->rx_control); + nc_buffer_deallocate(tcb->rx_buffer->peer.local_ep, + tcb->rx_buffer); + predicted_sar_header = SSM | (predicted_sar_header & MID); + tca_ec[FRAME_ERROR]++; + if (tca100_verbose) + printf("Frame error ep %d\n", ecb->id); + goto discard_cell; + } + } + t1 = rx_fifo[2]; + t2 = rx_fifo[3]; + msg[0] = t1; + msg[1] = t2; + t1 = rx_fifo[4]; + t2 = rx_fifo[5]; + msg[2] = t1; + msg[3] = t2; + t1 = rx_fifo[6]; + t2 = rx_fifo[7]; + msg[4] = t1; + msg[5] = t2; + t1 = rx_fifo[8]; + t2 = rx_fifo[9]; + msg[6] = t1; + msg[7] = t2; + t1 = rx_fifo[10]; + t2 = rx_fifo[11]; + msg[8] = t1; + msg[9] = t2; + t1 = rx_fifo[12]; + t2 = rx_fifo[13]; + msg[10] = t1; + if ((t2 & SAR_TRAILER_MASK) != FULL_SEGMENT_TRAILER) { + t1 = t2; + goto sar_trailer_error; + } + predicted_sar_header = (predicted_sar_header + SEQ_INC) & + (SEQ_NO | MID); + msg_count -= COM_DATA_SIZE; + msg += 11; + + } else if ((predicted_sar_header & BOM) != 0) { + cs_header = rx_fifo[2]; + + cs_header_check: + block_length = msg_count = (((cs_header >> 8) & 0xff00) | + (cs_header >> 24)); + protocol = cs_header & 0x00ff; + if (protocol == NW_RAW || protocol == NW_SEQ_PACKET) { + lep = ecb->conn->peer.local_ep; + rep = ecb->conn->peer.remote_ep; + if (delivery_type == NW_RECEIVE) + initial_offset = 0; + else + initial_offset = 4; + } else { + t1 = rx_fifo[3]; + block_length -= 4; + lep = (t1 >> 8) & 0xff00 | t1 >> 24; + rep = (t1 & 0xff00) >> 8 | (t1 & 0x00ff) << 8; + if (delivery_type == NW_RECEIVE) + initial_offset = 4; + else + initial_offset = 8; + } + if (protocol != ecb->protocol || (protocol == NW_DATAGRAM) || + (protocol == NW_LINE && ect[lep].protocol != NW_DATAGRAM) || + ((predicted_sar_header & 0x00ff) << 8) != (cs_header & 0xff00) || + ((delivery_type != NW_RECEIVE) && + msg_count - initial_offset > MTU_URGENT[protocol]) || + msg_count > MTU[protocol] || (msg_count & 0x3)) { + + cs_header_error: + if ((protocol != NW_RAW && msg_count > SMALL_WINDOW_SIZE) || + protocol == NW_SEQ_PACKET) { + reply = 0xffff0000 | TCA_NAK | (predicted_sar_header & MID); + tca100_synch_send(dev, tcb, reply); + } + tca_ec[CS_HEADER]++; + if (tca100_verbose) + printf("CS header error ep %d sar %x cs %x\n", ecb->id, + predicted_sar_header, cs_header); + goto discard_cell; + } + buffer = nc_buffer_allocate(lep, sizeof(nw_buffer_s) + block_length); + if (buffer == NULL) { + if ((protocol != NW_RAW && msg_count > SMALL_WINDOW_SIZE) || + protocol == NW_SEQ_PACKET) { + reply = 0xffff0000 | TCA_OVR | (predicted_sar_header & MID); + tca100_synch_send(dev, tcb, reply); + } + tca_ec[OVERRUN_ERROR]++; + if (tca100_verbose) + printf("Overrun error ep %d\n", ecb->id); + goto discard_cell; + } + if (protocol == NW_RAW) { + next_synch = -1; + } else if (msg_count > SMALL_WINDOW_SIZE) { + reply = (msg_count << 16) | TCA_ACK | (predicted_sar_header & MID); + if (long_rx_count[dev] >= MAX_LONG_RX) { + rx_header = nc_rx_header_allocate(); + if (rx_header == NULL) { + nc_buffer_deallocate(lep, buffer); + tca_ec[OVERRUN_ERROR]++; + goto discard_cell; + } + rx_header->buffer = buffer; + rx_header->receiver = ecb->id; + rx_header->reply = reply; + rx_header->time_stamp = tick[dev] + DELAYED_TIME_OUT; + rx_header->next = NULL; + if (delayed_rx_last[dev] == NULL) + delayed_rx_first[dev] = rx_header; + else + delayed_rx_last[dev]->next = rx_header; + delayed_rx_last[dev] = rx_header; + } else { + tca100_synch_send(dev, tcb, reply); + control = &nw_rx_control[dev][0]; + while (control->ep != 0) + control++; + control->ep = ecb->id; + control->time_out = tick[dev] + BASE_TIME_OUT; + tcb->rx_control = control; + if (long_rx_count[dev] + long_tx_count[dev] == 0) + nc_fast_timer_set(dev); + long_rx_count[dev]++; + } + if (msg_count > INITIAL_WINDOW_SIZE + FINAL_WINDOW_SIZE) + next_synch = msg_count - INITIAL_WINDOW_SIZE; + else if (protocol == NW_SEQ_PACKET) + next_synch = 0; + else + next_synch = -1; + } else if (protocol == NW_SEQ_PACKET) { + next_synch = 0; + } else { + next_synch = -1; + } + msg = (vol_u_int *) ((char *) buffer + sizeof(nw_buffer_s)); + tcb->rx_cs_header = cs_header; + tcb->rx_buffer = buffer; + buffer->buf_next = NULL; + buffer->msg_seqno = sar_header & MID; + buffer->block_offset = sizeof(nw_buffer_s); + buffer->block_length = block_length; + buffer->peer.rem_addr_1 = ecb->conn->peer.rem_addr_1; + buffer->peer.rem_addr_2 = ecb->conn->peer.rem_addr_2; + buffer->peer.local_ep = lep; + buffer->peer.remote_ep = rep; + + if ((predicted_sar_header & EOM) == 0) { /*BOM*/ + if (initial_offset == 0) { + t1 = rx_fifo[3]; + t2 = rx_fifo[4]; + msg[0] = t1; + msg[1] = t2; + msg += 2; + } else { + msg[0] = rx_fifo[4]; + msg++; + } + t1 = rx_fifo[5]; + t2 = rx_fifo[6]; + msg[0] = t1; + msg[1] = t2; + t1 = rx_fifo[7]; + t2 = rx_fifo[8]; + msg[2] = t1; + msg[3] = t2; + t1 = rx_fifo[9]; + t2 = rx_fifo[10]; + msg[4] = t1; + msg[5] = t2; + t1 = rx_fifo[11]; + t2 = rx_fifo[12]; + msg[6] = t1; + t1 = rx_fifo[13]; + msg[7] = t2; + if ((t1 & SAR_TRAILER_MASK) != FULL_SEGMENT_TRAILER) + goto sar_trailer_error; + msg_count -= BOM_DATA_SIZE; + msg += 8; + predicted_sar_header = (predicted_sar_header + SEQ_INC) & + (SEQ_NO | MID); + + } else { /*SSM*/ + end_count = msg_count + 4; + predicted_sar_trailer = (msg_count + 8) << 26; + if (delivery_type != NW_RECEIVE) { + msg[0] = NW_URGENT; + msg++; + } + msg_count -= initial_offset; + goto EOM_payload; + } + } else { /*EOM*/ + end_count = msg_count; + predicted_sar_trailer = (msg_count + 4) << 26; + + EOM_payload: + if (msg_count & 0x4) { + msg[0] = rx_fifo[2]; + } + msg = (vol_u_int *) ((char *) msg + msg_count); + /*Fall-through the cases is intentional*/ + switch (msg_count >> 3) { + case 5: + t1 = rx_fifo[2]; + t2 = rx_fifo[2]; + msg[-10] = t1; + msg[-9] = t2; + case 4: + t1 = rx_fifo[2]; + t2 = rx_fifo[2]; + msg[-8] = t1; + msg[-7] = t2; + case 3: + t1 = rx_fifo[2]; + t2 = rx_fifo[2]; + msg[-6] = t1; + msg[-5] = t2; + case 2: + t1 = rx_fifo[2]; + t2 = rx_fifo[2]; + msg[-4] = t1; + msg[-3] = t2; + case 1: + t1 = rx_fifo[2]; + t2 = rx_fifo[2]; + msg[-2] = t1; + msg[-1] = t2; + } + + /*CS trailer should be equal to the CS header, followed by + padding zeros*/ + cs_pad = (rx_fifo[2] != tcb->rx_cs_header); + /*Fall-through the cases is intentional*/ + t1 = t2 = 0; + switch (end_count) { + case 0: + t1 = rx_fifo[2]; + case 4: + t2 = rx_fifo[2]; + cs_pad |= t1; + case 8: + t1 = rx_fifo[2]; + cs_pad |= t2; + case 12: + t2 = rx_fifo[2]; + cs_pad |= t1; + case 16: + t1 = rx_fifo[2]; + cs_pad |= t2; + case 20: + t2 = rx_fifo[2]; + cs_pad |= t1; + case 24: + t1 = rx_fifo[2]; + cs_pad |= t2; + case 28: + t2 = rx_fifo[2]; + cs_pad |= t1; + case 32: + t1 = rx_fifo[2]; + cs_pad |= t2; + case 36: + t2 = rx_fifo[2]; + cs_pad |= t1; + cs_pad |= t2; + } + t1 = rx_fifo[13]; + if (cs_pad != 0) { + /*Errors in CS trailer or pad*/ + cs_trailer_error: + tca_ec[CS_TRAILER]++; + if (tca100_verbose) + printf("CS trailer error ep %d hd %x pad %x\n", ecb->id, + tcb->rx_cs_header, cs_pad); + goto trailer_error; + + } else if ((t1 & SAR_TRAILER_MASK) != predicted_sar_trailer) { + /*Error in SAR trailer or framing*/ + sar_trailer_error: + tca_ec[SAR_TRAILER]++; + if (tca100_verbose) + printf("SAR trailer error ep %d pred %x real %x\n", ecb->id, + predicted_sar_trailer, t1); + goto trailer_error; + + } else if (!nc_deliver_result(tcb->rx_buffer->peer.local_ep, + delivery_type, (int) tcb->rx_buffer)) { + tca_ec[DELIVERY_ERROR]++; + if (tca100_verbose) + printf("Delivery error ep %d\n", ecb->id); + + trailer_error: + if (next_synch >= 0 && !(t1 & HEADER_CRC_ERROR)) { + reply = (msg_count << 16) | TCA_NAK | (predicted_sar_header & MID); + tca100_synch_send(dev, tcb, reply); + } + rx_slot_free(dev, tcb->rx_control); + nc_buffer_deallocate(tcb->rx_buffer->peer.local_ep, + tcb->rx_buffer); + predicted_sar_header = SSM | (predicted_sar_header & MID); + delivery_type = NW_RECEIVE; + } else { + +#ifdef TRACING + printf("Received correctly ep %d\n", ecb->id); +#endif + + if (next_synch == 0) { + reply = TCA_ACK | (predicted_sar_header & MID); + tca100_synch_send(dev, tcb, reply); + } + rx_slot_free(dev, tcb->rx_control); + if (delivery_type != NW_RECEIVE) { + delivery_type = NW_RECEIVE; + predicted_sar_header = SSM | (predicted_sar_header & MID); + } else { + predicted_sar_header = (predicted_sar_header + MID_INC) & MID; + if (predicted_sar_header == 0) + predicted_sar_header = 1; + predicted_sar_header |= SSM; + } + } + } + } + + control = &nw_tx_control[dev][0]; + for (i = 0; i < MAX_LONG_TX; i++) { + if (control->ep != 0 && tct[control->ep].reply != TCA_SYNCH) { + tx_ecb = &ect[control->ep]; + tx_tcb = &tct[control->ep]; + rx_seqno = tx_tcb->reply & MID; + tx_seqno = tx_tcb->tx_sar_header & MID; + rx_count = tx_tcb->reply >> 16; + tx_count = tx_tcb->tx_synch; + reply = tx_tcb->reply & TCA_SYNCH; + if (reply == TCA_ACK) { + if (rx_seqno == tx_seqno && rx_count == tx_count) { + if (rx_count == 0) { +#ifdef TRACING + printf("Received final ack ep %d\n", tx_ecb->id); +#endif + + tx_seqno = (tx_seqno + MID_INC) & MID; + if (tx_seqno == 0) + tx_seqno = 1; + tx_tcb->tx_sar_header = tx_seqno; + tx_slot_free(dev, control); + tx_tcb->reply = NW_SUCCESS; + nc_deliver_result(tx_ecb->id, NW_SEND, NW_SUCCESS); + } else { + if (tca100_window_send(dev, tx_ecb, tx_tcb, + FALSE) == NW_SUCCESS) { + nc_deliver_result(control->ep, NW_SEND, NW_SUCCESS); + tx_tcb->reply = NW_SUCCESS; + tx_slot_free(dev, control); + } else { + control->time_out = tick[dev] + BASE_TIME_OUT; + tx_tcb->reply = TCA_SYNCH; + } + } + } else { + goto synch_error; + } + } else if (reply == TCA_OVR) { + if (rx_seqno == tx_seqno && rx_count == 0xffff && + ((int) tx_ecb->tx_initial->msg_length - + (int) tx_tcb->tx_synch) <= (int) SMALL_WINDOW_SIZE) { + nc_deliver_result(control->ep, NW_SEND, NW_OVERRUN); + tx_tcb->reply = NW_OVERRUN; + tx_slot_free(dev, control); + } else { + goto synch_error; + } + } else if (reply == TCA_NAK) { + if (rx_seqno == tx_seqno && + (rx_count == tx_count || (rx_count == 0xffff && + ((int) tx_ecb->tx_initial->msg_length - + (int) tx_tcb->tx_synch) <= (int) SMALL_WINDOW_SIZE))) { + if (++control->retry < MAX_RETRY) { + if (tca100_verbose) + printf("Sending retransmission ep %d\n", tx_ecb->id); + if (tca100_window_send(dev, tx_ecb, tx_tcb, + TRUE) == NW_SUCCESS) { + nc_deliver_result(control->ep, NW_SEND, NW_SUCCESS); + tx_tcb->reply = NW_SUCCESS; + tx_slot_free(dev, control); + } else { + control->time_out = tick[dev] + BASE_TIME_OUT; + tx_tcb->reply = TCA_SYNCH; + } + tca_ec[TX_RETRANSMISSION]++; + } else { + nc_deliver_result(control->ep, NW_SEND, NW_FAILURE); + tx_tcb->reply = NW_FAILURE; + tx_slot_free(dev, control); + } + } else { + goto synch_error; + } + } else if (reply == TCA_SEQ) { + if (rx_count == 0xffff && tx_ecb->protocol == NW_SEQ_PACKET && + ((int) tx_ecb->tx_initial->msg_length - + (int) tx_tcb->tx_synch) <= (int) SMALL_WINDOW_SIZE && + rx_seqno == ((((tx_seqno + MID_INC) & MID) == 0) ? + 1 : tx_seqno + MID_INC)) { + tx_tcb->tx_sar_header = rx_seqno; + if (tca100_window_send(dev, tx_ecb, tx_tcb, + TRUE) == NW_SUCCESS) { + nc_deliver_result(control->ep, NW_SEND, NW_SUCCESS); + tx_tcb->reply = NW_SUCCESS; + tx_slot_free(dev, control); + } else { + control->time_out = tick[dev] + BASE_TIME_OUT; + tx_tcb->reply = TCA_SYNCH; + } + tca_ec[TX_RETRANSMISSION]++; + if (tca100_verbose) + printf("Sending seq retransmission ep %d\n", tx_ecb->id); + } else { + goto synch_error; + } + } else { + synch_error: + tca_ec[SYNCH_ERROR]++; + tx_tcb->reply = NW_FAILURE; + if (tca100_verbose) + printf("Synch error\n"); + } + } + control++; + } + } + *status = ~(RX_COUNT_INTR | RX_EOM_INTR | RX_TIME_INTR); + tcb->rx_sar_header = predicted_sar_header; + tcb->rx_p = (u_int *) msg; + tcb->rx_count = msg_count; + tcb->rx_next_synch = next_synch; + *ctl_set = RX_COUNT_INTR; + return rx_cell_total; +} + + + +void tca100_timer_sweep(int dev) { + int i, rt; + u_int reply; + nw_control_t control; + nw_ecb_t ecb; + nw_tcb_t tcb; + nw_tx_header_t tx_header; + nw_rx_header_t rx_header; + + tick[dev]++; + control = &nw_rx_control[dev][0]; + for (i = 0; i < MAX_LONG_RX; i++) { + if (control->ep != 0 && control->time_out < tick[dev]) { + rx_slot_free(dev, control); + tcb = &tct[control->ep]; + nc_buffer_deallocate(tcb->rx_buffer->peer.local_ep, tcb->rx_buffer); + tcb->rx_sar_header = SSM | (tcb->rx_sar_header & MID); + } + control++; + } + control = &nw_tx_control[dev][0]; + for (i = 0; i < MAX_LONG_TX; i++) { + if (control->ep != 0 && control->time_out < tick[dev]) { + ecb = &ect[control->ep]; + tcb = &tct[control->ep]; + if (++control->retry < MAX_RETRY) { + if (control->retry == 1) + rt = ( /* random() */ + devct[dev].local_addr_2) & 0x000f; + else + rt = ( /* random() */ + devct[dev].local_addr_1 + + devct[dev].local_addr_2) & 0x00ff; + control->time_out = tick[dev] + BASE_TIME_OUT + rt; + tca100_window_send(dev, ecb, tcb, TRUE); + tca_ec[TX_RETRANSMISSION]++; + } else { + nc_deliver_result(control->ep, NW_SEND, NW_TIME_OUT); + tx_slot_free(dev, control); + } + } + control++; + } + if (long_tx_count[dev] + long_rx_count[dev] > 0) + nc_fast_timer_set(dev); + else + tick[dev] = 0; +} + +nw_buffer_t tca100_rpc(nw_ep ep, nw_tx_header_t header, nw_options options) { + nw_result rc; + nw_buffer_t buf; + nw_ecb_t ecb; + nw_tcb_t tcb; + nw_rx_header_t rx_header; + int dev, poll_time, ncells; + + tcb = &tct[ep]; + ecb = &ect[header->peer.local_ep]; + dev = NW_DEVICE(header->peer.rem_addr_1); + if ((rc = tca100_send(ep, header, options)) == NW_BAD_LENGTH) { + buf = NW_BUFFER_ERROR; + } else if (rc == NW_QUEUED) { + buf = NULL; + } else { + poll_time = 0; + if (rc == NW_SYNCH) { + while (tcb->reply == TCA_SYNCH && poll_time < POLL_LIMIT) { + ncells = tca100_poll(dev); + if (ncells == 0) + poll_time += POLL_IDLE_TIME; + else + poll_time += ncells * POLL_CELL_TIME; + } + } + if (tcb->reply != NW_SUCCESS) { + buf = NW_BUFFER_ERROR; + } else { + while (ecb->rx_first == NULL && poll_time < POLL_LIMIT) { + ncells = tca100_poll(dev); + if (ncells == 0) + poll_time += POLL_IDLE_TIME; + else + poll_time += ncells * POLL_CELL_TIME; + } + if (ecb->rx_first == NULL) { + buf = NULL; + } else { + rx_header = ecb->rx_first; + buf = rx_header->buffer; + ecb->rx_first = rx_header->next; + if (ecb->rx_first == NULL) + ecb->rx_last = NULL; + nc_rx_header_deallocate(rx_header); + } + } + } + + return buf; +} + + + + + + + + + |