162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * slip.c This module implements the SLIP protocol for kernel-based 462306a36Sopenharmony_ci * devices like TTY. It interfaces between a raw TTY, and the 562306a36Sopenharmony_ci * kernel's INET protocol layers. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Version: @(#)slip.c 0.8.3 12/24/94 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Authors: Laurence Culhane, <loz@holmes.demon.co.uk> 1062306a36Sopenharmony_ci * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Fixes: 1362306a36Sopenharmony_ci * Alan Cox : Sanity checks and avoid tx overruns. 1462306a36Sopenharmony_ci * Has a new sl->mtu field. 1562306a36Sopenharmony_ci * Alan Cox : Found cause of overrun. ifconfig sl0 1662306a36Sopenharmony_ci * mtu upwards. Driver now spots this 1762306a36Sopenharmony_ci * and grows/shrinks its buffers(hack!). 1862306a36Sopenharmony_ci * Memory leak if you run out of memory 1962306a36Sopenharmony_ci * setting up a slip driver fixed. 2062306a36Sopenharmony_ci * Matt Dillon : Printable slip (borrowed from NET2E) 2162306a36Sopenharmony_ci * Pauline Middelink : Slip driver fixes. 2262306a36Sopenharmony_ci * Alan Cox : Honours the old SL_COMPRESSED flag 2362306a36Sopenharmony_ci * Alan Cox : KISS AX.25 and AXUI IP support 2462306a36Sopenharmony_ci * Michael Riepe : Automatic CSLIP recognition added 2562306a36Sopenharmony_ci * Charles Hedrick : CSLIP header length problem fix. 2662306a36Sopenharmony_ci * Alan Cox : Corrected non-IP cases of the above. 2762306a36Sopenharmony_ci * Alan Cox : Now uses hardware type as per FvK. 2862306a36Sopenharmony_ci * Alan Cox : Default to 192.168.0.0 (RFC 1597) 2962306a36Sopenharmony_ci * A.N.Kuznetsov : dev_tint() recursion fix. 3062306a36Sopenharmony_ci * Dmitry Gorodchanin : SLIP memory leaks 3162306a36Sopenharmony_ci * Dmitry Gorodchanin : Code cleanup. Reduce tty driver 3262306a36Sopenharmony_ci * buffering from 4096 to 256 bytes. 3362306a36Sopenharmony_ci * Improving SLIP response time. 3462306a36Sopenharmony_ci * CONFIG_SLIP_MODE_SLIP6. 3562306a36Sopenharmony_ci * ifconfig sl? up & down now works 3662306a36Sopenharmony_ci * correctly. 3762306a36Sopenharmony_ci * Modularization. 3862306a36Sopenharmony_ci * Alan Cox : Oops - fix AX.25 buffer lengths 3962306a36Sopenharmony_ci * Dmitry Gorodchanin : Even more cleanups. Preserve CSLIP 4062306a36Sopenharmony_ci * statistics. Include CSLIP code only 4162306a36Sopenharmony_ci * if it really needed. 4262306a36Sopenharmony_ci * Alan Cox : Free slhc buffers in the right place. 4362306a36Sopenharmony_ci * Alan Cox : Allow for digipeated IP over AX.25 4462306a36Sopenharmony_ci * Matti Aarnio : Dynamic SLIP devices, with ideas taken 4562306a36Sopenharmony_ci * from Jim Freeman's <jfree@caldera.com> 4662306a36Sopenharmony_ci * dynamic PPP devices. We do NOT kfree() 4762306a36Sopenharmony_ci * device entries, just reg./unreg. them 4862306a36Sopenharmony_ci * as they are needed. We kfree() them 4962306a36Sopenharmony_ci * at module cleanup. 5062306a36Sopenharmony_ci * With MODULE-loading ``insmod'', user 5162306a36Sopenharmony_ci * can issue parameter: slip_maxdev=1024 5262306a36Sopenharmony_ci * (Or how much he/she wants.. Default 5362306a36Sopenharmony_ci * is 256) 5462306a36Sopenharmony_ci * Stanislav Voronyi : Slip line checking, with ideas taken 5562306a36Sopenharmony_ci * from multislip BSDI driver which was 5662306a36Sopenharmony_ci * written by Igor Chechik, RELCOM Corp. 5762306a36Sopenharmony_ci * Only algorithms have been ported to 5862306a36Sopenharmony_ci * Linux SLIP driver. 5962306a36Sopenharmony_ci * Vitaly E. Lavrov : Sane behaviour on tty hangup. 6062306a36Sopenharmony_ci * Alexey Kuznetsov : Cleanup interfaces to tty & netdevice 6162306a36Sopenharmony_ci * modules. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define SL_CHECK_TRANSMIT 6562306a36Sopenharmony_ci#include <linux/compat.h> 6662306a36Sopenharmony_ci#include <linux/module.h> 6762306a36Sopenharmony_ci#include <linux/moduleparam.h> 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#include <linux/uaccess.h> 7062306a36Sopenharmony_ci#include <linux/bitops.h> 7162306a36Sopenharmony_ci#include <linux/sched/signal.h> 7262306a36Sopenharmony_ci#include <linux/string.h> 7362306a36Sopenharmony_ci#include <linux/mm.h> 7462306a36Sopenharmony_ci#include <linux/interrupt.h> 7562306a36Sopenharmony_ci#include <linux/in.h> 7662306a36Sopenharmony_ci#include <linux/tty.h> 7762306a36Sopenharmony_ci#include <linux/errno.h> 7862306a36Sopenharmony_ci#include <linux/netdevice.h> 7962306a36Sopenharmony_ci#include <linux/etherdevice.h> 8062306a36Sopenharmony_ci#include <linux/skbuff.h> 8162306a36Sopenharmony_ci#include <linux/rtnetlink.h> 8262306a36Sopenharmony_ci#include <linux/if_arp.h> 8362306a36Sopenharmony_ci#include <linux/if_slip.h> 8462306a36Sopenharmony_ci#include <linux/delay.h> 8562306a36Sopenharmony_ci#include <linux/init.h> 8662306a36Sopenharmony_ci#include <linux/slab.h> 8762306a36Sopenharmony_ci#include <linux/workqueue.h> 8862306a36Sopenharmony_ci#include "slip.h" 8962306a36Sopenharmony_ci#ifdef CONFIG_INET 9062306a36Sopenharmony_ci#include <linux/ip.h> 9162306a36Sopenharmony_ci#include <linux/tcp.h> 9262306a36Sopenharmony_ci#include <net/slhc_vj.h> 9362306a36Sopenharmony_ci#endif 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define SLIP_VERSION "0.8.4-NET3.019-NEWTTY" 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic struct net_device **slip_devs; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int slip_maxdev = SL_NRUNIT; 10062306a36Sopenharmony_cimodule_param(slip_maxdev, int, 0); 10162306a36Sopenharmony_ciMODULE_PARM_DESC(slip_maxdev, "Maximum number of slip devices"); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int slip_esc(unsigned char *p, unsigned char *d, int len); 10462306a36Sopenharmony_cistatic void slip_unesc(struct slip *sl, unsigned char c); 10562306a36Sopenharmony_ci#ifdef CONFIG_SLIP_MODE_SLIP6 10662306a36Sopenharmony_cistatic int slip_esc6(unsigned char *p, unsigned char *d, int len); 10762306a36Sopenharmony_cistatic void slip_unesc6(struct slip *sl, unsigned char c); 10862306a36Sopenharmony_ci#endif 10962306a36Sopenharmony_ci#ifdef CONFIG_SLIP_SMART 11062306a36Sopenharmony_cistatic void sl_keepalive(struct timer_list *t); 11162306a36Sopenharmony_cistatic void sl_outfill(struct timer_list *t); 11262306a36Sopenharmony_cistatic int sl_siocdevprivate(struct net_device *dev, struct ifreq *rq, void __user *data, int cmd); 11362306a36Sopenharmony_ci#endif 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/******************************** 11662306a36Sopenharmony_ci* Buffer administration routines: 11762306a36Sopenharmony_ci* sl_alloc_bufs() 11862306a36Sopenharmony_ci* sl_free_bufs() 11962306a36Sopenharmony_ci* sl_realloc_bufs() 12062306a36Sopenharmony_ci* 12162306a36Sopenharmony_ci* NOTE: sl_realloc_bufs != sl_free_bufs + sl_alloc_bufs, because 12262306a36Sopenharmony_ci* sl_realloc_bufs provides strong atomicity and reallocation 12362306a36Sopenharmony_ci* on actively running device. 12462306a36Sopenharmony_ci*********************************/ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci Allocate channel buffers. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int sl_alloc_bufs(struct slip *sl, int mtu) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci int err = -ENOBUFS; 13362306a36Sopenharmony_ci unsigned long len; 13462306a36Sopenharmony_ci char *rbuff = NULL; 13562306a36Sopenharmony_ci char *xbuff = NULL; 13662306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 13762306a36Sopenharmony_ci char *cbuff = NULL; 13862306a36Sopenharmony_ci struct slcompress *slcomp = NULL; 13962306a36Sopenharmony_ci#endif 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * Allocate the SLIP frame buffers: 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * rbuff Receive buffer. 14562306a36Sopenharmony_ci * xbuff Transmit buffer. 14662306a36Sopenharmony_ci * cbuff Temporary compression buffer. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci len = mtu * 2; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * allow for arrival of larger UDP packets, even if we say not to 15262306a36Sopenharmony_ci * also fixes a bug in which SunOS sends 512-byte packets even with 15362306a36Sopenharmony_ci * an MSS of 128 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci if (len < 576 * 2) 15662306a36Sopenharmony_ci len = 576 * 2; 15762306a36Sopenharmony_ci rbuff = kmalloc(len + 4, GFP_KERNEL); 15862306a36Sopenharmony_ci if (rbuff == NULL) 15962306a36Sopenharmony_ci goto err_exit; 16062306a36Sopenharmony_ci xbuff = kmalloc(len + 4, GFP_KERNEL); 16162306a36Sopenharmony_ci if (xbuff == NULL) 16262306a36Sopenharmony_ci goto err_exit; 16362306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 16462306a36Sopenharmony_ci cbuff = kmalloc(len + 4, GFP_KERNEL); 16562306a36Sopenharmony_ci if (cbuff == NULL) 16662306a36Sopenharmony_ci goto err_exit; 16762306a36Sopenharmony_ci slcomp = slhc_init(16, 16); 16862306a36Sopenharmony_ci if (IS_ERR(slcomp)) 16962306a36Sopenharmony_ci goto err_exit; 17062306a36Sopenharmony_ci#endif 17162306a36Sopenharmony_ci spin_lock_bh(&sl->lock); 17262306a36Sopenharmony_ci if (sl->tty == NULL) { 17362306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 17462306a36Sopenharmony_ci err = -ENODEV; 17562306a36Sopenharmony_ci goto err_exit; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci sl->mtu = mtu; 17862306a36Sopenharmony_ci sl->buffsize = len; 17962306a36Sopenharmony_ci sl->rcount = 0; 18062306a36Sopenharmony_ci sl->xleft = 0; 18162306a36Sopenharmony_ci rbuff = xchg(&sl->rbuff, rbuff); 18262306a36Sopenharmony_ci xbuff = xchg(&sl->xbuff, xbuff); 18362306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 18462306a36Sopenharmony_ci cbuff = xchg(&sl->cbuff, cbuff); 18562306a36Sopenharmony_ci slcomp = xchg(&sl->slcomp, slcomp); 18662306a36Sopenharmony_ci#endif 18762306a36Sopenharmony_ci#ifdef CONFIG_SLIP_MODE_SLIP6 18862306a36Sopenharmony_ci sl->xdata = 0; 18962306a36Sopenharmony_ci sl->xbits = 0; 19062306a36Sopenharmony_ci#endif 19162306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 19262306a36Sopenharmony_ci err = 0; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* Cleanup */ 19562306a36Sopenharmony_cierr_exit: 19662306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 19762306a36Sopenharmony_ci kfree(cbuff); 19862306a36Sopenharmony_ci slhc_free(slcomp); 19962306a36Sopenharmony_ci#endif 20062306a36Sopenharmony_ci kfree(xbuff); 20162306a36Sopenharmony_ci kfree(rbuff); 20262306a36Sopenharmony_ci return err; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* Free a SLIP channel buffers. */ 20662306a36Sopenharmony_cistatic void sl_free_bufs(struct slip *sl) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci /* Free all SLIP frame buffers. */ 20962306a36Sopenharmony_ci kfree(xchg(&sl->rbuff, NULL)); 21062306a36Sopenharmony_ci kfree(xchg(&sl->xbuff, NULL)); 21162306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 21262306a36Sopenharmony_ci kfree(xchg(&sl->cbuff, NULL)); 21362306a36Sopenharmony_ci slhc_free(xchg(&sl->slcomp, NULL)); 21462306a36Sopenharmony_ci#endif 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* 21862306a36Sopenharmony_ci Reallocate slip channel buffers. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int sl_realloc_bufs(struct slip *sl, int mtu) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci int err = 0; 22462306a36Sopenharmony_ci struct net_device *dev = sl->dev; 22562306a36Sopenharmony_ci unsigned char *xbuff, *rbuff; 22662306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 22762306a36Sopenharmony_ci unsigned char *cbuff; 22862306a36Sopenharmony_ci#endif 22962306a36Sopenharmony_ci int len = mtu * 2; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* 23262306a36Sopenharmony_ci * allow for arrival of larger UDP packets, even if we say not to 23362306a36Sopenharmony_ci * also fixes a bug in which SunOS sends 512-byte packets even with 23462306a36Sopenharmony_ci * an MSS of 128 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci if (len < 576 * 2) 23762306a36Sopenharmony_ci len = 576 * 2; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci xbuff = kmalloc(len + 4, GFP_ATOMIC); 24062306a36Sopenharmony_ci rbuff = kmalloc(len + 4, GFP_ATOMIC); 24162306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 24262306a36Sopenharmony_ci cbuff = kmalloc(len + 4, GFP_ATOMIC); 24362306a36Sopenharmony_ci#endif 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 24762306a36Sopenharmony_ci if (xbuff == NULL || rbuff == NULL || cbuff == NULL) { 24862306a36Sopenharmony_ci#else 24962306a36Sopenharmony_ci if (xbuff == NULL || rbuff == NULL) { 25062306a36Sopenharmony_ci#endif 25162306a36Sopenharmony_ci if (mtu > sl->mtu) { 25262306a36Sopenharmony_ci printk(KERN_WARNING "%s: unable to grow slip buffers, MTU change cancelled.\n", 25362306a36Sopenharmony_ci dev->name); 25462306a36Sopenharmony_ci err = -ENOBUFS; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci goto done; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci spin_lock_bh(&sl->lock); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci err = -ENODEV; 26162306a36Sopenharmony_ci if (sl->tty == NULL) 26262306a36Sopenharmony_ci goto done_on_bh; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci xbuff = xchg(&sl->xbuff, xbuff); 26562306a36Sopenharmony_ci rbuff = xchg(&sl->rbuff, rbuff); 26662306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 26762306a36Sopenharmony_ci cbuff = xchg(&sl->cbuff, cbuff); 26862306a36Sopenharmony_ci#endif 26962306a36Sopenharmony_ci if (sl->xleft) { 27062306a36Sopenharmony_ci if (sl->xleft <= len) { 27162306a36Sopenharmony_ci memcpy(sl->xbuff, sl->xhead, sl->xleft); 27262306a36Sopenharmony_ci } else { 27362306a36Sopenharmony_ci sl->xleft = 0; 27462306a36Sopenharmony_ci dev->stats.tx_dropped++; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci sl->xhead = sl->xbuff; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (sl->rcount) { 28062306a36Sopenharmony_ci if (sl->rcount <= len) { 28162306a36Sopenharmony_ci memcpy(sl->rbuff, rbuff, sl->rcount); 28262306a36Sopenharmony_ci } else { 28362306a36Sopenharmony_ci sl->rcount = 0; 28462306a36Sopenharmony_ci dev->stats.rx_over_errors++; 28562306a36Sopenharmony_ci set_bit(SLF_ERROR, &sl->flags); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci sl->mtu = mtu; 28962306a36Sopenharmony_ci dev->mtu = mtu; 29062306a36Sopenharmony_ci sl->buffsize = len; 29162306a36Sopenharmony_ci err = 0; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cidone_on_bh: 29462306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cidone: 29762306a36Sopenharmony_ci kfree(xbuff); 29862306a36Sopenharmony_ci kfree(rbuff); 29962306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 30062306a36Sopenharmony_ci kfree(cbuff); 30162306a36Sopenharmony_ci#endif 30262306a36Sopenharmony_ci return err; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* Set the "sending" flag. This must be atomic hence the set_bit. */ 30762306a36Sopenharmony_cistatic inline void sl_lock(struct slip *sl) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci netif_stop_queue(sl->dev); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci/* Clear the "sending" flag. This must be atomic, hence the ASM. */ 31462306a36Sopenharmony_cistatic inline void sl_unlock(struct slip *sl) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci netif_wake_queue(sl->dev); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* Send one completely decapsulated IP datagram to the IP layer. */ 32062306a36Sopenharmony_cistatic void sl_bump(struct slip *sl) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct net_device *dev = sl->dev; 32362306a36Sopenharmony_ci struct sk_buff *skb; 32462306a36Sopenharmony_ci int count; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci count = sl->rcount; 32762306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 32862306a36Sopenharmony_ci if (sl->mode & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) { 32962306a36Sopenharmony_ci unsigned char c = sl->rbuff[0]; 33062306a36Sopenharmony_ci if (c & SL_TYPE_COMPRESSED_TCP) { 33162306a36Sopenharmony_ci /* ignore compressed packets when CSLIP is off */ 33262306a36Sopenharmony_ci if (!(sl->mode & SL_MODE_CSLIP)) { 33362306a36Sopenharmony_ci printk(KERN_WARNING "%s: compressed packet ignored\n", dev->name); 33462306a36Sopenharmony_ci return; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci /* make sure we've reserved enough space for uncompress 33762306a36Sopenharmony_ci to use */ 33862306a36Sopenharmony_ci if (count + 80 > sl->buffsize) { 33962306a36Sopenharmony_ci dev->stats.rx_over_errors++; 34062306a36Sopenharmony_ci return; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci count = slhc_uncompress(sl->slcomp, sl->rbuff, count); 34362306a36Sopenharmony_ci if (count <= 0) 34462306a36Sopenharmony_ci return; 34562306a36Sopenharmony_ci } else if (c >= SL_TYPE_UNCOMPRESSED_TCP) { 34662306a36Sopenharmony_ci if (!(sl->mode & SL_MODE_CSLIP)) { 34762306a36Sopenharmony_ci /* turn on header compression */ 34862306a36Sopenharmony_ci sl->mode |= SL_MODE_CSLIP; 34962306a36Sopenharmony_ci sl->mode &= ~SL_MODE_ADAPTIVE; 35062306a36Sopenharmony_ci printk(KERN_INFO "%s: header compression turned on\n", dev->name); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci sl->rbuff[0] &= 0x4f; 35362306a36Sopenharmony_ci if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) 35462306a36Sopenharmony_ci return; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci#endif /* SL_INCLUDE_CSLIP */ 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci dev->stats.rx_bytes += count; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci skb = dev_alloc_skb(count); 36262306a36Sopenharmony_ci if (skb == NULL) { 36362306a36Sopenharmony_ci printk(KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name); 36462306a36Sopenharmony_ci dev->stats.rx_dropped++; 36562306a36Sopenharmony_ci return; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci skb->dev = dev; 36862306a36Sopenharmony_ci skb_put_data(skb, sl->rbuff, count); 36962306a36Sopenharmony_ci skb_reset_mac_header(skb); 37062306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 37162306a36Sopenharmony_ci netif_rx(skb); 37262306a36Sopenharmony_ci dev->stats.rx_packets++; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* Encapsulate one IP datagram and stuff into a TTY queue. */ 37662306a36Sopenharmony_cistatic void sl_encaps(struct slip *sl, unsigned char *icp, int len) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci unsigned char *p; 37962306a36Sopenharmony_ci int actual, count; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (len > sl->mtu) { /* Sigh, shouldn't occur BUT ... */ 38262306a36Sopenharmony_ci printk(KERN_WARNING "%s: truncating oversized transmit packet!\n", sl->dev->name); 38362306a36Sopenharmony_ci sl->dev->stats.tx_dropped++; 38462306a36Sopenharmony_ci sl_unlock(sl); 38562306a36Sopenharmony_ci return; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci p = icp; 38962306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 39062306a36Sopenharmony_ci if (sl->mode & SL_MODE_CSLIP) 39162306a36Sopenharmony_ci len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1); 39262306a36Sopenharmony_ci#endif 39362306a36Sopenharmony_ci#ifdef CONFIG_SLIP_MODE_SLIP6 39462306a36Sopenharmony_ci if (sl->mode & SL_MODE_SLIP6) 39562306a36Sopenharmony_ci count = slip_esc6(p, sl->xbuff, len); 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci#endif 39862306a36Sopenharmony_ci count = slip_esc(p, sl->xbuff, len); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* Order of next two lines is *very* important. 40162306a36Sopenharmony_ci * When we are sending a little amount of data, 40262306a36Sopenharmony_ci * the transfer may be completed inside the ops->write() 40362306a36Sopenharmony_ci * routine, because it's running with interrupts enabled. 40462306a36Sopenharmony_ci * In this case we *never* got WRITE_WAKEUP event, 40562306a36Sopenharmony_ci * if we did not request it before write operation. 40662306a36Sopenharmony_ci * 14 Oct 1994 Dmitry Gorodchanin. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); 40962306a36Sopenharmony_ci actual = sl->tty->ops->write(sl->tty, sl->xbuff, count); 41062306a36Sopenharmony_ci#ifdef SL_CHECK_TRANSMIT 41162306a36Sopenharmony_ci netif_trans_update(sl->dev); 41262306a36Sopenharmony_ci#endif 41362306a36Sopenharmony_ci sl->xleft = count - actual; 41462306a36Sopenharmony_ci sl->xhead = sl->xbuff + actual; 41562306a36Sopenharmony_ci#ifdef CONFIG_SLIP_SMART 41662306a36Sopenharmony_ci /* VSV */ 41762306a36Sopenharmony_ci clear_bit(SLF_OUTWAIT, &sl->flags); /* reset outfill flag */ 41862306a36Sopenharmony_ci#endif 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/* Write out any remaining transmit buffer. Scheduled when tty is writable */ 42262306a36Sopenharmony_cistatic void slip_transmit(struct work_struct *work) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct slip *sl = container_of(work, struct slip, tx_work); 42562306a36Sopenharmony_ci int actual; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci spin_lock_bh(&sl->lock); 42862306a36Sopenharmony_ci /* First make sure we're connected. */ 42962306a36Sopenharmony_ci if (!sl->tty || sl->magic != SLIP_MAGIC || !netif_running(sl->dev)) { 43062306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 43162306a36Sopenharmony_ci return; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (sl->xleft <= 0) { 43562306a36Sopenharmony_ci /* Now serial buffer is almost free & we can start 43662306a36Sopenharmony_ci * transmission of another packet */ 43762306a36Sopenharmony_ci sl->dev->stats.tx_packets++; 43862306a36Sopenharmony_ci clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); 43962306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 44062306a36Sopenharmony_ci sl_unlock(sl); 44162306a36Sopenharmony_ci return; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci actual = sl->tty->ops->write(sl->tty, sl->xhead, sl->xleft); 44562306a36Sopenharmony_ci sl->xleft -= actual; 44662306a36Sopenharmony_ci sl->xhead += actual; 44762306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci/* 45162306a36Sopenharmony_ci * Called by the driver when there's room for more data. 45262306a36Sopenharmony_ci * Schedule the transmit. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_cistatic void slip_write_wakeup(struct tty_struct *tty) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct slip *sl; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci rcu_read_lock(); 45962306a36Sopenharmony_ci sl = rcu_dereference(tty->disc_data); 46062306a36Sopenharmony_ci if (sl) 46162306a36Sopenharmony_ci schedule_work(&sl->tx_work); 46262306a36Sopenharmony_ci rcu_read_unlock(); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic void sl_tx_timeout(struct net_device *dev, unsigned int txqueue) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct slip *sl = netdev_priv(dev); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci spin_lock(&sl->lock); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (netif_queue_stopped(dev)) { 47262306a36Sopenharmony_ci if (!netif_running(dev) || !sl->tty) 47362306a36Sopenharmony_ci goto out; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* May be we must check transmitter timeout here ? 47662306a36Sopenharmony_ci * 14 Oct 1994 Dmitry Gorodchanin. 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ci#ifdef SL_CHECK_TRANSMIT 47962306a36Sopenharmony_ci if (time_before(jiffies, dev_trans_start(dev) + 20 * HZ)) { 48062306a36Sopenharmony_ci /* 20 sec timeout not reached */ 48162306a36Sopenharmony_ci goto out; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci printk(KERN_WARNING "%s: transmit timed out, %s?\n", 48462306a36Sopenharmony_ci dev->name, 48562306a36Sopenharmony_ci (tty_chars_in_buffer(sl->tty) || sl->xleft) ? 48662306a36Sopenharmony_ci "bad line quality" : "driver error"); 48762306a36Sopenharmony_ci sl->xleft = 0; 48862306a36Sopenharmony_ci clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); 48962306a36Sopenharmony_ci sl_unlock(sl); 49062306a36Sopenharmony_ci#endif 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ciout: 49362306a36Sopenharmony_ci spin_unlock(&sl->lock); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci/* Encapsulate an IP datagram and kick it into a TTY queue. */ 49862306a36Sopenharmony_cistatic netdev_tx_t 49962306a36Sopenharmony_cisl_xmit(struct sk_buff *skb, struct net_device *dev) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct slip *sl = netdev_priv(dev); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci spin_lock(&sl->lock); 50462306a36Sopenharmony_ci if (!netif_running(dev)) { 50562306a36Sopenharmony_ci spin_unlock(&sl->lock); 50662306a36Sopenharmony_ci printk(KERN_WARNING "%s: xmit call when iface is down\n", dev->name); 50762306a36Sopenharmony_ci dev_kfree_skb(skb); 50862306a36Sopenharmony_ci return NETDEV_TX_OK; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci if (sl->tty == NULL) { 51162306a36Sopenharmony_ci spin_unlock(&sl->lock); 51262306a36Sopenharmony_ci dev_kfree_skb(skb); 51362306a36Sopenharmony_ci return NETDEV_TX_OK; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci sl_lock(sl); 51762306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 51862306a36Sopenharmony_ci sl_encaps(sl, skb->data, skb->len); 51962306a36Sopenharmony_ci spin_unlock(&sl->lock); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci dev_kfree_skb(skb); 52262306a36Sopenharmony_ci return NETDEV_TX_OK; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/****************************************** 52762306a36Sopenharmony_ci * Routines looking at netdevice side. 52862306a36Sopenharmony_ci ******************************************/ 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci/* Netdevice UP -> DOWN routine */ 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic int 53362306a36Sopenharmony_cisl_close(struct net_device *dev) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct slip *sl = netdev_priv(dev); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci spin_lock_bh(&sl->lock); 53862306a36Sopenharmony_ci if (sl->tty) 53962306a36Sopenharmony_ci /* TTY discipline is running. */ 54062306a36Sopenharmony_ci clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); 54162306a36Sopenharmony_ci netif_stop_queue(dev); 54262306a36Sopenharmony_ci sl->rcount = 0; 54362306a36Sopenharmony_ci sl->xleft = 0; 54462306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return 0; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci/* Netdevice DOWN -> UP routine */ 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int sl_open(struct net_device *dev) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct slip *sl = netdev_priv(dev); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (sl->tty == NULL) 55662306a36Sopenharmony_ci return -ENODEV; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci sl->flags &= (1 << SLF_INUSE); 55962306a36Sopenharmony_ci netif_start_queue(dev); 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/* Netdevice change MTU request */ 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic int sl_change_mtu(struct net_device *dev, int new_mtu) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct slip *sl = netdev_priv(dev); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return sl_realloc_bufs(sl, new_mtu); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/* Netdevice get statistics request */ 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic void 57562306a36Sopenharmony_cisl_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci struct net_device_stats *devstats = &dev->stats; 57862306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 57962306a36Sopenharmony_ci struct slip *sl = netdev_priv(dev); 58062306a36Sopenharmony_ci struct slcompress *comp = sl->slcomp; 58162306a36Sopenharmony_ci#endif 58262306a36Sopenharmony_ci stats->rx_packets = devstats->rx_packets; 58362306a36Sopenharmony_ci stats->tx_packets = devstats->tx_packets; 58462306a36Sopenharmony_ci stats->rx_bytes = devstats->rx_bytes; 58562306a36Sopenharmony_ci stats->tx_bytes = devstats->tx_bytes; 58662306a36Sopenharmony_ci stats->rx_dropped = devstats->rx_dropped; 58762306a36Sopenharmony_ci stats->tx_dropped = devstats->tx_dropped; 58862306a36Sopenharmony_ci stats->tx_errors = devstats->tx_errors; 58962306a36Sopenharmony_ci stats->rx_errors = devstats->rx_errors; 59062306a36Sopenharmony_ci stats->rx_over_errors = devstats->rx_over_errors; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci#ifdef SL_INCLUDE_CSLIP 59362306a36Sopenharmony_ci if (comp) { 59462306a36Sopenharmony_ci /* Generic compressed statistics */ 59562306a36Sopenharmony_ci stats->rx_compressed = comp->sls_i_compressed; 59662306a36Sopenharmony_ci stats->tx_compressed = comp->sls_o_compressed; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* Are we really still needs this? */ 59962306a36Sopenharmony_ci stats->rx_fifo_errors += comp->sls_i_compressed; 60062306a36Sopenharmony_ci stats->rx_dropped += comp->sls_i_tossed; 60162306a36Sopenharmony_ci stats->tx_fifo_errors += comp->sls_o_compressed; 60262306a36Sopenharmony_ci stats->collisions += comp->sls_o_misses; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci#endif 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci/* Netdevice register callback */ 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int sl_init(struct net_device *dev) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct slip *sl = netdev_priv(dev); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * Finish setting up the DEVICE info. 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci dev->mtu = sl->mtu; 61862306a36Sopenharmony_ci dev->type = ARPHRD_SLIP + sl->mode; 61962306a36Sopenharmony_ci#ifdef SL_CHECK_TRANSMIT 62062306a36Sopenharmony_ci dev->watchdog_timeo = 20*HZ; 62162306a36Sopenharmony_ci#endif 62262306a36Sopenharmony_ci return 0; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic void sl_uninit(struct net_device *dev) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct slip *sl = netdev_priv(dev); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci sl_free_bufs(sl); 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci/* Hook the destructor so we can free slip devices at the right point in time */ 63462306a36Sopenharmony_cistatic void sl_free_netdev(struct net_device *dev) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci int i = dev->base_addr; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci slip_devs[i] = NULL; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic const struct net_device_ops sl_netdev_ops = { 64262306a36Sopenharmony_ci .ndo_init = sl_init, 64362306a36Sopenharmony_ci .ndo_uninit = sl_uninit, 64462306a36Sopenharmony_ci .ndo_open = sl_open, 64562306a36Sopenharmony_ci .ndo_stop = sl_close, 64662306a36Sopenharmony_ci .ndo_start_xmit = sl_xmit, 64762306a36Sopenharmony_ci .ndo_get_stats64 = sl_get_stats64, 64862306a36Sopenharmony_ci .ndo_change_mtu = sl_change_mtu, 64962306a36Sopenharmony_ci .ndo_tx_timeout = sl_tx_timeout, 65062306a36Sopenharmony_ci#ifdef CONFIG_SLIP_SMART 65162306a36Sopenharmony_ci .ndo_siocdevprivate = sl_siocdevprivate, 65262306a36Sopenharmony_ci#endif 65362306a36Sopenharmony_ci}; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic void sl_setup(struct net_device *dev) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci dev->netdev_ops = &sl_netdev_ops; 65962306a36Sopenharmony_ci dev->needs_free_netdev = true; 66062306a36Sopenharmony_ci dev->priv_destructor = sl_free_netdev; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci dev->hard_header_len = 0; 66362306a36Sopenharmony_ci dev->addr_len = 0; 66462306a36Sopenharmony_ci dev->tx_queue_len = 10; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* MTU range: 68 - 65534 */ 66762306a36Sopenharmony_ci dev->min_mtu = 68; 66862306a36Sopenharmony_ci dev->max_mtu = 65534; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* New-style flags. */ 67162306a36Sopenharmony_ci dev->flags = IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci/****************************************** 67562306a36Sopenharmony_ci Routines looking at TTY side. 67662306a36Sopenharmony_ci ******************************************/ 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci/* 68062306a36Sopenharmony_ci * Handle the 'receiver data ready' interrupt. 68162306a36Sopenharmony_ci * This function is called by the 'tty_io' module in the kernel when 68262306a36Sopenharmony_ci * a block of SLIP data has been received, which can now be decapsulated 68362306a36Sopenharmony_ci * and sent on to some IP layer for further processing. This will not 68462306a36Sopenharmony_ci * be re-entered while running but other ldisc functions may be called 68562306a36Sopenharmony_ci * in parallel 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic void slip_receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp, 68962306a36Sopenharmony_ci size_t count) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct slip *sl = tty->disc_data; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (!sl || sl->magic != SLIP_MAGIC || !netif_running(sl->dev)) 69462306a36Sopenharmony_ci return; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* Read the characters out of the buffer */ 69762306a36Sopenharmony_ci while (count--) { 69862306a36Sopenharmony_ci if (fp && *fp++) { 69962306a36Sopenharmony_ci if (!test_and_set_bit(SLF_ERROR, &sl->flags)) 70062306a36Sopenharmony_ci sl->dev->stats.rx_errors++; 70162306a36Sopenharmony_ci cp++; 70262306a36Sopenharmony_ci continue; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci#ifdef CONFIG_SLIP_MODE_SLIP6 70562306a36Sopenharmony_ci if (sl->mode & SL_MODE_SLIP6) 70662306a36Sopenharmony_ci slip_unesc6(sl, *cp++); 70762306a36Sopenharmony_ci else 70862306a36Sopenharmony_ci#endif 70962306a36Sopenharmony_ci slip_unesc(sl, *cp++); 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci/************************************ 71462306a36Sopenharmony_ci * slip_open helper routines. 71562306a36Sopenharmony_ci ************************************/ 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci/* Collect hanged up channels */ 71862306a36Sopenharmony_cistatic void sl_sync(void) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci int i; 72162306a36Sopenharmony_ci struct net_device *dev; 72262306a36Sopenharmony_ci struct slip *sl; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci for (i = 0; i < slip_maxdev; i++) { 72562306a36Sopenharmony_ci dev = slip_devs[i]; 72662306a36Sopenharmony_ci if (dev == NULL) 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci sl = netdev_priv(dev); 73062306a36Sopenharmony_ci if (sl->tty || sl->leased) 73162306a36Sopenharmony_ci continue; 73262306a36Sopenharmony_ci if (dev->flags & IFF_UP) 73362306a36Sopenharmony_ci dev_close(dev); 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci/* Find a free SLIP channel, and link in this `tty' line. */ 73962306a36Sopenharmony_cistatic struct slip *sl_alloc(void) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci int i; 74262306a36Sopenharmony_ci char name[IFNAMSIZ]; 74362306a36Sopenharmony_ci struct net_device *dev = NULL; 74462306a36Sopenharmony_ci struct slip *sl; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci for (i = 0; i < slip_maxdev; i++) { 74762306a36Sopenharmony_ci dev = slip_devs[i]; 74862306a36Sopenharmony_ci if (dev == NULL) 74962306a36Sopenharmony_ci break; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci /* Sorry, too many, all slots in use */ 75262306a36Sopenharmony_ci if (i >= slip_maxdev) 75362306a36Sopenharmony_ci return NULL; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci sprintf(name, "sl%d", i); 75662306a36Sopenharmony_ci dev = alloc_netdev(sizeof(*sl), name, NET_NAME_UNKNOWN, sl_setup); 75762306a36Sopenharmony_ci if (!dev) 75862306a36Sopenharmony_ci return NULL; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci dev->base_addr = i; 76162306a36Sopenharmony_ci sl = netdev_priv(dev); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* Initialize channel control data */ 76462306a36Sopenharmony_ci sl->magic = SLIP_MAGIC; 76562306a36Sopenharmony_ci sl->dev = dev; 76662306a36Sopenharmony_ci spin_lock_init(&sl->lock); 76762306a36Sopenharmony_ci INIT_WORK(&sl->tx_work, slip_transmit); 76862306a36Sopenharmony_ci sl->mode = SL_MODE_DEFAULT; 76962306a36Sopenharmony_ci#ifdef CONFIG_SLIP_SMART 77062306a36Sopenharmony_ci /* initialize timer_list struct */ 77162306a36Sopenharmony_ci timer_setup(&sl->keepalive_timer, sl_keepalive, 0); 77262306a36Sopenharmony_ci timer_setup(&sl->outfill_timer, sl_outfill, 0); 77362306a36Sopenharmony_ci#endif 77462306a36Sopenharmony_ci slip_devs[i] = dev; 77562306a36Sopenharmony_ci return sl; 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci/* 77962306a36Sopenharmony_ci * Open the high-level part of the SLIP channel. 78062306a36Sopenharmony_ci * This function is called by the TTY module when the 78162306a36Sopenharmony_ci * SLIP line discipline is called for. Because we are 78262306a36Sopenharmony_ci * sure the tty line exists, we only have to link it to 78362306a36Sopenharmony_ci * a free SLIP channel... 78462306a36Sopenharmony_ci * 78562306a36Sopenharmony_ci * Called in process context serialized from other ldisc calls. 78662306a36Sopenharmony_ci */ 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic int slip_open(struct tty_struct *tty) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct slip *sl; 79162306a36Sopenharmony_ci int err; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 79462306a36Sopenharmony_ci return -EPERM; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (tty->ops->write == NULL) 79762306a36Sopenharmony_ci return -EOPNOTSUPP; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* RTnetlink lock is misused here to serialize concurrent 80062306a36Sopenharmony_ci opens of slip channels. There are better ways, but it is 80162306a36Sopenharmony_ci the simplest one. 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ci rtnl_lock(); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* Collect hanged up channels. */ 80662306a36Sopenharmony_ci sl_sync(); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci sl = tty->disc_data; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci err = -EEXIST; 81162306a36Sopenharmony_ci /* First make sure we're not already connected. */ 81262306a36Sopenharmony_ci if (sl && sl->magic == SLIP_MAGIC) 81362306a36Sopenharmony_ci goto err_exit; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* OK. Find a free SLIP channel to use. */ 81662306a36Sopenharmony_ci err = -ENFILE; 81762306a36Sopenharmony_ci sl = sl_alloc(); 81862306a36Sopenharmony_ci if (sl == NULL) 81962306a36Sopenharmony_ci goto err_exit; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci sl->tty = tty; 82262306a36Sopenharmony_ci tty->disc_data = sl; 82362306a36Sopenharmony_ci sl->pid = current->pid; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (!test_bit(SLF_INUSE, &sl->flags)) { 82662306a36Sopenharmony_ci /* Perform the low-level SLIP initialization. */ 82762306a36Sopenharmony_ci err = sl_alloc_bufs(sl, SL_MTU); 82862306a36Sopenharmony_ci if (err) 82962306a36Sopenharmony_ci goto err_free_chan; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci set_bit(SLF_INUSE, &sl->flags); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci err = register_netdevice(sl->dev); 83462306a36Sopenharmony_ci if (err) 83562306a36Sopenharmony_ci goto err_free_bufs; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci#ifdef CONFIG_SLIP_SMART 83962306a36Sopenharmony_ci if (sl->keepalive) { 84062306a36Sopenharmony_ci sl->keepalive_timer.expires = jiffies + sl->keepalive * HZ; 84162306a36Sopenharmony_ci add_timer(&sl->keepalive_timer); 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci if (sl->outfill) { 84462306a36Sopenharmony_ci sl->outfill_timer.expires = jiffies + sl->outfill * HZ; 84562306a36Sopenharmony_ci add_timer(&sl->outfill_timer); 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci#endif 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* Done. We have linked the TTY line to a channel. */ 85062306a36Sopenharmony_ci rtnl_unlock(); 85162306a36Sopenharmony_ci tty->receive_room = 65536; /* We don't flow control */ 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci /* TTY layer expects 0 on success */ 85462306a36Sopenharmony_ci return 0; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cierr_free_bufs: 85762306a36Sopenharmony_ci sl_free_bufs(sl); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cierr_free_chan: 86062306a36Sopenharmony_ci sl->tty = NULL; 86162306a36Sopenharmony_ci tty->disc_data = NULL; 86262306a36Sopenharmony_ci clear_bit(SLF_INUSE, &sl->flags); 86362306a36Sopenharmony_ci sl_free_netdev(sl->dev); 86462306a36Sopenharmony_ci /* do not call free_netdev before rtnl_unlock */ 86562306a36Sopenharmony_ci rtnl_unlock(); 86662306a36Sopenharmony_ci free_netdev(sl->dev); 86762306a36Sopenharmony_ci return err; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cierr_exit: 87062306a36Sopenharmony_ci rtnl_unlock(); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* Count references from TTY module */ 87362306a36Sopenharmony_ci return err; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci/* 87762306a36Sopenharmony_ci * Close down a SLIP channel. 87862306a36Sopenharmony_ci * This means flushing out any pending queues, and then returning. This 87962306a36Sopenharmony_ci * call is serialized against other ldisc functions. 88062306a36Sopenharmony_ci * 88162306a36Sopenharmony_ci * We also use this method fo a hangup event 88262306a36Sopenharmony_ci */ 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic void slip_close(struct tty_struct *tty) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci struct slip *sl = tty->disc_data; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* First make sure we're connected. */ 88962306a36Sopenharmony_ci if (!sl || sl->magic != SLIP_MAGIC || sl->tty != tty) 89062306a36Sopenharmony_ci return; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci spin_lock_bh(&sl->lock); 89362306a36Sopenharmony_ci rcu_assign_pointer(tty->disc_data, NULL); 89462306a36Sopenharmony_ci sl->tty = NULL; 89562306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci synchronize_rcu(); 89862306a36Sopenharmony_ci flush_work(&sl->tx_work); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* VSV = very important to remove timers */ 90162306a36Sopenharmony_ci#ifdef CONFIG_SLIP_SMART 90262306a36Sopenharmony_ci del_timer_sync(&sl->keepalive_timer); 90362306a36Sopenharmony_ci del_timer_sync(&sl->outfill_timer); 90462306a36Sopenharmony_ci#endif 90562306a36Sopenharmony_ci /* Flush network side */ 90662306a36Sopenharmony_ci unregister_netdev(sl->dev); 90762306a36Sopenharmony_ci /* This will complete via sl_free_netdev */ 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic void slip_hangup(struct tty_struct *tty) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci slip_close(tty); 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci /************************************************************************ 91562306a36Sopenharmony_ci * STANDARD SLIP ENCAPSULATION * 91662306a36Sopenharmony_ci ************************************************************************/ 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic int slip_esc(unsigned char *s, unsigned char *d, int len) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci unsigned char *ptr = d; 92162306a36Sopenharmony_ci unsigned char c; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* 92462306a36Sopenharmony_ci * Send an initial END character to flush out any 92562306a36Sopenharmony_ci * data that may have accumulated in the receiver 92662306a36Sopenharmony_ci * due to line noise. 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci *ptr++ = END; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* 93262306a36Sopenharmony_ci * For each byte in the packet, send the appropriate 93362306a36Sopenharmony_ci * character sequence, according to the SLIP protocol. 93462306a36Sopenharmony_ci */ 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci while (len-- > 0) { 93762306a36Sopenharmony_ci switch (c = *s++) { 93862306a36Sopenharmony_ci case END: 93962306a36Sopenharmony_ci *ptr++ = ESC; 94062306a36Sopenharmony_ci *ptr++ = ESC_END; 94162306a36Sopenharmony_ci break; 94262306a36Sopenharmony_ci case ESC: 94362306a36Sopenharmony_ci *ptr++ = ESC; 94462306a36Sopenharmony_ci *ptr++ = ESC_ESC; 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci default: 94762306a36Sopenharmony_ci *ptr++ = c; 94862306a36Sopenharmony_ci break; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci *ptr++ = END; 95262306a36Sopenharmony_ci return ptr - d; 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic void slip_unesc(struct slip *sl, unsigned char s) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci switch (s) { 95962306a36Sopenharmony_ci case END: 96062306a36Sopenharmony_ci#ifdef CONFIG_SLIP_SMART 96162306a36Sopenharmony_ci /* drop keeptest bit = VSV */ 96262306a36Sopenharmony_ci if (test_bit(SLF_KEEPTEST, &sl->flags)) 96362306a36Sopenharmony_ci clear_bit(SLF_KEEPTEST, &sl->flags); 96462306a36Sopenharmony_ci#endif 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && 96762306a36Sopenharmony_ci (sl->rcount > 2)) 96862306a36Sopenharmony_ci sl_bump(sl); 96962306a36Sopenharmony_ci clear_bit(SLF_ESCAPE, &sl->flags); 97062306a36Sopenharmony_ci sl->rcount = 0; 97162306a36Sopenharmony_ci return; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci case ESC: 97462306a36Sopenharmony_ci set_bit(SLF_ESCAPE, &sl->flags); 97562306a36Sopenharmony_ci return; 97662306a36Sopenharmony_ci case ESC_ESC: 97762306a36Sopenharmony_ci if (test_and_clear_bit(SLF_ESCAPE, &sl->flags)) 97862306a36Sopenharmony_ci s = ESC; 97962306a36Sopenharmony_ci break; 98062306a36Sopenharmony_ci case ESC_END: 98162306a36Sopenharmony_ci if (test_and_clear_bit(SLF_ESCAPE, &sl->flags)) 98262306a36Sopenharmony_ci s = END; 98362306a36Sopenharmony_ci break; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci if (!test_bit(SLF_ERROR, &sl->flags)) { 98662306a36Sopenharmony_ci if (sl->rcount < sl->buffsize) { 98762306a36Sopenharmony_ci sl->rbuff[sl->rcount++] = s; 98862306a36Sopenharmony_ci return; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci sl->dev->stats.rx_over_errors++; 99162306a36Sopenharmony_ci set_bit(SLF_ERROR, &sl->flags); 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci} 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci#ifdef CONFIG_SLIP_MODE_SLIP6 99762306a36Sopenharmony_ci/************************************************************************ 99862306a36Sopenharmony_ci * 6 BIT SLIP ENCAPSULATION * 99962306a36Sopenharmony_ci ************************************************************************/ 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic int slip_esc6(unsigned char *s, unsigned char *d, int len) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci unsigned char *ptr = d; 100462306a36Sopenharmony_ci unsigned char c; 100562306a36Sopenharmony_ci int i; 100662306a36Sopenharmony_ci unsigned short v = 0; 100762306a36Sopenharmony_ci short bits = 0; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci /* 101062306a36Sopenharmony_ci * Send an initial END character to flush out any 101162306a36Sopenharmony_ci * data that may have accumulated in the receiver 101262306a36Sopenharmony_ci * due to line noise. 101362306a36Sopenharmony_ci */ 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci *ptr++ = 0x70; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci /* 101862306a36Sopenharmony_ci * Encode the packet into printable ascii characters 101962306a36Sopenharmony_ci */ 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci for (i = 0; i < len; ++i) { 102262306a36Sopenharmony_ci v = (v << 8) | s[i]; 102362306a36Sopenharmony_ci bits += 8; 102462306a36Sopenharmony_ci while (bits >= 6) { 102562306a36Sopenharmony_ci bits -= 6; 102662306a36Sopenharmony_ci c = 0x30 + ((v >> bits) & 0x3F); 102762306a36Sopenharmony_ci *ptr++ = c; 102862306a36Sopenharmony_ci } 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci if (bits) { 103162306a36Sopenharmony_ci c = 0x30 + ((v << (6 - bits)) & 0x3F); 103262306a36Sopenharmony_ci *ptr++ = c; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci *ptr++ = 0x70; 103562306a36Sopenharmony_ci return ptr - d; 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic void slip_unesc6(struct slip *sl, unsigned char s) 103962306a36Sopenharmony_ci{ 104062306a36Sopenharmony_ci unsigned char c; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci if (s == 0x70) { 104362306a36Sopenharmony_ci#ifdef CONFIG_SLIP_SMART 104462306a36Sopenharmony_ci /* drop keeptest bit = VSV */ 104562306a36Sopenharmony_ci if (test_bit(SLF_KEEPTEST, &sl->flags)) 104662306a36Sopenharmony_ci clear_bit(SLF_KEEPTEST, &sl->flags); 104762306a36Sopenharmony_ci#endif 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && 105062306a36Sopenharmony_ci (sl->rcount > 2)) 105162306a36Sopenharmony_ci sl_bump(sl); 105262306a36Sopenharmony_ci sl->rcount = 0; 105362306a36Sopenharmony_ci sl->xbits = 0; 105462306a36Sopenharmony_ci sl->xdata = 0; 105562306a36Sopenharmony_ci } else if (s >= 0x30 && s < 0x70) { 105662306a36Sopenharmony_ci sl->xdata = (sl->xdata << 6) | ((s - 0x30) & 0x3F); 105762306a36Sopenharmony_ci sl->xbits += 6; 105862306a36Sopenharmony_ci if (sl->xbits >= 8) { 105962306a36Sopenharmony_ci sl->xbits -= 8; 106062306a36Sopenharmony_ci c = (unsigned char)(sl->xdata >> sl->xbits); 106162306a36Sopenharmony_ci if (!test_bit(SLF_ERROR, &sl->flags)) { 106262306a36Sopenharmony_ci if (sl->rcount < sl->buffsize) { 106362306a36Sopenharmony_ci sl->rbuff[sl->rcount++] = c; 106462306a36Sopenharmony_ci return; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci sl->dev->stats.rx_over_errors++; 106762306a36Sopenharmony_ci set_bit(SLF_ERROR, &sl->flags); 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci#endif /* CONFIG_SLIP_MODE_SLIP6 */ 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci/* Perform I/O control on an active SLIP channel. */ 107562306a36Sopenharmony_cistatic int slip_ioctl(struct tty_struct *tty, unsigned int cmd, 107662306a36Sopenharmony_ci unsigned long arg) 107762306a36Sopenharmony_ci{ 107862306a36Sopenharmony_ci struct slip *sl = tty->disc_data; 107962306a36Sopenharmony_ci unsigned int tmp; 108062306a36Sopenharmony_ci int __user *p = (int __user *)arg; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci /* First make sure we're connected. */ 108362306a36Sopenharmony_ci if (!sl || sl->magic != SLIP_MAGIC) 108462306a36Sopenharmony_ci return -EINVAL; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci switch (cmd) { 108762306a36Sopenharmony_ci case SIOCGIFNAME: 108862306a36Sopenharmony_ci tmp = strlen(sl->dev->name) + 1; 108962306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, sl->dev->name, tmp)) 109062306a36Sopenharmony_ci return -EFAULT; 109162306a36Sopenharmony_ci return 0; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci case SIOCGIFENCAP: 109462306a36Sopenharmony_ci if (put_user(sl->mode, p)) 109562306a36Sopenharmony_ci return -EFAULT; 109662306a36Sopenharmony_ci return 0; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci case SIOCSIFENCAP: 109962306a36Sopenharmony_ci if (get_user(tmp, p)) 110062306a36Sopenharmony_ci return -EFAULT; 110162306a36Sopenharmony_ci#ifndef SL_INCLUDE_CSLIP 110262306a36Sopenharmony_ci if (tmp & (SL_MODE_CSLIP|SL_MODE_ADAPTIVE)) 110362306a36Sopenharmony_ci return -EINVAL; 110462306a36Sopenharmony_ci#else 110562306a36Sopenharmony_ci if ((tmp & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) == 110662306a36Sopenharmony_ci (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) 110762306a36Sopenharmony_ci /* return -EINVAL; */ 110862306a36Sopenharmony_ci tmp &= ~SL_MODE_ADAPTIVE; 110962306a36Sopenharmony_ci#endif 111062306a36Sopenharmony_ci#ifndef CONFIG_SLIP_MODE_SLIP6 111162306a36Sopenharmony_ci if (tmp & SL_MODE_SLIP6) 111262306a36Sopenharmony_ci return -EINVAL; 111362306a36Sopenharmony_ci#endif 111462306a36Sopenharmony_ci sl->mode = tmp; 111562306a36Sopenharmony_ci sl->dev->type = ARPHRD_SLIP + sl->mode; 111662306a36Sopenharmony_ci return 0; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci case SIOCSIFHWADDR: 111962306a36Sopenharmony_ci return -EINVAL; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci#ifdef CONFIG_SLIP_SMART 112262306a36Sopenharmony_ci /* VSV changes start here */ 112362306a36Sopenharmony_ci case SIOCSKEEPALIVE: 112462306a36Sopenharmony_ci if (get_user(tmp, p)) 112562306a36Sopenharmony_ci return -EFAULT; 112662306a36Sopenharmony_ci if (tmp > 255) /* max for unchar */ 112762306a36Sopenharmony_ci return -EINVAL; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci spin_lock_bh(&sl->lock); 113062306a36Sopenharmony_ci if (!sl->tty) { 113162306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 113262306a36Sopenharmony_ci return -ENODEV; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci sl->keepalive = (u8)tmp; 113562306a36Sopenharmony_ci if (sl->keepalive != 0) { 113662306a36Sopenharmony_ci mod_timer(&sl->keepalive_timer, 113762306a36Sopenharmony_ci jiffies + sl->keepalive * HZ); 113862306a36Sopenharmony_ci set_bit(SLF_KEEPTEST, &sl->flags); 113962306a36Sopenharmony_ci } else 114062306a36Sopenharmony_ci del_timer(&sl->keepalive_timer); 114162306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 114262306a36Sopenharmony_ci return 0; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci case SIOCGKEEPALIVE: 114562306a36Sopenharmony_ci if (put_user(sl->keepalive, p)) 114662306a36Sopenharmony_ci return -EFAULT; 114762306a36Sopenharmony_ci return 0; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci case SIOCSOUTFILL: 115062306a36Sopenharmony_ci if (get_user(tmp, p)) 115162306a36Sopenharmony_ci return -EFAULT; 115262306a36Sopenharmony_ci if (tmp > 255) /* max for unchar */ 115362306a36Sopenharmony_ci return -EINVAL; 115462306a36Sopenharmony_ci spin_lock_bh(&sl->lock); 115562306a36Sopenharmony_ci if (!sl->tty) { 115662306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 115762306a36Sopenharmony_ci return -ENODEV; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci sl->outfill = (u8)tmp; 116062306a36Sopenharmony_ci if (sl->outfill != 0) { 116162306a36Sopenharmony_ci mod_timer(&sl->outfill_timer, 116262306a36Sopenharmony_ci jiffies + sl->outfill * HZ); 116362306a36Sopenharmony_ci set_bit(SLF_OUTWAIT, &sl->flags); 116462306a36Sopenharmony_ci } else 116562306a36Sopenharmony_ci del_timer(&sl->outfill_timer); 116662306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 116762306a36Sopenharmony_ci return 0; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci case SIOCGOUTFILL: 117062306a36Sopenharmony_ci if (put_user(sl->outfill, p)) 117162306a36Sopenharmony_ci return -EFAULT; 117262306a36Sopenharmony_ci return 0; 117362306a36Sopenharmony_ci /* VSV changes end */ 117462306a36Sopenharmony_ci#endif 117562306a36Sopenharmony_ci default: 117662306a36Sopenharmony_ci return tty_mode_ioctl(tty, cmd, arg); 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci/* VSV changes start here */ 118162306a36Sopenharmony_ci#ifdef CONFIG_SLIP_SMART 118262306a36Sopenharmony_ci/* function sl_siocdevprivate called from net/core/dev.c 118362306a36Sopenharmony_ci to allow get/set outfill/keepalive parameter 118462306a36Sopenharmony_ci by ifconfig */ 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistatic int sl_siocdevprivate(struct net_device *dev, struct ifreq *rq, 118762306a36Sopenharmony_ci void __user *data, int cmd) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci struct slip *sl = netdev_priv(dev); 119062306a36Sopenharmony_ci unsigned long *p = (unsigned long *)&rq->ifr_ifru; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (sl == NULL) /* Allocation failed ?? */ 119362306a36Sopenharmony_ci return -ENODEV; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (in_compat_syscall()) 119662306a36Sopenharmony_ci return -EOPNOTSUPP; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci spin_lock_bh(&sl->lock); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci if (!sl->tty) { 120162306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 120262306a36Sopenharmony_ci return -ENODEV; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci switch (cmd) { 120662306a36Sopenharmony_ci case SIOCSKEEPALIVE: 120762306a36Sopenharmony_ci /* max for unchar */ 120862306a36Sopenharmony_ci if ((unsigned)*p > 255) { 120962306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 121062306a36Sopenharmony_ci return -EINVAL; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci sl->keepalive = (u8)*p; 121362306a36Sopenharmony_ci if (sl->keepalive != 0) { 121462306a36Sopenharmony_ci sl->keepalive_timer.expires = 121562306a36Sopenharmony_ci jiffies + sl->keepalive * HZ; 121662306a36Sopenharmony_ci mod_timer(&sl->keepalive_timer, 121762306a36Sopenharmony_ci jiffies + sl->keepalive * HZ); 121862306a36Sopenharmony_ci set_bit(SLF_KEEPTEST, &sl->flags); 121962306a36Sopenharmony_ci } else 122062306a36Sopenharmony_ci del_timer(&sl->keepalive_timer); 122162306a36Sopenharmony_ci break; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci case SIOCGKEEPALIVE: 122462306a36Sopenharmony_ci *p = sl->keepalive; 122562306a36Sopenharmony_ci break; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci case SIOCSOUTFILL: 122862306a36Sopenharmony_ci if ((unsigned)*p > 255) { /* max for unchar */ 122962306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 123062306a36Sopenharmony_ci return -EINVAL; 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci sl->outfill = (u8)*p; 123362306a36Sopenharmony_ci if (sl->outfill != 0) { 123462306a36Sopenharmony_ci mod_timer(&sl->outfill_timer, 123562306a36Sopenharmony_ci jiffies + sl->outfill * HZ); 123662306a36Sopenharmony_ci set_bit(SLF_OUTWAIT, &sl->flags); 123762306a36Sopenharmony_ci } else 123862306a36Sopenharmony_ci del_timer(&sl->outfill_timer); 123962306a36Sopenharmony_ci break; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci case SIOCGOUTFILL: 124262306a36Sopenharmony_ci *p = sl->outfill; 124362306a36Sopenharmony_ci break; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci case SIOCSLEASE: 124662306a36Sopenharmony_ci /* Resolve race condition, when ioctl'ing hanged up 124762306a36Sopenharmony_ci and opened by another process device. 124862306a36Sopenharmony_ci */ 124962306a36Sopenharmony_ci if (sl->tty != current->signal->tty && 125062306a36Sopenharmony_ci sl->pid != current->pid) { 125162306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 125262306a36Sopenharmony_ci return -EPERM; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci sl->leased = 0; 125562306a36Sopenharmony_ci if (*p) 125662306a36Sopenharmony_ci sl->leased = 1; 125762306a36Sopenharmony_ci break; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci case SIOCGLEASE: 126062306a36Sopenharmony_ci *p = sl->leased; 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 126362306a36Sopenharmony_ci return 0; 126462306a36Sopenharmony_ci} 126562306a36Sopenharmony_ci#endif 126662306a36Sopenharmony_ci/* VSV changes end */ 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_cistatic struct tty_ldisc_ops sl_ldisc = { 126962306a36Sopenharmony_ci .owner = THIS_MODULE, 127062306a36Sopenharmony_ci .num = N_SLIP, 127162306a36Sopenharmony_ci .name = "slip", 127262306a36Sopenharmony_ci .open = slip_open, 127362306a36Sopenharmony_ci .close = slip_close, 127462306a36Sopenharmony_ci .hangup = slip_hangup, 127562306a36Sopenharmony_ci .ioctl = slip_ioctl, 127662306a36Sopenharmony_ci .receive_buf = slip_receive_buf, 127762306a36Sopenharmony_ci .write_wakeup = slip_write_wakeup, 127862306a36Sopenharmony_ci}; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_cistatic int __init slip_init(void) 128162306a36Sopenharmony_ci{ 128262306a36Sopenharmony_ci int status; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (slip_maxdev < 4) 128562306a36Sopenharmony_ci slip_maxdev = 4; /* Sanity */ 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci printk(KERN_INFO "SLIP: version %s (dynamic channels, max=%d)" 128862306a36Sopenharmony_ci#ifdef CONFIG_SLIP_MODE_SLIP6 128962306a36Sopenharmony_ci " (6 bit encapsulation enabled)" 129062306a36Sopenharmony_ci#endif 129162306a36Sopenharmony_ci ".\n", 129262306a36Sopenharmony_ci SLIP_VERSION, slip_maxdev); 129362306a36Sopenharmony_ci#if defined(SL_INCLUDE_CSLIP) 129462306a36Sopenharmony_ci printk(KERN_INFO "CSLIP: code copyright 1989 Regents of the University of California.\n"); 129562306a36Sopenharmony_ci#endif 129662306a36Sopenharmony_ci#ifdef CONFIG_SLIP_SMART 129762306a36Sopenharmony_ci printk(KERN_INFO "SLIP linefill/keepalive option.\n"); 129862306a36Sopenharmony_ci#endif 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci slip_devs = kcalloc(slip_maxdev, sizeof(struct net_device *), 130162306a36Sopenharmony_ci GFP_KERNEL); 130262306a36Sopenharmony_ci if (!slip_devs) 130362306a36Sopenharmony_ci return -ENOMEM; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* Fill in our line protocol discipline, and register it */ 130662306a36Sopenharmony_ci status = tty_register_ldisc(&sl_ldisc); 130762306a36Sopenharmony_ci if (status != 0) { 130862306a36Sopenharmony_ci printk(KERN_ERR "SLIP: can't register line discipline (err = %d)\n", status); 130962306a36Sopenharmony_ci kfree(slip_devs); 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci return status; 131262306a36Sopenharmony_ci} 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_cistatic void __exit slip_exit(void) 131562306a36Sopenharmony_ci{ 131662306a36Sopenharmony_ci int i; 131762306a36Sopenharmony_ci struct net_device *dev; 131862306a36Sopenharmony_ci struct slip *sl; 131962306a36Sopenharmony_ci unsigned long timeout = jiffies + HZ; 132062306a36Sopenharmony_ci int busy = 0; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci if (slip_devs == NULL) 132362306a36Sopenharmony_ci return; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci /* First of all: check for active disciplines and hangup them. 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_ci do { 132862306a36Sopenharmony_ci if (busy) 132962306a36Sopenharmony_ci msleep_interruptible(100); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci busy = 0; 133262306a36Sopenharmony_ci for (i = 0; i < slip_maxdev; i++) { 133362306a36Sopenharmony_ci dev = slip_devs[i]; 133462306a36Sopenharmony_ci if (!dev) 133562306a36Sopenharmony_ci continue; 133662306a36Sopenharmony_ci sl = netdev_priv(dev); 133762306a36Sopenharmony_ci spin_lock_bh(&sl->lock); 133862306a36Sopenharmony_ci if (sl->tty) { 133962306a36Sopenharmony_ci busy++; 134062306a36Sopenharmony_ci tty_hangup(sl->tty); 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci spin_unlock_bh(&sl->lock); 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci } while (busy && time_before(jiffies, timeout)); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci /* FIXME: hangup is async so we should wait when doing this second 134762306a36Sopenharmony_ci phase */ 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci for (i = 0; i < slip_maxdev; i++) { 135062306a36Sopenharmony_ci dev = slip_devs[i]; 135162306a36Sopenharmony_ci if (!dev) 135262306a36Sopenharmony_ci continue; 135362306a36Sopenharmony_ci slip_devs[i] = NULL; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci sl = netdev_priv(dev); 135662306a36Sopenharmony_ci if (sl->tty) { 135762306a36Sopenharmony_ci printk(KERN_ERR "%s: tty discipline still running\n", 135862306a36Sopenharmony_ci dev->name); 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci unregister_netdev(dev); 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci kfree(slip_devs); 136562306a36Sopenharmony_ci slip_devs = NULL; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci tty_unregister_ldisc(&sl_ldisc); 136862306a36Sopenharmony_ci} 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_cimodule_init(slip_init); 137162306a36Sopenharmony_cimodule_exit(slip_exit); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci#ifdef CONFIG_SLIP_SMART 137462306a36Sopenharmony_ci/* 137562306a36Sopenharmony_ci * This is start of the code for multislip style line checking 137662306a36Sopenharmony_ci * added by Stanislav Voronyi. All changes before marked VSV 137762306a36Sopenharmony_ci */ 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_cistatic void sl_outfill(struct timer_list *t) 138062306a36Sopenharmony_ci{ 138162306a36Sopenharmony_ci struct slip *sl = from_timer(sl, t, outfill_timer); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci spin_lock(&sl->lock); 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci if (sl->tty == NULL) 138662306a36Sopenharmony_ci goto out; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci if (sl->outfill) { 138962306a36Sopenharmony_ci if (test_bit(SLF_OUTWAIT, &sl->flags)) { 139062306a36Sopenharmony_ci /* no packets were transmitted, do outfill */ 139162306a36Sopenharmony_ci#ifdef CONFIG_SLIP_MODE_SLIP6 139262306a36Sopenharmony_ci unsigned char s = (sl->mode & SL_MODE_SLIP6)?0x70:END; 139362306a36Sopenharmony_ci#else 139462306a36Sopenharmony_ci unsigned char s = END; 139562306a36Sopenharmony_ci#endif 139662306a36Sopenharmony_ci /* put END into tty queue. Is it right ??? */ 139762306a36Sopenharmony_ci if (!netif_queue_stopped(sl->dev)) { 139862306a36Sopenharmony_ci /* if device busy no outfill */ 139962306a36Sopenharmony_ci sl->tty->ops->write(sl->tty, &s, 1); 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci } else 140262306a36Sopenharmony_ci set_bit(SLF_OUTWAIT, &sl->flags); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci mod_timer(&sl->outfill_timer, jiffies+sl->outfill*HZ); 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ciout: 140762306a36Sopenharmony_ci spin_unlock(&sl->lock); 140862306a36Sopenharmony_ci} 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_cistatic void sl_keepalive(struct timer_list *t) 141162306a36Sopenharmony_ci{ 141262306a36Sopenharmony_ci struct slip *sl = from_timer(sl, t, keepalive_timer); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci spin_lock(&sl->lock); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci if (sl->tty == NULL) 141762306a36Sopenharmony_ci goto out; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci if (sl->keepalive) { 142062306a36Sopenharmony_ci if (test_bit(SLF_KEEPTEST, &sl->flags)) { 142162306a36Sopenharmony_ci /* keepalive still high :(, we must hangup */ 142262306a36Sopenharmony_ci if (sl->outfill) 142362306a36Sopenharmony_ci /* outfill timer must be deleted too */ 142462306a36Sopenharmony_ci (void)del_timer(&sl->outfill_timer); 142562306a36Sopenharmony_ci printk(KERN_DEBUG "%s: no packets received during keepalive timeout, hangup.\n", sl->dev->name); 142662306a36Sopenharmony_ci /* this must hangup tty & close slip */ 142762306a36Sopenharmony_ci tty_hangup(sl->tty); 142862306a36Sopenharmony_ci /* I think we need not something else */ 142962306a36Sopenharmony_ci goto out; 143062306a36Sopenharmony_ci } else 143162306a36Sopenharmony_ci set_bit(SLF_KEEPTEST, &sl->flags); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci mod_timer(&sl->keepalive_timer, jiffies+sl->keepalive*HZ); 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ciout: 143662306a36Sopenharmony_ci spin_unlock(&sl->lock); 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci#endif 144062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 144162306a36Sopenharmony_ciMODULE_ALIAS_LDISC(N_SLIP); 1442