diff options
author | Andreas Eversberg <andreas@eversberg.eu> | 2008-09-06 09:03:46 +0200 |
---|---|---|
committer | Karsten Keil <kkeil@suse.de> | 2009-01-09 22:44:27 +0100 |
commit | 3bd69ad197a4a3d0085a5dc3b5796111bf176b12 (patch) | |
tree | 573d7c81f97ff0eb15f914d43dbf9f393cf00ded | |
parent | 55a6af9749d6a31e087f304f2ea39db3bb7b3f3e (diff) |
mISDN: Add ISDN sample clock API to mISDN core
Add ISDN sample clock API to mISDN core (new file clock.c)
hfcmulti and mISDNdsp use clock API.
Signed-off-by: Andreas Eversberg <andreas@eversberg.eu>
Signed-off-by: Karsten Keil <kkeil@suse.de>
-rw-r--r-- | drivers/isdn/hardware/mISDN/hfc_multi.h | 3 | ||||
-rw-r--r-- | drivers/isdn/hardware/mISDN/hfcmulti.c | 45 | ||||
-rw-r--r-- | drivers/isdn/mISDN/Makefile | 2 | ||||
-rw-r--r-- | drivers/isdn/mISDN/clock.c | 216 | ||||
-rw-r--r-- | drivers/isdn/mISDN/core.c | 1 | ||||
-rw-r--r-- | drivers/isdn/mISDN/core.h | 2 | ||||
-rw-r--r-- | drivers/isdn/mISDN/dsp_cmx.c | 40 | ||||
-rw-r--r-- | include/linux/mISDNif.h | 17 |
8 files changed, 294 insertions, 32 deletions
diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h index 4aa6a8b41f50..663b77f578be 100644 --- a/drivers/isdn/hardware/mISDN/hfc_multi.h +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -197,6 +197,9 @@ struct hfc_multi { spinlock_t lock; /* the lock */ + struct mISDNclock *iclock; /* isdn clock support */ + int iclock_on; + /* * the channel index is counted from 0, regardless where the channel * is located on the hfc-channel. diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index 3fc2e9d95341..592db93105f9 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -133,6 +133,12 @@ * Give the value of the clock control register (A_ST_CLK_DLY) * of the S/T interfaces in TE mode. * This register is needed for the TBR3 certification, so don't change it. + * + * clock: + * NOTE: only one clockdelay_te value must be given once + * Selects interface with clock source for mISDN and applications. + * Set to card number starting with 1. Set to -1 to disable. + * By default, the first card is used as clock source. */ /* @@ -190,12 +196,13 @@ static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30 }; */ static uint type[MAX_CARDS]; -static uint pcm[MAX_CARDS]; -static uint dslot[MAX_CARDS]; +static int pcm[MAX_CARDS]; +static int dslot[MAX_CARDS]; static uint iomode[MAX_CARDS]; static uint port[MAX_PORTS]; static uint debug; static uint poll; +static int clock; static uint timer; static uint clockdelay_te = CLKDEL_TE; static uint clockdelay_nt = CLKDEL_NT; @@ -207,12 +214,13 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(HFC_MULTI_VERSION); module_param(debug, uint, S_IRUGO | S_IWUSR); module_param(poll, uint, S_IRUGO | S_IWUSR); +module_param(clock, int, S_IRUGO | S_IWUSR); module_param(timer, uint, S_IRUGO | S_IWUSR); module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR); module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR); module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(pcm, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(dslot, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(pcm, int, NULL, S_IRUGO | S_IWUSR); +module_param_array(dslot, int, NULL, S_IRUGO | S_IWUSR); module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); @@ -2629,6 +2637,7 @@ hfcmulti_interrupt(int intno, void *dev_id) iqcnt = 0; } #endif + if (!r_irq_statech && !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | V_MISC_IRQSTA | V_FR_IRQSTA))) { @@ -2683,11 +2692,13 @@ hfcmulti_interrupt(int intno, void *dev_id) plxsd_checksync(hc, 0); } } - if (r_irq_misc & V_TI_IRQ) + if (r_irq_misc & V_TI_IRQ) { + if (hc->iclock_on) + mISDN_clock_update(hc->iclock, poll, NULL); handle_timer_irq(hc); + } if (r_irq_misc & V_DTMF_IRQ) { - /* -> DTMF IRQ */ hfcmulti_dtmf(hc); } if (r_irq_misc & V_IRQ_PROC) { @@ -4075,6 +4086,15 @@ hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) return err; } +static int +clockctl(void *priv, int enable) +{ + struct hfc_multi *hc = priv; + + hc->iclock_on = enable; + return 0; +} + /* * initialize the card */ @@ -4489,10 +4509,14 @@ release_card(struct hfc_multi *hc) printk(KERN_WARNING "%s: release card (%d) entered\n", __func__, hc->id); + /* unregister clock source */ + if (hc->iclock) + mISDN_unregister_clock(hc->iclock); + + /* disable irq */ spin_lock_irqsave(&hc->lock, flags); disable_hwirq(hc); spin_unlock_irqrestore(&hc->lock, flags); - udelay(1000); /* dimm leds */ @@ -5003,6 +5027,10 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) list_add_tail(&hc->list, &HFClist); spin_unlock_irqrestore(&HFClock, flags); + /* use as clock source */ + if (clock == HFC_cnt + 1) + hc->iclock = mISDN_register_clock("HFCMulti", 0, clockctl, hc); + /* initialize hardware */ ret_err = init_card(hc); if (ret_err) { @@ -5273,6 +5301,9 @@ HFCmulti_init(void) } + if (!clock) + clock = 1; + err = pci_register_driver(&hfcmultipci_driver); if (err < 0) { printk(KERN_ERR "error registering pci driver: %x\n", err); diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile index 1cb5e633cf75..0a6bd2a9e730 100644 --- a/drivers/isdn/mISDN/Makefile +++ b/drivers/isdn/mISDN/Makefile @@ -8,6 +8,6 @@ obj-$(CONFIG_MISDN_L1OIP) += l1oip.o # multi objects -mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o +mISDN_core-objs := core.o fsm.o socket.o clock.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o l1oip-objs := l1oip_core.o l1oip_codec.o diff --git a/drivers/isdn/mISDN/clock.c b/drivers/isdn/mISDN/clock.c new file mode 100644 index 000000000000..44d9c3d5d33d --- /dev/null +++ b/drivers/isdn/mISDN/clock.c @@ -0,0 +1,216 @@ +/* + * Copyright 2008 by Andreas Eversberg <andreas@eversberg.eu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * Quick API description: + * + * A clock source registers using mISDN_register_clock: + * name = text string to name clock source + * priority = value to priorize clock sources (0 = default) + * ctl = callback function to enable/disable clock source + * priv = private pointer of clock source + * return = pointer to clock source structure; + * + * Note: Callback 'ctl' can be called before mISDN_register_clock returns! + * Also it can be called during mISDN_unregister_clock. + * + * A clock source calls mISDN_clock_update with given samples elapsed, if + * enabled. If function call is delayed, tv must be set with the timestamp + * of the actual event. + * + * A clock source unregisters using mISDN_unregister_clock. + * + * To get current clock, call mISDN_clock_get. The signed short value + * counts the number of samples since. Time since last clock event is added. + * + */ + +#include <linux/types.h> +#include <linux/stddef.h> +#include <linux/spinlock.h> +#include <linux/mISDNif.h> +#include "core.h" + +static u_int *debug; +static LIST_HEAD(iclock_list); +DEFINE_RWLOCK(iclock_lock); +u16 iclock_count; /* counter of last clock */ +struct timeval iclock_tv; /* time stamp of last clock */ +int iclock_tv_valid; /* already received one timestamp */ +struct mISDNclock *iclock_current; + +void +mISDN_init_clock(u_int *dp) +{ + debug = dp; + do_gettimeofday(&iclock_tv); +} + +static void +select_iclock(void) +{ + struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL; + int pri = -128; + + list_for_each_entry(iclock, &iclock_list, list) { + if (iclock->pri > pri) { + pri = iclock->pri; + bestclock = iclock; + } + if (iclock_current == iclock) + lastclock = iclock; + } + if (lastclock && bestclock != lastclock) { + /* last used clock source still exists but changes, disable */ + if (*debug & DEBUG_CLOCK) + printk(KERN_DEBUG "Old clock source '%s' disable.\n", + lastclock->name); + lastclock->ctl(lastclock->priv, 0); + } + if (bestclock && bestclock != iclock_current) { + /* new clock source selected, enable */ + if (*debug & DEBUG_CLOCK) + printk(KERN_DEBUG "New clock source '%s' enable.\n", + bestclock->name); + bestclock->ctl(bestclock->priv, 1); + } + if (bestclock != iclock_current) { + /* no clock received yet */ + iclock_tv_valid = 0; + } + iclock_current = bestclock; +} + +struct mISDNclock +*mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv) +{ + u_long flags; + struct mISDNclock *iclock; + + if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) + printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri); + iclock = kzalloc(sizeof(struct mISDNclock), GFP_ATOMIC); + if (!iclock) { + printk(KERN_ERR "%s: No memory for clock entry.\n", __func__); + return NULL; + } + strncpy(iclock->name, name, sizeof(iclock->name)-1); + iclock->pri = pri; + iclock->priv = priv; + iclock->ctl = ctl; + write_lock_irqsave(&iclock_lock, flags); + list_add_tail(&iclock->list, &iclock_list); + select_iclock(); + write_unlock_irqrestore(&iclock_lock, flags); + return iclock; +} +EXPORT_SYMBOL(mISDN_register_clock); + +void +mISDN_unregister_clock(struct mISDNclock *iclock) +{ + u_long flags; + + if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) + printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name, + iclock->pri); + write_lock_irqsave(&iclock_lock, flags); + if (iclock_current == iclock) { + if (*debug & DEBUG_CLOCK) + printk(KERN_DEBUG + "Current clock source '%s' unregisters.\n", + iclock->name); + iclock->ctl(iclock->priv, 0); + } + list_del(&iclock->list); + select_iclock(); + write_unlock_irqrestore(&iclock_lock, flags); +} +EXPORT_SYMBOL(mISDN_unregister_clock); + +void +mISDN_clock_update(struct mISDNclock *iclock, int samples, struct timeval *tv) +{ + u_long flags; + struct timeval tv_now; + time_t elapsed_sec; + int elapsed_8000th; + + write_lock_irqsave(&iclock_lock, flags); + if (iclock_current != iclock) { + printk(KERN_ERR "%s: '%s' sends us clock updates, but we do " + "listen to '%s'. This is a bug!\n", __func__, + iclock->name, + iclock_current ? iclock_current->name : "nothing"); + iclock->ctl(iclock->priv, 0); + write_unlock_irqrestore(&iclock_lock, flags); + return; + } + if (iclock_tv_valid) { + /* increment sample counter by given samples */ + iclock_count += samples; + if (tv) { /* tv must be set, if function call is delayed */ + iclock_tv.tv_sec = tv->tv_sec; + iclock_tv.tv_usec = tv->tv_usec; + } else + do_gettimeofday(&iclock_tv); + } else { + /* calc elapsed time by system clock */ + if (tv) { /* tv must be set, if function call is delayed */ + tv_now.tv_sec = tv->tv_sec; + tv_now.tv_usec = tv->tv_usec; + } else + do_gettimeofday(&tv_now); + elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec; + elapsed_8000th = (tv_now.tv_usec / 125) + - (iclock_tv.tv_usec / 125); + if (elapsed_8000th < 0) { + elapsed_sec -= 1; + elapsed_8000th += 8000; + } + /* add elapsed time to counter and set new timestamp */ + iclock_count += elapsed_sec * 8000 + elapsed_8000th; + iclock_tv.tv_sec = tv_now.tv_sec; + iclock_tv.tv_usec = tv_now.tv_usec; + iclock_tv_valid = 1; + if (*debug & DEBUG_CLOCK) + printk("Received first clock from source '%s'.\n", + iclock_current ? iclock_current->name : "nothing"); + } + write_unlock_irqrestore(&iclock_lock, flags); +} +EXPORT_SYMBOL(mISDN_clock_update); + +unsigned short +mISDN_clock_get(void) +{ + u_long flags; + struct timeval tv_now; + time_t elapsed_sec; + int elapsed_8000th; + u16 count; + + read_lock_irqsave(&iclock_lock, flags); + /* calc elapsed time by system clock */ + do_gettimeofday(&tv_now); + elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec; + elapsed_8000th = (tv_now.tv_usec / 125) - (iclock_tv.tv_usec / 125); + if (elapsed_8000th < 0) { + elapsed_sec -= 1; + elapsed_8000th += 8000; + } + /* add elapsed time to counter */ + count = iclock_count + elapsed_sec * 8000 + elapsed_8000th; + read_unlock_irqrestore(&iclock_lock, flags); + return count; +} +EXPORT_SYMBOL(mISDN_clock_get); + diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c index 43fd97b0fe42..9116f54def2c 100644 --- a/drivers/isdn/mISDN/core.c +++ b/drivers/isdn/mISDN/core.c @@ -199,6 +199,7 @@ mISDNInit(void) printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n", MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); + mISDN_init_clock(&debug); mISDN_initstack(&debug); err = mISDN_inittimer(&debug); if (err) diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h index 7da7233b4c1a..7ac2f81a812b 100644 --- a/drivers/isdn/mISDN/core.h +++ b/drivers/isdn/mISDN/core.h @@ -74,4 +74,6 @@ extern void l1_cleanup(void); extern int Isdnl2_Init(u_int *); extern void Isdnl2_cleanup(void); +extern void mISDN_init_clock(u_int *); + #endif diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c index 04dbb407f7a0..efe4c7430e6d 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -1557,13 +1557,11 @@ send_packet: schedule_work(&dsp->workq); } -static u32 samplecount; +static u32 jittercount; /* counter for jitter check */; struct timer_list dsp_spl_tl; u32 dsp_spl_jiffies; /* calculate the next time to fire */ -#ifdef UNUSED -static u32 dsp_start_jiffies; /* jiffies at the time, the calculation begins */ -#endif /* UNUSED */ -static struct timeval dsp_start_tv; /* time at start of calculation */ +static u16 dsp_count; /* last sample count */ +static int dsp_count_valid ; /* if we have last sample count */ void dsp_cmx_send(void *arg) @@ -1577,38 +1575,32 @@ dsp_cmx_send(void *arg) int r, rr; int jittercheck = 0, delay, i; u_long flags; - struct timeval tv; - u32 elapsed; - s16 length; + u16 length, count; /* lock */ spin_lock_irqsave(&dsp_lock, flags); - if (!dsp_start_tv.tv_sec) { - do_gettimeofday(&dsp_start_tv); + if (!dsp_count_valid) { + dsp_count = mISDN_clock_get(); length = dsp_poll; + dsp_count_valid = 1; } else { - do_gettimeofday(&tv); - elapsed = ((tv.tv_sec - dsp_start_tv.tv_sec) * 8000) - + ((s32)(tv.tv_usec / 125) - (dsp_start_tv.tv_usec / 125)); - dsp_start_tv.tv_sec = tv.tv_sec; - dsp_start_tv.tv_usec = tv.tv_usec; - length = elapsed; + count = mISDN_clock_get(); + length = count - dsp_count; + dsp_count = count; } if (length > MAX_POLL + 100) length = MAX_POLL + 100; -/* printk(KERN_DEBUG "len=%d dsp_count=0x%x.%04x dsp_poll_diff=0x%x.%04x\n", - length, dsp_count >> 16, dsp_count & 0xffff, dsp_poll_diff >> 16, - dsp_poll_diff & 0xffff); - */ + /* printk(KERN_DEBUG "len=%d dsp_count=0x%x\n", length, dsp_count); */ /* - * check if jitter needs to be checked - * (this is about every second = 8192 samples) + * check if jitter needs to be checked (this is every second) */ - samplecount += length; - if ((samplecount & 8191) < length) + jittercount += length; + if (jittercount >= 8000) { + jittercount -= 8000; jittercheck = 1; + } /* loop all members that do not require conference mixing */ list_for_each_entry(dsp, &dsp_ilist, list) { diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index f75d596c5316..364f1018f0d1 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -371,6 +371,7 @@ struct mISDN_ctrl_req { #define DEBUG_L2_TEI 0x00100000 #define DEBUG_L2_TEIFSM 0x00200000 #define DEBUG_TIMER 0x01000000 +#define DEBUG_CLOCK 0x02000000 #define mISDN_HEAD_P(s) ((struct mISDNhead *)&s->cb[0]) #define mISDN_HEAD_PRIM(s) (((struct mISDNhead *)&s->cb[0])->prim) @@ -384,6 +385,7 @@ struct mISDN_ctrl_req { struct mISDNchannel; struct mISDNdevice; struct mISDNstack; +struct mISDNclock; struct channel_req { u_int protocol; @@ -460,6 +462,16 @@ struct mISDNstack { #endif }; +typedef int (clockctl_func_t)(void *, int); + +struct mISDNclock { + struct list_head list; + char name[64]; + int pri; + clockctl_func_t *ctl; + void *priv; +}; + /* global alloc/queue functions */ static inline struct sk_buff * @@ -510,8 +522,13 @@ extern int mISDN_register_device(struct mISDNdevice *, char *name); extern void mISDN_unregister_device(struct mISDNdevice *); extern int mISDN_register_Bprotocol(struct Bprotocol *); extern void mISDN_unregister_Bprotocol(struct Bprotocol *); +extern struct mISDNclock *mISDN_register_clock(char *, int, clockctl_func_t *, + void *); +extern void mISDN_unregister_clock(struct mISDNclock *); extern void set_channel_address(struct mISDNchannel *, u_int, u_int); +extern void mISDN_clock_update(struct mISDNclock *, int, struct timeval *); +extern unsigned short mISDN_clock_get(void); #endif /* __KERNEL__ */ #endif /* mISDNIF_H */ |