patch-2.3.4 linux/drivers/isdn/isdn_ppp.c

Next file: linux/drivers/isdn/isdn_ppp.h
Previous file: linux/drivers/isdn/isdn_net.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.33 1998/02/20 17:11:54 fritz Exp $
+/* $Id: isdn_ppp.c,v 1.47 1999/04/18 14:06:59 fritz Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -18,11 +18,50 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * Note: This file differs from the corresponding revision as present in the
- * isdn4linux CVS repository because some later bug fixes have been extracted
- * from the repository and merged into this file. -- Henner Eisen
- *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.47  1999/04/18 14:06:59  fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.46  1999/04/12 12:33:35  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.45  1998/12/30 17:48:24  paul
+ * fixed syncPPP callback out
+ *
+ * Revision 1.44  1998/10/30 17:55:34  he
+ * dialmode for x25iface and multulink ppp
+ *
+ * Revision 1.43  1998/10/29 17:23:54  hipp
+ * Minor MPPP fixes, verboser logging.
+ *
+ * Revision 1.42  1998/07/20 11:30:07  hipp
+ * Readded compression check
+ *
+ * Revision 1.41  1998/07/08 16:50:57  hipp
+ * Compression changes
+ *
+ * Revision 1.40  1998/04/06 19:07:27  hipp
+ * added check, whether compression is enabled.
+ *
+ * Revision 1.39  1998/03/25 22:46:53  hipp
+ * Some additional CCP changes.
+ *
+ * Revision 1.38  1998/03/24 16:33:06  hipp
+ * More CCP changes. BSD compression now "works" on a local loopback link.
+ * Moved some isdn_ppp stuff from isdn.h to isdn_ppp.h
+ *
+ * Revision 1.37  1998/03/22 18:50:49  hipp
+ * Added BSD Compression for syncPPP .. UNTESTED at the moment
+ *
+ * Revision 1.36  1998/03/09 17:46:30  he
+ * merged in 2.1.89 changes
+ *
+ * Revision 1.35  1998/03/07 18:21:11  cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
+ * Revision 1.34  1998/02/25 17:49:48  he
+ * Changed return codes caused be failing copy_{to,from}_user to -EFAULT
+ *
  * Revision 1.33  1998/02/20 17:11:54  fritz
  * Changes for recent kernels.
  *
@@ -157,13 +196,16 @@
  * experimental for dynamic addressing: readdress IP frames
  */
 #undef ISDN_SYNCPPP_READDRESS
+#define CONFIG_ISDN_CCP 1
 
 #include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/version.h>
-#include <linux/isdn.h>
 #include <linux/poll.h>
+#include <linux/isdn.h>
+#include <linux/ppp-comp.h>
+
 #include "isdn_common.h"
 #include "isdn_ppp.h"
 #include "isdn_net.h"
@@ -180,13 +222,33 @@
 static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
 				 struct sk_buff *skb, int proto);
 static int isdn_ppp_if_get_unit(char *namebuf);
-static int isdn_ppp_set_compressor(struct ippp_struct *is,int num);
+static int isdn_ppp_set_compressor(struct ippp_struct *is,struct isdn_ppp_comp_data *);
 static struct sk_buff *isdn_ppp_decompress(struct sk_buff *,
-				struct ippp_struct *,struct ippp_struct *);
+				struct ippp_struct *,struct ippp_struct *,int proto);
 static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp,
-				struct sk_buff *skb);
+				struct sk_buff *skb,int proto);
 static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
 	struct ippp_struct *is,struct ippp_struct *master,int type);
+static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
+	 struct sk_buff *skb);
+
+/* New CCP stuff */
+static void isdn_ppp_ccp_kickup(struct ippp_struct *is);
+static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
+				    unsigned char code, unsigned char id,
+				    unsigned char *data, int len);
+static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is);
+static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
+					  unsigned char id);
+static void isdn_ppp_ccp_timer_callback(unsigned long closure);
+static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
+						      unsigned char id);
+static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
+				     struct isdn_ppp_resetparams *rp);
+static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
+					unsigned char id);
+
+
 
 #ifdef CONFIG_ISDN_MPP
 static int isdn_ppp_bundle(struct ippp_struct *, int unit);
@@ -199,18 +261,16 @@
 static void isdn_ppp_free_mpqueue(isdn_net_dev *);
 #endif
 
-char *isdn_ppp_revision = "$Revision: 1.33 $";
+char *isdn_ppp_revision = "$Revision: 1.47 $";
 
 static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 static struct isdn_ppp_compressor *ipc_head = NULL;
 
-extern int isdn_net_force_dial_lp(isdn_net_local *);
-
 /*
  * frame log (debug)
  */
 static void
-isdn_ppp_frame_log(char *info, char *data, int len, int maxlen)
+isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot)
 {
 	int cnt,
 	 j,
@@ -223,13 +283,14 @@
 	for (i = 0, cnt = 0; cnt < maxlen; i++) {
 		for (j = 0; j < 16 && cnt < maxlen; j++, cnt++)
 			sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]);
-		printk(KERN_DEBUG "%s[%d]: %s\n", info, i, buf);
+		printk(KERN_DEBUG "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf);
 	}
 }
 
 /*
  * unbind isdn_net_local <=> ippp-device
  * note: it can happen, that we hangup/free the master before the slaves
+ *       in this case we bind another lp to the master device
  */
 int
 isdn_ppp_free(isdn_net_local * lp)
@@ -267,8 +328,7 @@
 	if ((is->state & IPPP_CONNECT))
 		isdn_ppp_closewait(lp->ppp_slot);	/* force wakeup on ippp device */
 	else if (is->state & IPPP_ASSIGNED)
-		is->state = IPPP_OPEN;	/* fallback to 'OPEN but not ASSIGEND' staet */
-
+		is->state = IPPP_OPEN;	/* fallback to 'OPEN but not ASSIGNED' state */
 
 	if (is->debug & 0x1)
 		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp);
@@ -320,14 +380,16 @@
 			}
 		}
 	} else {
-		for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-			if (ippp_table[i]->minor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN)
+		for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+			if (ippp_table[i]->minor == lp->pppbind &&
+			    (ippp_table[i]->state & IPPP_OPEN) == IPPP_OPEN)
 				break;
+		}
 	}
 
 	if (i >= ISDN_MAX_CHANNELS) {
 		restore_flags(flags);
-		printk(KERN_WARNING "isdn_ppp_bind: Can't find usable ippp device.\n");
+		printk(KERN_WARNING "isdn_ppp_bind: Can't find a (free) connection to the ipppd daemon.\n");
 		return -1;
 	}
 	unit = isdn_ppp_if_get_unit(lp->name);	/* get unit number from interface name .. ugly! */
@@ -336,6 +398,15 @@
 		return -1;
 	}
 	lp->ppp_slot = i;
