diff --git a/src/isdn/v110.c b/src/isdn/v110.c index 46962bfa5..47f4aabd7 100644 --- a/src/isdn/v110.c +++ b/src/isdn/v110.c @@ -30,6 +30,8 @@ #include #include +#include +#include #include @@ -581,3 +583,331 @@ int osmo_v110_sync_ra1_ir_to_user(enum osmo_v100_sync_ra1_rate rate, ubit_t *d_o return osmo_v110_sync_ra1_def[rate].adapt_ir_to_user(d_out, out_len, fr); } + +/********************************************************************************* + * V.110 TERMINAL ADAPTER FSMs + *********************************************************************************/ + +enum v110_ta_state { + V110_TA_S_IDLE_READY, /* Idle (or ready) state */ + V110_TA_S_CON_TA_LINE, /* Connect TA to line state */ + V110_TA_S_DATA_TRANSFER, /* Data transfer state */ + V110_TA_S_RESYNCING, /* Re-synchronizing state */ +}; + +enum v110_ta_event { + V110_TA_E_RX_FRAME_IND, /* Received V.110 frame indication */ + V110_TA_E_TX_FRAME_RTS, /* V.110 frame Ready-to-send indication */ +}; + +static const struct value_string v110_ta_event_names[] = { + { V110_TA_E_RX_FRAME_IND, "RX_FRAME_IND" }, + { V110_TA_E_TX_FRAME_RTS, "TX_FRAME_RTS" }, + { 0, NULL } +}; + +enum v110_ta_tx_d_bit_mode { + V110_TA_TX_FRAME_ALL_ONE, + V110_TA_TX_FRAME_ALL_ZERO, + V110_TA_TX_FRAME_FROM_DTE, +}; + +struct v110_ta_state { + /* V.24 status flags shared between DTE (user) and DCE (TA, us) */ + v24_flagmask v24_flags; + struct { + /* is end-to-end flow-control enabled or not? */ + bool end_to_end_flowctrl; + /* synchronous user rate */ + enum osmo_v100_sync_ra1_rate rate; + } cfg; + struct { + /* what kind of D-bits to transmit in V.110 frames */ + enum v110_ta_tx_d_bit_mode d_bit_mode; + /* what to put in S-bits of transmitted V.110 frames (true = ON) */ + bool s_bits; + /* what to put in X-bits of transmitted V.110 frames (true = OFF) */ + bool x_bits; + /* what to put in E-bits of transmitted V.110 frames */ + ubit_t e_bits[MAX_E_BITS]; + } tx; + struct { + enum v11o_ta_tx_d_bit_mode bit_mode; + } rx; +}; + +/* build one V.110 frame to transmit */ +static void v110_ta_build_frame(struct osmo_v110_decoded_frame *out, struct osmo_fsm_inst *fi) +{ + struct v110_ta_state *ts = (struct v110_ta_state *) fi->priv; + + /* D-bits */ + switch (ts->tx.d_bit_mode) { + case V110_TA_TX_FRAME_ALL_ONE: + memset(out->d_bits, 1, sizeof(out->d_bits)); + break; + case V110_TA_TX_FRAME_ALL_ZERO: + memset(out->d_bits, 0, sizeof(out->d_bits)); + break; + case V110_TA_TX_FRAME_FROM_DTE: + //FIXME: retrieve user bits */ + rc = osmo_v110_sync_ra1_user_to_ir(ts->cfg.rate, out, user_bits, num_user_bits); + OSMO_ASSERT(rc == 0); + break; + }; + + /* E-bits */ + memcpy(out->e_bits, ts->tx.e_bits, sizeof(out->e_bits)); + + /* S-bits */ + if (ts->tx.s_bits == true) + memset(out->s_bits, 0, sizeof(out->s_bits)); + else + memset(out->s_bits, 1, sizeof(out->s_bits)); + + /* X-bits */ + if (ts->tx.x_bits == true) + memset(out->x_bits, 0, sizeof(out->x_bits)); + else + memset(out->x_bits, 1, sizeof(out->x_bits)); +} + +static void v24_flags_updated(struct osmo_fsm_inst *fi) +{ + /* FIXME: somehow notify the USART about it */ +} + +/* ITU-T V.110 Section 7.1.1 */ +static void v110fsm_ta_idle_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state); +{ + struct v110_ta_state *ts = (struct v110_ta_state *) fi->priv; + + /* E4 .. E7 bits (lower 3 bits are generated by v110 frame encoder) */ + memset(ts->tx.e_bits+3, 1, 4); + ts->user_data_cunk_bitlen = osmo_v110_sync_ra1_get_user_data_chunk_bitlen(ts->cfg.rate); + + /* 7.1.1.2 During the idle (or ready) state the TA will transmit continuous binary 1s into the B-channel */ + /* 7.1.1.3 During the idle (or ready) state the TA (DCE) will transmit the following toward the DTE: * */ + /* - 104: continuous binary 1*/ + ts->rx.bit_mode = V110_TA_TX_FRAME_ALL_ONE; + /* - 107, 106, 109 = OFF */ + V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V24_C_106); + V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V24_C_107); + V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V24_C_109); + v24_flags_updated(fi); +} + +/* ITU-T V.110 Section 7.1.1 */ +static void v110fsm_ta_idle_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct v110_ta_state *ts = (struct v110_ta_state *) fi->priv; + const struct osmo_v110_decoded_frame *fr = NULL; + int rc; + + switch (event) { + case V110_TA_E_RX_FRAME_IND: + fr = data; + rc = osmo_v110_sync_ra1_ir_to_user(ts->cfg.rate, d_out, out_len, fr); + break; + case V110_TA_E_TX_FRAME_RTS: + /* transmit continuous binary 1 to B channels */ + break; + case V110_TA_E_SWITCH_TO_DATA_MODE: + /* When the TA is to be switched to the data mode, circuit 108 must be ON */ + if (V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V24_C_108_2)) { + /* 7.12.2: Start timer T1 when switching to CON_TA_LINE */ + osmo_fsm_inst_state_chg(fi, V110_TA_S_CON_TA_LINE, 10, 1); + } + break; + default: + OSMO_ASSERT(0); + } +} + +/* ITU-t V.110 Section 7.1.2 */ +static void v110fsm_ta_connect_to_line_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state); +{ + struct v110_ta_state *ts = (struct v110_ta_state *) fi->priv; + + /* frame sync pattern as per 5.1.3.1 / 5.2.1 */ + FIXME + /* data bits: binary 1 */ + ts->tx.d_bit_mode = V110_TA_TX_FRAME_ALL_ONE; + /* S = OFF, X = OFF (ON = binary 0; OFF = binary 1) */ + ts->tx.s_bits = false; + ts->tx.x_bits = false; + /* onenter: T1 has been started */ + OSMO_ASSERT(fi->T = 1); +} + +static bool all_bits_are(const ubit_t *in, ubit_t cmp, size_t in_len) +{ + for (unsigned int i = 0; i < in_len; i++) { + if (in[i] != cmp) + return false; + } + return true; +} +#define ARRAY_ALL_BITS_ONE(arr) all_bits_are((arr), 1, sizeof(arr)) +#define ARRAY_ALL_BITS_ZERO(arr) all_bits_are((arr), 0, sizeof(arr)) + +/* ITU-t V.110 Section 7.1.2 */ +static void v110fsm_ta_connect_ta_to_line(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct v110_ta_state *ts = (struct v110_ta_state *) fi->priv; + struct osmo_v110_decoded_frame *fr = NULL; + + switch (event) { + case V110_TA_E_RX_FRAME_IND: + fr = data; + if (ARRAY_ALL_BITS_ZERO(fr->s_bits) && ARRAY_ALL_BITS_ZERO(fr->x_bits)) { + /* 7.1.2.4 When the receiver recognizes that the status of bits S and X are in the ON + * condition, it will perform the following functions: */ + /* a) Turn ON circuit 107 toward the DTE and stop timer T1. */ + V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_107); + /* b) Then, circuit 103 may be connected to the data bits in the frame; however, the + * DTE must maintain a binary 1 condition on circuit 103 until circuit 106 is turned + * ON in the next portion of the sequence. */ + /* c) Turn ON circuit 109 and connect the data bits to circuit 104. */ + V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_109); + /* d) After an interval of N bits (see 6.3), it will turn ON circuit 106. */ + V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_106); + v24_flags_updated(fi); + /* Circuit 106 transitioning from OFF to ON will cause the transmitted data to + * transition from binary 1 to the data mode. */ + osmo_fsm_inst_state_chg(fi, V110_TA_S_DATA_XFER, 0, 0); + + rc = osmo_v110_sync_ra1_ir_to_user(ts->cfg.rate, d_out, out_len, fr); + } + break; + case V110_TA_E_TX_FRAME_RTS: + fr = data; + v110_ta_build_frame(fr, fi); + break; + case V110_TA_E_RX_SYNC_IND: + /* 7.1.2.3 When the receiver recognizes the frame synchronization pattern, it causes the S- + * and X-bits in the transmitted frames to be turned ON (provided that circuit 108 is ON). */ + if (V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V24_C_108_2)) { + ts->tx.s_bits = true; + ts->tx.x_bits = true; + } + break; + default: + OSMO_ASSERT(0); + } +} + +/* ITU-t V.110 Section 7.1.3 */ +static void v110fsm_ta_data_transfer_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct v110_ta_state *ts = (struct v110_ta_state *) fi->priv; + + ts->tx.d_bit_mode = V110_TA_TX_FRAME_FROM_DTE; + + /* 7.1.3.1 a): 105, 107, 108/1, 108/2 and 109 are in the ON condition */ + V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_105); + V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_107); + V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_108_1); + V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_108_2); + V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_109); + /* 7.1.3.1 c): 133 (when implemented) and 106 are in the ON condition unless local out-of-band + flow control is being used, either or both circuits may be in the ON or the OFF condition. */ + if (!ts->cfg.end_to_end_flowctrl) { + V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_133); + V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_106); + } + v24_flags_updated(fi); + /* 7.1.3.2 While in the data transfer state, the following status bit conditions exist: */ + /* a) status bits S in both directions are in the ON condition; */ + ts->tx.s_bits = true; + /* b) status bits X in both directions are in the ON condition unless end-to-end flow control is + being used, in which case status bit X in either or both directions may be in the ON or the OFF + condition. */ + if (!ts->cfg.end_to_end_flowctrl) { + ts->tx.x_bits = true; + } +} + +/* ITU-t V.110 Section 7.1.3 */ +static void v110fsm_ta_data_transfer(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct v110_ta_state *ts = (struct v110_ta_state *) fi->priv; + struct osmo_v110_decoded_frame *fr = NULL; + + switch (event) { + case V110_TA_E_RX_V24_STATUS_CHG: + /* 7.1.4.1 At the completion of the data transfer phase, the local DTE will indicate a + * disconnect request by turning OFF circuit 108 */ + if (V24_FLAGMASK_IS_OFF(ts->v24_flags, OSMO_V24_C_108_2)) { + /* a) the status bits S in the frame toward ISDN will turn OFF, status bits X are kept ON */ + ts->tx.s_bits = false; + /* b) circuit 106 will be turned OFF */ + V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V24_C_106); + v24_flags_updated(fi); + /* c) the data bits in the frame will be set to binary 0. */ + ts->tx.d_bit_mode = V110_TA_TX_FRAME_ALL_ZERO; + /* to guard against the failure of the remote TA to respond to the disconnect request, + * the local TA may start a timer T2 (suggested value 5 s) which is stopped by the + * reception or transmission of any D-channel clearing message (DISCONNECT, RELEASE, + * RELEASE COMPLETE) */ + osmo_fsm_inst_state_chg(fi, V110_TA_S_WAIT_DISC_CONF, 5, 2); + } + break; + case V110_TA_E_TX_FRAME_RTS: + fr = data; + v110_ta_build_frame(fr, fi); + break; + case V110_TA_E_RX_FRAME_IND: + fr = data; + rc = osmo_v110_sync_ra1_ir_to_user(ts->cfg.rate, d_out, out_len, fr); + break; + default: + OSMO_ASSERT(0); + } +} + +static int v110_ta_timer_cb(struct osmo_fsm_inst *fi) +{ + switch (fi->T) { + case 1: /* T1: wait for sync pattern */ + break; + case 2: /* T2: wait for response to disconnect */ + break; + } +} + +static const struct osmo_fsm_state v110_ta_states[] = { + [V110_TA_S_IDLE_READY] = { + .name = "IDLE_READY", + .in_event_mask = S(V110_TA_E_TX_FRAME_RTS), + .out_state_mask = S(V110_TA_S_CON_TA_LINE), + .action = v110fsm_ta_idle_ready, + .ontenter = v110fsm_ta_idle_ready_onenter, + }, + [V110_TA_S_CON_TA_LINE] = { + .name = "CONNECT_TA_TO_LINE", + .in_event_mask = S(V110_TA_E_TX_FRAME_RTS), + .out_state_mask = S(V110_TA_S_IDLE_READY) | + S(V110_TA_S_DATA_TRANSFER), + .action = v110fsm_ta_connect_ta_to_line, + .ontenter = v110fsm_ta_connect_ta_to_line_onenter, + }, + [V110_TA_S_DATA_TRANSFER] = { + .name = "DATA_TRANSFER", + .in_event_mask = , + .out_state_mask = , + .action = v110fsm_ta_data_transfer, + .onenter = v110fsm_ta_data_transfer_onenter, + }, +}; + +static struct osmo_fsm osmo_v110_ta_fsm = { + .name = "V110-TA", + .states = v110_ta_states, + .num_states = ARRAY_SIZE(v110_ta_states), + .allstate_event_mask = FIXME, + .allstate_action = FIXME, + .timer_cb = v110_ta_timer_cb, + .log_subsys = FIXME, + .event_names = v110_ta_event_names, +};