/* * (C) 2017-2018 by Vadim Yanitskiy * (C) 2022-2024 by sysmocom - s.f.m.c. GmbH * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Forward a Downlink voice frame to the external MNCC handler */ static int tch_forward_mncc(struct osmocom_ms *ms, struct msgb *msg) { struct gsm_data_frame *mncc; /* Drop the l1ctl_info_dl header */ msgb_pull_to_l2(msg); /* push mncc header in front of data */ mncc = (struct gsm_data_frame *) msgb_push(msg, sizeof(struct gsm_data_frame)); mncc->callref = ms->mncc_entity.ref; switch (ms->rrlayer.cd_now.mode) { case GSM48_CMODE_SPEECH_V1: { const uint8_t cbits = ms->rrlayer.cd_now.chan_nr >> 3; if (cbits == ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs) mncc->msg_type = GSM_TCHF_FRAME; else mncc->msg_type = GSM_TCHH_FRAME; break; } case GSM48_CMODE_SPEECH_EFR: mncc->msg_type = GSM_TCHF_FRAME_EFR; break; case GSM48_CMODE_SPEECH_AMR: /* TODO: no AMR support yet */ default: /* TODO: print error message here */ goto exit_free; } /* distribute and then free */ if (ms->mncc_entity.sock_state && ms->mncc_entity.ref) return mncc_sock_from_cc(ms->mncc_entity.sock_state, msg); exit_free: msgb_free(msg); return 0; } int tch_voice_recv(struct osmocom_ms *ms, struct msgb *msg) { struct tch_voice_state *state = &ms->tch_state->voice; switch (state->handler) { case TCH_VOICE_IOH_LOOPBACK: /* Remove the DL info header */ msgb_pull_to_l2(msg); /* Send voice frame back */ return tch_send_msg(ms, msg); case TCH_VOICE_IOH_MNCC_SOCK: return tch_forward_mncc(ms, msg); case TCH_VOICE_IOH_GAPK: #ifdef WITH_GAPK_IO if (state->gapk_io != NULL) { gapk_io_enqueue_dl(state->gapk_io, msg); gapk_io_dequeue_ul(ms, state->gapk_io); } else { msgb_free(msg); } break; #endif case TCH_VOICE_IOH_L1PHY: case TCH_VOICE_IOH_NONE: /* Drop voice frame */ msgb_free(msg); break; } return 0; } int tch_voice_state_init(struct gsm_trans *trans, struct tch_voice_state *state) { struct osmocom_ms *ms = trans->ms; struct gsm48_rrlayer *rr = &ms->rrlayer; const struct gsm48_rr_cd *cd = &rr->cd_now; switch (state->handler) { case TCH_VOICE_IOH_L1PHY: rr->audio_mode = AUDIO_RX_SPEAKER | AUDIO_TX_MICROPHONE; break; case TCH_VOICE_IOH_MNCC_SOCK: case TCH_VOICE_IOH_LOOPBACK: rr->audio_mode = AUDIO_RX_TRAFFIC_IND | AUDIO_TX_TRAFFIC_REQ; break; case TCH_VOICE_IOH_GAPK: #ifdef WITH_GAPK_IO if ((cd->chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_Bm_ACCHs) state->gapk_io = gapk_io_state_alloc_mode_rate(ms, cd->mode, true); else /* RSL_CHAN_Lm_ACCHs */ state->gapk_io = gapk_io_state_alloc_mode_rate(ms, cd->mode, false); if (state->gapk_io == NULL) return -1; rr->audio_mode = AUDIO_RX_TRAFFIC_IND | AUDIO_TX_TRAFFIC_REQ; break; #endif case TCH_VOICE_IOH_NONE: rr->audio_mode = 0x00; break; } return 0; } void tch_voice_state_free(struct tch_voice_state *state) { switch (state->handler) { #ifdef WITH_GAPK_IO case TCH_VOICE_IOH_GAPK: gapk_io_state_free(state->gapk_io); break; #endif default: break; } }