diff options
-rw-r--r-- | pciradio.c | 184 |
1 files changed, 144 insertions, 40 deletions
@@ -2,7 +2,7 @@ * PCI RADIO Card Zapata Telephony PCI Quad Radio Interface driver * * Written by Jim Dixon <jim@lambdatel.com> - * Based on previous work by Mark Spencer <markster@digium.com> + * Based on previous work by Mark Spencer <markster@linux-support.net> * Based on previous works, designs, and archetectures conceived and * written by Jim Dixon <jim@lambdatel.com>. * @@ -56,11 +56,7 @@ With driver: 303826 (1.5 %) #ifdef STANDALONE_ZAPATA #include "zaptel.h" #else -#include <zaptel/zaptel.h> -#endif - -#ifdef LINUX26 -#include <linux/moduleparam.h> +#include <linux/zaptel.h> #endif #define RAD_MAX_IFACES 128 @@ -105,6 +101,11 @@ With driver: 303826 (1.5 %) #define NUM_CHANS 4 #define RAD_GOTRX_DEBOUNCE_TIME 75 +#define RAD_CTCSS_ACQUIRE_TIME 10 +#define RAD_CTCSS_TALKOFF_TIME 1000 + +#define ZT_RADPAR_CTCSSACQUIRETIME 18 /* DEBUG only, this belongs in zaptel.h */ +#define ZT_RADPAR_CTCSSTALKOFFTIME 19 /* DEBUG only, this belongs in zaptel.h */ /* * MX828 Commands @@ -157,7 +158,7 @@ struct pciradio { int freeregion; int nchans; spinlock_t lock; - spinlock_t remotelock; + int remote_locked; unsigned char rxbuf[SERIAL_BUFLEN]; unsigned short rxindex; unsigned long srxtimer; @@ -169,17 +170,22 @@ struct pciradio { volatile unsigned long ioaddr; dma_addr_t readdma; dma_addr_t writedma; - volatile unsigned int *writechunk; /* Double-word aligned write memory */ - volatile unsigned int *readchunk; /* Double-word aligned read memory */ + volatile int *writechunk; /* Double-word aligned write memory */ + volatile int *readchunk; /* Double-word aligned read memory */ unsigned char saudio_status[NUM_CHANS]; char gotcor[NUM_CHANS]; char gotct[NUM_CHANS]; + char newctcssstate[NUM_CHANS]; + char ctcssstate[NUM_CHANS]; char gotrx[NUM_CHANS]; char gotrx1[NUM_CHANS]; char gottx[NUM_CHANS]; char lasttx[NUM_CHANS]; int gotrxtimer[NUM_CHANS]; + int ctcsstimer[NUM_CHANS]; int debouncetime[NUM_CHANS]; + int ctcssacquiretime[NUM_CHANS]; + int ctcsstalkofftime[NUM_CHANS]; int bursttime[NUM_CHANS]; int bursttimer[NUM_CHANS]; unsigned char remmode[NUM_CHANS]; @@ -474,19 +480,29 @@ unsigned char __pciradio_getcreg(struct pciradio *rad, unsigned char reg) void rbi_out(struct pciradio *rad, int n, unsigned char *rbicmd) { +unsigned long flags; int x; DECLARE_WAIT_QUEUE_HEAD(mywait); - spin_lock(&rad->remotelock); - while(__pciradio_getcreg(rad,0xc) & 2) interruptible_sleep_on_timeout(&mywait,2); + for(;;) + { + spin_lock_irqsave(&rad->lock,flags); + x = rad->remote_locked || (__pciradio_getcreg(rad,0xc) & 2); + if (!x) rad->remote_locked = 1; + spin_unlock_irqrestore(&rad->lock,flags); + if (x) interruptible_sleep_on_timeout(&mywait,2); + else break; + } + spin_lock_irqsave(&rad->lock,flags); /* enable and address RBI serializer */ __pciradio_setcreg(rad,0xf,rad->pfsave | (n << 4) | 0x40); /* output commands */ for(x = 0; x < 5; x++) __pciradio_setcreg(rad,0xc,rbicmd[x]); /* output it */ __pciradio_setcreg(rad,0xb,1); - spin_unlock(&rad->remotelock); + rad->remote_locked = 0; + spin_unlock_irqrestore(&rad->lock,flags); return; } @@ -539,7 +555,7 @@ unsigned long flags; static void _do_encdec(struct pciradio *rad) { int i,n; -unsigned char byte1 = 0, byte2 = 0; +unsigned char byte1,byte2; /* return doing nothing if busy */ if ((rad->encdec.lastcmd + 2) > jiffies) return; @@ -713,7 +729,11 @@ static void pciradio_stop_dma(struct pciradio *rad); static void pciradio_reset_serial(struct pciradio *rad); static void pciradio_restart_dma(struct pciradio *rad); -ZAP_IRQ_HANDLER(pciradio_interrupt) +#ifdef LINUX26 +static irqreturn_t pciradio_interrupt(int irq, void *dev_id, struct pt_regs *regs) +#else +static void pciradio_interrupt(int irq, void *dev_id, struct pt_regs *regs) +#endif { struct pciradio *rad = dev_id; unsigned char ints,byte1,byte2,gotcor,gotctcss,gotslowctcss,ctcss; @@ -810,13 +830,27 @@ ZAP_IRQ_HANDLER(pciradio_interrupt) gotctcss = gotslowctcss = ((byteuio & mask) != 0); } rad->gotct[x] = gotslowctcss; - if ((rad->radmode[x] & RADMODE_IGNORECT) || (!ctcss)) + if ((rad->radmode[x] & RADMODE_IGNORECT) || + ((!(rad->radmode[x] & RADMODE_EXTTONE)) && (!ctcss))) { gotctcss = 1; gotslowctcss = 1; rad->present_code[x] = 0; } - gotrx = gotcor && gotctcss; + if(rad->newctcssstate[x] != gotctcss){ + rad->newctcssstate[x] = gotctcss; + if(rad->newctcssstate[x]) + rad->ctcsstimer[x]=rad->ctcssacquiretime[x]; + else + rad->ctcsstimer[x]=rad->ctcsstalkofftime[x]; + } + else{ + if(!rad->ctcsstimer[x]) + rad->ctcssstate[x] = rad->newctcssstate[x]; + else + rad->ctcsstimer[x]--; + } + gotrx = gotcor && rad->ctcssstate[x]; if (gotrx != rad->gotrx[x]) { rad->gotrxtimer[x] = rad->debouncetime[x]; @@ -902,7 +936,7 @@ ZAP_IRQ_HANDLER(pciradio_interrupt) } } } -/* process serial if any */ + /* process serial if any */ /* send byte if there is one in buffer to send */ if (rad->txlen && (rad->txlen != rad->txindex)) { @@ -926,6 +960,14 @@ ZAP_IRQ_HANDLER(pciradio_interrupt) } pciradio_receiveprep(rad, ints); pciradio_transmitprep(rad, ints); + i = 0; + for(x = 0; x < 4; x++) + { + if (rad->gottx[x]) i |= (1 << (x * 2)); + if (rad->gotrx[x]) i |= (2 << (x * 2)); + } + /* output LED's */ + __pciradio_setcreg(rad, 9, i); } #ifdef LINUX26 return IRQ_RETVAL(1); @@ -1012,6 +1054,15 @@ static int pciradio_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long case ZT_RADPAR_DEBOUNCETIME: stack.p.data = rad->debouncetime[chan->chanpos - 1]; break; + + case ZT_RADPAR_CTCSSACQUIRETIME: + stack.p.data = rad->ctcssacquiretime[chan->chanpos - 1]; + break; + + case ZT_RADPAR_CTCSSTALKOFFTIME: + stack.p.data = rad->ctcsstalkofftime[chan->chanpos - 1]; + break; + case ZT_RADPAR_BURSTTIME: stack.p.data = rad->bursttime[chan->chanpos - 1]; break; @@ -1168,6 +1219,15 @@ static int pciradio_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long case ZT_RADPAR_DEBOUNCETIME: rad->debouncetime[chan->chanpos - 1] = stack.p.data; break; + + case ZT_RADPAR_CTCSSACQUIRETIME: + rad->ctcssacquiretime[chan->chanpos - 1] = stack.p.data; + break; + + case ZT_RADPAR_CTCSSTALKOFFTIME: + rad->ctcsstalkofftime[chan->chanpos - 1] = stack.p.data; + break; + case ZT_RADPAR_BURSTTIME: rad->bursttime[chan->chanpos - 1] = stack.p.data; break; @@ -1178,7 +1238,6 @@ static int pciradio_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long if (stack.p.data & 1) byte1 |= (1 << (chan->chanpos - 1)); if (stack.p.data & 2) byte1 |= (1 << (chan->chanpos + 3)); __pciradio_setcreg(rad,8,byte1); - spin_unlock_irqrestore(&rad->lock,flags); break; case ZT_RADPAR_UIOMODE: byte1 = __pciradio_getcreg(rad,0xe); @@ -1187,7 +1246,6 @@ static int pciradio_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long if (stack.p.data & 1) byte1 |= (1 << (chan->chanpos - 1)); if (stack.p.data & 2) byte1 |= (1 << (chan->chanpos + 3)); __pciradio_setcreg(rad,0xe,byte1); - spin_unlock_irqrestore(&rad->lock,flags); break; case ZT_RADPAR_REMMODE: rad->remmode[chan->chanpos - 1] = stack.p.data; @@ -1222,7 +1280,20 @@ static int pciradio_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long spin_lock_irqsave(&rad->lock,flags); break; } - /* set UIOA and UIOB for output */ + spin_unlock_irqrestore(&rad->lock,flags); + for(;;) + { + int x; + + spin_lock_irqsave(&rad->lock,flags); + x = rad->remote_locked || (__pciradio_getcreg(rad,0xc) & 2); + if (!x) rad->remote_locked = 1; + spin_unlock_irqrestore(&rad->lock,flags); + if (x) interruptible_sleep_on_timeout(&mywait,2); + else break; + } + spin_lock_irqsave(&rad->lock,flags); + /* set UIOA for input and UIOB for output */ byte1 = __pciradio_getcreg(rad,0xe); mask = 1 << (chan->chanpos + 3); /* B an output */ byte2 = byte1 & (~mask); @@ -1231,16 +1302,19 @@ static int pciradio_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long byte1 = __pciradio_getcreg(rad,8); byte2 = byte1 | mask; byte2 |= 1 << (chan->chanpos - 1); + byte2 |= 1 << (chan->chanpos + 3); __pciradio_setcreg(rad,8,byte2); spin_unlock_irqrestore(&rad->lock,flags); - if (byte1 != byte2) - interruptible_sleep_on_timeout(&mywait,100); + if (byte1 != byte2) + interruptible_sleep_on_timeout(&mywait,3); while (jiffies < rad->lastremcmd + 10) interruptible_sleep_on_timeout(&mywait,10); rad->lastremcmd = jiffies; - spin_lock(&rad->remotelock); - while(__pciradio_getcreg(rad,0xc) & 2) interruptible_sleep_on_timeout(&mywait,2); - spin_unlock(&rad->remotelock); + for(;;) + { + if (!(__pciradio_getcreg(rad,0xc) & 2)) break; + interruptible_sleep_on_timeout(&mywait,2); + } spin_lock_irqsave(&rad->lock,flags); /* enable and address async serializer */ __pciradio_setcreg(rad,0xf,rad->pfsave | ((chan->chanpos - 1) << 4) | 0x80); @@ -1258,7 +1332,7 @@ static int pciradio_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long if ((rad->rxindex < stack.p.data) && (rad->srxtimer < SRX_TIMEOUT) && ((rad->remmode[chan->chanpos - 1] == ZT_RADPAR_REM_SERIAL) || - (!strchr((char *) rad->rxbuf,'\r')))) + (!strchr(rad->rxbuf,'\r')))) { spin_unlock_irqrestore(&rad->lock,flags); interruptible_sleep_on_timeout(&mywait,2); @@ -1273,8 +1347,24 @@ static int pciradio_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long stack.p.index = rad->rxindex; break; } - if (copy_to_user((struct zt_radio_stat *)data,&stack.p,sizeof(struct zt_radio_param))) return -EFAULT; + /* wait for done only if in SERIAL_ASCII mode */ + if (rad->remmode[chan->chanpos - 1] == ZT_RADPAR_REM_SERIAL_ASCII) + { + /* wait for TX to be done if not already */ + while(rad->txlen && (rad->txindex < rad->txlen)) + { + spin_unlock_irqrestore(&rad->lock,flags); + interruptible_sleep_on_timeout(&mywait,2); + spin_lock_irqsave(&rad->lock,flags); + } + /* disable and un-address async serializer */ + __pciradio_setcreg(rad,0xf,rad->pfsave); + } + rad->remote_locked = 0; spin_unlock_irqrestore(&rad->lock,flags); + if (rad->remmode[chan->chanpos - 1] == ZT_RADPAR_REM_SERIAL_ASCII) + interruptible_sleep_on_timeout(&mywait,100); + if (copy_to_user((struct zt_radio_stat *)data,&stack.p,sizeof(struct zt_radio_param))) return -EFAULT; return 0; default: spin_unlock_irqrestore(&rad->lock,flags); @@ -1388,7 +1478,7 @@ static int pciradio_hooksig(struct zt_chan *chan, zt_txsig_t txsig) printk("pciradio: Can't set tx state to %d\n", txsig); break; } - if (debug) + if (debug) printk("pciradio: Setting Radio hook state to %d on chan %d\n", txsig, chan->chanpos); return 0; } @@ -1407,6 +1497,8 @@ static int pciradio_initialize(struct pciradio *rad) rad->chans[x].chanpos = x+1; rad->chans[x].pvt = rad; rad->debouncetime[x] = RAD_GOTRX_DEBOUNCE_TIME; + rad->ctcssacquiretime[x] = RAD_CTCSS_ACQUIRE_TIME; + rad->ctcsstalkofftime[x] = RAD_CTCSS_TALKOFF_TIME; } rad->span.chans = rad->chans; rad->span.channels = rad->nchans; @@ -1525,14 +1617,6 @@ unsigned long endjif; wait_just_a_bit(HZ/4); - rad->pasave = 0; - __pciradio_setcreg(rad,0xa,rad->pasave); - - __pciradio_setcreg(rad,8,0); - __pciradio_setcreg(rad,9,0x55); - __pciradio_setcreg(rad,0xe,0); - rad->pfsave = 0; - __pciradio_setcreg(rad,0xf,rad->pfsave); /* Back to normal, with automatic DMA wrap around */ outb(0x30 | 0x01, rad->ioaddr + RAD_CNTL); @@ -1540,6 +1624,15 @@ unsigned long endjif; /* Configure serial port for MSB->LSB operation */ outb(0xc1, rad->ioaddr + RAD_SERCTL); /* DEBUG set double dlck to 0 SR */ + rad->pasave = 0; + __pciradio_setcreg(rad,0xa,rad->pasave); + + __pciradio_setcreg(rad,0xf,rad->pfsave); + __pciradio_setcreg(rad,8,0xff); + __pciradio_setcreg(rad,0xe,0xff); + __pciradio_setcreg(rad,9,0); + rad->pfsave = 0; + /* Delay FSC by 0 so it's properly aligned */ outb(/* 1 */ 0, rad->ioaddr + RAD_FSCDELAY); @@ -1648,7 +1741,6 @@ static int __devinit pciradio_init_one(struct pci_dev *pdev, const struct pci_de ifaces[x] = rad; memset(rad, 0, sizeof(struct pciradio)); spin_lock_init(&rad->lock); - spin_lock_init(&rad->remotelock); rad->nchans = 4; rad->ioaddr = pci_resource_start(pdev, 0); rad->dev = pdev; @@ -1660,7 +1752,7 @@ static int __devinit pciradio_init_one(struct pci_dev *pdev, const struct pci_de /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses 32 bits. Allocate an extra set just for control too */ - rad->writechunk = pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, &rad->writedma); + rad->writechunk = (int *)pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, &rad->writedma); if (!rad->writechunk) { printk("pciradio: Unable to allocate DMA-able memory\n"); if (rad->freeregion) @@ -1676,6 +1768,9 @@ static int __devinit pciradio_init_one(struct pci_dev *pdev, const struct pci_de /* Set Reset Low */ x=inb(rad->ioaddr + RAD_CNTL); outb((~0x1)&x, rad->ioaddr + RAD_CNTL); + outb(x, rad->ioaddr + RAD_CNTL); + __pciradio_setcreg(rad,8,0xff); + __pciradio_setcreg(rad,0xe,0xff); /* Free Resources */ free_irq(pdev->irq, rad); if (rad->freeregion) @@ -1697,6 +1792,9 @@ static int __devinit pciradio_init_one(struct pci_dev *pdev, const struct pci_de /* Set Reset Low */ x=inb(rad->ioaddr + RAD_CNTL); outb((~0x1)&x, rad->ioaddr + RAD_CNTL); + outb(x, rad->ioaddr + RAD_CNTL); + __pciradio_setcreg(rad,8,0xff); + __pciradio_setcreg(rad,0xe,0xff); /* Free Resources */ free_irq(pdev->irq, rad); if (rad->freeregion) @@ -1709,7 +1807,7 @@ static int __devinit pciradio_init_one(struct pci_dev *pdev, const struct pci_de } - if (request_irq(pdev->irq, pciradio_interrupt, ZAP_IRQ_SHARED, "pciradio", rad)) { + if (request_irq(pdev->irq, pciradio_interrupt, SA_SHIRQ, "pciradio", rad)) { printk("pciradio: Unable to request IRQ %d\n", pdev->irq); if (rad->freeregion) release_region(rad->ioaddr, 0xff); @@ -1760,7 +1858,13 @@ static void __devexit pciradio_remove_one(struct pci_dev *pdev) free_irq(pdev->irq, rad); /* Reset PCI chip and registers */ - outb(0x3e, rad->ioaddr + RAD_CNTL); + outb(0x3e, rad->ioaddr + RAD_CNTL); + + /* Clear Reset Line */ + outb(0x3f, rad->ioaddr + RAD_CNTL); + + __pciradio_setcreg(rad,8,0xff); + __pciradio_setcreg(rad,0xe,0xff); /* Release span, possibly delayed */ if (!rad->usecount) |