+
+	/* reset some values */
+	lp->netdev->ib.bundled = 0;
+	lp->netdev->ib.next_num = 0;
+	lp->netdev->ib.modify = 0;
+	lp->netdev->ib.last = NULL;
+	lp->netdev->ib.min = 0;
+	lp->netdev->ib.sq = NULL;
+
 	is = ippp_table[i];
 	is->lp = lp;
 	is->unit = unit;
@@ -359,7 +430,10 @@
 
 	ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
 
-	wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
+#if LINUX_VERSION_CODE < 131841
+	if (ippp_table[lp->ppp_slot]->wq)
+#endif
+		wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
 }
 
 /*
@@ -376,7 +450,11 @@
 		return 0;
 	is = ippp_table[slot];
 
+#if LINUX_VERSION_CODE < 131841
+	if (is->state && is->wq)
+#else
 	if (is->state)
+#endif
 		wake_up_interruptible(&is->wq);
 
 	is->state = IPPP_CLOSEWAIT;
@@ -417,14 +495,19 @@
 	}
 	is = file->private_data = ippp_table[slot];
 
+#if 0
 	if (is->debug & 0x1)
+#endif
 		printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", slot, min, is->state);
 
 	/* compression stuff */
-	is->compressor = NULL;
-	is->decomp_stat = is->comp_stat = NULL;
-	is->link_compressor = NULL;
-	is->link_decomp_stat = is->link_comp_stat = NULL;
+	is->link_compressor   = is->compressor = NULL;
+	is->link_decompressor = is->decompressor = NULL;
+	is->link_comp_stat    = is->comp_stat = NULL;
+	is->link_decomp_stat  = is->decomp_stat = NULL;
+	is->compflags = 0;
+
+	is->reset = isdn_ppp_ccp_reset_alloc(is);
 
 	is->lp = NULL;
 	is->mp_seqno = 0;       /* MP sequence number */
@@ -436,9 +519,11 @@
 	is->mru = 1524;         /* MRU, default 1524 */
 	is->maxcid = 16;        /* VJ: maxcid */
 	is->tk = current;
-	/* next two are redundant, but be paranoid */ 
-	init_waitqueue_head(&is->wq);  /* read() wait queue */
-	init_waitqueue_head(&is->wql);  /* select() wait queue */
+#if LINUX_VERSION_CODE < 131841
+	is->wq = NULL;          /* read() wait queue */
+#else
+	init_waitqueue_head(&is->wq);
+#endif
 	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
 	is->last = is->rq;
 	is->minor = min;
@@ -491,10 +576,30 @@
 	is->last = is->rq;
 
 #ifdef CONFIG_ISDN_PPP_VJ
+/* TODO: if this was the previous master: link the slcomp to the new master */
 	slhc_free(is->slcomp);
 	is->slcomp = NULL;
 #endif
 
+/* TODO: if this was the previous master: link the the stuff to the new master */
+	if(is->comp_stat)
+		is->compressor->free(is->comp_stat);
+	if(is->link_comp_stat)
+		is->link_compressor->free(is->link_comp_stat);
+	if(is->link_decomp_stat)
+		is->link_decompressor->free(is->link_decomp_stat);
+	if(is->decomp_stat)
+		is->decompressor->free(is->decomp_stat);
+        is->compressor   = is->link_compressor   = NULL;
+        is->decompressor = is->link_decompressor = NULL;
+	is->comp_stat    = is->link_comp_stat    = NULL;
+        is->decomp_stat  = is->link_decomp_stat  = NULL;
+
+	if(is->reset)
+		kfree(is->reset);
+	is->reset = NULL;
+
+	/* this slot is ready for new connections */
 	is->state = 0;
 }
 
@@ -505,7 +610,7 @@
 get_arg(void *b, void *val, int len)
 {
 	if (len <= 0)
-		len = sizeof(unsigned long);
+		len = sizeof(void *);
 	if (copy_from_user((void *) val, b, len))
 		return -EFAULT;
 	return 0;
@@ -515,15 +620,12 @@
  * set arg .. ioctl helper
  */
 static int
-set_arg(void *b, unsigned long val, void *str)
+set_arg(void *b, void *val,int len)
 {
-	if (!str) {
-		if (copy_to_user(b, (void *) &val, 4))
-			return -EFAULT;
-	} else {
-		if (copy_to_user(b, str, val))
-			return -EFAULT;
-	}
+	if(len <= 0)
+		len = sizeof(void *);
+	if (copy_to_user(b, (void *) val, len))
+		return -EFAULT;
 	return 0;
 }
 
@@ -534,9 +636,10 @@
 isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	unsigned long val;
-	int num,r;
+	int r,i,j;
 	struct ippp_struct *is;
 	isdn_net_local *lp;
+	struct isdn_ppp_comp_data data;
 
 	is = (struct ippp_struct *) file->private_data;
 	lp = is->lp;
@@ -552,7 +655,7 @@
 #ifdef CONFIG_ISDN_MPP
 			if (!(is->state & IPPP_CONNECT))
 				return -EINVAL;
-			if ((r = get_arg((void *) arg, &val, 0)))
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
 				return r;
 			printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
 			       (int) min, (int) is->unit, (int) val);
@@ -562,24 +665,30 @@
 #endif
 			break;
 		case PPPIOCGUNIT:	/* get ppp/isdn unit number */
-			if ((r = set_arg((void *) arg, is->unit, NULL)))
+			if ((r = set_arg((void *) arg, &is->unit, sizeof(is->unit) )))
+				return r;
+			break;
+		case PPPIOCGIFNAME:
+			if(!lp)
+				return -EINVAL;
+			if ((r = set_arg((void *) arg, lp->name,strlen(lp->name))))
 				return r;
 			break;
 		case PPPIOCGMPFLAGS:	/* get configuration flags */
-			if ((r = set_arg((void *) arg, is->mpppcfg, NULL)))
+			if ((r = set_arg((void *) arg, &is->mpppcfg, sizeof(is->mpppcfg) )))
 				return r;
 			break;
 		case PPPIOCSMPFLAGS:	/* set configuration flags */
-			if ((r = get_arg((void *) arg, &val, 0)))
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
 				return r;
 			is->mpppcfg = val;
 			break;
 		case PPPIOCGFLAGS:	/* get configuration flags */
-			if ((r = set_arg((void *) arg, is->pppcfg, NULL)))
+			if ((r = set_arg((void *) arg, &is->pppcfg,sizeof(is->pppcfg) )))
 				return r;
 			break;
 		case PPPIOCSFLAGS:	/* set configuration flags */
