FreeBSD/Linux Kernel Cross Reference
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: releng/6.2/sys/dev/harp/if_harp.c 147256 2005-06-10 16:49:24Z brooks $");
35
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD: releng/6.2/sys/dev/harp/if_harp.c 147256 2005-06-10 16:49:24Z brooks $");
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);
Cache object: 973b15a84bdd724e8cee94a06a025299
|