/* * 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. * * 2000-09-21 adapted for kernel 2.4.0, it however still crashes * badly when killing kissattach and trying to re- * start the interface. Does the original SLIP driver * do the same? * */ #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" #define KISS_VERSION "KISS-NET3.019-NEWTTY" static int maxdev = KISS_NRUNIT; static kiss_ctrl_t **kiss_ctrls = NULL; 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 net_device *dev); static int kiss_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg); static int kiss_dev_ioctl(struct net_device *dev,struct ifreq *rq,int cmd); static int kiss_dev_set_mac_address(struct net_device *dev, void *addr); static void kiss_parameter_change_notify(struct net_device *dev, int valueno, int old, int new); static void kiss_update_parameters(struct net_device *dev); static void kiss_send_parameter(struct net_device *dev, unsigned char param, unsigned char value); static spinlock_t ks_ctrl_lock = SPIN_LOCK_UNLOCKED; /*---------------------------------------------------------------------------*/ 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; spin_lock_bh(&ks->lock); if (ks->tty == NULL) { 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); err = 0; /* Cleanup */ err_exit: spin_unlock_bh(&ks->lock); 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 net_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; } spin_lock_bh(&ks->lock); 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->stats.tx_dropped++; } } ks->xhead = ks->xbuff; if (ks->rcount) { if (ks->rcount <= len) { memcpy(ks->rbuff, rbuff, ks->rcount); } else { ks->rcount = 0; ks->stats.rx_over_errors++; ks->flags.error=1; } } ks->mtu = mtu + MAX_HEADER;; dev->mtu = mtu; ks->buffsize = len; err = 0; done_on_bh: spin_unlock_bh(&ks->lock); done: if (xbuff) kfree(xbuff); if (rbuff) kfree(rbuff); return err; } /* Send one completely decapsulated IP datagram to the IP layer. */ static void kiss_bump(struct kiss *ks) { struct sk_buff *skb; int count; if (ks->mode == KISS_MODE_ADAPTIVE) { if (ks->rbuff[0] & 0x20) { ks->mode = KISS_MODE_FLEX; } else if (ks->rbuff[0] & 0x80) { ks->mode = KISS_MODE_SMACK; } } if (ks->mode == KISS_MODE_FLEX) { if (check_crc_flex(ks->rbuff, ks->rcount) < 0) { ks->stats.rx_errors++; return; } ks->rcount -= 2; } else if (ks->mode == KISS_MODE_SMACK) { if (check_crc_16(ks->rbuff, ks->rcount) < 0) { ks->stats.rx_errors++; return; } ks->rcount -= 2; } count = ks->rcount-1; ks->stats.rx_bytes += count; skb = dev_alloc_skb(count+MAX_HEADER); if (skb == NULL) { printk("%s: memory squeeze, dropping packet.\n", ks->dev->name); ks->stats.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->stats.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: dropping oversized transmit packet!\n", ks->dev->name); ks->stats.tx_dropped++; netif_wake_queue(ks->dev); return; } p = icp; if (*p == 0) { /* KISS data */ 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; case KISS_MODE_SMACK: *p |= 0x80; crc = calc_crc_16(p, len); count = kiss_esc_crc(p, ks->xbuff, crc, len+2); break; default: count = kiss_esc(p, ks->xbuff, len); break; } } else { /* KISS parameter */ ks->xbuff[0] = END; memcpy(&ks->xbuff[1], p, len); ks->xbuff[len+1] = END; count = len+2; } /* 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); ks->dev->trans_start = jiffies; 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 || !netif_running(ks->dev)) { return; } if (ks->xleft <= 0) { /* Now serial buffer is almost free & we can start * transmission of another packet */ ks->stats.tx_packets++; tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); netif_wake_queue(ks->dev); 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 net_device *dev) { struct kiss *ks = (struct kiss*)(dev->priv); spin_lock(&ks->lock); /* ldisc disappeared under our feet, ouch! */ if (ks->tty == NULL) goto done; /* rant by dl1bke: * There used to be a completely useless copy of * the skb here. If there really _is_ a race condition * fix the upper protocols, dammit! Masquearading these * problems doesn't help at all, they'll rear their ugly * head somewhere else again anyway! */ /* We were not busy, so we are now... :-) */ if (skb_headroom(skb) > 0) { *skb_push(skb, 1) = 0; /* prepend KISS header */ netif_stop_queue(dev); ks->stats.tx_bytes+=skb->len; kiss_encaps(ks, skb->data, skb->len); } else { ks->stats.tx_dropped++; } done: dev_kfree_skb(skb); spin_unlock(&ks->lock); return 0; } /****************************************** * Routines looking at netdevice side. ******************************************/ /* Netdevice UP -> DOWN routine */ static int kiss_dev_close(struct net_device *dev) { struct kiss *ks = (struct kiss*)(dev->priv); spin_lock_bh(&ks->lock); if (ks->tty) { /* TTY discipline is running. */ ks->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); } ks->rcount = 0; ks->xleft = 0; netif_stop_queue(dev); spin_unlock_bh(&ks->lock); MOD_DEC_USE_COUNT; return 0; } /* Netdevice DOWN -> UP routine */ static int kiss_dev_open(struct net_device *dev) { struct kiss *ks = (struct kiss*)(dev->priv); if (ks->tty==NULL) return -ENODEV; ks->flags.in_use = 1; ks->flags.error = 0; ks->flags.escape = 0; MOD_INC_USE_COUNT; netif_start_queue(dev); return 0; } /* Netdevice change MTU request */ static int kiss_change_mtu(struct net_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 net_device *dev) { struct kiss *ks = (struct kiss*)(dev->priv); return (&ks->stats); } /* Netdevice register callback */ static int kiss_init(struct net_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; dev->do_ioctl = kiss_dev_ioctl; dev->set_mac_address = kiss_dev_set_mac_address; 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_PTR(dev) = &ks->ax25dev; memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev)); AX25_PTR(dev)->hw.fast = 1; AX25_PTR(dev)->hw.dcd = NULL; AX25_PTR(dev)->hw.ptt = NULL; AX25_PTR(dev)->hw.cts = NULL; AX25_PTR(dev)->hw.rts = NULL; AX25_PTR(dev)->hw.parameter_change_notify = kiss_parameter_change_notify; ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 0); ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, 200); ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, 20); ax25_dev_set_value(dev, AX25_VALUES_MEDIA_SLOTTIME, 10); ax25_dev_set_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE, 64); ax25_dev_set_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST, 0); return 0; } static void kiss_send_parameter(struct net_device *dev, unsigned char param, unsigned char value) { struct kiss *ks = (struct kiss *) dev->priv; unsigned char buf[2] = {param, value}; kiss_encaps(ks, buf, 2); return; } static void kiss_update_parameters(struct net_device *dev) { kiss_send_parameter(dev, KISS_PARAM_TXDELAY, ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY)/10); kiss_send_parameter(dev, KISS_PARAM_PPERS, ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE)); kiss_send_parameter(dev, KISS_PARAM_SLOT, ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME)/10); kiss_send_parameter(dev, KISS_PARAM_DUPLEX, ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX)); kiss_send_parameter(dev, KISS_PARAM_TXTAIL, ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL)/10); return; } static void kiss_parameter_change_notify(struct net_device *dev, int valueno, int old, int new) { struct kiss *ks = (struct kiss *) dev->priv; int br; switch (valueno) { case AX25_VALUES_MEDIA_RXBITRATE: case AX25_VALUES_MEDIA_TXBITRATE: /* * If anybody knows how to set TTY's baud rate * from kernel mode please add it here. We currently * reject baud rate change requests. */ br = tty_get_baud_rate(ks->tty); ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, br); ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, br); break; case AX25_VALUES_MEDIA_DUPLEX: case AX25_VALUES_MEDIA_TXDELAY: case AX25_VALUES_MEDIA_TXTAIL: case AX25_VALUES_MEDIA_SLOTTIME: case AX25_VALUES_MEDIA_PPERSISTENCE: kiss_update_parameters(dev); case AX25_VALUES_MEDIA_AUTO_ADJUST: default: break; } return; } /****************************************** 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 || !netif_running(ks->dev)) return; /* Read the characters out of the buffer */ while (count--) { if (fp && *fp++) { if (!ks->flags.error) { ks->flags.error = 1; ks->stats.rx_errors++; } cp++; continue; } kiss_unesc(ks, *cp++); } } /************************************ * kiss_open helper routines. ************************************/ /* Collect hanged up channels */ static void kiss_sync(void) { int i; spin_lock_bh(&ks_ctrl_lock); for (i = 0; i < 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); } spin_unlock_bh(&ks_ctrl_lock); } /* 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 ! */ spin_lock_bh(&ks_ctrl_lock); for (i = 0; i < 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.in_use = 1; ksp->ctrl.flags.error = 0; ksp->ctrl.flags.escape = 0; goto done; } } /* Sorry, too many, all slots in use */ if (i >= maxdev) goto outahere; if (ksp) { if (ksp->ctrl.flags.in_use) { unregister_netdevice(&ksp->dev); kiss_free_bufs(&ksp->ctrl); } } else if ((ksp = (kiss_ctrl_t *)kmalloc(sizeof(kiss_ctrl_t),GFP_KERNEL)) == NULL) goto outahere; memset(ksp, 0, sizeof(kiss_ctrl_t)); ks = &ksp->ctrl; /* Initialize channel control data */ ks->dev = &ksp->dev; ks->mode = KISS_MODE_ADAPTIVE; sprintf(ksp->dev.name, "ax%d", i); ksp->dev.base_addr = i; ksp->dev.priv = (void*)ks; ksp->dev.init = kiss_init; kiss_ctrls[i] = ksp; done: spin_lock_init(&ksp->ctrl.lock); spin_unlock_bh(&ks_ctrl_lock); return &ksp->ctrl; outahere: spin_unlock_bh(&ks_ctrl_lock); return NULL; } /* * 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; /* Collect hanged up channels. */ kiss_sync(); ks = (struct kiss *) tty->disc_data; err = -EEXIST; /* First make sure we're not already connected. */ if (ks) 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 (! ks->flags.in_use) { /* 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; } ks->flags.in_use = 1; } return ks->dev->base_addr; err_free_chan: ks->tty = NULL; tty->disc_data = NULL; ks->flags.in_use = 0; err_exit: printk(KERN_DEBUG "kiss_open: err:%d\n", err); /* 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->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 = 0; *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 (! ks->flags.error && (ks->rcount > 2)) kiss_bump(ks); ks->flags.error = 0; ks->flags.escape = 0; ks->rcount = 0; return; case ESC: ks->flags.escape = 1; return; case ESC_ESC: if (ks->flags.escape) { s = ESC; ks->flags.escape = 0; } break; case ESC_END: if (ks->flags.escape) { s = END; ks->flags.escape = 0; } break; } if (!ks->flags.error) { if (ks->rcount < ks->buffsize) { ks->rbuff[ks->rcount++] = s; } else { ks->stats.rx_over_errors++; ks->flags.error = 1; } } } static int kiss_set_mac_address(struct net_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 net_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) 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 SIOCSIFHWADDR: return kiss_set_mac_address(ks->dev, arg); 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_TNN_TOKENRING) return -EINVAL; if (tmp == KISS_MODE_COMPAT) tmp = KISS_MODE_ADAPTIVE; ks->mode = tmp; return 0; /* 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; } } static int kiss_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { int retval = -ENOIOCTLCMD; struct kiss *ks = (struct kiss *)dev->priv; if (ks == NULL) return -ENODEV; spin_lock_bh(&ks->lock); if (!ks->tty) { retval = -ENODEV; goto done; } printk(KERN_DEBUG "ioctl %d called for dev %s\n", cmd, dev->name); switch(cmd) { default: retval = 0; break; } done: spin_unlock_bh(&ks->lock); return retval; } /* Initialize KISS control device -- register KISS line discipline */ int __init kiss_init_driver(void) { int status; if (maxdev < KISS_NRUNIT) maxdev = KISS_NRUNIT; /* Sanity */ printk(KERN_INFO "KISS: version %s (dynamic channels, max=%d)\n", KISS_VERSION, maxdev); kiss_ctrls = (kiss_ctrl_t **) kmalloc(sizeof(void*)*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*)*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); } return status; /* Return "not found", so that dev_init() will unlink * the placeholder device entry for us. */ return -ENODEV; } void __exit kiss_exit_driver(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; spin_lock_bh(&ks_ctrl_lock); for (i = 0; i < maxdev; i++) { struct kiss_ctrl *ksc = kiss_ctrls[i]; if (ksc && ksc->ctrl.tty) { busy++; tty_hangup(ksc->ctrl.tty); } } spin_unlock_bh(&ks_ctrl_lock); } while (busy && jiffies - start < 1*HZ); busy = 0; for (i = 0; i < 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); } } MODULE_PARM(maxdev, "i"); MODULE_AUTHOR("Matthias Welwarsky "); MODULE_DESCRIPTION("KISS driver for AX.25"); module_init(kiss_init_driver); module_exit(kiss_exit_driver);