-			if ((r = get_arg((void *) arg, &val, 0))) {
+			if ((r = get_arg((void *) arg, &val, sizeof(val) ))) {
 				return r;
 			}
 			if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) {
@@ -598,12 +707,12 @@
 			if (lp) {
 				struct ppp_idle pidle;
 				pidle.xmit_idle = pidle.recv_idle = lp->huptimer;
-				if ((r = set_arg((void *) arg, sizeof(struct ppp_idle), &pidle)))
+				if ((r = set_arg((void *) arg, &pidle,sizeof(struct ppp_idle))))
 					 return r;
 			}
 			break;
 		case PPPIOCSMRU:	/* set receive unit size for PPP */
-			if ((r = get_arg((void *) arg, &val, 0)))
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
 				return r;
 			is->mru = val;
 			break;
@@ -612,7 +721,7 @@
 		case PPPIOCSMPMTU:
 			break;
 		case PPPIOCSMAXCID:	/* set the maximum compression slot id */
-			if ((r = get_arg((void *) arg, &val, 0)))
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
 				return r;
 			val++;
 			if (is->maxcid != val) {
@@ -635,31 +744,33 @@
 			}
 			break;
 		case PPPIOCGDEBUG:
-			if ((r = set_arg((void *) arg, is->debug, 0)))
+			if ((r = set_arg((void *) arg, &is->debug, sizeof(is->debug) )))
 				return r;
 			break;
 		case PPPIOCSDEBUG:
-			if ((r = get_arg((void *) arg, &val, 0)))
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
 				return r;
 			is->debug = val;
 			break;
 		case PPPIOCGCOMPRESSORS:
 			{
-				unsigned long protos = 0;
+				unsigned long protos[8] = {0,};
 				struct isdn_ppp_compressor *ipc = ipc_head;
 				while(ipc) {
-					protos |= (0x1<<ipc->num);
+					j = ipc->num / (sizeof(long)*8);
+					i = ipc->num % (sizeof(long)*8);
+					if(j < 8)
+						protos[j] |= (0x1<<i);
 					ipc = ipc->next;
 				}
-				if ((r = set_arg((void *) arg, protos, 0)))
+				if ((r = set_arg((void *) arg,protos,8*sizeof(long) )))
 					return r;
 			}
 			break;
 		case PPPIOCSCOMPRESSOR:
-			if ((r = get_arg((void *) arg, &num, sizeof(int))))
+			if ((r = get_arg((void *) arg, &data, sizeof(struct isdn_ppp_comp_data))))
 				return r;
-			return isdn_ppp_set_compressor(is, num);
-			break;
+			return isdn_ppp_set_compressor(is, &data);
 		case PPPIOCGCALLINFO:
 			{
 				struct pppcallinfo pci;
@@ -678,7 +789,7 @@
 					if(lp->flags & ISDN_NET_CALLBACK)
 						pci.calltype |= CALLTYPE_CALLBACK;
 				}
-				return set_arg((void *)arg,sizeof(struct pppcallinfo),&pci);
+				return set_arg((void *)arg,&pci,sizeof(struct pppcallinfo));
 			}
 		default:
 			break;
@@ -701,9 +812,12 @@
 		printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n",
 				MINOR(file->f_dentry->d_inode->i_rdev));
 
+	/* just registers wait_queue hook. This doesn't really wait. */
 	poll_wait(file, &is->wq, wait);
 
 	if (!(is->state & IPPP_OPEN)) {
+		if(is->state == IPPP_CLOSEWAIT)
+			return POLLHUP;
 		printk(KERN_DEBUG "isdn_ppp: device not open\n");
 		return POLLERR;
 	}
@@ -777,7 +891,10 @@
 	is->last = bl->next;
 	restore_flags(flags);
 
-	wake_up_interruptible(&is->wq);
+#if LINUX_VERSION_CODE < 131841
+	if (is->wq)
+#endif
+		wake_up_interruptible(&is->wq);
 
 	return len;
 }
@@ -863,7 +980,8 @@
 		if (lp->isdn_device < 0 || lp->isdn_channel < 0)
 			return 0;
 
-		if (dev->drv[lp->isdn_device]->running && lp->dialstate == 0 &&
+		if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) &&
+			lp->dialstate == 0 &&
 		    (lp->flags & ISDN_NET_CONNECTED)) {
 			int cnt;
 			struct sk_buff *skb;
@@ -876,8 +994,11 @@
 				return -EFAULT;
 			if (is->debug & 0x40) {
 				printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
-				isdn_ppp_frame_log("xmit", skb->data, skb->len, 32);
+				isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
 			}
+
+			isdn_ppp_send_ccp(lp->netdev,lp,skb); /* keeps CCP/compression states in sync */
+
 			if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb)) != count) {
 				if (lp->sav_skb) {
 					dev_kfree_skb(lp->sav_skb);
@@ -910,8 +1031,6 @@
 			return -1;
 		}
 		memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
-		init_waitqueue_head(&ippp_table[i]->wq); 
-		init_waitqueue_head(&ippp_table[i]->wql); 
 		ippp_table[i]->state = 0;
 		ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
 		ippp_table[i]->last = ippp_table[i]->rq;
@@ -963,8 +1082,9 @@
 	is = ippp_table[lp->ppp_slot];
 
 	if (is->debug & 0x4) {
-		printk(KERN_DEBUG "ippp_receive: len: %d\n", (int) skb->len);
-		isdn_ppp_frame_log("receive", skb->data, skb->len, 32);
+		printk(KERN_DEBUG "ippp_receive: is:%08lx lp:%08lx slot:%d unit:%d len:%d\n",
+		       (long)is,(long)lp,lp->ppp_slot,is->unit,(int) skb->len);
+		isdn_ppp_frame_log("receive", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
 	}
 	if (net_dev->local->master) {
 		printk(KERN_WARNING "isdn_ppp_receice: net_dev != master\n");
@@ -982,13 +1102,18 @@
 #ifdef CONFIG_ISDN_MPP
 	if (!(is->mpppcfg & SC_REJ_MP_PROT)) {
 		int sqno_end;
-		
-		if(proto == PPP_LINK_COMP) {
-			printk(KERN_DEBUG "received single link compressed frame\n");
-			skb = isdn_ppp_decompress(skb,is,NULL);
-			if(!skb)
-				return;
-			proto = isdn_ppp_strip_proto(skb);
+
+		if(is->compflags & SC_LINK_DECOMP_ON) {	
+			if(proto == PPP_LINK_COMP) {
+				if(is->debug & 0x10)
+					printk(KERN_DEBUG "received single link compressed frame\n");
+				skb = isdn_ppp_decompress(skb,is,NULL,proto);
+				if(!skb)
+					return;
+				proto = isdn_ppp_strip_proto(skb);
+			}
+			else
+				isdn_ppp_decompress(skb,is,NULL,proto);
 		}
 
 		if (proto == PPP_MP) {
@@ -1055,7 +1180,8 @@
 				}
 				min_sqno &= mask;
 				for (lpq = net_dev->queue;;) {
-					ippp_table[lpq->ppp_slot]->last_link_seqno &= mask;
+					if(ippp_table[lpq->ppp_slot]->last_link_seqno >= 0)
+						ippp_table[lpq->ppp_slot]->last_link_seqno &= mask;
 					lpq = lpq->next;
 					if (lpq == net_dev->queue)
 						break;
@@ -1150,17 +1276,31 @@
 
 	if (is->debug & 0x10) {
 		printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
-		isdn_ppp_frame_log("rpush", skb->data, skb->len, 32);
+		isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
 	}
 
 	if(proto == PPP_COMP) {
 		if(!lp->master)
-			skb = isdn_ppp_decompress(skb,is,is);
+			skb = isdn_ppp_decompress(skb,is,is,proto);
 		else
-			skb = isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot]);
-		if(!skb)
+			skb = isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot],proto);
+
+		if(!skb) {
+			printk(KERN_DEBUG "ippp: compressed frame discarded!\n");
 			return;
+		}
+
 		proto = isdn_ppp_strip_proto(skb);
+		if (is->debug & 0x10) {
+			printk(KERN_DEBUG "RPostDecomp, skb %d %04x\n", (int) skb->len, proto);
+			isdn_ppp_frame_log("R-Decomp", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
+		}
+	}
+	else if(is->compflags & SC_DECOMP_ON)  { /* If decomp is ON */
+		if(!lp->master)
+			isdn_ppp_decompress(skb,is,is,proto);
+		else
+			isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot],proto);
 	}
 
 	switch (proto) {
@@ -1227,7 +1367,13 @@
 #endif
 			break;
 		case PPP_CCP:
-			isdn_ppp_receive_ccp(net_dev,lp,skb);
+		case PPP_LINK_CCP:
+			isdn_ppp_receive_ccp(net_dev,lp,skb,proto);
+			/* Dont pop up ResetReq/Ack stuff to the daemon any
+			   longer - the job is done already */
+			if(skb->data[0] == CCP_RESETREQ ||
+			   skb->data[0] == CCP_RESETACK)
+				break;
 			/* fall through */
 		default:
 			isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot);	/* push data to pppd device */
@@ -1235,10 +1381,10 @@
 			return;
 	}
 
