summaryrefslogtreecommitdiff
path: root/tdm
diff options
context:
space:
mode:
Diffstat (limited to 'tdm')
-rw-r--r--tdm/xivo_tdm.c317
-rw-r--r--tdm/xivo_tdm_api.h30
2 files changed, 267 insertions, 80 deletions
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(