#include #include "xpd.h" #include "xpp_zap.h" #include "xpp_proto.h" #include "cards.h" static char rcsid[] = "$Id$"; extern int print_dbg; #include "zap_debug.h" #define MAX_SLIC_REGISTERS 100 struct FXS_private_data { slic_reply_t last_slic_reply; }; /*------------------------- FXS Functions --------------------------*/ int FXS_card_new(xpd_t *xpd) { xpd->direction = TO_PHONE; xpd->channels = min(8, CHANNELS_PERXPD); if(xpd->id == 0) { DBG("First XPD detected. Initialize digital outputs\n"); xpd->channels += 2; xpd->digital_outputs = BIT(8) | BIT(9); // Two extra channels } return 0; } int FXS_card_remove(xpd_t *xpd) { return 0; } static int FXS_card_startup(struct zt_span *span) { DBG("\n"); return 0; } /* * Called only for 'span' keyword in /etc/zaptel.conf */ static int FXS_card_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) { xpd_t *xpd = span->pvt; DBG("%s\n", xpd->xpdname); return 0; } /* Set signalling type (if appropriate) */ static int FXS_card_chanconfig(struct zt_chan *chan, int sigtype) { DBG("channel %d (%s), sigtype %d.\n", chan->channo, chan->name, sigtype); dump_sigtype(print_dbg, " ", sigtype); // FIXME: sanity checks: // - should be supported (within the sigcap) // - should not replace fxs <->fxo ??? (covered by previous?) return 0; } /* * Called only for 'span' keyword in /etc/zaptel.conf */ static int FXS_card_shutdown(struct zt_span *span) { xpd_t *xpd = span->pvt; DBG("%s\n", xpd->xpdname); return 0; } static int FXS_card_sethook(struct zt_chan *chan, int hookstate) { int pos = chan->chanpos - 1; xpd_t *xpd = chan->pvt; xbus_t *xbus; int ret = 0; if(!xpd) { ERR("%s: channel=%d without an XPD!\n", __FUNCTION__, pos); return -EINVAL; } xbus = xpd->xbus; // DBG("%s (%d) (old=0x%04X, hook-command=%d)\n", chan->name, pos, xpd->hookstate, hookstate); switch(hookstate) { /* On-hook, off-hook: The PBX is playing a phone on an FXO line. * Can be ignored for an FXS line */ case ZT_ONHOOK: if(IS_SET(xpd->digital_outputs, pos)) { DBG("ZT_ONHOOK %s digital output OFF\n", chan->name); ret = CALL_PROTO(RELAY_OUT, xpd->xbus, xpd, pos-8, 0); return ret; } DBG("ZT_ONHOOK: %s hookstate=0x%04X (stop ringing pos=%d)\n", chan->name, xpd->hookstate, pos); xpd->ringing[pos] = 0; #if 1 // FIXME: Not needed -- verify ret = CALL_PROTO(RING, xbus, xpd, pos, 0); // RING off #endif ret = CALL_PROTO(LED, xpd->xbus, xpd, BIT(pos), LED_GREEN, 0); ret = CALL_PROTO(CHAN_POWER, xbus, xpd, BIT(pos), 0); // Power down (prevent overheating!!!) if(ret) { DBG("ZT_ONHOOK(stop ring) Failed: ret=0x%02X\n", ret); break; } break; case ZT_START: DBG("ZT_START: %s hookstate=0x%04X (fall through ZT_OFFHOOK)\n", chan->name, xpd->hookstate); // Fall through case ZT_OFFHOOK: DBG("ZT_OFFHOOK: %s hookstate=0x%04X -- ignoring (FXS)\n", chan->name, xpd->hookstate); break; case ZT_WINK: DBG("ZT_WINK %s\n", chan->name); break; case ZT_FLASH: DBG("ZT_FLASH %s\n", chan->name); break; case ZT_RING: DBG("ZT_RING %s pos=%d (ringing[pos]=%d)\n", chan->name, pos, xpd->ringing[pos]); if(IS_SET(xpd->digital_outputs, pos)) { DBG("ZT_ONHOOK %s digital output ON\n", chan->name); ret = CALL_PROTO(RELAY_OUT, xpd->xbus, xpd, pos-8, 1); return ret; } xpd->ringing[pos] = RINGS_NUM*2; ret = CALL_PROTO(CHAN_POWER, xbus, xpd, (1 << pos), 1); // Power up (for ring) ret = CALL_PROTO(RING, xbus, xpd, pos, 1); // RING on if(ret) { DBG("ZT_RING Failed: ret=0x%02X\n", ret); } break; case ZT_RINGOFF: DBG("ZT_RINGOFF %s\n", chan->name); break; default: DBG("UNKNOWN hookstate=0x%X\n", hookstate); } return ret; } static int FXS_card_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg) { switch (cmd) { default: return xpp_ioctl(chan, cmd, arg); } return 0; } #if 0 int FXS_zaptel_setup(xpd_t *xpd) { struct zt_chan *cur_chan; struct zt_span *span; xbus_t *xbus; int i; int cn; BUG_ON(!xpd); sigfxs = ! (xpd->direction == TO_PHONE); /* signaling is opposite */ cn = xpd->channels; DBG("Initializing span: xpd %d have %d channels.\n", xpd->id, cn); xpd->chans = kmalloc(sizeof(struct zt_chan)*cn, GFP_ATOMIC); if (xpd->chans == NULL) { ERR("xpd: Unable to allocate channels\n"); return -ENOMEM; } memset(xpd->chans, 0, sizeof(struct zt_chan)*cn); memset(&xpd->span, 0, sizeof(struct zt_span)); span = &xpd->span; xbus = xpd->xbus; snprintf(span->name, MAX_SPANNAME, "%s/%s", xbus->busname, xpd->xpdname); { char tmp[MAX_SPANNAME]; struct xpd_sim *sim = &xbus->sim[xpd->id]; if(sim->simulated) snprintf(tmp, MAX_SPANNAME, " (sim to=%d)", sim->loopto); else tmp[0] = '\0'; snprintf(span->desc, MAX_SPANDESC, "Xorcom XPD #%d/%d: %s%s", xbus->num, xpd->id, (xpd->direction == TO_PHONE) ? "FXS" : "FXO", tmp ); } for(i = 0; i < cn; i++) { cur_chan = &xpd->chans[i]; DBG("setting channel %d\n", i); snprintf(cur_chan->name, MAX_CHANNAME, "XPP_FXS/%d-%d", xpd->id, i); cur_chan->chanpos = i + 1; cur_chan->pvt = xpd; cur_chan->sigcap = #if 1 ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | #else ZT_SIG_SF | ZT_SIG_EM | #endif 0; } span->deflaw = ZT_LAW_MULAW; init_waitqueue_head(&span->maintq); span->pvt = xpd; span->channels = cn; span->chans = xpd->chans; span->startup = FXS_xpp_startup; span->shutdown = FXS_xpp_shutdown; span->spanconfig = FXS_xpp_spanconfig; span->chanconfig = FXS_xpp_chanconfig; span->open = xpp_open; span->close = xpp_close; #ifdef WITH_RBS span->flags = ZT_FLAG_RBS; span->hooksig = xpp_hooksig; /* Only with RBS bits */ #else span->sethook = FXS_xpp_sethook; #endif span->ioctl = FXS_xpp_ioctl; span->maint = xpp_maint; #ifdef CONFIG_ZAPTEL_WATCHDOG span->watchdog = xpp_watchdog; #endif return 0; } #endif int FXS_zaptel_cleanup(xpd_t *xpd) { return 0; } /*------------------------- FXO Functions --------------------------*/ static int FXO_card_new(xpd_t *xpd) { xpd->direction = TO_TRUNK; xpd->channels = min(8, CHANNELS_PERXPD); return 0; } static int FXO_card_remove(xpd_t *xpd) { return 0; } static int FXO_card_startup(struct zt_span *span) { DBG("\n"); return 0; } /* * Called only for 'span' keyword in /etc/zaptel.conf */ static int FXO_card_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) { xpd_t *xpd = span->pvt; DBG("%s\n", xpd->xpdname); return 0; } /* Set signalling type (if appropriate) */ static int FXO_card_chanconfig(struct zt_chan *chan, int sigtype) { DBG("channel %d (%s), sigtype %d.\n", chan->channo, chan->name, sigtype); dump_sigtype(print_dbg, " ", sigtype); // FIXME: sanity checks: // - should be supported (within the sigcap) // - should not replace fxs <->fxo ??? (covered by previous?) return 0; } /* * Called only for 'span' keyword in /etc/zaptel.conf */ static int FXO_card_shutdown(struct zt_span *span) { xpd_t *xpd = span->pvt; DBG("%s\n", xpd->xpdname); return 0; } static int FXO_card_sethook(struct zt_chan *chan, int hookstate) { int pos = chan->chanpos - 1; xpd_t *xpd = chan->pvt; xbus_t *xbus; int ret = 0; BUG_ON(!xpd); xbus = xpd->xbus; // DBG("%s (%d) (old=0x%04X, hook-command=%d)\n", chan->name, pos, xpd->hookstate, hookstate); switch(hookstate) { /* On-hook, off-hook: The PBX is playing a phone on an FXO line. * Can be ignored for an FXS line */ case ZT_ONHOOK: if(IS_SET(xpd->digital_outputs, pos)) { DBG("ZT_ONHOOK %s digital output OFF\n", chan->name); ret = CALL_PROTO(RELAY_OUT, xpd->xbus, xpd, pos-8, 0); return ret; } DBG("ZT_ONHOOK: %s hookstate=0x%04X (pos=%d)\n", chan->name, xpd->hookstate, pos); xpd->ringing[pos] = 0; BIT_CLR(xpd->hookstate, pos); ret = CALL_PROTO(SETHOOK, xbus, xpd, xpd->hookstate); if(ret) { DBG("ZT_ONHOOK Failed: ret=0x%02X\n", ret); break; } break; case ZT_START: DBG("ZT_START: %s hookstate=0x%04X (fall through ZT_OFFHOOK)\n", chan->name, xpd->hookstate); // Fall through case ZT_OFFHOOK: DBG("ZT_OFFHOOK: %s hookstate=0x%04X (pos=%d)\n", chan->name, xpd->hookstate, pos); BIT_SET(xpd->hookstate, pos); xpd->ringing[pos] = 0; ret = CALL_PROTO(SETHOOK, xbus, xpd, xpd->hookstate); if(ret) { DBG("ZT_OFFHOOK Failed: ret=0x%02X\n", ret); break; } break; case ZT_WINK: DBG("ZT_WINK %s\n", chan->name); break; case ZT_FLASH: DBG("ZT_FLASH %s\n", chan->name); break; case ZT_RING: DBG("ZT_RING %s pos=%d (ringing[pos]=%d)\n", chan->name, pos, xpd->ringing[pos]); break; case ZT_RINGOFF: DBG("ZT_RINGOFF %s\n", chan->name); break; default: DBG("UNKNOWN hookstate=0x%X\n", hookstate); } return ret; } static int FXO_card_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg) { switch (cmd) { default: return xpp_ioctl(chan, cmd, arg); } return 0; } /*------------------------- Function table -------------------------*/ #define DEF_(t) \ [XPD_TYPE_ ## t] { \ .card_new = t ## _card_new, \ .card_remove = t ## _card_remove, \ .card_startup = t ## _card_startup, \ .card_shutdown = t ## _card_shutdown, \ .card_spanconfig = t ## _card_spanconfig, \ .card_chanconfig = t ## _card_chanconfig, \ .card_sethook = t ## _card_sethook, \ .card_ioctl = t ## _card_ioctl, \ } xops_t xpd_card_ops[XPD_TYPE_NOMODULE] = { DEF_(FXS), DEF_(FXO) }; xops_t *get_xops(xpd_type_t xpd_type) { if(xpd_type >= XPD_TYPE_NOMODULE) return NULL; return &xpd_card_ops[xpd_type]; }