* PPP driver, written by Michael Callahan and Al Longyear, and
* subsequently hacked by Paul Mackerras.
*
- * ==FILEVERSION 20020125==
+ * ==FILEVERSION 20040616==
*/
#include <linux/module.h>
struct sk_buff *tpkt;
unsigned long last_xmit;
- struct sk_buff *rpkt;
+ struct sk_buff_head rqueue;
+
+ struct tasklet_struct tsk;
atomic_t refcnt;
struct semaphore dead_sem;
static int ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb);
static int ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd,
unsigned long arg);
+static void ppp_sync_process(unsigned long arg);
static int ppp_sync_push(struct syncppp *ap);
static void ppp_sync_flush_output(struct syncppp *ap);
static void ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
ap->xaccm[3] = 0x60000000U;
ap->raccm = ~0U;
+ skb_queue_head_init(&ap->rqueue);
+ tasklet_init(&ap->tsk, ppp_sync_process, (unsigned long) ap);
+
atomic_set(&ap->refcnt, 1);
init_MUTEX_LOCKED(&ap->dead_sem);
*/
if (!atomic_dec_and_test(&ap->refcnt))
down(&ap->dead_sem);
+ tasklet_kill(&ap->tsk);
ppp_unregister_channel(&ap->chan);
- if (ap->rpkt != 0)
- kfree_skb(ap->rpkt);
+ skb_queue_purge(&ap->rqueue);
if (ap->tpkt != 0)
kfree_skb(ap->tpkt);
kfree(ap);
*/
static ssize_t
ppp_sync_read(struct tty_struct *tty, struct file *file,
- unsigned char *buf, size_t count)
+ unsigned char __user *buf, size_t count)
{
return -EAGAIN;
}
*/
static ssize_t
ppp_sync_write(struct tty_struct *tty, struct file *file,
- const unsigned char *buf, size_t count)
+ const unsigned char __user *buf, size_t count)
{
return -EAGAIN;
}
unsigned int cmd, unsigned long arg)
{
struct syncppp *ap = sp_get(tty);
+ int __user *p = (int __user *)arg;
int err, val;
if (ap == 0)
if (ap == 0)
break;
err = -EFAULT;
- if (put_user(ppp_channel_index(&ap->chan), (int *) arg))
+ if (put_user(ppp_channel_index(&ap->chan), p))
break;
err = 0;
break;
if (ap == 0)
break;
err = -EFAULT;
- if (put_user(ppp_unit_number(&ap->chan), (int *) arg))
+ if (put_user(ppp_unit_number(&ap->chan), p))
break;
err = 0;
break;
case FIONREAD:
val = 0;
- if (put_user(val, (int *) arg))
+ if (put_user(val, p))
break;
err = 0;
break;
return 65535;
}
+/*
+ * This can now be called from hard interrupt level as well
+ * as soft interrupt level or mainline.
+ */
static void
ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf,
- char *flags, int count)
+ char *cflags, int count)
{
struct syncppp *ap = sp_get(tty);
+ unsigned long flags;
if (ap == 0)
return;
- spin_lock_bh(&ap->recv_lock);
- ppp_sync_input(ap, buf, flags, count);
- spin_unlock_bh(&ap->recv_lock);
+ spin_lock_irqsave(&ap->recv_lock, flags);
+ ppp_sync_input(ap, buf, cflags, count);
+ spin_unlock_irqrestore(&ap->recv_lock, flags);
+ if (skb_queue_len(&ap->rqueue))
+ tasklet_schedule(&ap->tsk);
sp_put(ap);
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags)
&& tty->driver->unthrottle)
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (ap == 0)
return;
- if (ppp_sync_push(ap))
- ppp_output_wakeup(&ap->chan);
+ set_bit(XMIT_WAKEUP, &ap->xmit_flags);
+ tasklet_schedule(&ap->tsk);
sp_put(ap);
}
struct syncppp *ap = chan->private;
int err, val;
u32 accm[8];
+ void __user *argp = (void __user *)arg;
+ u32 __user *p = argp;
err = -EFAULT;
switch (cmd) {
case PPPIOCGFLAGS:
val = ap->flags | ap->rbits;
- if (put_user(val, (int *) arg))
+ if (put_user(val, (int __user *) argp))
break;
err = 0;
break;
case PPPIOCSFLAGS:
- if (get_user(val, (int *) arg))
+ if (get_user(val, (int __user *) argp))
break;
ap->flags = val & ~SC_RCV_BITS;
- spin_lock_bh(&ap->recv_lock);
+ spin_lock_irq(&ap->recv_lock);
ap->rbits = val & SC_RCV_BITS;
- spin_unlock_bh(&ap->recv_lock);
+ spin_unlock_irq(&ap->recv_lock);
err = 0;
break;
case PPPIOCGASYNCMAP:
- if (put_user(ap->xaccm[0], (u32 *) arg))
+ if (put_user(ap->xaccm[0], p))
break;
err = 0;
break;
case PPPIOCSASYNCMAP:
- if (get_user(ap->xaccm[0], (u32 *) arg))
+ if (get_user(ap->xaccm[0], p))
break;
err = 0;
break;
case PPPIOCGRASYNCMAP:
- if (put_user(ap->raccm, (u32 *) arg))
+ if (put_user(ap->raccm, p))
break;
err = 0;
break;
case PPPIOCSRASYNCMAP:
- if (get_user(ap->raccm, (u32 *) arg))
+ if (get_user(ap->raccm, p))
break;
err = 0;
break;
case PPPIOCGXASYNCMAP:
- if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
+ if (copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm)))
break;
err = 0;
break;
case PPPIOCSXASYNCMAP:
- if (copy_from_user(accm, (void *) arg, sizeof(accm)))
+ if (copy_from_user(accm, argp, sizeof(accm)))
break;
accm[2] &= ~0x40000000U; /* can't escape 0x5e */
accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
break;
case PPPIOCGMRU:
- if (put_user(ap->mru, (int *) arg))
+ if (put_user(ap->mru, (int __user *) argp))
break;
err = 0;
break;
case PPPIOCSMRU:
- if (get_user(val, (int *) arg))
+ if (get_user(val, (int __user *) argp))
break;
if (val < PPP_MRU)
val = PPP_MRU;
return err;
}
+/*
+ * This is called at softirq level to deliver received packets
+ * to the ppp_generic code, and to tell the ppp_generic code
+ * if we can accept more output now.
+ */
+static void ppp_sync_process(unsigned long arg)
+{
+ struct syncppp *ap = (struct syncppp *) arg;
+ struct sk_buff *skb;
+
+ /* process received packets */
+ while ((skb = skb_dequeue(&ap->rqueue)) != NULL) {
+ if (skb->len == 0) {
+ /* zero length buffers indicate error */
+ ppp_input_error(&ap->chan, 0);
+ kfree_skb(skb);
+ }
+ else
+ ppp_input(&ap->chan, skb);
+ }
+
+ /* try to push more stuff out */
+ if (test_bit(XMIT_WAKEUP, &ap->xmit_flags) && ppp_sync_push(ap))
+ ppp_output_wakeup(&ap->chan);
+}
+
/*
* Procedures for encapsulation and framing.
*/
struct tty_struct *tty = ap->tty;
int tty_stuffed = 0;
- set_bit(XMIT_WAKEUP, &ap->xmit_flags);
if (!spin_trylock_bh(&ap->xmit_lock))
return 0;
for (;;) {
* Receive-side routines.
*/
-static inline void
-process_input_packet(struct syncppp *ap)
+/* called when the tty driver has data for us.
+ *
+ * Data is frame oriented: each call to ppp_sync_input is considered
+ * a whole frame. If the 1st flag byte is non-zero then the whole
+ * frame is considered to be in error and is tossed.
+ */
+static void
+ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
+ char *flags, int count)
{
struct sk_buff *skb;
unsigned char *p;
- int code = 0;
- skb = ap->rpkt;
- ap->rpkt = 0;
+ if (count == 0)
+ return;
+
+ if (ap->flags & SC_LOG_INPKT)
+ ppp_print_buffer ("receive buffer", buf, count);
+
+ /* stuff the chars in the skb */
+ if ((skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2)) == 0) {
+ printk(KERN_ERR "PPPsync: no memory (input pkt)\n");
+ goto err;
+ }
+ /* Try to get the payload 4-byte aligned */
+ if (buf[0] != PPP_ALLSTATIONS)
+ skb_reserve(skb, 2 + (buf[0] & 1));
+
+ if (flags != 0 && *flags) {
+ /* error flag set, ignore frame */
+ goto err;
+ } else if (count > skb_tailroom(skb)) {
+ /* packet overflowed MRU */
+ goto err;
+ }
+
+ p = skb_put(skb, count);
+ memcpy(p, buf, count);
/* strip address/control field if present */
p = skb->data;
} else if (skb->len < 2)
goto err;
- /* pass to generic layer */
- ppp_input(&ap->chan, skb);
+ /* queue the frame to be processed */
+ skb_queue_tail(&ap->rqueue, skb);
return;
- err:
- kfree_skb(skb);
- ppp_input_error(&ap->chan, code);
-}
-
-/* called when the tty driver has data for us.
- *
- * Data is frame oriented: each call to ppp_sync_input is considered
- * a whole frame. If the 1st flag byte is non-zero then the whole
- * frame is considered to be in error and is tossed.
- */
-static void
-ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
- char *flags, int count)
-{
- struct sk_buff *skb;
- unsigned char *sp;
-
- if (count == 0)
- return;
-
- /* if flag set, then error, ignore frame */
- if (flags != 0 && *flags) {
- ppp_input_error(&ap->chan, *flags);
- return;
- }
-
- if (ap->flags & SC_LOG_INPKT)
- ppp_print_buffer ("receive buffer", buf, count);
-
- /* stuff the chars in the skb */
- if ((skb = ap->rpkt) == 0) {
- if ((skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2)) == 0) {
- printk(KERN_ERR "PPPsync: no memory (input pkt)\n");
- ppp_input_error(&ap->chan, 0);
- return;
- }
- /* Try to get the payload 4-byte aligned */
- if (buf[0] != PPP_ALLSTATIONS)
- skb_reserve(skb, 2 + (buf[0] & 1));
- ap->rpkt = skb;
- }
- if (count > skb_tailroom(skb)) {
- /* packet overflowed MRU */
- ppp_input_error(&ap->chan, 1);
- } else {
- sp = skb_put(skb, count);
- memcpy(sp, buf, count);
- process_input_packet(ap);
+err:
+ /* queue zero length packet as error indication */
+ if (skb || (skb = dev_alloc_skb(0))) {
+ skb_trim(skb, 0);
+ skb_queue_tail(&ap->rqueue, skb);
}
}