/* * Written by Oron Peled * Copyright (C) 2004, Xorcom * * Derived from ztdummy * * Copyright (C) 2002, Hermes Softlab * Copyright (C) 2004, Digium, Inc. * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) # warning "This module is tested only with 2.6 kernels" #endif #include #include #include #include #include #include /* for udelay */ #include #include #include #include "xbus-core.h" #include "xproto.h" #include "xpp_zap.h" #include "parport_debug.h" static const char rcsid[] = "$Id$"; #ifdef CONFIG_PROC_FS struct proc_dir_entry *xpp_proc_toplevel = NULL; #define PROC_DIR "xpp" #define PROC_SYNC "sync" #define PROC_XPD_ZTREGISTER "zt_registration" #define PROC_XPD_BLINK "blink" #define PROC_XPD_SUMMARY "summary" #endif #define MAX_QUEUE_LEN 10000 #define SAMPLE_TICKS 10000 #define DELAY_UNTIL_DIALTONE 3000 static struct timer_list xpp_timer; static xpd_t *sync_master = NULL; // Start with host based sync static unsigned int xpp_timer_count = 0; static unsigned int xpp_last_jiffies = 0; #ifdef ZAPTEL_SYNC_TICK static unsigned int zaptel_tick_count = 0; /* * Statistics variables */ static struct timeval ticked_zaptel; static struct timeval ticked_xpp; static long usec_lag_curr; /* current: zaptel - xpp */ static long sigma_lag; /* sum of lags over SYNC_ADJ_INTERVAL */ static long average_lag; /* average of lags over SYNC_ADJ_INTERVAL */ static bool zaptel_is_ticking; static bool have_sync_mastership; static unsigned int sync_tick_nomaster; #define ZAPTEL_BIG_LAG 2000 /* usec */ #define SYNC_ADJ_MIN (-30) /* minimal firmware drift unit */ #define SYNC_ADJ_MAX 30 /* maximal firmware drift unit */ #define SYNC_ADJ_INTERVAL 5000 /* minimum interval between fixes (usec) */ #define SYNC_ADJ_FACTOR 30 /* sigma usec/drift_unit */ DEF_PARM_BOOL(sync_tick_active, 1, 0644, "Measure via zaptel sync_tick() method"); #endif DEF_PARM_BOOL(pcm_tasklet, 0, 0644, "Handle PCM in a tasklet (lower interrupt load)"); DEF_PARM(int, disable_pcm, 0, 0644, "Disable all PCM transmissions"); DEF_PARM(int, print_dbg, 0, 0644, "Print DBG statements"); DEF_PARM_BOOL(zap_autoreg, 0, 0644, "Register spans automatically (1) or not (0)"); DEF_PARM_BOOL(prefmaster, 1, 0644, "Do we want to be zaptel preferred sync master"); #ifdef XPP_EC_CHUNK DEF_PARM_BOOL(xpp_ec, 0, 0444, "Do we use our own (1) or Zaptel's (0) echo canceller"); #else static int xpp_ec = 0; #endif #ifdef DEBUG_PCMTX DEF_PARM(int, pcmtx, -1, 0644, "Forced PCM value to transmit (negative to disable)"); DEF_PARM(int, pcmtx_chan, 0, 0644, "channel to force PCM value"); #endif // DEF_ARRAY(int, pcmtx, 4, 0, "Forced PCM values to transmit"); #include "zap_debug.h" #ifdef XPP_EC_CHUNK #include "supress/ec_xpp.h" #endif #ifdef DEBUG_SYNC_PARPORT /* * Use parallel port to sample our PCM sync and diagnose quality and * potential problems. A logic analizer or a scope should be connected * to the data bits of the parallel port. * * Array parameter: Choose the two xbuses Id's to sample. * This can be changed on runtime as well. Example: * echo "3,5" > /sys/module/xpp/parameters/parport_xbuses */ static int parport_xbuses[2] = { 0, 1 }; unsigned int parport_xbuses_num_values; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) module_param_array(parport_xbuses, int, &parport_xbuses_num_values, 0577); #else module_param_array(parport_xbuses, int, parport_xbuses_num_values, 0577); #endif MODULE_PARM_DESC(parport_xbuses, "Id's of xbuses to sample (1-2)"); /* * Flip a single bit in the parallel port: * - The bit number is either bitnum0 or bitnum1 * - Bit is selected by xbus number from parport_xbuses[] */ void xbus_flip_bit(xbus_t *xbus, unsigned int bitnum0, unsigned int bitnum1) { int num = xbus->num; if(num == parport_xbuses[0]) flip_parport_bit(bitnum0); if(num == parport_xbuses[1]) flip_parport_bit(bitnum1); } EXPORT_SYMBOL(xbus_flip_bit); #endif #ifdef ZAPTEL_SYNC_TICK static void send_drift(int drift); #endif static void xpp_tick(unsigned long param); static int zaptel_register_xpd(xpd_t *xpd); static int zaptel_unregister_xpd(xpd_t *xpd); static void xpp_receiveprep(xpd_t *xpd); static void do_ec(xpd_t *xpd); static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); static int proc_xpd_ztregister_read(char *page, char **start, off_t off, int count, int *eof, void *data); static int proc_xpd_ztregister_write(struct file *file, const char __user *buffer, unsigned long count, void *data); static int proc_xpd_blink_read(char *page, char **start, off_t off, int count, int *eof, void *data); static int proc_xpd_blink_write(struct file *file, const char __user *buffer, unsigned long count, void *data); static void xpd_free(xpd_t *xpd); static DECLARE_TASKLET(tasklet_tick, xpp_tick, 0L); static atomic_t missed_ticks = ATOMIC_INIT(0); /* In pcm_tasklet mode */ static void external_sync(xbus_t *the_xbus) { int i; DBG(SYNC, "%s\n", (the_xbus) ? the_xbus->busname : "HOST"); // Shut all down for(i = 0; i < MAX_BUSES; i++) { xbus_t *xbus = xbus_of(i); if(!xbus) continue; if (!xbus->hardware_exists) continue; CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, NULL, SYNC_MODE_HOST, 0); } if(the_xbus) CALL_PROTO(GLOBAL, SYNC_SOURCE, the_xbus, NULL, SYNC_MODE_AB, 0); } /* * Change sync_master. May block. Cannot be called from atomic context */ static void sync_master_is(const char *msg, xbus_t *xbus) { xpd_t *xpd = (xbus)? xpd_of(xbus, 0) : NULL; if(xbus && !xpd) { XBUS_NOTICE(xbus, "Cannot set sync master (has no XPD #0).\n"); xbus = NULL; /* Fallback to HOST */ } DBG(SYNC, "SYNC MASTER CHANGING(%s): %s => %s\n", msg, (sync_master) ? sync_master->xbus->busname : "HOST", (xbus) ? xbus->busname : "HOST"); /* First stop all generators */ if(sync_master) { tasklet_kill(&tasklet_tick); } else del_timer_sync(&xpp_timer); /* Now set a new master */ sync_master = xpd; if(sync_master) { // XPD external_sync(xbus); } else { // HOST external_sync(NULL); if(!timer_pending(&xpp_timer)) { xpp_timer.function = xpp_tick; xpp_timer.data = 0; xpp_timer.expires = jiffies + 1; /* Must be 1KHz rate */ add_timer(&xpp_timer); } } } void elect_syncer(const char *msg) { int i; int j; uint timing_priority = 0; xpd_t *best_xpd = NULL; for(i = 0; i < MAX_BUSES; i++) { xbus_t *xbus = xbus_of(i); if(!xbus) continue; if (!xbus->hardware_exists) continue; for(j = 0; j < MAX_XPDS; j++) { xpd_t *xpd = xpd_of(xbus, j); if(!xpd) continue; if(SPAN_REGISTERED(xpd)) { if(xpd->timing_priority > timing_priority) { timing_priority = xpd->timing_priority; best_xpd = xpd; } } } } if(!best_xpd) { INFO("%s: elected HOST\n", msg); sync_master_is(msg, NULL); } else if(!sync_master || (timing_priority > sync_master->timing_priority)) { XPD_INFO(best_xpd, "%s: elected with priority %d\n", msg, timing_priority); sync_master_is(msg, best_xpd->xbus); } } static inline void send_pcm_frame(xbus_t *xbus, xframe_t *xframe) { unsigned long flags; struct timeval now; unsigned long sec_diff; unsigned long usec_diff; spin_lock_irqsave(&xbus->lock, flags); do_gettimeofday(&now); if(unlikely(disable_pcm || !xbus->hardware_exists)) goto dropit; sec_diff = now.tv_sec - xbus->last_tx_sync.tv_sec; usec_diff = sec_diff * 1000000 + (now.tv_usec - xbus->last_tx_sync.tv_usec); if(unlikely(abs(sec_diff) > 2)) { XBUS_DBG(SYNC, xbus, "PCM TX timing restart (sec_diff=%ld)\n", sec_diff); xbus->last_tx_sync = now; goto dropit; } if(abs(usec_diff - 1000) > TICK_TOLERANCE) { static int rate_limit; if((rate_limit++ % 5003) == 0) XBUS_DBG(SYNC, xbus, "Bad PCM TX timing(%d): usec_diff=%ld.\n", rate_limit, usec_diff); } if(usec_diff > xbus->max_tx_sync) xbus->max_tx_sync = usec_diff; if(usec_diff < xbus->min_tx_sync) xbus->min_tx_sync = usec_diff; spin_unlock_irqrestore(&xbus->lock, flags); /* OK, really send it */ if(print_dbg & DBG_PCM ) dump_xframe("TX_XFRAME_PCM", xbus, xframe); xframe_send(xbus, xframe); XBUS_COUNTER(xbus, TX_XFRAME_PCM)++; xbus->last_tx_sync = now; return; dropit: spin_unlock_irqrestore(&xbus->lock, flags); xbus->ops->xframe_free(xbus, xframe); } /* * Generic implementations of card_pcmfromspan()/card_pcmtospan() * For FXS/FXO */ void generic_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, xpacket_t *pack) { byte *pcm; struct zt_chan *chans; unsigned long flags; int i; BUG_ON(!xbus); BUG_ON(!xpd); BUG_ON(!pack); RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines) = lines; pcm = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, pcm); spin_lock_irqsave(&xpd->lock, flags); chans = xpd->span.chans; for (i = 0; i < xpd->channels; i++) { if(IS_SET(lines, i)) { if(SPAN_REGISTERED(xpd)) { #ifdef DEBUG_PCMTX if(pcmtx >= 0 && pcmtx_chan == i) memset((u_char *)pcm, pcmtx, ZT_CHUNKSIZE); else #endif memcpy((u_char *)pcm, chans[i].writechunk, ZT_CHUNKSIZE); // fill_beep((u_char *)pcm, xpd->addr.subunit, 2); } else memset((u_char *)pcm, 0x7F, ZT_CHUNKSIZE); pcm += ZT_CHUNKSIZE; } } XPD_COUNTER(xpd, PCM_WRITE)++; spin_unlock_irqrestore(&xpd->lock, flags); } void generic_card_pcm_tospan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack) { volatile u_char *r; byte *pcm; xpp_line_t pcm_mask; unsigned long flags; int i; pcm = RPACKET_FIELD(pack, GLOBAL, PCM_READ, pcm); pcm_mask = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines); spin_lock_irqsave(&xpd->lock, flags); if (xpd->timer_count & 1) r = xpd->readchunk; /* First part */ else r = xpd->readchunk + ZT_CHUNKSIZE * CHANNELS_PERXPD; for (i = 0; i < CHANNELS_PERXPD; i++, r += ZT_CHUNKSIZE) { /* * We don't copy signalling buffers (they may be * longer than ZT_CHUNKSIZE). */ if(IS_SET(pcm_mask, i)) { // memset((u_char *)r, 0x5A, ZT_CHUNKSIZE); // DEBUG // fill_beep((u_char *)r, 1, 1); // DEBUG: BEEP memcpy((u_char *)r, pcm, ZT_CHUNKSIZE); pcm += ZT_CHUNKSIZE; } } XPD_COUNTER(xpd, PCM_READ)++; spin_unlock_irqrestore(&xpd->lock, flags); } static void xbus_tick(xbus_t *xbus) { int i; xpd_t *xpd; xframe_t *xframe = NULL; xpacket_t *pack = NULL; size_t pcm_len; /* * Update zaptel */ for(i = 0; i < MAX_XPDS; i++) { xpd = xpd_of(xbus, i); if(xpd && SPAN_REGISTERED(xpd)) { /* * calls to zt_transmit should be out of spinlocks, as it may call back * our hook setting methods. */ #ifdef OPTIMIZE_CHANMUTE int j; for_each_line(xpd, j) xpd->chans[j].chanmute = !IS_SET(xpd->wanted_pcm_mask, j) && !IS_SET(xpd->digital_signalling, j); #endif zt_transmit(&xpd->span); } } /* * Fill xframes */ for(i = 0; i < MAX_XPDS; i++) { if((xpd = xpd_of(xbus, i)) == NULL) continue; pcm_len = xpd->pcm_len; if(SPAN_REGISTERED(xpd)) { if(pcm_len && xpd->card_present) { do { // pack = NULL; /* FORCE single packet frames */ if(xframe && !pack) { /* FULL frame */ send_pcm_frame(xbus, xframe); xframe = NULL; XBUS_COUNTER(xbus, TX_PCM_FRAG)++; } if(!xframe) { /* Alloc frame */ xframe = xbus->ops->xframe_new(xbus, GFP_ATOMIC); if (!xframe) { XBUS_ERR(xbus, "%s: failed to allocate new xframe\n", __FUNCTION__); return; } } pack = xframe_next_packet(xframe, pcm_len); } while(!pack); XPACKET_INIT(pack, GLOBAL, PCM_WRITE); xpd_set_addr(&pack->addr, xpd->xbus_idx); pack->datalen = pcm_len; CALL_XMETHOD(card_pcm_fromspan, xbus, xpd, xpd->wanted_pcm_mask, pack); XBUS_COUNTER(xbus, TX_PACK_PCM)++; } } } if(xframe) /* clean any leftovers */ send_pcm_frame(xbus, xframe); /* * Receive PCM */ for(i = 0; i < MAX_XPDS; i++) { xpd = xpd_of(xbus, i); if(!xpd || !xpd->card_present) continue; if(SPAN_REGISTERED(xpd)) xpp_receiveprep(xpd); xpd->timer_count = xpp_timer_count; /* * Must be called *after* tx/rx so * D-Chan counters may be cleared */ CALL_XMETHOD(card_tick, xbus, xpd); } #ifdef ZAPTEL_SYNC_TICK if(xbus->sync_adjustment != xbus->sync_adjustment_offset) send_drift(xbus->sync_adjustment_offset); #endif } void xpp_tick(unsigned long param) { xbus_t *xbus; int i; #ifdef ZAPTEL_SYNC_TICK flip_parport_bit(3); do_gettimeofday(&ticked_xpp); #endif if(!sync_master) /* Called from timer */ mod_timer(&xpp_timer, jiffies + 1); /* Must be 1KHz rate */ else atomic_dec(&missed_ticks); /* Statistics */ if((xpp_timer_count % SAMPLE_TICKS) == 0) { xpp_last_jiffies = jiffies; } xpp_timer_count++; for(i = 0; i < MAX_BUSES; i++) { xbus = xbus_of(i); if(!xbus) continue; if (!xbus->hardware_exists) continue; if(!down_read_trylock(&xbus->in_use)) { XBUS_DBG(GENERAL, xbus, "Dropped packet. Is in_use\n"); continue; } /* Reset sync LEDs once in a while */ if((xpp_timer_count % 10000) == 0) CALL_PROTO(GLOBAL, RESET_SYNC_COUNTERS, xbus, NULL); if(atomic_dec_and_test(&xbus->pcm_nesting)) { xbus_tick(xbus); } else { XBUS_ERR(xbus, "NESTING ATTEMPT: %d\n", atomic_read(&xbus->pcm_nesting)); } atomic_inc(&xbus->pcm_nesting); up_read(&xbus->in_use); } } void got_sync_from(xpd_t *xpd) { xbus_t *xbus; xbus = xpd->xbus; if(xpd != sync_master) { static int rate_limit; if((rate_limit++ % 1003) == 0) XPD_DBG(SYNC, xpd, "is not SYNC master. Go away! (%d)\n", rate_limit); return; } atomic_inc(&missed_ticks); if(!pcm_tasklet) { xpp_tick(0L); return; } tasklet_schedule(&tasklet_tick); } #ifdef ZAPTEL_SYNC_TICK static void send_drift(int drift) { struct timeval now; const char *msg; xbus_t *xbus; BUG_ON(!sync_master); xbus = sync_master->xbus; BUG_ON(drift < SYNC_ADJ_MIN || drift > SYNC_ADJ_MAX); do_gettimeofday(&now); if(drift == 0) msg = "stop"; else if(drift > 0) msg = "up"; else msg = "down"; XBUS_DBG(SYNC, xbus, "DRIFT adjust %s (%d) (last update %ld seconds ago)\n", msg, drift, now.tv_sec - xbus->pll_updated_at); CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, NULL, SYNC_MODE_PLL, drift); xbus->pll_updated_at = now.tv_sec; } int zaptel_sync_tick(struct zt_span *span, int is_master) { xpd_t *xpd = span->pvt; int offset; if(!sync_tick_active) goto noop; /* * Detect if any of our spans is zaptel sync master */ if(is_master) { sync_tick_nomaster = 0; /* Yes */ have_sync_mastership = 1; } if(sync_tick_nomaster < MAX_BUSES * MAX_XPDS) { sync_tick_nomaster++; /* Maybe */ goto noop; } /* Now we know for sure someone else is zaptel sync master */ have_sync_mastership = 0; BUG_ON(!xpd); /* * Calculate only if: * - HOST sync (for information only) * - Called for the sync_master (the function is called * for each span, so we don't want to do it multiple times). */ if(sync_master && sync_master != xpd) return 0; zaptel_tick_count++; zaptel_is_ticking = 1; flip_parport_bit(1); do_gettimeofday(&ticked_zaptel); usec_lag_curr = (ticked_zaptel.tv_sec - ticked_xpp.tv_sec)*1000*1000 + (ticked_zaptel.tv_usec - ticked_xpp.tv_usec); if(unlikely(usec_lag_curr > ZAPTEL_BIG_LAG)) { static int rate_limit; if((rate_limit++ % 1003) == 0) DBG(SYNC, "Big lag of Xpp ticks relative to zaptel ticks: %ld\n", usec_lag_curr); } sigma_lag += usec_lag_curr; if((zaptel_tick_count % SYNC_ADJ_INTERVAL) == 0) { average_lag = sigma_lag / SYNC_ADJ_INTERVAL; sigma_lag = 0; if(sync_master == xpd) { xbus_t *xbus = sync_master->xbus; /* Calculate required fix */ offset = (average_lag - 500) / SYNC_ADJ_FACTOR; if(offset < SYNC_ADJ_MIN) offset = SYNC_ADJ_MIN; if(offset > SYNC_ADJ_MAX) offset = SYNC_ADJ_MAX; xbus->sync_adjustment_offset = offset; } } return 0; noop: zaptel_is_ticking = 0; sigma_lag = 0; return 0; /* No auto sync from zaptel */ } #endif static void xpd_free(xpd_t *xpd) { xbus_t *xbus = NULL; if(!xpd) return; xbus = xpd->xbus; if(!xbus) return; XPD_DBG(GENERAL, xpd, "\n"); #ifdef CONFIG_PROC_FS if(xpd->proc_xpd_dir) { if(xpd->proc_xpd_summary) { XPD_DBG(PROC, xpd, "Removing proc '%s'\n", PROC_XPD_SUMMARY); remove_proc_entry(PROC_XPD_SUMMARY, xpd->proc_xpd_dir); xpd->proc_xpd_summary = NULL; } if(xpd->proc_xpd_ztregister) { XPD_DBG(PROC, xpd, "Removing proc '%s'\n", PROC_XPD_ZTREGISTER); remove_proc_entry(PROC_XPD_ZTREGISTER, xpd->proc_xpd_dir); xpd->proc_xpd_ztregister = NULL; } if(xpd->proc_xpd_blink) { XPD_DBG(PROC, xpd, "Removing proc '%s'\n", PROC_XPD_BLINK); remove_proc_entry(PROC_XPD_BLINK, xpd->proc_xpd_dir); xpd->proc_xpd_blink = NULL; } XPD_DBG(PROC, xpd, "Removing proc directory\n"); remove_proc_entry(xpd->xpdname, xbus->proc_xbus_dir); xpd->proc_xpd_dir = NULL; } #endif xbus_unregister_xpd(xbus, xpd); if(xpd->readchunk) kfree((void *)xpd->readchunk); xpd->readchunk = NULL; if(xpd->xproto) xproto_put(xpd->xproto); xpd->xproto = NULL; kfree(xpd); } /*------------------------- XPD Management -------------------------*/ #define REV(x,y) (10 * (x) + (y)) static byte good_revs[] = { REV(2,6), }; #undef REV static bool good_rev(byte rev) { int i; for(i = 0; i < ARRAY_SIZE(good_revs); i++) { if(good_revs[i] == rev) return 1; } return 0; } /* * Synchronous part of XPD detection. * Called from xbus_poll() */ void card_detected(struct card_desc_struct *card_desc) { xbus_t *xbus; xpd_t *xpd = NULL; byte type; byte subtype; int unit; int subunit; byte rev; const xops_t *xops; const xproto_table_t *proto_table; BUG_ON(!card_desc); BUG_ON(card_desc->magic != CARD_DESC_MAGIC); xbus = card_desc->xbus; type = card_desc->type; subtype = card_desc->subtype; unit = card_desc->xpd_addr.unit; subunit = card_desc->xpd_addr.subunit; rev = card_desc->rev; BUG_ON(!xbus); if(!good_rev(rev)) { XBUS_NOTICE(xbus, "XPD at %d%d: type=%d.%d has bad firmware revision %d.%d\n", unit, subunit, type, subtype, rev / 10, rev % 10); goto err; } XBUS_INFO(xbus, "New XPD at %d%d type=%d.%d Revision %d.%d\n", unit, subunit, type, subtype, rev / 10, rev % 10); xpd = xpd_byaddr(xbus, unit, subunit); if(xpd) { if(type == XPD_TYPE_NOMODULE) { XBUS_NOTICE(xbus, "XPD at %d%d removed\n", unit, subunit); BUG(); goto out; } XPD_NOTICE(xpd, "XPD at %d%d already exists\n", unit, subunit); goto out; } if(type == XPD_TYPE_NOMODULE) { XBUS_NOTICE(xbus, "XPD at %d%d Missing\n", unit, subunit); goto out; } proto_table = xproto_get(type); if(!proto_table) { XBUS_NOTICE(xbus, "XPD at %d%d: missing protocol table for type=%d. Ignored.\n", unit, subunit, type); goto out; } xops = &proto_table->xops; BUG_ON(!xops); xpd = xops->card_new(xbus, unit, subunit, proto_table, subtype, rev); if(!xpd) { XBUS_NOTICE(xbus, "card_new(%d,%d,%d,%d,%d) failed. Ignored.\n", unit, subunit, proto_table->type, subtype, rev); xproto_put(proto_table); goto err; } xpd->addr = card_desc->xpd_addr; xpd->xbus_idx = XPD_IDX(unit,subunit); snprintf(xpd->xpdname, XPD_NAMELEN, "XPD-%1d%1d", unit, subunit); xpd->subtype = card_desc->subtype; xpd->offhook = card_desc->line_status; /* For USB-1 disable some channels */ if(xbus->max_packet_size < 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 packet size = %d, disabling some PCM channels. no_pcm=0x%04X\n", xbus->max_packet_size, xpd->no_pcm); } xbus_register_xpd(xbus, xpd); #ifdef CONFIG_PROC_FS XPD_DBG(PROC, xpd, "Creating proc directory\n"); xpd->proc_xpd_dir = proc_mkdir(xpd->xpdname, xbus->proc_xbus_dir); if(!xpd->proc_xpd_dir) { XPD_ERR(xpd, "Failed to create proc directory\n"); goto err; } xpd->proc_xpd_summary = create_proc_read_entry(PROC_XPD_SUMMARY, 0444, xpd->proc_xpd_dir, xpd_read_proc, xpd); if(!xpd->proc_xpd_summary) { XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_XPD_SUMMARY); goto err; } xpd->proc_xpd_summary->owner = THIS_MODULE; xpd->proc_xpd_ztregister = create_proc_entry(PROC_XPD_ZTREGISTER, 0644, xpd->proc_xpd_dir); if (!xpd->proc_xpd_ztregister) { XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_XPD_ZTREGISTER); goto err; } xpd->proc_xpd_ztregister->owner = THIS_MODULE; xpd->proc_xpd_ztregister->data = xpd; xpd->proc_xpd_ztregister->read_proc = proc_xpd_ztregister_read; xpd->proc_xpd_ztregister->write_proc = proc_xpd_ztregister_write; xpd->proc_xpd_blink = create_proc_entry(PROC_XPD_BLINK, 0644, xpd->proc_xpd_dir); if (!xpd->proc_xpd_blink) { XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_XPD_BLINK); goto err; } xpd->proc_xpd_blink->owner = THIS_MODULE; xpd->proc_xpd_blink->data = xpd; xpd->proc_xpd_blink->read_proc = proc_xpd_blink_read; xpd->proc_xpd_blink->write_proc = proc_xpd_blink_write; #endif if(CALL_XMETHOD(card_init, xbus, xpd) < 0) goto err; CALL_XMETHOD(XPD_STATE, xbus, xpd, 0); /* Turn off all channels */ CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, NULL, SYNC_MODE_HOST, 0); /* Not a SYNC master (yet) */ xpd->card_present = 1; if(xbus->last_tx_sync.tv_sec == 0 && xbus->last_tx_sync.tv_usec == 0) { do_gettimeofday(&xbus->last_tx_sync); /* start timing ticks */ xbus->last_rx_sync = xbus->last_tx_sync; } CALL_XMETHOD(XPD_STATE, xbus, xpd, 1); /* Turn on all channels */ if(zap_autoreg) zaptel_register_xpd(xpd); out: memset(card_desc, 0, sizeof(struct card_desc_struct)); kfree(card_desc); return; err: xpd_free(xpd); goto out; } #ifdef CONFIG_PROC_FS /** * Prints a general procfs entry for the bus, under xpp/BUSNAME/summary * @page TODO: figure out procfs * @start TODO: figure out procfs * @off TODO: figure out procfs * @count TODO: figure out procfs * @eof TODO: figure out procfs * @data an xbus_t pointer with the bus data. */ static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; xpd_t *xpd = data; xbus_t *xbus; int i; if(!xpd) goto out; xbus = xpd->xbus; len += sprintf(page + len, "%s (%s ,card %s, span %s) %s\n" "timer_count: %d span->mainttimer=%d\n" , xpd->xpdname, xpd->type_name, (xpd->card_present) ? "present" : "missing", (SPAN_REGISTERED(xpd)) ? "registered" : "NOT registered", (xpd == sync_master) ? "SYNC MASTER" : "SYNC SLAVE", xpd->timer_count, xpd->span.mainttimer ); len += sprintf(page + len, "Address: U=%d S=%d\n", xpd->addr.unit, xpd->addr.subunit); len += sprintf(page + len, "Type: %d.%d\n\n", xpd->type, xpd->subtype); len += sprintf(page + len, "pcm_len=%d\n\n", xpd->pcm_len); len += sprintf(page + len, "wanted_pcm_mask=0x%04X\n\n", xpd->wanted_pcm_mask); len += sprintf(page + len, "STATES:"); len += sprintf(page + len, "\n\t%-17s: ", "output_relays"); for_each_line(xpd, i) { len += sprintf(page + len, "%d ", IS_SET(xpd->digital_outputs, i)); } len += sprintf(page + len, "\n\t%-17s: ", "input_relays"); for_each_line(xpd, i) { len += sprintf(page + len, "%d ", IS_SET(xpd->digital_inputs, i)); } len += sprintf(page + len, "\n\t%-17s: ", "offhook"); for_each_line(xpd, i) { len += sprintf(page + len, "%d ", IS_SET(xpd->offhook, i)); } len += sprintf(page + len, "\n\t%-17s: ", "cid_on"); for_each_line(xpd, i) { len += sprintf(page + len, "%d ", IS_SET(xpd->cid_on, i)); } len += sprintf(page + len, "\n\t%-17s: ", "msg_waiting"); for_each_line(xpd, i) { len += sprintf(page + len, "%d ", IS_SET(xpd->msg_waiting, i)); } len += sprintf(page + len, "\n\t%-17s: ", "ringing"); for_each_line(xpd, i) { len += sprintf(page + len, "%d ", xpd->ringing[i]); } len += sprintf(page + len, "\n\t%-17s: ", "no_pcm"); for_each_line(xpd, i) { len += sprintf(page + len, "%d ", IS_SET(xpd->no_pcm, i)); } #if 1 if(SPAN_REGISTERED(xpd)) { len += sprintf(page + len, "\nPCM:\n | [readchunk] | [writechunk] | pass"); for_each_line(xpd, i) { struct zt_chan *chans = xpd->span.chans; byte rchunk[ZT_CHUNKSIZE]; byte wchunk[ZT_CHUNKSIZE]; byte *rp; byte *wp; int j; if(IS_SET(xpd->digital_outputs, i)) continue; if(IS_SET(xpd->digital_inputs, i)) continue; if(IS_SET(xpd->digital_signalling, i)) continue; #if 0 rp = chans[i].readchunk; #else rp = (byte *)xpd->readchunk + (ZT_CHUNKSIZE * i); #endif wp = chans[i].writechunk; memcpy(rchunk, rp, ZT_CHUNKSIZE); memcpy(wchunk, wp, ZT_CHUNKSIZE); len += sprintf(page + len, "\n port %2d> | ", i); for(j = 0; j < ZT_CHUNKSIZE; j++) { len += sprintf(page + len, "%02X ", rchunk[j]); } len += sprintf(page + len, " | "); for(j = 0; j < ZT_CHUNKSIZE; j++) { len += sprintf(page + len, "%02X ", wchunk[j]); } len += sprintf(page + len, " | %c", (IS_SET(xpd->wanted_pcm_mask, i))?'+':' '); } } #endif #if 0 if(SPAN_REGISTERED(xpd)) { len += sprintf(page + len, "\nSignalling:\n"); for_each_line(xpd, i) { struct zt_chan *chan = &xpd->span.chans[i]; len += sprintf(page + len, "\t%2d> sigcap=0x%04X sig=0x%04X\n", i, chan->sigcap, chan->sig); } } #endif len += sprintf(page + len, "\nCOUNTERS:\n"); for(i = 0; i < XPD_COUNTER_MAX; i++) { len += sprintf(page + len, "\t\t%-20s = %d\n", xpd_counters[i].name, xpd->counters[i]); } len += sprintf(page + len, "<-- len=%d\n", len); out: if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len > count) len = count; if (len < 0) len = 0; return len; } #endif /* * xpd_alloc - Allocator for new XPD's * */ xpd_t *xpd_alloc(size_t privsize, const xproto_table_t *proto_table, int channels) { xpd_t *xpd = NULL; size_t alloc_size = sizeof(xpd_t) + privsize; int type = proto_table->type; DBG(GENERAL, "type=%d channels=%d\n", type, channels); if(channels > CHANNELS_PERXPD) { ERR("%s: type=%d: too many channels %d\n", __FUNCTION__, type, channels); goto err; } if((xpd = kmalloc(alloc_size, GFP_KERNEL)) == NULL) { ERR("%s: type=%d: Unable to allocate memory\n", __FUNCTION__, type); goto err; } memset(xpd, 0, alloc_size); xpd->priv = (byte *)xpd + sizeof(xpd_t); spin_lock_init(&xpd->lock); xpd->xbus = NULL; xpd->xbus_idx = -1; xpd->channels = channels; xpd->chans = NULL; xpd->card_present = 0; xpd->offhook = 0x0; /* ONHOOK */ xpd->type = proto_table->type; xpd->xproto = proto_table; xpd->xops = &proto_table->xops; xpd->digital_outputs = 0; xpd->digital_inputs = 0; atomic_set(&xpd->zt_registered, 0); atomic_set(&xpd->open_counter, 0); xpd->chans = kmalloc(sizeof(struct zt_chan)*xpd->channels, GFP_KERNEL); if (xpd->chans == NULL) { ERR("%s: Unable to allocate channels\n", __FUNCTION__); goto err; } alloc_size = ZT_MAX_CHUNKSIZE * CHANNELS_PERXPD * 2; /* Double Buffer */ if((xpd->readchunk = kmalloc(alloc_size, GFP_KERNEL)) == NULL) { ERR("%s: Unable to allocate memory for readchunk\n", __FUNCTION__); goto err; } /* Initialize read buffers to all blank data */ memset((void *)xpd->readchunk, 0, alloc_size); return xpd; err: if(xpd) { if(xpd->chans) kfree((void *)xpd->chans); if(xpd->readchunk) kfree((void *)xpd->readchunk); kfree(xpd); } return NULL; } /* FIXME: this should be removed once digium patch their zaptel.h * I simply wish to avoid changing zaptel.h in the xpp patches. */ #ifndef ZT_EVENT_REMOVED #define ZT_EVENT_REMOVED (20) #endif void xpd_disconnect(xpd_t *xpd) { unsigned long flags; BUG_ON(!xpd); // TODO: elect a new sync master if(sync_master == xpd) { sync_master_is("DISCONNECT", NULL); elect_syncer("DISCONNECT"); } spin_lock_irqsave(&xpd->lock, flags); XPD_DBG(GENERAL, xpd, "(%p)\n", xpd->xproto); if(!xpd->card_present) /* Multiple reports */ goto out; xpd->card_present = 0; if(SPAN_REGISTERED(xpd)) { int i; update_xpd_status(xpd, ZT_ALARM_NOTOPEN); /* TODO: Should this be done before releasing the spinlock? */ XPD_DBG(GENERAL, xpd, "Queuing ZT_EVENT_REMOVED on all channels to ask user to release them\n"); for (i=0; ispan.channels; i++) zt_qevent_lock(&xpd->chans[i],ZT_EVENT_REMOVED); } out: spin_unlock_irqrestore(&xpd->lock, flags); } void xpd_remove(xpd_t *xpd) { xbus_t *xbus; BUG_ON(!xpd); xbus = xpd->xbus; XPD_INFO(xpd, "Remove\n"); zaptel_unregister_xpd(xpd); CALL_XMETHOD(card_remove, xbus, xpd); xpd_free(xpd); } void update_xpd_status(xpd_t *xpd, int alarm_flag) { struct zt_span *span = &xpd->span; if(!SPAN_REGISTERED(xpd)) { // XPD_NOTICE(xpd, "%s: XPD is not registered. Skipping.\n", __FUNCTION__); return; } switch (alarm_flag) { case ZT_ALARM_NONE: xpd->last_response = jiffies; break; default: // Nothing break; } if(span->alarms == alarm_flag) return; span->alarms = alarm_flag; zt_alarm_notify(span); XPD_DBG(GENERAL, xpd, "Update XPD alarms: %s -> %02X\n", xpd->span.name, alarm_flag); } void update_line_status(xpd_t *xpd, int pos, bool to_offhook) { zt_rxsig_t rxsig; BUG_ON(!xpd); if(to_offhook) { BIT_SET(xpd->offhook, pos); rxsig = ZT_RXSIG_OFFHOOK; } else { BIT_CLR(xpd->offhook, pos); BIT_CLR(xpd->cid_on, pos); rxsig = ZT_RXSIG_ONHOOK; } /* * We should not spinlock before calling zt_hooksig() as * it may call back into our xpp_hooksig() and cause * a nested spinlock scenario */ LINE_DBG(GENERAL, xpd, pos, "rxsig=%s\n", (rxsig == ZT_RXSIG_ONHOOK) ? "ONHOOK" : "OFFHOOK"); if(SPAN_REGISTERED(xpd)) zt_hooksig(&xpd->chans[pos], rxsig); } #ifdef CONFIG_PROC_FS int proc_sync_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; unsigned int xpp_timer_rate; unsigned int jiffies_now; len += sprintf(page + len, "# To modify sync source write into this file:\n"); len += sprintf(page + len, "# HOST - For host based sync\n"); len += sprintf(page + len, "# SYNC=nn - XBUS-nn provide sync\n"); len += sprintf(page + len, "# QUERY=nn - Query XBUS-nn for sync information (DEBUG)\n"); if(!sync_master) len += sprintf(page + len, "HOST\n"); else { len += sprintf(page + len, "SYNC=%02d\n", sync_master->xbus->num); len += sprintf(page + len, "PLL DRIFT=%d\n", sync_master->xbus->sync_adjustment); } #ifdef ZAPTEL_SYNC_TICK if(sync_tick_active) { if(!zaptel_is_ticking) { len += sprintf(page + len, "Zaptel Reference Sync Not activated\n"); goto noextsync; } len += sprintf(page + len, "Zaptel Reference Sync (%s):\n", (have_sync_mastership)?"xpp":"external"); len += sprintf(page + len, "\tzaptel_tick: #%d\n", zaptel_tick_count); len += sprintf(page + len, "\ttick - zaptel_tick = %d\n", xpp_timer_count - zaptel_tick_count); len += sprintf(page + len, "\t%-19s: %5ld (usec)\n", "current lag", usec_lag_curr); len += sprintf(page + len, "\t%-19s: %5ld (usec)\n", "average lag", average_lag); if(sync_master) { struct timeval now; xbus_t *xbus = sync_master->xbus; do_gettimeofday(&now); len += sprintf(page + len, "\t%-19s: %5ld seconds ago\n", "PLL updated", (xbus->pll_updated_at == 0) ? 0 : now.tv_sec - xbus->pll_updated_at); len += sprintf(page + len, "\t%-19s: %5d\n", "offset", xbus->sync_adjustment_offset); } } noextsync: #endif len += sprintf(page + len, "\ntick: #%d\n", xpp_timer_count); xpp_timer_rate = 0; jiffies_now = jiffies; if(jiffies_now - xpp_last_jiffies > 0) { unsigned long delta = (jiffies_now - xpp_last_jiffies); xpp_timer_rate = (xpp_timer_count % SAMPLE_TICKS) * HZ / delta; len += sprintf(page + len, "tick rate: %4d/second (SAMPLE_TICKS=%d)\n", xpp_timer_rate, SAMPLE_TICKS); } if(pcm_tasklet) len += sprintf(page + len, "TASKLETS: missed_ticks: %d\n", atomic_read(&missed_ticks)); if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len > count) len = count; if (len < 0) len = 0; return len; } static int proc_sync_write(struct file *file, const char __user *buffer, unsigned long count, void *data) { char buf[MAX_PROC_WRITE]; int xbus_num; int xpd_num; xbus_t *xbus; // DBG(SYNC, "%s: count=%ld\n", __FUNCTION__, count); if(count >= MAX_PROC_WRITE) return -EINVAL; if(copy_from_user(buf, buffer, count)) return -EFAULT; buf[count] = '\0'; if(strncmp("HOST", buf, 4) == 0) { sync_master_is("PROC", NULL); } else if(sscanf(buf, "SYNC=%d", &xbus_num) == 1) { DBG(SYNC, "SYNC=%d\n", xbus_num); if((xbus = xbus_of(xbus_num)) == NULL) { ERR("No bus %d exists\n", xbus_num); return -ENXIO; } sync_master_is("PROC", xbus); } else if(sscanf(buf, "QUERY=%d", &xbus_num) == 1) { DBG(SYNC, "QUERY=%d\n", xbus_num); if((xbus = xbus_of(xbus_num)) == NULL) { ERR("No bus %d exists\n", xbus_num); return -ENXIO; } CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, NULL, SYNC_MODE_QUERY, 0); } else if(sscanf(buf, "%d %d", &xbus_num, &xpd_num) == 2) { NOTICE("Using deprecated syntax to update /proc/%s/%s\n", PROC_DIR, PROC_SYNC); if(xpd_num != 0) { ERR("Currently can only set sync for XPD #0\n"); return -EINVAL; } if((xbus = xbus_of(xbus_num)) == NULL) { ERR("No bus %d exists\n", xbus_num); return -ENXIO; } sync_master_is("PROC", xbus); } else { ERR("%s: cannot parse '%s'\n", __FUNCTION__, buf); count = -EINVAL; } return count; } int proc_xpd_ztregister_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; unsigned long flags; xpd_t *xpd = data; BUG_ON(!xpd); spin_lock_irqsave(&xpd->lock, flags); len += sprintf(page + len, "%d\n", SPAN_REGISTERED(xpd)); spin_unlock_irqrestore(&xpd->lock, flags); if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len > count) len = count; if (len < 0) len = 0; return len; } static int proc_xpd_ztregister_write(struct file *file, const char __user *buffer, unsigned long count, void *data) { xpd_t *xpd = data; char buf[MAX_PROC_WRITE]; int zt_reg; int ret; BUG_ON(!xpd); if(count >= MAX_PROC_WRITE) return -EINVAL; if(copy_from_user(buf, buffer, count)) return -EFAULT; buf[count] = '\0'; ret = sscanf(buf, "%d", &zt_reg); if(ret != 1) return -EINVAL; XPD_DBG(GENERAL, xpd, "%s\n", (zt_reg) ? "register" : "unregister"); if(zt_reg) ret = zaptel_register_xpd(xpd); else ret = zaptel_unregister_xpd(xpd); return (ret < 0) ? ret : count; } int proc_xpd_blink_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; unsigned long flags; xpd_t *xpd = data; BUG_ON(!xpd); spin_lock_irqsave(&xpd->lock, flags); len += sprintf(page + len, "%d\n", xpd->blink_mode); spin_unlock_irqrestore(&xpd->lock, flags); if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len > count) len = count; if (len < 0) len = 0; return len; } static int proc_xpd_blink_write(struct file *file, const char __user *buffer, unsigned long count, void *data) { xpd_t *xpd = data; char buf[MAX_PROC_WRITE]; int blink; int ret; BUG_ON(!xpd); if(count >= MAX_PROC_WRITE) return -EINVAL; if(copy_from_user(buf, buffer, count)) return -EFAULT; buf[count] = '\0'; ret = sscanf(buf, "%d", &blink); if(ret != 1) return -EINVAL; XPD_DBG(GENERAL, xpd, "%s\n", (blink) ? "blink" : "unblink"); xpd->blink_mode = blink; return count; } #endif #define XPP_MAX_LEN 512 /*------------------------- Zaptel Interfaces ----------------------*/ void pcm_recompute(xpd_t *xpd, xpp_line_t pcm_mask) { int i; int line_count = 0; unsigned long flags; pcm_mask &= ~xpd->digital_signalling; /* without D-Channel */ for_each_line(xpd, i) if(IS_SET(pcm_mask, i)) line_count++; /* * FIXME: Workaround a bug in sync code of the Astribank. * Send dummy PCM for sync. */ if(xpd->addr.unit == 0 && pcm_mask == 0) { pcm_mask = BIT(0); line_count = 1; } spin_lock_irqsave(&xpd->lock, flags); xpd->pcm_len = (line_count) ? RPACKET_HEADERSIZE + sizeof(xpp_line_t) + line_count * ZT_CHUNKSIZE : 0L; xpd->wanted_pcm_mask = pcm_mask; spin_unlock_irqrestore(&xpd->lock, flags); } void fill_beep(u_char *buf, int num, int duration) { bool alternate = (duration) ? (jiffies/(duration*1000)) & 0x1 : 0; int which; u_char *snd; /* * debug tones */ static u_char beep[] = { 0x7F, 0xBE, 0xD8, 0xBE, 0x80, 0x41, 0x24, 0x41, /* Dima */ 0x67, 0x90, 0x89, 0x90, 0xFF, 0x10, 0x09, 0x10, /* Izzy */ }; static u_char beep_alt[] = { 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, /* silence */ }; if(alternate) { which = num % ARRAY_SIZE(beep_alt); snd = &beep_alt[which]; } else { which = num % ARRAY_SIZE(beep); snd = &beep[which]; } memcpy(buf, snd, ZT_CHUNKSIZE); } #ifdef XPP_EC_CHUNK /* * Taken from zaptel.c */ static inline void xpp_ec_chunk(struct zt_chan *chan, unsigned char *rxchunk, const unsigned char *txchunk) { int16_t rxlin; int x; unsigned long flags; /* Perform echo cancellation on a chunk if necessary */ if (!chan->ec) return; spin_lock_irqsave(&chan->lock, flags); for (x=0;xec, ZT_XLAW(txchunk[x], chan), rxlin); rxchunk[x] = ZT_LIN2X((int)rxlin, chan); } spin_unlock_irqrestore(&chan->lock, flags); } #endif static void do_ec(xpd_t *xpd) { struct zt_chan *chans = xpd->span.chans; int i; #if WITH_ECHO_SUPPRESSION /* FIXME: need to Echo cancel double buffered data */ for (i = 0;i < xpd->span.channels; i++) { if(unlikely(IS_SET(xpd->digital_signalling, i))) /* Don't echo cancel PRI/BRI D-chans */ continue; #ifdef XPP_EC_CHUNK /* even if defined, parameterr xpp_ec can override at run-time */ if (xpp_ec) xpp_ec_chunk(&chans[i], chans[i].readchunk, xpd->ec_chunk2[i]); else #endif zt_ec_chunk(&chans[i], chans[i].readchunk, xpd->ec_chunk2[i]); memcpy(xpd->ec_chunk2[i], xpd->ec_chunk1[i], ZT_CHUNKSIZE); memcpy(xpd->ec_chunk1[i], chans[i].writechunk, ZT_CHUNKSIZE); } #endif } static void xpp_receiveprep(xpd_t *xpd) { volatile u_char *readchunk; int i; int channels = xpd->channels; struct zt_chan *chans = xpd->span.chans; unsigned long flags; spin_lock_irqsave(&xpd->lock, flags); // if((xpd->timer_count % 1000) == 0) // XPD_DBG(GENERAL, xpd, "%d\n", xpd->timer_count); if (xpd->timer_count & 1) { /* First part */ readchunk = xpd->readchunk; } else { readchunk = xpd->readchunk + ZT_CHUNKSIZE * CHANNELS_PERXPD; } for (i = 0; i < channels; i++, readchunk += ZT_CHUNKSIZE) { /* * We don't copy signalling buffers (they may be * longer than ZT_CHUNKSIZE). */ if(unlikely(IS_SET(xpd->digital_signalling, i))) continue; if(IS_SET(xpd->offhook, i) || IS_SET(xpd->cid_on, i) || IS_BRI(xpd)) { // memset((u_char *)readchunk, 0x5A, ZT_CHUNKSIZE); // DEBUG // fill_beep((u_char *)readchunk, 1, 1); // DEBUG: BEEP memcpy(chans[i].readchunk, (u_char *)readchunk, ZT_CHUNKSIZE); } else { memset(chans[i].readchunk, 0x7F, ZT_CHUNKSIZE); // SILENCE } } do_ec(xpd); spin_unlock_irqrestore(&xpd->lock, flags); /* * This should be out of spinlocks, as it may call back our hook setting * methods */ zt_receive(&xpd->span); } /* * Called from zaptel with spinlock held on chan. Must not call back * zaptel functions. */ int xpp_open(struct zt_chan *chan) { xpd_t *xpd = chan->pvt; xbus_t *xbus = xpd->xbus; int pos = chan->chanpos - 1; unsigned long flags; spin_lock_irqsave(&xbus->lock, flags); xbus->open_counter++; atomic_inc(&xpd->open_counter); if(IS_SET(xpd->digital_signalling, pos)) /* D-chan offhook */ BIT_SET(xpd->offhook, pos); DBG(GENERAL, "chan=%d (open_counter=%d)\n", pos, xbus->open_counter); spin_unlock_irqrestore(&xbus->lock, flags); if(xpd->xops->card_open) xpd->xops->card_open(xpd, pos); return 0; } int xpp_close(struct zt_chan *chan) { xpd_t *xpd = chan->pvt; xbus_t *xbus = xpd->xbus; int pos = chan->chanpos - 1; unsigned long flags; bool should_remove = 0; spin_lock_irqsave(&xbus->lock, flags); xbus->open_counter--; atomic_dec(&xpd->open_counter); if (!xbus->hardware_exists && xbus->open_counter == 0) should_remove = 1; if(IS_SET(xpd->digital_signalling, pos)) /* D-chan onhook */ BIT_CLR(xpd->offhook, pos); spin_unlock_irqrestore(&xbus->lock, flags); if(xpd->xops->card_close) xpd->xops->card_close(xpd, pos); DBG(GENERAL, "chan=%d (open_counter=%d, should_remove=%d)\n", pos, xbus->open_counter, should_remove); if(should_remove) { XBUS_DBG(GENERAL, xbus, "Going to remove\n"); xbus_remove(xbus); } return 0; } int xpp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg) { xpd_t *xpd = chan->pvt; int pos = chan->chanpos - 1; int ret = 0; switch (cmd) { default: /* Some span-specific commands before we give up: */ if (xpd->xops->card_ioctl != NULL) { ret = xpd->xops->card_ioctl(xpd, pos, cmd, arg); if (ret != -ENOTTY) return ret; } DBG(GENERAL, "ENOTTY: chan=%d cmd=0x%x\n", pos, cmd); DBG(GENERAL, " IOC_TYPE=0x%02X\n", _IOC_TYPE(cmd)); DBG(GENERAL, " IOC_DIR=0x%02X\n", _IOC_DIR(cmd)); DBG(GENERAL, " IOC_NR=0x%02X\n", _IOC_NR(cmd)); DBG(GENERAL, " IOC_SIZE=0x%02X\n", _IOC_SIZE(cmd)); return -ENOTTY; } return 0; } static int xpp_hooksig(struct zt_chan *chan, zt_txsig_t txsig) { xpd_t *xpd = chan->pvt; xbus_t *xbus; int pos = chan->chanpos - 1; BUG_ON(!xpd); xbus = xpd->xbus; BUG_ON(!xbus); DBG(GENERAL, "Setting %s to %s (%d)\n", chan->name, txsig2str(txsig), txsig); return CALL_XMETHOD(card_hooksig, xbus, xpd, pos, txsig); } /* Req: Set the requested chunk size. This is the unit in which you must report results for conferencing, etc */ int xpp_setchunksize(struct zt_span *span, int chunksize); /* Enable maintenance modes */ int xpp_maint(struct zt_span *span, int cmd) { xpd_t *xpd = span->pvt; int ret = 0; #if 0 char loopback_data[] = "THE-QUICK-BROWN-FOX-JUMPED-OVER-THE-LAZY-DOG"; #endif BUG_ON(!xpd); DBG(GENERAL, "span->mainttimer=%d\n", span->mainttimer); switch(cmd) { case ZT_MAINT_NONE: printk("XXX Turn off local and remote loops XXX\n"); break; case ZT_MAINT_LOCALLOOP: printk("XXX Turn on local loopback XXX\n"); break; case ZT_MAINT_REMOTELOOP: printk("XXX Turn on remote loopback XXX\n"); break; case ZT_MAINT_LOOPUP: printk("XXX Send loopup code XXX\n"); // CALL_XMETHOD(LOOPBACK_AX, xpd->xbus, xpd, loopback_data, ARRAY_SIZE(loopback_data)); break; case ZT_MAINT_LOOPDOWN: printk("XXX Send loopdown code XXX\n"); break; case ZT_MAINT_LOOPSTOP: printk("XXX Stop sending loop codes XXX\n"); break; default: ERR("XPP: Unknown maint command: %d\n", cmd); ret = -EINVAL; break; } if (span->mainttimer || span->maintstat) update_xpd_status(xpd, ZT_ALARM_LOOPBACK); return ret; } #if 0 /* Okay, now we get to the signalling. You have several options: */ /* Option 1: If you're a T1 like interface, you can just provide a rbsbits function and we'll assert robbed bits for you. Be sure to set the ZT_FLAG_RBS in this case. */ /* Opt: If the span uses A/B bits, set them here */ int (*rbsbits)(struct zt_chan *chan, int bits); /* Option 2: If you don't know about sig bits, but do have their equivalents (i.e. you can disconnect battery, detect off hook, generate ring, etc directly) then you can just specify a sethook function, and we'll call you with appropriate hook states to set. Still set the ZT_FLAG_RBS in this case as well */ int (*hooksig)(struct zt_chan *chan, zt_txsig_t hookstate); /* Option 3: If you can't use sig bits, you can write a function which handles the individual hook states */ int (*sethook)(struct zt_chan *chan, int hookstate); #endif static int xpp_echocan(struct zt_chan *chan, int len) { #ifdef XPP_EC_CHUNK if(len == 0) { /* shut down */ /* zaptel calls this also during channel initialization */ if(chan->ec) { xpp_echo_can_free(chan->ec); } return 0; } if(chan->ec) { ERR("%s: Trying to override an existing EC (%p)\n", __FUNCTION__, chan->ec); return -EINVAL; } chan->ec = xpp_echo_can_create(len, 0); if(!chan->ec) { ERR("%s: Failed creating xpp EC (len=%d)\n", __FUNCTION__, len); return -EINVAL; } #endif return 0; } #ifdef CONFIG_ZAPTEL_WATCHDOG /* * If the watchdog detects no received data, it will call the * watchdog routine */ static int xpp_watchdog(struct zt_span *span, int cause) { static int rate_limit = 0; if((rate_limit++ % 1000) == 0) DBG(GENERAL, "\n"); return 0; } #endif /** * Unregister an xpd from zaptel and release related resources * @xpd The xpd to be unregistered * @returns 0 on success, errno otherwise * * Checks that nobody holds an open channel. * * Called by: * - User action through /proc * - During xpd_remove() */ static int zaptel_unregister_xpd(xpd_t *xpd) { unsigned long flags; BUG_ON(!xpd); spin_lock_irqsave(&xpd->lock, flags); if(!SPAN_REGISTERED(xpd)) { XPD_NOTICE(xpd, "Already unregistered\n"); spin_unlock_irqrestore(&xpd->lock, flags); return -EIDRM; } update_xpd_status(xpd, ZT_ALARM_NOTOPEN); if(atomic_read(&xpd->open_counter)) { XPD_NOTICE(xpd, "Busy (open_counter=%d). Skipping.\n", atomic_read(&xpd->open_counter)); spin_unlock_irqrestore(&xpd->lock, flags); return -EBUSY; } mdelay(2); // FIXME: This is to give chance for transmit/receiveprep to finish. spin_unlock_irqrestore(&xpd->lock, flags); if(xpd->card_present) xpd->xops->card_zaptel_preregistration(xpd, 0); atomic_dec(&xpd->zt_registered); zt_unregister(&xpd->span); #ifdef ZAPTEL_SYNC_TICK zaptel_is_ticking = 0; #endif if(xpd->card_present) xpd->xops->card_zaptel_postregistration(xpd, 0); return 0; } static int zaptel_register_xpd(xpd_t *xpd) { struct zt_span *span; xbus_t *xbus; int cn; const xops_t *xops; BUG_ON(!xpd); xops = xpd->xops; xbus = xpd->xbus; if (SPAN_REGISTERED(xpd)) { XPD_ERR(xpd, "Already registered\n"); return -EEXIST; } cn = xpd->channels; XPD_DBG(GENERAL, xpd, "Initializing span: %d channels.\n", cn); memset(xpd->chans, 0, sizeof(struct zt_chan)*cn); memset(&xpd->span, 0, sizeof(struct zt_span)); span = &xpd->span; snprintf(span->name, MAX_SPANNAME, "%s/%s", xbus->busname, xpd->xpdname); span->deflaw = ZT_LAW_MULAW; init_waitqueue_head(&span->maintq); span->pvt = xpd; span->channels = cn; span->chans = xpd->chans; span->open = xpp_open; span->close = xpp_close; span->flags = ZT_FLAG_RBS; span->hooksig = xpp_hooksig; /* Only with RBS bits */ span->ioctl = xpp_ioctl; span->maint = xpp_maint; #ifdef ZAPTEL_SYNC_TICK span->sync_tick = zaptel_sync_tick; #endif if (xpp_ec) span->echocan = xpp_echocan; #ifdef CONFIG_ZAPTEL_WATCHDOG span->watchdog = xpp_watchdog; #endif snprintf(xpd->span.desc, MAX_SPANDESC, "Xorcom XPD #%02d/%1d%1d: %s", xbus->num, xpd->addr.unit, xpd->addr.subunit, xpd->type_name); XPD_DBG(GENERAL, xpd, "Registering span '%s'\n", xpd->span.desc); xpd->xops->card_zaptel_preregistration(xpd, 1); if(zt_register(&xpd->span, prefmaster)) { XPD_ERR(xpd, "Failed to zt_register span\n"); return -ENODEV; } atomic_inc(&xpd->zt_registered); xpd->xops->card_zaptel_postregistration(xpd, 1); /* * Update zaptel about our state */ #if 0 /* * FIXME: since asterisk didn't open the channel yet, the report * is discarded anyway. OTOH, we cannot report in xpp_open or * xpp_chanconfig since zaptel call them with a spinlock on the channel * and zt_hooksig tries to acquire the same spinlock, resulting in * double spinlock deadlock (we are lucky that RH/Fedora kernel are * compiled with spinlock debugging).... tough. */ for_each_line(xpd, cn) { struct zt_chan *chans = xpd->span.chans; if(IS_SET(xpd->offhook, cn)) { LINE_NOTICE(xpd, cn, "Report OFFHOOK to zaptel\n"); zt_hooksig(&chans[cn], ZT_RXSIG_OFFHOOK); } } #endif return 0; } /*------------------------- Proc debugging interface ---------------*/ #ifdef CONFIG_PROC_FS #if 0 static int xpp_zap_write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data) { } #endif #endif /*------------------------- Initialization -------------------------*/ static void do_cleanup(void) { if(timer_pending(&xpp_timer)) del_timer_sync(&xpp_timer); tasklet_kill(&tasklet_tick); #ifdef CONFIG_PROC_FS DBG(GENERAL, "Removing '%s' from proc\n", PROC_SYNC); remove_proc_entry(PROC_SYNC, xpp_proc_toplevel); if(xpp_proc_toplevel) { DBG(GENERAL, "Removing '%s' from proc\n", PROC_DIR); remove_proc_entry(PROC_DIR, NULL); xpp_proc_toplevel = NULL; } #endif } int __init xpp_zap_init(void) { int ret = 0; struct proc_dir_entry *ent; INFO("revision %s MAX_XPDS=%d (%d*%d)\n", XPP_VERSION, MAX_XPDS, MAX_UNIT, MAX_SUBUNIT); #if WITH_ECHO_SUPPRESSION INFO("FEATURE: with ECHO_SUPPRESSION\n"); #else INFO("FEATURE: without ECHO_SUPPRESSION\n"); #endif if (xpp_ec) INFO("FEATURE: with XPP_EC_CHUNK\n"); else INFO("FEATURE: without XPP_EC_CHUNK\n"); #ifdef CONFIG_ZAPATA_BRI_DCHANS INFO("FEATURE: with BRISTUFF support\n"); #else INFO("FEATURE: without BRISTUFF support\n"); #endif #ifdef ZAPTEL_SYNC_TICK INFO("FEATURE: with sync_tick() from ZAPTEL\n"); #else INFO("FEATURE: without sync_tick() from ZAPTEL\n"); #endif #ifdef OPTIMIZE_CHANMUTE INFO("FEATURE: with CHANMUTE optimization\n"); #endif #ifdef CONFIG_PROC_FS xpp_proc_toplevel = proc_mkdir(PROC_DIR, NULL); if(!xpp_proc_toplevel) { ret = -EIO; goto err; } ent = create_proc_entry(PROC_SYNC, 0644, xpp_proc_toplevel); if(!ent) { ret = -EFAULT; goto err; } ent->read_proc = proc_sync_read; ent->write_proc = proc_sync_write; ent->data = NULL; #endif ret = xbus_core_init(); if(ret) { ERR("xbus_core_init failed (%d)\n", ret); goto err; } /* Only timer init. We add it only *after* zt_register */ init_timer(&xpp_timer); sync_master_is("INIT", NULL); /* Internal ticking */ return 0; err: do_cleanup(); return ret; } void __exit xpp_zap_cleanup(void) { xbus_core_shutdown(); do_cleanup(); } EXPORT_SYMBOL(print_dbg); EXPORT_SYMBOL(card_detected); EXPORT_SYMBOL(xpd_alloc); EXPORT_SYMBOL(xpd_disconnect); EXPORT_SYMBOL(pcm_recompute); EXPORT_SYMBOL(generic_card_pcm_fromspan); EXPORT_SYMBOL(generic_card_pcm_tospan); EXPORT_SYMBOL(update_xpd_status); EXPORT_SYMBOL(update_line_status); EXPORT_SYMBOL(fill_beep); EXPORT_SYMBOL(xpp_open); EXPORT_SYMBOL(xpp_close); EXPORT_SYMBOL(xpp_ioctl); EXPORT_SYMBOL(xpp_maint); EXPORT_SYMBOL(elect_syncer); MODULE_DESCRIPTION("XPP Zaptel Driver"); MODULE_AUTHOR("Oron Peled "); MODULE_LICENSE("GPL"); MODULE_VERSION(XPP_VERSION); module_init(xpp_zap_init); module_exit(xpp_zap_cleanup);