X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=linux%2Fppp.c;h=f5f9151f5cc3e1744ae9412dc5787f39790037d4;hp=71b6b4c716bd57cde138e7bafe84306292770ef7;hb=745f1b59d38e294452a502e4df4149902946f18d;hpb=0f61ac5460e89dd768f1fb56fbdd8fa6f8af79f1 diff --git a/linux/ppp.c b/linux/ppp.c index 71b6b4c..f5f9151 100644 --- a/linux/ppp.c +++ b/linux/ppp.c @@ -2,14 +2,13 @@ * * Michael Callahan * Al Longyear + * Extensively rewritten by Paul Mackerras * - * Dynamic PPP devices by Jim Freeman . - * ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid - * - * ==FILEVERSION 8== + * ==FILEVERSION 990510== * * NOTE TO MAINTAINERS: - * If you modify this file at all, increment the number above. + * If you modify this file at all, please set the number above to the + * date of the modification as YYMMDD (year month day). * ppp.c is shipped with a PPP distribution as well as with the kernel; * if everyone increases the FILEVERSION number above, then scripts * can do the right thing when deciding whether to install a new ppp.c @@ -39,64 +38,36 @@ CHECK_CHARACTERS - Enable the checking on all received characters for 8 data bits, no parity. This adds a small amount of processing for each received character. - - NEW_SKBUFF - Use NET3.020 sk_buff's - - IPX_CHANGE - Force the use of IPX support into the driver. - THIS IS **VERY** ALPHA LEVEL CODE!!!! */ -/* #define NEW_SKBUFF 1 */ #define OPTIMIZE_FLAG_TIME ((HZ * 3)/2) - #define CHECK_CHARACTERS 1 -#define PPP_COMPRESS 1 -#define USE_SKB_PROTOCOL 1 /* Set by the installation program! */ - -#ifdef NEW_SKBUFF -#undef USE_SKB_PROTOCOL -#define USE_SKB_PROTOCOL 2 -#endif - -#ifndef PPP_MAX_DEV -#define PPP_MAX_DEV 256 -#endif -#undef IPX_CHANGE +#define PPP_MAX_RCV_QLEN 32 /* max # frames we queue up for pppd */ -#if defined(IPX_CHANGE) || defined(NEW_SKBUFF) -#define PPP_REQUESTED_HDR_LEN PPP_HARD_HDR_LEN -#else -#define PPP_REQUESTED_HDR_LEN 0 -#endif - -/* $Id: ppp.c,v 1.6 1995/12/18 03:38:12 paulus Exp $ - * Added dynamic allocation of channels to eliminate - * compiled-in limits on the number of channels. - * - * Dynamic channel allocation code Copyright 1995 Caldera, Inc., - * released under the GNU General Public License Version 2. - */ +/* $Id: ppp.c,v 1.30 1999/07/23 07:07:34 paulus Exp $ */ -#if USE_SKB_PROTOCOL == 0 -#include /* still needed for 1.2 */ -#endif +#include +#include #include - -#ifndef MOD_INC_USE_COUNT /* for those 1.2 kernels still out there */ -#undef MOD_DEC_USE_COUNT -#define MOD_DEC_USE_COUNT do {} while (0) -#define MOD_INC_USE_COUNT do {} while (0) -#endif - -#include #include #include #include #include #include #include + +/* a macro to generate linux version number codes */ +#define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch)) + +#if LINUX_VERSION_CODE < VERSION(2,1,14) #include +#endif + +#if LINUX_VERSION_CODE >= VERSION(2,1,23) +#include +#endif + #include #include #include @@ -106,245 +77,236 @@ #include /* used in new tty drivers */ #include #include -#include -#include +#include #include #include #include #include #include - -#ifdef NEW_SKBUFF -#include -#else -typedef struct sk_buff sk_buff; -#define skb_data(skb) ((unsigned char *) (skb)->data) -#endif - -#include -#include +#include +#include #include -#include "slhc.h" +#include + +#define fcstab ppp_crc16_table /* Name of the table in the kernel */ #include + #include #include #include - -#undef PACKETPTR -#define PACKETPTR 1 #include -#undef PACKETPTR -#define bsd_decompress (*ppp->sc_rcomp->decompress) -#define bsd_compress (*ppp->sc_xcomp->compress) +#ifdef CONFIG_KMOD +#include +#endif +#ifdef CONFIG_KERNELD +#include +#endif + +#undef PPP_VERSION +#define PPP_VERSION "2.3.9" -#ifndef PPP_IPX -#define PPP_IPX 0x2b /* IPX protocol over PPP */ +#if LINUX_VERSION_CODE >= VERSION(2,1,4) + +#if LINUX_VERSION_CODE >= VERSION(2,1,5) +#include +#else +#include #endif -#ifndef PPP_LQR -#define PPP_LQR 0xc025 /* Link Quality Reporting Protocol */ +#define GET_USER get_user +#define PUT_USER put_user +#define COPY_FROM_USER copy_from_user +#define COPY_TO_USER copy_to_user + +#else /* 2.0.x and 2.1.x before 2.1.4 */ + +#define GET_USER(val, src) \ + (verify_area(VERIFY_READ, src, sizeof(*src))? -EFAULT: \ + ((val) = get_user(src), 0)) +#define PUT_USER(val, dst) \ + (verify_area(VERIFY_WRITE, dst, sizeof(*dst))? -EFAULT: \ + (put_user(val, dst), 0)) +#define COPY_FROM_USER(dst, src, size) \ + (verify_area(VERIFY_READ, src, size)? -EFAULT: \ + (memcpy_fromfs(dst, src, size), 0)) +#define COPY_TO_USER(dst, src, size) \ + (verify_area(VERIFY_WRITE, dst, size)? -EFAULT: \ + (memcpy_tofs(dst, src, size), 0)) + +#endif /* < 2.1.4 */ + +#if LINUX_VERSION_CODE < VERSION(2,1,37) +#define test_and_set_bit(nr, addr) set_bit(nr, addr) #endif -static int ppp_register_compressor (struct compressor *cp); -static void ppp_unregister_compressor (struct compressor *cp); +#if LINUX_VERSION_CODE < VERSION(2,1,25) +#define net_device_stats enet_statistics +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,57) +#define signal_pending(tsk) ((tsk)->signal & ~(tsk)->blocked) +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,60) +typedef int rw_ret_t; +typedef unsigned int rw_count_t; +#else +typedef ssize_t rw_ret_t; +typedef size_t rw_count_t; +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,86) +#define KFREE_SKB(s) dev_kfree_skb((s), FREE_WRITE) +#else +#define KFREE_SKB(s) kfree_skb(s) +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,15) +#define LIBERATE_SKB(s) ((s)->free = 1) +#else +#define LIBERATE_SKB(s) do { } while (0) +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,95) +#define SUSER() suser() +#else +#define SUSER() capable(CAP_NET_ADMIN) +#endif + +#if LINUX_VERSION_CODE < VERSION(2,2,0) +#define wmb() mb() +#endif /* * Local functions */ -static struct compressor *find_compressor (int type); -static void ppp_init_ctrl_blk (register struct ppp *); -static void ppp_kick_tty (struct ppp *, struct ppp_buffer *bfr); -static int ppp_doframe (struct ppp *); +static int ppp_register_compressor (struct compressor *cp); +static void ppp_unregister_compressor (struct compressor *cp); + +static void ppp_async_init(struct ppp *ppp); +static void ppp_async_release(struct ppp *ppp); +static int ppp_tty_sync_push(struct ppp *ppp); +static int ppp_tty_push(struct ppp *ppp); +static int ppp_async_encode(struct ppp *ppp); +static int ppp_async_send(struct ppp *, struct sk_buff *); +static int ppp_sync_send(struct ppp *, struct sk_buff *); +static void ppp_tty_flush_output(struct ppp *); + +static int ppp_ioctl(struct ppp *, unsigned int, unsigned long); +static int ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp); +static void ppp_proto_ccp(struct ppp *ppp, __u8 *dp, int len, int rcvd); +static void ppp_ccp_closed(struct ppp *ppp); +static int ppp_receive_frame(struct ppp *, struct sk_buff *); +static void ppp_receive_error(struct ppp *ppp); +static void ppp_output_wakeup(struct ppp *ppp); +static void ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb); +static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb); +static void ppp_send_frames(struct ppp *ppp); +static struct sk_buff *ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb); + +static struct ppp *ppp_find (int pid_value); static struct ppp *ppp_alloc (void); -static void ppp_print_buffer (const u_char *, const u_char *, int); -extern inline void ppp_stuff_char (struct ppp *ppp, - register struct ppp_buffer *buf, - register u_char chr); -extern inline int lock_buffer (register struct ppp_buffer *buf); - -static int rcv_proto_ip (struct ppp *, u_short, u_char *, int); -static int rcv_proto_ipx (struct ppp *, u_short, u_char *, int); -static int rcv_proto_vjc_comp (struct ppp *, u_short, u_char *, int); -static int rcv_proto_vjc_uncomp (struct ppp *, u_short, u_char *, int); -static int rcv_proto_unknown (struct ppp *, u_short, u_char *, int); -static int rcv_proto_lqr (struct ppp *, u_short, u_char *, int); -static void ppp_doframe_lower (struct ppp *, u_char *, int); -static int ppp_doframe (struct ppp *); - -extern int ppp_bsd_compressor_init(void); -static void ppp_proto_ccp (struct ppp *ppp, u_char *dp, int len, int rcvd); -static int rcv_proto_ccp (struct ppp *, u_short, u_char *, int); - -#define ins_char(pbuf,c) (buf_base(pbuf) [(pbuf)->count++] = (u_char)(c)) +static void ppp_generic_init(struct ppp *ppp); +static void ppp_release(struct ppp *ppp); +static void ppp_print_buffer (const char *, const __u8 *, int); +static struct compressor *find_compressor (int type); #ifndef OPTIMIZE_FLAG_TIME #define OPTIMIZE_FLAG_TIME 0 #endif -#ifndef PPP_MAX_DEV -#define PPP_MAX_DEV 256 -#endif - /* * Parameters which may be changed via insmod. */ static int flag_time = OPTIMIZE_FLAG_TIME; -static int max_dev = PPP_MAX_DEV; - -/* - * The "main" procedure to the ppp device - */ - -int ppp_init (struct device *); +#if LINUX_VERSION_CODE >= VERSION(2,1,19) +MODULE_PARM(flag_time, "i"); +#endif -/* - * Network device driver callback routines - */ +#define CHECK_PPP_MAGIC(ppp) do { \ + if (ppp->magic != PPP_MAGIC) { \ + printk(ppp_magic_warn, ppp, __FILE__, __LINE__); \ + } \ +} while (0) +#define CHECK_PPP(a) do { \ + CHECK_PPP_MAGIC(ppp); \ + if (!ppp->inuse) { \ + printk(ppp_warning, __LINE__); \ + return a; \ + } \ +} while (0) +#define CHECK_PPP_VOID() do { \ + CHECK_PPP_MAGIC(ppp); \ + if (!ppp->inuse) { \ + printk(ppp_warning, __LINE__); \ + return; \ + } \ +} while (0) + +#define tty2ppp(tty) ((struct ppp *) ((tty)->disc_data)) +#define dev2ppp(dev) ((struct ppp *) ((dev)->priv)) +#define ppp2tty(ppp) ((ppp)->tty) +#define ppp2dev(ppp) (&(ppp)->dev) + +static struct ppp *ppp_list = NULL; +static struct ppp *ppp_last = NULL; + +/* Define these strings only once for all macro invocations */ +static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n"; +static char ppp_magic_warn[] = KERN_WARNING "bad magic for ppp %p at %s:%d\n"; -static int ppp_dev_open (struct device *); -static int ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd); -static int ppp_dev_close (struct device *); -static int ppp_dev_xmit (sk_buff *, struct device *); -static struct enet_statistics *ppp_dev_stats (struct device *); - -#if USE_SKB_PROTOCOL == 0 /* The 1.2.x kernel is here */ -#define dev_alloc_skb(count) alloc_skb(count, GFP_ATOMIC) -#define skb_put(skb,count) skb_data(skb) -#define get_long_user(addr) get_user_long((void *) addr) -#define get_int_user(addr) ((int) get_user_long((void *) addr)) -#define put_byte_user(val,addr) put_fs_byte(val,((u_char *) (addr))) -#define put_long_user(val,addr) put_fs_long((val),((void *) (addr))) - -static unsigned short ppp_dev_type (sk_buff *, struct device *); -static int ppp_dev_header (unsigned char *buff, struct device *dev, - unsigned short type, void *daddr, void *saddr, - unsigned len, struct sk_buff *skb); - -#else /* The 1.3.x kernel is here */ -#define get_long_user(addr) get_user(((int *) addr)) -#define get_int_user(addr) ((int) get_user(((int *) addr))) -#define put_byte_user(val,addr) put_user((val),((u_char *) (addr))) -#define put_long_user(val,addr) put_user((val),((int *) (addr))) - -static int ppp_dev_header (sk_buff *, struct device *, unsigned short, - void *, void *, unsigned); -#endif +static char szVersion[] = PPP_VERSION; -#ifdef NEW_SKBUFF -static int ppp_dev_input (struct protocol *self, struct protocol *lower, - sk_buff *skb, void *saddr, void *daddr); -static int ppp_dev_output (struct protocol *self, sk_buff *skb, int type, - int subid, void *saddr, void *daddr, void *opt); -static int ppp_dev_getkey(int protocol, int subid, unsigned char *key); +#if LINUX_VERSION_CODE < VERSION(2,1,18) +static struct symbol_table ppp_syms = { +#include + X(ppp_register_compressor), + X(ppp_unregister_compressor), +#include +}; #else -static int ppp_dev_rebuild (void *, struct device *, unsigned long, - sk_buff *); +EXPORT_SYMBOL(ppp_register_compressor); +EXPORT_SYMBOL(ppp_unregister_compressor); #endif +/************************************************************* + * LINE DISCIPLINE SUPPORT + * The following code implements the PPP line discipline + * and supports using PPP on an async serial line. + *************************************************************/ + +#define in_xmap(ppp,c) (ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f))) +#define in_rmap(ppp,c) ((((unsigned int) (__u8) (c)) < 0x20) && \ + ppp->recv_async_map & (1 << (c))) + /* * TTY callbacks */ -static int ppp_tty_read (struct tty_struct *, struct file *, u_char *, - unsigned int); -static int ppp_tty_write (struct tty_struct *, struct file *, const u_char *, - unsigned int); -static int ppp_tty_ioctl (struct tty_struct *, struct file *, unsigned int, - unsigned long); -static int ppp_tty_select (struct tty_struct *tty, struct inode *inode, - struct file *filp, int sel_type, select_table * wait); +static rw_ret_t ppp_tty_read(struct tty_struct *, struct file *, __u8 *, + rw_count_t); +static rw_ret_t ppp_tty_write(struct tty_struct *, struct file *, const __u8 *, + rw_count_t); +static int ppp_tty_ioctl(struct tty_struct *, struct file *, unsigned int, + unsigned long); +#if LINUX_VERSION_CODE < VERSION(2,1,23) +static int ppp_tty_select(struct tty_struct *tty, struct inode *inode, + struct file *filp, int sel_type, select_table * wait); +#else +static unsigned int ppp_tty_poll(struct tty_struct *tty, struct file *filp, + poll_table * wait); +#endif static int ppp_tty_open (struct tty_struct *); static void ppp_tty_close (struct tty_struct *); static int ppp_tty_room (struct tty_struct *tty); -static void ppp_tty_receive (struct tty_struct *tty, const u_char * cp, +static void ppp_tty_receive (struct tty_struct *tty, const __u8 * cp, char *fp, int count); static void ppp_tty_wakeup (struct tty_struct *tty); -#define CHECK_PPP(a) if (!ppp->inuse) { printk (ppp_warning, __LINE__); return a;} -#define CHECK_PPP_VOID() if (!ppp->inuse) { printk (ppp_warning, __LINE__); return;} - -#define in_xmap(ppp,c) (ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f))) -#define in_rmap(ppp,c) ((((unsigned int) (u_char) (c)) < 0x20) && \ - ppp->recv_async_map & (1 << (c))) - -#define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) - -#define tty2ppp(tty) ((struct ppp *) (tty->disc_data)) -#define dev2ppp(dev) ((struct ppp *) (dev->priv)) -#define ppp2tty(ppp) ((struct tty_struct *) ppp->tty) -#define ppp2dev(ppp) ((struct device *) ppp->dev) - -struct ppp_hdr { - unsigned char address; - unsigned char control; - unsigned char protocol[2]; -}; - -#define PPP_HARD_HDR_LEN (sizeof (struct ppp_hdr)) - -typedef struct ppp_ctrl { - struct ppp_ctrl *next; /* Next structure in the list */ - char name [8]; /* Name of the device */ - struct ppp ppp; /* PPP control table */ - struct device dev; /* Device information table */ -} ppp_ctrl_t; - -static ppp_ctrl_t *ppp_list = NULL; - -#define ctl2ppp(ctl) (struct ppp *) &ctl->ppp -#define ctl2dev(ctl) (struct device *) &ctl->dev -#undef PPP_NRUNIT - -/* Buffer types */ -#define BUFFER_TYPE_DEV_RD 0 /* ppp read buffer */ -#define BUFFER_TYPE_TTY_WR 1 /* tty write buffer */ -#define BUFFER_TYPE_DEV_WR 2 /* ppp write buffer */ -#define BUFFER_TYPE_TTY_RD 3 /* tty read buffer */ -#define BUFFER_TYPE_VJ 4 /* vj compression buffer */ - -/* Define this string only once for all macro envocations */ -static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n"; - -static char szVersion[] = PPP_VERSION; - -#ifdef NEW_SKBUFF -static struct protocol proto_ppp; -#endif - -/* - * Information for the protocol decoder - */ - -typedef int (*pfn_proto) (struct ppp *, u_short, u_char *, int); - -typedef struct ppp_proto_struct { - int proto; - pfn_proto func; -} ppp_proto_type; - -static -ppp_proto_type proto_list[] = { - { PPP_IP, rcv_proto_ip }, - { PPP_IPX, rcv_proto_ipx }, - { PPP_VJC_COMP, rcv_proto_vjc_comp }, - { PPP_VJC_UNCOMP, rcv_proto_vjc_uncomp }, - { PPP_LQR, rcv_proto_lqr }, - { PPP_CCP, rcv_proto_ccp }, - { 0, rcv_proto_unknown } /* !!! MUST BE LAST !!! */ -}; - -/* - * Values for FCS calculations. - */ - -#define PPP_INITFCS 0xffff /* Initial FCS value */ -#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ -#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ ppp_crc16_table[((fcs) ^ (c)) & 0xff]) - -unsigned short ppp_crc16_table[256] = +__u16 ppp_crc16_table[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, @@ -379,743 +341,998 @@ unsigned short ppp_crc16_table[256] = 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; +#if LINUX_VERSION_CODE >= VERSION(2,1,18) +EXPORT_SYMBOL(ppp_crc16_table); +#endif #ifdef CHECK_CHARACTERS -static unsigned paritytab[8] = +static __u32 paritytab[8] = { 0x96696996, 0x69969669, 0x69969669, 0x96696996, 0x69969669, 0x96696996, 0x96696996, 0x69969669 }; #endif -/* local function to store a value into the LQR frame */ -extern inline u_char * store_long (register u_char *p, register int value) { - *p++ = (u_char) (value >> 24); - *p++ = (u_char) (value >> 16); - *p++ = (u_char) (value >> 8); - *p++ = (u_char) value; - return p; -} - -/************************************************************* - * INITIALIZATION - *************************************************************/ - -/* This procedure is called once and once only to define who we are to - * the operating system and the various procedures that it may use in - * accessing the ppp protocol. +/* + * This procedure is called at initialization time to register + * the PPP line discipline. */ - static int -ppp_first_time (void) +ppp_first_time(void) { static struct tty_ldisc ppp_ldisc; int status; - printk (KERN_INFO - "PPP: version %s (dynamic channel allocation)" -#ifdef NEW_SKBUFF - " NEW_SKBUFF" -#endif - "\n", szVersion); - -#ifndef MODULE /* slhc module logic has its own copyright announcment */ - printk (KERN_INFO - "TCP compression code copyright 1989 Regents of the " - "University of California\n"); -#endif - - printk (KERN_INFO - "PPP Dynamic channel allocation code copyright 1995 " - "Caldera, Inc.\n"); -/* - * Register the protocol for the device - */ + printk(KERN_INFO + "PPP: version %s (demand dialling)" + "\n", szVersion); -#ifdef NEW_SKBUFF - memset (&proto_ppp, 0, sizeof (proto_ppp)); - - proto_ppp.name = "PPP"; - proto_ppp.output = ppp_dev_output; - proto_ppp.input = ppp_dev_input; - proto_ppp.bh_input = ppp_dev_input; - proto_ppp.control_event = default_protocol_control; - proto_ppp.get_binding = ppp_dev_getkey; - proto_ppp.header_space = PPP_REQUESTED_HDR_LEN; - - protocol_register(&proto_ppp); +#ifndef MODULE /* slhc module logic has its own copyright announcement */ + printk(KERN_INFO + "TCP compression code copyright 1989 Regents of the " + "University of California\n"); #endif -/* - * Register the tty dicipline - */ + /* + * Register the tty discipline + */ (void) memset (&ppp_ldisc, 0, sizeof (ppp_ldisc)); ppp_ldisc.magic = TTY_LDISC_MAGIC; +#if LINUX_VERSION_CODE >= VERSION(2,1,28) + ppp_ldisc.name = "ppp"; +#endif ppp_ldisc.open = ppp_tty_open; ppp_ldisc.close = ppp_tty_close; ppp_ldisc.read = ppp_tty_read; ppp_ldisc.write = ppp_tty_write; ppp_ldisc.ioctl = ppp_tty_ioctl; +#if LINUX_VERSION_CODE < VERSION(2,1,23) ppp_ldisc.select = ppp_tty_select; +#else + ppp_ldisc.poll = ppp_tty_poll; +#endif ppp_ldisc.receive_room = ppp_tty_room; ppp_ldisc.receive_buf = ppp_tty_receive; ppp_ldisc.write_wakeup = ppp_tty_wakeup; - + status = tty_register_ldisc (N_PPP, &ppp_ldisc); if (status == 0) - printk (KERN_INFO "PPP line discipline registered.\n"); + printk(KERN_INFO "PPP line discipline registered.\n"); else - printk (KERN_ERR "error registering line discipline: %d\n", - status); + printk(KERN_ERR "error registering line discipline: %d\n", + status); return status; } -/************************************************************* - * INITIALIZATION - *************************************************************/ - -/* called when the device is actually created */ - -static int -ppp_init_dev (struct device *dev) -{ - int indx; -#ifdef NEW_SKBUFF - dev->default_protocol = &proto_ppp; /* Our protocol layer is PPP */ -#else - dev->hard_header = ppp_dev_header; -#if USE_SKB_PROTOCOL == 0 - dev->type_trans = ppp_dev_type; -#endif - dev->rebuild_header = ppp_dev_rebuild; - dev->hard_header_len = PPP_REQUESTED_HDR_LEN; -#endif - - /* device INFO */ - dev->mtu = PPP_MTU; - dev->hard_start_xmit = ppp_dev_xmit; - dev->open = ppp_dev_open; - dev->stop = ppp_dev_close; - dev->get_stats = ppp_dev_stats; - dev->do_ioctl = ppp_dev_ioctl; - dev->addr_len = 0; - dev->type = ARPHRD_PPP; - - for (indx = 0; indx < DEV_NUMBUFFS; indx++) - skb_queue_head_init (&dev->buffs[indx]); - - /* New-style flags */ - dev->flags = IFF_POINTOPOINT; - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; /* sizeof (unsigned long) */ - - return 0; -} +#ifndef MODULE /* - * Local procedure to initialize the ppp structure + * Called at boot time if the PPP driver is compiled into the kernel. */ - -static void -ppp_init_ctrl_blk (register struct ppp *ppp) -{ - ppp->magic = PPP_MAGIC; - ppp->toss = 0xE0; - ppp->escape = 0; - - ppp->flags = 0; - ppp->mtu = PPP_MTU; - ppp->mru = PPP_MRU; - - memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map)); - ppp->xmit_async_map[0] = 0xffffffff; - ppp->xmit_async_map[3] = 0x60000000; - ppp->recv_async_map = 0x00000000; - - ppp->rbuf = NULL; - ppp->wbuf = NULL; - ppp->ubuf = NULL; - ppp->cbuf = NULL; - ppp->slcomp = NULL; - ppp->read_wait = NULL; - ppp->write_wait = NULL; - ppp->last_xmit = jiffies - flag_time; - - /* clear statistics */ - memset (&ppp->stats, '\0', sizeof (struct pppstat)); - - /* Reset the demand dial information */ - ppp->ddinfo.xmit_idle= /* time since last NP packet sent */ - ppp->ddinfo.recv_idle=jiffies; /* time since last NP packet received */ - - /* PPP compression data */ - ppp->sc_xc_state = - ppp->sc_rc_state = NULL; -} - -static struct symbol_table ppp_syms = { -#include - X(ppp_register_compressor), - X(ppp_unregister_compressor), - X(ppp_crc16_table), -#include -}; - -/* called at boot/load time for each ppp device defined in the kernel */ - -#ifndef MODULE int -ppp_init (struct device *dev) +ppp_init(struct device *dev) { static int first_time = 1; int answer = 0; if (first_time) { first_time = 0; - answer = ppp_first_time(); + answer = ppp_first_time(); +#if LINUX_VERSION_CODE < VERSION(2,1,18) if (answer == 0) - (void) register_symtab (&ppp_syms); + (void) register_symtab(&ppp_syms); +#endif } if (answer == 0) - answer = -ENODEV; + answer = -ENODEV; return answer; } #endif /* - * Routine to allocate a buffer for later use by the driver. + * Initialize the async-specific parts of the ppp structure. */ - -static struct ppp_buffer * -ppp_alloc_buf (int size, int type) +static void +ppp_async_init(struct ppp *ppp) { - struct ppp_buffer *buf; + ppp->escape = 0; + ppp->toss = 0xE0; + ppp->tty_pushing = 0; - buf = (struct ppp_buffer *) kmalloc (size + sizeof (struct ppp_buffer), - GFP_ATOMIC); + memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map)); + ppp->xmit_async_map[0] = 0xffffffff; + ppp->xmit_async_map[3] = 0x60000000; + ppp->recv_async_map = 0xffffffff; - if (buf != NULL) { - buf->size = size - 1; /* Mask for the buffer size */ - buf->type = type; - buf->locked = 0; - buf->count = 0; - buf->head = 0; - buf->tail = 0; - buf->fcs = PPP_INITFCS; - } - return (buf); -} + ppp->tpkt = NULL; + ppp->tfcs = PPP_INITFCS; + ppp->optr = ppp->obuf; + ppp->olim = ppp->obuf; -/* - * Routine to release the allocated buffer. - */ + ppp->rpkt = NULL; + ppp->rfcs = PPP_INITFCS; -static void -ppp_free_buf (struct ppp_buffer *ptr) -{ - if (ptr != NULL) - kfree (ptr); + ppp->tty = NULL; + ppp->backup_tty = NULL; + + ppp->bytes_sent = 0; + ppp->bytes_rcvd = 0; } /* - * Lock the indicated transmit buffer + * Clean up the async-specific parts of the ppp structure. */ - -extern inline int -lock_buffer (register struct ppp_buffer *buf) +static void +ppp_async_release(struct ppp *ppp) { - register int state; - int flags; -/* - * Save the current state and if free then set it to the "busy" state - */ - save_flags (flags); - cli (); - state = buf->locked; - if (state == 0) - buf->locked = 2; -/* - * Restore the flags and return the previous state. 0 implies success. - */ - restore_flags (flags); - return (state); + struct sk_buff *skb; + + if ((skb = ppp->rpkt) != NULL) + KFREE_SKB(skb); + ppp->rpkt = NULL; + if ((skb = ppp->tpkt) != NULL) + KFREE_SKB(skb); + ppp->tpkt = NULL; } /* - * MTU has been changed by the IP layer. Unfortunately we are not told - * about this, but we spot it ourselves and fix things up. We could be - * in an upcall from the tty driver, or in an ip packet queue. + * TTY callback. + * + * Called when the tty discipline is switched to PPP. */ static int -ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru) +ppp_tty_open (struct tty_struct *tty) { - struct device *dev; - - struct ppp_buffer *new_rbuf; - struct ppp_buffer *new_wbuf; - struct ppp_buffer *new_cbuf; - struct ppp_buffer *new_tbuf; - - struct ppp_buffer *old_rbuf; - struct ppp_buffer *old_wbuf; - struct ppp_buffer *old_cbuf; - struct ppp_buffer *old_tbuf; - - int mtu, mru; -/* - * Allocate the buffer from the kernel for the data - */ - dev = ppp2dev (ppp); - mru = new_mru; - /* allow for possible escapement of every character */ - mtu = (new_mtu * 2) + 20; + struct ppp *ppp; - /* RFC 1331, section 7.2 says the minimum value is 1500 bytes */ - if (mru < PPP_MRU) - mru = PPP_MRU; + /* + * Allocate a ppp structure to use. + */ + tty->disc_data = NULL; + ppp = ppp_find(current->pid); + if (ppp != NULL) { + /* + * If we are taking over a ppp unit which is currently + * connected to a loopback pty, there's not much to do. + */ + CHECK_PPP(-EINVAL); - mru += 10; - - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO "ppp: channel %s mtu = %d, mru = %d\n", - dev->name, new_mtu, new_mru); + } else { + ppp = ppp_alloc(); + if (ppp == NULL) { + printk(KERN_ERR "ppp_alloc failed\n"); + return -ENFILE; + } - new_wbuf = ppp_alloc_buf (mtu+PPP_HARD_HDR_LEN, BUFFER_TYPE_DEV_WR); - new_tbuf = ppp_alloc_buf ((PPP_MTU * 2) + 24, BUFFER_TYPE_TTY_WR); - new_rbuf = ppp_alloc_buf (mru + 84, BUFFER_TYPE_DEV_RD); - new_cbuf = ppp_alloc_buf (mru+PPP_HARD_HDR_LEN, BUFFER_TYPE_VJ); -/* - * If the buffers failed to allocate then complain and release the partial - * allocations. - */ - if (new_wbuf == NULL || new_tbuf == NULL || - new_rbuf == NULL || new_cbuf == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp: failed to allocate new buffers\n"); + /* + * Initialize the control block + */ + ppp_generic_init(ppp); + ppp_async_init(ppp); - ppp_free_buf (new_wbuf); - ppp_free_buf (new_tbuf); - ppp_free_buf (new_rbuf); - ppp_free_buf (new_cbuf); - return 0; + MOD_INC_USE_COUNT; } -/* - * Update the pointers to the new buffer structures. - */ - cli (); - old_wbuf = ppp->wbuf; - old_rbuf = ppp->rbuf; - old_cbuf = ppp->cbuf; - old_tbuf = ppp->tbuf; - - ppp->wbuf = new_wbuf; - ppp->rbuf = new_rbuf; - ppp->cbuf = new_cbuf; - ppp->tbuf = new_tbuf; - - ppp->rbuf->size -= 80; /* reserve space for vj header expansion */ - - dev->mem_start = (unsigned long) buf_base (new_wbuf); - dev->mem_end = (unsigned long) (dev->mem_start + mtu); - dev->rmem_start = (unsigned long) buf_base (new_rbuf); - dev->rmem_end = (unsigned long) (dev->rmem_start + mru); -/* - * Update the parameters for the new buffer sizes - */ - ppp->toss = 0xE0; /* To ignore characters until new FLAG */ - ppp->escape = 0; /* No pending escape character */ - dev->mtu = - ppp->mtu = new_mtu; - ppp->mru = new_mru; + tty->disc_data = ppp; + ppp->tty = tty; - ppp->s1buf = NULL; - ppp->s2buf = NULL; - ppp->xbuf = NULL; + /* + * Flush any pending characters in the driver + */ + if (tty->driver.flush_buffer) + tty->driver.flush_buffer (tty); - ppp->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - ppp->flags &= ~SC_XMIT_BUSY; + return ppp->line; +} - sti (); /* - * Release old buffer pointers - */ - ppp_free_buf (old_rbuf); - ppp_free_buf (old_wbuf); - ppp_free_buf (old_cbuf); - ppp_free_buf (old_tbuf); - return 1; -} - -/* - * CCP is down; free (de)compressor state if necessary. + * TTY callback. + * + * Called when the line discipline is changed to something + * else, the tty is closed, or the tty detects a hangup. */ static void -ppp_ccp_closed (struct ppp *ppp) +ppp_tty_close (struct tty_struct *tty) { - if (ppp->sc_xc_state) { - (*ppp->sc_xcomp->comp_free) (ppp->sc_xc_state); - ppp->sc_xc_state = NULL; + struct ppp *ppp = tty2ppp(tty); + + if (ppp == NULL) + return; + tty->disc_data = NULL; + if (ppp->magic != PPP_MAGIC) { + printk(KERN_WARNING "ppp_tty_close: bogus\n"); + return; } + if (!ppp->inuse) { + printk(KERN_WARNING "ppp_tty_close: not inuse\n"); + ppp->tty = ppp->backup_tty = 0; + return; + } + if (tty == ppp->backup_tty) + ppp->backup_tty = 0; + if (tty != ppp->tty) + return; + if (ppp->backup_tty) { + ppp->tty = ppp->backup_tty; + if (ppp_tty_push(ppp)) + ppp_output_wakeup(ppp); + wake_up_interruptible(&ppp->read_wait); + } else { + ppp->tty = 0; + ppp->sc_xfer = 0; + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO "ppp: channel %s closing.\n", + ppp2dev(ppp)->name); - if (ppp->sc_rc_state) { - (*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state); - ppp->sc_rc_state = NULL; + ppp_async_release(ppp); + ppp_release(ppp); + MOD_DEC_USE_COUNT; } } /* - * Called to release all of the information in the current PPP structure. - * - * It is called when the ppp device goes down or if it is unable to go - * up. + * Read a PPP frame from the rcv_q list, + * waiting if necessary */ - -static void -ppp_release (struct ppp *ppp) +static rw_ret_t +ppp_tty_read(struct tty_struct *tty, struct file *file, __u8 * buf, + rw_count_t nr) { - struct tty_struct *tty; - struct device *dev; + struct ppp *ppp = tty2ppp (tty); + struct sk_buff *skb; + rw_ret_t len, err; - tty = ppp2tty (ppp); - dev = ppp2dev (ppp); + /* + * Validate the pointers + */ + if (!ppp) + return -EIO; + CHECK_PPP(-ENXIO); + + /* + * Before we attempt to write the frame to the user, ensure that the + * user has access to the pages for the total buffer length. + */ + err = verify_area(VERIFY_WRITE, buf, nr); + if (err != 0) + return (err); + + /* + * Wait for a frame to arrive if necessary. + * We increment the module use count so that the module + * can't go away while we're sleeping. + */ + MOD_INC_USE_COUNT; + skb = NULL; + for (;;) { + ppp = tty2ppp(tty); + err = 0; + if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse + || tty != ppp->tty) + break; - ppp_ccp_closed (ppp); + skb = skb_dequeue(&ppp->rcv_q); + if (skb != 0) + break; - if (tty != NULL && tty->disc_data == ppp) - tty->disc_data = NULL; /* Break the tty->ppp link */ + /* + * If no frame is available, return -EAGAIN or wait. + */ + err = -EAGAIN; + if (file->f_flags & O_NONBLOCK) + break; - if (dev && dev->flags & IFF_UP) { - dev_close (dev); /* close the device properly */ - dev->flags = 0; /* prevent recursion */ + interruptible_sleep_on(&ppp->read_wait); + err = -EINTR; + if (signal_pending(current)) + break; } + MOD_DEC_USE_COUNT; + if (skb == 0) + return err; - ppp_free_buf (ppp->rbuf); - ppp_free_buf (ppp->wbuf); - ppp_free_buf (ppp->cbuf); - ppp_free_buf (ppp->ubuf); - ppp_free_buf (ppp->tbuf); - - ppp->rbuf = - ppp->wbuf = - ppp->cbuf = - ppp->tbuf = - ppp->xbuf = - ppp->s1buf = - ppp->s2buf = - ppp->ubuf = NULL; - - if (ppp->slcomp) { - slhc_free (ppp->slcomp); - ppp->slcomp = NULL; + /* + * Ensure that the frame will fit within the caller's buffer. + * If not, just discard the frame. + */ + len = skb->len; + if (len > nr) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG + "ppp: read of %lu bytes too small for %ld " + "frame\n", (unsigned long) nr, (long) len); + ppp->stats.ppp_ierrors++; + err = -EOVERFLOW; + goto out; } - ppp->inuse = 0; - ppp->tty = NULL; + /* + * Copy the received data from the buffer to the caller's area. + */ + err = len; + if (COPY_TO_USER(buf, skb->data, len)) + err = -EFAULT; + +out: + KFREE_SKB(skb); + return err; } /* - * Device callback. - * - * Called when the PPP device goes down in response to an ifconfig request. + * Writing to a tty in ppp line discipline sends a PPP frame. + * Used by pppd to send control packets (LCP, etc.). */ - -static void -ppp_tty_close (struct tty_struct *tty) +static rw_ret_t +ppp_tty_write(struct tty_struct *tty, struct file *file, const __u8 * data, + rw_count_t count) { struct ppp *ppp = tty2ppp (tty); + __u8 *new_data; + struct sk_buff *skb; - if (ppp != NULL) { - if (ppp->magic != PPP_MAGIC) { - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp: trying to close unopened tty!\n"); - } else { - CHECK_PPP_VOID(); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO "ppp: channel %s closing.\n", - ppp2dev(ppp) -> name); - ppp_release (ppp); - MOD_DEC_USE_COUNT; - } + /* + * Verify the pointers. + */ + if (!ppp) + return -EIO; + + if (ppp->magic != PPP_MAGIC) + return -EIO; + + CHECK_PPP(-ENXIO); + + /* + * Ensure that the caller does not wish to send too much. + */ + if (count > PPP_MTU + PPP_HDRLEN) { + if (ppp->flags & SC_DEBUG) + printk(KERN_WARNING + "ppp_tty_write: truncating user packet " + "from %lu to mtu %d\n", (unsigned long) count, + PPP_MTU + PPP_HDRLEN); + count = PPP_MTU + PPP_HDRLEN; + } + + /* + * Allocate a buffer for the data and fetch it from the user space. + */ + skb = alloc_skb(count, GFP_KERNEL); + if (skb == NULL) { + printk(KERN_ERR "ppp_tty_write: no memory\n"); + return 0; } + LIBERATE_SKB(skb); + new_data = skb_put(skb, count); + + /* + * Retrieve the user's buffer + */ + if (COPY_FROM_USER(new_data, data, count)) { + KFREE_SKB(skb); + return -EFAULT; + } + + /* + * Send the frame + */ + ppp_send_ctrl(ppp, skb); + + return (rw_ret_t) count; } /* - * TTY callback. - * - * Called when the tty discipline is switched to PPP. + * Process the IOCTL call for the tty device. + * Only the ioctls that relate to using ppp on async serial lines + * are processed here; the rest are handled by ppp_ioctl. */ - static int -ppp_tty_open (struct tty_struct *tty) +ppp_tty_ioctl (struct tty_struct *tty, struct file * file, + unsigned int param2, unsigned long param3) { struct ppp *ppp = tty2ppp (tty); -/* - * There should not be an existing table for this slot. - */ - if (ppp) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_tty_open: gack! tty already associated to %s!\n", - ppp->magic == PPP_MAGIC ? ppp2dev(ppp)->name - : "unknown"); - return -EEXIST; - } -/* - * Allocate the structure from the system - */ - ppp = ppp_alloc(); - if (ppp == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_tty_open: couldn't allocate ppp channel\n"); - return -ENFILE; - } -/* - * Initialize the control block - */ - ppp_init_ctrl_blk (ppp); - ppp->tty = tty; - tty->disc_data = ppp; -/* - * Flush any pending characters in the driver and discipline. - */ - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer (tty); + register int temp_i = 0; + int error = -EFAULT; - if (tty->driver.flush_buffer) - tty->driver.flush_buffer (tty); -/* - * Allocate space for the default VJ header compression slots - */ - ppp->slcomp = slhc_init (16, 16); - if (ppp->slcomp == NULL) { + /* + * Verify the status of the PPP device. + */ + if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse) + return -ENXIO; + + /* + * The user must have an euid of root to do these requests. + */ + if (!SUSER()) + return -EPERM; + + switch (param2) { + case PPPIOCGASYNCMAP: + /* + * Retrieve the transmit async map + */ + if (PUT_USER(ppp->xmit_async_map[0], (int *) param3)) + break; + error = 0; + break; + + case PPPIOCSASYNCMAP: + /* + * Set the transmit async map + */ + if (GET_USER(temp_i, (int *) param3)) + break; + ppp->xmit_async_map[0] = temp_i; if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_tty_open: no space for compression buffers!\n"); - ppp_release (ppp); - return -ENOMEM; - } -/* - * Allocate space for the MTU and MRU buffers - */ - if (ppp_changedmtu (ppp, ppp2dev(ppp)->mtu, ppp->mru) == 0) { - ppp_release (ppp); - return -ENOMEM; - } -/* - * Allocate space for a user level buffer - */ - ppp->ubuf = ppp_alloc_buf (RBUFSIZE, BUFFER_TYPE_TTY_RD); - if (ppp->ubuf == NULL) { + printk(KERN_INFO + "ppp_tty_ioctl: set xmit asyncmap %x\n", + ppp->xmit_async_map[0]); + error = 0; + break; + + case PPPIOCSRASYNCMAP: + /* + * Set the receive async map + */ + if (GET_USER(temp_i, (int *) param3)) + break; + ppp->recv_async_map = temp_i; if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_tty_open: no space for user receive buffer\n"); - ppp_release (ppp); - return -ENOMEM; - } + printk(KERN_INFO + "ppp_tty_ioctl: set rcv asyncmap %x\n", + ppp->recv_async_map); + error = 0; + break; - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO "ppp: channel %s open\n", - ppp2dev(ppp)->name); + case PPPIOCGXASYNCMAP: + /* + * Get the map of characters to be escaped on transmission. + */ + if (COPY_TO_USER((void *) param3, ppp->xmit_async_map, + sizeof (ppp->xmit_async_map))) + break; + error = 0; + break; - MOD_INC_USE_COUNT; - return (ppp->line); + case PPPIOCSXASYNCMAP: + /* + * Set the map of characters to be escaped on transmission. + */ + { + __u32 temp_tbl[8]; + + if (COPY_FROM_USER(temp_tbl, (void *) param3, + sizeof (temp_tbl))) + break; + + temp_tbl[1] = 0x00000000; + temp_tbl[2] &= ~0x40000000; + temp_tbl[3] |= 0x60000000; + + memcpy(ppp->xmit_async_map, temp_tbl, + sizeof (ppp->xmit_async_map)); + + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO + "ppp_tty_ioctl: set xasyncmap\n"); + error = 0; + } + break; + + case PPPIOCXFERUNIT: + /* + * Set up this PPP unit to be used next time this + * process sets a tty to PPP line discipline. + */ + ppp->backup_tty = tty; + ppp->sc_xfer = current->pid; + error = 0; + break; + + case TCGETS: + case TCGETA: + /* + * Allow users to read, but not set, the serial port parameters + */ + error = n_tty_ioctl (tty, file, param2, param3); + break; + + case TCFLSH: + /* + * Flush our buffers, then call the generic code to + * flush the serial port's buffer. + */ + if (param3 == TCIFLUSH || param3 == TCIOFLUSH) { + struct sk_buff *skb; + while ((skb = skb_dequeue(&ppp->rcv_q)) != NULL) + KFREE_SKB(skb); + } + if (param3 == TCIOFLUSH || param3 == TCOFLUSH) + ppp_tty_flush_output(ppp); + error = n_tty_ioctl (tty, file, param2, param3); + break; + + case FIONREAD: + /* + * Returns how many bytes are available for a read(). + */ + { + unsigned long flags; + struct sk_buff *skb; + int count = 0; + + save_flags(flags); + cli(); + skb = skb_peek(&ppp->rcv_q); + if (skb != 0) + count = skb->len; + restore_flags(flags); + if (PUT_USER(count, (int *) param3)) + break; + error = 0; + } + break; + + default: + /* + * All other ioctl() events will come here. + */ + error = ppp_ioctl(ppp, param2, param3); + break; + } + return error; } /* - * Local function to send the next portion of the buffer. - * - * Called by the tty driver's tty_wakeup function should it be entered - * because the partial buffer was transmitted. - * - * Called by kick_tty to send the initial portion of the buffer. + * TTY callback. * - * Completion processing of the buffer transmission is handled here. + * Process the select() or poll() statement for the PPP device. */ -static void -ppp_tty_wakeup_code (struct ppp *ppp, struct tty_struct *tty, - struct ppp_buffer *xbuf) +#if LINUX_VERSION_CODE < VERSION(2,1,23) +static int +ppp_tty_select(struct tty_struct *tty, struct inode *inode, + struct file *filp, int sel_type, select_table * wait) { - register int count, actual; -/* - * Prevent re-entrancy by ensuring that this routine is called only once. - */ - cli (); - if (ppp->flags & SC_XMIT_BUSY) { - sti (); - return; + struct ppp *ppp = tty2ppp(tty); + int result = 1; + + /* + * Verify the status of the PPP device. + */ + if (!ppp || tty != ppp->tty) + return -EBADF; + + CHECK_PPP(-EBADF); + + switch (sel_type) { + case SEL_IN: + /* The fd is readable if the receive queue isn't empty. */ + if (skb_peek(&ppp->rcv_q) != NULL) + break; + /* fall through */ + case SEL_EX: + /* Check for exceptions or read errors. */ + /* Is this a pty link and the remote disconnected? */ + if (tty->flags & (1 << TTY_OTHER_CLOSED)) + break; + + /* Is this a local link and the modem disconnected? */ + if (tty_hung_up_p (filp)) + break; + + select_wait(&ppp->read_wait, wait); + result = 0; + break; + + case SEL_OUT: + /* The fd is always writable. */ + break; } - ppp->flags |= SC_XMIT_BUSY; - sti (); -/* - * Send the next block of data to the modem - */ - count = xbuf->count - xbuf->tail; - actual = tty->driver.write (tty, 0, - buf_base (xbuf) + xbuf->tail, count); -/* - * Terminate transmission of any block which may have an error. - * This could occur should the carrier drop. - */ - if (actual < 0) { - ppp->stats.ppp_oerrors++; - actual = count; - } else - ppp->bytes_sent += actual; -/* - * If the buffer has been transmitted then clear the indicators. - */ - xbuf->tail += actual; - if (actual == count) { - xbuf = NULL; - ppp->flags &= ~SC_XMIT_BUSY; -/* - * Complete the transmission on the current buffer. - */ - xbuf = ppp->xbuf; - if (xbuf != NULL) { - tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - xbuf->locked = 0; - ppp->xbuf = NULL; -/* - * If the completed buffer came from the device write, then complete the - * transmission block. - */ - if (ppp2dev (ppp) -> flags & IFF_UP) { - if (xbuf->type == BUFFER_TYPE_DEV_WR) - ppp2dev (ppp)->tbusy = 0; - mark_bh (NET_BH); - } -/* - * Wake up the transmission queue for all completion events. - */ - wake_up_interruptible (&ppp->write_wait); -/* - * Look at the priorities. Choose a daemon write over the device driver. - */ - cli(); - xbuf = ppp->s1buf; - ppp->s1buf = NULL; - if (xbuf == NULL) { - xbuf = ppp->s2buf; - ppp->s2buf = NULL; - } - sti(); -/* - * If there is a pending buffer then transmit it now. - */ - if (xbuf != NULL) { - ppp->flags &= ~SC_XMIT_BUSY; - ppp_kick_tty (ppp, xbuf); - return; - } - } + return result; +} + +#else /* 2.1.23 or later */ + +static unsigned int +ppp_tty_poll(struct tty_struct *tty, struct file *filp, poll_table * wait) +{ + struct ppp *ppp = tty2ppp(tty); + unsigned int mask = 0; + + if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) { + CHECK_PPP(0); + + poll_wait(filp, &ppp->read_wait, wait); + + if (skb_peek(&ppp->rcv_q) != NULL) + mask |= POLLIN | POLLRDNORM; + if (tty->flags & (1 << TTY_OTHER_CLOSED) + || tty_hung_up_p(filp)) + mask |= POLLHUP; + mask |= POLLOUT | POLLWRNORM; } -/* - * Clear the re-entry flag - */ - ppp->flags &= ~SC_XMIT_BUSY; + return mask; } +#endif /* >= 2.1.23 */ /* * This function is called by the tty driver when the transmit buffer has * additional space. It is used by the ppp code to continue to transmit * the current buffer should the buffer have been partially sent. - * - * In addition, it is used to send the first part of the buffer since the - * logic and the inter-locking would be identical. */ - static void ppp_tty_wakeup (struct tty_struct *tty) { - struct ppp_buffer *xbuf; struct ppp *ppp = tty2ppp (tty); + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); if (!ppp) return; - - if (ppp->magic != PPP_MAGIC) + CHECK_PPP_VOID(); + if (tty != ppp->tty) return; -/* - * Ensure that there is a transmission pending. Clear the re-entry flag if - * there is no pending buffer. Otherwise, send the buffer. - */ - xbuf = ppp->xbuf; - if (xbuf == NULL) - tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - else - ppp_tty_wakeup_code (ppp, tty, xbuf); + + if (ppp_tty_push(ppp)) + ppp_output_wakeup(ppp); } /* - * This function is called to transmit a buffer to the remote. The buffer - * is placed on the pending queue if there is presently a buffer being - * sent or it is transmitted with the aid of ppp_tty_wakeup. + * Send a packet to the peer over a synchronous tty line. + * All encoding and FCS are handled by hardware. + * Addr/Ctrl and Protocol field compression implemented. + * Returns -1 iff the packet could not be accepted at present, + * 0 if the packet was accepted but we can't accept another yet, or + * 1 if we can accept another packet immediately. + * If this procedure returns 0, ppp_output_wakeup will be called + * exactly once. */ - -static void -ppp_kick_tty (struct ppp *ppp, struct ppp_buffer *xbuf) +static int +ppp_sync_send(struct ppp *ppp, struct sk_buff *skb) { - register int flags; -/* - * Hold interrupts. - */ - save_flags (flags); - cli (); -/* - * Control the flags which are best performed with the interrupts masked. - */ - xbuf->locked = 1; - xbuf->tail = 0; -/* - * If the transmitter is busy then place the buffer on the appropriate - * priority queue. - */ - if (ppp->xbuf != NULL) { - if (xbuf->type == BUFFER_TYPE_TTY_WR) - ppp->s1buf = xbuf; - else - ppp->s2buf = xbuf; - restore_flags (flags); - return; + unsigned char *data; + int islcp; + + CHECK_PPP(0); + + if (ppp->tpkt != NULL) + return -1; + ppp->tpkt = skb; + + data = ppp->tpkt->data; + + /* + * LCP packets with code values between 1 (configure-reqest) + * and 7 (code-reject) must be sent as though no options + * had been negotiated. + */ + islcp = PPP_PROTOCOL(data) == PPP_LCP + && 1 <= data[PPP_HDRLEN] && data[PPP_HDRLEN] <= 7; + + /* only reset idle time for data packets */ + if (PPP_PROTOCOL(data) < 0x8000) + ppp->last_xmit = jiffies; + ++ppp->stats.ppp_opackets; + ppp->stats.ppp_ooctects += ppp->tpkt->len; + + if ( !(data[2]) && (ppp->flags & SC_COMP_PROT) ) { + /* compress protocol field */ + data[2] = data[1]; + data[1] = data[0]; + skb_pull(ppp->tpkt,1); + data = ppp->tpkt->data; + } + + /* + * Do address/control compression + */ + if ((ppp->flags & SC_COMP_AC) && !islcp + && PPP_ADDRESS(data) == PPP_ALLSTATIONS + && PPP_CONTROL(data) == PPP_UI) { + /* strip addr and control field */ + skb_pull(ppp->tpkt,2); + } + + return ppp_tty_sync_push(ppp); +} + +/* + * Push a synchronous frame out to the tty. + * Returns 1 if frame accepted (or discarded), 0 otherwise. + */ +static int +ppp_tty_sync_push(struct ppp *ppp) +{ + int sent; + struct tty_struct *tty = ppp2tty(ppp); + unsigned long flags; + + CHECK_PPP(0); + + if (ppp->tpkt == NULL) + return 0; + + /* prevent reentrancy with tty_pushing flag */ + save_flags(flags); + cli(); + if (ppp->tty_pushing) { + /* record wakeup attempt so we don't lose */ + /* a wakeup call while doing push processing */ + ppp->woke_up=1; + restore_flags(flags); + return 0; + } + ppp->tty_pushing = 1; + restore_flags(flags); + + if (tty == NULL || tty->disc_data != (void *) ppp) + goto flush; + + for(;;){ + ppp->woke_up=0; + + /* Note: Sync driver accepts complete frame or nothing */ + tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + sent = tty->driver.write(tty, 0, ppp->tpkt->data, ppp->tpkt->len); + if (sent < 0) { + /* write error (possible loss of CD) */ + /* record error and discard current packet */ + ppp->stats.ppp_oerrors++; + break; + } + ppp->stats.ppp_obytes += sent; + if (sent < ppp->tpkt->len) { + /* driver unable to accept frame just yet */ + save_flags(flags); + cli(); + if (ppp->woke_up) { + /* wake up called while processing */ + /* try to send the frame again */ + restore_flags(flags); + continue; + } + /* wait for wakeup callback to try send again */ + ppp->tty_pushing = 0; + restore_flags(flags); + return 0; + } + break; } +flush: + /* done with current packet (sent or discarded) */ + KFREE_SKB(ppp->tpkt); + ppp->tpkt = 0; + ppp->tty_pushing = 0; + return 1; +} + /* - * If the transmitter is not busy then this is the highest priority frame + * Send a packet to the peer over an async tty line. + * Returns -1 iff the packet could not be accepted at present, + * 0 if the packet was accepted but we can't accept another yet, or + * 1 if we can accept another packet immediately. + * If this procedure returns 0, ppp_output_wakeup will be called + * exactly once. */ - ppp->flags &= ~SC_XMIT_BUSY; - ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - ppp->xbuf = xbuf; - restore_flags (flags); +static int +ppp_async_send(struct ppp *ppp, struct sk_buff *skb) +{ + CHECK_PPP(0); + + ppp_tty_push(ppp); + + if (ppp->tpkt != NULL) + return -1; + ppp->tpkt = skb; + ppp->tpkt_pos = 0; + + return ppp_tty_push(ppp); +} + /* - * Do the "tty wakeup_code" to actually send this buffer. + * Push as much data as possible out to the tty. + * Returns 1 if we finished encoding the current frame, 0 otherwise. */ - ppp_tty_wakeup_code (ppp, ppp2tty (ppp), xbuf); +static int +ppp_tty_push(struct ppp *ppp) +{ + int avail, sent, done = 0; + struct tty_struct *tty = ppp2tty(ppp); + + if (ppp->flags & SC_SYNC) + return ppp_tty_sync_push(ppp); + + CHECK_PPP(0); + if (ppp->tty_pushing) { + ppp->woke_up = 1; + return 0; + } + if (tty == NULL || tty->disc_data != (void *) ppp) + goto flush; + while (ppp->optr < ppp->olim || ppp->tpkt != 0) { + ppp->tty_pushing = 1; + mb(); + ppp->woke_up = 0; + avail = ppp->olim - ppp->optr; + if (avail > 0) { + tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + sent = tty->driver.write(tty, 0, ppp->optr, avail); + if (sent < 0) + goto flush; /* error, e.g. loss of CD */ + ppp->stats.ppp_obytes += sent; + ppp->optr += sent; + if (sent < avail) { + wmb(); + ppp->tty_pushing = 0; + mb(); + if (ppp->woke_up) + continue; + return done; + } + } + if (ppp->tpkt != 0) + done = ppp_async_encode(ppp); + wmb(); + ppp->tty_pushing = 0; + } + return done; + +flush: + ppp->tty_pushing = 1; + mb(); + ppp->stats.ppp_oerrors++; + if (ppp->tpkt != 0) { + KFREE_SKB(ppp->tpkt); + ppp->tpkt = 0; + done = 1; + } + ppp->optr = ppp->olim; + wmb(); + ppp->tty_pushing = 0; + return done; } -/************************************************************* - * TTY INPUT - * The following functions handle input that arrives from - * the TTY. It recognizes PPP frames and either hands them - * to the network layer or queues them for delivery to a - * user process reading this TTY. - *************************************************************/ +/* + * Procedure to encode the data for async serial transmission. + * Does octet stuffing (escaping) and address/control + * and protocol compression. + * Assumes ppp->opkt != 0 on entry. + * Returns 1 if we finished the current frame, 0 otherwise. + */ +static int +ppp_async_encode(struct ppp *ppp) +{ + int fcs, i, count, c; + unsigned char *buf, *buflim; + unsigned char *data; + int islcp; + + CHECK_PPP(0); + + buf = ppp->obuf; + ppp->olim = buf; + ppp->optr = buf; + i = ppp->tpkt_pos; + data = ppp->tpkt->data; + count = ppp->tpkt->len; + fcs = ppp->tfcs; + + /* + * LCP packets with code values between 1 (configure-reqest) + * and 7 (code-reject) must be sent as though no options + * had been negotiated. + */ + islcp = PPP_PROTOCOL(data) == PPP_LCP + && 1 <= data[PPP_HDRLEN] && data[PPP_HDRLEN] <= 7; + + if (i == 0) { + /* + * Start of a new packet - insert the leading FLAG + * character if necessary. + */ + if (islcp || flag_time == 0 + || jiffies - ppp->last_xmit >= flag_time) + *buf++ = PPP_FLAG; + /* only reset idle time for data packets */ + if (PPP_PROTOCOL(data) < 0x8000) + ppp->last_xmit = jiffies; + fcs = PPP_INITFCS; + ++ppp->stats.ppp_opackets; + ppp->stats.ppp_ooctects += count; + + /* + * Do address/control compression + */ + if ((ppp->flags & SC_COMP_AC) != 0 && !islcp + && PPP_ADDRESS(data) == PPP_ALLSTATIONS + && PPP_CONTROL(data) == PPP_UI) + i += 2; + } + + /* + * Once we put in the last byte, we need to put in the FCS + * and closing flag, so make sure there is at least 7 bytes + * of free space in the output buffer. + */ + buflim = buf + OBUFSIZE - 6; + while (i < count && buf < buflim) { + c = data[i++]; + if (i == 3 && c == 0 && (ppp->flags & SC_COMP_PROT)) + continue; /* compress protocol field */ + fcs = PPP_FCS(fcs, c); + if (in_xmap(ppp, c) || (islcp && c < 0x20)) { + *buf++ = PPP_ESCAPE; + c ^= 0x20; + } + *buf++ = c; + } + + if (i == count) { + /* + * We have finished the packet. Add the FCS and flag. + */ + fcs = ~fcs; + c = fcs & 0xff; + if (in_xmap(ppp, c) || (islcp && c < 0x20)) { + *buf++ = PPP_ESCAPE; + c ^= 0x20; + } + *buf++ = c; + c = (fcs >> 8) & 0xff; + if (in_xmap(ppp, c) || (islcp && c < 0x20)) { + *buf++ = PPP_ESCAPE; + c ^= 0x20; + } + *buf++ = c; + *buf++ = PPP_FLAG; + ppp->olim = buf; + + KFREE_SKB(ppp->tpkt); + ppp->tpkt = 0; + return 1; + } + + /* + * Remember where we are up to in this packet. + */ + ppp->olim = buf; + ppp->tpkt_pos = i; + ppp->tfcs = fcs; + return 0; +} + +/* + * Flush output from our internal buffers. + * Called for the TCFLSH ioctl. + */ +static void +ppp_tty_flush_output(struct ppp *ppp) +{ + struct sk_buff *skb; + int done = 0; + + while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL) + KFREE_SKB(skb); + ppp->tty_pushing = 1; + mb(); + ppp->optr = ppp->olim; + if (ppp->tpkt != NULL) { + KFREE_SKB(ppp->tpkt); + ppp->tpkt = 0; + done = 1; + } + wmb(); + ppp->tty_pushing = 0; + if (done) + ppp_output_wakeup(ppp); +} /* * Callback function from tty driver. Return the amount of space left * in the receiver's buffer to decide if remote transmitter is to be * throttled. */ - static int ppp_tty_room (struct tty_struct *tty) { @@ -1125,58 +1342,139 @@ ppp_tty_room (struct tty_struct *tty) /* * Callback function when data is available at the tty driver. */ - static void -ppp_tty_receive (struct tty_struct *tty, const u_char * data, +ppp_tty_receive (struct tty_struct *tty, const __u8 * data, char *flags, int count) { register struct ppp *ppp = tty2ppp (tty); - register struct ppp_buffer *buf = NULL; - u_char chr; -/* - * Fetch the pointer to the buffer. Be careful about race conditions. - */ - if (ppp != NULL) - buf = ppp->rbuf; - - if (buf == NULL) + struct sk_buff *skb; + int chr, flg; + unsigned char *p; + + if (ppp != 0) + CHECK_PPP_VOID(); + /* + * This can happen if stuff comes in on the backup tty. + */ + if (ppp == 0 || tty != ppp->tty) return; -/* - * Verify the table pointer and ensure that the line is - * still in PPP discipline. - */ + /* + * Verify the table pointer and ensure that the line is + * still in PPP discipline. + */ if (ppp->magic != PPP_MAGIC) { if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "PPP: handler called but couldn't find " - "PPP struct.\n"); + printk(KERN_DEBUG + "PPP: tty_receive called but couldn't find " + "PPP struct.\n"); return; } - CHECK_PPP_VOID (); -/* - * Print the buffer if desired - */ + /* + * Print the buffer if desired + */ if (ppp->flags & SC_LOG_RAWIN) ppp_print_buffer ("receive buffer", data, count); -/* - * Collect the character and error condition for the character. Set the toss - * flag for the first character error. - */ + + ppp->stats.ppp_ibytes += count; + skb = ppp->rpkt; + + if ( ppp->flags & SC_SYNC ) { + /* synchronous mode */ + + if (ppp->toss==0xE0) { + /* this is the 1st frame, reset vj comp */ + ppp_receive_error(ppp); + ppp->toss = 0; + } + + /* + * Allocate an skbuff for frame. + * The 128 is room for VJ header expansion. + */ + + if (skb == NULL) + skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN); + + if (skb == NULL) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "couldn't " + "alloc skb for recv\n"); + } else { + LIBERATE_SKB(skb); + /* + * Decompress A/C and protocol compression here. + */ + p = skb_put(skb, 2); + p[0] = PPP_ALLSTATIONS; + p[1] = PPP_UI; + if (*data == PPP_ALLSTATIONS) { + data += 2; + count -= 2; + } + if ((*data & 1) != 0) { + p = skb_put(skb, 1); + p[0] = 0; + } + + /* copy frame to socket buffer */ + p = skb_put(skb, count); + memcpy(p,data,count); + + /* + * Check if we've overflowed the MRU + */ + if (skb->len >= ppp->mru + PPP_HDRLEN + 2 + || skb_tailroom(skb) <= 0) { + ++ppp->estats.rx_length_errors; + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "rcv frame too long: " + "len=%d mru=%d hroom=%d troom=%d\n", + skb->len, ppp->mru, skb_headroom(skb), + skb_tailroom(skb)); + } else { + if (!ppp_receive_frame(ppp, skb)) { + KFREE_SKB(skb); + ppp_receive_error(ppp); + } + } + + /* Reset for the next frame */ + skb = NULL; + } + ppp->rpkt = skb; + return; + } + while (count-- > 0) { - ppp->bytes_rcvd++; + /* + * Collect the character and error condition for the character. + * Set the toss flag for the first character error. + */ chr = *data++; if (flags) { - if (*flags && ppp->toss == 0) - ppp->toss = *flags; - ++flags; + flg = *flags++; + if (flg) { + if (ppp->toss == 0) + ppp->toss = flg; + switch (flg) { + case TTY_OVERRUN: + ++ppp->estats.rx_fifo_errors; + break; + case TTY_FRAME: + case TTY_BREAK: + ++ppp->estats.rx_frame_errors; + break; + } + continue; + } } -/* - * Set the flags for 8 data bits and no parity. - * - * Actually, it sets the flags for d7 being 0/1 and parity being even/odd - * so that the normal processing would have all flags set at the end of the - * session. A missing flag bit would denote an error condition. - */ + + /* + * Set the flags for d7 being 0/1 and parity being + * even/odd so that the normal processing would have + * all flags set at the end of the session. A + * missing flag bit indicates an error condition. + */ #ifdef CHECK_CHARACTERS if (chr & 0x80) @@ -1189,256 +1487,619 @@ ppp_tty_receive (struct tty_struct *tty, const u_char * data, else ppp->flags |= SC_RCV_EVNP; #endif -/* - * Branch on the character. Process the escape character. The sequence ESC ESC - * is defined to be ESC. - */ - switch (chr) { - case PPP_ESCAPE: /* PPP_ESCAPE: invert bit in next character */ - ppp->escape = PPP_TRANS; - break; -/* - * FLAG. This is the end of the block. If the block terminated by ESC FLAG, - * then the block is to be ignored. In addition, characters before the very - * first FLAG are also tossed by this procedure. - */ - case PPP_FLAG: /* PPP_FLAG: end of frame */ - ppp->stats.ppp_ibytes += ppp->rbuf->count; + + if (chr == PPP_FLAG) { + /* + * FLAG. This is the end of the block. If the block + * ends with ESC FLAG, then the block is to be ignored. + */ if (ppp->escape) ppp->toss |= 0x80; -/* - * Process frames which are not to be ignored. If the processing failed, - * then clean up the VJ tables. - */ - if ((ppp->toss & 0x80) != 0 || - ppp_doframe (ppp) == 0) { - slhc_toss (ppp->slcomp); + /* + * Process the frame if it was received correctly. + * If there was an error, let the VJ decompressor know. + * There are 4 cases here: + * skb != NULL, toss != 0: error in frame + * skb != NULL, toss == 0: frame ok + * skb == NULL, toss != 0: very first frame, + * error on 1st char, or alloc_skb failed + * skb == NULL, toss == 0: empty frame (~~) + */ + if (ppp->toss || !ppp_receive_frame(ppp, skb)) { + if (ppp->toss && (ppp->flags & SC_DEBUG)) + printk(KERN_DEBUG + "ppp: tossing frame (%x)\n", + ppp->toss); + if (skb != NULL) + KFREE_SKB(skb); + if (!(ppp->toss == 0xE0 || ppp->toss == 0x80)) + ++ppp->stats.ppp_ierrors; + ppp_receive_error(ppp); } -/* - * Reset all indicators for the new frame to follow. - */ - buf->count = 0; - buf->fcs = PPP_INITFCS; + /* + * Reset for the next frame. + */ + skb = NULL; + ppp->rfcs = PPP_INITFCS; ppp->escape = 0; - ppp->toss = 0; - break; -/* - * All other characters in the data come here. If the character is in the - * receive mask then ignore the character. - */ - default: - if (in_rmap (ppp, chr)) - break; -/* - * Adjust the character and if the frame is to be discarded then simply - * ignore the character until the ending FLAG is received. - */ - chr ^= ppp->escape; + ppp->toss = 0; + continue; + } + + /* If we're tossing, look no further. */ + if (ppp->toss != 0) + continue; + + /* If this is a control char to be ignored, do so */ + if (in_rmap(ppp, chr)) + continue; + + /* + * Modify the next character if preceded by escape. + * The escape character (0x7d) could be an escaped + * 0x5d, if it follows an escape :-) + */ + if (ppp->escape) { + chr ^= PPP_TRANS; ppp->escape = 0; + } else if (chr == PPP_ESCAPE) { + ppp->escape = PPP_TRANS; + continue; + } - if (ppp->toss != 0) - break; -/* - * If the count sent is within reason then store the character, bump the - * count, and update the FCS for the character. - */ - if (buf->count < buf->size) { - buf_base (buf)[buf->count++] = chr; - buf->fcs = PPP_FCS (buf->fcs, chr); - break; + /* + * Allocate an skbuff on the first character received. + * The 128 is room for VJ header expansion and FCS. + */ + if (skb == NULL) { + skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN); + if (skb == NULL) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "couldn't " + "alloc skb for recv\n"); + ppp->toss = 1; + continue; } -/* - * The peer sent too much data. Set the flags to discard the current frame - * and wait for the re-synchronization FLAG to be sent. - */ - ppp->stats.ppp_ierrors++; - ppp->toss |= 0xC0; - break; + LIBERATE_SKB(skb); + } + + /* + * Decompress A/C and protocol compression here. + */ + if (skb->len == 0 && chr != PPP_ALLSTATIONS) { + p = skb_put(skb, 2); + p[0] = PPP_ALLSTATIONS; + p[1] = PPP_UI; + } + if (skb->len == 2 && (chr & 1) != 0) { + p = skb_put(skb, 1); + p[0] = 0; + } + + /* + * Check if we've overflowed the MRU + */ + if (skb->len >= ppp->mru + PPP_HDRLEN + 2 + || skb_tailroom(skb) <= 0) { + ++ppp->estats.rx_length_errors; + ppp->toss = 0xC0; + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "rcv frame too long: " + "len=%d mru=%d hroom=%d troom=%d\n", + skb->len, ppp->mru, skb_headroom(skb), + skb_tailroom(skb)); + continue; } + + /* + * Store the character and update the FCS. + */ + p = skb_put(skb, 1); + *p = chr; + ppp->rfcs = PPP_FCS(ppp->rfcs, chr); } + ppp->rpkt = skb; } -/* - * Put the input frame into the networking system for the indicated protocol - */ +/************************************************************* + * PPP NETWORK INTERFACE SUPPORT + * The following code implements the PPP network + * interface device and handles those parts of + * the PPP processing which are independent of the + * type of hardware link being used, including + * VJ and packet compression. + *************************************************************/ -static int -ppp_rcv_rx (struct ppp *ppp, unsigned short proto, u_char * data, int count) -{ - sk_buff *skb = dev_alloc_skb (count); -/* - * Generate a skb buffer for the new frame. - */ - if (skb == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_do_ip: packet dropped on %s (no memory)!\n", - ppp2dev (ppp)->name); - return 0; - } /* - * Move the received data from the input buffer to the skb buffer. + * Network device driver callback routines */ - skb->dev = ppp2dev (ppp); /* We are the device */ -#if USE_SKB_PROTOCOL == 0 - skb->len = count; -#else - skb->protocol = proto; - skb->mac.raw = skb_data(skb); + +static int ppp_init_dev(struct device *dev); +static int ppp_dev_open(struct device *); +static int ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd); +static int ppp_dev_close(struct device *); +static int ppp_dev_xmit(struct sk_buff *, struct device *); +static struct net_device_stats *ppp_dev_stats (struct device *); + +#if LINUX_VERSION_CODE < VERSION(2,1,15) +static int ppp_dev_header(struct sk_buff *, struct device *, __u16, + void *, void *, unsigned int); +static int ppp_dev_rebuild(void *eth, struct device *dev, + unsigned long raddr, struct sk_buff *skb); #endif - memcpy (skb_put(skb,count), data, count); /* move data */ -/* - * Tag the frame and kick it to the proper receive routine - */ - skb->free = 1; - ppp->ddinfo.recv_idle = jiffies; - netif_rx (skb); - return 1; -} /* - * Process the receipt of an IP frame + * Information for the protocol decoder */ -static int -rcv_proto_ip (struct ppp *ppp, unsigned short proto, u_char * data, int count) -{ - if ((ppp2dev (ppp)->flags & IFF_UP) && (count > 0)) - return ppp_rcv_rx (ppp, htons (ETH_P_IP), data, count); - return 0; -} +typedef int (*pfn_proto) (struct ppp *, struct sk_buff *); + +typedef struct ppp_proto_struct { + int proto; + pfn_proto func; +} ppp_proto_type; + +static int rcv_proto_ip (struct ppp *, struct sk_buff *); +static int rcv_proto_ipv6 (struct ppp *, struct sk_buff *); +static int rcv_proto_ipx (struct ppp *, struct sk_buff *); +static int rcv_proto_at (struct ppp *, struct sk_buff *); +static int rcv_proto_vjc_comp (struct ppp *, struct sk_buff *); +static int rcv_proto_vjc_uncomp (struct ppp *, struct sk_buff *); +static int rcv_proto_ccp (struct ppp *, struct sk_buff *); +static int rcv_proto_unknown (struct ppp *, struct sk_buff *); + +static +ppp_proto_type proto_list[] = { + { PPP_IP, rcv_proto_ip }, + { PPP_IPV6, rcv_proto_ipv6 }, + { PPP_IPX, rcv_proto_ipx }, + { PPP_AT, rcv_proto_at }, + { PPP_VJC_COMP, rcv_proto_vjc_comp }, + { PPP_VJC_UNCOMP, rcv_proto_vjc_uncomp }, + { PPP_CCP, rcv_proto_ccp }, + { 0, rcv_proto_unknown } /* !!! MUST BE LAST !!! */ +}; /* - * Process the receipt of an IPX frame + * Called when the PPP network interface device is actually created. */ - static int -rcv_proto_ipx (struct ppp *ppp, unsigned short proto, u_char * data, int count) +ppp_init_dev (struct device *dev) { -#if defined(NEW_SKBUFF) || defined(IPX_CHANGE) - if (((ppp2dev (ppp)->flags & IFF_UP) != 0) && (count > 0)) - return ppp_rcv_rx (ppp, htons (ETH_P_IPX), data, count); + dev->hard_header_len = PPP_HDRLEN; +#if LINUX_VERSION_CODE < VERSION(2,1,15) + dev->hard_header = ppp_dev_header; + dev->rebuild_header = ppp_dev_rebuild; #endif - return 0; -} -/* - * Process the receipt of an VJ Compressed frame - */ + /* device INFO */ + dev->mtu = PPP_MTU; + dev->hard_start_xmit = ppp_dev_xmit; + dev->open = ppp_dev_open; + dev->stop = ppp_dev_close; + dev->get_stats = ppp_dev_stats; + dev->do_ioctl = ppp_dev_ioctl; + dev->addr_len = 0; + dev->tx_queue_len = 10; + dev->type = ARPHRD_PPP; + +#if LINUX_VERSION_CODE < VERSION(2,1,20) + { + int indx; -static int -rcv_proto_vjc_comp (struct ppp *ppp, unsigned short proto, - u_char *data, int count) -{ - if ((ppp->flags & SC_REJ_COMP_TCP) == 0) { - int new_count = slhc_uncompress (ppp->slcomp, data, count); - if (new_count >= 0) { - return rcv_proto_ip (ppp, PPP_IP, data, new_count); - } - if (ppp->flags & SC_DEBUG) - printk (KERN_NOTICE - "ppp: error in VJ decompression\n"); + for (indx = 0; indx < DEV_NUMBUFFS; indx++) + skb_queue_head_init (&dev->buffs[indx]); } +#else + dev_init_buffers(dev); +#endif + + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + return 0; } /* - * Process the receipt of an VJ Un-compressed frame + * Callback from the network layer when the device goes up. */ static int -rcv_proto_vjc_uncomp (struct ppp *ppp, unsigned short proto, - u_char *data, int count) +ppp_dev_open (struct device *dev) { - if ((ppp->flags & SC_REJ_COMP_TCP) == 0) { - if (slhc_remember (ppp->slcomp, data, count) > 0) { - return rcv_proto_ip (ppp, PPP_IP, data, count); - } - if (ppp->flags & SC_DEBUG) - printk (KERN_NOTICE - "ppp: error in VJ memorizing\n"); + struct ppp *ppp = dev2ppp(dev); + + if (!ppp->inuse || ppp2tty(ppp) == NULL) { + printk(KERN_ERR "ppp: %s not active\n", dev->name); + return -ENXIO; } + + MOD_INC_USE_COUNT; + return 0; } /* - * Receive all unclassified protocols. + * Callback from the network layer when the ppp device goes down. */ static int -rcv_proto_unknown (struct ppp *ppp, unsigned short proto, - u_char *data, int len) +ppp_dev_close (struct device *dev) { - int totlen; - register int current_idx; - -#define PUTC(c) \ -{ \ - buf_base (ppp->ubuf) [current_idx++] = (u_char) (c); \ - current_idx &= ppp->ubuf->size; \ - if (current_idx == ppp->ubuf->tail) \ - goto failure; \ -} + struct ppp *ppp = dev2ppp (dev); + + CHECK_PPP_MAGIC(ppp); + + MOD_DEC_USE_COUNT; + + return 0; +} + +static inline void +get_vj_stats(struct vjstat *vj, struct slcompress *slc) +{ + vj->vjs_packets = slc->sls_o_compressed + slc->sls_o_uncompressed; + vj->vjs_compressed = slc->sls_o_compressed; + vj->vjs_searches = slc->sls_o_searches; + vj->vjs_misses = slc->sls_o_misses; + vj->vjs_errorin = slc->sls_i_error; + vj->vjs_tossed = slc->sls_i_tossed; + vj->vjs_uncompressedin = slc->sls_i_uncompressed; + vj->vjs_compressedin = slc->sls_i_compressed; +} /* - * The total length includes the protocol data. - * Lock the user information buffer. + * Callback from the network layer to process the sockioctl functions. */ - if (set_bit (0, &ppp->ubuf->locked)) { - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_us_queue: can't get lock\n"); - } else { - current_idx = ppp->ubuf->head; +static int +ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd) +{ + struct ppp *ppp = dev2ppp(dev); + int nb; + union { + struct ppp_stats stats; + struct ppp_comp_stats cstats; + char vers[32]; + } u; + + CHECK_PPP_MAGIC(ppp); + + memset(&u, 0, sizeof(u)); + switch (cmd) { + case SIOCGPPPSTATS: + u.stats.p = ppp->stats; + if (ppp->slcomp != NULL) + get_vj_stats(&u.stats.vj, ppp->slcomp); + nb = sizeof(u.stats); + break; + + case SIOCGPPPCSTATS: + if (ppp->sc_xc_state != NULL) + (*ppp->sc_xcomp->comp_stat) + (ppp->sc_xc_state, &u.cstats.c); + if (ppp->sc_rc_state != NULL) + (*ppp->sc_rcomp->decomp_stat) + (ppp->sc_rc_state, &u.cstats.d); + nb = sizeof(u.cstats); + break; + + case SIOCGPPPVER: + strcpy(u.vers, szVersion); + nb = strlen(u.vers) + 1; + break; + + default: + return -EINVAL; + } + + if (COPY_TO_USER((void *) ifr->ifr_ifru.ifru_data, &u, nb)) + return -EFAULT; + return 0; +} + /* - * Insert the buffer length (not counted), the protocol, and the data + * Process the generic PPP ioctls, i.e. those which are not specific + * to any particular type of hardware link. */ - totlen = len + 2; - PUTC (totlen >> 8); - PUTC (totlen); +static int +ppp_ioctl(struct ppp *ppp, unsigned int param2, unsigned long param3) +{ + register int temp_i = 0, oldflags; + int error = -EFAULT; + unsigned long flags; + struct ppp_idle cur_ddinfo; + struct npioctl npi; + + CHECK_PPP(-ENXIO); + + /* + * The user must have an euid of root to do these requests. + */ + if (!SUSER()) + return -EPERM; + + switch (param2) { + case PPPIOCSMRU: + /* + * Set the MRU value + */ + if (GET_USER(temp_i, (int *) param3)) + break; + if (temp_i < PPP_MRU) + temp_i = PPP_MRU; + ppp->mru = temp_i; + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO + "ppp_ioctl: set mru to %x\n", temp_i); + error = 0; + break; + + case PPPIOCGFLAGS: + /* + * Fetch the current flags + */ + temp_i = ppp->flags & SC_MASK; +#ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */ + temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 | + SC_RCV_ODDP | SC_RCV_EVNP; +#endif + if (PUT_USER(temp_i, (int *) param3)) + break; + error = 0; + break; + + case PPPIOCSFLAGS: + /* + * Set the flags for the various options + */ + if (GET_USER(temp_i, (int *) param3)) + break; + + if (ppp->flags & ~temp_i & SC_CCP_OPEN) + ppp_ccp_closed(ppp); + + save_flags(flags); + cli(); + oldflags = ppp->flags; + temp_i = (temp_i & SC_MASK) | (oldflags & ~SC_MASK); + ppp->flags = temp_i; + restore_flags(flags); + + if ((oldflags | temp_i) & SC_DEBUG) + printk(KERN_INFO + "ppp_ioctl: set flags to %x\n", temp_i); + error = 0; + break; + + case PPPIOCSCOMPRESS: + /* + * Set the compression mode + */ + error = ppp_set_compression + (ppp, (struct ppp_option_data *) param3); + break; + + case PPPIOCGUNIT: + /* + * Obtain the unit number for this device. + */ + if (PUT_USER(ppp->line, (int *) param3)) + break; + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO + "ppp_ioctl: get unit: %d\n", ppp->line); + error = 0; + break; + + case PPPIOCSDEBUG: + /* + * Set the debug level + */ + if (GET_USER(temp_i, (int *) param3)) + break; + temp_i = (temp_i & 0x1F) << 16; + + if ((ppp->flags | temp_i) & SC_DEBUG) + printk(KERN_INFO + "ppp_ioctl: set dbg flags to %x\n", temp_i); + + save_flags(flags); + cli(); + ppp->flags = (ppp->flags & ~0x1F0000) | temp_i; + restore_flags(flags); + error = 0; + break; + + case PPPIOCGDEBUG: + /* + * Get the debug level + */ + temp_i = (ppp->flags >> 16) & 0x1F; + if (PUT_USER(temp_i, (int *) param3)) + break; + error = 0; + break; + + case PPPIOCGIDLE: + /* + * Get the times since the last send/receive frame operation + */ + /* change absolute times to relative times. */ + cur_ddinfo.xmit_idle = (jiffies - ppp->last_xmit) / HZ; + cur_ddinfo.recv_idle = (jiffies - ppp->last_recv) / HZ; + if (COPY_TO_USER((void *) param3, &cur_ddinfo, + sizeof (cur_ddinfo))) + break; + error = 0; + break; + + case PPPIOCSMAXCID: + /* + * Set the maximum VJ header compression slot number. + */ + if (GET_USER(temp_i, (int *) param3)) + break; + error = -EINVAL; + if (temp_i < 2 || temp_i > 255) + break; + ++temp_i; + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO "ppp_ioctl: set maxcid to %d\n", + temp_i); + if (ppp->slcomp != NULL) + slhc_free(ppp->slcomp); + ppp->slcomp = slhc_init(16, temp_i); + + error = -ENOMEM; + if (ppp->slcomp == NULL) { + printk(KERN_ERR "ppp: no memory for VJ compression\n"); + break; + } + error = 0; + break; - PUTC (proto >> 8); - PUTC (proto); + case PPPIOCGNPMODE: + case PPPIOCSNPMODE: + if (COPY_FROM_USER(&npi, (void *) param3, sizeof(npi))) + break; - totlen -= 2; - while (totlen-- > 0) { - PUTC (*data++); + switch (npi.protocol) { + case PPP_IP: + npi.protocol = NP_IP; + break; + case PPP_IPX: + npi.protocol = NP_IPX; + break; + case PPP_AT: + npi.protocol = NP_AT; + break; + default: + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "pppioc[gs]npmode: " + "invalid proto %d\n", npi.protocol); + error = -EINVAL; + goto out; } -#undef PUTC -/* - * The frame is complete. Update the head pointer and wakeup the pppd - * process. - */ - ppp->ubuf->head = current_idx; - clear_bit (0, &ppp->ubuf->locked); - wake_up_interruptible (&ppp->read_wait); - if (ppp->tty->fasync != NULL) - kill_fasync (ppp->tty->fasync, SIGIO); + if (param2 == PPPIOCGNPMODE) { + npi.mode = ppp->sc_npmode[npi.protocol]; + if (COPY_TO_USER((void *) param3, &npi, sizeof(npi))) + break; + } else { + ppp->sc_npmode[npi.protocol] = npi.mode; + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "ppp: set np %d to %d\n", + npi.protocol, npi.mode); + mark_bh(NET_BH); + } + error = 0; + break; + default: + /* + * All other ioctl() events will come here. + */ if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp: successfully queued %d bytes, flags = %x\n", - len + 2, ppp->flags); + printk(KERN_ERR + "ppp_ioctl: invalid ioctl: %x, addr %lx\n", + param2, param3); + + error = -ENOIOCTLCMD; + break; + } +out: + return error; +} - return 1; /* - * The buffer is full. Unlock the header + * Process the set-compression ioctl. */ -failure: - clear_bit (0, &ppp->ubuf->locked); +static int +ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp) +{ + struct compressor *cp; + int error, nb; + unsigned long flags; + __u8 *ptr; + __u8 ccp_option[CCP_MAX_OPTION_LENGTH]; + struct ppp_option_data data; + + /* + * Fetch the compression parameters + */ + error = -EFAULT; + if (COPY_FROM_USER(&data, odp, sizeof (data))) + goto out; + + nb = data.length; + ptr = data.ptr; + if ((unsigned) nb >= CCP_MAX_OPTION_LENGTH) + nb = CCP_MAX_OPTION_LENGTH; + + if (COPY_FROM_USER(ccp_option, ptr, nb)) + goto out; + + error = -EINVAL; + if (ccp_option[1] < 2) /* preliminary check on the length byte */ + goto out; + + save_flags(flags); + cli(); + ppp->flags &= ~(data.transmit? SC_COMP_RUN: SC_DECOMP_RUN); + restore_flags(flags); + + cp = find_compressor (ccp_option[0]); +#if defined(CONFIG_KMOD) || defined(CONFIG_KERNELD) + if (cp == NULL) { + char modname[32]; + sprintf(modname, "ppp-compress-%d", ccp_option[0]); + request_module(modname); + cp = find_compressor(ccp_option[0]); + } +#endif /* CONFIG_KMOD */ + + if (cp == NULL) { if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_us_queue: ran out of buffer space.\n"); + printk(KERN_DEBUG + "%s: no compressor for [%x %x %x], %x\n", + ppp->name, ccp_option[0], ccp_option[1], + ccp_option[2], nb); + goto out; /* compressor not loaded */ + } + + /* + * Found a handler for the protocol - try to allocate + * a compressor or decompressor. + */ + error = 0; + if (data.transmit) { + if (ppp->sc_xc_state != NULL) + (*ppp->sc_xcomp->comp_free)(ppp->sc_xc_state); + ppp->sc_xc_state = NULL; + + ppp->sc_xcomp = cp; + ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb); + if (ppp->sc_xc_state == NULL) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: comp_alloc failed\n", + ppp->name); + error = -ENOBUFS; + } + } else { + if (ppp->sc_rc_state != NULL) + (*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state); + ppp->sc_rc_state = NULL; + + ppp->sc_rcomp = cp; + ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb); + if (ppp->sc_rc_state == NULL) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: decomp_alloc failed\n", + ppp->name); + error = -ENOBUFS; + } } -/* - * Discard the frame. There are no takers for this protocol. - */ - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp: dropping packet on the floor.\n"); - slhc_toss (ppp->slcomp); - return 0; +out: + return error; } /* @@ -1449,23 +2110,29 @@ failure: * immediate or the compressors will become confused on the peer. */ -static void ppp_proto_ccp (struct ppp *ppp, u_char *dp, int len, int rcvd) +static void ppp_proto_ccp(struct ppp *ppp, __u8 *dp, int len, int rcvd) { int slen = CCP_LENGTH(dp); - u_char *opt = dp + CCP_HDRLEN; + __u8 *opt = dp + CCP_HDRLEN; int opt_len = slen - CCP_HDRLEN; + unsigned long flags; if (slen > len) return; + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "ppp_proto_ccp rcvd=%d code=%x flags=%x\n", + rcvd, CCP_CODE(dp), ppp->flags); + save_flags(flags); switch (CCP_CODE(dp)) { case CCP_CONFREQ: case CCP_TERMREQ: case CCP_TERMACK: -/* - * CCP must be going down - disable compression - */ + /* + * CCP must be going down - disable compression + */ if (ppp->flags & SC_CCP_UP) { + cli(); ppp->flags &= ~(SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN); @@ -1481,1937 +2148,1021 @@ static void ppp_proto_ccp (struct ppp *ppp, u_char *dp, int len, int rcvd) break; if (slen < (CCP_OPT_LENGTH (opt) + CCP_HDRLEN)) break; -/* - * we're agreeing to send compressed packets. - */ if (!rcvd) { + /* + * we're agreeing to send compressed packets. + */ if (ppp->sc_xc_state == NULL) break; if ((*ppp->sc_xcomp->comp_init) (ppp->sc_xc_state, - opt, - opt_len, - ppp2dev (ppp)->base_addr, - 0, - ppp->flags)) + opt, opt_len, + ppp->line, 0, ppp->flags & SC_DEBUG)) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: comp running\n", + ppp->name); + cli(); ppp->flags |= SC_COMP_RUN; + } break; } -/* - * peer is agreeing to send compressed packets. - */ + + /* + * peer is agreeing to send compressed packets. + */ if (ppp->sc_rc_state == NULL) break; if ((*ppp->sc_rcomp->decomp_init) - (ppp->sc_rc_state, - opt, - opt_len, - ppp2dev (ppp)->base_addr, - 0, - ppp->mru, - ppp->flags)) { - ppp->flags |= SC_DECOMP_RUN; - ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR); - } - break; -/* - * The protocol sequence is complete at this end - */ - case CCP_RESETACK: - if ((ppp->flags & SC_CCP_UP) == 0) - break; - - if (!rcvd) { - if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN)) - (*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state); - } else { - if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) { - (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state); - ppp->flags &= ~SC_DC_ERROR; - } - } - break; - } -} - -static int -rcv_proto_ccp (struct ppp *ppp, unsigned short proto, u_char *dp, int len) -{ - ppp_proto_ccp (ppp, dp, len, 1); - return rcv_proto_unknown (ppp, proto, dp, len); -} - -/* - * Handle a LQR packet. - * - * The LQR packet is passed along to the pppd process just like any - * other PPP frame. The difference is that some processing needs to be - * performed to append the current data to the end of the frame. - */ - -static int -rcv_proto_lqr (struct ppp *ppp, unsigned short proto, u_char * data, int len) -{ -#if 0 /* until support is in the pppd process don't corrupt the reject. */ - register u_char *p; - if (len > 8) { - if (len < 48) - memset (&data [len], '\0', 48 - len); -/* - * Fill in the fields from the driver data - */ - p = &data [48]; - p = store_long (p, ++ppp->stats.ppp_ilqrs); - p = store_long (p, ppp->stats.ppp_ipackets); - p = store_long (p, ppp->stats.ppp_discards); - p = store_long (p, ppp->stats.ppp_ierrors); - p = store_long (p, ppp->stats.ppp_ioctects + len); - - len = 68; - } -#endif -/* - * Pass the frame to the pppd daemon. - */ - return rcv_proto_unknown (ppp, proto, data, len); -} - -/* on entry, a received frame is in ppp->rbuf.bufr - check it and dispose as appropriate */ - -static void ppp_doframe_lower (struct ppp *ppp, u_char *data, int count) -{ - u_short proto = PPP_PROTOCOL (data); - ppp_proto_type *proto_ptr; -/* - * Ignore empty frames - */ - if (count <= 4) - return; -/* - * Count the frame and print it - */ - ++ppp->stats.ppp_ipackets; - if (ppp->flags & SC_LOG_INPKT) - ppp_print_buffer ("receive frame", data, count); -/* - * Find the procedure to handle this protocol. The last one is marked - * as a protocol 0 which is the 'catch-all' to feed it to the pppd daemon. - */ - proto_ptr = proto_list; - while (proto_ptr->proto != 0 && proto_ptr->proto != proto) - ++proto_ptr; -/* - * Update the appropriate statistic counter. - */ - if ((*proto_ptr->func) (ppp, proto, - &data[PPP_HARD_HDR_LEN], - count - PPP_HARD_HDR_LEN)) - ppp->stats.ppp_ioctects += count; - else - ++ppp->stats.ppp_discards; -} - -/* on entry, a received frame is in ppp->rbuf.bufr - check it and dispose as appropriate */ - -static int -ppp_doframe (struct ppp *ppp) -{ - u_char *data = buf_base (ppp->rbuf); - int count = ppp->rbuf->count; - int addr, ctrl, proto; - int new_count; - u_char *new_data; -/* - * If there is a pending error from the receiver then log it and discard - * the damaged frame. - */ - if (ppp->toss) { - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp_toss: tossing frame, reason = %d\n", - ppp->toss); - ppp->stats.ppp_ierrors++; - return 0; - } -/* - * An empty frame is ignored. This occurs if the FLAG sequence precedes and - * follows each frame. - */ - if (count == 0) - return 1; -/* - * Generate an error if the frame is too small. - */ - if (count < PPP_HARD_HDR_LEN) { - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp: got runt ppp frame, %d chars\n", count); - slhc_toss (ppp->slcomp); - ppp->stats.ppp_ierrors++; - return 1; - } -/* - * Verify the CRC of the frame and discard the CRC characters from the - * end of the buffer. - */ - if (ppp->rbuf->fcs != PPP_GOODFCS) { - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp: frame with bad fcs, excess = %x\n", - ppp->rbuf->fcs ^ PPP_GOODFCS); - ppp->stats.ppp_ierrors++; - return 0; - } - count -= 2; /* ignore the fcs characters */ -/* - * Ignore the leading ADDRESS and CONTROL fields in the frame. - */ - addr = PPP_ALLSTATIONS; - ctrl = PPP_UI; - - if ((data[0] == PPP_ALLSTATIONS) && (data[1] == PPP_UI)) { - data += 2; - count -= 2; - } -/* - * Obtain the protocol from the frame - */ - proto = (u_short) *data++; - if ((proto & 1) == 0) { - proto = (proto << 8) | (u_short) *data++; - --count; - } -/* - * Rewrite the header with the full information. This may encroach upon - * the 'filler' area in the buffer header. This is the purpose for the - * filler. - */ - *(--data) = proto; - *(--data) = proto >> 8; - *(--data) = ctrl; - *(--data) = addr; - count += 3; -/* - * Process the active decompressor. - */ - if ((ppp->sc_rc_state != (void *) 0) && - (ppp->flags & SC_DECOMP_RUN) && - ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) { - if (proto == PPP_COMP) { -/* - * If the frame is compressed then decompress it. - */ - new_data = kmalloc (ppp->mru + 4, GFP_ATOMIC); - if (new_data == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_doframe: no memory\n"); - slhc_toss (ppp->slcomp); - (*ppp->sc_rcomp->incomp) (ppp->sc_rc_state, - data, - count); - return 1; - } -/* - * Decompress the frame - */ - new_count = bsd_decompress (ppp->sc_rc_state, - data, - count, - new_data, - ppp->mru + 4); - switch (new_count) { - default: - ppp_doframe_lower (ppp, new_data, new_count); - kfree (new_data); - return 1; - - case DECOMP_OK: - break; - - case DECOMP_ERROR: - ppp->flags |= SC_DC_ERROR; - break; - - case DECOMP_FATALERROR: - ppp->flags |= SC_DC_FERROR; - break; - } -/* - * Log the error condition and discard the frame. - */ - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_proto_comp: " - "decompress err %d\n", new_count); - kfree (new_data); - slhc_toss (ppp->slcomp); - return 1; - } -/* - * The frame is not special. Pass it through the compressor without - * actually compressing the data - */ - (*ppp->sc_rcomp->incomp) (ppp->sc_rc_state, - data, - count); - } -/* - * Process the uncompressed frame. - */ - ppp_doframe_lower (ppp, data, count); - return 1; -} - -/************************************************************* - * LINE DISCIPLINE SUPPORT - * The following functions form support user programs - * which read and write data on a TTY with the PPP line - * discipline. Reading is done from a circular queue, - * filled by the lower TTY levels. - *************************************************************/ - -/* read a PPP frame from the us_rbuff circular buffer, - waiting if necessary -*/ - -static int -ppp_tty_read (struct tty_struct *tty, struct file *file, u_char * buf, - unsigned int nr) -{ - struct ppp *ppp = tty2ppp (tty); - u_char c; - int len, indx; - -#define GETC(c) \ -{ \ - c = buf_base (ppp->ubuf) [ppp->ubuf->tail++]; \ - ppp->ubuf->tail &= ppp->ubuf->size; \ -} - -/* - * Validate the pointer to the PPP structure - */ - if (!ppp) - return -EIO; - - if (ppp->magic != PPP_MAGIC) - return -EIO; - - CHECK_PPP (-ENXIO); - - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_tty_read: called buf=%p nr=%u\n", - buf, nr); -/* - * Acquire the read lock. - */ - for (;;) { - ppp = tty2ppp (tty); - if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse) - return 0; - - if (set_bit (0, &ppp->ubuf->locked) != 0) { - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_tty_read: sleeping(ubuf)\n"); - - current->timeout = 0; - current->state = TASK_INTERRUPTIBLE; - schedule (); - - if (current->signal & ~current->blocked) - return -EINTR; - continue; - } -/* - * Before we attempt to write the frame to the user, ensure that the - * user has access to the pages for the total buffer length. - */ - indx = verify_area (VERIFY_WRITE, buf, nr); - if (indx != 0) - return (indx); -/* - * Fetch the length of the buffer from the first two bytes. - */ - if (ppp->ubuf->head == ppp->ubuf->tail) - len = 0; - else { - GETC (c); - len = c << 8; - GETC (c); - len += c; - } -/* - * If there is no length then wait for the data to arrive. - */ - if (len == 0) { - /* no data */ - clear_bit (0, &ppp->ubuf->locked); - if (file->f_flags & O_NONBLOCK) { - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_tty_read: no data " - "(EWOULDBLOCK)\n"); - return -EWOULDBLOCK; - } - current->timeout = 0; - - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_tty_read: sleeping(read_wait)\n"); - - interruptible_sleep_on (&ppp->read_wait); - if (current->signal & ~current->blocked) - return -EINTR; - continue; - } -/* - * Reset the time of the last read operation. - */ - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG "ppp_tty_read: len = %d\n", len); -/* - * Ensure that the frame will fit within the caller's buffer. If not, then - * discard the frame from the input buffer and return an error to the caller. - */ - if (len + 2 > nr) { - /* Can't copy it, update us_rbuff_head */ - - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp: read of %u bytes too small for %d " - "frame\n", nr, len + 2); - ppp->ubuf->tail += len; - ppp->ubuf->tail &= ppp->ubuf->size; - clear_bit (0, &ppp->ubuf->locked); - ppp->stats.ppp_ierrors++; - return -EOVERFLOW; - } -/* - * Before we attempt to write the frame to the user, ensure that the - * page tables are proper. - */ - indx = verify_area (VERIFY_WRITE, buf, len + 2); - if (indx != 0) { - ppp->ubuf->tail += len; - ppp->ubuf->tail &= ppp->ubuf->size; - clear_bit (0, &ppp->ubuf->locked); - return (indx); - } -/* - * Fake the insertion of the ADDRESS and CONTROL information because these - * were not saved in the buffer. - */ - put_byte_user (PPP_ALLSTATIONS, buf++); - put_byte_user (PPP_UI, buf++); - - indx = len; -/* - * Copy the received data from the buffer to the caller's area. - */ - while (indx-- > 0) { - GETC (c); - put_byte_user (c, buf); - ++buf; - } -/* - * Release the lock and return the character count in the buffer area. - */ - clear_bit (0, &ppp->ubuf->locked); - len += 2; /* Account for ADDRESS and CONTROL bytes */ - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_tty_read: passing %d bytes up\n", len); - return len; - } -#undef GETC -} - -/* stuff a character into the transmit buffer, using PPP's way of escaping - special characters. - also, update fcs to take account of new character */ - -extern inline void -ppp_stuff_char (struct ppp *ppp, register struct ppp_buffer *buf, - register u_char chr) -{ -/* - * The buffer should not be full. - */ - if (ppp->flags & SC_DEBUG) { - if ((buf->count < 0) || (buf->count > 3000)) - printk (KERN_DEBUG "ppp_stuff_char: %x %d\n", - (unsigned int) buf->count, - (unsigned int) chr); - } -/* - * Update the FCS and if the character needs to be escaped, do it. - */ - buf->fcs = PPP_FCS (buf->fcs, chr); - if (in_xmap (ppp, chr)) { - chr ^= PPP_TRANS; - ins_char (buf, PPP_ESCAPE); - } -/* - * Add the character to the buffer. - */ - ins_char (buf, chr); -} - -/* - * Procedure to encode the data with the proper escapement and send the - * data to the remote system. - */ - -static void -ppp_dev_xmit_lower (struct ppp *ppp, struct ppp_buffer *buf, - u_char *data, int count, int non_ip) -{ - unsigned short int write_fcs; - int address, control; - int proto; -/* - * Insert the leading FLAG character - */ - buf->count = 0; - - if (non_ip || flag_time == 0) - ins_char (buf, PPP_FLAG); - else { - if (jiffies - ppp->last_xmit > flag_time) - ins_char (buf, PPP_FLAG); - } - ppp->last_xmit = jiffies; - buf->fcs = PPP_INITFCS; -/* - * Emit the address/control information if needed - */ - address = PPP_ADDRESS (data); - control = PPP_CONTROL (data); - proto = PPP_PROTOCOL (data); - - if (address != PPP_ALLSTATIONS || - control != PPP_UI || - (ppp->flags & SC_COMP_AC) == 0) { - ppp_stuff_char (ppp, buf, address); - ppp_stuff_char (ppp, buf, control); - } -/* - * Emit the protocol (compressed if possible) - */ - if ((ppp->flags & SC_COMP_PROT) == 0 || (proto & 0xFF00)) - ppp_stuff_char (ppp, buf, proto >> 8); - - ppp_stuff_char (ppp, buf, proto); -/* - * Insert the data - */ - data += 4; - count -= 4; - - while (count-- > 0) - ppp_stuff_char (ppp, buf, *data++); -/* - * Add the trailing CRC and the final flag character - */ - write_fcs = buf->fcs ^ 0xFFFF; - ppp_stuff_char (ppp, buf, write_fcs); - ppp_stuff_char (ppp, buf, write_fcs >> 8); - - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG "ppp_dev_xmit_lower: fcs is %hx\n", - write_fcs); -/* - * Add the trailing flag character - */ - ins_char (buf, PPP_FLAG); -/* - * Print the buffer - */ - if (ppp->flags & SC_LOG_FLUSH) - ppp_print_buffer ("ppp flush", buf_base (buf), - buf->count); - else { - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_dev_xmit: writing %d chars\n", - buf->count); - } -/* - * Send the block to the tty driver. - */ - ppp->stats.ppp_obytes += buf->count; - ppp_kick_tty (ppp, buf); -} - -/* - * Send an frame to the remote with the proper bsd compression. - * - * Return 0 if frame was queued for transmission. - * 1 if frame must be re-queued for later driver support. - */ - -static int -ppp_dev_xmit_frame (struct ppp *ppp, struct ppp_buffer *buf, - u_char *data, int count) -{ - int proto; - int address, control; - u_char *new_data; - int new_count; -/* - * Print the buffer - */ - if (ppp->flags & SC_LOG_OUTPKT) - ppp_print_buffer ("write frame", data, count); -/* - * Determine if the frame may be compressed. Attempt to compress the - * frame if possible. - */ - proto = PPP_PROTOCOL (data); - address = PPP_ADDRESS (data); - control = PPP_CONTROL (data); - - if (((ppp->flags & SC_COMP_RUN) != 0) && - (ppp->sc_xc_state != (void *) 0) && - (address == PPP_ALLSTATIONS) && - (control == PPP_UI) && - (proto != PPP_LCP) && - (proto != PPP_CCP)) { - new_data = kmalloc (count, GFP_ATOMIC); - if (new_data == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_dev_xmit_frame: no memory\n"); - return 1; - } - - new_count = bsd_compress (ppp->sc_xc_state, - data, - new_data, - count, - count); - - if (new_count > 0) { - ++ppp->stats.ppp_opackets; - ppp->stats.ppp_ooctects += new_count; - - ppp_dev_xmit_lower (ppp, buf, new_data, - new_count, 0); - kfree (new_data); - return 0; - } -/* - * The frame could not be compressed. - */ - kfree (new_data); - } -/* - * The frame may not be compressed. Update the statistics before the - * count field is destroyed. The frame will be transmitted. - */ - ++ppp->stats.ppp_opackets; - ppp->stats.ppp_ooctects += count; -/* - * Go to the escape encoding - */ - ppp_dev_xmit_lower (ppp, buf, data, count, !!(proto & 0xFF00)); - return 0; -} - -/* - * Revise the tty frame for specific protocols. - */ - -static int -send_revise_frame (register struct ppp *ppp, u_char *data, int len) -{ - u_char *p; - - switch (PPP_PROTOCOL (data)) { -/* - * Update the LQR frame with the current MIB information. This saves having - * the daemon read old MIB data from the driver. - */ - case PPP_LQR: - len = 48; /* total size of this frame */ - p = (u_char *) &data [40]; /* Point to last two items. */ - p = store_long (p, ppp->stats.ppp_opackets + 1); - p = store_long (p, ppp->stats.ppp_ooctects + len); - break; -/* - * Outbound compression frames - */ - case PPP_CCP: - ppp_proto_ccp (ppp, - data + PPP_HARD_HDR_LEN, - len - PPP_HARD_HDR_LEN, - 0); - break; -/* - * All other frame types - */ - default: - break; - } - - return len; -} - -/* - * write a frame with NR chars from BUF to TTY - * we have to put the FCS field on ourselves - */ - -static int -ppp_tty_write (struct tty_struct *tty, struct file *file, const u_char * data, - unsigned int count) -{ - struct ppp *ppp = tty2ppp (tty); - u_char *new_data; - int status; -/* - * Verify the pointer to the PPP data and that the tty is still in PPP mode. - */ - if (!ppp) - return -EIO; - - if (ppp->magic != PPP_MAGIC) - return -EIO; - - CHECK_PPP (-ENXIO); -/* - * Ensure that the caller does not wish to send too much. - */ - if (count > PPP_MTU) { - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp_tty_write: truncating user packet " - "from %u to mtu %d\n", count, PPP_MTU); - count = PPP_MTU; - } -/* - * Allocate a buffer for the data and fetch it from the user space. - */ - new_data = kmalloc (count, GFP_KERNEL); - if (new_data == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_tty_write: no memory\n"); - return 0; - } -/* - * lock this PPP unit so we will be the only writer; - * sleep if necessary - */ - while (lock_buffer (ppp->tbuf) != 0) { - current->timeout = 0; - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG "ppp_tty_write: sleeping\n"); - interruptible_sleep_on (&ppp->write_wait); - - ppp = tty2ppp (tty); - if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse) { - kfree (new_data); - return 0; - } - - if (current->signal & ~current->blocked) { - kfree (new_data); - return -EINTR; - } - } -/* - * Ensure that the caller's buffer is valid. - */ - status = verify_area (VERIFY_READ, data, count); - if (status != 0) { - kfree (new_data); - ppp->tbuf->locked = 0; - return status; - } - - memcpy_fromfs (new_data, data, count); -/* - * Change the LQR frame - */ - count = send_revise_frame (ppp, new_data, count); -/* - * Send the data - */ - ppp_dev_xmit_frame (ppp, ppp->tbuf, new_data, count); - kfree (new_data); - return (int) count; -} - -/* - * Process the BSD compression IOCTL event for the tty device. - */ - -static int -ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp) -{ - struct compressor *cp; - struct ppp_option_data data; - int error; - int nb; - u_char *ptr; - u_char ccp_option[CCP_MAX_OPTION_LENGTH]; -/* - * Fetch the compression parameters - */ - error = verify_area (VERIFY_READ, odp, sizeof (data)); - if (error == 0) { - memcpy_fromfs (&data, odp, sizeof (data)); - nb = data.length; - ptr = data.ptr; - if ((unsigned long) nb >= (unsigned long)CCP_MAX_OPTION_LENGTH) - nb = CCP_MAX_OPTION_LENGTH; - - error = verify_area (VERIFY_READ, ptr, nb); - } - - if (error != 0) - return error; - - memcpy_fromfs (ccp_option, ptr, nb); - - if (ccp_option[1] < 2) /* preliminary check on the length byte */ - return (-EINVAL); - - cp = find_compressor ((int) (unsigned) (unsigned char) ccp_option[0]); - if (cp != (struct compressor *) 0) { - /* - * Found a handler for the protocol - try to allocate - * a compressor or decompressor. - */ - error = 0; - if (data.transmit) { - if (ppp->sc_xc_state != NULL) - (*ppp->sc_xcomp->comp_free)(ppp->sc_xc_state); - - ppp->sc_xcomp = cp; - ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb); - - if (ppp->sc_xc_state == NULL) { - if (ppp->flags & SC_DEBUG) - printk("ppp%ld: comp_alloc failed\n", - ppp2dev (ppp)->base_addr); - error = -ENOBUFS; - } - ppp->flags &= ~SC_COMP_RUN; - } else { - if (ppp->sc_rc_state != NULL) - (*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state); - ppp->sc_rcomp = cp; - ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb); - if (ppp->sc_rc_state == NULL) { - if (ppp->flags & SC_DEBUG) - printk("ppp%ld: decomp_alloc failed\n", - ppp2dev (ppp)->base_addr); - error = ENOBUFS; - } - ppp->flags &= ~SC_DECOMP_RUN; - } - return (error); - } - - if (ppp->flags & SC_DEBUG) - printk(KERN_DEBUG "ppp%ld: no compressor for [%x %x %x], %x\n", - ppp2dev (ppp)->base_addr, ccp_option[0], ccp_option[1], - ccp_option[2], nb); - return (-EINVAL); /* no handler found */ -} - -/* - * Process the IOCTL event for the tty device. - */ - -static int -ppp_tty_ioctl (struct tty_struct *tty, struct file *file, unsigned int param2, - unsigned long param3) -{ - struct ppp *ppp = tty2ppp (tty); - register int temp_i = 0; - int error; -/* - * Verify the status of the PPP device. - */ - if (!ppp) - return -EBADF; - - if (ppp->magic != PPP_MAGIC) - return -EBADF; - - CHECK_PPP (-ENXIO); -/* - * The user must have an euid of root to do these requests. - */ - if (!suser ()) - return -EPERM; -/* - * Set the MRU value - */ - switch (param2) { - case PPPIOCSMRU: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - temp_i = get_int_user ((int *) param3); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set mru to %x\n", temp_i); - - if (ppp->mru != temp_i) - ppp_changedmtu (ppp, ppp2dev (ppp)->mtu, temp_i); - } - break; -/* - * Fetch the flags - */ - case PPPIOCGFLAGS: - error = verify_area (VERIFY_WRITE, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - temp_i = (ppp->flags & SC_MASK); -#ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */ - temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 | - SC_RCV_ODDP | SC_RCV_EVNP; -#endif - put_long_user ((long) temp_i, param3); - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_tty_ioctl: get flags: addr %lx flags " - "%x\n", param3, temp_i); - } - break; -/* - * Set the flags for the various options - */ - case PPPIOCSFLAGS: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - temp_i = get_int_user (param3) & SC_MASK; - temp_i |= (ppp->flags & ~SC_MASK); - - if ((ppp->flags & SC_CCP_OPEN) && - (temp_i & SC_CCP_OPEN) == 0) - ppp_ccp_closed (ppp); - - if ((ppp->flags | temp_i) & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set flags to %x\n", temp_i); - ppp->flags = temp_i; - } - break; -/* - * Set the compression mode - */ - case PPPIOCSCOMPRESS: - error = ppp_set_compression (ppp, - (struct ppp_option_data *) param3); - break; -/* - * Retrieve the transmit async map - */ - case PPPIOCGASYNCMAP: - error = verify_area (VERIFY_WRITE, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - put_long_user (ppp->xmit_async_map[0], param3); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: get asyncmap: addr " - "%lx asyncmap %lx\n", - param3, - (unsigned long) ppp->xmit_async_map[0]); - } - break; -/* - * Set the transmit async map - */ - case PPPIOCSASYNCMAP: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - ppp->xmit_async_map[0] = get_long_user (param3); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set xmit asyncmap %lx\n", - (unsigned long) ppp->xmit_async_map[0]); - } - break; -/* - * Set the receive async map - */ - case PPPIOCSRASYNCMAP: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - ppp->recv_async_map = get_long_user (param3); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set rcv asyncmap %lx\n", - (unsigned long) ppp->recv_async_map); - } - break; -/* - * Obtain the unit number for this device. - */ - case PPPIOCGUNIT: - error = verify_area (VERIFY_WRITE, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - put_long_user (ppp2dev (ppp)->base_addr, param3); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: get unit: %ld", - ppp2dev (ppp)->base_addr); - } - break; -/* - * Set the debug level - */ - case PPPIOCSDEBUG: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - temp_i = (get_int_user (param3) & 0x1F) << 16; - temp_i |= (ppp->flags & ~0x1F0000); - - if ((ppp->flags | temp_i) & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set flags to %x\n", temp_i); - ppp->flags = temp_i; - } - break; -/* - * Get the debug level - */ - case PPPIOCGDEBUG: - error = verify_area (VERIFY_WRITE, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - temp_i = (ppp->flags >> 16) & 0x1F; - put_long_user ((long) temp_i, param3); - - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: get debug level %d\n", - temp_i); - } - break; -/* - * Get the times since the last send/receive frame operation - */ - case PPPIOCGIDLE: - error = verify_area (VERIFY_WRITE, (void *) param3, - sizeof (struct ppp_idle)); - if (error == 0) { - struct ppp_idle cur_ddinfo; - unsigned long cur_jiffies = jiffies; - - /* change absolute times to relative times. */ - cur_ddinfo.xmit_idle = (cur_jiffies - ppp->ddinfo.xmit_idle) / HZ; - cur_ddinfo.recv_idle = (cur_jiffies - ppp->ddinfo.recv_idle) / HZ; - memcpy_tofs ((void *) param3, &cur_ddinfo, - sizeof (cur_ddinfo)); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: read demand dial info\n"); - } - break; -/* - * Retrieve the extended async map - */ - case PPPIOCGXASYNCMAP: - error = verify_area (VERIFY_WRITE, - (void *) param3, - sizeof (ppp->xmit_async_map)); - if (error == 0) { - memcpy_tofs ((void *) param3, - ppp->xmit_async_map, - sizeof (ppp->xmit_async_map)); - + (ppp->sc_rc_state, + opt, opt_len, + ppp->line, 0, ppp->mru, ppp->flags & SC_DEBUG)) { if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: get xasyncmap: addr %lx\n", - param3); + printk(KERN_DEBUG "%s: decomp running\n", + ppp->name); + cli(); + ppp->flags |= SC_DECOMP_RUN; + ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR); } break; -/* - * Set the async extended map - */ - case PPPIOCSXASYNCMAP: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (ppp->xmit_async_map)); - if (error == 0) { - __u32 temp_tbl[8]; - memcpy_fromfs (temp_tbl, (void *) param3, - sizeof (ppp->xmit_async_map)); - temp_tbl[1] = 0x00000000; - temp_tbl[2] &= ~0x40000000; - temp_tbl[3] |= 0x60000000; - - if ((temp_tbl[2] & temp_tbl[3]) != 0 || - (temp_tbl[4] & temp_tbl[5]) != 0 || - (temp_tbl[6] & temp_tbl[7]) != 0) - error = -EINVAL; - else { - memcpy (ppp->xmit_async_map, temp_tbl, - sizeof (ppp->xmit_async_map)); + case CCP_RESETACK: + /* + * CCP Reset-ack resets compressors and decompressors + * as it passes through. + */ + if ((ppp->flags & SC_CCP_UP) == 0) + break; + if (!rcvd) { + if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN)) { + (*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state); if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set xasyncmap\n"); + printk(KERN_DEBUG "%s: comp reset\n", + ppp->name); } - } - break; -/* - * Set the maximum VJ header compression slot number. - */ - case PPPIOCSMAXCID: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - temp_i = get_int_user (param3) + 1; - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set maxcid to %d\n", - temp_i); - if (ppp->slcomp != NULL) - slhc_free (ppp->slcomp); - ppp->slcomp = slhc_init (16, temp_i); - - if (ppp->slcomp == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp: no space for compression buffers!\n"); - ppp_release (ppp); - error = -ENOMEM; + } else { + if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) { + (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state); + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: decomp reset\n", + ppp->name); + cli(); + ppp->flags &= ~SC_DC_ERROR; } } break; -/* - * Allow users to read, but not set, the serial port parameters - */ - case TCGETS: - case TCGETA: - error = n_tty_ioctl (tty, file, param2, param3); - break; -/* - * All other ioctl() events will come here. - */ - default: - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_tty_ioctl: invalid ioctl: %x, addr %lx\n", - param2, - param3); - - error = -ENOIOCTLCMD; - break; } - return error; + restore_flags(flags); } /* - * TTY callback. - * - * Process the select() statement for the PPP device. + * CCP is down; free (de)compressor state if necessary. */ -static int -ppp_tty_select (struct tty_struct *tty, struct inode *inode, - struct file *filp, int sel_type, select_table * wait) +static void +ppp_ccp_closed(struct ppp *ppp) { - struct ppp *ppp = tty2ppp (tty); - int result = 1; -/* - * Verify the status of the PPP device. - */ - if (!ppp) - return -EBADF; - - if (ppp->magic != PPP_MAGIC) - return -EBADF; - - CHECK_PPP (0); -/* - * Branch on the type of select mode. A read request must lock the user - * buffer area. - */ - switch (sel_type) { - case SEL_IN: - if (set_bit (0, &ppp->ubuf->locked) == 0) { - /* Test for the presence of data in the queue */ - if (ppp->ubuf->head != ppp->ubuf->tail) { - clear_bit (0, &ppp->ubuf->locked); - break; - } - clear_bit (0, &ppp->ubuf->locked); - } /* fall through */ - /* - * Exceptions or read errors. - */ - case SEL_EX: - /* Is this a pty link and the remote disconnected? */ - if (tty->flags & (1 << TTY_SLAVE_CLOSED)) - break; + unsigned long flags; - /* Is this a local link and the modem disconnected? */ - if (tty_hung_up_p (filp)) - break; + save_flags(flags); + cli(); + ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN); + restore_flags(flags); + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: ccp closed\n", ppp->name); + if (ppp->sc_xc_state) { + (*ppp->sc_xcomp->comp_free) (ppp->sc_xc_state); + ppp->sc_xc_state = NULL; + } - select_wait (&ppp->read_wait, wait); - result = 0; - break; -/* - * Write mode. A write is allowed if there is no current transmission. - */ - case SEL_OUT: - if (ppp->tbuf->locked != 0) { - select_wait (&ppp->write_wait, wait); - result = 0; - } - break; + if (ppp->sc_rc_state) { + (*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state); + ppp->sc_rc_state = NULL; } - return result; } /************************************************************* - * NETWORK OUTPUT - * This routine accepts requests from the network layer - * and attempts to deliver the packets. - * It also includes various routines we are compelled to - * have to make the network layer work (arp, etc...). + * RECEIVE-SIDE ROUTINES *************************************************************/ /* - * Callback from the network layer when the device goes up. + * On entry, a received frame is in skb. + * Check it and dispose as appropriate. */ - static int -ppp_dev_open (struct device *dev) +ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb) { - struct ppp *ppp = dev2ppp (dev); - - /* reset POINTOPOINT every time, since dev_close zaps it! */ - dev->flags |= IFF_POINTOPOINT; + __u8 *data; + int count; + int proto; + int new_count; + struct sk_buff *new_skb; + ppp_proto_type *proto_ptr; + + /* + * An empty frame is ignored. This occurs if the FLAG sequence + * precedes and follows each frame. + */ + if (skb == NULL) + return 1; + if (skb->len == 0) { + KFREE_SKB(skb); + return 1; + } + data = skb->data; + count = skb->len; - if (ppp2tty (ppp) == NULL) { + /* + * Generate an error if the frame is too small. + */ + if (count < PPP_HDRLEN + 2) { if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp: %s not connected to a TTY! can't go open!\n", - dev->name); - return -ENXIO; + printk(KERN_DEBUG + "ppp: got runt ppp frame, %d chars\n", count); + ++ppp->estats.rx_length_errors; + return 0; } - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp: channel %s going up for IP packets!\n", - dev->name); + if ( !(ppp->flags & SC_SYNC) ) { + /* + * Verify the FCS of the frame and discard the FCS characters + * from the end of the buffer. + */ + if (ppp->rfcs != PPP_GOODFCS) { + if (ppp->flags & SC_DEBUG) { + printk(KERN_DEBUG + "ppp: frame with bad fcs, length = %d\n", + count); + ppp_print_buffer("bad frame", data, count); + } + ++ppp->estats.rx_crc_errors; + return 0; + } + count -= 2; /* ignore the fcs characters */ + skb_trim(skb, count); + } + + /* + * Process the active decompressor. + */ + if (ppp->sc_rc_state != NULL && + (ppp->flags & SC_DECOMP_RUN) && + ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) { + if (PPP_PROTOCOL(data) == PPP_COMP) { + /* + * If the frame is compressed then decompress it. + */ + new_skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN); + if (new_skb == NULL) { + printk(KERN_ERR "ppp_recv_frame: no memory\n"); + new_count = DECOMP_ERROR; + } else { + LIBERATE_SKB(new_skb); + new_count = (*ppp->sc_rcomp->decompress) + (ppp->sc_rc_state, data, count, + new_skb->data, ppp->mru + PPP_HDRLEN); + } + if (new_count > 0) { + /* Frame was decompressed OK */ + KFREE_SKB(skb); + skb = new_skb; + count = new_count; + data = skb_put(skb, count); + + } else { + /* + * On a decompression error, we pass the + * compressed frame up to pppd as an + * error indication. + */ + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO "%s: decomp err %d\n", + ppp->name, new_count); + if (new_skb != 0) + KFREE_SKB(new_skb); + if (ppp->slcomp != 0) + slhc_toss(ppp->slcomp); + ++ppp->stats.ppp_ierrors; + if (new_count == DECOMP_FATALERROR) { + ppp->flags |= SC_DC_FERROR; + } else { + ppp->flags |= SC_DC_ERROR; + } + } - CHECK_PPP (-ENXIO); - return 0; -} -/* - * Callback from the network layer when the ppp device goes down. - */ + } else { + /* + * The frame is not compressed. Pass it to the + * decompression code so it can update its + * dictionary if necessary. + */ + (*ppp->sc_rcomp->incomp)(ppp->sc_rc_state, + data, count); + } + } + else if (PPP_PROTOCOL(data) == PPP_COMP && (ppp->flags & SC_DEBUG)) + printk(KERN_INFO "%s: not decomp, rc_state=%p flags=%x\n", + ppp->name, ppp->sc_rc_state, ppp->flags); -static int -ppp_dev_close (struct device *dev) -{ - struct ppp *ppp = dev2ppp (dev); + /* + * Count the frame and print it + */ + ++ppp->stats.ppp_ipackets; + ppp->stats.ppp_ioctects += count; + if (ppp->flags & SC_LOG_INPKT) + ppp_print_buffer ("receive frame", data, count); - if (ppp2tty (ppp) == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp: %s not connected to a TTY! can't go down!\n", - dev->name); - return -ENXIO; + /* + * Find the procedure to handle this protocol. + * The last one is marked as protocol 0 which is the 'catch-all' + * to feed it to the pppd daemon. + */ + proto = PPP_PROTOCOL(data); + proto_ptr = proto_list; + while (proto_ptr->proto != 0 && proto_ptr->proto != proto) + ++proto_ptr; + + /* + * Update the appropriate statistic counter. + */ + if (!(*proto_ptr->func)(ppp, skb)) { + KFREE_SKB(skb); + ++ppp->stats.ppp_discards; } + + return 1; +} + /* - * We don't do anything about the device going down. It is not important - * for us. + * An input error has been detected, so we need to inform + * the VJ decompressor. */ - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp: channel %s going down for IP packets!\n", - dev->name); - CHECK_PPP (-ENXIO); - return 0; +static void +ppp_receive_error(struct ppp *ppp) +{ + CHECK_PPP_VOID(); + + if (ppp->slcomp != 0) + slhc_toss(ppp->slcomp); } /* - * IOCTL operation to read the version of the driver. + * Put the input frame into the networking system for the indicated protocol */ - static int -ppp_dev_ioctl_version (struct ppp *ppp, struct ifreq *ifr) +ppp_rcv_rx(struct ppp *ppp, __u16 proto, struct sk_buff *skb) { - int error; - int len; - char *result; -/* - * Must have write access to the buffer. - */ - result = (char *) ifr->ifr_ifru.ifru_data; - len = strlen (szVersion) + 1; - error = verify_area (VERIFY_WRITE, result, len); -/* - * Move the version data - */ - if (error == 0) - memcpy_tofs (result, szVersion, len); - return error; + /* + * Fill in a few fields of the skb and give it to netif_rx(). + */ + skb->dev = ppp2dev(ppp); /* We are the device */ + skb->protocol = htons(proto); + skb_pull(skb, PPP_HDRLEN); /* pull off ppp header */ + skb->mac.raw = skb->data; + ppp->last_recv = jiffies; + netif_rx (skb); + return 1; } /* - * IOCTL to read the statistics for the pppstats program. + * Process the receipt of an IP frame */ - static int -ppp_dev_ioctl_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev) +rcv_proto_ip(struct ppp *ppp, struct sk_buff *skb) { - struct ppp_stats *result, temp; - int error; -/* - * Must have write access to the buffer. - */ - result = (struct ppp_stats *) ifr->ifr_ifru.ifru_data; - error = verify_area (VERIFY_WRITE, - result, - sizeof (temp)); -/* - * Supply the information for the caller. First move the version data - * then move the ppp stats; and finally the vj stats. - */ - memset (&temp, 0, sizeof(temp)); - if (error == 0 && dev->flags & IFF_UP) { - memcpy (&temp.p, &ppp->stats, sizeof (struct pppstat)); - if (ppp->slcomp != NULL) { - temp.vj.vjs_packets = ppp->slcomp->sls_o_compressed+ - ppp->slcomp->sls_o_uncompressed; - temp.vj.vjs_compressed = ppp->slcomp->sls_o_compressed; - temp.vj.vjs_searches = ppp->slcomp->sls_o_searches; - temp.vj.vjs_misses = ppp->slcomp->sls_o_misses; - temp.vj.vjs_errorin = ppp->slcomp->sls_i_error; - temp.vj.vjs_tossed = ppp->slcomp->sls_i_tossed; - temp.vj.vjs_uncompressedin = ppp->slcomp->sls_i_uncompressed; - temp.vj.vjs_compressedin = ppp->slcomp->sls_i_compressed; - } - } -/* - * Move the data to the caller's buffer - */ - if (error == 0) - memcpy_tofs (result, &temp, sizeof (temp)); - return error; + CHECK_PPP(0); + if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0) + && ppp->sc_npmode[NP_IP] == NPMODE_PASS) + return ppp_rcv_rx(ppp, ETH_P_IP, skb); + return 0; } /* - * IOCTL to read the compression statistics for the pppstats program. + * Process the receipt of an IPv6 frame */ - static int -ppp_dev_ioctl_comp_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev) +rcv_proto_ipv6(struct ppp *ppp, struct sk_buff *skb) { - struct ppp_comp_stats *result, temp; - int error; -/* - * Must have write access to the buffer. - */ - result = (struct ppp_comp_stats *) ifr->ifr_ifru.ifru_data; - error = verify_area (VERIFY_WRITE, - result, - sizeof (temp)); -/* - * Supply the information for the caller. - */ - memset (&temp, 0, sizeof(temp)); - if (error == 0 && dev->flags & IFF_UP) { - if (ppp->sc_xc_state != NULL) - (*ppp->sc_xcomp->comp_stat) (ppp->sc_xc_state, - &temp.c); + CHECK_PPP(0); + if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0) + && ppp->sc_npmode[NP_IPV6] == NPMODE_PASS) + return ppp_rcv_rx(ppp, ETH_P_IPV6, skb); + return 0; +} - if (ppp->sc_rc_state != NULL) - (*ppp->sc_rcomp->decomp_stat) (ppp->sc_rc_state, - &temp.d); - } /* - * Move the data to the caller's buffer + * Process the receipt of an IPX frame */ - if (error == 0) - memcpy_tofs (result, &temp, sizeof (temp)); - return error; +static int +rcv_proto_ipx(struct ppp *ppp, struct sk_buff *skb) +{ + CHECK_PPP(0); + if (((ppp2dev(ppp)->flags & IFF_UP) != 0) && (skb->len > 0) + && ppp->sc_npmode[NP_IPX] == NPMODE_PASS) + return ppp_rcv_rx(ppp, ETH_P_IPX, skb); + return 0; } /* - * Callback from the network layer to process the sockioctl functions. + * Process the receipt of an Appletalk frame */ - static int -ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd) +rcv_proto_at(struct ppp *ppp, struct sk_buff *skb) { - struct ppp *ppp = dev2ppp (dev); - int error; -/* - * Process the requests - */ - switch (cmd) { - case SIOCGPPPSTATS: - error = ppp_dev_ioctl_stats (ppp, ifr, dev); - break; - - case SIOCGPPPCSTATS: - error = ppp_dev_ioctl_comp_stats (ppp, ifr, dev); - break; - - case SIOCGPPPVER: - error = ppp_dev_ioctl_version (ppp, ifr); - break; - - default: - error = -EINVAL; - break; - } - return error; + CHECK_PPP(0); + if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0) + && ppp->sc_npmode[NP_AT] == NPMODE_PASS) + return ppp_rcv_rx(ppp, ETH_P_PPPTALK, skb); + return 0; } /* - * Send an IP frame to the remote with vj header compression. - * - * Return 0 if frame was queued for transmission. - * 1 if frame must be re-queued for later driver support. + * Process the receipt of an VJ Compressed frame */ - -#if defined(IPX_CHANGE) -#define ppp_dev_xmit_ip1 ppp_dev_xmit_ip -#endif - static int -ppp_dev_xmit_ip1 (struct device *dev, struct ppp *ppp, u_char *data) +rcv_proto_vjc_comp(struct ppp *ppp, struct sk_buff *skb) { - int proto = PPP_IP; - int len; - struct ppp_hdr *hdr; - struct tty_struct *tty = ppp2tty (ppp); -/* - * Obtain the length from the IP header. - */ - len = ((struct iphdr *)data) -> tot_len; - len = ntohs (len); -/* - * Validate the tty interface - */ - if (tty == NULL) { + int new_count; + + CHECK_PPP(0); + if ((ppp->flags & SC_REJ_COMP_TCP) || ppp->slcomp == NULL) + return 0; + new_count = slhc_uncompress(ppp->slcomp, skb->data + PPP_HDRLEN, + skb->len - PPP_HDRLEN); + if (new_count <= 0) { if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_dev_xmit: %s not connected to a TTY!\n", - dev->name); + printk(KERN_NOTICE + "ppp: error in VJ decompression\n"); return 0; } + new_count += PPP_HDRLEN; + if (new_count > skb->len) + skb_put(skb, new_count - skb->len); + else + skb_trim(skb, new_count); + return rcv_proto_ip(ppp, skb); +} + /* - * Ensure that the PPP device is still up + * Process the receipt of an VJ Un-compressed frame */ - if (!(dev->flags & IFF_UP)) { +static int +rcv_proto_vjc_uncomp(struct ppp *ppp, struct sk_buff *skb) +{ + CHECK_PPP(0); + if ((ppp->flags & SC_REJ_COMP_TCP) || ppp->slcomp == NULL) + return 0; + if (slhc_remember(ppp->slcomp, skb->data + PPP_HDRLEN, + skb->len - PPP_HDRLEN) <= 0) { if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp_dev_xmit: packet sent on interface %s," - " which is down for IP\n", - dev->name); + printk(KERN_NOTICE "ppp: error in VJ memorizing\n"); return 0; } + return rcv_proto_ip(ppp, skb); +} + +static int +rcv_proto_ccp(struct ppp *ppp, struct sk_buff *skb) +{ + CHECK_PPP(0); + ppp_proto_ccp (ppp, skb->data + PPP_HDRLEN, skb->len - PPP_HDRLEN, 1); + return rcv_proto_unknown(ppp, skb); +} + /* - * Detect a change in the transfer size - */ - if (ppp->mtu != ppp2dev (ppp)->mtu) { - ppp_changedmtu (ppp, - ppp2dev (ppp)->mtu, - ppp->mru); - } -/* - * Acquire the lock on the transmission buffer. If the buffer was busy then - * mark the device as busy and return "failure to send, try back later" error. - */ - if (lock_buffer (ppp->wbuf) != 0) { - dev->tbusy = 1; - return 1; - } -/* - * Print the frame being sent - */ - if (ppp->flags & SC_LOG_OUTPKT) - ppp_print_buffer ("ppp outpkt", data, len); -/* - * At this point, the buffer will be transmitted. There is no other exit. - * - * Try to compress the header. + * Receive all unclassified protocols. */ - if (ppp->flags & SC_COMP_TCP) { - len = slhc_compress (ppp->slcomp, data, len, - buf_base (ppp->cbuf) + PPP_HARD_HDR_LEN, - &data, - (ppp->flags & SC_NO_TCP_CCID) == 0); - - if (data[0] & SL_TYPE_COMPRESSED_TCP) { - proto = PPP_VJC_COMP; - data[0] ^= SL_TYPE_COMPRESSED_TCP; - } else { - if (data[0] >= SL_TYPE_UNCOMPRESSED_TCP) - proto = PPP_VJC_UNCOMP; - data[0] = (data[0] & 0x0f) | 0x40; - } +static int +rcv_proto_unknown(struct ppp *ppp, struct sk_buff *skb) +{ + CHECK_PPP(0); + + /* + * Limit queue length by dropping old frames. + */ + skb_queue_tail(&ppp->rcv_q, skb); + while (ppp->rcv_q.qlen > PPP_MAX_RCV_QLEN) { + struct sk_buff *skb = skb_dequeue(&ppp->rcv_q); + if (skb) + KFREE_SKB(skb); } -/* - * Send the frame - */ - len += PPP_HARD_HDR_LEN; - hdr = &((struct ppp_hdr *) data)[-1]; - hdr->address = PPP_ALLSTATIONS; - hdr->control = PPP_UI; - hdr->protocol[0] = 0; - hdr->protocol[1] = proto; + wake_up_interruptible (&ppp->read_wait); + if (ppp->tty->fasync != NULL) + kill_fasync (ppp->tty->fasync, SIGIO); + + return 1; +} + +/************************************************************* + * TRANSMIT-SIDE ROUTINES + *************************************************************/ - return ppp_dev_xmit_frame (ppp, ppp->wbuf, (u_char *) hdr, len); +/* local function to store a value into the LQR frame */ +extern inline __u8 * store_long (register __u8 *p, register int value) { + *p++ = (__u8) (value >> 24); + *p++ = (__u8) (value >> 16); + *p++ = (__u8) (value >> 8); + *p++ = (__u8) value; + return p; } /* - * This is just an interum solution until the 1.3 kernel's networking is - * available. The 1.2 kernel has problems with device headers before the - * buffers. - * - * This routine should be deleted, and the ppp_dev_xmit_ip1 routine called - * by this name. + * Compress and send an frame to the peer. + * Should be called with xmit_busy == 1, having been set by the caller. + * That is, we use xmit_busy as a lock to prevent reentry of this + * procedure. */ - -#if !defined(IPX_CHANGE) -static int -ppp_dev_xmit_ip (struct device *dev, struct ppp *ppp, u_char *data) +static void +ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) { - struct ppp_hdr *hdr; - int len; - int answer; + int proto; + __u8 *data; + int count; + __u8 *p; + int ret; - len = ((struct iphdr *)data) -> tot_len; - len = ntohs (len); + CHECK_PPP_VOID(); + data = skb->data; + count = skb->len; + + /* dump the buffer */ + if (ppp->flags & SC_LOG_OUTPKT) + ppp_print_buffer ("write frame", data, count); + + /* + * Handle various types of protocol-specific compression + * and other processing, including: + * - VJ TCP header compression + * - updating LQR packets + * - updating CCP state on CCP packets + */ + proto = PPP_PROTOCOL(data); + switch (proto) { + case PPP_IP: + if ((ppp->flags & SC_COMP_TCP) && ppp->slcomp != NULL) + skb = ppp_vj_compress(ppp, skb); + break; + + case PPP_LQR: + /* + * Update the LQR frame with the current MIB information. + * This way the information is accurate and up-to-date. + */ + if (count < 48) + break; + p = data + 40; /* Point to last two items. */ + p = store_long(p, ppp->stats.ppp_opackets + 1); + p = store_long(p, ppp->stats.ppp_ooctects + count); + ++ppp->stats.ppp_olqrs; + break; - hdr = (struct ppp_hdr *) kmalloc (len + sizeof (struct ppp_hdr), - GFP_ATOMIC); + case PPP_CCP: + /* + * Outbound compression control frames + */ + ppp_proto_ccp(ppp, data + PPP_HDRLEN, count - PPP_HDRLEN, 0); + break; + } + data = skb->data; + count = skb->len; - if (hdr == NULL) - answer = 1; - else { - memcpy (&hdr[1], data, len); - answer = ppp_dev_xmit_ip1 (dev, ppp, (u_char *) &hdr[1]); - kfree (hdr); + /* + * Compress the whole frame if possible. + */ + if (((ppp->flags & SC_COMP_RUN) != 0) && + (ppp->sc_xc_state != (void *) 0) && + (proto != PPP_LCP) && + (proto != PPP_CCP)) { + struct sk_buff *new_skb; + int new_count; + + /* Allocate an skb for the compressed frame. */ + new_skb = alloc_skb(ppp->mtu + PPP_HDRLEN, GFP_ATOMIC); + if (new_skb == NULL) { + printk(KERN_ERR "ppp_send_frame: no memory\n"); + KFREE_SKB(skb); + ppp->xmit_busy = 0; + return; + } + LIBERATE_SKB(new_skb); + + /* Compress the frame. */ + new_count = (*ppp->sc_xcomp->compress) + (ppp->sc_xc_state, data, new_skb->data, + count, ppp->mtu + PPP_HDRLEN); + + /* Did it compress? */ + if (new_count > 0 && (ppp->flags & SC_CCP_UP)) { + skb_put(new_skb, new_count); + KFREE_SKB(skb); + skb = new_skb; + } else { + /* + * The frame could not be compressed, or it could not + * be sent in compressed form because CCP is down. + */ + KFREE_SKB(new_skb); + } } - return answer; + /* + * Send the frame + */ + if ( ppp->flags & SC_SYNC ) + ret = ppp_sync_send(ppp, skb); + else + ret = ppp_async_send(ppp, skb); + if (ret > 0) { + /* we can release the lock */ + ppp->xmit_busy = 0; + } else if (ret < 0) { + /* can't happen, since the caller got the xmit_busy lock */ + printk(KERN_ERR "ppp: ppp_async_send didn't accept pkt\n"); + } } -#endif /* !defined(IPX_CHANGE) */ /* - * Send an IPX (or any other non-IP) frame to the remote. - * - * Return 0 if frame was queued for transmission. - * 1 if frame must be re-queued for later driver support. + * Apply VJ TCP header compression to a packet. */ +static struct sk_buff * +ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb) +{ + __u8 *orig_data, *data; + struct sk_buff *new_skb; + int len, proto; -#if defined(IPX_CHANGE) -#define ppp_dev_xmit_ipx1 ppp_dev_xmit_ipx -#endif + new_skb = alloc_skb(skb->len, GFP_ATOMIC); + if (new_skb == NULL) { + printk(KERN_ERR "ppp: no memory for vj compression\n"); + return skb; + } + LIBERATE_SKB(new_skb); -#if defined(NEW_SKBUFF) || defined(IPX_CHANGE) -static int -ppp_dev_xmit_ipx1 (struct device *dev, struct ppp *ppp, - u_char *data, int len, int proto) -{ - struct tty_struct *tty = ppp2tty (ppp); - struct ppp_hdr *hdr; -/* - * Validate the tty interface - */ - if (tty == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_dev_xmit: %s not connected to a TTY!\n", - dev->name); - return 0; + orig_data = data = skb->data + PPP_HDRLEN; + len = slhc_compress(ppp->slcomp, data, skb->len - PPP_HDRLEN, + new_skb->data + PPP_HDRLEN, &data, + (ppp->flags & SC_NO_TCP_CCID) == 0); + + if (data == orig_data) { + /* Couldn't compress the data */ + KFREE_SKB(new_skb); + return skb; } -/* - * Ensure that the PPP device is still up - */ - if (!(dev->flags & IFF_UP)) { - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp_dev_xmit: packet sent on interface %s," - " which is down\n", - dev->name); - return 0; + + /* The data has been changed */ + if (data[0] & SL_TYPE_COMPRESSED_TCP) { + proto = PPP_VJC_COMP; + data[0] ^= SL_TYPE_COMPRESSED_TCP; + } else { + if (data[0] >= SL_TYPE_UNCOMPRESSED_TCP) + proto = PPP_VJC_UNCOMP; + else + proto = PPP_IP; + data[0] = orig_data[0]; } -/* - * Detect a change in the transfer size - */ - if (ppp->mtu != ppp2dev (ppp)->mtu) { - ppp_changedmtu (ppp, - ppp2dev (ppp)->mtu, - ppp->mru); + + data = skb_put(new_skb, len + PPP_HDRLEN); + data[0] = PPP_ALLSTATIONS; + data[1] = PPP_UI; + data[2] = 0; + data[3] = proto; + + KFREE_SKB(skb); + return new_skb; +} + +static inline void +ppp_send_frames(struct ppp *ppp) +{ + struct sk_buff *skb; + + while (!test_and_set_bit(0, &ppp->xmit_busy)) { + skb = skb_dequeue(&ppp->xmt_q); + if (skb == NULL) { + ppp->xmit_busy = 0; + break; + } + ppp_send_frame(ppp, skb); } -/* - * Acquire the lock on the transmission buffer. If the buffer was busy then - * mark the device as busy and return "failure to send, try back later" error. - */ - if (lock_buffer (ppp->wbuf) != 0) { - dev->tbusy = 1; - return 1; + if (!ppp->xmit_busy && ppp->dev.tbusy) { + ppp->dev.tbusy = 0; + mark_bh(NET_BH); } +} + /* - * Print the frame being sent - */ - if (ppp->flags & SC_LOG_OUTPKT) - ppp_print_buffer ("ppp outpkt", data, len); -/* - * Send the frame + * Called from the hardware (tty) layer when it can accept + * another packet. */ - len += PPP_HARD_HDR_LEN; - hdr = &((struct ppp_hdr *) data)[-1]; - - hdr->address = PPP_ALLSTATIONS; - hdr->control = PPP_UI; - hdr->protocol[0] = proto >> 8; - hdr->protocol[1] = proto; +static void +ppp_output_wakeup(struct ppp *ppp) +{ + CHECK_PPP_VOID(); - return ppp_dev_xmit_frame (ppp, ppp->wbuf, (u_char *) hdr, len); + if (!ppp->xmit_busy) { + printk(KERN_ERR "ppp_output_wakeup called but xmit_busy==0\n"); + return; + } + ppp->xmit_busy = 0; + ppp_send_frames(ppp); } /* - * This is just an interum solution until the 1.3 kernel's networking is - * available. The 1.2 kernel has problems with device headers before the - * buffers. - * - * This routine should be deleted, and the ppp_dev_xmit_ipx1 routine called - * by this name. + * Send a control frame (from pppd). */ - -#if !defined(IPX_CHANGE) -static int -ppp_dev_xmit_ipx (struct device *dev, struct ppp *ppp, - u_char *data, int len, int proto) +static void +ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb) { - struct ppp_hdr *hdr; - int answer; + CHECK_PPP_VOID(); - hdr = (struct ppp_hdr *) kmalloc (len + sizeof (struct ppp_hdr), - GFP_ATOMIC); - if (hdr == NULL) - answer = 1; - else { - memcpy (&hdr[1], data, len); - answer = ppp_dev_xmit_ipx1 (dev, ppp, (u_char *) &hdr[1], - len, proto); - kfree (hdr); - } - - return answer; + /* + * Put the packet on the queue, then send as many as we can. + */ + skb_queue_tail(&ppp->xmt_q, skb); + ppp_send_frames(ppp); } -#endif /* !defined(IPX_CHANGE) */ -#endif /* defined(NEW_SKBUFF) || defined(IPX_CHANGE) */ + +/************************************************************* + * NETWORK OUTPUT + * This routine accepts requests from the network layer + * and attempts to deliver the packets. + *************************************************************/ /* - * Send a frame to the remote. + * Send a frame to the peer. + * Returns 1 iff the frame was not accepted. */ - static int -ppp_dev_xmit (sk_buff *skb, struct device *dev) +ppp_dev_xmit(struct sk_buff *skb, struct device *dev) { - int answer, len; - u_char *data; - struct ppp *ppp = dev2ppp (dev); - struct tty_struct *tty = ppp2tty (ppp); -/* - * just a little sanity check. - */ - if (skb == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING "ppp_dev_xmit: null packet!\n"); + struct ppp *ppp = dev2ppp(dev); + struct tty_struct *tty = ppp2tty(ppp); + enum NPmode npmode; + int proto; + unsigned char *hdr; + + /* just a little sanity check. */ + if (skb == NULL) + return 0; + if (skb->data == NULL) { + KFREE_SKB(skb); return 0; } -/* - * Avoid timing problem should tty hangup while data is queued to be sent - */ + + /* + * Avoid timing problem should tty hangup while data is + * queued to be sent. + */ if (!ppp->inuse) { - dev_kfree_skb (skb, FREE_WRITE); - dev_close (dev); + KFREE_SKB(skb); return 0; } -/* - * Validate the tty linkage - */ - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG "ppp_dev_xmit [%s]: skb %p\n", - dev->name, skb); -/* - * Validate the tty interface - */ + + /* + * Validate the tty interface + */ if (tty == NULL) { if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_dev_xmit: %s not connected to a TTY!\n", - dev->name); - dev_kfree_skb (skb, FREE_WRITE); + printk(KERN_ERR + "ppp_dev_xmit: %s not connected to a TTY!\n", + dev->name); + KFREE_SKB(skb); return 0; } -/* - * Fetch the pointer to the data - */ - len = skb->len; - data = skb_data(skb); -/* - * Look at the protocol in the skb to determine the difference between - * an IP frame and an IPX frame. - */ -#if defined(NEW_SKBUFF) || defined(IPX_CHANGE) - switch (ntohs (skb->protocol)) { + /* + * Work out the appropriate network-protocol mode for this packet. + */ + npmode = NPMODE_PASS; /* default */ + switch (ntohs(skb->protocol)) { + case ETH_P_IP: + proto = PPP_IP; + npmode = ppp->sc_npmode[NP_IP]; + break; + case ETH_P_IPV6: + proto = PPP_IPV6; + npmode = ppp->sc_npmode[NP_IPV6]; + break; case ETH_P_IPX: - answer = ppp_dev_xmit_ipx (dev, ppp, data, len, PPP_IPX); + proto = PPP_IPX; + npmode = ppp->sc_npmode[NP_IPX]; break; + case ETH_P_PPPTALK: + case ETH_P_ATALK: + proto = PPP_AT; + npmode = ppp->sc_npmode[NP_AT]; + break; + default: + if (ppp->flags & SC_DEBUG) + printk(KERN_INFO "%s: packet for unknown proto %x\n", + ppp->name, ntohs(skb->protocol)); + KFREE_SKB(skb); + return 0; + } - case ETH_P_IP: - answer = ppp_dev_xmit_ip (dev, ppp, data); + /* + * Drop, accept or reject the packet depending on the mode. + */ + switch (npmode) { + case NPMODE_PASS: break; - default: /* All others have no support at this time. */ -#if 1 /* I **REALLY** want to toss this. For the time being, I'll assume - that this is IP. However, if you start to see the message below - then you should fix the skb->protocol to have the proper values. */ + case NPMODE_QUEUE: + /* + * We may not send the packet now, so drop it. + * XXX It would be nice to be able to return it to the + * network system to be queued and retransmitted later. + */ + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: returning frame\n", ppp->name); + KFREE_SKB(skb); + return 0; - printk (KERN_ERR - "ppp: strange protocol type %x in ppp_dev_xmit\n", - skb->protocol); - answer = ppp_dev_xmit_ip (dev, ppp, data); - break; -#else /* Shortly, this is what it will be! */ - dev_kfree_skb (skb, FREE_WRITE); + case NPMODE_ERROR: + case NPMODE_DROP: + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG + "ppp_dev_xmit: dropping (npmode = %d) on %s\n", + npmode, ppp->name); + KFREE_SKB(skb); return 0; -#endif /* if 1 */ } -#else /* defined(NEW_SKBUFF) || defined(IPX_CHANGE) */ - answer = ppp_dev_xmit_ip (dev, ppp, data); -#endif -/* - * This is the end of the transmission. Release the buffer if it was sent. - */ - if (answer == 0) { - dev_kfree_skb (skb, FREE_WRITE); - ppp->ddinfo.xmit_idle = jiffies; + /* + * The dev->tbusy field acts as a lock to allow only + * one packet to be processed at a time. If we can't + * get the lock, try again later. + * We deliberately queue as little as possible inside + * the ppp driver in order to minimize the latency + * for high-priority packets. + */ + if (test_and_set_bit(0, &ppp->xmit_busy)) { + dev->tbusy = 1; /* can't take it now */ + return 1; } - return answer; -} - -/* - * Generate the statistic information for the /proc/net/dev listing. - */ - -static struct enet_statistics * -ppp_dev_stats (struct device *dev) -{ - struct ppp *ppp = dev2ppp (dev); - static struct enet_statistics ppp_stats; - - ppp_stats.rx_packets = ppp->stats.ppp_ipackets; - ppp_stats.rx_errors = ppp->stats.ppp_ierrors; - ppp_stats.rx_dropped = ppp->stats.ppp_ierrors; - ppp_stats.rx_fifo_errors = 0; - ppp_stats.rx_length_errors = 0; - ppp_stats.rx_over_errors = 0; - ppp_stats.rx_crc_errors = 0; - ppp_stats.rx_frame_errors = 0; - ppp_stats.tx_packets = ppp->stats.ppp_opackets; - ppp_stats.tx_errors = ppp->stats.ppp_oerrors; - ppp_stats.tx_dropped = 0; - ppp_stats.tx_fifo_errors = 0; - ppp_stats.collisions = 0; - ppp_stats.tx_carrier_errors = 0; - ppp_stats.tx_aborted_errors = 0; - ppp_stats.tx_window_errors = 0; - ppp_stats.tx_heartbeat_errors = 0; - - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO "ppp_dev_stats called"); - return &ppp_stats; + dev->tbusy = 0; + + /* + * Put the 4-byte PPP header on the packet. + * If there isn't room for it, we have to copy the packet. + */ + if (skb_headroom(skb) < PPP_HDRLEN) { + struct sk_buff *new_skb; + + new_skb = alloc_skb(skb->len + PPP_HDRLEN, GFP_ATOMIC); + if (new_skb == NULL) { + printk(KERN_ERR "%s: skb hdr alloc failed\n", + ppp->name); + KFREE_SKB(skb); + ppp->xmit_busy = 0; + ppp_send_frames(ppp); + return 0; + } + LIBERATE_SKB(new_skb); + skb_reserve(new_skb, PPP_HDRLEN); + memcpy(skb_put(new_skb, skb->len), skb->data, skb->len); + KFREE_SKB(skb); + skb = new_skb; + } + + hdr = skb_push(skb, PPP_HDRLEN); + hdr[0] = PPP_ALLSTATIONS; + hdr[1] = PPP_UI; + hdr[2] = proto >> 8; + hdr[3] = proto; + + ppp_send_frame(ppp, skb); + if (!ppp->xmit_busy) + ppp_send_frames(ppp); + return 0; } -#if defined(NEW_SKBUFF) +#if LINUX_VERSION_CODE < VERSION(2,1,15) /* - * This defines the protocol layer which is blank since the - * driver does all the cooking. + * Null hard_header and header_rebuild routines. */ - -static int ppp_dev_input (struct protocol *self, struct protocol *lower, - sk_buff *skb, void *saddr, void *daddr) +static int ppp_dev_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, + void *saddr, unsigned int len) { - return protocol_pass_demultiplex(self, NULL, skb, NULL, NULL); -} - -static int ppp_dev_output (struct protocol *self, sk_buff *skb, int type, - int subid, void *saddr, void *daddr, void *opt) -{ - if(skb->dev==NULL) - { - printk("ppp_dev_output: No device.\n"); - kfree_skb(skb, FREE_WRITE); - return -1; - } - dev_queue_xmit(skb, skb->dev, skb->priority); return 0; } -static int ppp_dev_getkey(int protocol, int subid, unsigned char *key) +static int ppp_dev_rebuild(void *eth, struct device *dev, + unsigned long raddr, struct sk_buff *skb) { - switch (protocol) - { - case htons (ETH_P_IP): - case htons (ETH_P_IPX): - return 0; - - default: - break; - } - - return -EAFNOSUPPORT; + return 0; } +#endif /* < 2.1.15 */ -#else - -#if USE_SKB_PROTOCOL == 0 /* - * Called to enquire about the type of the frame in the buffer. Return - * ETH_P_IP for an IP frame, ETH_P_IPX for an IPX frame. + * Generate the statistic information for the /proc/net/dev listing. */ - -static unsigned short -ppp_dev_type (sk_buff *skb, struct device *dev) +static struct net_device_stats * +ppp_dev_stats (struct device *dev) { - return (htons (ETH_P_IP)); -} -#endif + struct ppp *ppp = dev2ppp (dev); -#if USE_SKB_PROTOCOL == 0 -static int ppp_dev_header (unsigned char *buff, struct device *dev, - unsigned short type, void *daddr, void *saddr, - unsigned len, struct sk_buff *skb) -#else -static int ppp_dev_header (sk_buff *skb, struct device *dev, - unsigned short type, void *daddr, - void *saddr, unsigned len) + ppp->estats.rx_packets = ppp->stats.ppp_ipackets; + ppp->estats.rx_errors = ppp->stats.ppp_ierrors; + ppp->estats.tx_packets = ppp->stats.ppp_opackets; + ppp->estats.tx_errors = ppp->stats.ppp_oerrors; +#if LINUX_VERSION_CODE >= VERSION(2,1,25) + ppp->estats.rx_bytes = ppp->stats.ppp_ibytes; + ppp->estats.tx_bytes = ppp->stats.ppp_obytes; #endif -{ - return (0); -} -static int -ppp_dev_rebuild (void *buff, struct device *dev, unsigned long raddr, - sk_buff *skb) -{ - return (0); + return &ppp->estats; } -#endif /************************************************************* * UTILITIES * Miscellany called by various functions above. *************************************************************/ +/* Locate the previous instance of the PPP channel */ +static struct ppp * +ppp_find(int pid_value) +{ + struct ppp *ppp; + + /* try to find the device which this pid is already using */ + for (ppp = ppp_list; ppp != 0; ppp = ppp->next) { + if (ppp->inuse && ppp->sc_xfer == pid_value) { + ppp->sc_xfer = 0; + break; + } + } + return ppp; +} + /* allocate or create a PPP channel */ static struct ppp * -ppp_alloc (void) +ppp_alloc(void) { int if_num; int status; - ppp_ctrl_t *ctl; struct device *dev; struct ppp *ppp; /* try to find an free device */ - ctl = ppp_list; - if_num = 0; - - while (ctl) { - ppp = ctl2ppp (ctl); - if (!set_bit(0, &ppp->inuse)) - return (ppp); - ctl = ctl->next; - if (++if_num == max_dev) - return (NULL); + for (ppp = ppp_list; ppp != 0; ppp = ppp->next) { + if (!test_and_set_bit(0, &ppp->inuse)) { + dev = ppp2dev(ppp); + if (dev->flags & IFF_UP) { + clear_bit(0, &ppp->inuse); + continue; + } + /* Reregister device */ + unregister_netdev(dev); + if (register_netdev(dev) == 0) + return ppp; + printk(KERN_DEBUG "could not reregister ppp device\n"); + /* leave inuse set in this case */ + } + } + + /* + * There are no available units, so make a new one. + */ + ppp = (struct ppp *) kmalloc(sizeof(struct ppp), GFP_KERNEL); + if (ppp == 0) { + printk(KERN_ERR "ppp: struct ppp allocation failed\n"); + return 0; + } + memset(ppp, 0, sizeof(*ppp)); + + /* initialize channel control data */ + ppp->magic = PPP_MAGIC; + ppp->next = NULL; + ppp->inuse = 1; + ppp->read_wait = NULL; + + /* + * Make up a suitable name for this device + */ + dev = ppp2dev(ppp); + dev->name = ppp->name; +#if LINUX_VERSION_CODE < VERSION(2,1,31) + if_num = (ppp_list == 0)? 0: ppp_last->line + 1; + sprintf(ppp->name, "ppp%d", if_num); +#else + if_num = dev_alloc_name(dev, "ppp%d"); +#endif + if (if_num < 0) { + printk(KERN_ERR "ppp: dev_alloc_name failed (%d)\n", if_num); + kfree(ppp); + return 0; + } + ppp->line = if_num; + ppp->slcomp = NULL; + + dev->next = NULL; + dev->init = ppp_init_dev; + dev->name = ppp->name; + dev->priv = (void *) ppp; + + /* register device so that we can be ifconfig'd */ + /* ppp_init_dev() will be called as a side-effect */ + status = register_netdev (dev); + if (status == 0) { + printk(KERN_INFO "registered device %s\n", dev->name); + } else { + printk(KERN_ERR + "ppp_alloc - register_netdev(%s) = %d failure.\n", + dev->name, status); + kfree(ppp); + ppp = NULL; } + + /* link this unit into our list */ + if (ppp_list == 0) + ppp_list = ppp; + else + ppp_last->next = ppp; + ppp_last = ppp; + + return ppp; +} + /* - * There are no available items. Allocate a device from the system pool + * Initialize the generic parts of the ppp structure. */ - ctl = (ppp_ctrl_t *) kmalloc (sizeof(ppp_ctrl_t), GFP_KERNEL); - if (ctl) { - (void) memset(ctl, 0, sizeof(ppp_ctrl_t)); - ppp = ctl2ppp (ctl); - dev = ctl2dev (ctl); - - /* initialize channel control data */ - set_bit(0, &ppp->inuse); - - ppp->line = if_num; - ppp->tty = NULL; - ppp->dev = dev; - - dev->next = NULL; - dev->init = ppp_init_dev; - dev->name = ctl->name; - dev->base_addr = (unsigned long) if_num; - dev->priv = (void *) ppp; - - sprintf (dev->name, "ppp%d", if_num); - - /* link in the new channel */ - ctl->next = ppp_list; - ppp_list = ctl; - -/* register device so that we can be ifconfig'd */ -/* ppp_init_dev() will be called as a side-effect */ - - status = register_netdev (dev); - if (status == 0) { - printk (KERN_INFO "registered device %s\n", dev->name); - return (ppp); - } +static void +ppp_generic_init(struct ppp *ppp) +{ + int indx; - printk (KERN_ERR - "ppp_alloc - register_netdev(%s) = %d failure.\n", - dev->name, status); - /* This one will forever be busy as it is not initialized */ + ppp->flags = 0; + ppp->mtu = PPP_MTU; + ppp->mru = PPP_MRU; + + skb_queue_head_init(&ppp->xmt_q); + skb_queue_head_init(&ppp->rcv_q); + + ppp->last_xmit = jiffies; + ppp->last_recv = jiffies; + ppp->xmit_busy = 0; + + /* clear statistics */ + memset(&ppp->stats, 0, sizeof (struct pppstat)); + memset(&ppp->estats, 0, sizeof(struct net_device_stats)); + + /* PPP compression data */ + ppp->sc_xc_state = NULL; + ppp->sc_rc_state = NULL; + + for (indx = 0; indx < NUM_NP; ++indx) + ppp->sc_npmode[indx] = NPMODE_PASS; +} + +/* + * Called to clean up the generic parts of the ppp structure. + */ +static void +ppp_release(struct ppp *ppp) +{ + struct sk_buff *skb; + + CHECK_PPP_MAGIC(ppp); + + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s released\n", ppp->name); + + ppp_ccp_closed(ppp); + + /* Ensure that the pppd process is not hanging on select()/poll() */ + wake_up_interruptible(&ppp->read_wait); + + if (ppp->slcomp) { + slhc_free(ppp->slcomp); + ppp->slcomp = NULL; + } + + while ((skb = skb_dequeue(&ppp->rcv_q)) != NULL) + KFREE_SKB(skb); + while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL) + KFREE_SKB(skb); + + ppp->inuse = 0; + if (ppp->dev.tbusy) { + ppp->dev.tbusy = 0; + mark_bh(NET_BH); } - return (NULL); } /* * Utility procedures to print a buffer in hex/ascii */ - static void -ppp_print_hex (register u_char * out, const u_char * in, int count) +ppp_print_hex (register __u8 * out, const __u8 * in, int count) { - register u_char next_ch; + register __u8 next_ch; static char hex[] = "0123456789ABCDEF"; while (count-- > 0) { @@ -3423,9 +3174,9 @@ ppp_print_hex (register u_char * out, const u_char * in, int count) } static void -ppp_print_char (register u_char * out, const u_char * in, int count) +ppp_print_char (register __u8 * out, const __u8 * in, int count) { - register u_char next_ch; + register __u8 next_ch; while (count-- > 0) { next_ch = *in++; @@ -3442,18 +3193,18 @@ ppp_print_char (register u_char * out, const u_char * in, int count) } static void -ppp_print_buffer (const u_char * name, const u_char * buf, int count) +ppp_print_buffer (const char *name, const __u8 *buf, int count) { - u_char line[44]; + __u8 line[44]; - if (name != (u_char *) NULL) - printk (KERN_DEBUG "ppp: %s, count = %d\n", name, count); + if (name != NULL) + printk(KERN_DEBUG "ppp: %s, count = %d\n", name, count); while (count > 8) { memset (line, 32, 44); ppp_print_hex (line, buf, 8); ppp_print_char (&line[8 * 3], buf, 8); - printk (KERN_DEBUG "%s\n", line); + printk(KERN_DEBUG "%s\n", line); count -= 8; buf += 8; } @@ -3462,7 +3213,7 @@ ppp_print_buffer (const u_char * name, const u_char * buf, int count) memset (line, 32, 44); ppp_print_hex (line, buf, count); ppp_print_char (&line[8 * 3], buf, count); - printk (KERN_DEBUG "%s\n", line); + printk(KERN_DEBUG "%s\n", line); } } @@ -3487,7 +3238,7 @@ static struct compressor *find_compressor (int type) lnk = ppp_compressors; while (lnk != (struct compressor_link *) 0) { - if ((int) (unsigned char) lnk->comp->compress_proto == type) { + if ((int) (__u8) lnk->comp->compress_proto == type) { restore_flags(flags); return lnk->comp; } @@ -3503,17 +3254,18 @@ static int ppp_register_compressor (struct compressor *cp) struct compressor_link *new; unsigned long flags; - new = (struct compressor_link *) kmalloc (sizeof (struct compressor_link), GFP_KERNEL); + new = (struct compressor_link *) + kmalloc (sizeof (struct compressor_link), GFP_KERNEL); if (new == (struct compressor_link *) 0) - return 1; + return 1; save_flags(flags); cli(); if (find_compressor (cp->compress_proto)) { restore_flags(flags); - kfree (new); + kfree (new); return 0; } @@ -3537,7 +3289,7 @@ static void ppp_unregister_compressor (struct compressor *cp) lnk = ppp_compressors; while (lnk != (struct compressor_link *) 0) { if (lnk->comp == cp) { - if (prev) + if (prev) prev->next = lnk->next; else ppp_compressors = lnk->next; @@ -3563,75 +3315,54 @@ init_module(void) /* register our line disciplines */ status = ppp_first_time(); if (status != 0) - printk (KERN_INFO - "PPP: ppp_init() failure %d\n", status); + printk(KERN_INFO "PPP: ppp_init() failure %d\n", status); +#if LINUX_VERSION_CODE < VERSION(2,1,18) else (void) register_symtab (&ppp_syms); - return (status); +#endif + + return status; } void cleanup_module(void) { int status; - ppp_ctrl_t *ctl, *next_ctl; - struct device *dev; - struct ppp *ppp; - int busy_flag = 0; -/* - * Ensure that the devices are not in operation. - */ - ctl = ppp_list; - while (ctl) { - ppp = ctl2ppp (ctl); - if (ppp->inuse && ppp->tty != NULL) { - busy_flag = 1; - break; - } - - dev = ctl2dev (ctl); - if (dev->start || dev->flags & IFF_UP) { - busy_flag = 1; - break; - } - ctl = ctl->next; - } -/* - * Ensure that there are no compressor modules registered - */ - if (ppp_compressors != NULL) - busy_flag = 1; - - if (busy_flag) { - printk (KERN_INFO - "PPP: device busy, remove delayed\n"); - return; - } -/* - * Release the tty registration of the line dicipline so that no new entries - * may be created. - */ + struct ppp *ppp, *next_ppp; + int busy = 0; + + /* + * Ensure that the devices are not in operation. + */ + for (ppp = ppp_list; ppp != 0; ppp = ppp->next) { + CHECK_PPP_MAGIC(ppp); + if (ppp->inuse || (ppp->dev.flags & IFF_UP)) + ++busy; + } + if (busy) + printk(KERN_CRIT "PPP: removing despite %d units in use!\n", + busy); + + /* + * Release the tty registration of the line discipline so that + * ttys can no longer be put into PPP line discipline. + */ status = tty_register_ldisc (N_PPP, NULL); if (status != 0) - printk (KERN_INFO - "PPP: Unable to unregister ppp line discipline " - "(err = %d)\n", status); + printk(KERN_ERR + "PPP: Unable to unregister ppp line discipline " + "(err = %d)\n", status); else - printk (KERN_INFO + printk(KERN_INFO "PPP: ppp line discipline successfully unregistered\n"); -/* - * De-register the devices so that there is no problem with them - */ - next_ctl = ppp_list; - while (next_ctl) { - ctl = next_ctl; - next_ctl = ctl->next; - ppp = ctl2ppp (ctl); - dev = ctl2dev (ctl); - - ppp_release (ppp); - unregister_netdev (dev); - kfree (ctl); + + /* + * De-register the devices so that there is no problem with them + */ + for (ppp = ppp_list; ppp != 0; ppp = next_ppp) { + next_ppp = ppp->next; + unregister_netdev(&ppp->dev); + kfree (ppp); } } #endif