Commit d6b03063 authored by Kai Germaschewski's avatar Kai Germaschewski

ISDN/PPP: cosmetics

No (well, hardly any ;) code changes, only moving all /dev/ipppX related
code next to each other and some indenting changes.
parent a0097af3
...@@ -21,6 +21,38 @@ ...@@ -21,6 +21,38 @@
#include "isdn_ppp_ccp.h" #include "isdn_ppp_ccp.h"
#include "isdn_net.h" #include "isdn_net.h"
static void
isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
struct sk_buff *skb);
static int
isdn_ppp_set_compressor(struct ipppd *is,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.
*/
#define IPPPD_DEBUG
#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
/* ipppd::flags */ /* ipppd::flags */
enum { enum {
IPPPD_FL_HUP = 0x01, IPPPD_FL_HUP = 0x01,
...@@ -47,73 +79,7 @@ struct ipppd { ...@@ -47,73 +79,7 @@ struct ipppd {
atomic_t refcnt; atomic_t refcnt;
}; };
#define IPPPD_DEBUG /* ====================================================================== */
#ifdef IPPPD_DEBUG
#define ipppd_debug(i, fmt, arg...) \
printk(KERN_DEBUG "ipppd %p state %#x %s:" fmt "\n", (i), (i)->state, \
__FUNCTION__ , ## arg)
#else
#define ipppd_debug(...) do { } while (0)
#endif
/* 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.
*/
/* 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 int isdn_ppp_set_compressor(struct ipppd *is,struct isdn_ppp_comp_data *);
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_dev_alloc_skb(void *priv, int len, int gfp_mask);
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);
static void
isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
struct sk_buff *skb);
/* New CCP stuff */
static void
isdn_ppp_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 $";
static spinlock_t ipppds_lock = SPIN_LOCK_UNLOCKED; static spinlock_t ipppds_lock = SPIN_LOCK_UNLOCKED;
static LIST_HEAD(ipppds); static LIST_HEAD(ipppds);
...@@ -128,7 +94,7 @@ ipppd_destroy(struct ipppd *ipppd) ...@@ -128,7 +94,7 @@ ipppd_destroy(struct ipppd *ipppd)
} }
static inline struct ipppd * static inline struct ipppd *
__ipppd_get(struct ipppd *ipppd) ipppd_get(struct ipppd *ipppd)
{ {
atomic_inc(&ipppd->refcnt); atomic_inc(&ipppd->refcnt);
printk("%s: %d\n", __FUNCTION__, atomic_read(&ipppd->refcnt)); printk("%s: %d\n", __FUNCTION__, atomic_read(&ipppd->refcnt));
...@@ -144,293 +110,230 @@ ipppd_put(struct ipppd *ipppd) ...@@ -144,293 +110,230 @@ ipppd_put(struct ipppd *ipppd)
ipppd_destroy(ipppd); ipppd_destroy(ipppd);
} }
/* /* ====================================================================== */
* frame log (debug) /* char dev ops */
*/
void /* --- open ------------------------------------------------------------- */
isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot)
static int
ipppd_open(struct inode *ino, struct file *file)
{ {
int cnt, unsigned long flags;
j, unsigned int minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
i; struct ipppd *ipppd;
char buf[80];
if (len < maxlen) ipppd = kmalloc(sizeof(*ipppd), GFP_KERNEL);
maxlen = len; if (!ipppd)
return -ENOMEM;
for (i = 0, cnt = 0; cnt < maxlen; i++) { memset(ipppd, 0, sizeof(*ipppd));
for (j = 0; j < 16 && cnt < maxlen; j++, cnt++) atomic_set(&ipppd->refcnt, 0);
sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]);
printk(KERN_DEBUG "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf); /* 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);
return 0;
} }
/* --- release --------------------------------------------------------- */
static void static int
isdn_ppp_push_header(isdn_net_dev *idev, struct sk_buff *skb, u16 proto) ipppd_release(struct inode *ino, struct file *file)
{ {
unsigned char *p; unsigned long flags;
struct ipppd *ipppd = file->private_data;
if (skb_headroom(skb) < 4) { ipppd_debug(ipppd, "");
isdn_BUG();
return;
}
if ((idev->pppcfg & SC_COMP_PROT) && proto <= 0xff) if (ipppd->state == IPPPD_ST_CONNECTED)
put_u8(skb_push(skb, 1), proto); isdn_net_hangup(ipppd->idev);
else
put_u16(skb_push(skb, 2), proto);
if (idev->pppcfg & SC_COMP_AC) spin_lock_irqsave(&ipppds, flags);
return; list_del(&ipppd->ipppds);
spin_unlock_irqrestore(&ipppds, flags);
p = skb_push(skb, 2); ipppd_put(ipppd);
p += put_u8(p, PPP_ALLSTATIONS);
p += put_u8(p, PPP_UI); return 0;
} }
/* /* --- read ------------------------------------------------------------- */
* unbind isdn_net_local <=> ippp-device
* note: it can happen, that we hangup/free the master before the slaves /* read() is always non blocking */
* in this case we bind another lp to the master device static ssize_t
*/ ipppd_read(struct file *file, char *buf, size_t count, loff_t *off)
static void
isdn_ppp_unbind(isdn_net_dev *idev)
{ {
struct ipppd *is = idev->ipppd; struct ipppd *is;
struct sk_buff *skb;
int retval;
if (!is) { if (off != &file->f_pos)
isdn_BUG(); return -ESPIPE;
return;
is = file->private_data;
skb = skb_dequeue(&is->rq);
if (!skb) {
retval = -EAGAIN;
goto out;
}
if (skb->len > count) {
retval = -EMSGSIZE;
goto out_free;
}
if (copy_to_user(buf, skb->data, skb->len)) {
retval = -EFAULT;
goto out_free;
} }
retval = skb->len;
ipppd_debug(is, ""); out_free:
dev_kfree_skb(skb);
out:
return retval;
}
if (is->state != IPPPD_ST_ASSIGNED) /* --- write ------------------------------------------------------------ */
isdn_BUG();
is->state = IPPPD_ST_OPEN; /* write() is always non blocking */
static ssize_t
ipppd_write(struct file *file, const char *buf, size_t count, loff_t *off)
{
isdn_net_dev *idev;
struct ipppd *is;
int proto;
unsigned char protobuf[4];
int retval;
/* is->idev will be invalid shortly */ if (off != &file->f_pos)
ippp_ccp_free(idev->ccp); return -ESPIPE;
is->idev = NULL; lock_kernel();
/* lose the reference we took on isdn_ppp_bind */
ipppd_put(is);
idev->ipppd = NULL;
return; is = file->private_data;
}
/* ipppd_debug(is, "");
* 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) { if (is->state != IPPPD_ST_CONNECTED) {
isdn_BUG(); retval = -ENOTCONN;
return 0; goto out;
} }
spin_lock_irqsave(&ipppds_lock, flags); /* -> push it directly to the lowlevel interface */
if (idev->pppbind < 0) { /* device bound to ippp device ? */
struct list_head *l; idev = is->idev;
char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */ if (!idev)
memset(exclusive, 0, ISDN_MAX_CHANNELS); printk(KERN_DEBUG "isdn_ppp_write: idev == NULL\n");
/* step through net devices to find exclusive minors */ else {
list_for_each(l, &isdn_net_devs) { /*
isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list); * Don't reset huptimer for
if (p->pppbind >= 0 && p->pppbind < ISDN_MAX_CHANNELS) * LCP packets. (Echo requests).
exclusive[p->pppbind] = 1;
}
/*
* search a free device / slot
*/ */
list_for_each_entry(ipppd, &ipppds, ipppds) { if (copy_from_user(protobuf, buf, 4)) {
if (!ipppd) retval = -EFAULT;
continue; goto out;
if (ipppd->state != IPPPD_ST_OPEN)
continue;
if (!exclusive[ipppd->minor])
break;
goto found;
} }
} else { proto = PPP_PROTOCOL(protobuf);
list_for_each_entry(ipppd, &ipppds, ipppds) { if (proto != PPP_LCP)
if (!ipppd) idev->huptimer = 0;
continue;
if (ipppd->state != IPPPD_ST_OPEN) if (idev->isdn_slot < 0) {
continue; retval = 0;
if (ipppd->minor == idev->pppbind) goto out;
goto found;
} }
if ((dev->drv[isdn_slot_driver(idev->isdn_slot)]->flags & DRV_FLAG_RUNNING)) {
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);
printk(KERN_INFO "isdn_ppp_bind: no ipppd\n"); if (copy_from_user(skb_put(skb, count), buf, count))
retval = -ESRCH; {
goto err; kfree_skb(skb);
retval = -EFAULT;
found: goto out;
unit = isdn_ppp_if_get_unit(idev->name); /* get unit number from interface name .. ugly! */ }
if (unit < 0) { if (is->debug & 0x40) {
printk(KERN_INFO "isdn_ppp_bind: illegal interface name %s.\n", idev->name); printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
retval = -ENODEV; isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,-1);
goto err;
} }
ipppd->unit = unit; /* keeps CCP/compression states in sync */
ipppd->state = IPPPD_ST_ASSIGNED; isdn_ppp_send_ccp(idev,idev->mlp,skb);
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(); /* FIXME: Somewhere we need protection against the
if (!idev->ccp) { * queue growing too large */
retval = -ENOMEM; isdn_net_write_super(idev, skb);
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:
if (retval) {
idev->ipppd->state = IPPPD_ST_OPEN;
ipppd_put(idev->ipppd);
idev->ipppd = NULL;
} }
retval = count;
return retval; out:
unlock_kernel();
err:
spin_unlock_irqrestore(&ipppds_lock, flags);
return retval; return retval;
} }
/* /* --- poll ------------------------------------------------------------- */
* kick the ipppd on the device
* (wakes up daemon after B-channel connect)
*/
static void
isdn_ppp_connected(isdn_net_dev *idev)
{
struct ipppd *ipppd = idev->ipppd;
ipppd_debug(ipppd, "");
ipppd->state = IPPPD_ST_CONNECTED;
ipppd->flags |= IPPPD_FL_WAKEUP;
wake_up(&ipppd->wq);
}
static void static unsigned int
isdn_ppp_disconnected(isdn_net_dev *idev) ipppd_poll(struct file *file, poll_table * wait)
{ {
struct ipppd *ipppd = idev->ipppd; unsigned int mask;
struct ipppd *is;
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; is = file->private_data;
ipppd->flags |= IPPPD_FL_HUP;
wake_up(&ipppd->wq);
#ifdef CONFIG_ISDN_MPP ipppd_debug(is, "");
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--; /* just registers wait_queue hook. This doesn't really wait. */
spin_unlock(&lp->netdev->pb->lock); poll_wait(file, &is->wq, wait);
#endif /* CONFIG_ISDN_MPP */
} if (is->flags & IPPPD_FL_HUP) {
mask = POLLHUP;
goto out;
}
/* we're always ready to send .. */
mask = POLLOUT | POLLWRNORM;
/* /*
* ipppd_open * if IPPP_FL_WAKEUP is set we return even if we have nothing to read
*/ */
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);
}
static int out:
ipppd_open(struct inode *ino, struct file *file) return mask;
{
unsigned long flags;
unsigned int minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
struct ipppd *ipppd;
ipppd = kmalloc(sizeof(*ipppd), GFP_KERNEL);
if (!ipppd)
return -ENOMEM;
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);
return 0;
} }
/* /* --- ioctl ------------------------------------------------------------ */
* release ippp device
*/
static int
ipppd_release(struct inode *ino, struct file *file)
{
unsigned long flags;
struct ipppd *ipppd = file->private_data;
ipppd_debug(ipppd, "");
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;
}
/* /* get_arg .. ioctl helper */
* get_arg .. ioctl helper
*/
static int static int
get_arg(void *b, void *val, int len) get_arg(void *b, void *val, int len)
{ {
...@@ -441,9 +344,7 @@ get_arg(void *b, void *val, int len) ...@@ -441,9 +344,7 @@ get_arg(void *b, void *val, int len)
return 0; return 0;
} }
/* /* set arg .. ioctl helper */
* set arg .. ioctl helper
*/
static int static int
set_arg(void *b, void *val,int len) set_arg(void *b, void *val,int len)
{ {
...@@ -452,11 +353,9 @@ set_arg(void *b, void *val,int len) ...@@ -452,11 +353,9 @@ set_arg(void *b, void *val,int len)
return 0; return 0;
} }
/*
* ippp device ioctl
*/
static int static int
ipppd_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; isdn_net_dev *idev;
unsigned long val; unsigned long val;
...@@ -465,11 +364,10 @@ ipppd_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned lon ...@@ -465,11 +364,10 @@ ipppd_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned lon
struct isdn_ppp_comp_data data; struct isdn_ppp_comp_data data;
unsigned int cfg; unsigned int cfg;
is = (struct ipppd *) file->private_data; is = file->private_data;
idev = is->idev; idev = is->idev;
if (is->debug & 0x1) ipppd_debug(is, "cmd %#x", cmd);
printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", is->minor, cmd, is->state);
switch (cmd) { switch (cmd) {
case PPPIOCBUNDLE: case PPPIOCBUNDLE:
...@@ -629,50 +527,30 @@ ipppd_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned lon ...@@ -629,50 +527,30 @@ ipppd_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned lon
return 0; return 0;
} }
static unsigned int /* --- fops ------------------------------------------------------------- */
ipppd_poll(struct file *file, poll_table * wait)
struct file_operations isdn_ppp_fops =
{ {
unsigned int mask; .owner = THIS_MODULE,
struct ipppd *is; .llseek = no_llseek,
.read = ipppd_read,
.write = ipppd_write,
.poll = ipppd_poll,
.ioctl = ipppd_ioctl,
.open = ipppd_open,
.release = ipppd_release,
};
is = file->private_data; /* --- ipppd_queue_read ------------------------------------------------- */
ipppd_debug(is, ""); /* Queue packets for ipppd to read(). */
/* just registers wait_queue hook. This doesn't really wait. */ static int
poll_wait(file, &is->wq, wait); ipppd_queue_read(struct ipppd *is, u16 proto, unsigned char *buf, int len)
{
if (is->flags & IPPPD_FL_HUP) { struct sk_buff *skb;
mask = POLLHUP; unsigned char *p;
goto out; int retval;
}
/* we're always ready to send .. */
mask = POLLOUT | POLLWRNORM;
/*
* if IPPP_FL_WAKEUP is set we return even if we have nothing to read
*/
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);
}
out:
return mask;
}
/*
* Queue packets for ipppd to read().
*/
static int
ipppd_queue_read(struct ipppd *is, u16 proto, unsigned char *buf, int len)
{
struct sk_buff *skb;
unsigned char *p;
int retval;
if (is->state != IPPPD_ST_CONNECTED) { if (is->state != IPPPD_ST_CONNECTED) {
printk(KERN_DEBUG "ippp: device not connected.\n"); printk(KERN_DEBUG "ippp: device not connected.\n");
...@@ -704,144 +582,274 @@ ipppd_queue_read(struct ipppd *is, u16 proto, unsigned char *buf, int len) ...@@ -704,144 +582,274 @@ ipppd_queue_read(struct ipppd *is, u16 proto, unsigned char *buf, int len)
return retval; 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_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp,
struct sk_buff *skb,int proto);
static struct sk_buff *
isdn_ppp_dev_alloc_skb(void *priv, int len, int gfp_mask);
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() * frame log (debug)
* reports, that there is data
*/ */
void
static ssize_t isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot)
ipppd_read(struct file *file, char *buf, size_t count, loff_t *off)
{ {
struct ipppd *is; int cnt,
struct sk_buff *skb; j,
int retval; i;
char buf[80];
if (off != &file->f_pos)
return -ESPIPE;
is = file->private_data; if (len < maxlen)
maxlen = len;
skb = skb_dequeue(&is->rq); for (i = 0, cnt = 0; cnt < maxlen; i++) {
if (!skb) { for (j = 0; j < 16 && cnt < maxlen; j++, cnt++)
retval = -EAGAIN; sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]);
goto out; printk(KERN_DEBUG "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf);
}
if (skb->len > count) {
retval = -EMSGSIZE;
goto out_free;
} }
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: if ((idev->pppcfg & SC_COMP_PROT) && proto <= 0xff)
dev_kfree_skb(skb); put_u8(skb_push(skb, 1), proto);
out: else
return retval; 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 void
static ssize_t isdn_ppp_unbind(isdn_net_dev *idev)
ipppd_write(struct file *file, const char *buf, size_t count, loff_t *off)
{ {
isdn_net_dev *idev; struct ipppd *is = idev->ipppd;
struct ipppd *is;
int proto;
unsigned char protobuf[4];
int retval;
if (off != &file->f_pos) if (!is) {
return -ESPIPE; isdn_BUG();
return;
}
lock_kernel(); ipppd_debug(is, "");
is = file->private_data; if (is->state != IPPPD_ST_ASSIGNED)
isdn_BUG();
ipppd_debug(is, ""); is->state = IPPPD_ST_OPEN;
if (is->state != IPPPD_ST_CONNECTED) { /* is->idev will be invalid shortly */
HERE; ippp_ccp_free(idev->ccp);
retval = -ENOTCONN;
goto out;
}
/* -> 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; return;
if (!idev) }
printk(KERN_DEBUG "isdn_ppp_write: idev == NULL\n");
else { /*
/* * bind isdn_net_local <=> ippp-device
* Don't reset huptimer for
* LCP packets. (Echo requests).
*/ */
if (copy_from_user(protobuf, buf, 4)) { int
retval = -EFAULT; isdn_ppp_bind(isdn_net_dev *idev)
goto out; {
int unit = 0;
unsigned long flags;
int retval = 0;
struct ipppd *ipppd;
if (idev->ipppd) {
isdn_BUG();
return 0;
} }
proto = PPP_PROTOCOL(protobuf);
if (proto != PPP_LCP)
idev->huptimer = 0;
if (idev->isdn_slot < 0) { spin_lock_irqsave(&ipppds_lock, flags);
retval = 0; if (idev->pppbind < 0) { /* device bound to ippp device ? */
goto out; 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;
} }
if ((dev->drv[isdn_slot_driver(idev->isdn_slot)]->flags & DRV_FLAG_RUNNING)) {
unsigned short hl;
struct sk_buff *skb;
/* /*
* we need to reserve enought space in front of * search a free device / slot
* 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); list_for_each_entry(ipppd, &ipppds, ipppds) {
skb = alloc_skb(hl+count, GFP_ATOMIC); if (!ipppd)
if (!skb) { continue;
printk(KERN_WARNING "isdn_ppp_write: out of memory!\n"); if (ipppd->state != IPPPD_ST_OPEN)
retval = count; continue;
goto out; if (!exclusive[ipppd->minor])
break;
goto found;
} }
skb_reserve(skb, hl); } else {
if (copy_from_user(skb_put(skb, count), buf, count)) list_for_each_entry(ipppd, &ipppds, ipppds) {
{ if (!ipppd)
kfree_skb(skb); continue;
retval = -EFAULT; if (ipppd->state != IPPPD_ST_OPEN)
goto out; continue;
if (ipppd->minor == idev->pppbind)
goto found;
} }
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,-1);
} }
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;
} }
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;
} }
retval = count; 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: 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; 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, struct ipppd *ipppd = idev->ipppd;
.llseek = no_llseek,
.read = ipppd_read, ipppd_debug(ipppd, "");
.write = ipppd_write,
.poll = ipppd_poll, ipppd->state = IPPPD_ST_CONNECTED;
.ioctl = ipppd_ioctl, ipppd->flags |= IPPPD_FL_WAKEUP;
.open = ipppd_open, wake_up(&ipppd->wq);
.release = ipppd_release, }
};
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. * init memory, structures etc.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment