18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PPP synchronous tty channel driver for Linux. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This is a ppp channel driver that can be used with tty device drivers 68c2ecf20Sopenharmony_ci * that are frame oriented, such as synchronous HDLC devices. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Complete PPP frames without encoding/decoding are exchanged between 98c2ecf20Sopenharmony_ci * the channel driver and the device driver. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The async map IOCTL codes are implemented to keep the user mode 128c2ecf20Sopenharmony_ci * applications happy if they call them. Synchronous PPP does not use 138c2ecf20Sopenharmony_ci * the async maps. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Copyright 1999 Paul Mackerras. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Also touched by the grubby hands of Paul Fulghum paulkf@microgate.com 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * This driver provides the encapsulation and framing for sending 208c2ecf20Sopenharmony_ci * and receiving PPP frames over sync serial lines. It relies on 218c2ecf20Sopenharmony_ci * the generic PPP layer to give it frames to send and to process 228c2ecf20Sopenharmony_ci * received frames. It implements the PPP line discipline. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Part of the code in this driver was inspired by the old async-only 258c2ecf20Sopenharmony_ci * PPP driver, written by Michael Callahan and Al Longyear, and 268c2ecf20Sopenharmony_ci * subsequently hacked by Paul Mackerras. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * ==FILEVERSION 20040616== 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <linux/module.h> 328c2ecf20Sopenharmony_ci#include <linux/kernel.h> 338c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 348c2ecf20Sopenharmony_ci#include <linux/tty.h> 358c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 368c2ecf20Sopenharmony_ci#include <linux/poll.h> 378c2ecf20Sopenharmony_ci#include <linux/ppp_defs.h> 388c2ecf20Sopenharmony_ci#include <linux/ppp-ioctl.h> 398c2ecf20Sopenharmony_ci#include <linux/ppp_channel.h> 408c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 418c2ecf20Sopenharmony_ci#include <linux/completion.h> 428c2ecf20Sopenharmony_ci#include <linux/init.h> 438c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 448c2ecf20Sopenharmony_ci#include <linux/slab.h> 458c2ecf20Sopenharmony_ci#include <linux/refcount.h> 468c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 478c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define PPP_VERSION "2.4.2" 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Structure for storing local state. */ 528c2ecf20Sopenharmony_cistruct syncppp { 538c2ecf20Sopenharmony_ci struct tty_struct *tty; 548c2ecf20Sopenharmony_ci unsigned int flags; 558c2ecf20Sopenharmony_ci unsigned int rbits; 568c2ecf20Sopenharmony_ci int mru; 578c2ecf20Sopenharmony_ci spinlock_t xmit_lock; 588c2ecf20Sopenharmony_ci spinlock_t recv_lock; 598c2ecf20Sopenharmony_ci unsigned long xmit_flags; 608c2ecf20Sopenharmony_ci u32 xaccm[8]; 618c2ecf20Sopenharmony_ci u32 raccm; 628c2ecf20Sopenharmony_ci unsigned int bytes_sent; 638c2ecf20Sopenharmony_ci unsigned int bytes_rcvd; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci struct sk_buff *tpkt; 668c2ecf20Sopenharmony_ci unsigned long last_xmit; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci struct sk_buff_head rqueue; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci struct tasklet_struct tsk; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci refcount_t refcnt; 738c2ecf20Sopenharmony_ci struct completion dead_cmp; 748c2ecf20Sopenharmony_ci struct ppp_channel chan; /* interface to generic ppp layer */ 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* Bit numbers in xmit_flags */ 788c2ecf20Sopenharmony_ci#define XMIT_WAKEUP 0 798c2ecf20Sopenharmony_ci#define XMIT_FULL 1 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* Bits in rbits */ 828c2ecf20Sopenharmony_ci#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define PPPSYNC_MAX_RQLEN 32 /* arbitrary */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* 878c2ecf20Sopenharmony_ci * Prototypes. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic struct sk_buff* ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *); 908c2ecf20Sopenharmony_cistatic int ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb); 918c2ecf20Sopenharmony_cistatic int ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd, 928c2ecf20Sopenharmony_ci unsigned long arg); 938c2ecf20Sopenharmony_cistatic void ppp_sync_process(unsigned long arg); 948c2ecf20Sopenharmony_cistatic int ppp_sync_push(struct syncppp *ap); 958c2ecf20Sopenharmony_cistatic void ppp_sync_flush_output(struct syncppp *ap); 968c2ecf20Sopenharmony_cistatic void ppp_sync_input(struct syncppp *ap, const unsigned char *buf, 978c2ecf20Sopenharmony_ci char *flags, int count); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const struct ppp_channel_ops sync_ops = { 1008c2ecf20Sopenharmony_ci .start_xmit = ppp_sync_send, 1018c2ecf20Sopenharmony_ci .ioctl = ppp_sync_ioctl, 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * Utility procedure to print a buffer in hex/ascii 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_cistatic void 1088c2ecf20Sopenharmony_cippp_print_buffer (const char *name, const __u8 *buf, int count) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci if (name != NULL) 1118c2ecf20Sopenharmony_ci printk(KERN_DEBUG "ppp_synctty: %s, count = %d\n", name, count); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci print_hex_dump_bytes("", DUMP_PREFIX_NONE, buf, count); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* 1188c2ecf20Sopenharmony_ci * Routines implementing the synchronous PPP line discipline. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * We have a potential race on dereferencing tty->disc_data, 1238c2ecf20Sopenharmony_ci * because the tty layer provides no locking at all - thus one 1248c2ecf20Sopenharmony_ci * cpu could be running ppp_synctty_receive while another 1258c2ecf20Sopenharmony_ci * calls ppp_synctty_close, which zeroes tty->disc_data and 1268c2ecf20Sopenharmony_ci * frees the memory that ppp_synctty_receive is using. The best 1278c2ecf20Sopenharmony_ci * way to fix this is to use a rwlock in the tty struct, but for now 1288c2ecf20Sopenharmony_ci * we use a single global rwlock for all ttys in ppp line discipline. 1298c2ecf20Sopenharmony_ci * 1308c2ecf20Sopenharmony_ci * FIXME: Fixed in tty_io nowadays. 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(disc_data_lock); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic struct syncppp *sp_get(struct tty_struct *tty) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct syncppp *ap; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci read_lock(&disc_data_lock); 1398c2ecf20Sopenharmony_ci ap = tty->disc_data; 1408c2ecf20Sopenharmony_ci if (ap != NULL) 1418c2ecf20Sopenharmony_ci refcount_inc(&ap->refcnt); 1428c2ecf20Sopenharmony_ci read_unlock(&disc_data_lock); 1438c2ecf20Sopenharmony_ci return ap; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void sp_put(struct syncppp *ap) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&ap->refcnt)) 1498c2ecf20Sopenharmony_ci complete(&ap->dead_cmp); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* 1538c2ecf20Sopenharmony_ci * Called when a tty is put into sync-PPP line discipline. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_cistatic int 1568c2ecf20Sopenharmony_cippp_sync_open(struct tty_struct *tty) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct syncppp *ap; 1598c2ecf20Sopenharmony_ci int err; 1608c2ecf20Sopenharmony_ci int speed; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (tty->ops->write == NULL) 1638c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ap = kzalloc(sizeof(*ap), GFP_KERNEL); 1668c2ecf20Sopenharmony_ci err = -ENOMEM; 1678c2ecf20Sopenharmony_ci if (!ap) 1688c2ecf20Sopenharmony_ci goto out; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* initialize the syncppp structure */ 1718c2ecf20Sopenharmony_ci ap->tty = tty; 1728c2ecf20Sopenharmony_ci ap->mru = PPP_MRU; 1738c2ecf20Sopenharmony_ci spin_lock_init(&ap->xmit_lock); 1748c2ecf20Sopenharmony_ci spin_lock_init(&ap->recv_lock); 1758c2ecf20Sopenharmony_ci ap->xaccm[0] = ~0U; 1768c2ecf20Sopenharmony_ci ap->xaccm[3] = 0x60000000U; 1778c2ecf20Sopenharmony_ci ap->raccm = ~0U; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci skb_queue_head_init(&ap->rqueue); 1808c2ecf20Sopenharmony_ci tasklet_init(&ap->tsk, ppp_sync_process, (unsigned long) ap); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci refcount_set(&ap->refcnt, 1); 1838c2ecf20Sopenharmony_ci init_completion(&ap->dead_cmp); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ap->chan.private = ap; 1868c2ecf20Sopenharmony_ci ap->chan.ops = &sync_ops; 1878c2ecf20Sopenharmony_ci ap->chan.mtu = PPP_MRU; 1888c2ecf20Sopenharmony_ci ap->chan.hdrlen = 2; /* for A/C bytes */ 1898c2ecf20Sopenharmony_ci speed = tty_get_baud_rate(tty); 1908c2ecf20Sopenharmony_ci ap->chan.speed = speed; 1918c2ecf20Sopenharmony_ci err = ppp_register_channel(&ap->chan); 1928c2ecf20Sopenharmony_ci if (err) 1938c2ecf20Sopenharmony_ci goto out_free; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci tty->disc_data = ap; 1968c2ecf20Sopenharmony_ci tty->receive_room = 65536; 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci out_free: 2008c2ecf20Sopenharmony_ci kfree(ap); 2018c2ecf20Sopenharmony_ci out: 2028c2ecf20Sopenharmony_ci return err; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* 2068c2ecf20Sopenharmony_ci * Called when the tty is put into another line discipline 2078c2ecf20Sopenharmony_ci * or it hangs up. We have to wait for any cpu currently 2088c2ecf20Sopenharmony_ci * executing in any of the other ppp_synctty_* routines to 2098c2ecf20Sopenharmony_ci * finish before we can call ppp_unregister_channel and free 2108c2ecf20Sopenharmony_ci * the syncppp struct. This routine must be called from 2118c2ecf20Sopenharmony_ci * process context, not interrupt or softirq context. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_cistatic void 2148c2ecf20Sopenharmony_cippp_sync_close(struct tty_struct *tty) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct syncppp *ap; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci write_lock_irq(&disc_data_lock); 2198c2ecf20Sopenharmony_ci ap = tty->disc_data; 2208c2ecf20Sopenharmony_ci tty->disc_data = NULL; 2218c2ecf20Sopenharmony_ci write_unlock_irq(&disc_data_lock); 2228c2ecf20Sopenharmony_ci if (!ap) 2238c2ecf20Sopenharmony_ci return; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* 2268c2ecf20Sopenharmony_ci * We have now ensured that nobody can start using ap from now 2278c2ecf20Sopenharmony_ci * on, but we have to wait for all existing users to finish. 2288c2ecf20Sopenharmony_ci * Note that ppp_unregister_channel ensures that no calls to 2298c2ecf20Sopenharmony_ci * our channel ops (i.e. ppp_sync_send/ioctl) are in progress 2308c2ecf20Sopenharmony_ci * by the time it returns. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ci if (!refcount_dec_and_test(&ap->refcnt)) 2338c2ecf20Sopenharmony_ci wait_for_completion(&ap->dead_cmp); 2348c2ecf20Sopenharmony_ci tasklet_kill(&ap->tsk); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci ppp_unregister_channel(&ap->chan); 2378c2ecf20Sopenharmony_ci skb_queue_purge(&ap->rqueue); 2388c2ecf20Sopenharmony_ci kfree_skb(ap->tpkt); 2398c2ecf20Sopenharmony_ci kfree(ap); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* 2438c2ecf20Sopenharmony_ci * Called on tty hangup in process context. 2448c2ecf20Sopenharmony_ci * 2458c2ecf20Sopenharmony_ci * Wait for I/O to driver to complete and unregister PPP channel. 2468c2ecf20Sopenharmony_ci * This is already done by the close routine, so just call that. 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_cistatic int ppp_sync_hangup(struct tty_struct *tty) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci ppp_sync_close(tty); 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* 2558c2ecf20Sopenharmony_ci * Read does nothing - no data is ever available this way. 2568c2ecf20Sopenharmony_ci * Pppd reads and writes packets via /dev/ppp instead. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_cistatic ssize_t 2598c2ecf20Sopenharmony_cippp_sync_read(struct tty_struct *tty, struct file *file, 2608c2ecf20Sopenharmony_ci unsigned char *buf, size_t count, 2618c2ecf20Sopenharmony_ci void **cookie, unsigned long offset) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci return -EAGAIN; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* 2678c2ecf20Sopenharmony_ci * Write on the tty does nothing, the packets all come in 2688c2ecf20Sopenharmony_ci * from the ppp generic stuff. 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_cistatic ssize_t 2718c2ecf20Sopenharmony_cippp_sync_write(struct tty_struct *tty, struct file *file, 2728c2ecf20Sopenharmony_ci const unsigned char *buf, size_t count) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci return -EAGAIN; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int 2788c2ecf20Sopenharmony_cippp_synctty_ioctl(struct tty_struct *tty, struct file *file, 2798c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct syncppp *ap = sp_get(tty); 2828c2ecf20Sopenharmony_ci int __user *p = (int __user *)arg; 2838c2ecf20Sopenharmony_ci int err, val; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (!ap) 2868c2ecf20Sopenharmony_ci return -ENXIO; 2878c2ecf20Sopenharmony_ci err = -EFAULT; 2888c2ecf20Sopenharmony_ci switch (cmd) { 2898c2ecf20Sopenharmony_ci case PPPIOCGCHAN: 2908c2ecf20Sopenharmony_ci err = -EFAULT; 2918c2ecf20Sopenharmony_ci if (put_user(ppp_channel_index(&ap->chan), p)) 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci err = 0; 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci case PPPIOCGUNIT: 2978c2ecf20Sopenharmony_ci err = -EFAULT; 2988c2ecf20Sopenharmony_ci if (put_user(ppp_unit_number(&ap->chan), p)) 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci err = 0; 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci case TCFLSH: 3048c2ecf20Sopenharmony_ci /* flush our buffers and the serial port's buffer */ 3058c2ecf20Sopenharmony_ci if (arg == TCIOFLUSH || arg == TCOFLUSH) 3068c2ecf20Sopenharmony_ci ppp_sync_flush_output(ap); 3078c2ecf20Sopenharmony_ci err = n_tty_ioctl_helper(tty, file, cmd, arg); 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci case FIONREAD: 3118c2ecf20Sopenharmony_ci val = 0; 3128c2ecf20Sopenharmony_ci if (put_user(val, p)) 3138c2ecf20Sopenharmony_ci break; 3148c2ecf20Sopenharmony_ci err = 0; 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci default: 3188c2ecf20Sopenharmony_ci err = tty_mode_ioctl(tty, file, cmd, arg); 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci sp_put(ap); 3238c2ecf20Sopenharmony_ci return err; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/* No kernel lock - fine */ 3278c2ecf20Sopenharmony_cistatic __poll_t 3288c2ecf20Sopenharmony_cippp_sync_poll(struct tty_struct *tty, struct file *file, poll_table *wait) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* May sleep, don't call from interrupt level or with interrupts disabled */ 3348c2ecf20Sopenharmony_cistatic void 3358c2ecf20Sopenharmony_cippp_sync_receive(struct tty_struct *tty, const unsigned char *buf, 3368c2ecf20Sopenharmony_ci char *cflags, int count) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct syncppp *ap = sp_get(tty); 3398c2ecf20Sopenharmony_ci unsigned long flags; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (!ap) 3428c2ecf20Sopenharmony_ci return; 3438c2ecf20Sopenharmony_ci spin_lock_irqsave(&ap->recv_lock, flags); 3448c2ecf20Sopenharmony_ci ppp_sync_input(ap, buf, cflags, count); 3458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ap->recv_lock, flags); 3468c2ecf20Sopenharmony_ci if (!skb_queue_empty(&ap->rqueue)) 3478c2ecf20Sopenharmony_ci tasklet_schedule(&ap->tsk); 3488c2ecf20Sopenharmony_ci sp_put(ap); 3498c2ecf20Sopenharmony_ci tty_unthrottle(tty); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic void 3538c2ecf20Sopenharmony_cippp_sync_wakeup(struct tty_struct *tty) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct syncppp *ap = sp_get(tty); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 3588c2ecf20Sopenharmony_ci if (!ap) 3598c2ecf20Sopenharmony_ci return; 3608c2ecf20Sopenharmony_ci set_bit(XMIT_WAKEUP, &ap->xmit_flags); 3618c2ecf20Sopenharmony_ci tasklet_schedule(&ap->tsk); 3628c2ecf20Sopenharmony_ci sp_put(ap); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic struct tty_ldisc_ops ppp_sync_ldisc = { 3678c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3688c2ecf20Sopenharmony_ci .magic = TTY_LDISC_MAGIC, 3698c2ecf20Sopenharmony_ci .name = "pppsync", 3708c2ecf20Sopenharmony_ci .open = ppp_sync_open, 3718c2ecf20Sopenharmony_ci .close = ppp_sync_close, 3728c2ecf20Sopenharmony_ci .hangup = ppp_sync_hangup, 3738c2ecf20Sopenharmony_ci .read = ppp_sync_read, 3748c2ecf20Sopenharmony_ci .write = ppp_sync_write, 3758c2ecf20Sopenharmony_ci .ioctl = ppp_synctty_ioctl, 3768c2ecf20Sopenharmony_ci .poll = ppp_sync_poll, 3778c2ecf20Sopenharmony_ci .receive_buf = ppp_sync_receive, 3788c2ecf20Sopenharmony_ci .write_wakeup = ppp_sync_wakeup, 3798c2ecf20Sopenharmony_ci}; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int __init 3828c2ecf20Sopenharmony_cippp_sync_init(void) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci int err; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci err = tty_register_ldisc(N_SYNC_PPP, &ppp_sync_ldisc); 3878c2ecf20Sopenharmony_ci if (err != 0) 3888c2ecf20Sopenharmony_ci printk(KERN_ERR "PPP_sync: error %d registering line disc.\n", 3898c2ecf20Sopenharmony_ci err); 3908c2ecf20Sopenharmony_ci return err; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* 3948c2ecf20Sopenharmony_ci * The following routines provide the PPP channel interface. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_cistatic int 3978c2ecf20Sopenharmony_cippp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct syncppp *ap = chan->private; 4008c2ecf20Sopenharmony_ci int err, val; 4018c2ecf20Sopenharmony_ci u32 accm[8]; 4028c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 4038c2ecf20Sopenharmony_ci u32 __user *p = argp; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci err = -EFAULT; 4068c2ecf20Sopenharmony_ci switch (cmd) { 4078c2ecf20Sopenharmony_ci case PPPIOCGFLAGS: 4088c2ecf20Sopenharmony_ci val = ap->flags | ap->rbits; 4098c2ecf20Sopenharmony_ci if (put_user(val, (int __user *) argp)) 4108c2ecf20Sopenharmony_ci break; 4118c2ecf20Sopenharmony_ci err = 0; 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci case PPPIOCSFLAGS: 4148c2ecf20Sopenharmony_ci if (get_user(val, (int __user *) argp)) 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci ap->flags = val & ~SC_RCV_BITS; 4178c2ecf20Sopenharmony_ci spin_lock_irq(&ap->recv_lock); 4188c2ecf20Sopenharmony_ci ap->rbits = val & SC_RCV_BITS; 4198c2ecf20Sopenharmony_ci spin_unlock_irq(&ap->recv_lock); 4208c2ecf20Sopenharmony_ci err = 0; 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci case PPPIOCGASYNCMAP: 4248c2ecf20Sopenharmony_ci if (put_user(ap->xaccm[0], p)) 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci err = 0; 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci case PPPIOCSASYNCMAP: 4298c2ecf20Sopenharmony_ci if (get_user(ap->xaccm[0], p)) 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci err = 0; 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci case PPPIOCGRASYNCMAP: 4358c2ecf20Sopenharmony_ci if (put_user(ap->raccm, p)) 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci err = 0; 4388c2ecf20Sopenharmony_ci break; 4398c2ecf20Sopenharmony_ci case PPPIOCSRASYNCMAP: 4408c2ecf20Sopenharmony_ci if (get_user(ap->raccm, p)) 4418c2ecf20Sopenharmony_ci break; 4428c2ecf20Sopenharmony_ci err = 0; 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci case PPPIOCGXASYNCMAP: 4468c2ecf20Sopenharmony_ci if (copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm))) 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci err = 0; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case PPPIOCSXASYNCMAP: 4518c2ecf20Sopenharmony_ci if (copy_from_user(accm, argp, sizeof(accm))) 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci accm[2] &= ~0x40000000U; /* can't escape 0x5e */ 4548c2ecf20Sopenharmony_ci accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ 4558c2ecf20Sopenharmony_ci memcpy(ap->xaccm, accm, sizeof(ap->xaccm)); 4568c2ecf20Sopenharmony_ci err = 0; 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci case PPPIOCGMRU: 4608c2ecf20Sopenharmony_ci if (put_user(ap->mru, (int __user *) argp)) 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci err = 0; 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci case PPPIOCSMRU: 4658c2ecf20Sopenharmony_ci if (get_user(val, (int __user *) argp)) 4668c2ecf20Sopenharmony_ci break; 4678c2ecf20Sopenharmony_ci if (val > U16_MAX) { 4688c2ecf20Sopenharmony_ci err = -EINVAL; 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci if (val < PPP_MRU) 4728c2ecf20Sopenharmony_ci val = PPP_MRU; 4738c2ecf20Sopenharmony_ci ap->mru = val; 4748c2ecf20Sopenharmony_ci err = 0; 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci default: 4788c2ecf20Sopenharmony_ci err = -ENOTTY; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci return err; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci/* 4848c2ecf20Sopenharmony_ci * This is called at softirq level to deliver received packets 4858c2ecf20Sopenharmony_ci * to the ppp_generic code, and to tell the ppp_generic code 4868c2ecf20Sopenharmony_ci * if we can accept more output now. 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_cistatic void ppp_sync_process(unsigned long arg) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct syncppp *ap = (struct syncppp *) arg; 4918c2ecf20Sopenharmony_ci struct sk_buff *skb; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* process received packets */ 4948c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&ap->rqueue)) != NULL) { 4958c2ecf20Sopenharmony_ci if (skb->len == 0) { 4968c2ecf20Sopenharmony_ci /* zero length buffers indicate error */ 4978c2ecf20Sopenharmony_ci ppp_input_error(&ap->chan, 0); 4988c2ecf20Sopenharmony_ci kfree_skb(skb); 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci else 5018c2ecf20Sopenharmony_ci ppp_input(&ap->chan, skb); 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* try to push more stuff out */ 5058c2ecf20Sopenharmony_ci if (test_bit(XMIT_WAKEUP, &ap->xmit_flags) && ppp_sync_push(ap)) 5068c2ecf20Sopenharmony_ci ppp_output_wakeup(&ap->chan); 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci/* 5108c2ecf20Sopenharmony_ci * Procedures for encapsulation and framing. 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic struct sk_buff* 5148c2ecf20Sopenharmony_cippp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci int proto; 5178c2ecf20Sopenharmony_ci unsigned char *data; 5188c2ecf20Sopenharmony_ci int islcp; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci data = skb->data; 5218c2ecf20Sopenharmony_ci proto = get_unaligned_be16(data); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* LCP packets with codes between 1 (configure-request) 5248c2ecf20Sopenharmony_ci * and 7 (code-reject) must be sent as though no options 5258c2ecf20Sopenharmony_ci * have been negotiated. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* compress protocol field if option enabled */ 5308c2ecf20Sopenharmony_ci if (data[0] == 0 && (ap->flags & SC_COMP_PROT) && !islcp) 5318c2ecf20Sopenharmony_ci skb_pull(skb,1); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* prepend address/control fields if necessary */ 5348c2ecf20Sopenharmony_ci if ((ap->flags & SC_COMP_AC) == 0 || islcp) { 5358c2ecf20Sopenharmony_ci if (skb_headroom(skb) < 2) { 5368c2ecf20Sopenharmony_ci struct sk_buff *npkt = dev_alloc_skb(skb->len + 2); 5378c2ecf20Sopenharmony_ci if (npkt == NULL) { 5388c2ecf20Sopenharmony_ci kfree_skb(skb); 5398c2ecf20Sopenharmony_ci return NULL; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci skb_reserve(npkt,2); 5428c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, 5438c2ecf20Sopenharmony_ci skb_put(npkt, skb->len), skb->len); 5448c2ecf20Sopenharmony_ci consume_skb(skb); 5458c2ecf20Sopenharmony_ci skb = npkt; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci skb_push(skb,2); 5488c2ecf20Sopenharmony_ci skb->data[0] = PPP_ALLSTATIONS; 5498c2ecf20Sopenharmony_ci skb->data[1] = PPP_UI; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci ap->last_xmit = jiffies; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (skb && ap->flags & SC_LOG_OUTPKT) 5558c2ecf20Sopenharmony_ci ppp_print_buffer ("send buffer", skb->data, skb->len); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci return skb; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci/* 5618c2ecf20Sopenharmony_ci * Transmit-side routines. 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/* 5658c2ecf20Sopenharmony_ci * Send a packet to the peer over an sync tty line. 5668c2ecf20Sopenharmony_ci * Returns 1 iff the packet was accepted. 5678c2ecf20Sopenharmony_ci * If the packet was not accepted, we will call ppp_output_wakeup 5688c2ecf20Sopenharmony_ci * at some later time. 5698c2ecf20Sopenharmony_ci */ 5708c2ecf20Sopenharmony_cistatic int 5718c2ecf20Sopenharmony_cippp_sync_send(struct ppp_channel *chan, struct sk_buff *skb) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct syncppp *ap = chan->private; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci ppp_sync_push(ap); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags)) 5788c2ecf20Sopenharmony_ci return 0; /* already full */ 5798c2ecf20Sopenharmony_ci skb = ppp_sync_txmunge(ap, skb); 5808c2ecf20Sopenharmony_ci if (skb != NULL) 5818c2ecf20Sopenharmony_ci ap->tpkt = skb; 5828c2ecf20Sopenharmony_ci else 5838c2ecf20Sopenharmony_ci clear_bit(XMIT_FULL, &ap->xmit_flags); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci ppp_sync_push(ap); 5868c2ecf20Sopenharmony_ci return 1; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci/* 5908c2ecf20Sopenharmony_ci * Push as much data as possible out to the tty. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_cistatic int 5938c2ecf20Sopenharmony_cippp_sync_push(struct syncppp *ap) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci int sent, done = 0; 5968c2ecf20Sopenharmony_ci struct tty_struct *tty = ap->tty; 5978c2ecf20Sopenharmony_ci int tty_stuffed = 0; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (!spin_trylock_bh(&ap->xmit_lock)) 6008c2ecf20Sopenharmony_ci return 0; 6018c2ecf20Sopenharmony_ci for (;;) { 6028c2ecf20Sopenharmony_ci if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags)) 6038c2ecf20Sopenharmony_ci tty_stuffed = 0; 6048c2ecf20Sopenharmony_ci if (!tty_stuffed && ap->tpkt) { 6058c2ecf20Sopenharmony_ci set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 6068c2ecf20Sopenharmony_ci sent = tty->ops->write(tty, ap->tpkt->data, ap->tpkt->len); 6078c2ecf20Sopenharmony_ci if (sent < 0) 6088c2ecf20Sopenharmony_ci goto flush; /* error, e.g. loss of CD */ 6098c2ecf20Sopenharmony_ci if (sent < ap->tpkt->len) { 6108c2ecf20Sopenharmony_ci tty_stuffed = 1; 6118c2ecf20Sopenharmony_ci } else { 6128c2ecf20Sopenharmony_ci consume_skb(ap->tpkt); 6138c2ecf20Sopenharmony_ci ap->tpkt = NULL; 6148c2ecf20Sopenharmony_ci clear_bit(XMIT_FULL, &ap->xmit_flags); 6158c2ecf20Sopenharmony_ci done = 1; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci continue; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci /* haven't made any progress */ 6208c2ecf20Sopenharmony_ci spin_unlock_bh(&ap->xmit_lock); 6218c2ecf20Sopenharmony_ci if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) || 6228c2ecf20Sopenharmony_ci (!tty_stuffed && ap->tpkt))) 6238c2ecf20Sopenharmony_ci break; 6248c2ecf20Sopenharmony_ci if (!spin_trylock_bh(&ap->xmit_lock)) 6258c2ecf20Sopenharmony_ci break; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci return done; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ciflush: 6308c2ecf20Sopenharmony_ci if (ap->tpkt) { 6318c2ecf20Sopenharmony_ci kfree_skb(ap->tpkt); 6328c2ecf20Sopenharmony_ci ap->tpkt = NULL; 6338c2ecf20Sopenharmony_ci clear_bit(XMIT_FULL, &ap->xmit_flags); 6348c2ecf20Sopenharmony_ci done = 1; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci spin_unlock_bh(&ap->xmit_lock); 6378c2ecf20Sopenharmony_ci return done; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci/* 6418c2ecf20Sopenharmony_ci * Flush output from our internal buffers. 6428c2ecf20Sopenharmony_ci * Called for the TCFLSH ioctl. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_cistatic void 6458c2ecf20Sopenharmony_cippp_sync_flush_output(struct syncppp *ap) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci int done = 0; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci spin_lock_bh(&ap->xmit_lock); 6508c2ecf20Sopenharmony_ci if (ap->tpkt != NULL) { 6518c2ecf20Sopenharmony_ci kfree_skb(ap->tpkt); 6528c2ecf20Sopenharmony_ci ap->tpkt = NULL; 6538c2ecf20Sopenharmony_ci clear_bit(XMIT_FULL, &ap->xmit_flags); 6548c2ecf20Sopenharmony_ci done = 1; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci spin_unlock_bh(&ap->xmit_lock); 6578c2ecf20Sopenharmony_ci if (done) 6588c2ecf20Sopenharmony_ci ppp_output_wakeup(&ap->chan); 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci/* 6628c2ecf20Sopenharmony_ci * Receive-side routines. 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci/* called when the tty driver has data for us. 6668c2ecf20Sopenharmony_ci * 6678c2ecf20Sopenharmony_ci * Data is frame oriented: each call to ppp_sync_input is considered 6688c2ecf20Sopenharmony_ci * a whole frame. If the 1st flag byte is non-zero then the whole 6698c2ecf20Sopenharmony_ci * frame is considered to be in error and is tossed. 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_cistatic void 6728c2ecf20Sopenharmony_cippp_sync_input(struct syncppp *ap, const unsigned char *buf, 6738c2ecf20Sopenharmony_ci char *flags, int count) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct sk_buff *skb; 6768c2ecf20Sopenharmony_ci unsigned char *p; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (count == 0) 6798c2ecf20Sopenharmony_ci return; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (ap->flags & SC_LOG_INPKT) 6828c2ecf20Sopenharmony_ci ppp_print_buffer ("receive buffer", buf, count); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* stuff the chars in the skb */ 6858c2ecf20Sopenharmony_ci skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2); 6868c2ecf20Sopenharmony_ci if (!skb) { 6878c2ecf20Sopenharmony_ci printk(KERN_ERR "PPPsync: no memory (input pkt)\n"); 6888c2ecf20Sopenharmony_ci goto err; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci /* Try to get the payload 4-byte aligned */ 6918c2ecf20Sopenharmony_ci if (buf[0] != PPP_ALLSTATIONS) 6928c2ecf20Sopenharmony_ci skb_reserve(skb, 2 + (buf[0] & 1)); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci if (flags && *flags) { 6958c2ecf20Sopenharmony_ci /* error flag set, ignore frame */ 6968c2ecf20Sopenharmony_ci goto err; 6978c2ecf20Sopenharmony_ci } else if (count > skb_tailroom(skb)) { 6988c2ecf20Sopenharmony_ci /* packet overflowed MRU */ 6998c2ecf20Sopenharmony_ci goto err; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci skb_put_data(skb, buf, count); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci /* strip address/control field if present */ 7058c2ecf20Sopenharmony_ci p = skb->data; 7068c2ecf20Sopenharmony_ci if (skb->len >= 2 && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) { 7078c2ecf20Sopenharmony_ci /* chop off address/control */ 7088c2ecf20Sopenharmony_ci if (skb->len < 3) 7098c2ecf20Sopenharmony_ci goto err; 7108c2ecf20Sopenharmony_ci p = skb_pull(skb, 2); 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* PPP packet length should be >= 2 bytes when protocol field is not 7148c2ecf20Sopenharmony_ci * compressed. 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_ci if (!(p[0] & 0x01) && skb->len < 2) 7178c2ecf20Sopenharmony_ci goto err; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci /* queue the frame to be processed */ 7208c2ecf20Sopenharmony_ci skb_queue_tail(&ap->rqueue, skb); 7218c2ecf20Sopenharmony_ci return; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cierr: 7248c2ecf20Sopenharmony_ci /* queue zero length packet as error indication */ 7258c2ecf20Sopenharmony_ci if (skb || (skb = dev_alloc_skb(0))) { 7268c2ecf20Sopenharmony_ci skb_trim(skb, 0); 7278c2ecf20Sopenharmony_ci skb_queue_tail(&ap->rqueue, skb); 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic void __exit 7328c2ecf20Sopenharmony_cippp_sync_cleanup(void) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci if (tty_unregister_ldisc(N_SYNC_PPP) != 0) 7358c2ecf20Sopenharmony_ci printk(KERN_ERR "failed to unregister Sync PPP line discipline\n"); 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cimodule_init(ppp_sync_init); 7398c2ecf20Sopenharmony_cimodule_exit(ppp_sync_cleanup); 7408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 7418c2ecf20Sopenharmony_ciMODULE_ALIAS_LDISC(N_SYNC_PPP); 742