/* * Synchronous part of XPD detection. * Called from xbus_poll() */ int create_xpd(xbus_t *xbus, const xproto_table_t *proto_table, int unit, int subunit, byte type, byte subtype, int subunits, byte port_dir) { xpd_t *xpd = NULL; bool to_phone; int ret = -EINVAL; BUG_ON(type == XPD_TYPE_NOMODULE); to_phone = BIT(subunit) & port_dir; BUG_ON(!xbus); xpd = xpd_byaddr(xbus, unit, subunit); if(xpd) { XPD_NOTICE(xpd, "XPD at %d%d already exists\n", unit, subunit); goto out; } xpd = proto_table->xops.card_new(xbus, unit, subunit, proto_table, subtype, subunits, to_phone); if(!xpd) { XBUS_NOTICE(xbus, "card_new(%d,%d,%d,%d,%d) failed. Ignored.\n", unit, subunit, proto_table->type, subtype, to_phone); goto err; } out: return 0; err: if(xpd) xpd_free(xpd); return ret; }
static xpd_t *FXO_card_new(xbus_t *xbus, int unit, int subunit, const xproto_table_t *proto_table, __u8 subtype, int subunits, int subunit_ports, bool to_phone) { xpd_t *xpd = NULL; int channels; if (to_phone) { XBUS_NOTICE(xbus, "XPD=%d%d: try to instanciate FXO with " "reverse direction\n", unit, subunit); return NULL; } if (subtype == 2) channels = min(2, subunit_ports); else channels = min(8, subunit_ports); xpd = xpd_alloc(xbus, unit, subunit, subtype, subunits, sizeof(struct FXO_priv_data), proto_table, channels); if (!xpd) return NULL; PHONEDEV(xpd).direction = TO_PSTN; xpd->type_name = "FXO"; if (fxo_proc_create(xbus, xpd) < 0) goto err; return xpd; err: xpd_free(xpd); return NULL; }
__must_check int xpd_common_init(xbus_t *xbus, xpd_t *xpd, int unit, int subunit, int subtype, int subunits) { int ret; MKADDR(&xpd->addr, unit, subunit); xpd->xbus_idx = XPD_IDX(unit,subunit); snprintf(xpd->xpdname, XPD_NAMELEN, "XPD-%1d%1d", unit, subunit); xpd->subtype = subtype; xpd->subunits = subunits; xpd->offhook = 0; /* For USB-1 disable some channels */ if(MAX_SEND_SIZE(xbus) < RPACKET_SIZE(GLOBAL, PCM_WRITE)) { xpp_line_t no_pcm; no_pcm = 0x7F | xpd->digital_outputs | xpd->digital_inputs; xpd->no_pcm = no_pcm; XBUS_NOTICE(xbus, "max xframe size = %d, disabling some PCM channels. no_pcm=0x%04X\n", MAX_SEND_SIZE(xbus), xpd->no_pcm); } if((ret = xpd_proc_create(xbus, xpd)) < 0) return ret; xbus_register_xpd(xbus, xpd); return 0; }
static int xframe_receive_cmd(xbus_t *xbus, xframe_t *xframe) { byte *xframe_end; xpacket_t *pack; byte *p; int len; int ret; if(debug & DBG_COMMANDS) dump_xframe("RX-CMD", xbus, xframe, DBG_ANY); p = xframe->packets; xframe_end = p + XFRAME_LEN(xframe); do { pack = (xpacket_t *)p; len = XPACKET_LEN(pack); /* Sanity checks */ if(unlikely(XPACKET_OP(pack) == XPROTO_NAME(GLOBAL,PCM_READ))) { static int rate_limit; if((rate_limit++ % 1003) == 0) { XBUS_DBG(GENERAL, xbus, "A PCM packet within a Non-PCM xframe\n"); dump_xframe("In Non-PCM xframe", xbus, xframe, debug); } ret = -EPROTO; goto out; } p += len; if(p > xframe_end || len < RPACKET_HEADERSIZE) { static int rate_limit; if((rate_limit++ % 1003) == 0) { XBUS_NOTICE(xbus, "Invalid packet length %d\n", len); dump_xframe("BAD LENGTH", xbus, xframe, debug); } ret = -EPROTO; goto out; } ret = packet_process(xbus, pack); if(unlikely(ret < 0)) break; } while(p < xframe_end); out: FREE_RECV_XFRAME(xbus, xframe); return ret; }
static DEVICE_ATTR_WRITER(xbus_state_store, dev, buf, count) { xbus_t *xbus; xbus = dev_to_xbus(dev); XBUS_DBG(GENERAL, xbus, "%s\n", buf); if (strncmp(buf, "stop", 4) == 0) xbus_deactivate(xbus); else if (XBUS_IS(xbus, IDLE) && strncmp(buf, "start", 5) == 0) xbus_activate(xbus); else { XBUS_NOTICE(xbus, "%s: Illegal action %s in state %s. Ignored.\n", __func__, buf, xbus_statename(XBUS_STATE(xbus))); return -EINVAL; } return count; }
int xframe_receive(xbus_t *xbus, xframe_t *xframe) { int ret = 0; struct timeval now; struct timeval tv_received; int usec; if(XFRAME_LEN(xframe) < RPACKET_HEADERSIZE) { static int rate_limit; if((rate_limit++ % 1003) == 0) { XBUS_NOTICE(xbus, "short xframe\n"); dump_xframe("short xframe", xbus, xframe, debug); } FREE_RECV_XFRAME(xbus, xframe); return -EPROTO; } if(!XBUS_FLAGS(xbus, CONNECTED)) { XBUS_DBG(GENERAL, xbus, "Dropped xframe. Is shutting down.\n"); return -ENODEV; } tv_received = xframe->tv_received; /* * We want to check that xframes do not mix PCM and other commands */ if(XPACKET_IS_PCM((xpacket_t *)xframe->packets)) { if(!XBUS_IS(xbus, READY)) FREE_RECV_XFRAME(xbus, xframe); else xframe_receive_pcm(xbus, xframe); } else { XBUS_COUNTER(xbus, RX_CMD)++; ret = xframe_receive_cmd(xbus, xframe); } /* Calculate total processing time */ do_gettimeofday(&now); usec = (now.tv_sec - tv_received.tv_sec) * 1000000 + now.tv_usec - tv_received.tv_usec; if(usec > xbus->max_rx_process) xbus->max_rx_process = usec; return ret; }
static int packet_process(xbus_t *xbus, xpacket_t *pack) { byte op; const xproto_entry_t *xe; xproto_handler_t handler; xproto_table_t *table; xpd_t *xpd; int ret = -EPROTO; BUG_ON(!pack); if(!valid_xpd_addr(&XPACKET_ADDR(pack))) { if(printk_ratelimit()) { XBUS_NOTICE(xbus, "%s: from %d%d: bad address.\n", __FUNCTION__, XPACKET_ADDR_UNIT(pack), XPACKET_ADDR_SUBUNIT(pack)); dump_packet("packet_process -- bad address", pack, debug); } goto out; } op = XPACKET_OP(pack); xpd = xpd_byaddr(xbus, XPACKET_ADDR_UNIT(pack), XPACKET_ADDR_SUBUNIT(pack)); /* XPD may be NULL (e.g: during bus polling */ xe = xproto_global_entry(op); /*-------- Validations -----------*/ if(!xe) { const xproto_table_t *xtable; if(!xpd) { if(printk_ratelimit()) { XBUS_NOTICE(xbus, "%s: from %d%d opcode=0x%02X: no such global command.\n", __FUNCTION__, XPACKET_ADDR_UNIT(pack), XPACKET_ADDR_SUBUNIT(pack), op); dump_packet("packet_process -- no such global command", pack, 1); } goto out; } xtable = xproto_table(xpd->type); if(!xtable) { if(printk_ratelimit()) XPD_ERR(xpd, "%s: no protocol table (type=%d)\n", __FUNCTION__, xpd->type); goto out; } xe = xproto_card_entry(xtable, op); if(!xe) { if(printk_ratelimit()) { XPD_NOTICE(xpd, "%s: bad command (type=%d,opcode=0x%x)\n", __FUNCTION__, xpd->type, op); dump_packet("packet_process -- bad command", pack, 1); } goto out; } } table = xe->table; BUG_ON(!table); if(!table->packet_is_valid(pack)) { if(printk_ratelimit()) { ERR("xpp: %s: wrong size %d for opcode=0x%02X\n", __FUNCTION__, XPACKET_LEN(pack), op); dump_packet("packet_process -- wrong size", pack, debug); } goto out; } ret = 0; /* All well */ handler = xe->handler; BUG_ON(!handler); XBUS_COUNTER(xbus, RX_BYTES) += XPACKET_LEN(pack); handler(xbus, xpd, xe, pack); out: return ret; }
void notify_bad_xpd(const char *funcname, xbus_t *xbus, const struct xpd_addr addr, const char *msg) { XBUS_NOTICE(xbus, "%s: non-existing address (%1d%1d): %s\n", funcname, addr.unit, addr.subunit, msg); }
static int copy_pcm_tospan(xbus_t *xbus, xframe_t *xframe) { byte *xframe_end; xpacket_t *pack; byte *p; int ret = -EPROTO; /* Assume error */ if(debug & DBG_PCM) dump_xframe("RX_XFRAME_PCM", xbus, xframe, debug); /* handle content */ p = xframe->packets; xframe_end = p + XFRAME_LEN(xframe); do { int len; xpd_t *xpd; pack = (xpacket_t *)p; len = XPACKET_LEN(pack); /* Sanity checks */ if(unlikely(XPACKET_OP(pack) != XPROTO_NAME(GLOBAL,PCM_READ))) { static int rate_limit; if((rate_limit++ % 1003) == 0) { XBUS_NOTICE(xbus, "%s: Non-PCM packet within a PCM xframe. (%d)\n", __FUNCTION__, rate_limit); dump_xframe("In PCM xframe", xbus, xframe, debug); } goto out; } p += len; if(p > xframe_end || len < RPACKET_HEADERSIZE) { static int rate_limit; if((rate_limit++ % 1003) == 0) { XBUS_NOTICE(xbus, "%s: Invalid packet length %d. (%d)\n", __FUNCTION__, len, rate_limit); dump_xframe("BAD LENGTH", xbus, xframe, debug); } goto out; } xpd = xpd_byaddr(xbus, XPACKET_ADDR_UNIT(pack), XPACKET_ADDR_SUBUNIT(pack)); if(unlikely(!xpd)) { static int rate_limit; if((rate_limit++ % 1003) == 0) { notify_bad_xpd(__FUNCTION__, xbus, XPACKET_ADDR(pack), "RECEIVE PCM"); dump_xframe("Unknown XPD addr", xbus, xframe, debug); } goto out; } if(!pcm_valid(xpd, pack)) goto out; if(SPAN_REGISTERED(xpd)) { XBUS_COUNTER(xbus, RX_PACK_PCM)++; CALL_XMETHOD(card_pcm_tospan, xbus, xpd, pack); } } while(p < xframe_end); ret = 0; /* all good */ XBUS_COUNTER(xbus, RX_XFRAME_PCM)++; out: FREE_RECV_XFRAME(xbus, xframe); return ret; }
static void xpp_drift_step(xbus_t *xbus, const struct timeval *tv) { struct xpp_drift *driftinfo = &xbus->drift; struct xpp_ticker *ticker = &xbus->ticker; unsigned long flags; bool cycled; spin_lock_irqsave(&driftinfo->lock, flags); cycled = xpp_ticker_step(&xbus->ticker, tv); if(ref_ticker && syncer && xbus->sync_mode == SYNC_MODE_PLL) { int new_delta_tick = ticker->count - ref_ticker->count; int lost_ticks = new_delta_tick - driftinfo->delta_tick; driftinfo->delta_tick = new_delta_tick; if(lost_ticks) { driftinfo->lost_ticks++; driftinfo->lost_tick_count += abs(lost_ticks); XBUS_DBG(SYNC, xbus, "Lost %d tick%s\n", lost_ticks, (abs(lost_ticks) > 1) ? "s": ""); ticker->cycle = SYNC_ADJ_QUICK; if(abs(lost_ticks) > 100) ticker->count = ref_ticker->count; } else { long usec_delta; bool nofix = 0; usec_delta = (long)usec_diff( &ticker->last_sample.tv, &ref_ticker->last_sample.tv); usec_delta -= driftinfo->wanted_offset; sample_tick(xbus, usec_delta); if(abs(usec_delta) > 300) { /* * We are close to the edge, send a brutal * fix, and skip calculation until next time. */ if(usec_delta > 0 && xbus->sync_adjustment > -SYNC_ADJ_MAX) { XBUS_DBG(SYNC, xbus, "Pullback usec_delta=%ld\n", usec_delta); send_drift(xbus, -SYNC_ADJ_MAX); /* emergency push */ } if(usec_delta < 0 && xbus->sync_adjustment < SYNC_ADJ_MAX) { XBUS_DBG(SYNC, xbus, "Pushback usec_delta=%ld\n", usec_delta); send_drift(xbus, SYNC_ADJ_MAX); /* emergency push */ } ticker->cycle = SYNC_ADJ_QUICK; nofix = 1; } else { /* good data, use it */ if(usec_delta > driftinfo->delta_max) driftinfo->delta_max = usec_delta; if(usec_delta < driftinfo->delta_min) driftinfo->delta_min = usec_delta; } if(!nofix && cycled) { int offset = 0; driftinfo->median = (driftinfo->delta_max + driftinfo->delta_min) / 2; driftinfo->jitter = driftinfo->delta_max - driftinfo->delta_min; if(abs(driftinfo->median) >= 150) { /* more than 1 usb uframe */ int factor = abs(driftinfo->median) / 125; factor = 1 + (factor * 8000) / ticker->cycle; if(driftinfo->median > 0) offset = driftinfo->calc_drift - factor; else offset = driftinfo->calc_drift + factor; /* for large median, push some more */ if(abs(driftinfo->median) >= 300) { /* more than 2 usb uframes */ ticker->cycle = SYNC_ADJ_QUICK; XBUS_NOTICE(xbus, "Back to quick: median=%d\n", driftinfo->median); } } else { ticker->cycle += 500; if(ticker->cycle >= SYNC_ADJ_SLOW) ticker->cycle = SYNC_ADJ_SLOW; } driftinfo->calc_drift = offset; XBUS_DBG(SYNC, xbus, "ADJ: min=%d max=%d jitter=%d median=%d offset=%d\n", driftinfo->delta_min, driftinfo->delta_max, driftinfo->jitter, driftinfo->median, offset); if(offset < -SYNC_ADJ_MAX) offset = -SYNC_ADJ_MAX; if(offset > SYNC_ADJ_MAX) offset = SYNC_ADJ_MAX; xbus->sync_adjustment_offset = offset; if(xbus != syncer && xbus->sync_adjustment != offset) send_drift(xbus, offset); driftinfo_recalc(driftinfo); } } } spin_unlock_irqrestore(&driftinfo->lock, flags); }