📄 ppp_async.c
字号:
/* * PPP async serial channel driver for Linux. * * Copyright 1999 Paul Mackerras. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * This driver provides the encapsulation and framing for sending * and receiving PPP frames over async serial lines. It relies on * the generic PPP layer to give it frames to send and to process * received frames. It implements the PPP line discipline. * * Part of the code in this driver was inspired by the old async-only * PPP driver, written by Michael Callahan and Al Longyear, and * subsequently hacked by Paul Mackerras. * * ==FILEVERSION 20000227== */#include <linux/module.h>#include <linux/kernel.h>#include <linux/skbuff.h>#include <linux/tty.h>#include <linux/netdevice.h>#include <linux/poll.h>#include <linux/ppp_defs.h>#include <linux/if_ppp.h>#include <linux/ppp_channel.h>#include <linux/spinlock.h>#include <linux/init.h>#include <asm/uaccess.h>#ifndef spin_trylock_bh#define spin_trylock_bh(lock) ({ int __r; local_bh_disable(); \ __r = spin_trylock(lock); \ if (!__r) local_bh_enable(); \ __r; })#endif#define PPP_VERSION "2.4.1"#define OBUFSIZE 256/* Structure for storing local state. */struct asyncppp { struct tty_struct *tty; unsigned int flags; unsigned int state; unsigned int rbits; int mru; spinlock_t xmit_lock; spinlock_t recv_lock; unsigned long xmit_flags; u32 xaccm[8]; u32 raccm; unsigned int bytes_sent; unsigned int bytes_rcvd; struct sk_buff *tpkt; int tpkt_pos; u16 tfcs; unsigned char *optr; unsigned char *olim; unsigned long last_xmit; struct sk_buff *rpkt; int lcp_fcs; struct ppp_channel chan; /* interface to generic ppp layer */ unsigned char obuf[OBUFSIZE];};/* Bit numbers in xmit_flags */#define XMIT_WAKEUP 0#define XMIT_FULL 1/* State bits */#define SC_TOSS 0x20000000#define SC_ESCAPE 0x40000000/* Bits in rbits */#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)static int flag_time = HZ;MODULE_PARM(flag_time, "i");/* * Prototypes. */static int ppp_async_encode(struct asyncppp *ap);static int ppp_async_send(struct ppp_channel *chan, struct sk_buff *skb);static int ppp_async_push(struct asyncppp *ap);static void ppp_async_flush_output(struct asyncppp *ap);static void ppp_async_input(struct asyncppp *ap, const unsigned char *buf, char *flags, int count);static int ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg);static void async_lcp_peek(struct asyncppp *ap, unsigned char *data, int len, int inbound);struct ppp_channel_ops async_ops = { ppp_async_send, ppp_async_ioctl};/* * Routines implementing the PPP line discipline. *//* * Called when a tty is put into PPP line discipline. */static intppp_asynctty_open(struct tty_struct *tty){ struct asyncppp *ap; int err; MOD_INC_USE_COUNT; err = -ENOMEM; ap = kmalloc(sizeof(*ap), GFP_KERNEL); if (ap == 0) goto out; /* initialize the asyncppp structure */ memset(ap, 0, sizeof(*ap)); ap->tty = tty; ap->mru = PPP_MRU; spin_lock_init(&ap->xmit_lock); spin_lock_init(&ap->recv_lock); ap->xaccm[0] = ~0U; ap->xaccm[3] = 0x60000000U; ap->raccm = ~0U; ap->optr = ap->obuf; ap->olim = ap->obuf; ap->lcp_fcs = -1; ap->chan.private = ap; ap->chan.ops = &async_ops; ap->chan.mtu = PPP_MRU; err = ppp_register_channel(&ap->chan); if (err) goto out_free; tty->disc_data = ap; return 0; out_free: kfree(ap); out: MOD_DEC_USE_COUNT; return err;}/* * Called when the tty is put into another line discipline * or it hangs up. * We assume that while we are in this routine, the tty layer * won't call any of the other line discipline entries for the * same tty. */static voidppp_asynctty_close(struct tty_struct *tty){ struct asyncppp *ap = tty->disc_data; if (ap == 0) return; tty->disc_data = 0; ppp_unregister_channel(&ap->chan); if (ap->rpkt != 0) kfree_skb(ap->rpkt); if (ap->tpkt != 0) kfree_skb(ap->tpkt); kfree(ap); MOD_DEC_USE_COUNT;}/* * Read does nothing. */static ssize_tppp_asynctty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t count){ /* For now, do the same as the old 2.3.x code useta */ struct asyncppp *ap = tty->disc_data; if (ap == 0) return -ENXIO; return ppp_channel_read(&ap->chan, file, buf, count);}/* * Write on the tty does nothing, the packets all come in * from the ppp generic stuff. */static ssize_tppp_asynctty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t count){ /* For now, do the same as the old 2.3.x code useta */ struct asyncppp *ap = tty->disc_data; if (ap == 0) return -ENXIO; return ppp_channel_write(&ap->chan, buf, count);}static intppp_asynctty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ struct asyncppp *ap = tty->disc_data; int err, val; err = -EFAULT; switch (cmd) { case PPPIOCGCHAN: err = -ENXIO; if (ap == 0) break; err = -EFAULT; if (put_user(ppp_channel_index(&ap->chan), (int *) arg)) break; err = 0; break; case PPPIOCGUNIT: err = -ENXIO; if (ap == 0) break; err = -EFAULT; if (put_user(ppp_unit_number(&ap->chan), (int *) arg)) break; err = 0; break; case TCGETS: case TCGETA: err = n_tty_ioctl(tty, file, cmd, arg); break; case TCFLSH: /* flush our buffers and the serial port's buffer */ if (arg == TCIOFLUSH || arg == TCOFLUSH) ppp_async_flush_output(ap); err = n_tty_ioctl(tty, file, cmd, arg); break; case FIONREAD: val = 0; if (put_user(val, (int *) arg)) break; err = 0; break;/* * For now, do the same as the old 2.3 driver useta */ case PPPIOCGFLAGS: case PPPIOCSFLAGS: case PPPIOCGASYNCMAP: case PPPIOCSASYNCMAP: case PPPIOCGRASYNCMAP: case PPPIOCSRASYNCMAP: case PPPIOCGXASYNCMAP: case PPPIOCSXASYNCMAP: case PPPIOCGMRU: case PPPIOCSMRU: err = -EPERM; if (!capable(CAP_NET_ADMIN)) break; err = ppp_async_ioctl(&ap->chan, cmd, arg); break; case PPPIOCATTACH: case PPPIOCDETACH: err = ppp_channel_ioctl(&ap->chan, cmd, arg); break; default: err = -ENOIOCTLCMD; } return err;}/* No kernel lock - fine */static unsigned intppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait){ unsigned int mask; struct asyncppp *ap = tty->disc_data; mask = POLLOUT | POLLWRNORM;/* * For now, do the same as the old 2.3 driver useta */ if (ap != 0) mask |= ppp_channel_poll(&ap->chan, file, wait); if (test_bit(TTY_OTHER_CLOSED, &tty->flags) || tty_hung_up_p(file)) mask |= POLLHUP; return mask;}static intppp_asynctty_room(struct tty_struct *tty){ return 65535;}static voidppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, char *flags, int count){ struct asyncppp *ap = tty->disc_data; if (ap == 0) return; spin_lock_bh(&ap->recv_lock); ppp_async_input(ap, buf, flags, count); spin_unlock_bh(&ap->recv_lock); if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && tty->driver.unthrottle) tty->driver.unthrottle(tty);}static voidppp_asynctty_wakeup(struct tty_struct *tty){ struct asyncppp *ap = tty->disc_data; clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); if (ap == 0) return; if (ppp_async_push(ap)) ppp_output_wakeup(&ap->chan);}static struct tty_ldisc ppp_ldisc = { magic: TTY_LDISC_MAGIC, name: "ppp", open: ppp_asynctty_open, close: ppp_asynctty_close, read: ppp_asynctty_read, write: ppp_asynctty_write, ioctl: ppp_asynctty_ioctl, poll: ppp_asynctty_poll, receive_room: ppp_asynctty_room, receive_buf: ppp_asynctty_receive, write_wakeup: ppp_asynctty_wakeup,};intppp_async_init(void){ int err; err = tty_register_ldisc(N_PPP, &ppp_ldisc); if (err != 0) printk(KERN_ERR "PPP_async: error %d registering line disc.\n", err); return err;}/* * The following routines provide the PPP channel interface. */static intppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg){ struct asyncppp *ap = chan->private; int err, val; u32 accm[8]; err = -EFAULT; switch (cmd) { case PPPIOCGFLAGS: val = ap->flags | ap->rbits; if (put_user(val, (int *) arg)) break; err = 0; break; case PPPIOCSFLAGS: if (get_user(val, (int *) arg)) break; ap->flags = val & ~SC_RCV_BITS; spin_lock_bh(&ap->recv_lock); ap->rbits = val & SC_RCV_BITS; spin_unlock_bh(&ap->recv_lock); err = 0; break; case PPPIOCGASYNCMAP: if (put_user(ap->xaccm[0], (u32 *) arg)) break; err = 0; break; case PPPIOCSASYNCMAP: if (get_user(ap->xaccm[0], (u32 *) arg)) break; err = 0; break; case PPPIOCGRASYNCMAP: if (put_user(ap->raccm, (u32 *) arg)) break; err = 0; break; case PPPIOCSRASYNCMAP: if (get_user(ap->raccm, (u32 *) arg)) break; err = 0; break; case PPPIOCGXASYNCMAP: if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm))) break; err = 0; break; case PPPIOCSXASYNCMAP: if (copy_from_user(accm, (void *) arg, sizeof(accm))) break; accm[2] &= ~0x40000000U; /* can't escape 0x5e */ accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ memcpy(ap->xaccm, accm, sizeof(ap->xaccm)); err = 0; break; case PPPIOCGMRU: if (put_user(ap->mru, (int *) arg)) break; err = 0; break; case PPPIOCSMRU: if (get_user(val, (int *) arg)) break; if (val < PPP_MRU) val = PPP_MRU; ap->mru = val; err = 0; break; default: err = -ENOTTY; } return err;}/* * Procedures for encapsulation and framing. */u16 ppp_crc16_table[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -