From e5067d7cd967cb17067de24a162306b79f432b20 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 24 Jun 2015 04:23:46 +0200 Subject: Import newax25-2.4.3.patch.1.bz2 And cleanup the *.orig and *.rej files and whitespace errors that are part of the original patch. Signed-off-by: Ralf Baechle --- drivers/net/hamradio/scc.c | 2529 +++++++++++++++++++++++--------------------- 1 file changed, 1300 insertions(+), 1229 deletions(-) (limited to 'drivers/net/hamradio/scc.c') diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c index d256ef600..4cf69753c 100644 --- a/drivers/net/hamradio/scc.c +++ b/drivers/net/hamradio/scc.c @@ -1,9 +1,10 @@ #define RCS_ID "$Id: scc.c,v 1.75 1998/11/04 15:15:01 jreuter Exp jreuter $" -#define VERSION "3.0" +#define VERSION "4.0" +#define BANNER "Z8530 SCC driver version "VERSION".dl1bke by DL1BKE\n" /* - * Please use z8530drv-utils-3.0 with this version. + * Please use z8530drv-utils-4.0 with this version. * ------------------ * * You can find a subset of the documentation in @@ -103,9 +104,8 @@ flags that aren't... Restarting the DPLL does not help either, it resynchronizes too slow and the first received frame gets lost. - 2000-02-13 Fixed for new network driver interface changes, still - does TX timeouts itself since it uses its own queue - scheme. + 1999-02-21 Started to implement the new AX.25 device interface + 2000-07-18 Ported to 2.4.x Thanks to all who contributed to this driver with ideas and bug reports! @@ -113,7 +113,7 @@ NB -- if you find errors, change something, please let me know first before you distribute it... And please don't touch the version number. Just replace my callsign in - "v3.0.dl1bke" with your own. Just to avoid confusion... + "v4.0.dl1bke" with your own. Just to avoid confusion... If you want to add your modification to the linux distribution please (!) contact me first. @@ -131,21 +131,23 @@ Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org AX-25 : DL1BKE @ DB0ABH.#BAY.DEU.EU Internet: jreuter@yaina.de - www : http://yaina.de/jreuter + www : http://yaina.de/jreuter/ */ /* ----------------------------------------------------------------------- */ -#undef SCC_LDELAY /* slow it even a bit more down */ #undef SCC_DONT_CHECK /* don't look if the SCCs you specified are available */ -#define SCC_MAXCHIPS 4 /* number of max. supported chips */ #define SCC_BUFSIZE 384 /* must not exceed 4096 */ #undef SCC_DEBUG #define SCC_DEFAULT_CLOCK 4915200 /* default pclock if nothing is specified */ +#define SCC_SIMPLE_MAC /* no rts/cts control by DDI layer */ +#define SCC_WATCHDOG_TIMEOUT 10 + /* ten seconds */ + /* ----------------------------------------------------------------------- */ #include @@ -162,6 +164,7 @@ #include #include #include +#include #include #include @@ -170,196 +173,199 @@ #include #include -#include -#include "z8530.h" +#include +#include +#include #include +#include + #include + #include #include #include #include -#include -#include -#include +#include +#include "z8530.h" -static const char banner[] __initdata = KERN_INFO "AX.25: Z8530 SCC driver version "VERSION".dl1bke\n"; +int scc_init(void); -static void t_dwait(unsigned long); -static void t_txdelay(unsigned long); -static void t_tail(unsigned long); -static void t_busy(unsigned long); -static void t_maxkeyup(unsigned long); -static void t_idle(unsigned long); static void scc_tx_done(struct scc_channel *); -static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long); -static void scc_start_maxkeyup(struct scc_channel *); -static void scc_start_defer(struct scc_channel *); - -static void z8530_init(void); +static void scc_kick_tx(struct scc_channel *); +static void scc_start_tx_timer(struct scc_channel *, void (*)(struct scc_channel *), unsigned long); +static void scc_tail(struct scc_channel *scc); +#ifndef SCC_SIMPLE_MAC +static void scc_tx_forced(struct scc_channel *scc); +static void scc_set_rts(struct scc_channel *scc); +#endif static void init_channel(struct scc_channel *scc); static void scc_key_trx (struct scc_channel *scc, char tx); static void scc_isr(int irq, void *dev_id, struct pt_regs *regs); static void scc_init_timer(struct scc_channel *scc); -static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev); +static unsigned int scc_ddi_report_dcd(struct net_device *); +static unsigned int scc_ddi_report_ptt(struct net_device *); +#ifndef SCC_SIMPLE_MAC +static unsigned int scc_ddi_report_cts(struct net_device *); +static void scc_ddi_set_rts(struct net_device *); +#endif +static void scc_ddi_set_bitrate(struct net_device *, unsigned int); +static void scc_ddi_param_update(struct net_device *); +static void scc_ddi_param_notify(struct net_device *, int, int, int); + +static int scc_net_setup(struct scc_channel *scc); static int scc_net_init(struct net_device *dev); static int scc_net_open(struct net_device *dev); static int scc_net_close(struct net_device *dev); static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb); static int scc_net_tx(struct sk_buff *skb, struct net_device *dev); +static void scc_net_timeout(struct net_device *dev); static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); static int scc_net_set_mac_address(struct net_device *dev, void *addr); static struct net_device_stats * scc_net_get_stats(struct net_device *dev); -static unsigned char *SCC_DriverName = "scc"; +static int scc_proc_intvec_nchips(ctl_table *, int *, int, void *, size_t *, void *, size_t, void **); +static int scc_proc_intvec_modem(ctl_table *, int *, int, void *, size_t *, void *, size_t, void **); +static int scc_proc_intvec_port(ctl_table *, int *, int, void *, size_t *, void *, size_t, void **); +static int scc_proc_intvec_chip(ctl_table *, int *, int, void *, size_t *, void *, size_t, void **); -static struct irqflags { unsigned char used : 1; } Ivec[16]; - -static struct scc_channel SCC_Info[2 * SCC_MAXCHIPS]; /* information per channel */ +struct scc_ctrl_proc_tables { + ctl_table parent[2]; /* /proc/sys/net/dev/ */ + ctl_table dir[2]; /* /.../dev/z8530drv/ */ + ctl_table chip[10]; /* /.../dev/z8530drv/chip */ +}; + +static int scc_irq_used[16]; static struct scc_ctrl { - io_port chan_A; - io_port chan_B; - int irq; -} SCC_ctrl[SCC_MAXCHIPS+1]; + struct scc_channel channel_a; + struct scc_channel channel_b; + struct scc_ctrl_proc_tables proc_tables; + + struct ctl_table_header * proc_table_head; +} **scc_ctrl; + + +static struct ctl_table_header *scc_proc_table_header; +static int maxchips = 4; +static int Nchips = 0; +static long IO_Delay = 0; +static spinlock_t IO_Spinlock = SPIN_LOCK_UNLOCKED; + +static ctl_table scc_proc_parent_table[]; +static ctl_table scc_proc_nchips_table[]; -static unsigned char Driver_Initialized; -static int Nchips; -static io_port Vector_Latch; /* ******************************************************************** */ /* * Port Access Functions * */ /* ******************************************************************** */ +static inline unsigned char +Inb(io_port port) +{ + int r; + + r = inb(port); + if (IO_Delay) udelay(IO_Delay); + return r; +} + +static inline void +Outb(io_port port, unsigned char val) +{ + outb(val, port); + if (IO_Delay) udelay(IO_Delay); +} + /* These provide interrupt save 2-step access to the Z8530 registers */ -static inline unsigned char InReg(io_port port, unsigned char reg) +static unsigned char +InReg(io_port port, unsigned char reg) { unsigned long flags; unsigned char r; - - save_flags(flags); - cli(); -#ifdef SCC_LDELAY - Outb(port, reg); - udelay(SCC_LDELAY); - r=Inb(port); - udelay(SCC_LDELAY); -#else - Outb(port, reg); - r=Inb(port); -#endif - restore_flags(flags); + + spin_lock_irqsave(&IO_Spinlock, flags); + if (IO_Delay) + { + outb(reg, port); + udelay(IO_Delay); + r=inb(port); + udelay(IO_Delay); + } else { + outb(reg, port); + r=inb(port); + } + spin_unlock_irqrestore(&IO_Spinlock, flags); return r; } -static inline void OutReg(io_port port, unsigned char reg, unsigned char val) +static void +OutReg(io_port port, unsigned char reg, unsigned char val) { unsigned long flags; - - save_flags(flags); - cli(); -#ifdef SCC_LDELAY - Outb(port, reg); udelay(SCC_LDELAY); - Outb(port, val); udelay(SCC_LDELAY); -#else - Outb(port, reg); - Outb(port, val); -#endif - restore_flags(flags); + + spin_lock_irqsave(&IO_Spinlock, flags); + if (IO_Delay) + { + outb(reg, port); + udelay(IO_Delay); + outb(val, port); + udelay(IO_Delay); + } else { + outb(reg, port); + outb(val, port); + } + spin_unlock_irqrestore(&IO_Spinlock, flags); } -static inline void wr(struct scc_channel *scc, unsigned char reg, - unsigned char val) +static inline +void wr(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] = val)); } -static inline void or(struct scc_channel *scc, unsigned char reg, unsigned char val) +static inline void +or(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val)); } -static inline void cl(struct scc_channel *scc, unsigned char reg, unsigned char val) +static inline void +cl(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val)); } -/* ******************************************************************** */ -/* * Some useful macros * */ -/* ******************************************************************** */ - -static inline void scc_discard_buffers(struct scc_channel *scc) -{ - unsigned long flags; - - save_flags(flags); - cli(); - - if (scc->tx_buff != NULL) - { - dev_kfree_skb(scc->tx_buff); - scc->tx_buff = NULL; - } - - while (skb_queue_len(&scc->tx_queue)) - dev_kfree_skb(skb_dequeue(&scc->tx_queue)); - - restore_flags(flags); -} - - - /* ******************************************************************** */ /* * Interrupt Service Routines * */ /* ******************************************************************** */ -/* ----> subroutines for the interrupt handlers <---- */ - -static inline void scc_notify(struct scc_channel *scc, int event) -{ - struct sk_buff *skb; - char *bp; - - if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA) - return; - - skb = dev_alloc_skb(2); - if (skb != NULL) - { - bp = skb_put(skb, 2); - *bp++ = PARAM_HWEVENT; - *bp++ = event; - scc_net_rx(scc, skb); - } else - scc->stat.nospace++; -} - static inline void flush_rx_FIFO(struct scc_channel *scc) { int k; - for (k=0; k<3; k++) + for (k=0; k<(scc->enhanced? 8:4); k++) Inb(scc->data); if(scc->rx_buff != NULL) /* did we receive something? */ { - scc->stat.rxerrs++; /* then count it as an error */ - dev_kfree_skb_irq(scc->rx_buff); + scc->stat.rxerrs++; /* then count it as an error */ + kfree_skb(scc->rx_buff); scc->rx_buff = NULL; } } static void start_hunt(struct scc_channel *scc) { - if ((scc->modem.clocksrc != CLK_EXTERNAL)) + if ((scc->modem.rx_clock_source == CLOCK_SOURCE_DPLL)) OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ - or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ + or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ } /* ----> four different interrupt handlers for Tx, Rx, changing of */ @@ -377,36 +383,9 @@ static inline void scc_txint(struct scc_channel *scc) if (skb == NULL) { - skb = skb_dequeue(&scc->tx_queue); - scc->tx_buff = skb; netif_wake_queue(scc->dev); - - if (skb == NULL) - { - scc_tx_done(scc); - Outb(scc->ctrl, RES_Tx_P); - return; - } - - if (skb->len == 0) /* Paranoia... */ - { - dev_kfree_skb_irq(skb); - scc->tx_buff = NULL; - scc_tx_done(scc); - Outb(scc->ctrl, RES_Tx_P); - return; - } - - scc->stat.tx_state = TXS_ACTIVE; - - OutReg(scc->ctrl, R0, RES_Tx_CRC); - /* reset CRC generator */ - or(scc,R10,ABUNDER); /* re-install underrun protection */ - Outb(scc->data,*skb->data); /* send byte */ - skb_pull(skb, 1); - - if (!scc->enhanced) /* reset EOM latch */ - Outb(scc->ctrl,RES_EOM_L); + scc_tx_done(scc); + Outb(scc->ctrl, RES_Tx_P); return; } @@ -416,11 +395,11 @@ static inline void scc_txint(struct scc_channel *scc) { Outb(scc->ctrl, RES_Tx_P); /* reset pending int */ cl(scc, R10, ABUNDER); /* send CRC */ - dev_kfree_skb_irq(skb); + dev_kfree_skb(skb); scc->tx_buff = NULL; - scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */ + scc_kick_tx(scc); /* next frame */ return; - } + } /* send octet */ @@ -447,25 +426,23 @@ static inline void scc_exint(struct scc_channel *scc) /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */ - if ((changes & SYNC_HUNT) && scc->kiss.softdcd) + if ((changes & SYNC_HUNT) && scc->modem.softdcd) { if (status & SYNC_HUNT) { scc->dcd = 0; flush_rx_FIFO(scc); - if ((scc->modem.clocksrc != CLK_EXTERNAL)) + if ((scc->modem.rx_clock_source == CLOCK_SOURCE_DPLL)) OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ } else { scc->dcd = 1; } - - scc_notify(scc, scc->dcd? HWEV_DCD_OFF:HWEV_DCD_ON); } /* DCD: on = start to receive packet, off = ABORT condition */ /* (a successfully received packet generates a special condition int) */ - if((changes & DCD) && !scc->kiss.softdcd) /* DCD input changed state */ + if((changes & DCD) && !scc->modem.softdcd) /* DCD input changed state */ { if(status & DCD) /* DCD is now ON */ { @@ -476,8 +453,6 @@ static inline void scc_exint(struct scc_channel *scc) flush_rx_FIFO(scc); scc->dcd = 0; } - - scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF); } #ifdef notdef @@ -489,24 +464,24 @@ static inline void scc_exint(struct scc_channel *scc) if (chg_and_stat & CTS) /* CTS is now ON */ { - if (scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */ - scc_start_tx_timer(scc, t_txdelay, 0); + if (scc->modem.tx_delay == 0) /* zero TXDELAY = wait for CTS */ + scc_start_tx_timer(scc, t_tx_delay, 0); } #endif if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM)) { - scc->stat.tx_under++; /* oops, an underrun! count 'em */ + scc->stat.tx_under++; /* oops, an underrun! count 'em */ Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */ if (scc->tx_buff != NULL) { - dev_kfree_skb_irq(scc->tx_buff); + dev_kfree_skb(scc->tx_buff); scc->tx_buff = NULL; } or(scc,R10,ABUNDER); - scc_start_tx_timer(scc, t_txdelay, 0); /* restart transmission */ + scc_tx_done(scc); } scc->status = status; @@ -517,11 +492,12 @@ static inline void scc_exint(struct scc_channel *scc) /* Receiver interrupt handler */ static inline void scc_rxint(struct scc_channel *scc) { + unsigned char status; struct sk_buff *skb; scc->stat.rxints++; - if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF) + if((scc->wreg[5] & RTS) && (scc->modem.fullduplex == 0)) { Inb(scc->data); /* discard char */ or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ @@ -529,7 +505,6 @@ static inline void scc_rxint(struct scc_channel *scc) } skb = scc->rx_buff; - if (skb == NULL) { skb = dev_alloc_skb(scc->stat.bufsize); @@ -541,167 +516,116 @@ static inline void scc_rxint(struct scc_channel *scc) or(scc, R3, ENT_HM); return; } - - scc->rx_buff = skb; - *(skb_put(skb, 1)) = 0; /* KISS data */ - } - if (skb->len >= scc->stat.bufsize) - { -#ifdef notdef - printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n"); -#endif - dev_kfree_skb_irq(skb); - scc->rx_buff = NULL; - Inb(scc->data); - or(scc, R3, ENT_HM); - return; + scc->rx_buff = skb; } - *(skb_put(skb, 1)) = Inb(scc->data); -} - - -/* Receive Special Condition interrupt handler */ -static inline void scc_spint(struct scc_channel *scc) -{ - unsigned char status; - struct sk_buff *skb; - - scc->stat.spints++; - - status = InReg(scc->ctrl,R1); /* read receiver status */ - - Inb(scc->data); /* throw away Rx byte */ - skb = scc->rx_buff; - if(status & Rx_OVR) /* receiver overrun */ + while(InReg(scc->ctrl, R0) & Rx_CH_AV) { - scc->stat.rx_over++; /* count them */ - or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ - - if (skb != NULL) - dev_kfree_skb_irq(skb); - scc->rx_buff = skb = NULL; - } + status = InReg(scc->ctrl, R1); - if(status & END_FR && skb != NULL) /* end of frame */ - { - /* CRC okay, frame ends on 8 bit boundary and received something ? */ - - if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0) + if (skb->len > scc->stat.bufsize) { - /* ignore last received byte (first of the CRC bytes) */ - skb_trim(skb, skb->len-1); - scc_net_rx(scc, skb); - scc->rx_buff = NULL; - scc->stat.rxframes++; - } else { /* a bad frame */ - dev_kfree_skb_irq(skb); +#ifdef notdef + printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n"); +#endif + kfree_skb(skb); scc->rx_buff = NULL; - scc->stat.rxerrs++; + Inb(scc->data); + or(scc, R3, ENT_HM); + return; } - } - Outb(scc->ctrl,ERR_RES); -} + *(skb_put(skb, 1)) = Inb(scc->data); + if (status & Rx_OVR) + { + scc->stat.rx_over++; + or(scc, R3, ENT_HM); + if (skb != NULL) kfree_skb(skb); + scc->rx_buff = NULL; + Outb(scc->ctrl,ERR_RES); + } -/* ----> interrupt service routine for the Z8530 <---- */ + if (status & END_FR && skb != NULL) + { + /* CRC okay, frame ends on 8 bit boundary and received something ? */ -static void scc_isr_dispatch(struct scc_channel *scc, int vector) -{ - switch (vector & VECTOR_MASK) - { - case TXINT: scc_txint(scc); break; - case EXINT: scc_exint(scc); break; - case RXINT: scc_rxint(scc); break; - case SPINT: scc_spint(scc); break; + if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0) + { + /* ignore last received byte (first of the CRC bytes) */ + skb_trim(skb, skb->len-1); + scc_net_rx(scc, skb); + scc->rx_buff = NULL; + scc->stat.rxframes++; + } else { /* a bad frame */ + kfree_skb(skb); + scc->rx_buff = NULL; + scc->stat.rxerrs++; + Outb(scc->ctrl,ERR_RES); + } + } } } -/* If the card has a latch for the interrupt vector (like the PA0HZP card) - use it to get the number of the chip that generated the int. - If not: poll all defined chips. - */ -#define SCC_IRQTIMEOUT 30000 +#define SCC_IRQTIMEOUT 5000 static void scc_isr(int irq, void *dev_id, struct pt_regs *regs) { - unsigned char vector; + unsigned char istat; struct scc_channel *scc; - struct scc_ctrl *ctrl; + struct scc_ctrl **ctrl_p, *ctrl; int k; - if (Vector_Latch) - { - for(k=0; k < SCC_IRQTIMEOUT; k++) - { - Outb(Vector_Latch, 0); /* Generate INTACK */ - - /* Read the vector */ - if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; - if (vector & 0x01) break; - - scc=&SCC_Info[vector >> 3 ^ 0x01]; - if (!scc->dev) break; - - scc_isr_dispatch(scc, vector); - - OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */ - } - - if (k == SCC_IRQTIMEOUT) - printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n"); - - return; - } - /* Find the SCC generating the interrupt by polling all attached SCCs * reading RR3A (the interrupt pending register) */ - ctrl = SCC_ctrl; - while (ctrl->chan_A) + ctrl = *scc_ctrl; + for (ctrl_p=scc_ctrl; *ctrl_p; ctrl_p++) { - if (ctrl->irq != irq) - { - ctrl++; - continue; - } + ctrl = *ctrl_p; + if (ctrl->channel_a.irq != irq) continue; - scc = NULL; - for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++) + for (k = 0; k < SCC_IRQTIMEOUT; k++) { - vector=InReg(ctrl->chan_B,R2); /* Read the vector */ - if (vector & 0x01) break; - - scc = &SCC_Info[vector >> 3 ^ 0x01]; - if (!scc->dev) break; + istat = InReg(ctrl->channel_a.ctrl, R3); + if (!(istat & (CHARxIP|CHATxIP|CHAEXT|CHBRxIP|CHBTxIP|CHBEXT))) + break; + + scc = &ctrl->channel_a; + if ((istat & (CHARxIP|CHATxIP|CHAEXT)) && scc->dev) + { + scc = &ctrl->channel_a; + if (istat & CHARxIP) + scc_rxint(scc); + if (istat & CHATxIP) + scc_txint(scc); + if (istat & CHAEXT) + scc_exint(scc); + } - scc_isr_dispatch(scc, vector); + scc = &ctrl->channel_b; + if ((istat & (CHBRxIP|CHBTxIP|CHBEXT)) && scc->dev) + { + scc = &ctrl->channel_a; + if (istat & CHBRxIP) + scc_rxint(scc); + if (istat & CHBTxIP) + scc_txint(scc); + if (istat & CHBEXT) + scc_exint(scc); + } } + if (k == SCC_IRQTIMEOUT) { printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n"); break; } - - /* This looks weird and it is. At least the BayCom USCC doesn't - * use the Interrupt Daisy Chain, thus we'll have to start - * all over again to be sure not to miss an interrupt from - * (any of) the other chip(s)... - * Honestly, the situation *is* braindamaged... - */ - - if (scc != NULL) - { - OutReg(scc->ctrl,R0,RES_H_IUS); - ctrl = SCC_ctrl; - } else - ctrl++; } } @@ -724,24 +648,131 @@ static inline void set_brg(struct scc_channel *scc, unsigned int tc) static inline void set_speed(struct scc_channel *scc) { - disable_irq(scc->irq); + unsigned long flags; - if (scc->modem.speed > 0) /* paranoia... */ - set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2); + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); - enable_irq(scc->irq); + if (scc->modem.rx_speed > 0) /* paranoia... */ + set_brg(scc, (unsigned) (scc->clock / (scc->modem.rx_speed * 64)) - 2); + + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); } +/* + Construct value for clock mode register (R11) + + Here are some common settings: + ------------------------------ + + normal half duplex operation: + tx_clock_source = CLOCK_SOURCE_DPLL; + rx_clock_source = CLOCK_SOURCE_DPLL; + trxc_pin_mode = TRXCP_MODE_DPLL_OUT; + + note: this seems bogus, we used to switch to + tx_clock_source = CLOCK_SOURCE_BRG; + rx_clock_source = CLOCK_SOURCE_DPLL; + trxc_pin_mode = TRXCP_MODE_BRG_OUT; + on transmit which appears to be correct for Rx as well. + + normal operation for modems w/ own clock (re)generation on BayCom cards + tx_clock_source = CLOCK_SOURCE_RTxC + rx_clock_source = CLOCK_SOURCE_TRxC + trxc_pin_mode = TRXCP_MODE_IN + + normal operation for modems w/ own clock (re)generation on elswhere + tx_clock_source = CLOCK_SOURCE_TRxC + rx_clock_source = CLOCK_SOURCE_RTxC + trxc_pin_mode = TRXCP_MODE_IN + + fullduplex mode for modems w/o own clock generator + (aka "divider mode") - BayCom style: + tx_clock_source = CLOCK_SOURCE_RTxC + rx_clock_source = CLOCK_SOURCE_DPLL + trxc_pin_mode = TRXCP_MODE_DPLL_OUT; + (divider divides by 2) + + fullduplex mode for modems w/o own clock generator + (aka "divider mode") - usual style: + tx_clock_source = CLOCK_SOURCE_RTxC + rx_clock_source = CLOCK_SOURCE_DPLL + trxc_pin_mode = TRXCP_MODE_BRG_OUT + (BRG clock = 32 * DPLL clock, divider divides by 16) + + Note that only TRxC can be programmed to output, RTxC + is _always_ input +*/ + +static int scc_calc_r11(int rx_clock_source, int tx_clock_source, int trxc_pin_mode) +{ + int clockmode = 0; + + switch(tx_clock_source) + { + case CLOCK_SOURCE_RTxC: + break; + case CLOCK_SOURCE_TRxC: + clockmode = TCTRxCP; + break; + case CLOCK_SOURCE_BRG: + clockmode = TCBR; + break; + case CLOCK_SOURCE_DPLL: + clockmode = TCDPLL; + } + + + switch(rx_clock_source) + { + case CLOCK_SOURCE_RTxC: + break; + case CLOCK_SOURCE_TRxC: + clockmode |= RCTRxCP; + break; + case CLOCK_SOURCE_BRG: + clockmode |= RCBR; + break; + case CLOCK_SOURCE_DPLL: + clockmode |= RCDPLL; + } + + + switch(trxc_pin_mode) + { + case TRXCP_MODE_IN: + break; + case TRXCP_MODE_TXC_OUT: + clockmode |= TRxCTC|TRxCOI; + break; + case TRXCP_MODE_BRG_OUT: + clockmode |= TRxCBR|TRxCOI; + break; + case TRXCP_MODE_DPLL_OUT: + clockmode |= TRxCDP|TRxCOI; + break; + } + + return clockmode; +} -/* ----> initialize a SCC channel <---- */ -static inline void init_brg(struct scc_channel *scc) +static void scc_init_brg_and_dpll(struct scc_channel *scc) { wr(scc, R14, BRSRC); /* BRG source = PCLK */ - OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ - OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */ + if (scc->modem.tx_clock_source == CLOCK_SOURCE_DPLL || + scc->modem.rx_clock_source == CLOCK_SOURCE_DPLL) + { + OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ + OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */ + } else { + OutReg(scc->ctrl, R14, DISDPLL|scc->wreg[R14]); + } } + +/* ----> initialize an SCC channel <---- */ + + /* * Initialization according to the Z8530 manual (SGS-Thomson's version): * @@ -789,10 +820,10 @@ static inline void init_brg(struct scc_channel *scc) static void init_channel(struct scc_channel *scc) { - del_timer(&scc->tx_t); - del_timer(&scc->tx_wdog); + unsigned long flags; - disable_irq(scc->irq); + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + del_timer(&scc->tx_timer); wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */ wr(scc,R1,0); /* no W/REQ operation */ @@ -801,56 +832,11 @@ static void init_channel(struct scc_channel *scc) wr(scc,R6,0); /* SDLC address zero (not used) */ wr(scc,R7,FLAG); /* SDLC flag value */ wr(scc,R9,VIS); /* vector includes status */ - wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */ + wr(scc,R10,(scc->modem.nrz_mode? NRZI : NRZ)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */ wr(scc,R14, 0); - -/* set clock sources: - - CLK_DPLL: normal halfduplex operation - - RxClk: use DPLL - TxClk: use DPLL - TRxC mode DPLL output - - CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem) - - BayCom: others: - - TxClk = pin RTxC TxClk = pin TRxC - RxClk = pin TRxC RxClk = pin RTxC - - - CLK_DIVIDER: - RxClk = use DPLL - TxClk = pin RTxC - - BayCom: others: - pin TRxC = DPLL pin TRxC = BRG - (RxClk * 1) (RxClk * 32) -*/ - - - switch(scc->modem.clocksrc) - { - case CLK_DPLL: - wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); - init_brg(scc); - break; - - case CLK_DIVIDER: - wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI); - init_brg(scc); - break; - - case CLK_EXTERNAL: - wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP); - OutReg(scc->ctrl, R14, DISDPLL); - break; - - } - - set_speed(scc); /* set baudrate */ + wr(scc, R11, scc_calc_r11(scc->modem.rx_clock_source, scc->modem.tx_clock_source, scc->modem.trxc_pin_mode)); + scc_init_brg_and_dpll(scc); if(scc->enhanced) { @@ -858,7 +844,7 @@ static void init_channel(struct scc_channel *scc) wr(scc,R7,AUTOEOM); } - if(scc->kiss.softdcd || (InReg(scc->ctrl,R0) & DCD)) + if(scc->modem.softdcd || (InReg(scc->ctrl,R0) & DCD)) /* DCD is now ON */ { start_hunt(scc); @@ -866,7 +852,7 @@ static void init_channel(struct scc_channel *scc) /* enable ABORT, DCD & SYNC/HUNT interrupts */ - wr(scc,R15, BRKIE|TxUIE|(scc->kiss.softdcd? SYNCIE:DCDIE)); + wr(scc,R15, BRKIE|TxUIE|(scc->modem.softdcd? SYNCIE:DCDIE)); Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */ @@ -875,14 +861,13 @@ static void init_channel(struct scc_channel *scc) scc->status = InReg(scc->ctrl,R0); /* read initial status */ - or(scc,R9,MIE); /* master interrupt enable */ - - scc_init_timer(scc); - - enable_irq(scc->irq); -} + or(scc,R9,MIE); /* master interrupt enable */ + scc_init_timer(scc); + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); + set_speed(scc); /* set baudrate */ +} /* ******************************************************************** */ @@ -895,17 +880,20 @@ static void init_channel(struct scc_channel *scc) static void scc_key_trx(struct scc_channel *scc, char tx) { + unsigned long flags; unsigned int time_const; if (scc->brand & PRIMUS) - Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0)); + Outb(scc->ctrl + 4, scc->special_option | (tx? 0x80 : 0)); + - if (scc->modem.speed < 300) - scc->modem.speed = 1200; + if (tx) + time_const = (unsigned) (scc->clock / (scc->modem.tx_speed * 2)) - 2; + else + time_const = (unsigned) (scc->clock / (scc->modem.rx_speed * 64)) - 2; - time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2; - disable_irq(scc->irq); + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); if (tx) { @@ -913,7 +901,7 @@ static void scc_key_trx(struct scc_channel *scc, char tx) or(scc, R15, TxUIE); } - if (scc->modem.clocksrc == CLK_DPLL) + if (scc->modem.rx_clock_source == CLOCK_SOURCE_DPLL) { /* force simplex operation */ if (tx) { @@ -927,7 +915,7 @@ static void scc_key_trx(struct scc_channel *scc, char tx) wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR); /* By popular demand: tx_inhibit */ - if (scc->kiss.tx_inhibit) + if (scc->modem.tx_inhibit) { or(scc,R5, TxENAB); scc->wreg[R5] |= RTS; @@ -943,10 +931,10 @@ static void scc_key_trx(struct scc_channel *scc, char tx) wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); #ifndef CONFIG_SCC_TRXECHO - if (scc->kiss.softdcd) + if (scc->modem.softdcd) #endif { - or(scc,R15, scc->kiss.softdcd? SYNCIE:DCDIE); + or(scc,R15, scc->modem.softdcd? SYNCIE:DCDIE); start_hunt(scc); } } @@ -954,14 +942,14 @@ static void scc_key_trx(struct scc_channel *scc, char tx) if (tx) { #ifdef CONFIG_SCC_TRXECHO - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + if (scc->modem.fullduplex == 0) { cl(scc, R3, RxENABLE); cl(scc, R15, DCDIE|SYNCIE); } #endif - if (scc->kiss.tx_inhibit) + if (scc->modem.tx_inhibit) { or(scc,R5, TxENAB); scc->wreg[R5] |= RTS; @@ -971,87 +959,84 @@ static void scc_key_trx(struct scc_channel *scc, char tx) } else { cl(scc,R5,RTS|TxENAB); /* disable tx */ - if ((scc->kiss.fulldup == KISS_DUPLEX_HALF) && + if ((scc->modem.fullduplex == 0) && #ifndef CONFIG_SCC_TRXECHO - scc->kiss.softdcd) + scc->modem.softdcd) #else 1) #endif { - or(scc, R15, scc->kiss.softdcd? SYNCIE:DCDIE); + or(scc, R15, scc->modem.softdcd? SYNCIE:DCDIE); start_hunt(scc); } } } - enable_irq(scc->irq); + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); } - -/* ----> SCC timer interrupt handler and friends. <---- */ - -static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when) +static void scc_kick_tx(struct scc_channel *scc) { + struct sk_buff *skb; unsigned long flags; - - - save_flags(flags); - cli(); - del_timer(&scc->tx_t); + spin_lock_irqsave(&scc->spinlocks.kick_tx, flags); - if (when == 0) - { - handler((unsigned long) scc); - } else - if (when != TIMER_OFF) - { - scc->tx_t.data = (unsigned long) scc; - scc->tx_t.function = handler; - scc->tx_t.expires = jiffies + (when*HZ)/100; - add_timer(&scc->tx_t); - } - - restore_flags(flags); -} + skb = scc->tx_new; + scc->tx_new = NULL; + netif_wake_queue(scc->dev); -static void scc_start_defer(struct scc_channel *scc) -{ - unsigned long flags; - - save_flags(flags); - cli(); + if (skb == NULL) goto nada; - del_timer(&scc->tx_wdog); - - if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF) + if (skb->len == 0) /* Paranoia... */ { - scc->tx_wdog.data = (unsigned long) scc; - scc->tx_wdog.function = t_busy; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer; - add_timer(&scc->tx_wdog); + dev_kfree_skb(skb); + scc->tx_buff = NULL; + Outb(scc->ctrl, RES_Tx_P); + goto nada; } - restore_flags(flags); + + scc->tx_buff = skb; + scc->stat.tx_state = TXS_ACTIVE; + OutReg(scc->ctrl, R0, RES_Tx_CRC); + /* reset CRC generator */ + or(scc,R10,ABUNDER); /* re-install underrun protection */ + Outb(scc->data,*skb->data); /* send byte */ + skb_pull(skb, 1); + + if (!scc->enhanced) /* reset EOM latch */ + Outb(scc->ctrl,RES_EOM_L); + + spin_unlock_irqrestore(&scc->spinlocks.kick_tx, flags); + return; + +nada: + scc_tx_done(scc); + spin_unlock_irqrestore(&scc->spinlocks.kick_tx, flags); + return; } -static void scc_start_maxkeyup(struct scc_channel *scc) +/* ----> SCC timer interrupt handler and friends. <---- */ + +static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(struct scc_channel *), unsigned long when) { unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&scc->spinlocks.timer, flags); - del_timer(&scc->tx_wdog); - - if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF) + del_timer(&scc->tx_timer); + + if (when != 0) { - scc->tx_wdog.data = (unsigned long) scc; - scc->tx_wdog.function = t_maxkeyup; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup; - add_timer(&scc->tx_wdog); + scc->tx_timer.data = (unsigned long) scc; + scc->tx_timer.function = (void (*)(unsigned long)) handler; + scc->tx_timer.expires = jiffies + (when*HZ)/1000; + add_timer(&scc->tx_timer); + } else { + handler(scc); } - - restore_flags(flags); + + spin_unlock_irqrestore(&scc->spinlocks.timer, flags); } /* @@ -1061,243 +1046,96 @@ static void scc_start_maxkeyup(struct scc_channel *scc) static void scc_tx_done(struct scc_channel *scc) { - /* - * trx remains keyed in fulldup mode 2 until t_idle expires. - */ - - switch (scc->kiss.fulldup) - { - case KISS_DUPLEX_LINK: - scc->stat.tx_state = TXS_IDLE2; - if (scc->kiss.idletime != TIMER_OFF) - scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100); - break; - case KISS_DUPLEX_OPTIMA: - scc_notify(scc, HWEV_ALL_SENT); - break; - default: - scc->stat.tx_state = TXS_BUSY; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); - } + scc->stat.tx_state = TXS_TAIL; - netif_wake_queue(scc->dev); + if (scc->modem.tx_tail != 0) + scc_start_tx_timer(scc, scc_tail, scc->modem.tx_tail); + else + scc_tail(scc); } +#ifndef SCC_SIMPLE_MAC +static void scc_tx_forced(struct scc_channel *scc) +{ + scc->stat.tx_state = TXS_TAIL; + + if (scc->tx_new) + scc_kick_tx(scc); + else + // remain key-up'ed for the time of tx_delay... + // what's the timeout in 6pack? + scc_start_tx_timer(scc, scc_tail, scc->modem.tx_delay); +} +#endif -static unsigned char Rand = 17; - -static inline int is_grouped(struct scc_channel *scc) +static void scc_tx_start(struct scc_channel *scc, struct sk_buff *skb) { - int k; - struct scc_channel *scc2; - unsigned char grp1, grp2; - - grp1 = scc->kiss.group; + scc->tx_new = skb; - for (k = 0; k < (Nchips * 2); k++) + /* + * scc_set_rts may also start a tx delay wait time, if we + * get a frame to transmit within this time RTS would be set, + * shorten the tx delay time... + */ + + if (scc->stat.tx_state != TXS_TXDELAY) { - scc2 = &SCC_Info[k]; - grp2 = scc2->kiss.group; - - if (scc2 == scc || !(scc2->dev && grp2)) - continue; - - if ((grp1 & 0x3f) == (grp2 & 0x3f)) + scc->stat.tx_state = TXS_TXDELAY; + + if ( !(scc->wreg[R5] & RTS) ) { - if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) ) - return 1; - - if ( (grp1 & RXGROUP) && scc2->dcd ) - return 1; + scc_key_trx(scc, TX_ON); + scc_start_tx_timer(scc, scc_kick_tx, scc->modem.tx_delay); + } else { + scc_start_tx_timer(scc, scc_kick_tx, 0); } } - return 0; } -/* DWAIT and SLOTTIME expired - * - * fulldup == 0: DCD is active or Rand > P-persistence: start t_busy timer - * else key trx and start txdelay - * fulldup == 1: key trx and start txdelay - * fulldup == 2: mintime expired, reset status or key trx and start txdelay - */ - -static void t_dwait(unsigned long channel) +#ifndef SCC_SIMPLE_MAC +static void scc_set_rts(struct scc_channel *scc) { - struct scc_channel *scc = (struct scc_channel *) channel; - - if (scc->stat.tx_state == TXS_WAIT) /* maxkeyup or idle timeout */ - { - if (skb_queue_len(&scc->tx_queue) == 0) /* nothing to send */ - { - scc->stat.tx_state = TXS_IDLE; - netif_wake_queue(scc->dev); /* t_maxkeyup locked it. */ - return; - } - - scc->stat.tx_state = TXS_BUSY; - } - - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - Rand = Rand * 17 + 31; - - if (scc->dcd || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) ) - { - scc_start_defer(scc); - scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime); - return ; - } - } + scc->stat.tx_state = TXS_TXDELAY; if ( !(scc->wreg[R5] & RTS) ) { scc_key_trx(scc, TX_ON); - scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); + scc_start_tx_timer(scc, scc_tx_forced, scc->modem.tx_delay); } else { - scc_start_tx_timer(scc, t_txdelay, 0); + scc_start_tx_timer(scc, scc_tx_forced, 0); } } +#endif - -/* TXDELAY expired - * - * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog. +/* + * TAILTIME expired */ -static void t_txdelay(unsigned long channel) +static void scc_tail(struct scc_channel *scc) { - struct scc_channel *scc = (struct scc_channel *) channel; - - scc_start_maxkeyup(scc); + if (scc->tx_buff != NULL) + return; - if (scc->tx_buff == NULL) + if (scc->tx_new != NULL) { - disable_irq(scc->irq); - scc_txint(scc); - enable_irq(scc->irq); + scc_kick_tx(scc); + return; } -} - -/* TAILTIME expired - * - * switch off transmitter. If we were stopped by Maxkeyup restart - * transmission after 'mintime' seconds - */ - -static void t_tail(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - unsigned long flags; - - save_flags(flags); - cli(); - - del_timer(&scc->tx_wdog); scc_key_trx(scc, TX_OFF); - - restore_flags(flags); - - if (scc->stat.tx_state == TXS_TIMEOUT) /* we had a timeout? */ - { - scc->stat.tx_state = TXS_WAIT; - - if (scc->kiss.mintime != TIMER_OFF) /* try it again */ - scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); - else - scc_start_tx_timer(scc, t_dwait, 0); - return; - } - scc->stat.tx_state = TXS_IDLE; - netif_wake_queue(scc->dev); -} - - -/* BUSY timeout - * - * throw away send buffers if DCD remains active too long. - */ - -static void t_busy(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - - del_timer(&scc->tx_t); - netif_stop_queue(scc->dev); /* don't pile on the wabbit! */ - - scc_discard_buffers(scc); - scc->stat.txerrs++; - scc->stat.tx_state = TXS_IDLE; - - netif_wake_queue(scc->dev); -} - -/* MAXKEYUP timeout - * - * this is our watchdog. - */ - -static void t_maxkeyup(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - unsigned long flags; - - save_flags(flags); - cli(); - - /* - * let things settle down before we start to - * accept new data. - */ - - netif_stop_queue(scc->dev); - scc_discard_buffers(scc); - - del_timer(&scc->tx_t); - - cl(scc, R1, TxINT_ENAB); /* force an ABORT, but don't */ - cl(scc, R15, TxUIE); /* count it. */ - OutReg(scc->ctrl, R0, RES_Tx_P); - - restore_flags(flags); - - scc->stat.txerrs++; - scc->stat.tx_state = TXS_TIMEOUT; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); -} - -/* IDLE timeout - * - * in fulldup mode 2 it keys down the transmitter after 'idle' seconds - * of inactivity. We will not restart transmission before 'mintime' - * expires. - */ - -static void t_idle(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - - del_timer(&scc->tx_wdog); - - scc_key_trx(scc, TX_OFF); - - if (scc->kiss.mintime != TIMER_OFF) - scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); - scc->stat.tx_state = TXS_WAIT; } static void scc_init_timer(struct scc_channel *scc) { unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&scc->spinlocks.timer, flags); scc->stat.tx_state = TXS_IDLE; + // FIXME: this can't be all, can it...? - restore_flags(flags); + spin_unlock_irqrestore(&scc->spinlocks.timer, flags); } @@ -1307,256 +1145,155 @@ static void scc_init_timer(struct scc_channel *scc) /* - * this will set the "hardware" parameters through KISS commands or ioctl() + * this will set the (some, anyway...) MODEM parameters */ -#define CAST(x) (unsigned long)(x) - -static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg) +static unsigned int scc_set_param(struct scc_channel *scc, struct scc_modem *modem) { - switch (cmd) - { - case PARAM_TXDELAY: scc->kiss.txdelay=arg; break; - case PARAM_PERSIST: scc->kiss.persist=arg; break; - case PARAM_SLOTTIME: scc->kiss.slottime=arg; break; - case PARAM_TXTAIL: scc->kiss.tailtime=arg; break; - case PARAM_FULLDUP: scc->kiss.fulldup=arg; break; - case PARAM_DTR: break; /* does someone need this? */ - case PARAM_GROUP: scc->kiss.group=arg; break; - case PARAM_IDLE: scc->kiss.idletime=arg; break; - case PARAM_MIN: scc->kiss.mintime=arg; break; - case PARAM_MAXKEY: scc->kiss.maxkeyup=arg; break; - case PARAM_WAIT: scc->kiss.waittime=arg; break; - case PARAM_MAXDEFER: scc->kiss.maxdefer=arg; break; - case PARAM_TX: scc->kiss.tx_inhibit=arg; break; - - case PARAM_SOFTDCD: - scc->kiss.softdcd=arg; - if (arg) - { - or(scc, R15, SYNCIE); - cl(scc, R15, DCDIE); - start_hunt(scc); - } else { - or(scc, R15, DCDIE); - cl(scc, R15, SYNCIE); - } - break; - - case PARAM_SPEED: - if (arg < 256) - scc->modem.speed=arg*100; - else - scc->modem.speed=arg; + scc->modem.tx_delay = modem->tx_delay; + scc->modem.tx_tail = modem->tx_tail; + scc->modem.fullduplex = modem->fullduplex; + scc->modem.tx_inhibit = modem->tx_inhibit; + scc->modem.softdcd = modem->softdcd; - if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */ - set_speed(scc); - break; - - case PARAM_RTS: - if ( !(scc->wreg[R5] & RTS) ) - { - if (arg != TX_OFF) - scc_key_trx(scc, TX_ON); - scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); - } else { - if (arg == TX_OFF) - { - scc->stat.tx_state = TXS_BUSY; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); - } - } - break; - - case PARAM_HWEVENT: - scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF); - break; - default: return -EINVAL; + if (modem->softdcd) + { + or(scc, R15, SYNCIE); + cl(scc, R15, DCDIE); + start_hunt(scc); + } else { + or(scc, R15, DCDIE); + cl(scc, R15, SYNCIE); } return 0; } - - -static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd) +static unsigned int scc_ddi_report_dcd(struct net_device *dev) { - switch (cmd) - { - case PARAM_TXDELAY: return CAST(scc->kiss.txdelay); - case PARAM_PERSIST: return CAST(scc->kiss.persist); - case PARAM_SLOTTIME: return CAST(scc->kiss.slottime); - case PARAM_TXTAIL: return CAST(scc->kiss.tailtime); - case PARAM_FULLDUP: return CAST(scc->kiss.fulldup); - case PARAM_SOFTDCD: return CAST(scc->kiss.softdcd); - case PARAM_DTR: return CAST((scc->wreg[R5] & DTR)? 1:0); - case PARAM_RTS: return CAST((scc->wreg[R5] & RTS)? 1:0); - case PARAM_SPEED: return CAST(scc->modem.speed); - case PARAM_GROUP: return CAST(scc->kiss.group); - case PARAM_IDLE: return CAST(scc->kiss.idletime); - case PARAM_MIN: return CAST(scc->kiss.mintime); - case PARAM_MAXKEY: return CAST(scc->kiss.maxkeyup); - case PARAM_WAIT: return CAST(scc->kiss.waittime); - case PARAM_MAXDEFER: return CAST(scc->kiss.maxdefer); - case PARAM_TX: return CAST(scc->kiss.tx_inhibit); - default: return NO_SUCH_PARAM; - } - + struct scc_channel *scc = (struct scc_channel *) dev->priv; +/* printk(KERN_INFO "dcd=%d\n", scc->dcd); */ + return scc->dcd; } -#undef CAST - -/* ******************************************************************* */ -/* * Send calibration pattern * */ -/* ******************************************************************* */ - -static void scc_stop_calibrate(unsigned long channel) +static unsigned int scc_ddi_report_ptt(struct net_device *dev) { - struct scc_channel *scc = (struct scc_channel *) channel; - unsigned long flags; - - save_flags(flags); - cli(); - - del_timer(&scc->tx_wdog); - scc_key_trx(scc, TX_OFF); - wr(scc, R6, 0); - wr(scc, R7, FLAG); - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); - - netif_wake_queue(scc->dev); - restore_flags(flags); + struct scc_channel *scc = (struct scc_channel *) dev->priv; +/* printk(KERN_INFO "rts=%d\n", (scc->wreg[R5] & RTS)? 1:0); */ + return (scc->wreg[R5] & RTS)? 1:0; } - -static void -scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern) +#ifndef SCC_SIMPLE_MAC +static unsigned int scc_ddi_report_cts(struct net_device *dev) { - unsigned long flags; - - save_flags(flags); - cli(); + struct scc_channel *scc = (struct scc_channel *) dev->priv; + int txs = scc->stat.tx_state; - netif_stop_queue(scc->dev); - scc_discard_buffers(scc); +/* printk(KERN_INFO "cts=%d\n", ((txs == TXS_ACTIVE) || (txs == TXS_TAIL))? 1:0); */ + return ((txs == TXS_ACTIVE) || (txs == TXS_TAIL))? 1:0; +} - del_timer(&scc->tx_wdog); +static void scc_ddi_set_rts(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; +/* printk(KERN_INFO "setrts\n"); */ + scc_set_rts(scc); +} +#endif - scc->tx_wdog.data = (unsigned long) scc; - scc->tx_wdog.function = scc_stop_calibrate; - scc->tx_wdog.expires = jiffies + HZ*duration; - add_timer(&scc->tx_wdog); +static void scc_ddi_set_bitrate(struct net_device *dev, unsigned int speed) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + scc->modem.rx_speed = speed; + scc->modem.tx_speed = speed; + if (scc->stat.tx_state != TXS_ACTIVE) + set_speed(scc); +} - /* This doesn't seem to work. Why not? */ - wr(scc, R6, 0); - wr(scc, R7, pattern); +/* Update general parameters so that they reflect our internal settings */ +static void scc_ddi_param_update(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; - /* - * Don't know if this works. - * Damn, where is my Z8530 programming manual...? - */ + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, scc->modem.fullduplex); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, scc->modem.rx_speed); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, scc->modem.tx_speed); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, scc->modem.tx_delay); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, scc->modem.tx_tail); + return; +} - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); +/* Called from upper layers when parameter was changed */ +static void scc_ddi_param_notify(struct net_device *dev, int valueno, int old, int new) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; - scc_key_trx(scc, TX_ON); - restore_flags(flags); + switch (valueno) + { + case AX25_VALUES_MEDIA_DUPLEX: + if (!netif_running(dev)) scc->modem.fullduplex = new; + break; + case AX25_VALUES_MEDIA_RXBITRATE: + case AX25_VALUES_MEDIA_TXBITRATE: + scc_ddi_set_bitrate(dev, new); + break; + case AX25_VALUES_MEDIA_TXDELAY: + scc->modem.tx_delay = new; + break; + case AX25_VALUES_MEDIA_TXTAIL: + scc->modem.tx_tail = new; + break; + default: + break; + } + scc_ddi_param_update(dev); + return; } /* ******************************************************************* */ /* * Init channel structures, special HW, etc... * */ /* ******************************************************************* */ -/* - * Reset the Z8530s and setup special hardware - */ - -static void z8530_init(void) -{ - struct scc_channel *scc; - int chip, k; - unsigned long flags; - char *flag; - - - printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2); - - flag=" "; - for (k = 0; k < 16; k++) - if (Ivec[k].used) - { - printk("%s%d", flag, k); - flag=","; - } - printk("\n"); - - /* reset and pre-init all chips in the system */ - for (chip = 0; chip < Nchips; chip++) - { - scc=&SCC_Info[2*chip]; - if (!scc->ctrl) continue; - - /* Special SCC cards */ - - if(scc->brand & EAGLE) /* this is an EAGLE card */ - Outb(scc->special,0x08); /* enable interrupt on the board */ - - if(scc->brand & (PC100 | PRIMUS)) /* this is a PC100/PRIMUS card */ - Outb(scc->special,scc->option); /* set the MODEM mode (0x22) */ - - - /* Reset and pre-init Z8530 */ - - save_flags(flags); - cli(); - - Outb(scc->ctrl, 0); - OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */ - udelay(100); /* give it 'a bit' more time than required */ - wr(scc, R2, chip*16); /* interrupt vector */ - wr(scc, R9, VIS); /* vector includes status */ - - restore_flags(flags); - } - - - Driver_Initialized = 1; -} /* * Allocate device structure, err, instance, and register driver */ -static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev) +static int scc_net_setup(struct scc_channel *scc) { + int k = 0; struct net_device *dev; - if (dev_get(name)) - { - printk(KERN_INFO "Z8530drv: device %s already exists.\n", name); - return -EEXIST; - } - if ((scc->dev = (struct net_device *) kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) return -ENOMEM; dev = scc->dev; memset(dev, 0, sizeof(struct net_device)); - strcpy(dev->name, name); + do { + sprintf(dev->name, "scc%d", k); + if (++k > 256) goto bail_out; /* avoid endless loop */ + } while (dev_get_by_name(dev->name) == NULL); + dev->priv = (void *) scc; dev->init = scc_net_init; - if ((addev? register_netdevice(dev) : register_netdev(dev)) != 0) { - kfree(dev); - return -EIO; - } + if (register_netdev(dev) != 0) goto bail_out; + + scc->proc_dev_name = dev->name; + scc->tx_buff = NULL; + scc->tx_new = NULL; + scc->init = 1; + scc_ddi_param_update(dev); - SET_MODULE_OWNER(dev); return 0; + +bail_out: + kfree(dev); + scc->dev = NULL; + return -EINVAL; } @@ -1565,15 +1302,12 @@ static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev /* * Network driver methods * */ /* ******************************************************************** */ -static unsigned char ax25_bcast[AX25_ADDR_LEN] = -{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; -static unsigned char ax25_nocall[AX25_ADDR_LEN] = -{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1}; - /* ----> Initialize device <----- */ static int scc_net_init(struct net_device *dev) { + struct scc_channel *scc = (struct scc_channel *) dev->priv; + struct ax25_dev *ax25dev; dev_init_buffers(dev); dev->tx_queue_len = 16; /* should be enough... */ @@ -1582,23 +1316,28 @@ static int scc_net_init(struct net_device *dev) dev->stop = scc_net_close; dev->hard_start_xmit = scc_net_tx; - dev->hard_header = ax25_encapsulate; - dev->rebuild_header = ax25_rebuild_header; dev->set_mac_address = scc_net_set_mac_address; dev->get_stats = scc_net_get_stats; dev->do_ioctl = scc_net_ioctl; - dev->tx_timeout = NULL; - - memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); - memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN); - - dev->flags = 0; + dev->tx_timeout = scc_net_timeout; + dev->watchdog_timeo = SCC_WATCHDOG_TIMEOUT; dev->type = ARPHRD_AX25; dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; dev->mtu = AX25_DEF_PACLEN; dev->addr_len = AX25_ADDR_LEN; + AX25_PTR(dev) = ax25dev = &scc->ax25dev; + memset(ax25dev, 0, sizeof(struct ax25_dev)); + ax25dev->hw.ptt = scc_ddi_report_ptt; + ax25dev->hw.dcd = scc_ddi_report_dcd; +#ifndef SCC_SIMPLE_MAC + ax25dev->hw.cts = scc_ddi_report_cts; + ax25dev->hw.rts = scc_ddi_set_rts; +#endif + ax25dev->hw.parameter_change_notify = scc_ddi_param_notify; + ax25dev->hw.fast = 0; + return 0; } @@ -1608,15 +1347,17 @@ static int scc_net_open(struct net_device *dev) { struct scc_channel *scc = (struct scc_channel *) dev->priv; - if (!scc->init) - return -EINVAL; + if (!scc->init) return -EINVAL; + + MOD_INC_USE_COUNT; scc->tx_buff = NULL; - skb_queue_head_init(&scc->tx_queue); init_channel(scc); + scc_ddi_param_update(dev); netif_start_queue(dev); + return 0; } @@ -1628,21 +1369,21 @@ static int scc_net_close(struct net_device *dev) unsigned long flags; netif_stop_queue(dev); - - save_flags(flags); - cli(); + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); - Outb(scc->ctrl,0); /* Make sure pointer is written */ - wr(scc,R1,0); /* disable interrupts */ + Outb(scc->ctrl,0); /* Make sure pointer is written */ + wr(scc,R1,0); /* disable interrupts */ wr(scc,R3,0); - del_timer(&scc->tx_t); - del_timer(&scc->tx_wdog); + del_timer(&scc->tx_timer); + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); - restore_flags(flags); - - scc_discard_buffers(scc); + if (scc->tx_buff) + kfree_skb(scc->tx_buff); + if (scc->tx_new) + kfree_skb(scc->tx_new); + MOD_DEC_USE_COUNT; return 0; } @@ -1650,8 +1391,9 @@ static int scc_net_close(struct net_device *dev) static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) { - if (skb->len == 0) { - dev_kfree_skb_irq(skb); + if (skb->len == 0) + { + kfree_skb(skb); return; } @@ -1671,299 +1413,186 @@ static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) static int scc_net_tx(struct sk_buff *skb, struct net_device *dev) { struct scc_channel *scc = (struct scc_channel *) dev->priv; - unsigned long flags; - char kisscmd; - if (skb->len > scc->stat.bufsize || skb->len < 2) { + if (skb->len > scc->stat.bufsize || skb->len < 2) + { scc->dev_stat.tx_dropped++; /* bogus frame */ dev_kfree_skb(skb); return 0; } - + + netif_stop_queue(dev); + scc->dev_stat.tx_packets++; scc->stat.txframes++; - - kisscmd = *skb->data & 0x1f; - skb_pull(skb, 1); - if (kisscmd) { - scc_set_param(scc, kisscmd, *skb->data); - dev_kfree_skb(skb); +#ifdef notdef + /**** The following "fix" is bogus. Fix the protocol, dammit! ****/ + + /* avoid race condition when skb is a cloned broadcast */ + skb_cp = skb_copy(skb, GFP_ATOMIC); + dev_kfree_skb(skb); + if (skb_cp == NULL) { + scc->dev_stat.tx_dropped++; /* out of memory */ return 0; } +#endif - save_flags(flags); - cli(); + /* transmit frame */ - if (skb_queue_len(&scc->tx_queue) > scc->dev->tx_queue_len) { - struct sk_buff *skb_del; - skb_del = skb_dequeue(&scc->tx_queue); - dev_kfree_skb(skb_del); - } - skb_queue_tail(&scc->tx_queue, skb); dev->trans_start = jiffies; - + scc_tx_start(scc, skb); - /* - * Start transmission if the trx state is idle or - * t_idle hasn't expired yet. Use dwait/persistance/slottime - * algorithm for normal halfduplex operation. - */ + return 0; +} - if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) { - scc->stat.tx_state = TXS_BUSY; - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime); - else - scc_start_tx_timer(scc, t_dwait, 0); - } - restore_flags(flags); - return 0; +/* -----> Watchdog <----- */ + +static void scc_net_timeout(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + unsigned long flags; + + del_timer(&scc->tx_timer); + + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + cl(scc, R1, TxINT_ENAB); + cl(scc, R15, TxUIE); + OutReg(scc->ctrl, R0, RES_Tx_P); + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); + + scc_start_tx_timer(scc, init_channel, 100); } -/* ----> ioctl functions <---- */ -/* - * SIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg - * SIOCSCCINI - initialize driver arg: --- - * SIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg - * SIOCSCCSMEM - set memory arg: (struct scc_mem_config *) arg - * SIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg - * SIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg - * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg - * SIOCSCCCAL - send calib. pattern arg: (struct scc_calibrate *) arg - */ + +/* ----> ioctl functions <---- */ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct scc_kiss_cmd kiss_cmd; - struct scc_mem_config memcfg; - struct scc_hw_config hwcfg; - struct scc_calibrate cal; - int chan; - unsigned char device_name[10]; - void *arg; - struct scc_channel *scc; - - scc = (struct scc_channel *) dev->priv; - arg = (void *) ifr->ifr_data; - - if (!Driver_Initialized) - { - if (cmd == SIOCSCCCFG) - { - int found = 1; + return -ENOIOCTLCMD; +} - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (!arg) return -EFAULT; +static int scc_check_and_init(struct scc_ctrl *ctrl) +{ + ctl_table *t_port[2]; + unsigned long flags; + int found_a = 1; + int found_b = 1; + int port, entry; - if (Nchips >= SCC_MAXCHIPS) - return -EINVAL; + /* are we still missing vital information? */ + if (ctrl->channel_a.ctrl <= 0 || ctrl->channel_a.data <= 0 || + ctrl->channel_b.ctrl <= 0 || ctrl->channel_b.ctrl <= 0 || + ctrl->channel_a.irq <= 0) + return 1; - if (copy_from_user(&hwcfg, arg, sizeof(hwcfg))) - return -EFAULT; + check_region(ctrl->channel_a.ctrl, 1); + check_region(ctrl->channel_b.ctrl, 1); - if (hwcfg.irq == 2) hwcfg.irq = 9; + spin_lock_irqsave(&ctrl->channel_a.spinlocks.hwaccess, flags); - if (!Ivec[hwcfg.irq].used && hwcfg.irq) - { - if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL)) - printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); - else - Ivec[hwcfg.irq].used = 1; - } + /* reset the chip */ - if (hwcfg.vector_latch) { - if (!request_region(Vector_Latch, 1, "scc vector latch")) - printk(KERN_WARNING "z8530drv: warning, cannot reserve vector latch port 0x%lx\n, disabled.", hwcfg.vector_latch); - else - Vector_Latch = hwcfg.vector_latch; - } + Outb(ctrl->channel_a.ctrl, 0); + OutReg(ctrl->channel_a.ctrl, R9, FHWRES); + udelay(100); - if (hwcfg.clock == 0) - hwcfg.clock = SCC_DEFAULT_CLOCK; + /* check if it's really there... */ #ifndef SCC_DONT_CHECK - disable_irq(hwcfg.irq); + OutReg(ctrl->channel_a.ctrl, R13, 0x55); + udelay(5); + if (InReg(ctrl->channel_a.ctrl, R13) != 0x55) + found_a = 0; + + OutReg(ctrl->channel_b.ctrl, R13, 0xaa); + udelay(5); + if (InReg(ctrl->channel_b.ctrl, R13) != 0xaa) + found_b = 0; +#endif + spin_unlock_irqrestore(&ctrl->channel_a.spinlocks.hwaccess, flags); - check_region(scc->ctrl, 1); - Outb(hwcfg.ctrl_a, 0); - OutReg(hwcfg.ctrl_a, R9, FHWRES); - udelay(100); - OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */ - udelay(5); + printk(KERN_INFO "z8530drv: chip %d, channel A (0x%3.3x/0x%3.3x) %sfound, " + "channel B (0x%3.3x/0x%3.3x) %sfound, irq %d\n", + Nchips, + (int) ctrl->channel_a.data, (int) ctrl->channel_a.ctrl, found_a? "":"not ", + (int) ctrl->channel_b.data, (int) ctrl->channel_b.ctrl, found_b? "":"not ", + ctrl->channel_a.irq); - if (InReg(hwcfg.ctrl_a,R13) != 0x55) - found = 0; + /* at least one channel not properly specified */ + if (!(found_a && found_b)) + return -EINVAL; - enable_irq(hwcfg.irq); -#endif + /* grab IRQ */ + if (!scc_irq_used[ctrl->channel_a.irq]) + { + if (request_irq(ctrl->channel_a.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL)) + { + printk(KERN_WARNING "z8530drv: cannot get IRQ %d\n", ctrl->channel_a.irq); + return -EINVAL; + } - if (found) - { - SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a; - SCC_Info[2*Nchips ].data = hwcfg.data_a; - SCC_Info[2*Nchips ].irq = hwcfg.irq; - SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b; - SCC_Info[2*Nchips+1].data = hwcfg.data_b; - SCC_Info[2*Nchips+1].irq = hwcfg.irq; - - SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a; - SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b; - SCC_ctrl[Nchips].irq = hwcfg.irq; - } + scc_irq_used[ctrl->channel_a.irq]++; + } + /* register IO ports */ + request_region(ctrl->channel_a.ctrl, 1, "scc ctrl"); + request_region(ctrl->channel_b.ctrl, 1, "scc ctrl"); + request_region(ctrl->channel_a.data, 1, "scc data"); + request_region(ctrl->channel_b.data, 1, "scc data"); - for (chan = 0; chan < 2; chan++) - { - sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan); + /* register network devices */ + scc_net_setup(&ctrl->channel_a); + scc_net_setup(&ctrl->channel_b); - SCC_Info[2*Nchips+chan].special = hwcfg.special; - SCC_Info[2*Nchips+chan].clock = hwcfg.clock; - SCC_Info[2*Nchips+chan].brand = hwcfg.brand; - SCC_Info[2*Nchips+chan].option = hwcfg.option; - SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc; -#ifdef SCC_DONT_CHECK - printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n", - device_name, - SCC_Info[2*Nchips+chan].data, - SCC_Info[2*Nchips+chan].ctrl); + /* initialize special hardware -- note that port & option need to be set at this point */ + if (ctrl->channel_a.special_port > 0) + { + if (ctrl->channel_a.brand & EAGLE) /* enable interrupt on EAGLE card */ + Outb(ctrl->channel_a.special_port, 0x08); + if (ctrl->channel_a.brand & (PC100 | PRIMUS)) /* set MODEM mode on PC100 or PRIMUS cards */ + Outb(ctrl->channel_a.special_port, ctrl->channel_a.special_option); + } -#else - printk(KERN_INFO "%s: data port = 0x%3.3lx control port = 0x%3.3lx -- %s\n", - device_name, - chan? hwcfg.data_b : hwcfg.data_a, - chan? hwcfg.ctrl_b : hwcfg.ctrl_a, - found? "found" : "missing"); -#endif - if (found) - { - request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl"); - request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data"); - if (Nchips+chan != 0) - scc_net_setup(&SCC_Info[2*Nchips+chan], device_name, 1); - } - } - - if (found) Nchips++; - - return 0; - } - - if (cmd == SIOCSCCINI) - { - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - - if (Nchips == 0) - return -EINVAL; + /* inhibit change of vital parameters after init; this probably isn't entirely race free + * free, but we can live with this (at worst the new mode hasn't been propagated to the VFS + * yet and we get an EINVAL though the file seems to be writable. *shrugs* */ - z8530_init(); - return 0; + for (entry=0; ctrl->proc_tables.chip[entry].ctl_name; entry++) + { + switch(ctrl->proc_tables.chip[entry].ctl_name) + { + case NET_DEV_Z8530DRV_IRQ: + case NET_DEV_Z8530DRV_ESCC: + case NET_DEV_Z8530DRV_SPECIAL_PORT: + case NET_DEV_Z8530DRV_SPECIAL_OPT: + ctrl->proc_tables.chip[entry].mode = 0444; + break; } - - return -EINVAL; /* confuse the user */ } - - if (!scc->init) + + t_port[0] = ctrl->channel_a.proc_tables.channel; + t_port[1] = ctrl->channel_b.proc_tables.channel; + + for (port=0; port < 2; port++) { - if (cmd == SIOCSCCCHANINI) + for (entry=0; t_port[port][entry].ctl_name; entry++) { - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (!arg) return -EINVAL; - - scc->stat.bufsize = SCC_BUFSIZE; - - if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem))) - return -EINVAL; - - /* default KISS Params */ - - if (scc->modem.speed < 4800) + switch(t_port[port][entry].ctl_name) { - scc->kiss.txdelay = 36; /* 360 ms */ - scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 16; /* 160 ms */ - scc->kiss.tailtime = 4; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50; /* 500 ms */ - scc->kiss.maxkeyup = 10; /* 10 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.softdcd = 0; /* hardware dcd */ - } else { - scc->kiss.txdelay = 10; /* 100 ms */ - scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 8; /* 160 ms */ - scc->kiss.tailtime = 1; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50; /* 500 ms */ - scc->kiss.maxkeyup = 7; /* 7 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.softdcd = 0; /* hardware dcd */ + case NET_DEV_Z8530DRV_DATA_PORT: + case NET_DEV_Z8530DRV_CTRL_PORT: + t_port[port][entry].mode = 0444; + break; } - - scc->tx_buff = NULL; - skb_queue_head_init(&scc->tx_queue); - scc->init = 1; - - return 0; } - - return -EINVAL; } - - switch(cmd) - { - case SIOCSCCRESERVED: - return -ENOIOCTLCMD; - - case SIOCSCCSMEM: - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg))) - return -EINVAL; - scc->stat.bufsize = memcfg.bufsize; - return 0; - - case SIOCSCCGSTAT: - if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat))) - return -EINVAL; - return 0; - - case SIOCSCCGKISS: - if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) - return -EINVAL; - kiss_cmd.param = scc_get_param(scc, kiss_cmd.command); - if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd))) - return -EINVAL; - return 0; - - case SIOCSCCSKISS: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) - return -EINVAL; - return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param); - - case SIOCSCCCAL: - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0) - return -EINVAL; - - scc_start_calibrate(scc, cal.time, cal.pattern); - return 0; - default: - return -ENOIOCTLCMD; - - } - - return -EINVAL; + return 0; } /* ----> set interface callsign <---- */ @@ -1980,7 +1609,7 @@ static int scc_net_set_mac_address(struct net_device *dev, void *addr) static struct net_device_stats *scc_net_get_stats(struct net_device *dev) { struct scc_channel *scc = (struct scc_channel *) dev->priv; - + scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over; scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under; scc->dev_stat.rx_fifo_errors = scc->stat.rx_over; @@ -1990,193 +1619,635 @@ static struct net_device_stats *scc_net_get_stats(struct net_device *dev) } /* ******************************************************************** */ -/* * dump statistics to /proc/net/z8530drv * */ +/* * /proc/sys/ interface * */ /* ******************************************************************** */ +/* + * structure: + * --------- + * + * /proc/sys/net/dev/z8530drv/ + * nchips + * delay + * chip_0/ + * vendor + * clock + * irq + * delay + * enhanced + * special_port + * special_opt + * port_a/ + * data_port + * ctrl_port + * ifname + * modem/ + * rx_speed + * tx_speed + * rx_clock_source + * tx_clock_source + * trx_pin_mode + * fullduplex + * trx_does_feedback + * nrz_mode + * tx_inhibit + * softdcd + * tx_delay + * tx_tail + * stats/ + * rx_ints + * tx_ints + * ex_ints + * sp_ints + * tx_frames + * rx_frames + * tx_errors + * rx_errors + * tx_underruns + * rx_overruns + * no_space + * tx_state + * port_b/ + * ... + * chip_1/ + * ... + * + * The idea is to be able to configure the driver through SNMP, store + * the configuration in an LDAP directory or place it into an XML + * document... The structure may look overly complex for the + * purpose, but the old "initialize through a special ioctl()" was too + * complex to maintain. + */ + +/* parent node /proc/sys/net/dev */ + +static ctl_table scc_proc_root_table[] = { + {NET_DEV, "dev", NULL, 0, 0555, scc_proc_parent_table}, + {0} +}; + +/* our directory */ + +static ctl_table scc_proc_parent_table[] = { + {NET_DEV_Z8530DRV, "z8530drv", NULL, 0, 0555, scc_proc_nchips_table}, + {0} +}; + +/* number of chips, subdirectories get added or removed dynamically */ -static int scc_net_get_info(char *buffer, char **start, off_t offset, int length) +static ctl_table scc_proc_nchips_table[] = { + {NET_DEV_Z8530DRV_NCHIPS, "nchips", + &Nchips, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_nchips, NULL}, + {NET_DEV_Z8530DRV_DELAY, "io_delay", + &IO_Delay, sizeof(int), 0644, NULL, + &proc_dointvec, &sysctl_intvec, NULL}, + {0} +}; + + +static void scc_proc_setup_channel(struct scc_ctrl *ctrl, struct scc_channel *scc) { - struct scc_channel *scc; - struct scc_kiss *kiss; - struct scc_stat *stat; - int len = 0; - off_t pos = 0; - off_t begin = 0; - int k; + int entry; + ctl_table *t_port = scc->proc_tables.channel; + ctl_table *t_modem = scc->proc_tables.modem; + ctl_table *t_stats = scc->proc_tables.stats; + + /* Set /.../chip_/channel_

