11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* net/atm/pppoatm.c - RFC2364 PPP over ATM/AAL5 */
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Copyright 1999-2000 by Mitchell Blank Jr */
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Based on clip.c; 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* And on ppp_async.c; Copyright 1999 Paul Mackerras */
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* And help from Jens Axboe */
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  This program is free software; you can redistribute it and/or
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  modify it under the terms of the GNU General Public License
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  as published by the Free Software Foundation; either version
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  2 of the License, or (at your option) any later version.
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This driver provides the encapsulation and framing for sending
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and receiving PPP frames in ATM AAL5 PDUs.
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * One shortcoming of this driver is that it does not comply with
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * section 8 of RFC2364 - we are supposed to detect a change
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in encapsulation and immediately abort the connection (in order
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to avoid a black-hole being created if our peer loses state
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and changes encapsulation unilaterally.  However, since the
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ppp_generic layer actually does the decapsulation, we need
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * a way of notifying it when we _think_ there might be a problem)
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * There's two cases:
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   1.	LLC-encapsulation was missing when it was enabled.  In
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	this case, we should tell the upper layer "tear down
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	this session if this skb looks ok to you"
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   2.	LLC-encapsulation was present when it was disabled.  Then
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	we need to tell the upper layer "this packet may be
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	ok, but if its in error tear down the session"
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * These hooks are not yet available in ppp_generic
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3699824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
3799824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
40a6b7a407865aab9f849dd99a71072b7cd1175116Alexey Dobriyan#include <linux/interrupt.h>
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/skbuff.h>
425a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/atm.h>
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/atmdev.h>
454fc268d24ceb9f4150777c1b5b2b8e6214e56b2bRandy Dunlap#include <linux/capability.h>
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ppp_defs.h>
474b32da2bcf1de2b7a196a0e48389d231b4472c36Paul Mackerras#include <linux/ppp-ioctl.h>
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ppp_channel.h>
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/atmppp.h>
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "common.h"
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsenum pppoatm_encaps {
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	e_autodetect = PPPOATM_ENCAPS_AUTODETECT,
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	e_vc = PPPOATM_ENCAPS_VC,
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	e_llc = PPPOATM_ENCAPS_LLC,
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct pppoatm_vcc {
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct atm_vcc	*atmvcc;	/* VCC descriptor */
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void (*old_push)(struct atm_vcc *, struct sk_buff *);
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void (*old_pop)(struct atm_vcc *, struct sk_buff *);
630e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse	void (*old_release_cb)(struct atm_vcc *);
64e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur	struct module *old_owner;
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* keep old push/pop for detaching */
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	enum pppoatm_encaps encaps;
679d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	atomic_t inflight;
689d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	unsigned long blocked;
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int flags;			/* SC_COMP_PROT - compress protocol */
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ppp_channel chan;	/* interface to generic ppp layer */
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct tasklet_struct wakeup_tasklet;
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
759d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * We want to allow two packets in the queue. The one that's currently in
769d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * flight, and *one* queued up ready for the ATM device to send immediately
779d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * from its TX done IRQ. We want to be able to use atomic_inc_not_zero(), so
789d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * inflight == -2 represents an empty queue, -1 one packet, and zero means
799d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * there are two packets in the queue.
809d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse */
819d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse#define NONE_INFLIGHT -2
829d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse
839d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse#define BLOCKED 0
849d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse
859d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse/*
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Header used for LLC Encapsulated PPP (4 bytes) followed by the LCP protocol
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ID (0xC021) used in autodetection
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const unsigned char pppllc[6] = { 0xFE, 0xFE, 0x03, 0xCF, 0xC0, 0x21 };
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LLC_LEN		(4)
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline struct pppoatm_vcc *atmvcc_to_pvcc(const struct atm_vcc *atmvcc)
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (struct pppoatm_vcc *) (atmvcc->user_back);
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline struct pppoatm_vcc *chan_to_pvcc(const struct ppp_channel *chan)
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (struct pppoatm_vcc *) (chan->private);
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We can't do this directly from our _pop handler, since the ppp code
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * doesn't want to be called in interrupt context, so we do it from
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * a tasklet
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void pppoatm_wakeup_sender(unsigned long arg)
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ppp_output_wakeup((struct ppp_channel *) arg);
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1120e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhousestatic void pppoatm_release_cb(struct atm_vcc *atmvcc)
1130e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse{
1140e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse	struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc);
1150e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse
1165b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse	/*
1175b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse	 * As in pppoatm_pop(), it's safe to clear the BLOCKED bit here because
1185b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse	 * the wakeup *can't* race with pppoatm_send(). They both hold the PPP
1195b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse	 * channel's ->downl lock. And the potential race with *setting* it,
1205b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse	 * which leads to the double-check dance in pppoatm_may_send(), doesn't
1215b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse	 * exist here. In the sock_owned_by_user() case in pppoatm_send(), we
1225b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse	 * set the BLOCKED bit while the socket is still locked. We know that
1235b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse	 * ->release_cb() can't be called until that's done.
1245b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse	 */
1255b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse	if (test_and_clear_bit(BLOCKED, &pvcc->blocked))
1265b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse		tasklet_schedule(&pvcc->wakeup_tasklet);
1270e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse	if (pvcc->old_release_cb)
1280e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse		pvcc->old_release_cb(atmvcc);
1290e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse}
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This gets called every time the ATM card has finished sending our
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * skb.  The ->old_pop will take care up normal atm flow control,
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but we also need to wake up the device if we blocked it
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void pppoatm_pop(struct atm_vcc *atmvcc, struct sk_buff *skb)
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc);
1389d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pvcc->old_pop(atmvcc, skb);
1409d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	atomic_dec(&pvcc->inflight);
1419d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1439d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * We always used to run the wakeup tasklet unconditionally here, for
1449d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * fear of race conditions where we clear the BLOCKED flag just as we
1459d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * refuse another packet in pppoatm_send(). This was quite inefficient.
1469d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 *
1479d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * In fact it's OK. The PPP core will only ever call pppoatm_send()
1489d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * while holding the channel->downl lock. And ppp_output_wakeup() as
1499d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * called by the tasklet will *also* grab that lock. So even if another
1509d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * CPU is in pppoatm_send() right now, the tasklet isn't going to race
1519d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * with it. The wakeup *will* happen after the other CPU is safely out
1529d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * of pppoatm_send() again.
1539d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 *
1549d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * So if the CPU in pppoatm_send() has already set the BLOCKED bit and
1559d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * it about to return, that's fine. We trigger a wakeup which will
1569d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * happen later. And if the CPU in pppoatm_send() *hasn't* set the
1579d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * BLOCKED bit yet, that's fine too because of the double check in
1589d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * pppoatm_may_send() which is commented there.
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1609d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	if (test_and_clear_bit(BLOCKED, &pvcc->blocked))
1619d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse		tasklet_schedule(&pvcc->wakeup_tasklet);
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Unbind from PPP - currently we only do this when closing the socket,
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but we could put this into an ioctl if need be
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void pppoatm_unassign_vcc(struct atm_vcc *atmvcc)
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct pppoatm_vcc *pvcc;
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pvcc = atmvcc_to_pvcc(atmvcc);
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atmvcc->push = pvcc->old_push;
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atmvcc->pop = pvcc->old_pop;
1740e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse	atmvcc->release_cb = pvcc->old_release_cb;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tasklet_kill(&pvcc->wakeup_tasklet);
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ppp_unregister_channel(&pvcc->chan);
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atmvcc->user_back = NULL;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(pvcc);
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Called when an AAL5 PDU comes in */
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void pppoatm_push(struct atm_vcc *atmvcc, struct sk_buff *skb)
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc);
18599824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches	pr_debug("\n");
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (skb == NULL) {			/* VCC was closed */
187e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur		struct module *module;
188e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur
189522400623e240ad134cb4101b1fddc3245d2a7edStephen Hemminger		pr_debug("removing ATMPPP VCC %p\n", pvcc);
190e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur		module = pvcc->old_owner;
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pppoatm_unassign_vcc(atmvcc);
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		atmvcc->push(atmvcc, NULL);	/* Pass along bad news */
193e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur		module_put(module);
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atm_return(atmvcc, skb->truesize);
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (pvcc->encaps) {
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case e_llc:
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (skb->len < LLC_LEN ||
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    memcmp(skb->data, pppllc, LLC_LEN))
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto error;
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		skb_pull(skb, LLC_LEN);
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case e_autodetect:
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (pvcc->chan.ppp == NULL) {	/* Not bound yet! */
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree_skb(skb);
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (skb->len >= sizeof(pppllc) &&
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    !memcmp(skb->data, pppllc, sizeof(pppllc))) {
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pvcc->encaps = e_llc;
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			skb_pull(skb, LLC_LEN);
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (skb->len >= (sizeof(pppllc) - LLC_LEN) &&
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    !memcmp(skb->data, &pppllc[LLC_LEN],
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    sizeof(pppllc) - LLC_LEN)) {
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pvcc->encaps = e_vc;
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pvcc->chan.mtu += LLC_LEN;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
22299824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches		pr_debug("Couldn't autodetect yet (skb: %02X %02X %02X %02X %02X %02X)\n",
22399824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches			 skb->data[0], skb->data[1], skb->data[2],
22499824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches			 skb->data[3], skb->data[4], skb->data[5]);
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case e_vc:
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ppp_input(&pvcc->chan, skb);
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
231d81219db6add0a176c37d6fe4e1c050778de9d2fJoe Perches
232d81219db6add0a176c37d6fe4e1c050778de9d2fJoe Percheserror:
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree_skb(skb);
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ppp_input_error(&pvcc->chan, 0);
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
237397ff16dce53888ec693b3718640be2560204751Krzysztof Mazurstatic int pppoatm_may_send(struct pppoatm_vcc *pvcc, int size)
2389d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse{
2399d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	/*
2409d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * It's not clear that we need to bother with using atm_may_send()
2419d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * to check we don't exceed sk->sk_sndbuf. If userspace sets a
2429d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * value of sk_sndbuf which is lower than the MTU, we're going to
2439d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * block for ever. But the code always did that before we introduced
2449d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * the packet count limit, so...
2459d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 */
2469d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	if (atm_may_send(pvcc->atmvcc, size) &&
2479d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	    atomic_inc_not_zero_hint(&pvcc->inflight, NONE_INFLIGHT))
2489d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse		return 1;
2499d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse
2509d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	/*
2519d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * We use test_and_set_bit() rather than set_bit() here because
2529d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * we need to ensure there's a memory barrier after it. The bit
2539d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * *must* be set before we do the atomic_inc() on pvcc->inflight.
2549d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * There's no smp_mb__after_set_bit(), so it's this or abuse
2554e857c58efeb99393cba5a5d0d8ec7117183137cPeter Zijlstra	 * smp_mb__after_atomic().
2569d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 */
2579d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	test_and_set_bit(BLOCKED, &pvcc->blocked);
2589d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse
2599d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	/*
2609d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * We may have raced with pppoatm_pop(). If it ran for the
2619d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * last packet in the queue, *just* before we set the BLOCKED
2629d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * bit, then it might never run again and the channel could
2639d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * remain permanently blocked. Cope with that race by checking
2649d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * *again*. If it did run in that window, we'll have space on
2659d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * the queue now and can return success. It's harmless to leave
2669d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * the BLOCKED flag set, since it's only used as a trigger to
2679d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * run the wakeup tasklet. Another wakeup will never hurt.
2689d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * If pppoatm_pop() is running but hasn't got as far as making
2699d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * space on the queue yet, then it hasn't checked the BLOCKED
2709d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * flag yet either, so we're safe in that case too. It'll issue
2719d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * an "immediate" wakeup... where "immediate" actually involves
2729d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * taking the PPP channel's ->downl lock, which is held by the
2739d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * code path that calls pppoatm_send(), and is thus going to
2749d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 * wait for us to finish.
2759d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	 */
2769d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	if (atm_may_send(pvcc->atmvcc, size) &&
2779d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	    atomic_inc_not_zero(&pvcc->inflight))
2789d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse		return 1;
2799d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse
2809d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	return 0;
2819d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse}
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Called by the ppp_generic.c to send a packet - returns true if packet
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * was accepted.  If we return false, then it's our job to call
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ppp_output_wakeup(chan) when we're feeling more up to it.
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Note that in the ENOMEM case (as opposed to the !atm_may_send case)
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * we should really drop the packet, but the generic layer doesn't
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * support this yet.  We just return 'DROP_PACKET' which we actually define
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * as success, just to be clear what we're really doing.
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DROP_PACKET 1
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct pppoatm_vcc *pvcc = chan_to_pvcc(chan);
2953ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur	struct atm_vcc *vcc;
2963ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur	int ret;
2973ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ATM_SKB(skb)->vcc = pvcc->atmvcc;
29999824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches	pr_debug("(skb=0x%p, vcc=0x%p)\n", skb, pvcc->atmvcc);
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (skb->data[0] == '\0' && (pvcc->flags & SC_COMP_PROT))
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(void) skb_pull(skb, 1);
3023ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur
3033ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur	vcc = ATM_SKB(skb)->vcc;
3043ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur	bh_lock_sock(sk_atm(vcc));
3055b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse	if (sock_owned_by_user(sk_atm(vcc))) {
3065b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse		/*
3075b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse		 * Needs to happen (and be flushed, hence test_and_) before we unlock
3085b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse		 * the socket. It needs to be seen by the time our ->release_cb gets
3095b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse		 * called.
3105b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse		 */
3115b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse		test_and_set_bit(BLOCKED, &pvcc->blocked);
3123ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur		goto nospace;
3135b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse	}
314071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur	if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
315071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur	    test_bit(ATM_VF_CLOSE, &vcc->flags) ||
316071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur	    !test_bit(ATM_VF_READY, &vcc->flags)) {
317071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur		bh_unlock_sock(sk_atm(vcc));
318071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur		kfree_skb(skb);
319071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur		return DROP_PACKET;
320071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur	}
3213ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (pvcc->encaps) {		/* LLC encapsulation needed */
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case e_llc:
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (skb_headroom(skb) < LLC_LEN) {
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct sk_buff *n;
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			n = skb_realloc_headroom(skb, LLC_LEN);
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (n != NULL &&
3289d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse			    !pppoatm_may_send(pvcc, n->truesize)) {
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				kfree_skb(n);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto nospace;
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3325d0ba55b6486f58cc890918d7167063d83f7fbb4Eric Dumazet			consume_skb(skb);
333d81219db6add0a176c37d6fe4e1c050778de9d2fJoe Perches			skb = n;
3343ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur			if (skb == NULL) {
3353ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur				bh_unlock_sock(sk_atm(vcc));
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return DROP_PACKET;
3373ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur			}
3389d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse		} else if (!pppoatm_may_send(pvcc, skb->truesize))
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto nospace;
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(skb_push(skb, LLC_LEN), pppllc, LLC_LEN);
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case e_vc:
3439d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse		if (!pppoatm_may_send(pvcc, skb->truesize))
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto nospace;
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case e_autodetect:
3473ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur		bh_unlock_sock(sk_atm(vcc));
348522400623e240ad134cb4101b1fddc3245d2a7edStephen Hemminger		pr_debug("Trying to send without setting encaps!\n");
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree_skb(skb);
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 1;
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atomic_add(skb->truesize, &sk_atm(ATM_SKB(skb)->vcc)->sk_wmem_alloc);
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ATM_SKB(skb)->atm_options = ATM_SKB(skb)->vcc->atm_options;
35599824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches	pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n",
35699824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches		 skb, ATM_SKB(skb)->vcc, ATM_SKB(skb)->vcc->dev);
3573ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur	ret = ATM_SKB(skb)->vcc->send(ATM_SKB(skb)->vcc, skb)
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    ? DROP_PACKET : 1;
3593ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur	bh_unlock_sock(sk_atm(vcc));
3603ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur	return ret;
361d81219db6add0a176c37d6fe4e1c050778de9d2fJoe Perchesnospace:
3623ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur	bh_unlock_sock(sk_atm(vcc));
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We don't have space to send this SKB now, but we might have
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * already applied SC_COMP_PROT compression, so may need to undo
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((pvcc->flags & SC_COMP_PROT) && skb_headroom(skb) > 0 &&
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    skb->data[-1] == '\0')
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(void) skb_push(skb, 1);
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This handles ioctls sent to the /dev/ppp interface */
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int pppoatm_devppp_ioctl(struct ppp_channel *chan, unsigned int cmd,
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long arg)
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case PPPIOCGFLAGS:
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return put_user(chan_to_pvcc(chan)->flags, (int __user *) arg)
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    ? -EFAULT : 0;
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case PPPIOCSFLAGS:
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return get_user(chan_to_pvcc(chan)->flags, (int __user *) arg)
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    ? -EFAULT : 0;
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOTTY;
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
388d7100da026317fcf07411f765fe1cdb044053917stephen hemmingerstatic const struct ppp_channel_ops pppoatm_ops = {
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.start_xmit = pppoatm_send,
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl = pppoatm_devppp_ioctl,
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int pppoatm_assign_vcc(struct atm_vcc *atmvcc, void __user *arg)
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct atm_backend_ppp be;
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct pppoatm_vcc *pvcc;
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Each PPPoATM instance has its own tasklet - this is just a
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * prototypical one used to initialize them
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static const DECLARE_TASKLET(tasklet_proto, pppoatm_wakeup_sender, 0);
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (copy_from_user(&be, arg, sizeof be))
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (be.encaps != PPPOATM_ENCAPS_AUTODETECT &&
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    be.encaps != PPPOATM_ENCAPS_VC && be.encaps != PPPOATM_ENCAPS_LLC)
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4080da974f4f303a6842516b764507e3c0a03f41e5aPanagiotis Issaris	pvcc = kzalloc(sizeof(*pvcc), GFP_KERNEL);
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (pvcc == NULL)
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pvcc->atmvcc = atmvcc;
4129d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse
4139d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	/* Maximum is zero, so that we can use atomic_inc_not_zero() */
4149d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse	atomic_set(&pvcc->inflight, NONE_INFLIGHT);
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pvcc->old_push = atmvcc->push;
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pvcc->old_pop = atmvcc->pop;
417e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur	pvcc->old_owner = atmvcc->owner;
4180e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse	pvcc->old_release_cb = atmvcc->release_cb;
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pvcc->encaps = (enum pppoatm_encaps) be.encaps;
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pvcc->chan.private = pvcc;
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pvcc->chan.ops = &pppoatm_ops;
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pvcc->chan.mtu = atmvcc->qos.txtp.max_sdu - PPP_HDRLEN -
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    (be.encaps == e_vc ? 0 : LLC_LEN);
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pvcc->wakeup_tasklet = tasklet_proto;
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pvcc->wakeup_tasklet.data = (unsigned long) &pvcc->chan;
426d81219db6add0a176c37d6fe4e1c050778de9d2fJoe Perches	err = ppp_register_channel(&pvcc->chan);
427d81219db6add0a176c37d6fe4e1c050778de9d2fJoe Perches	if (err != 0) {
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(pvcc);
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return err;
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atmvcc->user_back = pvcc;
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atmvcc->push = pppoatm_push;
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atmvcc->pop = pppoatm_pop;
4340e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse	atmvcc->release_cb = pppoatm_release_cb;
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__module_get(THIS_MODULE);
436e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur	atmvcc->owner = THIS_MODULE;
4374e55f5785825f18b1eb6c5cc5a9717e276925805Jorge Boncompte [DTI
4384e55f5785825f18b1eb6c5cc5a9717e276925805Jorge Boncompte [DTI	/* re-process everything received between connection setup and
4394e55f5785825f18b1eb6c5cc5a9717e276925805Jorge Boncompte [DTI	   backend setup */
4404e55f5785825f18b1eb6c5cc5a9717e276925805Jorge Boncompte [DTI	vcc_process_recv_queue(atmvcc);
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This handles ioctls actually performed on our vcc - we must return
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * -ENOIOCTLCMD for any unrecognized ioctl
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int pppoatm_ioctl(struct socket *sock, unsigned int cmd,
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long arg)
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct atm_vcc *atmvcc = ATM_SD(sock);
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __user *argp = (void __user *)arg;
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cmd != ATM_SETBACKEND && atmvcc->push != pppoatm_push)
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOIOCTLCMD;
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ATM_SETBACKEND: {
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		atm_backend_t b;
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (get_user(b, (atm_backend_t __user *) argp))
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (b != ATM_BACKEND_PPP)
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENOIOCTLCMD;
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!capable(CAP_NET_ADMIN))
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EPERM;
4653b1a914595f3f9beb9e38ff3ddc7bdafa092ba22Krzysztof Mazur		if (sock->state != SS_CONNECTED)
4663b1a914595f3f9beb9e38ff3ddc7bdafa092ba22Krzysztof Mazur			return -EINVAL;
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return pppoatm_assign_vcc(atmvcc, argp);
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case PPPIOCGCHAN:
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return put_user(ppp_channel_index(&atmvcc_to_pvcc(atmvcc)->
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    chan), (int __user *) argp) ? -EFAULT : 0;
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case PPPIOCGUNIT:
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return put_user(ppp_unit_number(&atmvcc_to_pvcc(atmvcc)->
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    chan), (int __user *) argp) ? -EFAULT : 0;
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct atm_ioctl pppoatm_ioctl_ops = {
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner	= THIS_MODULE,
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl	= pppoatm_ioctl,
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init pppoatm_init(void)
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	register_atm_ioctl(&pppoatm_ioctl_ops);
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit pppoatm_exit(void)
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	deregister_atm_ioctl(&pppoatm_ioctl_ops);
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(pppoatm_init);
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(pppoatm_exit);
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Mitchell Blank Jr <mitch@sfgoth.com>");
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("RFC2364 PPP over ATM/AAL5");
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
501