+ 	/* Reset hangup-timer */
+ 	lp->huptimer = 0;
 	netif_rx(skb);
 	/* net_dev->local->stats.rx_packets++; *//* done in isdn_net.c */
-	/* Reset hangup-timer */
-	lp->huptimer = 0;
 
 	return;
 }
@@ -1339,7 +1485,6 @@
 		lp = nlp;
 	}
 	ipt = ippp_table[lp->ppp_slot];
-
 	lp->huptimer = 0;
 
 	/*
@@ -1355,6 +1500,8 @@
 
 	if (ipt->debug & 0x4)
 		printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len);
+        if (ipts->debug & 0x40)
+                isdn_ppp_frame_log("xmit0", skb->data, skb->len, 32,ipts->unit,lp->ppp_slot);
 
 #ifdef CONFIG_ISDN_PPP_VJ
 	if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes, but check this again */
@@ -1394,10 +1541,11 @@
 	}
 #endif
 
-    /*
-     * normal or bundle compression
-     */
-	skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
+	/*
+	 * normal (single link) or bundle compression
+	 */
+	if(ipts->compflags & SC_COMP_ON)
+		skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
 
 	if (ipt->debug & 0x24)
 		printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto);
@@ -1431,9 +1579,10 @@
 #endif
 
 	/*
-	 * 'link' compression 
+	 * 'link in bundle' compression  ...
 	 */
-	skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
+	if(ipt->compflags & SC_LINK_COMP_ON)
+		skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
 
 	if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) {
 		unsigned char *data = isdn_ppp_skb_push(&skb,1);
@@ -1460,7 +1609,7 @@
 
 	if (ipts->debug & 0x40) {
 		printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len);
-		isdn_ppp_frame_log("xmit", skb->data, skb->len, 32);
+		isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,lp->ppp_slot);
 	}
 	if (isdn_net_send_skb(dev, lp, skb)) {
 		if (lp->sav_skb) {	/* whole sav_skb processing with disabled IRQs ?? */
@@ -1474,6 +1623,12 @@
 
 #ifdef CONFIG_ISDN_MPP
 
+/*
+ * free SQ queue
+ * -------------
+ * Note: We need two queues for MPPP. The SQ queue holds fully (re)assembled frames,
+ * that can't be delivered, because there is an outstanding earlier frame
+ */
 static void
 isdn_ppp_free_sqqueue(isdn_net_dev * p)
 {
@@ -1490,6 +1645,12 @@
 
 }
 
+/*
+ * free MP queue
+ * -------------
+ * Note: The MP queue holds all frame fragments of frames, that can't be
+ * reassembled, because there is at least one missing fragment.
+ */
 static void 
 isdn_ppp_free_mpqueue(isdn_net_dev * p)
 {
@@ -1551,7 +1712,9 @@
 	return 0;
 }
 
-
+/*
+ * Mask sequence numbers in MP queue
+ */
 static void
 isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
 {
@@ -1562,6 +1725,11 @@
 	}
 }
 
+/*
+ * put a fragment at the right place into the MP queue 
+ * Also checks, whether this fragment completes a frame. In this case
+ * the fragments are copied together into one SKB
+ */
 static int
 isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff **skb, int BEbyte, long *sqnop, int min_sqno)
 {
@@ -1762,13 +1930,11 @@
 		slhc_toss(ippp_table[dev->local->ppp_slot]->slcomp);
 #endif
 }
+#endif
 
 /*
  * a buffered packet timed-out?
  */
