/* * 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 "zaptel.h" #include /* For zaptel version */ #include "xbus-core.h" #include "xproto.h" #include "xpp_zap.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_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; DEF_PARM(int, print_dbg, 0, "Print DBG statements"); DEF_PARM(int, max_queue_len, MAX_QUEUE_LEN, "Maximum Queue Length."); DEF_PARM(bool, have_sync_bus, 0, "True if all Astribank(TM) devices are connected via a sync-cable"); DEF_PARM(bool, zap_autoreg, 1, "Register spans automatically (1) or not (0)"); #include "zap_debug.h" #ifdef XPP_EC_CHUNK #include "echo_supress/ec_xpp.h" #endif static int zaptel_register_xpd(xpd_t *xpd); static int zaptel_unregister_xpd(xpd_t *xpd); static void xpp_transmitprep(xpd_t *xpd); static void xpp_receiveprep(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 void xpd_free(xpd_t *xpd); static void external_sync(xpd_t *the_xpd) { int i, j; DBG("SYNC %s (%s sync cable)\n", (the_xpd)?"Astribanks":"HOST", (have_sync_bus)?"with":"without"); // Shut all down 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) { CALL_XMETHOD(SYNC_SOURCE, xbus, xpd, 1, 0); } } } if(the_xpd) CALL_XMETHOD(SYNC_SOURCE, the_xpd->xbus, the_xpd, 1, 1); } void sync_master_is(xpd_t *xpd) { DBG("SYNC MASTER CHANGED: %s => %s\n", (sync_master) ? sync_master->xpdname : "HOST", (xpd) ? xpd->xpdname : "HOST"); sync_master = xpd; if(xpd) { // XPD del_timer_sync(&xpp_timer); xpp_tick((unsigned long)xpd); } 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 xpp_tick(unsigned long param) { xbus_t *xbus; xpd_t *the_xpd = (xpd_t *)param; int i; int j; if(!the_xpd) { /* Called from timer */ #if 0 static int rate_limit = 0; if(rate_limit++ % 1000 == 0) DBG("FROM_TIMER\n"); #endif mod_timer(&xpp_timer, jiffies + 1); /* Must be 1KHz rate */ } else if(the_xpd != sync_master) return; /* 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)) { DBG("Dropped packet. %s is in_use\n", xbus->busname); continue; } #if 0 if(xbus->open_counter == 0) continue; // optimize, but zttool loopback won't function #endif for(j = 0; j < MAX_XPDS; j++) { xpd_t *xpd = xpd_of(xbus, j); if(!xpd) continue; if(!xpd->card_present) continue; xpd->timer_count++; CALL_XMETHOD(card_tick, xbus, xpd); if(!SPAN_REGISTERED(xpd)) continue; xpp_transmitprep(xpd); xpp_receiveprep(xpd); } up_read(&xbus->in_use); } } #if HZ != 1000 #warning "xpp_timer must be sampled EXACTLY 1000/per second" #endif static void xpd_free(xpd_t *xpd) { xbus_t *xbus = NULL; if(!xpd) return; xbus = xpd->xbus; if(!xbus) return; DBG("%s/%s\n", xbus->busname, xpd->xpdname); xbus_unregister_xpd(xbus, xpd); #ifdef CONFIG_PROC_FS if(xpd->proc_xpd_dir) { if(xpd->proc_xpd_summary) { DBG("Removing proc '%s' for %s/%s\n", PROC_XPD_SUMMARY, xbus->busname, xpd->xpdname); remove_proc_entry(PROC_XPD_SUMMARY, xpd->proc_xpd_dir); xpd->proc_xpd_summary = NULL; } if(xpd->proc_xpd_ztregister) { DBG("Removing proc '%s' for %s/%s\n", PROC_XPD_ZTREGISTER, xbus->busname, xpd->xpdname); remove_proc_entry(PROC_XPD_ZTREGISTER, xpd->proc_xpd_dir); xpd->proc_xpd_ztregister = NULL; } DBG("Removing proc directory for %s/%s\n", xbus->busname, xpd->xpdname); remove_proc_entry(xpd->xpdname, xbus->proc_xbus_dir); xpd->proc_xpd_dir = NULL; } #endif if(xpd->writechunk) kfree((void *)xpd->writechunk); xpd->writechunk = 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(1,9), REV(2,0), }; #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; int xpd_num; byte type; 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; xpd_num = xpd_addr2num(&card_desc->xpd_addr); type = card_desc->type; rev = card_desc->rev; BUG_ON(!xbus); if(!good_rev(rev)) { NOTICE("%s: New XPD #%d (%d-%d) type=%d has bad firmware revision %d.%d\n", xbus->busname, xpd_num, card_desc->xpd_addr.unit, card_desc->xpd_addr.subunit, type, rev / 10, rev % 10); goto err; } INFO("%s: New XPD #%d (%d-%d) type=%d Revision %d.%d\n", xbus->busname, xpd_num, card_desc->xpd_addr.unit, card_desc->xpd_addr.subunit, type, rev / 10, rev % 10); xpd = xpd_of(xbus, xpd_num); if(xpd) { if(type == XPD_TYPE_NOMODULE) { NOTICE("%s: xpd #%d: removed\n", __FUNCTION__, xpd_num); BUG(); goto out; } NOTICE("%s: xpd #%d: already exists\n", __FUNCTION__, xpd_num); goto out; } if(type == XPD_TYPE_NOMODULE) { DBG("No module at address=%d\n", xpd_num); goto out; } proto_table = xproto_get(type); if(!proto_table) { NOTICE("%s: xpd #%d: missing protocol table for type=%d. Ignored.\n", __FUNCTION__, xpd_num, type); goto out; } xops = &proto_table->xops; BUG_ON(!xops); xpd = xops->card_new(xbus, xpd_num, proto_table, rev); if(!xpd) { NOTICE("card_new(%s,%d,%d,%d) failed. Ignored.\n", xbus->busname, xpd_num, proto_table->type, rev); goto err; } xpd->addr = card_desc->xpd_addr; /* 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; NOTICE("%s: max packet size = %d, disabling some PCM channels. no_pcm=0x%04X\n", xbus->busname, xbus->max_packet_size, xpd->no_pcm); } #ifdef CONFIG_PROC_FS DBG("Creating xpd proc directory for %s/%s\n", xbus->busname, xpd->xpdname); xpd->proc_xpd_dir = proc_mkdir(xpd->xpdname, xbus->proc_xbus_dir); if(!xpd->proc_xpd_dir) { ERR("Failed to create proc directory for %s/%s\n", xbus->busname, xpd->xpdname); 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) { ERR("Failed to create proc '%s' for %s/%s\n", PROC_XPD_SUMMARY, xbus->busname, xpd->xpdname); 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) { ERR("Failed to create proc '%s' for %s/%s\n", PROC_XPD_ZTREGISTER, xbus->busname, xpd->xpdname); 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; #endif xbus_register_xpd(xbus, xpd); if(CALL_XMETHOD(card_init, xbus, xpd) < 0) goto err; // Turn off all channels CALL_XMETHOD(CHAN_ENABLE, xbus, xpd, ~0, 0); xpd->card_present = 1; // Turn on all channels CALL_XMETHOD(CHAN_ENABLE, xbus, xpd, ALL_LINES, 1); 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, xproto_name(xpd->type), (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, "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: ", "lasttxhook"); for_each_line(xpd, i) { len += sprintf(page + len, "%d ", xpd->lasttxhook[i]); } len += sprintf(page + len, "\n\t%-17s: ", "idletxhookstate"); for_each_line(xpd, i) { len += sprintf(page + len, "%d ", xpd->idletxhookstate[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] | delay"); 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 1 rp = chans[i].readchunk; wp = chans[i].writechunk; #else rp = (byte *)xpd->readchunk + (ZT_CHUNKSIZE * i); wp = (byte *)xpd->writechunk + (ZT_CHUNKSIZE * i); #endif 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, " | %d ", xpd->delay_until_dialtone[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, xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, int channels, byte revision) { xpd_t *xpd = NULL; size_t pcm_size; size_t alloc_size = sizeof(xpd_t) + privsize; int i; DBG("%s: xpd #%d\n", xbus->busname, xpd_num); if(!VALID_XPD_NUM(xpd_num)) { ERR("%s: illegal xpd id = %d\n", __FUNCTION__, xpd_num); goto err; } if(channels > CHANNELS_PERXPD) { ERR("%s: too many channels %d for xpd #%d\n", __FUNCTION__, channels, xpd_num); goto err; } if((xpd = kmalloc(alloc_size, GFP_KERNEL)) == NULL) { ERR("%s: Unable to allocate memory for xpd #%d\n", __FUNCTION__, xpd_num); goto err; } memset(xpd, 0, alloc_size); xpd->priv = (byte *)xpd + sizeof(xpd_t); spin_lock_init(&xpd->lock); xpd->xbus = xbus; xpd->id = xpd_num; xpd->channels = channels; xpd->chans = NULL; xpd->card_present = 0; snprintf(xpd->xpdname, XPD_NAMELEN, "XPD-%d", xpd_num); 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; for_each_line(xpd, i) { xpd->idletxhookstate[i] = FXS_LINE_ENABLED; /* By default, don't send on hook */ } 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; } pcm_size = ZT_MAX_CHUNKSIZE * CHANNELS_PERXPD * 2; /* Double Buffer */ alloc_size = pcm_size * 2; /* Read/Write */ if((xpd->writechunk = kmalloc(alloc_size, GFP_KERNEL)) == NULL) { ERR("%s: Unable to allocate memory for writechunks\n", __FUNCTION__); goto err; } /* Initialize Write/Buffers to all blank data */ memset((void *)xpd->writechunk, 0x00, alloc_size); xpd->readchunk = xpd->writechunk + pcm_size; return xpd; err: if(xpd) { if(xpd->chans) kfree((void *)xpd->chans); if(xpd->writechunk) kfree((void *)xpd->writechunk); 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(NULL); spin_lock_irqsave(&xpd->lock, flags); DBG("%s/%s (%p)\n", xpd->xbus->busname, xpd->xpdname, 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? */ DBG("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; INFO("%s: Remove XPD #%d from\n", xbus->busname, xpd->id); 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)) { NOTICE("%s: %s is not registered. Skipping.\n", __FUNCTION__, xpd->xpdname); 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); DBG("Update XPD alarms: %s -> %02X\n", xpd->span.name, alarm_flag); } void update_line_status(xpd_t *xpd, int pos, bool to_offhook) { struct zt_chan *chan; BUG_ON(!xpd); if(!SPAN_REGISTERED(xpd)) return; chan = &xpd->chans[pos]; /* * We should not spinlock before calling zt_hooksig() as * it may call back into our xpp_hooksig() and cause * a nested spinlock scenario */ if(to_offhook) { BIT_SET(xpd->offhook, pos); zt_hooksig(chan, ZT_RXSIG_OFFHOOK); } else { BIT_CLR(xpd->offhook, pos); BIT_CLR(xpd->cid_on, pos); zt_hooksig(chan, ZT_RXSIG_ONHOOK); } } void update_zap_ring(xpd_t *xpd, int pos, bool on) { struct zt_chan *chan; BUG_ON(!xpd); if(!SPAN_REGISTERED(xpd)) return; chan = &xpd->chans[pos]; /* * We should not spinlock before calling zt_hooksig() as * it may call back into our xpp_hooksig() and cause * a nested spinlock scenario */ if(on) { BIT_CLR(xpd->cid_on, pos); zt_hooksig(chan, ZT_RXSIG_RING); } else { BIT_SET(xpd->cid_on, pos); zt_hooksig(chan, ZT_RXSIG_OFFHOOK); } } #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 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, "# 0 0 - XBUS-0/XPD-0 provide sync\n"); len += sprintf(page + len, "# m n - XBUS-m/XPD-n provide sync\n"); if(!sync_master) len += sprintf(page + len, "HOST\n"); else len += sprintf(page + len, "%s/%s\n", sync_master->xbus->busname, sync_master->xpdname); len += sprintf(page + len, "tick: #%d\n", xpp_timer_count); xpp_timer_rate = 0; now = jiffies; if(now - xpp_last_jiffies > 0) { xpp_timer_rate = ((xpp_timer_count % SAMPLE_TICKS) * 1000) / (now - xpp_last_jiffies); len += sprintf(page + len, "tick rate: %4d/second (average over %d seconds)\n", xpp_timer_rate, SAMPLE_TICKS/HZ); } 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; xpd_t *xpd; int ret; bool setit; // DBG("%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(NULL); goto out; } ret = sscanf(buf, "%d %d %d", &xbus_num, &xpd_num, &setit); if(ret == 2) { // For backward compatibility: before query was introduced, // only two parameters were possible setit = 1; ret = 3; } if(ret != 3 || (setit != 0 && setit != 1)) { ERR("Bad format for SYNC.\n"); ERR("Usage: <0/1> # 0 - QUERY, 1 - SET\n"); return -EINVAL; } if(xbus_num >= MAX_BUSES) { ERR("Invalid xbus number %d\n", xbus_num); return -EINVAL; } xbus = xbus_of(xbus_num); if(!xbus) { ERR("No bus %d exists\n", xbus_num); return -EINVAL; } xpd = xpd_of(xbus, xpd_num); if(!xpd) { ERR("%s: XPD number %d does not exist\n", __FUNCTION__, xpd_num); return -ENXIO; } DBG("%s: %d/%d %s\n", __FUNCTION__, xbus_num, xpd_num, (setit)?"SET":"QUERY"); if(setit) external_sync(xpd); out: 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]; bool 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; DBG("%s: %s/%s %s\n", __FUNCTION__, xpd->xbus->busname, xpd->xpdname, (zt_reg) ? "register" : "unregister"); if(zt_reg) ret = zaptel_register_xpd(xpd); else ret = zaptel_unregister_xpd(xpd); return (ret < 0) ? ret : count; } #endif /** * * Packet is freed: * - In case of error, by this function. * - Otherwise, by the underlying sending mechanism */ int packet_send(xbus_t *xbus, xpacket_t *pack_tx) { int ret = -ENODEV; int toxpd; if(!pack_tx) { DBG("null pack\n"); return -EINVAL; } toxpd = XPD_NUM(pack_tx->content.addr); if(!xbus) { DBG("null xbus\n"); ret = -EINVAL; goto error; } if (!xbus->hardware_exists) { DBG("xbus %s Dropped a packet -- NO HARDWARE.", xbus->busname); ret = -ENODEV; goto error; } if(!VALID_XPD_NUM(toxpd)) { ERR("%s: toxpd=%d > MAX_XPDS\n", __FUNCTION__, toxpd); ret = -EINVAL; goto error; } #if 0 // DEBUG: For Dima if(pack_tx->content.opcode == XPP_PCM_WRITE) { static int rate_limit; static int count; if(sync_master == NULL) count = 0; if(count++ > 5) { ret = 0; goto error; } if(rate_limit++ % 1000 == 0) INFO("DEBUG: TRANSMIT (PCM_WRITE)\n"); } #endif if(down_read_trylock(&xbus->in_use)) { ret = xbus->ops->packet_send(xbus, pack_tx); XBUS_COUNTER(xbus, TX_BYTES) += pack_tx->datalen; up_read(&xbus->in_use); } else { DBG("Dropped packet. %s is in_use\n", xbus->busname); } return ret; error: xbus->ops->packet_free(xbus, pack_tx); return ret; } #define XPP_MAX_LEN 512 /*------------------------- Zaptel Interfaces ----------------------*/ #define PREP_REPORT_RATE 1000 static void xpp_transmitprep(xpd_t *xpd) { volatile u_char *writechunk; volatile u_char *w; int ret; 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 % PREP_REPORT_RATE) < 10) // DBG("%d\n", xpd->timer_count); if (xpd->timer_count & 1) { /* First part */ w = writechunk = xpd->writechunk /* + 1 */; } else { w = writechunk = xpd->writechunk + ZT_CHUNKSIZE * CHANNELS_PERXPD /* + 1 */; } spin_unlock_irqrestore(&xpd->lock, flags); /* * This should be out of spinlocks, as it may call back our hook setting * methods */ zt_transmit(&xpd->span); spin_lock_irqsave(&xpd->lock, flags); for (i = 0; i < channels; i++) { if (xpd->delay_until_dialtone[i] > 0) { xpd->delay_until_dialtone[i]--; if (xpd->delay_until_dialtone[i] <= 0) { xpd->delay_until_dialtone[i] = 0; wake_up_interruptible(&xpd->txstateq[i]); } } if(IS_SET(xpd->offhook, i) || IS_SET(xpd->cid_on, i)) { memcpy((u_char *)w, chans[i].writechunk, ZT_CHUNKSIZE); // fill_beep((u_char *)w, 5); } w += ZT_CHUNKSIZE; } // if(xpd->offhook != 0 || sync_master != xpd) { ret = CALL_XMETHOD(PCM_WRITE, xpd->xbus, xpd, xpd->offhook | xpd->cid_on, writechunk); if(ret < 0) { DBG("failed to write PCM %d\n", ret); } // } spin_unlock_irqrestore(&xpd->lock, flags); } void fill_beep(u_char *buf, int duration) { int which = (jiffies/(duration*HZ)) & 0x3; /* * debug tones */ static u_char beep[] = { // 0x7F, 0xBE, 0xD8, 0xBE, 0x80, 0x41, 0x24, 0x41, /* Dima */ // 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, /* silence */ // 0x67, 0x90, 0x89, 0x90, 0xFF, 0x10, 0x09, 0x10, /* Izzy */ // 0x67, 0xCD, 0xC5, 0xCD, 0xFF, 0x49, 0x41, 0x49, /* Dima 2 */ 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, /* silence */ }; memcpy(buf, &beep[(which*8) % ARRAY_SIZE(beep)], 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) { short 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 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 % PREP_REPORT_RATE) == 0) // DBG("%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++) { if(IS_SET(xpd->offhook, i) || IS_SET(xpd->cid_on, i)) { // memset((u_char *)readchunk, 0x5A, ZT_CHUNKSIZE); // DEBUG // fill_beep((u_char *)readchunk, 1); // DEBUG: BEEP memcpy(chans[i].readchunk, (u_char *)readchunk, ZT_CHUNKSIZE); } else { memset(chans[i].readchunk, 0x7F, ZT_CHUNKSIZE); // SILENCE } readchunk += ZT_CHUNKSIZE; } #if WITH_ECHO_SUPPRESSION /* FIXME: need to Echo cancel double buffered data */ for (i = 0;i < xpd->span.channels; i++) { #ifdef XPP_EC_CHUNK xpp_ec_chunk(&chans[i], chans[i].readchunk, xpd->ec_chunk2[i]); #else zt_ec_chunk(&chans[i], chans[i].readchunk, xpd->ec_chunk2[i]); #endif memcpy(xpd->ec_chunk2[i], xpd->ec_chunk1[i], ZT_CHUNKSIZE); memcpy(xpd->ec_chunk1[i], chans[i].writechunk, ZT_CHUNKSIZE); } #endif 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); } static int xpp_startup(struct zt_span *span) { DBG("\n"); return 0; } /* * Called only for 'span' keyword in /etc/zaptel.conf */ static int xpp_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) { xpd_t *xpd = span->pvt; DBG("%s\n", xpd->xpdname); return 0; } /* * Called only for 'span' keyword in /etc/zaptel.conf */ static int xpp_shutdown(struct zt_span *span) { xpd_t *xpd = span->pvt; DBG("%s\n", xpd->xpdname); return 0; } int xpp_open(struct zt_chan *chan) { xpd_t *xpd = chan->pvt; xbus_t *xbus = xpd->xbus; unsigned long flags; spin_lock_irqsave(&xbus->lock, flags); xbus->open_counter++; atomic_inc(&xpd->open_counter); DBG("chan=%d (open_counter=%d)\n", chan->chanpos, xbus->open_counter); spin_unlock_irqrestore(&xbus->lock, flags); return 0; } int xpp_close(struct zt_chan *chan) { xpd_t *xpd = chan->pvt; xbus_t *xbus = xpd->xbus; unsigned long flags; bool should_remove = 0; spin_lock_irqsave(&xbus->lock, flags); xbus->open_counter--; atomic_dec(&xpd->open_counter); if(xpd->direction == TO_PHONE) { /* Hangup phone */ xpd->idletxhookstate[chan->chanpos - 1] = FXS_LINE_ENABLED; } if (!xbus->hardware_exists && xbus->open_counter == 0) should_remove = 1; spin_unlock_irqrestore(&xbus->lock, flags); DBG("chan=%d (open_counter=%d, should_remove=%d)\n", chan->chanpos, xbus->open_counter, should_remove); if(should_remove) { DBG("Going to remove: %s\n", xbus->busname); 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 x; switch (cmd) { case ZT_ONHOOKTRANSFER: if (get_user(x, (int __user *)arg)) return -EFAULT; xpd->ohttimer[pos] = x << 3; xpd->idletxhookstate[pos] = FXS_LINE_CID; /* OHT mode when idle */ if (xpd->lasttxhook[pos] == FXS_LINE_ENABLED) { /* Apply the change if appropriate */ CALL_XMETHOD(CHAN_CID, xpd->xbus, xpd, pos); // CALLER ID } DBG("xpd=%d: ZT_ONHOOKTRANSFER (%d millis) chan=%d\n", xpd->id, x, pos); return -ENOTTY; case ZT_TONEDETECT: if (get_user(x, (int __user *)arg)) return -EFAULT; DBG("xpd=%d: ZT_TONEDETECT chan=%d: TONEDETECT_ON=%d TONEDETECT_MUTE=%d\n", xpd->id, pos, (x & ZT_TONEDETECT_ON), (x & ZT_TONEDETECT_MUTE)); return -ENOTTY; default: /* Some span-specific commands before we give up: */ if (xpd->xops->card_ioctl != NULL) { x = xpd->xops->card_ioctl(xpd, pos, cmd, arg); if (x != -ENOTTY) return x; } DBG("ENOTTY: chan=%d cmd=0x%x\n", pos, cmd); DBG(" IOC_TYPE=0x%02X\n", _IOC_TYPE(cmd)); DBG(" IOC_DIR=0x%02X\n", _IOC_DIR(cmd)); DBG(" IOC_NR=0x%02X\n", _IOC_NR(cmd)); DBG(" 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("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("%s: span->mainttimer=%d\n", __FUNCTION__, 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; } /* Set signalling type (if appropriate) */ static int xpp_chanconfig(struct zt_chan *chan, int sigtype) { DBG("channel %d (%s) -> %s\n", chan->channo, chan->name, sig2str(sigtype)); // FIXME: sanity checks: // - should be supported (within the sigcap) // - should not replace fxs <->fxo ??? (covered by previous?) return 0; } #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 #ifdef XPP_EC_CHUNK static int xpp_echocan(struct zt_chan *chan, int len) { 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; } return 0; } #endif #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("\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)) { NOTICE("%s/%s is already unregistered\n", xpd->xbus->busname, xpd->xpdname); spin_unlock_irqrestore(&xpd->lock, flags); return -EIDRM; } if(sync_master == xpd) sync_master_is(NULL); // FIXME: it's better to elect a new prince update_xpd_status(xpd, ZT_ALARM_NOTOPEN); if(atomic_read(&xpd->open_counter)) { NOTICE("%s/%s is busy (open_counter=%d). Skipping.\n", xpd->xbus->busname, xpd->xpdname, 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); 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; if (SPAN_REGISTERED(xpd)) { ERR("xpd %s already registered\n", xpd->xpdname); return -EEXIST; } cn = xpd->channels; DBG("Initializing span: xpd %d have %d channels.\n", xpd->id, cn); 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); span->deflaw = ZT_LAW_MULAW; init_waitqueue_head(&span->maintq); span->pvt = xpd; span->channels = cn; span->chans = xpd->chans; span->startup = xpp_startup; span->shutdown = xpp_shutdown; span->spanconfig = xpp_spanconfig; span->chanconfig = xpp_chanconfig; 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 XPP_EC_CHUNK span->echocan = xpp_echocan; #endif #ifdef CONFIG_ZAPTEL_WATCHDOG span->watchdog = xpp_watchdog; #endif DBG("Registering span of %s.\n", xpd->xpdname); xpd->xops->card_zaptel_preregistration(xpd, 1); if(zt_register(&xpd->span, 1)) { xbus_t *xbus = xpd->xbus; ERR("%s/%s: Failed to zt_register span\n", xbus->busname, xpd->xpdname); return -ENODEV; } atomic_inc(&xpd->zt_registered); xpd->xops->card_zaptel_postregistration(xpd, 1); 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); #ifdef CONFIG_PROC_FS DBG("Removing '%s' from proc\n", PROC_SYNC); remove_proc_entry(PROC_SYNC, xpp_proc_toplevel); if(xpp_proc_toplevel) { DBG("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; struct proc_dir_entry *ent; INFO("%s revision %s MAX_XPDS=%d\n", THIS_MODULE->name, ZAPTEL_VERSION, MAX_XPDS); #if WITH_ECHO_SUPPRESSION INFO("FEATURE: %s (with ECHO_SUPPRESSION)\n", THIS_MODULE->name); #else INFO("FEATURE: %s (without ECHO_SUPPRESSION)\n", THIS_MODULE->name); #endif #ifdef XPP_EC_CHUNK INFO("FEATURE: %s (with XPP_EC_CHUNK)\n", THIS_MODULE->name); #else INFO("FEATURE: %s (without XPP_EC_CHUNK)\n", THIS_MODULE->name); #endif #ifdef CONFIG_PROC_FS xpp_proc_toplevel = proc_mkdir(PROC_DIR, NULL); if(!xpp_proc_toplevel) { do_cleanup(); return -EIO; } ent = create_proc_entry(PROC_SYNC, 0644, xpp_proc_toplevel); if(!ent) { do_cleanup(); return -EFAULT; } 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); do_cleanup(); return ret; } /* Only timer init. We add it only *after* zt_register */ init_timer(&xpp_timer); sync_master_is(NULL); /* Internal ticking */ return 0; } 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(packet_send); EXPORT_SYMBOL(update_xpd_status); EXPORT_SYMBOL(update_zap_ring); EXPORT_SYMBOL(update_line_status); EXPORT_SYMBOL(fill_beep); EXPORT_SYMBOL(xpp_tick); EXPORT_SYMBOL(xpp_open); EXPORT_SYMBOL(xpp_close); EXPORT_SYMBOL(xpp_ioctl); EXPORT_SYMBOL(xpp_maint); MODULE_DESCRIPTION("XPP Zaptel Driver"); MODULE_AUTHOR("Oron Peled "); MODULE_LICENSE("GPL"); MODULE_VERSION(ZAPTEL_VERSION); module_init(xpp_zap_init); module_exit(xpp_zap_cleanup);