/* soft-modem B-channel based on spandsp library */ #include #include #include #include #include #include #include #include #include #include #include #include "bchan.h" #include "errno.h" /*********************************************************************** * spandsp callbacks ***********************************************************************/ static int cb_at_tx_handler(void *user_data, const uint8_t *buf, size_t len) { /* write to stdout for now */ return write(1, buf, len); } static int cb_modem_ctrl_hdlr(data_modems_state_t *ms, void *user_data, int op, const char *num) { printf("MODEM CONTROL: %s\n", at_modem_control_to_str(op)); switch (op) { case AT_MODEM_CONTROL_CALL: data_modems_call_event(ms, AT_CALL_EVENT_CONNECTED); break; case AT_MODEM_CONTROL_ANSWER: data_modems_call_event(ms, AT_CALL_EVENT_ANSWERED); break; } return 0; } static void cb_put_msg(void *user_data, const uint8_t msg[], int len) { if (len < 0) printf("Status %s\n", signal_status_to_str(len)); else printf("Put %d '%s'\n", len, msg); } static int cb_get_msg(void *user_data, uint8_t msg[], int len) { return 0; } /*********************************************************************** * bchan integration ***********************************************************************/ struct spandsp_priv { data_modems_state_t *ms; g711_state_t *g711; }; static int spandsp_init(struct call_state *cst) { struct spandsp_priv *rpp; logging_state_t *logging; rpp = cst->priv = talloc_zero(cst, struct spandsp_priv); if (!rpp) return -ENOMEM; rpp->ms = data_modems_init(NULL, false, cb_at_tx_handler, NULL, cb_modem_ctrl_hdlr, NULL, cb_put_msg, cb_get_msg, rpp); OSMO_ASSERT(rpp->ms); logging = data_modems_get_logging_state(rpp->ms); span_log_set_level(logging, SPAN_LOG_DEBUG | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME); span_log_set_tag(logging, "modem"); /* this is the default, but calling it now applies above-mentioned log level */ data_modems_set_modem_type(rpp->ms, DATA_MODEM_V8, 0, 0); //data_modems_set_modem_type(rpp->ms, DATA_MODEM_BELL103, 0, 0); rpp->g711 = g711_init(NULL, G711_ALAW); data_modems_call_event(rpp->ms, AT_CALL_EVENT_ANSWERED); return 0; } static void spandsp_rx(struct call_state *cst, const uint8_t *data, size_t len) { struct spandsp_priv *rpp = cst->priv; int16_t s16_buf[len]; uint8_t tx_buf[len]; int rc; /* mISDN/CAPI bit order != spandsp order */ osmo_revbytebits_buf((uint8_t *)data, len); /* A-law -> S16 */ rc = g711_decode(rpp->g711, s16_buf, data, len); OSMO_ASSERT(rc == len); rc = data_modems_rx(rpp->ms, s16_buf, len); OSMO_ASSERT(rc == 0); /* generate respective number of output samples and transmit them */ rc = data_modems_tx(rpp->ms, s16_buf, len); OSMO_ASSERT(rc == len); /* A-law <- S16 */ rc = g711_encode(rpp->g711, tx_buf, s16_buf, len); OSMO_ASSERT(rc == len); /* mISDN/CAPI bit order != spandsp order */ osmo_revbytebits_buf(tx_buf, len); bchan_call_tx(cst, tx_buf, len); } static void spandsp_fini(struct call_state *cst) { struct spandsp_priv *rpp = cst->priv; g711_free(rpp->g711); data_modems_free(rpp->ms); talloc_free(rpp); cst->priv = NULL; } static void spandsp_log_cb(void *user_data, int level, const char *text) { LOGP(DCAPI, LOGL_INFO, text); } static struct bchan_handler bch_spandsp = { .name = "spandsp", .cfg = { .proto = { CAPI_B1_64k_TRANSPARENT, CAPI_B2_TRANSPARENT, CAPI_B3_TRANSPARENT }, .ncpi = NULL, .max_b_data_blocks = 10, .max_b_data_len = 32, }, .ops = { .init = spandsp_init, .rx_data = spandsp_rx, .fini = spandsp_fini, }, }; static void *span_alloc_cb(size_t size) { return talloc_size(g_ctx, size); } static void span_free_cb(void *ptr) { talloc_free(ptr); } static __attribute__((constructor)) void hdlr_spandsp_init(void) { /* memory allocator */ span_mem_allocators(span_alloc_cb, NULL, span_free_cb, NULL, NULL); /* logging */ //span_set_message_handler(spandsp_log_cb, NULL); bchan_handler_register(&bch_spandsp); }