Index: linux/drivers/net/hamradio/Config.in diff -u linux/drivers/net/hamradio/Config.in:1.1.2.1 linux/drivers/net/hamradio/Config.in:1.1.2.1.2.1 --- linux/drivers/net/hamradio/Config.in:1.1.2.1 Sun Jul 4 10:15:05 1999 +++ linux/drivers/net/hamradio/Config.in Sun Jul 4 11:07:21 1999 @@ -1,6 +1,7 @@ comment 'AX.25 network device drivers' -dep_tristate 'Serial port KISS driver' CONFIG_MKISS $CONFIG_AX25 +dep_tristate 'Serial port KISS driver (old)' CONFIG_MKISS $CONFIG_AX25 +dep_tristate 'Serial port KISS driver (new)' CONFIG_KISS $CONFIG_AX25 dep_tristate 'Serial port 6PACK driver' CONFIG_6PACK $CONFIG_AX25 dep_tristate 'BPQ Ethernet driver' CONFIG_BPQETHER $CONFIG_AX25 Index: linux/drivers/net/hamradio/Makefile diff -u linux/drivers/net/hamradio/Makefile:1.1.2.1 linux/drivers/net/hamradio/Makefile:1.1.2.1.2.3 --- linux/drivers/net/hamradio/Makefile:1.1.2.1 Sun Jul 4 10:15:05 1999 +++ linux/drivers/net/hamradio/Makefile Wed Jul 7 00:59:24 1999 @@ -45,6 +45,14 @@ endif endif +ifeq ($(CONFIG_KISS),y) +L_OBJS += kiss.o +else + ifeq ($(CONFIG_KISS),m) + M_OBJS += kiss.o + endif +endif + ifeq ($(CONFIG_6PACK),y) L_OBJS += 6pack.o else Index: linux/drivers/net/hamradio/baycom_epp.c diff -u linux/drivers/net/hamradio/baycom_epp.c:1.1.2.1 linux/drivers/net/hamradio/baycom_epp.c:1.1.2.1.2.1 --- linux/drivers/net/hamradio/baycom_epp.c:1.1.2.1 Sun Jul 4 10:15:05 1999 +++ linux/drivers/net/hamradio/baycom_epp.c Sun Jul 4 11:07:21 1999 @@ -1,10 +1,9 @@ /*****************************************************************************/ /* - * baycom_epp.c -- baycom epp radio modem driver. + * baycom_epp.c -- baycom epp adaptor driver. * - * Copyright (C) 1998 - * Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,23 +24,23 @@ * authority of your country. * * - * History: - * 0.1 xx.xx.98 Initial version by Matthias Welwarsky (dg2fef) - * 0.2 21.04.98 Massive rework by Thomas Sailer - * Integrated FPGA EPP modem configuration routines - * 0.3 11.05.98 Took FPGA config out and moved it into a separate program + * Supported modems * + * History: + * 0.1 26.06.96 Adapted from baycom.c and made network driver interface + * 18.10.96 Changed to new user space access routines (copy_{to,from}_user) + * 0.3 26.04.97 init code/data tagged + * 0.4 08.07.97 alternative ser12 decoding algorithm (uses delta CTS ints) + * 0.5 11.11.97 split into separate files for ser12/par96 */ /*****************************************************************************/ -#include #include #include -#include -#include -#include #include +#include +#include #include #include #include @@ -49,24 +48,15 @@ #include #include #include +#include #include -#include #include #include #include -#include -//#include -#include +#include #include #include -#include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -/* prototypes for ax25_encapsulate and ax25_rebuild_header */ -#include -#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */ - -#define __KERNEL_SYSCALLS__ -#include +#include /* --------------------------------------------------------------------- */ @@ -91,7 +81,7 @@ #define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) #define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) -extern inline int copy_from_user(void *to, const void *from, unsigned long n) +extern __inline__ int copy_from_user(void *to, const void *from, unsigned long n) { int i = verify_area(VERIFY_READ, from, n); if (i) @@ -100,7 +90,7 @@ return 0; } -extern inline int copy_to_user(void *to, const void *from, unsigned long n) +extern __inline__ int copy_to_user(void *to, const void *from, unsigned long n) { int i = verify_area(VERIFY_WRITE, to, n); if (i) @@ -121,34 +111,12 @@ /* --------------------------------------------------------------------- */ #define BAYCOM_DEBUG -#define BAYCOM_MAGIC 19730510 /* --------------------------------------------------------------------- */ -static const char paranoia_str[] = KERN_ERR -"baycom_epp: bad magic number for hdlcdrv_state struct in routine %s\n"; - -#define baycom_paranoia_check(dev,routine,retval) \ -({ \ - if (!dev || !dev->priv || ((struct baycom_state *)dev->priv)->magic != BAYCOM_MAGIC) { \ - printk(paranoia_str, routine); \ - return retval; \ - } \ -}) - -#define baycom_paranoia_check_void(dev,routine) \ -({ \ - if (!dev || !dev->priv || ((struct baycom_state *)dev->priv)->magic != BAYCOM_MAGIC) { \ - printk(paranoia_str, routine); \ - return; \ - } \ -}) - -/* --------------------------------------------------------------------- */ - static const char bc_drvname[] = "baycom_epp"; -static const char bc_drvinfo[] = KERN_INFO "baycom_epp: (C) 1998 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "baycom_epp: version 0.3 compiled " __TIME__ " " __DATE__ "\n"; +static const char bc_drvinfo[] = KERN_INFO "baycom_epp: (C) 1998 Matthias Welwarsky DG2FEF/KC5RFB\n" +KERN_INFO "baycom_epp: version 0.5 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ @@ -163,58 +131,7 @@ /* --------------------------------------------------------------------- */ -/* EPP status register */ -#define EPP_DCDBIT 0x80 -#define EPP_PTTBIT 0x08 -#define EPP_NREF 0x01 -#define EPP_NRAEF 0x02 -#define EPP_NRHF 0x04 -#define EPP_NTHF 0x20 -#define EPP_NTAEF 0x10 -#define EPP_NTEF EPP_PTTBIT - -/* EPP control register */ -#define EPP_TX_FIFO_ENABLE 0x10 -#define EPP_RX_FIFO_ENABLE 0x08 -#define EPP_MODEM_ENABLE 0x20 -#define EPP_LEDS 0xC0 -#define EPP_IRQ_ENABLE 0x10 -/* LPT registers */ -#define LPTREG_ECONTROL 0x402 -#define LPTREG_CONFIGB 0x401 -#define LPTREG_CONFIGA 0x400 -#define LPTREG_EPPDATA 0x004 -#define LPTREG_EPPADDR 0x003 -#define LPTREG_CONTROL 0x002 -#define LPTREG_STATUS 0x001 -#define LPTREG_DATA 0x000 - -/* LPT control register */ -#define LPTCTRL_PROGRAM 0x04 /* 0 to reprogram */ -#define LPTCTRL_WRITE 0x01 -#define LPTCTRL_ADDRSTB 0x08 -#define LPTCTRL_DATASTB 0x02 -#define LPTCTRL_INTEN 0x10 - -/* LPT status register */ -#define LPTSTAT_SHIFT_NINTR 6 -#define LPTSTAT_WAIT 0x80 -#define LPTSTAT_NINTR (1< (b)) ? (a) : (b)) /* --------------------------------------------------------------------- */ - -#define KISS_VERBOSE -/* --------------------------------------------------------------------- */ - -#define PARAM_TXDELAY 1 -#define PARAM_PERSIST 2 -#define PARAM_SLOTTIME 3 -#define PARAM_TXTAIL 4 -#define PARAM_FULLDUP 5 -#define PARAM_HARDWARE 6 -#define PARAM_RETURN 255 - -/* --------------------------------------------------------------------- */ -/* - * the CRC routines are stolen from WAMPES - * by Dieter Deyke - */ - -static const unsigned short crc_ccitt_table[] = { - 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, - 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, - 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, - 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, - 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, - 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, - 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, - 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, - 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, - 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, - 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, - 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, - 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, - 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, - 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, - 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, - 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, - 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, - 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, - 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, - 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, - 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, - 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, - 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, - 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, - 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, - 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, - 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, - 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, - 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, - 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, - 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 -}; - -/*---------------------------------------------------------------------------*/ - -#if 0 -extern inline void append_crc_ccitt(unsigned char *buffer, int len) +static void __inline__ baycom_int_freq(struct baycom_state *bc) { - unsigned int crc = 0xffff; - - for (;len>0;len--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff]; - crc ^= 0xffff; - *buffer++ = crc; - *buffer++ = crc >> 8; -} -#endif - -/*---------------------------------------------------------------------------*/ - -extern inline int check_crc_ccitt(const unsigned char *buf, int cnt) -{ - unsigned int crc = 0xffff; - - for (; cnt > 0; cnt--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff]; - return (crc & 0xffff) == 0xf0b8; -} - -/*---------------------------------------------------------------------------*/ - -extern inline int calc_crc_ccitt(const unsigned char *buf, int cnt) -{ - unsigned int crc = 0xffff; - - for (; cnt > 0; cnt--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff]; - crc ^= 0xffff; - return (crc & 0xffff); -} - -/* ---------------------------------------------------------------------- */ - -#define tenms_to_flags(bc,tenms) ((tenms * bc->bitrate) / 800) - -/* --------------------------------------------------------------------- */ - -static void inline baycom_int_freq(struct baycom_state *bc) -{ #ifdef BAYCOM_DEBUG unsigned long cur_jiffies = jiffies; /* @@ -428,583 +226,180 @@ #endif /* BAYCOM_DEBUG */ } -/* ---------------------------------------------------------------------- */ +/* --------------------------------------------------------------------- */ /* - * eppconfig_path should be setable via /proc/sys. + * ===================== EPP specific routines ========================= + * + * EPP adapter specific stuff written by Matthias Welwarsky (DG2FEF). This + * routines contain code and definitions from the FlexNet EPP adapter + * driver written by Gunter Jost ((D)K7WJ). */ - -char eppconfig_path[256] = "/sbin/eppfpga"; -static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL }; +#define EPP_P_STAT(bc) bc->modem.epp.port_stat +#define EPP_P_MIRR(bc) bc->modem.epp.port_mirror -static int errno; +/* --------------------------------------------------------------------- */ -static int exec_eppfpga(void *b) +static unsigned int epp_getptt(struct device* dev) { - struct baycom_state *bc = (struct baycom_state *)b; - char modearg[256]; - char portarg[16]; - char *argv[] = { eppconfig_path, "-s", "-p", portarg, "-m", modearg, NULL}; - int i; - - /* set up arguments */ - sprintf(modearg, "%sclk,%smodem,divider=%d%s,extstat", - bc->cfg.intclk ? "int" : "ext", - bc->cfg.extmodem ? "ext" : "int", bc->cfg.divider, - bc->cfg.loopback ? ",loopback" : ""); - sprintf(portarg, "%ld", bc->pdev->port->base); - printk(KERN_DEBUG "%s: %s -s -p %s -m %s\n", bc_drvname, eppconfig_path, portarg, modearg); - - for (i = 0; i < current->files->max_fds; i++ ) - if (current->files->fd[i]) - close(i); - set_fs(KERNEL_DS); /* Allow execve args to be in kernel space. */ - current->uid = current->euid = current->fsuid = 0; - if (execve(eppconfig_path, argv, envp) < 0) { - printk(KERN_ERR "%s: failed to exec %s -s -p %s -m %s, errno = %d\n", - bc_drvname, eppconfig_path, portarg, modearg, errno); - return -errno; - } - return 0; + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + return EPP_P_STAT(bc) & EPP_PTTBIT; } - -/* eppconfig: called during ifconfig up to configure the modem */ +/* --------------------------------------------------------------------- */ -static int eppconfig(struct baycom_state *bc) +static unsigned int epp_getdcd(struct device* dev) { - int i, pid, r; - mm_segment_t fs; + struct baycom_state *bc = (struct baycom_state *)dev->priv; - pid = kernel_thread(exec_eppfpga, bc, CLONE_FS); - if (pid < 0) { - printk(KERN_ERR "%s: fork failed, errno %d\n", bc_drvname, -pid); - return pid; - } - fs = get_fs(); - set_fs(KERNEL_DS); /* Allow i to be in kernel space. */ - r = waitpid(pid, &i, __WCLONE); - set_fs(fs); - if (r != pid) { - printk(KERN_ERR "%s: waitpid(%d) failed, returning %d\n", - bc_drvname, pid, r); - return -1; - } - printk(KERN_DEBUG "%s: eppfpga returned %d\n", bc_drvname, i); - return i; + return !(EPP_P_STAT(bc) & EPP_DCDBIT); +/* return bc->modem.epp.dcd_delay; */ } - -/* ---------------------------------------------------------------------- */ -static void epp_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ -} - -/* ---------------------------------------------------------------------- */ +/* --------------------------------------------------------------------- */ -static void inline do_kiss_params(struct baycom_state *bc, - unsigned char *data, unsigned long len) +static void epp_dev_init(struct device *dev, struct parport *pp, int bitrate) { - -#ifdef KISS_VERBOSE -#define PKP(a,b) printk(KERN_INFO "%s: channel params: " a "\n", bc->ifname, b) -#else /* KISS_VERBOSE */ -#define PKP(a,b) -#endif /* KISS_VERBOSE */ - - if (len < 2) - return; - switch(data[0]) { - case PARAM_TXDELAY: - bc->ch_params.tx_delay = data[1]; - PKP("TX delay = %ums", 10 * bc->ch_params.tx_delay); - break; - case PARAM_PERSIST: - bc->ch_params.ppersist = data[1]; - PKP("p persistence = %u", bc->ch_params.ppersist); - break; - case PARAM_SLOTTIME: - bc->ch_params.slottime = data[1]; - PKP("slot time = %ums", bc->ch_params.slottime); - break; - case PARAM_TXTAIL: - bc->ch_params.tx_tail = data[1]; - PKP("TX tail = %ums", bc->ch_params.tx_tail); - break; - case PARAM_FULLDUP: - bc->ch_params.fulldup = !!data[1]; - PKP("%s duplex", bc->ch_params.fulldup ? "full" : "half"); - break; - default: - break; - } -#undef PKP -} - -/* --------------------------------------------------------------------- */ -/* - * high performance HDLC encoder - * yes, it's ugly, but generates pretty good code - */ + struct baycom_state *bc = (struct baycom_state *)dev->priv; + int burstscale; -#define ENCODEITERA(j) \ -({ \ - if (!(notbitstream & (0x1f0 << j))) \ - goto stuff##j; \ - encodeend##j: \ -}) - -#define ENCODEITERB(j) \ -({ \ - stuff##j: \ - bitstream &= ~(0x100 << j); \ - bitbuf = (bitbuf & (((2 << j) << numbit) - 1)) | \ - ((bitbuf & ~(((2 << j) << numbit) - 1)) << 1); \ - numbit++; \ - notbitstream = ~bitstream; \ - goto encodeend##j; \ -}) + /* prepare EPP mode, disable interrupts for now */ + parport_write_control(pp, 0); + bc->hdrv.par.bitrate = bitrate; + ax25_dev_setbitrate(dev, bitrate); + ax25_dev_setpttfunc(dev, epp_getptt); + ax25_dev_setdcdfunc(dev, epp_getdcd); + + if (bitrate > 76800) + burstscale = 7; + else if (bitrate <= 19200) + burstscale = 5; + else + burstscale = 6; -static void encode_hdlc(struct baycom_state *bc) -{ - struct sk_buff *skb; - unsigned char *wp, *bp; - int pkt_len; - unsigned bitstream, notbitstream, bitbuf, numbit, crc; - unsigned char crcarr[2]; - - if (bc->hdlctx.bufcnt > 0) - return; - while ((skb = skb_dequeue(&bc->send_queue))) { - if (skb->data[0] != 0) { - do_kiss_params(bc, skb->data, skb->len); - dev_kfree_skb(skb); - continue; - } - pkt_len = skb->len-1; /* strip KISS byte */ - if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) { - dev_kfree_skb(skb); - continue; - } - wp = bc->hdlctx.buf; - bp = skb->data+1; - crc = calc_crc_ccitt(bp, pkt_len); - crcarr[0] = crc; - crcarr[1] = crc >> 8; - *wp++ = 0x7e; - bitstream = bitbuf = numbit = 0; - while (pkt_len > -2) { - bitstream >>= 8; - bitstream |= ((unsigned int)*bp) << 8; - bitbuf |= ((unsigned int)*bp) << numbit; - notbitstream = ~bitstream; - bp++; - pkt_len--; - if (!pkt_len) - bp = crcarr; - ENCODEITERA(0); - ENCODEITERA(1); - ENCODEITERA(2); - ENCODEITERA(3); - ENCODEITERA(4); - ENCODEITERA(5); - ENCODEITERA(6); - ENCODEITERA(7); - goto enditer; - ENCODEITERB(0); - ENCODEITERB(1); - ENCODEITERB(2); - ENCODEITERB(3); - ENCODEITERB(4); - ENCODEITERB(5); - ENCODEITERB(6); - ENCODEITERB(7); - enditer: - numbit += 8; - while (numbit >= 8) { - *wp++ = bitbuf; - bitbuf >>= 8; - numbit -= 8; - } - } - bitbuf |= 0x7e7e << numbit; - numbit += 16; - while (numbit >= 8) { - *wp++ = bitbuf; - bitbuf >>= 8; - numbit -= 8; - } - bc->hdlctx.bufptr = bc->hdlctx.buf; - bc->hdlctx.bufcnt = wp - bc->hdlctx.buf; - dev_kfree_skb(skb); - bc->stats.tx_packets++; - return; - } -} + bc->modem.epp.bytecnt = 1 << burstscale; + bc->modem.epp.burstscale = burstscale; -/* ---------------------------------------------------------------------- */ + /* initialise the hardware */ + EPP_P_MIRR(bc) = burstscale+1; + parport_epp_write_addr(pp, EPP_P_MIRR(bc)); + EPP_P_MIRR(bc) |= (EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE); + parport_epp_write_addr(pp, EPP_P_MIRR(bc)); -static unsigned short random_seed; + /* enable interrupts */ + parport_write_control(pp, LPTCTRL_INTEN); -static inline unsigned short random_num(void) -{ - random_seed = 28629 * random_seed + 157; - return random_seed; } -/* ---------------------------------------------------------------------- */ +/* --------------------------------------------------------------------- */ -static void transmit(struct baycom_state *bc, int cnt, unsigned char stat) +static __inline__ void epp_rx(struct device *dev, struct baycom_state *bc) { + int cnt; + static unsigned int hdlc_data; + static unsigned char rxbyte; + static int complete = 0; struct parport *pp = bc->pdev->port; - int i; - if (bc->hdlctx.state == tx_tail && !(stat & EPP_PTTBIT)) - bc->hdlctx.state = tx_idle; - if (bc->hdlctx.state == tx_idle && bc->hdlctx.calibrate <= 0) { - if (bc->hdlctx.bufcnt <= 0) - encode_hdlc(bc); - if (bc->hdlctx.bufcnt <= 0) - return; - if (!bc->ch_params.fulldup) { - if (!(stat & EPP_DCDBIT)) { - bc->hdlctx.slotcnt = bc->ch_params.slottime; - return; - } - if ((--bc->hdlctx.slotcnt) > 0) - return; - bc->hdlctx.slotcnt = bc->ch_params.slottime; - if ((random_num() % 256) > bc->ch_params.ppersist) - return; - } - } - if (bc->hdlctx.state == tx_idle && bc->hdlctx.bufcnt > 0) { - bc->hdlctx.state = tx_keyup; - bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_delay); - bc->ptt_keyed++; - } - while (cnt > 0) { - switch (bc->hdlctx.state) { - case tx_keyup: - i = min(cnt, bc->hdlctx.flags); - cnt -= i; - bc->hdlctx.flags -= i; - if (bc->hdlctx.flags <= 0) - bc->hdlctx.state = tx_data; - for (; i > 0; i--) - parport_epp_write_data(pp, 0x7e); - break; - - case tx_data: - if (bc->hdlctx.bufcnt <= 0) { - encode_hdlc(bc); - if (bc->hdlctx.bufcnt <= 0) { - bc->hdlctx.state = tx_tail; - bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_tail); + EPP_P_STAT(bc) = parport_epp_read_addr(pp); + if (parport_epp_check_timeout(pp)) { + printk(KERN_DEBUG "epp_rx: epp timeout, reinit.\n"); + epp_dev_init(dev, pp, 76800); + } + if (bc->modem.epp.burstscale <= 5 || (EPP_P_STAT(bc) & EPP_RXAEBIT) || + !(EPP_P_STAT(bc) & EPP_RXHFULL)) + { + for (cnt = bc->modem.epp.bytecnt+8; cnt; cnt--) { + if (bc->modem.epp.burstscale <= 5) { + EPP_P_STAT(bc) = parport_epp_read_addr(pp); + if (!(EPP_P_STAT(bc) & EPP_RXEBIT)) break; - } - } - i = min(cnt, bc->hdlctx.bufcnt); - bc->hdlctx.bufcnt -= i; - cnt -= i; - for (; i > 0; i--) - parport_epp_write_data(pp, *(bc->hdlctx.bufptr)++); - break; - - case tx_tail: - encode_hdlc(bc); - if (bc->hdlctx.bufcnt > 0) { - bc->hdlctx.state = tx_data; - break; - } - i = min(cnt, bc->hdlctx.flags); - if (i) { - cnt -= i; - bc->hdlctx.flags -= i; - for (; i > 0; i--) - parport_epp_write_data(pp, 0x7e); - break; } - - default: /* fall through */ - if (bc->hdlctx.calibrate <= 0) - return; - i = min(cnt, bc->hdlctx.calibrate); - cnt -= i; - bc->hdlctx.calibrate -= i; - for (; i > 0; i--) - parport_epp_write_data(pp, 0); - break; + rxbyte = parport_epp_read_data(pp); + hdlc_data >>= 8; + hdlc_data |= rxbyte << 8; + if (complete) { + hdlcdrv_putbits(&bc->hdrv, hdlc_data); + complete = 0; + } else + complete = 1; } } } - -/* ---------------------------------------------------------------------- */ - -static void do_rxpacket(struct device *dev) -{ - struct baycom_state *bc = (struct baycom_state *)dev->priv; - struct sk_buff *skb; - unsigned char *cp; - unsigned pktlen; - if (bc->hdlcrx.bufcnt < 4) - return; - if (!check_crc_ccitt(bc->hdlcrx.buf, bc->hdlcrx.bufcnt)) - return; - pktlen = bc->hdlcrx.bufcnt-2+1; /* KISS kludge */ - if (!(skb = dev_alloc_skb(pktlen))) { - printk("%s: memory squeeze, dropping packet\n", bc->ifname); - bc->stats.rx_dropped++; - return; - } - skb->dev = dev; - cp = skb_put(skb, pktlen); - *cp++ = 0; /* KISS kludge */ - memcpy(cp, bc->hdlcrx.buf, pktlen - 1); - skb->protocol = htons(ETH_P_AX25); - skb->mac.raw = skb->data; - netif_rx(skb); - bc->stats.rx_packets++; -} +/* --------------------------------------------------------------------- */ -#define DECODEITERA(j) \ -({ \ - if (!(notbitstream & (0x0fc << j))) /* flag or abort */ \ - goto flgabrt##j; \ - if ((bitstream & (0x1f8 << j)) == (0xf8 << j)) /* stuffed bit */ \ - goto stuff##j; \ - enditer##j: \ -}) - -#define DECODEITERB(j) \ -({ \ - flgabrt##j: \ - if (!(notbitstream & (0x1fc << j))) { /* abort received */ \ - state = 0; \ - goto enditer##j; \ - } \ - if ((bitstream & (0x1fe << j)) != (0x0fc << j)) /* flag received */ \ - goto enditer##j; \ - if (state) \ - do_rxpacket(dev); \ - bc->hdlcrx.bufcnt = 0; \ - bc->hdlcrx.bufptr = bc->hdlcrx.buf; \ - state = 1; \ - numbits = 7-j; \ - goto enditer##j; \ - stuff##j: \ - numbits--; \ - bitbuf = (bitbuf & ((~0xff) << j)) | ((bitbuf & ~((~0xff) << j)) << 1); \ - goto enditer##j; \ -}) - -static void receive(struct device *dev, int cnt) +static __inline__ void epp_tx(struct device *dev, struct baycom_state *bc) { - struct baycom_state *bc = (struct baycom_state *)dev->priv; + int cnt; + unsigned int hdlc_data; + unsigned char txbyte; struct parport *pp = bc->pdev->port; - unsigned int bitbuf, notbitstream, bitstream, numbits, state; - unsigned char ch; - - numbits = bc->hdlcrx.numbits; - state = bc->hdlcrx.state; - bitstream = bc->hdlcrx.bitstream; - bitbuf = bc->hdlcrx.bitbuf; - for (; cnt > 0; cnt--) { - ch = parport_epp_read_data(pp); - bitstream >>= 8; - bitstream |= ch << 8; - bitbuf >>= 8; - bitbuf |= ch << 8; - numbits += 8; - notbitstream = ~bitstream; - DECODEITERA(0); - DECODEITERA(1); - DECODEITERA(2); - DECODEITERA(3); - DECODEITERA(4); - DECODEITERA(5); - DECODEITERA(6); - DECODEITERA(7); - goto enddec; - DECODEITERB(0); - DECODEITERB(1); - DECODEITERB(2); - DECODEITERB(3); - DECODEITERB(4); - DECODEITERB(5); - DECODEITERB(6); - DECODEITERB(7); - enddec: - while (state && numbits >= 8) { - if (bc->hdlcrx.bufcnt >= TXBUFFER_SIZE) { - state = 0; - } else { - *(bc->hdlcrx.bufptr)++ = bitbuf >> (16-numbits); - bc->hdlcrx.bufcnt++; - numbits -= 8; - } + + EPP_P_STAT(bc) = parport_epp_read_addr(pp); + if ((EPP_P_STAT(bc) & (EPP_NTAEF|EPP_NTHF)) == EPP_NTHF) { + cnt = bc->modem.epp.bytecnt; + + /* two bytes per cnt */ + while (cnt--) { + hdlc_data = hdlcdrv_getbits(&bc->hdrv); + txbyte = hdlc_data & 0xff; + parport_epp_write_data(pp, txbyte); + hdlc_data >>= 8; + txbyte = hdlc_data & 0xff; + parport_epp_write_data(pp, txbyte); } - } - bc->hdlcrx.numbits = numbits; - bc->hdlcrx.state = state; - bc->hdlcrx.bitstream = bitstream; - bc->hdlcrx.bitbuf = bitbuf; + } } /* --------------------------------------------------------------------- */ -#ifdef __i386__ -#define GETTICK(x) \ -({ \ - if (current_cpu_data.x86_capability & X86_FEATURE_TSC) \ - __asm__ __volatile__("rdtsc" : "=a" (x) : : "dx");\ -}) -#else /* __i386__ */ -#define GETTICK(x) -#endif /* __i386__ */ - static void epp_bh(struct device *dev) { - struct baycom_state *bc; - struct parport *pp; - unsigned char stat; - unsigned int time1 = 0, time2 = 0, time3 = 0; - int cnt, cnt2; - - baycom_paranoia_check_void(dev, "epp_bh"); - bc = (struct baycom_state *)dev->priv; - if (!bc->bh_running) - return; - baycom_int_freq(bc); - pp = bc->pdev->port; - /* update status */ - bc->stat = stat = parport_epp_read_addr(pp); - bc->debug_vals.last_pllcorr = stat; - GETTICK(time1); - if (bc->modem == EPP_FPGAEXTSTATUS) { - /* get input count */ - parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|1); - cnt = parport_epp_read_addr(pp); - cnt |= parport_epp_read_addr(pp) << 8; - cnt &= 0x7fff; - /* get output count */ - parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|2); - cnt2 = parport_epp_read_addr(pp); - cnt2 |= parport_epp_read_addr(pp) << 8; - cnt2 = 16384 - (cnt2 & 0x7fff); - /* return to normal */ - parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE); - transmit(bc, cnt2, stat); - GETTICK(time2); - receive(dev, cnt); - bc->stat = stat = parport_epp_read_addr(pp); - } else { - /* try to tx */ - switch (stat & (EPP_NTAEF|EPP_NTHF)) { - case EPP_NTHF: - cnt = 2048 - 256; - break; - - case EPP_NTAEF: - cnt = 2048 - 1793; - break; - - case 0: - cnt = 0; - break; - - default: - cnt = 2048 - 1025; - break; - } - transmit(bc, cnt, stat); - GETTICK(time2); - /* do receiver */ - while ((stat & (EPP_NRAEF|EPP_NRHF)) != EPP_NRHF) { - switch (stat & (EPP_NRAEF|EPP_NRHF)) { - case EPP_NRAEF: - cnt = 1025; - break; - - case 0: - cnt = 1793; - break; - - default: - cnt = 256; - break; - } - receive(dev, cnt); - stat = parport_epp_read_addr(pp); - if (parport_epp_check_timeout(pp)) - goto epptimeout; - } - cnt = 0; - if (bc->bitrate < 50000) - cnt = 256; - else if (bc->bitrate < 100000) - cnt = 128; - while (cnt > 0 && stat & EPP_NREF) { - receive(dev, 1); - cnt--; - stat = parport_epp_read_addr(pp); - } - } - GETTICK(time3); -#ifdef BAYCOM_DEBUG - bc->debug_vals.mod_cycles = time2 - time1; - bc->debug_vals.demod_cycles = time3 - time2; -#endif /* BAYCOM_DEBUG */ - if (parport_epp_check_timeout(pp)) - goto epptimeout; - queue_task(&bc->run_bh, &tq_timer); - return; - epptimeout: - printk(KERN_ERR "%s: EPP timeout!\n", bc_drvname); -} + struct baycom_state *bc = (struct baycom_state *)dev->priv; -/* ---------------------------------------------------------------------- */ -/* - * ===================== network driver interface ========================= - */ + hdlcdrv_transmitter(dev, &bc->hdrv); + hdlcdrv_receiver(dev, &bc->hdrv); -static int baycom_send_packet(struct sk_buff *skb, struct device *dev) -{ - struct baycom_state *bc; - - baycom_paranoia_check(dev, "baycom_send_packet", 0); - bc = (struct baycom_state *)dev->priv; - skb_queue_tail(&bc->send_queue, skb); - dev->trans_start = jiffies; - return 0; + bc->in_bh = 0; } /* --------------------------------------------------------------------- */ -static int baycom_set_mac_address(struct device *dev, void *addr) +static void epp_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - struct sockaddr *sa = (struct sockaddr *)addr; - - /* addr is an AX.25 shifted ASCII mac address */ - memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); - return 0; -} + struct device *dev = (struct device *)dev_id; + struct baycom_state *bc = (struct baycom_state *)dev->priv; -/* --------------------------------------------------------------------- */ + if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) + return; -static struct net_device_stats *baycom_get_stats(struct device *dev) -{ - struct baycom_state *bc; + baycom_int_freq(bc); + /* + * check if transmitter active + */ + if (hdlcdrv_ptt(&bc->hdrv)) + epp_tx(dev, bc); + epp_rx(dev, bc); - baycom_paranoia_check(dev, "baycom_get_stats", NULL); - bc = (struct baycom_state *)dev->priv; +#if 0 /* - * Get the current statistics. This may be called with the - * card open or closed. + * delay dcd */ - return &bc->stats; + if (!(EPP_P_STAT(bc) & EPP_DCDBIT)) + bc->modem.epp.dcd_delay = 5; + else if (bc->modem.epp.dcd_delay > 0) + bc->modem.epp.dcd_delay--; +#endif + if (!test_and_set_bit(0, &bc->in_bh)) { + bc->run_bh.routine = epp_bh; + bc->run_bh.data = dev; + bc->run_bh.sync = 0; + queue_task(&bc->run_bh, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } } /* --------------------------------------------------------------------- */ @@ -1012,65 +407,50 @@ static void epp_wakeup(void *handle) { struct device *dev = (struct device *)handle; - struct baycom_state *bc; + struct baycom_state *bc = (struct baycom_state *)dev->priv; - baycom_paranoia_check_void(dev, "epp_wakeup"); - bc = (struct baycom_state *)dev->priv; - printk(KERN_DEBUG "baycom_epp: %s: why am I being woken up?\n", dev->name); - if (!parport_claim(bc->pdev)) - printk(KERN_DEBUG "baycom_epp: %s: I'm broken.\n", dev->name); + printk(KERN_DEBUG "baycom_epp: %s: why am I being woken up?\n", dev->name); + if (!parport_claim(bc->pdev)) + printk(KERN_DEBUG "baycom_epp: %s: I'm broken.\n", dev->name); } /* --------------------------------------------------------------------- */ -/* - * Open/initialize 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 need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ - static int epp_open(struct device *dev) { - struct baycom_state *bc; - struct parport *pp; + struct baycom_state *bc = (struct baycom_state *)dev->priv; + struct parport *pp = parport_enumerate(); + const struct tq_struct run_bh = { 0, 0, (void *)(void *)epp_bh, dev }; - unsigned int i, j; - unsigned char stat; - unsigned long tstart; - - baycom_paranoia_check(dev, "epp_open", -ENXIO); - bc = (struct baycom_state *)dev->priv; - if (dev->start) - return 0; - pp = parport_enumerate(); - while (pp && pp->base != dev->base_addr) - pp = pp->next; - if (!pp) { - printk(KERN_ERR "%s: parport at 0x%lx unknown\n", bc_drvname, dev->base_addr); - return -ENXIO; - } -#if 0 - if (pp->irq < 0) { - printk(KERN_ERR "%s: parport at 0x%lx has no irq\n", bc_drvname, pp->base); - return -ENXIO; - } -#endif + + if (!dev || !bc) + return -ENXIO; + while (pp && pp->base != dev->base_addr) + pp = pp->next; + if (!pp) { + printk(KERN_ERR "baycom_epp: parport at 0x%lx unknown\n", dev->base_addr); + return -ENXIO; + } + if (pp->irq < 0) { + printk(KERN_ERR "baycom_epp: parport at 0x%lx has no irq\n", pp->base); + return -ENXIO; + } memset(&bc->modem, 0, sizeof(bc->modem)); - if (!(bc->pdev = parport_register_device(pp, dev->name, NULL, epp_wakeup, - epp_interrupt, PARPORT_DEV_EXCL, dev))) { - printk(KERN_ERR "%s: cannot register parport at 0x%lx\n", bc_drvname, pp->base); - return -ENXIO; - } - if (parport_claim(bc->pdev)) { - printk(KERN_ERR "%s: parport at 0x%lx busy\n", bc_drvname, pp->base); - parport_unregister_device(bc->pdev); - return -EBUSY; - } + + if (!(bc->pdev = parport_register_device(pp, dev->name, NULL, epp_wakeup, + epp_interrupt, PARPORT_DEV_EXCL, dev))) { + printk(KERN_ERR "baycom_epp: cannot register parport at 0x%lx\n", pp->base); + return -ENXIO; + } + if (parport_claim(bc->pdev)) { + printk(KERN_ERR "baycom_epp: parport at 0x%lx busy\n", pp->base); + parport_unregister_device(bc->pdev); + return -EBUSY; + } + +#if 0 /* FIXME: enable this again as soon as parport is fixed. */ if (!(pp->modes & (PARPORT_MODE_PCECPEPP|PARPORT_MODE_PCEPP))) { printk(KERN_ERR "%s: parport at 0x%lx does not support any EPP mode\n", bc_drvname, pp->base); @@ -1078,104 +458,41 @@ parport_unregister_device(bc->pdev); return -EIO; } - dev->irq = /*pp->irq*/ 0; +#endif + + dev->irq = pp->irq; bc->run_bh = run_bh; - bc->bh_running = 1; + if (pp->modes & PARPORT_MODE_PCECPEPP) { printk(KERN_INFO "%s: trying to enable EPP mode\n", bc_drvname); parport_frob_econtrol(pp, 0xe0, 0x80); } - /* bc->pdev->port->ops->change_mode(bc->pdev->port, PARPORT_MODE_PCEPP); not yet implemented */ - bc->modem = EPP_CONVENTIONAL; - if (eppconfig(bc)) - printk(KERN_INFO "%s: no FPGA detected, assuming conventional EPP modem\n", bc_drvname); - else - bc->modem = /*EPP_FPGA*/ EPP_FPGAEXTSTATUS; - parport_write_control(pp, LPTCTRL_PROGRAM); /* prepare EPP mode; we aren't using interrupts */ - /* reset the modem */ - parport_epp_write_addr(pp, 0); - parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE); - /* autoprobe baud rate */ - tstart = jiffies; - i = 0; - while ((signed)(jiffies-tstart-HZ/3) < 0) { - stat = parport_epp_read_addr(pp); - if ((stat & (EPP_NRAEF|EPP_NRHF)) == EPP_NRHF) { - schedule(); - continue; - } - for (j = 0; j < 256; j++) - parport_epp_read_data(pp); - i += 256; - } - for (j = 0; j < 256; j++) { - stat = parport_epp_read_addr(pp); - if (!(stat & EPP_NREF)) - break; - parport_epp_read_data(pp); - i++; - } - tstart = jiffies - tstart; - bc->bitrate = i * (8 * HZ) / tstart; - j = 1; - i = bc->bitrate >> 3; - while (j < 7 && i > 150) { - j++; - i >>= 1; - } - printk(KERN_INFO "%s: autoprobed bitrate: %d int divider: %d int rate: %d\n", - bc_drvname, bc->bitrate, j, bc->bitrate >> (j+2)); - parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE/*|j*/); - /* - * initialise hdlc variables - */ - bc->hdlcrx.state = 0; - bc->hdlcrx.numbits = 0; - bc->hdlctx.state = tx_idle; - bc->hdlctx.bufcnt = 0; - bc->hdlctx.slotcnt = bc->ch_params.slottime; - bc->hdlctx.calibrate = 0; - dev->start = 1; - dev->tbusy = 0; - dev->interrupt = 0; - /* start the bottom half stuff */ - queue_task(&bc->run_bh, &tq_timer); + + epp_dev_init(dev, pp, 76800); + + printk(KERN_INFO "%s: epp at iobase 0x%lx irq %u\n", + bc_drvname, dev->base_addr, dev->irq); MOD_INC_USE_COUNT; return 0; - -#if 0 - errreturn: - parport_release(bc->pdev); - parport_unregister_device(bc->pdev); - return -EIO; -#endif } /* --------------------------------------------------------------------- */ static int epp_close(struct device *dev) { - struct baycom_state *bc; - struct parport *pp; - struct sk_buff *skb; + struct baycom_state *bc = (struct baycom_state *)dev->priv; - baycom_paranoia_check(dev, "epp_close", -EINVAL); - if (!dev->start) - return 0; - bc = (struct baycom_state *)dev->priv; - pp = bc->pdev->port; - bc->bh_running = 0; - dev->start = 0; - dev->tbusy = 1; - run_task_queue(&tq_timer); /* dequeue bottom half */ - bc->stat = EPP_DCDBIT; - parport_epp_write_addr(pp, 0); - parport_write_control(pp, 0); /* reset the adapter */ - parport_release(bc->pdev); - parport_unregister_device(bc->pdev); - /* Free any buffers left in the hardware transmit queue */ - while ((skb = skb_dequeue(&bc->send_queue))) - dev_kfree_skb(skb); + if (!dev || !bc) + return -EINVAL; + + /* disable interrupt */ + parport_write_control(bc->pdev->port, 0); + + /* writing '0' to the address latch disables the adaptor */ + parport_epp_write_addr(bc->pdev->port, 0); + + parport_release(bc->pdev); + parport_unregister_device(bc->pdev); printk(KERN_INFO "%s: close epp at iobase 0x%lx irq %u\n", bc_drvname, dev->base_addr, dev->irq); MOD_DEC_USE_COUNT; @@ -1183,283 +500,132 @@ } /* --------------------------------------------------------------------- */ +/* + * ===================== hdlcdrv driver interface ========================= + */ + +static int baycom_ioctl(struct device *dev, struct ifreq *ifr, + struct hdlcdrv_ioctl *hi, int cmd); + +/* --------------------------------------------------------------------- */ +static struct hdlcdrv_ops epp_ops = { + bc_drvname, + bc_drvinfo, + epp_open, + epp_close, + baycom_ioctl +}; + +/* --------------------------------------------------------------------- */ + static int baycom_setmode(struct baycom_state *bc, const char *modestr) { - const char *cp; - - if (strstr(modestr,"intclk")) - bc->cfg.intclk = 1; - if (strstr(modestr,"extclk")) - bc->cfg.intclk = 0; - if (strstr(modestr,"intmodem")) - bc->cfg.extmodem = 0; - if (strstr(modestr,"extmodem")) - bc->cfg.extmodem = 1; - if (strstr(modestr,"noloopback")) - bc->cfg.loopback = 0; - if (strstr(modestr,"loopback")) - bc->cfg.loopback = 1; - if ((cp = strstr(modestr,"divider="))) { - bc->cfg.divider = simple_strtoul(cp+8, NULL, 0); - if (bc->cfg.divider < 1) - bc->cfg.divider = 1; - if (bc->cfg.divider > 1023) - bc->cfg.divider = 1023; - } - return 0; + return -EOPNOTSUPP; } /* --------------------------------------------------------------------- */ -static int baycom_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +static int baycom_ioctl(struct device *dev, struct ifreq *ifr, + struct hdlcdrv_ioctl *hi, int cmd) { struct baycom_state *bc; struct baycom_ioctl bi; - struct hdlcdrv_ioctl hi; - struct sm_ioctl si; + int cmd2; - baycom_paranoia_check(dev, "baycom_ioctl", -EINVAL); + if (!dev || !dev->priv || + ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) { + printk(KERN_ERR "bc_ioctl: invalid device struct\n"); + return -EINVAL; + } bc = (struct baycom_state *)dev->priv; + if (cmd != SIOCDEVPRIVATE) return -ENOIOCTLCMD; - if (get_user(cmd, (int *)ifr->ifr_data)) + if (get_user(cmd2, (int *)ifr->ifr_data)) return -EFAULT; -#ifdef BAYCOM_DEBUG - if (cmd == BAYCOMCTL_GETDEBUG) { - bi.data.dbg.debug1 = bc->ptt_keyed; - bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; - bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; - bc->debug_vals.last_intcnt = 0; - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - } - if (cmd == SMCTL_GETDEBUG) { - si.data.dbg.int_rate = bc->debug_vals.last_intcnt; - si.data.dbg.mod_cycles = bc->debug_vals.mod_cycles; - si.data.dbg.demod_cycles = bc->debug_vals.demod_cycles; - si.data.dbg.dma_residue = 0; - bc->debug_vals.mod_cycles = bc->debug_vals.demod_cycles = 0; - bc->debug_vals.last_intcnt = 0; - if (copy_to_user(ifr->ifr_data, &si, sizeof(si))) - return -EFAULT; - return 0; - } -#endif /* BAYCOM_DEBUG */ - - if (copy_from_user(&hi, ifr->ifr_data, sizeof(hi))) - return -EFAULT; - switch (hi.cmd) { + switch (hi->cmd) { default: - return -ENOIOCTLCMD; - - case HDLCDRVCTL_GETCHANNELPAR: - hi.data.cp.tx_delay = bc->ch_params.tx_delay; - hi.data.cp.tx_tail = bc->ch_params.tx_tail; - hi.data.cp.slottime = bc->ch_params.slottime; - hi.data.cp.ppersist = bc->ch_params.ppersist; - hi.data.cp.fulldup = bc->ch_params.fulldup; break; - case HDLCDRVCTL_SETCHANNELPAR: - if (!suser()) - return -EACCES; - bc->ch_params.tx_delay = hi.data.cp.tx_delay; - bc->ch_params.tx_tail = hi.data.cp.tx_tail; - bc->ch_params.slottime = hi.data.cp.slottime; - bc->ch_params.ppersist = hi.data.cp.ppersist; - bc->ch_params.fulldup = hi.data.cp.fulldup; - bc->hdlctx.slotcnt = 1; - return 0; - - case HDLCDRVCTL_GETMODEMPAR: - hi.data.mp.iobase = dev->base_addr; - hi.data.mp.irq = dev->irq; - hi.data.mp.dma = dev->dma; - hi.data.mp.dma2 = 0; - hi.data.mp.seriobase = 0; - hi.data.mp.pariobase = 0; - hi.data.mp.midiiobase = 0; - break; - - case HDLCDRVCTL_SETMODEMPAR: - if ((!suser()) || dev->start) - return -EACCES; - dev->base_addr = hi.data.mp.iobase; - dev->irq = /*hi.data.mp.irq*/0; - dev->dma = /*hi.data.mp.dma*/0; - return 0; - - case HDLCDRVCTL_GETSTAT: - hi.data.cs.ptt = !!(bc->stat & EPP_PTTBIT); - hi.data.cs.dcd = !(bc->stat & EPP_DCDBIT); - hi.data.cs.ptt_keyed = bc->ptt_keyed; - hi.data.cs.tx_packets = bc->stats.tx_packets; - hi.data.cs.tx_errors = bc->stats.tx_errors; - hi.data.cs.rx_packets = bc->stats.rx_packets; - hi.data.cs.rx_errors = bc->stats.rx_errors; - break; - - case HDLCDRVCTL_OLDGETSTAT: - hi.data.ocs.ptt = !!(bc->stat & EPP_PTTBIT); - hi.data.ocs.dcd = !(bc->stat & EPP_DCDBIT); - hi.data.ocs.ptt_keyed = bc->ptt_keyed; - break; - - case HDLCDRVCTL_CALIBRATE: - bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8; - return 0; - - case HDLCDRVCTL_DRIVERNAME: - strncpy(hi.data.drivername, "baycom_epp", sizeof(hi.data.drivername)); - break; - case HDLCDRVCTL_GETMODE: - sprintf(hi.data.modename, "%sclk,%smodem,divider=%d%s", - bc->cfg.intclk ? "int" : "ext", - bc->cfg.extmodem ? "ext" : "int", bc->cfg.divider, - bc->cfg.loopback ? ",loopback" : ""); - break; + strcpy(hi->data.modename, "epp"); + if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl))) + return -EFAULT; + return 0; case HDLCDRVCTL_SETMODE: - if (!suser() || dev->start) + if (dev->start || !suser()) return -EACCES; - hi.data.modename[sizeof(hi.data.modename)-1] = '\0'; - return baycom_setmode(bc, hi.data.modename); + hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; + return baycom_setmode(bc, hi->data.modename); case HDLCDRVCTL_MODELIST: - strncpy(hi.data.modename, "intclk,extclk,intmodem,extmodem,divider=x", - sizeof(hi.data.modename)); - break; + strcpy(hi->data.modename, "epp"); + if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl))) + return -EFAULT; + return 0; case HDLCDRVCTL_MODEMPARMASK: return HDLCDRV_PARMASK_IOBASE; } - if (copy_to_user(ifr->ifr_data, &hi, sizeof(hi))) - return -EFAULT; - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * Check for a network adaptor of this type, and return '0' if one exists. - * If dev->base_addr == 0, probe all likely locations. - * If dev->base_addr == 1, always return failure. - * If dev->base_addr == 2, allocate space for the device and return success - * (detachable devices only). - */ -static int baycom_probe(struct device *dev) -{ - static char ax25_bcast[AX25_ADDR_LEN] = { - 'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1 - }; - static char ax25_nocall[AX25_ADDR_LEN] = { - 'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1 - }; - const struct hdlcdrv_channel_params dflt_ch_params = { - 20, 2, 10, 40, 0 - }; - struct baycom_state *bc; - - if (!dev) - return -ENXIO; - baycom_paranoia_check(dev, "baycom_probe", -ENXIO); - /* - * not a real probe! only initialize data structures - */ - bc = (struct baycom_state *)dev->priv; - /* - * initialize the baycom_state struct - */ - bc->ch_params = dflt_ch_params; - bc->ptt_keyed = 0; - - /* - * initialize the device struct - */ - dev->open = epp_open; - dev->stop = epp_close; - dev->do_ioctl = baycom_ioctl; - dev->hard_start_xmit = baycom_send_packet; - dev->get_stats = baycom_get_stats; - - /* Fill in the fields of the device structure */ - dev_init_buffers(dev); - - skb_queue_head_init(&bc->send_queue); - -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - dev->hard_header = ax25_encapsulate; - dev->rebuild_header = ax25_rebuild_header; -#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */ - dev->hard_header = NULL; - dev->rebuild_header = NULL; -#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */ - dev->set_mac_address = baycom_set_mac_address; - - dev->type = ARPHRD_AX25; /* AF_AX25 device */ - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */ - dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */ - memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); - memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN); + if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) + return -EFAULT; + switch (bi.cmd) { + default: + return -ENOIOCTLCMD; - /* New style flags */ - dev->flags = 0; +#ifdef BAYCOM_DEBUG + case BAYCOMCTL_GETDEBUG: + bi.data.dbg.debug1 = bc->hdrv.ptt_keyed; + bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; + bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; + break; +#endif /* BAYCOM_DEBUG */ + } + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; return 0; + } /* --------------------------------------------------------------------- */ __initfunc(int baycom_epp_init(void)) { - struct device *dev; - int i, found = 0; + int i, j, found = 0; char set_hw = 1; struct baycom_state *bc; + char ifname[HDLCDRV_IFNAMELEN]; + printk(bc_drvinfo); /* * register net devices */ for (i = 0; i < NR_PORTS; i++) { - dev = baycom_device+i; + struct device *dev = baycom_device+i; + sprintf(ifname, "bce%d", i); + if (!baycom_ports[i].mode) set_hw = 0; if (!set_hw) baycom_ports[i].iobase = 0; - memset(dev, 0, sizeof(struct device)); - if (!(bc = dev->priv = kmalloc(sizeof(struct baycom_state), GFP_KERNEL))) - return -ENOMEM; - /* - * initialize part of the baycom_state struct - */ - memset(bc, 0, sizeof(struct baycom_state)); - bc->magic = BAYCOM_MAGIC; - sprintf(bc->ifname, "bce%d", i); - /* - * initialize part of the device struct - */ - dev->name = bc->ifname; - dev->if_port = 0; - dev->init = baycom_probe; - dev->start = 0; - dev->tbusy = 1; - dev->base_addr = baycom_ports[i].iobase; - dev->irq = 0; - dev->dma = 0; - if (register_netdev(dev)) { - printk(KERN_WARNING "%s: cannot register net device %s\n", bc_drvname, bc->ifname); - kfree(dev->priv); - return -ENXIO; + j = hdlcdrv_register_hdlcdrv(dev, &epp_ops, + sizeof(struct baycom_state), + ifname, baycom_ports[i].iobase, 0, 0); + if (!j) { + bc = (struct baycom_state *)dev->priv; + if (set_hw && baycom_setmode(bc, baycom_ports[i].mode)) + set_hw = 0; + found++; + } else { + printk(KERN_WARNING "%s: cannot register net device\n", + bc_drvname); } - if (set_hw && baycom_setmode(bc, baycom_ports[i].mode)) - set_hw = 0; - found++; } if (!found) return -ENXIO; @@ -1473,51 +639,47 @@ /* * command line settable parameters */ -static const char *mode[NR_PORTS] = { "epp", }; static int iobase[NR_PORTS] = { 0x378, }; #if LINUX_VERSION_CODE >= 0x20115 -MODULE_PARM(mode, "s"); -MODULE_PARM_DESC(mode, "baycom operating mode; epp"); -MODULE_PARM(iobase, "i"); +MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i"); MODULE_PARM_DESC(iobase, "baycom io base address"); -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom epp amateur radio modem driver"); +MODULE_AUTHOR("Matthias Welwarsky, dg2fef@afthd.tu-darmstadt.de"); +MODULE_DESCRIPTION("Baycom epp adaptor driver"); #endif __initfunc(int init_module(void)) { - int i; + int i; - for (i = 0; (i < NR_PORTS) && (mode[i]); i++) { - baycom_ports[i].mode = mode[i]; - baycom_ports[i].iobase = iobase[i]; - } - if (i < NR_PORTS-1) - baycom_ports[i+1].mode = NULL; - return baycom_epp_init(); + for (i = 0; i < NR_PORTS; i++) { + baycom_ports[i].mode = "epp"; + baycom_ports[i].iobase = iobase[i]; + } + if (i < NR_PORTS-1) + baycom_ports[i+1].mode = NULL; + return baycom_epp_init(); } /* --------------------------------------------------------------------- */ void cleanup_module(void) { - struct device *dev; - struct baycom_state *bc; int i; for(i = 0; i < NR_PORTS; i++) { - dev = baycom_device+i; - bc = (struct baycom_state *)dev->priv; + struct device *dev = baycom_device+i; + struct baycom_state *bc = (struct baycom_state *)dev->priv; + if (bc) { - if (bc->magic == BAYCOM_MAGIC) { - unregister_netdev(dev); - kfree(dev->priv); - } else - printk(paranoia_str, "cleanup_module"); + if (bc->hdrv.magic != HDLCDRV_MAGIC) + printk(KERN_ERR "baycom: invalid magic in " + "cleanup_module\n"); + else + hdlcdrv_unregister_hdlcdrv(dev); } } } @@ -1525,8 +687,7 @@ #else /* MODULE */ /* --------------------------------------------------------------------- */ /* - * format: baycom=io,mode - * mode: epp + * format: baycom_epp = io */ __initfunc(void baycom_epp_setup(char *str, int *ints)) @@ -1546,4 +707,5 @@ } #endif /* MODULE */ + /* --------------------------------------------------------------------- */ Index: linux/drivers/net/hamradio/baycom_par.c diff -u linux/drivers/net/hamradio/baycom_par.c:1.1.2.1 linux/drivers/net/hamradio/baycom_par.c:1.1.2.1.2.1 --- linux/drivers/net/hamradio/baycom_par.c:1.1.2.1 Sun Jul 4 10:15:05 1999 +++ linux/drivers/net/hamradio/baycom_par.c Sun Jul 4 11:07:21 1999 @@ -77,6 +77,7 @@ #include #include #include +#include #include #include #include Index: linux/drivers/net/hamradio/baycom_ser_fdx.c diff -u linux/drivers/net/hamradio/baycom_ser_fdx.c:1.1.2.1 linux/drivers/net/hamradio/baycom_ser_fdx.c:1.1.2.1.2.1 --- linux/drivers/net/hamradio/baycom_ser_fdx.c:1.1.2.1 Sun Jul 4 10:15:05 1999 +++ linux/drivers/net/hamradio/baycom_ser_fdx.c Sun Jul 4 11:07:21 1999 @@ -81,6 +81,7 @@ #include #include #include +#include #include #include #include Index: linux/drivers/net/hamradio/baycom_ser_hdx.c diff -u linux/drivers/net/hamradio/baycom_ser_hdx.c:1.1.2.1 linux/drivers/net/hamradio/baycom_ser_hdx.c:1.1.2.1.2.1 --- linux/drivers/net/hamradio/baycom_ser_hdx.c:1.1.2.1 Sun Jul 4 10:15:05 1999 +++ linux/drivers/net/hamradio/baycom_ser_hdx.c Sun Jul 4 11:07:21 1999 @@ -70,6 +70,7 @@ #include #include #include +#include #include #include #include Index: linux/drivers/net/hamradio/hdlcdrv.c diff -u linux/drivers/net/hamradio/hdlcdrv.c:1.1.2.1 linux/drivers/net/hamradio/hdlcdrv.c:1.1.2.1.2.1 --- linux/drivers/net/hamradio/hdlcdrv.c:1.1.2.1 Sun Jul 4 10:15:05 1999 +++ linux/drivers/net/hamradio/hdlcdrv.c Sun Jul 4 11:07:21 1999 @@ -58,9 +58,12 @@ #include #include #include -#include +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) /* prototypes for ax25_encapsulate and ax25_rebuild_header */ -#include +#include +#include +#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */ +#include /* make genksyms happy */ #include @@ -179,7 +182,7 @@ /* ---------------------------------------------------------------------- */ -#define tenms_to_2flags(s,tenms) ((tenms * s->par.bitrate) / 100 / 16) +#define ms_to_2flags(s,ms) ((ms * s->dev.hw.bitrate) / 1000 / 16) /* ---------------------------------------------------------------------- */ /* @@ -214,17 +217,17 @@ return; if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len)) return; - pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */ - if (!(skb = dev_alloc_skb(pkt_len))) { + pkt_len = s->hdlcrx.len - 2; /* KISS kludge gone :-) */ + if (!(skb = dev_alloc_skb(pkt_len+MAX_HEADER))) { printk("%s: memory squeeze, dropping packet\n", s->ifname); s->stats.rx_dropped++; return; } + skb_reserve(skb, MAX_HEADER); + skb->dev = dev; - cp = skb_put(skb, pkt_len); - *cp++ = 0; /* KISS kludge */ - memcpy(cp, s->hdlcrx.buffer, pkt_len - 1); + memcpy(skb_put(skb, pkt_len), s->hdlcrx.buffer, pkt_len); skb->protocol = htons(ETH_P_AX25); skb->mac.raw = skb->data; netif_rx(skb); @@ -287,47 +290,6 @@ /* ---------------------------------------------------------------------- */ -static void inline do_kiss_params(struct hdlcdrv_state *s, - unsigned char *data, unsigned long len) -{ - -#ifdef KISS_VERBOSE -#define PKP(a,b) printk(KERN_INFO "%s: channel params: " a "\n", s->ifname, b) -#else /* KISS_VERBOSE */ -#define PKP(a,b) -#endif /* KISS_VERBOSE */ - - if (len < 2) - return; - switch(data[0]) { - case PARAM_TXDELAY: - s->ch_params.tx_delay = data[1]; - PKP("TX delay = %ums", 10 * s->ch_params.tx_delay); - break; - case PARAM_PERSIST: - s->ch_params.ppersist = data[1]; - PKP("p persistence = %u", s->ch_params.ppersist); - break; - case PARAM_SLOTTIME: - s->ch_params.slottime = data[1]; - PKP("slot time = %ums", s->ch_params.slottime); - break; - case PARAM_TXTAIL: - s->ch_params.tx_tail = data[1]; - PKP("TX tail = %ums", s->ch_params.tx_tail); - break; - case PARAM_FULLDUP: - s->ch_params.fulldup = !!data[1]; - PKP("%s duplex", s->ch_params.fulldup ? "full" : "half"); - break; - default: - break; - } -#undef PKP -} - -/* ---------------------------------------------------------------------- */ - void hdlcdrv_transmitter(struct device *dev, struct hdlcdrv_state *s) { unsigned int mask1, mask2, mask3; @@ -367,7 +329,7 @@ return; } if (!(skb = skb_dequeue(&s->send_queue))) { - int flgs = tenms_to_2flags + int flgs = ms_to_2flags (s, s->ch_params.tx_tail); if (flgs < 2) flgs = 2; @@ -375,20 +337,16 @@ s->hdlctx.numflags = flgs; break; } - if (skb->data[0] != 0) { - do_kiss_params(s, skb->data, skb->len); - dev_kfree_skb(skb); - break; - } - pkt_len = skb->len-1; /* strip KISS byte */ + pkt_len = skb->len; /* KISS kludge gone :-) */ if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) { s->hdlctx.tx_state = 0; s->hdlctx.numflags = 1; dev_kfree_skb(skb); break; } - memcpy(s->hdlctx.buffer, skb->data+1, pkt_len); + memcpy(s->hdlctx.buffer, skb->data, pkt_len); dev_kfree_skb(skb); + mark_bh(NET_BH); s->hdlctx.bp = s->hdlctx.buffer; append_crc_ccitt(s->hdlctx.buffer, pkt_len); s->hdlctx.len = pkt_len+2; /* the appended CRC */ @@ -433,7 +391,7 @@ static void start_tx(struct device *dev, struct hdlcdrv_state *s) { s->hdlctx.tx_state = 0; - s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay); + s->hdlctx.numflags = ms_to_2flags(s, s->ch_params.tx_delay); s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0; hdlcdrv_transmitter(dev, s); s->hdlctx.ptt = 1; @@ -498,9 +456,29 @@ if (hdlcdrv_paranoia_check(dev, "hdlcdrv_send_packet")) return 0; + + if (dev->tbusy) { + /* + * If we get here, some higher level has decided we are broken. + * There should really be a "kick me" function call instead. + */ + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 5) + return 1; + printk(KERN_WARNING "%s: transmit timed out.\n", dev->name); + dev->tbusy=0; + dev->trans_start = jiffies; + } + sm = (struct hdlcdrv_state *)dev->priv; skb_queue_tail(&sm->send_queue, skb); - dev->trans_start = jiffies; + + /* this bypasses the arbiter */ + if (sm->hdlctx.ptt == 0) { + start_tx(dev, sm); + } + + dev->trans_start = jiffies; return 0; } @@ -657,6 +635,7 @@ s->ch_params.slottime = bi.data.cp.slottime; s->ch_params.ppersist = bi.data.cp.ppersist; s->ch_params.fulldup = bi.data.cp.fulldup; + s->dev.hw.duplex = bi.data.cp.fulldup; /* FIXME: should be done from DDI */ s->hdlctx.slotcnt = 1; return 0; @@ -702,7 +681,7 @@ break; case HDLCDRVCTL_CALIBRATE: - s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16; + s->hdlctx.calibrate = bi.data.calibrate * s->dev.hw.bitrate / 16; return 0; case HDLCDRVCTL_GETSAMPLES: @@ -749,6 +728,12 @@ /* --------------------------------------------------------------------- */ +static void set_multicast_list(struct device *dev) +{ +} + +/* --------------------------------------------------------------------- */ + /* * Check for a network adaptor of this type, and return '0' if one exists. * If dev->base_addr == 0, probe all likely locations. @@ -806,20 +791,17 @@ dev->do_ioctl = hdlcdrv_ioctl; dev->hard_start_xmit = hdlcdrv_send_packet; dev->get_stats = hdlcdrv_get_stats; + dev->set_multicast_list = set_multicast_list; /* Fill in the fields of the device structure */ - + dev->tx_queue_len = 0; dev_init_buffers(dev); skb_queue_head_init(&s->send_queue); -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - dev->hard_header = ax25_encapsulate; - dev->rebuild_header = ax25_rebuild_header; -#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */ dev->hard_header = NULL; dev->rebuild_header = NULL; -#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */ + dev->set_mac_address = hdlcdrv_set_mac_address; dev->type = ARPHRD_AX25; /* AF_AX25 device */ @@ -830,13 +812,24 @@ memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN); /* New style flags */ - dev->flags = 0; - + dev->flags = IFF_BROADCAST; return 0; } /* --------------------------------------------------------------------- */ +static unsigned int hdlcdrv_getptt(struct device *dev) +{ + return hdlcdrv_ptt((struct hdlcdrv_state *)dev->priv); +} + +static unsigned int hdlcdrv_getdcd(struct device *dev) +{ + return ((struct hdlcdrv_state *)dev->priv)->hdlcrx.dcd; +} + +/* --------------------------------------------------------------------- */ + int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops, unsigned int privsize, char *ifname, unsigned int baseaddr, unsigned int irq, @@ -856,6 +849,9 @@ */ memset(s, 0, privsize); s->magic = HDLCDRV_MAGIC; + ax25_dev_initdev(dev, &s->dev); + ax25_dev_setpttfunc(dev, hdlcdrv_getptt); /* may be overridden in the specific driver */ + ax25_dev_setdcdfunc(dev, hdlcdrv_getdcd); /* may be overridden in the specific driver */ strncpy(s->ifname, ifname, sizeof(s->ifname)); s->ops = ops; /* @@ -869,6 +865,7 @@ dev->base_addr = baseaddr; dev->irq = irq; dev->dma = dma; + if (register_netdev(dev)) { printk(KERN_WARNING "hdlcdrv: cannot register net " "device %s\n", s->ifname); Index: linux/drivers/net/hamradio/kiss.c diff -u /dev/null linux/drivers/net/hamradio/kiss.c:1.1.4.1 --- /dev/null Wed Jul 7 01:01:21 1999 +++ linux/drivers/net/hamradio/kiss.c Sun Jul 4 11:07:21 1999 @@ -0,0 +1,1279 @@ +/* + * kiss.c This module implements the KISS protocol for kernel-based + * devices like TTY. It interfaces between a raw TTY, and the + * kernel's AX.25 protocol layers. + * + * Shamelessly copied from slip.c on 99/01/04: + * Matthias Welwarsky + * + * Original Authors: + * Laurence Culhane, + * Fred N. van Kempen, + * + * See slip.c for additional credits and fixes. + * + */ + +#define KISS_CHECK_TRANSMIT +#undef KISS_DEV_IOCTL +#define HAVE_CRC_SMACK + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_INET +#include +#include +#endif + +#include "kiss.h" + +#ifdef MODULE +#define KISS_VERSION "KISS-NET3.019-NEWTTY-MODULAR" +#else +#define KISS_VERSION "KISS-NET3.019-NEWTTY" +#endif + + +typedef struct kiss_ctrl { + char if_name[8]; /* "ax0\0" .. "ax99999\0" */ + struct kiss ctrl; /* KISS things */ + struct device dev; /* the device */ +} kiss_ctrl_t; +static kiss_ctrl_t **kiss_ctrls = NULL; + +int kiss_maxdev = KISS_NRUNIT; /* Can be overridden with insmod! */ +MODULE_PARM(kiss_maxdev, "i"); + +static struct tty_ldisc kiss_ldisc; + +static int kiss_esc(unsigned char *p, unsigned char *d, int len); +static void kiss_unesc(struct kiss *ks, unsigned char c); +static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, unsigned int len); +static int kiss_init(struct device *dev); +static int kiss_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg); +#ifdef KISS_DEV_IOCTL +static int kiss_dev_ioctl(struct device *dev,struct ifreq *rq,int cmd); +#endif + +static int kiss_dev_set_mac_address(struct device *dev, void *addr); + +/*---------------------------------------------------------------------------*/ + +static const unsigned short Crc_16_table[] = { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, + 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, + 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, + 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, + 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, + 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, + 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, + 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, + 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, + 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, + 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, + 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, + 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, + 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, + 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, + 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, + 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, + 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, + 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, + 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, + 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, + 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, + 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, + 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, + 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, + 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, + 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, + 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, + 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 +}; + +/*---------------------------------------------------------------------------*/ + +static unsigned short +calc_crc_16(unsigned char *cp, int size) +{ + unsigned short crc = 0; + + while (size--) + crc = (crc >> 8) ^ Crc_16_table[(crc ^ *cp++) & 0xff]; + + return crc; +} + +/*---------------------------------------------------------------------------*/ + +static int +check_crc_16(unsigned char *cp, int size) +{ + unsigned short crc = 0xffff; + + if (size < 3) + return -1; + + while (size--) + crc = (crc >> 8) ^ Crc_16_table[(crc ^ *cp++) & 0xff]; + + if (crc != 0) + return -1; + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static const unsigned short Crc_flex_table[] = { + 0x0f87, 0x1e0e, 0x2c95, 0x3d1c, 0x49a3, 0x582a, 0x6ab1, 0x7b38, + 0x83cf, 0x9246, 0xa0dd, 0xb154, 0xc5eb, 0xd462, 0xe6f9, 0xf770, + 0x1f06, 0x0e8f, 0x3c14, 0x2d9d, 0x5922, 0x48ab, 0x7a30, 0x6bb9, + 0x934e, 0x82c7, 0xb05c, 0xa1d5, 0xd56a, 0xc4e3, 0xf678, 0xe7f1, + 0x2e85, 0x3f0c, 0x0d97, 0x1c1e, 0x68a1, 0x7928, 0x4bb3, 0x5a3a, + 0xa2cd, 0xb344, 0x81df, 0x9056, 0xe4e9, 0xf560, 0xc7fb, 0xd672, + 0x3e04, 0x2f8d, 0x1d16, 0x0c9f, 0x7820, 0x69a9, 0x5b32, 0x4abb, + 0xb24c, 0xa3c5, 0x915e, 0x80d7, 0xf468, 0xe5e1, 0xd77a, 0xc6f3, + 0x4d83, 0x5c0a, 0x6e91, 0x7f18, 0x0ba7, 0x1a2e, 0x28b5, 0x393c, + 0xc1cb, 0xd042, 0xe2d9, 0xf350, 0x87ef, 0x9666, 0xa4fd, 0xb574, + 0x5d02, 0x4c8b, 0x7e10, 0x6f99, 0x1b26, 0x0aaf, 0x3834, 0x29bd, + 0xd14a, 0xc0c3, 0xf258, 0xe3d1, 0x976e, 0x86e7, 0xb47c, 0xa5f5, + 0x6c81, 0x7d08, 0x4f93, 0x5e1a, 0x2aa5, 0x3b2c, 0x09b7, 0x183e, + 0xe0c9, 0xf140, 0xc3db, 0xd252, 0xa6ed, 0xb764, 0x85ff, 0x9476, + 0x7c00, 0x6d89, 0x5f12, 0x4e9b, 0x3a24, 0x2bad, 0x1936, 0x08bf, + 0xf048, 0xe1c1, 0xd35a, 0xc2d3, 0xb66c, 0xa7e5, 0x957e, 0x84f7, + 0x8b8f, 0x9a06, 0xa89d, 0xb914, 0xcdab, 0xdc22, 0xeeb9, 0xff30, + 0x07c7, 0x164e, 0x24d5, 0x355c, 0x41e3, 0x506a, 0x62f1, 0x7378, + 0x9b0e, 0x8a87, 0xb81c, 0xa995, 0xdd2a, 0xcca3, 0xfe38, 0xefb1, + 0x1746, 0x06cf, 0x3454, 0x25dd, 0x5162, 0x40eb, 0x7270, 0x63f9, + 0xaa8d, 0xbb04, 0x899f, 0x9816, 0xeca9, 0xfd20, 0xcfbb, 0xde32, + 0x26c5, 0x374c, 0x05d7, 0x145e, 0x60e1, 0x7168, 0x43f3, 0x527a, + 0xba0c, 0xab85, 0x991e, 0x8897, 0xfc28, 0xeda1, 0xdf3a, 0xceb3, + 0x3644, 0x27cd, 0x1556, 0x04df, 0x7060, 0x61e9, 0x5372, 0x42fb, + 0xc98b, 0xd802, 0xea99, 0xfb10, 0x8faf, 0x9e26, 0xacbd, 0xbd34, + 0x45c3, 0x544a, 0x66d1, 0x7758, 0x03e7, 0x126e, 0x20f5, 0x317c, + 0xd90a, 0xc883, 0xfa18, 0xeb91, 0x9f2e, 0x8ea7, 0xbc3c, 0xadb5, + 0x5542, 0x44cb, 0x7650, 0x67d9, 0x1366, 0x02ef, 0x3074, 0x21fd, + 0xe889, 0xf900, 0xcb9b, 0xda12, 0xaead, 0xbf24, 0x8dbf, 0x9c36, + 0x64c1, 0x7548, 0x47d3, 0x565a, 0x22e5, 0x336c, 0x01f7, 0x107e, + 0xf808, 0xe981, 0xdb1a, 0xca93, 0xbe2c, 0xafa5, 0x9d3e, 0x8cb7, + 0x7440, 0x65c9, 0x5752, 0x46db, 0x3264, 0x23ed, 0x1176, 0x00ff +}; + +/*---------------------------------------------------------------------------*/ + +static unsigned short +calc_crc_flex(unsigned char *cp, int size) +{ + unsigned short crc = 0xffff; + + while (size--) + crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; + + return crc; +} + +/*---------------------------------------------------------------------------*/ + +static int +check_crc_flex(unsigned char *cp, int size) +{ + unsigned short crc = 0xffff; + + if (size < 3) + return -1; + + while (size--) + crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; + + if ((crc & 0xffff) != 0x7070) + return -1; + + return 0; +} + +/******************************** +* Buffer administration routines: +* kiss_alloc_bufs() +* kiss_free_bufs() +* kiss_realloc_bufs() +* +* NOTE: kiss_realloc_bufs != kiss_free_bufs + kiss_alloc_bufs, because +* kiss_realloc_bufs provides strong atomicity and reallocation +* on actively running device. +*********************************/ + +/* + Allocate channel buffers. + */ + +static int +kiss_alloc_bufs(struct kiss *ks, int mtu) +{ + int err = -ENOBUFS; + unsigned long len; + char * rbuff = NULL; + char * xbuff = NULL; + + /* + * Allocate the KISS frame buffers: + * + * rbuff Receive buffer. + * xbuff Transmit buffer. + */ + len = mtu * 2; + + /* + * allow for arrival of larger UDP packets, even if we say not to + * also fixes a bug in which SunOS sends 512-byte packets even with + * an MSS of 128 + */ + if (len < 576 * 2) + len = 576 * 2; + rbuff = kmalloc(len + 4, GFP_KERNEL); + if (rbuff == NULL) + goto err_exit; + xbuff = kmalloc(len + 4, GFP_KERNEL); + if (xbuff == NULL) + goto err_exit; + start_bh_atomic(); + if (ks->tty == NULL) { + end_bh_atomic(); + err = -ENODEV; + goto err_exit; + } + ks->mtu = mtu; + ks->buffsize = len; + ks->rcount = 0; + ks->xleft = 0; + rbuff = xchg(&ks->rbuff, rbuff); + xbuff = xchg(&ks->xbuff, xbuff); + end_bh_atomic(); + err = 0; + + /* Cleanup */ +err_exit: + if (xbuff) + kfree(xbuff); + if (rbuff) + kfree(rbuff); + return err; +} + +/* Free a KISS channel buffers. */ +static void +kiss_free_bufs(struct kiss *ks) +{ + void * tmp; + + /* Free all KISS frame buffers. */ + if ((tmp = xchg(&ks->rbuff, NULL)) != NULL) + kfree(tmp); + if ((tmp = xchg(&ks->xbuff, NULL)) != NULL) + kfree(tmp); +} + +/* + Reallocate kiss channel buffers. + */ + +static int kiss_realloc_bufs(struct kiss *ks, int mtu) +{ + int err = 0; + struct device *dev = ks->dev; + unsigned char *xbuff, *rbuff; + int len = (mtu + MAX_HEADER) * 2; + +/* + * allow for arrival of larger UDP packets, even if we say not to + * also fixes a bug in which SunOS sends 512-byte packets even with + * an MSS of 128 + */ + if (len < 576 * 2) + len = 576 * 2; + + xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC); + rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC); + + if (xbuff == NULL || rbuff == NULL) { + if (mtu >= dev->mtu) { + printk("%s: unable to grow kiss buffers, MTU change cancelled.\n", + dev->name); + err = -ENOBUFS; + } + goto done; + } + + start_bh_atomic(); + + err = -ENODEV; + if (ks->tty == NULL) + goto done_on_bh; + + xbuff = xchg(&ks->xbuff, xbuff); + rbuff = xchg(&ks->rbuff, rbuff); + if (ks->xleft) { + if (ks->xleft <= len) { + memcpy(ks->xbuff, ks->xhead, ks->xleft); + } else { + ks->xleft = 0; + ks->tx_dropped++; + } + } + ks->xhead = ks->xbuff; + + if (ks->rcount) { + if (ks->rcount <= len) { + memcpy(ks->rbuff, rbuff, ks->rcount); + } else { + ks->rcount = 0; + ks->rx_over_errors++; + set_bit(KSF_ERROR, &ks->flags); + } + } + ks->mtu = mtu + MAX_HEADER;; + dev->mtu = mtu; + ks->buffsize = len; + err = 0; + +done_on_bh: + end_bh_atomic(); + +done: + if (xbuff) + kfree(xbuff); + if (rbuff) + kfree(rbuff); + return err; +} + + +/* Set the "sending" flag. This must be atomic hence the set_bit. */ +static inline void +kiss_lock(struct kiss *ks) +{ + if (test_and_set_bit(0, (void *) &ks->dev->tbusy)) { + printk("%s: trying to lock already locked device!\n", ks->dev->name); + } +} + + +/* Clear the "sending" flag. This must be atomic, hence the ASM. */ +static inline void +kiss_unlock(struct kiss *ks) +{ + if (!test_and_clear_bit(0, (void *)&ks->dev->tbusy)) { + printk("%s: trying to unlock already unlocked device!\n", ks->dev->name); + } +} + +/* Send one completely decapsulated IP datagram to the IP layer. */ +static void +kiss_bump(struct kiss *ks) +{ + struct sk_buff *skb; + int count; + + recheck_crc: + if (ks->mode == KISS_MODE_FLEX) { + if (check_crc_flex(ks->rbuff, ks->rcount) < 0) { + ks->rx_errors++; + return; + } + ks->rcount -= 2; +#ifdef HAVE_CRC_SMACK + } else if (ks->mode == KISS_MODE_SMACK) { + if (check_crc_16(ks->rbuff, ks->rcount) < 0) { + ks->rx_errors++; + return; + } + ks->rcount -= 2; +#endif + } else if (ks->mode == KISS_MODE_ADAPTIVE || ks->mode == KISS_MODE_COMPAT) { + if (ks->rbuff[0] & 0x20) { + ks->mode = KISS_MODE_FLEX; + goto recheck_crc; + } + } + + count = ks->rcount-1; + ks->rx_bytes+=count; + + skb = dev_alloc_skb(count+MAX_HEADER); + if (skb == NULL) { + printk("%s: memory squeeze, dropping packet.\n", ks->dev->name); + ks->rx_dropped++; + return; + } + skb_reserve(skb, MAX_HEADER); + skb->dev = ks->dev; + memcpy(skb_put(skb,count), ks->rbuff+1, count); + skb->mac.raw=skb->data; + skb->protocol= __constant_htons(ETH_P_AX25); + netif_rx(skb); + ks->rx_packets++; +} + +/* Encapsulate one IP datagram and stuff into a TTY queue. */ +static void +kiss_encaps(struct kiss *ks, unsigned char *icp, int len) +{ + unsigned char *p; + int actual, count; + + if (len > ks->mtu) { /* Sigh, shouldn't occur BUT ... */ + printk ("%s: truncating oversized transmit packet!\n", ks->dev->name); + ks->tx_dropped++; + kiss_unlock(ks); + return; + } + + p = icp; + + switch (ks->mode) { + unsigned short crc; + + case KISS_MODE_FLEX: + *p |= 0x20; + crc = calc_crc_flex(p, len); + count = kiss_esc_crc(p, ks->xbuff, crc, len+2); + break; +#ifdef HAVE_CRC_SMACK + case KISS_MODE_SMACK: + *p |= 0x80; + crc = calc_crc_16(p, len); + count = kiss_esc_crc(p, ks->xbuff, crc, len+2); + break; +#endif + default: + count = kiss_esc(p, ks->xbuff, len); + break; + } + + /* Order of next two lines is *very* important. + * When we are sending a little amount of data, + * the transfer may be completed inside driver.write() + * routine, because it's running with interrupts enabled. + * In this case we *never* got WRITE_WAKEUP event, + * if we did not request it before write operation. + * 14 Oct 1994 Dmitry Gorodchanin. + */ + ks->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + actual = ks->tty->driver.write(ks->tty, 0, ks->xbuff, count); +#ifdef KISS_CHECK_TRANSMIT + ks->dev->trans_start = jiffies; +#endif + ks->xleft = count - actual; + ks->xhead = ks->xbuff + actual; +} + +/* + * Called by the driver when there's room for more data. If we have + * more packets to send, we send them here. + */ +static void kiss_write_wakeup(struct tty_struct *tty) +{ + int actual; + struct kiss *ks = (struct kiss *) tty->disc_data; + + /* First make sure we're connected. */ + if (!ks || ks->magic != KISS_MAGIC || !ks->dev->start) { + return; + } + if (ks->xleft <= 0) { + /* Now serial buffer is almost free & we can start + * transmission of another packet */ + ks->tx_packets++; + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + kiss_unlock(ks); + mark_bh(NET_BH); + return; + } + + actual = tty->driver.write(tty, 0, ks->xhead, ks->xleft); + ks->xleft -= actual; + ks->xhead += actual; +} + +/* Encapsulate an IP datagram and kick it into a TTY queue. */ +static int +kiss_xmit(struct sk_buff *skb, struct device *dev) +{ + struct kiss *ks = (struct kiss*)(dev->priv); + + if (!dev->start) { + printk("%s: xmit call when iface is down\n", dev->name); + dev_kfree_skb(skb); + return 0; + } + if (ks->tty == NULL) { + dev_kfree_skb(skb); + return 0; + } + + if (skb_headroom(skb) > 0) { + *skb_push(skb, 1) = 0; /* prepend KISS header */ + } else { + dev_kfree_skb(skb); + return 0; + } + + if (dev->tbusy) { + /* May be we must check transmitter timeout here ? + * 14 Oct 1994 Dmitry Gorodchanin. + */ +#ifdef KISS_CHECK_TRANSMIT + if (jiffies - dev->trans_start < 20 * HZ) { + /* 20 sec timeout not reached */ + return 1; + } + printk("%s: transmit timed out, %s?\n", dev->name, + (ks->tty->driver.chars_in_buffer(ks->tty) || ks->xleft) ? + "bad line quality" : "driver error"); + ks->xleft = 0; + ks->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + kiss_unlock(ks); +#else + return 1; +#endif + } + + /* We were not busy, so we are now... :-) */ + if (skb != NULL) + { + kiss_lock(ks); + ks->tx_bytes+=skb->len; + kiss_encaps(ks, skb->data, skb->len); + dev_kfree_skb(skb); + } + return 0; +} + + +/****************************************** + * Routines looking at netdevice side. + ******************************************/ + +/* Netdevice UP -> DOWN routine */ + +static int +kiss_dev_close(struct device *dev) +{ + struct kiss *ks = (struct kiss*)(dev->priv); + + start_bh_atomic(); + if (ks->tty) { + /* TTY discipline is running. */ + ks->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + } + dev->tbusy = 1; + dev->start = 0; + ks->rcount = 0; + ks->xleft = 0; + end_bh_atomic(); + + MOD_DEC_USE_COUNT; + return 0; +} + +/* Netdevice DOWN -> UP routine */ + +static int kiss_dev_open(struct device *dev) +{ + struct kiss *ks = (struct kiss*)(dev->priv); + + if (ks->tty==NULL) + return -ENODEV; + + ks->flags &= (1 << KSF_INUSE); + dev->start = 1; + dev->tbusy = 0; + MOD_INC_USE_COUNT; + return 0; +} + +/* Netdevice change MTU request */ + +static int kiss_change_mtu(struct device *dev, int new_mtu) +{ + struct kiss *ks = (struct kiss*)(dev->priv); + + if (new_mtu < 68 || new_mtu > 65534) + return -EINVAL; + + if (new_mtu != dev->mtu) + return kiss_realloc_bufs(ks, new_mtu); + return 0; +} + +/* Netdevice get statistics request */ + +static struct net_device_stats * +kiss_get_stats(struct device *dev) +{ + static struct net_device_stats stats; + struct kiss *ks = (struct kiss*)(dev->priv); + + memset(&stats, 0, sizeof(struct net_device_stats)); + + stats.rx_packets = ks->rx_packets; + stats.tx_packets = ks->tx_packets; + stats.rx_bytes = ks->rx_bytes; + stats.tx_bytes = ks->tx_bytes; + stats.rx_dropped = ks->rx_dropped; + stats.tx_dropped = ks->tx_dropped; + stats.tx_errors = ks->tx_errors; + stats.rx_errors = ks->rx_errors; + stats.rx_over_errors = ks->rx_over_errors; + return (&stats); +} + +static unsigned int false(struct device *dev) +{ + return 0; +} + +/* Netdevice register callback */ + +static int kiss_init(struct device *dev) +{ + struct kiss *ks = (struct kiss*)(dev->priv); + + /* + * Finish setting up the DEVICE info. + */ + + dev->mtu = ks->mtu - MAX_HEADER; + dev->hard_start_xmit = kiss_xmit; + dev->open = kiss_dev_open; + dev->stop = kiss_dev_close; + dev->get_stats = kiss_get_stats; + dev->change_mtu = kiss_change_mtu; +#ifdef KISS_DEV_IOCTL + dev->do_ioctl = kiss_dev_ioctl; +#endif +#ifdef HAVE_SET_MAC_ADDR + dev->set_mac_address = kiss_dev_set_mac_address; +#endif + dev->hard_header_len = AX25_MAX_HEADER_LEN; + dev->addr_len = AX25_ADDR_LEN; + dev->type = ARPHRD_AX25; + dev->tx_queue_len = 10; + + dev_init_buffers(dev); + + /* New-style flags. */ + dev->flags = IFF_BROADCAST|IFF_MULTICAST; + + /* initialise DDI interface values */ + ax25_dev_initdev(dev, &ks->ax25dev); + ax25_dev_setpttfunc(dev, false); + ax25_dev_setdcdfunc(dev, false); + ax25_dev_setbitrate(dev, 10000000); /* FIXME: this is wrong, too, but ok for now */ + ax25_dev_setduplex(dev, 1); + ax25_dev_setfast(dev, 1); /* skip channel arbitration stuff */ + + return 0; +} + + +/****************************************** + Routines looking at TTY side. + ******************************************/ + + +static int kiss_receive_room(struct tty_struct *tty) +{ + return 65536; /* We can handle an infinite amount of data. :-) */ +} + +/* + * Handle the 'receiver data ready' interrupt. + * This function is called by the 'tty_io' module in the kernel when + * a block of KISS data has been received, which can now be decapsulated + * and sent on to some IP layer for further processing. + */ + +static void kiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct kiss *ks = (struct kiss *) tty->disc_data; + + if (!ks || ks->magic != KISS_MAGIC || !ks->dev->start) + return; + + /* Read the characters out of the buffer */ + while (count--) { + if (fp && *fp++) { + if (!test_and_set_bit(KSF_ERROR, &ks->flags)) { + ks->rx_errors++; + } + cp++; + continue; + } + kiss_unesc(ks, *cp++); + } +} + +/************************************ + * kiss_open helper routines. + ************************************/ + +/* Collect hanged up channels */ + +static void kiss_sync(void) +{ + int i; + + for (i = 0; i < kiss_maxdev; i++) { + kiss_ctrl_t *ksp = kiss_ctrls[i]; + if (ksp == NULL) + break; + if (ksp->ctrl.tty) + continue; + if (ksp->dev.flags&IFF_UP) + dev_close(&ksp->dev); + } +} + +/* Find a free KISS channel, and link in this `tty' line. */ +static struct kiss * +kiss_alloc(kdev_t line) +{ + struct kiss *ks; + kiss_ctrl_t *ksp = NULL; + int i; + int sel = -1; + int score = -1; + + if (kiss_ctrls == NULL) + return NULL; /* Master array missing ! */ + + for (i = 0; i < kiss_maxdev; i++) { + ksp = kiss_ctrls[i]; + if (ksp == NULL) + break; + + if (ksp->ctrl.tty) + continue; + + if (current->pid == ksp->ctrl.pid) { + if (ksp->ctrl.line == line && score < 3) { + sel = i; + score = 3; + continue; + } + if (score < 2) { + sel = i; + score = 2; + } + continue; + } + if (ksp->ctrl.line == line && score < 1) { + sel = i; + score = 1; + continue; + } + if (score < 0) { + sel = i; + score = 0; + } + } + + if (sel >= 0) { + i = sel; + ksp = kiss_ctrls[i]; + if (score > 1) { + ksp->ctrl.flags &= (1 << KSF_INUSE); + return &ksp->ctrl; + } + } + + /* Sorry, too many, all slots in use */ + if (i >= kiss_maxdev) + return NULL; + + if (ksp) { + if (test_bit(KSF_INUSE, &ksp->ctrl.flags)) { + unregister_netdevice(&ksp->dev); + kiss_free_bufs(&ksp->ctrl); + } + } else if ((ksp = (kiss_ctrl_t *)kmalloc(sizeof(kiss_ctrl_t),GFP_KERNEL)) == NULL) + return NULL; + + memset(ksp, 0, sizeof(kiss_ctrl_t)); + + ks = &ksp->ctrl; + /* Initialize channel control data */ + ks->magic = KISS_MAGIC; + ks->dev = &ksp->dev; + ks->mode = KISS_MODE_ADAPTIVE; + sprintf(ksp->if_name, "ax%d", i); + ksp->dev.name = ksp->if_name; + ksp->dev.base_addr = i; + ksp->dev.priv = (void*)ks; + ksp->dev.init = kiss_init; + kiss_ctrls[i] = ksp; + return &ksp->ctrl; +} + +/* + * Open the high-level part of the KISS channel. + * This function is called by the TTY module when the + * KISS line discipline is called for. Because we are + * sure the tty line exists, we only have to link it to + * a free KISS channel... + */ +static int +kiss_open(struct tty_struct *tty) +{ + struct kiss *ks; + int err; + + MOD_INC_USE_COUNT; + + /* RTnetlink lock is misused here to serialize concurrent + opens of kiss channels. There are better ways, but it is + the simplest one. + */ + rtnl_lock(); + + /* Collect hanged up channels. */ + kiss_sync(); + + ks = (struct kiss *) tty->disc_data; + + err = -EEXIST; + /* First make sure we're not already connected. */ + if (ks && ks->magic == KISS_MAGIC) + goto err_exit; + + /* OK. Find a free KISS channel to use. */ + err = -ENFILE; + if ((ks = kiss_alloc(tty->device)) == NULL) + goto err_exit; + + ks->tty = tty; + tty->disc_data = ks; + ks->line = tty->device; + ks->pid = current->pid; + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + + if (!test_bit(KSF_INUSE, &ks->flags)) { + /* Perform the low-level KISS initialization. */ + if ((err = kiss_alloc_bufs(ks, KISS_MTU)) != 0) + goto err_free_chan; + + if (register_netdevice(ks->dev)) { + kiss_free_bufs(ks); + goto err_free_chan; + } + + set_bit(KSF_INUSE, &ks->flags); + } + + /* Done. We have linked the TTY line to a channel. */ + rtnl_unlock(); + return ks->dev->base_addr; + +err_free_chan: + ks->tty = NULL; + tty->disc_data = NULL; + clear_bit(KSF_INUSE, &ks->flags); + +err_exit: + printk(KERN_DEBUG "kiss_open: err:%d\n", err); + rtnl_unlock(); + + /* Count references from TTY module */ + MOD_DEC_USE_COUNT; + return err; +} + +/* + Let me to blame a bit. + 1. TTY module calls this funstion on soft interrupt. + 2. TTY module calls this function WITH MASKED INTERRUPTS! + 3. TTY module does not notify us about line discipline + shutdown, + + Seems, now it is clean. The solution is to consider netdevice and + line discipline sides as two independent threads. + + By-product (not desired): sl? does not feel hangups and remains open. + It is supposed, that user level program (dip, diald, slattach...) + will catch SIGHUP and make the rest of work. + + I see no way to make more with current tty code. --ANK + */ + +/* + * Close down a KISS channel. + * This means flushing out any pending queues, and then restoring the + * TTY line discipline to what it was before it got hooked to KISS + * (which usually is TTY again). + */ +static void +kiss_close(struct tty_struct *tty) +{ + struct kiss *ks = (struct kiss *) tty->disc_data; + + /* First make sure we're connected. */ + if (!ks || ks->magic != KISS_MAGIC || ks->tty != tty) + return; + + tty->disc_data = 0; + ks->tty = NULL; + ks->line = 0; + + /* Count references from TTY module */ + MOD_DEC_USE_COUNT; +} + + /************************************************************************ + * STANDARD KISS ENCAPSULATION * + ************************************************************************/ + +int +kiss_esc(unsigned char *s, unsigned char *d, int len) +{ + unsigned char *ptr = d; + unsigned char c; + + /* + * Send an initial END character to flush out any + * data that may have accumulated in the receiver + * due to line noise. + */ + + *ptr++ = END; + + /* + * For each byte in the packet, send the appropriate + * character sequence, according to the KISS protocol. + */ + + while (len-- > 0) { + switch(c = *s++) { + case END: + *ptr++ = ESC; + *ptr++ = ESC_END; + break; + case ESC: + *ptr++ = ESC; + *ptr++ = ESC_ESC; + break; + default: + *ptr++ = c; + break; + } + } + *ptr++ = END; + return (ptr - d); +} + +/* + * MW: + * OK its ugly, but tell me a better solution without copying the + * packet to a temporary buffer :-) + */ +static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, unsigned int len) +{ + unsigned char *ptr = d; + unsigned char c; + + *ptr++ = END; + while (len > 0) { + if (len > 2) + c = *s++; + else if (len > 1) + c = crc >> 8; + else if (len > 0) + c = crc & 0xff; + + len--; + + switch (c) { + case END: + *ptr++ = ESC; + *ptr++ = ESC_END; + break; + case ESC: + *ptr++ = ESC; + *ptr++ = ESC_ESC; + break; + default: + *ptr++ = c; + break; + } + } + *ptr++ = END; + return ptr - d; +} + +static void kiss_unesc(struct kiss *ks, unsigned char s) +{ + + switch(s) { + case END: + if (!test_and_clear_bit(KSF_ERROR, &ks->flags) && (ks->rcount > 2)) { + kiss_bump(ks); + } + clear_bit(KSF_ESCAPE, &ks->flags); + ks->rcount = 0; + return; + + case ESC: + set_bit(KSF_ESCAPE, &ks->flags); + return; + case ESC_ESC: + if (test_and_clear_bit(KSF_ESCAPE, &ks->flags)) { + s = ESC; + } + break; + case ESC_END: + if (test_and_clear_bit(KSF_ESCAPE, &ks->flags)) { + s = END; + } + break; + } + if (!test_bit(KSF_ERROR, &ks->flags)) { + if (ks->rcount < ks->buffsize) { + ks->rbuff[ks->rcount++] = s; + return; + } + ks->rx_over_errors++; + set_bit(KSF_ERROR, &ks->flags); + } +} + + +static int kiss_set_mac_address(struct device *dev, void *addr) +{ + if (copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN)) + return -EFAULT; + return 0; +} + +static int kiss_dev_set_mac_address(struct device *dev, void *addr) +{ + struct sockaddr *sa = addr; + + memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN); + + return 0; +} +/* Perform I/O control on an active KISS channel. */ +static int +kiss_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg) +{ + struct kiss *ks = (struct kiss *) tty->disc_data; + unsigned int tmp; + + /* First make sure we're connected. */ + if (!ks || ks->magic != KISS_MAGIC) { + return -EINVAL; + } + + switch(cmd) { + case SIOCGIFNAME: + tmp = strlen(ks->dev->name) + 1; + if (copy_to_user(arg, ks->dev->name, tmp)) + return -EFAULT; + return 0; + + case SIOCGIFENCAP: + if (put_user(ks->mode, (int *)arg)) + return -EFAULT; + return 0; + + case SIOCSIFENCAP: + if (get_user(tmp,(int *)arg)) + return -EFAULT; + if (tmp < 0 || tmp > KISS_MODE_COMPAT) + return -EINVAL; + ks->mode = tmp; + return 0; + + case SIOCSIFHWADDR: + return kiss_set_mac_address(ks->dev, arg); + + /* Allow stty to read, but not set, the serial port */ + case TCGETS: + case TCGETA: + return n_tty_ioctl(tty, (struct file *) file, cmd, (unsigned long) arg); + + default: + return -ENOIOCTLCMD; + } +} + +#ifdef KISS_DEV_IOCTL +static int +kiss_dev_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + struct kiss *ks = (struct kiss *)dev->priv; + + if (ks == NULL) + return -ENODEV; + + start_bh_atomic(); + + if (!ks->tty) { + end_bh_atomic(); + return -ENODEV; + } + + printk(KERN_DEBUG "ioctl %d called for dev %s\n", cmd, dev->name); + + switch(cmd) { + default: + return 0; + } +} +#endif + +/* Initialize KISS control device -- register KISS line discipline */ +#ifdef MODULE +static int kiss_init_ctrl_dev(void) +#else /* !MODULE */ +__initfunc(int kiss_init_ctrl_dev(struct device *dummy)) +#endif /* !MODULE */ +{ + int status; + + if (kiss_maxdev < 4) kiss_maxdev = 4; /* Sanity */ + + printk(KERN_INFO "KISS: version %s (dynamic channels, max=%d)\n", + KISS_VERSION, kiss_maxdev ); + + kiss_ctrls = (kiss_ctrl_t **) kmalloc(sizeof(void*)*kiss_maxdev, GFP_KERNEL); + if (kiss_ctrls == NULL) + { + printk("KISS: Can't allocate kiss_ctrls[] array! Uaargh! (-> No KISS available)\n"); + return -ENOMEM; + } + + /* Clear the pointer array, we allocate devices when we need them */ + memset(kiss_ctrls, 0, sizeof(void*)*kiss_maxdev); /* Pointers */ + + /* Fill in our line protocol discipline, and register it */ + memset(&kiss_ldisc, 0, sizeof(kiss_ldisc)); + kiss_ldisc.magic = TTY_LDISC_MAGIC; + kiss_ldisc.name = "kiss"; + kiss_ldisc.flags = 0; + kiss_ldisc.open = kiss_open; + kiss_ldisc.close = kiss_close; + kiss_ldisc.read = NULL; + kiss_ldisc.write = NULL; + kiss_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *, + unsigned int, unsigned long)) kiss_ioctl; + kiss_ldisc.poll = NULL; + kiss_ldisc.receive_buf = kiss_receive_buf; + kiss_ldisc.receive_room = kiss_receive_room; + kiss_ldisc.write_wakeup = kiss_write_wakeup; + if ((status = tty_register_ldisc(N_KISS, &kiss_ldisc)) != 0) { + printk("KISS: can't register line discipline (err = %d)\n", status); + } + + +#ifdef MODULE + return status; +#else + /* Return "not found", so that dev_init() will unlink + * the placeholder device entry for us. + */ + return ENODEV; +#endif + } + + + +#ifdef MODULE + +int +init_module(void) +{ + return kiss_init_ctrl_dev(); +} + +void +cleanup_module(void) +{ + int i; + + if (kiss_ctrls != NULL) { + unsigned long start = jiffies; + int busy = 0; + + /* First of all: check for active disciplines and hangup them. + */ + do { + if (busy) { + current->counter = 0; + schedule(); + } + + busy = 0; + start_bh_atomic(); + for (i = 0; i < kiss_maxdev; i++) { + struct kiss_ctrl *ksc = kiss_ctrls[i]; + if (ksc && ksc->ctrl.tty) { + busy++; + tty_hangup(ksc->ctrl.tty); + } + } + end_bh_atomic(); + } while (busy && jiffies - start < 1*HZ); + + busy = 0; + for (i = 0; i < kiss_maxdev; i++) { + struct kiss_ctrl *ksc = kiss_ctrls[i]; + if (ksc) { + unregister_netdev(&ksc->dev); + if (ksc->ctrl.tty) { + printk("%s: tty discipline is still running\n", ksc->dev.name); + /* Pin module forever */ + MOD_INC_USE_COUNT; + busy++; + continue; + } + kiss_free_bufs(&ksc->ctrl); + kfree(ksc); + kiss_ctrls[i] = NULL; + } + } + if (!busy) { + kfree(kiss_ctrls); + kiss_ctrls = NULL; + } + } + if ((i = tty_register_ldisc(N_KISS, NULL))) + { + printk("KISS: can't unregister line discipline (err = %d)\n", i); + } +} +#endif /* MODULE */ Index: linux/drivers/net/hamradio/kiss.h diff -u /dev/null linux/drivers/net/hamradio/kiss.h:1.1.4.1 --- /dev/null Wed Jul 7 01:01:22 1999 +++ linux/drivers/net/hamradio/kiss.h Sun Jul 4 11:07:21 1999 @@ -0,0 +1,92 @@ +/* + * kiss.h Define the KISS device driver interface and constants. + * + * NOTE: THIS FILE WILL BE MOVED TO THE LINUX INCLUDE DIRECTORY + * AS SOON AS POSSIBLE! + * + * Version: @(#)kiss.h 1.2.0 03/28/93 + * + * Fixes: + * Alan Cox : Added slip mtu field. + * Matt Dillon : Printable slip (borrowed from net2e) + * Alan Cox : Added SL_SLIP_LOTS + * Dmitry Gorodchanin : A lot of changes in the 'struct slip' + * Dmitry Gorodchanin : Added CSLIP statistics. + * Stanislav Voronyi : Make line checking as created by + * Igor Chechik, RELCOM Corp. + * Craig Schlenter : Fixed #define bug that caused + * CSLIP telnets to hang in 1.3.61-6 + * + * Author: Fred N. van Kempen, + */ +#ifndef _LINUX_KISS_H +#define _LINUX_KISS_H + +#include + +/* KISS configuration. */ +#define KISS_NRUNIT 256 /* MAX number of KISS channels; + This can be overridden with + insmod -oslip_maxdev=nnn */ +#define KISS_MTU (256+MAX_HEADER) + +/* KISS protocol characters. */ +#define END 0300 /* indicates end of frame */ +#define ESC 0333 /* indicates byte stuffing */ +#define ESC_END 0334 /* ESC ESC_END means END 'data' */ +#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ + + +struct kiss { + int magic; + + /* Various fields. */ + struct tty_struct *tty; /* ptr to TTY structure */ + struct device *dev; /* easy for intr handling */ + + /* These are pointers to the malloc()ed frame buffers. */ + unsigned char *rbuff; /* receiver buffer */ + int rcount; /* received chars counter */ + unsigned char *xbuff; /* transmitter buffer */ + unsigned char *xhead; /* pointer to next byte to XMIT */ + int xleft; /* bytes left in XMIT queue */ + + /* KISS interface statistics. */ + unsigned long rx_packets; /* inbound frames counter */ + unsigned long tx_packets; /* outbound frames counter */ + unsigned long rx_bytes; /* inbound byte counte */ + unsigned long tx_bytes; /* outbound byte counter */ + unsigned long rx_errors; /* Parity, etc. errors */ + unsigned long tx_errors; /* Planned stuff */ + unsigned long rx_dropped; /* No memory for skb */ + unsigned long tx_dropped; /* When MTU change */ + unsigned long rx_over_errors; /* Frame bigger then KISS buf. */ + + int mtu; /* Our mtu (to spot changes!) */ + int buffsize; /* Max buffers sizes */ + + unsigned int flags; /* Flag values/ mode etc */ +#define KSF_INUSE 0 /* Channel in use */ +#define KSF_ESCAPE 1 /* ESC received */ +#define KSF_ERROR 2 /* Parity, etc. error */ + + unsigned char mode; /* KISS mode */ +#define KISS_MODE_NORMAL 0 +#define KISS_MODE_ADAPTIVE 1 +#define KISS_MODE_FLEX 2 +#define KISS_MODE_SMACK 3 +#define KISS_MODE_COMPAT 4 /* for old kissattach binaries */ + kdev_t line; + pid_t pid; + + struct ax25_dev ax25dev; + +}; + + + +#define KISS_MAGIC 0x5303 + +/* extern int kiss_init(struct device *dev); */ + +#endif /* _LINUX_KISS.H */ Index: linux/drivers/net/hamradio/mkiss.c diff -u linux/drivers/net/hamradio/mkiss.c:1.1.2.1 linux/drivers/net/hamradio/mkiss.c:1.1.2.1.2.1 --- linux/drivers/net/hamradio/mkiss.c:1.1.2.1 Sun Jul 4 10:15:05 1999 +++ linux/drivers/net/hamradio/mkiss.c Sun Jul 4 11:07:22 1999 @@ -18,10 +18,9 @@ * Hans Alblas * * History - * Jonathan (G4KLX) Fixed to match Linux networking changes - 2.1.15. - * Matthias (DG2FEF) Added support for FlexNet CRC (on special request) - * Fixed bug in ax25_close(): dev_lock_wait() was - * called twice, causing a deadlock. + * 06/21/98 Matthias Welwarsky (DG2FEF) Changed to new driver interface, + * implemented FlexNet-CRC. + * */ #include @@ -48,6 +47,7 @@ #include #include +#include #include "mkiss.h" @@ -94,7 +94,7 @@ static int mkiss_init(void); static int mkiss_write(struct tty_struct *, int, const unsigned char *, int); static int kiss_esc(unsigned char *, unsigned char *, int); -static int kiss_esc_crc(unsigned char *, unsigned char *, unsigned short, int); +static int kiss_esc_crc(unsigned char *, unsigned char *, unsigned short, unsigned int); static void kiss_unesc(struct ax_disp *, unsigned char); /*---------------------------------------------------------------------------*/ @@ -169,7 +169,7 @@ /*---------------------------------------------------------------------------*/ /* Find a free channel, and link in this `tty' line. */ -static inline struct ax_disp *ax_alloc(void) +static struct ax_disp *ax_alloc(void) { ax25_ctrl_t *axp; int i; @@ -232,7 +232,7 @@ } /* Free an AX25 channel. */ -static inline void ax_free(struct ax_disp *ax) +static void ax_free(struct ax_disp *ax) { /* Free all AX25 frame buffers. */ if (ax->rbuff) @@ -248,9 +248,8 @@ static void ax_changedmtu(struct ax_disp *ax) { struct device *dev = ax->dev; - unsigned char *xbuff, *rbuff, *oxbuff, *orbuff; + unsigned char *xbuff, *rbuff; int len; - unsigned long flags; len = dev->mtu * 2; @@ -276,13 +275,10 @@ return; } - save_flags(flags); - cli(); + start_bh_atomic(); - oxbuff = ax->xbuff; - ax->xbuff = xbuff; - orbuff = ax->rbuff; - ax->rbuff = rbuff; + xbuff = xchg(&ax->xbuff, xbuff); + rbuff = xchg(&ax->rbuff, rbuff); if (ax->xleft) { if (ax->xleft <= len) { @@ -297,7 +293,7 @@ if (ax->rcount) { if (ax->rcount <= len) { - memcpy(ax->rbuff, orbuff, ax->rcount); + memcpy(ax->rbuff, rbuff, ax->rcount); } else { ax->rcount = 0; ax->rx_over_errors++; @@ -308,15 +304,15 @@ ax->mtu = dev->mtu + 73; ax->buffsize = len; - restore_flags(flags); + end_bh_atomic(); - if (oxbuff != NULL) - kfree(oxbuff); - if (orbuff != NULL) - kfree(orbuff); + if (xbuff != NULL) + kfree(xbuff); + if (rbuff != NULL) + kfree(rbuff); } - +#if 0 /* Set the "sending" flag. This must be atomic, hence the ASM. */ static inline void ax_lock(struct ax_disp *ax) { @@ -331,6 +327,7 @@ if (!test_and_clear_bit(0, (void *)&ax->dev->tbusy)) printk(KERN_ERR "mkiss: %s: trying to unlock already unlocked device!\n", ax->dev->name); } +#endif /* Send one completely decapsulated AX.25 packet to the AX.25 layer. */ static void ax_bump(struct ax_disp *ax) @@ -357,16 +354,19 @@ } } - count = ax->rcount; + count = ax->rcount-1; - if ((skb = dev_alloc_skb(count)) == NULL) { + /* allocate an skb, leave some headroom */ + if ((skb = dev_alloc_skb(count + MAX_HEADER)) == NULL) { printk(KERN_ERR "mkiss: %s: memory squeeze, dropping packet.\n", ax->dev->name); ax->rx_dropped++; return; } + skb_reserve(skb, MAX_HEADER); skb->dev = tmp_ax->dev; - memcpy(skb_put(skb,count), ax->rbuff, count); + /* copy the buffer without the leading KISS header */ + memcpy(skb_put(skb,count), ax->rbuff+1, count); skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_AX25); netif_rx(skb); @@ -384,16 +384,15 @@ ax_changedmtu(ax); if (len > ax->mtu) { /* Sigh, shouldn't occur BUT ... */ - len = ax->mtu; printk(KERN_ERR "mkiss: %s: truncating oversized transmit packet!\n", ax->dev->name); ax->tx_dropped++; - ax_unlock(ax); +/* ax_unlock(ax); */ return; } p = icp; - if (mkiss->magic != MKISS_DRIVER_MAGIC) { + if (!mkiss || mkiss->magic != MKISS_DRIVER_MAGIC) { switch (ax->crcmode) { unsigned short crc; @@ -443,13 +442,13 @@ */ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - if (ax->mkiss != NULL) { - mkiss= ax->mkiss->tty->driver_data; - if (mkiss->magic == MKISS_DRIVER_MAGIC) - ax_unlock(ax->mkiss); - } +/* if (ax->mkiss != NULL) { */ +/* mkiss= ax->mkiss->tty->driver_data; */ +/* if (mkiss && mkiss->magic == MKISS_DRIVER_MAGIC) */ +/* ax_unlock(ax->mkiss); */ +/* } */ +/* ax_unlock(ax); */ - ax_unlock(ax); mark_bh(NET_BH); return; } @@ -467,52 +466,59 @@ struct ax_disp *tmp_ax; tmp_ax = NULL; + + if (skb_headroom(skb) > 0) + *skb_push(skb, 1) = 0; /* prepend KISS header */ + else { + kfree_skb(skb); + return 0; + } - if (mkiss->magic == MKISS_DRIVER_MAGIC) { - if (skb->data[0] < 0x10) - skb->data[0] = skb->data[0] + 0x10; + if (mkiss && mkiss->magic == MKISS_DRIVER_MAGIC) { + skb->data[0] = skb->data[0] + 0x10; tmp_ax = ax->mkiss; } if (!dev->start) { printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name); - return 1; + kfree_skb(skb); + return 0; } - - if (tmp_ax != NULL) - if (tmp_ax->dev->tbusy) - return 1; - - if (tmp_ax != NULL) - if (dev->tbusy) { - printk(KERN_ERR "mkiss: dev busy while serial dev is free\n"); - ax_unlock(ax); - } - if (dev->tbusy) { - /* - * May be we must check transmitter timeout here ? - * 14 Oct 1994 Dmitry Gorodchanin. - */ - if (jiffies - dev->trans_start < 20 * HZ) { - /* 20 sec timeout not reached */ - return 1; - } - - printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name, - (ax->tty->driver.chars_in_buffer(ax->tty) || ax->xleft) ? - "bad line quality" : "driver error"); - - ax->xleft = 0; - ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - ax_unlock(ax); - } +/* if (tmp_ax != NULL) { */ +/* if (tmp_ax->dev->tbusy) */ +/* return 1; */ + +/* if (dev->tbusy) { */ +/* printk(KERN_ERR "mkiss: dev busy while serial dev is free\n"); */ +/* ax_unlock(ax); */ +/* } */ +/* } */ + +/* if (dev->tbusy) { */ +/* /* */ +/* * May be we must check transmitter timeout here ? */ +/* * 14 Oct 1994 Dmitry Gorodchanin. */ +/* */ +/* if (jiffies - dev->trans_start < 20 * HZ) { */ +/* /* 20 sec timeout not reached */ +/* return 1; */ +/* } */ + +/* printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name, */ +/* (ax->tty->driver.chars_in_buffer(ax->tty) || ax->xleft) ? */ +/* "bad line quality" : "driver error"); */ + +/* ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); */ +/* ax->xleft = 0; */ +/* ax_unlock(ax); */ +/* } */ /* We were not busy, so we are now... :-) */ if (skb != NULL) { - ax_lock(ax); - if (tmp_ax != NULL) - ax_lock(tmp_ax); +/* ax_lock(ax); */ +/* if (tmp_ax != NULL) */ +/* ax_lock(tmp_ax); */ ax_encaps(ax, skb->data, skb->len); kfree_skb(skb); } @@ -520,31 +526,6 @@ return 0; } -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - -/* Return the frame type ID */ -static int ax_header(struct sk_buff *skb, struct device *dev, unsigned short type, - void *daddr, void *saddr, unsigned len) -{ -#ifdef CONFIG_INET - if (type != htons(ETH_P_AX25)) - return ax25_encapsulate(skb, dev, type, daddr, saddr, len); -#endif - return 0; -} - - -static int ax_rebuild_header(struct sk_buff *skb) -{ -#ifdef CONFIG_INET - return ax25_rebuild_header(skb); -#else - return 0; -#endif -} - -#endif /* CONFIG_{AX25,AX25_MODULE} */ - /* Open the low-level part of the AX25 channel. Easy! */ static int ax_open(struct device *dev) { @@ -586,11 +567,11 @@ dev->tbusy = 0; dev->start = 1; + ax25_dev_setbitrate(dev, 76800); + return 0; /* Cleanup */ - kfree(ax->xbuff); - noxbuff: kfree(ax->rbuff); @@ -686,9 +667,9 @@ if ((err = ax_open(ax->dev))) return err; - mkiss= ax->tty->driver_data; + mkiss = ax->tty->driver_data; - if (mkiss->magic == MKISS_DRIVER_MAGIC) { + if (mkiss && mkiss->magic == MKISS_DRIVER_MAGIC) { for (cnt = 1; cnt < ax25_maxdev; cnt++) { if (ax25_ctrls[cnt]) { if (ax25_ctrls[cnt]->dev.start) { @@ -799,7 +780,7 @@ * OK its ugly, but tell me a better solution without copying the * packet to a temporary buffer :-) */ -static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, int len) +static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, unsigned int len) { unsigned char *ptr = d; unsigned char c; @@ -838,8 +819,7 @@ switch (s) { case END: /* drop keeptest bit = VSV */ - if (test_bit(AXF_KEEPTEST, &ax->flags)) - clear_bit(AXF_KEEPTEST, &ax->flags); + clear_bit(AXF_KEEPTEST, &ax->flags); if (!test_and_clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2)) ax_bump(ax); @@ -914,7 +894,7 @@ get_user(tmp, (int *)arg); ax->mode = tmp; ax->dev->addr_len = AX25_ADDR_LEN; /* sizeof an AX.25 addr */ - ax->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3; + ax->dev->hard_header_len = AX25_MAX_HEADER_LEN; ax->dev->type = ARPHRD_AX25; return 0; @@ -983,6 +963,10 @@ #endif } +static unsigned int dummy_return(struct device* dev) +{ + return 0; +} /* Initialize the driver. Called by network startup. */ @@ -1000,8 +984,16 @@ /* Set up the "AX25 Control Block". (And clear statistics) */ memset(ax, 0, sizeof (struct ax_disp)); - ax->magic = AX25_MAGIC; - ax->dev = dev; + ax->magic = AX25_MAGIC; + ax->dev = dev; + + /* initialise DDI interface values */ + ax25_dev_initdev(dev, &ax->ax25_device); + ax25_dev_setpttfunc(dev, dummy_return); + ax25_dev_setdcdfunc(dev, dummy_return); + ax25_dev_setbitrate(dev, 76800); /* FIXME: this is wrong, too, but ok for now */ + ax25_dev_setduplex(dev, 1); + ax25_dev_setfast(dev, 1); /* skip channel arbitration stuff */ /* Finish setting up the DEVICE info. */ dev->mtu = AX_MTU; @@ -1020,15 +1012,10 @@ memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN); -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - dev->hard_header = ax_header; - dev->rebuild_header = ax_rebuild_header; -#endif - dev_init_buffers(dev); /* New-style flags. */ - dev->flags = 0; + dev->flags = IFF_BROADCAST|IFF_MULTICAST; return 0; } Index: linux/drivers/net/hamradio/mkiss.h diff -u linux/drivers/net/hamradio/mkiss.h:1.1.2.1 linux/drivers/net/hamradio/mkiss.h:1.1.2.1.2.1 --- linux/drivers/net/hamradio/mkiss.h:1.1.2.1 Sun Jul 4 10:15:05 1999 +++ linux/drivers/net/hamradio/mkiss.h Sun Jul 4 11:07:22 1999 @@ -21,6 +21,8 @@ struct device *dev; /* easy for intr handling */ struct ax_disp *mkiss; /* mkiss txport if mkiss channel*/ + struct ax25_dev ax25_device; + /* These are pointers to the malloc()ed frame buffers. */ unsigned char *rbuff; /* receiver buffer */ int rcount; /* received chars counter */ Index: linux/drivers/net/hamradio/soundmodem/sm.h diff -u linux/drivers/net/hamradio/soundmodem/sm.h:1.1.2.1 linux/drivers/net/hamradio/soundmodem/sm.h:1.1.2.1.2.1 --- linux/drivers/net/hamradio/soundmodem/sm.h:1.1.2.1 Sun Jul 4 10:15:06 1999 +++ linux/drivers/net/hamradio/soundmodem/sm.h Sun Jul 4 11:07:54 1999 @@ -30,6 +30,7 @@ /* ---------------------------------------------------------------------- */ +#include #include #include #include Index: linux/include/asm-alpha/termios.h diff -u linux/include/asm-alpha/termios.h:1.1.2.1 linux/include/asm-alpha/termios.h:1.1.2.1.2.1 --- linux/include/asm-alpha/termios.h:1.1.2.1 Sun Jul 4 10:16:47 1999 +++ linux/include/asm-alpha/termios.h Sun Jul 4 11:32:28 1999 @@ -71,7 +71,8 @@ #define N_SLIP 1 #define N_MOUSE 2 #define N_PPP 3 -#define N_AX25 5 +#define N_KISS 5 +#define N_AX25 N_KISS #define N_X25 6 /* X.25 async */ #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module */ Index: linux/include/asm-arm/termios.h diff -u linux/include/asm-arm/termios.h:1.1.2.1 linux/include/asm-arm/termios.h:1.1.2.1.2.1 --- linux/include/asm-arm/termios.h:1.1.2.1 Sun Jul 4 10:16:51 1999 +++ linux/include/asm-arm/termios.h Sun Jul 4 11:32:28 1999 @@ -52,7 +52,8 @@ #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 +#define N_AX25 N_KISS #define N_X25 6 /* X.25 async */ #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module */ Index: linux/include/asm-i386/termios.h diff -u linux/include/asm-i386/termios.h:1.1.2.1 linux/include/asm-i386/termios.h:1.1.2.1.2.1 --- linux/include/asm-i386/termios.h:1.1.2.1 Sun Jul 4 10:17:01 1999 +++ linux/include/asm-i386/termios.h Sun Jul 4 11:32:28 1999 @@ -44,7 +44,8 @@ #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 +#define N_AX25 N_KISS #define N_X25 6 /* X.25 async */ #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module */ Index: linux/include/asm-m68k/termios.h diff -u linux/include/asm-m68k/termios.h:1.1.2.1 linux/include/asm-m68k/termios.h:1.1.2.1.2.1 --- linux/include/asm-m68k/termios.h:1.1.2.1 Sun Jul 4 10:17:07 1999 +++ linux/include/asm-m68k/termios.h Sun Jul 4 11:32:29 1999 @@ -52,7 +52,8 @@ #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 +#define N_AX25 N_KISS #define N_X25 6 /* X.25 async */ #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module */ Index: linux/include/asm-mips/termios.h diff -u linux/include/asm-mips/termios.h:1.1.2.1 linux/include/asm-mips/termios.h:1.1.2.1.2.1 --- linux/include/asm-mips/termios.h:1.1.2.1 Sun Jul 4 10:17:12 1999 +++ linux/include/asm-mips/termios.h Sun Jul 4 11:32:29 1999 @@ -90,7 +90,8 @@ #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 +#define N_AX25 N_KISS #define N_X25 6 /* X.25 async */ #define N_6PACK 7 #define N_MASC 8 /* Reserved fo Mobitex module */ Index: linux/include/asm-ppc/termios.h diff -u linux/include/asm-ppc/termios.h:1.1.2.1 linux/include/asm-ppc/termios.h:1.1.2.1.2.1 --- linux/include/asm-ppc/termios.h:1.1.2.1 Sun Jul 4 10:17:17 1999 +++ linux/include/asm-ppc/termios.h Sun Jul 4 11:32:29 1999 @@ -176,7 +176,8 @@ #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 +#define N_AX25 N_KISS #define N_X25 6 /* X.25 async */ #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module */ Index: linux/include/asm-sparc/termios.h diff -u linux/include/asm-sparc/termios.h:1.1.2.1 linux/include/asm-sparc/termios.h:1.1.2.1.2.1 --- linux/include/asm-sparc/termios.h:1.1.2.1 Sun Jul 4 10:17:24 1999 +++ linux/include/asm-sparc/termios.h Sun Jul 4 11:32:29 1999 @@ -60,7 +60,8 @@ #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 +#define N_AX25 N_KISS #define N_X25 6 #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module */ Index: linux/include/asm-sparc64/termios.h diff -u linux/include/asm-sparc64/termios.h:1.1.2.1 linux/include/asm-sparc64/termios.h:1.1.2.1.2.1 --- linux/include/asm-sparc64/termios.h:1.1.2.1 Sun Jul 4 10:17:31 1999 +++ linux/include/asm-sparc64/termios.h Sun Jul 4 11:32:29 1999 @@ -60,7 +60,8 @@ #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 +#define N_AX25 N_KISS #define N_X25 6 #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module */ Index: linux/include/linux/ax25.h diff -u linux/include/linux/ax25.h:1.1.2.1 linux/include/linux/ax25.h:1.1.2.1.2.1 --- linux/include/linux/ax25.h:1.1.2.1 Sun Jul 4 10:17:32 1999 +++ linux/include/linux/ax25.h Sun Jul 4 11:32:29 1999 @@ -2,12 +2,14 @@ * These are the public elements of the Linux kernel AX.25 code. A similar * file netrom.h exists for the NET/ROM protocol. */ - + #ifndef AX25_KERNEL_H #define AX25_KERNEL_H + +#define AX25_NEW_DEVIF 1 #define AX25_MTU 256 -#define AX25_MAX_DIGIS 8 +#define AX25_MAX_DIGIS 8 #define AX25_WINDOW 1 #define AX25_T1 2 @@ -32,6 +34,10 @@ #define SIOCAX25GETINFO (SIOCPROTOPRIVATE+9) #define SIOCAX25ADDFWD (SIOCPROTOPRIVATE+10) #define SIOCAX25DELFWD (SIOCPROTOPRIVATE+11) +#define SIOCAX25DEVCTL (SIOCPROTOPRIVATE+12) + +#define AX25_SET_DEV_BITRATE 0 +#define AX25_SET_DEV_MODE 1 #define AX25_SET_RT_IPMODE 2 @@ -56,6 +62,59 @@ ax25_address fsa_digipeater[AX25_MAX_DIGIS]; }; +#define fsax25_family fsa_ax25.sax25_family +#define fsax25_call fsa_ax25.sax25_call +#define fsax25_ndigis fsa_ax25.sax25_ndigis + +typedef struct { + ax25_address addr; + ax25_address digipeater[AX25_MAX_DIGIS];; + unsigned char dcount; +} ax25_path_t; + +typedef struct { + ax25_address dest; + ax25_address src; + ax25_address digipeater[AX25_MAX_DIGIS]; + unsigned char dcount; + char lastrepeat; +} ax25_addr_t; + +struct ax25_rtmsg { + char port_name[32]; + unsigned int mode; + ax25_addr_t addr; +}; + +struct ax25_armsg { + char port_name[32]; + unsigned int ip_addr; + ax25_address ax_addr; +}; + +struct ax25_pathmsg { + char port_name[32]; + unsigned int mode; + ax25_path_t path; +}; + +struct ax25_nlmsg { + +#define AX25_MSG_RTINFO 0 +#define AX25_MSG_ARINFO 1 +#define AX25_MSG_SETRT 2 +#define AX25_MSG_DELRT 3 +#define AX25_MSG_OPTRT 4 + unsigned int msg_type; + + union { + unsigned char raw[128-sizeof(unsigned int)]; + struct ax25_rtmsg rtmsg; + struct ax25_armsg armsg; + struct ax25_pathmsg pathmsg; + } msg; +}; + struct ax25_routes_struct { ax25_address port_addr; ax25_address dest_addr; @@ -71,13 +130,11 @@ }; struct ax25_ctl_struct { - ax25_address port_addr; - ax25_address source_addr; - ax25_address dest_addr; - unsigned int cmd; - unsigned long arg; - unsigned char digi_count; - ax25_address digi_addr[AX25_MAX_DIGIS]; + ax25_address port_addr; + ax25_address source_addr; + ax25_address dest_addr; + unsigned int cmd; + unsigned long arg; }; struct ax25_info_struct { Index: linux/include/linux/hdlcdrv.h diff -u linux/include/linux/hdlcdrv.h:1.1.2.1 linux/include/linux/hdlcdrv.h:1.1.2.1.2.1 --- linux/include/linux/hdlcdrv.h:1.1.2.1 Sun Jul 4 10:17:38 1999 +++ linux/include/linux/hdlcdrv.h Sun Jul 4 11:32:31 1999 @@ -110,7 +110,7 @@ #define HDLCDRV_MAGIC 0x5ac6e778 #define HDLCDRV_IFNAMELEN 6 -#define HDLCDRV_HDLCBUFFER 32 /* should be a power of 2 for speed reasons */ +#define HDLCDRV_HDLCBUFFER 128 /* should be a power of 2 for speed reasons */ #define HDLCDRV_BITBUFFER 256 /* should be a power of 2 for speed reasons */ #undef HDLCDRV_LOOPBACK /* define for HDLC debugging purposes */ #define HDLCDRV_DEBUG @@ -237,6 +237,8 @@ unsigned char *bp; unsigned char buffer[HDLCDRV_MAXFLEN+2]; } hdlctx; + + struct ax25_dev dev; #ifdef HDLCDRV_DEBUG struct hdlcdrv_bitbuffer bitbuf_channel; Index: linux/include/linux/netdevice.h diff -u linux/include/linux/netdevice.h:1.1.2.1 linux/include/linux/netdevice.h:1.1.2.1.2.1 --- linux/include/linux/netdevice.h:1.1.2.1 Sun Jul 4 10:17:44 1999 +++ linux/include/linux/netdevice.h Sun Jul 4 11:32:31 1999 @@ -262,6 +262,7 @@ void *ip_ptr; /* IPv4 specific data */ void *dn_ptr; /* DECnet specific data */ void *ip6_ptr; /* IPv6 specific data */ + void *ax25_ptr; /* AX.25 specific data */ struct Qdisc *qdisc; struct Qdisc *qdisc_sleeping; Index: linux/include/linux/netlink.h diff -u linux/include/linux/netlink.h:1.1.2.1 linux/include/linux/netlink.h:1.1.2.1.2.1 --- linux/include/linux/netlink.h:1.1.2.1 Sun Jul 4 10:17:44 1999 +++ linux/include/linux/netlink.h Sun Jul 4 11:32:31 1999 @@ -5,6 +5,7 @@ #define NETLINK_SKIP 1 /* Reserved for ENskip */ #define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ #define NETLINK_FIREWALL 3 /* Firewalling hook */ +#define NETLINK_AX25 4 /* AX25 routing daemon hook */ #define NETLINK_ARPD 8 #define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */ #define NETLINK_IP6_FW 13 Index: linux/include/linux/proc_fs.h diff -u linux/include/linux/proc_fs.h:1.1.2.1 linux/include/linux/proc_fs.h:1.1.2.1.2.1 --- linux/include/linux/proc_fs.h:1.1.2.1 Sun Jul 4 10:17:46 1999 +++ linux/include/linux/proc_fs.h Sun Jul 4 11:32:31 1999 @@ -111,6 +111,7 @@ PROC_NET_AX25_ROUTE, PROC_NET_AX25, PROC_NET_AX25_CALLS, + PROC_NET_AX25_PORTS, /* XXX */ PROC_NET_BMAC, PROC_NET_NR_NODES, PROC_NET_NR_NEIGH, Index: linux/include/net/ax25.h diff -u linux/include/net/ax25.h:1.1.2.1 linux/include/net/ax25.h:1.1.2.1.2.1 --- linux/include/net/ax25.h:1.1.2.1 Sun Jul 4 10:17:54 1999 +++ linux/include/net/ax25.h Sun Jul 4 11:32:31 1999 @@ -6,19 +6,27 @@ #ifndef _AX25_H #define _AX25_H -#include #include -#define AX25_T1CLAMPLO 1 -#define AX25_T1CLAMPHI (30 * HZ) +#define AX25_TICS (HZ/10) /* AX25 timertic is 1/10 sec (100 ms) */ +#define AX25_SLOWHZ 10 /* 10 timertics, 1 second */ +#define AX25_T1CLAMPLO AX25_SLOWHZ /* 1 sec */ +#define AX25_T1CLAMPHI (30 * AX25_SLOWHZ) /* 30 secs */ + #define AX25_BPQ_HEADER_LEN 16 -#define AX25_KISS_HEADER_LEN 1 -#define AX25_HEADER_LEN 17 #define AX25_ADDR_LEN 7 -#define AX25_DIGI_HEADER_LEN (AX25_MAX_DIGIS * AX25_ADDR_LEN) -#define AX25_MAX_HEADER_LEN (AX25_HEADER_LEN + AX25_DIGI_HEADER_LEN) +#define AX25_MIN_HEADER_LEN (AX25_ADDR_LEN*2+1) /* Source, Destination, Control */ +#define AX25_MAX_HEADER_LEN (AX25_MIN_HEADER_LEN+1+AX25_ADDR_LEN*AX25_MAX_DIGIS) /* ... including Digipeaters */ + +/* + * these are obsolete + * + * #define AX25_KISS_HEADER_LEN 1 + * #define AX25_HEADER_LEN 17 + * #define AX25_DIGI_HEADER_LEN (AX25_MAX_DIGIS * AX25_ADDR_LEN) + */ /* AX.25 Protocol IDs */ #define AX25_P_ROSE 0x01 @@ -27,6 +35,8 @@ #define AX25_P_TEXT 0xF0 #define AX25_P_NETROM 0xCF #define AX25_P_SEGMENT 0x08 +#define AX25_P_VJCOMP 0x06 /* Matthias Welwarsky (DG2FEF) */ +#define AX25_P_VJUNCOMP 0x07 /* Matthias Welwarsky (DG2FEF) */ /* AX.25 Segment control values */ #define AX25_SEG_REM 0x7F @@ -38,18 +48,45 @@ #define AX25_SSSID_SPARE 0x60 /* Unused bits in SSID for standard AX.25 */ #define AX25_ESSID_SPARE 0x20 /* Unused bits in SSID for extended AX.25 */ -#define AX25_DAMA_FLAG 0x20 /* Well, it is *NOT* unused! (dl1bke 951121 */ +#define AX25_DAMA_FLAG 0x20 /* Well, it is *NOT* unused! (dl1bke 951121) */ #define AX25_COND_ACK_PENDING 0x01 #define AX25_COND_REJECT 0x02 #define AX25_COND_PEER_RX_BUSY 0x04 #define AX25_COND_OWN_RX_BUSY 0x08 -#define AX25_COND_DAMA_MODE 0x10 +#define AX25_COND_STATE_CHANGE 0x10 +#define AX25_COND_RELEASE 0x20 +#define AX25_COND_SETUP 0x40 +#define AX25_COND_START_T1 0x80 + +#define AX25_SCHED_IDLE 0 +#define AX25_SCHED_READY 1 #ifndef _LINUX_NETDEVICE_H #include #endif +/* + * These headers are taken from the KA9Q package by Phil Karn. These specific + * files have been placed under the GPL (not the whole package) by Phil. + * + * + * Copyright 1991 Phil Karn, KA9Q + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 dated June, 1991. + * + * 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. + */ + /* Upper sub-layer (LAPB) definitions */ /* Control field templates */ @@ -81,269 +118,115 @@ /* Define Link State constants. */ -enum { - AX25_STATE_0, +enum { + AX25_STATE_0 = 0, AX25_STATE_1, AX25_STATE_2, AX25_STATE_3, - AX25_STATE_4 + AX25_STATE_4, + AX25_LISTEN }; - -#define AX25_MODULUS 8 /* Standard AX.25 modulus */ -#define AX25_EMODULUS 128 /* Extended AX.25 modulus */ -enum { - AX25_PROTO_STD_SIMPLEX, - AX25_PROTO_STD_DUPLEX, - AX25_PROTO_DAMA_SLAVE, - AX25_PROTO_DAMA_MASTER -}; - -enum { - AX25_VALUES_IPDEFMODE, /* 0=DG 1=VC */ - AX25_VALUES_AXDEFMODE, /* 0=Normal 1=Extended Seq Nos */ - AX25_VALUES_BACKOFF, /* 0=None 1=Linear 2=Exponential */ - AX25_VALUES_CONMODE, /* Allow connected modes - 0=No 1=no "PID text" 2=all PIDs */ - AX25_VALUES_WINDOW, /* Default window size for standard AX.25 */ - AX25_VALUES_EWINDOW, /* Default window size for extended AX.25 */ - AX25_VALUES_T1, /* Default T1 timeout value */ - AX25_VALUES_T2, /* Default T2 timeout value */ - AX25_VALUES_T3, /* Default T3 timeout value */ - AX25_VALUES_IDLE, /* Connected mode idle timer */ - AX25_VALUES_N2, /* Default N2 value */ - AX25_VALUES_PACLEN, /* AX.25 MTU */ - AX25_VALUES_PROTOCOL, /* Std AX.25, DAMA Slave, DAMA Master */ - AX25_VALUES_DS_TIMEOUT, /* DAMA Slave timeout */ - AX25_MAX_VALUES /* THIS MUST REMAIN THE LAST ENTRY OF THIS LIST */ -}; +typedef enum { + AX25_SEQMASK = 7, + AX25_ESEQMASK = 127 +} ax25_seqmask_t; -#define AX25_DEF_IPDEFMODE 0 /* Datagram */ -#define AX25_DEF_AXDEFMODE 0 /* Normal */ -#define AX25_DEF_BACKOFF 1 /* Linear backoff */ -#define AX25_DEF_CONMODE 2 /* Connected mode allowed */ -#define AX25_DEF_WINDOW 2 /* Window=2 */ -#define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */ -#define AX25_DEF_T1 (10 * HZ) /* T1=10s */ -#define AX25_DEF_T2 (3 * HZ) /* T2=3s */ -#define AX25_DEF_T3 (300 * HZ) /* T3=300s */ -#define AX25_DEF_N2 10 /* N2=10 */ -#define AX25_DEF_IDLE (0 * 60 * HZ) /* Idle=None */ -#define AX25_DEF_PACLEN 256 /* Paclen=256 */ -#define AX25_DEF_PROTOCOL AX25_PROTO_STD_SIMPLEX /* Standard AX.25 */ -#define AX25_DEF_DS_TIMEOUT (3 * 60 * HZ) /* DAMA timeout 3 minutes */ - -typedef struct ax25_uid_assoc { - struct ax25_uid_assoc *next; - uid_t uid; - ax25_address call; -} ax25_uid_assoc; - -typedef struct { - ax25_address calls[AX25_MAX_DIGIS]; - unsigned char repeated[AX25_MAX_DIGIS]; - unsigned char ndigi; - char lastrepeat; -} ax25_digi; - -typedef struct ax25_route { - struct ax25_route *next; - ax25_address callsign; - struct device *dev; - ax25_digi *digipeat; - char ip_mode; -} ax25_route; - -typedef struct { - char slave; /* slave_mode? */ - struct timer_list slave_timer; /* timeout timer */ - unsigned short slave_timeout; /* when? */ -} ax25_dama_info; - -#ifndef _LINUX_SYSCTL_H -#include +#ifndef _AX25_VJ_H +struct axvj_slcomp; #endif -typedef struct ax25_dev { - struct ax25_dev *next; - struct device *dev; - struct device *forward; - struct ctl_table systable[AX25_MAX_VALUES+1]; - int values[AX25_MAX_VALUES]; -#if defined(CONFIG_AX25_DAMA_SLAVE) || defined(CONFIG_AX25_DAMA_MASTER) - ax25_dama_info dama; -#endif -} ax25_dev; +typedef struct { + unsigned int csum; + struct sk_buff *skb; +} ax25_reseq_t; typedef struct ax25_cb { + struct ax25_cb *prev; /* doubly linked list now */ struct ax25_cb *next; - ax25_address source_addr, dest_addr; - ax25_digi *digipeat; - ax25_dev *ax25_dev; - unsigned char iamdigi; - unsigned char state, modulus, pidincl; - unsigned short vs, vr, va; - unsigned char condition, backoff; + struct ax25_cb *peer; /* for FlexNet style digipeating */ + struct device *device; /* backlink to device structure */ + int inserted; /* Socket is on a list */ + struct sock *sk; /* Backlink to socket */ + + /* used by the DDI layer */ + struct { + int state; + struct ax25_cb *prev; + struct ax25_cb *next; + } ready; + + unsigned short tx_cmd, tx_rsp; + + /* layer II values */ + ax25_addr_t addr; + unsigned char state, condition; + unsigned short vs, vr, va, vs_max, window; + ax25_seqmask_t seqmask; + unsigned long rtt_timestamp; + short rtt, vs_rtt; unsigned char n2, n2count; - struct timer_list t1timer, t2timer, t3timer, idletimer; - unsigned long t1, t2, t3, idle, rtt; + unsigned short t1, t2, t3, idle; + unsigned short wrt_timer, ack_timer, idletimer, killtimer; + unsigned char dama_slave; unsigned short paclen, fragno, fraglen; + unsigned char iamdigi, pidincl, backoff; struct sk_buff_head write_queue; - struct sk_buff_head reseq_queue; + struct sk_buff_head rcv_queue; /* MW: for flow control handling */ struct sk_buff_head ack_queue; struct sk_buff_head frag_queue; - unsigned char window; - struct timer_list timer; - struct sock *sk; /* Backlink to socket */ + struct axvj_slcomp *slcomp; /* MW: for VJ Compression */ + unsigned char slcomp_enable; /* MW: dito. */ + ax25_reseq_t reseq[AX25_SEQMASK+1]; /* MW: resequencer, not for EMODULUS */ } ax25_cb; -/* af_ax25.c */ -extern ax25_cb *volatile ax25_list; -extern void ax25_free_cb(ax25_cb *); -extern void ax25_insert_socket(ax25_cb *); -struct sock *ax25_find_listener(ax25_address *, int, struct device *, int); -struct sock *ax25_find_socket(ax25_address *, ax25_address *, int); -extern ax25_cb *ax25_find_cb(ax25_address *, ax25_address *, ax25_digi *, struct device *); -extern struct sock *ax25_addr_match(ax25_address *); -extern void ax25_send_to_raw(struct sock *, struct sk_buff *, int); -extern void ax25_destroy_socket(ax25_cb *); -extern ax25_cb *ax25_create_cb(void); -extern void ax25_fillin_cb(ax25_cb *, ax25_dev *); -extern int ax25_create(struct socket *, int); -extern struct sock *ax25_make_new(struct sock *, struct ax25_dev *); +typedef struct { + ax25_addr_t addr; + unsigned short frametype; + unsigned short nr; + unsigned short ns; + unsigned char pf; + unsigned char cmdrsp; + unsigned char dama; +} ax25_pktinfo; + +/* needed by netrom/rose */ +extern __inline__ unsigned long ax25_display_timer(struct timer_list *timer) +{ + if (timer->prev == NULL && timer->next == NULL) + return 0; -/* ax25_addr.c */ -extern ax25_address null_ax25_address; -extern char *ax2asc(ax25_address *); -extern ax25_address *asc2ax(char *); -extern int ax25cmp(ax25_address *, ax25_address *); -extern int ax25digicmp(ax25_digi *, ax25_digi *); -extern unsigned char *ax25_addr_parse(unsigned char *, int, ax25_address *, ax25_address *, ax25_digi *, int *, int *); -extern int ax25_addr_build(unsigned char *, ax25_address *, ax25_address *, ax25_digi *, int, int); -extern int ax25_addr_size(ax25_digi *); -extern void ax25_digi_invert(ax25_digi *, ax25_digi *); - -/* ax25_dev.c */ -extern ax25_dev *ax25_dev_list; -extern ax25_dev *ax25_dev_ax25dev(struct device *); -extern ax25_dev *ax25_addr_ax25dev(ax25_address *); -extern void ax25_dev_device_up(struct device *); -extern void ax25_dev_device_down(struct device *); -extern int ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *); -extern struct device *ax25_fwd_dev(struct device *); -extern void ax25_dev_free(void); - -/* ax25_ds_in.c */ -extern int ax25_ds_frame_in(ax25_cb *, struct sk_buff *, int); - -/* ax25_ds_subr.c */ -extern void ax25_ds_nr_error_recovery(ax25_cb *); -extern void ax25_ds_enquiry_response(ax25_cb *); -extern void ax25_ds_establish_data_link(ax25_cb *); -extern void ax25_dev_dama_on(ax25_dev *); -extern void ax25_dev_dama_off(ax25_dev *); -extern void ax25_dama_on(ax25_cb *); -extern void ax25_dama_off(ax25_cb *); - -/* ax25_ds_timer.c */ -extern void ax25_ds_set_timer(ax25_dev *); -extern void ax25_ds_del_timer(ax25_dev *); -extern void ax25_ds_timer(ax25_cb *); -extern void ax25_ds_t1_timeout(ax25_cb *); -extern void ax25_ds_heartbeat_expiry(ax25_cb *); -extern void ax25_ds_t3timer_expiry(ax25_cb *); -extern void ax25_ds_idletimer_expiry(ax25_cb *); + return timer->expires - jiffies; +} #include -/* ax25_iface.c */ -extern int ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *)); -extern void ax25_protocol_release(unsigned int); -extern int ax25_linkfail_register(void (*)(ax25_cb *, int)); -extern void ax25_linkfail_release(void (*)(ax25_cb *, int)); -extern int ax25_listen_register(ax25_address *, struct device *); -extern void ax25_listen_release(ax25_address *, struct device *); -extern int (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *); -extern int ax25_listen_mine(ax25_address *, struct device *); -extern void ax25_link_failed(ax25_cb *, int); -extern int ax25_protocol_is_registered(unsigned int); - -/* ax25_in.c */ -extern int ax25_rx_iframe(ax25_cb *, struct sk_buff *); -extern int ax25_kiss_rcv(struct sk_buff *, struct device *, struct packet_type *); - -/* ax25_ip.c */ -extern int ax25_encapsulate(struct sk_buff *, struct device *, unsigned short, void *, void *, unsigned int); -extern int ax25_rebuild_header(struct sk_buff *); +extern ax25_address* asc2ax(char *); +extern ax25_address null_ax25_address; +extern char* ax2asc(ax25_address *); +extern int ax25cmp(ax25_address *, ax25_address *); +extern ax25_cb *ax25_send_frame(struct sk_buff*, int, ax25_addr_t*, struct device*); +extern ax25_cb *ax25_find_cb(ax25_addr_t*, struct device*); /* ax25_out.c */ -extern ax25_cb *ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct device *); extern void ax25_output(ax25_cb *, int, struct sk_buff *); -extern void ax25_kick(ax25_cb *); -extern void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int); -extern void ax25_queue_xmit(struct sk_buff *); -extern int ax25_check_iframes_acked(ax25_cb *, unsigned short); - -/* ax25_route.c */ -extern void ax25_rt_device_down(struct device *); -extern int ax25_rt_ioctl(unsigned int, void *); -extern int ax25_rt_get_info(char *, char **, off_t, int, int); -extern int ax25_rt_autobind(ax25_cb *, ax25_address *); -extern ax25_route *ax25_rt_find_route(ax25_address *, struct device *); -extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *); -extern void ax25_rt_free(void); - -/* ax25_std_in.c */ -extern int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int); - -/* ax25_std_subr.c */ -extern void ax25_std_nr_error_recovery(ax25_cb *); -extern void ax25_std_establish_data_link(ax25_cb *); -extern void ax25_std_transmit_enquiry(ax25_cb *); -extern void ax25_std_enquiry_response(ax25_cb *); -extern void ax25_std_timeout_response(ax25_cb *); - -/* ax25_std_timer.c */ -extern void ax25_std_heartbeat_expiry(ax25_cb *); -extern void ax25_std_t1timer_expiry(ax25_cb *); -extern void ax25_std_t2timer_expiry(ax25_cb *); -extern void ax25_std_t3timer_expiry(ax25_cb *); -extern void ax25_std_idletimer_expiry(ax25_cb *); - -/* ax25_subr.c */ -extern void ax25_clear_queues(ax25_cb *); -extern void ax25_frames_acked(ax25_cb *, unsigned short); -extern void ax25_requeue_frames(ax25_cb *); -extern int ax25_validate_nr(ax25_cb *, unsigned short); -extern int ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *); -extern void ax25_send_control(ax25_cb *, int, int, int); -extern void ax25_return_dm(struct device *, ax25_address *, ax25_address *, ax25_digi *); -extern void ax25_calculate_t1(ax25_cb *); -extern void ax25_calculate_rtt(ax25_cb *); -extern void ax25_disconnect(ax25_cb *, int); /* ax25_timer.c */ -extern void ax25_start_heartbeat(ax25_cb *); -extern void ax25_start_t1timer(ax25_cb *); -extern void ax25_start_t2timer(ax25_cb *); -extern void ax25_start_t3timer(ax25_cb *); -extern void ax25_start_idletimer(ax25_cb *); -extern void ax25_stop_heartbeat(ax25_cb *); -extern void ax25_stop_t1timer(ax25_cb *); -extern void ax25_stop_t2timer(ax25_cb *); -extern void ax25_stop_t3timer(ax25_cb *); -extern void ax25_stop_idletimer(ax25_cb *); -extern int ax25_t1timer_running(ax25_cb *); -extern unsigned long ax25_display_timer(struct timer_list *); - -/* ax25_uid.c */ -extern int ax25_uid_policy; -extern ax25_address *ax25_findbyuid(uid_t); -extern int ax25_uid_ioctl(int, struct sockaddr_ax25 *); -extern int ax25_uid_get_info(char *, char **, off_t, int, int); -extern void ax25_uid_free(void); +extern void ax25_timer(ax25_cb *); +extern void ax25_link_failed(ax25_cb *, int); +extern int (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *); /* sysctl_net_ax25.c */ extern void ax25_register_sysctl(void); extern void ax25_unregister_sysctl(void); + +/* support routines for modules that use AX.25, in ax25_timer.c */ +extern int ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *)); +extern void ax25_protocol_release(unsigned int); +extern int ax25_linkfail_register(void (*)(ax25_cb *, int)); +extern void ax25_linkfail_release(void (*)(ax25_cb *, int)); +extern int ax25_listen_register(ax25_address *, struct device *); +extern void ax25_listen_release(ax25_address *, struct device *); +extern int ax25_protocol_is_registered(unsigned int); #endif Index: linux/include/net/ax25_uid.h diff -u /dev/null linux/include/net/ax25_uid.h:1.1.4.1 --- /dev/null Wed Jul 7 01:02:24 1999 +++ linux/include/net/ax25_uid.h Sun Jul 4 13:00:57 1999 @@ -0,0 +1,25 @@ +/* + * This code REQUIRES 1.2.1 or higher/ NET3.029 + * + * This module: + * This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _AX25_UID_H +#define _AX25_UID_H + +typedef struct ax25_uid_assoc { + struct ax25_uid_assoc *next; + uid_t uid; + ax25_address call; +} ax25_uid_assoc; + +extern int ax25_uid_policy; +extern int ax25_cs_get_info(char *, char **, off_t, int, int); +extern int ax25_uid_ioctl(int, struct sockaddr_ax25*); +extern ax25_address* ax25_findbyuid(uid_t uid); + +#endif Index: linux/include/net/ax25dev.h diff -u /dev/null linux/include/net/ax25dev.h:1.1.4.1 --- /dev/null Wed Jul 7 01:02:24 1999 +++ linux/include/net/ax25dev.h Sun Jul 4 13:00:57 1999 @@ -0,0 +1,161 @@ +/* + * include/net/ax25dev.h + * + * intra-kernel interface declaration, anything needed by device drivers for + * AX.25 interfaces is defined here. + * + */ + +#ifndef _NET_AX25DEV_H +#define _NET_AX25DEV_H + +#include +#include +#include +#include + +#ifndef AX25_TICS +#define AX25_TICS (HZ/10) +#endif + +#define AX25_MAX_DEVICES 20 /* Max No of AX.25 devices */ +#define AX25_DEV_MAGIC 0x88babe73 + +enum { + AX25_VALUES_IPDEFMODE, /* 0=DG 1=VC */ + AX25_VALUES_AXDEFMODE, /* 0=Normal 1=Extended Seq Nos */ + AX25_VALUES_BACKOFF, /* 0=None 1=Linear 2=Exponential */ + AX25_VALUES_CONMODE, /* Allow connected modes - 0=No 1=no "PID text" 2=all PIDs */ + AX25_VALUES_WINDOW, /* Default window size for standard AX.25 */ + AX25_VALUES_EWINDOW, /* Default window size for extended AX.25 */ + AX25_VALUES_T1, /* Default T1 timeout value */ + AX25_VALUES_T2, /* Default T2 timeout value */ + AX25_VALUES_T3, /* Default T3 timeout value */ + AX25_VALUES_IDLE, /* Connected mode idle timer */ + AX25_VALUES_N2, /* Default N2 value */ + AX25_VALUES_PACLEN, /* AX.25 MTU */ + AX25_MAX_VALUES /* THIS MUST REMAIN THE LAST ENTRY OF THIS LIST */ +}; + +#define AX25_DEF_IPDEFMODE 1 /* VC */ +#define AX25_DEF_AXDEFMODE 0 /* Normal */ +#define AX25_DEF_BACKOFF 1 /* Linear backoff */ +#define AX25_DEF_CONMODE 2 /* Connected mode allowed */ +#define AX25_DEF_WINDOW 7 /* Window=7 */ +#define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */ +#define AX25_DEF_T1 (10 * AX25_SLOWHZ) /* T1=10s */ +#define AX25_DEF_T2 (3 * AX25_SLOWHZ) /* T2=3s */ +#define AX25_DEF_T3 (90 * AX25_SLOWHZ) /* T3=90s */ +#define AX25_DEF_N2 10 /* N2=10 */ +#define AX25_DEF_IDLE (120 * AX25_SLOWHZ) /* Idle=120s */ +#define AX25_DEF_PACLEN 256 /* Paclen=256 */ + +#ifndef _AX25_H +struct ax25_cb; +typedef struct ax25_cb ax25_cb; +#endif + +struct ax25_dev { + unsigned long magic; + struct device *netdev; /* backlink to the network device */ + struct device *forward; + struct ctl_table systable[AX25_MAX_VALUES+1]; + int values[AX25_MAX_VALUES]; + struct timer_list timer; + struct timer_list tics; + struct tq_struct task_queue; + struct sk_buff_head unproto_queue; + int needs_transmit; + struct { + ax25_cb *ready; + ax25_cb *all; + } list; + + struct { + /* handled by driver */ + unsigned char can_duplex; /* set if driver/hardware is duplex capable */ + unsigned char fast; /* set for "infinitely" fast channels */ + unsigned int bitrate; /* bitrate the channel runs on */ + + /* handled by DDI layer */ + unsigned char duplex; /* internal use */ + unsigned int bit_per_jiffie; /* internal use */ + unsigned int jiffies_per_slot; /* internal use */ + unsigned int persistence; /* internal use */ + unsigned char dcd_memory; /* internal use */ + unsigned char dcd_dropped; /* internal use */ + + /* downcalls */ + unsigned int (*dcd)(struct device*); /* required: report dcd state */ + unsigned int (*ptt)(struct device*); /* required: report ptt state */ + unsigned int (*cts)(struct device*); /* optional: report "clear to send" */ + void (*rts)(struct device*); /* optional: forced "key transmitter" */ + void (*set_txdelay)(struct device*, unsigned int); /* required: configure txdelay */ + void (*set_bitrate)(struct device*, unsigned int); /* optional: configure bitrate */ + } hw; + + unsigned int bytes_sent; + unsigned int max_bytes; + + /* statistics */ + unsigned long tx_iframes; + unsigned long rx_iframes; + unsigned long tx_bytes; + unsigned long rx_bytes; + unsigned long rx_rejects; +}; + +#define AX25_PTR(dev) ((struct ax25_dev *)dev->ax25_ptr) + +extern __inline__ void ax25_dev_initdev(struct device *dev, struct ax25_dev *axdev) +{ + memset(axdev, 0, sizeof (struct ax25_dev)); + AX25_PTR(dev) = axdev; + AX25_PTR(dev)->magic = AX25_DEV_MAGIC; +} + +extern __inline__ void ax25_dev_setduplex(struct device *dev, unsigned int dup) +{ + AX25_PTR(dev)->hw.can_duplex = dup; +} + +extern __inline__ void ax25_dev_setbitrate(struct device *dev, unsigned int br) +{ + AX25_PTR(dev)->hw.bitrate = br; + AX25_PTR(dev)->hw.bit_per_jiffie = br / HZ; + AX25_PTR(dev)->hw.jiffies_per_slot = 1200 * HZ / br + 1; + AX25_PTR(dev)->values[AX25_VALUES_T2] = (3600 / AX25_TICS) * HZ / br; + AX25_PTR(dev)->max_bytes = (br * 15) / 8; +} + +extern __inline__ unsigned int ax25_dev_getbitrate(struct device *dev) +{ + return AX25_PTR(dev)->hw.bitrate; +} + +extern __inline__ void ax25_dev_setpttfunc(struct device *dev, unsigned int (*pttfunc)(struct device*)) +{ + AX25_PTR(dev)->hw.ptt = pttfunc; +} + +extern __inline__ void ax25_dev_setdcdfunc(struct device *dev, unsigned int (*dcdfunc)(struct device*)) +{ + AX25_PTR(dev)->hw.dcd = dcdfunc; +} + +extern __inline__ void ax25_dev_setctsfunc(struct device *dev, unsigned int (*ctsfunc)(struct device*)) +{ + AX25_PTR(dev)->hw.cts = ctsfunc; +} + +extern __inline__ void ax25_dev_setrtsfunc(struct device *dev, void (*rtsfunc)(struct device*)) +{ + AX25_PTR(dev)->hw.rts = rtsfunc; +} + +extern __inline__ void ax25_dev_setfast(struct device *dev, unsigned int fast) +{ + AX25_PTR(dev)->hw.fast = fast; +} + +#endif Index: linux/include/net/netrom.h diff -u linux/include/net/netrom.h:1.1.2.1 linux/include/net/netrom.h:1.1.2.1.2.1 --- linux/include/net/netrom.h:1.1.2.1 Sun Jul 4 10:17:55 1999 +++ linux/include/net/netrom.h Sun Jul 4 11:32:31 1999 @@ -76,8 +76,7 @@ struct nr_neigh { struct nr_neigh *next; - ax25_address callsign; - ax25_digi *digipeat; + ax25_addr_t addr; ax25_cb *ax25; struct device *dev; unsigned char quality; Index: linux/include/net/rose.h diff -u linux/include/net/rose.h:1.1.2.1 linux/include/net/rose.h:1.1.2.1.2.1 --- linux/include/net/rose.h:1.1.2.1 Sun Jul 4 10:17:56 1999 +++ linux/include/net/rose.h Sun Jul 4 11:32:31 1999 @@ -81,8 +81,7 @@ struct rose_neigh { struct rose_neigh *next; - ax25_address callsign; - ax25_digi *digipeat; + ax25_addr_t addr; ax25_cb *ax25; struct device *dev; unsigned short count; Index: linux/net/netsyms.c diff -u linux/net/netsyms.c:1.1.2.1 linux/net/netsyms.c:1.1.2.1.2.1 --- linux/net/netsyms.c:1.1.2.1 Sun Jul 4 10:18:06 1999 +++ linux/net/netsyms.c Sun Jul 4 11:32:31 1999 @@ -247,6 +247,7 @@ EXPORT_SYMBOL(__ip_finish_output); EXPORT_SYMBOL(inet_dgram_ops); EXPORT_SYMBOL(ip_cmsg_recv); +EXPORT_SYMBOL(ip_options_fragment); /* Route manipulation */ EXPORT_SYMBOL(ip_rt_ioctl); Index: linux/net/ax25/Config.in diff -u linux/net/ax25/Config.in:1.1.2.1 linux/net/ax25/Config.in:1.1.2.1.2.1 --- linux/net/ax25/Config.in:1.1.2.1 Sun Jul 4 10:18:09 1999 +++ linux/net/ax25/Config.in Sun Jul 4 11:32:31 1999 @@ -15,7 +15,7 @@ comment 'Packet Radio protocols' tristate 'Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25 if [ "$CONFIG_AX25" != "n" ]; then - bool ' AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE +# bool ' AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE # bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER dep_tristate ' Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25 dep_tristate ' Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25 Index: linux/net/ax25/Makefile diff -u linux/net/ax25/Makefile:1.1.2.1 linux/net/ax25/Makefile:1.1.2.1.2.1 --- linux/net/ax25/Makefile:1.1.2.1 Sun Jul 4 10:18:09 1999 +++ linux/net/ax25/Makefile Sun Jul 4 11:32:31 1999 @@ -6,19 +6,16 @@ # unless it's something special (ie not a .c file). # # Note 2! The CFLAGS definition is now in the main makefile... +# +EXTRA_CFLAGS := - O_TARGET := ax25.o -O_OBJS := ax25_addr.o ax25_dev.o ax25_iface.o ax25_in.o ax25_ip.o ax25_out.o \ - ax25_route.o ax25_std_in.o ax25_std_subr.o ax25_std_timer.o \ - ax25_subr.o ax25_timer.o ax25_uid.o +O_OBJS := ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o \ + ax25_vj.o ax25_ddi.o ax25_uid.o ax25_ctl.o ax25_core.o \ + ax25_ipax.o ax25_lapb.o ax25_netlink.o M_OBJS := $(O_TARGET) OX_OBJS += af_ax25.o - -ifeq ($(CONFIG_AX25_DAMA_SLAVE),y) -O_OBJS += ax25_ds_in.o ax25_ds_subr.o ax25_ds_timer.o -endif ifeq ($(CONFIG_SYSCTL),y) O_OBJS += sysctl_net_ax25.o Index: linux/net/ax25/af_ax25.c diff -u linux/net/ax25/af_ax25.c:1.1.2.1 linux/net/ax25/af_ax25.c:1.1.2.1.2.1 --- linux/net/ax25/af_ax25.c:1.1.2.1 Sun Jul 4 10:18:09 1999 +++ linux/net/ax25/af_ax25.c Sun Jul 4 11:32:31 1999 @@ -1,7 +1,7 @@ /* - * AX.25 release 038 + * AX.25 release 035 * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * This code REQUIRES 1.2.1 or higher/ NET3.029 * * This module: * This module is free software; you can redistribute it and/or @@ -80,21 +80,17 @@ * datagrams per socket. * AX.25 033 Jonathan(G4KLX) Removed auto-router. * Hans(PE1AYX) Converted to Module. - * Joerg(DL1BKE) Moved BPQ Ethernet to separate driver. - * AX.25 034 Jonathan(G4KLX) 2.1 changes - * Alan(GW4PTS) Small POSIXisations - * AX.25 035 Alan(GW4PTS) Started fixing to the new - * format. - * Hans(PE1AYX) Fixed interface to IP layer. - * Alan(GW4PTS) Added asynchronous support. - * Frederic(F1OAT) Support for pseudo-digipeating. + * Joerg(DL1BKE) Moved BPQ Ethernet to seperate driver. + * Fixed 2.0.x specific IP over AX.25 problem. + * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. * Jonathan(G4KLX) Support for packet forwarding. - * AX.25 036 Jonathan(G4KLX) Major restructuring. - * Joerg(DL1BKE) Fixed DAMA Slave. - * Jonathan(G4KLX) Fix wildcard listen parameter setting. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - * AX.25 038 Matthias(DG2FEF) Small fixes to the syscall interface to make kernel - * independent of AX25_MAX_DIGIS used by applications. + * Jonathan(G4KLX) Fix wildcard listening parameter setting. + * Matthias(DG2FEF) Support for VJ TCP Compression via AX.25 + * + * To do: + * Restructure the ax25_rcv code to be cleaner/faster and + * copy only when needed. + * Consider better arbitrary protocol support. */ #include @@ -110,508 +106,322 @@ #include #include #include -#include #include #include #include #include -#include -#include #include #include #include /* For TIOCINQ/OUTQ */ -#include #include #include #include #include #include -#include #include -#include -#include -ax25_cb *volatile ax25_list = NULL; +#include +#include +#include +#include -static struct proto_ops ax25_proto_ops; +#include "af_ax25.h" +#include "ax25_vj.h" +#include "ax25_ddi.h" +#include "ax25_route.h" +#include "ax25_core.h" +#include "ax25_ctl.h" +#include "ax25_in.h" +#include "ax25_subr.h" +#include "ax25_netlink.h" /* - * Free an allocated ax25 control block. This is done to centralise - * the MOD count code. + * ------------------------------------------------------------------------ + * declaration of static functions + * ------------------------------------------------------------------------ */ -void ax25_free_cb(ax25_cb *ax25) -{ - if (ax25->digipeat != NULL) { - kfree(ax25->digipeat); - ax25->digipeat = NULL; - } +static void ax25_kill_by_device(struct device*); +static int ax25_device_event(struct notifier_block* ,unsigned long, void*); +static int ax25_setsockopt(struct socket*, int, int, char*, int); +static int ax25_getsockopt(struct socket*, int, int, char*, int*); +static int ax25_listen(struct socket*, int); +static int ax25_shutdown(struct socket*, int); +static int ax25_create(struct socket*, int); +static int ax25_release(struct socket*, struct socket*); +static int ax25_bind(struct socket*, struct sockaddr*, int); +static int ax25_connect(struct socket*, struct sockaddr*, int, int); +static int ax25_accept(struct socket*, struct socket*, int); +static int ax25_getname(struct socket*, struct sockaddr*, int*, int); +static int ax25_sendmsg(struct socket*, struct msghdr*, int, struct scm_cookie*); +static int ax25_recvmsg(struct socket*, struct msghdr*, int, int, struct scm_cookie*); +static int ax25_ioctl(struct socket*, unsigned int, unsigned long); +static int ax25_print_list(char*, off_t*, off_t, int, ax25_cb*, char*); +static int ax25_get_info(char*, char **, off_t, int, int); - kfree(ax25); - - MOD_DEC_USE_COUNT; -} - -static void ax25_free_sock(struct sock *sk) -{ - ax25_free_cb(sk->protinfo.ax25); -} - /* - * Socket removal during an interrupt is now safe. + * ------------------------------------------------------------------------ + * static variables and structures + * ------------------------------------------------------------------------ */ -static void ax25_remove_socket(ax25_cb *ax25) -{ - ax25_cb *s; - unsigned long flags; - - save_flags(flags); cli(); - - if ((s = ax25_list) == ax25) { - ax25_list = s->next; - restore_flags(flags); - return; - } - while (s != NULL && s->next != NULL) { - if (s->next == ax25) { - s->next = ax25->next; - restore_flags(flags); - return; - } - - s = s->next; - } - - restore_flags(flags); -} - /* - * Kill all bound sockets on a dropped device. + * table of exportes symbols */ -static void ax25_kill_by_device(struct device *dev) -{ - ax25_dev *ax25_dev; - ax25_cb *s; - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return; +EXPORT_SYMBOL(ax25_findbyuid); +EXPORT_SYMBOL(ax25_find_cb); +EXPORT_SYMBOL(ax25_linkfail_register); +EXPORT_SYMBOL(ax25_linkfail_release); +EXPORT_SYMBOL(ax25_listen_register); +EXPORT_SYMBOL(ax25_listen_release); +EXPORT_SYMBOL(ax25_protocol_register); +EXPORT_SYMBOL(ax25_protocol_release); +EXPORT_SYMBOL(ax25_send_frame); +EXPORT_SYMBOL(ax25_uid_policy); +EXPORT_SYMBOL(ax25cmp); +EXPORT_SYMBOL(ax2asc); +EXPORT_SYMBOL(asc2ax); +EXPORT_SYMBOL(null_ax25_address); - for (s = ax25_list; s != NULL; s = s->next) { - if (s->ax25_dev == ax25_dev) { - s->ax25_dev = NULL; - ax25_disconnect(s, ENETUNREACH); - } - } -} +/* for debugging */ +EXPORT_SYMBOL(ax25_kill_by_device); /* - * Handle device status changes. + * This list contains all sockets that are not bound to + * a specific device. */ -static int ax25_device_event(struct notifier_block *this,unsigned long event, void *ptr) -{ - struct device *dev = (struct device *)ptr; - - /* Reject non AX.25 devices */ - if (dev->type != ARPHRD_AX25) - return NOTIFY_DONE; - - switch (event) { - case NETDEV_UP: - ax25_dev_device_up(dev); - break; - case NETDEV_DOWN: - ax25_kill_by_device(dev); - ax25_rt_device_down(dev); - ax25_dev_device_down(dev); - break; - default: - break; - } - - return NOTIFY_DONE; -} +ax25_cb *volatile ax25_list = NULL; /* - * Add a socket to the bound sockets list. + * Protocol family registration data */ -void ax25_insert_socket(ax25_cb *ax25) +static struct net_proto_family ax25_family_ops = { - unsigned long flags; - - save_flags(flags); - cli(); - - ax25->next = ax25_list; - ax25_list = ax25; - - restore_flags(flags); -} + PF_AX25, + ax25_create +}; /* - * Find a socket that wants to accept the SABM we have just - * received. + * Protocol operations for AF_AX25 */ -struct sock *ax25_find_listener(ax25_address *addr, int digi, struct device *dev, int type) -{ - unsigned long flags; - ax25_cb *s; - - save_flags(flags); - cli(); - - for (s = ax25_list; s != NULL; s = s->next) { - if ((s->iamdigi && !digi) || (!s->iamdigi && digi)) - continue; - if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == type && s->sk->state == TCP_LISTEN) { - /* If device is null we match any device */ - if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) { - restore_flags(flags); - return s->sk; - } - } - } +static struct proto_ops ax25_proto_ops = { + PF_AX25, - restore_flags(flags); - return NULL; -} + sock_no_dup, + ax25_release, + ax25_bind, + ax25_connect, + sock_no_socketpair, + ax25_accept, + ax25_getname, + datagram_poll, + ax25_ioctl, + ax25_listen, + ax25_shutdown, + ax25_setsockopt, + ax25_getsockopt, + sock_no_fcntl, + ax25_sendmsg, + ax25_recvmsg +}; /* - * Find an AX.25 socket given both ends. + * Device up/down notifier block */ -struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, int type) -{ - ax25_cb *s; - unsigned long flags; - - save_flags(flags); - cli(); - - for (s = ax25_list; s != NULL; s = s->next) { - if (s->sk != NULL && ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->sk->type == type) { - restore_flags(flags); - return s->sk; - } - } +static struct notifier_block ax25_dev_notifier = { + ax25_device_event, + 0 +}; - restore_flags(flags); +/* + * procfs registration data + */ +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry proc_ax25_route = { + PROC_NET_AX25_ROUTE, 10, "ax25_route", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + ax25_rt_get_info +}; +static struct proc_dir_entry proc_ax25 = { + PROC_NET_AX25, 4, "ax25", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + ax25_get_info +}; +static struct proc_dir_entry proc_ax25_calls = { + PROC_NET_AX25_CALLS, 10, "ax25_calls", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + ax25_cs_get_info +}; +static struct proc_dir_entry proc_ax25_ports = { + PROC_NET_AX25_PORTS, 10, "ax25_ports", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + ax25_dev_get_info +}; +#endif - return NULL; -} +/* + * ------------------------------------------------------------------------ + * Interface implementation + * All public functions of this module are defined here + * ------------------------------------------------------------------------ + */ -/* - * Find an AX.25 control block given both ends. It will only pick up - * floating AX.25 control blocks or non Raw socket bound control blocks. +/* + * ------------------------------------------------------------------------ + * Init functions. called by the kernel on startup + * ------------------------------------------------------------------------ */ -ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi *digi, struct device *dev) +__initfunc(void ax25_proto_init(struct net_proto *pro)) { - ax25_cb *s; - unsigned long flags; + sock_register(&ax25_family_ops); + ax25_packet_type.type = htons(ETH_P_AX25); + dev_add_pack(&ax25_packet_type); + register_netdevice_notifier(&ax25_dev_notifier); +#ifdef CONFIG_SYSCTL + ax25_register_sysctl(); +#endif + ax25_ddi_init(); + ax25_netlink_init(); - save_flags(flags); - cli(); +#ifdef CONFIG_PROC_FS + proc_net_register(&proc_ax25_route); + proc_net_register(&proc_ax25); + proc_net_register(&proc_ax25_calls); + proc_net_register(&proc_ax25_ports); +#endif - for (s = ax25_list; s != NULL; s = s->next) { - if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET) - continue; - if (s->ax25_dev == NULL) - continue; - if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) { - if (digi != NULL && digi->ndigi != 0) { - if (s->digipeat == NULL) - continue; - if (ax25digicmp(s->digipeat, digi) != 0) - continue; - } else { - if (s->digipeat != NULL && s->digipeat->ndigi != 0) - continue; - } - restore_flags(flags); - return s; - } - } + printk(KERN_INFO "NET4: AX.25 for Linux 2.2-NET4 by DG2FEF\n"); - restore_flags(flags); +#ifdef CONFIG_INET + ipax_init(); +#endif - return NULL; } /* - * Look for any matching address - RAW sockets can bind to arbitrary names + * ------------------------------------------------------------------------ + * module registration/unregistration + * ------------------------------------------------------------------------ */ -struct sock *ax25_addr_match(ax25_address *addr) -{ - unsigned long flags; - ax25_cb *s; - - save_flags(flags); - cli(); +#ifdef MODULE - for (s = ax25_list; s != NULL; s = s->next) { - if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == SOCK_RAW) { - restore_flags(flags); - return s->sk; - } - } +MODULE_AUTHOR("Matthias Welwarsky, dg2fef@afthd.tu-darmstadt.de, dg2fef@db0ais.ampr.org"); +MODULE_DESCRIPTION("Packet Radio AX.25 Protocol stack"); - restore_flags(flags); +int init_module(void) +{ + ax25_proto_init(NULL); - return NULL; + return 0; } -void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto) +/* ---------------------------------------------------------------------*/ + +void cleanup_module(void) { - struct sk_buff *copy; +#ifdef CONFIG_PROC_FS + proc_net_unregister(PROC_NET_AX25_ROUTE); + proc_net_unregister(PROC_NET_AX25); + proc_net_unregister(PROC_NET_AX25_CALLS); + proc_net_unregister(PROC_NET_AX25_PORTS); +#endif + ax25_rt_free(); + ipax_cleanup(); + ax25_netlink_cleanup(); - while (sk != NULL) { - if (sk->type == SOCK_RAW && - sk->protocol == proto && - atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) { - if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL) - return; +#ifdef CONFIG_SYSCTL + ax25_unregister_sysctl(); +#endif + unregister_netdevice_notifier(&ax25_dev_notifier); - if (sock_queue_rcv_skb(sk, copy) != 0) - kfree_skb(copy); - } + ax25_packet_type.type = htons(ETH_P_AX25); + dev_remove_pack(&ax25_packet_type); - sk = sk->next; - } + sock_unregister(AF_AX25); } - -/* - * Deferred destroy. - */ -void ax25_destroy_socket(ax25_cb *); -/* - * Handler for deferred kills. - */ -static void ax25_destroy_timer(unsigned long data) -{ - ax25_destroy_socket((ax25_cb *)data); -} +#endif /* MODULE */ +/* ---------------------------------------------------------------------*/ /* - * This is called from user mode and the timers. Thus it protects itself against - * interrupt users but doesn't worry about being called during work. - * Once it is removed from the queue no interrupt or bottom half will - * touch it and we are (fairly 8-) ) safe. + * Find the AX.25 device that matches the hardware address supplied. */ -void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer */ +struct device *ax25rtr_get_dev(ax25_address *addr) { - struct sk_buff *skb; - unsigned long flags; + struct device *dev; + int i; - save_flags(flags); cli(); - - ax25_stop_heartbeat(ax25); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); - - ax25_remove_socket(ax25); - ax25_clear_queues(ax25); /* Flush the queues */ - - if (ax25->sk != NULL) { - while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) { - if (skb->sk != ax25->sk) { /* A pending connection */ - skb->sk->dead = 1; /* Queue the unaccepted socket for death */ - ax25_start_heartbeat(skb->sk->protinfo.ax25); - skb->sk->protinfo.ax25->state = AX25_STATE_0; - } - - kfree_skb(skb); - } - } - - if (ax25->sk != NULL) { - if (atomic_read(&ax25->sk->wmem_alloc) != 0 || - atomic_read(&ax25->sk->rmem_alloc) != 0) { - /* Defer: outstanding buffers */ - init_timer(&ax25->timer); - ax25->timer.expires = jiffies + 10 * HZ; - ax25->timer.function = ax25_destroy_timer; - ax25->timer.data = (unsigned long)ax25; - add_timer(&ax25->timer); - } else { - sk_free(ax25->sk); - } - } else { - ax25_free_cb(ax25); + start_bh_atomic(); + for (i = 0; i < AX25_MAX_DEVICES; i++) { + dev = ax25_devices[i]; + if (dev != NULL && !ax25cmp(addr, (ax25_address *)dev->dev_addr)) + break; } - - restore_flags(flags); + end_bh_atomic(); + return dev; } -/* - * dl1bke 960311: set parameters for existing AX.25 connections, - * includes a KILL command to abort any connection. - * VERY useful for debugging ;-) +/* + * ------------------------------------------------------------------------ + * End of public area, all private functions of this module are defined + * here. + * ------------------------------------------------------------------------ */ -static int ax25_ctl_ioctl(const unsigned int cmd, void *arg) -{ - struct ax25_ctl_struct ax25_ctl; - ax25_digi digi; - ax25_dev *ax25_dev; - ax25_cb *ax25; - unsigned int k; - - if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl))) - return -EFAULT; - - if ((ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr)) == NULL) - return -ENODEV; - - digi.ndigi = ax25_ctl.digi_count; - for (k = 0; k < digi.ndigi; k++) - digi.calls[k] = ax25_ctl.digi_addr[k]; - - if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, &digi, ax25_dev->dev)) == NULL) - return -ENOTCONN; - - switch (ax25_ctl.cmd) { - case AX25_KILL: - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); -#ifdef CONFIG_AX25_DAMA_SLAVE - if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE) - ax25_dama_off(ax25); -#endif - ax25_disconnect(ax25, ENETRESET); - break; - case AX25_WINDOW: - if (ax25->modulus == AX25_MODULUS) { - if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7) - return -EINVAL; - } else { - if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63) - return -EINVAL; - } - ax25->window = ax25_ctl.arg; - break; - - case AX25_T1: - if (ax25_ctl.arg < 1) - return -EINVAL; - ax25->rtt = (ax25_ctl.arg * HZ) / 2; - ax25->t1 = ax25_ctl.arg * HZ; - break; - - case AX25_T2: - if (ax25_ctl.arg < 1) - return -EINVAL; - ax25->t2 = ax25_ctl.arg * HZ; - break; - - case AX25_N2: - if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31) - return -EINVAL; - ax25->n2count = 0; - ax25->n2 = ax25_ctl.arg; - break; - - case AX25_T3: - if (ax25_ctl.arg < 0) - return -EINVAL; - ax25->t3 = ax25_ctl.arg * HZ; - break; - - case AX25_IDLE: - if (ax25_ctl.arg < 0) - return -EINVAL; - ax25->idle = ax25_ctl.arg * 60 * HZ; - break; - - case AX25_PACLEN: - if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535) - return -EINVAL; - ax25->paclen = ax25_ctl.arg; - break; - - default: - return -EINVAL; - } - - return 0; -} - +/* ---------------------------------------------------------------------*/ /* - * Fill in a created AX.25 created control block with the default - * values for a particular device. + * Kill all bound sockets on a dropped device. */ -void ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev) +static void ax25_kill_by_device(struct device *dev) { - ax25->ax25_dev = ax25_dev; + ax25_cb *s; - if (ax25->ax25_dev != NULL) { - ax25->rtt = ax25_dev->values[AX25_VALUES_T1] / 2; - ax25->t1 = ax25_dev->values[AX25_VALUES_T1]; - ax25->t2 = ax25_dev->values[AX25_VALUES_T2]; - ax25->t3 = ax25_dev->values[AX25_VALUES_T3]; - ax25->n2 = ax25_dev->values[AX25_VALUES_N2]; - ax25->paclen = ax25_dev->values[AX25_VALUES_PACLEN]; - ax25->idle = ax25_dev->values[AX25_VALUES_IDLE]; - ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF]; - - if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW]; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_WINDOW]; - } - } else { - ax25->rtt = AX25_DEF_T1 / 2; - ax25->t1 = AX25_DEF_T1; - ax25->t2 = AX25_DEF_T2; - ax25->t3 = AX25_DEF_T3; - ax25->n2 = AX25_DEF_N2; - ax25->paclen = AX25_DEF_PACLEN; - ax25->idle = AX25_DEF_IDLE; - ax25->backoff = AX25_DEF_BACKOFF; - - if (AX25_DEF_AXDEFMODE) { - ax25->modulus = AX25_EMODULUS; - ax25->window = AX25_DEF_EWINDOW; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = AX25_DEF_WINDOW; - } + for (s = ax25_dev_list(dev); s != NULL; s = s->next) { + if (s->peer && s->peer->device != s->device) + ax25_destroy_cb(s->peer); + if (s->sk) { + ax25_remove_cb(s); + ax25_disconnect(s, ENETUNREACH); + ax25_close_socket(s->sk, ENETUNREACH); + } else + ax25_destroy_cb(s); } } +/* ---------------------------------------------------------------------*/ /* - * Create an empty AX.25 control block. + * Handle device status changes. */ -ax25_cb *ax25_create_cb(void) +static int ax25_device_event(struct notifier_block *this,unsigned long event, void *ptr) { - ax25_cb *ax25; - - if ((ax25 = kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL) - return NULL; - - MOD_INC_USE_COUNT; - - memset(ax25, 0x00, sizeof(*ax25)); - - skb_queue_head_init(&ax25->write_queue); - skb_queue_head_init(&ax25->frag_queue); - skb_queue_head_init(&ax25->ack_queue); - skb_queue_head_init(&ax25->reseq_queue); - - init_timer(&ax25->timer); - init_timer(&ax25->t1timer); - init_timer(&ax25->t2timer); - init_timer(&ax25->t3timer); - init_timer(&ax25->idletimer); + struct device *dev = (struct device *)ptr; - ax25_fillin_cb(ax25, NULL); + /* Reject non AX.25 devices */ + if (dev->type != ARPHRD_AX25) + return NOTIFY_DONE; - ax25->state = AX25_STATE_0; + switch (event) { + case NETDEV_UP: + ax25_dev_device_up(dev); + break; + case NETDEV_DOWN: + ax25_kill_by_device(dev); + ax25_rt_device_down(dev); + ax25_dev_device_down(dev); + break; + default: + break; + } - return ax25; + return NOTIFY_DONE; } + +/* ---------------------------------------------------------------------*/ /* * Handling for system calls applied via the various interfaces to an * AX25 socket object @@ -633,7 +443,7 @@ switch (optname) { case AX25_WINDOW: - if (sk->protinfo.ax25->modulus == AX25_MODULUS) { + if (sk->protinfo.ax25->seqmask == AX25_SEQMASK) { if (opt < 1 || opt > 7) return -EINVAL; } else { @@ -646,14 +456,14 @@ case AX25_T1: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->rtt = (opt * HZ) / 2; - sk->protinfo.ax25->t1 = opt * HZ; + sk->protinfo.ax25->t1 = opt; + sk->protinfo.ax25->rtt = (opt * AX25_TICS) / 4; return 0; case AX25_T2: - if (opt < 1) + if (opt < 0) return -EINVAL; - sk->protinfo.ax25->t2 = opt * HZ; + sk->protinfo.ax25->t2 = opt; return 0; case AX25_N2: @@ -665,13 +475,13 @@ case AX25_T3: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->t3 = opt * HZ; + sk->protinfo.ax25->t3 = opt * AX25_SLOWHZ; return 0; case AX25_IDLE: if (opt < 0) return -EINVAL; - sk->protinfo.ax25->idle = opt * 60 * HZ; + sk->protinfo.ax25->idle = opt * AX25_SLOWHZ; return 0; case AX25_BACKOFF: @@ -681,7 +491,7 @@ return 0; case AX25_EXTSEQ: - sk->protinfo.ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS; + sk->protinfo.ax25->seqmask = opt ? AX25_ESEQMASK : AX25_SEQMASK; return 0; case AX25_PIDINCL: @@ -703,6 +513,8 @@ } } +/* ---------------------------------------------------------------------*/ + static int ax25_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { struct sock *sk = sock->sk; @@ -721,11 +533,11 @@ break; case AX25_T1: - val = sk->protinfo.ax25->t1 / HZ; + val = sk->protinfo.ax25->t1; break; case AX25_T2: - val = sk->protinfo.ax25->t2 / HZ; + val = sk->protinfo.ax25->t2; break; case AX25_N2: @@ -733,11 +545,11 @@ break; case AX25_T3: - val = sk->protinfo.ax25->t3 / HZ; + val = sk->protinfo.ax25->t3 / AX25_SLOWHZ; break; case AX25_IDLE: - val = sk->protinfo.ax25->idle / (60 * HZ); + val = sk->protinfo.ax25->idle / AX25_SLOWHZ; break; case AX25_BACKOFF: @@ -745,7 +557,7 @@ break; case AX25_EXTSEQ: - val = (sk->protinfo.ax25->modulus == AX25_EMODULUS); + val = (sk->protinfo.ax25->seqmask == AX25_ESEQMASK); break; case AX25_PIDINCL: @@ -775,65 +587,79 @@ return 0; } +/* ---------------------------------------------------------------------*/ + static int ax25_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; + + if (sk->type != SOCK_SEQPACKET) + return -EOPNOTSUPP; + + if (sk->state != TCP_LISTEN) { + /* + * POSIX VIOLATION: according to listen(2) the call can + * never return EADDRINUSE. bind(2) should have done this. + * However, things are different with AX.25. You are + * _required_ to bind() before connect() to set the + * source callsign of the outgoing connection. But as you + * may open multiple connections at one time with the + * same source callsign, you cannot perform this check + * within bind(). And as I like to have descriptive errors, + * EADDRINUSE is perfect to be returned here. + */ + if (ax25_find_listener(&sk->protinfo.ax25->addr.src, 0, sk->protinfo.ax25->device) + || ax25_find_listener(&sk->protinfo.ax25->addr.src, 1, sk->protinfo.ax25->device)) + return -EADDRINUSE; - if (sk->type == SOCK_SEQPACKET && sk->state != TCP_LISTEN) { + ax25_insert_cb(sk->protinfo.ax25); sk->max_ack_backlog = backlog; sk->state = TCP_LISTEN; return 0; } - return -EOPNOTSUPP; + return -EINVAL; } -int ax25_create(struct socket *sock, int protocol) +/* ---------------------------------------------------------------------*/ + +static int ax25_create(struct socket *sock, int protocol) { struct sock *sk; ax25_cb *ax25; switch (sock->type) { - case SOCK_DGRAM: - if (protocol == 0 || protocol == PF_AX25) - protocol = AX25_P_TEXT; + case SOCK_DGRAM: + if (protocol == 0 || protocol == PF_AX25) + protocol = AX25_P_TEXT; + break; + case SOCK_SEQPACKET: + switch (protocol) { + case 0: + case PF_AX25: /* For CLX */ + protocol = AX25_P_TEXT; break; - case SOCK_SEQPACKET: - switch (protocol) { - case 0: - case PF_AX25: /* For CLX */ - protocol = AX25_P_TEXT; - break; - case AX25_P_SEGMENT: + case AX25_P_SEGMENT: #ifdef CONFIG_INET - case AX25_P_ARP: - case AX25_P_IP: + case AX25_P_ARP: #endif #ifdef CONFIG_NETROM - case AX25_P_NETROM: + case AX25_P_NETROM: #endif #ifdef CONFIG_ROSE - case AX25_P_ROSE: -#endif - return -ESOCKTNOSUPPORT; -#ifdef CONFIG_NETROM_MODULE - case AX25_P_NETROM: - if (ax25_protocol_is_registered(AX25_P_NETROM)) - return -ESOCKTNOSUPPORT; -#endif -#ifdef CONFIG_ROSE_MODULE - case AX25_P_ROSE: - if (ax25_protocol_is_registered(AX25_P_ROSE)) - return -ESOCKTNOSUPPORT; + case AX25_P_ROSE: #endif - default: - break; - } - break; - case SOCK_RAW: - break; - default: return -ESOCKTNOSUPPORT; + + default: + if (ax25_protocol_is_registered(protocol)) + return -ESOCKTNOSUPPORT; + } + break; + case SOCK_RAW: + break; + default: + return -ESOCKTNOSUPPORT; } if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL) @@ -856,227 +682,143 @@ return 0; } -struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev) -{ - struct sock *sk; - ax25_cb *ax25; - - if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL) - return NULL; - - if ((ax25 = ax25_create_cb()) == NULL) { - sk_free(sk); - return NULL; - } - - switch (osk->type) { - case SOCK_DGRAM: - break; - case SOCK_SEQPACKET: - break; - default: - sk_free(sk); - ax25_free_cb(ax25); - return NULL; - } - - sock_init_data(NULL, sk); - - sk->destruct = ax25_free_sock; - sk->type = osk->type; - sk->socket = osk->socket; - sk->priority = osk->priority; - sk->protocol = osk->protocol; - sk->rcvbuf = osk->rcvbuf; - sk->sndbuf = osk->sndbuf; - sk->debug = osk->debug; - sk->state = TCP_ESTABLISHED; - sk->sleep = osk->sleep; - sk->zapped = osk->zapped; - - ax25->modulus = osk->protinfo.ax25->modulus; - ax25->backoff = osk->protinfo.ax25->backoff; - ax25->pidincl = osk->protinfo.ax25->pidincl; - ax25->iamdigi = osk->protinfo.ax25->iamdigi; - ax25->rtt = osk->protinfo.ax25->rtt; - ax25->t1 = osk->protinfo.ax25->t1; - ax25->t2 = osk->protinfo.ax25->t2; - ax25->t3 = osk->protinfo.ax25->t3; - ax25->n2 = osk->protinfo.ax25->n2; - ax25->idle = osk->protinfo.ax25->idle; - ax25->paclen = osk->protinfo.ax25->paclen; - ax25->window = osk->protinfo.ax25->window; - - ax25->ax25_dev = ax25_dev; - ax25->source_addr = osk->protinfo.ax25->source_addr; - - if (osk->protinfo.ax25->digipeat != NULL) { - if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { - sk_free(sk); - return NULL; - } - - *ax25->digipeat = *osk->protinfo.ax25->digipeat; - } - - sk->protinfo.ax25 = ax25; - ax25->sk = sk; - - return sk; -} - static int ax25_release(struct socket *sock, struct socket *peer) { struct sock *sk = sock->sk; + ax25_cb *ax25; + + if (sk == NULL) + return 0; - if (sk == NULL) return 0; + ax25 = sk->protinfo.ax25; + + sk->state = TCP_CLOSE; + sk->shutdown = SHUTDOWN_MASK; + /* + * don't wake me, I'm dying: this one has cost me nerves a bit. + * Seems that we should not attempt to wake an application + * that is currently exiting, which is exactly what state_change() + * does. It results in calling __wake_up() with invalid arguments + */ + if (!(current->flags & PF_EXITING)) + sk->state_change(sk); + sk->dead = 1; - if (sk->type == SOCK_SEQPACKET) { - switch (sk->protinfo.ax25->state) { - case AX25_STATE_0: - ax25_disconnect(sk->protinfo.ax25, 0); - ax25_destroy_socket(sk->protinfo.ax25); - break; - - case AX25_STATE_1: - case AX25_STATE_2: - ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_disconnect(sk->protinfo.ax25, 0); - ax25_destroy_socket(sk->protinfo.ax25); - break; - - case AX25_STATE_3: - case AX25_STATE_4: - ax25_clear_queues(sk->protinfo.ax25); - sk->protinfo.ax25->n2count = 0; - switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_stop_t2timer(sk->protinfo.ax25); - ax25_stop_t3timer(sk->protinfo.ax25); - ax25_stop_idletimer(sk->protinfo.ax25); - break; -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25_stop_t3timer(sk->protinfo.ax25); - ax25_stop_idletimer(sk->protinfo.ax25); - break; -#endif - } - ax25_calculate_t1(sk->protinfo.ax25); - ax25_start_t1timer(sk->protinfo.ax25); - sk->protinfo.ax25->state = AX25_STATE_2; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - sk->destroy = 1; - break; + if (sk->type == SOCK_STREAM || sk->type == SOCK_SEQPACKET) { + switch (ax25->state) { + default: + ax25_remove_cb(ax25); + break; - default: - break; + case AX25_STATE_3: /* connected */ + case AX25_STATE_4: /* timer recovery */ + ax25_set_cond(ax25, AX25_COND_RELEASE); + break; } - } else { - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - ax25_destroy_socket(sk->protinfo.ax25); } - sock->sk = NULL; - sk->socket = NULL; /* Not used, but we should do this */ - + sk->protinfo.ax25 = NULL; + if (ax25->inserted && ax25->device != NULL) { + ax25->killtimer = 0; + ax25->sk = NULL; + } else { + ax25_destroy_cb(ax25); + } + ax25_destroy_socket(sk); return 0; } +/* ---------------------------------------------------------------------*/ /* - * We support a funny extension here so you can (as root) give any callsign - * digipeated via a local address as source. This is a hack until we add - * BSD 4.4 ADDIFADDR type support. It is however small and trivially backward - * compatible 8) + * New bind semantics for AF_AX25: + * + * bind(
) binds to an address and a specific device. + * listeners on this socket only receive requests from the device they bound + * to. + * + * bind(
[null_ax25_address] ) binds to an address on no specific device. + * listeners receive requests from any device. If a connection shall be + * established, the initiator must supply the full address of the destination. + * the connect call tries to find the appropriate device itself. + * + *
MUST be supplied, it will no longer be taken from the call/uid mapper. + * this is to allow applications to use all ssids of a mapped callsign as source + * address. */ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr; + struct device *dev; ax25_address *call; - ax25_dev *ax25_dev = NULL; + /* already bound */ if (sk->zapped == 0) return -EINVAL; - + /* invalid size of argument */ if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25)) return -EINVAL; - - if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25))) + if (addr_len < (addr->fsax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25))) return -EINVAL; - - if (addr->fsa_ax25.sax25_family != AF_AX25) + /* wrong family */ + if (addr->fsax25_family != AF_AX25) return -EINVAL; - call = ax25_findbyuid(current->euid); - if (call == NULL && ax25_uid_policy && !suser()) - return -EACCES; - - if (call == NULL) - sk->protinfo.ax25->source_addr = addr->fsa_ax25.sax25_call; - else - sk->protinfo.ax25->source_addr = *call; - - SOCK_DEBUG(sk, "AX25: source address set to %s\n", ax2asc(&sk->protinfo.ax25->source_addr)); - - if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) { - if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) == 0) { - ax25_dev = NULL; - SOCK_DEBUG(sk, "AX25: bound to any device\n"); - } else { - if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) { - SOCK_DEBUG(sk, "AX25: bind failed - no device\n"); - return -EADDRNOTAVAIL; - } - SOCK_DEBUG(sk, "AX25: bound to device %s\n", ax25_dev->dev->name); - } - } else { - if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) { - SOCK_DEBUG(sk, "AX25: bind failed - no device\n"); + if (!suser() && ax25_uid_policy) { + call = ax25_findbyuid(current->euid); + if (!call || ax25cmp(call, &addr->fsax25_call) == 1) + return -EPERM; + } + + sk->protinfo.ax25->addr.src = addr->fsax25_call; + sk->protinfo.ax25->addr.dcount = 0; + sk->protinfo.ax25->addr.lastrepeat = -1; + SOCK_DEBUG(sk, "AX25: source address set to %s\n", ax2asc(&sk->protinfo.ax25->addr.src)); + + if (addr_len > sizeof(struct sockaddr_ax25) + && addr->fsax25_ndigis == 1 + && ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address)) + { + if ((dev = ax25rtr_get_dev(&addr->fsa_digipeater[0])) == NULL) { + SOCK_DEBUG(sk, "AX25: bind failed - no device\n"); return -EADDRNOTAVAIL; } - SOCK_DEBUG(sk, "AX25: bound to device %s\n", ax25_dev->dev->name); + SOCK_DEBUG(sk, "AX25: bound to device %s\n", dev->name); + } else { + dev = NULL; + SOCK_DEBUG(sk, "AX25: bound to any device\n"); } - - if (ax25_dev != NULL) - ax25_fillin_cb(sk->protinfo.ax25, ax25_dev); - ax25_insert_socket(sk->protinfo.ax25); - + ax25_fillin_cb(sk->protinfo.ax25, dev); sk->zapped = 0; - SOCK_DEBUG(sk, "AX25: socket is bound\n"); return 0; } -/* - * FIXME: nonblock behaviour looks like it may have a bug. - */ -static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) +/* ---------------------------------------------------------------------*/ + +static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) { struct sock *sk = sock->sk; struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; - ax25_digi *digi = NULL; + ax25_addr_t *addr; + ax25_address *dest; int ct = 0, err; /* deal with restarts */ if (sock->state == SS_CONNECTING) { switch (sk->state) { case TCP_SYN_SENT: /* still trying */ - return -EINPROGRESS; + if (!(flags & O_NONBLOCK)) + goto wait_for_con; + return -EALREADY; case TCP_ESTABLISHED: /* connection established */ sock->state = SS_CONNECTED; return 0; case TCP_CLOSE: /* connection refused */ + case TCP_CLOSE_WAIT: sock->state = SS_UNCONNECTED; return -ECONNREFUSED; } @@ -1093,101 +835,85 @@ */ if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25)) return -EINVAL; - - if (fsa->fsa_ax25.sax25_family != AF_AX25) + if (addr_len < (fsa->fsax25_ndigis * AX25_ADDR_LEN + sizeof(struct sockaddr_ax25))) return -EINVAL; - if (sk->protinfo.ax25->digipeat != NULL) { - kfree(sk->protinfo.ax25->digipeat); - sk->protinfo.ax25->digipeat = NULL; - } - /* * Handle digi-peaters to be used. - */ - if (addr_len > sizeof(struct sockaddr_ax25) && fsa->fsa_ax25.sax25_ndigis != 0) { + */ + SOCK_DEBUG(sk, "ax25_connect: ndigi=%d\n", fsa->fsax25_ndigis); + + addr = &sk->protinfo.ax25->addr; + addr->dcount = 0; + addr->lastrepeat = -1; + if (addr_len > sizeof(struct sockaddr_ax25) && fsa->fsax25_ndigis != 0) { /* Valid number of digipeaters ? */ - if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS) + if (fsa->fsax25_ndigis < 0 || fsa->fsax25_ndigis > AX25_MAX_DIGIS) return -EINVAL; - - if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) - return -ENOBUFS; - digi->ndigi = fsa->fsa_ax25.sax25_ndigis; - digi->lastrepeat = -1; + addr->dcount = fsa->fsax25_ndigis; - while (ct < fsa->fsa_ax25.sax25_ndigis) { - if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) { - digi->repeated[ct] = 1; - digi->lastrepeat = ct; - } else { - digi->repeated[ct] = 0; - } - digi->calls[ct] = fsa->fsa_digipeater[ct]; + while (ct < fsa->fsax25_ndigis) { + if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) + addr->lastrepeat = ct; + addr->digipeater[ct] = fsa->fsa_digipeater[ct]; ct++; } + + /* where to go next? either to next digipeater in path ...*/ + dest = &addr->digipeater[addr->lastrepeat+1]; + } else { + /* ... or directly to the destination */ + dest = &fsa->fsax25_call; } /* * Must bind first - autobinding in this may or may not work. If * the socket is already bound, check to see if the device has - * been filled in, error if it hasn't. + * been filled in. If not, try to find the right device, error + * if this fails. */ if (sk->zapped) { - if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0) + if ((err = ax25_rt_autobind(sk->protinfo.ax25, dest)) < 0) return err; - ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev); - ax25_insert_socket(sk->protinfo.ax25); - } else { - if (sk->protinfo.ax25->ax25_dev == NULL) - return -EHOSTUNREACH; + SOCK_DEBUG(sk, "ax25_connect: autobind success\n"); + + } else if (sk->protinfo.ax25->device == NULL) { + if ((err = ax25_rt_fillin_dev(sk->protinfo.ax25, dest)) < 0) + return err; + SOCK_DEBUG(sk, "ax25_connect: device filled in\n"); } + addr->dest = fsa->fsax25_call; - if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, sk->protinfo.ax25->ax25_dev->dev) != NULL) { - if (digi != NULL) kfree(digi); - return -EADDRINUSE; /* Already such a connection */ + if (sk->type == SOCK_SEQPACKET) { + ax25_cb* ax25 = ax25_find_cb(addr, sk->protinfo.ax25->device); + if (ax25) { + if (ax25->state != AX25_STATE_0) + return -EADDRINUSE; /* Already such a connection */ + ax25_destroy_cb(ax25); + } } - - sk->protinfo.ax25->dest_addr = fsa->fsa_ax25.sax25_call; - sk->protinfo.ax25->digipeat = digi; + + ax25_insert_cb(sk->protinfo.ax25); /* First the easy one */ - if (sk->type != SOCK_SEQPACKET) { + if (sk->type != SOCK_SEQPACKET && sk->type != SOCK_STREAM) { sock->state = SS_CONNECTED; sk->state = TCP_ESTABLISHED; return 0; } /* Move to connecting socket, ax.25 lapb WAIT_UA.. */ - sock->state = SS_CONNECTING; - sk->state = TCP_SYN_SENT; - - switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_establish_data_link(sk->protinfo.ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - sk->protinfo.ax25->modulus = AX25_MODULUS; - sk->protinfo.ax25->window = sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - if (sk->protinfo.ax25->ax25_dev->dama.slave) - ax25_ds_establish_data_link(sk->protinfo.ax25); - else - ax25_std_establish_data_link(sk->protinfo.ax25); - break; -#endif - } - - sk->protinfo.ax25->state = AX25_STATE_1; - - ax25_start_heartbeat(sk->protinfo.ax25); + sock->state = SS_CONNECTING; + sk->state = TCP_SYN_SENT; + /* Start going SABM SABM until a UA or a give up and DM */ + ax25_establish_data_link(sk->protinfo.ax25); /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) return -EINPROGRESS; + wait_for_con: cli(); /* To avoid races on the sleep */ /* A DM or timeout will go to closed, a UA will go to ABM */ @@ -1207,12 +933,11 @@ } sock->state = SS_CONNECTED; - sti(); - return 0; } +/* ---------------------------------------------------------------------*/ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) { @@ -1256,8 +981,8 @@ } } while (skb == NULL); - newsk = skb->sk; - newsk->pair = NULL; + newsk = skb->sk; + newsk->pair = NULL; newsk->socket = newsock; newsk->sleep = &newsock->wait; @@ -1272,125 +997,107 @@ return 0; } +/* ---------------------------------------------------------------------*/ + static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { + struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr; struct sock *sk = sock->sk; - unsigned char ndigi, i; - struct full_sockaddr_ax25 fsa; + unsigned char dcount; - if (peer != 0) { + if (peer) { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; - fsa.fsa_ax25.sax25_family = AF_AX25; - fsa.fsa_ax25.sax25_call = sk->protinfo.ax25->dest_addr; - fsa.fsa_ax25.sax25_ndigis = 0; - - if (sk->protinfo.ax25->digipeat != NULL) { - ndigi = sk->protinfo.ax25->digipeat->ndigi; - fsa.fsa_ax25.sax25_ndigis = ndigi; - for (i = 0; i < ndigi; i++) - fsa.fsa_digipeater[i] = sk->protinfo.ax25->digipeat->calls[i]; - } + sax->fsax25_family = AF_AX25; + sax->fsax25_call = sk->protinfo.ax25->addr.dest; + dcount = sax->fsax25_ndigis = sk->protinfo.ax25->addr.dcount; + memcpy(sax->fsa_digipeater, sk->protinfo.ax25->addr.digipeater, dcount * AX25_ADDR_LEN); } else { - fsa.fsa_ax25.sax25_family = AF_AX25; - fsa.fsa_ax25.sax25_call = sk->protinfo.ax25->source_addr; - fsa.fsa_ax25.sax25_ndigis = 1; - if (sk->protinfo.ax25->ax25_dev != NULL) { - memcpy(&fsa.fsa_digipeater[0], sk->protinfo.ax25->ax25_dev->dev->dev_addr, AX25_ADDR_LEN); + sax->fsax25_family = AF_AX25; + sax->fsax25_call = sk->protinfo.ax25->addr.src; + + if (sk->protinfo.ax25->device != NULL) { + sax->fsax25_ndigis = 1; + memcpy(&sax->fsa_digipeater[0], sk->protinfo.ax25->device->dev_addr, AX25_ADDR_LEN); } else { - fsa.fsa_digipeater[0] = null_ax25_address; + sax->fsax25_ndigis = 0; } } - if (*uaddr_len > sizeof (struct full_sockaddr_ax25)) - *uaddr_len = sizeof (struct full_sockaddr_ax25); - memcpy(uaddr, &fsa, *uaddr_len); - + *uaddr_len = sizeof (struct full_sockaddr_ax25); return 0; } +/* ---------------------------------------------------------------------*/ + static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { struct sock *sk = sock->sk; struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name; int err; - struct sockaddr_ax25 sax; struct sk_buff *skb; unsigned char *asmptr; int size; - ax25_digi *dp; - ax25_digi dtmp; int lv; int addr_len = msg->msg_namelen; + ax25_addr_t addr; + /* only MSG_DONTWAIT is valid */ if (msg->msg_flags & ~MSG_DONTWAIT) return -EINVAL; + /* socket must be bound to a name */ if (sk->zapped) return -EADDRNOTAVAIL; + /* socket ist shut down */ if (sk->shutdown & SEND_SHUTDOWN) { send_sig(SIGPIPE, current, 0); return -EPIPE; } - if (sk->protinfo.ax25->ax25_dev == NULL) + if (sk->protinfo.ax25->device == NULL) return -ENETUNREACH; - if (usax != NULL) { + if (addr_len != 0) { if (usax->sax25_family != AF_AX25) return -EINVAL; + if (sk->type == SOCK_SEQPACKET) + return -EISCONN; if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25)) return -EINVAL; if (addr_len < (usax->sax25_ndigis * AX25_ADDR_LEN + sizeof(struct sockaddr_ax25))) return -EINVAL; if (addr_len > sizeof(struct sockaddr_ax25) && usax->sax25_ndigis != 0) { - int ct = 0; struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)usax; + int ct; /* Valid number of digipeaters ? */ - if (usax->sax25_ndigis < 1 || usax->sax25_ndigis > AX25_MAX_DIGIS) + if (usax->sax25_ndigis < 0 || usax->sax25_ndigis > AX25_MAX_DIGIS) return -EINVAL; - - dtmp.ndigi = usax->sax25_ndigis; - - while (ct < usax->sax25_ndigis) { - dtmp.repeated[ct] = 0; - dtmp.calls[ct] = fsa->fsa_digipeater[ct]; - ct++; - } - dtmp.lastrepeat = 0; + for (ct = 0; ct < usax->sax25_ndigis; ct++) + addr.digipeater[ct] = fsa->fsa_digipeater[ct]; + addr.lastrepeat = -1; + addr.dcount = usax->sax25_ndigis; } - - sax = *usax; - if (sk->type == SOCK_SEQPACKET && ax25cmp(&sk->protinfo.ax25->dest_addr, &sax.sax25_call) != 0) - return -EISCONN; - if (usax->sax25_ndigis == 0) - dp = NULL; - else - dp = &dtmp; + addr.dest = usax->sax25_call; } else { - /* - * FIXME: 1003.1g - if the socket is like this because - * it has become closed (not started closed) and is VC - * we ought to SIGPIPE, EPIPE - */ - if (sk->state != TCP_ESTABLISHED) + if (sk->state != TCP_ESTABLISHED) { + if (sk->dead) { + send_sig(SIGPIPE, current, 0); + return -EPIPE; + } return -ENOTCONN; - sax.sax25_family = AF_AX25; - sax.sax25_call = sk->protinfo.ax25->dest_addr; - dp = sk->protinfo.ax25->digipeat; + } + addr = sk->protinfo.ax25->addr; } - SOCK_DEBUG(sk, "AX.25: sendto: Addresses built.\n"); + SOCK_DEBUG(sk, "AX.25: sendto: Addresses built, building packet.\n"); - /* Build a packet */ - SOCK_DEBUG(sk, "AX.25: sendto: building packet.\n"); - /* Assume the worst case */ - size = len + 3 + ax25_addr_size(dp) + AX25_BPQ_HEADER_LEN; + size = len + 3 + ax25_sizeof_addr(&addr) + AX25_BPQ_HEADER_LEN; if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) return err; @@ -1402,12 +1109,10 @@ /* User data follows immediately after the AX.25 data */ memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); skb->nh.raw = skb->data; - + /* Add the PID if one is not supplied by the user in the skb */ - if (!sk->protinfo.ax25->pidincl) { - asmptr = skb_push(skb, 1); - *asmptr = sk->protocol; - } + if (!sk->protinfo.ax25->pidincl) + *skb_push(skb, 1) = sk->protocol; SOCK_DEBUG(sk, "AX.25: Transmitting buffer\n"); @@ -1417,44 +1122,38 @@ kfree_skb(skb); return -ENOTCONN; } - ax25_output(sk->protinfo.ax25, sk->protinfo.ax25->paclen, skb); /* Shove it onto the queue and kick */ - - return len; - } else { - asmptr = skb_push(skb, 1 + ax25_addr_size(dp)); - - SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp); - - if (dp != NULL) - SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi); - - /* Build an AX.25 header */ - asmptr += (lv = ax25_addr_build(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, AX25_COMMAND, AX25_MODULUS)); - - SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv); - - skb->h.raw = asmptr; - - SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr); - - *asmptr = AX25_UI; - - /* Datagram frames go straight out of the door as UI */ - skb->dev = sk->protinfo.ax25->ax25_dev->dev; - - ax25_queue_xmit(skb); - return len; - } + } + + addr.src = sk->protinfo.ax25->addr.src; + asmptr = skb_push(skb, ax25_sizeof_addr(&addr)+1); + + SOCK_DEBUG(sk, "Num digipeaters=%d\n", addr.dcount); + + /* Build an AX.25 header */ + lv = ax25_build_addr(asmptr, &addr, AX25_COMMAND, AX25_SEQMASK); + asmptr += lv; + + SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv); + SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr); + + *asmptr = AX25_UI; + + /* Datagram frames go straight out of the door as UI */ + ax25_send_unproto(skb, sk->protinfo.ax25->device); + + return len; } +/* ---------------------------------------------------------------------*/ + static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) { struct sock *sk = sock->sk; int copied; struct sk_buff *skb; - int er; + int err; /* * This works for seqpacket too. The receiver has ordered the @@ -1464,8 +1163,8 @@ return -ENOTCONN; /* Now we can treat all alike */ - if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) - return er; + if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err)) == NULL) + return err; if (!sk->protinfo.ax25->pidincl) skb_pull(skb, 1); /* Remove PID */ @@ -1481,25 +1180,19 @@ skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); if (msg->msg_namelen != 0) { - struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name; - ax25_digi digi; - ax25_address dest; - - ax25_addr_parse(skb->mac.raw+1, skb->data-skb->mac.raw-1, NULL, &dest, &digi, NULL, NULL); - - sax->sax25_family = AF_AX25; - /* We set this correctly, even though we may not let the - application know the digi calls further down (because it - did NOT ask to know them). This could get political... **/ - sax->sax25_ndigis = digi.ndigi; - sax->sax25_call = dest; + struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)msg->msg_name; + ax25_pktinfo pkt; - if (sax->sax25_ndigis != 0) { - int ct; - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)sax; + ax25_parse_addr(skb->mac.raw, skb->data-skb->mac.raw, &pkt); + + sax->fsax25_family = AF_AX25; + sax->fsax25_ndigis = pkt.addr.dcount; + sax->fsax25_call = pkt.addr.dest; - for (ct = 0; ct < digi.ndigi; ct++) - fsa->fsa_digipeater[ct] = digi.calls[ct]; + if (sax->fsax25_ndigis != 0) { + int ct; + for (ct = 0; ct < pkt.addr.dcount; ct++) + sax->fsa_digipeater[ct] = pkt.addr.digipeater[ct]; } msg->msg_namelen = sizeof(struct full_sockaddr_ax25); } @@ -1508,13 +1201,32 @@ return copied; } + +/* ---------------------------------------------------------------------*/ -static int ax25_shutdown(struct socket *sk, int how) +static int ax25_shutdown(struct socket *sock, int how) { - /* FIXME - generate DM and RNR states */ - return -EOPNOTSUPP; + switch (how) { + case 0: + sock->sk->shutdown = RCV_SHUTDOWN; + break; + + case 1: + sock->sk->shutdown = SEND_SHUTDOWN; + break; + + case 2: + sock->sk->shutdown = SHUTDOWN_MASK; + break; + + default: + return -EINVAL; + } + return 0; } +/* ---------------------------------------------------------------------*/ + static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; @@ -1586,15 +1298,15 @@ case SIOCAX25GETINFO: { struct ax25_info_struct ax25_info; - ax25_info.t1 = sk->protinfo.ax25->t1 / HZ; - ax25_info.t2 = sk->protinfo.ax25->t2 / HZ; - ax25_info.t3 = sk->protinfo.ax25->t3 / HZ; - ax25_info.idle = sk->protinfo.ax25->idle / (60 * HZ); + ax25_info.t1 = sk->protinfo.ax25->t1; + ax25_info.t2 = sk->protinfo.ax25->t2; + ax25_info.t3 = sk->protinfo.ax25->t3 / AX25_SLOWHZ; + ax25_info.idle = sk->protinfo.ax25->idle / AX25_SLOWHZ; ax25_info.n2 = sk->protinfo.ax25->n2; - ax25_info.t1timer = ax25_display_timer(&sk->protinfo.ax25->t1timer) / HZ; - ax25_info.t2timer = ax25_display_timer(&sk->protinfo.ax25->t2timer) / HZ; - ax25_info.t3timer = ax25_display_timer(&sk->protinfo.ax25->t3timer) / HZ; - ax25_info.idletimer = ax25_display_timer(&sk->protinfo.ax25->idletimer) / (60 * HZ); + ax25_info.t1timer = sk->protinfo.ax25->wrt_timer; + ax25_info.t3timer = sk->protinfo.ax25->wrt_timer / AX25_SLOWHZ; + ax25_info.t2timer = sk->protinfo.ax25->ack_timer; + ax25_info.idletimer = sk->protinfo.ax25->idletimer / AX25_SLOWHZ; ax25_info.n2count = sk->protinfo.ax25->n2count; ax25_info.state = sk->protinfo.ax25->state; ax25_info.rcv_q = atomic_read(&sk->rmem_alloc); @@ -1634,214 +1346,88 @@ return 0; } -static int ax25_get_info(char *buffer, char **start, off_t offset, int length, int dummy) -{ - ax25_cb *ax25; - int k; +/* ---------------------------------------------------------------------*/ + +static int ax25_print_list(char *buffer, off_t *begin, off_t offset, int length, ax25_cb *ax25, char *devname) { int len = 0; off_t pos = 0; - off_t begin = 0; + char callbuf[15]; - cli(); - - /* - * New format: - * magic dev src_addr dest_addr,digi1,digi2,.. st vs vr va t1 t1 t2 t2 t3 t3 idle idle n2 n2 rtt window paclen Snd-Q Rcv-Q inode - */ - - for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) { - len += sprintf(buffer+len, "%8.8lx %s %s%s ", - (long) ax25, - ax25->ax25_dev == NULL? "???" : ax25->ax25_dev->dev->name, - ax2asc(&ax25->source_addr), - ax25->iamdigi? "*":""); - - len += sprintf(buffer+len, "%s", ax2asc(&ax25->dest_addr)); - - for (k=0; (ax25->digipeat != NULL) && (k < ax25->digipeat->ndigi); k++) { - len += sprintf(buffer+len, ",%s%s", - ax2asc(&ax25->digipeat->calls[k]), - ax25->digipeat->repeated[k]? "*":""); - } + for ( ; ax25 != NULL; ax25 = ax25->next) { + len += sprintf(buffer + len, "%-9s ", ax2asc(&ax25->addr.dest)); - len += sprintf(buffer+len, " %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %d %d", - ax25->state, - ax25->vs, ax25->vr, ax25->va, - ax25_display_timer(&ax25->t1timer) / HZ, ax25->t1 / HZ, - ax25_display_timer(&ax25->t2timer) / HZ, ax25->t2 / HZ, - ax25_display_timer(&ax25->t3timer) / HZ, ax25->t3 / HZ, - ax25_display_timer(&ax25->idletimer) / (60 * HZ), - ax25->idle / (60 * HZ), - ax25->n2count, ax25->n2, - ax25->rtt / HZ, - ax25->window, - ax25->paclen); - + sprintf(callbuf, "%s%c", ax2asc(&ax25->addr.src), (ax25->iamdigi) ? '*' : ' '); + + len += sprintf(buffer + len, "%-10s %-4s %2d %3d %3d %3d %3d %2d %3d %3d %2d %3d/%03d %2d/%02d %3d %3d %5d", + callbuf, devname, + ax25->state, + ax25->vs, ax25->vr, ax25->va, + ax25->wrt_timer, + ax25->ack_timer, + ax25->t1 , + ax25->t2 , + ax25->t3 / AX25_SLOWHZ, + ax25->idletimer / AX25_SLOWHZ, + ax25->idle / AX25_SLOWHZ, + ax25->n2count, ax25->n2, + ax25->rtt / AX25_TICS, + ax25->window, + ax25->paclen); + if (ax25->sk != NULL) { - len += sprintf(buffer + len, " %d %d %ld\n", - atomic_read(&ax25->sk->wmem_alloc), - atomic_read(&ax25->sk->rmem_alloc), - ax25->sk->socket != NULL ? ax25->sk->socket->inode->i_ino : 0L); + len += sprintf(buffer + len, " %5d %5d\n", + atomic_read(&ax25->sk->wmem_alloc), + atomic_read(&ax25->sk->rmem_alloc)); } else { - len += sprintf(buffer + len, " * * *\n"); + len += sprintf(buffer + len, " %5d %5d\n", + ax25->write_queue.qlen, + ax25->rcv_queue.qlen); } - - pos = begin + len; - + + pos = *begin + len; + if (pos < offset) { len = 0; - begin = pos; + *begin = pos; } - + if (pos > offset + length) break; } - - sti(); - - *start = buffer + (offset - begin); - len -= (offset - begin); - - if (len > length) len = length; - return(len); + return len; } - -static struct net_proto_family ax25_family_ops = -{ - PF_AX25, - ax25_create -}; - -static struct proto_ops ax25_proto_ops = { - PF_AX25, - - sock_no_dup, - ax25_release, - ax25_bind, - ax25_connect, - sock_no_socketpair, - ax25_accept, - ax25_getname, - datagram_poll, - ax25_ioctl, - ax25_listen, - ax25_shutdown, - ax25_setsockopt, - ax25_getsockopt, - sock_no_fcntl, - ax25_sendmsg, - ax25_recvmsg -}; - -/* - * Called by socket.c on kernel start up - */ -static struct packet_type ax25_packet_type = -{ - 0, /* MUTTER ntohs(ETH_P_AX25),*/ - 0, /* copy */ - ax25_kiss_rcv, - NULL, - NULL, -}; - -static struct notifier_block ax25_dev_notifier = { - ax25_device_event, - 0 -}; - -EXPORT_SYMBOL(ax25_encapsulate); -EXPORT_SYMBOL(ax25_rebuild_header); -EXPORT_SYMBOL(ax25_findbyuid); -EXPORT_SYMBOL(ax25_find_cb); -EXPORT_SYMBOL(ax25_linkfail_register); -EXPORT_SYMBOL(ax25_linkfail_release); -EXPORT_SYMBOL(ax25_listen_register); -EXPORT_SYMBOL(ax25_listen_release); -EXPORT_SYMBOL(ax25_protocol_register); -EXPORT_SYMBOL(ax25_protocol_release); -EXPORT_SYMBOL(ax25_send_frame); -EXPORT_SYMBOL(ax25_uid_policy); -EXPORT_SYMBOL(ax25cmp); -EXPORT_SYMBOL(ax2asc); -EXPORT_SYMBOL(asc2ax); -EXPORT_SYMBOL(null_ax25_address); -EXPORT_SYMBOL(ax25_display_timer); - -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry proc_ax25_route = { - PROC_NET_AX25_ROUTE, 10, "ax25_route", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - ax25_rt_get_info -}; -static struct proc_dir_entry proc_ax25 = { - PROC_NET_AX25, 4, "ax25", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - ax25_get_info -}; -static struct proc_dir_entry proc_ax25_calls = { - PROC_NET_AX25_CALLS, 10, "ax25_calls", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - ax25_uid_get_info -}; -#endif + + +/* ---------------------------------------------------------------------*/ -__initfunc(void ax25_proto_init(struct net_proto *pro)) +static int ax25_get_info(char *buffer, char **start, off_t offset, int length, int dummy) { - sock_register(&ax25_family_ops); - ax25_packet_type.type = htons(ETH_P_AX25); - dev_add_pack(&ax25_packet_type); - register_netdevice_notifier(&ax25_dev_notifier); -#ifdef CONFIG_SYSCTL - ax25_register_sysctl(); -#endif - -#ifdef CONFIG_PROC_FS - proc_net_register(&proc_ax25_route); - proc_net_register(&proc_ax25); - proc_net_register(&proc_ax25_calls); -#endif - - printk(KERN_INFO "NET4: G4KLX/GW4PTS AX.25 for Linux. Version 0.37 for Linux NET4.0\n"); -} - -#ifdef MODULE -MODULE_AUTHOR("Jonathan Naylor G4KLX "); -MODULE_DESCRIPTION("The amateur radio AX.25 link layer protocol"); + struct device *dev; + int len = 0; + off_t begin = 0; + int i; -int init_module(void) -{ - ax25_proto_init(NULL); + start_bh_atomic(); + len += sprintf(buffer, "dest_addr src_addr dev st vs vr va t1 t2 t3 idle n2 rtt wnd paclen Snd-Q Rcv-Q\n"); - return 0; -} + len += ax25_print_list(buffer + len, &begin, offset, length, ax25_list, "*"); + + for (i = 0; i < AX25_MAX_DEVICES; i++) { + dev = ax25_devices[i]; + if (!dev || !(dev->flags & IFF_UP)) + continue; -void cleanup_module(void) -{ -#ifdef CONFIG_PROC_FS - proc_net_unregister(PROC_NET_AX25_ROUTE); - proc_net_unregister(PROC_NET_AX25); - proc_net_unregister(PROC_NET_AX25_CALLS); - proc_net_unregister(PROC_NET_AX25_ROUTE); -#endif - ax25_rt_free(); - ax25_uid_free(); - ax25_dev_free(); + len += ax25_print_list(buffer + len, &begin, offset, length, ax25_dev_list(dev), dev->name); + } + end_bh_atomic(); -#ifdef CONFIG_SYSCTL - ax25_unregister_sysctl(); -#endif - unregister_netdevice_notifier(&ax25_dev_notifier); + *start = buffer + (offset - begin); + len -= (offset - begin); - ax25_packet_type.type = htons(ETH_P_AX25); - dev_remove_pack(&ax25_packet_type); + if (len > length) len = length; - sock_unregister(PF_AX25); -} -#endif + return(len); +} #endif Index: linux/net/ax25/af_ax25.h diff -u /dev/null linux/net/ax25/af_ax25.h:1.1.4.1 --- /dev/null Wed Jul 7 01:02:31 1999 +++ linux/net/ax25/af_ax25.h Sun Jul 4 13:03:45 1999 @@ -0,0 +1,19 @@ +/* + * Interface declaration for AF_AX25 base routines layer. + * + * Matthias Welwarsky (DG2FEF) 05/28/98 + * + */ + + +#ifndef _AF_AX25_H +#define _AF_AX25_H + +#include +#include + +extern struct device* ax25rtr_get_dev(ax25_address *); +extern struct sock* ax25_make_new(struct sock*, struct device*); +extern ax25_cb *volatile ax25_list; + +#endif Index: linux/net/ax25/ax25_addr.c diff -u linux/net/ax25/ax25_addr.c:1.1.2.1 linux/net/ax25/ax25_addr.c:removed --- linux/net/ax25/ax25_addr.c:1.1.2.1 Sun Jul 4 10:18:09 1999 +++ linux/net/ax25/ax25_addr.c Wed Jul 7 01:02:31 1999 @@ -1,306 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; - * - * History - * AX.25 036 Jonathan(G4KLX) Split from ax25_subr.c. - */ - -#include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * The null address is defined as a callsign of all spaces with an - * SSID of zero. - */ -ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}}; - -/* - * ax25 -> ascii conversion - */ -char *ax2asc(ax25_address *a) -{ - static char buf[11]; - char c, *s; - int n; - - for (n = 0, s = buf; n < 6; n++) { - c = (a->ax25_call[n] >> 1) & 0x7F; - - if (c != ' ') *s++ = c; - } - - *s++ = '-'; - - if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) { - *s++ = '1'; - n -= 10; - } - - *s++ = n + '0'; - *s++ = '\0'; - - if (*buf == '\0' || *buf == '-') - return "*"; - - return buf; - -} - -/* - * ascii -> ax25 conversion - */ -ax25_address *asc2ax(char *callsign) -{ - static ax25_address addr; - char *s; - int n; - - for (s = callsign, n = 0; n < 6; n++) { - if (*s != '\0' && *s != '-') - addr.ax25_call[n] = *s++; - else - addr.ax25_call[n] = ' '; - addr.ax25_call[n] <<= 1; - addr.ax25_call[n] &= 0xFE; - } - - if (*s++ == '\0') { - addr.ax25_call[6] = 0x00; - return &addr; - } - - addr.ax25_call[6] = *s++ - '0'; - - if (*s != '\0') { - addr.ax25_call[6] *= 10; - addr.ax25_call[6] += *s++ - '0'; - } - - addr.ax25_call[6] <<= 1; - addr.ax25_call[6] &= 0x1E; - - return &addr; -} - -/* - * Compare two ax.25 addresses - */ -int ax25cmp(ax25_address *a, ax25_address *b) -{ - int ct = 0; - - while (ct < 6) { - if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */ - return 1; - ct++; - } - - if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */ - return 0; - - return 2; /* Partial match */ -} - -/* - * Compare two AX.25 digipeater paths. - */ -int ax25digicmp(ax25_digi *digi1, ax25_digi *digi2) -{ - int i; - - if (digi1->ndigi != digi2->ndigi) - return 1; - - if (digi1->lastrepeat != digi2->lastrepeat) - return 1; - - for (i = 0; i < digi1->ndigi; i++) - if (ax25cmp(&digi1->calls[i], &digi2->calls[i]) != 0) - return 1; - - return 0; -} - -/* - * Given an AX.25 address pull of to, from, digi list, command/response and the start of data - * - */ -unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama) -{ - int d = 0; - - if (len < 14) return NULL; - - if (flags != NULL) { - *flags = 0; - - if (buf[6] & AX25_CBIT) - *flags = AX25_COMMAND; - if (buf[13] & AX25_CBIT) - *flags = AX25_RESPONSE; - } - - if (dama != NULL) - *dama = ~buf[13] & AX25_DAMA_FLAG; - - /* Copy to, from */ - if (dest != NULL) - memcpy(dest, buf + 0, AX25_ADDR_LEN); - if (src != NULL) - memcpy(src, buf + 7, AX25_ADDR_LEN); - - buf += 2 * AX25_ADDR_LEN; - len -= 2 * AX25_ADDR_LEN; - - digi->lastrepeat = -1; - digi->ndigi = 0; - - while (!(buf[-1] & AX25_EBIT)) { - if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */ - if (len < 7) return NULL; /* Short packet */ - - memcpy(&digi->calls[d], buf, AX25_ADDR_LEN); - digi->ndigi = d + 1; - - if (buf[6] & AX25_HBIT) { - digi->repeated[d] = 1; - digi->lastrepeat = d; - } else { - digi->repeated[d] = 0; - } - - buf += AX25_ADDR_LEN; - len -= AX25_ADDR_LEN; - d++; - } - - return buf; -} - -/* - * Assemble an AX.25 header from the bits - */ -int ax25_addr_build(unsigned char *buf, ax25_address *src, ax25_address *dest, ax25_digi *d, int flag, int modulus) -{ - int len = 0; - int ct = 0; - - memcpy(buf, dest, AX25_ADDR_LEN); - buf[6] &= ~(AX25_EBIT | AX25_CBIT); - buf[6] |= AX25_SSSID_SPARE; - - if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT; - - buf += AX25_ADDR_LEN; - len += AX25_ADDR_LEN; - - memcpy(buf, src, AX25_ADDR_LEN); - buf[6] &= ~(AX25_EBIT | AX25_CBIT); - buf[6] &= ~AX25_SSSID_SPARE; - - if (modulus == AX25_MODULUS) - buf[6] |= AX25_SSSID_SPARE; - else - buf[6] |= AX25_ESSID_SPARE; - - if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT; - - /* - * Fast path the normal digiless path - */ - if (d == NULL || d->ndigi == 0) { - buf[6] |= AX25_EBIT; - return 2 * AX25_ADDR_LEN; - } - - buf += AX25_ADDR_LEN; - len += AX25_ADDR_LEN; - - while (ct < d->ndigi) { - memcpy(buf, &d->calls[ct], AX25_ADDR_LEN); - - if (d->repeated[ct]) - buf[6] |= AX25_HBIT; - else - buf[6] &= ~AX25_HBIT; - - buf[6] &= ~AX25_EBIT; - buf[6] |= AX25_SSSID_SPARE; - - buf += AX25_ADDR_LEN; - len += AX25_ADDR_LEN; - ct++; - } - - buf[-1] |= AX25_EBIT; - - return len; -} - -int ax25_addr_size(ax25_digi *dp) -{ - if (dp == NULL) - return 2 * AX25_ADDR_LEN; - - return AX25_ADDR_LEN * (2 + dp->ndigi); -} - -/* - * Reverse Digipeat List. May not pass both parameters as same struct - */ -void ax25_digi_invert(ax25_digi *in, ax25_digi *out) -{ - int ct; - - out->ndigi = in->ndigi; - out->lastrepeat = in->ndigi - in->lastrepeat - 2; - - /* Invert the digipeaters */ - for (ct = 0; ct < in->ndigi; ct++) { - out->calls[ct] = in->calls[in->ndigi - ct - 1]; - - if (ct <= out->lastrepeat) { - out->calls[ct].ax25_call[6] |= AX25_HBIT; - out->repeated[ct] = 1; - } else { - out->calls[ct].ax25_call[6] &= ~AX25_HBIT; - out->repeated[ct] = 0; - } - } -} - -#endif Index: linux/net/ax25/ax25_core.c diff -u /dev/null linux/net/ax25/ax25_core.c:1.1.4.1 --- /dev/null Wed Jul 7 01:02:31 1999 +++ linux/net/ax25/ax25_core.c Sun Jul 4 13:03:45 1999 @@ -0,0 +1,508 @@ +/* + * net/ax25/ax25_core.c + * + * AX.25 core functions. These were taken out of af_ax25.c. + * + * This code REQUIRES NET4 + * + * This module: + * This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * History: + * 06/21/98 Matthias(DG2FEF) Initial Coding + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include + +#include "af_ax25.h" +#include "ax25_vj.h" +#include "ax25_ddi.h" +#include "ax25_core.h" +#include "ax25_in.h" +#include "ax25_subr.h" + +/* ---------------------------------------------------------------------*/ +/* + * The null address is defined as a callsign of all spaces with an + * SSID of zero. + */ +ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}}; + +/* --------------------------------------------------------------------- */ +/* + * ax25 -> ascii conversion + */ +char *ax2asc(ax25_address *a) +{ + static char buf[11]; + char c, *s; + int n; + + for (n = 0, s = buf; n < 6; n++) { + c = (a->ax25_call[n] >> 1) & 0x7F; + + if (c != ' ') *s++ = c; + } + + *s++ = '-'; + + if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) { + *s++ = '1'; + n -= 10; + } + + *s++ = n + '0'; + *s++ = '\0'; + + if (*buf == '\0' || *buf == '-') + return "*"; + + return buf; + +} + +/* ---------------------------------------------------------------------*/ +/* + * ascii -> ax25 conversion + */ +ax25_address *asc2ax(char *callsign) +{ + static ax25_address addr; + char *s; + int n; + + for (s = callsign, n = 0; n < 6; n++) { + if (*s != '\0' && *s != '-') + addr.ax25_call[n] = *s++; + else + addr.ax25_call[n] = ' '; + addr.ax25_call[n] <<= 1; + addr.ax25_call[n] &= 0xFE; + } + + if (*s++ == '\0') { + addr.ax25_call[6] = 0x00; + return &addr; + } + + addr.ax25_call[6] = *s++ - '0'; + + if (*s != '\0') { + addr.ax25_call[6] *= 10; + addr.ax25_call[6] += *s++ - '0'; + } + + addr.ax25_call[6] <<= 1; + addr.ax25_call[6] &= 0x1E; + + return &addr; +} + +/* ---------------------------------------------------------------------*/ +/* + * Compare two ax.25 addresses + */ +int ax25cmp(ax25_address *a, ax25_address *b) +{ + int ct = 0; + + while (ct < 6) { + if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */ + return 1; + ct++; + } + + if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */ + return 0; + + return 2; /* Partial match */ +} + +/* ---------------------------------------------------------------------*/ +/* + * Add a socket to the bound sockets list. + */ +void ax25_insert_cb(ax25_cb *ax25) +{ + if (ax25->device != NULL) { + ax25_dev_insert_cb(ax25); + return; + } + + ax25->prev = NULL; + ax25->next = ax25_list; + start_bh_atomic(); + if (ax25_list != NULL) + ax25_list->prev = ax25; + ax25_list = ax25; + end_bh_atomic(); + + ax25->inserted = 1; +} + +/* ---------------------------------------------------------------------*/ +/* + * Socket removal is now protected agains bottom half ints + * with a start/end_bh_atomic bracket. There should be no + * need to mask interrupts on hardware level. + */ +void ax25_remove_cb(ax25_cb *ax25) +{ + /* + * unbound sockets are not in any list + */ + + if (!ax25->inserted) + return; + + if (ax25->device != NULL) { + ax25_dev_remove_cb(ax25); + return; + } + + + if (ax25_list != NULL) { + start_bh_atomic(); + if (ax25->prev == NULL) + ax25_list = ax25->next; + else + ax25->prev->next = ax25->next; + + if (ax25->next != NULL) + ax25->next->prev = ax25->prev; + end_bh_atomic(); + } + + ax25->inserted = 0; + +} + +/* ---------------------------------------------------------------------*/ +/* + * Find an AX.25 control block given both ends. + */ +ax25_cb *ax25_find_cb(ax25_addr_t *addr, struct device *dev) +{ + ax25_cb *s; + + start_bh_atomic(); + for (s = ax25_dev_list(dev) ; s != NULL; s = s->next) { + + if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET) + continue; + + if (s->addr.dcount == addr->dcount && !ax25cmp(&s->addr.src, &addr->src) + && !ax25cmp(&s->addr.dest, &addr->dest)) + { + int i; + + if (addr->dcount == 0) + break; + if (addr->lastrepeat != s->addr.lastrepeat) + continue; + i = addr->dcount; + while (i--) { + if (ax25cmp(&s->addr.digipeater[i], &addr->digipeater[i])) + break; + } + if (i < 0) + break; + } + } + end_bh_atomic(); + return s; +} + +/* ---------------------------------------------------------------------*/ + +void ax25_destroy_cb(ax25_cb *ax25) +{ + ax25_remove_cb(ax25); + ax25_clear_queues(ax25); + ax25_free_cb(ax25); +} + +/* ---------------------------------------------------------------------*/ + +void ax25_destroy_socket(struct sock *sk) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { + /* + * this may be a pending SABM, waiting in the receive queue + * to become accepted. But the listener just died :-) + */ + if (skb->sk != sk) { + /* + * signal the peer that we have closed the socket. + * if he has already disconnected himself, just mark + * the socket dead and move to TCP_CLOSE. This is only + * for security, as ax25_disconnect should have already + * done this. + */ + if (skb->sk->state == TCP_ESTABLISHED) + skb->sk->protinfo.ax25->condition |= AX25_COND_RELEASE; + else { + printk(KERN_DEBUG "ax25_destroy_socket: TCP_CLOSE\n"); + skb->sk->state = TCP_CLOSE; + } + skb->sk->dead = 1; + } + kfree_skb(skb); + } + sk_free(sk); +} + +/* ---------------------------------------------------------------------*/ +/* + * Fill in a created AX.25 control block with the default + * values for a particular device. + */ +void ax25_fillin_cb(ax25_cb *ax25, struct device *dev) +{ + ax25->device = dev; + ax25->vs_rtt = -1; + + if (dev != NULL) { + ax25->rtt = ax25_dev_get_value(dev, AX25_VALUES_T1) / 4; + ax25->t1 = ax25_dev_get_value(dev, AX25_VALUES_T1); + ax25->t2 = ax25_dev_get_value(dev, AX25_VALUES_T2); + ax25->t3 = ax25_dev_get_value(dev, AX25_VALUES_T3); + ax25->n2 = ax25_dev_get_value(dev, AX25_VALUES_N2); + ax25->paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN); + ax25->idle = ax25_dev_get_value(dev, AX25_VALUES_IDLE); + ax25->backoff = ax25_dev_get_value(dev, AX25_VALUES_BACKOFF); + + if (ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE)) { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW); + } else { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW); + } + } else { + ax25->rtt = AX25_DEF_T1 / 4; + ax25->t1 = AX25_DEF_T1; + ax25->t2 = AX25_DEF_T2; + ax25->t3 = AX25_DEF_T3; + ax25->n2 = AX25_DEF_N2; + ax25->paclen = AX25_DEF_PACLEN; + ax25->idle = AX25_DEF_IDLE; + ax25->backoff = AX25_DEF_BACKOFF; + + if (AX25_DEF_AXDEFMODE) { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = AX25_DEF_EWINDOW; + } else { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = AX25_DEF_WINDOW; + } + } +} + +/* ---------------------------------------------------------------------*/ +/* + * Create an empty AX.25 control block. + */ +ax25_cb *ax25_create_cb(void) +{ + ax25_cb *ax25; + + if ((ax25 = (ax25_cb *)kmalloc(sizeof(ax25_cb), GFP_ATOMIC)) == NULL) + return NULL; + + MOD_INC_USE_COUNT; + + memset(ax25, 0x00, sizeof(ax25_cb)); + + skb_queue_head_init(&ax25->write_queue); + skb_queue_head_init(&ax25->frag_queue); + skb_queue_head_init(&ax25->ack_queue); + skb_queue_head_init(&ax25->rcv_queue); + + ax25->state = AX25_LISTEN; + ax25->condition = AX25_COND_SETUP; + + return ax25; +} + +/* ---------------------------------------------------------------------*/ +/* + * Free an allocated ax25 control block. This is done to centralise + * the MOD count code. + */ +void ax25_free_cb(ax25_cb *ax25) +{ + if (ax25->slcomp != NULL) { + axhc_free(ax25->slcomp); + } + + if (ax25->peer != NULL) { + ax25->peer->peer = NULL; + } + + kfree(ax25); + + MOD_DEC_USE_COUNT; +} + +/* ---------------------------------------------------------------------*/ + +void ax25_free_sock(struct sock *sk) +{ + if (sk->protinfo.ax25 != NULL) + ax25_free_cb(sk->protinfo.ax25); +} + +/* ---------------------------------------------------------------------*/ +/* + * Given an AX.25 address pull of to, from, digi list, command/response and the start of data + * + */ +unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_pktinfo *pkt_info) +{ + int d = 0; + + if (len < 15) + return NULL; + + pkt_info->cmdrsp = 0; + + memcpy(&pkt_info->addr.dest, buf, AX25_ADDR_LEN); + + if (buf[6] & AX25_CBIT) + pkt_info->cmdrsp = AX25_COMMAND; + buf += AX25_ADDR_LEN; + len -= AX25_ADDR_LEN; + + memcpy(&pkt_info->addr.src, buf, AX25_ADDR_LEN); + + if (buf[6] & AX25_CBIT) + pkt_info->cmdrsp = AX25_RESPONSE; + + if (!(buf[6] & AX25_DAMA_FLAG)) + pkt_info->dama = 1; + + pkt_info->addr.lastrepeat = -1; + pkt_info->addr.dcount = 0; + + while (!(buf[6] & AX25_EBIT)) { + buf += AX25_ADDR_LEN; + len -= AX25_ADDR_LEN; + + if (d < AX25_MAX_DIGIS && len >= 7) { + memcpy(&pkt_info->addr.digipeater[d], buf, AX25_ADDR_LEN); + if (buf[6] & AX25_HBIT) + pkt_info->addr.lastrepeat = d; + ++d; + pkt_info->addr.dcount = d; + } else + return NULL; + } + + return buf + AX25_ADDR_LEN; +} + +/* ---------------------------------------------------------------------*/ +/* + * Assemble an AX.25 header from the bits + */ +int ax25_build_addr(unsigned char *buf, ax25_addr_t *addr, int flag, int seqmask) +{ + int len = 0; + int ct = 0; + + memcpy(buf, &addr->dest, AX25_ADDR_LEN); + buf[6] &= ~(AX25_EBIT | AX25_CBIT); + buf[6] |= AX25_SSSID_SPARE; + + if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT; + + buf += AX25_ADDR_LEN; + len += AX25_ADDR_LEN; + + memcpy(buf, &addr->src, AX25_ADDR_LEN); + buf[6] &= ~(AX25_EBIT | AX25_CBIT); + buf[6] &= ~AX25_SSSID_SPARE; + + if (seqmask == AX25_SEQMASK) + buf[6] |= AX25_SSSID_SPARE; + else + buf[6] |= AX25_ESSID_SPARE; + + if (flag == AX25_RESPONSE) + buf[6] |= AX25_CBIT; + + /* + * Fast path the normal digiless path + */ + if (addr->dcount == 0) { + buf[6] |= AX25_EBIT; + return 2 * AX25_ADDR_LEN; + } + + buf += AX25_ADDR_LEN; + len += AX25_ADDR_LEN; + + while (ct < addr->dcount) { + memcpy(buf, &addr->digipeater[ct], AX25_ADDR_LEN); + + if (ct <= addr->lastrepeat) + buf[6] |= AX25_HBIT; + else + buf[6] &= ~AX25_HBIT; + + buf[6] &= ~AX25_EBIT; + buf[6] |= AX25_SSSID_SPARE; + + buf += AX25_ADDR_LEN; + len += AX25_ADDR_LEN; + ct++; + } + + buf[-1] |= AX25_EBIT; + + return len; +} + +/* ---------------------------------------------------------------------*/ + +int ax25_sizeof_addr(ax25_addr_t *addr) +{ + return AX25_ADDR_LEN * (addr->dcount+2); +} + +/* ---------------------------------------------------------------------*/ +/* + * Invert AX.25 address. May not pass both parameters as same struct + */ +void ax25_invert_addr(ax25_addr_t *in, ax25_addr_t *out) +{ + ax25_address *ip, *op; + int dcount; + + dcount = out->dcount = in->dcount; + out->lastrepeat = dcount - in->lastrepeat - 2; + + /* source/destination */ + out->dest = in->src; + out->src = in->dest; + + /* Invert the digipeaters */ + if (dcount) { + ip = in->digipeater; + op = out->digipeater + (dcount-1); /* pointer scaled! */ + while (dcount--) + *op-- = *ip++; + } +} Index: linux/net/ax25/ax25_core.h diff -u /dev/null linux/net/ax25/ax25_core.h:1.1.4.1 --- /dev/null Wed Jul 7 01:02:31 1999 +++ linux/net/ax25/ax25_core.h Sun Jul 4 13:03:45 1999 @@ -0,0 +1,21 @@ +#ifndef _AX25_CORE_H +#define _AX25_CORE_H + +extern char* ax2asc(ax25_address*); +extern int ax25cmp(ax25_address*, ax25_address*); +extern ax25_address* asc2ax(char*); +extern void ax25_insert_cb(ax25_cb*); +extern void ax25_remove_cb(ax25_cb*); +extern ax25_cb* ax25_find_cb(ax25_addr_t*, struct device*); +extern void ax25_destroy_cb(ax25_cb*); +extern void ax25_destroy_socket(struct sock*); +extern void ax25_fillin_cb(ax25_cb*, struct device*); +extern ax25_cb* ax25_create_cb(void); +extern void ax25_free_cb(ax25_cb*); +extern void ax25_free_sock(struct sock*); +extern int ax25_build_addr(unsigned char*, ax25_addr_t*, int, int); +extern int ax25_sizeof_addr(ax25_addr_t*); +extern void ax25_invert_addr(ax25_addr_t*, ax25_addr_t*); + +extern ax25_address null_ax25_address; +#endif Index: linux/net/ax25/ax25_ctl.c diff -u /dev/null linux/net/ax25/ax25_ctl.c:1.1.4.1 --- /dev/null Wed Jul 7 01:02:31 1999 +++ linux/net/ax25/ax25_ctl.c Sun Jul 4 13:03:45 1999 @@ -0,0 +1,117 @@ +#include +#include +#include + +#include "af_ax25.h" +#include "ax25_in.h" +#include "ax25_subr.h" + +/* + * dl1bke 960311: set parameters for existing AX.25 connections, + * includes a KILL command to abort any connection. + * VERY useful for debugging ;-) + */ +int ax25_ctl_ioctl(const unsigned int cmd, void *arg) +{ + struct ax25_ctl_struct ax25_ctl; + struct device *dev; + ax25_cb *ax25; + ax25_addr_t addr; + int err; + + if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_ctl))) != 0) + return err; + + if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl))) + return -EFAULT;; + + if ((dev = ax25rtr_get_dev(&ax25_ctl.port_addr)) == NULL) + return -ENODEV; + + addr.src = ax25_ctl.source_addr; + addr.dest = ax25_ctl.dest_addr; + addr.dcount = 0; + + if ((ax25 = ax25_find_cb(&addr, dev)) == NULL) + return -ENOTCONN; + + switch (ax25_ctl.cmd) { + case AX25_KILL: + ax25_tx_command(ax25, AX25_DISC, AX25_POLLON); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25_disconnect(ax25, ENETRESET); + if (ax25->sk) + ax25_close_socket(ax25->sk, ENETRESET); + break; + + case AX25_WINDOW: + if (ax25->seqmask == AX25_SEQMASK) { + if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7) + return -EINVAL; + } else { + if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63) + return -EINVAL; + } + ax25->window = ax25_ctl.arg; + break; + + case AX25_T1: + if (ax25_ctl.arg < 1) + return -EINVAL; + ax25->rtt = (ax25_ctl.arg * AX25_TICS) / 4; + ax25->t1 = ax25_ctl.arg; + start_bh_atomic(); + if (ax25->wrt_timer > ax25->t1) + ax25->wrt_timer = ax25->t1; + end_bh_atomic(); + break; + + case AX25_T2: + if (ax25_ctl.arg < 0) + return -EINVAL; + ax25->t2 = ax25_ctl.arg; + start_bh_atomic(); + if (ax25->ack_timer > ax25->t2) + ax25->ack_timer = ax25->t2; + end_bh_atomic(); + break; + + case AX25_N2: + if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31) + return -EINVAL; + ax25->n2count = 0; + ax25->n2 = ax25_ctl.arg; + break; + + case AX25_T3: + if (ax25_ctl.arg < 0) + return -EINVAL; + ax25->t3 = ax25_ctl.arg * AX25_SLOWHZ; + start_bh_atomic(); + if (ax25->wrt_timer != 0) + ax25->wrt_timer = ax25->t3; + end_bh_atomic(); + break; + + case AX25_IDLE: + if (ax25_ctl.arg < 0) + return -EINVAL; + ax25->idle = ax25_ctl.arg * AX25_SLOWHZ; + start_bh_atomic(); + if (ax25->idletimer != 0) + ax25->idletimer = ax25->idle; + end_bh_atomic(); + break; + + case AX25_PACLEN: + if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535) + return -EINVAL; + ax25->paclen = ax25_ctl.arg; + break; + + default: + return -EINVAL; + } + + return 0; +} Index: linux/net/ax25/ax25_ctl.h diff -u /dev/null linux/net/ax25/ax25_ctl.h:1.1.4.1 --- /dev/null Wed Jul 7 01:02:31 1999 +++ linux/net/ax25/ax25_ctl.h Sun Jul 4 13:03:45 1999 @@ -0,0 +1,6 @@ +#ifndef _AX25_CTL_H +#define _AX25_CTL_H + +extern int ax25_ctl_ioctl(const unsigned int, void*); + +#endif Index: linux/net/ax25/ax25_ddi.c diff -u /dev/null linux/net/ax25/ax25_ddi.c:1.1.4.1 --- /dev/null Wed Jul 7 01:02:31 1999 +++ linux/net/ax25/ax25_ddi.c Sun Jul 4 13:03:45 1999 @@ -0,0 +1,1008 @@ +/* + * This code REQUIRES 1.2.1 or higher/ NET3.029 + * + * This module: + * This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "af_ax25.h" +#include "ax25_ddi.h" +#include "ax25_core.h" +#include "ax25_in.h" +#include "ax25_subr.h" + +struct device *ax25_devices[AX25_MAX_DEVICES]; + +/* + * ------------------------------------------------------------------------ + * declaration of private functions + * ------------------------------------------------------------------------ + */ + +static void clear_ax25devices(void); +static void register_ax25device(struct device*); +static void unregister_ax25device(struct device*); +static void ax25_dev_timer(unsigned long); +static void ax25_dev_tic(unsigned long); +static void ax25_transmit_buffer(ax25_cb*, struct sk_buff*, int); +static void ax25_send_iframe(ax25_cb*, struct sk_buff*, int); +static void ax25_send_control(ax25_cb*, int, int, int); +static void ax25_kick_device(struct ax25_dev*); +static __inline__ void ax25_dev_add_ready(struct ax25_dev *, ax25_cb *); +static __inline__ void ax25_dev_remove_active(struct ax25_dev *); +static __inline__ void ax25_dev_remove_ready(struct ax25_dev *, ax25_cb *); +static void ax25_dev_set_tic(struct ax25_dev *); +static void ax25_dev_set_timer(struct ax25_dev *, unsigned int); +static void ax25_queue_xmit(struct sk_buff *); +static struct ax25_dev *ax25_dev_get_dev(struct device *); + +/* + * ------------------------------------------------------------------------ + * Interface implementation + * All public functions of this module are defined here + * ------------------------------------------------------------------------ + */ + +void ax25_ddi_init(void) +{ + clear_ax25devices(); +} + +/* + * queue a fully assembled frame in the unproto queue of the + * device and mark the channel ready for transmission + */ +void ax25_send_unproto(struct sk_buff* skb, struct device* dev) +{ + struct ax25_dev* ax25_device = AX25_PTR(dev); + + skb->dev = dev; + skb_queue_tail(&ax25_device->unproto_queue, skb); + ax25_kick_device(ax25_device); +} + +void ax25_send_broadcast(struct sk_buff *skb) +{ + int i; + + for (i = 0; i < AX25_MAX_DEVICES; i++) { + struct device *dev = ax25_devices[i]; + + if (dev != NULL && (dev->flags & (IFF_UP|IFF_BROADCAST)) != 0) { + struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); + if (newskb != NULL) + ax25_send_unproto(newskb, dev); + else + printk(KERN_ERR "ax25_send_broadcast: unable to clone packet.\n"); + } + } + + /* caller frees original packet */ +} + +/* + * put a connection on the ready list of it's device and mark the device + * ready for transmission. + */ +void ax25_kick(ax25_cb *ax25) +{ + if (ax25->device != NULL) { + struct ax25_dev *ax25_device = AX25_PTR(ax25->device); + + /* + * put the connection on the readylist of this channel, + * if it's not already there. + */ + ax25_dev_add_ready(ax25_device, ax25); + + /* + * mark the channel ready + */ + ax25_kick_device(ax25_device); + } +} + +/* + * return the connection list to a given device + */ +ax25_cb *ax25_dev_list(struct device *dev) +{ + struct ax25_dev *ax25_device; + + if (dev == NULL) + return ax25_list; + + if ((ax25_device = AX25_PTR(dev)) != NULL && ax25_device->magic == AX25_DEV_MAGIC) + return ax25_device->list.all; + + return NULL; +} + +/* + * insert a connection into a device queue + */ +void ax25_dev_insert_cb(ax25_cb *ax25) +{ + struct ax25_dev *ax25_device = AX25_PTR(ax25->device); + + if (ax25_device->magic != AX25_DEV_MAGIC) { + printk(KERN_ERR "ax25_dev_insert_cb: wrong magic number.\n"); + return; + } + + ax25->prev = NULL; + ax25->next = ax25_device->list.all; + start_bh_atomic(); + if (ax25_device->list.all != NULL) + ax25_device->list.all->prev = ax25; + ax25_device->list.all = ax25; + end_bh_atomic(); + + ax25->inserted = 1; +} + +/* + * remove a connection from a device queue + */ +void ax25_dev_remove_cb(ax25_cb *ax25) +{ + struct ax25_dev *ax25_device = AX25_PTR(ax25->device); + + if (ax25_device->magic != AX25_DEV_MAGIC) { + printk(KERN_ERR "ax25_dev_remove_cb: wrong magic number.\n"); + return; + } + + if (ax25_device->list.all == NULL) { + printk(KERN_ERR "ax25_dev_remove_cb: empty list.\n"); + return; + } + + start_bh_atomic(); + if (ax25->prev == NULL) + ax25_device->list.all = ax25->next; + else + ax25->prev->next = ax25->next; + + if (ax25->next != NULL) + ax25->next->prev = ax25->prev; + + ax25->inserted = 0; + + if (xchg(&ax25->ready.state, AX25_SCHED_IDLE) == AX25_SCHED_READY) + ax25_dev_remove_ready(ax25_device, ax25); + end_bh_atomic(); +} + +/* + * Look for any matching address. + */ +int ax25_dev_match_addr(ax25_address *addr, struct device *dev) +{ + ax25_cb *s; + + for (s = ax25_dev_list(dev); s != NULL; s = s->next) { + if (s->state == AX25_LISTEN && s->sk == NULL && ax25cmp(&s->addr.src, addr) == 0) + return 1; + } + return 0; +} + +/* + * Find a control block that wants to accept the SABM we have just + * received. + */ +ax25_cb *ax25_dev_find_listener(ax25_address *addr, int digi, struct device *dev) +{ + ax25_cb *s; + + start_bh_atomic(); + for (s = ax25_dev_list(dev); s != NULL; s = s->next) { + if (s->state != AX25_LISTEN) + continue; + if ((s->iamdigi && !digi) || (!s->iamdigi && digi)) + continue; + if (ax25cmp(&s->addr.src, addr) == 0) { + end_bh_atomic(); + return s; + } + } + end_bh_atomic(); + return NULL; +} + +/* + * Find an AX.25 socket given both ends. + */ +struct sock *ax25_dev_find_socket(ax25_address *my_addr, ax25_address *dest_addr, struct device *dev, int type) +{ + ax25_cb *s; + + start_bh_atomic(); + for (s = ax25_dev_list(dev); s != NULL; s = s->next) { + if (s->sk != NULL && ax25cmp(&s->addr.src, my_addr) == 0 + && ax25cmp(&s->addr.dest, dest_addr) == 0 && s->sk->type == type) { + end_bh_atomic(); + return s->sk; + } + } + end_bh_atomic(); + return NULL; +} + +/* + * Wow, a bit of data hiding. Is this C++ or what ? + */ +int ax25_dev_get_value(struct device *dev, int valueno) +{ + struct ax25_dev *ax25_device = AX25_PTR(dev); + + if (ax25_device->magic != AX25_DEV_MAGIC) { + printk(KERN_WARNING "ax25_dev_get_value called with invalid device.\n"); + return 1; + } + + return ax25_device->values[valueno]; +} + +/* + * This is called when an interface is brought up. These are + * reasonable defaults. + */ +void ax25_dev_device_up(struct device *dev) +{ + struct ax25_dev *ax25_device = AX25_PTR(dev); + + if (!ax25_device || ax25_device->magic != AX25_DEV_MAGIC) + return; + +#ifdef CONFIG_SYSCTL + ax25_unregister_sysctl(); +#endif + ax25_device->netdev = dev; + ax25_device->forward = NULL; + ax25_device->list.all = NULL; + ax25_device->list.ready = NULL; + + skb_queue_head_init(&ax25_device->unproto_queue); + + ax25_device->hw.persistence = 1; + ax25_device->bytes_sent = 0; + + ax25_device->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE; + ax25_device->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE; + ax25_device->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF; + ax25_device->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE; + ax25_device->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW; + ax25_device->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW; + ax25_device->values[AX25_VALUES_T1] = AX25_DEF_T1; + + ax25_device->values[AX25_VALUES_T3] = AX25_DEF_T3; + ax25_device->values[AX25_VALUES_IDLE] = AX25_DEF_IDLE; + ax25_device->values[AX25_VALUES_N2] = AX25_DEF_N2; + ax25_device->values[AX25_VALUES_PACLEN] = AX25_DEF_PACLEN; + + init_timer(&ax25_device->timer); + ax25_dev_set_timer(ax25_device, AX25_TICS); + + init_timer(&ax25_device->tics); + ax25_dev_set_tic(ax25_device); + + register_ax25device(dev); +#ifdef CONFIG_SYSCTL + ax25_register_sysctl(); +#endif +} + +/* + * this is called when a device is brought down. delete the device + * timers and update the sysctl interface. + */ +void ax25_dev_device_down(struct device *dev) +{ + struct ax25_dev *ax25_device = AX25_PTR(dev); + + if (!ax25_device || ax25_device->magic != AX25_DEV_MAGIC) { + printk(KERN_ERR "ax25_dev_device_down: not an AX.25 device.\n"); + return; + } + + del_timer(&ax25_device->timer); + del_timer(&ax25_device->tics); + + start_bh_atomic(); + skb_queue_purge(&ax25_device->unproto_queue); + end_bh_atomic(); + +#ifdef CONFIG_SYSCTL + ax25_unregister_sysctl(); +#endif + unregister_ax25device(dev); +#ifdef CONFIG_SYSCTL + ax25_register_sysctl(); +#endif +} + +/* + * packet forwarding control ioctl + * FIXME: does anybody really need this feature? + */ +int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd) +{ + struct device *dev; + struct ax25_dev *ax25_dev; + + if ((dev = ax25rtr_get_dev(&fwd->port_from)) == NULL) + return -EINVAL; + + if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL) + return -EINVAL; + + switch (cmd) { + case SIOCAX25ADDFWD: + if ((dev = ax25rtr_get_dev(&fwd->port_to)) == NULL) + return -EINVAL; + if (ax25_dev->forward != NULL) + return -EINVAL; + ax25_dev->forward = dev; + break; + + case SIOCAX25DELFWD: + if (ax25_dev->forward == NULL) + return -EINVAL; + ax25_dev->forward = NULL; + break; + + default: + return -EINVAL; + } + + return 0; +} + +struct device *ax25_fwd_dev(struct device *dev) +{ + struct ax25_dev *ax25_dev; + + if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL) + return dev; + + if (ax25_dev->forward == NULL) + return dev; + + return ax25_dev->forward; +} + +int ax25_dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +{ + int i; + struct device *dev; + char devname[7]; + + int len = 0; + off_t pos = 0; + off_t begin = 0; + + len += sprintf(buffer, "device name rifr tifr rrej rkby tkby bitrate ppers\n"); + + for (i = 0; i < AX25_MAX_DEVICES; i++) { + if ((dev = ax25_devices[i]) != NULL) { + strncpy(devname, dev->name, 6); + devname[6] = 0; + len += sprintf(buffer+len, "%6s %9s %9ld %9ld %9ld %9ld %9ld %9d %5d\n", + devname, ax2asc((ax25_address *)dev->dev_addr), + AX25_PTR(dev)->rx_iframes, AX25_PTR(dev)->tx_iframes, + AX25_PTR(dev)->rx_rejects, + AX25_PTR(dev)->rx_bytes/1024, + AX25_PTR(dev)->tx_bytes/1024, + ax25_dev_getbitrate(dev), AX25_PTR(dev)->hw.persistence); + + pos = begin + len; + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + break; + } + } + + *start = buffer + (offset - begin); + len -= offset - begin; + + if (len > length) len = length; + + return len; +} + +/* + * ------------------------------------------------------------------------ + * End of public area, all private functions of this module are defined + * here. + * ------------------------------------------------------------------------ + */ + +static void clear_ax25devices(void) +{ + int i; + + for (i = 0; i < AX25_MAX_DEVICES; i++) + ax25_devices[i] = NULL; +} + +static void register_ax25device(struct device *dev) +{ + int i; + + for (i = 0; i < AX25_MAX_DEVICES; i++) { + if (ax25_devices[i] == NULL) { + ax25_devices[i] = dev; + return; + } + } + + printk(KERN_ERR "AX.25: cannot register device.\n"); +} + +static void unregister_ax25device(struct device *dev) +{ + int i; + + for (i = 0; i < AX25_MAX_DEVICES; i++) { + if (ax25_devices[i] == dev) { + ax25_devices[i] = NULL; + return; + } + } +} + +/* + * simple pseudo-random number generator, stolen from hdlcdrv.c :) + */ +static inline unsigned short random_num(void) +{ + static unsigned short random_seed; + + random_seed = 28629 * random_seed + 157; + return random_seed & 0xFF; +} + +/* + * add a connection to the channels readylist + */ +static inline void ax25_dev_add_ready(struct ax25_dev *ax25_device, ax25_cb *ax25) +{ + start_bh_atomic(); + if (ax25->ready.state != AX25_SCHED_READY) { + ax25->ready.state = AX25_SCHED_READY; + if (ax25_device->list.ready == NULL) { + ax25->ready.prev = ax25; + ax25->ready.next = ax25; + ax25_device->list.ready = ax25; + } else { + ax25->ready.next = ax25_device->list.ready; + ax25->ready.prev = ax25_device->list.ready->ready.prev; + ax25_device->list.ready->ready.prev->ready.next = ax25; + ax25_device->list.ready->ready.prev = ax25; + } + } + end_bh_atomic(); +} + +/* + * remove the active connection from the channels readylist + */ +static inline void ax25_dev_remove_active(struct ax25_dev *ax25_device) +{ + ax25_cb *active = ax25_device->list.ready; + + if (active->ready.next == active) { + ax25_device->list.ready = NULL; + } else { + ax25_device->list.ready = active->ready.next; + active->ready.next->ready.prev = active->ready.prev; + active->ready.prev->ready.next = active->ready.next; + } + active->ready.state = AX25_SCHED_IDLE; +} + +/* + * remove a connection from the channels readylist + */ +static inline void ax25_dev_remove_ready(struct ax25_dev *ax25_device, ax25_cb *ax25) +{ + ax25_cb *active = ax25_device->list.ready; + + if (ax25 == active) { + ax25_dev_remove_active(ax25_device); + } else { + ax25->ready.next->ready.prev = ax25->ready.prev; + ax25->ready.prev->ready.next = ax25->ready.next; + ax25->ready.state = AX25_SCHED_IDLE; + } +} + +/* + * Timer for a per device 100ms timing tic. AX.25 Timers of all + * connections on this device are driven by this timer. + */ +static void ax25_dev_set_tic(struct ax25_dev *this) +{ + this->tics.data = (unsigned long)this; + this->tics.function = &ax25_dev_tic; + this->tics.expires = jiffies + AX25_TICS; + + add_timer(&this->tics); +} + +static void ax25_dev_tic(unsigned long param) +{ + ax25_cb *active; + struct ax25_dev *this = (struct ax25_dev *)param; + + if (!this->needs_transmit && !this->hw.ptt(this->netdev)) { + for (active = this->list.all; active; active = active->next) { + /* + * only run the timer on idle connections. + */ + if (!active->ready.state) + ax25_timer(active); + } + } + ax25_dev_set_tic(this); +} + +/* + * Timer for channel access arbitration. Fires every 100ms if the channel + * is idle (i.e. no connections need to transmit), and in intervals of + * half of a frame length if trying to transmit + */ +static void ax25_dev_set_timer(struct ax25_dev *this, unsigned int tics) +{ + this->timer.data = (unsigned long)this; + this->timer.function = &ax25_dev_timer; + this->timer.expires = jiffies + tics; + + add_timer(&this->timer); +} + +static void ax25_dev_timer(unsigned long param) +{ + struct ax25_dev *this = (struct ax25_dev *)param; + ax25_cb *active; + struct sk_buff *skb; + unsigned int bytes_sent = 0; + unsigned int max_bytes; + + /* + * this implements a rather innovative channel access method. + * the basic idea is to run the usual slottime/persistence + * scheme, but with two significant changes: + * 1. slottime is derived from the bitrate of the channel + * 2. persistence is variable, depending on the dcd pattern + * of the channel. + * + * "Sample the dcd in intervals of half of a frames length and + * - increment persistence value if dcd is inactive, + * - decrement persistence value if dcd is active." + * + * simulations show that this scheme gives good collision + * avoidance and throughput without knowledge about the + * dcd propagation delay and station count. It will probably + * perform *much* too aggressive in a hidden station environment. + * + * Note: The check for hw.fast skips the channel arbitration + * stuff. Set this for KISS and ethernet devices. + */ + if (!this->hw.fast && !this->hw.duplex && !this->hw.ptt(this->netdev)) { + /* decide whether this is a "good" slot or not */ + if (random_num() < this->hw.persistence) { + /* ok, a good one, check the dcd now */ + if (this->hw.dcd(this->netdev)) { + this->hw.dcd_memory = 1; + /* + * too bad, dcd is up. we're too aggressive, + * but we must wait for a falling edge of the dcd + * before we can decrement persistence + */ + if (this->hw.dcd_dropped && this->hw.persistence > 1) + --this->hw.persistence; + if (this->needs_transmit) + ax25_dev_set_timer(this, this->hw.jiffies_per_slot); + return; + } + /* update dcd memory */ + this->hw.dcd_memory = 0; + this->hw.dcd_dropped = 0; + /* start transmitting */ + if (this->hw.rts) + this->hw.rts(this->netdev); + } else { + /* a bad slot, check the dcd */ + if (!this->hw.dcd(this->netdev)) { + /* um. dcd is down, we should have tx'd here. */ + if (this->hw.persistence < 128) + ++this->hw.persistence; + /* was it up the slot before? */ + if (this->hw.dcd_memory) { + this->hw.dcd_dropped = 1; + } + this->hw.dcd_memory = 0; + } else { + this->hw.dcd_memory = 1; + } + if (this->needs_transmit) + ax25_dev_set_timer(this, this->hw.jiffies_per_slot); + return; + } + } + + /* + * compute the amount of bytes to send during 100ms (AX25_TICS) + */ + max_bytes = ((this->hw.bit_per_jiffie * AX25_TICS) / 8); + + /* + * send a frame off the unproto queue + * FIXME: this is wrong here. the channel is not charged for unprotos + * but it has to, to prevent excess of the transmit time limit. + */ + while ((skb = skb_dequeue(&this->unproto_queue)) != NULL) + ax25_queue_xmit(skb); + + /* + * traverse our list of connections. we're messing with a + * private list here and we will not sleep and schedule, so no + * further protection should be necessary. + * + * we implement a simple round robin style packet scheduler here. + * each device has a list of cnnections ready to transmit packets, + * and we loop through the connections until + * a. the list becomes empty + * b. the transmit time limit is reached. + * if a connection has no more packets left or exceeds its window + * of outbound packets, it is removed from the list. + */ + while ((active = this->list.ready) != NULL && bytes_sent < max_bytes) { + unsigned short start; + unsigned short end; + struct sk_buff *skbn; + ax25_cb *peer; + int in_retransmit; + + in_retransmit = 0; + skbn = skb_peek(&active->ack_queue); + + /* transmit supervisory stuff first */ + if (active->tx_rsp) { + int poll_bit = active->tx_rsp & 0x100; + int frametype = active->tx_rsp & 0x0ff; + + active->tx_rsp = 0; + + ax25_send_control(active, frametype, poll_bit, AX25_RESPONSE); + + /* + * supervisory stuff is all done, clear state-change flag + */ + ax25_clr_cond(active, AX25_COND_STATE_CHANGE); + + if ((frametype & AX25_U) == AX25_S) { /* S frames carry NR */ + active->ack_timer = 0; + ax25_clr_cond(active, AX25_COND_ACK_PENDING); + } + } + if (active->tx_cmd) { + int poll_bit = active->tx_cmd & 0x100; + int frametype = active->tx_cmd & 0x0ff; + + active->tx_cmd = 0; + + /* + * a problem exists due to a race condition between linux' + * packet-scheduler and the timer routine: a write timeout might + * happen before the packet actually reaches the device and is copied + * for transmission. our transmit routine will then grab the first + * packet off the ack queue, put a header in front of the data and + * queue it for transmission. now we have the obscure situation that + * we have two packets in our transmit queue that share a single data + * segment. this isn't bad by itself, but since the first + * retransmitted frame will have the poll bit set and eventually will + * carry an updated N(r), we modify the header of a yet unsent packet, + * resulting in a protocol violation. + * + * we do the obvious thing to prevent this here: if the packet we + * got from the ack queue is cloned, we make a private copy of the + * data. + */ + if (poll_bit && skbn + && frametype == AX25_RR + && !(active->condition & (AX25_COND_PEER_RX_BUSY|AX25_COND_STATE_CHANGE)) + && active->n2count < 4) + { + if (skb_cloned(skbn)) { + skb = skb_copy(skbn, GFP_ATOMIC); + } else + skb = skb_clone(skbn, GFP_ATOMIC); + if (skb) { + active->vs = active->va; + ax25_send_iframe(active, skb, AX25_POLLON); + active->vs = active->vs_max; + } + } else + ax25_send_control(active, frametype, poll_bit, AX25_COMMAND); + + /* + * supervisory stuff is all done, clear state-change flag + */ + ax25_clr_cond(active, AX25_COND_STATE_CHANGE); + if ((frametype & AX25_U) == AX25_S) { /* S frames carry NR */ + active->ack_timer = 0; + ax25_clr_cond(active, AX25_COND_ACK_PENDING); + } + } + + /* + * if the write queue and ack queue are both empty, + * or connection is not in info transfer state + * or the peer station is busy + * or the window is closed + * or the write queue is empty and we may not retransmit yet + * then remove connection from the devices' readylist; + * + * NOTE: ax25_dev_remove_active implicitly advances the + * round robin pointer to schedule the next connection + * on the readylist. + */ + skb = skb_peek(&active->write_queue); + if ((skb == NULL && skbn == NULL) + || active->state != AX25_STATE_3 + || (active->condition & AX25_COND_PEER_RX_BUSY) != 0 + || (start = active->vs) == (end = (active->va + active->window) & active->seqmask) + || (skb == NULL && start != active->va)) + { + if (active->condition & AX25_COND_START_T1) { + ax25_clr_cond(active, AX25_COND_START_T1); + active->wrt_timer = active->t1 = ax25_calculate_t1(active); + } + ax25_dev_remove_active(this); + continue; + } + + /* + * handle RTS/CTS handshaking. drivers can request TX-Delay + * by returning 0 in the cts method. Note, that the driver still + * has to handle handshaking itself, but it can prevent to be + * flooded with frames while it's not ready to send. + */ + if (this->needs_transmit < AX25_TX_STATE_CTS) { + if (this->hw.cts == NULL || this->hw.cts(this->netdev)) + this->needs_transmit = AX25_TX_STATE_CTS; + else if (this->needs_transmit == AX25_TX_STATE_RTS) + this->needs_transmit = AX25_TX_STATE_WAIT_CTS; + else + break; + } + + if (skbn != NULL && start == active->va) { + skb = skbn; + in_retransmit = 1; + } + + /* + * clone the buffer, put the original into the + * ack_queue and transmit the copy. That way the + * socket will be uncharged from the memory when + * the packet is acked, not when it's transmitted. + */ + if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) + break; + + /* advance pointer to current connection */ + this->list.ready = active->ready.next; + + ax25_send_iframe(active, skbn, AX25_POLLOFF); + ax25_start_t1(active); + + /* implicit ACK */ + ax25_clr_cond(active, AX25_COND_ACK_PENDING); + + if (!in_retransmit) { + active->vs_max = active->vs = (active->vs + 1) & active->seqmask; + skb_dequeue(&active->write_queue); + skb_queue_tail(&active->ack_queue, skb); + + if (active->vs_rtt == -1) { + active->rtt_timestamp = jiffies; + active->vs_rtt = active->vs; + } + + this->tx_iframes++; + this->tx_bytes += skbn->len; + } else { + active->vs = active->vs_max; + if (active->condition & AX25_COND_START_T1) { + ax25_clr_cond(active, AX25_COND_START_T1); + active->wrt_timer = active->t1 = ax25_calculate_t1(active); + } + ax25_dev_remove_ready(this, active); + } + + bytes_sent += skbn->len; + + peer = active->peer; + if (peer && (peer->condition & AX25_COND_OWN_RX_BUSY) + && skb_queue_len(&active->write_queue) < 5) + { + ax25_clr_cond(peer, AX25_COND_OWN_RX_BUSY); + ax25_set_cond(peer, AX25_COND_STATE_CHANGE); + peer->state = AX25_STATE_4; + ax25_transmit_enquiry(peer); + } + } + + this->bytes_sent += bytes_sent; + + if (this->list.ready == NULL) { + this->bytes_sent = 0; + this->needs_transmit = AX25_TX_STATE_IDLE; + } else { + if (this->bytes_sent > this->max_bytes) { + this->bytes_sent = 0; + ax25_dev_set_timer(this, HZ/2); + } else + ax25_dev_set_timer(this, AX25_TICS); + } +} + +/* + * send a control frame + */ +static void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type) +{ + struct sk_buff *skb; + unsigned char *dptr; + struct device *dev; + + if ((dev = ax25->device) == NULL) + return; /* Route died */ + + if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_sizeof_addr(&ax25->addr) + 2, GFP_ATOMIC)) == NULL) + return; + + skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_sizeof_addr(&ax25->addr)); + + /* Assume a response - address structure for DTE */ + if (ax25->seqmask == AX25_SEQMASK) { + dptr = skb_put(skb, 1); + *dptr = frametype; + *dptr |= (poll_bit) ? AX25_PF : 0; + if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */ + *dptr |= (ax25->vr << 5); + } else { + if ((frametype & AX25_U) == AX25_U) { + dptr = skb_put(skb, 1); + *dptr = frametype; + *dptr |= (poll_bit) ? AX25_PF : 0; + } else { + dptr = skb_put(skb, 2); + dptr[0] = frametype; + dptr[1] = (ax25->vr << 1); + dptr[1] |= (poll_bit) ? AX25_EPF : 0; + } + } + + skb->nh.raw = skb->data; + ax25_transmit_buffer(ax25, skb, type); +} + +static void ax25_kick_device(struct ax25_dev* ax25_device) +{ + start_bh_atomic(); + if (!ax25_device->needs_transmit) { + ax25_device->needs_transmit = AX25_TX_STATE_RTS; + ax25_device->task_queue.routine = ax25_dev_timer; + ax25_device->task_queue.data = (void *)ax25_device; + ax25_device->task_queue.sync = 0; + queue_task(&ax25_device->task_queue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + end_bh_atomic(); +} + +/* + * This procedure is passed a buffer descriptor for an iframe. It builds + * the rest of the control part of the frame and then writes it out. + * + */ +static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit) +{ + unsigned char *frame; + + skb->nh.raw = skb->data; + + if (ax25->seqmask == AX25_SEQMASK) { + frame = skb_push(skb, 1); + + *frame = AX25_I; + *frame |= (poll_bit) ? AX25_PF : 0; + *frame |= (ax25->vr << 5); + *frame |= (ax25->vs << 1); + } else { + frame = skb_push(skb, 2); + + frame[0] = AX25_I; + frame[0] |= (ax25->vs << 1); + frame[1] = (poll_bit) ? AX25_EPF : 0; + frame[1] |= (ax25->vr << 1); + } + + ax25->idletimer = ax25->idle; + ax25_transmit_buffer(ax25, skb, AX25_COMMAND); +} + +static void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) +{ + unsigned char *ptr; + + if (ax25->device == NULL) + return; + + if (skb_headroom(skb) < ax25_sizeof_addr(&ax25->addr)) { + printk(KERN_WARNING "ax25_transmit_buffer: not enough room for digi-peaters\n"); + kfree_skb(skb); + return; + } + + ptr = skb_push(skb, ax25_sizeof_addr(&ax25->addr)); + ax25_build_addr(ptr, &ax25->addr, type, ax25->seqmask); + skb->dev = ax25->device; + ax25_queue_xmit(skb); +} + +/* ---------------------------------------------------------------------*/ +/* + * A small shim to dev_queue_xmit to add the KISS control byte, and do + * any packet forwarding in operation. + */ +static void ax25_queue_xmit(struct sk_buff *skb) +{ + + if (call_out_firewall(PF_AX25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) { + kfree_skb(skb); + return; + } + + skb->protocol = htons(ETH_P_AX25); + skb->dev = ax25_fwd_dev(skb->dev); + + dev_queue_xmit(skb); +} + +/* ---------------------------------------------------------------------*/ + +static struct ax25_dev *ax25_dev_get_dev(struct device *dev) +{ + struct ax25_dev *ax25_device = AX25_PTR(dev); + + if (ax25_device == NULL) + return NULL; + + if (ax25_device->magic == AX25_DEV_MAGIC) + return ax25_device; + + return NULL; +} Index: linux/net/ax25/ax25_ddi.h diff -u /dev/null linux/net/ax25/ax25_ddi.h:1.1.4.1 --- /dev/null Wed Jul 7 01:02:32 1999 +++ linux/net/ax25/ax25_ddi.h Sun Jul 4 13:03:45 1999 @@ -0,0 +1,38 @@ +/* + * Interface declaration for the DDI layer. + * + * Matthias Welwarsky (DG2FEF) 05/25/98 + * + */ + + +#ifndef _AX25_DDI_H +#define _AX25_DDI_H + +enum { + AX25_TX_STATE_IDLE = 0, + AX25_TX_STATE_RTS, + AX25_TX_STATE_WAIT_CTS, + AX25_TX_STATE_CTS +}; + +extern void ax25_ddi_init(void); +extern ax25_cb* ax25_dev_list(struct device *); +extern void ax25_dev_insert_cb(ax25_cb *); +extern void ax25_dev_remove_cb(ax25_cb *); +extern ax25_cb* ax25_dev_find_listener(ax25_address *, int, struct device *); +extern struct sock* ax25_dev_find_socket(ax25_address *, ax25_address *, struct device *, int); +extern int ax25_dev_match_addr(ax25_address *, struct device *); +extern void ax25_kick(ax25_cb *); +extern void ax25_send_unproto(struct sk_buff*, struct device*); +extern void ax25_send_broadcast(struct sk_buff*); +extern int ax25_dev_get_value(struct device *, int); +extern void ax25_dev_device_up(struct device *); +extern void ax25_dev_device_down(struct device *); +extern int ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *); +extern struct device* ax25_fwd_dev(struct device *); +extern int ax25_dev_get_info(char*, char**, off_t, int, int); + +extern struct device* ax25_devices[]; +#endif + Index: linux/net/ax25/ax25_dev.c diff -u linux/net/ax25/ax25_dev.c:1.1.2.1 linux/net/ax25/ax25_dev.c:removed --- linux/net/ax25/ax25_dev.c:1.1.2.1 Sun Jul 4 10:18:09 1999 +++ linux/net/ax25/ax25_dev.c Wed Jul 7 01:02:32 1999 @@ -1,231 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Other kernels modules in this kit are generally BSD derived. See the copyright headers. - * - * - * History - * AX.25 036 Jonathan(G4KLX) Split from ax25_route.c. - */ - -#include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ax25_dev *ax25_dev_list = NULL; - -ax25_dev *ax25_dev_ax25dev(struct device *dev) -{ - ax25_dev *ax25_dev; - - for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) - if (ax25_dev->dev == dev) - return ax25_dev; - - return NULL; -} - -ax25_dev *ax25_addr_ax25dev(ax25_address *addr) -{ - ax25_dev *ax25_dev; - - for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) - if (ax25cmp(addr, (ax25_address *)ax25_dev->dev->dev_addr) == 0) - return ax25_dev; - - return NULL; -} - -/* - * This is called when an interface is brought up. These are - * reasonable defaults. - */ -void ax25_dev_device_up(struct device *dev) -{ - ax25_dev *ax25_dev; - unsigned long flags; - - if ((ax25_dev = kmalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) { - printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n"); - return; - } - -#ifdef CONFIG_SYSCTL - ax25_unregister_sysctl(); -#endif - - memset(ax25_dev, 0x00, sizeof(*ax25_dev)); - - ax25_dev->dev = dev; - ax25_dev->forward = NULL; - - ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE; - ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE; - ax25_dev->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF; - ax25_dev->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE; - ax25_dev->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW; - ax25_dev->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW; - ax25_dev->values[AX25_VALUES_T1] = AX25_DEF_T1; - ax25_dev->values[AX25_VALUES_T2] = AX25_DEF_T2; - ax25_dev->values[AX25_VALUES_T3] = AX25_DEF_T3; - ax25_dev->values[AX25_VALUES_IDLE] = AX25_DEF_IDLE; - ax25_dev->values[AX25_VALUES_N2] = AX25_DEF_N2; - ax25_dev->values[AX25_VALUES_PACLEN] = AX25_DEF_PACLEN; - ax25_dev->values[AX25_VALUES_PROTOCOL] = AX25_DEF_PROTOCOL; - ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT; - - save_flags(flags); cli(); - ax25_dev->next = ax25_dev_list; - ax25_dev_list = ax25_dev; - restore_flags(flags); - -#ifdef CONFIG_SYSCTL - ax25_register_sysctl(); -#endif -} - -void ax25_dev_device_down(struct device *dev) -{ - ax25_dev *s, *ax25_dev; - unsigned long flags; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return; - -#ifdef CONFIG_SYSCTL - ax25_unregister_sysctl(); -#endif - - save_flags(flags); cli(); - -#ifdef CONFIG_AX25_DAMA_SLAVE - ax25_ds_del_timer(ax25_dev); -#endif - - /* - * Remove any packet forwarding that points to this device. - */ - for (s = ax25_dev_list; s != NULL; s = s->next) - if (s->forward == dev) - s->forward = NULL; - - if ((s = ax25_dev_list) == ax25_dev) { - ax25_dev_list = s->next; - restore_flags(flags); - kfree(ax25_dev); -#ifdef CONFIG_SYSCTL - ax25_register_sysctl(); -#endif - return; - } - - while (s != NULL && s->next != NULL) { - if (s->next == ax25_dev) { - s->next = ax25_dev->next; - restore_flags(flags); - kfree(ax25_dev); -#ifdef CONFIG_SYSCTL - ax25_register_sysctl(); -#endif - return; - } - - s = s->next; - } - - restore_flags(flags); -#ifdef CONFIG_SYSCTL - ax25_register_sysctl(); -#endif -} - -int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd) -{ - ax25_dev *ax25_dev, *fwd_dev; - - if ((ax25_dev = ax25_addr_ax25dev(&fwd->port_from)) == NULL) - return -EINVAL; - - switch (cmd) { - case SIOCAX25ADDFWD: - if ((fwd_dev = ax25_addr_ax25dev(&fwd->port_to)) == NULL) - return -EINVAL; - if (ax25_dev->forward != NULL) - return -EINVAL; - ax25_dev->forward = fwd_dev->dev; - break; - - case SIOCAX25DELFWD: - if (ax25_dev->forward == NULL) - return -EINVAL; - ax25_dev->forward = NULL; - break; - - default: - return -EINVAL; - } - - return 0; -} - -struct device *ax25_fwd_dev(struct device *dev) -{ - ax25_dev *ax25_dev; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return dev; - - if (ax25_dev->forward == NULL) - return dev; - - return ax25_dev->forward; -} - -#ifdef MODULE - -/* - * Free all memory associated with device structures. - */ -void ax25_dev_free(void) -{ - ax25_dev *s, *ax25_dev = ax25_dev_list; - - while (ax25_dev != NULL) { - s = ax25_dev; - ax25_dev = ax25_dev->next; - - kfree(s); - } -} - -#endif - -#endif Index: linux/net/ax25/ax25_ds_in.c diff -u linux/net/ax25/ax25_ds_in.c:1.1.2.1 linux/net/ax25/ax25_ds_in.c:removed --- linux/net/ax25/ax25_ds_in.c:1.1.2.1 Sun Jul 4 10:18:09 1999 +++ linux/net/ax25/ax25_ds_in.c Wed Jul 7 01:02:32 1999 @@ -1,317 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; - * - * History - * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c - * Joerg(DL1BKE) Fixed it. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - * Joerg(DL1BKE) ax25->n2count never got reset - */ - -#include -#if defined(CONFIG_AX25_DAMA_SLAVE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For ip_rcv */ -#include -#include -#include -#include -#include - -/* - * State machine for state 1, Awaiting Connection State. - * The handling of the timer(s) is in file ax25_ds_timer.c. - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_SABME: - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); - break; - - case AX25_UA: - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25->state = AX25_STATE_3; - ax25->n2count = 0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_ESTABLISHED; - /* For WAIT_SABM connections we will produce an accept ready socket here */ - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - } - ax25_dama_on(ax25); - - /* according to DK4EG´s spec we are required to - * send a RR RESPONSE FINAL NR=0. Please mail - * if this causes problems - * with the TheNetNode DAMA Master implementation. - */ - - ax25_std_enquiry_response(ax25); - break; - - case AX25_DM: - if (pf) ax25_disconnect(ax25, ECONNREFUSED); - break; - - default: - if (pf) ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - break; - } - - return 0; -} - -/* - * State machine for state 2, Awaiting Release State. - * The handling of the timer(s) is in file ax25_ds_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_dama_off(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_dama_off(ax25); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - case AX25_UA: - if (pf) { - ax25_dama_off(ax25); - ax25_disconnect(ax25, 0); - } - break; - - case AX25_I: - case AX25_REJ: - case AX25_RNR: - case AX25_RR: - if (pf) { - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_dama_off(ax25); - } - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 3, Connected State. - * The handling of the timer(s) is in file ax25_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) -{ - int queued = 0; - - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - if (frametype == AX25_SABM) { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } else { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - } - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->condition = 0x00; - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25_requeue_frames(ax25); - ax25_dama_on(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_dama_off(ax25); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - ax25_dama_off(ax25); - ax25_disconnect(ax25, ECONNRESET); - break; - - case AX25_RR: - case AX25_RNR: - if (frametype == AX25_RR) - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - else - ax25->condition |= AX25_COND_PEER_RX_BUSY; - - if (ax25_validate_nr(ax25, nr)) { - if (ax25_check_iframes_acked(ax25, nr)) - ax25->n2count=0; - if (type == AX25_COMMAND && pf) - ax25_ds_enquiry_response(ax25); - } else { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - - if (ax25_validate_nr(ax25, nr)) { - if (ax25->va != nr) - ax25->n2count=0; - - ax25_frames_acked(ax25, nr); - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_requeue_frames(ax25); - - if (type == AX25_COMMAND && pf) - ax25_ds_enquiry_response(ax25); - } else { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_I: - if (!ax25_validate_nr(ax25, nr)) { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - break; - } - if (ax25->condition & AX25_COND_PEER_RX_BUSY) { - ax25_frames_acked(ax25, nr); - ax25->n2count = 0; - } else { - if (ax25_check_iframes_acked(ax25, nr)) - ax25->n2count = 0; - } - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { - if (pf) ax25_ds_enquiry_response(ax25); - break; - } - if (ns == ax25->vr) { - ax25->vr = (ax25->vr + 1) % ax25->modulus; - queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25->vr = ns; /* ax25->vr - 1 */ - ax25->condition &= ~AX25_COND_REJECT; - if (pf) { - ax25_ds_enquiry_response(ax25); - } else { - if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->condition |= AX25_COND_ACK_PENDING; - ax25_start_t2timer(ax25); - } - } - } else { - if (ax25->condition & AX25_COND_REJECT) { - if (pf) ax25_ds_enquiry_response(ax25); - } else { - ax25->condition |= AX25_COND_REJECT; - ax25_ds_enquiry_response(ax25); - ax25->condition &= ~AX25_COND_ACK_PENDING; - } - } - break; - - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_ds_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - - default: - break; - } - - return queued; -} - -/* - * Higher level upcall for a LAPB frame - */ -int ax25_ds_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) -{ - int queued = 0, frametype, ns, nr, pf; - - frametype = ax25_decode(ax25, skb, &ns, &nr, &pf); - - switch (ax25->state) { - case AX25_STATE_1: - queued = ax25_ds_state1_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_2: - queued = ax25_ds_state2_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_3: - queued = ax25_ds_state3_machine(ax25, skb, frametype, ns, nr, pf, type); - break; - } - - return queued; -} - -#endif Index: linux/net/ax25/ax25_ds_subr.c diff -u linux/net/ax25/ax25_ds_subr.c:1.1.2.1 linux/net/ax25/ax25_ds_subr.c:removed --- linux/net/ax25/ax25_ds_subr.c:1.1.2.1 Sun Jul 4 10:18:09 1999 +++ linux/net/ax25/ax25_ds_subr.c Wed Jul 7 01:02:32 1999 @@ -1,219 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; - * - * History - * AX.25 036 Jonathan(G4KLX) Cloned from ax25_out.c and ax25_subr.c. - * Joerg(DL1BKE) Changed ax25_ds_enquiry_response(), - * fixed ax25_dama_on() and ax25_dama_off(). - * AX.25 037 Jonathan(G4KLX) New timer architecture. - */ - -#include -#if defined(CONFIG_AX25_DAMA_SLAVE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void ax25_ds_nr_error_recovery(ax25_cb *ax25) -{ - ax25_ds_establish_data_link(ax25); -} - -/* - * dl1bke 960114: transmit I frames on DAMA poll - */ -void ax25_ds_enquiry_response(ax25_cb *ax25) -{ - ax25_cb *ax25o; - - /* Please note that neither DK4EG´s nor DG2FEF´s - * DAMA spec mention the following behaviour as seen - * with TheFirmware: - * - * DB0ACH->DL1BKE [DAMA] - * DL1BKE->DB0ACH - * DL1BKE-7->DB0PRA-6 DB0ACH - * DL1BKE->DB0ACH - * - * The Flexnet DAMA Master implementation apparently - * insists on the "proper" AX.25 behaviour: - * - * DB0ACH->DL1BKE [DAMA] - * DL1BKE->DB0ACH - * DL1BKE->DB0ACH - * DL1BKE-7->DB0PRA-6 DB0ACH - * - * Flexnet refuses to send us *any* I frame if we send - * a REJ in case AX25_COND_REJECT is set. It is superfluous in - * this mode anyway (a RR or RNR invokes the retransmission). - * Is this a Flexnet bug? - */ - - ax25_std_enquiry_response(ax25); - - if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) { - ax25_requeue_frames(ax25); - ax25_kick(ax25); - } - - if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL) - ax25_ds_t1_timeout(ax25); - else - ax25->n2count = 0; - - ax25_start_t3timer(ax25); - ax25_ds_set_timer(ax25->ax25_dev); - - for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) { - if (ax25o == ax25) - continue; - - if (ax25o->ax25_dev != ax25->ax25_dev) - continue; - - if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) { - ax25_ds_t1_timeout(ax25o); - continue; - } - - if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && ax25o->state == AX25_STATE_3) { - ax25_requeue_frames(ax25o); - ax25_kick(ax25o); - } - - if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL) - ax25_ds_t1_timeout(ax25o); - - /* do not start T3 for listening sockets (tnx DD8NE) */ - - if (ax25o->state != AX25_STATE_0) - ax25_start_t3timer(ax25o); - } -} - -void ax25_ds_establish_data_link(ax25_cb *ax25) -{ - ax25->condition &= AX25_COND_DAMA_MODE; - ax25->n2count = 0; - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t3timer(ax25); -} - -/* - * :::FIXME::: - * This is a kludge. Not all drivers recognize kiss commands. - * We need a driver level request to switch duplex mode, that does - * either SCC changing, PI config or KISS as required. Currently - * this request isn't reliable. - */ -static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char param) -{ - struct sk_buff *skb; - unsigned char *p; - - if (ax25_dev->dev == NULL) - return; - - if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL) - return; - - p = skb_put(skb, 2); - - *p++ = cmd; - *p++ = param; - - skb->dev = ax25_dev->dev; - skb->protocol = htons(ETH_P_AX25); - - dev_queue_xmit(skb); -} - -/* - * A nasty problem arises if we count the number of DAMA connections - * wrong, especially when connections on the device already existed - * and our network node (or the sysop) decides to turn on DAMA Master - * mode. We thus flag the 'real' slave connections with - * ax25->dama_slave=1 and look on every disconnect if still slave - * connections exist. - */ -static int ax25_check_dama_slave(ax25_dev *ax25_dev) -{ - ax25_cb *ax25; - - for (ax25 = ax25_list; ax25 != NULL ; ax25 = ax25->next) - if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1) - return 1; - - return 0; -} - -void ax25_dev_dama_on(ax25_dev *ax25_dev) -{ - if (ax25_dev == NULL) - return; - - if (ax25_dev->dama.slave == 0) - ax25_kiss_cmd(ax25_dev, 5, 1); - - ax25_dev->dama.slave = 1; - ax25_ds_set_timer(ax25_dev); -} - -void ax25_dev_dama_off(ax25_dev *ax25_dev) -{ - if (ax25_dev == NULL) - return; - - if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev)) { - ax25_kiss_cmd(ax25_dev, 5, 0); - ax25_dev->dama.slave = 0; - ax25_ds_del_timer(ax25_dev); - } -} - -void ax25_dama_on(ax25_cb *ax25) -{ - ax25_dev_dama_on(ax25->ax25_dev); - ax25->condition |= AX25_COND_DAMA_MODE; -} - -void ax25_dama_off(ax25_cb *ax25) -{ - ax25->condition &= ~AX25_COND_DAMA_MODE; - ax25_dev_dama_off(ax25->ax25_dev); -} - -#endif Index: linux/net/ax25/ax25_ds_timer.c diff -u linux/net/ax25/ax25_ds_timer.c:1.1.2.1 linux/net/ax25/ax25_ds_timer.c:removed --- linux/net/ax25/ax25_ds_timer.c:1.1.2.1 Sun Jul 4 10:18:09 1999 +++ linux/net/ax25/ax25_ds_timer.c Wed Jul 7 01:02:32 1999 @@ -1,228 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * History - * AX.25 036 Jonathan(G4KLX) Cloned from ax25_timer.c. - * Joerg(DL1BKE) Added DAMA Slave Timeout timer - * AX.25 037 Jonathan(G4KLX) New timer architecture. - */ - -#include -#if defined(CONFIG_AX25_DAMA_SLAVE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void ax25_ds_timeout(unsigned long); - -/* - * Add DAMA slave timeout timer to timer list. - * Unlike the connection based timers the timeout function gets - * triggered every second. Please note that NET_AX25_DAMA_SLAVE_TIMEOUT - * (aka /proc/sys/net/ax25/{dev}/dama_slave_timeout) is still in - * 1/10th of a second. - */ - -static void ax25_ds_add_timer(ax25_dev *ax25_dev) -{ - struct timer_list *t = &ax25_dev->dama.slave_timer; - t->data = (unsigned long) ax25_dev; - t->function = &ax25_ds_timeout; - t->expires = jiffies + HZ; - add_timer(t); -} - -void ax25_ds_del_timer(ax25_dev *ax25_dev) -{ - if (ax25_dev) del_timer(&ax25_dev->dama.slave_timer); -} - -void ax25_ds_set_timer(ax25_dev *ax25_dev) -{ - if (ax25_dev == NULL) /* paranoia */ - return; - - del_timer(&ax25_dev->dama.slave_timer); - ax25_dev->dama.slave_timeout = ax25_dev->values[AX25_VALUES_DS_TIMEOUT] / 10; - ax25_ds_add_timer(ax25_dev); -} - -/* - * DAMA Slave Timeout - * Silently discard all (slave) connections in case our master forgot us... - */ - -static void ax25_ds_timeout(unsigned long arg) -{ - ax25_dev *ax25_dev = (struct ax25_dev *) arg; - ax25_cb *ax25; - - if (ax25_dev == NULL || !ax25_dev->dama.slave) - return; /* Yikes! */ - - if (!ax25_dev->dama.slave_timeout || --ax25_dev->dama.slave_timeout) { - ax25_ds_set_timer(ax25_dev); - return; - } - - for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next) { - if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE)) - continue; - - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_disconnect(ax25, ETIMEDOUT); - } - - ax25_dev_dama_off(ax25_dev); -} - -void ax25_ds_heartbeat_expiry(ax25_cb *ax25) -{ - switch (ax25->state) { - - case AX25_STATE_0: - /* Magic here: If we listen() and a new link dies before it - is accepted() it isn't 'dead' so doesn't get removed. */ - if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) { - ax25_destroy_socket(ax25); - return; - } - break; - - case AX25_STATE_3: - /* - * Check the state of the receive buffer. - */ - if (ax25->sk != NULL) { - if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) && - (ax25->condition & AX25_COND_OWN_RX_BUSY)) { - ax25->condition &= ~AX25_COND_OWN_RX_BUSY; - ax25->condition &= ~AX25_COND_ACK_PENDING; - break; - } - } - break; - } - - ax25_start_heartbeat(ax25); -} - -/* dl1bke 960114: T3 works much like the IDLE timeout, but - * gets reloaded with every frame for this - * connection. - */ -void ax25_ds_t3timer_expiry(ax25_cb *ax25) -{ - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_dama_off(ax25); - ax25_disconnect(ax25, ETIMEDOUT); -} - -/* dl1bke 960228: close the connection when IDLE expires. - * unlike T3 this timer gets reloaded only on - * I frames. - */ -void ax25_ds_idletimer_expiry(ax25_cb *ax25) -{ - ax25_clear_queues(ax25); - - ax25->n2count = 0; - ax25->state = AX25_STATE_2; - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25_stop_t3timer(ax25); - - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } -} - -/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC - * within the poll of any connected channel. Remember - * that we are not allowed to send anything unless we - * get polled by the Master. - * - * Thus we'll have to do parts of our T1 handling in - * ax25_enquiry_response(). - */ -void ax25_ds_t1_timeout(ax25_cb *ax25) -{ - switch (ax25->state) { - - case AX25_STATE_1: - if (ax25->n2count == ax25->n2) { - if (ax25->modulus == AX25_MODULUS) { - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25->n2count = 0; - ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND); - } - } else { - ax25->n2count++; - if (ax25->modulus == AX25_MODULUS) - ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_SABME, AX25_POLLOFF, AX25_COMMAND); - } - break; - - case AX25_STATE_2: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - } - break; - - case AX25_STATE_3: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - } - break; - } - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); -} - -#endif Index: linux/net/ax25/ax25_iface.c diff -u linux/net/ax25/ax25_iface.c:1.1.2.1 linux/net/ax25/ax25_iface.c:removed --- linux/net/ax25/ax25_iface.c:1.1.2.1 Sun Jul 4 10:18:10 1999 +++ linux/net/ax25/ax25_iface.c Wed Jul 7 01:02:32 1999 @@ -1,270 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * History - * AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c. - */ - -#include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct protocol_struct { - struct protocol_struct *next; - unsigned int pid; - int (*func)(struct sk_buff *, ax25_cb *); -} *protocol_list = NULL; - -static struct linkfail_struct { - struct linkfail_struct *next; - void (*func)(ax25_cb *, int); -} *linkfail_list = NULL; - -static struct listen_struct { - struct listen_struct *next; - ax25_address callsign; - struct device *dev; -} *listen_list = NULL; - -int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *)) -{ - struct protocol_struct *protocol; - unsigned long flags; - - if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT) - return 0; -#ifdef CONFIG_INET - if (pid == AX25_P_IP || pid == AX25_P_ARP) - return 0; -#endif - if ((protocol = kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL) - return 0; - - protocol->pid = pid; - protocol->func = func; - - save_flags(flags); - cli(); - - protocol->next = protocol_list; - protocol_list = protocol; - - restore_flags(flags); - - return 1; -} - -void ax25_protocol_release(unsigned int pid) -{ - struct protocol_struct *s, *protocol = protocol_list; - unsigned long flags; - - if (protocol == NULL) - return; - - save_flags(flags); - cli(); - - if (protocol->pid == pid) { - protocol_list = protocol->next; - restore_flags(flags); - kfree(protocol); - return; - } - - while (protocol != NULL && protocol->next != NULL) { - if (protocol->next->pid == pid) { - s = protocol->next; - protocol->next = protocol->next->next; - restore_flags(flags); - kfree(s); - return; - } - - protocol = protocol->next; - } - - restore_flags(flags); -} - -int ax25_linkfail_register(void (*func)(ax25_cb *, int)) -{ - struct linkfail_struct *linkfail; - unsigned long flags; - - if ((linkfail = kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL) - return 0; - - linkfail->func = func; - - save_flags(flags); - cli(); - - linkfail->next = linkfail_list; - linkfail_list = linkfail; - - restore_flags(flags); - - return 1; -} - -void ax25_linkfail_release(void (*func)(ax25_cb *, int)) -{ - struct linkfail_struct *s, *linkfail = linkfail_list; - unsigned long flags; - - if (linkfail == NULL) - return; - - save_flags(flags); - cli(); - - if (linkfail->func == func) { - linkfail_list = linkfail->next; - restore_flags(flags); - kfree(linkfail); - return; - } - - while (linkfail != NULL && linkfail->next != NULL) { - if (linkfail->next->func == func) { - s = linkfail->next; - linkfail->next = linkfail->next->next; - restore_flags(flags); - kfree(s); - return; - } - - linkfail = linkfail->next; - } - - restore_flags(flags); -} - -int ax25_listen_register(ax25_address *callsign, struct device *dev) -{ - struct listen_struct *listen; - unsigned long flags; - - if (ax25_listen_mine(callsign, dev)) - return 0; - - if ((listen = kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL) - return 0; - - listen->callsign = *callsign; - listen->dev = dev; - - save_flags(flags); - cli(); - - listen->next = listen_list; - listen_list = listen; - - restore_flags(flags); - - return 1; -} - -void ax25_listen_release(ax25_address *callsign, struct device *dev) -{ - struct listen_struct *s, *listen = listen_list; - unsigned long flags; - - if (listen == NULL) - return; - - save_flags(flags); - cli(); - - if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) { - listen_list = listen->next; - restore_flags(flags); - kfree(listen); - return; - } - - while (listen != NULL && listen->next != NULL) { - if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) { - s = listen->next; - listen->next = listen->next->next; - restore_flags(flags); - kfree(s); - return; - } - - listen = listen->next; - } - - restore_flags(flags); -} - -int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *) -{ - struct protocol_struct *protocol; - - for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) - if (protocol->pid == pid) - return protocol->func; - - return NULL; -} - -int ax25_listen_mine(ax25_address *callsign, struct device *dev) -{ - struct listen_struct *listen; - - for (listen = listen_list; listen != NULL; listen = listen->next) - if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL)) - return 1; - - return 0; -} - -void ax25_link_failed(ax25_cb *ax25, int reason) -{ - struct linkfail_struct *linkfail; - - for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next) - (linkfail->func)(ax25, reason); -} - -int ax25_protocol_is_registered(unsigned int pid) -{ - struct protocol_struct *protocol; - - for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) - if (protocol->pid == pid) - return 1; - - return 0; -} - -#endif Index: linux/net/ax25/ax25_in.c diff -u linux/net/ax25/ax25_in.c:1.1.2.1 linux/net/ax25/ax25_in.c:1.1.2.1.2.1 --- linux/net/ax25/ax25_in.c:1.1.2.1 Sun Jul 4 10:18:10 1999 +++ linux/net/ax25/ax25_in.c Sun Jul 4 11:32:32 1999 @@ -1,7 +1,7 @@ /* - * AX.25 release 037 + * AX.25 release 035 * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * This code REQUIRES 1.2.1 or higher/ NET3.029 * * This module: * This module is free software; you can redistribute it and/or @@ -16,55 +16,348 @@ * easy to break; * * History - * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. - * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from - * the sock structure. - * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. - * Jonathan(G4KLX) Added IP mode registration. - * AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception. - * Upgraded state machine for SABME. - * Added arbitrary protocol id support. - * AX.25 031 Joerg(DL1BKE) Added DAMA support - * HaJo(DD8NE) Added Idle Disc Timer T5 - * Joerg(DL1BKE) Renamed it to "IDLE" with a slightly - * different behaviour. Fixed defrag - * routine (I hope) - * AX.25 032 Darryl(G7LED) AX.25 segmentation fixed. - * AX.25 033 Jonathan(G4KLX) Remove auto-router. - * Modularisation changes. - * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer. - * AX.25 036 Jonathan(G4KLX) Move DAMA code into own file. - * Joerg(DL1BKE) Fixed DAMA Slave. - * AX.25 037 Jonathan(G4KLX) New timer architecture. + * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. + * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from + * the sock structure. + * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. + * Jonathan(G4KLX) Added IP mode registration. + * AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception. + * Upgraded state machine for SABME. + * Added arbitrary protocol id support. + * AX.25 031 Joerg(DL1BKE) Added DAMA support + * HaJo(DD8NE) Added Idle Disc Timer T5 + * Joerg(DL1BKE) Renamed it to "IDLE" with a slightly + * different behaviour. Fixed defrag + * routine (I hope) + * AX.25 032 Darryl(G7LED) AX.25 segmentation fixed. + * AX.25 033 Jonathan(G4KLX) Remove auto-router. + * Modularisation changes. + * Joerg(DL1BKE) Fixed 2.0.x specific IP over AX.25 problem. + * Matthias(DG2FEF) Support for VJ TCP Compression over AX.25 */ #include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include #include #include -#include /* For ip_rcv */ -#include /* For arp_rcv */ -#include -#include -#include -#include -#include +#include +#include +#include +#include + +#include "af_ax25.h" +#include "ax25_route.h" +#include "ax25_ddi.h" +#include "ax25_in.h" +#include "ax25_lapb.h" +#include "ax25_core.h" +#include "ax25_subr.h" +#include "ax25_netlink.h" + +static int ax25_rx_fragment(ax25_cb*, struct sk_buff*); +static void ax25_decode(ax25_cb*, struct sk_buff*, ax25_pktinfo*); +static struct sock* ax25_find_socket(ax25_address*, ax25_address*, struct device*, int); +static int ax25_match_addr(ax25_address*, struct device*); +static void ax25_send_to_raw(struct sock*, struct sk_buff*, int); +static int ax25_rcv(struct sk_buff*, struct device*, struct packet_type*); + +/* + * Packet type registration data + */ +struct packet_type ax25_packet_type = +{ + 0, /* MUTTER ntohs(ETH_P_AX25),*/ + 0, /* copy */ + ax25_rcv, + NULL, + NULL, +}; /* + * Higher level upcall for a LAPB frame + */ +int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo* pkt_info) +{ + ax25_decode(ax25, skb, pkt_info); + return ax25_lapb_table[ax25->state](ax25, skb, pkt_info); +} + +/* + * Resequencer + */ +static inline unsigned int ax25_csum_skb(struct sk_buff *skb) +{ + unsigned char *cp = skb->data; + unsigned int csum = 0; + + while (cp < (skb->data + skb->len)) + csum += *cp++; + + return csum; +} + +void ax25_reseq_update(ax25_cb* ax25, struct sk_buff *skb, int ns) +{ + if (ax25->seqmask == AX25_ESEQMASK) + return; + if (ax25->reseq[ns].skb != NULL) + return; + + ax25->reseq[ns].csum = ax25_csum_skb(skb); +} + +int ax25_reseq_in(ax25_cb *ax25, struct sk_buff *skb, int ns, int pf) +{ + unsigned int csum; + + if (ax25->seqmask == AX25_ESEQMASK) + return 0; + + if (pf) + return 0; + + + /* + * if the slot is not empty, drop it + */ + if (ax25->reseq[ns].skb != NULL) { + printk(KERN_DEBUG "ax25_reseq_in: slot in use.\n"); + return 0; + } + + /* + * if we've already seen this one, drop it + */ + + if (ax25->reseq[ns].csum == (csum = ax25_csum_skb(skb))) { + printk(KERN_DEBUG "ax25_reseq_in: ns:%d seen before.\n", ns); + return 0; + } + + /* + * queue it + */ + ax25->reseq[ns].skb = skb; + ax25->reseq[ns].csum = csum; + + return 1; +} + +void ax25_reseq_out(ax25_cb *ax25) +{ + struct sk_buff *skb; + + while ((skb = ax25->reseq[ax25->vr].skb) != NULL && ax25_rx_iframe(ax25, skb)) { + ax25->reseq[ax25->vr].skb = NULL; + ax25->vr = (ax25->vr+1) & ax25->seqmask; + } +} + +/* + * This is where all valid I frames are sent to, to be dispatched to + * whichever protocol requires them. + */ +int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) +{ + ax25_cb *peer; + unsigned char pid = *skb->data; + struct sock *sk = NULL; + int queued = 0; + + int (*func)(struct sk_buff *, ax25_cb *); + + if (ax25->device) { + AX25_PTR(ax25->device)->rx_iframes++; + AX25_PTR(ax25->device)->rx_bytes += skb->len; + } + + ax25_clr_cond(ax25, AX25_COND_SETUP); + + /* + * don't take any more data if we're about to close + * the connection. + */ + if (ax25->condition & AX25_COND_RELEASE) { + ax25_set_cond(ax25, AX25_COND_OWN_RX_BUSY); + kfree_skb(skb); + return 1; + } + + /* + * we're only digipeating, don't care about pid + * and idle timeouts. + */ + if ((peer = ax25->peer) != NULL) { + int max_qlen; + + /* + * don't queue any more data if the peer is just about to + * be disconnected. + */ + if (peer->state < AX25_STATE_3 || peer->condition & AX25_COND_RELEASE) { + kfree_skb(skb); + return 1; + } + + skb_queue_tail(&peer->write_queue, skb); + + /* + * check the length of write_queue, set OWN_RX_BUSY if + * enough queued. + */ + max_qlen = peer->seqmask << 1; + if (max_qlen > 16) + max_qlen = 16; + if (skb_queue_len(&peer->write_queue) > max_qlen) { + ax25->condition |= AX25_COND_OWN_RX_BUSY; + } + ax25_kick(peer); + + return 1; + } + + ax25->idletimer = ax25->idle; + + if (pid == AX25_P_SEGMENT) { + skb_pull(skb, 1); + queued = ax25_rx_fragment(ax25, skb); + + } else if ((func = ax25_protocol_function(pid)) != NULL) { + skb_pull(skb, 1); /* Remove PID */ + queued = func(skb, ax25); + + } else if ((sk = ax25->sk) != NULL) { + struct sk_buff *oskb; + /* + * check if we have frames left in ax25->rcv_queue, these + * must be delivered first to maintain the sequence. + */ + while ((oskb = skb_peek(&ax25->rcv_queue)) != NULL) { + if (sk->shutdown & RCV_SHUTDOWN) + break; + if (atomic_read(&sk->rmem_alloc) + oskb->truesize < sk->rcvbuf) { + skb_dequeue(&ax25->rcv_queue); + sock_queue_rcv_skb(sk, oskb); + } else + break; + } + if (oskb == NULL) + ax25->condition &= ~AX25_COND_OWN_RX_BUSY; + + /* + * now handle the frame that has just come in. + */ + if (ax25->pidincl || sk->protocol == pid) { + if (sk->shutdown & RCV_SHUTDOWN) { + ax25->condition |= AX25_COND_OWN_RX_BUSY; + kfree_skb(skb); + } else if (sock_queue_rcv_skb(sk, skb) != 0) { + /* + * no space left on the socket, become busy + * but keep queueing up the data. + */ + ax25->condition |= AX25_COND_OWN_RX_BUSY; + + /* + * don't queue an infinite amount of + * data, 10 frames should be enough + * and we'll send an RNR anyway. If + * the peer keeps bombing us with + * valid iframes, "return 0" forces + * the l2 to drop the frame without + * ACK'ng it. This will produce heavy + * load on the channel due to constantly + * retransmitted frames, but we cannot + * just drop the packets, the application + * cannot handle the data loss induced. + */ + if (skb_queue_len(&ax25->rcv_queue) < ax25->seqmask) + skb_queue_tail(&ax25->rcv_queue, skb); + else + return 0; + } + queued = 1; + } + } + + /* + * this is a kludge to satisfy the IMHO broken interface + * between the L2 and upper protocol handlers. There was no + * handler for the PID and the socket didn't accept the PID + * either, so we free the buffer and pretend to have queued + * it. + */ + if (!queued) + kfree_skb(skb); + return 1; +} + +/* ---------------------------------------------------------------------*/ + +struct sock *ax25_make_new(struct sock *osk, struct device *dev) +{ + struct sock *sk; + ax25_cb *ax25; + + if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL) + return NULL; + + if ((ax25 = ax25_create_cb()) == NULL) { + sk_free(sk); + return NULL; + } + + switch (osk->type) { + case SOCK_DGRAM: + break; + case SOCK_SEQPACKET: + break; + default: + sk_free(sk); + ax25_free_cb(ax25); + return NULL; + } + + sock_init_data(NULL, sk); + + sk->destruct = ax25_free_sock; + sk->type = osk->type; + sk->socket = osk->socket; + sk->priority = osk->priority; + sk->protocol = osk->protocol; + sk->rcvbuf = osk->rcvbuf; + sk->sndbuf = osk->sndbuf; + sk->debug = osk->debug; + sk->state = TCP_ESTABLISHED; + sk->sleep = osk->sleep; + sk->zapped = osk->zapped; + + ax25->seqmask = osk->protinfo.ax25->seqmask; + ax25->backoff = osk->protinfo.ax25->backoff; + ax25->pidincl = osk->protinfo.ax25->pidincl; + ax25->iamdigi = osk->protinfo.ax25->iamdigi; + ax25->rtt = osk->protinfo.ax25->rtt; + ax25->t1 = osk->protinfo.ax25->t1; + ax25->t2 = osk->protinfo.ax25->t2; + ax25->t3 = osk->protinfo.ax25->t3; + ax25->n2 = osk->protinfo.ax25->n2; + ax25->idle = osk->protinfo.ax25->idle; + ax25->paclen = osk->protinfo.ax25->paclen; + ax25->window = osk->protinfo.ax25->window; + + ax25->device = dev; + ax25->addr = osk->protinfo.ax25->addr; + + sk->protinfo.ax25 = ax25; + ax25->sk = sk; + + return sk; +} + +/* * Given a fragment, queue it on the fragment queue and if the fragment * is complete, send it back to ax25_rx_iframe. */ @@ -83,16 +376,16 @@ /* Last fragment received ? */ if (ax25->fragno == 0) { - if ((skbn = alloc_skb(AX25_MAX_HEADER_LEN + ax25->fraglen, GFP_ATOMIC)) == NULL) { + if ((skbn = alloc_skb(MAX_HEADER + ax25->fraglen, GFP_ATOMIC)) == NULL) { while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) kfree_skb(skbo); return 1; } - skb_reserve(skbn, AX25_MAX_HEADER_LEN); + skb_reserve(skbn, MAX_HEADER); - skbn->dev = ax25->ax25_dev->dev; - skbn->h.raw = skbn->data; + skbn->dev = ax25->device; + skbn->nh.raw = skbn->data; /* Copy data from the fragments */ while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) { @@ -125,363 +418,410 @@ return 0; } + /* - * This is where all valid I frames are sent to, to be dispatched to - * whichever protocol requires them. + * This routine is the centralised routine for parsing the control + * information for the different frame formats. */ -int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) +static void ax25_decode(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt_info) { - int (*func)(struct sk_buff *, ax25_cb *); - volatile int queued = 0; - unsigned char pid; - - if (skb == NULL) return 0; - - ax25_start_idletimer(ax25); + unsigned char *frame; - pid = *skb->data; - -#ifdef CONFIG_INET - if (pid == AX25_P_IP) { - /* working around a TCP bug to keep additional listeners - * happy. TCP re-uses the buffer and destroys the original - * content. - */ - struct sk_buff *skbn = skb_copy(skb, GFP_ATOMIC); - if (skbn != NULL) { - kfree_skb(skb); - skb = skbn; + frame = skb->data; + pkt_info->frametype = AX25_ILLEGAL; + pkt_info->ns = pkt_info->nr = pkt_info->pf = 0; + + if (ax25->seqmask == AX25_SEQMASK) { + if ((frame[0] & AX25_S) == 0) { + pkt_info->frametype = AX25_I; /* I frame - carries NR/NS/PF */ + pkt_info->ns = (frame[0] >> 1) & 0x07; + pkt_info->nr = (frame[0] >> 5) & 0x07; + pkt_info->pf = frame[0] & AX25_PF; + } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ + pkt_info->frametype = frame[0] & 0x0F; + pkt_info->nr = (frame[0] >> 5) & 0x07; + pkt_info->pf = frame[0] & AX25_PF; + } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ + pkt_info->frametype = frame[0] & ~AX25_PF; + pkt_info->pf = frame[0] & AX25_PF; } - - skb_pull(skb, 1); /* Remove PID */ - skb->h.raw = skb->data; - skb->nh.raw = skb->data; - skb->dev = ax25->ax25_dev->dev; - skb->pkt_type = PACKET_HOST; - ip_rcv(skb, skb->dev, NULL); /* Wrong ptype */ - return 1; - } -#endif - if (pid == AX25_P_SEGMENT) { - skb_pull(skb, 1); /* Remove PID */ - return ax25_rx_fragment(ax25, skb); + skb_pull(skb, 1); + } else { + if ((frame[0] & AX25_S) == 0) { + pkt_info->frametype = AX25_I; /* I frame - carries NR/NS/PF */ + pkt_info->ns = (frame[0] >> 1) & 0x7F; + pkt_info->nr = (frame[1] >> 1) & 0x7F; + pkt_info->pf = frame[1] & AX25_EPF; + skb_pull(skb, 2); + } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ + pkt_info->frametype = frame[0] & 0x0F; + pkt_info->nr = (frame[1] >> 1) & 0x7F; + pkt_info->pf = frame[1] & AX25_EPF; + skb_pull(skb, 2); + } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ + pkt_info->frametype = frame[0] & ~AX25_PF; + pkt_info->pf = frame[0] & AX25_PF; + skb_pull(skb, 1); + } } +} - if ((func = ax25_protocol_function(pid)) != NULL) { - skb_pull(skb, 1); /* Remove PID */ - return (*func)(skb, ax25); +/* ---------------------------------------------------------------------*/ +/* + * Find a control block that wants to accept the SABM we have just + * received. + */ +ax25_cb *ax25_find_listener(ax25_address *addr, int digi, struct device *dev) +{ + ax25_cb *s; + + if (dev != NULL) + if ((s = ax25_dev_find_listener(addr, digi, dev)) != NULL) + return s; + + for (s = ax25_list; s != NULL; s = s->next) { + if (s->state == AX25_LISTEN + && s->iamdigi == digi + && !ax25cmp(&s->addr.src, addr)) + return s; } + return NULL; +} - if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2) { - if ((!ax25->pidincl && ax25->sk->protocol == pid) || ax25->pidincl) { - if (sock_queue_rcv_skb(ax25->sk, skb) == 0) - queued = 1; - else - ax25->condition |= AX25_COND_OWN_RX_BUSY; +/* ---------------------------------------------------------------------*/ +/* + * Find an AX.25 socket given both ends. + */ +static struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, struct device *dev, int type) +{ + ax25_cb *s; + struct sock *sk; + + if (dev != NULL) + if ((sk = ax25_dev_find_socket(my_addr, dest_addr, dev, type)) != NULL) + return sk; + + for (s = ax25_list; s != NULL; s = s->next) { + if (s->sk != NULL && s->sk->type == type + && !ax25cmp(&s->addr.src, my_addr) + && !ax25cmp(&s->addr.dest, dest_addr)) + { + return s->sk; } } + return NULL; +} + +/* ---------------------------------------------------------------------*/ - return queued; +static int ax25_match_addr(ax25_address *addr, struct device *dev) +{ + ax25_cb *s; + + if (dev != NULL && ax25_dev_match_addr(addr, dev)) + return 1; + + for (s = ax25_list; s != NULL; s = s->next) { + if (s->state == AX25_LISTEN && s->sk == NULL && ax25cmp(&s->addr.src, addr) == 0) + return 1; + } + return 0; } +/* ---------------------------------------------------------------------*/ + /* - * Higher level upcall for a LAPB frame + * this is completely broken. + * + * it depends on that sk is the first of a linked list of sockets + * and traverses the list to find them all, cloning the packet if the + * protocol and socket type both match. */ -static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama) +static void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto) { - int queued = 0; + struct sk_buff *copy; - if (ax25->state == AX25_STATE_0) - return 0; - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - queued = ax25_std_frame_in(ax25, skb, type); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (dama || ax25->ax25_dev->dama.slave) - queued = ax25_ds_frame_in(ax25, skb, type); - else - queued = ax25_std_frame_in(ax25, skb, type); - break; -#endif + while (sk != NULL) { + if (sk->type == SOCK_RAW && + sk->protocol == proto && + atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) { + if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL) + return; + + if (sock_queue_rcv_skb(sk, copy) != 0) + kfree_skb(copy); + } + + sk = sk->next; } - - return queued; } + +/* ---------------------------------------------------------------------*/ -static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_addr, struct packet_type *ptype) +static int ax25_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) { struct sock *make; struct sock *sk; - int type = 0; - ax25_digi dp, reverse_dp; - ax25_cb *ax25; - ax25_address src, dest; - ax25_address *next_digi = NULL; - ax25_dev *ax25_dev; - struct sock *raw; - int mine = 0; - int dama; + ax25_cb *ax25, *peer = NULL; + ax25_address *next_digi; + ax25_pktinfo pinf; + ax25_addr_t reverse_addr; + int queued; /* * Process the AX.25/LAPB frame. */ - - skb->h.raw = skb->data; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) { - kfree_skb(skb); - return 0; - } - if (call_in_firewall(PF_AX25, skb->dev, skb->h.raw, NULL, &skb) != FW_ACCEPT) { - kfree_skb(skb); - return 0; - } + skb->sk = NULL; + queued = 0; + if (call_in_firewall(PF_AX25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) + goto out_normal; /* - * Parse the address header. + * Parse the frame and Pull of the AX.25 headers + * leaving the CTRL/PID byte */ + if (ax25_parse_addr(skb->data, skb->len, &pinf) == NULL) + goto out_normal; + skb_pull(skb, ax25_sizeof_addr(&pinf.addr)); - if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) { - kfree_skb(skb); - return 0; - } - + /* - * Ours perhaps ? + * Steps to perform while processing the frame, fast path'd for + * digipeating. */ - if (dp.lastrepeat + 1 < dp.ndigi) /* Not yet digipeated completely */ - next_digi = &dp.calls[dp.lastrepeat + 1]; + next_digi = NULL; + if (pinf.addr.lastrepeat + 1 < pinf.addr.dcount) { + /* Not yet digipeated completely */ + struct device *out_dev; + next_digi = &pinf.addr.digipeater[pinf.addr.lastrepeat + 1]; + + /* check the frame type. do the easy thing first */ + if ((*skb->data & ~AX25_PF) == AX25_UI) { + /* digipeat UI frame */ + + /* check if next_digi matches one of our interfaces */ + if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL) + goto out_normal; + + /* rebuild the header and transmit the frame */ + out_dev = ax25_rt_set_addr(&reverse_addr, &pinf.addr, dev, out_dev); + skb->nh.raw = skb->data; + skb_push(skb, ax25_sizeof_addr(&reverse_addr)); + ax25_build_addr(skb->data, &reverse_addr, pinf.cmdrsp, AX25_SEQMASK); + ax25_send_unproto(skb, out_dev); + return 0; + } + + ax25_invert_addr(&pinf.addr, &reverse_addr); - /* - * Pull of the AX.25 headers leaving the CTRL/PID bytes - */ - skb_pull(skb, ax25_addr_size(&dp)); + if ((ax25 = ax25_find_cb(&reverse_addr, dev)) != NULL) { + /* no UI, but matches a given context */ + queued = ax25_process_rx_frame(ax25, skb, &pinf); + goto out_queued; + } - /* For our port addresses ? */ - if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi) - mine = 1; + if ((*skb->data & ~AX25_PF) == AX25_SABM || (*skb->data & ~AX25_PF) == AX25_SABME) { + if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL) + goto out_normal; + if ((peer = ax25_create_cb()) == NULL) + goto out_normal; + if ((ax25 = ax25_create_cb()) == NULL) { + ax25_free_cb(peer); + goto out_normal; + } - /* Also match on any registered callsign from L3/4 */ - if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi) - mine = 1; + out_dev = ax25_rt_set_addr(&peer->addr, &pinf.addr, dev, out_dev); + ax25_fillin_cb(peer, out_dev); + ax25_nlpost_route(&pinf, dev); + + /* set up the new control block */ + ax25_fillin_cb(ax25, dev); + ax25->addr = reverse_addr; + + if ((*skb->data & ~AX25_PF) == AX25_SABME) { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW); + } else { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW); + } + + /* crosslink the peers */ + ax25->peer = peer; + peer->peer = ax25; + + /* set window and modulus */ + peer->window = ax25->window; + peer->seqmask = ax25->seqmask; + + ax25->killtimer = 30 * AX25_SLOWHZ; + ax25->state = AX25_STATE_0; + ax25_insert_cb(ax25); + + peer->state = AX25_STATE_1; + ax25_insert_cb(peer); + if (peer->seqmask == AX25_SEQMASK) + ax25_tx_command(peer, AX25_SABM, AX25_POLLON); + else + ax25_tx_command(peer, AX25_SABME, AX25_POLLON); - /* UI frame - bypass LAPB processing */ - if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) { - skb->h.raw = skb->data + 2; /* skip control and pid */ + goto out_normal; + } - if ((raw = ax25_addr_match(&dest)) != NULL) - ax25_send_to_raw(raw, skb, skb->data[1]); + /* + * the packet was no UI or SABM(E) and didn't match an existing + * context. If it's via us, digipeat blindly. + */ + + /* check if next_digi matches one of our interfaces */ + if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL) + goto out_normal; + + /* rebuild the header and transmit the frame */ + out_dev = ax25_rt_set_addr(&reverse_addr, &pinf.addr, dev, out_dev); + skb->nh.raw = skb->data; + skb_push(skb, ax25_sizeof_addr(&reverse_addr)); + ax25_build_addr(skb->data, &reverse_addr, pinf.cmdrsp, AX25_SEQMASK); + ax25_send_unproto(skb, out_dev); + return 0; + } - if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) { - kfree_skb(skb); - return 0; - } + /* + * UI frame - bypass LAPB processing + */ + if ((*skb->data & ~0x10) == AX25_UI) { + int queued = 0; + int pid; + /* skip control and pid */ + skb->nh.raw = skb->data + 2; + + /* copy the datagram to all raw sockets */ +/* ax25_send_to_raw(&dest, skb, dev); */ + + /* if it's not a broadcast or one of our floating listeners, drop it */ + if (!ax25_match_addr(&pinf.addr.dest, dev) + && ax25cmp(&pinf.addr.dest, (ax25_address *)dev->broadcast)) + goto out_normal; /* Now we are pointing at the pid byte */ - switch (skb->data[1]) { -#ifdef CONFIG_INET - case AX25_P_IP: - skb_pull(skb,2); /* drop PID/CTRL */ - skb->h.raw = skb->data; - skb->nh.raw = skb->data; - skb->dev = dev; - skb->pkt_type = PACKET_HOST; - ip_rcv(skb, dev, ptype); /* Note ptype here is the wrong one, fix me later */ - break; + switch (pid = skb->data[1]) { + int (*func)(struct sk_buff *, ax25_cb *); - case AX25_P_ARP: - skb_pull(skb,2); - skb->h.raw = skb->data; - skb->nh.raw = skb->data; - skb->dev = dev; - skb->pkt_type = PACKET_HOST; - arp_rcv(skb, dev, ptype); /* Note ptype here is wrong... */ - break; -#endif - case AX25_P_TEXT: - /* Now find a suitable dgram socket */ - if ((sk = ax25_find_socket(&dest, &src, SOCK_DGRAM)) != NULL) { - if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) { - kfree_skb(skb); - } else { - /* - * Remove the control and PID. - */ - skb_pull(skb, 2); - if (sock_queue_rcv_skb(sk, skb) != 0) - kfree_skb(skb); - } - } else { + case AX25_P_TEXT: + /* Now find a suitable dgram socket */ + if ((sk = ax25_find_socket(&pinf.addr.dest, &pinf.addr.src, dev, SOCK_DGRAM)) != NULL) { + if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) { kfree_skb(skb); + } else { + /* + * Remove the control and PID. + */ + skb_pull(skb, 2); + if (sock_queue_rcv_skb(sk, skb) != 0) + kfree_skb(skb); } - break; - - default: + } else { + kfree_skb(skb); + } + break; + + default: + if ((func = ax25_protocol_function(pid)) != NULL) { + skb_pull(skb, 2); /* Remove CTL/PID */ + queued = func(skb, NULL); + } + if (!queued) kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */ - break; + break; } - + return 0; } + /* LAPB */ /* - * Is connected mode supported on this device ? - * If not, should we DM the incoming frame (except DMs) or - * silently ignore them. For now we stay quiet. + * invert the digipeater path and try to find a context for the + * received packet. */ - if (ax25_dev->values[AX25_VALUES_CONMODE] == 0) { - kfree_skb(skb); - return 0; - } - - /* LAPB */ - - /* AX.25 state 1-4 */ - - ax25_digi_invert(&dp, &reverse_dp); - - if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) { + ax25_invert_addr(&pinf.addr, &reverse_addr); + if ((ax25 = ax25_find_cb(&reverse_addr, dev)) != NULL) { /* * Process the frame. If it is queued up internally it returns one otherwise we * free it immediately. This routine itself wakes the user context layers so we * do no further work */ - if (ax25_process_rx_frame(ax25, skb, type, dama) == 0) - kfree_skb(skb); - - return 0; + queued = ax25_process_rx_frame(ax25, skb, &pinf); + goto out_queued; } - /* AX.25 state 0 (disconnected) */ - - /* a) received not a SABM(E) */ - - if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) { + if ((ax25 = ax25_find_listener(&pinf.addr.dest, 0, dev)) != NULL) { /* - * Never reply to a DM. Also ignore any connects for - * addresses that are not our interfaces and not a socket. + * if this frame is not a SABM(E) return DM to the sender. + * it belongs to a stale connection. maybe we have rebootet or the + * peer didn't see our UA/DM */ - if ((*skb->data & ~AX25_PF) != AX25_DM && mine) - ax25_return_dm(dev, &src, &dest, &dp); - - kfree_skb(skb); - return 0; - } - - /* b) received SABM(E) */ - - if (dp.lastrepeat + 1 == dp.ndigi) - sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET); - else - sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET); - - if (sk != NULL) { - if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, ax25_dev)) == NULL) { - if (mine) ax25_return_dm(dev, &src, &dest, &dp); - kfree_skb(skb); - return 0; - } - - ax25 = make->protinfo.ax25; - skb_set_owner_r(skb, make); - skb_queue_head(&sk->receive_queue, skb); - - make->state = TCP_ESTABLISHED; - make->pair = sk; - - sk->ack_backlog++; - } else { - if (!mine) { - kfree_skb(skb); - return 0; + if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) { + ax25_return_dm(dev, &pinf); + goto out_normal; } - if ((ax25 = ax25_create_cb()) == NULL) { - ax25_return_dm(dev, &src, &dest, &dp); - kfree_skb(skb); - return 0; + /* ok, we have a listener */ + ax25_nlpost_route(&pinf, dev); + if ((sk = ax25->sk) != NULL) { + /* the listener is a socket */ + if (sk->type != SOCK_SEQPACKET || sk->state != TCP_LISTEN) + goto out_normal; + + if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) { + ax25_return_dm(dev, &pinf); + goto out_normal; + } + skb->sk = make; + make->state = TCP_ESTABLISHED; + make->pair = sk; + ax25 = make->protinfo.ax25; + skb_queue_head(&sk->receive_queue, skb); + sk->ack_backlog++; + if (!sk->dead) + sk->data_ready(sk, skb->len); + queued = 1; + } else { + /* + * the listener is no socket, must be a callsign registered + * by a higher protocol, we just take the connection + */ + if ((ax25 = ax25_create_cb()) == NULL) { + ax25_return_dm(dev, &pinf); + goto out_normal; + } + ax25->slcomp_enable = ax25_rt_mode_get(&pinf.addr.src) == 'C'; } - ax25_fillin_cb(ax25, ax25_dev); - } - - ax25->source_addr = dest; - ax25->dest_addr = src; - - /* - * Sort out any digipeated paths. - */ - if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { - kfree_skb(skb); - ax25_destroy_socket(ax25); - return 0; - } - - if (dp.ndigi == 0) { - if (ax25->digipeat != NULL) { - kfree(ax25->digipeat); - ax25->digipeat = NULL; + /* set up the new control block */ + ax25_fillin_cb(ax25, dev); + ax25->addr = reverse_addr; + + if ((*skb->data & ~AX25_PF) == AX25_SABME) { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW); + } else { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW); } - } else { - /* Reverse the source SABM's path */ - *ax25->digipeat = reverse_dp; - } - if ((*skb->data & ~AX25_PF) == AX25_SABME) { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW]; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_WINDOW]; + ax25->device = dev; + ax25->wrt_timer = ax25->t3; + ax25->idletimer = ax25->idle; + ax25->state = AX25_STATE_3; + ax25_insert_cb(ax25); + ax25_tx_response(ax25, AX25_UA, AX25_POLLON); } - - ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE); -#ifdef CONFIG_AX25_DAMA_SLAVE - if (dama && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE) - ax25_dama_on(ax25); -#endif - - ax25->state = AX25_STATE_3; - - ax25_insert_socket(ax25); - - ax25_start_heartbeat(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - - if (sk != NULL) { - if (!sk->dead) - sk->data_ready(sk, skb->len); - } else { + out_queued: + if (!queued) { + out_normal: kfree_skb(skb); } - return 0; } - -/* - * Receive an AX.25 frame via a SLIP interface. - */ -int ax25_kiss_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) -{ - skb->sk = NULL; /* Initially we don't know who it's for */ - skb->destructor = NULL; /* Who initializes this, dammit?! */ - - if ((*skb->data & 0x0F) != 0) { - kfree_skb(skb); /* Not a KISS data frame */ - return 0; - } - - skb_pull(skb, AX25_KISS_HEADER_LEN); /* Remove the KISS byte */ - - return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype); -} - -#endif Index: linux/net/ax25/ax25_in.h diff -u /dev/null linux/net/ax25/ax25_in.h:1.1.4.1 --- /dev/null Wed Jul 7 01:02:32 1999 +++ linux/net/ax25/ax25_in.h Sun Jul 4 13:03:45 1999 @@ -0,0 +1,26 @@ +#ifndef _AX25_IN_H +#define _AX25_IN_H + +/* + * prototype for state machine functions. + */ + +typedef int (*ax25_statefunc_t)(ax25_cb*, struct sk_buff*, ax25_pktinfo*); + +extern int ax25_process_rx_frame(ax25_cb*, struct sk_buff*, ax25_pktinfo*); +extern int ax25_rx_iframe(ax25_cb*, struct sk_buff*); +extern void ax25_reseq_update(ax25_cb*, struct sk_buff*, int); +extern int ax25_reseq_in(ax25_cb*, struct sk_buff*, int, int); +extern void ax25_reseq_out(ax25_cb*); +extern ax25_cb* ax25_find_listener(ax25_address*, int, struct device*); +extern struct sock* ax25_make_new(struct sock*, struct device*); + +extern struct packet_type ax25_packet_type; + +#endif + + + + + + Index: linux/net/ax25/ax25_ip.c diff -u linux/net/ax25/ax25_ip.c:1.1.2.1 linux/net/ax25/ax25_ip.c:removed --- linux/net/ax25/ax25_ip.c:1.1.2.1 Sun Jul 4 10:18:10 1999 +++ linux/net/ax25/ax25_ip.c Wed Jul 7 01:02:32 1999 @@ -1,212 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * History - * AX.25 036 Jonathan(G4KLX) Split from af_ax25.c. - */ - -#include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For TIOCINQ/OUTQ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * IP over AX.25 encapsulation. - */ - -/* - * Shove an AX.25 UI header on an IP packet and handle ARP - */ - -#ifdef CONFIG_INET - -int ax25_encapsulate(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) -{ - /* header is an AX.25 UI frame from us to them */ - unsigned char *buff = skb_push(skb, AX25_HEADER_LEN); - - *buff++ = 0x00; /* KISS DATA */ - - if (daddr != NULL) - memcpy(buff, daddr, dev->addr_len); /* Address specified */ - - buff[6] &= ~AX25_CBIT; - buff[6] &= ~AX25_EBIT; - buff[6] |= AX25_SSSID_SPARE; - buff += AX25_ADDR_LEN; - - if (saddr != NULL) - memcpy(buff, saddr, dev->addr_len); - else - memcpy(buff, dev->dev_addr, dev->addr_len); - - buff[6] &= ~AX25_CBIT; - buff[6] |= AX25_EBIT; - buff[6] |= AX25_SSSID_SPARE; - buff += AX25_ADDR_LEN; - - *buff++ = AX25_UI; /* UI */ - - /* Append a suitable AX.25 PID */ - switch (type) { - case ETH_P_IP: - *buff++ = AX25_P_IP; - break; - case ETH_P_ARP: - *buff++ = AX25_P_ARP; - break; - default: - printk(KERN_ERR "AX.25: ax25_encapsulate - wrong protocol type 0x%x2.2\n", type); - *buff++ = 0; - break; - } - - if (daddr != NULL) - return AX25_HEADER_LEN; - - return -AX25_HEADER_LEN; /* Unfinished header */ -} - -int ax25_rebuild_header(struct sk_buff *skb) -{ - struct sk_buff *ourskb; - unsigned char *bp = skb->data; - struct device *dev; - ax25_address *src, *dst; - ax25_route *route; - ax25_dev *ax25_dev; - - dst = (ax25_address *)(bp + 1); - src = (ax25_address *)(bp + 8); - - if (arp_find(bp + 1, skb)) - return 1; - - route = ax25_rt_find_route(dst, NULL); - dev = route->dev; - - if (dev == NULL) - dev = skb->dev; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return 1; - - if (bp[16] == AX25_P_IP) { - if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { - /* - * We copy the buffer and release the original thereby - * keeping it straight - * - * Note: we report 1 back so the caller will - * not feed the frame direct to the physical device - * We don't want that to happen. (It won't be upset - * as we have pulled the frame from the queue by - * freeing it). - * - * NB: TCP modifies buffers that are still - * on a device queue, thus we use skb_copy() - * instead of using skb_clone() unless this - * gets fixed. - */ - - ax25_address src_c; - ax25_address dst_c; - - if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) { - kfree_skb(skb); - return 1; - } - - if (skb->sk != NULL) - skb_set_owner_w(ourskb, skb->sk); - - kfree_skb(skb); - - src_c = *src; - dst_c = *dst; - - skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */ - - ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], &src_c, -&dst_c, route->digipeat, dev); - - return 1; - } - } - - bp[7] &= ~AX25_CBIT; - bp[7] &= ~AX25_EBIT; - bp[7] |= AX25_SSSID_SPARE; - - bp[14] &= ~AX25_CBIT; - bp[14] |= AX25_EBIT; - bp[14] |= AX25_SSSID_SPARE; - - skb_pull(skb, AX25_KISS_HEADER_LEN); - - if (route->digipeat != NULL) { - if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) { - kfree_skb(skb); - return 1; - } - - skb = ourskb; - } - - skb->dev = dev; - - ax25_queue_xmit(skb); - - return 1; -} - -#else /* INET */ - -int ax25_encapsulate(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) -{ - return -AX25_HEADER_LEN; -} - -int ax25_rebuild_header(struct sk_buff *skb) -{ - return 1; -} - -#endif - -#endif Index: linux/net/ax25/ax25_ipax.c diff -u /dev/null linux/net/ax25/ax25_ipax.c:1.1.4.1 --- /dev/null Wed Jul 7 01:02:32 1999 +++ linux/net/ax25/ax25_ipax.c Sun Jul 4 13:03:45 1999 @@ -0,0 +1,691 @@ +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ax25_route.h" +#include "ax25_core.h" +#include "ax25_ddi.h" +#include "ax25_vj.h" +#include "ax25_netlink.h" +#include "ax25_subr.h" + +static int ipax_hard_header(struct sk_buff*, struct device*, unsigned short, void*, void*, unsigned); +static int ipax_set_mac_address(struct device*, void*); +static int ipax_probe(struct device*); +static int ipax_device_event(struct notifier_block*, unsigned long, void*); +static int ipax_open(struct device*); +static int ipax_close(struct device*); +static int ipax_rcv(struct sk_buff*, ax25_cb*); +static int ipax_arp_rcv(struct sk_buff*, ax25_cb*); +static int ipax_vjc_rcv(struct sk_buff*, struct ax25_cb*); +static int ipax_vjunc_rcv(struct sk_buff*, struct ax25_cb*); +static int ipax_send_packet(struct sk_buff*, struct device*); +static struct net_device_stats *ipax_get_stats(struct device*); + +/* --------------------------------------------------------------------- */ + +static struct device ipax_device; + +static struct ipax_local { + struct net_device_stats stats; +}; + +/* + * Device up/down notifier block + */ +static struct notifier_block ipax_dev_notifier = { + ipax_device_event, + 0 +}; + +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}; + +/* ---------------------------------------------------------------------*/ + +int ipax_init(void) +{ + struct device *dev = &ipax_device; + + memset(dev, 0, sizeof (struct device)); + + dev->name = "ipax0"; + dev->if_port = 0; + dev->init = ipax_probe; + dev->start = 0; + dev->tbusy = 1; + dev->base_addr = 0; + dev->irq = 0; + dev->dma = 0; + + register_netdevice_notifier(&ipax_dev_notifier); + + if (register_netdev(dev)) { + printk(KERN_WARNING "ipax: cannot register net device\n"); + return -ENXIO; + } + + return 0; +} + +int ipax_cleanup(void) +{ + unregister_netdevice_notifier(&ipax_dev_notifier); + unregister_netdev(&ipax_device); + return 0; +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_device_event(struct notifier_block *this, unsigned long event, void *ptr) +{ +/* struct device *dev = (struct device *)ptr; */ + + return NOTIFY_DONE; + +} + +/* ---------------------------------------------------------------------*/ +/* + * set the MAC layer address of the interface. (i.e. the callsign) + * + */ +static int ipax_set_mac_address(struct device *dev, void *addr) +{ + struct sockaddr *sa = (struct sockaddr *)addr; + ax25_listen_release((ax25_address *)dev->dev_addr, NULL); + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + ax25_listen_register((ax25_address *)dev->dev_addr, NULL); + return 0; +} + +/* ---------------------------------------------------------------------*/ +/* + * Interface startup. + * + */ +static int ipax_probe(struct device *dev) +{ + if (dev == NULL) + return -ENXIO; + + dev->base_addr = 0; + dev->irq = 0; + dev->dma = 0; + + /* Initialize the device structure. */ + if (dev->priv == NULL) { + dev->priv = kmalloc(sizeof(struct ipax_local), GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + } + + memset(dev->priv, 0, sizeof(struct ipax_local)); + + dev->open = ipax_open; + dev->stop = ipax_close; + dev->hard_start_xmit = ipax_send_packet; + dev->get_stats = ipax_get_stats; + + dev->hard_header = ipax_hard_header; + dev->set_mac_address = ipax_set_mac_address; + + dev->type = ARPHRD_AX25; + dev->hard_header_len = AX25_MAX_HEADER_LEN; + dev->mtu = 1500; + dev->addr_len = AX25_ADDR_LEN; + dev->tx_queue_len = 8; + + memcpy(dev->broadcast, ax25_bcast, dev->addr_len); + memcpy(dev->dev_addr, ax25_test, dev->addr_len); + + dev->flags = IFF_BROADCAST; + return 0; +} + +/* ---------------------------------------------------------------------*/ +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + * + */ +static int ipax_open(struct device *dev) +{ + if (dev->start) + return 0; + + if (!ax25_listen_register((ax25_address *)dev->dev_addr, NULL)) + return 0; + + if (!ax25_protocol_register(AX25_P_IP, ipax_rcv)) + return 0; + + if (!ax25_protocol_register(AX25_P_ARP, ipax_arp_rcv)) + return 0; + + if (!ax25_protocol_register(AX25_P_VJCOMP, ipax_vjc_rcv)) + return 0; + + if (!ax25_protocol_register(AX25_P_VJUNCOMP, ipax_vjunc_rcv)) + return 0; + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + MOD_INC_USE_COUNT; + + return 0; +} + +/* ---------------------------------------------------------------------*/ +/* + * the inverse routine to ipax_open. close the interface end + * decrement the module use count. + */ +static int ipax_close(struct device *dev) +{ + if (!dev->start) + return 0; + + dev->tbusy = 1; + dev->start = 0; + + ax25_listen_release((ax25_address *)dev->dev_addr, NULL); + ax25_protocol_release(AX25_P_IP); + ax25_protocol_release(AX25_P_ARP); + ax25_protocol_release(AX25_P_VJCOMP); + ax25_protocol_release(AX25_P_VJUNCOMP); + + MOD_DEC_USE_COUNT; + + return 0; + +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_rcv(struct sk_buff *skb, ax25_cb *ax25) +{ + struct device *dev = &ipax_device; + struct ipax_local *lp = (struct ipax_local *)dev->priv; + + skb->nh.raw = skb->data; + skb->dev = dev; + skb->pkt_type = PACKET_HOST; + skb->protocol = __constant_htons(ETH_P_IP); + if (ax25) /* FIXME: works only for VC */ + ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev); + ip_rcv(skb, dev, NULL); /* Wrong ptype */ + lp->stats.rx_packets++; + + return 1; +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_arp_rcv(struct sk_buff *skb, ax25_cb *ax25) +{ + struct device *dev = &ipax_device; + struct ipax_local *lp = (struct ipax_local *)dev->priv; + + skb->nh.raw = skb->data; + skb->dev = dev; + skb->pkt_type = PACKET_HOST; + skb->protocol = __constant_htons(ETH_P_ARP); + arp_rcv(skb, dev, NULL); /* Wrong ptype */ + + lp->stats.rx_packets++; + + return 1; +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_vjc_rcv(struct sk_buff *skb, struct ax25_cb *ax25) +{ + struct device *dev = &ipax_device; + struct ipax_local *lp = (struct ipax_local *)dev->priv; + + if (!ax25) + return 0; + + ax25->slcomp_enable = 1; + + /* + * check if we already have initialized slots, + * if not, it's too late. flush the frame. + */ + if (ax25->slcomp == NULL) { + ax25->slcomp = axhc_init(32, 32); + printk(KERN_DEBUG "ax25_vjc_recv: no vjc-slots allocated, packet dropped.\n"); + lp->stats.rx_dropped++; + return 0; + } + + /* + * the data will grow upwards while decompressing, + * crippling the ax.25 header. copy the packet only if + * it's smaller than 256 bytes, else the defragmenter has + * already copied it, we don't do that twice. + */ + if (skb->len <= 256) { + struct sk_buff *skbn; + if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) { + kfree_skb(skb); + skb = skbn; + } + } + + /* set device the packet came in */ + skb->dev = dev; + skb->pkt_type = PACKET_HOST; + + /* + * make headroom for MAX_HEADER bytes of TCP/IP header + */ + if (skb_headroom(skb) < MAX_HEADER) { + struct sk_buff *skbn; + if ((skbn = skb_realloc_headroom(skb, MAX_HEADER)) == NULL) { + printk(KERN_DEBUG "ax25_vjc_recv: cannot reallocate headroom, packet dropped.\n"); + lp->stats.rx_dropped++; + return 0; + } + kfree_skb(skb); + skb = skbn; + } + + if (axhc_uncompress(ax25->slcomp, skb) <= 0) { + printk(KERN_DEBUG "ipax_vjc_rcv: error decompressing packet, dropped.\n"); + lp->stats.rx_dropped++; + return 0; + } + /* adjust pointer to network header */ + skb->nh.raw = skb->data; + skb->protocol = __constant_htons(ETH_P_IP); + ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev); + ip_rcv(skb, dev, NULL); + lp->stats.rx_packets++; + return 1; +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_vjunc_rcv(struct sk_buff *skb, struct ax25_cb *ax25) +{ + struct device *dev = &ipax_device; + struct ipax_local *lp = (struct ipax_local *)dev->priv; + + if (!ax25) + return 0; + + ax25->slcomp_enable = 1; + + /* + * MW: + * check if we already have initialized slots, + * do so if not. + */ + if (ax25->slcomp == NULL) + ax25->slcomp = axhc_init(32, 32); + + if (axhc_remember(ax25->slcomp, skb) <= 0) { + printk(KERN_DEBUG "ipax_vjunc_rcv: unable to remember slot, packet dropped.\n"); + lp->stats.rx_dropped++; + return 0; + } + skb->nh.raw = skb->data; + skb->dev = dev; + skb->pkt_type = PACKET_HOST; + skb->protocol = __constant_htons(ETH_P_IP); + ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev); + ip_rcv(skb, dev, NULL); + lp->stats.rx_packets++; + return 1; +} + +/* ---------------------------------------------------------------------*/ + +static void ipax_fragment(struct sk_buff *skb, struct device *dev, ax25_addr_t *addr) +{ + struct iphdr *iph; + unsigned char *raw; + unsigned char *ptr; + struct sk_buff *skb2; + unsigned int mtu, hlen, left, len; + int offset; + int not_last_frag; + + /* + * Point into the IP datagram header. + */ + + raw = skb->nh.raw; + iph = (struct iphdr*)raw; + + /* + * Setup starting values. + */ + + hlen = iph->ihl * 4; + left = ntohs(iph->tot_len) - hlen; /* Space per frame */ + mtu = dev->mtu; /* Size of data space */ + ptr = raw + hlen; /* Where to start from */ + + /* + * The protocol doesn't seem to say what to do in the case that the + * frame + options doesn't fit the mtu. As it used to fall down dead + * in this case we were fortunate it didn't happen + * + * It is impossible, because mtu>=68. --ANK (980801) + */ + +#ifdef CONFIG_NET_PARANOIA + if (mtu<8) + goto fail; +#endif + + /* + * Fragment the datagram. + */ + + offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3; + not_last_frag = iph->frag_off & htons(IP_MF); + + /* + * Keep copying data until we run out. + */ + + while(left > 0) { + len = left; + /* IF: it doesn't fit, use 'mtu' - the data space left */ + if (len > mtu) + len = mtu; + /* IF: we are not sending upto and including the packet end + then align the next start on an eight byte boundary */ + if (len < left) { + len &= ~7; + } + /* + * Allocate buffer. + */ + + if ((skb2 = alloc_skb(len+hlen+AX25_MAX_HEADER_LEN+15,GFP_ATOMIC)) == NULL) { + NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n")); + goto fail; + } + + /* + * Set up data on packet + */ + + skb2->pkt_type = skb->pkt_type; + skb2->priority = skb->priority; + skb_reserve(skb2, (AX25_MAX_HEADER_LEN+15)&~15); + skb_put(skb2, len + hlen); + skb2->nh.raw = skb2->data; + skb2->h.raw = skb2->data + hlen; + + /* + * Charge the memory for the fragment to any owner + * it might possess + */ + + if (skb->sk) + skb_set_owner_w(skb2, skb->sk); + skb2->dst = dst_clone(skb->dst); + + /* + * Copy the packet header into the new buffer. + */ + + memcpy(skb2->nh.raw, raw, hlen); + + /* + * Copy a block of the IP datagram. + */ + memcpy(skb2->h.raw, ptr, len); + left -= len; + + /* + * Fill in the new header fields. + */ + iph = skb2->nh.iph; + iph->frag_off = htons((offset >> 3)); + + /* ANK: dirty, but effective trick. Upgrade options only if + * the segment to be fragmented was THE FIRST (otherwise, + * options are already fixed) and make it ONCE + * on the initial skb, so that all the following fragments + * will inherit fixed options. + */ + if (offset == 0) + ip_options_fragment(skb); + + /* + * Added AC : If we are fragmenting a fragment that's not the + * last fragment then keep MF on each bit + */ + if (left > 0 || not_last_frag) + iph->frag_off |= htons(IP_MF); + ptr += len; + offset += len; + + /* + * Put this fragment into the sending queue. + */ + + ip_statistics.IpFragCreates++; + + iph->tot_len = htons(len + hlen); + + ip_send_check(iph); + + /* build UI packet header */ + *skb_push(skb2, 1) = AX25_P_IP; + *skb_push(skb2, 1) = AX25_UI; + skb_push(skb2, ax25_sizeof_addr(addr)); + ax25_build_addr(skb2->data, addr, AX25_COMMAND, AX25_SEQMASK); + ax25_send_unproto(skb2, dev); + } + kfree_skb(skb); + ip_statistics.IpFragOKs++; + return; + +fail: + kfree_skb(skb); + ip_statistics.IpFragFails++; + +} + +/* ---------------------------------------------------------------------*/ + +static unsigned char ipax_try_compress(ax25_cb *ax25, struct sk_buff *skb) +{ + unsigned char pid; + + if (ax25->slcomp == NULL) + ax25->slcomp = axhc_init(32, 32); + /* attempt to compress the frame */ + pid = axhc_compress(ax25->slcomp, skb, ax25->slcomp_enable); + if (pid) + *skb_push(skb, 1) = pid; + return pid; +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_send_packet(struct sk_buff *skb, struct device *dev) +{ + struct ipax_local *lp = (struct ipax_local *)dev->priv; + ax25_address *dest, *source; + struct ax25_route *axrt; + struct device *axdev; + ax25_addr_t addr; + int tbusy = 0; + + /* + * send the packet down to the tunneling protocol + */ + dest = (ax25_address *)skb->data; + source = (ax25_address *)(skb->data+AX25_ADDR_LEN); + + if (!ax25cmp(dest, (ax25_address *)ax25_bcast)) { + ax25_send_broadcast(skb); + kfree_skb(skb); + lp->stats.tx_packets++; + return 0; + } + + axrt = ax25_find_route(dest); + if (axrt == NULL) { + printk(KERN_DEBUG "ax25: ipax_send_packet(): no route found, discarding packet.\n"); + kfree_skb(skb); + lp->stats.tx_dropped++; + return 0; + } + + axdev = axrt->dev; + + addr.dest = *dest; + addr.src = *source; + addr.dcount = axrt->path.dcount; + addr.lastrepeat = -1; + memcpy(addr.digipeater, axrt->path.digipeater, sizeof(ax25_address)*addr.dcount); + + if (skb->data[15] == AX25_P_IP) { + struct iphdr *iph = (struct iphdr *)(skb->data+16); + int mode = axrt->ip_mode; + + if (iph->protocol != IPPROTO_UDP) { + if (mode == 'V' || mode == 'C' + || (mode == ' ' && ax25_dev_get_value(axdev, AX25_VALUES_IPDEFMODE))) + { + int paclen = ax25_dev_get_value(axdev, AX25_VALUES_PACLEN); + ax25_cb* ax25 = ax25_find_cb(&addr, axdev); + + if (ax25 != NULL) { + /* reuse a just disconnected control block */ + if (ax25->state == AX25_STATE_0 || ax25->state == AX25_STATE_2) { + if (ax25->slcomp) { + axhc_free(ax25->slcomp); + ax25->slcomp = NULL; + } + goto out_reused; + } + goto out_ok; + } + if ((ax25 = ax25_create_cb()) == NULL) + goto out_dropped; + ax25_fillin_cb(ax25, axdev); + ax25->addr = addr; + ax25_insert_cb(ax25); + out_reused: + ax25->slcomp_enable = (mode == 'C'); + ax25_establish_data_link(ax25); + out_ok: + /* completely strip the header */ + skb_pull(skb, AX25_MIN_HEADER_LEN+1); + if (!ipax_try_compress(ax25, skb)) + goto out_dropped; + ax25_output(ax25, paclen, skb); + dev->trans_start = jiffies; + dev->tbusy = tbusy; + lp->stats.tx_packets++; + return 0; + + out_dropped: + lp->stats.tx_dropped++; + kfree_skb(skb); + dev->tbusy = tbusy; + return 0; + } + } else if (skb->len > axdev->mtu) { + /* completely strip the header */ + skb_pull(skb, AX25_MIN_HEADER_LEN+1); + ipax_fragment(skb, axdev, &addr); + return 0; + } + } + + /* rebuild the header if digipeaters are to be used */ + if (addr.dcount != 0) { + /* strip address field, keep the control byte */ + skb_pull(skb, AX25_MIN_HEADER_LEN-1); + skb_push(skb, ax25_sizeof_addr(&addr)); + ax25_build_addr(skb->data, &addr, AX25_COMMAND, AX25_SEQMASK); + } + + /* + * queue a completely assembled frame to the unproto + * queue of an interface + */ + ax25_send_unproto(skb, axdev); + dev->trans_start = jiffies; + lp->stats.tx_packets++; + dev->tbusy = tbusy; + return 0; +} + +/* ---------------------------------------------------------------------*/ +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ +static struct net_device_stats *ipax_get_stats(struct device *dev) +{ + struct ipax_local *lp = (struct ipax_local *)dev->priv; + return &lp->stats; +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_hard_header(struct sk_buff* skb, struct device* dev, unsigned short type, void* daddr, void* saddr, unsigned len) +{ + unsigned char pid; + unsigned char *buf; + int hlen; + + if (type == ETH_P_IP) + pid = AX25_P_IP; + else if (type == ETH_P_ARP) + pid = AX25_P_ARP; + else { + printk(KERN_ERR "ax25: ipax_hard_header: unknown packet type %d.\n", type); + pid = 0; + } + *skb_push(skb, 1) = pid; + *skb_push(skb, 1) = AX25_UI; + + if (saddr != NULL) + memcpy(skb_push(skb, AX25_ADDR_LEN), saddr, AX25_ADDR_LEN); + else + memcpy(skb_push(skb, dev->addr_len), dev->dev_addr, dev->addr_len); + + if (daddr != NULL) { + memcpy(skb_push(skb, AX25_ADDR_LEN), daddr, AX25_ADDR_LEN); + hlen = (AX25_MIN_HEADER_LEN+1); + } else { + memcpy(skb_push(skb, AX25_ADDR_LEN), &null_ax25_address, AX25_ADDR_LEN); + hlen = -(AX25_MIN_HEADER_LEN+1); + } + + buf = skb->data; + buf[6] &= ~(AX25_CBIT|AX25_EBIT); + buf[6] |= AX25_SSSID_SPARE; + buf[13] &= ~AX25_CBIT; + buf[13] |= (AX25_EBIT|AX25_SSSID_SPARE); + + return hlen; +} Index: linux/net/ax25/ax25_lapb.c diff -u /dev/null linux/net/ax25/ax25_lapb.c:1.1.4.1 --- /dev/null Wed Jul 7 01:02:32 1999 +++ linux/net/ax25/ax25_lapb.c Sun Jul 4 13:03:45 1999 @@ -0,0 +1,698 @@ +#include +#include +#include +#include +#include + +#include "ax25_ddi.h" +#include "ax25_vj.h" +#include "ax25_route.h" +#include "ax25_in.h" +#include "ax25_lapb.h" +#include "ax25_core.h" +#include "ax25_subr.h" + +/* + * Calculate the Round Trip Time + */ +static void ax25_calculate_rtt(ax25_cb *ax25, unsigned long rtt_raw) +{ + switch (ax25->backoff) { + case 0: + ax25->rtt = rtt_raw; + break; + + case 1: + case 2: + ax25->rtt = (9 * ax25->rtt + rtt_raw) / 10; + break; + } +} + +/* + * This routine purges the input queue of those frames that have been + * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the + * SDL diagram. + */ +static void ax25_frames_acked(ax25_cb *ax25, unsigned short nr) +{ + struct sk_buff *skb; + /* + * Remove all the ack-ed frames from ack_queue. + */ + while (ax25->va != nr && (skb = skb_dequeue(&ax25->ack_queue)) != NULL) { + ax25->va = (ax25->va + 1) & ax25->seqmask; + kfree_skb(skb); + + if (ax25->vs_rtt == ax25->va) { + ax25_calculate_rtt(ax25, jiffies - ax25->rtt_timestamp + 1); + ax25->vs_rtt = -1; + } + } + + if (ax25->condition & AX25_COND_SETUP) + ax25_clr_cond(ax25, AX25_COND_SETUP); +} + +/* + * This routine decides whether to restart T1 or not on an incoming + * frame. If the frame acks all outstanding frames, T1 is + * stopped, T3 is started. Else if it acks at least one + * outstanding frame T1 is restarted. + */ +static void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr) +{ + if (ax25->va != nr) { + /* at least one frame acked */ + ax25_frames_acked(ax25, nr); + + if (ax25->vs == nr) + /* all frames acked */ + ax25->wrt_timer = ax25->t3; + } +} + +/* + * Due to the changed retransmission handling frames are not + * requeued from the ack_queue into the write queue. All + * retransmission stuff is done in ax25_dev_timer() now. This + * function is for clarity only, it will either be removed + * completely or turn into an inline function. + */ +static void ax25_requeue_frames(ax25_cb *ax25) +{ + /* + * reset the send sequence counter. + */ + ax25->vs = ax25->va; +} + +/* + * Validate that the value of nr is between va and vs. Return true or + * false for testing. + */ +static int ax25_validate_nr(ax25_cb *ax25, unsigned short nr) +{ + unsigned short ds; + unsigned short dr; + + ds = ax25->vs_max - ax25->va; + ds &= ax25->seqmask; + + dr = nr - ax25->va; + dr &= ax25->seqmask; + + return (dr <= ds); +} + +/* + * link reset handling. This is a rather complex routine, as it + * has to cope with a tricky situation in the protocol flow. + */ +static int ax25_reset_link(ax25_cb* ax25, struct sk_buff* skb) +{ + ax25_cb* peer = ax25->peer; + + ax25->wrt_timer = 0; + ax25->ack_timer = 0; + ax25->idletimer = ax25->idle; + ax25->n2count = 0; + + /* + * peer just didn't get our UA, handle gracefully. + */ + if (ax25->condition & AX25_COND_SETUP) { + ax25_requeue_frames(ax25); + ax25->state = AX25_STATE_3; + ax25->vs_rtt = -1; + ax25_tx_response(ax25, AX25_UA, AX25_POLLON); + return 0; + } + + /* + * Ok, this is a *real* reset. If we have an attached socket, + * disconnect it and produce a new, accept-ready one. + */ + if (ax25->sk != NULL) { + struct sock* make; + struct sock* sk; + ax25_cb* tmp = ax25_find_listener(&ax25->addr.src, 0, ax25->device); + + printk(KERN_DEBUG "ax25_lapb.c: resetting socket\n"); + + if (!tmp || !(sk = tmp->sk) + || sk->ack_backlog == sk->max_ack_backlog + || (make = ax25_make_new(sk, ax25->device)) == NULL) + { + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25->state = AX25_STATE_0; + ax25_tx_response(ax25, AX25_DM, AX25_POLLON); + return 0; + } + + /* set the destination address */ + make->protinfo.ax25->addr = ax25->addr; + + /* + * ax25_make_new() has produced a new ax25_cb, attached to + * a struct sock, we just throw away the old one the regular + * way and insert the new one into the appropriate device queue + * + * It should be enough to disconnect() the old control block, + * as this will wake up the application which then in turn + * does a close() on the respective socket descriptor. + */ + ax25_remove_cb(ax25); + ax25_disconnect(ax25, ECONNRESET); + ax25_close_socket(ax25->sk, ECONNRESET); + + /* + * done with the old one, now for the new one + */ + ax25 = make->protinfo.ax25; + ax25->state = AX25_STATE_3; + ax25->wrt_timer = ax25->t3; + ax25->idletimer = ax25->idle; + ax25->vs_rtt = -1; + + make->pair = sk; + skb->sk = make; + skb_queue_head(&sk->receive_queue, skb); + sk->ack_backlog++; + ax25_dev_insert_cb(ax25); + ax25_tx_response(ax25, AX25_UA, AX25_POLLON); + sk->data_ready(sk, skb->len); + return 1; + } + + ax25->vs = 0; + ax25->va = 0; + ax25->vr = 0; + + /* + * check if we have a peer connection to notify + */ + if (peer) { + + printk(KERN_DEBUG "ax25_lapb.c: resetting digipeated connection, state:%d peer state:%d\n", + ax25->state, peer->state); + + ax25_clear_queues(ax25); + ax25->killtimer = 30 * AX25_SLOWHZ; + ax25->wrt_timer = 0; + ax25->ack_timer = 0; + ax25->state = AX25_STATE_0; + ax25->condition = AX25_COND_SETUP; + + ax25_clear_queues(peer); + peer->wrt_timer = 0; + peer->ack_timer = 0; + peer->vs = 0; + peer->va = 0; + peer->vr = 0; + peer->condition = AX25_COND_SETUP; + peer->state = AX25_STATE_1; + if (peer->seqmask == AX25_SEQMASK) + ax25_tx_command(peer, AX25_SABM, AX25_POLLON); + else + ax25_tx_command(peer, AX25_SABME, AX25_POLLON); + return 0; + } + + printk(KERN_DEBUG "ax25_lapb.c: resetting protocol uplink\n"); + + if (ax25->slcomp != NULL) { + axhc_free(ax25->slcomp); + ax25->slcomp = NULL; + } + ax25->state = AX25_STATE_3; + ax25->vs_rtt = -1; + ax25->wrt_timer = ax25->t3; + ax25->condition = AX25_COND_SETUP; + ax25_tx_response(ax25, AX25_UA, AX25_POLLON); + + return 0; +} + +/* + * State machine for state 0, Disconnected State. + * we need this now for the hop-2-hop acknowledgement + */ +static int ax25_state0_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + ax25_cb *peer = ax25->peer; + + switch (pkt->frametype) { + case AX25_SABM: + case AX25_SABME: + if (peer) { + if (peer->condition & AX25_COND_SETUP) { + if (peer->seqmask == AX25_SEQMASK) + ax25_tx_command(peer, AX25_SABM, AX25_POLLON); + else + ax25_tx_command(peer, AX25_SABME, AX25_POLLON); + ax25->killtimer = 30 * AX25_SLOWHZ; + } else { + struct device *dev; + if ((dev = ax25_rt_set_addr(&peer->addr, &pkt->addr, ax25->device, peer->device)) == NULL) + ax25_tx_command(ax25, AX25_DM, AX25_POLLON); + else { + ax25_fillin_cb(peer, dev); + ax25_reset_link(ax25, skb); + } + } + } else if (!ax25->sk) + ax25_reset_link(ax25, skb); + break; + + case AX25_DISC: + ax25_tx_response(ax25, AX25_DM, pkt->pf); + break; + + default: + if (pkt->pf && pkt->cmdrsp == AX25_COMMAND) + ax25_tx_response(ax25, AX25_DM, AX25_POLLON); + break; + } + return 0; /* we never queue */ +} + + +/* + * State machine for state 1, Awaiting Connection State. + * The handling of the timer(s) is in file ax25_timer.c. + * Handling of state 0 and connection release is in ax25.c. + */ +static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + ax25_cb *peer = ax25->peer; + + switch (pkt->frametype) { + case AX25_SABM: + if (peer || ax25->sk == NULL) { + if (ax25->seqmask != AX25_SEQMASK) { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + } + ax25->state = AX25_STATE_3; + ax25->vs_rtt = -1; + ax25_tx_response(ax25, AX25_UA, pkt->pf); + if (peer) { + peer->state = AX25_STATE_3; + peer->vs_rtt = -1; + ax25_tx_response(peer, AX25_UA, pkt->pf); + } + } + break; + + case AX25_SABME: + if (ax25->seqmask == AX25_ESEQMASK && (peer || ax25->sk == NULL)) { + ax25->state = AX25_STATE_3; + ax25->vs_rtt = -1; + ax25_tx_response(ax25, AX25_UA, pkt->pf); + if (peer) { + peer->state = AX25_STATE_3; + peer->vs_rtt = -1; + ax25_tx_response(peer, AX25_UA, pkt->pf); + } + } + break; + + case AX25_DISC: + ax25_tx_response(ax25, AX25_DM, pkt->pf); + break; + + case AX25_UA: + if (pkt->pf) { + ax25->state = AX25_STATE_3; + ax25->wrt_timer = ax25->t3; + ax25->idletimer = ax25->idle; + ax25->killtimer = 0; + ax25->vs = 0; + ax25->va = 0; + ax25->vr = 0; + ax25->vs_rtt = -1; + ax25->n2count = 0; + ax25->tx_cmd = 0; + ax25->tx_rsp = 0; + if (peer != NULL) { + peer->wrt_timer = peer->t3; + peer->idletimer = 0; + peer->killtimer = 0; + peer->vs = 0; + peer->va = 0; + peer->vr = 0; + peer->vs_rtt = -1; + peer->n2count = 0; + peer->state = AX25_STATE_3; + ax25_clr_cond(peer, AX25_COND_STATE_CHANGE); + ax25_tx_response(peer, AX25_UA, pkt->pf); + } else if (ax25->sk != NULL) { + ax25->sk->state = TCP_ESTABLISHED; + /* For WAIT_SABM connections we will produce an accept ready socket here */ + ax25->sk->state_change(ax25->sk); + } + ax25_kick(ax25); + } + break; + + case AX25_DM: + if (peer != NULL) { + ax25_set_cond(peer, AX25_COND_STATE_CHANGE); + ax25_disconnect(peer, 0); + ax25_tx_response(peer, AX25_DM, pkt->pf); + } else if (ax25->sk) + ax25_close_socket(ax25->sk, ECONNREFUSED); + ax25_disconnect(ax25, ECONNREFUSED); + break; + + default: + break; + } + + return 0; +} + +/* + * State machine for state 2, Awaiting Release State. + * The handling of the timer(s) is in file ax25_timer.c + * Handling of state 0 and connection release is in ax25.c. + */ +static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + ax25_cb *peer = ax25->peer; + int queued = 0; + + switch (pkt->frametype) { + case AX25_SABM: + case AX25_SABME: + if (pkt->frametype == AX25_SABM) { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + } else { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW); + } + queued = ax25_reset_link(ax25, skb); + break; + + case AX25_DISC: + if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25_disconnect(ax25, 0); + ax25_tx_response(ax25, AX25_DM, pkt->pf); + break; + + case AX25_DM: + case AX25_UA: + if (pkt->pf) { + if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_disconnect(ax25, 0); + } + break; + + case AX25_I: + case AX25_REJ: + case AX25_RNR: + case AX25_RR: + if (pkt->pf && pkt->cmdrsp == AX25_COMMAND) + ax25_tx_response(ax25, AX25_DM, AX25_POLLON); + break; + + default: + break; + } + return queued; +} + +/* + * State machine for state 3, Connected State. + * The handling of the timer(s) is in file ax25_timer.c + * Handling of state 0 and connection release is in ax25.c. + */ +static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + int queued = 0; + ax25_cb *peer = ax25->peer; + + switch (pkt->frametype) { + case AX25_SABM: + case AX25_SABME: + if (pkt->frametype == AX25_SABM) { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + } else { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW); + } + queued = ax25_reset_link(ax25, skb); + break; + + case AX25_DISC: + if (peer) + ax25_set_cond(peer, AX25_COND_RELEASE); + else if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25_disconnect(ax25, 0); + ax25_tx_response(ax25, AX25_UA, pkt->pf); + break; + + case AX25_DM: + if (peer) + ax25_set_cond(peer, AX25_COND_RELEASE); + else if (ax25->sk) + ax25_close_socket(ax25->sk, ECONNRESET); + ax25_disconnect(ax25, ECONNRESET); + break; + + case AX25_RR: + case AX25_RNR: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + + ax25_check_iframes_acked(ax25, pkt->nr); + + if (pkt->frametype == AX25_RR) + ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY); + else + ax25_set_cond(ax25, AX25_COND_PEER_RX_BUSY); + + if (pkt->cmdrsp == AX25_COMMAND && pkt->pf) + ax25_enquiry_response(ax25); + else + ax25_kick(ax25); + break; + + case AX25_REJ: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + + ax25_check_iframes_acked(ax25, pkt->nr); + ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY); + ax25_requeue_frames(ax25); + ax25_kick(ax25); + AX25_PTR(ax25->device)->rx_rejects++; + break; + + case AX25_I: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + + ax25_check_iframes_acked(ax25, pkt->nr); + + if (pkt->ns == ax25->vr) { + if ((queued = ax25_rx_iframe(ax25, skb)) != 0) { + ax25_reseq_update(ax25, skb, pkt->ns); + ax25->vr = (ax25->vr+1) & ax25->seqmask; + ax25_reseq_out(ax25); + ax25_clr_cond(ax25, AX25_COND_REJECT); + } + + if (pkt->pf) { + ax25_enquiry_response(ax25); + } else if (ax25->ack_timer == 0) { + ax25->ack_timer = ax25->t2; + ax25_set_cond(ax25, AX25_COND_ACK_PENDING); + } + } else { + /* frame is not in sequence */ + queued = ax25_reseq_in(ax25, skb, pkt->ns, pkt->pf); + if (ax25->condition & AX25_COND_REJECT) { + if (pkt->pf) { + ax25_enquiry_response(ax25); + } else if (ax25->ack_timer == 0) { + ax25->ack_timer = ax25->t2; + ax25_set_cond(ax25, AX25_COND_ACK_PENDING); + } + } else { + ax25->ack_timer = 0; + ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); + ax25_set_cond(ax25, AX25_COND_REJECT); + ax25_tx_response(ax25, AX25_REJ, pkt->pf); + } + } + ax25_kick(ax25); + break; + + case AX25_FRMR: + case AX25_ILLEGAL: + ax25_nr_error_recovery(ax25); + break; + + default: + break; + } + + return queued; +} + +/* + * State machine for state 4, Timer Recovery State. + * The handling of the timer(s) is in file ax25_timer.c + * Handling of state 0 and connection release is in ax25.c. + */ +static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + int queued = 0; + ax25_cb *peer = ax25->peer; + + switch (pkt->frametype) { + case AX25_SABM: + case AX25_SABME: + if (pkt->frametype == AX25_SABM) { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + } else { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW); + } + queued = ax25_reset_link(ax25, skb); + break; + + case AX25_DISC: + if (peer) + ax25_set_cond(peer, AX25_COND_RELEASE); + else if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25_disconnect(ax25, 0); + ax25_tx_response(ax25, AX25_UA, pkt->pf); + break; + + case AX25_DM: + if (peer) + ax25_set_cond(peer, AX25_COND_RELEASE); + else if (ax25->sk) + ax25_close_socket(ax25->sk, ECONNRESET); + ax25_disconnect(ax25, ECONNRESET); + break; + + case AX25_RR: + case AX25_RNR: + case AX25_REJ: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + ax25_frames_acked(ax25, pkt->nr); + + if (pkt->frametype == AX25_RR) + ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY); + else if (pkt->frametype == AX25_REJ) { + ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY); + AX25_PTR(ax25->device)->rx_rejects++; + } else + ax25_set_cond(ax25, AX25_COND_PEER_RX_BUSY); + + if (pkt->pf) { + if (pkt->cmdrsp == AX25_RESPONSE) { + ax25->n2count = 0; + ax25->wrt_timer = 0; + ax25->state = AX25_STATE_3; + if (ax25->vs == ax25->va) + ax25->wrt_timer = ax25->t3; + else { + ax25_requeue_frames(ax25); + } + ax25_kick(ax25); + } else + ax25_enquiry_response(ax25); + } + break; + + case AX25_I: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + ax25_frames_acked(ax25, pkt->nr); + if (pkt->ns == ax25->vr) { + /* frame is in sequence */ + if ((queued = ax25_rx_iframe(ax25, skb)) != 0) { + ax25_reseq_update(ax25, skb, pkt->ns); + ax25->vr = (ax25->vr+1) & ax25->seqmask; + ax25_reseq_out(ax25); + ax25_clr_cond(ax25, AX25_COND_REJECT); + } + + if (pkt->pf) { + ax25_enquiry_response(ax25); + } else if (ax25->ack_timer == 0) { + ax25->ack_timer = ax25->t2; + ax25_set_cond(ax25, AX25_COND_ACK_PENDING); + } + } else { + /* frame is not in sequence */ + queued = ax25_reseq_in(ax25, skb, pkt->ns, pkt->pf); + if (ax25->condition & AX25_COND_REJECT) { + if (pkt->pf) + ax25_enquiry_response(ax25); + else if (ax25->ack_timer == 0) { + ax25->ack_timer = ax25->t2; + ax25_set_cond(ax25, AX25_COND_ACK_PENDING); + } + } else { + ax25->ack_timer = 0; + ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); + ax25_set_cond(ax25, AX25_COND_REJECT); + ax25_tx_response(ax25, AX25_REJ, pkt->pf); + } + } + break; + + case AX25_FRMR: + case AX25_ILLEGAL: + ax25_nr_error_recovery(ax25); + break; + + default: + break; + } + + return queued; +} + +static int ax25_state_nop(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + printk(KERN_DEBUG "ax25_state_nop()\n"); + return 0; +} + +ax25_statefunc_t ax25_lapb_table[] = +{ + ax25_state0_machine, + ax25_state1_machine, + ax25_state2_machine, + ax25_state3_machine, + ax25_state4_machine, + ax25_state_nop +}; Index: linux/net/ax25/ax25_lapb.h diff -u /dev/null linux/net/ax25/ax25_lapb.h:1.1.4.1 --- /dev/null Wed Jul 7 01:02:32 1999 +++ linux/net/ax25/ax25_lapb.h Sun Jul 4 13:03:45 1999 @@ -0,0 +1,11 @@ +#ifndef _AX25_LAPB_H +#define _AX25_LAPB_H + + +/* + * state machine tables for lapb and dama slave + */ +extern ax25_statefunc_t ax25_lapb_table[]; +extern ax25_statefunc_t ax25_dama_table[]; + +#endif Index: linux/net/ax25/ax25_netlink.c diff -u /dev/null linux/net/ax25/ax25_netlink.c:1.1.4.1 --- /dev/null Wed Jul 7 01:02:32 1999 +++ linux/net/ax25/ax25_netlink.c Sun Jul 4 13:03:45 1999 @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include + +#include "ax25_netlink.h" +#include "ax25_route.h" + +static struct sock *axrtnl; + +static void ax25_netlink_rcv(struct sock *sk, int len) +{ + struct ax25_nlmsg *nlmsg; + struct sk_buff *skb; + struct device *dev; + + while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { + struct ax25_rtmsg *rtmsg; + + if (skb->len < sizeof(struct ax25_nlmsg)) { + kfree_skb(skb); + break; + } + nlmsg = (struct ax25_nlmsg *)skb->data; + + switch (nlmsg->msg_type) { + case AX25_MSG_SETRT: + if ((dev = dev_get(nlmsg->msg.pathmsg.port_name)) != NULL + && nlmsg->msg.pathmsg.path.dcount <= AX25_MAX_DIGIS) + ax25_add_route(&nlmsg->msg.pathmsg.path, dev); + break; + + case AX25_MSG_DELRT: + if ((dev = dev_get(nlmsg->msg.pathmsg.port_name)) != NULL) + ax25_del_route(&nlmsg->msg.pathmsg.path.addr); + break; + + case AX25_MSG_OPTRT: + if ((dev = dev_get(nlmsg->msg.pathmsg.port_name)) != NULL + && nlmsg->msg.pathmsg.path.dcount <= AX25_MAX_DIGIS) + { + ax25_add_route(&nlmsg->msg.pathmsg.path, dev); + ax25_ipopt_route(&nlmsg->msg.pathmsg.path.addr, nlmsg->msg.pathmsg.mode); + } + } + kfree_skb(skb); + } +} + +void ax25_nlpost_route(ax25_pktinfo *pkt, struct device *dev) +{ + struct ax25_nlmsg *nlmsg; + struct sk_buff *skb; + + skb = alloc_skb(sizeof(struct ax25_nlmsg), GFP_ATOMIC); + if (!skb) + return; + nlmsg = (struct ax25_nlmsg *)skb_put(skb, sizeof(struct ax25_nlmsg)); + + nlmsg->msg_type = AX25_MSG_RTINFO; + strncpy(nlmsg->msg.rtmsg.port_name, dev->name, sizeof(nlmsg->msg.rtmsg.port_name)); + nlmsg->msg.rtmsg.addr = pkt->addr; + netlink_broadcast(axrtnl, skb, 0, ~0, GFP_KERNEL); +} + +void ax25_nlpost_armsg(unsigned int ip_addr, ax25_address *ax_addr, struct device *dev) +{ + struct ax25_nlmsg *nlmsg; + struct sk_buff *skb; + + skb = alloc_skb(sizeof(struct ax25_nlmsg), GFP_ATOMIC); + if (!skb) + return; + nlmsg = (struct ax25_nlmsg *)skb_put(skb, sizeof(struct ax25_nlmsg)); + + nlmsg->msg_type = AX25_MSG_ARINFO; + strncpy(nlmsg->msg.armsg.port_name, dev->name, sizeof(nlmsg->msg.armsg.port_name)); + nlmsg->msg.armsg.ip_addr = ip_addr; + nlmsg->msg.armsg.ax_addr = *ax_addr; + + netlink_broadcast(axrtnl, skb, 0, ~0, GFP_KERNEL); +} + +void ax25_netlink_init(void) +{ + axrtnl = netlink_kernel_create(NETLINK_AX25, ax25_netlink_rcv); +} + +void ax25_netlink_cleanup(void) +{ + sock_release(axrtnl->socket); +} + Index: linux/net/ax25/ax25_netlink.h diff -u /dev/null linux/net/ax25/ax25_netlink.h:1.1.4.1 --- /dev/null Wed Jul 7 01:02:32 1999 +++ linux/net/ax25/ax25_netlink.h Sun Jul 4 13:03:45 1999 @@ -0,0 +1,10 @@ +#ifndef _AX25_NETLINK_H +#define _AX25_NETLINK_H + +extern void ax25_netlink_init(void); +extern void ax25_netlink_cleanup(void); + +extern void ax25_nlpost_route(ax25_pktinfo*, struct device*); +extern void ax25_nlpost_armsg(unsigned int, ax25_address*, struct device*); + +#endif Index: linux/net/ax25/ax25_out.c diff -u linux/net/ax25/ax25_out.c:1.1.2.1 linux/net/ax25/ax25_out.c:1.1.2.1.2.1 --- linux/net/ax25/ax25_out.c:1.1.2.1 Sun Jul 4 10:18:10 1999 +++ linux/net/ax25/ax25_out.c Sun Jul 4 11:32:32 1999 @@ -1,7 +1,7 @@ /* - * AX.25 release 037 + * AX.25 release 035 * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * This code REQUIRES 1.2.1 or higher/ NET3.029 * * This module: * This module is free software; you can redistribute it and/or @@ -16,115 +16,70 @@ * easy to break; * * History - * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. - * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. - * Jonathan(G4KLX) Only poll when window is full. - * AX.25 030 Jonathan(G4KLX) Added fragmentation to ax25_output. - * Added support for extended AX.25. - * AX.25 031 Joerg(DL1BKE) Added DAMA support - * Joerg(DL1BKE) Modified fragmenter to fragment vanilla - * AX.25 I-Frames. Added PACLEN parameter. - * Joerg(DL1BKE) Fixed a problem with buffer allocation - * for fragments. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - * Joerg(DL1BKE) Fixed DAMA Slave mode: will work - * on non-DAMA interfaces like AX25L2V2 - * again (this behaviour is _required_). - * Joerg(DL1BKE) ax25_check_iframes_acked() returns a - * value now (for DAMA n2count handling) + * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. + * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. + * Jonathan(G4KLX) Only poll when window is full. + * AX.25 030 Jonathan(G4KLX) Added fragmentation to ax25_output. + * Added support for extended AX.25. + * AX.25 031 Joerg(DL1BKE) Added DAMA support + * Joerg(DL1BKE) Modified fragmenter to fragment vanilla + * AX.25 I-Frames. Added PACLEN parameter. + * Joerg(DL1BKE) Fixed a problem with buffer allocation + * for fragments. + * Matthias(DG2FEF) Support for VJ TCP Compression via AX.25 */ #include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include +#include +#include #include -#include -#include -#include -#include -#include -ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev) +#include "ax25_core.h" +#include "ax25_route.h" +#include "ax25_vj.h" +#include "ax25_ddi.h" +#include "ax25_subr.h" + +/* ---------------------------------------------------------------------*/ +/* + * send an iframe on a connection, maybe establish the connection first. + */ +ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_addr_t *addr, struct device *dev) { - ax25_dev *ax25_dev; ax25_cb *ax25; - /* - * Take the default packet length for the device if zero is - * specified. - */ - if (paclen == 0) { - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return NULL; + if (paclen == 0) + paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN); - paclen = ax25_dev->values[AX25_VALUES_PACLEN]; - } - /* * Look for an existing connection. */ - if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) { + if ((ax25 = ax25_find_cb(addr, dev)) != NULL) { + /* reuse a just disconnected control block */ + if (ax25->state == AX25_STATE_0 || ax25->state == AX25_STATE_2) { + if (ax25->slcomp) { + axhc_free(ax25->slcomp); + ax25->slcomp = NULL; + } + ax25->slcomp_enable = ax25_rt_mode_get(&addr->dest) == 'C'; + ax25_establish_data_link(ax25); + } ax25_output(ax25, paclen, skb); return ax25; /* It already existed */ } - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return NULL; - if ((ax25 = ax25_create_cb()) == NULL) return NULL; - ax25_fillin_cb(ax25, ax25_dev); + ax25_fillin_cb(ax25, dev); - ax25->source_addr = *src; - ax25->dest_addr = *dest; + ax25->addr = *addr; + ax25->slcomp_enable = ax25_rt_mode_get(&addr->dest) == 'C'; - if (digi != NULL) { - if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { - ax25_free_cb(ax25); - return NULL; - } - *ax25->digipeat = *digi; - } - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_establish_data_link(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25_dev->dama.slave) - ax25_ds_establish_data_link(ax25); - else - ax25_std_establish_data_link(ax25); - break; -#endif - } - - ax25_insert_socket(ax25); - - ax25->state = AX25_STATE_1; - - ax25_start_heartbeat(ax25); - + ax25_establish_data_link(ax25); + ax25_insert_cb(ax25); ax25_output(ax25, paclen, skb); - return ax25; /* We had to create it */ } @@ -139,7 +94,6 @@ struct sk_buff *skbn; unsigned char *p; int frontlen, len, fragno, ka9qfrag, first = 1; - long flags; if ((skb->len - 1) > paclen) { if (*skb->data == AX25_P_TEXT) { @@ -156,11 +110,7 @@ frontlen = skb_headroom(skb); /* Address space + CTRL */ while (skb->len > 0) { - save_flags(flags); - cli(); - if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) { - restore_flags(flags); printk(KERN_CRIT "AX.25: ax25_output - out of memory\n"); return; } @@ -168,8 +118,6 @@ if (skb->sk != NULL) skb_set_owner_w(skbn, skb->sk); - restore_flags(flags); - len = (paclen > skb->len) ? skb->len : paclen; if (ka9qfrag == 1) { @@ -200,216 +148,7 @@ } else { skb_queue_tail(&ax25->write_queue, skb); /* Throw it on the queue */ } - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_kick(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - /* - * A DAMA slave is _required_ to work as normal AX.25L2V2 - * if no DAMA master is available. - */ - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) ax25_kick(ax25); - break; -#endif - } -} - -/* - * This procedure is passed a buffer descriptor for an iframe. It builds - * the rest of the control part of the frame and then writes it out. - */ -static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit) -{ - unsigned char *frame; - - if (skb == NULL) - return; - - skb->nh.raw = skb->data; - - if (ax25->modulus == AX25_MODULUS) { - frame = skb_push(skb, 1); - - *frame = AX25_I; - *frame |= (poll_bit) ? AX25_PF : 0; - *frame |= (ax25->vr << 5); - *frame |= (ax25->vs << 1); - } else { - frame = skb_push(skb, 2); - - frame[0] = AX25_I; - frame[0] |= (ax25->vs << 1); - frame[1] = (poll_bit) ? AX25_EPF : 0; - frame[1] |= (ax25->vr << 1); - } - - ax25_start_idletimer(ax25); - ax25_transmit_buffer(ax25, skb, AX25_COMMAND); -} - -void ax25_kick(ax25_cb *ax25) -{ - struct sk_buff *skb, *skbn; - int last = 1; - unsigned short start, end, next; - - if (ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4) - return; - - if (ax25->condition & AX25_COND_PEER_RX_BUSY) - return; - - if (skb_peek(&ax25->write_queue) == NULL) - return; - - start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs; - end = (ax25->va + ax25->window) % ax25->modulus; - - if (start == end) - return; - - ax25->vs = start; - - /* - * Transmit data until either we're out of data to send or - * the window is full. Send a poll on the final I frame if - * the window is filled. - */ - - /* - * Dequeue the frame and copy it. - */ - skb = skb_dequeue(&ax25->write_queue); - - do { - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { - skb_queue_head(&ax25->write_queue, skb); - break; - } - - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); - - next = (ax25->vs + 1) % ax25->modulus; - last = (next == end); - - /* - * Transmit the frame copy. - * bke 960114: do not set the Poll bit on the last frame - * in DAMA mode. - */ - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25_send_iframe(ax25, skbn, AX25_POLLOFF); - break; -#endif - } - - ax25->vs = next; - - /* - * Requeue the original data frame. - */ - skb_queue_tail(&ax25->ack_queue, skb); - - } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL); - - ax25->condition &= ~AX25_COND_ACK_PENDING; - - if (!ax25_t1timer_running(ax25)) { - ax25_stop_t3timer(ax25); - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - } -} - -void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) -{ - struct sk_buff *skbn; - unsigned char *ptr; - int headroom; - - if (ax25->ax25_dev == NULL) { - ax25_disconnect(ax25, ENETUNREACH); - return; - } - - headroom = ax25_addr_size(ax25->digipeat); - - if (skb_headroom(skb) < headroom) { - if ((skbn = skb_realloc_headroom(skb, headroom)) == NULL) { - printk(KERN_CRIT "AX.25: ax25_transmit_buffer - out of memory\n"); - kfree_skb(skb); - return; - } - - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); - - kfree_skb(skb); - skb = skbn; - } - - ptr = skb_push(skb, headroom); - - ax25_addr_build(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus); - - skb->dev = ax25->ax25_dev->dev; - - ax25_queue_xmit(skb); -} - -/* - * A small shim to dev_queue_xmit to add the KISS control byte, and do - * any packet forwarding in operation. - */ -void ax25_queue_xmit(struct sk_buff *skb) -{ - unsigned char *ptr; - - if (call_out_firewall(PF_AX25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) { - kfree_skb(skb); - return; - } - - skb->protocol = htons(ETH_P_AX25); - skb->dev = ax25_fwd_dev(skb->dev); - - ptr = skb_push(skb, 1); - *ptr = 0x00; /* KISS */ - - dev_queue_xmit(skb); -} - -int ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr) -{ - if (ax25->vs == nr) { - ax25_frames_acked(ax25, nr); - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - return 1; - } else { - if (ax25->va != nr) { - ax25_frames_acked(ax25, nr); - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - return 1; - } - } - return 0; + ax25_kick(ax25); } -#endif Index: linux/net/ax25/ax25_route.c diff -u linux/net/ax25/ax25_route.c:1.1.2.1 linux/net/ax25/ax25_route.c:1.1.2.1.2.1 --- linux/net/ax25/ax25_route.c:1.1.2.1 Sun Jul 4 10:18:10 1999 +++ linux/net/ax25/ax25_route.c Sun Jul 4 11:32:32 1999 @@ -1,7 +1,7 @@ /* - * AX.25 release 037 + * AX.25 release 035 * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * This code REQUIRES 1.2.1 or higher/ NET3.029 * * This module: * This module is free software; you can redistribute it and/or @@ -35,247 +35,219 @@ * "SIOCAX25OPTRT" to set IP mode and a 'permanent' flag * on routes. * AX.25 033 Jonathan(G4KLX) Remove auto-router. - * Joerg(DL1BKE) Moved BPQ Ethernet driver to separate device. + * Joerg(DL1BKE) Moved BPQ Ethernet driver to seperate device. * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. * Jonathan(G4KLX) Support for packet forwarding. */ #include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include +#include +#include -static ax25_route *ax25_route_list = NULL; +#include "af_ax25.h" +#include "ax25_core.h" +#include "ax25_route.h" -static ax25_route *ax25_find_route(ax25_address *, struct device *); +static struct ax25_route *ax25_route = NULL; /* - * small macro to drop non-digipeated digipeaters and reverse path + * delete all routes on a given device */ -static inline void ax25_route_invert(ax25_digi *in, ax25_digi *out) +void ax25_rt_device_down(struct device *dev) { - int k; + struct ax25_route *this, *last; - for (k = 0; k < in->ndigi; k++) - if (!in->repeated[k]) - break; + this = ax25_route; + last = NULL; - in->ndigi = k; + while (this) { + if (this->dev != dev) { + last = this; + this = this->next; + continue; + } + if (!last) { + ax25_route = this->next; + kfree(this); + this = ax25_route; + continue; + } + last->next = this->next; + kfree(this); + this = last->next; + } +} + +int ax25_add_route(ax25_path_t *path, struct device *dev) +{ + struct ax25_route *this; + + for (this = ax25_route; this; this = this->next) { + if (ax25cmp(&this->path.addr, &path->addr) == 0) { + this->path = *path; + this->dev = dev; + return 0; + } + } - ax25_digi_invert(in, out); + if ((this = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_KERNEL)) == NULL) + return -ENOMEM; + this->path = *path; + this->dev = dev; + this->ip_mode = ' '; + this->next = ax25_route; + ax25_route = this; + return 0; } -void ax25_rt_device_down(struct device *dev) +int ax25_del_route(ax25_address *addr) { - ax25_route *s, *t, *ax25_rt = ax25_route_list; - - while (ax25_rt != NULL) { - s = ax25_rt; - ax25_rt = ax25_rt->next; + struct ax25_route *this, *last; - if (s->dev == dev) { - if (ax25_route_list == s) { - ax25_route_list = s->next; - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); - } else { - for (t = ax25_route_list; t != NULL; t = t->next) { - if (t->next == s) { - t->next = s->next; - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); - break; - } - } - } + this = ax25_route; + last = NULL; + while (this != NULL) { + if (ax25cmp(&this->path.addr, addr) != 0) { + last = this; + this = this->next; + continue; } + if (!last) { + ax25_route = this->next; + kfree(this); + return 0; + } + last->next = this->next; + kfree(this); + return 0; } + return -EINVAL; } +int ax25_ipopt_route(ax25_address *addr, unsigned char opt) +{ + struct ax25_route *this; + int err = -EINVAL; + + for (this = ax25_route; this; this = this->next) { + if (ax25cmp(&this->path.addr, addr) == 0) { + switch(opt) { + case ' ': + case 'D': + case 'V': + case 'C': + this->ip_mode = opt; + err = 0; + break; + } + } + } + return err; +} + int ax25_rt_ioctl(unsigned int cmd, void *arg) { - unsigned long flags; - ax25_route *s, *t, *ax25_rt; struct ax25_routes_struct route; struct ax25_route_opt_struct rt_option; - ax25_dev *ax25_dev; + struct device *dev; + ax25_path_t ax25_path; + int err = -EINVAL; int i; switch (cmd) { - case SIOCADDRT: - if (copy_from_user(&route, arg, sizeof(route))) - return -EFAULT; - if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) - return -EINVAL; - if (route.digi_count > AX25_MAX_DIGIS) - return -EINVAL; - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == ax25_dev->dev) { - if (ax25_rt->digipeat != NULL) { - kfree(ax25_rt->digipeat); - ax25_rt->digipeat = NULL; - } - if (route.digi_count != 0) { - if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) - return -ENOMEM; - ax25_rt->digipeat->lastrepeat = -1; - ax25_rt->digipeat->ndigi = route.digi_count; - for (i = 0; i < route.digi_count; i++) { - ax25_rt->digipeat->repeated[i] = 0; - ax25_rt->digipeat->calls[i] = route.digi_addr[i]; - } - } - return 0; - } - } - if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL) - return -ENOMEM; - ax25_rt->callsign = route.dest_addr; - ax25_rt->dev = ax25_dev->dev; - ax25_rt->digipeat = NULL; - ax25_rt->ip_mode = ' '; - if (route.digi_count != 0) { - if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { - kfree(ax25_rt); - return -ENOMEM; - } - ax25_rt->digipeat->lastrepeat = -1; - ax25_rt->digipeat->ndigi = route.digi_count; - for (i = 0; i < route.digi_count; i++) { - ax25_rt->digipeat->repeated[i] = 0; - ax25_rt->digipeat->calls[i] = route.digi_addr[i]; - } - } - save_flags(flags); cli(); - ax25_rt->next = ax25_route_list; - ax25_route_list = ax25_rt; - restore_flags(flags); - break; - - case SIOCDELRT: - if (copy_from_user(&route, arg, sizeof(route))) - return -EFAULT; - if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) - return -EINVAL; - ax25_rt = ax25_route_list; - while (ax25_rt != NULL) { - s = ax25_rt; - ax25_rt = ax25_rt->next; - if (s->dev == ax25_dev->dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) { - if (ax25_route_list == s) { - ax25_route_list = s->next; - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); - } else { - for (t = ax25_route_list; t != NULL; t = t->next) { - if (t->next == s) { - t->next = s->next; - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); - break; - } - } - } - } - } + case SIOCADDRT: + /* do some sanity checks */ + if (copy_from_user(&route, arg, sizeof(route))) { + err = -EFAULT; + break; + } + if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL) break; + if (route.digi_count > AX25_MAX_DIGIS) + break; - case SIOCAX25OPTRT: - if (copy_from_user(&rt_option, arg, sizeof(rt_option))) - return -EFAULT; - if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL) - return -EINVAL; - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (ax25_rt->dev == ax25_dev->dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) { - switch (rt_option.cmd) { - case AX25_SET_RT_IPMODE: - switch (rt_option.arg) { - case ' ': - case 'D': - case 'V': - ax25_rt->ip_mode = rt_option.arg; - break; - default: - return -EINVAL; - } - break; - default: - return -EINVAL; - } - } - } + ax25_path.addr = route.dest_addr; + for (i = 0; i < route.digi_count; i++) + ax25_path.digipeater[i] = route.digi_addr[i]; + ax25_path.dcount = route.digi_count; + err = ax25_add_route(&ax25_path, dev); + break; + + case SIOCDELRT: + /* sanity checks */ + if (copy_from_user(&route, arg, sizeof(route))) { + err = -EFAULT; + break; + } + if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL) break; - default: - return -EINVAL; - } + err = ax25_del_route(&route.dest_addr); + break; - return 0; + case SIOCAX25OPTRT: + /* sanity checks */ + if (copy_from_user(&rt_option, arg, sizeof(rt_option))) { + err = -EFAULT; + break; + } + if ((dev = ax25rtr_get_dev(&rt_option.port_addr)) == NULL) + break; + + switch (rt_option.cmd) { + case AX25_SET_RT_IPMODE: + err = ax25_ipopt_route(&rt_option.dest_addr, rt_option.arg); + break; + } + } + return err; } int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy) { - ax25_route *ax25_rt; + struct ax25_route *ax25_rt; int len = 0; off_t pos = 0; off_t begin = 0; char *callsign; int i; - cli(); + start_bh_atomic(); len += sprintf(buffer, "callsign dev mode digipeaters\n"); - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0) + for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) { + if (ax25cmp(&ax25_rt->path.addr, &null_ax25_address) == 0) callsign = "default"; else - callsign = ax2asc(&ax25_rt->callsign); + callsign = ax2asc(&ax25_rt->path.addr); len += sprintf(buffer + len, "%-9s %-4s", - callsign, - ax25_rt->dev ? ax25_rt->dev->name : "???"); - + callsign, + ax25_rt->dev ? ax25_rt->dev->name : "???"); + switch (ax25_rt->ip_mode) { - case 'V': - len += sprintf(buffer + len, " vc"); - break; - case 'D': - len += sprintf(buffer + len, " dg"); - break; - default: - len += sprintf(buffer + len, " *"); - break; + case 'V': + len += sprintf(buffer + len, " vc"); + break; + case 'D': + len += sprintf(buffer + len, " dg"); + break; + case 'C': + len += sprintf(buffer + len, " vj"); + break; + default: + len += sprintf(buffer + len, " *"); + break; } - - if (ax25_rt->digipeat != NULL) - for (i = 0; i < ax25_rt->digipeat->ndigi; i++) - len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->digipeat->calls[i])); - + + for (i = 0; i < ax25_rt->path.dcount; i++) + len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->path.digipeater[i])); + len += sprintf(buffer + len, "\n"); - + pos = begin + len; if (pos < offset) { @@ -287,7 +259,7 @@ break; } - sti(); + end_bh_atomic(); *start = buffer + (offset - begin); len -= (offset - begin); @@ -300,28 +272,17 @@ /* * Find AX.25 route */ -static ax25_route *ax25_find_route(ax25_address *addr, struct device *dev) +struct ax25_route *ax25_find_route(ax25_address *addr) { - ax25_route *ax25_spe_rt = NULL; - ax25_route *ax25_def_rt = NULL; - ax25_route *ax25_rt; - - /* - * Bind to the physical interface we heard them on, or the default - * route if none is found; - */ - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (dev == NULL) { - if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL) - ax25_spe_rt = ax25_rt; - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL) - ax25_def_rt = ax25_rt; - } else { - if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev) - ax25_spe_rt = ax25_rt; - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev) - ax25_def_rt = ax25_rt; - } + struct ax25_route *ax25_spe_rt = NULL; + struct ax25_route *ax25_def_rt = NULL; + struct ax25_route *ax25_rt; + + for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) { + if (ax25cmp(&ax25_rt->path.addr, addr) == 0) + ax25_spe_rt = ax25_rt; + if (ax25cmp(&ax25_rt->path.addr, &null_ax25_address) == 0) + ax25_def_rt = ax25_rt; } if (ax25_spe_rt != NULL) @@ -331,128 +292,147 @@ } /* - * Adjust path: If you specify a default route and want to connect - * a target on the digipeater path but w/o having a special route - * set before, the path has to be truncated from your target on. + * MW: This is the core of the digipeating stuff. For a given + * src/dest it finds the appropriate device and digipeaterpath + * to route to. */ -static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat) +struct device *ax25_rt_set_addr(ax25_addr_t *out, ax25_addr_t *in, struct device *dev, struct device *out_dev) { - int k; + struct ax25_route *ax25rt; + ax25_address *next_dest; + ax25_address *dptr; + int more_digis; + + /* + * find out where to go next. we route the packet either + * to the next digi behind us or to the destination. We + * NEVER route to the destination if there are digipeaters + * left. + */ + more_digis = in->dcount - (in->lastrepeat+2); + if (more_digis > 0) + next_dest = &in->digipeater[in->lastrepeat+2]; + else + next_dest = &in->dest; - for (k = 0; k < digipeat->ndigi; k++) { - if (ax25cmp(addr, &digipeat->calls[k]) == 0) - break; - } + /* + * check for a route. + */ + if ((ax25rt = ax25_find_route(next_dest)) != NULL) + out_dev = ax25rt->dev; + + /* + * set up the digipeater path. + * for now we just copy the path of the incoming SABM + * up to the digipeater before us, if any. + */ + out->dest = in->dest; + out->src = in->src; + + if (in->lastrepeat >= 0) + memcpy(out->digipeater, in->digipeater, sizeof(ax25_address) * (in->lastrepeat+1)); + + /* + * then we fill in the callsign of the device the frame + * came in. + */ + out->lastrepeat = in->lastrepeat+1; + out->dcount = out->lastrepeat+1; + dptr = &out->digipeater[out->lastrepeat]; + *dptr++ = *((ax25_address *)dev->dev_addr); - digipeat->ndigi = k; + /* + * insert the route to next_dest, if any. + */ + if (ax25rt != NULL && ax25rt->path.dcount != 0) { + memcpy(dptr, ax25rt->path.digipeater, sizeof(ax25_address) * ax25rt->path.dcount); + out->dcount += ax25rt->path.dcount; + dptr += ax25rt->path.dcount; + } + + /* + * append next_dest if we route to a waypoint + */ + while (more_digis-- > 0 && out->dcount <= AX25_MAX_DIGIS) { + *dptr++ = *next_dest++; + out->dcount++; + } + return out_dev; } - /* * Find which interface to use. */ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) { - ax25_route *ax25_rt; + struct ax25_route *ax25_rt; ax25_address *call; - if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL) + if ((ax25_rt = ax25_find_route(addr)) == NULL) return -EHOSTUNREACH; - if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) - return -EHOSTUNREACH; - if ((call = ax25_findbyuid(current->euid)) == NULL) { if (ax25_uid_policy && !suser()) return -EPERM; - call = (ax25_address *)ax25->ax25_dev->dev->dev_addr; + call = (ax25_address *)ax25->device->dev_addr; } - ax25->source_addr = *call; + ax25->device = ax25_rt->dev; + ax25->addr.src = *call; - if (ax25_rt->digipeat != NULL) { - if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) - return -ENOMEM; - *ax25->digipeat = *ax25_rt->digipeat; - ax25_adjust_path(addr, ax25->digipeat); - } +/* ax25_insert_cb(ax25); */ + ax25->sk->zapped = 0; - if (ax25->sk != NULL) - ax25->sk->zapped = 0; - return 0; } /* - * dl1bke 960117: build digipeater path - * dl1bke 960301: use the default route if it exists + * Find the device to use */ -ax25_route *ax25_rt_find_route(ax25_address *addr, struct device *dev) +int ax25_rt_fillin_dev(ax25_cb *ax25, ax25_address *addr) { - static ax25_route route; - ax25_route *ax25_rt; + struct ax25_route *ax25_rt; - if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) { - route.next = NULL; - route.callsign = *addr; - route.dev = dev; - route.digipeat = NULL; - route.ip_mode = ' '; - return &route; - } + if ((ax25_rt = ax25_find_route(addr)) == NULL) + return -EHOSTUNREACH; - return ax25_rt; +/* ax25_remove_cb(ax25); */ + ax25_fillin_cb(ax25, ax25_rt->dev); +/* ax25_insert_cb(ax25); */ + + return 0; } -struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi) +/* + * Return the IP mode of a given callsign/device pair. + */ +char ax25_rt_mode_get(ax25_address *callsign) { - struct sk_buff *skbn; - unsigned char *bp; - int len; - - len = digi->ndigi * AX25_ADDR_LEN; - - if (skb_headroom(skb) < len) { - if ((skbn = skb_realloc_headroom(skb, len)) == NULL) { - printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n"); - return NULL; - } + struct ax25_route *ax25_rt; - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); - - kfree_skb(skb); - - skb = skbn; - } - - bp = skb_push(skb, len); + for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) + if (ax25cmp(&ax25_rt->path.addr, callsign) == 0) + return ax25_rt->ip_mode; + return ' '; +} - ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS); - return skb; -} #ifdef MODULE /* - * Free all memory associated with routing structures. + * Free all memory associated with routing and device structures. */ void ax25_rt_free(void) { - ax25_route *s, *ax25_rt = ax25_route_list; + struct ax25_route *s, *ax25_rt = ax25_route; while (ax25_rt != NULL) { s = ax25_rt; ax25_rt = ax25_rt->next; - - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); } } #endif -#endif Index: linux/net/ax25/ax25_route.h diff -u /dev/null linux/net/ax25/ax25_route.h:1.1.4.1 --- /dev/null Wed Jul 7 01:02:33 1999 +++ linux/net/ax25/ax25_route.h Sun Jul 4 13:03:45 1999 @@ -0,0 +1,29 @@ +#ifndef _AX25_ROUTE_H +#define _AX25_ROUTE_H + +struct ax25_route { + struct ax25_route *next; + ax25_path_t path; + struct device *dev; + char ip_mode; +}; + +extern void ax25_rt_device_down(struct device*); +extern int ax25_rt_ioctl(unsigned int, void*); +extern int ax25_rt_get_info(char*, char**, off_t, int, int); +extern struct device* ax25_rt_set_addr(ax25_addr_t*, ax25_addr_t*, struct device*, struct device*); +extern int ax25_rt_autobind(ax25_cb*, ax25_address*); +extern int ax25_rt_fillin_dev(ax25_cb*, ax25_address*); +extern char ax25_rt_mode_get(ax25_address*); +extern struct ax25_route* ax25_find_route(ax25_address*); + +extern int ax25_add_route(ax25_path_t*, struct device*); +extern int ax25_del_route(ax25_address*); +extern int ax25_ipopt_route(ax25_address*, unsigned char); + +#ifdef MODULE +void ax25_rt_free(void); +#endif + +#endif + Index: linux/net/ax25/ax25_std_in.c diff -u linux/net/ax25/ax25_std_in.c:1.1.2.1 linux/net/ax25/ax25_std_in.c:removed --- linux/net/ax25/ax25_std_in.c:1.1.2.1 Sun Jul 4 10:18:10 1999 +++ linux/net/ax25/ax25_std_in.c Wed Jul 7 01:02:33 1999 @@ -1,471 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; - * - * History - * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. - * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from - * the sock structure. - * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. - * Jonathan(G4KLX) Added IP mode registration. - * AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception. - * Upgraded state machine for SABME. - * Added arbitrary protocol id support. - * AX.25 031 Joerg(DL1BKE) Added DAMA support - * HaJo(DD8NE) Added Idle Disc Timer T5 - * Joerg(DL1BKE) Renamed it to "IDLE" with a slightly - * different behaviour. Fixed defrag - * routine (I hope) - * AX.25 032 Darryl(G7LED) AX.25 segmentation fixed. - * AX.25 033 Jonathan(G4KLX) Remove auto-router. - * Modularisation changes. - * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer. - * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - */ - -#include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For ip_rcv */ -#include -#include -#include -#include -#include - -/* - * State machine for state 1, Awaiting Connection State. - * The handling of the timer(s) is in file ax25_std_timer.c. - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_SABME: - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); - break; - - case AX25_UA: - if (pf) { - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25->state = AX25_STATE_3; - ax25->n2count = 0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_ESTABLISHED; - /* For WAIT_SABM connections we will produce an accept ready socket here */ - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - } - } - break; - - case AX25_DM: - if (pf) { - if (ax25->modulus == AX25_MODULUS) { - ax25_disconnect(ax25, ECONNREFUSED); - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } - } - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 2, Awaiting Release State. - * The handling of the timer(s) is in file ax25_std_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - case AX25_UA: - if (pf) ax25_disconnect(ax25, 0); - break; - - case AX25_I: - case AX25_REJ: - case AX25_RNR: - case AX25_RR: - if (pf) ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 3, Connected State. - * The handling of the timer(s) is in file ax25_std_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) -{ - int queued = 0; - - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - if (frametype == AX25_SABM) { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } else { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - } - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->condition = 0x00; - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25_requeue_frames(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - ax25_disconnect(ax25, ECONNRESET); - break; - - case AX25_RR: - case AX25_RNR: - if (frametype == AX25_RR) - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - else - ax25->condition |= AX25_COND_PEER_RX_BUSY; - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_check_iframes_acked(ax25, nr); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_requeue_frames(ax25); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_I: - if (!ax25_validate_nr(ax25, nr)) { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - break; - } - if (ax25->condition & AX25_COND_PEER_RX_BUSY) { - ax25_frames_acked(ax25, nr); - } else { - ax25_check_iframes_acked(ax25, nr); - } - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { - if (pf) ax25_std_enquiry_response(ax25); - break; - } - if (ns == ax25->vr) { - ax25->vr = (ax25->vr + 1) % ax25->modulus; - queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25->vr = ns; /* ax25->vr - 1 */ - ax25->condition &= ~AX25_COND_REJECT; - if (pf) { - ax25_std_enquiry_response(ax25); - } else { - if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->condition |= AX25_COND_ACK_PENDING; - ax25_start_t2timer(ax25); - } - } - } else { - if (ax25->condition & AX25_COND_REJECT) { - if (pf) ax25_std_enquiry_response(ax25); - } else { - ax25->condition |= AX25_COND_REJECT; - ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE); - ax25->condition &= ~AX25_COND_ACK_PENDING; - } - } - break; - - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_std_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - - default: - break; - } - - return queued; -} - -/* - * State machine for state 4, Timer Recovery State. - * The handling of the timer(s) is in file ax25_std_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) -{ - int queued = 0; - - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - if (frametype == AX25_SABM) { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } else { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - } - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->condition = 0x00; - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25->state = AX25_STATE_3; - ax25->n2count = 0; - ax25_requeue_frames(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - ax25_disconnect(ax25, ECONNRESET); - break; - - case AX25_RR: - case AX25_RNR: - if (frametype == AX25_RR) - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - else - ax25->condition |= AX25_COND_PEER_RX_BUSY; - if (type == AX25_RESPONSE && pf) { - ax25_stop_t1timer(ax25); - ax25->n2count = 0; - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - if (ax25->vs == ax25->va) { - ax25_start_t3timer(ax25); - ax25->state = AX25_STATE_3; - } else { - ax25_requeue_frames(ax25); - } - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - } - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - if (pf && type == AX25_RESPONSE) { - ax25_stop_t1timer(ax25); - ax25->n2count = 0; - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - if (ax25->vs == ax25->va) { - ax25_start_t3timer(ax25); - ax25->state = AX25_STATE_3; - } else { - ax25_requeue_frames(ax25); - } - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - } - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - ax25_requeue_frames(ax25); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_I: - if (!ax25_validate_nr(ax25, nr)) { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - break; - } - ax25_frames_acked(ax25, nr); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { - if (pf) ax25_std_enquiry_response(ax25); - break; - } - if (ns == ax25->vr) { - ax25->vr = (ax25->vr + 1) % ax25->modulus; - queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25->vr = ns; /* ax25->vr - 1 */ - ax25->condition &= ~AX25_COND_REJECT; - if (pf) { - ax25_std_enquiry_response(ax25); - } else { - if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->condition |= AX25_COND_ACK_PENDING; - ax25_start_t2timer(ax25); - } - } - } else { - if (ax25->condition & AX25_COND_REJECT) { - if (pf) ax25_std_enquiry_response(ax25); - } else { - ax25->condition |= AX25_COND_REJECT; - ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE); - ax25->condition &= ~AX25_COND_ACK_PENDING; - } - } - break; - - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_std_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - - default: - break; - } - - return queued; -} - -/* - * Higher level upcall for a LAPB frame - */ -int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) -{ - int queued = 0, frametype, ns, nr, pf; - - frametype = ax25_decode(ax25, skb, &ns, &nr, &pf); - - switch (ax25->state) { - case AX25_STATE_1: - queued = ax25_std_state1_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_2: - queued = ax25_std_state2_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_3: - queued = ax25_std_state3_machine(ax25, skb, frametype, ns, nr, pf, type); - break; - case AX25_STATE_4: - queued = ax25_std_state4_machine(ax25, skb, frametype, ns, nr, pf, type); - break; - } - - ax25_kick(ax25); - - return queued; -} - -#endif Index: linux/net/ax25/ax25_std_subr.c diff -u linux/net/ax25/ax25_std_subr.c:1.1.2.1 linux/net/ax25/ax25_std_subr.c:removed --- linux/net/ax25/ax25_std_subr.c:1.1.2.1 Sun Jul 4 10:18:10 1999 +++ linux/net/ax25/ax25_std_subr.c Wed Jul 7 01:02:33 1999 @@ -1,106 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; - * - * History - * AX.25 036 Jonathan(G4KLX) Split from ax25_out.c. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - */ - -#include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * The following routines are taken from page 170 of the 7th ARRL Computer - * Networking Conference paper, as is the whole state machine. - */ - -void ax25_std_nr_error_recovery(ax25_cb *ax25) -{ - ax25_std_establish_data_link(ax25); -} - -void ax25_std_establish_data_link(ax25_cb *ax25) -{ - ax25->condition = 0x00; - ax25->n2count = 0; - - if (ax25->modulus == AX25_MODULUS) - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND); - - ax25_calculate_t1(ax25); - ax25_stop_idletimer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t1timer(ax25); -} - -void ax25_std_transmit_enquiry(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_COMMAND); - - ax25->condition &= ~AX25_COND_ACK_PENDING; - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); -} - -void ax25_std_enquiry_response(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_RESPONSE); - else - ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_RESPONSE); - - ax25->condition &= ~AX25_COND_ACK_PENDING; -} - -void ax25_std_timeout_response(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25_send_control(ax25, AX25_RNR, AX25_POLLOFF, AX25_RESPONSE); - else - ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE); - - ax25->condition &= ~AX25_COND_ACK_PENDING; -} - -#endif Index: linux/net/ax25/ax25_std_timer.c diff -u linux/net/ax25/ax25_std_timer.c:1.1.2.1 linux/net/ax25/ax25_std_timer.c:removed --- linux/net/ax25/ax25_std_timer.c:1.1.2.1 Sun Jul 4 10:18:10 1999 +++ linux/net/ax25/ax25_std_timer.c Wed Jul 7 01:02:33 1999 @@ -1,174 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * History - * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. - * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the - * sock structure. - * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. - * AX.25 031 Joerg(DL1BKE) Added DAMA support - * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug - * AX.25 033 Jonathan(G4KLX) Modularisation functions. - * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. - * AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - */ - -#include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void ax25_std_heartbeat_expiry(ax25_cb *ax25) -{ - switch (ax25->state) { - - case AX25_STATE_0: - /* Magic here: If we listen() and a new link dies before it - is accepted() it isn't 'dead' so doesn't get removed. */ - if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) { - ax25_destroy_socket(ax25); - return; - } - break; - - case AX25_STATE_3: - case AX25_STATE_4: - /* - * Check the state of the receive buffer. - */ - if (ax25->sk != NULL) { - if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) && - (ax25->condition & AX25_COND_OWN_RX_BUSY)) { - ax25->condition &= ~AX25_COND_OWN_RX_BUSY; - ax25->condition &= ~AX25_COND_ACK_PENDING; - ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE); - break; - } - } - } - - ax25_start_heartbeat(ax25); -} - -void ax25_std_t2timer_expiry(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_ACK_PENDING) { - ax25->condition &= ~AX25_COND_ACK_PENDING; - ax25_std_timeout_response(ax25); - } -} - -void ax25_std_t3timer_expiry(ax25_cb *ax25) -{ - ax25->n2count = 0; - ax25_std_transmit_enquiry(ax25); - ax25->state = AX25_STATE_4; -} - -void ax25_std_idletimer_expiry(ax25_cb *ax25) -{ - ax25_clear_queues(ax25); - - ax25->n2count = 0; - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25->state = AX25_STATE_2; - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } -} - -void ax25_std_t1timer_expiry(ax25_cb *ax25) -{ - switch (ax25->state) { - case AX25_STATE_1: - if (ax25->n2count == ax25->n2) { - if (ax25->modulus == AX25_MODULUS) { - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25->n2count = 0; - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - } - } else { - ax25->n2count++; - if (ax25->modulus == AX25_MODULUS) - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND); - } - break; - - case AX25_STATE_2: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - } - break; - - case AX25_STATE_3: - ax25->n2count = 1; - ax25_std_transmit_enquiry(ax25); - ax25->state = AX25_STATE_4; - break; - - case AX25_STATE_4: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - ax25_std_transmit_enquiry(ax25); - } - break; - } - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); -} - -#endif Index: linux/net/ax25/ax25_subr.c diff -u linux/net/ax25/ax25_subr.c:1.1.2.1 linux/net/ax25/ax25_subr.c:1.1.2.1.2.1 --- linux/net/ax25/ax25_subr.c:1.1.2.1 Sun Jul 4 10:18:10 1999 +++ linux/net/ax25/ax25_subr.c Sun Jul 4 11:32:32 1999 @@ -1,7 +1,7 @@ /* - * AX.25 release 037 + * AX.25 release 035 * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * This code REQUIRES 1.3.61 or higher/ NET3.029 * * This module: * This module is free software; you can redistribute it and/or @@ -30,196 +30,61 @@ * AX.25 032 Joerg(DL1BKE) Added ax25_queue_length to count the number of * enqueued buffers of a socket.. * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. - * AX.25 037 Jonathan(G4KLX) New timer architecture. */ -#include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include +#include #include -#include +#include +#include + +#include "ax25_ddi.h" +#include "ax25_in.h" +#include "ax25_subr.h" +#include "ax25_core.h" +#include "ax25_vj.h" /* * This routine purges all the queues of frames. */ void ax25_clear_queues(ax25_cb *ax25) -{ - struct sk_buff *skb; - - while ((skb = skb_dequeue(&ax25->write_queue)) != NULL) - kfree_skb(skb); - - while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL) - kfree_skb(skb); - - while ((skb = skb_dequeue(&ax25->reseq_queue)) != NULL) - kfree_skb(skb); - - while ((skb = skb_dequeue(&ax25->frag_queue)) != NULL) - kfree_skb(skb); -} - -/* - * This routine purges the input queue of those frames that have been - * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the - * SDL diagram. - */ -void ax25_frames_acked(ax25_cb *ax25, unsigned short nr) { - struct sk_buff *skb; + int i; - /* - * Remove all the ack-ed frames from the ack queue. - */ - if (ax25->va != nr) { - while (skb_peek(&ax25->ack_queue) != NULL && ax25->va != nr) { - skb = skb_dequeue(&ax25->ack_queue); + skb_queue_purge(&ax25->frag_queue); + skb_queue_purge(&ax25->rcv_queue); + skb_queue_purge(&ax25->write_queue); + skb_queue_purge(&ax25->ack_queue); + + for (i = 0; i <= AX25_SEQMASK; i++) { + struct sk_buff *skb; + + if ((skb = ax25->reseq[i].skb) != NULL) { + ax25->reseq[i].skb = NULL; + ax25->reseq[i].csum = 0; kfree_skb(skb); - ax25->va = (ax25->va + 1) % ax25->modulus; } } } -void ax25_requeue_frames(ax25_cb *ax25) -{ - struct sk_buff *skb, *skb_prev = NULL; - - /* - * Requeue all the un-ack-ed frames on the output queue to be picked - * up by ax25_kick called from the timer. This arrangement handles the - * possibility of an empty output queue. - */ - while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL) { - if (skb_prev == NULL) - skb_queue_head(&ax25->write_queue, skb); - else - skb_append(skb_prev, skb); - skb_prev = skb; - } -} - /* - * Validate that the value of nr is between va and vs. Return true or - * false for testing. + * this is new */ -int ax25_validate_nr(ax25_cb *ax25, unsigned short nr) -{ - unsigned short vc = ax25->va; - - while (vc != ax25->vs) { - if (nr == vc) return 1; - vc = (vc + 1) % ax25->modulus; - } - if (nr == ax25->vs) return 1; - - return 0; -} - -/* - * This routine is the centralised routine for parsing the control - * information for the different frame formats. - */ -int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf) +void ax25_tx_command(ax25_cb *ax25, int frametype, int poll_bit) { - unsigned char *frame; - int frametype = AX25_ILLEGAL; - - frame = skb->data; - *ns = *nr = *pf = 0; - - if (ax25->modulus == AX25_MODULUS) { - if ((frame[0] & AX25_S) == 0) { - frametype = AX25_I; /* I frame - carries NR/NS/PF */ - *ns = (frame[0] >> 1) & 0x07; - *nr = (frame[0] >> 5) & 0x07; - *pf = frame[0] & AX25_PF; - } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ - frametype = frame[0] & 0x0F; - *nr = (frame[0] >> 5) & 0x07; - *pf = frame[0] & AX25_PF; - } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ - frametype = frame[0] & ~AX25_PF; - *pf = frame[0] & AX25_PF; - } - skb_pull(skb, 1); - } else { - if ((frame[0] & AX25_S) == 0) { - frametype = AX25_I; /* I frame - carries NR/NS/PF */ - *ns = (frame[0] >> 1) & 0x7F; - *nr = (frame[1] >> 1) & 0x7F; - *pf = frame[1] & AX25_EPF; - skb_pull(skb, 2); - } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ - frametype = frame[0] & 0x0F; - *nr = (frame[1] >> 1) & 0x7F; - *pf = frame[1] & AX25_EPF; - skb_pull(skb, 2); - } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ - frametype = frame[0] & ~AX25_PF; - *pf = frame[0] & AX25_PF; - skb_pull(skb, 1); - } - } - - return frametype; + if (poll_bit) + frametype |= 0x100; + ax25->tx_cmd = frametype; + ax25_kick(ax25); } -/* - * This routine is called when the HDLC layer internally generates a - * command or response for the remote machine ( eg. RR, UA etc. ). - * Only supervisory or unnumbered frames are processed. - */ -void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type) +void ax25_tx_response(ax25_cb *ax25, int frametype, int poll_bit) { - struct sk_buff *skb; - unsigned char *dptr; - - if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat) + 2, GFP_ATOMIC)) == NULL) - return; - - skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat)); - - skb->nh.raw = skb->data; - - /* Assume a response - address structure for DTE */ - if (ax25->modulus == AX25_MODULUS) { - dptr = skb_put(skb, 1); - *dptr = frametype; - *dptr |= (poll_bit) ? AX25_PF : 0; - if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */ - *dptr |= (ax25->vr << 5); - } else { - if ((frametype & AX25_U) == AX25_U) { - dptr = skb_put(skb, 1); - *dptr = frametype; - *dptr |= (poll_bit) ? AX25_PF : 0; - } else { - dptr = skb_put(skb, 2); - dptr[0] = frametype; - dptr[1] = (ax25->vr << 1); - dptr[1] |= (poll_bit) ? AX25_EPF : 0; - } - } - - ax25_transmit_buffer(ax25, skb, type); + if (poll_bit) + frametype |= 0x100; + ax25->tx_rsp = frametype; + ax25_kick(ax25); } /* @@ -227,43 +92,39 @@ * * Note: src here is the sender, thus it's the target of the DM */ -void ax25_return_dm(struct device *dev, ax25_address *src, ax25_address *dest, ax25_digi *digi) +void ax25_return_dm(struct device *dev, ax25_pktinfo *pkt) { struct sk_buff *skb; - char *dptr; - ax25_digi retdigi; + unsigned char *dptr; + int sizeof_addr; + ax25_addr_t addr; - if (dev == NULL) - return; + sizeof_addr = ax25_sizeof_addr(&pkt->addr); - if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(digi) + 1, GFP_ATOMIC)) == NULL) + if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + sizeof_addr + 1, GFP_ATOMIC)) == NULL) return; /* Next SABM will get DM'd */ - skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(digi)); + skb_reserve(skb, AX25_BPQ_HEADER_LEN + sizeof_addr); + ax25_invert_addr(&pkt->addr, &addr); - ax25_digi_invert(digi, &retdigi); + *skb_put(skb, 1) = AX25_DM|AX25_PF; - dptr = skb_put(skb, 1); - - *dptr = AX25_DM | AX25_PF; - /* * Do the address ourselves */ - dptr = skb_push(skb, ax25_addr_size(digi)); - dptr += ax25_addr_build(dptr, dest, src, &retdigi, AX25_RESPONSE, AX25_MODULUS); - - skb->dev = dev; - - ax25_queue_xmit(skb); + skb->nh.raw = skb->data; + dptr = skb_push(skb, sizeof_addr); + dptr += ax25_build_addr(dptr, &addr, AX25_RESPONSE, AX25_SEQMASK); + ax25_send_unproto(skb, dev); } /* * Exponential backoff for AX.25 */ -void ax25_calculate_t1(ax25_cb *ax25) +unsigned short ax25_calculate_t1(ax25_cb *ax25) { - int n, t = 2; + int n; + int t = 1; switch (ax25->backoff) { case 0: @@ -274,54 +135,99 @@ break; case 2: - for (n = 0; n < ax25->n2count; n++) - t *= 2; - if (t > 8) t = 8; + t <<= (ax25->n2count < 8 ? ax25->n2count : 8); break; } - ax25->t1 = t * ax25->rtt; -} + n = (t * ax25->rtt) / AX25_TICS; -/* - * Calculate the Round Trip Time - */ -void ax25_calculate_rtt(ax25_cb *ax25) -{ - if (ax25->backoff == 0) - return; + if (n > AX25_T1CLAMPHI) + return AX25_T1CLAMPHI; - if (ax25_t1timer_running(ax25) && ax25->n2count == 0) - ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25_display_timer(&ax25->t1timer)) / 10; + if (n < AX25_T1CLAMPLO) + return AX25_T1CLAMPLO; - if (ax25->rtt < AX25_T1CLAMPLO) - ax25->rtt = AX25_T1CLAMPLO; + return n; +} - if (ax25->rtt > AX25_T1CLAMPHI) - ax25->rtt = AX25_T1CLAMPHI; +void ax25_close_socket(struct sock *sk, int reason) +{ + sk->err = reason; + sk->shutdown = SHUTDOWN_MASK; + sk->state = TCP_CLOSE; + sk->state_change(sk); + sk->dead = 1; } void ax25_disconnect(ax25_cb *ax25, int reason) { ax25_clear_queues(ax25); + ax25_link_failed(ax25, reason); + ax25->wrt_timer = 0; + ax25->ack_timer = 0; + ax25->idletimer = 0; + ax25->vs = 0; + ax25->va = 0; + ax25->vr = 0; + ax25->killtimer = 90 * AX25_SLOWHZ; + ax25->state = AX25_STATE_0; + if (ax25->peer && ax25->peer->state > AX25_STATE_2) + ax25_set_cond(ax25->peer, AX25_COND_RELEASE); + if (ax25->slcomp) { + axhc_free(ax25->slcomp); + ax25->slcomp = NULL; + } +} - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); +void ax25_nr_error_recovery(ax25_cb *ax25) +{ + printk(KERN_DEBUG "ax25_nr_error_recovery: dest=%s\n", ax2asc(&ax25->addr.dest)); - ax25->state = AX25_STATE_0; + if (ax25->peer != NULL) + ax25->peer->condition |= AX25_COND_RELEASE; - ax25_link_failed(ax25, reason); + ax25_clear_queues(ax25); + ax25_start_t1(ax25); + ax25->state = AX25_STATE_2; + ax25_tx_command(ax25, AX25_DISC, AX25_POLLON); +} - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = reason; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } +void ax25_establish_data_link(ax25_cb *ax25) +{ + ax25->state = AX25_STATE_1; + ax25->n2count = 0; + ax25->ack_timer = 0; + ax25->vs = 0; + ax25->va = 0; + ax25->vr = 0; + ax25_set_cond(ax25, AX25_COND_SETUP); + ax25_start_t1(ax25); + + if (ax25->seqmask == AX25_SEQMASK) + ax25_tx_command(ax25, AX25_SABM, AX25_POLLON); + else + ax25_tx_command(ax25, AX25_SABME, AX25_POLLON); + } -#endif +void ax25_transmit_enquiry(ax25_cb *ax25) +{ + int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR; + ax25_tx_command(ax25, ft, AX25_POLLON); + ax25_start_t1(ax25); + ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); +} + +void ax25_enquiry_response(ax25_cb *ax25) +{ + int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR; + ax25_tx_response(ax25, ft, AX25_POLLON); + ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); +} + +void ax25_timeout_response(ax25_cb *ax25) +{ + int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR; + ax25_tx_response(ax25, ft, AX25_POLLOFF); + ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); +} Index: linux/net/ax25/ax25_subr.h diff -u /dev/null linux/net/ax25/ax25_subr.h:1.1.4.1 --- /dev/null Wed Jul 7 01:02:33 1999 +++ linux/net/ax25/ax25_subr.h Sun Jul 4 13:03:45 1999 @@ -0,0 +1,35 @@ +#ifndef _AX25_SUBR_H +#define _AX25_SUBR_H + +extern unsigned char *ax25_parse_addr(unsigned char*, int, ax25_pktinfo*); +extern unsigned short ax25_calculate_t1(ax25_cb*); +extern void ax25_return_dm(struct device*, ax25_pktinfo*); +extern void ax25_tx_command(ax25_cb*, int, int); +extern void ax25_tx_response(ax25_cb*, int, int); +extern void ax25_clear_queues(ax25_cb*); +extern void ax25_disconnect(ax25_cb*, int); +extern void ax25_nr_error_recovery(ax25_cb*); +extern void ax25_establish_data_link(ax25_cb*); +extern void ax25_transmit_enquiry(ax25_cb*); +extern void ax25_enquiry_response(ax25_cb*); +extern void ax25_timeout_response(ax25_cb*); +extern void ax25_close_socket(struct sock*, int); + + +extern inline void ax25_set_cond(ax25_cb* ax25, unsigned int cond) +{ + ax25->condition |= cond; +} + +extern inline void ax25_clr_cond(ax25_cb* ax25, unsigned int cond) +{ + ax25->condition &= ~cond; +} + +extern inline void ax25_start_t1(ax25_cb* ax25) +{ + ax25_set_cond(ax25, AX25_COND_START_T1); + ax25->wrt_timer = 0; +} + +#endif Index: linux/net/ax25/ax25_timer.c diff -u linux/net/ax25/ax25_timer.c:1.1.2.1 linux/net/ax25/ax25_timer.c:1.1.2.1.2.1 --- linux/net/ax25/ax25_timer.c:1.1.2.1 Sun Jul 4 10:18:10 1999 +++ linux/net/ax25/ax25_timer.c Sun Jul 4 11:32:32 1999 @@ -1,7 +1,7 @@ /* - * AX.25 release 037 + * AX.25 release 035 * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * This code REQUIRES 1.2.1 or higher/ NET3.029 * * This module: * This module is free software; you can redistribute it and/or @@ -18,237 +18,423 @@ * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug * AX.25 033 Jonathan(G4KLX) Modularisation functions. * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. - * AX.25 036 Jonathan(G4KLX) Split Standard and DAMA code into separate files. - * Joerg(DL1BKE) Fixed DAMA Slave. We are *required* to start with - * standard AX.25 mode. - * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include +#include #include -#include -#include -#include -#include -#include +#include +#include + +#include "ax25_core.h" +#include "ax25_ddi.h" +#include "ax25_in.h" +#include "ax25_subr.h" -static void ax25_heartbeat_expiry(unsigned long); -static void ax25_t1timer_expiry(unsigned long); -static void ax25_t2timer_expiry(unsigned long); -static void ax25_t3timer_expiry(unsigned long); -static void ax25_idletimer_expiry(unsigned long); +static void ax25_wrt_timeout(ax25_cb *); -void ax25_start_heartbeat(ax25_cb *ax25) +/* + * AX.25 TIMER + * + * This routine is called every 100ms. Decrement timer by this + * amount - if expired then process the event. + */ +void ax25_timer(ax25_cb *ax25) { - del_timer(&ax25->timer); + switch (ax25->state) { + case AX25_LISTEN: + /* + * never kill listening sockets. let the be moved to + * AX25_STATE_0 first. ax25_release() does this. + */ + return; + + case AX25_STATE_0: + /* + * don't kill if a frame signalling a state change is + * still pending + */ + if (ax25->condition & AX25_COND_STATE_CHANGE) + break; - ax25->timer.data = (unsigned long)ax25; - ax25->timer.function = &ax25_heartbeat_expiry; - ax25->timer.expires = jiffies + 5 * HZ; + if (ax25->sk) { + /* + * ax25_release() sets ax25->sk = NULL, releasing the + * connection between the socket and the underlying control + * structure, we handle that further down + */ + + /* almost dead, notify socket */ + if (!ax25->sk->dead) + ax25_close_socket(ax25->sk, 0); + break; + } + + /* + * wait a certain time before destroying the control block + */ + if (ax25->killtimer > 0 && --ax25->killtimer > 0) + break; - add_timer(&ax25->timer); -} + /* + * if a peer exists in STATE [12], disconnect it. this handles + * "connection timed out" for digipeated connections. + * + * else if no peer exists, destroy this control block. + */ + if (ax25->peer) { + if (ax25->peer->state < AX25_STATE_3) + ax25_destroy_cb(ax25->peer); + else + break; + } + + ax25_destroy_cb(ax25); + return; + + case AX25_STATE_3: + if (!ax25->peer && !ax25->sk && ax25->idletimer > 0 && --ax25->idletimer == 0) + ax25_set_cond(ax25, AX25_COND_RELEASE); + /* fall through */ + case AX25_STATE_4: + if (ax25->condition & AX25_COND_ACK_PENDING) { + if (ax25->ack_timer > 0) + ax25->ack_timer--; + else + ax25_timeout_response(ax25); + } -void ax25_start_t1timer(ax25_cb *ax25) -{ - del_timer(&ax25->t1timer); + /* + * Our peer connection has seen a DISC or DM and we're about to change to + * state 2. We don't DISC until we've delivered all queued data + */ + if (ax25->condition & AX25_COND_RELEASE) { + if (!skb_queue_len(&ax25->write_queue) && !skb_queue_len(&ax25->ack_queue)) { + ax25->n2count = 1; + ax25->ack_timer = 0; + ax25_start_t1(ax25); + ax25_clr_cond(ax25, AX25_COND_RELEASE); + ax25->state = AX25_STATE_2; + ax25_tx_command(ax25, AX25_DISC, AX25_POLLON); + } + + /* + * Check the state of the receive buffer. This is part of the flow control + * and should be done in ax25_rcvmsg(); + */ + } else if (ax25->sk && skb_queue_len(&ax25->rcv_queue)) { + struct sk_buff *skb; + + while ((skb = skb_peek(&ax25->rcv_queue)) != NULL) { + if (ax25->sk->shutdown & RCV_SHUTDOWN) + break; + if (atomic_read(&ax25->sk->rmem_alloc) + skb->truesize < ax25->sk->rcvbuf) { + skb_dequeue(&ax25->rcv_queue); + sock_queue_rcv_skb(ax25->sk, skb); + } else + break; + } + if (skb == NULL) { + ax25_clr_cond(ax25, AX25_COND_OWN_RX_BUSY); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25->state = AX25_STATE_4; + ax25_transmit_enquiry(ax25); + break; + } + } + break; + + default: /* state 1/2 */ + break; + } - ax25->t1timer.data = (unsigned long)ax25; - ax25->t1timer.function = &ax25_t1timer_expiry; - ax25->t1timer.expires = jiffies + ax25->t1; + if (ax25->wrt_timer > 0 && --ax25->wrt_timer == 0) + ax25_wrt_timeout(ax25); - add_timer(&ax25->t1timer); } -void ax25_start_t2timer(ax25_cb *ax25) -{ - del_timer(&ax25->t2timer); +static void ax25_wrt_timeout(ax25_cb *ax25) +{ + ax25_cb *peer = ax25->peer; + + switch (ax25->state) { + case AX25_STATE_1: + if (ax25->n2count == ax25->n2) { + if (ax25->seqmask == AX25_SEQMASK) { + ax25_disconnect(ax25, ETIMEDOUT); + if (ax25->sk) + ax25_close_socket(ax25->sk, ETIMEDOUT); + else if (peer) { + ax25_disconnect(peer, 0); + } + } else { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + ax25->n2count = 0; + ax25_start_t1(ax25); + ax25_tx_command(ax25, AX25_SABM, AX25_POLLON); + } + } else { + ax25->n2count++; + ax25_start_t1(ax25); + if (ax25->seqmask == AX25_SEQMASK) + ax25_tx_command(ax25, AX25_SABM, AX25_POLLON); + else + ax25_tx_command(ax25, AX25_SABME, AX25_POLLON); + } + break; + + case AX25_STATE_2: + if (ax25->n2count == ax25->n2) { + if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_disconnect(ax25, 0); + } else { + ax25->n2count++; + ax25_start_t1(ax25); + ax25_tx_command(ax25, AX25_DISC, AX25_POLLON); + } + break; + + case AX25_STATE_3: + ax25->vs_rtt = -1; + ax25->n2count = 1; + ax25_start_t1(ax25); + ax25->state = AX25_STATE_4; + ax25_transmit_enquiry(ax25); + break; + + case AX25_STATE_4: + if (ax25->n2count == ax25->n2) { + if (peer) { + ax25_set_cond(peer, AX25_COND_RELEASE); + ax25->killtimer = 90 * AX25_SLOWHZ; + } else if (ax25->sk) + ax25_close_socket(ax25->sk, ETIMEDOUT); + ax25_disconnect(ax25, ETIMEDOUT); + } else { + ax25->n2count++; + ax25_transmit_enquiry(ax25); + } + break; + } +} - ax25->t2timer.data = (unsigned long)ax25; - ax25->t2timer.function = &ax25_t2timer_expiry; - ax25->t2timer.expires = jiffies + ax25->t2; - add_timer(&ax25->t2timer); -} +/* -------------------------------------------------------------------- */ + +/************************************************************************/ +/* Module support functions follow. */ +/************************************************************************/ + +static struct protocol_struct { + struct protocol_struct *next; + unsigned int pid; + int (*func)(struct sk_buff *, ax25_cb *); +} *protocol_list = NULL; + +static struct linkfail_struct { + struct linkfail_struct *next; + void (*func)(ax25_cb *, int); +} *linkfail_list = NULL; + +static struct listen_struct { + struct listen_struct *next; + ax25_address callsign; + struct device *dev; +} *listen_list = NULL; -void ax25_start_t3timer(ax25_cb *ax25) +int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *)) { - del_timer(&ax25->t3timer); + struct protocol_struct *protocol; + unsigned long flags; - if (ax25->t3 > 0) { - ax25->t3timer.data = (unsigned long)ax25; - ax25->t3timer.function = &ax25_t3timer_expiry; - ax25->t3timer.expires = jiffies + ax25->t3; + if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT) + return 0; - add_timer(&ax25->t3timer); - } + if ((protocol = (struct protocol_struct *)kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL) + return 0; + + protocol->pid = pid; + protocol->func = func; + + save_flags(flags); + cli(); + + protocol->next = protocol_list; + protocol_list = protocol; + + restore_flags(flags); + + return 1; } -void ax25_start_idletimer(ax25_cb *ax25) +void ax25_protocol_release(unsigned int pid) { - del_timer(&ax25->idletimer); + struct protocol_struct *s, *protocol = protocol_list; + unsigned long flags; + + if (protocol == NULL) + return; - if (ax25->idle > 0) { - ax25->idletimer.data = (unsigned long)ax25; - ax25->idletimer.function = &ax25_idletimer_expiry; - ax25->idletimer.expires = jiffies + ax25->idle; + save_flags(flags); + cli(); - add_timer(&ax25->idletimer); + if (protocol->pid == pid) { + protocol_list = protocol->next; + restore_flags(flags); + kfree(protocol); + return; } -} -void ax25_stop_heartbeat(ax25_cb *ax25) -{ - del_timer(&ax25->timer); -} + while (protocol != NULL && protocol->next != NULL) { + if (protocol->next->pid == pid) { + s = protocol->next; + protocol->next = protocol->next->next; + restore_flags(flags); + kfree(s); + return; + } -void ax25_stop_t1timer(ax25_cb *ax25) -{ - del_timer(&ax25->t1timer); -} + protocol = protocol->next; + } -void ax25_stop_t2timer(ax25_cb *ax25) -{ - del_timer(&ax25->t2timer); + restore_flags(flags); } -void ax25_stop_t3timer(ax25_cb *ax25) +int ax25_linkfail_register(void (*func)(ax25_cb *, int)) { - del_timer(&ax25->t3timer); -} + struct linkfail_struct *linkfail; + unsigned long flags; -void ax25_stop_idletimer(ax25_cb *ax25) -{ - del_timer(&ax25->idletimer); + if ((linkfail = (struct linkfail_struct *)kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL) + return 0; + + linkfail->func = func; + + save_flags(flags); + cli(); + + linkfail->next = linkfail_list; + linkfail_list = linkfail; + + restore_flags(flags); + + return 1; } -int ax25_t1timer_running(ax25_cb *ax25) +void ax25_linkfail_release(void (*func)(ax25_cb *, int)) { - return (ax25->t1timer.prev != NULL || ax25->t1timer.next != NULL); + struct linkfail_struct *s, *linkfail = linkfail_list; + unsigned long flags; + + if (linkfail == NULL) + return; + + save_flags(flags); + cli(); + + if (linkfail->func == func) { + linkfail_list = linkfail->next; + restore_flags(flags); + kfree(linkfail); + return; + } + + while (linkfail != NULL && linkfail->next != NULL) { + if (linkfail->next->func == func) { + s = linkfail->next; + linkfail->next = linkfail->next->next; + restore_flags(flags); + kfree(s); + return; + } + + linkfail = linkfail->next; + } + + restore_flags(flags); } + +static char empty_addr[AX25_ADDR_LEN] = {0, 0, 0, 0, 0, 0, 0}; -unsigned long ax25_display_timer(struct timer_list *timer) +int ax25_listen_register(ax25_address *callsign, struct device *dev) { - if (timer->prev == NULL && timer->next == NULL) + ax25_cb *ax25; + + ax25_addr_t addr; + + addr.dcount = 0; + addr.src = *callsign; + memcpy(&addr.dest, empty_addr, AX25_ADDR_LEN); + + if (ax25_find_cb(&addr, dev) != NULL) return 0; - return timer->expires - jiffies; + if ((ax25 = ax25_create_cb()) == NULL) + return 0; + + ax25->addr.src = *callsign; + + ax25_fillin_cb(ax25, dev); + ax25_insert_cb(ax25); + + return 1; } -static void ax25_heartbeat_expiry(unsigned long param) +void ax25_listen_release(ax25_address *callsign, struct device *dev) { - ax25_cb *ax25 = (ax25_cb *)param; + ax25_cb *ax25; - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_heartbeat_expiry(ax25); - break; + ax25_addr_t addr; -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25->ax25_dev->dama.slave) - ax25_ds_heartbeat_expiry(ax25); - else - ax25_std_heartbeat_expiry(ax25); - break; -#endif - } + addr.dcount = 0; + addr.src = *callsign; + memcpy(&addr.dest, empty_addr, AX25_ADDR_LEN); + + if ((ax25 = ax25_find_cb(&addr, dev)) != NULL) + ax25_destroy_cb(ax25); } -static void ax25_t1timer_expiry(unsigned long param) +int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *) { - ax25_cb *ax25 = (ax25_cb *)param; + struct protocol_struct *protocol; - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t1timer_expiry(ax25); - break; + for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) + if (protocol->pid == pid) + return protocol->func; -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) - ax25_std_t1timer_expiry(ax25); - break; -#endif - } + return NULL; } -static void ax25_t2timer_expiry(unsigned long param) +int ax25_listen_mine(ax25_address *callsign, struct device *dev) { - ax25_cb *ax25 = (ax25_cb *)param; + struct listen_struct *listen; - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t2timer_expiry(ax25); - break; + for (listen = listen_list; listen != NULL; listen = listen->next) + if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL)) + return 1; -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) - ax25_std_t2timer_expiry(ax25); - break; -#endif - } + return 0; } -static void ax25_t3timer_expiry(unsigned long param) +void ax25_link_failed(ax25_cb *ax25, int reason) { - ax25_cb *ax25 = (ax25_cb *)param; + struct linkfail_struct *linkfail; - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t3timer_expiry(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25->ax25_dev->dama.slave) - ax25_ds_t3timer_expiry(ax25); - else - ax25_std_t3timer_expiry(ax25); - break; -#endif - } + for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next) + (linkfail->func)(ax25, reason); } -static void ax25_idletimer_expiry(unsigned long param) +int ax25_protocol_is_registered(unsigned int pid) { - ax25_cb *ax25 = (ax25_cb *)param; + struct protocol_struct *protocol; - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_idletimer_expiry(ax25); - break; + for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) + if (protocol->pid == pid) + return 1; -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25->ax25_dev->dama.slave) - ax25_ds_idletimer_expiry(ax25); - else - ax25_std_idletimer_expiry(ax25); - break; -#endif - } + return 0; } -#endif Index: linux/net/ax25/ax25_uid.c diff -u linux/net/ax25/ax25_uid.c:1.1.2.1 linux/net/ax25/ax25_uid.c:1.1.2.1.2.1 --- linux/net/ax25/ax25_uid.c:1.1.2.1 Sun Jul 4 10:18:11 1999 +++ linux/net/ax25/ax25_uid.c Sun Jul 4 11:32:32 1999 @@ -1,64 +1,27 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * History - * AX.25 036 Jonathan(G4KLX) Split from af_ax25.c. - */ - -#include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -#include #include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include + +#include +#include "af_ax25.h" + /* * Callsign/UID mapper. This is in kernel space for security on multi-amateur machines. */ -static ax25_uid_assoc *ax25_uid_list = NULL; - int ax25_uid_policy = 0; +static ax25_uid_assoc *ax25_uid_list; ax25_address *ax25_findbyuid(uid_t uid) { - ax25_uid_assoc *ax25_uid; + ax25_uid_assoc *a; - for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { - if (ax25_uid->uid == uid) - return &ax25_uid->call; + for (a = ax25_uid_list; a != NULL; a = a->next) { + if (a->uid == uid) + return &a->call; } return NULL; @@ -66,14 +29,13 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) { - ax25_uid_assoc *s, *ax25_uid; - unsigned long flags; + ax25_uid_assoc *a; switch (cmd) { case SIOCAX25GETUID: - for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { - if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) - return ax25_uid->uid; + for (a = ax25_uid_list; a != NULL; a = a->next) { + if (ax25cmp(&sax->sax25_call, &a->call) == 0) + return a->uid; } return -ENOENT; @@ -84,43 +46,33 @@ return -EEXIST; if (sax->sax25_uid == 0) return -EINVAL; - if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL) + a = (ax25_uid_assoc *)kmalloc(sizeof(*a), GFP_KERNEL); + if (a == NULL) return -ENOMEM; - ax25_uid->uid = sax->sax25_uid; - ax25_uid->call = sax->sax25_call; - save_flags(flags); cli(); - ax25_uid->next = ax25_uid_list; - ax25_uid_list = ax25_uid; - restore_flags(flags); + a->uid = sax->sax25_uid; + a->call = sax->sax25_call; + a->next = ax25_uid_list; + ax25_uid_list = a; return 0; + + case SIOCAX25DELUID: { + ax25_uid_assoc **l; - case SIOCAX25DELUID: if (!suser()) return -EPERM; - for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { - if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) - break; - } - if (ax25_uid == NULL) - return -ENOENT; - save_flags(flags); cli(); - if ((s = ax25_uid_list) == ax25_uid) { - ax25_uid_list = s->next; - restore_flags(flags); - kfree(ax25_uid); - return 0; - } - while (s != NULL && s->next != NULL) { - if (s->next == ax25_uid) { - s->next = ax25_uid->next; - restore_flags(flags); - kfree(ax25_uid); + l = &ax25_uid_list; + while ((*l) != NULL) { + if (ax25cmp(&((*l)->call), &(sax->sax25_call)) == 0) { + a = *l; + *l = (*l)->next; + kfree(a); return 0; } - s = s->next; + + l = &((*l)->next); } - restore_flags(flags); return -ENOENT; + } default: return -EINVAL; @@ -129,14 +81,14 @@ return -EINVAL; /*NOTREACHED */ } -int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +int ax25_cs_get_info(char *buffer, char **start, off_t offset, int length, int dummy) { ax25_uid_assoc *pt; int len = 0; off_t pos = 0; off_t begin = 0; - cli(); + start_bh_atomic(); len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy); @@ -154,7 +106,7 @@ break; } - sti(); + end_bh_atomic(); *start = buffer + (offset - begin); len -= offset - begin; @@ -164,23 +116,4 @@ return len; } -#ifdef MODULE - -/* - * Free all memory associated with UID/Callsign structures. - */ -void ax25_uid_free(void) -{ - ax25_uid_assoc *s, *ax25_uid = ax25_uid_list; - - while (ax25_uid != NULL) { - s = ax25_uid; - ax25_uid = ax25_uid->next; - - kfree(s); - } -} - -#endif -#endif Index: linux/net/ax25/ax25_vj.c diff -u /dev/null linux/net/ax25/ax25_vj.c:1.1.4.1 --- /dev/null Wed Jul 7 01:02:33 1999 +++ linux/net/ax25/ax25_vj.c Sun Jul 4 13:03:45 1999 @@ -0,0 +1,720 @@ +/* + * + * ax25_vj.c written by Matthias Welwarsky, DG2FEF. + * + * Routines to compress TCP/IP packets according to RFC 1144 and to + * suppress redundant retransmissions on a reliable virtual circuit. + * Largely based on code by Van Jacobson, Phil Karn et.al. Directly + * derived from WAMPES' slhc.c written by Dieter Deyke, DK5SG. + * + * This module: + * This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * History + * 25.02.98 Adapted to Linux. contains code from drivers/net/slhc.c + * 04.03.98 fixed problem with nonatomically calling kmalloc from + * interrupt + * 08.03.98 fixed problem in axhc_recv_vjc() that lead to a system + * panic when the incoming sk_buff didn't contain enough + * headroom to rebuild the TCP/IP header after decompression + */ + +#include +#if defined(CONFIG_INET) +#include +#include +#include + +#include "ax25_vj.h" + +#ifdef DEBUG +#define PRINTK(x) printk x +#else +#define PRINTK(x) +#endif + +/* Bits in first octet of compressed packet */ +#define NEW_C 0x40 /* flag bits for what changed in a packet */ +#define NEW_I 0x20 +#define NEW_S 0x08 +#define NEW_A 0x04 +#define NEW_W 0x02 +#define NEW_U 0x01 + +/* reserved, special-case values of above */ +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) + +#define TCP_PUSH_BIT 0x10 + +static int axhc_toss(struct axvj_slcomp *); + +/* Put a short in host order into a char array in network order */ +static inline unsigned char * +put16(unsigned char *cp, unsigned short x) +{ + *cp++ = x >> 8; + *cp++ = x; + + return cp; +} + +/* Pull a 16-bit integer in host order from buffer in network byte order */ +static unsigned short pull16(unsigned char **cpp) +{ + short rval; + + rval = *(*cpp)++; + rval <<= 8; + rval |= *(*cpp)++; + return rval; +} + +/* Encode a number */ +static unsigned char * +encode(unsigned char *cp, unsigned short n) +{ + if (n >= 256 || n == 0) { + *cp++ = 0; + cp = put16(cp, n); + } else { + *cp++ = n; + } + return cp; +} + +/* Decode a number */ +static long decode(unsigned char **cpp) +{ + register int x; + + x = *(*cpp)++; + if (x == 0) { + return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */ + } else { + return x & 0xff; /* -1 if PULLCHAR returned error */ + } +} + + +/* Initialize compression data structure + * slots must be in range 0 to 255 (zero meaning no compression) + */ +struct axvj_slcomp * +axhc_init(int rslots, int tslots) +{ + register short i; + register struct axvj_cstate *ts; + struct axvj_slcomp *comp; + + comp = (struct axvj_slcomp *)kmalloc(sizeof(struct axvj_slcomp), + GFP_ATOMIC); + if (! comp) + return NULL; + + memset(comp, 0, sizeof(struct axvj_slcomp)); + + if ( rslots > 0 && rslots < 256 ) { + size_t rsize = rslots * sizeof(struct axvj_cstate); + comp->rstate = (struct axvj_cstate *) kmalloc(rsize, GFP_ATOMIC); + if (! comp->rstate) + { + kfree((unsigned char *)comp); + return NULL; + } + memset(comp->rstate, 0, rsize); + comp->rslot_limit = rslots - 1; + } + + if ( tslots > 0 && tslots < 256 ) { + size_t tsize = tslots * sizeof(struct axvj_cstate); + comp->tstate = (struct axvj_cstate *) kmalloc(tsize, GFP_ATOMIC); + if (! comp->tstate) + { + kfree((unsigned char *)comp->rstate); + kfree((unsigned char *)comp); + return NULL; + } + memset(comp->tstate, 0, tsize); + comp->tslot_limit = tslots - 1; + } + + comp->xmit_oldest = 0; + comp->xmit_current = 255; + comp->recv_current = 255; + /* + * don't accept any packets with implicit index until we get + * one with an explicit index. Otherwise the uncompress code + * will try to use connection 255, which is almost certainly + * out of range + */ + comp->flags |= SLF_TOSS; + + if ( tslots > 0 ) { + ts = comp->tstate; + for(i = comp->tslot_limit; i > 0; --i){ + ts[i].cs_this = i; + ts[i].next = &(ts[i - 1]); + } + ts[0].next = &(ts[comp->tslot_limit]); + ts[0].cs_this = 0; + } + return comp; +} + + +/* Free a compression data structure */ +void +axhc_free(struct axvj_slcomp *comp) +{ + if ( comp == NULL ) + return; + + if ( comp->rstate != NULL ) + kfree( comp->rstate ); + + if ( comp->tstate != NULL ) + kfree( comp->tstate ); + + kfree( comp ); +} + +/* Dear hacker. I assume that you have read and understood RFC 1144 + * and the original slhc_compress() procedure before tinkering with + * this code. + * + * procedure is as follows: + * 1. check if packet is TCP. return AX25_P_IP if not. + * 2. check if SYN, FIN, MSS, WSCALE, TSTAMP or RST is set, or if ACK is not + * set. deny compression for these packets (do_compression = 0). + * 3. try to find the appopriate slot, reuse an old one if no match is found + * 4. attempt to compress the packet and check the following rules: + * - if the packet contains an old (outdated) seq and no new ack or + * window or urgent data, drop it (return 0). + * - if nothing changed since the last frame sent (no new seq, ack, + * window, urgent data, or changing TCP flags), drop it. + * - before dropping a packet, check if any packet made it through the + * filter within the last 120sec. If not, assume a packet loss and + * transmit the packet. + * 5. transmit a compressed, uncompressed or regular packet, depending + * on do_compression and cs->deny_compression. + */ + +int axhc_compress(struct axvj_slcomp *comp, struct sk_buff *skb, int do_compression) +{ + struct axvj_cstate *ocs = &(comp->tstate[comp->xmit_oldest]); + struct axvj_cstate *lcs = ocs; + struct axvj_cstate *cs = lcs->next; + unsigned int hlen; + struct tcphdr *th, *oth; + struct iphdr *iph; + unsigned long deltaS, deltaA; + unsigned short changes = 0; + unsigned char new_seq[16]; + unsigned char *cp = new_seq; + + /* Peek at IP header */ + iph = (struct iphdr *) skb->data; + + /* Bail if this packet isn't TCP, or is an IP fragment */ + if (iph->protocol != IPPROTO_TCP || + (ntohs(iph->frag_off) & 0x1fff) || + (iph->frag_off & 32)) { + /* Send as regular IP */ + if (iph->protocol != IPPROTO_TCP) + comp->sls_o_nontcp++; + else + comp->sls_o_tcp++; + return AX25_P_IP; + } + /* Extract TCP header */ + th = (struct tcphdr *) (((unsigned char *) iph) + iph->ihl * 4); + hlen = iph->ihl * 4 + th->doff * 4; + + PRINTK((KERN_DEBUG "ax25_vj.c: th.seq=%0x\n", ntohl(th->seq))); + /* + * check if packet may be compressed. + */ + if (th->syn || th->fin || th->rst || !th->ack) { + comp->sls_o_tcp++; + do_compression = 0; + } + /* + * locate the connection state slot + */ + for (;;) { + if (iph->saddr == cs->cs_ip.saddr + && iph->daddr == cs->cs_ip.daddr + && th->source == cs->cs_tcp.source + && th->dest == cs->cs_tcp.dest) + goto found; + + /* if current equal oldest, at end of list */ + if (cs == ocs) + break; + lcs = cs; + cs = cs->next; + comp->sls_o_searches++; + } + /* + * Didn't find it -- re-use oldest axvj_cstate. Send an + * uncompressed packet that tells the other side what + * connection number we're using for this conversation. + * + * Note that since the state list is circular, the oldest + * state points to the newest and we only need to set + * xmit_oldest to update the lru linkage. + */ + comp->sls_o_misses++; + comp->xmit_oldest = lcs->cs_this; + cs->deny_compression = 0; + cs->lastdropped = 0; + PRINTK((KERN_DEBUG "ax25_vj.c: new slot %d\n", cs->cs_this)); + goto uncompressed; + + found: + /* + * Found it -- move to the front on the connection list. + */ + if (lcs == ocs) { + /* found at most recently used */ + } else if (cs == ocs) { + /* found at least recently used */ + comp->xmit_oldest = lcs->cs_this; + } else { + /* more than 2 elements */ + lcs->next = cs->next; + cs->next = ocs->next; + ocs->next = cs; + } + PRINTK((KERN_DEBUG "ax25_vj.c: found slot %d\n", cs->cs_this)); + /* + * Make sure that only what we expect to change changed. + * Check the following: + * IP protocol version, header length & type of service. + * The "Don't fragment" bit. + * The time-to-live field. + * The TCP header length. + * IP options, if any. + * TCP options, if any. + * If any of these things are different between the previous & + * current datagram, we send the current datagram `uncompressed'. + */ + oth = &cs->cs_tcp; + + if (iph->version != cs->cs_ip.version || iph->ihl != cs->cs_ip.ihl + || iph->tos != cs->cs_ip.tos + || (iph->frag_off & 64) != (cs->cs_ip.frag_off & 64) + || iph->ttl != cs->cs_ip.ttl + || th->doff != cs->cs_tcp.doff + || (iph->ihl > 5 && memcmp(iph + 1, cs->cs_ipopt, ((iph->ihl) - 5) * 4) != 0) + || (th->doff > 5 && memcmp(th + 1, cs->cs_tcpopt, ((th->doff) - 5) * 4) != 0)) { + PRINTK((KERN_DEBUG "ax25_vj.c: packet uncompressable\n")); + goto uncompressed; + } + /* + * Figure out which of the changing fields changed. The + * receiver expects changes in the order: urgent, window, + * ack, seq (the order minimizes the number of temporaries + * needed in this section of code). + */ + if (th->urg) { + deltaS = ntohs(th->urg_ptr); + cp = encode(cp, deltaS); + changes |= NEW_U; + } else if (th->urg_ptr != oth->urg_ptr) { + /* argh! URG not set but urp changed -- a sensible + * implementation should never do this but RFC793 + * doesn't prohibit the change so we have to deal + * with it. */ + goto uncompressed; + } + if ((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0) { + cp = encode(cp, deltaS); + changes |= NEW_W; + } + if ((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L) { + if (deltaA > 0x0000ffff) + goto uncompressed; + cp = encode(cp, deltaA); + changes |= NEW_A; + } + if ((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L) { + if (deltaS > 0x0000ffff) { + + /* + * - if the packet contains an old (outdated) seq and no + * new ack or window or urgent data, drop it (return 0) + */ + if (before(ntohl(th->seq), ntohl(oth->seq)) && !changes) { + if (cs->lastdropped != 0) { + if (jiffies - cs->lastdropped > 120 * HZ) { + goto uncompressed; + } + } else { + cs->lastdropped = jiffies; + } + PRINTK((KERN_DEBUG "ax25_vj.c: old packet, dS=%0x th.seq=%0x oth.seq=%0x\n", deltaS, ntohl(th->seq), ntohl(oth->seq))); + return 0; + } + goto uncompressed; + } + cp = encode(cp, deltaS); + changes |= NEW_S; + } + switch (changes) { + case 0: /* Nothing changed. If this packet contains data and the + * last one didn't, this is probably a data packet following + * an ack (normal on an interactive connection) and we send + * it compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. */ + if (iph->tot_len != cs->cs_ip.tot_len + && ntohs(cs->cs_ip.tot_len) == hlen) { + PRINTK((KERN_DEBUG "ax25_vj.c: data following ack\n")); + break; + } + /* + * MW: drop retransmitted packet. seq and ack did not change, + * check if flags have changed. + */ + if (th->fin != oth->fin || th->syn != oth->syn || th->rst != oth->rst + || th->ack != oth->ack) { + PRINTK((KERN_DEBUG "ax25_vj.c: tcp flags changed\n")); + goto uncompressed; + } + if (cs->lastdropped != 0) { + if (jiffies - cs->lastdropped > 120 * HZ) { + goto uncompressed; + } + } else { + cs->lastdropped = jiffies; + } + PRINTK((KERN_DEBUG "ax25_vj.c: no changes detected\n")); + return 0; + + case SPECIAL_I: + case SPECIAL_D: + /* actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto uncompressed; + case NEW_S | NEW_A: + if (deltaS == deltaA && + deltaS == ntohs(cs->cs_ip.tot_len) - hlen) { + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + case NEW_S: + if (deltaS == ntohs(cs->cs_ip.tot_len) - hlen) { + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + } + + /* + * The Packet contains new information, it has not been dropped + * until here. But compression has been denied, so we transmit an + * uncompressed packet instead. + */ + if (cs->deny_compression) { + goto uncompressed; + } + deltaS = ntohs(iph->id) - ntohs(cs->cs_ip.id); + if (deltaS != 1) { + cp = encode(cp, deltaS); + changes |= NEW_I; + } + if (th->psh) + changes |= TCP_PUSH_BIT; + /* Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + deltaA = ntohs(th->check); + memcpy(&cs->cs_ip, iph, 20); + memcpy(&cs->cs_tcp, th, 20); + cs->lastdropped = 0; + + /* + * MW: We don't actually perform the compression if we run on an + * uncompressible stream. + */ + if (!do_compression) { + cs->deny_compression = 1; + return AX25_P_IP; + } + /* We want to use the original packet as our compressed packet. + * (cp - new_seq) is the number of bytes we need for compressed + * sequence numbers. In addition we need one byte for the change + * mask, one for the connection id and two for the tcp checksum. + * So, (cp - new_seq) + 4 bytes of header are needed. + */ + deltaS = cp - new_seq; + skb_pull(skb, hlen); /* Strip TCP/IP headers */ + if (comp->xmit_current != cs->cs_this) { + cp = skb_push(skb, deltaS + 4); + *cp++ = changes | NEW_C; + *cp++ = cs->cs_this; + comp->xmit_current = cs->cs_this; + } else { + cp = skb_push(skb, deltaS + 3); + *cp++ = changes; + } + cp = put16(cp, (short) deltaA); /* Write TCP checksum */ + memcpy(cp, new_seq, deltaS); /* Write list of deltas */ + comp->sls_o_compressed++; + return AX25_P_VJCOMP; + + /* Update connection state cs & send uncompressed packet (i.e., + * a regular ip/tcp packet but with the 'conversation id' we hope + * to use on future compressed packets in the protocol field). + */ + uncompressed: + memcpy(&cs->cs_ip, iph, 20); + memcpy(&cs->cs_tcp, th, 20); + if (iph->ihl > 5) + memcpy(cs->cs_ipopt, iph + 1, ((iph->ihl) - 5) * 4); + if (th->doff > 5) + memcpy(cs->cs_tcpopt, th + 1, ((th->doff) - 5) * 4); + comp->xmit_current = cs->cs_this; + cs->lastdropped = 0; + + if (!do_compression) { + cs->deny_compression = 1; + return AX25_P_IP; + } + iph->protocol = cs->cs_this; + cs->deny_compression = 0; + comp->sls_o_uncompressed++; + return AX25_P_VJUNCOMP; +} + +int axhc_uncompress(struct axvj_slcomp *comp, struct sk_buff *skb) +{ + register int changes; + long x; + register struct tcphdr *thp; + register struct iphdr *ip; + register struct axvj_cstate *cs; + int len, hdrlen; + + int isize = skb->len; + unsigned char *icp = skb->data; + unsigned char *cp = icp; + + /* We've got a compressed packet; read the change byte */ + comp->sls_i_compressed++; + if (isize < 3) { + comp->sls_i_error++; + return 0; + } + changes = *cp++; + if (changes & NEW_C) { + /* Make sure the state index is in range, then grab the state. + * If we have a good state index, clear the 'discard' flag. + */ + x = *cp++; /* Read conn index */ + if (x < 0 || x > comp->rslot_limit) + goto bad; + + comp->flags &= ~SLF_TOSS; + comp->recv_current = x; + } else { + /* this packet has an implicit state index. If we've + * had a line error since the last time we got an + * explicit state index, we have to toss the packet. */ + if (comp->flags & SLF_TOSS) { + comp->sls_i_tossed++; + return 0; + } + } + cs = &comp->rstate[comp->recv_current]; + thp = &cs->cs_tcp; + ip = &cs->cs_ip; + + if ((x = pull16(&cp)) == -1) { /* Read the TCP checksum */ + goto bad; + } + thp->check = htons(x); + + thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0; +/* + * we can use the same number for the length of the saved header and + * the current one, because the packet wouldn't have been sent + * as compressed unless the options were the same as the previous one + */ + + hdrlen = ip->ihl * 4 + thp->doff * 4; + + switch (changes & SPECIALS_MASK) { + case SPECIAL_I: /* Echoed terminal traffic */ + { + register short i; + i = ntohs(ip->tot_len) - hdrlen; + thp->ack_seq = htonl(ntohl(thp->ack_seq) + i); + thp->seq = htonl(ntohl(thp->seq) + i); + } + break; + + case SPECIAL_D: /* Unidirectional data */ + thp->seq = htonl(ntohl(thp->seq) + + ntohs(ip->tot_len) - hdrlen); + break; + + default: + if (changes & NEW_U) { + thp->urg = 1; + if ((x = decode(&cp)) == -1) { + goto bad; + } + thp->urg_ptr = htons(x); + } else + thp->urg = 0; + if (changes & NEW_W) { + if ((x = decode(&cp)) == -1) { + goto bad; + } + thp->window = htons(ntohs(thp->window) + x); + } + if (changes & NEW_A) { + if ((x = decode(&cp)) == -1) { + goto bad; + } + thp->ack_seq = htonl(ntohl(thp->ack_seq) + x); + } + if (changes & NEW_S) { + if ((x = decode(&cp)) == -1) { + goto bad; + } + thp->seq = htonl(ntohl(thp->seq) + x); + } + break; + } + if (changes & NEW_I) { + if ((x = decode(&cp)) == -1) { + goto bad; + } + ip->id = htons(ntohs(ip->id) + x); + } else + ip->id = htons(ntohs(ip->id) + 1); + + /* + * At this point, cp points to the first byte of data in the + * packet. Put the reconstructed TCP and IP headers back on the + * packet. Recalculate IP checksum (but not TCP checksum). + */ + + len = isize - (cp - icp); + if (len < 0) + goto bad; + len += hdrlen; + ip->tot_len = htons(len); + ip->check = 0; + + /* + * MW: + * we are working on sk_buffs here, so we can spare the memmove() + * and simply skb_push() the hdrlen + */ + + skb_push(skb, hdrlen - (cp - icp)); + + cp = icp = skb->data; + memcpy(cp, ip, 20); + cp += 20; + + if (ip->ihl > 5) { + memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4); + cp += (ip->ihl - 5) * 4; + } + put_unaligned(ip_fast_csum(icp, ip->ihl), + &((struct iphdr *) icp)->check); + + memcpy(cp, thp, 20); + cp += 20; + + if (thp->doff > 5) { + memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4); + cp += ((thp->doff) - 5) * 4; + } + return len; + bad: + comp->sls_i_error++; + return axhc_toss(comp); +} + + +int axhc_remember(struct axvj_slcomp *comp, struct sk_buff *skb) +{ + register struct axvj_cstate *cs; + unsigned ihl; + + unsigned char index; + + int isize = skb->len; + unsigned char *icp = skb->data; + + if (isize < 20) { + /* The packet is shorter than a legal IP header */ + printk(KERN_DEBUG "axhc_remember: short packet from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr)); + comp->sls_i_runt++; + return axhc_toss(comp); + } + /* Peek at the IP header's IHL field to find its length */ + ihl = icp[0] & 0xf; + if (ihl < 20 / 4) { + /* The IP header length field is too small */ + printk(KERN_DEBUG "axhc_remember: ihl too small from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr)); + comp->sls_i_runt++; + return axhc_toss(comp); + } + index = icp[9]; + icp[9] = IPPROTO_TCP; + + if (ip_fast_csum(icp, ihl)) { + /* Bad IP header checksum; discard */ + printk(KERN_DEBUG "axhc_remember: bad ip header checksum from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr)); + comp->sls_i_badcheck++; + return axhc_toss(comp); + } + if (index > comp->rslot_limit) { + printk(KERN_DEBUG "axhc_remember: illegal slot from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr)); + comp->sls_i_error++; + return axhc_toss(comp); + } + /* Update local state */ + cs = &comp->rstate[comp->recv_current = index]; + comp->flags &= ~SLF_TOSS; + memcpy(&cs->cs_ip, icp, 20); + memcpy(&cs->cs_tcp, icp + ihl * 4, 20); + if (ihl > 5) + memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4); + if (cs->cs_tcp.doff > 5) + memcpy(cs->cs_tcpopt, icp + ihl * 4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4); + cs->cs_hsize = ihl * 2 + cs->cs_tcp.doff * 2; + /* Put headers back on packet + * Neither header checksum is recalculated + */ + comp->sls_i_uncompressed++; + return isize; +} + +static int +axhc_toss(struct axvj_slcomp *comp) +{ + if ( comp == NULL ) + return 0; + + comp->flags |= SLF_TOSS; + return 0; +} + +#endif Index: linux/net/ax25/ax25_vj.h diff -u /dev/null linux/net/ax25/ax25_vj.h:1.1.4.1 --- /dev/null Wed Jul 7 01:02:33 1999 +++ linux/net/ax25/ax25_vj.h Sun Jul 4 13:03:45 1999 @@ -0,0 +1,73 @@ +/* + * Interface declaration of the VJ compression code + * + * Matthias Welwarsky (DG2FEF) 05/25/98 + */ + +#ifndef _AX25_VJ_H +#define _AX25_VJ_H + +#include +#include + +/* + * MW: copied this into ax25.h to unclobber the original struct cstate. + * "state" data for each active tcp conversation on the "wire". This is + * basically a copy of the entire IP/TCP header from the last packet + * we saw from the conversation together with a small identifier + * the transmit & receive ends of the line use to locate saved header. + */ +struct axvj_cstate { + unsigned char cs_this; /* connection id number (xmit) */ + struct axvj_cstate *next; /* next in ring (xmit) */ + struct iphdr cs_ip; /* ip/tcp hdr from most recent packet */ + struct tcphdr cs_tcp; + unsigned char cs_ipopt[64]; + unsigned char cs_tcpopt[64]; + int cs_hsize; + unsigned char deny_compression; /* MW: for vj compression via AX.25 */ + unsigned long lastdropped; +}; + +/* + * all the state data for one VC (we need one of these per VC). + */ +struct axvj_slcomp { + struct axvj_cstate *tstate; /* transmit connection states (array)*/ + struct axvj_cstate *rstate; /* receive connection states (array)*/ + + unsigned char tslot_limit; /* highest transmit slot id (0-l)*/ + unsigned char rslot_limit; /* highest receive slot id (0-l)*/ + + unsigned char xmit_oldest; /* oldest xmit in ring */ + unsigned char xmit_current; /* most recent xmit id */ + unsigned char recv_current; /* most recent rcvd id */ + + unsigned char flags; +#define SLF_TOSS 0x01 /* tossing rcvd frames until id received */ + + int sls_o_nontcp; /* outbound non-TCP packets */ + int sls_o_tcp; /* outbound TCP packets */ + int sls_o_uncompressed; /* outbound uncompressed packets */ + int sls_o_compressed; /* outbound compressed packets */ + int sls_o_searches; /* searches for connection state */ + int sls_o_misses; /* times couldn't find conn. state */ + + int sls_i_uncompressed; /* inbound uncompressed packets */ + int sls_i_compressed; /* inbound compressed packets */ + int sls_i_error; /* inbound error packets */ + int sls_i_tossed; /* inbound packets tossed because of error */ + + int sls_i_runt; + int sls_i_badcheck; +}; + +extern struct axvj_slcomp* axhc_init(int, int); +extern void axhc_free(struct axvj_slcomp*); +extern int axhc_compress(struct axvj_slcomp *comp, struct sk_buff *skb, int do_compression); +extern void ax25_vj_init(void); +extern void ax25_vj_cleanup(void); +extern int axhc_uncompress(struct axvj_slcomp *comp, struct sk_buff *skb); +extern int axhc_remember(struct axvj_slcomp *comp, struct sk_buff *skb); + +#endif Index: linux/net/ax25/sysctl_net_ax25.c diff -u linux/net/ax25/sysctl_net_ax25.c:1.1.2.1 linux/net/ax25/sysctl_net_ax25.c:1.1.2.1.2.1 --- linux/net/ax25/sysctl_net_ax25.c:1.1.2.1 Sun Jul 4 10:18:11 1999 +++ linux/net/ax25/sysctl_net_ax25.c Sun Jul 4 11:32:32 1999 @@ -1,14 +1,18 @@ -/* -*- linux-c -*- +/* * sysctl_net_ax25.c: sysctl interface to net AX.25 subsystem. * * Begun April 1, 1996, Mike Shaver. * Added /proc/sys/net/ax25 directory entry (empty =) ). [MS] */ -#include #include #include +#include + #include +#include + +#include "ax25_ddi.h" static int min_ipdefmode[] = {0}, max_ipdefmode[] = {1}; static int min_axdefmode[] = {0}, max_axdefmode[] = {1}; @@ -16,22 +20,19 @@ static int min_conmode[] = {0}, max_conmode[] = {2}; static int min_window[] = {1}, max_window[] = {7}; static int min_ewindow[] = {1}, max_ewindow[] = {63}; -static int min_t1[] = {1}, max_t1[] = {30 * HZ}; -static int min_t2[] = {1}, max_t2[] = {20 * HZ}; -static int min_t3[] = {0}, max_t3[] = {3600 * HZ}; -static int min_idle[] = {0}, max_idle[] = {65535 * HZ}; +static int min_t1[] = {1}, max_t1[] = {30 * AX25_SLOWHZ}; +static int min_t2[] = {0}, max_t2[] = {20 * AX25_SLOWHZ}; +static int min_t3[] = {0}, max_t3[] = {3600 * AX25_SLOWHZ}; +static int min_idle[] = {0}, max_idle[] = {65535 * AX25_SLOWHZ}; static int min_n2[] = {1}, max_n2[] = {31}; static int min_paclen[] = {1}, max_paclen[] = {512}; -static int min_proto[] = {0}, max_proto[] = {3}; -static int min_ds_timeout[] = {0}, max_ds_timeout[] = {65535 * HZ}; static struct ctl_table_header *ax25_table_header; -static ctl_table *ax25_table = NULL; -static int ax25_table_size = 0; +static ctl_table ax25_table[AX25_MAX_DEVICES + 1]; static ctl_table ax25_dir_table[] = { - {NET_AX25, "ax25", NULL, 0, 0555, NULL}, + {NET_AX25, "ax25", NULL, 0, 0555, ax25_table}, {0} }; @@ -89,69 +90,48 @@ NULL, sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_paclen, &max_paclen}, - {NET_AX25_PROTOCOL, "protocol", - NULL, sizeof(int), 0644, NULL, - &proc_dointvec_minmax, &sysctl_intvec, NULL, - &min_proto, &max_proto}, - {NET_AX25_DAMA_SLAVE_TIMEOUT, "dama_slave_timeout", - NULL, sizeof(int), 0644, NULL, - &proc_dointvec_minmax, &sysctl_intvec, NULL, - &min_ds_timeout, &max_ds_timeout}, {0} /* that's all, folks! */ }; void ax25_register_sysctl(void) { - ax25_dev *ax25_dev; - int n, k; - - for (ax25_table_size = sizeof(ctl_table), ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) - ax25_table_size += sizeof(ctl_table); - - if ((ax25_table = kmalloc(ax25_table_size, GFP_ATOMIC)) == NULL) - return; + struct device *dev; + struct ax25_dev *ax25_device; - memset(ax25_table, 0x00, ax25_table_size); + int n, k, i; - for (n = 0, ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) { - ax25_table[n].ctl_name = n + 1; - ax25_table[n].procname = ax25_dev->dev->name; - ax25_table[n].data = NULL; - ax25_table[n].maxlen = 0; - ax25_table[n].mode = 0555; - ax25_table[n].child = ax25_dev->systable; - ax25_table[n].proc_handler = NULL; + memset(ax25_table, 0x00, (AX25_MAX_DEVICES + 1) * sizeof(ctl_table)); - memcpy(ax25_dev->systable, ax25_param_table, sizeof(ax25_dev->systable)); + n = 0; -#ifndef CONFIG_AX25_DAMA_SLAVE - /* - * We do not wish to have a representation of this parameter - * in /proc/sys/ when configured *not* to include the - * AX.25 DAMA slave code, do we? - */ - - ax25_dev->systable[AX25_VALUES_DS_TIMEOUT].procname = NULL; -#endif - - ax25_dev->systable[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */ - - for (k = 0; k < AX25_MAX_VALUES; k++) - ax25_dev->systable[k].data = &ax25_dev->values[k]; - - n++; + for (i = 0; i < AX25_MAX_DEVICES; i++) { + if ((dev = ax25_devices[i]) != NULL) { + ax25_device = AX25_PTR(dev); + if (n <= AX25_MAX_DEVICES) { + ax25_table[n].ctl_name = n + 1; + ax25_table[n].procname = dev->name; + ax25_table[n].data = NULL; + ax25_table[n].maxlen = 0; + ax25_table[n].mode = 0555; + ax25_table[n].child = ax25_device->systable; + ax25_table[n].proc_handler = NULL; + + memcpy(ax25_device->systable, ax25_param_table, sizeof(ax25_device->systable)); + + ax25_device->systable[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */ + + for (k = 0; k < AX25_MAX_VALUES; k++) + ax25_device->systable[k].data = &ax25_device->values[k]; + + n++; + } + } } - ax25_dir_table[0].child = ax25_table; - ax25_table_header = register_sysctl_table(ax25_root_table, 1); } void ax25_unregister_sysctl(void) { unregister_sysctl_table(ax25_table_header); - - kfree(ax25_table); - - ax25_dir_table[0].child = NULL; } Index: linux/net/ipv4/arp.c diff -u linux/net/ipv4/arp.c:1.1.2.1 linux/net/ipv4/arp.c:1.1.2.1.2.1 --- linux/net/ipv4/arp.c:1.1.2.1 Sun Jul 4 10:18:15 1999 +++ linux/net/ipv4/arp.c Sun Jul 4 11:32:32 1999 @@ -247,7 +247,7 @@ in old paradigm. */ -#if 1 +#if 0 /* So... these "amateur" devices are hopeless. The only thing, that I can say now: It is very sad that we need to keep ugly obsolete Index: linux/net/netrom/af_netrom.c diff -u linux/net/netrom/af_netrom.c:1.1.2.1 linux/net/netrom/af_netrom.c:1.1.2.1.2.1 --- linux/net/netrom/af_netrom.c:1.1.2.1 Sun Jul 4 10:18:30 1999 +++ linux/net/netrom/af_netrom.c Sun Jul 4 11:32:32 1999 @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include Index: linux/net/netrom/nr_route.c diff -u linux/net/netrom/nr_route.c:1.1.2.1 linux/net/netrom/nr_route.c:1.1.2.1.2.1 --- linux/net/netrom/nr_route.c:1.1.2.1 Sun Jul 4 10:18:31 1999 +++ linux/net/netrom/nr_route.c Sun Jul 4 11:32:32 1999 @@ -17,7 +17,6 @@ * Change default quality for new neighbour when same * as node callsign. * Alan Cox(GW4PTS) Added the firewall hooks. - * NET/ROM 006 Jonathan(G4KLX) Added the setting of digipeated neighbours. * Tomi(OH2BNS) Routing quality and link failure changes. */ @@ -61,8 +60,8 @@ * Add a new route to a node, and in the process add the node and the * neighbour if it is new. */ -static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax25, - ax25_digi *ax25_digi, struct device *dev, int quality, int obs_count) +static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_addr_t *ax25_addr, + struct device *dev, int quality, int obs_count) { struct nr_node *nr_node; struct nr_neigh *nr_neigh; @@ -78,7 +77,7 @@ break; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(ax25, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) + if (ax25cmp(&ax25_addr->dest, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) break; /* @@ -107,8 +106,8 @@ if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; - nr_neigh->callsign = *ax25; - nr_neigh->digipeat = NULL; + nr_neigh->addr = *ax25_addr; + nr_neigh->addr.src = *((ax25_address*)dev->dev_addr); nr_neigh->ax25 = NULL; nr_neigh->dev = dev; nr_neigh->quality = sysctl_netrom_default_path_quality; @@ -117,14 +116,6 @@ nr_neigh->number = nr_neigh_no++; nr_neigh->failed = 0; - if (ax25_digi != NULL && ax25_digi->ndigi > 0) { - if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) { - kfree(nr_neigh); - return -ENOMEM; - } - *nr_neigh->digipeat = *ax25_digi; - } - save_flags(flags); cli(); @@ -134,7 +125,7 @@ restore_flags(flags); } - if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked) + if (quality != 0 && ax25cmp(nr, &ax25_addr->dest) == 0 && !nr_neigh->locked) nr_neigh->quality = quality; if (nr_node == NULL) { @@ -295,8 +286,6 @@ if ((s = nr_neigh_list) == nr_neigh) { nr_neigh_list = nr_neigh->next; restore_flags(flags); - if (nr_neigh->digipeat != NULL) - kfree(nr_neigh->digipeat); kfree(nr_neigh); return; } @@ -305,8 +294,6 @@ if (s->next == nr_neigh) { s->next = nr_neigh->next; restore_flags(flags); - if (nr_neigh->digipeat != NULL) - kfree(nr_neigh->digipeat); kfree(nr_neigh); return; } @@ -334,7 +321,7 @@ if (nr_node == NULL) return -EINVAL; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(neighbour, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) + if (ax25cmp(neighbour, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) break; if (nr_neigh == NULL) return -EINVAL; @@ -371,13 +358,13 @@ /* * Lock a neighbour with a quality. */ -static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct device *dev, unsigned int quality) +static int nr_add_neigh(ax25_addr_t *ax25_addr, struct device *dev, unsigned int quality) { struct nr_neigh *nr_neigh; unsigned long flags; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) { - if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) { + if (ax25cmp(&ax25_addr->dest, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) { nr_neigh->quality = quality; nr_neigh->locked = 1; return 0; @@ -387,8 +374,8 @@ if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; - nr_neigh->callsign = *callsign; - nr_neigh->digipeat = NULL; + nr_neigh->addr = *ax25_addr; + nr_neigh->addr.src = *((ax25_address*)dev->dev_addr); nr_neigh->ax25 = NULL; nr_neigh->dev = dev; nr_neigh->quality = quality; @@ -397,14 +384,6 @@ nr_neigh->number = nr_neigh_no++; nr_neigh->failed = 0; - if (ax25_digi != NULL && ax25_digi->ndigi > 0) { - if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) { - kfree(nr_neigh); - return -ENOMEM; - } - *nr_neigh->digipeat = *ax25_digi; - } - save_flags(flags); cli(); @@ -425,7 +404,7 @@ struct nr_neigh *nr_neigh; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) + if (ax25cmp(callsign, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) break; if (nr_neigh == NULL) return -EINVAL; @@ -592,23 +571,18 @@ return dev; } -static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters) +static ax25_addr_t *nr_neigh_to_addr(ax25_address *callsign, int ndigis, ax25_address *digipeater) { - static ax25_digi ax25_digi; + static ax25_addr_t ax25_addr; int i; - if (ndigis == 0) - return NULL; - - for (i = 0; i < ndigis; i++) { - ax25_digi.calls[i] = digipeaters[i]; - ax25_digi.repeated[i] = 0; - } + ax25_addr.dest = *callsign; + ax25_addr.dcount = ndigis; - ax25_digi.ndigi = ndigis; - ax25_digi.lastrepeat = -1; + for (i = 0; i < ndigis; i++) + ax25_addr.digipeater[i] = digipeater[i]; - return &ax25_digi; + return &ax25_addr; } /* @@ -630,15 +604,12 @@ return -EINVAL; switch (nr_route.type) { case NETROM_NODE: - return nr_add_node(&nr_route.callsign, - nr_route.mnemonic, - &nr_route.neighbour, - nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters), + return nr_add_node(&nr_route.callsign, nr_route.mnemonic, + nr_neigh_to_addr(&nr_route.neighbour, nr_route.ndigis, nr_route.digipeaters), dev, nr_route.quality, nr_route.obs_count); case NETROM_NEIGH: - return nr_add_neigh(&nr_route.callsign, - nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters), + return nr_add_neigh(nr_neigh_to_addr(&nr_route.callsign, nr_route.ndigis, nr_route.digipeaters), dev, nr_route.quality); default: return -EINVAL; @@ -716,8 +687,8 @@ nr_dest = (ax25_address *)(skb->data + 7); if (ax25 != NULL) - nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat, - ax25->ax25_dev->dev, 0, sysctl_netrom_obsolescence_count_initialiser); + nr_add_node(nr_src, "", &ax25->addr, ax25->device, 0, + sysctl_netrom_obsolescence_count_initialiser); if ((dev = nr_dev_get(nr_dest)) != NULL) { /* Its for me */ if (ax25 == NULL) /* Its from me */ @@ -751,7 +722,7 @@ dptr = skb_push(skb, 1); *dptr = AX25_P_NETROM; - nr_neigh->ax25 = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); + nr_neigh->ax25 = ax25_send_frame(skb, 256, &nr_neigh->addr, nr_neigh->dev); return (nr_neigh->ax25 != NULL); } @@ -822,16 +793,16 @@ for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) { len += sprintf(buffer + len, "%05d %-9s %-4s %3d %d %3d %3d", nr_neigh->number, - ax2asc(&nr_neigh->callsign), + ax2asc(&nr_neigh->addr.dest), nr_neigh->dev ? nr_neigh->dev->name : "???", nr_neigh->quality, nr_neigh->locked, nr_neigh->count, nr_neigh->failed); - if (nr_neigh->digipeat != NULL) { - for (i = 0; i < nr_neigh->digipeat->ndigi; i++) - len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->digipeat->calls[i])); + if (nr_neigh->addr.dcount) { + for (i = 0; i < nr_neigh->addr.dcount; i++) + len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->addr.digipeater[i])); } len += sprintf(buffer + len, "\n"); Index: linux/net/rose/af_rose.c diff -u linux/net/rose/af_rose.c:1.1.2.1 linux/net/rose/af_rose.c:1.1.2.1.2.1 --- linux/net/rose/af_rose.c:1.1.2.1 Sun Jul 4 10:18:32 1999 +++ linux/net/rose/af_rose.c Sun Jul 4 11:32:32 1999 @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include Index: linux/net/rose/rose_link.c diff -u linux/net/rose/rose_link.c:1.1.2.1 linux/net/rose/rose_link.c:1.1.2.1.2.1 --- linux/net/rose/rose_link.c:1.1.2.1 Sun Jul 4 10:18:32 1999 +++ linux/net/rose/rose_link.c Sun Jul 4 11:32:32 1999 @@ -106,14 +106,12 @@ */ static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh) { - ax25_address *rose_call; - if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) - rose_call = (ax25_address *)neigh->dev->dev_addr; + neigh->addr.src = *((ax25_address *)neigh->dev->dev_addr); else - rose_call = &rose_callsign; + neigh->addr.src = rose_callsign; - neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + neigh->ax25 = ax25_send_frame(skb, 260, &neigh->addr, neigh->dev); return (neigh->ax25 != NULL); } @@ -125,14 +123,12 @@ */ static int rose_link_up(struct rose_neigh *neigh) { - ax25_address *rose_call; - if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) - rose_call = (ax25_address *)neigh->dev->dev_addr; + neigh->addr.src = *((ax25_address *)neigh->dev->dev_addr); else - rose_call = &rose_callsign; + neigh->addr.src = rose_callsign; - neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + neigh->ax25 = ax25_find_cb(&neigh->addr, neigh->dev); return (neigh->ax25 != NULL); } Index: linux/net/rose/rose_route.c diff -u linux/net/rose/rose_route.c:1.1.2.1 linux/net/rose/rose_route.c:1.1.2.1.2.1 --- linux/net/rose/rose_route.c:1.1.2.1 Sun Jul 4 10:18:32 1999 +++ linux/net/rose/rose_route.c Sun Jul 4 11:32:32 1999 @@ -78,23 +78,23 @@ return -EINVAL; for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) - if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev) + if (ax25cmp(&rose_route->neighbour, &rose_neigh->addr.dest) == 0 && rose_neigh->dev == dev) break; if (rose_neigh == NULL) { if ((rose_neigh = kmalloc(sizeof(*rose_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; - rose_neigh->callsign = rose_route->neighbour; - rose_neigh->digipeat = NULL; - rose_neigh->ax25 = NULL; - rose_neigh->dev = dev; - rose_neigh->count = 0; - rose_neigh->use = 0; - rose_neigh->dce_mode = 0; - rose_neigh->loopback = 0; - rose_neigh->number = rose_neigh_no++; - rose_neigh->restarted = 0; + rose_neigh->addr.dest = rose_route->neighbour; + rose_neigh->addr.dcount = 0; + rose_neigh->ax25 = NULL; + rose_neigh->dev = dev; + rose_neigh->count = 0; + rose_neigh->use = 0; + rose_neigh->dce_mode = 0; + rose_neigh->loopback = 0; + rose_neigh->number = rose_neigh_no++; + rose_neigh->restarted = 0; skb_queue_head_init(&rose_neigh->queue); @@ -102,18 +102,11 @@ init_timer(&rose_neigh->t0timer); if (rose_route->ndigis != 0) { - if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) { - kfree(rose_neigh); - return -ENOMEM; - } - - rose_neigh->digipeat->ndigi = rose_route->ndigis; - rose_neigh->digipeat->lastrepeat = -1; + rose_neigh->addr.dcount = rose_route->ndigis; + rose_neigh->addr.lastrepeat = -1; - for (i = 0; i < rose_route->ndigis; i++) { - rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i]; - rose_neigh->digipeat->repeated[i] = 0; - } + for (i = 0; i < rose_route->ndigis; i++) + rose_neigh->addr.digipeater[i] = rose_route->digipeaters[i]; } save_flags(flags); cli(); @@ -235,8 +228,6 @@ if ((s = rose_neigh_list) == rose_neigh) { rose_neigh_list = rose_neigh->next; restore_flags(flags); - if (rose_neigh->digipeat != NULL) - kfree(rose_neigh->digipeat); kfree(rose_neigh); return; } @@ -245,8 +236,6 @@ if (s->next == rose_neigh) { s->next = rose_neigh->next; restore_flags(flags); - if (rose_neigh->digipeat != NULL) - kfree(rose_neigh->digipeat); kfree(rose_neigh); return; } @@ -310,7 +299,7 @@ if (rose_node->loopback) return -EINVAL; for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) - if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev) + if (ax25cmp(&rose_route->neighbour, &rose_neigh->addr.dest) == 0 && rose_neigh->dev == dev) break; if (rose_neigh == NULL) return -EINVAL; @@ -354,16 +343,16 @@ if ((rose_loopback_neigh = kmalloc(sizeof(struct rose_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; - rose_loopback_neigh->callsign = null_ax25_address; - rose_loopback_neigh->digipeat = NULL; - rose_loopback_neigh->ax25 = NULL; - rose_loopback_neigh->dev = NULL; - rose_loopback_neigh->count = 0; - rose_loopback_neigh->use = 0; - rose_loopback_neigh->dce_mode = 1; - rose_loopback_neigh->loopback = 1; - rose_loopback_neigh->number = rose_neigh_no++; - rose_loopback_neigh->restarted = 1; + rose_loopback_neigh->addr.dest = null_ax25_address; + rose_loopback_neigh->addr.dcount = 0; + rose_loopback_neigh->ax25 = NULL; + rose_loopback_neigh->dev = NULL; + rose_loopback_neigh->count = 0; + rose_loopback_neigh->use = 0; + rose_loopback_neigh->dce_mode = 1; + rose_loopback_neigh->loopback = 1; + rose_loopback_neigh->number = rose_neigh_no++; + rose_loopback_neigh->restarted = 1; skb_queue_head_init(&rose_loopback_neigh->queue); @@ -758,11 +747,11 @@ dest_addr = (rose_address *)(skb->data + 4); for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) - if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->ax25_dev->dev == rose_neigh->dev) + if (ax25cmp(&ax25->addr.dest, &rose_neigh->addr.dest) == 0 && ax25->device == rose_neigh->dev) break; if (rose_neigh == NULL) { - printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->dest_addr)); + printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->addr.dest)); return 0; } @@ -1004,7 +993,7 @@ /* if (!rose_neigh->loopback) { */ len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3d %3s %3s %3lu %3lu", rose_neigh->number, - (rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(&rose_neigh->callsign), + (rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(&rose_neigh->addr.dest), rose_neigh->dev ? rose_neigh->dev->name : "???", rose_neigh->count, rose_neigh->use, @@ -1013,9 +1002,9 @@ ax25_display_timer(&rose_neigh->t0timer) / HZ, ax25_display_timer(&rose_neigh->ftimer) / HZ); - if (rose_neigh->digipeat != NULL) { - for (i = 0; i < rose_neigh->digipeat->ndigi; i++) - len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->digipeat->calls[i])); + if (rose_neigh->addr.dcount != 0) { + for (i = 0; i < rose_neigh->addr.dcount; i++) + len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->addr.digipeater[i])); } len += sprintf(buffer + len, "\n");