162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PPP async serial channel driver for Linux. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 1999 Paul Mackerras. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This driver provides the encapsulation and framing for sending 862306a36Sopenharmony_ci * and receiving PPP frames over async serial lines. It relies on 962306a36Sopenharmony_ci * the generic PPP layer to give it frames to send and to process 1062306a36Sopenharmony_ci * received frames. It implements the PPP line discipline. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Part of the code in this driver was inspired by the old async-only 1362306a36Sopenharmony_ci * PPP driver, written by Michael Callahan and Al Longyear, and 1462306a36Sopenharmony_ci * subsequently hacked by Paul Mackerras. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/skbuff.h> 2062306a36Sopenharmony_ci#include <linux/tty.h> 2162306a36Sopenharmony_ci#include <linux/netdevice.h> 2262306a36Sopenharmony_ci#include <linux/poll.h> 2362306a36Sopenharmony_ci#include <linux/crc-ccitt.h> 2462306a36Sopenharmony_ci#include <linux/ppp_defs.h> 2562306a36Sopenharmony_ci#include <linux/ppp-ioctl.h> 2662306a36Sopenharmony_ci#include <linux/ppp_channel.h> 2762306a36Sopenharmony_ci#include <linux/spinlock.h> 2862306a36Sopenharmony_ci#include <linux/init.h> 2962306a36Sopenharmony_ci#include <linux/interrupt.h> 3062306a36Sopenharmony_ci#include <linux/jiffies.h> 3162306a36Sopenharmony_ci#include <linux/slab.h> 3262306a36Sopenharmony_ci#include <asm/unaligned.h> 3362306a36Sopenharmony_ci#include <linux/uaccess.h> 3462306a36Sopenharmony_ci#include <asm/string.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define PPP_VERSION "2.4.2" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define OBUFSIZE 4096 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* Structure for storing local state. */ 4162306a36Sopenharmony_cistruct asyncppp { 4262306a36Sopenharmony_ci struct tty_struct *tty; 4362306a36Sopenharmony_ci unsigned int flags; 4462306a36Sopenharmony_ci unsigned int state; 4562306a36Sopenharmony_ci unsigned int rbits; 4662306a36Sopenharmony_ci int mru; 4762306a36Sopenharmony_ci spinlock_t xmit_lock; 4862306a36Sopenharmony_ci spinlock_t recv_lock; 4962306a36Sopenharmony_ci unsigned long xmit_flags; 5062306a36Sopenharmony_ci u32 xaccm[8]; 5162306a36Sopenharmony_ci u32 raccm; 5262306a36Sopenharmony_ci unsigned int bytes_sent; 5362306a36Sopenharmony_ci unsigned int bytes_rcvd; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci struct sk_buff *tpkt; 5662306a36Sopenharmony_ci int tpkt_pos; 5762306a36Sopenharmony_ci u16 tfcs; 5862306a36Sopenharmony_ci unsigned char *optr; 5962306a36Sopenharmony_ci unsigned char *olim; 6062306a36Sopenharmony_ci unsigned long last_xmit; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci struct sk_buff *rpkt; 6362306a36Sopenharmony_ci int lcp_fcs; 6462306a36Sopenharmony_ci struct sk_buff_head rqueue; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci struct tasklet_struct tsk; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci refcount_t refcnt; 6962306a36Sopenharmony_ci struct completion dead; 7062306a36Sopenharmony_ci struct ppp_channel chan; /* interface to generic ppp layer */ 7162306a36Sopenharmony_ci unsigned char obuf[OBUFSIZE]; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* Bit numbers in xmit_flags */ 7562306a36Sopenharmony_ci#define XMIT_WAKEUP 0 7662306a36Sopenharmony_ci#define XMIT_FULL 1 7762306a36Sopenharmony_ci#define XMIT_BUSY 2 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* State bits */ 8062306a36Sopenharmony_ci#define SC_TOSS 1 8162306a36Sopenharmony_ci#define SC_ESCAPE 2 8262306a36Sopenharmony_ci#define SC_PREV_ERROR 4 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* Bits in rbits */ 8562306a36Sopenharmony_ci#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int flag_time = HZ; 8862306a36Sopenharmony_cimodule_param(flag_time, int, 0); 8962306a36Sopenharmony_ciMODULE_PARM_DESC(flag_time, "ppp_async: interval between flagged packets (in clock ticks)"); 9062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 9162306a36Sopenharmony_ciMODULE_ALIAS_LDISC(N_PPP); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* 9462306a36Sopenharmony_ci * Prototypes. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_cistatic int ppp_async_encode(struct asyncppp *ap); 9762306a36Sopenharmony_cistatic int ppp_async_send(struct ppp_channel *chan, struct sk_buff *skb); 9862306a36Sopenharmony_cistatic int ppp_async_push(struct asyncppp *ap); 9962306a36Sopenharmony_cistatic void ppp_async_flush_output(struct asyncppp *ap); 10062306a36Sopenharmony_cistatic void ppp_async_input(struct asyncppp *ap, const unsigned char *buf, 10162306a36Sopenharmony_ci const u8 *flags, int count); 10262306a36Sopenharmony_cistatic int ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, 10362306a36Sopenharmony_ci unsigned long arg); 10462306a36Sopenharmony_cistatic void ppp_async_process(struct tasklet_struct *t); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void async_lcp_peek(struct asyncppp *ap, unsigned char *data, 10762306a36Sopenharmony_ci int len, int inbound); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic const struct ppp_channel_ops async_ops = { 11062306a36Sopenharmony_ci .start_xmit = ppp_async_send, 11162306a36Sopenharmony_ci .ioctl = ppp_async_ioctl, 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 11562306a36Sopenharmony_ci * Routines implementing the PPP line discipline. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* 11962306a36Sopenharmony_ci * We have a potential race on dereferencing tty->disc_data, 12062306a36Sopenharmony_ci * because the tty layer provides no locking at all - thus one 12162306a36Sopenharmony_ci * cpu could be running ppp_asynctty_receive while another 12262306a36Sopenharmony_ci * calls ppp_asynctty_close, which zeroes tty->disc_data and 12362306a36Sopenharmony_ci * frees the memory that ppp_asynctty_receive is using. The best 12462306a36Sopenharmony_ci * way to fix this is to use a rwlock in the tty struct, but for now 12562306a36Sopenharmony_ci * we use a single global rwlock for all ttys in ppp line discipline. 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * FIXME: this is no longer true. The _close path for the ldisc is 12862306a36Sopenharmony_ci * now guaranteed to be sane. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic DEFINE_RWLOCK(disc_data_lock); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic struct asyncppp *ap_get(struct tty_struct *tty) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct asyncppp *ap; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci read_lock(&disc_data_lock); 13762306a36Sopenharmony_ci ap = tty->disc_data; 13862306a36Sopenharmony_ci if (ap != NULL) 13962306a36Sopenharmony_ci refcount_inc(&ap->refcnt); 14062306a36Sopenharmony_ci read_unlock(&disc_data_lock); 14162306a36Sopenharmony_ci return ap; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void ap_put(struct asyncppp *ap) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci if (refcount_dec_and_test(&ap->refcnt)) 14762306a36Sopenharmony_ci complete(&ap->dead); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* 15162306a36Sopenharmony_ci * Called when a tty is put into PPP line discipline. Called in process 15262306a36Sopenharmony_ci * context. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic int 15562306a36Sopenharmony_cippp_asynctty_open(struct tty_struct *tty) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct asyncppp *ap; 15862306a36Sopenharmony_ci int err; 15962306a36Sopenharmony_ci int speed; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (tty->ops->write == NULL) 16262306a36Sopenharmony_ci return -EOPNOTSUPP; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci err = -ENOMEM; 16562306a36Sopenharmony_ci ap = kzalloc(sizeof(*ap), GFP_KERNEL); 16662306a36Sopenharmony_ci if (!ap) 16762306a36Sopenharmony_ci goto out; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* initialize the asyncppp structure */ 17062306a36Sopenharmony_ci ap->tty = tty; 17162306a36Sopenharmony_ci ap->mru = PPP_MRU; 17262306a36Sopenharmony_ci spin_lock_init(&ap->xmit_lock); 17362306a36Sopenharmony_ci spin_lock_init(&ap->recv_lock); 17462306a36Sopenharmony_ci ap->xaccm[0] = ~0U; 17562306a36Sopenharmony_ci ap->xaccm[3] = 0x60000000U; 17662306a36Sopenharmony_ci ap->raccm = ~0U; 17762306a36Sopenharmony_ci ap->optr = ap->obuf; 17862306a36Sopenharmony_ci ap->olim = ap->obuf; 17962306a36Sopenharmony_ci ap->lcp_fcs = -1; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci skb_queue_head_init(&ap->rqueue); 18262306a36Sopenharmony_ci tasklet_setup(&ap->tsk, ppp_async_process); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci refcount_set(&ap->refcnt, 1); 18562306a36Sopenharmony_ci init_completion(&ap->dead); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci ap->chan.private = ap; 18862306a36Sopenharmony_ci ap->chan.ops = &async_ops; 18962306a36Sopenharmony_ci ap->chan.mtu = PPP_MRU; 19062306a36Sopenharmony_ci speed = tty_get_baud_rate(tty); 19162306a36Sopenharmony_ci ap->chan.speed = speed; 19262306a36Sopenharmony_ci err = ppp_register_channel(&ap->chan); 19362306a36Sopenharmony_ci if (err) 19462306a36Sopenharmony_ci goto out_free; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci tty->disc_data = ap; 19762306a36Sopenharmony_ci tty->receive_room = 65536; 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci out_free: 20162306a36Sopenharmony_ci kfree(ap); 20262306a36Sopenharmony_ci out: 20362306a36Sopenharmony_ci return err; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* 20762306a36Sopenharmony_ci * Called when the tty is put into another line discipline 20862306a36Sopenharmony_ci * or it hangs up. We have to wait for any cpu currently 20962306a36Sopenharmony_ci * executing in any of the other ppp_asynctty_* routines to 21062306a36Sopenharmony_ci * finish before we can call ppp_unregister_channel and free 21162306a36Sopenharmony_ci * the asyncppp struct. This routine must be called from 21262306a36Sopenharmony_ci * process context, not interrupt or softirq context. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_cistatic void 21562306a36Sopenharmony_cippp_asynctty_close(struct tty_struct *tty) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct asyncppp *ap; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci write_lock_irq(&disc_data_lock); 22062306a36Sopenharmony_ci ap = tty->disc_data; 22162306a36Sopenharmony_ci tty->disc_data = NULL; 22262306a36Sopenharmony_ci write_unlock_irq(&disc_data_lock); 22362306a36Sopenharmony_ci if (!ap) 22462306a36Sopenharmony_ci return; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * We have now ensured that nobody can start using ap from now 22862306a36Sopenharmony_ci * on, but we have to wait for all existing users to finish. 22962306a36Sopenharmony_ci * Note that ppp_unregister_channel ensures that no calls to 23062306a36Sopenharmony_ci * our channel ops (i.e. ppp_async_send/ioctl) are in progress 23162306a36Sopenharmony_ci * by the time it returns. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci if (!refcount_dec_and_test(&ap->refcnt)) 23462306a36Sopenharmony_ci wait_for_completion(&ap->dead); 23562306a36Sopenharmony_ci tasklet_kill(&ap->tsk); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci ppp_unregister_channel(&ap->chan); 23862306a36Sopenharmony_ci kfree_skb(ap->rpkt); 23962306a36Sopenharmony_ci skb_queue_purge(&ap->rqueue); 24062306a36Sopenharmony_ci kfree_skb(ap->tpkt); 24162306a36Sopenharmony_ci kfree(ap); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/* 24562306a36Sopenharmony_ci * Called on tty hangup in process context. 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * Wait for I/O to driver to complete and unregister PPP channel. 24862306a36Sopenharmony_ci * This is already done by the close routine, so just call that. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_cistatic void ppp_asynctty_hangup(struct tty_struct *tty) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci ppp_asynctty_close(tty); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/* 25662306a36Sopenharmony_ci * Read does nothing - no data is ever available this way. 25762306a36Sopenharmony_ci * Pppd reads and writes packets via /dev/ppp instead. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_cistatic ssize_t 26062306a36Sopenharmony_cippp_asynctty_read(struct tty_struct *tty, struct file *file, u8 *buf, 26162306a36Sopenharmony_ci size_t count, void **cookie, unsigned long offset) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci return -EAGAIN; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* 26762306a36Sopenharmony_ci * Write on the tty does nothing, the packets all come in 26862306a36Sopenharmony_ci * from the ppp generic stuff. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_cistatic ssize_t 27162306a36Sopenharmony_cippp_asynctty_write(struct tty_struct *tty, struct file *file, const u8 *buf, 27262306a36Sopenharmony_ci size_t count) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci return -EAGAIN; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci * Called in process context only. May be re-entered by multiple 27962306a36Sopenharmony_ci * ioctl calling threads. 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int 28362306a36Sopenharmony_cippp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct asyncppp *ap = ap_get(tty); 28662306a36Sopenharmony_ci int err, val; 28762306a36Sopenharmony_ci int __user *p = (int __user *)arg; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (!ap) 29062306a36Sopenharmony_ci return -ENXIO; 29162306a36Sopenharmony_ci err = -EFAULT; 29262306a36Sopenharmony_ci switch (cmd) { 29362306a36Sopenharmony_ci case PPPIOCGCHAN: 29462306a36Sopenharmony_ci err = -EFAULT; 29562306a36Sopenharmony_ci if (put_user(ppp_channel_index(&ap->chan), p)) 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci err = 0; 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci case PPPIOCGUNIT: 30162306a36Sopenharmony_ci err = -EFAULT; 30262306a36Sopenharmony_ci if (put_user(ppp_unit_number(&ap->chan), p)) 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci err = 0; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci case TCFLSH: 30862306a36Sopenharmony_ci /* flush our buffers and the serial port's buffer */ 30962306a36Sopenharmony_ci if (arg == TCIOFLUSH || arg == TCOFLUSH) 31062306a36Sopenharmony_ci ppp_async_flush_output(ap); 31162306a36Sopenharmony_ci err = n_tty_ioctl_helper(tty, cmd, arg); 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci case FIONREAD: 31562306a36Sopenharmony_ci val = 0; 31662306a36Sopenharmony_ci if (put_user(val, p)) 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci err = 0; 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci default: 32262306a36Sopenharmony_ci /* Try the various mode ioctls */ 32362306a36Sopenharmony_ci err = tty_mode_ioctl(tty, cmd, arg); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ap_put(ap); 32762306a36Sopenharmony_ci return err; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/* May sleep, don't call from interrupt level or with interrupts disabled */ 33162306a36Sopenharmony_cistatic void 33262306a36Sopenharmony_cippp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags, 33362306a36Sopenharmony_ci size_t count) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct asyncppp *ap = ap_get(tty); 33662306a36Sopenharmony_ci unsigned long flags; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!ap) 33962306a36Sopenharmony_ci return; 34062306a36Sopenharmony_ci spin_lock_irqsave(&ap->recv_lock, flags); 34162306a36Sopenharmony_ci ppp_async_input(ap, buf, cflags, count); 34262306a36Sopenharmony_ci spin_unlock_irqrestore(&ap->recv_lock, flags); 34362306a36Sopenharmony_ci if (!skb_queue_empty(&ap->rqueue)) 34462306a36Sopenharmony_ci tasklet_schedule(&ap->tsk); 34562306a36Sopenharmony_ci ap_put(ap); 34662306a36Sopenharmony_ci tty_unthrottle(tty); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void 35062306a36Sopenharmony_cippp_asynctty_wakeup(struct tty_struct *tty) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct asyncppp *ap = ap_get(tty); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 35562306a36Sopenharmony_ci if (!ap) 35662306a36Sopenharmony_ci return; 35762306a36Sopenharmony_ci set_bit(XMIT_WAKEUP, &ap->xmit_flags); 35862306a36Sopenharmony_ci tasklet_schedule(&ap->tsk); 35962306a36Sopenharmony_ci ap_put(ap); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic struct tty_ldisc_ops ppp_ldisc = { 36462306a36Sopenharmony_ci .owner = THIS_MODULE, 36562306a36Sopenharmony_ci .num = N_PPP, 36662306a36Sopenharmony_ci .name = "ppp", 36762306a36Sopenharmony_ci .open = ppp_asynctty_open, 36862306a36Sopenharmony_ci .close = ppp_asynctty_close, 36962306a36Sopenharmony_ci .hangup = ppp_asynctty_hangup, 37062306a36Sopenharmony_ci .read = ppp_asynctty_read, 37162306a36Sopenharmony_ci .write = ppp_asynctty_write, 37262306a36Sopenharmony_ci .ioctl = ppp_asynctty_ioctl, 37362306a36Sopenharmony_ci .receive_buf = ppp_asynctty_receive, 37462306a36Sopenharmony_ci .write_wakeup = ppp_asynctty_wakeup, 37562306a36Sopenharmony_ci}; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int __init 37862306a36Sopenharmony_cippp_async_init(void) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci int err; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci err = tty_register_ldisc(&ppp_ldisc); 38362306a36Sopenharmony_ci if (err != 0) 38462306a36Sopenharmony_ci printk(KERN_ERR "PPP_async: error %d registering line disc.\n", 38562306a36Sopenharmony_ci err); 38662306a36Sopenharmony_ci return err; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/* 39062306a36Sopenharmony_ci * The following routines provide the PPP channel interface. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_cistatic int 39362306a36Sopenharmony_cippp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct asyncppp *ap = chan->private; 39662306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 39762306a36Sopenharmony_ci int __user *p = argp; 39862306a36Sopenharmony_ci int err, val; 39962306a36Sopenharmony_ci u32 accm[8]; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci err = -EFAULT; 40262306a36Sopenharmony_ci switch (cmd) { 40362306a36Sopenharmony_ci case PPPIOCGFLAGS: 40462306a36Sopenharmony_ci val = ap->flags | ap->rbits; 40562306a36Sopenharmony_ci if (put_user(val, p)) 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci err = 0; 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci case PPPIOCSFLAGS: 41062306a36Sopenharmony_ci if (get_user(val, p)) 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci ap->flags = val & ~SC_RCV_BITS; 41362306a36Sopenharmony_ci spin_lock_irq(&ap->recv_lock); 41462306a36Sopenharmony_ci ap->rbits = val & SC_RCV_BITS; 41562306a36Sopenharmony_ci spin_unlock_irq(&ap->recv_lock); 41662306a36Sopenharmony_ci err = 0; 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci case PPPIOCGASYNCMAP: 42062306a36Sopenharmony_ci if (put_user(ap->xaccm[0], (u32 __user *)argp)) 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci err = 0; 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci case PPPIOCSASYNCMAP: 42562306a36Sopenharmony_ci if (get_user(ap->xaccm[0], (u32 __user *)argp)) 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci err = 0; 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci case PPPIOCGRASYNCMAP: 43162306a36Sopenharmony_ci if (put_user(ap->raccm, (u32 __user *)argp)) 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci err = 0; 43462306a36Sopenharmony_ci break; 43562306a36Sopenharmony_ci case PPPIOCSRASYNCMAP: 43662306a36Sopenharmony_ci if (get_user(ap->raccm, (u32 __user *)argp)) 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci err = 0; 43962306a36Sopenharmony_ci break; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci case PPPIOCGXASYNCMAP: 44262306a36Sopenharmony_ci if (copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm))) 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci err = 0; 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci case PPPIOCSXASYNCMAP: 44762306a36Sopenharmony_ci if (copy_from_user(accm, argp, sizeof(accm))) 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci accm[2] &= ~0x40000000U; /* can't escape 0x5e */ 45062306a36Sopenharmony_ci accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ 45162306a36Sopenharmony_ci memcpy(ap->xaccm, accm, sizeof(ap->xaccm)); 45262306a36Sopenharmony_ci err = 0; 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci case PPPIOCGMRU: 45662306a36Sopenharmony_ci if (put_user(ap->mru, p)) 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci err = 0; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci case PPPIOCSMRU: 46162306a36Sopenharmony_ci if (get_user(val, p)) 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci if (val > U16_MAX) { 46462306a36Sopenharmony_ci err = -EINVAL; 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci if (val < PPP_MRU) 46862306a36Sopenharmony_ci val = PPP_MRU; 46962306a36Sopenharmony_ci ap->mru = val; 47062306a36Sopenharmony_ci err = 0; 47162306a36Sopenharmony_ci break; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci default: 47462306a36Sopenharmony_ci err = -ENOTTY; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return err; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci/* 48162306a36Sopenharmony_ci * This is called at softirq level to deliver received packets 48262306a36Sopenharmony_ci * to the ppp_generic code, and to tell the ppp_generic code 48362306a36Sopenharmony_ci * if we can accept more output now. 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_cistatic void ppp_async_process(struct tasklet_struct *t) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct asyncppp *ap = from_tasklet(ap, t, tsk); 48862306a36Sopenharmony_ci struct sk_buff *skb; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* process received packets */ 49162306a36Sopenharmony_ci while ((skb = skb_dequeue(&ap->rqueue)) != NULL) { 49262306a36Sopenharmony_ci if (skb->cb[0]) 49362306a36Sopenharmony_ci ppp_input_error(&ap->chan, 0); 49462306a36Sopenharmony_ci ppp_input(&ap->chan, skb); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* try to push more stuff out */ 49862306a36Sopenharmony_ci if (test_bit(XMIT_WAKEUP, &ap->xmit_flags) && ppp_async_push(ap)) 49962306a36Sopenharmony_ci ppp_output_wakeup(&ap->chan); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/* 50362306a36Sopenharmony_ci * Procedures for encapsulation and framing. 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/* 50762306a36Sopenharmony_ci * Procedure to encode the data for async serial transmission. 50862306a36Sopenharmony_ci * Does octet stuffing (escaping), puts the address/control bytes 50962306a36Sopenharmony_ci * on if A/C compression is disabled, and does protocol compression. 51062306a36Sopenharmony_ci * Assumes ap->tpkt != 0 on entry. 51162306a36Sopenharmony_ci * Returns 1 if we finished the current frame, 0 otherwise. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci#define PUT_BYTE(ap, buf, c, islcp) do { \ 51562306a36Sopenharmony_ci if ((islcp && c < 0x20) || (ap->xaccm[c >> 5] & (1 << (c & 0x1f)))) {\ 51662306a36Sopenharmony_ci *buf++ = PPP_ESCAPE; \ 51762306a36Sopenharmony_ci *buf++ = c ^ PPP_TRANS; \ 51862306a36Sopenharmony_ci } else \ 51962306a36Sopenharmony_ci *buf++ = c; \ 52062306a36Sopenharmony_ci} while (0) 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int 52362306a36Sopenharmony_cippp_async_encode(struct asyncppp *ap) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci int fcs, i, count, c, proto; 52662306a36Sopenharmony_ci unsigned char *buf, *buflim; 52762306a36Sopenharmony_ci unsigned char *data; 52862306a36Sopenharmony_ci int islcp; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci buf = ap->obuf; 53162306a36Sopenharmony_ci ap->olim = buf; 53262306a36Sopenharmony_ci ap->optr = buf; 53362306a36Sopenharmony_ci i = ap->tpkt_pos; 53462306a36Sopenharmony_ci data = ap->tpkt->data; 53562306a36Sopenharmony_ci count = ap->tpkt->len; 53662306a36Sopenharmony_ci fcs = ap->tfcs; 53762306a36Sopenharmony_ci proto = get_unaligned_be16(data); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* 54062306a36Sopenharmony_ci * LCP packets with code values between 1 (configure-reqest) 54162306a36Sopenharmony_ci * and 7 (code-reject) must be sent as though no options 54262306a36Sopenharmony_ci * had been negotiated. 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_ci islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (i == 0) { 54762306a36Sopenharmony_ci if (islcp) 54862306a36Sopenharmony_ci async_lcp_peek(ap, data, count, 0); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* 55162306a36Sopenharmony_ci * Start of a new packet - insert the leading FLAG 55262306a36Sopenharmony_ci * character if necessary. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci if (islcp || flag_time == 0 || 55562306a36Sopenharmony_ci time_after_eq(jiffies, ap->last_xmit + flag_time)) 55662306a36Sopenharmony_ci *buf++ = PPP_FLAG; 55762306a36Sopenharmony_ci ap->last_xmit = jiffies; 55862306a36Sopenharmony_ci fcs = PPP_INITFCS; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* 56162306a36Sopenharmony_ci * Put in the address/control bytes if necessary 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_ci if ((ap->flags & SC_COMP_AC) == 0 || islcp) { 56462306a36Sopenharmony_ci PUT_BYTE(ap, buf, 0xff, islcp); 56562306a36Sopenharmony_ci fcs = PPP_FCS(fcs, 0xff); 56662306a36Sopenharmony_ci PUT_BYTE(ap, buf, 0x03, islcp); 56762306a36Sopenharmony_ci fcs = PPP_FCS(fcs, 0x03); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* 57262306a36Sopenharmony_ci * Once we put in the last byte, we need to put in the FCS 57362306a36Sopenharmony_ci * and closing flag, so make sure there is at least 7 bytes 57462306a36Sopenharmony_ci * of free space in the output buffer. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci buflim = ap->obuf + OBUFSIZE - 6; 57762306a36Sopenharmony_ci while (i < count && buf < buflim) { 57862306a36Sopenharmony_ci c = data[i++]; 57962306a36Sopenharmony_ci if (i == 1 && c == 0 && (ap->flags & SC_COMP_PROT)) 58062306a36Sopenharmony_ci continue; /* compress protocol field */ 58162306a36Sopenharmony_ci fcs = PPP_FCS(fcs, c); 58262306a36Sopenharmony_ci PUT_BYTE(ap, buf, c, islcp); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (i < count) { 58662306a36Sopenharmony_ci /* 58762306a36Sopenharmony_ci * Remember where we are up to in this packet. 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_ci ap->olim = buf; 59062306a36Sopenharmony_ci ap->tpkt_pos = i; 59162306a36Sopenharmony_ci ap->tfcs = fcs; 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* 59662306a36Sopenharmony_ci * We have finished the packet. Add the FCS and flag. 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_ci fcs = ~fcs; 59962306a36Sopenharmony_ci c = fcs & 0xff; 60062306a36Sopenharmony_ci PUT_BYTE(ap, buf, c, islcp); 60162306a36Sopenharmony_ci c = (fcs >> 8) & 0xff; 60262306a36Sopenharmony_ci PUT_BYTE(ap, buf, c, islcp); 60362306a36Sopenharmony_ci *buf++ = PPP_FLAG; 60462306a36Sopenharmony_ci ap->olim = buf; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci consume_skb(ap->tpkt); 60762306a36Sopenharmony_ci ap->tpkt = NULL; 60862306a36Sopenharmony_ci return 1; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci/* 61262306a36Sopenharmony_ci * Transmit-side routines. 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci/* 61662306a36Sopenharmony_ci * Send a packet to the peer over an async tty line. 61762306a36Sopenharmony_ci * Returns 1 iff the packet was accepted. 61862306a36Sopenharmony_ci * If the packet was not accepted, we will call ppp_output_wakeup 61962306a36Sopenharmony_ci * at some later time. 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_cistatic int 62262306a36Sopenharmony_cippp_async_send(struct ppp_channel *chan, struct sk_buff *skb) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci struct asyncppp *ap = chan->private; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci ppp_async_push(ap); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags)) 62962306a36Sopenharmony_ci return 0; /* already full */ 63062306a36Sopenharmony_ci ap->tpkt = skb; 63162306a36Sopenharmony_ci ap->tpkt_pos = 0; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ppp_async_push(ap); 63462306a36Sopenharmony_ci return 1; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* 63862306a36Sopenharmony_ci * Push as much data as possible out to the tty. 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_cistatic int 64162306a36Sopenharmony_cippp_async_push(struct asyncppp *ap) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci int avail, sent, done = 0; 64462306a36Sopenharmony_ci struct tty_struct *tty = ap->tty; 64562306a36Sopenharmony_ci int tty_stuffed = 0; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* 64862306a36Sopenharmony_ci * We can get called recursively here if the tty write 64962306a36Sopenharmony_ci * function calls our wakeup function. This can happen 65062306a36Sopenharmony_ci * for example on a pty with both the master and slave 65162306a36Sopenharmony_ci * set to PPP line discipline. 65262306a36Sopenharmony_ci * We use the XMIT_BUSY bit to detect this and get out, 65362306a36Sopenharmony_ci * leaving the XMIT_WAKEUP bit set to tell the other 65462306a36Sopenharmony_ci * instance that it may now be able to write more now. 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_ci if (test_and_set_bit(XMIT_BUSY, &ap->xmit_flags)) 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci spin_lock_bh(&ap->xmit_lock); 65962306a36Sopenharmony_ci for (;;) { 66062306a36Sopenharmony_ci if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags)) 66162306a36Sopenharmony_ci tty_stuffed = 0; 66262306a36Sopenharmony_ci if (!tty_stuffed && ap->optr < ap->olim) { 66362306a36Sopenharmony_ci avail = ap->olim - ap->optr; 66462306a36Sopenharmony_ci set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 66562306a36Sopenharmony_ci sent = tty->ops->write(tty, ap->optr, avail); 66662306a36Sopenharmony_ci if (sent < 0) 66762306a36Sopenharmony_ci goto flush; /* error, e.g. loss of CD */ 66862306a36Sopenharmony_ci ap->optr += sent; 66962306a36Sopenharmony_ci if (sent < avail) 67062306a36Sopenharmony_ci tty_stuffed = 1; 67162306a36Sopenharmony_ci continue; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci if (ap->optr >= ap->olim && ap->tpkt) { 67462306a36Sopenharmony_ci if (ppp_async_encode(ap)) { 67562306a36Sopenharmony_ci /* finished processing ap->tpkt */ 67662306a36Sopenharmony_ci clear_bit(XMIT_FULL, &ap->xmit_flags); 67762306a36Sopenharmony_ci done = 1; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci continue; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci /* 68262306a36Sopenharmony_ci * We haven't made any progress this time around. 68362306a36Sopenharmony_ci * Clear XMIT_BUSY to let other callers in, but 68462306a36Sopenharmony_ci * after doing so we have to check if anyone set 68562306a36Sopenharmony_ci * XMIT_WAKEUP since we last checked it. If they 68662306a36Sopenharmony_ci * did, we should try again to set XMIT_BUSY and go 68762306a36Sopenharmony_ci * around again in case XMIT_BUSY was still set when 68862306a36Sopenharmony_ci * the other caller tried. 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_ci clear_bit(XMIT_BUSY, &ap->xmit_flags); 69162306a36Sopenharmony_ci /* any more work to do? if not, exit the loop */ 69262306a36Sopenharmony_ci if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) || 69362306a36Sopenharmony_ci (!tty_stuffed && ap->tpkt))) 69462306a36Sopenharmony_ci break; 69562306a36Sopenharmony_ci /* more work to do, see if we can do it now */ 69662306a36Sopenharmony_ci if (test_and_set_bit(XMIT_BUSY, &ap->xmit_flags)) 69762306a36Sopenharmony_ci break; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci spin_unlock_bh(&ap->xmit_lock); 70062306a36Sopenharmony_ci return done; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ciflush: 70362306a36Sopenharmony_ci clear_bit(XMIT_BUSY, &ap->xmit_flags); 70462306a36Sopenharmony_ci if (ap->tpkt) { 70562306a36Sopenharmony_ci kfree_skb(ap->tpkt); 70662306a36Sopenharmony_ci ap->tpkt = NULL; 70762306a36Sopenharmony_ci clear_bit(XMIT_FULL, &ap->xmit_flags); 70862306a36Sopenharmony_ci done = 1; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci ap->optr = ap->olim; 71162306a36Sopenharmony_ci spin_unlock_bh(&ap->xmit_lock); 71262306a36Sopenharmony_ci return done; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci/* 71662306a36Sopenharmony_ci * Flush output from our internal buffers. 71762306a36Sopenharmony_ci * Called for the TCFLSH ioctl. Can be entered in parallel 71862306a36Sopenharmony_ci * but this is covered by the xmit_lock. 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_cistatic void 72162306a36Sopenharmony_cippp_async_flush_output(struct asyncppp *ap) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci int done = 0; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci spin_lock_bh(&ap->xmit_lock); 72662306a36Sopenharmony_ci ap->optr = ap->olim; 72762306a36Sopenharmony_ci if (ap->tpkt != NULL) { 72862306a36Sopenharmony_ci kfree_skb(ap->tpkt); 72962306a36Sopenharmony_ci ap->tpkt = NULL; 73062306a36Sopenharmony_ci clear_bit(XMIT_FULL, &ap->xmit_flags); 73162306a36Sopenharmony_ci done = 1; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci spin_unlock_bh(&ap->xmit_lock); 73462306a36Sopenharmony_ci if (done) 73562306a36Sopenharmony_ci ppp_output_wakeup(&ap->chan); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci/* 73962306a36Sopenharmony_ci * Receive-side routines. 74062306a36Sopenharmony_ci */ 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci/* see how many ordinary chars there are at the start of buf */ 74362306a36Sopenharmony_cistatic inline int 74462306a36Sopenharmony_ciscan_ordinary(struct asyncppp *ap, const unsigned char *buf, int count) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci int i, c; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci for (i = 0; i < count; ++i) { 74962306a36Sopenharmony_ci c = buf[i]; 75062306a36Sopenharmony_ci if (c == PPP_ESCAPE || c == PPP_FLAG || 75162306a36Sopenharmony_ci (c < 0x20 && (ap->raccm & (1 << c)) != 0)) 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci return i; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci/* called when a flag is seen - do end-of-packet processing */ 75862306a36Sopenharmony_cistatic void 75962306a36Sopenharmony_ciprocess_input_packet(struct asyncppp *ap) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci struct sk_buff *skb; 76262306a36Sopenharmony_ci unsigned char *p; 76362306a36Sopenharmony_ci unsigned int len, fcs; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci skb = ap->rpkt; 76662306a36Sopenharmony_ci if (ap->state & (SC_TOSS | SC_ESCAPE)) 76762306a36Sopenharmony_ci goto err; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (skb == NULL) 77062306a36Sopenharmony_ci return; /* 0-length packet */ 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* check the FCS */ 77362306a36Sopenharmony_ci p = skb->data; 77462306a36Sopenharmony_ci len = skb->len; 77562306a36Sopenharmony_ci if (len < 3) 77662306a36Sopenharmony_ci goto err; /* too short */ 77762306a36Sopenharmony_ci fcs = PPP_INITFCS; 77862306a36Sopenharmony_ci for (; len > 0; --len) 77962306a36Sopenharmony_ci fcs = PPP_FCS(fcs, *p++); 78062306a36Sopenharmony_ci if (fcs != PPP_GOODFCS) 78162306a36Sopenharmony_ci goto err; /* bad FCS */ 78262306a36Sopenharmony_ci skb_trim(skb, skb->len - 2); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* check for address/control and protocol compression */ 78562306a36Sopenharmony_ci p = skb->data; 78662306a36Sopenharmony_ci if (p[0] == PPP_ALLSTATIONS) { 78762306a36Sopenharmony_ci /* chop off address/control */ 78862306a36Sopenharmony_ci if (p[1] != PPP_UI || skb->len < 3) 78962306a36Sopenharmony_ci goto err; 79062306a36Sopenharmony_ci p = skb_pull(skb, 2); 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* If protocol field is not compressed, it can be LCP packet */ 79462306a36Sopenharmony_ci if (!(p[0] & 0x01)) { 79562306a36Sopenharmony_ci unsigned int proto; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (skb->len < 2) 79862306a36Sopenharmony_ci goto err; 79962306a36Sopenharmony_ci proto = (p[0] << 8) + p[1]; 80062306a36Sopenharmony_ci if (proto == PPP_LCP) 80162306a36Sopenharmony_ci async_lcp_peek(ap, p, skb->len, 1); 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* queue the frame to be processed */ 80562306a36Sopenharmony_ci skb->cb[0] = ap->state; 80662306a36Sopenharmony_ci skb_queue_tail(&ap->rqueue, skb); 80762306a36Sopenharmony_ci ap->rpkt = NULL; 80862306a36Sopenharmony_ci ap->state = 0; 80962306a36Sopenharmony_ci return; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci err: 81262306a36Sopenharmony_ci /* frame had an error, remember that, reset SC_TOSS & SC_ESCAPE */ 81362306a36Sopenharmony_ci ap->state = SC_PREV_ERROR; 81462306a36Sopenharmony_ci if (skb) { 81562306a36Sopenharmony_ci /* make skb appear as freshly allocated */ 81662306a36Sopenharmony_ci skb_trim(skb, 0); 81762306a36Sopenharmony_ci skb_reserve(skb, - skb_headroom(skb)); 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci/* Called when the tty driver has data for us. Runs parallel with the 82262306a36Sopenharmony_ci other ldisc functions but will not be re-entered */ 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic void 82562306a36Sopenharmony_cippp_async_input(struct asyncppp *ap, const u8 *buf, const u8 *flags, int count) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct sk_buff *skb; 82862306a36Sopenharmony_ci int c, i, j, n, s, f; 82962306a36Sopenharmony_ci unsigned char *sp; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* update bits used for 8-bit cleanness detection */ 83262306a36Sopenharmony_ci if (~ap->rbits & SC_RCV_BITS) { 83362306a36Sopenharmony_ci s = 0; 83462306a36Sopenharmony_ci for (i = 0; i < count; ++i) { 83562306a36Sopenharmony_ci c = buf[i]; 83662306a36Sopenharmony_ci if (flags && flags[i] != 0) 83762306a36Sopenharmony_ci continue; 83862306a36Sopenharmony_ci s |= (c & 0x80)? SC_RCV_B7_1: SC_RCV_B7_0; 83962306a36Sopenharmony_ci c = ((c >> 4) ^ c) & 0xf; 84062306a36Sopenharmony_ci s |= (0x6996 & (1 << c))? SC_RCV_ODDP: SC_RCV_EVNP; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci ap->rbits |= s; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci while (count > 0) { 84662306a36Sopenharmony_ci /* scan through and see how many chars we can do in bulk */ 84762306a36Sopenharmony_ci if ((ap->state & SC_ESCAPE) && buf[0] == PPP_ESCAPE) 84862306a36Sopenharmony_ci n = 1; 84962306a36Sopenharmony_ci else 85062306a36Sopenharmony_ci n = scan_ordinary(ap, buf, count); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci f = 0; 85362306a36Sopenharmony_ci if (flags && (ap->state & SC_TOSS) == 0) { 85462306a36Sopenharmony_ci /* check the flags to see if any char had an error */ 85562306a36Sopenharmony_ci for (j = 0; j < n; ++j) 85662306a36Sopenharmony_ci if ((f = flags[j]) != 0) 85762306a36Sopenharmony_ci break; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci if (f != 0) { 86062306a36Sopenharmony_ci /* start tossing */ 86162306a36Sopenharmony_ci ap->state |= SC_TOSS; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci } else if (n > 0 && (ap->state & SC_TOSS) == 0) { 86462306a36Sopenharmony_ci /* stuff the chars in the skb */ 86562306a36Sopenharmony_ci skb = ap->rpkt; 86662306a36Sopenharmony_ci if (!skb) { 86762306a36Sopenharmony_ci skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2); 86862306a36Sopenharmony_ci if (!skb) 86962306a36Sopenharmony_ci goto nomem; 87062306a36Sopenharmony_ci ap->rpkt = skb; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci if (skb->len == 0) { 87362306a36Sopenharmony_ci /* Try to get the payload 4-byte aligned. 87462306a36Sopenharmony_ci * This should match the 87562306a36Sopenharmony_ci * PPP_ALLSTATIONS/PPP_UI/compressed tests in 87662306a36Sopenharmony_ci * process_input_packet, but we do not have 87762306a36Sopenharmony_ci * enough chars here to test buf[1] and buf[2]. 87862306a36Sopenharmony_ci */ 87962306a36Sopenharmony_ci if (buf[0] != PPP_ALLSTATIONS) 88062306a36Sopenharmony_ci skb_reserve(skb, 2 + (buf[0] & 1)); 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci if (n > skb_tailroom(skb)) { 88362306a36Sopenharmony_ci /* packet overflowed MRU */ 88462306a36Sopenharmony_ci ap->state |= SC_TOSS; 88562306a36Sopenharmony_ci } else { 88662306a36Sopenharmony_ci sp = skb_put_data(skb, buf, n); 88762306a36Sopenharmony_ci if (ap->state & SC_ESCAPE) { 88862306a36Sopenharmony_ci sp[0] ^= PPP_TRANS; 88962306a36Sopenharmony_ci ap->state &= ~SC_ESCAPE; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (n >= count) 89562306a36Sopenharmony_ci break; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci c = buf[n]; 89862306a36Sopenharmony_ci if (flags != NULL && flags[n] != 0) { 89962306a36Sopenharmony_ci ap->state |= SC_TOSS; 90062306a36Sopenharmony_ci } else if (c == PPP_FLAG) { 90162306a36Sopenharmony_ci process_input_packet(ap); 90262306a36Sopenharmony_ci } else if (c == PPP_ESCAPE) { 90362306a36Sopenharmony_ci ap->state |= SC_ESCAPE; 90462306a36Sopenharmony_ci } else if (I_IXON(ap->tty)) { 90562306a36Sopenharmony_ci if (c == START_CHAR(ap->tty)) 90662306a36Sopenharmony_ci start_tty(ap->tty); 90762306a36Sopenharmony_ci else if (c == STOP_CHAR(ap->tty)) 90862306a36Sopenharmony_ci stop_tty(ap->tty); 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci /* otherwise it's a char in the recv ACCM */ 91162306a36Sopenharmony_ci ++n; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci buf += n; 91462306a36Sopenharmony_ci if (flags) 91562306a36Sopenharmony_ci flags += n; 91662306a36Sopenharmony_ci count -= n; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci return; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci nomem: 92162306a36Sopenharmony_ci printk(KERN_ERR "PPPasync: no memory (input pkt)\n"); 92262306a36Sopenharmony_ci ap->state |= SC_TOSS; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci/* 92662306a36Sopenharmony_ci * We look at LCP frames going past so that we can notice 92762306a36Sopenharmony_ci * and react to the LCP configure-ack from the peer. 92862306a36Sopenharmony_ci * In the situation where the peer has been sent a configure-ack 92962306a36Sopenharmony_ci * already, LCP is up once it has sent its configure-ack 93062306a36Sopenharmony_ci * so the immediately following packet can be sent with the 93162306a36Sopenharmony_ci * configured LCP options. This allows us to process the following 93262306a36Sopenharmony_ci * packet correctly without pppd needing to respond quickly. 93362306a36Sopenharmony_ci * 93462306a36Sopenharmony_ci * We only respond to the received configure-ack if we have just 93562306a36Sopenharmony_ci * sent a configure-request, and the configure-ack contains the 93662306a36Sopenharmony_ci * same data (this is checked using a 16-bit crc of the data). 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_ci#define CONFREQ 1 /* LCP code field values */ 93962306a36Sopenharmony_ci#define CONFACK 2 94062306a36Sopenharmony_ci#define LCP_MRU 1 /* LCP option numbers */ 94162306a36Sopenharmony_ci#define LCP_ASYNCMAP 2 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic void async_lcp_peek(struct asyncppp *ap, unsigned char *data, 94462306a36Sopenharmony_ci int len, int inbound) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci int dlen, fcs, i, code; 94762306a36Sopenharmony_ci u32 val; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci data += 2; /* skip protocol bytes */ 95062306a36Sopenharmony_ci len -= 2; 95162306a36Sopenharmony_ci if (len < 4) /* 4 = code, ID, length */ 95262306a36Sopenharmony_ci return; 95362306a36Sopenharmony_ci code = data[0]; 95462306a36Sopenharmony_ci if (code != CONFACK && code != CONFREQ) 95562306a36Sopenharmony_ci return; 95662306a36Sopenharmony_ci dlen = get_unaligned_be16(data + 2); 95762306a36Sopenharmony_ci if (len < dlen) 95862306a36Sopenharmony_ci return; /* packet got truncated or length is bogus */ 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (code == (inbound? CONFACK: CONFREQ)) { 96162306a36Sopenharmony_ci /* 96262306a36Sopenharmony_ci * sent confreq or received confack: 96362306a36Sopenharmony_ci * calculate the crc of the data from the ID field on. 96462306a36Sopenharmony_ci */ 96562306a36Sopenharmony_ci fcs = PPP_INITFCS; 96662306a36Sopenharmony_ci for (i = 1; i < dlen; ++i) 96762306a36Sopenharmony_ci fcs = PPP_FCS(fcs, data[i]); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (!inbound) { 97062306a36Sopenharmony_ci /* outbound confreq - remember the crc for later */ 97162306a36Sopenharmony_ci ap->lcp_fcs = fcs; 97262306a36Sopenharmony_ci return; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci /* received confack, check the crc */ 97662306a36Sopenharmony_ci fcs ^= ap->lcp_fcs; 97762306a36Sopenharmony_ci ap->lcp_fcs = -1; 97862306a36Sopenharmony_ci if (fcs != 0) 97962306a36Sopenharmony_ci return; 98062306a36Sopenharmony_ci } else if (inbound) 98162306a36Sopenharmony_ci return; /* not interested in received confreq */ 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* process the options in the confack */ 98462306a36Sopenharmony_ci data += 4; 98562306a36Sopenharmony_ci dlen -= 4; 98662306a36Sopenharmony_ci /* data[0] is code, data[1] is length */ 98762306a36Sopenharmony_ci while (dlen >= 2 && dlen >= data[1] && data[1] >= 2) { 98862306a36Sopenharmony_ci switch (data[0]) { 98962306a36Sopenharmony_ci case LCP_MRU: 99062306a36Sopenharmony_ci val = get_unaligned_be16(data + 2); 99162306a36Sopenharmony_ci if (inbound) 99262306a36Sopenharmony_ci ap->mru = val; 99362306a36Sopenharmony_ci else 99462306a36Sopenharmony_ci ap->chan.mtu = val; 99562306a36Sopenharmony_ci break; 99662306a36Sopenharmony_ci case LCP_ASYNCMAP: 99762306a36Sopenharmony_ci val = get_unaligned_be32(data + 2); 99862306a36Sopenharmony_ci if (inbound) 99962306a36Sopenharmony_ci ap->raccm = val; 100062306a36Sopenharmony_ci else 100162306a36Sopenharmony_ci ap->xaccm[0] = val; 100262306a36Sopenharmony_ci break; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci dlen -= data[1]; 100562306a36Sopenharmony_ci data += data[1]; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cistatic void __exit ppp_async_cleanup(void) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci tty_unregister_ldisc(&ppp_ldisc); 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cimodule_init(ppp_async_init); 101562306a36Sopenharmony_cimodule_exit(ppp_async_cleanup); 1016