/modem/ */ + + + /* Praise the Lord! We can do this with GCC! */ + entry=0; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_RX_SPEED, "rx_speed", + &scc->modem.rx_speed, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TX_SPEED, "tx_speed", + &scc->modem.tx_speed, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_RX_CLOCK_SOURCE, "rx_clock_source", + &scc->modem.rx_clock_source, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TX_CLOCK_SOURCE, "tx_clock_source", + &scc->modem.tx_clock_source, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TRXC_PIN_MODE, "trxc_pin_mode", + &scc->modem.trxc_pin_mode, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_FULLDUPLEX, "fullduplex", + &scc->modem.fullduplex, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TRX_FEEDBACK, "trx_feedback", + &scc->modem.trx_feedback, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_NRZ_MODE, "nrz_mode", + &scc->modem.nrz_mode, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TX_INHIBIT, "tx_inhibit", + &scc->modem.tx_inhibit, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_SOFTDCD, "softdcd", + &scc->modem.softdcd, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TX_DELAY, "tx_delay", + &scc->modem.tx_delay, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TX_TAIL, "tx_tail", + &scc->modem.tx_tail, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry] = (ctl_table) {0}; + + /* Set /.../chip_/channel_

/stats/ */ + entry = 0; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_RX_INTS, "rx_ints", + &scc->stat.rxints, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_TX_INTS, "tx_ints", + &scc->stat.txints, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_EX_INTS, "ex_ints", + &scc->stat.exints, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_TX_FRAMES, "tx_frames", + &scc->stat.txframes, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_RX_FRAMES, "rx_frames", + &scc->stat.rxframes, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_TX_ERRORS, "tx_errors", + &scc->stat.txerrs, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_RX_ERRORS, "rx_errors", + &scc->stat.rxerrs, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_TX_UNDERRUNS, "tx_underruns", + &scc->stat.tx_under, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_RX_OVERRUNS, "rx_overruns", + &scc->stat.rx_over, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_NO_SPACE, "no_space", + &scc->stat.nospace, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_TX_STATE, "tx_state", + &scc->stat.tx_state, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry] = (ctl_table) {0}; + + /* Set /.../chip_/channel_

/ */ + + entry = 0; + t_port[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_DATA_PORT, "data_port", + &scc->data, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_port, NULL, + (void *) ctrl, (void *) scc + }; + t_port[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_CTRL_PORT, "ctrl_port", + &scc->ctrl, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) ctrl, (void *) scc + }; + t_port[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_IFNAME, "ifname", + &scc->proc_dev_name, IFNAMSIZ, 0444, NULL, + &proc_dostring, &sysctl_string, NULL, + (void *) ctrl, (void *) scc + }; + t_port[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_BUFSIZE, "bufsize", + &scc->stat.bufsize, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_port, NULL, + (void *) ctrl, (void *) scc + }; + t_port[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_MODEM, "modem", + NULL, 0, 0555, t_modem + }; + t_port[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT, "stats", + NULL, 0, 0555, t_stats + }; + t_port[entry] = (ctl_table) {0}; + + memset(scc, 0, sizeof(struct scc_channel)); + scc->clock = SCC_DEFAULT_CLOCK; + scc->ctrl = -1; + scc->data = -1; + scc->special_port = -1; + scc->irq = -1; + scc->modem.rx_speed = 9600; + scc->modem.tx_speed = 9600; + scc->modem.tx_clock_source = CLOCK_SOURCE_RTxC; + scc->modem.rx_clock_source = CLOCK_SOURCE_TRxC; + scc->modem.trxc_pin_mode = TRXCP_MODE_IN; + spin_lock_init(&scc->spinlocks.hwaccess); + spin_lock_init(&scc->spinlocks.timer); + spin_lock_init(&scc->spinlocks.kick_tx); +} - len += sprintf(buffer, "z8530drv-"VERSION"\n"); - if (!Driver_Initialized) - { - len += sprintf(buffer+len, "not initialized\n"); - goto done; - } +static void scc_proc_setup_chip(void) +{ + int entry; + ctl_table *t_chip = scc_ctrl[Nchips]->proc_tables.chip; + struct scc_channel *channel_a = &scc_ctrl[Nchips]->channel_a; + struct scc_channel *channel_b = &scc_ctrl[Nchips]->channel_b; + + /* Set /.../chip_/ */ + + entry=0; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_VENDOR, "vendor", + &channel_a->brand, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) &scc_ctrl[Nchips] + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_CLOCK, "clock", + &channel_a->clock, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) &scc_ctrl[Nchips] + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_IRQ, "irq", + &channel_a->irq, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) &scc_ctrl[Nchips] + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_ESCC, "enhanced", + &channel_a->enhanced, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) &scc_ctrl[Nchips] + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_SPECIAL_PORT, "special_port", + &channel_a->special_port, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) &scc_ctrl[Nchips] + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_SPECIAL_OPT, "special_opt", + &channel_a->special_option, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) &scc_ctrl[Nchips] + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_CHANNEL_A, "channel_a", + NULL, 0, 0555, channel_a->proc_tables.channel + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_CHANNEL_B, "channel_b", + NULL, 0, 0555, channel_b->proc_tables.channel + }; + t_chip[entry] = (ctl_table) {0}; +} + +static void scc_proc_remove_channel(struct scc_ctrl *ctrl, struct scc_channel *scc) +{ + long flags; + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + + /* disable interrupts */ + cl(scc, R9, MIE); + udelay(50); - if (!Nchips) + /* free io ports */ + release_region(scc->ctrl, 1); + release_region(scc->data, 1); + if (scc->dev) { - len += sprintf(buffer+len, "chips missing\n"); - goto done; + unregister_netdev(scc->dev); + kfree(scc->dev); } - for (k = 0; k < Nchips*2; k++) - { - scc = &SCC_Info[k]; - stat = &scc->stat; - kiss = &scc->kiss; - - if (!scc->init) - continue; - - /* dev data ctrl irq clock brand enh vector special option - * baud nrz clocksrc softdcd bufsize - * rxints txints exints spints - * rcvd rxerrs over / xmit txerrs under / nospace bufsize - * txd pers slot tail ful wait min maxk idl defr txof grp - * W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## - * R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ## - */ - - len += sprintf(buffer+len, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n", - scc->dev->name, - scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand, - scc->enhanced, Vector_Latch, scc->special, - scc->option); - len += sprintf(buffer+len, "\t%lu %d %d %d %d\n", - scc->modem.speed, scc->modem.nrz, - scc->modem.clocksrc, kiss->softdcd, - stat->bufsize); - len += sprintf(buffer+len, "\t%lu %lu %lu %lu\n", - stat->rxints, stat->txints, stat->exints, stat->spints); - len += sprintf(buffer+len, "\t%lu %lu %d / %lu %lu %d / %d %d\n", - stat->rxframes, stat->rxerrs, stat->rx_over, - stat->txframes, stat->txerrs, stat->tx_under, - stat->nospace, stat->tx_state); - -#define K(x) kiss->x - len += sprintf(buffer+len, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n", - K(txdelay), K(persist), K(slottime), K(tailtime), - K(fulldup), K(waittime), K(mintime), K(maxkeyup), - K(idletime), K(maxdefer), K(tx_inhibit), K(group)); -#undef K -#ifdef SCC_DEBUG - { - int reg; + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); +} - len += sprintf(buffer+len, "\tW "); - for (reg = 0; reg < 16; reg++) - len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]); - len += sprintf(buffer+len, "\n"); - - len += sprintf(buffer+len, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1)); - for (reg = 3; reg < 8; reg++) - len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); - len += sprintf(buffer+len, "XX "); - for (reg = 9; reg < 16; reg++) - len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); - len += sprintf(buffer+len, "\n"); - } -#endif - len += sprintf(buffer+len, "\n"); +static void scc_proc_remove_chip(struct scc_ctrl *ctrl) +{ + long flags; + int irq; - pos = begin + len; + spin_lock_irqsave(&ctrl->channel_a.spinlocks.hwaccess, flags); - if (pos < offset) { - len = 0; - begin = pos; - } + Outb(ctrl->channel_a.ctrl, 0); + OutReg(ctrl->channel_a.ctrl, R9, FHWRES); - if (pos > offset + length) - break; + irq = ctrl->channel_a.irq; + if (--scc_irq_used[irq] <= 0) + { + free_irq(irq, NULL); + scc_irq_used[irq] = 0; /* paranoia */ } -done: + spin_unlock_irqrestore(&ctrl->channel_a.spinlocks.hwaccess, flags); - *start = buffer + (offset - begin); - len -= (offset - begin); + unregister_sysctl_table(scc_ctrl[Nchips]->proc_table_head); + kfree(scc_ctrl[Nchips]->proc_tables.dir[0].procname); + kfree(scc_ctrl[Nchips]); +} - if (len > length) len = length; - return len; -} +static int scc_proc_intvec_modem(ctl_table *table, int *name, int nlen, void *oldval, + size_t *oldlenp, void *newval, size_t newlen, void **context) +{ + int retval = 0; + struct scc_channel *scc = (struct scc_channel *) table->extra1; + if (scc == NULL || !scc->init) return -EINVAL; - -/* ******************************************************************** */ -/* * Init SCC driver * */ -/* ******************************************************************** */ + switch(table->ctl_name) + { + case NET_DEV_Z8530DRV_RX_SPEED: + case NET_DEV_Z8530DRV_TX_SPEED: + if (scc->stat.tx_state != TXS_ACTIVE) + set_speed(scc); + break; + case NET_DEV_Z8530DRV_RX_CLOCK_SOURCE: + case NET_DEV_Z8530DRV_TX_CLOCK_SOURCE: + case NET_DEV_Z8530DRV_TRXC_PIN_MODE: + wr(scc, R11, scc_calc_r11(scc->modem.rx_clock_source, scc->modem.tx_clock_source, scc->modem.trxc_pin_mode)); + scc_init_brg_and_dpll(scc); + break; + case NET_DEV_Z8530DRV_FULLDUPLEX: + case NET_DEV_Z8530DRV_TRX_FEEDBACK: + case NET_DEV_Z8530DRV_TX_INHIBIT: + case NET_DEV_Z8530DRV_TX_DELAY: + case NET_DEV_Z8530DRV_TX_TAIL: + break; + case NET_DEV_Z8530DRV_NRZ_MODE: + case NET_DEV_Z8530DRV_SOFTDCD: + break; + default: + retval = -EINVAL; + } + + scc_ddi_param_update(scc->dev); + return retval; +} -static int __init scc_init_driver (void) +static int scc_proc_intvec_port(ctl_table *table, int *name, int nlen, void *oldval, + size_t *oldlenp, void *newval, size_t newlen, void **context) { - int result; - char devname[10]; - - printk(banner); - - sprintf(devname,"%s0", SCC_DriverName); + struct scc_ctrl *ctrl = (struct scc_ctrl *) table->extra1; + struct scc_channel *scc = (struct scc_channel *) table->extra2; + int val; + + val = *((int *) newval); + + if (scc == NULL) return -EINVAL; - result = scc_net_setup(SCC_Info, devname, 0); - if (result) + switch(table->ctl_name) { - printk(KERN_ERR "z8530drv: cannot initialize module\n"); - return result; + case NET_DEV_Z8530DRV_DATA_PORT: + if (val > 0) + { + scc_check_and_init(ctrl); + return 1; + } + break; + case NET_DEV_Z8530DRV_CTRL_PORT: + if (val > 0) + { + scc_check_and_init(ctrl); + return 1; + } + break; + case NET_DEV_Z8530DRV_BUFSIZE: + break; } - proc_net_create("z8530drv", 0, scc_net_get_info); - return 0; } -static void __exit scc_cleanup_driver(void) +static int scc_proc_intvec_chip(ctl_table *table, int *name, int nlen, void *oldval, + size_t *oldlenp, void *newval, size_t newlen, void **context) { - long flags; - io_port ctrl; - int k; - struct scc_channel *scc; - - save_flags(flags); - cli(); + struct scc_ctrl *ctrl = (struct scc_ctrl *) table->extra1; + int val; + + if (ctrl == NULL) return -EINVAL; + if (ctrl->channel_a.init || ctrl->channel_b.init) + return -EINVAL; - if (Nchips == 0) + val = *((int *) newval); + + switch(table->ctl_name) { - unregister_netdev(SCC_Info[0].dev); - kfree(SCC_Info[0].dev); + case NET_DEV_Z8530DRV_VENDOR: + case NET_DEV_Z8530DRV_CLOCK: + case NET_DEV_Z8530DRV_ESCC: + case NET_DEV_Z8530DRV_SPECIAL_PORT: + case NET_DEV_Z8530DRV_SPECIAL_OPT: + break; + case NET_DEV_Z8530DRV_IRQ: + if (val > 0) + { + ctrl->channel_a.irq = val; + ctrl->channel_b.irq = val; + scc_check_and_init(ctrl); + return 1; + } + break; } - for (k = 0; k < Nchips; k++) - if ( (ctrl = SCC_ctrl[k].chan_A) ) - { - Outb(ctrl, 0); - OutReg(ctrl,R9,FHWRES); /* force hardware reset */ - udelay(50); - } - - for (k = 0; k < Nchips*2; k++) + return 0; +} + +static int scc_proc_intvec_nchips(ctl_table *table, int *name, int nlen, void *oldval, + size_t *oldlenp, void *newval, size_t newlen, void **context) +{ + struct scc_ctrl_proc_tables *tables = NULL; + struct scc_ctrl *ctrl = NULL; + int chip; + char *dirname; + int new_nchips, old_nchips; + + new_nchips = *((int *) newval); + old_nchips = *((int *) oldval); + + /* sanity checks...*/ + + if (new_nchips < 0 || new_nchips > maxchips) + return -EINVAL; + + /* adding chips */ + if (new_nchips > old_nchips) { - scc = &SCC_Info[k]; - if (scc->ctrl) + for (chip=0; chip < new_nchips - old_nchips; chip++) { - release_region(scc->ctrl, 1); - release_region(scc->data, 1); + ctrl = (struct scc_ctrl *) kmalloc(sizeof(struct scc_ctrl), GFP_KERNEL); + if (ctrl == NULL) return -ENOMEM; + scc_ctrl[Nchips] = ctrl; + memset(ctrl, 0, sizeof(struct scc_ctrl)); + + tables = &ctrl->proc_tables; + scc_proc_setup_channel(ctrl, &ctrl->channel_a); + scc_proc_setup_channel(ctrl, &ctrl->channel_b); + scc_proc_setup_chip(); + + /* !!FIXME!! free memory when removing a chip */ + dirname = kmalloc(sizeof(char)*IFNAMSIZ, GFP_KERNEL); + if (dirname == NULL) return -ENOMEM; + + sprintf(dirname, "chip_%d", Nchips); + tables->dir[0] = (ctl_table) { + NET_DEV_Z8530DRV_CHIP_BASE+Nchips, dirname, + NULL, 0, 0555, tables->chip + }; + tables->dir[1] = (ctl_table) {0}; + + tables->parent[0] = (ctl_table) { + NET_DEV_Z8530DRV, "z8530drv", + NULL, 0, 0555, tables->dir + }; + tables->parent[1] = (ctl_table) {0}; + + /* add return value to scc structure */ + scc_ctrl[Nchips]->proc_table_head = register_sysctl_table(tables->parent, 1); + Nchips++; } - if (scc->dev) + + *((int *) newval) = Nchips; + } + else if (new_nchips < old_nchips) + { + for(chip=0; chip < old_nchips - new_nchips; chip++) { - unregister_netdev(scc->dev); - kfree(scc->dev); + ctrl = scc_ctrl[Nchips-1]; + + /* !!FIXME!! possible race condition here */ + + if (netif_running(ctrl->channel_a.dev) || + netif_running(ctrl->channel_b.dev)) + break; + + scc_proc_remove_channel(ctrl, &ctrl->channel_a); + scc_proc_remove_channel(ctrl, &ctrl->channel_b); + scc_proc_remove_chip(ctrl); + + Nchips--; } + + *((int *) newval) = Nchips; } - for (k=0; k < 16 ; k++) - if (Ivec[k].used) free_irq(k, NULL); - - if (Vector_Latch) - release_region(Vector_Latch, 1); + return 0; +} - restore_flags(flags); + +/* ******************************************************************** */ +/* * Init SCC driver * */ +/* ******************************************************************** */ + + +int __init scc_init_driver(void) +{ + int k; + size_t size; + + printk(KERN_INFO BANNER); + + size=sizeof(struct scc_ctrl)*(maxchips+1); + scc_ctrl = kmalloc(size, GFP_KERNEL); + if (scc_ctrl == NULL) return -ENOMEM; + memset(scc_ctrl, 0, size); + + for (k = 0; k < 16; k++) scc_irq_used[k] = 0; + +#ifdef CONFIG_PROC_FS + scc_proc_table_header = register_sysctl_table(scc_proc_root_table, 1); +#endif + + return 0; +} + +/* ******************************************************************** */ +/* * Module support * */ +/* ******************************************************************** */ + + +void __exit scc_cleanup_driver(void) +{ + int k; + for (k = 0; k < Nchips; k++) + { + scc_proc_remove_channel(scc_ctrl[k], &scc_ctrl[k]->channel_a); + scc_proc_remove_channel(scc_ctrl[k], &scc_ctrl[k]->channel_b); + scc_proc_remove_chip(scc_ctrl[k]); + } + +#ifdef CONFIG_PROC_FS + unregister_sysctl_table(scc_proc_table_header); proc_net_remove("z8530drv"); +#endif + + kfree(scc_ctrl); } MODULE_AUTHOR("Joerg Reuter "); -MODULE_DESCRIPTION("AX.25 Device Driver for Z8530 based HDLC cards"); -MODULE_SUPPORTED_DEVICE("Z8530 based SCC cards for Amateur Radio"); +MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio"); +MODULE_SUPPORTED_DEVICE("Z8530 based SCC cards without DMA support"); +MODULE_PARM(maxchips, "i"); module_init(scc_init_driver); module_exit(scc_cleanup_driver); -- cgit v1.2.3