From 619cb8f38b88a8f7e01e51bc39fb387e86e7b0b4 Mon Sep 17 00:00:00 2001 From: Guillaume Knispel Date: Tue, 19 Oct 2010 14:32:37 +0200 Subject: added a new (very very low quality way) to propagate audio --- tdm/xivo_tdm.c | 317 ++++++++++++++++++++++++++++++++++++++++------------- tdm/xivo_tdm_api.h | 30 ++++- 2 files changed, 267 insertions(+), 80 deletions(-) (limited to 'tdm') diff --git a/tdm/xivo_tdm.c b/tdm/xivo_tdm.c index 1424058..2f96b5a 100644 --- a/tdm/xivo_tdm.c +++ b/tdm/xivo_tdm.c @@ -30,8 +30,16 @@ MODULE_LICENSE("GPL"); typedef unsigned char bool; enum { false = 0, true = 1 }; -/* fixed for now: */ -#define TS_NUM 32 +struct xivo_tdm_port; + +struct xivo_tdm_cb_chan { + struct xivo_tdm_port *xtp; + unsigned int hss_chan_id; + unsigned int timeslot; + u8 *rx_buf; /* 8 bytes */ + u8 *tx_buf; /* 8 bytes */ + int must_start_tx; +}; struct xivo_tdm_port { int port_num; @@ -39,7 +47,7 @@ struct xivo_tdm_port { u32 ts_allocated; u32 ts_started; - unsigned int hss_chan_ids[TS_NUM]; + unsigned int hss_chan_ids[XIVO_TDM_TS_NUM]; spinlock_t lock; struct work_struct work; @@ -47,8 +55,16 @@ struct xivo_tdm_port { /* WARNING: this file is a big hack, coherency of following chunks is * not guaranteed. Blame Intel for their shitty nearly unusable code. */ - u8 tx_bufs[TS_NUM][8]; - u8 rx_bufs[TS_NUM][8]; + u8 tx_bufs[XIVO_TDM_TS_NUM][8]; + u8 rx_bufs[XIVO_TDM_TS_NUM][8]; + + struct xivo_tdm_cb_chan cb_chans[XIVO_TDM_TS_NUM]; + + int cb_cnum_trigger; + void (*cb)(void *data); + void *cb_data; + + unsigned tx_errs; }; static struct xivo_tdm_port xivo_tdm_ports[MIN_HSS_PORTS_WANTED]; @@ -169,14 +185,149 @@ static int add_one_chan( return 0; } +static int xivo_tdm_common_rx( + struct xivo_tdm_port* xtp, + const unsigned int cnum, + u8 *rx_buf) +{ + IX_OSAL_MBUF *osal_buf = NULL; + icp_status_t status; + int nb_read = 0; + const unsigned int hss_chan_id = xtp->hss_chan_ids[cnum]; + + while (1) { + char *data_buf; + status = icp_HssAccReceive(hss_chan_id, &osal_buf); + if (status != ICP_STATUS_SUCCESS || osal_buf == NULL) + return nb_read; + nb_read++; + data_buf = IX_OSAL_MBUF_MDATA(osal_buf); + if (likely(rx_buf)) + memcpy(rx_buf, data_buf, 8 /* XXX is this right? */ ); + IX_OSAL_MBUF_MLEN(osal_buf) = IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(osal_buf); + status = icp_HssAccRxFreeReplenish(ICP_HSSACC_CHAN_TYPE_VOICE, osal_buf); + if (status != ICP_STATUS_SUCCESS) { + if (printk_ratelimit()) + printk(KERN_ERR "%s: icp_HssAccRxFreeReplenish " + "err %d cnum %u\n", + __func__, (int) status, cnum); + free_osal_buf(osal_buf); + } + osal_buf = NULL; + } +} + +static void xivo_tdm_common_tx( + struct xivo_tdm_port* xtp, + const unsigned int cnum, + u8 *tx_buf) +{ + IX_OSAL_MBUF *osal_buf = NULL; + icp_status_t status; + const unsigned int hss_chan_id = xtp->hss_chan_ids[cnum]; + + status = icp_HssAccTxDoneRetrieve(hss_chan_id, &osal_buf); + if (status != ICP_STATUS_SUCCESS) { + if (status == ICP_STATUS_UNDERFLOW) { + osal_buf = alloc_init_osal_buf(TDM_SAMPLE_SIZE_FOR_DAHDI); + if (!osal_buf && printk_ratelimit()) + printk(KERN_ERR + "%s: osal_buf alloc failed - cnum %u\n", + __func__, cnum); + } else { + osal_buf = NULL; + if (printk_ratelimit()) + printk(KERN_ERR "%s: icp_HssAccTxDoneRetrieve " + "err %d cnum %u\n", + __func__, (int) status, cnum); + } + } + + if (!osal_buf) + return; + + if (likely(tx_buf)) + memcpy(IX_OSAL_MBUF_MDATA(osal_buf), tx_buf, 8); + IX_OSAL_MBUF_MLEN(osal_buf) = 8; + IX_OSAL_MBUF_PKT_LEN(osal_buf) = 8; + + status = icp_HssAccTransmit(hss_chan_id, osal_buf); + if (status != ICP_STATUS_SUCCESS) { + xtp->tx_errs++; + if (printk_ratelimit()) + printk("%s%s: icp_HssAccTransmit err %d cnum %u " + "tx_errs %u\n", + status == ICP_STATUS_OVERFLOW ? + KERN_DEBUG : + KERN_ERR, + __func__, (int) status, cnum, xtp->tx_errs); + free_osal_buf(osal_buf); + if (status != ICP_STATUS_OVERFLOW) + xtp->cb_chans[cnum].must_start_tx = 1; + } else + xtp->cb_chans[cnum].must_start_tx = 0; +} + +static void xivo_tdm_rx_cb(icp_user_context_t userContext) +{ + struct xivo_tdm_cb_chan *cb_chan = + (struct xivo_tdm_cb_chan *) userContext; + struct xivo_tdm_port *xtp = cb_chan->xtp; + unsigned int cnum; + + if (xtp->cb_cnum_trigger == cb_chan->timeslot) + if (likely(xtp->cb)) + xtp->cb(xtp->cb_data); + + cnum = cb_chan->timeslot; + + if (unlikely(cb_chan->must_start_tx)) + xivo_tdm_common_tx(xtp, cnum, cb_chan->tx_buf); + + (void) xivo_tdm_common_rx(xtp, cnum, cb_chan->rx_buf); +} + +static void xivo_tdm_tx_cb(icp_user_context_t userContext) +{ + struct xivo_tdm_cb_chan *cb_chan = + (struct xivo_tdm_cb_chan *) userContext; + + xivo_tdm_common_tx(cb_chan->xtp, cb_chan->timeslot, cb_chan->tx_buf); +} + static int start_one_chan( struct xivo_tdm_port* xtp, - const int cnum) + const int cnum, + struct xivo_tdm_cb_struct *cb_struct) { const unsigned int hss_chan_id = xtp->hss_chan_ids[cnum]; unsigned long flags; + icp_status_t status; + + if (cb_struct) { + struct xivo_tdm_cb_chan *cb_chan = &(xtp->cb_chans[cnum]); + + cb_chan->hss_chan_id = hss_chan_id; + cb_chan->rx_buf = cb_struct->rx_bufs[cnum]; + cb_chan->tx_buf = cb_struct->tx_bufs[cnum]; + + status = icp_HssAccChannelCallbacksRegister( + hss_chan_id, + (icp_user_context_t) cb_chan, + xivo_tdm_rx_cb, + xivo_tdm_tx_cb); + if (status != ICP_STATUS_SUCCESS) { + printk(KERN_ERR "%s: icp_HssAccChannelCallbacksRegister " + "returned error %d \n", + __func__, status); + cb_chan->hss_chan_id = 0; + cb_chan->rx_buf = NULL; + cb_chan->tx_buf = NULL; + return -EIO; + } + } - icp_status_t status = icp_HssAccChannelUp(hss_chan_id); + status = icp_HssAccChannelUp(hss_chan_id); if (status != ICP_STATUS_SUCCESS) { printk(KERN_ERR "%s: icp_HssAccChannelUp returned error %d \n", __func__, status); @@ -184,13 +335,36 @@ static int start_one_chan( } spin_lock_irqsave(&xtp->lock, flags); - /* WARNING: only protect startup, not shutdown! */ xtp->ts_started |= (1u << cnum); spin_unlock_irqrestore(&xtp->lock, flags); return 0; } +static int stop_one_chan( + struct xivo_tdm_port* xtp, + const int cnum) +{ + icp_status_t status; + unsigned int hss_chan_id = xtp->hss_chan_ids[cnum]; + unsigned long flags; + + spin_lock_irqsave(&xtp->lock, flags); + xtp->ts_started &= ~(1u << cnum); + spin_unlock_irqrestore(&xtp->lock, flags); + + flush_workqueue(xivo_tdm_wq); + + status = icp_HssAccChannelDown(hss_chan_id); + if (status != ICP_STATUS_SUCCESS) { + printk(KERN_ERR "%s: icp_HssAccChannelDown returned %d\n", + __func__, (int) status); + return -EIO; + } + + return 0; +} + static void xivo_hss_port_error_cb( icp_user_context_t user_context, icp_hssacc_port_error_t error_type) @@ -270,80 +444,16 @@ static int xivo_tdm_deferred_receive( struct xivo_tdm_port* xtp, const unsigned int cnum) { - IX_OSAL_MBUF *osal_buf = NULL; - icp_status_t status; - int nb_read = 0; - const unsigned int hss_chan_id = xtp->hss_chan_ids[cnum]; u8 *rx_buf = xtp->rx_bufs[cnum]; - - while (1) { - char *data_buf; - status = icp_HssAccReceive(hss_chan_id, &osal_buf); - if (status != ICP_STATUS_SUCCESS || osal_buf == NULL) - return nb_read; - nb_read++; - data_buf = IX_OSAL_MBUF_MDATA(osal_buf); - memcpy(rx_buf, data_buf, 8 /* XXX is this right? */ ); - IX_OSAL_MBUF_MLEN(osal_buf) = IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(osal_buf); - status = icp_HssAccRxFreeReplenish(ICP_HSSACC_CHAN_TYPE_VOICE, osal_buf); - if (status != ICP_STATUS_SUCCESS) { - if (printk_ratelimit()) - printk(KERN_ERR "%s: icp_HssAccRxFreeReplenish " - "err %d cnum %u\n", - __func__, (int) status, cnum); - free_osal_buf(osal_buf); - } - osal_buf = NULL; - } + return xivo_tdm_common_rx(xtp, cnum, rx_buf); } static void xivo_tdm_deferred_transmit( struct xivo_tdm_port* xtp, const unsigned int cnum) { - static unsigned tx_errs = 0; - - IX_OSAL_MBUF *osal_buf = NULL; - icp_status_t status; - const unsigned int hss_chan_id = xtp->hss_chan_ids[cnum]; u8 *tx_buf = xtp->tx_bufs[cnum]; - - status = icp_HssAccTxDoneRetrieve(hss_chan_id, &osal_buf); - if (status != ICP_STATUS_SUCCESS) { - if (status == ICP_STATUS_UNDERFLOW) { - osal_buf = alloc_init_osal_buf(TDM_SAMPLE_SIZE_FOR_DAHDI); - if (!osal_buf && printk_ratelimit()) - printk(KERN_ERR - "%s: osal_buf alloc failed - cnum %u\n", - __func__, cnum); - } else { - osal_buf = NULL; - if (printk_ratelimit()) - printk(KERN_ERR "%s: icp_HssAccTxDoneRetrieve " - "err %d cnum %u\n", - __func__, (int) status, cnum); - } - } - - if (!osal_buf) - return; - - memcpy(IX_OSAL_MBUF_MDATA(osal_buf), tx_buf, 8); - IX_OSAL_MBUF_MLEN(osal_buf) = 8; - IX_OSAL_MBUF_PKT_LEN(osal_buf) = 8; - - status = icp_HssAccTransmit(hss_chan_id, osal_buf); - if (status != ICP_STATUS_SUCCESS) { - tx_errs++; - if (printk_ratelimit()) - printk("%s%s: icp_HssAccTransmit err %d cnum %u " - "tx_errs %u\n", - status == ICP_STATUS_OVERFLOW ? - KERN_DEBUG : - KERN_ERR, - __func__, (int) status, cnum, tx_errs); - free_osal_buf(osal_buf); - } + xivo_tdm_common_tx(xtp, cnum, tx_buf); } static void xivo_tdm_work(void *arg) @@ -419,6 +529,7 @@ void xivo_tdm_put_port(struct xivo_tdm_port *xtp) "allocated\n", __func__, idx); } else { + xivo_tdm_stop_chans(xtp); xtp->allocated = false; } up(&xivo_tdm_mutex); @@ -474,7 +585,8 @@ EXPORT_SYMBOL(xivo_tdm_config_port); int xivo_tdm_start_chans( struct xivo_tdm_port* xtp, - const u32 chans) + const u32 chans, + struct xivo_tdm_cb_struct *cb_struct) { u32 scan; int cnum; @@ -487,11 +599,21 @@ int xivo_tdm_start_chans( } } + if (cb_struct) { + xtp->cb = cb_struct->xivo_tdm_cb; + xtp->cb_data = cb_struct->xivo_tdm_cb_data; + } + for (cnum = 0, scan = 1; scan; scan <<= 1, cnum++) { if (scan & chans) { printk(KERN_INFO "%s: starting chan %d\n", __func__, cnum); - /* XXX retval */ (void) start_one_chan(xtp, cnum); + if (cb_struct) { + if (xtp->cb_cnum_trigger < 0) + xtp->cb_cnum_trigger = cnum; + } + /* XXX retval */ (void) start_one_chan( + xtp, cnum, cb_struct); } } @@ -501,6 +623,28 @@ int xivo_tdm_start_chans( } EXPORT_SYMBOL(xivo_tdm_start_chans); +void xivo_tdm_stop_chans( + struct xivo_tdm_port* xtp) +{ + u32 scan; + int cnum; + for (cnum = 0, scan = 1; scan; scan <<= 1, cnum++) { + if (scan & xtp->ts_started) { + printk(KERN_INFO "%s: stopping chan %d\n", + __func__, cnum); + (void) stop_one_chan(xtp, cnum); + } + } + for (cnum = 0, scan = 1; scan; scan <<= 1, cnum++) { + if (scan & xtp->ts_allocated) { + printk(KERN_INFO "%s: removing chan %d\n", + __func__, cnum); + (void) dealloc_one_chan(xtp, cnum); + } + } +} +EXPORT_SYMBOL(xivo_tdm_stop_chans); + void xivo_tdm_receive( struct xivo_tdm_port* xtp, const unsigned int cnum, @@ -532,6 +676,7 @@ EXPORT_SYMBOL(xivo_tdm_tick); static void xivo_internal_cleanup(void) { if (xivo_tdm_wq) { + /* XXX we should prevent queueing here */ destroy_workqueue(xivo_tdm_wq); xivo_tdm_wq = NULL; } @@ -539,7 +684,7 @@ static void xivo_internal_cleanup(void) static int __init xivo_internal_init(void) { - int i; + int i, ts; xivo_tdm_wq = create_singlethread_workqueue("xivo_tdm"); if (!xivo_tdm_wq) { @@ -566,6 +711,24 @@ static int __init xivo_internal_init(void) sizeof xivo_tdm_ports[i].tx_bufs); memset(xivo_tdm_ports[i].rx_bufs, 0xD5, sizeof xivo_tdm_ports[i].rx_bufs); + + for (ts = 0; + ts < (int)ARRAY_SIZE(xivo_tdm_ports[i].cb_chans); + ts++) { + struct xivo_tdm_cb_chan *cb_chan = &(xivo_tdm_ports[i].cb_chans[ts]); + cb_chan->xtp = &xivo_tdm_ports[i]; + cb_chan->hss_chan_id = 0; + cb_chan->timeslot = ts; + cb_chan->rx_buf = NULL; + cb_chan->tx_buf = NULL; + cb_chan->must_start_tx = 1; + } + + xivo_tdm_ports[i].cb_cnum_trigger = -1; + xivo_tdm_ports[i].cb = NULL; + xivo_tdm_ports[i].cb_data = NULL; + + xivo_tdm_ports[i].tx_errs = 0; } return 0; diff --git a/tdm/xivo_tdm_api.h b/tdm/xivo_tdm_api.h index 13e2889..cde2269 100644 --- a/tdm/xivo_tdm_api.h +++ b/tdm/xivo_tdm_api.h @@ -2,13 +2,28 @@ #define XIVO_TDM_API_H /* XXX matches with ICP_HSSDRV_PORT_XHFC_MEGREZ_PROTO_XIVO_CONFIG */ -#define XHFC_MEGREZ_PROTO_XIVO_CONFIG 4 +#define XHFC_MEGREZ_PROTO_XIVO_CONFIG (4) /* XXX matches with ICP_HSSDRV_PORT_LE89316_MEGREZ_PROTO_XIVO_CONFIG */ -#define LE89316_MEGREZ_PROTO_XIVO_CONFIG 5 +#define LE89316_MEGREZ_PROTO_XIVO_CONFIG (5) + +/* fixed for now: */ +#define XIVO_TDM_TS_NUM (32) + + +/* for audio propagation, xivo_tdm_cb will be called every ms (hopefully...) */ +struct xivo_tdm_cb_struct { + u8 *rx_bufs[XIVO_TDM_TS_NUM]; + u8 *tx_bufs[XIVO_TDM_TS_NUM]; + + void (*xivo_tdm_cb)(void *data); + void *xivo_tdm_cb_data; +}; + struct xivo_tdm_port; + /* TDM port allocation * Returns a xivo_tdm_port pointer if available, else NULL. */ /* hardirq: no -- softirq: no -- user: yes */ @@ -27,7 +42,16 @@ int xivo_tdm_config_port( /* hardirq: no -- softirq: no -- user: yes */ int xivo_tdm_start_chans( struct xivo_tdm_port* xtp, - u32 chans); + u32 chans, + struct xivo_tdm_cb_struct *cb_struct); // optional, can be null + +void xivo_tdm_stop_chans( + struct xivo_tdm_port* xtp); + + +/* If cb_struct is NULL, the user has to regularly call + * xivo_tdm_receive(), xivo_tdm_transmit() and xivo_tdm_tick() + */ /* hardirq: yes -- softirq: yes -- user: yes */ void xivo_tdm_receive( -- cgit v1.2.3