#undef PT_DEBUG 1
/*
* pt.c: Linux device driver for the Gracilis PackeTwin.
* Copyright (c) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.)
*
* 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.
*
* 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.
*
* This driver is largely based upon the PI driver by David Perry.
*
* Revision History
* 23/02/95 cs Started again on driver, last one scrapped
* 27/02/95 cs Program works, we have chan A only. Tx stays on
* 28/02/95 cs Fix Tx problem (& TxUIE instead of | )
* Fix Chan B Tx timer problem, used TMR2 instead of TMR1
* 03/03/95 cs Painfully found out (after 3 days) SERIAL_CFG is write only
* created image of it and DMA_CFG
* 21/06/95 cs Upgraded to suit PI driver 0.8 ALPHA
* 22/08/95 cs Changed it all around to make it like pi driver
* 23/08/95 cs It now works, got caught again by TMR2 and we must have
* auto-enables for daughter boards.
* 07/10/95 cs Fixed for 1.3.30 (hopefully)
* 26/11/95 cs Fixed for 1.3.43, ala 29/10 for pi2.c by ac
* 21/12/95 cs Got rid of those nasty warnings when compiling, for 1.3.48
*/
/*
* default configuration of the PackeTwin,
* ie What Craig uses his PT for.
*/
#define PT_DMA 3
#define DEF_A_SPEED 4800 /* 4800 baud */
#define DEF_A_TXDELAY 350 /* 350 mS */
#define DEF_A_PERSIST 64 /* 25% persistence */
#define DEF_A_SLOTIME 10 /* 10 mS */
#define DEF_A_SQUELDELAY 30 /* 30 mS */
#define DEF_A_CLOCKMODE 0 /* Normal clock mode */
#define DEF_A_NRZI 1 /* NRZI mode */
#define DEF_B_SPEED 0 /* 0 means external clock */
#define DEF_B_TXDELAY 250 /* 250 mS */
#define DEF_B_PERSIST 64 /* 25% */
#define DEF_B_SLOTIME 10 /* 10 mS */
#define DEF_B_SQUELDELAY 30 /* 30 mS */
#define DEF_B_CLOCKMODE 0 /* Normal clock mode ?!? */
#define DEF_B_NRZI 1 /* NRZI mode */
#define PARAM_TXDELAY 1
#define PARAM_PERSIST 2
#define PARAM_SLOTTIME 3
#define PARAM_FULLDUP 5
#define PARAM_HARDWARE 6
#define PARAM_RETURN 255
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/segment.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/timer.h>
#include <linux/if_arp.h>
#include "pt.h"
#include "z8530.h"
#include <net/ax25.h>
static char *version =
"PT: 0.41 ALPHA 07 October 1995 Craig Small (vk2xlz@vk2xlz.ampr.org)\n";
struct mbuf {
struct mbuf *next;
int cnt;
char data[0];
};
/*
* The actual PT devices we will use
*/
static int pt0_preprobe(struct device *dev) {return 0;} /* Dummy probe function */
static struct device pt0a = { "pt0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe };
static struct device pt0b = { "pt0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe };
/* Ok, they shouldn't be here, but both channels share them */
/* The Images of the Serial and DMA config registers */
static unsigned char pt_sercfg = 0;
static unsigned char pt_dmacfg = 0;
/* The number of IO ports used by the card */
#define PT_TOTAL_SIZE 16
/* Index to functions, as function prototypes. */
static int pt_probe(struct device *dev);
static int pt_open(struct device *dev);
static int pt_send_packet(struct sk_buff *skb, struct device *dev);
static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static int pt_close(struct device *dev);
static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
static struct enet_statistics *pt_get_stats(struct device *dev);
static void pt_rts(struct pt_local *lp, int x);
static void pt_rxisr(struct device *dev);
static void pt_txisr(struct pt_local *lp);
static void pt_exisr(struct pt_local *lp);
static void pt_tmrisr(struct pt_local *lp);
static char *get_dma_buffer(unsigned long *mem_ptr);
static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize);
static int hw_probe(int ioaddr);
static void tdelay(struct pt_local *lp, int time);
static void empty_scc(struct pt_local *lp);
static void chipset_init(struct device *dev);
static void send_kiss(struct device *dev, unsigned char arg, unsigned char val);
static char ax25_bcast[7] =
{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
static char ax25_test[7] =
{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
static int ext2_secrm_seed = 152;
static inline unsigned char random(void)
{
return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed * 60691 + 1);
}
static inline void wrtscc(int cbase, int ctl, int sccreg, unsigned char val)
{
outb_p(sccreg, ctl); /* Select register */
outb_p(val, ctl); /* Output value */
}
static inline unsigned char rdscc(int cbase, int ctl, int sccreg)
{
unsigned char retval;
outb_p(sccreg, ctl); /* Select register */
retval = inb_p(ctl);
return retval;
}
static void switchbuffers(struct pt_local *lp)
{
if (lp->rcvbuf == lp->rxdmabuf1)
lp->rcvbuf = lp->rxdmabuf2;
else
lp->rcvbuf = lp->rxdmabuf1;
}
static void hardware_send_packet(struct pt_local *lp, struct sk_buff *skb)
{
char kickflag;
unsigned long flags;
char *ptr;
struct device *dev;
/* First, let's see if this packet is actually a KISS packet */
ptr = skb->data;
if (ptr[0] != 0 && skb->len >= 2)
{
printk("Rx KISS... Control = %d, value = %d.\n", ptr[0], (skb->len > 1? ptr[1] : -1));
/* Kludge to get device */
if ((struct pt_local*)(&pt0b.priv) == lp)
dev = &pt0b;
else
dev = &pt0a;
switch(ptr[0])
{
case PARAM_TXDELAY:
/*TxDelay is in 10mS increments */
lp->txdelay = ptr[1] * 10;
send_kiss(dev, PARAM_TXDELAY, (u_char)(lp->txdelay/10));
break;
case PARAM_PERSIST:
lp->persist = ptr[1];
send_kiss(dev, PARAM_PERSIST, (u_char)(lp->persist));
break;
case PARAM_SLOTTIME:
lp->slotime = ptr[1];
send_kiss(dev, PARAM_SLOTTIME, (u_char)(lp->slotime/10));
break;
case PARAM_FULLDUP:
/* Yeah right, you wish! Fullduplex is a little while to
* go folks, but this is how you fire it up
*/
send_kiss(dev, PARAM_FULLDUP, 0);
break;
/* Perhaps we should have txtail here?? */
} /*switch */
return;
}
lp->stats.tx_packets++;
save_flags(flags);
cli();
kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL);
restore_flags(flags);
#ifdef PT_DEBUG
printk("PTd hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA);
#endif
skb_queue_tail(&lp->sndq, skb);
if (kickflag) {
/* Simulate interrupt to transmit */
if (lp->dmachan)
{
pt_txisr(lp);
} else {
save_flags(flags);
cli();
if (lp->tstate == IDLE)
pt_txisr(lp);
restore_flags(flags);
}
}
} /* hardware_send_packet() */
static void setup_rx_dma(struct pt_local *lp)
{
unsigned long flags;
int cmd;
unsigned long dma_abs;
unsigned char dmachan;
save_flags(flags);
cli();
dma_abs = (unsigned long) (lp->rcvbuf->data);
dmachan = lp->dmachan;
cmd = lp->base + CTL;
if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
panic("PI: RX buffer violates DMA boundary!");
/* Get ready for RX DMA */
wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
disable_dma(dmachan);
clear_dma_ff(dmachan);
/* Set DMA mode register to single transfers, incrementing address,
* auto init, writes
*/
set_dma_mode(dmachan, DMA_MODE_READ | 0x10);
set_dma_addr(dmachan, dma_abs);
set_dma_count(dmachan, lp->bufsiz);
enable_dma(dmachan);
/* If a packet is already coming in, this line is supposed to
avoid receiving a partial packet.
*/
wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC);
/* Enable RX dma */
wrtscc(lp->cardbase, cmd, R1,
WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
restore_flags(flags);
}
static void setup_tx_dma(struct pt_local *lp, int length)
{
unsigned long dma_abs;
unsigned long flags;
unsigned long dmachan;
save_flags(flags);
cli();
dmachan = lp->dmachan;
dma_abs = (unsigned long) (lp->txdmabuf);
if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
panic("PT: TX buffer violates DMA boundary!");
disable_dma(dmachan);
/* Set DMA mode register to single transfers, incrementing address,
* no auto init, reads
*/
set_dma_mode(dmachan, DMA_MODE_WRITE);
clear_dma_ff(dmachan);
set_dma_addr(dmachan, dma_abs);
/* output byte count */
set_dma_count(dmachan, length);
restore_flags(flags);
}
static void free_p(struct sk_buff *skb)
{
dev_kfree_skb(skb, FREE_WRITE);
}
static void pt_loopback(struct pt_local *lp, int onoff)
{
if (lp->base & CHANA) {
if (onoff == ON)
outb_p(pt_sercfg |= PT_LOOPA_ON, lp->cardbase + SERIAL_CFG);
else
outb_p(pt_sercfg &= ~PT_LOOPA_ON, lp->cardbase + SERIAL_CFG);
} else { /* it's channel B */
if (onoff == ON)
outb_p(pt_sercfg |= PT_LOOPB_ON, lp->cardbase + SERIAL_CFG);
else
outb_p(pt_sercfg &= ~PT_LOOPB_ON, lp->cardbase + SERIAL_CFG);
}
} /*pt_loopback */
/* Fill in the MAC-level header */
static int pt_header (struct sk_buff *skb, struct device *dev, unsigned short type,
void *daddr, void *saddr, unsigned len)
{
return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
}
/* Rebuild the MAC-level header */
static int pt_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
struct sk_buff *skb)
{
return ax25_rebuild_header(buff, dev, raddr, skb);
}
/*
* This sets up all the registers in the SCC for the given channel
* based upon tsync_hwint()
*/
static void scc_init(struct device *dev)
{
unsigned long flags;
struct pt_local *lp = (struct pt_local*) dev->priv;
register int cmd = lp->base + CTL;
int tc, br;
#ifdef PT_DEBUG
printk("PTd scc_init(): (%d).\n", lp->base & CHANA);
#endif
save_flags(flags);
cli();
/* We may put something here to enable_escc */
if (cmd & CHANA)
{
wrtscc(lp->cardbase, cmd, R9, CHRA); /* Reset channel A */
wrtscc(lp->cardbase, cmd, R2, 0xff); /* Initialise interrupt vector */
} else {
wrtscc(lp->cardbase, cmd, R9, CHRB); /* Reset channel B */
}
/* Deselect all Rx and Tx interrupts */
wrtscc(lp->cardbase, cmd, R1, 0);
/* Turn off external interrupts (like CTS/CD) */
wrtscc(lp->cardbase, cmd, R15, 0);
/* X1 clock, SDLC mode */
wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK);
/* Preset CRC and set mode */
if (lp->nrzi)
{
/* Preset Tx CRC, put into NRZI mode */
wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
} else {
/* Preset Tx CRC, put into NRZ mode */
wrtscc(lp->cardbase, cmd, R10, CRCPS);
}
/* Tx/Rx parameters */
if (lp->speed) /* Use internal clocking */
{
/* Tx Clk from BRG. Rx Clk form DPLL, TRxC pin outputs DPLL */
wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI);
} else { /* Use external clocking */
/* Tx Clk from TRxCL. Rx Clk from RTxCL, TRxC pin if input */
wrtscc(lp->cardbase, cmd, R11, TCTRxCP | RCRTxCP | TRxCBR);
wrtscc(lp->cardbase,cmd, R14, 0); /* wiz1 */
}
/* Null out SDLC start address */
wrtscc(lp->cardbase, cmd, R6, 0);
/* SDLC flag */
wrtscc(lp->cardbase, cmd, R7, FLAG);
/* Setup Tx but don't enable it */
wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
/* Setup Rx */
wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);
/* Setup the BRG, turn it off first */
wrtscc(lp->cardbase, cmd, R14, BRSRC);
/* set the 32x time constant for the BRG in Rx mode */
if (lp->speed)
{
br = lp->speed;
tc = ((lp->xtal / 32) / (br * 2)) - 2;
wrtscc(lp->cardbase, cmd, R12, tc & 0xff); /* lower byte */
wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff); /* upper byte */
}
/* Turn transmitter off, to setup stuff */
pt_rts(lp, OFF);
/* External clocking */
if (lp->speed)
{
/* DPLL frm BRG, BRG src PCLK */
wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR);
wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH); /* SEARCH mode, keep BRG src */
wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL); /* Enable the BRG */
/* Turn off external clock port */
if (lp->base & CHANA)
outb_p( (pt_sercfg &= ~PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) );
else
outb_p( (pt_sercfg &= ~PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) );
} else {
/* DPLL frm rtxc,BRG src PCLK */
/* wrtscc(lp->cardbase, cmd, R14, BRSRC | SSRTxC);*/
/* Turn on external clock port */
if (lp->base & CHANA)
outb_p( (pt_sercfg |= PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) );
else
outb_p( (pt_sercfg |= PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) );
}
if (!lp->dmachan)
wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
wrtscc(lp->cardbase, cmd, R15, BRKIE); /* ABORT int */
/* Turn on the DTR to tell modem we're alive */
if (lp->base & CHANA)
outb_p( (pt_sercfg |= PT_DTRA_ON), (lp->cardbase + SERIAL_CFG) );
else
outb_p( (pt_sercfg |= PT_DTRB_ON), (lp->cardbase + SERIAL_CFG) );
/* Now, turn on the receiver and hunt for a flag */
wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | AUTO_ENAB | Rx8 );
restore_flags(flags);
} /* scc_init() */
/* Resets the given channel and whole SCC if both channels off */
static void chipset_init(struct device *dev)
{
struct pt_local *lp = (struct pt_local*) dev->priv;
#ifdef PT_DEBUG
printk("PTd chipset_init(): pt0a tstate = %d.\n", ((struct pt_local*)pt0a.priv)->tstate);
printk("PTd chipset_init(): pt0b tstate = %d.\n", ((struct pt_local*)pt0b.priv)->tstate);
#endif
/* Reset SCC if both channels are to be canned */
if ( ((lp->base & CHANA) && !(pt_sercfg & PT_DTRB_ON)) ||
(!(lp->base & CHANA) && !(pt_sercfg & PT_DTRA_ON)) )
{
wrtscc(lp->cardbase, lp->base + CTL, R9, FHWRES);
/* Reset int and dma registers */
outb_p((pt_sercfg = 0), lp->cardbase + SERIAL_CFG);
outb_p((pt_dmacfg = 0), lp->cardbase + DMA_CFG);
#ifdef PT_DEBUG
printk("PTd chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA);
#endif
}
/* Reset individual channel */
if (lp->base & CHANA) {
wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRA);
outb_p( (pt_sercfg &= ~PT_DTRA_ON), lp->cardbase + SERIAL_CFG);
} else {
wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRB);
outb_p( (pt_sercfg &= ~PT_DTRB_ON), lp->cardbase + SERIAL_CFG);
}
} /* chipset_init() */
int pt_init(void)
{
int *port;
int ioaddr = 0;
int card_type = 0;
int ports[] =
{ 0x230, 0x240, 0x250, 0x260, 0x270, 0x280, 0x290, 0x2a0,
0x2b0, 0x300, 0x330, 0x3f0, 0};
printk(version);
for (port = &ports[0]; *port && !card_type; port++) {
ioaddr = *port;
if (check_region(ioaddr, PT_TOTAL_SIZE) == 0) {
printk("PT: Probing for card at address %#3x\n", ioaddr);
card_type = hw_probe(ioaddr);
}
}
if (card_type) {
printk("PT: Found a PT at address %#3x\n",ioaddr);
} else {
printk("PT: ERROR: No card found.\n");
return -EIO;
}
/*
* Link a couple of device structures into the chain
*
* For the A port
* Allocate space for 4 buffers even though we only need 3,
* because one of them may cross a DMA page boundary and
* be rejected by get_dma_buffer().
*/
register_netdev(&pt0a);
pt0a.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
pt0a.dma = 0; /* wizzer - no dma yet */
pt0a.base_addr = ioaddr + CHANA;
pt0a.irq = 0;
/* And B port */
register_netdev(&pt0b);
pt0b.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
pt0b.base_addr = ioaddr + CHANB;
pt0b.irq = 0;
/* Now initialise them */
pt_probe(&pt0a);
pt_probe(&pt0b);
pt0b.irq = pt0a.irq; /* IRQ is shared */
return 0;
} /* pt_init() */
/*
* Probe for PT card. Also initialises the timers
*/
static int hw_probe(int ioaddr)
{
int time = 1000; /* Number of milliseconds to test */
int a = 1;
int b = 1;
unsigned long start_time, end_time;
inb_p(ioaddr + TMR1CLR);
inb_p(ioaddr + TMR2CLR);
/* Timer counter channel 0, 1mS period */
outb_p(SC0 | LSB_MSB | MODE3, ioaddr + TMRCMD);
outb_p(0x00, ioaddr + TMR0);
outb_p(0x18, ioaddr + TMR0);
/* Setup timer control word for timer 1 */
outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD);
outb_p((time << 1) & 0xff, ioaddr + TMR1);
outb_p((time >> 7) & 0xff, ioaddr + TMR1);
/* wait until counter reg is loaded */
do {
/* Latch count for reading */
outb_p(SC1, ioaddr + TMRCMD);
a = inb_p(ioaddr + TMR1);
b = inb_p(ioaddr + TMR1);
} while (b == 0);
start_time = jiffies;
while(b != 0)
{
/* Latch count for reading */
outb_p(SC1, ioaddr + TMRCMD);
a = inb_p(ioaddr + TMR1);
b = inb_p(ioaddr + TMR1);
end_time = jiffies;
/* Don't wait forever - there may be no card here */
if ((end_time - start_time) > 200)
{
inb_p(ioaddr + TMR1CLR);
return 0;
}
}
/* Now fix the timers up for general operation */
/* Clear the timers */
inb_p(ioaddr + TMR1CLR);
inb_p(ioaddr + TMR2CLR);
outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD);
inb_p(ioaddr + TMR1CLR);
outb_p(SC2 | LSB_MSB | MODE0, ioaddr + TMRCMD);
/* Should this be tmr1 or tmr2? wiz3*/
inb_p(ioaddr + TMR1CLR);
return 1;
} /* hw_probe() */
static void pt_rts(struct pt_local *lp, int x)
{
int tc;
long br;
int cmd = lp->base + CTL;
#ifdef PT_DEBUG
printk("PTd pt_rts(): Transmitter status will be %d (%d).\n", x, lp->base & CHANA);
#endif
if (x == ON) {
/* Ex ints off to avoid int */
wrtscc(lp->cardbase, cmd, R15, 0);
wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8); /* Rx off */
lp->rstate = IDLE;
if(lp->dmachan)
{
/* Setup for Tx DMA */
wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
} else {
/* No interrupts */
wrtscc(lp->cardbase, cmd, R1, 0);
}
if (!lp->clockmode)
{
if (lp->speed)
{
br = lp->speed;
tc = (lp->xtal / (br * 2)) - 2;
wrtscc(lp->cardbase, cmd, R12, tc & 0xff);
wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);
}
}
/* Turn on Tx by raising RTS */
wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR);
/* Transmitter on now */
} else { /* turning off Tx */
lp->tstate = IDLE;
/* Turn off Tx by dropping RTS */
wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
if (!lp->clockmode)
{
if (lp->speed) /* internally clocked */
{
/* Reprogram BRG from 32x clock for Rx DPLL */
/* BRG off, keep PClk source */
wrtscc(lp->cardbase, cmd, R14, BRSRC);
br = lp->speed;
tc = ((lp->xtal / 32) / (br * 2)) - 2;
wrtscc(lp->cardbase, cmd, R12, tc & 0xff);
wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);
/* SEARCH mode, BRG source */
wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);
/* Enable the BRG */
wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);
}
}
/* Flush Rx fifo */
/* Turn Rx off */
wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);
/* Reset error latch */
wrtscc(lp->cardbase, cmd, R0, ERR_RES);
/* get status byte from R1 */
(void) rdscc(lp->cardbase, cmd, R1);
/* Read and dump data in queue */
(void) rdscc(lp->cardbase, cmd, R8);
(void) rdscc(lp->cardbase, cmd, R8);
(void) rdscc(lp->cardbase, cmd, R8);
/* Now, turn on Rx and hunt for a flag */
wrtscc(lp->cardbase, cmd, R3, RxENABLE | AUTO_ENAB | Rx8 );
lp->rstate = ACTIVE;
if (lp->dmachan)
{
setup_rx_dma(lp);
} else {
/* Reset buffer pointers */
lp->rcp = lp->rcvbuf->data;
lp->rcvbuf->cnt = 0;
/* Allow aborts to interrupt us */
wrtscc(lp->cardbase, cmd, R1, INT_ALL_Rx | EXT_INT_ENAB);
}
wrtscc(lp->cardbase, cmd, R15, BRKIE );
}
} /* pt_rts() */
static int valid_dma_page(unsigned long addr, unsigned long dev_bufsize)
{
if (((addr & 0xffff) + dev_bufsize) <= 0x10000)
return 1;
else
return 0;
}
static int pt_set_mac_address(struct device *dev, struct sockaddr *sa)
{
memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); /* addr is an AX.25 shifted ASCII */
return 0; /* mac address */
}
/* Allocate a buffer which does not cross a DMA page boundary */
static char * get_dma_buffer(unsigned long *mem_ptr)
{
char *ret;
ret = (char *) *mem_ptr;
if (!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))) {
*mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
ret = (char *) *mem_ptr;
}
*mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
return (ret);
} /* get_dma_buffer() */
/*
* Sets up all the structures for the PT device
*/
static int pt_probe(struct device *dev)
{
short ioaddr;
struct pt_local *lp;
int i;
unsigned long flags;
unsigned long mem_ptr;
ioaddr = dev->base_addr;
/*
* Initialise the device structure.
* Must be done before chipset_init()
* Make sure data structures used by the PT are aligned
*/
dev->priv = (void *) (((int) dev->priv + 7) & ~7);
lp = (struct pt_local*) dev->priv;
memset(dev->priv, 0, sizeof(struct pt_local));
/* Allocate some buffers which do not cross DMA boundaries */
mem_ptr = (unsigned long) dev->priv + sizeof(struct pt_local);
lp->txdmabuf = get_dma_buffer(&mem_ptr);
lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr);
lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr);
/* Initialise the Rx buffer */
lp->rcvbuf = lp->rxdmabuf1;
lp->rcp = lp->rcvbuf->data;
lp->rcvbuf->cnt = 0;
/* Initialise the transmit queue head structure */
skb_queue_head_init(&lp->sndq);
lp->base = dev->base_addr;
lp->cardbase = dev->base_addr & 0x3f0;
/* These need to be initialised before scc_init() is called.
*/
lp->xtal = XTAL;
if (dev->base_addr & CHANA) {
lp->speed = DEF_A_SPEED;
lp->txdelay = DEF_A_TXDELAY;
lp->persist = DEF_A_PERSIST;
lp->slotime = DEF_A_SLOTIME;
lp->squeldelay = DEF_A_SQUELDELAY;
lp->clockmode = DEF_A_CLOCKMODE;
lp->nrzi = DEF_A_NRZI;
} else {
lp->speed = DEF_B_SPEED;
lp->txdelay = DEF_B_TXDELAY;
lp->persist = DEF_B_PERSIST;
lp->slotime = DEF_B_SLOTIME;
lp->squeldelay = DEF_B_SQUELDELAY;
lp->clockmode = DEF_B_CLOCKMODE;
lp->nrzi = DEF_B_NRZI;
}
lp->bufsiz = DMA_BUFF_SIZE;
lp->tstate = IDLE;
chipset_init(dev);
if (dev->base_addr & CHANA) {
/* Note that a single IRQ services 2 devices (A and B channels)
*/
/*
* We disable the dma for a while, we have to get ints working
* properly first!!
*/
lp->dmachan = 0;
if (dev->irq < 2) {
autoirq_setup(0);
/* Turn on PT interrupts */
save_flags(flags);
cli();
outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG);
restore_flags(flags);
/* Set a timer interrupt */
tdelay(lp, 1);
dev->irq = autoirq_report(20);
/* Turn off PT interrupts */
save_flags(flags);
cli();
outb_p( (pt_sercfg &= ~ PT_EI), lp->cardbase + INT_CFG);
restore_flags(flags);
if (!dev->irq) {
printk("PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n");
}
}
printk("PT: Autodetected IRQ %d, assuming DMA %d\n", dev->irq, dev->dma);
/* This board has jumpered interrupts. Snarf the interrupt vector
* now. There is no point in waiting since no other device can use
* the interrupt, and this marks the 'irqaction' as busy.
*/
{
int irqval = request_irq(dev->irq, &pt_interrupt,0, "pt", NULL);
if (irqval) {
printk("PT: ERROR: Unable to get IRQ %d (irqval = %d).\n",
dev->irq, irqval);
return EAGAIN;
}
}
/* Grab the region */
request_region(ioaddr & 0x3f0, PT_TOTAL_SIZE, "pt" );
} /* A port */
dev->open = pt_open;
dev->stop = pt_close;
dev->do_ioctl = pt_ioctl;
dev->hard_start_xmit = pt_send_packet;
dev->get_stats = pt_get_stats;
/* Fill in the fields of the device structure */
for (i=0; i < DEV_NUMBUFFS; i++)
skb_queue_head_init(&dev->buffs[i]);
dev->hard_header = pt_header;
dev->rebuild_header = pt_rebuild_header;
dev->set_mac_address = pt_set_mac_address;
dev->type = ARPHRD_AX25; /* AF_AX25 device */
dev->hard_header_len = 73; /* We do digipeaters now */
dev->mtu = 1500; /* eth_mtu is default */
dev->addr_len = 7; /* sizeof an ax.25 address */
memcpy(dev->broadcast, ax25_bcast, 7);
memcpy(dev->dev_addr, ax25_test, 7);
/* New style flags */
dev->flags = 0;
dev->family = AF_INET;
dev->pa_addr = 0;
dev->pa_brdaddr = 0;
dev->pa_mask = 0;
dev->pa_alen = sizeof(unsigned long);
return 0;
} /* pt_probe() */
/* Open/initialise the board. This is called (in the current kernel)
* sometime after booting when the 'ifconfig' program is run.
*
* This routine should set everything up anew at each open, even
* registers that 'should' only be set once at boot, so that there is
* a non-reboot way to recover if something goes wrong.
* derived from last half of tsync_attach()
*/
static int pt_open(struct device *dev)
{
unsigned long flags;
struct pt_local *lp = dev->priv;
static first_time = 1;
if (dev->base_addr & CHANA)
{
if (first_time)
{
if (request_dma(dev->dma, "pt"))
{
free_irq(dev->irq, NULL);
return -EAGAIN;
}
}
irq2dev_map[dev->irq] = dev;
/* Reset hardware */
chipset_init(dev);
}
lp->tstate = IDLE;
if (dev->base_addr & CHANA)
{
scc_init(dev);
scc_init(dev->next);
}
/* Save a copy of register RR0 for comparing with later on */
/* We always put 0 in zero count */
lp->saved_RR0 = rdscc(lp->cardbase, lp->base + CTL, R0) & ~ZCOUNT;
/* master interrupt enable */
save_flags(flags);
cli();
wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | NV);
outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG);
restore_flags(flags);
lp->open_time = jiffies;
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
first_time = 0;
return 0;
} /* pt_open() */
static int pt_send_packet(struct sk_buff *skb, struct device *dev)
{
struct pt_local *lp = (struct pt_local *) dev->priv;
#ifdef PT_DEBUG
printk("PTd pt_send_packet(): (%d)\n", lp->base & CHANA);
#endif
/* If some higher layer thinks we've missed an tx-done interrupt
we are passed NULL. Caution: dev_tint() handles the cli()/sti()
itself.*/
if (skb == NULL) {
dev_tint(dev);
return 0;
}
hardware_send_packet(lp, skb);
dev->trans_start = jiffies;
return 0;
}
/* The inverse routine to pt_open() */
static int pt_close(struct device *dev)
{
unsigned long flags;
struct pt_local *lp = dev->priv;
struct sk_buff *ptr = NULL;
int cmd;
cmd = lp->base + CTL;
save_flags(flags);
cli();
/* Reset SCC or channel */
chipset_init(dev);
disable_dma(lp->dmachan);
lp->open_time = 0;
dev->tbusy = 1;
dev->start = 0;
/* Free any buffers left in the hardware transmit queue */
while ((ptr = skb_dequeue(&lp->sndq)) != NULL)
free_p(ptr);
restore_flags(flags);
#ifdef PT_DEBUG
printk("PTd pt_close(): Closing down channel (%d).\n", lp->base & CHANA);
#endif
return 0;
} /* pt_close() */
static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
{
unsigned long flags;
struct pt_req rq;
struct pt_local *lp = (struct pt_local *) dev->priv;
int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pt_req));
if (ret)
return ret;
if (cmd != SIOCDEVPRIVATE)
return -EINVAL;
memcpy_fromfs(&rq, ifr->ifr_data, sizeof(struct pt_req));
switch (rq.cmd) {
case SIOCSPIPARAM:
if (!suser())
return -EPERM;
save_flags(flags);
cli();
lp->txdelay = rq.txdelay;
lp->persist = rq.persist;
lp->slotime = rq.slotime;
lp->squeldelay = rq.squeldelay;
lp->clockmode = rq.clockmode;
lp->speed = rq.speed;
pt_open(&pt0a);
restore_flags(flags);
ret = 0;
break;
case SIOCSPIDMA:
if (!suser())
return -EPERM;
ret = 0;
if (dev->base_addr & CHANA) { /* if A channel */
if (rq.dmachan < 1 || rq.dmachan > 3)
return -EINVAL;
save_flags(flags);
cli();
pt_close(dev);
free_dma(lp->dmachan);
dev->dma = lp->dmachan = rq.dmachan;
if (request_dma(lp->dmachan,"pt"))
ret = -EAGAIN;
pt_open(dev);
restore_flags(flags);
}
break;
case SIOCSPIIRQ:
ret = -EINVAL; /* add this later */
break;
case SIOCGPIPARAM:
case SIOCGPIDMA:
case SIOCGPIIRQ:
rq.speed = lp->speed;
rq.txdelay = lp->txdelay;
rq.persist = lp->persist;
rq.slotime = lp->slotime;
rq.squeldelay = lp->squeldelay;
rq.clockmode = lp->clockmode;
rq.dmachan = lp->dmachan;
rq.irq = dev->irq;
memcpy_tofs(ifr->ifr_data, &rq, sizeof(struct pt_req));
ret = 0;
break;
default:
ret = -EINVAL;
}
return ret;
}
/* Get the current statistics. This may be called with the card open or
closed. */
static struct netstats *
pt_get_stats(struct device *dev)
{
struct pt_local *lp = (struct pt_local *) dev->priv;
return &lp->stats;
}
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c"
* version-control: t
* kept-new-versions: 5
* tab-width: 4
* End:
*/
static void tdelay(struct pt_local *lp, int time)
{
/* For some reason, we turn off the Tx interrupts here! */
if (!lp->dmachan)
wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB);
if (lp->base & CHANA) {
outb_p(time & 0xff, lp->cardbase + TMR1);
outb_p((time >> 8)&0xff, lp->cardbase + TMR1);
} else {
outb_p(time & 0xff, lp->cardbase + TMR2);
outb_p((time >> 8)&0xff, lp->cardbase + TMR2);
}
} /* tdelay */
static void pt_txisr(struct pt_local *lp)
{
unsigned long flags;
int cmd;
unsigned char c;
save_flags(flags);
cli();
cmd = lp->base + CTL;
#ifdef PT_DEBUG
printk("PTd pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
#endif
switch (lp->tstate)
{
case CRCOUT:
lp->tstate = FLAGOUT;
tdelay(lp, lp->squeldelay);
restore_flags(flags);
return;
case IDLE:
/* Transmitter idle. Find a frame for transmission */
if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL)
{
/* Nothing to send - return to receive mode
* Tx off now - flag should have gone
*/
pt_rts(lp, OFF);
restore_flags(flags);
return;
}
if (!lp->dmachan)
{
lp->txptr = lp->sndbuf->data;
lp->txptr++; /* Ignore KISS control byte */
lp->txcnt = (int) lp->sndbuf->len - 1;
}
/* If a buffer to send, drop though here */
case DEFER:
/* Check DCD - debounce it */
/* See Intel Microcommunications Handbook p2-308 */
wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0)
{
lp->tstate = DEFER;
tdelay(lp, 100);
/* DEFER until DCD transition or timeout */
wrtscc(lp->cardbase, cmd, R15, DCDIE);
restore_flags(flags);
return;
}
if (random() > lp->persist)
{
lp->tstate = DEFER;
tdelay(lp, lp->slotime);
restore_flags(flags);
return;
}
pt_rts(lp, ON); /* Tx on */
if (lp->dmachan)
wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
lp->tstate = ST_TXDELAY;
tdelay(lp, lp->txdelay);
restore_flags(flags);
return;
case ACTIVE:
/* Here we are actively sending a frame */
if (lp->txcnt--)
{
/* XLZ - checkout Gracilis PT code to see if the while
* loop is better or not.
*/
c = *lp->txptr++;
/* next char is gone */
wrtscc(lp->cardbase, cmd, R8, c);
/* stuffing a char satisfies interrupt condition */
} else {
/* No more to send */
free_p(lp->sndbuf);
lp->sndbuf = NULL;
if ((rdscc(lp->cardbase, cmd, R0) & TxEOM))
{
/* Did we underrun */
lp->stats.tx_errors++;
lp->stats.tx_fifo_errors++;
wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
lp->tstate = FLAGOUT;
tdelay(lp, lp->squeldelay);
restore_flags(flags);
return;
}
lp->tstate = UNDERRUN;
/* Send flags on underrun */
if (lp->nrzi)
{
wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
} else {
wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ);
}
/* Reset Tx interrupt pending */
wrtscc(lp->cardbase, cmd, R0, RES_Tx_P);
}
restore_flags(flags);
return;
default:
printk("PT: pt_txisr(): Invalid tstate (%d) for chan %s.\n", lp->tstate, (cmd & CHANA? "A": "B") );
pt_rts(lp, OFF);
lp->tstate = IDLE;
break;
} /*switch */
restore_flags(flags);
}
static void pt_rxisr(struct device *dev)
{
struct pt_local *lp = (struct pt_local*) dev->priv;
int cmd = lp->base + CTL;
int bytecount;
unsigned long flags;
char rse;
struct sk_buff *skb;
int sksize, pkt_len;
struct mbuf *cur_buf;
unsigned char *cfix;
save_flags(flags);
cli();
/* Get status byte from R1 */
rse = rdscc(lp->cardbase, cmd, R1);
#ifdef PT_DEBUG
printk("PTd pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA);
#endif
if (lp->dmachan && (rse & Rx_OVR))
lp->rstate = RXERROR;
if (rdscc(lp->cardbase, cmd, R0) & Rx_CH_AV && !lp->dmachan)
{
/* There is a char to be stored
* Read special condition bits before reading the data char
*/
if (rse & Rx_OVR)
{
/* Rx overrun - toss buffer */
/* wind back the pointers */
lp->rcp = lp->rcvbuf->data;
lp->rcvbuf->cnt = 0;
lp->rstate = RXERROR;
lp->stats.rx_errors++;
lp->stats.rx_fifo_errors++;
} else if (lp->rcvbuf->cnt >= lp->bufsiz)
{
/* Too large packet
* wind back Rx buffer pointers
*/
lp->rcp = lp->rcvbuf->data;
lp->rcvbuf->cnt = 0;
lp->rstate = TOOBIG;
}
/* ok, we can store the Rx char if no errors */
if (lp->rstate == ACTIVE)
{
*lp->rcp++ = rdscc(lp->cardbase, cmd, R8);
lp->rcvbuf->cnt++;
} else {
/* we got an error, dump the FIFO */
(void) rdscc(lp->cardbase, cmd, R8);
(void) rdscc(lp->cardbase, cmd, R8);
(void) rdscc(lp->cardbase, cmd, R8);
/* Reset error latch */
wrtscc(lp->cardbase, cmd, R0, ERR_RES);
lp->rstate = ACTIVE;
/* Resync the SCC */
wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
}
}
if (rse & END_FR)
{
#ifdef PT_DEBUG
printk("PTd pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt);
#endif
if (lp->dmachan)
{
clear_dma_ff(lp->dmachan);
bytecount = lp->bufsiz - get_dma_residue(lp->dmachan);
} else {
bytecount = lp->rcvbuf->cnt;
}
/* END OF FRAME - Make sure Rx was active */
if (lp->rcvbuf->cnt > 0 || lp->dmachan)
{
if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10))
{
if ((bytecount >= 10) && (rse & CRC_ERR))
{
lp->stats.rx_crc_errors++;
}
if (lp->dmachan)
{
if (lp->rstate == RXERROR)
{
lp->stats.rx_errors++;
lp->stats.rx_over_errors++;
}
lp->rstate = ACTIVE;
setup_rx_dma(lp);
} else {
/* wind back Rx buffer pointers */
lp->rcp = lp->rcvbuf->data;
lp->rcvbuf->cnt = 0;
/* Re-sync the SCC */
wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
}
#ifdef PT_DEBUG
printk("PTd pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state");
#endif
} else {
/* We have a valid frame */
if (lp->dmachan)
{
pkt_len = lp->rcvbuf->cnt = bytecount - 2 +1;
/* Get buffer for next frame */
cur_buf = lp->rcvbuf;
switchbuffers(lp);
setup_rx_dma(lp);
} else {
pkt_len = lp->rcvbuf->cnt -= 2; /* Toss 2 CRC bytes */
pkt_len += 1; /* make room for KISS control byte */
}
/* Malloc up new buffer */
sksize = pkt_len;
skb = dev_alloc_skb(sksize);
if (skb == NULL)
{
printk("PT: %s: Memory squeeze, dropping packet.\n", dev->name);
lp->stats.rx_dropped++;
restore_flags(flags);
return;
}
skb->dev = dev;
/* KISS kludge = prefix with a 0 byte */
cfix=skb_put(skb,pkt_len);
*cfix++=0;
/* skb->data points to the start of sk_buff area */
if (lp->dmachan)
memcpy(cfix, (char*)cur_buf->data, pkt_len - 1);
else
memcpy(cfix, lp->rcvbuf->data, pkt_len - 1);
skb->protocol = ntohs(ETH_P_AX25);
skb->mac.raw=skb->data;
IS_SKB(skb);
netif_rx(skb);
lp->stats.rx_packets++;
if (!lp->dmachan)
{
/* packet queued - wind back buffer for next frame */
lp->rcp = lp->rcvbuf->data;
lp->rcvbuf->cnt = 0;
}
} /* good frame */
} /* check active Rx */
/* Clear error status */
lp->rstate = ACTIVE;
/* Reset error latch */
} /* end EOF check */
wrtscc(lp->cardbase, cmd, R0, ERR_RES);
restore_flags(flags);
} /* pt_rxisr() */
/* Read the SCC channel till no more data in receiver */
static void empty_scc(struct pt_local *lp)
{
while( rdscc(lp->cardbase, lp->base + CTL, R0) & Rx_CH_AV) {
/* Get data from Rx buffer and toss it */
(void) inb_p(lp->base + DATA);
}
} /* empty_scc()*/
/*
* This handles the two timer interrupts.
* This is a real bugger, cause you have to rip it out of the pi's
* external status code. They use the CTS line or something.
*/
static void pt_tmrisr(struct pt_local *lp)
{
unsigned long flags;
#ifdef PT_DEBUG
printk("PTd pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
#endif
save_flags(flags);
cli();
switch (lp->tstate)
{
/* Most of this stuff is in pt_exisr() */
case FLAGOUT:
case ST_TXDELAY:
case DEFER:
/* case ACTIVE:
case UNDERRUN:*/
pt_exisr(lp);
break;
default:
if (lp->base & CHANA)
printk("PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate);
else
printk("PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate);
break;
} /* end switch */
restore_flags(flags);
} /* pt_tmrisr() */
/*
* This routine is called by the kernel when there is an interrupt for the
* PT.
*/
static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
/* It's a tad dodgy here, but we assume pt0a until proven otherwise */
struct device *dev = &pt0a;
struct pt_local *lp = dev->priv;
unsigned char intreg;
unsigned char st;
register int cbase = dev->base_addr & 0x3f0;
unsigned long flags;
/* Read the PT's interrupt register, this is not the SCC one! */
intreg = inb_p(cbase + INT_REG);
while(( intreg & 0x07) != 0x07) {
/* Read interrupt register pending from Channel A */
while ((st = rdscc(cbase, cbase + CHANA + CTL, R3)) != 0)
{
/* Read interrupt vector from R2, channel B */
#ifdef PT_DEBUG
printk("PTd pt_interrupt(): R3 = %#3x", st);
#endif
/* st = rdscc(lp->cardbase, cbase + CHANB + CTL, R2) & 0x0e;*/
#ifdef PT_DEBUG
printk(" R2 = %#3x.\n", st);
#endif
if (st & CHARxIP) {
/* Channel A Rx */
lp = (struct pt_local*)pt0a.priv;
pt_rxisr(&pt0a);
} else if (st & CHATxIP) {
/* Channel A Tx */
lp = (struct pt_local*)pt0a.priv;
pt_txisr(lp);
} else if (st & CHAEXT) {
/* Channel A External Status */
lp = (struct pt_local*)pt0a.priv;
pt_exisr(lp);
} else if (st & CHBRxIP) {
/* Channel B Rx */
lp= (struct pt_local*)pt0b.priv;
pt_rxisr(&pt0b);
} else if (st & CHBTxIP) {
/* Channel B Tx */
lp = (struct pt_local*)pt0b.priv;
pt_txisr(lp);
} else if (st & CHBEXT) {
/* Channel B External Status */
lp = (struct pt_local*)pt0b.priv;
pt_exisr(lp);
}
/* Reset highest interrupt under service */
save_flags(flags);
cli();
wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS);
restore_flags(flags);
} /* end of SCC ints */
if (!(intreg & PT_TMR1_MSK))
{
/* Clear timer 1 */
inb_p(cbase + TMR1CLR);
pt_tmrisr( (struct pt_local*)pt0a.priv);
}
if (!(intreg & PT_TMR2_MSK))
{
/* Clear timer 2 */
inb_p(cbase + TMR2CLR);
pt_tmrisr( (struct pt_local*)pt0b.priv);
}
/* Get the next PT interrupt vector */
intreg = inb_p(cbase + INT_REG);
} /* while (intreg) */
} /* pt_interrupt() */
static void pt_exisr(struct pt_local *lp)
{
unsigned long flags;
int cmd = lp->base + CTL;
unsigned char st;
char c;
int length;
save_flags(flags);
cli();
/* Get external status */
st = rdscc(lp->cardbase, cmd, R0);
#ifdef PT_DEBUG
printk("PTd exisr(): R0 = %#3x tstate = %d (%d).\n", st, lp->tstate, lp->base & CHANA);
#endif
/* Reset external status latch */
wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT) && lp->dmachan)
{
setup_rx_dma(lp);
lp->rstate = ACTIVE;
}
switch (lp->tstate)
{
case ACTIVE: /* Unexpected underrun */
#ifdef PT_DEBUG
printk("PTd exisr(): unexpected underrun detected.\n");
#endif
free_p(lp->sndbuf);
lp->sndbuf = NULL;
if (!lp->dmachan)
{
wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
lp->stats.tx_errors++;
lp->stats.tx_fifo_errors++;
}
lp->tstate = FLAGOUT;
tdelay(lp, lp->squeldelay);
restore_flags(flags);
return;
case UNDERRUN:
lp->tstate = CRCOUT;
restore_flags(flags);
return;
case FLAGOUT:
/* squeldelay has timed out */
/* Find a frame for transmission */
if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL)
{
/* Nothing to send - return to Rx mode */
pt_rts(lp, OFF);
lp->tstate = IDLE;
restore_flags(flags);
return;
}
if (!lp->dmachan)
{
lp->txptr = lp->sndbuf->data;
lp->txptr++; /* Ignore KISS control byte */
lp->txcnt = (int) lp->sndbuf->len - 1;
}
/* Fall through if we have a packet */
case ST_TXDELAY:
if (lp->dmachan)
{
/* Disable DMA chan */
disable_dma(lp->dmachan);
/* Set up for TX dma */
wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
length = lp->sndbuf->len - 1;
memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length);
/* Setup DMA controller for Tx */
setup_tx_dma(lp, length);
enable_dma(lp->dmachan);
/* Reset CRC, Txint pending */
wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P);
/* Allow underrun only */
wrtscc(lp->cardbase, cmd, R15, TxUIE);
/* Enable TX DMA */
wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB);
/* Send CRC on underrun */
wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
lp->tstate = ACTIVE;
break;
}
/* Get first char to send */
lp->txcnt--;
c = *lp->txptr++;
/* Reset CRC for next frame */
wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC);
/* send abort on underrun */
if (lp->nrzi)
{
wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
} else {
wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ | ABUNDER);
}
/* send first char */
wrtscc(lp->cardbase, cmd, R8, c);
/* Reset end of message latch */
wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
/* stuff an extra one in */
/* while ((rdscc(lp->cardbase, cmd, R0) & Tx_BUF_EMP) && lp->txcnt)
{
lp->txcnt--;
c = *lp->txptr++;
wrtscc(lp->cardbase, cmd, R8, c);
}*/
/* select Tx interrupts to enable */
/* Allow underrun int only */
wrtscc(lp->cardbase, cmd, R15, TxUIE);
/* Reset external interrupts */
wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
/* Tx and Rx ints enabled */
wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB);
lp->tstate = ACTIVE;
restore_flags(flags);
return;
/* slotime has timed out */
case DEFER:
/* Check DCD - debounce it
* see Intel Microcommunications Handbook, p2-308
*/
wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0)
{
lp->tstate = DEFER;
tdelay(lp, 100);
/* DEFER until DCD transition or timeout */
wrtscc(lp->cardbase, cmd, R15, DCDIE);
restore_flags(flags);
return;
}
if (random() > lp->persist)
{
lp->tstate = DEFER;
tdelay(lp, lp->slotime);
restore_flags(flags);
return;
}
if (lp->dmachan)
wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
pt_rts(lp, ON); /* Tx on */
lp->tstate = ST_TXDELAY;
tdelay(lp, lp->txdelay);
restore_flags(flags);
return;
/* Only for int driven parts */
if (lp->dmachan)
{
restore_flags(flags);
return;
}
} /* end switch */
/*
* Rx mode only
* This triggers when hunt mode is entered, & since an ABORT
* automatically enters hunt mode, we use that to clean up
* any waiting garbage
*/
if ((lp->rstate == ACTIVE) && (st & BRK_ABRT) )
{
#ifdef PT_DEBUG
printk("PTd exisr(): abort detected.\n");
#endif
/* read and dump all of SCC Rx FIFO */
(void) rdscc(lp->cardbase, cmd, R8);
(void) rdscc(lp->cardbase, cmd, R8);
(void) rdscc(lp->cardbase, cmd, R8);
lp->rcp = lp->rcvbuf->data;
lp->rcvbuf->cnt = 0;
/* Re-sync the SCC */
wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
}
/* Check for DCD transitions */
if ( (st & DCD) != (lp->saved_RR0 & DCD))
{
#ifdef PT_DEBUG
printk("PTd: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" );
#endif
if (st & DCD)
{
/* Check that we don't already have some data */
if (lp->rcvbuf->cnt > 0)
{
#ifdef PT_DEBUG
printk("PTd pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt);
#endif
/* wind back buffers */
lp->rcp = lp->rcvbuf->data;
lp->rcvbuf->cnt = 0;
}
} else { /* DCD off */
/* read and dump al SCC FIFO */
(void)rdscc(lp->cardbase, cmd, R8);
(void)rdscc(lp->cardbase, cmd, R8);
(void)rdscc(lp->cardbase, cmd, R8);
/* wind back buffers */
lp->rcp = lp->rcvbuf->data;
lp->rcvbuf->cnt = 0;
/* Re-sync the SCC */
wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
}
}
/* Update the saved version of register RR) */
lp->saved_RR0 = st &~ ZCOUNT;
restore_flags(flags);
} /* pt_exisr() */
/* This function is used to send the KISS params back to the kernel itself,
* just like the TNCs do (I think)
* It's a (bit of a) kludge
*/
static void send_kiss(struct device *dev, unsigned char arg, unsigned char val)
{
struct sk_buff *skb;
unsigned char *cfix;
/* struct pt_local *lp = (struct pt_local*)dev->priv;*/
skb = dev_alloc_skb(2);
if (skb == NULL)
{
printk("PT: send_kiss(): Memory squeeze, dropping KISS reply.\n");
return;
}
skb->dev = dev;
cfix = skb_put(skb, 2);
cfix[0]=arg;
cfix[1]=val;
skb->protocol=htons(ETH_P_AX25);
skb->mac.raw=skb->data;
IS_SKB(skb);
netif_rx(skb);
}