-
-#endif
-
 void
 isdn_ppp_timer_timeout(void)
 {
@@ -1940,7 +2106,7 @@
 	if (!sdev)
 		return 2;
 
-	isdn_net_force_dial_lp((isdn_net_local *) sdev->priv);
+	isdn_net_dial_req((isdn_net_local *) sdev->priv);
 	return 0;
 #else
 	return -1;
@@ -1981,47 +2147,440 @@
 /*
  * PPP compression stuff
  */
-static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master)
+
+
+/* Push an empty CCP Data Frame up to the daemon to wake it up and let it
+   generate a CCP Reset-Request or tear down CCP altogether */
+
+static void isdn_ppp_ccp_kickup(struct ippp_struct *is)
+{
+	isdn_ppp_fill_rq(NULL, 0, PPP_COMP, is->lp->ppp_slot);
+}
+
+/* In-kernel handling of CCP Reset-Request and Reset-Ack is necessary,
+   but absolutely nontrivial. The most abstruse problem we are facing is
+   that the generation, reception and all the handling of timeouts and
+   resends including proper request id management should be entirely left
+   to the (de)compressor, but indeed is not covered by the current API to
+   the (de)compressor. The API is a prototype version from PPP where only
+   some (de)compressors have yet been implemented and all of them are
+   rather simple in their reset handling. Especially, their is only one
+   outstanding ResetAck at a time with all of them and ResetReq/-Acks do
+   not have parameters. For this very special case it was sufficient to
+   just return an error code from the decompressor and have a single
+   reset() entry to communicate all the necessary information between
+   the framework and the (de)compressor. Bad enough, LZS is different
+   (and any other compressor may be different, too). It has multiple
+   histories (eventually) and needs to Reset each of them independently
+   and thus uses multiple outstanding Acks and history numbers as an
+   additional parameter to Reqs/Acks.
+   All that makes it harder to port the reset state engine into the
+   kernel because it is not just the same simple one as in (i)pppd but
+   it must be able to pass additional parameters and have multiple out-
+   standing Acks. We are trying to achieve the impossible by handling
+   reset transactions independent by their id. The id MUST change when
+   the data portion changes, thus any (de)compressor who uses more than
+   one resettable state must provide and recognize individual ids for
+   each individual reset transaction. The framework itself does _only_
+   differentiate them by id, because it has no other semantics like the
+   (de)compressor might.
+   This looks like a major redesign of the interface would be nice,
+   but I don't have an idea how to do it better. */
+
+/* Send a CCP Reset-Request or Reset-Ack directly from the kernel. This is
+   getting that lengthy because there is no simple "send-this-frame-out"
+   function above but every wrapper does a bit different. Hope I guess
+   correct in this hack... */
+
+static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
+				    unsigned char code, unsigned char id,
+				    unsigned char *data, int len)
+{
+	struct sk_buff *skb;
+	unsigned char *p;
+	int count;
+	int cnt = 0;
+	isdn_net_local *lp = is->lp;
+
+	/* Alloc large enough skb */
+	skb = dev_alloc_skb(len + 16);
+	if(!skb) {
+		printk(KERN_WARNING
+		       "ippp: CCP cannot send reset - out of memory\n");
+		return;
+	}
+
+	/* We may need to stuff an address and control field first */
+	if(!(is->pppcfg & SC_COMP_AC)) {
+		p = skb_put(skb, 2);
+		*p++ = 0xff;
+		*p++ = 0x03;
+	}
+
+	/* Stuff proto, code, id and length */
+	p = skb_put(skb, 6);
+	*p++ = (proto >> 8);
+	*p++ = (proto & 0xff);
+	*p++ = code;
+	*p++ = id;
+	cnt = 4 + len;
+	*p++ = (cnt >> 8);
+	*p++ = (cnt & 0xff);
+
+	/* Now stuff remaining bytes */
+	if(len) {
+		p = skb_put(skb, len);
+		memcpy(p, data, len);
+	}
+
+	/* skb is now ready for xmit */
+	printk(KERN_DEBUG "Sending CCP Frame:\n");
+	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+	/* Just ripped from isdn_ppp_write. Dunno whether it makes sense,
+	   especially dunno what the sav_skb stuff is good for. */
+
+	count = skb->len;
+	if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel,
+					  1, skb)) != count) {
+		if (lp->sav_skb) {
+			dev_kfree_skb(lp->sav_skb);
+			printk(KERN_INFO
+			       "isdn_ppp_write: freeing sav_skb (%d,%d)!\n",
+			       cnt, count);
+		} else
+			printk(KERN_INFO
+			       "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n",
+			       cnt, count);
+		lp->sav_skb = skb;
+	}
+}
+
+/* Allocate the reset state vector */
+static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is)
+{
+	struct ippp_ccp_reset *r;
+	printk(KERN_DEBUG "ippp_ccp: allocating reset data structure\n");
+	r = kmalloc(sizeof(struct ippp_ccp_reset), GFP_KERNEL);
+	if(!r)
+		return NULL;
+	memset(r, 0, sizeof(struct ippp_ccp_reset));
+	is->reset = r;
+	return r;
+}
+
+/* Free a given state and clear everything up for later reallocation */
+static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
+					  unsigned char id)
+{
+	struct ippp_ccp_reset_state *rs;
+
+	if(is->reset->rs[id]) {
+		printk(KERN_DEBUG "ippp_ccp: freeing state for id %d\n", id);
+		rs = is->reset->rs[id];
+		/* Make sure the kernel will not call back later */
+		if(rs->ta)
+			del_timer(&rs->timer);
+		is->reset->rs[id] = NULL;
+		kfree(rs);
+	} else {
+		printk(KERN_WARNING "ippp_ccp: id %d is not allocated\n", id);
+	}
+}
+
+/* The timer callback function which is called when a ResetReq has timed out,
+   aka has never been answered by a ResetAck */
+static void isdn_ppp_ccp_timer_callback(unsigned long closure)
+{
+	struct ippp_ccp_reset_state *rs =
+		(struct ippp_ccp_reset_state *)closure;
+
+	if(!rs) {
+		printk(KERN_ERR "ippp_ccp: timer cb with zero closure.\n");
+		return;
+	}
+	if(rs->ta && rs->state == CCPResetSentReq) {
+		/* We are correct here */
+		printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n",
+		       rs->id);
+		if(!rs->expra) {
+			/* Hmm, there is no Ack really expected. We can clean
+			   up the state now, it will be reallocated if the
+			   decompressor insists on another reset */
+			rs->ta = 0;
+			isdn_ppp_ccp_reset_free_state(rs->is, rs->id);
+			return;
+		}
+		/* Push it again */
+		isdn_ppp_ccp_xmit_reset(rs->is, PPP_CCP, CCP_RESETREQ, rs->id,
+					rs->data, rs->dlen);
+		/* Restart timer */
+		rs->timer.expires = jiffies + HZ*5;
+		add_timer(&rs->timer);
+	} else {
+		printk(KERN_WARNING "ippp_ccp: timer cb in wrong state %d\n",
+		       rs->state);
+	}
+}
+
+/* Allocate a new reset transaction state */
+static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
+						      unsigned char id)
+{
+	struct ippp_ccp_reset_state *rs;
+	if(is->reset->rs[id]) {
+		printk(KERN_WARNING "ippp_ccp: old state exists for id %d\n",
+		       id);
+		return NULL;
+	} else {
+		rs = kmalloc(sizeof(struct ippp_ccp_reset_state), GFP_KERNEL);
+		if(!rs)
+			return NULL;
+		memset(rs, 0, sizeof(struct ippp_ccp_reset_state));
+		rs->state = CCPResetIdle;
+		rs->is = is;
+		rs->id = id;
+		rs->timer.data = (unsigned long)rs;
+		rs->timer.function = isdn_ppp_ccp_timer_callback;
+		is->reset->rs[id] = rs;
+	}
+	return rs;
+}
+
+
+/* A decompressor wants a reset with a set of parameters - do what is
+   necessary to fulfill it */
+static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
+				     struct isdn_ppp_resetparams *rp)
+{
+	struct ippp_ccp_reset_state *rs;
+
+	if(rp->valid) {
+		/* The decompressor defines parameters by itself */
+		if(rp->rsend) {
+			/* And he wants us to send a request */
+			if(!(rp->idval)) {
+				printk(KERN_ERR "ippp_ccp: decompressor must"
+				       " specify reset id\n");
+				return;
+			}
+			if(is->reset->rs[rp->id]) {
+				/* There is already a transaction in existence
+				   for this id. May be still waiting for a
+				   Ack or may be wrong. */
+				rs = is->reset->rs[rp->id];
+				if(rs->state == CCPResetSentReq && rs->ta) {
+					printk(KERN_DEBUG "ippp_ccp: reset"
+					       " trans still in progress"
+					       " for id %d\n", rp->id);
+				} else {
+					printk(KERN_WARNING "ippp_ccp: reset"
+					       " trans in wrong state %d for"
+					       " id %d\n", rs->state, rp->id);
+				}
+			} else {
+				/* Ok, this is a new transaction */
+				printk(KERN_DEBUG "ippp_ccp: new trans for id"
+				       " %d to be started\n", rp->id);
+				rs = isdn_ppp_ccp_reset_alloc_state(is, rp->id);
+				if(!rs) {
+					printk(KERN_ERR "ippp_ccp: out of mem"
+					       " allocing ccp trans\n");
+					return;
+				}
+				rs->state = CCPResetSentReq;
+				rs->expra = rp->expra;
+				if(rp->dtval) {
+					rs->dlen = rp->dlen;
+					memcpy(rs->data, rp->data, rp->dlen);
+				}
+				/* HACK TODO - add link comp here */
+				isdn_ppp_ccp_xmit_reset(is, PPP_CCP,
+							CCP_RESETREQ, rs->id,
+							rs->data, rs->dlen);
+				/* Start the timer */
+				rs->timer.expires = jiffies + 5*HZ;
+				add_timer(&rs->timer);
+				rs->ta = 1;
+			}
+		} else {
+			printk(KERN_DEBUG "ippp_ccp: no reset sent\n");
+		}
+	} else {
+		/* The reset params are invalid. The decompressor does not
+		   care about them, so we just send the minimal requests
+		   and increase ids only when an Ack is received for a
+		   given id */
+		if(is->reset->rs[is->reset->lastid]) {
+			/* There is already a transaction in existence
+			   for this id. May be still waiting for a
+			   Ack or may be wrong. */
+			rs = is->reset->rs[is->reset->lastid];
+			if(rs->state == CCPResetSentReq && rs->ta) {
+				printk(KERN_DEBUG "ippp_ccp: reset"
+				       " trans still in progress"
+				       " for id %d\n", rp->id);
+			} else {
+				printk(KERN_WARNING "ippp_ccp: reset"
+				       " trans in wrong state %d for"
+				       " id %d\n", rs->state, rp->id);
+			}
+		} else {
+			printk(KERN_DEBUG "ippp_ccp: new trans for id"
+			       " %d to be started\n", is->reset->lastid);
+			rs = isdn_ppp_ccp_reset_alloc_state(is,
+							    is->reset->lastid);
+			if(!rs) {
+				printk(KERN_ERR "ippp_ccp: out of mem"
+				       " allocing ccp trans\n");
+				return;
+			}
+			rs->state = CCPResetSentReq;
+			/* We always expect an Ack if the decompressor doesnt
+			   know	better */
+			rs->expra = 1;
+			rs->dlen = 0;
+			/* HACK TODO - add link comp here */
+			isdn_ppp_ccp_xmit_reset(is, PPP_CCP, CCP_RESETREQ,
+						rs->id, NULL, 0);
+			/* Start the timer */
+			rs->timer.expires = jiffies + 5*HZ;
+			add_timer(&rs->timer);
+			rs->ta = 1;
+		}
+	}
+}
+
+/* An Ack was received for this id. This means we stop the timer and clean
+   up the state prior to calling the decompressors reset routine. */
+static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
+					unsigned char id)
+{
+	struct ippp_ccp_reset_state *rs = is->reset->rs[id];
+
+	if(rs) {
+		if(rs->ta && rs->state == CCPResetSentReq) {
+			/* Great, we are correct */
+			if(!rs->expra)
+				printk(KERN_DEBUG "ippp_ccp: ResetAck received"
+				       " for id %d but not expected\n", id);
+		} else {
+			printk(KERN_INFO "ippp_ccp: ResetAck received out of"
+			       "sync for id %d\n", id);
+		}
+		if(rs->ta) {
+			rs->ta = 0;
+			del_timer(&rs->timer);
+		}
+		isdn_ppp_ccp_reset_free_state(is, id);
+	} else {
+		printk(KERN_INFO "ippp_ccp: ResetAck received for unknown id"
+		       " %d\n", id);
+	}
+	/* Make sure the simple reset stuff uses a new id next time */
+	is->reset->lastid++;
+}
+
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master,
+	int proto)
 {
-#if 1
-	printk(KERN_ERR "compression not included!\n");
-	dev_kfree_skb(skb);
-	return NULL;
+#ifndef CONFIG_ISDN_CCP
+	if(proto == PPP_COMP || proto == PPP_LINK_COMP) {
+		printk(KERN_ERR "isdn_ppp: Ouch! Compression not included!\n");
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	return skb;
 #else
+	void *stat = NULL;
+	struct isdn_ppp_compressor *ipc = NULL;
+	struct sk_buff *skb_out;
+	int len;
+	struct ippp_struct *ri;
+	struct isdn_ppp_resetparams rsparm;
+	unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
+
 	if(!master) {
 		/* 
-		 * single link compression 
+		 * single link decompression 
 		 */
-		if(!is->link_compressor) {
-			printk(KERN_ERR "ippp: no (link) compressor defined!\n");
+		if(!is->link_decompressor) {
+			printk(KERN_ERR "ippp: no link decompressor defined!\n");
 			dev_kfree_skb(skb);
 			return NULL;
 		}
 		if(!is->link_decomp_stat) {
-			printk(KERN_DEBUG "ippp: initialize link compressor\n");
+			printk(KERN_DEBUG "ippp: no link decompressor data allocated\n");
+			dev_kfree_skb(skb);
+			return NULL;
 		}
-/*
-		-> decompress link
-*/
-    }
+		stat = is->link_decomp_stat;
+		ipc = is->link_decompressor;
+		ri = is;
+	}
 	else {
 		/*
 		 * 'normal' or bundle-compression 
 		 */
-		if(!master->compressor) {
-			printk(KERN_ERR "ippp: no (link) compressor defined!\n");
+		if(!master->decompressor) {
+			printk(KERN_ERR "ippp: no decompressor defined!\n");
 			dev_kfree_skb(skb);
 			return NULL;
 		}
 		if(!master->decomp_stat) {
-#if 0
-			master->decomp_stat = (master->compressor->decomp_alloc)( .. );
-#endif
-			printk(KERN_DEBUG "ippp: initialize compressor\n");
+			printk(KERN_DEBUG "ippp: no decompressor data allocated\n");
+			dev_kfree_skb(skb);
+			return NULL;
 		}
+		stat = master->decomp_stat;
+		ipc = master->decompressor;
+		ri = master;
+	}
+
+	/*
+	printk(KERN_DEBUG "ippp: Decompress valid!\n");
+	*/
+
+	if((master && proto == PPP_COMP) || (!master && proto == PPP_LINK_COMP) ) {
+		/* Set up reset params for the decompressor */
+		memset(&rsparm, 0, sizeof(rsparm));
+		rsparm.data = rsdata;
+		rsparm.maxdlen = IPPP_RESET_MAXDATABYTES;
+
+/* !!!HACK,HACK,HACK!!! 2048 is only assumed */
+		skb_out = dev_alloc_skb(2048);
+		len = ipc->decompress(stat,skb,skb_out, &rsparm);
+		dev_kfree_skb(skb);
+		if(len <= 0) {
+		  /* Ok, some error */
+		  switch(len) {
+		  case DECOMP_ERROR:
+		    ri->pppcfg |= SC_DC_ERROR;
+		    printk(KERN_INFO "ippp: decomp wants reset %s params\n",
+			   rsparm.valid ? "with" : "without");
+
+		    isdn_ppp_ccp_reset_trans(ri, &rsparm);
+
+		    break;
+		  case DECOMP_FATALERROR:
+		    ri->pppcfg |= SC_DC_FERROR;
+		    /* Kick ipppd to recognize the error */
+		    isdn_ppp_ccp_kickup(ri);
+		    break;
+		  }
+		  /* Did I see a leak here ? */
+		  dev_kfree_skb(skb_out);
+		  return NULL;
+		}
+		return skb_out;
+	}
+	else {
+		/*
+		printk(KERN_DEBUG "isdn_ppp: [%d] Calling incomp with this frame!\n",is->unit);
+		*/
+		ipc->incomp(stat,skb,proto);
+		return skb;
 	}
-	
-	return skb;
 #endif
 }
 
@@ -2035,19 +2594,29 @@
 static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
 	struct ippp_struct *is,struct ippp_struct *master,int type)
 {
-#if 1	
-	return skb_in;
-#else
     int ret;
     int new_proto;
     struct isdn_ppp_compressor *compressor;
     void *stat;
     struct sk_buff *skb_out;
 
+#ifdef CONFIG_ISDN_CCP
+	/* we do not compress control protocols */
+    if(*proto < 0 || *proto > 0x3fff) {
+#else
+    {
+#endif
+      return skb_in;
+    }
+
 	if(type) { /* type=1 => Link compression */
+#if 0
 		compressor = is->link_compressor;
 		stat = is->link_comp_stat;
 		new_proto = PPP_LINK_COMP;
+#else
+		return skb_in;
+#endif
 	}
 	else {
 		if(!master) {
@@ -2062,15 +2631,16 @@
 	}
 
 	if(!compressor) {
-		printk(KERN_ERR "No compressor set!\n");
+		printk(KERN_ERR "isdn_ppp: No compressor set!\n");
 		return skb_in;
 	}
 	if(!stat) {
-		/* init here ? */
+		printk(KERN_ERR "isdn_ppp: Compressor not initialized?\n");
 		return skb_in;
 	}
 
-	skb_out = dev_alloc_skb(skb_in->len);
+	/* Allow for at least 150 % expansion (for now) */
+	skb_out = dev_alloc_skb(skb_in->len + skb_in->len/2 + 32);
 	if(!skb_out)
 		return skb_in;
 
@@ -2083,24 +2653,225 @@
 	dev_kfree_skb(skb_in);
 	*proto = new_proto;
 	return skb_out;
-#endif
-
 }
 
 /*
  * we received a CCP frame .. 
- * not a clean solution, but we SHOULD handle a few cased in the kernel
+ * not a clean solution, but we MUST handle a few cases in the kernel
  */
 static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
-	 struct sk_buff *skb)
+	 struct sk_buff *skb,int proto)
 {
-#if 0
-	printk(KERN_DEBUG "isdn_ppp_receive_cpp: %02x %02x %02x %02x %02x %02x %02x %02x\n",
-		skb->data[0],skb->data[1],skb->data[2],skb->data[3],
-		skb->data[4],skb->data[5],skb->data[6],skb->data[7] );
-#endif
+	struct ippp_struct *is = ippp_table[lp->ppp_slot];
+	struct ippp_struct *mis;
+	int len;
+	struct isdn_ppp_resetparams rsparm;
+	unsigned char rsdata[IPPP_RESET_MAXDATABYTES];	
+
+	printk(KERN_DEBUG "Received CCP frame from peer\n");
+	isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+	if(lp->master)
+		mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot];
+	else
+		mis = is;
+
+	switch(skb->data[0]) {
+	case CCP_CONFREQ:
+	case CCP_TERMREQ:
+	case CCP_TERMACK:
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Disable (de)compression here!\n");
+		if(proto == PPP_CCP)
+			mis->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);		
+		else
+			is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);		
+		break;
+	case CCP_CONFACK:
+		/* if we RECEIVE an ackowledge we enable the decompressor */
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Enable decompression here!\n");
+		if(proto == PPP_CCP)
+			mis->compflags |= SC_DECOMP_ON;
+		else
+			is->compflags |= SC_LINK_DECOMP_ON;
+		break;
+
+	case CCP_RESETACK:
+		printk(KERN_DEBUG "Received ResetAck from peer\n");
+		len = (skb->data[2] << 8) | skb->data[3];
+		len -= 4;
+
+		if(proto == PPP_CCP) {
+			/* If a reset Ack was outstanding for this id, then
+			   clean up the state engine */
+			isdn_ppp_ccp_reset_ack_rcvd(mis, skb->data[1]);
+			if(mis->decompressor && mis->decomp_stat)
+				mis->decompressor->
+					reset(mis->decomp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, NULL);
+			/* TODO: This is not easy to decide here */
+			mis->compflags &= ~SC_DECOMP_DISCARD;
+			mis->pppcfg &= ~SC_DC_ERROR;
+		}
+		else {
+			isdn_ppp_ccp_reset_ack_rcvd(is, skb->data[1]);
+			if(is->link_decompressor && is->link_decomp_stat)
+				is->link_decompressor->
+					reset(is->link_decomp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, NULL);
+			/* TODO: neither here */
+			is->compflags &= ~SC_LINK_DECOMP_DISCARD;
+			is->pppcfg &= ~SC_DC_ERROR;
+		}
+		break;
+
+	case CCP_RESETREQ:
+		printk(KERN_DEBUG "Received ResetReq from peer\n");
+		/* Receiving a ResetReq means we must reset our compressor */
+		/* Set up reset params for the reset entry */
+		memset(&rsparm, 0, sizeof(rsparm));
+		rsparm.data = rsdata;
+		rsparm.maxdlen = IPPP_RESET_MAXDATABYTES; 
+		/* Isolate data length */
+		len = (skb->data[2] << 8) | skb->data[3];
+		len -= 4;
+		if(proto == PPP_CCP) {
+			if(mis->compressor && mis->comp_stat)
+				mis->compressor->
+					reset(mis->comp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, &rsparm);
+		}
+		else {
+			if(is->link_compressor && is->link_comp_stat)
+				is->link_compressor->
+					reset(is->link_comp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, &rsparm);
+		}
+		/* Ack the Req as specified by rsparm */
+		if(rsparm.valid) {
+			/* Compressor reset handler decided how to answer */
+			if(rsparm.rsend) {
+				/* We should send a Frame */
+				isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
+							rsparm.idval ? rsparm.id
+							: skb->data[1],
+							rsparm.dtval ?
+							rsparm.data : NULL,
+							rsparm.dtval ?
+							rsparm.dlen : 0);
+			} else {
+				printk(KERN_DEBUG "ResetAck suppressed\n");
+			}
+		} else {
+			/* We answer with a straight reflected Ack */
+			isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
+						skb->data[1],
+						len ? &skb->data[4] : NULL,
+						len);
+		}
+		break;
+	}
 }
 
