diff options
Diffstat (limited to 'xivovp/xivovp.c')
-rw-r--r-- | xivovp/xivovp.c | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/xivovp/xivovp.c b/xivovp/xivovp.c new file mode 100644 index 0000000..6a6890f --- /dev/null +++ b/xivovp/xivovp.c @@ -0,0 +1,422 @@ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <linux/compiler.h> +#include <linux/stringify.h> + +#include <dahdi/kernel.h> + +#include "vp_api.h" +#include "EVB_Le71HR8921G_rev_E_2M048.h" + +#define TICK_MS 5 /* matches with device profile configuration */ + +#include "tolapai_spi.h" + +#define DRV_NAME KBUILD_BASENAME + +#define TRACES_VANISH_DEFAULT 2000 + +static int traces_vanish = TRACES_VANISH_DEFAULT; +module_param(traces_vanish, int, 0444); +MODULE_PARM_DESC(traces_vanish, + "number of " __stringify(TICK_MS) "ms ticks after device init complete " + "event at which point VP-API II Lite traces will be reduced to only " + "ERROR/WARNING/INFO (default: " __stringify(TRACES_VANISH_DEFAULT) ")"); + + +#define NB_LINES 2 +#define TICK_JIFFIES DIV_ROUND_UP(TICK_MS * HZ, 1000) +#define DEBUG_SELECT_RUNNING (VP_DBG_ERROR \ + | VP_DBG_WARNING \ + | VP_DBG_INFO) + +enum xivovp_line_type { FXS_LINE = 0, FXO_LINE = 1 }; + +struct xivovp_line { + enum xivovp_line_type type; + //int reverse_polarity; + + Vp890LineObjectType line_obj; + VpLineCtxType vp_ctx; + struct dahdi_chan chan; +}; + +static struct xivovp { + struct dahdi_span span; + struct timer_list vp_tick_timer; + + struct xivovp_line line[NB_LINES]; + struct dahdi_chan *chans[NB_LINES]; + + Vp890DeviceObjectType ve890_dev_obj; + VpDevCtxType dev_ctx; +} xivovp; + +/* Shouldn't there be something better there? TOTHINK */ +static struct xivovp_line* xivovp_line_from_ctx(VpLineCtxType* line_ctx) +{ + Vp890LineObjectType* line_obj = line_ctx->pLineObj; + int i; + + for(i = 0; i < NB_LINES; i++) + if(&xivovp.line[i].line_obj == line_obj) + return &xivovp.line[i]; + + printk(KERN_ERR DRV_NAME ": a FX line we don't know of? This certainly shouldn't happen.\n"); + return NULL; +} + +static void vp_set_debug_select_running(void) +{ + int i; + uint32 debug_select; + + for(i = 0; i < ARRAY_SIZE(xivovp.line); i++) { + debug_select = DEBUG_SELECT_RUNNING; + VpSetOption(&xivovp.line[i].vp_ctx, NULL, VP_OPTION_ID_DEBUG_SELECT, + &debug_select); + } + + debug_select = DEBUG_SELECT_RUNNING; + VpSetOption(NULL, &xivovp.dev_ctx, VP_OPTION_ID_DEBUG_SELECT, &debug_select); + + debug_select = DEBUG_SELECT_RUNNING; + VpSetOption(NULL, NULL, VP_OPTION_ID_DEBUG_SELECT, &debug_select); +} + +static void vp_tick(const unsigned long data) +{ + static VpEventType event; + static VpResultsType result; + struct xivovp_line* line; + bool ev_pending; + VpStatusType vpst; + static bool count_and_stop_traces = FALSE; + + (void)data; + + vpst = VpApiTick(&xivovp.dev_ctx, &ev_pending); + if (vpst != VP_STATUS_SUCCESS) { + printk(KERN_ERR DRV_NAME ": VpApiTick returned %d\n", + (int)vpst); + return; + } + + while (ev_pending) { + ev_pending = VpGetEvent(&xivovp.dev_ctx, &event); + if (event.status != VP_STATUS_SUCCESS) { + printk(KERN_ERR DRV_NAME ": VpGetEvent returned event.status == %d\n", + (int)event.status); + return; + } + + if (ev_pending) { + printk(KERN_INFO "vp evt: %d %d\n", + (int)event.eventCategory, + (int)event.eventId); + + switch ((int)event.eventCategory) { + case VP_EVCAT_RESPONSE: + switch (event.eventId) { + case VP_DEV_EVID_DEV_INIT_CMP: { + VpOptionTimeslotType timeslot = { + .tx = 12, + .rx = 12, + }; + VpOptionEventMaskType event_mask = { + .faults = 0, + .signaling = 0, + .response = 0, + .test = 0, + .process = 0, + .fxo = 0, + }; + printk(KERN_INFO DRV_NAME ": vp dev init complete\n"); + vpst = VpSetOption(event.pLineCtx, NULL, + VP_OPTION_ID_EVENT_MASK, &event_mask); + if (vpst != VP_STATUS_SUCCESS) { + printk(KERN_ERR DRV_NAME ": VpSetOption VP_OPTION_ID_EVENT_MASK returned %d\n", + (int)vpst); + return; + } + vpst = VpSetOption(event.pLineCtx, NULL, + VP_OPTION_ID_TIMESLOT, + ×lot); + if (vpst != VP_STATUS_SUCCESS) { + printk(KERN_ERR DRV_NAME ": VpSetOption VP_OPTION_ID_TIMESLOT returned %d\n", + (int)vpst); + return; + } + vpst = VpSetLineState(event.pLineCtx, + VP_LINE_OHT); + if (vpst != VP_STATUS_SUCCESS) { + printk(KERN_ERR DRV_NAME ": VpSetLineState returned %d\n", + (int)vpst); + return; + } + vpst = VpCalLine(event.pLineCtx); + if (vpst != VP_STATUS_SUCCESS) { + printk(KERN_ERR DRV_NAME ": VpCalLine returned %d\n", + (int)vpst); + return; + } + count_and_stop_traces = TRUE; + break; + } + + case VP_EVID_CAL_CMP: + printk(KERN_INFO DRV_NAME ": calibration succeeded\n"); + vp_set_debug_select_running(); + break; + + case VP_EVID_CAL_BUSY: + printk(KERN_WARNING DRV_NAME ": calibration failed (returned VP_EVID_CAL_BUSY)\n"); + vp_set_debug_select_running(); + break; + } + break; + case VP_EVCAT_FAULT: + break; + case VP_EVCAT_SIGNALING: + if(!(line = xivovp_line_from_ctx(event.pLineCtx))) + break; + + switch (event.eventId) { + case VP_LINE_EVID_HOOK_OFF: + dahdi_hooksig(&line->chan, + DAHDI_RXSIG_OFFHOOK); + break; + case VP_LINE_EVID_HOOK_ON: + dahdi_hooksig(&line->chan, + DAHDI_RXSIG_ONHOOK); + break; + } + break; + case VP_EVCAT_TEST: + break; + case VP_EVCAT_PROCESS: + break; + case VP_EVCAT_FXO: + break; + } + + if (event.hasResults) { + printk(KERN_INFO "VpGetResults\n"); + vpst = VpGetResults(&event, &result); + if (vpst != VP_STATUS_SUCCESS) { + printk(KERN_ERR DRV_NAME ": VpGetResults returned %d\n", (int) vpst); + return; + } + } + } + } + + if (count_and_stop_traces) { + if (traces_vanish >= 0 && --traces_vanish < 0) + vp_set_debug_select_running(); + } + + mod_timer(&xivovp.vp_tick_timer, jiffies + TICK_JIFFIES); +} + +/* + * hooksig transmits sig from DAHDI to VP. + * sig from VP is handled in vp_tick and transmitted to DAHDI with + * dahdi_hooksig + */ +static int +xivovp_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) +{ + struct xivovp_line* line = &xivovp.line[chan->chanpos - 1]; + + if(line->type == FXO_LINE) { + switch(txsig) { + case DAHDI_TXSIG_START: + case DAHDI_TXSIG_OFFHOOK: + printk(KERN_WARNING DRV_NAME "(chan %d): transmitting %s state on FXO, setting to TALK.\n", chan->chanpos, txsig == DAHDI_TXSIG_START ? "START" : "OFFHOOK"); + VpSetLineState(&line->vp_ctx, VP_LINE_FXO_TALK); + break; + case DAHDI_TXSIG_ONHOOK: + printk(KERN_WARNING DRV_NAME "(chan %d): transmitting ONHOOK on FXO, setting to OHT.\n", chan->chanpos); + VpSetLineState(&line->vp_ctx, VP_LINE_FXO_OHT); + break; + default: + printk(KERN_WARNING DRV_NAME "(chan %d): unsupported tx state for FXO: %d\n", chan->chanpos, txsig); + } + } else /* FXS */ { + switch(txsig) { + case DAHDI_TXSIG_START: + printk(KERN_WARNING DRV_NAME "(chan %d): transmitting START on FXS, setting to RINGING.\n", chan->chanpos); + VpSetLineState(&line->vp_ctx, VP_LINE_RINGING); + break; + case DAHDI_TXSIG_OFFHOOK: + printk(KERN_WARNING DRV_NAME "(chan %d): transmitting OFFHOOK on FXS, setting to TALK.\n", chan->chanpos); + VpSetLineState(&line->vp_ctx, VP_LINE_TALK); + break; + case DAHDI_TXSIG_ONHOOK: + printk(KERN_WARNING DRV_NAME "(chan %d): transmitting ONHOOK on FXS, setting to OHT.\n", chan->chanpos); + VpSetLineState(&line->vp_ctx, VP_LINE_OHT); + break; + case DAHDI_TXSIG_KEWL: + printk(KERN_WARNING DRV_NAME "(chan %d): requested to transmit DAHDI_TXSIG_KEWL but I have no idea what it means.\n", chan->chanpos); + //VpSetLineState(line->vp_ctx, ); + return -1; + default: + printk(KERN_WARNING DRV_NAME "(chan %d): unsupported tx state for FXS: %d\n", chan->chanpos, txsig); + return -1; + } + } + + return 0; +} + +static const struct dahdi_span_ops xivovp_span_ops = { + .owner = THIS_MODULE, + .hooksig = xivovp_hooksig, +}; + +static int +span_init(void) +{ + int i; + + dahdi_copy_string(xivovp.span.name, "XiVO_FX", sizeof(xivovp.span.name)); + dahdi_copy_string(xivovp.span.desc, + "FXO/FXS driver for Avencall's XiVO IPBX OpenHardware " + "platform", sizeof(xivovp.span.desc)); + xivovp.span.manufacturer = "Avencall"; + dahdi_copy_string(xivovp.span.devicetype, "Ve890", + sizeof(xivovp.span.devicetype)); + xivovp.span.ops = &xivovp_span_ops; + xivovp.span.chans = xivovp.chans; + for(i = 0; i < NB_LINES; i++) + xivovp.chans[i] = &xivovp.line[i].chan; + + init_waitqueue_head(&xivovp.span.maintq); /* still dunno what this is */ + + return 0; +} + +static int +vp_init(void) +{ + int rc; + VpStatusType vpst; + + vpst = VpMakeDeviceObject( + VP_DEV_890_SERIES, + /* deviceId */ 0, + &xivovp.dev_ctx, + (void*)&xivovp.ve890_dev_obj); + if (vpst != VP_STATUS_SUCCESS) { + printk(KERN_ERR DRV_NAME ": VpMakeDeviceObject failed (%d)\n", vpst); + rc = -EIO; + goto err_vp_make_device_object; + } + + vpst = VpMakeLineObject( + VP_TERM_FXS_GENERIC, + FXS_LINE, + &xivovp.line[0].vp_ctx, + (void*)&xivovp.line[0].line_obj, + &xivovp.dev_ctx); + if (vpst != VP_STATUS_SUCCESS) { + printk(KERN_ERR DRV_NAME ": VpMakeLineObject (FXS) failed (%d)\n", vpst); + rc = -EIO; + goto err_vp_make_line_object; + } + +#if 0 + vpst = VpMakeLineObject( + VP_TERM_FXO_GENERIC, + FXO_LINE, + &xivovp.line[1].vp_ctx, + (void*)&xivovp.line[1].line_obj, + &xivovp.dev_ctx); + if (vpst != VP_STATUS_SUCCESS) { + printk(KERN_ERR DRV_NAME ": VpMakeLineObject (FXO) failed (%d)\n", vpst); + rc = -EIO; + goto err_vp_make_line_object; + } +#endif + + vpst = VpInitDevice( + &xivovp.dev_ctx, + DEV_PROFILE_Buck_Boost, + AC_FXS_RF50_FR, + DC_FXS_DEF, + RING_FR, + AC_FXO_LC_FR, + FXO_DIALING_FR); + if (vpst != VP_STATUS_SUCCESS) { + printk(KERN_ERR DRV_NAME ": VpInitDevice failed (%d)\n", vpst); + rc = -EIO; + goto err_vp_init_device; + } + + /* nothing */ + err_vp_init_device: + /* nothing */ + err_vp_make_line_object: + /* nothing */ + err_vp_make_device_object: + return rc; +} + +static int +test_evb_ve890_init(void) +{ + int rc; + printk(KERN_INFO DRV_NAME ": entering %s\n", __func__); + + init_timer(&xivovp.vp_tick_timer); + xivovp.vp_tick_timer.function = vp_tick; + xivovp.vp_tick_timer.data = 42; + + rc = tlp_spidev_init(); + if (rc < 0) { + printk(KERN_ERR DRV_NAME ": tlp_spidev_init failed (%d)\n", rc); + goto err_spidev_init; + } + + rc = vp_init(); + if(rc < 0) + goto err_vp_init; + + rc = span_init(); + if(rc < 0) { + printk(KERN_ERR DRV_NAME ": ve890_dahdi_init failed\n"); + goto err_dahdi_init; + } + + mod_timer(&xivovp.vp_tick_timer, jiffies + TICK_JIFFIES); + + return 0; + + err_dahdi_init: + /* nothing */ + err_vp_init: + tlp_spidev_exit(); + err_spidev_init: + return rc; +} + +static void +test_evb_ve890_exit(void) +{ + printk(KERN_INFO DRV_NAME ": %s\n", __func__); + + del_timer_sync(&xivovp.vp_tick_timer); /* XXX : TODO update upstream Linux doc of del_timer_sync */ + + tlp_spidev_exit(); +} + +module_init(test_evb_ve890_init); +module_exit(test_evb_ve890_exit); + +MODULE_DESCRIPTION("Test driver for EVB Ve890"); +MODULE_AUTHOR("Guillaume Knispel"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("48"); |