diff options
-rw-r--r-- | kernel/zaptel-base.c | 313 | ||||
-rw-r--r-- | kernel/zconfig.h | 6 |
2 files changed, 228 insertions, 91 deletions
diff --git a/kernel/zaptel-base.c b/kernel/zaptel-base.c index 23e6a6c..89346b4 100644 --- a/kernel/zaptel-base.c +++ b/kernel/zaptel-base.c @@ -278,6 +278,17 @@ static struct int dst; /* dst conf number */ } conf_links[ZT_MAX_CONF + 1]; +#ifdef CONFIG_ZAPTEL_CORE_TIMER + +static struct core_timer { + struct timer_list timer; + struct timespec start_interval; + atomic_t count; + atomic_t shutdown; + atomic_t last_count; +} core_timer; + +#endif /* CONFIG_ZAPTEL_CORE_TIMER */ /* There are three sets of conference sum accumulators. One for the current sample chunk (conf_sums), one for the next sample chunk (conf_sums_next), and @@ -2544,12 +2555,21 @@ static int zt_specchan_release(struct inode *node, struct file *file, int unit) return res; } +static int can_open_timer(void) +{ +#ifdef CONFIG_ZAPTEL_CORE_TIMER + return 1; +#else + return maxspans > 0; +#endif +} + static struct zt_chan *zt_alloc_pseudo(void) { struct zt_chan *pseudo; unsigned long flags; - /* Don't allow /dev/zap/pseudo to open if there are no spans */ - if (maxspans < 1) + /* Don't allow /dev/zap/pseudo to open if there is not timing */ + if (!can_open_timer()) return NULL; pseudo = kmalloc(sizeof(struct zt_chan), GFP_KERNEL); if (!pseudo) @@ -2609,7 +2629,7 @@ static int zt_open(struct inode *inode, struct file *file) return -ENXIO; } if (unit == 253) { - if (maxspans) { + if (can_open_timer()) { return zt_timing_open(inode, file); } else { return -ENXIO; @@ -2618,16 +2638,13 @@ static int zt_open(struct inode *inode, struct file *file) if (unit == 254) return zt_chan_open(inode, file); if (unit == 255) { - if (maxspans) { - chan = zt_alloc_pseudo(); - if (chan) { - file->private_data = chan; - return zt_specchan_open(inode, file, chan->channo, 1); - } else { - return -ENXIO; - } - } else + chan = zt_alloc_pseudo(); + if (chan) { + file->private_data = chan; + return zt_specchan_open(inode, file, chan->channo, 1); + } else { return -ENXIO; + } } return zt_specchan_open(inode, file, unit, 1); } @@ -7449,10 +7466,197 @@ int zt_transmit(struct zt_span *span) return 0; } +static void process_masterspan(void) +{ + int unsigned long flags, flagso; + int x, y, z; + +#ifdef CONFIG_ZAPTEL_CORE_TIMER + /* We increment the calls since start here, so that if we switch over + * to the core timer, we know how many times we need to call + * process_masterspan in order to catch up since this function needs + * to be called 1000 times per second. */ + atomic_inc(&core_timer.count); +#endif + /* Hold the big zap lock for the duration of major + activities which touch all sorts of channels */ + spin_lock_irqsave(&bigzaplock, flagso); + /* Process any timers */ + process_timers(); + /* If we have dynamic stuff, call the ioctl with 0,0 parameters to + make it run */ + if (zt_dynamic_ioctl) + zt_dynamic_ioctl(0,0); + for (x=1;x<maxchans;x++) { + if (chans[x] && chans[x]->confmode && !(chans[x]->flags & ZT_FLAG_PSEUDO)) { + u_char *data; + spin_lock_irqsave(&chans[x]->lock, flags); + data = __buf_peek(&chans[x]->confin); + __zt_receive_chunk(chans[x], data); + if (data) + __buf_pull(&chans[x]->confin, NULL,chans[x], "confreceive"); + spin_unlock_irqrestore(&chans[x]->lock, flags); + } + } + /* This is the master channel, so make things switch over */ + rotate_sums(); + /* do all the pseudo and/or conferenced channel receives (getbuf's) */ + for (x=1;x<maxchans;x++) { + if (chans[x] && (chans[x]->flags & ZT_FLAG_PSEUDO)) { + spin_lock_irqsave(&chans[x]->lock, flags); + __zt_transmit_chunk(chans[x], NULL); + spin_unlock_irqrestore(&chans[x]->lock, flags); + } + } + if (maxlinks) { +#ifdef CONFIG_ZAPTEL_MMX + zt_kernel_fpu_begin(); +#endif + /* process all the conf links */ + for(x = 1; x <= maxlinks; x++) { + /* if we have a destination conf */ + if (((z = confalias[conf_links[x].dst]) > 0) && + ((y = confalias[conf_links[x].src]) > 0)) { + ACSS(conf_sums[z], conf_sums[y]); + } + } +#ifdef CONFIG_ZAPTEL_MMX + kernel_fpu_end(); +#endif + } + /* do all the pseudo/conferenced channel transmits (putbuf's) */ + for (x=1;x<maxchans;x++) { + if (chans[x] && (chans[x]->flags & ZT_FLAG_PSEUDO)) { + unsigned char tmp[ZT_CHUNKSIZE]; + spin_lock_irqsave(&chans[x]->lock, flags); + __zt_getempty(chans[x], tmp); + __zt_receive_chunk(chans[x], tmp); + spin_unlock_irqrestore(&chans[x]->lock, flags); + } + } + for (x=1;x<maxchans;x++) { + if (chans[x] && chans[x]->confmode && !(chans[x]->flags & ZT_FLAG_PSEUDO)) { + u_char *data; + spin_lock_irqsave(&chans[x]->lock, flags); + data = __buf_pushpeek(&chans[x]->confout); + __zt_transmit_chunk(chans[x], data); + if (data) + __buf_push(&chans[x]->confout, NULL, "conftransmit"); + spin_unlock_irqrestore(&chans[x]->lock, flags); + } + } +#ifdef ZAPTEL_SYNC_TICK + for (x=0;x<maxspans;x++) { + struct zt_span *s = spans[x]; + + if (s && s->sync_tick) + s->sync_tick(s, s == master); + } +#endif + spin_unlock_irqrestore(&bigzaplock, flagso); +} + +#ifndef CONFIG_ZAPTEL_CORE_TIMER + +static void coretimer_init(void) +{ + return; +} + +static void coretimer_cleanup(void) +{ + return; +} + +#else + +static unsigned long core_diff_ms(struct timespec *t0, struct timespec *t1) +{ + long nanosec, sec; + unsigned long ms; + sec = (t1->tv_sec - t0->tv_sec); + nanosec = (t1->tv_nsec - t0->tv_nsec); + while (nanosec >= NSEC_PER_SEC) { + nanosec -= NSEC_PER_SEC; + ++sec; + } + while (nanosec < 0) { + nanosec += NSEC_PER_SEC; + --sec; + } + ms = (sec * 1000) + (nanosec / 1000000L); + return ms; +} + +static void coretimer_func(unsigned long param) +{ + unsigned long ms_since_start; + struct timespec now; + const unsigned long MAX_INTERVAL = 100000L; + const unsigned long FOURMS_INTERVAL = HZ/250; + const unsigned long ONESEC_INTERVAL = HZ; + + now = current_kernel_time(); + + if (atomic_read(&core_timer.count) == + atomic_read(&core_timer.last_count)) { + + /* This is the code path if a board driver is not calling + * dahdi_receive, and therefore the core of dahdi needs to + * perform the master span processing itself. */ + + if (!atomic_read(&core_timer.shutdown)) + mod_timer(&core_timer.timer, jiffies + FOURMS_INTERVAL); + + ms_since_start = core_diff_ms(&core_timer.start_interval, &now); + while (ms_since_start > atomic_read(&core_timer.count)) + process_masterspan(); + + if (ms_since_start > MAX_INTERVAL) { + atomic_set(&core_timer.count, 0); + atomic_set(&core_timer.last_count, 0); + core_timer.start_interval = now; + } else { + atomic_set(&core_timer.last_count, + atomic_read(&core_timer.count)); + } + + } else { + + /* It looks like a board driver is calling dahdi_receive. We + * will just check again in a second. */ + atomic_set(&core_timer.count, 0); + atomic_set(&core_timer.last_count, 0); + core_timer.start_interval = now; + if (!atomic_read(&core_timer.shutdown)) + mod_timer(&core_timer.timer, jiffies + ONESEC_INTERVAL); + } +} + +static void coretimer_init(void) +{ + init_timer(&core_timer.timer); + core_timer.timer.function = coretimer_func; + core_timer.start_interval = current_kernel_time(); + core_timer.timer.expires = jiffies + HZ; + atomic_set(&core_timer.count, 0); + atomic_set(&core_timer.shutdown, 0); + add_timer(&core_timer.timer); +} + +static void coretimer_cleanup(void) +{ + atomic_set(&core_timer.shutdown, 1); + del_timer_sync(&core_timer.timer); +} + +#endif /* CONFIG_ZAPTEL_CORE_TIMER */ + + int zt_receive(struct zt_span *span) { int x,y,z; - unsigned long flags, flagso; + unsigned long flags; #if 1 #ifdef CONFIG_ZAPTEL_WATCHDOG @@ -7531,84 +7735,8 @@ int zt_receive(struct zt_span *span) } } - if (span == master) { - /* Hold the big zap lock for the duration of major - activities which touch all sorts of channels */ - spin_lock_irqsave(&bigzaplock, flagso); - /* Process any timers */ - process_timers(); - /* If we have dynamic stuff, call the ioctl with 0,0 parameters to - make it run */ - if (zt_dynamic_ioctl) - zt_dynamic_ioctl(0,0); - for (x=1;x<maxchans;x++) { - if (chans[x] && chans[x]->confmode && !(chans[x]->flags & ZT_FLAG_PSEUDO)) { - u_char *data; - spin_lock_irqsave(&chans[x]->lock, flags); - data = __buf_peek(&chans[x]->confin); - __zt_receive_chunk(chans[x], data); - if (data) - __buf_pull(&chans[x]->confin, NULL,chans[x], "confreceive"); - spin_unlock_irqrestore(&chans[x]->lock, flags); - } - } - /* This is the master channel, so make things switch over */ - rotate_sums(); - /* do all the pseudo and/or conferenced channel receives (getbuf's) */ - for (x=1;x<maxchans;x++) { - if (chans[x] && (chans[x]->flags & ZT_FLAG_PSEUDO)) { - spin_lock_irqsave(&chans[x]->lock, flags); - __zt_transmit_chunk(chans[x], NULL); - spin_unlock_irqrestore(&chans[x]->lock, flags); - } - } - if (maxlinks) { -#ifdef CONFIG_ZAPTEL_MMX - zt_kernel_fpu_begin(); -#endif - /* process all the conf links */ - for(x = 1; x <= maxlinks; x++) { - /* if we have a destination conf */ - if (((z = confalias[conf_links[x].dst]) > 0) && - ((y = confalias[conf_links[x].src]) > 0)) { - ACSS(conf_sums[z], conf_sums[y]); - } - } -#ifdef CONFIG_ZAPTEL_MMX - kernel_fpu_end(); -#endif - } - /* do all the pseudo/conferenced channel transmits (putbuf's) */ - for (x=1;x<maxchans;x++) { - if (chans[x] && (chans[x]->flags & ZT_FLAG_PSEUDO)) { - unsigned char tmp[ZT_CHUNKSIZE]; - spin_lock_irqsave(&chans[x]->lock, flags); - __zt_getempty(chans[x], tmp); - __zt_receive_chunk(chans[x], tmp); - spin_unlock_irqrestore(&chans[x]->lock, flags); - } - } - for (x=1;x<maxchans;x++) { - if (chans[x] && chans[x]->confmode && !(chans[x]->flags & ZT_FLAG_PSEUDO)) { - u_char *data; - spin_lock_irqsave(&chans[x]->lock, flags); - data = __buf_pushpeek(&chans[x]->confout); - __zt_transmit_chunk(chans[x], data); - if (data) - __buf_push(&chans[x]->confout, NULL, "conftransmit"); - spin_unlock_irqrestore(&chans[x]->lock, flags); - } - } -#ifdef ZAPTEL_SYNC_TICK - for (x=0;x<maxspans;x++) { - struct zt_span *s = spans[x]; - - if (s && s->sync_tick) - s->sync_tick(s, s == master); - } -#endif - spin_unlock_irqrestore(&bigzaplock, flagso); - } + if (span == master) + process_masterspan(); #endif return 0; } @@ -7783,12 +7911,15 @@ static int __init zt_init(void) { #ifdef CONFIG_ZAPTEL_WATCHDOG watchdog_init(); #endif + coretimer_init(); return res; } static void __exit zt_cleanup(void) { int x; + coretimer_cleanup(); + #ifdef CONFIG_PROC_FS remove_proc_entry("zaptel", NULL); #endif diff --git a/kernel/zconfig.h b/kernel/zconfig.h index 26d1755..d2eca7d 100644 --- a/kernel/zconfig.h +++ b/kernel/zconfig.h @@ -150,6 +150,12 @@ /* #define CONFIG_ZAPTEL_WATCHDOG */ /* + * Define CONFIG_ZAPTEL_CORE_TIMER if you would like dahdi to always provide a + * timing source regardless of which spans / drivers are configured. + */ +/* #define CONFIG_ZAPTEL_CORE_TIMER */ + +/* * Uncomment for Non-standard FXS groundstart start state (A=Low, B=Low) * particularly for CAC channel bank groundstart FXO ports. */ |