+
+/*
+ * Daemon sends a CCP frame ...
+ */
+
+/* TODO: Clean this up with new Reset semantics */
+
+static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb)
+{
+	struct ippp_struct *mis,*is = ippp_table[lp->ppp_slot];
+	int proto;
+	unsigned char *data;
+
+	if(!skb || skb->len < 3)
+		return;
+
+	/* Daemon may send with or without address and control field comp */
+	data = skb->data;
+	if(!(is->pppcfg & SC_COMP_AC) && data[0] == 0xff && data[1] == 0x03) {
+		data += 2;
+		if(skb->len < 5)
+			return;
+	}
+
+	proto = ((int)data[0]<<8)+data[1];
+	if(proto != PPP_CCP && proto != PPP_LINK_CCP)
+		return;
+
+	printk(KERN_DEBUG "Received CCP frame from daemon:\n");
+	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+        if(lp->master)
+                mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot];
+        else
+                mis = is;
+	
+	if(mis != is)
+		printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n");
+	
+        switch(data[2]) {
+	case CCP_CONFREQ:
+	case CCP_TERMREQ:
+	case CCP_TERMACK:
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Disable (de)compression here!\n");
+		if(proto == PPP_CCP)
+			is->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);
+		else
+			is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);
+		break;
+	case CCP_CONFACK:
+		/* if we SEND an ackowledge we can/must enable the compressor */
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Enable compression here!\n");
+		if(proto == PPP_CCP)
+			is->compflags |= SC_COMP_ON;
+		else
+			is->compflags |= SC_LINK_COMP_ON;
+		break;
+	case CCP_RESETACK:
+		/* If we send a ACK we should reset our compressor */
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Reset decompression state here!\n");
+		printk(KERN_DEBUG "ResetAck from daemon passed by\n");
+		if(proto == PPP_CCP) {
+			/* link to master? */
+			if(is->compressor && is->comp_stat)
+				is->compressor->reset(is->comp_stat, 0, 0,
+						      NULL, 0, NULL);
+			is->compflags &= ~SC_COMP_DISCARD;	
+		}
+		else {
+			if(is->link_compressor && is->link_comp_stat)
+				is->link_compressor->reset(is->link_comp_stat,
+							   0, 0, NULL, 0, NULL);
+			is->compflags &= ~SC_LINK_COMP_DISCARD;	
+		}
+		break;
+	case CCP_RESETREQ:
+		/* Just let it pass by */
+		printk(KERN_DEBUG "ResetReq from daemon passed by\n");
+		break;
+	}
+}
+
+
 int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc)
 {
 	ipc->next = ipc_head;
@@ -2124,32 +2895,67 @@
 	return 0;
 }
 
