diff -urN dahdi-linux.orig/drivers/dahdi/dahdi_tdmox/dahdi_tdmox-base.c dahdi-linux/drivers/dahdi/dahdi_tdmox/dahdi_tdmox-base.c --- dahdi-linux.orig/drivers/dahdi/dahdi_tdmox/dahdi_tdmox-base.c 1970-01-01 07:00:00.000000000 +0700 +++ dahdi-linux/drivers/dahdi/dahdi_tdmox/dahdi_tdmox-base.c 2014-07-31 00:43:30.000000000 +0700 @@ -0,0 +1,1210 @@ +/* + * Dynamic Span Interface for DAHDI + * + * Written by Mark Spencer + * + * Copyright (C) 2001-2012, Digium, Inc. + * + * All rights reserved. + * + */ + +/* + Pavel Selivanov, 2009-2014 + Tasklet fixes. + Single tasklet for tx and (optionally) rx. See "rxfifo" param. + RX FIFO to handle jitter between dynamic SPAN's, DAHDI (timing=0) + TDMoX statistics in procfs, for diagnostics + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "dahdi_tdmox.h" +#include "dahdi_tdmox-sysfs.h" + +#ifndef DAHDI_SYNC_TICK +#error "Dynamic support depends on DAHDI_SYNC_TICK being enabled." +#endif + +/* + * Tasklets provide better system interactive response at the cost of the + * possibility of losing a frame of data at very infrequent intervals. If + * you are more concerned with the performance of your machine, enable the + * tasklets. If you are strict about absolutely no drops, then do not enable + * tasklets. + */ + +/* + * Dynamic spans implemented using TDM over X with standard message + * types. Message format is as follows: + * + * Byte #: Meaning + * 0 Number of samples per channel + * 1 Current flags on span + * Bit 0: Yellow Alarm + * Bit 1: Sig bits present + * Bits 2-7: reserved for future use + * 2-3 16-bit counter value for detecting drops, network byte order. + * 4-5 Number of channels in the message, network byte order + * 6... 16-bit words, containing sig bits for each + * four channels, least significant 4 bits being + * the least significant channel, network byte order. + * the rest data for each channel, all samples per channel + before moving to the next. + */ + +#define DAHDI_DYNAMIC_FLAG_YELLOW_ALARM (1 << 0) +#define DAHDI_DYNAMIC_FLAG_SIGBITS_PRESENT (1 << 1) +#define DAHDI_DYNAMIC_FLAG_LOOPBACK (1 << 2) + +#define ERR_NSAMP (1 << 16) +#define ERR_NCHAN (1 << 17) +#define ERR_LEN (1 << 18) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) +# define kfifo_in(fifo,buf,n) __kfifo_put(fifo,buf,n) +# define kfifo_in_spinlocked(fifo,buf,n,lock) kfifo_put(fifo,buf,n) +# define kfifo_out(fifo,buf,n) __kfifo_get(fifo,buf,n) +# define kfifo_out_spinlocked(fifo,buf,n,lock) kfifo_get(fifo,buf,n) +# define kfifo_size(fifo) (fifo->size) +# define my_kfifo_alloc(size,mask,lock) kfifo_alloc(size,mask,lock) +# define my_kfifo_free(fifo) kfifo_free(fifo) +#else // >= 2.6.35 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) /* 2.6.35 <= x < 2.6.36 */ +#define kfifo_in_spinlocked(fifo, buf, n, lock) \ + kfifo_in_locked(fifo, buf, n, lock) +#define kfifo_out_spinlocked(fifo, buf, n, lock) \ + kfifo_out_locked(fifo, buf, n, lock) +#endif // < KERNEL_VERSION(2,6,36) +static inline struct kfifo *my_kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock) { + int ret; + struct kfifo *fifo = kzalloc(sizeof(struct kfifo), gfp_mask); + ret = kfifo_alloc (fifo, size, gfp_mask); + return fifo; +} +static inline void my_kfifo_free(struct kfifo *fifo) { + kfifo_free (fifo); + kfree (fifo); +} +#endif // < KERNEL_VERSION(2,6,35) + +int debug = 0; /* Non-static. Used in dahdi_tdmox-sysfs.c */ +static int rxfifo = DAHDI_TDMOX_RXFIFO; +static int rxtasklet = 1; +static int noflood = 1; +static int tasklet_quota = DAHDI_TDMOX_TASKLET_QUOTA; +static int rxtx_time=1; +static int tasklet_time=0; + +/* +* As written at net/core/dev.c , interrupts MUST be enabled while calling dev_queue_xmit. +* Otherwise, we can get deadlock. +* As long, as sync_tick can be called in interrupt context (of another device/span), +* we MUST place our flush to tasklet/work/something else. +* So, it's not the questions, should we use tasklet or not. The questions is where. +*/ +static struct dahdi_tdmox_tasklet_stat dahdi_tdmox_tlstat, *tlstat=&dahdi_tdmox_tlstat; +static struct dahdi_tdmox_tasklet dahdi_tdmox_tlet, *tlet=&dahdi_tdmox_tlet; +static char *versionstr = DAHDI_TDMOX_VERSIONSTR; + +static int hasmaster = 0; +static int dynamic_master_tick; /* ticks from dynamic master SPAN. */ +static int dahdi_sync_tick; /* ticks from DAHDI to dynamic master SPAN */ + +static DEFINE_MUTEX(dspan_mutex); +static DEFINE_SPINLOCK(dspan_lock); +static DEFINE_SPINLOCK(driver_lock); + +static LIST_HEAD(dspan_list); +static LIST_HEAD(driver_list); + +static int dahdi_dynamic_init(void); +static void dahdi_dynamic_cleanup(void); +static void dahdi_dynamic_tasklet(unsigned long data); + +static inline long elapsed_ms(struct timeval *t1, struct timeval *t0) +{ + unsigned long elapsed; + + elapsed = (t1->tv_sec - t0->tv_sec)*1000000L; + elapsed = min(elapsed, 3000000UL); /* tv_usec sub can be negative */ + elapsed += t1->tv_usec - t0->tv_usec; + elapsed = min(elapsed, 2000000UL); /* we don't care if more */ + + return elapsed; +} + +static inline void _tdmox_span_clearstat (struct dahdi_tdmox *d) +{ + d->slip = d->skip = d->rxnuerr = 0; + memset(&d->rxtv, 0, sizeof(d->rxtv)); + memset(&d->txtv, 0, sizeof(d->txtv)); +} + +static void _dahdi_dynamic_clearstat (void) { + struct dahdi_tdmox *d; + + dynamic_master_tick = dahdi_sync_tick = 0; + memset(tlstat, 0, sizeof(*tlstat)); + + rcu_read_lock(); + list_for_each_entry_rcu(d, &dspan_list, list) { + _tdmox_span_clearstat(d); + } + rcu_read_unlock(); +} + +static void _dahdi_tdmox_save_tv(struct dahdi_tdmox_tv *dtv) +{ + struct timeval *tv = dtv->tv; + unsigned int elapsed; + + memmove (&(tv[1]), tv, sizeof(struct timeval)*(DAHDI_TDMOX_TVHIST-1)); + do_gettimeofday(&(tv[0])); + /* Should I do sanity check of tv_usec < 1000000 ? */ + + if (0 == tv[1].tv_sec && 0 == tv[1].tv_usec) { + dtv->min = dtv->max = 1000UL; + return; /* Have no hostory yet */ + } + elapsed = elapsed_ms(&tv[0], &tv[1]); + dtv->max = max(elapsed, dtv->max); + dtv->min = min(elapsed, dtv->min); +} + +static inline void dahdi_tdmox_save_tv(struct dahdi_tdmox_tv *dtv, int enable) +{ + if (enable) + _dahdi_tdmox_save_tv(dtv); +} + +static void checkmaster(void) +{ + int newhasmaster=0; + int best = 9999999; + struct dahdi_tdmox *d, *master = NULL; + + rcu_read_lock(); + + list_for_each_entry_rcu(d, &dspan_list, list) { + d->span.cannot_provide_timing = 1; + if (d->timing) { + d->master = 0; + d->span.syncsrc = 0; + if (!(d->span.alarms & DAHDI_ALARM_RED) && + (d->timing < best)) { + /* If not in alarm and they're + a better timing source, use them */ + master = d; + best = d->timing; + newhasmaster = 1; + } + } + } + + hasmaster = newhasmaster; + /* Mark the new master if there is one */ + if (master) { + /* Marking dynamic master SPAN with cannot_provide_timing = 0. + Thus, DAHDI will take this SPAN as a master for DAHDI. + In DAHDI < 2.6.0, We had to do the same with DAHDI_FLAG_RUNNING. */ + master->span.cannot_provide_timing = 0; + master->span.syncsrc = master->span.spanno; + master->master = 1; + dahdi_alarm_notify(&(master->span)); + } + + rcu_read_unlock(); + + if (master) + printk(KERN_INFO "TDMoX: New master: %s\n", master->span.name); + else + printk(KERN_INFO "TDMoX: No master.\n"); +} + +static void dahdi_dynamic_sendmessage(struct dahdi_tdmox *d) +{ + unsigned char *buf = d->msgbuf; + unsigned short bits; + int msglen = 0; + int x; + int offset; + + /* Byte 0: Number of samples per channel */ + *buf = DAHDI_CHUNKSIZE; + buf++; msglen++; + + /* Byte 1: Flags */ + *buf = 0; + if (d->span.alarms & DAHDI_ALARM_RED) + *buf |= DAHDI_DYNAMIC_FLAG_YELLOW_ALARM; + *buf |= DAHDI_DYNAMIC_FLAG_SIGBITS_PRESENT; + buf++; msglen++; + + /* Bytes 2-3: Transmit counter */ + *((unsigned short *)buf) = htons((unsigned short)d->txcnt); + d->txcnt++; + buf++; msglen++; + buf++; msglen++; + + /* Bytes 4-5: Number of channels */ + *((unsigned short *)buf) = htons((unsigned short)d->span.channels); + buf++; msglen++; + buf++; msglen++; + bits = 0; + offset = 0; + for (x = 0; x < d->span.channels; x++) { + offset = x % 4; + bits |= (d->chans[x]->txsig & 0xf) << (offset << 2); + if (offset == 3) { + /* Write the bits when we have four channels */ + *((unsigned short *)buf) = htons(bits); + buf++; msglen++; + buf++; msglen++; + bits = 0; + } + } + + if (offset != 3) { + /* Finish it off if it's not done already */ + *((unsigned short *)buf) = htons(bits); + buf++; msglen++; + buf++; msglen++; + } + + for (x = 0; x < d->span.channels; x++) { + memcpy(buf, d->chans[x]->writechunk, DAHDI_CHUNKSIZE); + buf += DAHDI_CHUNKSIZE; + msglen += DAHDI_CHUNKSIZE; + } + + d->driver->transmit(d, d->msgbuf, msglen); +} + +static int dahdi_dynamic_process_rxfifo (struct dahdi_tdmox *d) { + unsigned char *msg = d->msgbuf; + int x = d->span.channels * DAHDI_CHUNKSIZE; + + if (kfifo_out_spinlocked(d->rxfifo, msg, x, &d->rxfifo_lock) != x) { + ++d->slip; + return 1; + } + + for (x=0;xspan.channels;x++) { + memcpy(d->span.chans[x]->readchunk, msg, DAHDI_CHUNKSIZE); + msg += DAHDI_CHUNKSIZE; + } + return 0; +} + +static void dahdi_dynamic_run(void) { + int pending = atomic_read(&tlet->pending); + + tlstat->req++; + dahdi_tdmox_save_tv(&tlstat->time.req, tasklet_time); + + if (likely(pending < tasklet_quota)) + atomic_inc(&tlet->pending); + else + tlstat->errors++; + + tlstat->sched++; + dahdi_tdmox_save_tv(&tlstat->time.sched, tasklet_time); + tasklet_hi_schedule(&tlet->tlet); +} + +static void __dahdi_dynamic_rx(struct dahdi_tdmox *d) { + /* Don't call dahdi_receive if have no data. + Otherwise, we can have deadlock via process_masterspan & sync_tick. */ + if (likely(0 == dahdi_dynamic_process_rxfifo (d))) { + dahdi_ec_span(&d->span); + dahdi_receive(&d->span); + } +} + +static void dahdi_dynamic_rx(struct dahdi_tdmox *d) { + if (rxtasklet) { + /* + TODO: Schedule tasklet for this dynamic SPAN. It will be sheduled on the processor/core it was started from. + So, if NIC driver is good - every new IRQ/packet will run on the next processor/core. + */ + } else { + __dahdi_dynamic_rx(d); + } +} + +static void __dahdi_dynamic_tx(struct dahdi_tdmox *d) { + if (noflood && (d->span.alarms & DAHDI_ALARM_RED)) { + /* Flood protection: just send a probe twice once a second during red alarm */ + if (0 != (d->txcnt & 0x1FF)) { + ++d->txcnt; /* If sendmessage not called (not incremented txcnt) - do it here. */ + return; + } + } + dahdi_transmit(&d->span); /* Should we call dahdi_transmit if not going to send ? */ + dahdi_dynamic_sendmessage(d); +} + +static void dahdi_dynamic_flush(void) { + struct dahdi_tdmox_driver *drv; + + rcu_read_lock(); + list_for_each_entry_rcu(drv, &driver_list, list) { + /* Flush any traffic still pending in the driver */ + if (drv->flush) { + drv->flush(); + } + } + rcu_read_unlock(); +} + +static void __dahdi_dynamic_run(void) +{ + struct dahdi_tdmox *d; + + rcu_read_lock(); + list_for_each_entry_rcu(d, &dspan_list, list) { + if(rxtasklet) { + __dahdi_dynamic_rx(d); + } + __dahdi_dynamic_tx(d); /* Handle all transmission. */ + /* Should be called just before the final flush. + But, we have no idea about tx queue status of the driver (eth, maybe), + so, we don care about inaccurancy. */ + dahdi_tdmox_save_tv(&d->txtv, rxtx_time); + } + rcu_read_unlock(); + dahdi_dynamic_flush(); +} + +static void dahdi_dynamic_tasklet(unsigned long data) +{ + int i = 0; + + tlstat->run++; + dahdi_tdmox_save_tv(&tlstat->time.run, tasklet_time); + + for (i=0; atomic_read(&tlet->pending) > 0; ++i) { + if (unlikely(i >= tasklet_quota)) + break; + atomic_dec(&tlet->pending); + tlstat->exec++; + dahdi_tdmox_save_tv(&tlstat->time.exec, tasklet_time); + __dahdi_dynamic_run(); + } +} + +static inline struct dahdi_tdmox *dynamic_from_span(struct dahdi_span *span) +{ + return container_of(span, struct dahdi_tdmox, span); +} + +void dahdi_tdmox_receive(struct dahdi_span *span, unsigned char *msg, int msglen) +{ + struct dahdi_tdmox *dtd = dynamic_from_span(span); + int newerr=0; + int sflags; + int xlen; + int x, bits, sig; + int nchans, master; + int newalarm; + unsigned short rxpos, rxcnt; + + rcu_read_lock(); + + if (unlikely(msglen < 6)) { + rcu_read_unlock(); + newerr = ERR_LEN; + if (newerr != dtd->err) + printk(KERN_NOTICE "Span %s: Insufficient samples for header (only %d)\n", span->name, msglen); + dtd->err = newerr; + return; + } + + /* First, check the chunksize */ + if (unlikely(*msg != DAHDI_CHUNKSIZE)) { + rcu_read_unlock(); + newerr = ERR_NSAMP | msg[0]; + if (newerr != dtd->err) + printk(KERN_NOTICE "Span %s: Expected %d samples, but receiving %d\n", span->name, DAHDI_CHUNKSIZE, msg[0]); + dtd->err = newerr; + return; + } + msg++; + sflags = *msg; + msg++; + + rxpos = ntohs(*((unsigned short *)msg)); + msg++; + msg++; + + nchans = ntohs(*((unsigned short *)msg)); + if (unlikely(nchans != span->channels)) { + rcu_read_unlock(); + newerr = ERR_NCHAN | nchans; + if (newerr != dtd->err) + printk(KERN_NOTICE "Span %s: Expected %d channels, but receiving %d\n", span->name, span->channels, nchans); + dtd->err = newerr; + return; + } + msg++; + msg++; + + /* Okay now we've accepted the header, lets check our message + length... */ + + /* Start with header */ + xlen = 6; + /* Add samples of audio */ + xlen += nchans * DAHDI_CHUNKSIZE; + /* If RBS info is there, add that */ + if (sflags & DAHDI_DYNAMIC_FLAG_SIGBITS_PRESENT) { + /* Account for sigbits -- one short per 4 channels*/ + xlen += ((nchans + 3) / 4) * 2; + } + + if (unlikely(xlen != msglen)) { + rcu_read_unlock(); + newerr = ERR_LEN | xlen; + if (newerr != dtd->err) + printk(KERN_NOTICE "Span %s: Expected message size %d, but was %d instead\n", span->name, xlen, msglen); + dtd->err = newerr; + return; + } + + bits = 0; + + /* Record sigbits if present */ + if (sflags & DAHDI_DYNAMIC_FLAG_SIGBITS_PRESENT) { + for (x=0;x> ((x % 4) << 2)) & 0xff; + + /* Update signalling if appropriate */ + if (sig != span->chans[x]->rxsig) + dahdi_rbsbits(span->chans[x], sig); + + } + } + + /* Record data for channels */ + x = nchans*DAHDI_CHUNKSIZE; + /* Make sure we have enougth space in fifo for the whole packet. */ + if (dtd->rxfifo_size >= kfifo_len (dtd->rxfifo) + x) + kfifo_in_spinlocked(dtd->rxfifo, msg, x, &dtd->rxfifo_lock); + else + ++dtd->skip; + + /* Should be called just after sanity check, but I'd like to put data + to fifo first. Is it safe to move rbs code behind ? */ + dahdi_tdmox_save_tv(&dtd->rxtv, rxtx_time); + master = dtd->master; + + rxcnt = dtd->rxcnt; + dtd->rxcnt = rxpos+1; + + /* Keep track of last received packet */ + dtd->rxjif = jiffies; + + rcu_read_unlock(); + + /* Check for Yellow alarm */ + newalarm = span->alarms & ~(DAHDI_ALARM_YELLOW | DAHDI_ALARM_RED); + if (sflags & DAHDI_DYNAMIC_FLAG_YELLOW_ALARM) + newalarm |= DAHDI_ALARM_YELLOW; + + if (newalarm != span->alarms) { + span->alarms = newalarm; + checkmaster(); + dahdi_alarm_notify(span); + } + + /* note if we had a missing packet */ + if (unlikely(rxpos != rxcnt)) { + ++dtd->rxnuerr; + if (debug) + printk(KERN_NOTICE "Span %s: Expected seq no %d, but received %d instead\n", + span->name, rxcnt, rxpos); + } + + dahdi_dynamic_rx(dtd); + /* If this is our master span, then run everything */ + if (master) { + ++dynamic_master_tick; + dahdi_dynamic_run(); + } +} +EXPORT_SYMBOL(dahdi_tdmox_receive); + +/** + * dahdi_dynamic_release() - Free the memory associated with the dahdi_dynamic. + * @kref: Pointer to kref embedded in dahdi_dynamic structure. + * + */ +static void dahdi_dynamic_release(struct kref *kref) +{ + struct dahdi_tdmox *d = container_of(kref, struct dahdi_tdmox, + kref); + unsigned int x; + + WARN_ON(test_bit(DAHDI_FLAGBIT_REGISTERED, &d->span.flags)); + + kfree(d->msgbuf); + + for (x = 0; x < d->span.channels; x++) + kfree(d->chans[x]); + + /* Free rxfifo */ + if (d->rxfifo) { + my_kfifo_free (d->rxfifo); + d->rxfifo = NULL; + } + + dahdi_free_device(d->ddev); + kfree(d); +} + +static inline int dynamic_put(struct dahdi_tdmox *d) +{ + return kref_put(&d->kref, dahdi_dynamic_release); +} + +static inline void dynamic_get(struct dahdi_tdmox *d) +{ + kref_get(&d->kref); +} + +static struct dahdi_tdmox *find_dynamic(struct dahdi_tdmox_span *dds) +{ + struct dahdi_tdmox *d = NULL, *found = NULL; + + rcu_read_lock(); + list_for_each_entry_rcu(d, &dspan_list, list) { + if (!strcmp(d->dname, dds->driver) && + !strcmp(d->addr, dds->addr)) { + dynamic_get(d); + found = d; + break; + } + } + rcu_read_unlock(); + + return found; +} + +static struct dahdi_tdmox_driver *find_driver(const char *name) +{ + struct dahdi_tdmox_driver *dtd, *found = NULL; + + rcu_read_lock(); + list_for_each_entry_rcu(dtd, &driver_list, list) { + /* here's our driver */ + if (!strcmp(name, dtd->name)) { + found = dtd; + break; + } + } + rcu_read_unlock(); + + return found; +} + +static int _destroy_dynamic(struct dahdi_tdmox_span *dds) +{ + unsigned long flags; + struct dahdi_tdmox *d; + + d = find_dynamic(dds); + if (unlikely(!d)) + return -EINVAL; + + /* We shouldn't have more than the two references at this point. If + * we do, there are probably channels that are still opened. */ + if (atomic_read(&d->kref.refcount) > 2) { + dynamic_put(d); + return -EBUSY; + } + + if (d->pvt) { + if (d->driver && d->driver->destroy) { + if (!try_module_get(d->driver->owner)) { + /* The driver for this device is in the + * process of unloading. Leave this dynamic on + * the list so it's cleaned up when the driver + * unregisters. */ + dynamic_put(d); + return -ENXIO; + } + d->driver->destroy(d); + module_put(d->driver->owner); + } else { + WARN_ON(1); + } + d->pvt = NULL; + } + + dahdi_unregister_device(d->ddev); + + spin_lock_irqsave(&dspan_lock, flags); + list_del_rcu(&d->list); + spin_unlock_irqrestore(&dspan_lock, flags); + synchronize_rcu(); + + /* One since we've removed the item from the list... */ + dynamic_put(d); + /* ...and one for find_dynamic. */ + dynamic_put(d); + return 0; +} + +int dahdi_tdmox_destroy(struct dahdi_tdmox_span *dds) +{ + int ret; + mutex_lock(&dspan_mutex); + ret = _destroy_dynamic(dds); + mutex_unlock(&dspan_mutex); + return ret; +} + +static int dahdi_dynamic_rbsbits(struct dahdi_chan *chan, int bits) +{ + /* Don't have to do anything */ + return 0; +} + +static int dahdi_dynamic_open(struct dahdi_chan *chan) +{ + struct dahdi_tdmox *d = dynamic_from_span(chan->span); + if (!try_module_get(d->driver->owner)) + return -ENODEV; + dynamic_get(d); + return 0; +} + +static int dahdi_dynamic_chanconfig(struct file *file, + struct dahdi_chan *chan, int sigtype) +{ + return 0; +} + +static int dahdi_dynamic_close(struct dahdi_chan *chan) +{ + struct dahdi_tdmox *d = dynamic_from_span(chan->span); + struct module *owner = d->driver->owner; + dynamic_put(d); + module_put(owner); + return 0; +} + +static void dahdi_dynamic_sync_tick(struct dahdi_span *span, int is_master) +{ + struct dahdi_tdmox *head; + struct dahdi_tdmox *d = dynamic_from_span(span); + + rcu_read_lock(); + head = list_entry(dspan_list.next, struct dahdi_tdmox, list); + rcu_read_unlock(); + + if (d != head) + return; + + ++dahdi_sync_tick; + if (!hasmaster) + dahdi_dynamic_run(); + + return; +} + +static int dahdi_dynamic_shutdown(struct dahdi_span *span) +{ + return 0; +} + +#ifdef HAVE_SPAN_MAINTQ +static int dahdi_dynamic_startup(struct dahdi_span *span) +#else +static int dahdi_dynamic_startup(struct file *file, struct dahdi_span *span) +#endif //HAVE_SPAN_MAINTQ +{ + struct dahdi_tdmox *d = dynamic_from_span(span); + + _tdmox_span_clearstat(d); + span->flags |= DAHDI_FLAG_RUNNING; + + return 0; +} + +#ifdef HAVE_SPAN_MAINTQ +static int dahdi_dynamic_spanconfig(struct dahdi_span *span, struct dahdi_lineconfig *lc) +#else +static int dahdi_dynamic_spanconfig(struct file *file, struct dahdi_span *span, struct dahdi_lineconfig *lc) +#endif //HAVE_SPAN_MAINTQ +{ + struct dahdi_tdmox *d = dynamic_from_span(span); + + _tdmox_span_clearstat(d); + span->lineconfig = lc->lineconfig; + span->txlevel = 0; + span->rxlevel = 0; + + return 0; +} + +static const struct dahdi_span_ops dynamic_ops = { + .owner = THIS_MODULE, + .rbsbits = dahdi_dynamic_rbsbits, + .open = dahdi_dynamic_open, + .close = dahdi_dynamic_close, + .chanconfig = dahdi_dynamic_chanconfig, + .sync_tick = dahdi_dynamic_sync_tick, + + + .spanconfig = dahdi_dynamic_spanconfig, + .startup = dahdi_dynamic_startup, + .shutdown = dahdi_dynamic_shutdown, +// .maint = dahdi_dynamic_maint, +// .ioctl = dahdi_dynamic_ioctl, +// .dacs = dahdi_dynamic_dacs, +}; + +static int _create_dynamic(struct dahdi_tdmox_span *dds) +{ + int res = 0; + struct dahdi_tdmox *d; + struct dahdi_tdmox_driver *dtd; + unsigned long flags; + int x; + int bufsize; + + if (list_empty_careful(&dspan_list)) + _dahdi_dynamic_clearstat(); + + if (dds->numchans < 1) { + printk(KERN_NOTICE "Can't be less than 1 channel (%d)!\n", + dds->numchans); + return -EINVAL; + } + if (dds->numchans > ARRAY_SIZE(d->chans)) { + printk(KERN_NOTICE "Can't create dynamic span with greater " + "than %d channels. See dahdi_dynamic.c and increase " + "DAHDI_DYNAMIC_MAX_CHANS\n", dds->numchans); + return -EINVAL; + } + + d = find_dynamic(dds); + if (d) { + dynamic_put(d); + return -EEXIST; + } + + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) + return -ENOMEM; + kref_init(&d->kref); + d->ddev = dahdi_create_device(); + + for (x = 0; x < dds->numchans; x++) { + d->chans[x] = kzalloc(sizeof(*d->chans[x]), GFP_KERNEL); + if (!d->chans[x]) { + dynamic_put(d); + return -ENOMEM; + } + d->span.channels++; + } + + /* Allocate message buffer with sample space and header space */ + bufsize = dds->numchans * DAHDI_CHUNKSIZE + dds->numchans / 4 + 48; + + d->msgbuf = kzalloc(bufsize, GFP_KERNEL); + + if (!d->msgbuf) { + dynamic_put(d); + return -ENOMEM; + } + + /* We'd like to allocate rxfifo_size bytes FIFO */ + d->rxfifo_size = (dds->numchans * DAHDI_CHUNKSIZE) * rxfifo; + /* kfifo can handle FIFO size equal to power of 2. + kfifo_alloc >= 2.6.35 is buggy, let's round-up here */ + bufsize = roundup_pow_of_two(d->rxfifo_size); + /* TODO: Do we need FIFO locking ? We have one reader & one writer. + kfifo insist, it's safe. */ + spin_lock_init(&d->rxfifo_lock); + d->rxfifo = my_kfifo_alloc(bufsize, GFP_KERNEL, &d->rxfifo_lock); + if (!d->rxfifo) { + dynamic_put(d); + return -ENOMEM; + } + if (debug) + printk(KERN_INFO "Allocated kfifo. Requested %d, allocated %d\n", + d->rxfifo_size, kfifo_size(d->rxfifo)); + d->rxfifo_size=min(d->rxfifo_size, ((int)kfifo_size(d->rxfifo))); + + /* Setup parameters properly assuming we're going to be okay. */ + strlcpy(d->dname, dds->driver, sizeof(d->dname)); /* */ + strlcpy(d->addr, dds->addr, sizeof(d->addr)); + + d->span.chans = d->chans; + d->span.ops = &dynamic_ops; + + res = dahdi_tdmox_sysfs_cfg_ddev(d, dds->span_id_sysfs); + if (res) { + dynamic_put(d); + return res; + } + + dtd = find_driver(dds->driver); + if (!dtd) { + request_module("dahdi_tdmox_%s", dds->driver); + dtd = find_driver(dds->driver); + } + + + if (!dtd) { + printk(KERN_NOTICE "No such driver '%s' for dynamic span\n", + dds->driver); + dynamic_put(d); + return -EINVAL; + } + + if (!try_module_get(dtd->owner)) { + dynamic_put(d); + return -ENODEV; + } + + /* Remember the driver. We also give our reference to the driver to + * the dahdi_dyanmic here. Do not access dtd directly now. */ + d->driver = dtd; + + /* Create the stuff */ + res = dtd->create(d, d->addr); + if (res) { + printk(KERN_NOTICE "Driver '%s' (%s) rejected address '%s'\n", + dtd->name, dtd->desc, d->addr); + dynamic_put(d); + module_put(dtd->owner); + return res; + } + + d->span.cannot_provide_timing = 1; /* We cannot provide timing yet... */ + + dev_set_name(&d->ddev->dev, "tdmox:%s:%d", dds->driver, dtd->id++); + list_add_tail(&d->span.device_node, &d->ddev->spans); + /* Whee! We're created. Now register the span */ + if (dahdi_register_device(d->ddev, d->dev)) { + printk(KERN_NOTICE "Unable to register span '%s'\n", + d->span.name); + dynamic_put(d); + module_put(dtd->owner); + return -EINVAL; + } + + x = d->span.spanno; + + /* Transfer our reference to the dspan_list. Do not touch d after + * this point. It also must remain on the list while registered. */ + spin_lock_irqsave(&dspan_lock, flags); + list_add_tail_rcu(&d->list, &dspan_list); + spin_unlock_irqrestore(&dspan_lock, flags); + + /* First incoming packet will clear red alarm, so, checkmaster will be called in a proper time. */ + d->span.alarms |= DAHDI_ALARM_RED; /* We don't know our state yet... */ + dahdi_alarm_notify(&d->span); + + module_put(dtd->owner); + return x; +} + +int dahdi_tdmox_create(struct dahdi_tdmox_span *dds) +{ + int ret; + mutex_lock(&dspan_mutex); + ret = _create_dynamic(dds); + //TODO: cleanup if ret < 0 + mutex_unlock(&dspan_mutex); + return ret; +} + +int dahdi_tdmox_register_driver(struct dahdi_tdmox_driver *dri) +{ + unsigned long flags; + int res = 0; + + if (!dri->owner) + return -EINVAL; + + if (find_driver(dri->name)) { + res = -1; + } else { + spin_lock_irqsave(&driver_lock, flags); + list_add_tail_rcu(&dri->list, &driver_list); + spin_unlock_irqrestore(&driver_lock, flags); + } + return res; +} +EXPORT_SYMBOL(dahdi_tdmox_register_driver); + +void dahdi_tdmox_unregister_driver(struct dahdi_tdmox_driver *dri) +{ + struct dahdi_tdmox *d, *n; + unsigned long flags; + + mutex_lock(&dspan_mutex); + + list_for_each_entry_safe(d, n, &dspan_list, list) { + if (d->driver == dri) { + if (d->pvt) { + if (d->driver && d->driver->destroy) + d->driver->destroy(d); + else + WARN_ON(1); + } + dahdi_unregister_device(d->ddev); + spin_lock_irqsave(&dspan_lock, flags); + list_del_rcu(&d->list); + spin_unlock_irqrestore(&dspan_lock, flags); + synchronize_rcu(); + d->driver = NULL; + dynamic_put(d); + } + } + + spin_lock_irqsave(&driver_lock, flags); + list_del_rcu(&dri->list); + spin_unlock_irqrestore(&driver_lock, flags); + synchronize_rcu(); + + mutex_unlock(&dspan_mutex); +} +EXPORT_SYMBOL(dahdi_tdmox_unregister_driver); + +static struct timer_list alarmcheck; + +static void check_for_red_alarm(unsigned long ignored) +{ + int newalarm; + int alarmchanged = 0; + struct dahdi_tdmox *d; + + rcu_read_lock(); + list_for_each_entry_rcu(d, &dspan_list, list) { + newalarm = d->span.alarms & ~DAHDI_ALARM_RED; + /* If nothing received for a second, consider that RED ALARM */ + if ((jiffies - d->rxjif) > 1 * HZ) { + newalarm |= DAHDI_ALARM_RED; + if (d->span.alarms != newalarm) { + d->span.alarms = newalarm; + checkmaster(); + dahdi_alarm_notify(&d->span); + alarmchanged++; + } + } + } + rcu_read_unlock(); + + if (alarmchanged) + checkmaster(); + + /* Do the next one */ + mod_timer(&alarmcheck, jiffies + 1 * HZ); +} + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_entry = NULL; +static const char *dahdi_dynamic_procname="dahdi/tdmox_stats"; + +static void print_tls(struct seq_file *sfile, const char *s, unsigned int cnt, struct dahdi_tdmox_tv *dtv) +{ + seq_printf(sfile, "%s%10u", s, cnt); + if (tasklet_time) + seq_printf(sfile, ", min: %10u, max: %10u", dtv->min, dtv->max); + seq_printf(sfile, "\n"); +} + +static int dahdi_dynamic_show(struct seq_file *sfile, void *data) +{ + struct dahdi_tdmox *d; + + seq_printf(sfile, "dahdi_tdmox statistics\n"); + + print_tls(sfile, "\ttaskletreq: ", tlstat->req, &tlstat->time.req); + print_tls(sfile, "\ttaskletsched: ", tlstat->sched, &tlstat->time.sched); + print_tls(sfile, "\ttaskletrun: ", tlstat->run, &tlstat->time.run); + print_tls(sfile, "\ttaskletexec: ", tlstat->exec, &tlstat->time.exec); + seq_printf(sfile, "\ttaskletpending: %10d\n", atomic_read(&tlet->pending)); + seq_printf(sfile, "\ttaskleterrors: %10u\n", tlstat->errors); + seq_printf(sfile, "\ttdmox_sync_tick: %10u\n", dynamic_master_tick); + seq_printf(sfile, "\tdahdi_sync_tick: %10u\n", dahdi_sync_tick); +// seq_printf(sfile, "\tdahdi_in_sync: %s\n", dahdi_in_sync ? "yes" : "NO"); + + rcu_read_lock(); + list_for_each_entry_rcu(d, &dspan_list, list) { + struct dahdi_span *span = &(d->span); + if (!span) + break; + + seq_printf(sfile, "\n"); + seq_printf(sfile, "Span %d: %s ", span->spanno, span->name); + seq_printf(sfile, "\"%s\"", span->desc ? span->desc : ""); + + seq_printf(sfile, " "); + if (d->master) + seq_printf(sfile, "ClockSource "); + + seq_printf(sfile, "\n"); + seq_printf(sfile, "slip: %10u", d->slip); + seq_printf(sfile, ", skip: %10u", d->skip); + seq_printf(sfile, ", rxnuerr: %10u", d->rxnuerr); + seq_printf(sfile, ", rxfifo: %4u", kfifo_len(d->rxfifo)/span->channels/DAHDI_CHUNKSIZE); + + if (rxtx_time) { + seq_printf(sfile, "\n"); + seq_printf(sfile, "rxmin: %10u", d->rxtv.min); + seq_printf(sfile, ", rxmax: %10u", d->rxtv.max); + seq_printf(sfile, ", txmin: %10u", d->txtv.min); + seq_printf(sfile, ", txmax: %10u", d->txtv.max); + } + + seq_printf(sfile, "\n"); + } + rcu_read_unlock(); + + return 0; +} + +static int dahdi_dynamic_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, dahdi_dynamic_show, PDE_DATA(inode)); +} + +static ssize_t dahdi_dynamic_proc_write(struct file *file, const char __user *usrbuf, + size_t count, loff_t *ppos) +{ + _dahdi_dynamic_clearstat(); + return count; +} + +static const struct file_operations dahdi_dynamic_proc_ops = { + .owner = THIS_MODULE, + .open = dahdi_dynamic_proc_open, + .read = seq_read, + .write = dahdi_dynamic_proc_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static void proc_init (void) +{ + proc_entry = proc_create_data(dahdi_dynamic_procname, 0444, + NULL, &dahdi_dynamic_proc_ops, NULL); + if (!proc_entry) { + /* TODO */ + return; + } +} + +static void proc_cleanup (void) +{ + if (proc_entry) { + remove_proc_entry(dahdi_dynamic_procname, NULL); + proc_entry = NULL; + } +} +#else +# define proc_init() +# define proc_cleanup() +#endif //CONFIG_PROC_FS + +static int dahdi_dynamic_init(void) +{ + rxfifo = max(rxfifo, 1); + tasklet_quota = max(tasklet_quota, 1); + + /* Start process to check for RED ALARM */ + init_timer(&alarmcheck); + alarmcheck.expires = 0; + alarmcheck.data = 0; + alarmcheck.function = check_for_red_alarm; + /* Check once per second */ + mod_timer(&alarmcheck, jiffies + 1 * HZ); + atomic_set(&tlet->pending, 0); + tasklet_init(&tlet->tlet, dahdi_dynamic_tasklet, 0); + proc_init (); + dahdi_tdmox_sysfs_init(); + + module_printk(KERN_INFO, "Version: %s\n", versionstr); + printk(KERN_INFO "DAHDI TDMoX Span support LOADED, rxfifo=%d, rxtasklet=%d, noflood=%d\n", + rxfifo, rxtasklet, noflood); + return 0; +} + +static void dahdi_dynamic_cleanup(void) +{ + dahdi_tdmox_sysfs_cleanup(); + proc_cleanup (); + tasklet_disable(&tlet->tlet); + tasklet_kill(&tlet->tlet); + del_timer_sync(&alarmcheck); + /* Must call again in case it was running before and rescheduled + * itself. */ + del_timer(&alarmcheck); + + printk(KERN_INFO "DAHDI TDMoX Span support unloaded\n"); +} + +module_param(debug, int, 0600); +module_param(noflood, int, 0600); +MODULE_PARM_DESC(noflood, "Send a probe twice a second during red alarm."); +module_param(rxfifo, int, 0600); +MODULE_PARM_DESC(rxfifo, "RX fifo size (milliseconds)."); +module_param(rxtasklet, int, 0600); +MODULE_PARM_DESC(rxtasklet, "Process receive in tasklet, if enabled."); +module_param(rxtx_time, int, 0600); +MODULE_PARM_DESC(rxtx_time, "Enable rx/tx time statistics."); +module_param(tasklet_time, int, 0600); +MODULE_PARM_DESC(tasklet_time, "Enable tasklet time statistics."); +module_param(tasklet_quota, int, 0600); +MODULE_PARM_DESC(tasklet_quota, "Maximum number of operations per tasklet."); + +MODULE_DESCRIPTION("DAHDI TDMoX driver - Alternative Dynamic implementation"); +MODULE_AUTHOR("Mark Spencer "); +MODULE_AUTHOR("Pavel Selivanov - sysfs, improvements"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DAHDI_TDMOX_VERSIONSTR); + +module_init(dahdi_dynamic_init); +module_exit(dahdi_dynamic_cleanup); diff -urN dahdi-linux.orig/drivers/dahdi/dahdi_tdmox/dahdi_tdmox_eth.c dahdi-linux/drivers/dahdi/dahdi_tdmox/dahdi_tdmox_eth.c --- dahdi-linux.orig/drivers/dahdi/dahdi_tdmox/dahdi_tdmox_eth.c 1970-01-01 07:00:00.000000000 +0700 +++ dahdi-linux/drivers/dahdi/dahdi_tdmox/dahdi_tdmox_eth.c 2014-07-24 17:10:02.000000000 +0700 @@ -0,0 +1,434 @@ +/* + * Dynamic Span Interface for DAHDI (Ethernet Interface) + * + * Written by Mark Spencer + * + * Copyright (C) 2001-2012, Digium, Inc. + * + * All rights reserved. + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "dahdi_tdmox.h" + +#define ETH_P_DAHDI_DETH 0xd00d + +struct ztdeth_header { + unsigned short subaddr; +}; + +/* We take the raw message, put it in an ethernet frame, and add a + two byte addressing header at the top for future use */ +static DEFINE_SPINLOCK(zlock); + +static struct sk_buff_head skbs; + +static struct ztdeth { + unsigned char addr[ETH_ALEN]; + unsigned short subaddr; /* Network byte order */ + struct dahdi_span *span; + char ethdev[IFNAMSIZ]; + struct net_device *dev; + struct ztdeth *next; +} *zdevs = NULL; + +static struct dahdi_span *ztdeth_getspan(unsigned char *addr, unsigned short subaddr) +{ + unsigned long flags; + struct ztdeth *z; + struct dahdi_span *span = NULL; + spin_lock_irqsave(&zlock, flags); + z = zdevs; + while(z) { + if (!memcmp(addr, z->addr, ETH_ALEN) && + z->subaddr == subaddr) + break; + z = z->next; + } + if (z) + span = z->span; + spin_unlock_irqrestore(&zlock, flags); + if (!span || !test_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags)) + return NULL; + return span; +} + +static int ztdeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) +{ + struct dahdi_span *span; + struct ztdeth_header *zh; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + zh = (struct ztdeth_header *)skb_network_header(skb); +#else + zh = (struct ztdeth_header *)skb->nh.raw; +#endif + span = ztdeth_getspan(eth_hdr(skb)->h_source, zh->subaddr); + if (span) { + skb_pull(skb, sizeof(struct ztdeth_header)); +#ifdef NEW_SKB_LINEARIZE + if (skb_is_nonlinear(skb)) + skb_linearize(skb); +#else + if (skb_is_nonlinear(skb)) + skb_linearize(skb, GFP_KERNEL); +#endif + dahdi_tdmox_receive(span, (unsigned char *)skb->data, skb->len); + } + kfree_skb(skb); + return 0; +} + +static int ztdeth_notifier(struct notifier_block *block, unsigned long event, void *ptr) +{ + struct net_device *dev = ptr; + struct ztdeth *z; + unsigned long flags; + switch(event) { + case NETDEV_GOING_DOWN: + case NETDEV_DOWN: + spin_lock_irqsave(&zlock, flags); + z = zdevs; + while(z) { + /* Note that the device no longer exists */ + if (z->dev == dev) + z->dev = NULL; + z = z->next; + } + spin_unlock_irqrestore(&zlock, flags); + break; + case NETDEV_UP: + spin_lock_irqsave(&zlock, flags); + z = zdevs; + while(z) { + /* Now that the device exists again, use it */ + if (!strcmp(z->ethdev, dev->name)) + z->dev = dev; + z = z->next; + } + spin_unlock_irqrestore(&zlock, flags); + break; + } + return 0; +} + +static void ztdeth_transmit(struct dahdi_tdmox *dyn, u8 *msg, size_t msglen) +{ + struct ztdeth *z; + struct sk_buff *skb; + struct ztdeth_header *zh; + unsigned long flags; + struct net_device *dev; + unsigned char addr[ETH_ALEN]; + unsigned short subaddr; /* Network byte order */ + + spin_lock_irqsave(&zlock, flags); + z = dyn->pvt; + if (z && z->dev) { + /* Copy fields to local variables to remove spinlock ASAP */ + dev = z->dev; + memcpy(addr, z->addr, sizeof(z->addr)); + subaddr = z->subaddr; + spin_unlock_irqrestore(&zlock, flags); + skb = dev_alloc_skb(msglen + dev->hard_header_len + sizeof(struct ztdeth_header) + 32); + if (skb) { + /* Reserve header space */ + skb_reserve(skb, dev->hard_header_len + sizeof(struct ztdeth_header)); + + /* Copy message body */ + memcpy(skb_put(skb, msglen), msg, msglen); + + /* Throw on header */ + zh = (struct ztdeth_header *)skb_push(skb, sizeof(struct ztdeth_header)); + zh->subaddr = subaddr; + + /* Setup protocol and such */ + skb->protocol = __constant_htons(ETH_P_DAHDI_DETH); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + skb_set_network_header(skb, 0); +#else + skb->nh.raw = skb->data; +#endif + skb->dev = dev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + dev_hard_header(skb, dev, ETH_P_DAHDI_DETH, addr, dev->dev_addr, skb->len); +#else + if (dev->hard_header) + dev->hard_header(skb, dev, ETH_P_DAHDI_DETH, addr, dev->dev_addr, skb->len); +#endif + skb_queue_tail(&skbs, skb); + } + } + else + spin_unlock_irqrestore(&zlock, flags); +} + +/** + * ztdeth_flush - Flush all pending transactions. + * + * This function is always called in softirq context. + */ +static int ztdeth_flush(void) +{ + struct sk_buff *skb; + while ((skb = skb_dequeue(&skbs))) + dev_queue_xmit(skb); + return 0; +} + +static struct packet_type ztdeth_ptype = { + .type = __constant_htons(ETH_P_DAHDI_DETH), /* Protocol */ + .dev = NULL, /* Device (NULL = wildcard) */ + .func = ztdeth_rcv, /* Receiver */ +}; + +static int digit2int(char d) +{ + switch(d) { + case 'F': + case 'E': + case 'D': + case 'C': + case 'B': + case 'A': + return d - 'A' + 10; + case 'f': + case 'e': + case 'd': + case 'c': + case 'b': + case 'a': + return d - 'a' + 10; + case '9': + case '8': + case '7': + case '6': + case '5': + case '4': + case '3': + case '2': + case '1': + case '0': + return d - '0'; + } + return -1; +} + +static int hex2int(char *s) +{ + int res; + int tmp; + /* Gotta be at least one digit */ + if (strlen(s) < 1) + return -1; + /* Can't be more than two */ + if (strlen(s) > 2) + return -1; + /* Grab the first digit */ + res = digit2int(s[0]); + if (res < 0) + return -1; + tmp = res; + /* Grab the next */ + if (strlen(s) > 1) { + res = digit2int(s[1]); + if (res < 0) + return -1; + tmp = tmp * 16 + res; + } + return tmp; +} + +static void ztdeth_destroy(struct dahdi_tdmox *dyn) +{ + struct ztdeth *z = dyn->pvt; + unsigned long flags; + struct ztdeth *prev=NULL, *cur; + spin_lock_irqsave(&zlock, flags); + cur = zdevs; + while(cur) { + if (cur == z) { + if (prev) + prev->next = cur->next; + else + zdevs = cur->next; + break; + } + prev = cur; + cur = cur->next; + } + if (cur == z) { /* Successfully removed */ + dyn->pvt = NULL; + printk(KERN_INFO "TDMoE: Removed interface for %s\n", z->span->name); + kfree(z); + } + spin_unlock_irqrestore(&zlock, flags); +} + +static int ztdeth_create(struct dahdi_tdmox *dyn, const char *addr) +{ + struct ztdeth *z; + char src[256]; + char tmp[256], *tmp2, *tmp3, *tmp4 = NULL; + int res,x; + unsigned long flags; + struct dahdi_span *const span = &dyn->span; + + z = kmalloc(sizeof(struct ztdeth), GFP_KERNEL); + if (z) { + /* Zero it out */ + memset(z, 0, sizeof(struct ztdeth)); + + /* Address should be /[/subaddr] */ + strlcpy(tmp, addr, sizeof(tmp)); + tmp2 = strchr(tmp, '/'); + if (tmp2) { + *tmp2 = '\0'; + tmp2++; + strlcpy(z->ethdev, tmp, sizeof(z->ethdev)); + } else { + printk(KERN_NOTICE "Invalid TDMoE address (no device) '%s'\n", addr); + kfree(z); + return -EINVAL; + } + if (tmp2) { + tmp4 = strchr(tmp2+1, '/'); + if (tmp4) { + *tmp4 = '\0'; + tmp4++; + } + /* We don't have SSCANF :( Gotta do this the hard way */ + tmp3 = strchr(tmp2, ':'); + for (x=0;x<6;x++) { + if (tmp2) { + if (tmp3) { + *tmp3 = '\0'; + tmp3++; + } + res = hex2int(tmp2); + if (res < 0) + break; + z->addr[x] = res & 0xff; + } else + break; + if ((tmp2 = tmp3)) + tmp3 = strchr(tmp2, ':'); + } + if (x != 6) { + printk(KERN_NOTICE "TDMoE: Invalid MAC address in: %s\n", addr); + kfree(z); + return -EINVAL; + } + } else { + printk(KERN_NOTICE "TDMoE: Missing MAC address\n"); + kfree(z); + return -EINVAL; + } + if (tmp4) { + int sub = 0; + int mul = 1; + + /* We have a subaddr */ + tmp3 = tmp4 + strlen (tmp4) - 1; + while (tmp3 >= tmp4) { + if (*tmp3 >= '0' && *tmp3 <= '9') { + sub += (*tmp3 - '0') * mul; + } else { + printk(KERN_NOTICE "TDMoE: Invalid subaddress\n"); + kfree(z); + return -EINVAL; + } + mul *= 10; + tmp3--; + } + z->subaddr = htons(sub); + } + z->dev = dev_get_by_name( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + &init_net, +#endif + z->ethdev); + if (!z->dev) { + printk(KERN_NOTICE "TDMoE: Invalid device '%s'\n", z->ethdev); + kfree(z); + return -EINVAL; + } + z->span = span; + src[0] ='\0'; + for (x=0;x<5;x++) + sprintf(src + strlen(src), "%02x:", z->dev->dev_addr[x]); + sprintf(src + strlen(src), "%02x", z->dev->dev_addr[5]); + printk(KERN_INFO "TDMoE: Added new interface for %s at %s (addr=%s, src=%s, subaddr=%d)\n", span->name, z->dev->name, addr, src, ntohs(z->subaddr)); + + spin_lock_irqsave(&zlock, flags); + z->next = zdevs; + zdevs = z; + dyn->pvt = z; + spin_unlock_irqrestore(&zlock, flags); + } + return (z) ? 0 : -ENOMEM; +} + +static struct dahdi_tdmox_driver ztd_eth = { + .owner = THIS_MODULE, + .name = "eth", + .desc = "Ethernet", + .create = ztdeth_create, + .destroy = ztdeth_destroy, + .transmit = ztdeth_transmit, + .flush = ztdeth_flush, +}; + +static struct notifier_block ztdeth_nblock = { + .notifier_call = ztdeth_notifier, +}; + +static int __init ztdeth_init(void) +{ + skb_queue_head_init(&skbs); + + dev_add_pack(&ztdeth_ptype); + register_netdevice_notifier(&ztdeth_nblock); + dahdi_tdmox_register_driver(&ztd_eth); + + return 0; +} + +static void __exit ztdeth_exit(void) +{ + dahdi_tdmox_unregister_driver(&ztd_eth); + unregister_netdevice_notifier(&ztdeth_nblock); + dev_remove_pack(&ztdeth_ptype); + + skb_queue_purge(&skbs); +} + +MODULE_DESCRIPTION("DAHDI TDMoE Driver - Alternative Dynamic implementation"); +MODULE_AUTHOR("Mark Spencer "); +MODULE_LICENSE("GPL v2"); + +module_init(ztdeth_init); +module_exit(ztdeth_exit); diff -urN dahdi-linux.orig/drivers/dahdi/dahdi_tdmox/dahdi_tdmox.h dahdi-linux/drivers/dahdi/dahdi_tdmox/dahdi_tdmox.h --- dahdi-linux.orig/drivers/dahdi/dahdi_tdmox/dahdi_tdmox.h 1970-01-01 07:00:00.000000000 +0700 +++ dahdi-linux/drivers/dahdi/dahdi_tdmox/dahdi_tdmox.h 2014-07-30 20:06:35.000000000 +0700 @@ -0,0 +1,122 @@ +#ifndef _DAHDI_TDMOX_H +#define _DAHDI_TDMOX_H + +#include /* tasklet */ + +#define DAHDI_TDMOX_VERSION 1 +#define DAHDI_TDMOX_PATCHLEVEL 1 +#define DAHDI_TDMOX_SUBLEVEL 0 +#define DAHDI_TDMOX_VERSIONSTR __stringify(DAHDI_TDMOX_VERSION) "." \ + __stringify(DAHDI_TDMOX_PATCHLEVEL) "." \ + __stringify(DAHDI_TDMOX_SUBLEVEL) + +#define DAHDI_DYNAMIC_MAX_CHANS 256 +#define DAHDI_TDMOX_TASKLET_QUOTA 8 /* Will handle 250Hz timer from DAHDI */ +#define DAHDI_TDMOX_RXFIFO 4 + +#define DAHDI_TDMOX_TVHIST 2 + +struct dahdi_tdmox_tv { + struct timeval tv[DAHDI_TDMOX_TVHIST]; /* Requested to schedule */ + unsigned int min; + unsigned int max; +}; +struct dahdi_tdmox_tasklet_time { + struct dahdi_tdmox_tv req; /* Requested to schedule */ + struct dahdi_tdmox_tv sched; /* Scheduled */ + struct dahdi_tdmox_tv run; /* Runned */ + struct dahdi_tdmox_tv exec; /* Executed operation */ +}; +struct dahdi_tdmox_tasklet_stat { + unsigned int req; + unsigned int sched; + unsigned int run; + unsigned int exec; + unsigned int errors; + struct dahdi_tdmox_tasklet_time time; +}; + +struct dahdi_tdmox_tasklet { + struct tasklet_struct tlet; + /* pending has 1 incrementor & 1 decrimentor. + No need in lock or mutex. */ + atomic_t pending; +}; + +struct dahdi_tdmox_span { + char driver[20]; /* Which low-level driver to use */ + char addr[40]; /* Destination address */ + int numchans; /* Number of channels */ + int timing; /* Timing source preference */ + int spanno; /* Span number (filled in by DAHDI) */ + int span_id_sysfs; /* Pavel. Will remove that struct later. */ +}; + +struct dahdi_tdmox { + char addr[40]; + char dname[20]; + int err; + struct kref kref; + long rxjif; + unsigned short txcnt; + unsigned short rxcnt; + struct dahdi_device *ddev; + struct dahdi_span span; + struct dahdi_chan *chans[DAHDI_DYNAMIC_MAX_CHANS]; + struct dahdi_tdmox_driver *driver; + void *pvt; + int timing; + int master; + unsigned char *msgbuf; + struct device *dev; + struct kfifo *rxfifo; + int rxfifo_size; /* Not aligned size of FIFO, we actually use */ + spinlock_t rxfifo_lock; /* See connent below (spin_lock_init) */ + /* Statistic */ + unsigned int slip; + unsigned int skip; + unsigned int rxnuerr; + struct dahdi_tdmox_tv rxtv; + struct dahdi_tdmox_tv txtv; + + struct list_head list; +}; + +struct dahdi_tdmox_driver { + /*! Driver name (e.g. Eth) */ + const char *name; + + /*! Driver description */ + const char *desc; + + /*! Create a new transmission pipe */ + int (*create)(struct dahdi_tdmox *d, const char *address); + + /*! Destroy a created transmission pipe */ + void (*destroy)(struct dahdi_tdmox *d); + + /*! Transmit a given message */ + void (*transmit)(struct dahdi_tdmox *d, u8 *msg, size_t msglen); + + /*! Flush any pending messages */ + int (*flush)(void); + + struct list_head list; + struct module *owner; + + /*! Numberic id of next device created by this driver. */ + unsigned int id; +}; + +struct dahdi_tdmox_ops { + struct module *owner; + int (*ioctl)(unsigned int cmd, unsigned long data); +}; + +int dahdi_tdmox_register_driver(struct dahdi_tdmox_driver *); +void dahdi_tdmox_unregister_driver(struct dahdi_tdmox_driver *); +void dahdi_tdmox_receive(struct dahdi_span *, unsigned char *, int); +int dahdi_tdmox_create(struct dahdi_tdmox_span *); +int dahdi_tdmox_destroy(struct dahdi_tdmox_span *); + +#endif /* _DAHDI_TDMOX_H */ diff -urN dahdi-linux.orig/drivers/dahdi/dahdi_tdmox/dahdi_tdmox-sysfs.c dahdi-linux/drivers/dahdi/dahdi_tdmox/dahdi_tdmox-sysfs.c --- dahdi-linux.orig/drivers/dahdi/dahdi_tdmox/dahdi_tdmox-sysfs.c 1970-01-01 07:00:00.000000000 +0700 +++ dahdi-linux/drivers/dahdi/dahdi_tdmox/dahdi_tdmox-sysfs.c 2014-07-24 17:10:02.000000000 +0700 @@ -0,0 +1,694 @@ +/* + * Alternative TDMoX implementation for DAHDI - sysfs code + * + * Written by Pavel Selivanov + * + * Copyright (C) 2014, Parabel, 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 +#include +#include +#include +#include +#include +#include +#include /* for tdmox_device_add_groups. Remove, when obsolete. */ + +#include +#include + +#include "dahdi_tdmox.h" +#include "dahdi_tdmox-sysfs.h" + +/* + Full names (dahdi_tdmox_sysfs_) for exported functions. + Stripped names (tdmox_sysfs_) for static(local) functions & structs. +*/ + +/*== SECTION: declarations ==*/ + +#define TDMOX_CHAN_ATTRS 10 /* Attributes per CHAN */ +#define TDMOX_SPAN_ATTRS 32 /* Attributes per SPAN */ + +#define to_tdmox_sysfs_attr(attr_ptr) container_of(attr_ptr, struct tdmox_sysfs_attr, attr); +struct tdmox_sysfs_attr { + struct device_attribute attr; + char name[TDMOX_ATTR_NAME_STRLEN]; + char data[TDMOX_CHANNELS_STRLEN]; +}; + +struct tdmox_sysfs_chan { + char name[TDMOX_CHAN_NAME_STRLEN]; +/* int tdmox_span_id; Not used for now. Reserved. */ +/* int tdmox_chan_id; Not used for now. Reserved. */ + struct tdmox_sysfs_attr attr_name; + struct tdmox_sysfs_attr attr_sig; + struct tdmox_sysfs_attr attr_sigcap; + struct tdmox_sysfs_attr *tsattrs[TDMOX_CHAN_ATTRS+1]; /* Pointers to attrubutes above */ + struct attribute *attrs[TDMOX_CHAN_ATTRS+1]; + struct attribute_group attr_group; + int attrs_total; +}; + +#define to_tdmox_sysfs_span(dev_ptr) container_of(dev_ptr, struct tdmox_sysfs_span, device); +struct tdmox_sysfs_span { + struct device device; + /* SPAN-specific attributes. */ + struct tdmox_sysfs_attr attr_name; + struct tdmox_sysfs_attr attr_spantype; + struct tdmox_sysfs_attr attr_channels; + struct tdmox_sysfs_attr attr_driver; + struct tdmox_sysfs_attr attr_addr; + struct tdmox_sysfs_attr attr_desc; + struct tdmox_sysfs_attr attr_manufacturer; + struct tdmox_sysfs_attr attr_location; + struct tdmox_sysfs_attr attr_devicetype; + struct tdmox_sysfs_attr attr_flags; + struct tdmox_sysfs_attr attr_linecompat; + struct tdmox_sysfs_attr attr_deflaw; + struct tdmox_sysfs_attr attr_register; + struct tdmox_sysfs_attr attr_timing; + struct tdmox_sysfs_attr attr_hardware_id; + struct tdmox_sysfs_attr *tsattrs[TDMOX_SPAN_ATTRS+1]; /* Pointers to attrubutes above */ + int attrs_total; + int span_id; + int device_registered; + int attrs_registered; + int groups_registered; + /* Channels-specific data */ + struct tdmox_sysfs_chan *chans; + struct attribute_group *_dev_attr_group[TDMOX_CHANNELS_MAX+1]; /* tailing NULL */ + const struct attribute_group **dev_attr_group; /* tailing NULL */ +}; + +struct str_flag { + const char *str; + unsigned long flag; +}; + +/*== SECTION: global variables ==*/ + +extern int debug; +static int spans = 8; +static int channels = 128; +static int device_registered = 0; +static int bus_registered = 0; + +static struct str_flag tdmox_law[] = { + {"DEFAULT", DAHDI_LAW_DEFAULT}, + {"MULAW", DAHDI_LAW_MULAW}, + {"ALAW", DAHDI_LAW_ALAW}, + {NULL, DAHDI_LAW_DEFAULT} +}; + +static struct str_flag tdmox_flags[] = { + {"RBS", DAHDI_FLAG_RBS}, + {NULL, 0} +}; + +static struct str_flag tdmox_signalling[] = { + {"FXSLS", DAHDI_SIG_FXSLS}, + {"FXSKS", DAHDI_SIG_FXSKS}, + {"FXSGS", DAHDI_SIG_FXSGS}, + {"FXOLS", DAHDI_SIG_FXOLS}, + {"FXOKS", DAHDI_SIG_FXOKS}, + {"FXOGS", DAHDI_SIG_FXOGS}, + {"E&M", DAHDI_SIG_EM}, + {"E&M-E1", DAHDI_SIG_EM_E1}, + {"Clear", DAHDI_SIG_CLEAR}, + {"HDLCRAW", DAHDI_SIG_HDLCRAW}, + {"HDLCFCS", DAHDI_SIG_HDLCFCS}, + {"HDLCNET", DAHDI_SIG_HDLCNET}, + {"Hardware-assisted HDLC", DAHDI_SIG_HARDHDLC}, + {"MTP2", DAHDI_SIG_MTP2}, + {"Slave", DAHDI_SIG_SLAVE}, + {"CAS", DAHDI_SIG_CAS}, + {"DACS", DAHDI_SIG_DACS}, + {"DACS+RBS", DAHDI_SIG_DACS_RBS}, + {"SF (ToneOnly)", DAHDI_SIG_SF}, + {"Unconfigured", DAHDI_SIG_NONE}, /* 0 */ + {NULL, 0} +}; + +static struct str_flag tdmox_linecompat[] = { + {"D4", DAHDI_CONFIG_D4}, + {"ESF", DAHDI_CONFIG_ESF}, + {"AMI", DAHDI_CONFIG_AMI}, + {"B8ZS", DAHDI_CONFIG_B8ZS}, + {"CCS", DAHDI_CONFIG_CCS}, + {"HDB3", DAHDI_CONFIG_HDB3}, + {"CRC4", DAHDI_CONFIG_CRC4}, + {"NTTE", DAHDI_CONFIG_NOTOPEN}, + {"TERM", DAHDI_CONFIG_NTTE}, + {"NOTOPEN", DAHDI_CONFIG_TERM}, + {NULL, 0} +}; + +static struct tdmox_sysfs_span *tdmox_spans[TDMOX_SPANS_MAX]; + +/*== SECTION: kernel abstraction ==*/ + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18) + #define tdmox_sysfs_set_groups(_dev,_groups) _dev->groups=_groups; + static inline int tdmox_sysfs_add_groups(struct device *dev, + const struct attribute_group **groups) { return 0; } + #define tdmox_sysfs_remove_groups(dev,groups) do {} while (0) +#else + #define tdmox_sysfs_set_groups(dev,groups) do {} while (0) + static int tdmox_sysfs_add_groups(struct device *dev, + const struct attribute_group **groups) + { + int error = 0, i; + + for (i = 0; groups[i]; i++) { + error = sysfs_create_group(&dev->kobj, groups[i]); + if (error) { + while (--i >= 0) + sysfs_remove_group(&dev->kobj, + groups[i]); + break; + } + } + return error; + } + + static void tdmox_sysfs_remove_groups(struct device *dev, + const struct attribute_group **groups) + { + int i; + + for (i = 0; groups[i]; i++) + sysfs_remove_group(&dev->kobj, groups[i]); + } +#endif + +/*== SECTION: Code ==*/ + +//TODO - correct ? +static int tdmox_match(struct device *dev, struct device_driver *driver) +{ + return !strncmp(dev->bus->name, driver->name, strlen(driver->name)); +} + +static void tdmox_bus_release(struct device *dev) +{ + module_printk(KERN_INFO, "dahdi_tdmox bus release\n"); +} + +struct bus_type tdmox_bus_type = { + .name = "dahdi_tdmox", + .match = tdmox_match, +//TODO?? .hotplug = tdmox_hotplug, +}; + +struct device tdmox_bus = { + .bus = &tdmox_bus_type, + .release = tdmox_bus_release +}; + +/* no references to dahdi_tdmox devices, so, fake release (for now) */ +static void tdmox_dev_release(struct device *dev) +{ } + +static int tdmox_strtoi(const char *s, unsigned int base, int dflt) { + long val = 0; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) + if (0 == kstrtol(s, base, &val)) + return val; + return dflt; +#else + char *endp=NULL; + val = simple_strtol(s, &endp, base); + if (0 == val && endp == s) + return dflt; + return val; +#endif +} + +static ssize_t show_attr(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct tdmox_sysfs_attr *tsattr = to_tdmox_sysfs_attr(attr); + return snprintf(buf, PAGE_SIZE, "%s\n", tsattr->data); +} + +static void strip_cpy(char *dst, const char *src, int len) +{ + const char *s = src; + + while(*s && isspace(*s)) s++; /* Locating first non-space symbol */ + strncpy(dst, s, len); + dst = strstrip(dst); /* Compiler will warn us if we will not use result. */ +} + +static void tdmox_sysfs_clear_attrs(struct tdmox_sysfs_span *tsspan) +{ + int sa_id, ca_id, chan_id; + + for (sa_id=0; tsspan->tsattrs[sa_id]; ++sa_id) + tsspan->tsattrs[sa_id]->data[0] = 0; + + for (chan_id=0; tsspan->dev_attr_group[chan_id]; ++chan_id) { + struct tdmox_sysfs_chan *tschan = &(tsspan->chans[chan_id]); + for (ca_id=0; tschan->tsattrs[ca_id]; ++ca_id) { + tschan->tsattrs[ca_id]->data[0] = 0; + } + } +} + +int tdmox_register(struct device *dev, struct device_attribute *attr) +{ + struct tdmox_sysfs_attr *tsattr; + struct tdmox_sysfs_span *tsspan; + struct dahdi_tdmox_span dds; + int ret = 0; + + tsattr = to_tdmox_sysfs_attr(attr); + tsspan = to_tdmox_sysfs_span(dev); + + dds.numchans = tdmox_strtoi(tsspan->attr_channels.data, 10, 0); + strncpy(dds.driver, tsspan->attr_driver.data, sizeof(dds.driver)); + strncpy(dds.addr, tsspan->attr_addr.data, sizeof(dds.addr)); + dds.span_id_sysfs = tsspan->span_id; + + if (!strcmp(tsattr->data, "1")) { + return dahdi_tdmox_create(&dds); + } else { + ret = dahdi_tdmox_destroy(&dds); + tdmox_sysfs_clear_attrs(tsspan); + return ret; + } +} + +static ssize_t store_attr(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct tdmox_sysfs_attr *tsattr = to_tdmox_sysfs_attr(attr); + + strip_cpy(tsattr->data, buf, sizeof(tsattr->data)); + + if (!strncmp(tsattr->name, "register", sizeof(tsattr->name))) + tdmox_register(dev, attr); + + return count; +} + +static void tdmox_sysfs_init_attr(struct tdmox_sysfs_attr *attr, const char *name) +{ + if (!attr) + return; + + strncpy(attr->name, name, sizeof(attr->name)); + attr->attr.attr.name = attr->name; +// attr->owner = driver->module; + attr->attr.attr.mode = S_IRUSR | S_IWUSR; + attr->attr.show = show_attr; + attr->attr.store = store_attr; +} + +static int tdmox_sysfs_create_attrs(struct device *dev, struct tdmox_sysfs_span *tsspan) +{ + int ret = 0, attr_id; + + for (attr_id=0; tsspan->tsattrs[attr_id]; ++attr_id) { + ret = device_create_file(dev, &(tsspan->tsattrs[attr_id]->attr)); + if (ret) { + /* TODO: Check this (simulate), print error here */ + while(attr_id--) + device_remove_file(dev, &(tsspan->tsattrs[attr_id]->attr)); + return ret; + } + } + return ret; +} + +static void tdmox_sysfs_remove_attrs(struct device *dev, struct tdmox_sysfs_span *tsspan) +{ + int attr_id; + + for (attr_id=0; tsspan->tsattrs[attr_id]; ++attr_id) { + device_remove_file(dev, &(tsspan->tsattrs[attr_id]->attr)); + } +} + +static void tdmox_sysfs_addattr(struct tdmox_sysfs_span *tsspan, struct tdmox_sysfs_attr *attr, const char *name) +{ + if (tsspan->attrs_total >= TDMOX_SPAN_ATTRS-1) /* Tailing element MUST be NULL */ + return; //TODO: WARN here (assert ?) + + tdmox_sysfs_init_attr(attr, name); + tsspan->tsattrs[tsspan->attrs_total] = attr; + tsspan->attrs_total++; +} + +static void tdmox_sysfs_chan_addattr(struct tdmox_sysfs_chan *tschan, struct tdmox_sysfs_attr *attr, const char *name) +{ + if (tschan->attrs_total >= TDMOX_CHAN_ATTRS-1) /* Tailing element MUST be NULL */ + return; //TODO: WARN here (assert ?) + + tdmox_sysfs_init_attr(attr, name); + tschan->tsattrs[tschan->attrs_total] = attr; + tschan->attrs[tschan->attrs_total] = &(attr->attr.attr); + tschan->attrs_total++; +} + +static int tdmox_sysfs_init_spans(void) +{ + int span_id, chan_id, ret=0; + + for (span_id=0; span_idchans = kzalloc(sizeof(struct tdmox_sysfs_chan)*channels, GFP_KERNEL); + if (!tsspan->chans) + return -ENOMEM; + + dev = &(tsspan->device); + tsspan->dev_attr_group = (const struct attribute_group **)tsspan->_dev_attr_group; + tsspan->span_id = span_id; + dev_set_name(dev, "span-%d", span_id+1); + dev->bus = &tdmox_bus_type; + dev->parent = &tdmox_bus; + // dev_set_drvdata(dev, span); + dev->release = tdmox_dev_release; + + for (chan_id=0; chan_idchans[chan_id]); + + tdmox_sysfs_chan_addattr(tschan, &(tschan->attr_name), "name"); + tdmox_sysfs_chan_addattr(tschan, &(tschan->attr_sig), "sig"); + tdmox_sysfs_chan_addattr(tschan, &(tschan->attr_sigcap), "sigcap"); + + snprintf (tschan->name, sizeof(tschan->name), "chan-%d", chan_id+1); + tschan->attr_group.name = tschan->name; + tschan->attr_group.attrs = tschan->attrs; + tsspan->dev_attr_group[chan_id] = &(tschan->attr_group); + } + tdmox_sysfs_set_groups(dev, tsspan->dev_attr_group); + + ret = device_register(dev); + if (ret) { + module_printk(KERN_ERR, "device register failed\n"); + while(span_id>0) + device_unregister(&(tdmox_spans[span_id-1]->device)); + return ret; + } + tsspan->device_registered = 1; + tdmox_spans[span_id] = tsspan; + + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_addr), "addr"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_channels), "channels"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_deflaw), "deflaw"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_desc), "desc"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_devicetype), "devicetype"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_driver), "driver"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_flags), "flags"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_linecompat), "linecompat"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_location), "location"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_manufacturer), "manufacturer"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_name), "name"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_spantype), "spantype"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_timing), "timing"); + tdmox_sysfs_addattr(tsspan, &(tsspan->attr_register), "register"); + + ret = tdmox_sysfs_create_attrs(dev, tsspan); + if (ret) + return ret; + tsspan->attrs_registered = 1; + + ret = tdmox_sysfs_add_groups(dev, tsspan->dev_attr_group); + if (ret) { + module_printk(KERN_ERR, "device add_groups failed\n"); + return ret; + } + tsspan->groups_registered = 1; + } + return ret; +} + +static void tdmox_copy_tsattr(char *dst, const struct tdmox_sysfs_attr *tsattr, int len) +{ + if (strlen(tsattr->data) > 0) + strlcpy(dst, tsattr->data, len); +} + +static void tdmox_ptr_tsattr(const char **dst, const struct tdmox_sysfs_attr *tsattr) +{ + if (strlen(tsattr->data) > 0) + *dst = tsattr->data; +} + +static void tdmox_parse_spantype (const char *str, enum spantypes *type) +{ + char data[TDMOX_CHANNELS_STRLEN]; + + if (!strlen(str)) + return; + + *type = SPANTYPE_INVALID; + if (sscanf(str, "%s", data) > 0) + *type = dahdi_str2spantype(data); +} + +/* up to unsigned long bits in flags */ +void tdmox_parse_flags(const char *str, const struct str_flag *str2flag, void *res_flag) +{ + char data[TDMOX_CHANNELS_STRLEN], *stringp, *pdata; + int i; + + if (!strlen(str)) + return; + *((unsigned long *)res_flag) = 0; + strncpy(data, str, TDMOX_CHANNELS_STRLEN); + + + stringp = data; + while ( (pdata = strsep(&stringp, " \r\t\n")) ) { + for (i=0; NULL != str2flag[i].str; ++i) { + if (!strcmp(pdata, str2flag[i].str)) { + *((unsigned long *)res_flag) |= str2flag[i].flag; + } + } + } +} + +void tdmox_dump_dynamic(const struct dahdi_tdmox *d) +{ + const struct dahdi_span *span = &(d->span); + + module_printk(KERN_INFO, "Dumping dahdi_tdmox...\n"); + module_printk(KERN_INFO, "d::addr: %s\n", d->addr); + module_printk(KERN_INFO, "d::dname: %s\n", d->dname); + module_printk(KERN_INFO, "ddev::devicetype: %s\n", d->ddev->devicetype); + module_printk(KERN_INFO, "ddev::hardware_id: %s\n", d->ddev->hardware_id); + module_printk(KERN_INFO, "ddev::location: %s\n", d->ddev->location); + module_printk(KERN_INFO, "ddev::manufacturer: %s\n", d->ddev->manufacturer); + + module_printk(KERN_INFO, "span::deflaw: %x\n", span->deflaw); + module_printk(KERN_INFO, "span::desc: %s\n", span->desc); + module_printk(KERN_INFO, "span::flags: %lx\n", span->flags); + module_printk(KERN_INFO, "span::linecompat: %x\n", span->linecompat); + module_printk(KERN_INFO, "span::name: %s\n", span->name); + module_printk(KERN_INFO, "span::spantype: %x\n", span->spantype); +} + +int dahdi_tdmox_sysfs_cfg_ddev(struct dahdi_tdmox *d, int span_id_sysfs) +{ + int ret=0, sigcap=0, chan_id=0; + struct dahdi_span *span = &(d->span); + struct tdmox_sysfs_span *tsspan = tdmox_spans[span_id_sysfs]; + const char *name_prefix=NULL; + + /* SPAN's and channel's defaults */ +/* span->linecompat = DAHDI_CONFIG_HDB3 | DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4 | DAHDI_CONFIG_AMI; */ + span->deflaw = DAHDI_LAW_ALAW; + sigcap = DAHDI_SIG_EM | + DAHDI_SIG_FXSLS | DAHDI_SIG_FXSKS | + DAHDI_SIG_FXSGS | DAHDI_SIG_FXOLS | + DAHDI_SIG_FXOKS | DAHDI_SIG_FXOGS | + DAHDI_SIG_SF | DAHDI_SIG_DACS_RBS | + DAHDI_SIG_CAS | DAHDI_SIG_CLEAR; + +/* driver (dname) and addr have to be filled by create_span, but, ... + * Doing once again, in case, if someone will remove it from create_dynamic. */ + tdmox_copy_tsattr(d->addr, &(tsspan->attr_addr), sizeof(d->addr)); + tdmox_copy_tsattr(d->dname, &(tsspan->attr_driver), sizeof(d->dname)); + + tdmox_parse_flags(tsspan->attr_linecompat.data, tdmox_linecompat, &(span->linecompat)); //int + + if (span->linecompat) { + span->spantype = SPANTYPE_DIGITAL_E1; + /* Should I add RBS flag by default ? Possible to disable via sysfs ? */ + sigcap |= DAHDI_SIG_EM_E1; + name_prefix = "TDM_D"; + } else { + span->spantype = SPANTYPE_ANALOG_MIXED; + span->flags |= DAHDI_FLAG_RBS; + name_prefix = "TDM_A"; + } + snprintf(span->name, sizeof(span->name), "%s/%d", + name_prefix, tsspan->span_id+1); + snprintf(span->desc, sizeof(span->name), "%s/%s/%s", + name_prefix, d->dname, d->addr); + + d->ddev->devicetype = span->name; + d->ddev->hardware_id = span->name; + d->timing = tdmox_strtoi(tsspan->attr_timing.data, 10, 0); + + tdmox_copy_tsattr(span->desc, &(tsspan->attr_desc), sizeof(span->desc)); + tdmox_copy_tsattr(span->name, &(tsspan->attr_name), sizeof(span->name)); + + tdmox_ptr_tsattr(&(d->ddev->devicetype), &(tsspan->attr_devicetype)); + tdmox_ptr_tsattr(&(d->ddev->hardware_id), &(tsspan->attr_hardware_id)); + tdmox_ptr_tsattr(&(d->ddev->location), &(tsspan->attr_location)); + tdmox_ptr_tsattr(&(d->ddev->manufacturer), &(tsspan->attr_manufacturer)); + + tdmox_parse_spantype(tsspan->attr_spantype.data, &(span->spantype)); + tdmox_parse_flags(tsspan->attr_flags.data, tdmox_flags, &(span->flags)); //unsigned long + /* We have to parse linecompat at head. Moved there */ + tdmox_parse_flags(tsspan->attr_deflaw.data, tdmox_law, &(span->deflaw)); //int + + for (chan_id = 0; chan_id < span->channels; chan_id++) { + struct tdmox_sysfs_chan *tschan = &(tsspan->chans[chan_id]); + struct dahdi_chan *dchan = d->chans[chan_id]; + const char *linetype=""; + + dchan->sigcap = sigcap; + tdmox_parse_flags(tschan->attr_sigcap.data, tdmox_signalling, &(dchan->sigcap)); + tdmox_parse_flags(tschan->attr_sig.data, tdmox_signalling, &(dchan->sig)); + if (dchan->sig) { + /* Seems user know, what he wants. + dahdi_genconf(Chans.pm) will probe "$type = 'FXO' if $signalling =~ /^FXS/;" + But, dahdi_cfg will see no diff for the channel, and will not ioctl(DAHDI_CHANCONFIG). + Let's do it manually. Any ideas ? + channel name= TDM_A_S(FXS), TDM_A_O(FXO), TDM_A_E(E&M) and patch Chans.pm ?*/ + if (dchan->sig & DAHDI_SIG_CLEAR) + dchan->flags |= DAHDI_FLAG_CLEAR; + else + dchan->flags |= DAHDI_FLAG_AUDIO; + } + /* Analog SPAN */ + if (dchan->sigcap & __DAHDI_SIG_FXS) + linetype = "O"; + else if (dchan->sigcap & __DAHDI_SIG_FXO) + linetype = "S"; + else if (dchan->sigcap & DAHDI_SIG_EM) + linetype = "E"; + /* Digital SPAN */ + if (span->linecompat) + linetype = ""; + + /* dahdi_chan->name HAVE TO BE <= 40 bytes... + channels > 999 or interface name > 4 bytes will overflow... */ + snprintf(dchan->name, sizeof(dchan->name), "%s%s/%s/%s/%d", + name_prefix, linetype, d->dname, d->addr, chan_id+1); + tdmox_copy_tsattr(dchan->name, &(tschan->attr_name), sizeof(dchan->name)); + + dchan->chanpos = chan_id + 1; + dchan->pvt = d; + } + + if (debug) + tdmox_dump_dynamic(d); + + return ret; +} + +int dahdi_tdmox_sysfs_init(void) +{ + int ret; + + spans = min(spans, TDMOX_SPANS_MAX); + channels = min(channels, TDMOX_CHANNELS_MAX); + memset(tdmox_spans, 0, sizeof(tdmox_spans)); + + ret = bus_register(&tdmox_bus_type); + if (ret) { + module_printk(KERN_ERR, "Failed to register dahdi_tdmox bus\n"); + return ret; + } + bus_registered = 1; + + dev_set_name(&tdmox_bus, "dahdi_tdmox"); + ret = device_register(&tdmox_bus); + if (ret) { + module_printk(KERN_ERR, "Unable to register dahdi_tdmox bus device\n"); + device_unregister(&tdmox_bus); + return ret; + } + device_registered = 1; + + ret = tdmox_sysfs_init_spans(); + if (ret) { + module_printk(KERN_ERR, "Unable to register SPANs in dahdi_tdmox\n"); + device_unregister(&tdmox_bus); + bus_unregister(&tdmox_bus_type); + bus_registered = device_registered = 0; + return ret; + } + module_printk(KERN_INFO, "dahdi_tdmox-sysfs initialized, spans=%d, channels=%d\n", + spans, channels); + + return ret; +} + +void dahdi_tdmox_sysfs_cleanup(void) +{ + int i; + + for (i=0; idevice); + if (tsspan->groups_registered) + tdmox_sysfs_remove_groups(dev, tsspan->dev_attr_group); + if (tsspan->attrs_registered) + tdmox_sysfs_remove_attrs(dev, tsspan); + if (tsspan->device_registered) + device_unregister(dev); + if (tsspan->chans) + kfree(tsspan->chans); + if (tsspan) + kfree(tsspan); + tdmox_spans[i] = NULL; + } + } + if (device_registered) + device_unregister(&tdmox_bus); + if (bus_registered) + bus_unregister(&tdmox_bus_type); + device_registered = bus_registered = 0; + + module_printk(KERN_INFO, "dahdi_tdmox-sysfs cleanup\n"); +} + +EXPORT_SYMBOL(dahdi_tdmox_sysfs_cfg_ddev); +EXPORT_SYMBOL(dahdi_tdmox_sysfs_init); +EXPORT_SYMBOL(dahdi_tdmox_sysfs_cleanup); + +module_param(spans, int, 0400); +MODULE_PARM_DESC(spans, "Maximum SPANs"); +module_param(channels, int, 0400); +MODULE_PARM_DESC(channels, "Maximum channels per SPAN"); diff -urN dahdi-linux.orig/drivers/dahdi/dahdi_tdmox/dahdi_tdmox-sysfs.h dahdi-linux/drivers/dahdi/dahdi_tdmox/dahdi_tdmox-sysfs.h --- dahdi-linux.orig/drivers/dahdi/dahdi_tdmox/dahdi_tdmox-sysfs.h 1970-01-01 07:00:00.000000000 +0700 +++ dahdi-linux/drivers/dahdi/dahdi_tdmox/dahdi_tdmox-sysfs.h 2014-07-24 17:10:02.000000000 +0700 @@ -0,0 +1,24 @@ +#ifndef _DAHDI_TDMOX_SYSFS_H +#define _DAHDI_TDMOX_SYSFS_H + +/* + WARNING: Before editing definitions, + CARE about tdmox_sysfs_span & tdmox_sysfs_span[TDMOX_CHANNELS_MAX] size. + kzalloc will probably try to allocate from cache, which, on many platforms + limited to 131072 bytes. +*/ +#define TDMOX_SPANS_MAX 32 /* Maximum SPAN's */ +/* Maximim channels per SPAN. Limited in dahdi_tdmox.h */ +#define TDMOX_CHANNELS_MAX DAHDI_DYNAMIC_MAX_CHANS +#define TDMOX_CHANNELS_STRLEN 80 /* sysfs attribute data length */ +#define TDMOX_ATTR_NAME_STRLEN 16 /* Attribute name length (Span) */ +#define TDMOX_CHAN_NAME_STRLEN 16 /* Attribute name length (Chan) */ + +struct dahdi_dynamic; +struct dahdi_tdmox; + +int dahdi_tdmox_sysfs_cfg_ddev(struct dahdi_tdmox *d, int span_id_sysfs); +int dahdi_tdmox_sysfs_init(void); +void dahdi_tdmox_sysfs_cleanup(void); + +#endif /* _DAHDI_TDMOX_SYSFS_H */ diff -urN dahdi-linux.orig/drivers/dahdi/dahdi_tdmox/Kbuild dahdi-linux/drivers/dahdi/dahdi_tdmox/Kbuild --- dahdi-linux.orig/drivers/dahdi/dahdi_tdmox/Kbuild 1970-01-01 07:00:00.000000000 +0700 +++ dahdi-linux/drivers/dahdi/dahdi_tdmox/Kbuild 2014-08-07 18:47:15.733045715 +0700 @@ -0,0 +1,16 @@ +ifneq (,$(wildcard $(srctree)/include/linux/skbuff.h)) +ifeq ($(shell grep "skb_linearize.*(.*, .* gfp)" $(srctree)/include/linux/skbuff.h),) +CFLAGS_dahdi_tdmox_eth.o := -DNEW_SKB_LINEARIZE +CFLAGS_dahdi_tdmox_ethmf.o := -DNEW_SKB_LINEARIZE +endif +endif + +EXTRA_CFLAGS += -I$(src)/.. -Wno-undef + +obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_TDMOX) += dahdi_tdmox.o +obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_TDMOX_ETH) += dahdi_tdmox_eth.o + +dahdi_tdmox-objs := dahdi_tdmox-base.o dahdi_tdmox-sysfs.o + +$(obj)/dahdi_tdmox.o: $(src)/dahdi_tdmox.h $(src)/dahdi_tdmox-sysfs.h +$(obj)/dahdi_tdmox_eth.o: $(src)/dahdi_tdmox.h $(src)/dahdi_tdmox-sysfs.h diff -urN dahdi-linux.orig/drivers/dahdi/Kbuild dahdi-linux/drivers/dahdi/Kbuild --- dahdi-linux.orig/drivers/dahdi/Kbuild 2014-08-07 18:38:39.036741758 +0700 +++ dahdi-linux/drivers/dahdi/Kbuild 2014-08-07 18:47:15.741045906 +0700 @@ -5,6 +5,7 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC_ETH) += dahdi_dynamic_eth.o obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC_ETHMF) += dahdi_dynamic_ethmf.o obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_TRANSCODE) += dahdi_transcode.o +obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_TDMOX) += dahdi_tdmox/ ifdef CONFIG_PCI obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OCT612X) += oct612x/