summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tdm/Makefile29
-rw-r--r--tdm/xivo_tdm.c659
-rw-r--r--tdm/xivo_tdm_api.h45
-rw-r--r--xhfc/Makefile2
-rw-r--r--xhfc/xhfc.c74
-rw-r--r--xhfc/xhfc.h17
6 files changed, 817 insertions, 9 deletions
diff --git a/tdm/Makefile b/tdm/Makefile
new file mode 100644
index 0000000..7393f89
--- /dev/null
+++ b/tdm/Makefile
@@ -0,0 +1,29 @@
+PWD := $(shell pwd)
+
+KSRC ?= /bad__ksrc__not_set
+ICP_ROOT ?= /bad__icp_root___not_set
+
+include $(ICP_ROOT)/build_system/build_files/includes.mk
+
+# XXX This is an ugly evil hack, but this is probably the only way to build
+# against Intel code without killing myself because of depression induced by
+# their overcomplicated makefile mess (which BTW is buggy anyway, and has most
+# probably been written under influence of much illegal drugs -- i don't have
+# that kind of drugs available)
+CFLAGS_MODULE += -O2 -DENABLE_IOMEM -DENABLE_BUFFERMGT
+
+CFLAGS_MODULE += $(INCLUDES)
+
+CFLAGS_MODULE += -DEP805XX -D__ep805xx -DTOLAPAI -D__tolapai \
+ -DIX_HW_COHERENT_MEMORY=1
+
+
+obj-m := xivo_tdm.o
+
+modules:
+
+modules modules_install clean:
+ $(MAKE) -C $(KSRC) M=$(PWD) $@
+
+distclean: clean
+ rm -f Module.symvers
diff --git a/tdm/xivo_tdm.c b/tdm/xivo_tdm.c
new file mode 100644
index 0000000..00cfae9
--- /dev/null
+++ b/tdm/xivo_tdm.c
@@ -0,0 +1,659 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#include <IxOsal.h>
+#include <icp.h>
+#include <icp_hssdrv_common.h>
+
+#include "xivo_tdm_api.h"
+
+
+MODULE_LICENSE("GPL");
+
+
+#define MIN_HSS_PORTS_WANTED (2)
+#define MIN_HSS_CHANS_WANTED (10)
+
+#define TDM_SAMPLE_SIZE_FOR_DAHDI (8)
+
+#define RX_BUF_NUM (2048)
+
+/* for Linux < 2.6.19; WARNING: DO NOT USE IN INTERFACES IN THIS CASE!
+ * (no ifdef so that this warning can be respected when porting
+ * to modern kernel) thanks to a clash between this definition of 'bool'
+ * and those of Linux
+ */
+typedef unsigned char bool;
+enum { false = 0, true = 1 };
+
+/* fixed for now: */
+#define TS_NUM 32
+
+struct xivo_tdm_port {
+ int port_num;
+ bool allocated;
+
+ u32 ts_allocated;
+ u32 ts_started;
+ unsigned int hss_chan_ids[TS_NUM];
+
+ spinlock_t lock;
+ struct work_struct work;
+
+ /* 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];
+};
+
+static struct xivo_tdm_port xivo_tdm_ports[MIN_HSS_PORTS_WANTED];
+
+static DECLARE_MUTEX(xivo_tdm_mutex);
+static struct workqueue_struct *xivo_tdm_wq;
+
+/* INTERNAL FUNCS */
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! USER CONTEXT ONLY !!!!!!!!!!!!!!!! */
+static IX_OSAL_MBUF *alloc_init_osal_buf(
+ unsigned int data_buf_length)
+{
+ IX_OSAL_MBUF *osal_buf;
+ char *data_buf;
+
+ osal_buf = kmalloc(sizeof(IX_OSAL_MBUF), GFP_KERNEL);
+ if (!osal_buf)
+ return NULL;
+
+ data_buf = kmalloc(data_buf_length, GFP_KERNEL);
+ if (!data_buf) {
+ kfree(osal_buf);
+ return NULL;
+ }
+
+ IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(osal_buf) = data_buf_length;
+
+ /*Fill in the allocated size of the data buf */
+ IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(osal_buf) = data_buf_length;
+ IX_OSAL_MBUF_MLEN(osal_buf) = data_buf_length;
+
+ IX_OSAL_MBUF_NEXT_PKT_IN_CHAIN_PTR(osal_buf) = NULL;
+ IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(osal_buf) = NULL;
+
+ /*Attach the data buf to the OSAL buf */
+ IX_OSAL_MBUF_MDATA(osal_buf) = data_buf;
+
+ return osal_buf;
+}
+
+static void free_osal_buf(
+ IX_OSAL_MBUF *osal_buf)
+{
+ if (osal_buf) {
+ char *data_buf = IX_OSAL_MBUF_MDATA(osal_buf);
+ kfree(data_buf);
+
+ kfree(osal_buf);
+ }
+}
+
+static void dealloc_one_chan(
+ struct xivo_tdm_port* xtp,
+ const int cnum)
+{
+ icp_status_t status = icp_HssAccChannelDelete(xtp->hss_chan_ids[cnum]);
+ if (status != ICP_STATUS_SUCCESS)
+ printk(KERN_ERR "%s: icp_HssAccChannelDelete returned "
+ "error %d\n", __func__, (int) status);
+ xtp->ts_allocated &= ~(1u << cnum);
+ xtp->hss_chan_ids[cnum] = 0;
+}
+
+static int add_one_chan(
+ struct xivo_tdm_port* xtp,
+ const int cnum)
+{
+ icp_status_t status;
+ icp_hssacc_timeslot_map_t ts_map = {0};
+
+ if (xtp->ts_allocated & (1u << cnum)) {
+ printk(KERN_ERR "%s: EVIL BUG, DIE!\n", __func__);
+ return -42;
+ }
+
+ ts_map.line0_timeslot_bit_map = (1u << cnum);
+
+ /*Allocate the channel */
+ status = icp_HssAccChannelAllocate(
+ &xtp->hss_chan_ids[cnum],
+ xtp->port_num,
+ ts_map,
+ ICP_HSSACC_CHAN_TYPE_VOICE);
+ if (status != ICP_STATUS_SUCCESS) {
+ printk(KERN_ERR "%s: icp_HssAccChannelAllocate returned "
+ "error %d\n", __func__, (int) status);
+ return -EIO;
+ }
+
+ status = icp_HssAccChannelConfigure(
+ xtp->hss_chan_ids[cnum],
+ /* channelDataInvert */ 0,
+ /* channelBitEndianness */ 1, /* MSb first */
+ /* channelByteSwap */ 0,
+ ICP_FALSE, ICP_FALSE, ICP_FALSE);
+ if (status != ICP_STATUS_SUCCESS) {
+ printk(KERN_ERR "%s: icp_HssAccChannelConfigure returned "
+ "error %d\n", __func__, (int) status);
+ dealloc_one_chan(xtp, cnum);
+ return -EIO;
+ }
+
+ status = icp_HssAccChannelVoiceServiceConfigure(
+ xtp->hss_chan_ids[cnum],
+ ICP_HSSACC_VOICE_TX_IDLE_REPEAT_LAST_FRAME,
+ 0xD5,
+ TDM_SAMPLE_SIZE_FOR_DAHDI);
+ if (status != ICP_STATUS_SUCCESS) {
+ printk(KERN_ERR "%s: icp_HssAccChannelVoiceServiceConfigure "
+ "returned error %d\n", __func__, (int) status);
+ dealloc_one_chan(xtp, cnum);
+ return -EIO;
+ }
+
+ xtp->ts_allocated |= (1u << cnum);
+
+ return 0;
+}
+
+static int start_one_chan(
+ struct xivo_tdm_port* xtp,
+ const int cnum)
+{
+ const unsigned int hss_chan_id = xtp->hss_chan_ids[cnum];
+ unsigned long flags;
+
+ icp_status_t status = icp_HssAccChannelUp(hss_chan_id);
+ if (status != ICP_STATUS_SUCCESS) {
+ printk(KERN_ERR "%s: icp_HssAccChannelUp returned error %d \n",
+ __func__, status);
+ return -EIO;
+ }
+
+ 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 void xivo_hss_port_error_cb(
+ icp_user_context_t user_context,
+ icp_hssacc_port_error_t error_type)
+{
+ struct xivo_tdm_port* xtp = (struct xivo_tdm_port*) user_context;
+ unsigned port_num = xtp->port_num;
+
+ switch (error_type) {
+ case ICP_HSSACC_PORT_ERROR_TX_LOS:
+ printk(KERN_ERR "%s - Port error for port %u :"
+ " Transmit loss of sync\n",
+ __func__, port_num);
+ break;
+ case ICP_HSSACC_PORT_ERROR_RX_LOS:
+ printk(KERN_ERR "%s - Port error for port %u :"
+ " Receive loss of sync\n",
+ __func__, port_num);
+ break;
+ case ICP_HSSACC_PORT_ERROR_TX_UNDERFLOW:
+ printk(KERN_ERR "%s - Port error for port %u :"
+ " Transmit underflow\n",
+ __func__, port_num);
+ break;
+ case ICP_HSSACC_PORT_ERROR_RX_OVERFLOW:
+ printk(KERN_ERR "%s - Port error for port %u :"
+ " Receive overflow\n",
+ __func__, port_num);
+ break;
+ case ICP_HSSACC_PORT_ERROR_TX_PARITY:
+ printk(KERN_ERR "%s - Port error for port %u :"
+ " Tx Parity Error\n",
+ __func__, port_num);
+ break;
+ case ICP_HSSACC_PORT_ERROR_RX_PARITY:
+ printk(KERN_ERR "%s - Port error for port %u :"
+ " Rx Parity Error\n",
+ __func__, port_num);
+ break;
+ default:
+ printk(KERN_ERR "%s - Port error for port %u :"
+ " Unidentified error %u\n",
+ __func__, port_num, (unsigned) error_type);
+ break;
+ }
+}
+
+static void xivo_hss_error_cb(
+ icp_user_context_t user_context,
+ icp_hssacc_error_t error_type)
+{
+ (void) user_context;
+
+ switch (error_type) {
+ case ICP_HSSACC_ERROR_RX_OVERFLOW:
+ printk(KERN_ERR "%s - Error: Receive Queue Overflow "
+ "reported\n",
+ __func__);
+ break;
+ case ICP_HSSACC_ERROR_RX_FREE_UNDERFLOW:
+ printk(KERN_ERR "%s - Error: Receive Free Queue Underflow "
+ " reported\n",
+ __func__);
+ break;
+ case ICP_HSSACC_ERROR_MESSAGE_FIFO_OVERFLOW:
+ printk(KERN_ERR "%s - Error: Messaging FIFO Overflow "
+ "reported\n",
+ __func__);
+ break;
+ default:
+ printk(KERN_ERR "%s - Unidentified Error %u\n",
+ __func__, (unsigned) error_type);
+ break;
+ }
+}
+
+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;
+ }
+}
+
+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(KERN_ERR
+ "%s: icp_HssAccTransmit err %d cnum %u "
+ "tx_errs %u\n",
+ __func__, (int) status, cnum, tx_errs);
+ free_osal_buf(osal_buf);
+ }
+}
+
+static void xivo_tdm_work(void *arg)
+{
+ struct xivo_tdm_port *xtp = arg;
+
+ unsigned long flags;
+ u32 ts_started;
+ u32 scan;
+ int cnum;
+
+ spin_lock_irqsave(&xtp->lock, flags);
+ ts_started = xtp->ts_started;
+ spin_unlock_irqrestore(&xtp->lock, flags);
+
+ for (cnum = 0, scan = 1; scan; scan <<= 1, cnum++) {
+ if (ts_started & scan) {
+ /* XXX use ret value: */
+ (void) xivo_tdm_deferred_receive(xtp, cnum);
+ xivo_tdm_deferred_transmit(xtp, cnum);
+ }
+ }
+}
+
+
+/* EXPORTED FUNCS */
+
+struct xivo_tdm_port *xivo_tdm_get_port(int tdm_port_num)
+{
+ struct xivo_tdm_port *xtp;
+
+ if (tdm_port_num < 0 || tdm_port_num >= MIN_HSS_PORTS_WANTED) {
+ printk(KERN_ERR "%s: attempt to allocate an out of range TDM "
+ "port %d\n", __func__, tdm_port_num);
+ return NULL;
+ }
+
+ down(&xivo_tdm_mutex);
+ if (xivo_tdm_ports[tdm_port_num].allocated) {
+ xtp = NULL;
+ printk(KERN_ERR "%s: attempt to allocate TDM port %d, which "
+ "is already allocated\n",
+ __func__, tdm_port_num);
+ } else {
+ xivo_tdm_ports[tdm_port_num].allocated = true;
+ xtp = &xivo_tdm_ports[tdm_port_num];
+ }
+ up(&xivo_tdm_mutex);
+
+ return xtp;
+}
+EXPORT_SYMBOL(xivo_tdm_get_port);
+
+void xivo_tdm_put_port(struct xivo_tdm_port *xtp)
+{
+ unsigned long xtp_addr = (unsigned long)xtp;
+
+ unsigned idx = (xtp_addr - (unsigned long)xivo_tdm_ports) / sizeof(*xtp);
+
+ if (xtp_addr == 0
+ || xtp_addr < (unsigned long)xivo_tdm_ports
+ || xtp_addr > (unsigned long)(xivo_tdm_ports + MIN_HSS_PORTS_WANTED - 1)
+ || xtp_addr != (unsigned long)(xivo_tdm_ports + idx)) {
+ printk(KERN_ERR "%s: attempt to free an invalid struct "
+ "xivo_tdm_port pointer: %p\n",
+ __func__, (void *) xtp);
+ return;
+ }
+
+ down(&xivo_tdm_mutex);
+ if (!xtp->allocated) {
+ printk(KERN_ERR "%s: attempt to free TDM port %u, which is not "
+ "allocated\n",
+ __func__, idx);
+ } else {
+ xtp->allocated = false;
+ }
+ up(&xivo_tdm_mutex);
+}
+EXPORT_SYMBOL(xivo_tdm_put_port);
+
+int xivo_tdm_config_port(
+ struct xivo_tdm_port* xtp,
+ unsigned int port_config)
+{
+ icp_status_t status;
+ icp_hssacc_port_config_params_t hss_port_config;
+
+ if (port_config >= ICP_HSSDRV_PORT_CONFIG_DELIMITER) {
+ printk(KERN_ERR "%s: invalid port config %u\n",
+ __func__, port_config);
+ return -EINVAL;
+ }
+
+ port_config_create(port_config,
+ ICP_HSSDRV_NO_LOOPBACK,
+ &hss_port_config);
+
+ status = icp_HssAccPortConfig(xtp->port_num, &hss_port_config);
+ if (status != ICP_STATUS_SUCCESS) {
+ printk(KERN_ERR "%s: icp_HssAccPortConfig returned error %d\n",
+ __func__, (int) status);
+ return -EIO;
+ }
+
+ status = icp_HssAccPortErrorCallbackRegister( /* XXX to unreg? */
+ xtp->port_num,
+ ICP_HSSACC_CHAN_TYPE_VOICE,
+ (icp_user_context_t) xtp,
+ xivo_hss_port_error_cb);
+ if (status != ICP_STATUS_SUCCESS) {
+ printk(KERN_ERR "%s: icp_HssAccPortErrorCallbackRegister "
+ "returned error %d\n",
+ __func__, (int) status);
+ return -EIO;
+ }
+
+ status = icp_HssAccPortUp(xtp->port_num);
+ if (status != ICP_STATUS_SUCCESS) {
+ printk(KERN_ERR "%s: icp_HssAccPortUp returned error %d\n",
+ __func__, (int) status);
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(xivo_tdm_config_port);
+
+int xivo_tdm_start_chans(
+ struct xivo_tdm_port* xtp,
+ const u32 chans)
+{
+ u32 scan;
+ int cnum;
+
+ for (cnum = 0, scan = 1; scan; scan <<= 1, cnum++) {
+ if (scan & chans) {
+ printk(KERN_INFO "%s: adding chan %d\n",
+ __func__, cnum);
+ /* XXX retval */ (void) add_one_chan(xtp, cnum);
+ }
+ }
+
+ 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);
+ }
+ }
+
+ printk(KERN_INFO "%s: DONE\n", __func__);
+
+ return 0;
+}
+EXPORT_SYMBOL(xivo_tdm_start_chans);
+
+void xivo_tdm_receive(
+ struct xivo_tdm_port* xtp,
+ const unsigned int cnum,
+ u8 samples[8])
+{
+ memcpy(samples, xtp->rx_bufs[cnum], 8);
+}
+EXPORT_SYMBOL(xivo_tdm_receive);
+
+void xivo_tdm_transmit(
+ struct xivo_tdm_port* xtp,
+ const unsigned int cnum,
+ const u8 samples[8])
+{
+ memcpy(xtp->tx_bufs[cnum], samples, 8);
+}
+EXPORT_SYMBOL(xivo_tdm_transmit);
+
+void xivo_tdm_tick(
+ struct xivo_tdm_port* xtp)
+{
+ queue_work(xivo_tdm_wq, &xtp->work);
+}
+EXPORT_SYMBOL(xivo_tdm_tick);
+
+
+/* INIT / CLEANUP */
+
+static void xivo_internal_cleanup(void)
+{
+ if (xivo_tdm_wq) {
+ destroy_workqueue(xivo_tdm_wq);
+ xivo_tdm_wq = NULL;
+ }
+}
+
+static int __init xivo_internal_init(void)
+{
+ int i;
+
+ xivo_tdm_wq = create_singlethread_workqueue("xivo_tdm");
+ if (!xivo_tdm_wq) {
+ printk(KERN_ERR "%s: create_singlethread_worqueue "
+ "returned NULL\n", __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < (int)ARRAY_SIZE(xivo_tdm_ports); i++) {
+ xivo_tdm_ports[i].port_num = i;
+ xivo_tdm_ports[i].allocated = false;
+
+ xivo_tdm_ports[i].ts_allocated = 0;
+ xivo_tdm_ports[i].ts_started = 0;
+ memset(xivo_tdm_ports[i].hss_chan_ids, 0,
+ sizeof xivo_tdm_ports[i].hss_chan_ids);
+
+ spin_lock_init(&xivo_tdm_ports[i].lock);
+ INIT_WORK(&xivo_tdm_ports[i].work,
+ xivo_tdm_work,
+ &xivo_tdm_ports[i]);
+
+ memset(xivo_tdm_ports[i].tx_bufs, 0xD5,
+ sizeof xivo_tdm_ports[i].tx_bufs);
+ memset(xivo_tdm_ports[i].rx_bufs, 0xD5,
+ sizeof xivo_tdm_ports[i].rx_bufs);
+ }
+
+ return 0;
+}
+
+static int __init xivo_icp_Hss_init(void)
+{
+ icp_status_t status;
+ int hss_port_number;
+ int hss_chan_number;
+ int i;
+
+ hss_port_number = icp_HssAccNumSupportedPortsGet();
+ if (hss_port_number < MIN_HSS_PORTS_WANTED) {
+ printk(KERN_ERR "%s: wants %d HSS (TDM) ports but the system "
+ "has only %d\n",
+ __func__, MIN_HSS_PORTS_WANTED, hss_port_number);
+ return -ENXIO;
+ }
+ hss_chan_number = icp_HssAccNumSupportedChannelsGet();
+ if (hss_chan_number < MIN_HSS_CHANS_WANTED) {
+ printk(KERN_ERR "%s: wants %d HSS (TDM) chans but the system "
+ "can only use %d\n",
+ __func__, MIN_HSS_CHANS_WANTED, hss_chan_number);
+ return -ENXIO;
+ }
+
+ status = icp_HssAccVoiceInit(TDM_SAMPLE_SIZE_FOR_DAHDI,
+ /* intGenerationEnable */ ICP_TRUE);
+ if (status != ICP_STATUS_SUCCESS) {
+ printk(KERN_ERR "%s: icp_HssAccVoiceInit() returned error %d\n",
+ __func__, (int) status);
+ return -EIO;
+ }
+
+ status = icp_HssAccErrorCallbackRegister(ICP_HSSACC_CHAN_TYPE_VOICE,
+ NULL, xivo_hss_error_cb);
+ if (status != ICP_STATUS_SUCCESS) {
+ printk(KERN_ERR "%s: icp_HssAccErrorCallbackRegister() "
+ "returned error %d\n",
+ __func__, (int) status);
+ return -EIO;
+ }
+
+ /* GIVE ME A spare_wheel_osal_buf AND A PONEY (and LSD) */
+ for (i = 0; i < RX_BUF_NUM; i++) {
+ IX_OSAL_MBUF* osal_buf =
+ alloc_init_osal_buf(TDM_SAMPLE_SIZE_FOR_DAHDI);
+ if (!osal_buf) {
+ printk(KERN_ERR "BUGBUG WARNING MEMORY LEAK YOU'LL DIE "
+ "(triggered by memory alloc failure in "
+ "alloc_init_osal_buf)\n");
+ return -ENOMEM;
+ }
+
+ status = icp_HssAccRxFreeReplenish(ICP_HSSACC_CHAN_TYPE_VOICE,
+ osal_buf);
+ if (status != ICP_STATUS_SUCCESS) {
+ printk(KERN_WARNING "the hss soft is not hungry "
+ "anymore, giving up feeding\n");
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int __init xivo_tdm_init(void)
+{
+ int rc;
+
+ if ((rc = xivo_internal_init()) < 0)
+ return rc;
+
+ if ((rc = xivo_icp_Hss_init()) < 0) {
+ xivo_internal_cleanup();
+ return rc;
+ }
+
+ printk(KERN_NOTICE "%s DONE\n", __func__);
+ return 0;
+}
+
+static void __exit xivo_tdm_exit(void)
+{
+ printk(KERN_ERR "BUGBUG WARNING MEMORY LEAK YOU'LL DIE "
+ "(triggered by module unload)\n");
+ /* XXX */
+ xivo_internal_cleanup();
+}
+
+module_init(xivo_tdm_init);
+module_exit(xivo_tdm_exit);
diff --git a/tdm/xivo_tdm_api.h b/tdm/xivo_tdm_api.h
new file mode 100644
index 0000000..ce0d448
--- /dev/null
+++ b/tdm/xivo_tdm_api.h
@@ -0,0 +1,45 @@
+#ifndef XIVO_TDM_API_H
+#define XIVO_TDM_API_H
+
+/* XXX matches with ICP_HSSDRV_PORT_XHFC_MEGREZ_PROTO_XIVO_CONFIG */
+#define XHFC_MEGREZ_PROTO_XIVO_CONFIG 4
+
+struct xivo_tdm_port;
+
+/* TDM port allocation
+ * Returns a xivo_tdm_port pointer if available, else NULL. */
+/* hardirq: no -- softirq: no -- user: yes */
+struct xivo_tdm_port *xivo_tdm_get_port(int tdm_port_num);
+
+/* TDM port release */
+/* hardirq: no -- softirq: no -- user: yes */
+void xivo_tdm_put_port(struct xivo_tdm_port *);
+
+/* TDM port config + startup */
+/* hardirq: no -- softirq: no -- user: yes */
+int xivo_tdm_config_port(
+ struct xivo_tdm_port* xtp,
+ unsigned int port_config);
+
+/* hardirq: no -- softirq: no -- user: yes */
+int xivo_tdm_start_chans(
+ struct xivo_tdm_port* xtp,
+ u32 chans);
+
+/* hardirq: yes -- softirq: yes -- user: yes */
+void xivo_tdm_receive(
+ struct xivo_tdm_port* xtp,
+ const unsigned int cnum,
+ u8 samples[8]);
+
+/* hardirq: yes -- softirq: yes -- user: yes */
+void xivo_tdm_transmit(
+ struct xivo_tdm_port* xtp,
+ const unsigned int cnum,
+ const u8 samples[8]);
+
+/* hardirq: yes -- softirq: yes -- user: yes */
+void xivo_tdm_tick(
+ struct xivo_tdm_port* xtp);
+
+#endif /* XIVO_TDM_API_H */
diff --git a/xhfc/Makefile b/xhfc/Makefile
index 6644ad6..62626f1 100644
--- a/xhfc/Makefile
+++ b/xhfc/Makefile
@@ -2,7 +2,7 @@ PWD := $(shell pwd)
KSRC ?= /bad__ksrc__not_set
DAHDI_INCLUDE ?= /bad__dahdi_include__not_set
-CFLAGS_MODULE += -I$(DAHDI_INCLUDE) -DUSE_GPIO
+CFLAGS_MODULE += -I$(DAHDI_INCLUDE) -I$(abspath $(src)/../) -DUSE_GPIO
obj-m := xhfcdm.o
diff --git a/xhfc/xhfc.c b/xhfc/xhfc.c
index e5c6ae1..020f4ff 100644
--- a/xhfc/xhfc.c
+++ b/xhfc/xhfc.c
@@ -5,7 +5,7 @@
#include <linux/pci.h>
#ifdef USE_GPIO
-#include "../gpio/gpio.h"
+#include <gpio/gpio.h>
#endif
#include "xhfc24sucd.h"
@@ -56,6 +56,9 @@ MODULE_PARM_DESC(reset_gpio, "Reset the XHFC using this GPIO");
#endif
MODULE_PARM_DESC(exit_after_reset, "Exit after hard reset");
+#define RX_HISTO_NB 16
+static int rx_histo[RX_HISTO_NB];
+
void xhfc_waitbusy(struct xhfc *xhfc)
{
while (read_xhfc(xhfc, R_STATUS) & M_BUSY)
@@ -110,12 +113,16 @@ int xhfc_reset(struct xhfc *xhfc)
void xhfc_config_st(struct xhfc *x, int port, int nt)
{
- u8 r_su_sel, a_su_ctrl0, a_su_ctrl1, a_su_wr_sta, a_su_clk_dly;
- r_su_sel = a_su_ctrl0 = a_su_ctrl1 = a_su_wr_sta = a_su_clk_dly = 0x00;
+ u8 r_su_sel, a_su_ctrl0, a_su_ctrl1, a_su_ctrl2, a_su_wr_sta, a_su_clk_dly;
+ r_su_sel = a_su_ctrl0 = a_su_ctrl1 = a_su_ctrl2 = a_su_wr_sta = a_su_clk_dly = 0x00;
SET_V_SU_SEL(r_su_sel, port);
SET_V_SU_MD(a_su_ctrl0, !!nt);
+ SET_V_B1_TX_EN(a_su_ctrl0, 1);
+ SET_V_B2_TX_EN(a_su_ctrl0, 1);
SET_V_ST_E_IGNO(a_su_ctrl1, nt ? 1 : 0);
SET_V_G2_G3_EN(a_su_ctrl1, 1);
+ SET_V_B1_RX_EN(a_su_ctrl2, 1);
+ SET_V_B2_RX_EN(a_su_ctrl2, 1);
SET_V_SU_ACT(a_su_wr_sta, 3); /* activation */
SET_V_SU_CLK_DLY(a_su_clk_dly, nt ? 0xC : 0xE); /* normal operation
* WARNING: if the
@@ -127,13 +134,12 @@ void xhfc_config_st(struct xhfc *x, int port, int nt)
write_xhfc(x, R_SU_SEL, r_su_sel);
xhfc_waitbusy(x);
- write_xhfc(x, A_SU_CTRL0, a_su_ctrl0); /* XXX V_B1_TX_EN V_B2_TX_EN */
+ write_xhfc(x, A_SU_CTRL0, a_su_ctrl0);
write_xhfc(x, A_SU_CTRL1, a_su_ctrl1);
+ write_xhfc(x, A_SU_CTRL2, a_su_ctrl2);
write_xhfc(x, A_SU_CLK_DLY, a_su_clk_dly);
write_xhfc(x, A_SU_WR_STA, a_su_wr_sta);
xhfc_waitbusy(x);
-
- /* XXX also in A_SU_CTRL2: V_B1_RX_EN V_B2_RX_EN */
}
/* 2 MBit/s (C4IO is 4.096 MHz, 32 time slots): */
@@ -155,7 +161,12 @@ int xhfc_config_pcm(struct xhfc *xhfc, int master_or_slave)
write_xhfc(xhfc, R_PCM_MD0, SET_V_PCM_IDX(r_pcm_md0, 0x9));
/* use slow PCM clock adjust speed */
- write_xhfc(xhfc, R_PCM_MD1, M_PLL_ADJ | M_PCM_OD | V_PCM_DR_2M);
+ write_xhfc(xhfc, R_PCM_MD1, M_PLL_ADJ | V_PCM_DR_2M);
+ /* NOTE: open drain on the PCM: add bit M_PCM_OD in R_PCM_MD1
+ * We can't do that with the demo interface card for
+ * Megrez <-> XHFC EVB because Xavier did not managed to get
+ * SMD 1k R before soldering :P
+ */
if (master_or_slave == XHFC_PCM_MASTER) {
write_xhfc(xhfc, R_PCM_MD0,
@@ -589,15 +600,34 @@ irqreturn_t xhfc_interrupt(int irq, void *dev_id, struct pt_regs* ptregs)
hdlc_rx_frame(s);
if (atomic_read(&s->hdlc_pending))
hdlc_tx_frame(s);
+#if 1
+ /* Put in readchunk chunk _previously_ retrieved
+ * from icp soft.
+ * We can't update it at icp level right now,
+ * because it has been written by trippy monkeys
+ * and it would probably be a very bad idea to
+ * try to call it from hardirq context.
+ * (look at ixOsalMutexLock ... :/ )
+ */
+ xivo_tdm_receive(pi->tdm_port, i*2 + 0,
+ s->chans[0]->readchunk);
+ xivo_tdm_receive(pi->tdm_port, i*2 + 1,
+ s->chans[1]->readchunk);
-#if 0 // Why is this commented out? XXX
dahdi_receive(&s->span);
dahdi_transmit(&s->span);
dahdi_ec_span(&s->span);
+
+ xivo_tdm_transmit(pi->tdm_port, i*2 + 0,
+ s->chans[0]->writechunk);
+ xivo_tdm_transmit(pi->tdm_port, i*2 + 1,
+ s->chans[1]->writechunk);
#endif
}
}
+ xivo_tdm_tick(pi->tdm_port);
+
r_su_irq = read_xhfc(xhfc, R_SU_IRQ);
for (i = 0; i < ARRAY_SIZE(xhfc->spans); i++)
@@ -956,6 +986,11 @@ static struct pci_driver xhfc_driver = {
};
+#if 0 /* <stub> */
+struct xivo_tdm_port kikoolol;
+#endif /* </stub> */
+
+
/* pci.txt: called from process context */
int __devinit xhfc_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
@@ -1028,6 +1063,18 @@ int __devinit xhfc_init_one(struct pci_dev *pdev,
leb_init(pi);
+
+ /*************
+ * TDM alloc *
+ *************/
+
+ pi->tdm_port = xivo_tdm_get_port(XIVO_XHFC_TDM_PORT);
+ if (!pi->tdm_port) {
+ rc = -EBUSY;
+ goto err_tdm_get_port;
+ }
+
+
/********************
* XHFC struct init *
********************/
@@ -1072,6 +1119,12 @@ int __devinit xhfc_init_one(struct pci_dev *pdev,
init_spans(&pi->xhfc);
xhfc_init_and_configure(&pi->xhfc);
+ /* TDM started on the XHFC side, XHFC is MASTER */
+ /* Now it's possible to start the TDM bus on the EP80579 side, as SLAVE: */
+ if ((rc = xivo_tdm_config_port(pi->tdm_port, XHFC_MEGREZ_PROTO_XIVO_CONFIG)) < 0) {
+ printk(KERN_ERR "%s %s: xivo_tdm_config_port failed (err=%d)\n", pi->name, __func__, rc);
+ goto err_tdm_config_port;
+ }
for (span = 0; span < SPANS_PER_CHIP; span++)
if ((rc = dahdi_register(&pi->xhfc.spans[span].span, /*prefmaster*/ 1)) < 0) {
@@ -1081,16 +1134,21 @@ int __devinit xhfc_init_one(struct pci_dev *pdev,
enable_interrupts(&pi->xhfc); /* enabled in startup <== XXX gnnnnn??? BUGBUG? */
+ xivo_tdm_start_chans(pi->tdm_port, /* timeslots 0->7 */ 0xFF);
+
return 0;
err_in_dahdi_register:
for (span--; span >= 0; span--)
dahdi_unregister(&pi->xhfc.spans[span].span);
+err_tdm_config_port:
free_irq(pi->irq, pi);
err_request_irq:
err_collect_chip_id:
//acpi_unregister_gsi(IRQ_TLP_GPIO_30); /* symbol doesn't exist??? */
err_acpi_register:
+ xivo_tdm_put_port(pi->tdm_port);
+err_tdm_get_port:
exit_after_reset:
iounmap(pi->cs_n0);
err_ioremap_mmbar:
diff --git a/xhfc/xhfc.h b/xhfc/xhfc.h
index 53c99bc..e6f3293 100644
--- a/xhfc/xhfc.h
+++ b/xhfc/xhfc.h
@@ -4,10 +4,26 @@
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <dahdi/kernel.h>
+
+#if 1
+# include <tdm/xivo_tdm_api.h>
+#else
+/* place holder */
+#define ICP_HSSDRV_PORT_XHFC_MEGREZ_PROTO_XIVO_CONFIG 42
+struct xivo_tdm_port { int lol; };
+extern struct xivo_tdm_port kikoolol;
+static inline struct xivo_tdm_port* xivo_tdm_get_port(int port) { return &kikoolol; }
+static inline void xivo_tdm_put_port(struct xivo_tdm_port* x) {}
+static inline int xivo_tdm_config_port(struct xivo_tdm_port* x, unsigned int port_config) { return 0; }
+#endif
+
#include "xhfc24sucd.h"
+
#define DRIVER_NAME "xhfc"
+#define XIVO_XHFC_TDM_PORT 0
+
#ifndef CHIP_ID_2S4U
#define CHIP_ID_2S4U 0x62
#endif
@@ -145,6 +161,7 @@ struct xhfc_pi {
struct xhfc xhfc; /* mem for one XHFC */
struct pci_dev *pci_dev;
+ struct xivo_tdm_port *tdm_port;
};
#define dchan_fifo(span) (span->port * 4 + 2)