125603Skjc/*      $NetBSD: if_atmsubr.c,v 1.10 1997/03/11 23:19:51 chuck Exp $       */
225603Skjc
3139823Simp/*-
425603Skjc *
525603Skjc * Copyright (c) 1996 Charles D. Cranor and Washington University.
625603Skjc * All rights reserved.
725603Skjc *
825603Skjc * Redistribution and use in source and binary forms, with or without
925603Skjc * modification, are permitted provided that the following conditions
1025603Skjc * are met:
1125603Skjc * 1. Redistributions of source code must retain the above copyright
1225603Skjc *    notice, this list of conditions and the following disclaimer.
1325603Skjc * 2. Redistributions in binary form must reproduce the above copyright
1425603Skjc *    notice, this list of conditions and the following disclaimer in the
1525603Skjc *    documentation and/or other materials provided with the distribution.
1625603Skjc * 3. All advertising materials mentioning features or use of this software
1725603Skjc *    must display the following acknowledgement:
1825603Skjc *      This product includes software developed by Charles D. Cranor and
1925603Skjc *	Washington University.
2025603Skjc * 4. The name of the author may not be used to endorse or promote products
2125603Skjc *    derived from this software without specific prior written permission.
2225603Skjc *
2325603Skjc * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2425603Skjc * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2525603Skjc * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2625603Skjc * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2725603Skjc * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2825603Skjc * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2925603Skjc * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3025603Skjc * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3125603Skjc * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3225603Skjc * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3354263Sshin *
3425603Skjc * if_atmsubr.c
3525603Skjc */
3625603Skjc
37116720Sharti#include <sys/cdefs.h>
38116720Sharti__FBSDID("$FreeBSD$");
39116720Sharti
4032350Seivind#include "opt_inet.h"
4154263Sshin#include "opt_inet6.h"
4232925Seivind#include "opt_natm.h"
4332350Seivind
4425603Skjc#include <sys/param.h>
4525603Skjc#include <sys/systm.h>
46114201Sharti#include <sys/kernel.h>
47114201Sharti#include <sys/module.h>
4825603Skjc#include <sys/mbuf.h>
4925603Skjc#include <sys/socket.h>
5037939Skjc#include <sys/sockio.h>
5137939Skjc#include <sys/errno.h>
52114201Sharti#include <sys/sysctl.h>
53117630Sharti#include <sys/malloc.h>
5425603Skjc
5525603Skjc#include <net/if.h>
5625603Skjc#include <net/netisr.h>
5725603Skjc#include <net/route.h>
5825603Skjc#include <net/if_dl.h>
5925603Skjc#include <net/if_types.h>
6025603Skjc#include <net/if_atm.h>
6125603Skjc
6225603Skjc#include <netinet/in.h>
6325603Skjc#include <netinet/if_atm.h>
6425603Skjc#include <netinet/if_ether.h> /* XXX: for ETHERTYPE_* */
6537939Skjc#if defined(INET) || defined(INET6)
6625603Skjc#include <netinet/in_var.h>
6725603Skjc#endif
6825603Skjc#ifdef NATM
6925603Skjc#include <netnatm/natm.h>
7025603Skjc#endif
7125603Skjc
72163606Srwatson#include <security/mac/mac_framework.h>
73163606Srwatson
74116741Sharti/*
75116741Sharti * Netgraph interface functions.
76116741Sharti * These need not be protected by a lock, because ng_atm nodes are persitent.
77116741Sharti * The ng_atm module can be unloaded only if all ATM interfaces have been
78116741Sharti * unloaded, so nobody should be in the code paths accessing these function
79116741Sharti * pointers.
80116741Sharti */
81116741Shartivoid	(*ng_atm_attach_p)(struct ifnet *);
82116741Shartivoid	(*ng_atm_detach_p)(struct ifnet *);
83116741Shartiint	(*ng_atm_output_p)(struct ifnet *, struct mbuf **);
84116741Shartivoid	(*ng_atm_input_p)(struct ifnet *, struct mbuf **,
85116741Sharti	    struct atm_pseudohdr *, void *);
86116741Shartivoid	(*ng_atm_input_orphan_p)(struct ifnet *, struct mbuf *,
87116741Sharti	    struct atm_pseudohdr *, void *);
88118157Shartivoid	(*ng_atm_event_p)(struct ifnet *, uint32_t, void *);
89116741Sharti
90116741Sharti/*
91116741Sharti * Harp pseudo interface hooks
92116741Sharti */
93116741Shartivoid	(*atm_harp_input_p)(struct ifnet *ifp, struct mbuf **m,
94116741Sharti	    struct atm_pseudohdr *ah, void *rxhand);
95116741Shartivoid	(*atm_harp_attach_p)(struct ifnet *);
96116741Shartivoid	(*atm_harp_detach_p)(struct ifnet *);
97118157Shartivoid	(*atm_harp_event_p)(struct ifnet *, uint32_t, void *);
98116741Sharti
99114201ShartiSYSCTL_NODE(_hw, OID_AUTO, atm, CTLFLAG_RW, 0, "ATM hardware");
100114201Sharti
101249132Smavstatic MALLOC_DEFINE(M_IFATM, "ifatm", "atm interface internals");
102147256Sbrooks
10337939Skjc#ifndef ETHERTYPE_IPV6
104116720Sharti#define	ETHERTYPE_IPV6	0x86dd
10537939Skjc#endif
10625603Skjc
107116720Sharti#define	senderr(e) do { error = (e); goto bad; } while (0)
10825603Skjc
10925603Skjc/*
11025603Skjc * atm_output: ATM output routine
11125603Skjc *   inputs:
11225603Skjc *     "ifp" = ATM interface to output to
11325603Skjc *     "m0" = the packet to output
11425603Skjc *     "dst" = the sockaddr to send to (either IP addr, or raw VPI/VCI)
115191148Skmacy *     "ro" = the route to use
11625603Skjc *   returns: error code   [0 == ok]
11725603Skjc *
11825603Skjc *   note: special semantic: if (dst == NULL) then we assume "m" already
11925603Skjc *		has an atm_pseudohdr on it and just send it directly.
12025603Skjc *		[for native mode ATM output]   if dst is null, then
121191148Skmacy *		ro->ro_rt must also be NULL.
12225603Skjc */
12325603Skjcint
124116720Shartiatm_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst,
125191148Skmacy    struct route *ro)
12625603Skjc{
12725603Skjc	u_int16_t etype = 0;			/* if using LLC/SNAP */
12878249Speter	int error = 0, sz;
12925603Skjc	struct atm_pseudohdr atmdst, *ad;
13059633Skjc	struct mbuf *m = m0;
13125603Skjc	struct atmllc *atmllc;
13237939Skjc	struct atmllc *llc_hdr = NULL;
13325603Skjc	u_int32_t atm_flags;
13425603Skjc
135105576Srwatson#ifdef MAC
136172930Srwatson	error = mac_ifnet_check_transmit(ifp, m);
137105576Srwatson	if (error)
138105576Srwatson		senderr(error);
139105576Srwatson#endif
140105576Srwatson
141148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
142148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
14325603Skjc		senderr(ENETDOWN);
14425603Skjc
14525603Skjc	/*
14625603Skjc	 * check for non-native ATM traffic   (dst != NULL)
14725603Skjc	 */
14825603Skjc	if (dst) {
14925603Skjc		switch (dst->sa_family) {
150116720Sharti
15137939Skjc#if defined(INET) || defined(INET6)
15225603Skjc		case AF_INET:
15337939Skjc		case AF_INET6:
154128636Sluigi		{
15546695Skjc			if (dst->sa_family == AF_INET6)
156112193Sharti			        etype = ETHERTYPE_IPV6;
15746695Skjc			else
158112193Sharti			        etype = ETHERTYPE_IP;
159191148Skmacy			if (!atmresolve(ro->ro_rt, m, dst, &atmdst)) {
16025603Skjc				m = NULL;
16125603Skjc				/* XXX: atmresolve already free'd it */
16225603Skjc				senderr(EHOSTUNREACH);
16325603Skjc				/* XXX: put ATMARP stuff here */
16425603Skjc				/* XXX: watch who frees m on failure */
16525603Skjc			}
166128636Sluigi		}
16725603Skjc			break;
16837939Skjc#endif /* INET || INET6 */
16925603Skjc
17037939Skjc		case AF_UNSPEC:
17137939Skjc			/*
17246695Skjc			 * XXX: bpfwrite. assuming dst contains 12 bytes
17346695Skjc			 * (atm pseudo header (4) + LLC/SNAP (8))
17437939Skjc			 */
17537939Skjc			bcopy(dst->sa_data, &atmdst, sizeof(atmdst));
176116720Sharti			llc_hdr = (struct atmllc *)(dst->sa_data +
177116720Sharti			    sizeof(atmdst));
17837939Skjc			break;
17937939Skjc
18025603Skjc		default:
18125603Skjc			printf("%s: can't handle af%d\n", ifp->if_xname,
18225603Skjc			    dst->sa_family);
18325603Skjc			senderr(EAFNOSUPPORT);
18425603Skjc		}
18525603Skjc
18625603Skjc		/*
18725603Skjc		 * must add atm_pseudohdr to data
18825603Skjc		 */
18925603Skjc		sz = sizeof(atmdst);
19025603Skjc		atm_flags = ATM_PH_FLAGS(&atmdst);
191116720Sharti		if (atm_flags & ATM_PH_LLCSNAP)
192116720Sharti			sz += 8;	/* sizeof snap == 8 */
193111119Simp		M_PREPEND(m, sz, M_DONTWAIT);
19425603Skjc		if (m == 0)
19525603Skjc			senderr(ENOBUFS);
19625603Skjc		ad = mtod(m, struct atm_pseudohdr *);
19725603Skjc		*ad = atmdst;
19825603Skjc		if (atm_flags & ATM_PH_LLCSNAP) {
19925603Skjc			atmllc = (struct atmllc *)(ad + 1);
20037939Skjc			if (llc_hdr == NULL) {
20137939Skjc			        bcopy(ATMLLC_HDR, atmllc->llchdr,
20237939Skjc				      sizeof(atmllc->llchdr));
203116720Sharti				/* note: in host order */
20437939Skjc				ATM_LLC_SETTYPE(atmllc, etype);
20537939Skjc			}
20637939Skjc			else
20737939Skjc			        bcopy(llc_hdr, atmllc, sizeof(struct atmllc));
20825603Skjc		}
20925603Skjc	}
21025603Skjc
211116741Sharti	if (ng_atm_output_p != NULL) {
212116741Sharti		if ((error = (*ng_atm_output_p)(ifp, &m)) != 0) {
213116741Sharti			if (m != NULL)
214116741Sharti				m_freem(m);
215116741Sharti			return (error);
216116741Sharti		}
217116741Sharti		if (m == NULL)
218116741Sharti			return (0);
219116741Sharti	}
220116741Sharti
22125603Skjc	/*
22225603Skjc	 * Queue message on interface, and start output if interface
22325603Skjc	 * not yet active.
22425603Skjc	 */
225117629Sharti	if (!IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp,
226117629Sharti	    -(int)sizeof(struct atm_pseudohdr)))
22769152Sjlemon		return (ENOBUFS);
22825603Skjc	return (error);
22925603Skjc
23025603Skjcbad:
23125603Skjc	if (m)
23225603Skjc		m_freem(m);
23325603Skjc	return (error);
23425603Skjc}
23525603Skjc
23625603Skjc/*
23725603Skjc * Process a received ATM packet;
23825603Skjc * the packet is in the mbuf chain m.
23925603Skjc */
24025603Skjcvoid
241116720Shartiatm_input(struct ifnet *ifp, struct atm_pseudohdr *ah, struct mbuf *m,
242116720Sharti    void *rxhand)
24325603Skjc{
244111888Sjlemon	int isr;
245116720Sharti	u_int16_t etype = ETHERTYPE_IP;		/* default */
24625603Skjc
24725603Skjc	if ((ifp->if_flags & IFF_UP) == 0) {
24825603Skjc		m_freem(m);
24925603Skjc		return;
25025603Skjc	}
251105576Srwatson#ifdef MAC
252172930Srwatson	mac_ifnet_create_mbuf(ifp, m);
253105576Srwatson#endif
25425603Skjc	ifp->if_ibytes += m->m_pkthdr.len;
25525603Skjc
256116741Sharti	if (ng_atm_input_p != NULL) {
257116741Sharti		(*ng_atm_input_p)(ifp, &m, ah, rxhand);
258116741Sharti		if (m == NULL)
259116741Sharti			return;
260116741Sharti	}
261116741Sharti
262116741Sharti	/* not eaten by ng_atm. Maybe it's a pseudo-harp PDU? */
263116741Sharti	if (atm_harp_input_p != NULL) {
264116741Sharti		(*atm_harp_input_p)(ifp, &m, ah, rxhand);
265116741Sharti		if (m == NULL)
266116741Sharti			return;
267116741Sharti	}
268116741Sharti
26925603Skjc	if (rxhand) {
27025603Skjc#ifdef NATM
271148125Srwatson		struct natmpcb *npcb;
272116720Sharti
273148125Srwatson		/*
274148125Srwatson		 * XXXRW: this use of 'rxhand' is not a very good idea, and
275148125Srwatson		 * was subject to races even before SMPng due to the release
276148125Srwatson		 * of spl here.
277148125Srwatson		 */
278148125Srwatson		NATM_LOCK();
279148125Srwatson		npcb = rxhand;
28037939Skjc		npcb->npcb_inq++;	/* count # in queue */
281111888Sjlemon		isr = NETISR_NATM;
28237939Skjc		m->m_pkthdr.rcvif = rxhand; /* XXX: overload */
283148125Srwatson		NATM_UNLOCK();
28425603Skjc#else
285116720Sharti		printf("atm_input: NATM detected but not "
286116720Sharti		    "configured in kernel\n");
287116741Sharti		goto dropit;
28825603Skjc#endif
28925603Skjc	} else {
29037939Skjc		/*
29137939Skjc		 * handle LLC/SNAP header, if present
29237939Skjc		 */
29337939Skjc		if (ATM_PH_FLAGS(ah) & ATM_PH_LLCSNAP) {
29437939Skjc			struct atmllc *alc;
295116720Sharti
29637939Skjc			if (m->m_len < sizeof(*alc) &&
29737939Skjc			    (m = m_pullup(m, sizeof(*alc))) == 0)
29837939Skjc				return; /* failed */
29937939Skjc			alc = mtod(m, struct atmllc *);
30037939Skjc			if (bcmp(alc, ATMLLC_HDR, 6)) {
301116720Sharti				printf("%s: recv'd invalid LLC/SNAP frame "
302116720Sharti				    "[vp=%d,vc=%d]\n", ifp->if_xname,
303116720Sharti				    ATM_PH_VPI(ah), ATM_PH_VCI(ah));
30437939Skjc				m_freem(m);
30537939Skjc				return;
30637939Skjc			}
30737939Skjc			etype = ATM_LLC_TYPE(alc);
30837939Skjc			m_adj(m, sizeof(*alc));
30937939Skjc		}
31025603Skjc
31137939Skjc		switch (etype) {
312116720Sharti
31325603Skjc#ifdef INET
31437939Skjc		case ETHERTYPE_IP:
315111888Sjlemon			isr = NETISR_IP;
31637939Skjc			break;
31725603Skjc#endif
318116720Sharti
31937939Skjc#ifdef INET6
32037939Skjc		case ETHERTYPE_IPV6:
321111888Sjlemon			isr = NETISR_IPV6;
32237939Skjc			break;
32337939Skjc#endif
32437939Skjc		default:
325116741Sharti#ifndef NATM
326116741Sharti  dropit:
327116741Sharti#endif
328116741Sharti			if (ng_atm_input_orphan_p != NULL)
329116741Sharti				(*ng_atm_input_orphan_p)(ifp, m, ah, rxhand);
330116741Sharti			else
331116741Sharti				m_freem(m);
33237939Skjc			return;
33337939Skjc		}
33425603Skjc	}
335223741Sbz	M_SETFIB(m, ifp->if_fib);
336111888Sjlemon	netisr_dispatch(isr, m);
33725603Skjc}
33825603Skjc
33925603Skjc/*
340114201Sharti * Perform common duties while attaching to interface list.
34125603Skjc */
34225603Skjcvoid
343116720Shartiatm_ifattach(struct ifnet *ifp)
34425603Skjc{
345111774Smdodd	struct ifaddr *ifa;
346111774Smdodd	struct sockaddr_dl *sdl;
347147256Sbrooks	struct ifatm *ifatm = ifp->if_l2com;
34825603Skjc
34925603Skjc	ifp->if_addrlen = 0;
35025603Skjc	ifp->if_hdrlen = 0;
351114201Sharti	if_attach(ifp);
35225603Skjc	ifp->if_mtu = ATMMTU;
35325603Skjc	ifp->if_output = atm_output;
354106939Ssam#if 0
355106939Ssam	ifp->if_input = atm_input;
356106939Ssam#endif
35746695Skjc	ifp->if_snd.ifq_maxlen = 50;	/* dummy */
35825603Skjc
359160035Syar	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
360160038Syar		if (ifa->ifa_addr->sa_family == AF_LINK) {
361160038Syar			sdl = (struct sockaddr_dl *)ifa->ifa_addr;
36225603Skjc			sdl->sdl_type = IFT_ATM;
36325603Skjc			sdl->sdl_alen = ifp->if_addrlen;
36425603Skjc#ifdef notyet /* if using ATMARP, store hardware address using the next line */
36525603Skjc			bcopy(ifp->hw_addr, LLADDR(sdl), ifp->if_addrlen);
36625603Skjc#endif
36725603Skjc			break;
36825603Skjc		}
36937939Skjc
370114739Sharti	ifp->if_linkmib = &ifatm->mib;
371114739Sharti	ifp->if_linkmiblen = sizeof(ifatm->mib);
372116741Sharti
373116741Sharti	if(ng_atm_attach_p)
374116741Sharti		(*ng_atm_attach_p)(ifp);
375116741Sharti	if (atm_harp_attach_p)
376116741Sharti		(*atm_harp_attach_p)(ifp);
37737939Skjc}
378114201Sharti
379114201Sharti/*
380114201Sharti * Common stuff for detaching an ATM interface
381114201Sharti */
382114201Shartivoid
383114201Shartiatm_ifdetach(struct ifnet *ifp)
384114201Sharti{
385116741Sharti	if (atm_harp_detach_p)
386116741Sharti		(*atm_harp_detach_p)(ifp);
387116741Sharti	if(ng_atm_detach_p)
388116741Sharti		(*ng_atm_detach_p)(ifp);
389114201Sharti	if_detach(ifp);
390114201Sharti}
391114201Sharti
392117630Sharti/*
393117630Sharti * Support routine for the SIOCATMGVCCS ioctl().
394117630Sharti *
395117630Sharti * This routine assumes, that the private VCC structures used by the driver
396117630Sharti * begin with a struct atmio_vcc.
397117630Sharti *
398117630Sharti * Return a table of VCCs in a freshly allocated memory area.
399117630Sharti * Here we have a problem: we first count, how many vccs we need
400117630Sharti * to return. The we allocate the memory and finally fill it in.
401117630Sharti * Because we cannot lock while calling malloc, the number of active
402117630Sharti * vccs may change while we're in malloc. So we allocate a couple of
403117630Sharti * vccs more and if space anyway is not enough re-iterate.
404117630Sharti *
405117630Sharti * We could use an sx lock for the vcc tables.
406117630Sharti */
407117630Shartistruct atmio_vcctable *
408117630Shartiatm_getvccs(struct atmio_vcc **table, u_int size, u_int start,
409117630Sharti    struct mtx *lock, int waitok)
410117630Sharti{
411117630Sharti	u_int cid, alloc;
412117630Sharti	size_t len;
413117630Sharti	struct atmio_vcctable *vccs;
414117630Sharti	struct atmio_vcc *v;
415117630Sharti
416117630Sharti	alloc = start + 10;
417117630Sharti	vccs = NULL;
418117630Sharti
419117630Sharti	for (;;) {
420117630Sharti		len = sizeof(*vccs) + alloc * sizeof(vccs->vccs[0]);
421117630Sharti		vccs = reallocf(vccs, len, M_TEMP,
422117630Sharti		    waitok ? M_WAITOK : M_NOWAIT);
423117630Sharti		if (vccs == NULL)
424117630Sharti			return (NULL);
425117630Sharti		bzero(vccs, len);
426117630Sharti
427117630Sharti		vccs->count = 0;
428117630Sharti		v = vccs->vccs;
429117630Sharti
430117630Sharti		mtx_lock(lock);
431117630Sharti		for (cid = 0; cid < size; cid++)
432117630Sharti			if (table[cid] != NULL) {
433117630Sharti				if (++vccs->count == alloc)
434117630Sharti					/* too many - try again */
435117630Sharti					break;
436117630Sharti				*v++ = *table[cid];
437117630Sharti			}
438117630Sharti		mtx_unlock(lock);
439117630Sharti
440117630Sharti		if (cid == size)
441117630Sharti			break;
442117630Sharti
443117630Sharti		alloc *= 2;
444117630Sharti	}
445117630Sharti	return (vccs);
446117630Sharti}
447117630Sharti
448118157Sharti/*
449118157Sharti * Driver or channel state has changed. Inform whoever is interested
450118157Sharti * in these events.
451118157Sharti */
452118157Shartivoid
453118157Shartiatm_event(struct ifnet *ifp, u_int event, void *arg)
454118157Sharti{
455118157Sharti	if (ng_atm_event_p != NULL)
456118157Sharti		(*ng_atm_event_p)(ifp, event, arg);
457118157Sharti	if (atm_harp_event_p != NULL)
458118157Sharti		(*atm_harp_event_p)(ifp, event, arg);
459118157Sharti}
460118157Sharti
461147256Sbrooksstatic void *
462147256Sbrooksatm_alloc(u_char type, struct ifnet *ifp)
463147256Sbrooks{
464147256Sbrooks	struct ifatm	*ifatm;
465147256Sbrooks
466147256Sbrooks	ifatm = malloc(sizeof(struct ifatm), M_IFATM, M_WAITOK | M_ZERO);
467147256Sbrooks	ifatm->ifp = ifp;
468147256Sbrooks
469147256Sbrooks	return (ifatm);
470147256Sbrooks}
471147256Sbrooks
472147256Sbrooksstatic void
473147256Sbrooksatm_free(void *com, u_char type)
474147256Sbrooks{
475147256Sbrooks
476147256Sbrooks	free(com, M_IFATM);
477147256Sbrooks}
478147256Sbrooks
479147256Sbrooksstatic int
480147256Sbrooksatm_modevent(module_t mod, int type, void *data)
481147256Sbrooks{
482147256Sbrooks	switch (type) {
483147256Sbrooks	case MOD_LOAD:
484147256Sbrooks		if_register_com_alloc(IFT_ATM, atm_alloc, atm_free);
485147256Sbrooks		break;
486147256Sbrooks	case MOD_UNLOAD:
487147256Sbrooks		if_deregister_com_alloc(IFT_ATM);
488147256Sbrooks		break;
489147256Sbrooks	default:
490147256Sbrooks		return (EOPNOTSUPP);
491147256Sbrooks	}
492147256Sbrooks
493147256Sbrooks	return (0);
494147256Sbrooks}
495147256Sbrooks
496114201Shartistatic moduledata_t atm_mod = {
497114201Sharti        "atm",
498147256Sbrooks        atm_modevent,
499114201Sharti        0
500114201Sharti};
501114201Sharti
502147256SbrooksDECLARE_MODULE(atm, atm_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
503114201ShartiMODULE_VERSION(atm, 1);
504