1996-01-09 04:13:18 +00:00
|
|
|
/* $Id$
|
|
|
|
*
|
|
|
|
* Linux ISDN subsystem, functions for synchronous PPP (linklevel).
|
|
|
|
*
|
1996-01-22 05:08:06 +00:00
|
|
|
* Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
|
1996-01-09 04:13:18 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*
|
|
|
|
* $Log$
|
1996-10-22 23:14:19 +00:00
|
|
|
* Revision 1.17 1996/10/22 09:39:49 hipp
|
|
|
|
* a few MP changes and bugfixes
|
|
|
|
*
|
|
|
|
* Revision 1.16 1996/09/23 01:58:10 fritz
|
|
|
|
* Fix: With syncPPP encapsulation, discard LCP packets
|
|
|
|
* when calculating hangup timeout.
|
|
|
|
*
|
|
|
|
* Revision 1.15 1996/09/07 12:50:12 hipp
|
|
|
|
* bugfixes (unknown device after failed dial attempt, minor bugs)
|
|
|
|
*
|
1996-09-07 12:50:12 +00:00
|
|
|
* Revision 1.14 1996/08/12 16:26:47 hipp
|
|
|
|
* code cleanup
|
|
|
|
* changed connection management from minors to slots
|
|
|
|
*
|
1996-08-12 16:26:47 +00:00
|
|
|
* Revision 1.13 1996/07/01 19:47:24 hipp
|
|
|
|
* Fixed memory leak in VJ handling and more VJ changes
|
|
|
|
*
|
1996-07-01 19:47:24 +00:00
|
|
|
* Revision 1.12 1996/06/24 17:42:03 fritz
|
|
|
|
* Minor bugfixes.
|
|
|
|
*
|
1996-06-24 17:42:03 +00:00
|
|
|
* Revision 1.11 1996/06/16 17:46:05 tsbogend
|
|
|
|
* changed unsigned long to u32 to make Alpha people happy
|
|
|
|
*
|
1996-06-16 17:46:05 +00:00
|
|
|
* Revision 1.10 1996/06/11 14:50:29 hipp
|
|
|
|
* Lot of changes and bugfixes.
|
|
|
|
* New scheme to resend packets to busy LL devices.
|
|
|
|
*
|
1996-06-11 14:50:29 +00:00
|
|
|
* Revision 1.9 1996/05/18 01:37:01 fritz
|
|
|
|
* Added spelling corrections and some minor changes
|
|
|
|
* to stay in sync with kernel.
|
|
|
|
*
|
1996-05-18 01:37:19 +00:00
|
|
|
* Revision 1.8 1996/05/06 11:34:55 hipp
|
|
|
|
* fixed a few bugs
|
|
|
|
*
|
1996-05-06 11:34:57 +00:00
|
|
|
* Revision 1.7 1996/04/30 11:07:42 fritz
|
|
|
|
* Added Michael's ippp-bind patch.
|
|
|
|
*
|
1996-04-30 11:10:42 +00:00
|
|
|
* Revision 1.6 1996/04/30 09:33:09 fritz
|
|
|
|
* Removed compatibility-macros.
|
|
|
|
*
|
1996-04-30 09:34:35 +00:00
|
|
|
* Revision 1.5 1996/04/20 16:32:32 fritz
|
|
|
|
* Changed ippp_table to an array of pointers, allocating each part
|
|
|
|
* separately.
|
|
|
|
*
|
1996-04-20 16:32:32 +00:00
|
|
|
* Revision 1.4 1996/02/19 15:25:50 fritz
|
|
|
|
* Bugfix: Sync-PPP packets got compressed twice, when resent due to
|
|
|
|
* send-queue-full reject.
|
|
|
|
*
|
1996-02-19 15:25:50 +00:00
|
|
|
* Revision 1.3 1996/02/11 02:27:12 fritz
|
|
|
|
* Lot of Bugfixes my Michael.
|
|
|
|
* Moved calls to skb_push() into isdn_net_header()
|
|
|
|
* Fixed a possible race-condition in isdn_ppp_timer_timeout().
|
|
|
|
*
|
1996-02-11 02:27:12 +00:00
|
|
|
* Revision 1.2 1996/01/22 05:08:06 fritz
|
|
|
|
* Merged in Michael's patches for MP.
|
|
|
|
* Minor changes in isdn_ppp_xmit.
|
|
|
|
*
|
1996-01-22 05:08:06 +00:00
|
|
|
* Revision 1.1 1996/01/09 04:11:29 fritz
|
|
|
|
* Initial revision
|
|
|
|
*
|
1996-01-09 04:13:18 +00:00
|
|
|
*/
|
|
|
|
|
1996-02-11 02:27:12 +00:00
|
|
|
/* TODO: right tbusy handling when using MP */
|
|
|
|
|
1996-10-22 09:39:49 +00:00
|
|
|
#undef ISDN_SYNCPPP_READDRESS
|
|
|
|
|
1996-01-22 05:08:06 +00:00
|
|
|
#include <linux/config.h>
|
|
|
|
#define __NO_VERSION__
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/isdn.h>
|
1996-01-09 04:13:18 +00:00
|
|
|
#include "isdn_common.h"
|
|
|
|
#include "isdn_ppp.h"
|
|
|
|
#include "isdn_net.h"
|
|
|
|
|
1996-01-22 05:08:06 +00:00
|
|
|
#ifndef PPP_IPX
|
|
|
|
#define PPP_IPX 0x002b
|
|
|
|
#endif
|
1996-10-22 09:39:49 +00:00
|
|
|
|
|
|
|
/* set this if you use dynamic addressing */
|
1996-01-22 05:08:06 +00:00
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
/* Prototypes */
|
1996-08-12 16:26:47 +00:00
|
|
|
static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot);
|
|
|
|
static int isdn_ppp_closewait(int slot);
|
1996-01-09 04:13:18 +00:00
|
|
|
static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
|
1996-01-22 05:08:06 +00:00
|
|
|
struct sk_buff *skb, int proto);
|
1996-08-12 16:26:47 +00:00
|
|
|
static int isdn_ppp_if_get_unit(char *namebuf);
|
1996-02-11 02:27:12 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_ISDN_MPP
|
1996-08-12 16:26:47 +00:00
|
|
|
static int isdn_ppp_bundle(struct ippp_struct *, int unit);
|
1996-02-11 02:27:12 +00:00
|
|
|
static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask);
|
1996-10-22 09:39:49 +00:00
|
|
|
static void isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min);
|
|
|
|
static void isdn_ppp_cleanup_sqqueue(isdn_net_dev * dev,isdn_net_local *, long min);
|
1996-02-11 02:27:12 +00:00
|
|
|
static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb,
|
1996-10-22 09:39:49 +00:00
|
|
|
int BEbyte, long *sqno, int min_sqno);
|
1996-02-11 02:27:12 +00:00
|
|
|
#endif
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-02-11 02:27:12 +00:00
|
|
|
char *isdn_ppp_revision = "$Revision$";
|
1996-09-07 12:50:12 +00:00
|
|
|
|
1996-04-20 16:32:32 +00:00
|
|
|
struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-01-22 05:08:06 +00:00
|
|
|
extern int isdn_net_force_dial_lp(isdn_net_local *);
|
|
|
|
|
1996-10-22 09:39:49 +00:00
|
|
|
/*
|
|
|
|
* frame log (debug)
|
|
|
|
*/
|
|
|
|
static void isdn_ppp_frame_log(char *info,char *data,int len,int maxlen)
|
|
|
|
{
|
|
|
|
int cnt,j,i;
|
|
|
|
char buf[80];
|
|
|
|
|
|
|
|
if(len < maxlen)
|
|
|
|
maxlen = len;
|
|
|
|
|
|
|
|
for(i=0,cnt=0;cnt<maxlen;i++) {
|
|
|
|
for(j=0;j<16 && cnt<maxlen;j++,cnt++)
|
|
|
|
sprintf(buf+j*3,"%02x ",(unsigned char) data[cnt]);
|
|
|
|
printk(KERN_DEBUG "%s[%d]: %s\n",info,i,buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1996-05-06 11:34:57 +00:00
|
|
|
/*
|
|
|
|
* unbind isdn_net_local <=> ippp-device
|
|
|
|
* note: it can happen, that we hangup/free the master before the slaves
|
|
|
|
*/
|
|
|
|
int isdn_ppp_free(isdn_net_local *lp)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
1996-05-06 11:34:57 +00:00
|
|
|
isdn_net_local *master_lp=lp;
|
1996-06-24 17:42:03 +00:00
|
|
|
unsigned long flags;
|
1996-08-12 16:26:47 +00:00
|
|
|
struct ippp_struct *is;
|
1996-05-06 11:34:57 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS)
|
1996-01-09 04:13:18 +00:00
|
|
|
return 0;
|
1996-01-22 05:08:06 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
is = ippp_table[lp->ppp_slot];
|
|
|
|
|
1996-06-24 17:42:03 +00:00
|
|
|
save_flags(flags);
|
|
|
|
cli();
|
1996-02-11 02:27:12 +00:00
|
|
|
#ifdef CONFIG_ISDN_MPP
|
1996-01-22 05:08:06 +00:00
|
|
|
if(lp->master)
|
1996-05-06 11:34:57 +00:00
|
|
|
master_lp = (isdn_net_local *) lp->master->priv;
|
|
|
|
|
|
|
|
lp->last->next = lp->next;
|
|
|
|
lp->next->last = lp->last;
|
|
|
|
if(master_lp->netdev->queue == lp) {
|
|
|
|
master_lp->netdev->queue = lp->next;
|
|
|
|
if(lp->next == lp) { /* last link in queue? */
|
1996-10-22 09:39:49 +00:00
|
|
|
master_lp->netdev->ib.bundled = 0;
|
1996-05-06 11:34:57 +00:00
|
|
|
isdn_ppp_free_mpqueue(master_lp->netdev);
|
|
|
|
isdn_ppp_free_sqqueue(master_lp->netdev);
|
1996-01-22 05:08:06 +00:00
|
|
|
}
|
|
|
|
}
|
1996-05-06 11:34:57 +00:00
|
|
|
lp->next = lp->last = lp; /* (re)set own pointers */
|
1996-01-22 05:08:06 +00:00
|
|
|
#endif
|
|
|
|
|
1996-09-07 12:50:12 +00:00
|
|
|
if( (is->state & IPPP_CONNECT) )
|
|
|
|
isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */
|
|
|
|
else if(is->state & IPPP_ASSIGNED)
|
1996-10-22 23:14:19 +00:00
|
|
|
is->state = IPPP_OPEN; /* fallback to 'OPEN but not ASSIGEND' staet */
|
1996-09-07 12:50:12 +00:00
|
|
|
|
1996-05-06 11:34:57 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if(is->debug & 0x1)
|
|
|
|
printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp,(long) is->lp);
|
1996-05-06 11:34:57 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
is->lp = NULL; /* link is down .. set lp to NULL */
|
1996-10-22 09:39:49 +00:00
|
|
|
#ifdef ISDN_SYNCPPP_READDRESS
|
|
|
|
is->old_pa_addr = 0x0;
|
|
|
|
is->old_pa_dstaddr = 0x0;
|
|
|
|
#endif
|
1996-08-12 16:26:47 +00:00
|
|
|
lp->ppp_slot = -1; /* is this OK ?? */
|
1996-06-24 17:42:03 +00:00
|
|
|
restore_flags(flags);
|
1996-07-01 19:47:24 +00:00
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1996-05-06 11:34:57 +00:00
|
|
|
/*
|
|
|
|
* bind isdn_net_local <=> ippp-device
|
|
|
|
*/
|
1996-01-09 04:13:18 +00:00
|
|
|
int isdn_ppp_bind(isdn_net_local * lp)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int unit = 0;
|
|
|
|
long flags;
|
1996-08-12 16:26:47 +00:00
|
|
|
struct ippp_struct *is;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
|
1996-08-12 16:26:47 +00:00
|
|
|
return -1;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
save_flags(flags);
|
|
|
|
cli();
|
1996-01-22 05:08:06 +00:00
|
|
|
|
1996-04-30 11:10:42 +00:00
|
|
|
if(lp->pppbind < 0) /* device bounded to ippp device ? */
|
|
|
|
{
|
|
|
|
isdn_net_dev *net_dev = dev->netdev;
|
|
|
|
char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */
|
|
|
|
memset(exclusive,0,ISDN_MAX_CHANNELS);
|
|
|
|
while (net_dev) { /* step through net devices to find exclusive minors */
|
|
|
|
isdn_net_local *lp = &net_dev->local;
|
|
|
|
if(lp->pppbind >= 0)
|
|
|
|
exclusive[lp->pppbind] = 1;
|
|
|
|
net_dev = net_dev->next;
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
1996-04-30 11:10:42 +00:00
|
|
|
/*
|
1996-08-12 16:26:47 +00:00
|
|
|
* search a free device / slot
|
1996-04-30 11:10:42 +00:00
|
|
|
*/
|
|
|
|
for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
|
1996-08-12 16:26:47 +00:00
|
|
|
if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) { /* OPEN, but not connected! */
|
1996-04-30 11:10:42 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
1996-08-12 16:26:47 +00:00
|
|
|
for(i=0;i<ISDN_MAX_CHANNELS;i++)
|
|
|
|
if(ippp_table[i]->minor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN)
|
|
|
|
break;
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (i >= ISDN_MAX_CHANNELS) {
|
|
|
|
restore_flags(flags);
|
|
|
|
printk(KERN_WARNING "isdn_ppp_bind: Can't find usable ippp device.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
unit = isdn_ppp_if_get_unit(lp->name); /* get unit number from interface name .. ugly! */
|
|
|
|
if(unit < 0) {
|
1996-09-07 12:50:12 +00:00
|
|
|
printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n",lp->name);
|
1996-08-12 16:26:47 +00:00
|
|
|
return -1;
|
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
lp->ppp_slot = i;
|
|
|
|
is = ippp_table[i];
|
|
|
|
is->lp = lp;
|
|
|
|
is->unit = unit;
|
1996-09-07 12:50:12 +00:00
|
|
|
is->state = IPPP_OPEN | IPPP_ASSIGNED; /* assigned to a netdevice but not connected */
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
restore_flags(flags);
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
return lp->ppp_slot;
|
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
/*
|
|
|
|
* kick the ipppd on the device
|
|
|
|
* (wakes up daemon after B-channel connect)
|
|
|
|
*/
|
|
|
|
|
|
|
|
void isdn_ppp_wakeup_daemon(isdn_net_local *lp)
|
|
|
|
{
|
1996-09-07 12:50:12 +00:00
|
|
|
if(lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if (ippp_table[lp->ppp_slot]->wq)
|
|
|
|
wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
|
1996-05-06 11:34:57 +00:00
|
|
|
/*
|
|
|
|
* there was a hangup on the netdevice
|
|
|
|
* force wakeup of the ippp device
|
|
|
|
* go into 'device waits for release' state
|
|
|
|
*/
|
1996-08-12 16:26:47 +00:00
|
|
|
static int isdn_ppp_closewait(int slot)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
1996-08-12 16:26:47 +00:00
|
|
|
struct ippp_struct *is;
|
|
|
|
|
|
|
|
if (slot < 0 || slot >= ISDN_MAX_CHANNELS)
|
1996-01-09 04:13:18 +00:00
|
|
|
return 0;
|
1996-08-12 16:26:47 +00:00
|
|
|
is = ippp_table[slot];
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if (is->state && is->wq)
|
|
|
|
wake_up_interruptible(&is->wq);
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
is->state = IPPP_CLOSEWAIT;
|
1996-01-09 04:13:18 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
/*
|
|
|
|
* isdn_ppp_find_slot / isdn_ppp_free_slot
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int isdn_ppp_get_slot(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i=0;i<ISDN_MAX_CHANNELS;i++) {
|
|
|
|
if(!ippp_table[i]->state)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
/*
|
|
|
|
* isdn_ppp_open
|
|
|
|
*/
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
int isdn_ppp_open(int min, struct file *file)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
1996-08-12 16:26:47 +00:00
|
|
|
int slot;
|
|
|
|
struct ippp_struct *is;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-09-07 12:50:12 +00:00
|
|
|
if(min < 0 || min > ISDN_MAX_CHANNELS)
|
|
|
|
return -ENODEV;
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
slot = isdn_ppp_get_slot();
|
|
|
|
if(slot < 0) {
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
is = file->private_data = ippp_table[slot];
|
|
|
|
|
|
|
|
if(is->debug & 0x1)
|
|
|
|
printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n",slot, min,is->state);
|
|
|
|
|
|
|
|
is->lp = 0;
|
|
|
|
is->mp_seqno = 0; /* MP sequence number */
|
|
|
|
is->pppcfg = 0; /* ppp configuration */
|
|
|
|
is->mpppcfg = 0; /* mppp configuration */
|
|
|
|
is->range = 0x1000000; /* MP: 24 bit range */
|
|
|
|
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;
|
|
|
|
is->wq = NULL; /* read() wait queue */
|
|
|
|
is->wq1 = NULL; /* select() wait queue */
|
|
|
|
is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
|
|
|
|
is->last = is->rq;
|
|
|
|
is->minor = min;
|
1996-01-09 04:13:18 +00:00
|
|
|
#ifdef CONFIG_ISDN_PPP_VJ
|
1996-01-22 05:08:06 +00:00
|
|
|
/*
|
|
|
|
* VJ header compression init
|
|
|
|
*/
|
1996-08-12 16:26:47 +00:00
|
|
|
is->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */
|
1996-01-09 04:13:18 +00:00
|
|
|
#endif
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
is->state = IPPP_OPEN;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1996-05-06 11:34:57 +00:00
|
|
|
/*
|
|
|
|
* release ippp device
|
|
|
|
*/
|
1996-08-12 16:26:47 +00:00
|
|
|
void isdn_ppp_release(int min, struct file *file)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
|
|
|
int i;
|
1996-08-12 16:26:47 +00:00
|
|
|
struct ippp_struct *is;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if (min < 0 || min >= ISDN_MAX_CHANNELS)
|
1996-01-09 04:13:18 +00:00
|
|
|
return;
|
1996-08-12 16:26:47 +00:00
|
|
|
is = file->private_data;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if(is->debug & 0x1)
|
|
|
|
printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", min, (long) is->lp);
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if (is->lp) { /* a lp address says: this link is still up */
|
1996-09-07 12:50:12 +00:00
|
|
|
isdn_net_dev *p = is->lp->netdev;
|
|
|
|
|
|
|
|
is->state &= ~IPPP_CONNECT; /* -> effect: no call of wakeup */
|
|
|
|
/*
|
|
|
|
* 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()
|
|
|
|
*/
|
|
|
|
isdn_net_hangup(&p->dev);
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
for (i = 0; i < NUM_RCV_BUFFS; i++) {
|
1996-08-12 16:26:47 +00:00
|
|
|
if (is->rq[i].buf) {
|
|
|
|
kfree(is->rq[i].buf);
|
|
|
|
is->rq[i].buf = NULL;
|
1996-05-06 11:34:57 +00:00
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
1996-08-12 16:26:47 +00:00
|
|
|
is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
|
|
|
|
is->last = is->rq;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_ISDN_PPP_VJ
|
1996-08-12 16:26:47 +00:00
|
|
|
slhc_free(is->slcomp);
|
|
|
|
is->slcomp = NULL;
|
1996-01-09 04:13:18 +00:00
|
|
|
#endif
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
is->state = 0;
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
|
1996-05-06 11:34:57 +00:00
|
|
|
/*
|
|
|
|
* get_arg .. ioctl helper
|
|
|
|
*/
|
1996-09-07 12:50:12 +00:00
|
|
|
static int get_arg(void *b,void *val,int len)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
|
|
|
int r;
|
1996-09-07 12:50:12 +00:00
|
|
|
if(len <= 0)
|
|
|
|
len = sizeof(unsigned long);
|
|
|
|
if ((r = verify_area(VERIFY_READ, (void *) b, len )))
|
1996-01-09 04:13:18 +00:00
|
|
|
return r;
|
1996-10-22 23:14:19 +00:00
|
|
|
copy_from_user((void *) val, b, len );
|
1996-01-09 04:13:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1996-05-06 11:34:57 +00:00
|
|
|
/*
|
|
|
|
* set arg .. ioctl helper
|
|
|
|
*/
|
1996-09-07 12:50:12 +00:00
|
|
|
static int set_arg(void *b, unsigned long val,void *str)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
|
|
|
int r;
|
1996-09-07 12:50:12 +00:00
|
|
|
if(!str) {
|
|
|
|
if ((r = verify_area(VERIFY_WRITE, b, 4 )))
|
|
|
|
return r;
|
1996-10-22 23:14:19 +00:00
|
|
|
copy_to_user(b, (void *) &val, 4 );
|
1996-09-07 12:50:12 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if ((r = verify_area(VERIFY_WRITE, b,val)))
|
|
|
|
return r;
|
1996-10-22 23:14:19 +00:00
|
|
|
copy_to_user(b,str,val);
|
1996-09-07 12:50:12 +00:00
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1996-05-06 11:34:57 +00:00
|
|
|
/*
|
|
|
|
* ippp device ioctl
|
|
|
|
*/
|
1996-08-12 16:26:47 +00:00
|
|
|
int isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
|
|
|
unsigned long val;
|
|
|
|
int r;
|
1996-08-12 16:26:47 +00:00
|
|
|
struct ippp_struct *is;
|
|
|
|
|
|
|
|
is = file->private_data;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if(is->debug & 0x1)
|
|
|
|
printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n",min,cmd,is->state);
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if (!(is->state & IPPP_OPEN))
|
1996-01-09 04:13:18 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case PPPIOCBUNDLE:
|
1996-02-11 02:27:12 +00:00
|
|
|
#ifdef CONFIG_ISDN_MPP
|
1996-09-07 12:50:12 +00:00
|
|
|
if( !(is->state & IPPP_CONNECT) )
|
|
|
|
return -EINVAL;
|
|
|
|
if ((r = get_arg((void *) arg, &val,0)))
|
1996-01-09 04:13:18 +00:00
|
|
|
return r;
|
1996-01-22 05:08:06 +00:00
|
|
|
printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
|
1996-08-12 16:26:47 +00:00
|
|
|
(int) min, (int) is->unit, (int) val);
|
|
|
|
return isdn_ppp_bundle(is, val);
|
1996-02-11 02:27:12 +00:00
|
|
|
#else
|
|
|
|
return -1;
|
|
|
|
#endif
|
1996-01-09 04:13:18 +00:00
|
|
|
break;
|
|
|
|
case PPPIOCGUNIT: /* get ppp/isdn unit number */
|
1996-09-07 12:50:12 +00:00
|
|
|
if ((r = set_arg((void *) arg, is->unit,NULL)))
|
1996-01-09 04:13:18 +00:00
|
|
|
return r;
|
|
|
|
break;
|
|
|
|
case PPPIOCGMPFLAGS: /* get configuration flags */
|
1996-09-07 12:50:12 +00:00
|
|
|
if ((r = set_arg((void *) arg, is->mpppcfg,NULL)))
|
1996-01-09 04:13:18 +00:00
|
|
|
return r;
|
|
|
|
break;
|
|
|
|
case PPPIOCSMPFLAGS: /* set configuration flags */
|
1996-09-07 12:50:12 +00:00
|
|
|
if ((r = get_arg((void *) arg, &val,0)))
|
1996-01-09 04:13:18 +00:00
|
|
|
return r;
|
1996-08-12 16:26:47 +00:00
|
|
|
is->mpppcfg = val;
|
1996-01-09 04:13:18 +00:00
|
|
|
break;
|
|
|
|
case PPPIOCGFLAGS: /* get configuration flags */
|
1996-09-07 12:50:12 +00:00
|
|
|
if ((r = set_arg((void *) arg, is->pppcfg,NULL)))
|
1996-01-09 04:13:18 +00:00
|
|
|
return r;
|
|
|
|
break;
|
|
|
|
case PPPIOCSFLAGS: /* set configuration flags */
|
1996-09-07 12:50:12 +00:00
|
|
|
if ((r = get_arg((void *) arg, &val,0))) {
|
1996-01-09 04:13:18 +00:00
|
|
|
return r;
|
|
|
|
}
|
1996-09-07 12:50:12 +00:00
|
|
|
if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT) ) {
|
1996-08-12 16:26:47 +00:00
|
|
|
isdn_net_local *lp = is->lp;
|
1996-09-07 12:50:12 +00:00
|
|
|
if(lp) {
|
|
|
|
lp->netdev->dev.tbusy = 0;
|
|
|
|
mark_bh(NET_BH); /* OK .. we are ready to send buffers */
|
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
1996-08-12 16:26:47 +00:00
|
|
|
is->pppcfg = val;
|
1996-01-09 04:13:18 +00:00
|
|
|
break;
|
|
|
|
#if 0
|
|
|
|
case PPPIOCGSTAT: /* read PPP statistic information */
|
|
|
|
break;
|
|
|
|
#endif
|
1996-09-07 12:50:12 +00:00
|
|
|
case PPPIOCGIDLE: /* get idle time information */
|
|
|
|
if(is->lp)
|
|
|
|
{
|
|
|
|
struct ppp_idle pidle;
|
|
|
|
pidle.xmit_idle = pidle.recv_idle = is->lp->huptimer;
|
|
|
|
if((r = set_arg((void *) arg,sizeof(struct ppp_idle),&pidle)))
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
break;
|
1996-01-09 04:13:18 +00:00
|
|
|
case PPPIOCSMRU: /* set receive unit size for PPP */
|
1996-09-07 12:50:12 +00:00
|
|
|
if ((r = get_arg((void *) arg, &val,0)))
|
1996-01-09 04:13:18 +00:00
|
|
|
return r;
|
1996-08-12 16:26:47 +00:00
|
|
|
is->mru = val;
|
1996-01-09 04:13:18 +00:00
|
|
|
break;
|
|
|
|
case PPPIOCSMPMRU:
|
|
|
|
break;
|
|
|
|
case PPPIOCSMPMTU:
|
|
|
|
break;
|
|
|
|
case PPPIOCSMAXCID: /* set the maximum compression slot id */
|
1996-09-07 12:50:12 +00:00
|
|
|
if ((r = get_arg((void *) arg, &val,0)))
|
1996-01-09 04:13:18 +00:00
|
|
|
return r;
|
1996-06-11 14:50:29 +00:00
|
|
|
val++;
|
1996-08-12 16:26:47 +00:00
|
|
|
if(is->maxcid != val) {
|
1996-06-11 14:50:29 +00:00
|
|
|
#ifdef CONFIG_ISDN_PPP_VJ
|
|
|
|
struct slcompress *sltmp;
|
|
|
|
#endif
|
1996-08-12 16:26:47 +00:00
|
|
|
if(is->debug & 0x1)
|
1996-06-11 14:50:29 +00:00
|
|
|
printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n",val);
|
1996-08-12 16:26:47 +00:00
|
|
|
is->maxcid = val;
|
1996-06-11 14:50:29 +00:00
|
|
|
#ifdef CONFIG_ISDN_PPP_VJ
|
|
|
|
sltmp = slhc_init(16,val);
|
|
|
|
if(!sltmp) {
|
|
|
|
printk(KERN_ERR "ippp, can't realloc slhc struct\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
1996-08-12 16:26:47 +00:00
|
|
|
if(is->slcomp)
|
|
|
|
slhc_free(is->slcomp);
|
|
|
|
is->slcomp = sltmp;
|
1996-06-11 14:50:29 +00:00
|
|
|
#endif
|
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
break;
|
|
|
|
case PPPIOCGDEBUG:
|
1996-09-07 12:50:12 +00:00
|
|
|
if ((r = set_arg((void *) arg, is->debug,0)))
|
1996-05-06 11:34:57 +00:00
|
|
|
return r;
|
1996-01-09 04:13:18 +00:00
|
|
|
break;
|
|
|
|
case PPPIOCSDEBUG:
|
1996-09-07 12:50:12 +00:00
|
|
|
if ((r = get_arg((void *) arg, &val,0)))
|
1996-05-06 11:34:57 +00:00
|
|
|
return r;
|
1996-08-12 16:26:47 +00:00
|
|
|
is->debug = val;
|
1996-01-09 04:13:18 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
int isdn_ppp_select(int min, struct file *file, int type, select_table * st)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
|
|
|
struct ippp_buf_queue *bf, *bl;
|
|
|
|
unsigned long flags;
|
1996-08-12 16:26:47 +00:00
|
|
|
struct ippp_struct *is;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
is = file->private_data;
|
1996-01-22 05:08:06 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if(is->debug & 0x2)
|
|
|
|
printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",min,type);
|
|
|
|
|
|
|
|
if (!(is->state & IPPP_OPEN))
|
1996-01-09 04:13:18 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case SEL_IN:
|
|
|
|
save_flags(flags);
|
|
|
|
cli();
|
1996-08-12 16:26:47 +00:00
|
|
|
bl = is->last;
|
|
|
|
bf = is->first;
|
1996-05-06 11:34:57 +00:00
|
|
|
/*
|
|
|
|
* if IPPP_NOBLOCK is set we return even if we have nothing to read
|
|
|
|
*/
|
1996-08-12 16:26:47 +00:00
|
|
|
if (bf->next == bl && !(is->state & IPPP_NOBLOCK)) {
|
|
|
|
select_wait(&is->wq, st);
|
1996-01-09 04:13:18 +00:00
|
|
|
restore_flags(flags);
|
|
|
|
return 0;
|
|
|
|
}
|
1996-08-12 16:26:47 +00:00
|
|
|
is->state &= ~IPPP_NOBLOCK;
|
1996-01-09 04:13:18 +00:00
|
|
|
restore_flags(flags);
|
|
|
|
return 1;
|
|
|
|
case SEL_OUT:
|
1996-01-22 05:08:06 +00:00
|
|
|
/* we're always ready to send .. */
|
1996-01-09 04:13:18 +00:00
|
|
|
return 1;
|
|
|
|
case SEL_EX:
|
1996-08-12 16:26:47 +00:00
|
|
|
select_wait(&is->wq1, st);
|
1996-01-09 04:13:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1996-01-22 05:08:06 +00:00
|
|
|
* fill up isdn_ppp_read() queue ..
|
1996-01-09 04:13:18 +00:00
|
|
|
*/
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
|
|
|
struct ippp_buf_queue *bf, *bl;
|
|
|
|
unsigned long flags;
|
1996-06-11 14:50:29 +00:00
|
|
|
unsigned char *nbuf;
|
1996-08-12 16:26:47 +00:00
|
|
|
struct ippp_struct *is;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
|
|
|
|
printk(KERN_WARNING "ippp: illegal slot.\n");
|
1996-01-09 04:13:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
1996-08-12 16:26:47 +00:00
|
|
|
is = ippp_table[slot];
|
|
|
|
|
|
|
|
if (!(is->state & IPPP_CONNECT)) {
|
1996-01-09 04:13:18 +00:00
|
|
|
printk(KERN_DEBUG "ippp: device not activated.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
1996-06-11 14:50:29 +00:00
|
|
|
|
|
|
|
nbuf = (unsigned char *) kmalloc(len+4, GFP_ATOMIC);
|
|
|
|
if(!nbuf) {
|
|
|
|
printk(KERN_WARNING "ippp: Can't alloc buf\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
nbuf[0] = PPP_ALLSTATIONS;
|
|
|
|
nbuf[1] = PPP_UI;
|
|
|
|
nbuf[2] = proto >> 8;
|
|
|
|
nbuf[3] = proto & 0xff;
|
|
|
|
memcpy(nbuf+4, buf, len);
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
save_flags(flags);
|
|
|
|
cli();
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
bf = is->first;
|
|
|
|
bl = is->last;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
if (bf == bl) {
|
|
|
|
printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n");
|
|
|
|
bf = bf->next;
|
|
|
|
kfree(bf->buf);
|
1996-08-12 16:26:47 +00:00
|
|
|
is->first = bf;
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
1996-06-11 14:50:29 +00:00
|
|
|
bl->buf = (char *) nbuf;
|
1996-05-06 11:34:57 +00:00
|
|
|
bl->len = len+4;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
is->last = bl->next;
|
1996-01-09 04:13:18 +00:00
|
|
|
restore_flags(flags);
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if (is->wq)
|
|
|
|
wake_up_interruptible(&is->wq);
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1996-01-22 05:08:06 +00:00
|
|
|
* read() .. non-blocking: ipppd calls it only after select()
|
|
|
|
* reports, that there is data
|
1996-01-09 04:13:18 +00:00
|
|
|
*/
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
int isdn_ppp_read(int min, struct file *file, char *buf, int count)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
1996-08-12 16:26:47 +00:00
|
|
|
struct ippp_struct *is;
|
1996-01-09 04:13:18 +00:00
|
|
|
struct ippp_buf_queue *b;
|
|
|
|
int r;
|
|
|
|
unsigned long flags;
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
is = file->private_data;
|
|
|
|
|
|
|
|
if (!(is->state & IPPP_OPEN))
|
1996-01-09 04:13:18 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((r = verify_area(VERIFY_WRITE, (void *) buf, count)))
|
|
|
|
return r;
|
|
|
|
|
|
|
|
save_flags(flags);
|
|
|
|
cli();
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
b = is->first->next;
|
1996-01-09 04:13:18 +00:00
|
|
|
if (!b->buf) {
|
|
|
|
restore_flags(flags);
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
if (b->len < count)
|
|
|
|
count = b->len;
|
1996-10-22 23:14:19 +00:00
|
|
|
copy_to_user(buf, b->buf, count);
|
1996-01-09 04:13:18 +00:00
|
|
|
kfree(b->buf);
|
|
|
|
b->buf = NULL;
|
1996-08-12 16:26:47 +00:00
|
|
|
is->first = b;
|
1996-01-09 04:13:18 +00:00
|
|
|
restore_flags(flags);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
1996-01-22 05:08:06 +00:00
|
|
|
/*
|
|
|
|
* ipppd wanna write a packet to the card .. non-blocking
|
|
|
|
*/
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
int isdn_ppp_write(int min, struct file *file, const char *buf, int count)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
|
|
|
isdn_net_local *lp;
|
1996-08-12 16:26:47 +00:00
|
|
|
struct ippp_struct *is;
|
1996-10-22 23:14:19 +00:00
|
|
|
int proto;
|
|
|
|
unsigned char protobuf[4];
|
1996-08-12 16:26:47 +00:00
|
|
|
|
|
|
|
is = file->private_data;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if (!(is->state & IPPP_CONNECT))
|
1996-01-09 04:13:18 +00:00
|
|
|
return 0;
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
lp = is->lp;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
/* -> push it directly to the lowlevel interface */
|
|
|
|
|
|
|
|
if (!lp)
|
|
|
|
printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
|
|
|
|
else {
|
1996-10-22 23:14:19 +00:00
|
|
|
/*
|
|
|
|
* Don't reset huptimer for
|
|
|
|
* LCP packets. (Echo requests).
|
|
|
|
*/
|
|
|
|
copy_from_user(protobuf, buf, 4);
|
|
|
|
proto = PPP_PROTOCOL(protobuf);
|
|
|
|
if (proto != PPP_LCP)
|
|
|
|
lp->huptimer = 0;
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
if (lp->isdn_device < 0 || lp->isdn_channel < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (dev->drv[lp->isdn_device]->running && lp->dialstate == 0 &&
|
1996-06-11 14:50:29 +00:00
|
|
|
(lp->flags & ISDN_NET_CONNECTED)) {
|
1996-10-22 09:39:49 +00:00
|
|
|
int cnt;
|
1996-06-11 14:50:29 +00:00
|
|
|
struct sk_buff *skb;
|
|
|
|
skb = dev_alloc_skb(count);
|
1996-10-22 09:39:49 +00:00
|
|
|
if(!skb) {
|
1996-06-11 14:50:29 +00:00
|
|
|
printk(KERN_WARNING "isdn_ppp_write: out of memory!\n");
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
skb->free = 1;
|
1996-10-22 23:14:19 +00:00
|
|
|
copy_from_user(skb_put(skb, count), buf, count);
|
1996-10-22 09:39:49 +00:00
|
|
|
if(is->debug & 0x40) {
|
|
|
|
printk(KERN_DEBUG "ppp xmit: len %ld\n",skb->len);
|
|
|
|
isdn_ppp_frame_log("xmit",skb->data,skb->len,32);
|
|
|
|
}
|
|
|
|
if( (cnt=isdn_writebuf_skb_stub(lp->isdn_device,lp->isdn_channel,skb)) != count) {
|
1996-06-11 14:50:29 +00:00
|
|
|
if(lp->sav_skb) {
|
|
|
|
dev_kfree_skb(lp->sav_skb,FREE_WRITE);
|
1996-10-22 09:39:49 +00:00
|
|
|
printk(KERN_INFO "isdn_ppp_write: freeing sav_skb (%d,%d)!\n",cnt,count);
|
1996-06-11 14:50:29 +00:00
|
|
|
}
|
1996-10-22 09:39:49 +00:00
|
|
|
else
|
|
|
|
printk(KERN_INFO "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n",cnt,count);
|
1996-06-11 14:50:29 +00:00
|
|
|
lp->sav_skb = skb;
|
|
|
|
}
|
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
1996-01-22 05:08:06 +00:00
|
|
|
/*
|
|
|
|
* init memory, structures etc.
|
|
|
|
*/
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
int isdn_ppp_init(void)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
|
1996-04-20 16:32:32 +00:00
|
|
|
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");
|
1996-05-06 11:34:57 +00:00
|
|
|
for (j = 0; j < i; j++)
|
|
|
|
kfree(ippp_table[i]);
|
1996-04-20 16:32:32 +00:00
|
|
|
return -1;
|
|
|
|
}
|
1996-05-06 11:34:57 +00:00
|
|
|
memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
|
1996-04-20 16:32:32 +00:00
|
|
|
ippp_table[i]->state = 0;
|
|
|
|
ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
|
|
|
|
ippp_table[i]->last = ippp_table[i]->rq;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
for (j = 0; j < NUM_RCV_BUFFS; j++) {
|
1996-04-20 16:32:32 +00:00
|
|
|
ippp_table[i]->rq[j].buf = NULL;
|
|
|
|
ippp_table[i]->rq[j].last = ippp_table[i]->rq +
|
1996-01-09 04:13:18 +00:00
|
|
|
(NUM_RCV_BUFFS + j - 1) % NUM_RCV_BUFFS;
|
1996-04-20 16:32:32 +00:00
|
|
|
ippp_table[i]->rq[j].next = ippp_table[i]->rq + (j + 1) % NUM_RCV_BUFFS;
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void isdn_ppp_cleanup(void)
|
|
|
|
{
|
1996-04-20 16:32:32 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ISDN_MAX_CHANNELS; i++)
|
|
|
|
kfree(ippp_table[i]);
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* handler for incoming packets on a syncPPP interface
|
|
|
|
*/
|
1996-01-22 05:08:06 +00:00
|
|
|
void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
1996-08-12 16:26:47 +00:00
|
|
|
struct ippp_struct *is;
|
|
|
|
is = ippp_table[lp->ppp_slot];
|
|
|
|
|
1996-10-22 09:39:49 +00:00
|
|
|
if(is->debug & 0x4) {
|
|
|
|
printk(KERN_DEBUG "ippp_receive: len: %ld\n",skb->len);
|
|
|
|
isdn_ppp_frame_log("receive",skb->data,skb->len,32);
|
|
|
|
}
|
1996-01-22 05:08:06 +00:00
|
|
|
|
1996-06-11 14:50:29 +00:00
|
|
|
if(net_dev->local.master) {
|
|
|
|
printk(KERN_WARNING "isdn_ppp_receice: net_dev != master\n");
|
|
|
|
net_dev = ((isdn_net_local*) net_dev->local.master->priv)->netdev;
|
|
|
|
}
|
|
|
|
|
1996-01-22 05:08:06 +00:00
|
|
|
if(skb->data[0] == 0xff && skb->data[1] == 0x03)
|
|
|
|
skb_pull(skb,2);
|
1996-08-12 16:26:47 +00:00
|
|
|
else if (is->pppcfg & SC_REJ_COMP_AC) {
|
1996-07-01 19:47:24 +00:00
|
|
|
skb->free = 1;
|
|
|
|
dev_kfree_skb(skb,0 /* FREE_READ */ );
|
1996-01-09 04:13:18 +00:00
|
|
|
return; /* discard it silently */
|
1996-05-06 11:34:57 +00:00
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-02-11 02:27:12 +00:00
|
|
|
#ifdef CONFIG_ISDN_MPP
|
1996-08-12 16:26:47 +00:00
|
|
|
if (!(is->mpppcfg & SC_REJ_MP_PROT)) {
|
1996-02-11 02:27:12 +00:00
|
|
|
int proto;
|
|
|
|
int sqno_end;
|
1996-01-22 05:08:06 +00:00
|
|
|
if (skb->data[0] & 0x1) {
|
|
|
|
proto = skb->data[0];
|
|
|
|
skb_pull(skb,1); /* protocol ID is only 8 bit */
|
1996-01-09 04:13:18 +00:00
|
|
|
} else {
|
1996-01-22 05:08:06 +00:00
|
|
|
proto = ((int) skb->data[0] << 8) + skb->data[1];
|
|
|
|
skb_pull(skb,2);
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
if (proto == PPP_MP) {
|
|
|
|
isdn_net_local *lpq;
|
1996-10-22 09:39:49 +00:00
|
|
|
long sqno, min_sqno, tseq;
|
1996-01-22 05:08:06 +00:00
|
|
|
u_char BEbyte = skb->data[0];
|
1996-08-12 16:26:47 +00:00
|
|
|
if(is->debug & 0x8)
|
|
|
|
printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_slot, proto ,
|
1996-05-06 11:34:57 +00:00
|
|
|
(int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2],
|
|
|
|
(int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
|
1996-08-12 16:26:47 +00:00
|
|
|
if (!(is->mpppcfg & SC_IN_SHORT_SEQ)) {
|
1996-01-22 05:08:06 +00:00
|
|
|
sqno = ((int) skb->data[1] << 16) + ((int) skb->data[2] << 8) + (int) skb->data[3];
|
|
|
|
skb_pull(skb,4);
|
1996-01-09 04:13:18 +00:00
|
|
|
} else {
|
1996-01-22 05:08:06 +00:00
|
|
|
sqno = (((int) skb->data[0] & 0xf) << 8) + (int) skb->data[1];
|
|
|
|
skb_pull(skb,2);
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if ((tseq = is->last_link_seqno) >= sqno) {
|
|
|
|
int range = is->range;
|
1996-01-22 05:08:06 +00:00
|
|
|
if (tseq + 1024 < range + sqno) /* redundancy check .. not MP conform */
|
1996-10-22 09:39:49 +00:00
|
|
|
printk(KERN_WARNING "isdn_ppp_receive, MP, detected overflow with sqno: %ld, last: %ld !!!\n", sqno, tseq);
|
1996-01-09 04:13:18 +00:00
|
|
|
else {
|
|
|
|
sqno += range;
|
1996-08-12 16:26:47 +00:00
|
|
|
is->last_link_seqno = sqno;
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
} else
|
1996-08-12 16:26:47 +00:00
|
|
|
is->last_link_seqno = sqno;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-10-22 09:39:49 +00:00
|
|
|
for (min_sqno = LONG_MAX, lpq = net_dev->queue;;) {
|
|
|
|
long lls = ippp_table[lpq->ppp_slot]->last_link_seqno;
|
|
|
|
if (lls >= 0 && lls < min_sqno)
|
|
|
|
min_sqno = lls;
|
1996-01-09 04:13:18 +00:00
|
|
|
lpq = lpq->next;
|
|
|
|
if (lpq == net_dev->queue)
|
|
|
|
break;
|
|
|
|
}
|
1996-08-12 16:26:47 +00:00
|
|
|
if (min_sqno >= ippp_table[lpq->ppp_slot]->range) { /* OK, every link overflowed */
|
|
|
|
int mask = ippp_table[lpq->ppp_slot]->range - 1; /* range is a power of 2 */
|
1996-10-22 09:39:49 +00:00
|
|
|
#if 0
|
1996-01-09 04:13:18 +00:00
|
|
|
isdn_ppp_cleanup_queue(net_dev, min_sqno);
|
1996-10-22 09:39:49 +00:00
|
|
|
#endif
|
1996-01-09 04:13:18 +00:00
|
|
|
isdn_ppp_mask_queue(net_dev, mask);
|
|
|
|
net_dev->ib.next_num &= mask;
|
|
|
|
{
|
|
|
|
struct sqqueue *q = net_dev->ib.sq;
|
|
|
|
while (q) {
|
|
|
|
q->sqno_start &= mask;
|
|
|
|
q->sqno_end &= mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
min_sqno &= mask;
|
|
|
|
for (lpq = net_dev->queue;;) {
|
1996-08-12 16:26:47 +00:00
|
|
|
ippp_table[lpq->ppp_slot]->last_link_seqno &= mask;
|
1996-01-09 04:13:18 +00:00
|
|
|
lpq = lpq->next;
|
|
|
|
if (lpq == net_dev->queue)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((BEbyte & (MP_BEGIN_FRAG | MP_END_FRAG)) != (MP_BEGIN_FRAG | MP_END_FRAG)) {
|
1996-08-12 16:26:47 +00:00
|
|
|
printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_slot);
|
1996-10-22 09:39:49 +00:00
|
|
|
if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb , BEbyte, &sqno, min_sqno)) < 0) {
|
|
|
|
net_dev->ib.modify = 1; /* block timeout-timer */
|
|
|
|
isdn_ppp_cleanup_sqqueue(net_dev,lp,min_sqno);
|
|
|
|
net_dev->ib.modify = 0;
|
1996-01-09 04:13:18 +00:00
|
|
|
return; /* no packet complete */
|
1996-10-22 09:39:49 +00:00
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
} else
|
|
|
|
sqno_end = sqno;
|
|
|
|
|
1996-10-22 09:39:49 +00:00
|
|
|
if(is->debug & 0x40)
|
1996-10-22 23:14:19 +00:00
|
|
|
printk(KERN_DEBUG "min_sqno: %ld sqno_end %d next: %ld\n",min_sqno,sqno_end,net_dev->ib.next_num );
|
1996-10-22 09:39:49 +00:00
|
|
|
|
1996-01-22 05:08:06 +00:00
|
|
|
/*
|
|
|
|
* MP buffer management .. reorders incoming packets ..
|
|
|
|
* lotsa mem-copies and not heavily tested.
|
|
|
|
*
|
|
|
|
* first check whether there is more than one link in the bundle
|
|
|
|
* then check whether the number is in order
|
|
|
|
*/
|
1996-01-09 04:13:18 +00:00
|
|
|
net_dev->ib.modify = 1; /* block timeout-timer */
|
1996-02-11 02:27:12 +00:00
|
|
|
if (net_dev->ib.bundled && net_dev->ib.next_num != sqno) {
|
1996-01-22 05:08:06 +00:00
|
|
|
/*
|
|
|
|
* packet is not 'in order'
|
|
|
|
*/
|
1996-01-09 04:13:18 +00:00
|
|
|
struct sqqueue *q;
|
|
|
|
|
|
|
|
q = (struct sqqueue *) kmalloc(sizeof(struct sqqueue), GFP_ATOMIC);
|
|
|
|
if (!q) {
|
|
|
|
net_dev->ib.modify = 0;
|
1996-05-06 11:34:57 +00:00
|
|
|
printk(KERN_WARNING "ippp/MPPP: Bad! Can't alloc sq node!\n");
|
1996-07-01 19:47:24 +00:00
|
|
|
skb->free = 1;
|
|
|
|
dev_kfree_skb(skb, 0 /* FREE_READ */ );
|
1996-01-09 04:13:18 +00:00
|
|
|
return; /* discard */
|
|
|
|
}
|
1996-01-22 05:08:06 +00:00
|
|
|
q->skb = skb;
|
1996-01-09 04:13:18 +00:00
|
|
|
q->sqno_end = sqno_end;
|
|
|
|
q->sqno_start = sqno;
|
|
|
|
q->timer = jiffies + (ISDN_TIMER_1SEC) * 5; /* timeout after 5 seconds */
|
|
|
|
|
|
|
|
if (!net_dev->ib.sq) {
|
|
|
|
net_dev->ib.sq = q;
|
|
|
|
q->next = NULL;
|
|
|
|
} else {
|
|
|
|
struct sqqueue *ql = net_dev->ib.sq;
|
|
|
|
if (ql->sqno_start > q->sqno_start) {
|
|
|
|
q->next = ql;
|
|
|
|
net_dev->ib.sq = q;
|
|
|
|
} else {
|
|
|
|
while (ql->next && ql->next->sqno_start < q->sqno_start)
|
|
|
|
ql = ql->next;
|
|
|
|
q->next = ql->next;
|
|
|
|
ql->next = q;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
1996-01-22 05:08:06 +00:00
|
|
|
/*
|
|
|
|
* packet was 'in order' .. push it higher
|
|
|
|
*/
|
1996-01-09 04:13:18 +00:00
|
|
|
net_dev->ib.next_num = sqno_end + 1;
|
1996-01-22 05:08:06 +00:00
|
|
|
isdn_ppp_push_higher(net_dev, lp, skb, -1);
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
1996-10-22 09:39:49 +00:00
|
|
|
isdn_ppp_cleanup_sqqueue(net_dev,lp,min_sqno);
|
1996-01-09 04:13:18 +00:00
|
|
|
net_dev->ib.modify = 0;
|
|
|
|
|
|
|
|
} else
|
1996-01-22 05:08:06 +00:00
|
|
|
isdn_ppp_push_higher(net_dev, lp, skb , proto);
|
1996-01-09 04:13:18 +00:00
|
|
|
} else
|
1996-02-11 02:27:12 +00:00
|
|
|
#endif
|
1996-01-22 05:08:06 +00:00
|
|
|
isdn_ppp_push_higher(net_dev, lp, skb , -1);
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
|
1996-10-22 09:39:49 +00:00
|
|
|
/*
|
|
|
|
* check sq-queue, whether we have still buffered the next packet(s)
|
|
|
|
* or packets with a sqno less or equal to min_sqno
|
|
|
|
* net_dev: master netdevice , lp: 'real' local connection
|
|
|
|
*/
|
|
|
|
static void isdn_ppp_cleanup_sqqueue(isdn_net_dev *net_dev, isdn_net_local *lp,long min_sqno)
|
|
|
|
{
|
|
|
|
struct sqqueue *q;
|
|
|
|
|
|
|
|
while ((q = net_dev->ib.sq) && (q->sqno_start == net_dev->ib.next_num || q->sqno_end <= min_sqno) ) {
|
|
|
|
if(q->sqno_start != net_dev->ib.next_num) {
|
|
|
|
printk(KERN_DEBUG "ippp: MP, stepping over missing frame: %ld\n",net_dev->ib.next_num);
|
|
|
|
slhc_toss(ippp_table[net_dev->local.ppp_slot]->slcomp);
|
|
|
|
}
|
|
|
|
isdn_ppp_push_higher(net_dev, lp, q->skb, -1);
|
|
|
|
net_dev->ib.sq = q->next;
|
|
|
|
net_dev->ib.next_num = q->sqno_end + 1;
|
|
|
|
kfree(q);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
/*
|
|
|
|
* push frame to higher layers
|
|
|
|
* note: net_dev has to be master net_dev
|
|
|
|
*/
|
1996-01-22 05:08:06 +00:00
|
|
|
static void isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb,int proto)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
|
|
|
struct device *dev = &net_dev->dev;
|
1996-08-12 16:26:47 +00:00
|
|
|
struct ippp_struct *is = ippp_table[lp->ppp_slot];
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
if (proto < 0) { /* MP, oder normales Paket bei REJ_MP, MP Pakete gehen bei REJ zum pppd */
|
1996-01-22 05:08:06 +00:00
|
|
|
if (skb->data[0] & 0x01) { /* is it odd? */
|
|
|
|
proto = (unsigned char) skb->data[0];
|
|
|
|
skb_pull(skb,1); /* protocol ID is only 8 bit */
|
1996-01-09 04:13:18 +00:00
|
|
|
} else {
|
1996-01-22 05:08:06 +00:00
|
|
|
proto = ((int) (unsigned char) skb->data[0] << 8) + (unsigned char) skb->data[1];
|
|
|
|
skb_pull(skb,2);
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1996-10-22 09:39:49 +00:00
|
|
|
if(is->debug & 0x10) {
|
1996-05-06 11:34:57 +00:00
|
|
|
printk(KERN_DEBUG "push, skb %ld %04x\n",skb->len,proto);
|
1996-10-22 09:39:49 +00:00
|
|
|
isdn_ppp_frame_log("rpush",skb->data,skb->len,32);
|
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
switch (proto) {
|
1996-01-22 05:08:06 +00:00
|
|
|
case PPP_IPX: /* untested */
|
1996-08-12 16:26:47 +00:00
|
|
|
if(is->debug & 0x20)
|
1996-09-07 12:50:12 +00:00
|
|
|
printk(KERN_DEBUG "isdn_ppp: IPX\n");
|
1996-01-22 05:08:06 +00:00
|
|
|
skb->dev = dev;
|
|
|
|
skb->mac.raw = skb->data;
|
|
|
|
skb->protocol = htons(ETH_P_IPX);
|
|
|
|
break;
|
1996-01-09 04:13:18 +00:00
|
|
|
#ifdef CONFIG_ISDN_PPP_VJ
|
1996-01-22 05:08:06 +00:00
|
|
|
case PPP_VJC_UNCOMP:
|
1996-08-12 16:26:47 +00:00
|
|
|
if(is->debug & 0x20)
|
1996-07-01 19:47:24 +00:00
|
|
|
printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
|
1996-08-12 16:26:47 +00:00
|
|
|
if(slhc_remember(ippp_table[net_dev->local.ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
|
1996-07-01 19:47:24 +00:00
|
|
|
printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
|
|
|
|
net_dev->local.stats.rx_dropped++;
|
|
|
|
skb->free = 1;
|
|
|
|
dev_kfree_skb(skb,0 /* FREE_READ */ );
|
|
|
|
return;
|
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
#endif
|
|
|
|
case PPP_IP:
|
1996-08-12 16:26:47 +00:00
|
|
|
if(is->debug & 0x20)
|
1996-07-01 19:47:24 +00:00
|
|
|
printk(KERN_DEBUG "isdn_ppp: IP\n");
|
1996-01-22 05:08:06 +00:00
|
|
|
skb->dev = dev;
|
|
|
|
skb->mac.raw = skb->data;
|
|
|
|
skb->protocol = htons(ETH_P_IP);
|
1996-01-09 04:13:18 +00:00
|
|
|
break;
|
|
|
|
case PPP_VJC_COMP:
|
1996-08-12 16:26:47 +00:00
|
|
|
if(is->debug & 0x20)
|
1996-07-01 19:47:24 +00:00
|
|
|
printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n");
|
1996-01-09 04:13:18 +00:00
|
|
|
#ifdef CONFIG_ISDN_PPP_VJ
|
|
|
|
{
|
1996-01-22 05:08:06 +00:00
|
|
|
struct sk_buff *skb_old = skb;
|
|
|
|
int pkt_len;
|
|
|
|
skb = dev_alloc_skb(skb_old->len + 40);
|
|
|
|
|
1996-07-01 19:47:24 +00:00
|
|
|
skb_old->free = 1;
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
if (!skb) {
|
|
|
|
printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
|
|
|
|
net_dev->local.stats.rx_dropped++;
|
1996-07-01 19:47:24 +00:00
|
|
|
dev_kfree_skb(skb_old,0 /* FREE_READ */ );
|
1996-01-09 04:13:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
skb->dev = dev;
|
1996-01-22 05:08:06 +00:00
|
|
|
skb_put(skb,skb_old->len + 40);
|
|
|
|
memcpy(skb->data, skb_old->data, skb_old->len);
|
1996-01-09 04:13:18 +00:00
|
|
|
skb->mac.raw = skb->data;
|
1996-08-12 16:26:47 +00:00
|
|
|
pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_slot]->slcomp,
|
1996-01-22 05:08:06 +00:00
|
|
|
skb->data, skb_old->len);
|
1996-07-01 19:47:24 +00:00
|
|
|
dev_kfree_skb(skb_old,0 /* FREE_READ */ );
|
1996-06-11 14:50:29 +00:00
|
|
|
if(pkt_len < 0) {
|
1996-07-01 19:47:24 +00:00
|
|
|
skb->free = 1;
|
|
|
|
dev_kfree_skb(skb, 0 /* FREE_READ */ );
|
1996-06-11 14:50:29 +00:00
|
|
|
lp->stats.rx_dropped++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
skb_trim(skb, pkt_len);
|
1996-01-09 04:13:18 +00:00
|
|
|
skb->protocol = htons(ETH_P_IP);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
printk(KERN_INFO "isdn: Ooopsa .. VJ-Compression support not compiled into isdn driver.\n");
|
|
|
|
lp->stats.rx_dropped++;
|
1996-07-01 19:47:24 +00:00
|
|
|
skb->free = 1;
|
|
|
|
dev_kfree_skb(skb,0 /* FREE_READ */ );
|
1996-01-09 04:13:18 +00:00
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
default:
|
1996-08-12 16:26:47 +00:00
|
|
|
isdn_ppp_fill_rq(skb->data, skb->len,proto, lp->ppp_slot); /* push data to pppd device */
|
1996-07-01 19:47:24 +00:00
|
|
|
skb->free = 1;
|
|
|
|
dev_kfree_skb(skb,0 /* FREE_READ */ );
|
1996-01-09 04:13:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
netif_rx(skb);
|
1996-09-07 12:50:12 +00:00
|
|
|
/* net_dev->local.stats.rx_packets++; */ /* done in isdn_net.c */
|
1996-01-09 04:13:18 +00:00
|
|
|
/* Reset hangup-timer */
|
1996-10-22 09:39:49 +00:00
|
|
|
lp->huptimer = 0;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
1996-01-22 05:08:06 +00:00
|
|
|
/*
|
1996-05-06 11:34:57 +00:00
|
|
|
* send ppp frame .. we expect a PIDCOMPressable proto --
|
1996-01-22 05:08:06 +00:00
|
|
|
* (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
|
1996-07-01 19:47:24 +00:00
|
|
|
*
|
|
|
|
* VJ compression may change skb pointer!!! .. requeue with old
|
|
|
|
* skb isn't allowed!!
|
1996-01-22 05:08:06 +00:00
|
|
|
*/
|
1996-06-11 14:50:29 +00:00
|
|
|
|
1996-09-07 12:50:12 +00:00
|
|
|
static void isdn_ppp_skb_destructor(struct sk_buff *skb) /* debug function */
|
1996-08-12 16:26:47 +00:00
|
|
|
{
|
|
|
|
char outstr[80],*outpnt=outstr;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
*outpnt = 0;
|
|
|
|
for(i=0;i<24 && i<skb->len;i++) {
|
|
|
|
sprintf(outpnt,"%02x ",skb->data[i]);
|
|
|
|
outpnt += 3;
|
|
|
|
}
|
|
|
|
printk(KERN_DEBUG "ippp_dstrct: %s\n",outstr);
|
|
|
|
}
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
int isdn_ppp_xmit(struct sk_buff *skb, struct device *dev)
|
|
|
|
{
|
1996-05-06 11:34:57 +00:00
|
|
|
struct device *mdev = ((isdn_net_local *) (dev->priv) )->master; /* get master (for redundancy) */
|
|
|
|
isdn_net_local *lp,*mlp;
|
|
|
|
isdn_net_dev *nd;
|
1996-01-09 04:13:18 +00:00
|
|
|
int proto = PPP_IP; /* 0x21 */
|
1996-05-06 11:34:57 +00:00
|
|
|
struct ippp_struct *ipt,*ipts;
|
|
|
|
|
|
|
|
if(mdev)
|
|
|
|
mlp = (isdn_net_local *) (mdev->priv);
|
1996-10-22 09:39:49 +00:00
|
|
|
else {
|
|
|
|
mdev = dev;
|
1996-05-06 11:34:57 +00:00
|
|
|
mlp = (isdn_net_local *) (dev->priv);
|
1996-10-22 09:39:49 +00:00
|
|
|
}
|
1996-05-06 11:34:57 +00:00
|
|
|
nd = mlp->netdev; /* get master lp */
|
1996-08-12 16:26:47 +00:00
|
|
|
ipts = ippp_table[mlp->ppp_slot];
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-06-11 14:50:29 +00:00
|
|
|
if (!(ipts->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */
|
1996-10-22 09:39:49 +00:00
|
|
|
#ifdef ISDN_SYNCPPP_READDRESS
|
|
|
|
if(!ipts->old_pa_addr)
|
|
|
|
ipts->old_pa_addr = mdev->pa_addr;
|
|
|
|
if(!ipts->old_pa_dstaddr)
|
|
|
|
ipts->old_pa_dstaddr = mdev->pa_dstaddr;
|
|
|
|
#endif
|
1996-08-12 16:26:47 +00:00
|
|
|
if(ipts->debug & 0x1) {
|
|
|
|
printk(KERN_INFO "%s: IP frame delayed.\n",dev->name);
|
|
|
|
skb->destructor = isdn_ppp_skb_destructor;
|
|
|
|
}
|
|
|
|
return 1;
|
1996-10-22 09:39:49 +00:00
|
|
|
}
|
1996-06-11 14:50:29 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
skb->destructor = NULL;
|
|
|
|
|
1996-10-22 09:39:49 +00:00
|
|
|
switch(ntohs(skb->protocol)) {
|
|
|
|
case ETH_P_IP:
|
|
|
|
proto = PPP_IP;
|
|
|
|
#ifdef ISDN_SYNCPPP_READDRESS
|
|
|
|
if(ipts->old_pa_addr != mdev->pa_addr)
|
|
|
|
{
|
|
|
|
struct iphdr *ipfr;
|
|
|
|
ipfr = (struct iphdr *) skb->data;
|
|
|
|
printk(KERN_DEBUG "IF-address changed from %lx to %lx\n",ipts->old_pa_addr,mdev->pa_addr);
|
|
|
|
if(ipfr->version == 4) {
|
|
|
|
if(ipfr->saddr == ipts->old_pa_addr) {
|
|
|
|
printk(KERN_DEBUG "readdressing %lx to %lx\n",ipfr->saddr,mdev->pa_addr);
|
|
|
|
ipfr->saddr = mdev->pa_addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* dstaddr change not so improtant */
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case ETH_P_IPX:
|
|
|
|
proto = PPP_IPX; /* untested */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1996-06-11 14:50:29 +00:00
|
|
|
lp = nd->queue; /* get lp on top of queue */
|
1996-09-07 12:50:12 +00:00
|
|
|
|
1996-06-11 14:50:29 +00:00
|
|
|
if(lp->sav_skb) { /* find a non-busy device */
|
|
|
|
isdn_net_local *nlp = lp->next;
|
|
|
|
while(lp->sav_skb) {
|
|
|
|
if(lp == nlp)
|
|
|
|
return 1;
|
|
|
|
nlp = nd->queue = nd->queue->next;
|
|
|
|
}
|
|
|
|
lp = nlp;
|
|
|
|
}
|
1996-08-12 16:26:47 +00:00
|
|
|
ipt = ippp_table[lp->ppp_slot];
|
1996-06-11 14:50:29 +00:00
|
|
|
|
1996-05-06 11:34:57 +00:00
|
|
|
lp->huptimer = 0;
|
1996-02-19 15:25:50 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
/*
|
|
|
|
* after this line .. requeueing in the device queue is no longer allowed!!!
|
|
|
|
*/
|
1996-02-19 15:25:50 +00:00
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if(ipt->debug & 0x4)
|
1996-05-06 11:34:57 +00:00
|
|
|
printk(KERN_DEBUG "xmit skb, len %ld\n",skb->len);
|
1996-01-22 05:08:06 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_ISDN_PPP_VJ
|
1996-10-22 09:39:49 +00:00
|
|
|
if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes .. but this check again */
|
1996-07-01 19:47:24 +00:00
|
|
|
struct sk_buff *new_skb;
|
1996-08-12 16:26:47 +00:00
|
|
|
|
1996-07-01 19:47:24 +00:00
|
|
|
new_skb = dev_alloc_skb(skb->len);
|
|
|
|
if(new_skb) {
|
|
|
|
u_char *buf;
|
|
|
|
int pktlen;
|
|
|
|
|
|
|
|
new_skb->dev = skb->dev;
|
|
|
|
new_skb->free = 1;
|
|
|
|
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));
|
|
|
|
|
|
|
|
if(buf != skb->data) { /* copied to new buffer ??? (btw: WHY must slhc copy it?? *sigh*) */
|
|
|
|
if(new_skb->data != buf)
|
|
|
|
printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n");
|
|
|
|
dev_kfree_skb(skb,FREE_WRITE);
|
|
|
|
skb = new_skb;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
dev_kfree_skb(new_skb,0 /* FREE_WRITE */ );
|
|
|
|
}
|
|
|
|
|
|
|
|
skb_trim(skb,pktlen);
|
|
|
|
if (skb->data[0] & SL_TYPE_COMPRESSED_TCP) { /* cslip? style -> PPP */
|
|
|
|
proto = PPP_VJC_COMP;
|
|
|
|
skb->data[0] ^= SL_TYPE_COMPRESSED_TCP;
|
|
|
|
} else {
|
|
|
|
if (skb->data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
|
|
|
|
proto = PPP_VJC_UNCOMP;
|
|
|
|
skb->data[0] = (skb->data[0] & 0x0f) | 0x40;
|
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
}
|
1996-01-22 05:08:06 +00:00
|
|
|
#endif
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
if(ipt->debug & 0x24)
|
1996-05-06 11:34:57 +00:00
|
|
|
printk(KERN_DEBUG "xmit2 skb, len %ld, proto %04x\n",skb->len,proto);
|
1996-01-22 05:08:06 +00:00
|
|
|
|
1996-02-11 02:27:12 +00:00
|
|
|
#ifdef CONFIG_ISDN_MPP
|
1996-01-22 05:08:06 +00:00
|
|
|
if (ipt->mpppcfg & SC_MP_PROT) {
|
1996-01-09 04:13:18 +00:00
|
|
|
/* we get mp_seqno from static isdn_net_local */
|
|
|
|
long mp_seqno = ipts->mp_seqno;
|
|
|
|
ipts->mp_seqno++;
|
1996-02-11 02:27:12 +00:00
|
|
|
nd->queue = nd->queue->next;
|
1996-01-09 04:13:18 +00:00
|
|
|
if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) {
|
1996-08-12 16:26:47 +00:00
|
|
|
skb_push(skb, 3);
|
1996-01-09 04:13:18 +00:00
|
|
|
mp_seqno &= 0xfff;
|
1996-08-12 16:26:47 +00:00
|
|
|
skb->data[0] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8); /* (B)egin & (E)ndbit .. */
|
|
|
|
skb->data[1] = mp_seqno & 0xff;
|
|
|
|
skb->data[2] = proto; /* PID compression */
|
1996-01-09 04:13:18 +00:00
|
|
|
} else {
|
1996-08-12 16:26:47 +00:00
|
|
|
skb_push(skb, 5);
|
|
|
|
skb->data[0] = MP_BEGIN_FRAG | MP_END_FRAG; /* (B)egin & (E)ndbit .. */
|
|
|
|
skb->data[1] = (mp_seqno >> 16) & 0xff; /* sequence number: 24bit */
|
|
|
|
skb->data[2] = (mp_seqno >> 8) & 0xff;
|
|
|
|
skb->data[3] = (mp_seqno >> 0) & 0xff;
|
|
|
|
skb->data[4] = proto; /* PID compression */
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
1996-01-22 05:08:06 +00:00
|
|
|
proto = PPP_MP; /* MP Protocol, 0x003d */
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
1996-02-11 02:27:12 +00:00
|
|
|
#endif
|
1996-08-12 16:26:47 +00:00
|
|
|
skb_push(skb,4);
|
1996-01-22 05:08:06 +00:00
|
|
|
skb->data[0] = 0xff; /* All Stations */
|
1996-04-20 16:32:32 +00:00
|
|
|
skb->data[1] = 0x03; /* Unnumbered information */
|
1996-01-22 05:08:06 +00:00
|
|
|
skb->data[2] = proto >> 8;
|
|
|
|
skb->data[3] = proto & 0xff;
|
|
|
|
|
|
|
|
/* tx-stats are now updated via BSENT-callback */
|
1996-10-22 09:39:49 +00:00
|
|
|
|
|
|
|
if(ipts->debug & 0x40) {
|
|
|
|
printk(KERN_DEBUG "skb xmit: len: %ld\n",skb->len);
|
|
|
|
isdn_ppp_frame_log("xmit",skb->data,skb->len,32);
|
|
|
|
}
|
|
|
|
|
1996-06-11 14:50:29 +00:00
|
|
|
if(isdn_net_send_skb(dev , lp , skb)) {
|
|
|
|
if(lp->sav_skb) { /* whole sav_skb processing with disabled IRQs ?? */
|
1996-07-01 19:47:24 +00:00
|
|
|
printk(KERN_ERR "%s: whoops .. there is another stored skb!\n",dev->name);
|
1996-06-11 14:50:29 +00:00
|
|
|
dev_kfree_skb(skb,FREE_WRITE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
lp->sav_skb = skb;
|
|
|
|
}
|
|
|
|
return 0;
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
|
1996-05-06 11:34:57 +00:00
|
|
|
void isdn_ppp_free_sqqueue(isdn_net_dev * p)
|
|
|
|
{
|
|
|
|
struct sqqueue *q = p->ib.sq;
|
|
|
|
|
|
|
|
p->ib.sq = NULL;
|
|
|
|
while(q) {
|
|
|
|
struct sqqueue *qn = q->next;
|
1996-07-01 19:47:24 +00:00
|
|
|
if(q->skb) {
|
|
|
|
q->skb->free = 1;
|
|
|
|
dev_kfree_skb(q->skb,0 /* FREE_READ */ );
|
|
|
|
}
|
1996-05-06 11:34:57 +00:00
|
|
|
kfree(q);
|
|
|
|
q = qn;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
void isdn_ppp_free_mpqueue(isdn_net_dev * p)
|
|
|
|
{
|
|
|
|
struct mpqueue *ql, *q = p->mp_last;
|
|
|
|
while (q) {
|
|
|
|
ql = q->next;
|
1996-07-01 19:47:24 +00:00
|
|
|
q->skb->free = 1;
|
|
|
|
dev_kfree_skb(q->skb,0 /* FREE_READ */ );
|
1996-01-09 04:13:18 +00:00
|
|
|
kfree(q);
|
|
|
|
q = ql;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1996-02-11 02:27:12 +00:00
|
|
|
#ifdef CONFIG_ISDN_MPP
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
static int isdn_ppp_bundle(struct ippp_struct *is, int unit)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
|
|
|
char ifn[IFNAMSIZ + 1];
|
|
|
|
long flags;
|
|
|
|
isdn_net_dev *p;
|
1996-01-22 05:08:06 +00:00
|
|
|
isdn_net_local *lp,*nlp;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
sprintf(ifn, "ippp%d", unit);
|
|
|
|
p = isdn_net_findif(ifn);
|
|
|
|
if (!p)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
isdn_timer_ctrl(ISDN_TIMER_IPPP, 1); /* enable timer for ippp/MP */
|
|
|
|
|
|
|
|
save_flags(flags);
|
|
|
|
cli();
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
nlp = is->lp;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
lp = p->queue;
|
1996-02-11 02:27:12 +00:00
|
|
|
p->ib.bundled = 1;
|
1996-01-09 04:13:18 +00:00
|
|
|
nlp->last = lp->last;
|
|
|
|
lp->last->next = nlp;
|
|
|
|
lp->last = nlp;
|
|
|
|
nlp->next = lp;
|
|
|
|
p->queue = nlp;
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit;
|
1996-01-09 04:13:18 +00:00
|
|
|
/* maybe also SC_CCP stuff */
|
1996-08-12 16:26:47 +00:00
|
|
|
ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg &
|
1996-01-09 04:13:18 +00:00
|
|
|
(SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg &
|
1996-01-09 04:13:18 +00:00
|
|
|
(SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
|
|
|
|
#if 0
|
1996-08-12 16:26:47 +00:00
|
|
|
if (ippp_table[nlp->ppp_slot]->mpppcfg != ippp_table[lp->ppp_slot]->mpppcfg) {
|
1996-01-09 04:13:18 +00:00
|
|
|
printk(KERN_WARNING "isdn_ppp_bundle: different MP options %04x and %04x\n",
|
1996-08-12 16:26:47 +00:00
|
|
|
ippp_table[nlp->ppp_slot]->mpppcfg, ippp_table[lp->ppp_slot]->mpppcfg);
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
restore_flags(flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
|
|
|
|
{
|
|
|
|
struct mpqueue *q = dev->mp_last;
|
|
|
|
while (q) {
|
|
|
|
q->sqno &= mask;
|
|
|
|
q = q->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1996-10-22 09:39:49 +00:00
|
|
|
static int isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff ** skb, int BEbyte, long *sqnop, int min_sqno)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
|
|
|
struct mpqueue *qe, *q1, *q;
|
|
|
|
long cnt, flags;
|
|
|
|
int pktlen, sqno_end;
|
|
|
|
int sqno = *sqnop;
|
|
|
|
|
|
|
|
q1 = (struct mpqueue *) kmalloc(sizeof(struct mpqueue), GFP_KERNEL);
|
|
|
|
if (!q1) {
|
|
|
|
printk(KERN_WARNING "isdn_ppp_fill_mpqueue: Can't alloc struct memory.\n");
|
|
|
|
save_flags(flags);
|
|
|
|
cli();
|
1996-10-22 09:39:49 +00:00
|
|
|
isdn_ppp_cleanup_mpqueue(dev, min_sqno);
|
1996-01-09 04:13:18 +00:00
|
|
|
restore_flags(flags);
|
|
|
|
return -1;
|
|
|
|
}
|
1996-01-22 05:08:06 +00:00
|
|
|
q1->skb = *skb;
|
1996-01-09 04:13:18 +00:00
|
|
|
q1->sqno = sqno;
|
|
|
|
q1->BEbyte = BEbyte;
|
|
|
|
q1->time = jiffies;
|
|
|
|
|
|
|
|
save_flags(flags);
|
|
|
|
cli();
|
|
|
|
|
|
|
|
if (!(q = dev->mp_last)) {
|
|
|
|
dev->mp_last = q1;
|
|
|
|
q1->next = NULL;
|
|
|
|
q1->last = NULL;
|
1996-10-22 09:39:49 +00:00
|
|
|
isdn_ppp_cleanup_mpqueue(dev, min_sqno); /* not necessary */
|
1996-01-09 04:13:18 +00:00
|
|
|
restore_flags(flags);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
for (;;) { /* the faster way would be to step from the queue-end to the start */
|
|
|
|
if (sqno > q->sqno) {
|
|
|
|
if (q->next) {
|
|
|
|
q = q->next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
q->next = q1;
|
|
|
|
q1->next = NULL;
|
|
|
|
q1->last = q;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (sqno == q->sqno)
|
|
|
|
printk(KERN_WARNING "isdn_fill_mpqueue: illegal sqno received!!\n");
|
|
|
|
q1->last = q->last;
|
|
|
|
q1->next = q;
|
|
|
|
if (q->last) {
|
|
|
|
q->last->next = q1;
|
|
|
|
} else
|
|
|
|
dev->mp_last = q1;
|
|
|
|
q->last = q1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now we check whether we completed a packet with this fragment */
|
1996-01-22 05:08:06 +00:00
|
|
|
pktlen = -q1->skb->len;
|
1996-01-09 04:13:18 +00:00
|
|
|
q = q1;
|
|
|
|
cnt = q1->sqno;
|
|
|
|
while (!(q->BEbyte & MP_END_FRAG)) {
|
|
|
|
cnt++;
|
|
|
|
if (!(q->next) || q->next->sqno != cnt) {
|
1996-10-22 09:39:49 +00:00
|
|
|
isdn_ppp_cleanup_mpqueue(dev, min_sqno);
|
1996-01-09 04:13:18 +00:00
|
|
|
restore_flags(flags);
|
|
|
|
return -1;
|
|
|
|
}
|
1996-01-22 05:08:06 +00:00
|
|
|
pktlen += q->skb->len;
|
1996-01-09 04:13:18 +00:00
|
|
|
q = q->next;
|
|
|
|
}
|
1996-01-22 05:08:06 +00:00
|
|
|
pktlen += q->skb->len;
|
1996-01-09 04:13:18 +00:00
|
|
|
qe = q;
|
|
|
|
|
|
|
|
q = q1;
|
|
|
|
cnt = q1->sqno;
|
|
|
|
while (!(q->BEbyte & MP_BEGIN_FRAG)) {
|
|
|
|
cnt--;
|
|
|
|
if (!(q->last) || q->last->sqno != cnt) {
|
1996-10-22 09:39:49 +00:00
|
|
|
isdn_ppp_cleanup_mpqueue(dev, min_sqno);
|
1996-01-09 04:13:18 +00:00
|
|
|
restore_flags(flags);
|
|
|
|
return -1;
|
|
|
|
}
|
1996-01-22 05:08:06 +00:00
|
|
|
pktlen += q->skb->len;
|
1996-01-09 04:13:18 +00:00
|
|
|
q = q->last;
|
|
|
|
}
|
1996-01-22 05:08:06 +00:00
|
|
|
pktlen += q->skb->len;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
if (q->last)
|
|
|
|
q->last->next = qe->next;
|
|
|
|
else
|
|
|
|
dev->mp_last = qe->next;
|
|
|
|
|
|
|
|
if (qe->next)
|
|
|
|
qe->next->last = q->last;
|
|
|
|
qe->next = NULL;
|
|
|
|
sqno_end = qe->sqno;
|
|
|
|
*sqnop = q->sqno;
|
|
|
|
|
1996-10-22 09:39:49 +00:00
|
|
|
isdn_ppp_cleanup_mpqueue(dev, min_sqno);
|
1996-01-09 04:13:18 +00:00
|
|
|
restore_flags(flags);
|
|
|
|
|
1996-01-22 05:08:06 +00:00
|
|
|
*skb = dev_alloc_skb(pktlen + 40); /* not needed: +40 for VJ compression .. */
|
|
|
|
|
|
|
|
if (!(*skb)) {
|
1996-01-09 04:13:18 +00:00
|
|
|
while (q) {
|
|
|
|
struct mpqueue *ql = q->next;
|
1996-07-01 19:47:24 +00:00
|
|
|
q->skb->free = 1;
|
|
|
|
dev_kfree_skb(q->skb,0 /* FREE_READ */ );
|
1996-01-09 04:13:18 +00:00
|
|
|
kfree(q);
|
|
|
|
q = ql;
|
|
|
|
}
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
cnt = 0;
|
1996-01-22 05:08:06 +00:00
|
|
|
skb_put(*skb,pktlen);
|
1996-01-09 04:13:18 +00:00
|
|
|
while (q) {
|
|
|
|
struct mpqueue *ql = q->next;
|
1996-01-22 05:08:06 +00:00
|
|
|
memcpy((*skb)->data + cnt, q->skb->data, q->skb->len);
|
|
|
|
cnt += q->skb->len;
|
1996-07-01 19:47:24 +00:00
|
|
|
q->skb->free = 1;
|
|
|
|
dev_kfree_skb(q->skb,0 /* FREE_READ */ );
|
1996-01-09 04:13:18 +00:00
|
|
|
kfree(q);
|
|
|
|
q = ql;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sqno_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* remove stale packets from list
|
|
|
|
*/
|
|
|
|
|
1996-10-22 09:39:49 +00:00
|
|
|
static void isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min_sqno)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
1996-06-11 14:50:29 +00:00
|
|
|
#ifdef CONFIG_ISDN_PPP_VJ
|
|
|
|
int toss = 0;
|
|
|
|
#endif
|
1996-01-09 04:13:18 +00:00
|
|
|
/* z.z einfaches aussortieren gammeliger pakete. Fuer die Zukunft:
|
|
|
|
eventuell, solange vorne kein B-paket ist und sqno<=min_sqno: auch rauswerfen
|
|
|
|
wenn sqno<min_sqno und Luecken vorhanden sind: auch weg (die koennen nicht mehr gefuellt werden)
|
|
|
|
bei paketen groesser min_sqno: ueber mp_mrru: wenn summe ueber pktlen der rumhaengenden Pakete
|
|
|
|
groesser als mrru ist: raus damit , Pakete muessen allerdings zusammenhaengen sonst koennte
|
|
|
|
ja ein Paket mit B und eins mit E dazwischenpassen */
|
|
|
|
|
|
|
|
struct mpqueue *ql, *q = dev->mp_last;
|
|
|
|
while (q) {
|
|
|
|
if (q->sqno < min_sqno) {
|
|
|
|
if (q->BEbyte & MP_END_FRAG) {
|
|
|
|
printk(KERN_DEBUG "ippp: freeing stale packet!\n");
|
|
|
|
if ((dev->mp_last = q->next))
|
|
|
|
q->next->last = NULL;
|
|
|
|
while (q) {
|
|
|
|
ql = q->last;
|
1996-07-01 19:47:24 +00:00
|
|
|
q->skb->free = 1;
|
|
|
|
dev_kfree_skb(q->skb,0 /* FREE_READ */ );
|
1996-01-09 04:13:18 +00:00
|
|
|
kfree(q);
|
1996-06-11 14:50:29 +00:00
|
|
|
#ifdef CONFIG_ISDN_PPP_VJ
|
|
|
|
toss = 1;
|
|
|
|
#endif
|
1996-01-09 04:13:18 +00:00
|
|
|
q = ql;
|
|
|
|
}
|
|
|
|
q = dev->mp_last;
|
|
|
|
} else
|
|
|
|
q = q->next;
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
1996-06-11 14:50:29 +00:00
|
|
|
#ifdef CONFIG_ISDN_PPP_VJ
|
|
|
|
/* did we free a stale frame ? */
|
|
|
|
if(toss)
|
1996-08-12 16:26:47 +00:00
|
|
|
slhc_toss(ippp_table[dev->local.ppp_slot]->slcomp);
|
1996-06-11 14:50:29 +00:00
|
|
|
#endif
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* a buffered packet timed-out?
|
|
|
|
*/
|
|
|
|
|
1996-02-11 02:27:12 +00:00
|
|
|
#endif
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
void isdn_ppp_timer_timeout(void)
|
|
|
|
{
|
1996-02-11 02:27:12 +00:00
|
|
|
#ifdef CONFIG_ISDN_MPP
|
1996-01-09 04:13:18 +00:00
|
|
|
isdn_net_dev *net_dev = dev->netdev;
|
|
|
|
struct sqqueue *q, *ql = NULL, *qn;
|
|
|
|
|
|
|
|
while (net_dev) {
|
|
|
|
isdn_net_local *lp = &net_dev->local;
|
1996-06-11 14:50:29 +00:00
|
|
|
if (net_dev->ib.modify || lp->master) { /* interface locked or slave?*/
|
1996-10-22 09:39:49 +00:00
|
|
|
net_dev = net_dev->next;
|
1996-01-09 04:13:18 +00:00
|
|
|
continue;
|
1996-10-22 09:39:49 +00:00
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
|
|
|
|
q = net_dev->ib.sq;
|
|
|
|
while (q) {
|
|
|
|
if (q->sqno_start == net_dev->ib.next_num || q->timer < jiffies) {
|
1996-06-11 14:50:29 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_ISDN_PPP_VJ
|
|
|
|
/* did we step over a missing frame ? */
|
|
|
|
if(q->sqno_start != net_dev->ib.next_num)
|
1996-08-12 16:26:47 +00:00
|
|
|
slhc_toss(ippp_table[lp->ppp_slot]->slcomp);
|
1996-06-11 14:50:29 +00:00
|
|
|
#endif
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
ql = net_dev->ib.sq;
|
|
|
|
net_dev->ib.sq = q->next;
|
|
|
|
net_dev->ib.next_num = q->sqno_end + 1;
|
|
|
|
q->next = NULL;
|
|
|
|
for (; ql;) {
|
1996-01-22 05:08:06 +00:00
|
|
|
isdn_ppp_push_higher(net_dev, lp, ql->skb, -1);
|
1996-01-09 04:13:18 +00:00
|
|
|
qn = ql->next;
|
|
|
|
kfree(ql);
|
|
|
|
ql = qn;
|
|
|
|
}
|
|
|
|
q = net_dev->ib.sq;
|
|
|
|
} else
|
|
|
|
q = q->next;
|
|
|
|
}
|
|
|
|
net_dev = net_dev->next;
|
|
|
|
}
|
1996-02-11 02:27:12 +00:00
|
|
|
#endif
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
|
1996-06-11 14:50:29 +00:00
|
|
|
/*
|
|
|
|
* network device ioctl handlers
|
|
|
|
*/
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
static int isdn_ppp_dev_ioctl_stats(int slot,struct ifreq *ifr,struct device *dev)
|
1996-06-11 14:50:29 +00:00
|
|
|
{
|
|
|
|
struct ppp_stats *res, t;
|
|
|
|
isdn_net_local *lp = (isdn_net_local *) dev->priv;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
|
|
|
|
err = verify_area (VERIFY_WRITE, res,sizeof(struct ppp_stats));
|
|
|
|
|
|
|
|
if(err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* build a temporary stat struct and copy it to user space */
|
|
|
|
|
|
|
|
memset (&t, 0, sizeof(struct ppp_stats));
|
|
|
|
if(dev->flags & IFF_UP) {
|
|
|
|
t.p.ppp_ipackets = lp->stats.rx_packets;
|
|
|
|
t.p.ppp_ierrors = lp->stats.rx_errors;
|
|
|
|
t.p.ppp_opackets = lp->stats.tx_packets;
|
|
|
|
t.p.ppp_oerrors = lp->stats.tx_errors;
|
|
|
|
#ifdef CONFIG_ISDN_PPP_VJ
|
1996-08-12 16:26:47 +00:00
|
|
|
if(slot >= 0 && ippp_table[slot]->slcomp) {
|
|
|
|
struct slcompress *slcomp = ippp_table[slot]->slcomp;
|
1996-06-11 14:50:29 +00:00
|
|
|
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;
|
|
|
|
t.vj.vjs_misses = slcomp->sls_o_misses;
|
|
|
|
t.vj.vjs_errorin = slcomp->sls_i_error;
|
|
|
|
t.vj.vjs_tossed = slcomp->sls_i_tossed;
|
|
|
|
t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed;
|
|
|
|
t.vj.vjs_compressedin = slcomp->sls_i_compressed;
|
|
|
|
}
|
|
|
|
#endif
|
1996-06-24 17:42:03 +00:00
|
|
|
}
|
1996-10-22 23:14:19 +00:00
|
|
|
copy_to_user (res, &t, sizeof (struct ppp_stats));
|
1996-06-11 14:50:29 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
char *r;
|
|
|
|
int len;
|
|
|
|
isdn_net_local *lp = (isdn_net_local *) dev->priv;
|
|
|
|
|
1996-07-01 19:47:24 +00:00
|
|
|
#if 0
|
1996-08-12 16:26:47 +00:00
|
|
|
printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n",cmd,lp->ppp_slot);
|
1996-06-11 14:50:29 +00:00
|
|
|
#endif
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
switch (cmd) {
|
1996-06-11 14:50:29 +00:00
|
|
|
case SIOCGPPPVER:
|
|
|
|
r = (char *) ifr->ifr_ifru.ifru_data;
|
|
|
|
len = strlen(PPP_VERSION) + 1;
|
|
|
|
error = verify_area(VERIFY_WRITE, r, len);
|
|
|
|
if (!error)
|
1996-10-22 23:14:19 +00:00
|
|
|
copy_to_user(r, PPP_VERSION, len);
|
1996-06-11 14:50:29 +00:00
|
|
|
break;
|
|
|
|
case SIOCGPPPSTATS:
|
1996-08-12 16:26:47 +00:00
|
|
|
error = isdn_ppp_dev_ioctl_stats (lp->ppp_slot, ifr, dev);
|
1996-06-11 14:50:29 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error = -EINVAL;
|
|
|
|
break;
|
1996-01-09 04:13:18 +00:00
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
1996-08-12 16:26:47 +00:00
|
|
|
static int isdn_ppp_if_get_unit(char *name)
|
1996-01-09 04:13:18 +00:00
|
|
|
{
|
|
|
|
int len, i, unit = 0, deci;
|
|
|
|
|
|
|
|
len = strlen(name);
|
1996-08-12 16:26:47 +00:00
|
|
|
|
|
|
|
if(strncmp("ippp",name,4) || len > 8)
|
|
|
|
return -1;
|
|
|
|
|
1996-01-09 04:13:18 +00:00
|
|
|
for (i = 0, deci = 1; i < len; i++, deci *= 10) {
|
1996-08-12 16:26:47 +00:00
|
|
|
char a = name[len-i-1];
|
|
|
|
if (a >= '0' && a <= '9')
|
|
|
|
unit += (a - '0') * deci;
|
1996-01-09 04:13:18 +00:00
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
1996-08-12 16:26:47 +00:00
|
|
|
if (!i || len-i != 4)
|
1996-01-09 04:13:18 +00:00
|
|
|
unit = -1;
|
|
|
|
|
|
|
|
return unit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1996-01-22 05:08:06 +00:00
|
|
|
int isdn_ppp_dial_slave(char *name)
|
|
|
|
{
|
1996-02-11 02:27:12 +00:00
|
|
|
#ifdef CONFIG_ISDN_MPP
|
1996-01-22 05:08:06 +00:00
|
|
|
isdn_net_dev *ndev;
|
|
|
|
isdn_net_local *lp;
|
|
|
|
struct device *sdev;
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-01-22 05:08:06 +00:00
|
|
|
if(!(ndev = isdn_net_findif(name)))
|
|
|
|
return 1;
|
|
|
|
lp = &ndev->local;
|
|
|
|
if(!(lp->flags & ISDN_NET_CONNECTED))
|
|
|
|
return 5;
|
|
|
|
|
|
|
|
sdev = lp->slave;
|
|
|
|
while(sdev)
|
|
|
|
{
|
|
|
|
isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
|
|
|
|
if(!(mlp->flags & ISDN_NET_CONNECTED))
|
|
|
|
break;
|
|
|
|
sdev = mlp->slave;
|
|
|
|
}
|
|
|
|
if(!sdev)
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
isdn_net_force_dial_lp((isdn_net_local *) sdev->priv);
|
|
|
|
return 0;
|
1996-02-11 02:27:12 +00:00
|
|
|
#else
|
|
|
|
return -1;
|
|
|
|
#endif
|
1996-01-22 05:08:06 +00:00
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
|
1996-05-06 11:34:57 +00:00
|
|
|
int isdn_ppp_hangup_slave(char *name)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_ISDN_MPP
|
|
|
|
isdn_net_dev *ndev;
|
|
|
|
isdn_net_local *lp;
|
|
|
|
struct device *sdev;
|
|
|
|
|
|
|
|
if(!(ndev = isdn_net_findif(name)))
|
|
|
|
return 1;
|
|
|
|
lp = &ndev->local;
|
|
|
|
if(!(lp->flags & ISDN_NET_CONNECTED))
|
|
|
|
return 5;
|
|
|
|
|
|
|
|
sdev = lp->slave;
|
|
|
|
while(sdev)
|
|
|
|
{
|
|
|
|
isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
|
|
|
|
if((mlp->flags & ISDN_NET_CONNECTED))
|
|
|
|
break;
|
|
|
|
sdev = mlp->slave;
|
|
|
|
}
|
|
|
|
if(!sdev)
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
isdn_net_hangup(sdev);
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
1996-01-09 04:13:18 +00:00
|
|
|
|