diff --git a/src/libs/compat/freebsd_network/Jamfile b/src/libs/compat/freebsd_network/Jamfile index 0693c52063..be132d0ba3 100644 --- a/src/libs/compat/freebsd_network/Jamfile +++ b/src/libs/compat/freebsd_network/Jamfile @@ -11,6 +11,7 @@ Library libfreebsd_network.a : bus.c compat.c device.c + if_media.c mbuf.c mutex.c ; diff --git a/src/libs/compat/freebsd_network/bus.c b/src/libs/compat/freebsd_network/bus.c index 8d9100b1fc..bbbad7f2a3 100644 --- a/src/libs/compat/freebsd_network/bus.c +++ b/src/libs/compat/freebsd_network/bus.c @@ -17,6 +17,9 @@ #include +#define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1)) + + struct internal_intr { driver_intr_t handler; void *arg; @@ -24,6 +27,26 @@ struct internal_intr { }; +static area_id +map_mem(void **virtualAddr, void *_phy, size_t size, uint32 protection, + const char *name) +{ + uint32 offset = (uint32)_phy & (B_PAGE_SIZE - 1); + void *physicalAddr = (uint8 *)_phy - offset; + area_id area; + + size = ROUNDUP(size + offset, B_PAGE_SIZE); + area = map_physical_memory(name, physicalAddr, size, B_ANY_KERNEL_ADDRESS, + protection, virtualAddr); + if (area < B_OK) + return area; + + *virtualAddr = (uint8 *)(*virtualAddr) + offset; + + return area; +} + + /* straight from Marcus' ipro1000 driver */ struct resource * bus_alloc_resource(device_t dev, int type, int *rid, unsigned long start, diff --git a/src/libs/compat/freebsd_network/compat.c b/src/libs/compat/freebsd_network/compat.c index e3d6b05fe1..3c186723ae 100644 --- a/src/libs/compat/freebsd_network/compat.c +++ b/src/libs/compat/freebsd_network/compat.c @@ -74,6 +74,22 @@ device_printf(device_t dev, const char *format, ...) } +void * +_kernel_malloc(size_t size, int flags) +{ + // our kernel malloc() is insufficent + + return malloc(size); +} + + +void +_kernel_free(void *ptr) +{ + free(ptr); +} + + status_t init_compat_layer() { diff --git a/src/libs/compat/freebsd_network/compat/net/if.h b/src/libs/compat/freebsd_network/compat/net/if.h index 8586313c4d..6740ac83a7 100644 --- a/src/libs/compat/freebsd_network/compat/net/if.h +++ b/src/libs/compat/freebsd_network/compat/net/if.h @@ -6,6 +6,11 @@ #include /* for timeval */ +#define IF_Kbps(x) ((x) * 1000) +#define IF_Mbps(x) (IF_Kbps((x) * 1000)) +#define IF_Gbps(x) (IF_Mbps((x) * 1000)) + + /* Capabilities that interfaces can advertise. */ #define IFCAP_RXCSUM 0x0001 /* can offload checksum on RX */ #define IFCAP_TXCSUM 0x0002 /* can offload checksum on TX */ diff --git a/src/libs/compat/freebsd_network/compat/sys/malloc.h b/src/libs/compat/freebsd_network/compat/sys/malloc.h index 1b7315dfcd..d6dda4f149 100644 --- a/src/libs/compat/freebsd_network/compat/sys/malloc.h +++ b/src/libs/compat/freebsd_network/compat/sys/malloc.h @@ -1,8 +1,20 @@ #ifndef _FBSD_COMPAT_SYS_MALLOC_H_ #define _FBSD_COMPAT_SYS_MALLOC_H_ +#include + #define M_NOWAIT 0x0001 #define M_WAITOK 0x0002 #define M_ZERO 0x0100 + +void *_kernel_malloc(size_t size, int flags); +void _kernel_free(void *ptr); + +#define kernel_malloc(size, base, flags) \ + _kernel_malloc(size, flags) + +#define kernel_free( ptr, base) \ + _kernel_free(ptr) + #endif diff --git a/src/libs/compat/freebsd_network/compat/sys/mbuf.h b/src/libs/compat/freebsd_network/compat/sys/mbuf.h index 78763af84a..e9b9a7b0d6 100644 --- a/src/libs/compat/freebsd_network/compat/sys/mbuf.h +++ b/src/libs/compat/freebsd_network/compat/sys/mbuf.h @@ -2,12 +2,27 @@ #define _FBSD_COMPAT_SYS_MBUF_H_ #include +#include -struct m_pkthdr { +#define MLEN (MSIZE - sizeof(struct m_hdr)) +#define MHLEN (MSIZE - sizeof(struct pkthdr)) + +#ifdef _KERNEL + +struct m_hdr { + struct mbuf * mh_next; + struct mbuf * mh_nextpkt; + void * mh_data; + int mh_len; + int mh_flags; + short mh_type; +}; + +struct pkthdr { + struct ifnet * rcvif; int len; int csum_flags; uint16_t csum_data; - struct ifnet * rcvif; }; struct m_ext { @@ -16,16 +31,32 @@ struct m_ext { }; struct mbuf { - struct mbuf * m_next; - struct mbuf * m_nextpkt; - int m_len; - int m_flags; - void * m_data; - - struct m_pkthdr m_pkthdr; - struct m_ext m_ext; + struct m_hdr m_hdr; + union { + struct { + struct pkthdr MH_pkthdr; + union { + struct m_ext MH_ext; + char MH_databuf[MHLEN]; + } MH_dat; + } MH; + char M_databuf[MLEN]; + } M_dat; }; +#define m_next m_hdr.mh_next +#define m_len m_hdr.mh_len +#define m_data m_hdr.mh_data +#define m_type m_hdr.mh_type +#define m_flags m_hdr.mh_flags +#define m_nextpkt m_hdr.mh_nextpkt +#define m_act m_nextpkt +#define m_pkthdr M_dat.MH.MH_pkthdr +#define m_ext M_dat.MH.MH_dat.MH_ext +#define m_pktdat M_dat.MH.MH_dat.MH_databuf +#define m_dat M_dat.M_databuf + + #define M_DONTWAIT M_NOWAIT #define M_TRYWAIT M_WAITOK #define M_WAIT M_WAITOK @@ -51,3 +82,5 @@ struct mbuf *m_defrag(struct mbuf *m, int); #define mtod(m, type) (type)((m)->m_data) #endif + +#endif diff --git a/src/libs/compat/freebsd_network/if_media.c b/src/libs/compat/freebsd_network/if_media.c new file mode 100644 index 0000000000..bb64b2d6fd --- /dev/null +++ b/src/libs/compat/freebsd_network/if_media.c @@ -0,0 +1,563 @@ +/* $NetBSD: if_media.c,v 1.1 1997/03/17 02:55:15 thorpej Exp $ */ +/* $FreeBSD: src/sys/net/if_media.c,v 1.21.2.1 2006/03/17 20:17:43 glebius Exp $ */ + +/*- + * Copyright (c) 1997 + * Jonathan Stone and Jason R. Thorpe. All rights reserved. + * + * This software is derived from information provided by Matt Thomas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jonathan Stone + * and Jason R. Thorpe for the NetBSD Project. + * 4. The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * BSD/OS-compatible network interface media selection. + * + * Where it is safe to do so, this code strays slightly from the BSD/OS + * design. Software which uses the API (device drivers, basically) + * shouldn't notice any difference. + * + * Many thanks to Matt Thomas for providing the information necessary + * to implement this interface. + */ + +#include +#include +#include +#include +#include + +#include +#include + +/* + * Compile-time options: + * IFMEDIA_DEBUG: + * turn on implementation-level debug printfs. + * Useful for debugging newly-ported drivers. + */ + +static struct ifmedia_entry *ifmedia_match(struct ifmedia *ifm, + int flags, int mask); + +#ifdef IFMEDIA_DEBUG +int ifmedia_debug = 0; +static void ifmedia_printword(int); +#endif + +/* + * Initialize if_media struct for a specific interface instance. + */ +void +ifmedia_init(ifm, dontcare_mask, change_callback, status_callback) + struct ifmedia *ifm; + int dontcare_mask; + ifm_change_cb_t change_callback; + ifm_stat_cb_t status_callback; +{ + + LIST_INIT(&ifm->ifm_list); + ifm->ifm_cur = NULL; + ifm->ifm_media = 0; + ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */ + ifm->ifm_change = change_callback; + ifm->ifm_status = status_callback; +} + +void +ifmedia_removeall(ifm) + struct ifmedia *ifm; +{ + struct ifmedia_entry *entry; + + for (entry = LIST_FIRST(&ifm->ifm_list); entry; + entry = LIST_FIRST(&ifm->ifm_list)) { + LIST_REMOVE(entry, ifm_list); + kernel_free(entry, M_IFADDR); + } +} + +/* + * Add a media configuration to the list of supported media + * for a specific interface instance. + */ +void +ifmedia_add(ifm, mword, data, aux) + struct ifmedia *ifm; + int mword; + int data; + void *aux; +{ + register struct ifmedia_entry *entry; + +#ifdef IFMEDIA_DEBUG + if (ifmedia_debug) { + if (ifm == NULL) { + printf("ifmedia_add: null ifm\n"); + return; + } + printf("Adding entry for "); + ifmedia_printword(mword); + } +#endif + + entry = kernel_malloc(sizeof(*entry), M_IFADDR, M_NOWAIT); + if (entry == NULL) + panic("ifmedia_add: can't malloc entry"); + + entry->ifm_media = mword; + entry->ifm_data = data; + entry->ifm_aux = aux; + + LIST_INSERT_HEAD(&ifm->ifm_list, entry, ifm_list); +} + +/* + * Add an array of media configurations to the list of + * supported media for a specific interface instance. + */ +void +ifmedia_list_add(ifm, lp, count) + struct ifmedia *ifm; + struct ifmedia_entry *lp; + int count; +{ + int i; + + for (i = 0; i < count; i++) + ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data, + lp[i].ifm_aux); +} + +/* + * Set the default active media. + * + * Called by device-specific code which is assumed to have already + * selected the default media in hardware. We do _not_ call the + * media-change callback. + */ +void +ifmedia_set(ifm, target) + struct ifmedia *ifm; + int target; + +{ + struct ifmedia_entry *match; + + match = ifmedia_match(ifm, target, ifm->ifm_mask); + + if (match == NULL) { + printf("ifmedia_set: no match for 0x%x/0x%x\n", + target, ~ifm->ifm_mask); + panic("ifmedia_set"); + } + ifm->ifm_cur = match; + +#ifdef IFMEDIA_DEBUG + if (ifmedia_debug) { + printf("ifmedia_set: target "); + ifmedia_printword(target); + printf("ifmedia_set: setting to "); + ifmedia_printword(ifm->ifm_cur->ifm_media); + } +#endif +} + +/* + * Device-independent media ioctl support function. + */ +int +ifmedia_ioctl(ifp, ifr, ifm, cmd) + struct ifnet *ifp; + struct ifreq *ifr; + struct ifmedia *ifm; + u_long cmd; +{ +#if 0 + struct ifmedia_entry *match; + struct ifmediareq *ifmr = (struct ifmediareq *) ifr; + int error = 0, sticky; + + if (ifp == NULL || ifr == NULL || ifm == NULL) + return(EINVAL); + + switch (cmd) { + + /* + * Set the current media. + */ + case SIOCSIFMEDIA: + { + struct ifmedia_entry *oldentry; + int oldmedia; + int newmedia = ifr->ifr_media; + + match = ifmedia_match(ifm, newmedia, ifm->ifm_mask); + if (match == NULL) { +#ifdef IFMEDIA_DEBUG + if (ifmedia_debug) { + printf( + "ifmedia_ioctl: no media found for 0x%x\n", + newmedia); + } +#endif + return (ENXIO); + } + + /* + * If no change, we're done. + * XXX Automedia may invole software intervention. + * Keep going in case the the connected media changed. + * Similarly, if best match changed (kernel debugger?). + */ + if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) && + (newmedia == ifm->ifm_media) && + (match == ifm->ifm_cur)) + return 0; + + /* + * We found a match, now make the driver switch to it. + * Make sure to preserve our old media type in case the + * driver can't switch. + */ +#ifdef IFMEDIA_DEBUG + if (ifmedia_debug) { + printf("ifmedia_ioctl: switching %s to ", + ifp->if_xname); + ifmedia_printword(match->ifm_media); + } +#endif + oldentry = ifm->ifm_cur; + oldmedia = ifm->ifm_media; + ifm->ifm_cur = match; + ifm->ifm_media = newmedia; + error = (*ifm->ifm_change)(ifp); + if (error) { + ifm->ifm_cur = oldentry; + ifm->ifm_media = oldmedia; + } + break; + } + + /* + * Get list of available media and current media on interface. + */ + case SIOCGIFMEDIA: + { + struct ifmedia_entry *ep; + int *kptr, count; + int usermax; /* user requested max */ + + kptr = NULL; /* XXX gcc */ + + ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? + ifm->ifm_cur->ifm_media : IFM_NONE; + ifmr->ifm_mask = ifm->ifm_mask; + ifmr->ifm_status = 0; + (*ifm->ifm_status)(ifp, ifmr); + + count = 0; + usermax = 0; + + /* + * If there are more interfaces on the list, count + * them. This allows the caller to set ifmr->ifm_count + * to 0 on the first call to know how much space to + * allocate. + */ + LIST_FOREACH(ep, &ifm->ifm_list, ifm_list) + usermax++; + + /* + * Don't allow the user to ask for too many + * or a negative number. + */ + if (ifmr->ifm_count > usermax) + ifmr->ifm_count = usermax; + else if (ifmr->ifm_count < 0) + return (EINVAL); + + if (ifmr->ifm_count != 0) { + kptr = (int *)kernel_malloc(ifmr->ifm_count * sizeof(int), + M_TEMP, M_NOWAIT); + + if (kptr == NULL) + return (ENOMEM); + /* + * Get the media words from the interface's list. + */ + ep = LIST_FIRST(&ifm->ifm_list); + for (; ep != NULL && count < ifmr->ifm_count; + ep = LIST_NEXT(ep, ifm_list), count++) + kptr[count] = ep->ifm_media; + + if (ep != NULL) + error = E2BIG; /* oops! */ + } else { + count = usermax; + } + + /* + * We do the copyout on E2BIG, because that's + * just our way of telling userland that there + * are more. This is the behavior I've observed + * under BSD/OS 3.0 + */ + sticky = error; + if ((error == 0 || error == E2BIG) && ifmr->ifm_count != 0) { + error = copyout((caddr_t)kptr, + (caddr_t)ifmr->ifm_ulist, + ifmr->ifm_count * sizeof(int)); + } + + if (error == 0) + error = sticky; + + if (ifmr->ifm_count != 0) + kernel_free(kptr, M_TEMP); + + ifmr->ifm_count = count; + break; + } + + default: + return (EINVAL); + } + + return (error); +#endif + return ENOTSUP; +} + +/* + * Find media entry matching a given ifm word. + * + */ +static struct ifmedia_entry * +ifmedia_match(ifm, target, mask) + struct ifmedia *ifm; + int target; + int mask; +{ + struct ifmedia_entry *match, *next; + + match = NULL; + mask = ~mask; + + LIST_FOREACH(next, &ifm->ifm_list, ifm_list) { + if ((next->ifm_media & mask) == (target & mask)) { +#if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC) + if (match) { + printf("ifmedia_match: multiple match for " + "0x%x/0x%x\n", target, mask); + } +#endif + match = next; + } + } + + return match; +} + +/* + * Compute the interface `baudrate' from the media, for the interface + * metrics (used by routing daemons). + */ +static const struct ifmedia_baudrate ifmedia_baudrate_descriptions[] = + IFM_BAUDRATE_DESCRIPTIONS; + +uint64_t +ifmedia_baudrate(int mword) +{ + int i; + + for (i = 0; ifmedia_baudrate_descriptions[i].ifmb_word != 0; i++) { + if ((mword & (IFM_NMASK|IFM_TMASK)) == + ifmedia_baudrate_descriptions[i].ifmb_word) + return (ifmedia_baudrate_descriptions[i].ifmb_baudrate); + } + + /* Not known. */ + return (0); +} + +#ifdef IFMEDIA_DEBUG +struct ifmedia_description ifm_type_descriptions[] = + IFM_TYPE_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ethernet_descriptions[] = + IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] = + IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_tokenring_descriptions[] = + IFM_SUBTYPE_TOKENRING_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] = + IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_fddi_descriptions[] = + IFM_SUBTYPE_FDDI_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_fddi_option_descriptions[] = + IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_descriptions[] = + IFM_SUBTYPE_IEEE80211_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] = + IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] = + IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_atm_descriptions[] = + IFM_SUBTYPE_ATM_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_atm_option_descriptions[] = + IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_shared_descriptions[] = + IFM_SUBTYPE_SHARED_DESCRIPTIONS; + +struct ifmedia_description ifm_shared_option_descriptions[] = + IFM_SHARED_OPTION_DESCRIPTIONS; + +struct ifmedia_type_to_subtype { + struct ifmedia_description *subtypes; + struct ifmedia_description *options; + struct ifmedia_description *modes; +}; + +/* must be in the same order as IFM_TYPE_DESCRIPTIONS */ +struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { + { + &ifm_subtype_ethernet_descriptions[0], + &ifm_subtype_ethernet_option_descriptions[0], + NULL, + }, + { + &ifm_subtype_tokenring_descriptions[0], + &ifm_subtype_tokenring_option_descriptions[0], + NULL, + }, + { + &ifm_subtype_fddi_descriptions[0], + &ifm_subtype_fddi_option_descriptions[0], + NULL, + }, + { + &ifm_subtype_ieee80211_descriptions[0], + &ifm_subtype_ieee80211_option_descriptions[0], + &ifm_subtype_ieee80211_mode_descriptions[0] + }, + { + &ifm_subtype_atm_descriptions[0], + &ifm_subtype_atm_option_descriptions[0], + NULL, + }, +}; + +/* + * print a media word. + */ +static void +ifmedia_printword(ifmw) + int ifmw; +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int seen_option = 0; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (IFM_TYPE(ifmw) == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) { + printf("\n"); + return; + } + printf(desc->ifmt_string); + + /* Any mode. */ + for (desc = ttos->modes; desc && desc->ifmt_string != NULL; desc++) + if (IFM_MODE(ifmw) == desc->ifmt_word) { + if (desc->ifmt_string != NULL) + printf(" mode %s", desc->ifmt_string); + break; + } + + /* + * Check for the shared subtype descriptions first, then the + * type-specific ones. + */ + for (desc = ifm_subtype_shared_descriptions; + desc->ifmt_string != NULL; desc++) + if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) + goto got_subtype; + + for (desc = ttos->subtypes; desc->ifmt_string != NULL; desc++) + if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) { + printf(" \n"); + return; + } + + got_subtype: + printf(" %s", desc->ifmt_string); + + /* + * Look for shared options. + */ + for (desc = ifm_shared_option_descriptions; + desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + if (seen_option == 0) + printf(" <"); + printf("%s%s", seen_option++ ? "," : "", + desc->ifmt_string); + } + } + + /* + * Look for subtype-specific options. + */ + for (desc = ttos->options; desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + if (seen_option == 0) + printf(" <"); + printf("%s%s", seen_option++ ? "," : "", + desc->ifmt_string); + } + } + printf("%s\n", seen_option ? ">" : ""); +} +#endif /* IFMEDIA_DEBUG */ diff --git a/src/libs/compat/freebsd_network/mbuf.c b/src/libs/compat/freebsd_network/mbuf.c index 8343c721ae..32c13905f0 100644 --- a/src/libs/compat/freebsd_network/mbuf.c +++ b/src/libs/compat/freebsd_network/mbuf.c @@ -24,6 +24,43 @@ static object_cache *sMBufCache; static object_cache *sChunkCache; +static void +construct_mbuf(struct mbuf *mb, short type, int flags) +{ + mb->m_next = NULL; + mb->m_nextpkt = NULL; + mb->m_len = 0; + mb->m_flags = flags; + mb->m_type = type; + + if (flags & M_PKTHDR) { + mb->m_data = mb->m_pktdat; + memset(&mb->m_pkthdr, 0, sizeof(mb->m_pkthdr)); + } else { + mb->m_data = mb->m_dat; + } +} + + +struct mbuf * +m_getcl(int how, short type, int flags) +{ + uint32 cacheFlags = 0; + struct mbuf *mb; + + if (how & M_NOWAIT) + cacheFlags = CACHE_DONT_SLEEP; + + mb = (struct mbuf *)object_cache_alloc(sMBufCache, cacheFlags); + if (mb == NULL) + return NULL; + + construct_mbuf(mb, type, flags); + + return mb; +} + + void m_freem(struct mbuf *mp) {