summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/isdn/i4l/Makefile2
-rw-r--r--drivers/isdn/i4l/isdn_bsdcomp.c19
-rw-r--r--drivers/isdn/i4l/isdn_ciscohdlck.c13
-rw-r--r--drivers/isdn/i4l/isdn_common.h2
-rw-r--r--drivers/isdn/i4l/isdn_net.c412
-rw-r--r--drivers/isdn/i4l/isdn_net.h153
-rw-r--r--drivers/isdn/i4l/isdn_net_lib.c524
-rw-r--r--drivers/isdn/i4l/isdn_ppp.c2540
-rw-r--r--drivers/isdn/i4l/isdn_ppp.h20
-rw-r--r--drivers/isdn/i4l/isdn_ppp_ccp.c601
-rw-r--r--drivers/isdn/i4l/isdn_ppp_ccp.h66
-rw-r--r--drivers/isdn/isdnloop/isdnloop.c8
-rw-r--r--include/linux/isdn.h39
-rw-r--r--include/linux/isdn_ppp.h46
14 files changed, 2120 insertions, 2325 deletions
diff --git a/drivers/isdn/i4l/Makefile b/drivers/isdn/i4l/Makefile
index 407501c0d6b1..58c4e6d3ed35 100644
--- a/drivers/isdn/i4l/Makefile
+++ b/drivers/isdn/i4l/Makefile
@@ -19,7 +19,7 @@ isdn-objs := isdn_net.o isdn_net_lib.o \
# Optional parts of multipart objects.
-isdn-objs-$(CONFIG_ISDN_PPP) += isdn_ppp.o
+isdn-objs-$(CONFIG_ISDN_PPP) += isdn_ppp.o isdn_ppp_ccp.o
isdn-objs-$(CONFIG_ISDN_X25) += isdn_concap.o isdn_x25iface.o
isdn-objs-$(CONFIG_ISDN_AUDIO) += isdn_audio.o
isdn-objs-$(CONFIG_ISDN_TTY_FAX) += isdn_ttyfax.o
diff --git a/drivers/isdn/i4l/isdn_bsdcomp.c b/drivers/isdn/i4l/isdn_bsdcomp.c
index 6049970e1f2a..c48275b76141 100644
--- a/drivers/isdn/i4l/isdn_bsdcomp.c
+++ b/drivers/isdn/i4l/isdn_bsdcomp.c
@@ -907,16 +907,15 @@ static int bsd_decompress (void *state, struct sk_buff *skb_in, struct sk_buff *
*************************************************************/
static struct isdn_ppp_compressor ippp_bsd_compress = {
- NULL,NULL, /* prev,next: overwritten by isdn_ppp */
- CI_BSD_COMPRESS, /* compress_proto */
- bsd_alloc, /* alloc */
- bsd_free, /* free */
- bsd_init, /* init */
- bsd_reset, /* reset */
- bsd_compress, /* compress */
- bsd_decompress, /* decompress */
- bsd_incomp, /* incomp */
- bsd_stats /* comp_stat */
+ .num = CI_BSD_COMPRESS,
+ .alloc = bsd_alloc,
+ .free = bsd_free,
+ .init = bsd_init,
+ .reset = bsd_reset,
+ .compress = bsd_compress,
+ .decompress = bsd_decompress,
+ .incomp = bsd_incomp,
+ .stat = bsd_stats,
};
/*************************************************************
diff --git a/drivers/isdn/i4l/isdn_ciscohdlck.c b/drivers/isdn/i4l/isdn_ciscohdlck.c
index 0f08f3daea88..cab5b1a2100f 100644
--- a/drivers/isdn/i4l/isdn_ciscohdlck.c
+++ b/drivers/isdn/i4l/isdn_ciscohdlck.c
@@ -20,6 +20,19 @@
#include <linux/if_arp.h>
#include <linux/inetdevice.h>
+/*
+ * Definitions for Cisco-HDLC header.
+ */
+
+#define CISCO_ADDR_UNICAST 0x0f
+#define CISCO_ADDR_BROADCAST 0x8f
+#define CISCO_CTRL 0x00
+#define CISCO_TYPE_CDP 0x2000
+#define CISCO_TYPE_SLARP 0x8035
+#define CISCO_SLARP_REQUEST 0
+#define CISCO_SLARP_REPLY 1
+#define CISCO_SLARP_KEEPALIVE 2
+
/*
* CISCO HDLC keepalive specific stuff
*/
diff --git a/drivers/isdn/i4l/isdn_common.h b/drivers/isdn/i4l/isdn_common.h
index 3c81ffcb5d80..64e1c1e9c819 100644
--- a/drivers/isdn/i4l/isdn_common.h
+++ b/drivers/isdn/i4l/isdn_common.h
@@ -47,7 +47,7 @@
#endif
#define isdn_BUG() \
-do { printk(KERN_WARNING "ISDN Bug at %s:%d\n", __FILE__, __LINE__); \
+do { printk(KERN_WARNING "ISDN BUG at %s:%d\n", __FILE__, __LINE__); \
} while(0)
#define HERE printk("%s:%d (%s)\n", __FILE__, __LINE__, __FUNCTION__)
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index 4b504da722de..f41603c1287d 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -33,412 +33,8 @@
#include "isdn_concap.h"
#include "isdn_ciscohdlck.h"
-/*
- * Outline of new tbusy handling:
- *
- * Old method, roughly spoken, consisted of setting tbusy when entering
- * isdn_net_start_xmit() and at several other locations and clearing
- * it from isdn_net_start_xmit() thread when sending was successful.
- *
- * With 2.3.x multithreaded network core, to prevent problems, tbusy should
- * only be set by the isdn_net_start_xmit() thread and only when a tx-busy
- * condition is detected. Other threads (in particular isdn_net_stat_callb())
- * are only allowed to clear tbusy.
- *
- * -HE
- */
-
-/*
- * About SOFTNET:
- * Most of the changes were pretty obvious and basically done by HE already.
- *
- * One problem of the isdn net device code is that is uses struct net_device
- * for masters and slaves. However, only master interface are registered to
- * the network layer, and therefore, it only makes sense to call netif_*
- * functions on them.
- *
- * --KG
- */
-
-/*
- * Find out if the netdevice has been ifup-ed yet.
- */
-static inline int
-isdn_net_device_started(isdn_net_dev *idev)
-{
- return netif_running(&idev->mlp->dev);
-}
-
-/*
- * stop the network -> net_device queue.
- */
-static inline void
-isdn_net_dev_stop_queue(isdn_net_dev *idev)
-{
- netif_stop_queue(&idev->mlp->dev);
-}
-
-/*
- * find out if the net_device which this lp belongs to (lp can be
- * master or slave) is busy. It's busy iff all (master and slave)
- * queues are busy
- */
-static inline int
-isdn_net_device_busy(isdn_net_dev *idev)
-{
- isdn_net_local *mlp = idev->mlp;
- unsigned long flags;
- int retval = 1;
-
- if (!isdn_net_dev_busy(idev))
- return 0;
-
- spin_lock_irqsave(&mlp->online_lock, flags);
- list_for_each_entry(idev, &mlp->online, online) {
- if (!isdn_net_dev_busy(idev)) {
- retval = 0;
- break;
- }
- }
- spin_unlock_irqrestore(&mlp->online_lock, flags);
- return retval;
-}
-
-static inline
-void isdn_net_inc_frame_cnt(isdn_net_dev *idev)
-{
- atomic_inc(&idev->frame_cnt);
- if (isdn_net_device_busy(idev))
- isdn_net_dev_stop_queue(idev);
-}
-
-static inline void
-isdn_net_dec_frame_cnt(isdn_net_dev *idev)
-{
- atomic_dec(&idev->frame_cnt);
-
- if (!isdn_net_device_busy(idev)) {
- if (!skb_queue_empty(&idev->super_tx_queue))
- tasklet_schedule(&idev->tlet);
- else
- isdn_net_dev_wake_queue(idev);
- }
-}
-
-static inline
-void isdn_net_zero_frame_cnt(isdn_net_dev *idev)
-{
- atomic_set(&idev->frame_cnt, 0);
-}
-
-/* Prototypes */
-
-int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg);
-
char *isdn_net_revision = "$Revision: 1.140.6.11 $";
-/* A packet has successfully been sent out. */
-
-int
-isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c)
-{
- isdn_net_local *mlp = idev->mlp;
-
- isdn_net_dec_frame_cnt(idev);
- mlp->stats.tx_packets++;
- mlp->stats.tx_bytes += c->parm.length;
- return 1;
-}
-
-static void
-isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
-{
- u_short proto = ntohs(skb->protocol);
-
- printk(KERN_DEBUG "isdn_net: %s: %s, signalling dst_link_failure %s\n",
- dev->name,
- (reason != NULL) ? reason : "unknown",
- (proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : "");
-
- dst_link_failure(skb);
-}
-
-static void
-isdn_net_log_skb(struct sk_buff *skb, isdn_net_dev *idev)
-{
- unsigned char *p = skb->nh.raw; /* hopefully, this was set correctly */
- unsigned short proto = ntohs(skb->protocol);
- int data_ofs;
- struct ip_ports {
- unsigned short source;
- unsigned short dest;
- } *ipp;
- char addinfo[100];
-
- data_ofs = ((p[0] & 15) * 4);
- switch (proto) {
- case ETH_P_IP:
- switch (p[9]) {
- case IPPROTO_ICMP:
- strcpy(addinfo, "ICMP");
- break;
- case IPPROTO_TCP:
- case IPPROTO_UDP:
- ipp = (struct ip_ports *) (&p[data_ofs]);
- sprintf(addinfo, "%s, port: %d -> %d",
- p[9] == IPPROTO_TCP ? "TCP" : "UDP",
- ntohs(ipp->source), ntohs(ipp->dest));
- break;
- default:
- sprintf(addinfo, "type %d", p[9]);
- }
- printk(KERN_INFO
- "OPEN: %u.%u.%u.%u -> %u.%u.%u.%u %s\n",
-
- NIPQUAD(*(u32 *)(p + 12)), NIPQUAD(*(u32 *)(p + 16)),
- addinfo);
- break;
- case ETH_P_ARP:
- printk(KERN_INFO
- "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
- NIPQUAD(*(u32 *)(p + 14)), NIPQUAD(*(u32 *)(p + 24)));
- break;
- default:
- printk(KERN_INFO "OPEN: unknown proto %#x\n", proto);
- }
-}
-
-/*
- * this function is used to send supervisory data, i.e. data which was
- * not received from the network layer, but e.g. frames from ipppd, CCP
- * reset frames etc.
- */
-void
-isdn_net_write_super(isdn_net_dev *idev, struct sk_buff *skb)
-{
- if (in_irq()) {
- // we can't grab the lock from irq context,
- // so we just queue the packet
- skb_queue_tail(&idev->super_tx_queue, skb);
-
- tasklet_schedule(&idev->tlet);
- return;
- }
-
- spin_lock_bh(&idev->xmit_lock);
- if (!isdn_net_dev_busy(idev)) {
- isdn_net_writebuf_skb(idev, skb);
- } else {
- skb_queue_tail(&idev->super_tx_queue, skb);
- }
- spin_unlock_bh(&idev->xmit_lock);
-}
-
-/*
- * all frames sent from the (net) LL to a HL driver should go via this function
- * it's serialized by the caller holding the idev->xmit_lock spinlock
- */
-void isdn_net_writebuf_skb(isdn_net_dev *idev, struct sk_buff *skb)
-{
- isdn_net_local *mlp = idev->mlp;
- int ret;
- int len = skb->len; /* save len */
-
- /* before obtaining the lock the caller should have checked that
- the lp isn't busy */
- if (isdn_net_dev_busy(idev)) {
- isdn_BUG();
- goto error;
- }
-
- if (!isdn_net_online(idev)) {
- isdn_BUG();
- goto error;
- }
- ret = isdn_slot_write(idev->isdn_slot, skb);
- if (ret != len) {
- /* we should never get here */
- printk(KERN_WARNING "%s: HL driver queue full\n", idev->name);
- goto error;
- }
-
- idev->transcount += len;
- isdn_net_inc_frame_cnt(idev);
- return;
-
- error:
- dev_kfree_skb(skb);
- mlp->stats.tx_errors++;
-}
-
-static void
-isdn_net_dial_slave(isdn_net_local *mlp)
-{
- isdn_net_dev *idev;
-
- list_for_each_entry(idev, &mlp->slaves, slaves) {
- if (!isdn_net_bound(idev)) {
- isdn_net_dial(idev);
- break;
- }
- }
-}
-
-/*
- * Based on cps-calculation, check if device is overloaded.
- * If so, and if a slave exists, trigger dialing for it.
- * If any slave is online, deliver packets using a simple round robin
- * scheme.
- *
- * Return: 0 on success, !0 on failure.
- */
-
-int
-isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
-{
- isdn_net_dev *idev;
- isdn_net_local *mlp = ndev->priv;
-
- ndev->trans_start = jiffies;
-
- if (list_empty(&mlp->online))
- return isdn_net_autodial(skb, ndev);
-
- idev = isdn_net_get_locked_dev(mlp);
- if (!idev) {
- printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
- netif_stop_queue(ndev);
- return 1;
- }
- /* we have our idev locked from now on */
-
- isdn_net_writebuf_skb(idev, skb);
- spin_unlock_bh(&idev->xmit_lock);
-
- /* the following stuff is here for backwards compatibility.
- * in future, start-up and hangup of slaves (based on current load)
- * should move to userspace and get based on an overall cps
- * calculation
- */
- if (jiffies != idev->last_jiffies) {
- idev->cps = idev->transcount * HZ / (jiffies - idev->last_jiffies);
- idev->last_jiffies = jiffies;
- idev->transcount = 0;
- }
- if (dev->net_verbose > 3)
- printk(KERN_DEBUG "%s: %d bogocps\n", idev->name, idev->cps);
-
- if (idev->cps > mlp->triggercps) {
- if (!idev->sqfull) {
- /* First time overload: set timestamp only */
- idev->sqfull = 1;
- idev->sqfull_stamp = jiffies;
- } else {
- /* subsequent overload: if slavedelay exceeded, start dialing */
- if (time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay)) {
- isdn_net_dial_slave(mlp);
- }
- }
- } else {
- if (idev->sqfull && time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay + 10 * HZ)) {
- idev->sqfull = 0;
- }
- /* this is a hack to allow auto-hangup for slaves on moderate loads */
- list_del(&mlp->online);
- list_add_tail(&mlp->online, &idev->online);
- }
-
- return 0;
-}
-
-int
-isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev)
-{
- isdn_net_local *mlp = ndev->priv;
- isdn_net_dev *idev = list_entry(mlp->slaves.next, isdn_net_dev, slaves);
-
- /* are we dialing already? */
- if (isdn_net_bound(idev))
- goto stop_queue;
-
- if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
- goto discard;
-
- if (isdn_net_dial(idev) < 0)
- goto discard;
-
- /* Log packet, which triggered dialing */
- if (dev->net_verbose)
- isdn_net_log_skb(skb, idev);
-
- stop_queue:
- netif_stop_queue(ndev);
- return 1;
-
- discard:
- isdn_net_unreachable(ndev, skb, "dial rejected");
- dev_kfree_skb(skb);
- return 0;
-}
-
-
-/*
- * Got a packet from ISDN-Channel.
- */
-static void
-isdn_net_receive(isdn_net_dev *idev, struct sk_buff *skb)
-{
- isdn_net_local *mlp = idev->mlp;
-
- idev->transcount += skb->len;
-
- mlp->stats.rx_packets++;
- mlp->stats.rx_bytes += skb->len;
- skb->dev = &mlp->dev;
- skb->pkt_type = PACKET_HOST;
- skb->mac.raw = skb->data;
- isdn_dumppkt("R:", skb->data, skb->len, 40);
-
- mlp->ops->receive(mlp, idev, skb);
-}
-
-/*
- * A packet arrived via ISDN. Search interface-chain for a corresponding
- * interface. If found, deliver packet to receiver-function and return 1,
- * else return 0.
- */
-int
-isdn_net_rcv_skb(int idx, struct sk_buff *skb)
-{
- isdn_net_dev *idev = isdn_slot_idev(idx);
-
- if (!idev) {
- HERE;
- return 0;
- }
- if (!isdn_net_online(idev))
- return 0;
-
- isdn_net_receive(idev, skb);
- return 0;
-}
-
-/*
- * This is called from certain upper protocol layers (multilink ppp
- * and x25iface encapsulation module) that want to initiate dialing
- * themselves.
- */
-int
-isdn_net_dial_req(isdn_net_dev *idev)
-{
- isdn_net_local *mlp = idev->mlp;
- /* is there a better error code? */
- if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
- return -EBUSY;
-
- return isdn_net_dial(idev);
-}
-
// ISDN_NET_ENCAP_IPTYP
// ethernet type field
// ======================================================================
@@ -581,6 +177,8 @@ static struct isdn_netif_ops ether_ops = {
void
isdn_net_init(void)
{
+ isdn_net_lib_init();
+
register_isdn_netif(ISDN_NET_ENCAP_ETHER, &ether_ops);
register_isdn_netif(ISDN_NET_ENCAP_RAWIP, &rawip_ops);
register_isdn_netif(ISDN_NET_ENCAP_IPTYP, &iptyp_ops);
@@ -593,7 +191,11 @@ isdn_net_init(void)
#ifdef CONFIG_ISDN_PPP
register_isdn_netif(ISDN_NET_ENCAP_SYNCPPP, &isdn_ppp_ops);
#endif
+}
- isdn_net_lib_init();
+void
+isdn_net_exit(void)
+{
+ isdn_net_lib_exit();
}
diff --git a/drivers/isdn/i4l/isdn_net.h b/drivers/isdn/i4l/isdn_net.h
index 16a443f584dd..14f94befc91f 100644
--- a/drivers/isdn/i4l/isdn_net.h
+++ b/drivers/isdn/i4l/isdn_net.h
@@ -14,138 +14,29 @@
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/isdn.h>
- /* Definitions for hupflags: */
-#define ISDN_CHARGEHUP 4 /* We want to use the charge mechanism */
-#define ISDN_INHUP 8 /* Even if incoming, close after huptimeout */
-#define ISDN_MANCHARGE 16 /* Charge Interval manually set */
-/*
- * Definitions for Cisco-HDLC header.
- */
-
-#define CISCO_ADDR_UNICAST 0x0f
-#define CISCO_ADDR_BROADCAST 0x8f
-#define CISCO_CTRL 0x00
-#define CISCO_TYPE_CDP 0x2000
-#define CISCO_TYPE_SLARP 0x8035
-#define CISCO_SLARP_REQUEST 0
-#define CISCO_SLARP_REPLY 1
-#define CISCO_SLARP_KEEPALIVE 2
-
-extern void isdn_net_init(void);
-extern void isdn_net_exit(void);
-extern void isdn_net_lib_init(void);
-extern void isdn_net_lib_exit(void);
-extern void isdn_net_hangup_all(void);
-extern int isdn_net_ioctl(struct inode *, struct file *, uint, ulong);
-
-extern int register_isdn_netif(int encap, struct isdn_netif_ops *ops);
-extern int isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev);
-extern int isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev);
-
-extern int isdn_net_dial(isdn_net_dev *idev);
-
-extern int isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c);
-
-extern int isdn_net_stat_callback(int, isdn_ctrl *);
-extern int isdn_net_find_icall(int, int, int, setup_parm *);
-extern int isdn_net_hangup(isdn_net_dev *);
-extern int isdn_net_rcv_skb(int, struct sk_buff *);
-extern int isdn_net_dial_req(isdn_net_dev *);
-extern void isdn_net_writebuf_skb(isdn_net_dev *, struct sk_buff *skb);
-extern void isdn_net_write_super(isdn_net_dev *, struct sk_buff *skb);
-extern int isdn_net_online(isdn_net_dev *);
-
-enum {
- ST_CHARGE_NULL,
- ST_CHARGE_GOT_CINF, /* got a first charge info */
- ST_CHARGE_HAVE_CINT, /* got a second chare info and thus the timing */
-};
-
-#define ISDN_NET_MAX_QUEUE_LENGTH 2
-
-/*
- * is this particular channel busy?
- */
-static inline int
-isdn_net_dev_busy(isdn_net_dev *idev)
-{
- if (atomic_read(&idev->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH)
- return 0;
- else
- return 1;
-}
-
-/*
- * For the given net device, this will get a non-busy channel out of the
- * corresponding bundle. The returned channel is locked.
- */
-static inline isdn_net_dev *
-isdn_net_get_locked_dev(isdn_net_local *mlp)
-{
- unsigned long flags;
- isdn_net_dev *idev;
-
- spin_lock_irqsave(&mlp->online_lock, flags);
-
- list_for_each_entry(idev, &mlp->online, online) {
- spin_lock_bh(&idev->xmit_lock);
- if (!isdn_net_dev_busy(idev)) {
- /* point the head to next online channel */
- list_del(&mlp->online);
- list_add(&mlp->online, &idev->online);
- goto found;
- }
- spin_unlock_bh(&idev->xmit_lock);
- }
- idev = NULL;
-
- found:
- spin_unlock_irqrestore(&mlp->online_lock, flags);
- return idev;
-}
-
-/*
- * add a channel to a bundle
- */
-static inline void
-isdn_net_add_to_bundle(isdn_net_local *mlp, isdn_net_dev *idev)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&mlp->online_lock, flags);
- list_add(&idev->online, &mlp->online);
- spin_unlock_irqrestore(&mlp->online_lock, flags);
-}
-/*
- * remove a channel from the bundle it belongs to
- */
-static inline void
-isdn_net_rm_from_bundle(isdn_net_dev *idev)
-{
- isdn_net_local *mlp = idev->mlp;
- unsigned long flags;
-
- spin_lock_irqsave(&mlp->online_lock, flags);
- // list_del(&idev->online); FIXME
- spin_unlock_irqrestore(&mlp->online_lock, flags);
-}
-
-/*
- * wake up the network -> net_device queue.
- * For slaves, wake the corresponding master interface.
- */
-static inline void
-isdn_net_dev_wake_queue(isdn_net_dev *idev)
-{
- netif_wake_queue(&idev->mlp->dev);
-}
-
-static inline int
-isdn_net_bound(isdn_net_dev *idev)
-{
- return idev->isdn_slot >= 0;
-}
+void isdn_net_init(void);
+void isdn_net_exit(void);
+void isdn_net_lib_init(void);
+void isdn_net_lib_exit(void);
+void isdn_net_hangup_all(void);
+int isdn_net_ioctl(struct inode *, struct file *, uint, ulong);
+
+int register_isdn_netif(int encap, struct isdn_netif_ops *ops);
+int isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev);
+void isdn_net_online(isdn_net_dev *idev);
+void isdn_net_offline(isdn_net_dev *idev);
+
+int isdn_net_stat_callback(int, isdn_ctrl *);
+int isdn_net_find_icall(int, int, int, setup_parm *);
+int isdn_net_rcv_skb(int, struct sk_buff *);
+
+int isdn_net_hangup(isdn_net_dev *);
+int isdn_net_dial_req(isdn_net_dev *);
+void isdn_net_writebuf_skb(isdn_net_dev *, struct sk_buff *skb);
+void isdn_net_write_super(isdn_net_dev *, struct sk_buff *skb);
+int isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev);
+isdn_net_dev *isdn_net_get_xmit_dev(isdn_net_local *mlp);
static inline int
put_u8(unsigned char *p, u8 x)
diff --git a/drivers/isdn/i4l/isdn_net_lib.c b/drivers/isdn/i4l/isdn_net_lib.c
index 4cb417240aae..ba0144200c1f 100644
--- a/drivers/isdn/i4l/isdn_net_lib.c
+++ b/drivers/isdn/i4l/isdn_net_lib.c
@@ -80,12 +80,13 @@ lp_put(isdn_net_local *lp)
isdn_BUG();
}
-int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg); /* FIXME */
-
+static int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg);
static void isdn_net_tasklet(unsigned long data);
static void isdn_net_dial_timer(unsigned long data);
static int isdn_init_netif(struct net_device *ndev);
static void isdn_net_dev_debug(struct fsm_inst *fi, char *fmt, ...);
+static int isdn_net_dial(isdn_net_dev *idev);
+static int isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c);
static struct fsm isdn_net_fsm;
@@ -151,6 +152,18 @@ static char *isdn_net_ev_str[] = {
"EV_DO_ACCEPT",
};
+/* Definitions for hupflags: */
+
+#define ISDN_CHARGEHUP 4 /* We want to use the charge mechanism */
+#define ISDN_INHUP 8 /* Even if incoming, close after huptimeout */
+#define ISDN_MANCHARGE 16 /* Charge Interval manually set */
+
+enum {
+ ST_CHARGE_NULL,
+ ST_CHARGE_GOT_CINF, /* got a first charge info */
+ ST_CHARGE_HAVE_CINT, /* got a second chare info and thus the timing */
+};
+
/* ====================================================================== */
/* Registration of ISDN network interface types */
/* ====================================================================== */
@@ -208,7 +221,8 @@ isdn_net_set_encap(isdn_net_local *lp, int encap)
if (lp->ops && lp->ops->cleanup)
lp->ops->cleanup(lp);
- if (encap < 0 || encap >= ISDN_NET_ENCAP_NR) {
+ if (encap < 0 || encap >= ISDN_NET_ENCAP_NR ||
+ !isdn_netif_ops[encap]) {
lp->p_encap = -1;
lp->ops = NULL;
retval = -EINVAL;
@@ -348,7 +362,6 @@ isdn_net_addif(char *name, isdn_net_local *mlp)
strcpy(idev->name, name);
tasklet_init(&idev->tlet, isdn_net_tasklet, (unsigned long) idev);
- spin_lock_init(&idev->xmit_lock);
skb_queue_head_init(&idev->super_tx_queue);
idev->isdn_slot = -1;
@@ -356,7 +369,7 @@ isdn_net_addif(char *name, isdn_net_local *mlp)
idev->pre_channel = -1;
idev->exclusive = -1;
- idev->ppp_slot = -1;
+ idev->ipppd = NULL;
idev->pppbind = -1;
init_timer(&idev->dial_timer);
@@ -380,6 +393,7 @@ isdn_net_addif(char *name, isdn_net_local *mlp)
mlp->magic = ISDN_NET_MAGIC;
INIT_LIST_HEAD(&mlp->slaves);
INIT_LIST_HEAD(&mlp->online);
+ spin_lock_init(&mlp->xmit_lock);
mlp->p_encap = -1;
isdn_net_set_encap(mlp, ISDN_NET_ENCAP_RAWIP);
@@ -1007,7 +1021,7 @@ isdn_net_hangup_all(void)
* Remove all network-interfaces
*/
void
-isdn_net_exit(void)
+isdn_net_cleanup(void)
{
isdn_net_dev *idev;
int retval;
@@ -1025,9 +1039,6 @@ isdn_net_exit(void)
isdn_BUG();
}
up(&sem);
-
- // FIXME
- isdn_net_lib_exit();
}
/* ====================================================================== */
@@ -1053,7 +1064,7 @@ isdn_net_open(struct net_device *dev)
if (lp->ops->open)
retval = lp->ops->open(lp);
- if (!retval)
+ if (retval)
return retval;
netif_start_queue(dev);
@@ -1069,7 +1080,6 @@ isdn_net_open(struct net_device *dev)
/*
* Shutdown a net-interface.
*/
-// FIXME share?
static int
isdn_net_close(struct net_device *dev)
{
@@ -1083,14 +1093,14 @@ isdn_net_close(struct net_device *dev)
netif_stop_queue(dev);
- list_for_each_safe(l, n, &lp->online) {
- sdev = list_entry(l, isdn_net_dev, online);
+ list_for_each_safe(l, n, &lp->slaves) {
+ sdev = list_entry(l, isdn_net_dev, slaves);
isdn_net_hangup(sdev);
}
/* The hangup will make the refcnt drop back to
* 1 (referenced by list only) soon. */
spin_lock_irqsave(&running_devs_lock, flags);
- while (atomic_read(&dev->refcnt) != 1) {
+ while (atomic_read(&lp->refcnt) != 1) {
spin_unlock_irqrestore(&running_devs_lock, flags);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/10);
@@ -1147,29 +1157,12 @@ isdn_init_netif(struct net_device *ndev)
}
/* ====================================================================== */
-
-static void
-isdn_net_tasklet(unsigned long data)
-{
- isdn_net_dev *idev = (isdn_net_dev *) data;
- struct sk_buff *skb;
-
- spin_lock_bh(&idev->xmit_lock);
- while (!isdn_net_dev_busy(idev)) {
- skb = skb_dequeue(&idev->super_tx_queue);
- if (!skb)
- break;
- isdn_net_writebuf_skb(idev, skb);
- }
- spin_unlock_bh(&idev->xmit_lock);
-}
-
-/* ====================================================================== */
/* call control state machine */
/* ====================================================================== */
// FIXME
-int isdn_net_online(isdn_net_dev *idev)
+static int
+isdn_net_is_connected(isdn_net_dev *idev)
{
return idev->fi.state == ST_ACTIVE;
}
@@ -1234,7 +1227,7 @@ isdn_net_bind_channel(isdn_net_dev *idev, int slot)
return retval;
}
-int
+static int
isdn_net_dial(isdn_net_dev *idev)
{
int retval;
@@ -1250,6 +1243,127 @@ isdn_net_dial(isdn_net_dev *idev)
return retval;
}
+static void
+isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
+{
+ u_short proto = ntohs(skb->protocol);
+
+ printk(KERN_DEBUG "isdn_net: %s: %s, signalling dst_link_failure %s\n",
+ dev->name,
+ (reason != NULL) ? reason : "unknown",
+ (proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : "");
+
+ dst_link_failure(skb);
+}
+
+/*
+ * This is called from certain upper protocol layers (multilink ppp
+ * and x25iface encapsulation module) that want to initiate dialing
+ * themselves.
+ */
+int
+isdn_net_dial_req(isdn_net_dev *idev)
+{
+ isdn_net_local *mlp = idev->mlp;
+ /* is there a better error code? */
+ if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
+ return -EBUSY;
+
+ return isdn_net_dial(idev);
+}
+
+static void
+isdn_net_log_skb(struct sk_buff *skb, isdn_net_dev *idev)
+{
+ unsigned char *p = skb->nh.raw; /* hopefully, this was set correctly */
+ unsigned short proto = ntohs(skb->protocol);
+ int data_ofs;
+ struct ip_ports {
+ unsigned short source;
+ unsigned short dest;
+ } *ipp;
+ char addinfo[100];
+
+ data_ofs = ((p[0] & 15) * 4);
+ switch (proto) {
+ case ETH_P_IP:
+ switch (p[9]) {
+ case IPPROTO_ICMP:
+ strcpy(addinfo, "ICMP");
+ break;
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ ipp = (struct ip_ports *) (&p[data_ofs]);
+ sprintf(addinfo, "%s, port: %d -> %d",
+ p[9] == IPPROTO_TCP ? "TCP" : "UDP",
+ ntohs(ipp->source), ntohs(ipp->dest));
+ break;
+ default:
+ sprintf(addinfo, "type %d", p[9]);
+ }
+ printk(KERN_INFO
+ "OPEN: %u.%u.%u.%u -> %u.%u.%u.%u %s\n",
+
+ NIPQUAD(*(u32 *)(p + 12)), NIPQUAD(*(u32 *)(p + 16)),
+ addinfo);
+ break;
+ case ETH_P_ARP:
+ printk(KERN_INFO
+ "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
+ NIPQUAD(*(u32 *)(p + 14)), NIPQUAD(*(u32 *)(p + 24)));
+ break;
+ default:
+ printk(KERN_INFO "OPEN: unknown proto %#x\n", proto);
+ }
+}
+
+int
+isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev)
+{
+ isdn_net_local *mlp = ndev->priv;
+ isdn_net_dev *idev = list_entry(mlp->slaves.next, isdn_net_dev, slaves);
+ int retval;
+
+ if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
+ goto discard;
+
+ retval = isdn_net_dial(idev);
+ if (retval == -ESRCH)
+ goto stop_queue;
+
+ if (retval < 0)
+ goto discard;
+
+ /* Log packet, which triggered dialing */
+ if (dev->net_verbose)
+ isdn_net_log_skb(skb, idev);
+
+ stop_queue:
+ netif_stop_queue(ndev);
+ return 1;
+
+ discard:
+ isdn_net_unreachable(ndev, skb, "dial rejected");
+ dev_kfree_skb(skb);
+ return 0;
+}
+
+
+static void
+isdn_net_dial_slave(isdn_net_local *mlp)
+{
+ isdn_net_dev *idev;
+
+ if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
+ return;
+
+ list_for_each_entry(idev, &mlp->slaves, slaves) {
+ if (fsm_event(&idev->fi, EV_DO_DIAL, NULL) != -ESRCH) {
+ break;
+ }
+ }
+}
+
static int
accept_icall(struct fsm_inst *fi, int pr, void *arg)
{
@@ -1640,14 +1754,12 @@ bconn(struct fsm_inst *fi, int pr, void *arg)
del_timer(&idev->dial_timer);
}
- isdn_net_add_to_bundle(mlp, idev);
-
printk(KERN_INFO "%s connected\n", idev->name);
/* If first Chargeinfo comes before B-Channel connect,
* we correct the timestamp here.
*/
idev->chargetime = jiffies;
-
+ idev->frame_cnt = 0;
idev->transcount = 0;
idev->cps = 0;
idev->last_jiffies = jiffies;
@@ -1655,8 +1767,8 @@ bconn(struct fsm_inst *fi, int pr, void *arg)
if (mlp->ops->connected)
mlp->ops->connected(idev);
else
- isdn_net_dev_wake_queue(idev);
-
+ isdn_net_online(idev);
+
return 0;
}
@@ -1669,11 +1781,11 @@ bhup(struct fsm_inst *fi, int pr, void *arg)
del_timer(&idev->dial_timer);
if (mlp->ops->disconnected)
mlp->ops->disconnected(idev);
+ else
+ isdn_net_offline(idev);
printk(KERN_INFO "%s: disconnected\n", idev->name);
fsm_change_state(fi, ST_WAIT_DHUP);
- isdn_net_rm_from_bundle(idev);
- return 0;
}
static int
@@ -1748,10 +1860,7 @@ isdn_net_hangup(isdn_net_dev *idev)
isdn_ctrl cmd;
del_timer(&idev->dial_timer);
- if (!isdn_net_bound(idev)) {
- isdn_BUG();
- return 1;
- }
+
printk(KERN_INFO "%s: local hangup\n", idev->name);
isdn_slot_command(idev->isdn_slot, ISDN_CMD_HANGUP, &cmd);
return 1;
@@ -1769,7 +1878,6 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c)
isdn_net_dev *idev = isdn_slot_idev(idx);
if (!idev) {
- HERE;
return 0;
}
switch (c->command) {
@@ -1791,7 +1899,7 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c)
}
}
-int
+static int
isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg)
{
fsm_event(&idev->fi, pr, arg);
@@ -1873,6 +1981,326 @@ static void isdn_net_dev_debug(struct fsm_inst *fi, char *fmt, ...)
printk(KERN_DEBUG "%s\n", buf);
}
+/* ====================================================================== */
+/* xmit path */
+/* ====================================================================== */
+
+#define ISDN_NET_MAX_QUEUE_LENGTH 2
+
+/*
+ * is this particular channel busy?
+ */
+static inline int
+isdn_net_dev_busy(isdn_net_dev *idev)
+{
+ return idev->frame_cnt >= ISDN_NET_MAX_QUEUE_LENGTH;
+}
+
+/*
+ * find out if the net_device which this mlp is belongs to is busy.
+ * It's busy iff all channels are busy.
+ * must hold mlp->xmit_lock
+ * FIXME: Use a mlp->frame_cnt instead of loop?
+ */
+static inline int
+isdn_net_local_busy(isdn_net_local *mlp)
+{
+ isdn_net_dev *idev;
+
+ list_for_each_entry(idev, &mlp->online, online) {
+ if (!isdn_net_dev_busy(idev))
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * For the given net device, this will get a non-busy channel out of the
+ * corresponding bundle.
+ * must hold mlp->xmit_lock
+ */
+isdn_net_dev *
+isdn_net_get_xmit_dev(isdn_net_local *mlp)
+{
+ isdn_net_dev *idev;
+
+ list_for_each_entry(idev, &mlp->online, online) {
+ if (!isdn_net_dev_busy(idev)) {
+ /* point the head to next online channel */
+ list_del(&mlp->online);
+ list_add(&mlp->online, &idev->online);
+ return idev;
+ }
+ }
+ return NULL;
+}
+
+/* mlp->xmit_lock must be held */
+static inline void
+isdn_net_inc_frame_cnt(isdn_net_dev *idev)
+{
+ isdn_net_local *mlp = idev->mlp;
+
+ if (isdn_net_dev_busy(idev))
+ isdn_BUG();
+
+ idev->frame_cnt++;
+ if (isdn_net_local_busy(mlp))
+ netif_stop_queue(&mlp->dev);
+}
+
+/* mlp->xmit_lock must be held */
+static inline void
+isdn_net_dec_frame_cnt(isdn_net_dev *idev)
+{
+ isdn_net_local *mlp = idev->mlp;
+
+ idev->frame_cnt--;
+
+ if (isdn_net_dev_busy(idev))
+ isdn_BUG();
+
+ if (!skb_queue_empty(&idev->super_tx_queue))
+ tasklet_schedule(&idev->tlet);
+ else
+ netif_wake_queue(&mlp->dev);
+}
+
+static void
+isdn_net_tasklet(unsigned long data)
+{
+ isdn_net_dev *idev = (isdn_net_dev *) data;
+ isdn_net_local *mlp = idev->mlp;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mlp->xmit_lock, flags);
+ while (!isdn_net_dev_busy(idev) &&
+ (skb = skb_dequeue(&idev->super_tx_queue))) {
+ isdn_net_writebuf_skb(idev, skb);
+ }
+ spin_unlock_irqrestore(&mlp->xmit_lock, flags);
+}
+
+/* We're good to accept (IP/whatever) traffic now */
+
+void
+isdn_net_online(isdn_net_dev *idev)
+{
+ // FIXME check we're connected
+ isdn_net_local *mlp = idev->mlp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mlp->xmit_lock, flags);
+ list_add(&idev->online, &mlp->online);
+ spin_unlock_irqrestore(&mlp->xmit_lock, flags);
+
+ netif_wake_queue(&mlp->dev);
+}
+
+/* No more (IP/whatever) traffic over the net interface */
+
+void
+isdn_net_offline(isdn_net_dev *idev)
+{
+ isdn_net_local *mlp = idev->mlp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mlp->xmit_lock, flags);
+ list_del(&idev->online);
+ spin_unlock_irqrestore(&mlp->xmit_lock, flags);
+
+ skb_queue_purge(&idev->super_tx_queue);
+}
+
+/*
+ * all frames sent from the (net) LL to a HL driver should go via this function
+ * must hold mlp->xmit_lock
+ */
+void
+isdn_net_writebuf_skb(isdn_net_dev *idev, struct sk_buff *skb)
+{
+ isdn_net_local *mlp = idev->mlp;
+ int ret;
+ int len = skb->len; /* save len */
+
+ /* before obtaining the lock the caller should have checked that
+ the lp isn't busy */
+ if (isdn_net_dev_busy(idev)) {
+ isdn_BUG();
+ goto error;
+ }
+
+ if (!isdn_net_is_connected(idev)) {
+ isdn_BUG();
+ goto error;
+ }
+ ret = isdn_slot_write(idev->isdn_slot, skb);
+ if (ret != len) {
+ /* we should never get here */
+ printk(KERN_WARNING "%s: HL driver queue full\n", idev->name);
+ goto error;
+ }
+
+ idev->transcount += len;
+ isdn_net_inc_frame_cnt(idev);
+ return;
+
+ error:
+ dev_kfree_skb(skb);
+ mlp->stats.tx_errors++;
+}
+
+/* A packet has successfully been sent out. */
+
+static int
+isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c)
+{
+ isdn_net_local *mlp = idev->mlp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mlp->xmit_lock, flags);
+ isdn_net_dec_frame_cnt(idev);
+ spin_unlock_irqrestore(&mlp->xmit_lock, flags);
+ mlp->stats.tx_packets++;
+ mlp->stats.tx_bytes += c->parm.length;
+ return 1;
+}
+
+/*
+ * Based on cps-calculation, check if device is overloaded.
+ * If so, and if a slave exists, trigger dialing for it.
+ * If any slave is online, deliver packets using a simple round robin
+ * scheme.
+ *
+ * Return: 0 on success, !0 on failure.
+ */
+
+int
+isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ isdn_net_dev *idev;
+ isdn_net_local *mlp = ndev->priv;
+ unsigned long flags;
+ int retval;
+
+ ndev->trans_start = jiffies;
+
+ spin_lock_irqsave(&mlp->xmit_lock, flags);
+
+ if (list_empty(&mlp->online)) {
+ retval = isdn_net_autodial(skb, ndev);
+ goto out;
+ }
+
+ idev = isdn_net_get_xmit_dev(mlp);
+ if (!idev) {
+ printk(KERN_INFO "%s: all channels busy - requeuing!\n", ndev->name);
+ netif_stop_queue(ndev);
+ retval = 1;
+ goto out;
+ }
+
+ isdn_net_writebuf_skb(idev, skb);
+
+ /* the following stuff is here for backwards compatibility.
+ * in future, start-up and hangup of slaves (based on current load)
+ * should move to userspace and get based on an overall cps
+ * calculation
+ */
+ if (jiffies != idev->last_jiffies) {
+ idev->cps = idev->transcount * HZ / (jiffies - idev->last_jiffies);
+ idev->last_jiffies = jiffies;
+ idev->transcount = 0;
+ }
+ if (dev->net_verbose > 3)
+ printk(KERN_DEBUG "%s: %d bogocps\n", idev->name, idev->cps);
+
+ if (idev->cps > mlp->triggercps) {
+ if (!idev->sqfull) {
+ /* First time overload: set timestamp only */
+ idev->sqfull = 1;
+ idev->sqfull_stamp = jiffies;
+ } else {
+ /* subsequent overload: if slavedelay exceeded, start dialing */
+ if (time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay)) {
+ isdn_net_dial_slave(mlp);
+ }
+ }
+ } else {
+ if (idev->sqfull && time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay + 10 * HZ)) {
+ idev->sqfull = 0;
+ }
+ /* this is a hack to allow auto-hangup for slaves on moderate loads */
+ list_del(&mlp->online);
+ list_add_tail(&mlp->online, &idev->online);
+ }
+
+ retval = 0;
+ out:
+ spin_unlock_irqrestore(&mlp->xmit_lock, flags);
+ return retval;
+}
+
+/*
+ * this function is used to send supervisory data, i.e. data which was
+ * not received from the network layer, but e.g. frames from ipppd, CCP
+ * reset frames etc.
+ * must hold mlp->xmit_lock
+ */
+void
+isdn_net_write_super(isdn_net_dev *idev, struct sk_buff *skb)
+{
+ if (!isdn_net_dev_busy(idev)) {
+ isdn_net_writebuf_skb(idev, skb);
+ } else {
+ skb_queue_tail(&idev->super_tx_queue, skb);
+ }
+}
+
+/* ====================================================================== */
+/* receive path */
+/* ====================================================================== */
+
+/*
+ * A packet arrived via ISDN. Search interface-chain for a corresponding
+ * interface. If found, deliver packet to receiver-function and return 1,
+ * else return 0.
+ */
+int
+isdn_net_rcv_skb(int idx, struct sk_buff *skb)
+{
+ isdn_net_dev *idev = isdn_slot_idev(idx);
+ isdn_net_local *mlp;
+
+ if (!idev) {
+ isdn_BUG();
+ return 0;
+ }
+ if (!isdn_net_is_connected(idev)) {
+ isdn_BUG();
+ return 0;
+ }
+
+ mlp = idev->mlp;
+
+ idev->transcount += skb->len;
+
+ mlp->stats.rx_packets++;
+ mlp->stats.rx_bytes += skb->len;
+ skb->dev = &mlp->dev;
+ skb->pkt_type = PACKET_HOST;
+ isdn_dumppkt("R:", skb->data, skb->len, 40);
+
+ mlp->ops->receive(mlp, idev, skb);
+
+ return 1;
+}
+
+/* ====================================================================== */
+/* init / exit */
+/* ====================================================================== */
+
void
isdn_net_lib_init(void)
{
@@ -1882,5 +2310,7 @@ isdn_net_lib_init(void)
void
isdn_net_lib_exit(void)
{
+ isdn_net_cleanup();
+
fsm_free(&isdn_net_fsm);
}
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
index 3c59a373459b..13395d86720a 100644
--- a/drivers/isdn/i4l/isdn_ppp.c
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -18,635 +18,535 @@
#include "isdn_common.h"
#include "isdn_ppp.h"
+#include "isdn_ppp_ccp.h"
#include "isdn_net.h"
-/* Prototypes */
-static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot);
-static int isdn_ppp_closewait(int slot);
-static void isdn_ppp_push_higher(isdn_net_local *lp, isdn_net_dev *idev,
- 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,struct isdn_ppp_comp_data *);
-static struct sk_buff *isdn_ppp_decompress(struct sk_buff *,
- 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,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);
+static struct sk_buff *
+isdn_ppp_dev_alloc_skb(void *priv, int len, int gfp_mask);
-/* 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(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);
+static int
+isdn_ppp_set_compressor(isdn_net_dev *idev, struct isdn_ppp_comp_data *);
+/* ====================================================================== */
+/* IPPPD handling */
+/* ====================================================================== */
+/* We use reference counting for struct ipppd. It is alloced on
+ * open() on /dev/ipppX and saved into file->private, making for one
+ * reference. release() will release this reference, after all other
+ * references are gone, the destructor frees it.
+ *
+ * Another reference is taken by isdn_ppp_bind() and freed by
+ * isdn_ppp_unbind(). The callbacks from isdn_net_lib.c happen only
+ * between isdn_ppp_bind() and isdn_ppp_unbind(), i.e. access to
+ * idev->ipppd is safe without further locking.
+ */
-#ifdef CONFIG_ISDN_MPP
-static ippp_bundle * isdn_ppp_bundle_arr = NULL;
-
-static int isdn_ppp_mp_bundle_array_init(void);
-static int isdn_ppp_mp_init(isdn_net_local *lp, ippp_bundle *add_to);
-static void isdn_ppp_mp_receive(isdn_net_local *lp, isdn_net_dev *idev,
- struct sk_buff *skb);
-static void isdn_ppp_mp_cleanup(isdn_net_local *lp );
+#define IPPPD_DEBUG
-static int isdn_ppp_bundle(struct ippp_struct *, int unit);
-#endif /* CONFIG_ISDN_MPP */
-
-char *isdn_ppp_revision = "$Revision: 1.85.6.9 $";
+#ifdef IPPPD_DEBUG
+#define ipppd_debug(i, fmt, arg...) \
+ printk(KERN_DEBUG "ipppd %p minor %d state %#x %s: " fmt "\n", (i), \
+ (i)->minor, (i)->state, __FUNCTION__ , ## arg)
+#else
+#define ipppd_debug(...) do { } while (0)
+#endif
-static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
+/* ipppd::flags */
+enum {
+ IPPPD_FL_HUP = 0x01,
+ IPPPD_FL_WAKEUP = 0x02,
+};
-static struct isdn_ppp_compressor *ipc_head = NULL;
+/* ipppd::state */
+enum {
+ IPPPD_ST_OPEN,
+ IPPPD_ST_ASSIGNED,
+ IPPPD_ST_CONNECTED,
+};
+
+struct ipppd {
+ struct list_head ipppds;
+ int state;
+ int flags;
+ struct sk_buff_head rq;
+ wait_queue_head_t wq;
+ struct isdn_net_dev_s *idev;
+ int unit;
+ int minor;
+ unsigned long debug;
+ atomic_t refcnt;
+};
+
+/* ====================================================================== */
+
+static spinlock_t ipppds_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(ipppds);
-/*
- * frame log (debug)
- */
static void
-isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot)
+ipppd_destroy(struct ipppd *ipppd)
{
- int cnt,
- j,
- i;
- char buf[80];
+ HERE;
- if (len < maxlen)
- maxlen = len;
+ skb_queue_purge(&ipppd->rq);
+ kfree(ipppd);
+}
- 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 "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf);
- }
+static inline struct ipppd *
+ipppd_get(struct ipppd *ipppd)
+{
+ atomic_inc(&ipppd->refcnt);
+ printk("%s: %d\n", __FUNCTION__, atomic_read(&ipppd->refcnt));
+ return ipppd;
}
-/*
- * 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
- */
-static void
-isdn_ppp_free(isdn_net_dev *idev)
+static inline void
+ipppd_put(struct ipppd *ipppd)
+{
+ printk("%s: %d\n", __FUNCTION__, atomic_read(&ipppd->refcnt));
+
+ if (atomic_dec_and_test(&ipppd->refcnt))
+ ipppd_destroy(ipppd);
+}
+
+/* ====================================================================== */
+/* char dev ops */
+
+/* --- open ------------------------------------------------------------- */
+
+static int
+ipppd_open(struct inode *ino, struct file *file)
{
unsigned long flags;
- struct ippp_struct *is;
+ unsigned int minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
+ struct ipppd *ipppd;
- if (idev->ppp_slot < 0 || idev->ppp_slot > ISDN_MAX_CHANNELS) {
- printk(KERN_ERR "%s: ppp_slot(%d) out of range\n",
- __FUNCTION__ , idev->ppp_slot);
- return;
- }
+ ipppd = kmalloc(sizeof(*ipppd), GFP_KERNEL);
+ if (!ipppd)
+ return -ENOMEM;
- save_flags(flags);
- cli();
+ memset(ipppd, 0, sizeof(*ipppd));
+ atomic_set(&ipppd->refcnt, 0);
+
+ /* file->private_data holds a reference */
+ file->private_data = ipppd_get(ipppd);
+
+ ipppd->unit = -1; /* set by isdn_ppp_bind */
+ ipppd->minor = minor;
+ ipppd->state = IPPPD_ST_OPEN;
+ init_waitqueue_head(&ipppd->wq);
+ skb_queue_head_init(&ipppd->rq);
+
+ spin_lock_irqsave(&ipppds, flags);
+ list_add(&ipppd->ipppds, &ipppds);
+ spin_unlock_irqrestore(&ipppds, flags);
+
+ ipppd_debug(ipppd, "minor %d", minor);
-#ifdef CONFIG_ISDN_MPP
- spin_lock(&idev->pb->lock);
-#endif
- isdn_net_rm_from_bundle(idev);
-#ifdef CONFIG_ISDN_MPP
- if (lp->netdev->pb->ref_ct == 1) /* last link in queue? */
- isdn_ppp_mp_cleanup(lp);
+ return 0;
+}
- lp->netdev->pb->ref_ct--;
- spin_unlock(&lp->netdev->pb->lock);
-#endif /* CONFIG_ISDN_MPP */
- if (idev->ppp_slot < 0 || idev->ppp_slot > ISDN_MAX_CHANNELS) {
- printk(KERN_ERR "%s: ppp_slot(%d) now invalid\n",
- __FUNCTION__ , idev->ppp_slot);
- restore_flags(flags);
- return;
- }
- is = ippp_table[idev->ppp_slot];
- if ((is->state & IPPP_CONNECT))
- isdn_ppp_closewait(idev->ppp_slot); /* force wakeup on ippp device */
- else if (is->state & IPPP_ASSIGNED)
- is->state = IPPP_OPEN; /* fallback to 'OPEN but not ASSIGNED' state */
+/* --- release --------------------------------------------------------- */
- if (is->debug & 0x1)
- printk(KERN_DEBUG "isdn_ppp_free %d %p\n", idev->ppp_slot, is->idev);
+static int
+ipppd_release(struct inode *ino, struct file *file)
+{
+ unsigned long flags;
+ struct ipppd *ipppd = file->private_data;
- is->idev = NULL; /* link is down .. set lp to NULL */
- idev->ppp_slot = -1; /* is this OK ?? */
+ ipppd_debug(ipppd, "");
- restore_flags(flags);
- return;
+ if (ipppd->state == IPPPD_ST_CONNECTED)
+ isdn_net_hangup(ipppd->idev);
+
+ spin_lock_irqsave(&ipppds, flags);
+ list_del(&ipppd->ipppds);
+ spin_unlock_irqrestore(&ipppds, flags);
+
+ ipppd_put(ipppd);
+
+ return 0;
}
-/*
- * bind isdn_net_local <=> ippp-device
- */
-int
-isdn_ppp_bind(isdn_net_dev *idev)
+/* --- read ------------------------------------------------------------- */
+
+/* read() is always non blocking */
+static ssize_t
+ipppd_read(struct file *file, char *buf, size_t count, loff_t *off)
{
- int i;
- int unit = 0;
- long flags;
- struct ippp_struct *is;
+ struct ipppd *is;
+ struct sk_buff *skb;
int retval;
- save_flags(flags);
- cli();
- if (idev->pppbind < 0) { /* device bound to ippp device ? */
- struct list_head *l;
- char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */
- memset(exclusive, 0, ISDN_MAX_CHANNELS);
- /* step through net devices to find exclusive minors */
- list_for_each(l, &isdn_net_devs) {
- isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list);
- if (p->pppbind >= 0)
- exclusive[p->pppbind] = 1;
- }
- /*
- * search a free device / slot
- */
- for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
- if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) { /* OPEN, but not connected! */
- break;
- }
- }
- } else {
- for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
- if (ippp_table[i]->minor == idev->pppbind &&
- (ippp_table[i]->state & IPPP_OPEN) == IPPP_OPEN)
- break;
- }
- }
+ if (off != &file->f_pos)
+ return -ESPIPE;
+
+ is = file->private_data;
- if (i >= ISDN_MAX_CHANNELS) {
- restore_flags(flags);
- printk(KERN_WARNING "isdn_ppp_bind: Can't find a (free) connection to the ipppd daemon.\n");
- retval = -1;
+ skb = skb_dequeue(&is->rq);
+ if (!skb) {
+ retval = -EAGAIN;
goto out;
}
- unit = isdn_ppp_if_get_unit(idev->name); /* get unit number from interface name .. ugly! */
- if (unit < 0) {
- printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n", idev->name);
- retval = -1;
- goto out;
+ if (skb->len > count) {
+ retval = -EMSGSIZE;
+ goto out_free;
}
-
- idev->ppp_slot = i;
- is = ippp_table[i];
- is->idev = idev;
- is->unit = unit;
- is->state = IPPP_OPEN | IPPP_ASSIGNED; /* assigned to a netdevice but not connected */
-#ifdef CONFIG_ISDN_MPP
- retval = isdn_ppp_mp_init(lp, NULL);
- if (retval < 0)
- goto out;
-#endif /* CONFIG_ISDN_MPP */
-
- retval = idev->ppp_slot;
+ if (copy_to_user(buf, skb->data, skb->len)) {
+ retval = -EFAULT;
+ goto out_free;
+ }
+ retval = skb->len;
+ out_free:
+ dev_kfree_skb(skb);
out:
- restore_flags(flags);
return retval;
}
-/*
- * kick the ipppd on the device
- * (wakes up daemon after B-channel connect)
- */
+/* --- write ------------------------------------------------------------ */
-static void
-isdn_ppp_wakeup_daemon(isdn_net_dev *idev)
+/* write() is always non blocking */
+static ssize_t
+ipppd_write(struct file *file, const char *buf, size_t count, loff_t *off)
{
- if (idev->ppp_slot < 0 || idev->ppp_slot >= ISDN_MAX_CHANNELS) {
- printk(KERN_ERR "%s: ppp_slot(%d) out of range\n",
- __FUNCTION__, idev->ppp_slot);
- return;
- }
- ippp_table[idev->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
- wake_up_interruptible(&ippp_table[idev->ppp_slot]->wq);
-}
+ isdn_net_dev *idev;
+ struct ipppd *ipppd;
+ struct sk_buff *skb;
+ char *p;
+ int retval;
+ u16 proto;
-/*
- * there was a hangup on the netdevice
- * force wakeup of the ippp device
- * go into 'device waits for release' state
- */
-static int
-isdn_ppp_closewait(int slot)
-{
- struct ippp_struct *is;
+ if (off != &file->f_pos)
+ return -ESPIPE;
- if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
- printk(KERN_ERR "%s: slot(%d) out of range\n",
- __FUNCTION__ , slot);
- return 0;
- }
- is = ippp_table[slot];
- if (is->state)
- wake_up_interruptible(&is->wq);
- is->state = IPPP_CLOSEWAIT;
- return 1;
-}
+ ipppd = file->private_data;
+ ipppd_debug(ipppd, "count = %d", count);
-/*
- * isdn_ppp_find_slot / isdn_ppp_free_slot
- */
+ if (ipppd->state != IPPPD_ST_CONNECTED) {
+ retval = -ENOTCONN;
+ goto out;
+ }
-static int
-isdn_ppp_get_slot(void)
-{
- int i;
- for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
- if (!ippp_table[i]->state)
- return i;
+ idev = ipppd->idev;
+ if (!idev) {
+ isdn_BUG();
+ retval = -ENODEV;
+ goto out;
}
- return -1;
+ /* Daemon needs to send at least full header, AC + proto */
+ if (count < 4) {
+ retval = -EMSGSIZE;
+ goto out;
+ }
+ skb = isdn_ppp_dev_alloc_skb(idev, count, GFP_KERNEL);
+ if (!skb) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ p = skb_put(skb, count);
+ if (copy_from_user(p, buf, count)) {
+ kfree_skb(skb);
+ retval = -EFAULT;
+ goto out;
+ }
+ /* Don't reset huptimer for LCP packets. (Echo requests). */
+ proto = PPP_PROTOCOL(p);
+ if (proto != PPP_LCP)
+ idev->huptimer = 0;
+
+ /* Keeps CCP/compression states in sync */
+ switch (proto) {
+ case PPP_CCP:
+ ippp_ccp_send_ccp(idev->mlp->ccp, skb);
+ break;
+ case PPP_CCPFRAG:
+ ippp_ccp_send_ccp(idev->ccp, skb);
+ break;
+ }
+ /* FIXME: Somewhere we need protection against the
+ * queue growing too large */
+ isdn_net_write_super(idev, skb);
+
+ retval = count;
+
+ out:
+ return retval;
}
-/*
- * isdn_ppp_open
- */
+/* --- poll ------------------------------------------------------------- */
-static int
-isdn_ppp_open(struct inode *ino, struct file *file)
+static unsigned int
+ipppd_poll(struct file *file, poll_table * wait)
{
- uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
- int slot;
- struct ippp_struct *is;
+ unsigned int mask;
+ struct ipppd *is;
- slot = isdn_ppp_get_slot();
- if (slot < 0) {
- return -EBUSY;
- }
- is = file->private_data = ippp_table[slot];
+ is = file->private_data;
- printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", slot, minor, is->state);
+ ipppd_debug(is, "");
- /* compression stuff */
- 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;
+ /* just registers wait_queue hook. This doesn't really wait. */
+ poll_wait(file, &is->wq, wait);
- is->reset = isdn_ppp_ccp_reset_alloc(is);
+ if (is->flags & IPPPD_FL_HUP) {
+ mask = POLLHUP;
+ goto out;
+ }
+ /* we're always ready to send .. */
+ mask = POLLOUT | POLLWRNORM;
- is->idev = NULL;
- is->mp_seqno = 0; /* MP sequence number */
- is->pppcfg = 0; /* ppp configuration */
- is->mpppcfg = 0; /* mppp configuration */
- is->last_link_seqno = -1; /* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
- is->unit = -1; /* set, when we have our interface */
- is->mru = 1524; /* MRU, default 1524 */
- is->maxcid = 16; /* VJ: maxcid */
- is->tk = current;
- init_waitqueue_head(&is->wq);
- is->minor = minor;
-#ifdef CONFIG_ISDN_PPP_VJ
/*
- * VJ header compression init
+ * if IPPP_FL_WAKEUP is set we return even if we have nothing to read
*/
- is->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */
-#endif
-
- is->state = IPPP_OPEN;
- isdn_lock_drivers();
-
- return 0;
-}
-
-/*
- * release ippp device
- */
-static int
-isdn_ppp_release(struct inode *ino, struct file *file)
-{
- uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
- struct ippp_struct *is;
-
- lock_kernel();
-
- is = file->private_data;
-
- if (is->debug & 0x1)
- printk(KERN_DEBUG "ippp: release, minor: %d %p\n", minor, is->idev);
-
- if (is->idev) { /* a lp address says: this link is still up */
- /*
- * isdn_net_hangup() calls isdn_ppp_free()
- * isdn_ppp_free() sets is->lp to NULL and lp->ppp_slot to -1
- * removing the IPPP_CONNECT flag omits calling of isdn_ppp_wakeup_daemon()
- */
- is->state &= ~IPPP_CONNECT;
- isdn_net_hangup(is->idev);
+ if (!skb_queue_empty(&is->rq) || is->flags & IPPPD_FL_WAKEUP) {
+ is->flags &= ~IPPPD_FL_WAKEUP;
+ mask |= POLLIN | POLLRDNORM;
+ set_current_state(TASK_INTERRUPTIBLE); // FIXME
+ schedule_timeout(HZ);
}
- skb_queue_purge(&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;
-
- /* Clean up if necessary */
- if(is->reset)
- isdn_ppp_ccp_reset_free(is);
-
- /* this slot is ready for new connections */
- is->state = 0;
-
- isdn_unlock_drivers();
-
- unlock_kernel();
- return 0;
+ out:
+ return mask;
}
-/*
- * get_arg .. ioctl helper
- */
+/* --- ioctl ------------------------------------------------------------ */
+
+/* get_arg .. ioctl helper */
static int
-get_arg(void *b, void *val, int len)
+get_arg(unsigned long arg, void *val, int len)
{
- if (len <= 0)
- len = sizeof(void *);
- if (copy_from_user((void *) val, b, len))
+ if (copy_from_user((void *) val, (void *) arg, len))
return -EFAULT;
return 0;
}
-/*
- * set arg .. ioctl helper
- */
+/* set arg .. ioctl helper */
static int
-set_arg(void *b, void *val,int len)
+set_arg(unsigned long arg, void *val,int len)
{
- if (copy_to_user(b, (void *) val, len))
+ if (copy_to_user((void *) arg, (void *) val, len))
return -EFAULT;
return 0;
}
-/*
- * ippp device ioctl
- */
static int
-isdn_ppp_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned long arg)
+ipppd_ioctl(struct inode *ino, struct file *file, unsigned int cmd,
+ unsigned long arg)
{
isdn_net_dev *idev;
unsigned long val;
- int r,i,j;
- struct ippp_struct *is;
+ int r;
+ struct ipppd *is;
struct isdn_ppp_comp_data data;
+ unsigned int cfg;
- is = (struct ippp_struct *) file->private_data;
+ is = file->private_data;
idev = is->idev;
- if (is->debug & 0x1)
- printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", is->minor, cmd, is->state);
-
- if (!(is->state & IPPP_OPEN))
- return -EINVAL;
+ ipppd_debug(is, "cmd %#x", cmd);
switch (cmd) {
- case PPPIOCBUNDLE:
+ case PPPIOCBUNDLE:
#ifdef CONFIG_ISDN_MPP
- if (!(is->state & IPPP_CONNECT))
- return -EINVAL;
- 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) is->minor, (int) is->unit, (int) val);
- return isdn_ppp_bundle(is, val);
-#else
- return -1;
-#endif
- break;
- case PPPIOCGUNIT: /* get ppp/isdn unit number */
- if ((r = set_arg((void *) arg, &is->unit, sizeof(is->unit) )))
- return r;
- break;
- case PPPIOCGIFNAME:
- if(!idev)
- return -EINVAL;
- if ((r = set_arg((void *) arg, idev->name, strlen(idev->name))))
- return r;
- break;
- case PPPIOCGMPFLAGS: /* get configuration flags */
- if ((r = set_arg((void *) arg, &is->mpppcfg, sizeof(is->mpppcfg) )))
- return r;
+ if (is->state != IPPPD_ST_CONNECTED) {
+ r = -EINVAL;
break;
- case PPPIOCSMPFLAGS: /* set configuration flags */
- 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,sizeof(is->pppcfg) )))
- return r;
+ }
+ r = get_arg(arg, &val, sizeof(val));
+ if (r)
break;
- case PPPIOCSFLAGS: /* set configuration flags */
- 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)) {
- if (idev) {
- /* OK .. we are ready to send buffers */
- isdn_net_dev_wake_queue(idev);
- }
- }
- is->pppcfg = val;
+
+ printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
+ is->minor, is->unit, val);
+ r = isdn_ppp_bundle(is, val);
+#else
+ r = -EINVAL;
+#endif
+ break;
+ case PPPIOCGUNIT: /* get ppp/isdn unit number */
+ r = set_arg(arg, &is->unit, sizeof(is->unit));
+ break;
+ case PPPIOCGDEBUG:
+ r = set_arg(arg, &is->debug, sizeof(is->debug));
+ break;
+ case PPPIOCSDEBUG:
+ r = get_arg(arg, &val, sizeof(val));
+ if (r)
break;
- case PPPIOCGIDLE: /* get idle time information */
- if (idev) {
- struct ppp_idle pidle;
- pidle.xmit_idle = pidle.recv_idle = idev->huptimer;
- if ((r = set_arg((void *) arg, &pidle,sizeof(struct ppp_idle))))
- return r;
- }
+ is->debug = val;
+ if (idev) {
+ idev->debug = val;
+ idev->mlp->debug = val;
+ }
+ break;
+ case PPPIOCGCOMPRESSORS:
+ {
+ unsigned long protos[8];
+ ippp_ccp_get_compressors(protos);
+ r = set_arg(arg, protos, sizeof(protos));
+ break;
+ }
+ default:
+ r = -ENOTTY;
+ break;
+ }
+
+ if (r != -ENOTTY)
+ goto out;
+
+ if (!idev) {
+ r = -ENODEV;
+ goto out;
+ }
+
+ switch (cmd) {
+ case PPPIOCGIFNAME:
+ r = set_arg(arg, idev->name, strlen(idev->name)+1);
+ break;
+ case PPPIOCGMPFLAGS: /* get configuration flags */
+ r = set_arg(arg, &idev->mlp->mpppcfg, sizeof(idev->mlp->mpppcfg));
+ break;
+ case PPPIOCSMPFLAGS: /* set configuration flags */
+ r = get_arg(arg, &val, sizeof(val));
+ if (r)
break;
- case PPPIOCSMRU: /* set receive unit size for PPP */
- if ((r = get_arg((void *) arg, &val, sizeof(val) )))
- return r;
- is->mru = val;
+ idev->mlp->mpppcfg = val;
+ break;
+ case PPPIOCGFLAGS: /* get configuration flags */
+ cfg = idev->pppcfg | ippp_ccp_get_flags(idev->ccp);
+ r = set_arg(arg, &cfg, sizeof(cfg));
+ break;
+ case PPPIOCSFLAGS: /* set configuration flags */
+ r = get_arg(arg, &val, sizeof(val));
+ if (r)
break;
- case PPPIOCSMPMRU:
+ if ((val & SC_ENABLE_IP) && !(idev->pppcfg & SC_ENABLE_IP)) {
+ idev->pppcfg = val;
+ /* OK .. we are ready to send buffers */
+ isdn_net_online(idev);
break;
- case PPPIOCSMPMTU:
+ }
+ idev->pppcfg = val;
+ break;
+ case PPPIOCGIDLE: /* get idle time information */
+ {
+ struct ppp_idle pidle;
+ pidle.xmit_idle = pidle.recv_idle = idev->huptimer;
+ r = set_arg(arg, &pidle,sizeof(pidle));
+ break;
+ }
+ case PPPIOCSMRU: /* set receive unit size for PPP */
+ r = get_arg(arg, &val, sizeof(val));
+ if (r)
break;
- case PPPIOCSMAXCID: /* set the maximum compression slot id */
- if ((r = get_arg((void *) arg, &val, sizeof(val) )))
- return r;
- val++;
- if (is->maxcid != val) {
-#ifdef CONFIG_ISDN_PPP_VJ
- struct slcompress *sltmp;
-#endif
- if (is->debug & 0x1)
- printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n", val);
- is->maxcid = val;
+ r = ippp_ccp_set_mru(idev->ccp, val);
+ break;
+ case PPPIOCSMPMRU:
+ break;
+ case PPPIOCSMPMTU:
+ break;
#ifdef CONFIG_ISDN_PPP_VJ
- sltmp = slhc_init(16, val);
- if (!sltmp) {
- printk(KERN_ERR "ippp, can't realloc slhc struct\n");
- return -ENOMEM;
- }
- if (is->slcomp)
- slhc_free(is->slcomp);
- is->slcomp = sltmp;
-#endif
- }
- break;
- case PPPIOCGDEBUG:
- if ((r = set_arg((void *) arg, &is->debug, sizeof(is->debug) )))
- return r;
+ case PPPIOCSMAXCID: /* set the maximum compression slot id */
+ {
+ struct slcompress *sltmp;
+ r = get_arg(arg, &val, sizeof(val));
+ if (r)
break;
- case PPPIOCSDEBUG:
- if ((r = get_arg((void *) arg, &val, sizeof(val) )))
- return r;
- is->debug = val;
+ val++;
+ sltmp = slhc_init(16, val);
+ if (!sltmp) {
+ r = -ENOMEM;
break;
- case PPPIOCGCOMPRESSORS:
- {
- unsigned long protos[8] = {0,};
- struct isdn_ppp_compressor *ipc = ipc_head;
- while(ipc) {
- 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,8*sizeof(long) )))
- return r;
- }
+ }
+ if (idev->mlp->slcomp)
+ slhc_free(idev->mlp->slcomp);
+ idev->mlp->slcomp = sltmp;
+ r = 0;
+ break;
+ }
+#endif
+ case PPPIOCSCOMPRESSOR:
+ r = get_arg(arg, &data, sizeof(data));
+ if (r)
break;
- case PPPIOCSCOMPRESSOR:
- if ((r = get_arg((void *) arg, &data, sizeof(struct isdn_ppp_comp_data))))
- return r;
- return isdn_ppp_set_compressor(is, &data);
- case PPPIOCGCALLINFO:
- {
- isdn_net_local *mlp;
- struct isdn_net_phone *phone;
- struct pppcallinfo pci;
- int i;
- memset((char *) &pci,0,sizeof(struct pppcallinfo));
- if(idev) {
- mlp = idev->mlp;
- strncpy(pci.local_num, mlp->msn, 63);
- i = 0;
- list_for_each_entry(phone, &mlp->phone[1], list) {
- if (i++ == idev->dial) {
- strncpy(pci.remote_num,phone->num,63);
- break;
- }
- }
- pci.charge_units = idev->charge;
- if(idev->outgoing)
- pci.calltype = CALLTYPE_OUTGOING;
- else
- pci.calltype = CALLTYPE_INCOMING;
- if(mlp->flags & ISDN_NET_CALLBACK)
- pci.calltype |= CALLTYPE_CALLBACK;
- }
- return set_arg((void *)arg,&pci,sizeof(struct pppcallinfo));
+ r = isdn_ppp_set_compressor(idev, &data);
+ break;
+ case PPPIOCGCALLINFO:
+ {
+ isdn_net_local *mlp;
+ struct isdn_net_phone *phone;
+ struct pppcallinfo pci;
+ int i;
+ memset(&pci, 0, sizeof(pci));
+
+ mlp = idev->mlp;
+ strncpy(pci.local_num, mlp->msn, 63);
+ i = 0;
+ list_for_each_entry(phone, &mlp->phone[1], list) {
+ if (i++ == idev->dial) {
+ strncpy(pci.remote_num,phone->num,63);
+ break;
}
- default:
- break;
- }
- return 0;
-}
-
-static unsigned int
-isdn_ppp_poll(struct file *file, poll_table * wait)
-{
- unsigned int mask;
- struct ippp_struct *is;
-
- is = file->private_data;
-
- if (is->debug & 0x2)
- 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) {
- mask = POLLHUP;
- goto out;
}
- printk(KERN_DEBUG "isdn_ppp: device not open\n");
- mask = POLLERR;
- goto out;
+ pci.charge_units = idev->charge;
+ if (idev->outgoing)
+ pci.calltype = CALLTYPE_OUTGOING;
+ else
+ pci.calltype = CALLTYPE_INCOMING;
+ if (mlp->flags & ISDN_NET_CALLBACK)
+ pci.calltype |= CALLTYPE_CALLBACK;
+ r = set_arg(arg, &pci, sizeof(pci));
+ break;
}
- /* we're always ready to send .. */
- mask = POLLOUT | POLLWRNORM;
-
- /*
- * if IPPP_NOBLOCK is set we return even if we have nothing to read
- */
- if (!skb_queue_empty(&is->rq) || is->state & IPPP_NOBLOCK) {
- is->state &= ~IPPP_NOBLOCK;
- mask |= POLLIN | POLLRDNORM;
+ default:
+ r = -ENOTTY;
+ break;
}
-
out:
- return mask;
+ return r;
}
-/*
- * fill up isdn_ppp_read() queue ..
- */
+/* --- fops ------------------------------------------------------------- */
+
+struct file_operations isdn_ppp_fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = ipppd_read,
+ .write = ipppd_write,
+ .poll = ipppd_poll,
+ .ioctl = ipppd_ioctl,
+ .open = ipppd_open,
+ .release = ipppd_release,
+};
+
+/* --- ipppd_queue_read ------------------------------------------------- */
+
+/* Queue packets for ipppd to read(). */
static int
-isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
+ipppd_queue_read(struct ipppd *is, u16 proto, unsigned char *buf, int len)
{
struct sk_buff *skb;
unsigned char *p;
- struct ippp_struct *is;
-
- if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
- printk(KERN_WARNING "ippp: illegal slot(%d).\n", slot);
- return 0;
- }
- is = ippp_table[slot];
+ int retval;
- if (!(is->state & IPPP_CONNECT)) {
- printk(KERN_DEBUG "ippp: device not activated.\n");
- return 0;
+ if (is->state != IPPPD_ST_CONNECTED) {
+ printk(KERN_DEBUG "ippp: device not connected.\n");
+ retval = -ENOTCONN;
+ goto out;
}
if (skb_queue_len(&is->rq) > IPPP_MAX_RQ_LEN) {
printk(KERN_WARNING "ippp: Queue is full\n");
- return 0;
+ retval = -EBUSY;
+ goto out;
}
skb = dev_alloc_skb(len + 4);
if (!skb) {
printk(KERN_WARNING "ippp: Can't alloc buf\n");
- return 0;
+ retval = -ENOMEM;
+ goto out;
}
p = skb_put(skb, 4);
p += put_u8(p, PPP_ALLSTATIONS);
@@ -655,151 +555,274 @@ isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
memcpy(skb_put(skb, len), buf, len);
skb_queue_tail(&is->rq, skb);
- wake_up_interruptible(&is->wq);
+ wake_up(&is->wq);
- return len;
+ retval = len;
+ out:
+ return retval;
}
+/* ====================================================================== */
+
+/* Prototypes */
+static void isdn_ppp_push_higher(isdn_net_local *lp, isdn_net_dev *idev,
+ struct sk_buff *skb, int proto);
+static int isdn_ppp_if_get_unit(char *namebuf);
+
+static void
+isdn_ppp_dev_push_header(void *priv, struct sk_buff *skb, u16 proto);
+
+static void
+isdn_ppp_dev_xmit(void *priv, struct sk_buff *skb);
+
+static struct sk_buff *
+isdn_ppp_lp_alloc_skb(void *priv, int len, int gfp_mask);
+
+static void
+isdn_ppp_lp_push_header(void *priv, struct sk_buff *skb, u16 proto);
+
+/* New CCP stuff */
+static void
+isdn_ppp_dev_kick_up(void *priv);
+
+#ifdef CONFIG_ISDN_MPP
+static ippp_bundle * isdn_ppp_bundle_arr = NULL;
+
+static int isdn_ppp_mp_bundle_array_init(void);
+static int isdn_ppp_mp_init(isdn_net_local *lp, ippp_bundle *add_to);
+static void isdn_ppp_mp_receive(isdn_net_local *lp, isdn_net_dev *idev,
+ struct sk_buff *skb);
+static void isdn_ppp_mp_cleanup(isdn_net_local *lp );
+
+static int isdn_ppp_bundle(struct ipppd *, int unit);
+#endif /* CONFIG_ISDN_MPP */
+
+char *isdn_ppp_revision = "$Revision: 1.85.6.9 $";
+
/*
- * read() .. non-blocking: ipppd calls it only after select()
- * reports, that there is data
+ * frame log (debug)
*/
-
-static ssize_t
-isdn_ppp_read(struct file *file, char *buf, size_t count, loff_t *off)
+void
+isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot)
{
- struct ippp_struct *is;
- struct sk_buff *skb;
- int retval;
+ int cnt,
+ j,
+ i;
+ char buf[80];
- if (off != &file->f_pos)
- return -ESPIPE;
-
- is = file->private_data;
+ if (len < maxlen)
+ maxlen = len;
- if (!(is->state & IPPP_OPEN)) {
- retval = 0;
- goto out;
- }
- skb = skb_dequeue(&is->rq);
- if (!skb) {
- retval = -EAGAIN;
- goto out;
- }
- if (skb->len > count) {
- retval = -EMSGSIZE;
- goto out_free;
+ 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 "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf);
}
- if (copy_to_user(buf, skb->data, skb->len)) {
- retval = -EFAULT;
- goto out_free;
+}
+
+
+static void
+isdn_ppp_push_header(isdn_net_dev *idev, struct sk_buff *skb, u16 proto)
+{
+ unsigned char *p;
+
+ if (skb_headroom(skb) < 4) {
+ isdn_BUG();
+ return;
}
- retval = skb->len;
- out_free:
- dev_kfree_skb(skb);
- out:
- return retval;
+ if ((idev->pppcfg & SC_COMP_PROT) && proto <= 0xff)
+ put_u8(skb_push(skb, 1), proto);
+ else
+ put_u16(skb_push(skb, 2), proto);
+
+ if (idev->pppcfg & SC_COMP_AC)
+ return;
+
+ p = skb_push(skb, 2);
+ p += put_u8(p, PPP_ALLSTATIONS);
+ p += put_u8(p, PPP_UI);
}
/*
- * ipppd wanna write a packet to the card .. non-blocking
+ * 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
*/
-
-static ssize_t
-isdn_ppp_write(struct file *file, const char *buf, size_t count, loff_t *off)
+static void
+isdn_ppp_unbind(isdn_net_dev *idev)
{
- isdn_net_dev *idev;
- struct ippp_struct *is;
- int proto;
- unsigned char protobuf[4];
- int retval;
+ struct ipppd *is = idev->ipppd;
+
+ if (!is) {
+ isdn_BUG();
+ return;
+ }
- if (off != &file->f_pos)
- return -ESPIPE;
+ ipppd_debug(is, "");
- lock_kernel();
+ if (is->state != IPPPD_ST_ASSIGNED)
+ isdn_BUG();
- is = file->private_data;
+ is->state = IPPPD_ST_OPEN;
- if (!(is->state & IPPP_CONNECT)) {
- retval = 0;
- goto out;
- }
+ /* is->idev will be invalid shortly */
+ ippp_ccp_free(idev->ccp);
- /* -> push it directly to the lowlevel interface */
+ is->idev = NULL;
+ /* lose the reference we took on isdn_ppp_bind */
+ ipppd_put(is);
+ idev->ipppd = NULL;
- idev = is->idev;
- if (!idev)
- printk(KERN_DEBUG "isdn_ppp_write: idev == NULL\n");
- else {
+ return;
+}
+
+/*
+ * bind isdn_net_local <=> ippp-device
+ */
+int
+isdn_ppp_bind(isdn_net_dev *idev)
+{
+ int unit = 0;
+ unsigned long flags;
+ int retval = 0;
+ struct ipppd *ipppd;
+
+ if (idev->ipppd) {
+ isdn_BUG();
+ return 0;
+ }
+
+ spin_lock_irqsave(&ipppds_lock, flags);
+ if (idev->pppbind < 0) { /* device bound to ippp device ? */
+ struct list_head *l;
+ char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */
+ memset(exclusive, 0, ISDN_MAX_CHANNELS);
+ /* step through net devices to find exclusive minors */
+ list_for_each(l, &isdn_net_devs) {
+ isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list);
+ if (p->pppbind >= 0 && p->pppbind < ISDN_MAX_CHANNELS)
+ exclusive[p->pppbind] = 1;
+ }
/*
- * Don't reset huptimer for
- * LCP packets. (Echo requests).
+ * search a free device / slot
*/
- if (copy_from_user(protobuf, buf, 4)) {
- retval = -EFAULT;
- goto out;
+ list_for_each_entry(ipppd, &ipppds, ipppds) {
+ if (!ipppd)
+ continue;
+ if (ipppd->state != IPPPD_ST_OPEN)
+ continue;
+ if (!exclusive[ipppd->minor])
+ break;
+ goto found;
}
- proto = PPP_PROTOCOL(protobuf);
- if (proto != PPP_LCP)
- idev->huptimer = 0;
-
- if (idev->isdn_slot < 0) {
- retval = 0;
- goto out;
+ } else {
+ list_for_each_entry(ipppd, &ipppds, ipppds) {
+ if (!ipppd)
+ continue;
+ if (ipppd->state != IPPPD_ST_OPEN)
+ continue;
+ if (ipppd->minor == idev->pppbind)
+ goto found;
}
- if ((dev->drv[isdn_slot_driver(idev->isdn_slot)]->flags & DRV_FLAG_RUNNING) &&
- isdn_net_online(idev)) {
- unsigned short hl;
- struct sk_buff *skb;
- /*
- * we need to reserve enought space in front of
- * sk_buff. old call to dev_alloc_skb only reserved
- * 16 bytes, now we are looking what the driver want
- */
- hl = isdn_slot_hdrlen(idev->isdn_slot);
- skb = alloc_skb(hl+count, GFP_ATOMIC);
- if (!skb) {
- printk(KERN_WARNING "isdn_ppp_write: out of memory!\n");
- retval = count;
- goto out;
- }
- skb_reserve(skb, hl);
- if (copy_from_user(skb_put(skb, count), buf, count))
- {
- kfree_skb(skb);
- retval = -EFAULT;
- goto out;
- }
- 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,is->unit,idev->ppp_slot);
- }
+ }
- isdn_ppp_send_ccp(idev,idev->mlp,skb); /* keeps CCP/compression states in sync */
+ printk(KERN_INFO "isdn_ppp_bind: no ipppd\n");
+ retval = -ESRCH;
+ goto err;
- isdn_net_write_super(idev, skb);
- }
+ found:
+ unit = isdn_ppp_if_get_unit(idev->name); /* get unit number from interface name .. ugly! */
+ if (unit < 0) {
+ printk(KERN_INFO "isdn_ppp_bind: illegal interface name %s.\n", idev->name);
+ retval = -ENODEV;
+ goto err;
}
- retval = count;
+ ipppd->unit = unit;
+ ipppd->state = IPPPD_ST_ASSIGNED;
+ ipppd->idev = idev;
+ /* we hold a reference until isdn_ppp_unbind() */
+ idev->ipppd = ipppd_get(ipppd);
+ spin_unlock_irqrestore(&ipppds_lock, flags);
+
+ idev->pppcfg = 0; /* config flags */
+ /* seq no last seen, maybe set to bundle min, when joining? */
+ idev->pppseq = -1;
+
+ idev->ccp = ippp_ccp_alloc();
+ if (!idev->ccp) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ idev->ccp->proto = PPP_COMPFRAG;
+ idev->ccp->priv = idev;
+ idev->ccp->alloc_skb = isdn_ppp_dev_alloc_skb;
+ idev->ccp->push_header = isdn_ppp_dev_push_header;
+ idev->ccp->xmit = isdn_ppp_dev_xmit;
+ idev->ccp->kick_up = isdn_ppp_dev_kick_up;
+
+#ifdef CONFIG_ISDN_MPP
+ retval = isdn_ppp_mp_init(lp, NULL);
+#endif /* CONFIG_ISDN_MPP */
out:
- unlock_kernel();
+ if (retval) {
+ idev->ipppd->state = IPPPD_ST_OPEN;
+ ipppd_put(idev->ipppd);
+ idev->ipppd = NULL;
+ }
+
+ return retval;
+
+ err:
+ spin_unlock_irqrestore(&ipppds_lock, flags);
return retval;
}
-struct file_operations isdn_ppp_fops =
+/*
+ * kick the ipppd on the device
+ * (wakes up daemon after B-channel connect)
+ */
+
+static void
+isdn_ppp_connected(isdn_net_dev *idev)
{
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = isdn_ppp_read,
- .write = isdn_ppp_write,
- .poll = isdn_ppp_poll,
- .ioctl = isdn_ppp_ioctl,
- .open = isdn_ppp_open,
- .release = isdn_ppp_release,
-};
+ struct ipppd *ipppd = idev->ipppd;
+
+ ipppd_debug(ipppd, "");
+
+ ipppd->state = IPPPD_ST_CONNECTED;
+ ipppd->flags |= IPPPD_FL_WAKEUP;
+ wake_up(&ipppd->wq);
+}
+
+static void
+isdn_ppp_disconnected(isdn_net_dev *idev)
+{
+ struct ipppd *ipppd = idev->ipppd;
+
+ ipppd_debug(ipppd, "");
+
+ if (idev->pppcfg & SC_ENABLE_IP)
+ isdn_net_offline(idev);
+
+ if (ipppd->state != IPPPD_ST_CONNECTED)
+ isdn_BUG();
+
+ ipppd->state = IPPPD_ST_ASSIGNED;
+ ipppd->flags |= IPPPD_FL_HUP;
+ wake_up(&ipppd->wq);
+
+#ifdef CONFIG_ISDN_MPP
+ spin_lock(&idev->pb->lock);
+ if (lp->netdev->pb->ref_ct == 1) /* last link in queue? */
+ isdn_ppp_mp_cleanup(lp);
+
+ lp->netdev->pb->ref_ct--;
+ spin_unlock(&lp->netdev->pb->lock);
+#endif /* CONFIG_ISDN_MPP */
+
+}
/*
* init memory, structures etc.
@@ -808,37 +831,17 @@ struct file_operations isdn_ppp_fops =
int
isdn_ppp_init(void)
{
- int i,
- j;
-
#ifdef CONFIG_ISDN_MPP
if( isdn_ppp_mp_bundle_array_init() < 0 )
return -ENOMEM;
#endif /* CONFIG_ISDN_MPP */
- for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
- if (!(ippp_table[i] = (struct ippp_struct *)
- kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) {
- printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
- for (j = 0; j < i; j++)
- kfree(ippp_table[j]);
- return -1;
- }
- memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
- ippp_table[i]->state = 0;
- skb_queue_head_init(&ippp_table[i]->rq);
- }
return 0;
}
void
isdn_ppp_cleanup(void)
{
- int i;
-
- for (i = 0; i < ISDN_MAX_CHANNELS; i++)
- kfree(ippp_table[i]);
-
#ifdef CONFIG_ISDN_MPP
if (isdn_ppp_bundle_arr)
kfree(isdn_ppp_bundle_arr);
@@ -850,25 +853,31 @@ isdn_ppp_cleanup(void)
* check for address/control field and skip if allowed
* retval != 0 -> discard packet silently
*/
-static int isdn_ppp_skip_ac(struct ippp_struct *is, struct sk_buff *skb)
+static int isdn_ppp_skip_ac(isdn_net_dev *idev, struct sk_buff *skb)
{
+ u8 val;
+
if (skb->len < 1)
return -1;
- if (skb->data[0] == 0xff) {
- if (skb->len < 2)
- return -1;
-
- if (skb->data[1] != 0x03)
+ get_u8(skb->data, &val);
+ if (val != PPP_ALLSTATIONS) {
+ /* if AC compression was not negotiated, but no AC present,
+ discard packet */
+ if (idev->pppcfg & SC_REJ_COMP_AC)
return -1;
- // skip address/control (AC) field
- skb_pull(skb, 2);
- } else {
- if (is->pppcfg & SC_REJ_COMP_AC)
- // if AC compression was not negotiated, but used, discard packet
- return -1;
+ return 0;
}
+ if (skb->len < 2)
+ return -1;
+
+ get_u8(skb->data + 1, &val);
+ if (val != PPP_UI)
+ return -1;
+
+ /* skip address/control (AC) field */
+ skb_pull(skb, 2);
return 0;
}
@@ -876,84 +885,80 @@ static int isdn_ppp_skip_ac(struct ippp_struct *is, struct sk_buff *skb)
* get the PPP protocol header and pull skb
* retval < 0 -> discard packet silently
*/
-static int isdn_ppp_strip_proto(struct sk_buff *skb)
+int isdn_ppp_strip_proto(struct sk_buff *skb)
{
- int proto;
-
+ u16 proto;
+ u8 val;
+
if (skb->len < 1)
return -1;
- if (skb->data[0] & 0x1) {
- // protocol field is compressed
- proto = skb->data[0];
+ get_u8(skb->data, &val);
+ if (val & 0x1) {
+ /* protocol field is compressed */
+ proto = val;
skb_pull(skb, 1);
} else {
if (skb->len < 2)
return -1;
- proto = ((int) skb->data[0] << 8) + skb->data[1];
+ get_u16(skb->data, &proto);
skb_pull(skb, 2);
}
return proto;
}
-
/*
* handler for incoming packets on a syncPPP interface
*/
static void isdn_ppp_receive(isdn_net_local *lp, isdn_net_dev *idev,
struct sk_buff *skb)
{
- struct ippp_struct *is;
- int slot;
+ struct ipppd *is;
int proto;
- /*
- * If encapsulation is syncppp, don't reset
- * huptimer on LCP packets.
- */
- if (PPP_PROTOCOL(skb->data) != PPP_LCP)
- idev->huptimer = 0;
-
- slot = idev->ppp_slot;
- if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
- printk(KERN_ERR "isdn_ppp_receive: lp->ppp_slot(%d)\n",
- slot);
- kfree_skb(skb);
- return;
- }
- is = ippp_table[slot];
+ is = idev->ipppd;
+ if (!is)
+ goto err;
if (is->debug & 0x4) {
- printk(KERN_DEBUG "ippp_receive: is:%p lp:%p slot:%d unit:%d len:%d\n",
- is,lp,idev->ppp_slot,is->unit,(int) skb->len);
- isdn_ppp_frame_log("receive", skb->data, skb->len, 32,is->unit,idev->ppp_slot);
+ printk(KERN_DEBUG "ippp_receive: is:%p lp:%p unit:%d len:%d\n",
+ is, lp, is->unit, skb->len);
+ isdn_ppp_frame_log("receive", skb->data, skb->len, 32,is->unit,-1);
}
- if (isdn_ppp_skip_ac(is, skb) < 0) {
- kfree_skb(skb);
- return;
- }
+ if (isdn_ppp_skip_ac(idev, skb) < 0)
+ goto err;
+
proto = isdn_ppp_strip_proto(skb);
- if (proto < 0) {
- kfree_skb(skb);
- return;
- }
+ if (proto < 0)
+ goto err;
+
+ /* Don't reset huptimer on LCP packets. */
+ if (proto != PPP_LCP)
+ idev->huptimer = 0;
#ifdef CONFIG_ISDN_MPP
if (is->compflags & SC_LINK_DECOMP_ON) {
skb = isdn_ppp_decompress(skb, is, NULL, &proto);
if (!skb) // decompression error
- return;
+ goto put;
}
if (!(is->mpppcfg & SC_REJ_MP_PROT)) { // we agreed to receive MPPP
if (proto == PPP_MP) {
isdn_ppp_mp_receive(lp, idev, skb);
- return;
+ goto put;
}
}
-#endif
isdn_ppp_push_higher(lp, idev, skb, proto);
+ put:
+#else
+ isdn_ppp_push_higher(lp, idev, skb, proto);
+#endif
+ return;
+
+ err:
+ kfree_skb(skb);
}
/*
@@ -966,28 +971,16 @@ isdn_ppp_push_higher(isdn_net_local *lp, isdn_net_dev *idev,
struct sk_buff *skb, int proto)
{
struct net_device *dev = &lp->dev;
- struct ippp_struct *is, *mis;
- int slot;
-
- slot = idev->ppp_slot;
- if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
- printk(KERN_ERR "isdn_ppp_push_higher: lp->ppp_slot(%d)\n",
- slot);
- goto drop_packet;
- }
- is = ippp_table[slot];
-
- mis = ippp_table[slot];
+ struct ipppd *is = idev->ipppd;
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,is->unit,slot);
+ isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit, -1);
}
- if (mis->compflags & SC_DECOMP_ON) {
- skb = isdn_ppp_decompress(skb, is, mis, &proto);
- if (!skb) // decompression error
- return;
- }
+ skb = ippp_ccp_decompress(lp->ccp, skb, &proto);
+ if (!skb) // decompression error
+ goto out;
+
switch (proto) {
case PPP_IPX: /* untested */
if (is->debug & 0x20)
@@ -1002,19 +995,14 @@ isdn_ppp_push_higher(isdn_net_local *lp, isdn_net_dev *idev,
case PPP_COMP:
case PPP_COMPFRAG:
printk(KERN_INFO "isdn_ppp: unexpected compressed frame dropped\n");
- goto drop_packet;
+ goto drop;
#ifdef CONFIG_ISDN_PPP_VJ
case PPP_VJC_UNCOMP:
if (is->debug & 0x20)
printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
- if (idev->ppp_slot < 0) {
- printk(KERN_ERR "%s: net_dev->ppp_slot(%d) out of range\n",
- __FUNCTION__ , idev->ppp_slot);
- goto drop_packet;
- }
- if (slhc_remember(ippp_table[idev->ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
+ if (slhc_remember(lp->slcomp, skb->data, skb->len) <= 0) {
printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
- goto drop_packet;
+ goto drop;
}
skb->protocol = htons(ETH_P_IP);
break;
@@ -1029,82 +1017,55 @@ isdn_ppp_push_higher(isdn_net_local *lp, isdn_net_dev *idev,
if (!skb) {
printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
skb = skb_old;
- goto drop_packet;
+ goto drop;
}
skb_put(skb, skb_old->len + 128);
memcpy(skb->data, skb_old->data, skb_old->len);
- if (idev->ppp_slot < 0) {
- printk(KERN_ERR "%s: net_dev->ppp_slot(%d) out of range\n",
- __FUNCTION__ , idev->ppp_slot);
- goto drop_packet;
- }
- pkt_len = slhc_uncompress(ippp_table[idev->ppp_slot]->slcomp,
+ pkt_len = slhc_uncompress(lp->slcomp,
skb->data, skb_old->len);
kfree_skb(skb_old);
if (pkt_len < 0)
- goto drop_packet;
+ goto drop;
skb_trim(skb, pkt_len);
skb->protocol = htons(ETH_P_IP);
}
break;
#endif
- case PPP_CCP:
case PPP_CCPFRAG:
- isdn_ppp_receive_ccp(idev,lp,skb,proto);
+ ippp_ccp_receive_ccp(idev->ccp, skb);
+ goto ccp;
+ case PPP_CCP:
+ ippp_ccp_receive_ccp(lp->ccp, skb);
+ ccp:
/* 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;
+ goto free;
/* fall through */
default:
- isdn_ppp_fill_rq(skb->data, skb->len, proto, idev->ppp_slot); /* push data to pppd device */
- kfree_skb(skb);
- return;
+ // FIXME use skb directly
+ ipppd_queue_read(is, proto, skb->data, skb->len);
+ goto free;
}
/* Reset hangup-timer */
idev->huptimer = 0;
skb->dev = dev;
- skb->mac.raw = skb->data;
netif_rx(skb);
/* net_dev->local->stats.rx_packets++; done in isdn_net.c */
+ out:
return;
- drop_packet:
+ drop:
lp->stats.rx_dropped++;
+ free:
kfree_skb(skb);
}
/*
- * isdn_ppp_skb_push ..
- * checks whether we have enough space at the beginning of the skb
- * and allocs a new SKB if necessary
- */
-static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len)
-{
- struct sk_buff *skb = *skb_p;
-
- if(skb_headroom(skb) < len) {
- struct sk_buff *nskb = skb_realloc_headroom(skb, len);
-
- if (!nskb) {
- printk(KERN_ERR "isdn_ppp_skb_push: can't realloc headroom!\n");
- dev_kfree_skb(skb);
- return NULL;
- }
- printk(KERN_DEBUG "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len);
- dev_kfree_skb(skb);
- *skb_p = nskb;
- return skb_push(nskb, len);
- }
- return skb_push(skb,len);
-}
-
-
-/*
* send ppp frame .. we expect a PIDCOMPressable proto --
* (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
*
@@ -1118,30 +1079,13 @@ isdn_ppp_start_xmit(struct sk_buff *skb, struct net_device *ndev)
isdn_net_local *mlp = ndev->priv;
isdn_net_dev *idev = list_entry(mlp->online.next, isdn_net_dev, online);
unsigned int proto = PPP_IP; /* 0x21 */
- struct ippp_struct *ipt,*ipts;
- int slot;
+ struct ipppd *ipppd;
ndev->trans_start = jiffies;
if (list_empty(&mlp->online))
return isdn_net_autodial(skb, ndev);
- slot = idev->ppp_slot;
- if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
- printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n",
- slot);
- kfree_skb(skb);
- return 0;
- }
- ipts = ippp_table[slot];
-
- if (!(ipts->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */
- if (ipts->debug & 0x1)
- printk(KERN_INFO "%s: IP frame delayed.\n", ndev->name);
- netif_stop_queue(ndev);
- return 1;
- }
-
switch (ntohs(skb->protocol)) {
case ETH_P_IP:
proto = PPP_IP;
@@ -1153,42 +1097,32 @@ isdn_ppp_start_xmit(struct sk_buff *skb, struct net_device *ndev)
printk(KERN_ERR "isdn_ppp: skipped unsupported protocol: %#x.\n",
skb->protocol);
dev_kfree_skb(skb);
- return 0;
+ goto out;
}
- idev = isdn_net_get_locked_dev(mlp);
+ idev = isdn_net_get_xmit_dev(mlp);
if (!idev) {
- printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
- netif_stop_queue(ndev);
- return 1;
+ printk(KERN_INFO "%s: IP frame delayed.\n", ndev->name);
+ goto stop;
}
- /* we have our lp locked from now on */
- slot = idev->ppp_slot;
- if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
- printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n",
- slot);
- kfree_skb(skb);
- return 0;
+ if (!(idev->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */
+ isdn_BUG();
+ goto stop;
}
- ipt = ippp_table[slot];
+ ipppd = idev->ipppd;
idev->huptimer = 0;
/*
* after this line .. requeueing in the device queue is no longer allowed!!!
*/
- /* Pull off the fake header we stuck on earlier to keep
- * the fragmentation code happy.
- */
- skb_pull(skb,IPPP_MAX_HEADER);
-
- if (ipt->debug & 0x4)
+ if (ipppd->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,idev->ppp_slot);
+ if (ipppd->debug & 0x40)
+ isdn_ppp_frame_log("xmit0", skb->data, skb->len, 32, ipppd->unit, -1);
#ifdef CONFIG_ISDN_PPP_VJ
- if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes, but check this again */
+ if (proto == PPP_IP && idev->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes, but check this again */
struct sk_buff *new_skb;
unsigned short hl;
/*
@@ -1213,8 +1147,8 @@ isdn_ppp_start_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_put(new_skb, skb->len);
buf = skb->data;
- pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data,
- &buf, !(ipts->pppcfg & SC_NO_TCP_CCID));
+ pktlen = slhc_compress(mlp->slcomp, skb->data, skb->len, new_skb->data,
+ &buf, !(idev->pppcfg & SC_NO_TCP_CCID));
if (buf != skb->data) {
if (new_skb->data != buf)
@@ -1241,17 +1175,9 @@ isdn_ppp_start_xmit(struct sk_buff *skb, struct net_device *ndev)
/*
* normal (single link) or bundle compression
*/
- if(ipts->compflags & SC_COMP_ON) {
- /* We send compressed only if both down- und upstream
- compression is negotiated, that means, CCP is up */
- if(ipts->compflags & SC_DECOMP_ON) {
- skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
- } else {
- printk(KERN_DEBUG "isdn_ppp: CCP not yet up - sending as-is\n");
- }
- }
+ skb = ippp_ccp_compress(mlp->ccp, skb, &proto);
- if (ipt->debug & 0x24)
+ if (ipppd->debug & 0x24)
printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto);
#ifdef CONFIG_ISDN_MPP
@@ -1281,45 +1207,29 @@ isdn_ppp_start_xmit(struct sk_buff *skb, struct net_device *ndev)
}
#endif
+#if 0
/*
* 'link in bundle' compression ...
*/
- if(ipt->compflags & SC_LINK_COMP_ON)
+ if (ipt->compflags & SC_LINK_COMP_ON)
skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
+#endif
- if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) {
- unsigned char *data = isdn_ppp_skb_push(&skb,1);
- if(!data)
- goto unlock;
- data[0] = proto & 0xff;
- }
- else {
- unsigned char *data = isdn_ppp_skb_push(&skb,2);
- if(!data)
- goto unlock;
- data[0] = (proto >> 8) & 0xff;
- data[1] = proto & 0xff;
- }
- if(!(ipt->pppcfg & SC_COMP_AC)) {
- unsigned char *data = isdn_ppp_skb_push(&skb,2);
- if(!data)
- goto unlock;
- data[0] = 0xff; /* All Stations */
- data[1] = 0x03; /* Unnumbered information */
- }
-
- /* tx-stats are now updated via BSENT-callback */
+ isdn_ppp_push_header(idev, skb, proto);
- if (ipts->debug & 0x40) {
+ if (ipppd->debug & 0x40) {
printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len);
- isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,idev->ppp_slot);
+ isdn_ppp_frame_log("xmit", skb->data, skb->len, 32, ipppd->unit, -1);
}
isdn_net_writebuf_skb(idev, skb);
- unlock:
- spin_unlock_bh(&idev->xmit_lock);
+ out:
return 0;
+
+ stop:
+ netif_stop_queue(ndev);
+ return 1;
}
#ifdef CONFIG_ISDN_MPP
@@ -1372,7 +1282,7 @@ static ippp_bundle * isdn_ppp_mp_bundle_alloc(void)
static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
{
isdn_net_dev *idev = lp->netdev;
- struct ippp_struct * is;
+ struct ipppd * is;
if (idev->ppp_slot < 0) {
printk(KERN_ERR "%s: >ppp_slot(%d) out of range\n",
@@ -1396,7 +1306,7 @@ static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
}
lp->netdev->pb->ref_ct++;
- is->last_link_seqno = 0;
+ is->pppseq = 0;
return 0;
}
@@ -1413,7 +1323,7 @@ static void isdn_ppp_mp_receive(isdn_net_local *lp, isdn_net_dev *dev,
struct sk_buff *skb)
{
isdn_net_dev *idev = lp->netdev;
- struct ippp_struct *is;
+ struct ipppd *is;
isdn_net_dev *qdev;
ippp_bundle * mp;
isdn_mppp_stats * stats;
@@ -1442,7 +1352,7 @@ static void isdn_ppp_mp_receive(isdn_net_local *lp, isdn_net_dev *dev,
isdn_ppp_mp_print_recv_pkt(slot, skb);
newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ,
- skb, is->last_link_seqno);
+ skb, is->pppseq);
/* if this packet seq # is less than last already processed one,
@@ -1460,14 +1370,14 @@ static void isdn_ppp_mp_receive(isdn_net_local *lp, isdn_net_dev *dev,
}
/* find the minimum received sequence number over all links */
- is->last_link_seqno = minseq = newseq;
+ is->pppseq = minseq = newseq;
list_for_each_entry(qdev, &lp->online, online) {
slot = qdev->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "%s: lpq->ppp_slot(%d)\n",
__FUNCTION__ ,slot);
} else {
- u32 lls = ippp_table[slot]->last_link_seqno;
+ u32 lls = ippp_table[slot]->pppseq;
if (MP_LT(lls, minseq))
minseq = lls;
}
@@ -1754,7 +1664,7 @@ static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb )
}
static int
-isdn_ppp_bundle(struct ippp_struct *is, int unit)
+isdn_ppp_bundle(struct ipppd *is, int unit)
{
char ifn[IFNAMSIZ + 1];
isdn_net_dev *p;
@@ -1804,11 +1714,11 @@ out:
*/
static int
-isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct net_device *dev)
+isdn_ppp_dev_ioctl_stats(struct ifreq *ifr, struct net_device *dev)
{
- struct ppp_stats *res,
- t;
+ struct ppp_stats *res, t;
isdn_net_local *lp = (isdn_net_local *) dev->priv;
+ struct slcompress *slcomp;
int err;
res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
@@ -1828,8 +1738,8 @@ isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct net_device *dev)
t.p.ppp_obytes = lp->stats.tx_bytes;
t.p.ppp_oerrors = lp->stats.tx_errors;
#ifdef CONFIG_ISDN_PPP_VJ
- if (slot >= 0 && ippp_table[slot]->slcomp) {
- struct slcompress *slcomp = ippp_table[slot]->slcomp;
+ slcomp = lp->slcomp;
+ if (slcomp) {
t.vj.vjs_packets = slcomp->sls_o_compressed + slcomp->sls_o_uncompressed;
t.vj.vjs_compressed = slcomp->sls_o_compressed;
t.vj.vjs_searches = slcomp->sls_o_searches;
@@ -1858,17 +1768,18 @@ isdn_ppp_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -EINVAL;
switch (cmd) {
- case SIOCGPPPVER:
- r = (char *) ifr->ifr_ifru.ifru_data;
- len = strlen(PPP_VERSION) + 1;
- if(copy_to_user(r, PPP_VERSION, len)) error = -EFAULT;
- break;
- case SIOCGPPPSTATS:
- error = isdn_ppp_dev_ioctl_stats(0, ifr, dev);
- break;
- default:
- error = -EINVAL;
- break;
+ case SIOCGPPPVER:
+ r = (char *) ifr->ifr_ifru.ifru_data;
+ len = strlen(PPP_VERSION) + 1;
+ if (copy_to_user(r, PPP_VERSION, len))
+ error = -EFAULT;
+ break;
+ case SIOCGPPPSTATS:
+ error = isdn_ppp_dev_ioctl_stats(ifr, dev);
+ break;
+ default:
+ error = -EINVAL;
+ break;
}
return error;
}
@@ -1962,868 +1873,167 @@ isdn_ppp_hangup_slave(char *name)
/* 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)
+static void isdn_ppp_dev_kick_up(void *priv)
{
- isdn_ppp_fill_rq(NULL, 0, PPP_COMP, is->idev->ppp_slot);
+ isdn_net_dev *idev = priv;
+
+ ipppd_queue_read(idev->ipppd, PPP_COMPFRAG, NULL, 0);
}
-/* 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)
+static void isdn_ppp_lp_kick_up(void *priv)
{
- struct sk_buff *skb;
- unsigned char *p;
- int hl;
- int cnt = 0;
- isdn_net_dev *idev = is->idev;
-
- /* Alloc large enough skb */
- hl = isdn_slot_hdrlen(idev->isdn_slot);
- skb = alloc_skb(len + hl + 16,GFP_ATOMIC);
- if(!skb) {
- printk(KERN_WARNING
- "ippp: CCP cannot send reset - out of memory\n");
- return;
- }
- skb_reserve(skb, hl);
-
- /* 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;
- }
+ isdn_net_local *lp = priv;
+ isdn_net_dev *idev;
- /* 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);
+ if (list_empty(&lp->online)) {
+ isdn_BUG();
+ return;
}
-
- /* 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,idev->ppp_slot);
-
- isdn_net_write_super(idev, skb);
+ idev = list_entry(lp->online.next, isdn_net_dev, online);
+ ipppd_queue_read(idev->ipppd, PPP_COMP, NULL, 0);
}
-/* Allocate the reset state vector */
-static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is)
-{
- struct ippp_ccp_reset *r;
- r = kmalloc(sizeof(struct ippp_ccp_reset), GFP_KERNEL);
- if(!r) {
- printk(KERN_ERR "ippp_ccp: failed to allocate reset data"
- " structure - no mem\n");
- return NULL;
- }
- memset(r, 0, sizeof(struct ippp_ccp_reset));
- printk(KERN_DEBUG "ippp_ccp: allocated reset data structure %p\n", r);
- is->reset = r;
- return r;
-}
+/* Send a CCP Reset-Request or Reset-Ack directly from the kernel. */
-/* Destroy the reset state vector. Kill all pending timers first. */
-static void isdn_ppp_ccp_reset_free(struct ippp_struct *is)
+static struct sk_buff *
+__isdn_ppp_alloc_skb(isdn_net_dev *idev, int len, unsigned int gfp_mask)
{
- unsigned int id;
+ int hl = IPPP_MAX_HEADER + isdn_slot_hdrlen(idev->isdn_slot);
+ struct sk_buff *skb;
- printk(KERN_DEBUG "ippp_ccp: freeing reset data structure %p\n",
- is->reset);
- for(id = 0; id < 256; id++) {
- if(is->reset->rs[id]) {
- isdn_ppp_ccp_reset_free_state(is, (unsigned char)id);
- }
- }
- kfree(is->reset);
- is->reset = NULL;
-}
+ skb = alloc_skb(hl + len, gfp_mask);
+ if (!skb)
+ return NULL;
-/* 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);
- }
+ skb_reserve(skb, hl);
+ return skb;
}
-/* 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)
+static struct sk_buff *
+isdn_ppp_dev_alloc_skb(void *priv, int len, int gfp_mask)
{
- struct ippp_ccp_reset_state *rs =
- (struct ippp_ccp_reset_state *)closure;
+ isdn_net_dev *idev = priv;
- if(!rs) {
- printk(KERN_ERR "ippp_ccp: timer cb with zero closure.\n");
- return;
- }
- if(rs->ta && rs->state == CCPResetSentReq) {
- /* We are correct here */
- 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;
- }
- printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n",
- rs->id);
- /* 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);
- }
+ return __isdn_ppp_alloc_skb(idev, len, gfp_mask);
}
-/* 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)
+static struct sk_buff *
+isdn_ppp_lp_alloc_skb(void *priv, int len, int gfp_mask)
{
- struct ippp_ccp_reset_state *rs;
- if(is->reset->rs[id]) {
- printk(KERN_WARNING "ippp_ccp: old state exists for id %d\n",
- id);
+ isdn_net_local *lp = priv;
+ isdn_net_dev *idev;
+
+ if (list_empty(&lp->online)) {
+ isdn_BUG();
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;
+ idev = list_entry(lp->online.next, isdn_net_dev, online);
+ return __isdn_ppp_alloc_skb(idev, len, gfp_mask);
}
-
-/* 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)
+static void
+isdn_ppp_dev_push_header(void *priv, struct sk_buff *skb, u16 proto)
{
- 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;
- }
- }
-}
+ isdn_net_dev *idev = priv;
-/* 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++;
+ isdn_ppp_push_header(idev, skb, proto);
}
-/*
- * decompress packet
- *
- * if master = 0, we're trying to uncompress an per-link compressed packet,
- * as opposed to an compressed reconstructed-from-MPPP packet.
- * proto is updated to protocol field of uncompressed packet.
- *
- * retval: decompressed packet,
- * same packet if uncompressed,
- * NULL if decompression error
- */
-
-static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master,
- int *proto)
+static void
+isdn_ppp_lp_push_header(void *priv, struct sk_buff *skb, u16 proto)
{
- 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) {
- // per-link decompression
- stat = is->link_decomp_stat;
- ipc = is->link_decompressor;
- ri = is;
- } else {
- stat = master->decomp_stat;
- ipc = master->decompressor;
- ri = master;
- }
-
- if (!ipc) {
- // no decompressor -> we can't decompress.
- printk(KERN_DEBUG "ippp: no decompressor defined!\n");
- return skb;
- }
- if (!stat) // if we have a compressor, stat has been set as well
- BUG();
-
- if((master && *proto == PPP_COMP) || (!master && *proto == PPP_COMPFRAG) ) {
- // compressed packets are compressed by their protocol type
+ isdn_net_local *lp = priv;
+ isdn_net_dev *idev;
- // Set up reset params for the decompressor
- memset(&rsparm, 0, sizeof(rsparm));
- rsparm.data = rsdata;
- rsparm.maxdlen = IPPP_RESET_MAXDATABYTES;
-
- skb_out = dev_alloc_skb(is->mru + PPP_HDRLEN);
- len = ipc->decompress(stat, skb, skb_out, &rsparm);
- kfree_skb(skb);
- if (len <= 0) {
- switch(len) {
- case DECOMP_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;
- }
- kfree_skb(skb_out);
- return NULL;
- }
- *proto = isdn_ppp_strip_proto(skb_out);
- if (*proto < 0) {
- kfree_skb(skb_out);
- return NULL;
- }
- return skb_out;
- } else {
- // uncompressed packets are fed through the decompressor to
- // update the decompressor state
- ipc->incomp(stat, skb, *proto);
- return skb;
+ if (list_empty(&lp->online)) {
+ isdn_BUG();
+ return;
}
+ idev = list_entry(lp->online.next, isdn_net_dev, online);
+ isdn_ppp_push_header(idev, skb, proto);
}
-/*
- * compress a frame
- * type=0: normal/bundle compression
- * =1: link compression
- * returns original skb if we haven't compressed the frame
- * and a new skb pointer if we've done it
- */
-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_dev_xmit(void *priv, struct sk_buff *skb)
{
- int ret;
- int new_proto;
- struct isdn_ppp_compressor *compressor;
- void *stat;
- struct sk_buff *skb_out;
-
- /* we do not compress control protocols */
- if(*proto < 0 || *proto > 0x3fff) {
- return skb_in;
- }
-
- if(type) { /* type=1 => Link compression */
- return skb_in;
- }
- else {
- if(!master) {
- compressor = is->compressor;
- stat = is->comp_stat;
- }
- else {
- compressor = master->compressor;
- stat = master->comp_stat;
- }
- new_proto = PPP_COMP;
- }
-
- if(!compressor) {
- printk(KERN_ERR "isdn_ppp: No compressor set!\n");
- return skb_in;
- }
- if(!stat) {
- printk(KERN_ERR "isdn_ppp: Compressor not initialized?\n");
- return skb_in;
- }
+ isdn_net_dev *idev = priv;
- /* Allow for at least 150 % expansion (for now) */
- skb_out = alloc_skb(skb_in->len + skb_in->len/2 + 32 +
- skb_headroom(skb_in), GFP_ATOMIC);
- if(!skb_out)
- return skb_in;
- skb_reserve(skb_out, skb_headroom(skb_in));
-
- ret = (compressor->compress)(stat,skb_in,skb_out,*proto);
- if(!ret) {
- dev_kfree_skb(skb_out);
- return skb_in;
- }
-
- dev_kfree_skb(skb_in);
- *proto = new_proto;
- return skb_out;
+ isdn_net_write_super(idev, skb);
}
-/*
- * we received a CCP frame ..
- * not a clean solution, but we MUST handle a few cases in the kernel
- */
-static void isdn_ppp_receive_ccp(isdn_net_dev *idev, isdn_net_local *lp,
- struct sk_buff *skb,int proto)
+static void
+isdn_ppp_lp_xmit(void *priv, struct sk_buff *skb)
{
- struct ippp_struct *is;
- struct ippp_struct *mis;
- int len;
- struct isdn_ppp_resetparams rsparm;
- unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
+ isdn_net_local *lp = priv;
+ isdn_net_dev *idev;
- printk(KERN_DEBUG "Received CCP frame from peer slot(%d)\n",
- idev->ppp_slot);
- if (idev->ppp_slot < 0 || idev->ppp_slot > ISDN_MAX_CHANNELS) {
- printk(KERN_ERR "%s: ppp_slot(%d) out of range\n",
- __FUNCTION__ , idev->ppp_slot);
+ if (list_empty(&lp->online)) {
+ isdn_BUG();
return;
}
- is = ippp_table[idev->ppp_slot];
- isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,idev->ppp_slot);
-
- mis = is;
-
- switch(skb->data[0]) {
- case CCP_CONFREQ:
- if(is->debug & 0x10)
- printk(KERN_DEBUG "Disable compression here!\n");
- if(proto == PPP_CCP)
- mis->compflags &= ~SC_COMP_ON;
- else
- is->compflags &= ~SC_LINK_COMP_ON;
- break;
- 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) {
- if (!mis->decompressor)
- break;
- mis->compflags |= SC_DECOMP_ON;
- } else {
- if (!is->decompressor)
- break;
- 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;
- }
- 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;
- }
- 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;
- }
+ idev = list_entry(lp->online.next, isdn_net_dev, online);
+ isdn_net_write_super(idev, skb);
}
-
-/*
- * Daemon sends a CCP frame ...
- */
-
-/* TODO: Clean this up with new Reset semantics */
-
-/* I believe the CCP handling as-is is done wrong. Compressed frames
- * should only be sent/received after CCP reaches UP state, which means
- * both sides have sent CONF_ACK. Currently, we handle both directions
- * independently, which means we may accept compressed frames too early
- * (supposedly not a problem), but may also mean we send compressed frames
- * too early, which may turn out to be a problem.
- * This part of state machine should actually be handled by (i)pppd, but
- * that's too big of a change now. --kai
- */
-
-/* Actually, we might turn this into an advantage: deal with the RFC in
- * the old tradition of beeing generous on what we accept, but beeing
- * strict on what we send. Thus we should just
- * - accept compressed frames as soon as decompression is negotiated
- * - send compressed frames only when decomp *and* comp are negotiated
- * - drop rx compressed frames if we cannot decomp (instead of pushing them
- * up to ipppd)
- * and I tried to modify this file according to that. --abp
- */
-
-static void isdn_ppp_send_ccp(isdn_net_dev *idev, isdn_net_local *lp, struct sk_buff *skb)
+static int
+isdn_ppp_set_compressor(isdn_net_dev *idev, struct isdn_ppp_comp_data *data)
{
- struct ippp_struct *mis,*is;
- int proto, slot = idev->ppp_slot;
- unsigned char *data;
-
- if(!skb || skb->len < 3)
- return;
- if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
- printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
- __FUNCTION__ , slot);
- return;
- }
- is = ippp_table[slot];
- /* 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_CCPFRAG)
- return;
+ struct ippp_ccp *ccp;
- printk(KERN_DEBUG "Received CCP frame from daemon:\n");
- isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,idev->ppp_slot);
+ if (data->flags & IPPP_COMP_FLAG_LINK)
+ ccp = idev->ccp;
+ else
+ ccp = idev->mlp->ccp;
- mis = is;
- if (mis != is)
- printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n");
-
- switch(data[2]) {
- case CCP_CONFREQ:
- if(is->debug & 0x10)
- printk(KERN_DEBUG "Disable decompression here!\n");
- if(proto == PPP_CCP)
- is->compflags &= ~SC_DECOMP_ON;
- else
- is->compflags &= ~SC_LINK_DECOMP_ON;
- break;
- 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) {
- if (!is->compressor)
- break;
- is->compflags |= SC_COMP_ON;
- } else {
- if (!is->compressor)
- break;
- 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;
- }
+ return ippp_ccp_set_compressor(ccp, idev->ipppd->unit, data);
}
-int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc)
-{
- ipc->next = ipc_head;
- ipc->prev = NULL;
- if(ipc_head) {
- ipc_head->prev = ipc;
- }
- ipc_head = ipc;
- return 0;
-}
+// ISDN_NET_ENCAP_SYNCPPP
+// ======================================================================
-int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc)
+static int
+isdn_ppp_open(isdn_net_local *lp)
{
- if(ipc->prev)
- ipc->prev->next = ipc->next;
- else
- ipc_head = ipc->next;
- if(ipc->next)
- ipc->next->prev = ipc->prev;
- ipc->prev = ipc->next = NULL;
- return 0;
-}
+ lp->mpppcfg = 0; /* mppp configuration */
+ lp->mp_seqno = 0; /* MP sequence number */
-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);
-
- /* If is has no valid reset state vector, we cannot allocate a
- decompressor. The decompressor would cause reset transactions
- sooner or later, and they need that vector. */
-
- if(!(data->flags & IPPP_COMP_FLAG_XMIT) && !is->reset) {
- printk(KERN_ERR "ippp_ccp: no reset data structure - can't"
- " allow decompression.\n");
+#ifdef CONFIG_ISDN_PPP_VJ
+ lp->slcomp = slhc_init(16, 16);
+#endif
+ lp->ccp = ippp_ccp_alloc();
+ if (!lp->ccp)
return -ENOMEM;
- }
+ lp->ccp->proto = PPP_COMP;
+ lp->ccp->priv = lp;
+ lp->ccp->alloc_skb = isdn_ppp_lp_alloc_skb;
+ lp->ccp->push_header = isdn_ppp_lp_push_header;
+ lp->ccp->xmit = isdn_ppp_lp_xmit;
+ lp->ccp->kick_up = isdn_ppp_lp_kick_up;
- while(ipc) {
- if(ipc->num == num) {
- 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;
+ return 0;
}
-// ISDN_NET_ENCAP_SYNCPPP
-// ======================================================================
-
-static int
-isdn_ppp_header(struct sk_buff *skb, struct net_device *dev,
- unsigned short type, void *daddr, void *saddr,
- unsigned plen)
+static void
+isdn_ppp_close(isdn_net_local *lp)
{
- skb_push(skb, IPPP_MAX_HEADER);
-
- return IPPP_MAX_HEADER;
+#ifdef CONFIG_ISDN_PPP_VJ
+ slhc_free(lp->slcomp);
+ lp->slcomp = NULL;
+#endif
+ ippp_ccp_free(lp->ccp);
+ lp->ccp = NULL;
}
struct isdn_netif_ops isdn_ppp_ops = {
.hard_start_xmit = isdn_ppp_start_xmit,
- .hard_header = isdn_ppp_header,
.do_ioctl = isdn_ppp_dev_ioctl,
.flags = IFF_NOARP | IFF_POINTOPOINT,
.type = ARPHRD_PPP,
.receive = isdn_ppp_receive,
- .connected = isdn_ppp_wakeup_daemon,
+ .connected = isdn_ppp_connected,
+ .disconnected = isdn_ppp_disconnected,
.bind = isdn_ppp_bind,
- .unbind = isdn_ppp_free,
-
+ .unbind = isdn_ppp_unbind,
+ .open = isdn_ppp_open,
+ .close = isdn_ppp_close,
};
diff --git a/drivers/isdn/i4l/isdn_ppp.h b/drivers/isdn/i4l/isdn_ppp.h
index af6f50fd11db..20745b31ef1d 100644
--- a/drivers/isdn/i4l/isdn_ppp.h
+++ b/drivers/isdn/i4l/isdn_ppp.h
@@ -15,17 +15,15 @@
extern struct file_operations isdn_ppp_fops;
extern struct isdn_netif_ops isdn_ppp_ops;
-extern int isdn_ppp_init(void);
-extern void isdn_ppp_cleanup(void);
-extern int isdn_ppp_dial_slave(char *);
-extern int isdn_ppp_hangup_slave(char *);
+int isdn_ppp_init(void);
+void isdn_ppp_cleanup(void);
+int isdn_ppp_dial_slave(char *);
+int isdn_ppp_hangup_slave(char *);
-#define IPPP_OPEN 0x01
-#define IPPP_CONNECT 0x02
-#define IPPP_CLOSEWAIT 0x04
-#define IPPP_NOBLOCK 0x08
-#define IPPP_ASSIGNED 0x10
+void
+isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,
+ int unit, int slot);
+int
+isdn_ppp_strip_proto(struct sk_buff *skb);
#define IPPP_MAX_HEADER 10
-
-
diff --git a/drivers/isdn/i4l/isdn_ppp_ccp.c b/drivers/isdn/i4l/isdn_ppp_ccp.c
new file mode 100644
index 000000000000..ab573e7a6376
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_ppp_ccp.c
@@ -0,0 +1,601 @@
+
+#include "isdn_ppp_ccp.h"
+#include "isdn_common.h"
+#include "isdn_net.h"
+#include "isdn_ppp.h"
+#include <linux/ppp-comp.h>
+
+/* 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. */
+
+/* ====================================================================== */
+
+/* Free a given state and clear everything up for later reallocation */
+static void
+ippp_ccp_reset_free_state(struct ippp_ccp *ccp, unsigned char id)
+{
+ struct ippp_ccp_reset_state *rs = ccp->reset->rs[id];
+
+ if (!rs)
+ return;
+
+ if (rs->ta) // FIXME?
+ del_timer_sync(&rs->timer);
+
+ kfree(rs);
+ ccp->reset->rs[id] = NULL;
+}
+
+static void
+do_xmit_reset(struct ippp_ccp *ccp, unsigned char code, unsigned char id,
+ unsigned char *data, int len)
+{
+ struct sk_buff *skb;
+ unsigned char *p;
+ u16 proto = ccp->proto == PPP_COMP ? PPP_CCP : PPP_CCPFRAG;
+
+ skb = ccp->alloc_skb(ccp->priv, 4 + len, GFP_ATOMIC);
+ ccp->push_header(ccp->priv, skb, proto);
+
+ p = skb_put(skb, 4);
+ p += put_u8 (p, code);
+ p += put_u8 (p, id);
+ p += put_u16(p, len + 4);
+
+ if (len)
+ memcpy(skb_put(skb, len), data, len);
+
+ isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, -1, -1);
+
+ ccp->xmit(ccp->priv, skb);
+}
+
+/* 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 data)
+{
+ struct ippp_ccp_reset_state *rs = (struct ippp_ccp_reset_state *) data;
+
+ if (!rs->ta) {
+ isdn_BUG();
+ return;
+ }
+ if (rs->state != CCPResetSentReq) {
+ printk(KERN_WARNING "ippp_ccp: timer cb in wrong state %d\n",
+ rs->state);
+ rs->ta = 0;
+ return;
+ }
+ /* We are correct here */
+ 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;
+ ippp_ccp_reset_free_state(rs->ccp, rs->id);
+ return;
+ }
+ printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n",
+ rs->id);
+ /* Push it again */
+ do_xmit_reset(rs->ccp, CCP_RESETREQ, rs->id, rs->data, rs->dlen);
+
+ mod_timer(&rs->timer, jiffies + 5 * HZ);
+}
+
+/* Allocate a new reset transaction state */
+static struct ippp_ccp_reset_state *
+ippp_ccp_reset_alloc_state(struct ippp_ccp *ccp, unsigned char id)
+{
+ struct ippp_ccp_reset_state *rs;
+
+ 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->ccp = ccp;
+ rs->id = id;
+ init_timer(&rs->timer);
+ rs->timer.data = (unsigned long)rs;
+ rs->timer.function = isdn_ppp_ccp_timer_callback;
+
+ ccp->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
+ippp_ccp_reset_xmit(struct ippp_ccp *ccp,
+ struct isdn_ppp_resetparams *rp)
+{
+ struct ippp_ccp_reset_state *rs;
+ int id;
+
+ if (rp->valid) {
+ /* The decompressor defines parameters by itself */
+ if (!rp->rsend)
+ return;
+
+ /* And it wants us to send a request */
+ if (!rp->idval) {
+ isdn_BUG();
+ return;
+ }
+ id = rp->id;
+ } 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 */
+ id = ccp->reset->lastid++;
+ /* We always expect an Ack if the decompressor doesnt
+ know better */
+ rp->expra = 1;
+ rp->dtval = 0;
+ }
+ rs = ccp->reset->rs[id];
+ if (rs) {
+ printk(KERN_INFO "ippp_ccp: reset xmit in wrong state %d "
+ "for id %d (%d)\n", rs->state, id, rs->ta);
+ return;
+ }
+ /* Ok, this is a new transaction */
+ printk(KERN_DEBUG "ippp_ccp: new xmit for id %d\n", id);
+ rs = ippp_ccp_reset_alloc_state(ccp, id);
+ if(!rs) {
+ printk(KERN_INFO "ippp_ccp: out of mem allocing ccp trans\n");
+ return;
+ }
+ rs->expra = rp->expra;
+ rs->id = id;
+ if (rp->dtval) {
+ rs->dlen = rp->dlen;
+ memcpy(rs->data, rp->data, rp->dlen);
+ } else {
+ rs->dlen = 0;
+ }
+
+ rs->state = CCPResetSentReq;
+ do_xmit_reset(rs->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;
+}
+
+/* ====================================================================== */
+
+struct ippp_ccp *
+ippp_ccp_alloc(void)
+{
+ struct ippp_ccp *ccp;
+
+ ccp = kmalloc(sizeof(*ccp), GFP_ATOMIC); // FIXME
+ memset(ccp, 0, sizeof(*ccp));
+ ccp->mru = 1524; /* MRU, default 1524 */
+ ccp->reset = kmalloc(sizeof(*ccp->reset), GFP_ATOMIC); // FIXME alloc together?
+ if (!ccp->reset) {
+ kfree(ccp);
+ return NULL;
+ }
+ memset(ccp->reset, 0, sizeof(*ccp->reset));
+ return ccp;
+}
+
+void
+ippp_ccp_free(struct ippp_ccp *ccp)
+{
+ int id;
+
+ if (ccp->comp_stat)
+ ccp->compressor->free(ccp->comp_stat);
+ if (ccp->decomp_stat)
+ ccp->decompressor->free(ccp->decomp_stat);
+
+ for (id = 0; id < 256; id++) {
+ if (ccp->reset->rs[id])
+ ippp_ccp_reset_free_state(ccp, id);
+ }
+ kfree(ccp->reset);
+ kfree(ccp);
+}
+
+int
+ippp_ccp_set_mru(struct ippp_ccp *ccp, unsigned int mru)
+{
+ ccp->mru = mru;
+ return 0;
+}
+
+unsigned int
+ippp_ccp_get_flags(struct ippp_ccp *ccp)
+{
+ return ccp->compflags & (SC_DC_ERROR|SC_DC_FERROR);
+}
+
+/*
+ * compress a frame
+ * returns original skb if we did not compress the frame
+ * and a new skb otherwise
+ */
+struct sk_buff *
+ippp_ccp_compress(struct ippp_ccp *ccp, struct sk_buff *skb_in, int *proto)
+{
+ struct sk_buff *skb;
+
+ if (!(ccp->compflags & (SC_COMP_ON|SC_DECOMP_ON))) {
+ /* We send compressed only if both down- und upstream
+ compression is negotiated, that means, CCP is up */
+ return skb_in;
+ }
+ /* we do not compress control protocols */
+ if (*proto < 0 || *proto > 0x3fff) {
+ return skb_in;
+ }
+ if (!ccp->compressor || !ccp->comp_stat) {
+ isdn_BUG();
+ return skb_in;
+ }
+ /* Allow for at least 150 % expansion (for now) */
+ skb = alloc_skb(skb_in->len*2 + skb_headroom(skb_in), GFP_ATOMIC);
+ if (!skb)
+ return skb_in;
+
+ skb_reserve(skb, skb_headroom(skb_in));
+ if (!ccp->compressor->compress(ccp->comp_stat, skb_in, skb, *proto)) {
+ dev_kfree_skb(skb);
+ return skb_in;
+ }
+ isdn_ppp_frame_log("comp in:", skb_in->data, skb_in->len, 20, -1, -1);
+ isdn_ppp_frame_log("comp out:", skb->data, skb->len, 20, -1, -1);
+ dev_kfree_skb(skb_in);
+ *proto = ccp->proto;
+ return skb;
+}
+
+/*
+ * decompress packet
+ *
+ * proto is updated to protocol field of uncompressed packet.
+ * retval: decompressed packet,
+ * same packet if uncompressed,
+ * NULL if decompression error
+ */
+
+struct sk_buff *
+ippp_ccp_decompress(struct ippp_ccp *ccp, struct sk_buff *skb_in, int *proto)
+{
+ struct sk_buff *skb;
+ struct isdn_ppp_resetparams rsparm;
+ unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
+ int len;
+
+ if (!(ccp->compflags & SC_DECOMP_ON)) {
+ return skb_in;
+ }
+ if (!ccp->decompressor || !ccp->decomp_stat) {
+ isdn_BUG();
+ return skb_in;
+ }
+ if (*proto != ccp->proto) {
+ /* uncompressed packets are fed through the decompressor to
+ * update the decompressor state */
+ ccp->decompressor->incomp(ccp->decomp_stat, skb_in, *proto);
+ return skb_in;
+ }
+ skb = dev_alloc_skb(ccp->mru + PPP_HDRLEN); // FIXME oom?
+
+ // Set up reset params for the decompressor
+ memset(&rsparm, 0, sizeof(rsparm));
+ rsparm.data = rsdata;
+ rsparm.maxdlen = IPPP_RESET_MAXDATABYTES;
+
+ len = ccp->decompressor->decompress(ccp->decomp_stat, skb_in, skb,
+ &rsparm);
+ isdn_ppp_frame_log("deco in:", skb_in->data, skb_in->len, 20, -1, -1);
+ isdn_ppp_frame_log("deco out:", skb->data, skb->len, 20, -1, -1);
+ kfree_skb(skb_in);
+
+ if (len <= 0) {
+ switch(len) {
+ case DECOMP_ERROR:
+ printk(KERN_INFO "ippp: decomp wants reset with%s params\n",
+ rsparm.valid ? "" : "out");
+
+ ippp_ccp_reset_xmit(ccp, &rsparm);
+ break;
+ case DECOMP_FATALERROR:
+ ccp->compflags |= SC_DC_FERROR;
+ /* Kick ipppd to recognize the error */
+ ccp->kick_up(ccp->priv);
+ break;
+ }
+ kfree_skb(skb);
+ return NULL;
+ }
+ *proto = isdn_ppp_strip_proto(skb);
+ if (*proto < 0) {
+ kfree_skb(skb);
+ return NULL;
+ }
+ return skb;
+}
+
+/* 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_ccp *ccp, unsigned char id)
+{
+ struct ippp_ccp_reset_state *rs = ccp->reset->rs[id];
+
+ if (!rs) {
+ printk(KERN_INFO "ippp_ccp: ResetAck received for unknown id"
+ " %d\n", id);
+ return;
+ }
+
+ 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);
+ }
+ ippp_ccp_reset_free_state(ccp, id);
+}
+
+void
+ippp_ccp_receive_ccp(struct ippp_ccp *ccp, struct sk_buff *skb)
+{
+ int len;
+ struct isdn_ppp_resetparams rsparm;
+ unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
+
+ isdn_ppp_frame_log("ccp-recv", skb->data, skb->len, 32, -1, -1);
+
+ switch(skb->data[0]) {
+ case CCP_CONFREQ:
+ if (ccp->debug & 0x10)
+ printk(KERN_DEBUG "Disable compression here!\n");
+
+ ccp->compflags &= ~SC_COMP_ON;
+ break;
+ case CCP_TERMREQ:
+ case CCP_TERMACK:
+ if (ccp->debug & 0x10)
+ printk(KERN_DEBUG "Disable (de)compression here!\n");
+
+ ccp->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);
+ break;
+ case CCP_CONFACK:
+ /* if we RECEIVE an ackowledge we enable the decompressor */
+ if (ccp->debug & 0x10)
+ printk(KERN_DEBUG "Enable decompression here!\n");
+
+ if (!ccp->decomp_stat)
+ break;
+ ccp->compflags |= SC_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 a reset Ack was outstanding for this id, then
+ clean up the state engine */
+ isdn_ppp_ccp_reset_ack_rcvd(ccp, skb->data[1]);
+ if (ccp->decomp_stat)
+ ccp->decompressor->reset(ccp->decomp_stat,
+ skb->data[0], skb->data[1],
+ len ? &skb->data[4] : NULL,
+ len, NULL);
+ /* TODO: This is not easy to decide here */
+ ccp->compflags &= ~SC_DECOMP_DISCARD;
+ 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 (ccp->comp_stat)
+ ccp->compressor->reset(ccp->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) {
+ printk(KERN_DEBUG "ResetAck suppressed\n");
+ return;
+ }
+ /* We should send a Frame */
+ do_xmit_reset(ccp, CCP_RESETACK,
+ rsparm.idval ? rsparm.id : skb->data[1],
+ rsparm.data,
+ rsparm.dtval ? rsparm.dlen : 0);
+ return;
+ }
+ /* We answer with a straight reflected Ack */
+ do_xmit_reset(ccp, CCP_RESETACK, skb->data[1],
+ skb->data + 4, len);
+ }
+}
+
+void
+ippp_ccp_send_ccp(struct ippp_ccp *ccp, struct sk_buff *skb)
+{
+ isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, -1, -1);
+
+ switch (skb->data[2]) {
+ case CCP_CONFREQ:
+ if (ccp->debug & 0x10)
+ printk(KERN_DEBUG "Disable decompression here!\n");
+
+ ccp->compflags &= ~SC_DECOMP_ON;
+ break;
+ case CCP_TERMREQ:
+ case CCP_TERMACK:
+ if (ccp->debug & 0x10)
+ printk(KERN_DEBUG "Disable (de)compression here!\n");
+
+ ccp->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);
+ break;
+ case CCP_CONFACK:
+ /* if we SEND an ackowledge we can/must enable the compressor */
+ if (ccp->debug & 0x10)
+ printk(KERN_DEBUG "Enable compression here!\n");
+
+ if (!ccp->compressor)
+ break;
+
+ ccp->compflags |= SC_COMP_ON;
+ break;
+ case CCP_RESETACK:
+ /* If we send a ACK we should reset our compressor */
+ if (ccp->debug & 0x10)
+ printk(KERN_DEBUG "Reset decompression state here!\n");
+
+ printk(KERN_DEBUG "ResetAck from daemon passed by\n");
+
+ if (!ccp->comp_stat)
+ break;
+
+ ccp->compressor->reset(ccp->comp_stat, 0, 0, NULL, 0, NULL);
+ ccp->compflags &= ~SC_COMP_DISCARD;
+ break;
+ case CCP_RESETREQ:
+ /* Just let it pass by */
+ printk(KERN_DEBUG "ResetReq from daemon passed by\n");
+ break;
+ }
+}
+
+static struct isdn_ppp_compressor *ipc_head = NULL;
+
+int
+ippp_ccp_set_compressor(struct ippp_ccp *ccp, int unit,
+ struct isdn_ppp_comp_data *data)
+{
+ struct isdn_ppp_compressor *ipc = ipc_head;
+ int ret;
+ void *stat;
+ int num = data->num;
+
+ if (ccp->debug & 0x10)
+ printk(KERN_DEBUG "[%d] Set %scompressor type %d\n", unit,
+ data->flags & IPPP_COMP_FLAG_XMIT ? "" : "de", num);
+
+ for (ipc = ipc_head; ipc; ipc = ipc->next) {
+ if (ipc->num != num)
+ continue;
+
+ stat = ipc->alloc(data);
+ if (!stat) {
+ printk(KERN_ERR "Can't alloc (de)compression!\n");
+ break;
+ }
+ ret = ipc->init(stat, data, unit, 0);
+ if(!ret) {
+ printk(KERN_ERR "Can't init (de)compression!\n");
+ ipc->free(stat);
+ break;
+ }
+ if (data->flags & IPPP_COMP_FLAG_XMIT) {
+ if (ccp->comp_stat)
+ ccp->compressor->free(ccp->comp_stat);
+ ccp->comp_stat = stat;
+ ccp->compressor = ipc;
+ } else {
+ if (ccp->decomp_stat)
+ ccp->decompressor->free(ccp->decomp_stat);
+ ccp->decomp_stat = stat;
+ ccp->decompressor = ipc;
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+
+void
+ippp_ccp_get_compressors(unsigned long protos[8])
+{
+ struct isdn_ppp_compressor *ipc;
+ int i, j;
+
+ memset(protos, 0, sizeof(unsigned long) * 8);
+ for (ipc = ipc_head; ipc; ipc = ipc->next) {
+ j = ipc->num / (sizeof(long)*8);
+ i = ipc->num % (sizeof(long)*8);
+ if (j < 8)
+ protos[j] |= 1 << i;
+ }
+}
+
+int
+isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc)
+{
+ ipc->next = ipc_head;
+ ipc->prev = NULL;
+ if (ipc_head) {
+ ipc_head->prev = ipc;
+ }
+ ipc_head = ipc;
+ return 0;
+}
+
+int
+isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc)
+{
+ if (ipc->prev)
+ ipc->prev->next = ipc->next;
+ else
+ ipc_head = ipc->next;
+ if (ipc->next)
+ ipc->next->prev = ipc->prev;
+ ipc->prev = ipc->next = NULL;
+ return 0;
+}
+
diff --git a/drivers/isdn/i4l/isdn_ppp_ccp.h b/drivers/isdn/i4l/isdn_ppp_ccp.h
new file mode 100644
index 000000000000..d9ece852d24f
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_ppp_ccp.h
@@ -0,0 +1,66 @@
+
+#include <linux/kernel.h>
+#include <linux/isdn_ppp.h>
+
+/* for ippp_ccp::flags */
+
+#define SC_DECOMP_ON 0x01
+#define SC_COMP_ON 0x02
+#define SC_DECOMP_DISCARD 0x04
+#define SC_COMP_DISCARD 0x08
+
+/* SC_DC_ERROR/FERROR go in here as well, but are defined elsewhere
+
+ #define SC_DC_FERROR 0x00800000
+ #define SC_DC_ERROR 0x00400000
+*/
+
+struct ippp_ccp {
+ int proto;
+ struct isdn_ppp_compressor *compressor;
+ struct isdn_ppp_compressor *decompressor;
+ void *comp_stat;
+ void *decomp_stat;
+ unsigned long compflags;
+ struct ippp_ccp_reset *reset;
+ int mru;
+ int debug;
+ void *priv;
+ void (*xmit)(void *priv, struct sk_buff *skb);
+ void (*kick_up)(void *priv);
+ void (*push_header)(void *priv, struct sk_buff *skb, u16);
+ struct sk_buff *(*alloc_skb)(void *priv, int len, int gfp_mask);
+};
+
+struct ippp_ccp *
+ippp_ccp_alloc(void);
+
+void
+ippp_ccp_free(struct ippp_ccp *ccp);
+
+int
+ippp_ccp_set_mru(struct ippp_ccp *ccp, unsigned int mru);
+
+unsigned int
+ippp_ccp_get_flags(struct ippp_ccp *ccp);
+
+struct sk_buff *
+ippp_ccp_compress(struct ippp_ccp *ccp, struct sk_buff *skb, int *proto);
+
+struct sk_buff *
+ippp_ccp_decompress(struct ippp_ccp *ccp, struct sk_buff *skb, int *proto);
+
+void
+ippp_ccp_send_ccp(struct ippp_ccp *ccp, struct sk_buff *skb);
+
+void
+ippp_ccp_receive_ccp(struct ippp_ccp *ccp, struct sk_buff *skb);
+
+void
+ippp_ccp_get_compressors(unsigned long protos[8]);
+
+int
+ippp_ccp_set_compressor(struct ippp_ccp *ccp, int unit,
+ struct isdn_ppp_comp_data *data);
+
+
diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c
index e757d5063398..612c7b19c85a 100644
--- a/drivers/isdn/isdnloop/isdnloop.c
+++ b/drivers/isdn/isdnloop/isdnloop.c
@@ -11,11 +11,12 @@
#include <linux/config.h>
#include <linux/module.h>
+#include <linux/interrupt.h>
#include <linux/init.h>
#include "isdnloop.h"
static char *revision = "$Revision: 1.11.6.7 $";
-static char *isdnloop_id;
+static char *isdnloop_id = "loop0";
MODULE_DESCRIPTION("ISDN4Linux: Pseudo Driver that simulates an ISDN card");
MODULE_AUTHOR("Fritz Elfert");
@@ -76,7 +77,7 @@ isdnloop_bchan_send(isdnloop_card * card, int ch)
};
cmd.command = ISDN_STAT_BSENT;
cmd.parm.length = len;
- if ( ack ) card->interface.statcallb(&cmd);
+ card->interface.statcallb(&cmd);
} else
card->sndcount[ch] = 0;
}
@@ -421,8 +422,9 @@ isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card * card)
return 0;
save_flags(flags);
cli();
- nskb = skb_clone(skb, GFP_ATOMIC);
+ nskb = dev_alloc_skb(skb->len);
if (nskb) {
+ memcpy(skb_put(nskb, len), skb->data, len);
skb_queue_tail(&card->bqueue[channel], nskb);
dev_kfree_skb(skb);
} else
diff --git a/include/linux/isdn.h b/include/linux/isdn.h
index 8203314e2cb7..0d0ce25b333e 100644
--- a/include/linux/isdn.h
+++ b/include/linux/isdn.h
@@ -338,16 +338,35 @@ typedef struct isdn_net_local_s {
/* phone[0] = Incoming Numbers */
/* phone[1] = Outgoing Numbers */
- struct list_head slaves; /* list of all bundled channels */
- struct list_head online; /* list of all bundled channels,
- which are currently online */
- spinlock_t online_lock; /* lock to protect online list */
+ struct list_head slaves; /* list of all bundled channels
+ protected by serializing config
+ ioctls / no change allowed when
+ interface is running */
+ struct list_head online; /* list of all bundled channels
+ which can be used for actual
+ data (IP) transfer
+ protected by xmit_lock */
+
+ spinlock_t xmit_lock; /* used to protect the xmit path of
+ a net_device, including all
+ associated channels's frame_cnt */
struct list_head running_devs; /* member of global running_devs */
atomic_t refcnt; /* references held by ISDN code */
#ifdef CONFIG_ISDN_X25
struct concap_device_ops *dops; /* callbacks used by encapsulator */
#endif
+#ifdef CONFIG_ISDN_PPP
+ unsigned int mpppcfg;
+ long mp_seqno;
+ struct ippp_ccp *ccp;
+ unsigned long debug;
+#ifdef CONFIG_ISDN_PPP_VJ
+ unsigned char *cbuf;
+ struct slcompress *slcomp;
+#endif
+#endif
+
/* use an own struct for that in later versions */
ulong cisco_myseq; /* Local keepalive seq. for Cisco */
ulong cisco_mineseen; /* returned keepalive seq. from remote */
@@ -391,14 +410,11 @@ typedef struct isdn_net_dev_s {
int chargeint; /* Interval between charge-infos */
int pppbind; /* ippp device for bindings */
- int ppp_slot; /* PPPD device slot number */
+ struct ipppd *ipppd; /* /dev/ipppX which controls us */
- spinlock_t xmit_lock; /* used to protect the xmit path of */
- /* a particular channel (including */
- /* the frame_cnt */
struct sk_buff_head super_tx_queue; /* List of supervisory frames to */
/* be transmitted asap */
- atomic_t frame_cnt; /* number of frames currently */
+ int frame_cnt; /* number of frames currently */
/* queued in HL driver */
struct tasklet_struct tlet;
@@ -410,6 +426,11 @@ typedef struct isdn_net_dev_s {
char name[10]; /* Name of device */
struct list_head global_list; /* global list of all isdn_net_devs */
#ifdef CONFIG_ISDN_PPP
+ unsigned int pppcfg;
+ unsigned int pppseq; /* last seq no seen */
+ struct ippp_ccp *ccp;
+ unsigned long debug;
+
ippp_bundle * pb; /* pointer to the common bundle structure
* with the per-bundle data */
#endif
diff --git a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h
index 68353a39b98c..ec6016c37421 100644
--- a/include/linux/isdn_ppp.h
+++ b/include/linux/isdn_ppp.h
@@ -8,7 +8,6 @@
#ifndef _LINUX_ISDN_PPP_H
#define _LINUX_ISDN_PPP_H
-
#define CALLTYPE_INCOMING 0x1
#define CALLTYPE_OUTGOING 0x2
#define CALLTYPE_CALLBACK 0x4
@@ -39,15 +38,6 @@ struct pppcallinfo
#define SC_OUT_SHORT_SEQ 0x00000800
#define SC_IN_SHORT_SEQ 0x00004000
-#define SC_DECOMP_ON 0x01
-#define SC_COMP_ON 0x02
-#define SC_DECOMP_DISCARD 0x04
-#define SC_COMP_DISCARD 0x08
-#define SC_LINK_DECOMP_ON 0x10
-#define SC_LINK_COMP_ON 0x20
-#define SC_LINK_DECOMP_DISCARD 0x40
-#define SC_LINK_COMP_DISCARD 0x80
-
#define ISDN_PPP_COMP_MAX_OPTIONS 16
#define IPPP_COMP_FLAG_XMIT 0x1
@@ -64,7 +54,8 @@ struct isdn_ppp_comp_data {
#include <linux/config.h>
-
+#include <linux/skbuff.h>
+#include <linux/ppp_defs.h>
#define DECOMP_ERR_NOMEM (-10)
@@ -172,8 +163,8 @@ enum ippp_ccp_reset_states {
struct ippp_ccp_reset_state {
enum ippp_ccp_reset_states state; /* State of this transaction */
- struct ippp_struct *is; /* Backlink to device stuff */
- unsigned char id; /* Backlink id index */
+ struct ippp_ccp *ccp; /* Backlink */
+ unsigned char id; /* id index */
unsigned char ta:1; /* The timer is active (flag) */
unsigned char expra:1; /* We expect a ResetAck at all */
int dlen; /* Databytes stored in data */
@@ -191,34 +182,5 @@ struct ippp_ccp_reset {
unsigned char lastid; /* Last id allocated by the engine */
};
-struct ippp_struct {
- struct ippp_struct *next_link;
- int state;
- struct sk_buff_head rq;
- wait_queue_head_t wq;
- struct task_struct *tk;
- unsigned int mpppcfg;
- unsigned int pppcfg;
- unsigned int mru;
- unsigned int mpmru;
- unsigned int mpmtu;
- unsigned int maxcid;
- struct isdn_net_dev_s *idev;
- int unit;
- int minor;
- unsigned int last_link_seqno;
- long mp_seqno;
-#ifdef CONFIG_ISDN_PPP_VJ
- unsigned char *cbuf;
- struct slcompress *slcomp;
-#endif
- unsigned long debug;
- struct isdn_ppp_compressor *compressor,*decompressor;
- struct isdn_ppp_compressor *link_compressor,*link_decompressor;
- void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat;
- struct ippp_ccp_reset *reset; /* Allocated on demand, may never be needed */
- unsigned long compflags;
-};
-
#endif /* __KERNEL__ */
#endif /* _LINUX_ISDN_PPP_H */