diff options
| author | Kai Germaschewski <kai@tp1.ruhr-uni-bochum.de> | 2002-10-16 01:48:30 -0500 |
|---|---|---|
| committer | Kai Germaschewski <kai@tp1.ruhr-uni-bochum.de> | 2002-10-16 01:48:30 -0500 |
| commit | bf5939d1679010f00637864d93abc96caafcd264 (patch) | |
| tree | 629718638bfe53566e2f38f1aca7b9bf7d6d6942 | |
| parent | 9beb225e0f1616a6cea6b0086a9ff94a09965c6c (diff) | |
| parent | cb35e72a1b0cb37f26579f0531aa4643f2cff40f (diff) | |
Merge tp1.ruhr-uni-bochum.de:/home/kai/src/kernel/v2.5/linux-2.5
into tp1.ruhr-uni-bochum.de:/home/kai/src/kernel/v2.5/linux-2.5.isdn
| -rw-r--r-- | drivers/isdn/i4l/Makefile | 2 | ||||
| -rw-r--r-- | drivers/isdn/i4l/isdn_bsdcomp.c | 19 | ||||
| -rw-r--r-- | drivers/isdn/i4l/isdn_ciscohdlck.c | 13 | ||||
| -rw-r--r-- | drivers/isdn/i4l/isdn_common.h | 2 | ||||
| -rw-r--r-- | drivers/isdn/i4l/isdn_net.c | 412 | ||||
| -rw-r--r-- | drivers/isdn/i4l/isdn_net.h | 153 | ||||
| -rw-r--r-- | drivers/isdn/i4l/isdn_net_lib.c | 524 | ||||
| -rw-r--r-- | drivers/isdn/i4l/isdn_ppp.c | 2540 | ||||
| -rw-r--r-- | drivers/isdn/i4l/isdn_ppp.h | 20 | ||||
| -rw-r--r-- | drivers/isdn/i4l/isdn_ppp_ccp.c | 601 | ||||
| -rw-r--r-- | drivers/isdn/i4l/isdn_ppp_ccp.h | 66 | ||||
| -rw-r--r-- | drivers/isdn/isdnloop/isdnloop.c | 8 | ||||
| -rw-r--r-- | include/linux/isdn.h | 39 | ||||
| -rw-r--r-- | include/linux/isdn_ppp.h | 46 |
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, ðer_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 */ |