-static int isdn_ppp_set_compressor(struct ippp_struct *is,int num)
+static int isdn_ppp_set_compressor(struct ippp_struct *is, struct isdn_ppp_comp_data *data)
 {
 	struct isdn_ppp_compressor *ipc = ipc_head;
+	int ret;
+	void *stat;
+	int num = data->num;
+
+	if(is->debug & 0x10)
+		printk(KERN_DEBUG "[%d] Set %s type %d\n",is->unit,
+			(data->flags&IPPP_COMP_FLAG_XMIT)?"compressor":"decompressor",num);
 
 	while(ipc) {
 		if(ipc->num == num) {
-			return 0;	
-			is->compressor = ipc;
-			is->link_compressor = ipc;
+			stat = ipc->alloc(data);
+			if(stat) {
+				ret = ipc->init(stat,data,is->unit,0);
+				if(!ret) {
+					printk(KERN_ERR "Can't init (de)compression!\n");
+					ipc->free(stat);
+					stat = NULL;
+					break;
+				}
+			}
+			else {
+				printk(KERN_ERR "Can't alloc (de)compression!\n");
+				break;
+			}
+
+                        if(data->flags & IPPP_COMP_FLAG_XMIT) {
+				if(data->flags & IPPP_COMP_FLAG_LINK) {
+					if(is->link_comp_stat)
+						is->link_compressor->free(is->link_comp_stat);
+					is->link_comp_stat = stat;
+                                	is->link_compressor = ipc;
+				}
+				else {
+					if(is->comp_stat)
+						is->compressor->free(is->comp_stat);
+					is->comp_stat = stat;
+                                	is->compressor = ipc;
+				}
+			}
+                        else {
+				if(data->flags & IPPP_COMP_FLAG_LINK) {
+					if(is->link_decomp_stat)
+						is->link_decompressor->free(is->link_decomp_stat);
+					is->link_decomp_stat = stat;
+        	                        is->link_decompressor = ipc;
+				}
+				else {
+					if(is->decomp_stat)
+						is->decompressor->free(is->decomp_stat);
+					is->decomp_stat = stat;
+        	                        is->decompressor = ipc;
+				}
+			}
+			return 0;
 		}
 		ipc = ipc->next;
 	}
 	return -EINVAL;
 }
-
-
-#if 0
-static struct symbol_table isdn_ppp_syms =
-{
-#include <linux/symtab_begin.h>
-	X(isdn_ppp_register_compressor),
-	X(isdn_ppp_unregister_compressor),
-#include <linux/symtab_end.h>
-};
-#endif
-
-
 
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)