]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/harp/if_harp.c
This commit was generated by cvs2svn to compensate for changes in r151497,
[FreeBSD/FreeBSD.git] / sys / dev / harp / if_harp.c
1 /*-
2  * Copyright (c) 2003
3  *      Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *      All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * Author: Harti Brandt <harti@freebsd.org>
28  *
29  * HARP pseudo-driver. This driver when loaded attaches to all ngATM drivers
30  * in the system and creates a HARP physical interface for each of them.
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/malloc.h>
42 #include <sys/kernel.h>
43 #include <sys/conf.h>
44 #include <sys/module.h>
45 #include <sys/queue.h>
46 #include <sys/syslog.h>
47
48 #include <sys/sockio.h>
49 #include <sys/mbuf.h>
50 #include <sys/socket.h>
51 #include <sys/socketvar.h>
52
53 #include <net/if.h>
54 #include <net/if_var.h>
55 #include <net/if_types.h>
56 #include <net/if_media.h>
57 #include <net/netisr.h>
58
59 #include <netatm/port.h>
60 #include <netatm/queue.h>
61 #include <netatm/atm.h>
62 #include <netatm/atm_sys.h>
63 #include <netatm/atm_sap.h>
64 #include <netatm/atm_cm.h>
65 #include <netatm/atm_if.h>
66 #include <netatm/atm_stack.h>
67 #include <netatm/atm_pcb.h>
68 #include <netatm/atm_var.h>
69 #include <netatm/atm_vc.h>
70
71 #include <net/if_atm.h>
72
73 #define HARP_MTU        9188
74
75 /*
76  * Physical interface softc
77  */
78 struct harp_softc {
79         Cmn_unit        cmn;
80         struct ifnet    *parent;
81         LIST_ENTRY(harp_softc) link;
82 };
83
84 struct harp_vcc {
85         struct cmn_vcc  cmn;
86 };
87
88 MODULE_VERSION(harp, 1);
89 MODULE_DEPEND(harp, atm, 1, 1, 1);
90
91 /* hooks from if_atmsubr.c */
92 extern void (*atm_harp_input_p)(struct ifnet *ifp, struct mbuf **m,
93     struct atm_pseudohdr *ah, void *rxhand);
94 extern void (*atm_harp_attach_p)(struct ifnet *);
95 extern void (*atm_harp_detach_p)(struct ifnet *);
96
97 static MALLOC_DEFINE(M_HARP, "harp", "Harp pseudo interface");
98
99 static uma_zone_t harp_nif_zone;
100 static uma_zone_t harp_vcc_zone;
101
102 /* List of all existing 'harp' interfaces */
103 static LIST_HEAD(, harp_softc) harp_softc_list =
104     LIST_HEAD_INITIALIZER(harp_softc_list);
105
106 static struct stack_defn harp_svaal5 = {
107         NULL,
108         SAP_CPCS_AAL5,
109         SDF_TERM,
110         atm_dev_inst,
111         atm_dev_lower,
112         NULL,
113         0,
114 };
115
116 static struct stack_defn *harp_services = &harp_svaal5;
117
118 /*
119  * Map between constants
120  */
121 static const struct {
122         u_int   vendor;
123         u_int   api;
124         u_int   dev;
125 } map_devs[] = {
126         [ATM_DEVICE_UNKNOWN] =
127                 { VENDOR_UNKNOWN, VENDAPI_UNKNOWN, DEV_UNKNOWN },
128         [ATM_DEVICE_PCA200E] =
129                 { VENDOR_FORE, VENDAPI_FORE_1, DEV_FORE_PCA200E },
130         [ATM_DEVICE_HE155] =
131                 { VENDOR_FORE, VENDAPI_FORE_2, DEV_FORE_HE155 },
132         [ATM_DEVICE_HE622] =
133                 { VENDOR_FORE, VENDAPI_FORE_2, DEV_FORE_HE622 },
134         [ATM_DEVICE_ENI155P] =
135                 { VENDOR_ENI, VENDAPI_ENI_1, DEV_ENI_155P },
136         [ATM_DEVICE_ADP155P] =
137                 { VENDOR_ENI, VENDAPI_ENI_1, DEV_ENI_155P },
138         [ATM_DEVICE_FORELE25] =
139                 { VENDOR_FORE, VENDAPI_IDT_1, DEV_FORE_LE25 },
140         [ATM_DEVICE_FORELE155] =
141                 { VENDOR_FORE, VENDAPI_IDT_1, DEV_FORE_LE155 },
142         [ATM_DEVICE_NICSTAR25] =
143                 { VENDOR_IDT, VENDAPI_IDT_1, DEV_IDT_25 },
144         [ATM_DEVICE_NICSTAR155] =
145                 { VENDOR_IDT, VENDAPI_IDT_1, DEV_IDT_155 },
146         [ATM_DEVICE_IDTABR25] =
147                 { VENDOR_IDT, VENDAPI_IDT_2, DEV_IDTABR_25 },
148         [ATM_DEVICE_IDTABR155] =
149                 { VENDOR_IDT, VENDAPI_IDT_2, DEV_IDTABR_155 },
150         [ATM_DEVICE_PROATM25] =
151                 { VENDOR_PROSUM, VENDAPI_IDT_2, DEV_PROATM_25 },
152         [ATM_DEVICE_PROATM155] =
153                 { VENDOR_PROSUM, VENDAPI_IDT_2, DEV_PROATM_155 },
154 };
155
156 /*
157  * Return zero if this interface is ok for us.
158  * XXX This should go away when we have full ngATM-ified the en driver.
159  */
160 static int
161 harp_check_if(const struct ifnet *ifp)
162 {
163         if (ifp->if_type == IFT_ATM && strcmp(ifp->if_dname, "en"))
164                 return (0);
165         else
166                 return (-1);
167 }
168
169 /*
170  * Instantiate a VCC stack.
171  *
172  * Could check for correct attributes here.
173  */
174 static int
175 harp_instvcc(Cmn_unit *up, Cmn_vcc *vp)
176 {
177         struct harp_softc *sc;
178
179         if (up == NULL || vp == NULL || vp->cv_connvc == NULL)
180                 return (EINVAL);
181
182         sc = (struct harp_softc *)up;
183
184         return (0);
185 }
186
187 /*
188  * Open a VCC.
189  */
190 static int
191 harp_openvcc(Cmn_unit *up, Cmn_vcc *vp)
192 {
193         struct harp_softc *sc;
194         struct atmio_openvcc data;
195         Atm_attributes *attrib;
196         struct vccb *vccinf;
197         const struct ifatm_mib *mib;
198         int err;
199
200         if (up == NULL || vp == NULL || vp->cv_connvc == NULL)
201                 return (EINVAL);
202
203         sc = (struct harp_softc *)up;
204         mib = sc->parent->if_linkmib;
205
206         attrib = &vp->cv_connvc->cvc_attr;
207         vccinf = vp->cv_connvc->cvc_vcc;
208
209         if (attrib == NULL || vccinf == NULL)
210                 return (EINVAL);
211
212         if (vccinf->vc_vpi >= (1 << mib->vpi_bits) ||
213             vccinf->vc_vci >= (1 << mib->vci_bits))
214                 return (EINVAL);
215
216         memset(&data, 0, sizeof(data));
217
218         switch (attrib->aal.type) {
219
220           case ATM_AAL0:
221                 data.param.aal = ATMIO_AAL_0;
222                 break;
223
224           case ATM_AAL5:
225                 data.param.aal = ATMIO_AAL_5;
226                 break;
227
228           default:
229                 return (EINVAL);
230         }
231         data.param.vpi = vccinf->vc_vpi;
232         data.param.vci = vccinf->vc_vci;
233         data.param.rmtu = HARP_MTU;
234         data.param.tmtu = HARP_MTU;
235
236         switch (attrib->bearer.v.bearer_class) {
237
238           case T_ATM_CLASS_C:
239                 data.param.traffic = ATMIO_TRAFFIC_VBR;
240                 break;
241
242           case T_ATM_CLASS_X:
243                 switch (attrib->bearer.v.traffic_type) {
244
245                   case T_ATM_CBR:
246                         data.param.traffic = ATMIO_TRAFFIC_CBR;
247                         break;
248
249                   case T_ATM_VBR:
250                         data.param.traffic = ATMIO_TRAFFIC_VBR;
251                         break;
252
253                   case T_ATM_ABR:
254                         /* not really supported by HARP */
255                         return (EINVAL);
256
257                   default:
258                   case T_ATM_UBR:
259                         data.param.traffic = ATMIO_TRAFFIC_UBR;
260                         break;
261                 }
262                 break;
263
264           default:
265                 return (EINVAL);
266         }
267         data.param.tparam.pcr = attrib->traffic.v.forward.PCR_all_traffic;
268         data.param.tparam.scr = attrib->traffic.v.forward.SCR_all_traffic;
269         data.param.tparam.mbs = attrib->traffic.v.forward.MBS_all_traffic;
270
271         data.rxhand = sc;
272         data.param.flags = ATMIO_FLAG_HARP;
273
274         err = (*sc->parent->if_ioctl)(sc->parent, SIOCATMOPENVCC,
275             (caddr_t)&data);
276
277         return (err);
278 }
279
280 /*
281  * Close VCC
282  */
283 static int
284 harp_closevcc(Cmn_unit *up, Cmn_vcc *vp)
285 {
286         struct harp_softc *sc;
287         struct atmio_closevcc data;
288         int err;
289
290         if (vp == NULL || vp->cv_connvc == NULL ||
291             vp->cv_connvc->cvc_vcc == NULL)
292                 return (EINVAL);
293
294         sc = (struct harp_softc *)up;
295         
296         data.vpi = vp->cv_connvc->cvc_vcc->vc_vpi;
297         data.vci = vp->cv_connvc->cvc_vcc->vc_vci;
298
299         err = (*sc->parent->if_ioctl)(sc->parent, SIOCATMCLOSEVCC,
300             (caddr_t)&data);
301
302         return (err);
303 }
304
305 /*
306  * IOCTLs
307  */
308 static int
309 harp_ioctl(int code, caddr_t addr, caddr_t arg)
310 {
311         return (ENOSYS);
312 }
313
314 /*
315  * Output data
316  */
317 static void
318 harp_output(Cmn_unit *cu, Cmn_vcc *cv, KBuffer *m)
319 {
320         struct harp_softc *sc = (struct harp_softc *)cu;
321         struct atm_pseudohdr *aph;
322         int error;
323         int mlen;
324
325         if (cv == NULL || cv->cv_connvc == NULL ||
326             cv->cv_connvc->cvc_vcc == NULL) {
327                 m_freem(m);
328                 return;
329         }
330         M_ASSERTPKTHDR(m);
331
332         /*
333          * Harp seems very broken with regard to mbuf handling. The length
334          * in the packet header is mostly broken here so recompute it.
335          */
336         m->m_pkthdr.len = mlen = m_length(m, NULL);
337
338         /*
339          * Prepend pseudo-hdr. Drivers don't care about the flags.
340          */
341         M_PREPEND(m, sizeof(*aph), M_DONTWAIT);
342         if (m == NULL)
343                 return;
344
345         aph = mtod(m, struct atm_pseudohdr *);
346         ATM_PH_VPI(aph) = cv->cv_connvc->cvc_vcc->vc_vpi;
347         ATM_PH_SETVCI(aph, cv->cv_connvc->cvc_vcc->vc_vci);
348         ATM_PH_FLAGS(aph) = 0;
349
350         error = atm_output(sc->parent, m, NULL, NULL);
351
352         if (error) {
353                 printf("%s: error %d\n", __func__, error);
354                 sc->cmn.cu_pif.pif_oerrors++;
355                 cv->cv_connvc->cvc_vcc->vc_oerrors++;
356                 if (cv->cv_connvc->cvc_vcc->vc_nif)
357                         ANIF2IFP(cv->cv_connvc->cvc_vcc->vc_nif)->if_oerrors++;
358                 return;
359         }
360
361         /* statistics */
362         sc->cmn.cu_pif.pif_opdus++;
363         sc->cmn.cu_pif.pif_obytes += mlen;
364         cv->cv_connvc->cvc_vcc->vc_opdus++;
365         cv->cv_connvc->cvc_vcc->vc_obytes += mlen;
366         if (cv->cv_connvc->cvc_vcc->vc_nif) {
367                 cv->cv_connvc->cvc_vcc->vc_nif->nif_obytes += mlen;
368                 ANIF2IFP(cv->cv_connvc->cvc_vcc->vc_nif)->if_obytes += mlen;
369                 ANIF2IFP(cv->cv_connvc->cvc_vcc->vc_nif)->if_opackets++;
370         }
371 }
372
373 /*
374  * Attach a new interface
375  */
376 static void
377 harp_attach(struct ifnet *parent)
378 {
379         struct harp_softc *sc;
380         const struct ifatm_mib *mib;
381         int error;
382
383         if (harp_check_if(parent) != 0)
384                 return;
385
386         sc = malloc(sizeof(*sc), M_HARP, M_WAITOK | M_ZERO);
387
388         sc->parent = parent;
389         sc->cmn.cu_unit = parent->if_dunit;
390         sc->cmn.cu_mtu = HARP_MTU;
391         sc->cmn.cu_ioctl = harp_ioctl;
392         sc->cmn.cu_instvcc = harp_instvcc;
393         sc->cmn.cu_openvcc = harp_openvcc;
394         sc->cmn.cu_closevcc = harp_closevcc;
395         sc->cmn.cu_output = harp_output;
396         sc->cmn.cu_vcc_zone = harp_vcc_zone;
397         sc->cmn.cu_nif_zone = harp_nif_zone;
398         sc->cmn.cu_softc = sc;
399
400         /* config */
401         mib = parent->if_linkmib;
402         if (mib->device >= sizeof(map_devs) / sizeof(map_devs[0])) {
403                 sc->cmn.cu_config.ac_vendor = VENDOR_UNKNOWN;
404                 sc->cmn.cu_config.ac_vendapi = VENDAPI_UNKNOWN;
405                 sc->cmn.cu_config.ac_device = DEV_UNKNOWN;
406         } else {
407                 sc->cmn.cu_config.ac_vendor = map_devs[mib->device].vendor;
408                 sc->cmn.cu_config.ac_vendapi = map_devs[mib->device].api;
409                 sc->cmn.cu_config.ac_device = map_devs[mib->device].dev;
410         }
411
412         switch (mib->media) {
413
414           case IFM_ATM_UTP_25:
415                 sc->cmn.cu_config.ac_media = MEDIA_UTP25;;
416                 break;
417
418           case IFM_ATM_TAXI_100:
419                 sc->cmn.cu_config.ac_media = MEDIA_TAXI_100;
420                 break;
421
422           case IFM_ATM_TAXI_140:
423                 sc->cmn.cu_config.ac_media = MEDIA_TAXI_140;
424                 break;
425
426           case IFM_ATM_MM_155:
427           case IFM_ATM_SM_155:
428                 sc->cmn.cu_config.ac_media = MEDIA_OC3C;
429                 break;
430
431           case IFM_ATM_MM_622:
432           case IFM_ATM_SM_622:
433                 sc->cmn.cu_config.ac_media = MEDIA_OC12C;
434                 break;
435
436           case IFM_ATM_UTP_155:
437                 sc->cmn.cu_config.ac_media = MEDIA_UTP155;
438                 break;
439
440           default:
441                 sc->cmn.cu_config.ac_media = MEDIA_UNKNOWN;
442                 break;
443         }
444         sc->cmn.cu_config.ac_bustype = BUS_PCI;
445         sc->cmn.cu_pif.pif_pcr = mib->pcr;
446         sc->cmn.cu_pif.pif_maxvpi = (1 << mib->vpi_bits) - 1;
447         sc->cmn.cu_pif.pif_maxvci = (1 << mib->vci_bits) - 1;
448
449         snprintf(sc->cmn.cu_config.ac_hard_vers,
450             sizeof(sc->cmn.cu_config.ac_hard_vers), "0x%lx",
451             (u_long)mib->hw_version);
452         snprintf(sc->cmn.cu_config.ac_firm_vers,
453             sizeof(sc->cmn.cu_config.ac_firm_vers), "0x%lx",
454             (u_long)mib->sw_version);
455         sc->cmn.cu_config.ac_serial = mib->serial;
456         sc->cmn.cu_config.ac_ram = 0;
457         sc->cmn.cu_config.ac_ramsize = 0;
458
459         sc->cmn.cu_config.ac_macaddr.ma_data[0] =
460             sc->cmn.cu_pif.pif_macaddr.ma_data[0] = mib->esi[0];
461         sc->cmn.cu_config.ac_macaddr.ma_data[1] =
462             sc->cmn.cu_pif.pif_macaddr.ma_data[1] = mib->esi[1];
463         sc->cmn.cu_config.ac_macaddr.ma_data[2] =
464             sc->cmn.cu_pif.pif_macaddr.ma_data[2] = mib->esi[2];
465         sc->cmn.cu_config.ac_macaddr.ma_data[3] =
466             sc->cmn.cu_pif.pif_macaddr.ma_data[3] = mib->esi[3];
467         sc->cmn.cu_config.ac_macaddr.ma_data[4] =
468             sc->cmn.cu_pif.pif_macaddr.ma_data[4] = mib->esi[4];
469         sc->cmn.cu_config.ac_macaddr.ma_data[5] =
470             sc->cmn.cu_pif.pif_macaddr.ma_data[5] = mib->esi[5];
471
472         error = atm_physif_register(&sc->cmn, parent->if_dname, harp_services);
473         if (error) {
474                 log(LOG_ERR, "%s: pif registration failed %d\n",
475                     parent->if_dname, error);
476                 free(sc, M_HARP);
477                 return;
478         }
479         LIST_INSERT_HEAD(&harp_softc_list, sc, link);
480
481         sc->cmn.cu_flags |= CUF_INITED;
482 }
483
484 /*
485  * Destroy a cloned device
486  */
487 static void
488 harp_detach(struct ifnet *ifp)
489 {
490         struct harp_softc *sc;
491         int error;
492
493         LIST_FOREACH(sc, &harp_softc_list, link)
494                 if (sc->parent == ifp)
495                         break;
496         if (sc == NULL)
497                 return;
498
499         error = atm_physif_deregister(&sc->cmn);
500         if (error)
501                 log(LOG_ERR, "%s: de-registration failed %d\n", ifp->if_dname,
502                     error);
503
504         LIST_REMOVE(sc, link);
505
506         free(sc, M_HARP);
507 }
508
509 /*
510  * Pass PDU up the stack
511  */
512 static void
513 harp_recv_stack(void *tok, KBuffer *m)
514 {
515         Cmn_vcc *vcc = tok;
516         int err;
517
518         M_ASSERTPKTHDR(m);
519         STACK_CALL(CPCS_UNITDATA_SIG, vcc->cv_upper, vcc->cv_toku,
520             vcc->cv_connvc, (intptr_t)m, 0, err);
521         if (err) {
522                 printf("%s: error %d\n", __func__, err);
523                 KB_FREEALL(m);
524         }
525 }
526
527 /*
528  * Possible input from NATM
529  */
530 static void
531 harp_input(struct ifnet *ifp, struct mbuf **mp, struct atm_pseudohdr *ah,
532     void *rxhand)
533 {
534         struct harp_softc *sc = rxhand;
535         Cmn_vcc *vcc;
536         char *cp;
537         u_int pfxlen;
538         struct mbuf *m, *m0;
539         int mlen;
540
541         if ((ATM_PH_FLAGS(ah) & ATMIO_FLAG_HARP) == 0)
542                 return;
543
544         /* grab the packet */
545         m = *mp;
546         *mp = NULL;
547
548         if (sc->parent != ifp) {
549                 printf("%s: parent=%p ifp=%p\n", __func__, sc->parent, ifp);
550                 goto drop;
551         }
552
553         vcc = atm_dev_vcc_find(&sc->cmn, ATM_PH_VPI(ah),
554             ATM_PH_VCI(ah), VCC_IN);
555         if (vcc == NULL) {
556                 printf("%s: VCC %u/%u not found\n", __func__,ATM_PH_VPI(ah),
557                     ATM_PH_VCI(ah));
558                 goto drop;
559         }
560
561         /* fit two pointers into the mbuf - assume, that the the data is
562          * pointer aligned. If it doesn't fit into the first mbuf, prepend
563          * another one.
564          * Don't count the new fields in the packet length (XXX)
565          */
566         mlen = m->m_pkthdr.len;
567         pfxlen = sizeof(atm_intr_func_t) + sizeof(void *);
568         if (M_LEADINGSPACE(m) < pfxlen) {
569                 MGETHDR(m0, 0, MT_DATA);
570                 if (m0 == NULL) {
571                         printf("%s: no leading space in buffer\n", __func__);
572                         goto drop;
573                 }
574                 m0->m_len = 0;
575                 m0->m_next = m;
576
577                 M_MOVE_PKTHDR(m0, m);
578
579                 m = m0;
580         }
581         m->m_len += pfxlen;
582         m->m_data -= pfxlen;
583         cp = mtod(m, char *);
584         *((atm_intr_func_t *)cp) = harp_recv_stack;
585         cp += sizeof(atm_intr_func_t);
586         *((void **)cp) = (void *)vcc;
587
588         /* count the packet */
589         sc->cmn.cu_pif.pif_ipdus++;
590         sc->cmn.cu_pif.pif_ibytes += mlen;
591         vcc->cv_connvc->cvc_vcc->vc_ipdus++;
592         vcc->cv_connvc->cvc_vcc->vc_ibytes += mlen;
593         if (vcc->cv_connvc->cvc_vcc->vc_nif) {
594                 vcc->cv_connvc->cvc_vcc->vc_nif->nif_ibytes += mlen;
595                 ANIF2IFP(vcc->cv_connvc->cvc_vcc->vc_nif)->if_ipackets++;
596                 ANIF2IFP(vcc->cv_connvc->cvc_vcc->vc_nif)->if_ibytes += mlen;
597         }
598
599         /* hand it off */
600         netisr_dispatch(NETISR_ATM, m);
601         return;
602
603   drop:
604         m_freem(m);
605 }
606
607 /*
608  * Module loading/unloading
609  */
610 static int
611 harp_modevent(module_t mod, int event, void *data)
612 {
613         struct ifnet *ifp;
614
615         switch (event) {
616
617           case MOD_LOAD:
618                 harp_nif_zone = uma_zcreate("harp nif", sizeof(struct atm_nif),
619                     NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
620                 if (harp_nif_zone == NULL)
621                         panic("%s: nif_zone", __func__);
622
623                 harp_vcc_zone = uma_zcreate("harp vcc", sizeof(struct harp_vcc),
624                     NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
625                 if (harp_vcc_zone == NULL)
626                         panic("%s: vcc_zone", __func__);
627
628                 /* Create harp interfaces for all existing ATM interfaces */
629                 TAILQ_FOREACH(ifp, &ifnet, if_link)
630                         harp_attach(ifp);
631
632                 atm_harp_attach_p = harp_attach;
633                 atm_harp_detach_p = harp_detach;
634                 atm_harp_input_p = harp_input;
635                 break;
636
637           case MOD_UNLOAD:
638                 atm_harp_attach_p = NULL;
639                 atm_harp_detach_p = NULL;
640                 atm_harp_input_p = NULL;
641
642                 while (!LIST_EMPTY(&harp_softc_list))
643                         harp_detach(LIST_FIRST(&harp_softc_list)->parent);
644
645                 uma_zdestroy(harp_nif_zone);
646                 uma_zdestroy(harp_vcc_zone);
647
648                 break;
649           default:
650                 return (EOPNOTSUPP);
651         }
652         return (0);
653 }
654
655 static moduledata_t harp_mod = {
656         "if_harp",
657         harp_modevent,
658         0
659 };
660
661 DECLARE_MODULE(harp, harp_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);