Index: head/share/man/man4/ng_ksocket.4 =================================================================== --- head/share/man/man4/ng_ksocket.4 (revision 131107) +++ head/share/man/man4/ng_ksocket.4 (revision 131108) @@ -1,200 +1,219 @@ .\" Copyright (c) 1999 Whistle Communications, Inc. .\" All rights reserved. .\" .\" Subject to the following obligations and disclaimer of warranty, use and .\" redistribution of this software, in source or object code forms, with or .\" without modifications are expressly permitted by Whistle Communications; .\" provided, however, that: .\" 1. Any and all reproductions of the source or object code must include the .\" copyright notice above and the following disclaimer of warranties; and .\" 2. No rights are granted, in any manner or form, to use Whistle .\" Communications, Inc. trademarks, including the mark "WHISTLE .\" COMMUNICATIONS" on advertising, endorsements, or otherwise except as .\" such appears in the above copyright notice or in the software. .\" .\" THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND .\" TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO .\" REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, .\" INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. .\" WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY .\" REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS .\" SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. .\" IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES .\" RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING .\" WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, .\" PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR .\" SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY .\" OF SUCH DAMAGE. .\" .\" Author: Archie Cobbs .\" .\" $FreeBSD$ .\" -.Dd November 15, 1999 +.Dd June 8, 2004 .Dt NG_KSOCKET 4 .Os .Sh NAME .Nm ng_ksocket .Nd kernel socket netgraph node type .Sh SYNOPSIS .In netgraph/ng_ksocket.h .Sh DESCRIPTION A .Nm ksocket node is both a netgraph node and a .Bx socket. The .Nm node type allows one to open a socket inside the kernel and have it appear as a Netgraph node. The .Nm node type is the reverse of the socket node type (see .Xr ng_socket 4 ) : whereas the socket node type enables the user-level manipulation (via a socket) of what is normally a kernel-level entity (the associated Netgraph node), the .Nm node type enables the kernel-level manipulation (via a Netgraph node) of what is normally a user-level entity (the associated socket). .Pp A .Nm node allows at most one hook connection. Connecting to the node is equivalent to opening the associated socket. The name given to the hook determines what kind of socket the node will open (see below). When the hook is disconnected and/or the node is shutdown, the associated socket is closed. .Sh HOOKS This node type supports a single hook connection at a time. The name of the hook must be of the form .Em // , where the .Em family , .Em type , and .Em proto are the decimal equivalent of the same arguments to .Xr socket 2 . Alternately, aliases for the commonly used values are accepted as well. For example .Dv inet/dgram/udp is a more readable but equivalent version of .Dv 2/2/17 . +.Pp +Data received into socket is sent out via hook. +Data received on hook is sent out from socket, if the latter is +connected (an +.Dv NGM_KSOCKET_CONNECT +was sent to node before). +If socket is not connected, destination +.Dv "struct sockaddr" +must be supplied in an mbuf tag with cookie +.Dv NGM_KSOCKET_COOKIE +and type +.Dv NG_KSOCKET_TAG_SOCKADDR +attached to data. +Otherwise +.Nm +will return +.Dv ENOTCONN +to sender. .Sh CONTROL MESSAGES This node type supports the generic control messages, plus the following: .Bl -tag -width foo .It Dv NGM_KSOCKET_BIND This functions exactly like the .Xr bind 2 system call. The .Dv "struct sockaddr" socket address parameter should be supplied as an argument. .It Dv NGM_KSOCKET_LISTEN This functions exactly like the .Xr listen 2 system call. The backlog parameter (a single 32 bit .Dv int ) should be supplied as an argument. .It Dv NGM_KSOCKET_CONNECT This functions exactly like the .Xr connect 2 system call. The .Dv "struct sockaddr" destination address parameter should be supplied as an argument. .It Dv NGM_KSOCKET_ACCEPT Currently unimplemented. .It Dv NGM_KSOCKET_GETNAME Equivalent to the .Xr getsockname 2 system call. The name is returned as a .Dv "struct sockaddr" in the arguments field of the reply. .It Dv NGM_KSOCKET_GETPEERNAME Equivalent to the .Xr getpeername 2 system call. The name is returned as a .Dv "struct sockaddr" in the arguments field of the reply. .It Dv NGM_KSOCKET_SETOPT Equivalent to the .Xr setsockopt 2 system call, except that the option name, level, and value are passed in a .Dv "struct ng_ksocket_sockopt" . .It Dv NGM_KSOCKET_GETOPT Equivalent to the .Xr getsockopt 2 system call, except that the option is passed in a .Dv "struct ng_ksocket_sockopt" . When sending this command, the .Dv value field should be empty; upon return, it will contain the retrieved value. .El .Sh ASCII FORM CONTROL MESSAGES For control messages that pass a .Dv "struct sockaddr" in the argument field, the normal .Tn ASCII equivalent of the C structure is an acceptable form. For the .Dv PF_INET and .Dv PF_LOCAL address families, a more convenient form is also used, which is the protocol family name, followed by a slash, followed by the actual address. For .Dv PF_INET , the address is an IP address followed by an optional colon and port number. For .Dv PF_LOCAL , the address is the pathname as a doubly quoted string. .Pp Examples: .Bl -tag -width XXXXXXXXXX .It Dv PF_LOCAL local/"/tmp/foo.socket" .It Dv PF_INET inet/192.168.1.1:1234 .It Other .Dv "\&{ family=16 len=16 data=[0x70 0x00 0x01 0x23] \&}" .El .Pp For control messages that pass a .Dv "struct ng_ksocket_sockopt" , the normal .Tn ASCII form for that structure is used. In the future, more convenient encoding of the more common socket options may be supported. .Sh SHUTDOWN This node shuts down upon receipt of a .Dv NGM_SHUTDOWN control message, or when the hook is disconnected. Shutdown of the node closes the associated socket. .Sh SEE ALSO .Xr socket 2 , .Xr netgraph 4 , .Xr ng_socket 4 , -.Xr ngctl 8 +.Xr ngctl 8 , +.Xr mbuf_tags 9 .Sh HISTORY The .Nm node type was implemented in .Fx 4.0 . .Sh AUTHORS .An Archie Cobbs Aq archie@FreeBSD.org Index: head/sys/dev/ar/if_ar.c =================================================================== --- head/sys/dev/ar/if_ar.c (revision 131107) +++ head/sys/dev/ar/if_ar.c (revision 131108) @@ -1,2363 +1,2362 @@ /* * Copyright (c) 1995 - 2001 John Hay. All rights reserved. * * 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. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY John Hay ``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 John Hay 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. */ #include __FBSDID("$FreeBSD$"); /* * Programming assumptions and other issues. * * The descriptors of a DMA channel will fit in a 16K memory window. * * The buffers of a transmit DMA channel will fit in a 16K memory window. * * Only the ISA bus cards with X.21 and V.35 is tested. * * When interface is going up, handshaking is set and it is only cleared * when the interface is down'ed. * * There should be a way to set/reset Raw HDLC/PPP, Loopback, DCE/DTE, * internal/external clock, etc..... */ #include "opt_netgraph.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NETGRAPH #include #include #include #include #else /* NETGRAPH */ #include #include #endif /* NETGRAPH */ #include #include #include #ifdef TRACE #define TRC(x) x #else #define TRC(x) #endif #define TRCL(x) x #define PPP_HEADER_LEN 4 devclass_t ar_devclass; struct ar_softc { #ifndef NETGRAPH struct sppp ifsppp; #endif /* NETGRAPH */ int unit; /* With regards to all ar devices */ int subunit; /* With regards to this card */ struct ar_hardc *hc; struct buf_block { u_int txdesc; /* On card address */ u_int txstart; /* On card address */ u_int txend; /* On card address */ u_int txtail; /* Index of first unused buffer */ u_int txmax; /* number of usable buffers/descriptors */ u_int txeda; /* Error descriptor addresses */ }block[AR_TX_BLOCKS]; char xmit_busy; /* Transmitter is busy */ char txb_inuse; /* Number of tx blocks currently in use */ u_char txb_new; /* Index to where new buffer will be added */ u_char txb_next_tx; /* Index to next block ready to tx */ u_int rxdesc; /* On card address */ u_int rxstart; /* On card address */ u_int rxend; /* On card address */ u_int rxhind; /* Index to the head of the rx buffers. */ u_int rxmax; /* number of usable buffers/descriptors */ int scano; int scachan; sca_regs *sca; #ifdef NETGRAPH int running; /* something is attached so we are running */ int dcd; /* do we have dcd? */ /* ---netgraph bits --- */ char nodename[NG_NODESIZ]; /* store our node name */ int datahooks; /* number of data hooks attached */ node_p node; /* netgraph node */ hook_p hook; /* data hook */ hook_p debug_hook; struct ifqueue xmitq_hipri; /* hi-priority transmit queue */ struct ifqueue xmitq; /* transmit queue */ int flags; /* state */ #define SCF_RUNNING 0x01 /* board is active */ #define SCF_OACTIVE 0x02 /* output is active */ int out_dog; /* watchdog cycles output count-down */ struct callout_handle handle; /* timeout(9) handle */ u_long inbytes, outbytes; /* stats */ u_long lastinbytes, lastoutbytes; /* a second ago */ u_long inrate, outrate; /* highest rate seen */ u_long inlast; /* last input N secs ago */ u_long out_deficit; /* output since last input */ u_long oerrors, ierrors[6]; u_long opackets, ipackets; #endif /* NETGRAPH */ }; static int next_ar_unit = 0; #ifdef NETGRAPH #define DOG_HOLDOFF 6 /* dog holds off for 6 secs */ #define QUITE_A_WHILE 300 /* 5 MINUTES */ #define LOTS_OF_PACKETS 100 #endif /* NETGRAPH */ /* * This translate from irq numbers to * the value that the arnet card needs * in the lower part of the AR_INT_SEL * register. */ static int irqtable[16] = { 0, /* 0 */ 0, /* 1 */ 0, /* 2 */ 1, /* 3 */ 0, /* 4 */ 2, /* 5 */ 0, /* 6 */ 3, /* 7 */ 0, /* 8 */ 0, /* 9 */ 4, /* 10 */ 5, /* 11 */ 6, /* 12 */ 0, /* 13 */ 0, /* 14 */ 7 /* 15 */ }; #ifndef NETGRAPH MODULE_DEPEND(if_ar, sppp, 1, 1, 1); #else MODULE_DEPEND(ng_sync_ar, netgraph, 1, 1, 1); #endif static void arintr(void *arg); static void ar_xmit(struct ar_softc *sc); #ifndef NETGRAPH static void arstart(struct ifnet *ifp); static int arioctl(struct ifnet *ifp, u_long cmd, caddr_t data); static void arwatchdog(struct ifnet *ifp); #else /* NETGRAPH */ static void arstart(struct ar_softc *sc); static void arwatchdog(struct ar_softc *sc); #endif /* NETGRAPH */ static int ar_packet_avail(struct ar_softc *sc, int *len, u_char *rxstat); static void ar_copy_rxbuf(struct mbuf *m, struct ar_softc *sc, int len); static void ar_eat_packet(struct ar_softc *sc, int single); static void ar_get_packets(struct ar_softc *sc); static int ar_read_pim_iface(volatile struct ar_hardc *hc, int channel); static void ar_up(struct ar_softc *sc); static void ar_down(struct ar_softc *sc); static void arc_init(struct ar_hardc *hc); static void ar_init_sca(struct ar_hardc *hc, int scano); static void ar_init_msci(struct ar_softc *sc); static void ar_init_rx_dmac(struct ar_softc *sc); static void ar_init_tx_dmac(struct ar_softc *sc); static void ar_dmac_intr(struct ar_hardc *hc, int scano, u_char isr); static void ar_msci_intr(struct ar_hardc *hc, int scano, u_char isr); static void ar_timer_intr(struct ar_hardc *hc, int scano, u_char isr); #ifdef NETGRAPH static void ngar_watchdog_frame(void * arg); static void ngar_init(void* ignored); static ng_constructor_t ngar_constructor; static ng_rcvmsg_t ngar_rcvmsg; static ng_shutdown_t ngar_shutdown; static ng_newhook_t ngar_newhook; /*static ng_findhook_t ngar_findhook; */ static ng_connect_t ngar_connect; static ng_rcvdata_t ngar_rcvdata; static ng_disconnect_t ngar_disconnect; static struct ng_type typestruct = { .version = NG_ABI_VERSION, .name = NG_AR_NODE_TYPE, .constructor = ngar_constructor, .rcvmsg = ngar_rcvmsg, .shutdown = ngar_shutdown, .newhook = ngar_newhook, .connect = ngar_connect, .rcvdata = ngar_rcvdata, .disconnect = ngar_disconnect, }; static int ngar_done_init = 0; #endif /* NETGRAPH */ int ar_attach(device_t device) { struct ar_hardc *hc; struct ar_softc *sc; #ifndef NETGRAPH struct ifnet *ifp; char *iface; #endif /* NETGRAPH */ int unit; hc = (struct ar_hardc *)device_get_softc(device); printf("arc%d: %uK RAM, %u ports, rev %u.\n", hc->cunit, hc->memsize/1024, hc->numports, hc->revision); arc_init(hc); if(BUS_SETUP_INTR(device_get_parent(device), device, hc->res_irq, INTR_TYPE_NET, arintr, hc, &hc->intr_cookie) != 0) return (1); sc = hc->sc; for(unit=0;unitnumports;unit+=NCHAN) ar_init_sca(hc, unit / NCHAN); /* * Now configure each port on the card. */ for(unit=0;unitnumports;sc++,unit++) { sc->hc = hc; sc->subunit = unit; sc->unit = next_ar_unit; next_ar_unit++; sc->scano = unit / NCHAN; sc->scachan = unit%NCHAN; ar_init_rx_dmac(sc); ar_init_tx_dmac(sc); ar_init_msci(sc); #ifndef NETGRAPH ifp = &sc->ifsppp.pp_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(device), device_get_unit(device)); ifp->if_mtu = PP_MTU; ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; ifp->if_ioctl = arioctl; ifp->if_start = arstart; ifp->if_watchdog = arwatchdog; sc->ifsppp.pp_flags = PP_KEEPALIVE; switch(hc->interface[unit]) { default: iface = "UNKNOWN"; break; case AR_IFACE_EIA_232: iface = "EIA-232"; break; case AR_IFACE_V_35: iface = "EIA-232 or V.35"; break; case AR_IFACE_EIA_530: iface = "EIA-530"; break; case AR_IFACE_X_21: iface = "X.21"; break; case AR_IFACE_COMBO: iface = "COMBO X.21 / EIA-530"; break; } printf("ar%d: Adapter %d, port %d, interface %s.\n", sc->unit, hc->cunit, sc->subunit, iface); sppp_attach((struct ifnet *)&sc->ifsppp); if_attach(ifp); bpfattach(ifp, DLT_PPP, PPP_HEADER_LEN); #else /* NETGRAPH */ /* * we have found a node, make sure our 'type' is availabe. */ if (ngar_done_init == 0) ngar_init(NULL); if (ng_make_node_common(&typestruct, &sc->node) != 0) return (1); sprintf(sc->nodename, "%s%d", NG_AR_NODE_TYPE, sc->unit); if (ng_name_node(sc->node, sc->nodename)) { NG_NODE_UNREF(sc->node); /* drop it again */ return (1); } NG_NODE_SET_PRIVATE(sc->node, sc); callout_handle_init(&sc->handle); sc->xmitq.ifq_maxlen = IFQ_MAXLEN; sc->xmitq_hipri.ifq_maxlen = IFQ_MAXLEN; mtx_init(&sc->xmitq.ifq_mtx, "ar_xmitq", NULL, MTX_DEF); mtx_init(&sc->xmitq_hipri.ifq_mtx, "ar_xmitq_hipri", NULL, MTX_DEF); sc->running = 0; #endif /* NETGRAPH */ } if(hc->bustype == AR_BUS_ISA) ARC_SET_OFF(hc); return (0); } int ar_detach(device_t device) { device_t parent = device_get_parent(device); struct ar_hardc *hc = device_get_softc(device); if (hc->intr_cookie != NULL) { if (BUS_TEARDOWN_INTR(parent, device, hc->res_irq, hc->intr_cookie) != 0) { printf("intr teardown failed.. continuing\n"); } hc->intr_cookie = NULL; } /* * deallocate any system resources we may have * allocated on behalf of this driver. */ FREE(hc->sc, M_DEVBUF); hc->sc = NULL; hc->mem_start = NULL; return (ar_deallocate_resources(device)); } int ar_allocate_ioport(device_t device, int rid, u_long size) { struct ar_hardc *hc = device_get_softc(device); hc->rid_ioport = rid; hc->res_ioport = bus_alloc_resource(device, SYS_RES_IOPORT, &hc->rid_ioport, 0ul, ~0ul, size, RF_ACTIVE); if (hc->res_ioport == NULL) { goto errexit; } hc->bt = rman_get_bustag(hc->res_ioport); hc->bh = rman_get_bushandle(hc->res_ioport); return (0); errexit: ar_deallocate_resources(device); return (ENXIO); } int ar_allocate_irq(device_t device, int rid, u_long size) { struct ar_hardc *hc = device_get_softc(device); hc->rid_irq = rid; hc->res_irq = bus_alloc_resource_any(device, SYS_RES_IRQ, &hc->rid_irq, RF_SHAREABLE|RF_ACTIVE); if (hc->res_irq == NULL) { goto errexit; } return (0); errexit: ar_deallocate_resources(device); return (ENXIO); } int ar_allocate_memory(device_t device, int rid, u_long size) { struct ar_hardc *hc = device_get_softc(device); hc->rid_memory = rid; hc->res_memory = bus_alloc_resource(device, SYS_RES_MEMORY, &hc->rid_memory, 0ul, ~0ul, size, RF_ACTIVE); if (hc->res_memory == NULL) { goto errexit; } return (0); errexit: ar_deallocate_resources(device); return (ENXIO); } int ar_allocate_plx_memory(device_t device, int rid, u_long size) { struct ar_hardc *hc = device_get_softc(device); hc->rid_plx_memory = rid; hc->res_plx_memory = bus_alloc_resource(device, SYS_RES_MEMORY, &hc->rid_plx_memory, 0ul, ~0ul, size, RF_ACTIVE); if (hc->res_plx_memory == NULL) { goto errexit; } return (0); errexit: ar_deallocate_resources(device); return (ENXIO); } int ar_deallocate_resources(device_t device) { struct ar_hardc *hc = device_get_softc(device); if (hc->res_irq != 0) { bus_deactivate_resource(device, SYS_RES_IRQ, hc->rid_irq, hc->res_irq); bus_release_resource(device, SYS_RES_IRQ, hc->rid_irq, hc->res_irq); hc->res_irq = 0; } if (hc->res_ioport != 0) { bus_deactivate_resource(device, SYS_RES_IOPORT, hc->rid_ioport, hc->res_ioport); bus_release_resource(device, SYS_RES_IOPORT, hc->rid_ioport, hc->res_ioport); hc->res_ioport = 0; } if (hc->res_memory != 0) { bus_deactivate_resource(device, SYS_RES_MEMORY, hc->rid_memory, hc->res_memory); bus_release_resource(device, SYS_RES_MEMORY, hc->rid_memory, hc->res_memory); hc->res_memory = 0; } if (hc->res_plx_memory != 0) { bus_deactivate_resource(device, SYS_RES_MEMORY, hc->rid_plx_memory, hc->res_plx_memory); bus_release_resource(device, SYS_RES_MEMORY, hc->rid_plx_memory, hc->res_plx_memory); hc->res_plx_memory = 0; } return (0); } /* * First figure out which SCA gave the interrupt. * Process it. * See if there is other interrupts pending. * Repeat until there is no more interrupts. */ static void arintr(void *arg) { struct ar_hardc *hc = (struct ar_hardc *)arg; sca_regs *sca; u_char isr0, isr1, isr2, arisr; int scano; /* XXX Use the PCI interrupt score board register later */ if(hc->bustype == AR_BUS_PCI) arisr = hc->orbase[AR_ISTAT * 4]; else arisr = ar_inb(hc, AR_ISTAT); while(arisr & AR_BD_INT) { TRC(printf("arisr = %x\n", arisr)); if(arisr & AR_INT_0) scano = 0; else if(arisr & AR_INT_1) scano = 1; else { /* XXX Oops this shouldn't happen. */ printf("arc%d: Interrupted with no interrupt.\n", hc->cunit); return; } sca = hc->sca[scano]; if(hc->bustype == AR_BUS_ISA) ARC_SET_SCA(hc, scano); isr0 = sca->isr0; isr1 = sca->isr1; isr2 = sca->isr2; TRC(printf("arc%d: ARINTR isr0 %x, isr1 %x, isr2 %x\n", hc->cunit, isr0, isr1, isr2)); if(isr0) ar_msci_intr(hc, scano, isr0); if(isr1) ar_dmac_intr(hc, scano, isr1); if(isr2) ar_timer_intr(hc, scano, isr2); /* * Proccess the second sca's interrupt if available. * Else see if there are any new interrupts. */ if((arisr & AR_INT_0) && (arisr & AR_INT_1)) arisr &= ~AR_INT_0; else { if(hc->bustype == AR_BUS_PCI) arisr = hc->orbase[AR_ISTAT * 4]; else arisr = ar_inb(hc, AR_ISTAT); } } if(hc->bustype == AR_BUS_ISA) ARC_SET_OFF(hc); } /* * This will only start the transmitter. It is assumed that the data * is already there. It is normally called from arstart() or ar_dmac_intr(). * */ static void ar_xmit(struct ar_softc *sc) { #ifndef NETGRAPH struct ifnet *ifp; #endif /* NETGRAPH */ dmac_channel *dmac; #ifndef NETGRAPH ifp = &sc->ifsppp.pp_if; #endif /* NETGRAPH */ dmac = &sc->sca->dmac[DMAC_TXCH(sc->scachan)]; if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_SCA(sc->hc, sc->scano); dmac->cda = (u_short)(sc->block[sc->txb_next_tx].txdesc & 0xffff); dmac->eda = (u_short)(sc->block[sc->txb_next_tx].txeda & 0xffff); dmac->dsr = SCA_DSR_DE; sc->xmit_busy = 1; sc->txb_next_tx++; if(sc->txb_next_tx == AR_TX_BLOCKS) sc->txb_next_tx = 0; #ifndef NETGRAPH ifp->if_timer = 2; /* Value in seconds. */ #else /* NETGRAPH */ sc->out_dog = DOG_HOLDOFF; /* give ourself some breathing space*/ #endif /* NETGRAPH */ if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_OFF(sc->hc); } /* * This function will be called from the upper level when a user add a * packet to be send, and from the interrupt handler after a finished * transmit. * * NOTE: it should run at spl_imp(). * * This function only place the data in the oncard buffers. It does not * start the transmition. ar_xmit() does that. * * Transmitter idle state is indicated by the IFF_OACTIVE flag. The function * that clears that should ensure that the transmitter and its DMA is * in a "good" idle state. */ #ifndef NETGRAPH static void arstart(struct ifnet *ifp) { struct ar_softc *sc = ifp->if_softc; #else /* NETGRAPH */ static void arstart(struct ar_softc *sc) { #endif /* NETGRAPH */ int i, len, tlen; struct mbuf *mtx; u_char *txdata; sca_descriptor *txdesc; struct buf_block *blkp; #ifndef NETGRAPH if(!(ifp->if_flags & IFF_RUNNING)) return; #else /* NETGRAPH */ /* XXX */ #endif /* NETGRAPH */ top_arstart: /* * See if we have space for more packets. */ if(sc->txb_inuse == AR_TX_BLOCKS) { #ifndef NETGRAPH ifp->if_flags |= IFF_OACTIVE; /* yes, mark active */ #else /* NETGRAPH */ /*XXX*/ /*ifp->if_flags |= IFF_OACTIVE;*/ /* yes, mark active */ #endif /* NETGRAPH */ return; } #ifndef NETGRAPH mtx = sppp_dequeue(ifp); #else /* NETGRAPH */ IF_DEQUEUE(&sc->xmitq_hipri, mtx); if (mtx == NULL) { IF_DEQUEUE(&sc->xmitq, mtx); } #endif /* NETGRAPH */ if(!mtx) return; /* * It is OK to set the memory window outside the loop because * all tx buffers and descriptors are assumed to be in the same * 16K window. */ if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_MEM(sc->hc, sc->block[0].txdesc); /* * We stay in this loop until there is nothing in the * TX queue left or the tx buffer is full. */ i = 0; blkp = &sc->block[sc->txb_new]; txdesc = (sca_descriptor *) (sc->hc->mem_start + (blkp->txdesc & sc->hc->winmsk)); txdata = (u_char *)(sc->hc->mem_start + (blkp->txstart & sc->hc->winmsk)); for(;;) { len = mtx->m_pkthdr.len; TRC(printf("ar%d: ARstart len %u\n", sc->unit, len)); /* * We can do this because the tx buffers don't wrap. */ m_copydata(mtx, 0, len, txdata); tlen = len; while(tlen > AR_BUF_SIZ) { txdesc->stat = 0; txdesc->len = AR_BUF_SIZ; tlen -= AR_BUF_SIZ; txdesc++; txdata += AR_BUF_SIZ; i++; } /* XXX Move into the loop? */ txdesc->stat = SCA_DESC_EOM; txdesc->len = tlen; txdesc++; txdata += AR_BUF_SIZ; i++; #ifndef NETGRAPH BPF_MTAP(ifp, mtx); m_freem(mtx); ++sc->ifsppp.pp_if.if_opackets; #else /* NETGRAPH */ m_freem(mtx); sc->outbytes += len; ++sc->opackets; #endif /* NETGRAPH */ /* * Check if we have space for another mbuf. * XXX This is hardcoded. A packet won't be larger * than 3 buffers (3 x 512). */ if((i + 3) >= blkp->txmax) break; #ifndef NETGRAPH mtx = sppp_dequeue(ifp); #else /* NETGRAPH */ IF_DEQUEUE(&sc->xmitq_hipri, mtx); if (mtx == NULL) { IF_DEQUEUE(&sc->xmitq, mtx); } #endif /* NETGRAPH */ if(!mtx) break; } blkp->txtail = i; /* * Mark the last descriptor, so that the SCA know where * to stop. */ txdesc--; txdesc->stat |= SCA_DESC_EOT; txdesc = (sca_descriptor *)blkp->txdesc; blkp->txeda = (u_short)((u_int)&txdesc[i]); #if 0 printf("ARstart: %p desc->cp %x\n", &txdesc->cp, txdesc->cp); printf("ARstart: %p desc->bp %x\n", &txdesc->bp, txdesc->bp); printf("ARstart: %p desc->bpb %x\n", &txdesc->bpb, txdesc->bpb); printf("ARstart: %p desc->len %x\n", &txdesc->len, txdesc->len); printf("ARstart: %p desc->stat %x\n", &txdesc->stat, txdesc->stat); #endif sc->txb_inuse++; sc->txb_new++; if(sc->txb_new == AR_TX_BLOCKS) sc->txb_new = 0; if(sc->xmit_busy == 0) ar_xmit(sc); if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_OFF(sc->hc); goto top_arstart; } #ifndef NETGRAPH static int arioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { int s, error; int was_up, should_be_up; struct ar_softc *sc = ifp->if_softc; TRC(if_printf(ifp, "arioctl.\n");) was_up = ifp->if_flags & IFF_RUNNING; error = sppp_ioctl(ifp, cmd, data); TRC(if_printf(ifp, "ioctl: ifsppp.pp_flags = %x, if_flags %x.\n", ((struct sppp *)ifp)->pp_flags, ifp->if_flags);) if(error) return (error); if((cmd != SIOCSIFFLAGS) && cmd != (SIOCSIFADDR)) return (0); TRC(if_printf(ifp, "arioctl %s.\n", (cmd == SIOCSIFFLAGS) ? "SIOCSIFFLAGS" : "SIOCSIFADDR");) s = splimp(); should_be_up = ifp->if_flags & IFF_RUNNING; if(!was_up && should_be_up) { /* Interface should be up -- start it. */ ar_up(sc); arstart(ifp); /* XXX Maybe clear the IFF_UP flag so that the link * will only go up after sppp lcp and ipcp negotiation. */ } else if(was_up && !should_be_up) { /* Interface should be down -- stop it. */ ar_down(sc); sppp_flush(ifp); } splx(s); return (0); } #endif /* NETGRAPH */ /* * This is to catch lost tx interrupts. */ static void #ifndef NETGRAPH arwatchdog(struct ifnet *ifp) { struct ar_softc *sc = ifp->if_softc; #else /* NETGRAPH */ arwatchdog(struct ar_softc *sc) { #endif /* NETGRAPH */ msci_channel *msci = &sc->sca->msci[sc->scachan]; #ifndef NETGRAPH if(!(ifp->if_flags & IFF_RUNNING)) return; #endif /* NETGRAPH */ if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_SCA(sc->hc, sc->scano); /* XXX if(sc->ifsppp.pp_if.if_flags & IFF_DEBUG) */ printf("ar%d: transmit failed, " "ST0 %x, ST1 %x, ST3 %x, DSR %x.\n", sc->unit, msci->st0, msci->st1, msci->st3, sc->sca->dmac[DMAC_TXCH(sc->scachan)].dsr); if(msci->st1 & SCA_ST1_UDRN) { msci->cmd = SCA_CMD_TXABORT; msci->cmd = SCA_CMD_TXENABLE; msci->st1 = SCA_ST1_UDRN; } sc->xmit_busy = 0; #ifndef NETGRAPH ifp->if_flags &= ~IFF_OACTIVE; #else /* NETGRAPH */ /* XXX ifp->if_flags &= ~IFF_OACTIVE; */ #endif /* NETGRAPH */ if(sc->txb_inuse && --sc->txb_inuse) ar_xmit(sc); #ifndef NETGRAPH arstart(ifp); #else /* NETGRAPH */ arstart(sc); #endif /* NETGRAPH */ } static void ar_up(struct ar_softc *sc) { sca_regs *sca; msci_channel *msci; sca = sc->sca; msci = &sca->msci[sc->scachan]; TRC(printf("ar%d: sca %p, msci %p, ch %d\n", sc->unit, sca, msci, sc->scachan)); /* * Enable transmitter and receiver. * Raise DTR and RTS. * Enable interrupts. */ if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_SCA(sc->hc, sc->scano); /* XXX * What about using AUTO mode in msci->md0 ??? * And what about CTS/DCD etc... ? */ if(sc->hc->handshake & AR_SHSK_RTS) msci->ctl &= ~SCA_CTL_RTS; if(sc->hc->handshake & AR_SHSK_DTR) { sc->hc->txc_dtr[sc->scano] &= sc->scachan ? ~AR_TXC_DTR_DTR1 : ~AR_TXC_DTR_DTR0; if(sc->hc->bustype == AR_BUS_PCI) sc->hc->orbase[sc->hc->txc_dtr_off[sc->scano]] = sc->hc->txc_dtr[sc->scano]; else ar_outb(sc->hc, sc->hc->txc_dtr_off[sc->scano], sc->hc->txc_dtr[sc->scano]); } if(sc->scachan == 0) { sca->ier0 |= 0x0F; sca->ier1 |= 0x0F; } else { sca->ier0 |= 0xF0; sca->ier1 |= 0xF0; } msci->cmd = SCA_CMD_RXENABLE; if(sc->hc->bustype == AR_BUS_ISA) ar_inb(sc->hc, AR_ID_5); /* XXX slow it down a bit. */ msci->cmd = SCA_CMD_TXENABLE; if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_OFF(sc->hc); #ifdef NETGRAPH untimeout(ngar_watchdog_frame, sc, sc->handle); sc->handle = timeout(ngar_watchdog_frame, sc, hz); sc->running = 1; #endif /* NETGRAPH */ } static void ar_down(struct ar_softc *sc) { sca_regs *sca; msci_channel *msci; sca = sc->sca; msci = &sca->msci[sc->scachan]; #ifdef NETGRAPH untimeout(ngar_watchdog_frame, sc, sc->handle); sc->running = 0; #endif /* NETGRAPH */ /* * Disable transmitter and receiver. * Lower DTR and RTS. * Disable interrupts. */ if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_SCA(sc->hc, sc->scano); msci->cmd = SCA_CMD_RXDISABLE; if(sc->hc->bustype == AR_BUS_ISA) ar_inb(sc->hc, AR_ID_5); /* XXX slow it down a bit. */ msci->cmd = SCA_CMD_TXDISABLE; if(sc->hc->handshake & AR_SHSK_RTS) msci->ctl |= SCA_CTL_RTS; if(sc->hc->handshake & AR_SHSK_DTR) { sc->hc->txc_dtr[sc->scano] |= sc->scachan ? AR_TXC_DTR_DTR1 : AR_TXC_DTR_DTR0; if(sc->hc->bustype == AR_BUS_PCI) sc->hc->orbase[sc->hc->txc_dtr_off[sc->scano]] = sc->hc->txc_dtr[sc->scano]; else ar_outb(sc->hc, sc->hc->txc_dtr_off[sc->scano], sc->hc->txc_dtr[sc->scano]); } if(sc->scachan == 0) { sca->ier0 &= ~0x0F; sca->ier1 &= ~0x0F; } else { sca->ier0 &= ~0xF0; sca->ier1 &= ~0xF0; } if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_OFF(sc->hc); } static int ar_read_pim_iface(volatile struct ar_hardc *hc, int channel) { int ctype, i, val, x; volatile u_char *pimctrl; ctype = 0; val = 0; pimctrl = hc->orbase + AR_PIMCTRL; /* Reset the PIM */ *pimctrl = 0x00; *pimctrl = AR_PIM_STROBE; /* Check if there is a PIM */ *pimctrl = 0x00; *pimctrl = AR_PIM_READ; x = *pimctrl; TRC(printf("x = %x", x)); if(x & AR_PIM_DATA) { printf("No PIM installed\n"); return (AR_IFACE_UNKNOWN); } x = (x >> 1) & 0x01; val |= x << 0; /* Now read the next 15 bits */ for(i = 1; i < 16; i++) { *pimctrl = AR_PIM_READ; *pimctrl = AR_PIM_READ | AR_PIM_STROBE; x = *pimctrl; TRC(printf(" %x ", x)); x = (x >> 1) & 0x01; val |= x << i; if(i == 8 && (val & 0x000f) == 0x0004) { int ii; /* Start bit */ *pimctrl = AR_PIM_A2D_DOUT | AR_PIM_A2D_STROBE; *pimctrl = AR_PIM_A2D_DOUT; /* Mode bit */ *pimctrl = AR_PIM_A2D_DOUT | AR_PIM_A2D_STROBE; *pimctrl = AR_PIM_A2D_DOUT; /* Sign bit */ *pimctrl = AR_PIM_A2D_DOUT | AR_PIM_A2D_STROBE; *pimctrl = AR_PIM_A2D_DOUT; /* Select channel */ *pimctrl = AR_PIM_A2D_STROBE | ((channel & 2) << 2); *pimctrl = ((channel & 2) << 2); *pimctrl = AR_PIM_A2D_STROBE | ((channel & 1) << 3); *pimctrl = ((channel & 1) << 3); *pimctrl = AR_PIM_A2D_STROBE; x = *pimctrl; if(x & AR_PIM_DATA) printf("\nOops A2D start bit not zero (%X)\n", x); for(ii = 7; ii >= 0; ii--) { *pimctrl = 0x00; *pimctrl = AR_PIM_A2D_STROBE; x = *pimctrl; if(x & AR_PIM_DATA) ctype |= 1 << ii; } } } TRC(printf("\nPIM val %x, ctype %x, %d\n", val, ctype, ctype)); *pimctrl = AR_PIM_MODEG; *pimctrl = AR_PIM_MODEG | AR_PIM_AUTO_LED; if(ctype > 255) return (AR_IFACE_UNKNOWN); if(ctype > 239) return (AR_IFACE_V_35); if(ctype > 207) return (AR_IFACE_EIA_232); if(ctype > 178) return (AR_IFACE_X_21); if(ctype > 150) return (AR_IFACE_EIA_530); if(ctype > 25) return (AR_IFACE_UNKNOWN); if(ctype > 7) return (AR_IFACE_LOOPBACK); return (AR_IFACE_UNKNOWN); } /* * Initialize the card, allocate memory for the ar_softc structures * and fill in the pointers. */ static void arc_init(struct ar_hardc *hc) { struct ar_softc *sc; int x; u_int chanmem; u_int bufmem; u_int next; u_int descneeded; u_char isr, mar; u_long memst; MALLOC(sc, struct ar_softc *, hc->numports * sizeof(struct ar_softc), M_DEVBUF, M_WAITOK | M_ZERO); if (sc == NULL) return; hc->sc = sc; hc->txc_dtr[0] = AR_TXC_DTR_NOTRESET | AR_TXC_DTR_DTR0 | AR_TXC_DTR_DTR1; hc->txc_dtr[1] = AR_TXC_DTR_DTR0 | AR_TXC_DTR_DTR1; hc->txc_dtr_off[0] = AR_TXC_DTR0; hc->txc_dtr_off[1] = AR_TXC_DTR2; if(hc->bustype == AR_BUS_PCI) { hc->txc_dtr_off[0] *= 4; hc->txc_dtr_off[1] *= 4; } /* * reset the card and wait at least 1uS. */ if(hc->bustype == AR_BUS_PCI) hc->orbase[AR_TXC_DTR0 * 4] = ~AR_TXC_DTR_NOTRESET & hc->txc_dtr[0]; else ar_outb(hc, AR_TXC_DTR0, ~AR_TXC_DTR_NOTRESET & hc->txc_dtr[0]); DELAY(2); if(hc->bustype == AR_BUS_PCI) hc->orbase[AR_TXC_DTR0 * 4] = hc->txc_dtr[0]; else ar_outb(hc, AR_TXC_DTR0, hc->txc_dtr[0]); if(hc->bustype == AR_BUS_ISA) { /* * Configure the card. * Mem address, irq, */ memst = rman_get_start(hc->res_memory); mar = memst >> 16; isr = irqtable[hc->isa_irq] << 1; if(isr == 0) printf("ar%d: Warning illegal interrupt %d\n", hc->cunit, hc->isa_irq); isr = isr | ((memst & 0xc000) >> 10); hc->sca[0] = (sca_regs *)hc->mem_start; hc->sca[1] = (sca_regs *)hc->mem_start; ar_outb(hc, AR_MEM_SEL, mar); ar_outb(hc, AR_INT_SEL, isr | AR_INTS_CEN); } if(hc->bustype == AR_BUS_PCI && hc->interface[0] == AR_IFACE_PIM) for(x = 0; x < hc->numports; x++) hc->interface[x] = ar_read_pim_iface(hc, x); /* * Set the TX clock direction and enable TX. */ for(x=0;xnumports;x++) { switch(hc->interface[x]) { case AR_IFACE_V_35: hc->txc_dtr[x / NCHAN] |= (x % NCHAN == 0) ? AR_TXC_DTR_TX0 : AR_TXC_DTR_TX1; hc->txc_dtr[x / NCHAN] |= (x % NCHAN == 0) ? AR_TXC_DTR_TXCS0 : AR_TXC_DTR_TXCS1; break; case AR_IFACE_EIA_530: case AR_IFACE_COMBO: case AR_IFACE_X_21: hc->txc_dtr[x / NCHAN] |= (x % NCHAN == 0) ? AR_TXC_DTR_TX0 : AR_TXC_DTR_TX1; break; } } if(hc->bustype == AR_BUS_PCI) hc->orbase[AR_TXC_DTR0 * 4] = hc->txc_dtr[0]; else ar_outb(hc, AR_TXC_DTR0, hc->txc_dtr[0]); if(hc->numports > NCHAN) { if(hc->bustype == AR_BUS_PCI) hc->orbase[AR_TXC_DTR2 * 4] = hc->txc_dtr[1]; else ar_outb(hc, AR_TXC_DTR2, hc->txc_dtr[1]); } chanmem = hc->memsize / hc->numports; next = 0; for(x=0;xnumports;x++, sc++) { int blk; sc->sca = hc->sca[x / NCHAN]; for(blk = 0; blk < AR_TX_BLOCKS; blk++) { sc->block[blk].txdesc = next; bufmem = (16 * 1024) / AR_TX_BLOCKS; descneeded = bufmem / AR_BUF_SIZ; sc->block[blk].txstart = sc->block[blk].txdesc + ((((descneeded * sizeof(sca_descriptor)) / AR_BUF_SIZ) + 1) * AR_BUF_SIZ); sc->block[blk].txend = next + bufmem; sc->block[blk].txmax = (sc->block[blk].txend - sc->block[blk].txstart) / AR_BUF_SIZ; next += bufmem; TRC(printf("ar%d: blk %d: txdesc %x, txstart %x, " "txend %x, txmax %d\n", x, blk, sc->block[blk].txdesc, sc->block[blk].txstart, sc->block[blk].txend, sc->block[blk].txmax)); } sc->rxdesc = next; bufmem = chanmem - (bufmem * AR_TX_BLOCKS); descneeded = bufmem / AR_BUF_SIZ; sc->rxstart = sc->rxdesc + ((((descneeded * sizeof(sca_descriptor)) / AR_BUF_SIZ) + 1) * AR_BUF_SIZ); sc->rxend = next + bufmem; sc->rxmax = (sc->rxend - sc->rxstart) / AR_BUF_SIZ; next += bufmem; TRC(printf("ar%d: rxdesc %x, rxstart %x, " "rxend %x, rxmax %d\n", x, sc->rxdesc, sc->rxstart, sc->rxend, sc->rxmax)); } if(hc->bustype == AR_BUS_PCI) hc->orbase[AR_PIMCTRL] = AR_PIM_MODEG | AR_PIM_AUTO_LED; } /* * The things done here are channel independent. * * Configure the sca waitstates. * Configure the global interrupt registers. * Enable master dma enable. */ static void ar_init_sca(struct ar_hardc *hc, int scano) { sca_regs *sca; sca = hc->sca[scano]; if(hc->bustype == AR_BUS_ISA) ARC_SET_SCA(hc, scano); /* * Do the wait registers. * Set everything to 0 wait states. */ sca->pabr0 = 0; sca->pabr1 = 0; sca->wcrl = 0; sca->wcrm = 0; sca->wcrh = 0; /* * Configure the interrupt registers. * Most are cleared until the interface is configured. */ sca->ier0 = 0x00; /* MSCI interrupts... Not used with dma. */ sca->ier1 = 0x00; /* DMAC interrupts */ sca->ier2 = 0x00; /* TIMER interrupts... Not used yet. */ sca->itcr = 0x00; /* Use ivr and no intr ack */ sca->ivr = 0x40; /* Fill in the interrupt vector. */ sca->imvr = 0x40; /* * Configure the timers. * XXX Later */ /* * Set the DMA channel priority to rotate between * all four channels. * * Enable all dma channels. */ if(hc->bustype == AR_BUS_PCI) { u_char *t; /* * Stupid problem with the PCI interface chip that break * things. * XXX */ t = (u_char *)sca; t[AR_PCI_SCA_PCR] = SCA_PCR_PR2; t[AR_PCI_SCA_DMER] = SCA_DMER_EN; } else { sca->pcr = SCA_PCR_PR2; sca->dmer = SCA_DMER_EN; } } /* * Configure the msci * * NOTE: The serial port configuration is hardcoded at the moment. */ static void ar_init_msci(struct ar_softc *sc) { msci_channel *msci; msci = &sc->sca->msci[sc->scachan]; if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_SCA(sc->hc, sc->scano); msci->cmd = SCA_CMD_RESET; msci->md0 = SCA_MD0_CRC_1 | SCA_MD0_CRC_CCITT | SCA_MD0_CRC_ENABLE | SCA_MD0_MODE_HDLC; msci->md1 = SCA_MD1_NOADDRCHK; msci->md2 = SCA_MD2_DUPLEX | SCA_MD2_NRZ; /* * Acording to the manual I should give a reset after changing the * mode registers. */ msci->cmd = SCA_CMD_RXRESET; msci->ctl = SCA_CTL_IDLPAT | SCA_CTL_UDRNC | SCA_CTL_RTS; /* * For now all interfaces are programmed to use the RX clock for * the TX clock. */ switch(sc->hc->interface[sc->subunit]) { case AR_IFACE_V_35: msci->rxs = SCA_RXS_CLK_RXC0 | SCA_RXS_DIV1; msci->txs = SCA_TXS_CLK_TXC | SCA_TXS_DIV1; break; case AR_IFACE_X_21: case AR_IFACE_EIA_530: case AR_IFACE_COMBO: msci->rxs = SCA_RXS_CLK_RXC0 | SCA_RXS_DIV1; msci->txs = SCA_TXS_CLK_RX | SCA_TXS_DIV1; } msci->tmc = 153; /* This give 64k for loopback */ /* XXX * Disable all interrupts for now. I think if you are using * the dmac you don't use these interrupts. */ msci->ie0 = 0; msci->ie1 = 0x0C; /* XXX CTS and DCD (DSR on 570I) level change. */ msci->ie2 = 0; msci->fie = 0; msci->sa0 = 0; msci->sa1 = 0; msci->idl = 0x7E; /* XXX This is what cisco does. */ /* * This is what the ARNET diags use. */ msci->rrc = 0x0E; msci->trc0 = 0x12; msci->trc1 = 0x1F; } /* * Configure the rx dma controller. */ static void ar_init_rx_dmac(struct ar_softc *sc) { dmac_channel *dmac; sca_descriptor *rxd; u_int rxbuf; u_int rxda; u_int rxda_d; int x = 0; dmac = &sc->sca->dmac[DMAC_RXCH(sc->scachan)]; if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_MEM(sc->hc, sc->rxdesc); rxd = (sca_descriptor *)(sc->hc->mem_start + (sc->rxdesc&sc->hc->winmsk)); rxda_d = (u_int)sc->hc->mem_start - (sc->rxdesc & ~sc->hc->winmsk); for(rxbuf=sc->rxstart;rxbufrxend;rxbuf += AR_BUF_SIZ, rxd++) { rxda = (u_int)&rxd[1] - rxda_d; rxd->cp = (u_short)(rxda & 0xfffful); x++; if(x < 6) TRC(printf("Descrp %p, data pt %x, data %x, ", rxd, rxda, rxbuf)); rxd->bp = (u_short)(rxbuf & 0xfffful); rxd->bpb = (u_char)((rxbuf >> 16) & 0xff); rxd->len = 0; rxd->stat = 0xff; /* The sca write here when it is finished. */ if(x < 6) TRC(printf("bpb %x, bp %x.\n", rxd->bpb, rxd->bp)); } rxd--; rxd->cp = (u_short)(sc->rxdesc & 0xfffful); sc->rxhind = 0; if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_SCA(sc->hc, sc->scano); dmac->dsr = 0; /* Disable DMA transfer */ dmac->dcr = SCA_DCR_ABRT; /* XXX maybe also SCA_DMR_CNTE */ dmac->dmr = SCA_DMR_TMOD | SCA_DMR_NF; dmac->bfl = AR_BUF_SIZ; dmac->cda = (u_short)(sc->rxdesc & 0xffff); dmac->sarb = (u_char)((sc->rxdesc >> 16) & 0xff); rxd = (sca_descriptor *)sc->rxstart; dmac->eda = (u_short)((u_int)&rxd[sc->rxmax - 1] & 0xffff); dmac->dir = 0xF0; dmac->dsr = SCA_DSR_DE; } /* * Configure the TX DMA descriptors. * Initialize the needed values and chain the descriptors. */ static void ar_init_tx_dmac(struct ar_softc *sc) { dmac_channel *dmac; struct buf_block *blkp; int blk; sca_descriptor *txd; u_int txbuf; u_int txda; u_int txda_d; dmac = &sc->sca->dmac[DMAC_TXCH(sc->scachan)]; if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_MEM(sc->hc, sc->block[0].txdesc); for(blk = 0; blk < AR_TX_BLOCKS; blk++) { blkp = &sc->block[blk]; txd = (sca_descriptor *)(sc->hc->mem_start + (blkp->txdesc&sc->hc->winmsk)); txda_d = (u_int)sc->hc->mem_start - (blkp->txdesc & ~sc->hc->winmsk); txbuf=blkp->txstart; for(;txbuftxend;txbuf += AR_BUF_SIZ, txd++) { txda = (u_int)&txd[1] - txda_d; txd->cp = (u_short)(txda & 0xfffful); txd->bp = (u_short)(txbuf & 0xfffful); txd->bpb = (u_char)((txbuf >> 16) & 0xff); TRC(printf("ar%d: txbuf %x, bpb %x, bp %x\n", sc->unit, txbuf, txd->bpb, txd->bp)); txd->len = 0; txd->stat = 0; } txd--; txd->cp = (u_short)(blkp->txdesc & 0xfffful); blkp->txtail = (u_int)txd - (u_int)sc->hc->mem_start; TRC(printf("TX Descriptors start %x, end %x.\n", blkp->txdesc, blkp->txtail)); } if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_SCA(sc->hc, sc->scano); dmac->dsr = 0; /* Disable DMA */ dmac->dcr = SCA_DCR_ABRT; dmac->dmr = SCA_DMR_TMOD | SCA_DMR_NF; dmac->dir = SCA_DIR_EOT | SCA_DIR_BOF | SCA_DIR_COF; dmac->sarb = (u_char)((sc->block[0].txdesc >> 16) & 0xff); } /* * Look through the descriptors to see if there is a complete packet * available. Stop if we get to where the sca is busy. * * Return the length and status of the packet. * Return nonzero if there is a packet available. * * NOTE: * It seems that we get the interrupt a bit early. The updateing of * descriptor values is not always completed when this is called. */ static int ar_packet_avail(struct ar_softc *sc, int *len, u_char *rxstat) { dmac_channel *dmac; sca_descriptor *rxdesc; sca_descriptor *endp; sca_descriptor *cda; if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_SCA(sc->hc, sc->scano); dmac = &sc->sca->dmac[DMAC_RXCH(sc->scachan)]; cda = (sca_descriptor *)(sc->hc->mem_start + ((((u_int)dmac->sarb << 16) + dmac->cda) & sc->hc->winmsk)); if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_MEM(sc->hc, sc->rxdesc); rxdesc = (sca_descriptor *) (sc->hc->mem_start + (sc->rxdesc & sc->hc->winmsk)); endp = rxdesc; rxdesc = &rxdesc[sc->rxhind]; endp = &endp[sc->rxmax]; *len = 0; while(rxdesc != cda) { *len += rxdesc->len; if(rxdesc->stat & SCA_DESC_EOM) { *rxstat = rxdesc->stat; TRC(printf("ar%d: PKT AVAIL len %d, %x.\n", sc->unit, *len, *rxstat)); return (1); } rxdesc++; if(rxdesc == endp) rxdesc = (sca_descriptor *) (sc->hc->mem_start + (sc->rxdesc & sc->hc->winmsk)); } *len = 0; *rxstat = 0; return (0); } /* * Copy a packet from the on card memory into a provided mbuf. * Take into account that buffers wrap and that a packet may * be larger than a buffer. */ static void ar_copy_rxbuf(struct mbuf *m, struct ar_softc *sc, int len) { sca_descriptor *rxdesc; u_int rxdata; u_int rxmax; u_int off = 0; u_int tlen; rxdata = sc->rxstart + (sc->rxhind * AR_BUF_SIZ); rxmax = sc->rxstart + (sc->rxmax * AR_BUF_SIZ); rxdesc = (sca_descriptor *) (sc->hc->mem_start + (sc->rxdesc & sc->hc->winmsk)); rxdesc = &rxdesc[sc->rxhind]; while(len) { tlen = (len < AR_BUF_SIZ) ? len : AR_BUF_SIZ; if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_MEM(sc->hc, rxdata); bcopy(sc->hc->mem_start + (rxdata & sc->hc->winmsk), mtod(m, caddr_t) + off, tlen); off += tlen; len -= tlen; if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_MEM(sc->hc, sc->rxdesc); rxdesc->len = 0; rxdesc->stat = 0xff; rxdata += AR_BUF_SIZ; rxdesc++; if(rxdata == rxmax) { rxdata = sc->rxstart; rxdesc = (sca_descriptor *) (sc->hc->mem_start + (sc->rxdesc & sc->hc->winmsk)); } } } /* * If single is set, just eat a packet. Otherwise eat everything up to * where cda points. Update pointers to point to the next packet. */ static void ar_eat_packet(struct ar_softc *sc, int single) { dmac_channel *dmac; sca_descriptor *rxdesc; sca_descriptor *endp; sca_descriptor *cda; int loopcnt = 0; u_char stat; if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_SCA(sc->hc, sc->scano); dmac = &sc->sca->dmac[DMAC_RXCH(sc->scachan)]; cda = (sca_descriptor *)(sc->hc->mem_start + ((((u_int)dmac->sarb << 16) + dmac->cda) & sc->hc->winmsk)); /* * Loop until desc->stat == (0xff || EOM) * Clear the status and length in the descriptor. * Increment the descriptor. */ if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_MEM(sc->hc, sc->rxdesc); rxdesc = (sca_descriptor *) (sc->hc->mem_start + (sc->rxdesc & sc->hc->winmsk)); endp = rxdesc; rxdesc = &rxdesc[sc->rxhind]; endp = &endp[sc->rxmax]; while(rxdesc != cda) { loopcnt++; if(loopcnt > sc->rxmax) { printf("ar%d: eat pkt %d loop, cda %p, " "rxdesc %p, stat %x.\n", sc->unit, loopcnt, (void *)cda, (void *)rxdesc, rxdesc->stat); break; } stat = rxdesc->stat; rxdesc->len = 0; rxdesc->stat = 0xff; rxdesc++; sc->rxhind++; if(rxdesc == endp) { rxdesc = (sca_descriptor *) (sc->hc->mem_start + (sc->rxdesc & sc->hc->winmsk)); sc->rxhind = 0; } if(single && (stat == SCA_DESC_EOM)) break; } /* * Update the eda to the previous descriptor. */ if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_SCA(sc->hc, sc->scano); rxdesc = (sca_descriptor *)sc->rxdesc; rxdesc = &rxdesc[(sc->rxhind + sc->rxmax - 2 ) % sc->rxmax]; sc->sca->dmac[DMAC_RXCH(sc->scachan)].eda = (u_short)((u_int)rxdesc & 0xffff); } /* * While there is packets available in the rx buffer, read them out * into mbufs and ship them off. */ static void ar_get_packets(struct ar_softc *sc) { sca_descriptor *rxdesc; struct mbuf *m = NULL; int i; int len; u_char rxstat; #ifdef NETGRAPH int error; #endif while(ar_packet_avail(sc, &len, &rxstat)) { TRC(printf("apa: len %d, rxstat %x\n", len, rxstat)); if(((rxstat & SCA_DESC_ERRORS) == 0) && (len < MCLBYTES)) { MGETHDR(m, M_DONTWAIT, MT_DATA); if(m == NULL) { /* eat packet if get mbuf fail!! */ ar_eat_packet(sc, 1); continue; } #ifndef NETGRAPH m->m_pkthdr.rcvif = &sc->ifsppp.pp_if; #else /* NETGRAPH */ m->m_pkthdr.rcvif = NULL; sc->inbytes += len; sc->inlast = 0; #endif /* NETGRAPH */ m->m_pkthdr.len = m->m_len = len; if(len > MHLEN) { MCLGET(m, M_DONTWAIT); if((m->m_flags & M_EXT) == 0) { m_freem(m); ar_eat_packet(sc, 1); continue; } } ar_copy_rxbuf(m, sc, len); #ifndef NETGRAPH BPF_MTAP(&sc->ifsppp.pp_if, m); sppp_input(&sc->ifsppp.pp_if, m); sc->ifsppp.pp_if.if_ipackets++; #else /* NETGRAPH */ NG_SEND_DATA_ONLY(error, sc->hook, m); sc->ipackets++; #endif /* NETGRAPH */ /* * Update the eda to the previous descriptor. */ i = (len + AR_BUF_SIZ - 1) / AR_BUF_SIZ; sc->rxhind = (sc->rxhind + i) % sc->rxmax; if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_SCA(sc->hc, sc->scano); rxdesc = (sca_descriptor *)sc->rxdesc; rxdesc = &rxdesc[(sc->rxhind + sc->rxmax - 2 ) % sc->rxmax]; sc->sca->dmac[DMAC_RXCH(sc->scachan)].eda = (u_short)((u_int)rxdesc & 0xffff); } else { int tries = 5; while((rxstat == 0xff) && --tries) ar_packet_avail(sc, &len, &rxstat); /* * It look like we get an interrupt early * sometimes and then the status is not * filled in yet. */ if(tries && (tries != 5)) continue; ar_eat_packet(sc, 1); #ifndef NETGRAPH sc->ifsppp.pp_if.if_ierrors++; #else /* NETGRAPH */ sc->ierrors[0]++; #endif /* NETGRAPH */ if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_SCA(sc->hc, sc->scano); TRCL(printf("ar%d: Receive error chan %d, " "stat %x, msci st3 %x," "rxhind %d, cda %x, eda %x.\n", sc->unit, sc->scachan, rxstat, sc->sca->msci[sc->scachan].st3, sc->rxhind, sc->sca->dmac[ DMAC_RXCH(sc->scachan)].cda, sc->sca->dmac[ DMAC_RXCH(sc->scachan)].eda)); } } } /* * All DMA interrupts come here. * * Each channel has two interrupts. * Interrupt A for errors and Interrupt B for normal stuff like end * of transmit or receive dmas. */ static void ar_dmac_intr(struct ar_hardc *hc, int scano, u_char isr1) { u_char dsr; u_char dotxstart = isr1; int mch; struct ar_softc *sc; sca_regs *sca; dmac_channel *dmac; sca = hc->sca[scano]; mch = 0; /* * Shortcut if there is no interrupts for dma channel 0 or 1 */ if((isr1 & 0x0F) == 0) { mch = 1; isr1 >>= 4; } do { sc = &hc->sc[mch + (NCHAN * scano)]; /* * Transmit channel */ if(isr1 & 0x0C) { dmac = &sca->dmac[DMAC_TXCH(mch)]; if(hc->bustype == AR_BUS_ISA) ARC_SET_SCA(hc, scano); dsr = dmac->dsr; dmac->dsr = dsr; /* Counter overflow */ if(dsr & SCA_DSR_COF) { printf("ar%d: TX DMA Counter overflow, " "txpacket no %lu.\n", sc->unit, #ifndef NETGRAPH sc->ifsppp.pp_if.if_opackets); sc->ifsppp.pp_if.if_oerrors++; #else /* NETGRAPH */ sc->opackets); sc->oerrors++; #endif /* NETGRAPH */ } /* Buffer overflow */ if(dsr & SCA_DSR_BOF) { printf("ar%d: TX DMA Buffer overflow, " "txpacket no %lu, dsr %02x, " "cda %04x, eda %04x.\n", sc->unit, #ifndef NETGRAPH sc->ifsppp.pp_if.if_opackets, #else /* NETGRAPH */ sc->opackets, #endif /* NETGRAPH */ dsr, dmac->cda, dmac->eda); #ifndef NETGRAPH sc->ifsppp.pp_if.if_oerrors++; #else /* NETGRAPH */ sc->oerrors++; #endif /* NETGRAPH */ } /* End of Transfer */ if(dsr & SCA_DSR_EOT) { /* * This should be the most common case. * * Clear the IFF_OACTIVE flag. * * Call arstart to start a new transmit if * there is data to transmit. */ sc->xmit_busy = 0; #ifndef NETGRAPH sc->ifsppp.pp_if.if_flags &= ~IFF_OACTIVE; sc->ifsppp.pp_if.if_timer = 0; #else /* NETGRAPH */ /* XXX c->ifsppp.pp_if.if_flags &= ~IFF_OACTIVE; */ sc->out_dog = 0; /* XXX */ #endif /* NETGRAPH */ if(sc->txb_inuse && --sc->txb_inuse) ar_xmit(sc); } } /* * Receive channel */ if(isr1 & 0x03) { dmac = &sca->dmac[DMAC_RXCH(mch)]; if(hc->bustype == AR_BUS_ISA) ARC_SET_SCA(hc, scano); dsr = dmac->dsr; dmac->dsr = dsr; TRC(printf("AR: RX DSR %x\n", dsr)); /* End of frame */ if(dsr & SCA_DSR_EOM) { TRC(int tt = sc->ifsppp.pp_if.if_ipackets;) TRC(int ind = sc->rxhind;) ar_get_packets(sc); #ifndef NETGRAPH #define IPACKETS sc->ifsppp.pp_if.if_ipackets #else /* NETGRAPH */ #define IPACKETS sc->ipackets #endif /* NETGRAPH */ TRC(if(tt == IPACKETS) { sca_descriptor *rxdesc; int i; if(hc->bustype == AR_BUS_ISA) ARC_SET_SCA(hc, scano); printf("AR: RXINTR isr1 %x, dsr %x, " "no data %d pkts, orxhind %d.\n", dotxstart, dsr, tt, ind); printf("AR: rxdesc %x, rxstart %x, " "rxend %x, rxhind %d, " "rxmax %d.\n", sc->rxdesc, sc->rxstart, sc->rxend, sc->rxhind, sc->rxmax); printf("AR: cda %x, eda %x.\n", dmac->cda, dmac->eda); if(sc->hc->bustype == AR_BUS_ISA) ARC_SET_MEM(sc->hc, sc->rxdesc); rxdesc = (sca_descriptor *) (sc->hc->mem_start + (sc->rxdesc & sc->hc->winmsk)); rxdesc = &rxdesc[sc->rxhind]; for(i=0;i<3;i++,rxdesc++) printf("AR: rxdesc->stat %x, " "len %d.\n", rxdesc->stat, rxdesc->len); }) } /* Counter overflow */ if(dsr & SCA_DSR_COF) { printf("ar%d: RX DMA Counter overflow, " "rxpkts %lu.\n", sc->unit, #ifndef NETGRAPH sc->ifsppp.pp_if.if_ipackets); sc->ifsppp.pp_if.if_ierrors++; #else /* NETGRAPH */ sc->ipackets); sc->ierrors[1]++; #endif /* NETGRAPH */ } /* Buffer overflow */ if(dsr & SCA_DSR_BOF) { if(hc->bustype == AR_BUS_ISA) ARC_SET_SCA(hc, scano); printf("ar%d: RX DMA Buffer overflow, " "rxpkts %lu, rxind %d, " "cda %x, eda %x, dsr %x.\n", sc->unit, #ifndef NETGRAPH sc->ifsppp.pp_if.if_ipackets, #else /* NETGRAPH */ sc->ipackets, #endif /* NETGRAPH */ sc->rxhind, dmac->cda, dmac->eda, dsr); /* * Make sure we eat as many as possible. * Then get the system running again. */ ar_eat_packet(sc, 0); #ifndef NETGRAPH sc->ifsppp.pp_if.if_ierrors++; #else /* NETGRAPH */ sc->ierrors[2]++; #endif /* NETGRAPH */ if(hc->bustype == AR_BUS_ISA) ARC_SET_SCA(hc, scano); sca->msci[mch].cmd = SCA_CMD_RXMSGREJ; dmac->dsr = SCA_DSR_DE; TRC(printf("ar%d: RX DMA Buffer overflow, " "rxpkts %lu, rxind %d, " "cda %x, eda %x, dsr %x. After\n", sc->unit, sc->ifsppp.pp_if.if_ipackets, sc->rxhind, dmac->cda, dmac->eda, dmac->dsr);) } /* End of Transfer */ if(dsr & SCA_DSR_EOT) { /* * If this happen, it means that we are * receiving faster than what the processor * can handle. * * XXX We should enable the dma again. */ printf("ar%d: RX End of transfer, rxpkts %lu.\n", sc->unit, #ifndef NETGRAPH sc->ifsppp.pp_if.if_ipackets); sc->ifsppp.pp_if.if_ierrors++; #else /* NETGRAPH */ sc->ipackets); sc->ierrors[3]++; #endif /* NETGRAPH */ } } isr1 >>= 4; mch++; }while((mchsc[mch + (NCHAN * scano)]; #ifndef NETGRAPH arstart(&sc->ifsppp.pp_if); #else /* NETGRAPH */ arstart(sc); #endif /* NETGRAPH */ } dotxstart >>= 4; } } static void ar_msci_intr(struct ar_hardc *hc, int scano, u_char isr0) { printf("arc%d: ARINTR: MSCI\n", hc->cunit); } static void ar_timer_intr(struct ar_hardc *hc, int scano, u_char isr2) { printf("arc%d: ARINTR: TIMER\n", hc->cunit); } #ifdef NETGRAPH /***************************************** * Device timeout/watchdog routine. * called once per second. * checks to see that if activity was expected, that it hapenned. * At present we only look to see if expected output was completed. */ static void ngar_watchdog_frame(void * arg) { struct ar_softc * sc = arg; int s; int speed; if(sc->running == 0) return; /* if we are not running let timeouts die */ /* * calculate the apparent throughputs * XXX a real hack */ s = splimp(); speed = sc->inbytes - sc->lastinbytes; sc->lastinbytes = sc->inbytes; if ( sc->inrate < speed ) sc->inrate = speed; speed = sc->outbytes - sc->lastoutbytes; sc->lastoutbytes = sc->outbytes; if ( sc->outrate < speed ) sc->outrate = speed; sc->inlast++; splx(s); if ((sc->inlast > QUITE_A_WHILE) && (sc->out_deficit > LOTS_OF_PACKETS)) { log(LOG_ERR, "ar%d: No response from remote end\n", sc->unit); s = splimp(); ar_down(sc); ar_up(sc); sc->inlast = sc->out_deficit = 0; splx(s); } else if ( sc->xmit_busy ) { /* no TX -> no TX timeouts */ if (sc->out_dog == 0) { log(LOG_ERR, "ar%d: Transmit failure.. no clock?\n", sc->unit); s = splimp(); arwatchdog(sc); #if 0 ar_down(sc); ar_up(sc); #endif splx(s); sc->inlast = sc->out_deficit = 0; } else { sc->out_dog--; } } sc->handle = timeout(ngar_watchdog_frame, sc, hz); } /*********************************************************************** * This section contains the methods for the Netgraph interface ***********************************************************************/ /* * It is not possible or allowable to create a node of this type. * If the hardware exists, it will already have created it. */ static int ngar_constructor(node_p node) { return (EINVAL); } /* * give our ok for a hook to be added... * If we are not running this should kick the device into life. * The hook's private info points to our stash of info about that * channel. */ static int ngar_newhook(node_p node, hook_p hook, const char *name) { struct ar_softc * sc = NG_NODE_PRIVATE(node); /* * check if it's our friend the debug hook */ if (strcmp(name, NG_AR_HOOK_DEBUG) == 0) { NG_HOOK_SET_PRIVATE(hook, NULL); /* paranoid */ sc->debug_hook = hook; return (0); } /* * Check for raw mode hook. */ if (strcmp(name, NG_AR_HOOK_RAW) != 0) { return (EINVAL); } NG_HOOK_SET_PRIVATE(hook, sc); sc->hook = hook; sc->datahooks++; ar_up(sc); return (0); } /* * incoming messages. * Just respond to the generic TEXT_STATUS message */ static int ngar_rcvmsg(node_p node, item_p item, hook_p lasthook) { struct ar_softc * sc; struct ng_mesg *resp = NULL; int error = 0; struct ng_mesg *msg; NGI_GET_MSG(item, msg); sc = NG_NODE_PRIVATE(node); switch (msg->header.typecookie) { case NG_AR_COOKIE: error = EINVAL; break; case NGM_GENERIC_COOKIE: switch(msg->header.cmd) { case NGM_TEXT_STATUS: { char *arg; int pos = 0; int resplen = sizeof(struct ng_mesg) + 512; NG_MKRESPONSE(resp, msg, resplen, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } arg = (resp)->data; pos = sprintf(arg, "%ld bytes in, %ld bytes out\n" "highest rate seen: %ld B/S in, %ld B/S out\n", sc->inbytes, sc->outbytes, sc->inrate, sc->outrate); pos += sprintf(arg + pos, "%ld output errors\n", sc->oerrors); pos += sprintf(arg + pos, "ierrors = %ld, %ld, %ld, %ld\n", sc->ierrors[0], sc->ierrors[1], sc->ierrors[2], sc->ierrors[3]); resp->header.arglen = pos + 1; break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } /* Take care of synchronous response, if any */ NG_RESPOND_MSG(error, node, item, resp); NG_FREE_MSG(msg); return (error); } /* * get data from another node and transmit it to the correct channel */ static int ngar_rcvdata(hook_p hook, item_p item) { int s; int error = 0; struct ar_softc * sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); struct ifqueue *xmitq_p; struct mbuf *m; - meta_p meta; + struct ng_tag_prio *ptag; NGI_GET_M(item, m); - NGI_GET_META(item, meta); NG_FREE_ITEM(item); /* * data doesn't come in from just anywhere (e.g control hook) */ if ( NG_HOOK_PRIVATE(hook) == NULL) { error = ENETDOWN; goto bad; } /* * Now queue the data for when it can be sent */ - if (meta && meta->priority > 0) { + if ((ptag = (struct ng_tag_prio *)m_tag_locate(m, NGM_GENERIC_COOKIE, + NG_TAG_PRIO, NULL)) != NULL && (ptag->priority > NG_PRIO_CUTOFF) ) xmitq_p = (&sc->xmitq_hipri); - } else { + else xmitq_p = (&sc->xmitq); - } + s = splimp(); IF_LOCK(xmitq_p); if (_IF_QFULL(xmitq_p)) { _IF_DROP(xmitq_p); IF_UNLOCK(xmitq_p); splx(s); error = ENOBUFS; goto bad; } _IF_ENQUEUE(xmitq_p, m); IF_UNLOCK(xmitq_p); arstart(sc); splx(s); return (0); bad: /* * It was an error case. * check if we need to free the mbuf, and then return the error */ NG_FREE_M(m); - NG_FREE_META(meta); return (error); } /* * do local shutdown processing.. * this node will refuse to go away, unless the hardware says to.. * don't unref the node, or remove our name. just clear our links up. */ static int ngar_shutdown(node_p node) { struct ar_softc * sc = NG_NODE_PRIVATE(node); ar_down(sc); NG_NODE_UNREF(node); /* XXX need to drain the output queues! */ /* The node is dead, long live the node! */ /* stolen from the attach routine */ if (ng_make_node_common(&typestruct, &sc->node) != 0) return (0); sprintf(sc->nodename, "%s%d", NG_AR_NODE_TYPE, sc->unit); if (ng_name_node(sc->node, sc->nodename)) { sc->node = NULL; printf("node naming failed\n"); NG_NODE_UNREF(sc->node); /* node dissappears */ return (0); } NG_NODE_SET_PRIVATE(sc->node, sc); sc->running = 0; return (0); } /* already linked */ static int ngar_connect(hook_p hook) { /* probably not at splnet, force outward queueing */ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); /* be really amiable and just say "YUP that's OK by me! " */ return (0); } /* * notify on hook disconnection (destruction) * * Invalidate the private data associated with this dlci. * For this type, removal of the last link resets tries to destroy the node. * As the device still exists, the shutdown method will not actually * destroy the node, but reset the device and leave it 'fresh' :) * * The node removal code will remove all references except that owned by the * driver. */ static int ngar_disconnect(hook_p hook) { struct ar_softc * sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); int s; /* * If it's the data hook, then free resources etc. */ if (NG_HOOK_PRIVATE(hook)) { s = splimp(); sc->datahooks--; if (sc->datahooks == 0) ar_down(sc); splx(s); } else { sc->debug_hook = NULL; } return (0); } /* * called during bootup * or LKM loading to put this type into the list of known modules */ static void ngar_init(void *ignored) { if (ng_newtype(&typestruct)) printf("ngar install failed\n"); ngar_done_init = 1; } #endif /* NETGRAPH */ /* ********************************* END ************************************ */ Index: head/sys/dev/cp/if_cp.c =================================================================== --- head/sys/dev/cp/if_cp.c (revision 131107) +++ head/sys/dev/cp/if_cp.c (revision 131108) @@ -1,2487 +1,2491 @@ /* * Cronyx-Tau-PCI adapter driver for FreeBSD. * Supports PPP/HDLC, Cisco/HDLC and FrameRelay protocol in synchronous mode, * and asyncronous channels with full modem control. * Keepalive protocol implemented in both Cisco and PPP modes. * * Copyright (C) 1999-2004 Cronyx Engineering. * Author: Kurakin Roman, * * Copyright (C) 1999-2002 Cronyx Engineering. * Author: Serge Vakulenko, * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations a permission to use, * modify and redistribute this software in source and binary forms, * as long as this message is kept with the software, all derivative * works or modified versions. * * Cronyx Id: if_cp.c,v 1.1.2.41 2004/06/23 17:09:13 rik Exp $ */ #include __FBSDID("$FreeBSD$"); #include #if __FreeBSD_version >= 500000 # define NPCI 1 #else # include "pci.h" #endif #if NPCI > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __FreeBSD_version > 501000 # include # include #else # include # include #endif #include #include #include "opt_ng_cronyx.h" #ifdef NETGRAPH_CRONYX # include "opt_netgraph.h" # ifndef NETGRAPH # error #option NETGRAPH missed from configuration # endif # include # include # include #else # include # define PP_CISCO IFF_LINK2 # if __FreeBSD_version < 500000 # include # endif # include # define NBPFILTER NBPF #endif #include #include #include #include #include /* If we don't have Cronyx's sppp version, we don't have fr support via sppp */ #ifndef PP_FR #define PP_FR 0 #endif #define CP_DEBUG(d,s) ({if (d->chan->debug) {\ printf ("%s: ", d->name); printf s;}}) #define CP_DEBUG2(d,s) ({if (d->chan->debug>1) {\ printf ("%s: ", d->name); printf s;}}) #define CDEV_MAJOR 134 static int cp_probe __P((device_t)); static int cp_attach __P((device_t)); static int cp_detach __P((device_t)); static device_method_t cp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cp_probe), DEVMETHOD(device_attach, cp_attach), DEVMETHOD(device_detach, cp_detach), {0, 0} }; typedef struct _cp_dma_mem_t { unsigned long phys; void *virt; size_t size; #if __FreeBSD_version >= 500000 bus_dma_tag_t dmat; bus_dmamap_t mapp; #endif } cp_dma_mem_t; typedef struct _drv_t { char name [8]; cp_chan_t *chan; cp_board_t *board; cp_dma_mem_t dmamem; int running; #ifdef NETGRAPH char nodename [NG_NODELEN+1]; hook_p hook; hook_p debug_hook; node_p node; struct ifqueue queue; struct ifqueue hi_queue; short timeout; struct callout_handle timeout_handle; #else struct sppp pp; #endif struct cdev *devt; } drv_t; typedef struct _bdrv_t { cp_board_t *board; struct resource *cp_res; struct resource *cp_irq; void *cp_intrhand; cp_dma_mem_t dmamem; drv_t channel [NCHAN]; } bdrv_t; static driver_t cp_driver = { "cp", cp_methods, sizeof(bdrv_t), }; static devclass_t cp_devclass; static void cp_receive (cp_chan_t *c, unsigned char *data, int len); static void cp_transmit (cp_chan_t *c, void *attachment, int len); static void cp_error (cp_chan_t *c, int data); static void cp_up (drv_t *d); static void cp_start (drv_t *d); static void cp_down (drv_t *d); static void cp_watchdog (drv_t *d); #ifdef NETGRAPH extern struct ng_type typestruct; #else static void cp_ifstart (struct ifnet *ifp); static void cp_tlf (struct sppp *sp); static void cp_tls (struct sppp *sp); static void cp_ifwatchdog (struct ifnet *ifp); static int cp_sioctl (struct ifnet *ifp, u_long cmd, caddr_t data); static void cp_initialize (void *softc); #endif static cp_board_t *adapter [NBRD]; static drv_t *channel [NBRD*NCHAN]; static struct callout_handle led_timo [NBRD]; static struct callout_handle timeout_handle; static int cp_destroy = 0; /* * Print the mbuf chain, for debug purposes only. */ static void printmbuf (struct mbuf *m) { printf ("mbuf:"); for (; m; m=m->m_next) { if (m->m_flags & M_PKTHDR) printf (" HDR %d:", m->m_pkthdr.len); if (m->m_flags & M_EXT) printf (" EXT:"); printf (" %d", m->m_len); } printf ("\n"); } /* * Make an mbuf from data. */ static struct mbuf *makembuf (void *buf, unsigned len) { struct mbuf *m; MGETHDR (m, M_DONTWAIT, MT_DATA); if (! m) return 0; MCLGET (m, M_DONTWAIT); if (! (m->m_flags & M_EXT)) { m_freem (m); return 0; } m->m_pkthdr.len = m->m_len = len; bcopy (buf, mtod (m, caddr_t), len); return m; } static int cp_probe (device_t dev) { if ((pci_get_vendor (dev) == cp_vendor_id) && (pci_get_device (dev) == cp_device_id)) { device_set_desc (dev, "Cronyx-Tau-PCI serial adapter"); return 0; } return ENXIO; } static void cp_timeout (void *arg) { drv_t *d; int s, i; for (i=0; ichan->type) { case T_G703: cp_g703_timer (d->chan); break; case T_E1: cp_e1_timer (d->chan); break; case T_E3: case T_T3: case T_STS1: cp_e3_timer (d->chan); break; default: break; } splx (s); } s = splimp (); if (!cp_destroy) timeout_handle = timeout (cp_timeout, 0, hz); splx (s); } static void cp_led_off (void *arg) { cp_board_t *b = arg; int s = splimp (); if (cp_destroy) { splx (s); return; } cp_led (b, 0); led_timo[b->num].callout = 0; splx (s); } static void cp_intr (void *arg) { bdrv_t *bd = arg; cp_board_t *b = bd->board; int s = splimp (); if (cp_destroy) { splx (s); return; } /* Turn LED on. */ cp_led (b, 1); cp_interrupt (b); /* Turn LED off 50 msec later. */ if (!led_timo[b->num].callout) led_timo[b->num] = timeout (cp_led_off, b, hz/20); splx (s); } extern struct cdevsw cp_cdevsw; #if __FreeBSD_version >= 500000 static void cp_bus_dmamap_addr (void *arg, bus_dma_segment_t *segs, int nseg, int error) { unsigned long *addr; if (error) return; KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); addr = arg; *addr = segs->ds_addr; } static int cp_bus_dma_mem_alloc (int bnum, int cnum, cp_dma_mem_t *dmem) { int error; error = bus_dma_tag_create (NULL, 16, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, dmem->size, 1, dmem->size, 0, NULL, NULL, &dmem->dmat); if (error) { if (cnum >= 0) printf ("cp%d-%d: ", bnum, cnum); else printf ("cp%d: ", bnum); printf ("couldn't allocate tag for dma memory\n"); return 0; } error = bus_dmamem_alloc (dmem->dmat, (void **)&dmem->virt, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &dmem->mapp); if (error) { if (cnum >= 0) printf ("cp%d-%d: ", bnum, cnum); else printf ("cp%d: ", bnum); printf ("couldn't allocate mem for dma memory\n"); bus_dma_tag_destroy (dmem->dmat); return 0; } error = bus_dmamap_load (dmem->dmat, dmem->mapp, dmem->virt, dmem->size, cp_bus_dmamap_addr, &dmem->phys, 0); if (error) { if (cnum >= 0) printf ("cp%d-%d: ", bnum, cnum); else printf ("cp%d: ", bnum); printf ("couldn't load mem map for dma memory\n"); bus_dmamem_free (dmem->dmat, dmem->virt, dmem->mapp); bus_dma_tag_destroy (dmem->dmat); return 0; } return 1; } static void cp_bus_dma_mem_free (cp_dma_mem_t *dmem) { bus_dmamap_unload (dmem->dmat, dmem->mapp); bus_dmamem_free (dmem->dmat, dmem->virt, dmem->mapp); bus_dma_tag_destroy (dmem->dmat); } #else static int cp_bus_dma_mem_alloc (int bnum, int cnum, cp_dma_mem_t *dmem) { dmem->virt = contigmalloc (dmem->size, M_DEVBUF, M_WAITOK, 0x100000, 0xffffffff, 16, 0); if (dmem->virt == NULL) { if (cnum >= 0) printf ("cp%d-%d: ", bnum, cnum); else printf ("cp%d: ", bnum); printf ("couldn't allocate memory for dma memory\n", unit); return 0; } dmem->phys = vtophys (dmem->virt); return 1; } static void cp_bus_dma_mem_free (cp_dma_mem_t *dmem) { contigfree (dmem->virt, dmem->size, M_DEVBUF); } #endif /* * Called if the probe succeeded. */ static int cp_attach (device_t dev) { bdrv_t *bd = device_get_softc (dev); int unit = device_get_unit (dev); unsigned short res; vm_offset_t vbase; int rid, error; cp_board_t *b; cp_chan_t *c; drv_t *d; int s = splimp (); b = malloc (sizeof(cp_board_t), M_DEVBUF, M_WAITOK); if (!b) { printf ("cp%d: couldn't allocate memory\n", unit); splx (s); return (ENXIO); } adapter[unit] = b; bzero (b, sizeof(cp_board_t)); bd->board = b; b->sys = bd; rid = PCIR_BAR(0); bd->cp_res = bus_alloc_resource (dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, RF_ACTIVE); if (! bd->cp_res) { printf ("cp%d: cannot map memory\n", unit); free (b, M_DEVBUF); splx (s); return (ENXIO); } vbase = (vm_offset_t) rman_get_virtual (bd->cp_res); res = cp_init (b, unit, (u_char*) vbase); if (res) { printf ("cp%d: can't init, error code:%x\n", unit, res); bus_release_resource (dev, SYS_RES_MEMORY, PCIR_BAR(0), bd->cp_res); free (b, M_DEVBUF); splx (s); return (ENXIO); } bd->dmamem.size = sizeof(cp_qbuf_t); if (! cp_bus_dma_mem_alloc (unit, -1, &bd->dmamem)) { free (b, M_DEVBUF); splx (s); return (ENXIO); } cp_reset (b, bd->dmamem.virt, bd->dmamem.phys); rid = 0; bd->cp_irq = bus_alloc_resource (dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (! bd->cp_irq) { printf ("cp%d: cannot map interrupt\n", unit); bus_release_resource (dev, SYS_RES_MEMORY, PCIR_BAR(0), bd->cp_res); free (b, M_DEVBUF); splx (s); return (ENXIO); } error = bus_setup_intr (dev, bd->cp_irq, INTR_TYPE_NET, cp_intr, bd, &bd->cp_intrhand); if (error) { printf ("cp%d: cannot set up irq\n", unit); bus_release_resource (dev, SYS_RES_MEMORY, PCIR_BAR(0), bd->cp_res); bus_release_resource (dev, SYS_RES_IRQ, 0, bd->cp_irq); free (b, M_DEVBUF); splx (s); return (ENXIO); } printf ("cp%d: %s, clock %ld MHz\n", unit, b->name, b->osc / 1000000); for (c=b->chan; cchan+NCHAN; ++c) { if (! c->type) continue; d = &bd->channel[c->num]; d->dmamem.size = sizeof(cp_buf_t); if (! cp_bus_dma_mem_alloc (unit, c->num, &d->dmamem)) continue; channel [b->num*NCHAN + c->num] = d; sprintf (d->name, "cp%d.%d", b->num, c->num); d->board = b; d->chan = c; c->sys = d; #ifdef NETGRAPH if (ng_make_node_common (&typestruct, &d->node) != 0) { printf ("%s: cannot make common node\n", d->name); d->node = NULL; continue; } #if __FreeBSD_version >= 500000 NG_NODE_SET_PRIVATE (d->node, d); #else d->node->private = d; #endif sprintf (d->nodename, "%s%d", NG_CP_NODE_TYPE, c->board->num*NCHAN + c->num); if (ng_name_node (d->node, d->nodename)) { printf ("%s: cannot name node\n", d->nodename); #if __FreeBSD_version >= 500000 NG_NODE_UNREF (d->node); #else ng_rmnode (d->node); ng_unref (d->node); #endif continue; } d->queue.ifq_maxlen = IFQ_MAXLEN; d->hi_queue.ifq_maxlen = IFQ_MAXLEN; #if __FreeBSD_version >= 500000 mtx_init (&d->queue.ifq_mtx, "cp_queue", NULL, MTX_DEF); mtx_init (&d->hi_queue.ifq_mtx, "cp_queue_hi", NULL, MTX_DEF); #endif #else /*NETGRAPH*/ d->pp.pp_if.if_softc = d; #if __FreeBSD_version > 501000 if_initname (&d->pp.pp_if, "cp", b->num * NCHAN + c->num); #else d->pp.pp_if.if_unit = b->num * NCHAN + c->num; d->pp.pp_if.if_name = "cp"; #endif d->pp.pp_if.if_mtu = PP_MTU; d->pp.pp_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST; d->pp.pp_if.if_ioctl = cp_sioctl; d->pp.pp_if.if_start = cp_ifstart; d->pp.pp_if.if_watchdog = cp_ifwatchdog; d->pp.pp_if.if_init = cp_initialize; sppp_attach (&d->pp.pp_if); if_attach (&d->pp.pp_if); d->pp.pp_tlf = cp_tlf; d->pp.pp_tls = cp_tls; /* If BPF is in the kernel, call the attach for it. * The header size of PPP or Cisco/HDLC is 4 bytes. */ bpfattach (&d->pp.pp_if, DLT_PPP, 4); #endif /*NETGRAPH*/ cp_start_e1 (c); cp_start_chan (c, 1, 1, d->dmamem.virt, d->dmamem.phys); /* Register callback functions. */ cp_register_transmit (c, &cp_transmit); cp_register_receive (c, &cp_receive); cp_register_error (c, &cp_error); d->devt = make_dev (&cp_cdevsw, b->num*NCHAN+c->num, UID_ROOT, GID_WHEEL, 0600, "cp%d", b->num*NCHAN+c->num); } splx (s); return 0; } static int cp_detach (device_t dev) { bdrv_t *bd = device_get_softc (dev); cp_board_t *b = bd->board; cp_chan_t *c; int s = splimp (); /* Check if the device is busy (open). */ for (c=b->chan; cchan+NCHAN; ++c) { drv_t *d = (drv_t*) c->sys; if (! d || ! d->chan->type) continue; if (d->running) { splx (s); return EBUSY; } } /* Ok, we can unload driver */ /* At first we should stop all channels */ for (c=b->chan; cchan+NCHAN; ++c) { drv_t *d = (drv_t*) c->sys; if (! d || ! d->chan->type) continue; cp_stop_chan (c); cp_stop_e1 (c); cp_set_dtr (d->chan, 0); cp_set_rts (d->chan, 0); } /* Reset the adapter. */ cp_destroy = 1; cp_interrupt_poll (b, 1); cp_led_off (b); cp_reset (b, 0 ,0); if (led_timo[b->num].callout) untimeout (cp_led_off, b, led_timo[b->num]); for (c=b->chan; cchan+NCHAN; ++c) { drv_t *d = (drv_t*) c->sys; if (! d || ! d->chan->type) continue; #ifndef NETGRAPH #if __FreeBSD_version >= 410000 && NBPFILTER > 0 /* Detach from the packet filter list of interfaces. */ bpfdetach (&d->pp.pp_if); #endif /* Detach from the sync PPP list. */ sppp_detach (&d->pp.pp_if); /* Detach from the system list of interfaces. */ if_detach (&d->pp.pp_if); #else #if __FreeBSD_version >= 500000 if (d->node) { ng_rmnode_self (d->node); NG_NODE_UNREF (d->node); d->node = NULL; } mtx_destroy (&d->queue.ifq_mtx); mtx_destroy (&d->hi_queue.ifq_mtx); #else ng_rmnode (d->node); d->node = 0; #endif #endif destroy_dev (d->devt); } /* Disable the interrupt request. */ bus_teardown_intr (dev, bd->cp_irq, bd->cp_intrhand); bus_deactivate_resource (dev, SYS_RES_IRQ, 0, bd->cp_irq); bus_release_resource (dev, SYS_RES_IRQ, 0, bd->cp_irq); bus_release_resource (dev, SYS_RES_MEMORY, PCIR_BAR(0), bd->cp_res); cp_led_off (b); if (led_timo[b->num].callout) untimeout (cp_led_off, b, led_timo[b->num]); splx (s); s = splimp (); for (c=b->chan; cchan+NCHAN; ++c) { drv_t *d = (drv_t*) c->sys; if (! d || ! d->chan->type) continue; channel [b->num*NCHAN + c->num] = 0; /* Deallocate buffers. */ cp_bus_dma_mem_free (&d->dmamem); } adapter [b->num] = 0; cp_bus_dma_mem_free (&bd->dmamem); free (b, M_DEVBUF); splx (s); return 0; } #ifndef NETGRAPH static void cp_ifstart (struct ifnet *ifp) { drv_t *d = ifp->if_softc; cp_start (d); } static void cp_ifwatchdog (struct ifnet *ifp) { drv_t *d = ifp->if_softc; cp_watchdog (d); } static void cp_tlf (struct sppp *sp) { drv_t *d = sp->pp_if.if_softc; CP_DEBUG2 (d, ("cp_tlf\n")); /* cp_set_dtr (d->chan, 0);*/ /* cp_set_rts (d->chan, 0);*/ sp->pp_down (sp); } static void cp_tls (struct sppp *sp) { drv_t *d = sp->pp_if.if_softc; CP_DEBUG2 (d, ("cp_tls\n")); sp->pp_up (sp); } /* * Process an ioctl request. */ static int cp_sioctl (struct ifnet *ifp, u_long cmd, caddr_t data) { drv_t *d = ifp->if_softc; int error, s, was_up, should_be_up; was_up = (ifp->if_flags & IFF_RUNNING) != 0; error = sppp_ioctl (ifp, cmd, data); if (error) return error; if (! (ifp->if_flags & IFF_DEBUG)) d->chan->debug = 0; else if (! d->chan->debug) d->chan->debug = 1; switch (cmd) { default: CP_DEBUG2 (d, ("ioctl 0x%lx\n", cmd)); return 0; case SIOCADDMULTI: CP_DEBUG2 (d, ("ioctl SIOCADDMULTI\n")); return 0; case SIOCDELMULTI: CP_DEBUG2 (d, ("ioctl SIOCDELMULTI\n")); return 0; case SIOCSIFFLAGS: CP_DEBUG2 (d, ("ioctl SIOCSIFFLAGS\n")); break; case SIOCSIFADDR: CP_DEBUG2 (d, ("ioctl SIOCSIFADDR\n")); break; } /* We get here only in case of SIFFLAGS or SIFADDR. */ s = splimp (); should_be_up = (ifp->if_flags & IFF_RUNNING) != 0; if (! was_up && should_be_up) { /* Interface goes up -- start it. */ cp_up (d); cp_start (d); } else if (was_up && ! should_be_up) { /* Interface is going down -- stop it. */ /* if ((d->pp.pp_flags & PP_FR) || (ifp->if_flags & PP_CISCO))*/ cp_down (d); } CP_DEBUG (d, ("ioctl 0x%lx p4\n", cmd)); splx (s); return 0; } /* * Initialization of interface. * It seems to be never called by upper level? */ static void cp_initialize (void *softc) { drv_t *d = softc; CP_DEBUG (d, ("cp_initialize\n")); } #endif /*NETGRAPH*/ /* * Stop the interface. Called on splimp(). */ static void cp_down (drv_t *d) { CP_DEBUG (d, ("cp_down\n")); /* Interface is going down -- stop it. */ cp_set_dtr (d->chan, 0); cp_set_rts (d->chan, 0); d->running = 0; } /* * Start the interface. Called on splimp(). */ static void cp_up (drv_t *d) { CP_DEBUG (d, ("cp_up\n")); cp_set_dtr (d->chan, 1); cp_set_rts (d->chan, 1); d->running = 1; } /* * Start output on the interface. Get another datagram to send * off of the interface queue, and copy it to the interface * before starting the output. */ static void cp_send (drv_t *d) { struct mbuf *m; u_short len; CP_DEBUG2 (d, ("cp_send, tn=%d te=%d\n", d->chan->tn, d->chan->te)); /* No output if the interface is down. */ if (! d->running) return; /* No output if the modem is off. */ if (! (d->chan->lloop || d->chan->type != T_SERIAL || cp_get_dsr (d->chan))) return; while (cp_transmit_space (d->chan)) { /* Get the packet to send. */ #ifdef NETGRAPH IF_DEQUEUE (&d->hi_queue, m); if (! m) IF_DEQUEUE (&d->queue, m); #else m = sppp_dequeue (&d->pp.pp_if); #endif if (! m) return; #ifndef NETGRAPH if (d->pp.pp_if.if_bpf) #if __FreeBSD_version >= 500000 BPF_MTAP (&d->pp.pp_if, m); #else bpf_mtap (&d->pp.pp_if, m); #endif #endif len = m->m_pkthdr.len; if (len >= BUFSZ) printf ("%s: too long packet: %d bytes: ", d->name, len); else if (! m->m_next) cp_send_packet (d->chan, (u_char*) mtod (m, caddr_t), len, 0); else { u_char *buf = d->chan->tbuf[d->chan->te]; m_copydata (m, 0, len, buf); cp_send_packet (d->chan, buf, len, 0); } m_freem (m); /* Set up transmit timeout, if the transmit ring is not empty.*/ #ifdef NETGRAPH d->timeout = 10; #else d->pp.pp_if.if_timer = 10; #endif } #ifndef NETGRAPH d->pp.pp_if.if_flags |= IFF_OACTIVE; #endif } /* * Start output on the interface. * Always called on splimp(). */ static void cp_start (drv_t *d) { if (d->running) { if (! d->chan->dtr) cp_set_dtr (d->chan, 1); if (! d->chan->rts) cp_set_rts (d->chan, 1); cp_send (d); } } /* * Handle transmit timeouts. * Recover after lost transmit interrupts. * Always called on splimp(). */ static void cp_watchdog (drv_t *d) { CP_DEBUG (d, ("device timeout\n")); if (d->running) { int s = splimp (); cp_stop_chan (d->chan); cp_stop_e1 (d->chan); cp_start_e1 (d->chan); cp_start_chan (d->chan, 1, 1, 0, 0); cp_set_dtr (d->chan, 1); cp_set_rts (d->chan, 1); cp_start (d); splx (s); } } static void cp_transmit (cp_chan_t *c, void *attachment, int len) { drv_t *d = c->sys; #ifdef NETGRAPH d->timeout = 0; #else ++d->pp.pp_if.if_opackets; d->pp.pp_if.if_flags &= ~IFF_OACTIVE; d->pp.pp_if.if_timer = 0; #endif cp_start (d); } static void cp_receive (cp_chan_t *c, unsigned char *data, int len) { drv_t *d = c->sys; struct mbuf *m; #if __FreeBSD_version >= 500000 && defined NETGRAPH int error; #endif if (! d->running) return; m = makembuf (data, len); if (! m) { CP_DEBUG (d, ("no memory for packet\n")); #ifndef NETGRAPH ++d->pp.pp_if.if_iqdrops; #endif return; } if (c->debug > 1) printmbuf (m); #ifdef NETGRAPH m->m_pkthdr.rcvif = 0; #if __FreeBSD_version >= 500000 NG_SEND_DATA_ONLY (error, d->hook, m); #else ng_queue_data (d->hook, m, 0); #endif #else ++d->pp.pp_if.if_ipackets; m->m_pkthdr.rcvif = &d->pp.pp_if; /* Check if there's a BPF listener on this interface. * If so, hand off the raw packet to bpf. */ if (d->pp.pp_if.if_bpf) #if __FreeBSD_version >= 500000 BPF_TAP (&d->pp.pp_if, data, len); #else bpf_tap (&d->pp.pp_if, data, len); #endif sppp_input (&d->pp.pp_if, m); #endif } static void cp_error (cp_chan_t *c, int data) { drv_t *d = c->sys; switch (data) { case CP_FRAME: CP_DEBUG (d, ("frame error\n")); #ifndef NETGRAPH ++d->pp.pp_if.if_ierrors; #endif break; case CP_CRC: CP_DEBUG (d, ("crc error\n")); #ifndef NETGRAPH ++d->pp.pp_if.if_ierrors; #endif break; case CP_OVERRUN: CP_DEBUG (d, ("overrun error\n")); #ifndef NETGRAPH ++d->pp.pp_if.if_collisions; ++d->pp.pp_if.if_ierrors; #endif break; case CP_OVERFLOW: CP_DEBUG (d, ("overflow error\n")); #ifndef NETGRAPH ++d->pp.pp_if.if_ierrors; #endif break; case CP_UNDERRUN: CP_DEBUG (d, ("underrun error\n")); #ifdef NETGRAPH d->timeout = 0; #else ++d->pp.pp_if.if_oerrors; d->pp.pp_if.if_flags &= ~IFF_OACTIVE; d->pp.pp_if.if_timer = 0; #endif cp_start (d); break; default: CP_DEBUG (d, ("error #%d\n", data)); break; } } /* * You also need read, write, open, close routines. * This should get you started */ #if __FreeBSD_version < 500000 static int cp_open (dev_t dev, int oflags, int devtype, struct proc *p) #else static int cp_open (struct cdev *dev, int oflags, int devtype, struct thread *td) #endif { int unit = minor (dev); drv_t *d; if (unit >= NBRD*NCHAN || ! (d = channel[unit])) return ENXIO; CP_DEBUG2 (d, ("cp_open\n")); return 0; } /* * Only called on the LAST close. */ #if __FreeBSD_version < 500000 static int cp_close (dev_t dev, int fflag, int devtype, struct proc *p) #else static int cp_close (struct cdev *dev, int fflag, int devtype, struct thread *td) #endif { drv_t *d = channel [minor (dev)]; CP_DEBUG2 (d, ("cp_close\n")); return 0; } static int cp_modem_status (cp_chan_t *c) { drv_t *d = c->sys; int status, s; status = d->running ? TIOCM_LE : 0; s = splimp (); if (cp_get_cd (c)) status |= TIOCM_CD; if (cp_get_cts (c)) status |= TIOCM_CTS; if (cp_get_dsr (c)) status |= TIOCM_DSR; if (c->dtr) status |= TIOCM_DTR; if (c->rts) status |= TIOCM_RTS; splx (s); return status; } #if __FreeBSD_version < 500000 static int cp_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) #else static int cp_ioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) #endif { drv_t *d = channel [minor (dev)]; cp_chan_t *c = d->chan; struct serial_statistics *st; struct e1_statistics *opte1; struct e3_statistics *opte3; int error, s; char mask[16]; switch (cmd) { case SERIAL_GETREGISTERED: CP_DEBUG2 (d, ("ioctl: getregistered\n")); bzero (mask, sizeof(mask)); for (s=0; spp.pp_flags & PP_FR) ? "fr" : (d->pp.pp_if.if_flags & PP_CISCO) ? "cisco" : "ppp"); return 0; case SERIAL_SETPROTO: CP_DEBUG2 (d, ("ioctl: setproto\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if (d->pp.pp_if.if_flags & IFF_RUNNING) return EBUSY; if (! strcmp ("cisco", (char*)data)) { d->pp.pp_flags &= ~(PP_FR); d->pp.pp_flags |= PP_KEEPALIVE; d->pp.pp_if.if_flags |= PP_CISCO; } else if (! strcmp ("fr", (char*)data) && !PP_FR) { d->pp.pp_if.if_flags &= ~(PP_CISCO); d->pp.pp_flags |= PP_FR | PP_KEEPALIVE; } else if (! strcmp ("ppp", (char*)data)) { d->pp.pp_flags &= ~PP_FR; d->pp.pp_flags &= ~PP_KEEPALIVE; d->pp.pp_if.if_flags &= ~(PP_CISCO); } else return EINVAL; return 0; case SERIAL_GETKEEPALIVE: CP_DEBUG2 (d, ("ioctl: getkeepalive\n")); if ((d->pp.pp_flags & PP_FR) || (d->pp.pp_if.if_flags & PP_CISCO)) return EINVAL; *(int*)data = (d->pp.pp_flags & PP_KEEPALIVE) ? 1 : 0; return 0; case SERIAL_SETKEEPALIVE: CP_DEBUG2 (d, ("ioctl: setkeepalive\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if ((d->pp.pp_flags & PP_FR) || (d->pp.pp_if.if_flags & PP_CISCO)) return EINVAL; s = splimp (); if (*(int*)data) d->pp.pp_flags |= PP_KEEPALIVE; else d->pp.pp_flags &= ~PP_KEEPALIVE; splx (s); return 0; #endif /*NETGRAPH*/ case SERIAL_GETMODE: CP_DEBUG2 (d, ("ioctl: getmode\n")); *(int*)data = SERIAL_HDLC; return 0; case SERIAL_SETMODE: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (*(int*)data != SERIAL_HDLC) return EINVAL; return 0; case SERIAL_GETCFG: CP_DEBUG2 (d, ("ioctl: getcfg\n")); if (c->type != T_E1 || c->unfram) return EINVAL; *(char*)data = c->board->mux ? 'c' : 'a'; return 0; case SERIAL_SETCFG: CP_DEBUG2 (d, ("ioctl: setcfg\n")); #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_E1) return EINVAL; s = splimp (); cp_set_mux (c->board, *((char*)data) == 'c'); splx (s); return 0; case SERIAL_GETSTAT: CP_DEBUG2 (d, ("ioctl: getstat\n")); st = (struct serial_statistics*) data; st->rintr = c->rintr; st->tintr = c->tintr; st->mintr = 0; st->ibytes = c->ibytes; st->ipkts = c->ipkts; st->obytes = c->obytes; st->opkts = c->opkts; st->ierrs = c->overrun + c->frame + c->crc; st->oerrs = c->underrun; return 0; case SERIAL_GETESTAT: CP_DEBUG2 (d, ("ioctl: getestat\n")); if (c->type != T_E1 && c->type != T_G703) return EINVAL; opte1 = (struct e1_statistics*) data; opte1->status = c->status; opte1->cursec = c->cursec; opte1->totsec = c->totsec + c->cursec; opte1->currnt.bpv = c->currnt.bpv; opte1->currnt.fse = c->currnt.fse; opte1->currnt.crce = c->currnt.crce; opte1->currnt.rcrce = c->currnt.rcrce; opte1->currnt.uas = c->currnt.uas; opte1->currnt.les = c->currnt.les; opte1->currnt.es = c->currnt.es; opte1->currnt.bes = c->currnt.bes; opte1->currnt.ses = c->currnt.ses; opte1->currnt.oofs = c->currnt.oofs; opte1->currnt.css = c->currnt.css; opte1->currnt.dm = c->currnt.dm; opte1->total.bpv = c->total.bpv + c->currnt.bpv; opte1->total.fse = c->total.fse + c->currnt.fse; opte1->total.crce = c->total.crce + c->currnt.crce; opte1->total.rcrce = c->total.rcrce + c->currnt.rcrce; opte1->total.uas = c->total.uas + c->currnt.uas; opte1->total.les = c->total.les + c->currnt.les; opte1->total.es = c->total.es + c->currnt.es; opte1->total.bes = c->total.bes + c->currnt.bes; opte1->total.ses = c->total.ses + c->currnt.ses; opte1->total.oofs = c->total.oofs + c->currnt.oofs; opte1->total.css = c->total.css + c->currnt.css; opte1->total.dm = c->total.dm + c->currnt.dm; for (s=0; s<48; ++s) { opte1->interval[s].bpv = c->interval[s].bpv; opte1->interval[s].fse = c->interval[s].fse; opte1->interval[s].crce = c->interval[s].crce; opte1->interval[s].rcrce = c->interval[s].rcrce; opte1->interval[s].uas = c->interval[s].uas; opte1->interval[s].les = c->interval[s].les; opte1->interval[s].es = c->interval[s].es; opte1->interval[s].bes = c->interval[s].bes; opte1->interval[s].ses = c->interval[s].ses; opte1->interval[s].oofs = c->interval[s].oofs; opte1->interval[s].css = c->interval[s].css; opte1->interval[s].dm = c->interval[s].dm; } return 0; case SERIAL_GETE3STAT: CP_DEBUG2 (d, ("ioctl: gete3stat\n")); if (c->type != T_E3 && c->type != T_T3 && c->type != T_STS1) return EINVAL; opte3 = (struct e3_statistics*) data; opte3->status = c->e3status; opte3->cursec = (c->e3csec_5 * 2 + 1) / 10; opte3->totsec = c->e3tsec + opte3->cursec; opte3->ccv = c->e3ccv; opte3->tcv = c->e3tcv + opte3->ccv; for (s = 0; s < 48; ++s) { opte3->icv[s] = c->e3icv[s]; } return 0; case SERIAL_CLRSTAT: CP_DEBUG2 (d, ("ioctl: clrstat\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; c->rintr = 0; c->tintr = 0; c->ibytes = 0; c->obytes = 0; c->ipkts = 0; c->opkts = 0; c->overrun = 0; c->frame = 0; c->crc = 0; c->underrun = 0; bzero (&c->currnt, sizeof (c->currnt)); bzero (&c->total, sizeof (c->total)); bzero (c->interval, sizeof (c->interval)); c->e3ccv = 0; c->e3tcv = 0; bzero (c->e3icv, sizeof (c->e3icv)); return 0; case SERIAL_GETBAUD: CP_DEBUG2 (d, ("ioctl: getbaud\n")); *(long*)data = c->baud; return 0; case SERIAL_SETBAUD: CP_DEBUG2 (d, ("ioctl: setbaud\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; s = splimp (); cp_set_baud (c, *(long*)data); splx (s); return 0; case SERIAL_GETLOOP: CP_DEBUG2 (d, ("ioctl: getloop\n")); *(int*)data = c->lloop; return 0; case SERIAL_SETLOOP: CP_DEBUG2 (d, ("ioctl: setloop\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; s = splimp (); cp_set_lloop (c, *(int*)data); splx (s); return 0; case SERIAL_GETDPLL: CP_DEBUG2 (d, ("ioctl: getdpll\n")); if (c->type != T_SERIAL) return EINVAL; *(int*)data = c->dpll; return 0; case SERIAL_SETDPLL: CP_DEBUG2 (d, ("ioctl: setdpll\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_SERIAL) return EINVAL; s = splimp (); cp_set_dpll (c, *(int*)data); splx (s); return 0; case SERIAL_GETNRZI: CP_DEBUG2 (d, ("ioctl: getnrzi\n")); if (c->type != T_SERIAL) return EINVAL; *(int*)data = c->nrzi; return 0; case SERIAL_SETNRZI: CP_DEBUG2 (d, ("ioctl: setnrzi\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_SERIAL) return EINVAL; s = splimp (); cp_set_nrzi (c, *(int*)data); splx (s); return 0; case SERIAL_GETDEBUG: CP_DEBUG2 (d, ("ioctl: getdebug\n")); *(int*)data = d->chan->debug; return 0; case SERIAL_SETDEBUG: CP_DEBUG2 (d, ("ioctl: setdebug\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; d->chan->debug = *(int*)data; #ifndef NETGRAPH if (d->chan->debug) d->pp.pp_if.if_flags |= IFF_DEBUG; else d->pp.pp_if.if_flags &= ~IFF_DEBUG; #endif return 0; case SERIAL_GETHIGAIN: CP_DEBUG2 (d, ("ioctl: gethigain\n")); if (c->type != T_E1) return EINVAL; *(int*)data = c->higain; return 0; case SERIAL_SETHIGAIN: CP_DEBUG2 (d, ("ioctl: sethigain\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_E1) return EINVAL; s = splimp (); cp_set_higain (c, *(int*)data); splx (s); return 0; case SERIAL_GETPHONY: CP_DEBUG2 (d, ("ioctl: getphony\n")); if (c->type != T_E1) return EINVAL; *(int*)data = c->phony; return 0; case SERIAL_SETPHONY: CP_DEBUG2 (d, ("ioctl: setphony\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_E1) return EINVAL; s = splimp (); cp_set_phony (c, *(int*)data); splx (s); return 0; case SERIAL_GETUNFRAM: CP_DEBUG2 (d, ("ioctl: getunfram\n")); if (c->type != T_E1) return EINVAL; *(int*)data = c->unfram; return 0; case SERIAL_SETUNFRAM: CP_DEBUG2 (d, ("ioctl: setunfram\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_E1) return EINVAL; s = splimp (); cp_set_unfram (c, *(int*)data); splx (s); return 0; case SERIAL_GETSCRAMBLER: CP_DEBUG2 (d, ("ioctl: getscrambler\n")); if (c->type != T_G703 && !c->unfram) return EINVAL; *(int*)data = c->scrambler; return 0; case SERIAL_SETSCRAMBLER: CP_DEBUG2 (d, ("ioctl: setscrambler\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_G703 && !c->unfram) return EINVAL; s = splimp (); cp_set_scrambler (c, *(int*)data); splx (s); return 0; case SERIAL_GETMONITOR: CP_DEBUG2 (d, ("ioctl: getmonitor\n")); if (c->type != T_E1 && c->type != T_E3 && c->type != T_T3 && c->type != T_STS1) return EINVAL; *(int*)data = c->monitor; return 0; case SERIAL_SETMONITOR: CP_DEBUG2 (d, ("ioctl: setmonitor\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_E1) return EINVAL; s = splimp (); cp_set_monitor (c, *(int*)data); splx (s); return 0; case SERIAL_GETUSE16: CP_DEBUG2 (d, ("ioctl: getuse16\n")); if (c->type != T_E1 || c->unfram) return EINVAL; *(int*)data = c->use16; return 0; case SERIAL_SETUSE16: CP_DEBUG2 (d, ("ioctl: setuse16\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_E1) return EINVAL; s = splimp (); cp_set_use16 (c, *(int*)data); splx (s); return 0; case SERIAL_GETCRC4: CP_DEBUG2 (d, ("ioctl: getcrc4\n")); if (c->type != T_E1 || c->unfram) return EINVAL; *(int*)data = c->crc4; return 0; case SERIAL_SETCRC4: CP_DEBUG2 (d, ("ioctl: setcrc4\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_E1) return EINVAL; s = splimp (); cp_set_crc4 (c, *(int*)data); splx (s); return 0; case SERIAL_GETCLK: CP_DEBUG2 (d, ("ioctl: getclk\n")); if (c->type != T_E1 && c->type != T_G703 && c->type != T_E3 && c->type != T_T3 && c->type != T_STS1) return EINVAL; switch (c->gsyn) { default: *(int*)data = E1CLK_INTERNAL; break; case GSYN_RCV: *(int*)data = E1CLK_RECEIVE; break; case GSYN_RCV0: *(int*)data = E1CLK_RECEIVE_CHAN0; break; case GSYN_RCV1: *(int*)data = E1CLK_RECEIVE_CHAN1; break; case GSYN_RCV2: *(int*)data = E1CLK_RECEIVE_CHAN2; break; case GSYN_RCV3: *(int*)data = E1CLK_RECEIVE_CHAN3; break; } return 0; case SERIAL_SETCLK: CP_DEBUG2 (d, ("ioctl: setclk\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_E1 && c->type != T_G703 && c->type != T_E3 && c->type != T_T3 && c->type != T_STS1) return EINVAL; s = splimp (); switch (*(int*)data) { default: cp_set_gsyn (c, GSYN_INT); break; case E1CLK_RECEIVE: cp_set_gsyn (c, GSYN_RCV); break; case E1CLK_RECEIVE_CHAN0: cp_set_gsyn (c, GSYN_RCV0); break; case E1CLK_RECEIVE_CHAN1: cp_set_gsyn (c, GSYN_RCV1); break; case E1CLK_RECEIVE_CHAN2: cp_set_gsyn (c, GSYN_RCV2); break; case E1CLK_RECEIVE_CHAN3: cp_set_gsyn (c, GSYN_RCV3); break; } splx (s); return 0; case SERIAL_GETTIMESLOTS: CP_DEBUG2 (d, ("ioctl: gettimeslots\n")); if ((c->type != T_E1 || c->unfram) && c->type != T_DATA) return EINVAL; *(u_long*)data = c->ts; return 0; case SERIAL_SETTIMESLOTS: CP_DEBUG2 (d, ("ioctl: settimeslots\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if ((c->type != T_E1 || c->unfram) && c->type != T_DATA) return EINVAL; s = splimp (); cp_set_ts (c, *(u_long*)data); splx (s); return 0; case SERIAL_GETINVCLK: CP_DEBUG2 (d, ("ioctl: getinvclk\n")); #if 1 return EINVAL; #else if (c->type != T_SERIAL) return EINVAL; *(int*)data = c->invtxc; return 0; #endif case SERIAL_SETINVCLK: CP_DEBUG2 (d, ("ioctl: setinvclk\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_SERIAL) return EINVAL; s = splimp (); cp_set_invtxc (c, *(int*)data); cp_set_invrxc (c, *(int*)data); splx (s); return 0; case SERIAL_GETINVTCLK: CP_DEBUG2 (d, ("ioctl: getinvtclk\n")); if (c->type != T_SERIAL) return EINVAL; *(int*)data = c->invtxc; return 0; case SERIAL_SETINVTCLK: CP_DEBUG2 (d, ("ioctl: setinvtclk\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_SERIAL) return EINVAL; s = splimp (); cp_set_invtxc (c, *(int*)data); splx (s); return 0; case SERIAL_GETINVRCLK: CP_DEBUG2 (d, ("ioctl: getinvrclk\n")); if (c->type != T_SERIAL) return EINVAL; *(int*)data = c->invrxc; return 0; case SERIAL_SETINVRCLK: CP_DEBUG2 (d, ("ioctl: setinvrclk\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; if (c->type != T_SERIAL) return EINVAL; s = splimp (); cp_set_invrxc (c, *(int*)data); splx (s); return 0; case SERIAL_GETLEVEL: CP_DEBUG2 (d, ("ioctl: getlevel\n")); if (c->type != T_G703) return EINVAL; s = splimp (); *(int*)data = cp_get_lq (c); splx (s); return 0; #if 0 case SERIAL_RESET: CP_DEBUG2 (d, ("ioctl: reset\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; s = splimp (); cp_reset (c->board, 0, 0); splx (s); return 0; case SERIAL_HARDRESET: CP_DEBUG2 (d, ("ioctl: hardreset\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; s = splimp (); /* hard_reset (c->board); */ splx (s); return 0; #endif case SERIAL_GETCABLE: CP_DEBUG2 (d, ("ioctl: getcable\n")); if (c->type != T_SERIAL) return EINVAL; s = splimp (); *(int*)data = cp_get_cable (c); splx (s); return 0; case SERIAL_GETDIR: CP_DEBUG2 (d, ("ioctl: getdir\n")); if (c->type != T_E1 && c->type != T_DATA) return EINVAL; *(int*)data = c->dir; return 0; case SERIAL_SETDIR: CP_DEBUG2 (d, ("ioctl: setdir\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; s = splimp (); cp_set_dir (c, *(int*)data); splx (s); return 0; case SERIAL_GETRLOOP: CP_DEBUG2 (d, ("ioctl: getrloop\n")); if (c->type != T_G703 && c->type != T_E3 && c->type != T_T3 && c->type != T_STS1) return EINVAL; *(int*)data = cp_get_rloop (c); return 0; case SERIAL_SETRLOOP: CP_DEBUG2 (d, ("ioctl: setloop\n")); if (c->type != T_E3 && c->type != T_T3 && c->type != T_STS1) return EINVAL; /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; s = splimp (); cp_set_rloop (c, *(int*)data); splx (s); return 0; case SERIAL_GETCABLEN: CP_DEBUG2 (d, ("ioctl: getcablen\n")); if (c->type != T_T3 && c->type != T_STS1) return EINVAL; *(int*)data = c->cablen; return 0; case SERIAL_SETCABLEN: CP_DEBUG2 (d, ("ioctl: setloop\n")); if (c->type != T_T3 && c->type != T_STS1) return EINVAL; /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else error = suser (td); #endif if (error) return error; s = splimp (); cp_set_cablen (c, *(int*)data); splx (s); return 0; case TIOCSDTR: /* Set DTR */ s = splimp (); cp_set_dtr (c, 1); splx (s); return 0; case TIOCCDTR: /* Clear DTR */ s = splimp (); cp_set_dtr (c, 0); splx (s); return 0; case TIOCMSET: /* Set DTR/RTS */ s = splimp (); cp_set_dtr (c, (*(int*)data & TIOCM_DTR) ? 1 : 0); cp_set_rts (c, (*(int*)data & TIOCM_RTS) ? 1 : 0); splx (s); return 0; case TIOCMBIS: /* Add DTR/RTS */ s = splimp (); if (*(int*)data & TIOCM_DTR) cp_set_dtr (c, 1); if (*(int*)data & TIOCM_RTS) cp_set_rts (c, 1); splx (s); return 0; case TIOCMBIC: /* Clear DTR/RTS */ s = splimp (); if (*(int*)data & TIOCM_DTR) cp_set_dtr (c, 0); if (*(int*)data & TIOCM_RTS) cp_set_rts (c, 0); splx (s); return 0; case TIOCMGET: /* Get modem status */ *(int*)data = cp_modem_status (c); return 0; } return ENOTTY; } #if __FreeBSD_version < 500000 static struct cdevsw cp_cdevsw = { cp_open, cp_close, noread, nowrite, cp_ioctl, nopoll, nommap, nostrategy, "cp", CDEV_MAJOR, nodump, nopsize, D_NAGGED, -1 }; #elif __FreeBSD_version == 500000 static struct cdevsw cp_cdevsw = { cp_open, cp_close, noread, nowrite, cp_ioctl, nopoll, nommap, nostrategy, "cp", CDEV_MAJOR, nodump, nopsize, D_NAGGED, }; #elif __FreeBSD_version <= 501000 static struct cdevsw cp_cdevsw = { .d_open = cp_open, .d_close = cp_close, .d_read = noread, .d_write = nowrite, .d_ioctl = cp_ioctl, .d_poll = nopoll, .d_mmap = nommap, .d_strategy = nostrategy, .d_name = "cp", .d_maj = CDEV_MAJOR, .d_dump = nodump, .d_flags = D_NAGGED, }; #elif __FreeBSD_version < 502103 static struct cdevsw cp_cdevsw = { .d_open = cp_open, .d_close = cp_close, .d_ioctl = cp_ioctl, .d_name = "cp", .d_maj = CDEV_MAJOR, .d_flags = D_NAGGED, }; #else /* __FreeBSD_version >= 502103 */ static struct cdevsw cp_cdevsw = { .d_version = D_VERSION, .d_open = cp_open, .d_close = cp_close, .d_ioctl = cp_ioctl, .d_name = "cp", .d_maj = CDEV_MAJOR, .d_flags = D_NEEDGIANT, }; #endif #ifdef NETGRAPH #if __FreeBSD_version >= 500000 static int ng_cp_constructor (node_p node) { drv_t *d = NG_NODE_PRIVATE (node); #else static int ng_cp_constructor (node_p *node) { drv_t *d = (*node)->private; #endif CP_DEBUG (d, ("Constructor\n")); return EINVAL; } static int ng_cp_newhook (node_p node, hook_p hook, const char *name) { int s; #if __FreeBSD_version >= 500000 drv_t *d = NG_NODE_PRIVATE (node); #else drv_t *d = node->private; #endif CP_DEBUG (d, ("Newhook\n")); /* Attach debug hook */ if (strcmp (name, NG_CP_HOOK_DEBUG) == 0) { #if __FreeBSD_version >= 500000 NG_HOOK_SET_PRIVATE (hook, NULL); #else hook->private = 0; #endif d->debug_hook = hook; return 0; } /* Check for raw hook */ if (strcmp (name, NG_CP_HOOK_RAW) != 0) return EINVAL; #if __FreeBSD_version >= 500000 NG_HOOK_SET_PRIVATE (hook, d); #else hook->private = d; #endif d->hook = hook; s = splimp (); cp_up (d); splx (s); return 0; } static char *format_timeslots (u_long s) { static char buf [100]; char *p = buf; int i; for (i=1; i<32; ++i) if ((s >> i) & 1) { int prev = (i > 1) & (s >> (i-1)); int next = (i < 31) & (s >> (i+1)); if (prev) { if (next) continue; *p++ = '-'; } else if (p > buf) *p++ = ','; if (i >= 10) *p++ = '0' + i / 10; *p++ = '0' + i % 10; } *p = 0; return buf; } static int print_modems (char *s, cp_chan_t *c, int need_header) { int status = cp_modem_status (c); int length = 0; if (need_header) length += sprintf (s + length, " LE DTR DSR RTS CTS CD\n"); length += sprintf (s + length, "%4s %4s %4s %4s %4s %4s\n", status & TIOCM_LE ? "On" : "-", status & TIOCM_DTR ? "On" : "-", status & TIOCM_DSR ? "On" : "-", status & TIOCM_RTS ? "On" : "-", status & TIOCM_CTS ? "On" : "-", status & TIOCM_CD ? "On" : "-"); return length; } static int print_stats (char *s, cp_chan_t *c, int need_header) { int length = 0; if (need_header) length += sprintf (s + length, " Rintr Tintr Mintr Ibytes Ipkts Ierrs Obytes Opkts Oerrs\n"); length += sprintf (s + length, "%7ld %7ld %7ld %8lu %7ld %7ld %8lu %7ld %7ld\n", c->rintr, c->tintr, 0l, (unsigned long) c->ibytes, c->ipkts, c->overrun + c->frame + c->crc, (unsigned long) c->obytes, c->opkts, c->underrun); return length; } static char *format_e1_status (u_char status) { static char buf [80]; if (status & E1_NOALARM) return "Ok"; buf[0] = 0; if (status & E1_LOS) strcat (buf, ",LOS"); if (status & E1_AIS) strcat (buf, ",AIS"); if (status & E1_LOF) strcat (buf, ",LOF"); if (status & E1_LOMF) strcat (buf, ",LOMF"); if (status & E1_FARLOF) strcat (buf, ",FARLOF"); if (status & E1_AIS16) strcat (buf, ",AIS16"); if (status & E1_FARLOMF) strcat (buf, ",FARLOMF"); if (status & E1_TSTREQ) strcat (buf, ",TSTREQ"); if (status & E1_TSTERR) strcat (buf, ",TSTERR"); if (buf[0] == ',') return buf+1; return "Unknown"; } static int print_frac (char *s, int leftalign, u_long numerator, u_long divider) { int n, length = 0; if (numerator < 1 || divider < 1) { length += sprintf (s+length, leftalign ? "/- " : " -"); return length; } n = (int) (0.5 + 1000.0 * numerator / divider); if (n < 1000) { length += sprintf (s+length, leftalign ? "/.%-3d" : " .%03d", n); return length; } *(s + length) = leftalign ? '/' : ' '; length ++; if (n >= 1000000) n = (n+500) / 1000 * 1000; else if (n >= 100000) n = (n+50) / 100 * 100; else if (n >= 10000) n = (n+5) / 10 * 10; switch (n) { case 1000: length += printf (s+length, ".999"); return length; case 10000: n = 9990; break; case 100000: n = 99900; break; case 1000000: n = 999000; break; } if (n < 10000) length += sprintf (s+length, "%d.%d", n/1000, n/10%100); else if (n < 100000) length += sprintf (s+length, "%d.%d", n/1000, n/100%10); else if (n < 1000000) length += sprintf (s+length, "%d.", n/1000); else length += sprintf (s+length, "%d", n/1000); return length; } static int print_e1_stats (char *s, cp_chan_t *c) { struct e1_counters total; u_long totsec; int length = 0; totsec = c->totsec + c->cursec; total.bpv = c->total.bpv + c->currnt.bpv; total.fse = c->total.fse + c->currnt.fse; total.crce = c->total.crce + c->currnt.crce; total.rcrce = c->total.rcrce + c->currnt.rcrce; total.uas = c->total.uas + c->currnt.uas; total.les = c->total.les + c->currnt.les; total.es = c->total.es + c->currnt.es; total.bes = c->total.bes + c->currnt.bes; total.ses = c->total.ses + c->currnt.ses; total.oofs = c->total.oofs + c->currnt.oofs; total.css = c->total.css + c->currnt.css; total.dm = c->total.dm + c->currnt.dm; length += sprintf (s + length, " Unav/Degr Bpv/Fsyn CRC/RCRC Err/Lerr Sev/Bur Oof/Slp Status\n"); /* Unavailable seconds, degraded minutes */ length += print_frac (s + length, 0, c->currnt.uas, c->cursec); length += print_frac (s + length, 1, 60 * c->currnt.dm, c->cursec); /* Bipolar violations, frame sync errors */ length += print_frac (s + length, 0, c->currnt.bpv, c->cursec); length += print_frac (s + length, 1, c->currnt.fse, c->cursec); /* CRC errors, remote CRC errors (E-bit) */ length += print_frac (s + length, 0, c->currnt.crce, c->cursec); length += print_frac (s + length, 1, c->currnt.rcrce, c->cursec); /* Errored seconds, line errored seconds */ length += print_frac (s + length, 0, c->currnt.es, c->cursec); length += print_frac (s + length, 1, c->currnt.les, c->cursec); /* Severely errored seconds, burst errored seconds */ length += print_frac (s + length, 0, c->currnt.ses, c->cursec); length += print_frac (s + length, 1, c->currnt.bes, c->cursec); /* Out of frame seconds, controlled slip seconds */ length += print_frac (s + length, 0, c->currnt.oofs, c->cursec); length += print_frac (s + length, 1, c->currnt.css, c->cursec); length += sprintf (s + length, " %s\n", format_e1_status (c->status)); /* Print total statistics. */ length += print_frac (s + length, 0, total.uas, totsec); length += print_frac (s + length, 1, 60 * total.dm, totsec); length += print_frac (s + length, 0, total.bpv, totsec); length += print_frac (s + length, 1, total.fse, totsec); length += print_frac (s + length, 0, total.crce, totsec); length += print_frac (s + length, 1, total.rcrce, totsec); length += print_frac (s + length, 0, total.es, totsec); length += print_frac (s + length, 1, total.les, totsec); length += print_frac (s + length, 0, total.ses, totsec); length += print_frac (s + length, 1, total.bes, totsec); length += print_frac (s + length, 0, total.oofs, totsec); length += print_frac (s + length, 1, total.css, totsec); length += sprintf (s + length, " -- Total\n"); return length; } static int print_chan (char *s, cp_chan_t *c) { drv_t *d = c->sys; int length = 0; length += sprintf (s + length, "cp%d", c->board->num * NCHAN + c->num); if (d->chan->debug) length += sprintf (s + length, " debug=%d", d->chan->debug); if (c->board->mux) { length += sprintf (s + length, " cfg=C"); } else { length += sprintf (s + length, " cfg=A"); } if (c->baud) length += sprintf (s + length, " %ld", c->baud); else length += sprintf (s + length, " extclock"); if (c->type == T_E1 || c->type == T_G703) switch (c->gsyn) { case GSYN_INT : length += sprintf (s + length, " syn=int"); break; case GSYN_RCV : length += sprintf (s + length, " syn=rcv"); break; case GSYN_RCV0 : length += sprintf (s + length, " syn=rcv0"); break; case GSYN_RCV1 : length += sprintf (s + length, " syn=rcv1"); break; case GSYN_RCV2 : length += sprintf (s + length, " syn=rcv2"); break; case GSYN_RCV3 : length += sprintf (s + length, " syn=rcv3"); break; } if (c->type == T_SERIAL) { length += sprintf (s + length, " dpll=%s", c->dpll ? "on" : "off"); length += sprintf (s + length, " nrzi=%s", c->nrzi ? "on" : "off"); length += sprintf (s + length, " invclk=%s", c->invtxc ? "on" : "off"); } if (c->type == T_E1) length += sprintf (s + length, " higain=%s", c->higain ? "on" : "off"); length += sprintf (s + length, " loop=%s", c->lloop ? "on" : "off"); if (c->type == T_E1) length += sprintf (s + length, " ts=%s", format_timeslots (c->ts)); if (c->type == T_G703) { int lq, x; x = splimp (); lq = cp_get_lq (c); splx (x); length += sprintf (s + length, " (level=-%.1fdB)", lq / 10.0); } length += sprintf (s + length, "\n"); return length; } #if __FreeBSD_version >= 500000 static int ng_cp_rcvmsg (node_p node, item_p item, hook_p lasthook) { drv_t *d = NG_NODE_PRIVATE (node); struct ng_mesg *msg; #else static int ng_cp_rcvmsg (node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) { drv_t *d = node->private; #endif struct ng_mesg *resp = NULL; int error = 0; CP_DEBUG (d, ("Rcvmsg\n")); #if __FreeBSD_version >= 500000 NGI_GET_MSG (item, msg); #endif switch (msg->header.typecookie) { default: error = EINVAL; break; case NGM_CP_COOKIE: printf ("Not implemented yet\n"); error = EINVAL; break; case NGM_GENERIC_COOKIE: switch (msg->header.cmd) { default: error = EINVAL; break; case NGM_TEXT_STATUS: { char *s; int l = 0; int dl = sizeof (struct ng_mesg) + 730; #if __FreeBSD_version >= 500000 NG_MKRESPONSE (resp, msg, dl, M_NOWAIT); if (! resp) { error = ENOMEM; break; } #else MALLOC (resp, struct ng_mesg *, dl, M_NETGRAPH, M_NOWAIT); if (! resp) { error = ENOMEM; break; } bzero (resp, dl); #endif s = (resp)->data; if (d) { l += print_chan (s + l, d->chan); l += print_stats (s + l, d->chan, 1); l += print_modems (s + l, d->chan, 1); l += print_e1_stats (s + l, d->chan); } else l += sprintf (s + l, "Error: node not connect to channel"); #if __FreeBSD_version < 500000 (resp)->header.version = NG_VERSION; (resp)->header.arglen = strlen (s) + 1; (resp)->header.token = msg->header.token; (resp)->header.typecookie = NGM_CP_COOKIE; (resp)->header.cmd = msg->header.cmd; #endif strncpy ((resp)->header.cmdstr, "status", NG_CMDSTRLEN); } break; } break; } #if __FreeBSD_version >= 500000 NG_RESPOND_MSG (error, node, item, resp); NG_FREE_MSG (msg); #else *rptr = resp; FREE (msg, M_NETGRAPH); #endif return error; } #if __FreeBSD_version >= 500000 static int ng_cp_rcvdata (hook_p hook, item_p item) { drv_t *d = NG_NODE_PRIVATE (NG_HOOK_NODE(hook)); struct mbuf *m; - meta_p meta; + struct ng_tag_prio *ptag; #else static int ng_cp_rcvdata (hook_p hook, struct mbuf *m, meta_p meta) { drv_t *d = hook->node->private; #endif struct ifqueue *q; int s; CP_DEBUG2 (d, ("Rcvdata\n")); #if __FreeBSD_version >= 500000 NGI_GET_M (item, m); - NGI_GET_META (item, meta); NG_FREE_ITEM (item); if (! NG_HOOK_PRIVATE (hook) || ! d) { NG_FREE_M (m); - NG_FREE_META (meta); #else if (! hook->private || ! d) { NG_FREE_DATA (m,meta); #endif return ENETDOWN; } - q = (meta && meta->priority > 0) ? &d->hi_queue : &d->queue; + + /* Check for high priority data */ + if ((ptag = (struct ng_tag_prio *)m_tag_locate(m, NGM_GENERIC_COOKIE, + NG_TAG_PRIO, NULL)) != NULL && (ptag->priority > NG_PRIO_CUTOFF) ) + q = &d->hi_queue; + else + q = &d->queue; + s = splimp (); #if __FreeBSD_version >= 500000 IF_LOCK (q); if (_IF_QFULL (q)) { _IF_DROP (q); IF_UNLOCK (q); splx (s); NG_FREE_M (m); - NG_FREE_META (meta); return ENOBUFS; } _IF_ENQUEUE (q, m); IF_UNLOCK (q); #else if (IF_QFULL (q)) { IF_DROP (q); splx (s); NG_FREE_DATA (m, meta); return ENOBUFS; } IF_ENQUEUE (q, m); #endif cp_start (d); splx (s); return 0; } static int ng_cp_rmnode (node_p node) { #if __FreeBSD_version >= 500000 drv_t *d = NG_NODE_PRIVATE (node); CP_DEBUG (d, ("Rmnode\n")); if (d && d->running) { int s = splimp (); cp_down (d); splx (s); } #ifdef KLD_MODULE if (node->nd_flags & NG_REALLY_DIE) { NG_NODE_SET_PRIVATE (node, NULL); NG_NODE_UNREF (node); } node->nd_flags &= ~NG_INVALID; #endif #else /* __FreeBSD_version < 500000 */ drv_t *d = node->private; if (d && d->running) { int s = splimp (); cp_down (d); splx (s); } node->flags |= NG_INVALID; ng_cutlinks (node); #ifdef KLD_MODULE ng_unname (node); ng_unref (node); #endif #endif return 0; } static void ng_cp_watchdog (void *arg) { drv_t *d = arg; if (d) { if (d->timeout == 1) cp_watchdog (d); if (d->timeout) d->timeout--; d->timeout_handle = timeout (ng_cp_watchdog, d, hz); } } static int ng_cp_connect (hook_p hook) { #if __FreeBSD_version >= 500000 drv_t *d = NG_NODE_PRIVATE (NG_HOOK_NODE (hook)); #else drv_t *d = hook->node->private; #endif if (d) { CP_DEBUG (d, ("Connect\n")); d->timeout_handle = timeout (ng_cp_watchdog, d, hz); } return 0; } static int ng_cp_disconnect (hook_p hook) { #if __FreeBSD_version >= 500000 drv_t *d = NG_NODE_PRIVATE (NG_HOOK_NODE (hook)); #else drv_t *d = hook->node->private; #endif if (d) { CP_DEBUG (d, ("Disconnect\n")); #if __FreeBSD_version >= 500000 if (NG_HOOK_PRIVATE (hook)) #else if (hook->private) #endif { int s = splimp (); cp_down (d); splx (s); } untimeout (ng_cp_watchdog, d, d->timeout_handle); } return 0; } #endif static int cp_modevent (module_t mod, int type, void *unused) { struct cdev *dev; static int load_count = 0; struct cdevsw *cdsw; #if __FreeBSD_version >= 502103 dev = findcdev (makedev(CDEV_MAJOR, 0)); #else dev = makedev (CDEV_MAJOR, 0); #endif switch (type) { case MOD_LOAD: if (dev != NULL && (cdsw = devsw (dev)) && cdsw->d_maj == CDEV_MAJOR) { printf ("Tau-PCI driver is already in system\n"); return (ENXIO); } #if __FreeBSD_version >= 500000 && defined NETGRAPH if (ng_newtype (&typestruct)) printf ("Failed to register ng_cp\n"); #endif ++load_count; #if __FreeBSD_version <= 500000 cdevsw_add (&cp_cdevsw); #endif timeout_handle = timeout (cp_timeout, 0, hz*5); break; case MOD_UNLOAD: if (load_count == 1) { printf ("Removing device entry for Tau-PCI\n"); #if __FreeBSD_version <= 500000 cdevsw_remove (&cp_cdevsw); #endif #if __FreeBSD_version >= 500000 && defined NETGRAPH ng_rmtype (&typestruct); #endif } untimeout (cp_timeout, 0, timeout_handle); --load_count; break; case MOD_SHUTDOWN: break; } return 0; } #ifdef NETGRAPH static struct ng_type typestruct = { .version = NG_ABI_VERSION, .name = NG_CP_NODE_TYPE, .constructor = ng_cp_constructor, .rcvmsg = ng_cp_rcvmsg, .shutdown = ng_cp_rmnode, .newhook = ng_cp_newhook, .connect = ng_cp_connect, .rcvdata = ng_cp_rcvdata, .disconnect = ng_cp_disconnect, }; #endif /*NETGRAPH*/ #if __FreeBSD_version >= 500000 #ifdef NETGRAPH MODULE_DEPEND (ng_cp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); #else MODULE_DEPEND (cp, sppp, 1, 1, 1); #endif #ifdef KLD_MODULE DRIVER_MODULE (cpmod, pci, cp_driver, cp_devclass, cp_modevent, NULL); #else DRIVER_MODULE (cp, pci, cp_driver, cp_devclass, cp_modevent, NULL); #endif #elif __FreeBSD_version >= 400000 #ifdef NETGRAPH DRIVER_MODULE (cp, pci, cp_driver, cp_devclass, ng_mod_event, &typestruct); #else DRIVER_MODULE (cp, pci, cp_driver, cp_devclass, cp_modevent, NULL); #endif #endif /* __FreeBSD_version >= 400000 */ #endif /* NPCI */ Index: head/sys/dev/ctau/if_ct.c =================================================================== --- head/sys/dev/ctau/if_ct.c (revision 131107) +++ head/sys/dev/ctau/if_ct.c (revision 131108) @@ -1,2415 +1,2419 @@ /* * Cronyx-Tau adapter driver for FreeBSD. * Supports PPP/HDLC and Cisco/HDLC protocol in synchronous mode, * and asyncronous channels with full modem control. * Keepalive protocol implemented in both Cisco and PPP modes. * * Copyright (C) 1994-2002 Cronyx Engineering. * Author: Serge Vakulenko, * * Copyright (C) 1999-2004 Cronyx Engineering. * Author: Roman Kurakin, * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations a permission to use, * modify and redistribute this software in source and binary forms, * as long as this message is kept with the software, all derivative * works or modified versions. * * Cronyx Id: if_ct.c,v 1.1.2.31 2004/06/23 17:09:13 rik Exp $ */ #include __FBSDID("$FreeBSD$"); #include #if __FreeBSD_version >= 500000 # define NCTAU 1 #else # include "ctau.h" #endif #if NCTAU > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __FreeBSD_version < 500000 # include #endif #include #if __FreeBSD_version <= 501000 # include #endif #include #include #include #include "opt_ng_cronyx.h" #ifdef NETGRAPH_CRONYX # include "opt_netgraph.h" # include # include # include #else # include # if __FreeBSD_version < 500000 # include "sppp.h" # if NSPPP <= 0 # error The device ctau requires sppp or netgraph. # endif # endif # include # define PP_CISCO IFF_LINK2 # if __FreeBSD_version < 500000 # include # endif # include # define NBPFILTER NBPF #endif /* If we don't have Cronyx's sppp version, we don't have fr support via sppp */ #ifndef PP_FR #define PP_FR 0 #endif #define CT_DEBUG(d,s) ({if (d->chan->debug) {\ printf ("%s: ", d->name); printf s;}}) #define CT_DEBUG2(d,s) ({if (d->chan->debug>1) {\ printf ("%s: ", d->name); printf s;}}) #define CDEV_MAJOR 99 static void ct_identify __P((driver_t *, device_t)); static int ct_probe __P((device_t)); static int ct_attach __P((device_t)); static int ct_detach __P((device_t)); static device_method_t ct_isa_methods [] = { DEVMETHOD(device_identify, ct_identify), DEVMETHOD(device_probe, ct_probe), DEVMETHOD(device_attach, ct_attach), DEVMETHOD(device_detach, ct_detach), {0, 0} }; typedef struct _ct_dma_mem_t { unsigned long phys; void *virt; size_t size; #if __FreeBSD_version >= 500000 bus_dma_tag_t dmat; bus_dmamap_t mapp; #endif } ct_dma_mem_t; typedef struct _drv_t { char name [8]; ct_chan_t *chan; ct_board_t *board; ct_dma_mem_t dmamem; int running; #ifdef NETGRAPH char nodename [NG_NODELEN+1]; hook_p hook; hook_p debug_hook; node_p node; struct ifqueue queue; struct ifqueue hi_queue; short timeout; struct callout_handle timeout_handle; #else struct sppp pp; #endif struct cdev *devt; } drv_t; typedef struct _bdrv_t { ct_board_t *board; struct resource *base_res; struct resource *drq_res; struct resource *irq_res; int base_rid; int drq_rid; int irq_rid; void *intrhand; drv_t channel [NCHAN]; } bdrv_t; static driver_t ct_isa_driver = { "ct", ct_isa_methods, sizeof (bdrv_t), }; static devclass_t ct_devclass; static void ct_receive (ct_chan_t *c, char *data, int len); static void ct_transmit (ct_chan_t *c, void *attachment, int len); static void ct_error (ct_chan_t *c, int data); static void ct_up (drv_t *d); static void ct_start (drv_t *d); static void ct_down (drv_t *d); static void ct_watchdog (drv_t *d); #ifdef NETGRAPH extern struct ng_type typestruct; #else static void ct_ifstart (struct ifnet *ifp); static void ct_tlf (struct sppp *sp); static void ct_tls (struct sppp *sp); static void ct_ifwatchdog (struct ifnet *ifp); static int ct_sioctl (struct ifnet *ifp, u_long cmd, caddr_t data); static void ct_initialize (void *softc); #endif static ct_board_t *adapter [NCTAU]; static drv_t *channel [NCTAU*NCHAN]; static struct callout_handle led_timo [NCTAU]; static struct callout_handle timeout_handle; /* * Print the mbuf chain, for debug purposes only. */ static void printmbuf (struct mbuf *m) { printf ("mbuf:"); for (; m; m=m->m_next) { if (m->m_flags & M_PKTHDR) printf (" HDR %d:", m->m_pkthdr.len); if (m->m_flags & M_EXT) printf (" EXT:"); printf (" %d", m->m_len); } printf ("\n"); } /* * Make an mbuf from data. */ static struct mbuf *makembuf (void *buf, u_int len) { struct mbuf *m; MGETHDR (m, M_DONTWAIT, MT_DATA); if (! m) return 0; MCLGET (m, M_DONTWAIT); if (! (m->m_flags & M_EXT)) { m_freem (m); return 0; } m->m_pkthdr.len = m->m_len = len; bcopy (buf, mtod (m, caddr_t), len); return m; } static void ct_timeout (void *arg) { drv_t *d; int s, i; for (i=0; ichan->mode != M_G703) continue; s = splimp (); ct_g703_timer (d->chan); splx (s); } timeout_handle = timeout (ct_timeout, 0, hz); } static void ct_led_off (void *arg) { ct_board_t *b = arg; int s = splimp (); ct_led (b, 0); led_timo[b->num].callout = 0; splx (s); } /* * Activate interupt handler from DDK. */ static void ct_intr (void *arg) { bdrv_t *bd = arg; ct_board_t *b = bd->board; int s = splimp (); /* Turn LED on. */ ct_led (b, 1); ct_int_handler (b); /* Turn LED off 50 msec later. */ if (! led_timo[b->num].callout) led_timo[b->num] = timeout (ct_led_off, b, hz/20); splx (s); } static int probe_irq (ct_board_t *b, int irq) { int mask, busy, cnt; /* Clear pending irq, if any. */ ct_probe_irq (b, -irq); DELAY (100); for (cnt=0; cnt<5; ++cnt) { /* Get the mask of pending irqs, assuming they are busy. * Activate the adapter on given irq. */ busy = ct_probe_irq (b, irq); DELAY (1000); /* Get the mask of active irqs. * Deactivate our irq. */ mask = ct_probe_irq (b, -irq); DELAY (100); if ((mask & ~busy) == 1 << irq) { ct_probe_irq (b, 0); /* printf ("ct%d: irq %d ok, mask=0x%04x, busy=0x%04x\n", b->num, irq, mask, busy); */ return 1; } } /* printf ("ct%d: irq %d not functional, mask=0x%04x, busy=0x%04x\n", b->num, irq, mask, busy); */ ct_probe_irq (b, 0); return 0; } static short porttab [] = { 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0x3c0, 0x3e0, 0 }; static char dmatab [] = { 7, 6, 5, 0 }; static char irqtab [] = { 5, 10, 11, 7, 3, 15, 12, 0 }; static int ct_is_free_res (device_t dev, int rid, int type, u_long start, u_long end, u_long count) { struct resource *res; if (!(res = bus_alloc_resource (dev, type, &rid, start, end, count, RF_ALLOCATED))) return 0; bus_release_resource (dev, type, rid, res); return 1; } static void ct_identify (driver_t *driver, device_t dev) { u_long iobase, rescount; int devcount; device_t *devices; device_t child; devclass_t my_devclass; int i, k; if ((my_devclass = devclass_find ("ct")) == NULL) return; devclass_get_devices (my_devclass, &devices, &devcount); if (devcount == 0) { /* We should find all devices by our self. We could alter other * devices, but we don't have a choise */ for (i = 0; (iobase = porttab [i]) != 0; i++) { if (!ct_is_free_res (dev, 1, SYS_RES_IOPORT, iobase, iobase + NPORT, NPORT)) continue; if (ct_probe_board (iobase, -1, -1) == 0) continue; devcount++; child = BUS_ADD_CHILD (dev, ISA_ORDER_SPECULATIVE, "ct", -1); if (child == NULL) return; device_set_desc_copy (child, "Cronyx Tau-ISA"); device_set_driver (child, driver); bus_set_resource (child, SYS_RES_IOPORT, 0, iobase, NPORT); if (devcount >= NCTAU) break; } } else { static short porttab [] = { 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0x3c0, 0x3e0, 0 }; /* Lets check user choise. */ for (k = 0; k < devcount; k++) { if (bus_get_resource (devices[k], SYS_RES_IOPORT, 0, &iobase, &rescount) != 0) continue; for (i = 0; porttab [i] != 0; i++) { if (porttab [i] != iobase) continue; if (!ct_is_free_res (devices[k], 1, SYS_RES_IOPORT, iobase, iobase + NPORT, NPORT)) continue; if (ct_probe_board (iobase, -1, -1) == 0) continue; porttab [i] = -1; device_set_desc_copy (devices[k], "Cronyx Tau-ISA"); break; } if (porttab [i] == 0) { device_delete_child ( device_get_parent (devices[k]), devices [k]); devices[k] = 0; continue; } } for (k = 0; k < devcount; k++) { if (devices[k] == 0) continue; if (bus_get_resource (devices[k], SYS_RES_IOPORT, 0, &iobase, &rescount) == 0) continue; for (i = 0; (iobase = porttab [i]) != 0; i++) { if (porttab [i] == -1) continue; if (!ct_is_free_res (devices[k], 1, SYS_RES_IOPORT, iobase, iobase + NPORT, NPORT)) continue; if (ct_probe_board (iobase, -1, -1) == 0) continue; bus_set_resource (devices[k], SYS_RES_IOPORT, 0, iobase, NPORT); porttab [i] = -1; device_set_desc_copy (devices[k], "Cronyx Tau-ISA"); break; } if (porttab [i] == 0) { device_delete_child ( device_get_parent (devices[k]), devices [k]); } } free (devices, M_TEMP); } return; } static int ct_probe (device_t dev) { int unit = device_get_unit (dev); u_long iobase, rescount; if (!device_get_desc (dev) || strcmp (device_get_desc (dev), "Cronyx Tau-ISA")) return ENXIO; /* KASSERT ((bd != NULL), ("ct%d: NULL device softc\n", unit));*/ if (bus_get_resource (dev, SYS_RES_IOPORT, 0, &iobase, &rescount) != 0) { printf ("ct%d: Couldn't get IOPORT\n", unit); return ENXIO; } if (!ct_is_free_res (dev, 1, SYS_RES_IOPORT, iobase, iobase + NPORT, NPORT)) { printf ("ct%d: Resource IOPORT isn't free\n", unit); return ENXIO; } if (!ct_probe_board (iobase, -1, -1)) { printf ("ct%d: probing for Tau-ISA at %lx faild\n", unit, iobase); return ENXIO; } return 0; } extern struct cdevsw ct_cdevsw; #if __FreeBSD_version >= 500000 static void ct_bus_dmamap_addr (void *arg, bus_dma_segment_t *segs, int nseg, int error) { unsigned long *addr; if (error) return; KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); addr = arg; *addr = segs->ds_addr; } static int ct_bus_dma_mem_alloc (int bnum, int cnum, ct_dma_mem_t *dmem) { int error; error = bus_dma_tag_create (NULL, 16, 0, BUS_SPACE_MAXADDR_24BIT, BUS_SPACE_MAXADDR, NULL, NULL, dmem->size, 1, dmem->size, 0, NULL, NULL, &dmem->dmat); if (error) { if (cnum >= 0) printf ("ct%d-%d: ", bnum, cnum); else printf ("ct%d: ", bnum); printf ("couldn't allocate tag for dma memory\n"); return 0; } error = bus_dmamem_alloc (dmem->dmat, (void **)&dmem->virt, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &dmem->mapp); if (error) { if (cnum >= 0) printf ("ct%d-%d: ", bnum, cnum); else printf ("ct%d: ", bnum); printf ("couldn't allocate mem for dma memory\n"); bus_dma_tag_destroy (dmem->dmat); return 0; } error = bus_dmamap_load (dmem->dmat, dmem->mapp, dmem->virt, dmem->size, ct_bus_dmamap_addr, &dmem->phys, 0); if (error) { if (cnum >= 0) printf ("ct%d-%d: ", bnum, cnum); else printf ("ct%d: ", bnum); printf ("couldn't load mem map for dma memory\n"); bus_dmamem_free (dmem->dmat, dmem->virt, dmem->mapp); bus_dma_tag_destroy (dmem->dmat); return 0; } return 1; } static void ct_bus_dma_mem_free (ct_dma_mem_t *dmem) { bus_dmamap_unload (dmem->dmat, dmem->mapp); bus_dmamem_free (dmem->dmat, dmem->virt, dmem->mapp); bus_dma_tag_destroy (dmem->dmat); } #else static int ct_bus_dma_mem_alloc (int bnum, int cnum, ct_dma_mem_t *dmem) { dmem->virt = contigmalloc (dmem->size, M_DEVBUF, M_WAITOK, 0x100000, 0x1000000, 16, 0); if (dmem->virt == NULL) { if (cnum >= 0) printf ("ct%d-%d: ", bnum, cnum); else printf ("ct%d: ", bnum); printf ("couldn't allocate memory for dma memory\n", unit); return 0; } dmem->phys = vtophys (dmem->virt); return 1; } static void ct_bus_dma_mem_free (ct_dma_mem_t *dmem) { contigfree (dmem->virt, dmem->size, M_DEVBUF); } #endif /* * The adapter is present, initialize the driver structures. */ static int ct_attach (device_t dev) { bdrv_t *bd = device_get_softc (dev); u_long iobase, drq, irq, rescount; int unit = device_get_unit (dev); ct_board_t *b; ct_chan_t *c; drv_t *d; int i; int s; KASSERT ((bd != NULL), ("ct%d: NULL device softc\n", unit)); bus_get_resource (dev, SYS_RES_IOPORT, 0, &iobase, &rescount); bd->base_rid = 0; bd->base_res = bus_alloc_resource (dev, SYS_RES_IOPORT, &bd->base_rid, iobase, iobase + NPORT, NPORT, RF_ACTIVE); if (! bd->base_res) { printf ("ct%d: cannot alloc base address\n", unit); return ENXIO; } if (bus_get_resource (dev, SYS_RES_DRQ, 0, &drq, &rescount) != 0) { for (i = 0; (drq = dmatab [i]) != 0; i++) { if (!ct_is_free_res (dev, 1, SYS_RES_DRQ, drq, drq + 1, 1)) continue; bus_set_resource (dev, SYS_RES_DRQ, 0, drq, 1); break; } if (dmatab[i] == 0) { bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); printf ("ct%d: Couldn't get DRQ\n", unit); return ENXIO; } } bd->drq_rid = 0; bd->drq_res = bus_alloc_resource (dev, SYS_RES_DRQ, &bd->drq_rid, drq, drq + 1, 1, RF_ACTIVE); if (! bd->drq_res) { printf ("ct%d: cannot allocate drq\n", unit); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); return ENXIO; } if (bus_get_resource (dev, SYS_RES_IRQ, 0, &irq, &rescount) != 0) { for (i = 0; (irq = irqtab [i]) != 0; i++) { if (!ct_is_free_res (dev, 1, SYS_RES_IRQ, irq, irq + 1, 1)) continue; bus_set_resource (dev, SYS_RES_IRQ, 0, irq, 1); break; } if (irqtab[i] == 0) { bus_release_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); printf ("ct%d: Couldn't get IRQ\n", unit); return ENXIO; } } bd->irq_rid = 0; bd->irq_res = bus_alloc_resource (dev, SYS_RES_IRQ, &bd->irq_rid, irq, irq + 1, 1, RF_ACTIVE); if (! bd->irq_res) { printf ("ct%d: Couldn't allocate irq\n", unit); bus_release_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); return ENXIO; } b = malloc (sizeof (ct_board_t), M_DEVBUF, M_WAITOK); if (!b) { printf ("ct:%d: Couldn't allocate memory\n", unit); return (ENXIO); } adapter[unit] = b; bzero (b, sizeof(ct_board_t)); if (! ct_open_board (b, unit, iobase, irq, drq)) { printf ("ct%d: error loading firmware\n", unit); free (b, M_DEVBUF); bus_release_resource (dev, SYS_RES_IRQ, bd->irq_rid, bd->irq_res); bus_release_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); return ENXIO; } bd->board = b; if (! probe_irq (b, irq)) { printf ("ct%d: irq %ld not functional\n", unit, irq); bd->board = 0; adapter [unit] = 0; free (b, M_DEVBUF); bus_release_resource (dev, SYS_RES_IRQ, bd->irq_rid, bd->irq_res); bus_release_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); return ENXIO; } s = splimp (); if (bus_setup_intr (dev, bd->irq_res, INTR_TYPE_NET, ct_intr, bd, &bd->intrhand)) { printf ("ct%d: Can't setup irq %ld\n", unit, irq); bd->board = 0; adapter [unit] = 0; free (b, M_DEVBUF); bus_release_resource (dev, SYS_RES_IRQ, bd->irq_rid, bd->irq_res); bus_release_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); splx (s); return ENXIO; } ct_init_board (b, b->num, b->port, irq, drq, b->type, b->osc); ct_setup_board (b, 0, 0, 0); printf ("ct%d: , clock %s MHz\n", b->num, b->name, b->osc == 20000000 ? "20" : "16.384"); for (c=b->chan; cchan+NCHAN; ++c) { d = &bd->channel[c->num]; d->dmamem.size = sizeof(ct_buf_t); if (! ct_bus_dma_mem_alloc (unit, c->num, &d->dmamem)) continue; channel [b->num*NCHAN + c->num] = d; sprintf (d->name, "ct%d.%d", b->num, c->num); d->board = b; d->chan = c; c->sys = d; #ifdef NETGRAPH if (ng_make_node_common (&typestruct, &d->node) != 0) { printf ("%s: cannot make common node\n", d->name); channel [b->num*NCHAN + c->num] = 0; c->sys = 0; ct_bus_dma_mem_free (&d->dmamem); continue; } #if __FreeBSD_version >= 500000 NG_NODE_SET_PRIVATE (d->node, d); #else d->node->private = d; #endif sprintf (d->nodename, "%s%d", NG_CT_NODE_TYPE, c->board->num*NCHAN + c->num); if (ng_name_node (d->node, d->nodename)) { printf ("%s: cannot name node\n", d->nodename); #if __FreeBSD_version >= 500000 NG_NODE_UNREF (d->node); #else ng_rmnode (d->node); ng_unref (d->node); #endif channel [b->num*NCHAN + c->num] = 0; c->sys = 0; ct_bus_dma_mem_free (&d->dmamem); continue; } d->queue.ifq_maxlen = IFQ_MAXLEN; d->hi_queue.ifq_maxlen = IFQ_MAXLEN; #if __FreeBSD_version >= 500000 mtx_init (&d->queue.ifq_mtx, "ct_queue", NULL, MTX_DEF); mtx_init (&d->hi_queue.ifq_mtx, "ct_queue_hi", NULL, MTX_DEF); #endif #else /*NETGRAPH*/ d->pp.pp_if.if_softc = d; #if __FreeBSD_version > 501000 if_initname (&d->pp.pp_if, "ct", b->num * NCHAN + c->num); #else d->pp.pp_if.if_unit = b->num * NCHAN + c->num; d->pp.pp_if.if_name = "ct"; #endif d->pp.pp_if.if_mtu = PP_MTU; d->pp.pp_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST; d->pp.pp_if.if_ioctl = ct_sioctl; d->pp.pp_if.if_start = ct_ifstart; d->pp.pp_if.if_watchdog = ct_ifwatchdog; d->pp.pp_if.if_init = ct_initialize; sppp_attach (&d->pp.pp_if); if_attach (&d->pp.pp_if); d->pp.pp_tlf = ct_tlf; d->pp.pp_tls = ct_tls; /* If BPF is in the kernel, call the attach for it. * Header size is 4 bytes. */ bpfattach (&d->pp.pp_if, DLT_PPP, 4); #endif /*NETGRAPH*/ ct_start_chan (c, d->dmamem.virt, d->dmamem.phys); ct_register_receive (c, &ct_receive); ct_register_transmit (c, &ct_transmit); ct_register_error (c, &ct_error); d->devt = make_dev (&ct_cdevsw, b->num*NCHAN+c->num, UID_ROOT, GID_WHEEL, 0600, "ct%d", b->num*NCHAN+c->num); } splx (s); return 0; } static int ct_detach (device_t dev) { bdrv_t *bd = device_get_softc (dev); ct_board_t *b = bd->board; ct_chan_t *c; int s = splimp (); /* Check if the device is busy (open). */ for (c = b->chan; c < b->chan + NCHAN; ++c) { drv_t *d = (drv_t*) c->sys; if (!d || !d->chan->type) continue; if (d->running) { splx (s); return EBUSY; } } /* Deactivate the timeout routine. */ if (led_timo[b->num].callout) untimeout (ct_led_off, b, led_timo[b->num]); bus_teardown_intr (dev, bd->irq_res, bd->intrhand); bus_deactivate_resource (dev, SYS_RES_IRQ, bd->irq_rid, bd->irq_res); bus_release_resource (dev, SYS_RES_IRQ, bd->irq_rid, bd->irq_res); bus_deactivate_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_release_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_deactivate_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->irq_res); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); ct_close_board (b); /* Detach the interfaces, free buffer memory. */ for (c = b->chan; c < b->chan + NCHAN; ++c) { drv_t *d = (drv_t*) c->sys; if (!d || !d->chan->type) continue; #ifdef NETGRAPH #if __FreeBSD_version >= 500000 if (d->node) { ng_rmnode_self (d->node); NG_NODE_UNREF (d->node); d->node = NULL; } mtx_destroy (&d->queue.ifq_mtx); mtx_destroy (&d->hi_queue.ifq_mtx); #else ng_rmnode (d->node); d->node = 0; #endif #else #if __FreeBSD_version >= 410000 && NBPFILTER > 0 /* Detach from the packet filter list of interfaces. */ bpfdetach (&d->pp.pp_if); #endif /* Detach from the sync PPP list. */ sppp_detach (&d->pp.pp_if); if_detach (&d->pp.pp_if); #endif destroy_dev (d->devt); } ct_led_off (b); if (led_timo[b->num].callout) untimeout (ct_led_off, b, led_timo[b->num]); splx (s); s = splimp (); for (c = b->chan; c < b->chan + NCHAN; ++c) { drv_t *d = (drv_t*) c->sys; if (!d || !d->chan->type) continue; /* Deallocate buffers. */ ct_bus_dma_mem_free (&d->dmamem); } bd->board = 0; adapter [b->num] = 0; free (b, M_DEVBUF); splx (s); return 0; } #ifndef NETGRAPH static void ct_ifstart (struct ifnet *ifp) { drv_t *d = ifp->if_softc; ct_start (d); } static void ct_ifwatchdog (struct ifnet *ifp) { drv_t *d = ifp->if_softc; ct_watchdog (d); } static void ct_tlf (struct sppp *sp) { drv_t *d = sp->pp_if.if_softc; CT_DEBUG (d, ("ct_tlf\n")); /* ct_set_dtr (d->chan, 0);*/ /* ct_set_rts (d->chan, 0);*/ sp->pp_down (sp); } static void ct_tls (struct sppp *sp) { drv_t *d = sp->pp_if.if_softc; CT_DEBUG (d, ("ct_tls\n")); sp->pp_up (sp); } /* * Initialization of interface. * Ii seems to be never called by upper level. */ static void ct_initialize (void *softc) { drv_t *d = softc; CT_DEBUG (d, ("ct_initialize\n")); } /* * Process an ioctl request. */ static int ct_sioctl (struct ifnet *ifp, u_long cmd, caddr_t data) { drv_t *d = ifp->if_softc; int error, s, was_up, should_be_up; was_up = (ifp->if_flags & IFF_RUNNING) != 0; error = sppp_ioctl (ifp, cmd, data); if (error) return error; if (! (ifp->if_flags & IFF_DEBUG)) d->chan->debug = 0; else if (! d->chan->debug) d->chan->debug = 1; switch (cmd) { default: CT_DEBUG2 (d, ("ioctl 0x%lx\n", cmd)); return 0; case SIOCADDMULTI: CT_DEBUG2 (d, ("SIOCADDMULTI\n")); return 0; case SIOCDELMULTI: CT_DEBUG2 (d, ("SIOCDELMULTI\n")); return 0; case SIOCSIFFLAGS: CT_DEBUG2 (d, ("SIOCSIFFLAGS\n")); break; case SIOCSIFADDR: CT_DEBUG2 (d, ("SIOCSIFADDR\n")); break; } /* We get here only in case of SIFFLAGS or SIFADDR. */ s = splimp (); should_be_up = (ifp->if_flags & IFF_RUNNING) != 0; if (! was_up && should_be_up) { /* Interface goes up -- start it. */ ct_up (d); ct_start (d); } else if (was_up && ! should_be_up) { /* Interface is going down -- stop it. */ /* if ((d->pp.pp_flags & PP_FR) || (ifp->if_flags & PP_CISCO))*/ ct_down (d); } splx (s); return 0; } #endif /*NETGRAPH*/ /* * Stop the interface. Called on splimp(). */ static void ct_down (drv_t *d) { int s = splimp (); CT_DEBUG (d, ("ct_down\n")); ct_set_dtr (d->chan, 0); ct_set_rts (d->chan, 0); d->running = 0; splx (s); } /* * Start the interface. Called on splimp(). */ static void ct_up (drv_t *d) { int s = splimp (); CT_DEBUG (d, ("ct_up\n")); ct_set_dtr (d->chan, 1); ct_set_rts (d->chan, 1); d->running = 1; splx (s); } /* * Start output on the (slave) interface. Get another datagram to send * off of the interface queue, and copy it to the interface * before starting the output. */ static void ct_send (drv_t *d) { struct mbuf *m; u_short len; CT_DEBUG2 (d, ("ct_send, tn=%d\n", d->chan->tn)); /* No output if the interface is down. */ if (! d->running) return; /* No output if the modem is off. */ if (! ct_get_dsr (d->chan) && !ct_get_loop (d->chan)) return; while (ct_buf_free (d->chan)) { /* Get the packet to send. */ #ifdef NETGRAPH IF_DEQUEUE (&d->hi_queue, m); if (! m) IF_DEQUEUE (&d->queue, m); #else m = sppp_dequeue (&d->pp.pp_if); #endif if (! m) return; #ifndef NETGRAPH if (d->pp.pp_if.if_bpf) #if __FreeBSD_version >= 500000 BPF_MTAP (&d->pp.pp_if, m); #else bpf_mtap (&d->pp.pp_if, m); #endif #endif len = m->m_pkthdr.len; if (! m->m_next) ct_send_packet (d->chan, (u_char*)mtod (m, caddr_t), len, 0); else { m_copydata (m, 0, len, d->chan->tbuf[d->chan->te]); ct_send_packet (d->chan, d->chan->tbuf[d->chan->te], len, 0); } m_freem (m); /* Set up transmit timeout, if the transmit ring is not empty. * Transmit timeout is 10 seconds. */ #ifdef NETGRAPH d->timeout = 10; #else d->pp.pp_if.if_timer = 10; #endif } #ifndef NETGRAPH d->pp.pp_if.if_flags |= IFF_OACTIVE; #endif } /* * Start output on the interface. * Always called on splimp(). */ static void ct_start (drv_t *d) { int s = splimp (); if (d->running) { if (! d->chan->dtr) ct_set_dtr (d->chan, 1); if (! d->chan->rts) ct_set_rts (d->chan, 1); ct_send (d); } splx (s); } /* * Handle transmit timeouts. * Recover after lost transmit interrupts. * Always called on splimp(). */ static void ct_watchdog (drv_t *d) { int s = splimp (); CT_DEBUG (d, ("device timeout\n")); if (d->running) { ct_setup_chan (d->chan); ct_start_chan (d->chan, 0, 0); ct_set_dtr (d->chan, 1); ct_set_rts (d->chan, 1); ct_start (d); } splx (s); } /* * Transmit callback function. */ static void ct_transmit (ct_chan_t *c, void *attachment, int len) { drv_t *d = c->sys; if (!d) return; #ifdef NETGRAPH d->timeout = 0; #else ++d->pp.pp_if.if_opackets; d->pp.pp_if.if_flags &= ~IFF_OACTIVE; d->pp.pp_if.if_timer = 0; #endif ct_start (d); } /* * Process the received packet. */ static void ct_receive (ct_chan_t *c, char *data, int len) { drv_t *d = c->sys; struct mbuf *m; #if __FreeBSD_version >= 500000 && defined NETGRAPH int error; #endif if (!d || !d->running) return; m = makembuf (data, len); if (! m) { CT_DEBUG (d, ("no memory for packet\n")); #ifndef NETGRAPH ++d->pp.pp_if.if_iqdrops; #endif return; } if (c->debug > 1) printmbuf (m); #ifdef NETGRAPH m->m_pkthdr.rcvif = 0; #if __FreeBSD_version >= 500000 NG_SEND_DATA_ONLY (error, d->hook, m); #else ng_queue_data (d->hook, m, 0); #endif #else ++d->pp.pp_if.if_ipackets; m->m_pkthdr.rcvif = &d->pp.pp_if; /* Check if there's a BPF listener on this interface. * If so, hand off the raw packet to bpf. */ if (d->pp.pp_if.if_bpf) #if __FreeBSD_version >= 500000 BPF_TAP (&d->pp.pp_if, data, len); #else bpf_tap (&d->pp.pp_if, data, len); #endif sppp_input (&d->pp.pp_if, m); #endif } /* * Error callback function. */ static void ct_error (ct_chan_t *c, int data) { drv_t *d = c->sys; if (!d) return; switch (data) { case CT_FRAME: CT_DEBUG (d, ("frame error\n")); #ifndef NETGRAPH ++d->pp.pp_if.if_ierrors; #endif break; case CT_CRC: CT_DEBUG (d, ("crc error\n")); #ifndef NETGRAPH ++d->pp.pp_if.if_ierrors; #endif break; case CT_OVERRUN: CT_DEBUG (d, ("overrun error\n")); #ifndef NETGRAPH ++d->pp.pp_if.if_collisions; ++d->pp.pp_if.if_ierrors; #endif break; case CT_OVERFLOW: CT_DEBUG (d, ("overflow error\n")); #ifndef NETGRAPH ++d->pp.pp_if.if_ierrors; #endif break; case CT_UNDERRUN: CT_DEBUG (d, ("underrun error\n")); #ifdef NETGRAPH d->timeout = 0; #else ++d->pp.pp_if.if_oerrors; d->pp.pp_if.if_flags &= ~IFF_OACTIVE; d->pp.pp_if.if_timer = 0; #endif ct_start (d); break; default: CT_DEBUG (d, ("error #%d\n", data)); } } #if __FreeBSD_version < 500000 static int ct_open (dev_t dev, int oflags, int devtype, struct proc *p) #else static int ct_open (struct cdev *dev, int oflags, int devtype, struct thread *td) #endif { drv_t *d; if (minor(dev) >= NCTAU*NCHAN || ! (d = channel[minor(dev)])) return ENXIO; CT_DEBUG2 (d, ("ct_open\n")); return 0; } #if __FreeBSD_version < 500000 static int ct_close (dev_t dev, int fflag, int devtype, struct proc *p) #else static int ct_close (struct cdev *dev, int fflag, int devtype, struct thread *td) #endif { drv_t *d = channel [minor(dev)]; if (!d) return 0; CT_DEBUG2 (d, ("ct_close\n")); return 0; } static int ct_modem_status (ct_chan_t *c) { drv_t *d = c->sys; int status, s; if (!d) return 0; status = d->running ? TIOCM_LE : 0; s = splimp (); if (ct_get_cd (c)) status |= TIOCM_CD; if (ct_get_cts (c)) status |= TIOCM_CTS; if (ct_get_dsr (c)) status |= TIOCM_DSR; if (c->dtr) status |= TIOCM_DTR; if (c->rts) status |= TIOCM_RTS; splx (s); return status; } /* * Process an ioctl request on /dev/cronyx/ctauN. */ #if __FreeBSD_version < 500000 static int ct_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) #else static int ct_ioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) #endif { drv_t *d = channel [minor (dev)]; ct_chan_t *c; struct serial_statistics *st; struct e1_statistics *opte1; int error, s; char mask[16]; if (!d || !d->chan) return 0; c = d->chan; switch (cmd) { case SERIAL_GETREGISTERED: bzero (mask, sizeof(mask)); for (s=0; spp.pp_flags & PP_FR) ? "fr" : (d->pp.pp_if.if_flags & PP_CISCO) ? "cisco" : "ppp"); return 0; case SERIAL_SETPROTO: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if (d->pp.pp_if.if_flags & IFF_RUNNING) return EBUSY; if (! strcmp ("cisco", (char*)data)) { d->pp.pp_flags &= ~(PP_FR); d->pp.pp_flags |= PP_KEEPALIVE; d->pp.pp_if.if_flags |= PP_CISCO; } else if (! strcmp ("fr", (char*)data)) { d->pp.pp_if.if_flags &= ~(PP_CISCO); d->pp.pp_flags |= PP_FR | PP_KEEPALIVE; } else if (! strcmp ("ppp", (char*)data)) { d->pp.pp_flags &= ~(PP_FR | PP_KEEPALIVE); d->pp.pp_if.if_flags &= ~(PP_CISCO); } else return EINVAL; return 0; case SERIAL_GETKEEPALIVE: if ((d->pp.pp_flags & PP_FR) || (d->pp.pp_if.if_flags & PP_CISCO)) return EINVAL; *(int*)data = (d->pp.pp_flags & PP_KEEPALIVE) ? 1 : 0; return 0; case SERIAL_SETKEEPALIVE: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if ((d->pp.pp_flags & PP_FR) || (d->pp.pp_if.if_flags & PP_CISCO)) return EINVAL; if (*(int*)data) d->pp.pp_flags |= PP_KEEPALIVE; else d->pp.pp_flags &= ~PP_KEEPALIVE; return 0; #endif /*NETGRAPH*/ case SERIAL_GETMODE: *(int*)data = SERIAL_HDLC; return 0; case SERIAL_GETCFG: if (c->mode == M_HDLC) return EINVAL; switch (ct_get_config (c->board)) { default: *(char*)data = 'a'; break; case CFG_B: *(char*)data = 'b'; break; case CFG_C: *(char*)data = 'c'; break; } return 0; case SERIAL_SETCFG: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if (c->mode == M_HDLC) return EINVAL; s = splimp (); switch (*(char*)data) { case 'a': ct_set_config (c->board, CFG_A); break; case 'b': ct_set_config (c->board, CFG_B); break; case 'c': ct_set_config (c->board, CFG_C); break; } splx (s); return 0; case SERIAL_GETSTAT: st = (struct serial_statistics*) data; st->rintr = c->rintr; st->tintr = c->tintr; st->mintr = c->mintr; st->ibytes = c->ibytes; st->ipkts = c->ipkts; st->ierrs = c->ierrs; st->obytes = c->obytes; st->opkts = c->opkts; st->oerrs = c->oerrs; return 0; case SERIAL_GETESTAT: opte1 = (struct e1_statistics*)data; opte1->status = c->status; opte1->cursec = c->cursec; opte1->totsec = c->totsec + c->cursec; opte1->currnt.bpv = c->currnt.bpv; opte1->currnt.fse = c->currnt.fse; opte1->currnt.crce = c->currnt.crce; opte1->currnt.rcrce = c->currnt.rcrce; opte1->currnt.uas = c->currnt.uas; opte1->currnt.les = c->currnt.les; opte1->currnt.es = c->currnt.es; opte1->currnt.bes = c->currnt.bes; opte1->currnt.ses = c->currnt.ses; opte1->currnt.oofs = c->currnt.oofs; opte1->currnt.css = c->currnt.css; opte1->currnt.dm = c->currnt.dm; opte1->total.bpv = c->total.bpv + c->currnt.bpv; opte1->total.fse = c->total.fse + c->currnt.fse; opte1->total.crce = c->total.crce + c->currnt.crce; opte1->total.rcrce = c->total.rcrce + c->currnt.rcrce; opte1->total.uas = c->total.uas + c->currnt.uas; opte1->total.les = c->total.les + c->currnt.les; opte1->total.es = c->total.es + c->currnt.es; opte1->total.bes = c->total.bes + c->currnt.bes; opte1->total.ses = c->total.ses + c->currnt.ses; opte1->total.oofs = c->total.oofs + c->currnt.oofs; opte1->total.css = c->total.css + c->currnt.css; opte1->total.dm = c->total.dm + c->currnt.dm; for (s=0; s<48; ++s) { opte1->interval[s].bpv = c->interval[s].bpv; opte1->interval[s].fse = c->interval[s].fse; opte1->interval[s].crce = c->interval[s].crce; opte1->interval[s].rcrce = c->interval[s].rcrce; opte1->interval[s].uas = c->interval[s].uas; opte1->interval[s].les = c->interval[s].les; opte1->interval[s].es = c->interval[s].es; opte1->interval[s].bes = c->interval[s].bes; opte1->interval[s].ses = c->interval[s].ses; opte1->interval[s].oofs = c->interval[s].oofs; opte1->interval[s].css = c->interval[s].css; opte1->interval[s].dm = c->interval[s].dm; } return 0; case SERIAL_CLRSTAT: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; c->rintr = 0; c->tintr = 0; c->mintr = 0; c->ibytes = 0; c->ipkts = 0; c->ierrs = 0; c->obytes = 0; c->opkts = 0; c->oerrs = 0; bzero (&c->currnt, sizeof (c->currnt)); bzero (&c->total, sizeof (c->total)); bzero (c->interval, sizeof (c->interval)); return 0; case SERIAL_GETBAUD: *(long*)data = ct_get_baud(c); return 0; case SERIAL_SETBAUD: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; s = splimp (); ct_set_baud (c, *(long*)data); splx (s); return 0; case SERIAL_GETLOOP: *(int*)data = ct_get_loop (c); return 0; case SERIAL_SETLOOP: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; s = splimp (); ct_set_loop (c, *(int*)data); splx (s); return 0; case SERIAL_GETDPLL: if (c->mode == M_E1 || c->mode == M_G703) return EINVAL; *(int*)data = ct_get_dpll (c); return 0; case SERIAL_SETDPLL: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if (c->mode == M_E1 || c->mode == M_G703) return EINVAL; s = splimp (); ct_set_dpll (c, *(int*)data); splx (s); return 0; case SERIAL_GETNRZI: if (c->mode == M_E1 || c->mode == M_G703) return EINVAL; *(int*)data = ct_get_nrzi (c); return 0; case SERIAL_SETNRZI: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if (c->mode == M_E1 || c->mode == M_G703) return EINVAL; s = splimp (); ct_set_nrzi (c, *(int*)data); splx (s); return 0; case SERIAL_GETDEBUG: *(int*)data = c->debug; return 0; case SERIAL_SETDEBUG: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; c->debug = *(int*)data; #ifndef NETGRAPH if (d->chan->debug) d->pp.pp_if.if_flags |= IFF_DEBUG; else d->pp.pp_if.if_flags &= (~IFF_DEBUG); #endif return 0; case SERIAL_GETHIGAIN: if (c->mode != M_E1) return EINVAL; *(int*)data = ct_get_higain (c); return 0; case SERIAL_SETHIGAIN: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; s = splimp (); ct_set_higain (c, *(int*)data); splx (s); return 0; case SERIAL_GETPHONY: CT_DEBUG2 (d, ("ioctl: getphony\n")); if (c->mode != M_E1) return EINVAL; *(int*)data = c->gopt.phony; return 0; case SERIAL_SETPHONY: CT_DEBUG2 (d, ("ioctl: setphony\n")); if (c->mode != M_E1) return EINVAL; /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; s = splimp (); ct_set_phony (c, *(int*)data); splx (s); return 0; case SERIAL_GETCLK: if (c->mode != M_E1 && c->mode != M_G703) return EINVAL; switch (ct_get_clk(c)) { default: *(int*)data = E1CLK_INTERNAL; break; case GCLK_RCV: *(int*)data = E1CLK_RECEIVE; break; case GCLK_RCLKO: *(int*)data = c->num ? E1CLK_RECEIVE_CHAN0 : E1CLK_RECEIVE_CHAN1; break; } return 0; case SERIAL_SETCLK: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; s = splimp (); switch (*(int*)data) { default: ct_set_clk (c, GCLK_INT); break; case E1CLK_RECEIVE: ct_set_clk (c, GCLK_RCV); break; case E1CLK_RECEIVE_CHAN0: case E1CLK_RECEIVE_CHAN1: ct_set_clk (c, GCLK_RCLKO); break; } splx (s); return 0; case SERIAL_GETTIMESLOTS: if (c->mode != M_E1) return EINVAL; *(long*)data = ct_get_ts (c); return 0; case SERIAL_SETTIMESLOTS: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; s = splimp (); ct_set_ts (c, *(long*)data); splx (s); return 0; case SERIAL_GETSUBCHAN: if (c->mode != M_E1) return EINVAL; *(long*)data = ct_get_subchan (c->board); return 0; case SERIAL_SETSUBCHAN: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; s = splimp (); ct_set_subchan (c->board, *(long*)data); splx (s); return 0; case SERIAL_GETINVCLK: case SERIAL_GETINVTCLK: if (c->mode == M_E1 || c->mode == M_G703) return EINVAL; *(int*)data = ct_get_invtxc (c); return 0; case SERIAL_GETINVRCLK: if (c->mode == M_E1 || c->mode == M_G703) return EINVAL; *(int*)data = ct_get_invrxc (c); return 0; case SERIAL_SETINVCLK: case SERIAL_SETINVTCLK: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if (c->mode == M_E1 || c->mode == M_G703) return EINVAL; s = splimp (); ct_set_invtxc (c, *(int*)data); splx (s); return 0; case SERIAL_SETINVRCLK: /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if (c->mode == M_E1 || c->mode == M_G703) return EINVAL; s = splimp (); ct_set_invrxc (c, *(int*)data); splx (s); return 0; case SERIAL_GETLEVEL: if (c->mode != M_G703) return EINVAL; s = splimp (); *(int*)data = ct_get_lq (c); splx (s); return 0; case TIOCSDTR: /* Set DTR */ s = splimp (); ct_set_dtr (c, 1); splx (s); return 0; case TIOCCDTR: /* Clear DTR */ s = splimp (); ct_set_dtr (c, 0); splx (s); return 0; case TIOCMSET: /* Set DTR/RTS */ s = splimp (); ct_set_dtr (c, (*(int*)data & TIOCM_DTR) ? 1 : 0); ct_set_rts (c, (*(int*)data & TIOCM_RTS) ? 1 : 0); splx (s); return 0; case TIOCMBIS: /* Add DTR/RTS */ s = splimp (); if (*(int*)data & TIOCM_DTR) ct_set_dtr (c, 1); if (*(int*)data & TIOCM_RTS) ct_set_rts (c, 1); splx (s); return 0; case TIOCMBIC: /* Clear DTR/RTS */ s = splimp (); if (*(int*)data & TIOCM_DTR) ct_set_dtr (c, 0); if (*(int*)data & TIOCM_RTS) ct_set_rts (c, 0); splx (s); return 0; case TIOCMGET: /* Get modem status */ *(int*)data = ct_modem_status (c); return 0; } return ENOTTY; } #if __FreeBSD_version < 500000 static struct cdevsw ct_cdevsw = { ct_open, ct_close, noread, nowrite, ct_ioctl, nopoll, nommap, nostrategy, "ct", CDEV_MAJOR, nodump, nopsize, D_NAGGED, -1 }; #elif __FreeBSD_version == 500000 static struct cdevsw ct_cdevsw = { ct_open, ct_close, noread, nowrite, ct_ioctl, nopoll, nommap, nostrategy, "ct", CDEV_MAJOR, nodump, nopsize, D_NAGGED, }; #elif __FreeBSD_version <= 501000 static struct cdevsw ct_cdevsw = { .d_open = ct_open, .d_close = ct_close, .d_read = noread, .d_write = nowrite, .d_ioctl = ct_ioctl, .d_poll = nopoll, .d_mmap = nommap, .d_strategy = nostrategy, .d_name = "ct", .d_maj = CDEV_MAJOR, .d_dump = nodump, .d_flags = D_NAGGED, }; #elif __FreeBSD_version < 502103 static struct cdevsw ct_cdevsw = { .d_open = ct_open, .d_close = ct_close, .d_ioctl = ct_ioctl, .d_name = "ct", .d_maj = CDEV_MAJOR, .d_flags = D_NAGGED, }; #else /* __FreeBSD_version >= 502103 */ static struct cdevsw ct_cdevsw = { .d_version = D_VERSION, .d_open = ct_open, .d_close = ct_close, .d_ioctl = ct_ioctl, .d_name = "ct", .d_maj = CDEV_MAJOR, .d_flags = D_NEEDGIANT, }; #endif /* __FreeBSD_version > 501000 */ #ifdef NETGRAPH #if __FreeBSD_version >= 500000 static int ng_ct_constructor (node_p node) { drv_t *d = NG_NODE_PRIVATE (node); #else static int ng_ct_constructor (node_p *node) { drv_t *d = (*node)->private; #endif CT_DEBUG (d, ("Constructor\n")); return EINVAL; } static int ng_ct_newhook (node_p node, hook_p hook, const char *name) { int s; #if __FreeBSD_version >= 500000 drv_t *d = NG_NODE_PRIVATE (node); #else drv_t *d = node->private; #endif if (!d) return EINVAL; /* Attach debug hook */ if (strcmp (name, NG_CT_HOOK_DEBUG) == 0) { #if __FreeBSD_version >= 500000 NG_HOOK_SET_PRIVATE (hook, NULL); #else hook->private = 0; #endif d->debug_hook = hook; return 0; } /* Check for raw hook */ if (strcmp (name, NG_CT_HOOK_RAW) != 0) return EINVAL; #if __FreeBSD_version >= 500000 NG_HOOK_SET_PRIVATE (hook, d); #else hook->private = d; #endif d->hook = hook; s = splimp (); ct_up (d); splx (s); return 0; } static char *format_timeslots (u_long s) { static char buf [100]; char *p = buf; int i; for (i=1; i<32; ++i) if ((s >> i) & 1) { int prev = (i > 1) & (s >> (i-1)); int next = (i < 31) & (s >> (i+1)); if (prev) { if (next) continue; *p++ = '-'; } else if (p > buf) *p++ = ','; if (i >= 10) *p++ = '0' + i / 10; *p++ = '0' + i % 10; } *p = 0; return buf; } static int print_modems (char *s, ct_chan_t *c, int need_header) { int status = ct_modem_status (c); int length = 0; if (need_header) length += sprintf (s + length, " LE DTR DSR RTS CTS CD\n"); length += sprintf (s + length, "%4s %4s %4s %4s %4s %4s\n", status & TIOCM_LE ? "On" : "-", status & TIOCM_DTR ? "On" : "-", status & TIOCM_DSR ? "On" : "-", status & TIOCM_RTS ? "On" : "-", status & TIOCM_CTS ? "On" : "-", status & TIOCM_CD ? "On" : "-"); return length; } static int print_stats (char *s, ct_chan_t *c, int need_header) { struct serial_statistics st; int length = 0; st.rintr = c->rintr; st.tintr = c->tintr; st.mintr = c->mintr; st.ibytes = c->ibytes; st.ipkts = c->ipkts; st.ierrs = c->ierrs; st.obytes = c->obytes; st.opkts = c->opkts; st.oerrs = c->oerrs; if (need_header) length += sprintf (s + length, " Rintr Tintr Mintr Ibytes Ipkts Ierrs Obytes Opkts Oerrs\n"); length += sprintf (s + length, "%7ld %7ld %7ld %8ld %7ld %7ld %8ld %7ld %7ld\n", st.rintr, st.tintr, st.mintr, st.ibytes, st.ipkts, st.ierrs, st.obytes, st.opkts, st.oerrs); return length; } static char *format_e1_status (u_char status) { static char buf [80]; if (status & E1_NOALARM) return "Ok"; buf[0] = 0; if (status & E1_LOS) strcat (buf, ",LOS"); if (status & E1_AIS) strcat (buf, ",AIS"); if (status & E1_LOF) strcat (buf, ",LOF"); if (status & E1_LOMF) strcat (buf, ",LOMF"); if (status & E1_FARLOF) strcat (buf, ",FARLOF"); if (status & E1_AIS16) strcat (buf, ",AIS16"); if (status & E1_FARLOMF) strcat (buf, ",FARLOMF"); if (status & E1_TSTREQ) strcat (buf, ",TSTREQ"); if (status & E1_TSTERR) strcat (buf, ",TSTERR"); if (buf[0] == ',') return buf+1; return "Unknown"; } static int print_frac (char *s, int leftalign, u_long numerator, u_long divider) { int n, length = 0; if (numerator < 1 || divider < 1) { length += sprintf (s+length, leftalign ? "/- " : " -"); return length; } n = (int) (0.5 + 1000.0 * numerator / divider); if (n < 1000) { length += sprintf (s+length, leftalign ? "/.%-3d" : " .%03d", n); return length; } *(s + length) = leftalign ? '/' : ' '; length ++; if (n >= 1000000) n = (n+500) / 1000 * 1000; else if (n >= 100000) n = (n+50) / 100 * 100; else if (n >= 10000) n = (n+5) / 10 * 10; switch (n) { case 1000: length += printf (s+length, ".999"); return length; case 10000: n = 9990; break; case 100000: n = 99900; break; case 1000000: n = 999000; break; } if (n < 10000) length += sprintf (s+length, "%d.%d", n/1000, n/10%100); else if (n < 100000) length += sprintf (s+length, "%d.%d", n/1000, n/100%10); else if (n < 1000000) length += sprintf (s+length, "%d.", n/1000); else length += sprintf (s+length, "%d", n/1000); return length; } static int print_e1_stats (char *s, ct_chan_t *c) { struct e1_counters total; u_long totsec; int length = 0; totsec = c->totsec + c->cursec; total.bpv = c->total.bpv + c->currnt.bpv; total.fse = c->total.fse + c->currnt.fse; total.crce = c->total.crce + c->currnt.crce; total.rcrce = c->total.rcrce + c->currnt.rcrce; total.uas = c->total.uas + c->currnt.uas; total.les = c->total.les + c->currnt.les; total.es = c->total.es + c->currnt.es; total.bes = c->total.bes + c->currnt.bes; total.ses = c->total.ses + c->currnt.ses; total.oofs = c->total.oofs + c->currnt.oofs; total.css = c->total.css + c->currnt.css; total.dm = c->total.dm + c->currnt.dm; length += sprintf (s + length, " Unav/Degr Bpv/Fsyn CRC/RCRC Err/Lerr Sev/Bur Oof/Slp Status\n"); /* Unavailable seconds, degraded minutes */ length += print_frac (s + length, 0, c->currnt.uas, c->cursec); length += print_frac (s + length, 1, 60 * c->currnt.dm, c->cursec); /* Bipolar violations, frame sync errors */ length += print_frac (s + length, 0, c->currnt.bpv, c->cursec); length += print_frac (s + length, 1, c->currnt.fse, c->cursec); /* CRC errors, remote CRC errors (E-bit) */ length += print_frac (s + length, 0, c->currnt.crce, c->cursec); length += print_frac (s + length, 1, c->currnt.rcrce, c->cursec); /* Errored seconds, line errored seconds */ length += print_frac (s + length, 0, c->currnt.es, c->cursec); length += print_frac (s + length, 1, c->currnt.les, c->cursec); /* Severely errored seconds, burst errored seconds */ length += print_frac (s + length, 0, c->currnt.ses, c->cursec); length += print_frac (s + length, 1, c->currnt.bes, c->cursec); /* Out of frame seconds, controlled slip seconds */ length += print_frac (s + length, 0, c->currnt.oofs, c->cursec); length += print_frac (s + length, 1, c->currnt.css, c->cursec); length += sprintf (s + length, " %s\n", format_e1_status (c->status)); /* Print total statistics. */ length += print_frac (s + length, 0, total.uas, totsec); length += print_frac (s + length, 1, 60 * total.dm, totsec); length += print_frac (s + length, 0, total.bpv, totsec); length += print_frac (s + length, 1, total.fse, totsec); length += print_frac (s + length, 0, total.crce, totsec); length += print_frac (s + length, 1, total.rcrce, totsec); length += print_frac (s + length, 0, total.es, totsec); length += print_frac (s + length, 1, total.les, totsec); length += print_frac (s + length, 0, total.ses, totsec); length += print_frac (s + length, 1, total.bes, totsec); length += print_frac (s + length, 0, total.oofs, totsec); length += print_frac (s + length, 1, total.css, totsec); length += sprintf (s + length, " -- Total\n"); return length; } static int print_chan (char *s, ct_chan_t *c) { drv_t *d = c->sys; int length = 0; length += sprintf (s + length, "ct%d", c->board->num * NCHAN + c->num); if (d->chan->debug) length += sprintf (s + length, " debug=%d", d->chan->debug); switch (ct_get_config (c->board)) { case CFG_A: length += sprintf (s + length, " cfg=A"); break; case CFG_B: length += sprintf (s + length, " cfg=B"); break; case CFG_C: length += sprintf (s + length, " cfg=C"); break; default: length += sprintf (s + length, " cfg=unknown"); break; } if (ct_get_baud (c)) length += sprintf (s + length, " %ld", ct_get_baud (c)); else length += sprintf (s + length, " extclock"); if (c->mode == M_E1 || c->mode == M_G703) switch (ct_get_clk(c)) { case GCLK_INT : length += sprintf (s + length, " syn=int"); break; case GCLK_RCV : length += sprintf (s + length, " syn=rcv"); break; case GCLK_RCLKO : length += sprintf (s + length, " syn=xrcv"); break; } if (c->mode == M_HDLC) { length += sprintf (s + length, " dpll=%s", ct_get_dpll (c) ? "on" : "off"); length += sprintf (s + length, " nrzi=%s", ct_get_nrzi (c) ? "on" : "off"); length += sprintf (s + length, " invtclk=%s", ct_get_invtxc (c) ? "on" : "off"); length += sprintf (s + length, " invrclk=%s", ct_get_invrxc (c) ? "on" : "off"); } if (c->mode == M_E1) length += sprintf (s + length, " higain=%s", ct_get_higain (c)? "on" : "off"); length += sprintf (s + length, " loop=%s", ct_get_loop (c) ? "on" : "off"); if (c->mode == M_E1) length += sprintf (s + length, " ts=%s", format_timeslots (ct_get_ts(c))); if (c->mode == M_E1 && ct_get_config (c->board) != CFG_A) length += sprintf (s + length, " pass=%s", format_timeslots (ct_get_subchan(c->board))); if (c->mode == M_G703) { int lq, x; x = splimp (); lq = ct_get_lq (c); splx (x); length += sprintf (s + length, " (level=-%.1fdB)", lq / 10.0); } length += sprintf (s + length, "\n"); return length; } #if __FreeBSD_version >= 500000 static int ng_ct_rcvmsg (node_p node, item_p item, hook_p lasthook) { drv_t *d = NG_NODE_PRIVATE (node); struct ng_mesg *msg; #else static int ng_ct_rcvmsg (node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) { drv_t *d = node->private; #endif struct ng_mesg *resp = NULL; int error = 0; if (!d) return EINVAL; CT_DEBUG (d, ("Rcvmsg\n")); #if __FreeBSD_version >= 500000 NGI_GET_MSG (item, msg); #endif switch (msg->header.typecookie) { default: error = EINVAL; break; case NGM_CT_COOKIE: printf ("Don't forget to implement\n"); error = EINVAL; break; case NGM_GENERIC_COOKIE: switch (msg->header.cmd) { default: error = EINVAL; break; case NGM_TEXT_STATUS: { char *s; int l = 0; int dl = sizeof (struct ng_mesg) + 730; #if __FreeBSD_version >= 500000 NG_MKRESPONSE (resp, msg, dl, M_NOWAIT); if (! resp) { error = ENOMEM; break; } #else MALLOC (resp, struct ng_mesg *, dl, M_NETGRAPH, M_NOWAIT); if (! resp) { error = ENOMEM; break; } bzero (resp, dl); #endif s = (resp)->data; l += print_chan (s + l, d->chan); l += print_stats (s + l, d->chan, 1); l += print_modems (s + l, d->chan, 1); l += print_e1_stats (s + l, d->chan); #if __FreeBSD_version < 500000 (resp)->header.version = NG_VERSION; (resp)->header.arglen = strlen (s) + 1; (resp)->header.token = msg->header.token; (resp)->header.typecookie = NGM_CT_COOKIE; (resp)->header.cmd = msg->header.cmd; #endif strncpy ((resp)->header.cmdstr, "status", NG_CMDSTRLEN); } break; } break; } #if __FreeBSD_version >= 500000 NG_RESPOND_MSG (error, node, item, resp); NG_FREE_MSG (msg); #else *rptr = resp; FREE (msg, M_NETGRAPH); #endif return error; } #if __FreeBSD_version >= 500000 static int ng_ct_rcvdata (hook_p hook, item_p item) { drv_t *d = NG_NODE_PRIVATE (NG_HOOK_NODE(hook)); struct mbuf *m; - meta_p meta; + struct ng_tag_prio *ptag; #else static int ng_ct_rcvdata (hook_p hook, struct mbuf *m, meta_p meta) { drv_t *d = hook->node->private; #endif struct ifqueue *q; int s; if (!d) return ENETDOWN; #if __FreeBSD_version >= 500000 NGI_GET_M (item, m); - NGI_GET_META (item, meta); NG_FREE_ITEM (item); if (! NG_HOOK_PRIVATE (hook) || ! d) { NG_FREE_M (m); - NG_FREE_META (meta); #else if (! hook->private || ! d) { NG_FREE_DATA (m,meta); #endif return ENETDOWN; } - q = (meta && meta->priority > 0) ? &d->hi_queue : &d->queue; + + /* Check for high priority data */ + if ((ptag = (struct ng_tag_prio *)m_tag_locate(m, NGM_GENERIC_COOKIE, + NG_TAG_PRIO, NULL)) != NULL && (ptag->priority > NG_PRIO_CUTOFF) ) + q = &d->hi_queue; + else + q = &d->queue; + s = splimp (); #if __FreeBSD_version >= 500000 IF_LOCK (q); if (_IF_QFULL (q)) { _IF_DROP (q); IF_UNLOCK (q); splx (s); NG_FREE_M (m); - NG_FREE_META (meta); return ENOBUFS; } _IF_ENQUEUE (q, m); IF_UNLOCK (q); #else if (IF_QFULL (q)) { IF_DROP (q); splx (s); NG_FREE_DATA (m, meta); return ENOBUFS; } IF_ENQUEUE (q, m); #endif ct_start (d); splx (s); return 0; } static int ng_ct_rmnode (node_p node) { #if __FreeBSD_version >= 500000 drv_t *d = NG_NODE_PRIVATE (node); CT_DEBUG (d, ("Rmnode\n")); if (d && d->running) { int s = splimp (); ct_down (d); splx (s); } #ifdef KLD_MODULE if (node->nd_flags & NG_REALLY_DIE) { NG_NODE_SET_PRIVATE (node, NULL); NG_NODE_UNREF (node); } node->nd_flags &= ~NG_INVALID; #endif #else /* __FreeBSD_version < 500000 */ drv_t *d = node->private; int s; if (!d) return 0; s = splimp (); ct_down (d); splx (s); node->flags |= NG_INVALID; ng_cutlinks (node); #ifdef KLD_MODULE ng_unname (node); ng_unref (node); #else node->flags &= ~NG_INVALID; #endif #endif return 0; } static void ng_ct_watchdog (void *arg) { drv_t *d = arg; if (!d) return; if (d->timeout == 1) ct_watchdog (d); if (d->timeout) d->timeout--; d->timeout_handle = timeout (ng_ct_watchdog, d, hz); } static int ng_ct_connect (hook_p hook) { #if __FreeBSD_version >= 500000 drv_t *d = NG_NODE_PRIVATE (NG_HOOK_NODE (hook)); #else drv_t *d = hook->node->private; #endif if (!d) return 0; d->timeout_handle = timeout (ng_ct_watchdog, d, hz); return 0; } static int ng_ct_disconnect (hook_p hook) { #if __FreeBSD_version >= 500000 drv_t *d = NG_NODE_PRIVATE (NG_HOOK_NODE (hook)); #else drv_t *d = hook->node->private; #endif if (!d) return 0; #if __FreeBSD_version >= 500000 if (NG_HOOK_PRIVATE (hook)) #else if (hook->private) #endif ct_down (d); untimeout (ng_ct_watchdog, d, d->timeout_handle); return 0; } #endif static int ct_modevent (module_t mod, int type, void *unused) { struct cdev *dev; static int load_count = 0; struct cdevsw *cdsw; #if __FreeBSD_version >= 502103 dev = findcdev (makedev(CDEV_MAJOR, 0)); #else dev = makedev (CDEV_MAJOR, 0); #endif switch (type) { case MOD_LOAD: if (dev != NULL && (cdsw = devsw (dev)) && cdsw->d_maj == CDEV_MAJOR) { printf ("Tau-ISA driver is already in system\n"); return (ENXIO); } #if __FreeBSD_version >= 500000 && defined NETGRAPH if (ng_newtype (&typestruct)) printf ("Failed to register ng_ct\n"); #endif ++load_count; #if __FreeBSD_version <= 500000 cdevsw_add (&ct_cdevsw); #endif timeout_handle = timeout (ct_timeout, 0, hz*5); break; case MOD_UNLOAD: if (load_count == 1) { printf ("Removing device entry for Tau-ISA\n"); #if __FreeBSD_version <= 500000 cdevsw_remove (&ct_cdevsw); #endif #if __FreeBSD_version >= 500000 && defined NETGRAPH ng_rmtype (&typestruct); #endif } if (timeout_handle.callout) untimeout (ct_timeout, 0, timeout_handle); --load_count; break; case MOD_SHUTDOWN: break; } return 0; } #ifdef NETGRAPH static struct ng_type typestruct = { .version = NG_ABI_VERSION, .name = NG_CT_NODE_TYPE, .constructor = ng_ct_constructor, .rcvmsg = ng_ct_rcvmsg, .shutdown = ng_ct_rmnode, .newhook = ng_ct_newhook, .connect = ng_ct_connect, .rcvdata = ng_ct_rcvdata, .disconnect = ng_ct_disconnect, }; #endif /*NETGRAPH*/ #if __FreeBSD_version >= 500000 #ifdef NETGRAPH MODULE_DEPEND (ng_ct, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); #else MODULE_DEPEND (ct, sppp, 1, 1, 1); #endif #ifdef KLD_MODULE DRIVER_MODULE (ctmod, isa, ct_isa_driver, ct_devclass, ct_modevent, NULL); #else DRIVER_MODULE (ct, isa, ct_isa_driver, ct_devclass, ct_modevent, NULL); #endif #elif __FreeBSD_version >= 400000 #ifdef NETGRAPH DRIVER_MODULE(ct, isa, ct_isa_driver, ct_devclass, ng_mod_event, &typestruct); #else DRIVER_MODULE(ct, isa, ct_isa_driver, ct_devclass, ct_modevent, 0); #endif #endif /* __FreeBSD_version >= 400000 */ #endif /* NCTAU */ Index: head/sys/dev/cx/if_cx.c =================================================================== --- head/sys/dev/cx/if_cx.c (revision 131107) +++ head/sys/dev/cx/if_cx.c (revision 131108) @@ -1,2933 +1,2937 @@ /* * Cronyx-Sigma adapter driver for FreeBSD. * Supports PPP/HDLC and Cisco/HDLC protocol in synchronous mode, * and asyncronous channels with full modem control. * Keepalive protocol implemented in both Cisco and PPP modes. * * Copyright (C) 1994-2002 Cronyx Engineering. * Author: Serge Vakulenko, * * Copyright (C) 1999-2004 Cronyx Engineering. * Rewritten on DDK, ported to NETGRAPH, rewritten for FreeBSD 3.x-5.x by * Kurakin Roman, * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations a permission to use, * modify and redistribute this software in source and binary forms, * as long as this message is kept with the software, all derivative * works or modified versions. * * Cronyx Id: if_cx.c,v 1.1.2.34 2004/06/23 17:09:13 rik Exp $ */ #include __FBSDID("$FreeBSD$"); #include #if __FreeBSD_version >= 500000 # define NCX 1 #else # include "cx.h" #endif #if NCX > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __FreeBSD_version < 500000 # include # include #endif #include #if __FreeBSD_version <= 501000 # include #endif #include #include #include #include "opt_ng_cronyx.h" #ifdef NETGRAPH_CRONYX # include "opt_netgraph.h" # include # include # include #else # include # if __FreeBSD_version < 500000 # include "sppp.h" # if NSPPP <= 0 # error The device cx requires sppp or netgraph. # endif # endif # include # define PP_CISCO IFF_LINK2 # if __FreeBSD_version < 500000 # include # endif # include # define NBPFILTER NBPF #endif #if __FreeBSD_version < 502113 #define ttyld_modem(foo, bar) ((*linesw[(foo)->t_line].l_modem)((foo), (bar))) #define ttyld_rint(foo, bar) ((*linesw[(foo)->t_line].l_rint)((bar), (foo))) #define ttyld_start(foo) ((*linesw[(foo)->t_line].l_start)((foo))) #define ttyld_open(foo, bar) ((*linesw[(foo)->t_line].l_open) ((bar), (foo))) #define ttyld_close(foo, bar) ((*linesw[(foo)->t_line].l_close) ((foo), (bar))) #define ttyld_read(foo, bar, barf) ((*linesw[(foo)->t_line].l_read) ((foo), (bar), (barf))) #define ttyld_write(foo, bar, barf) ((*linesw[(foo)->t_line].l_write) ((foo), (bar), (barf))) #endif /* If we don't have Cronyx's sppp version, we don't have fr support via sppp */ #ifndef PP_FR #define PP_FR 0 #endif #define CX_DEBUG(d,s) ({if (d->chan->debug) {\ printf ("%s: ", d->name); printf s;}}) #define CX_DEBUG2(d,s) ({if (d->chan->debug>1) {\ printf ("%s: ", d->name); printf s;}}) #define UNIT(d) (minor(d) & 0x3f) #define IF_CUNIT(d) (minor(d) & 0x40) #define UNIT_CTL 0x3f #define CALLOUT(d) (minor(d) & 0x80) #define CDEV_MAJOR 42 typedef struct _async_q { int beg; int end; #define BF_SZ 14400 int buf[BF_SZ+1]; } async_q; #define AQ_GSZ(q) ((BF_SZ + (q)->end - (q)->beg)%BF_SZ) #define AQ_PUSH(q,c) {*((q)->buf + (q)->end) = c;\ (q)->end = ((q)->end + 1)%BF_SZ;} #define AQ_POP(q,c) {c = *((q)->buf + (q)->beg);\ (q)->beg = ((q)->beg + 1)%BF_SZ;} static void cx_identify __P((driver_t *, device_t)); static int cx_probe __P((device_t)); static int cx_attach __P((device_t)); static int cx_detach __P((device_t)); static device_method_t cx_isa_methods [] = { DEVMETHOD(device_identify, cx_identify), DEVMETHOD(device_probe, cx_probe), DEVMETHOD(device_attach, cx_attach), DEVMETHOD(device_detach, cx_detach), {0, 0} }; typedef struct _cx_dma_mem_t { unsigned long phys; void *virt; size_t size; #if __FreeBSD_version >= 500000 bus_dma_tag_t dmat; bus_dmamap_t mapp; #endif } cx_dma_mem_t; typedef struct _drv_t { char name [8]; cx_chan_t *chan; cx_board_t *board; cx_dma_mem_t dmamem; struct tty *tty; struct callout_handle dcd_timeout_handle; unsigned dtrwait; unsigned dtroff; unsigned callout; unsigned lock; int open_dev; int cd; int running; struct callout_handle dtr_timeout_handle; #ifdef NETGRAPH char nodename [NG_NODELEN+1]; hook_p hook; hook_p debug_hook; node_p node; struct ifqueue lo_queue; struct ifqueue hi_queue; short timeout; struct callout_handle timeout_handle; #else struct sppp pp; #endif struct cdev *devt[3]; async_q aqueue; #define CX_READ 1 #define CX_WRITE 2 int intr_action; short atimeout; } drv_t; typedef struct _bdrv_t { cx_board_t *board; struct resource *base_res; struct resource *drq_res; struct resource *irq_res; int base_rid; int drq_rid; int irq_rid; void *intrhand; drv_t channel [NCHAN]; } bdrv_t; static driver_t cx_isa_driver = { "cx", cx_isa_methods, sizeof (bdrv_t), }; static devclass_t cx_devclass; extern long csigma_fw_len; extern const char *csigma_fw_version; extern const char *csigma_fw_date; extern const char *csigma_fw_copyright; extern const cr_dat_tst_t csigma_fw_tvec[]; extern const u_char csigma_fw_data[]; static void cx_oproc (struct tty *tp); static int cx_param (struct tty *tp, struct termios *t); static void cx_stop (struct tty *tp, int flag); static void cx_dtrwakeup (void *a); static void cx_receive (cx_chan_t *c, char *data, int len); static void cx_transmit (cx_chan_t *c, void *attachment, int len); static void cx_error (cx_chan_t *c, int data); static void cx_modem (cx_chan_t *c); static void cx_up (drv_t *d); static void cx_start (drv_t *d); #if __FreeBSD_version < 502113 static void ttyldoptim(struct tty *tp); #endif #if __FreeBSD_version < 500000 static swihand_t cx_softintr; #else static void cx_softintr (void *); static void *cx_fast_ih; #endif static void cx_down (drv_t *d); static void cx_watchdog (drv_t *d); static void cx_carrier (void *arg); #ifdef NETGRAPH extern struct ng_type typestruct; #else static void cx_ifstart (struct ifnet *ifp); static void cx_tlf (struct sppp *sp); static void cx_tls (struct sppp *sp); static void cx_ifwatchdog (struct ifnet *ifp); static int cx_sioctl (struct ifnet *ifp, u_long cmd, caddr_t data); static void cx_initialize (void *softc); #endif static cx_board_t *adapter [NCX]; static drv_t *channel [NCX*NCHAN]; static struct callout_handle led_timo [NCX]; static struct callout_handle timeout_handle; extern struct cdevsw cx_cdevsw; static int MY_SOFT_INTR; /* * Print the mbuf chain, for debug purposes only. */ static void printmbuf (struct mbuf *m) { printf ("mbuf:"); for (; m; m=m->m_next) { if (m->m_flags & M_PKTHDR) printf (" HDR %d:", m->m_pkthdr.len); if (m->m_flags & M_EXT) printf (" EXT:"); printf (" %d", m->m_len); } printf ("\n"); } /* * Make an mbuf from data. */ static struct mbuf *makembuf (void *buf, u_int len) { struct mbuf *m, *o, *p; MGETHDR (m, M_DONTWAIT, MT_DATA); if (! m) return 0; if (len >= MINCLSIZE) MCLGET (m, M_DONTWAIT); m->m_pkthdr.len = len; m->m_len = 0; p = m; while (len) { u_int n = M_TRAILINGSPACE (p); if (n > len) n = len; if (! n) { /* Allocate new mbuf. */ o = p; MGET (p, M_DONTWAIT, MT_DATA); if (! p) { m_freem (m); return 0; } if (len >= MINCLSIZE) MCLGET (p, M_DONTWAIT); p->m_len = 0; o->m_next = p; n = M_TRAILINGSPACE (p); if (n > len) n = len; } bcopy (buf, mtod (p, caddr_t) + p->m_len, n); p->m_len += n; buf = n + (char*) buf; len -= n; } return m; } /* * Recover after lost transmit interrupts. */ static void cx_timeout (void *arg) { drv_t *d; int s, i; for (i=0; iatimeout == 1 && d->tty && d->tty->t_state & TS_BUSY) { d->tty->t_state &= ~TS_BUSY; if (d->tty->t_dev) { d->intr_action |= CX_WRITE; MY_SOFT_INTR = 1; #if __FreeBSD_version >= 500000 swi_sched (cx_fast_ih, 0); #else setsofttty (); #endif } CX_DEBUG (d, ("cx_timeout\n")); } if (d->atimeout) d->atimeout--; splx (s); } timeout_handle = timeout (cx_timeout, 0, hz*5); } static void cx_led_off (void *arg) { cx_board_t *b = arg; int s = splhigh (); cx_led (b, 0); led_timo[b->num].callout = 0; splx (s); } /* * Activate interupt handler from DDK. */ static void cx_intr (void *arg) { bdrv_t *bd = arg; cx_board_t *b = bd->board; int s = splhigh (); /* Turn LED on. */ cx_led (b, 1); cx_int_handler (b); /* Turn LED off 50 msec later. */ if (! led_timo[b->num].callout) led_timo[b->num] = timeout (cx_led_off, b, hz/20); splx (s); } static int probe_irq (cx_board_t *b, int irq) { int mask, busy, cnt; /* Clear pending irq, if any. */ cx_probe_irq (b, -irq); DELAY (100); for (cnt=0; cnt<5; ++cnt) { /* Get the mask of pending irqs, assuming they are busy. * Activate the adapter on given irq. */ busy = cx_probe_irq (b, irq); DELAY (100); /* Get the mask of active irqs. * Deactivate our irq. */ mask = cx_probe_irq (b, -irq); DELAY (100); if ((mask & ~busy) == 1 << irq) { cx_probe_irq (b, 0); /* printf ("cx%d: irq %d ok, mask=0x%04x, busy=0x%04x\n", b->num, irq, mask, busy); */ return 1; } } /* printf ("cx%d: irq %d not functional, mask=0x%04x, busy=0x%04x\n", b->num, irq, mask, busy); */ cx_probe_irq (b, 0); return 0; } static short porttab [] = { 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0x3c0, 0x3e0, 0 }; static char dmatab [] = { 7, 6, 5, 0 }; static char irqtab [] = { 5, 10, 11, 7, 3, 15, 12, 0 }; static int cx_is_free_res (device_t dev, int rid, int type, u_long start, u_long end, u_long count) { struct resource *res; if (!(res = bus_alloc_resource (dev, type, &rid, start, end, count, RF_ALLOCATED))) return 0; bus_release_resource (dev, type, rid, res); return 1; } static void cx_identify (driver_t *driver, device_t dev) { u_long iobase, rescount; int devcount; device_t *devices; device_t child; devclass_t my_devclass; int i, k; if ((my_devclass = devclass_find ("cx")) == NULL) return; devclass_get_devices (my_devclass, &devices, &devcount); if (devcount == 0) { /* We should find all devices by our self. We could alter other * devices, but we don't have a choise */ for (i = 0; (iobase = porttab [i]) != 0; i++) { if (!cx_is_free_res (dev, 1, SYS_RES_IOPORT, iobase, iobase + NPORT, NPORT)) continue; if (cx_probe_board (iobase, -1, -1) == 0) continue; devcount++; child = BUS_ADD_CHILD (dev, ISA_ORDER_SPECULATIVE, "cx", -1); if (child == NULL) return; device_set_desc_copy (child, "Cronyx Sigma"); device_set_driver (child, driver); bus_set_resource (child, SYS_RES_IOPORT, 0, iobase, NPORT); if (devcount >= NCX) break; } } else { static short porttab [] = { 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0x3c0, 0x3e0, 0 }; /* Lets check user choise. */ for (k = 0; k < devcount; k++) { if (bus_get_resource (devices[k], SYS_RES_IOPORT, 0, &iobase, &rescount) != 0) continue; for (i = 0; porttab [i] != 0; i++) { if (porttab [i] != iobase) continue; if (!cx_is_free_res (devices[k], 1, SYS_RES_IOPORT, iobase, iobase + NPORT, NPORT)) continue; if (cx_probe_board (iobase, -1, -1) == 0) continue; porttab [i] = -1; device_set_desc_copy (devices[k], "Cronyx Sigma"); break; } if (porttab [i] == 0) { device_delete_child ( device_get_parent (devices[k]), devices [k]); devices[k] = 0; continue; } } for (k = 0; k < devcount; k++) { if (devices[k] == 0) continue; if (bus_get_resource (devices[k], SYS_RES_IOPORT, 0, &iobase, &rescount) == 0) continue; for (i = 0; (iobase = porttab [i]) != 0; i++) { if (porttab [i] == -1) { continue; } if (!cx_is_free_res (devices[k], 1, SYS_RES_IOPORT, iobase, iobase + NPORT, NPORT)) continue; if (cx_probe_board (iobase, -1, -1) == 0) continue; bus_set_resource (devices[k], SYS_RES_IOPORT, 0, iobase, NPORT); porttab [i] = -1; device_set_desc_copy (devices[k], "Cronyx Sigma"); break; } if (porttab [i] == 0) { device_delete_child ( device_get_parent (devices[k]), devices [k]); } } free (devices, M_TEMP); } return; } static int cx_probe (device_t dev) { int unit = device_get_unit (dev); int i; u_long iobase, rescount; if (!device_get_desc (dev) || strcmp (device_get_desc (dev), "Cronyx Sigma")) return ENXIO; if (bus_get_resource (dev, SYS_RES_IOPORT, 0, &iobase, &rescount) != 0) { printf ("cx%d: Couldn't get IOPORT\n", unit); return ENXIO; } if (!cx_is_free_res (dev, 1, SYS_RES_IOPORT, iobase, iobase + NPORT, NPORT)) { printf ("cx%d: Resource IOPORT isn't free %lx\n", unit, iobase); return ENXIO; } for (i = 0; porttab [i] != 0; i++) { if (porttab [i] == iobase) { porttab [i] = -1; break; } } if (porttab [i] == 0) { return ENXIO; } if (!cx_probe_board (iobase, -1, -1)) { printf ("cx%d: probing for Sigma at %lx faild\n", unit, iobase); return ENXIO; } return 0; } #if __FreeBSD_version >= 500000 static void cx_bus_dmamap_addr (void *arg, bus_dma_segment_t *segs, int nseg, int error) { unsigned long *addr; if (error) return; KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); addr = arg; *addr = segs->ds_addr; } static int cx_bus_dma_mem_alloc (int bnum, int cnum, cx_dma_mem_t *dmem) { int error; error = bus_dma_tag_create (NULL, 16, 0, BUS_SPACE_MAXADDR_24BIT, BUS_SPACE_MAXADDR, NULL, NULL, dmem->size, 1, dmem->size, 0, NULL, NULL, &dmem->dmat); if (error) { if (cnum >= 0) printf ("cx%d-%d: ", bnum, cnum); else printf ("cx%d: ", bnum); printf ("couldn't allocate tag for dma memory\n"); return 0; } error = bus_dmamem_alloc (dmem->dmat, (void **)&dmem->virt, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &dmem->mapp); if (error) { if (cnum >= 0) printf ("cx%d-%d: ", bnum, cnum); else printf ("cx%d: ", bnum); printf ("couldn't allocate mem for dma memory\n"); bus_dma_tag_destroy (dmem->dmat); return 0; } error = bus_dmamap_load (dmem->dmat, dmem->mapp, dmem->virt, dmem->size, cx_bus_dmamap_addr, &dmem->phys, 0); if (error) { if (cnum >= 0) printf ("cx%d-%d: ", bnum, cnum); else printf ("cx%d: ", bnum); printf ("couldn't load mem map for dma memory\n"); bus_dmamem_free (dmem->dmat, dmem->virt, dmem->mapp); bus_dma_tag_destroy (dmem->dmat); return 0; } return 1; } static void cx_bus_dma_mem_free (cx_dma_mem_t *dmem) { bus_dmamap_unload (dmem->dmat, dmem->mapp); bus_dmamem_free (dmem->dmat, dmem->virt, dmem->mapp); bus_dma_tag_destroy (dmem->dmat); } #else static int cx_bus_dma_mem_alloc (int bnum, int cnum, cx_dma_mem_t *dmem) { dmem->virt = contigmalloc (dmem->size, M_DEVBUF, M_WAITOK, 0x100000, 0x1000000, 16, 0); if (dmem->virt == NULL) { if (cnum >= 0) printf ("cx%d-%d: ", bnum, cnum); else printf ("cx%d: ", bnum); printf ("couldn't allocate memory for dma memory\n", unit); return 0; } dmem->phys = vtophys (dmem->virt); return 1; } static void cx_bus_dma_mem_free (cx_dma_mem_t *dmem) { contigfree (dmem->virt, dmem->size, M_DEVBUF); } #endif /* * The adapter is present, initialize the driver structures. */ static int cx_attach (device_t dev) { bdrv_t *bd = device_get_softc (dev); u_long iobase, drq, irq, rescount; int unit = device_get_unit (dev); cx_board_t *b; cx_chan_t *c; drv_t *d; int i; int s; KASSERT ((bd != NULL), ("cx%d: NULL device softc\n", unit)); bus_get_resource (dev, SYS_RES_IOPORT, 0, &iobase, &rescount); bd->base_rid = 0; bd->base_res = bus_alloc_resource (dev, SYS_RES_IOPORT, &bd->base_rid, iobase, iobase + NPORT, NPORT, RF_ACTIVE); if (! bd->base_res) { printf ("cx%d: cannot allocate base address\n", unit); return ENXIO; } if (bus_get_resource (dev, SYS_RES_DRQ, 0, &drq, &rescount) != 0) { for (i = 0; (drq = dmatab [i]) != 0; i++) { if (!cx_is_free_res (dev, 1, SYS_RES_DRQ, drq, drq + 1, 1)) continue; bus_set_resource (dev, SYS_RES_DRQ, 0, drq, 1); break; } if (dmatab[i] == 0) { bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); printf ("cx%d: Couldn't get DRQ\n", unit); return ENXIO; } } bd->drq_rid = 0; bd->drq_res = bus_alloc_resource (dev, SYS_RES_DRQ, &bd->drq_rid, drq, drq + 1, 1, RF_ACTIVE); if (! bd->drq_res) { printf ("cx%d: cannot allocate drq\n", unit); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); return ENXIO; } if (bus_get_resource (dev, SYS_RES_IRQ, 0, &irq, &rescount) != 0) { for (i = 0; (irq = irqtab [i]) != 0; i++) { if (!cx_is_free_res (dev, 1, SYS_RES_IRQ, irq, irq + 1, 1)) continue; bus_set_resource (dev, SYS_RES_IRQ, 0, irq, 1); break; } if (irqtab[i] == 0) { bus_release_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); printf ("cx%d: Couldn't get IRQ\n", unit); return ENXIO; } } bd->irq_rid = 0; bd->irq_res = bus_alloc_resource (dev, SYS_RES_IRQ, &bd->irq_rid, irq, irq + 1, 1, RF_ACTIVE); if (! bd->irq_res) { printf ("cx%d: Couldn't allocate irq\n", unit); bus_release_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); return ENXIO; } b = malloc (sizeof (cx_board_t), M_DEVBUF, M_WAITOK); if (!b) { printf ("cx:%d: Couldn't allocate memory\n", unit); return (ENXIO); } adapter[unit] = b; bzero (b, sizeof(cx_board_t)); if (! cx_open_board (b, unit, iobase, irq, drq)) { printf ("cx%d: error loading firmware\n", unit); free (b, M_DEVBUF); bus_release_resource (dev, SYS_RES_IRQ, bd->irq_rid, bd->irq_res); bus_release_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); return ENXIO; } bd->board = b; if (! probe_irq (b, irq)) { printf ("cx%d: irq %ld not functional\n", unit, irq); bd->board = 0; adapter [unit] = 0; free (b, M_DEVBUF); bus_release_resource (dev, SYS_RES_IRQ, bd->irq_rid, bd->irq_res); bus_release_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); return ENXIO; } s = splhigh (); if (bus_setup_intr (dev, bd->irq_res, INTR_TYPE_NET, cx_intr, bd, &bd->intrhand)) { printf ("cx%d: Can't setup irq %ld\n", unit, irq); bd->board = 0; adapter [unit] = 0; free (b, M_DEVBUF); bus_release_resource (dev, SYS_RES_IRQ, bd->irq_rid, bd->irq_res); bus_release_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); splx (s); return ENXIO; } cx_init (b, b->num, b->port, irq, drq); cx_setup_board (b, 0, 0, 0); printf ("cx%d: \n", b->num, b->name); for (c=b->chan; cchan+NCHAN; ++c) { char *dnmt="tty %x"; char *dnmc="cua %x"; if (c->type == T_NONE) continue; d = &bd->channel[c->num]; d->dmamem.size = sizeof(cx_buf_t); if (! cx_bus_dma_mem_alloc (unit, c->num, &d->dmamem)) continue; channel [b->num*NCHAN + c->num] = d; sprintf (d->name, "cx%d.%d", b->num, c->num); d->board = b; d->chan = c; d->dtrwait = 3 * hz; /* Default DTR off timeout is 3 seconds. */ d->open_dev = 0; c->sys = d; switch (c->type) { case T_SYNC_RS232: case T_SYNC_V35: case T_SYNC_RS449: case T_UNIV: case T_UNIV_RS232: case T_UNIV_RS449: case T_UNIV_V35: #ifdef NETGRAPH if (ng_make_node_common (&typestruct, &d->node) != 0) { printf ("%s: cannot make common node\n", d->name); channel [b->num*NCHAN + c->num] = 0; c->sys = 0; cx_bus_dma_mem_free (&d->dmamem); continue; } #if __FreeBSD_version >= 500000 NG_NODE_SET_PRIVATE (d->node, d); #else d->node->private = d; #endif sprintf (d->nodename, "%s%d", NG_CX_NODE_TYPE, c->board->num*NCHAN + c->num); if (ng_name_node (d->node, d->nodename)) { printf ("%s: cannot name node\n", d->nodename); #if __FreeBSD_version >= 500000 NG_NODE_UNREF (d->node); #else ng_rmnode (d->node); ng_unref (d->node); #endif channel [b->num*NCHAN + c->num] = 0; c->sys = 0; cx_bus_dma_mem_free (&d->dmamem); continue; } d->lo_queue.ifq_maxlen = IFQ_MAXLEN; d->hi_queue.ifq_maxlen = IFQ_MAXLEN; #if __FreeBSD_version >= 500000 mtx_init (&d->lo_queue.ifq_mtx, "cx_queue_lo", NULL, MTX_DEF); mtx_init (&d->hi_queue.ifq_mtx, "cx_queue_hi", NULL, MTX_DEF); #endif #else /*NETGRAPH*/ d->pp.pp_if.if_softc = d; #if __FreeBSD_version > 501000 if_initname (&d->pp.pp_if, "cx", b->num * NCHAN + c->num); #else d->pp.pp_if.if_unit = b->num * NCHAN + c->num; d->pp.pp_if.if_name = "cx"; #endif d->pp.pp_if.if_mtu = PP_MTU; d->pp.pp_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST; d->pp.pp_if.if_ioctl = cx_sioctl; d->pp.pp_if.if_start = cx_ifstart; d->pp.pp_if.if_watchdog = cx_ifwatchdog; d->pp.pp_if.if_init = cx_initialize; sppp_attach (&d->pp.pp_if); if_attach (&d->pp.pp_if); d->pp.pp_tlf = cx_tlf; d->pp.pp_tls = cx_tls; /* If BPF is in the kernel, call the attach for it. * Size of PPP header is 4 bytes. */ bpfattach (&d->pp.pp_if, DLT_PPP, 4); #endif /*NETGRAPH*/ } cx_start_chan (c, d->dmamem.virt, d->dmamem.phys); cx_register_receive (c, &cx_receive); cx_register_transmit (c, &cx_transmit); cx_register_error (c, &cx_error); cx_register_modem (c, &cx_modem); dnmt[3] = 'x'+b->num; dnmc[3] = 'x'+b->num; d->devt[0] = make_dev (&cx_cdevsw, b->num*NCHAN + c->num, UID_ROOT, GID_WHEEL, 0644, dnmt, b->num*NCHAN + c->num); d->devt[1] = make_dev (&cx_cdevsw, b->num*NCHAN + c->num + 64, UID_ROOT, GID_WHEEL, 0600, "cx%d", b->num*NCHAN + c->num); d->devt[2] = make_dev (&cx_cdevsw, b->num*NCHAN + c->num + 128, UID_ROOT, GID_WHEEL, 0660, dnmc, b->num*NCHAN + c->num); } splx (s); return 0; } static int cx_detach (device_t dev) { bdrv_t *bd = device_get_softc (dev); cx_board_t *b = bd->board; cx_chan_t *c; int s = splhigh (); /* Check if the device is busy (open). */ for (c = b->chan; c < b->chan + NCHAN; ++c) { drv_t *d = (drv_t*) c->sys; if (!d || d->chan->type == T_NONE) continue; if (d->lock) { splx (s); return EBUSY; } if (c->mode == M_ASYNC && d->tty && (d->tty->t_state & TS_ISOPEN) && (d->open_dev|0x2)) { splx (s); return EBUSY; } if (d->running) { splx (s); return EBUSY; } } /* Deactivate the timeout routine. And soft interrupt*/ if (led_timo[b->num].callout) untimeout (cx_led_off, b, led_timo[b->num]); for (c = b->chan; c < b->chan + NCHAN; ++c) { drv_t *d = c->sys; if (!d || d->chan->type == T_NONE) continue; if (d->dtr_timeout_handle.callout) untimeout (cx_dtrwakeup, d, d->dtr_timeout_handle); if (d->dcd_timeout_handle.callout) untimeout (cx_carrier, c, d->dcd_timeout_handle); } bus_teardown_intr (dev, bd->irq_res, bd->intrhand); bus_deactivate_resource (dev, SYS_RES_IRQ, bd->irq_rid, bd->irq_res); bus_release_resource (dev, SYS_RES_IRQ, bd->irq_rid, bd->irq_res); bus_deactivate_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_release_resource (dev, SYS_RES_DRQ, bd->drq_rid, bd->drq_res); bus_deactivate_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->irq_res); bus_release_resource (dev, SYS_RES_IOPORT, bd->base_rid, bd->base_res); cx_close_board (b); /* Detach the interfaces, free buffer memory. */ for (c = b->chan; c < b->chan + NCHAN; ++c) { drv_t *d = (drv_t*) c->sys; if (!d || d->chan->type == T_NONE) continue; #if __FreeBSD_version >= 502113 if (d->tty) { ttyrel (d->tty); d->tty = NULL; } #endif #ifdef NETGRAPH #if __FreeBSD_version >= 500000 if (d->node) { ng_rmnode_self (d->node); NG_NODE_UNREF (d->node); d->node = NULL; } mtx_destroy (&d->lo_queue.ifq_mtx); mtx_destroy (&d->hi_queue.ifq_mtx); #else ng_rmnode (d->node); d->node = NULL; #endif #else #if __FreeBSD_version >= 410000 && NBPFILTER > 0 /* Detach from the packet filter list of interfaces. */ bpfdetach (&d->pp.pp_if); #endif /* Detach from the sync PPP list. */ sppp_detach (&d->pp.pp_if); if_detach (&d->pp.pp_if); #endif destroy_dev (d->devt[0]); destroy_dev (d->devt[1]); destroy_dev (d->devt[2]); } cx_led_off (b); if (led_timo[b->num].callout) untimeout (cx_led_off, b, led_timo[b->num]); splx (s); s = splhigh (); for (c = b->chan; c < b->chan + NCHAN; ++c) { drv_t *d = (drv_t*) c->sys; if (!d || d->chan->type == T_NONE) continue; /* Deallocate buffers. */ cx_bus_dma_mem_free (&d->dmamem); } bd->board = 0; adapter [b->num] = 0; free (b, M_DEVBUF); splx (s); return 0; } #ifndef NETGRAPH static void cx_ifstart (struct ifnet *ifp) { drv_t *d = ifp->if_softc; cx_start (d); } static void cx_ifwatchdog (struct ifnet *ifp) { drv_t *d = ifp->if_softc; cx_watchdog (d); } static void cx_tlf (struct sppp *sp) { drv_t *d = sp->pp_if.if_softc; CX_DEBUG (d, ("cx_tlf\n")); /* cx_set_dtr (d->chan, 0);*/ /* cx_set_rts (d->chan, 0);*/ sp->pp_down (sp); } static void cx_tls (struct sppp *sp) { drv_t *d = sp->pp_if.if_softc; CX_DEBUG (d, ("cx_tls\n")); sp->pp_up (sp); } /* * Initialization of interface. * It seems to be never called by upper level. */ static void cx_initialize (void *softc) { drv_t *d = softc; CX_DEBUG (d, ("cx_initialize\n")); } /* * Process an ioctl request. */ static int cx_sioctl (struct ifnet *ifp, u_long cmd, caddr_t data) { drv_t *d = ifp->if_softc; int error, s, was_up, should_be_up; /* No socket ioctls while the channel is in async mode. */ if (d->chan->type == T_NONE || d->chan->mode == M_ASYNC) return EBUSY; /* Socket ioctls on slave subchannels are not allowed. */ was_up = (ifp->if_flags & IFF_RUNNING) != 0; error = sppp_ioctl (ifp, cmd, data); if (error) return error; if (! (ifp->if_flags & IFF_DEBUG)) d->chan->debug = 0; else if (! d->chan->debug) d->chan->debug = 1; switch (cmd) { default: CX_DEBUG2 (d, ("ioctl 0x%lx\n", cmd)); return 0; case SIOCADDMULTI: CX_DEBUG2 (d, ("SIOCADDMULTI\n")); return 0; case SIOCDELMULTI: CX_DEBUG2 (d, ("SIOCDELMULTI\n")); return 0; case SIOCSIFFLAGS: CX_DEBUG2 (d, ("SIOCSIFFLAGS\n")); break; case SIOCSIFADDR: CX_DEBUG2 (d, ("SIOCSIFADDR\n")); break; } /* We get here only in case of SIFFLAGS or SIFADDR. */ s = splhigh (); should_be_up = (ifp->if_flags & IFF_RUNNING) != 0; if (!was_up && should_be_up) { /* Interface goes up -- start it. */ cx_up (d); cx_start (d); } else if (was_up && !should_be_up) { /* Interface is going down -- stop it. */ /* if ((d->pp.pp_flags & PP_FR) || (ifp->if_flags & PP_CISCO))*/ cx_down (d); } splx (s); return 0; } #endif /*NETGRAPH*/ /* * Stop the interface. Called on splimp(). */ static void cx_down (drv_t *d) { int s = splhigh (); CX_DEBUG (d, ("cx_down\n")); cx_set_dtr (d->chan, 0); cx_set_rts (d->chan, 0); d->running = 0; splx (s); } /* * Start the interface. Called on splimp(). */ static void cx_up (drv_t *d) { int s = splhigh (); CX_DEBUG (d, ("cx_up\n")); cx_set_dtr (d->chan, 1); cx_set_rts (d->chan, 1); d->running = 1; splx (s); } /* * Start output on the (slave) interface. Get another datagram to send * off of the interface queue, and copy it to the interface * before starting the output. */ static void cx_send (drv_t *d) { struct mbuf *m; u_short len; CX_DEBUG2 (d, ("cx_send\n")); /* No output if the interface is down. */ if (! d->running) return; /* No output if the modem is off. */ if (! cx_get_dsr (d->chan) && ! cx_get_loop(d->chan)) return; if (cx_buf_free (d->chan)) { /* Get the packet to send. */ #ifdef NETGRAPH IF_DEQUEUE (&d->hi_queue, m); if (! m) IF_DEQUEUE (&d->lo_queue, m); #else m = sppp_dequeue (&d->pp.pp_if); #endif if (! m) return; #ifndef NETGRAPH if (d->pp.pp_if.if_bpf) #if __FreeBSD_version >= 500000 BPF_MTAP (&d->pp.pp_if, m); #else bpf_mtap (&d->pp.pp_if, m); #endif #endif len = m->m_pkthdr.len; if (! m->m_next) cx_send_packet (d->chan, (u_char*)mtod (m, caddr_t), len, 0); else { u_char buf [DMABUFSZ]; m_copydata (m, 0, len, buf); cx_send_packet (d->chan, buf, len, 0); } m_freem (m); /* Set up transmit timeout, 10 seconds. */ #ifdef NETGRAPH d->timeout = 10; #else d->pp.pp_if.if_timer = 10; #endif } #ifndef NETGRAPH d->pp.pp_if.if_flags |= IFF_OACTIVE; #endif } /* * Start output on the interface. * Always called on splimp(). */ static void cx_start (drv_t *d) { int s = splhigh (); if (d->running) { if (! d->chan->dtr) cx_set_dtr (d->chan, 1); if (! d->chan->rts) cx_set_rts (d->chan, 1); cx_send (d); } splx (s); } /* * Handle transmit timeouts. * Recover after lost transmit interrupts. * Always called on splimp(). */ static void cx_watchdog (drv_t *d) { int s = splhigh (); CX_DEBUG (d, ("device timeout\n")); if (d->running) { cx_setup_chan (d->chan); cx_start_chan (d->chan, 0, 0); cx_set_dtr (d->chan, 1); cx_set_rts (d->chan, 1); cx_start (d); } splx (s); } /* * Transmit callback function. */ static void cx_transmit (cx_chan_t *c, void *attachment, int len) { drv_t *d = c->sys; if (!d) return; if (c->mode == M_ASYNC && d->tty) { d->tty->t_state &= ~(TS_BUSY | TS_FLUSH); d->atimeout = 0; if (d->tty->t_dev) { d->intr_action |= CX_WRITE; MY_SOFT_INTR = 1; #if __FreeBSD_version >= 500000 swi_sched (cx_fast_ih, 0); #else setsofttty (); #endif } return; } #ifdef NETGRAPH d->timeout = 0; #else ++d->pp.pp_if.if_opackets; d->pp.pp_if.if_flags &= ~IFF_OACTIVE; d->pp.pp_if.if_timer = 0; #endif cx_start (d); } /* * Process the received packet. */ static void cx_receive (cx_chan_t *c, char *data, int len) { drv_t *d = c->sys; struct mbuf *m; char *cc = data; #if __FreeBSD_version >= 500000 && defined NETGRAPH int error; #endif if (!d) return; if (c->mode == M_ASYNC && d->tty) { if (d->tty->t_state & TS_ISOPEN) { async_q *q = &d->aqueue; int size = BF_SZ - 1 - AQ_GSZ (q); if (len <= 0 && !size) return; if (len > size) { c->ierrs++; cx_error (c, CX_OVERRUN); len = size - 1; } while (len--) { AQ_PUSH (q, *(unsigned char *)cc); cc++; } d->intr_action |= CX_READ; MY_SOFT_INTR = 1; #if __FreeBSD_version >= 500000 swi_sched (cx_fast_ih, 0); #else setsofttty (); #endif } return; } if (! d->running) return; m = makembuf (data, len); if (! m) { CX_DEBUG (d, ("no memory for packet\n")); #ifndef NETGRAPH ++d->pp.pp_if.if_iqdrops; #endif return; } if (c->debug > 1) printmbuf (m); #ifdef NETGRAPH m->m_pkthdr.rcvif = 0; #if __FreeBSD_version >= 500000 NG_SEND_DATA_ONLY (error, d->hook, m); #else ng_queue_data (d->hook, m, 0); #endif #else ++d->pp.pp_if.if_ipackets; m->m_pkthdr.rcvif = &d->pp.pp_if; /* Check if there's a BPF listener on this interface. * If so, hand off the raw packet to bpf. */ if (d->pp.pp_if.if_bpf) #if __FreeBSD_version >= 500000 BPF_TAP (&d->pp.pp_if, data, len); #else bpf_tap (&d->pp.pp_if, data, len); #endif sppp_input (&d->pp.pp_if, m); #endif } #if __FreeBSD_version < 502113 #define CONDITION(t,tp) (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))\ && (!(tp->t_iflag & BRKINT) || (tp->t_iflag & IGNBRK))\ && (!(tp->t_iflag & PARMRK)\ || (tp->t_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))\ && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))\ && linesw[tp->t_line].l_rint == ttyinput) #else #define CONDITION(t,tp) (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))\ && (!(tp->t_iflag & BRKINT) || (tp->t_iflag & IGNBRK))\ && (!(tp->t_iflag & PARMRK)\ || (tp->t_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))\ && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))\ && linesw[tp->t_line]->l_rint == ttyinput) #endif /* * Error callback function. */ static void cx_error (cx_chan_t *c, int data) { drv_t *d = c->sys; async_q *q; if (!d) return; q = &(d->aqueue); switch (data) { case CX_FRAME: CX_DEBUG (d, ("frame error\n")); if (c->mode == M_ASYNC && d->tty && (d->tty->t_state & TS_ISOPEN) && (AQ_GSZ (q) < BF_SZ - 1) && (!CONDITION((&d->tty->t_termios), (d->tty)) || !(d->tty->t_iflag & (IGNPAR | PARMRK)))) { AQ_PUSH (q, TTY_FE); d->intr_action |= CX_READ; MY_SOFT_INTR = 1; #if __FreeBSD_version >= 500000 swi_sched (cx_fast_ih, 0); #else setsofttty (); #endif } #ifndef NETGRAPH else ++d->pp.pp_if.if_ierrors; #endif break; case CX_CRC: CX_DEBUG (d, ("crc error\n")); if (c->mode == M_ASYNC && d->tty && (d->tty->t_state & TS_ISOPEN) && (AQ_GSZ (q) < BF_SZ - 1) && (!CONDITION((&d->tty->t_termios), (d->tty)) || !(d->tty->t_iflag & INPCK) || !(d->tty->t_iflag & (IGNPAR | PARMRK)))) { AQ_PUSH (q, TTY_PE); d->intr_action |= CX_READ; MY_SOFT_INTR = 1; #if __FreeBSD_version >= 500000 swi_sched (cx_fast_ih, 0); #else setsofttty (); #endif } #ifndef NETGRAPH else ++d->pp.pp_if.if_ierrors; #endif break; case CX_OVERRUN: CX_DEBUG (d, ("overrun error\n")); #ifdef TTY_OE if (c->mode == M_ASYNC && d->tty && (d->tty->t_state & TS_ISOPEN) && (AQ_GSZ (q) < BF_SZ - 1) && (!CONDITION((&d->tty->t_termios), (d->tty)))) { AQ_PUSH (q, TTY_OE); d->intr_action |= CX_READ; MY_SOFT_INTR = 1; #if __FreeBSD_version >= 500000 swi_sched (cx_fast_ih, 0); #else setsofttty (); #endif } #endif #ifndef NETGRAPH else { ++d->pp.pp_if.if_collisions; ++d->pp.pp_if.if_ierrors; } #endif break; case CX_OVERFLOW: CX_DEBUG (d, ("overflow error\n")); #ifndef NETGRAPH if (c->mode != M_ASYNC) ++d->pp.pp_if.if_ierrors; #endif break; case CX_UNDERRUN: CX_DEBUG (d, ("underrun error\n")); if (c->mode != M_ASYNC) { #ifdef NETGRAPH d->timeout = 0; #else ++d->pp.pp_if.if_oerrors; d->pp.pp_if.if_flags &= ~IFF_OACTIVE; d->pp.pp_if.if_timer = 0; cx_start (d); #endif } break; case CX_BREAK: CX_DEBUG (d, ("break error\n")); if (c->mode == M_ASYNC && d->tty && (d->tty->t_state & TS_ISOPEN) && (AQ_GSZ (q) < BF_SZ - 1) && (!CONDITION((&d->tty->t_termios), (d->tty)) || !(d->tty->t_iflag & (IGNBRK | BRKINT | PARMRK)))) { AQ_PUSH (q, TTY_BI); d->intr_action |= CX_READ; MY_SOFT_INTR = 1; #if __FreeBSD_version >= 500000 swi_sched (cx_fast_ih, 0); #else setsofttty (); #endif } #ifndef NETGRAPH else ++d->pp.pp_if.if_ierrors; #endif break; default: CX_DEBUG (d, ("error #%d\n", data)); } } #if __FreeBSD_version < 500000 static int cx_open (dev_t dev, int flag, int mode, struct proc *p) #else static int cx_open (struct cdev *dev, int flag, int mode, struct thread *td) #endif { int unit = UNIT (dev); drv_t *d; int error; if (unit >= NCX*NCHAN || ! (d = channel[unit])) return ENXIO; CX_DEBUG2 (d, ("cx_open unit=%d, flag=0x%x, mode=0x%x\n", unit, flag, mode)); if (d->chan->mode != M_ASYNC || IF_CUNIT(dev)) { d->open_dev |= 0x1; return 0; } if (!d->tty) { d->tty = ttymalloc (d->tty); d->tty->t_oproc = cx_oproc; d->tty->t_param = cx_param; d->tty->t_stop = cx_stop; } dev->si_tty = d->tty; d->tty->t_dev = dev; again: if (d->dtroff) { error = tsleep (&d->dtrwait, TTIPRI | PCATCH, "cxdtr", 0); if (error) return error; goto again; } if ((d->tty->t_state & TS_ISOPEN) && (d->tty->t_state & TS_XCLUDE) && #if __FreeBSD_version >= 500000 suser (td)) #else p->p_ucred->cr_uid != 0) #endif return EBUSY; if (d->tty->t_state & TS_ISOPEN) { /* * Cannot open /dev/cua if /dev/tty already opened. */ if (CALLOUT (dev) && ! d->callout) return EBUSY; /* * Opening /dev/tty when /dev/cua is already opened. * Wait for close, then try again. */ if (! CALLOUT (dev) && d->callout) { if (flag & O_NONBLOCK) return EBUSY; error = tsleep (d, TTIPRI | PCATCH, "cxbi", 0); if (error) return error; goto again; } } else if (d->lock && ! CALLOUT (dev) && (flag & O_NONBLOCK)) /* * We try to open /dev/tty in non-blocking mode * while somebody is already waiting for carrier on it. */ return EBUSY; else { ttychars (d->tty); if (d->tty->t_ispeed == 0) { d->tty->t_iflag = 0; d->tty->t_oflag = 0; d->tty->t_lflag = 0; d->tty->t_cflag = CREAD | CS8 | HUPCL; d->tty->t_ispeed = d->chan->rxbaud; d->tty->t_ospeed = d->chan->txbaud; } if (CALLOUT (dev)) d->tty->t_cflag |= CLOCAL; else d->tty->t_cflag &= ~CLOCAL; cx_param (d->tty, &d->tty->t_termios); ttsetwater (d->tty); } splhigh (); if (! (d->tty->t_state & TS_ISOPEN)) { cx_start_chan (d->chan, 0, 0); cx_set_dtr (d->chan, 1); cx_set_rts (d->chan, 1); d->cd = cx_get_cd (d->chan); if (CALLOUT (dev) || cx_get_cd (d->chan)) ttyld_modem(d->tty, 1); } if (! (flag & O_NONBLOCK) && ! (d->tty->t_cflag & CLOCAL) && ! (d->tty->t_state & TS_CARR_ON)) { /* Lock the channel against cxconfig while we are * waiting for carrier. */ d->lock++; error = tsleep (&d->tty->t_rawq, TTIPRI | PCATCH, "cxdcd", 0); /* Unlock the channel. */ d->lock--; spl0 (); if (error) goto failed; goto again; } error = ttyld_open (d->tty, dev); ttyldoptim (d->tty); spl0 (); if (error) { failed: if (! (d->tty->t_state & TS_ISOPEN)) { splhigh (); cx_set_dtr (d->chan, 0); cx_set_rts (d->chan, 0); if (d->dtrwait) { d->dtr_timeout_handle = timeout (cx_dtrwakeup, d, d->dtrwait); d->dtroff = 1; } spl0 (); } return error; } if (d->tty->t_state & TS_ISOPEN) d->callout = CALLOUT (dev) ? 1 : 0; d->open_dev |= 0x2; CX_DEBUG2 (d, ("cx_open done\n")); return 0; } #if __FreeBSD_version < 500000 static int cx_close (dev_t dev, int flag, int mode, struct proc *p) #else static int cx_close (struct cdev *dev, int flag, int mode, struct thread *td) #endif { drv_t *d = channel [UNIT (dev)]; int s; CX_DEBUG2 (d, ("cx_close\n")); if ((!(d->open_dev&0x2)) || IF_CUNIT(dev)){ d->open_dev &= ~0x1; return 0; } s = splhigh (); ttyld_close(d->tty, flag); ttyldoptim (d->tty); /* Disable receiver. * Transmitter continues sending the queued data. */ cx_enable_receive (d->chan, 0); /* Clear DTR and RTS. */ if ((d->tty->t_cflag & HUPCL) || ! (d->tty->t_state & TS_ISOPEN)) { cx_set_dtr (d->chan, 0); cx_set_rts (d->chan, 0); if (d->dtrwait) { d->dtr_timeout_handle = timeout (cx_dtrwakeup, d, d->dtrwait); d->dtroff = 1; } } ttyclose (d->tty); splx (s); d->callout = 0; /* * Wake up bidirectional opens. * Since we may be opened twice we couldn't call ttyrel() here. * So just keep d->tty for future use. It would be freed by * ttyrel() at cx_detach(). */ wakeup (d); d->open_dev &= ~0x2; return 0; } static int cx_read (struct cdev *dev, struct uio *uio, int flag) { drv_t *d = channel [UNIT (dev)]; if (d) CX_DEBUG2 (d, ("cx_read\n")); if (!d || d->chan->mode != M_ASYNC || IF_CUNIT(dev) || !d->tty) return EBADF; return ttyld_read (d->tty, uio, flag); } static int cx_write (struct cdev *dev, struct uio *uio, int flag) { drv_t *d = channel [UNIT (dev)]; if (d) CX_DEBUG2 (d, ("cx_write\n")); if (!d || d->chan->mode != M_ASYNC || IF_CUNIT(dev) || !d->tty) return EBADF; return ttyld_write (d->tty, uio, flag); } static int cx_modem_status (drv_t *d) { int status = 0, s = splhigh (); /* Already opened by someone or network interface is up? */ if ((d->chan->mode == M_ASYNC && d->tty && (d->tty->t_state & TS_ISOPEN) && (d->open_dev|0x2)) || (d->chan->mode != M_ASYNC && d->running)) status = TIOCM_LE; /* always enabled while open */ if (cx_get_dsr (d->chan)) status |= TIOCM_DSR; if (cx_get_cd (d->chan)) status |= TIOCM_CD; if (cx_get_cts (d->chan)) status |= TIOCM_CTS; if (d->chan->dtr) status |= TIOCM_DTR; if (d->chan->rts) status |= TIOCM_RTS; splx (s); return status; } #if __FreeBSD_version < 500000 static int cx_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) #else static int cx_ioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) #endif { drv_t *d = channel [UNIT (dev)]; cx_chan_t *c; struct serial_statistics *st; int error, s; char mask[16]; if (!d || !(c = d->chan)) return EINVAL; switch (cmd) { case SERIAL_GETREGISTERED: CX_DEBUG2 (d, ("ioctl: getregistered\n")); bzero (mask, sizeof(mask)); for (s=0; s= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; s = splhigh (); cx_set_port (c, *(int *)data); splx (s); return 0; #ifndef NETGRAPH case SERIAL_GETPROTO: CX_DEBUG2 (d, ("ioctl: getproto\n")); s = splhigh (); strcpy ((char*)data, (c->mode == M_ASYNC) ? "async" : (d->pp.pp_flags & PP_FR) ? "fr" : (d->pp.pp_if.if_flags & PP_CISCO) ? "cisco" : "ppp"); splx (s); return 0; case SERIAL_SETPROTO: CX_DEBUG2 (d, ("ioctl: setproto\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if (c->mode == M_ASYNC) return EBUSY; if (d->pp.pp_if.if_flags & IFF_RUNNING) return EBUSY; if (! strcmp ("cisco", (char*)data)) { d->pp.pp_flags &= ~(PP_FR); d->pp.pp_flags |= PP_KEEPALIVE; d->pp.pp_if.if_flags |= PP_CISCO; } else if (! strcmp ("fr", (char*)data)) { d->pp.pp_if.if_flags &= ~(PP_CISCO); d->pp.pp_flags |= PP_FR | PP_KEEPALIVE; } else if (! strcmp ("ppp", (char*)data)) { d->pp.pp_flags &= ~(PP_FR | PP_KEEPALIVE); d->pp.pp_if.if_flags &= ~(PP_CISCO); } else return EINVAL; return 0; case SERIAL_GETKEEPALIVE: CX_DEBUG2 (d, ("ioctl: getkeepalive\n")); if ((d->pp.pp_flags & PP_FR) || (d->pp.pp_if.if_flags & PP_CISCO) || (c->mode == M_ASYNC)) return EINVAL; s = splhigh (); *(int*)data = (d->pp.pp_flags & PP_KEEPALIVE) ? 1 : 0; splx (s); return 0; case SERIAL_SETKEEPALIVE: CX_DEBUG2 (d, ("ioctl: setkeepalive\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if ((d->pp.pp_flags & PP_FR) || (d->pp.pp_if.if_flags & PP_CISCO)) return EINVAL; s = splhigh (); if (*(int*)data) d->pp.pp_flags |= PP_KEEPALIVE; else d->pp.pp_flags &= ~PP_KEEPALIVE; splx (s); return 0; #endif /*NETGRAPH*/ case SERIAL_GETMODE: CX_DEBUG2 (d, ("ioctl: getmode\n")); s = splhigh (); *(int*)data = (c->mode == M_ASYNC) ? SERIAL_ASYNC : SERIAL_HDLC; splx (s); return 0; case SERIAL_SETMODE: CX_DEBUG2 (d, ("ioctl: setmode\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; /* Somebody is waiting for carrier? */ if (d->lock) return EBUSY; /* /dev/ttyXX is already opened by someone? */ if (c->mode == M_ASYNC && d->tty && (d->tty->t_state & TS_ISOPEN) && (d->open_dev|0x2)) return EBUSY; /* Network interface is up? * Cannot change to async mode. */ if (c->mode != M_ASYNC && d->running && (*(int*)data == SERIAL_ASYNC)) return EBUSY; s = splhigh (); if (c->mode == M_HDLC && *(int*)data == SERIAL_ASYNC) { cx_set_mode (c, M_ASYNC); cx_enable_receive (c, 0); cx_enable_transmit (c, 0); } else if (c->mode == M_ASYNC && *(int*)data == SERIAL_HDLC) { cx_set_mode (c, M_HDLC); cx_enable_receive (c, 1); cx_enable_transmit (c, 1); } splx (s); return 0; case SERIAL_GETSTAT: CX_DEBUG2 (d, ("ioctl: getestat\n")); st = (struct serial_statistics*) data; s = splhigh (); st->rintr = c->rintr; st->tintr = c->tintr; st->mintr = c->mintr; st->ibytes = c->ibytes; st->ipkts = c->ipkts; st->ierrs = c->ierrs; st->obytes = c->obytes; st->opkts = c->opkts; st->oerrs = c->oerrs; splx (s); return 0; case SERIAL_CLRSTAT: CX_DEBUG2 (d, ("ioctl: clrstat\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; s = splhigh (); c->rintr = 0; c->tintr = 0; c->mintr = 0; c->ibytes = 0; c->ipkts = 0; c->ierrs = 0; c->obytes = 0; c->opkts = 0; c->oerrs = 0; splx (s); return 0; case SERIAL_GETBAUD: CX_DEBUG2 (d, ("ioctl: getbaud\n")); if (c->mode == M_ASYNC) return EINVAL; s = splhigh (); *(long*)data = cx_get_baud(c); splx (s); return 0; case SERIAL_SETBAUD: CX_DEBUG2 (d, ("ioctl: setbaud\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if (c->mode == M_ASYNC) return EINVAL; s = splhigh (); cx_set_baud (c, *(long*)data); splx (s); return 0; case SERIAL_GETLOOP: CX_DEBUG2 (d, ("ioctl: getloop\n")); if (c->mode == M_ASYNC) return EINVAL; s = splhigh (); *(int*)data = cx_get_loop (c); splx (s); return 0; case SERIAL_SETLOOP: CX_DEBUG2 (d, ("ioctl: setloop\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if (c->mode == M_ASYNC) return EINVAL; s = splhigh (); cx_set_loop (c, *(int*)data); splx (s); return 0; case SERIAL_GETDPLL: CX_DEBUG2 (d, ("ioctl: getdpll\n")); if (c->mode == M_ASYNC) return EINVAL; s = splhigh (); *(int*)data = cx_get_dpll (c); splx (s); return 0; case SERIAL_SETDPLL: CX_DEBUG2 (d, ("ioctl: setdpll\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if (c->mode == M_ASYNC) return EINVAL; s = splhigh (); cx_set_dpll (c, *(int*)data); splx (s); return 0; case SERIAL_GETNRZI: CX_DEBUG2 (d, ("ioctl: getnrzi\n")); if (c->mode == M_ASYNC) return EINVAL; s = splhigh (); *(int*)data = cx_get_nrzi (c); splx (s); return 0; case SERIAL_SETNRZI: CX_DEBUG2 (d, ("ioctl: setnrzi\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; if (c->mode == M_ASYNC) return EINVAL; s = splhigh (); cx_set_nrzi (c, *(int*)data); splx (s); return 0; case SERIAL_GETDEBUG: CX_DEBUG2 (d, ("ioctl: getdebug\n")); s = splhigh (); *(int*)data = c->debug; splx (s); return 0; case SERIAL_SETDEBUG: CX_DEBUG2 (d, ("ioctl: setdebug\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; s = splhigh (); c->debug = *(int*)data; splx (s); #ifndef NETGRAPH if (d->chan->debug) d->pp.pp_if.if_flags |= IFF_DEBUG; else d->pp.pp_if.if_flags &= (~IFF_DEBUG); #endif return 0; } if (c->mode == M_ASYNC && !IF_CUNIT(dev) && d->tty) { #if __FreeBSD_version >= 502113 error = ttyioctl (dev, cmd, data, flag, td); ttyldoptim (d->tty); if (error != ENOTTY) { if (error) CX_DEBUG2 (d, ("ttioctl: 0x%lx, error %d\n", cmd, error)); return error; } #else #if __FreeBSD_version >= 500000 error = (*linesw[d->tty->t_line].l_ioctl) (d->tty, cmd, data, flag, td); #else error = (*linesw[d->tty->t_line].l_ioctl) (d->tty, cmd, data, flag, p); #endif ttyldoptim (d->tty); if (error != ENOIOCTL) { if (error) CX_DEBUG2 (d, ("l_ioctl: 0x%lx, error %d\n", cmd, error)); return error; } error = ttioctl (d->tty, cmd, data, flag); ttyldoptim (d->tty); if (error != ENOIOCTL) { if (error) CX_DEBUG2 (d, ("ttioctl: 0x%lx, error %d\n", cmd, error)); return error; } #endif } switch (cmd) { case TIOCSBRK: /* Start sending line break */ CX_DEBUG2 (d, ("ioctl: tiocsbrk\n")); s = splhigh (); cx_send_break (c, 500); splx (s); return 0; case TIOCCBRK: /* Stop sending line break */ CX_DEBUG2 (d, ("ioctl: tioccbrk\n")); return 0; case TIOCSDTR: /* Set DTR */ CX_DEBUG2 (d, ("ioctl: tiocsdtr\n")); s = splhigh (); cx_set_dtr (c, 1); splx (s); return 0; case TIOCCDTR: /* Clear DTR */ CX_DEBUG2 (d, ("ioctl: tioccdtr\n")); s = splhigh (); cx_set_dtr (c, 0); splx (s); return 0; case TIOCMSET: /* Set DTR/RTS */ CX_DEBUG2 (d, ("ioctl: tiocmset\n")); s = splhigh (); cx_set_dtr (c, (*(int*)data & TIOCM_DTR) ? 1 : 0); cx_set_rts (c, (*(int*)data & TIOCM_RTS) ? 1 : 0); splx (s); return 0; case TIOCMBIS: /* Add DTR/RTS */ CX_DEBUG2 (d, ("ioctl: tiocmbis\n")); s = splhigh (); if (*(int*)data & TIOCM_DTR) cx_set_dtr (c, 1); if (*(int*)data & TIOCM_RTS) cx_set_rts (c, 1); splx (s); return 0; case TIOCMBIC: /* Clear DTR/RTS */ CX_DEBUG2 (d, ("ioctl: tiocmbic\n")); s = splhigh (); if (*(int*)data & TIOCM_DTR) cx_set_dtr (c, 0); if (*(int*)data & TIOCM_RTS) cx_set_rts (c, 0); splx (s); return 0; case TIOCMGET: /* Get modem status */ CX_DEBUG2 (d, ("ioctl: tiocmget\n")); *(int*)data = cx_modem_status (d); return 0; #ifdef TIOCMSDTRWAIT case TIOCMSDTRWAIT: CX_DEBUG2 (d, ("ioctl: tiocmsdtrwait\n")); /* Only for superuser! */ #if __FreeBSD_version < 500000 error = suser (p); #else /* __FreeBSD_version >= 500000 */ error = suser (td); #endif /* __FreeBSD_version >= 500000 */ if (error) return error; s = splhigh (); d->dtrwait = *(int*)data * hz / 100; splx (s); return 0; #endif #ifdef TIOCMGDTRWAIT case TIOCMGDTRWAIT: CX_DEBUG2 (d, ("ioctl: tiocmgdtrwait\n")); s = splhigh (); *(int*)data = d->dtrwait * 100 / hz; splx (s); return 0; #endif } CX_DEBUG2 (d, ("ioctl: 0x%lx\n", cmd)); return ENOTTY; } /* * Wake up opens() waiting for DTR ready. */ static void cx_dtrwakeup (void *arg) { drv_t *d = arg; d->dtroff = 0; wakeup (&d->dtrwait); } #if __FreeBSD_version < 502113 static void ttyldoptim(tp) struct tty *tp; { struct termios *t; t = &tp->t_termios; if (CONDITION(t,tp)) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; } #endif #if __FreeBSD_version >= 500000 void cx_softintr (void *unused) #else void cx_softintr () #endif { drv_t *d; async_q *q; int i, s, ic, k; while (MY_SOFT_INTR) { MY_SOFT_INTR = 0; for (i=0; ichan || d->chan->type == T_NONE || d->chan->mode != M_ASYNC || !d->tty || !d->tty->t_dev) continue; s = splhigh (); if (d->intr_action & CX_READ) { q = &(d->aqueue); if (d->tty->t_state & TS_CAN_BYPASS_L_RINT) { k = AQ_GSZ(q); if (d->tty->t_rawq.c_cc + k > d->tty->t_ihiwat && (d->tty->t_cflag & CRTS_IFLOW || d->tty->t_iflag & IXOFF) && !(d->tty->t_state & TS_TBLOCK)) ttyblock(d->tty); d->tty->t_rawcc += k; while (k>0) { k--; AQ_POP (q, ic); splx (s); putc (ic, &d->tty->t_rawq); s = splhigh (); } ttwakeup(d->tty); if (d->tty->t_state & TS_TTSTOP && (d->tty->t_iflag & IXANY || d->tty->t_cc[VSTART] == d->tty->t_cc[VSTOP])) { d->tty->t_state &= ~TS_TTSTOP; d->tty->t_lflag &= ~FLUSHO; d->intr_action |= CX_WRITE; } } else { while (q->end != q->beg) { AQ_POP (q, ic); splx (s); ttyld_rint (d->tty, ic); s = splhigh (); } } d->intr_action &= ~CX_READ; } splx (s); s = splhigh (); if (d->intr_action & CX_WRITE) { if (d->tty->t_line) ttyld_start (d->tty); else cx_oproc (d->tty); d->intr_action &= ~CX_WRITE; } splx (s); } } } /* * Fill transmitter buffer with data. */ static void cx_oproc (struct tty *tp) { int s = splhigh (), k; drv_t *d = channel [UNIT (tp->t_dev)]; static u_char buf[DMABUFSZ]; u_char *p; u_short len = 0, sublen = 0; if (!d) { splx (s); return; } CX_DEBUG2 (d, ("cx_oproc\n")); if (tp->t_cflag & CRTSCTS && (tp->t_state & TS_TBLOCK) && d->chan->rts) cx_set_rts (d->chan, 0); else if (tp->t_cflag & CRTSCTS && ! (tp->t_state & TS_TBLOCK) && ! d->chan->rts) cx_set_rts (d->chan, 1); if (! (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))) { /* Start transmitter. */ cx_enable_transmit (d->chan, 1); /* Is it busy? */ if (! cx_buf_free (d->chan)) { tp->t_state |= TS_BUSY; splx (s); return; } if (tp->t_iflag & IXOFF) { p = (buf + (DMABUFSZ/2)); sublen = q_to_b (&tp->t_outq, p, (DMABUFSZ/2)); k = sublen; while (k--) { /* Send XON/XOFF out of band. */ if (*p == tp->t_cc[VSTOP]) { cx_xflow_ctl (d->chan, 0); p++; continue; } if (*p == tp->t_cc[VSTART]) { cx_xflow_ctl (d->chan, 1); p++; continue; } buf[len] = *p; len++; p++; } } else { p = buf; len = q_to_b (&tp->t_outq, p, (DMABUFSZ/2)); } if (len) { cx_send_packet (d->chan, buf, len, 0); tp->t_state |= TS_BUSY; d->atimeout = 10; CX_DEBUG2 (d, ("out %d bytes\n", len)); } } ttwwakeup (tp); splx (s); } static int cx_param (struct tty *tp, struct termios *t) { drv_t *d = channel [UNIT (tp->t_dev)]; int s, bits, parity; if (!d) return EINVAL; s = splhigh (); if (t->c_ospeed == 0) { /* Clear DTR and RTS. */ cx_set_dtr (d->chan, 0); splx (s); CX_DEBUG2 (d, ("cx_param (hangup)\n")); return 0; } CX_DEBUG2 (d, ("cx_param\n")); /* Check requested parameters. */ if (t->c_ospeed < 300 || t->c_ospeed > 256*1024) { splx (s); return EINVAL; } if (t->c_ispeed && (t->c_ispeed < 300 || t->c_ispeed > 256*1024)) { splx (s); return EINVAL; } /* And copy them to tty and channel structures. */ tp->t_ispeed = t->c_ispeed = tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; /* Set character length and parity mode. */ switch (t->c_cflag & CSIZE) { default: case CS8: bits = 8; break; case CS7: bits = 7; break; case CS6: bits = 6; break; case CS5: bits = 5; break; } parity = ((t->c_cflag & PARENB) ? 1 : 0) * (1 + ((t->c_cflag & PARODD) ? 0 : 1)); /* Set current channel number. */ if (! d->chan->dtr) cx_set_dtr (d->chan, 1); ttyldoptim (tp); cx_set_async_param (d->chan, t->c_ospeed, bits, parity, (t->c_cflag & CSTOPB), !(t->c_cflag & PARENB), (t->c_cflag & CRTSCTS), (t->c_iflag & IXON), (t->c_iflag & IXANY), t->c_cc[VSTART], t->c_cc[VSTOP]); splx (s); return 0; } /* * Stop output on a line */ static void cx_stop (struct tty *tp, int flag) { drv_t *d = channel [UNIT (tp->t_dev)]; int s; if (!d) return; s = splhigh (); if (tp->t_state & TS_BUSY) { /* Stop transmitter */ CX_DEBUG2 (d, ("cx_stop\n")); cx_transmitter_ctl (d->chan, 0); } splx (s); } /* * Process the (delayed) carrier signal setup. */ static void cx_carrier (void *arg) { drv_t *d = arg; cx_chan_t *c = d->chan; int s, cd; s = splhigh (); cd = cx_get_cd (c); if (d->cd != cd) { if (cd) { CX_DEBUG (d, ("carrier on\n")); d->cd = 1; splx (s); if (d->tty) ttyld_modem(d->tty, 1); } else { CX_DEBUG (d, ("carrier loss\n")); d->cd = 0; splx (s); if (d->tty) ttyld_modem(d->tty, 0); } } } /* * Modem signal callback function. */ static void cx_modem (cx_chan_t *c) { drv_t *d = c->sys; if (!d || c->mode != M_ASYNC) return; /* Handle carrier detect/loss. */ untimeout (cx_carrier, c, d->dcd_timeout_handle); /* Carrier changed - delay processing DCD for a while * to give both sides some time to initialize. */ d->dcd_timeout_handle = timeout (cx_carrier, d, hz/2); } #if __FreeBSD_version < 500000 static struct cdevsw cx_cdevsw = { cx_open, cx_close, cx_read, cx_write, cx_ioctl, ttypoll, nommap, nostrategy, "cx", CDEV_MAJOR, nodump, nopsize, D_TTY, -1 }; #elif __FreeBSD_version == 500000 static struct cdevsw cx_cdevsw = { cx_open, cx_close, cx_read, cx_write, cx_ioctl, ttypoll, nommap, nostrategy, "cx", CDEV_MAJOR, nodump, nopsize, D_TTY, }; #elif __FreeBSD_version <= 501000 static struct cdevsw cx_cdevsw = { .d_open = cx_open, .d_close = cx_close, .d_read = cx_read, .d_write = cx_write, .d_ioctl = cx_ioctl, .d_poll = ttypoll, .d_mmap = nommap, .d_strategy = nostrategy, .d_name = "cx", .d_maj = CDEV_MAJOR, .d_dump = nodump, .d_flags = D_TTY, }; #elif __FreeBSD_version < 502103 static struct cdevsw cx_cdevsw = { .d_open = cx_open, .d_close = cx_close, .d_read = cx_read, .d_write = cx_write, .d_ioctl = cx_ioctl, .d_poll = ttypoll, .d_name = "cx", .d_maj = CDEV_MAJOR, .d_flags = D_TTY, }; #else /* __FreeBSD_version >= 502103 */ static struct cdevsw cx_cdevsw = { .d_version = D_VERSION, .d_open = cx_open, .d_close = cx_close, .d_read = cx_read, .d_write = cx_write, .d_ioctl = cx_ioctl, .d_name = "cx", .d_maj = CDEV_MAJOR, .d_flags = D_TTY | D_NEEDGIANT, }; #endif #ifdef NETGRAPH #if __FreeBSD_version >= 500000 static int ng_cx_constructor (node_p node) { drv_t *d = NG_NODE_PRIVATE (node); #else static int ng_cx_constructor (node_p *node) { drv_t *d = (*node)->private; #endif CX_DEBUG (d, ("Constructor\n")); return EINVAL; } static int ng_cx_newhook (node_p node, hook_p hook, const char *name) { int s; #if __FreeBSD_version >= 500000 drv_t *d = NG_NODE_PRIVATE (node); #else drv_t *d = node->private; #endif if (d->chan->mode == M_ASYNC) return EINVAL; /* Attach debug hook */ if (strcmp (name, NG_CX_HOOK_DEBUG) == 0) { #if __FreeBSD_version >= 500000 NG_HOOK_SET_PRIVATE (hook, NULL); #else hook->private = 0; #endif d->debug_hook = hook; return 0; } /* Check for raw hook */ if (strcmp (name, NG_CX_HOOK_RAW) != 0) return EINVAL; #if __FreeBSD_version >= 500000 NG_HOOK_SET_PRIVATE (hook, d); #else hook->private = d; #endif d->hook = hook; s = splhigh (); cx_up (d); splx (s); return 0; } static int print_modems (char *s, cx_chan_t *c, int need_header) { int status = cx_modem_status (c->sys); int length = 0; if (need_header) length += sprintf (s + length, " LE DTR DSR RTS CTS CD\n"); length += sprintf (s + length, "%4s %4s %4s %4s %4s %4s\n", status & TIOCM_LE ? "On" : "-", status & TIOCM_DTR ? "On" : "-", status & TIOCM_DSR ? "On" : "-", status & TIOCM_RTS ? "On" : "-", status & TIOCM_CTS ? "On" : "-", status & TIOCM_CD ? "On" : "-"); return length; } static int print_stats (char *s, cx_chan_t *c, int need_header) { int length = 0; if (need_header) length += sprintf (s + length, " Rintr Tintr Mintr Ibytes Ipkts Ierrs Obytes Opkts Oerrs\n"); length += sprintf (s + length, "%7ld %7ld %7ld %8ld %7ld %7ld %8ld %7ld %7ld\n", c->rintr, c->tintr, c->mintr, c->ibytes, c->ipkts, c->ierrs, c->obytes, c->opkts, c->oerrs); return length; } static int print_chan (char *s, cx_chan_t *c) { drv_t *d = c->sys; int length = 0; length += sprintf (s + length, "cx%d", c->board->num * NCHAN + c->num); if (d->chan->debug) length += sprintf (s + length, " debug=%d", d->chan->debug); if (cx_get_baud (c)) length += sprintf (s + length, " %ld", cx_get_baud (c)); else length += sprintf (s + length, " extclock"); if (c->mode == M_HDLC) { length += sprintf (s + length, " dpll=%s", cx_get_dpll (c) ? "on" : "off"); length += sprintf (s + length, " nrzi=%s", cx_get_nrzi (c) ? "on" : "off"); } length += sprintf (s + length, " loop=%s", cx_get_loop (c) ? "on\n" : "off\n"); return length; } #if __FreeBSD_version >= 500000 static int ng_cx_rcvmsg (node_p node, item_p item, hook_p lasthook) { drv_t *d = NG_NODE_PRIVATE (node); struct ng_mesg *msg; #else static int ng_cx_rcvmsg (node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) { drv_t *d = node->private; #endif struct ng_mesg *resp = NULL; int error = 0; if (!d) return EINVAL; CX_DEBUG (d, ("Rcvmsg\n")); #if __FreeBSD_version >= 500000 NGI_GET_MSG (item, msg); #endif switch (msg->header.typecookie) { default: error = EINVAL; break; case NGM_CX_COOKIE: printf ("Don't forget to implement\n"); error = EINVAL; break; case NGM_GENERIC_COOKIE: switch (msg->header.cmd) { default: error = EINVAL; break; case NGM_TEXT_STATUS: { char *s; int l = 0; int dl = sizeof (struct ng_mesg) + 730; #if __FreeBSD_version >= 500000 NG_MKRESPONSE (resp, msg, dl, M_NOWAIT); if (! resp) { error = ENOMEM; break; } #else MALLOC (resp, struct ng_mesg *, dl, M_NETGRAPH, M_NOWAIT); if (! resp) { error = ENOMEM; break; } #endif bzero (resp, dl); s = (resp)->data; l += print_chan (s + l, d->chan); l += print_stats (s + l, d->chan, 1); l += print_modems (s + l, d->chan, 1); #if __FreeBSD_version < 500000 (resp)->header.version = NG_VERSION; (resp)->header.arglen = strlen (s) + 1; (resp)->header.token = msg->header.token; (resp)->header.typecookie = NGM_CX_COOKIE; (resp)->header.cmd = msg->header.cmd; #endif strncpy ((resp)->header.cmdstr, "status", NG_CMDSTRLEN); } break; } break; } #if __FreeBSD_version >= 500000 NG_RESPOND_MSG (error, node, item, resp); NG_FREE_MSG (msg); #else *rptr = resp; FREE (msg, M_NETGRAPH); #endif return error; } #if __FreeBSD_version >= 500000 static int ng_cx_rcvdata (hook_p hook, item_p item) { drv_t *d = NG_NODE_PRIVATE (NG_HOOK_NODE(hook)); struct mbuf *m; - meta_p meta; + struct ng_tag_prio *ptag; #else static int ng_cx_rcvdata (hook_p hook, struct mbuf *m, meta_p meta) { drv_t *d = hook->node->private; #endif struct ifqueue *q; int s; #if __FreeBSD_version >= 500000 NGI_GET_M (item, m); - NGI_GET_META (item, meta); NG_FREE_ITEM (item); if (! NG_HOOK_PRIVATE (hook) || ! d) { NG_FREE_M (m); - NG_FREE_META (meta); #else if (! hook->private || ! d) { NG_FREE_DATA (m,meta); #endif return ENETDOWN; } - q = (meta && meta->priority > 0) ? &d->hi_queue : &d->lo_queue; + + /* Check for high priority data */ + if ((ptag = (struct ng_tag_prio *)m_tag_locate(m, NGM_GENERIC_COOKIE, + NG_TAG_PRIO, NULL)) != NULL && (ptag->priority > NG_PRIO_CUTOFF) ) + q = &d->hi_queue; + else + q = &d->lo_queue; + s = splhigh (); #if __FreeBSD_version >= 500000 IF_LOCK (q); if (_IF_QFULL (q)) { _IF_DROP (q); IF_UNLOCK (q); splx (s); NG_FREE_M (m); - NG_FREE_META (meta); return ENOBUFS; } _IF_ENQUEUE (q, m); IF_UNLOCK (q); #else if (IF_QFULL (q)) { IF_DROP (q); splx (s); NG_FREE_DATA (m, meta); return ENOBUFS; } IF_ENQUEUE (q, m); #endif cx_start (d); splx (s); return 0; } static int ng_cx_rmnode (node_p node) { #if __FreeBSD_version >= 500000 drv_t *d = NG_NODE_PRIVATE (node); CX_DEBUG (d, ("Rmnode\n")); if (d && d->running) { int s = splhigh (); cx_down (d); splx (s); } #ifdef KLD_MODULE if (node->nd_flags & NG_REALLY_DIE) { NG_NODE_SET_PRIVATE (node, NULL); NG_NODE_UNREF (node); } node->nd_flags &= ~NG_INVALID; #endif #else /* __FreeBSD_version < 500000 */ drv_t *d = node->private; int s; s = splhigh (); cx_down (d); splx (s); node->flags |= NG_INVALID; ng_cutlinks (node); #ifdef KLD_MODULE ng_unname (node); ng_unref (node); #else node->flags &= ~NG_INVALID; #endif #endif return 0; } static void ng_cx_watchdog (void *arg) { drv_t *d = arg; if (d->timeout == 1) cx_watchdog (d); if (d->timeout) d->timeout--; d->timeout_handle = timeout (ng_cx_watchdog, d, hz); } static int ng_cx_connect (hook_p hook) { #if __FreeBSD_version >= 500000 drv_t *d = NG_NODE_PRIVATE (NG_HOOK_NODE (hook)); #else drv_t *d = hook->node->private; #endif d->timeout_handle = timeout (ng_cx_watchdog, d, hz); return 0; } static int ng_cx_disconnect (hook_p hook) { #if __FreeBSD_version >= 500000 drv_t *d = NG_NODE_PRIVATE (NG_HOOK_NODE (hook)); #else drv_t *d = hook->node->private; #endif int s; s = splhigh (); #if __FreeBSD_version >= 500000 if (NG_HOOK_PRIVATE (hook)) #else if (hook->private) #endif cx_down (d); splx (s); untimeout (ng_cx_watchdog, d, d->timeout_handle); return 0; } #endif /*NETGRAPH*/ static int cx_modevent (module_t mod, int type, void *unused) { struct cdev *dev; static int load_count = 0; struct cdevsw *cdsw; #if __FreeBSD_version >= 502103 dev = findcdev (makedev(CDEV_MAJOR, 0)); #else dev = makedev (CDEV_MAJOR, 0); #endif switch (type) { case MOD_LOAD: if (dev != NULL && (cdsw = devsw (dev)) && cdsw->d_maj == CDEV_MAJOR) { printf ("Sigma driver is already in system\n"); return (EEXIST); } #if __FreeBSD_version >= 500000 && defined NETGRAPH if (ng_newtype (&typestruct)) printf ("Failed to register ng_cx\n"); #endif ++load_count; #if __FreeBSD_version <= 500000 cdevsw_add (&cx_cdevsw); #endif timeout_handle = timeout (cx_timeout, 0, hz*5); /* Software interrupt. */ #if __FreeBSD_version < 500000 register_swi (SWI_TTY, cx_softintr); #else swi_add(&tty_ithd, "tty:cx", cx_softintr, NULL, SWI_TTY, 0, &cx_fast_ih); #endif break; case MOD_UNLOAD: if (load_count == 1) { printf ("Removing device entry for Sigma\n"); #if __FreeBSD_version <= 500000 cdevsw_remove (&cx_cdevsw); #endif #if __FreeBSD_version >= 500000 && defined NETGRAPH ng_rmtype (&typestruct); #endif } if (timeout_handle.callout) untimeout (cx_timeout, 0, timeout_handle); #if __FreeBSD_version >= 500000 ithread_remove_handler (cx_fast_ih); #else unregister_swi (SWI_TTY, cx_softintr); #endif --load_count; break; case MOD_SHUTDOWN: break; } return 0; } #ifdef NETGRAPH static struct ng_type typestruct = { .version = NG_ABI_VERSION, .name = NG_CX_NODE_TYPE, .constructor = ng_cx_constructor, .rcvmsg = ng_cx_rcvmsg, .shutdown = ng_cx_rmnode, .newhook = ng_cx_newhook, .connect = ng_cx_connect, .rcvdata = ng_cx_rcvdata, .disconnect = ng_cx_disconnect, }; #endif /*NETGRAPH*/ #if __FreeBSD_version >= 500000 #ifdef NETGRAPH MODULE_DEPEND (ng_cx, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); #else MODULE_DEPEND (isa_cx, sppp, 1, 1, 1); #endif #ifdef KLD_MODULE DRIVER_MODULE (cxmod, isa, cx_isa_driver, cx_devclass, cx_modevent, NULL); #else DRIVER_MODULE (cx, isa, cx_isa_driver, cx_devclass, cx_modevent, NULL); #endif #elif __FreeBSD_version >= 400000 #ifdef NETGRAPH DRIVER_MODULE(cx, isa, cx_isa_driver, cx_devclass, ng_mod_event, &typestruct); #else DRIVER_MODULE(cx, isa, cx_isa_driver, cx_devclass, cx_modevent, 0); #endif #endif /* __FreeBSD_version >= 400000 */ #endif /* NCX */ Index: head/sys/dev/sr/if_sr.c =================================================================== --- head/sys/dev/sr/if_sr.c (revision 131107) +++ head/sys/dev/sr/if_sr.c (revision 131108) @@ -1,2963 +1,2962 @@ /* * Copyright (c) 1996 - 2001 John Hay. * Copyright (c) 1996 SDL Communications, Inc. * All rights reserved. * * 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. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * */ #include __FBSDID("$FreeBSD$"); /* * Programming assumptions and other issues. * * Only a 16K window will be used. * * The descriptors of a DMA channel will fit in a 16K memory window. * * The buffers of a transmit DMA channel will fit in a 16K memory window. * * When interface is going up, handshaking is set and it is only cleared * when the interface is down'ed. * * There should be a way to set/reset Raw HDLC/PPP, Loopback, DCE/DTE, * internal/external clock, etc..... * */ #include "opt_netgraph.h" #ifdef NETGRAPH #include #endif /* NETGRAPH */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NETGRAPH #include #else /* NETGRAPH */ #include #include #endif /* NETGRAPH */ #include #include #include #ifdef NETGRAPH #include #include #endif /* NETGRAPH */ /* #define USE_MODEMCK */ #ifndef BUGGY #define BUGGY 0 #endif #ifndef NETGRAPH #define PPP_HEADER_LEN 4 #endif /* NETGRAPH */ static int next_sc_unit = 0; #ifndef NETGRAPH #ifdef USE_MODEMCK static int sr_watcher = 0; #endif #endif /* NETGRAPH */ /* * Define the software interface for the card... There is one for * every channel (port). */ struct sr_softc { #ifndef NETGRAPH struct sppp ifsppp; /* PPP service w/in system */ #endif /* NETGRAPH */ struct sr_hardc *hc; /* card-level information */ int unit; /* With regard to all sr devices */ int subunit; /* With regard to this card */ struct buf_block { u_int txdesc; /* DPRAM offset */ u_int txstart;/* DPRAM offset */ u_int txend; /* DPRAM offset */ u_int txtail; /* # of 1st free gran */ u_int txmax; /* # of free grans */ u_int txeda; /* err descr addr */ } block[SR_TX_BLOCKS]; char xmit_busy; /* Transmitter is busy */ char txb_inuse; /* # of tx grans in use */ u_int txb_new; /* ndx to new buffer */ u_int txb_next_tx; /* ndx to next gran rdy tx */ u_int rxdesc; /* DPRAM offset */ u_int rxstart; /* DPRAM offset */ u_int rxend; /* DPRAM offset */ u_int rxhind; /* ndx to the hd of rx bufrs */ u_int rxmax; /* # of avail grans */ u_int clk_cfg; /* Clock configuration */ int scachan; /* channel # on card */ #ifdef NETGRAPH int running; /* something is attached so we are running */ int dcd; /* do we have dcd? */ /* ---netgraph bits --- */ char nodename[NG_NODESIZ]; /* store our node name */ int datahooks; /* number of data hooks attached */ node_p node; /* netgraph node */ hook_p hook; /* data hook */ hook_p debug_hook; struct ifqueue xmitq_hipri; /* hi-priority transmit queue */ struct ifqueue xmitq; /* transmit queue */ int flags; /* state */ #define SCF_RUNNING 0x01 /* board is active */ #define SCF_OACTIVE 0x02 /* output is active */ int out_dog; /* watchdog cycles output count-down */ struct callout_handle handle; /* timeout(9) handle */ u_long inbytes, outbytes; /* stats */ u_long lastinbytes, lastoutbytes; /* a second ago */ u_long inrate, outrate; /* highest rate seen */ u_long inlast; /* last input N secs ago */ u_long out_deficit; /* output since last input */ u_long oerrors, ierrors[6]; u_long opackets, ipackets; #endif /* NETGRAPH */ }; #ifdef NETGRAPH #define DOG_HOLDOFF 6 /* dog holds off for 6 secs */ #define QUITE_A_WHILE 300 /* 5 MINUTES */ #define LOTS_OF_PACKETS 100 #endif /* NETGRAPH */ /* * Baud Rate table for Sync Mode. * Each entry consists of 3 elements: * Baud Rate (x100) , TMC, BR * * Baud Rate = FCLK / TMC / 2^BR * Baud table for Crystal freq. of 9.8304 Mhz */ #ifdef N2_TEST_SPEED struct rate_line { int target; /* target rate/100 */ int tmc_reg; /* TMC register value */ int br_reg; /* BR (BaudRateClk) selector */ } n2_rates[] = { /* Baudx100 TMC BR */ { 3, 128, 8 }, { 6, 128, 7 }, { 12, 128, 6 }, { 24, 128, 5 }, { 48, 128, 4 }, { 96, 128, 3 }, { 192, 128, 2 }, { 384, 128, 1 }, { 560, 88, 1 }, { 640, 77, 1 }, { 1280, 38, 1 }, { 2560, 19, 1 }, { 5120, 10, 1 }, { 10000, 5, 1 }, { 15000, 3, 1 }, { 25000, 2, 1 }, { 50000, 1, 1 }, { 0, 0, 0 } }; int sr_test_speed[] = { N2_TEST_SPEED, N2_TEST_SPEED }; int etc0vals[] = { SR_MCR_ETC0, /* ISA channel 0 */ SR_MCR_ETC1, /* ISA channel 1 */ SR_FECR_ETC0, /* PCI channel 0 */ SR_FECR_ETC1 /* PCI channel 1 */ }; #endif devclass_t sr_devclass; #ifndef NETGRAPH MODULE_DEPEND(if_sr, sppp, 1, 1, 1); #else MODULE_DEPEND(ng_sync_sr, netgraph, 1, 1, 1); #endif static void srintr(void *arg); static void sr_xmit(struct sr_softc *sc); #ifndef NETGRAPH static void srstart(struct ifnet *ifp); static int srioctl(struct ifnet *ifp, u_long cmd, caddr_t data); static void srwatchdog(struct ifnet *ifp); #else static void srstart(struct sr_softc *sc); static void srwatchdog(struct sr_softc *sc); #endif /* NETGRAPH */ static int sr_packet_avail(struct sr_softc *sc, int *len, u_char *rxstat); static void sr_copy_rxbuf(struct mbuf *m, struct sr_softc *sc, int len); static void sr_eat_packet(struct sr_softc *sc, int single); static void sr_get_packets(struct sr_softc *sc); static void sr_up(struct sr_softc *sc); static void sr_down(struct sr_softc *sc); static void src_init(struct sr_hardc *hc); static void sr_init_sca(struct sr_hardc *hc); static void sr_init_msci(struct sr_softc *sc); static void sr_init_rx_dmac(struct sr_softc *sc); static void sr_init_tx_dmac(struct sr_softc *sc); static void sr_dmac_intr(struct sr_hardc *hc, u_char isr); static void sr_msci_intr(struct sr_hardc *hc, u_char isr); static void sr_timer_intr(struct sr_hardc *hc, u_char isr); #ifndef NETGRAPH #ifdef USE_MODEMCK static void sr_modemck(void *x); #endif #else static void sr_modemck(struct sr_softc *x); #endif /* NETGRAPH */ #ifdef NETGRAPH static void ngsr_watchdog_frame(void * arg); static void ngsr_init(void* ignored); static ng_constructor_t ngsr_constructor; static ng_rcvmsg_t ngsr_rcvmsg; static ng_shutdown_t ngsr_shutdown; static ng_newhook_t ngsr_newhook; /*static ng_findhook_t ngsr_findhook; */ static ng_connect_t ngsr_connect; static ng_rcvdata_t ngsr_rcvdata; static ng_disconnect_t ngsr_disconnect; static struct ng_type typestruct = { .version = NG_ABI_VERSION, .name = NG_SR_NODE_TYPE, .constructor = ngsr_constructor, .rcvmsg = ngsr_rcvmsg, .shutdown = ngsr_shutdown, .newhook = ngsr_newhook, .connect = ngsr_connect, .rcvdata = ngsr_rcvdata, .disconnect = ngsr_disconnect, }; static int ngsr_done_init = 0; #endif /* NETGRAPH */ /* * Register the ports on the adapter. * Fill in the info for each port. #ifndef NETGRAPH * Attach each port to sppp and bpf. #endif */ int sr_attach(device_t device) { int intf_sw, pndx; u_int32_t flags; u_int fecr; struct sr_hardc *hc; struct sr_softc *sc; #ifndef NETGRAPH struct ifnet *ifp; #endif /* NETGRAPH */ int unit; /* index: channel w/in card */ hc = (struct sr_hardc *)device_get_softc(device); MALLOC(sc, struct sr_softc *, hc->numports * sizeof(struct sr_softc), M_DEVBUF, M_WAITOK | M_ZERO); if (sc == NULL) goto errexit; hc->sc = sc; /* * Get the TX clock direction and configuration. The default is a * single external clock which is used by RX and TX. */ switch(hc->cardtype) { case SR_CRD_N2: flags = device_get_flags(device); #ifdef N2_TEST_SPEED if (sr_test_speed[0] > 0) hc->sc[0].clk_cfg = SR_FLAGS_INT_CLK; else #endif if (flags & SR_FLAGS_0_CLK_MSK) hc->sc[0].clk_cfg = (flags & SR_FLAGS_0_CLK_MSK) >> SR_FLAGS_CLK_SHFT; if (hc->numports == 2) #ifdef N2_TEST_SPEED if (sr_test_speed[1] > 0) hc->sc[0].clk_cfg = SR_FLAGS_INT_CLK; else #endif if (flags & SR_FLAGS_1_CLK_MSK) hc->sc[1].clk_cfg = (flags & SR_FLAGS_1_CLK_MSK) >> (SR_FLAGS_CLK_SHFT + SR_FLAGS_CLK_CHAN_SHFT); break; case SR_CRD_N2PCI: fecr = sr_read_fecr(hc); for (pndx = 0; pndx < hc->numports; pndx++, sc++) { switch (pndx) { case 1: intf_sw = fecr & SR_FECR_ID1 >> SR_FE_ID1_SHFT; break; case 0: default: intf_sw = fecr & SR_FECR_ID0 >> SR_FE_ID0_SHFT; } #ifdef N2_TEST_SPEED if (sr_test_speed[pndx] > 0) sc->clk_cfg = SR_FLAGS_INT_CLK; else #endif switch (intf_sw) { default: case SR_FE_ID_RS232: case SR_FE_ID_HSSI: case SR_FE_ID_RS422: case SR_FE_ID_TEST: break; case SR_FE_ID_V35: sc->clk_cfg = SR_FLAGS_EXT_SEP_CLK; break; case SR_FE_ID_X21: sc->clk_cfg = SR_FLAGS_EXT_CLK; break; } } sc = hc->sc; break; } /* * Report Card configuration information before we start configuring * each channel on the card... */ printf("src%d: %uK RAM (%d mempages) @ %p-%p, %u ports.\n", hc->cunit, hc->memsize / 1024, hc->mempages, hc->mem_start, hc->mem_end, hc->numports); src_init(hc); sr_init_sca(hc); if (BUS_SETUP_INTR(device_get_parent(device), device, hc->res_irq, INTR_TYPE_NET, srintr, hc, &hc->intr_cookie) != 0) goto errexit; /* * Now configure each port on the card. */ for (unit = 0; unit < hc->numports; sc++, unit++) { sc->hc = hc; sc->subunit = unit; sc->unit = next_sc_unit; next_sc_unit++; sc->scachan = unit % NCHAN; sr_init_rx_dmac(sc); sr_init_tx_dmac(sc); sr_init_msci(sc); printf("sr%d: Adapter %d, port %d.\n", sc->unit, hc->cunit, sc->subunit); #ifndef NETGRAPH ifp = &sc->ifsppp.pp_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(device), device_get_unit(device)); ifp->if_mtu = PP_MTU; ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; ifp->if_ioctl = srioctl; ifp->if_start = srstart; ifp->if_watchdog = srwatchdog; sc->ifsppp.pp_flags = PP_KEEPALIVE; sppp_attach((struct ifnet *)&sc->ifsppp); if_attach(ifp); bpfattach(ifp, DLT_PPP, PPP_HEADER_LEN); #else /* NETGRAPH */ /* * we have found a node, make sure our 'type' is availabe. */ if (ngsr_done_init == 0) ngsr_init(NULL); if (ng_make_node_common(&typestruct, &sc->node) != 0) goto errexit; sprintf(sc->nodename, "%s%d", NG_SR_NODE_TYPE, sc->unit); if (ng_name_node(sc->node, sc->nodename)) { NG_NODE_UNREF(sc->node); /* make it go away again */ goto errexit; } NG_NODE_SET_PRIVATE(sc->node, sc); callout_handle_init(&sc->handle); sc->xmitq.ifq_maxlen = IFQ_MAXLEN; sc->xmitq_hipri.ifq_maxlen = IFQ_MAXLEN; mtx_init(&sc->xmitq.ifq_mtx, "sr_xmitq", NULL, MTX_DEF); mtx_init(&sc->xmitq_hipri.ifq_mtx, "sr_xmitq_hipri", NULL, MTX_DEF); sc->running = 0; #endif /* NETGRAPH */ } if (hc->mempages) SRC_SET_OFF(hc); return (0); errexit: sr_deallocate_resources(device); return (ENXIO); } int sr_detach(device_t device) { device_t parent = device_get_parent(device); struct sr_hardc *hc = device_get_softc(device); if (hc->intr_cookie != NULL) { if (BUS_TEARDOWN_INTR(parent, device, hc->res_irq, hc->intr_cookie) != 0) { printf("intr teardown failed.. continuing\n"); } hc->intr_cookie = NULL; } /* XXX Stop the DMA. */ /* * deallocate any system resources we may have * allocated on behalf of this driver. */ FREE(hc->sc, M_DEVBUF); hc->sc = NULL; hc->mem_start = NULL; return (sr_deallocate_resources(device)); } int sr_allocate_ioport(device_t device, int rid, u_long size) { struct sr_hardc *hc = device_get_softc(device); hc->rid_ioport = rid; hc->res_ioport = bus_alloc_resource(device, SYS_RES_IOPORT, &hc->rid_ioport, 0ul, ~0ul, size, RF_ACTIVE); if (hc->res_ioport == NULL) { goto errexit; } hc->bt_ioport = rman_get_bustag(hc->res_ioport); hc->bh_ioport = rman_get_bushandle(hc->res_ioport); return (0); errexit: sr_deallocate_resources(device); return (ENXIO); } int sr_allocate_irq(device_t device, int rid, u_long size) { struct sr_hardc *hc = device_get_softc(device); hc->rid_irq = rid; hc->res_irq = bus_alloc_resource_any(device, SYS_RES_IRQ, &hc->rid_irq, RF_SHAREABLE|RF_ACTIVE); if (hc->res_irq == NULL) { goto errexit; } return (0); errexit: sr_deallocate_resources(device); return (ENXIO); } int sr_allocate_memory(device_t device, int rid, u_long size) { struct sr_hardc *hc = device_get_softc(device); hc->rid_memory = rid; hc->res_memory = bus_alloc_resource(device, SYS_RES_MEMORY, &hc->rid_memory, 0ul, ~0ul, size, RF_ACTIVE); if (hc->res_memory == NULL) { goto errexit; } hc->bt_memory = rman_get_bustag(hc->res_memory); hc->bh_memory = rman_get_bushandle(hc->res_memory); return (0); errexit: sr_deallocate_resources(device); return (ENXIO); } int sr_allocate_plx_memory(device_t device, int rid, u_long size) { struct sr_hardc *hc = device_get_softc(device); hc->rid_plx_memory = rid; hc->res_plx_memory = bus_alloc_resource(device, SYS_RES_MEMORY, &hc->rid_plx_memory, 0ul, ~0ul, size, RF_ACTIVE); if (hc->res_plx_memory == NULL) { goto errexit; } return (0); errexit: sr_deallocate_resources(device); return (ENXIO); } int sr_deallocate_resources(device_t device) { struct sr_hardc *hc = device_get_softc(device); if (hc->res_irq != 0) { bus_deactivate_resource(device, SYS_RES_IRQ, hc->rid_irq, hc->res_irq); bus_release_resource(device, SYS_RES_IRQ, hc->rid_irq, hc->res_irq); hc->res_irq = 0; } if (hc->res_ioport != 0) { bus_deactivate_resource(device, SYS_RES_IOPORT, hc->rid_ioport, hc->res_ioport); bus_release_resource(device, SYS_RES_IOPORT, hc->rid_ioport, hc->res_ioport); hc->res_ioport = 0; } if (hc->res_memory != 0) { bus_deactivate_resource(device, SYS_RES_MEMORY, hc->rid_memory, hc->res_memory); bus_release_resource(device, SYS_RES_MEMORY, hc->rid_memory, hc->res_memory); hc->res_memory = 0; } if (hc->res_plx_memory != 0) { bus_deactivate_resource(device, SYS_RES_MEMORY, hc->rid_plx_memory, hc->res_plx_memory); bus_release_resource(device, SYS_RES_MEMORY, hc->rid_plx_memory, hc->res_plx_memory); hc->res_plx_memory = 0; } return (0); } /* * N2 Interrupt Service Routine * * First figure out which SCA gave the interrupt. * Process it. * See if there is other interrupts pending. * Repeat until there no interrupts remain. */ static void srintr(void *arg) { struct sr_hardc *hc = (struct sr_hardc *)arg; sca_regs *sca = hc->sca; /* MSCI register tree */ u_char isr0, isr1, isr2; /* interrupt statii captured */ #if BUGGY > 1 printf("sr: srintr_hc(hc=%08x)\n", hc); #endif /* * Since multiple interfaces may share this interrupt, we must loop * until no interrupts are still pending service. */ while (1) { /* * Read all three interrupt status registers from the N2 * card... */ isr0 = SRC_GET8(hc, sca->isr0); isr1 = SRC_GET8(hc, sca->isr1); isr2 = SRC_GET8(hc, sca->isr2); /* * If all three registers returned 0, we've finished * processing interrupts from this device, so we can quit * this loop... */ if ((isr0 | isr1 | isr2) == 0) break; #if BUGGY > 2 printf("src%d: srintr_hc isr0 %x, isr1 %x, isr2 %x\n", #ifndef NETGRAPH unit, isr0, isr1, isr2); #else hc->cunit, isr0, isr1, isr2); #endif /* NETGRAPH */ #endif /* * Now we can dispatch the interrupts. Since we don't expect * either MSCI or timer interrupts, we'll test for DMA * interrupts first... */ if (isr1) /* DMA-initiated interrupt */ sr_dmac_intr(hc, isr1); if (isr0) /* serial part IRQ? */ sr_msci_intr(hc, isr0); if (isr2) /* timer-initiated interrupt */ sr_timer_intr(hc, isr2); } } /* * This will only start the transmitter. It is assumed that the data * is already there. * It is normally called from srstart() or sr_dmac_intr(). */ static void sr_xmit(struct sr_softc *sc) { u_short cda_value; /* starting descriptor */ u_short eda_value; /* ending descriptor */ struct sr_hardc *hc; #ifndef NETGRAPH struct ifnet *ifp; /* O/S Network Services */ #endif /* NETGRAPH */ dmac_channel *dmac; /* DMA channel registers */ #if BUGGY > 0 printf("sr: sr_xmit( sc=%08x)\n", sc); #endif hc = sc->hc; #ifndef NETGRAPH ifp = &sc->ifsppp.pp_if; #endif /* NETGRAPH */ dmac = &hc->sca->dmac[DMAC_TXCH(sc->scachan)]; /* * Get the starting and ending addresses of the chain to be * transmitted and pass these on to the DMA engine on-chip. */ cda_value = sc->block[sc->txb_next_tx].txdesc + hc->mem_pstart; cda_value &= 0x00ffff; eda_value = sc->block[sc->txb_next_tx].txeda + hc->mem_pstart; eda_value &= 0x00ffff; SRC_PUT16(hc, dmac->cda, cda_value); SRC_PUT16(hc, dmac->eda, eda_value); /* * Now we'll let the DMA status register know about this change */ SRC_PUT8(hc, dmac->dsr, SCA_DSR_DE); sc->xmit_busy = 1; /* mark transmitter busy */ #if BUGGY > 2 printf("sr%d: XMIT cda=%04x, eda=%4x, rcda=%08lx\n", sc->unit, cda_value, eda_value, sc->block[sc->txb_next_tx].txdesc + hc->mem_pstart); #endif sc->txb_next_tx++; /* update next transmit seq# */ if (sc->txb_next_tx == SR_TX_BLOCKS) /* handle wrap... */ sc->txb_next_tx = 0; #ifndef NETGRAPH /* * Finally, we'll set a timout (which will start srwatchdog()) * within the O/S network services layer... */ ifp->if_timer = 2; /* Value in seconds. */ #else /* * Don't time out for a while. */ sc->out_dog = DOG_HOLDOFF; /* give ourself some breathing space*/ #endif /* NETGRAPH */ } /* * This function will be called from the upper level when a user add a * packet to be send, and from the interrupt handler after a finished * transmit. * * NOTE: it should run at spl_imp(). * * This function only place the data in the oncard buffers. It does not * start the transmition. sr_xmit() does that. * * Transmitter idle state is indicated by the IFF_OACTIVE flag. * The function that clears that should ensure that the transmitter * and its DMA is in a "good" idle state. */ #ifndef NETGRAPH static void srstart(struct ifnet *ifp) { struct sr_softc *sc; /* channel control structure */ #else static void srstart(struct sr_softc *sc) { #endif /* NETGRAPH */ struct sr_hardc *hc; /* card control/config block */ int len; /* total length of a packet */ int pkts; /* packets placed in DPRAM */ int tlen; /* working length of pkt */ u_int i; struct mbuf *mtx; /* message buffer from O/S */ u_char *txdata; /* buffer address in DPRAM */ sca_descriptor *txdesc; /* working descriptor pointr */ struct buf_block *blkp; #ifndef NETGRAPH #if BUGGY > 0 printf("sr: srstart( ifp=%08x)\n", ifp); #endif sc = ifp->if_softc; if ((ifp->if_flags & IFF_RUNNING) == 0) return; #endif /* NETGRAPH */ hc = sc->hc; /* * It is OK to set the memory window outside the loop because all tx * buffers and descriptors are assumed to be in the same 16K window. */ if (hc->mempages) { SRC_SET_ON(hc); SRC_SET_MEM(hc, sc->block[0].txdesc); } /* * Loop to place packets into DPRAM. * * We stay in this loop until there is nothing in * the TX queue left or the tx buffers are full. */ top_srstart: /* * See if we have space for more packets. */ if (sc->txb_inuse == SR_TX_BLOCKS) { /* out of space? */ #ifndef NETGRAPH ifp->if_flags |= IFF_OACTIVE; /* yes, mark active */ #else /*ifp->if_flags |= IFF_OACTIVE;*/ /* yes, mark active */ #endif /* NETGRAPH */ if (hc->mempages) SRC_SET_OFF(hc); #if BUGGY > 9 printf("sr%d.srstart: sc->txb_inuse=%d; DPRAM full...\n", sc->unit, sc->txb_inuse); #endif return; } /* * OK, the card can take more traffic. Let's see if there's any * pending from the system... * * NOTE: * The architecture of the networking interface doesn't * actually call us like 'write()', providing an address. We get * started, a lot like a disk strategy routine, and we actually call * back out to the system to get traffic to send... * * NOTE: * If we were gonna run through another layer, we would use a * dispatch table to select the service we're getting a packet * from... */ #ifndef NETGRAPH mtx = sppp_dequeue(ifp); #else /* NETGRAPH */ IF_DEQUEUE(&sc->xmitq_hipri, mtx); if (mtx == NULL) { IF_DEQUEUE(&sc->xmitq, mtx); } #endif /* NETGRAPH */ if (!mtx) { if (hc->mempages) SRC_SET_OFF(hc); return; } /* * OK, we got a packet from the network services of the O/S. Now we * can move it into the DPRAM (under control of the descriptors) and * fire it off... */ pkts = 0; i = 0; /* counts # of granules used */ blkp = &sc->block[sc->txb_new]; /* address of free granule */ txdesc = (sca_descriptor *) (hc->mem_start + (blkp->txdesc & hc->winmsk)); txdata = (u_char *)(hc->mem_start + (blkp->txstart & hc->winmsk)); /* * Now we'll try to install as many packets as possible into the * card's DP RAM buffers. */ for (;;) { /* perform actual copy of packet */ len = mtx->m_pkthdr.len; /* length of message */ #if BUGGY > 1 printf("sr%d.srstart: mbuf @ %08lx, %d bytes\n", sc->unit, mtx, len); #endif #ifndef NETGRAPH BPF_MTAP(ifp, mtx); #else /* NETGRAPH */ sc->outbytes += len; #endif /* NETGRAPH */ /* * We can perform a straight copy because the tranmit * buffers won't wrap. */ m_copydata(mtx, 0, len, txdata); /* * Now we know how big the message is gonna be. We must now * construct the descriptors to drive this message out... */ tlen = len; while (tlen > SR_BUF_SIZ) { /* loop for full granules */ txdesc->stat = 0; /* reset bits */ txdesc->len = SR_BUF_SIZ; /* size of granule */ tlen -= SR_BUF_SIZ; txdesc++; /* move to next dscr */ txdata += SR_BUF_SIZ; /* adjust data addr */ i++; } /* * This section handles the setting of the final piece of a * message. */ txdesc->stat = SCA_DESC_EOM; txdesc->len = tlen; pkts++; /* * prepare for subsequent packets (if any) */ txdesc++; txdata += SR_BUF_SIZ; /* next mem granule */ i++; /* count of granules */ /* * OK, we've now placed the message into the DPRAM where it * can be transmitted. We'll now release the message memory * and update the statistics... */ m_freem(mtx); #ifndef NETGRAPH ++sc->ifsppp.pp_if.if_opackets; #else /* NETGRAPH */ sc->opackets++; #endif /* NETGRAPH */ /* * Check if we have space for another packet. XXX This is * hardcoded. A packet can't be larger than 3 buffers (3 x * 512). */ if ((i + 3) >= blkp->txmax) { /* enough remains? */ #if BUGGY > 9 printf("sr%d.srstart: i=%d (%d pkts); card full.\n", sc->unit, i, pkts); #endif break; } /* * We'll pull the next message to be sent (if any) */ #ifndef NETGRAPH mtx = sppp_dequeue(ifp); #else /* NETGRAPH */ IF_DEQUEUE(&sc->xmitq_hipri, mtx); if (mtx == NULL) { IF_DEQUEUE(&sc->xmitq, mtx); } #endif /* NETGRAPH */ if (!mtx) { /* no message? We're done! */ #if BUGGY > 9 printf("sr%d.srstart: pending=0, pkts=%d\n", sc->unit, pkts); #endif break; } } blkp->txtail = i; /* record next free granule */ /* * Mark the last descriptor, so that the SCA know where to stop. */ txdesc--; /* back up to last descriptor in list */ txdesc->stat |= SCA_DESC_EOT; /* mark as end of list */ /* * Now we'll reset the transmit granule's descriptor address so we * can record this in the structure and fire it off w/ the DMA * processor of the serial chip... */ txdesc = (sca_descriptor *)(uintptr_t)blkp->txdesc; blkp->txeda = (u_short)((uintptr_t)&txdesc[i]); sc->txb_inuse++; /* update inuse status */ sc->txb_new++; /* new traffic wuz added */ if (sc->txb_new == SR_TX_BLOCKS) sc->txb_new = 0; /* * If the tranmitter wasn't marked as "busy" we will force it to be * started... */ if (sc->xmit_busy == 0) { sr_xmit(sc); #if BUGGY > 9 printf("sr%d.srstart: called sr_xmit()\n", sc->unit); #endif } goto top_srstart; } #ifndef NETGRAPH /* * Handle ioctl's at the device level, though we *will* call up * a layer... */ #if BUGGY > 2 static int bug_splats[] = {0, 0, 0, 0, 0, 0, 0, 0}; #endif static int srioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { int s, error, was_up, should_be_up; struct sr_softc *sc = ifp->if_softc; #if BUGGY > 0 if_printf(ifp, "srioctl(ifp=%08x, cmd=%08x, data=%08x)\n", ifp, cmd, data); #endif was_up = ifp->if_flags & IFF_RUNNING; error = sppp_ioctl(ifp, cmd, data); #if BUGGY > 1 if_printf(ifp, "ioctl: ifsppp.pp_flags = %08x, if_flags %08x.\n", ((struct sppp *)ifp)->pp_flags, ifp->if_flags); #endif if (error) return error; if ((cmd != SIOCSIFFLAGS) && (cmd != SIOCSIFADDR)) { #if BUGGY > 2 if (bug_splats[sc->unit]++ < 2) { printf("sr(%d).if_addrlist = %08x\n", sc->unit, ifp->if_addrlist); printf("sr(%d).if_bpf = %08x\n", sc->unit, ifp->if_bpf); printf("sr(%d).if_init = %08x\n", sc->unit, ifp->if_init); printf("sr(%d).if_output = %08x\n", sc->unit, ifp->if_output); printf("sr(%d).if_start = %08x\n", sc->unit, ifp->if_start); printf("sr(%d).if_done = %08x\n", sc->unit, ifp->if_done); printf("sr(%d).if_ioctl = %08x\n", sc->unit, ifp->if_ioctl); printf("sr(%d).if_reset = %08x\n", sc->unit, ifp->if_reset); printf("sr(%d).if_watchdog = %08x\n", sc->unit, ifp->if_watchdog); } #endif return 0; } s = splimp(); should_be_up = ifp->if_flags & IFF_RUNNING; if (!was_up && should_be_up) { /* * Interface should be up -- start it. */ sr_up(sc); srstart(ifp); /* * XXX Clear the IFF_UP flag so that the link will only go * up after sppp lcp and ipcp negotiation. */ /* ifp->if_flags &= ~IFF_UP; */ } else if (was_up && !should_be_up) { /* * Interface should be down -- stop it. */ sr_down(sc); sppp_flush(ifp); } splx(s); return 0; } #endif /* NETGRAPH */ /* * This is to catch lost tx interrupts. */ static void #ifndef NETGRAPH srwatchdog(struct ifnet *ifp) #else srwatchdog(struct sr_softc *sc) #endif /* NETGRAPH */ { int got_st0, got_st1, got_st3, got_dsr; #ifndef NETGRAPH struct sr_softc *sc = ifp->if_softc; #endif /* NETGRAPH */ struct sr_hardc *hc = sc->hc; msci_channel *msci = &hc->sca->msci[sc->scachan]; dmac_channel *dmac = &sc->hc->sca->dmac[sc->scachan]; #if BUGGY > 0 #ifndef NETGRAPH printf("srwatchdog(unit=%d)\n", unit); #else printf("srwatchdog(unit=%d)\n", sc->unit); #endif /* NETGRAPH */ #endif #ifndef NETGRAPH if (!(ifp->if_flags & IFF_RUNNING)) return; ifp->if_oerrors++; /* update output error count */ #else /* NETGRAPH */ sc->oerrors++; /* update output error count */ #endif /* NETGRAPH */ got_st0 = SRC_GET8(hc, msci->st0); got_st1 = SRC_GET8(hc, msci->st1); got_st3 = SRC_GET8(hc, msci->st3); got_dsr = SRC_GET8(hc, dmac->dsr); #ifndef NETGRAPH #if 0 if (ifp->if_flags & IFF_DEBUG) #endif printf("sr%d: transmit failed, " #else /* NETGRAPH */ printf("sr%d: transmit failed, " #endif /* NETGRAPH */ "ST0 %02x, ST1 %02x, ST3 %02x, DSR %02x.\n", sc->unit, got_st0, got_st1, got_st3, got_dsr); if (SRC_GET8(hc, msci->st1) & SCA_ST1_UDRN) { SRC_PUT8(hc, msci->cmd, SCA_CMD_TXABORT); SRC_PUT8(hc, msci->cmd, SCA_CMD_TXENABLE); SRC_PUT8(hc, msci->st1, SCA_ST1_UDRN); } sc->xmit_busy = 0; #ifndef NETGRAPH ifp->if_flags &= ~IFF_OACTIVE; #else /*ifp->if_flags &= ~IFF_OACTIVE; */ #endif /* NETGRAPH */ if (sc->txb_inuse && --sc->txb_inuse) sr_xmit(sc); #ifndef NETGRAPH srstart(ifp); /* restart transmitter */ #else srstart(sc); /* restart transmitter */ #endif /* NETGRAPH */ } static void sr_up(struct sr_softc *sc) { struct sr_hardc *hc = sc->hc; sca_regs *sca = hc->sca; msci_channel *msci = &sca->msci[sc->scachan]; #if BUGGY > 0 printf("sr_up(sc=%08x)\n", sc); #endif /* * Enable transmitter and receiver. Raise DTR and RTS. Enable * interrupts. * * XXX What about using AUTO mode in msci->md0 ??? */ SRC_PUT8(hc, msci->ctl, SRC_GET8(hc, msci->ctl) & ~SCA_CTL_RTS); if (sc->scachan == 0) switch (hc->cardtype) { case SR_CRD_N2: sr_outb(hc, SR_MCR, (sr_inb(hc, SR_MCR) & ~SR_MCR_DTR0)); break; case SR_CRD_N2PCI: sr_write_fecr(hc, sr_read_fecr(hc) & ~SR_FECR_DTR0); break; } else switch (hc->cardtype) { case SR_CRD_N2: sr_outb(hc, SR_MCR, (sr_inb(hc, SR_MCR) & ~SR_MCR_DTR1)); break; case SR_CRD_N2PCI: sr_write_fecr(hc, sr_read_fecr(hc) & ~SR_FECR_DTR1); break; } if (sc->scachan == 0) { SRC_PUT8(hc, sca->ier0, SRC_GET8(hc, sca->ier0) | 0x000F); SRC_PUT8(hc, sca->ier1, SRC_GET8(hc, sca->ier1) | 0x000F); } else { SRC_PUT8(hc, sca->ier0, SRC_GET8(hc, sca->ier0) | 0x00F0); SRC_PUT8(hc, sca->ier1, SRC_GET8(hc, sca->ier1) | 0x00F0); } SRC_PUT8(hc, msci->cmd, SCA_CMD_RXENABLE); sr_inb(hc, 0); /* XXX slow it down a bit. */ SRC_PUT8(hc, msci->cmd, SCA_CMD_TXENABLE); #ifndef NETGRAPH #ifdef USE_MODEMCK if (sr_watcher == 0) sr_modemck(NULL); #endif #else /* NETGRAPH */ untimeout(ngsr_watchdog_frame, sc, sc->handle); sc->handle = timeout(ngsr_watchdog_frame, sc, hz); sc->running = 1; #endif /* NETGRAPH */ } static void sr_down(struct sr_softc *sc) { struct sr_hardc *hc = sc->hc; sca_regs *sca = hc->sca; msci_channel *msci = &sca->msci[sc->scachan]; #if BUGGY > 0 printf("sr_down(sc=%08x)\n", sc); #endif #ifdef NETGRAPH untimeout(ngsr_watchdog_frame, sc, sc->handle); sc->running = 0; #endif /* NETGRAPH */ /* * Disable transmitter and receiver. Lower DTR and RTS. Disable * interrupts. */ SRC_PUT8(hc, msci->cmd, SCA_CMD_RXDISABLE); sr_inb(hc, 0); /* XXX slow it down a bit. */ SRC_PUT8(hc, msci->cmd, SCA_CMD_TXDISABLE); SRC_PUT8(hc, msci->ctl, SRC_GET8(hc, msci->ctl) | SCA_CTL_RTS); if (sc->scachan == 0) switch (hc->cardtype) { case SR_CRD_N2: sr_outb(hc, SR_MCR, sr_inb(hc, SR_MCR) | SR_MCR_DTR0); break; case SR_CRD_N2PCI: sr_write_fecr(hc, sr_read_fecr(hc) | SR_FECR_DTR0); break; } else switch (hc->cardtype) { case SR_CRD_N2: sr_outb(hc, SR_MCR, sr_inb(hc, SR_MCR) | SR_MCR_DTR1); break; case SR_CRD_N2PCI: sr_write_fecr(hc, sr_read_fecr(hc) | SR_FECR_DTR1); break; } if (sc->scachan == 0) { SRC_PUT8(hc, sca->ier0, SRC_GET8(hc, sca->ier0) & ~0x0F); SRC_PUT8(hc, sca->ier1, SRC_GET8(hc, sca->ier1) & ~0x0F); } else { SRC_PUT8(hc, sca->ier0, SRC_GET8(hc, sca->ier0) & ~0xF0); SRC_PUT8(hc, sca->ier1, SRC_GET8(hc, sca->ier1) & ~0xF0); } } /* * Initialize the card, allocate memory for the sr_softc structures * and fill in the pointers. */ static void src_init(struct sr_hardc *hc) { struct sr_softc *sc = hc->sc; int x; u_int chanmem; u_int bufmem; u_int next; u_int descneeded; #if BUGGY > 0 printf("src_init(hc=%08x)\n", hc); #endif chanmem = hc->memsize / hc->numports; next = 0; for (x = 0; x < hc->numports; x++, sc++) { int blk; for (blk = 0; blk < SR_TX_BLOCKS; blk++) { sc->block[blk].txdesc = next; bufmem = (16 * 1024) / SR_TX_BLOCKS; descneeded = bufmem / SR_BUF_SIZ; sc->block[blk].txstart = sc->block[blk].txdesc + ((((descneeded * sizeof(sca_descriptor)) / SR_BUF_SIZ) + 1) * SR_BUF_SIZ); sc->block[blk].txend = next + bufmem; sc->block[blk].txmax = (sc->block[blk].txend - sc->block[blk].txstart) / SR_BUF_SIZ; next += bufmem; #if BUGGY > 2 printf("sr%d: blk %d: txdesc %08x, txstart %08x\n", sc->unit, blk, sc->block[blk].txdesc, sc->block[blk].txstart); #endif } sc->rxdesc = next; bufmem = chanmem - (bufmem * SR_TX_BLOCKS); descneeded = bufmem / SR_BUF_SIZ; sc->rxstart = sc->rxdesc + ((((descneeded * sizeof(sca_descriptor)) / SR_BUF_SIZ) + 1) * SR_BUF_SIZ); sc->rxend = next + bufmem; sc->rxmax = (sc->rxend - sc->rxstart) / SR_BUF_SIZ; next += bufmem; } } /* * The things done here are channel independent. * * Configure the sca waitstates. * Configure the global interrupt registers. * Enable master dma enable. */ static void sr_init_sca(struct sr_hardc *hc) { sca_regs *sca = hc->sca; #if BUGGY > 0 printf("sr_init_sca(hc=%08x)\n", hc); #endif /* * Do the wait registers. Set everything to 0 wait states. */ SRC_PUT8(hc, sca->pabr0, 0); SRC_PUT8(hc, sca->pabr1, 0); SRC_PUT8(hc, sca->wcrl, 0); SRC_PUT8(hc, sca->wcrm, 0); SRC_PUT8(hc, sca->wcrh, 0); /* * Configure the interrupt registers. Most are cleared until the * interface is configured. */ SRC_PUT8(hc, sca->ier0, 0x00); /* MSCI interrupts. */ SRC_PUT8(hc, sca->ier1, 0x00); /* DMAC interrupts */ SRC_PUT8(hc, sca->ier2, 0x00); /* TIMER interrupts. */ SRC_PUT8(hc, sca->itcr, 0x00); /* Use ivr and no intr ack */ SRC_PUT8(hc, sca->ivr, 0x40); /* Interrupt vector. */ SRC_PUT8(hc, sca->imvr, 0x40); /* * Configure the timers. XXX Later */ /* * Set the DMA channel priority to rotate between all four channels. * * Enable all dma channels. */ SRC_PUT8(hc, sca->pcr, SCA_PCR_PR2); SRC_PUT8(hc, sca->dmer, SCA_DMER_EN); } /* * Configure the msci * * NOTE: The serial port configuration is hardcoded at the moment. */ static void sr_init_msci(struct sr_softc *sc) { int portndx; /* on-board port number */ u_int mcr_v; /* contents of modem control */ struct sr_hardc *hc = sc->hc; msci_channel *msci = &hc->sca->msci[sc->scachan]; #ifdef N2_TEST_SPEED int br_v; /* contents for BR divisor */ int etcndx; /* index into ETC table */ int fifo_v, gotspeed; /* final tabled speed found */ int tmc_v; /* timer control register */ int wanted; /* speed (bitrate) wanted... */ struct rate_line *rtp; #endif portndx = sc->scachan; #if BUGGY > 0 printf("sr: sr_init_msci( sc=%08x)\n", sc); #endif SRC_PUT8(hc, msci->cmd, SCA_CMD_RESET); SRC_PUT8(hc, msci->md0, SCA_MD0_CRC_1 | SCA_MD0_CRC_CCITT | SCA_MD0_CRC_ENABLE | SCA_MD0_MODE_HDLC); SRC_PUT8(hc, msci->md1, SCA_MD1_NOADDRCHK); SRC_PUT8(hc, msci->md2, SCA_MD2_DUPLEX | SCA_MD2_NRZ); /* * According to the manual I should give a reset after changing the * mode registers. */ SRC_PUT8(hc, msci->cmd, SCA_CMD_RXRESET); SRC_PUT8(hc, msci->ctl, SCA_CTL_IDLPAT | SCA_CTL_UDRNC | SCA_CTL_RTS); /* * XXX Later we will have to support different clock settings. */ switch (sc->clk_cfg) { default: #if BUGGY > 0 printf("sr%: clk_cfg=%08x, selected default clock.\n", portndx, sc->clk_cfg); #endif /* FALLTHROUGH */ case SR_FLAGS_EXT_CLK: /* * For now all interfaces are programmed to use the RX clock * for the TX clock. */ #if BUGGY > 0 printf("sr%d: External Clock Selected.\n", portndx); #endif SRC_PUT8(hc, msci->rxs, SCA_RXS_CLK_RXC0 | SCA_RXS_DIV1); SRC_PUT8(hc, msci->txs, SCA_TXS_CLK_RX | SCA_TXS_DIV1); break; case SR_FLAGS_EXT_SEP_CLK: #if BUGGY > 0 printf("sr%d: Split Clocking Selected.\n", portndx); #endif SRC_PUT8(hc, msci->rxs, SCA_RXS_CLK_RXC0 | SCA_RXS_DIV1); SRC_PUT8(hc, msci->txs, SCA_TXS_CLK_TXC | SCA_TXS_DIV1); break; case SR_FLAGS_INT_CLK: #if BUGGY > 0 printf("sr%d: Internal Clocking selected.\n", portndx); #endif /* * XXX I do need some code to set the baud rate here! */ #ifdef N2_TEST_SPEED switch (hc->cardtype) { case SR_CRD_N2PCI: mcr_v = sr_read_fecr(hc); etcndx = 2; break; case SR_CRD_N2: default: mcr_v = sr_inb(hc, SR_MCR); etcndx = 0; } fifo_v = 0x10; /* stolen from Linux version */ /* * search for appropriate speed in table, don't calc it: */ wanted = sr_test_speed[portndx]; rtp = &n2_rates[0]; /* point to first table item */ while ((rtp->target > 0) /* search table for speed */ &&(rtp->target != wanted)) rtp++; /* * We've searched the table for a matching speed. If we've * found the correct rate line, we'll get the pre-calc'd * values for the TMC and baud rate divisor for subsequent * use... */ if (rtp->target > 0) { /* use table-provided values */ gotspeed = wanted; tmc_v = rtp->tmc_reg; br_v = rtp->br_reg; } else { /* otherwise assume 1MBit comm rate */ gotspeed = 10000; tmc_v = 5; br_v = 1; } /* * Now we mask in the enable clock output for the MCR: */ mcr_v |= etc0vals[etcndx + portndx]; /* * Now we'll program the registers with these speed- related * contents... */ SRC_PUT8(hc, msci->tmc, tmc_v); SRC_PUT8(hc, msci->trc0, fifo_v); SRC_PUT8(hc, msci->rxs, SCA_RXS_CLK_INT + br_v); SRC_PUT8(hc, msci->txs, SCA_TXS_CLK_INT + br_v); switch (hc->cardtype) { case SR_CRD_N2PCI: sr_write_fecr(hc, mcr_v); break; case SR_CRD_N2: default: sr_outb(hc, SR_MCR, mcr_v); } #if BUGGY > 0 if (wanted != gotspeed) printf("sr%d: Speed wanted=%d, found=%d\n", wanted, gotspeed); printf("sr%d: Internal Clock %dx100 BPS, tmc=%d, div=%d\n", portndx, gotspeed, tmc_v, br_v); #endif #else SRC_PUT8(hc, msci->rxs, SCA_RXS_CLK_INT | SCA_RXS_DIV1); SRC_PUT8(hc, msci->txs, SCA_TXS_CLK_INT | SCA_TXS_DIV1); SRC_PUT8(hc, msci->tmc, 5); if (portndx == 0) switch (hc->cardtype) { case SR_CRD_N2PCI: sr_write_fecr(hc, sr_read_fecr(hc) | SR_FECR_ETC0); break; case SR_CRD_N2: default: mcr_v = sr_inb(hc, SR_MCR); mcr_v |= SR_MCR_ETC0; sr_outb(hc, SR_MCR, mcr_v); } else switch (hc->cardtype) { case SR_CRD_N2: mcr_v = sr_inb(hc, SR_MCR); mcr_v |= SR_MCR_ETC1; sr_outb(hc, SR_MCR, mcr_v); break; case SR_CRD_N2PCI: sr_write_fecr(hc, sr_read_fecr(hc) | SR_FECR_ETC1); break; } #endif } /* * XXX Disable all interrupts for now. I think if you are using the * dmac you don't use these interrupts. */ SRC_PUT8(hc, msci->ie0, 0); SRC_PUT8(hc, msci->ie1, 0x0C); SRC_PUT8(hc, msci->ie2, 0); SRC_PUT8(hc, msci->fie, 0); SRC_PUT8(hc, msci->sa0, 0); SRC_PUT8(hc, msci->sa1, 0); SRC_PUT8(hc, msci->idl, 0x7E); /* set flags value */ SRC_PUT8(hc, msci->rrc, 0x0E); SRC_PUT8(hc, msci->trc0, 0x10); SRC_PUT8(hc, msci->trc1, 0x1F); } /* * Configure the rx dma controller. */ static void sr_init_rx_dmac(struct sr_softc *sc) { struct sr_hardc *hc; dmac_channel *dmac; sca_descriptor *rxd; u_int cda_v, sarb_v, rxbuf, rxda, rxda_d; #if BUGGY > 0 printf("sr_init_rx_dmac(sc=%08x)\n", sc); #endif hc = sc->hc; dmac = &hc->sca->dmac[DMAC_RXCH(sc->scachan)]; if (hc->mempages) SRC_SET_MEM(hc, sc->rxdesc); /* * This phase initializes the contents of the descriptor table * needed to construct a circular buffer... */ rxd = (sca_descriptor *)(hc->mem_start + (sc->rxdesc & hc->winmsk)); rxda_d = (uintptr_t) hc->mem_start - (sc->rxdesc & ~hc->winmsk); for (rxbuf = sc->rxstart; rxbuf < sc->rxend; rxbuf += SR_BUF_SIZ, rxd++) { /* * construct the circular chain... */ rxda = (uintptr_t) &rxd[1] - rxda_d + hc->mem_pstart; rxd->cp = (u_short)(rxda & 0xffff); /* * set the on-card buffer address... */ rxd->bp = (u_short)((rxbuf + hc->mem_pstart) & 0xffff); rxd->bpb = (u_char)(((rxbuf + hc->mem_pstart) >> 16) & 0xff); rxd->len = 0; /* bytes resident w/in granule */ rxd->stat = 0xff; /* The sca write here when finished */ } /* * heal the chain so that the last entry points to the first... */ rxd--; rxd->cp = (u_short)((sc->rxdesc + hc->mem_pstart) & 0xffff); /* * reset the reception handler's index... */ sc->rxhind = 0; /* * We'll now configure the receiver's DMA logic... */ SRC_PUT8(hc, dmac->dsr, 0); /* Disable DMA transfer */ SRC_PUT8(hc, dmac->dcr, SCA_DCR_ABRT); /* XXX maybe also SCA_DMR_CNTE */ SRC_PUT8(hc, dmac->dmr, SCA_DMR_TMOD | SCA_DMR_NF); SRC_PUT16(hc, dmac->bfl, SR_BUF_SIZ); cda_v = (u_short)((sc->rxdesc + hc->mem_pstart) & 0xffff); sarb_v = (u_char)(((sc->rxdesc + hc->mem_pstart) >> 16) & 0xff); SRC_PUT16(hc, dmac->cda, cda_v); SRC_PUT8(hc, dmac->sarb, sarb_v); rxd = (sca_descriptor *)(uintptr_t)sc->rxstart; SRC_PUT16(hc, dmac->eda, (u_short)((uintptr_t)&rxd[sc->rxmax - 1] & 0xffff)); SRC_PUT8(hc, dmac->dir, 0xF0); SRC_PUT8(hc, dmac->dsr, SCA_DSR_DE); /* Enable DMA */ } /* * Configure the TX DMA descriptors. * Initialize the needed values and chain the descriptors. */ static void sr_init_tx_dmac(struct sr_softc *sc) { int blk; u_int txbuf, txda, txda_d; struct sr_hardc *hc; sca_descriptor *txd; dmac_channel *dmac; struct buf_block *blkp; u_int x; u_int sarb_v; #if BUGGY > 0 printf("sr_init_tx_dmac(sc=%08x)\n", sc); #endif hc = sc->hc; dmac = &hc->sca->dmac[DMAC_TXCH(sc->scachan)]; if (hc->mempages) SRC_SET_MEM(hc, sc->block[0].txdesc); /* * Initialize the array of descriptors for transmission */ for (blk = 0; blk < SR_TX_BLOCKS; blk++) { blkp = &sc->block[blk]; txd = (sca_descriptor *)(hc->mem_start + (blkp->txdesc & hc->winmsk)); txda_d = (uintptr_t) hc->mem_start - (blkp->txdesc & ~hc->winmsk); x = 0; txbuf = blkp->txstart; for (; txbuf < blkp->txend; txbuf += SR_BUF_SIZ, txd++) { txda = (uintptr_t) &txd[1] - txda_d + hc->mem_pstart; txd->cp = (u_short)(txda & 0xffff); txd->bp = (u_short)((txbuf + hc->mem_pstart) & 0xffff); txd->bpb = (u_char)(((txbuf + hc->mem_pstart) >> 16) & 0xff); txd->len = 0; txd->stat = 0; x++; } txd--; txd->cp = (u_short)((blkp->txdesc + hc->mem_pstart) & 0xffff); blkp->txtail = (uintptr_t)txd - (uintptr_t)hc->mem_start; } SRC_PUT8(hc, dmac->dsr, 0); /* Disable DMA */ SRC_PUT8(hc, dmac->dcr, SCA_DCR_ABRT); SRC_PUT8(hc, dmac->dmr, SCA_DMR_TMOD | SCA_DMR_NF); SRC_PUT8(hc, dmac->dir, SCA_DIR_EOT | SCA_DIR_BOF | SCA_DIR_COF); sarb_v = (sc->block[0].txdesc + hc->mem_pstart) >> 16; sarb_v &= 0x00ff; SRC_PUT8(hc, dmac->sarb, (u_char) sarb_v); } /* * Look through the descriptors to see if there is a complete packet * available. Stop if we get to where the sca is busy. * * Return the length and status of the packet. * Return nonzero if there is a packet available. * * NOTE: * It seems that we get the interrupt a bit early. The updateing of * descriptor values is not always completed when this is called. */ static int sr_packet_avail(struct sr_softc *sc, int *len, u_char *rxstat) { int granules; /* count of granules in pkt */ int wki, wko; struct sr_hardc *hc; sca_descriptor *rxdesc; /* current descriptor */ sca_descriptor *endp; /* ending descriptor */ sca_descriptor *cda; /* starting descriptor */ hc = sc->hc; /* get card's information */ /* * set up starting descriptor by pulling that info from the DMA half * of the HD chip... */ wki = DMAC_RXCH(sc->scachan); wko = SRC_GET16(hc, hc->sca->dmac[wki].cda); cda = (sca_descriptor *)(hc->mem_start + (wko & hc->winmsk)); #if BUGGY > 1 printf("sr_packet_avail(): wki=%d, wko=%04x, cda=%08x\n", wki, wko, cda); #endif /* * open the appropriate memory window and set our expectations... */ if (hc->mempages) { SRC_SET_MEM(hc, sc->rxdesc); SRC_SET_ON(hc); } rxdesc = (sca_descriptor *) (hc->mem_start + (sc->rxdesc & hc->winmsk)); endp = rxdesc; rxdesc = &rxdesc[sc->rxhind]; endp = &endp[sc->rxmax]; *len = 0; /* reset result total length */ granules = 0; /* reset count of granules */ /* * This loop will scan descriptors, but it *will* puke up if we wrap * around to our starting point... */ while (rxdesc != cda) { *len += rxdesc->len; /* increment result length */ granules++; /* * If we hit a valid packet's completion we'll know we've * got a live one, and that we can deliver the packet. * Since we're only allowed to report a packet available, * somebody else does that... */ if (rxdesc->stat & SCA_DESC_EOM) { /* End Of Message */ *rxstat = rxdesc->stat; /* return closing */ #if BUGGY > 0 printf("sr%d: PKT AVAIL len %d, %x, bufs %u.\n", sc->unit, *len, *rxstat, granules); #endif return 1; /* indicate success */ } /* * OK, this packet take up multiple granules. Move on to * the next descriptor so we can consider it... */ rxdesc++; if (rxdesc == endp) /* recognize & act on wrap point */ rxdesc = (sca_descriptor *) (hc->mem_start + (sc->rxdesc & hc->winmsk)); } /* * Nothing found in the DPRAM. Let the caller know... */ *len = 0; *rxstat = 0; return 0; } /* * Copy a packet from the on card memory into a provided mbuf. * Take into account that buffers wrap and that a packet may * be larger than a buffer. */ static void sr_copy_rxbuf(struct mbuf *m, struct sr_softc *sc, int len) { struct sr_hardc *hc; sca_descriptor *rxdesc; u_int rxdata; u_int rxmax; u_int off = 0; u_int tlen; #if BUGGY > 0 printf("sr_copy_rxbuf(m=%08x,sc=%08x,len=%d)\n", m, sc, len); #endif hc = sc->hc; rxdata = sc->rxstart + (sc->rxhind * SR_BUF_SIZ); rxmax = sc->rxstart + (sc->rxmax * SR_BUF_SIZ); rxdesc = (sca_descriptor *) (hc->mem_start + (sc->rxdesc & hc->winmsk)); rxdesc = &rxdesc[sc->rxhind]; /* * Using the count of bytes in the received packet, we decrement it * for each granule (controller by an SCA descriptor) to control the * looping... */ while (len) { /* * tlen gets the length of *this* granule... ...which is * then copied to the target buffer. */ tlen = (len < SR_BUF_SIZ) ? len : SR_BUF_SIZ; if (hc->mempages) SRC_SET_MEM(hc, rxdata); bcopy(hc->mem_start + (rxdata & hc->winmsk), mtod(m, caddr_t) +off, tlen); off += tlen; len -= tlen; /* * now, return to the descriptor's window in DPRAM and reset * the descriptor we've just suctioned... */ if (hc->mempages) SRC_SET_MEM(hc, sc->rxdesc); rxdesc->len = 0; rxdesc->stat = 0xff; /* * Move on to the next granule. If we've any remaining * bytes to process we'll just continue in our loop... */ rxdata += SR_BUF_SIZ; rxdesc++; if (rxdata == rxmax) { /* handle the wrap point */ rxdata = sc->rxstart; rxdesc = (sca_descriptor *) (hc->mem_start + (sc->rxdesc & hc->winmsk)); } } } /* * If single is set, just eat a packet. Otherwise eat everything up to * where cda points. Update pointers to point to the next packet. * * This handles "flushing" of a packet as received... * * If the "single" parameter is zero, all pending reeceive traffic will * be flushed out of existence. A non-zero value will only drop the * *next* (currently) pending packet... */ static void sr_eat_packet(struct sr_softc *sc, int single) { struct sr_hardc *hc; sca_descriptor *rxdesc; /* current descriptor being eval'd */ sca_descriptor *endp; /* last descriptor in chain */ sca_descriptor *cda; /* current start point */ u_int loopcnt = 0; /* count of packets flushed ??? */ u_char stat; /* captured status byte from descr */ hc = sc->hc; cda = (sca_descriptor *)(hc->mem_start + (SRC_GET16(hc, hc->sca->dmac[DMAC_RXCH(sc->scachan)].cda) & hc->winmsk)); /* * loop until desc->stat == (0xff || EOM) Clear the status and * length in the descriptor. Increment the descriptor. */ if (hc->mempages) SRC_SET_MEM(hc, sc->rxdesc); rxdesc = (sca_descriptor *) (hc->mem_start + (sc->rxdesc & hc->winmsk)); endp = rxdesc; rxdesc = &rxdesc[sc->rxhind]; endp = &endp[sc->rxmax]; /* * allow loop, but abort it if we wrap completely... */ while (rxdesc != cda) { loopcnt++; if (loopcnt > sc->rxmax) { printf("sr%d: eat pkt %d loop, cda %p, " "rxdesc %p, stat %x.\n", sc->unit, loopcnt, cda, rxdesc, rxdesc->stat); break; } stat = rxdesc->stat; rxdesc->len = 0; rxdesc->stat = 0xff; rxdesc++; sc->rxhind++; if (rxdesc == endp) { rxdesc = (sca_descriptor *) (hc->mem_start + (sc->rxdesc & hc->winmsk)); sc->rxhind = 0; } if (single && (stat == SCA_DESC_EOM)) break; } /* * Update the eda to the previous descriptor. */ rxdesc = (sca_descriptor *)(uintptr_t)sc->rxdesc; rxdesc = &rxdesc[(sc->rxhind + sc->rxmax - 2) % sc->rxmax]; SRC_PUT16(hc, hc->sca->dmac[DMAC_RXCH(sc->scachan)].eda, (u_short)(((uintptr_t)rxdesc + hc->mem_pstart) & 0xffff)); } /* * While there is packets available in the rx buffer, read them out * into mbufs and ship them off. */ static void sr_get_packets(struct sr_softc *sc) { u_char rxstat; /* acquired status byte */ int i; int pkts; /* count of packets found */ int rxndx; /* rcv buffer index */ int tries; /* settling time counter */ u_int len; /* length of pending packet */ struct sr_hardc *hc; /* card-level information */ sca_descriptor *rxdesc; /* descriptor in memory */ #ifndef NETGRAPH struct ifnet *ifp; /* network intf ctl table */ #else int error; #endif /* NETGRAPH */ struct mbuf *m = NULL; /* message buffer */ #if BUGGY > 0 printf("sr_get_packets(sc=%08x)\n", sc); #endif hc = sc->hc; #ifndef NETGRAPH ifp = &sc->ifsppp.pp_if; #endif /* NETGRAPH */ if (hc->mempages) { SRC_SET_MEM(hc, sc->rxdesc); SRC_SET_ON(hc); /* enable shared memory */ } pkts = 0; /* reset count of found packets */ /* * for each complete packet in the receiving pool, process each * packet... */ while (sr_packet_avail(sc, &len, &rxstat)) { /* packet pending? */ /* * I have seen situations where we got the interrupt but the * status value wasn't deposited. This code should allow * the status byte's value to settle... */ tries = 5; while ((rxstat == 0x00ff) && --tries) sr_packet_avail(sc, &len, &rxstat); #if BUGGY > 1 printf("sr_packet_avail() returned len=%d, rxstat=%02ux\n", len, rxstat); #endif pkts++; #ifdef NETGRAPH sc->inbytes += len; sc->inlast = 0; #endif /* NETGRAPH */ /* * OK, we've settled the incoming message status. We can now * process it... */ if (((rxstat & SCA_DESC_ERRORS) == 0) && (len < MCLBYTES)) { #if BUGGY > 1 printf("sr%d: sr_get_packet() rxstat=%02x, len=%d\n", sc->unit, rxstat, len); #endif MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { /* * eat (flush) packet if get mbuf fail!! */ sr_eat_packet(sc, 1); continue; } /* * construct control information for pass-off */ #ifndef NETGRAPH m->m_pkthdr.rcvif = ifp; #else m->m_pkthdr.rcvif = NULL; #endif /* NETGRAPH */ m->m_pkthdr.len = m->m_len = len; if (len > MHLEN) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { /* * We couldn't get a big enough * message packet, so we'll send the * packet to /dev/null... */ m_freem(m); sr_eat_packet(sc, 1); continue; } } /* * OK, we've got a good message buffer. Now we can * copy the received message into it */ sr_copy_rxbuf(m, sc, len); /* copy from DPRAM */ #ifndef NETGRAPH BPF_MTAP(ifp, m); #if BUGGY > 3 { u_char *bp; bp = (u_char *)m; printf("sr%d: rcvd=%02x%02x%02x%02x%02x%02x\n", sc->unit, bp[0], bp[1], bp[2], bp[4], bp[5], bp[6]); } #endif sppp_input(ifp, m); ifp->if_ipackets++; #else /* NETGRAPH */ #if BUGGY > 3 { u_char *bp; bp = mtod(m,u_char *); printf("sr%d: rd=%02x:%02x:%02x:%02x:%02x:%02x", sc->unit, bp[0], bp[1], bp[2], bp[4], bp[5], bp[6]); printf(":%02x:%02x:%02x:%02x:%02x:%02x\n", bp[6], bp[7], bp[8], bp[9], bp[10], bp[11]); } #endif NG_SEND_DATA_ONLY(error, sc->hook, m); sc->ipackets++; #endif /* NETGRAPH */ /* * Update the eda to the previous descriptor. */ i = (len + SR_BUF_SIZ - 1) / SR_BUF_SIZ; sc->rxhind = (sc->rxhind + i) % sc->rxmax; rxdesc = (sca_descriptor *)(uintptr_t)sc->rxdesc; rxndx = (sc->rxhind + sc->rxmax - 2) % sc->rxmax; rxdesc = &rxdesc[rxndx]; SRC_PUT16(hc, hc->sca->dmac[DMAC_RXCH(sc->scachan)].eda, (u_short)(((uintptr_t)rxdesc + hc->mem_pstart) & 0xffff)); } else { int got_st3, got_cda, got_eda; int tries = 5; while ((rxstat == 0xff) && --tries) sr_packet_avail(sc, &len, &rxstat); /* * It look like we get an interrupt early * sometimes and then the status is not * filled in yet. */ if (tries && (tries != 5)) continue; /* * This chunk of code handles the error packets. * We'll log them for posterity... */ sr_eat_packet(sc, 1); #ifndef NETGRAPH ifp->if_ierrors++; #else sc->ierrors[0]++; #endif /* NETGRAPH */ got_st3 = SRC_GET8(hc, hc->sca->msci[sc->scachan].st3); got_cda = SRC_GET16(hc, hc->sca->dmac[DMAC_RXCH(sc->scachan)].cda); got_eda = SRC_GET16(hc, hc->sca->dmac[DMAC_RXCH(sc->scachan)].eda); #if BUGGY > 0 printf("sr%d: Receive error chan %d, " "stat %02x, msci st3 %02x," "rxhind %d, cda %04x, eda %04x.\n", sc->unit, sc->scachan, rxstat, got_st3, sc->rxhind, got_cda, got_eda); #endif } } #if BUGGY > 0 printf("sr%d: sr_get_packets() found %d packet(s)\n", sc->unit, pkts); #endif if (hc->mempages) SRC_SET_OFF(hc); } /* * All DMA interrupts come here. * * Each channel has two interrupts. * Interrupt A for errors and Interrupt B for normal stuff like end * of transmit or receive dmas. */ static void sr_dmac_intr(struct sr_hardc *hc, u_char isr1) { u_char dsr; /* contents of DMA Stat Reg */ u_char dotxstart; /* enables for tranmit part */ int mch; /* channel being processed */ struct sr_softc *sc; /* channel's softc structure */ sca_regs *sca = hc->sca; dmac_channel *dmac; /* dma structure of chip */ #if BUGGY > 0 printf("sr_dmac_intr(hc=%08x,isr1=%04x)\n", hc, isr1); #endif mch = 0; /* assume chan0 on card */ dotxstart = isr1; /* copy for xmitter starts */ /* * Shortcut if there is no interrupts for dma channel 0 or 1. * Skip processing for channel 0 if no incoming hit */ if ((isr1 & 0x0F) == 0) { mch = 1; isr1 >>= 4; } do { sc = &hc->sc[mch]; /* * Transmit channel - DMA Status Register Evaluation */ if (isr1 & 0x0C) { dmac = &sca->dmac[DMAC_TXCH(mch)]; /* * get the DMA Status Register contents and write * back to reset interrupt... */ dsr = SRC_GET8(hc, dmac->dsr); SRC_PUT8(hc, dmac->dsr, dsr); /* * Check for (& process) a Counter overflow */ if (dsr & SCA_DSR_COF) { printf("sr%d: TX DMA Counter overflow, " "txpacket no %lu.\n", #ifndef NETGRAPH sc->unit, sc->ifsppp.pp_if.if_opackets); sc->ifsppp.pp_if.if_oerrors++; #else sc->unit, sc->opackets); sc->oerrors++; #endif /* NETGRAPH */ } /* * Check for (& process) a Buffer overflow */ if (dsr & SCA_DSR_BOF) { printf("sr%d: TX DMA Buffer overflow, " "txpacket no %lu, dsr %02x, " "cda %04x, eda %04x.\n", #ifndef NETGRAPH sc->unit, sc->ifsppp.pp_if.if_opackets, #else sc->unit, sc->opackets, #endif /* NETGRAPH */ dsr, SRC_GET16(hc, dmac->cda), SRC_GET16(hc, dmac->eda)); #ifndef NETGRAPH sc->ifsppp.pp_if.if_oerrors++; #else sc->oerrors++; #endif /* NETGRAPH */ } /* * Check for (& process) an End of Transfer (OK) */ if (dsr & SCA_DSR_EOT) { /* * This should be the most common case. * * Clear the IFF_OACTIVE flag. * * Call srstart to start a new transmit if * there is data to transmit. */ #if BUGGY > 0 printf("sr%d: TX Completed OK\n", sc->unit); #endif sc->xmit_busy = 0; #ifndef NETGRAPH sc->ifsppp.pp_if.if_flags &= ~IFF_OACTIVE; sc->ifsppp.pp_if.if_timer = 0; #else /* XXX may need to mark tx inactive? */ sc->out_deficit++; sc->out_dog = DOG_HOLDOFF; #endif /* NETGRAPH */ if (sc->txb_inuse && --sc->txb_inuse) sr_xmit(sc); } } /* * Receive channel processing of DMA Status Register */ if (isr1 & 0x03) { dmac = &sca->dmac[DMAC_RXCH(mch)]; dsr = SRC_GET8(hc, dmac->dsr); SRC_PUT8(hc, dmac->dsr, dsr); /* * End of frame processing (MSG OK?) */ if (dsr & SCA_DSR_EOM) { #if BUGGY > 0 int tt, ind; #ifndef NETGRAPH tt = sc->ifsppp.pp_if.if_ipackets; #else /* NETGRAPH */ tt = sc->ipackets; #endif /* NETGRAPH */ ind = sc->rxhind; #endif sr_get_packets(sc); #if BUGGY > 0 #ifndef NETGRAPH if (tt == sc->ifsppp.pp_if.if_ipackets) #else /* NETGRAPH */ if (tt == sc->ipackets) #endif /* NETGRAPH */ { sca_descriptor *rxdesc; int i; printf("SR: RXINTR isr1 %x, dsr %x, " "no data %d pkts, orxind %d.\n", dotxstart, dsr, tt, ind); printf("SR: rxdesc %x, rxstart %x, " "rxend %x, rxhind %d, " "rxmax %d.\n", sc->rxdesc, sc->rxstart, sc->rxend, sc->rxhind, sc->rxmax); printf("SR: cda %x, eda %x.\n", SRC_GET16(hc, dmac->cda), SRC_GET16(hc, dmac->eda)); if (hc->mempages) { SRC_SET_ON(hc); SRC_SET_MEM(hc, sc->rxdesc); } rxdesc = (sca_descriptor *) (hc->mem_start + (sc->rxdesc & hc->winmsk)); rxdesc = &rxdesc[sc->rxhind]; for (i = 0; i < 3; i++, rxdesc++) printf("SR: rxdesc->stat %x, " "len %d.\n", rxdesc->stat, rxdesc->len); if (hc->mempages) SRC_SET_OFF(hc); } #endif /* BUGGY */ } /* * Check for Counter overflow */ if (dsr & SCA_DSR_COF) { printf("sr%d: RX DMA Counter overflow, " "rxpkts %lu.\n", #ifndef NETGRAPH sc->unit, sc->ifsppp.pp_if.if_ipackets); sc->ifsppp.pp_if.if_ierrors++; #else /* NETGRAPH */ sc->unit, sc->ipackets); sc->ierrors[1]++; #endif /* NETGRAPH */ } /* * Check for Buffer overflow */ if (dsr & SCA_DSR_BOF) { printf("sr%d: RX DMA Buffer overflow, " "rxpkts %lu, rxind %d, " "cda %x, eda %x, dsr %x.\n", #ifndef NETGRAPH sc->unit, sc->ifsppp.pp_if.if_ipackets, #else /* NETGRAPH */ sc->unit, sc->ipackets, #endif /* NETGRAPH */ sc->rxhind, SRC_GET16(hc, dmac->cda), SRC_GET16(hc, dmac->eda), dsr); /* * Make sure we eat as many as possible. * Then get the system running again. */ if (hc->mempages) SRC_SET_ON(hc); sr_eat_packet(sc, 0); #ifndef NETGRAPH sc->ifsppp.pp_if.if_ierrors++; #else /* NETGRAPH */ sc->ierrors[2]++; #endif /* NETGRAPH */ SRC_PUT8(hc, sca->msci[mch].cmd, SCA_CMD_RXMSGREJ); SRC_PUT8(hc, dmac->dsr, SCA_DSR_DE); #if BUGGY > 0 printf("sr%d: RX DMA Buffer overflow, " "rxpkts %lu, rxind %d, " "cda %x, eda %x, dsr %x. After\n", sc->unit, #ifndef NETGRAPH sc->ipackets, #else /* NETGRAPH */ sc->ifsppp.pp_if.if_ipackets, #endif /* NETGRAPH */ sc->rxhind, SRC_GET16(hc, dmac->cda), SRC_GET16(hc, dmac->eda), SRC_GET8(hc, dmac->dsr)); #endif if (hc->mempages) SRC_SET_OFF(hc); } /* * End of Transfer */ if (dsr & SCA_DSR_EOT) { /* * If this happen, it means that we are * receiving faster than what the processor * can handle. * * XXX We should enable the dma again. */ printf("sr%d: RX End of xfer, rxpkts %lu.\n", sc->unit, #ifndef NETGRAPH sc->ifsppp.pp_if.if_ipackets); sc->ifsppp.pp_if.if_ierrors++; #else sc->ipackets); sc->ierrors[3]++; #endif /* NETGRAPH */ } } isr1 >>= 4; /* process next half of ISR */ mch++; /* and move to next channel */ } while ((mch < NCHAN) && isr1); /* loop for each chn */ /* * Now that we have done all the urgent things, see if we can fill * the transmit buffers. */ for (mch = 0; mch < NCHAN; mch++) { if (dotxstart & 0x0C) { /* TX initiation enabled? */ sc = &hc->sc[mch]; #ifndef NETGRAPH srstart(&sc->ifsppp.pp_if); #else srstart(sc); #endif /* NETGRAPH */ } dotxstart >>= 4;/* shift for next channel */ } } #ifndef NETGRAPH #ifdef USE_MODEMCK /* * Perform timeout on an FR channel * * Establish a periodic check of open N2 ports; If * a port is open/active, its DCD state is checked * and a loss of DCD is recognized (and eventually * processed). */ static void sr_modemck(void *arg) { u_int s; int card; /* card index in table */ int cards; /* card list index */ int mch; /* channel on card */ u_char dcd_v; /* Data Carrier Detect */ u_char got_st0; /* contents of ST0 */ u_char got_st1; /* contents of ST1 */ u_char got_st2; /* contents of ST2 */ u_char got_st3; /* contents of ST3 */ struct sr_hardc *hc; /* card's configuration */ struct sr_hardc *Card[16];/* up to 16 cards in system */ struct sr_softc *sc; /* channel's softc structure */ struct ifnet *ifp; /* interface control table */ msci_channel *msci; /* regs specific to channel */ s = splimp(); #if 0 if (sr_opens == 0) { /* count of "up" channels */ sr_watcher = 0; /* indicate no watcher */ splx(s); return; } #endif sr_watcher = 1; /* mark that we're online */ /* * Now we'll need a list of cards to process. Since we can handle * both ISA and PCI cards (and I didn't think of making this logic * global YET) we'll generate a single table of card table * addresses. */ cards = 0; for (card = 0; card < NSR; card++) { hc = &sr_hardc[card]; if (hc->sc == (void *)0) continue; Card[cards++] = hc; } hc = sr_hardc_pci; while (hc) { Card[cards++] = hc; hc = hc->next; } /* * OK, we've got work we can do. Let's do it... (Please note that * this code _only_ deals w/ ISA cards) */ for (card = 0; card < cards; card++) { hc = Card[card];/* get card table */ for (mch = 0; mch < hc->numports; mch++) { sc = &hc->sc[mch]; ifp = &sc->ifsppp.pp_if; /* * if this channel isn't "up", skip it */ if ((ifp->if_flags & IFF_UP) == 0) continue; /* * OK, now we can go looking at this channel's * actual register contents... */ msci = &hc->sca->msci[sc->scachan]; /* * OK, now we'll look into the actual status of this * channel... * * I suck in more registers than strictly needed */ got_st0 = SRC_GET8(hc, msci->st0); got_st1 = SRC_GET8(hc, msci->st1); got_st2 = SRC_GET8(hc, msci->st2); got_st3 = SRC_GET8(hc, msci->st3); /* * We want to see if the DCD signal is up (DCD is * true if zero) */ dcd_v = (got_st3 & SCA_ST3_DCD) == 0; if (dcd_v == 0) printf("sr%d: DCD lost\n", sc->unit); } } /* * OK, now set up for the next modem signal checking pass... */ timeout(sr_modemck, NULL, hz); splx(s); } #endif #else /* NETGRAPH */ /* * If a port is open/active, it's DCD state is checked * and a loss of DCD is recognized (and eventually processed?). */ static void sr_modemck(struct sr_softc *sc ) { u_int s; u_char got_st3; /* contents of ST3 */ struct sr_hardc *hc = sc->hc; /* card's configuration */ msci_channel *msci; /* regs specific to channel */ s = splimp(); if (sc->running == 0) return; /* * OK, now we can go looking at this channel's register contents... */ msci = &hc->sca->msci[sc->scachan]; got_st3 = SRC_GET8(hc, msci->st3); /* * We want to see if the DCD signal is up (DCD is true if zero) */ sc->dcd = (got_st3 & SCA_ST3_DCD) == 0; splx(s); } #endif /* NETGRAPH */ static void sr_msci_intr(struct sr_hardc *hc, u_char isr0) { printf("src%d: SRINTR: MSCI\n", hc->cunit); } static void sr_timer_intr(struct sr_hardc *hc, u_char isr2) { printf("src%d: SRINTR: TIMER\n", hc->cunit); } #ifdef NETGRAPH /***************************************** * Device timeout/watchdog routine. * called once per second. * checks to see that if activity was expected, that it hapenned. * At present we only look to see if expected output was completed. */ static void ngsr_watchdog_frame(void * arg) { struct sr_softc * sc = arg; int s; int speed; if (sc->running == 0) return; /* if we are not running let timeouts die */ /* * calculate the apparent throughputs * XXX a real hack */ s = splimp(); speed = sc->inbytes - sc->lastinbytes; sc->lastinbytes = sc->inbytes; if ( sc->inrate < speed ) sc->inrate = speed; speed = sc->outbytes - sc->lastoutbytes; sc->lastoutbytes = sc->outbytes; if ( sc->outrate < speed ) sc->outrate = speed; sc->inlast++; splx(s); if ((sc->inlast > QUITE_A_WHILE) && (sc->out_deficit > LOTS_OF_PACKETS)) { log(LOG_ERR, "sr%d: No response from remote end\n", sc->unit); s = splimp(); sr_down(sc); sr_up(sc); sc->inlast = sc->out_deficit = 0; splx(s); } else if ( sc->xmit_busy ) { /* no TX -> no TX timeouts */ if (sc->out_dog == 0) { log(LOG_ERR, "sr%d: Transmit failure.. no clock?\n", sc->unit); s = splimp(); srwatchdog(sc); #if 0 sr_down(sc); sr_up(sc); #endif splx(s); sc->inlast = sc->out_deficit = 0; } else { sc->out_dog--; } } sr_modemck(sc); /* update the DCD status */ sc->handle = timeout(ngsr_watchdog_frame, sc, hz); } /*********************************************************************** * This section contains the methods for the Netgraph interface ***********************************************************************/ /* * It is not possible or allowable to create a node of this type. * If the hardware exists, it will already have created it. */ static int ngsr_constructor(node_p node) { return (EINVAL); } /* * give our ok for a hook to be added... * If we are not running this should kick the device into life. * The hook's private info points to our stash of info about that * channel. */ static int ngsr_newhook(node_p node, hook_p hook, const char *name) { struct sr_softc * sc = NG_NODE_PRIVATE(node); /* * check if it's our friend the debug hook */ if (strcmp(name, NG_SR_HOOK_DEBUG) == 0) { NG_HOOK_SET_PRIVATE(hook, NULL); /* paranoid */ sc->debug_hook = hook; return (0); } /* * Check for raw mode hook. */ if (strcmp(name, NG_SR_HOOK_RAW) != 0) { return (EINVAL); } NG_HOOK_SET_PRIVATE(hook, sc); sc->hook = hook; sc->datahooks++; sr_up(sc); return (0); } /* * incoming messages. * Just respond to the generic TEXT_STATUS message */ static int ngsr_rcvmsg(node_p node, item_p item, hook_p lasthook) { struct sr_softc * sc; struct ng_mesg *resp = NULL; int error = 0; struct ng_mesg *msg; NGI_GET_MSG(item,msg); sc = NG_NODE_PRIVATE(node); switch (msg->header.typecookie) { case NG_SR_COOKIE: error = EINVAL; break; case NGM_GENERIC_COOKIE: switch(msg->header.cmd) { case NGM_TEXT_STATUS: { char *arg; int pos = 0; int resplen = sizeof(struct ng_mesg) + 512; NG_MKRESPONSE(resp, msg, resplen, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } arg = (resp)->data; pos = sprintf(arg, "%ld bytes in, %ld bytes out\n" "highest rate seen: %ld B/S in, %ld B/S out\n", sc->inbytes, sc->outbytes, sc->inrate, sc->outrate); pos += sprintf(arg + pos, "%ld output errors\n", sc->oerrors); pos += sprintf(arg + pos, "ierrors = %ld, %ld, %ld, %ld, %ld, %ld\n", sc->ierrors[0], sc->ierrors[1], sc->ierrors[2], sc->ierrors[3], sc->ierrors[4], sc->ierrors[5]); resp->header.arglen = pos + 1; break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } /* Take care of synchronous response, if any */ NG_RESPOND_MSG(error, node, item, resp); NG_FREE_MSG(msg); return (error); } /* * get data from another node and transmit it to the correct channel */ static int ngsr_rcvdata(hook_p hook, item_p item) { int s; int error = 0; struct sr_softc * sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); struct ifqueue *xmitq_p; struct mbuf *m; - meta_p meta; + struct ng_tag_prio *ptag; NGI_GET_M(item, m); - NGI_GET_META(item, meta); NG_FREE_ITEM(item); /* * data doesn't come in from just anywhere (e.g control hook) */ if ( NG_HOOK_PRIVATE(hook) == NULL) { error = ENETDOWN; goto bad; } /* * Now queue the data for when it can be sent */ - if (meta && meta->priority > 0) { + if ((ptag = (struct ng_tag_prio *)m_tag_locate(m, NGM_GENERIC_COOKIE, + NG_TAG_PRIO, NULL)) != NULL && (ptag->priority > NG_PRIO_CUTOFF) ) xmitq_p = (&sc->xmitq_hipri); - } else { + else xmitq_p = (&sc->xmitq); - } + s = splimp(); IF_LOCK(xmitq_p); if (_IF_QFULL(xmitq_p)) { _IF_DROP(xmitq_p); IF_UNLOCK(xmitq_p); splx(s); error = ENOBUFS; goto bad; } _IF_ENQUEUE(xmitq_p, m); IF_UNLOCK(xmitq_p); srstart(sc); splx(s); return (0); bad: /* * It was an error case. * check if we need to free the mbuf, and then return the error */ NG_FREE_M(m); - NG_FREE_META(meta); return (error); } /* * do local shutdown processing.. * this node will refuse to go away, unless the hardware says to.. * don't unref the node, or remove our name. just clear our links up. */ static int ngsr_shutdown(node_p node) { struct sr_softc * sc = NG_NODE_PRIVATE(node); sr_down(sc); NG_NODE_UNREF(node); /* XXX should drain queues! */ if (ng_make_node_common(&typestruct, &sc->node) != 0) return (0); sprintf(sc->nodename, "%s%d", NG_SR_NODE_TYPE, sc->unit); if (ng_name_node(sc->node, sc->nodename)) { printf("node naming failed\n"); sc->node = NULL; NG_NODE_UNREF(sc->node); /* drop it again */ return (0); } NG_NODE_SET_PRIVATE(sc->node, sc); callout_handle_init(&sc->handle); /* should kill timeout */ sc->running = 0; return (0); } /* already linked */ static int ngsr_connect(hook_p hook) { /* probably not at splnet, force outward queueing */ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); /* be really amiable and just say "YUP that's OK by me! " */ return (0); } /* * notify on hook disconnection (destruction) * * Invalidate the private data associated with this dlci. * For this type, removal of the last link resets tries to destroy the node. * As the device still exists, the shutdown method will not actually * destroy the node, but reset the device and leave it 'fresh' :) * * The node removal code will remove all references except that owned by the * driver. */ static int ngsr_disconnect(hook_p hook) { struct sr_softc * sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); int s; /* * If it's the data hook, then free resources etc. */ if (NG_HOOK_PRIVATE(hook)) { s = splimp(); sc->datahooks--; if (sc->datahooks == 0) sr_down(sc); splx(s); } else { sc->debug_hook = NULL; } return (0); } /* * called during bootup * or LKM loading to put this type into the list of known modules */ static void ngsr_init(void *ignored) { if (ng_newtype(&typestruct)) printf("ngsr install failed\n"); ngsr_done_init = 1; } #endif /* NETGRAPH */ /* ********************************* END ************************************ */ Index: head/sys/dev/usb/udbp.c =================================================================== --- head/sys/dev/usb/udbp.c (revision 131107) +++ head/sys/dev/usb/udbp.c (revision 131108) @@ -1,843 +1,843 @@ /* * Copyright (c) 1996-2000 Whistle Communications, Inc. * All rights reserved. * * 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. Neither the name of author nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY NICK HIBMA AND CONTRIBUTORS * ``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 OR CONTRIBUTORS * 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. * */ #include __FBSDID("$FreeBSD$"); /* Driver for arbitrary double bulk pipe devices. * The driver assumes that there will be the same driver on the other side. * * XXX Some more information on what the framing of the IP packets looks like. * * To take full advantage of bulk transmission, packets should be chosen * between 1k and 5k in size (1k to make sure the sending side starts * straming, and <5k to avoid overflowing the system with small TDs). */ /* probe/attach/detach: * Connect the driver to the hardware and netgraph * * udbp_setup_out_transfer(sc); * Setup an outbound transfer. Only one transmit can be active at the same * time. * XXX If it is required that the driver is able to queue multiple requests * let me know. That is slightly difficult, due to the fact that we * cannot call usbd_alloc_xfer in int context. * * udbp_setup_in_transfer(sc) * Prepare an in transfer that will be waiting for data to come in. It * is submitted and sits there until data is available. * The callback resubmits a new transfer on completion. * * The reason we submit a bulk in transfer is that USB does not know about * interrupts. The bulk transfer continuously polls the device for data. * While the device has no data available, the device NAKs the TDs. As soon * as there is data, the transfer happens and the data comes flowing in. * * In case you were wondering, interrupt transfers happen exactly that way. * It therefore doesn't make sense to use the interrupt pipe to signal * 'data ready' and then schedule a bulk transfer to fetch it. That would * incur a 2ms delay at least, without reducing bandwidth requirements. * */ #include #include #include #include #include #include #include #include #if __FreeBSD_version >= 500014 #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USB_DEBUG #define DPRINTF(x) if (udbpdebug) logprintf x #define DPRINTFN(n,x) if (udbpdebug>(n)) logprintf x int udbpdebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, udbp, CTLFLAG_RW, 0, "USB udbp"); SYSCTL_INT(_hw_usb_udbp, OID_AUTO, debug, CTLFLAG_RW, &udbpdebug, 0, "udbp debug level"); #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define MS_TO_TICKS(ms) ((ms) * hz / 1000) #define UDBP_TIMEOUT 2000 /* timeout on outbound transfers, in msecs */ #define UDBP_BUFFERSIZE 2048 /* maximum number of bytes in one transfer */ struct udbp_softc { device_t sc_dev; /* base device */ usbd_interface_handle sc_iface; usbd_pipe_handle sc_bulkin_pipe; int sc_bulkin; usbd_xfer_handle sc_bulkin_xfer; void *sc_bulkin_buffer; int sc_bulkin_bufferlen; int sc_bulkin_datalen; usbd_pipe_handle sc_bulkout_pipe; int sc_bulkout; usbd_xfer_handle sc_bulkout_xfer; void *sc_bulkout_buffer; int sc_bulkout_bufferlen; int sc_bulkout_datalen; int flags; # define DISCONNECTED 0x01 # define OUT_BUSY 0x02 # define NETGRAPH_INITIALISED 0x04 node_p node; /* back pointer to node */ hook_p hook; /* pointer to the hook */ u_int packets_in; /* packets in from downstream */ u_int packets_out; /* packets out towards downstream */ struct ifqueue xmitq_hipri; /* hi-priority transmit queue */ struct ifqueue xmitq; /* low-priority transmit queue */ }; typedef struct udbp_softc *udbp_p; Static ng_constructor_t ng_udbp_constructor; Static ng_rcvmsg_t ng_udbp_rcvmsg; Static ng_shutdown_t ng_udbp_rmnode; Static ng_newhook_t ng_udbp_newhook; Static ng_connect_t ng_udbp_connect; Static ng_rcvdata_t ng_udbp_rcvdata; Static ng_disconnect_t ng_udbp_disconnect; /* Parse type for struct ngudbpstat */ Static const struct ng_parse_struct_field ng_udbp_stat_type_fields[] = NG_UDBP_STATS_TYPE_INFO; Static const struct ng_parse_type ng_udbp_stat_type = { &ng_parse_struct_type, &ng_udbp_stat_type_fields }; /* List of commands and how to convert arguments to/from ASCII */ Static const struct ng_cmdlist ng_udbp_cmdlist[] = { { NGM_UDBP_COOKIE, NGM_UDBP_GET_STATUS, "getstatus", NULL, &ng_udbp_stat_type, }, { NGM_UDBP_COOKIE, NGM_UDBP_SET_FLAG, "setflag", &ng_parse_int32_type, NULL }, { 0 } }; /* Netgraph node type descriptor */ Static struct ng_type ng_udbp_typestruct = { .version = NG_ABI_VERSION, .name = NG_UDBP_NODE_TYPE, .constructor = ng_udbp_constructor, .rcvmsg = ng_udbp_rcvmsg, .shutdown = ng_udbp_rmnode, .newhook = ng_udbp_newhook, .connect = ng_udbp_connect, .rcvdata = ng_udbp_rcvdata, .disconnect = ng_udbp_disconnect, .cmdlist = ng_udbp_cmdlist, }; Static int udbp_setup_in_transfer (udbp_p sc); Static void udbp_in_transfer_cb (usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status err); Static int udbp_setup_out_transfer (udbp_p sc); Static void udbp_out_transfer_cb (usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status err); USB_DECLARE_DRIVER(udbp); USB_MATCH(udbp) { USB_MATCH_START(udbp, uaa); usb_interface_descriptor_t *id; if (!uaa->iface) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); /* XXX Julian, add the id of the device if you have one to test * things with. run 'usbdevs -v' and note the 3 ID's that appear. * The Vendor Id and Product Id are in hex and the Revision Id is in * bcd. But as usual if the revision is 0x101 then you should compare * the revision id in the device descriptor with 0x101 * Or go search the file usbdevs.h. Maybe the device is already in * there. */ if ((uaa->vendor == USB_VENDOR_NETCHIP && uaa->product == USB_PRODUCT_NETCHIP_TURBOCONNECT)) return(UMATCH_VENDOR_PRODUCT); if ((uaa->vendor == USB_VENDOR_PROLIFIC && (uaa->product == USB_PRODUCT_PROLIFIC_PL2301 || uaa->product == USB_PRODUCT_PROLIFIC_PL2302))) return(UMATCH_VENDOR_PRODUCT); if ((uaa->vendor == USB_VENDOR_ANCHOR && uaa->product == USB_PRODUCT_ANCHOR_EZLINK)) return(UMATCH_VENDOR_PRODUCT); return (UMATCH_NONE); } USB_ATTACH(udbp) { USB_ATTACH_START(udbp, sc, uaa); usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed, *ed_bulkin = NULL, *ed_bulkout = NULL; usbd_status err; char devinfo[1024]; int i; static int ngudbp_done_init=0; sc->flags |= DISCONNECTED; /* fetch the interface handle for the first interface */ (void) usbd_device2interface_handle(uaa->device, 0, &iface); id = usbd_get_interface_descriptor(iface); usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), devinfo, id->bInterfaceClass, id->bInterfaceSubClass); /* Find the two first bulk endpoints */ for (i = 0 ; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(iface, i); if (!ed) { printf("%s: could not read endpoint descriptor\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { ed_bulkin = ed; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { ed_bulkout = ed; } if (ed_bulkin && ed_bulkout) /* found all we need */ break; } /* Verify that we goething sensible */ if (ed_bulkin == NULL || ed_bulkout == NULL) { printf("%s: bulk-in and/or bulk-out endpoint not found\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } if (ed_bulkin->wMaxPacketSize[0] != ed_bulkout->wMaxPacketSize[0] || ed_bulkin->wMaxPacketSize[1] != ed_bulkout->wMaxPacketSize[1]) { printf("%s: bulk-in and bulk-out have different packet sizes %d %d %d %d\n", USBDEVNAME(sc->sc_dev), ed_bulkin->wMaxPacketSize[0], ed_bulkout->wMaxPacketSize[0], ed_bulkin->wMaxPacketSize[1], ed_bulkout->wMaxPacketSize[1]); USB_ATTACH_ERROR_RETURN; } sc->sc_bulkin = ed_bulkin->bEndpointAddress; sc->sc_bulkout = ed_bulkout->bEndpointAddress; DPRINTF(("%s: Bulk-in: 0x%02x, bulk-out 0x%02x, packet size = %d\n", USBDEVNAME(sc->sc_dev), sc->sc_bulkin, sc->sc_bulkout, ed_bulkin->wMaxPacketSize[0])); /* Allocate the in transfer struct */ sc->sc_bulkin_xfer = usbd_alloc_xfer(uaa->device); if (!sc->sc_bulkin_xfer) { goto bad; } sc->sc_bulkout_xfer = usbd_alloc_xfer(uaa->device); if (!sc->sc_bulkout_xfer) { goto bad; } sc->sc_bulkin_buffer = malloc(UDBP_BUFFERSIZE, M_USBDEV, M_WAITOK); if (!sc->sc_bulkin_buffer) { goto bad; } sc->sc_bulkout_buffer = malloc(UDBP_BUFFERSIZE, M_USBDEV, M_WAITOK); if (!sc->sc_bulkout_xfer || !sc->sc_bulkout_buffer) { goto bad; } sc->sc_bulkin_bufferlen = UDBP_BUFFERSIZE; sc->sc_bulkout_bufferlen = UDBP_BUFFERSIZE; /* We have decided on which endpoints to use, now open the pipes */ err = usbd_open_pipe(iface, sc->sc_bulkin, USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe); if (err) { printf("%s: cannot open bulk-in pipe (addr %d)\n", USBDEVNAME(sc->sc_dev), sc->sc_bulkin); goto bad; } err = usbd_open_pipe(iface, sc->sc_bulkout, USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe); if (err) { printf("%s: cannot open bulk-out pipe (addr %d)\n", USBDEVNAME(sc->sc_dev), sc->sc_bulkout); goto bad; } if (!ngudbp_done_init){ ngudbp_done_init=1; if (ng_newtype(&ng_udbp_typestruct)) { printf("ngudbp install failed\n"); goto bad; } } if ((err = ng_make_node_common(&ng_udbp_typestruct, &sc->node)) == 0) { char nodename[128]; sprintf(nodename, "%s", USBDEVNAME(sc->sc_dev)); if ((err = ng_name_node(sc->node, nodename))) { NG_NODE_UNREF(sc->node); sc->node = NULL; goto bad; } else { NG_NODE_SET_PRIVATE(sc->node, sc); sc->xmitq.ifq_maxlen = IFQ_MAXLEN; sc->xmitq_hipri.ifq_maxlen = IFQ_MAXLEN; mtx_init(&sc->xmitq.ifq_mtx, "usb_xmitq", NULL, MTX_DEF); mtx_init(&sc->xmitq_hipri.ifq_mtx, "usb_xmitq_hipri", NULL, MTX_DEF); } } sc->flags = NETGRAPH_INITIALISED; /* sc->flags &= ~DISCONNECTED; */ /* XXX */ /* the device is now operational */ /* schedule the first incoming xfer */ err = udbp_setup_in_transfer(sc); if (err) { goto bad; } USB_ATTACH_SUCCESS_RETURN; bad: #if 0 /* probably done in udbp_detach() */ if (sc->sc_bulkout_buffer) { FREE(sc->sc_bulkout_buffer, M_USBDEV); } if (sc->sc_bulkin_buffer) { FREE(sc->sc_bulkin_buffer, M_USBDEV); } if (sc->sc_bulkout_xfer) { usbd_free_xfer(sc->sc_bulkout_xfer); } if (sc->sc_bulkin_xfer) { usbd_free_xfer(sc->sc_bulkin_xfer); } #endif udbp_detach(self); USB_ATTACH_ERROR_RETURN; } USB_DETACH(udbp) { USB_DETACH_START(udbp, sc); sc->flags |= DISCONNECTED; DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); if (sc->sc_bulkin_pipe) { usbd_abort_pipe(sc->sc_bulkin_pipe); usbd_close_pipe(sc->sc_bulkin_pipe); } if (sc->sc_bulkout_pipe) { usbd_abort_pipe(sc->sc_bulkout_pipe); usbd_close_pipe(sc->sc_bulkout_pipe); } if (sc->flags & NETGRAPH_INITIALISED) { ng_rmnode_self(sc->node); NG_NODE_SET_PRIVATE(sc->node, NULL); NG_NODE_UNREF(sc->node); sc->node = NULL; /* Paranoid */ } if (sc->sc_bulkin_xfer) usbd_free_xfer(sc->sc_bulkin_xfer); if (sc->sc_bulkout_xfer) usbd_free_xfer(sc->sc_bulkout_xfer); if (sc->sc_bulkin_buffer) free(sc->sc_bulkin_buffer, M_USBDEV); if (sc->sc_bulkout_buffer) free(sc->sc_bulkout_buffer, M_USBDEV); return 0; } Static int udbp_setup_in_transfer(udbp_p sc) { void *priv = sc; /* XXX this should probably be some pointer to * struct describing the transfer (mbuf?) * See also below. */ usbd_status err; /* XXX * How should we arrange for 2 extra bytes at the start of the * packet? */ /* Initialise a USB transfer and then schedule it */ (void) usbd_setup_xfer( sc->sc_bulkin_xfer, sc->sc_bulkin_pipe, priv, sc->sc_bulkin_buffer, sc->sc_bulkin_bufferlen, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, udbp_in_transfer_cb); err = usbd_transfer(sc->sc_bulkin_xfer); if (err && err != USBD_IN_PROGRESS) { DPRINTF(("%s: failed to setup in-transfer, %s\n", USBDEVNAME(sc->sc_dev), usbd_errstr(err))); return(err); } return (USBD_NORMAL_COMPLETION); } Static void udbp_in_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status err) { udbp_p sc = priv; /* XXX see priv above */ int s; int len; struct mbuf *m; if (err) { if (err != USBD_CANCELLED) { DPRINTF(("%s: bulk-out transfer failed: %s\n", USBDEVNAME(sc->sc_dev), usbd_errstr(err))); } else { /* USBD_CANCELLED happens at unload of the driver */ return; } /* Transfer has failed, packet is not received */ } else { len = xfer->actlen; s = splimp(); /* block network stuff too */ if (sc->hook) { /* get packet from device and send on */ m = m_devget(sc->sc_bulkin_buffer, len, 0, NULL, NULL); NG_SEND_DATA_ONLY(err, sc->hook, m); } splx(s); } /* schedule the next in transfer */ udbp_setup_in_transfer(sc); } Static int udbp_setup_out_transfer(udbp_p sc) { void *priv = sc; /* XXX this should probably be some pointer to * struct describing the transfer (mbuf?) * See also below. */ int pktlen; usbd_status err; int s, s1; struct mbuf *m; s = splusb(); if (sc->flags & OUT_BUSY) panic("out transfer already in use, we should add queuing"); sc->flags |= OUT_BUSY; splx(s); s1 = splimp(); /* Queueing happens at splnet */ IF_DEQUEUE(&sc->xmitq_hipri, m); if (m == NULL) { IF_DEQUEUE(&sc->xmitq, m); } splx(s1); if (!m) { sc->flags &= ~OUT_BUSY; return (USBD_NORMAL_COMPLETION); } pktlen = m->m_pkthdr.len; if (pktlen > sc->sc_bulkout_bufferlen) { printf("%s: Packet too large, %d > %d\n", USBDEVNAME(sc->sc_dev), pktlen, sc->sc_bulkout_bufferlen); return (USBD_IOERROR); } m_copydata(m, 0, pktlen, sc->sc_bulkout_buffer); m_freem(m); /* Initialise a USB transfer and then schedule it */ (void) usbd_setup_xfer( sc->sc_bulkout_xfer, sc->sc_bulkout_pipe, priv, sc->sc_bulkout_buffer, pktlen, USBD_SHORT_XFER_OK, UDBP_TIMEOUT, udbp_out_transfer_cb); err = usbd_transfer(sc->sc_bulkout_xfer); if (err && err != USBD_IN_PROGRESS) { DPRINTF(("%s: failed to setup out-transfer, %s\n", USBDEVNAME(sc->sc_dev), usbd_errstr(err))); return(err); } return (USBD_NORMAL_COMPLETION); } Static void udbp_out_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status err) { udbp_p sc = priv; /* XXX see priv above */ int s; if (err) { DPRINTF(("%s: bulk-out transfer failed: %s\n", USBDEVNAME(sc->sc_dev), usbd_errstr(err))); /* Transfer has failed, packet is not transmitted */ /* XXX Invalidate packet */ return; } /* packet has been transmitted */ s = splusb(); /* mark the buffer available */ sc->flags &= ~OUT_BUSY; udbp_setup_out_transfer(sc); splx(s); } DRIVER_MODULE(udbp, uhub, udbp_driver, udbp_devclass, usbd_driver_load, 0); MODULE_DEPEND(udbp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); /*********************************************************************** * Start of Netgraph methods **********************************************************************/ /* * If this is a device node so this work is done in the attach() * routine and the constructor will return EINVAL as you should not be able * to create nodes that depend on hardware (unless you can add the hardware :) */ Static int ng_udbp_constructor(node_p node) { return (EINVAL); } /* * Give our ok for a hook to be added... * If we are not running this might kick a device into life. * Possibly decode information out of the hook name. * Add the hook's private info to the hook structure. * (if we had some). In this example, we assume that there is a * an array of structs, called 'channel' in the private info, * one for each active channel. The private * pointer of each hook points to the appropriate UDBP_hookinfo struct * so that the source of an input packet is easily identified. */ Static int ng_udbp_newhook(node_p node, hook_p hook, const char *name) { const udbp_p sc = NG_NODE_PRIVATE(node); #if 0 /* Possibly start up the device if it's not already going */ if ((sc->flags & SCF_RUNNING) == 0) { ng_udbp_start_hardware(sc); } #endif if (strcmp(name, NG_UDBP_HOOK_NAME) == 0) { sc->hook = hook; NG_HOOK_SET_PRIVATE(hook, NULL); } else { return (EINVAL); /* not a hook we know about */ } return(0); } /* * Get a netgraph control message. * Check it is one we understand. If needed, send a response. * We could save the address for an async action later, but don't here. * Always free the message. * The response should be in a malloc'd region that the caller can 'free'. * A response is not required. * Theoretically you could respond defferently to old message types if * the cookie in the header didn't match what we consider to be current * (so that old userland programs could continue to work). */ Static int ng_udbp_rcvmsg(node_p node, item_p item, hook_p lasthook) { const udbp_p sc = NG_NODE_PRIVATE(node); struct ng_mesg *resp = NULL; int error = 0; struct ng_mesg *msg; NGI_GET_MSG(item, msg); /* Deal with message according to cookie and command */ switch (msg->header.typecookie) { case NGM_UDBP_COOKIE: switch (msg->header.cmd) { case NGM_UDBP_GET_STATUS: { struct ngudbpstat *stats; NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); if (!resp) { error = ENOMEM; break; } stats = (struct ngudbpstat *) resp->data; stats->packets_in = sc->packets_in; stats->packets_out = sc->packets_out; break; } case NGM_UDBP_SET_FLAG: if (msg->header.arglen != sizeof(u_int32_t)) { error = EINVAL; break; } sc->flags = *((u_int32_t *) msg->data); break; default: error = EINVAL; /* unknown command */ break; } break; default: error = EINVAL; /* unknown cookie type */ break; } /* Take care of synchronous response, if any */ NG_RESPOND_MSG(error, node, item, resp); NG_FREE_MSG(msg); return(error); } /* * Accept data from the hook and queue it for output. */ Static int ng_udbp_rcvdata(hook_p hook, item_p item) { const udbp_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); int error; struct ifqueue *xmitq_p; int s; struct mbuf *m; - meta_p meta; + struct ng_tag_prio *ptag; NGI_GET_M(item, m); - NGI_GET_META(item, meta); NG_FREE_ITEM(item); + /* * Now queue the data for when it can be sent */ - if (meta && meta->priority > 0) { + if ((ptag = (struct ng_tag_prio *)m_tag_locate(m, NGM_GENERIC_COOKIE, + NG_TAG_PRIO, NULL)) != NULL && (ptag->priority > NG_PRIO_CUTOFF) ) xmitq_p = (&sc->xmitq_hipri); - } else { + else xmitq_p = (&sc->xmitq); - } + s = splusb(); IF_LOCK(xmitq_p); if (_IF_QFULL(xmitq_p)) { _IF_DROP(xmitq_p); IF_UNLOCK(xmitq_p); splx(s); error = ENOBUFS; goto bad; } _IF_ENQUEUE(xmitq_p, m); IF_UNLOCK(xmitq_p); if (!(sc->flags & OUT_BUSY)) udbp_setup_out_transfer(sc); splx(s); return (0); bad: /* * It was an error case. * check if we need to free the mbuf, and then return the error */ NG_FREE_M(m); - NG_FREE_META(meta); return (error); } /* * Do local shutdown processing.. * We are a persistant device, we refuse to go away, and * only remove our links and reset ourself. */ Static int ng_udbp_rmnode(node_p node) { const udbp_p sc = NG_NODE_PRIVATE(node); int err; if (sc->flags & DISCONNECTED) { /* * WE are really going away.. hardware must have gone. * Assume that the hardware drive part will clear up the * sc, in fact it may already have done so.. * In which case we may have just segfaulted..XXX */ return (0); } /* stolen from attach routine */ /* Drain the queues */ IF_DRAIN(&sc->xmitq_hipri); IF_DRAIN(&sc->xmitq); sc->packets_in = 0; /* reset stats */ sc->packets_out = 0; NG_NODE_UNREF(node); /* forget it ever existed */ if ((err = ng_make_node_common(&ng_udbp_typestruct, &sc->node)) == 0) { char nodename[128]; sprintf(nodename, "%s", USBDEVNAME(sc->sc_dev)); if ((err = ng_name_node(sc->node, nodename))) { NG_NODE_UNREF(sc->node); /* out damned spot! */ sc->flags &= ~NETGRAPH_INITIALISED; sc->node = NULL; } else { NG_NODE_SET_PRIVATE(sc->node, sc); } } return (err); } /* * This is called once we've already connected a new hook to the other node. * It gives us a chance to balk at the last minute. */ Static int ng_udbp_connect(hook_p hook) { /* probably not at splnet, force outward queueing */ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); /* be really amiable and just say "YUP that's OK by me! " */ return (0); } /* * Dook disconnection * * For this type, removal of the last link destroys the node */ Static int ng_udbp_disconnect(hook_p hook) { const udbp_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); sc->hook = NULL; if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) ng_rmnode_self(NG_HOOK_NODE(hook)); return (0); } Index: head/sys/modules/ar/Makefile =================================================================== --- head/sys/modules/ar/Makefile (revision 131107) +++ head/sys/modules/ar/Makefile (revision 131108) @@ -1,8 +1,17 @@ # $FreeBSD$ - + .PATH: ${.CURDIR}/../../dev/ar KMOD = if_ar SRCS = if_ar.c if_ar_isa.c if_ar_pci.c SRCS += device_if.h bus_if.h pci_if.h isa_if.h opt_netgraph.h - + +NETGRAPH?= 0 + +opt_netgraph.h: +.if ${NETGRAPH} != 0 + echo "#define NETGRAPH ${NETGRAPH}" > opt_netgraph.h +.else + echo "" > opt_netgraph.h +.endif + .include Index: head/sys/modules/sr/Makefile =================================================================== --- head/sys/modules/sr/Makefile (revision 131107) +++ head/sys/modules/sr/Makefile (revision 131108) @@ -1,8 +1,17 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../dev/sr KMOD = if_sr SRCS = if_sr.c if_sr_isa.c if_sr_pci.c SRCS += device_if.h bus_if.h pci_if.h isa_if.h opt_netgraph.h - + +NETGRAPH?= 0 + +opt_netgraph.h: +.if ${NETGRAPH} != 0 + echo "#define NETGRAPH ${NETGRAPH}" > opt_netgraph.h +.else + echo "" > opt_netgraph.h +.endif + .include Index: head/sys/netgraph/atm/uni/ng_uni_cust.h =================================================================== --- head/sys/netgraph/atm/uni/ng_uni_cust.h (revision 131107) +++ head/sys/netgraph/atm/uni/ng_uni_cust.h (revision 131108) @@ -1,152 +1,153 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Hartmut Brandt * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * * Customisation of signalling source to the NG environment. * * $FreeBSD$ */ #include #include #include #include #include #include #include +#include #include #include #include #define ASSERT(E, M) KASSERT(E,M) /* * Memory */ enum unimem { UNIMEM_INS = 0, UNIMEM_ALL, UNIMEM_SIG, UNIMEM_CALL, UNIMEM_PARTY, }; #define UNIMEM_TYPES 5 void *ng_uni_malloc(enum unimem, const char *, u_int); void ng_uni_free(enum unimem, void *, const char *, u_int); #define INS_ALLOC() ng_uni_malloc(UNIMEM_INS, __FILE__, __LINE__) #define INS_FREE(P) ng_uni_free(UNIMEM_INS, P, __FILE__, __LINE__) #define UNI_ALLOC() ng_uni_malloc(UNIMEM_ALL, __FILE__, __LINE__) #define UNI_FREE(P) ng_uni_free(UNIMEM_ALL, P, __FILE__, __LINE__) #define SIG_ALLOC() ng_uni_malloc(UNIMEM_SIG, __FILE__, __LINE__) #define SIG_FREE(P) ng_uni_free(UNIMEM_SIG, P, __FILE__, __LINE__) #define CALL_ALLOC() ng_uni_malloc(UNIMEM_CALL, __FILE__, __LINE__) #define CALL_FREE(P) ng_uni_free(UNIMEM_CALL, P, __FILE__, __LINE__) #define PARTY_ALLOC() ng_uni_malloc(UNIMEM_PARTY, __FILE__, __LINE__) #define PARTY_FREE(P) ng_uni_free(UNIMEM_PARTY, P, __FILE__, __LINE__) /* * Timers */ struct uni_timer { struct callout_handle c; }; #define _TIMER_INIT(X,T) callout_handle_init(&(X)->T.c) #define _TIMER_DESTROY(UNI,FIELD) _TIMER_STOP(UNI,FIELD) #define _TIMER_STOP(UNI,FIELD) do { \ ng_untimeout(FIELD.c, (UNI)->arg); \ callout_handle_init(&FIELD.c); \ } while (0) #define TIMER_ISACT(UNI,T) ((UNI)->T.c.callout != NULL) #define _TIMER_START(UNI,ARG,FIELD,DUE,FUNC) do { \ _TIMER_STOP(UNI, FIELD); \ FIELD.c = ng_timeout((UNI)->arg, NULL, \ hz * (DUE) / 1000, FUNC, (ARG), 0); \ } while (0) #define TIMER_FUNC_UNI(T,F) \ static void F(struct uni *); \ static void \ _##T##_func(node_p node, hook_p hook, void *arg1, int arg2) \ { \ struct uni *uni = (struct uni *)arg1; \ \ callout_handle_init(&uni->T.c); \ (F)(uni); \ uni_work(uni); \ } /* * Be careful: call may be invalid after the call to F */ #define TIMER_FUNC_CALL(T,F) \ static void F(struct call *); \ static void \ _##T##_func(node_p node, hook_p hook, void *arg1, int arg2) \ { \ struct call *call = (struct call *)arg1; \ struct uni *uni = call->uni; \ \ callout_handle_init(&call->T.c); \ (F)(call); \ uni_work(uni); \ } /* * Be careful: call/party may be invalid after the call to F */ #define TIMER_FUNC_PARTY(T,F) \ static void F(struct party *); \ static void \ _##T##_func(node_p node, hook_p hook, void *arg1, int arg2) \ { \ struct party *party = (struct party *)arg1; \ struct uni *uni = party->call->uni; \ \ callout_handle_init(&party->T.c); \ (F)(party); \ uni_work(uni); \ } extern size_t unimem_sizes[UNIMEM_TYPES]; #define UNICORE \ size_t unimem_sizes[UNIMEM_TYPES] = { \ [UNIMEM_INS] sizeof(struct uni), \ [UNIMEM_ALL] sizeof(struct uni_all), \ [UNIMEM_SIG] sizeof(struct sig), \ [UNIMEM_CALL] sizeof(struct call), \ [UNIMEM_PARTY] sizeof(struct party) \ }; #define memmove(T, F, L) bcopy((F), (T), (L)) Index: head/sys/netgraph/netgraph.h =================================================================== --- head/sys/netgraph/netgraph.h (revision 131107) +++ head/sys/netgraph/netgraph.h (revision 131108) @@ -1,1146 +1,1160 @@ /* * netgraph.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Author: Julian Elischer * * $FreeBSD$ * $Whistle: netgraph.h,v 1.29 1999/11/01 07:56:13 julian Exp $ */ #ifndef _NETGRAPH_NETGRAPH_H_ #define _NETGRAPH_NETGRAPH_H_ #ifndef _KERNEL #error "This file should not be included in user level programs" #endif #include #include #include #include #include /* debugging options */ #define NETGRAPH_DEBUG #define NG_SEPARATE_MALLOC /* make modules use their own malloc types */ /* * This defines the in-kernel binary interface version. * It is possible to change this but leave the external message * API the same. Each type also has it's own cookies for versioning as well. * Change it for NETGRAPH_DEBUG version so we cannot mix debug and non debug * modules. */ #define _NG_ABI_VERSION 8 #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ #define NG_ABI_VERSION (_NG_ABI_VERSION + 0x10000) #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ #define NG_ABI_VERSION _NG_ABI_VERSION #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ /* * Forward references for the basic structures so we can * define the typedefs and use them in the structures themselves. */ struct ng_hook ; struct ng_node ; struct ng_item ; typedef struct ng_item *item_p; typedef struct ng_node *node_p; typedef struct ng_hook *hook_p; /* node method definitions */ typedef int ng_constructor_t(node_p node); typedef int ng_close_t(node_p node); typedef int ng_shutdown_t(node_p node); typedef int ng_newhook_t(node_p node, hook_p hook, const char *name); typedef hook_p ng_findhook_t(node_p node, const char *name); typedef int ng_connect_t(hook_p hook); typedef int ng_rcvmsg_t(node_p node, item_p item, hook_p lasthook); typedef int ng_rcvdata_t(hook_p hook, item_p item); typedef int ng_disconnect_t(hook_p hook); typedef int ng_rcvitem (node_p node, hook_p hook, item_p item); /*********************************************************************** ***************** Hook Structure and Methods ************************** *********************************************************************** * * Structure of a hook */ struct ng_hook { char hk_name[NG_HOOKSIZ]; /* what this node knows this link as */ void *hk_private; /* node dependant ID for this hook */ int hk_flags; /* info about this hook/link */ int hk_refs; /* dont actually free this till 0 */ struct ng_hook *hk_peer; /* the other end of this link */ struct ng_node *hk_node; /* The node this hook is attached to */ LIST_ENTRY(ng_hook) hk_hooks; /* linked list of all hooks on node */ ng_rcvmsg_t *hk_rcvmsg; /* control messages come here */ ng_rcvdata_t *hk_rcvdata; /* data comes here */ #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ #define HK_MAGIC 0x78573011 int hk_magic; char *lastfile; int lastline; SLIST_ENTRY(ng_hook) hk_all; /* all existing items */ #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ }; /* Flags for a hook */ #define HK_INVALID 0x0001 /* don't trust it! */ #define HK_QUEUE 0x0002 /* queue for later delivery */ #define HK_FORCE_WRITER 0x0004 /* Incoming data queued as a writer */ #define HK_DEAD 0x0008 /* This is the dead hook.. don't free */ /* * Public Methods for hook * If you can't do it with these you probably shouldn;t be doing it. */ void ng_unref_hook(hook_p hook); /* don't move this */ #define _NG_HOOK_REF(hook) atomic_add_int(&(hook)->hk_refs, 1) #define _NG_HOOK_NAME(hook) ((hook)->hk_name) #define _NG_HOOK_UNREF(hook) ng_unref_hook(hook) #define _NG_HOOK_SET_PRIVATE(hook, val) do {(hook)->hk_private = val;} while (0) #define _NG_HOOK_SET_RCVMSG(hook, val) do {(hook)->hk_rcvmsg = val;} while (0) #define _NG_HOOK_SET_RCVDATA(hook, val) do {(hook)->hk_rcvdata = val;} while (0) #define _NG_HOOK_PRIVATE(hook) ((hook)->hk_private) #define _NG_HOOK_NOT_VALID(hook) ((hook)->hk_flags & HK_INVALID) #define _NG_HOOK_IS_VALID(hook) (!((hook)->hk_flags & HK_INVALID)) #define _NG_HOOK_NODE(hook) ((hook)->hk_node) /* only rvalue! */ #define _NG_HOOK_PEER(hook) ((hook)->hk_peer) /* only rvalue! */ #define _NG_HOOK_FORCE_WRITER(hook) \ do { hook->hk_flags |= HK_FORCE_WRITER; } while (0) #define _NG_HOOK_FORCE_QUEUE(hook) do { hook->hk_flags |= HK_QUEUE; } while (0) /* Some shortcuts */ #define NG_PEER_NODE(hook) NG_HOOK_NODE(NG_HOOK_PEER(hook)) #define NG_PEER_HOOK_NAME(hook) NG_HOOK_NAME(NG_HOOK_PEER(hook)) #define NG_PEER_NODE_NAME(hook) NG_NODE_NAME(NG_PEER_NODE(hook)) #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ #define _NN_ __FILE__,__LINE__ void dumphook (hook_p hook, char *file, int line); static __inline void _chkhook(hook_p hook, char *file, int line); static __inline void _ng_hook_ref(hook_p hook, char * file, int line); static __inline char * _ng_hook_name(hook_p hook, char * file, int line); static __inline void _ng_hook_unref(hook_p hook, char * file, int line); static __inline void _ng_hook_set_private(hook_p hook, void * val, char * file, int line); static __inline void _ng_hook_set_rcvmsg(hook_p hook, ng_rcvmsg_t *val, char * file, int line); static __inline void _ng_hook_set_rcvdata(hook_p hook, ng_rcvdata_t *val, char * file, int line); static __inline void * _ng_hook_private(hook_p hook, char * file, int line); static __inline int _ng_hook_not_valid(hook_p hook, char * file, int line); static __inline int _ng_hook_is_valid(hook_p hook, char * file, int line); static __inline node_p _ng_hook_node(hook_p hook, char * file, int line); static __inline hook_p _ng_hook_peer(hook_p hook, char * file, int line); static __inline void _ng_hook_force_writer(hook_p hook, char * file, int line); static __inline void _ng_hook_force_queue(hook_p hook, char * file, int line); static void __inline _chkhook(hook_p hook, char *file, int line) { if (hook->hk_magic != HK_MAGIC) { printf("Accessing freed hook "); dumphook(hook, file, line); } hook->lastline = line; hook->lastfile = file; } static __inline void _ng_hook_ref(hook_p hook, char * file, int line) { _chkhook(hook, file, line); _NG_HOOK_REF(hook); } static __inline char * _ng_hook_name(hook_p hook, char * file, int line) { _chkhook(hook, file, line); return (_NG_HOOK_NAME(hook)); } static __inline void _ng_hook_unref(hook_p hook, char * file, int line) { _chkhook(hook, file, line); _NG_HOOK_UNREF(hook); } static __inline void _ng_hook_set_private(hook_p hook, void *val, char * file, int line) { _chkhook(hook, file, line); _NG_HOOK_SET_PRIVATE(hook, val); } static __inline void _ng_hook_set_rcvmsg(hook_p hook, ng_rcvmsg_t *val, char * file, int line) { _chkhook(hook, file, line); _NG_HOOK_SET_RCVMSG(hook, val); } static __inline void _ng_hook_set_rcvdata(hook_p hook, ng_rcvdata_t *val, char * file, int line) { _chkhook(hook, file, line); _NG_HOOK_SET_RCVDATA(hook, val); } static __inline void * _ng_hook_private(hook_p hook, char * file, int line) { _chkhook(hook, file, line); return (_NG_HOOK_PRIVATE(hook)); } static __inline int _ng_hook_not_valid(hook_p hook, char * file, int line) { _chkhook(hook, file, line); return (_NG_HOOK_NOT_VALID(hook)); } static __inline int _ng_hook_is_valid(hook_p hook, char * file, int line) { _chkhook(hook, file, line); return (_NG_HOOK_IS_VALID(hook)); } static __inline node_p _ng_hook_node(hook_p hook, char * file, int line) { _chkhook(hook, file, line); return (_NG_HOOK_NODE(hook)); } static __inline hook_p _ng_hook_peer(hook_p hook, char * file, int line) { _chkhook(hook, file, line); return (_NG_HOOK_PEER(hook)); } static __inline void _ng_hook_force_writer(hook_p hook, char * file, int line) { _chkhook(hook, file, line); _NG_HOOK_FORCE_WRITER(hook); } static __inline void _ng_hook_force_queue(hook_p hook, char * file, int line) { _chkhook(hook, file, line); _NG_HOOK_FORCE_QUEUE(hook); } #define NG_HOOK_REF(hook) _ng_hook_ref(hook, _NN_) #define NG_HOOK_NAME(hook) _ng_hook_name(hook, _NN_) #define NG_HOOK_UNREF(hook) _ng_hook_unref(hook, _NN_) #define NG_HOOK_SET_PRIVATE(hook, val) _ng_hook_set_private(hook, val, _NN_) #define NG_HOOK_SET_RCVMSG(hook, val) _ng_hook_set_rcvmsg(hook, val, _NN_) #define NG_HOOK_SET_RCVDATA(hook, val) _ng_hook_set_rcvdata(hook, val, _NN_) #define NG_HOOK_PRIVATE(hook) _ng_hook_private(hook, _NN_) #define NG_HOOK_NOT_VALID(hook) _ng_hook_not_valid(hook, _NN_) #define NG_HOOK_IS_VALID(hook) _ng_hook_is_valid(hook, _NN_) #define NG_HOOK_NODE(hook) _ng_hook_node(hook, _NN_) #define NG_HOOK_PEER(hook) _ng_hook_peer(hook, _NN_) #define NG_HOOK_FORCE_WRITER(hook) _ng_hook_force_writer(hook, _NN_) #define NG_HOOK_FORCE_QUEUE(hook) _ng_hook_force_queue(hook, _NN_) #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ #define NG_HOOK_REF(hook) _NG_HOOK_REF(hook) #define NG_HOOK_NAME(hook) _NG_HOOK_NAME(hook) #define NG_HOOK_UNREF(hook) _NG_HOOK_UNREF(hook) #define NG_HOOK_SET_PRIVATE(hook, val) _NG_HOOK_SET_PRIVATE(hook, val) #define NG_HOOK_SET_RCVMSG(hook, val) _NG_HOOK_SET_RCVMSG(hook, val) #define NG_HOOK_SET_RCVDATA(hook, val) _NG_HOOK_SET_RCVDATA(hook, val) #define NG_HOOK_PRIVATE(hook) _NG_HOOK_PRIVATE(hook) #define NG_HOOK_NOT_VALID(hook) _NG_HOOK_NOT_VALID(hook) #define NG_HOOK_IS_VALID(hook) _NG_HOOK_IS_VALID(hook) #define NG_HOOK_NODE(hook) _NG_HOOK_NODE(hook) #define NG_HOOK_PEER(hook) _NG_HOOK_PEER(hook) #define NG_HOOK_FORCE_WRITER(hook) _NG_HOOK_FORCE_WRITER(hook) #define NG_HOOK_FORCE_QUEUE(hook) _NG_HOOK_FORCE_QUEUE(hook) #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ /*********************************************************************** ***************** Node Structure and Methods ************************** *********************************************************************** * Structure of a node * including the eembedded queue structure. * * The structure for queueing Netgraph request items * embedded in the node structure */ struct ng_queue { u_long q_flags; struct mtx q_mtx; item_p queue; item_p *last; struct ng_node *q_node; /* find the front of the node.. */ }; struct ng_node { char nd_name[NG_NODESIZ]; /* optional globally unique name */ struct ng_type *nd_type; /* the installed 'type' */ int nd_flags; /* see below for bit definitions */ int nd_refs; /* # of references to this node */ int nd_numhooks; /* number of hooks */ void *nd_private; /* node type dependant node ID */ ng_ID_t nd_ID; /* Unique per node */ LIST_HEAD(hooks, ng_hook) nd_hooks; /* linked list of node hooks */ LIST_ENTRY(ng_node) nd_nodes; /* linked list of all nodes */ LIST_ENTRY(ng_node) nd_idnodes; /* ID hash collision list */ TAILQ_ENTRY(ng_node) nd_work; /* nodes with work to do */ struct ng_queue nd_input_queue; /* input queue for locking */ #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ #define ND_MAGIC 0x59264837 int nd_magic; char *lastfile; int lastline; SLIST_ENTRY(ng_node) nd_all; /* all existing nodes */ #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ }; /* Flags for a node */ #define NG_INVALID 0x00000001 /* free when refs go to 0 */ #define NG_WORKQ 0x00000002 /* node is on the work queue */ #define NG_FORCE_WRITER 0x00000004 /* Never multithread this node */ #define NG_CLOSING 0x00000008 /* ng_rmnode() at work */ #define NG_REALLY_DIE 0x00000010 /* "persistant" node is unloading */ #define NGF_TYPE1 0x10000000 /* reserved for type specific storage */ #define NGF_TYPE2 0x20000000 /* reserved for type specific storage */ #define NGF_TYPE3 0x40000000 /* reserved for type specific storage */ #define NGF_TYPE4 0x80000000 /* reserved for type specific storage */ /* * Public methods for nodes. * If you can't do it with these you probably shouldn't be doing it. */ int ng_unref_node(node_p node); /* don't move this */ #define _NG_NODE_NAME(node) ((node)->nd_name + 0) #define _NG_NODE_HAS_NAME(node) ((node)->nd_name[0] + 0) #define _NG_NODE_ID(node) ((node)->nd_ID + 0) #define _NG_NODE_REF(node) atomic_add_int(&(node)->nd_refs, 1) #define _NG_NODE_UNREF(node) ng_unref_node(node) #define _NG_NODE_SET_PRIVATE(node, val) do {(node)->nd_private = val;} while (0) #define _NG_NODE_PRIVATE(node) ((node)->nd_private) #define _NG_NODE_IS_VALID(node) (!((node)->nd_flags & NG_INVALID)) #define _NG_NODE_NOT_VALID(node) ((node)->nd_flags & NG_INVALID) #define _NG_NODE_NUMHOOKS(node) ((node)->nd_numhooks + 0) /* rvalue */ #define _NG_NODE_FORCE_WRITER(node) \ do{ node->nd_flags |= NG_FORCE_WRITER; }while (0) #define _NG_NODE_REALLY_DIE(node) \ do{ node->nd_flags |= (NG_REALLY_DIE|NG_INVALID); }while (0) /* * The hook iterator. * This macro will call a function of type ng_fn_eachhook for each * hook attached to the node. If the function returns 0, then the * iterator will stop and return a pointer to the hook that returned 0. */ typedef int ng_fn_eachhook(hook_p hook, void* arg); #define _NG_NODE_FOREACH_HOOK(node, fn, arg, rethook) \ do { \ hook_p _hook; \ (rethook) = NULL; \ LIST_FOREACH(_hook, &((node)->nd_hooks), hk_hooks) { \ if ((fn)(_hook, arg) == 0) { \ (rethook) = _hook; \ break; \ } \ } \ } while (0) #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ void dumpnode(node_p node, char *file, int line); static void __inline _chknode(node_p node, char *file, int line); static __inline char * _ng_node_name(node_p node, char *file, int line); static __inline int _ng_node_has_name(node_p node, char *file, int line); static __inline ng_ID_t _ng_node_id(node_p node, char *file, int line); void ng_ref_node(node_p node); static __inline void _ng_node_ref(node_p node, char *file, int line); static __inline int _ng_node_unref(node_p node, char *file, int line); static __inline void _ng_node_set_private(node_p node, void * val, char *file, int line); static __inline void * _ng_node_private(node_p node, char *file, int line); static __inline int _ng_node_is_valid(node_p node, char *file, int line); static __inline int _ng_node_not_valid(node_p node, char *file, int line); static __inline int _ng_node_numhooks(node_p node, char *file, int line); static __inline void _ng_node_force_writer(node_p node, char *file, int line); static __inline hook_p _ng_node_foreach_hook(node_p node, ng_fn_eachhook *fn, void *arg, char *file, int line); static void __inline _chknode(node_p node, char *file, int line) { if (node->nd_magic != ND_MAGIC) { printf("Accessing freed node "); dumpnode(node, file, line); } node->lastline = line; node->lastfile = file; } static __inline char * _ng_node_name(node_p node, char *file, int line) { _chknode(node, file, line); return(_NG_NODE_NAME(node)); } static __inline int _ng_node_has_name(node_p node, char *file, int line) { _chknode(node, file, line); return(_NG_NODE_HAS_NAME(node)); } static __inline ng_ID_t _ng_node_id(node_p node, char *file, int line) { _chknode(node, file, line); return(_NG_NODE_ID(node)); } static __inline void _ng_node_ref(node_p node, char *file, int line) { _chknode(node, file, line); /*_NG_NODE_REF(node);*/ ng_ref_node(node); } static __inline int _ng_node_unref(node_p node, char *file, int line) { _chknode(node, file, line); return (_NG_NODE_UNREF(node)); } static __inline void _ng_node_set_private(node_p node, void * val, char *file, int line) { _chknode(node, file, line); _NG_NODE_SET_PRIVATE(node, val); } static __inline void * _ng_node_private(node_p node, char *file, int line) { _chknode(node, file, line); return (_NG_NODE_PRIVATE(node)); } static __inline int _ng_node_is_valid(node_p node, char *file, int line) { _chknode(node, file, line); return(_NG_NODE_IS_VALID(node)); } static __inline int _ng_node_not_valid(node_p node, char *file, int line) { _chknode(node, file, line); return(_NG_NODE_NOT_VALID(node)); } static __inline int _ng_node_numhooks(node_p node, char *file, int line) { _chknode(node, file, line); return(_NG_NODE_NUMHOOKS(node)); } static __inline void _ng_node_force_writer(node_p node, char *file, int line) { _chknode(node, file, line); _NG_NODE_FORCE_WRITER(node); } static __inline void _ng_node_really_die(node_p node, char *file, int line) { _chknode(node, file, line); _NG_NODE_REALLY_DIE(node); } static __inline hook_p _ng_node_foreach_hook(node_p node, ng_fn_eachhook *fn, void *arg, char *file, int line) { hook_p hook; _chknode(node, file, line); _NG_NODE_FOREACH_HOOK(node, fn, arg, hook); return (hook); } #define NG_NODE_NAME(node) _ng_node_name(node, _NN_) #define NG_NODE_HAS_NAME(node) _ng_node_has_name(node, _NN_) #define NG_NODE_ID(node) _ng_node_id(node, _NN_) #define NG_NODE_REF(node) _ng_node_ref(node, _NN_) #define NG_NODE_UNREF(node) _ng_node_unref(node, _NN_) #define NG_NODE_SET_PRIVATE(node, val) _ng_node_set_private(node, val, _NN_) #define NG_NODE_PRIVATE(node) _ng_node_private(node, _NN_) #define NG_NODE_IS_VALID(node) _ng_node_is_valid(node, _NN_) #define NG_NODE_NOT_VALID(node) _ng_node_not_valid(node, _NN_) #define NG_NODE_FORCE_WRITER(node) _ng_node_force_writer(node, _NN_) #define NG_NODE_REALLY_DIE(node) _ng_node_really_die(node, _NN_) #define NG_NODE_NUMHOOKS(node) _ng_node_numhooks(node, _NN_) #define NG_NODE_FOREACH_HOOK(node, fn, arg, rethook) \ do { \ rethook = _ng_node_foreach_hook(node, fn, (void *)arg, _NN_); \ } while (0) #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ #define NG_NODE_NAME(node) _NG_NODE_NAME(node) #define NG_NODE_HAS_NAME(node) _NG_NODE_HAS_NAME(node) #define NG_NODE_ID(node) _NG_NODE_ID(node) #define NG_NODE_REF(node) _NG_NODE_REF(node) #define NG_NODE_UNREF(node) _NG_NODE_UNREF(node) #define NG_NODE_SET_PRIVATE(node, val) _NG_NODE_SET_PRIVATE(node, val) #define NG_NODE_PRIVATE(node) _NG_NODE_PRIVATE(node) #define NG_NODE_IS_VALID(node) _NG_NODE_IS_VALID(node) #define NG_NODE_NOT_VALID(node) _NG_NODE_NOT_VALID(node) #define NG_NODE_FORCE_WRITER(node) _NG_NODE_FORCE_WRITER(node) #define NG_NODE_REALLY_DIE(node) _NG_NODE_REALLY_DIE(node) #define NG_NODE_NUMHOOKS(node) _NG_NODE_NUMHOOKS(node) #define NG_NODE_FOREACH_HOOK(node, fn, arg, rethook) \ _NG_NODE_FOREACH_HOOK(node, fn, arg, rethook) #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ /*********************************************************************** ***************** Meta Data Structures and Methods ******************** *********************************************************************** * * The structure that holds meta_data about a data packet (e.g. priority) * Nodes might add or subtract options as needed if there is room. * They might reallocate the struct to make more room if they need to. * Meta-data is still experimental. */ struct meta_field_header { u_long cookie; /* cookie for the field. Skip fields you don't * know about (same cookie as in messgaes) */ u_short type; /* field ID */ u_short len; /* total len of this field including extra * data */ char data[0]; /* data starts here */ }; /* To zero out an option 'in place' set it's cookie to this */ #define NGM_INVALID_COOKIE 865455152 /* This part of the metadata is always present if the pointer is non NULL */ struct ng_meta { char priority; /* -ve is less priority, 0 is default */ char discardability; /* higher is less valuable.. discard first */ u_short allocated_len; /* amount malloc'd */ u_short used_len; /* sum of all fields, options etc. */ u_short flags; /* see below.. generic flags */ struct meta_field_header options[0]; /* add as (if) needed */ }; typedef struct ng_meta *meta_p; /* Flags for meta-data */ #define NGMF_TEST 0x01 /* discard at the last moment before sending */ #define NGMF_TRACE 0x02 /* trace when handing this data to a node */ /*********************************************************************** ************* Node Queue and Item Structures and Methods ************** *********************************************************************** * */ typedef void ng_item_fn(node_p node, hook_p hook, void *arg1, int arg2); struct ng_item { u_long el_flags; item_p el_next; node_p el_dest; /* The node it will be applied against (or NULL) */ hook_p el_hook; /* Entering hook. Optional in Control messages */ union { struct { struct mbuf *da_m; meta_p da_meta; } data; struct { struct ng_mesg *msg_msg; ng_ID_t msg_retaddr; } msg; struct { ng_item_fn *fn_fn; void *fn_arg1; int fn_arg2; } fn; } body; #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ char *lastfile; int lastline; TAILQ_ENTRY(ng_item) all; /* all existing items */ #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ }; #define NGQF_TYPE 0x03 /* MASK of content definition */ #define NGQF_MESG 0x00 /* the queue element is a message */ #define NGQF_DATA 0x01 /* the queue element is data */ #define NGQF_FN 0x02 /* the queue element is a function */ #define NGQF_UNDEF 0x03 /* UNDEFINED */ #define NGQF_RW 0x04 /* MASK for queue entry read/write */ #define NGQF_READER 0x04 /* queued as a reader */ #define NGQF_WRITER 0x00 /* queued as a writer */ #define NGQF_FREE 0x08 /* * Get the mbuf (etc) out of an item. * Sets the value in the item to NULL in case we need to call NG_FREE_ITEM() * with it, (to avoid freeing the things twice). * If you don't want to zero out the item then realise that the * item still owns it. * Retaddr is different. There are no references on that. It's just a number. * The debug versions must be either all used everywhere or not at all. */ #define _NGI_M(i) ((i)->body.data.da_m) #define _NGI_META(i) ((i)->body.data.da_meta) #define _NGI_MSG(i) ((i)->body.msg.msg_msg) #define _NGI_RETADDR(i) ((i)->body.msg.msg_retaddr) #define _NGI_FN(i) ((i)->body.fn.fn_fn) #define _NGI_ARG1(i) ((i)->body.fn.fn_arg1) #define _NGI_ARG2(i) ((i)->body.fn.fn_arg2) #define _NGI_NODE(i) ((i)->el_dest) #define _NGI_HOOK(i) ((i)->el_hook) #define _NGI_SET_HOOK(i,h) do { _NGI_HOOK(i) = h; h = NULL;} while (0) #define _NGI_CLR_HOOK(i) do { \ hook_p _hook = _NGI_HOOK(i); \ if (_hook) { \ _NG_HOOK_UNREF(_hook); \ _NGI_HOOK(i) = NULL; \ } \ } while (0) #define _NGI_SET_NODE(i,n) do { _NGI_NODE(i) = n; n = NULL;} while (0) #define _NGI_CLR_NODE(i) do { \ node_p _node = _NGI_NODE(i); \ if (_node) { \ _NG_NODE_UNREF(_node); \ _NGI_NODE(i) = NULL; \ } \ } while (0) #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ void dumpitem(item_p item, char *file, int line); static __inline void _ngi_check(item_p item, char *file, int line) ; static __inline struct mbuf ** _ngi_m(item_p item, char *file, int line) ; static __inline meta_p * _ngi_meta(item_p item, char *file, int line) ; static __inline ng_ID_t * _ngi_retaddr(item_p item, char *file, int line); static __inline struct ng_mesg ** _ngi_msg(item_p item, char *file, int line) ; static __inline ng_item_fn ** _ngi_fn(item_p item, char *file, int line) ; static __inline void ** _ngi_arg1(item_p item, char *file, int line) ; static __inline int * _ngi_arg2(item_p item, char *file, int line) ; static __inline node_p _ngi_node(item_p item, char *file, int line); static __inline hook_p _ngi_hook(item_p item, char *file, int line); static __inline void _ngi_check(item_p item, char *file, int line) { if (item->el_flags & NGQF_FREE) { dumpitem(item, file, line); panic ("free item!"); } (item)->lastline = line; (item)->lastfile = file; } static __inline struct mbuf ** _ngi_m(item_p item, char *file, int line) { _ngi_check(item, file, line); return (&_NGI_M(item)); } static __inline meta_p * _ngi_meta(item_p item, char *file, int line) { _ngi_check(item, file, line); return (&_NGI_META(item)); } static __inline struct ng_mesg ** _ngi_msg(item_p item, char *file, int line) { _ngi_check(item, file, line); return (&_NGI_MSG(item)); } static __inline ng_ID_t * _ngi_retaddr(item_p item, char *file, int line) { _ngi_check(item, file, line); return (&_NGI_RETADDR(item)); } static __inline ng_item_fn ** _ngi_fn(item_p item, char *file, int line) { _ngi_check(item, file, line); return (&_NGI_FN(item)); } static __inline void ** _ngi_arg1(item_p item, char *file, int line) { _ngi_check(item, file, line); return (&_NGI_ARG1(item)); } static __inline int * _ngi_arg2(item_p item, char *file, int line) { _ngi_check(item, file, line); return (&_NGI_ARG2(item)); } static __inline node_p _ngi_node(item_p item, char *file, int line) { _ngi_check(item, file, line); return (_NGI_NODE(item)); } static __inline hook_p _ngi_hook(item_p item, char *file, int line) { _ngi_check(item, file, line); return (_NGI_HOOK(item)); } #define NGI_M(i) (*_ngi_m(i, _NN_)) #define NGI_META(i) (*_ngi_meta(i, _NN_)) #define NGI_MSG(i) (*_ngi_msg(i, _NN_)) #define NGI_RETADDR(i) (*_ngi_retaddr(i, _NN_)) #define NGI_FN(i) (*_ngi_fn(i, _NN_)) #define NGI_ARG1(i) (*_ngi_arg1(i, _NN_)) #define NGI_ARG2(i) (*_ngi_arg2(i, _NN_)) #define NGI_HOOK(i) _ngi_hook(i, _NN_) #define NGI_NODE(i) _ngi_node(i, _NN_) #define NGI_SET_HOOK(i,h) \ do { _ngi_check(i, _NN_); _NGI_SET_HOOK(i, h); } while (0) #define NGI_CLR_HOOK(i) \ do { _ngi_check(i, _NN_); _NGI_CLR_HOOK(i); } while (0) #define NGI_SET_NODE(i,n) \ do { _ngi_check(i, _NN_); _NGI_SET_NODE(i, n); } while (0) #define NGI_CLR_NODE(i) \ do { _ngi_check(i, _NN_); _NGI_CLR_NODE(i); } while (0) #define NG_FREE_ITEM(item) \ do { \ _ngi_check(item, _NN_); \ ng_free_item((item)); \ } while (0) #define SAVE_LINE(item) \ do { \ (item)->lastline = __LINE__; \ (item)->lastfile = __FILE__; \ } while (0) #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ #define NGI_M(i) _NGI_M(i) #define NGI_META(i) _NGI_META(i) #define NGI_MSG(i) _NGI_MSG(i) #define NGI_RETADDR(i) _NGI_RETADDR(i) #define NGI_FN(i) _NGI_FN(i) #define NGI_ARG1(i) _NGI_ARG1(i) #define NGI_ARG2(i) _NGI_ARG2(i) #define NGI_NODE(i) _NGI_NODE(i) #define NGI_HOOK(i) _NGI_HOOK(i) #define NGI_SET_HOOK(i,h) _NGI_SET_HOOK(i,h) #define NGI_CLR_HOOK(i) _NGI_CLR_HOOK(i) #define NGI_SET_NODE(i,n) _NGI_SET_NODE(i,n) #define NGI_CLR_NODE(i) _NGI_CLR_NODE(i) #define NG_FREE_ITEM(item) ng_free_item((item)) #define SAVE_LINE(item) do {} while (0) #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ #define NGI_GET_M(i,m) \ do { \ (m) = NGI_M(i); \ _NGI_M(i) = NULL; \ } while (0) #define NGI_GET_META(i,m) \ do { \ (m) = NGI_META(i); \ _NGI_META(i) = NULL; \ } while (0) #define NGI_GET_MSG(i,m) \ do { \ (m) = NGI_MSG(i); \ _NGI_MSG(i) = NULL; \ } while (0) #define NGI_GET_NODE(i,n) /* YOU NOW HAVE THE REFERENCE */ \ do { \ (n) = NGI_NODE(i); \ _NGI_NODE(i) = NULL; \ } while (0) #define NGI_GET_HOOK(i,h) \ do { \ (h) = NGI_HOOK(i); \ _NGI_HOOK(i) = NULL; \ } while (0) /********************************************************************** * Data macros. Send, manipulate and free. **********************************************************************/ /* * Assuming the data is already ok, just set the new address and send */ #define NG_FWD_ITEM_HOOK(error, item, hook) \ do { \ (error) = \ ng_address_hook(NULL, (item), (hook), 0); \ if (error == 0) { \ SAVE_LINE(item); \ (error) = ng_snd_item((item), 0); \ } \ (item) = NULL; \ } while (0) /* * Forward a data packet with no new meta-data. * old metadata is passed along without change. * Mbuf pointer is updated to new value. We presume you dealt with the * old one when you update it to the new one (or it maybe the old one). * We got a packet and possibly had to modify the mbuf. * You should probably use NGI_GET_M() if you are going to use this too */ #define NG_FWD_NEW_DATA(error, item, hook, m) \ do { \ NGI_M(item) = (m); \ (m) = NULL; \ NG_FWD_ITEM_HOOK(error, item, hook); \ } while (0) /* Send a previously unpackaged mbuf when we have no metadata to send */ #define NG_SEND_DATA_ONLY(error, hook, m) \ do { \ item_p _item; \ if ((_item = ng_package_data((m), NULL))) { \ NG_FWD_ITEM_HOOK(error, _item, hook); \ } else { \ (error) = ENOMEM; \ } \ (m) = NULL; \ } while (0) /* Send previously unpackeged data and metadata. */ #define NG_SEND_DATA(error, hook, m, meta) \ do { \ item_p _item; \ if ((_item = ng_package_data((m), (meta)))) { \ NG_FWD_ITEM_HOOK(error, _item, hook); \ } else { \ (error) = ENOMEM; \ } \ (m) = NULL; \ (meta) = NULL; \ } while (0) #define NG_FREE_MSG(msg) \ do { \ if ((msg)) { \ FREE((msg), M_NETGRAPH_MSG); \ (msg) = NULL; \ } \ } while (0) #define NG_FREE_META(meta) \ do { \ if ((meta)) { \ FREE((meta), M_NETGRAPH_META); \ (meta) = NULL; \ } \ } while (0) #define NG_FREE_M(m) \ do { \ if ((m)) { \ m_freem((m)); \ (m) = NULL; \ } \ } while (0) /***************************************** * Message macros *****************************************/ #define NG_SEND_MSG_HOOK(error, here, msg, hook, retaddr) \ do { \ item_p _item; \ if ((_item = ng_package_msg(msg)) == NULL) { \ (msg) = NULL; \ (error) = ENOMEM; \ break; \ } \ if (((error) = ng_address_hook((here), (_item), \ (hook), (retaddr))) == 0) { \ SAVE_LINE(_item); \ (error) = ng_snd_item((_item), 0); \ } \ (msg) = NULL; \ } while (0) #define NG_SEND_MSG_PATH(error, here, msg, path, retaddr) \ do { \ item_p _item; \ if ((_item = ng_package_msg(msg)) == NULL) { \ (msg) = NULL; \ (error) = ENOMEM; \ break; \ } \ if (((error) = ng_address_path((here), (_item), \ (path), (retaddr))) == 0) { \ SAVE_LINE(_item); \ (error) = ng_snd_item((_item), 0); \ } \ (msg) = NULL; \ } while (0) #define NG_SEND_MSG_ID(error, here, msg, ID, retaddr) \ do { \ item_p _item; \ if ((_item = ng_package_msg(msg)) == NULL) { \ (msg) = NULL; \ (error) = ENOMEM; \ break; \ } \ if (((error) = ng_address_ID((here), (_item), \ (ID), (retaddr))) == 0) { \ SAVE_LINE(_item); \ (error) = ng_snd_item((_item), 0); \ } \ (msg) = NULL; \ } while (0) /* * Redirect the message to the next hop using the given hook. * ng_retarget_msg() frees the item if there is an error * and returns an error code. It returns 0 on success. */ #define NG_FWD_MSG_HOOK(error, here, item, hook, retaddr) \ do { \ if (((error) = ng_address_hook((here), (item), \ (hook), (retaddr))) == 0) { \ SAVE_LINE(item); \ (error) = ng_snd_item((item), 0); \ } \ (item) = NULL; \ } while (0) /* * Send a queue item back to it's originator with a response message. * Assume original message was removed and freed separatly. */ #define NG_RESPOND_MSG(error, here, item, resp) \ do { \ if (resp) { \ ng_ID_t _dest = NGI_RETADDR(item); \ NGI_RETADDR(item) = 0; \ NGI_MSG(item) = resp; \ if ((ng_address_ID((here), (item), \ _dest, 0)) == 0) { \ SAVE_LINE(item); \ (error) = ng_snd_item((item), 1); \ } else { \ (error) = EINVAL; \ } \ } else { \ NG_FREE_ITEM(item); \ } \ (item) = NULL; \ } while (0) /*********************************************************************** ******** Structures Definitions and Macros for defining a node ******* *********************************************************************** * * Here we define the structures needed to actually define a new node * type. */ /* * Command list -- each node type specifies the command that it knows * how to convert between ASCII and binary using an array of these. * The last element in the array must be a terminator with cookie=0. */ struct ng_cmdlist { u_int32_t cookie; /* command typecookie */ int cmd; /* command number */ const char *name; /* command name */ const struct ng_parse_type *mesgType; /* args if !NGF_RESP */ const struct ng_parse_type *respType; /* args if NGF_RESP */ }; /* * Structure of a node type * If data is sent to the "rcvdata()" entrypoint then the system * may decide to defer it until later by queing it with the normal netgraph * input queuing system. This is decidde by the HK_QUEUE flag being set in * the flags word of the peer (receiving) hook. The dequeuing mechanism will * ensure it is not requeued again. * Note the input queueing system is to allow modules * to 'release the stack' or to pass data across spl layers. * The data will be redelivered as soon as the NETISR code runs * which may be almost immediatly. A node may also do it's own queueing * for other reasons (e.g. device output queuing). */ struct ng_type { u_int32_t version; /* must equal NG_API_VERSION */ const char *name; /* Unique type name */ modeventhand_t mod_event; /* Module event handler (optional) */ ng_constructor_t *constructor; /* Node constructor */ ng_rcvmsg_t *rcvmsg; /* control messages come here */ ng_close_t *close; /* warn about forthcoming shutdown */ ng_shutdown_t *shutdown; /* reset, and free resources */ ng_newhook_t *newhook; /* first notification of new hook */ ng_findhook_t *findhook; /* only if you have lots of hooks */ ng_connect_t *connect; /* final notification of new hook */ ng_rcvdata_t *rcvdata; /* data comes here */ ng_disconnect_t *disconnect; /* notify on disconnect */ const struct ng_cmdlist *cmdlist; /* commands we can convert */ /* R/W data private to the base netgraph code DON'T TOUCH! */ LIST_ENTRY(ng_type) types; /* linked list of all types */ int refs; /* number of instances */ }; /* * Use the NETGRAPH_INIT() macro to link a node type into the * netgraph system. This works for types compiled into the kernel * as well as KLD modules. The first argument should be the type * name (eg, echo) and the second a pointer to the type struct. * * If a different link time is desired, e.g., a device driver that * needs to install its netgraph type before probing, use the * NETGRAPH_INIT_ORDERED() macro instead. Deivce drivers probably * want to use SI_SUB_DRIVERS instead of SI_SUB_PSEUDO. */ #define NETGRAPH_INIT_ORDERED(typename, typestructp, sub, order) \ static moduledata_t ng_##typename##_mod = { \ "ng_" #typename, \ ng_mod_event, \ (typestructp) \ }; \ DECLARE_MODULE(ng_##typename, ng_##typename##_mod, sub, order); \ MODULE_DEPEND(ng_##typename, netgraph, NG_ABI_VERSION, \ NG_ABI_VERSION, \ NG_ABI_VERSION) #define NETGRAPH_INIT(tn, tp) \ NETGRAPH_INIT_ORDERED(tn, tp, SI_SUB_PSEUDO, SI_ORDER_ANY) /* Special malloc() type for netgraph structs and ctrl messages */ /* Only these two types should be visible to nodes */ MALLOC_DECLARE(M_NETGRAPH); MALLOC_DECLARE(M_NETGRAPH_MSG); MALLOC_DECLARE(M_NETGRAPH_META); /* declare the base of the netgraph sysclt hierarchy */ /* but only if this file cares about sysctls */ #ifdef SYSCTL_DECL SYSCTL_DECL(_net_graph); #endif /* * Methods that the nodes can use. * Many of these methods should usually NOT be used directly but via * Macros above. */ int ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr); int ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr); int ng_address_path(node_p here, item_p item, char *address, ng_ID_t raddr); int ng_bypass(hook_p hook1, hook_p hook2); meta_p ng_copy_meta(meta_p meta); hook_p ng_findhook(node_p node, const char *name); int ng_make_node_common(struct ng_type *typep, node_p *nodep); int ng_name_node(node_p node, const char *name); int ng_newtype(struct ng_type *tp); ng_ID_t ng_node2ID(node_p node); item_p ng_package_data(struct mbuf *m, meta_p meta); item_p ng_package_msg(struct ng_mesg *msg); item_p ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg); void ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr); int ng_rmhook_self(hook_p hook); /* if a node wants to kill a hook */ int ng_rmnode_self(node_p here); /* if a node wants to suicide */ int ng_rmtype(struct ng_type *tp); int ng_snd_item(item_p item, int queue); int ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void *arg1, int arg2); int ng_untimeout(struct callout_handle handle, node_p node); struct callout_handle ng_timeout(node_p node, hook_p hook, int ticks, ng_item_fn *fn, void * arg1, int arg2); /* - * prototypes the user should DEFINITLY not use directly + * prototypes the user should DEFINITELY not use directly */ void ng_free_item(item_p item); /* Use NG_FREE_ITEM instead */ int ng_mod_event(module_t mod, int what, void *arg); -#endif /* _NETGRAPH_NETGRAPH_H_ */ +/* + * Tag definitions and constants + */ +#define NG_TAG_PRIO 1 + +struct ng_tag_prio { + struct m_tag tag; + char priority; + char discardability; +}; + +#define NG_PRIO_CUTOFF 32 +#define NG_PRIO_LINKSTATE 64 + +#endif /* _NETGRAPH_NETGRAPH_H_ */ Index: head/sys/netgraph/ng_ksocket.c =================================================================== --- head/sys/netgraph/ng_ksocket.c (revision 131107) +++ head/sys/netgraph/ng_ksocket.c (revision 131108) @@ -1,1312 +1,1289 @@ /* * ng_ksocket.c * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_ksocket.c,v 1.1 1999/11/16 20:04:40 archie Exp $ */ /* * Kernel socket node type. This node type is basically a kernel-mode * version of a socket... kindof like the reverse of the socket node type. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NG_SEPARATE_MALLOC MALLOC_DEFINE(M_NETGRAPH_KSOCKET, "netgraph_ksock", "netgraph ksock node "); #else #define M_NETGRAPH_KSOCKET M_NETGRAPH #endif #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) #define SADATA_OFFSET (OFFSETOF(struct sockaddr, sa_data)) /* Node private data */ struct ng_ksocket_private { node_p node; hook_p hook; struct socket *so; LIST_HEAD(, ng_ksocket_private) embryos; LIST_ENTRY(ng_ksocket_private) siblings; u_int32_t flags; u_int32_t response_token; ng_ID_t response_addr; }; typedef struct ng_ksocket_private *priv_p; /* Flags for priv_p */ #define KSF_CONNECTING 0x00000001 /* Waiting for connection complete */ #define KSF_ACCEPTING 0x00000002 /* Waiting for accept complete */ #define KSF_EOFSEEN 0x00000004 /* Have sent 0-length EOF mbuf */ #define KSF_CLONED 0x00000008 /* Cloned from an accepting socket */ #define KSF_EMBRYONIC 0x00000010 /* Cloned node with no hooks yet */ #define KSF_SENDING 0x00000020 /* Sending on socket */ /* Netgraph node methods */ static ng_constructor_t ng_ksocket_constructor; static ng_rcvmsg_t ng_ksocket_rcvmsg; static ng_shutdown_t ng_ksocket_shutdown; static ng_newhook_t ng_ksocket_newhook; static ng_rcvdata_t ng_ksocket_rcvdata; static ng_connect_t ng_ksocket_connect; static ng_disconnect_t ng_ksocket_disconnect; /* Alias structure */ struct ng_ksocket_alias { const char *name; const int value; const int family; }; /* Protocol family aliases */ static const struct ng_ksocket_alias ng_ksocket_families[] = { { "local", PF_LOCAL }, { "inet", PF_INET }, { "inet6", PF_INET6 }, { "atalk", PF_APPLETALK }, { "ipx", PF_IPX }, { "atm", PF_ATM }, { NULL, -1 }, }; /* Socket type aliases */ static const struct ng_ksocket_alias ng_ksocket_types[] = { { "stream", SOCK_STREAM }, { "dgram", SOCK_DGRAM }, { "raw", SOCK_RAW }, { "rdm", SOCK_RDM }, { "seqpacket", SOCK_SEQPACKET }, { NULL, -1 }, }; /* Protocol aliases */ static const struct ng_ksocket_alias ng_ksocket_protos[] = { { "ip", IPPROTO_IP, PF_INET }, { "raw", IPPROTO_RAW, PF_INET }, { "icmp", IPPROTO_ICMP, PF_INET }, { "igmp", IPPROTO_IGMP, PF_INET }, { "tcp", IPPROTO_TCP, PF_INET }, { "udp", IPPROTO_UDP, PF_INET }, { "gre", IPPROTO_GRE, PF_INET }, { "esp", IPPROTO_ESP, PF_INET }, { "ah", IPPROTO_AH, PF_INET }, { "swipe", IPPROTO_SWIPE, PF_INET }, { "encap", IPPROTO_ENCAP, PF_INET }, { "divert", IPPROTO_DIVERT, PF_INET }, { "pim", IPPROTO_PIM, PF_INET }, { "ddp", ATPROTO_DDP, PF_APPLETALK }, { "aarp", ATPROTO_AARP, PF_APPLETALK }, { NULL, -1 }, }; /* Helper functions */ static int ng_ksocket_check_accept(priv_p); static void ng_ksocket_finish_accept(priv_p); static void ng_ksocket_incoming(struct socket *so, void *arg, int waitflag); static int ng_ksocket_parse(const struct ng_ksocket_alias *aliases, const char *s, int family); static void ng_ksocket_incoming2(node_p node, hook_p hook, void *arg1, int waitflag); /************************************************************************ STRUCT SOCKADDR PARSE TYPE ************************************************************************/ /* Get the length of the data portion of a generic struct sockaddr */ static int ng_parse_generic_sockdata_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { const struct sockaddr *sa; sa = (const struct sockaddr *)(buf - SADATA_OFFSET); return (sa->sa_len < SADATA_OFFSET) ? 0 : sa->sa_len - SADATA_OFFSET; } /* Type for the variable length data portion of a generic struct sockaddr */ static const struct ng_parse_type ng_ksocket_generic_sockdata_type = { &ng_parse_bytearray_type, &ng_parse_generic_sockdata_getLength }; /* Type for a generic struct sockaddr */ static const struct ng_parse_struct_field ng_parse_generic_sockaddr_type_fields[] = { { "len", &ng_parse_uint8_type }, { "family", &ng_parse_uint8_type }, { "data", &ng_ksocket_generic_sockdata_type }, { NULL } }; static const struct ng_parse_type ng_ksocket_generic_sockaddr_type = { &ng_parse_struct_type, &ng_parse_generic_sockaddr_type_fields }; /* Convert a struct sockaddr from ASCII to binary. If its a protocol family that we specially handle, do that, otherwise defer to the generic parse type ng_ksocket_generic_sockaddr_type. */ static int ng_ksocket_sockaddr_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { struct sockaddr *const sa = (struct sockaddr *)buf; enum ng_parse_token tok; char fambuf[32]; int family, len; char *t; /* If next token is a left curly brace, use generic parse type */ if ((tok = ng_parse_get_token(s, off, &len)) == T_LBRACE) { return (*ng_ksocket_generic_sockaddr_type.supertype->parse) (&ng_ksocket_generic_sockaddr_type, s, off, start, buf, buflen); } /* Get socket address family followed by a slash */ while (isspace(s[*off])) (*off)++; if ((t = index(s + *off, '/')) == NULL) return (EINVAL); if ((len = t - (s + *off)) > sizeof(fambuf) - 1) return (EINVAL); strncpy(fambuf, s + *off, len); fambuf[len] = '\0'; *off += len + 1; if ((family = ng_ksocket_parse(ng_ksocket_families, fambuf, 0)) == -1) return (EINVAL); /* Set family */ if (*buflen < SADATA_OFFSET) return (ERANGE); sa->sa_family = family; /* Set family-specific data and length */ switch (sa->sa_family) { case PF_LOCAL: /* Get pathname */ { const int pathoff = OFFSETOF(struct sockaddr_un, sun_path); struct sockaddr_un *const sun = (struct sockaddr_un *)sa; int toklen, pathlen; char *path; if ((path = ng_get_string_token(s, off, &toklen, NULL)) == NULL) return (EINVAL); pathlen = strlen(path); if (pathlen > SOCK_MAXADDRLEN) { FREE(path, M_NETGRAPH_KSOCKET); return (E2BIG); } if (*buflen < pathoff + pathlen) { FREE(path, M_NETGRAPH_KSOCKET); return (ERANGE); } *off += toklen; bcopy(path, sun->sun_path, pathlen); sun->sun_len = pathoff + pathlen; FREE(path, M_NETGRAPH_KSOCKET); break; } case PF_INET: /* Get an IP address with optional port */ { struct sockaddr_in *const sin = (struct sockaddr_in *)sa; int i; /* Parse this: [:port] */ for (i = 0; i < 4; i++) { u_long val; char *eptr; val = strtoul(s + *off, &eptr, 10); if (val > 0xff || eptr == s + *off) return (EINVAL); *off += (eptr - (s + *off)); ((u_char *)&sin->sin_addr)[i] = (u_char)val; if (i < 3) { if (s[*off] != '.') return (EINVAL); (*off)++; } else if (s[*off] == ':') { (*off)++; val = strtoul(s + *off, &eptr, 10); if (val > 0xffff || eptr == s + *off) return (EINVAL); *off += (eptr - (s + *off)); sin->sin_port = htons(val); } else sin->sin_port = 0; } bzero(&sin->sin_zero, sizeof(sin->sin_zero)); sin->sin_len = sizeof(*sin); break; } #if 0 case PF_APPLETALK: /* XXX implement these someday */ case PF_INET6: case PF_IPX: #endif default: return (EINVAL); } /* Done */ *buflen = sa->sa_len; return (0); } /* Convert a struct sockaddr from binary to ASCII */ static int ng_ksocket_sockaddr_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { const struct sockaddr *sa = (const struct sockaddr *)(data + *off); int slen = 0; /* Output socket address, either in special or generic format */ switch (sa->sa_family) { case PF_LOCAL: { const int pathoff = OFFSETOF(struct sockaddr_un, sun_path); const struct sockaddr_un *sun = (const struct sockaddr_un *)sa; const int pathlen = sun->sun_len - pathoff; char pathbuf[SOCK_MAXADDRLEN + 1]; char *pathtoken; bcopy(sun->sun_path, pathbuf, pathlen); if ((pathtoken = ng_encode_string(pathbuf, pathlen)) == NULL) return (ENOMEM); slen += snprintf(cbuf, cbuflen, "local/%s", pathtoken); FREE(pathtoken, M_NETGRAPH_KSOCKET); if (slen >= cbuflen) return (ERANGE); *off += sun->sun_len; return (0); } case PF_INET: { const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; slen += snprintf(cbuf, cbuflen, "inet/%d.%d.%d.%d", ((const u_char *)&sin->sin_addr)[0], ((const u_char *)&sin->sin_addr)[1], ((const u_char *)&sin->sin_addr)[2], ((const u_char *)&sin->sin_addr)[3]); if (sin->sin_port != 0) { slen += snprintf(cbuf + strlen(cbuf), cbuflen - strlen(cbuf), ":%d", (u_int)ntohs(sin->sin_port)); } if (slen >= cbuflen) return (ERANGE); *off += sizeof(*sin); return(0); } #if 0 case PF_APPLETALK: /* XXX implement these someday */ case PF_INET6: case PF_IPX: #endif default: return (*ng_ksocket_generic_sockaddr_type.supertype->unparse) (&ng_ksocket_generic_sockaddr_type, data, off, cbuf, cbuflen); } } /* Parse type for struct sockaddr */ static const struct ng_parse_type ng_ksocket_sockaddr_type = { NULL, NULL, NULL, &ng_ksocket_sockaddr_parse, &ng_ksocket_sockaddr_unparse, NULL /* no such thing as a default struct sockaddr */ }; /************************************************************************ STRUCT NG_KSOCKET_SOCKOPT PARSE TYPE ************************************************************************/ /* Get length of the struct ng_ksocket_sockopt value field, which is the just the excess of the message argument portion over the length of the struct ng_ksocket_sockopt. */ static int ng_parse_sockoptval_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { static const int offset = OFFSETOF(struct ng_ksocket_sockopt, value); const struct ng_ksocket_sockopt *sopt; const struct ng_mesg *msg; sopt = (const struct ng_ksocket_sockopt *)(buf - offset); msg = (const struct ng_mesg *)((const u_char *)sopt - sizeof(*msg)); return msg->header.arglen - sizeof(*sopt); } /* Parse type for the option value part of a struct ng_ksocket_sockopt XXX Eventually, we should handle the different socket options specially. XXX This would avoid byte order problems, eg an integer value of 1 is XXX going to be "[1]" for little endian or "[3=1]" for big endian. */ static const struct ng_parse_type ng_ksocket_sockoptval_type = { &ng_parse_bytearray_type, &ng_parse_sockoptval_getLength }; /* Parse type for struct ng_ksocket_sockopt */ static const struct ng_parse_struct_field ng_ksocket_sockopt_type_fields[] = NG_KSOCKET_SOCKOPT_INFO(&ng_ksocket_sockoptval_type); static const struct ng_parse_type ng_ksocket_sockopt_type = { &ng_parse_struct_type, &ng_ksocket_sockopt_type_fields }; /* Parse type for struct ng_ksocket_accept */ static const struct ng_parse_struct_field ng_ksocket_accept_type_fields[] = NGM_KSOCKET_ACCEPT_INFO; static const struct ng_parse_type ng_ksocket_accept_type = { &ng_parse_struct_type, &ng_ksocket_accept_type_fields }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_ksocket_cmds[] = { { NGM_KSOCKET_COOKIE, NGM_KSOCKET_BIND, "bind", &ng_ksocket_sockaddr_type, NULL }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_LISTEN, "listen", &ng_parse_int32_type, NULL }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_ACCEPT, "accept", NULL, &ng_ksocket_accept_type }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_CONNECT, "connect", &ng_ksocket_sockaddr_type, &ng_parse_int32_type }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_GETNAME, "getname", NULL, &ng_ksocket_sockaddr_type }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_GETPEERNAME, "getpeername", NULL, &ng_ksocket_sockaddr_type }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_SETOPT, "setopt", &ng_ksocket_sockopt_type, NULL }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_GETOPT, "getopt", &ng_ksocket_sockopt_type, &ng_ksocket_sockopt_type }, { 0 } }; /* Node type descriptor */ static struct ng_type ng_ksocket_typestruct = { .version = NG_ABI_VERSION, .name = NG_KSOCKET_NODE_TYPE, .constructor = ng_ksocket_constructor, .rcvmsg = ng_ksocket_rcvmsg, .shutdown = ng_ksocket_shutdown, .newhook = ng_ksocket_newhook, .connect = ng_ksocket_connect, .rcvdata = ng_ksocket_rcvdata, .disconnect = ng_ksocket_disconnect, .cmdlist = ng_ksocket_cmds, }; NETGRAPH_INIT(ksocket, &ng_ksocket_typestruct); #define ERROUT(x) do { error = (x); goto done; } while (0) /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Node type constructor * The NODE part is assumed to be all set up. * There is already a reference to the node for us. */ static int ng_ksocket_constructor(node_p node) { priv_p priv; /* Allocate private structure */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH_KSOCKET, M_NOWAIT | M_ZERO); if (priv == NULL) return (ENOMEM); LIST_INIT(&priv->embryos); /* cross link them */ priv->node = node; NG_NODE_SET_PRIVATE(node, priv); /* Done */ return (0); } /* * Give our OK for a hook to be added. The hook name is of the * form "//" where the three components may * be decimal numbers or else aliases from the above lists. * * Connecting a hook amounts to opening the socket. Disconnecting * the hook closes the socket and destroys the node as well. */ static int ng_ksocket_newhook(node_p node, hook_p hook, const char *name0) { struct thread *td = curthread ? curthread : &thread0; /* XXX broken */ const priv_p priv = NG_NODE_PRIVATE(node); char *s1, *s2, name[NG_HOOKSIZ]; int family, type, protocol, error; /* Check if we're already connected */ if (priv->hook != NULL) return (EISCONN); if (priv->flags & KSF_CLONED) { if (priv->flags & KSF_EMBRYONIC) { /* Remove ourselves from our parent's embryo list */ LIST_REMOVE(priv, siblings); priv->flags &= ~KSF_EMBRYONIC; } } else { /* Extract family, type, and protocol from hook name */ snprintf(name, sizeof(name), "%s", name0); s1 = name; if ((s2 = index(s1, '/')) == NULL) return (EINVAL); *s2++ = '\0'; family = ng_ksocket_parse(ng_ksocket_families, s1, 0); if (family == -1) return (EINVAL); s1 = s2; if ((s2 = index(s1, '/')) == NULL) return (EINVAL); *s2++ = '\0'; type = ng_ksocket_parse(ng_ksocket_types, s1, 0); if (type == -1) return (EINVAL); s1 = s2; protocol = ng_ksocket_parse(ng_ksocket_protos, s1, family); if (protocol == -1) return (EINVAL); /* Create the socket */ error = socreate(family, &priv->so, type, protocol, td->td_ucred, td); if (error != 0) return (error); /* XXX call soreserve() ? */ } /* OK */ priv->hook = hook; return(0); } static int ng_ksocket_connect(hook_p hook) { node_p node = NG_HOOK_NODE(hook); const priv_p priv = NG_NODE_PRIVATE(node); struct socket *const so = priv->so; /* Add our hook for incoming data and other events */ priv->so->so_upcallarg = (caddr_t)node; priv->so->so_upcall = ng_ksocket_incoming; SOCKBUF_LOCK(&priv->so->so_rcv); priv->so->so_rcv.sb_flags |= SB_UPCALL; SOCKBUF_UNLOCK(&priv->so->so_rcv); SOCKBUF_LOCK(&priv->so->so_snd); priv->so->so_snd.sb_flags |= SB_UPCALL; SOCKBUF_UNLOCK(&priv->so->so_snd); SOCK_LOCK(priv->so); priv->so->so_state |= SS_NBIO; SOCK_UNLOCK(priv->so); /* * --Original comment-- * On a cloned socket we may have already received one or more * upcalls which we couldn't handle without a hook. Handle * those now. * We cannot call the upcall function directly * from here, because until this function has returned our * hook isn't connected. * * ---meta comment for -current --- * XXX This is dubius. * Upcalls between the time that the hook was * first created and now (on another processesor) will * be earlier on the queue than the request to finalise the hook. * By the time the hook is finalised, * The queued upcalls will have happenned and the code * will have discarded them because of a lack of a hook. * (socket not open). * * This is a bad byproduct of the complicated way in which hooks * are now created (3 daisy chained async events). * * Since we are a netgraph operation * We know that we hold a lock on this node. This forces the * request we make below to be queued rather than implemented * immediatly which will cause the upcall function to be called a bit * later. * However, as we will run any waiting queued operations immediatly * after doing this one, if we have not finalised the other end * of the hook, those queued operations will fail. */ if (priv->flags & KSF_CLONED) { ng_send_fn(node, NULL, &ng_ksocket_incoming2, so, M_NOWAIT); } return (0); } /* * Receive a control message */ static int ng_ksocket_rcvmsg(node_p node, item_p item, hook_p lasthook) { struct thread *td = curthread ? curthread : &thread0; /* XXX broken */ const priv_p priv = NG_NODE_PRIVATE(node); struct socket *const so = priv->so; struct ng_mesg *resp = NULL; int error = 0; struct ng_mesg *msg; ng_ID_t raddr; NGI_GET_MSG(item, msg); switch (msg->header.typecookie) { case NGM_KSOCKET_COOKIE: switch (msg->header.cmd) { case NGM_KSOCKET_BIND: { struct sockaddr *const sa = (struct sockaddr *)msg->data; /* Sanity check */ if (msg->header.arglen < SADATA_OFFSET || msg->header.arglen < sa->sa_len) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Bind */ error = sobind(so, sa, td); break; } case NGM_KSOCKET_LISTEN: { /* Sanity check */ if (msg->header.arglen != sizeof(int32_t)) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Listen */ error = solisten(so, *((int32_t *)msg->data), td); break; } case NGM_KSOCKET_ACCEPT: { /* Sanity check */ if (msg->header.arglen != 0) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Make sure the socket is capable of accepting */ if (!(so->so_options & SO_ACCEPTCONN)) ERROUT(EINVAL); if (priv->flags & KSF_ACCEPTING) ERROUT(EALREADY); error = ng_ksocket_check_accept(priv); if (error != 0 && error != EWOULDBLOCK) ERROUT(error); /* * If a connection is already complete, take it. * Otherwise let the upcall function deal with * the connection when it comes in. */ priv->response_token = msg->header.token; raddr = priv->response_addr = NGI_RETADDR(item); if (error == 0) { ng_ksocket_finish_accept(priv); } else priv->flags |= KSF_ACCEPTING; break; } case NGM_KSOCKET_CONNECT: { struct sockaddr *const sa = (struct sockaddr *)msg->data; /* Sanity check */ if (msg->header.arglen < SADATA_OFFSET || msg->header.arglen < sa->sa_len) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Do connect */ if ((so->so_state & SS_ISCONNECTING) != 0) ERROUT(EALREADY); if ((error = soconnect(so, sa, td)) != 0) { so->so_state &= ~SS_ISCONNECTING; ERROUT(error); } if ((so->so_state & SS_ISCONNECTING) != 0) { /* We will notify the sender when we connect */ priv->response_token = msg->header.token; raddr = priv->response_addr = NGI_RETADDR(item); priv->flags |= KSF_CONNECTING; ERROUT(EINPROGRESS); } break; } case NGM_KSOCKET_GETNAME: case NGM_KSOCKET_GETPEERNAME: { int (*func)(struct socket *so, struct sockaddr **nam); struct sockaddr *sa = NULL; int len; /* Sanity check */ if (msg->header.arglen != 0) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Get function */ if (msg->header.cmd == NGM_KSOCKET_GETPEERNAME) { if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) ERROUT(ENOTCONN); func = so->so_proto->pr_usrreqs->pru_peeraddr; } else func = so->so_proto->pr_usrreqs->pru_sockaddr; /* Get local or peer address */ if ((error = (*func)(so, &sa)) != 0) goto bail; len = (sa == NULL) ? 0 : sa->sa_len; /* Send it back in a response */ NG_MKRESPONSE(resp, msg, len, M_NOWAIT); if (resp == NULL) { error = ENOMEM; goto bail; } bcopy(sa, resp->data, len); bail: /* Cleanup */ if (sa != NULL) FREE(sa, M_SONAME); break; } case NGM_KSOCKET_GETOPT: { struct ng_ksocket_sockopt *ksopt = (struct ng_ksocket_sockopt *)msg->data; struct sockopt sopt; /* Sanity check */ if (msg->header.arglen != sizeof(*ksopt)) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Get response with room for option value */ NG_MKRESPONSE(resp, msg, sizeof(*ksopt) + NG_KSOCKET_MAX_OPTLEN, M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); /* Get socket option, and put value in the response */ sopt.sopt_dir = SOPT_GET; sopt.sopt_level = ksopt->level; sopt.sopt_name = ksopt->name; sopt.sopt_td = NULL; sopt.sopt_valsize = NG_KSOCKET_MAX_OPTLEN; ksopt = (struct ng_ksocket_sockopt *)resp->data; sopt.sopt_val = ksopt->value; if ((error = sogetopt(so, &sopt)) != 0) { NG_FREE_MSG(resp); break; } /* Set actual value length */ resp->header.arglen = sizeof(*ksopt) + sopt.sopt_valsize; break; } case NGM_KSOCKET_SETOPT: { struct ng_ksocket_sockopt *const ksopt = (struct ng_ksocket_sockopt *)msg->data; const int valsize = msg->header.arglen - sizeof(*ksopt); struct sockopt sopt; /* Sanity check */ if (valsize < 0) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Set socket option */ sopt.sopt_dir = SOPT_SET; sopt.sopt_level = ksopt->level; sopt.sopt_name = ksopt->name; sopt.sopt_val = ksopt->value; sopt.sopt_valsize = valsize; sopt.sopt_td = NULL; error = sosetopt(so, &sopt); break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } done: NG_RESPOND_MSG(error, node, item, resp); NG_FREE_MSG(msg); return (error); } /* * Receive incoming data on our hook. Send it out the socket. */ static int ng_ksocket_rcvdata(hook_p hook, item_p item) { struct thread *td = curthread ? curthread : &thread0; /* XXX broken */ const node_p node = NG_HOOK_NODE(hook); const priv_p priv = NG_NODE_PRIVATE(node); struct socket *const so = priv->so; struct sockaddr *sa = NULL; - meta_p meta; int error; struct mbuf *m; + struct sa_tag *stag; /* Avoid reentrantly sending on the socket */ if ((priv->flags & KSF_SENDING) != 0) { NG_FREE_ITEM(item); return (EDEADLK); } - /* Extract data and meta information */ + /* Extract data */ NGI_GET_M(item, m); - NGI_GET_META(item, meta); NG_FREE_ITEM(item); - /* If any meta info, look for peer socket address */ - if (meta != NULL) { - struct meta_field_header *field; + /* Look if socket address is stored in packet tags */ + if ((stag = (struct sa_tag *)m_tag_locate(m, NGM_KSOCKET_COOKIE, + NG_KSOCKET_TAG_SOCKADDR, NULL)) != NULL) + sa = &stag->sa; - /* Look for peer socket address */ - for (field = &meta->options[0]; - (caddr_t)field < (caddr_t)meta + meta->used_len; - field = (struct meta_field_header *) - ((caddr_t)field + field->len)) { - if (field->cookie != NGM_KSOCKET_COOKIE - || field->type != NG_KSOCKET_META_SOCKADDR) - continue; - sa = (struct sockaddr *)field->data; - break; - } - } - /* Send packet */ priv->flags |= KSF_SENDING; error = (*so->so_proto->pr_usrreqs->pru_sosend)(so, sa, 0, m, 0, 0, td); priv->flags &= ~KSF_SENDING; - /* Clean up and exit */ - NG_FREE_META(meta); return (error); } /* * Destroy node */ static int ng_ksocket_shutdown(node_p node) { const priv_p priv = NG_NODE_PRIVATE(node); priv_p embryo; /* Close our socket (if any) */ if (priv->so != NULL) { priv->so->so_upcall = NULL; SOCKBUF_LOCK(&priv->so->so_rcv); priv->so->so_rcv.sb_flags &= ~SB_UPCALL; SOCKBUF_UNLOCK(&priv->so->so_rcv); SOCKBUF_LOCK(&priv->so->so_snd); priv->so->so_snd.sb_flags &= ~SB_UPCALL; SOCKBUF_UNLOCK(&priv->so->so_snd); soclose(priv->so); priv->so = NULL; } /* If we are an embryo, take ourselves out of the parent's list */ if (priv->flags & KSF_EMBRYONIC) { LIST_REMOVE(priv, siblings); priv->flags &= ~KSF_EMBRYONIC; } /* Remove any embryonic children we have */ while (!LIST_EMPTY(&priv->embryos)) { embryo = LIST_FIRST(&priv->embryos); ng_rmnode_self(embryo->node); } /* Take down netgraph node */ bzero(priv, sizeof(*priv)); FREE(priv, M_NETGRAPH_KSOCKET); NG_NODE_SET_PRIVATE(node, NULL); NG_NODE_UNREF(node); /* let the node escape */ return (0); } /* * Hook disconnection */ static int ng_ksocket_disconnect(hook_p hook) { KASSERT(NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0, ("%s: numhooks=%d?", __func__, NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)))); if (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) ng_rmnode_self(NG_HOOK_NODE(hook)); return (0); } /************************************************************************ HELPER STUFF ************************************************************************/ /* * You should no-longer "just call" a netgraph node function * from an external asynchronous event. * This is because in doing so you are ignoring the locking on the netgraph * nodes. Instead call your function via * "int ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, * void *arg1, int arg2);" * this will call the function you chose, but will first do all the * locking rigmarole. Your function MAY only be called at some distant future * time (several millisecs away) so don't give it any arguments * that may be revoked soon (e.g. on your stack). * In this case even the 'so' argument is doubtful. * While the function request is being processed the node * has an extra reference and as such will not disappear until * the request has at least been done, but the 'so' may not be so lucky. * handle this by checking the validity of the node in the target function * before dereferencing the socket pointer. */ static void ng_ksocket_incoming(struct socket *so, void *arg, int waitflag) { const node_p node = arg; ng_send_fn(node, NULL, &ng_ksocket_incoming2, so, waitflag); } /* * When incoming data is appended to the socket, we get notified here. * This is also called whenever a significant event occurs for the socket. * We know that HOOK is NULL. Because of how we were called we know we have a * lock on this node an are participating inthe netgraph locking. * Our original caller may have queued this even some time ago and * we cannot trust that he even still exists. The node however is being * held with a reference by the queueing code, at least until we finish, * even if it has been zapped, so first check it's validiy * before we trust the socket (which was derived from it). */ static void ng_ksocket_incoming2(node_p node, hook_p hook, void *arg1, int waitflag) { struct socket *so = arg1; const priv_p priv = NG_NODE_PRIVATE(node); struct mbuf *m; struct ng_mesg *response; struct uio auio; int s, flags, error; s = splnet(); /* Sanity check */ if (NG_NODE_NOT_VALID(node)) { splx(s); return; } /* so = priv->so; *//* XXX could have derived this like so */ KASSERT(so == priv->so, ("%s: wrong socket", __func__)); /* Check whether a pending connect operation has completed */ if (priv->flags & KSF_CONNECTING) { if ((error = so->so_error) != 0) { so->so_error = 0; so->so_state &= ~SS_ISCONNECTING; } if (!(so->so_state & SS_ISCONNECTING)) { NG_MKMESSAGE(response, NGM_KSOCKET_COOKIE, NGM_KSOCKET_CONNECT, sizeof(int32_t), waitflag); if (response != NULL) { response->header.flags |= NGF_RESP; response->header.token = priv->response_token; *(int32_t *)response->data = error; /* * send an async "response" message * to the node that set us up * (if it still exists) */ NG_SEND_MSG_ID(error, node, response, priv->response_addr, 0); } priv->flags &= ~KSF_CONNECTING; } } /* Check whether a pending accept operation has completed */ if (priv->flags & KSF_ACCEPTING) { error = ng_ksocket_check_accept(priv); if (error != EWOULDBLOCK) priv->flags &= ~KSF_ACCEPTING; if (error == 0) ng_ksocket_finish_accept(priv); } /* * If we don't have a hook, we must handle data events later. When * the hook gets created and is connected, this upcall function * will be called again. */ if (priv->hook == NULL) { splx(s); return; } /* Read and forward available mbuf's */ auio.uio_td = NULL; auio.uio_resid = 1000000000; flags = MSG_DONTWAIT; while (1) { struct sockaddr *sa = NULL; - meta_p meta = NULL; struct mbuf *n; /* Try to get next packet from socket */ if ((error = (*so->so_proto->pr_usrreqs->pru_soreceive) (so, (so->so_state & SS_ISCONNECTED) ? NULL : &sa, &auio, &m, (struct mbuf **)0, &flags)) != 0) break; /* See if we got anything */ if (m == NULL) { if (sa != NULL) FREE(sa, M_SONAME); break; } /* Don't trust the various socket layers to get the packet header and length correct (eg. kern/15175) */ for (n = m, m->m_pkthdr.len = 0; n != NULL; n = n->m_next) m->m_pkthdr.len += n->m_len; - /* Put peer's socket address (if any) into a meta info blob */ + /* Put peer's socket address (if any) into a tag */ if (sa != NULL) { - struct meta_field_header *mhead; - u_int len; + struct sa_tag *stag; - len = sizeof(*meta) + sizeof(*mhead) + sa->sa_len; - MALLOC(meta, meta_p, len, M_NETGRAPH_META, - M_NOWAIT | M_ZERO); - if (meta == NULL) { + stag = (struct sa_tag *)m_tag_alloc(NGM_KSOCKET_COOKIE, + NG_KSOCKET_TAG_SOCKADDR, sa->sa_len, M_NOWAIT); + if (stag == NULL) { FREE(sa, M_SONAME); goto sendit; } - mhead = &meta->options[0]; - meta->allocated_len = len; - meta->used_len = len; - mhead->cookie = NGM_KSOCKET_COOKIE; - mhead->type = NG_KSOCKET_META_SOCKADDR; - mhead->len = sizeof(*mhead) + sa->sa_len; - bcopy(sa, mhead->data, sa->sa_len); + bcopy(sa, &stag->sa, sa->sa_len); FREE(sa, M_SONAME); + m_tag_prepend(m, &stag->tag); } -sendit: /* Forward data with optional peer sockaddr as meta info */ - NG_SEND_DATA(error, priv->hook, m, meta); +sendit: /* Forward data with optional peer sockaddr as packet tag */ + NG_SEND_DATA_ONLY(error, priv->hook, m); } /* * If the peer has closed the connection, forward a 0-length mbuf * to indicate end-of-file. */ if (so->so_rcv.sb_state & SBS_CANTRCVMORE && !(priv->flags & KSF_EOFSEEN)) { MGETHDR(m, waitflag, MT_DATA); if (m != NULL) { m->m_len = m->m_pkthdr.len = 0; NG_SEND_DATA_ONLY(error, priv->hook, m); } priv->flags |= KSF_EOFSEEN; } splx(s); } /* * Check for a completed incoming connection and return 0 if one is found. * Otherwise return the appropriate error code. */ static int ng_ksocket_check_accept(priv_p priv) { struct socket *const head = priv->so; int error; if ((error = head->so_error) != 0) { head->so_error = 0; return error; } /* Unlocked read. */ if (TAILQ_EMPTY(&head->so_comp)) { if (head->so_rcv.sb_state & SBS_CANTRCVMORE) return ECONNABORTED; return EWOULDBLOCK; } return 0; } /* * Handle the first completed incoming connection, assumed to be already * on the socket's so_comp queue. */ static void ng_ksocket_finish_accept(priv_p priv) { struct socket *const head = priv->so; struct socket *so; struct sockaddr *sa = NULL; struct ng_mesg *resp; struct ng_ksocket_accept *resp_data; node_p node; priv_p priv2; int len; int error; ACCEPT_LOCK(); so = TAILQ_FIRST(&head->so_comp); if (so == NULL) { /* Should never happen */ ACCEPT_UNLOCK(); return; } TAILQ_REMOVE(&head->so_comp, so, so_list); head->so_qlen--; so->so_qstate &= ~SQ_COMP; so->so_head = NULL; SOCK_LOCK(so); soref(so); so->so_state |= SS_NBIO; SOCK_UNLOCK(so); ACCEPT_UNLOCK(); /* XXX KNOTE(&head->so_rcv.sb_sel.si_note, 0); */ soaccept(so, &sa); len = OFFSETOF(struct ng_ksocket_accept, addr); if (sa != NULL) len += sa->sa_len; NG_MKMESSAGE(resp, NGM_KSOCKET_COOKIE, NGM_KSOCKET_ACCEPT, len, M_NOWAIT); if (resp == NULL) { soclose(so); goto out; } resp->header.flags |= NGF_RESP; resp->header.token = priv->response_token; /* Clone a ksocket node to wrap the new socket */ error = ng_make_node_common(&ng_ksocket_typestruct, &node); if (error) { FREE(resp, M_NETGRAPH); soclose(so); goto out; } if (ng_ksocket_constructor(node) != 0) { NG_NODE_UNREF(node); FREE(resp, M_NETGRAPH); soclose(so); goto out; } priv2 = NG_NODE_PRIVATE(node); priv2->so = so; priv2->flags |= KSF_CLONED | KSF_EMBRYONIC; /* * Insert the cloned node into a list of embryonic children * on the parent node. When a hook is created on the cloned * node it will be removed from this list. When the parent * is destroyed it will destroy any embryonic children it has. */ LIST_INSERT_HEAD(&priv->embryos, priv2, siblings); so->so_upcallarg = (caddr_t)node; so->so_upcall = ng_ksocket_incoming; SOCKBUF_LOCK(&so->so_rcv); so->so_rcv.sb_flags |= SB_UPCALL; SOCKBUF_UNLOCK(&so->so_rcv); SOCKBUF_LOCK(&so->so_snd); so->so_snd.sb_flags |= SB_UPCALL; SOCKBUF_UNLOCK(&so->so_snd); /* Fill in the response data and send it or return it to the caller */ resp_data = (struct ng_ksocket_accept *)resp->data; resp_data->nodeid = NG_NODE_ID(node); if (sa != NULL) bcopy(sa, &resp_data->addr, sa->sa_len); NG_SEND_MSG_ID(error, node, resp, priv->response_addr, 0); out: if (sa != NULL) FREE(sa, M_SONAME); } /* * Parse out either an integer value or an alias. */ static int ng_ksocket_parse(const struct ng_ksocket_alias *aliases, const char *s, int family) { int k, val; char *eptr; /* Try aliases */ for (k = 0; aliases[k].name != NULL; k++) { if (strcmp(s, aliases[k].name) == 0 && aliases[k].family == family) return aliases[k].value; } /* Try parsing as a number */ val = (int)strtoul(s, &eptr, 10); if (val < 0 || *eptr != '\0') return (-1); return (val); } Index: head/sys/netgraph/ng_ksocket.h =================================================================== --- head/sys/netgraph/ng_ksocket.h (revision 131107) +++ head/sys/netgraph/ng_ksocket.h (revision 131108) @@ -1,100 +1,106 @@ /* * ng_ksocket.h * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Author: Archie Cobbs * * $FreeBSD$ * $Whistle: ng_ksocket.h,v 1.1 1999/11/16 20:04:40 archie Exp $ */ #ifndef _NETGRAPH_NG_KSOCKET_H_ #define _NETGRAPH_NG_KSOCKET_H_ #include /* Node type name and magic cookie */ #define NG_KSOCKET_NODE_TYPE "ksocket" #define NGM_KSOCKET_COOKIE 942710669 /* For NGM_KSOCKET_SETOPT and NGM_KSOCKET_GETOPT control messages */ struct ng_ksocket_sockopt { int32_t level; /* second arg of [gs]etsockopt() */ int32_t name; /* third arg of [gs]etsockopt() */ u_char value[0]; /* fourth arg of [gs]etsockopt() */ }; /* Max length socket option we can return via NGM_KSOCKET_GETOPT XXX This should not be necessary, we should dynamically size XXX the response. Until then.. */ #define NG_KSOCKET_MAX_OPTLEN 1024 /* Keep this in sync with the above structure definition */ #define NG_KSOCKET_SOCKOPT_INFO(svtype) { \ { "level", &ng_parse_int32_type }, \ { "name", &ng_parse_int32_type }, \ { "value", (svtype) }, \ { NULL } \ } /* For NGM_KSOCKET_ACCEPT control message responses */ struct ng_ksocket_accept { u_int32_t nodeid; /* node ID of connected ksocket */ struct sockaddr addr; /* peer's address (variable length) */ }; /* Keep this in sync with the above structure definition */ #define NGM_KSOCKET_ACCEPT_INFO { \ { "nodeid", &ng_parse_hint32_type }, \ { "addr", &ng_ksocket_generic_sockaddr_type }, \ { NULL } \ } /* Netgraph commands */ enum { NGM_KSOCKET_BIND = 1, NGM_KSOCKET_LISTEN, NGM_KSOCKET_ACCEPT, NGM_KSOCKET_CONNECT, NGM_KSOCKET_GETNAME, NGM_KSOCKET_GETPEERNAME, NGM_KSOCKET_SETOPT, NGM_KSOCKET_GETOPT, }; -/* Meta information ID's */ -#define NG_KSOCKET_META_SOCKADDR 1 /* data is struct sockaddr */ +/* Structure for sockaddr tag */ +struct sa_tag { + struct m_tag tag; + struct sockaddr sa; +}; + +/* Tag information ID's */ +#define NG_KSOCKET_TAG_SOCKADDR 1 /* data is struct sockaddr */ #endif /* _NETGRAPH_NG_KSOCKET_H_ */ Index: head/sys/netgraph/ng_lmi.c =================================================================== --- head/sys/netgraph/ng_lmi.c (revision 131107) +++ head/sys/netgraph/ng_lmi.c (revision 131108) @@ -1,1082 +1,1082 @@ /* * ng_lmi.c * * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Author: Julian Elischer * * $FreeBSD$ * $Whistle: ng_lmi.c,v 1.38 1999/11/01 09:24:52 julian Exp $ */ /* * This node performs the frame relay LMI protocol. It knows how * to do ITU Annex A, ANSI Annex D, and "Group-of-Four" variants * of the protocol. * * A specific protocol can be forced by connecting the corresponding * hook to DLCI 0 or 1023 (as appropriate) of a frame relay link. * * Alternately, this node can do auto-detection of the LMI protocol * by connecting hook "auto0" to DLCI 0 and "auto1023" to DLCI 1023. */ #include #include #include #include #include #include #include #include #include #include /* * Human readable names for LMI */ #define NAME_ANNEXA NG_LMI_HOOK_ANNEXA #define NAME_ANNEXD NG_LMI_HOOK_ANNEXD #define NAME_GROUP4 NG_LMI_HOOK_GROUPOF4 #define NAME_NONE "None" #define MAX_DLCIS 128 #define MAXDLCI 1023 /* * DLCI states */ #define DLCI_NULL 0 #define DLCI_UP 1 #define DLCI_DOWN 2 /* * Any received LMI frame should be at least this long */ #define LMI_MIN_LENGTH 8 /* XXX verify */ /* * Netgraph node methods and type descriptor */ static ng_constructor_t nglmi_constructor; static ng_rcvmsg_t nglmi_rcvmsg; static ng_shutdown_t nglmi_shutdown; static ng_newhook_t nglmi_newhook; static ng_rcvdata_t nglmi_rcvdata; static ng_disconnect_t nglmi_disconnect; static int nglmi_checkdata(hook_p hook, struct mbuf *m); static struct ng_type typestruct = { .version = NG_ABI_VERSION, .name = NG_LMI_NODE_TYPE, .constructor = nglmi_constructor, .rcvmsg = nglmi_rcvmsg, .shutdown = nglmi_shutdown, .newhook = nglmi_newhook, .rcvdata = nglmi_rcvdata, .disconnect = nglmi_disconnect, }; NETGRAPH_INIT(lmi, &typestruct); /* * Info and status per node */ struct nglmi_softc { node_p node; /* netgraph node */ int flags; /* state */ int poll_count; /* the count of times for autolmi */ int poll_state; /* state of auto detect machine */ u_char remote_seq; /* sequence number the remote sent */ u_char local_seq; /* last sequence number we sent */ u_char protoID; /* 9 for group of 4, 8 otherwise */ u_long seq_retries; /* sent this how many time so far */ struct callout_handle handle; /* see timeout(9) */ int liv_per_full; int liv_rate; int livs; int need_full; hook_p lmi_channel; /* whatever we ended up using */ hook_p lmi_annexA; hook_p lmi_annexD; hook_p lmi_group4; hook_p lmi_channel0; /* auto-detect on DLCI 0 */ hook_p lmi_channel1023;/* auto-detect on DLCI 1023 */ char *protoname; /* cache protocol name */ u_char dlci_state[MAXDLCI + 1]; int invalidx; /* next dlci's to invalidate */ }; typedef struct nglmi_softc *sc_p; /* * Other internal functions */ static void LMI_ticker(void *arg); static void nglmi_startup_fixed(sc_p sc, hook_p hook); static void nglmi_startup_auto(sc_p sc); static void nglmi_startup(sc_p sc); static void nglmi_inquire(sc_p sc, int full); static void ngauto_state_machine(sc_p sc); /* * Values for 'flags' field * NB: the SCF_CONNECTED flag is set if and only if the timer is running. */ #define SCF_CONNECTED 0x01 /* connected to something */ #define SCF_AUTO 0x02 /* we are auto-detecting */ #define SCF_FIXED 0x04 /* we are fixed from the start */ #define SCF_LMITYPE 0x18 /* mask for determining Annex mode */ #define SCF_NOLMI 0x00 /* no LMI type selected yet */ #define SCF_ANNEX_A 0x08 /* running annex A mode */ #define SCF_ANNEX_D 0x10 /* running annex D mode */ #define SCF_GROUP4 0x18 /* running group of 4 */ #define SETLMITYPE(sc, annex) \ do { \ (sc)->flags &= ~SCF_LMITYPE; \ (sc)->flags |= (annex); \ } while (0) #define NOPROTO(sc) (((sc)->flags & SCF_LMITYPE) == SCF_NOLMI) #define ANNEXA(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_A) #define ANNEXD(sc) (((sc)->flags & SCF_LMITYPE) == SCF_ANNEX_D) #define GROUP4(sc) (((sc)->flags & SCF_LMITYPE) == SCF_GROUP4) #define LMIPOLLSIZE 3 #define LMI_PATIENCE 8 /* declare all DLCI DOWN after N LMI failures */ /* * Node constructor */ static int nglmi_constructor(node_p node) { sc_p sc; MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); if (sc == NULL) return (ENOMEM); callout_handle_init(&sc->handle); NG_NODE_SET_PRIVATE(node, sc); sc->protoname = NAME_NONE; sc->node = node; sc->liv_per_full = NG_LMI_SEQ_PER_FULL; /* make this dynamic */ sc->liv_rate = NG_LMI_KEEPALIVE_RATE; return (0); } /* * The LMI channel has a private pointer which is the same as the * node private pointer. The debug channel has a NULL private pointer. */ static int nglmi_newhook(node_p node, hook_p hook, const char *name) { sc_p sc = NG_NODE_PRIVATE(node); if (strcmp(name, NG_LMI_HOOK_DEBUG) == 0) { NG_HOOK_SET_PRIVATE(hook, NULL); return (0); } if (sc->flags & SCF_CONNECTED) { /* already connected, return an error */ return (EINVAL); } if (strcmp(name, NG_LMI_HOOK_ANNEXA) == 0) { sc->lmi_annexA = hook; NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node)); sc->protoID = 8; SETLMITYPE(sc, SCF_ANNEX_A); sc->protoname = NAME_ANNEXA; nglmi_startup_fixed(sc, hook); } else if (strcmp(name, NG_LMI_HOOK_ANNEXD) == 0) { sc->lmi_annexD = hook; NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node)); sc->protoID = 8; SETLMITYPE(sc, SCF_ANNEX_D); sc->protoname = NAME_ANNEXD; nglmi_startup_fixed(sc, hook); } else if (strcmp(name, NG_LMI_HOOK_GROUPOF4) == 0) { sc->lmi_group4 = hook; NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node)); sc->protoID = 9; SETLMITYPE(sc, SCF_GROUP4); sc->protoname = NAME_GROUP4; nglmi_startup_fixed(sc, hook); } else if (strcmp(name, NG_LMI_HOOK_AUTO0) == 0) { /* Note this, and if B is already installed, we're complete */ sc->lmi_channel0 = hook; sc->protoname = NAME_NONE; NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node)); if (sc->lmi_channel1023) nglmi_startup_auto(sc); } else if (strcmp(name, NG_LMI_HOOK_AUTO1023) == 0) { /* Note this, and if A is already installed, we're complete */ sc->lmi_channel1023 = hook; sc->protoname = NAME_NONE; NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node)); if (sc->lmi_channel0) nglmi_startup_auto(sc); } else return (EINVAL); /* unknown hook */ return (0); } /* * We have just attached to a live (we hope) node. * Fire out a LMI inquiry, and then start up the timers. */ static void LMI_ticker(void *arg) { sc_p sc = arg; int s = splnet(); if (sc->flags & SCF_AUTO) { ngauto_state_machine(sc); sc->handle = timeout(LMI_ticker, sc, NG_LMI_POLL_RATE * hz); } else { if (sc->livs++ >= sc->liv_per_full) { nglmi_inquire(sc, 1); /* sc->livs = 0; *//* do this when we get the answer! */ } else { nglmi_inquire(sc, 0); } sc->handle = timeout(LMI_ticker, sc, sc->liv_rate * hz); } splx(s); } static void nglmi_startup_fixed(sc_p sc, hook_p hook) { sc->flags |= (SCF_FIXED | SCF_CONNECTED); sc->lmi_channel = hook; nglmi_startup(sc); } static void nglmi_startup_auto(sc_p sc) { sc->flags |= (SCF_AUTO | SCF_CONNECTED); sc->poll_state = 0; /* reset state machine */ sc->poll_count = 0; nglmi_startup(sc); } static void nglmi_startup(sc_p sc) { sc->remote_seq = 0; sc->local_seq = 1; sc->seq_retries = 0; sc->livs = sc->liv_per_full - 1; /* start off the ticker in 1 sec */ sc->handle = timeout(LMI_ticker, sc, hz); } -#define META_PAD 16 static void nglmi_inquire(sc_p sc, int full) { struct mbuf *m; + struct ng_tag_prio *ptag; char *cptr, *start; int error; - meta_p meta = NULL; if (sc->lmi_channel == NULL) return; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { log(LOG_ERR, "nglmi: unable to start up LMI processing\n"); return; } m->m_pkthdr.rcvif = NULL; - /* Allocate a meta struct (and leave some slop for options to be - * added by other modules). */ - MALLOC(meta, meta_p, sizeof(*meta) + META_PAD, M_NETGRAPH_META, M_NOWAIT); - if (meta != NULL) { /* if it failed, well, it was optional anyhow */ - meta->used_len = (u_short) sizeof(struct ng_meta); - meta->allocated_len - = (u_short) sizeof(struct ng_meta) + META_PAD; - meta->flags = 0; - meta->priority = NG_LMI_LMI_PRIORITY; - meta->discardability = -1; + + /* Attach a tag to packet, marking it of link level state priority, so + * that device driver would put it in the beginning of queue */ + + ptag = (struct ng_tag_prio *)m_tag_alloc(NGM_GENERIC_COOKIE, NG_TAG_PRIO, + (sizeof(struct ng_tag_prio) - sizeof(struct m_tag)), M_NOWAIT); + if (ptag != NULL) { /* if it failed, well, it was optional anyhow */ + ptag->priority = NG_PRIO_LINKSTATE; + ptag->discardability = -1; + m_tag_prepend(m, &ptag->tag); } + m->m_data += 4; /* leave some room for a header */ cptr = start = mtod(m, char *); /* add in the header for an LMI inquiry. */ *cptr++ = 0x03; /* UI frame */ if (GROUP4(sc)) *cptr++ = 0x09; /* proto discriminator */ else *cptr++ = 0x08; /* proto discriminator */ *cptr++ = 0x00; /* call reference */ *cptr++ = 0x75; /* inquiry */ /* If we are Annex-D, there is this extra thing.. */ if (ANNEXD(sc)) *cptr++ = 0x95; /* ??? */ /* Add a request type */ if (ANNEXA(sc)) *cptr++ = 0x51; /* report type */ else *cptr++ = 0x01; /* report type */ *cptr++ = 0x01; /* size = 1 */ if (full) *cptr++ = 0x00; /* full */ else *cptr++ = 0x01; /* partial */ /* Add a link verification IE */ if (ANNEXA(sc)) *cptr++ = 0x53; /* verification IE */ else *cptr++ = 0x03; /* verification IE */ *cptr++ = 0x02; /* 2 extra bytes */ *cptr++ = sc->local_seq; *cptr++ = sc->remote_seq; sc->seq_retries++; /* Send it */ m->m_len = m->m_pkthdr.len = cptr - start; - NG_SEND_DATA(error, sc->lmi_channel, m, meta); + NG_SEND_DATA_ONLY(error, sc->lmi_channel, m); /* If we've been sending requests for long enough, and there has * been no response, then mark as DOWN, any DLCIs that are UP. */ if (sc->seq_retries == LMI_PATIENCE) { int count; for (count = 0; count < MAXDLCI; count++) if (sc->dlci_state[count] == DLCI_UP) sc->dlci_state[count] = DLCI_DOWN; } } /* * State machine for LMI auto-detect. The transitions are ordered * to try the more likely possibilities first. */ static void ngauto_state_machine(sc_p sc) { if ((sc->poll_count <= 0) || (sc->poll_count > LMIPOLLSIZE)) { /* time to change states in the auto probe machine */ /* capture wild values of poll_count while we are at it */ sc->poll_count = LMIPOLLSIZE; sc->poll_state++; } switch (sc->poll_state) { case 7: log(LOG_WARNING, "nglmi: no response from exchange\n"); default: /* capture bad states */ sc->poll_state = 1; case 1: sc->lmi_channel = sc->lmi_channel0; SETLMITYPE(sc, SCF_ANNEX_D); break; case 2: sc->lmi_channel = sc->lmi_channel1023; SETLMITYPE(sc, SCF_ANNEX_D); break; case 3: sc->lmi_channel = sc->lmi_channel0; SETLMITYPE(sc, SCF_ANNEX_A); break; case 4: sc->lmi_channel = sc->lmi_channel1023; SETLMITYPE(sc, SCF_GROUP4); break; case 5: sc->lmi_channel = sc->lmi_channel1023; SETLMITYPE(sc, SCF_ANNEX_A); break; case 6: sc->lmi_channel = sc->lmi_channel0; SETLMITYPE(sc, SCF_GROUP4); break; } /* send an inquirey encoded appropriatly */ nglmi_inquire(sc, 0); sc->poll_count--; } /* * Receive a netgraph control message. */ static int nglmi_rcvmsg(node_p node, item_p item, hook_p lasthook) { sc_p sc = NG_NODE_PRIVATE(node); struct ng_mesg *resp = NULL; int error = 0; struct ng_mesg *msg; NGI_GET_MSG(item, msg); switch (msg->header.typecookie) { case NGM_GENERIC_COOKIE: switch (msg->header.cmd) { case NGM_TEXT_STATUS: { char *arg; int pos, count; NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } arg = resp->data; pos = sprintf(arg, "protocol %s ", sc->protoname); if (sc->flags & SCF_FIXED) pos += sprintf(arg + pos, "fixed\n"); else if (sc->flags & SCF_AUTO) pos += sprintf(arg + pos, "auto-detecting\n"); else pos += sprintf(arg + pos, "auto on dlci %d\n", (sc->lmi_channel == sc->lmi_channel0) ? 0 : 1023); pos += sprintf(arg + pos, "keepalive period: %d seconds\n", sc->liv_rate); pos += sprintf(arg + pos, "unacknowledged keepalives: %ld\n", sc->seq_retries); for (count = 0; ((count <= MAXDLCI) && (pos < (NG_TEXTRESPONSE - 20))); count++) { if (sc->dlci_state[count]) { pos += sprintf(arg + pos, "dlci %d %s\n", count, (sc->dlci_state[count] == DLCI_UP) ? "up" : "down"); } } resp->header.arglen = pos + 1; break; } default: error = EINVAL; break; } break; case NGM_LMI_COOKIE: switch (msg->header.cmd) { case NGM_LMI_GET_STATUS: { struct nglmistat *stat; int k; NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT); if (!resp) { error = ENOMEM; break; } stat = (struct nglmistat *) resp->data; strncpy(stat->proto, sc->protoname, sizeof(stat->proto) - 1); strncpy(stat->hook, sc->protoname, sizeof(stat->hook) - 1); stat->autod = !!(sc->flags & SCF_AUTO); stat->fixed = !!(sc->flags & SCF_FIXED); for (k = 0; k <= MAXDLCI; k++) { switch (sc->dlci_state[k]) { case DLCI_UP: stat->up[k / 8] |= (1 << (k % 8)); /* fall through */ case DLCI_DOWN: stat->seen[k / 8] |= (1 << (k % 8)); break; } } break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } NG_RESPOND_MSG(error, node, item, resp); NG_FREE_MSG(msg); return (error); } #define STEPBY(stepsize) \ do { \ packetlen -= (stepsize); \ data += (stepsize); \ } while (0) /* * receive data, and use it to update our status. * Anything coming in on the debug port is discarded. */ static int nglmi_rcvdata(hook_p hook, item_p item) { sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); const u_char *data; unsigned short dlci; u_short packetlen; int resptype_seen = 0; int seq_seen = 0; struct mbuf *m; NGI_GET_M(item, m); NG_FREE_ITEM(item); if (NG_HOOK_PRIVATE(hook) == NULL) { goto drop; } packetlen = m->m_hdr.mh_len; /* XXX what if it's more than 1 mbuf? */ if ((packetlen > MHLEN) && !(m->m_flags & M_EXT)) { log(LOG_WARNING, "nglmi: packetlen (%d) too big\n", packetlen); goto drop; } if (m->m_len < packetlen && (m = m_pullup(m, packetlen)) == NULL) { log(LOG_WARNING, "nglmi: m_pullup failed for %d bytes\n", packetlen); return (0); } if (nglmi_checkdata(hook, m) == 0) return (0); /* pass the first 4 bytes (already checked in the nglmi_checkdata()) */ data = mtod(m, const u_char *); STEPBY(4); /* Now check if there is a 'locking shift'. This is only seen in * Annex D frames. don't bother checking, we already did that. Don't * increment immediatly as it might not be there. */ if (ANNEXD(sc)) STEPBY(1); /* If we get this far we should consider that it is a legitimate * frame and we know what it is. */ if (sc->flags & SCF_AUTO) { /* note the hook that this valid channel came from and drop * out of auto probe mode. */ if (ANNEXA(sc)) sc->protoname = NAME_ANNEXA; else if (ANNEXD(sc)) sc->protoname = NAME_ANNEXD; else if (GROUP4(sc)) sc->protoname = NAME_GROUP4; else { log(LOG_ERR, "nglmi: No known type\n"); goto drop; } sc->lmi_channel = hook; sc->flags &= ~SCF_AUTO; log(LOG_INFO, "nglmi: auto-detected %s LMI on DLCI %d\n", sc->protoname, hook == sc->lmi_channel0 ? 0 : 1023); } /* While there is more data in the status packet, keep processing * status items. First make sure there is enough data for the * segment descriptor's length field. */ while (packetlen >= 2) { u_int segtype = data[0]; u_int segsize = data[1]; /* Now that we know how long it claims to be, make sure * there is enough data for the next seg. */ if (packetlen < segsize + 2) break; switch (segtype) { case 0x01: case 0x51: if (resptype_seen) { log(LOG_WARNING, "nglmi: dup MSGTYPE\n"); goto nextIE; } resptype_seen++; /* The remote end tells us what kind of response * this is. Only expect a type 0 or 1. if we are a * full status, invalidate a few DLCIs just to see * that they are still ok. */ if (segsize != 1) goto nextIE; switch (data[2]) { case 1: /* partial status, do no extra processing */ break; case 0: { int count = 0; int idx = sc->invalidx; for (count = 0; count < 10; count++) { if (idx > MAXDLCI) idx = 0; if (sc->dlci_state[idx] == DLCI_UP) sc->dlci_state[idx] = DLCI_DOWN; idx++; } sc->invalidx = idx; /* we got and we wanted one. relax * now.. but don't reset to 0 if it * was unrequested. */ if (sc->livs > sc->liv_per_full) sc->livs = 0; break; } } break; case 0x03: case 0x53: /* The remote tells us what it thinks the sequence * numbers are. If it's not size 2, it must be a * duplicate to have gotten this far, skip it. */ if (seq_seen != 0) /* already seen seq numbers */ goto nextIE; if (segsize != 2) goto nextIE; sc->remote_seq = data[2]; if (sc->local_seq == data[3]) { sc->local_seq++; sc->seq_retries = 0; /* Note that all 3 Frame protocols seem to * not like 0 as a sequence number. */ if (sc->local_seq == 0) sc->local_seq = 1; } break; case 0x07: case 0x57: /* The remote tells us about a DLCI that it knows * about. There may be many of these in a single * status response */ switch (segsize) { case 6:/* only on 'group of 4' */ dlci = ((u_short) data[2] & 0xff) << 8; dlci |= (data[3] & 0xff); if ((dlci < 1024) && (dlci > 0)) { /* XXX */ } break; case 3: dlci = ((u_short) data[2] & 0x3f) << 4; dlci |= ((data[3] & 0x78) >> 3); if ((dlci < 1024) && (dlci > 0)) { /* set up the bottom half of the * support for that dlci if it's not * already been done */ /* store this information somewhere */ } break; default: goto nextIE; } if (sc->dlci_state[dlci] != DLCI_UP) { /* bring new DLCI to life */ /* may do more here some day */ if (sc->dlci_state[dlci] != DLCI_DOWN) log(LOG_INFO, "nglmi: DLCI %d became active\n", dlci); sc->dlci_state[dlci] = DLCI_UP; } break; } nextIE: STEPBY(segsize + 2); } NG_FREE_M(m); return (0); drop: NG_FREE_M(m); return (EINVAL); } /* * Check that a packet is entirely kosha. * return 1 of ok, and 0 if not. * All data is discarded if a 0 is returned. */ static int nglmi_checkdata(hook_p hook, struct mbuf *m) { sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); const u_char *data; u_short packetlen; unsigned short dlci; u_char type; u_char nextbyte; int seq_seen = 0; int resptype_seen = 0; /* 0 , 1 (partial) or 2 (full) */ int highest_dlci = 0; packetlen = m->m_hdr.mh_len; data = mtod(m, const u_char *); if (*data != 0x03) { log(LOG_WARNING, "nglmi: unexpected value in LMI(%d)\n", 1); goto reject; } STEPBY(1); /* look at the protocol ID */ nextbyte = *data; if (sc->flags & SCF_AUTO) { SETLMITYPE(sc, SCF_NOLMI); /* start with a clean slate */ switch (nextbyte) { case 0x8: sc->protoID = 8; break; case 0x9: SETLMITYPE(sc, SCF_GROUP4); sc->protoID = 9; break; default: log(LOG_WARNING, "nglmi: bad Protocol ID(%d)\n", (int) nextbyte); goto reject; } } else { if (nextbyte != sc->protoID) { log(LOG_WARNING, "nglmi: unexpected Protocol ID(%d)\n", (int) nextbyte); goto reject; } } STEPBY(1); /* check call reference (always null in non ISDN frame relay) */ if (*data != 0x00) { log(LOG_WARNING, "nglmi: unexpected Call Reference (0x%x)\n", data[-1]); goto reject; } STEPBY(1); /* check message type */ switch ((type = *data)) { case 0x75: /* Status enquiry */ log(LOG_WARNING, "nglmi: unexpected message type(0x%x)\n", data[-1]); goto reject; case 0x7D: /* Status message */ break; default: log(LOG_WARNING, "nglmi: unexpected msg type(0x%x) \n", (int) type); goto reject; } STEPBY(1); /* Now check if there is a 'locking shift'. This is only seen in * Annex D frames. Don't increment immediately as it might not be * there. */ nextbyte = *data; if (sc->flags & SCF_AUTO) { if (!(GROUP4(sc))) { if (nextbyte == 0x95) { SETLMITYPE(sc, SCF_ANNEX_D); STEPBY(1); } else SETLMITYPE(sc, SCF_ANNEX_A); } else if (nextbyte == 0x95) { log(LOG_WARNING, "nglmi: locking shift seen in G4\n"); goto reject; } } else { if (ANNEXD(sc)) { if (*data == 0x95) STEPBY(1); else { log(LOG_WARNING, "nglmi: locking shift missing\n"); goto reject; } } else if (*data == 0x95) { log(LOG_WARNING, "nglmi: locking shift seen\n"); goto reject; } } /* While there is more data in the status packet, keep processing * status items. First make sure there is enough data for the * segment descriptor's length field. */ while (packetlen >= 2) { u_int segtype = data[0]; u_int segsize = data[1]; /* Now that we know how long it claims to be, make sure * there is enough data for the next seg. */ if (packetlen < (segsize + 2)) { log(LOG_WARNING, "nglmi: IE longer than packet\n"); break; } switch (segtype) { case 0x01: case 0x51: /* According to MCI's HP analyser, we should just * ignore if there is mor ethan one of these (?). */ if (resptype_seen) { log(LOG_WARNING, "nglmi: dup MSGTYPE\n"); goto nextIE; } if (segsize != 1) { log(LOG_WARNING, "nglmi: MSGTYPE wrong size\n"); goto reject; } /* The remote end tells us what kind of response * this is. Only expect a type 0 or 1. if it was a * full (type 0) check we just asked for a type * full. */ switch (data[2]) { case 1:/* partial */ if (sc->livs > sc->liv_per_full) { log(LOG_WARNING, "nglmi: LIV when FULL expected\n"); goto reject; /* need full */ } resptype_seen = 1; break; case 0:/* full */ /* Full response is always acceptable */ resptype_seen = 2; break; default: log(LOG_WARNING, "nglmi: Unknown report type %d\n", data[2]); goto reject; } break; case 0x03: case 0x53: /* The remote tells us what it thinks the sequence * numbers are. I would have thought that there * needs to be one and only one of these, but MCI * want us to just ignore extras. (?) */ if (resptype_seen == 0) { log(LOG_WARNING, "nglmi: no TYPE before SEQ\n"); goto reject; } if (seq_seen != 0) /* already seen seq numbers */ goto nextIE; if (segsize != 2) { log(LOG_WARNING, "nglmi: bad SEQ sts size\n"); goto reject; } if (sc->local_seq != data[3]) { log(LOG_WARNING, "nglmi: unexpected SEQ\n"); goto reject; } seq_seen = 1; break; case 0x07: case 0x57: /* The remote tells us about a DLCI that it knows * about. There may be many of these in a single * status response */ if (seq_seen != 1) { /* already seen seq numbers? */ log(LOG_WARNING, "nglmi: No sequence before DLCI\n"); goto reject; } if (resptype_seen != 2) { /* must be full */ log(LOG_WARNING, "nglmi: No resp type before DLCI\n"); goto reject; } if (GROUP4(sc)) { if (segsize != 6) { log(LOG_WARNING, "nglmi: wrong IE segsize\n"); goto reject; } dlci = ((u_short) data[2] & 0xff) << 8; dlci |= (data[3] & 0xff); } else { if (segsize != 3) { log(LOG_WARNING, "nglmi: DLCI headersize of %d" " not supported\n", segsize - 1); goto reject; } dlci = ((u_short) data[2] & 0x3f) << 4; dlci |= ((data[3] & 0x78) >> 3); } /* async can only have one of these */ #if 0 /* async not yet accepted */ if (async && highest_dlci) { log(LOG_WARNING, "nglmi: Async with > 1 DLCI\n"); goto reject; } #endif /* Annex D says these will always be Ascending, but * the HP test for G4 says we should accept * duplicates, so for now allow that. ( <= vs. < ) */ #if 0 /* MCI tests want us to accept out of order for AnxD */ if ((!GROUP4(sc)) && (dlci < highest_dlci)) { /* duplicate or mis-ordered dlci */ /* (spec says they will increase in number) */ log(LOG_WARNING, "nglmi: DLCI out of order\n"); goto reject; } #endif if (dlci > 1023) { log(LOG_WARNING, "nglmi: DLCI out of range\n"); goto reject; } highest_dlci = dlci; break; default: log(LOG_WARNING, "nglmi: unknown LMI segment type %d\n", segtype); } nextIE: STEPBY(segsize + 2); } if (packetlen != 0) { /* partial junk at end? */ log(LOG_WARNING, "nglmi: %d bytes extra at end of packet\n", packetlen); goto print; } if (resptype_seen == 0) { log(LOG_WARNING, "nglmi: No response type seen\n"); goto reject; /* had no response type */ } if (seq_seen == 0) { log(LOG_WARNING, "nglmi: No sequence numbers seen\n"); goto reject; /* had no sequence numbers */ } return (1); print: { int i, j, k, pos; char buf[100]; int loc; const u_char *bp = mtod(m, const u_char *); k = i = 0; loc = (m->m_hdr.mh_len - packetlen); log(LOG_WARNING, "nglmi: error at location %d\n", loc); while (k < m->m_hdr.mh_len) { pos = 0; j = 0; while ((j++ < 16) && k < m->m_hdr.mh_len) { pos += sprintf(buf + pos, "%c%02x", ((loc == k) ? '>' : ' '), bp[k]); k++; } if (i == 0) log(LOG_WARNING, "nglmi: packet data:%s\n", buf); else log(LOG_WARNING, "%04d :%s\n", k, buf); i++; } } return (1); reject: { int i, j, k, pos; char buf[100]; int loc; const u_char *bp = mtod(m, const u_char *); k = i = 0; loc = (m->m_hdr.mh_len - packetlen); log(LOG_WARNING, "nglmi: error at location %d\n", loc); while (k < m->m_hdr.mh_len) { pos = 0; j = 0; while ((j++ < 16) && k < m->m_hdr.mh_len) { pos += sprintf(buf + pos, "%c%02x", ((loc == k) ? '>' : ' '), bp[k]); k++; } if (i == 0) log(LOG_WARNING, "nglmi: packet data:%s\n", buf); else log(LOG_WARNING, "%04d :%s\n", k, buf); i++; } } NG_FREE_M(m); return (0); } /* * Do local shutdown processing.. * Cut any remaining links and free our local resources. */ static int nglmi_shutdown(node_p node) { const sc_p sc = NG_NODE_PRIVATE(node); NG_NODE_SET_PRIVATE(node, NULL); NG_NODE_UNREF(sc->node); FREE(sc, M_NETGRAPH); return (0); } /* * Hook disconnection * For this type, removal of any link except "debug" destroys the node. */ static int nglmi_disconnect(hook_p hook) { const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); /* OK to remove debug hook(s) */ if (NG_HOOK_PRIVATE(hook) == NULL) return (0); /* Stop timer if it's currently active */ if (sc->flags & SCF_CONNECTED) untimeout(LMI_ticker, sc, sc->handle); /* Self-destruct */ if (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) ng_rmnode_self(NG_HOOK_NODE(hook)); return (0); } Index: head/sys/netgraph/ng_parse.c =================================================================== --- head/sys/netgraph/ng_parse.c (revision 131107) +++ head/sys/netgraph/ng_parse.c (revision 131108) @@ -1,1862 +1,1863 @@ /* * ng_parse.c * * Copyright (c) 1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Author: Archie Cobbs * * $Whistle: ng_parse.c,v 1.3 1999/11/29 01:43:48 archie Exp $ * $FreeBSD$ */ #include #include #include #include #include #include +#include #include #include #include #include #include #include #ifdef NG_SEPARATE_MALLOC MALLOC_DEFINE(M_NETGRAPH_PARSE, "netgraph_parse", "netgraph parse info"); #else #define M_NETGRAPH_PARSE M_NETGRAPH #endif /* Compute alignment for primitive integral types */ struct int16_temp { char x; int16_t y; }; struct int32_temp { char x; int32_t y; }; struct int64_temp { char x; int64_t y; }; #define INT8_ALIGNMENT 1 #define INT16_ALIGNMENT ((int)&((struct int16_temp *)0)->y) #define INT32_ALIGNMENT ((int)&((struct int32_temp *)0)->y) #define INT64_ALIGNMENT ((int)&((struct int64_temp *)0)->y) /* Output format for integral types */ #define INT_UNSIGNED 0 #define INT_SIGNED 1 #define INT_HEX 2 /* Type of composite object: struct, array, or fixedarray */ enum comptype { CT_STRUCT, CT_ARRAY, CT_FIXEDARRAY, }; /* Composite types helper functions */ static int ng_parse_composite(const struct ng_parse_type *type, const char *s, int *off, const u_char *start, u_char *const buf, int *buflen, enum comptype ctype); static int ng_unparse_composite(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen, enum comptype ctype); static int ng_get_composite_elem_default(const struct ng_parse_type *type, int index, const u_char *start, u_char *buf, int *buflen, enum comptype ctype); static int ng_get_composite_len(const struct ng_parse_type *type, const u_char *start, const u_char *buf, enum comptype ctype); static const struct ng_parse_type *ng_get_composite_etype(const struct ng_parse_type *type, int index, enum comptype ctype); static int ng_parse_get_elem_pad(const struct ng_parse_type *type, int index, enum comptype ctype, int posn); /* Parsing helper functions */ static int ng_parse_skip_value(const char *s, int off, int *lenp); /* Poor man's virtual method calls */ #define METHOD(t,m) (ng_get_ ## m ## _method(t)) #define INVOKE(t,m) (*METHOD(t,m)) static ng_parse_t *ng_get_parse_method(const struct ng_parse_type *t); static ng_unparse_t *ng_get_unparse_method(const struct ng_parse_type *t); static ng_getDefault_t *ng_get_getDefault_method(const struct ng_parse_type *t); static ng_getAlign_t *ng_get_getAlign_method(const struct ng_parse_type *t); #define ALIGNMENT(t) (METHOD(t, getAlign) == NULL ? \ 0 : INVOKE(t, getAlign)(t)) /* For converting binary to string */ #define NG_PARSE_APPEND(fmt, args...) \ do { \ int len; \ \ len = snprintf((cbuf), (cbuflen), \ fmt , ## args); \ if (len >= (cbuflen)) \ return (ERANGE); \ (cbuf) += len; \ (cbuflen) -= len; \ } while (0) /************************************************************************ PUBLIC FUNCTIONS ************************************************************************/ /* * Convert an ASCII string to binary according to the supplied type descriptor */ int ng_parse(const struct ng_parse_type *type, const char *string, int *off, u_char *buf, int *buflen) { return INVOKE(type, parse)(type, string, off, buf, buf, buflen); } /* * Convert binary to an ASCII string according to the supplied type descriptor */ int ng_unparse(const struct ng_parse_type *type, const u_char *data, char *cbuf, int cbuflen) { int off = 0; return INVOKE(type, unparse)(type, data, &off, cbuf, cbuflen); } /* * Fill in the default value according to the supplied type descriptor */ int ng_parse_getDefault(const struct ng_parse_type *type, u_char *buf, int *buflen) { ng_getDefault_t *const func = METHOD(type, getDefault); if (func == NULL) return (EOPNOTSUPP); return (*func)(type, buf, buf, buflen); } /************************************************************************ STRUCTURE TYPE ************************************************************************/ static int ng_struct_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { return ng_parse_composite(type, s, off, start, buf, buflen, CT_STRUCT); } static int ng_struct_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { return ng_unparse_composite(type, data, off, cbuf, cbuflen, CT_STRUCT); } static int ng_struct_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int off = 0; return ng_parse_composite(type, "{}", &off, start, buf, buflen, CT_STRUCT); } static int ng_struct_getAlign(const struct ng_parse_type *type) { const struct ng_parse_struct_field *field; int align = 0; for (field = type->info; field->name != NULL; field++) { int falign = ALIGNMENT(field->type); if (falign > align) align = falign; } return align; } const struct ng_parse_type ng_parse_struct_type = { NULL, NULL, NULL, ng_struct_parse, ng_struct_unparse, ng_struct_getDefault, ng_struct_getAlign }; /************************************************************************ FIXED LENGTH ARRAY TYPE ************************************************************************/ static int ng_fixedarray_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { return ng_parse_composite(type, s, off, start, buf, buflen, CT_FIXEDARRAY); } static int ng_fixedarray_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { return ng_unparse_composite(type, data, off, cbuf, cbuflen, CT_FIXEDARRAY); } static int ng_fixedarray_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int off = 0; return ng_parse_composite(type, "[]", &off, start, buf, buflen, CT_FIXEDARRAY); } static int ng_fixedarray_getAlign(const struct ng_parse_type *type) { const struct ng_parse_fixedarray_info *fi = type->info; return ALIGNMENT(fi->elementType); } const struct ng_parse_type ng_parse_fixedarray_type = { NULL, NULL, NULL, ng_fixedarray_parse, ng_fixedarray_unparse, ng_fixedarray_getDefault, ng_fixedarray_getAlign }; /************************************************************************ VARIABLE LENGTH ARRAY TYPE ************************************************************************/ static int ng_array_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { return ng_parse_composite(type, s, off, start, buf, buflen, CT_ARRAY); } static int ng_array_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { return ng_unparse_composite(type, data, off, cbuf, cbuflen, CT_ARRAY); } static int ng_array_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int off = 0; return ng_parse_composite(type, "[]", &off, start, buf, buflen, CT_ARRAY); } static int ng_array_getAlign(const struct ng_parse_type *type) { const struct ng_parse_array_info *ai = type->info; return ALIGNMENT(ai->elementType); } const struct ng_parse_type ng_parse_array_type = { NULL, NULL, NULL, ng_array_parse, ng_array_unparse, ng_array_getDefault, ng_array_getAlign }; /************************************************************************ INT8 TYPE ************************************************************************/ static int ng_int8_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { long val; int8_t val8; char *eptr; val = strtol(s + *off, &eptr, 0); if (val < (int8_t)0x80 || val > (u_int8_t)0xff || eptr == s + *off) return (EINVAL); *off = eptr - s; val8 = (int8_t)val; bcopy(&val8, buf, sizeof(int8_t)); *buflen = sizeof(int8_t); return (0); } static int ng_int8_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { const char *fmt; int fval; int8_t val; bcopy(data + *off, &val, sizeof(int8_t)); switch ((intptr_t)type->info) { case INT_SIGNED: fmt = "%d"; fval = val; break; case INT_UNSIGNED: fmt = "%u"; fval = (u_int8_t)val; break; case INT_HEX: fmt = "0x%x"; fval = (u_int8_t)val; break; default: panic("%s: unknown type", __func__); #ifdef RESTARTABLE_PANICS return(0); #endif } NG_PARSE_APPEND(fmt, fval); *off += sizeof(int8_t); return (0); } static int ng_int8_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int8_t val; if (*buflen < sizeof(int8_t)) return (ERANGE); val = 0; bcopy(&val, buf, sizeof(int8_t)); *buflen = sizeof(int8_t); return (0); } static int ng_int8_getAlign(const struct ng_parse_type *type) { return INT8_ALIGNMENT; } const struct ng_parse_type ng_parse_int8_type = { NULL, (void *)INT_SIGNED, NULL, ng_int8_parse, ng_int8_unparse, ng_int8_getDefault, ng_int8_getAlign }; const struct ng_parse_type ng_parse_uint8_type = { &ng_parse_int8_type, (void *)INT_UNSIGNED }; const struct ng_parse_type ng_parse_hint8_type = { &ng_parse_int8_type, (void *)INT_HEX }; /************************************************************************ INT16 TYPE ************************************************************************/ static int ng_int16_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { long val; int16_t val16; char *eptr; val = strtol(s + *off, &eptr, 0); if (val < (int16_t)0x8000 || val > (u_int16_t)0xffff || eptr == s + *off) return (EINVAL); *off = eptr - s; val16 = (int16_t)val; bcopy(&val16, buf, sizeof(int16_t)); *buflen = sizeof(int16_t); return (0); } static int ng_int16_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { const char *fmt; int fval; int16_t val; bcopy(data + *off, &val, sizeof(int16_t)); switch ((intptr_t)type->info) { case INT_SIGNED: fmt = "%d"; fval = val; break; case INT_UNSIGNED: fmt = "%u"; fval = (u_int16_t)val; break; case INT_HEX: fmt = "0x%x"; fval = (u_int16_t)val; break; default: panic("%s: unknown type", __func__); #ifdef RESTARTABLE_PANICS return(0); #endif } NG_PARSE_APPEND(fmt, fval); *off += sizeof(int16_t); return (0); } static int ng_int16_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int16_t val; if (*buflen < sizeof(int16_t)) return (ERANGE); val = 0; bcopy(&val, buf, sizeof(int16_t)); *buflen = sizeof(int16_t); return (0); } static int ng_int16_getAlign(const struct ng_parse_type *type) { return INT16_ALIGNMENT; } const struct ng_parse_type ng_parse_int16_type = { NULL, (void *)INT_SIGNED, NULL, ng_int16_parse, ng_int16_unparse, ng_int16_getDefault, ng_int16_getAlign }; const struct ng_parse_type ng_parse_uint16_type = { &ng_parse_int16_type, (void *)INT_UNSIGNED }; const struct ng_parse_type ng_parse_hint16_type = { &ng_parse_int16_type, (void *)INT_HEX }; /************************************************************************ INT32 TYPE ************************************************************************/ static int ng_int32_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { long val; /* assumes long is at least 32 bits */ int32_t val32; char *eptr; val = strtol(s + *off, &eptr, 0); if (val < (int32_t)0x80000000 || val > (u_int32_t)0xffffffff || eptr == s + *off) return (EINVAL); *off = eptr - s; val32 = (int32_t)val; bcopy(&val32, buf, sizeof(int32_t)); *buflen = sizeof(int32_t); return (0); } static int ng_int32_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { const char *fmt; long fval; int32_t val; bcopy(data + *off, &val, sizeof(int32_t)); switch ((intptr_t)type->info) { case INT_SIGNED: fmt = "%ld"; fval = val; break; case INT_UNSIGNED: fmt = "%lu"; fval = (u_int32_t)val; break; case INT_HEX: fmt = "0x%lx"; fval = (u_int32_t)val; break; default: panic("%s: unknown type", __func__); #ifdef RESTARTABLE_PANICS return(0); #endif } NG_PARSE_APPEND(fmt, fval); *off += sizeof(int32_t); return (0); } static int ng_int32_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int32_t val; if (*buflen < sizeof(int32_t)) return (ERANGE); val = 0; bcopy(&val, buf, sizeof(int32_t)); *buflen = sizeof(int32_t); return (0); } static int ng_int32_getAlign(const struct ng_parse_type *type) { return INT32_ALIGNMENT; } const struct ng_parse_type ng_parse_int32_type = { NULL, (void *)INT_SIGNED, NULL, ng_int32_parse, ng_int32_unparse, ng_int32_getDefault, ng_int32_getAlign }; const struct ng_parse_type ng_parse_uint32_type = { &ng_parse_int32_type, (void *)INT_UNSIGNED }; const struct ng_parse_type ng_parse_hint32_type = { &ng_parse_int32_type, (void *)INT_HEX }; /************************************************************************ INT64 TYPE ************************************************************************/ static int ng_int64_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { quad_t val; int64_t val64; char *eptr; val = strtoq(s + *off, &eptr, 0); if (eptr == s + *off) return (EINVAL); *off = eptr - s; val64 = (int64_t)val; bcopy(&val64, buf, sizeof(int64_t)); *buflen = sizeof(int64_t); return (0); } static int ng_int64_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { const char *fmt; long long fval; int64_t val; bcopy(data + *off, &val, sizeof(int64_t)); switch ((intptr_t)type->info) { case INT_SIGNED: fmt = "%lld"; fval = val; break; case INT_UNSIGNED: fmt = "%llu"; fval = (u_int64_t)val; break; case INT_HEX: fmt = "0x%llx"; fval = (u_int64_t)val; break; default: panic("%s: unknown type", __func__); #ifdef RESTARTABLE_PANICS return(0); #endif } NG_PARSE_APPEND(fmt, fval); *off += sizeof(int64_t); return (0); } static int ng_int64_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { int64_t val; if (*buflen < sizeof(int64_t)) return (ERANGE); val = 0; bcopy(&val, buf, sizeof(int64_t)); *buflen = sizeof(int64_t); return (0); } static int ng_int64_getAlign(const struct ng_parse_type *type) { return INT64_ALIGNMENT; } const struct ng_parse_type ng_parse_int64_type = { NULL, (void *)INT_SIGNED, NULL, ng_int64_parse, ng_int64_unparse, ng_int64_getDefault, ng_int64_getAlign }; const struct ng_parse_type ng_parse_uint64_type = { &ng_parse_int64_type, (void *)INT_UNSIGNED }; const struct ng_parse_type ng_parse_hint64_type = { &ng_parse_int64_type, (void *)INT_HEX }; /************************************************************************ STRING TYPE ************************************************************************/ static int ng_string_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { char *sval; int len; int slen; if ((sval = ng_get_string_token(s, off, &len, &slen)) == NULL) return (EINVAL); *off += len; bcopy(sval, buf, slen + 1); FREE(sval, M_NETGRAPH_PARSE); *buflen = slen + 1; return (0); } static int ng_string_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { const char *const raw = (const char *)data + *off; char *const s = ng_encode_string(raw, strlen(raw)); if (s == NULL) return (ENOMEM); NG_PARSE_APPEND("%s", s); *off += strlen(raw) + 1; FREE(s, M_NETGRAPH_PARSE); return (0); } static int ng_string_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { if (*buflen < 1) return (ERANGE); buf[0] = (u_char)'\0'; *buflen = 1; return (0); } const struct ng_parse_type ng_parse_string_type = { NULL, NULL, NULL, ng_string_parse, ng_string_unparse, ng_string_getDefault, NULL }; /************************************************************************ FIXED BUFFER STRING TYPE ************************************************************************/ static int ng_fixedstring_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { const struct ng_parse_fixedstring_info *const fi = type->info; char *sval; int len; int slen; if ((sval = ng_get_string_token(s, off, &len, &slen)) == NULL) return (EINVAL); if (slen + 1 > fi->bufSize) return (E2BIG); *off += len; bcopy(sval, buf, slen); FREE(sval, M_NETGRAPH_PARSE); bzero(buf + slen, fi->bufSize - slen); *buflen = fi->bufSize; return (0); } static int ng_fixedstring_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { const struct ng_parse_fixedstring_info *const fi = type->info; int error, temp = *off; if ((error = ng_string_unparse(type, data, &temp, cbuf, cbuflen)) != 0) return (error); *off += fi->bufSize; return (0); } static int ng_fixedstring_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { const struct ng_parse_fixedstring_info *const fi = type->info; if (*buflen < fi->bufSize) return (ERANGE); bzero(buf, fi->bufSize); *buflen = fi->bufSize; return (0); } const struct ng_parse_type ng_parse_fixedstring_type = { NULL, NULL, NULL, ng_fixedstring_parse, ng_fixedstring_unparse, ng_fixedstring_getDefault, NULL }; const struct ng_parse_fixedstring_info ng_parse_nodebuf_info = { NG_NODESIZ }; const struct ng_parse_type ng_parse_nodebuf_type = { &ng_parse_fixedstring_type, &ng_parse_nodebuf_info }; const struct ng_parse_fixedstring_info ng_parse_hookbuf_info = { NG_HOOKSIZ }; const struct ng_parse_type ng_parse_hookbuf_type = { &ng_parse_fixedstring_type, &ng_parse_hookbuf_info }; const struct ng_parse_fixedstring_info ng_parse_pathbuf_info = { NG_PATHSIZ }; const struct ng_parse_type ng_parse_pathbuf_type = { &ng_parse_fixedstring_type, &ng_parse_pathbuf_info }; const struct ng_parse_fixedstring_info ng_parse_typebuf_info = { NG_TYPESIZ }; const struct ng_parse_type ng_parse_typebuf_type = { &ng_parse_fixedstring_type, &ng_parse_typebuf_info }; const struct ng_parse_fixedstring_info ng_parse_cmdbuf_info = { NG_CMDSTRSIZ }; const struct ng_parse_type ng_parse_cmdbuf_type = { &ng_parse_fixedstring_type, &ng_parse_cmdbuf_info }; /************************************************************************ EXPLICITLY SIZED STRING TYPE ************************************************************************/ static int ng_sizedstring_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { char *sval; int len; int slen; if ((sval = ng_get_string_token(s, off, &len, &slen)) == NULL) return (EINVAL); if (slen > 0xffff) return (EINVAL); *off += len; *((u_int16_t *)buf) = (u_int16_t)slen; bcopy(sval, buf + 2, slen); FREE(sval, M_NETGRAPH_PARSE); *buflen = 2 + slen; return (0); } static int ng_sizedstring_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { const char *const raw = (const char *)data + *off + 2; const int slen = *((const u_int16_t *)(data + *off)); char *const s = ng_encode_string(raw, slen); if (s == NULL) return (ENOMEM); NG_PARSE_APPEND("%s", s); FREE(s, M_NETGRAPH_PARSE); *off += slen + 2; return (0); } static int ng_sizedstring_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { if (*buflen < 2) return (ERANGE); bzero(buf, 2); *buflen = 2; return (0); } const struct ng_parse_type ng_parse_sizedstring_type = { NULL, NULL, NULL, ng_sizedstring_parse, ng_sizedstring_unparse, ng_sizedstring_getDefault, NULL }; /************************************************************************ IP ADDRESS TYPE ************************************************************************/ static int ng_ipaddr_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { int i, error; for (i = 0; i < 4; i++) { if ((error = ng_int8_parse(&ng_parse_int8_type, s, off, start, buf + i, buflen)) != 0) return (error); if (i < 3 && s[*off] != '.') return (EINVAL); (*off)++; } *buflen = 4; return (0); } static int ng_ipaddr_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { struct in_addr ip; bcopy(data + *off, &ip, sizeof(ip)); NG_PARSE_APPEND("%d.%d.%d.%d", ((u_char *)&ip)[0], ((u_char *)&ip)[1], ((u_char *)&ip)[2], ((u_char *)&ip)[3]); *off += sizeof(ip); return (0); } static int ng_ipaddr_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { struct in_addr ip = { 0 }; if (*buflen < sizeof(ip)) return (ERANGE); bcopy(&ip, buf, sizeof(ip)); *buflen = sizeof(ip); return (0); } const struct ng_parse_type ng_parse_ipaddr_type = { NULL, NULL, NULL, ng_ipaddr_parse, ng_ipaddr_unparse, ng_ipaddr_getDefault, ng_int32_getAlign }; /************************************************************************ ETHERNET ADDRESS TYPE ************************************************************************/ static int ng_enaddr_parse(const struct ng_parse_type *type, const char *s, int *const off, const u_char *const start, u_char *const buf, int *const buflen) { char *eptr; u_long val; int i; if (*buflen < ETHER_ADDR_LEN) return (ERANGE); for (i = 0; i < ETHER_ADDR_LEN; i++) { val = strtoul(s + *off, &eptr, 16); if (val > 0xff || eptr == s + *off) return (EINVAL); buf[i] = (u_char)val; *off = (eptr - s); if (i < ETHER_ADDR_LEN - 1) { if (*eptr != ':') return (EINVAL); (*off)++; } } *buflen = ETHER_ADDR_LEN; return (0); } static int ng_enaddr_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { int len; len = snprintf(cbuf, cbuflen, "%02x:%02x:%02x:%02x:%02x:%02x", data[*off], data[*off + 1], data[*off + 2], data[*off + 3], data[*off + 4], data[*off + 5]); if (len >= cbuflen) return (ERANGE); *off += ETHER_ADDR_LEN; return (0); } const struct ng_parse_type ng_parse_enaddr_type = { NULL, NULL, NULL, ng_enaddr_parse, ng_enaddr_unparse, NULL, 0 }; /************************************************************************ BYTE ARRAY TYPE ************************************************************************/ /* Get the length of a byte array */ static int ng_parse_bytearray_subtype_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { ng_parse_array_getLength_t *const getLength = type->private; return (*getLength)(type, start, buf); } /* Byte array element type is hex int8 */ static const struct ng_parse_array_info ng_parse_bytearray_subtype_info = { &ng_parse_hint8_type, &ng_parse_bytearray_subtype_getLength, NULL }; static const struct ng_parse_type ng_parse_bytearray_subtype = { &ng_parse_array_type, &ng_parse_bytearray_subtype_info }; static int ng_bytearray_parse(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen) { char *str; int toklen; int slen; /* We accept either an array of bytes or a string constant */ if ((str = ng_get_string_token(s, off, &toklen, &slen)) != NULL) { ng_parse_array_getLength_t *const getLength = type->info; int arraylen; arraylen = (*getLength)(type, start, buf); if (arraylen > *buflen) { FREE(str, M_NETGRAPH_PARSE); return (ERANGE); } if (slen > arraylen) { FREE(str, M_NETGRAPH_PARSE); return (E2BIG); } bcopy(str, buf, slen); bzero(buf + slen, arraylen - slen); FREE(str, M_NETGRAPH_PARSE); *off += toklen; *buflen = arraylen; return (0); } else { struct ng_parse_type subtype; subtype = ng_parse_bytearray_subtype; (const void *)subtype.private = type->info; return ng_array_parse(&subtype, s, off, start, buf, buflen); } } static int ng_bytearray_unparse(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen) { struct ng_parse_type subtype; subtype = ng_parse_bytearray_subtype; (const void *)subtype.private = type->info; return ng_array_unparse(&subtype, data, off, cbuf, cbuflen); } static int ng_bytearray_getDefault(const struct ng_parse_type *type, const u_char *const start, u_char *buf, int *buflen) { struct ng_parse_type subtype; subtype = ng_parse_bytearray_subtype; (const void *)subtype.private = type->info; return ng_array_getDefault(&subtype, start, buf, buflen); } const struct ng_parse_type ng_parse_bytearray_type = { NULL, NULL, NULL, ng_bytearray_parse, ng_bytearray_unparse, ng_bytearray_getDefault, NULL }; /************************************************************************ STRUCT NG_MESG TYPE ************************************************************************/ /* Get msg->header.arglen when "buf" is pointing to msg->data */ static int ng_parse_ng_mesg_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { const struct ng_mesg *msg; msg = (const struct ng_mesg *)(buf - sizeof(*msg)); return msg->header.arglen; } /* Type for the variable length data portion of a struct ng_mesg */ static const struct ng_parse_type ng_msg_data_type = { &ng_parse_bytearray_type, &ng_parse_ng_mesg_getLength }; /* Type for the entire struct ng_mesg header with data section */ static const struct ng_parse_struct_field ng_parse_ng_mesg_type_fields[] = NG_GENERIC_NG_MESG_INFO(&ng_msg_data_type); const struct ng_parse_type ng_parse_ng_mesg_type = { &ng_parse_struct_type, &ng_parse_ng_mesg_type_fields, }; /************************************************************************ COMPOSITE HELPER ROUTINES ************************************************************************/ /* * Convert a structure or array from ASCII to binary */ static int ng_parse_composite(const struct ng_parse_type *type, const char *s, int *off, const u_char *const start, u_char *const buf, int *buflen, const enum comptype ctype) { const int num = ng_get_composite_len(type, start, buf, ctype); int nextIndex = 0; /* next implicit array index */ u_int index; /* field or element index */ int *foff; /* field value offsets in string */ int align, len, blen, error = 0; /* Initialize */ MALLOC(foff, int *, num * sizeof(*foff), M_NETGRAPH_PARSE, M_NOWAIT | M_ZERO); if (foff == NULL) { error = ENOMEM; goto done; } /* Get opening brace/bracket */ if (ng_parse_get_token(s, off, &len) != (ctype == CT_STRUCT ? T_LBRACE : T_LBRACKET)) { error = EINVAL; goto done; } *off += len; /* Get individual element value positions in the string */ for (;;) { enum ng_parse_token tok; /* Check for closing brace/bracket */ tok = ng_parse_get_token(s, off, &len); if (tok == (ctype == CT_STRUCT ? T_RBRACE : T_RBRACKET)) { *off += len; break; } /* For arrays, the 'name' (ie, index) is optional, so distinguish name from values by seeing if the next token is an equals sign */ if (ctype != CT_STRUCT) { int len2, off2; char *eptr; /* If an opening brace/bracket, index is implied */ if (tok == T_LBRACE || tok == T_LBRACKET) { index = nextIndex++; goto gotIndex; } /* Might be an index, might be a value, either way... */ if (tok != T_WORD) { error = EINVAL; goto done; } /* If no equals sign follows, index is implied */ off2 = *off + len; if (ng_parse_get_token(s, &off2, &len2) != T_EQUALS) { index = nextIndex++; goto gotIndex; } /* Index was specified explicitly; parse it */ index = (u_int)strtoul(s + *off, &eptr, 0); if (index < 0 || eptr - (s + *off) != len) { error = EINVAL; goto done; } nextIndex = index + 1; *off += len + len2; } else { /* a structure field */ const struct ng_parse_struct_field *const fields = type->info; /* Find the field by name (required) in field list */ if (tok != T_WORD) { error = EINVAL; goto done; } for (index = 0; index < num; index++) { const struct ng_parse_struct_field *const field = &fields[index]; if (strncmp(&s[*off], field->name, len) == 0 && field->name[len] == '\0') break; } if (index == num) { error = ENOENT; goto done; } *off += len; /* Get equals sign */ if (ng_parse_get_token(s, off, &len) != T_EQUALS) { error = EINVAL; goto done; } *off += len; } gotIndex: /* Check array index */ if (index >= num) { error = E2BIG; goto done; } /* Save value's position and skip over it for now */ if (foff[index] != 0) { error = EALREADY; /* duplicate */ goto done; } while (isspace(s[*off])) (*off)++; foff[index] = *off; if ((error = ng_parse_skip_value(s, *off, &len)) != 0) goto done; *off += len; } /* Now build binary structure from supplied values and defaults */ for (blen = index = 0; index < num; index++) { const struct ng_parse_type *const etype = ng_get_composite_etype(type, index, ctype); int k, pad, vlen; /* Zero-pad any alignment bytes */ pad = ng_parse_get_elem_pad(type, index, ctype, blen); for (k = 0; k < pad; k++) { if (blen >= *buflen) { error = ERANGE; goto done; } buf[blen++] = 0; } /* Get value */ vlen = *buflen - blen; if (foff[index] == 0) { /* use default value */ error = ng_get_composite_elem_default(type, index, start, buf + blen, &vlen, ctype); } else { /* parse given value */ *off = foff[index]; error = INVOKE(etype, parse)(etype, s, off, start, buf + blen, &vlen); } if (error != 0) goto done; blen += vlen; } /* Make total composite structure size a multiple of its alignment */ if ((align = ALIGNMENT(type)) != 0) { while (blen % align != 0) { if (blen >= *buflen) { error = ERANGE; goto done; } buf[blen++] = 0; } } /* Done */ *buflen = blen; done: if (foff != NULL) FREE(foff, M_NETGRAPH_PARSE); return (error); } /* * Convert an array or structure from binary to ASCII */ static int ng_unparse_composite(const struct ng_parse_type *type, const u_char *data, int *off, char *cbuf, int cbuflen, const enum comptype ctype) { const struct ng_mesg *const hdr = (const struct ng_mesg *)(data - sizeof(*hdr)); const int num = ng_get_composite_len(type, data, data + *off, ctype); const int workSize = 20 * 1024; /* XXX hard coded constant */ int nextIndex = 0, didOne = 0; int error, index; u_char *workBuf; /* Get workspace for checking default values */ MALLOC(workBuf, u_char *, workSize, M_NETGRAPH_PARSE, M_NOWAIT); if (workBuf == NULL) return (ENOMEM); /* Opening brace/bracket */ NG_PARSE_APPEND("%c", (ctype == CT_STRUCT) ? '{' : '['); /* Do each item */ for (index = 0; index < num; index++) { const struct ng_parse_type *const etype = ng_get_composite_etype(type, index, ctype); /* Skip any alignment pad bytes */ *off += ng_parse_get_elem_pad(type, index, ctype, *off); /* * See if element is equal to its default value; skip if so. * Copy struct ng_mesg header for types that peek into it. */ if (sizeof(*hdr) + *off < workSize) { int tempsize = workSize - sizeof(*hdr) - *off; bcopy(hdr, workBuf, sizeof(*hdr) + *off); if (ng_get_composite_elem_default(type, index, workBuf + sizeof(*hdr), workBuf + sizeof(*hdr) + *off, &tempsize, ctype) == 0 && bcmp(workBuf + sizeof(*hdr) + *off, data + *off, tempsize) == 0) { *off += tempsize; continue; } } /* Print name= */ NG_PARSE_APPEND(" "); if (ctype != CT_STRUCT) { if (index != nextIndex) { nextIndex = index; NG_PARSE_APPEND("%d=", index); } nextIndex++; } else { const struct ng_parse_struct_field *const fields = type->info; NG_PARSE_APPEND("%s=", fields[index].name); } /* Print value */ if ((error = INVOKE(etype, unparse) (etype, data, off, cbuf, cbuflen)) != 0) { FREE(workBuf, M_NETGRAPH_PARSE); return (error); } cbuflen -= strlen(cbuf); cbuf += strlen(cbuf); didOne = 1; } FREE(workBuf, M_NETGRAPH_PARSE); /* Closing brace/bracket */ NG_PARSE_APPEND("%s%c", didOne ? " " : "", (ctype == CT_STRUCT) ? '}' : ']'); return (0); } /* * Generate the default value for an element of an array or structure * Returns EOPNOTSUPP if default value is unspecified. */ static int ng_get_composite_elem_default(const struct ng_parse_type *type, int index, const u_char *const start, u_char *buf, int *buflen, const enum comptype ctype) { const struct ng_parse_type *etype; ng_getDefault_t *func; switch (ctype) { case CT_STRUCT: break; case CT_ARRAY: { const struct ng_parse_array_info *const ai = type->info; if (ai->getDefault != NULL) { return (*ai->getDefault)(type, index, start, buf, buflen); } break; } case CT_FIXEDARRAY: { const struct ng_parse_fixedarray_info *const fi = type->info; if (*fi->getDefault != NULL) { return (*fi->getDefault)(type, index, start, buf, buflen); } break; } default: panic("%s", __func__); } /* Default to element type default */ etype = ng_get_composite_etype(type, index, ctype); func = METHOD(etype, getDefault); if (func == NULL) return (EOPNOTSUPP); return (*func)(etype, start, buf, buflen); } /* * Get the number of elements in a struct, variable or fixed array. */ static int ng_get_composite_len(const struct ng_parse_type *type, const u_char *const start, const u_char *buf, const enum comptype ctype) { switch (ctype) { case CT_STRUCT: { const struct ng_parse_struct_field *const fields = type->info; int numFields = 0; for (numFields = 0; ; numFields++) { const struct ng_parse_struct_field *const fi = &fields[numFields]; if (fi->name == NULL) break; } return (numFields); } case CT_ARRAY: { const struct ng_parse_array_info *const ai = type->info; return (*ai->getLength)(type, start, buf); } case CT_FIXEDARRAY: { const struct ng_parse_fixedarray_info *const fi = type->info; return fi->length; } default: panic("%s", __func__); } return (0); } /* * Return the type of the index'th element of a composite structure */ static const struct ng_parse_type * ng_get_composite_etype(const struct ng_parse_type *type, int index, const enum comptype ctype) { const struct ng_parse_type *etype = NULL; switch (ctype) { case CT_STRUCT: { const struct ng_parse_struct_field *const fields = type->info; etype = fields[index].type; break; } case CT_ARRAY: { const struct ng_parse_array_info *const ai = type->info; etype = ai->elementType; break; } case CT_FIXEDARRAY: { const struct ng_parse_fixedarray_info *const fi = type->info; etype = fi->elementType; break; } default: panic("%s", __func__); } return (etype); } /* * Get the number of bytes to skip to align for the next * element in a composite structure. */ static int ng_parse_get_elem_pad(const struct ng_parse_type *type, int index, enum comptype ctype, int posn) { const struct ng_parse_type *const etype = ng_get_composite_etype(type, index, ctype); int align; /* Get element's alignment, and possibly override */ align = ALIGNMENT(etype); if (ctype == CT_STRUCT) { const struct ng_parse_struct_field *const fields = type->info; if (fields[index].alignment != 0) align = fields[index].alignment; } /* Return number of bytes to skip to align */ return (align ? (align - (posn % align)) % align : 0); } /************************************************************************ PARSING HELPER ROUTINES ************************************************************************/ /* * Skip over a value */ static int ng_parse_skip_value(const char *s, int off0, int *lenp) { int len, nbracket, nbrace; int off = off0; len = nbracket = nbrace = 0; do { switch (ng_parse_get_token(s, &off, &len)) { case T_LBRACKET: nbracket++; break; case T_LBRACE: nbrace++; break; case T_RBRACKET: if (nbracket-- == 0) return (EINVAL); break; case T_RBRACE: if (nbrace-- == 0) return (EINVAL); break; case T_EOF: return (EINVAL); default: break; } off += len; } while (nbracket > 0 || nbrace > 0); *lenp = off - off0; return (0); } /* * Find the next token in the string, starting at offset *startp. * Returns the token type, with *startp pointing to the first char * and *lenp the length. */ enum ng_parse_token ng_parse_get_token(const char *s, int *startp, int *lenp) { char *t; int i; while (isspace(s[*startp])) (*startp)++; switch (s[*startp]) { case '\0': *lenp = 0; return T_EOF; case '{': *lenp = 1; return T_LBRACE; case '}': *lenp = 1; return T_RBRACE; case '[': *lenp = 1; return T_LBRACKET; case ']': *lenp = 1; return T_RBRACKET; case '=': *lenp = 1; return T_EQUALS; case '"': if ((t = ng_get_string_token(s, startp, lenp, NULL)) == NULL) return T_ERROR; FREE(t, M_NETGRAPH_PARSE); return T_STRING; default: for (i = *startp + 1; s[i] != '\0' && !isspace(s[i]) && s[i] != '{' && s[i] != '}' && s[i] != '[' && s[i] != ']' && s[i] != '=' && s[i] != '"'; i++) ; *lenp = i - *startp; return T_WORD; } } /* * Get a string token, which must be enclosed in double quotes. * The normal C backslash escapes are recognized. */ char * ng_get_string_token(const char *s, int *startp, int *lenp, int *slenp) { char *cbuf, *p; int start, off; int slen; while (isspace(s[*startp])) (*startp)++; start = *startp; if (s[*startp] != '"') return (NULL); MALLOC(cbuf, char *, strlen(s + start), M_NETGRAPH_PARSE, M_NOWAIT); if (cbuf == NULL) return (NULL); strcpy(cbuf, s + start + 1); for (slen = 0, off = 1, p = cbuf; *p != '\0'; slen++, off++, p++) { if (*p == '"') { *p = '\0'; *lenp = off + 1; if (slenp != NULL) *slenp = slen; return (cbuf); } else if (p[0] == '\\' && p[1] != '\0') { int x, k; char *v; strcpy(p, p + 1); v = p; switch (*p) { case 't': *v = '\t'; off++; continue; case 'n': *v = '\n'; off++; continue; case 'r': *v = '\r'; off++; continue; case 'v': *v = '\v'; off++; continue; case 'f': *v = '\f'; off++; continue; case '"': *v = '"'; off++; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': for (x = k = 0; k < 3 && *v >= '0' && *v <= '7'; v++) { x = (x << 3) + (*v - '0'); off++; } *--v = (char)x; break; case 'x': for (v++, x = k = 0; k < 2 && isxdigit(*v); v++) { x = (x << 4) + (isdigit(*v) ? (*v - '0') : (tolower(*v) - 'a' + 10)); off++; } *--v = (char)x; break; default: continue; } strcpy(p, v); } } FREE(cbuf, M_NETGRAPH_PARSE); return (NULL); /* no closing quote */ } /* * Encode a string so it can be safely put in double quotes. * Caller must free the result. Exactly "slen" characters * are encoded. */ char * ng_encode_string(const char *raw, int slen) { char *cbuf; int off = 0; int i; MALLOC(cbuf, char *, strlen(raw) * 4 + 3, M_NETGRAPH_PARSE, M_NOWAIT); if (cbuf == NULL) return (NULL); cbuf[off++] = '"'; for (i = 0; i < slen; i++, raw++) { switch (*raw) { case '\t': cbuf[off++] = '\\'; cbuf[off++] = 't'; break; case '\f': cbuf[off++] = '\\'; cbuf[off++] = 'f'; break; case '\n': cbuf[off++] = '\\'; cbuf[off++] = 'n'; break; case '\r': cbuf[off++] = '\\'; cbuf[off++] = 'r'; break; case '\v': cbuf[off++] = '\\'; cbuf[off++] = 'v'; break; case '"': case '\\': cbuf[off++] = '\\'; cbuf[off++] = *raw; break; default: if (*raw < 0x20 || *raw > 0x7e) { off += sprintf(cbuf + off, "\\x%02x", (u_char)*raw); break; } cbuf[off++] = *raw; break; } } cbuf[off++] = '"'; cbuf[off] = '\0'; return (cbuf); } /************************************************************************ VIRTUAL METHOD LOOKUP ************************************************************************/ static ng_parse_t * ng_get_parse_method(const struct ng_parse_type *t) { while (t != NULL && t->parse == NULL) t = t->supertype; return (t ? t->parse : NULL); } static ng_unparse_t * ng_get_unparse_method(const struct ng_parse_type *t) { while (t != NULL && t->unparse == NULL) t = t->supertype; return (t ? t->unparse : NULL); } static ng_getDefault_t * ng_get_getDefault_method(const struct ng_parse_type *t) { while (t != NULL && t->getDefault == NULL) t = t->supertype; return (t ? t->getDefault : NULL); } static ng_getAlign_t * ng_get_getAlign_method(const struct ng_parse_type *t) { while (t != NULL && t->getAlign == NULL) t = t->supertype; return (t ? t->getAlign : NULL); }